Make "finfo_selected" a member of a "capture_file" structure rather than
[obnox/wireshark/wip.git] / gtk / prefs_dlg.c
1 /* prefs_dlg.c
2  * Routines for handling preferences
3  *
4  * $Id: prefs_dlg.c,v 1.61 2003/09/24 00:47:37 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 #ifdef HAVE_CONFIG_H
26 # include "config.h"
27 #endif
28
29 #include <gtk/gtk.h>
30
31 #include <string.h>
32
33 #include <epan/filesystem.h>
34
35 #include "main.h"
36 #include <epan/packet.h>
37 #include "file.h"
38 #include "prefs.h"
39 #include "column_prefs.h"
40 #include "print.h"
41 #include "prefs_dlg.h"
42 #include "print_prefs.h"
43 #include "stream_prefs.h"
44 #include "gui_prefs.h"
45 #include "capture_prefs.h"
46 #include "nameres_prefs.h"
47 #include "ui_util.h"
48 #include "dlg_utils.h"
49 #include "simple_dialog.h"
50 #include "compat_macros.h"
51
52 #include "prefs-int.h"
53
54 #ifdef HAVE_LIBPCAP
55 #ifdef WIN32
56 #include "capture-wpcap.h"
57 #endif /* _WIN32 */
58 #endif /* HAVE_LIBPCAP */
59
60 static void     prefs_main_ok_cb(GtkWidget *, gpointer);
61 static void     prefs_main_apply_cb(GtkWidget *, gpointer);
62 static void     prefs_main_save_cb(GtkWidget *, gpointer);
63 static void     prefs_main_cancel_cb(GtkWidget *, gpointer);
64 static gboolean prefs_main_delete_cb(GtkWidget *, gpointer);
65 static void     prefs_main_destroy_cb(GtkWidget *, gpointer);
66 #if GTK_MAJOR_VERSION < 2
67 static void     prefs_tree_select_cb(GtkCTree *, GtkCTreeNode *, gint,
68                                      gpointer);
69 #else
70 static void     prefs_tree_select_cb(GtkTreeSelection *, gpointer);
71 #endif
72
73 #define E_PRINT_PAGE_KEY   "printer_options_page"
74 #define E_COLUMN_PAGE_KEY  "column_options_page"
75 #define E_STREAM_PAGE_KEY  "tcp_stream_options_page"
76 #define E_GUI_PAGE_KEY     "gui_options_page"
77 #define E_CAPTURE_PAGE_KEY "capture_options_page"
78 #define E_NAMERES_PAGE_KEY "nameres_options_page"
79 #define E_TOOLTIPS_KEY     "tooltips"
80
81 static int first_proto_prefs_page = -1;
82
83 /*
84  * Keep a static pointer to the notebook to be able to choose the
85  * displayed page.
86  */
87 static GtkWidget *notebook;
88
89 /*
90  * Keep a static pointer to the current "Preferences" window, if any, so that
91  * if somebody tries to do "Edit:Preferences" while there's already a
92  * "Preferences" window up, we just pop up the existing one, rather than
93  * creating a new one.
94  */
95 static GtkWidget *prefs_w;
96
97 /*
98  * Save the value of the preferences as of when the preferences dialog
99  * box was first popped up, so we can revert to those values if the
100  * user selects "Cancel".
101  */
102 static e_prefs saved_prefs;
103
104 struct ct_struct {
105   GtkWidget    *main_vb;
106   GtkWidget    *notebook;
107   GtkWidget    *tree;
108 #if GTK_MAJOR_VERSION < 2
109   GtkCTreeNode *node;
110 #else
111   GtkTreeIter  iter;
112 #endif
113   GtkTooltips  *tooltips;
114   gint         page;
115   gboolean     is_protocol;
116 };
117
118 static void
119 pref_show(pref_t *pref, gpointer user_data)
120 {
121   GtkWidget *main_tb = user_data;
122   const char *title;
123   char *label_string;
124   char uint_str[10+1];
125
126   /* Give this preference a label which is its title, followed by a colon,
127      and left-align it. */
128   title = pref->title;
129   label_string = g_malloc(strlen(title) + 2);
130   strcpy(label_string, title);
131   strcat(label_string, ":");
132
133   /* Save the current value of the preference, so that we can revert it if
134      the user does "Apply" and then "Cancel", and create the control for
135      editing the preference. */
136   switch (pref->type) {
137
138   case PREF_UINT:
139     pref->saved_val.uint = *pref->varp.uint;
140
141     /* XXX - there are no uint spinbuttons, so we can't use a spinbutton.
142        Even more annoyingly, even if there were, GLib doesn't define
143        G_MAXUINT - but I think ANSI C may define UINT_MAX, so we could
144        use that. */
145     switch (pref->info.base) {
146
147     case 10:
148       sprintf(uint_str, "%u", pref->saved_val.uint);
149       break;
150
151     case 8:
152       sprintf(uint_str, "%o", pref->saved_val.uint);
153       break;
154
155     case 16:
156       sprintf(uint_str, "%x", pref->saved_val.uint);
157       break;
158     }
159     pref->control = create_preference_entry(main_tb, pref->ordinal,
160                                             label_string, pref->description,
161                                             uint_str);
162     break;
163
164   case PREF_BOOL:
165     pref->saved_val.boolval = *pref->varp.boolp;
166     pref->control = create_preference_check_button(main_tb, pref->ordinal,
167                                                label_string, pref->description,
168                                                pref->saved_val.boolval);
169     break;
170
171   case PREF_ENUM:
172     pref->saved_val.enumval = *pref->varp.enump;
173     if (pref->info.enum_info.radio_buttons) {
174       /* Show it as radio buttons. */
175       pref->control = create_preference_radio_buttons(main_tb, pref->ordinal,
176                                                   label_string, pref->description,
177                                                   pref->info.enum_info.enumvals,
178                                                   pref->saved_val.enumval);
179     } else {
180       /* Show it as an option menu. */
181       pref->control = create_preference_option_menu(main_tb, pref->ordinal,
182                                          label_string, pref->description,
183                                          pref->info.enum_info.enumvals,
184                                          pref->saved_val.enumval);
185     }
186     break;
187
188   case PREF_STRING:
189     if (pref->saved_val.string != NULL)
190       g_free(pref->saved_val.string);
191     pref->saved_val.string = g_strdup(*pref->varp.string);
192     pref->control = create_preference_entry(main_tb, pref->ordinal,
193                                             label_string, pref->description,
194                                             pref->saved_val.string);
195     break;
196
197   case PREF_OBSOLETE:
198     g_assert_not_reached();
199     break;
200   }
201   g_free(label_string);
202 }
203
204 #define MAX_TREE_NODE_NAME_LEN 64
205 static void
206 module_prefs_show(module_t *module, gpointer user_data)
207 {
208   struct ct_struct *cts = user_data;
209   struct ct_struct child_cts;
210   GtkWidget        *main_vb, *main_tb, *frame;
211   gchar            label_str[MAX_TREE_NODE_NAME_LEN];
212 #if GTK_MAJOR_VERSION < 2
213   gchar            *label_ptr = label_str;
214   GtkCTreeNode     *ct_node;
215 #else
216   GtkTreeStore     *model;
217   GtkTreeIter      iter;
218 #endif
219
220   /*
221    * Add this module to the tree.
222    */
223   strcpy(label_str, module->title);
224 #if GTK_MAJOR_VERSION < 2
225   ct_node = gtk_ctree_insert_node(GTK_CTREE(cts->tree), cts->node, NULL,
226                 &label_ptr, 5, NULL, NULL, NULL, NULL, !module->is_subtree,
227                 FALSE);
228 #else
229   model = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(cts->tree)));
230   if (module->is_subtree)
231       gtk_tree_store_append(model, &iter, NULL);
232   else
233       gtk_tree_store_append(model, &iter, &cts->iter);
234 #endif
235
236   /*
237    * Is this a subtree?
238    */
239   if (module->is_subtree) {
240     /*
241      * Yes.
242      */
243
244     /* Note that there's no page attached to this item */
245 #if GTK_MAJOR_VERSION < 2
246     gtk_ctree_node_set_row_data(GTK_CTREE(cts->tree), ct_node,
247                 GINT_TO_POINTER(-1));
248 #else
249     gtk_tree_store_set(model, &iter, 0, label_str, 1, -1, -1);
250 #endif
251
252     /*
253      * Walk the subtree and attach stuff to it.
254      */
255     child_cts = *cts;
256 #if GTK_MAJOR_VERSION < 2
257     child_cts.node = ct_node;
258 #else
259     child_cts.iter = iter;
260 #endif
261     if (module == protocols_module)
262       child_cts.is_protocol = TRUE;
263     prefs_module_list_foreach(module->prefs, module_prefs_show, &child_cts);
264   } else {
265     /*
266      * No.
267      * Create a notebook page for it.
268      */
269
270     /* Frame */
271     frame = gtk_frame_new(module->title);
272     gtk_widget_show(frame);
273
274     /* Main vertical box */
275     main_vb = gtk_vbox_new(FALSE, 5);
276     gtk_container_border_width(GTK_CONTAINER(main_vb), 5);
277     gtk_container_add(GTK_CONTAINER(frame), main_vb);
278
279     /* Main table */
280     main_tb = gtk_table_new(module->numprefs, 2, FALSE);
281     gtk_box_pack_start(GTK_BOX(main_vb), main_tb, FALSE, FALSE, 0);
282     gtk_table_set_row_spacings(GTK_TABLE(main_tb), 10);
283     gtk_table_set_col_spacings(GTK_TABLE(main_tb), 15);
284     OBJECT_SET_DATA(main_tb, E_TOOLTIPS_KEY, cts->tooltips);
285
286     /* Add items for each of the preferences */
287     prefs_pref_foreach(module, pref_show, main_tb);
288
289     /* Add the page to the notebook */
290     gtk_notebook_append_page(GTK_NOTEBOOK(cts->notebook), frame, NULL);
291
292     /* Attach the page to the tree item */
293 #if GTK_MAJOR_VERSION < 2
294     gtk_ctree_node_set_row_data(GTK_CTREE(cts->tree), ct_node,
295                 GINT_TO_POINTER(cts->page));
296 #else
297     gtk_tree_store_set(model, &iter, 0, label_str, 1, cts->page, -1);
298 #endif
299
300     /* If this is the first protocol page, remember its page number */
301     if (first_proto_prefs_page == -1)
302       first_proto_prefs_page = cts->page;
303     cts->page++;
304
305     /* Show 'em what we got */
306     gtk_widget_show_all(main_vb);
307   }
308 }
309
310 void
311 prefs_cb(GtkWidget *w _U_, gpointer dummy _U_)
312 {
313   GtkWidget         *top_hb, *bbox, *prefs_nb, *ct_sb, *frame,
314                     *ok_bt, *apply_bt, *save_bt, *cancel_bt;
315   GtkWidget         *print_pg, *column_pg, *stream_pg, *gui_pg;
316 #ifdef HAVE_LIBPCAP
317   GtkWidget         *capture_pg;
318 #endif
319   GtkWidget         *nameres_pg;
320   gchar             label_str[MAX_TREE_NODE_NAME_LEN];
321   struct ct_struct  cts;
322 #if GTK_MAJOR_VERSION < 2
323   gchar             *label_ptr = label_str;
324   GtkCTreeNode      *ct_node;
325 #else
326   GtkTreeStore      *store;
327   GtkTreeSelection  *selection;
328   GtkCellRenderer   *renderer;
329   GtkTreeViewColumn *column;
330   gint              col_offset;
331   GtkTreeIter       iter;
332 #endif
333
334   if (prefs_w != NULL) {
335     /* There's already a "Preferences" dialog box; reactivate it. */
336     reactivate_window(prefs_w);
337     return;
338   }
339
340   /* Save the current preferences, so we can revert to those values
341      if the user presses "Cancel". */
342   copy_prefs(&saved_prefs, &prefs);
343
344   prefs_w = dlg_window_new("Ethereal: Preferences");
345   SIGNAL_CONNECT(prefs_w, "delete_event", prefs_main_delete_cb, NULL);
346   SIGNAL_CONNECT(prefs_w, "destroy", prefs_main_destroy_cb, NULL);
347
348   /*
349    * Unfortunately, we can't arrange that a GtkTable widget wrap an event box
350    * around a table row, so the spacing between the preference item's label
351    * and its control widgets is inactive and the tooltip doesn't pop up when
352    * the mouse is over it.
353    */
354   cts.tooltips = gtk_tooltips_new();
355
356   /* Container for each row of widgets */
357   cts.main_vb = gtk_vbox_new(FALSE, 5);
358   gtk_container_border_width(GTK_CONTAINER(cts.main_vb), 5);
359   gtk_container_add(GTK_CONTAINER(prefs_w), cts.main_vb);
360   gtk_widget_show(cts.main_vb);
361
362   /* Top row: Preferences tree and notebook */
363   top_hb = gtk_hbox_new(FALSE, 10);
364   gtk_container_add(GTK_CONTAINER(cts.main_vb), top_hb);
365   gtk_widget_show(top_hb);
366
367   /* Place a Ctree on the left for preference categories */
368   ct_sb = scrolled_window_new(NULL, NULL);
369   gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(ct_sb),
370         GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
371   gtk_container_add(GTK_CONTAINER(top_hb), ct_sb);
372   gtk_widget_show(ct_sb);
373
374 #if GTK_MAJOR_VERSION < 2
375   cts.tree = ctree_new(1, 0);
376   cts.node = NULL;
377 #else
378   store = gtk_tree_store_new(2, G_TYPE_STRING, G_TYPE_INT);
379   cts.tree = tree_view_new(GTK_TREE_MODEL(store));
380   gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(cts.tree), FALSE);
381   selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(cts.tree));
382   gtk_tree_selection_set_mode(selection, GTK_SELECTION_SINGLE);
383   renderer = gtk_cell_renderer_text_new();
384   col_offset = gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(cts.tree),
385                                                            -1, "Name", renderer,
386                                                            "text", 0, NULL);
387   column = gtk_tree_view_get_column(GTK_TREE_VIEW(cts.tree),
388                                     col_offset - 1);
389   gtk_tree_view_column_set_sizing(GTK_TREE_VIEW_COLUMN(column),
390                                   GTK_TREE_VIEW_COLUMN_AUTOSIZE);
391 #endif
392   cts.page = 0;
393   gtk_container_add(GTK_CONTAINER(ct_sb), cts.tree);
394
395 #if GTK_MAJOR_VERSION < 2
396   gtk_clist_set_column_auto_resize(GTK_CLIST(cts.tree), 0, TRUE);
397   SIGNAL_CONNECT(cts.tree, "tree-select-row", prefs_tree_select_cb, NULL);
398 #else
399   SIGNAL_CONNECT(selection, "changed", prefs_tree_select_cb, NULL);
400 #endif
401   gtk_widget_show(cts.tree);
402
403   /* A notebook widget sans tabs is used to flip between prefs */
404   notebook = prefs_nb = gtk_notebook_new();
405   gtk_notebook_set_show_tabs(GTK_NOTEBOOK(prefs_nb), FALSE);
406   gtk_notebook_set_show_border(GTK_NOTEBOOK(prefs_nb), FALSE);
407   gtk_container_add(GTK_CONTAINER(top_hb), prefs_nb);
408   gtk_widget_show(prefs_nb);
409
410   /* Printing prefs */
411   frame = gtk_frame_new("Printing");
412   gtk_widget_show(GTK_WIDGET(frame));
413   print_pg = printer_prefs_show();
414   gtk_container_add(GTK_CONTAINER(frame), print_pg);
415   OBJECT_SET_DATA(prefs_w, E_PRINT_PAGE_KEY, print_pg);
416   gtk_notebook_append_page (GTK_NOTEBOOK(prefs_nb), frame, NULL);
417   strcpy(label_str, "Printing");
418 #if GTK_MAJOR_VERSION < 2
419   ct_node = gtk_ctree_insert_node(GTK_CTREE(cts.tree), NULL, NULL,
420                 &label_ptr, 5, NULL, NULL, NULL, NULL, TRUE, TRUE);
421   gtk_ctree_node_set_row_data(GTK_CTREE(cts.tree), ct_node,
422                 GINT_TO_POINTER(cts.page));
423 #else
424   gtk_tree_store_append(store, &iter, NULL);
425   gtk_tree_store_set(store, &iter, 0, label_str, 1, cts.page, -1);
426 #endif
427   cts.page++;
428
429   /* Column prefs */
430   frame = gtk_frame_new("Columns");
431   gtk_widget_show(GTK_WIDGET(frame));
432   column_pg = column_prefs_show();
433   gtk_container_add(GTK_CONTAINER(frame), column_pg);
434   OBJECT_SET_DATA(prefs_w, E_COLUMN_PAGE_KEY, column_pg);
435   gtk_notebook_append_page (GTK_NOTEBOOK(prefs_nb), frame, NULL);
436   strcpy(label_str, "Columns");
437 #if GTK_MAJOR_VERSION < 2
438   ct_node = gtk_ctree_insert_node(GTK_CTREE(cts.tree), NULL, NULL,
439                 &label_ptr, 5, NULL, NULL, NULL, NULL, TRUE, TRUE);
440   gtk_ctree_node_set_row_data(GTK_CTREE(cts.tree), ct_node,
441                 GINT_TO_POINTER(cts.page));
442 #else
443   gtk_tree_store_append(store, &iter, NULL);
444   gtk_tree_store_set(store, &iter, 0, label_str, 1, cts.page, -1);
445 #endif
446   cts.page++;
447
448   /* TCP Streams prefs */
449   frame = gtk_frame_new("TCP Streams");
450   gtk_widget_show(GTK_WIDGET(frame));
451   stream_pg = stream_prefs_show();
452   gtk_container_add(GTK_CONTAINER(frame), stream_pg);
453   OBJECT_SET_DATA(prefs_w, E_STREAM_PAGE_KEY, stream_pg);
454   gtk_notebook_append_page (GTK_NOTEBOOK(prefs_nb), frame, NULL);
455   strcpy(label_str, "TCP Streams");
456 #if GTK_MAJOR_VERSION < 2
457   ct_node = gtk_ctree_insert_node(GTK_CTREE(cts.tree), NULL, NULL,
458                 &label_ptr, 5, NULL, NULL, NULL, NULL, TRUE, TRUE);
459   gtk_ctree_node_set_row_data(GTK_CTREE(cts.tree), ct_node,
460                 GINT_TO_POINTER(cts.page));
461 #else
462   gtk_tree_store_append(store, &iter, NULL);
463   gtk_tree_store_set(store, &iter, 0, label_str, 1, cts.page, -1);
464 #endif
465   cts.page++;
466
467   /* GUI prefs */
468   frame = gtk_frame_new("User Interface");
469   gtk_widget_show(GTK_WIDGET(frame));
470   gui_pg = gui_prefs_show();
471   gtk_container_add(GTK_CONTAINER(frame), gui_pg);
472   OBJECT_SET_DATA(prefs_w, E_GUI_PAGE_KEY, gui_pg);
473   gtk_notebook_append_page (GTK_NOTEBOOK(prefs_nb), frame, NULL);
474   strcpy(label_str, "User Interface");
475 #if GTK_MAJOR_VERSION < 2
476   ct_node = gtk_ctree_insert_node(GTK_CTREE(cts.tree), NULL, NULL,
477                 &label_ptr, 5, NULL, NULL, NULL, NULL, TRUE, TRUE);
478   gtk_ctree_node_set_row_data(GTK_CTREE(cts.tree), ct_node,
479                 GINT_TO_POINTER(cts.page));
480 #else
481   gtk_tree_store_append(store, &iter, NULL);
482   gtk_tree_store_set(store, &iter, 0, label_str, 1, cts.page, -1);
483 #endif
484   cts.page++;
485
486 #ifdef HAVE_LIBPCAP
487 #ifdef _WIN32
488   /* Is WPcap loaded? */
489   if (has_wpcap) {
490 #endif /* _WIN32 */
491   /* capture prefs */
492   frame = gtk_frame_new("Capture");
493   gtk_widget_show(GTK_WIDGET(frame));
494   capture_pg = capture_prefs_show();
495   gtk_container_add(GTK_CONTAINER(frame), capture_pg);
496   OBJECT_SET_DATA(prefs_w, E_CAPTURE_PAGE_KEY, capture_pg);
497   gtk_notebook_append_page (GTK_NOTEBOOK(prefs_nb), frame, NULL);
498   strcpy(label_str, "Capture");
499 #if GTK_MAJOR_VERSION < 2
500   ct_node = gtk_ctree_insert_node(GTK_CTREE(cts.tree), NULL, NULL,
501                 &label_ptr, 5, NULL, NULL, NULL, NULL, TRUE, TRUE);
502   gtk_ctree_node_set_row_data(GTK_CTREE(cts.tree), ct_node,
503                 GINT_TO_POINTER(cts.page));
504 #else
505   gtk_tree_store_append(store, &iter, NULL);
506   gtk_tree_store_set(store, &iter, 0, label_str, 1, cts.page, -1);
507 #endif
508   cts.page++;
509 #ifdef _WIN32
510   }
511 #endif /* _WIN32 */
512 #endif /* HAVE_LIBPCAP */
513
514   /* Name resolution prefs */
515   frame = gtk_frame_new("Name Resolution");
516   gtk_widget_show(GTK_WIDGET(frame));
517   nameres_pg = nameres_prefs_show();
518   gtk_container_add(GTK_CONTAINER(frame), nameres_pg);
519   OBJECT_SET_DATA(prefs_w, E_NAMERES_PAGE_KEY, nameres_pg);
520   gtk_notebook_append_page (GTK_NOTEBOOK(prefs_nb), frame, NULL);
521   strcpy(label_str, "Name Resolution");
522 #if GTK_MAJOR_VERSION < 2
523   ct_node = gtk_ctree_insert_node(GTK_CTREE(cts.tree), NULL, NULL,
524                 &label_ptr, 5, NULL, NULL, NULL, NULL, TRUE, TRUE);
525   gtk_ctree_node_set_row_data(GTK_CTREE(cts.tree), ct_node,
526                 GINT_TO_POINTER(cts.page));
527 #else
528   gtk_tree_store_append(store, &iter, NULL);
529   gtk_tree_store_set(store, &iter, 0, label_str, 1, cts.page, -1);
530 #endif
531   cts.page++;
532
533   /* Registered prefs */
534   cts.notebook = prefs_nb;
535   cts.is_protocol = FALSE;
536   prefs_module_list_foreach(NULL, module_prefs_show, &cts);
537
538   /* Button row: OK and cancel buttons */
539   bbox = gtk_hbutton_box_new();
540   gtk_button_box_set_layout (GTK_BUTTON_BOX (bbox), GTK_BUTTONBOX_END);
541   gtk_button_box_set_spacing(GTK_BUTTON_BOX(bbox), 5);
542   gtk_container_add(GTK_CONTAINER(cts.main_vb), bbox);
543   gtk_widget_show(bbox);
544
545 #if GTK_MAJOR_VERSION < 2
546   ok_bt = gtk_button_new_with_label ("OK");
547 #else
548   ok_bt = gtk_button_new_from_stock(GTK_STOCK_OK);
549 #endif
550   SIGNAL_CONNECT(ok_bt, "clicked", prefs_main_ok_cb, prefs_w);
551   GTK_WIDGET_SET_FLAGS(ok_bt, GTK_CAN_DEFAULT);
552   gtk_box_pack_start (GTK_BOX (bbox), ok_bt, TRUE, TRUE, 0);
553   gtk_widget_grab_default(ok_bt);
554   gtk_widget_show(ok_bt);
555
556 #if GTK_MAJOR_VERSION < 2
557   apply_bt = gtk_button_new_with_label ("Apply");
558 #else
559   apply_bt = gtk_button_new_from_stock(GTK_STOCK_APPLY);
560 #endif
561   SIGNAL_CONNECT(apply_bt, "clicked", prefs_main_apply_cb, prefs_w);
562   GTK_WIDGET_SET_FLAGS(apply_bt, GTK_CAN_DEFAULT);
563   gtk_box_pack_start(GTK_BOX (bbox), apply_bt, TRUE, TRUE, 0);
564   gtk_widget_show(apply_bt);
565
566 #if GTK_MAJOR_VERSION < 2
567   save_bt = gtk_button_new_with_label ("Save");
568 #else
569   save_bt = gtk_button_new_from_stock(GTK_STOCK_SAVE);
570 #endif
571   SIGNAL_CONNECT(save_bt, "clicked", prefs_main_save_cb, prefs_w);
572   GTK_WIDGET_SET_FLAGS(save_bt, GTK_CAN_DEFAULT);
573   gtk_box_pack_start (GTK_BOX (bbox), save_bt, TRUE, TRUE, 0);
574   gtk_widget_show(save_bt);
575
576 #if GTK_MAJOR_VERSION < 2
577   cancel_bt = gtk_button_new_with_label ("Cancel");
578 #else
579   cancel_bt = gtk_button_new_from_stock(GTK_STOCK_CANCEL);
580 #endif
581   SIGNAL_CONNECT(cancel_bt, "clicked", prefs_main_cancel_cb, prefs_w);
582   GTK_WIDGET_SET_FLAGS(cancel_bt, GTK_CAN_DEFAULT);
583   gtk_box_pack_start (GTK_BOX (bbox), cancel_bt, TRUE, TRUE, 0);
584   gtk_widget_show(cancel_bt);
585
586   /* Catch the "key_press_event" signal in the window, so that we can catch
587      the ESC key being pressed and act as if the "Cancel" button had
588      been selected. */
589   dlg_set_cancel(prefs_w, cancel_bt);
590
591   gtk_widget_show(prefs_w);
592
593 #if GTK_MAJOR_VERSION >= 2
594   g_object_unref(G_OBJECT(store));
595 #endif
596 }
597
598 static void
599 set_option_label(GtkWidget *main_tb, int table_position,
600     const gchar *label_text, const gchar *tooltip_text, GtkTooltips *tooltips)
601 {
602         GtkWidget *label;
603         GtkWidget *event_box;
604
605         label = gtk_label_new(label_text);
606         gtk_misc_set_alignment(GTK_MISC(label), 1.0, 0.5);
607         gtk_widget_show(label);
608
609         event_box = gtk_event_box_new();
610         gtk_table_attach_defaults(GTK_TABLE(main_tb), event_box, 0, 1,
611             table_position, table_position + 1);
612         if (tooltip_text != NULL && tooltips != NULL)
613                 gtk_tooltips_set_tip(tooltips, event_box, tooltip_text, NULL);
614         gtk_container_add(GTK_CONTAINER(event_box), label);
615         gtk_widget_show(event_box);
616 }
617
618 GtkWidget *
619 create_preference_check_button(GtkWidget *main_tb, int table_position,
620     const gchar *label_text, const gchar *tooltip_text, gboolean active)
621 {
622         GtkTooltips *tooltips;
623         GtkWidget *check_box;
624
625         tooltips = OBJECT_GET_DATA(main_tb, E_TOOLTIPS_KEY);
626
627         set_option_label(main_tb, table_position, label_text, tooltip_text,
628             tooltips);
629
630         check_box = gtk_check_button_new();
631         gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check_box), active);
632         gtk_table_attach_defaults(GTK_TABLE(main_tb), check_box, 1, 2,
633             table_position, table_position + 1);
634         if (tooltip_text != NULL && tooltips != NULL)
635                 gtk_tooltips_set_tip(tooltips, check_box, tooltip_text, NULL);
636
637         return check_box;
638 }
639
640 GtkWidget *
641 create_preference_radio_buttons(GtkWidget *main_tb, int table_position,
642     const gchar *label_text, const gchar *tooltip_text,
643     const enum_val_t *enumvals, gint current_val)
644 {
645         GtkTooltips *tooltips;
646         GtkWidget *radio_button_hbox, *button = NULL;
647         GSList *rb_group;
648         int index;
649         const enum_val_t *enum_valp;
650         GtkWidget *event_box;
651
652         tooltips = OBJECT_GET_DATA(main_tb, E_TOOLTIPS_KEY);
653
654         set_option_label(main_tb, table_position, label_text, tooltip_text,
655             tooltips);
656
657         radio_button_hbox = gtk_hbox_new(FALSE, 0);
658         rb_group = NULL;
659         for (enum_valp = enumvals, index = 0; enum_valp->name != NULL;
660             enum_valp++, index++) {
661                 button = gtk_radio_button_new_with_label(rb_group,
662                     enum_valp->name);
663                 gtk_widget_show(button);
664                 if (rb_group == NULL)
665                         rb_group = gtk_radio_button_group(GTK_RADIO_BUTTON(button));
666                 gtk_box_pack_start(GTK_BOX(radio_button_hbox), button, FALSE,
667                     FALSE, 10);
668                 if (enum_valp->value == current_val) {
669                         gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(button),
670                             TRUE);
671                 }
672         }
673         gtk_widget_show(radio_button_hbox);
674
675         event_box = gtk_event_box_new();
676         gtk_container_add(GTK_CONTAINER(event_box), radio_button_hbox);
677         gtk_table_attach_defaults(GTK_TABLE(main_tb), event_box, 1, 2,
678             table_position, table_position+1);
679         if (tooltip_text != NULL && tooltips != NULL)
680                 gtk_tooltips_set_tip(tooltips, event_box, tooltip_text, NULL);
681         gtk_widget_show(event_box);
682
683         /*
684          * It doesn't matter which of the buttons we return - we fetch
685          * the value by looking at the entire radio button group to
686          * which it belongs, and we can get that from any button.
687          */
688         return button;
689 }
690
691 static gint
692 label_to_enum_val(GtkWidget *label, const enum_val_t *enumvals)
693 {
694         char *label_string;
695         gint enumval;
696
697         /* Get the label's text, and translate it to a value. */
698         gtk_label_get(GTK_LABEL(label), &label_string);
699         enumval = find_val_for_string(label_string, enumvals, 1);
700
701         return enumval;
702 }
703
704 gint
705 fetch_preference_radio_buttons_val(GtkWidget *button,
706     const enum_val_t *enumvals)
707 {
708         GSList *rb_group;
709         GSList *rb_entry;
710
711         /*
712          * Go through the list of of radio buttons in the button's group,
713          * and find the first one that's active.
714          */
715         rb_group = gtk_radio_button_group(GTK_RADIO_BUTTON(button));
716         button = NULL;
717         for (rb_entry = rb_group; rb_entry != NULL;
718             rb_entry = g_slist_next(rb_entry)) {
719                 button = rb_entry->data;
720                 if (GTK_TOGGLE_BUTTON(button)->active)
721                         break;
722         }
723
724         /* OK, now return the value corresponding to that button's label. */
725         return label_to_enum_val(GTK_BIN(button)->child, enumvals);
726 }
727
728 GtkWidget *
729 create_preference_option_menu(GtkWidget *main_tb, int table_position,
730     const gchar *label_text, const gchar *tooltip_text,
731     const enum_val_t *enumvals, gint current_val)
732 {
733         GtkTooltips *tooltips;
734         GtkWidget *menu_box, *menu, *menu_item, *option_menu;
735         int menu_index, index;
736         const enum_val_t *enum_valp;
737         GtkWidget *event_box;
738
739         tooltips = OBJECT_GET_DATA(main_tb, E_TOOLTIPS_KEY);
740
741         set_option_label(main_tb, table_position, label_text, tooltip_text,
742             tooltips);
743
744         /* Create a menu from the enumvals */
745         menu = gtk_menu_new();
746         if (tooltip_text != NULL && tooltips != NULL)
747                 gtk_tooltips_set_tip(tooltips, menu, tooltip_text, NULL);
748         menu_index = -1;
749         for (enum_valp = enumvals, index = 0; enum_valp->name != NULL;
750             enum_valp++, index++) {
751                 menu_item = gtk_menu_item_new_with_label(enum_valp->name);
752                 gtk_menu_append(GTK_MENU(menu), menu_item);
753                 if (enum_valp->value == current_val)
754                         menu_index = index;
755                 gtk_widget_show(menu_item);
756         }
757
758         /* Create the option menu from the menu */
759         option_menu = gtk_option_menu_new();
760         gtk_option_menu_set_menu(GTK_OPTION_MENU(option_menu), menu);
761
762         /* Set its current value to the variable's current value */
763         if (menu_index != -1)
764                 gtk_option_menu_set_history(GTK_OPTION_MENU(option_menu),
765                     menu_index);
766
767         /*
768          * Put the option menu in an hbox, so that it's only as wide
769          * as the widest entry, rather than being as wide as the table
770          * space.
771          */
772         menu_box = gtk_hbox_new(FALSE, 0);
773         gtk_box_pack_start(GTK_BOX(menu_box), option_menu, FALSE, FALSE, 0);
774
775         event_box = gtk_event_box_new();
776         gtk_table_attach_defaults(GTK_TABLE(main_tb), event_box,
777             1, 2, table_position, table_position + 1);
778         if (tooltip_text != NULL && tooltips != NULL)
779                 gtk_tooltips_set_tip(tooltips, event_box, tooltip_text, NULL);
780         gtk_container_add(GTK_CONTAINER(event_box), menu_box);
781
782         return option_menu;
783 }
784
785 gint
786 fetch_preference_option_menu_val(GtkWidget *optmenu, const enum_val_t *enumvals)
787 {
788         /*
789          * OK, now return the value corresponding to the label for the
790          * currently active entry in the option menu.
791          *
792          * Yes, this is how you get the label for that entry.  See FAQ
793          * 6.8 in the GTK+ FAQ.
794          */
795         return label_to_enum_val(GTK_BIN(optmenu)->child, enumvals);
796 }
797
798 GtkWidget *
799 create_preference_entry(GtkWidget *main_tb, int table_position,
800     const gchar *label_text, const gchar *tooltip_text, char *value)
801 {
802         GtkTooltips *tooltips;
803         GtkWidget *entry;
804
805         tooltips = OBJECT_GET_DATA(main_tb, E_TOOLTIPS_KEY);
806
807         set_option_label(main_tb, table_position, label_text, tooltip_text,
808             tooltips);
809
810         entry = gtk_entry_new();
811         if (value != NULL)
812                 gtk_entry_set_text(GTK_ENTRY(entry), value);
813         gtk_table_attach_defaults(GTK_TABLE(main_tb), entry, 1, 2,
814             table_position, table_position + 1);
815         if (tooltip_text != NULL && tooltips != NULL)
816                 gtk_tooltips_set_tip(tooltips, entry, tooltip_text, NULL);
817         gtk_widget_show(entry);
818
819         return entry;
820 }
821
822 static void
823 pref_fetch(pref_t *pref, gpointer user_data)
824 {
825   char *str_val;
826   char *p;
827   guint uval;
828   gboolean bval;
829   gint enumval;
830   gboolean *pref_changed_p = user_data;
831
832   /* Fetch the value of the preference, and set the appropriate variable
833      to it. */
834   switch (pref->type) {
835
836   case PREF_UINT:
837     str_val = gtk_entry_get_text(GTK_ENTRY(pref->control));
838     uval = strtoul(str_val, &p, pref->info.base);
839 #if 0
840     if (p == value || *p != '\0')
841       return PREFS_SET_SYNTAX_ERR;      /* number was bad */
842 #endif
843     if (*pref->varp.uint != uval) {
844       *pref_changed_p = TRUE;
845       *pref->varp.uint = uval;
846     }
847     break;
848
849   case PREF_BOOL:
850     bval = GTK_TOGGLE_BUTTON(pref->control)->active;
851     if (*pref->varp.boolp != bval) {
852       *pref_changed_p = TRUE;
853       *pref->varp.boolp = bval;
854     }
855     break;
856
857   case PREF_ENUM:
858     if (pref->info.enum_info.radio_buttons) {
859       enumval = fetch_preference_radio_buttons_val(pref->control,
860           pref->info.enum_info.enumvals);
861     } else {
862       enumval = fetch_preference_option_menu_val(pref->control,
863           pref->info.enum_info.enumvals);
864     }
865
866     if (*pref->varp.enump != enumval) {
867       *pref_changed_p = TRUE;
868       *pref->varp.enump = enumval;
869     }
870     break;
871
872   case PREF_STRING:
873     str_val = gtk_entry_get_text(GTK_ENTRY(pref->control));
874     if (strcmp(*pref->varp.string, str_val) != 0) {
875       *pref_changed_p = TRUE;
876       g_free(*pref->varp.string);
877       *pref->varp.string = g_strdup(str_val);
878     }
879     break;
880
881   case PREF_OBSOLETE:
882     g_assert_not_reached();
883     break;
884   }
885 }
886
887 static void
888 module_prefs_fetch(module_t *module, gpointer user_data)
889 {
890   gboolean *must_redissect_p = user_data;
891
892   /* For all preferences in this module, fetch its value from this
893      module's notebook page.  Find out whether any of them changed. */
894   module->prefs_changed = FALSE;        /* assume none of them changed */
895   prefs_pref_foreach(module, pref_fetch, &module->prefs_changed);
896
897   /* If any of them changed, indicate that we must redissect and refilter
898      the current capture (if we have one), as the preference change
899      could cause packets to be dissected differently. */
900   if (module->prefs_changed)
901     *must_redissect_p = TRUE;
902 }
903
904 static void
905 pref_clean(pref_t *pref, gpointer user_data _U_)
906 {
907   switch (pref->type) {
908
909   case PREF_UINT:
910     break;
911
912   case PREF_BOOL:
913     break;
914
915   case PREF_ENUM:
916     break;
917
918   case PREF_STRING:
919     if (pref->saved_val.string != NULL) {
920       g_free(pref->saved_val.string);
921       pref->saved_val.string = NULL;
922     }
923     break;
924
925   case PREF_OBSOLETE:
926     g_assert_not_reached();
927     break;
928   }
929 }
930
931 static void
932 module_prefs_clean(module_t *module, gpointer user_data _U_)
933 {
934   /* For all preferences in this module, clean up any cruft allocated for
935      use by the GUI code. */
936   prefs_pref_foreach(module, pref_clean, NULL);
937 }
938
939 static void
940 prefs_main_ok_cb(GtkWidget *ok_bt _U_, gpointer parent_w)
941 {
942   gboolean must_redissect = FALSE;
943
944   /* Fetch the preferences (i.e., make sure all the values set in all of
945      the preferences panes have been copied to "prefs" and the registered
946      preferences). */
947   printer_prefs_fetch(OBJECT_GET_DATA(parent_w, E_PRINT_PAGE_KEY));
948   column_prefs_fetch(OBJECT_GET_DATA(parent_w, E_COLUMN_PAGE_KEY));
949   stream_prefs_fetch(OBJECT_GET_DATA(parent_w, E_STREAM_PAGE_KEY));
950   gui_prefs_fetch(OBJECT_GET_DATA(parent_w, E_GUI_PAGE_KEY));
951 #ifdef HAVE_LIBPCAP
952 #ifdef _WIN32
953   /* Is WPcap loaded? */
954   if (has_wpcap) {
955 #endif /* _WIN32 */
956   capture_prefs_fetch(OBJECT_GET_DATA(parent_w, E_CAPTURE_PAGE_KEY));
957 #ifdef _WIN32
958   }
959 #endif /* _WIN32 */
960 #endif /* HAVE_LIBPCAP */
961   nameres_prefs_fetch(OBJECT_GET_DATA(parent_w, E_NAMERES_PAGE_KEY));
962   prefs_modules_foreach(module_prefs_fetch, &must_redissect);
963
964   /* Now apply those preferences. */
965   printer_prefs_apply(OBJECT_GET_DATA(parent_w, E_PRINT_PAGE_KEY));
966   column_prefs_apply(OBJECT_GET_DATA(parent_w, E_COLUMN_PAGE_KEY));
967   stream_prefs_apply(OBJECT_GET_DATA(parent_w, E_STREAM_PAGE_KEY));
968   gui_prefs_apply(OBJECT_GET_DATA(parent_w, E_GUI_PAGE_KEY));
969 #ifdef HAVE_LIBPCAP
970 #ifdef _WIN32
971   /* Is WPcap loaded? */
972   if (has_wpcap) {
973 #endif /* _WIN32 */
974   capture_prefs_apply(OBJECT_GET_DATA(parent_w, E_CAPTURE_PAGE_KEY));
975 #ifdef _WIN32
976   }
977 #endif /* _WIN32 */
978 #endif /* HAVE_LIBPCAP */
979   nameres_prefs_apply(OBJECT_GET_DATA(parent_w, E_NAMERES_PAGE_KEY));
980   prefs_apply_all();
981
982   /* Now destroy the "Preferences" dialog. */
983   gtk_widget_destroy(GTK_WIDGET(parent_w));
984
985   if (must_redissect) {
986     /* Redissect all the packets, and re-evaluate the display filter. */
987     redissect_packets(&cfile);
988   }
989 }
990
991 static void
992 prefs_main_apply_cb(GtkWidget *apply_bt _U_, gpointer parent_w)
993 {
994   gboolean must_redissect = FALSE;
995
996   /* Fetch the preferences (i.e., make sure all the values set in all of
997      the preferences panes have been copied to "prefs" and the registered
998      preferences). */
999   printer_prefs_fetch(OBJECT_GET_DATA(parent_w, E_PRINT_PAGE_KEY));
1000   column_prefs_fetch(OBJECT_GET_DATA(parent_w, E_COLUMN_PAGE_KEY));
1001   stream_prefs_fetch(OBJECT_GET_DATA(parent_w, E_STREAM_PAGE_KEY));
1002   gui_prefs_fetch(OBJECT_GET_DATA(parent_w, E_GUI_PAGE_KEY));
1003 #ifdef HAVE_LIBPCAP
1004 #ifdef _WIN32
1005   /* Is WPcap loaded? */
1006   if (has_wpcap) {
1007 #endif /* _WIN32 */
1008   capture_prefs_fetch(OBJECT_GET_DATA(parent_w, E_CAPTURE_PAGE_KEY));
1009 #ifdef _WIN32
1010   }
1011 #endif /* _WIN32 */
1012 #endif /* HAVE_LIBPCAP */
1013   nameres_prefs_fetch(OBJECT_GET_DATA(parent_w, E_NAMERES_PAGE_KEY));
1014   prefs_modules_foreach(module_prefs_fetch, &must_redissect);
1015
1016   /* Now apply those preferences. */
1017   printer_prefs_apply(OBJECT_GET_DATA(parent_w, E_PRINT_PAGE_KEY));
1018   column_prefs_apply(OBJECT_GET_DATA(parent_w, E_COLUMN_PAGE_KEY));
1019   stream_prefs_apply(OBJECT_GET_DATA(parent_w, E_STREAM_PAGE_KEY));
1020   gui_prefs_apply(OBJECT_GET_DATA(parent_w, E_GUI_PAGE_KEY));
1021 #ifdef HAVE_LIBPCAP
1022 #ifdef _WIN32
1023   /* Is WPcap loaded? */
1024   if (has_wpcap) {
1025 #endif /* _WIN32 */
1026   capture_prefs_apply(OBJECT_GET_DATA(parent_w, E_CAPTURE_PAGE_KEY));
1027 #ifdef _WIN32
1028   }
1029 #endif /* _WIN32 */
1030 #endif /* HAVE_LIBPCAP */
1031   nameres_prefs_apply(OBJECT_GET_DATA(parent_w, E_NAMERES_PAGE_KEY));
1032   prefs_apply_all();
1033
1034   if (must_redissect) {
1035     /* Redissect all the packets, and re-evaluate the display filter. */
1036     redissect_packets(&cfile);
1037   }
1038 }
1039
1040 static void
1041 prefs_main_save_cb(GtkWidget *save_bt _U_, gpointer parent_w)
1042 {
1043   gboolean must_redissect = FALSE;
1044   int err;
1045   char *pf_dir_path;
1046   char *pf_path;
1047
1048   /* Fetch the preferences (i.e., make sure all the values set in all of
1049      the preferences panes have been copied to "prefs" and the registered
1050      preferences). */
1051   printer_prefs_fetch(OBJECT_GET_DATA(parent_w, E_PRINT_PAGE_KEY));
1052   column_prefs_fetch(OBJECT_GET_DATA(parent_w, E_COLUMN_PAGE_KEY));
1053   stream_prefs_fetch(OBJECT_GET_DATA(parent_w, E_STREAM_PAGE_KEY));
1054   gui_prefs_fetch(OBJECT_GET_DATA(parent_w, E_GUI_PAGE_KEY));
1055 #ifdef HAVE_LIBPCAP
1056 #ifdef _WIN32
1057   /* Is WPcap loaded? */
1058   if (has_wpcap) {
1059 #endif /* _WIN32 */
1060   capture_prefs_fetch(OBJECT_GET_DATA(parent_w, E_CAPTURE_PAGE_KEY));
1061 #ifdef _WIN32
1062   }
1063 #endif /* _WIN32 */
1064 #endif /* HAVE_LIBPCAP */
1065   nameres_prefs_fetch(OBJECT_GET_DATA(parent_w, E_NAMERES_PAGE_KEY));
1066   prefs_modules_foreach(module_prefs_fetch, &must_redissect);
1067
1068   /* Create the directory that holds personal configuration files, if
1069      necessary.  */
1070   if (create_persconffile_dir(&pf_dir_path) == -1) {
1071      simple_dialog(ESD_TYPE_WARN, NULL,
1072       "Can't create directory\n\"%s\"\nfor preferences file: %s.", pf_dir_path,
1073       strerror(errno));
1074      g_free(pf_dir_path);
1075   } else {
1076     /* Write the preferencs out. */
1077     err = write_prefs(&pf_path);
1078     if (err != 0) {
1079        simple_dialog(ESD_TYPE_WARN, NULL,
1080         "Can't open preferences file\n\"%s\": %s.", pf_path,
1081         strerror(err));
1082        g_free(pf_path);
1083     }
1084   }
1085
1086   /* Now apply those preferences.
1087      XXX - should we do this?  The user didn't click "OK" or "Apply".
1088      However:
1089
1090         1) by saving the preferences they presumably indicate that they
1091            like them;
1092
1093         2) the next time they fire Ethereal up, those preferences will
1094            apply;
1095
1096         3) we'd have to buffer "must_redissect" so that if they do
1097            "Apply" after this, we know we have to redissect;
1098
1099         4) we did apply the protocol preferences, at least, in the past. */
1100   printer_prefs_apply(OBJECT_GET_DATA(parent_w, E_PRINT_PAGE_KEY));
1101   column_prefs_apply(OBJECT_GET_DATA(parent_w, E_COLUMN_PAGE_KEY));
1102   stream_prefs_apply(OBJECT_GET_DATA(parent_w, E_STREAM_PAGE_KEY));
1103   gui_prefs_apply(OBJECT_GET_DATA(parent_w, E_GUI_PAGE_KEY));
1104 #ifdef HAVE_LIBPCAP
1105 #ifdef _WIN32
1106   /* Is WPcap loaded? */
1107   if (has_wpcap) {
1108 #endif /* _WIN32 */
1109   capture_prefs_apply(OBJECT_GET_DATA(parent_w, E_CAPTURE_PAGE_KEY));
1110 #ifdef _WIN32
1111   }
1112 #endif /* _WIN32 */
1113 #endif /* HAVE_LIBPCAP */
1114   nameres_prefs_apply(OBJECT_GET_DATA(parent_w, E_NAMERES_PAGE_KEY));
1115   prefs_apply_all();
1116
1117   if (must_redissect) {
1118     /* Redissect all the packets, and re-evaluate the display filter. */
1119     redissect_packets(&cfile);
1120   }
1121 }
1122
1123 static void
1124 pref_revert(pref_t *pref, gpointer user_data)
1125 {
1126   gboolean *pref_changed_p = user_data;
1127
1128   /* Revert the preference to its saved value. */
1129   switch (pref->type) {
1130
1131   case PREF_UINT:
1132     if (*pref->varp.uint != pref->saved_val.uint) {
1133       *pref_changed_p = TRUE;
1134       *pref->varp.uint = pref->saved_val.uint;
1135     }
1136     break;
1137
1138   case PREF_BOOL:
1139     if (*pref->varp.boolp != pref->saved_val.boolval) {
1140       *pref_changed_p = TRUE;
1141       *pref->varp.boolp = pref->saved_val.boolval;
1142     }
1143     break;
1144
1145   case PREF_ENUM:
1146     if (*pref->varp.enump != pref->saved_val.enumval) {
1147       *pref_changed_p = TRUE;
1148       *pref->varp.enump = pref->saved_val.enumval;
1149     }
1150     break;
1151
1152   case PREF_STRING:
1153     if (strcmp(*pref->varp.string, pref->saved_val.string) != 0) {
1154       *pref_changed_p = TRUE;
1155       g_free(*pref->varp.string);
1156       *pref->varp.string = g_strdup(pref->saved_val.string);
1157     }
1158     break;
1159
1160   case PREF_OBSOLETE:
1161     g_assert_not_reached();
1162     break;
1163   }
1164 }
1165
1166 static void
1167 module_prefs_revert(module_t *module, gpointer user_data)
1168 {
1169   gboolean *must_redissect_p = user_data;
1170
1171   /* For all preferences in this module, revert its value to the value
1172      it had when we popped up the Preferences dialog.  Find out whether
1173      this changes any of them. */
1174   module->prefs_changed = FALSE;        /* assume none of them changed */
1175   prefs_pref_foreach(module, pref_revert, &module->prefs_changed);
1176
1177   /* If any of them changed, indicate that we must redissect and refilter
1178      the current capture (if we have one), as the preference change
1179      could cause packets to be dissected differently. */
1180   if (module->prefs_changed)
1181     *must_redissect_p = TRUE;
1182 }
1183
1184 static void
1185 prefs_main_cancel_cb(GtkWidget *cancel_bt _U_, gpointer parent_w)
1186 {
1187   gboolean must_redissect = FALSE;
1188
1189   /* Free up the current preferences and copy the saved preferences to the
1190      current preferences. */
1191   free_prefs(&prefs);
1192   copy_prefs(&prefs, &saved_prefs);
1193
1194   /* Now revert the registered preferences. */
1195   prefs_modules_foreach(module_prefs_revert, &must_redissect);
1196
1197   /* Now apply the reverted-to preferences. */
1198   printer_prefs_apply(OBJECT_GET_DATA(parent_w, E_PRINT_PAGE_KEY));
1199   column_prefs_apply(OBJECT_GET_DATA(parent_w, E_COLUMN_PAGE_KEY));
1200   stream_prefs_apply(OBJECT_GET_DATA(parent_w, E_STREAM_PAGE_KEY));
1201   gui_prefs_apply(OBJECT_GET_DATA(parent_w, E_GUI_PAGE_KEY));
1202   nameres_prefs_apply(OBJECT_GET_DATA(parent_w, E_NAMERES_PAGE_KEY));
1203   prefs_apply_all();
1204
1205   gtk_widget_destroy(GTK_WIDGET(parent_w));
1206
1207   if (must_redissect) {
1208     /* Redissect all the packets, and re-evaluate the display filter. */
1209     redissect_packets(&cfile);
1210   }
1211 }
1212
1213 /* Treat this as a cancel, by calling "prefs_main_cancel_cb()".
1214    XXX - that'll destroy the Preferences dialog; will that upset
1215    a higher-level handler that says "OK, we've been asked to delete
1216    this, so destroy it"? */
1217 static gboolean
1218 prefs_main_delete_cb(GtkWidget *prefs_w, gpointer dummy _U_)
1219 {
1220   prefs_main_cancel_cb(NULL, prefs_w);
1221   return FALSE;
1222 }
1223
1224 static void
1225 prefs_main_destroy_cb(GtkWidget *win _U_, gpointer user_data _U_)
1226 {
1227   /* Let the preference tabs clean up anything they've done. */
1228   printer_prefs_destroy(OBJECT_GET_DATA(prefs_w, E_PRINT_PAGE_KEY));
1229   column_prefs_destroy(OBJECT_GET_DATA(prefs_w, E_COLUMN_PAGE_KEY));
1230   stream_prefs_destroy(OBJECT_GET_DATA(prefs_w, E_STREAM_PAGE_KEY));
1231   gui_prefs_destroy(OBJECT_GET_DATA(prefs_w, E_GUI_PAGE_KEY));
1232 #ifdef HAVE_LIBPCAP
1233 #ifdef _WIN32
1234   /* Is WPcap loaded? */
1235   if (has_wpcap) {
1236 #endif /* _WIN32 */
1237   capture_prefs_destroy(OBJECT_GET_DATA(prefs_w, E_CAPTURE_PAGE_KEY));
1238 #ifdef _WIN32
1239   }
1240 #endif /* _WIN32 */
1241 #endif /* HAVE_LIBPCAP */
1242   nameres_prefs_destroy(OBJECT_GET_DATA(prefs_w, E_NAMERES_PAGE_KEY));
1243
1244   /* Free up the saved preferences (both for "prefs" and for registered
1245      preferences). */
1246   free_prefs(&saved_prefs);
1247   prefs_modules_foreach(module_prefs_clean, NULL);
1248
1249   /* Note that we no longer have a "Preferences" dialog box. */
1250   prefs_w = NULL;
1251   first_proto_prefs_page = -1;
1252 }
1253
1254 struct properties_data {
1255   GtkWidget *w;
1256   int page_num;
1257   const char *title;
1258 };
1259
1260 /* XXX this way of searching the correct page number is really ugly ... */
1261 static void
1262 module_search_properties(module_t *module, gpointer user_data)
1263 {
1264   struct properties_data *p = (struct properties_data *)user_data;
1265
1266   if (p->title == NULL) return;
1267   if (strcmp(module->title, p->title) == 0) {
1268     /* found it */
1269     gtk_notebook_set_page(GTK_NOTEBOOK(p->w), p->page_num);
1270     p->title = NULL;
1271   } else {
1272     p->page_num++;
1273   }
1274 }
1275
1276 void
1277 properties_cb(GtkWidget *w, gpointer dummy)
1278 {
1279   const gchar *title = NULL;
1280   struct properties_data p;
1281
1282   if (cfile.finfo_selected) {
1283     header_field_info *hfinfo = cfile.finfo_selected->hfinfo;
1284     if (hfinfo->parent == -1) {
1285       title = prefs_get_title_by_name(hfinfo->abbrev);
1286     } else {
1287       title =
1288         prefs_get_title_by_name(proto_registrar_get_abbrev(hfinfo->parent));
1289     }
1290   } else {
1291     return;
1292   }
1293
1294   if (!title) return;
1295
1296   if (prefs_w != NULL) {
1297     reactivate_window(prefs_w);
1298   } else {
1299     prefs_cb(w, dummy);
1300   }
1301
1302   p.w = notebook;
1303   p.page_num = first_proto_prefs_page;
1304   p.title = title;
1305
1306   prefs_module_list_foreach(protocols_module->prefs, module_search_properties,
1307       &p);
1308 }
1309
1310 /* Prefs tree selection callback.  The node data has been loaded with
1311    the proper notebook page to load. */
1312 #if GTK_MAJOR_VERSION < 2
1313 static void
1314 prefs_tree_select_cb(GtkCTree *ct, GtkCTreeNode *node, gint col _U_,
1315                      gpointer dummy _U_)
1316 #else
1317 static void
1318 prefs_tree_select_cb(GtkTreeSelection *sel, gpointer dummy _U_)
1319 #endif
1320 {
1321   gint page;
1322 #if GTK_MAJOR_VERSION >= 2
1323   GtkTreeModel *model;
1324   GtkTreeIter   iter;
1325 #endif
1326
1327 #if GTK_MAJOR_VERSION < 2
1328   page = GPOINTER_TO_INT(gtk_ctree_node_get_row_data(ct, node));
1329
1330   if (page >= 0)
1331     gtk_notebook_set_page(GTK_NOTEBOOK(notebook), page);
1332 #else
1333   if (gtk_tree_selection_get_selected(sel, &model, &iter))
1334   {
1335     gtk_tree_model_get(model, &iter, 1, &page, -1);
1336     if (page >= 0)
1337       gtk_notebook_set_page(GTK_NOTEBOOK(notebook), page);
1338   }
1339 #endif
1340 }