Replace a handful of tabs with spaces.
[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  *      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 "gui_utils.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, 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 #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, const 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, (const 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                             (const 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, const gchar *string, const 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_UINT24:
700             case FT_UINT32:
701                 g_snprintf(value_string, sizeof value_string,
702                          "%u", value->value);
703                 break;
704
705             case FT_INT8:
706             case FT_INT16:
707             case FT_INT24:
708             case FT_INT32:
709                 g_snprintf(value_string, sizeof value_string,
710                          "%d", value->value);
711                 break;
712
713             default:
714                 g_assert_not_reached();
715             }
716             break;
717
718         case BASE_HEX:
719             g_snprintf(value_string, sizeof value_string, "0x%x",
720                      value->value);
721             break;
722
723         case BASE_OCT:
724             g_snprintf(value_string, sizeof value_string, "%#o",
725                      value->value);
726             break;
727
728         default:
729             g_assert_not_reached();
730         }
731     }
732     gtk_entry_set_text(GTK_ENTRY(value_entry), value_string);
733 }
734
735 static void
736 dfilter_report_bad_value(const char *format, ...)
737 {
738         char error_msg_buf[1024];
739         va_list args;
740
741         va_start(args, format);
742         g_vsnprintf(error_msg_buf, sizeof error_msg_buf, format, args);
743         va_end(args);
744
745         simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "%s", error_msg_buf);
746 }
747
748 static void
749 dfilter_expr_dlg_accept_cb(GtkWidget *w, gpointer filter_te_arg)
750 {
751     GtkWidget *filter_te = filter_te_arg;
752     GtkWidget *window = gtk_widget_get_toplevel(w);
753     GtkWidget *relation_list =
754         OBJECT_GET_DATA(window, E_DFILTER_EXPR_RELATION_LIST_KEY);
755     GtkWidget *range_entry =
756         OBJECT_GET_DATA(window, E_DFILTER_EXPR_RANGE_ENTRY_KEY);
757     GtkWidget *value_entry =
758         OBJECT_GET_DATA(window, E_DFILTER_EXPR_VALUE_ENTRY_KEY);
759     header_field_info *hfinfo;
760     gchar        *item_str;
761     gchar        *range_str, *stripped_range_str;
762     gchar        *value_str, *stripped_value_str;
763     int           pos;
764     gchar        *chars;
765     ftenum_t      ftype;
766     gboolean      can_compare;
767     fvalue_t     *fvalue;
768 #if GTK_MAJOR_VERSION < 2
769     GList        *sl;
770     GtkWidget    *item, *item_label;
771 #else
772     GtkTreeModel *model;
773     GtkTreeIter   iter;
774 #endif
775     gboolean      quote_it;
776
777     /*
778      * Get the variable to be tested.
779      */
780     hfinfo = OBJECT_GET_DATA(window, E_DFILTER_EXPR_CURRENT_VAR_KEY);
781
782     /*
783      * Get the relation operator to use.
784      */
785 #if GTK_MAJOR_VERSION < 2
786     sl = GTK_LIST(relation_list)->selection;
787     item = GTK_WIDGET(sl->data);
788     item_label = GTK_BIN(item)->child;
789     gtk_label_get(GTK_LABEL(item_label), &item_str);
790 #else
791     if (gtk_tree_selection_get_selected(gtk_tree_view_get_selection(GTK_TREE_VIEW(relation_list)),
792                                         &model, &iter)) {
793         gtk_tree_model_get(model, &iter, 0, &item_str, -1);
794     }
795 #endif
796
797     /*
798      * Get the range to use, if any.
799      */
800     if (GTK_WIDGET_SENSITIVE(range_entry)) {
801         range_str = g_strdup(gtk_entry_get_text(GTK_ENTRY(range_entry)));
802         /*
803          * XXX - strip this even for strings?
804          * Doing so for strings mean you can't match a string that has
805          * leading or trailing whitespace, but you can't see trailing
806          * whitespace in a text field, so it's not clear that it's
807          * a good idea to allow that.
808          */
809         stripped_range_str = g_strstrip(range_str);
810         if (strcmp(stripped_range_str, "") == 0) {
811             /*
812              * No range was specified.
813              */
814             g_free(range_str);
815             range_str = NULL;
816             stripped_range_str = NULL;
817         }
818
819         /*
820          * XXX - check it for validity?
821          */
822     } else {
823         range_str = NULL;
824         stripped_range_str = NULL;
825     }
826
827     /*
828      * If a range was specified, the type of the LHS of the
829      * comparison is FT_BYTES; otherwise, it's the type of the field.
830      */
831     if (range_str == NULL)
832         ftype = hfinfo->type;
833     else
834         ftype = FT_BYTES;
835
836     /*
837      * Make sure the relation is valid for the type in question.
838      * We may be offering relations that the type of the field
839      * can't support, because the field's type supports slicing,
840      * and the relation *is* supported on byte strings.
841      */
842     if (strcmp(item_str, "==") == 0)
843         can_compare = ftype_can_eq(ftype);
844     else if (strcmp(item_str, "!=") == 0)
845         can_compare = ftype_can_ne(ftype);
846     else if (strcmp(item_str, ">") == 0)
847         can_compare = ftype_can_gt(ftype);
848     else if (strcmp(item_str, "<") == 0)
849         can_compare = ftype_can_lt(ftype);
850     else if (strcmp(item_str, ">=") == 0)
851         can_compare = ftype_can_ge(ftype);
852     else if (strcmp(item_str, "<=") == 0)
853         can_compare = ftype_can_le(ftype);
854     else if (strcmp(item_str, "contains") == 0)
855         can_compare = ftype_can_contains(ftype);
856     else if (strcmp(item_str, "matches") == 0)
857         can_compare = ftype_can_matches(ftype);
858     else
859         can_compare = TRUE;     /* not a comparison */
860     if (!can_compare) {
861         if (range_str == NULL) {
862             simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
863                           "That field can't be tested with \"%s\".",
864                           item_str);
865         } else {
866             simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
867                           "Ranges of that field can't be tested with \"%s\".",
868                           item_str);
869         }
870         if (range_str != NULL)
871             g_free(range_str);
872         return;
873     }
874
875     /*
876      * Get the value to use, if any.
877      */
878     if (GTK_WIDGET_SENSITIVE(value_entry)) {
879         value_str = g_strdup(gtk_entry_get_text(GTK_ENTRY(value_entry)));
880         stripped_value_str = g_strstrip(value_str);
881         if (strcmp(stripped_value_str, "") == 0) {
882             /*
883              * This field takes a value, but they didn't supply
884              * one.
885              */
886             simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
887                           "That field must be compared with a value, "
888                           "but you didn't specify a value with which to "
889                           "compare it.");
890             if (range_str != NULL)
891                 g_free(range_str);
892             g_free(value_str);
893             return;
894         }
895
896         /*
897          * Make sure the value is valid.
898          *
899          * If no range string was specified, it must be valid
900          * for the type of the field; if a range string was
901          * specified, must be valid for FT_BYTES.
902          */
903         if (strcmp(item_str, "contains") == 0) {
904                 fvalue = fvalue_from_unparsed(ftype, stripped_value_str, TRUE,
905                                             dfilter_report_bad_value);
906         }
907         else {
908                 fvalue = fvalue_from_unparsed(ftype, stripped_value_str, FALSE,
909                                             dfilter_report_bad_value);
910         }
911         if (fvalue == NULL) {
912             /*
913              * It's not valid.
914              *
915              * The dialog box was already popped up by
916              * "dfilter_report_bad_value()".
917              */
918             if (range_str != NULL)
919                 g_free(range_str);
920             g_free(value_str);
921             return;
922         }
923         FVALUE_FREE(fvalue);
924     } else {
925         value_str = NULL;
926         stripped_value_str = NULL;
927     }
928
929     /*
930      * Insert the expression at the current cursor position.
931      * If there's a non-whitespace character to the left of it,
932      * insert a blank first; if there's a non-whitespace character
933      * to the right of it, insert a blank after it.
934      */
935     pos = gtk_editable_get_position(GTK_EDITABLE(filter_te));
936     chars = gtk_editable_get_chars(GTK_EDITABLE(filter_te), pos, pos + 1);
937     if (strcmp(chars, "") != 0 && !isspace((unsigned char)chars[0]))
938         gtk_editable_insert_text(GTK_EDITABLE(filter_te), " ", 1, &pos);
939     g_free(chars);
940
941     gtk_editable_insert_text(GTK_EDITABLE(filter_te), hfinfo->abbrev,
942                              strlen(hfinfo->abbrev), &pos);
943     if (range_str != NULL) {
944         gtk_editable_insert_text(GTK_EDITABLE(filter_te), "[", 1, &pos);
945         gtk_editable_insert_text(GTK_EDITABLE(filter_te),
946                                  stripped_range_str, strlen(stripped_range_str), &pos);
947         gtk_editable_insert_text(GTK_EDITABLE(filter_te), "]", 1, &pos);
948         g_free(range_str);
949     }
950     if (item_str != NULL && !relation_is_presence_test(item_str)) {
951         gtk_editable_insert_text(GTK_EDITABLE(filter_te), " ", 1, &pos);
952         gtk_editable_insert_text(GTK_EDITABLE(filter_te), item_str,
953                                  strlen(item_str), &pos);
954     }
955     if (value_str != NULL) {
956         gtk_editable_insert_text(GTK_EDITABLE(filter_te), " ", 1, &pos);
957         /*
958          * XXX - we should do this by generating an appropriate display
959          * filter value string for this field; that requires us to have
960          * a "generate display filter string" method for every FT_ type.
961          */
962         switch (hfinfo->type) {
963
964         case FT_STRING:
965         case FT_STRINGZ:
966         case FT_UINT_STRING:
967         case FT_ABSOLUTE_TIME:
968             /*
969              * Always put quotes around the string.
970              */
971             quote_it = TRUE;
972             break;
973
974         default:
975             /*
976              * If the string contains white space, put quotes around it.
977              */
978             quote_it = (strpbrk(stripped_value_str, " \t") != NULL);
979             break;
980         }
981         if (quote_it) {
982             /*
983              * Put quotes around the string.
984              */
985             gtk_editable_insert_text(GTK_EDITABLE(filter_te), "\"",
986                                      1, &pos);
987         }
988         gtk_editable_insert_text(GTK_EDITABLE(filter_te),
989                                  stripped_value_str, strlen(stripped_value_str), &pos);
990         if (quote_it) {
991             /*
992              * Put quotes around the string.
993              */
994             gtk_editable_insert_text(GTK_EDITABLE(filter_te), "\"",
995                                      1, &pos);
996         }
997         g_free(value_str);
998     }
999     chars = gtk_editable_get_chars(GTK_EDITABLE(filter_te), pos + 1, pos + 2);
1000     if (strcmp(chars, "") != 0 && !isspace((unsigned char)chars[0]))
1001         gtk_editable_insert_text(GTK_EDITABLE(filter_te), " ", 1, &pos);
1002     g_free(chars);
1003
1004     /*
1005      * Put the cursor after the expression we just entered into
1006      * the text entry widget.
1007      */
1008     gtk_editable_set_position(GTK_EDITABLE(filter_te), pos);
1009
1010     /*
1011      * We're done; destroy the dialog box (which is the top-level
1012      * widget for the "Accept" button).
1013      */
1014     window_destroy(window);
1015 #if GTK_MAJOR_VERSION >= 2
1016     g_free(item_str);
1017 #endif
1018 }
1019
1020 static void
1021 dfilter_expr_dlg_cancel_cb(GtkWidget *w _U_, gpointer parent_w)
1022 {
1023         /*
1024          * User pressed the cancel button; close the dialog box.
1025          */
1026         window_destroy(GTK_WIDGET(parent_w));
1027 }
1028
1029 /* Treat this as a cancel, by calling "prefs_main_cancel_cb()" */
1030 static gboolean
1031 dfilter_expr_dlg_delete_event_cb(GtkWidget *w _U_, GdkEvent *event _U_,
1032                                  gpointer parent_w)
1033 {
1034         dfilter_expr_dlg_cancel_cb(NULL, parent_w);
1035         return FALSE;
1036 }
1037
1038 static void
1039 dfilter_expr_dlg_destroy_cb(GtkWidget *w, gpointer filter_te)
1040 {
1041         /*
1042          * The dialog box is being destroyed; disconnect from the
1043          * "destroy" signal on the text entry box to which we're
1044          * attached, as the handler for that signal is supposed
1045          * to destroy us, but we're already gone.
1046          */
1047         SIGNAL_DISCONNECT_BY_FUNC(filter_te, dfilter_expr_dlg_cancel_cb, w);
1048 }
1049
1050 /*
1051  * Length of string used for protocol fields.
1052  */
1053 #define TAG_STRING_LEN  256
1054
1055 GtkWidget *
1056 dfilter_expr_dlg_new(GtkWidget *filter_te)
1057 {
1058     GtkWidget *window, *main_vb, *main_hb;
1059
1060     GtkWidget *field_vb, *field_tree_lb, *field_tree, *tree_scrolled_win;
1061
1062     GtkWidget *relation_vb, *relation_label, *relation_list, *relation_list_scrolled_win;
1063 /*    GtkWidget *relation_present_rb, *relation_equals_rb, *relation_unequals_rb,
1064               *relation_greater_rb, *relation_less_rb,
1065               *relation_greaterequal_rb, *relation_lessequal_rb,
1066               *relation_contains_rb, *relation_matches_rb;*/
1067
1068     GtkWidget *value_vb, *value_label, *value_entry;
1069     GtkWidget *value_list_label, *value_list_scrolled_win, *value_list;
1070     GtkWidget *range_label, *range_entry;
1071
1072     GtkWidget *list_bb, *ok_bt, *cancel_bt;
1073     header_field_info       *hfinfo;
1074     int i;
1075     protocol_t *protocol;
1076 #if GTK_MAJOR_VERSION < 2
1077     int len;
1078     void *cookie;
1079     const gchar *name;
1080     GHashTable *proto_array;
1081     GtkCTreeNode *protocol_node, *item_node;
1082 #else
1083     GtkTreeStore *store;
1084     GtkTreeSelection *selection;
1085     GtkCellRenderer *renderer;
1086     GtkTreeViewColumn *column;
1087     GtkListStore      *l_store;
1088     GtkTreeSelection  *l_sel;
1089 #endif
1090
1091     window = dlg_window_new("Wireshark: Filter Expression");
1092         gtk_window_set_default_size(GTK_WINDOW(window), 500, 400);
1093     gtk_container_set_border_width(GTK_CONTAINER(window), 5);
1094
1095     main_vb = gtk_vbox_new(FALSE, 5);
1096     gtk_container_border_width(GTK_CONTAINER(main_vb), 5);
1097     gtk_container_add(GTK_CONTAINER(window), main_vb);
1098
1099     main_hb = gtk_hbox_new(FALSE, 5);
1100     gtk_container_border_width(GTK_CONTAINER(main_hb), 5);
1101     gtk_container_add(GTK_CONTAINER(main_vb), main_hb);
1102
1103     field_vb = gtk_vbox_new(FALSE, 5);
1104     gtk_container_border_width(GTK_CONTAINER(field_vb), 5);
1105     gtk_container_add(GTK_CONTAINER(main_hb), field_vb);
1106
1107     field_tree_lb = gtk_label_new("Field name");
1108     gtk_misc_set_alignment(GTK_MISC(field_tree_lb), 0.0, 0.0);
1109     gtk_box_pack_start(GTK_BOX(field_vb), field_tree_lb, FALSE, FALSE, 0);
1110
1111     tree_scrolled_win = scrolled_window_new(NULL, NULL);
1112 #if GTK_MAJOR_VERSION >= 2
1113     gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(tree_scrolled_win),
1114                                    GTK_SHADOW_IN);
1115 #endif
1116     gtk_box_pack_start(GTK_BOX(field_vb), tree_scrolled_win, TRUE, TRUE, 0);
1117         WIDGET_SET_SIZE(tree_scrolled_win, 300, -1);
1118
1119
1120 #if GTK_MAJOR_VERSION < 2
1121     field_tree = ctree_new(1, 0);
1122     SIGNAL_CONNECT(field_tree, "tree-select-row", field_select_row_cb, field_tree);
1123 #else
1124     store = gtk_tree_store_new(2, G_TYPE_STRING, G_TYPE_POINTER);
1125     field_tree = tree_view_new(GTK_TREE_MODEL(store));
1126     gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(field_tree), FALSE);
1127     selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(field_tree));
1128     gtk_tree_selection_set_mode(selection, GTK_SELECTION_BROWSE);
1129     renderer = gtk_cell_renderer_text_new();
1130     column = gtk_tree_view_column_new_with_attributes("Field name", renderer,
1131                                                       "text", 0, NULL);
1132     gtk_tree_view_append_column(GTK_TREE_VIEW(field_tree), column);
1133     gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
1134     gtk_tree_view_column_set_sort_column_id(column, 0);
1135     SIGNAL_CONNECT(selection, "changed", field_select_row_cb, field_tree);
1136 #endif
1137     gtk_container_add(GTK_CONTAINER(tree_scrolled_win), field_tree);
1138
1139 #if GTK_MAJOR_VERSION < 2
1140     /*
1141      * GTK's annoying CTree widget will deliver a selection event
1142      * the instant you add an item to the field_tree, *the fact that you
1143      * haven't even had time to set the item's row data nonwithstanding*.
1144      *
1145      * We'll put the widget into GTK_SELECTION_SINGLE mode in the
1146      * hopes that it's *STOP DOING THAT*.
1147      */
1148     gtk_clist_set_selection_mode(GTK_CLIST(field_tree),
1149                                  GTK_SELECTION_SINGLE);
1150 #endif
1151
1152     relation_vb = gtk_vbox_new(FALSE, 5);
1153     gtk_container_border_width(GTK_CONTAINER(relation_vb), 5);
1154     gtk_container_add(GTK_CONTAINER(main_hb), relation_vb);
1155
1156     relation_label = gtk_label_new("Relation");
1157     gtk_misc_set_alignment(GTK_MISC(relation_label), 0.0, 0.0);
1158     gtk_box_pack_start(GTK_BOX(relation_vb), relation_label, FALSE, FALSE, 0);
1159
1160     relation_list_scrolled_win = scrolled_window_new(NULL, NULL);
1161     /* never use a scrollbar in x direction, show the complete relation string */
1162     gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(relation_list_scrolled_win),
1163                                    GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
1164 #if GTK_MAJOR_VERSION >= 2
1165     gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(relation_list_scrolled_win),
1166                                    GTK_SHADOW_IN);
1167 #endif
1168
1169 #if GTK_MAJOR_VERSION < 2
1170     relation_list = gtk_list_new();
1171     gtk_list_set_selection_mode(GTK_LIST(relation_list),
1172                                 GTK_SELECTION_BROWSE);
1173 #else
1174     l_store = gtk_list_store_new(1, G_TYPE_STRING);
1175     relation_list = tree_view_new(GTK_TREE_MODEL(l_store));
1176     gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(relation_list), FALSE);
1177     g_object_unref(G_OBJECT(l_store));
1178     renderer = gtk_cell_renderer_text_new();
1179     column = gtk_tree_view_column_new_with_attributes("relation", renderer,
1180                                                       "text", 0, NULL);
1181     gtk_tree_view_append_column(GTK_TREE_VIEW(relation_list), column);
1182     l_sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(relation_list));
1183     gtk_tree_selection_set_mode(l_sel, GTK_SELECTION_BROWSE);
1184 #endif
1185 #if GTK_MAJOR_VERSION < 2
1186     gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(relation_list_scrolled_win),
1187                                           relation_list);
1188 #else
1189     gtk_container_add(GTK_CONTAINER(relation_list_scrolled_win), relation_list);
1190 #endif
1191     gtk_box_pack_start(GTK_BOX(relation_vb), relation_list_scrolled_win, TRUE, TRUE, 0);
1192
1193     /*
1194      * OK, show the relation label and range stuff as it would be
1195      * with everything turned on, so it'll request as much space
1196      * as it'll ever need, so the dialog box and widgets start out
1197      * with the right sizes.
1198      *
1199      * XXX - this doesn't work.  It *doesn't* request as much space
1200      * as it'll ever need.
1201      *
1202      * XXX - FT_UINT8 doesn't support ranges, so even if it did work,
1203      * it wouldn't work right.
1204      *
1205      * XXX - this no longer affects the range stuff, as that's
1206      * controlled both by the type and by the relational operator
1207      * selected.
1208      */
1209     show_relations(relation_list, FT_UINT8);
1210
1211     /*
1212     relation_present_rb = RADIO_BUTTON_NEW_WITH_MNEMONIC(NULL, "is present", NULL);
1213     gtk_box_pack_start(GTK_BOX(relation_vb), relation_present_rb, FALSE, FALSE, 0);
1214
1215     relation_equals_rb = RADIO_BUTTON_NEW_WITH_MNEMONIC(relation_present_rb, "==", NULL);
1216     gtk_box_pack_start(GTK_BOX(relation_vb), relation_equals_rb, FALSE, FALSE, 0);
1217
1218     relation_unequals_rb = RADIO_BUTTON_NEW_WITH_MNEMONIC(relation_present_rb, "!=", NULL);
1219     gtk_box_pack_start(GTK_BOX(relation_vb), relation_unequals_rb, FALSE, FALSE, 0);
1220
1221     relation_greater_rb = RADIO_BUTTON_NEW_WITH_MNEMONIC(relation_present_rb, ">", NULL);
1222     gtk_box_pack_start(GTK_BOX(relation_vb), relation_greater_rb, FALSE, FALSE, 0);
1223
1224     relation_less_rb = RADIO_BUTTON_NEW_WITH_MNEMONIC(relation_present_rb, "<", NULL);
1225     gtk_box_pack_start(GTK_BOX(relation_vb), relation_less_rb, FALSE, FALSE, 0);
1226
1227     relation_greaterequal_rb = RADIO_BUTTON_NEW_WITH_MNEMONIC(relation_present_rb, ">=", NULL);
1228     gtk_box_pack_start(GTK_BOX(relation_vb), relation_greaterequal_rb, FALSE, FALSE, 0);
1229
1230     relation_lessequal_rb = RADIO_BUTTON_NEW_WITH_MNEMONIC(relation_present_rb, "<=", NULL);
1231     gtk_box_pack_start(GTK_BOX(relation_vb), relation_lessequal_rb, FALSE, FALSE, 0);
1232
1233     relation_contains_rb = RADIO_BUTTON_NEW_WITH_MNEMONIC(relation_present_rb, "contains", NULL);
1234     gtk_box_pack_start(GTK_BOX(relation_vb), relation_contains_rb, FALSE, FALSE, 0);
1235
1236     relation_matches_rb = RADIO_BUTTON_NEW_WITH_MNEMONIC(relation_present_rb, "matches", NULL);
1237     gtk_box_pack_start(GTK_BOX(relation_vb), relation_matches_rb, FALSE, FALSE, 0);
1238 */
1239     /* value column */
1240     value_vb = gtk_vbox_new(FALSE, 5);
1241     gtk_container_border_width(GTK_CONTAINER(value_vb), 5);
1242     gtk_container_add(GTK_CONTAINER(main_hb), value_vb);
1243
1244     value_label = gtk_label_new("Value");
1245     gtk_misc_set_alignment(GTK_MISC(value_label), 0.0, 0.0);
1246     gtk_box_pack_start(GTK_BOX(value_vb), value_label, FALSE, FALSE, 0);
1247
1248     value_entry = gtk_entry_new();
1249     gtk_box_pack_start(GTK_BOX(value_vb), value_entry, FALSE, FALSE, 0);
1250
1251     value_list_label = gtk_label_new("Predefined values:");
1252     gtk_misc_set_alignment(GTK_MISC(value_list_label), 0.0, 0.0);
1253     gtk_box_pack_start(GTK_BOX(value_vb), value_list_label, FALSE, FALSE, 0);
1254
1255     value_list_scrolled_win = scrolled_window_new(NULL, NULL);
1256     gtk_box_pack_start(GTK_BOX(value_vb), value_list_scrolled_win, TRUE,
1257                        TRUE, 0);
1258
1259 #if GTK_MAJOR_VERSION < 2
1260     value_list = gtk_list_new();
1261     gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(value_list_scrolled_win),
1262                                           value_list);
1263     SIGNAL_CONNECT(value_list, "select-child", value_list_sel_cb, value_entry);
1264     gtk_list_set_selection_mode(GTK_LIST(value_list), GTK_SELECTION_SINGLE);
1265     /* This remains hidden until an enumerated field is selected */
1266
1267     /*
1268      * The value stuff may be hidden or shown depending on what
1269      * relation was selected; connect to the "select-child" signal
1270      * for the relation list, so we can make that happen.
1271      */
1272     SIGNAL_CONNECT(relation_list, "select-child", relation_list_sel_cb, NULL);
1273 #else
1274     l_store = gtk_list_store_new(2, G_TYPE_STRING, G_TYPE_POINTER);
1275     value_list = tree_view_new(GTK_TREE_MODEL(l_store));
1276     gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(value_list), FALSE);
1277     g_object_unref(G_OBJECT(l_store));
1278     renderer = gtk_cell_renderer_text_new();
1279     column = gtk_tree_view_column_new_with_attributes("value", renderer,
1280                                                       "text", 0, NULL);
1281     gtk_tree_view_append_column(GTK_TREE_VIEW(value_list), column);
1282     SIGNAL_CONNECT(gtk_tree_view_get_selection(GTK_TREE_VIEW(value_list)),
1283                    "changed", value_list_sel_cb, value_entry);
1284
1285     /*
1286      * The value stuff may be hidden or shown depending on what
1287      * relation was selected; connect to the "changed" signal
1288      * for the relation list, so we can make that happen.
1289      */
1290     SIGNAL_CONNECT(gtk_tree_view_get_selection(GTK_TREE_VIEW(relation_list)),
1291                    "changed", relation_list_sel_cb, NULL);
1292     l_sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(value_list));
1293     gtk_tree_selection_set_mode(l_sel, GTK_SELECTION_SINGLE);
1294     gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(value_list_scrolled_win),
1295                                           value_list);
1296     /* This remains hidden until an enumerated field is selected */
1297 #endif
1298
1299     /*
1300      * Put the items in the Tree; we don't want to do that until
1301      * we've constructed the value list and set the tree's
1302      * E_DFILTER_EXPR_VALUE_LIST_KEY data to point to it, and
1303      * constructed the "Accept" button and set the tree's
1304      * E_DFILTER_EXPR_OK_BT_KEY data to point to it, so that
1305      * when the list item is "helpfully" automatically selected for us
1306      * we're ready to cope with the selection signal.
1307      */
1308
1309 #if GTK_MAJOR_VERSION < 2
1310     /* a hash table seems excessive, but I don't see support for a
1311        sparse array in glib */
1312     proto_array = g_hash_table_new(g_direct_hash, g_direct_equal);
1313     for (i = proto_get_first_protocol(&cookie); i != -1;
1314          i = proto_get_next_protocol(&cookie)) {
1315         hfinfo = proto_registrar_get_nth(i);
1316
1317         /* Create a node for the protocol, and remember it for
1318            later use. */
1319         protocol = find_protocol_by_id(i);
1320
1321         if (!proto_is_protocol_enabled(protocol))
1322             continue;
1323
1324         name = proto_get_protocol_short_name(protocol); /* name, short_name or filter name ? */
1325         protocol_node = gtk_ctree_insert_node(GTK_CTREE(field_tree),
1326                                               NULL, NULL,
1327                                               (gchar **) &name, 5,
1328                                               NULL, NULL, NULL, NULL,
1329                                               FALSE, FALSE);
1330         gtk_ctree_node_set_row_data(GTK_CTREE(field_tree), protocol_node,
1331                                     hfinfo);
1332         g_hash_table_insert(proto_array, GINT_TO_POINTER(i), protocol_node);
1333     }
1334
1335     len = proto_registrar_n();
1336     for (i = 0; i < len; i++) {
1337         char *strp, str[TAG_STRING_LEN+1];
1338
1339         /*
1340          * If this field is a protocol, skip it - we already put
1341          * it in above.
1342          */
1343         if (proto_registrar_is_protocol(i))
1344             continue;
1345
1346         hfinfo = proto_registrar_get_nth(i);
1347
1348         /*
1349          * If this field isn't at the head of the list of
1350          * fields with this name, skip this field - all
1351          * fields with the same name are really just versions
1352          * of the same field stored in different bits, and
1353          * should have the same type/radix/value list, and
1354          * just differ in their bit masks.  (If a field isn't
1355          * a bitfield, but can be, say, 1 or 2 bytes long,
1356          * it can just be made FT_UINT16, meaning the
1357          * *maximum* length is 2 bytes, and be used
1358          * for all lengths.)
1359          */
1360         if (hfinfo->same_name_prev != NULL)
1361             continue;
1362
1363         if (!proto_is_protocol_enabled(find_protocol_by_id(
1364                                        proto_registrar_get_parent(i)))) {
1365             continue;
1366         }
1367
1368         /* Create a node for the item, and put it
1369            under its parent protocol. */
1370         protocol_node = g_hash_table_lookup(proto_array,
1371                                             GINT_TO_POINTER(proto_registrar_get_parent(i)));
1372         if (hfinfo->blurb != NULL && hfinfo->blurb[0] != '\0') {
1373             g_snprintf(str, TAG_STRING_LEN, "%s - %s (%s)", hfinfo->abbrev,
1374                      hfinfo->name, hfinfo->blurb);
1375         } else {
1376             g_snprintf(str, TAG_STRING_LEN, "%s - %s", hfinfo->abbrev,
1377                      hfinfo->name);
1378         }
1379         str[TAG_STRING_LEN]=0;
1380         strp=str;
1381         item_node = gtk_ctree_insert_node(GTK_CTREE(field_tree),
1382                                           protocol_node, NULL,
1383                                           &strp, 5,
1384                                           NULL, NULL, NULL, NULL,
1385                                           TRUE, FALSE);
1386         gtk_ctree_node_set_row_data(GTK_CTREE(field_tree),
1387                                     item_node, hfinfo);
1388     }
1389     g_hash_table_destroy(proto_array);
1390
1391 #else /* GTK_MAJOR_VERSION < 2 */
1392 {
1393     /* GTK2 code using two levels iterator to enumerate all protocol fields */
1394
1395     GtkTreeIter iter, child_iter;
1396     void *cookie, *cookie2;
1397     const gchar *name;
1398
1399     for (i = proto_get_first_protocol(&cookie); i != -1;
1400          i = proto_get_next_protocol(&cookie)) {
1401         char *strp, str[TAG_STRING_LEN+1];
1402
1403         protocol = find_protocol_by_id(i);
1404
1405         if (!proto_is_protocol_enabled(protocol)) {
1406             continue;
1407         }
1408
1409         name = proto_get_protocol_short_name(protocol); /* name, short_name or filter name ? */
1410         hfinfo = proto_registrar_get_nth(i);
1411
1412         gtk_tree_store_append(store, &iter, NULL);
1413         gtk_tree_store_set(store, &iter, 0, name, 1, hfinfo, -1);
1414
1415         for (hfinfo = proto_get_first_protocol_field(i, &cookie2); hfinfo != NULL;
1416              hfinfo = proto_get_next_protocol_field(&cookie2)) {
1417
1418                 if (hfinfo->same_name_prev != NULL) /* ignore duplicate names */
1419                         continue;
1420
1421                 if (hfinfo->blurb != NULL && hfinfo->blurb[0] != '\0') {
1422                         g_snprintf(str, TAG_STRING_LEN, "%s - %s (%s)",
1423                             hfinfo->abbrev, hfinfo->name, hfinfo->blurb);
1424                 } else {
1425                         g_snprintf(str, TAG_STRING_LEN, "%s - %s", hfinfo->abbrev,
1426                             hfinfo->name);
1427                 }
1428                 str[TAG_STRING_LEN]=0;
1429                 strp=str;
1430                 gtk_tree_store_append(store, &child_iter, &iter);
1431                 gtk_tree_store_set(store, &child_iter, 0, strp, 1, hfinfo, -1);
1432         }
1433     }
1434     g_object_unref(G_OBJECT(store));
1435 }
1436 #endif /* GTK_MAJOR_VERSION < 2 */
1437
1438     range_label = gtk_label_new("Range (offset:length)");
1439     gtk_misc_set_alignment(GTK_MISC(range_label), 0.0, 0.0);
1440     gtk_box_pack_start(GTK_BOX(value_vb), range_label, FALSE, FALSE, 0);
1441
1442     range_entry = gtk_entry_new();
1443     gtk_box_pack_start(GTK_BOX(value_vb), range_entry, FALSE, FALSE, 0);
1444
1445
1446     /* button box */
1447     list_bb = dlg_button_row_new(GTK_STOCK_OK, GTK_STOCK_CANCEL, NULL);
1448         gtk_box_pack_start(GTK_BOX(main_vb), list_bb, FALSE, FALSE, 0);
1449     gtk_container_set_border_width  (GTK_CONTAINER (list_bb), 0);
1450
1451     ok_bt = OBJECT_GET_DATA(list_bb, GTK_STOCK_OK);
1452     gtk_widget_set_sensitive(ok_bt, FALSE);
1453     SIGNAL_CONNECT(ok_bt, "clicked", dfilter_expr_dlg_accept_cb, filter_te);
1454
1455     cancel_bt = OBJECT_GET_DATA(list_bb, GTK_STOCK_CANCEL);
1456     window_set_cancel_button(window, cancel_bt, NULL);
1457     SIGNAL_CONNECT(cancel_bt, "clicked", dfilter_expr_dlg_cancel_cb, window);
1458
1459     gtk_widget_grab_default(ok_bt);
1460
1461     /* Catch the "activate" signal on the range and value text entries,
1462        so that if the user types Return there, we act as if the "Accept"
1463        button had been selected, as happens if Return is typed if some
1464        widget that *doesn't* handle the Return key has the input focus. */
1465     dlg_set_activate(range_entry, ok_bt);
1466     dlg_set_activate(value_entry, ok_bt);
1467
1468     OBJECT_SET_DATA(window, E_DFILTER_EXPR_RELATION_LIST_KEY, relation_list);
1469     OBJECT_SET_DATA(window, E_DFILTER_EXPR_RANGE_LABEL_KEY, range_label);
1470     OBJECT_SET_DATA(window, E_DFILTER_EXPR_RANGE_ENTRY_KEY, range_entry);
1471     OBJECT_SET_DATA(window, E_DFILTER_EXPR_VALUE_LABEL_KEY, value_label);
1472     OBJECT_SET_DATA(window, E_DFILTER_EXPR_VALUE_ENTRY_KEY, value_entry);
1473     OBJECT_SET_DATA(window, E_DFILTER_EXPR_VALUE_LIST_KEY, value_list);
1474     OBJECT_SET_DATA(window, E_DFILTER_EXPR_VALUE_LIST_LABEL_KEY, value_list_label);
1475     OBJECT_SET_DATA(window, E_DFILTER_EXPR_VALUE_LIST_SW_KEY,
1476                     value_list_scrolled_win);
1477     OBJECT_SET_DATA(window, E_DFILTER_EXPR_OK_BT_KEY, ok_bt);
1478
1479 #if GTK_MAJOR_VERSION < 2
1480     /*
1481      * OK, we've finally built the entire list, complete with the row data,
1482      * and attached to the top-level widget pointers to the relevant
1483      * subwidgets, so it's safe to put the list in browse mode.
1484      */
1485     gtk_clist_set_selection_mode (GTK_CLIST(field_tree),
1486                                   GTK_SELECTION_BROWSE);
1487 #endif
1488
1489     SIGNAL_CONNECT(window, "delete_event", dfilter_expr_dlg_delete_event_cb,
1490                    window);
1491
1492     /*
1493      * Catch the "destroy" signal on our top-level window, and,
1494      * when it's destroyed, disconnect the signal we'll be
1495      * connecting below.
1496      */
1497     SIGNAL_CONNECT(window, "destroy", dfilter_expr_dlg_destroy_cb, filter_te);
1498
1499     /*
1500      * Catch the "destroy" signal on the text entry widget to which
1501      * we're attached; if it's destroyed, we should destroy ourselves
1502      * as well.
1503      */
1504     SIGNAL_CONNECT(filter_te, "destroy", dfilter_expr_dlg_cancel_cb, window);
1505
1506     gtk_widget_show_all(window);
1507     window_present(window);
1508
1509     return window;
1510 }