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