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