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