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