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>
37 #include "filter_dlg.h"
38 #include "dlg_utils.h"
39 #include "gui_utils.h"
40 #include "simple_dialog.h"
41 #include "dfilter_expr_dlg.h"
42 #include "compat_macros.h"
43 #include "gtkglobals.h"
46 #define E_FILT_DIALOG_PTR_KEY "filter_dialog_ptr"
47 #define E_FILT_BUTTON_PTR_KEY "filter_button_ptr"
48 #define E_FILT_PARENT_FILTER_TE_KEY "filter_parent_filter_te"
49 #define E_FILT_CONSTRUCT_ARGS_KEY "filter_construct_args"
50 #define E_FILT_LIST_ITEM_MODEL_KEY "filter_list_item_model"
51 #define E_FILT_LBL_KEY "filter_label"
52 #define E_FILT_FILTER_L_KEY "filter_filter_l"
53 #define E_FILT_CHG_BT_KEY "filter_chg_bt"
54 #define E_FILT_COPY_BT_KEY "filter_copy_bt"
55 #define E_FILT_DEL_BT_KEY "filter_del_bt"
56 #define E_FILT_NAME_TE_KEY "filter_name_te"
57 #define E_FILT_DBLFUNC_KEY "filter_dblfunc"
58 #define E_FILT_DBLARG_KEY "filter_dblarg"
59 #define E_FILT_DBLACTIVATE_KEY "filter_dblactivate"
61 typedef struct _filter_cb_data {
66 static GtkWidget *filter_dialog_new(GtkWidget *button, GtkWidget *filter_te,
67 filter_list_type_t list,
68 construct_args_t *construct_args);
69 static void filter_dlg_dclick(GtkWidget *dummy, gpointer main_w_arg,
71 static void filter_dlg_ok_cb(GtkWidget *ok_bt, gpointer dummy);
72 static void filter_dlg_apply_cb(GtkWidget *apply_bt, gpointer dummy);
73 static void filter_apply(GtkWidget *main_w, gboolean destroy);
74 static void filter_dlg_save_cb(GtkWidget *save_bt, gpointer parent_w);
75 static void filter_dlg_destroy_cb(GtkWidget *win, gpointer data);
77 static gint filter_sel_list_button_cb(GtkWidget *, GdkEventButton *,
79 #if GTK_MAJOR_VERSION < 2
80 static void filter_sel_list_cb(GtkWidget *, gpointer);
82 static void filter_sel_list_cb(GtkTreeSelection *, gpointer);
84 static void filter_new_bt_clicked_cb(GtkWidget *, gpointer);
85 static void filter_del_bt_clicked_cb(GtkWidget *, gpointer);
86 static void filter_name_te_changed_cb(GtkWidget *, gpointer);
89 /* Create a filter dialog for constructing a capture filter.
91 This is to be used as a callback for a button next to a text entry box,
92 which, when clicked, pops up this dialog to allow you to construct a
93 display filter by browsing the list of saved filters (the dialog
94 for constructing expressions assumes display filter syntax, not
95 capture filter syntax). The "OK" button sets the text entry box to the
96 constructed filter and activates that text entry box (which should have
97 no effect in the main capture dialog); this dialog is then dismissed. */
99 capture_filter_construct_cb(GtkWidget *w, gpointer user_data _U_)
101 GtkWidget *filter_browse_w;
102 GtkWidget *parent_filter_te;
103 /* No Apply button, and "OK" just sets our text widget, it doesn't
104 activate it (i.e., it doesn't cause us to try to open the file). */
105 static construct_args_t args = {
106 "Ethereal: Capture Filter",
112 /* Has a filter dialog box already been opened for that button? */
113 filter_browse_w = OBJECT_GET_DATA(w, E_FILT_DIALOG_PTR_KEY);
115 if (filter_browse_w != NULL) {
116 /* Yes. Just re-activate that dialog box. */
117 reactivate_window(filter_browse_w);
121 /* No. Get the text entry attached to the button. */
122 parent_filter_te = OBJECT_GET_DATA(w, E_FILT_TE_PTR_KEY);
124 /* Now create a new dialog, without an "Add Expression..." button. */
125 filter_browse_w = filter_dialog_new(w, parent_filter_te,
126 CFILTER_LIST, &args);
130 /* Create a filter dialog for constructing a display filter.
132 This is to be used as a callback for a button next to a text entry box,
133 which, when clicked, pops up this dialog to allow you to construct a
134 display filter by browsing the list of saved filters and/or by adding
135 test expressions constructed with another dialog. The "OK" button
136 sets the text entry box to the constructed filter and activates that
137 text entry box, causing the filter to be used; this dialog is then
140 If "wants_apply_button" is non-null, we add an "Apply" button that
141 acts like "OK" but doesn't dismiss this dialog. */
143 display_filter_construct_cb(GtkWidget *w, gpointer construct_args_ptr)
145 construct_args_t *construct_args = construct_args_ptr;
146 GtkWidget *filter_browse_w;
147 GtkWidget *parent_filter_te;
149 /* Has a filter dialog box already been opened for the button? */
150 filter_browse_w = OBJECT_GET_DATA(w, E_FILT_DIALOG_PTR_KEY);
152 if (filter_browse_w != NULL) {
153 /* Yes. Just re-activate that dialog box. */
154 reactivate_window(filter_browse_w);
158 /* No. Get the text entry attached to the button. */
159 parent_filter_te = OBJECT_GET_DATA(w, E_FILT_TE_PTR_KEY);
161 /* Now create a new dialog, possibly with an "Apply" button, and
162 definitely with an "Add Expression..." button. */
163 filter_browse_w = filter_dialog_new(w, parent_filter_te,
164 DFILTER_LIST, construct_args);
167 /* Should be called when a button that creates filters is destroyed; it
168 destroys any filter dialog created by that button. */
170 filter_button_destroy_cb(GtkWidget *button, gpointer user_data _U_)
174 /* Is there a filter edit/selection dialog associated with this
176 filter_w = OBJECT_GET_DATA(button, E_FILT_DIALOG_PTR_KEY);
178 if (filter_w != NULL) {
179 /* Yes. Break the association, and destroy the dialog. */
180 OBJECT_SET_DATA(button, E_FILT_DIALOG_PTR_KEY, NULL);
181 window_destroy(filter_w);
186 static GtkWidget *global_cfilter_w;
188 /* Create a filter dialog for editing capture filters; this is to be used
189 as a callback for menu items, toolbars, etc.. */
191 cfilter_dialog_cb(GtkWidget *w _U_)
193 /* No Apply button, and there's no text widget to set, much less
194 activate, on "OK". */
195 static construct_args_t args = {
196 "Ethereal: Capture Filter",
202 /* Has a filter dialog box already been opened for editing
204 if (global_cfilter_w != NULL) {
205 /* Yes. Just reactivate it. */
206 reactivate_window(global_cfilter_w);
211 * No. Create one; we didn't pop this up as a result of pressing
212 * a button next to some text entry field, so don't associate it
213 * with a text entry field or button.
215 global_cfilter_w = filter_dialog_new(NULL, NULL, CFILTER_LIST, &args);
219 /* Create a filter dialog for editing display filters; this is to be used
220 as a callback for menu items, toolbars, etc.. */
222 dfilter_dialog_cb(GtkWidget *w _U_)
224 static construct_args_t args = {
225 "Ethereal: Display Filter",
231 display_filter_construct_cb(OBJECT_GET_DATA(top_level, E_FILT_BT_PTR_KEY), &args);
234 /* List of capture filter dialogs, so that if the list of filters changes
235 (the model, if you will), we can update all of their lists displaying
236 the filters (the views). */
237 static GList *cfilter_dialogs;
239 /* List of display 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 *dfilter_dialogs;
245 remember_filter_dialog(GtkWidget *main_w, GList **filter_dialogs)
247 *filter_dialogs = g_list_append(*filter_dialogs, main_w);
250 /* Remove a filter dialog from the specified list of filter_dialogs. */
252 forget_filter_dialog(GtkWidget *main_w, filter_list_type_t list)
257 cfilter_dialogs = g_list_remove(cfilter_dialogs, main_w);
261 dfilter_dialogs = g_list_remove(dfilter_dialogs, main_w);
265 g_assert_not_reached();
270 /* Get the dialog list corresponding to a particular filter list. */
272 get_filter_dialog_list(filter_list_type_t list)
277 return cfilter_dialogs;
280 return dfilter_dialogs;
283 g_assert_not_reached();
289 filter_dialog_new(GtkWidget *button, GtkWidget *parent_filter_te,
290 filter_list_type_t list, construct_args_t *construct_args)
292 GtkWidget *main_w, /* main window */
293 *main_vb, /* main container */
294 *bbox, /* button container */
295 *ok_bt, /* "OK" button */
296 *apply_bt, /* "Apply" button */
297 *save_bt, /* "Save" button */
298 *close_bt, /* "Cancel" button */
299 *help_bt; /* "Help" button */
300 GtkWidget *filter_vb, /* filter settings box */
318 GtkTooltips *tooltips;
321 static filter_list_type_t cfilter_list_type = CFILTER_LIST;
322 static filter_list_type_t dfilter_list_type = DFILTER_LIST;
323 filter_list_type_t *filter_list_type_p;
324 GList **filter_dialogs;
325 const gchar *filter_te_str = NULL;
326 #if GTK_MAJOR_VERSION < 2
331 gboolean l_select = FALSE;
333 GtkCellRenderer *renderer;
334 GtkTreeViewColumn *column;
335 GtkTreeSelection *sel;
337 GtkTreeIter sel_iter;
340 /* Get a pointer to a static variable holding the type of filter on
341 which we're working, so we can pass that pointer to callback
346 filter_dialogs = &cfilter_dialogs;
347 filter_list_type_p = &cfilter_list_type;
351 filter_dialogs = &dfilter_dialogs;
352 filter_list_type_p = &dfilter_list_type;
356 g_assert_not_reached();
357 filter_dialogs = NULL;
358 filter_list_type_p = NULL;
362 tooltips = gtk_tooltips_new ();
364 main_w = dlg_window_new(construct_args->title);
365 gtk_window_set_default_size(GTK_WINDOW(main_w), 400, 400);
366 OBJECT_SET_DATA(main_w, E_FILT_CONSTRUCT_ARGS_KEY, construct_args);
368 if(construct_args->modal_and_transient) {
369 GdkWindow* parent = gtk_widget_get_parent_window(parent_filter_te);
370 gtk_window_set_transient_for(GTK_WINDOW(main_w), GTK_WINDOW(parent));
371 gtk_window_set_modal(GTK_WINDOW(main_w), TRUE);
374 main_vb = gtk_vbox_new(FALSE, 0);
375 gtk_container_border_width(GTK_CONTAINER(main_vb), 5);
376 gtk_container_add(GTK_CONTAINER(main_w), main_vb);
377 gtk_widget_show(main_vb);
379 /* Make sure everything is set up */
380 if (parent_filter_te)
381 filter_te_str = gtk_entry_get_text(GTK_ENTRY(parent_filter_te));
383 /* Container for each row of widgets */
384 filter_vb = gtk_vbox_new(FALSE, 0);
385 gtk_container_border_width(GTK_CONTAINER(filter_vb), 0);
386 gtk_container_add(GTK_CONTAINER(main_vb), filter_vb);
387 gtk_widget_show(filter_vb);
389 /* Top row: Buttons and filter list */
390 top_hb = gtk_hbox_new(FALSE, 0);
391 gtk_container_add(GTK_CONTAINER(filter_vb), top_hb);
392 gtk_widget_show(top_hb);
394 edit_fr = gtk_frame_new("Edit");
395 gtk_box_pack_start(GTK_BOX(top_hb), edit_fr, FALSE, FALSE, 0);
396 gtk_widget_show(edit_fr);
398 list_bb = gtk_vbox_new(TRUE, 0);
399 gtk_container_border_width(GTK_CONTAINER(list_bb), 5);
400 gtk_container_add(GTK_CONTAINER(edit_fr), list_bb);
401 gtk_widget_show(list_bb);
403 new_bt = BUTTON_NEW_FROM_STOCK(GTK_STOCK_NEW);
404 SIGNAL_CONNECT(new_bt, "clicked", filter_new_bt_clicked_cb, filter_list_type_p);
405 #if GTK_MAJOR_VERSION < 2
406 WIDGET_SET_SIZE(new_bt, 50, 20);
408 gtk_widget_show(new_bt);
409 gtk_box_pack_start (GTK_BOX (list_bb), new_bt, FALSE, FALSE, 0);
410 gtk_tooltips_set_tip (tooltips, new_bt,
411 "Create a new filter at the end of the list (with the current properties)", NULL);
413 del_bt = BUTTON_NEW_FROM_STOCK(GTK_STOCK_DELETE);
414 gtk_widget_set_sensitive(del_bt, FALSE);
415 SIGNAL_CONNECT(del_bt, "clicked", filter_del_bt_clicked_cb, filter_list_type_p);
416 OBJECT_SET_DATA(main_w, E_FILT_DEL_BT_KEY, del_bt);
417 #if GTK_MAJOR_VERSION < 2
418 WIDGET_SET_SIZE(del_bt, 50, 20);
420 gtk_widget_show(del_bt);
421 gtk_box_pack_start (GTK_BOX (list_bb), del_bt, FALSE, FALSE, 0);
422 gtk_tooltips_set_tip (tooltips, del_bt, ("Delete the selected filter"), NULL);
424 filter_fr = gtk_frame_new("Filter");
425 gtk_box_pack_start(GTK_BOX(top_hb), filter_fr, TRUE, TRUE, 0);
426 gtk_widget_show(filter_fr);
428 filter_sc = scrolled_window_new(NULL, NULL);
429 #if GTK_MAJOR_VERSION >= 2
430 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(filter_sc),
434 gtk_container_set_border_width (GTK_CONTAINER (filter_sc), 5);
435 gtk_container_add(GTK_CONTAINER(filter_fr), filter_sc);
436 gtk_widget_show(filter_sc);
438 #if GTK_MAJOR_VERSION < 2
439 filter_l = gtk_list_new();
440 gtk_list_set_selection_mode(GTK_LIST(filter_l), GTK_SELECTION_SINGLE);
441 SIGNAL_CONNECT(filter_l, "selection_changed", filter_sel_list_cb,
444 store = gtk_list_store_new(2, G_TYPE_STRING, G_TYPE_POINTER);
445 filter_l = tree_view_new(GTK_TREE_MODEL(store));
446 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(filter_l), FALSE);
447 renderer = gtk_cell_renderer_text_new();
448 column = gtk_tree_view_column_new_with_attributes("", renderer, "text",
450 gtk_tree_view_column_set_sort_column_id(column, 0);
451 gtk_tree_view_append_column(GTK_TREE_VIEW(filter_l), column);
452 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(filter_l));
453 gtk_tree_selection_set_mode(sel, GTK_SELECTION_SINGLE);
454 SIGNAL_CONNECT(sel, "changed", filter_sel_list_cb, filter_vb);
455 SIGNAL_CONNECT(filter_l, "button_press_event", filter_sel_list_button_cb,
458 OBJECT_SET_DATA(main_w, E_FILT_FILTER_L_KEY, filter_l);
459 #if GTK_MAJOR_VERSION < 2
460 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(filter_sc),
463 gtk_container_add(GTK_CONTAINER(filter_sc), filter_l);
465 gtk_widget_show(filter_l);
467 OBJECT_SET_DATA(filter_l, E_FILT_DBLFUNC_KEY, filter_dlg_dclick);
468 OBJECT_SET_DATA(filter_l, E_FILT_DBLARG_KEY, main_w);
469 /* This is a Boolean, but we make it a non-null pointer for TRUE
470 and a null pointer for FALSE, as object data is a pointer. */
471 OBJECT_SET_DATA(filter_l, E_FILT_DBLACTIVATE_KEY,
472 construct_args->activate_on_ok ? "" : NULL);
475 fl_entry = get_filter_list_first(list);
476 while (fl_entry != NULL) {
477 filt = (filter_def *) fl_entry->data;
478 #if GTK_MAJOR_VERSION < 2
479 nl_lb = gtk_label_new(filt->name);
480 nl_item = gtk_list_item_new();
482 SIGNAL_CONNECT(nl_item, "button_press_event", filter_sel_list_button_cb,
485 gtk_misc_set_alignment (GTK_MISC (nl_lb), 0.0, 0.5);
486 gtk_container_add(GTK_CONTAINER(nl_item), nl_lb);
487 gtk_widget_show(nl_lb);
488 gtk_container_add(GTK_CONTAINER(filter_l), nl_item);
489 gtk_widget_show(nl_item);
490 OBJECT_SET_DATA(nl_item, E_FILT_LBL_KEY, nl_lb);
491 OBJECT_SET_DATA(nl_item, E_FILT_LIST_ITEM_MODEL_KEY, fl_entry);
493 gtk_list_store_append(store, &iter);
494 gtk_list_store_set(store, &iter, 0, filt->name,
498 if (filter_te_str && filt->strval) {
499 if (strcmp(filter_te_str, filt->strval) == 0) {
500 #if GTK_MAJOR_VERSION < 2
509 fl_entry = fl_entry->next;
511 #if GTK_MAJOR_VERSION >= 2
512 g_object_unref(G_OBJECT(store));
516 props_fr = gtk_frame_new("Properties");
517 gtk_box_pack_start(GTK_BOX(filter_vb), props_fr, FALSE, FALSE, 0);
518 gtk_widget_show(props_fr);
520 props_vb = gtk_vbox_new(FALSE, 3);
521 gtk_container_border_width(GTK_CONTAINER(props_vb), 5);
522 gtk_container_add(GTK_CONTAINER(props_fr), props_vb);
523 gtk_widget_show(props_vb);
525 /* row: Filter name entry */
526 middle_hb = gtk_hbox_new(FALSE, 3);
527 gtk_container_add(GTK_CONTAINER(props_vb), middle_hb);
528 gtk_widget_show(middle_hb);
530 name_lb = gtk_label_new("Filter name:");
531 gtk_box_pack_start(GTK_BOX(middle_hb), name_lb, FALSE, FALSE, 0);
532 gtk_widget_show(name_lb);
534 name_te = gtk_entry_new();
535 gtk_box_pack_start(GTK_BOX(middle_hb), name_te, TRUE, TRUE, 0);
536 OBJECT_SET_DATA(main_w, E_FILT_NAME_TE_KEY, name_te);
537 SIGNAL_CONNECT(name_te, "changed", filter_name_te_changed_cb, filter_list_type_p);
538 gtk_widget_show(name_te);
540 /* row: Filter text entry */
541 bottom_hb = gtk_hbox_new(FALSE, 3);
542 gtk_container_add(GTK_CONTAINER(props_vb), bottom_hb);
543 gtk_widget_show(bottom_hb);
545 filter_lb = gtk_label_new("Filter string:");
546 gtk_box_pack_start(GTK_BOX(bottom_hb), filter_lb, FALSE, FALSE, 0);
547 gtk_widget_show(filter_lb);
549 filter_te = gtk_entry_new();
550 gtk_box_pack_start(GTK_BOX(bottom_hb), filter_te, TRUE, TRUE, 0);
551 OBJECT_SET_DATA(main_w, E_FILT_FILTER_TE_KEY, filter_te);
552 SIGNAL_CONNECT(filter_te, "changed", filter_name_te_changed_cb, filter_list_type_p);
553 gtk_widget_show(filter_te);
555 OBJECT_SET_DATA(main_w, E_FILT_PARENT_FILTER_TE_KEY, parent_filter_te);
557 if (list == DFILTER_LIST) {
558 gtk_tooltips_set_tip(tooltips, filter_te,
559 "Enter a display filter. "
560 "The background color of this field is changed by a continuous syntax check (green is valid, red is invalid).",
563 /* Create the "Add Expression..." button, to pop up a dialog
564 for constructing filter comparison expressions. */
565 add_expression_bt = BUTTON_NEW_FROM_STOCK(ETHEREAL_STOCK_ADD_EXPRESSION);
566 SIGNAL_CONNECT(add_expression_bt, "clicked", filter_add_expr_bt_cb, main_w);
567 gtk_box_pack_start(GTK_BOX(bottom_hb), add_expression_bt, FALSE, FALSE, 0);
568 gtk_widget_show(add_expression_bt);
569 gtk_tooltips_set_tip (tooltips, add_expression_bt, ("Add an expression to the filter string"), NULL);
574 if (parent_filter_te != NULL) {
575 if (construct_args->wants_apply_button) {
576 bbox = dlg_button_row_new(GTK_STOCK_OK, GTK_STOCK_APPLY, GTK_STOCK_SAVE, GTK_STOCK_CLOSE, GTK_STOCK_HELP, NULL);
578 bbox = dlg_button_row_new(GTK_STOCK_OK, GTK_STOCK_SAVE, GTK_STOCK_CLOSE, GTK_STOCK_HELP, NULL);
581 if (construct_args->wants_apply_button) {
582 bbox = dlg_button_row_new(GTK_STOCK_APPLY, GTK_STOCK_SAVE, GTK_STOCK_CLOSE, GTK_STOCK_HELP, NULL);
584 bbox = dlg_button_row_new(GTK_STOCK_SAVE, GTK_STOCK_CLOSE, GTK_STOCK_HELP, NULL);
587 gtk_box_pack_start(GTK_BOX(main_vb), bbox, FALSE, FALSE, 5);
588 gtk_widget_show(bbox);
591 if (parent_filter_te != NULL) {
593 * We have a filter text entry that we can fill in if
594 * the "OK" button is clicked, so put in an "OK" button.
596 ok_bt = OBJECT_GET_DATA(bbox, GTK_STOCK_OK);
597 SIGNAL_CONNECT(ok_bt, "clicked", filter_dlg_ok_cb, NULL);
598 gtk_tooltips_set_tip (tooltips, ok_bt, ("Apply the filters and close this dialog"), NULL);
600 /* Catch the "activate" signal on the filter name and filter
601 expression text entries, so that if the user types Return
602 there, we act as if the "OK" button had been selected, as
603 happens if Return is typed if some widget that *doesn't*
604 handle the Return key has the input focus. */
605 dlg_set_activate(name_te, ok_bt);
606 dlg_set_activate(filter_te, ok_bt);
609 if (construct_args->wants_apply_button) {
610 apply_bt = OBJECT_GET_DATA(bbox, GTK_STOCK_APPLY);
611 SIGNAL_CONNECT(apply_bt, "clicked", filter_dlg_apply_cb, NULL);
612 gtk_tooltips_set_tip (tooltips, apply_bt, ("Apply the filters and keep this dialog open"), NULL);
615 save_bt = OBJECT_GET_DATA(bbox, GTK_STOCK_SAVE);
616 SIGNAL_CONNECT(save_bt, "clicked", filter_dlg_save_cb, filter_list_type_p);
617 gtk_tooltips_set_tip (tooltips, save_bt, ("Save the filters permanently and keep this dialog open"), NULL);
619 close_bt = OBJECT_GET_DATA(bbox, GTK_STOCK_CLOSE);
620 gtk_tooltips_set_tip (tooltips, close_bt, ("Close this dialog but don't apply the filter changes"), NULL);
621 if (parent_filter_te == NULL)
622 gtk_widget_grab_default(close_bt);
624 help_bt = OBJECT_GET_DATA(bbox, GTK_STOCK_HELP);
625 if (list == CFILTER_LIST) {
626 SIGNAL_CONNECT(help_bt, "clicked", topic_cb, HELP_CAPTURE_FILTERS_DIALOG);
628 SIGNAL_CONNECT(help_bt, "clicked", topic_cb, HELP_DISPLAY_FILTERS_DIALOG);
630 gtk_tooltips_set_tip (tooltips, help_bt, ("Show topic specific help"), NULL);
632 window_set_cancel_button(main_w, close_bt, window_cancel_button_cb);
635 gtk_widget_grab_default(ok_bt);
638 remember_filter_dialog(main_w, filter_dialogs);
640 SIGNAL_CONNECT(main_w, "delete_event", window_delete_event_cb, NULL);
641 /* Call a handler when we're destroyed, so we can detach ourselves
643 SIGNAL_CONNECT(main_w, "destroy", filter_dlg_destroy_cb, filter_list_type_p);
645 if (button != NULL) {
646 /* This dialog box was created by a "Filter" button.
647 Set the E_FILT_BUTTON_PTR_KEY for the new dialog to point to
649 OBJECT_SET_DATA(main_w, E_FILT_BUTTON_PTR_KEY, button);
651 /* Set the E_FILT_DIALOG_PTR_KEY for the button to point to us */
652 OBJECT_SET_DATA(button, E_FILT_DIALOG_PTR_KEY, main_w);
655 /* DO SELECTION THINGS *AFTER* SHOWING THE DIALOG! */
656 /* otherwise the updatings can get confused */
657 #if GTK_MAJOR_VERSION < 2
659 gtk_list_select_child(GTK_LIST(filter_l), l_select);
660 } else if (filter_te_str && filter_te_str[0]) {
661 gtk_entry_set_text(GTK_ENTRY(name_te), "New filter");
662 gtk_entry_set_text(GTK_ENTRY(filter_te), filter_te_str);
666 gtk_tree_selection_select_iter(sel, &sel_iter);
667 } else if (filter_te_str && filter_te_str[0]) {
668 gtk_entry_set_text(GTK_ENTRY(name_te), "New filter");
669 gtk_entry_set_text(GTK_ENTRY(filter_te), filter_te_str);
673 gtk_widget_show(main_w);
674 window_present(main_w);
680 filter_dlg_dclick(GtkWidget *filter_l, gpointer main_w_arg, gpointer activate)
682 GtkWidget *main_w = GTK_WIDGET(main_w_arg);
683 GtkWidget *parent_filter_te =
684 OBJECT_GET_DATA(main_w, E_FILT_PARENT_FILTER_TE_KEY);
687 #if GTK_MAJOR_VERSION < 2
691 GtkTreeSelection *sel;
695 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(filter_l));
698 if (parent_filter_te != NULL) {
700 * We have a text entry widget associated with this dialog
701 * box; is one of the filters in the list selected?
703 #if GTK_MAJOR_VERSION < 2
704 sl = GTK_LIST(filter_l)->selection;
707 if (gtk_tree_selection_get_selected(sel, &model, &iter)) {
710 * Yes. Is there a filter definition for that filter?
712 #if GTK_MAJOR_VERSION < 2
713 l_item = GTK_OBJECT(sl->data);
714 flp = (GList *)OBJECT_GET_DATA(l_item,
715 E_FILT_LIST_ITEM_MODEL_KEY);
717 gtk_tree_model_get(model, &iter, 1, &flp, -1);
721 * Yes - put it in the text entry widget.
723 filt = (filter_def *) flp->data;
724 gtk_entry_set_text(GTK_ENTRY(parent_filter_te),
728 * Are we supposed to cause the filter we
729 * put there to be applied?
731 if (activate != NULL) {
735 SIGNAL_EMIT_BY_NAME(parent_filter_te, "activate", NULL);
741 window_destroy(main_w);
745 filter_dlg_ok_cb(GtkWidget *ok_bt, gpointer data _U_)
748 * Destroy the dialog box and apply the filter.
750 filter_apply(gtk_widget_get_toplevel(ok_bt), TRUE);
754 filter_dlg_apply_cb(GtkWidget *apply_bt, gpointer dummy _U_)
757 * Apply the filter, but don't destroy the dialog box.
759 filter_apply(gtk_widget_get_toplevel(apply_bt), FALSE);
763 filter_apply(GtkWidget *main_w, gboolean destroy)
765 construct_args_t *construct_args =
766 OBJECT_GET_DATA(main_w, E_FILT_CONSTRUCT_ARGS_KEY);
767 GtkWidget *parent_filter_te =
768 OBJECT_GET_DATA(main_w, E_FILT_PARENT_FILTER_TE_KEY);
769 GtkWidget *filter_te;
770 const gchar *filter_string;
772 if (parent_filter_te != NULL) {
774 * We have a text entry widget associated with this dialog
775 * box; put the filter in our text entry widget into that
778 filter_te = OBJECT_GET_DATA(main_w, E_FILT_FILTER_TE_KEY);
780 (const gchar *)gtk_entry_get_text(GTK_ENTRY(filter_te));
781 gtk_entry_set_text(GTK_ENTRY(parent_filter_te), filter_string);
787 * Destroy the filter dialog box.
789 window_destroy(main_w);
792 if (parent_filter_te != NULL) {
794 * We have a text entry widget associated with this dialog
795 * box; activate that widget to cause the filter we put
796 * there to be applied if we're supposed to do so.
798 * We do this after dismissing the filter dialog box,
799 * as activating the widget the dialog box to which
800 * it belongs to be dismissed, and that may cause it
801 * to destroy our dialog box if the filter succeeds.
802 * This means that our subsequent attempt to destroy
805 * We don't know whether it'll destroy our dialog box,
806 * so we can't rely on it to do so. Instead, we
807 * destroy it ourselves, which will clear the
808 * E_FILT_DIALOG_PTR_KEY pointer for their dialog box,
809 * meaning they won't think it has one and won't try
812 if (construct_args->activate_on_ok) {
813 SIGNAL_EMIT_BY_NAME(parent_filter_te, "activate", NULL);
819 filter_dlg_save_cb(GtkWidget *save_bt _U_, gpointer data)
821 filter_list_type_t list = *(filter_list_type_t *)data;
825 const char *filter_type;
827 /* Create the directory that holds personal configuration files,
829 if (create_persconffile_dir(&pf_dir_path) == -1) {
830 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
831 "Can't create directory\n\"%s\"\nfor filter files: %s.",
832 pf_dir_path, strerror(errno));
837 save_filter_list(list, &f_path, &f_save_errno);
838 if (f_path != NULL) {
839 /* We had an error saving the filter. */
843 filter_type = "capture";
847 filter_type = "display";
851 g_assert_not_reached();
855 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
856 "Could not save to your %s filter file\n\"%s\": %s.",
857 filter_type, f_path, strerror(f_save_errno));
863 filter_dlg_destroy_cb(GtkWidget *win, gpointer data)
865 filter_list_type_t list = *(filter_list_type_t *)data;
868 /* Get the button that requested that we be popped up, if any.
869 (It should arrange to destroy us if it's destroyed, so
870 that we don't get a pointer to a non-existent window here.) */
871 button = OBJECT_GET_DATA(win, E_FILT_BUTTON_PTR_KEY);
873 if (button != NULL) {
874 /* Tell it we no longer exist. */
875 OBJECT_SET_DATA(button, E_FILT_DIALOG_PTR_KEY, NULL);
877 /* This is an editing dialog popped up from, for example,
878 a menu item; note that we no longer have one. */
883 g_assert(win == global_cfilter_w);
884 global_cfilter_w = NULL;
888 g_assert_not_reached();
893 /* Remove this from the list of filter dialog windows. */
894 forget_filter_dialog(win, list);
897 #if GTK_MAJOR_VERSION < 2
899 filter_sel_list_button_cb(GtkWidget *widget, GdkEventButton *event,
903 filter_sel_list_button_cb(GtkWidget *list, GdkEventButton *event,
907 #if GTK_MAJOR_VERSION < 2
908 GtkWidget *list = func_data;
910 void (* func)(GtkWidget *, gpointer, gpointer);
912 gpointer func_activate;
914 #if GTK_MAJOR_VERSION < 2
915 if (!GTK_IS_LIST_ITEM(widget)) return FALSE;
917 if (event->type == GDK_2BUTTON_PRESS) {
918 func = OBJECT_GET_DATA(list, E_FILT_DBLFUNC_KEY);
919 func_arg = OBJECT_GET_DATA(list, E_FILT_DBLARG_KEY);
920 func_activate = OBJECT_GET_DATA(list, E_FILT_DBLACTIVATE_KEY);
923 (*func)(list, func_arg, func_activate);
929 #if GTK_MAJOR_VERSION < 2
931 filter_sel_list_cb(GtkWidget *l, gpointer data _U_)
934 filter_sel_list_cb(GtkTreeSelection *sel, gpointer data _U_)
937 #if GTK_MAJOR_VERSION < 2
938 GtkWidget *main_w = gtk_widget_get_toplevel(l);
942 GtkWidget *filter_l = GTK_WIDGET(gtk_tree_selection_get_tree_view(sel));
943 GtkWidget *main_w = gtk_widget_get_toplevel(filter_l);
947 GtkWidget *name_te = OBJECT_GET_DATA(main_w, E_FILT_NAME_TE_KEY);
948 GtkWidget *filter_te = OBJECT_GET_DATA(main_w, E_FILT_FILTER_TE_KEY);
949 GtkWidget *chg_bt = OBJECT_GET_DATA(main_w, E_FILT_CHG_BT_KEY);
950 GtkWidget *copy_bt = OBJECT_GET_DATA(main_w, E_FILT_COPY_BT_KEY);
951 GtkWidget *del_bt = OBJECT_GET_DATA(main_w, E_FILT_DEL_BT_KEY);
953 gchar *name = NULL, *strval = NULL;
955 gint sensitivity = FALSE;
957 #if GTK_MAJOR_VERSION < 2
959 sl = GTK_LIST(l)->selection;
963 if (sl) { /* Something was selected */
964 l_item = GTK_OBJECT(sl->data);
965 flp = (GList *) OBJECT_GET_DATA(l_item, E_FILT_LIST_ITEM_MODEL_KEY);
967 if (gtk_tree_selection_get_selected(sel, &model, &iter)) {
968 gtk_tree_model_get(model, &iter, 1, &flp, -1);
971 filt = (filter_def *) flp->data;
972 name = g_strdup(filt->name);
973 strval = g_strdup(filt->strval);
979 * Did you know that this function is called when the window is destroyed?
981 * This means that we have to:
983 * attach to the top-level window data items containing pointers to
984 * the widgets we affect here;
986 * give each of those widgets their own destroy callbacks;
988 * clear that pointer when the widget is destroyed;
990 * don't do anything to the widget if the pointer we get back is
993 * so that if we're called after any of the widgets we'd affect are
994 * destroyed, we know that we shouldn't do anything to those widgets.
997 gtk_entry_set_text(GTK_ENTRY(name_te), name ? name : "");
998 if (filter_te != NULL)
999 gtk_entry_set_text(GTK_ENTRY(filter_te), strval ? strval : "");
1001 gtk_widget_set_sensitive(chg_bt, sensitivity);
1002 if (copy_bt != NULL)
1003 gtk_widget_set_sensitive(copy_bt, sensitivity);
1005 gtk_widget_set_sensitive(del_bt, sensitivity);
1012 /* To do: add input checking to each of these callbacks */
1014 /* Structure containing arguments to be passed to "new_filter_cb()".
1016 "active_filter_l" is the list in the dialog box in which "New" or
1017 "Copy" was clicked; in that dialog box, but not in any other dialog
1018 box, we select the newly created list item.
1020 "nflp" is the GList member in the model (filter list) for the new
1023 GtkWidget *active_filter_l;
1025 } new_filter_cb_args_t;
1028 new_filter_cb(gpointer data, gpointer user_data)
1030 GtkWidget *main_w = data;
1031 #if GTK_MAJOR_VERSION < 2
1032 GtkWidget *filter_l = OBJECT_GET_DATA(main_w, E_FILT_FILTER_L_KEY);
1033 GtkWidget *nl_lb, *nl_item;
1035 GtkTreeView *filter_l;
1036 GtkListStore *store;
1039 new_filter_cb_args_t *args = user_data;
1040 filter_def *nfilt = args->nflp->data;
1042 #if GTK_MAJOR_VERSION < 2
1043 nl_lb = gtk_label_new(nfilt->name);
1044 nl_item = gtk_list_item_new();
1045 gtk_misc_set_alignment(GTK_MISC(nl_lb), 0.0, 0.5);
1046 gtk_container_add(GTK_CONTAINER(nl_item), nl_lb);
1047 gtk_widget_show(nl_lb);
1048 gtk_container_add(GTK_CONTAINER(filter_l), nl_item);
1049 gtk_widget_show(nl_item);
1050 OBJECT_SET_DATA(nl_item, E_FILT_LBL_KEY, nl_lb);
1051 OBJECT_SET_DATA(GTK_OBJECT(nl_item), E_FILT_LIST_ITEM_MODEL_KEY,
1053 if (filter_l == args->active_filter_l) {
1054 /* Select the item. */
1055 gtk_list_select_child(GTK_LIST(filter_l), nl_item);
1058 filter_l = GTK_TREE_VIEW(OBJECT_GET_DATA(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),
1071 filter_new_bt_clicked_cb(GtkWidget *w, gpointer data)
1073 GtkWidget *main_w = gtk_widget_get_toplevel(w);
1074 GtkWidget *name_te = OBJECT_GET_DATA(main_w, E_FILT_NAME_TE_KEY);
1075 GtkWidget *filter_te = OBJECT_GET_DATA(main_w, E_FILT_FILTER_TE_KEY);
1076 GtkWidget *filter_l = OBJECT_GET_DATA(main_w, E_FILT_FILTER_L_KEY);
1077 filter_list_type_t list = *(filter_list_type_t *)data;
1079 const gchar *name, *strval;
1080 new_filter_cb_args_t args;
1082 name = gtk_entry_get_text(GTK_ENTRY(name_te));
1083 strval = gtk_entry_get_text(GTK_ENTRY(filter_te));
1085 /* if the user didn't entered a name, set default one */
1086 if (strlen(name) == 0) {
1090 /* if the user didn't entered a string value, set default one */
1091 if (strlen(strval) == 0) {
1095 /* Add a new entry to the filter list. */
1096 fl_entry = add_to_filter_list(list, name, strval);
1098 /* Update all the filter list widgets, not just the one in
1099 the dialog box in which we clicked on "Copy". */
1100 args.active_filter_l = filter_l;
1101 args.nflp = fl_entry;
1102 g_list_foreach(get_filter_dialog_list(list), new_filter_cb, &args);
1106 #if GTK_MAJOR_VERSION < 2
1108 chg_list_item_cb(GtkWidget *nl_item, gpointer data)
1111 chg_list_item_cb(GtkTreeModel *model, GtkTreePath *path _U_, GtkTreeIter *iter,
1116 filter_def *filt = flp->data;
1117 #if GTK_MAJOR_VERSION < 2
1119 GTK_LABEL(OBJECT_GET_DATA(nl_item, E_FILT_LBL_KEY));
1121 OBJECT_GET_DATA(nl_item, E_FILT_LIST_ITEM_MODEL_KEY);
1126 #if GTK_MAJOR_VERSION >= 2
1127 gtk_tree_model_get(model, iter, 1, &nl_model, -1);
1129 /* Is this the item corresponding to the filter list item in question? */
1130 if (flp == nl_model) {
1131 /* Yes - change the label to correspond to the new name for the
1133 #if GTK_MAJOR_VERSION < 2
1134 gtk_label_set(nl_lb, filt->name);
1136 gtk_list_store_set(GTK_LIST_STORE(model), iter, 0, filt->name, -1);
1140 #if GTK_MAJOR_VERSION >= 2
1146 chg_filter_cb(gpointer data, gpointer user_data)
1148 GtkWidget *main_w = data;
1149 GtkWidget *filter_l = OBJECT_GET_DATA(main_w, E_FILT_FILTER_L_KEY);
1151 #if GTK_MAJOR_VERSION < 2
1152 gtk_container_foreach(GTK_CONTAINER(filter_l), chg_list_item_cb, user_data);
1154 gtk_tree_model_foreach(gtk_tree_view_get_model(GTK_TREE_VIEW(filter_l)),
1155 chg_list_item_cb, user_data);
1160 filter_name_te_changed_cb(GtkWidget *w, gpointer data)
1162 GtkWidget *main_w = gtk_widget_get_toplevel(w);
1163 GtkWidget *name_te = OBJECT_GET_DATA(main_w, E_FILT_NAME_TE_KEY);
1164 GtkWidget *filter_te = OBJECT_GET_DATA(main_w, E_FILT_FILTER_TE_KEY);
1165 GtkWidget *filter_l = OBJECT_GET_DATA(main_w, E_FILT_FILTER_L_KEY);
1168 filter_list_type_t list = *(filter_list_type_t *)data;
1169 const gchar *name = "";
1170 const gchar *strval = "";
1172 #if GTK_MAJOR_VERSION < 2
1177 GtkTreeSelection *sel;
1178 GtkTreeModel *model;
1182 #if GTK_MAJOR_VERSION < 2
1183 sl = GTK_LIST(filter_l)->selection;
1185 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(filter_l));
1187 name = gtk_entry_get_text(GTK_ENTRY(name_te));
1188 strval = gtk_entry_get_text(GTK_ENTRY(filter_te));
1190 if (DFILTER_LIST == list) {
1191 /* colorize filter string entry */
1192 filter_te_syntax_check_cb(filter_te);
1195 /* if something was selected */
1196 #if GTK_MAJOR_VERSION < 2
1198 l_item = GTK_OBJECT(sl->data);
1199 fl_entry = (GList *) OBJECT_GET_DATA(l_item,
1200 E_FILT_LIST_ITEM_MODEL_KEY);
1201 nl_lb = (GtkLabel *) OBJECT_GET_DATA(l_item, E_FILT_LBL_KEY);
1202 if (fl_entry != NULL && nl_lb != NULL) {
1204 if (gtk_tree_selection_get_selected(sel, &model, &iter)) {
1205 gtk_tree_model_get(model, &iter, 1, &fl_entry, -1);
1206 if (fl_entry != NULL) {
1208 filt = (filter_def *) fl_entry->data;
1210 if (strlen(name) > 0 && strlen(strval) > 0 && filt) {
1212 g_free(filt->strval);
1213 filt->name = g_strdup(name);
1214 filt->strval = g_strdup(strval);
1216 /* Update all the filter list widgets, not just the one in
1217 the dialog box in which we clicked on "Copy". */
1218 g_list_foreach(get_filter_dialog_list(list), chg_filter_cb,
1226 delete_filter_cb(gpointer data, gpointer user_data)
1228 GtkWidget *main_w = data;
1229 GtkWidget *filter_l = OBJECT_GET_DATA(main_w, E_FILT_FILTER_L_KEY);
1230 #if GTK_MAJOR_VERSION < 2
1231 gint pos = *(gint *)user_data;
1233 gchar *pos = (gchar *)user_data;
1234 GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW(filter_l));
1238 #if GTK_MAJOR_VERSION < 2
1239 gtk_list_clear_items(GTK_LIST(filter_l), pos, pos + 1);
1241 gtk_tree_model_get_iter_from_string(model, &iter, pos);
1242 gtk_list_store_remove(GTK_LIST_STORE(model), &iter);
1247 filter_del_bt_clicked_cb(GtkWidget *w, gpointer data)
1249 GtkWidget *main_w = gtk_widget_get_toplevel(w);
1250 GtkWidget *filter_l = OBJECT_GET_DATA(main_w, E_FILT_FILTER_L_KEY);
1251 filter_list_type_t list = *(filter_list_type_t *)data;
1253 #if GTK_MAJOR_VERSION < 2
1259 GtkTreeSelection *sel;
1260 GtkTreeModel *model;
1265 #if GTK_MAJOR_VERSION < 2
1266 sl = GTK_LIST(filter_l)->selection;
1267 if (sl) { /* Something was selected */
1268 l_item = GTK_OBJECT(sl->data);
1269 pos = gtk_list_child_position(GTK_LIST(filter_l),
1270 GTK_WIDGET(l_item));
1271 fl_entry = (GList *) OBJECT_GET_DATA(l_item, E_FILT_LIST_ITEM_MODEL_KEY);
1273 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(filter_l));
1274 /* If something was selected */
1275 if (gtk_tree_selection_get_selected(sel, &model, &iter)) {
1276 gtk_tree_model_get(model, &iter, 1, &fl_entry, -1);
1277 path = gtk_tree_model_get_path(model, &iter);
1278 pos = gtk_tree_path_to_string(path);
1279 gtk_tree_path_free(path);
1281 if (fl_entry != NULL) {
1282 /* Remove the entry from the filter list. */
1283 remove_from_filter_list(list, fl_entry);
1285 /* Update all the filter list widgets, not just the one in
1286 the dialog box in which we clicked on "Delete". */
1287 #if GTK_MAJOR_VERSION < 2
1288 g_list_foreach(get_filter_dialog_list(list), delete_filter_cb,
1291 g_list_foreach(get_filter_dialog_list(list), delete_filter_cb, pos);
1294 #if GTK_MAJOR_VERSION >= 2
1301 filter_add_expr_bt_cb(GtkWidget *w _U_, gpointer main_w_arg)
1303 GtkWidget *main_w = GTK_WIDGET(main_w_arg);
1304 GtkWidget *filter_te;
1306 filter_te = OBJECT_GET_DATA(main_w, E_FILT_FILTER_TE_KEY);
1307 dfilter_expr_dlg_new(filter_te);
1311 color_filter_te(GtkWidget *w, guint16 red, guint16 green, guint16 blue)
1321 style = gtk_style_copy(gtk_widget_get_style(w));
1322 style->base[GTK_STATE_NORMAL] = bg;
1323 gtk_widget_set_style(w, style);
1324 gtk_style_unref(style);
1328 colorize_filter_te_as_empty(GtkWidget *w)
1331 color_filter_te(w, 0xFFFF, 0xFFFF, 0xFFFF);
1335 colorize_filter_te_as_invalid(GtkWidget *w)
1338 color_filter_te(w, 0xFFFF, 0xAFFF, 0xAFFF);
1342 colorize_filter_te_as_valid(GtkWidget *w)
1345 color_filter_te(w, 0xAFFF, 0xFFFF, 0xAFFF);
1349 filter_te_syntax_check_cb(GtkWidget *w)
1351 const gchar *strval;
1354 strval = gtk_entry_get_text(GTK_ENTRY(w));
1356 /* colorize filter string entry */
1357 if (strval && dfilter_compile(strval, &dfp)) {
1360 if (strlen(strval) == 0)
1361 colorize_filter_te_as_empty(w);
1363 colorize_filter_te_as_valid(w);
1365 colorize_filter_te_as_invalid(w);