Remove const type qualifier from cast. Resolves CID's 582-585.
[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, (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, (gpointer) values);
401         values++;
402     }
403
404     /*
405      * OK, we're done, so we can finally put it in browse mode.
406      */
407     gtk_tree_selection_set_mode(sel, GTK_SELECTION_BROWSE);
408 }
409
410 static void
411 add_value_list_item(GtkWidget *value_list, const gchar *string, const gpointer data)
412 {
413     GtkListStore *store = GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(value_list)));
414     GtkTreeIter       iter;
415
416     gtk_list_store_append(store, &iter);
417     gtk_list_store_set(store, &iter, 0, string, 1, data, -1);
418 }
419
420 /*
421  * Show or hide the various values fields as appropriate for the field
422  * and currently-selected relation.
423  */
424 static void
425 display_value_fields(header_field_info *hfinfo, gboolean is_comparison,
426                      GtkWidget *value_label, GtkWidget *value_entry,
427                      GtkWidget *value_list_label,
428                      GtkWidget *value_list _U_,
429                      GtkWidget *value_list_scrolled_win, GtkWidget *range_label,
430                      GtkWidget *range_entry)
431 {
432         /* Default values */
433         gboolean show_value_label = FALSE;
434         gboolean show_value_list = FALSE;
435         gboolean show_range = FALSE;
436
437         /*
438          * Either:
439          *
440          *      this is an FT_NONE variable, in which case you can
441          *      only check whether it's present or absent in the
442          *      protocol tree
443          *
444          * or
445          *
446          *      this is a Boolean variable, in which case you
447          *      can't specify a value to compare with, you can
448          *      only specify whether to test for the Boolean
449          *      being true or to test for it being false
450          *
451          * or
452          *
453          *      this isn't a Boolean variable, in which case you
454          *      can test for its presence in the protocol tree,
455          *      and the relation is such a test, in
456          *      which case you don't compare with a value
457          *
458          * so we hide the value entry.
459          */
460
461         switch (hfinfo->type) {
462
463         case FT_BOOLEAN:
464                 if (is_comparison) {
465                         show_value_label = TRUE;  /* XXX: Allow value entry (contrary to the comment above) ?? */
466                         show_value_list  = TRUE;
467                 }
468                 break;
469
470         case FT_UINT8:
471         case FT_UINT16:
472         case FT_UINT24:
473         case FT_UINT32:
474         case FT_INT8:
475         case FT_INT16:
476         case FT_INT24:
477         case FT_INT32:
478                 if (is_comparison) {
479                         show_value_label = TRUE;
480                         if ((hfinfo->strings != NULL) && !(hfinfo->display & BASE_RANGE_STRING)) {
481                         /*
482                          * We have a list of values to show.
483                          */
484                                 show_value_list = TRUE;
485                         }
486                 }
487                 break;
488
489         default:
490                 /*
491                  * There is no list of names for values; only show the value_label if needed.
492                  */
493                 if (is_comparison)
494                         show_value_label = TRUE;
495                 break;
496         }
497
498         gtk_widget_set_sensitive(value_label,               show_value_label);
499         gtk_widget_set_sensitive(value_entry,               show_value_label);
500
501         gtk_widget_set_sensitive(value_list_label,          show_value_list);
502         gtk_widget_set_sensitive(value_list_scrolled_win,   show_value_list);
503
504         /*
505          * Is this a comparison, and are ranges supported by this type?
506          * If both are true, show the range stuff, otherwise hide it.
507          */
508         show_range = (is_comparison && ftype_can_slice(hfinfo->type));
509         gtk_widget_set_sensitive(range_label, show_range);
510         gtk_widget_set_sensitive(range_entry, show_range);
511 }
512
513 static void
514 value_list_sel_cb(GtkTreeSelection *sel, gpointer value_entry_arg)
515 {
516     GtkWidget *value_entry = value_entry_arg;
517     GtkWidget *window = gtk_widget_get_toplevel(GTK_WIDGET(gtk_tree_selection_get_tree_view(sel)));
518     GtkTreeModel *model;
519     GtkTreeIter   iter;
520     header_field_info *hfinfo = g_object_get_data(G_OBJECT(window),
521                                                 E_DFILTER_EXPR_CURRENT_VAR_KEY);
522     const value_string *value = NULL;
523     gchar *value_display_string = NULL;
524
525     if (!gtk_tree_selection_get_selected(sel, &model, &iter))
526         return;
527     gtk_tree_model_get(model, &iter, 1, &value, -1);
528
529     /*
530      * This should either be a numeric type or a Boolean type.
531      */
532     if (hfinfo->type == FT_BOOLEAN) {
533         /*
534          * Boolean type; if the value key for the selected item
535          * is non-null, it's the item for "true", otherwise it's
536          * the item for "false".  Compare with 1 if we're
537          * testing for "true", and compare with 0 if we're
538          * testing for "false".
539          */
540         if (value != NULL)
541                 value_display_string = g_strdup("1");
542         else
543                 value_display_string = g_strdup("0");
544     } else {
545         /*
546          * Numeric type; get the value corresponding to the
547          * selected item, and display it in the base for this
548          * field.
549          */
550         switch ((hfinfo->display) & BASE_DISPLAY_E_MASK) {
551
552         case BASE_NONE:
553         case BASE_DEC:
554             switch (hfinfo->type) {
555
556             case FT_UINT8:
557             case FT_UINT16:
558             case FT_UINT24:
559             case FT_UINT32:
560                 value_display_string = g_strdup_printf("%u", value->value);
561                 break;
562
563             case FT_INT8:
564             case FT_INT16:
565             case FT_INT24:
566             case FT_INT32:
567                 value_display_string = g_strdup_printf("%d", value->value);
568                 break;
569
570             default:
571                 g_assert_not_reached();
572             }
573             break;
574
575         case BASE_HEX:
576             value_display_string = g_strdup_printf("0x%x", value->value);
577             break;
578
579         case BASE_OCT:
580             value_display_string = g_strdup_printf("%#o", value->value);
581             break;
582
583         default:
584             g_assert_not_reached();
585         }
586     }
587
588     gtk_entry_set_text(GTK_ENTRY(value_entry), value_display_string);
589     g_free (value_display_string);
590 }
591
592 static void
593 dfilter_report_bad_value(const char *format, ...)
594 {
595         char error_msg_buf[1024];
596         va_list args;
597
598         va_start(args, format);
599         g_vsnprintf(error_msg_buf, sizeof error_msg_buf, format, args);
600         va_end(args);
601
602         simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "%s", error_msg_buf);
603 }
604
605 static void
606 dfilter_expr_dlg_accept_cb(GtkWidget *w, gpointer filter_te_arg)
607 {
608     GtkWidget *filter_te = filter_te_arg;
609     GtkWidget *window = gtk_widget_get_toplevel(w);
610     GtkWidget *relation_list =
611         g_object_get_data(G_OBJECT(window), E_DFILTER_EXPR_RELATION_LIST_KEY);
612     GtkWidget *range_entry =
613         g_object_get_data(G_OBJECT(window), E_DFILTER_EXPR_RANGE_ENTRY_KEY);
614     GtkWidget *value_entry =
615         g_object_get_data(G_OBJECT(window), E_DFILTER_EXPR_VALUE_ENTRY_KEY);
616     header_field_info *hfinfo;
617     gchar        *item_str;
618     gchar        *range_str, *stripped_range_str;
619     gchar        *value_str, *stripped_value_str;
620     int           pos;
621     gchar        *chars;
622     ftenum_t      ftype;
623     gboolean      can_compare;
624     fvalue_t     *fvalue;
625     GtkTreeModel *model;
626     GtkTreeIter   iter;
627     gboolean      quote_it;
628
629     /*
630      * Get the variable to be tested.
631      */
632     hfinfo = g_object_get_data(G_OBJECT(window), E_DFILTER_EXPR_CURRENT_VAR_KEY);
633
634     /*
635      * Get the relation operator to use.
636      */
637     if (gtk_tree_selection_get_selected(gtk_tree_view_get_selection(GTK_TREE_VIEW(relation_list)),
638                                         &model, &iter)) {
639         gtk_tree_model_get(model, &iter, 0, &item_str, -1);
640     } else {
641         /* Nothing selected */
642         return;
643     }
644
645     /*
646      * Get the range to use, if any.
647      */
648 #if GTK_CHECK_VERSION(2,20,0)
649     if (gtk_widget_get_sensitive(range_entry)) {
650 #else
651     if (GTK_WIDGET_SENSITIVE(range_entry)) {
652 #endif
653         range_str = g_strdup(gtk_entry_get_text(GTK_ENTRY(range_entry)));
654         /*
655          * XXX - strip this even for strings?
656          * Doing so for strings means you can't match a string that has
657          * leading or trailing whitespace, but you can't see trailing
658          * whitespace in a text field, so it's not clear that it's
659          * a good idea to allow that.
660          */
661         stripped_range_str = g_strstrip(range_str);
662         if (strcmp(stripped_range_str, "") == 0) {
663             /*
664              * No range was specified.
665              */
666             g_free(range_str);
667             range_str = NULL;
668             stripped_range_str = NULL;
669         }
670
671         /*
672          * XXX - check it for validity?
673          */
674     } else {
675         range_str = NULL;
676         stripped_range_str = NULL;
677     }
678
679     /*
680      * If a range was specified, the type of the LHS of the
681      * comparison is FT_BYTES; otherwise, it's the type of the field.
682      */
683     if (range_str == NULL)
684         ftype = hfinfo->type;
685     else
686         ftype = FT_BYTES;
687
688     /*
689      * Make sure the relation is valid for the type in question.
690      * We may be offering relations that the type of the field
691      * can't support, because the field's type supports slicing,
692      * and the relation *is* supported on byte strings.
693      */
694     if (strcmp(item_str, "==") == 0)
695         can_compare = ftype_can_eq(ftype);
696     else if (strcmp(item_str, "!=") == 0)
697         can_compare = ftype_can_ne(ftype);
698     else if (strcmp(item_str, ">") == 0)
699         can_compare = ftype_can_gt(ftype);
700     else if (strcmp(item_str, "<") == 0)
701         can_compare = ftype_can_lt(ftype);
702     else if (strcmp(item_str, ">=") == 0)
703         can_compare = ftype_can_ge(ftype);
704     else if (strcmp(item_str, "<=") == 0)
705         can_compare = ftype_can_le(ftype);
706     else if (strcmp(item_str, "contains") == 0)
707         can_compare = ftype_can_contains(ftype);
708     else if (strcmp(item_str, "matches") == 0)
709         can_compare = ftype_can_matches(ftype);
710     else
711         can_compare = TRUE;     /* not a comparison */
712     if (!can_compare) {
713         if (range_str == NULL) {
714             simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
715                           "That field can't be tested with \"%s\".",
716                           item_str);
717         } else {
718             simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
719                           "Ranges of that field can't be tested with \"%s\".",
720                           item_str);
721         }
722         g_free(range_str);
723         g_free(item_str);
724         return;
725     }
726
727     /*
728      * Get the value to use, if any.
729      */
730 #if GTK_CHECK_VERSION(2,20,0)
731     if (gtk_widget_get_sensitive(value_entry)) {
732 #else
733     if (GTK_WIDGET_SENSITIVE(value_entry)) {
734 #endif
735         value_str = g_strdup(gtk_entry_get_text(GTK_ENTRY(value_entry)));
736         stripped_value_str = g_strstrip(value_str);
737         if (strcmp(stripped_value_str, "") == 0) {
738             /*
739              * This field takes a value, but they didn't supply
740              * one.
741              */
742             simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
743                           "That field must be compared with a value, "
744                           "but you didn't specify a value with which to "
745                           "compare it.");
746             g_free(range_str);
747             g_free(value_str);
748             g_free(item_str);
749             return;
750         }
751
752         /*
753          * Make sure the value is valid.
754          *
755          * If no range string was specified, it must be valid
756          * for the type of the field; if a range string was
757          * specified, must be valid for FT_BYTES.
758          */
759         if (strcmp(item_str, "contains") == 0) {
760             fvalue = fvalue_from_unparsed(ftype, stripped_value_str, TRUE,
761                                           dfilter_report_bad_value);
762         }
763         else {
764             fvalue = fvalue_from_unparsed(ftype, stripped_value_str, FALSE,
765                                           dfilter_report_bad_value);
766         }
767         if (fvalue == NULL) {
768             /*
769              * It's not valid.
770              *
771              * The dialog box was already popped up by
772              * "dfilter_report_bad_value()".
773              */
774             g_free(range_str);
775             g_free(value_str);
776             g_free(item_str);
777             return;
778         }
779         FVALUE_FREE(fvalue);
780     } else {
781         value_str = NULL;
782         stripped_value_str = NULL;
783     }
784
785     /*
786      * Insert the expression at the current cursor position.
787      * If there's a non-whitespace character to the left of it,
788      * insert a blank first; if there's a non-whitespace character
789      * to the right of it, insert a blank after it.
790      */
791     pos = gtk_editable_get_position(GTK_EDITABLE(filter_te));
792     chars = gtk_editable_get_chars(GTK_EDITABLE(filter_te), pos, pos + 1);
793     if (strcmp(chars, "") != 0 && !isspace((unsigned char)chars[0]))
794         gtk_editable_insert_text(GTK_EDITABLE(filter_te), " ", 1, &pos);
795     g_free(chars);
796
797     gtk_editable_insert_text(GTK_EDITABLE(filter_te), hfinfo->abbrev,
798                              (gint) strlen(hfinfo->abbrev), &pos);
799     if (range_str != NULL) {
800         gtk_editable_insert_text(GTK_EDITABLE(filter_te), "[", 1, &pos);
801         gtk_editable_insert_text(GTK_EDITABLE(filter_te),
802                                  stripped_range_str, (gint) strlen(stripped_range_str), &pos);
803         gtk_editable_insert_text(GTK_EDITABLE(filter_te), "]", 1, &pos);
804         g_free(range_str);
805     }
806     if (item_str != NULL && !relation_is_presence_test(item_str)) {
807         gtk_editable_insert_text(GTK_EDITABLE(filter_te), " ", 1, &pos);
808         gtk_editable_insert_text(GTK_EDITABLE(filter_te), item_str,
809                                  (gint) strlen(item_str), &pos);
810     }
811     if (value_str != NULL) {
812         gtk_editable_insert_text(GTK_EDITABLE(filter_te), " ", 1, &pos);
813         /*
814          * XXX - we should do this by generating an appropriate display
815          * filter value string for this field; that requires us to have
816          * a "generate display filter string" method for every FT_ type.
817          */
818         switch (hfinfo->type) {
819
820         case FT_STRING:
821         case FT_STRINGZ:
822         case FT_UINT_STRING:
823         case FT_ABSOLUTE_TIME:
824             /*
825              * Always put quotes around the string.
826              */
827             quote_it = TRUE;
828             break;
829
830         default:
831             /*
832              * If the string contains white space, put quotes around it.
833              */
834             quote_it = (strpbrk(stripped_value_str, " \t") != NULL);
835             break;
836         }
837         if (quote_it) {
838             /*
839              * Put quotes around the string.
840              */
841             gtk_editable_insert_text(GTK_EDITABLE(filter_te), "\"",
842                                      1, &pos);
843         }
844         gtk_editable_insert_text(GTK_EDITABLE(filter_te),
845                                  stripped_value_str, (gint) strlen(stripped_value_str), &pos);
846         if (quote_it) {
847             /*
848              * Put quotes around the string.
849              */
850             gtk_editable_insert_text(GTK_EDITABLE(filter_te), "\"",
851                                      1, &pos);
852         }
853         g_free(value_str);
854     }
855     chars = gtk_editable_get_chars(GTK_EDITABLE(filter_te), pos + 1, pos + 2);
856     if (strcmp(chars, "") != 0 && !isspace((unsigned char)chars[0]))
857         gtk_editable_insert_text(GTK_EDITABLE(filter_te), " ", 1, &pos);
858     g_free(chars);
859
860     /*
861      * Put the cursor after the expression we just entered into
862      * the text entry widget.
863      */
864     gtk_editable_set_position(GTK_EDITABLE(filter_te), pos);
865
866     /*
867      * We're done; destroy the dialog box (which is the top-level
868      * widget for the "Accept" button).
869      */
870     window_destroy(window);
871     g_free(item_str);
872 }
873
874 static void
875 dfilter_expr_dlg_cancel_cb(GtkWidget *w _U_, gpointer parent_w)
876 {
877         /*
878          * User pressed the cancel button; close the dialog box.
879          */
880         window_destroy(GTK_WIDGET(parent_w));
881 }
882
883 /* Treat this as a cancel, by calling "prefs_main_cancel_cb()" */
884 static gboolean
885 dfilter_expr_dlg_delete_event_cb(GtkWidget *w _U_, GdkEvent *event _U_,
886                                  gpointer parent_w)
887 {
888         dfilter_expr_dlg_cancel_cb(NULL, parent_w);
889         return FALSE;
890 }
891
892 static void
893 dfilter_expr_dlg_destroy_cb(GtkWidget *w, gpointer filter_te)
894 {
895         /*
896          * The dialog box is being destroyed; disconnect from the
897          * "destroy" signal on the text entry box to which we're
898          * attached, as the handler for that signal is supposed
899          * to destroy us, but we're already gone.
900          */
901         g_signal_handlers_disconnect_by_func(filter_te, dfilter_expr_dlg_cancel_cb, w);
902 }
903
904 /*
905  * Length of string used for protocol fields.
906  */
907 #define TAG_STRING_LEN  256
908
909 GtkWidget *
910 dfilter_expr_dlg_new(GtkWidget *filter_te)
911 {
912     GtkWidget *window, *main_vb, *main_hb;
913
914     GtkWidget *field_vb, *field_tree_lb, *field_tree, *tree_scrolled_win;
915
916     GtkWidget *relation_vb, *relation_label, *relation_list, *relation_list_scrolled_win;
917 /*    GtkWidget *relation_present_rb, *relation_equals_rb, *relation_unequals_rb,
918               *relation_greater_rb, *relation_less_rb,
919               *relation_greaterequal_rb, *relation_lessequal_rb,
920               *relation_contains_rb, *relation_matches_rb;*/
921
922     GtkWidget *value_vb, *value_label, *value_entry;
923     GtkWidget *value_list_label, *value_list_scrolled_win, *value_list;
924     GtkWidget *range_label, *range_entry;
925
926     GtkWidget *list_bb, *ok_bt, *cancel_bt;
927     header_field_info       *hfinfo;
928     int i;
929     protocol_t *protocol;
930     GtkTreeStore *store;
931     GtkTreeSelection *selection;
932     GtkCellRenderer *renderer;
933     GtkTreeViewColumn *column;
934     GtkListStore      *l_store;
935     GtkTreeSelection  *l_sel;
936
937         proto_initialize_all_prefixes();
938
939     window = dlg_conf_window_new("Wireshark: Filter Expression");
940     gtk_window_set_default_size(GTK_WINDOW(window), 500, 400);
941     gtk_container_set_border_width(GTK_CONTAINER(window), 5);
942
943     main_vb = gtk_vbox_new(FALSE, 5);
944     gtk_container_set_border_width(GTK_CONTAINER(main_vb), 5);
945     gtk_container_add(GTK_CONTAINER(window), main_vb);
946
947     main_hb = gtk_hbox_new(FALSE, 5);
948     gtk_container_set_border_width(GTK_CONTAINER(main_hb), 5);
949     gtk_container_add(GTK_CONTAINER(main_vb), main_hb);
950
951     field_vb = gtk_vbox_new(FALSE, 5);
952     gtk_container_set_border_width(GTK_CONTAINER(field_vb), 5);
953     gtk_container_add(GTK_CONTAINER(main_hb), field_vb);
954
955     field_tree_lb = gtk_label_new("Field name");
956     gtk_misc_set_alignment(GTK_MISC(field_tree_lb), 0.0f, 0.0f);
957     gtk_box_pack_start(GTK_BOX(field_vb), field_tree_lb, FALSE, FALSE, 0);
958
959     tree_scrolled_win = scrolled_window_new(NULL, NULL);
960     gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(tree_scrolled_win),
961                                    GTK_SHADOW_IN);
962     gtk_box_pack_start(GTK_BOX(field_vb), tree_scrolled_win, TRUE, TRUE, 0);
963     gtk_widget_set_size_request(tree_scrolled_win, 300, -1);
964
965
966     store = gtk_tree_store_new(2, G_TYPE_STRING, G_TYPE_POINTER);
967     field_tree = tree_view_new(GTK_TREE_MODEL(store));
968     gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(field_tree), FALSE);
969     selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(field_tree));
970     gtk_tree_selection_set_mode(selection, GTK_SELECTION_BROWSE);
971     renderer = gtk_cell_renderer_text_new();
972     column = gtk_tree_view_column_new_with_attributes("Field name", renderer,
973                                                       "text", 0, NULL);
974     gtk_tree_view_append_column(GTK_TREE_VIEW(field_tree), column);
975     gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
976     gtk_tree_view_column_set_sort_column_id(column, 0);
977     g_signal_connect(selection, "changed", G_CALLBACK(field_select_row_cb), field_tree);
978     gtk_container_add(GTK_CONTAINER(tree_scrolled_win), field_tree);
979
980     relation_vb = gtk_vbox_new(FALSE, 5);
981     gtk_container_set_border_width(GTK_CONTAINER(relation_vb), 5);
982     gtk_container_add(GTK_CONTAINER(main_hb), relation_vb);
983
984     relation_label = gtk_label_new("Relation");
985     gtk_misc_set_alignment(GTK_MISC(relation_label), 0.0f, 0.0f);
986     gtk_box_pack_start(GTK_BOX(relation_vb), relation_label, FALSE, FALSE, 0);
987
988     relation_list_scrolled_win = scrolled_window_new(NULL, NULL);
989     /* never use a scrollbar in x direction, show the complete relation string */
990     gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(relation_list_scrolled_win),
991                                    GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
992     gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(relation_list_scrolled_win),
993                                    GTK_SHADOW_IN);
994
995     l_store = gtk_list_store_new(1, G_TYPE_STRING);
996     relation_list = tree_view_new(GTK_TREE_MODEL(l_store));
997     gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(relation_list), FALSE);
998     g_object_unref(G_OBJECT(l_store));
999     renderer = gtk_cell_renderer_text_new();
1000     column = gtk_tree_view_column_new_with_attributes("relation", renderer,
1001                                                       "text", 0, NULL);
1002     gtk_tree_view_append_column(GTK_TREE_VIEW(relation_list), column);
1003     l_sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(relation_list));
1004     gtk_tree_selection_set_mode(l_sel, GTK_SELECTION_BROWSE);
1005     gtk_container_add(GTK_CONTAINER(relation_list_scrolled_win), relation_list);
1006     gtk_box_pack_start(GTK_BOX(relation_vb), relation_list_scrolled_win, TRUE, TRUE, 0);
1007
1008     /*
1009      * OK, show the relation label and range stuff as it would be
1010      * with everything turned on, so it'll request as much space
1011      * as it'll ever need, so the dialog box and widgets start out
1012      * with the right sizes.
1013      *
1014      * XXX - this doesn't work.  It *doesn't* request as much space
1015      * as it'll ever need.
1016      *
1017      * XXX - FT_UINT8 doesn't support ranges, so even if it did work,
1018      * it wouldn't work right.
1019      *
1020      * XXX - this no longer affects the range stuff, as that's
1021      * controlled both by the type and by the relational operator
1022      * selected.
1023      */
1024     show_relations(relation_list, FT_UINT8);
1025
1026     /*
1027     relation_present_rb = gtk_radio_button_new_with_mnemonic_from_widget(NULL, "is present");
1028     gtk_box_pack_start(GTK_BOX(relation_vb), relation_present_rb, FALSE, FALSE, 0);
1029
1030     relation_equals_rb = gtk_radio_button_new_with_mnemonic_from_widget(GTK_RADIO_BUTTON(relation_present_rb), "==");
1031     gtk_box_pack_start(GTK_BOX(relation_vb), relation_equals_rb, FALSE, FALSE, 0);
1032
1033     relation_unequals_rb = gtk_radio_button_new_with_mnemonic_from_widget(GTK_RADIO_BUTTON(relation_present_rb), "!=");
1034     gtk_box_pack_start(GTK_BOX(relation_vb), relation_unequals_rb, FALSE, FALSE, 0);
1035
1036     relation_greater_rb = gtk_radio_button_new_with_mnemonic_from_widget(GTK_RADIO_BUTTON(relation_present_rb), ">");
1037     gtk_box_pack_start(GTK_BOX(relation_vb), relation_greater_rb, FALSE, FALSE, 0);
1038
1039     relation_less_rb = gtk_radio_button_new_with_mnemonic_from_widget(GTK_RADIO_BUTTON(relation_present_rb), "<");
1040     gtk_box_pack_start(GTK_BOX(relation_vb), relation_less_rb, FALSE, FALSE, 0);
1041
1042     relation_greaterequal_rb = gtk_radio_button_new_with_mnemonic_from_widget(GTK_RADIO_BUTTON(relation_present_rb), ">=");
1043     gtk_box_pack_start(GTK_BOX(relation_vb), relation_greaterequal_rb, FALSE, FALSE, 0);
1044
1045     relation_lessequal_rb = gtk_radio_button_new_with_mnemonic_from_widget(GTK_RADIO_BUTTON(relation_present_rb), "<=");
1046     gtk_box_pack_start(GTK_BOX(relation_vb), relation_lessequal_rb, FALSE, FALSE, 0);
1047
1048     relation_contains_rb = gtk_radio_button_new_with_mnemonic_from_widget(GTK_RADIO_BUTTON(relation_present_rb), "contains");
1049     gtk_box_pack_start(GTK_BOX(relation_vb), relation_contains_rb, FALSE, FALSE, 0);
1050
1051     relation_matches_rb = gtk_radio_button_new_with_mnemonic_from_widget(GTK_RADIO_BUTTON(relation_present_rb), "matches");
1052     gtk_box_pack_start(GTK_BOX(relation_vb), relation_matches_rb, FALSE, FALSE, 0);
1053 */
1054     /* value column */
1055     value_vb = gtk_vbox_new(FALSE, 5);
1056     gtk_container_set_border_width(GTK_CONTAINER(value_vb), 5);
1057     gtk_container_add(GTK_CONTAINER(main_hb), value_vb);
1058
1059     value_label = gtk_label_new("Value");
1060     gtk_misc_set_alignment(GTK_MISC(value_label), 0.0f, 0.0f);
1061     gtk_box_pack_start(GTK_BOX(value_vb), value_label, FALSE, FALSE, 0);
1062
1063     value_entry = gtk_entry_new();
1064     gtk_box_pack_start(GTK_BOX(value_vb), value_entry, FALSE, FALSE, 0);
1065
1066     value_list_label = gtk_label_new("Predefined values:");
1067     gtk_misc_set_alignment(GTK_MISC(value_list_label), 0.0f, 0.0f);
1068     gtk_box_pack_start(GTK_BOX(value_vb), value_list_label, FALSE, FALSE, 0);
1069
1070     value_list_scrolled_win = scrolled_window_new(NULL, NULL);
1071     gtk_box_pack_start(GTK_BOX(value_vb), value_list_scrolled_win, TRUE,
1072                        TRUE, 0);
1073
1074     l_store = gtk_list_store_new(2, G_TYPE_STRING, G_TYPE_POINTER);
1075     value_list = tree_view_new(GTK_TREE_MODEL(l_store));
1076     gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(value_list), FALSE);
1077     g_object_unref(G_OBJECT(l_store));
1078     renderer = gtk_cell_renderer_text_new();
1079     column = gtk_tree_view_column_new_with_attributes("value", renderer,
1080                                                       "text", 0, NULL);
1081     gtk_tree_view_append_column(GTK_TREE_VIEW(value_list), column);
1082     g_signal_connect(gtk_tree_view_get_selection(GTK_TREE_VIEW(value_list)),
1083                    "changed", G_CALLBACK(value_list_sel_cb), value_entry);
1084
1085     /*
1086      * The value stuff may be hidden or shown depending on what
1087      * relation was selected; connect to the "changed" signal
1088      * for the relation list, so we can make that happen.
1089      */
1090     g_signal_connect(gtk_tree_view_get_selection(GTK_TREE_VIEW(relation_list)),
1091                    "changed", G_CALLBACK(relation_list_sel_cb), NULL);
1092     l_sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(value_list));
1093     gtk_tree_selection_set_mode(l_sel, GTK_SELECTION_SINGLE);
1094     gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(value_list_scrolled_win),
1095                                           value_list);
1096     /* This remains hidden until an enumerated field is selected */
1097
1098     /*
1099      * Put the items in the Tree; we don't want to do that until
1100      * we've constructed the value list and set the tree's
1101      * E_DFILTER_EXPR_VALUE_LIST_KEY data to point to it, and
1102      * constructed the "Accept" button and set the tree's
1103      * E_DFILTER_EXPR_OK_BT_KEY data to point to it, so that
1104      * when the list item is "helpfully" automatically selected for us
1105      * we're ready to cope with the selection signal.
1106      */
1107
1108 {
1109     /* GTK2 code using two levels iterator to enumerate all protocol fields */
1110
1111     GtkTreeIter iter, child_iter;
1112     void *cookie, *cookie2;
1113
1114     for (i = proto_get_first_protocol(&cookie); i != -1;
1115          i = proto_get_next_protocol(&cookie)) {
1116         char *strp, str[TAG_STRING_LEN+1];
1117
1118         protocol = find_protocol_by_id(i);
1119
1120         if (!proto_is_protocol_enabled(protocol)) {
1121             continue;
1122         }
1123
1124         g_snprintf(str, TAG_STRING_LEN, "%s - %s",
1125                    proto_get_protocol_short_name(protocol),
1126                    proto_get_protocol_long_name(protocol));
1127         strp=str;
1128
1129         hfinfo = proto_registrar_get_nth(i);
1130
1131         gtk_tree_store_append(store, &iter, NULL);
1132         gtk_tree_store_set(store, &iter, 0, strp, 1, hfinfo, -1);
1133
1134         for (hfinfo = proto_get_first_protocol_field(i, &cookie2); hfinfo != NULL;
1135              hfinfo = proto_get_next_protocol_field(&cookie2)) {
1136
1137             if (hfinfo->same_name_prev != NULL) /* ignore duplicate names */
1138                 continue;
1139
1140             if (hfinfo->blurb != NULL && hfinfo->blurb[0] != '\0') {
1141                 g_snprintf(str, TAG_STRING_LEN, "%s - %s (%s)",
1142                            hfinfo->abbrev, hfinfo->name, hfinfo->blurb);
1143             } else {
1144                 g_snprintf(str, TAG_STRING_LEN, "%s - %s", hfinfo->abbrev,
1145                            hfinfo->name);
1146             }
1147             gtk_tree_store_append(store, &child_iter, &iter);
1148             gtk_tree_store_set(store, &child_iter, 0, strp, 1, hfinfo, -1);
1149         }
1150     }
1151     g_object_unref(G_OBJECT(store));
1152 }
1153
1154     range_label = gtk_label_new("Range (offset:length)");
1155     gtk_misc_set_alignment(GTK_MISC(range_label), 0.0f, 0.0f);
1156     gtk_box_pack_start(GTK_BOX(value_vb), range_label, FALSE, FALSE, 0);
1157
1158     range_entry = gtk_entry_new();
1159     gtk_box_pack_start(GTK_BOX(value_vb), range_entry, FALSE, FALSE, 0);
1160
1161
1162     /* button box */
1163     list_bb = dlg_button_row_new(GTK_STOCK_OK, GTK_STOCK_CANCEL, NULL);
1164     gtk_box_pack_start(GTK_BOX(main_vb), list_bb, FALSE, FALSE, 0);
1165     gtk_container_set_border_width  (GTK_CONTAINER (list_bb), 0);
1166
1167     ok_bt = g_object_get_data(G_OBJECT(list_bb), GTK_STOCK_OK);
1168     gtk_widget_set_sensitive(ok_bt, FALSE);
1169     g_signal_connect(ok_bt, "clicked", G_CALLBACK(dfilter_expr_dlg_accept_cb), filter_te);
1170
1171     cancel_bt = g_object_get_data(G_OBJECT(list_bb), GTK_STOCK_CANCEL);
1172     window_set_cancel_button(window, cancel_bt, NULL);
1173     g_signal_connect(cancel_bt, "clicked", G_CALLBACK(dfilter_expr_dlg_cancel_cb), window);
1174
1175     gtk_widget_grab_default(ok_bt);
1176
1177     /* Catch the "activate" signal on the range and value text entries,
1178        so that if the user types Return there, we act as if the "Accept"
1179        button had been selected, as happens if Return is typed if some
1180        widget that *doesn't* handle the Return key has the input focus. */
1181     dlg_set_activate(range_entry, ok_bt);
1182     dlg_set_activate(value_entry, ok_bt);
1183
1184     g_object_set_data(G_OBJECT(window), E_DFILTER_EXPR_RELATION_LIST_KEY, relation_list);
1185     g_object_set_data(G_OBJECT(window), E_DFILTER_EXPR_RANGE_LABEL_KEY, range_label);
1186     g_object_set_data(G_OBJECT(window), E_DFILTER_EXPR_RANGE_ENTRY_KEY, range_entry);
1187     g_object_set_data(G_OBJECT(window), E_DFILTER_EXPR_VALUE_LABEL_KEY, value_label);
1188     g_object_set_data(G_OBJECT(window), E_DFILTER_EXPR_VALUE_ENTRY_KEY, value_entry);
1189     g_object_set_data(G_OBJECT(window), E_DFILTER_EXPR_VALUE_LIST_KEY, value_list);
1190     g_object_set_data(G_OBJECT(window), E_DFILTER_EXPR_VALUE_LIST_LABEL_KEY, value_list_label);
1191     g_object_set_data(G_OBJECT(window), E_DFILTER_EXPR_VALUE_LIST_SW_KEY,
1192                     value_list_scrolled_win);
1193     g_object_set_data(G_OBJECT(window), E_DFILTER_EXPR_OK_BT_KEY, ok_bt);
1194
1195     g_signal_connect(window, "delete_event", G_CALLBACK(dfilter_expr_dlg_delete_event_cb), window);
1196
1197     /*
1198      * Catch the "destroy" signal on our top-level window, and,
1199      * when it's destroyed, disconnect the signal we'll be
1200      * connecting below.
1201      */
1202     g_signal_connect(window, "destroy", G_CALLBACK(dfilter_expr_dlg_destroy_cb), filter_te);
1203
1204     /*
1205      * Catch the "destroy" signal on the text entry widget to which
1206      * we're attached; if it's destroyed, we should destroy ourselves
1207      * as well.
1208      */
1209     g_signal_connect(filter_te, "destroy", G_CALLBACK(dfilter_expr_dlg_cancel_cb), window);
1210
1211     gtk_widget_show_all(window);
1212     window_present(window);
1213
1214     return window;
1215 }