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