Remove unneeded includes from ui folder
[metze/wireshark/wip.git] / ui / gtk / dfilter_expr_dlg.c
1 /* dfilter_expr_dlg.c
2  *
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.
6  *
7  * Copyright 2000, Jeffrey C. Foster <jfoste@woodward.com> and
8  * Guy Harris <guy@alum.mit.edu>
9  *
10  * Wireshark - Network traffic analyzer
11  * By Gerald Combs <gerald@wireshark.org>
12  * Copyright 2000 Gerald Combs
13  *
14  * This program is free software; you can redistribute it and/or
15  * modify it under the terms of the GNU General Public License
16  * as published by the Free Software Foundation; either version 2
17  * of the License, or (at your option) any later version.
18  *
19  * This program is distributed in the hope that it will be useful,
20  * but WITHOUT ANY WARRANTY; without even the implied warranty of
21  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22  * GNU General Public License for more details.
23  *
24  * You should have received a copy of the GNU General Public License
25  * along with this program; if not, write to the Free Software
26  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
27  */
28
29 /* Todo -
30  * may want to check the enable field to decide if protocol should be in tree
31  * improve speed of dialog box creation
32  *      - I believe this is slow because of tree widget creation.
33  *              1) could improve the widget
34  *              2) keep a copy in memory after the first time.
35  * user can pop multiple tree dialogs by pressing the "Tree" button multiple
36  *  times.  not a good thing.
37  * Sort the protocols and children
38  */
39
40 #include "config.h"
41 #include <string.h>
42
43 #include <gtk/gtk.h>
44
45 #include <epan/ftypes/ftypes-int.h>
46
47 #include "ui/simple_dialog.h"
48
49 #include "ui/gtk/gui_utils.h"
50 #include "ui/gtk/dlg_utils.h"
51 #include "ui/gtk/dfilter_expr_dlg.h"
52 #include "ui/gtk/proto_hier_tree_model.h"
53
54
55 #define E_DFILTER_EXPR_TREE_KEY                 "dfilter_expr_tree"
56 #define E_DFILTER_EXPR_CURRENT_VAR_KEY          "dfilter_expr_current_var"
57 #define E_DFILTER_EXPR_RELATION_LIST_KEY        "dfilter_expr_relation_list"
58 #define E_DFILTER_EXPR_RANGE_LABEL_KEY          "dfilter_expr_range_label"
59 #define E_DFILTER_EXPR_RANGE_ENTRY_KEY          "dfilter_expr_range_entry"
60 #define E_DFILTER_EXPR_VALUE_LABEL_KEY          "dfilter_expr_value_label"
61 #define E_DFILTER_EXPR_VALUE_ENTRY_KEY          "dfilter_expr_value_entry"
62 #define E_DFILTER_EXPR_VALUE_LIST_LABEL_KEY "dfilter_expr_value_list_label"
63 #define E_DFILTER_EXPR_VALUE_LIST_KEY           "dfilter_expr_value_list"
64 #define E_DFILTER_EXPR_VALUE_LIST_SW_KEY        "dfilter_expr_value_list_sw"
65 #define E_DFILTER_EXPR_OK_BT_KEY                "dfilter_expr_accept_bt"
66 #define E_DFILTER_EXPR_VALUE_KEY                "dfilter_expr_value"
67
68 static void show_relations(GtkWidget *relation_list, ftenum_t ftype);
69 static gboolean relation_is_presence_test(const char *string);
70 static void add_relation_list(GtkWidget *relation_list, const char *relation, gboolean sensitive);
71 static void build_boolean_values(GtkWidget *value_list_scrolled_win,
72                                  GtkWidget *value_list,
73                                  const true_false_string *values);
74 static void build_enum_values(GtkWidget *value_list_scrolled_win,
75                               GtkWidget *value_list,
76                               const value_string *values);
77 static void add_value_list_item(GtkWidget *value_list, const gchar *string,
78                                 const gpointer data);
79 static void display_value_fields(header_field_info *hfinfo,
80                                  gboolean is_comparison, GtkWidget *value_label,
81                                  GtkWidget *value_entry,
82                                  GtkWidget *value_list_label, GtkWidget *value_list,
83                                  GtkWidget *value_list_scrolled_win,
84                                  GtkWidget *range_label,
85                                  GtkWidget *range_entry);
86
87 /*
88  * Note that this is called every time the user clicks on an item,
89  * whether it is already selected or not.
90  */
91 static void
92 field_select_row_cb(GtkTreeSelection *sel, gpointer tree)
93 {
94     GtkWidget *window                  = (GtkWidget *)gtk_widget_get_toplevel((GtkWidget *)tree);
95     GtkWidget *relation_list           = (GtkWidget *)g_object_get_data(G_OBJECT(window),
96                                                E_DFILTER_EXPR_RELATION_LIST_KEY);
97     GtkWidget *range_label             = (GtkWidget *)g_object_get_data(G_OBJECT(window),
98                                              E_DFILTER_EXPR_RANGE_LABEL_KEY);
99     GtkWidget *range_entry             = (GtkWidget *)g_object_get_data(G_OBJECT(window),
100                                              E_DFILTER_EXPR_RANGE_ENTRY_KEY);
101     GtkWidget *value_label             = (GtkWidget *)g_object_get_data(G_OBJECT(window),
102                                              E_DFILTER_EXPR_VALUE_LABEL_KEY);
103     GtkWidget *value_entry             = (GtkWidget *)g_object_get_data(G_OBJECT(window),
104                                              E_DFILTER_EXPR_VALUE_ENTRY_KEY);
105     GtkWidget *value_list_label        = (GtkWidget *)g_object_get_data(G_OBJECT(window),
106                                              E_DFILTER_EXPR_VALUE_LIST_LABEL_KEY);
107     GtkWidget *value_list              = (GtkWidget *)g_object_get_data(G_OBJECT(window),
108                                              E_DFILTER_EXPR_VALUE_LIST_KEY);
109     GtkWidget *value_list_scrolled_win = (GtkWidget *)g_object_get_data(G_OBJECT(window),
110                                              E_DFILTER_EXPR_VALUE_LIST_SW_KEY);
111     GtkWidget *ok_bt                   = (GtkWidget *)g_object_get_data(G_OBJECT(window),
112                                              E_DFILTER_EXPR_OK_BT_KEY);
113     header_field_info *hfinfo, *cur_hfinfo;
114     const char *value_type;
115     char value_label_string[1024+1];   /* XXX - should be large enough */
116     GtkTreeModel *model;
117     GtkTreeIter   iter;
118
119     if (!gtk_tree_selection_get_selected(sel, &model, &iter))
120         return;
121     gtk_tree_model_get(model, &iter, 0, &hfinfo, -1);
122
123     /*
124      * What was the item that was last selected?
125      */
126     cur_hfinfo = (header_field_info *)g_object_get_data(G_OBJECT(window), E_DFILTER_EXPR_CURRENT_VAR_KEY);
127     if (cur_hfinfo == hfinfo) {
128         /*
129          * It's still selected; no need to change anything.
130          */
131         return;
132     }
133
134     /*
135      * Mark it as currently selected.
136      */
137     g_object_set_data(G_OBJECT(window), E_DFILTER_EXPR_CURRENT_VAR_KEY, hfinfo);
138
139     show_relations(relation_list, hfinfo->type);
140
141     /*
142      * Set the label for the value to indicate what type of value
143      * it is.
144      */
145     value_type = ftype_pretty_name(hfinfo->type);
146     if (value_type != NULL) {
147         /*
148          * Indicate what type of value it is.
149          */
150         g_snprintf(value_label_string, sizeof value_label_string,
151                  "Value (%s)", value_type);
152         gtk_label_set_text(GTK_LABEL(value_label), value_label_string);
153     }
154
155     /*
156      * Clear the entry widget for the value, as whatever
157      * was there before doesn't apply.
158      */
159     gtk_entry_set_text(GTK_ENTRY(value_entry), "");
160
161     switch (hfinfo->type) {
162
163     case FT_BOOLEAN:
164         /*
165          * The list of values should be the strings for "true"
166          * and "false"; show them in the value list.
167          */
168         build_boolean_values(value_list_scrolled_win, value_list,
169                              (const true_false_string *)hfinfo->strings);
170         break;
171
172     case FT_UINT8:
173     case FT_UINT16:
174     case FT_UINT24:
175     case FT_UINT32:
176     case FT_INT8:
177     case FT_INT16:
178     case FT_INT24:
179     case FT_INT32:
180         /*
181          * If this has a value_string table (not a range_string table) associated with it,
182          * fill up the list of values, otherwise clear the list of values.
183          */
184         /* XXX: ToDo: Implement "range-string" filter ?   */
185         if ((hfinfo->strings != NULL) &&
186             ! (hfinfo->display & BASE_RANGE_STRING) &&
187             ! (hfinfo->display & BASE_VAL64_STRING) &&
188             ! ((hfinfo->display & FIELD_DISPLAY_E_MASK) == BASE_CUSTOM)) {
189             const value_string *vals = (const value_string *)hfinfo->strings;
190             if (hfinfo->display & BASE_EXT_STRING)
191                 vals = VALUE_STRING_EXT_VS_P((value_string_ext *)vals);
192             build_enum_values(value_list_scrolled_win, value_list, vals);
193         } else
194             gtk_list_store_clear(GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(value_list))));
195         break;
196
197     default:
198         /*
199          * Clear the list of values.
200          */
201         gtk_list_store_clear(GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(value_list))));
202         break;
203     }
204
205     /*
206      * Display various items for the value, as appropriate.
207      * The relation we start out with is never a comparison.
208      */
209     display_value_fields(hfinfo, FALSE, value_label, value_entry,
210                          value_list_label, value_list, value_list_scrolled_win, range_label, range_entry);
211
212     /*
213      * XXX - in browse mode, there always has to be something
214      * selected, so this should always be sensitive.
215      */
216     gtk_widget_set_sensitive(ok_bt, TRUE);
217 }
218
219 static void
220 show_relations(GtkWidget *relation_list, ftenum_t ftype)
221 {
222         GtkTreeIter iter;
223         /*
224          * Clear out the currently displayed list of relations.
225          */
226         gtk_list_store_clear(GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(relation_list))));
227
228         /*
229          * Add the supported relations.
230          */
231         add_relation_list(relation_list, "is present", TRUE);
232         add_relation_list(relation_list, "==",
233             ftype_can_eq(ftype) || (ftype_can_slice(ftype) && ftype_can_eq(FT_BYTES)));
234         add_relation_list(relation_list, "!=",
235             ftype_can_ne(ftype) || (ftype_can_slice(ftype) && ftype_can_ne(FT_BYTES)));
236         add_relation_list(relation_list, ">",
237             ftype_can_gt(ftype) || (ftype_can_slice(ftype) && ftype_can_gt(FT_BYTES)));
238
239         add_relation_list(relation_list, "<",
240             ftype_can_lt(ftype) || (ftype_can_slice(ftype) && ftype_can_lt(FT_BYTES)));
241         add_relation_list(relation_list, ">=",
242             ftype_can_ge(ftype) || (ftype_can_slice(ftype) && ftype_can_ge(FT_BYTES)));
243         add_relation_list(relation_list, "<=",
244             ftype_can_le(ftype) || (ftype_can_slice(ftype) && ftype_can_le(FT_BYTES)));
245         add_relation_list(relation_list, "contains",
246             ftype_can_contains(ftype) || (ftype_can_slice(ftype) && ftype_can_contains(FT_BYTES)));
247         add_relation_list(relation_list, "matches",
248             ftype_can_matches(ftype) || (ftype_can_slice(ftype) && ftype_can_matches(FT_BYTES)));
249
250         gtk_tree_model_get_iter_first(gtk_tree_view_get_model(GTK_TREE_VIEW(relation_list)), &iter);
251         gtk_tree_selection_select_iter(gtk_tree_view_get_selection(GTK_TREE_VIEW(relation_list)), &iter);
252 }
253
254 /*
255  * Given a string that represents a test to be made on a field, returns
256  * TRUE if it tests for the field's presence, FALSE otherwise.
257  */
258 static gboolean
259 relation_is_presence_test(const char *string)
260 {
261         return (strcmp(string, "is present") == 0);
262 }
263
264 static void
265 add_relation_list(GtkWidget *relation_list, const char *relation, gboolean sensitive)
266 {
267     GtkListStore *store = GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(relation_list)));
268     GtkTreeIter   iter;
269
270     /* XXX: I currently see no way to insensitive the item,
271      * so for a first step, just don't show it (as before these changes :-) */
272     if (!sensitive) {
273         return;
274     }
275
276     gtk_list_store_append(store, &iter);
277     gtk_list_store_set(store, &iter, 0, relation, -1);
278 }
279
280 static void
281 relation_list_sel_cb(GtkTreeSelection *sel, gpointer user_data _U_)
282 {
283     GtkWidget *window = gtk_widget_get_toplevel(GTK_WIDGET(gtk_tree_selection_get_tree_view(sel)));
284     GtkWidget *range_label =
285         (GtkWidget *)g_object_get_data(G_OBJECT(window), E_DFILTER_EXPR_RANGE_LABEL_KEY);
286     GtkWidget *range_entry =
287         (GtkWidget *)g_object_get_data(G_OBJECT(window), E_DFILTER_EXPR_RANGE_ENTRY_KEY);
288     GtkWidget *value_label =
289         (GtkWidget *)g_object_get_data(G_OBJECT(window), E_DFILTER_EXPR_VALUE_LABEL_KEY);
290     GtkWidget *value_entry =
291         (GtkWidget *)g_object_get_data(G_OBJECT(window), E_DFILTER_EXPR_VALUE_ENTRY_KEY);
292     GtkWidget *value_list_label =
293         (GtkWidget *)g_object_get_data(G_OBJECT(window), E_DFILTER_EXPR_VALUE_LIST_LABEL_KEY);
294     GtkWidget *value_list =
295         (GtkWidget *)g_object_get_data(G_OBJECT(window), E_DFILTER_EXPR_VALUE_LIST_KEY);
296     GtkWidget *value_list_scrolled_win =
297         (GtkWidget *)g_object_get_data(G_OBJECT(window), E_DFILTER_EXPR_VALUE_LIST_SW_KEY);
298     header_field_info *hfinfo =
299         (header_field_info *)g_object_get_data(G_OBJECT(window), E_DFILTER_EXPR_CURRENT_VAR_KEY);
300     gchar *item_str;
301     GtkTreeModel *model;
302     GtkTreeIter   iter;
303
304     /*
305      * What's the relation?
306      */
307     if (!gtk_tree_selection_get_selected(sel, &model, &iter))
308         return;
309     gtk_tree_model_get(model, &iter, 0, &item_str, -1);
310
311     /*
312      * Update the display of various items for the value, as appropriate.
313      */
314     display_value_fields(hfinfo,
315                          !relation_is_presence_test(item_str),
316                          value_label, value_entry, value_list_label, value_list,
317                          value_list_scrolled_win, range_label, range_entry);
318     g_free(item_str);
319 }
320
321 static void
322 build_boolean_values(GtkWidget *value_list_scrolled_win, GtkWidget *value_list,
323                      const true_false_string *values)
324 {
325     static const true_false_string true_false = { "True", "False" };
326     GtkTreeSelection *sel;
327     GtkTreeIter       iter;
328
329     sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(value_list));
330
331     /*
332      * Clear out the items for the list, and put in the names
333      * from the value_string list.
334      */
335     gtk_list_store_clear(GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(value_list))));
336
337     /*
338      * Put the list in single mode, so we don't get any selection
339      * events while we're building it (i.e., so we don't get any
340      * on a list item BEFORE WE GET TO SET THE DATA FOR THE LIST
341      * ITEM SO THAT THE HANDLER CAN HANDLE IT).
342      */
343     gtk_tree_selection_set_mode(sel, GTK_SELECTION_SINGLE);
344
345     /*
346      * Build the list.
347      */
348     if (values == NULL)
349         values = &true_false;
350     add_value_list_item(value_list, values->true_string, (gpointer) values);
351     add_value_list_item(value_list, values->false_string, NULL);
352
353     /*
354      * OK, we're done, so we can finally put it in browse mode.
355      * Select the first item, so that the user doesn't have to, under
356      * the assumption that they're most likely to test if something
357      * is true, not false.
358      */
359     gtk_tree_selection_set_mode(sel, GTK_SELECTION_BROWSE);
360     gtk_tree_model_get_iter_first(gtk_tree_view_get_model(GTK_TREE_VIEW(value_list)), &iter);
361     gtk_tree_selection_select_iter(sel, &iter);
362
363     gtk_widget_show_all(value_list_scrolled_win);
364 }
365
366 static void
367 build_enum_values(GtkWidget *value_list_scrolled_win _U_, GtkWidget *value_list,
368                   const value_string *values)
369 {
370     GtkTreeSelection *sel;
371
372     sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(value_list));
373     /*
374      * Clear out the items for the list, and put in the names
375      * from the value_string list.
376      */
377     gtk_list_store_clear(GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(value_list))));
378
379     /*
380      * Put the list in single mode, so we don't get any selection
381      * events while we're building it (i.e., so we don't get any
382      * on a list item BEFORE WE GET TO SET THE DATA FOR THE LIST
383      * ITEM SO THAT THE HANDLER CAN HANDLE IT).
384      */
385     gtk_tree_selection_set_mode(sel, GTK_SELECTION_SINGLE);
386
387     /*
388      * Build the list.
389      */
390     while (values->strptr != NULL) {
391         add_value_list_item(value_list, values->strptr, (const gpointer) values);
392         values++;
393     }
394
395     /*
396      * OK, we're done, so we can finally put it in browse mode.
397      */
398     gtk_tree_selection_set_mode(sel, GTK_SELECTION_BROWSE);
399 }
400
401 static void
402 add_value_list_item(GtkWidget *value_list, const gchar *string, const gpointer data)
403 {
404     GtkListStore *store = GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(value_list)));
405     GtkTreeIter       iter;
406
407     gtk_list_store_append(store, &iter);
408     gtk_list_store_set(store, &iter, 0, string, 1, data, -1);
409 }
410
411 /*
412  * Show or hide the various values fields as appropriate for the field
413  * and currently-selected relation.
414  */
415 static void
416 display_value_fields(header_field_info *hfinfo, gboolean is_comparison,
417                      GtkWidget *value_label, GtkWidget *value_entry,
418                      GtkWidget *value_list_label,
419                      GtkWidget *value_list _U_,
420                      GtkWidget *value_list_scrolled_win, GtkWidget *range_label,
421                      GtkWidget *range_entry)
422 {
423         /* Default values */
424         gboolean show_value_label = FALSE;
425         gboolean show_value_list = FALSE;
426         gboolean show_range = FALSE;
427
428         /*
429          * Either:
430          *
431          *      this is an FT_NONE variable, in which case you can
432          *      only check whether it's present or absent in the
433          *      protocol tree
434          *
435          * or
436          *
437          *      this is a Boolean variable, in which case you
438          *      can't specify a value to compare with, you can
439          *      only specify whether to test for the Boolean
440          *      being true or to test for it being false
441          *
442          * or
443          *
444          *      this isn't a Boolean variable, in which case you
445          *      can test for its presence in the protocol tree,
446          *      and the relation is such a test, in
447          *      which case you don't compare with a value
448          *
449          * so we hide the value entry.
450          */
451
452         switch (hfinfo->type) {
453
454         case FT_BOOLEAN:
455                 if (is_comparison) {
456                         show_value_label = TRUE;  /* XXX: Allow value entry (contrary to the comment above) ?? */
457                         show_value_list  = TRUE;
458                 }
459                 break;
460
461         case FT_UINT8:
462         case FT_UINT16:
463         case FT_UINT24:
464         case FT_UINT32:
465         case FT_INT8:
466         case FT_INT16:
467         case FT_INT24:
468         case FT_INT32:
469                 if (is_comparison) {
470                         show_value_label = TRUE;
471                         if ((hfinfo->strings != NULL) && !(hfinfo->display & BASE_RANGE_STRING)) {
472                         /*
473                          * We have a list of values to show.
474                          */
475                                 show_value_list = TRUE;
476                         }
477                 }
478                 break;
479
480         default:
481                 /*
482                  * There is no list of names for values; only show the value_label if needed.
483                  */
484                 if (is_comparison)
485                         show_value_label = TRUE;
486                 break;
487         }
488
489         gtk_widget_set_sensitive(value_label,               show_value_label);
490         gtk_widget_set_sensitive(value_entry,               show_value_label);
491
492         gtk_widget_set_sensitive(value_list_label,          show_value_list);
493         gtk_widget_set_sensitive(value_list_scrolled_win,   show_value_list);
494
495         /*
496          * Is this a comparison, and are ranges supported by this type?
497          * If both are true, show the range stuff, otherwise hide it.
498          */
499         show_range = (is_comparison && ftype_can_slice(hfinfo->type));
500         gtk_widget_set_sensitive(range_label, show_range);
501         gtk_widget_set_sensitive(range_entry, show_range);
502 }
503
504 static void
505 value_list_sel_cb(GtkTreeSelection *sel, gpointer value_entry_arg)
506 {
507     GtkWidget *value_entry = (GtkWidget *)value_entry_arg;
508     GtkWidget *window = gtk_widget_get_toplevel(GTK_WIDGET(gtk_tree_selection_get_tree_view(sel)));
509     GtkTreeModel *model;
510     GtkTreeIter   iter;
511     header_field_info *hfinfo = (header_field_info *)g_object_get_data(G_OBJECT(window),
512                                                 E_DFILTER_EXPR_CURRENT_VAR_KEY);
513     const value_string *value = NULL;
514     gchar *value_display_string = NULL;
515
516     if (!gtk_tree_selection_get_selected(sel, &model, &iter))
517         return;
518     gtk_tree_model_get(model, &iter, 1, &value, -1);
519
520     /*
521      * This should either be a numeric type or a Boolean type.
522      */
523     if (hfinfo->type == FT_BOOLEAN) {
524         /*
525          * Boolean type; if the value key for the selected item
526          * is non-null, it's the item for "true", otherwise it's
527          * the item for "false".  Compare with 1 if we're
528          * testing for "true", and compare with 0 if we're
529          * testing for "false".
530          */
531         if (value != NULL)
532                 value_display_string = g_strdup("1");
533         else
534                 value_display_string = g_strdup("0");
535     } else {
536         /*
537          * Numeric type; get the value corresponding to the
538          * selected item, and display it in the base for this
539          * field.
540          */
541         switch ((hfinfo->display) & FIELD_DISPLAY_E_MASK) {
542
543         case BASE_NONE:
544         case BASE_DEC:
545         case BASE_DEC_HEX:
546             switch (hfinfo->type) {
547
548             case FT_UINT8:
549             case FT_UINT16:
550             case FT_UINT24:
551             case FT_UINT32:
552                 value_display_string = g_strdup_printf("%u", value->value);
553                 break;
554
555             case FT_INT8:
556             case FT_INT16:
557             case FT_INT24:
558             case FT_INT32:
559                 value_display_string = g_strdup_printf("%d", value->value);
560                 break;
561
562             default:
563                 g_assert_not_reached();
564             }
565             break;
566
567         case BASE_HEX:
568         case BASE_HEX_DEC:
569             value_display_string = g_strdup_printf("0x%x", value->value);
570             break;
571
572         case BASE_OCT:
573             value_display_string = g_strdup_printf("%#o", value->value);
574             break;
575
576         default:
577             g_assert_not_reached();
578         }
579     }
580
581     gtk_entry_set_text(GTK_ENTRY(value_entry), value_display_string);
582     g_free (value_display_string);
583 }
584
585 static void
586 dfilter_report_bad_value(const char *format, ...)
587 {
588         char error_msg_buf[1024];
589         va_list args;
590
591         va_start(args, format);
592         g_vsnprintf(error_msg_buf, sizeof error_msg_buf, format, args);
593         va_end(args);
594
595         simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "%s", error_msg_buf);
596 }
597
598 static void
599 dfilter_expr_dlg_accept_cb(GtkWidget *w, gpointer filter_te_arg)
600 {
601     GtkWidget *filter_te = (GtkWidget *)filter_te_arg;
602     GtkWidget *window = gtk_widget_get_toplevel(w);
603     GtkWidget *relation_list =
604         (GtkWidget *)g_object_get_data(G_OBJECT(window), E_DFILTER_EXPR_RELATION_LIST_KEY);
605     GtkWidget *range_entry =
606         (GtkWidget *)g_object_get_data(G_OBJECT(window), E_DFILTER_EXPR_RANGE_ENTRY_KEY);
607     GtkWidget *value_entry =
608         (GtkWidget *)g_object_get_data(G_OBJECT(window), E_DFILTER_EXPR_VALUE_ENTRY_KEY);
609     header_field_info *hfinfo;
610     gchar        *item_str;
611     gchar        *range_str, *stripped_range_str;
612     gchar        *value_str, *stripped_value_str;
613     int           pos;
614     gchar        *chars;
615     ftenum_t      ftype;
616     gboolean      can_compare;
617     fvalue_t     *fvalue;
618     GtkTreeModel *model;
619     GtkTreeIter   iter;
620     gboolean      quote_it;
621
622     /*
623      * Get the variable to be tested.
624      */
625     hfinfo = (header_field_info *)g_object_get_data(G_OBJECT(window), E_DFILTER_EXPR_CURRENT_VAR_KEY);
626
627     /*
628      * Get the relation operator to use.
629      */
630     if (gtk_tree_selection_get_selected(gtk_tree_view_get_selection(GTK_TREE_VIEW(relation_list)),
631                                         &model, &iter)) {
632         gtk_tree_model_get(model, &iter, 0, &item_str, -1);
633     } else {
634         /* Nothing selected */
635         return;
636     }
637
638     /*
639      * Get the range to use, if any.
640      */
641     if (gtk_widget_get_sensitive(range_entry)) {
642         range_str = g_strdup(gtk_entry_get_text(GTK_ENTRY(range_entry)));
643         /*
644          * XXX - strip this even for strings?
645          * Doing so for strings means you can't match a string that has
646          * leading or trailing whitespace, but you can't see trailing
647          * whitespace in a text field, so it's not clear that it's
648          * a good idea to allow that.
649          */
650         stripped_range_str = g_strstrip(range_str);
651         if (strcmp(stripped_range_str, "") == 0) {
652             /*
653              * No range was specified.
654              */
655             g_free(range_str);
656             range_str = NULL;
657             stripped_range_str = NULL;
658         }
659
660         /*
661          * XXX - check it for validity?
662          */
663     } else {
664         range_str = NULL;
665         stripped_range_str = NULL;
666     }
667
668     /*
669      * If a range was specified, the type of the LHS of the
670      * comparison is FT_BYTES; otherwise, it's the type of the field.
671      */
672     if (range_str == NULL)
673         ftype = hfinfo->type;
674     else
675         ftype = FT_BYTES;
676
677     /*
678      * Make sure the relation is valid for the type in question.
679      * We may be offering relations that the type of the field
680      * can't support, because the field's type supports slicing,
681      * and the relation *is* supported on byte strings.
682      */
683     if (strcmp(item_str, "==") == 0)
684         can_compare = ftype_can_eq(ftype);
685     else if (strcmp(item_str, "!=") == 0)
686         can_compare = ftype_can_ne(ftype);
687     else if (strcmp(item_str, ">") == 0)
688         can_compare = ftype_can_gt(ftype);
689     else if (strcmp(item_str, "<") == 0)
690         can_compare = ftype_can_lt(ftype);
691     else if (strcmp(item_str, ">=") == 0)
692         can_compare = ftype_can_ge(ftype);
693     else if (strcmp(item_str, "<=") == 0)
694         can_compare = ftype_can_le(ftype);
695     else if (strcmp(item_str, "contains") == 0)
696         can_compare = ftype_can_contains(ftype);
697     else if (strcmp(item_str, "matches") == 0)
698         can_compare = ftype_can_matches(ftype);
699     else
700         can_compare = TRUE;     /* not a comparison */
701     if (!can_compare) {
702         if (range_str == NULL) {
703             simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
704                           "That field can't be tested with \"%s\".",
705                           item_str);
706         } else {
707             simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
708                           "Ranges of that field can't be tested with \"%s\".",
709                           item_str);
710         }
711         g_free(range_str);
712         g_free(item_str);
713         return;
714     }
715
716     /*
717      * Get the value to use, if any.
718      */
719     if (gtk_widget_get_sensitive(value_entry)) {
720         value_str = g_strdup(gtk_entry_get_text(GTK_ENTRY(value_entry)));
721         stripped_value_str = g_strstrip(value_str);
722         if (strcmp(stripped_value_str, "") == 0) {
723             /*
724              * This field takes a value, but they didn't supply
725              * one.
726              */
727             simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
728                           "That field must be compared with a value, "
729                           "but you didn't specify a value with which to "
730                           "compare it.");
731             g_free(range_str);
732             g_free(value_str);
733             g_free(item_str);
734             return;
735         }
736
737         /*
738          * Make sure the value is valid.
739          *
740          * If no range string was specified, it must be valid
741          * for the type of the field; if a range string was
742          * specified, must be valid for FT_BYTES.
743          */
744         if (strcmp(item_str, "contains") == 0) {
745             fvalue = fvalue_from_unparsed(ftype, stripped_value_str, TRUE,
746                                           dfilter_report_bad_value);
747         }
748         else {
749             fvalue = fvalue_from_unparsed(ftype, stripped_value_str, FALSE,
750                                           dfilter_report_bad_value);
751         }
752         if (fvalue == NULL) {
753             /*
754              * It's not valid.
755              *
756              * The dialog box was already popped up by
757              * "dfilter_report_bad_value()".
758              */
759             g_free(range_str);
760             g_free(value_str);
761             g_free(item_str);
762             return;
763         }
764         FVALUE_FREE(fvalue);
765     } else {
766         value_str = NULL;
767         stripped_value_str = NULL;
768     }
769
770     /*
771      * Insert the expression at the current cursor position.
772      * If there's a non-whitespace character to the left of it,
773      * insert a blank first; if there's a non-whitespace character
774      * to the right of it, insert a blank after it.
775      */
776     pos = gtk_editable_get_position(GTK_EDITABLE(filter_te));
777     chars = gtk_editable_get_chars(GTK_EDITABLE(filter_te), pos, pos + 1);
778     if (strcmp(chars, "") != 0 && !g_ascii_isspace(chars[0]))
779         gtk_editable_insert_text(GTK_EDITABLE(filter_te), " ", 1, &pos);
780     g_free(chars);
781
782     gtk_editable_insert_text(GTK_EDITABLE(filter_te), hfinfo->abbrev,
783                              (gint) strlen(hfinfo->abbrev), &pos);
784     if (range_str != NULL) {
785         gtk_editable_insert_text(GTK_EDITABLE(filter_te), "[", 1, &pos);
786         gtk_editable_insert_text(GTK_EDITABLE(filter_te),
787                                  stripped_range_str, (gint) strlen(stripped_range_str), &pos);
788         gtk_editable_insert_text(GTK_EDITABLE(filter_te), "]", 1, &pos);
789         g_free(range_str);
790     }
791     if (!relation_is_presence_test(item_str)) {
792         gtk_editable_insert_text(GTK_EDITABLE(filter_te), " ", 1, &pos);
793         gtk_editable_insert_text(GTK_EDITABLE(filter_te), item_str,
794                                  (gint) strlen(item_str), &pos);
795     }
796     if (value_str != NULL) {
797         gtk_editable_insert_text(GTK_EDITABLE(filter_te), " ", 1, &pos);
798         /*
799          * XXX - we should do this by generating an appropriate display
800          * filter value string for this field; that requires us to have
801          * a "generate display filter string" method for every FT_ type.
802          */
803         switch (hfinfo->type) {
804
805         case FT_STRING:
806         case FT_STRINGZ:
807         case FT_UINT_STRING:
808         case FT_ABSOLUTE_TIME:
809             /*
810              * Always put quotes around the string.
811              */
812             quote_it = TRUE;
813             break;
814
815         default:
816             /*
817              * If the string contains white space, put quotes around it.
818              */
819             quote_it = (strpbrk(stripped_value_str, " \t") != NULL);
820             break;
821         }
822         if (quote_it) {
823             /*
824              * Put quotes around the string.
825              */
826             gtk_editable_insert_text(GTK_EDITABLE(filter_te), "\"",
827                                      1, &pos);
828         }
829         gtk_editable_insert_text(GTK_EDITABLE(filter_te),
830                                  stripped_value_str, (gint) strlen(stripped_value_str), &pos);
831         if (quote_it) {
832             /*
833              * Put quotes around the string.
834              */
835             gtk_editable_insert_text(GTK_EDITABLE(filter_te), "\"",
836                                      1, &pos);
837         }
838         g_free(value_str);
839     }
840     chars = gtk_editable_get_chars(GTK_EDITABLE(filter_te), pos + 1, pos + 2);
841     if (strcmp(chars, "") != 0 && !g_ascii_isspace(chars[0]))
842         gtk_editable_insert_text(GTK_EDITABLE(filter_te), " ", 1, &pos);
843     g_free(chars);
844
845     /*
846      * Put the cursor after the expression we just entered into
847      * the text entry widget.
848      */
849     gtk_editable_set_position(GTK_EDITABLE(filter_te), pos);
850
851     /*
852      * We're done; destroy the dialog box (which is the top-level
853      * widget for the "Accept" button).
854      */
855     window_destroy(window);
856     g_free(item_str);
857 }
858
859 static void
860 dfilter_expr_dlg_cancel_cb(GtkWidget *w _U_, gpointer parent_w)
861 {
862         /*
863          * User pressed the cancel button; close the dialog box.
864          */
865         window_destroy(GTK_WIDGET(parent_w));
866 }
867
868 /* Treat this as a cancel, by calling "prefs_main_cancel_cb()" */
869 static gboolean
870 dfilter_expr_dlg_delete_event_cb(GtkWidget *w _U_, GdkEvent *event _U_,
871                                  gpointer parent_w)
872 {
873         dfilter_expr_dlg_cancel_cb(NULL, parent_w);
874         return FALSE;
875 }
876
877 static void
878 dfilter_expr_dlg_destroy_cb(GtkWidget *w, gpointer filter_te)
879 {
880         /*
881          * The dialog box is being destroyed; disconnect from the
882          * "destroy" signal on the text entry box to which we're
883          * attached, as the handler for that signal is supposed
884          * to destroy us, but we're already gone.
885          */
886         g_signal_handlers_disconnect_by_func(filter_te, dfilter_expr_dlg_cancel_cb, w);
887 }
888
889 GtkWidget *
890 dfilter_expr_dlg_new(GtkWidget *filter_te)
891 {
892     GtkWidget *window, *main_vb, *main_hb;
893
894     GtkWidget *field_vb, *field_tree_lb, *field_tree, *tree_scrolled_win;
895
896     GtkWidget *relation_vb, *relation_label, *relation_list, *relation_list_scrolled_win;
897 /*    GtkWidget *relation_present_rb, *relation_equals_rb, *relation_unequals_rb,
898               *relation_greater_rb, *relation_less_rb,
899               *relation_greaterequal_rb, *relation_lessequal_rb,
900               *relation_contains_rb, *relation_matches_rb;*/
901
902     GtkWidget *value_vb, *value_label, *value_entry;
903     GtkWidget *value_list_label, *value_list_scrolled_win, *value_list;
904     GtkWidget *range_label, *range_entry;
905
906     GtkWidget *list_bb, *ok_bt, *cancel_bt;
907     ProtoHierTreeModel *store_proto;
908     GtkTreeModel *store;
909     GtkTreeSelection *selection;
910     GtkCellRenderer *renderer;
911     GtkTreeViewColumn *column;
912     GtkListStore      *l_store;
913     GtkTreeSelection  *l_sel;
914
915         proto_initialize_all_prefixes();
916
917     window = dlg_conf_window_new("Wireshark: Filter Expression");
918     gtk_window_set_default_size(GTK_WINDOW(window), 500, 400);
919     gtk_container_set_border_width(GTK_CONTAINER(window), 5);
920
921     main_vb = ws_gtk_box_new(GTK_ORIENTATION_VERTICAL, 5, FALSE);
922     gtk_container_set_border_width(GTK_CONTAINER(main_vb), 5);
923     gtk_container_add(GTK_CONTAINER(window), main_vb);
924
925     main_hb = ws_gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 5, FALSE);
926     gtk_container_set_border_width(GTK_CONTAINER(main_hb), 5);
927     gtk_box_pack_start(GTK_BOX (main_vb), main_hb, TRUE, TRUE, 0);
928
929     field_vb = ws_gtk_box_new(GTK_ORIENTATION_VERTICAL, 5, FALSE);
930     gtk_container_set_border_width(GTK_CONTAINER(field_vb), 5);
931     gtk_box_pack_start(GTK_BOX (main_hb), field_vb, TRUE, TRUE, 0);
932
933     field_tree_lb = gtk_label_new("Field name");
934     gtk_misc_set_alignment(GTK_MISC(field_tree_lb), 0.0f, 0.0f);
935     gtk_box_pack_start(GTK_BOX(field_vb), field_tree_lb, FALSE, FALSE, 0);
936
937     tree_scrolled_win = scrolled_window_new(NULL, NULL);
938     gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(tree_scrolled_win),
939                                    GTK_SHADOW_IN);
940     gtk_box_pack_start(GTK_BOX(field_vb), tree_scrolled_win, TRUE, TRUE, 0);
941     gtk_widget_set_size_request(tree_scrolled_win, 300, -1);
942
943
944     field_tree = tree_view_new(GTK_TREE_MODEL(NULL));
945     gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(field_tree), FALSE);
946     selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(field_tree));
947     gtk_tree_selection_set_mode(selection, GTK_SELECTION_BROWSE);
948     renderer = gtk_cell_renderer_text_new();
949     column = gtk_tree_view_column_new_with_attributes("Field name", renderer,
950                                                       "text", 1, NULL);
951     gtk_tree_view_append_column(GTK_TREE_VIEW(field_tree), column);
952     gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
953     gtk_tree_view_column_set_sort_column_id(column, 0);
954     g_signal_connect(selection, "changed", G_CALLBACK(field_select_row_cb), field_tree);
955     gtk_container_add(GTK_CONTAINER(tree_scrolled_win), field_tree);
956
957     relation_vb = ws_gtk_box_new(GTK_ORIENTATION_VERTICAL, 5, FALSE);
958     gtk_container_set_border_width(GTK_CONTAINER(relation_vb), 5);
959     gtk_box_pack_start(GTK_BOX (main_hb), relation_vb, TRUE, TRUE, 0);
960
961     relation_label = gtk_label_new("Relation");
962     gtk_misc_set_alignment(GTK_MISC(relation_label), 0.0f, 0.0f);
963     gtk_box_pack_start(GTK_BOX(relation_vb), relation_label, FALSE, FALSE, 0);
964
965     relation_list_scrolled_win = scrolled_window_new(NULL, NULL);
966     /* never use a scrollbar in x direction, show the complete relation string */
967     gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(relation_list_scrolled_win),
968                                    GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
969     gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(relation_list_scrolled_win),
970                                    GTK_SHADOW_IN);
971
972     l_store = gtk_list_store_new(1, G_TYPE_STRING);
973     relation_list = tree_view_new(GTK_TREE_MODEL(l_store));
974     gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(relation_list), FALSE);
975     g_object_unref(G_OBJECT(l_store));
976     renderer = gtk_cell_renderer_text_new();
977     column = gtk_tree_view_column_new_with_attributes("relation", renderer,
978                                                       "text", 0, NULL);
979     gtk_tree_view_append_column(GTK_TREE_VIEW(relation_list), column);
980     l_sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(relation_list));
981     gtk_tree_selection_set_mode(l_sel, GTK_SELECTION_BROWSE);
982     gtk_container_add(GTK_CONTAINER(relation_list_scrolled_win), relation_list);
983     gtk_box_pack_start(GTK_BOX(relation_vb), relation_list_scrolled_win, TRUE, TRUE, 0);
984
985     /*
986      * OK, show the relation label and range stuff as it would be
987      * with everything turned on, so it'll request as much space
988      * as it'll ever need, so the dialog box and widgets start out
989      * with the right sizes.
990      *
991      * XXX - this doesn't work.  It *doesn't* request as much space
992      * as it'll ever need.
993      *
994      * XXX - FT_UINT8 doesn't support ranges, so even if it did work,
995      * it wouldn't work right.
996      *
997      * XXX - this no longer affects the range stuff, as that's
998      * controlled both by the type and by the relational operator
999      * selected.
1000      */
1001     show_relations(relation_list, FT_UINT8);
1002
1003     /*
1004     relation_present_rb = gtk_radio_button_new_with_mnemonic_from_widget(NULL, "is present");
1005     gtk_box_pack_start(GTK_BOX(relation_vb), relation_present_rb, FALSE, FALSE, 0);
1006
1007     relation_equals_rb = gtk_radio_button_new_with_mnemonic_from_widget(GTK_RADIO_BUTTON(relation_present_rb), "==");
1008     gtk_box_pack_start(GTK_BOX(relation_vb), relation_equals_rb, FALSE, FALSE, 0);
1009
1010     relation_unequals_rb = gtk_radio_button_new_with_mnemonic_from_widget(GTK_RADIO_BUTTON(relation_present_rb), "!=");
1011     gtk_box_pack_start(GTK_BOX(relation_vb), relation_unequals_rb, FALSE, FALSE, 0);
1012
1013     relation_greater_rb = gtk_radio_button_new_with_mnemonic_from_widget(GTK_RADIO_BUTTON(relation_present_rb), ">");
1014     gtk_box_pack_start(GTK_BOX(relation_vb), relation_greater_rb, FALSE, FALSE, 0);
1015
1016     relation_less_rb = gtk_radio_button_new_with_mnemonic_from_widget(GTK_RADIO_BUTTON(relation_present_rb), "<");
1017     gtk_box_pack_start(GTK_BOX(relation_vb), relation_less_rb, FALSE, FALSE, 0);
1018
1019     relation_greaterequal_rb = gtk_radio_button_new_with_mnemonic_from_widget(GTK_RADIO_BUTTON(relation_present_rb), ">=");
1020     gtk_box_pack_start(GTK_BOX(relation_vb), relation_greaterequal_rb, FALSE, FALSE, 0);
1021
1022     relation_lessequal_rb = gtk_radio_button_new_with_mnemonic_from_widget(GTK_RADIO_BUTTON(relation_present_rb), "<=");
1023     gtk_box_pack_start(GTK_BOX(relation_vb), relation_lessequal_rb, FALSE, FALSE, 0);
1024
1025     relation_contains_rb = gtk_radio_button_new_with_mnemonic_from_widget(GTK_RADIO_BUTTON(relation_present_rb), "contains");
1026     gtk_box_pack_start(GTK_BOX(relation_vb), relation_contains_rb, FALSE, FALSE, 0);
1027
1028     relation_matches_rb = gtk_radio_button_new_with_mnemonic_from_widget(GTK_RADIO_BUTTON(relation_present_rb), "matches");
1029     gtk_box_pack_start(GTK_BOX(relation_vb), relation_matches_rb, FALSE, FALSE, 0);
1030 */
1031     /* value column */
1032     value_vb = ws_gtk_box_new(GTK_ORIENTATION_VERTICAL, 5, FALSE);
1033     gtk_container_set_border_width(GTK_CONTAINER(value_vb), 5);
1034     gtk_box_pack_start(GTK_BOX (main_hb), value_vb, TRUE, TRUE, 0);
1035
1036     value_label = gtk_label_new("Value");
1037     gtk_misc_set_alignment(GTK_MISC(value_label), 0.0f, 0.0f);
1038     gtk_box_pack_start(GTK_BOX(value_vb), value_label, FALSE, FALSE, 0);
1039
1040     value_entry = gtk_entry_new();
1041     gtk_box_pack_start(GTK_BOX(value_vb), value_entry, FALSE, FALSE, 0);
1042
1043     value_list_label = gtk_label_new("Predefined values:");
1044     gtk_misc_set_alignment(GTK_MISC(value_list_label), 0.0f, 0.0f);
1045     gtk_box_pack_start(GTK_BOX(value_vb), value_list_label, FALSE, FALSE, 0);
1046
1047     value_list_scrolled_win = scrolled_window_new(NULL, NULL);
1048     gtk_box_pack_start(GTK_BOX(value_vb), value_list_scrolled_win, TRUE,
1049                        TRUE, 0);
1050
1051     l_store = gtk_list_store_new(2, G_TYPE_STRING, G_TYPE_POINTER);
1052     value_list = tree_view_new(GTK_TREE_MODEL(l_store));
1053     gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(value_list), FALSE);
1054     g_object_unref(G_OBJECT(l_store));
1055     renderer = gtk_cell_renderer_text_new();
1056     column = gtk_tree_view_column_new_with_attributes("value", renderer,
1057                                                       "text", 0, NULL);
1058     gtk_tree_view_append_column(GTK_TREE_VIEW(value_list), column);
1059     g_signal_connect(gtk_tree_view_get_selection(GTK_TREE_VIEW(value_list)),
1060                    "changed", G_CALLBACK(value_list_sel_cb), value_entry);
1061
1062     /*
1063      * The value stuff may be hidden or shown depending on what
1064      * relation was selected; connect to the "changed" signal
1065      * for the relation list, so we can make that happen.
1066      */
1067     g_signal_connect(gtk_tree_view_get_selection(GTK_TREE_VIEW(relation_list)),
1068                    "changed", G_CALLBACK(relation_list_sel_cb), NULL);
1069     l_sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(value_list));
1070     gtk_tree_selection_set_mode(l_sel, GTK_SELECTION_SINGLE);
1071 #if ! GTK_CHECK_VERSION(3,8,0)
1072     gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(value_list_scrolled_win),
1073                                           value_list);
1074 #else
1075     gtk_container_add(GTK_CONTAINER(value_list_scrolled_win),
1076                                           value_list);
1077 #endif
1078     /* This remains hidden until an enumerated field is selected */
1079
1080     /*
1081      * Put the items in the Tree; we don't want to do that until
1082      * we've constructed the value list and set the tree's
1083      * E_DFILTER_EXPR_VALUE_LIST_KEY data to point to it, and
1084      * constructed the "Accept" button and set the tree's
1085      * E_DFILTER_EXPR_OK_BT_KEY data to point to it, so that
1086      * when the list item is "helpfully" automatically selected for us
1087      * we're ready to cope with the selection signal.
1088      */
1089
1090     store_proto = proto_hier_tree_model_new();
1091     store = gtk_tree_model_sort_new_with_model(GTK_TREE_MODEL(store_proto));
1092     gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(store), 1, GTK_SORT_ASCENDING);
1093     g_object_unref(G_OBJECT(store_proto));
1094
1095     gtk_tree_view_set_model(GTK_TREE_VIEW(field_tree), GTK_TREE_MODEL(store));
1096     gtk_tree_view_set_search_column(GTK_TREE_VIEW(field_tree), 1);
1097     gtk_tree_view_set_enable_search(GTK_TREE_VIEW(field_tree), TRUE);
1098     g_object_unref(G_OBJECT(store));
1099
1100     range_label = gtk_label_new("Range (offset:length)");
1101     gtk_misc_set_alignment(GTK_MISC(range_label), 0.0f, 0.0f);
1102     gtk_box_pack_start(GTK_BOX(value_vb), range_label, FALSE, FALSE, 0);
1103
1104     range_entry = gtk_entry_new();
1105     gtk_box_pack_start(GTK_BOX(value_vb), range_entry, FALSE, FALSE, 0);
1106
1107
1108     /* button box */
1109     list_bb = dlg_button_row_new(GTK_STOCK_OK, GTK_STOCK_CANCEL, NULL);
1110     gtk_box_pack_start(GTK_BOX(main_vb), list_bb, FALSE, FALSE, 0);
1111     gtk_container_set_border_width  (GTK_CONTAINER (list_bb), 0);
1112
1113     ok_bt = (GtkWidget *)g_object_get_data(G_OBJECT(list_bb), GTK_STOCK_OK);
1114     gtk_widget_set_sensitive(ok_bt, FALSE);
1115     g_signal_connect(ok_bt, "clicked", G_CALLBACK(dfilter_expr_dlg_accept_cb), filter_te);
1116
1117     cancel_bt = (GtkWidget *)g_object_get_data(G_OBJECT(list_bb), GTK_STOCK_CANCEL);
1118     window_set_cancel_button(window, cancel_bt, NULL);
1119     g_signal_connect(cancel_bt, "clicked", G_CALLBACK(dfilter_expr_dlg_cancel_cb), window);
1120
1121     gtk_widget_grab_default(ok_bt);
1122
1123     /* Catch the "activate" signal on the range and value text entries,
1124        so that if the user types Return there, we act as if the "Accept"
1125        button had been selected, as happens if Return is typed if some
1126        widget that *doesn't* handle the Return key has the input focus. */
1127     dlg_set_activate(range_entry, ok_bt);
1128     dlg_set_activate(value_entry, ok_bt);
1129
1130     g_object_set_data(G_OBJECT(window), E_DFILTER_EXPR_RELATION_LIST_KEY, relation_list);
1131     g_object_set_data(G_OBJECT(window), E_DFILTER_EXPR_RANGE_LABEL_KEY, range_label);
1132     g_object_set_data(G_OBJECT(window), E_DFILTER_EXPR_RANGE_ENTRY_KEY, range_entry);
1133     g_object_set_data(G_OBJECT(window), E_DFILTER_EXPR_VALUE_LABEL_KEY, value_label);
1134     g_object_set_data(G_OBJECT(window), E_DFILTER_EXPR_VALUE_ENTRY_KEY, value_entry);
1135     g_object_set_data(G_OBJECT(window), E_DFILTER_EXPR_VALUE_LIST_KEY, value_list);
1136     g_object_set_data(G_OBJECT(window), E_DFILTER_EXPR_VALUE_LIST_LABEL_KEY, value_list_label);
1137     g_object_set_data(G_OBJECT(window), E_DFILTER_EXPR_VALUE_LIST_SW_KEY,
1138                     value_list_scrolled_win);
1139     g_object_set_data(G_OBJECT(window), E_DFILTER_EXPR_OK_BT_KEY, ok_bt);
1140
1141     g_signal_connect(window, "delete_event", G_CALLBACK(dfilter_expr_dlg_delete_event_cb), window);
1142
1143     /*
1144      * Catch the "destroy" signal on our top-level window, and,
1145      * when it's destroyed, disconnect the signal we'll be
1146      * connecting below.
1147      */
1148     g_signal_connect(window, "destroy", G_CALLBACK(dfilter_expr_dlg_destroy_cb), filter_te);
1149
1150     /*
1151      * Catch the "destroy" signal on the text entry widget to which
1152      * we're attached; if it's destroyed, we should destroy ourselves
1153      * as well.
1154      */
1155     g_signal_connect(filter_te, "destroy", G_CALLBACK(dfilter_expr_dlg_cancel_cb), window);
1156
1157     gtk_widget_show_all(window);
1158     window_present(window);
1159
1160     return window;
1161 }