32dd1ec28d8941b0d617bb13dc2974f7f175b70d
[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  * Ethereal - Network traffic analyzer
13  * By Gerald Combs <gerald@ethereal.com>
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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, 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  *      time.  not a good thing.
39  * Sort the protocols and children
40  */
41
42 #ifdef HAVE_CONFIG_H
43 # include "config.h"
44 #endif
45
46 #include <gtk/gtk.h>
47 #include <ctype.h>
48 #include <string.h>
49
50 #include "globals.h"
51 #include "main.h"
52 #include "ui_util.h"
53 #include "simple_dialog.h"
54 #include "dlg_utils.h"
55 #include "proto_dlg.h"
56 #include "filter_dlg.h"
57 #include "dfilter_expr_dlg.h"
58 #include "compat_macros.h"
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, 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, gchar *string,
88                                 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 #if GTK_MAJOR_VERSION < 2
102 static void
103 field_select_row_cb(GtkWidget *tree, GList *node, gint column _U_,
104                     gpointer user_data _U_)
105 #else
106 static void
107 field_select_row_cb(GtkTreeSelection *sel, gpointer tree)
108 #endif
109 {
110     GtkWidget *window = gtk_widget_get_toplevel(tree);
111     GtkWidget *relation_list = OBJECT_GET_DATA(window,
112                                                E_DFILTER_EXPR_RELATION_LIST_KEY);
113     GtkWidget *range_label = OBJECT_GET_DATA(window,
114                                              E_DFILTER_EXPR_RANGE_LABEL_KEY);
115     GtkWidget *range_entry = OBJECT_GET_DATA(window,
116                                              E_DFILTER_EXPR_RANGE_ENTRY_KEY);
117     GtkWidget *value_label = OBJECT_GET_DATA(window,
118                                              E_DFILTER_EXPR_VALUE_LABEL_KEY);
119     GtkWidget *value_entry = OBJECT_GET_DATA(window,
120                                              E_DFILTER_EXPR_VALUE_ENTRY_KEY);
121     GtkWidget *value_list_label = OBJECT_GET_DATA(window,
122                                             E_DFILTER_EXPR_VALUE_LIST_LABEL_KEY);
123     GtkWidget *value_list = OBJECT_GET_DATA(window,
124                                             E_DFILTER_EXPR_VALUE_LIST_KEY);
125     GtkWidget *value_list_scrolled_win = OBJECT_GET_DATA(window,
126                                                          E_DFILTER_EXPR_VALUE_LIST_SW_KEY);
127     GtkWidget *ok_bt = OBJECT_GET_DATA(window,
128                                            E_DFILTER_EXPR_OK_BT_KEY);
129     header_field_info *hfinfo, *cur_hfinfo;
130     const char *value_type;
131     char value_label_string[1024+1];   /* XXX - should be large enough */
132 #if GTK_MAJOR_VERSION >= 2
133     GtkTreeModel *model;
134     GtkTreeIter   iter;
135
136     if (!gtk_tree_selection_get_selected(sel, &model, &iter))
137         return;
138     gtk_tree_model_get(model, &iter, 1, &hfinfo, -1);
139 #else
140
141     hfinfo = gtk_ctree_node_get_row_data(GTK_CTREE(tree),
142                                          GTK_CTREE_NODE(node));
143 #endif
144
145     /*
146      * What was the item that was last selected?
147      */
148     cur_hfinfo = OBJECT_GET_DATA(window, E_DFILTER_EXPR_CURRENT_VAR_KEY);
149     if (cur_hfinfo == hfinfo) {
150         /*
151          * It's still selected; no need to change anything.
152          */
153         return;
154     }
155
156     /*
157      * Mark it as currently selected.
158      */
159     OBJECT_SET_DATA(window, E_DFILTER_EXPR_CURRENT_VAR_KEY, hfinfo);
160
161     show_relations(relation_list, hfinfo->type);
162
163     /*
164      * Set the label for the value to indicate what type of value
165      * it is.
166      */
167     value_type = ftype_pretty_name(hfinfo->type);
168     if (value_type != NULL) {
169         /*
170          * Indicate what type of value it is.
171          */
172         g_snprintf(value_label_string, sizeof value_label_string,
173                  "Value (%s)", value_type);
174         gtk_label_set_text(GTK_LABEL(value_label), value_label_string);
175     }
176
177     /*
178      * Clear the entry widget for the value, as whatever
179      * was there before doesn't apply.
180      */
181     gtk_entry_set_text(GTK_ENTRY(value_entry), "");
182
183     switch (hfinfo->type) {
184
185     case FT_BOOLEAN:
186         /*
187          * The list of values should be the strings for "true"
188          * and "false"; show them in the value list.
189          */
190         build_boolean_values(value_list_scrolled_win, value_list,
191                              hfinfo->strings);
192         break;
193
194     case FT_UINT8:
195     case FT_UINT16:
196     case FT_UINT24:
197     case FT_UINT32:
198     case FT_INT8:
199     case FT_INT16:
200     case FT_INT24:
201     case FT_INT32:
202         /*
203          * If this has a value_string table associated with it,
204          * fill up the list of values, otherwise clear the list
205          * of values.
206          */
207         if (hfinfo->strings != NULL) {
208             build_enum_values(value_list_scrolled_win, value_list,
209                               hfinfo->strings);
210         } else
211 #if GTK_MAJOR_VERSION < 2
212             gtk_list_clear_items(GTK_LIST(value_list), 0, -1);
213 #else
214             gtk_list_store_clear(GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(value_list))));
215 #endif
216         break;
217
218     default:
219         /*
220          * Clear the list of values.
221          */
222 #if GTK_MAJOR_VERSION < 2
223         gtk_list_clear_items(GTK_LIST(value_list), 0, -1);
224 #else
225         gtk_list_store_clear(GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(value_list))));
226 #endif
227         break;
228     }
229
230     /*
231      * Display various items for the value, as appropriate.
232      * The relation we start out with is never a comparison.
233      */
234     display_value_fields(hfinfo, FALSE, value_label, value_entry,
235                          value_list_label, value_list, value_list_scrolled_win, range_label, range_entry);
236
237     /*
238      * XXX - in browse mode, there always has to be something
239      * selected, so this should always be sensitive.
240      */
241     gtk_widget_set_sensitive(ok_bt, TRUE);
242 }
243
244 static void
245 show_relations(GtkWidget *relation_list, ftenum_t ftype)
246 {
247 #if GTK_MAJOR_VERSION >= 2
248         GtkTreeIter iter;
249 #endif
250         /*
251          * Clear out the currently displayed list of relations.
252          */
253 #if GTK_MAJOR_VERSION < 2
254         gtk_list_clear_items(GTK_LIST(relation_list), 0, -1);
255 #else
256         gtk_list_store_clear(GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(relation_list))));
257 #endif
258
259         /*
260          * Add the supported relations.
261          */
262         add_relation_list(relation_list, "is present", TRUE);
263         add_relation_list(relation_list, "==",
264             ftype_can_eq(ftype) || (ftype_can_slice(ftype) && ftype_can_eq(FT_BYTES)));
265         add_relation_list(relation_list, "!=",
266             ftype_can_ne(ftype) || (ftype_can_slice(ftype) && ftype_can_ne(FT_BYTES)));
267         add_relation_list(relation_list, ">",
268             ftype_can_gt(ftype) || (ftype_can_slice(ftype) && ftype_can_gt(FT_BYTES)));
269
270         add_relation_list(relation_list, "<",
271         ftype_can_lt(ftype) || (ftype_can_slice(ftype) && ftype_can_lt(FT_BYTES)));
272         add_relation_list(relation_list, ">=",
273             ftype_can_ge(ftype) || (ftype_can_slice(ftype) && ftype_can_ge(FT_BYTES)));
274         add_relation_list(relation_list, "<=",
275             ftype_can_le(ftype) || (ftype_can_slice(ftype) && ftype_can_le(FT_BYTES)));
276         add_relation_list(relation_list, "contains",
277             ftype_can_contains(ftype) || (ftype_can_slice(ftype) && ftype_can_contains(FT_BYTES)));
278 #ifdef HAVE_LIBPCRE
279         add_relation_list(relation_list, "matches",
280             ftype_can_matches(ftype) || (ftype_can_slice(ftype) && ftype_can_matches(FT_BYTES)));
281 #endif
282
283 #if GTK_MAJOR_VERSION >= 2
284         gtk_tree_model_get_iter_first(gtk_tree_view_get_model(GTK_TREE_VIEW(relation_list)), &iter);
285         gtk_tree_selection_select_iter(gtk_tree_view_get_selection(GTK_TREE_VIEW(relation_list)), &iter);
286 #endif
287 }
288
289 /*
290  * Given a string that represents a test to be made on a field, returns
291  * TRUE if it tests for the field's presence, FALSE otherwise.
292  */
293 static gboolean
294 relation_is_presence_test(const char *string)
295 {
296         return (strcmp(string, "is present") == 0);
297 }
298
299 static void
300 add_relation_list(GtkWidget *relation_list, char *relation, gboolean sensitive)
301 {
302 #if GTK_MAJOR_VERSION < 2
303     GtkWidget *label, *item;
304
305     label = gtk_label_new(relation);
306     item = gtk_list_item_new();
307
308     gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
309     gtk_container_add(GTK_CONTAINER(item), label);
310     gtk_widget_show(label);
311     gtk_container_add(GTK_CONTAINER(relation_list), item);
312     gtk_widget_show(item);
313     gtk_widget_set_sensitive(item, sensitive);
314 #else
315     GtkListStore *store = GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(relation_list)));
316     GtkTreeIter   iter;
317
318     /* XXX: I currently see no way to insensitive the item,
319      * so for a first step, just don't show it (as before these changes :-) */
320     if (!sensitive) {
321         return;
322     }
323
324     gtk_list_store_append(store, &iter);
325     gtk_list_store_set(store, &iter, 0, relation, -1);
326 #endif
327 }
328
329 #if GTK_MAJOR_VERSION < 2
330 static void
331 relation_list_sel_cb(GtkList *relation_list, GtkWidget *child _U_,
332                      gpointer user_data _U_)
333 #else
334 static void
335 relation_list_sel_cb(GtkTreeSelection *sel, gpointer user_data _U_)
336 #endif
337 {
338 #if GTK_MAJOR_VERSION < 2
339     GtkWidget *window = gtk_widget_get_toplevel(GTK_WIDGET(relation_list));
340 #else
341     GtkWidget *window = gtk_widget_get_toplevel(GTK_WIDGET(gtk_tree_selection_get_tree_view(sel)));
342 #endif
343     GtkWidget *range_label =
344         OBJECT_GET_DATA(window, E_DFILTER_EXPR_RANGE_LABEL_KEY);
345     GtkWidget *range_entry =
346         OBJECT_GET_DATA(window, E_DFILTER_EXPR_RANGE_ENTRY_KEY);
347     GtkWidget *value_label =
348         OBJECT_GET_DATA(window, E_DFILTER_EXPR_VALUE_LABEL_KEY);
349     GtkWidget *value_entry =
350         OBJECT_GET_DATA(window, E_DFILTER_EXPR_VALUE_ENTRY_KEY);
351     GtkWidget *value_list_label =
352         OBJECT_GET_DATA(window, E_DFILTER_EXPR_VALUE_LIST_LABEL_KEY);
353     GtkWidget *value_list =
354         OBJECT_GET_DATA(window, E_DFILTER_EXPR_VALUE_LIST_KEY);
355     GtkWidget *value_list_scrolled_win =
356         OBJECT_GET_DATA(window, E_DFILTER_EXPR_VALUE_LIST_SW_KEY);
357     header_field_info *hfinfo =
358         OBJECT_GET_DATA(window, E_DFILTER_EXPR_CURRENT_VAR_KEY);
359     gchar *item_str;
360 #if GTK_MAJOR_VERSION < 2
361     GList *sl;
362     GtkWidget *item, *item_label;
363
364     /*
365      * What's the relation?
366      */
367     sl = GTK_LIST(relation_list)->selection;
368     item = GTK_WIDGET(sl->data);
369     item_label = GTK_BIN(item)->child;
370     gtk_label_get(GTK_LABEL(item_label), &item_str);
371 #else
372     GtkTreeModel *model;
373     GtkTreeIter   iter;
374
375     /*
376      * What's the relation?
377      */
378     if (!gtk_tree_selection_get_selected(sel, &model, &iter))
379         return;
380     gtk_tree_model_get(model, &iter, 0, &item_str, -1);
381 #endif
382
383     /*
384      * Update the display of various items for the value, as appropriate.
385      */
386     display_value_fields(hfinfo,
387                          !relation_is_presence_test(item_str),
388                          value_label, value_entry, value_list_label, value_list,
389                          value_list_scrolled_win, range_label, range_entry);
390 #if GTK_MAJOR_VERSION >= 2
391     g_free(item_str);
392 #endif
393 }
394
395 static void
396 build_boolean_values(GtkWidget *value_list_scrolled_win, GtkWidget *value_list,
397                      const true_false_string *values)
398 {
399     static const true_false_string true_false = { "True", "False" };
400 #if GTK_MAJOR_VERSION >= 2
401     GtkTreeSelection *sel;
402     GtkTreeIter       iter;
403
404     sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(value_list));
405 #endif
406
407     /*
408      * Clear out the items for the list, and put in the names
409      * from the value_string list.
410      */
411 #if GTK_MAJOR_VERSION < 2
412     gtk_list_clear_items(GTK_LIST(value_list), 0, -1);
413 #else
414     gtk_list_store_clear(GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(value_list))));
415 #endif
416
417     /*
418      * Put the list in single mode, so we don't get any selection
419      * events while we're building it (i.e., so we don't get any
420      * on a list item BEFORE WE GET TO SET THE DATA FOR THE LIST
421      * ITEM SO THAT THE HANDLER CAN HANDLE IT).
422      */
423 #if GTK_MAJOR_VERSION < 2
424     gtk_list_set_selection_mode(GTK_LIST(value_list), GTK_SELECTION_SINGLE);
425 #else
426     gtk_tree_selection_set_mode(sel, GTK_SELECTION_SINGLE);
427 #endif
428
429     /*
430      * Build the list.
431      */
432     if (values == NULL)
433         values = &true_false;
434     add_value_list_item(value_list, values->true_string, (gpointer)values);
435     add_value_list_item(value_list, values->false_string, NULL);
436
437     /*
438      * OK, we're done, so we can finally put it in browse mode.
439      * Select the first item, so that the user doesn't have to, under
440      * the assumption that they're most likely to test if something
441      * is true, not false.
442      */
443 #if GTK_MAJOR_VERSION < 2
444     gtk_list_set_selection_mode(GTK_LIST(value_list), GTK_SELECTION_BROWSE);
445     gtk_list_select_item(GTK_LIST(value_list), 0);
446 #else
447     gtk_tree_selection_set_mode(sel, GTK_SELECTION_BROWSE);
448     gtk_tree_model_get_iter_first(gtk_tree_view_get_model(GTK_TREE_VIEW(value_list)), &iter);
449     gtk_tree_selection_select_iter(sel, &iter);
450 #endif
451
452     gtk_widget_show_all(value_list_scrolled_win);
453 }
454
455 static void
456 build_enum_values(GtkWidget *value_list_scrolled_win _U_, GtkWidget *value_list,
457                   const value_string *values)
458 {
459 #if GTK_MAJOR_VERSION >= 2
460     GtkTreeSelection *sel;
461
462     sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(value_list));
463 #endif
464     /*
465      * Clear out the items for the list, and put in the names
466      * from the value_string list.
467      */
468 #if GTK_MAJOR_VERSION < 2
469     gtk_list_clear_items(GTK_LIST(value_list), 0, -1);
470 #else
471     gtk_list_store_clear(GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(value_list))));
472 #endif
473
474     /*
475      * Put the list in single mode, so we don't get any selection
476      * events while we're building it (i.e., so we don't get any
477      * on a list item BEFORE WE GET TO SET THE DATA FOR THE LIST
478      * ITEM SO THAT THE HANDLER CAN HANDLE IT).
479      */
480 #if GTK_MAJOR_VERSION < 2
481     gtk_list_set_selection_mode(GTK_LIST(value_list), GTK_SELECTION_SINGLE);
482 #else
483     gtk_tree_selection_set_mode(sel, GTK_SELECTION_SINGLE);
484 #endif
485
486     /*
487      * Build the list.
488      */
489     while (values->strptr != NULL) {
490         add_value_list_item(value_list, values->strptr,
491                             (gpointer)values);
492         values++;
493     }
494
495     /*
496      * OK, we're done, so we can finally put it in browse mode.
497      */
498 #if GTK_MAJOR_VERSION < 2
499     gtk_list_set_selection_mode(GTK_LIST(value_list), GTK_SELECTION_BROWSE);
500 #else
501     gtk_tree_selection_set_mode(sel, GTK_SELECTION_BROWSE);
502 #endif
503 }
504
505 static void
506 add_value_list_item(GtkWidget *value_list, gchar *string, gpointer data)
507 {
508 #if GTK_MAJOR_VERSION < 2
509     GtkWidget *label, *item;
510
511     label = gtk_label_new(string);
512     item = gtk_list_item_new();
513
514     gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
515     gtk_container_add(GTK_CONTAINER(item), label);
516     gtk_widget_show(label);
517     gtk_container_add(GTK_CONTAINER(value_list), item);
518     OBJECT_SET_DATA(item, E_DFILTER_EXPR_VALUE_KEY, data);
519     gtk_widget_show(item);
520 #else
521     GtkListStore *store = GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(value_list)));
522     GtkTreeIter       iter;
523
524     gtk_list_store_append(store, &iter);
525     gtk_list_store_set(store, &iter, 0, string, 1, data, -1);
526 #endif
527 }
528
529 /*
530  * Show or hide the various values fields as appropriate for the field
531  * and currently-selected relation.
532  */
533 static void
534 display_value_fields(header_field_info *hfinfo, gboolean is_comparison,
535                      GtkWidget *value_label, GtkWidget *value_entry,
536                      GtkWidget *value_list_label,
537                      GtkWidget  *value_list _U_,
538                      GtkWidget *value_list_scrolled_win, GtkWidget *range_label,
539                      GtkWidget *range_entry)
540 {
541         gboolean show_value_label = FALSE;
542         gboolean show_value_list = FALSE;
543         gboolean show_range = FALSE;
544
545         /*
546          * Either:
547          *
548          *      this is an FT_NONE variable, in which case you can
549          *      only check whether it's present or absent in the
550          *      protocol tree
551          *
552          * or
553          *
554          *      this is a Boolean variable, in which case you
555          *      can't specify a value to compare with, you can
556          *      only specify whether to test for the Boolean
557          *      being true or to test for it being false
558          *
559          * or
560          *
561          *      this isn't a Boolean variable, in which case you
562          *      can test for its presence in the protocol tree,
563          *      and the default relation is such a test, in
564          *      which case you don't compare with a value
565          *
566          * so we hide the value entry.
567          */
568     show_value_list = is_comparison;
569         if (is_comparison) {
570                 /*
571                  * If we're showing the entry; show the label as well.
572                  */
573                 show_value_label = TRUE;
574     }
575
576         switch (hfinfo->type) {
577
578         case FT_BOOLEAN:
579         show_value_list = is_comparison;
580                 if (is_comparison) {
581                         /*
582                          * If we're showing the value list; show the label as well.
583                          */
584                         show_value_label = TRUE;
585                 }
586                 break;
587
588         case FT_UINT8:
589         case FT_UINT16:
590         case FT_UINT24:
591         case FT_UINT32:
592         case FT_INT8:
593         case FT_INT16:
594         case FT_INT24:
595         case FT_INT32:
596                 if (hfinfo->strings != NULL) {
597                         /*
598                          * We have a list of values to show.
599                          */
600
601             show_value_list = is_comparison;
602                         if (is_comparison) {
603                                 /*
604                                  * We're showing the entry; show the label
605                                  * as well.
606                                  */
607                                 show_value_label = TRUE;
608                         }
609                 } else {
610                         /*
611                          * There is no list of names for values, so don't
612                          * show it.
613                          */
614             show_value_list = FALSE;
615                 }
616                 break;
617
618         default:
619                 /*
620                  * There is no list of names for values; hide the list.
621                  */
622         show_value_list = FALSE;
623                 break;
624         }
625
626     gtk_widget_set_sensitive(value_label,               show_value_label);
627     gtk_widget_set_sensitive(value_entry,               show_value_label);
628
629     gtk_widget_set_sensitive(value_list_label,          show_value_list);
630     gtk_widget_set_sensitive(value_list_scrolled_win,   show_value_list);
631
632         /*
633          * Is this a comparison, and are ranges supported by this type?
634          * If both are true, show the range stuff, otherwise hide it.
635          */
636     show_range = (is_comparison && ftype_can_slice(hfinfo->type));
637     gtk_widget_set_sensitive(range_label, show_range);
638     gtk_widget_set_sensitive(range_entry, show_range);
639 }
640
641 #if GTK_MAJOR_VERSION < 2
642 static void
643 value_list_sel_cb(GtkList *value_list, GtkWidget *child,
644                   gpointer value_entry_arg)
645 #else
646 static void
647 value_list_sel_cb(GtkTreeSelection *sel, gpointer value_entry_arg)
648 #endif
649 {
650     GtkWidget *value_entry = value_entry_arg;
651 #if GTK_MAJOR_VERSION < 2
652     GtkWidget *window = gtk_widget_get_toplevel(GTK_WIDGET(value_list));
653 #else
654     GtkWidget *window = gtk_widget_get_toplevel(GTK_WIDGET(gtk_tree_selection_get_tree_view(sel)));
655     GtkTreeModel *model;
656     GtkTreeIter   iter;
657 #endif
658     header_field_info *hfinfo = OBJECT_GET_DATA(window,
659                                                 E_DFILTER_EXPR_CURRENT_VAR_KEY);
660     const value_string *value = NULL;
661     char value_string[11+1];    /* long enough for 32-bit octal value */
662
663 #if GTK_MAJOR_VERSION < 2
664     value = OBJECT_GET_DATA(child, E_DFILTER_EXPR_VALUE_KEY);
665 #else
666     if (!gtk_tree_selection_get_selected(sel, &model, &iter))
667         return;
668     gtk_tree_model_get(model, &iter, 1, &value, -1);
669 #endif
670
671     /*
672      * This should either be a numeric type or a Boolean type.
673      */
674     if (hfinfo->type == FT_BOOLEAN) {
675         /*
676          * Boolean type; if the value key for the selected item
677          * is non-null, it's the item for "true", otherwise it's
678          * the item for "false".  Compare with 1 if we're
679          * testing for "true", and compare with 0 if we're
680          * testing for "false".
681          */
682         if (value != NULL)
683             strcpy(value_string, "1");
684         else
685             strcpy(value_string, "0");
686     } else {
687         /*
688          * Numeric type; get the value corresponding to the
689          * selected item, and display it in the base for this
690          * field.
691          */
692         switch (hfinfo->display) {
693
694         case BASE_DEC:
695             switch (hfinfo->type) {
696
697             case FT_UINT8:
698             case FT_UINT16:
699             case FT_UINT32:
700                 g_snprintf(value_string, sizeof value_string,
701                          "%u", value->value);
702                 break;
703
704             case FT_INT8:
705             case FT_INT16:
706             case FT_INT32:
707                 g_snprintf(value_string, sizeof value_string,
708                          "%d", value->value);
709                 break;
710
711             default:
712                 g_assert_not_reached();
713             }
714             break;
715
716         case BASE_HEX:
717             g_snprintf(value_string, sizeof value_string, "0x%x",
718                      value->value);
719             break;
720
721         case BASE_OCT:
722             g_snprintf(value_string, sizeof value_string, "%#o",
723                      value->value);
724             break;
725
726         default:
727             g_assert_not_reached();
728         }
729     }
730     gtk_entry_set_text(GTK_ENTRY(value_entry), value_string);
731 }
732
733 static void
734 dfilter_report_bad_value(char *format, ...)
735 {
736         char error_msg_buf[1024];
737         va_list args;
738
739         va_start(args, format);
740         g_vsnprintf(error_msg_buf, sizeof error_msg_buf, format, args);
741         va_end(args);
742
743         simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "%s", error_msg_buf);
744 }
745
746 static void
747 dfilter_expr_dlg_accept_cb(GtkWidget *w, gpointer filter_te_arg)
748 {
749     GtkWidget *filter_te = filter_te_arg;
750     GtkWidget *window = gtk_widget_get_toplevel(w);
751     GtkWidget *relation_list =
752         OBJECT_GET_DATA(window, E_DFILTER_EXPR_RELATION_LIST_KEY);
753     GtkWidget *range_entry =
754         OBJECT_GET_DATA(window, E_DFILTER_EXPR_RANGE_ENTRY_KEY);
755     GtkWidget *value_entry =
756         OBJECT_GET_DATA(window, E_DFILTER_EXPR_VALUE_ENTRY_KEY);
757     header_field_info *hfinfo;
758     gchar        *item_str;
759     gchar        *range_str, *stripped_range_str;
760     gchar        *value_str, *stripped_value_str;
761     int           pos;
762     gchar        *chars;
763     ftenum_t      ftype;
764     gboolean      can_compare;
765     fvalue_t     *fvalue;
766 #if GTK_MAJOR_VERSION < 2
767     GList        *sl;
768     GtkWidget    *item, *item_label;
769 #else
770     GtkTreeModel *model;
771     GtkTreeIter   iter;
772 #endif
773     gboolean      quote_it;
774
775     /*
776      * Get the variable to be tested.
777      */
778     hfinfo = OBJECT_GET_DATA(window, E_DFILTER_EXPR_CURRENT_VAR_KEY);
779
780     /*
781      * Get the relation operator to use.
782      */
783 #if GTK_MAJOR_VERSION < 2
784     sl = GTK_LIST(relation_list)->selection;
785     item = GTK_WIDGET(sl->data);
786     item_label = GTK_BIN(item)->child;
787     gtk_label_get(GTK_LABEL(item_label), &item_str);
788 #else
789     if (gtk_tree_selection_get_selected(gtk_tree_view_get_selection(GTK_TREE_VIEW(relation_list)),
790                                         &model, &iter))
791         gtk_tree_model_get(model, &iter, 0, &item_str, -1);
792     else {
793         /* XXX - the relation list is in GTK_SELECTION_BROWSE mode; how
794            can this ever be null? */
795         item_str = NULL;
796     }
797 #endif
798
799     /*
800      * Get the range to use, if any.
801      */
802     if (GTK_WIDGET_SENSITIVE(range_entry)) {
803         range_str = g_strdup(gtk_entry_get_text(GTK_ENTRY(range_entry)));
804         /*
805          * XXX - strip this even for strings?
806          * Doing so for strings mean you can't match a string that has
807          * leading or trailing whitespace, but you can't see trailing
808          * whitespace in a text field, so it's not clear that it's
809          * a good idea to allow that.
810          */
811         stripped_range_str = g_strstrip(range_str);
812         if (strcmp(stripped_range_str, "") == 0) {
813             /*
814              * No range was specified.
815              */
816             g_free(range_str);
817             range_str = NULL;
818             stripped_range_str = NULL;
819         }
820
821         /*
822          * XXX - check it for validity?
823          */
824     } else {
825         range_str = NULL;
826         stripped_range_str = NULL;
827     }
828
829     /*
830      * If a range was specified, the type of the LHS of the
831      * comparison is FT_BYTES; otherwise, it's the type of the field.
832      */
833     if (range_str == NULL)
834         ftype = hfinfo->type;
835     else
836         ftype = FT_BYTES;
837
838     /*
839      * Make sure the relation is valid for the type in question.
840      * We may be offering relations that the type of the field
841      * can't support, because the field's type supports slicing,
842      * and the relation *is* supported on byte strings.
843      */
844     if (strcmp(item_str, "==") == 0)
845         can_compare = ftype_can_eq(ftype);
846     else if (strcmp(item_str, "!=") == 0)
847         can_compare = ftype_can_ne(ftype);
848     else if (strcmp(item_str, ">") == 0)
849         can_compare = ftype_can_gt(ftype);
850     else if (strcmp(item_str, "<") == 0)
851         can_compare = ftype_can_lt(ftype);
852     else if (strcmp(item_str, ">=") == 0)
853         can_compare = ftype_can_ge(ftype);
854     else if (strcmp(item_str, "<=") == 0)
855         can_compare = ftype_can_le(ftype);
856     else if (strcmp(item_str, "contains") == 0)
857         can_compare = ftype_can_contains(ftype);
858     else if (strcmp(item_str, "matches") == 0)
859         can_compare = ftype_can_matches(ftype);
860     else
861         can_compare = TRUE;     /* not a comparison */
862     if (!can_compare) {
863         if (range_str == NULL) {
864             simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
865                           "That field cannot be tested with \"%s\".",
866                           item_str);
867         } else {
868             simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
869                           "Ranges of that field cannot be tested with \"%s\".",
870                           item_str);
871         }
872         if (range_str != NULL)
873             g_free(range_str);
874         return;
875     }
876
877     /*
878      * Get the value to use, if any.
879      */
880     if (GTK_WIDGET_SENSITIVE(value_entry)) {
881         value_str = g_strdup(gtk_entry_get_text(GTK_ENTRY(value_entry)));
882         stripped_value_str = g_strstrip(value_str);
883         if (strcmp(stripped_value_str, "") == 0) {
884             /*
885              * This field takes a value, but they didn't supply
886              * one.
887              */
888             simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
889                           "That field must be compared with a value, "
890                           "but you didn't specify a value with which to "
891                           "compare it.");
892             if (range_str != NULL)
893                 g_free(range_str);
894             g_free(value_str);
895             return;
896         }
897
898         /*
899          * Make sure the value is valid.
900          *
901          * If no range string was specified, it must be valid
902          * for the type of the field; if a range string was
903          * specified, must be valid for FT_BYTES.
904          */
905         if (strcmp(item_str, "contains") == 0) {
906                 fvalue = fvalue_from_unparsed(ftype, stripped_value_str, TRUE,
907                                             dfilter_report_bad_value);
908         }
909         else {
910                 fvalue = fvalue_from_unparsed(ftype, stripped_value_str, FALSE,
911                                             dfilter_report_bad_value);
912         }
913         if (fvalue == NULL) {
914             /*
915              * It's not valid.
916              *
917              * The dialog box was already popped up by
918              * "dfilter_report_bad_value()".
919              */
920             if (range_str != NULL)
921                 g_free(range_str);
922             g_free(value_str);
923             return;
924         }
925         FVALUE_FREE(fvalue);
926     } else {
927         value_str = NULL;
928         stripped_value_str = NULL;
929     }
930
931     /*
932      * Insert the expression at the current cursor position.
933      * If there's a non-whitespace character to the left of it,
934      * insert a blank first; if there's a non-whitespace character
935      * to the right of it, insert a blank after it.
936      */
937     pos = gtk_editable_get_position(GTK_EDITABLE(filter_te));
938     chars = gtk_editable_get_chars(GTK_EDITABLE(filter_te), pos, pos + 1);
939     if (strcmp(chars, "") != 0 && !isspace((unsigned char)chars[0]))
940         gtk_editable_insert_text(GTK_EDITABLE(filter_te), " ", 1, &pos);
941     g_free(chars);
942
943     gtk_editable_insert_text(GTK_EDITABLE(filter_te), hfinfo->abbrev,
944                              strlen(hfinfo->abbrev), &pos);
945     if (range_str != NULL) {
946         gtk_editable_insert_text(GTK_EDITABLE(filter_te), "[", 1, &pos);
947         gtk_editable_insert_text(GTK_EDITABLE(filter_te),
948                                  stripped_range_str, strlen(stripped_range_str), &pos);
949         gtk_editable_insert_text(GTK_EDITABLE(filter_te), "]", 1, &pos);
950         g_free(range_str);
951     }
952     if (item_str != NULL && !relation_is_presence_test(item_str)) {
953         gtk_editable_insert_text(GTK_EDITABLE(filter_te), " ", 1, &pos);
954         gtk_editable_insert_text(GTK_EDITABLE(filter_te), item_str,
955                                  strlen(item_str), &pos);
956     }
957     if (value_str != NULL) {
958         gtk_editable_insert_text(GTK_EDITABLE(filter_te), " ", 1, &pos);
959         /*
960          * XXX - we should do this by generating an appropriate display
961          * filter value string for this field; that requires us to have
962          * a "generate display filter string" method for every FT_ type.
963          */
964         switch (hfinfo->type) {
965
966         case FT_STRING:
967         case FT_STRINGZ:
968         case FT_UINT_STRING:
969         case FT_ABSOLUTE_TIME:
970             /*
971              * Always put quotes around the string.
972              */
973             quote_it = TRUE;
974             break;
975
976         default:
977             /*
978              * If the string contains white space, put quotes around it.
979              */
980             quote_it = (strpbrk(stripped_value_str, " \t") != NULL);
981             break;
982         }
983         if (quote_it) {
984             /*
985              * Put quotes around the string.
986              */
987             gtk_editable_insert_text(GTK_EDITABLE(filter_te), "\"",
988                                      1, &pos);
989         }
990         gtk_editable_insert_text(GTK_EDITABLE(filter_te),
991                                  stripped_value_str, strlen(stripped_value_str), &pos);
992         if (quote_it) {
993             /*
994              * Put quotes around the string.
995              */
996             gtk_editable_insert_text(GTK_EDITABLE(filter_te), "\"",
997                                      1, &pos);
998         }
999         g_free(value_str);
1000     }
1001     chars = gtk_editable_get_chars(GTK_EDITABLE(filter_te), pos + 1, pos + 2);
1002     if (strcmp(chars, "") != 0 && !isspace((unsigned char)chars[0]))
1003         gtk_editable_insert_text(GTK_EDITABLE(filter_te), " ", 1, &pos);
1004     g_free(chars);
1005
1006     /*
1007      * Put the cursor after the expression we just entered into
1008      * the text entry widget.
1009      */
1010     gtk_editable_set_position(GTK_EDITABLE(filter_te), pos);
1011
1012     /*
1013      * We're done; destroy the dialog box (which is the top-level
1014      * widget for the "Accept" button).
1015      */
1016     window_destroy(window);
1017 #if GTK_MAJOR_VERSION >= 2
1018     g_free(item_str);
1019 #endif
1020 }
1021
1022 static void
1023 dfilter_expr_dlg_cancel_cb(GtkWidget *w _U_, gpointer parent_w)
1024 {
1025         /*
1026          * User pressed the cancel button; close the dialog box.
1027          */
1028         window_destroy(GTK_WIDGET(parent_w));
1029 }
1030
1031 /* Treat this as a cancel, by calling "prefs_main_cancel_cb()" */
1032 static gboolean
1033 dfilter_expr_dlg_delete_event_cb(GtkWidget *w _U_, GdkEvent *event _U_,
1034                                  gpointer parent_w)
1035 {
1036         dfilter_expr_dlg_cancel_cb(NULL, parent_w);
1037         return FALSE;
1038 }
1039
1040 static void
1041 dfilter_expr_dlg_destroy_cb(GtkWidget *w, gpointer filter_te)
1042 {
1043         /*
1044          * The dialog box is being destroyed; disconnect from the
1045          * "destroy" signal on the text entry box to which we're
1046          * attached, as the handler for that signal is supposed
1047          * to destroy us, but we're already gone.
1048          */
1049         SIGNAL_DISCONNECT_BY_FUNC(filter_te, dfilter_expr_dlg_cancel_cb, w);
1050 }
1051
1052 /*
1053  * Length of string used for protocol fields.
1054  */
1055 #define TAG_STRING_LEN  256
1056
1057 void
1058 dfilter_expr_dlg_new(GtkWidget *filter_te)
1059 {
1060     GtkWidget *window, *main_vb, *main_hb;
1061
1062     GtkWidget *field_vb, *field_tree_lb, *field_tree, *tree_scrolled_win;
1063
1064     GtkWidget *relation_vb, *relation_label, *relation_list, *relation_list_scrolled_win;
1065 /*    GtkWidget *relation_present_rb, *relation_equals_rb, *relation_unequals_rb,
1066               *relation_greater_rb, *relation_less_rb,
1067               *relation_greaterequal_rb, *relation_lessequal_rb,
1068               *relation_contains_rb, *relation_matches_rb;*/
1069
1070     GtkWidget *value_vb, *value_label, *value_entry;
1071     GtkWidget *value_list_label, *value_list_scrolled_win, *value_list;
1072     GtkWidget *range_label, *range_entry;
1073
1074     GtkWidget *list_bb, *ok_bt, *cancel_bt;
1075     header_field_info       *hfinfo;
1076     int i;
1077     protocol_t *protocol;
1078 #if GTK_MAJOR_VERSION < 2
1079     int len;
1080     void *cookie;
1081     gchar *name;
1082     GHashTable *proto_array;
1083     GtkCTreeNode *protocol_node, *item_node;
1084 #else
1085     GtkTreeStore *store;
1086     GtkTreeSelection *selection;
1087     GtkCellRenderer *renderer;
1088     GtkTreeViewColumn *column;
1089     GtkListStore      *l_store;
1090     GtkTreeSelection  *l_sel;
1091 #endif
1092
1093     window = dlg_window_new("Ethereal: Filter Expression");
1094         gtk_window_set_default_size(GTK_WINDOW(window), 500, 400);
1095     gtk_container_set_border_width(GTK_CONTAINER(window), 5);
1096
1097     main_vb = gtk_vbox_new(FALSE, 5);
1098     gtk_container_border_width(GTK_CONTAINER(main_vb), 5);
1099     gtk_container_add(GTK_CONTAINER(window), main_vb);
1100
1101     main_hb = gtk_hbox_new(FALSE, 5);
1102     gtk_container_border_width(GTK_CONTAINER(main_hb), 5);
1103     gtk_container_add(GTK_CONTAINER(main_vb), main_hb);
1104
1105     field_vb = gtk_vbox_new(FALSE, 5);
1106     gtk_container_border_width(GTK_CONTAINER(field_vb), 5);
1107     gtk_container_add(GTK_CONTAINER(main_hb), field_vb);
1108
1109     field_tree_lb = gtk_label_new("Field name");
1110     gtk_misc_set_alignment(GTK_MISC(field_tree_lb), 0.0, 0.0);
1111     gtk_box_pack_start(GTK_BOX(field_vb), field_tree_lb, FALSE, FALSE, 0);
1112
1113     tree_scrolled_win = scrolled_window_new(NULL, NULL);
1114 #if GTK_MAJOR_VERSION >= 2
1115     gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(tree_scrolled_win), 
1116                                    GTK_SHADOW_IN);
1117 #endif
1118     gtk_box_pack_start(GTK_BOX(field_vb), tree_scrolled_win, TRUE, TRUE, 0);
1119         WIDGET_SET_SIZE(tree_scrolled_win, 300, -1);
1120
1121
1122 #if GTK_MAJOR_VERSION < 2
1123     field_tree = ctree_new(1, 0);
1124     SIGNAL_CONNECT(field_tree, "tree-select-row", field_select_row_cb, field_tree);
1125 #else
1126     store = gtk_tree_store_new(2, G_TYPE_STRING, G_TYPE_POINTER);
1127     field_tree = tree_view_new(GTK_TREE_MODEL(store));
1128     gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(field_tree), FALSE);
1129     selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(field_tree));
1130     gtk_tree_selection_set_mode(selection, GTK_SELECTION_BROWSE);
1131     renderer = gtk_cell_renderer_text_new();
1132     column = gtk_tree_view_column_new_with_attributes("Field name", renderer,
1133                                                       "text", 0, NULL);
1134     gtk_tree_view_append_column(GTK_TREE_VIEW(field_tree), column);
1135     gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
1136     gtk_tree_view_column_set_sort_column_id(column, 0);
1137     SIGNAL_CONNECT(selection, "changed", field_select_row_cb, field_tree);
1138 #endif
1139     gtk_container_add(GTK_CONTAINER(tree_scrolled_win), field_tree);
1140
1141 #if GTK_MAJOR_VERSION < 2
1142     /*
1143      * GTK's annoying CTree widget will deliver a selection event
1144      * the instant you add an item to the field_tree, *the fact that you
1145      * haven't even had time to set the item's row data nonwithstanding*.
1146      *
1147      * We'll put the widget into GTK_SELECTION_SINGLE mode in the
1148      * hopes that it's *STOP DOING THAT*.
1149      */
1150     gtk_clist_set_selection_mode(GTK_CLIST(field_tree),
1151                                  GTK_SELECTION_SINGLE);
1152 #endif
1153
1154     relation_vb = gtk_vbox_new(FALSE, 5);
1155     gtk_container_border_width(GTK_CONTAINER(relation_vb), 5);
1156     gtk_container_add(GTK_CONTAINER(main_hb), relation_vb);
1157
1158     relation_label = gtk_label_new("Relation");
1159     gtk_misc_set_alignment(GTK_MISC(relation_label), 0.0, 0.0);
1160     gtk_box_pack_start(GTK_BOX(relation_vb), relation_label, FALSE, FALSE, 0);
1161
1162     relation_list_scrolled_win = scrolled_window_new(NULL, NULL);
1163     /* never use a scrollbar in x direction, show the complete relation string */
1164     gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(relation_list_scrolled_win),
1165                                    GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
1166 #if GTK_MAJOR_VERSION >= 2
1167     gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(relation_list_scrolled_win), 
1168                                    GTK_SHADOW_IN);
1169 #endif
1170
1171 #if GTK_MAJOR_VERSION < 2
1172     relation_list = gtk_list_new();
1173     gtk_list_set_selection_mode(GTK_LIST(relation_list),
1174                                 GTK_SELECTION_BROWSE);
1175 #else
1176     l_store = gtk_list_store_new(1, G_TYPE_STRING);
1177     relation_list = tree_view_new(GTK_TREE_MODEL(l_store));
1178     gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(relation_list), FALSE);
1179     g_object_unref(G_OBJECT(l_store));
1180     renderer = gtk_cell_renderer_text_new();
1181     column = gtk_tree_view_column_new_with_attributes("relation", renderer,
1182                                                       "text", 0, NULL);
1183     gtk_tree_view_append_column(GTK_TREE_VIEW(relation_list), column);
1184     l_sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(relation_list));
1185     gtk_tree_selection_set_mode(l_sel, GTK_SELECTION_BROWSE);
1186 #endif
1187 #if GTK_MAJOR_VERSION < 2
1188     gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(relation_list_scrolled_win),
1189                                           relation_list);
1190 #else
1191     gtk_container_add(GTK_CONTAINER(relation_list_scrolled_win), relation_list);
1192 #endif
1193     gtk_box_pack_start(GTK_BOX(relation_vb), relation_list_scrolled_win, TRUE, TRUE, 0);
1194
1195     /*
1196      * OK, show the relation label and range stuff as it would be
1197      * with everything turned on, so it'll request as much space
1198      * as it'll ever need, so the dialog box and widgets start out
1199      * with the right sizes.
1200      *
1201      * XXX - this doesn't work.  It *doesn't* request as much space
1202      * as it'll ever need.
1203      *
1204      * XXX - FT_UINT8 doesn't support ranges, so even if it did work,
1205      * it wouldn't work right.
1206      *
1207      * XXX - this no longer affects the range stuff, as that's
1208      * controlled both by the type and by the relational operator
1209      * selected.
1210      */
1211     show_relations(relation_list, FT_UINT8);
1212
1213     /*
1214     relation_present_rb = RADIO_BUTTON_NEW_WITH_MNEMONIC(NULL, "is present", NULL);
1215     gtk_box_pack_start(GTK_BOX(relation_vb), relation_present_rb, FALSE, FALSE, 0);
1216
1217     relation_equals_rb = RADIO_BUTTON_NEW_WITH_MNEMONIC(relation_present_rb, "==", NULL);
1218     gtk_box_pack_start(GTK_BOX(relation_vb), relation_equals_rb, FALSE, FALSE, 0);
1219
1220     relation_unequals_rb = RADIO_BUTTON_NEW_WITH_MNEMONIC(relation_present_rb, "!=", NULL);
1221     gtk_box_pack_start(GTK_BOX(relation_vb), relation_unequals_rb, FALSE, FALSE, 0);
1222
1223     relation_greater_rb = RADIO_BUTTON_NEW_WITH_MNEMONIC(relation_present_rb, ">", NULL);
1224     gtk_box_pack_start(GTK_BOX(relation_vb), relation_greater_rb, FALSE, FALSE, 0);
1225
1226     relation_less_rb = RADIO_BUTTON_NEW_WITH_MNEMONIC(relation_present_rb, "<", NULL);
1227     gtk_box_pack_start(GTK_BOX(relation_vb), relation_less_rb, FALSE, FALSE, 0);
1228
1229     relation_greaterequal_rb = RADIO_BUTTON_NEW_WITH_MNEMONIC(relation_present_rb, ">=", NULL);
1230     gtk_box_pack_start(GTK_BOX(relation_vb), relation_greaterequal_rb, FALSE, FALSE, 0);
1231
1232     relation_lessequal_rb = RADIO_BUTTON_NEW_WITH_MNEMONIC(relation_present_rb, "<=", NULL);
1233     gtk_box_pack_start(GTK_BOX(relation_vb), relation_lessequal_rb, FALSE, FALSE, 0);
1234
1235     relation_contains_rb = RADIO_BUTTON_NEW_WITH_MNEMONIC(relation_present_rb, "contains", NULL);
1236     gtk_box_pack_start(GTK_BOX(relation_vb), relation_contains_rb, FALSE, FALSE, 0);
1237
1238     relation_matches_rb = RADIO_BUTTON_NEW_WITH_MNEMONIC(relation_present_rb, "matches", NULL);
1239     gtk_box_pack_start(GTK_BOX(relation_vb), relation_matches_rb, FALSE, FALSE, 0);
1240 */
1241     /* value column */
1242     value_vb = gtk_vbox_new(FALSE, 5);
1243     gtk_container_border_width(GTK_CONTAINER(value_vb), 5);
1244     gtk_container_add(GTK_CONTAINER(main_hb), value_vb);
1245
1246     value_label = gtk_label_new("Value");
1247     gtk_misc_set_alignment(GTK_MISC(value_label), 0.0, 0.0);
1248     gtk_box_pack_start(GTK_BOX(value_vb), value_label, FALSE, FALSE, 0);
1249
1250     value_entry = gtk_entry_new();
1251     gtk_box_pack_start(GTK_BOX(value_vb), value_entry, FALSE, FALSE, 0);
1252
1253     value_list_label = gtk_label_new("Predefined values:");
1254     gtk_misc_set_alignment(GTK_MISC(value_list_label), 0.0, 0.0);
1255     gtk_box_pack_start(GTK_BOX(value_vb), value_list_label, FALSE, FALSE, 0);
1256
1257     value_list_scrolled_win = scrolled_window_new(NULL, NULL);
1258     gtk_box_pack_start(GTK_BOX(value_vb), value_list_scrolled_win, TRUE,
1259                        TRUE, 0);
1260
1261 #if GTK_MAJOR_VERSION < 2
1262     value_list = gtk_list_new();
1263     gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(value_list_scrolled_win),
1264                                           value_list);
1265     SIGNAL_CONNECT(value_list, "select-child", value_list_sel_cb, value_entry);
1266     gtk_list_set_selection_mode(GTK_LIST(value_list), GTK_SELECTION_SINGLE);
1267     /* This remains hidden until an enumerated field is selected */
1268
1269     /*
1270      * The value stuff may be hidden or shown depending on what
1271      * relation was selected; connect to the "select-child" signal
1272      * for the relation list, so we can make that happen.
1273      */
1274     SIGNAL_CONNECT(relation_list, "select-child", relation_list_sel_cb, NULL);
1275 #else
1276     l_store = gtk_list_store_new(2, G_TYPE_STRING, G_TYPE_POINTER);
1277     value_list = tree_view_new(GTK_TREE_MODEL(l_store));
1278     gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(value_list), FALSE);
1279     g_object_unref(G_OBJECT(l_store));
1280     renderer = gtk_cell_renderer_text_new();
1281     column = gtk_tree_view_column_new_with_attributes("value", renderer,
1282                                                       "text", 0, NULL);
1283     gtk_tree_view_append_column(GTK_TREE_VIEW(value_list), column);
1284     SIGNAL_CONNECT(gtk_tree_view_get_selection(GTK_TREE_VIEW(value_list)),
1285                    "changed", value_list_sel_cb, value_entry);
1286
1287     /*
1288      * The value stuff may be hidden or shown depending on what
1289      * relation was selected; connect to the "changed" signal
1290      * for the relation list, so we can make that happen.
1291      */
1292     SIGNAL_CONNECT(gtk_tree_view_get_selection(GTK_TREE_VIEW(relation_list)),
1293                    "changed", relation_list_sel_cb, NULL);
1294     l_sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(value_list));
1295     gtk_tree_selection_set_mode(l_sel, GTK_SELECTION_SINGLE);
1296     gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(value_list_scrolled_win),
1297                                           value_list);
1298     /* This remains hidden until an enumerated field is selected */
1299 #endif
1300
1301     /*
1302      * Put the items in the Tree; we don't want to do that until
1303      * we've constructed the value list and set the tree's
1304      * E_DFILTER_EXPR_VALUE_LIST_KEY data to point to it, and
1305      * constructed the "Accept" button and set the tree's
1306      * E_DFILTER_EXPR_OK_BT_KEY data to point to it, so that
1307      * when the list item is "helpfully" automatically selected for us
1308      * we're ready to cope with the selection signal.
1309      */
1310
1311 #if GTK_MAJOR_VERSION < 2
1312     /* a hash table seems excessive, but I don't see support for a
1313        sparse array in glib */
1314     proto_array = g_hash_table_new(g_direct_hash, g_direct_equal);
1315     for (i = proto_get_first_protocol(&cookie); i != -1;
1316          i = proto_get_next_protocol(&cookie)) {
1317         hfinfo = proto_registrar_get_nth(i);
1318
1319         /* Create a node for the protocol, and remember it for
1320            later use. */
1321         protocol = find_protocol_by_id(i);
1322
1323         if (!proto_is_protocol_enabled(protocol))
1324             continue;
1325
1326         name = proto_get_protocol_short_name(protocol); /* name, short_name or filter name ? */
1327         protocol_node = gtk_ctree_insert_node(GTK_CTREE(field_tree),
1328                                               NULL, NULL,
1329                                               &name, 5,
1330                                               NULL, NULL, NULL, NULL,
1331                                               FALSE, FALSE);
1332         gtk_ctree_node_set_row_data(GTK_CTREE(field_tree), protocol_node,
1333                                     hfinfo);
1334         g_hash_table_insert(proto_array, (gpointer)i, protocol_node);
1335     }
1336
1337     len = proto_registrar_n();
1338     for (i = 0; i < len; i++) {
1339         char *strp, str[TAG_STRING_LEN+1];
1340
1341         /*
1342          * If this field is a protocol, skip it - we already put
1343          * it in above.
1344          */
1345         if (proto_registrar_is_protocol(i))
1346             continue;
1347
1348         hfinfo = proto_registrar_get_nth(i);
1349
1350         /*
1351          * If this field isn't at the head of the list of
1352          * fields with this name, skip this field - all
1353          * fields with the same name are really just versions
1354          * of the same field stored in different bits, and
1355          * should have the same type/radix/value list, and
1356          * just differ in their bit masks.  (If a field isn't
1357          * a bitfield, but can be, say, 1 or 2 bytes long,
1358          * it can just be made FT_UINT16, meaning the
1359          * *maximum* length is 2 bytes, and be used
1360          * for all lengths.)
1361          */
1362         if (hfinfo->same_name_prev != NULL)
1363             continue;
1364
1365         if (!proto_is_protocol_enabled(find_protocol_by_id(
1366                                        proto_registrar_get_parent(i)))) {
1367             continue;
1368         }
1369
1370         /* Create a node for the item, and put it
1371            under its parent protocol. */
1372         protocol_node = g_hash_table_lookup(proto_array,
1373                                             GINT_TO_POINTER(proto_registrar_get_parent(i)));
1374         if (hfinfo->blurb != NULL && hfinfo->blurb[0] != '\0') {
1375             g_snprintf(str, TAG_STRING_LEN, "%s - %s (%s)", hfinfo->abbrev,
1376                      hfinfo->name, hfinfo->blurb);
1377         } else {
1378             g_snprintf(str, TAG_STRING_LEN, "%s - %s", hfinfo->abbrev,
1379                      hfinfo->name);
1380         }
1381         str[TAG_STRING_LEN]=0;
1382         strp=str;
1383         item_node = gtk_ctree_insert_node(GTK_CTREE(field_tree),
1384                                           protocol_node, NULL,
1385                                           &strp, 5,
1386                                           NULL, NULL, NULL, NULL,
1387                                           TRUE, FALSE);
1388         gtk_ctree_node_set_row_data(GTK_CTREE(field_tree),
1389                                     item_node, hfinfo);
1390     }
1391     g_hash_table_destroy(proto_array);
1392
1393 #else /* GTK_MAJOR_VERSION < 2 */
1394 {
1395     /* GTK2 code using two levels iterator to enumerate all protocol fields */
1396
1397     GtkTreeIter iter, child_iter;
1398     void *cookie, *cookie2;
1399     gchar *name;
1400
1401     for (i = proto_get_first_protocol(&cookie); i != -1;
1402          i = proto_get_next_protocol(&cookie)) {
1403         char *strp, str[TAG_STRING_LEN+1];
1404
1405         protocol = find_protocol_by_id(i);
1406
1407         if (!proto_is_protocol_enabled(protocol)) {
1408             continue;
1409         }
1410
1411         name = proto_get_protocol_short_name(protocol); /* name, short_name or filter name ? */
1412         hfinfo = proto_registrar_get_nth(i);
1413
1414         gtk_tree_store_append(store, &iter, NULL);
1415         gtk_tree_store_set(store, &iter, 0, name, 1, hfinfo, -1);
1416
1417         for (hfinfo = proto_get_first_protocol_field(i, &cookie2); hfinfo != NULL;
1418              hfinfo = proto_get_next_protocol_field(&cookie2)) {
1419
1420                 if (hfinfo->same_name_prev != NULL) /* ignore duplicate names */
1421                         continue;
1422
1423                 if (hfinfo->blurb != NULL && hfinfo->blurb[0] != '\0') {
1424                         g_snprintf(str, TAG_STRING_LEN, "%s - %s (%s)",
1425                             hfinfo->abbrev, hfinfo->name, hfinfo->blurb);
1426                 } else {
1427                         g_snprintf(str, TAG_STRING_LEN, "%s - %s", hfinfo->abbrev,
1428                             hfinfo->name);
1429                 }
1430                 str[TAG_STRING_LEN]=0;
1431                 strp=str;
1432                 gtk_tree_store_append(store, &child_iter, &iter);
1433                 gtk_tree_store_set(store, &child_iter, 0, strp, 1, hfinfo, -1);
1434         }
1435     }
1436     g_object_unref(G_OBJECT(store));
1437 }
1438 #endif /* GTK_MAJOR_VERSION < 2 */
1439
1440     range_label = gtk_label_new("Range (offset:length)");
1441     gtk_misc_set_alignment(GTK_MISC(range_label), 0.0, 0.0);
1442     gtk_box_pack_start(GTK_BOX(value_vb), range_label, FALSE, FALSE, 0);
1443
1444     range_entry = gtk_entry_new();
1445     gtk_box_pack_start(GTK_BOX(value_vb), range_entry, FALSE, FALSE, 0);
1446
1447
1448     /* button box */
1449     list_bb = dlg_button_row_new(GTK_STOCK_OK, GTK_STOCK_CANCEL, NULL);
1450         gtk_box_pack_start(GTK_BOX(main_vb), list_bb, FALSE, FALSE, 0);
1451     gtk_container_set_border_width  (GTK_CONTAINER (list_bb), 0);
1452
1453     ok_bt = OBJECT_GET_DATA(list_bb, GTK_STOCK_OK);
1454     gtk_widget_set_sensitive(ok_bt, FALSE);
1455     SIGNAL_CONNECT(ok_bt, "clicked", dfilter_expr_dlg_accept_cb, filter_te);
1456
1457     cancel_bt = OBJECT_GET_DATA(list_bb, GTK_STOCK_CANCEL);
1458     window_set_cancel_button(window, cancel_bt, NULL);
1459     SIGNAL_CONNECT(cancel_bt, "clicked", dfilter_expr_dlg_cancel_cb, window);
1460
1461     gtk_widget_grab_default(ok_bt);
1462
1463     /* Catch the "activate" signal on the range and value text entries,
1464        so that if the user types Return there, we act as if the "Accept"
1465        button had been selected, as happens if Return is typed if some
1466        widget that *doesn't* handle the Return key has the input focus. */
1467     dlg_set_activate(range_entry, ok_bt);
1468     dlg_set_activate(value_entry, ok_bt);
1469
1470     OBJECT_SET_DATA(window, E_DFILTER_EXPR_RELATION_LIST_KEY, relation_list);
1471     OBJECT_SET_DATA(window, E_DFILTER_EXPR_RANGE_LABEL_KEY, range_label);
1472     OBJECT_SET_DATA(window, E_DFILTER_EXPR_RANGE_ENTRY_KEY, range_entry);
1473     OBJECT_SET_DATA(window, E_DFILTER_EXPR_VALUE_LABEL_KEY, value_label);
1474     OBJECT_SET_DATA(window, E_DFILTER_EXPR_VALUE_ENTRY_KEY, value_entry);
1475     OBJECT_SET_DATA(window, E_DFILTER_EXPR_VALUE_LIST_KEY, value_list);
1476     OBJECT_SET_DATA(window, E_DFILTER_EXPR_VALUE_LIST_LABEL_KEY, value_list_label);
1477     OBJECT_SET_DATA(window, E_DFILTER_EXPR_VALUE_LIST_SW_KEY,
1478                     value_list_scrolled_win);
1479     OBJECT_SET_DATA(window, E_DFILTER_EXPR_OK_BT_KEY, ok_bt);
1480
1481 #if GTK_MAJOR_VERSION < 2
1482     /*
1483      * OK, we've finally built the entire list, complete with the row data,
1484      * and attached to the top-level widget pointers to the relevant
1485      * subwidgets, so it's safe to put the list in browse mode.
1486      */
1487     gtk_clist_set_selection_mode (GTK_CLIST(field_tree),
1488                                   GTK_SELECTION_BROWSE);
1489 #endif
1490
1491     SIGNAL_CONNECT(window, "delete_event", dfilter_expr_dlg_delete_event_cb,
1492                    window);
1493
1494     /*
1495      * Catch the "destroy" signal on our top-level window, and,
1496      * when it's destroyed, disconnect the signal we'll be
1497      * connecting below.
1498      */
1499     SIGNAL_CONNECT(window, "destroy", dfilter_expr_dlg_destroy_cb, filter_te);
1500
1501     /*
1502      * Catch the "destroy" signal on the text entry widget to which
1503      * we're attached; if it's destroyed, we should destroy ourselves
1504      * as well.
1505      */
1506     SIGNAL_CONNECT(filter_te, "destroy", dfilter_expr_dlg_cancel_cb, window);
1507
1508     gtk_widget_show_all(window);
1509     window_present(window);
1510 }