3 * Allow the user to construct a subexpression of a display filter
4 * expression, testing a particular field; display the tree of fields
5 * and the relations and values with which it can be compared.
7 * Copyright 2000, Jeffrey C. Foster <jfoste@woodward.com> and
8 * Guy Harris <guy@alum.mit.edu>
12 * Wireshark - Network traffic analyzer
13 * By Gerald Combs <gerald@wireshark.org>
14 * Copyright 2000 Gerald Combs
16 * This program is free software; you can redistribute it and/or
17 * modify it under the terms of the GNU General Public License
18 * as published by the Free Software Foundation; either version 2
19 * of the License, or (at your option) any later version.
21 * This program is distributed in the hope that it will be useful,
22 * but WITHOUT ANY WARRANTY; without even the implied warranty of
23 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24 * GNU General Public License for more details.
26 * You should have received a copy of the GNU General Public License
27 * along with this program; if not, write to the Free Software
28 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
32 * may want to check the enable field to decide if protocol should be in tree
33 * improve speed of dialog box creation
34 * - I believe this is slow because of tree widget creation.
35 * 1) could improve the widget
36 * 2) keep a copy in memory after the first time.
37 * user can pop multiple tree dialogs by pressing the "Tree" button multiple
38 * times. not a good thing.
39 * Sort the protocols and children
50 #include "../globals.h"
51 #include "../simple_dialog.h"
54 #include "gtk/gui_utils.h"
55 #include "gtk/dlg_utils.h"
56 #include "gtk/proto_dlg.h"
57 #include "gtk/filter_dlg.h"
58 #include "gtk/dfilter_expr_dlg.h"
61 #define E_DFILTER_EXPR_TREE_KEY "dfilter_expr_tree"
62 #define E_DFILTER_EXPR_CURRENT_VAR_KEY "dfilter_expr_current_var"
63 #define E_DFILTER_EXPR_RELATION_LIST_KEY "dfilter_expr_relation_list"
64 #define E_DFILTER_EXPR_RANGE_LABEL_KEY "dfilter_expr_range_label"
65 #define E_DFILTER_EXPR_RANGE_ENTRY_KEY "dfilter_expr_range_entry"
66 #define E_DFILTER_EXPR_VALUE_LABEL_KEY "dfilter_expr_value_label"
67 #define E_DFILTER_EXPR_VALUE_ENTRY_KEY "dfilter_expr_value_entry"
68 #define E_DFILTER_EXPR_VALUE_LIST_LABEL_KEY "dfilter_expr_value_list_label"
69 #define E_DFILTER_EXPR_VALUE_LIST_KEY "dfilter_expr_value_list"
70 #define E_DFILTER_EXPR_VALUE_LIST_SW_KEY "dfilter_expr_value_list_sw"
71 #define E_DFILTER_EXPR_OK_BT_KEY "dfilter_expr_accept_bt"
72 #define E_DFILTER_EXPR_VALUE_KEY "dfilter_expr_value"
74 typedef struct protocol_data {
79 static void show_relations(GtkWidget *relation_list, ftenum_t ftype);
80 static gboolean relation_is_presence_test(const char *string);
81 static void add_relation_list(GtkWidget *relation_list, const char *relation, gboolean sensitive);
82 static void build_boolean_values(GtkWidget *value_list_scrolled_win,
83 GtkWidget *value_list,
84 const true_false_string *values);
85 static void build_enum_values(GtkWidget *value_list_scrolled_win,
86 GtkWidget *value_list,
87 const value_string *values);
88 static void add_value_list_item(GtkWidget *value_list, const gchar *string,
90 static void display_value_fields(header_field_info *hfinfo,
91 gboolean is_comparison, GtkWidget *value_label,
92 GtkWidget *value_entry,
93 GtkWidget *value_list_label, GtkWidget *value_list,
94 GtkWidget *value_list_scrolled_win,
95 GtkWidget *range_label,
96 GtkWidget *range_entry);
99 * Note that this is called every time the user clicks on an item,
100 * whether it is already selected or not.
103 field_select_row_cb(GtkTreeSelection *sel, gpointer tree)
105 GtkWidget *window = gtk_widget_get_toplevel(tree);
106 GtkWidget *relation_list = g_object_get_data(G_OBJECT(window),
107 E_DFILTER_EXPR_RELATION_LIST_KEY);
108 GtkWidget *range_label = g_object_get_data(G_OBJECT(window),
109 E_DFILTER_EXPR_RANGE_LABEL_KEY);
110 GtkWidget *range_entry = g_object_get_data(G_OBJECT(window),
111 E_DFILTER_EXPR_RANGE_ENTRY_KEY);
112 GtkWidget *value_label = g_object_get_data(G_OBJECT(window),
113 E_DFILTER_EXPR_VALUE_LABEL_KEY);
114 GtkWidget *value_entry = g_object_get_data(G_OBJECT(window),
115 E_DFILTER_EXPR_VALUE_ENTRY_KEY);
116 GtkWidget *value_list_label = g_object_get_data(G_OBJECT(window),
117 E_DFILTER_EXPR_VALUE_LIST_LABEL_KEY);
118 GtkWidget *value_list = g_object_get_data(G_OBJECT(window),
119 E_DFILTER_EXPR_VALUE_LIST_KEY);
120 GtkWidget *value_list_scrolled_win = g_object_get_data(G_OBJECT(window),
121 E_DFILTER_EXPR_VALUE_LIST_SW_KEY);
122 GtkWidget *ok_bt = g_object_get_data(G_OBJECT(window),
123 E_DFILTER_EXPR_OK_BT_KEY);
124 header_field_info *hfinfo, *cur_hfinfo;
125 const char *value_type;
126 char value_label_string[1024+1]; /* XXX - should be large enough */
130 if (!gtk_tree_selection_get_selected(sel, &model, &iter))
132 gtk_tree_model_get(model, &iter, 1, &hfinfo, -1);
135 * What was the item that was last selected?
137 cur_hfinfo = g_object_get_data(G_OBJECT(window), E_DFILTER_EXPR_CURRENT_VAR_KEY);
138 if (cur_hfinfo == hfinfo) {
140 * It's still selected; no need to change anything.
146 * Mark it as currently selected.
148 g_object_set_data(G_OBJECT(window), E_DFILTER_EXPR_CURRENT_VAR_KEY, hfinfo);
150 show_relations(relation_list, hfinfo->type);
153 * Set the label for the value to indicate what type of value
156 value_type = ftype_pretty_name(hfinfo->type);
157 if (value_type != NULL) {
159 * Indicate what type of value it is.
161 g_snprintf(value_label_string, sizeof value_label_string,
162 "Value (%s)", value_type);
163 gtk_label_set_text(GTK_LABEL(value_label), value_label_string);
167 * Clear the entry widget for the value, as whatever
168 * was there before doesn't apply.
170 gtk_entry_set_text(GTK_ENTRY(value_entry), "");
172 switch (hfinfo->type) {
176 * The list of values should be the strings for "true"
177 * and "false"; show them in the value list.
179 build_boolean_values(value_list_scrolled_win, value_list,
192 * If this has a value_string table (not a range_string table) associated with it,
193 * fill up the list of values, otherwise clear the list of values.
195 /* XXX: ToDo: Implement "range-string" filter ? */
196 if ((hfinfo->strings != NULL) & !(hfinfo->display & BASE_RANGE_STRING)) {
197 build_enum_values(value_list_scrolled_win, value_list, hfinfo->strings);
199 gtk_list_store_clear(GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(value_list))));
204 * Clear the list of values.
206 gtk_list_store_clear(GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(value_list))));
211 * Display various items for the value, as appropriate.
212 * The relation we start out with is never a comparison.
214 display_value_fields(hfinfo, FALSE, value_label, value_entry,
215 value_list_label, value_list, value_list_scrolled_win, range_label, range_entry);
218 * XXX - in browse mode, there always has to be something
219 * selected, so this should always be sensitive.
221 gtk_widget_set_sensitive(ok_bt, TRUE);
225 show_relations(GtkWidget *relation_list, ftenum_t ftype)
229 * Clear out the currently displayed list of relations.
231 gtk_list_store_clear(GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(relation_list))));
234 * Add the supported relations.
236 add_relation_list(relation_list, "is present", TRUE);
237 add_relation_list(relation_list, "==",
238 ftype_can_eq(ftype) || (ftype_can_slice(ftype) && ftype_can_eq(FT_BYTES)));
239 add_relation_list(relation_list, "!=",
240 ftype_can_ne(ftype) || (ftype_can_slice(ftype) && ftype_can_ne(FT_BYTES)));
241 add_relation_list(relation_list, ">",
242 ftype_can_gt(ftype) || (ftype_can_slice(ftype) && ftype_can_gt(FT_BYTES)));
244 add_relation_list(relation_list, "<",
245 ftype_can_lt(ftype) || (ftype_can_slice(ftype) && ftype_can_lt(FT_BYTES)));
246 add_relation_list(relation_list, ">=",
247 ftype_can_ge(ftype) || (ftype_can_slice(ftype) && ftype_can_ge(FT_BYTES)));
248 add_relation_list(relation_list, "<=",
249 ftype_can_le(ftype) || (ftype_can_slice(ftype) && ftype_can_le(FT_BYTES)));
250 add_relation_list(relation_list, "contains",
251 ftype_can_contains(ftype) || (ftype_can_slice(ftype) && ftype_can_contains(FT_BYTES)));
252 #if defined(HAVE_LIBPCRE) || GLIB_CHECK_VERSION(2,14,0)
253 add_relation_list(relation_list, "matches",
254 ftype_can_matches(ftype) || (ftype_can_slice(ftype) && ftype_can_matches(FT_BYTES)));
257 gtk_tree_model_get_iter_first(gtk_tree_view_get_model(GTK_TREE_VIEW(relation_list)), &iter);
258 gtk_tree_selection_select_iter(gtk_tree_view_get_selection(GTK_TREE_VIEW(relation_list)), &iter);
262 * Given a string that represents a test to be made on a field, returns
263 * TRUE if it tests for the field's presence, FALSE otherwise.
266 relation_is_presence_test(const char *string)
268 return (strcmp(string, "is present") == 0);
272 add_relation_list(GtkWidget *relation_list, const char *relation, gboolean sensitive)
274 GtkListStore *store = GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(relation_list)));
277 /* XXX: I currently see no way to insensitive the item,
278 * so for a first step, just don't show it (as before these changes :-) */
283 gtk_list_store_append(store, &iter);
284 gtk_list_store_set(store, &iter, 0, relation, -1);
288 relation_list_sel_cb(GtkTreeSelection *sel, gpointer user_data _U_)
290 GtkWidget *window = gtk_widget_get_toplevel(GTK_WIDGET(gtk_tree_selection_get_tree_view(sel)));
291 GtkWidget *range_label =
292 g_object_get_data(G_OBJECT(window), E_DFILTER_EXPR_RANGE_LABEL_KEY);
293 GtkWidget *range_entry =
294 g_object_get_data(G_OBJECT(window), E_DFILTER_EXPR_RANGE_ENTRY_KEY);
295 GtkWidget *value_label =
296 g_object_get_data(G_OBJECT(window), E_DFILTER_EXPR_VALUE_LABEL_KEY);
297 GtkWidget *value_entry =
298 g_object_get_data(G_OBJECT(window), E_DFILTER_EXPR_VALUE_ENTRY_KEY);
299 GtkWidget *value_list_label =
300 g_object_get_data(G_OBJECT(window), E_DFILTER_EXPR_VALUE_LIST_LABEL_KEY);
301 GtkWidget *value_list =
302 g_object_get_data(G_OBJECT(window), E_DFILTER_EXPR_VALUE_LIST_KEY);
303 GtkWidget *value_list_scrolled_win =
304 g_object_get_data(G_OBJECT(window), E_DFILTER_EXPR_VALUE_LIST_SW_KEY);
305 header_field_info *hfinfo =
306 g_object_get_data(G_OBJECT(window), E_DFILTER_EXPR_CURRENT_VAR_KEY);
312 * What's the relation?
314 if (!gtk_tree_selection_get_selected(sel, &model, &iter))
316 gtk_tree_model_get(model, &iter, 0, &item_str, -1);
319 * Update the display of various items for the value, as appropriate.
321 display_value_fields(hfinfo,
322 !relation_is_presence_test(item_str),
323 value_label, value_entry, value_list_label, value_list,
324 value_list_scrolled_win, range_label, range_entry);
329 build_boolean_values(GtkWidget *value_list_scrolled_win, GtkWidget *value_list,
330 const true_false_string *values)
332 static const true_false_string true_false = { "True", "False" };
333 GtkTreeSelection *sel;
336 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(value_list));
339 * Clear out the items for the list, and put in the names
340 * from the value_string list.
342 gtk_list_store_clear(GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(value_list))));
345 * Put the list in single mode, so we don't get any selection
346 * events while we're building it (i.e., so we don't get any
347 * on a list item BEFORE WE GET TO SET THE DATA FOR THE LIST
348 * ITEM SO THAT THE HANDLER CAN HANDLE IT).
350 gtk_tree_selection_set_mode(sel, GTK_SELECTION_SINGLE);
356 values = &true_false;
357 add_value_list_item(value_list, values->true_string, (const gpointer) values);
358 add_value_list_item(value_list, values->false_string, NULL);
361 * OK, we're done, so we can finally put it in browse mode.
362 * Select the first item, so that the user doesn't have to, under
363 * the assumption that they're most likely to test if something
364 * is true, not false.
366 gtk_tree_selection_set_mode(sel, GTK_SELECTION_BROWSE);
367 gtk_tree_model_get_iter_first(gtk_tree_view_get_model(GTK_TREE_VIEW(value_list)), &iter);
368 gtk_tree_selection_select_iter(sel, &iter);
370 gtk_widget_show_all(value_list_scrolled_win);
374 build_enum_values(GtkWidget *value_list_scrolled_win _U_, GtkWidget *value_list,
375 const value_string *values)
377 GtkTreeSelection *sel;
379 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(value_list));
381 * Clear out the items for the list, and put in the names
382 * from the value_string list.
384 gtk_list_store_clear(GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(value_list))));
387 * Put the list in single mode, so we don't get any selection
388 * events while we're building it (i.e., so we don't get any
389 * on a list item BEFORE WE GET TO SET THE DATA FOR THE LIST
390 * ITEM SO THAT THE HANDLER CAN HANDLE IT).
392 gtk_tree_selection_set_mode(sel, GTK_SELECTION_SINGLE);
397 while (values->strptr != NULL) {
398 add_value_list_item(value_list, values->strptr,
399 (const gpointer) values);
404 * OK, we're done, so we can finally put it in browse mode.
406 gtk_tree_selection_set_mode(sel, GTK_SELECTION_BROWSE);
410 add_value_list_item(GtkWidget *value_list, const gchar *string, const gpointer data)
412 GtkListStore *store = GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(value_list)));
415 gtk_list_store_append(store, &iter);
416 gtk_list_store_set(store, &iter, 0, string, 1, data, -1);
420 * Show or hide the various values fields as appropriate for the field
421 * and currently-selected relation.
424 display_value_fields(header_field_info *hfinfo, gboolean is_comparison,
425 GtkWidget *value_label, GtkWidget *value_entry,
426 GtkWidget *value_list_label,
427 GtkWidget *value_list _U_,
428 GtkWidget *value_list_scrolled_win, GtkWidget *range_label,
429 GtkWidget *range_entry)
432 gboolean show_value_label = FALSE;
433 gboolean show_value_list = FALSE;
434 gboolean show_range = FALSE;
439 * this is an FT_NONE variable, in which case you can
440 * only check whether it's present or absent in the
445 * this is a Boolean variable, in which case you
446 * can't specify a value to compare with, you can
447 * only specify whether to test for the Boolean
448 * being true or to test for it being false
452 * this isn't a Boolean variable, in which case you
453 * can test for its presence in the protocol tree,
454 * and the relation is such a test, in
455 * which case you don't compare with a value
457 * so we hide the value entry.
460 switch (hfinfo->type) {
464 show_value_label = TRUE; /* XXX: Allow value entry (contrary to the comment above) ?? */
465 show_value_list = TRUE;
478 show_value_label = TRUE;
479 if ((hfinfo->strings != NULL) && !(hfinfo->display & BASE_RANGE_STRING)) {
481 * We have a list of values to show.
483 show_value_list = TRUE;
490 * There is no list of names for values; only show the value_label if needed.
493 show_value_label = TRUE;
497 gtk_widget_set_sensitive(value_label, show_value_label);
498 gtk_widget_set_sensitive(value_entry, show_value_label);
500 gtk_widget_set_sensitive(value_list_label, show_value_list);
501 gtk_widget_set_sensitive(value_list_scrolled_win, show_value_list);
504 * Is this a comparison, and are ranges supported by this type?
505 * If both are true, show the range stuff, otherwise hide it.
507 show_range = (is_comparison && ftype_can_slice(hfinfo->type));
508 gtk_widget_set_sensitive(range_label, show_range);
509 gtk_widget_set_sensitive(range_entry, show_range);
513 value_list_sel_cb(GtkTreeSelection *sel, gpointer value_entry_arg)
515 GtkWidget *value_entry = value_entry_arg;
516 GtkWidget *window = gtk_widget_get_toplevel(GTK_WIDGET(gtk_tree_selection_get_tree_view(sel)));
519 header_field_info *hfinfo = g_object_get_data(G_OBJECT(window),
520 E_DFILTER_EXPR_CURRENT_VAR_KEY);
521 const value_string *value = NULL;
522 gchar *value_display_string = NULL;
524 if (!gtk_tree_selection_get_selected(sel, &model, &iter))
526 gtk_tree_model_get(model, &iter, 1, &value, -1);
529 * This should either be a numeric type or a Boolean type.
531 if (hfinfo->type == FT_BOOLEAN) {
533 * Boolean type; if the value key for the selected item
534 * is non-null, it's the item for "true", otherwise it's
535 * the item for "false". Compare with 1 if we're
536 * testing for "true", and compare with 0 if we're
537 * testing for "false".
540 value_display_string = g_strdup("1");
542 value_display_string = g_strdup("0");
545 * Numeric type; get the value corresponding to the
546 * selected item, and display it in the base for this
549 switch ((hfinfo->display) & BASE_DISPLAY_E_MASK) {
553 switch (hfinfo->type) {
559 value_display_string = g_strdup_printf("%u", value->value);
566 value_display_string = g_strdup_printf("%d", value->value);
570 g_assert_not_reached();
575 value_display_string = g_strdup_printf("0x%x", value->value);
579 value_display_string = g_strdup_printf("%#o", value->value);
583 g_assert_not_reached();
587 gtk_entry_set_text(GTK_ENTRY(value_entry), value_display_string);
588 g_free (value_display_string);
592 dfilter_report_bad_value(const char *format, ...)
594 char error_msg_buf[1024];
597 va_start(args, format);
598 g_vsnprintf(error_msg_buf, sizeof error_msg_buf, format, args);
601 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "%s", error_msg_buf);
605 dfilter_expr_dlg_accept_cb(GtkWidget *w, gpointer filter_te_arg)
607 GtkWidget *filter_te = filter_te_arg;
608 GtkWidget *window = gtk_widget_get_toplevel(w);
609 GtkWidget *relation_list =
610 g_object_get_data(G_OBJECT(window), E_DFILTER_EXPR_RELATION_LIST_KEY);
611 GtkWidget *range_entry =
612 g_object_get_data(G_OBJECT(window), E_DFILTER_EXPR_RANGE_ENTRY_KEY);
613 GtkWidget *value_entry =
614 g_object_get_data(G_OBJECT(window), E_DFILTER_EXPR_VALUE_ENTRY_KEY);
615 header_field_info *hfinfo;
617 gchar *range_str, *stripped_range_str;
618 gchar *value_str, *stripped_value_str;
622 gboolean can_compare;
629 * Get the variable to be tested.
631 hfinfo = g_object_get_data(G_OBJECT(window), E_DFILTER_EXPR_CURRENT_VAR_KEY);
634 * Get the relation operator to use.
636 if (gtk_tree_selection_get_selected(gtk_tree_view_get_selection(GTK_TREE_VIEW(relation_list)),
638 gtk_tree_model_get(model, &iter, 0, &item_str, -1);
640 /* Nothing selected */
645 * Get the range to use, if any.
647 if (GTK_WIDGET_SENSITIVE(range_entry)) {
648 range_str = g_strdup(gtk_entry_get_text(GTK_ENTRY(range_entry)));
650 * XXX - strip this even for strings?
651 * Doing so for strings means you can't match a string that has
652 * leading or trailing whitespace, but you can't see trailing
653 * whitespace in a text field, so it's not clear that it's
654 * a good idea to allow that.
656 stripped_range_str = g_strstrip(range_str);
657 if (strcmp(stripped_range_str, "") == 0) {
659 * No range was specified.
663 stripped_range_str = NULL;
667 * XXX - check it for validity?
671 stripped_range_str = NULL;
675 * If a range was specified, the type of the LHS of the
676 * comparison is FT_BYTES; otherwise, it's the type of the field.
678 if (range_str == NULL)
679 ftype = hfinfo->type;
684 * Make sure the relation is valid for the type in question.
685 * We may be offering relations that the type of the field
686 * can't support, because the field's type supports slicing,
687 * and the relation *is* supported on byte strings.
689 if (strcmp(item_str, "==") == 0)
690 can_compare = ftype_can_eq(ftype);
691 else if (strcmp(item_str, "!=") == 0)
692 can_compare = ftype_can_ne(ftype);
693 else if (strcmp(item_str, ">") == 0)
694 can_compare = ftype_can_gt(ftype);
695 else if (strcmp(item_str, "<") == 0)
696 can_compare = ftype_can_lt(ftype);
697 else if (strcmp(item_str, ">=") == 0)
698 can_compare = ftype_can_ge(ftype);
699 else if (strcmp(item_str, "<=") == 0)
700 can_compare = ftype_can_le(ftype);
701 else if (strcmp(item_str, "contains") == 0)
702 can_compare = ftype_can_contains(ftype);
703 else if (strcmp(item_str, "matches") == 0)
704 can_compare = ftype_can_matches(ftype);
706 can_compare = TRUE; /* not a comparison */
708 if (range_str == NULL) {
709 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
710 "That field can't be tested with \"%s\".",
713 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
714 "Ranges of that field can't be tested with \"%s\".",
723 * Get the value to use, if any.
725 if (GTK_WIDGET_SENSITIVE(value_entry)) {
726 value_str = g_strdup(gtk_entry_get_text(GTK_ENTRY(value_entry)));
727 stripped_value_str = g_strstrip(value_str);
728 if (strcmp(stripped_value_str, "") == 0) {
730 * This field takes a value, but they didn't supply
733 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
734 "That field must be compared with a value, "
735 "but you didn't specify a value with which to "
744 * Make sure the value is valid.
746 * If no range string was specified, it must be valid
747 * for the type of the field; if a range string was
748 * specified, must be valid for FT_BYTES.
750 if (strcmp(item_str, "contains") == 0) {
751 fvalue = fvalue_from_unparsed(ftype, stripped_value_str, TRUE,
752 dfilter_report_bad_value);
755 fvalue = fvalue_from_unparsed(ftype, stripped_value_str, FALSE,
756 dfilter_report_bad_value);
758 if (fvalue == NULL) {
762 * The dialog box was already popped up by
763 * "dfilter_report_bad_value()".
773 stripped_value_str = NULL;
777 * Insert the expression at the current cursor position.
778 * If there's a non-whitespace character to the left of it,
779 * insert a blank first; if there's a non-whitespace character
780 * to the right of it, insert a blank after it.
782 pos = gtk_editable_get_position(GTK_EDITABLE(filter_te));
783 chars = gtk_editable_get_chars(GTK_EDITABLE(filter_te), pos, pos + 1);
784 if (strcmp(chars, "") != 0 && !isspace((unsigned char)chars[0]))
785 gtk_editable_insert_text(GTK_EDITABLE(filter_te), " ", 1, &pos);
788 gtk_editable_insert_text(GTK_EDITABLE(filter_te), hfinfo->abbrev,
789 (gint) strlen(hfinfo->abbrev), &pos);
790 if (range_str != NULL) {
791 gtk_editable_insert_text(GTK_EDITABLE(filter_te), "[", 1, &pos);
792 gtk_editable_insert_text(GTK_EDITABLE(filter_te),
793 stripped_range_str, (gint) strlen(stripped_range_str), &pos);
794 gtk_editable_insert_text(GTK_EDITABLE(filter_te), "]", 1, &pos);
797 if (item_str != NULL && !relation_is_presence_test(item_str)) {
798 gtk_editable_insert_text(GTK_EDITABLE(filter_te), " ", 1, &pos);
799 gtk_editable_insert_text(GTK_EDITABLE(filter_te), item_str,
800 (gint) strlen(item_str), &pos);
802 if (value_str != NULL) {
803 gtk_editable_insert_text(GTK_EDITABLE(filter_te), " ", 1, &pos);
805 * XXX - we should do this by generating an appropriate display
806 * filter value string for this field; that requires us to have
807 * a "generate display filter string" method for every FT_ type.
809 switch (hfinfo->type) {
814 case FT_ABSOLUTE_TIME:
816 * Always put quotes around the string.
823 * If the string contains white space, put quotes around it.
825 quote_it = (strpbrk(stripped_value_str, " \t") != NULL);
830 * Put quotes around the string.
832 gtk_editable_insert_text(GTK_EDITABLE(filter_te), "\"",
835 gtk_editable_insert_text(GTK_EDITABLE(filter_te),
836 stripped_value_str, (gint) strlen(stripped_value_str), &pos);
839 * Put quotes around the string.
841 gtk_editable_insert_text(GTK_EDITABLE(filter_te), "\"",
846 chars = gtk_editable_get_chars(GTK_EDITABLE(filter_te), pos + 1, pos + 2);
847 if (strcmp(chars, "") != 0 && !isspace((unsigned char)chars[0]))
848 gtk_editable_insert_text(GTK_EDITABLE(filter_te), " ", 1, &pos);
852 * Put the cursor after the expression we just entered into
853 * the text entry widget.
855 gtk_editable_set_position(GTK_EDITABLE(filter_te), pos);
858 * We're done; destroy the dialog box (which is the top-level
859 * widget for the "Accept" button).
861 window_destroy(window);
866 dfilter_expr_dlg_cancel_cb(GtkWidget *w _U_, gpointer parent_w)
869 * User pressed the cancel button; close the dialog box.
871 window_destroy(GTK_WIDGET(parent_w));
874 /* Treat this as a cancel, by calling "prefs_main_cancel_cb()" */
876 dfilter_expr_dlg_delete_event_cb(GtkWidget *w _U_, GdkEvent *event _U_,
879 dfilter_expr_dlg_cancel_cb(NULL, parent_w);
884 dfilter_expr_dlg_destroy_cb(GtkWidget *w, gpointer filter_te)
887 * The dialog box is being destroyed; disconnect from the
888 * "destroy" signal on the text entry box to which we're
889 * attached, as the handler for that signal is supposed
890 * to destroy us, but we're already gone.
892 g_signal_handlers_disconnect_by_func(filter_te, dfilter_expr_dlg_cancel_cb, w);
896 * Length of string used for protocol fields.
898 #define TAG_STRING_LEN 256
901 dfilter_expr_dlg_new(GtkWidget *filter_te)
903 GtkWidget *window, *main_vb, *main_hb;
905 GtkWidget *field_vb, *field_tree_lb, *field_tree, *tree_scrolled_win;
907 GtkWidget *relation_vb, *relation_label, *relation_list, *relation_list_scrolled_win;
908 /* GtkWidget *relation_present_rb, *relation_equals_rb, *relation_unequals_rb,
909 *relation_greater_rb, *relation_less_rb,
910 *relation_greaterequal_rb, *relation_lessequal_rb,
911 *relation_contains_rb, *relation_matches_rb;*/
913 GtkWidget *value_vb, *value_label, *value_entry;
914 GtkWidget *value_list_label, *value_list_scrolled_win, *value_list;
915 GtkWidget *range_label, *range_entry;
917 GtkWidget *list_bb, *ok_bt, *cancel_bt;
918 header_field_info *hfinfo;
920 protocol_t *protocol;
922 GtkTreeSelection *selection;
923 GtkCellRenderer *renderer;
924 GtkTreeViewColumn *column;
925 GtkListStore *l_store;
926 GtkTreeSelection *l_sel;
928 proto_initialize_all_prefixes();
930 window = dlg_conf_window_new("Wireshark: Filter Expression");
931 gtk_window_set_default_size(GTK_WINDOW(window), 500, 400);
932 gtk_container_set_border_width(GTK_CONTAINER(window), 5);
934 main_vb = gtk_vbox_new(FALSE, 5);
935 gtk_container_set_border_width(GTK_CONTAINER(main_vb), 5);
936 gtk_container_add(GTK_CONTAINER(window), main_vb);
938 main_hb = gtk_hbox_new(FALSE, 5);
939 gtk_container_set_border_width(GTK_CONTAINER(main_hb), 5);
940 gtk_container_add(GTK_CONTAINER(main_vb), main_hb);
942 field_vb = gtk_vbox_new(FALSE, 5);
943 gtk_container_set_border_width(GTK_CONTAINER(field_vb), 5);
944 gtk_container_add(GTK_CONTAINER(main_hb), field_vb);
946 field_tree_lb = gtk_label_new("Field name");
947 gtk_misc_set_alignment(GTK_MISC(field_tree_lb), 0.0f, 0.0f);
948 gtk_box_pack_start(GTK_BOX(field_vb), field_tree_lb, FALSE, FALSE, 0);
950 tree_scrolled_win = scrolled_window_new(NULL, NULL);
951 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(tree_scrolled_win),
953 gtk_box_pack_start(GTK_BOX(field_vb), tree_scrolled_win, TRUE, TRUE, 0);
954 gtk_widget_set_size_request(tree_scrolled_win, 300, -1);
957 store = gtk_tree_store_new(2, G_TYPE_STRING, G_TYPE_POINTER);
958 field_tree = tree_view_new(GTK_TREE_MODEL(store));
959 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(field_tree), FALSE);
960 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(field_tree));
961 gtk_tree_selection_set_mode(selection, GTK_SELECTION_BROWSE);
962 renderer = gtk_cell_renderer_text_new();
963 column = gtk_tree_view_column_new_with_attributes("Field name", renderer,
965 gtk_tree_view_append_column(GTK_TREE_VIEW(field_tree), column);
966 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
967 gtk_tree_view_column_set_sort_column_id(column, 0);
968 g_signal_connect(selection, "changed", G_CALLBACK(field_select_row_cb), field_tree);
969 gtk_container_add(GTK_CONTAINER(tree_scrolled_win), field_tree);
971 relation_vb = gtk_vbox_new(FALSE, 5);
972 gtk_container_set_border_width(GTK_CONTAINER(relation_vb), 5);
973 gtk_container_add(GTK_CONTAINER(main_hb), relation_vb);
975 relation_label = gtk_label_new("Relation");
976 gtk_misc_set_alignment(GTK_MISC(relation_label), 0.0f, 0.0f);
977 gtk_box_pack_start(GTK_BOX(relation_vb), relation_label, FALSE, FALSE, 0);
979 relation_list_scrolled_win = scrolled_window_new(NULL, NULL);
980 /* never use a scrollbar in x direction, show the complete relation string */
981 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(relation_list_scrolled_win),
982 GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
983 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(relation_list_scrolled_win),
986 l_store = gtk_list_store_new(1, G_TYPE_STRING);
987 relation_list = tree_view_new(GTK_TREE_MODEL(l_store));
988 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(relation_list), FALSE);
989 g_object_unref(G_OBJECT(l_store));
990 renderer = gtk_cell_renderer_text_new();
991 column = gtk_tree_view_column_new_with_attributes("relation", renderer,
993 gtk_tree_view_append_column(GTK_TREE_VIEW(relation_list), column);
994 l_sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(relation_list));
995 gtk_tree_selection_set_mode(l_sel, GTK_SELECTION_BROWSE);
996 gtk_container_add(GTK_CONTAINER(relation_list_scrolled_win), relation_list);
997 gtk_box_pack_start(GTK_BOX(relation_vb), relation_list_scrolled_win, TRUE, TRUE, 0);
1000 * OK, show the relation label and range stuff as it would be
1001 * with everything turned on, so it'll request as much space
1002 * as it'll ever need, so the dialog box and widgets start out
1003 * with the right sizes.
1005 * XXX - this doesn't work. It *doesn't* request as much space
1006 * as it'll ever need.
1008 * XXX - FT_UINT8 doesn't support ranges, so even if it did work,
1009 * it wouldn't work right.
1011 * XXX - this no longer affects the range stuff, as that's
1012 * controlled both by the type and by the relational operator
1015 show_relations(relation_list, FT_UINT8);
1018 relation_present_rb = gtk_radio_button_new_with_mnemonic_from_widget(NULL, "is present");
1019 gtk_box_pack_start(GTK_BOX(relation_vb), relation_present_rb, FALSE, FALSE, 0);
1021 relation_equals_rb = gtk_radio_button_new_with_mnemonic_from_widget(GTK_RADIO_BUTTON(relation_present_rb), "==");
1022 gtk_box_pack_start(GTK_BOX(relation_vb), relation_equals_rb, FALSE, FALSE, 0);
1024 relation_unequals_rb = gtk_radio_button_new_with_mnemonic_from_widget(GTK_RADIO_BUTTON(relation_present_rb), "!=");
1025 gtk_box_pack_start(GTK_BOX(relation_vb), relation_unequals_rb, FALSE, FALSE, 0);
1027 relation_greater_rb = gtk_radio_button_new_with_mnemonic_from_widget(GTK_RADIO_BUTTON(relation_present_rb), ">");
1028 gtk_box_pack_start(GTK_BOX(relation_vb), relation_greater_rb, FALSE, FALSE, 0);
1030 relation_less_rb = gtk_radio_button_new_with_mnemonic_from_widget(GTK_RADIO_BUTTON(relation_present_rb), "<");
1031 gtk_box_pack_start(GTK_BOX(relation_vb), relation_less_rb, FALSE, FALSE, 0);
1033 relation_greaterequal_rb = gtk_radio_button_new_with_mnemonic_from_widget(GTK_RADIO_BUTTON(relation_present_rb), ">=");
1034 gtk_box_pack_start(GTK_BOX(relation_vb), relation_greaterequal_rb, FALSE, FALSE, 0);
1036 relation_lessequal_rb = gtk_radio_button_new_with_mnemonic_from_widget(GTK_RADIO_BUTTON(relation_present_rb), "<=");
1037 gtk_box_pack_start(GTK_BOX(relation_vb), relation_lessequal_rb, FALSE, FALSE, 0);
1039 relation_contains_rb = gtk_radio_button_new_with_mnemonic_from_widget(GTK_RADIO_BUTTON(relation_present_rb), "contains");
1040 gtk_box_pack_start(GTK_BOX(relation_vb), relation_contains_rb, FALSE, FALSE, 0);
1042 relation_matches_rb = gtk_radio_button_new_with_mnemonic_from_widget(GTK_RADIO_BUTTON(relation_present_rb), "matches");
1043 gtk_box_pack_start(GTK_BOX(relation_vb), relation_matches_rb, FALSE, FALSE, 0);
1046 value_vb = gtk_vbox_new(FALSE, 5);
1047 gtk_container_set_border_width(GTK_CONTAINER(value_vb), 5);
1048 gtk_container_add(GTK_CONTAINER(main_hb), value_vb);
1050 value_label = gtk_label_new("Value");
1051 gtk_misc_set_alignment(GTK_MISC(value_label), 0.0f, 0.0f);
1052 gtk_box_pack_start(GTK_BOX(value_vb), value_label, FALSE, FALSE, 0);
1054 value_entry = gtk_entry_new();
1055 gtk_box_pack_start(GTK_BOX(value_vb), value_entry, FALSE, FALSE, 0);
1057 value_list_label = gtk_label_new("Predefined values:");
1058 gtk_misc_set_alignment(GTK_MISC(value_list_label), 0.0f, 0.0f);
1059 gtk_box_pack_start(GTK_BOX(value_vb), value_list_label, FALSE, FALSE, 0);
1061 value_list_scrolled_win = scrolled_window_new(NULL, NULL);
1062 gtk_box_pack_start(GTK_BOX(value_vb), value_list_scrolled_win, TRUE,
1065 l_store = gtk_list_store_new(2, G_TYPE_STRING, G_TYPE_POINTER);
1066 value_list = tree_view_new(GTK_TREE_MODEL(l_store));
1067 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(value_list), FALSE);
1068 g_object_unref(G_OBJECT(l_store));
1069 renderer = gtk_cell_renderer_text_new();
1070 column = gtk_tree_view_column_new_with_attributes("value", renderer,
1072 gtk_tree_view_append_column(GTK_TREE_VIEW(value_list), column);
1073 g_signal_connect(gtk_tree_view_get_selection(GTK_TREE_VIEW(value_list)),
1074 "changed", G_CALLBACK(value_list_sel_cb), value_entry);
1077 * The value stuff may be hidden or shown depending on what
1078 * relation was selected; connect to the "changed" signal
1079 * for the relation list, so we can make that happen.
1081 g_signal_connect(gtk_tree_view_get_selection(GTK_TREE_VIEW(relation_list)),
1082 "changed", G_CALLBACK(relation_list_sel_cb), NULL);
1083 l_sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(value_list));
1084 gtk_tree_selection_set_mode(l_sel, GTK_SELECTION_SINGLE);
1085 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(value_list_scrolled_win),
1087 /* This remains hidden until an enumerated field is selected */
1090 * Put the items in the Tree; we don't want to do that until
1091 * we've constructed the value list and set the tree's
1092 * E_DFILTER_EXPR_VALUE_LIST_KEY data to point to it, and
1093 * constructed the "Accept" button and set the tree's
1094 * E_DFILTER_EXPR_OK_BT_KEY data to point to it, so that
1095 * when the list item is "helpfully" automatically selected for us
1096 * we're ready to cope with the selection signal.
1100 /* GTK2 code using two levels iterator to enumerate all protocol fields */
1102 GtkTreeIter iter, child_iter;
1103 void *cookie, *cookie2;
1105 for (i = proto_get_first_protocol(&cookie); i != -1;
1106 i = proto_get_next_protocol(&cookie)) {
1107 char *strp, str[TAG_STRING_LEN+1];
1109 protocol = find_protocol_by_id(i);
1111 if (!proto_is_protocol_enabled(protocol)) {
1115 g_snprintf(str, TAG_STRING_LEN, "%s - %s",
1116 proto_get_protocol_short_name(protocol),
1117 proto_get_protocol_long_name(protocol));
1120 hfinfo = proto_registrar_get_nth(i);
1122 gtk_tree_store_append(store, &iter, NULL);
1123 gtk_tree_store_set(store, &iter, 0, strp, 1, hfinfo, -1);
1125 for (hfinfo = proto_get_first_protocol_field(i, &cookie2); hfinfo != NULL;
1126 hfinfo = proto_get_next_protocol_field(&cookie2)) {
1128 if (hfinfo->same_name_prev != NULL) /* ignore duplicate names */
1131 if (hfinfo->blurb != NULL && hfinfo->blurb[0] != '\0') {
1132 g_snprintf(str, TAG_STRING_LEN, "%s - %s (%s)",
1133 hfinfo->abbrev, hfinfo->name, hfinfo->blurb);
1135 g_snprintf(str, TAG_STRING_LEN, "%s - %s", hfinfo->abbrev,
1138 gtk_tree_store_append(store, &child_iter, &iter);
1139 gtk_tree_store_set(store, &child_iter, 0, strp, 1, hfinfo, -1);
1142 g_object_unref(G_OBJECT(store));
1145 range_label = gtk_label_new("Range (offset:length)");
1146 gtk_misc_set_alignment(GTK_MISC(range_label), 0.0f, 0.0f);
1147 gtk_box_pack_start(GTK_BOX(value_vb), range_label, FALSE, FALSE, 0);
1149 range_entry = gtk_entry_new();
1150 gtk_box_pack_start(GTK_BOX(value_vb), range_entry, FALSE, FALSE, 0);
1154 list_bb = dlg_button_row_new(GTK_STOCK_OK, GTK_STOCK_CANCEL, NULL);
1155 gtk_box_pack_start(GTK_BOX(main_vb), list_bb, FALSE, FALSE, 0);
1156 gtk_container_set_border_width (GTK_CONTAINER (list_bb), 0);
1158 ok_bt = g_object_get_data(G_OBJECT(list_bb), GTK_STOCK_OK);
1159 gtk_widget_set_sensitive(ok_bt, FALSE);
1160 g_signal_connect(ok_bt, "clicked", G_CALLBACK(dfilter_expr_dlg_accept_cb), filter_te);
1162 cancel_bt = g_object_get_data(G_OBJECT(list_bb), GTK_STOCK_CANCEL);
1163 window_set_cancel_button(window, cancel_bt, NULL);
1164 g_signal_connect(cancel_bt, "clicked", G_CALLBACK(dfilter_expr_dlg_cancel_cb), window);
1166 gtk_widget_grab_default(ok_bt);
1168 /* Catch the "activate" signal on the range and value text entries,
1169 so that if the user types Return there, we act as if the "Accept"
1170 button had been selected, as happens if Return is typed if some
1171 widget that *doesn't* handle the Return key has the input focus. */
1172 dlg_set_activate(range_entry, ok_bt);
1173 dlg_set_activate(value_entry, ok_bt);
1175 g_object_set_data(G_OBJECT(window), E_DFILTER_EXPR_RELATION_LIST_KEY, relation_list);
1176 g_object_set_data(G_OBJECT(window), E_DFILTER_EXPR_RANGE_LABEL_KEY, range_label);
1177 g_object_set_data(G_OBJECT(window), E_DFILTER_EXPR_RANGE_ENTRY_KEY, range_entry);
1178 g_object_set_data(G_OBJECT(window), E_DFILTER_EXPR_VALUE_LABEL_KEY, value_label);
1179 g_object_set_data(G_OBJECT(window), E_DFILTER_EXPR_VALUE_ENTRY_KEY, value_entry);
1180 g_object_set_data(G_OBJECT(window), E_DFILTER_EXPR_VALUE_LIST_KEY, value_list);
1181 g_object_set_data(G_OBJECT(window), E_DFILTER_EXPR_VALUE_LIST_LABEL_KEY, value_list_label);
1182 g_object_set_data(G_OBJECT(window), E_DFILTER_EXPR_VALUE_LIST_SW_KEY,
1183 value_list_scrolled_win);
1184 g_object_set_data(G_OBJECT(window), E_DFILTER_EXPR_OK_BT_KEY, ok_bt);
1186 g_signal_connect(window, "delete_event", G_CALLBACK(dfilter_expr_dlg_delete_event_cb), window);
1189 * Catch the "destroy" signal on our top-level window, and,
1190 * when it's destroyed, disconnect the signal we'll be
1193 g_signal_connect(window, "destroy", G_CALLBACK(dfilter_expr_dlg_destroy_cb), filter_te);
1196 * Catch the "destroy" signal on the text entry widget to which
1197 * we're attached; if it's destroyed, we should destroy ourselves
1200 g_signal_connect(filter_te, "destroy", G_CALLBACK(dfilter_expr_dlg_cancel_cb), window);
1202 gtk_widget_show_all(window);
1203 window_present(window);