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