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