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