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