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