On Windows, put Ethereal configuration files under the "Application
[obnox/wireshark/wip.git] / gtk / prefs_dlg.c
1 /* prefs_dlg.c
2  * Routines for handling preferences
3  *
4  * $Id: prefs_dlg.c,v 1.32 2001/10/24 06:13:07 guy Exp $
5  *
6  * Ethereal - Network traffic analyzer
7  * By Gerald Combs <gerald@ethereal.com>
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 #ifdef HAVE_SYS_TYPES_H
30 #include <sys/types.h>
31 #endif
32
33 #include <gtk/gtk.h>
34
35 #include <stdlib.h>
36 #include <string.h>
37 #include <ctype.h>
38 #include <errno.h>
39
40 #ifdef HAVE_UNISTD_H
41 #include <unistd.h>
42 #endif
43
44 #ifdef HAVE_SYS_STAT_H
45 #include <sys/stat.h>
46 #endif
47
48 #include <epan/filesystem.h>
49
50 #include "main.h"
51 #include "gtkglobals.h"
52 #include "packet.h"
53 #include "file.h"
54 #include "prefs.h"
55 #include "column_prefs.h"
56 #include "print.h"
57 #include "prefs_dlg.h"
58 #include "print_prefs.h"
59 #include "stream_prefs.h"
60 #include "gui_prefs.h"
61 #include "ui_util.h"
62 #include "dlg_utils.h"
63 #include "simple_dialog.h"
64
65 #include "prefs-int.h"
66
67 static void     prefs_main_ok_cb(GtkWidget *, gpointer);
68 static void     prefs_main_apply_cb(GtkWidget *, gpointer);
69 static void     prefs_main_save_cb(GtkWidget *, gpointer);
70 static void     prefs_main_cancel_cb(GtkWidget *, gpointer);
71 static gboolean prefs_main_delete_cb(GtkWidget *, gpointer);
72 static void     prefs_main_destroy_cb(GtkWidget *, gpointer);
73 static void     prefs_tree_select_cb(GtkCTree *, GtkCTreeNode *, gint, gpointer);
74
75 #define E_PRINT_PAGE_KEY  "printer_options_page"
76 #define E_COLUMN_PAGE_KEY "column_options_page"
77 #define E_STREAM_PAGE_KEY "tcp_stream_options_page"
78 #define E_GUI_PAGE_KEY    "gui_options_page"
79
80 #define FIRST_PROTO_PREFS_PAGE  4
81
82 /* 
83  * Keep a static pointer to the notebook to be able to choose the 
84  * displayed page.
85  */
86 static GtkWidget *notebook;
87
88 /*
89  * Keep a static pointer to the current "Preferences" window, if any, so that
90  * if somebody tries to do "Edit:Preferences" while there's already a
91  * "Preferences" window up, we just pop up the existing one, rather than
92  * creating a new one.
93  */
94 static GtkWidget *prefs_w;
95
96 /*
97  * Save the value of the preferences as of when the preferences dialog
98  * box was first popped up, so we can revert to those values if the
99  * user selects "Cancel".
100  */
101 static e_prefs saved_prefs;
102
103 struct ct_struct {
104   GtkWidget    *notebook;
105   GtkWidget    *ctree;
106   GtkCTreeNode *node;
107   gint         page;
108 };
109
110 static void
111 pref_show(pref_t *pref, gpointer user_data)
112 {
113   GtkWidget *main_tb = user_data;
114   const char *title;
115   char *label_string;
116   GtkWidget *label, *menu, *menu_item, *widget, *button;
117   GSList *rb_group;
118   char uint_str[10+1];
119   const enum_val_t *enum_valp;
120   int menu_index, index;
121
122   /* Give this preference a label which is its title, followed by a colon,
123      and left-align it. */
124   title = pref->title;
125   label_string = g_malloc(strlen(title) + 2);
126   strcpy(label_string, title);
127   strcat(label_string, ":");
128   label = gtk_label_new(label_string);
129   g_free(label_string);
130   gtk_misc_set_alignment(GTK_MISC(label), 1.0, 0.5);
131
132   /* Attach it to the table. */
133   gtk_table_attach_defaults(GTK_TABLE(main_tb), label, 0, 1, pref->ordinal,
134                                 pref->ordinal+1);
135
136   /* Save the current value of the preference, so that we can revert it if
137      the user does "Apply" and then "Cancel", and create the control for
138      editing the preference. */
139   switch (pref->type) {
140
141   case PREF_UINT:
142     pref->saved_val.uint = *pref->varp.uint;
143
144     /* XXX - there are no uint spinbuttons, so we can't use a spinbutton.
145        Even more annoyingly, even if there were, GLib doesn't define
146        G_MAXUINT - but I think ANSI C may define UINT_MAX, so we could
147        use that. */
148     widget = gtk_entry_new();
149     switch (pref->info.base) {
150
151     case 10:
152       sprintf(uint_str, "%u", pref->saved_val.uint);
153       break;
154
155     case 8:
156       sprintf(uint_str, "%o", pref->saved_val.uint);
157       break;
158
159     case 16:
160       sprintf(uint_str, "%x", pref->saved_val.uint);
161       break;
162     }
163     gtk_entry_set_text(GTK_ENTRY(widget), uint_str);
164     pref->control = widget;
165     break;
166
167   case PREF_BOOL:
168     pref->saved_val.bool = *pref->varp.bool;
169     widget = gtk_check_button_new();
170     gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(widget), pref->saved_val.bool);
171     pref->control = widget;
172     break;
173
174   case PREF_ENUM:
175     pref->saved_val.enumval = *pref->varp.enump;
176     if (pref->info.enum_info.radio_buttons) {
177       /* Show it as radio buttons. */
178       widget = gtk_hbox_new(FALSE, 0);
179       rb_group = NULL;
180       for (enum_valp = pref->info.enum_info.enumvals, index = 0;
181                 enum_valp->name != NULL; enum_valp++, index++) {
182         button = gtk_radio_button_new_with_label(rb_group, enum_valp->name);
183         if (rb_group == NULL)
184           rb_group = gtk_radio_button_group(GTK_RADIO_BUTTON(button));
185         gtk_box_pack_start(GTK_BOX(widget), button, FALSE, FALSE, 10);
186         if (enum_valp->value == pref->saved_val.enumval)
187           gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(button), TRUE);
188         pref->control = button;
189       }
190     } else {
191       /* Show it as an option menu. */
192       menu = gtk_menu_new();
193       menu_index = -1;
194       for (enum_valp = pref->info.enum_info.enumvals, index = 0;
195                 enum_valp->name != NULL; enum_valp++, index++) {
196         menu_item = gtk_menu_item_new_with_label(enum_valp->name);
197         gtk_menu_append(GTK_MENU(menu), menu_item);
198         if (enum_valp->value == pref->saved_val.enumval)
199           menu_index = index;
200         gtk_widget_show(menu_item);
201       }
202
203       /* Create the option menu from the option */
204       widget = gtk_option_menu_new();
205       gtk_option_menu_set_menu(GTK_OPTION_MENU(widget), menu);
206
207       /* Set its current value to the variable's current value */
208       if (menu_index != -1)
209         gtk_option_menu_set_history(GTK_OPTION_MENU(widget), menu_index);
210       pref->control = widget;
211     }
212     break;
213
214   case PREF_STRING:
215     widget = gtk_entry_new();
216     if (pref->saved_val.string != NULL)
217       g_free(pref->saved_val.string);
218     pref->saved_val.string = g_strdup(*pref->varp.string);
219     gtk_entry_set_text(GTK_ENTRY(widget), pref->saved_val.string);
220     pref->control = widget;
221     break;
222
223   default:
224     g_assert_not_reached();
225     widget = NULL;
226     break;
227   }
228
229   gtk_table_attach_defaults(GTK_TABLE(main_tb), widget, 1, 2, pref->ordinal,
230                                 pref->ordinal+1);
231 }
232
233 #define MAX_TREE_NODE_NAME_LEN 64
234 static void
235 module_prefs_show(module_t *module, gpointer user_data)
236 {
237   struct ct_struct *cts = user_data;
238   GtkWidget        *main_vb, *main_tb, *frame;
239   gchar            label_str[MAX_TREE_NODE_NAME_LEN], *label_ptr = label_str;
240   GtkCTreeNode     *ct_node;
241
242   /* Frame */
243   frame = gtk_frame_new(module->title);
244   gtk_widget_show(frame);
245
246   /* Main vertical box */
247   main_vb = gtk_vbox_new(FALSE, 5);
248   gtk_container_border_width(GTK_CONTAINER(main_vb), 5);
249   gtk_container_add(GTK_CONTAINER(frame), main_vb);
250
251   /* Main table */
252   main_tb = gtk_table_new(module->numprefs, 2, FALSE);
253   gtk_box_pack_start(GTK_BOX(main_vb), main_tb, FALSE, FALSE, 0);
254   gtk_table_set_row_spacings(GTK_TABLE(main_tb), 10);
255   gtk_table_set_col_spacings(GTK_TABLE(main_tb), 15);
256
257   /* Add items for each of the preferences */
258   prefs_pref_foreach(module, pref_show, main_tb);
259
260   gtk_notebook_append_page(GTK_NOTEBOOK(cts->notebook), frame, NULL);
261   strcpy(label_str, module->title);
262   ct_node = gtk_ctree_insert_node(GTK_CTREE(cts->ctree), cts->node, NULL, 
263                 &label_ptr, 5, NULL, NULL, NULL, NULL, TRUE, TRUE);
264   gtk_ctree_node_set_row_data(GTK_CTREE(cts->ctree), ct_node,
265                 GINT_TO_POINTER(cts->page));
266   cts->page++;
267
268   /* Show 'em what we got */
269   gtk_widget_show_all(main_vb);
270 }
271
272 void
273 prefs_cb(GtkWidget *w, gpointer dummy) {
274   GtkWidget        *main_vb, *top_hb, *bbox, *prefs_nb, *ct_sb, *frame,
275                    *ok_bt, *apply_bt, *save_bt, *cancel_bt;
276   GtkWidget        *print_pg, *column_pg, *stream_pg, *gui_pg;
277   gchar            label_str[MAX_TREE_NODE_NAME_LEN], *label_ptr = label_str;
278   GtkCTreeNode     *ct_node;
279   struct ct_struct cts;
280
281
282   if (prefs_w != NULL) {
283     /* There's already a "Preferences" dialog box; reactivate it. */
284     reactivate_window(prefs_w);
285     return;
286   }
287
288   /* Save the current preferences, so we can revert to those values
289      if the user presses "Cancel". */
290   copy_prefs(&saved_prefs, &prefs);
291
292   prefs_w = dlg_window_new("Ethereal: Preferences");
293   gtk_signal_connect(GTK_OBJECT(prefs_w), "delete_event",
294     GTK_SIGNAL_FUNC(prefs_main_delete_cb), NULL);
295   gtk_signal_connect(GTK_OBJECT(prefs_w), "destroy",
296         GTK_SIGNAL_FUNC(prefs_main_destroy_cb), NULL);
297   
298   /* Container for each row of widgets */
299   main_vb = gtk_vbox_new(FALSE, 5);
300   gtk_container_border_width(GTK_CONTAINER(main_vb), 5);
301   gtk_container_add(GTK_CONTAINER(prefs_w), main_vb);
302   gtk_widget_show(main_vb);
303   
304   /* Top row: Preferences tree and notebook */
305   top_hb = gtk_hbox_new(FALSE, 10);
306   gtk_container_add(GTK_CONTAINER(main_vb), top_hb);
307   gtk_widget_show(top_hb);
308
309   /* Place a Ctree on the left for preference categories */
310   ct_sb = gtk_scrolled_window_new(NULL, NULL);
311   gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(ct_sb),
312         GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
313   set_scrollbar_placement_scrollw(ct_sb, prefs.gui_scrollbar_on_right);
314   remember_scrolled_window(ct_sb);
315   gtk_container_add(GTK_CONTAINER(top_hb), ct_sb);
316   gtk_widget_show(ct_sb);
317
318   cts.ctree = gtk_ctree_new(1, 0);
319   cts.page  = 0;
320   gtk_container_add(GTK_CONTAINER(ct_sb), cts.ctree);
321
322   /* Be consistent with our line/expander styles */
323   g_assert(prefs.gui_ptree_line_style >= GTK_CTREE_LINES_NONE &&
324         prefs.gui_ptree_line_style <= GTK_CTREE_LINES_TABBED);
325   gtk_ctree_set_line_style(GTK_CTREE(cts.ctree), prefs.gui_ptree_line_style);
326   g_assert(prefs.gui_ptree_expander_style >= GTK_CTREE_EXPANDER_NONE &&
327         prefs.gui_ptree_expander_style <= GTK_CTREE_EXPANDER_CIRCULAR);
328   gtk_ctree_set_expander_style(GTK_CTREE(cts.ctree),
329         prefs.gui_ptree_expander_style);
330
331   gtk_clist_set_column_auto_resize(GTK_CLIST(cts.ctree), 0, TRUE);
332   gtk_signal_connect(GTK_OBJECT(cts.ctree), "tree-select-row",
333     GTK_SIGNAL_FUNC(prefs_tree_select_cb), NULL);
334   gtk_widget_show(cts.ctree);
335   
336   /* A notebook widget sans tabs is used to flip between prefs */
337   notebook = prefs_nb = gtk_notebook_new();
338   gtk_notebook_set_show_tabs(GTK_NOTEBOOK(notebook), FALSE);
339   gtk_notebook_set_show_border(GTK_NOTEBOOK(notebook), FALSE);
340   gtk_container_add(GTK_CONTAINER(top_hb), prefs_nb);
341   gtk_widget_show(prefs_nb);
342   
343   /* Printing prefs */
344   frame = gtk_frame_new("Printing");
345   gtk_widget_show(GTK_WIDGET(frame));
346   print_pg = printer_prefs_show();
347   gtk_container_add(GTK_CONTAINER(frame), print_pg);
348   gtk_object_set_data(GTK_OBJECT(prefs_w), E_PRINT_PAGE_KEY, print_pg);
349   gtk_notebook_append_page (GTK_NOTEBOOK(prefs_nb), frame, NULL);
350   strcpy(label_str, "Printing");
351   ct_node = gtk_ctree_insert_node(GTK_CTREE(cts.ctree), NULL, NULL, 
352                 &label_ptr, 5, NULL, NULL, NULL, NULL, TRUE, TRUE);
353   gtk_ctree_node_set_row_data(GTK_CTREE(cts.ctree), ct_node,
354                 GINT_TO_POINTER(cts.page));
355   cts.page++;
356     
357   /* Column prefs */
358   frame = gtk_frame_new("Columns");
359   gtk_widget_show(GTK_WIDGET(frame));
360   column_pg = column_prefs_show();
361   gtk_container_add(GTK_CONTAINER(frame), column_pg);
362   gtk_object_set_data(GTK_OBJECT(prefs_w), E_COLUMN_PAGE_KEY, column_pg);
363   gtk_notebook_append_page (GTK_NOTEBOOK(prefs_nb), frame, NULL);
364   strcpy(label_str, "Columns");
365   ct_node = gtk_ctree_insert_node(GTK_CTREE(cts.ctree), NULL, NULL, 
366                 &label_ptr, 5, NULL, NULL, NULL, NULL, TRUE, TRUE);
367   gtk_ctree_node_set_row_data(GTK_CTREE(cts.ctree), ct_node,
368                 GINT_TO_POINTER(cts.page));
369   cts.page++;
370   
371   /* TCP Streams prefs */
372   frame = gtk_frame_new("TCP Streams");
373   gtk_widget_show(GTK_WIDGET(frame));
374   stream_pg = stream_prefs_show();
375   gtk_container_add(GTK_CONTAINER(frame), stream_pg);
376   gtk_object_set_data(GTK_OBJECT(prefs_w), E_STREAM_PAGE_KEY, stream_pg);
377   gtk_notebook_append_page (GTK_NOTEBOOK(prefs_nb), frame, NULL);
378   strcpy(label_str, "TCP Streams");
379   ct_node = gtk_ctree_insert_node(GTK_CTREE(cts.ctree), NULL, NULL, 
380                 &label_ptr, 5, NULL, NULL, NULL, NULL, TRUE, TRUE);
381   gtk_ctree_node_set_row_data(GTK_CTREE(cts.ctree), ct_node,
382                 GINT_TO_POINTER(cts.page));
383   cts.page++;
384
385   /* GUI prefs */
386   frame = gtk_frame_new("User Interface");
387   gtk_widget_show(GTK_WIDGET(frame));
388   gui_pg = gui_prefs_show();
389   gtk_container_add(GTK_CONTAINER(frame), gui_pg);
390   gtk_object_set_data(GTK_OBJECT(prefs_w), E_GUI_PAGE_KEY, gui_pg);
391   gtk_notebook_append_page (GTK_NOTEBOOK(prefs_nb), frame, NULL);
392   strcpy(label_str, "User Interface");
393   ct_node = gtk_ctree_insert_node(GTK_CTREE(cts.ctree), NULL, NULL, 
394                 &label_ptr, 5, NULL, NULL, NULL, NULL, TRUE, TRUE);
395   gtk_ctree_node_set_row_data(GTK_CTREE(cts.ctree), ct_node,
396                 GINT_TO_POINTER(cts.page));
397   cts.page++;
398
399
400   /* Registered prefs */
401   cts.notebook = prefs_nb;
402   strcpy(label_str, "Protocols");
403   cts.node = gtk_ctree_insert_node(GTK_CTREE(cts.ctree), NULL, NULL,
404                 &label_ptr, 5, NULL, NULL, NULL, NULL, FALSE, FALSE);
405   gtk_ctree_node_set_row_data(GTK_CTREE(cts.ctree), cts.node,
406                 GINT_TO_POINTER(-1));
407   gtk_ctree_node_set_selectable(GTK_CTREE(cts.ctree), cts.node, FALSE);
408
409   prefs_module_foreach(module_prefs_show, &cts);
410
411
412   /* Button row: OK and cancel buttons */
413   bbox = gtk_hbutton_box_new();
414   gtk_button_box_set_layout (GTK_BUTTON_BOX (bbox), GTK_BUTTONBOX_END);
415   gtk_button_box_set_spacing(GTK_BUTTON_BOX(bbox), 5);
416   gtk_container_add(GTK_CONTAINER(main_vb), bbox);
417   gtk_widget_show(bbox);
418   
419   ok_bt = gtk_button_new_with_label ("OK");
420   gtk_signal_connect(GTK_OBJECT(ok_bt), "clicked",
421     GTK_SIGNAL_FUNC(prefs_main_ok_cb), GTK_OBJECT(prefs_w));
422   GTK_WIDGET_SET_FLAGS(ok_bt, GTK_CAN_DEFAULT);
423   gtk_box_pack_start (GTK_BOX (bbox), ok_bt, TRUE, TRUE, 0);
424   gtk_widget_grab_default(ok_bt);
425   gtk_widget_show(ok_bt);
426
427   apply_bt = gtk_button_new_with_label ("Apply");
428   gtk_signal_connect(GTK_OBJECT(apply_bt), "clicked",
429     GTK_SIGNAL_FUNC(prefs_main_apply_cb), GTK_OBJECT(prefs_w));
430   GTK_WIDGET_SET_FLAGS(apply_bt, GTK_CAN_DEFAULT);
431   gtk_box_pack_start(GTK_BOX (bbox), apply_bt, TRUE, TRUE, 0);
432   gtk_widget_show(apply_bt);
433
434   save_bt = gtk_button_new_with_label ("Save");
435   gtk_signal_connect(GTK_OBJECT(save_bt), "clicked",
436     GTK_SIGNAL_FUNC(prefs_main_save_cb), GTK_OBJECT(prefs_w));
437   GTK_WIDGET_SET_FLAGS(save_bt, GTK_CAN_DEFAULT);
438   gtk_box_pack_start (GTK_BOX (bbox), save_bt, TRUE, TRUE, 0);
439   gtk_widget_show(save_bt);
440   
441   cancel_bt = gtk_button_new_with_label ("Cancel");
442   gtk_signal_connect(GTK_OBJECT(cancel_bt), "clicked",
443     GTK_SIGNAL_FUNC(prefs_main_cancel_cb), GTK_OBJECT(prefs_w));
444   GTK_WIDGET_SET_FLAGS(cancel_bt, GTK_CAN_DEFAULT);
445   gtk_box_pack_start (GTK_BOX (bbox), cancel_bt, TRUE, TRUE, 0);
446   gtk_widget_show(cancel_bt);
447
448   /* Catch the "key_press_event" signal in the window, so that we can catch
449      the ESC key being pressed and act as if the "Cancel" button had
450      been selected. */
451   dlg_set_cancel(prefs_w, cancel_bt);
452
453   gtk_widget_show(prefs_w);
454 }
455
456 static void
457 pref_fetch(pref_t *pref, gpointer user_data)
458 {
459   GtkWidget *label;
460   char *label_string;
461   char *str_val;
462   char *p;
463   guint uval;
464   gboolean bval;
465   GSList *rb_entry;
466   GtkWidget *button;
467   gint enumval;
468   gboolean *pref_changed_p = user_data;
469
470   /* Fetch the value of the preference, and set the appropriate variable
471      to it. */
472   switch (pref->type) {
473
474   case PREF_UINT:
475     str_val = gtk_entry_get_text(GTK_ENTRY(pref->control));
476     uval = strtoul(str_val, &p, pref->info.base);
477 #if 0
478     if (p == value || *p != '\0')
479       return PREFS_SET_SYNTAX_ERR;      /* number was bad */
480 #endif
481     if (*pref->varp.uint != uval) {
482       *pref_changed_p = TRUE;
483       *pref->varp.uint = uval;
484     }
485     break;
486
487   case PREF_BOOL:
488     bval = GTK_TOGGLE_BUTTON(pref->control)->active;
489     if (*pref->varp.bool != bval) {
490       *pref_changed_p = TRUE;
491       *pref->varp.bool = bval;
492     }
493     break;
494
495   case PREF_ENUM:
496     if (pref->info.enum_info.radio_buttons) {
497       /* Go through the list of of radio buttons in the group, and find
498          the first one that's active. */
499       button = NULL;
500       for (rb_entry = gtk_radio_button_group(GTK_RADIO_BUTTON(pref->control));
501                 rb_entry != NULL;
502                 rb_entry = g_slist_next(rb_entry)) {
503         button = rb_entry->data;
504         if (GTK_TOGGLE_BUTTON(button)->active)
505           break;
506       }
507       /* OK, now find that button's label. */
508       label = GTK_BIN(button)->child;
509     } else {
510       /* Get the label for the currently active entry in the option menu.
511          Yes, this is how you do it.  See FAQ 6.8 in the GTK+ FAQ. */
512       label = GTK_BIN(pref->control)->child;
513     }
514
515     /* Get the label, and translate it to a value. */
516     gtk_label_get(GTK_LABEL(label), &label_string);
517     enumval = find_val_for_string(label_string,
518                                         pref->info.enum_info.enumvals, 1);
519     if (*pref->varp.enump != enumval) {
520       *pref_changed_p = TRUE;
521       *pref->varp.enump = enumval;
522     }
523     break;
524
525   case PREF_STRING:
526     str_val = gtk_entry_get_text(GTK_ENTRY(pref->control));
527     if (*pref->varp.string == NULL || strcmp(*pref->varp.string, str_val) != 0) {
528       *pref_changed_p = TRUE;
529       if (*pref->varp.string != NULL)
530         g_free(*pref->varp.string);
531       *pref->varp.string = g_strdup(str_val);
532     }
533     break;
534   }
535 }
536
537 static void
538 module_prefs_fetch(module_t *module, gpointer user_data)
539 {
540   gboolean *must_redissect_p = user_data;
541
542   /* For all preferences in this module, fetch its value from this
543      module's notebook page.  Find out whether any of them changed. */
544   module->prefs_changed = FALSE;        /* assume none of them changed */
545   prefs_pref_foreach(module, pref_fetch, &module->prefs_changed);
546
547   /* If any of them changed, indicate that we must redissect and refilter
548      the current capture (if we have one), as the preference change
549      could cause packets to be dissected differently. */
550   if (module->prefs_changed)
551     *must_redissect_p = TRUE;
552 }
553
554 static void
555 pref_clean(pref_t *pref, gpointer user_data)
556 {
557   switch (pref->type) {
558
559   case PREF_UINT:
560     break;
561
562   case PREF_BOOL:
563     break;
564
565   case PREF_ENUM:
566     break;
567
568   case PREF_STRING:
569     if (pref->saved_val.string != NULL) {
570       g_free(pref->saved_val.string);
571       pref->saved_val.string = NULL;
572     }
573     break;
574   }
575 }
576
577 static void
578 module_prefs_clean(module_t *module, gpointer user_data)
579 {
580   /* For all preferences in this module, clean up any cruft allocated for
581      use by the GUI code. */
582   prefs_pref_foreach(module, pref_clean, NULL);
583 }
584
585 static void
586 prefs_main_ok_cb(GtkWidget *ok_bt, gpointer parent_w)
587 {
588   gboolean must_redissect = FALSE;
589
590   /* Fetch the preferences (i.e., make sure all the values set in all of
591      the preferences panes have been copied to "prefs" and the registered
592      preferences). */
593   printer_prefs_fetch(gtk_object_get_data(GTK_OBJECT(parent_w), E_PRINT_PAGE_KEY));
594   column_prefs_fetch(gtk_object_get_data(GTK_OBJECT(parent_w), E_COLUMN_PAGE_KEY));
595   stream_prefs_fetch(gtk_object_get_data(GTK_OBJECT(parent_w), E_STREAM_PAGE_KEY));
596   gui_prefs_fetch(gtk_object_get_data(GTK_OBJECT(parent_w), E_GUI_PAGE_KEY));
597   prefs_module_foreach(module_prefs_fetch, &must_redissect);
598
599   /* Now apply those preferences. */
600   printer_prefs_apply(gtk_object_get_data(GTK_OBJECT(parent_w), E_PRINT_PAGE_KEY));
601   column_prefs_apply(gtk_object_get_data(GTK_OBJECT(parent_w), E_COLUMN_PAGE_KEY));
602   stream_prefs_apply(gtk_object_get_data(GTK_OBJECT(parent_w), E_STREAM_PAGE_KEY));
603   gui_prefs_apply(gtk_object_get_data(GTK_OBJECT(parent_w), E_GUI_PAGE_KEY));
604   prefs_apply_all();
605
606   /* Now destroy the "Preferences" dialog. */
607   gtk_widget_destroy(GTK_WIDGET(parent_w));
608
609   if (must_redissect) {
610     /* Redissect all the packets, and re-evaluate the display filter. */
611     redissect_packets(&cfile);
612   }
613 }
614
615 static void
616 prefs_main_apply_cb(GtkWidget *apply_bt, gpointer parent_w)
617 {
618   gboolean must_redissect = FALSE;
619
620   /* Fetch the preferences (i.e., make sure all the values set in all of
621      the preferences panes have been copied to "prefs" and the registered
622      preferences). */
623   printer_prefs_fetch(gtk_object_get_data(GTK_OBJECT(parent_w), E_PRINT_PAGE_KEY));
624   column_prefs_fetch(gtk_object_get_data(GTK_OBJECT(parent_w), E_COLUMN_PAGE_KEY));
625   stream_prefs_fetch(gtk_object_get_data(GTK_OBJECT(parent_w), E_STREAM_PAGE_KEY));
626   gui_prefs_fetch(gtk_object_get_data(GTK_OBJECT(parent_w), E_GUI_PAGE_KEY));
627   prefs_module_foreach(module_prefs_fetch, &must_redissect);
628
629   /* Now apply those preferences. */
630   printer_prefs_apply(gtk_object_get_data(GTK_OBJECT(parent_w), E_PRINT_PAGE_KEY));
631   column_prefs_apply(gtk_object_get_data(GTK_OBJECT(parent_w), E_COLUMN_PAGE_KEY));
632   stream_prefs_apply(gtk_object_get_data(GTK_OBJECT(parent_w), E_STREAM_PAGE_KEY));
633   gui_prefs_apply(gtk_object_get_data(GTK_OBJECT(parent_w), E_GUI_PAGE_KEY));
634   prefs_module_foreach(module_prefs_fetch, &must_redissect);
635   prefs_apply_all();
636
637   if (must_redissect) {
638     /* Redissect all the packets, and re-evaluate the display filter. */
639     redissect_packets(&cfile);
640   }
641 }
642
643 static void
644 prefs_main_save_cb(GtkWidget *save_bt, gpointer parent_w)
645 {
646   gboolean must_redissect = FALSE;
647   int err;
648   char *pf_dir_path;
649   const char *pf_path;
650
651   /* Fetch the preferences (i.e., make sure all the values set in all of
652      the preferences panes have been copied to "prefs" and the registered
653      preferences). */
654   printer_prefs_fetch(gtk_object_get_data(GTK_OBJECT(parent_w), E_PRINT_PAGE_KEY));
655   column_prefs_fetch(gtk_object_get_data(GTK_OBJECT(parent_w), E_COLUMN_PAGE_KEY));
656   stream_prefs_fetch(gtk_object_get_data(GTK_OBJECT(parent_w), E_STREAM_PAGE_KEY));
657   gui_prefs_fetch(gtk_object_get_data(GTK_OBJECT(parent_w), E_GUI_PAGE_KEY));
658   prefs_module_foreach(module_prefs_fetch, &must_redissect);
659
660   /* Create the directory that holds personal configuration files, if
661      necessary.  */
662   if (create_persconffile_dir(&pf_dir_path) == -1) {
663      simple_dialog(ESD_TYPE_WARN, NULL,
664       "Can't create directory\n\"%s\"\nfor preferences file: %s.", pf_dir_path,
665       strerror(errno));
666      g_free(pf_dir_path);
667   } else {
668     /* Write the preferencs out. */
669     err = write_prefs(&pf_path);
670     if (err != 0) {
671        simple_dialog(ESD_TYPE_WARN, NULL,
672         "Can't open preferences file\n\"%s\": %s.", pf_path,
673         strerror(err));
674     }
675   }
676
677   /* Now apply those preferences.
678      XXX - should we do this?  The user didn't click "OK" or "Apply".
679      However:
680
681         1) by saving the preferences they presumably indicate that they
682            like them;
683
684         2) the next time they fire Ethereal up, those preferences will
685            apply;
686
687         3) we'd have to buffer "must_redissect" so that if they do
688            "Apply" after this, we know we have to redissect;
689
690         4) we did apply the protocol preferences, at least, in the past. */
691   printer_prefs_apply(gtk_object_get_data(GTK_OBJECT(parent_w), E_PRINT_PAGE_KEY));
692   column_prefs_apply(gtk_object_get_data(GTK_OBJECT(parent_w), E_COLUMN_PAGE_KEY));
693   stream_prefs_apply(gtk_object_get_data(GTK_OBJECT(parent_w), E_STREAM_PAGE_KEY));
694   gui_prefs_apply(gtk_object_get_data(GTK_OBJECT(parent_w), E_GUI_PAGE_KEY));
695   prefs_module_foreach(module_prefs_fetch, &must_redissect);
696   prefs_apply_all();
697
698   if (must_redissect) {
699     /* Redissect all the packets, and re-evaluate the display filter. */
700     redissect_packets(&cfile);
701   }
702 }
703
704 static void
705 pref_revert(pref_t *pref, gpointer user_data)
706 {
707   gboolean *pref_changed_p = user_data;
708
709   /* Revert the preference to its saved value. */
710   switch (pref->type) {
711
712   case PREF_UINT:
713     if (*pref->varp.uint != pref->saved_val.uint) {
714       *pref_changed_p = TRUE;
715       *pref->varp.uint = pref->saved_val.uint;
716     }
717     break;
718
719   case PREF_BOOL:
720     if (*pref->varp.bool != pref->saved_val.bool) {
721       *pref_changed_p = TRUE;
722       *pref->varp.bool = pref->saved_val.bool;
723     }
724     break;
725
726   case PREF_ENUM:
727     if (*pref->varp.enump != pref->saved_val.enumval) {
728       *pref_changed_p = TRUE;
729       *pref->varp.enump = pref->saved_val.enumval;
730     }
731     break;
732
733   case PREF_STRING:
734     if (*pref->varp.string != pref->saved_val.string &&
735         (*pref->varp.string == NULL ||
736          pref->saved_val.string == NULL ||
737          strcmp(*pref->varp.string, pref->saved_val.string) != 0)) {
738       *pref_changed_p = TRUE;
739       if (*pref->varp.string != NULL)
740         g_free(*pref->varp.string);
741       *pref->varp.string = g_strdup(pref->saved_val.string);
742     }
743     break;
744   }
745 }
746
747 static void
748 module_prefs_revert(module_t *module, gpointer user_data)
749 {
750   gboolean *must_redissect_p = user_data;
751
752   /* For all preferences in this module, revert its value to the value
753      it had when we popped up the Preferences dialog.  Find out whether
754      this changes any of them. */
755   module->prefs_changed = FALSE;        /* assume none of them changed */
756   prefs_pref_foreach(module, pref_revert, &module->prefs_changed);
757
758   /* If any of them changed, indicate that we must redissect and refilter
759      the current capture (if we have one), as the preference change
760      could cause packets to be dissected differently. */
761   if (module->prefs_changed)
762     *must_redissect_p = TRUE;
763 }
764
765 static void
766 prefs_main_cancel_cb(GtkWidget *cancel_bt, gpointer parent_w)
767 {
768   gboolean must_redissect = FALSE;
769
770   /* Free up the current preferences and copy the saved preferences to the
771      current preferences. */
772   free_prefs(&prefs);
773   copy_prefs(&prefs, &saved_prefs);
774
775   /* Now revert the registered preferences. */
776   prefs_module_foreach(module_prefs_revert, &must_redissect);
777
778   /* Now apply the reverted-to preferences. */
779   printer_prefs_apply(gtk_object_get_data(GTK_OBJECT(parent_w), E_PRINT_PAGE_KEY));
780   column_prefs_apply(gtk_object_get_data(GTK_OBJECT(parent_w), E_COLUMN_PAGE_KEY));
781   stream_prefs_apply(gtk_object_get_data(GTK_OBJECT(parent_w), E_STREAM_PAGE_KEY));
782   gui_prefs_apply(gtk_object_get_data(GTK_OBJECT(parent_w), E_GUI_PAGE_KEY));
783   prefs_apply_all();
784
785   gtk_widget_destroy(GTK_WIDGET(parent_w));
786
787   if (must_redissect) {
788     /* Redissect all the packets, and re-evaluate the display filter. */
789     redissect_packets(&cfile);
790   }
791 }
792
793 /* Treat this as a cancel, by calling "prefs_main_cancel_cb()".
794    XXX - that'll destroy the Preferences dialog; will that upset
795    a higher-level handler that says "OK, we've been asked to delete
796    this, so destroy it"? */
797 static gboolean
798 prefs_main_delete_cb(GtkWidget *prefs_w, gpointer dummy)
799 {
800   prefs_main_cancel_cb(NULL, prefs_w);
801   return FALSE;
802 }
803
804 static void
805 prefs_main_destroy_cb(GtkWidget *win, gpointer user_data)
806 {
807   /* Let the preference tabs clean up anything they've done. */
808   printer_prefs_destroy(gtk_object_get_data(GTK_OBJECT(prefs_w), E_PRINT_PAGE_KEY));
809   column_prefs_destroy(gtk_object_get_data(GTK_OBJECT(prefs_w), E_COLUMN_PAGE_KEY));
810   stream_prefs_destroy(gtk_object_get_data(GTK_OBJECT(prefs_w), E_STREAM_PAGE_KEY));
811   gui_prefs_destroy(gtk_object_get_data(GTK_OBJECT(prefs_w), E_GUI_PAGE_KEY));
812
813   /* Free up the saved preferences (both for "prefs" and for registered
814      preferences). */
815   free_prefs(&saved_prefs);
816   prefs_module_foreach(module_prefs_clean, NULL);
817
818   /* Note that we no longer have a "Preferences" dialog box. */
819   prefs_w = NULL;
820 }
821
822 struct properties_data {
823   GtkWidget *w;
824   int page_num;
825   char *title;
826 };
827
828 /* XXX this way of searching the correct page number is really ugly ... */
829 static void
830 module_search_properties(module_t *module, gpointer user_data)
831 {
832   struct properties_data *p = (struct properties_data *)user_data;
833
834   if (p->title == NULL) return;
835   if (strcmp(module->title, p->title) == 0) {
836     /* found it */
837     gtk_notebook_set_page(GTK_NOTEBOOK(p->w), p->page_num);
838     p->title = NULL;
839   } else {
840     p->page_num++;
841   }
842 }
843
844 void
845 properties_cb(GtkWidget *w, gpointer dummy) 
846 {
847   gchar *title = NULL;
848   struct properties_data p;
849
850   if (finfo_selected) {
851     header_field_info *hfinfo = finfo_selected->hfinfo;
852     if (hfinfo->parent == -1) {
853       title = (gchar *)prefs_get_title_by_name(hfinfo->abbrev);
854     } else {
855       title = (gchar *)
856         prefs_get_title_by_name(proto_registrar_get_abbrev(hfinfo->parent));
857     }
858   } else {
859     return;
860   }
861   
862   if (!title) return;
863   
864   if (prefs_w != NULL) {
865     reactivate_window(prefs_w);
866   } else {
867     prefs_cb(w, dummy);
868   }
869
870   p.w = notebook;
871   p.page_num = FIRST_PROTO_PREFS_PAGE;
872   p.title = title;
873
874   prefs_module_foreach(module_search_properties, &p);
875
876 }
877
878 /* Prefs tree selection callback.  The node data has been loaded with 
879    the proper notebook page to load. */
880 static void
881 prefs_tree_select_cb(GtkCTree *ct, GtkCTreeNode *node, gint col, gpointer dummy)
882 {
883   gint page = GPOINTER_TO_INT(gtk_ctree_node_get_row_data(ct, node));
884   
885   if (page >= 0) 
886     gtk_notebook_set_page(GTK_NOTEBOOK(notebook), page);
887 }
888