From Cal Turney:
[metze/wireshark/wip.git] / ui / gtk / prefs_dlg.c
1 /* prefs_dlg.c
2  * Routines for handling preferences
3  *
4  * $Id$
5  *
6  * Wireshark - Network traffic analyzer
7  * By Gerald Combs <gerald@wireshark.org>
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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
23  */
24
25 #include "config.h"
26
27 #include <gtk/gtk.h>
28
29 #include <string.h>
30
31 #include <epan/packet.h>
32 #include <epan/prefs.h>
33 #include <epan/strutil.h>
34 #include <epan/prefs-int.h>
35 #include <epan/epan_dissect.h>
36 #include <epan/print.h>
37
38 #include "../file.h"
39 #include "ui/preference_utils.h"
40 #include "ui/simple_dialog.h"
41
42 #include "ui/gtk/main.h"
43 #include "ui/gtk/prefs_column.h"
44 #include "ui/gtk/prefs_dlg.h"
45 #include "ui/gtk/prefs_filter_expressions.h"
46 #include "ui/gtk/prefs_font_color.h"
47 #include "ui/gtk/prefs_gui.h"
48 #include "ui/gtk/prefs_layout.h"
49 #include "ui/gtk/prefs_capture.h"
50 #include "ui/gtk/gui_utils.h"
51 #include "ui/gtk/dlg_utils.h"
52 #include "ui/gtk/stock_icons.h"
53 #include "ui/gtk/help_dlg.h"
54 #include "ui/gtk/keys.h"
55 #include "ui/gtk/uat_gui.h"
56 #include "ui/gtk/old-gtk-compat.h"
57 #include "ui/gtk/file_dlg.h"
58 #include "ui/gtk/dlg_utils.h"
59 #include "ui/gtk/packet_win.h"
60
61 #ifdef HAVE_LIBPCAP
62 #ifdef _WIN32
63 #include "capture-wpcap.h"
64 #endif /* _WIN32 */
65 #ifdef HAVE_AIRPCAP
66 #include "airpcap.h"
67 #include "airpcap_loader.h"
68 #include "airpcap_gui_utils.h"
69 #endif
70 #endif
71
72 static void     prefs_main_ok_cb(GtkWidget *, gpointer);
73 static void     prefs_main_apply_cb(GtkWidget *, gpointer);
74 static void     prefs_main_save_cb(GtkWidget *, gpointer);
75 static void     prefs_main_cancel_cb(GtkWidget *, gpointer);
76 static gboolean prefs_main_delete_event_cb(GtkWidget *, GdkEvent *, gpointer);
77 static void     prefs_main_destroy_cb(GtkWidget *, gpointer);
78 static void     prefs_tree_select_cb(GtkTreeSelection *, gpointer);
79
80 static GtkWidget *create_preference_path_entry(GtkWidget *, int,
81    const gchar *, const gchar *, char *, gboolean);
82
83 #define E_PREFSW_SCROLLW_KEY          "prefsw_scrollw"
84 #define E_PREFSW_TREE_KEY             "prefsw_tree"
85 #define E_PREFSW_NOTEBOOK_KEY         "prefsw_notebook"
86 #define E_PREFSW_SAVE_BT_KEY          "prefsw_save_bt"
87 #define E_PAGE_ITER_KEY               "page_iter"
88 #define E_PAGE_MODULE_KEY             "page_module"
89 #define E_PAGESW_FRAME_KEY            "pagesw_frame"
90
91 #define E_GUI_PAGE_KEY                "gui_options_page"
92 #define E_GUI_LAYOUT_PAGE_KEY         "gui_layout_page"
93 #define E_GUI_COLUMN_PAGE_KEY         "gui_column_options_page"
94 #define E_GUI_FONT_PAGE_KEY           "gui_font_options_page"
95 #define E_GUI_FONT_COLORS_PAGE_KEY    "gui_font_colors_options_page"
96 #define E_CAPTURE_PAGE_KEY            "capture_options_page"
97 #define E_NAMERES_PAGE_KEY            "nameres_options_page"
98 #define E_FILTER_EXPRESSIONS_PAGE_KEY "filter_expressions_page"
99 #define E_GRID_MODULE_KEY             "grid_module"
100
101 /*
102  * Keep a static pointer to the current "Preferences" window, if any, so that
103  * if somebody tries to do "Edit:Preferences" while there's already a
104  * "Preferences" window up, we just pop up the existing one, rather than
105  * creating a new one.
106  */
107 static GtkWidget *prefs_w;
108
109 struct ct_struct {
110   GtkWidget    *main_vb;
111   GtkWidget    *notebook;
112   GtkWidget    *tree;
113   GtkTreeIter   iter;
114   gint          page;
115   GtkTreeStore *store;
116 };
117
118 static guint
119 pref_exists(pref_t *pref _U_, gpointer user_data _U_)
120 {
121   return 1;
122 }
123
124 /* show a single preference on the GtkGrid of a preference page */
125 static guint
126 pref_show(pref_t *pref, gpointer user_data)
127 {
128   GtkWidget  *main_grid = (GtkWidget *)user_data;
129   module_t   *module  = (module_t *)g_object_get_data(G_OBJECT(main_grid), E_GRID_MODULE_KEY);
130   const char *title;
131   const char *type_name = prefs_pref_type_name(pref);
132   char       *label_string;
133   size_t      label_len;
134   char        uint_str[10+1];
135   char *tooltip_txt;
136
137   /* Give this preference a label which is its title, followed by a colon,
138      and left-align it. */
139   title = pref->title;
140   label_len = strlen(title) + 2;
141   label_string = (char *)g_malloc(label_len);
142   g_strlcpy(label_string, title, label_len);
143
144   tooltip_txt = pref->description? g_strdup_printf("%s\n\nName: %s.%s\nType: %s",
145                                                    pref->description,
146                                                    module->name,
147                                                    pref->name,
148                                                    type_name ? type_name : "Unknown"
149                                                    ): NULL;
150
151   /*
152    * Sometimes we don't want to append a ':' after a static text string...
153    * If it is needed, we will specify it in the string itself.
154    */
155   if (pref->type != PREF_STATIC_TEXT)
156     g_strlcat(label_string, ":", label_len);
157
158   pref_stash(pref, NULL);
159
160   /* Save the current value of the preference, so that we can revert it if
161      the user does "Apply" and then "Cancel", and create the control for
162      editing the preference. */
163   switch (pref->type) {
164
165   case PREF_UINT:
166     /* XXX - there are no uint spinbuttons, so we can't use a spinbutton.
167        Even more annoyingly, even if there were, GLib doesn't define
168        G_MAXUINT - but I think ANSI C may define UINT_MAX, so we could
169        use that. */
170     switch (pref->info.base) {
171
172     case 10:
173       g_snprintf(uint_str, sizeof(uint_str), "%u", pref->stashed_val.uint);
174       break;
175
176     case 8:
177       g_snprintf(uint_str, sizeof(uint_str), "%o", pref->stashed_val.uint);
178       break;
179
180     case 16:
181       g_snprintf(uint_str, sizeof(uint_str), "%x", pref->stashed_val.uint);
182       break;
183     }
184     pref->control = create_preference_entry(main_grid, pref->ordinal,
185                                             label_string, tooltip_txt,
186                                             uint_str);
187     break;
188
189   case PREF_BOOL:
190     pref->control = create_preference_check_button(main_grid, pref->ordinal,
191                                                    label_string, tooltip_txt,
192                                                    pref->stashed_val.boolval);
193     break;
194
195   case PREF_ENUM:
196     if (pref->info.enum_info.radio_buttons) {
197       /* Show it as radio buttons. */
198       pref->control = create_preference_radio_buttons(main_grid, pref->ordinal,
199                                                       label_string, tooltip_txt,
200                                                       pref->info.enum_info.enumvals,
201                                                       pref->stashed_val.enumval);
202     } else {
203       /* Show it as an option menu. */
204       pref->control = create_preference_option_menu(main_grid, pref->ordinal,
205                                                     label_string, tooltip_txt,
206                                                     pref->info.enum_info.enumvals,
207                                                     pref->stashed_val.enumval);
208     }
209     break;
210
211   case PREF_STRING:
212     pref->control = create_preference_entry(main_grid, pref->ordinal,
213                                             label_string, tooltip_txt,
214                                             pref->stashed_val.string);
215     break;
216
217   case PREF_FILENAME:
218     pref->control = create_preference_path_entry(main_grid, pref->ordinal,
219                                                      label_string,
220                                                      tooltip_txt,
221                                                      pref->stashed_val.string, FALSE);
222     break;
223
224   case PREF_DIRNAME:
225     pref->control = create_preference_path_entry(main_grid, pref->ordinal,
226                                                      label_string,
227                                                      tooltip_txt,
228                                                      pref->stashed_val.string, TRUE);
229     break;
230
231   case PREF_RANGE:
232   {
233     char *range_str_p;
234
235     range_str_p = range_convert_range(*pref->varp.range);
236     pref->control = create_preference_entry(main_grid, pref->ordinal,
237                                             label_string, tooltip_txt,
238                                             range_str_p);
239     break;
240   }
241
242   case PREF_STATIC_TEXT:
243   {
244     pref->control = create_preference_static_text(main_grid, pref->ordinal,
245                                                   label_string, tooltip_txt);
246     break;
247   }
248
249   case PREF_UAT:
250   {
251     pref->control = create_preference_uat(main_grid, pref->ordinal,
252                                           label_string, tooltip_txt,
253                                           pref->varp.uat);
254     break;
255   }
256
257   case PREF_COLOR:
258   case PREF_CUSTOM:
259       /* currently not supported */
260
261   case PREF_OBSOLETE:
262     g_assert_not_reached();
263     break;
264   }
265   g_free(tooltip_txt);
266   g_free(label_string);
267
268   return 0;
269 }
270
271 #define prefs_tree_iter GtkTreeIter
272
273 /* add a page to the tree */
274 static prefs_tree_iter
275 prefs_tree_page_add(const gchar *title, gint page_nr,
276                     gpointer store, prefs_tree_iter *parent_iter)
277 {
278   prefs_tree_iter   iter;
279
280   gtk_tree_store_append((GtkTreeStore *)store, &iter, parent_iter);
281   gtk_tree_store_set((GtkTreeStore *)store, &iter, 0, title, 1, page_nr, -1);
282   return iter;
283 }
284
285 /* add a page to the notebook */
286 static GtkWidget *
287 prefs_nb_page_add(GtkWidget *notebook, const gchar *title _U_, GtkWidget *page, const char *page_key)
288 {
289   GtkWidget         *sw;
290   GtkWidget         *frame;
291
292   sw = gtk_scrolled_window_new(NULL, NULL);
293   gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
294   gtk_widget_show(sw);
295
296   frame = gtk_frame_new(NULL);
297   gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_NONE);
298   gtk_container_set_border_width(GTK_CONTAINER(frame), DLG_OUTER_MARGIN);
299 #if ! GTK_CHECK_VERSION(3,8,0)
300   gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(sw), frame);
301 #else
302   gtk_container_add(GTK_CONTAINER(sw), frame);
303 #endif
304   gtk_widget_show(frame);
305
306   if (page) {
307     gtk_container_add(GTK_CONTAINER(frame), page);
308     g_object_set_data(G_OBJECT(prefs_w), page_key, page);
309   }
310
311   gtk_notebook_append_page (GTK_NOTEBOOK(notebook), sw, NULL);
312
313   return sw;
314 }
315
316 #define MAX_TREE_NODE_NAME_LEN 64
317
318 /* show prefs page for each registered module (protocol) */
319 static guint
320 module_prefs_show(module_t *module, gpointer user_data)
321 {
322   struct ct_struct *cts = (struct ct_struct *)user_data;
323   struct ct_struct  child_cts;
324   GtkWidget        *main_vb, *main_grid, *frame, *main_sw;
325   gchar             label_str[MAX_TREE_NODE_NAME_LEN];
326   GtkTreeStore     *model;
327   GtkTreeIter       iter;
328
329   if (!module->use_gui) {
330       /* This module uses its own GUI interface to modify its
331        * preferences, so ignore it
332        */
333       return 0;
334   }
335
336   /*
337    * Is this module an interior node, with modules underneath it?
338    */
339   if (!prefs_module_has_submodules(module)) {
340     /*
341      * No.
342      * Does it have any preferences (other than possibly obsolete ones)?
343      */
344     if (prefs_pref_foreach(module, pref_exists, NULL) == 0) {
345       /*
346        * No.  Don't put the module into the preferences window,
347        * as there's nothing to show.
348        *
349        * XXX - we should do the same for interior ndes; if the module
350        * has no non-obsolete preferences *and* nothing under it has
351        * non-obsolete preferences, don't put it into the window.
352        */
353       return 0;
354     }
355   }
356
357   /*
358    * Add this module to the tree.
359    */
360   g_strlcpy(label_str, module->title, MAX_TREE_NODE_NAME_LEN);
361   model = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(cts->tree)));
362
363   if (module->parent == NULL)
364     gtk_tree_store_append(model, &iter, NULL);
365   else
366     gtk_tree_store_append(model, &iter, &cts->iter);
367
368   /*
369    * Is this an interior node?
370    */
371   if (prefs_module_has_submodules(module)) {
372     /*
373      * Yes.
374      */
375     gtk_tree_store_set(model, &iter, 0, label_str, 1, -1, -1);
376
377     /*
378      * Walk the subtree and attach stuff to it.
379      */
380     child_cts = *cts;
381     child_cts.iter = iter;
382     prefs_modules_foreach_submodules(module, module_prefs_show, &child_cts);
383
384     /* keep the page count right */
385     cts->page = child_cts.page;
386   }
387
388   /*
389    * We create pages for interior nodes even if they don't have
390    * preferences, so that we at least have something to show
391    * if the user clicks on them, even if it's empty.
392    */
393
394   /* Scrolled window */
395   main_sw = gtk_scrolled_window_new(NULL, NULL);
396   gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(main_sw), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
397
398   /* Frame */
399   frame = gtk_frame_new(NULL);
400   gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_NONE);
401   gtk_container_set_border_width(GTK_CONTAINER(frame), DLG_OUTER_MARGIN);
402 #if ! GTK_CHECK_VERSION(3,8,0)
403   gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(main_sw), frame);
404 #else
405   gtk_container_add(GTK_CONTAINER(main_sw), frame);
406 #endif
407   g_object_set_data(G_OBJECT(main_sw), E_PAGESW_FRAME_KEY, frame);
408
409   /* Main vertical box */
410   main_vb = ws_gtk_box_new(GTK_ORIENTATION_VERTICAL, 5, FALSE);
411   gtk_container_set_border_width(GTK_CONTAINER(main_vb), 5);
412   gtk_container_add(GTK_CONTAINER(frame), main_vb);
413
414   /* Main grid */
415   main_grid = ws_gtk_grid_new();
416   gtk_box_pack_start(GTK_BOX(main_vb), main_grid, FALSE, FALSE, 0);
417 #if GTK_CHECK_VERSION(3,0,0)
418   gtk_widget_set_vexpand(GTK_WIDGET(main_grid), FALSE); /* Ignore VEXPAND requests from children */
419 #endif
420   ws_gtk_grid_set_row_spacing(GTK_GRID(main_grid), 10);
421   ws_gtk_grid_set_column_spacing(GTK_GRID(main_grid), 15);
422
423   /* Add items for each of the preferences */
424   g_object_set_data(G_OBJECT(main_grid), E_GRID_MODULE_KEY, module);
425   prefs_pref_foreach(module, pref_show, main_grid);
426   g_object_set_data(G_OBJECT(main_grid), E_GRID_MODULE_KEY, NULL);
427
428   /* Associate this module with the page's frame. */
429   g_object_set_data(G_OBJECT(frame), E_PAGE_MODULE_KEY, module);
430
431   /* Add the page to the notebook */
432   gtk_notebook_append_page(GTK_NOTEBOOK(cts->notebook), main_sw, NULL);
433
434   /* Attach the page to the tree item */
435   gtk_tree_store_set(model, &iter, 0, label_str, 1, cts->page, -1);
436   g_object_set_data(G_OBJECT(frame), E_PAGE_ITER_KEY, gtk_tree_iter_copy(&iter));
437
438   cts->page++;
439
440   /* Show 'em what we got */
441   gtk_widget_show_all(main_sw);
442
443   return 0;
444 }
445
446
447 /* show the dialog */
448 void
449 prefs_cb(GtkWidget *w, gpointer dummy)
450 {
451   prefs_page_cb (w, dummy, PREFS_PAGE_USER_INTERFACE);
452 }
453
454 void
455 prefs_page_cb(GtkWidget *w _U_, gpointer dummy _U_, PREFS_PAGE_E prefs_page)
456 {
457   GtkWidget         *top_hb, *bbox, *prefs_nb, *ct_sb,
458                     *ok_bt, *apply_bt, *save_bt, *cancel_bt, *help_bt;
459   gchar              label_str[MAX_TREE_NODE_NAME_LEN];
460   struct ct_struct   cts;
461   GtkTreeStore      *store;
462   GtkTreeSelection  *selection;
463   GtkCellRenderer   *renderer;
464   GtkTreeViewColumn *column;
465   gint               col_offset;
466   prefs_tree_iter    gui_iter, layout_iter, columns_iter, capture_iter;
467   gint               layout_page, columns_page;
468   gint               capture_page = 0;
469
470
471   if (prefs_w != NULL) {
472     /* There's already a "Preferences" dialog box; reactivate it. */
473     reactivate_window(prefs_w);
474     return;
475   }
476
477   prefs_w = dlg_conf_window_new("Wireshark: Preferences");
478   gtk_window_set_default_size(GTK_WINDOW(prefs_w), 400, 650);
479
480   /*
481    * Unfortunately, we can't arrange that a GtkGrid widget wrap an event box
482    * around a grid row, so the spacing between the preference item's label
483    * and its control widgets is inactive and the tooltip doesn't pop up when
484    * the mouse is over it.
485    */
486
487   /* Container for each row of widgets */
488   cts.main_vb = ws_gtk_box_new(GTK_ORIENTATION_VERTICAL, 5, FALSE);
489   gtk_container_set_border_width(GTK_CONTAINER(cts.main_vb), 5);
490   gtk_container_add(GTK_CONTAINER(prefs_w), cts.main_vb);
491   gtk_widget_show(cts.main_vb);
492
493   /* Top row: Preferences tree and notebook */
494   top_hb = ws_gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 10, FALSE);
495   gtk_box_pack_start(GTK_BOX(cts.main_vb), top_hb, TRUE, TRUE, 0);
496   gtk_widget_show(top_hb);
497
498   /* scrolled window on the left for the categories tree */
499   ct_sb = scrolled_window_new(NULL, NULL);
500   gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(ct_sb),
501                                    GTK_SHADOW_IN);
502   gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(ct_sb),
503                                  GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
504   gtk_box_pack_start(GTK_BOX(top_hb), ct_sb, TRUE, TRUE, 0);
505   gtk_widget_show(ct_sb);
506   g_object_set_data(G_OBJECT(prefs_w), E_PREFSW_SCROLLW_KEY, ct_sb);
507
508   /* categories tree */
509   store = gtk_tree_store_new(2, G_TYPE_STRING, G_TYPE_INT);
510   cts.tree = tree_view_new(GTK_TREE_MODEL(store));
511   cts.iter.stamp = 0; /* mark this as the toplevel */
512   g_object_set_data(G_OBJECT(prefs_w), E_PREFSW_TREE_KEY, cts.tree);
513   gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(cts.tree), FALSE);
514   selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(cts.tree));
515   gtk_tree_selection_set_mode(selection, GTK_SELECTION_SINGLE);
516   renderer = gtk_cell_renderer_text_new();
517   col_offset = gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(cts.tree),
518                                                            -1, "Name", renderer,
519                                                            "text", 0, NULL);
520   column = gtk_tree_view_get_column(GTK_TREE_VIEW(cts.tree),
521                                     col_offset - 1);
522   gtk_tree_view_column_set_sizing(GTK_TREE_VIEW_COLUMN(column),
523                                   GTK_TREE_VIEW_COLUMN_AUTOSIZE);
524   g_signal_connect(selection, "changed", G_CALLBACK(prefs_tree_select_cb), NULL);
525   gtk_container_add(GTK_CONTAINER(ct_sb), cts.tree);
526   gtk_widget_show(cts.tree);
527
528   /* A notebook widget without tabs is used to flip between prefs */
529   prefs_nb = gtk_notebook_new();
530   g_object_set_data(G_OBJECT(prefs_w), E_PREFSW_NOTEBOOK_KEY, prefs_nb);
531   gtk_notebook_set_show_tabs(GTK_NOTEBOOK(prefs_nb), FALSE);
532   gtk_notebook_set_show_border(GTK_NOTEBOOK(prefs_nb), FALSE);
533   gtk_box_pack_start(GTK_BOX(top_hb), prefs_nb, TRUE, TRUE, 0);
534   gtk_widget_show(prefs_nb);
535
536   cts.page = 0;
537
538   /* GUI prefs */
539   g_strlcpy(label_str, "User Interface", MAX_TREE_NODE_NAME_LEN);
540   prefs_nb_page_add(prefs_nb, label_str, gui_prefs_show(), E_GUI_PAGE_KEY);
541   gui_iter = prefs_tree_page_add(label_str, cts.page, store, NULL);
542   cts.page++;
543
544   /* GUI layout prefs */
545   g_strlcpy(label_str, "Layout", MAX_TREE_NODE_NAME_LEN);
546   prefs_nb_page_add(prefs_nb, label_str, layout_prefs_show(), E_GUI_LAYOUT_PAGE_KEY);
547   layout_iter = prefs_tree_page_add(label_str, cts.page, store, &gui_iter);
548   layout_page = cts.page++;
549
550   /* GUI Column prefs */
551   g_strlcpy(label_str, "Columns", MAX_TREE_NODE_NAME_LEN);
552   prefs_nb_page_add(prefs_nb, label_str, column_prefs_show(prefs_w), E_GUI_COLUMN_PAGE_KEY);
553   columns_iter = prefs_tree_page_add(label_str, cts.page, store, &gui_iter);
554   columns_page = cts.page++;
555
556   /* GUI Colors prefs */
557   g_strlcpy(label_str, "Font and Colors", MAX_TREE_NODE_NAME_LEN);
558   prefs_nb_page_add(prefs_nb, label_str, font_color_prefs_show(), E_GUI_FONT_COLORS_PAGE_KEY);
559   prefs_tree_page_add(label_str, cts.page, store, &gui_iter);
560   cts.page++;
561
562   /* select the main GUI page as the default page and expand its children */
563   gtk_tree_selection_select_iter(selection, &gui_iter);
564   /* (expand will only take effect, when at least one child exists) */
565   gtk_tree_view_expand_all(GTK_TREE_VIEW(cts.tree));
566
567 #ifdef HAVE_LIBPCAP
568 #ifdef _WIN32
569   /* Is WPcap loaded? */
570   if (has_wpcap) {
571 #endif /* _WIN32 */
572   /* capture prefs */
573   g_strlcpy(label_str, "Capture", MAX_TREE_NODE_NAME_LEN);
574   prefs_nb_page_add(prefs_nb, label_str, capture_prefs_show(), E_CAPTURE_PAGE_KEY);
575   capture_iter = prefs_tree_page_add(label_str, cts.page, store, NULL);
576   capture_page = cts.page++;
577 #ifdef _WIN32
578   }
579 #endif /* _WIN32 */
580 #endif /* HAVE_LIBPCAP */
581
582   /* Saved filter prefs */
583   g_strlcpy(label_str, "Filter Expressions", MAX_TREE_NODE_NAME_LEN);
584   prefs_nb_page_add(prefs_nb, label_str, filter_expressions_prefs_show(),
585     E_FILTER_EXPRESSIONS_PAGE_KEY);
586   prefs_tree_page_add(label_str, cts.page, store, NULL);
587   cts.page++;
588
589   /* Registered prefs */
590   cts.notebook = prefs_nb;
591   cts.store = store;
592   prefs_modules_foreach_submodules(NULL, module_prefs_show, &cts);
593
594   /* Button row: OK and alike buttons */
595   bbox = dlg_button_row_new(GTK_STOCK_HELP, GTK_STOCK_OK, GTK_STOCK_APPLY, GTK_STOCK_SAVE, GTK_STOCK_CANCEL, NULL);
596   gtk_box_pack_start(GTK_BOX(cts.main_vb), bbox, FALSE, FALSE, 0);
597   gtk_widget_show(bbox);
598
599   ok_bt = (GtkWidget *)g_object_get_data(G_OBJECT(bbox), GTK_STOCK_OK);
600   g_signal_connect(ok_bt, "clicked", G_CALLBACK(prefs_main_ok_cb), prefs_w);
601
602   apply_bt = (GtkWidget *)g_object_get_data(G_OBJECT(bbox), GTK_STOCK_APPLY);
603   g_signal_connect(apply_bt, "clicked", G_CALLBACK(prefs_main_apply_cb), prefs_w);
604
605   save_bt = (GtkWidget *)g_object_get_data(G_OBJECT(bbox), GTK_STOCK_SAVE);
606   g_signal_connect(save_bt, "clicked", G_CALLBACK(prefs_main_save_cb), prefs_w);
607   g_object_set_data(G_OBJECT(prefs_w), E_PREFSW_SAVE_BT_KEY, save_bt);
608
609   cancel_bt = (GtkWidget *)g_object_get_data(G_OBJECT(bbox), GTK_STOCK_CANCEL);
610   g_signal_connect(cancel_bt, "clicked", G_CALLBACK(prefs_main_cancel_cb), prefs_w);
611   window_set_cancel_button(prefs_w, cancel_bt, NULL);
612
613   gtk_widget_grab_default(ok_bt);
614
615   help_bt = (GtkWidget *)g_object_get_data(G_OBJECT(bbox), GTK_STOCK_HELP);
616   g_signal_connect(help_bt, "clicked", G_CALLBACK(topic_cb), (gpointer)HELP_PREFERENCES_DIALOG);
617
618   g_signal_connect(prefs_w, "delete_event", G_CALLBACK(prefs_main_delete_event_cb), NULL);
619   g_signal_connect(prefs_w, "destroy", G_CALLBACK(prefs_main_destroy_cb), prefs_w);
620
621   gtk_widget_show(prefs_w);
622
623   /* hide the Save button if the user uses implicit save */
624   if (!prefs.gui_use_pref_save) {
625     gtk_widget_hide(save_bt);
626   }
627
628   window_present(prefs_w);
629
630   switch (prefs_page) {
631   case PREFS_PAGE_LAYOUT:
632     gtk_tree_selection_select_iter(selection, &layout_iter);
633     gtk_notebook_set_current_page((GtkNotebook *)g_object_get_data(G_OBJECT(prefs_w), E_PREFSW_NOTEBOOK_KEY), layout_page);
634     break;
635   case PREFS_PAGE_COLUMNS:
636     gtk_tree_selection_select_iter(selection, &columns_iter);
637     gtk_notebook_set_current_page((GtkNotebook *)g_object_get_data(G_OBJECT(prefs_w), E_PREFSW_NOTEBOOK_KEY), columns_page);
638     break;
639   case PREFS_PAGE_CAPTURE:
640     if (capture_page) {
641       gtk_tree_selection_select_iter(selection, &capture_iter);
642       gtk_notebook_set_current_page((GtkNotebook *)g_object_get_data(G_OBJECT(prefs_w), E_PREFSW_NOTEBOOK_KEY), capture_page);
643     }
644     break;
645   default:
646     /* Not implemented yet */
647     break;
648   }
649
650   g_object_unref(G_OBJECT(store));
651 }
652
653 static void
654 set_option_label(GtkWidget *main_grid, int grid_position,
655     const gchar *label_text, const gchar *tooltip_text)
656 {
657   GtkWidget *label;
658   GtkWidget *event_box;
659
660   label = gtk_label_new(label_text);
661   gtk_misc_set_alignment(GTK_MISC(label), 1.0f, 0.5f);
662   gtk_widget_show(label);
663
664   event_box = gtk_event_box_new();
665   gtk_event_box_set_visible_window (GTK_EVENT_BOX(event_box), FALSE);
666   ws_gtk_grid_attach_defaults(GTK_GRID(main_grid), event_box, 0, grid_position, 1, 1);
667   if (tooltip_text != NULL)
668     gtk_widget_set_tooltip_text(event_box, tooltip_text);
669   gtk_container_add(GTK_CONTAINER(event_box), label);
670   gtk_widget_show(event_box);
671 }
672
673 GtkWidget *
674 create_preference_check_button(GtkWidget *main_grid, int grid_position,
675     const gchar *label_text, const gchar *tooltip_text, gboolean active)
676 {
677   GtkWidget *check_box;
678
679   set_option_label(main_grid, grid_position, label_text, tooltip_text);
680
681   check_box = gtk_check_button_new();
682   gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check_box), active);
683   ws_gtk_grid_attach_defaults(GTK_GRID(main_grid), check_box, 1, grid_position, 1, 1);
684   if (tooltip_text != NULL)
685     gtk_widget_set_tooltip_text(check_box, tooltip_text);
686
687   return check_box;
688 }
689
690 GtkWidget *
691 create_preference_radio_buttons(GtkWidget *main_grid, int grid_position,
692     const gchar *label_text, const gchar *tooltip_text,
693     const enum_val_t *enumvals, gint current_val)
694 {
695   GtkWidget        *radio_button_hbox, *button = NULL;
696   GSList           *rb_group;
697   int               idx;
698   const enum_val_t *enum_valp;
699   GtkWidget        *event_box;
700
701   set_option_label(main_grid, grid_position, label_text, tooltip_text);
702
703   radio_button_hbox = ws_gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0, FALSE);
704   rb_group = NULL;
705   for (enum_valp = enumvals, idx = 0; enum_valp->name != NULL;
706        enum_valp++, idx++) {
707     button = gtk_radio_button_new_with_label(rb_group,
708                                              enum_valp->description);
709     gtk_widget_show(button);
710     rb_group = gtk_radio_button_get_group(GTK_RADIO_BUTTON(button));
711     gtk_box_pack_start(GTK_BOX(radio_button_hbox), button, FALSE,
712                        FALSE, 10);
713     if (enum_valp->value == current_val) {
714       gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button),
715                                    TRUE);
716     }
717   }
718   gtk_widget_show(radio_button_hbox);
719
720   event_box = gtk_event_box_new();
721   gtk_event_box_set_visible_window (GTK_EVENT_BOX(event_box), FALSE);
722   gtk_container_add(GTK_CONTAINER(event_box), radio_button_hbox);
723   ws_gtk_grid_attach_defaults(GTK_GRID(main_grid), event_box, 1, grid_position, 1, 1);
724   if (tooltip_text != NULL)
725     gtk_widget_set_tooltip_text(event_box, tooltip_text);
726   gtk_widget_show(event_box);
727
728   /*
729    * It doesn't matter which of the buttons we return - we fetch
730    * the value by looking at the entire radio button group to
731    * which it belongs, and we can get that from any button.
732    */
733   return button;
734 }
735
736 static gint
737 label_to_enum_val(GtkWidget *label, const enum_val_t *enumvals)
738 {
739   const gchar *label_string;
740   int i;
741
742   /* Get the label's text, and translate it to a value.
743      We match only the descriptions, as those are what appear in
744      the option menu items or as labels for radio buttons.
745      We fail if we don't find a match, as that "can't happen". */
746   label_string = gtk_label_get_text(GTK_LABEL(label));
747
748   for (i = 0; enumvals[i].name != NULL; i++) {
749     if (g_ascii_strcasecmp(label_string, enumvals[i].description) == 0) {
750       return enumvals[i].value;
751     }
752   }
753   g_assert_not_reached();
754   return -1;
755 }
756
757 gint
758 fetch_preference_radio_buttons_val(GtkWidget *button,
759     const enum_val_t *enumvals)
760 {
761   GSList *rb_group;
762   GSList *rb_entry;
763
764   /*
765    * Go through the list of of radio buttons in the button's group,
766    * and find the first one that's active.
767    */
768   rb_group = gtk_radio_button_get_group(GTK_RADIO_BUTTON(button));
769   button = NULL;
770   for (rb_entry = rb_group; rb_entry != NULL;
771        rb_entry = g_slist_next(rb_entry)) {
772     button = (GtkWidget *)rb_entry->data;
773     if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button)))
774       break;
775   }
776
777   /* OK, now return the value corresponding to that button's label. */
778   return label_to_enum_val(gtk_bin_get_child(GTK_BIN(button)), enumvals);
779 }
780
781 GtkWidget *
782 create_preference_option_menu(GtkWidget *main_grid, int grid_position,
783     const gchar *label_text, const gchar *tooltip_text,
784     const enum_val_t *enumvals, gint current_val)
785 {
786   GtkWidget        *menu_box, *combo_box;
787   int               menu_idx, idx;
788   const enum_val_t *enum_valp;
789   GtkWidget        *event_box;
790
791   set_option_label(main_grid, grid_position, label_text, tooltip_text);
792
793   /* Create a menu from the enumvals */
794   combo_box = gtk_combo_box_text_new();
795   if (tooltip_text != NULL)
796     gtk_widget_set_tooltip_text(combo_box, tooltip_text);
797   menu_idx = 0;
798   for (enum_valp = enumvals, idx = 0; enum_valp->name != NULL;
799        enum_valp++, idx++) {
800      gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (combo_box), enum_valp->description);
801     if (enum_valp->value == current_val)
802       menu_idx = idx;
803   }
804   /* Set the current value active */
805   gtk_combo_box_set_active(GTK_COMBO_BOX(combo_box), menu_idx);
806
807   /*
808    * Put the combo box in an hbox, so that it's only as wide
809    * as the widest entry, rather than being as wide as the grid
810    * space.
811    */
812   menu_box = ws_gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0, FALSE);
813   gtk_box_pack_start(GTK_BOX(menu_box), combo_box, FALSE, FALSE, 0);
814
815   event_box = gtk_event_box_new();
816   gtk_event_box_set_visible_window (GTK_EVENT_BOX(event_box), FALSE);
817   ws_gtk_grid_attach_defaults(GTK_GRID(main_grid), event_box, 1, grid_position, 1, 1);
818   if (tooltip_text != NULL)
819     gtk_widget_set_tooltip_text(event_box, tooltip_text);
820   gtk_container_add(GTK_CONTAINER(event_box), menu_box);
821
822   return combo_box;
823 }
824
825 gint
826 fetch_preference_option_menu_val(GtkWidget *combo_box, const enum_val_t *enumvals)
827 {
828   /*
829    * OK, now return the value corresponding to the label for the
830    * currently active entry in the combo box.
831    */
832     int i;
833
834     i = gtk_combo_box_get_active (GTK_COMBO_BOX(combo_box));
835
836     return enumvals[i].value;
837 }
838
839 GtkWidget *
840 create_preference_entry(GtkWidget *main_grid, int grid_position,
841     const gchar *label_text, const gchar *tooltip_text, char *value)
842 {
843   GtkWidget *entry;
844
845   set_option_label(main_grid, grid_position, label_text, tooltip_text);
846
847   entry = gtk_entry_new();
848   if (value != NULL)
849     gtk_entry_set_text(GTK_ENTRY(entry), value);
850   ws_gtk_grid_attach_defaults(GTK_GRID(main_grid), entry, 1, grid_position, 1, 1);
851   if (tooltip_text != NULL)
852     gtk_widget_set_tooltip_text(entry, tooltip_text);
853   gtk_widget_show(entry);
854
855   return entry;
856 }
857
858 static void
859 preference_filename_entry_cb(GtkWidget *button, GtkWidget *filename_te)
860 {
861     /* XXX - use a better browser dialog title */
862     file_selection_browse(button, filename_te, "Wireshark: File preference",
863                           FILE_SELECTION_READ_BROWSE);
864 }
865
866 static void
867 preference_dirname_entry_cb(GtkWidget *button, GtkWidget *filename_te)
868 {
869     /* XXX - use a better browser dialog title */
870     file_selection_browse(button, filename_te, "Wireshark: Directory preference",
871                           FILE_SELECTION_CREATE_FOLDER);
872 }
873
874 static GtkWidget *
875 create_preference_path_entry(GtkWidget *main_grid, int grid_position,
876     const gchar *label_text, const gchar *tooltip_text, char *value, gboolean dir_only)
877 {
878   GtkWidget *entry;
879   GtkWidget *button, *file_bt_hb;
880
881   set_option_label(main_grid, grid_position, label_text, tooltip_text);
882   file_bt_hb = ws_gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0, FALSE);
883   ws_gtk_grid_attach_defaults(GTK_GRID(main_grid), file_bt_hb, 1, grid_position, 1, 1);
884   gtk_widget_show(file_bt_hb);
885
886   button = gtk_button_new_from_stock(WIRESHARK_STOCK_BROWSE);
887   gtk_box_pack_end(GTK_BOX(file_bt_hb), button, FALSE, FALSE, 0);
888   gtk_widget_show(button);
889
890   entry = gtk_entry_new();
891   gtk_box_pack_start(GTK_BOX(file_bt_hb), entry, TRUE, TRUE, 0);
892   if (value != NULL)
893     gtk_entry_set_text(GTK_ENTRY(entry), value);
894   if (tooltip_text != NULL)
895     gtk_widget_set_tooltip_text(entry, tooltip_text);
896   gtk_widget_show(entry);
897
898   if (dir_only) {
899     g_signal_connect(button, "clicked", G_CALLBACK(preference_dirname_entry_cb), entry);
900   } else {
901     g_signal_connect(button, "clicked", G_CALLBACK(preference_filename_entry_cb), entry);
902   }
903
904   return entry;
905 }
906
907 GtkWidget *
908 create_preference_static_text(GtkWidget *main_grid, int grid_position,
909     const gchar *label_text, const gchar *tooltip_text)
910 {
911   GtkWidget *label;
912
913   if (label_text != NULL)
914     label = gtk_label_new(label_text);
915   else
916     label = gtk_label_new("");
917   ws_gtk_grid_attach_defaults(GTK_GRID(main_grid), label, 0, grid_position, 2, 1);
918   if (tooltip_text != NULL)
919     gtk_widget_set_tooltip_text(label, tooltip_text);
920   gtk_widget_show(label);
921
922   return label;
923 }
924
925 GtkWidget *
926 create_preference_uat(GtkWidget *main_grid, int grid_position,
927     const gchar *label_text, const gchar *tooltip_text, void* uat)
928 {
929   GtkWidget *button;
930
931   set_option_label(main_grid, grid_position, label_text, tooltip_text);
932
933   button = gtk_button_new_from_stock(WIRESHARK_STOCK_EDIT);
934
935   g_signal_connect(button, "clicked", G_CALLBACK(uat_window_cb), uat);
936
937   ws_gtk_grid_attach_defaults(GTK_GRID(main_grid), button, 1, grid_position, 1, 1);
938   if (tooltip_text != NULL)
939     gtk_widget_set_tooltip_text(button, tooltip_text);
940   gtk_widget_show(button);
941
942   return button;
943 }
944
945
946 static guint
947 pref_check(pref_t *pref, gpointer user_data)
948 {
949   const char  *str_val;
950   char        *p;
951   pref_t     **badpref = (pref_t **)user_data;
952
953   /* Fetch the value of the preference, and check whether it's valid. */
954   switch (pref->type) {
955
956   case PREF_UINT:
957     str_val = gtk_entry_get_text(GTK_ENTRY(pref->control));
958     errno = 0;
959
960     /* XXX: The following ugly hack prevents a gcc warning
961        "ignoring return value of 'strtoul', declared with attribute warn_unused_result"
962        which can occur when using certain gcc configurations (see -D_FORTIFY_SOURCE).
963        A dummy variable is not used because when using gcc 4.6 with -Wextra a
964        "set but not used [-Wunused-but-set-variable]" warning will occur.
965        (Coverity & CLang apparently do not object to this hack).
966
967        [Guy Harris comment:
968         "... perhaps either using spin buttons for numeric preferences, or otherwise making
969          it impossible to type something that's not a number into the GUI for those preferences,
970          and thus avoiding the need to check whether it's a valid number, would also be a good idea."
971        ]
972     */
973     if (strtoul(str_val, &p, pref->info.base)){}
974     if (p == str_val || *p != '\0' || errno != 0) {
975       *badpref = pref;
976       return PREFS_SET_SYNTAX_ERR;      /* number was bad */
977     }
978     break;
979
980   case PREF_BOOL:
981     /* Value can't be bad. */
982     break;
983
984   case PREF_ENUM:
985     /* Value can't be bad. */
986     break;
987
988   case PREF_STRING:
989   case PREF_FILENAME:
990   case PREF_DIRNAME:
991     /* Value can't be bad. */
992     break;
993
994   case PREF_RANGE:
995     str_val = gtk_entry_get_text(GTK_ENTRY(pref->control));
996
997     if (strlen(str_val) != 0) {
998       range_t *newrange;
999
1000       if (range_convert_str(&newrange, str_val, pref->info.max_value) != CVT_NO_ERROR) {
1001         *badpref = pref;
1002         return PREFS_SET_SYNTAX_ERR;    /* range was bad */
1003       }
1004       g_free(newrange);
1005     }
1006     break;
1007
1008   case PREF_STATIC_TEXT:
1009   case PREF_UAT:
1010     /* Value can't be bad. */
1011     break;
1012
1013   case PREF_COLOR:
1014   case PREF_CUSTOM:
1015       /* currently not supported */
1016
1017   case PREF_OBSOLETE:
1018     g_assert_not_reached();
1019     break;
1020   }
1021   return 0;
1022 }
1023
1024 static guint
1025 module_prefs_check(module_t *module, gpointer user_data)
1026 {
1027   /* Ignore any preferences with their own interface */
1028   if (!module->use_gui) {
1029       return 0;
1030   }
1031
1032   /* For all preferences in this module, fetch its value from this
1033      module's notebook page and check whether it's valid. */
1034   return prefs_pref_foreach(module, pref_check, user_data);
1035 }
1036
1037 static guint
1038 pref_fetch(pref_t *pref, gpointer user_data)
1039 {
1040   const char *str_val;
1041   char       *p;
1042   guint       uval;
1043   gboolean    bval;
1044   gint        enumval;
1045   gboolean   *pref_changed_p = (gboolean *)user_data;
1046
1047   /* Fetch the value of the preference, and set the appropriate variable
1048      to it. */
1049   switch (pref->type) {
1050
1051   case PREF_UINT:
1052     str_val = gtk_entry_get_text(GTK_ENTRY(pref->control));
1053     uval = (guint)strtoul(str_val, &p, pref->info.base);
1054 #if 0
1055     if (p == value || *p != '\0')
1056       return PREFS_SET_SYNTAX_ERR;      /* number was bad */
1057 #endif
1058     if (*pref->varp.uint != uval) {
1059       *pref_changed_p = TRUE;
1060       *pref->varp.uint = uval;
1061     }
1062     break;
1063
1064   case PREF_BOOL:
1065     bval = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(pref->control));
1066     if (*pref->varp.boolp != bval) {
1067       *pref_changed_p = TRUE;
1068       *pref->varp.boolp = bval;
1069     }
1070     break;
1071
1072   case PREF_ENUM:
1073     if (pref->info.enum_info.radio_buttons) {
1074       enumval = fetch_preference_radio_buttons_val((GtkWidget *)pref->control,
1075           pref->info.enum_info.enumvals);
1076     } else {
1077       enumval = fetch_preference_option_menu_val((GtkWidget *)pref->control,
1078                                                  pref->info.enum_info.enumvals);
1079     }
1080
1081     if (*pref->varp.enump != enumval) {
1082       *pref_changed_p = TRUE;
1083       *pref->varp.enump = enumval;
1084     }
1085     break;
1086
1087   case PREF_STRING:
1088   case PREF_FILENAME:
1089   case PREF_DIRNAME:
1090     str_val = gtk_entry_get_text(GTK_ENTRY(pref->control));
1091     if (strcmp(*pref->varp.string, str_val) != 0) {
1092       *pref_changed_p = TRUE;
1093       g_free((void *)*pref->varp.string);
1094       *pref->varp.string = g_strdup(str_val);
1095     }
1096     break;
1097
1098   case PREF_RANGE:
1099   {
1100     range_t *newrange;
1101     convert_ret_t ret;
1102
1103     str_val = gtk_entry_get_text(GTK_ENTRY(pref->control));
1104     ret = range_convert_str(&newrange, str_val, pref->info.max_value);
1105     if (ret != CVT_NO_ERROR)
1106 #if 0
1107       return PREFS_SET_SYNTAX_ERR;      /* range was bad */
1108 #else
1109       return 0; /* XXX - should fail */
1110 #endif
1111
1112     if (!ranges_are_equal(*pref->varp.range, newrange)) {
1113       *pref_changed_p = TRUE;
1114       g_free(*pref->varp.range);
1115       *pref->varp.range = newrange;
1116     } else
1117       g_free(newrange);
1118
1119     break;
1120   }
1121
1122   case PREF_STATIC_TEXT:
1123   case PREF_UAT:
1124     break;
1125
1126   case PREF_COLOR:
1127   case PREF_CUSTOM:
1128       /* currently not supported */
1129
1130   case PREF_OBSOLETE:
1131     g_assert_not_reached();
1132     break;
1133   }
1134   return 0;
1135 }
1136
1137 static guint
1138 module_prefs_fetch(module_t *module, gpointer user_data)
1139 {
1140   gboolean *must_redissect_p = (gboolean *)user_data;
1141
1142   /* Ignore any preferences with their own interface */
1143   if (!module->use_gui) {
1144       return 0;
1145   }
1146
1147   /* For all preferences in this module, fetch its value from this
1148      module's notebook page.  Find out whether any of them changed. */
1149   module->prefs_changed = FALSE;        /* assume none of them changed */
1150   prefs_pref_foreach(module, pref_fetch, &module->prefs_changed);
1151
1152   /* If any of them changed, indicate that we must redissect and refilter
1153      the current capture (if we have one), as the preference change
1154      could cause packets to be dissected differently. */
1155   if (module->prefs_changed)
1156     *must_redissect_p = TRUE;
1157
1158   return 0;     /* keep fetching module preferences */
1159 }
1160
1161 #ifdef HAVE_AIRPCAP
1162 /*
1163  * This function is used to apply changes and update the Wireless Toolbar
1164  * whenever we apply some changes to the WEP preferences
1165  */
1166 static void
1167 prefs_airpcap_update(void)
1168 {
1169   GtkWidget *decryption_cm;
1170   gint       cur_active;
1171   gboolean   wireshark_decryption_was_enabled    = FALSE;
1172   gboolean   airpcap_decryption_was_enabled      = FALSE;
1173   gboolean   wireshark_decryption_is_now_enabled = FALSE;
1174
1175   decryption_cm = GTK_WIDGET(g_object_get_data(G_OBJECT(wireless_tb),AIRPCAP_TOOLBAR_DECRYPTION_KEY));
1176
1177   if (decryption_cm == NULL) {
1178     return;
1179   }
1180
1181   cur_active = gtk_combo_box_get_active(GTK_COMBO_BOX(decryption_cm));
1182
1183   if (cur_active < 0) {
1184     return;
1185   }
1186
1187   switch(cur_active) {
1188     /* XXX - Don't use magic numbers here. cf airpcap_dlg.c:on_decryption_mode_cb_changed() */
1189     case 1: /* Wireshark */
1190       wireshark_decryption_was_enabled = TRUE;
1191       airpcap_decryption_was_enabled = FALSE;
1192       break;
1193     case 2: /* Driver */
1194       wireshark_decryption_was_enabled = FALSE;
1195       airpcap_decryption_was_enabled = TRUE;
1196       break;
1197     default:
1198       wireshark_decryption_was_enabled = FALSE;
1199       airpcap_decryption_was_enabled = FALSE;
1200       break;
1201   }
1202
1203   wireshark_decryption_is_now_enabled = wireshark_decryption_on();
1204
1205   if (wireshark_decryption_is_now_enabled && airpcap_decryption_was_enabled)
1206   {
1207     set_airpcap_decryption(FALSE);
1208     gtk_combo_box_set_active(GTK_COMBO_BOX(decryption_cm), 1);
1209   }
1210   if (wireshark_decryption_is_now_enabled && !airpcap_decryption_was_enabled)
1211   {
1212     set_airpcap_decryption(FALSE);
1213     gtk_combo_box_set_active(GTK_COMBO_BOX(decryption_cm), 1);
1214   }
1215   else if (!wireshark_decryption_is_now_enabled && wireshark_decryption_was_enabled)
1216   {
1217     if (airpcap_decryption_was_enabled)
1218     {
1219       set_airpcap_decryption(TRUE);
1220       gtk_combo_box_set_active(GTK_COMBO_BOX(decryption_cm), 2);
1221     }
1222     else
1223     {
1224       set_airpcap_decryption(FALSE);
1225       gtk_combo_box_set_active(GTK_COMBO_BOX(decryption_cm), 0);
1226     }
1227   }
1228 }
1229 #endif
1230
1231 static guint
1232 module_prefs_clean_stash(module_t *module, gpointer user_data _U_)
1233 {
1234   /* Ignore any preferences with their own interface */
1235   if (!module->use_gui) {
1236       return 0;
1237   }
1238
1239   /* For all preferences in this module, clean up any cruft allocated for
1240      use by the GUI code. */
1241   prefs_pref_foreach(module, pref_clean_stash, NULL);
1242   return 0;     /* keep cleaning modules */
1243 }
1244
1245 /* fetch all pref values from all pages */
1246 static gboolean
1247 prefs_main_fetch_all(GtkWidget *dlg, gboolean *must_redissect)
1248 {
1249   pref_t *badpref;
1250
1251   /* First, check that the values are all valid. */
1252   /* XXX - check the non-registered preferences too */
1253   switch (prefs_modules_foreach(module_prefs_check, (gpointer)&badpref)) {
1254
1255     case PREFS_SET_SYNTAX_ERR:
1256       switch (badpref->type) {
1257
1258     case PREF_UINT:
1259       simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
1260                     "The value for \"%s\" isn't a valid number.",
1261                     badpref->title);
1262       return FALSE;
1263
1264     case PREF_RANGE:
1265       simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
1266                     "The value for \"%s\" isn't a valid range.",
1267                     badpref->title);
1268       return FALSE;
1269
1270     default:
1271       g_assert_not_reached();
1272       break;
1273     }
1274   }
1275
1276   /* Fetch the preferences (i.e., make sure all the values set in all of
1277      the preferences panes have been copied to "prefs" and the registered
1278      preferences). */
1279   gui_prefs_fetch((GtkWidget *)g_object_get_data(G_OBJECT(dlg), E_GUI_PAGE_KEY));
1280   layout_prefs_fetch((GtkWidget *)g_object_get_data(G_OBJECT(dlg), E_GUI_LAYOUT_PAGE_KEY));
1281   column_prefs_fetch((GtkWidget *)g_object_get_data(G_OBJECT(dlg), E_GUI_COLUMN_PAGE_KEY));
1282   font_color_prefs_fetch((GtkWidget *)g_object_get_data(G_OBJECT(dlg), E_GUI_FONT_COLORS_PAGE_KEY));
1283
1284 #ifdef HAVE_LIBPCAP
1285 #ifdef _WIN32
1286   /* Is WPcap loaded? */
1287   if (has_wpcap) {
1288 #endif /* _WIN32 */
1289   capture_prefs_fetch((GtkWidget *)g_object_get_data(G_OBJECT(dlg), E_CAPTURE_PAGE_KEY));
1290 #ifdef _WIN32
1291   }
1292 #endif /* _WIN32 */
1293 #endif /* HAVE_LIBPCAP */
1294   filter_expressions_prefs_fetch((GtkWidget *)g_object_get_data(G_OBJECT(dlg),
1295     E_FILTER_EXPRESSIONS_PAGE_KEY));
1296   prefs_modules_foreach(module_prefs_fetch, must_redissect);
1297
1298   return TRUE;
1299 }
1300
1301 /* apply all pref values to the real world */
1302 static void
1303 prefs_main_apply_all(GtkWidget *dlg, gboolean redissect)
1304 {
1305   GtkWidget *save_bt;
1306
1307   /*
1308    * Apply the protocol preferences first - "gui_prefs_apply()" could
1309    * cause redissection, and we have to make sure the protocol
1310    * preference changes have been fully applied.
1311    */
1312   prefs_apply_all();
1313
1314   gui_prefs_apply((GtkWidget *)g_object_get_data(G_OBJECT(dlg), E_GUI_PAGE_KEY));
1315   layout_prefs_apply((GtkWidget *)g_object_get_data(G_OBJECT(dlg), E_GUI_LAYOUT_PAGE_KEY));
1316   column_prefs_apply((GtkWidget *)g_object_get_data(G_OBJECT(dlg), E_GUI_COLUMN_PAGE_KEY));
1317   font_color_prefs_apply((GtkWidget *)g_object_get_data(G_OBJECT(dlg), E_GUI_FONT_COLORS_PAGE_KEY), redissect);
1318
1319 #ifdef HAVE_LIBPCAP
1320 #ifdef _WIN32
1321   /* Is WPcap loaded? */
1322   if (has_wpcap) {
1323 #endif /* _WIN32 */
1324   capture_prefs_apply((GtkWidget *)g_object_get_data(G_OBJECT(dlg), E_CAPTURE_PAGE_KEY));
1325 #ifdef _WIN32
1326   }
1327 #endif /* _WIN32 */
1328 #endif /* HAVE_LIBPCAP */
1329
1330   /* show/hide the Save button - depending on setting */
1331   save_bt = (GtkWidget *)g_object_get_data(G_OBJECT(prefs_w), E_PREFSW_SAVE_BT_KEY);
1332   if (prefs.gui_use_pref_save) {
1333     gtk_widget_show(save_bt);
1334   } else {
1335     gtk_widget_hide(save_bt);
1336   }
1337 }
1338
1339
1340 /* destroy all preferences ressources from all pages */
1341 static void
1342 prefs_main_destroy_all(GtkWidget *dlg)
1343 {
1344   int        page_num;
1345   GtkWidget *frame;
1346
1347   for (page_num = 0;
1348        (frame = gtk_notebook_get_nth_page((GtkNotebook *)g_object_get_data(G_OBJECT(prefs_w), E_PREFSW_NOTEBOOK_KEY), page_num)) != NULL;
1349        page_num++) {
1350     if (g_object_get_data(G_OBJECT(frame), E_PAGE_ITER_KEY))
1351       gtk_tree_iter_free((GtkTreeIter *)g_object_get_data(G_OBJECT(frame), E_PAGE_ITER_KEY));
1352   }
1353
1354   gui_prefs_destroy((GtkWidget *)g_object_get_data(G_OBJECT(dlg), E_GUI_PAGE_KEY));
1355   layout_prefs_destroy((GtkWidget *)g_object_get_data(G_OBJECT(dlg), E_GUI_LAYOUT_PAGE_KEY));
1356   column_prefs_destroy((GtkWidget *)g_object_get_data(G_OBJECT(dlg), E_GUI_COLUMN_PAGE_KEY));
1357   font_color_prefs_destroy((GtkWidget *)g_object_get_data(G_OBJECT(dlg), E_GUI_FONT_COLORS_PAGE_KEY));
1358
1359 #ifdef HAVE_LIBPCAP
1360 #ifdef _WIN32
1361   /* Is WPcap loaded? */
1362   if (has_wpcap) {
1363 #endif /* _WIN32 */
1364   capture_prefs_destroy((GtkWidget *)g_object_get_data(G_OBJECT(dlg), E_CAPTURE_PAGE_KEY));
1365 #ifdef _WIN32
1366   }
1367 #endif /* _WIN32 */
1368 #endif /* HAVE_LIBPCAP */
1369
1370   /* Free up the saved preferences (both for "prefs" and for registered
1371      preferences). */
1372   prefs_modules_foreach(module_prefs_clean_stash, NULL);
1373 }
1374
1375 static guint
1376 module_prefs_copy(module_t *module, gpointer user_data _U_)
1377 {
1378   /* Ignore any preferences with their own interface */
1379   if (!module->use_gui) {
1380       return 0;
1381   }
1382
1383   /* For all preferences in this module, (re)save current value */
1384   prefs_pref_foreach(module, pref_stash, NULL);
1385   return 0;     /* continue making copies */
1386 }
1387
1388 /* Copy prefs to saved values so we can revert to these values */
1389 /*  if the user selects Cancel.                                */
1390 static void prefs_copy(void) {
1391   prefs_modules_foreach(module_prefs_copy, NULL);
1392 }
1393
1394 static void
1395 overwrite_existing_prefs_cb(gpointer dialog _U_, gint btn, gpointer parent_w)
1396 {
1397   switch (btn) {
1398     case(ESD_BTN_SAVE):
1399       prefs_main_write();
1400           prefs.unknown_prefs = FALSE;
1401       break;
1402     case(ESD_BTN_DONT_SAVE):
1403       break;
1404     default:
1405       g_assert_not_reached();
1406   }
1407 }
1408 static void
1409 prefs_main_save(gpointer parent_w)
1410 {
1411   if (prefs.unknown_prefs) {
1412     gpointer dialog;
1413     gchar *msg;
1414     gchar *msg1 = "These preferences were last saved at version ";
1415     gchar *msg2 = "Obsolete or unrecognized preferences have been detected. "
1416                   "If you plan to use this profile with ";
1417     gchar *msg3 = "that version of Wireshark, ";
1418     gchar *msg4 = "an older or nonstandard Wireshark version, ";
1419     gchar *msg5 = "click 'Continue without Saving' and save this profile under a another name.\n";
1420     
1421     if (prefs.saved_at_version) {
1422       gulong tot_msg_len = 246 + (gulong)strlen(prefs.saved_at_version) + 1;
1423       
1424       msg = (gchar *)g_malloc(tot_msg_len);
1425       g_snprintf(msg, tot_msg_len, "%s\"%s\". %s%s%s",
1426         msg1, prefs.saved_at_version, msg2, msg3, msg5);
1427       dialog = simple_dialog(ESD_TYPE_CONFIRMATION, ESD_BTNS_SAVE_DONTSAVE, msg);
1428     } else { 
1429       msg = (gchar *)g_malloc(214);
1430
1431       g_snprintf(msg, 214, "%s%s%s", msg2, msg4, msg5);
1432       dialog = simple_dialog(ESD_TYPE_CONFIRMATION, ESD_BTNS_SAVE_DONTSAVE, msg);
1433     }
1434
1435     simple_dialog_set_cb(dialog, overwrite_existing_prefs_cb, parent_w);
1436     g_free(msg);
1437     
1438   } else {
1439     prefs_main_write();
1440   }
1441 }
1442
1443 static void
1444 prefs_main_ok_cb(GtkWidget *ok_bt _U_, gpointer parent_w)
1445 {
1446   gboolean must_redissect = FALSE;
1447
1448   if (!prefs_main_fetch_all((GtkWidget *)parent_w, &must_redissect))
1449     return; /* Errors in some preference setting - already reported */
1450
1451   /* if we don't have a Save button, just save the settings now */
1452   if (!prefs.gui_use_pref_save) {
1453     prefs_main_save(parent_w);
1454   }
1455
1456 #ifdef HAVE_AIRPCAP
1457   /*
1458    * Load the Wireshark decryption keys (just set) and save
1459    * the changes to the adapters' registry
1460    */
1461   airpcap_load_decryption_keys(airpcap_if_list);
1462 #endif
1463
1464   prefs_main_apply_all((GtkWidget *)parent_w, must_redissect);
1465
1466   /* Fill in capture options with values from the preferences */
1467   prefs_to_capture_opts();
1468
1469 #ifdef HAVE_AIRPCAP
1470   prefs_airpcap_update();
1471 #endif
1472
1473   /* Now destroy the "Preferences" dialog. */
1474   window_destroy(GTK_WIDGET(parent_w));
1475
1476   if (must_redissect) {
1477     /* Redissect all the packets, and re-evaluate the display filter. */
1478     redissect_packets();
1479     redissect_all_packet_windows();
1480   }
1481
1482 }
1483
1484 static void
1485 prefs_main_apply_cb(GtkWidget *apply_bt _U_, gpointer parent_w)
1486 {
1487   gboolean must_redissect = FALSE;
1488
1489   if (!prefs_main_fetch_all((GtkWidget *)parent_w, &must_redissect))
1490     return; /* Errors in some preference setting - already reported */
1491
1492   /* if we don't have a Save button, just save the settings now */
1493   if (!prefs.gui_use_pref_save) {
1494     prefs_main_save(parent_w);
1495     prefs_copy();     /* save prefs for reverting if Cancel */
1496   }
1497
1498   prefs_main_apply_all((GtkWidget *)parent_w, must_redissect);
1499
1500   /* Fill in capture options with values from the preferences */
1501   prefs_to_capture_opts();
1502
1503 #ifdef HAVE_AIRPCAP
1504   prefs_airpcap_update();
1505 #endif
1506
1507   if (must_redissect) {
1508     /* Redissect all the packets, and re-evaluate the display filter. */
1509     redissect_packets();
1510     redissect_all_packet_windows();
1511   }
1512 }
1513
1514 static void
1515 prefs_main_save_cb(GtkWidget *save_bt _U_, gpointer parent_w)
1516 {
1517   gboolean must_redissect = FALSE;
1518
1519   if (!prefs_main_fetch_all((GtkWidget *)parent_w, &must_redissect))
1520     return; /* Errors in some preference setting - already reported */
1521
1522   prefs_main_save(parent_w);
1523   prefs_copy();     /* save prefs for reverting if Cancel */
1524
1525   /* Now apply those preferences.
1526      XXX - should we do this?  The user didn't click "OK" or "Apply".
1527      However:
1528
1529         1) by saving the preferences they presumably indicate that they
1530            like them;
1531
1532         2) the next time they fire Wireshark up, those preferences will
1533            apply;
1534
1535         3) we'd have to buffer "must_redissect" so that if they do
1536            "Apply" after this, we know we have to redissect;
1537
1538         4) we did apply the protocol preferences, at least, in the past. */
1539   prefs_main_apply_all((GtkWidget *)parent_w, must_redissect);
1540
1541   /* Fill in capture options with values from the preferences */
1542   prefs_to_capture_opts();
1543
1544   if (must_redissect) {
1545     /* Redissect all the packets, and re-evaluate the display filter. */
1546     redissect_packets();
1547     redissect_all_packet_windows();
1548   }
1549 }
1550
1551 static guint
1552 module_prefs_revert(module_t *module, gpointer user_data)
1553 {
1554   gboolean *must_redissect_p = (gboolean *)user_data;
1555
1556   /* Ignore any preferences with their own interface */
1557   if (!module->use_gui) {
1558       return 0;
1559   }
1560
1561   /* For all preferences in this module, revert its value to the value
1562      it had when we popped up the Preferences dialog.  Find out whether
1563      this changes any of them. */
1564   module->prefs_changed = FALSE;        /* assume none of them changed */
1565   prefs_pref_foreach(module, pref_unstash, &module->prefs_changed);
1566
1567   /* If any of them changed, indicate that we must redissect and refilter
1568      the current capture (if we have one), as the preference change
1569      could cause packets to be dissected differently. */
1570   if (module->prefs_changed)
1571     *must_redissect_p = TRUE;
1572   return 0;     /* keep processing modules */
1573 }
1574
1575 /* cancel button pressed, revert prefs to saved and exit dialog */
1576 static void
1577 prefs_main_cancel_cb(GtkWidget *cancel_bt _U_, gpointer parent_w)
1578 {
1579   gboolean must_redissect = FALSE;
1580
1581   /* Free up the current preferences and copy the saved preferences to the
1582      current preferences. */
1583   cfile.columns_changed = FALSE; /* [XXX: "columns_changed" should treally be stored in prefs struct ??] */
1584
1585   /* Now revert the registered preferences. */
1586   prefs_modules_foreach(module_prefs_revert, &must_redissect);
1587
1588   /* Now apply the reverted-to preferences. */
1589   prefs_main_apply_all((GtkWidget *)parent_w, must_redissect);
1590
1591   window_destroy(GTK_WIDGET(parent_w));
1592
1593   if (must_redissect) {
1594     /* Redissect all the packets, and re-evaluate the display filter. */
1595     redissect_packets();
1596     redissect_all_packet_windows();
1597   }
1598 }
1599
1600 /* Treat this as a cancel, by calling "prefs_main_cancel_cb()" */
1601 static gboolean
1602 prefs_main_delete_event_cb(GtkWidget *prefs_w_lcl, GdkEvent *event _U_,
1603                            gpointer user_data _U_)
1604 {
1605   prefs_main_cancel_cb(NULL, prefs_w_lcl);
1606   return FALSE;
1607 }
1608
1609
1610 /* dialog *is* already destroyed, clean up memory and such */
1611 static void
1612 prefs_main_destroy_cb(GtkWidget *win _U_, gpointer parent_w)
1613 {
1614   prefs_main_destroy_all((GtkWidget *)parent_w);
1615
1616   /* Note that we no longer have a "Preferences" dialog box. */
1617   prefs_w = NULL;
1618 }
1619
1620 struct properties_data {
1621   const char *title;
1622   module_t   *module;
1623 };
1624
1625 static guint
1626 module_search_properties(module_t *module, gpointer user_data)
1627 {
1628   struct properties_data *p = (struct properties_data *)user_data;
1629
1630   if (!module->use_gui) {
1631       /* This module uses its own GUI interface, so its not a part
1632        * of this search
1633        */
1634       return 0;
1635   }
1636
1637   /* If this module has the specified title, remember it. */
1638   if (strcmp(module->title, p->title) == 0) {
1639     p->module = module;
1640     return 1;   /* stops the search */
1641   }
1642
1643   if (prefs_module_has_submodules(module))
1644     return prefs_modules_foreach_submodules(module, module_search_properties, p);
1645
1646   return 0;
1647 }
1648
1649 static void
1650 tree_expand_row(GtkTreeModel *model, GtkTreeView *tree_view, GtkTreeIter *iter)
1651 {
1652   GtkTreeIter  parent;
1653   GtkTreePath *path;
1654
1655   /* expand the parent first */
1656   if (gtk_tree_model_iter_parent(model, &parent, iter))
1657     tree_expand_row(model, tree_view, &parent);
1658
1659   path = gtk_tree_model_get_path(model, iter);
1660   gtk_tree_view_expand_row(tree_view, path, FALSE);
1661   /*expand_tree(tree_view, &parent, NULL, NULL);*/
1662
1663   gtk_tree_path_free(path);
1664 }
1665
1666 /* select a node in the tree view */
1667 /* XXX - this is almost 100% copied from byte_view_select() in proto_draw.c,
1668  *       find a way to combine both to have a generic function for this */
1669 static void
1670 tree_select_node(GtkWidget *tree, prefs_tree_iter *iter)
1671 {
1672   GtkTreeIter   local_iter = *iter;
1673   GtkTreeView  *tree_view  = GTK_TREE_VIEW(tree);
1674   GtkTreeModel *model;
1675   GtkTreePath  *first_path;
1676
1677   model = gtk_tree_view_get_model(tree_view);
1678
1679   /* Expand our field's row */
1680   first_path = gtk_tree_model_get_path(model, &local_iter);
1681
1682   /* expand from the top down */
1683   tree_expand_row(model, tree_view, &local_iter);
1684
1685   /* select our field's row */
1686   gtk_tree_selection_select_path(gtk_tree_view_get_selection(tree_view),
1687                                  first_path);
1688
1689   /* And position the window so the selection is visible.
1690    * Position the selection in the middle of the viewable
1691    * pane. */
1692   gtk_tree_view_scroll_to_cell(tree_view, first_path, NULL, TRUE, 0.5f, 0.0f);
1693
1694   gtk_tree_path_free(first_path);
1695 }
1696
1697
1698 /* search the corresponding protocol page of the currently selected field */
1699 void
1700 properties_cb(GtkWidget *w, gpointer dummy)
1701 {
1702   header_field_info      *hfinfo;
1703   const gchar            *title = NULL;
1704   struct properties_data  p;
1705   int                     page_num;
1706   GtkWidget              *sw;
1707   GtkWidget              *frame;
1708   module_t               *page_module;
1709
1710   if (cfile.finfo_selected == NULL) {
1711     const gchar *abbrev;
1712
1713     /* There is no field selected, try use on top protocol */
1714     if (cfile.edt && cfile.edt->tree) {
1715         GPtrArray          *ga;
1716         field_info         *v;
1717         guint              i;
1718
1719         ga = proto_all_finfos(cfile.edt->tree);
1720
1721         for (i = ga->len - 1; i > 0 ; i -= 1) {
1722
1723             v = (field_info *)g_ptr_array_index (ga, i);
1724             hfinfo =  v->hfinfo;
1725
1726             if (!g_str_has_prefix(hfinfo->abbrev, "text") &&
1727                     !g_str_has_prefix(hfinfo->abbrev, "expert") &&
1728                     !g_str_has_prefix(hfinfo->abbrev, "malformed")) {
1729                 if (hfinfo->parent == -1) {
1730                     abbrev = hfinfo->abbrev;
1731                 } else {
1732                     abbrev = proto_registrar_get_abbrev(hfinfo->parent);
1733                 }
1734                 title = prefs_get_title_by_name(abbrev);
1735                 break;
1736             }
1737         }
1738     }
1739   } else {
1740     /* Find the title for the protocol for the selected field. */
1741     hfinfo = cfile.finfo_selected->hfinfo;
1742     if (hfinfo->parent == -1)
1743         title = prefs_get_title_by_name(hfinfo->abbrev);
1744     else
1745         title = prefs_get_title_by_name(proto_registrar_get_abbrev(hfinfo->parent));
1746   }
1747
1748   if (!title)
1749     return;     /* Couldn't find it. XXX - just crash? "Can't happen"? */
1750
1751   /* Find the module for that protocol by searching for one with that title.
1752      XXX - should we just associate protocols with modules directly? */
1753   p.title = title;
1754   p.module = NULL;
1755   prefs_modules_foreach_submodules(protocols_module, module_search_properties,
1756                             &p);
1757   if (p.module == NULL) {
1758     /* We didn't find it - that protocol probably has no preferences. */
1759     return;
1760   }
1761
1762   /* Create a preferences window, or pop up an existing one. */
1763   if (prefs_w != NULL) {
1764     reactivate_window(prefs_w);
1765   } else {
1766     prefs_cb(w, dummy);
1767   }
1768
1769   /* Search all the pages in that window for the one with the specified
1770      module. */
1771   for (page_num = 0;
1772        (sw = gtk_notebook_get_nth_page((GtkNotebook *)g_object_get_data(G_OBJECT(prefs_w), E_PREFSW_NOTEBOOK_KEY), page_num)) != NULL;
1773        page_num++) {
1774     /* Get the frame from the scrollable window */
1775     frame = (GtkWidget *)g_object_get_data(G_OBJECT(sw), E_PAGESW_FRAME_KEY);
1776     /* Get the module for this page (non-protocol prefs don't have one). */
1777     if (frame) {
1778       page_module = (module_t *)g_object_get_data(G_OBJECT(frame), E_PAGE_MODULE_KEY);
1779       if (page_module != NULL) {
1780         if (page_module == p.module) {
1781           tree_select_node(
1782             (GtkWidget *)g_object_get_data(G_OBJECT(prefs_w), E_PREFSW_TREE_KEY),
1783             (GtkTreeIter *)g_object_get_data(G_OBJECT(frame), E_PAGE_ITER_KEY));
1784           return;
1785         }
1786       }
1787     }
1788   }
1789 }
1790
1791 /* Prefs tree selection callback.  The node data has been loaded with
1792    the proper notebook page to load. */
1793 static void
1794 prefs_tree_select_cb(GtkTreeSelection *sel, gpointer dummy _U_)
1795 {
1796   gint          page;
1797   GtkTreeModel *model;
1798   GtkTreeIter   iter;
1799
1800   if (gtk_tree_selection_get_selected(sel, &model, &iter))
1801   {
1802     gtk_tree_model_get(model, &iter, 1, &page, -1);
1803     if (page >= 0)
1804       gtk_notebook_set_current_page((GtkNotebook *)g_object_get_data(G_OBJECT(prefs_w), E_PREFSW_NOTEBOOK_KEY), page);
1805   }
1806 }
1807
1808
1809
1810 /*
1811  * Editor modelines
1812  *
1813  * Local Variables:
1814  * c-basic-offset: 2
1815  * tab-width: 8
1816  * indent-tabs-mode: nil
1817  * End:
1818  *
1819  * ex: set shiftwidth=2 tabstop=8 expandtab:
1820  * :indentSize=2:tabSize=8:noTabs=true:
1821  */