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