75d2e399faf3042e3c5e1ca9c8c0b147c790d0c3
[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.31 2001/10/23 05:01:02 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   const char *pf_path;
649
650   /* Fetch the preferences (i.e., make sure all the values set in all of
651      the preferences panes have been copied to "prefs" and the registered
652      preferences). */
653   printer_prefs_fetch(gtk_object_get_data(GTK_OBJECT(parent_w), E_PRINT_PAGE_KEY));
654   column_prefs_fetch(gtk_object_get_data(GTK_OBJECT(parent_w), E_COLUMN_PAGE_KEY));
655   stream_prefs_fetch(gtk_object_get_data(GTK_OBJECT(parent_w), E_STREAM_PAGE_KEY));
656   gui_prefs_fetch(gtk_object_get_data(GTK_OBJECT(parent_w), E_GUI_PAGE_KEY));
657   prefs_module_foreach(module_prefs_fetch, &must_redissect);
658
659   /* Create the directory that holds personal configuration files, if
660      necessary.  */
661   if (create_persconffile_dir(&pf_path) == -1) {
662      simple_dialog(ESD_TYPE_WARN, NULL,
663       "Can't create directory\n\"%s\"\nfor preferences file: %s.", pf_path,
664       strerror(errno));
665   } else {
666     /* Write the preferencs out. */
667     err = write_prefs(&pf_path);
668     if (err != 0) {
669        simple_dialog(ESD_TYPE_WARN, NULL,
670         "Can't open preferences file\n\"%s\": %s.", pf_path,
671         strerror(err));
672     }
673   }
674
675   /* Now apply those preferences.
676      XXX - should we do this?  The user didn't click "OK" or "Apply".
677      However:
678
679         1) by saving the preferences they presumably indicate that they
680            like them;
681
682         2) the next time they fire Ethereal up, those preferences will
683            apply;
684
685         3) we'd have to buffer "must_redissect" so that if they do
686            "Apply" after this, we know we have to redissect;
687
688         4) we did apply the protocol preferences, at least, in the past. */
689   printer_prefs_apply(gtk_object_get_data(GTK_OBJECT(parent_w), E_PRINT_PAGE_KEY));
690   column_prefs_apply(gtk_object_get_data(GTK_OBJECT(parent_w), E_COLUMN_PAGE_KEY));
691   stream_prefs_apply(gtk_object_get_data(GTK_OBJECT(parent_w), E_STREAM_PAGE_KEY));
692   gui_prefs_apply(gtk_object_get_data(GTK_OBJECT(parent_w), E_GUI_PAGE_KEY));
693   prefs_module_foreach(module_prefs_fetch, &must_redissect);
694   prefs_apply_all();
695
696   if (must_redissect) {
697     /* Redissect all the packets, and re-evaluate the display filter. */
698     redissect_packets(&cfile);
699   }
700 }
701
702 static void
703 pref_revert(pref_t *pref, gpointer user_data)
704 {
705   gboolean *pref_changed_p = user_data;
706
707   /* Revert the preference to its saved value. */
708   switch (pref->type) {
709
710   case PREF_UINT:
711     if (*pref->varp.uint != pref->saved_val.uint) {
712       *pref_changed_p = TRUE;
713       *pref->varp.uint = pref->saved_val.uint;
714     }
715     break;
716
717   case PREF_BOOL:
718     if (*pref->varp.bool != pref->saved_val.bool) {
719       *pref_changed_p = TRUE;
720       *pref->varp.bool = pref->saved_val.bool;
721     }
722     break;
723
724   case PREF_ENUM:
725     if (*pref->varp.enump != pref->saved_val.enumval) {
726       *pref_changed_p = TRUE;
727       *pref->varp.enump = pref->saved_val.enumval;
728     }
729     break;
730
731   case PREF_STRING:
732     if (*pref->varp.string != pref->saved_val.string &&
733         (*pref->varp.string == NULL ||
734          pref->saved_val.string == NULL ||
735          strcmp(*pref->varp.string, pref->saved_val.string) != 0)) {
736       *pref_changed_p = TRUE;
737       if (*pref->varp.string != NULL)
738         g_free(*pref->varp.string);
739       *pref->varp.string = g_strdup(pref->saved_val.string);
740     }
741     break;
742   }
743 }
744
745 static void
746 module_prefs_revert(module_t *module, gpointer user_data)
747 {
748   gboolean *must_redissect_p = user_data;
749
750   /* For all preferences in this module, revert its value to the value
751      it had when we popped up the Preferences dialog.  Find out whether
752      this changes any of them. */
753   module->prefs_changed = FALSE;        /* assume none of them changed */
754   prefs_pref_foreach(module, pref_revert, &module->prefs_changed);
755
756   /* If any of them changed, indicate that we must redissect and refilter
757      the current capture (if we have one), as the preference change
758      could cause packets to be dissected differently. */
759   if (module->prefs_changed)
760     *must_redissect_p = TRUE;
761 }
762
763 static void
764 prefs_main_cancel_cb(GtkWidget *cancel_bt, gpointer parent_w)
765 {
766   gboolean must_redissect = FALSE;
767
768   /* Free up the current preferences and copy the saved preferences to the
769      current preferences. */
770   free_prefs(&prefs);
771   copy_prefs(&prefs, &saved_prefs);
772
773   /* Now revert the registered preferences. */
774   prefs_module_foreach(module_prefs_revert, &must_redissect);
775
776   /* Now apply the reverted-to preferences. */
777   printer_prefs_apply(gtk_object_get_data(GTK_OBJECT(parent_w), E_PRINT_PAGE_KEY));
778   column_prefs_apply(gtk_object_get_data(GTK_OBJECT(parent_w), E_COLUMN_PAGE_KEY));
779   stream_prefs_apply(gtk_object_get_data(GTK_OBJECT(parent_w), E_STREAM_PAGE_KEY));
780   gui_prefs_apply(gtk_object_get_data(GTK_OBJECT(parent_w), E_GUI_PAGE_KEY));
781   prefs_apply_all();
782
783   gtk_widget_destroy(GTK_WIDGET(parent_w));
784
785   if (must_redissect) {
786     /* Redissect all the packets, and re-evaluate the display filter. */
787     redissect_packets(&cfile);
788   }
789 }
790
791 /* Treat this as a cancel, by calling "prefs_main_cancel_cb()".
792    XXX - that'll destroy the Preferences dialog; will that upset
793    a higher-level handler that says "OK, we've been asked to delete
794    this, so destroy it"? */
795 static gboolean
796 prefs_main_delete_cb(GtkWidget *prefs_w, gpointer dummy)
797 {
798   prefs_main_cancel_cb(NULL, prefs_w);
799   return FALSE;
800 }
801
802 static void
803 prefs_main_destroy_cb(GtkWidget *win, gpointer user_data)
804 {
805   /* Let the preference tabs clean up anything they've done. */
806   printer_prefs_destroy(gtk_object_get_data(GTK_OBJECT(prefs_w), E_PRINT_PAGE_KEY));
807   column_prefs_destroy(gtk_object_get_data(GTK_OBJECT(prefs_w), E_COLUMN_PAGE_KEY));
808   stream_prefs_destroy(gtk_object_get_data(GTK_OBJECT(prefs_w), E_STREAM_PAGE_KEY));
809   gui_prefs_destroy(gtk_object_get_data(GTK_OBJECT(prefs_w), E_GUI_PAGE_KEY));
810
811   /* Free up the saved preferences (both for "prefs" and for registered
812      preferences). */
813   free_prefs(&saved_prefs);
814   prefs_module_foreach(module_prefs_clean, NULL);
815
816   /* Note that we no longer have a "Preferences" dialog box. */
817   prefs_w = NULL;
818 }
819
820 struct properties_data {
821   GtkWidget *w;
822   int page_num;
823   char *title;
824 };
825
826 /* XXX this way of searching the correct page number is really ugly ... */
827 static void
828 module_search_properties(module_t *module, gpointer user_data)
829 {
830   struct properties_data *p = (struct properties_data *)user_data;
831
832   if (p->title == NULL) return;
833   if (strcmp(module->title, p->title) == 0) {
834     /* found it */
835     gtk_notebook_set_page(GTK_NOTEBOOK(p->w), p->page_num);
836     p->title = NULL;
837   } else {
838     p->page_num++;
839   }
840 }
841
842 void
843 properties_cb(GtkWidget *w, gpointer dummy) 
844 {
845   gchar *title = NULL;
846   struct properties_data p;
847
848   if (finfo_selected) {
849     header_field_info *hfinfo = finfo_selected->hfinfo;
850     if (hfinfo->parent == -1) {
851       title = (gchar *)prefs_get_title_by_name(hfinfo->abbrev);
852     } else {
853       title = (gchar *)
854         prefs_get_title_by_name(proto_registrar_get_abbrev(hfinfo->parent));
855     }
856   } else {
857     return;
858   }
859   
860   if (!title) return;
861   
862   if (prefs_w != NULL) {
863     reactivate_window(prefs_w);
864   } else {
865     prefs_cb(w, dummy);
866   }
867
868   p.w = notebook;
869   p.page_num = FIRST_PROTO_PREFS_PAGE;
870   p.title = title;
871
872   prefs_module_foreach(module_search_properties, &p);
873
874 }
875
876 /* Prefs tree selection callback.  The node data has been loaded with 
877    the proper notebook page to load. */
878 static void
879 prefs_tree_select_cb(GtkCTree *ct, GtkCTreeNode *node, gint col, gpointer dummy)
880 {
881   gint page = GPOINTER_TO_INT(gtk_ctree_node_get_row_data(ct, node));
882   
883   if (page >= 0) 
884     gtk_notebook_set_page(GTK_NOTEBOOK(notebook), page);
885 }
886