d542ac46fef42249e115fcaa758195ff8d264470
[obnox/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   guint uval;
929   pref_t **badpref = user_data;
930
931   /* Fetch the value of the preference, and check whether it's valid. */
932   switch (pref->type) {
933
934   case PREF_UINT:
935     str_val = gtk_entry_get_text(GTK_ENTRY(pref->control));
936     uval = strtoul(str_val, &p, pref->info.base);
937     if (p == str_val || *p != '\0') {
938       *badpref = pref;
939       return PREFS_SET_SYNTAX_ERR;      /* number was bad */
940     }
941     break;
942
943   case PREF_BOOL:
944     /* Value can't be bad. */
945     break;
946
947   case PREF_ENUM:
948     /* Value can't be bad. */
949     break;
950
951   case PREF_STRING:
952     /* Value can't be bad. */
953     break;
954
955   case PREF_RANGE:
956     str_val = gtk_entry_get_text(GTK_ENTRY(pref->control));
957
958     if (strlen(str_val) != 0) {
959       range_t *newrange;
960
961       if (range_convert_str(&newrange, str_val, pref->info.max_value) != CVT_NO_ERROR) {
962         *badpref = pref;
963         return PREFS_SET_SYNTAX_ERR;    /* range was bad */
964       }
965       g_free(newrange);
966     }
967     break;
968
969   case PREF_STATIC_TEXT:
970   case PREF_UAT:
971     /* Value can't be bad. */
972     break;
973
974   case PREF_OBSOLETE:
975     g_assert_not_reached();
976     break;
977   }
978   return 0;
979 }
980
981 static guint
982 module_prefs_check(module_t *module, gpointer user_data)
983 {
984   /* For all preferences in this module, fetch its value from this
985      module's notebook page and check whether it's valid. */
986   return prefs_pref_foreach(module, pref_check, user_data);
987 }
988
989 static guint
990 pref_fetch(pref_t *pref, gpointer user_data)
991 {
992   const char *str_val;
993   char *p;
994   guint uval;
995   gboolean bval;
996   gint enumval;
997   gboolean *pref_changed_p = user_data;
998
999   /* Fetch the value of the preference, and set the appropriate variable
1000      to it. */
1001   switch (pref->type) {
1002
1003   case PREF_UINT:
1004     str_val = gtk_entry_get_text(GTK_ENTRY(pref->control));
1005     uval = strtoul(str_val, &p, pref->info.base);
1006 #if 0
1007     if (p == value || *p != '\0')
1008       return PREFS_SET_SYNTAX_ERR;      /* number was bad */
1009 #endif
1010     if (*pref->varp.uint != uval) {
1011       *pref_changed_p = TRUE;
1012       *pref->varp.uint = uval;
1013     }
1014     break;
1015
1016   case PREF_BOOL:
1017     bval = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(pref->control));
1018     if (*pref->varp.boolp != bval) {
1019       *pref_changed_p = TRUE;
1020       *pref->varp.boolp = bval;
1021     }
1022     break;
1023
1024   case PREF_ENUM:
1025     if (pref->info.enum_info.radio_buttons) {
1026       enumval = fetch_preference_radio_buttons_val(pref->control,
1027           pref->info.enum_info.enumvals);
1028     } else {
1029       enumval = fetch_preference_option_menu_val(pref->control,
1030                                                  pref->info.enum_info.enumvals);
1031     }
1032
1033     if (*pref->varp.enump != enumval) {
1034       *pref_changed_p = TRUE;
1035       *pref->varp.enump = enumval;
1036     }
1037     break;
1038
1039   case PREF_STRING:
1040     str_val = gtk_entry_get_text(GTK_ENTRY(pref->control));
1041     if (strcmp(*pref->varp.string, str_val) != 0) {
1042       *pref_changed_p = TRUE;
1043       g_free((void *)*pref->varp.string);
1044       *pref->varp.string = g_strdup(str_val);
1045     }
1046     break;
1047
1048   case PREF_RANGE:
1049   {
1050     range_t *newrange;
1051     convert_ret_t ret;
1052
1053     str_val = gtk_entry_get_text(GTK_ENTRY(pref->control));
1054     ret = range_convert_str(&newrange, str_val, pref->info.max_value);
1055     if (ret != CVT_NO_ERROR)
1056 #if 0
1057       return PREFS_SET_SYNTAX_ERR;      /* range was bad */
1058 #else
1059       return 0; /* XXX - should fail */
1060 #endif
1061
1062     if (!ranges_are_equal(*pref->varp.range, newrange)) {
1063       *pref_changed_p = TRUE;
1064       g_free(*pref->varp.range);
1065       *pref->varp.range = newrange;
1066     } else
1067       g_free(newrange);
1068
1069     break;
1070   }
1071
1072   case PREF_STATIC_TEXT:
1073   case PREF_UAT:
1074     break;
1075
1076   case PREF_OBSOLETE:
1077     g_assert_not_reached();
1078     break;
1079   }
1080   return 0;
1081 }
1082
1083 static guint
1084 module_prefs_fetch(module_t *module, gpointer user_data)
1085 {
1086   gboolean *must_redissect_p = user_data;
1087
1088   /* For all preferences in this module, fetch its value from this
1089      module's notebook page.  Find out whether any of them changed. */
1090   module->prefs_changed = FALSE;        /* assume none of them changed */
1091   prefs_pref_foreach(module, pref_fetch, &module->prefs_changed);
1092
1093   /* If any of them changed, indicate that we must redissect and refilter
1094      the current capture (if we have one), as the preference change
1095      could cause packets to be dissected differently. */
1096   if (module->prefs_changed)
1097     *must_redissect_p = TRUE;
1098
1099   return 0;     /* keep fetching module preferences */
1100 }
1101
1102 #ifdef HAVE_AIRPCAP
1103 /*
1104  * This function is used to apply changes and update the Wireless Toolbar
1105  * whenever we apply some changes to the WEP preferences
1106  */
1107 static void
1108 prefs_airpcap_update(void)
1109 {
1110   GtkWidget *decryption_cm;
1111   GtkWidget *decryption_en;
1112   gboolean wireshark_decryption_was_enabled = FALSE;
1113   gboolean airpcap_decryption_was_enabled = FALSE;
1114   gboolean wireshark_decryption_is_now_enabled = FALSE;
1115
1116   decryption_cm = GTK_WIDGET(g_object_get_data(G_OBJECT(airpcap_tb),AIRPCAP_TOOLBAR_DECRYPTION_KEY));
1117   decryption_en = GTK_WIDGET(GTK_ENTRY(GTK_COMBO(decryption_cm)->entry));
1118
1119   if( g_ascii_strcasecmp(gtk_entry_get_text(GTK_ENTRY(decryption_en)),AIRPCAP_DECRYPTION_TYPE_STRING_WIRESHARK) == 0 )
1120   {
1121     wireshark_decryption_was_enabled = TRUE;
1122     airpcap_decryption_was_enabled = FALSE;
1123   }
1124   else if( g_ascii_strcasecmp(gtk_entry_get_text(GTK_ENTRY(decryption_en)),AIRPCAP_DECRYPTION_TYPE_STRING_AIRPCAP) == 0 )
1125   {
1126     wireshark_decryption_was_enabled = FALSE;
1127     airpcap_decryption_was_enabled = TRUE;
1128   }
1129   else if( g_ascii_strcasecmp(gtk_entry_get_text(GTK_ENTRY(decryption_en)),AIRPCAP_DECRYPTION_TYPE_STRING_NONE) == 0 )
1130   {
1131     wireshark_decryption_was_enabled = FALSE;
1132     airpcap_decryption_was_enabled = FALSE;
1133   }
1134
1135   wireshark_decryption_is_now_enabled = wireshark_decryption_on();
1136
1137   if(wireshark_decryption_is_now_enabled && airpcap_decryption_was_enabled)
1138   {
1139     set_airpcap_decryption(FALSE);
1140     gtk_entry_set_text(GTK_ENTRY(decryption_en),AIRPCAP_DECRYPTION_TYPE_STRING_WIRESHARK);
1141   }
1142   if(wireshark_decryption_is_now_enabled && !airpcap_decryption_was_enabled)
1143   {
1144     set_airpcap_decryption(FALSE);
1145     gtk_entry_set_text(GTK_ENTRY(decryption_en),AIRPCAP_DECRYPTION_TYPE_STRING_WIRESHARK);
1146   }
1147   else if(!wireshark_decryption_is_now_enabled && wireshark_decryption_was_enabled)
1148   {
1149     if(airpcap_decryption_was_enabled)
1150     {
1151       set_airpcap_decryption(TRUE);
1152       gtk_entry_set_text(GTK_ENTRY(decryption_en),AIRPCAP_DECRYPTION_TYPE_STRING_AIRPCAP);
1153     }
1154     else
1155     {
1156       set_airpcap_decryption(FALSE);
1157       gtk_entry_set_text(GTK_ENTRY(decryption_en),AIRPCAP_DECRYPTION_TYPE_STRING_NONE);
1158     }
1159   }
1160 }
1161 #endif
1162
1163 static guint
1164 pref_clean(pref_t *pref, gpointer user_data _U_)
1165 {
1166   switch (pref->type) {
1167
1168   case PREF_UINT:
1169     break;
1170
1171   case PREF_BOOL:
1172     break;
1173
1174   case PREF_ENUM:
1175     break;
1176
1177   case PREF_STRING:
1178     if (pref->saved_val.string != NULL) {
1179       g_free(pref->saved_val.string);
1180       pref->saved_val.string = NULL;
1181     }
1182     break;
1183
1184   case PREF_RANGE:
1185     if (pref->saved_val.range != NULL) {
1186       g_free(pref->saved_val.range);
1187       pref->saved_val.range = NULL;
1188     }
1189     break;
1190
1191   case PREF_STATIC_TEXT:
1192   case PREF_UAT:
1193     break;
1194
1195   case PREF_OBSOLETE:
1196     g_assert_not_reached();
1197     break;
1198   }
1199   return 0;
1200 }
1201
1202 static guint
1203 module_prefs_clean(module_t *module, gpointer user_data _U_)
1204 {
1205   /* For all preferences in this module, clean up any cruft allocated for
1206      use by the GUI code. */
1207   prefs_pref_foreach(module, pref_clean, NULL);
1208   return 0;     /* keep cleaning modules */
1209 }
1210
1211 /* fetch all pref values from all pages */
1212 static gboolean
1213 prefs_main_fetch_all(GtkWidget *dlg, gboolean *must_redissect)
1214 {
1215   pref_t *badpref;
1216
1217   /* First, check that the values are all valid. */
1218   /* XXX - check the non-registered preferences too */
1219   switch (prefs_modules_foreach(module_prefs_check, (gpointer)&badpref)) {
1220
1221   case PREFS_SET_SYNTAX_ERR:
1222     switch (badpref->type) {
1223
1224     case PREF_UINT:
1225       simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
1226                     "The value for \"%s\" isn't a valid number.",
1227                     badpref->title);
1228       return FALSE;
1229
1230     case PREF_RANGE:
1231       simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
1232                     "The value for \"%s\" isn't a valid range.",
1233                     badpref->title);
1234       return FALSE;
1235
1236     default:
1237       g_assert_not_reached();
1238       break;
1239     }
1240   }
1241
1242   /* Fetch the preferences (i.e., make sure all the values set in all of
1243      the preferences panes have been copied to "prefs" and the registered
1244      preferences). */
1245   gui_prefs_fetch(g_object_get_data(G_OBJECT(dlg), E_GUI_PAGE_KEY));
1246   layout_prefs_fetch(g_object_get_data(G_OBJECT(dlg), E_GUI_LAYOUT_PAGE_KEY));
1247   column_prefs_fetch(g_object_get_data(G_OBJECT(dlg), E_GUI_COLUMN_PAGE_KEY));
1248   stream_prefs_fetch(g_object_get_data(G_OBJECT(dlg), E_GUI_COLORS_PAGE_KEY));
1249
1250 #ifdef HAVE_LIBPCAP
1251 #ifdef _WIN32
1252   /* Is WPcap loaded? */
1253   if (has_wpcap) {
1254 #endif /* _WIN32 */
1255   capture_prefs_fetch(g_object_get_data(G_OBJECT(dlg), E_CAPTURE_PAGE_KEY));
1256 #ifdef _WIN32
1257   }
1258 #endif /* _WIN32 */
1259 #endif /* HAVE_LIBPCAP */
1260   printer_prefs_fetch(g_object_get_data(G_OBJECT(dlg), E_PRINT_PAGE_KEY));
1261   nameres_prefs_fetch(g_object_get_data(G_OBJECT(dlg), E_NAMERES_PAGE_KEY));
1262   stats_prefs_fetch(g_object_get_data(G_OBJECT(dlg), E_TAPS_PAGE_KEY));
1263   protocols_prefs_fetch(g_object_get_data(G_OBJECT(dlg), E_PROTOCOLS_PAGE_KEY));
1264   prefs_modules_foreach(module_prefs_fetch, must_redissect);
1265
1266   return TRUE;
1267 }
1268
1269 /* apply all pref values to the real world */
1270 static void
1271 prefs_main_apply_all(GtkWidget *dlg, gboolean redissect)
1272 {
1273   GtkWidget *save_bt;
1274
1275   /*
1276    * Apply the protocol preferences first - "gui_prefs_apply()" could
1277    * cause redissection, and we have to make sure the protocol
1278    * preference changes have been fully applied.
1279    */
1280   prefs_apply_all();
1281
1282   gui_prefs_apply(g_object_get_data(G_OBJECT(dlg), E_GUI_PAGE_KEY), redissect);
1283   layout_prefs_apply(g_object_get_data(G_OBJECT(dlg), E_GUI_LAYOUT_PAGE_KEY));
1284   column_prefs_apply(g_object_get_data(G_OBJECT(dlg), E_GUI_COLUMN_PAGE_KEY));
1285   stream_prefs_apply(g_object_get_data(G_OBJECT(dlg), E_GUI_COLORS_PAGE_KEY));
1286
1287 #ifdef HAVE_LIBPCAP
1288 #ifdef _WIN32
1289   /* Is WPcap loaded? */
1290   if (has_wpcap) {
1291 #endif /* _WIN32 */
1292   capture_prefs_apply(g_object_get_data(G_OBJECT(dlg), E_CAPTURE_PAGE_KEY));
1293 #ifdef _WIN32
1294   }
1295 #endif /* _WIN32 */
1296 #endif /* HAVE_LIBPCAP */
1297   printer_prefs_apply(g_object_get_data(G_OBJECT(dlg), E_PRINT_PAGE_KEY));
1298   nameres_prefs_apply(g_object_get_data(G_OBJECT(dlg), E_NAMERES_PAGE_KEY));
1299   stats_prefs_apply(g_object_get_data(G_OBJECT(dlg), E_TAPS_PAGE_KEY));
1300   protocols_prefs_apply(g_object_get_data(G_OBJECT(dlg), E_PROTOCOLS_PAGE_KEY));
1301
1302   /* show/hide the Save button - depending on setting */
1303   save_bt = g_object_get_data(G_OBJECT(prefs_w), E_PREFSW_SAVE_BT_KEY);
1304   if(prefs.gui_use_pref_save) {
1305     gtk_widget_show(save_bt);
1306   } else {
1307     gtk_widget_hide(save_bt);
1308   }
1309 }
1310
1311
1312 /* destroy all preferences ressources from all pages */
1313 static void
1314 prefs_main_destroy_all(GtkWidget *dlg)
1315 {
1316   int page_num;
1317   GtkWidget *frame;
1318
1319   for (page_num = 0;
1320        (frame = gtk_notebook_get_nth_page(g_object_get_data(G_OBJECT(prefs_w), E_PREFSW_NOTEBOOK_KEY), page_num)) != NULL;
1321        page_num++) {
1322     if(g_object_get_data(G_OBJECT(frame), E_PAGE_ITER_KEY))
1323       gtk_tree_iter_free(g_object_get_data(G_OBJECT(frame), E_PAGE_ITER_KEY));
1324   }
1325
1326   gui_prefs_destroy(g_object_get_data(G_OBJECT(dlg), E_GUI_PAGE_KEY));
1327   layout_prefs_destroy(g_object_get_data(G_OBJECT(dlg), E_GUI_LAYOUT_PAGE_KEY));
1328   column_prefs_destroy(g_object_get_data(G_OBJECT(dlg), E_GUI_COLUMN_PAGE_KEY));
1329   stream_prefs_destroy(g_object_get_data(G_OBJECT(dlg), E_GUI_COLORS_PAGE_KEY));
1330
1331 #ifdef HAVE_LIBPCAP
1332 #ifdef _WIN32
1333   /* Is WPcap loaded? */
1334   if (has_wpcap) {
1335 #endif /* _WIN32 */
1336   capture_prefs_destroy(g_object_get_data(G_OBJECT(dlg), E_CAPTURE_PAGE_KEY));
1337 #ifdef _WIN32
1338   }
1339 #endif /* _WIN32 */
1340 #endif /* HAVE_LIBPCAP */
1341   printer_prefs_destroy(g_object_get_data(G_OBJECT(dlg), E_PRINT_PAGE_KEY));
1342   nameres_prefs_destroy(g_object_get_data(G_OBJECT(dlg), E_NAMERES_PAGE_KEY));
1343   stats_prefs_destroy(g_object_get_data(G_OBJECT(dlg), E_TAPS_PAGE_KEY));
1344
1345   /* Free up the saved preferences (both for "prefs" and for registered
1346      preferences). */
1347   free_prefs(&saved_prefs);
1348   prefs_modules_foreach(module_prefs_clean, NULL);
1349   protocols_prefs_destroy(g_object_get_data(G_OBJECT(dlg), E_PROTOCOLS_PAGE_KEY));
1350 }
1351
1352
1353 static guint
1354 pref_copy(pref_t *pref, gpointer user_data _U_)
1355 {
1356   switch (pref->type) {
1357
1358   case PREF_UINT:
1359     pref->saved_val.uint = *pref->varp.uint;
1360     break;
1361
1362   case PREF_BOOL:
1363     pref->saved_val.boolval = *pref->varp.boolp;
1364     break;
1365
1366   case PREF_ENUM:
1367     pref->saved_val.enumval = *pref->varp.enump;
1368     break;
1369
1370   case PREF_STRING:
1371     g_free(pref->saved_val.string);
1372     pref->saved_val.string = g_strdup(*pref->varp.string);
1373     break;
1374
1375   case PREF_RANGE:
1376     g_free(pref->saved_val.range);
1377     pref->saved_val.range = range_copy(*pref->varp.range);
1378     break;
1379
1380   case PREF_STATIC_TEXT:
1381   case PREF_UAT:
1382     break;
1383
1384   case PREF_OBSOLETE:
1385     g_assert_not_reached();
1386     break;
1387   }
1388   return 0;
1389 }
1390
1391 static guint
1392 module_prefs_copy(module_t *module, gpointer user_data _U_)
1393 {
1394   /* For all preferences in this module, (re)save current value */
1395   prefs_pref_foreach(module, pref_copy, NULL);
1396   return 0;     /* continue making copies */
1397 }
1398
1399 /* Copy prefs to saved values so we can revert to these values */
1400 /*  if the user selects Cancel.                                */
1401 static void prefs_copy(void) {
1402   free_prefs(&saved_prefs);
1403   copy_prefs(&saved_prefs, &prefs);
1404   prefs_modules_foreach(module_prefs_copy, NULL);
1405 }
1406
1407
1408 void
1409 prefs_main_write(void)
1410 {
1411   int err;
1412   char *pf_dir_path;
1413   char *pf_path;
1414
1415   /* Create the directory that holds personal configuration files, if
1416      necessary.  */
1417   if (create_persconffile_dir(&pf_dir_path) == -1) {
1418     simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
1419                   "Can't create directory\n\"%s\"\nfor preferences file: %s.", pf_dir_path,
1420                   strerror(errno));
1421     g_free(pf_dir_path);
1422   } else {
1423     /* Write the preferencs out. */
1424     err = write_prefs(&pf_path);
1425     if (err != 0) {
1426       simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
1427                     "Can't open preferences file\n\"%s\": %s.", pf_path,
1428                     strerror(err));
1429       g_free(pf_path);
1430     }
1431   }
1432
1433 #ifdef HAVE_AIRPCAP
1434   /*
1435    * Load the Wireshark decryption keys (just set) and save
1436    * the changes to the adapters' registry
1437    */
1438   airpcap_load_decryption_keys(airpcap_if_list);
1439 #endif
1440 }
1441
1442
1443 static void
1444 prefs_main_ok_cb(GtkWidget *ok_bt _U_, gpointer parent_w)
1445 {
1446   gboolean must_redissect = FALSE;
1447
1448   if (!prefs_main_fetch_all(parent_w, &must_redissect))
1449     return; /* Errors in some preference setting - already reported */
1450
1451   /* if we don't have a Save button, just save the settings now */
1452   if (!prefs.gui_use_pref_save) {
1453     prefs_main_write();
1454   }
1455
1456   prefs_main_apply_all(parent_w, must_redissect);
1457
1458   /* Fill in capture options with values from the preferences */
1459   prefs_to_capture_opts();
1460
1461 #ifdef HAVE_AIRPCAP
1462   prefs_airpcap_update();
1463 #endif
1464
1465   /* Now destroy the "Preferences" dialog. */
1466   window_destroy(GTK_WIDGET(parent_w));
1467
1468   if (must_redissect) {
1469     /* Redissect all the packets, and re-evaluate the display filter. */
1470     redissect_packets();
1471   }
1472
1473 }
1474
1475 static void
1476 prefs_main_apply_cb(GtkWidget *apply_bt _U_, gpointer parent_w)
1477 {
1478   gboolean must_redissect = FALSE;
1479
1480   if (!prefs_main_fetch_all(parent_w, &must_redissect))
1481     return; /* Errors in some preference setting - already reported */
1482
1483   /* if we don't have a Save button, just save the settings now */
1484   if (!prefs.gui_use_pref_save) {
1485     prefs_main_write();
1486     prefs_copy();     /* save prefs for reverting if Cancel */
1487   }
1488
1489   prefs_main_apply_all(parent_w, must_redissect);
1490
1491   /* Fill in capture options with values from the preferences */
1492   prefs_to_capture_opts();
1493
1494 #ifdef HAVE_AIRPCAP
1495   prefs_airpcap_update();
1496 #endif
1497
1498   if (must_redissect) {
1499     /* Redissect all the packets, and re-evaluate the display filter. */
1500     redissect_packets();
1501   }
1502 }
1503
1504 static void
1505 prefs_main_save_cb(GtkWidget *save_bt _U_, gpointer parent_w)
1506 {
1507   gboolean must_redissect = FALSE;
1508
1509   if (!prefs_main_fetch_all(parent_w, &must_redissect))
1510     return; /* Errors in some preference setting - already reported */
1511
1512   prefs_main_write();
1513   prefs_copy();     /* save prefs for reverting if Cancel */
1514
1515   /* Now apply those preferences.
1516      XXX - should we do this?  The user didn't click "OK" or "Apply".
1517      However:
1518
1519         1) by saving the preferences they presumably indicate that they
1520            like them;
1521
1522         2) the next time they fire Wireshark up, those preferences will
1523            apply;
1524
1525         3) we'd have to buffer "must_redissect" so that if they do
1526            "Apply" after this, we know we have to redissect;
1527
1528         4) we did apply the protocol preferences, at least, in the past. */
1529   prefs_main_apply_all(parent_w, must_redissect);
1530
1531   /* Fill in capture options with values from the preferences */
1532   prefs_to_capture_opts();
1533
1534   if (must_redissect) {
1535     /* Redissect all the packets, and re-evaluate the display filter. */
1536     redissect_packets();
1537   }
1538 }
1539
1540 static guint
1541 pref_revert(pref_t *pref, gpointer user_data)
1542 {
1543   gboolean *pref_changed_p = user_data;
1544
1545   /* Revert the preference to its saved value. */
1546   switch (pref->type) {
1547
1548   case PREF_UINT:
1549     if (*pref->varp.uint != pref->saved_val.uint) {
1550       *pref_changed_p = TRUE;
1551       *pref->varp.uint = pref->saved_val.uint;
1552     }
1553     break;
1554
1555   case PREF_BOOL:
1556     if (*pref->varp.boolp != pref->saved_val.boolval) {
1557       *pref_changed_p = TRUE;
1558       *pref->varp.boolp = pref->saved_val.boolval;
1559     }
1560     break;
1561
1562   case PREF_ENUM:
1563     if (*pref->varp.enump != pref->saved_val.enumval) {
1564       *pref_changed_p = TRUE;
1565       *pref->varp.enump = pref->saved_val.enumval;
1566     }
1567     break;
1568
1569   case PREF_STRING:
1570     if (strcmp(*pref->varp.string, pref->saved_val.string) != 0) {
1571       *pref_changed_p = TRUE;
1572       g_free((void *)*pref->varp.string);
1573       *pref->varp.string = g_strdup(pref->saved_val.string);
1574     }
1575     break;
1576
1577   case PREF_RANGE:
1578     if (!ranges_are_equal(*pref->varp.range, pref->saved_val.range)) {
1579       *pref_changed_p = TRUE;
1580       g_free(*pref->varp.range);
1581       *pref->varp.range = range_copy(pref->saved_val.range);
1582     }
1583     break;
1584
1585   case PREF_STATIC_TEXT:
1586   case PREF_UAT:
1587     break;
1588
1589   case PREF_OBSOLETE:
1590     g_assert_not_reached();
1591     break;
1592   }
1593   return 0;
1594 }
1595
1596 static guint
1597 module_prefs_revert(module_t *module, gpointer user_data)
1598 {
1599   gboolean *must_redissect_p = user_data;
1600
1601   /* For all preferences in this module, revert its value to the value
1602      it had when we popped up the Preferences dialog.  Find out whether
1603      this changes any of them. */
1604   module->prefs_changed = FALSE;        /* assume none of them changed */
1605   prefs_pref_foreach(module, pref_revert, &module->prefs_changed);
1606
1607   /* If any of them changed, indicate that we must redissect and refilter
1608      the current capture (if we have one), as the preference change
1609      could cause packets to be dissected differently. */
1610   if (module->prefs_changed)
1611     *must_redissect_p = TRUE;
1612   return 0;     /* keep processing modules */
1613 }
1614
1615 /* cancel button pressed, revert prefs to saved and exit dialog */
1616 static void
1617 prefs_main_cancel_cb(GtkWidget *cancel_bt _U_, gpointer parent_w)
1618 {
1619   gboolean must_redissect = FALSE;
1620
1621   /* Free up the current preferences and copy the saved preferences to the
1622      current preferences. */
1623   free_prefs(&prefs);
1624   copy_prefs(&prefs, &saved_prefs);
1625   cfile.cinfo.columns_changed = FALSE; /* [XXX: "columns_changed" should treally be stored in prefs struct ??] */
1626
1627   /* Now revert the registered preferences. */
1628   prefs_modules_foreach(module_prefs_revert, &must_redissect);
1629
1630   /* Now apply the reverted-to preferences. */
1631   prefs_main_apply_all(parent_w, must_redissect);
1632
1633   window_destroy(GTK_WIDGET(parent_w));
1634
1635   if (must_redissect) {
1636     /* Redissect all the packets, and re-evaluate the display filter. */
1637     redissect_packets();
1638   }
1639 }
1640
1641 /* Treat this as a cancel, by calling "prefs_main_cancel_cb()" */
1642 static gboolean
1643 prefs_main_delete_event_cb(GtkWidget *prefs_w_lcl, GdkEvent *event _U_,
1644                            gpointer user_data _U_)
1645 {
1646   prefs_main_cancel_cb(NULL, prefs_w_lcl);
1647   return FALSE;
1648 }
1649
1650
1651 /* dialog *is* already destroyed, clean up memory and such */
1652 static void
1653 prefs_main_destroy_cb(GtkWidget *win _U_, gpointer parent_w)
1654 {
1655   prefs_main_destroy_all(parent_w);
1656
1657   /* Note that we no longer have a "Preferences" dialog box. */
1658   prefs_w = NULL;
1659 }
1660
1661 struct properties_data {
1662   const char *title;
1663   module_t *module;
1664 };
1665
1666 static guint
1667 module_search_properties(module_t *module, gpointer user_data)
1668 {
1669   struct properties_data *p = (struct properties_data *)user_data;
1670
1671   /* If this module has the specified title, remember it. */
1672   if (strcmp(module->title, p->title) == 0) {
1673     p->module = module;
1674     return 1;   /* stops the search */
1675   }
1676   
1677   if(prefs_module_has_submodules(module))
1678     return prefs_modules_foreach_submodules(module, module_search_properties, p);
1679
1680   return 0;
1681 }
1682
1683 static void
1684 tree_expand_row(GtkTreeModel *model, GtkTreeView *tree_view, GtkTreeIter *iter)
1685 {
1686   GtkTreeIter   parent;
1687   GtkTreePath   *path;
1688
1689   /* expand the parent first */
1690   if(gtk_tree_model_iter_parent(model, &parent, iter))
1691     tree_expand_row(model, tree_view, &parent);
1692
1693   path = gtk_tree_model_get_path(model, iter);
1694   gtk_tree_view_expand_row(tree_view, path, FALSE);
1695   /*expand_tree(tree_view, &parent, NULL, NULL);*/
1696
1697   gtk_tree_path_free(path);
1698 }
1699
1700 /* select a node in the tree view */
1701 /* XXX - this is almost 100% copied from byte_view_select() in proto_draw.c,
1702  *       find a way to combine both to have a generic function for this */
1703 static void
1704 tree_select_node(GtkWidget *tree, prefs_tree_iter *iter)
1705 {
1706   GtkTreeIter  local_iter = *iter;
1707   GtkTreeView  *tree_view = GTK_TREE_VIEW(tree);
1708   GtkTreeModel *model;
1709   GtkTreePath  *first_path;
1710
1711   model = gtk_tree_view_get_model(tree_view);
1712
1713   /* Expand our field's row */
1714   first_path = gtk_tree_model_get_path(model, &local_iter);
1715
1716   /* expand from the top down */
1717   tree_expand_row(model, tree_view, &local_iter);
1718
1719   /* select our field's row */
1720   gtk_tree_selection_select_path(gtk_tree_view_get_selection(tree_view),
1721                                  first_path);
1722
1723   /* And position the window so the selection is visible.
1724    * Position the selection in the middle of the viewable
1725    * pane. */
1726   gtk_tree_view_scroll_to_cell(tree_view, first_path, NULL, TRUE, 0.5f, 0.0f);
1727
1728   gtk_tree_path_free(first_path);
1729 }
1730
1731
1732 /* search the corresponding protocol page of the currently selected field */
1733 void
1734 properties_cb(GtkWidget *w, gpointer dummy)
1735 {
1736   header_field_info *hfinfo;
1737   const gchar *title;
1738   struct properties_data p;
1739   int page_num;
1740   GtkWidget *sw;
1741   GtkWidget *frame;
1742   module_t *page_module;
1743
1744   if (cfile.finfo_selected == NULL) {
1745     /* There is no field selected */
1746     return;
1747   }
1748
1749   /* Find the title for the protocol for the selected field. */
1750   hfinfo = cfile.finfo_selected->hfinfo;
1751   if (hfinfo->parent == -1)
1752     title = prefs_get_title_by_name(hfinfo->abbrev);
1753   else
1754     title = prefs_get_title_by_name(proto_registrar_get_abbrev(hfinfo->parent));
1755   if (!title)
1756     return;     /* Couldn't find it. XXX - just crash? "Can't happen"? */
1757
1758   /* Find the module for that protocol by searching for one with that title.
1759      XXX - should we just associate protocols with modules directly? */
1760   p.title = title;
1761   p.module = NULL;
1762   prefs_modules_foreach_submodules(protocols_module, module_search_properties,
1763                             &p);
1764   if (p.module == NULL) {
1765     /* We didn't find it - that protocol probably has no preferences. */
1766     return;
1767   }
1768
1769   /* Create a preferences window, or pop up an existing one. */
1770   if (prefs_w != NULL) {
1771     reactivate_window(prefs_w);
1772   } else {
1773     prefs_cb(w, dummy);
1774   }
1775
1776   /* Search all the pages in that window for the one with the specified
1777      module. */
1778   for (page_num = 0;
1779        (sw = gtk_notebook_get_nth_page(g_object_get_data(G_OBJECT(prefs_w), E_PREFSW_NOTEBOOK_KEY), page_num)) != NULL;
1780        page_num++) {
1781     /* Get the frame from the scrollable window */
1782     frame = g_object_get_data(G_OBJECT(sw), E_PAGESW_FRAME_KEY);
1783     /* Get the module for this page (non-protocol prefs don't have one). */
1784     if(frame) {
1785       page_module = g_object_get_data(G_OBJECT(frame), E_PAGE_MODULE_KEY);
1786       if (page_module != NULL) {
1787         if (page_module == p.module) {
1788           tree_select_node(
1789             g_object_get_data(G_OBJECT(prefs_w), E_PREFSW_TREE_KEY),
1790             g_object_get_data(G_OBJECT(frame), E_PAGE_ITER_KEY));
1791           return;
1792         }
1793       }
1794     }
1795   }
1796 }
1797
1798 /* Prefs tree selection callback.  The node data has been loaded with
1799    the proper notebook page to load. */
1800 static void
1801 prefs_tree_select_cb(GtkTreeSelection *sel, gpointer dummy _U_)
1802 {
1803   gint page;
1804   GtkTreeModel *model;
1805   GtkTreeIter   iter;
1806
1807   if (gtk_tree_selection_get_selected(sel, &model, &iter))
1808   {
1809     gtk_tree_model_get(model, &iter, 1, &page, -1);
1810     if (page >= 0)
1811       gtk_notebook_set_current_page(g_object_get_data(G_OBJECT(prefs_w), E_PREFSW_NOTEBOOK_KEY), page);
1812   }
1813 }
1814
1815
1816 /*
1817  * Editor modelines
1818  *
1819  * Local Variables:
1820  * c-basic-offset: 2
1821  * tab-width: 8
1822  * indent-tabs-mode: nil
1823  * End:
1824  *
1825  * ex: set shiftwidth=2 tabstop=8 expandtab
1826  * :indentSize=2:tabSize=8:noTabs=true:
1827  */