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"
39 #include "../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 gboolean 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), NULL);
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 #if GTK_CHECK_VERSION(2,14,0)
642 gdk_window_set_transient_for(gtk_widget_get_window(main_w), parent);
644 gdk_window_set_transient_for(main_w->window, parent);
646 gtk_window_set_modal(GTK_WINDOW(main_w), TRUE);
649 /* hide the Ok button, if we don't have to apply it and our caller wants a Save button */
650 if (parent_filter_te == NULL && prefs.gui_use_pref_save) {
651 gtk_widget_hide(ok_bt);
654 /* hide the Apply button, if our caller don't wants one */
655 if (!construct_args->wants_apply_button) {
656 gtk_widget_hide(apply_bt);
659 /* hide the Save button if the user uses implicit save */
660 if (!prefs.gui_use_pref_save) {
661 gtk_widget_hide(save_bt);
664 window_present(main_w);
670 filter_dlg_dclick(GtkWidget *filter_l, gpointer main_w_arg, gpointer activate)
672 GtkWidget *main_w = GTK_WIDGET(main_w_arg);
673 GtkWidget *parent_filter_te =
674 g_object_get_data(G_OBJECT(main_w), E_FILT_PARENT_FILTER_TE_KEY);
677 GtkTreeSelection *sel;
681 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(filter_l));
683 if (parent_filter_te != NULL) {
685 * We have a text entry widget associated with this dialog
686 * box; is one of the filters in the list selected?
688 if (gtk_tree_selection_get_selected(sel, &model, &iter)) {
690 * Yes. Is there a filter definition for that filter?
692 gtk_tree_model_get(model, &iter, 1, &flp, -1);
695 * Yes - put it in the text entry widget.
697 filt = (filter_def *) flp->data;
698 gtk_entry_set_text(GTK_ENTRY(parent_filter_te),
702 * Are we supposed to cause the filter we
703 * put there to be applied?
705 if (activate != NULL) {
709 g_signal_emit_by_name(G_OBJECT(parent_filter_te), "activate", NULL);
715 window_destroy(main_w);
719 filter_dlg_ok_cb(GtkWidget *ok_bt, gpointer data)
721 filter_list_type_t list_type = *(filter_list_type_t *)data;
724 * Destroy the dialog box and apply the filter.
726 filter_apply(gtk_widget_get_toplevel(ok_bt), TRUE);
728 /* if we don't have a Save button, just save the settings now */
729 if (!prefs.gui_use_pref_save) {
730 filter_dlg_save(list_type);
735 filter_dlg_apply_cb(GtkWidget *apply_bt, gpointer data)
737 filter_list_type_t list_type = *(filter_list_type_t *)data;
740 * Apply the filter, but don't destroy the dialog box.
742 filter_apply(gtk_widget_get_toplevel(apply_bt), FALSE);
744 /* if we don't have a Save button, just save the settings now */
745 if (!prefs.gui_use_pref_save) {
746 filter_dlg_save(list_type);
751 filter_apply(GtkWidget *main_w, gboolean destroy)
753 construct_args_t *construct_args =
754 g_object_get_data(G_OBJECT(main_w), E_FILT_CONSTRUCT_ARGS_KEY);
755 GtkWidget *parent_filter_te =
756 g_object_get_data(G_OBJECT(main_w), E_FILT_PARENT_FILTER_TE_KEY);
757 GtkWidget *filter_te;
758 const gchar *filter_string;
760 if (parent_filter_te != NULL) {
762 * We have a text entry widget associated with this dialog
763 * box; put the filter in our text entry widget into that
766 filter_te = g_object_get_data(G_OBJECT(main_w), E_FILT_FILTER_TE_KEY);
768 (const gchar *)gtk_entry_get_text(GTK_ENTRY(filter_te));
769 gtk_entry_set_text(GTK_ENTRY(parent_filter_te), filter_string);
775 * Destroy the filter dialog box.
777 window_destroy(main_w);
780 if (parent_filter_te != NULL) {
782 * We have a text entry widget associated with this dialog
783 * box; activate that widget to cause the filter we put
784 * there to be applied if we're supposed to do so.
786 * We do this after dismissing the filter dialog box,
787 * as activating the widget the dialog box to which
788 * it belongs to be dismissed, and that may cause it
789 * to destroy our dialog box if the filter succeeds.
790 * This means that our subsequent attempt to destroy
793 * We don't know whether it'll destroy our dialog box,
794 * so we can't rely on it to do so. Instead, we
795 * destroy it ourselves, which will clear the
796 * E_FILT_DIALOG_PTR_KEY pointer for their dialog box,
797 * meaning they won't think it has one and won't try
800 if (construct_args->activate_on_ok) {
801 g_signal_emit_by_name(G_OBJECT(parent_filter_te), "activate", NULL);
808 filter_dlg_save(filter_list_type_t list_type)
813 const char *filter_type;
817 case CFILTER_EDITED_LIST:
818 filter_type = "capture";
819 list_type = CFILTER_LIST;
820 copy_filter_list(CFILTER_LIST, CFILTER_EDITED_LIST);
823 case DFILTER_EDITED_LIST:
824 filter_type = "display";
825 list_type = DFILTER_LIST;
826 copy_filter_list(DFILTER_LIST, DFILTER_EDITED_LIST);
830 g_assert_not_reached();
835 /* Create the directory that holds personal configuration files,
837 if (create_persconffile_dir(&pf_dir_path) == -1) {
838 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
839 "Can't create directory\n\"%s\"\nfor filter files: %s.",
840 pf_dir_path, strerror(errno));
845 save_filter_list(list_type, &f_path, &f_save_errno);
846 if (f_path != NULL) {
847 /* We had an error saving the filter. */
848 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
849 "Could not save to your %s filter file\n\"%s\": %s.",
850 filter_type, f_path, strerror(f_save_errno));
857 filter_dlg_save_cb(GtkWidget *save_bt _U_, gpointer data)
859 filter_list_type_t list_type = *(filter_list_type_t *)data;
861 filter_dlg_save(list_type);
865 /* update a remaining dialog if another one was cancelled */
867 filter_dlg_update_list_cb(gpointer data, gpointer user_data)
869 GtkWidget *main_w = data;
870 filter_list_type_t list_type = *(filter_list_type_t *)user_data;
872 /* refill the list */
874 fill_list(main_w, list_type, NULL);
878 /* cancel button pressed, revert changes and exit dialog */
880 filter_dlg_cancel_cb(GtkWidget *cancel_bt, gpointer data)
882 filter_list_type_t list_type = *(filter_list_type_t *)data;
883 GtkWidget *main_w = gtk_widget_get_toplevel(cancel_bt);
884 static GList *filter_list;
887 window_destroy(GTK_WIDGET(main_w));
889 /* if this was the last open filter dialog, revert the changes made */
890 filter_list = get_filter_dialog_list(list_type);
891 if(g_list_length(filter_list) == 0) {
892 /* revert changes in the edited list */
894 case CFILTER_EDITED_LIST:
895 copy_filter_list(CFILTER_EDITED_LIST, CFILTER_LIST);
897 case DFILTER_EDITED_LIST:
898 copy_filter_list(DFILTER_EDITED_LIST, DFILTER_LIST);
901 g_assert_not_reached();
907 /* update other open filter dialogs */
908 g_list_foreach(get_filter_dialog_list(list_type), filter_dlg_update_list_cb, &list_type);
912 /* Treat this as a cancel, by calling "filter_dlg_cancel_cb()" */
914 filter_dlg_delete_event_cb(GtkWidget *main_w, GdkEvent *event _U_,
917 filter_dlg_cancel_cb(main_w, data);
923 filter_dlg_destroy_cb(GtkWidget *win, gpointer data)
925 filter_list_type_t list_type = *(filter_list_type_t *)data;
928 /* Get the button that requested that we be popped up, if any.
929 (It should arrange to destroy us if it's destroyed, so
930 that we don't get a pointer to a non-existent window here.) */
931 button = g_object_get_data(G_OBJECT(win), E_FILT_BUTTON_PTR_KEY);
933 if (button != NULL) {
934 /* Tell it we no longer exist. */
935 g_object_set_data(G_OBJECT(button), E_FILT_DIALOG_PTR_KEY, NULL);
937 /* This is an editing dialog popped up from, for example,
938 a menu item; note that we no longer have one. */
942 case CFILTER_EDITED_LIST:
943 g_assert(win == global_cfilter_w);
944 global_cfilter_w = NULL;
948 g_assert_not_reached();
953 /* Remove this from the list of filter dialog windows. */
954 forget_filter_dialog(win, list_type);
958 filter_sel_list_button_cb(GtkWidget *list, GdkEventButton *event,
961 void (* func)(GtkWidget *, gpointer, gpointer);
963 gpointer func_activate;
965 if (event->type == GDK_2BUTTON_PRESS) {
966 func = g_object_get_data(G_OBJECT(list), E_FILT_DBLFUNC_KEY);
967 func_arg = g_object_get_data(G_OBJECT(list), E_FILT_DBLARG_KEY);
968 func_activate = g_object_get_data(G_OBJECT(list), E_FILT_DBLACTIVATE_KEY);
971 (*func)(list, func_arg, func_activate);
978 filter_sel_list_cb(GtkTreeSelection *sel, gpointer data _U_)
980 GtkWidget *filter_l = GTK_WIDGET(gtk_tree_selection_get_tree_view(sel));
981 GtkWidget *main_w = gtk_widget_get_toplevel(filter_l);
984 GtkWidget *name_te = g_object_get_data(G_OBJECT(main_w), E_FILT_NAME_TE_KEY);
985 GtkWidget *filter_te = g_object_get_data(G_OBJECT(main_w), E_FILT_FILTER_TE_KEY);
986 GtkWidget *chg_bt = g_object_get_data(G_OBJECT(main_w), E_FILT_CHG_BT_KEY);
987 GtkWidget *copy_bt = g_object_get_data(G_OBJECT(main_w), E_FILT_COPY_BT_KEY);
988 GtkWidget *del_bt = g_object_get_data(G_OBJECT(main_w), E_FILT_DEL_BT_KEY);
990 gchar *name = NULL, *strval = NULL;
992 gint sensitivity = FALSE;
994 if (gtk_tree_selection_get_selected(sel, &model, &iter)) {
995 gtk_tree_model_get(model, &iter, 1, &flp, -1);
997 filt = (filter_def *) flp->data;
998 name = g_strdup(filt->name);
999 strval = g_strdup(filt->strval);
1005 * Did you know that this function is called when the window is destroyed?
1007 * This means that we have to:
1009 * attach to the top-level window data items containing pointers to
1010 * the widgets we affect here;
1012 * give each of those widgets their own destroy callbacks;
1014 * clear that pointer when the widget is destroyed;
1016 * don't do anything to the widget if the pointer we get back is
1019 * so that if we're called after any of the widgets we'd affect are
1020 * destroyed, we know that we shouldn't do anything to those widgets.
1022 if (name_te != NULL)
1023 gtk_entry_set_text(GTK_ENTRY(name_te), name ? name : "");
1024 if (filter_te != NULL)
1025 gtk_entry_set_text(GTK_ENTRY(filter_te), strval ? strval : "");
1027 gtk_widget_set_sensitive(chg_bt, sensitivity);
1028 if (copy_bt != NULL)
1029 gtk_widget_set_sensitive(copy_bt, sensitivity);
1031 gtk_widget_set_sensitive(del_bt, sensitivity);
1036 /* To do: add input checking to each of these callbacks */
1038 /* Structure containing arguments to be passed to "new_filter_cb()".
1040 "active_filter_l" is the list in the dialog box in which "New" or
1041 "Copy" was clicked; in that dialog box, but not in any other dialog
1042 box, we select the newly created list item.
1044 "nflp" is the GList member in the model (filter list) for the new
1047 GtkWidget *active_filter_l;
1049 } new_filter_cb_args_t;
1052 new_filter_cb(gpointer data, gpointer user_data)
1054 GtkWidget *main_w = data;
1055 GtkTreeView *filter_l;
1056 GtkListStore *store;
1058 new_filter_cb_args_t *args = user_data;
1059 filter_def *nfilt = args->nflp->data;
1061 filter_l = GTK_TREE_VIEW(g_object_get_data(G_OBJECT(main_w), E_FILT_FILTER_L_KEY));
1062 store = GTK_LIST_STORE(gtk_tree_view_get_model(filter_l));
1063 gtk_list_store_append(store, &iter);
1064 gtk_list_store_set(store, &iter, 0, nfilt->name, 1, args->nflp, -1);
1065 if (GTK_WIDGET(filter_l) == args->active_filter_l) {
1066 /* Select the item. */
1067 gtk_tree_selection_select_iter(gtk_tree_view_get_selection(filter_l),
1073 filter_new_bt_clicked_cb(GtkWidget *w, gpointer data)
1075 GtkWidget *main_w = gtk_widget_get_toplevel(w);
1076 GtkWidget *name_te = g_object_get_data(G_OBJECT(main_w), E_FILT_NAME_TE_KEY);
1077 GtkWidget *filter_te = g_object_get_data(G_OBJECT(main_w), E_FILT_FILTER_TE_KEY);
1078 GtkWidget *filter_l = g_object_get_data(G_OBJECT(main_w), E_FILT_FILTER_L_KEY);
1079 filter_list_type_t list_type = *(filter_list_type_t *)data;
1081 const gchar *name, *strval;
1082 new_filter_cb_args_t args;
1084 name = gtk_entry_get_text(GTK_ENTRY(name_te));
1085 strval = gtk_entry_get_text(GTK_ENTRY(filter_te));
1087 /* if the user didn't entered a name, set default one */
1088 if (strlen(name) == 0) {
1092 /* if the user didn't entered a string value, set default one */
1093 if (strlen(strval) == 0) {
1097 /* Add a new entry to the filter list. */
1098 fl_entry = add_to_filter_list(list_type, name, strval);
1100 /* Update all the filter list widgets, not just the one in
1101 the dialog box in which we clicked on "Copy". */
1102 args.active_filter_l = filter_l;
1103 args.nflp = fl_entry;
1104 g_list_foreach(get_filter_dialog_list(list_type), new_filter_cb, &args);
1109 chg_list_item_cb(GtkTreeModel *model, GtkTreePath *path _U_, GtkTreeIter *iter,
1113 filter_def *filt = flp->data;
1116 gtk_tree_model_get(model, iter, 1, &nl_model, -1);
1117 /* Is this the item corresponding to the filter list item in question? */
1118 if (flp == nl_model) {
1119 /* Yes - change the label to correspond to the new name for the
1121 gtk_list_store_set(GTK_LIST_STORE(model), iter, 0, filt->name, -1);
1128 chg_filter_cb(gpointer data, gpointer user_data)
1130 GtkWidget *main_w = data;
1131 GtkWidget *filter_l = g_object_get_data(G_OBJECT(main_w), E_FILT_FILTER_L_KEY);
1133 gtk_tree_model_foreach(gtk_tree_view_get_model(GTK_TREE_VIEW(filter_l)),
1134 chg_list_item_cb, user_data);
1138 filter_name_te_changed_cb(GtkWidget *w, gpointer data)
1140 GtkWidget *main_w = gtk_widget_get_toplevel(w);
1141 GtkWidget *name_te = g_object_get_data(G_OBJECT(main_w), E_FILT_NAME_TE_KEY);
1142 GtkWidget *filter_te = g_object_get_data(G_OBJECT(main_w), E_FILT_FILTER_TE_KEY);
1143 GtkWidget *filter_l = g_object_get_data(G_OBJECT(main_w), E_FILT_FILTER_L_KEY);
1146 filter_list_type_t list_type = *(filter_list_type_t *)data;
1147 const gchar *name = "";
1148 const gchar *strval = "";
1150 GtkTreeSelection *sel;
1151 GtkTreeModel *model;
1154 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(filter_l));
1155 name = gtk_entry_get_text(GTK_ENTRY(name_te));
1156 strval = gtk_entry_get_text(GTK_ENTRY(filter_te));
1158 if (DFILTER_EDITED_LIST == list_type) {
1159 /* colorize filter string entry */
1160 filter_te_syntax_check_cb(filter_te, NULL);
1163 /* if something was selected */
1164 if (gtk_tree_selection_get_selected(sel, &model, &iter)) {
1165 gtk_tree_model_get(model, &iter, 1, &fl_entry, -1);
1166 if (fl_entry != NULL) {
1167 filt = (filter_def *) fl_entry->data;
1169 if (strlen(name) > 0 && strlen(strval) > 0 && filt) {
1171 g_free(filt->strval);
1172 filt->name = g_strdup(name);
1173 filt->strval = g_strdup(strval);
1175 /* Update all the filter list widgets, not just the one in
1176 the dialog box in which we clicked on "Copy". */
1177 g_list_foreach(get_filter_dialog_list(list_type), chg_filter_cb,
1185 delete_filter_cb(gpointer data, gpointer user_data)
1187 GtkWidget *main_w = data;
1188 GtkWidget *filter_l = g_object_get_data(G_OBJECT(main_w), E_FILT_FILTER_L_KEY);
1189 gchar *pos = (gchar *)user_data;
1190 GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW(filter_l));
1193 gtk_tree_model_get_iter_from_string(model, &iter, pos);
1194 gtk_list_store_remove(GTK_LIST_STORE(model), &iter);
1198 filter_del_bt_clicked_cb(GtkWidget *w, gpointer data)
1200 GtkWidget *main_w = gtk_widget_get_toplevel(w);
1201 GtkWidget *filter_l = g_object_get_data(G_OBJECT(main_w), E_FILT_FILTER_L_KEY);
1202 filter_list_type_t list_type = *(filter_list_type_t *)data;
1205 GtkTreeSelection *sel;
1206 GtkTreeModel *model;
1210 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(filter_l));
1211 /* If something was selected */
1212 if (gtk_tree_selection_get_selected(sel, &model, &iter)) {
1213 gtk_tree_model_get(model, &iter, 1, &fl_entry, -1);
1214 path = gtk_tree_model_get_path(model, &iter);
1215 pos = gtk_tree_path_to_string(path);
1216 gtk_tree_path_free(path);
1217 if (fl_entry != NULL) {
1218 /* Remove the entry from the filter list. */
1219 remove_from_filter_list(list_type, fl_entry);
1221 /* Update all the filter list widgets, not just the one in
1222 the dialog box in which we clicked on "Delete". */
1223 g_list_foreach(get_filter_dialog_list(list_type), delete_filter_cb, pos);
1230 filter_add_expr_bt_cb(GtkWidget *w _U_, gpointer main_w_arg)
1232 GtkWidget *main_w = GTK_WIDGET(main_w_arg);
1233 GtkWidget *filter_te, *dfilter_w;
1235 filter_te = g_object_get_data(G_OBJECT(main_w), E_FILT_FILTER_TE_KEY);
1236 dfilter_w = dfilter_expr_dlg_new(filter_te);
1238 /* If we're opening a series of modal dialogs (such as when going
1239 * through file->open, make the latest dialog modal also so that it
1240 * takes over "control" from the other modal dialogs. Also set
1241 * the transient property of the new dialog so the user doesn't try
1242 * to interact with the previous window when they can't.
1243 * XXX: containing widget might be the Filter Toolbar */
1245 if ( GTK_IS_WINDOW(main_w) && gtk_window_get_modal(GTK_WINDOW(main_w))) {
1246 gtk_window_set_modal(GTK_WINDOW(dfilter_w), TRUE);
1247 gtk_window_set_transient_for(GTK_WINDOW(dfilter_w),
1248 GTK_WINDOW(main_w));
1253 color_filter_te(GtkWidget *w, guint16 red, guint16 green, guint16 blue)
1255 static GdkColor black = { 0, 0, 0, 0 };
1263 gtk_widget_modify_text(w, GTK_STATE_NORMAL, &black);
1264 gtk_widget_modify_base(w, GTK_STATE_NORMAL, &bg);
1265 #if GTK_CHECK_VERSION(2,12,0)
1266 gtk_widget_modify_cursor(w, &black, &black);
1271 colorize_filter_te_as_empty(GtkWidget *w)
1274 gtk_widget_modify_text(w, GTK_STATE_NORMAL, NULL);
1275 gtk_widget_modify_base(w, GTK_STATE_NORMAL, NULL);
1276 #if GTK_CHECK_VERSION(2,12,0)
1277 gtk_widget_modify_cursor(w, NULL, NULL);
1282 colorize_filter_te_as_invalid(GtkWidget *w)
1285 color_filter_te(w, 0xFFFF, 0xAFFF, 0xAFFF);
1289 colorize_filter_te_as_deprecated(GtkWidget *w)
1292 color_filter_te(w, 0xFFFF, 0xFFFF, 0xAFFF);
1296 colorize_filter_te_as_valid(GtkWidget *w)
1299 color_filter_te(w, 0xAFFF, 0xFFFF, 0xAFFF);
1303 * XXX This calls dfilter_compile, which might call get_host_ipaddr or
1304 * get_host_ipaddr6. Either of of these will freeze the UI if the host
1305 * name resolution takes a long time to complete. We need to work
1306 * around this, either by disabling host name resolution or by doing
1307 * the resolution asynchronously.
1309 * We could use a separate thread but we have be careful to only call
1310 * GTK+/GDK routines from the main thread. From the GDK threads
1313 * "With the Win32 backend, GDK calls should not be attempted from
1314 * multiple threads at all."
1318 filter_te_syntax_check_cb(GtkWidget *w, gpointer user_data _U_)
1320 const gchar *strval;
1322 GPtrArray *depr = NULL;
1323 gboolean use_statusbar;
1326 strval = gtk_entry_get_text(GTK_ENTRY(w));
1327 use_statusbar = g_object_get_data(G_OBJECT(w), E_FILT_FIELD_USE_STATUSBAR_KEY) ? TRUE : FALSE;
1329 if (use_statusbar) {
1330 statusbar_pop_filter_msg();
1333 /* colorize filter string entry */
1334 if (g_object_get_data(G_OBJECT(w), E_FILT_FIELD_NAME_ONLY_KEY) &&
1335 strval && (c = proto_check_field_name(strval)) != 0)
1337 colorize_filter_te_as_invalid(w);
1338 if (use_statusbar) {
1339 statusbar_push_filter_msg(" Illegal character in field name: '%c'", c);
1341 } else if (strval && dfilter_compile(strval, &dfp)) {
1343 depr = dfilter_deprecated_tokens(dfp);
1345 if (strlen(strval) == 0) {
1346 colorize_filter_te_as_empty(w);
1348 /* You keep using that word. I do not think it means what you think it means. */
1349 colorize_filter_te_as_deprecated(w);
1350 if (use_statusbar) {
1352 * We're being lazy and only printing the first "problem" token.
1353 * Would it be better to print all of them?
1355 statusbar_push_temporary_msg(" \"%s\" may have unexpected results (see the User's Guide)",
1356 (const char *) g_ptr_array_index(depr, 0));
1359 colorize_filter_te_as_valid(w);
1363 colorize_filter_te_as_invalid(w);
1364 if (use_statusbar) {
1365 if (dfilter_error_msg) {
1366 statusbar_push_filter_msg(" Invalid filter: %s", dfilter_error_msg);
1368 statusbar_push_filter_msg(" Invalid filter");
1380 * indent-tabs-mode: nil
1383 * ex: set shiftwidth=4 tabstop=8 expandtab
1384 * :indentSize=4:tabSize=8:noTabs=true: