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>
10 * $Id: dfilter_expr_dlg.c,v 1.23 2001/08/21 06:39:18 guy Exp $
12 * Ethereal - Network traffic analyzer
13 * By Gerald Combs <gerald@ethereal.com>
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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, 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 * time. not a good thing.
39 * Sort the protocols and children
52 #ifdef HAVE_SYS_TYPES_H
53 #include <sys/types.h>
56 #ifdef NEED_SNPRINTF_H
57 # include "snprintf.h"
62 #include "gtkglobals.h"
65 #include "simple_dialog.h"
66 #include "dlg_utils.h"
67 #include "proto_dlg.h"
68 #include "filter_prefs.h"
69 #include "dfilter_expr_dlg.h"
71 #define E_DFILTER_EXPR_TREE_KEY "dfilter_expr_tree"
72 #define E_DFILTER_EXPR_CURRENT_VAR_KEY "dfilter_expr_current_var"
73 #define E_DFILTER_EXPR_RELATION_LABEL_KEY "dfilter_expr_relation_label"
74 #define E_DFILTER_EXPR_RELATION_LIST_KEY "dfilter_expr_relation_list"
75 #define E_DFILTER_EXPR_RANGE_LABEL_KEY "dfilter_expr_range_label"
76 #define E_DFILTER_EXPR_RANGE_ENTRY_KEY "dfilter_expr_range_entry"
77 #define E_DFILTER_EXPR_VALUE_LABEL_KEY "dfilter_expr_value_label"
78 #define E_DFILTER_EXPR_VALUE_ENTRY_KEY "dfilter_expr_value_entry"
79 #define E_DFILTER_EXPR_VALUE_LIST_KEY "dfilter_expr_value_list"
80 #define E_DFILTER_EXPR_VALUE_LIST_SW_KEY "dfilter_expr_value_list_sw"
81 #define E_DFILTER_EXPR_ACCEPT_BT_KEY "dfilter_expr_accept_bt"
82 #define E_DFILTER_EXPR_VALUE_KEY "dfilter_expr_value"
84 typedef struct protocol_data {
89 static void show_relations(GtkWidget *relation_label, GtkWidget *relation_list,
91 static gboolean relation_is_presence_test(const char *string);
92 static void add_relation_list(GtkWidget *relation_list, char *relation);
93 static void build_boolean_values(GtkWidget *value_list_scrolled_win,
94 GtkWidget *value_list, const true_false_string *values);
95 static void build_enum_values(GtkWidget *value_list_scrolled_win,
96 GtkWidget *value_list, const value_string *values);
97 static void add_value_list_item(GtkWidget *value_list, gchar *string,
99 static void display_value_fields(header_field_info *hfinfo,
100 gboolean is_comparison, GtkWidget *value_label, GtkWidget *value_entry,
101 GtkWidget *value_list, GtkWidget *value_list_scrolled_win,
102 GtkWidget *range_label, GtkWidget *range_entry);
105 * Note that this is called every time the user clicks on an item,
106 * whether it is already selected or not.
109 field_select_row_cb(GtkWidget *tree, GList *node, gint column,
112 GtkWidget *window = gtk_widget_get_toplevel(tree);
113 GtkWidget *relation_label = gtk_object_get_data(GTK_OBJECT(window),
114 E_DFILTER_EXPR_RELATION_LABEL_KEY);
115 GtkWidget *relation_list = gtk_object_get_data(GTK_OBJECT(window),
116 E_DFILTER_EXPR_RELATION_LIST_KEY);
117 GtkWidget *range_label = gtk_object_get_data(GTK_OBJECT(window),
118 E_DFILTER_EXPR_RANGE_LABEL_KEY);
119 GtkWidget *range_entry = gtk_object_get_data(GTK_OBJECT(window),
120 E_DFILTER_EXPR_RANGE_ENTRY_KEY);
121 GtkWidget *value_label = gtk_object_get_data(GTK_OBJECT(window),
122 E_DFILTER_EXPR_VALUE_LABEL_KEY);
123 GtkWidget *value_entry = gtk_object_get_data(GTK_OBJECT(window),
124 E_DFILTER_EXPR_VALUE_ENTRY_KEY);
125 GtkWidget *value_list = gtk_object_get_data(GTK_OBJECT(window),
126 E_DFILTER_EXPR_VALUE_LIST_KEY);
127 GtkWidget *value_list_scrolled_win = gtk_object_get_data(GTK_OBJECT(window),
128 E_DFILTER_EXPR_VALUE_LIST_SW_KEY);
129 GtkWidget *accept_bt = gtk_object_get_data(GTK_OBJECT(window),
130 E_DFILTER_EXPR_ACCEPT_BT_KEY);
131 header_field_info *hfinfo, *cur_hfinfo;
132 const char *value_type;
133 char value_label_string[1024+1]; /* XXX - should be large enough */
135 hfinfo = gtk_ctree_node_get_row_data(GTK_CTREE(tree),
136 GTK_CTREE_NODE(node));
139 * What was the item that was last selected?
141 cur_hfinfo = gtk_object_get_data(GTK_OBJECT(window),
142 E_DFILTER_EXPR_CURRENT_VAR_KEY);
143 if (cur_hfinfo == hfinfo) {
145 * It's still selected; no need to change anything.
151 * Mark it as currently selected.
153 gtk_object_set_data(GTK_OBJECT(window), E_DFILTER_EXPR_CURRENT_VAR_KEY,
156 show_relations(relation_label, relation_list, hfinfo->type);
159 * Set the label for the value to indicate what type of value
162 value_type = ftype_pretty_name(hfinfo->type);
163 if (value_type != NULL) {
165 * Indicate what type of value it is.
167 snprintf(value_label_string, sizeof value_label_string,
168 "Value (%s)", value_type);
169 gtk_label_set_text(GTK_LABEL(value_label), value_label_string);
173 * Clear the entry widget for the value, as whatever
174 * was there before doesn't apply.
176 gtk_entry_set_text(GTK_ENTRY(value_entry), "");
178 switch (hfinfo->type) {
182 * The list of values should be the strings for "true"
183 * and "false"; show them in the value list.
185 build_boolean_values(value_list_scrolled_win, value_list,
198 * If this has a value_string table associated with it,
199 * fill up the list of values, otherwise clear the list
202 if (hfinfo->strings != NULL) {
203 build_enum_values(value_list_scrolled_win, value_list,
206 gtk_list_clear_items(GTK_LIST(value_list), 0, -1);
211 * Clear the list of values.
213 gtk_list_clear_items(GTK_LIST(value_list), 0, -1);
218 * Display various items for the value, as appropriate.
219 * The relation we start out with is never a comparison.
221 display_value_fields(hfinfo, FALSE, value_label, value_entry,
222 value_list, value_list_scrolled_win, range_label, range_entry);
225 * XXX - in browse mode, there always has to be something
226 * selected, so this should always be sensitive.
228 gtk_widget_set_sensitive(accept_bt, TRUE);
232 show_relations(GtkWidget *relation_label, GtkWidget *relation_list,
236 * Clear out the currently displayed list of relations.
238 gtk_list_clear_items(GTK_LIST(relation_list), 0, -1);
241 * Add the supported relations.
243 add_relation_list(relation_list, "is present");
244 if (ftype_can_eq(ftype) ||
245 (ftype_can_slice(ftype) && ftype_can_eq(FT_BYTES)))
246 add_relation_list(relation_list, "==");
247 if (ftype_can_ne(ftype) ||
248 (ftype_can_slice(ftype) && ftype_can_ne(FT_BYTES)))
249 add_relation_list(relation_list, "!=");
250 if (ftype_can_gt(ftype) ||
251 (ftype_can_slice(ftype) && ftype_can_gt(FT_BYTES)))
252 add_relation_list(relation_list, ">");
253 if (ftype_can_lt(ftype) ||
254 (ftype_can_slice(ftype) && ftype_can_lt(FT_BYTES)))
255 add_relation_list(relation_list, "<");
256 if (ftype_can_ge(ftype) ||
257 (ftype_can_slice(ftype) && ftype_can_ge(FT_BYTES)))
258 add_relation_list(relation_list, ">=");
259 if (ftype_can_le(ftype) ||
260 (ftype_can_slice(ftype) && ftype_can_le(FT_BYTES)))
261 add_relation_list(relation_list, "<=");
266 gtk_widget_show(relation_label);
267 gtk_widget_show(relation_list);
271 * Given a string that represents a test to be made on a field, returns
272 * TRUE if it tests for the field's presence, FALSE otherwise.
275 relation_is_presence_test(const char *string)
277 return (strcmp(string, "is present") == 0);
281 add_relation_list(GtkWidget *relation_list, char *relation)
283 GtkWidget *label, *item;
285 label = gtk_label_new(relation);
286 item = gtk_list_item_new();
288 gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
289 gtk_container_add(GTK_CONTAINER(item), label);
290 gtk_widget_show(label);
291 gtk_container_add(GTK_CONTAINER(relation_list), item);
292 gtk_widget_show(item);
296 relation_list_sel_cb(GtkList *relation_list, GtkWidget *child,
299 GtkWidget *window = gtk_widget_get_toplevel(GTK_WIDGET(relation_list));
300 GtkWidget *range_label = gtk_object_get_data(GTK_OBJECT(window),
301 E_DFILTER_EXPR_RANGE_LABEL_KEY);
302 GtkWidget *range_entry = gtk_object_get_data(GTK_OBJECT(window),
303 E_DFILTER_EXPR_RANGE_ENTRY_KEY);
304 GtkWidget *value_label = gtk_object_get_data(GTK_OBJECT(window),
305 E_DFILTER_EXPR_VALUE_LABEL_KEY);
306 GtkWidget *value_entry = gtk_object_get_data(GTK_OBJECT(window),
307 E_DFILTER_EXPR_VALUE_ENTRY_KEY);
308 GtkWidget *value_list = gtk_object_get_data(GTK_OBJECT(window),
309 E_DFILTER_EXPR_VALUE_LIST_KEY);
310 GtkWidget *value_list_scrolled_win = gtk_object_get_data(GTK_OBJECT(window),
311 E_DFILTER_EXPR_VALUE_LIST_SW_KEY);
312 header_field_info *hfinfo = gtk_object_get_data(GTK_OBJECT(window),
313 E_DFILTER_EXPR_CURRENT_VAR_KEY);
315 GtkWidget *item, *item_label;
319 * What's the relation?
321 sl = GTK_LIST(relation_list)->selection;
322 item = GTK_WIDGET(sl->data);
323 item_label = GTK_BIN(item)->child;
324 gtk_label_get(GTK_LABEL(item_label), &item_str);
327 * Update the display of various items for the value, as appropriate.
329 display_value_fields(hfinfo,
330 !relation_is_presence_test(item_str),
331 value_label, value_entry, value_list, value_list_scrolled_win,
332 range_label, range_entry);
336 build_boolean_values(GtkWidget *value_list_scrolled_win, GtkWidget *value_list,
337 const true_false_string *values)
339 static const true_false_string true_false = { "True", "False" };
342 * Clear out the items for the list, and put in the names
343 * from the value_string list.
345 gtk_list_clear_items(GTK_LIST(value_list), 0, -1);
348 * Put the list in single mode, so we don't get any selection
349 * events while we're building it (i.e., so we don't get any
350 * on a list item BEFORE WE GET TO SET THE DATA FOR THE LIST
351 * ITEM SO THAT THE HANDLER CAN HANDLE IT).
353 gtk_list_set_selection_mode(GTK_LIST(value_list), GTK_SELECTION_SINGLE);
359 values = &true_false;
360 add_value_list_item(value_list, values->true_string, (gpointer)values);
361 add_value_list_item(value_list, values->false_string, NULL);
364 * OK, we're done, so we can finally put it in browse mode.
365 * Select the first item, so that the user doesn't have to, under
366 * the assumption that they're most likely to test if something
367 * is true, not false.
369 gtk_list_set_selection_mode(GTK_LIST(value_list), GTK_SELECTION_BROWSE);
370 gtk_list_select_item(GTK_LIST(value_list), 0);
372 gtk_widget_show_all(value_list_scrolled_win);
376 build_enum_values(GtkWidget *value_list_scrolled_win, GtkWidget *value_list,
377 const value_string *values)
380 * Clear out the items for the list, and put in the names
381 * from the value_string list.
383 gtk_list_clear_items(GTK_LIST(value_list), 0, -1);
386 * Put the list in single mode, so we don't get any selection
387 * events while we're building it (i.e., so we don't get any
388 * on a list item BEFORE WE GET TO SET THE DATA FOR THE LIST
389 * ITEM SO THAT THE HANDLER CAN HANDLE IT).
391 gtk_list_set_selection_mode(GTK_LIST(value_list), GTK_SELECTION_SINGLE);
396 while (values->strptr != NULL) {
397 add_value_list_item(value_list, values->strptr,
403 * OK, we're done, so we can finally put it in browse mode.
405 gtk_list_set_selection_mode(GTK_LIST(value_list), GTK_SELECTION_BROWSE);
409 add_value_list_item(GtkWidget *value_list, gchar *string, gpointer data)
411 GtkWidget *label, *item;
413 label = gtk_label_new(string);
414 item = gtk_list_item_new();
416 gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
417 gtk_container_add(GTK_CONTAINER(item), label);
418 gtk_widget_show(label);
419 gtk_container_add(GTK_CONTAINER(value_list), item);
420 gtk_object_set_data(GTK_OBJECT(item), E_DFILTER_EXPR_VALUE_KEY, data);
421 gtk_widget_show(item);
425 * Show or hide the various values fields as appropriate for the field
426 * and currently-selected relation.
429 display_value_fields(header_field_info *hfinfo, gboolean is_comparison,
430 GtkWidget *value_label, GtkWidget *value_entry, GtkWidget *value_list,
431 GtkWidget *value_list_scrolled_win, GtkWidget *range_label,
432 GtkWidget *range_entry)
434 gboolean show_value_label = 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 default relation is such a test, in
455 * which case you don't compare with a value
457 * so we hide the value entry.
461 * The relation is a comparison; display the entry for
462 * the value with which to compare.
464 gtk_widget_show(value_entry);
467 * We're showing the entry; show the label as well.
469 show_value_label = TRUE;
472 * The relation isn't a comparison; there's no value with
473 * which to compare, so don't show the entry for it.
475 gtk_widget_hide(value_entry);
478 switch (hfinfo->type) {
483 * The relation is a comparison, so we're showing
484 * an entry for the value with which to compare;
485 * show the list of names for values as well.
486 * (The list of values contains the strings for
487 * "true" and "false".)
489 gtk_widget_show_all(value_list_scrolled_win);
492 * We're showing the value list; show the label as
495 show_value_label = TRUE;
498 * It's not a comparison, so we're not showing
499 * the entry for the value; don't show the
500 * list of names for values, either.
502 gtk_widget_hide_all(value_list_scrolled_win);
514 if (hfinfo->strings != NULL) {
516 * We have a list of values to show.
520 * The relation is a comparison, so we're
521 * showing an entry for the value with
522 * which to compare; show the list of
523 * names for values as well.
525 gtk_widget_show_all(value_list_scrolled_win);
528 * We're showing the entry; show the label
531 show_value_label = TRUE;
534 * It's not a comparison, so we're not showing
535 * the entry for the value; don't show the
536 * list of names for values, either.
538 gtk_widget_hide_all(value_list_scrolled_win);
542 * There is no list of names for values, so don't
545 gtk_widget_hide_all(value_list_scrolled_win);
551 * There is no list of names for values; hide the list.
553 gtk_widget_hide_all(value_list_scrolled_win);
557 if (show_value_label)
558 gtk_widget_show(value_label);
560 gtk_widget_hide(value_label);
563 * Is this a comparison, and are ranges supported by this type?
564 * If both are true, show the range stuff, otherwise hide it.
566 if (is_comparison && ftype_can_slice(hfinfo->type)) {
567 gtk_widget_show(range_label);
568 gtk_widget_show(range_entry);
570 gtk_widget_hide(range_label);
571 gtk_widget_hide(range_entry);
576 value_list_sel_cb(GtkList *value_list, GtkWidget *child,
577 gpointer value_entry_arg)
579 GtkWidget *value_entry = value_entry_arg;
580 GtkWidget *window = gtk_widget_get_toplevel(GTK_WIDGET(value_list));
581 header_field_info *hfinfo = gtk_object_get_data(GTK_OBJECT(window),
582 E_DFILTER_EXPR_CURRENT_VAR_KEY);
583 const value_string *value;
584 char value_string[11+1]; /* long enough for 32-bit octal value */
586 value = gtk_object_get_data(GTK_OBJECT(child),
587 E_DFILTER_EXPR_VALUE_KEY);
590 * This should either be a numeric type or a Boolean type.
592 if (hfinfo->type == FT_BOOLEAN) {
594 * Boolean type; if the value key for the selected item
595 * is non-null, it's the item for "true", otherwise it's
596 * the item for "false". Compare with 1 if we're
597 * testing for "true", and compare with 0 if we're
598 * testing for "false".
601 strcpy(value_string, "1");
603 strcpy(value_string, "0");
606 * Numeric type; get the value corresponding to the
607 * selected item, and display it in the base for this
610 switch (hfinfo->display) {
613 case BASE_BIN: /* binary - treated as decimal, for now */
614 switch (hfinfo->type) {
619 snprintf(value_string, sizeof value_string,
626 snprintf(value_string, sizeof value_string,
631 g_assert_not_reached();
636 snprintf(value_string, sizeof value_string, "0x%x",
641 snprintf(value_string, sizeof value_string, "%#o",
646 g_assert_not_reached();
649 gtk_entry_set_text(GTK_ENTRY(value_entry), value_string);
653 dfilter_report_bad_value(char *format, ...)
655 char error_msg_buf[1024];
658 va_start(args, format);
659 vsnprintf(error_msg_buf, sizeof error_msg_buf, format, args);
662 simple_dialog(ESD_TYPE_CRIT | ESD_TYPE_MODAL, NULL,
663 "%s", error_msg_buf);
667 dfilter_expr_dlg_accept_cb(GtkWidget *w, gpointer filter_te_arg)
669 GtkWidget *filter_te = filter_te_arg;
670 GtkWidget *window = gtk_widget_get_toplevel(w);
671 GtkWidget *relation_list = gtk_object_get_data(GTK_OBJECT(window),
672 E_DFILTER_EXPR_RELATION_LIST_KEY);
673 GtkWidget *range_entry = gtk_object_get_data(GTK_OBJECT(window),
674 E_DFILTER_EXPR_RANGE_ENTRY_KEY);
675 GtkWidget *value_entry = gtk_object_get_data(GTK_OBJECT(window),
676 E_DFILTER_EXPR_VALUE_ENTRY_KEY);
677 header_field_info *hfinfo;
679 GtkWidget *item, *item_label;
681 gchar *range_str, *stripped_range_str;
682 gchar *value_str, *stripped_value_str;
686 gboolean can_compare;
690 * Get the variable to be tested.
692 hfinfo = gtk_object_get_data(GTK_OBJECT(window),
693 E_DFILTER_EXPR_CURRENT_VAR_KEY);
696 * Get the relation to use, if any.
698 if (GTK_WIDGET_VISIBLE(relation_list)) {
700 * The list of relations is visible, so we can get a
701 * relation operator from it.
703 sl = GTK_LIST(relation_list)->selection;
704 item = GTK_WIDGET(sl->data);
705 item_label = GTK_BIN(item)->child;
706 gtk_label_get(GTK_LABEL(item_label), &item_str);
708 item_str = NULL; /* no relation operator */
711 * Get the range to use, if any.
713 if (GTK_WIDGET_VISIBLE(range_entry)) {
714 range_str = g_strdup(gtk_entry_get_text(GTK_ENTRY(range_entry)));
715 stripped_range_str = g_strstrip(range_str);
716 if (strcmp(stripped_range_str, "") == 0) {
718 * No range was specified.
722 stripped_range_str = NULL;
726 * XXX - check it for validity?
730 stripped_range_str = NULL;
734 * If a range was specified, the type of the LHS of the
735 * comparison is FT_BYTES; otherwise, it's the type of the field.
737 if (range_str == NULL)
738 ftype = hfinfo->type;
743 * Make sure the relation is valid for the type in question.
744 * We may be offering relations that the type of the field
745 * can't support, because the field's type supports slicing,
746 * and the relation *is* supported on byte strings.
748 if (strcmp(item_str, "==") == 0)
749 can_compare = ftype_can_eq(ftype);
750 else if (strcmp(item_str, "!=") == 0)
751 can_compare = ftype_can_ne(ftype);
752 else if (strcmp(item_str, ">") == 0)
753 can_compare = ftype_can_gt(ftype);
754 else if (strcmp(item_str, "<") == 0)
755 can_compare = ftype_can_lt(ftype);
756 else if (strcmp(item_str, ">=") == 0)
757 can_compare = ftype_can_ge(ftype);
758 else if (strcmp(item_str, "<=") == 0)
759 can_compare = ftype_can_le(ftype);
761 can_compare = TRUE; /* not a comparison */
763 if (range_str == NULL) {
764 simple_dialog(ESD_TYPE_CRIT | ESD_TYPE_MODAL, NULL,
765 "That field cannot be tested with \"%s\".",
768 simple_dialog(ESD_TYPE_CRIT | ESD_TYPE_MODAL, NULL,
769 "Ranges of that field cannot be tested with \"%s\".",
772 if (range_str != NULL)
778 * Get the value to use, if any.
780 if (GTK_WIDGET_VISIBLE(value_entry)) {
781 value_str = g_strdup(gtk_entry_get_text(GTK_ENTRY(value_entry)));
782 stripped_value_str = g_strstrip(value_str);
783 if (strcmp(stripped_value_str, "") == 0) {
785 * This field takes a value, but they didn't supply
788 simple_dialog(ESD_TYPE_CRIT | ESD_TYPE_MODAL, NULL,
789 "That field must be compared with a value, "
790 "but you didn't specify a value with which to "
792 if (range_str != NULL)
799 * Make sure the value is valid.
801 * If no range string was specified, it must be valid
802 * for the type of the field; if a range string was
803 * specified, must be valid for FT_BYTES.
805 fvalue = fvalue_from_string(ftype, stripped_value_str,
806 dfilter_report_bad_value);
807 if (fvalue == NULL) {
811 * The dialog box was already popped up by
812 * "dfilter_report_bad_value()".
814 if (range_str != NULL)
822 stripped_value_str = NULL;
826 * Insert the expression at the current cursor position.
827 * If there's a non-whitespace character to the left of it,
828 * insert a blank first; if there's a non-whitespace character
829 * to the right of it, insert a blank after it.
831 pos = gtk_editable_get_position(GTK_EDITABLE(filter_te));
832 chars = gtk_editable_get_chars(GTK_EDITABLE(filter_te), pos, pos + 1);
833 if (strcmp(chars, "") != 0 && !isspace((unsigned char)chars[0]))
834 gtk_editable_insert_text(GTK_EDITABLE(filter_te), " ", 1, &pos);
837 gtk_editable_insert_text(GTK_EDITABLE(filter_te), hfinfo->abbrev,
838 strlen(hfinfo->abbrev), &pos);
839 if (range_str != NULL) {
840 gtk_editable_insert_text(GTK_EDITABLE(filter_te), "[", 1, &pos);
841 gtk_editable_insert_text(GTK_EDITABLE(filter_te),
842 stripped_range_str, strlen(stripped_range_str), &pos);
843 gtk_editable_insert_text(GTK_EDITABLE(filter_te), "]", 1, &pos);
846 if (item_str != NULL && !relation_is_presence_test(item_str)) {
847 gtk_editable_insert_text(GTK_EDITABLE(filter_te), " ", 1, &pos);
848 gtk_editable_insert_text(GTK_EDITABLE(filter_te), item_str,
849 strlen(item_str), &pos);
851 if (value_str != NULL) {
852 gtk_editable_insert_text(GTK_EDITABLE(filter_te), " ", 1, &pos);
853 switch (hfinfo->type) {
859 * Put quotes around the string.
861 gtk_editable_insert_text(GTK_EDITABLE(filter_te), "\"",
867 gtk_editable_insert_text(GTK_EDITABLE(filter_te),
868 stripped_value_str, strlen(stripped_value_str), &pos);
869 switch (hfinfo->type) {
875 * Put quotes around the string.
877 gtk_editable_insert_text(GTK_EDITABLE(filter_te), "\"",
885 chars = gtk_editable_get_chars(GTK_EDITABLE(filter_te), pos + 1, pos + 2);
886 if (strcmp(chars, "") != 0 && !isspace((unsigned char)chars[0]))
887 gtk_editable_insert_text(GTK_EDITABLE(filter_te), " ", 1, &pos);
891 * Put the cursor after the expression we just entered into
892 * the text entry widget.
894 gtk_editable_set_position(GTK_EDITABLE(filter_te), pos);
897 * We're done; destroy the dialog box (which is the top-level
898 * widget for the "Accept" button).
900 gtk_widget_destroy(window);
904 dfilter_expr_dlg_cancel_cb(GtkWidget *w, gpointer parent_w)
907 * User pressed the cancel button; close the dialog box.
909 gtk_widget_destroy(GTK_WIDGET(parent_w));
913 dfilter_expr_dlg_destroy_cb(GtkWidget *w, gpointer filter_te)
916 * The dialog box is being destroyed; disconnect from the
917 * "destroy" signal on the text entry box to which we're
918 * attached, as the handler for that signal is supposed
919 * to destroy us, but we're already gone.
921 gtk_signal_disconnect_by_func(GTK_OBJECT(filter_te),
922 dfilter_expr_dlg_cancel_cb, w);
926 dfilter_expr_dlg_new(GtkWidget *filter_te)
932 GtkWidget *tree_label, *tree, *tree_scrolled_win;
934 GtkWidget *relation_label, *relation_list;
935 GtkWidget *range_label, *range_entry;
937 GtkWidget *value_label, *value_entry, *value_list_scrolled_win, *value_list;
938 GtkWidget *list_bb, *alignment, *accept_bt, *close_bt;
939 GtkCTreeNode *protocol_node, *item_node;
940 header_field_info *hfinfo;
944 GHashTable *proto_array;
946 window = dlg_window_new("Ethereal: Filter Expression");
947 gtk_container_set_border_width(GTK_CONTAINER(window), 5);
949 main_vb = gtk_vbox_new(FALSE, 5);
950 gtk_container_border_width(GTK_CONTAINER(main_vb), 5);
951 gtk_container_add(GTK_CONTAINER(window), main_vb);
952 gtk_widget_show(main_vb);
954 hb = gtk_hbox_new(FALSE, 5);
955 gtk_container_border_width(GTK_CONTAINER(hb), 5);
956 gtk_container_add(GTK_CONTAINER(main_vb), hb);
959 col1_vb = gtk_vbox_new(FALSE, 5);
960 gtk_container_border_width(GTK_CONTAINER(col1_vb), 5);
961 gtk_container_add(GTK_CONTAINER(hb), col1_vb);
962 gtk_widget_show(col1_vb);
964 tree_label = gtk_label_new("Field name");
965 gtk_misc_set_alignment(GTK_MISC(tree_label), 0.0, 0.0);
966 gtk_box_pack_start(GTK_BOX(col1_vb), tree_label, FALSE, FALSE, 0);
967 gtk_widget_show(tree_label);
969 tree_scrolled_win = gtk_scrolled_window_new(NULL, NULL);
970 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(tree_scrolled_win),
971 GTK_POLICY_AUTOMATIC,
972 GTK_POLICY_AUTOMATIC);
973 gtk_widget_set_usize(tree_scrolled_win, 300, 400);
974 gtk_box_pack_start(GTK_BOX(col1_vb), tree_scrolled_win, FALSE, FALSE, 0);
975 gtk_widget_show(tree_scrolled_win);
977 tree = gtk_ctree_new(1, 0);
978 gtk_ctree_set_line_style(GTK_CTREE(tree), GTK_CTREE_LINES_NONE);
979 gtk_signal_connect(GTK_OBJECT(tree), "tree-select-row",
980 GTK_SIGNAL_FUNC(field_select_row_cb), tree);
981 gtk_container_add(GTK_CONTAINER(tree_scrolled_win), tree);
984 * GTK's annoying CTree widget will deliver a selection event
985 * the instant you add an item to the tree, *the fact that you
986 * haven't even had time to set the item's row data nonwithstanding*.
988 * We'll put the widget into GTK_SELECTION_SINGLE mode in the
989 * hopes that it's *STOP DOING THAT*.
991 gtk_clist_set_selection_mode(GTK_CLIST(tree),
992 GTK_SELECTION_SINGLE);
994 col2_vb = gtk_vbox_new(FALSE, 5);
995 gtk_container_border_width(GTK_CONTAINER(col2_vb), 5);
996 gtk_container_add(GTK_CONTAINER(hb), col2_vb);
997 gtk_widget_show(col2_vb);
999 relation_label = gtk_label_new("Relation");
1000 gtk_misc_set_alignment(GTK_MISC(relation_label), 0.0, 0.0);
1001 gtk_box_pack_start(GTK_BOX(col2_vb), relation_label, FALSE, FALSE, 0);
1003 relation_list = gtk_list_new();
1004 gtk_box_pack_start(GTK_BOX(col2_vb), relation_list, TRUE, TRUE, 0);
1005 gtk_list_set_selection_mode(GTK_LIST(relation_list),
1006 GTK_SELECTION_BROWSE);
1008 range_label = gtk_label_new("Range (offset:length)");
1009 gtk_misc_set_alignment(GTK_MISC(range_label), 0.0, 0.0);
1010 gtk_box_pack_start(GTK_BOX(col2_vb), range_label, FALSE, FALSE, 0);
1012 range_entry = gtk_entry_new();
1013 gtk_box_pack_start(GTK_BOX(col2_vb), range_entry, FALSE, FALSE, 0);
1016 * OK, show the relation label and range stuff as it would be
1017 * with everything turned on, so it'll request as much space
1018 * as it'll ever need, so the dialog box and widgets start out
1019 * with the right sizes.
1021 * XXX - this doesn't work. It *doesn't* request as much space
1022 * as it'll ever need.
1024 * XXX - FT_UINT8 doesn't support ranges, so even if it did work,
1025 * it wouldn't work right.
1027 * XXX - this no longer affects the range stuff, as that's
1028 * controlled both by the type and by the relational operator
1031 show_relations(relation_label, relation_list, FT_UINT8);
1033 value_vb = gtk_vbox_new(FALSE, 5);
1034 gtk_container_border_width(GTK_CONTAINER(value_vb), 5);
1035 gtk_container_add(GTK_CONTAINER(hb), value_vb);
1036 gtk_widget_show(value_vb);
1038 value_label = gtk_label_new("Value");
1039 gtk_misc_set_alignment(GTK_MISC(value_label), 0.0, 0.0);
1040 gtk_box_pack_start(GTK_BOX(value_vb), value_label, FALSE, FALSE, 0);
1041 gtk_widget_show(value_label);
1043 value_entry = gtk_entry_new();
1044 gtk_box_pack_start(GTK_BOX(value_vb), value_entry, FALSE, FALSE, 0);
1045 gtk_widget_show(value_entry);
1047 value_list_scrolled_win = gtk_scrolled_window_new(NULL, NULL);
1048 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(value_list_scrolled_win),
1049 GTK_POLICY_AUTOMATIC,
1050 GTK_POLICY_AUTOMATIC);
1051 gtk_box_pack_start(GTK_BOX(value_vb), value_list_scrolled_win, TRUE,
1053 gtk_widget_show(value_list_scrolled_win);
1055 value_list = gtk_list_new();
1056 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(value_list_scrolled_win),
1058 gtk_signal_connect(GTK_OBJECT(value_list), "select-child",
1059 GTK_SIGNAL_FUNC(value_list_sel_cb), value_entry);
1060 gtk_list_set_selection_mode(GTK_LIST(value_list), GTK_SELECTION_SINGLE);
1061 /* This remains hidden until an enumerated field is selected */
1064 * The value stuff may be hidden or shown depending on what
1065 * relation was selected; connect to the "select-child" signal
1066 * for the relation list, so we can make that happen.
1068 gtk_signal_connect(GTK_OBJECT(relation_list), "select-child",
1069 GTK_SIGNAL_FUNC(relation_list_sel_cb), NULL);
1072 * Put the items in the CTree; we don't want to do that until
1073 * we've constructed the value list and set the tree's
1074 * E_DFILTER_EXPR_VALUE_LIST_KEY data to point to it, and
1075 * constructed the "Accept" button and set the tree's
1076 * E_DFILTER_EXPR_ACCEPT_BT_KEY data to point to it, so that
1077 * when the list item is "helpfully" automatically selected for us
1078 * we're ready to cope with the selection signal.
1081 /* a hash table seems excessive, but I don't see support for a
1082 sparse array in glib */
1083 proto_array = g_hash_table_new(g_direct_hash, g_direct_equal);
1084 for (i = proto_get_first_protocol(&cookie); i != -1;
1085 i = proto_get_next_protocol(&cookie)) {
1086 hfinfo = proto_registrar_get_nth(i);
1087 /* Create a node for the protocol, and remember it for
1089 name = proto_get_protocol_short_name(i);
1090 protocol_node = gtk_ctree_insert_node(GTK_CTREE(tree),
1093 NULL, NULL, NULL, NULL,
1095 gtk_ctree_node_set_row_data(GTK_CTREE(tree), protocol_node,
1097 g_hash_table_insert(proto_array, (gpointer)i, protocol_node);
1100 len = proto_registrar_n();
1101 for (i = 0; i < len; i++) {
1103 * If this field is a protocol, skip it - we already put
1106 if (proto_registrar_is_protocol(i))
1109 hfinfo = proto_registrar_get_nth(i);
1112 * If this field isn't at the head of the list of
1113 * fields with this name, skip this field - all
1114 * fields with the same name are really just versions
1115 * of the same field stored in different bits, and
1116 * should have the same type/radix/value list, and
1117 * just differ in their bit masks. (If a field isn't
1118 * a bitfield, but can be, say, 1 or 2 bytes long,
1119 * it can just be made FT_UINT16, meaning the
1120 * *maximum* length is 2 bytes, and be used
1123 if (hfinfo->same_name_prev != NULL)
1126 /* Create a node for the item, and put it
1127 under its parent protocol. */
1128 protocol_node = g_hash_table_lookup(proto_array,
1129 (gpointer)proto_registrar_get_parent(i));
1130 item_node = gtk_ctree_insert_node(GTK_CTREE(tree),
1131 protocol_node, NULL,
1133 NULL, NULL, NULL, NULL,
1135 gtk_ctree_node_set_row_data(GTK_CTREE(tree),
1139 g_hash_table_destroy(proto_array);
1141 gtk_widget_show_all(tree);
1143 list_bb = gtk_hbutton_box_new();
1144 gtk_button_box_set_layout(GTK_BUTTON_BOX(list_bb), GTK_BUTTONBOX_START);
1145 gtk_button_box_set_spacing(GTK_BUTTON_BOX(list_bb), 5);
1146 gtk_container_add(GTK_CONTAINER(main_vb), list_bb);
1147 gtk_widget_show(list_bb);
1149 accept_bt = gtk_button_new_with_label("Accept");
1150 gtk_widget_set_sensitive(accept_bt, FALSE);
1151 gtk_signal_connect(GTK_OBJECT(accept_bt), "clicked",
1152 GTK_SIGNAL_FUNC(dfilter_expr_dlg_accept_cb), filter_te);
1153 GTK_WIDGET_SET_FLAGS(accept_bt, GTK_CAN_DEFAULT);
1154 alignment = gtk_alignment_new(0.0, 0.5, 1.0, 0.0);
1155 gtk_container_add(GTK_CONTAINER(alignment), accept_bt);
1156 gtk_box_pack_start(GTK_BOX(list_bb), alignment, TRUE, TRUE, 0);
1157 gtk_widget_grab_default(accept_bt);
1158 gtk_widget_show(accept_bt);
1159 gtk_widget_show(alignment);
1161 close_bt = gtk_button_new_with_label("Close");
1162 gtk_signal_connect(GTK_OBJECT(close_bt), "clicked",
1163 GTK_SIGNAL_FUNC(dfilter_expr_dlg_cancel_cb), window);
1164 alignment = gtk_alignment_new(0.0, 0.5, 1.0, 0.0);
1165 gtk_container_add(GTK_CONTAINER(alignment), close_bt);
1166 gtk_box_pack_start(GTK_BOX(list_bb), alignment, TRUE, TRUE, 0);
1167 gtk_widget_show(close_bt);
1168 gtk_widget_show(alignment);
1170 /* Catch the "activate" signal on the range and value text entries,
1171 so that if the user types Return there, we act as if the "Accept"
1172 button had been selected, as happens if Return is typed if some
1173 widget that *doesn't* handle the Return key has the input focus. */
1174 dlg_set_activate(range_entry, accept_bt);
1175 dlg_set_activate(value_entry, accept_bt);
1178 * Catch the "key_press_event" signal in the window, so that we can
1179 * catch the ESC key being pressed and act as if the "Close" button
1180 * had been selected.
1182 dlg_set_cancel(window, close_bt);
1184 gtk_object_set_data(GTK_OBJECT(window),
1185 E_DFILTER_EXPR_RELATION_LABEL_KEY, relation_label);
1186 gtk_object_set_data(GTK_OBJECT(window),
1187 E_DFILTER_EXPR_RELATION_LIST_KEY, relation_list);
1188 gtk_object_set_data(GTK_OBJECT(window),
1189 E_DFILTER_EXPR_RANGE_LABEL_KEY, range_label);
1190 gtk_object_set_data(GTK_OBJECT(window),
1191 E_DFILTER_EXPR_RANGE_ENTRY_KEY, range_entry);
1192 gtk_object_set_data(GTK_OBJECT(window),
1193 E_DFILTER_EXPR_VALUE_LABEL_KEY, value_label);
1194 gtk_object_set_data(GTK_OBJECT(window),
1195 E_DFILTER_EXPR_VALUE_ENTRY_KEY, value_entry);
1196 gtk_object_set_data(GTK_OBJECT(window),
1197 E_DFILTER_EXPR_VALUE_LIST_KEY, value_list);
1198 gtk_object_set_data(GTK_OBJECT(window),
1199 E_DFILTER_EXPR_VALUE_LIST_SW_KEY, value_list_scrolled_win);
1200 gtk_object_set_data(GTK_OBJECT(window),
1201 E_DFILTER_EXPR_ACCEPT_BT_KEY, accept_bt);
1204 * OK, we've finally built the entire list, complete with the row data,
1205 * and attached to the top-level widget pointers to the relevant
1206 * subwidgets, so it's safe to put the list in browse mode.
1208 gtk_clist_set_selection_mode (GTK_CLIST(tree),
1209 GTK_SELECTION_BROWSE);
1212 * Catch the "destroy" signal on our top-level window, and,
1213 * when it's destroyed, disconnect the signal we'll be
1216 gtk_signal_connect(GTK_OBJECT(window), "destroy",
1217 GTK_SIGNAL_FUNC(dfilter_expr_dlg_destroy_cb), filter_te);
1220 * Catch the "destroy" signal on the text entry widget to which
1221 * we're attached; if it's destroyed, we should destroy ourselves
1224 gtk_signal_connect(GTK_OBJECT(filter_te), "destroy",
1225 GTK_SIGNAL_FUNC(dfilter_expr_dlg_cancel_cb), window);
1227 gtk_widget_show(window);