Damn the torpedos[1], commit it anyway.
[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: dfilter_expr_dlg.c,v 1.23 2001/08/21 06:39:18 guy Exp $
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 <stdlib.h>
48 #include <stdio.h>
49 #include <string.h>
50 #include <ctype.h>
51
52 #ifdef HAVE_SYS_TYPES_H
53 #include <sys/types.h>
54 #endif
55
56 #ifdef NEED_SNPRINTF_H
57 # include "snprintf.h"
58 #endif
59
60 #include "prefs.h"
61 #include "globals.h"
62 #include "gtkglobals.h"
63 #include "main.h"
64 #include "ui_util.h"
65 #include "simple_dialog.h"
66 #include "dlg_utils.h"
67 #include "proto_dlg.h"
68 #include "filter_prefs.h"
69 #include "dfilter_expr_dlg.h"
70
71 #define E_DFILTER_EXPR_TREE_KEY                 "dfilter_expr_tree"
72 #define E_DFILTER_EXPR_CURRENT_VAR_KEY          "dfilter_expr_current_var"
73 #define E_DFILTER_EXPR_RELATION_LABEL_KEY       "dfilter_expr_relation_label"
74 #define E_DFILTER_EXPR_RELATION_LIST_KEY        "dfilter_expr_relation_list"
75 #define E_DFILTER_EXPR_RANGE_LABEL_KEY          "dfilter_expr_range_label"
76 #define E_DFILTER_EXPR_RANGE_ENTRY_KEY          "dfilter_expr_range_entry"
77 #define E_DFILTER_EXPR_VALUE_LABEL_KEY          "dfilter_expr_value_label"
78 #define E_DFILTER_EXPR_VALUE_ENTRY_KEY          "dfilter_expr_value_entry"
79 #define E_DFILTER_EXPR_VALUE_LIST_KEY           "dfilter_expr_value_list"
80 #define E_DFILTER_EXPR_VALUE_LIST_SW_KEY        "dfilter_expr_value_list_sw"
81 #define E_DFILTER_EXPR_ACCEPT_BT_KEY            "dfilter_expr_accept_bt"
82 #define E_DFILTER_EXPR_VALUE_KEY                "dfilter_expr_value"
83
84 typedef struct protocol_data {
85   char  *abbrev;
86   int   hfinfo_index;
87 } protocol_data_t;
88
89 static void show_relations(GtkWidget *relation_label, GtkWidget *relation_list,
90     ftenum_t ftype);
91 static gboolean relation_is_presence_test(const char *string);
92 static void add_relation_list(GtkWidget *relation_list, char *relation);
93 static void build_boolean_values(GtkWidget *value_list_scrolled_win,
94     GtkWidget *value_list, const true_false_string *values);
95 static void build_enum_values(GtkWidget *value_list_scrolled_win,
96     GtkWidget *value_list, const value_string *values);
97 static void add_value_list_item(GtkWidget *value_list, gchar *string,
98     gpointer data);
99 static void display_value_fields(header_field_info *hfinfo,
100     gboolean is_comparison, GtkWidget *value_label, GtkWidget *value_entry,
101     GtkWidget *value_list, GtkWidget *value_list_scrolled_win,
102     GtkWidget *range_label, GtkWidget *range_entry);
103
104 /*
105  * Note that this is called every time the user clicks on an item,
106  * whether it is already selected or not.
107  */
108 static void
109 field_select_row_cb(GtkWidget *tree, GList *node, gint column,
110     gpointer user_data)
111 {
112         GtkWidget *window = gtk_widget_get_toplevel(tree);
113         GtkWidget *relation_label = gtk_object_get_data(GTK_OBJECT(window),
114             E_DFILTER_EXPR_RELATION_LABEL_KEY);
115         GtkWidget *relation_list = gtk_object_get_data(GTK_OBJECT(window),
116             E_DFILTER_EXPR_RELATION_LIST_KEY);
117         GtkWidget *range_label = gtk_object_get_data(GTK_OBJECT(window),
118             E_DFILTER_EXPR_RANGE_LABEL_KEY);
119         GtkWidget *range_entry = gtk_object_get_data(GTK_OBJECT(window),
120             E_DFILTER_EXPR_RANGE_ENTRY_KEY);
121         GtkWidget *value_label = gtk_object_get_data(GTK_OBJECT(window),
122             E_DFILTER_EXPR_VALUE_LABEL_KEY);
123         GtkWidget *value_entry = gtk_object_get_data(GTK_OBJECT(window),
124             E_DFILTER_EXPR_VALUE_ENTRY_KEY);
125         GtkWidget *value_list = gtk_object_get_data(GTK_OBJECT(window),
126             E_DFILTER_EXPR_VALUE_LIST_KEY);
127         GtkWidget *value_list_scrolled_win = gtk_object_get_data(GTK_OBJECT(window),
128             E_DFILTER_EXPR_VALUE_LIST_SW_KEY);
129         GtkWidget *accept_bt = gtk_object_get_data(GTK_OBJECT(window),
130             E_DFILTER_EXPR_ACCEPT_BT_KEY);
131         header_field_info *hfinfo, *cur_hfinfo;
132         const char *value_type;
133         char value_label_string[1024+1];        /* XXX - should be large enough */
134
135         hfinfo = gtk_ctree_node_get_row_data(GTK_CTREE(tree),
136             GTK_CTREE_NODE(node));
137
138         /*
139          * What was the item that was last selected?
140          */
141         cur_hfinfo = gtk_object_get_data(GTK_OBJECT(window),
142             E_DFILTER_EXPR_CURRENT_VAR_KEY);
143         if (cur_hfinfo == hfinfo) {
144                 /*
145                  * It's still selected; no need to change anything.
146                  */
147                 return;
148         }
149
150         /*
151          * Mark it as currently selected.
152          */
153         gtk_object_set_data(GTK_OBJECT(window), E_DFILTER_EXPR_CURRENT_VAR_KEY,
154             hfinfo);
155
156         show_relations(relation_label, relation_list, hfinfo->type);
157
158         /*
159          * Set the label for the value to indicate what type of value
160          * it is.
161          */
162         value_type = ftype_pretty_name(hfinfo->type);
163         if (value_type != NULL) {
164                 /*
165                  * Indicate what type of value it is.
166                  */
167                 snprintf(value_label_string, sizeof value_label_string,
168                     "Value (%s)", value_type);
169                 gtk_label_set_text(GTK_LABEL(value_label), value_label_string);
170         }
171
172         /*
173          * Clear the entry widget for the value, as whatever
174          * was there before doesn't apply.
175          */
176         gtk_entry_set_text(GTK_ENTRY(value_entry), "");
177
178         switch (hfinfo->type) {
179
180         case FT_BOOLEAN:
181                 /*
182                  * The list of values should be the strings for "true"
183                  * and "false"; show them in the value list.
184                  */
185                 build_boolean_values(value_list_scrolled_win, value_list,
186                     hfinfo->strings);
187                 break;
188
189         case FT_UINT8:
190         case FT_UINT16:
191         case FT_UINT24:
192         case FT_UINT32:
193         case FT_INT8:
194         case FT_INT16:
195         case FT_INT24:
196         case FT_INT32:
197                 /*
198                  * If this has a value_string table associated with it,
199                  * fill up the list of values, otherwise clear the list
200                  * of values.
201                  */
202                 if (hfinfo->strings != NULL) {
203                         build_enum_values(value_list_scrolled_win, value_list,
204                             hfinfo->strings);
205                 } else
206                         gtk_list_clear_items(GTK_LIST(value_list), 0, -1);
207                 break;
208
209         default:
210                 /*
211                  * Clear the list of values.
212                  */
213                 gtk_list_clear_items(GTK_LIST(value_list), 0, -1);
214                 break;
215         }
216
217         /*
218          * Display various items for the value, as appropriate.
219          * The relation we start out with is never a comparison.
220          */
221         display_value_fields(hfinfo, FALSE, value_label, value_entry,
222             value_list, value_list_scrolled_win, range_label, range_entry);
223
224         /*
225          * XXX - in browse mode, there always has to be something
226          * selected, so this should always be sensitive.
227          */
228         gtk_widget_set_sensitive(accept_bt, TRUE);
229 }
230
231 static void
232 show_relations(GtkWidget *relation_label, GtkWidget *relation_list,
233     ftenum_t ftype)
234 {
235         /*
236          * Clear out the currently displayed list of relations.
237          */
238         gtk_list_clear_items(GTK_LIST(relation_list), 0, -1);
239
240         /*
241          * Add the supported relations.
242          */
243         add_relation_list(relation_list, "is present");
244         if (ftype_can_eq(ftype) ||
245             (ftype_can_slice(ftype) && ftype_can_eq(FT_BYTES)))
246                 add_relation_list(relation_list, "==");
247         if (ftype_can_ne(ftype) ||
248             (ftype_can_slice(ftype) && ftype_can_ne(FT_BYTES)))
249                 add_relation_list(relation_list, "!=");
250         if (ftype_can_gt(ftype) ||
251             (ftype_can_slice(ftype) && ftype_can_gt(FT_BYTES)))
252                 add_relation_list(relation_list, ">");
253         if (ftype_can_lt(ftype) ||
254             (ftype_can_slice(ftype) && ftype_can_lt(FT_BYTES)))
255                 add_relation_list(relation_list, "<");
256         if (ftype_can_ge(ftype) ||
257             (ftype_can_slice(ftype) && ftype_can_ge(FT_BYTES)))
258                 add_relation_list(relation_list, ">=");
259         if (ftype_can_le(ftype) ||
260             (ftype_can_slice(ftype) && ftype_can_le(FT_BYTES)))
261                 add_relation_list(relation_list, "<=");
262
263         /*
264          * And show the list.
265          */
266         gtk_widget_show(relation_label);
267         gtk_widget_show(relation_list);
268 }
269
270 /*
271  * Given a string that represents a test to be made on a field, returns
272  * TRUE if it tests for the field's presence, FALSE otherwise.
273  */
274 static gboolean
275 relation_is_presence_test(const char *string)
276 {
277         return (strcmp(string, "is present") == 0);
278 }
279
280 static void
281 add_relation_list(GtkWidget *relation_list, char *relation)
282 {
283         GtkWidget *label, *item;
284
285         label = gtk_label_new(relation);
286         item = gtk_list_item_new();
287
288         gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
289         gtk_container_add(GTK_CONTAINER(item), label);
290         gtk_widget_show(label);
291         gtk_container_add(GTK_CONTAINER(relation_list), item);
292         gtk_widget_show(item);
293 }
294
295 static void
296 relation_list_sel_cb(GtkList *relation_list, GtkWidget *child,
297     gpointer user_data)
298 {
299         GtkWidget *window = gtk_widget_get_toplevel(GTK_WIDGET(relation_list));
300         GtkWidget *range_label = gtk_object_get_data(GTK_OBJECT(window),
301             E_DFILTER_EXPR_RANGE_LABEL_KEY);
302         GtkWidget *range_entry = gtk_object_get_data(GTK_OBJECT(window),
303             E_DFILTER_EXPR_RANGE_ENTRY_KEY);
304         GtkWidget *value_label = gtk_object_get_data(GTK_OBJECT(window),
305             E_DFILTER_EXPR_VALUE_LABEL_KEY);
306         GtkWidget *value_entry = gtk_object_get_data(GTK_OBJECT(window),
307             E_DFILTER_EXPR_VALUE_ENTRY_KEY);
308         GtkWidget *value_list = gtk_object_get_data(GTK_OBJECT(window),
309             E_DFILTER_EXPR_VALUE_LIST_KEY);
310         GtkWidget *value_list_scrolled_win = gtk_object_get_data(GTK_OBJECT(window),
311             E_DFILTER_EXPR_VALUE_LIST_SW_KEY);
312         header_field_info *hfinfo = gtk_object_get_data(GTK_OBJECT(window),
313             E_DFILTER_EXPR_CURRENT_VAR_KEY);
314         GList *sl;
315         GtkWidget *item, *item_label;
316         gchar *item_str;
317
318         /*
319          * What's the relation?
320          */
321         sl = GTK_LIST(relation_list)->selection;
322         item = GTK_WIDGET(sl->data);
323         item_label = GTK_BIN(item)->child;
324         gtk_label_get(GTK_LABEL(item_label), &item_str);
325
326         /*
327          * Update the display of various items for the value, as appropriate.
328          */
329         display_value_fields(hfinfo,
330             !relation_is_presence_test(item_str),
331             value_label, value_entry, value_list, value_list_scrolled_win,
332             range_label, range_entry);
333 }
334
335 static void
336 build_boolean_values(GtkWidget *value_list_scrolled_win, GtkWidget *value_list,
337     const true_false_string *values)
338 {
339         static const true_false_string true_false = { "True", "False" };
340
341         /*
342          * Clear out the items for the list, and put in the names
343          * from the value_string list.
344          */
345         gtk_list_clear_items(GTK_LIST(value_list), 0, -1);
346
347         /*
348          * Put the list in single mode, so we don't get any selection
349          * events while we're building it (i.e., so we don't get any
350          * on a list item BEFORE WE GET TO SET THE DATA FOR THE LIST
351          * ITEM SO THAT THE HANDLER CAN HANDLE IT).
352          */
353         gtk_list_set_selection_mode(GTK_LIST(value_list), GTK_SELECTION_SINGLE);
354
355         /*
356          * Build the list.
357          */
358         if (values == NULL)
359                 values = &true_false;
360         add_value_list_item(value_list, values->true_string, (gpointer)values);
361         add_value_list_item(value_list, values->false_string, NULL);
362
363         /*
364          * OK, we're done, so we can finally put it in browse mode.
365          * Select the first item, so that the user doesn't have to, under
366          * the assumption that they're most likely to test if something
367          * is true, not false.
368          */
369         gtk_list_set_selection_mode(GTK_LIST(value_list), GTK_SELECTION_BROWSE);
370         gtk_list_select_item(GTK_LIST(value_list), 0);
371
372         gtk_widget_show_all(value_list_scrolled_win);
373 }
374
375 static void
376 build_enum_values(GtkWidget *value_list_scrolled_win, GtkWidget *value_list,
377     const value_string *values)
378 {
379         /*
380          * Clear out the items for the list, and put in the names
381          * from the value_string list.
382          */
383         gtk_list_clear_items(GTK_LIST(value_list), 0, -1);
384
385         /*
386          * Put the list in single mode, so we don't get any selection
387          * events while we're building it (i.e., so we don't get any
388          * on a list item BEFORE WE GET TO SET THE DATA FOR THE LIST
389          * ITEM SO THAT THE HANDLER CAN HANDLE IT).
390          */
391         gtk_list_set_selection_mode(GTK_LIST(value_list), GTK_SELECTION_SINGLE);
392
393         /*
394          * Build the list.
395          */
396         while (values->strptr != NULL) {
397                 add_value_list_item(value_list, values->strptr,
398                     (gpointer)values);
399                 values++;
400         }
401
402         /*
403          * OK, we're done, so we can finally put it in browse mode.
404          */
405         gtk_list_set_selection_mode(GTK_LIST(value_list), GTK_SELECTION_BROWSE);
406 }
407
408 static void
409 add_value_list_item(GtkWidget *value_list, gchar *string, gpointer data)
410 {
411         GtkWidget *label, *item;
412
413         label = gtk_label_new(string);
414         item = gtk_list_item_new();
415
416         gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
417         gtk_container_add(GTK_CONTAINER(item), label);
418         gtk_widget_show(label);
419         gtk_container_add(GTK_CONTAINER(value_list), item);
420         gtk_object_set_data(GTK_OBJECT(item), E_DFILTER_EXPR_VALUE_KEY, data);
421         gtk_widget_show(item);
422 }
423
424 /*
425  * Show or hide the various values fields as appropriate for the field
426  * and currently-selected relation.
427  */
428 static void
429 display_value_fields(header_field_info *hfinfo, gboolean is_comparison,
430     GtkWidget *value_label, GtkWidget *value_entry, GtkWidget *value_list,
431     GtkWidget *value_list_scrolled_win, GtkWidget *range_label,
432     GtkWidget *range_entry)
433 {
434         gboolean show_value_label = FALSE;
435
436         /*
437          * Either:
438          *
439          *      this is an FT_NONE variable, in which case you can
440          *      only check whether it's present or absent in the
441          *      protocol tree
442          *
443          * or
444          *
445          *      this is a Boolean variable, in which case you
446          *      can't specify a value to compare with, you can
447          *      only specify whether to test for the Boolean
448          *      being true or to test for it being false
449          *
450          * or
451          *
452          *      this isn't a Boolean variable, in which case you
453          *      can test for its presence in the protocol tree,
454          *      and the default relation is such a test, in
455          *      which case you don't compare with a value
456          *
457          * so we hide the value entry.
458          */
459         if (is_comparison) {
460                 /*
461                  * The relation is a comparison; display the entry for
462                  * the value with which to compare.
463                  */
464                 gtk_widget_show(value_entry);
465
466                 /*
467                  * We're showing the entry; show the label as well.
468                  */
469                 show_value_label = TRUE;
470         } else {
471                 /*
472                  * The relation isn't a comparison; there's no value with
473                  * which to compare, so don't show the entry for it.
474                  */
475                 gtk_widget_hide(value_entry);
476         }
477
478         switch (hfinfo->type) {
479
480         case FT_BOOLEAN:
481                 if (is_comparison) {
482                         /*
483                          * The relation is a comparison, so we're showing
484                          * an entry for the value with which to compare;
485                          * show the list of names for values as well.
486                          * (The list of values contains the strings for
487                          * "true" and "false".)
488                          */
489                         gtk_widget_show_all(value_list_scrolled_win);
490
491                         /*
492                          * We're showing the value list; show the label as
493                          * well.
494                          */
495                         show_value_label = TRUE;
496                 } else {
497                         /*
498                          * It's not a comparison, so we're not showing
499                          * the entry for the value; don't show the
500                          * list of names for values, either.
501                          */
502                         gtk_widget_hide_all(value_list_scrolled_win);
503                 }
504                 break;
505
506         case FT_UINT8:
507         case FT_UINT16:
508         case FT_UINT24:
509         case FT_UINT32:
510         case FT_INT8:
511         case FT_INT16:
512         case FT_INT24:
513         case FT_INT32:
514                 if (hfinfo->strings != NULL) {
515                         /*
516                          * We have a list of values to show.
517                          */
518                         if (is_comparison) {
519                                 /*
520                                  * The relation is a comparison, so we're
521                                  * showing an entry for the value with
522                                  * which to compare; show the list of
523                                  * names for values as well.
524                                  */
525                                 gtk_widget_show_all(value_list_scrolled_win);
526
527                                 /*
528                                  * We're showing the entry; show the label
529                                  * as well.
530                                  */
531                                 show_value_label = TRUE;
532                         } else {
533                                 /*
534                                  * It's not a comparison, so we're not showing
535                                  * the entry for the value; don't show the
536                                  * list of names for values, either.
537                                  */
538                                 gtk_widget_hide_all(value_list_scrolled_win);
539                         }
540                 } else {
541                         /*
542                          * There is no list of names for values, so don't
543                          * show it.
544                          */
545                         gtk_widget_hide_all(value_list_scrolled_win);
546                 }
547                 break;
548
549         default:
550                 /*
551                  * There is no list of names for values; hide the list.
552                  */
553                 gtk_widget_hide_all(value_list_scrolled_win);
554                 break;
555         }
556
557         if (show_value_label)
558                 gtk_widget_show(value_label);
559         else
560                 gtk_widget_hide(value_label);
561
562         /*
563          * Is this a comparison, and are ranges supported by this type?
564          * If both are true, show the range stuff, otherwise hide it.
565          */
566         if (is_comparison && ftype_can_slice(hfinfo->type)) {
567                 gtk_widget_show(range_label);
568                 gtk_widget_show(range_entry);
569         } else {
570                 gtk_widget_hide(range_label);
571                 gtk_widget_hide(range_entry);
572         }
573 }
574
575 static void
576 value_list_sel_cb(GtkList *value_list, GtkWidget *child,
577     gpointer value_entry_arg)
578 {
579         GtkWidget *value_entry = value_entry_arg;
580         GtkWidget *window = gtk_widget_get_toplevel(GTK_WIDGET(value_list));
581         header_field_info *hfinfo = gtk_object_get_data(GTK_OBJECT(window),
582             E_DFILTER_EXPR_CURRENT_VAR_KEY);
583         const value_string *value;
584         char value_string[11+1];        /* long enough for 32-bit octal value */
585
586         value = gtk_object_get_data(GTK_OBJECT(child),
587             E_DFILTER_EXPR_VALUE_KEY);
588
589         /*
590          * This should either be a numeric type or a Boolean type.
591          */
592         if (hfinfo->type == FT_BOOLEAN) {
593                 /*
594                  * Boolean type; if the value key for the selected item
595                  * is non-null, it's the item for "true", otherwise it's
596                  * the item for "false".  Compare with 1 if we're
597                  * testing for "true", and compare with 0 if we're
598                  * testing for "false".
599                  */
600                 if (value != NULL)
601                         strcpy(value_string, "1");
602                 else
603                         strcpy(value_string, "0");
604         } else {
605                 /*
606                  * Numeric type; get the value corresponding to the
607                  * selected item, and display it in the base for this
608                  * field.
609                  */
610                 switch (hfinfo->display) {
611
612                 case BASE_DEC:
613                 case BASE_BIN:  /* binary - treated as decimal, for now */
614                         switch (hfinfo->type) {
615
616                         case FT_UINT8:
617                         case FT_UINT16:
618                         case FT_UINT32:
619                                 snprintf(value_string, sizeof value_string,
620                                     "%u", value->value);
621                                 break;
622
623                         case FT_INT8:
624                         case FT_INT16:
625                         case FT_INT32:
626                                 snprintf(value_string, sizeof value_string,
627                                     "%d", value->value);
628                                 break;
629
630                         default:
631                                 g_assert_not_reached();
632                         }
633                         break;
634
635                 case BASE_HEX:
636                         snprintf(value_string, sizeof value_string, "0x%x",
637                             value->value);
638                         break;
639
640                 case BASE_OCT:
641                         snprintf(value_string, sizeof value_string, "%#o",
642                             value->value);
643                         break;
644
645                 default:
646                         g_assert_not_reached();
647                 }
648         }
649         gtk_entry_set_text(GTK_ENTRY(value_entry), value_string);
650 }
651
652 static void
653 dfilter_report_bad_value(char *format, ...)
654 {
655         char error_msg_buf[1024];
656         va_list args;
657
658         va_start(args, format);
659         vsnprintf(error_msg_buf, sizeof error_msg_buf, format, args);
660         va_end(args);
661
662         simple_dialog(ESD_TYPE_CRIT | ESD_TYPE_MODAL, NULL,
663             "%s", error_msg_buf);
664 }
665
666 static void
667 dfilter_expr_dlg_accept_cb(GtkWidget *w, gpointer filter_te_arg)
668 {
669         GtkWidget *filter_te = filter_te_arg;
670         GtkWidget *window = gtk_widget_get_toplevel(w);
671         GtkWidget *relation_list = gtk_object_get_data(GTK_OBJECT(window),
672             E_DFILTER_EXPR_RELATION_LIST_KEY);
673         GtkWidget *range_entry = gtk_object_get_data(GTK_OBJECT(window),
674             E_DFILTER_EXPR_RANGE_ENTRY_KEY);
675         GtkWidget *value_entry = gtk_object_get_data(GTK_OBJECT(window),
676             E_DFILTER_EXPR_VALUE_ENTRY_KEY);
677         header_field_info *hfinfo;
678         GList *sl;
679         GtkWidget *item, *item_label;
680         gchar *item_str;
681         gchar *range_str, *stripped_range_str;
682         gchar *value_str, *stripped_value_str;
683         int pos;
684         gchar *chars;
685         ftenum_t ftype;
686         gboolean can_compare;
687         fvalue_t *fvalue;
688
689         /*
690          * Get the variable to be tested.
691          */
692         hfinfo = gtk_object_get_data(GTK_OBJECT(window),
693             E_DFILTER_EXPR_CURRENT_VAR_KEY);
694
695         /*
696          * Get the relation to use, if any.
697          */
698         if (GTK_WIDGET_VISIBLE(relation_list)) {
699                 /*
700                  * The list of relations is visible, so we can get a
701                  * relation operator from it.
702                  */
703                 sl = GTK_LIST(relation_list)->selection;
704                 item = GTK_WIDGET(sl->data);
705                 item_label = GTK_BIN(item)->child;
706                 gtk_label_get(GTK_LABEL(item_label), &item_str);
707         } else
708                 item_str = NULL;        /* no relation operator */
709
710         /*
711          * Get the range to use, if any.
712          */
713         if (GTK_WIDGET_VISIBLE(range_entry)) {
714                 range_str = g_strdup(gtk_entry_get_text(GTK_ENTRY(range_entry)));
715                 stripped_range_str = g_strstrip(range_str);
716                 if (strcmp(stripped_range_str, "") == 0) {
717                         /*
718                          * No range was specified.
719                          */
720                         g_free(range_str);
721                         range_str = NULL;
722                         stripped_range_str = NULL;
723                 }
724
725                 /*
726                  * XXX - check it for validity?
727                  */
728         } else {
729                 range_str = NULL;
730                 stripped_range_str = NULL;
731         }
732
733         /*
734          * If a range was specified, the type of the LHS of the
735          * comparison is FT_BYTES; otherwise, it's the type of the field.
736          */
737         if (range_str == NULL)
738                 ftype = hfinfo->type;
739         else
740                 ftype = FT_BYTES;
741
742         /*
743          * Make sure the relation is valid for the type in question.
744          * We may be offering relations that the type of the field
745          * can't support, because the field's type supports slicing,
746          * and the relation *is* supported on byte strings.
747          */
748         if (strcmp(item_str, "==") == 0)
749                 can_compare = ftype_can_eq(ftype);
750         else if (strcmp(item_str, "!=") == 0)
751                 can_compare = ftype_can_ne(ftype);
752         else if (strcmp(item_str, ">") == 0)
753                 can_compare = ftype_can_gt(ftype);
754         else if (strcmp(item_str, "<") == 0)
755                 can_compare = ftype_can_lt(ftype);
756         else if (strcmp(item_str, ">=") == 0)
757                 can_compare = ftype_can_ge(ftype);
758         else if (strcmp(item_str, "<=") == 0)
759                 can_compare = ftype_can_le(ftype);
760         else
761                 can_compare = TRUE;     /* not a comparison */
762         if (!can_compare) {
763                 if (range_str == NULL) {
764                         simple_dialog(ESD_TYPE_CRIT | ESD_TYPE_MODAL, NULL,
765                             "That field cannot be tested with \"%s\".",
766                             item_str);
767                 } else {
768                         simple_dialog(ESD_TYPE_CRIT | ESD_TYPE_MODAL, NULL,
769                             "Ranges of that field cannot be tested with \"%s\".",
770                             item_str);
771                 }
772                 if (range_str != NULL)
773                         g_free(range_str);
774                 return;
775         }
776
777         /*
778          * Get the value to use, if any.
779          */
780         if (GTK_WIDGET_VISIBLE(value_entry)) {
781                 value_str = g_strdup(gtk_entry_get_text(GTK_ENTRY(value_entry)));
782                 stripped_value_str = g_strstrip(value_str);
783                 if (strcmp(stripped_value_str, "") == 0) {
784                         /*
785                          * This field takes a value, but they didn't supply
786                          * one.
787                          */
788                         simple_dialog(ESD_TYPE_CRIT | ESD_TYPE_MODAL, NULL,
789                             "That field must be compared with a value, "
790                             "but you didn't specify a value with which to "
791                             "compare it.");
792                         if (range_str != NULL)
793                                 g_free(range_str);
794                         g_free(value_str);
795                         return;
796                 }
797         
798                 /*
799                  * Make sure the value is valid.
800                  *
801                  * If no range string was specified, it must be valid
802                  * for the type of the field; if a range string was
803                  * specified, must be valid for FT_BYTES.
804                  */
805                 fvalue = fvalue_from_string(ftype, stripped_value_str,
806                     dfilter_report_bad_value);
807                 if (fvalue == NULL) {
808                         /*
809                          * It's not valid.
810                          *
811                          * The dialog box was already popped up by
812                          * "dfilter_report_bad_value()".
813                          */
814                         if (range_str != NULL)
815                                 g_free(range_str);
816                         g_free(value_str);
817                         return;
818                 }
819                 fvalue_free(fvalue);
820         } else {
821                 value_str = NULL;
822                 stripped_value_str = NULL;
823         }
824
825         /*
826          * Insert the expression at the current cursor position.
827          * If there's a non-whitespace character to the left of it,
828          * insert a blank first; if there's a non-whitespace character
829          * to the right of it, insert a blank after it.
830          */
831         pos = gtk_editable_get_position(GTK_EDITABLE(filter_te));
832         chars = gtk_editable_get_chars(GTK_EDITABLE(filter_te), pos, pos + 1);
833         if (strcmp(chars, "") != 0 && !isspace((unsigned char)chars[0]))
834                 gtk_editable_insert_text(GTK_EDITABLE(filter_te), " ", 1, &pos);
835         g_free(chars);
836
837         gtk_editable_insert_text(GTK_EDITABLE(filter_te), hfinfo->abbrev,
838             strlen(hfinfo->abbrev), &pos);
839         if (range_str != NULL) {
840                 gtk_editable_insert_text(GTK_EDITABLE(filter_te), "[", 1, &pos);
841                 gtk_editable_insert_text(GTK_EDITABLE(filter_te),
842                     stripped_range_str, strlen(stripped_range_str), &pos);
843                 gtk_editable_insert_text(GTK_EDITABLE(filter_te), "]", 1, &pos);
844                 g_free(range_str);
845         }
846         if (item_str != NULL && !relation_is_presence_test(item_str)) {
847                 gtk_editable_insert_text(GTK_EDITABLE(filter_te), " ", 1, &pos);
848                 gtk_editable_insert_text(GTK_EDITABLE(filter_te), item_str,
849                     strlen(item_str), &pos);
850         }
851         if (value_str != NULL) {
852                 gtk_editable_insert_text(GTK_EDITABLE(filter_te), " ", 1, &pos);
853                 switch (hfinfo->type) {
854
855                 case FT_STRING:
856                 case FT_STRINGZ:
857                 case FT_UINT_STRING:
858                         /*
859                          * Put quotes around the string.
860                          */
861                         gtk_editable_insert_text(GTK_EDITABLE(filter_te), "\"",
862                             1, &pos);
863
864                 default:
865                         break;
866                 }
867                 gtk_editable_insert_text(GTK_EDITABLE(filter_te),
868                     stripped_value_str, strlen(stripped_value_str), &pos);
869                 switch (hfinfo->type) {
870
871                 case FT_STRING:
872                 case FT_STRINGZ:
873                 case FT_UINT_STRING:
874                         /*
875                          * Put quotes around the string.
876                          */
877                         gtk_editable_insert_text(GTK_EDITABLE(filter_te), "\"",
878                             1, &pos);
879
880                 default:
881                         break;
882                 }
883                 g_free(value_str);
884         }
885         chars = gtk_editable_get_chars(GTK_EDITABLE(filter_te), pos + 1, pos + 2);
886         if (strcmp(chars, "") != 0 && !isspace((unsigned char)chars[0]))
887                 gtk_editable_insert_text(GTK_EDITABLE(filter_te), " ", 1, &pos);
888         g_free(chars);
889
890         /*
891          * Put the cursor after the expression we just entered into
892          * the text entry widget.
893          */
894         gtk_editable_set_position(GTK_EDITABLE(filter_te), pos);
895
896         /*
897          * We're done; destroy the dialog box (which is the top-level
898          * widget for the "Accept" button).
899          */
900         gtk_widget_destroy(window);
901 }
902
903 static void
904 dfilter_expr_dlg_cancel_cb(GtkWidget *w, gpointer parent_w)
905 {
906         /*
907          * User pressed the cancel button; close the dialog box.
908          */
909         gtk_widget_destroy(GTK_WIDGET(parent_w));
910 }
911
912 static void
913 dfilter_expr_dlg_destroy_cb(GtkWidget *w, gpointer filter_te)
914 {
915         /*
916          * The dialog box is being destroyed; disconnect from the
917          * "destroy" signal on the text entry box to which we're
918          * attached, as the handler for that signal is supposed
919          * to destroy us, but we're already gone.
920          */
921         gtk_signal_disconnect_by_func(GTK_OBJECT(filter_te),
922             dfilter_expr_dlg_cancel_cb, w);
923 }
924
925 void
926 dfilter_expr_dlg_new(GtkWidget *filter_te)
927 {
928         GtkWidget *window;
929         GtkWidget *main_vb;
930         GtkWidget *hb;
931         GtkWidget *col1_vb;
932         GtkWidget *tree_label, *tree, *tree_scrolled_win;
933         GtkWidget *col2_vb;
934         GtkWidget *relation_label, *relation_list;
935         GtkWidget *range_label, *range_entry;
936         GtkWidget *value_vb;
937         GtkWidget *value_label, *value_entry, *value_list_scrolled_win, *value_list;
938         GtkWidget *list_bb, *alignment, *accept_bt, *close_bt;
939         GtkCTreeNode *protocol_node, *item_node;
940         header_field_info       *hfinfo;
941         int i, len;
942         void *cookie;
943         gchar *name;
944         GHashTable *proto_array;
945
946         window = dlg_window_new("Ethereal: Filter Expression");
947         gtk_container_set_border_width(GTK_CONTAINER(window), 5);
948
949         main_vb = gtk_vbox_new(FALSE, 5);
950         gtk_container_border_width(GTK_CONTAINER(main_vb), 5);
951         gtk_container_add(GTK_CONTAINER(window), main_vb);
952         gtk_widget_show(main_vb);
953
954         hb = gtk_hbox_new(FALSE, 5);
955         gtk_container_border_width(GTK_CONTAINER(hb), 5);
956         gtk_container_add(GTK_CONTAINER(main_vb), hb);
957         gtk_widget_show(hb);
958
959         col1_vb = gtk_vbox_new(FALSE, 5);
960         gtk_container_border_width(GTK_CONTAINER(col1_vb), 5);
961         gtk_container_add(GTK_CONTAINER(hb), col1_vb);
962         gtk_widget_show(col1_vb);
963
964         tree_label = gtk_label_new("Field name");
965         gtk_misc_set_alignment(GTK_MISC(tree_label), 0.0, 0.0);
966         gtk_box_pack_start(GTK_BOX(col1_vb), tree_label, FALSE, FALSE, 0);
967         gtk_widget_show(tree_label);
968
969         tree_scrolled_win = gtk_scrolled_window_new(NULL, NULL);
970         gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(tree_scrolled_win),
971                         GTK_POLICY_AUTOMATIC,
972                         GTK_POLICY_AUTOMATIC);
973         gtk_widget_set_usize(tree_scrolled_win, 300, 400);
974         gtk_box_pack_start(GTK_BOX(col1_vb), tree_scrolled_win, FALSE, FALSE, 0);
975         gtk_widget_show(tree_scrolled_win);
976
977         tree = gtk_ctree_new(1, 0);
978         gtk_ctree_set_line_style(GTK_CTREE(tree), GTK_CTREE_LINES_NONE);
979         gtk_signal_connect(GTK_OBJECT(tree), "tree-select-row",
980                              GTK_SIGNAL_FUNC(field_select_row_cb), tree);
981         gtk_container_add(GTK_CONTAINER(tree_scrolled_win), tree);
982
983         /*
984          * GTK's annoying CTree widget will deliver a selection event
985          * the instant you add an item to the tree, *the fact that you
986          * haven't even had time to set the item's row data nonwithstanding*.
987          *
988          * We'll put the widget into GTK_SELECTION_SINGLE mode in the
989          * hopes that it's *STOP DOING THAT*.
990          */
991         gtk_clist_set_selection_mode(GTK_CLIST(tree),
992                                       GTK_SELECTION_SINGLE);
993
994         col2_vb = gtk_vbox_new(FALSE, 5);
995         gtk_container_border_width(GTK_CONTAINER(col2_vb), 5);
996         gtk_container_add(GTK_CONTAINER(hb), col2_vb);
997         gtk_widget_show(col2_vb);
998
999         relation_label = gtk_label_new("Relation");
1000         gtk_misc_set_alignment(GTK_MISC(relation_label), 0.0, 0.0);
1001         gtk_box_pack_start(GTK_BOX(col2_vb), relation_label, FALSE, FALSE, 0);
1002
1003         relation_list = gtk_list_new();
1004         gtk_box_pack_start(GTK_BOX(col2_vb), relation_list, TRUE, TRUE, 0);
1005         gtk_list_set_selection_mode(GTK_LIST(relation_list),
1006             GTK_SELECTION_BROWSE);
1007
1008         range_label = gtk_label_new("Range (offset:length)");
1009         gtk_misc_set_alignment(GTK_MISC(range_label), 0.0, 0.0);
1010         gtk_box_pack_start(GTK_BOX(col2_vb), range_label, FALSE, FALSE, 0);
1011
1012         range_entry = gtk_entry_new();
1013         gtk_box_pack_start(GTK_BOX(col2_vb), range_entry, FALSE, FALSE, 0);
1014         
1015         /*
1016          * OK, show the relation label and range stuff as it would be
1017          * with everything turned on, so it'll request as much space
1018          * as it'll ever need, so the dialog box and widgets start out
1019          * with the right sizes.
1020          *
1021          * XXX - this doesn't work.  It *doesn't* request as much space
1022          * as it'll ever need.
1023          *
1024          * XXX - FT_UINT8 doesn't support ranges, so even if it did work,
1025          * it wouldn't work right.
1026          *
1027          * XXX - this no longer affects the range stuff, as that's
1028          * controlled both by the type and by the relational operator
1029          * selected.
1030          */
1031         show_relations(relation_label, relation_list, FT_UINT8);
1032
1033         value_vb = gtk_vbox_new(FALSE, 5);
1034         gtk_container_border_width(GTK_CONTAINER(value_vb), 5);
1035         gtk_container_add(GTK_CONTAINER(hb), value_vb);
1036         gtk_widget_show(value_vb);
1037
1038         value_label = gtk_label_new("Value");
1039         gtk_misc_set_alignment(GTK_MISC(value_label), 0.0, 0.0);
1040         gtk_box_pack_start(GTK_BOX(value_vb), value_label, FALSE, FALSE, 0);
1041         gtk_widget_show(value_label);
1042
1043         value_entry = gtk_entry_new();
1044         gtk_box_pack_start(GTK_BOX(value_vb), value_entry, FALSE, FALSE, 0);
1045         gtk_widget_show(value_entry);
1046         
1047         value_list_scrolled_win = gtk_scrolled_window_new(NULL, NULL);
1048         gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(value_list_scrolled_win),
1049                         GTK_POLICY_AUTOMATIC,
1050                         GTK_POLICY_AUTOMATIC);
1051         gtk_box_pack_start(GTK_BOX(value_vb), value_list_scrolled_win, TRUE,
1052             TRUE, 0);
1053         gtk_widget_show(value_list_scrolled_win);
1054
1055         value_list = gtk_list_new();
1056         gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(value_list_scrolled_win),
1057                                                 value_list);
1058         gtk_signal_connect(GTK_OBJECT(value_list), "select-child",
1059             GTK_SIGNAL_FUNC(value_list_sel_cb), value_entry);
1060         gtk_list_set_selection_mode(GTK_LIST(value_list), GTK_SELECTION_SINGLE);
1061         /* This remains hidden until an enumerated field is selected */
1062
1063         /*
1064          * The value stuff may be hidden or shown depending on what
1065          * relation was selected; connect to the "select-child" signal
1066          * for the relation list, so we can make that happen.
1067          */
1068         gtk_signal_connect(GTK_OBJECT(relation_list), "select-child",
1069             GTK_SIGNAL_FUNC(relation_list_sel_cb), NULL);
1070
1071         /*
1072          * Put the items in the CTree; we don't want to do that until
1073          * we've constructed the value list and set the tree's
1074          * E_DFILTER_EXPR_VALUE_LIST_KEY data to point to it, and
1075          * constructed the "Accept" button and set the tree's
1076          * E_DFILTER_EXPR_ACCEPT_BT_KEY data to point to it, so that
1077          * when the list item is "helpfully" automatically selected for us
1078          * we're ready to cope with the selection signal.
1079          */
1080
1081         /* a hash table seems excessive, but I don't see support for a
1082            sparse array in glib */
1083         proto_array = g_hash_table_new(g_direct_hash, g_direct_equal);
1084         for (i = proto_get_first_protocol(&cookie); i != -1;
1085             i = proto_get_next_protocol(&cookie)) {
1086                 hfinfo = proto_registrar_get_nth(i);
1087                 /* Create a node for the protocol, and remember it for
1088                    later use. */
1089                 name = proto_get_protocol_short_name(i);
1090                 protocol_node = gtk_ctree_insert_node(GTK_CTREE(tree),
1091                     NULL, NULL,
1092                     &name, 5,
1093                     NULL, NULL, NULL, NULL,
1094                     FALSE, FALSE);
1095                 gtk_ctree_node_set_row_data(GTK_CTREE(tree), protocol_node,
1096                     hfinfo);
1097                 g_hash_table_insert(proto_array, (gpointer)i, protocol_node);
1098         }
1099
1100         len = proto_registrar_n();
1101         for (i = 0; i < len; i++) {
1102                 /*
1103                  * If this field is a protocol, skip it - we already put
1104                  * it in above.
1105                  */
1106                 if (proto_registrar_is_protocol(i))
1107                         continue;
1108
1109                 hfinfo = proto_registrar_get_nth(i);
1110
1111                 /*
1112                  * If this field isn't at the head of the list of
1113                  * fields with this name, skip this field - all
1114                  * fields with the same name are really just versions
1115                  * of the same field stored in different bits, and
1116                  * should have the same type/radix/value list, and
1117                  * just differ in their bit masks.  (If a field isn't
1118                  * a bitfield, but can be, say, 1 or 2 bytes long,
1119                  * it can just be made FT_UINT16, meaning the
1120                  * *maximum* length is 2 bytes, and be used
1121                  * for all lengths.)
1122                  */
1123                 if (hfinfo->same_name_prev != NULL)
1124                         continue;
1125
1126                 /* Create a node for the item, and put it
1127                    under its parent protocol. */
1128                 protocol_node = g_hash_table_lookup(proto_array,
1129                                 (gpointer)proto_registrar_get_parent(i));
1130                 item_node = gtk_ctree_insert_node(GTK_CTREE(tree),
1131                     protocol_node, NULL,
1132                     &hfinfo->name, 5,
1133                     NULL, NULL, NULL, NULL,
1134                     FALSE, FALSE);
1135                 gtk_ctree_node_set_row_data(GTK_CTREE(tree),
1136                     item_node, hfinfo);
1137         }
1138
1139         g_hash_table_destroy(proto_array);
1140
1141         gtk_widget_show_all(tree);
1142
1143         list_bb = gtk_hbutton_box_new();
1144         gtk_button_box_set_layout(GTK_BUTTON_BOX(list_bb), GTK_BUTTONBOX_START);
1145         gtk_button_box_set_spacing(GTK_BUTTON_BOX(list_bb), 5);
1146         gtk_container_add(GTK_CONTAINER(main_vb), list_bb);
1147         gtk_widget_show(list_bb);
1148
1149         accept_bt = gtk_button_new_with_label("Accept");
1150         gtk_widget_set_sensitive(accept_bt, FALSE);
1151         gtk_signal_connect(GTK_OBJECT(accept_bt), "clicked",
1152             GTK_SIGNAL_FUNC(dfilter_expr_dlg_accept_cb), filter_te);
1153         GTK_WIDGET_SET_FLAGS(accept_bt, GTK_CAN_DEFAULT);
1154         alignment = gtk_alignment_new(0.0, 0.5, 1.0, 0.0);
1155         gtk_container_add(GTK_CONTAINER(alignment), accept_bt);
1156         gtk_box_pack_start(GTK_BOX(list_bb), alignment, TRUE, TRUE, 0);
1157         gtk_widget_grab_default(accept_bt);
1158         gtk_widget_show(accept_bt);
1159         gtk_widget_show(alignment);
1160
1161         close_bt = gtk_button_new_with_label("Close");
1162         gtk_signal_connect(GTK_OBJECT(close_bt), "clicked",
1163             GTK_SIGNAL_FUNC(dfilter_expr_dlg_cancel_cb), window);
1164         alignment = gtk_alignment_new(0.0, 0.5, 1.0, 0.0);
1165         gtk_container_add(GTK_CONTAINER(alignment), close_bt);
1166         gtk_box_pack_start(GTK_BOX(list_bb), alignment, TRUE, TRUE, 0);
1167         gtk_widget_show(close_bt);
1168         gtk_widget_show(alignment);
1169
1170         /* Catch the "activate" signal on the range and value text entries,
1171            so that if the user types Return there, we act as if the "Accept"
1172            button had been selected, as happens if Return is typed if some
1173            widget that *doesn't* handle the Return key has the input focus. */
1174         dlg_set_activate(range_entry, accept_bt);
1175         dlg_set_activate(value_entry, accept_bt);
1176
1177         /*
1178          * Catch the "key_press_event" signal in the window, so that we can
1179          * catch the ESC key being pressed and act as if the "Close" button
1180          * had been selected.
1181          */
1182         dlg_set_cancel(window, close_bt);
1183
1184         gtk_object_set_data(GTK_OBJECT(window),
1185             E_DFILTER_EXPR_RELATION_LABEL_KEY, relation_label);
1186         gtk_object_set_data(GTK_OBJECT(window),
1187             E_DFILTER_EXPR_RELATION_LIST_KEY, relation_list);
1188         gtk_object_set_data(GTK_OBJECT(window),
1189             E_DFILTER_EXPR_RANGE_LABEL_KEY, range_label);
1190         gtk_object_set_data(GTK_OBJECT(window),
1191             E_DFILTER_EXPR_RANGE_ENTRY_KEY, range_entry);
1192         gtk_object_set_data(GTK_OBJECT(window),
1193             E_DFILTER_EXPR_VALUE_LABEL_KEY, value_label);
1194         gtk_object_set_data(GTK_OBJECT(window),
1195             E_DFILTER_EXPR_VALUE_ENTRY_KEY, value_entry);
1196         gtk_object_set_data(GTK_OBJECT(window),
1197             E_DFILTER_EXPR_VALUE_LIST_KEY, value_list);
1198         gtk_object_set_data(GTK_OBJECT(window),
1199             E_DFILTER_EXPR_VALUE_LIST_SW_KEY, value_list_scrolled_win);
1200         gtk_object_set_data(GTK_OBJECT(window),
1201             E_DFILTER_EXPR_ACCEPT_BT_KEY, accept_bt);
1202
1203         /*
1204          * OK, we've finally built the entire list, complete with the row data,
1205          * and attached to the top-level widget pointers to the relevant
1206          * subwidgets, so it's safe to put the list in browse mode.
1207          */
1208         gtk_clist_set_selection_mode (GTK_CLIST(tree),
1209                                       GTK_SELECTION_BROWSE);
1210
1211         /*
1212          * Catch the "destroy" signal on our top-level window, and,
1213          * when it's destroyed, disconnect the signal we'll be
1214          * connecting below.
1215          */
1216         gtk_signal_connect(GTK_OBJECT(window), "destroy",
1217             GTK_SIGNAL_FUNC(dfilter_expr_dlg_destroy_cb), filter_te);
1218
1219         /*
1220          * Catch the "destroy" signal on the text entry widget to which
1221          * we're attached; if it's destroyed, we should destroy ourselves
1222          * as well.
1223          */
1224         gtk_signal_connect(GTK_OBJECT(filter_te), "destroy",
1225             GTK_SIGNAL_FUNC(dfilter_expr_dlg_cancel_cb), window);
1226
1227         gtk_widget_show(window);
1228 }