Added two new arguments to epan_init() and proto_init() to
[obnox/wireshark/wip.git] / gtk / capture_dlg.c
1 /* capture_dlg.c
2  * Routines for packet capture windows
3  *
4  * $Id: capture_dlg.c,v 1.37 2001/01/28 23:56:29 guy Exp $
5  *
6  * Ethereal - Network traffic analyzer
7  * By Gerald Combs <gerald@zing.org>
8  * Copyright 1998 Gerald Combs
9  *
10  * 
11  * This program is free software; you can redistribute it and/or
12  * modify it under the terms of the GNU General Public License
13  * as published by the Free Software Foundation; either version 2
14  * of the License, or (at your option) any later version.
15  * 
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  * GNU General Public License for more details.
20  * 
21  * You should have received a copy of the GNU General Public License
22  * along with this program; if not, write to the Free Software
23  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
24  */
25
26
27 #ifdef HAVE_CONFIG_H
28 # include "config.h"
29 #endif
30
31 #ifdef HAVE_LIBPCAP
32
33 #include <stdlib.h>
34 #include <stdio.h>
35 #include <string.h>
36 #include <gtk/gtk.h>
37
38 #ifdef HAVE_UNISTD_H
39 #include <unistd.h>
40 #endif
41
42 #include <time.h>
43
44 #include <pcap.h>
45
46 #ifdef NEED_SNPRINTF_H
47 # include "snprintf.h"
48 #endif
49
50 #include "capture.h"
51 #include "globals.h"
52 #include "resolv.h"
53 #include "main.h"
54 #include "ui_util.h"
55 #include "capture_dlg.h"
56 #include "filter_prefs.h"
57 #include "simple_dialog.h"
58 #include "dlg_utils.h"
59 #include "util.h"
60
61 /* Capture callback data keys */
62 #define E_CAP_IFACE_KEY       "cap_iface"
63 #define E_CAP_FILT_KEY        "cap_filter_te"
64 #define E_CAP_FILE_TE_KEY     "cap_file_te"
65 #define E_CAP_COUNT_KEY       "cap_count"
66 #define E_CAP_SNAP_KEY        "cap_snap"
67 #define E_CAP_PROMISC_KEY     "cap_promisc"
68 #define E_CAP_SYNC_KEY        "cap_sync"
69 #define E_CAP_AUTO_SCROLL_KEY "cap_auto_scroll"
70 #define E_CAP_RESOLVE_KEY     "cap_resolve"
71
72 #define E_FS_CALLER_PTR_KEY       "fs_caller_ptr"
73 #define E_FILE_SEL_DIALOG_PTR_KEY "file_sel_dialog_ptr"
74
75 static void
76 capture_prep_file_cb(GtkWidget *w, gpointer te);
77
78 static void
79 cap_prep_fs_ok_cb(GtkWidget *w, gpointer data);
80
81 static void
82 cap_prep_fs_cancel_cb(GtkWidget *w, gpointer data);
83
84 static void
85 cap_prep_fs_destroy_cb(GtkWidget *win, gpointer data);
86
87 static void
88 capture_prep_ok_cb(GtkWidget *ok_bt, gpointer parent_w);
89
90 static void
91 capture_prep_close_cb(GtkWidget *close_bt, gpointer parent_w);
92
93 static void
94 capture_prep_destroy_cb(GtkWidget *win, gpointer user_data);
95
96 void
97 capture_stop_cb(GtkWidget *w, gpointer d)
98 {
99     capture_stop();
100 }
101
102 /*
103  * Keep a static pointer to the current "Capture Preferences" window, if
104  * any, so that if somebody tries to do "Capture:Start" while there's
105  * already a "Capture Preferences" window up, we just pop up the existing
106  * one, rather than creating a new one.
107  */
108 static GtkWidget *cap_open_w;
109
110 void
111 capture_prep_cb(GtkWidget *w, gpointer d)
112 {
113   GtkWidget     *if_cb, *if_lb,
114                 *count_lb, *count_cb, *main_vb,
115                 *filter_bt, *filter_te,
116                 *file_bt, *file_te,
117                 *caplen_hb, *table,
118                 *bbox, *ok_bt, *cancel_bt, *snap_lb,
119                 *snap_sb, *promisc_cb, *sync_cb, *auto_scroll_cb, *resolv_cb;
120   GtkAccelGroup *accel_group;
121   GtkAdjustment *adj;
122   GList         *if_list, *count_list = NULL;
123   gchar         *count_item1 = "0 (Infinite)", count_item2[16];
124   int           err;
125   char          err_str[PCAP_ERRBUF_SIZE];
126
127   if (cap_open_w != NULL) {
128     /* There's already a "Capture Preferences" dialog box; reactivate it. */
129     reactivate_window(cap_open_w);
130     return;
131   }
132
133   if_list = get_interface_list(&err, err_str);
134   if (if_list == NULL && err == CANT_GET_INTERFACE_LIST) {
135     simple_dialog(ESD_TYPE_WARN, NULL, "Can't get list of interfaces: %s",
136                         err_str);
137   }
138   
139   cap_open_w = dlg_window_new("Ethereal: Capture Preferences");
140   gtk_signal_connect(GTK_OBJECT(cap_open_w), "destroy",
141         GTK_SIGNAL_FUNC(capture_prep_destroy_cb), NULL);
142
143   /* Accelerator group for the accelerators (or, as they're called in
144      Windows and, I think, in Motif, "mnemonics"; Alt+<key> is a mnemonic,
145      Ctrl+<key> is an accelerator). */
146   accel_group = gtk_accel_group_new();
147   gtk_window_add_accel_group(GTK_WINDOW(cap_open_w), accel_group);
148   
149   main_vb = gtk_vbox_new(FALSE, 3);
150   gtk_container_border_width(GTK_CONTAINER(main_vb), 5);
151   gtk_container_add(GTK_CONTAINER(cap_open_w), main_vb);
152   gtk_widget_show(main_vb);
153   
154   /* Table : container of the first 4 rows */
155   table = gtk_table_new (4, 2, FALSE);
156   gtk_table_set_row_spacings(GTK_TABLE (table), 5);
157   gtk_table_set_col_spacings(GTK_TABLE (table), 5);
158   gtk_container_add(GTK_CONTAINER(main_vb), table);
159   gtk_widget_show(table);
160
161   /* Interface row */
162   
163   if_lb = gtk_label_new("Interface:");
164   gtk_table_attach_defaults(GTK_TABLE(table), if_lb, 0, 1, 0, 1);
165   gtk_widget_show(if_lb);
166   
167   if_cb = gtk_combo_new();
168   if (if_list != NULL)
169     gtk_combo_set_popdown_strings(GTK_COMBO(if_cb), if_list);
170   if (cfile.iface)
171     gtk_entry_set_text(GTK_ENTRY(GTK_COMBO(if_cb)->entry), cfile.iface);
172   else if (if_list)
173     gtk_entry_set_text(GTK_ENTRY(GTK_COMBO(if_cb)->entry), if_list->data);
174   gtk_table_attach_defaults(GTK_TABLE(table), if_cb, 1, 2, 0, 1);
175   gtk_widget_show(if_cb);
176   
177   free_interface_list(if_list);
178
179   /* Count row */
180   
181   count_lb = gtk_label_new("Count:");
182   gtk_table_attach_defaults(GTK_TABLE(table), count_lb, 0, 1, 1, 2);
183   gtk_widget_show(count_lb);
184   
185   count_list = g_list_append(count_list, count_item1);
186   if (cfile.count) {
187     snprintf(count_item2, 15, "%d", cfile.count);
188     count_list = g_list_append(count_list, count_item2);
189   }
190
191   count_cb = gtk_combo_new();
192   gtk_combo_set_popdown_strings(GTK_COMBO(count_cb), count_list);
193   gtk_table_attach_defaults(GTK_TABLE(table), count_cb, 1, 2, 1, 2);
194   gtk_widget_show(count_cb);
195
196   while (count_list)
197     count_list = g_list_remove_link(count_list, count_list);
198
199   /* Filter row */
200   
201   filter_bt = gtk_button_new_with_label("Filter:");
202   gtk_signal_connect(GTK_OBJECT(filter_bt), "clicked",
203     GTK_SIGNAL_FUNC(capture_filter_construct_cb), NULL);
204   gtk_table_attach_defaults(GTK_TABLE(table), filter_bt, 0, 1, 2, 3);
205   gtk_widget_show(filter_bt);
206   
207   filter_te = gtk_entry_new();
208   if (cfile.cfilter) gtk_entry_set_text(GTK_ENTRY(filter_te), cfile.cfilter);
209   gtk_object_set_data(GTK_OBJECT(filter_bt), E_FILT_TE_PTR_KEY, filter_te);
210   gtk_table_attach_defaults(GTK_TABLE(table), filter_te, 1, 2, 2, 3);
211   gtk_widget_show(filter_te);
212   
213   /* File row */
214   
215   file_bt = gtk_button_new_with_label("File:");
216   gtk_table_attach_defaults(GTK_TABLE(table), file_bt, 0, 1, 3, 4);
217   gtk_widget_show(file_bt);
218   
219   file_te = gtk_entry_new();
220   gtk_table_attach_defaults(GTK_TABLE(table), file_te, 1, 2, 3, 4);
221   gtk_widget_show(file_te);
222
223   gtk_signal_connect(GTK_OBJECT(file_bt), "clicked",
224     GTK_SIGNAL_FUNC(capture_prep_file_cb), GTK_OBJECT(file_te));
225
226   /* Misc row: Capture file checkbox and snap spinbutton */
227   caplen_hb = gtk_hbox_new(FALSE, 3);
228   gtk_container_add(GTK_CONTAINER(main_vb), caplen_hb);
229   gtk_widget_show(caplen_hb);
230
231   snap_lb = gtk_label_new("Capture length");
232   gtk_misc_set_alignment(GTK_MISC(snap_lb), 0, 0.5);
233   gtk_box_pack_start(GTK_BOX(caplen_hb), snap_lb, FALSE, FALSE, 6);
234   gtk_widget_show(snap_lb);
235
236   adj = (GtkAdjustment *) gtk_adjustment_new((float) cfile.snap,
237     MIN_PACKET_SIZE, WTAP_MAX_PACKET_SIZE, 1.0, 10.0, 0.0);
238   snap_sb = gtk_spin_button_new (adj, 0, 0);
239   gtk_spin_button_set_wrap (GTK_SPIN_BUTTON (snap_sb), TRUE);
240   gtk_widget_set_usize (snap_sb, 80, 0);
241   gtk_box_pack_start (GTK_BOX(caplen_hb), snap_sb, FALSE, FALSE, 3); 
242   gtk_widget_show(snap_sb);
243   
244   promisc_cb = dlg_check_button_new_with_label_with_mnemonic(
245                 "Capture packets in _promiscuous mode", accel_group);
246   gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(promisc_cb), promisc_mode);
247   gtk_container_add(GTK_CONTAINER(main_vb), promisc_cb);
248   gtk_widget_show(promisc_cb);
249
250   sync_cb = dlg_check_button_new_with_label_with_mnemonic(
251                 "_Update list of packets in real time", accel_group);
252   gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(sync_cb), sync_mode);
253   gtk_container_add(GTK_CONTAINER(main_vb), sync_cb);
254   gtk_widget_show(sync_cb);
255
256   auto_scroll_cb = dlg_check_button_new_with_label_with_mnemonic(
257                 "_Automatic scrolling in live capture", accel_group);
258   gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(auto_scroll_cb), auto_scroll_live);
259   gtk_container_add(GTK_CONTAINER(main_vb), auto_scroll_cb);
260   gtk_widget_show(auto_scroll_cb);
261
262   resolv_cb = dlg_check_button_new_with_label_with_mnemonic(
263                 "Enable _name resolution", accel_group);
264   gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(resolv_cb), g_resolving_actif);
265   gtk_container_add(GTK_CONTAINER(main_vb), resolv_cb);
266   gtk_widget_show(resolv_cb);
267   
268   /* Button row: OK and cancel buttons */
269   bbox = gtk_hbutton_box_new();
270   gtk_button_box_set_layout (GTK_BUTTON_BOX (bbox), GTK_BUTTONBOX_END);
271   gtk_button_box_set_spacing(GTK_BUTTON_BOX(bbox), 5);
272   gtk_container_add(GTK_CONTAINER(main_vb), bbox);
273   gtk_widget_show(bbox);
274
275   ok_bt = gtk_button_new_with_label ("OK");
276   gtk_signal_connect(GTK_OBJECT(ok_bt), "clicked",
277     GTK_SIGNAL_FUNC(capture_prep_ok_cb), GTK_OBJECT(cap_open_w));
278   GTK_WIDGET_SET_FLAGS(ok_bt, GTK_CAN_DEFAULT);
279   gtk_box_pack_start (GTK_BOX (bbox), ok_bt, TRUE, TRUE, 0);
280   gtk_widget_grab_default(ok_bt);
281   gtk_widget_show(ok_bt);
282
283   cancel_bt = gtk_button_new_with_label ("Cancel");
284   gtk_signal_connect(GTK_OBJECT(cancel_bt), "clicked",
285     GTK_SIGNAL_FUNC(capture_prep_close_cb), GTK_OBJECT(cap_open_w));
286   GTK_WIDGET_SET_FLAGS(cancel_bt, GTK_CAN_DEFAULT);
287   gtk_box_pack_start (GTK_BOX (bbox), cancel_bt, TRUE, TRUE, 0);
288   gtk_widget_show(cancel_bt);
289
290   /* Attach pointers to needed widgets to the capture prefs window/object */
291   gtk_object_set_data(GTK_OBJECT(cap_open_w), E_CAP_IFACE_KEY, if_cb);
292   gtk_object_set_data(GTK_OBJECT(cap_open_w), E_CAP_FILT_KEY,  filter_te);
293   gtk_object_set_data(GTK_OBJECT(cap_open_w), E_CAP_FILE_TE_KEY,  file_te);
294   gtk_object_set_data(GTK_OBJECT(cap_open_w), E_CAP_COUNT_KEY, count_cb);
295   gtk_object_set_data(GTK_OBJECT(cap_open_w), E_CAP_SNAP_KEY,  snap_sb);
296   gtk_object_set_data(GTK_OBJECT(cap_open_w), E_CAP_PROMISC_KEY, promisc_cb);
297   gtk_object_set_data(GTK_OBJECT(cap_open_w), E_CAP_SYNC_KEY,  sync_cb);
298   gtk_object_set_data(GTK_OBJECT(cap_open_w), E_CAP_AUTO_SCROLL_KEY, auto_scroll_cb);
299   gtk_object_set_data(GTK_OBJECT(cap_open_w), E_CAP_RESOLVE_KEY,  resolv_cb);
300
301   /* Catch the "activate" signal on the frame number and file name text
302      entries, so that if the user types Return there, we act as if the
303      "OK" button had been selected, as happens if Return is typed if some
304      widget that *doesn't* handle the Return key has the input focus. */
305   dlg_set_activate(filter_te, ok_bt);
306   dlg_set_activate(file_te, ok_bt);
307
308   /* Catch the "key_press_event" signal in the window, so that we can catch
309      the ESC key being pressed and act as if the "Cancel" button had
310      been selected. */
311   dlg_set_cancel(cap_open_w, cancel_bt);
312
313   /* XXX - why does not
314
315      gtk_widget_grab_focus(if_cb);
316
317     give the initial focus to the "Interface" combo box?
318
319     Or should I phrase that as "why does GTK+ continually frustrate
320     attempts to make GUIs driveable from the keyboard?"  We have to
321     go catch the activate signal on every single GtkEntry widget
322     (rather than having widgets whose activate signal is *not*
323     caught not catch the Return keystroke, so that it passes on,
324     ultimately, to the window, which can activate the default
325     widget, i.e. the "OK" button); we have to catch the "key_press_event"
326     signal and have the handler check for ESC, so that we can have ESC
327     activate the "Cancel" button; in order to support Alt+<key> mnemonics
328     for buttons and the like, we may have to construct an accelerator
329     group by hand and set up the accelerators by hand (if that even
330     works - I've not tried it yet); we have to do a "gtk_widget_grab_focus()"
331     to keep some container widget from getting the initial focus, so that
332     you don't have to tab into the first widget in order to start typing
333     in it; and it now appears that you simply *can't* make a combo box
334     get the initial focus, at least not in the obvious fashion. Sigh.... */
335
336   gtk_widget_show(cap_open_w);
337 }
338
339 static void
340 capture_prep_file_cb(GtkWidget *w, gpointer file_te)
341 {
342   GtkWidget *caller = gtk_widget_get_toplevel(w);
343   GtkWidget *fs;
344
345   /* Has a file selection dialog box already been opened for that top-level
346      widget? */
347   fs = gtk_object_get_data(GTK_OBJECT(caller), E_FILE_SEL_DIALOG_PTR_KEY);
348
349   if (fs != NULL) {
350     /* Yes.  Just re-activate that dialog box. */
351     reactivate_window(fs);
352     return;
353   }
354
355   fs = gtk_file_selection_new ("Ethereal: Capture File");
356
357   gtk_object_set_data(GTK_OBJECT(fs), E_CAP_FILE_TE_KEY, file_te);
358
359   /* Set the E_FS_CALLER_PTR_KEY for the new dialog to point to our caller. */
360   gtk_object_set_data(GTK_OBJECT(fs), E_FS_CALLER_PTR_KEY, caller);
361
362   /* Set the E_FILE_SEL_DIALOG_PTR_KEY for the caller to point to us */
363   gtk_object_set_data(GTK_OBJECT(caller), E_FILE_SEL_DIALOG_PTR_KEY, fs);
364
365   /* Call a handler when the file selection box is destroyed, so we can inform
366      our caller, if any, that it's been destroyed. */
367   gtk_signal_connect(GTK_OBJECT(fs), "destroy",
368             GTK_SIGNAL_FUNC(cap_prep_fs_destroy_cb), NULL);
369
370   gtk_signal_connect (GTK_OBJECT (GTK_FILE_SELECTION(fs)->ok_button),
371     "clicked", (GtkSignalFunc) cap_prep_fs_ok_cb, fs);
372
373   /* Connect the cancel_button to destroy the widget */
374   gtk_signal_connect (GTK_OBJECT (GTK_FILE_SELECTION(fs)->cancel_button),
375     "clicked", (GtkSignalFunc) cap_prep_fs_cancel_cb, fs);
376
377   /* Catch the "key_press_event" signal in the window, so that we can catch
378      the ESC key being pressed and act as if the "Cancel" button had
379      been selected. */
380   dlg_set_cancel(fs, GTK_FILE_SELECTION(fs)->cancel_button);
381   
382   gtk_widget_show(fs);
383 }
384
385 static void
386 cap_prep_fs_ok_cb(GtkWidget *w, gpointer data)
387 {
388   gtk_entry_set_text(GTK_ENTRY(gtk_object_get_data(GTK_OBJECT(data),
389       E_CAP_FILE_TE_KEY)),
390       gtk_file_selection_get_filename (GTK_FILE_SELECTION(data)));
391   gtk_widget_destroy(GTK_WIDGET(data));
392 }
393
394 static void
395 cap_prep_fs_cancel_cb(GtkWidget *w, gpointer data)
396 {
397   gtk_widget_destroy(GTK_WIDGET(data));
398 }  
399
400 static void
401 cap_prep_fs_destroy_cb(GtkWidget *win, gpointer data)
402 {
403   GtkWidget *caller;
404
405   /* Get the widget that requested that we be popped up.
406      (It should arrange to destroy us if it's destroyed, so
407      that we don't get a pointer to a non-existent window here.) */
408   caller = gtk_object_get_data(GTK_OBJECT(win), E_FS_CALLER_PTR_KEY);
409
410   /* Tell it we no longer exist. */
411   gtk_object_set_data(GTK_OBJECT(caller), E_FILE_SEL_DIALOG_PTR_KEY, NULL);
412
413   /* Now nuke this window. */
414   gtk_grab_remove(GTK_WIDGET(win));
415   gtk_widget_destroy(GTK_WIDGET(win));
416 }
417
418 static void
419 capture_prep_ok_cb(GtkWidget *ok_bt, gpointer parent_w) {
420   GtkWidget *if_cb, *filter_te, *file_te, *count_cb, *snap_sb, *promisc_cb,
421             *sync_cb, *auto_scroll_cb, *resolv_cb;
422   gchar *if_text;
423   gchar *if_name;
424   gchar *filter_text;
425   gchar *save_file;
426
427   if_cb     = (GtkWidget *) gtk_object_get_data(GTK_OBJECT(parent_w), E_CAP_IFACE_KEY);
428   filter_te = (GtkWidget *) gtk_object_get_data(GTK_OBJECT(parent_w), E_CAP_FILT_KEY);
429   file_te   = (GtkWidget *) gtk_object_get_data(GTK_OBJECT(parent_w), E_CAP_FILE_TE_KEY);
430   count_cb  = (GtkWidget *) gtk_object_get_data(GTK_OBJECT(parent_w), E_CAP_COUNT_KEY);
431   snap_sb   = (GtkWidget *) gtk_object_get_data(GTK_OBJECT(parent_w), E_CAP_SNAP_KEY);
432   promisc_cb = (GtkWidget *) gtk_object_get_data(GTK_OBJECT(parent_w), E_CAP_PROMISC_KEY);
433   sync_cb   = (GtkWidget *) gtk_object_get_data(GTK_OBJECT(parent_w), E_CAP_SYNC_KEY);
434   auto_scroll_cb = (GtkWidget *) gtk_object_get_data(GTK_OBJECT(parent_w), E_CAP_AUTO_SCROLL_KEY);
435   resolv_cb = (GtkWidget *) gtk_object_get_data(GTK_OBJECT(parent_w), E_CAP_RESOLVE_KEY);
436
437   if_text =
438     g_strdup(gtk_entry_get_text(GTK_ENTRY(GTK_COMBO(if_cb)->entry)));
439   if_name = strtok(if_text, " \t");
440   if (if_name == NULL) {
441     simple_dialog(ESD_TYPE_CRIT, NULL,
442       "You didn't specify an interface on which to capture packets.");
443     g_free(if_name);
444     return;
445   }
446   if (cfile.iface)
447     g_free(cfile.iface);
448   cfile.iface = g_strdup(if_name);
449   g_free(if_text);
450
451   /* XXX - don't try to get clever and set "cfile.filter" to NULL if the
452      filter string is empty, as an indication that we don't have a filter
453      and thus don't have to set a filter when capturing - the version of
454      libpcap in Red Hat Linux 6.1, and versions based on later patches
455      in that series, don't bind the AF_PACKET socket to an interface
456      until a filter is set, which means they aren't bound at all if
457      no filter is set, which means no packets arrive as input on that
458      socket, which means Ethereal never sees any packets. */
459   filter_text = gtk_entry_get_text(GTK_ENTRY(filter_te));
460   if (cfile.cfilter)
461     g_free(cfile.cfilter);
462   g_assert(filter_text != NULL);
463   cfile.cfilter = g_strdup(filter_text); 
464
465   save_file = gtk_entry_get_text(GTK_ENTRY(file_te));
466   if (save_file && save_file[0]) {
467     /* User specified a file to which the capture should be written. */
468     save_file = g_strdup(save_file);
469   } else {
470     /* User didn't specify a file; save to a temporary file. */
471     save_file = NULL;
472   }
473
474   cfile.count = atoi(gtk_entry_get_text(GTK_ENTRY(GTK_COMBO(count_cb)->entry)));
475
476   cfile.snap = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(snap_sb));
477   if (cfile.snap < 1)
478     cfile.snap = WTAP_MAX_PACKET_SIZE;
479   else if (cfile.snap < MIN_PACKET_SIZE)
480     cfile.snap = MIN_PACKET_SIZE;
481
482   promisc_mode = GTK_TOGGLE_BUTTON (promisc_cb)->active;
483
484   sync_mode = GTK_TOGGLE_BUTTON (sync_cb)->active;
485
486   auto_scroll_live = GTK_TOGGLE_BUTTON (auto_scroll_cb)->active;
487
488   g_resolving_actif = GTK_TOGGLE_BUTTON (resolv_cb)->active;
489
490   gtk_widget_destroy(GTK_WIDGET(parent_w));
491
492   do_capture(save_file);
493 }
494
495 static void
496 capture_prep_close_cb(GtkWidget *close_bt, gpointer parent_w)
497 {
498   gtk_grab_remove(GTK_WIDGET(parent_w));
499   gtk_widget_destroy(GTK_WIDGET(parent_w));
500 }
501
502 static void
503 capture_prep_destroy_cb(GtkWidget *win, gpointer user_data)
504 {
505   GtkWidget *capture_prep_filter_w;
506   GtkWidget *fs;
507
508   /* Is there a filter edit/selection dialog associated with this
509      Capture Preferences dialog? */
510   capture_prep_filter_w = gtk_object_get_data(GTK_OBJECT(win), E_FILT_DIALOG_PTR_KEY);
511
512   if (capture_prep_filter_w != NULL) {
513     /* Yes.  Destroy it. */
514     gtk_widget_destroy(capture_prep_filter_w);
515   }
516
517   /* Is there a file selection dialog associated with this
518      Print File dialog? */
519   fs = gtk_object_get_data(GTK_OBJECT(win), E_FILE_SEL_DIALOG_PTR_KEY);
520
521   if (fs != NULL) {
522     /* Yes.  Destroy it. */
523     gtk_widget_destroy(fs);
524   }
525
526   /* Note that we no longer have a "Capture Preferences" dialog box. */
527   cap_open_w = NULL;
528 }
529
530 #endif /* HAVE_LIBPCAP */