From Greg Morris: add support for case-insensitive full-text searches.
[obnox/wireshark/wip.git] / gtk / find_dlg.c
1 /* find_dlg.c
2  * Routines for "find frame" window
3  *
4  * $Id: find_dlg.c,v 1.30 2003/08/05 00:01:27 guy Exp $
5  *
6  * Ethereal - Network traffic analyzer
7  * By Gerald Combs <gerald@ethereal.com>
8  * Copyright 1998 Gerald Combs
9  *
10  * This program is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU General Public License
12  * as published by the Free Software Foundation; either version 2
13  * of the License, or (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
23  */
24
25
26 #ifdef HAVE_CONFIG_H
27 # include "config.h"
28 #endif
29
30 #include <gtk/gtk.h>
31
32 #include <epan/proto.h>
33 #include <epan/dfilter/dfilter.h>
34 #include "globals.h"
35
36 #include "ui_util.h"
37 #include "find_dlg.h"
38 #include "filter_prefs.h"
39 #include "simple_dialog.h"
40 #include "dlg_utils.h"
41 #include "compat_macros.h"
42 #include "prefs.h"
43 #include "prefs_dlg.h"
44
45 /* Capture callback data keys */
46 #define E_FIND_FILT_KEY     "find_filter_te"
47 #define E_FIND_BACKWARD_KEY "find_backward"
48 #define E_FIND_HEXDATA_KEY "find_hex"
49 #define E_FIND_ASCIIDATA_KEY "find_ascii"
50 #define E_FIND_FILTERDATA_KEY "find_filter"
51 #define E_FIND_STRINGTYPE_KEY "find_string_type"
52 #define E_CASE_SEARCH_KEY "case_insensitive_search"
53
54 static gboolean case_type = TRUE;
55
56 static void
57 find_frame_ok_cb(GtkWidget *ok_bt, gpointer parent_w);
58
59 static void
60 find_frame_close_cb(GtkWidget *close_bt, gpointer parent_w);
61
62 static void
63 find_frame_destroy_cb(GtkWidget *win, gpointer user_data);
64
65 /*
66  * Keep a static pointer to the current "Find Frame" window, if any, so
67  * that if somebody tries to do "Find Frame" while there's already a
68  * "Find Frame" window up, we just pop up the existing one, rather than
69  * creating a new one.
70  */
71 static GtkWidget *find_frame_w;
72
73 void
74 find_frame_cb(GtkWidget *w _U_, gpointer d _U_)
75 {
76   GtkWidget     *main_vb, *filter_hb, *filter_bt, *filter_te,
77                 *direction_hb, *forward_rb, *backward_rb, 
78                 *hex_hb, *hex_rb, *ascii_rb, *filter_rb,
79                 *combo_hb, *combo_cb, *combo_lb,
80                 *bbox, *ok_bt, *cancel_bt, *case_cb;
81 #if GTK_MAJOR_VERSION < 2
82   GtkAccelGroup *accel_group;
83 #endif
84   GList *glist = NULL;
85   /* No Apply button, but "OK" not only sets our text widget, it
86      activates it (i.e., it causes us to do the search). */
87   static construct_args_t args = {
88         "Ethereal: Search Filter",
89         FALSE,
90         TRUE
91   };
92
93   if (find_frame_w != NULL) {
94     /* There's already a "Find Frame" dialog box; reactivate it. */
95     reactivate_window(find_frame_w);
96     return;
97   }
98
99   find_frame_w = dlg_window_new("Ethereal: Find Frame");
100   SIGNAL_CONNECT(find_frame_w, "destroy", find_frame_destroy_cb, NULL);
101
102 #if GTK_MAJOR_VERSION < 2
103   /* Accelerator group for the accelerators (or, as they're called in
104      Windows and, I think, in Motif, "mnemonics"; Alt+<key> is a mnemonic,
105      Ctrl+<key> is an accelerator). */
106   accel_group = gtk_accel_group_new();
107   gtk_window_add_accel_group(GTK_WINDOW(find_frame_w), accel_group);
108 #endif
109
110   /* Container for each row of widgets */
111   main_vb = gtk_vbox_new(FALSE, 3);
112   gtk_container_border_width(GTK_CONTAINER(main_vb), 5);
113   gtk_container_add(GTK_CONTAINER(find_frame_w), main_vb);
114   gtk_widget_show(main_vb);
115
116   /* Filter row */
117   filter_hb = gtk_hbox_new(FALSE, 3);
118   gtk_container_add(GTK_CONTAINER(main_vb), filter_hb);
119   gtk_widget_show(filter_hb);
120
121   filter_bt = gtk_button_new_with_label("Filter:");
122   SIGNAL_CONNECT(filter_bt, "clicked", display_filter_construct_cb, &args);
123   SIGNAL_CONNECT(filter_bt, "destroy", filter_button_destroy_cb, NULL);
124   gtk_box_pack_start(GTK_BOX(filter_hb), filter_bt, FALSE, TRUE, 0);
125   gtk_widget_show(filter_bt);
126
127   filter_te = gtk_entry_new();
128   if (cfile.sfilter) gtk_entry_set_text(GTK_ENTRY(filter_te), cfile.sfilter);
129   OBJECT_SET_DATA(filter_bt, E_FILT_TE_PTR_KEY, filter_te);
130   gtk_box_pack_start(GTK_BOX(filter_hb), filter_te, TRUE, TRUE, 0);
131   gtk_widget_show(filter_te);
132
133   /* Misc row: Forward and reverse radio buttons */
134   direction_hb = gtk_hbox_new(FALSE, 3);
135   gtk_container_add(GTK_CONTAINER(main_vb), direction_hb);
136   gtk_widget_show(direction_hb);
137
138 #if GTK_MAJOR_VERSION < 2
139   forward_rb = dlg_radio_button_new_with_label_with_mnemonic(NULL, "_Forward",
140                                                              accel_group);
141 #else
142   forward_rb = gtk_radio_button_new_with_mnemonic(NULL, "_Forward");
143 #endif
144   gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(forward_rb), !cfile.sbackward);
145   gtk_box_pack_start(GTK_BOX(direction_hb), forward_rb, TRUE, TRUE, 0);
146   gtk_widget_show(forward_rb);
147
148 #if GTK_MAJOR_VERSION < 2
149   backward_rb = dlg_radio_button_new_with_label_with_mnemonic(
150                gtk_radio_button_group(GTK_RADIO_BUTTON(forward_rb)),
151                "_Backward", accel_group);
152 #else
153   backward_rb = gtk_radio_button_new_with_mnemonic_from_widget(
154                GTK_RADIO_BUTTON(forward_rb), "_Backward");
155 #endif
156   gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(backward_rb), cfile.sbackward);
157   gtk_box_pack_start(GTK_BOX(direction_hb), backward_rb, TRUE, TRUE, 0);
158   gtk_widget_show(backward_rb);
159
160
161   /* Filter/Hex/Ascii Search */
162   /* Filter */
163   hex_hb = gtk_hbox_new(FALSE, 3);
164   gtk_container_add(GTK_CONTAINER(main_vb), hex_hb);
165   gtk_widget_show(hex_hb);
166
167 #if GTK_MAJOR_VERSION < 2
168   filter_rb = dlg_radio_button_new_with_label_with_mnemonic(NULL, "_Display Filter",
169                                                              accel_group);
170 #else
171   filter_rb = gtk_radio_button_new_with_mnemonic(NULL, "_Display Filter");
172 #endif
173   gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(filter_rb), !cfile.hex && !cfile.ascii);
174   gtk_box_pack_start(GTK_BOX(hex_hb), filter_rb, TRUE, TRUE, 0);
175   gtk_widget_show(filter_rb);
176
177   /* Hex */
178 #if GTK_MAJOR_VERSION < 2
179   hex_rb = dlg_radio_button_new_with_label_with_mnemonic(
180                gtk_radio_button_group(GTK_RADIO_BUTTON(filter_rb)),
181                "_Hex", accel_group);
182 #else
183   hex_rb = gtk_radio_button_new_with_mnemonic_from_widget(
184                GTK_RADIO_BUTTON(filter_rb), "_Hex");
185 #endif
186   gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(hex_rb), cfile.hex);
187   gtk_box_pack_start(GTK_BOX(hex_hb), hex_rb, TRUE, TRUE, 0);
188   gtk_widget_show(hex_rb);
189
190   /* ASCII Search */
191
192 #if GTK_MAJOR_VERSION < 2
193   ascii_rb = dlg_radio_button_new_with_label_with_mnemonic(
194                gtk_radio_button_group(GTK_RADIO_BUTTON(filter_rb)),
195                "_String", accel_group);
196 #else
197   ascii_rb = gtk_radio_button_new_with_mnemonic_from_widget(
198                GTK_RADIO_BUTTON(filter_rb), "_String");
199 #endif
200   gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(ascii_rb), cfile.ascii);
201   gtk_box_pack_start(GTK_BOX(hex_hb), ascii_rb, TRUE, TRUE, 0);
202   gtk_widget_show(ascii_rb);
203
204   /* String Type Selection Dropdown Box */
205   combo_hb = gtk_hbox_new(FALSE, 3);
206   gtk_container_add(GTK_CONTAINER(main_vb), combo_hb);
207   gtk_widget_show(combo_hb);
208   /* Create Label */
209   combo_lb = gtk_label_new("Find String Type:");
210   gtk_box_pack_start(GTK_BOX(combo_hb), combo_lb, FALSE, FALSE, 6);
211   gtk_widget_show(combo_lb);
212   /* Create Combo Box */
213   combo_cb = gtk_combo_new();
214
215   glist = g_list_append(glist, "ASCII Unicode & Non-Unicode");
216   glist = g_list_append(glist, "ASCII Non-Unicode");
217   glist = g_list_append(glist, "ASCII Unicode");
218   glist = g_list_append(glist, "EBCDIC");
219
220   gtk_combo_set_popdown_strings(GTK_COMBO(combo_cb), glist);
221   gtk_container_add(GTK_CONTAINER(main_vb), combo_cb);
222   gtk_widget_show(combo_cb);
223
224 #if GTK_MAJOR_VERSION < 2
225   case_cb = dlg_check_button_new_with_label_with_mnemonic(
226                 "Case Insensitive Search", accel_group);
227 #else
228   case_cb = gtk_check_button_new_with_mnemonic(
229                 "Case Insensitive Search");
230 #endif
231   gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(case_cb),
232                 case_type);
233   gtk_container_add(GTK_CONTAINER(main_vb), case_cb);
234   gtk_widget_show(case_cb);
235
236   /* Button row: OK and cancel buttons */
237   bbox = gtk_hbutton_box_new();
238   gtk_button_box_set_layout (GTK_BUTTON_BOX (bbox), GTK_BUTTONBOX_END);
239   gtk_button_box_set_spacing(GTK_BUTTON_BOX(bbox), 5);
240   gtk_container_add(GTK_CONTAINER(main_vb), bbox);
241   gtk_widget_show(bbox);
242
243 #if GTK_MAJOR_VERSION < 2
244   ok_bt = gtk_button_new_with_label ("OK");
245 #else
246   ok_bt = gtk_button_new_from_stock(GTK_STOCK_OK);
247 #endif
248   SIGNAL_CONNECT(ok_bt, "clicked", find_frame_ok_cb, find_frame_w);
249   GTK_WIDGET_SET_FLAGS(ok_bt, GTK_CAN_DEFAULT);
250   gtk_box_pack_start (GTK_BOX (bbox), ok_bt, TRUE, TRUE, 0);
251   gtk_widget_grab_default(ok_bt);
252   gtk_widget_show(ok_bt);
253
254 #if GTK_MAJOR_VERSION < 2
255   cancel_bt = gtk_button_new_with_label ("Cancel");
256 #else
257   cancel_bt = gtk_button_new_from_stock(GTK_STOCK_CANCEL);
258 #endif
259   SIGNAL_CONNECT(cancel_bt, "clicked", find_frame_close_cb, find_frame_w);
260   GTK_WIDGET_SET_FLAGS(cancel_bt, GTK_CAN_DEFAULT);
261   gtk_box_pack_start (GTK_BOX (bbox), cancel_bt, TRUE, TRUE, 0);
262   gtk_widget_show(cancel_bt);
263
264   /* Attach pointers to needed widgets to the capture prefs window/object */
265   OBJECT_SET_DATA(find_frame_w, E_FIND_FILT_KEY, filter_te);
266   OBJECT_SET_DATA(find_frame_w, E_FIND_BACKWARD_KEY, backward_rb);
267   OBJECT_SET_DATA(find_frame_w, E_FIND_FILTERDATA_KEY, filter_rb);
268   OBJECT_SET_DATA(find_frame_w, E_FIND_HEXDATA_KEY, hex_rb);
269   OBJECT_SET_DATA(find_frame_w, E_FIND_ASCIIDATA_KEY, ascii_rb);
270   OBJECT_SET_DATA(find_frame_w, E_FIND_STRINGTYPE_KEY, combo_cb);
271   OBJECT_SET_DATA(find_frame_w, E_CASE_SEARCH_KEY, case_cb);
272   
273
274   /* Catch the "activate" signal on the filter text entry, so that
275      if the user types Return there, we act as if the "OK" button
276      had been selected, as happens if Return is typed if some widget
277      that *doesn't* handle the Return key has the input focus. */
278   dlg_set_activate(filter_te, ok_bt);
279
280   /* Catch the "key_press_event" signal in the window, so that we can catch
281      the ESC key being pressed and act as if the "Cancel" button had
282      been selected. */
283   dlg_set_cancel(find_frame_w, cancel_bt);
284
285   /* Give the initial focus to the "Filter" entry box. */
286   gtk_widget_grab_focus(filter_te);
287
288   gtk_widget_show(find_frame_w);
289 }
290
291 static void
292 find_frame_ok_cb(GtkWidget *ok_bt _U_, gpointer parent_w)
293 {
294   GtkWidget *filter_te, *backward_rb, *hex_rb, *ascii_rb, *combo_cb, *case_cb;
295   gchar     *filter_text, *string_type;
296   dfilter_t *sfcode;
297
298   filter_te = (GtkWidget *)OBJECT_GET_DATA(parent_w, E_FIND_FILT_KEY);
299   backward_rb = (GtkWidget *)OBJECT_GET_DATA(parent_w, E_FIND_BACKWARD_KEY);
300   hex_rb = (GtkWidget *)OBJECT_GET_DATA(parent_w, E_FIND_HEXDATA_KEY);
301   ascii_rb = (GtkWidget *)OBJECT_GET_DATA(parent_w, E_FIND_ASCIIDATA_KEY);
302   combo_cb = (GtkWidget *)OBJECT_GET_DATA(parent_w, E_FIND_STRINGTYPE_KEY);
303   case_cb = (GtkWidget *) OBJECT_GET_DATA(parent_w, E_CASE_SEARCH_KEY);
304
305   filter_text = gtk_entry_get_text(GTK_ENTRY(filter_te));
306   string_type = gtk_entry_get_text(GTK_ENTRY(GTK_COMBO(combo_cb)->entry));
307
308   case_type = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(case_cb));
309
310   /*
311    * Try to compile the filter.
312    */
313   if (!dfilter_compile(filter_text, &sfcode) && !GTK_TOGGLE_BUTTON (hex_rb)->active && !GTK_TOGGLE_BUTTON (ascii_rb)->active) {
314     /* The attempt failed; report an error. */
315     simple_dialog(ESD_TYPE_CRIT, NULL, dfilter_error_msg);
316     return;
317   }
318
319   /* Was it empty? */
320   if (sfcode == NULL && !GTK_TOGGLE_BUTTON (hex_rb)->active && !GTK_TOGGLE_BUTTON (ascii_rb)->active) {
321     /* Yes - complain. */
322     simple_dialog(ESD_TYPE_CRIT, NULL,
323        "You didn't specify valid search criteria.");
324     return;
325   }
326
327   /*
328    * Remember the filter.
329    */
330   if (cfile.sfilter)
331     g_free(cfile.sfilter);
332   cfile.sfilter = g_strdup(filter_text);
333
334   cfile.sbackward = GTK_TOGGLE_BUTTON (backward_rb)->active;
335   cfile.hex = GTK_TOGGLE_BUTTON (hex_rb)->active;
336   cfile.ascii = GTK_TOGGLE_BUTTON (ascii_rb)->active;
337   cfile.ftype = g_strdup(string_type);
338
339   if (!GTK_TOGGLE_BUTTON (hex_rb)->active && !GTK_TOGGLE_BUTTON (ascii_rb)->active ) {
340       if (!find_packet(&cfile, sfcode)) {
341         /* We didn't find the packet. */
342         simple_dialog(ESD_TYPE_CRIT, NULL, "No packet matched that filter.");
343         return;
344       }
345   }
346   else
347   {
348       if (!find_ascii(&cfile, filter_text, cfile.ascii, string_type, case_type)) {
349           /* We didn't find the packet. */
350           simple_dialog(ESD_TYPE_CRIT, NULL, "No packet matched search criteria.");
351           return;
352       }
353   }
354   gtk_widget_destroy(GTK_WIDGET(parent_w));
355 }
356
357 static void
358 find_frame_close_cb(GtkWidget *close_bt _U_, gpointer parent_w)
359 {
360   gtk_grab_remove(GTK_WIDGET(parent_w));
361   gtk_widget_destroy(GTK_WIDGET(parent_w));
362 }
363
364 static void
365 find_frame_destroy_cb(GtkWidget *win _U_, gpointer user_data _U_)
366 {
367   /* Note that we no longer have a "Find Frame" dialog box. */
368   find_frame_w = NULL;
369 }
370
371 static void
372 find_previous_next(GtkWidget *w, gpointer d, gboolean sens)
373 {
374   dfilter_t *sfcode;
375
376
377   if (cfile.sfilter) {
378      if (!dfilter_compile(cfile.sfilter, &sfcode) && !cfile.hex && !cfile.ascii)
379         return;
380      if (sfcode == NULL && !cfile.hex && !cfile.ascii)
381         return;
382      cfile.sbackward = sens;
383      if (cfile.hex || cfile.ascii) 
384      {
385          find_ascii(&cfile, cfile.sfilter, cfile.ascii, cfile.ftype, case_type);
386      }
387      else 
388      {
389          find_packet(&cfile, sfcode);
390      }
391   } else
392      find_frame_cb(w, d);
393 }
394
395 void
396 find_next_cb(GtkWidget *w , gpointer d)
397 {
398   find_previous_next(w, d, FALSE);
399 }
400
401 void
402 find_previous_cb(GtkWidget *w , gpointer d)
403 {
404   find_previous_next(w, d, TRUE);
405 }