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