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