Instead of each set of built-in preferences having "ok", "save",
[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.21 2000/08/21 08:09:14 guy Exp $
5  *
6  * Ethereal - Network traffic analyzer
7  * By Gerald Combs <gerald@zing.org>
8  * Copyright 1998 Gerald Combs
9  *
10  * 
11  * This program is free software; you can redistribute it and/or
12  * modify it under the terms of the GNU General Public License
13  * as published by the Free Software Foundation; either version 2
14  * of the License, or (at your option) any later version.
15  * 
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  * GNU General Public License for more details.
20  * 
21  * You should have received a copy of the GNU General Public License
22  * along with this program; if not, write to the Free Software
23  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
24  */
25
26 #ifdef HAVE_CONFIG_H
27 # include "config.h"
28 #endif
29
30 #ifdef HAVE_SYS_TYPES_H
31 #include <sys/types.h>
32 #endif
33
34 #include <gtk/gtk.h>
35
36 #include <stdlib.h>
37 #include <string.h>
38 #include <ctype.h>
39 #include <errno.h>
40
41 #ifdef HAVE_UNISTD_H
42 #include <unistd.h>
43 #endif
44
45 #ifdef HAVE_SYS_STAT_H
46 #include <sys/stat.h>
47 #endif
48
49 #include "main.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 "util.h"
60 #include "ui_util.h"
61 #include "dlg_utils.h"
62 #include "simple_dialog.h"
63
64 #include "prefs-int.h"
65
66 static void     prefs_main_ok_cb(GtkWidget *, gpointer);
67 static void     prefs_main_apply_cb(GtkWidget *, gpointer);
68 static void     prefs_main_save_cb(GtkWidget *, gpointer);
69 static void     prefs_main_cancel_cb(GtkWidget *, gpointer);
70 static gboolean prefs_main_delete_cb(GtkWidget *, gpointer);
71 static void     prefs_main_destroy_cb(GtkWidget *, 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 static void
102 pref_show(pref_t *pref, gpointer user_data)
103 {
104   GtkWidget *main_tb = user_data;
105   const char *title;
106   char *label_string;
107   GtkWidget *label, *menu, *menu_item, *widget, *button;
108   GSList *rb_group;
109   char uint_str[10+1];
110   const enum_val *enum_valp;
111   int menu_index, index;
112
113   /* Give this preference a label which is its title, followed by a colon,
114      and left-align it. */
115   title = pref->title;
116   label_string = g_malloc(strlen(title) + 2);
117   strcpy(label_string, title);
118   strcat(label_string, ":");
119   label = gtk_label_new(label_string);
120   g_free(label_string);
121   gtk_misc_set_alignment(GTK_MISC(label), 1.0, 0.5);
122
123   /* Attach it to the table. */
124   gtk_table_attach_defaults(GTK_TABLE(main_tb), label, 0, 1, pref->ordinal,
125                                 pref->ordinal+1);
126
127   /* Save the current value of the preference, so that we can revert it if
128      the user does "Apply" and then "Cancel", and create the control for
129      editing the preference. */
130   switch (pref->type) {
131
132   case PREF_UINT:
133     pref->saved_val.uint = *pref->varp.uint;
134
135     /* XXX - there are no uint spinbuttons, so we can't use a spinbutton.
136        Even more annoyingly, even if there were, GLib doesn't define
137        G_MAXUINT - but I think ANSI C may define UINT_MAX, so we could
138        use that. */
139     widget = gtk_entry_new();
140     switch (pref->info.base) {
141
142     case 10:
143       sprintf(uint_str, "%u", pref->saved_val.uint);
144       break;
145
146     case 8:
147       sprintf(uint_str, "%o", pref->saved_val.uint);
148       break;
149
150     case 16:
151       sprintf(uint_str, "%x", pref->saved_val.uint);
152       break;
153     }
154     gtk_entry_set_text(GTK_ENTRY(widget), uint_str);
155     pref->control = widget;
156     break;
157
158   case PREF_BOOL:
159     pref->saved_val.bool = *pref->varp.bool;
160     widget = gtk_check_button_new();
161     gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(widget), pref->saved_val.bool);
162     pref->control = widget;
163     break;
164
165   case PREF_ENUM:
166     pref->saved_val.enumval = *pref->varp.enump;
167     if (pref->info.enum_info.radio_buttons) {
168       /* Show it as radio buttons. */
169       widget = gtk_hbox_new(FALSE, 0);
170       rb_group = NULL;
171       for (enum_valp = pref->info.enum_info.enumvals, index = 0;
172                 enum_valp->name != NULL; enum_valp++, index++) {
173         button = gtk_radio_button_new_with_label(rb_group, enum_valp->name);
174         if (rb_group == NULL)
175           rb_group = gtk_radio_button_group(GTK_RADIO_BUTTON(button));
176         gtk_box_pack_start(GTK_BOX(widget), button, FALSE, FALSE, 10);
177         if (enum_valp->value == pref->saved_val.enumval)
178           gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(button), TRUE);
179         pref->control = button;
180       }
181     } else {
182       /* Show it as an option menu. */
183       menu = gtk_menu_new();
184       menu_index = -1;
185       for (enum_valp = pref->info.enum_info.enumvals, index = 0;
186                 enum_valp->name != NULL; enum_valp++, index++) {
187         menu_item = gtk_menu_item_new_with_label(enum_valp->name);
188         gtk_menu_append(GTK_MENU(menu), menu_item);
189         if (enum_valp->value == pref->saved_val.enumval)
190           menu_index = index;
191       }
192
193       /* Create the option menu from the option */
194       widget = gtk_option_menu_new();
195       gtk_option_menu_set_menu(GTK_OPTION_MENU(widget), menu);
196
197       /* Set its current value to the variable's current value */
198       if (menu_index != -1)
199         gtk_option_menu_set_history(GTK_OPTION_MENU(widget), menu_index);
200       pref->control = widget;
201     }
202     break;
203
204   case PREF_STRING:
205     widget = gtk_entry_new();
206     if (pref->saved_val.string != NULL)
207       g_free(pref->saved_val.string);
208     pref->saved_val.string = g_strdup(*pref->varp.string);
209     gtk_entry_set_text(GTK_ENTRY(widget), pref->saved_val.string);
210     pref->control = widget;
211     break;
212
213   default:
214     g_assert_not_reached();
215     widget = NULL;
216     break;
217   }
218
219   gtk_table_attach_defaults(GTK_TABLE(main_tb), widget, 1, 2, pref->ordinal,
220                                 pref->ordinal+1);
221 }
222
223 static void
224 module_prefs_show(module_t *module, gpointer user_data)
225 {
226   GtkWidget *prefs_nb = user_data;
227   GtkWidget *main_vb, *main_tb, *label;
228
229   /* Main vertical box */
230   main_vb = gtk_vbox_new(FALSE, 5);
231   gtk_container_border_width(GTK_CONTAINER(main_vb), 5);
232
233   /* Main table */
234   main_tb = gtk_table_new(module->numprefs, 2, FALSE);
235   gtk_box_pack_start(GTK_BOX(main_vb), main_tb, FALSE, FALSE, 0);
236   gtk_table_set_row_spacings(GTK_TABLE(main_tb), 10);
237   gtk_table_set_col_spacings(GTK_TABLE(main_tb), 15);
238
239   /* Add items for each of the preferences */
240   prefs_pref_foreach(module, pref_show, main_tb);
241
242   label = gtk_label_new(module->title);
243   gtk_notebook_append_page(GTK_NOTEBOOK(prefs_nb), main_vb, label);
244
245   /* Show 'em what we got */
246   gtk_widget_show_all(main_vb);
247 }
248
249 void
250 prefs_cb(GtkWidget *w, gpointer dummy) {
251   GtkWidget *main_vb, *top_hb, *bbox, *prefs_nb,
252             *ok_bt, *apply_bt, *save_bt, *cancel_bt;
253   GtkWidget *print_pg, *column_pg, *stream_pg, *gui_pg, *label;
254
255   if (prefs_w != NULL) {
256     /* There's already a "Preferences" dialog box; reactivate it. */
257     reactivate_window(prefs_w);
258     return;
259   }
260
261   /* Save the current preferences, so we can revert to those values
262      if the user presses "Cancel". */
263   copy_prefs(&saved_prefs, &prefs);
264
265   prefs_w = dlg_window_new();
266   gtk_window_set_title(GTK_WINDOW(prefs_w), "Ethereal: Preferences");
267   gtk_signal_connect(GTK_OBJECT(prefs_w), "delete_event",
268     GTK_SIGNAL_FUNC(prefs_main_delete_cb), NULL);
269   gtk_signal_connect(GTK_OBJECT(prefs_w), "destroy",
270         GTK_SIGNAL_FUNC(prefs_main_destroy_cb), NULL);
271   
272   /* Container for each row of widgets */
273   main_vb = gtk_vbox_new(FALSE, 5);
274   gtk_container_border_width(GTK_CONTAINER(main_vb), 5);
275   gtk_container_add(GTK_CONTAINER(prefs_w), main_vb);
276   gtk_widget_show(main_vb);
277   
278   /* Top row: Preferences notebook */
279   top_hb = gtk_hbox_new(FALSE, 1);
280   gtk_container_add(GTK_CONTAINER(main_vb), top_hb);
281   gtk_widget_show(top_hb);
282   
283   notebook = prefs_nb = gtk_notebook_new();
284   gtk_container_add(GTK_CONTAINER(main_vb), prefs_nb);
285   gtk_widget_show(prefs_nb);
286   
287   /* Printing prefs */
288   print_pg = printer_prefs_show();
289   gtk_object_set_data(GTK_OBJECT(prefs_w), E_PRINT_PAGE_KEY, print_pg);
290   label = gtk_label_new ("Printing");
291   gtk_notebook_append_page (GTK_NOTEBOOK(prefs_nb), print_pg, label);
292     
293   /* Column prefs */
294   column_pg = column_prefs_show();
295   gtk_object_set_data(GTK_OBJECT(prefs_w), E_COLUMN_PAGE_KEY, column_pg);
296   label = gtk_label_new ("Columns");
297   gtk_notebook_append_page (GTK_NOTEBOOK(prefs_nb), column_pg, label);
298   
299   /* TCP Streams prefs */
300   stream_pg = stream_prefs_show();
301   gtk_object_set_data(GTK_OBJECT(prefs_w), E_STREAM_PAGE_KEY, stream_pg);
302   label = gtk_label_new ("TCP Streams");
303   gtk_notebook_append_page (GTK_NOTEBOOK(prefs_nb), stream_pg, label);
304
305   /* GUI prefs */
306   gui_pg = gui_prefs_show();
307   gtk_object_set_data(GTK_OBJECT(prefs_w), E_GUI_PAGE_KEY, gui_pg);
308   label = gtk_label_new ("GUI");
309   gtk_notebook_append_page (GTK_NOTEBOOK(prefs_nb), gui_pg, label);
310
311   /* Registered prefs */
312   prefs_module_foreach(module_prefs_show, prefs_nb);
313
314   /* Button row: OK and cancel buttons */
315   bbox = gtk_hbutton_box_new();
316   gtk_button_box_set_layout (GTK_BUTTON_BOX (bbox), GTK_BUTTONBOX_END);
317   gtk_button_box_set_spacing(GTK_BUTTON_BOX(bbox), 5);
318   gtk_container_add(GTK_CONTAINER(main_vb), bbox);
319   gtk_widget_show(bbox);
320   
321   ok_bt = gtk_button_new_with_label ("OK");
322   gtk_signal_connect(GTK_OBJECT(ok_bt), "clicked",
323     GTK_SIGNAL_FUNC(prefs_main_ok_cb), GTK_OBJECT(prefs_w));
324   GTK_WIDGET_SET_FLAGS(ok_bt, GTK_CAN_DEFAULT);
325   gtk_box_pack_start (GTK_BOX (bbox), ok_bt, TRUE, TRUE, 0);
326   gtk_widget_grab_default(ok_bt);
327   gtk_widget_show(ok_bt);
328
329   apply_bt = gtk_button_new_with_label ("Apply");
330   gtk_signal_connect(GTK_OBJECT(apply_bt), "clicked",
331     GTK_SIGNAL_FUNC(prefs_main_apply_cb), GTK_OBJECT(prefs_w));
332   GTK_WIDGET_SET_FLAGS(apply_bt, GTK_CAN_DEFAULT);
333   gtk_box_pack_start(GTK_BOX (bbox), apply_bt, TRUE, TRUE, 0);
334   gtk_widget_show(apply_bt);
335
336   save_bt = gtk_button_new_with_label ("Save");
337   gtk_signal_connect(GTK_OBJECT(save_bt), "clicked",
338     GTK_SIGNAL_FUNC(prefs_main_save_cb), GTK_OBJECT(prefs_w));
339   GTK_WIDGET_SET_FLAGS(save_bt, GTK_CAN_DEFAULT);
340   gtk_box_pack_start (GTK_BOX (bbox), save_bt, TRUE, TRUE, 0);
341   gtk_widget_show(save_bt);
342   
343   cancel_bt = gtk_button_new_with_label ("Cancel");
344   gtk_signal_connect(GTK_OBJECT(cancel_bt), "clicked",
345     GTK_SIGNAL_FUNC(prefs_main_cancel_cb), GTK_OBJECT(prefs_w));
346   GTK_WIDGET_SET_FLAGS(cancel_bt, GTK_CAN_DEFAULT);
347   gtk_box_pack_start (GTK_BOX (bbox), cancel_bt, TRUE, TRUE, 0);
348   gtk_widget_show(cancel_bt);
349
350   /* Catch the "key_press_event" signal in the window, so that we can catch
351      the ESC key being pressed and act as if the "Cancel" button had
352      been selected. */
353   dlg_set_cancel(prefs_w, cancel_bt);
354
355   gtk_widget_show(prefs_w);
356 }
357
358 static void
359 pref_fetch(pref_t *pref, gpointer user_data)
360 {
361   GtkWidget *label;
362   char *label_string;
363   char *str_val;
364   char *p;
365   guint uval;
366   gboolean bval;
367   GSList *rb_entry;
368   GtkWidget *button;
369   gint enumval;
370   gboolean *pref_changed_p = user_data;
371
372   /* Fetch the value of the preference, and set the appropriate variable
373      to it. */
374   switch (pref->type) {
375
376   case PREF_UINT:
377     str_val = gtk_entry_get_text(GTK_ENTRY(pref->control));
378     uval = strtoul(str_val, &p, pref->info.base);
379 #if 0
380     if (p == value || *p != '\0')
381       return PREFS_SET_SYNTAX_ERR;      /* number was bad */
382 #endif
383     if (*pref->varp.uint != uval) {
384       *pref_changed_p = TRUE;
385       *pref->varp.uint = uval;
386     }
387     break;
388
389   case PREF_BOOL:
390     bval = GTK_TOGGLE_BUTTON(pref->control)->active;
391     if (*pref->varp.bool != bval) {
392       *pref_changed_p = TRUE;
393       *pref->varp.bool = bval;
394     }
395     break;
396
397   case PREF_ENUM:
398     if (pref->info.enum_info.radio_buttons) {
399       /* Go through the list of of radio buttons in the group, and find
400          the first one that's active. */
401       button = NULL;
402       for (rb_entry = gtk_radio_button_group(GTK_RADIO_BUTTON(pref->control));
403                 rb_entry != NULL;
404                 rb_entry = g_slist_next(rb_entry)) {
405         button = rb_entry->data;
406         if (GTK_TOGGLE_BUTTON(button)->active)
407           break;
408       }
409       /* OK, now find that button's label. */
410       label = GTK_BIN(button)->child;
411     } else {
412       /* Get the label for the currently active entry in the option menu.
413          Yes, this is how you do it.  See FAQ 6.8 in the GTK+ FAQ. */
414       label = GTK_BIN(pref->control)->child;
415     }
416
417     /* Get the label, and translate it to a value. */
418     gtk_label_get(GTK_LABEL(label), &label_string);
419     enumval = find_val_for_string(label_string,
420                                         pref->info.enum_info.enumvals, 1);
421     if (*pref->varp.enump != enumval) {
422       *pref_changed_p = TRUE;
423       *pref->varp.enump = enumval;
424     }
425     break;
426
427   case PREF_STRING:
428     str_val = gtk_entry_get_text(GTK_ENTRY(pref->control));
429     if (*pref->varp.string == NULL || strcmp(*pref->varp.string, str_val) != 0) {
430       *pref_changed_p = TRUE;
431       if (*pref->varp.string != NULL)
432         g_free(*pref->varp.string);
433       *pref->varp.string = g_strdup(str_val);
434     }
435     break;
436   }
437 }
438
439 static void
440 module_prefs_fetch(module_t *module, gpointer user_data)
441 {
442   gboolean *must_redissect_p = user_data;
443
444   /* For all preferences in this module, fetch its value from this
445      module's notebook page.  Find out whether any of them changed. */
446   module->prefs_changed = FALSE;        /* assume none of them changed */
447   prefs_pref_foreach(module, pref_fetch, &module->prefs_changed);
448
449   /* If any of them changed, indicate that we must redissect and refilter
450      the current capture (if we have one), as the preference change
451      could cause packets to be dissected differently. */
452   if (module->prefs_changed)
453     *must_redissect_p = TRUE;
454 }
455
456 static void
457 pref_clean(pref_t *pref, gpointer user_data)
458 {
459   switch (pref->type) {
460
461   case PREF_UINT:
462     break;
463
464   case PREF_BOOL:
465     break;
466
467   case PREF_ENUM:
468     break;
469
470   case PREF_STRING:
471     if (pref->saved_val.string != NULL) {
472       g_free(pref->saved_val.string);
473       pref->saved_val.string = NULL;
474     }
475     break;
476   }
477 }
478
479 static void
480 module_prefs_clean(module_t *module, gpointer user_data)
481 {
482   /* For all preferences in this module, clean up any cruft allocated for
483      use by the GUI code. */
484   prefs_pref_foreach(module, pref_clean, NULL);
485 }
486
487 static void
488 prefs_main_ok_cb(GtkWidget *ok_bt, gpointer parent_w)
489 {
490   gboolean must_redissect = FALSE;
491
492   /* Fetch the preferences (i.e., make sure all the values set in all of
493      the preferences panes have been copied to "prefs" and the registered
494      preferences). */
495   printer_prefs_fetch(gtk_object_get_data(GTK_OBJECT(parent_w), E_PRINT_PAGE_KEY));
496   column_prefs_fetch(gtk_object_get_data(GTK_OBJECT(parent_w), E_COLUMN_PAGE_KEY));
497   stream_prefs_fetch(gtk_object_get_data(GTK_OBJECT(parent_w), E_STREAM_PAGE_KEY));
498   gui_prefs_fetch(gtk_object_get_data(GTK_OBJECT(parent_w), E_GUI_PAGE_KEY));
499   prefs_module_foreach(module_prefs_fetch, &must_redissect);
500
501   /* Now apply those preferences. */
502   printer_prefs_apply(gtk_object_get_data(GTK_OBJECT(parent_w), E_PRINT_PAGE_KEY));
503   column_prefs_apply(gtk_object_get_data(GTK_OBJECT(parent_w), E_COLUMN_PAGE_KEY));
504   stream_prefs_apply(gtk_object_get_data(GTK_OBJECT(parent_w), E_STREAM_PAGE_KEY));
505   gui_prefs_apply(gtk_object_get_data(GTK_OBJECT(parent_w), E_GUI_PAGE_KEY));
506   prefs_apply_all();
507
508   /* Now destroy the "Preferences" dialog. */
509   gtk_widget_destroy(GTK_WIDGET(parent_w));
510
511   if (must_redissect) {
512     /* Redissect all the packets, and re-evaluate the display filter. */
513     redissect_packets(&cfile);
514   }
515 }
516
517 static void
518 prefs_main_apply_cb(GtkWidget *apply_bt, gpointer parent_w)
519 {
520   gboolean must_redissect = FALSE;
521
522   /* Fetch the preferences (i.e., make sure all the values set in all of
523      the preferences panes have been copied to "prefs" and the registered
524      preferences). */
525   printer_prefs_fetch(gtk_object_get_data(GTK_OBJECT(parent_w), E_PRINT_PAGE_KEY));
526   column_prefs_fetch(gtk_object_get_data(GTK_OBJECT(parent_w), E_COLUMN_PAGE_KEY));
527   stream_prefs_fetch(gtk_object_get_data(GTK_OBJECT(parent_w), E_STREAM_PAGE_KEY));
528   gui_prefs_fetch(gtk_object_get_data(GTK_OBJECT(parent_w), E_GUI_PAGE_KEY));
529   prefs_module_foreach(module_prefs_fetch, &must_redissect);
530
531   /* Now apply those preferences. */
532   printer_prefs_apply(gtk_object_get_data(GTK_OBJECT(parent_w), E_PRINT_PAGE_KEY));
533   column_prefs_apply(gtk_object_get_data(GTK_OBJECT(parent_w), E_COLUMN_PAGE_KEY));
534   stream_prefs_apply(gtk_object_get_data(GTK_OBJECT(parent_w), E_STREAM_PAGE_KEY));
535   gui_prefs_apply(gtk_object_get_data(GTK_OBJECT(parent_w), E_GUI_PAGE_KEY));
536   prefs_module_foreach(module_prefs_fetch, &must_redissect);
537   prefs_apply_all();
538
539   if (must_redissect) {
540     /* Redissect all the packets, and re-evaluate the display filter. */
541     redissect_packets(&cfile);
542   }
543 }
544
545 static void
546 prefs_main_save_cb(GtkWidget *save_bt, gpointer parent_w)
547 {
548   gboolean must_redissect = FALSE;
549   int err;
550   char *pf_path;
551
552   /* Fetch the preferences (i.e., make sure all the values set in all of
553      the preferences panes have been copied to "prefs" and the registered
554      preferences). */
555   printer_prefs_fetch(gtk_object_get_data(GTK_OBJECT(parent_w), E_PRINT_PAGE_KEY));
556   column_prefs_fetch(gtk_object_get_data(GTK_OBJECT(parent_w), E_COLUMN_PAGE_KEY));
557   stream_prefs_fetch(gtk_object_get_data(GTK_OBJECT(parent_w), E_STREAM_PAGE_KEY));
558   gui_prefs_fetch(gtk_object_get_data(GTK_OBJECT(parent_w), E_GUI_PAGE_KEY));
559   prefs_module_foreach(module_prefs_fetch, &must_redissect);
560
561   /* Write the preferencs out. */
562   err = write_prefs(&pf_path);
563   if (err != 0) {
564      simple_dialog(ESD_TYPE_WARN, NULL,
565       "Can't open preferences file\n\"%s\": %s.", pf_path,
566       strerror(err));
567   }
568
569   /* Now apply those preferences.
570      XXX - should we do this?  The user didn't click "OK" or "Apply".
571      However:
572
573         1) by saving the preferences they presumably indicate that they
574            like them;
575
576         2) the next time they fire Ethereal up, those preferences will
577            apply;
578
579         3) we'd have to buffer "must_redissect" so that if they do
580            "Apply" after this, we know we have to redissect;
581
582         4) we did apply the protocol preferences, at least, in the past. */
583   printer_prefs_apply(gtk_object_get_data(GTK_OBJECT(parent_w), E_PRINT_PAGE_KEY));
584   column_prefs_apply(gtk_object_get_data(GTK_OBJECT(parent_w), E_COLUMN_PAGE_KEY));
585   stream_prefs_apply(gtk_object_get_data(GTK_OBJECT(parent_w), E_STREAM_PAGE_KEY));
586   gui_prefs_apply(gtk_object_get_data(GTK_OBJECT(parent_w), E_GUI_PAGE_KEY));
587   prefs_module_foreach(module_prefs_fetch, &must_redissect);
588   prefs_apply_all();
589
590   if (must_redissect) {
591     /* Redissect all the packets, and re-evaluate the display filter. */
592     redissect_packets(&cfile);
593   }
594 }
595
596 static void
597 pref_revert(pref_t *pref, gpointer user_data)
598 {
599   gboolean *pref_changed_p = user_data;
600
601   /* Revert the preference to its saved value. */
602   switch (pref->type) {
603
604   case PREF_UINT:
605     if (*pref->varp.uint != pref->saved_val.uint) {
606       *pref_changed_p = TRUE;
607       *pref->varp.uint = pref->saved_val.uint;
608     }
609     break;
610
611   case PREF_BOOL:
612     if (*pref->varp.bool != pref->saved_val.bool) {
613       *pref_changed_p = TRUE;
614       *pref->varp.bool = pref->saved_val.bool;
615     }
616     break;
617
618   case PREF_ENUM:
619     if (*pref->varp.enump != pref->saved_val.enumval) {
620       *pref_changed_p = TRUE;
621       *pref->varp.enump = pref->saved_val.enumval;
622     }
623     break;
624
625   case PREF_STRING:
626     if (*pref->varp.string != pref->saved_val.string &&
627         (*pref->varp.string == NULL ||
628          pref->saved_val.string == NULL ||
629          strcmp(*pref->varp.string, pref->saved_val.string) != 0)) {
630       *pref_changed_p = TRUE;
631       if (*pref->varp.string != NULL)
632         g_free(*pref->varp.string);
633       *pref->varp.string = g_strdup(pref->saved_val.string);
634     }
635     break;
636   }
637 }
638
639 static void
640 module_prefs_revert(module_t *module, gpointer user_data)
641 {
642   gboolean *must_redissect_p = user_data;
643
644   /* For all preferences in this module, revert its value to the value
645      it had when we popped up the Preferences dialog.  Find out whether
646      this changes any of them. */
647   module->prefs_changed = FALSE;        /* assume none of them changed */
648   prefs_pref_foreach(module, pref_revert, &module->prefs_changed);
649
650   /* If any of them changed, indicate that we must redissect and refilter
651      the current capture (if we have one), as the preference change
652      could cause packets to be dissected differently. */
653   if (module->prefs_changed)
654     *must_redissect_p = TRUE;
655 }
656
657 static void
658 prefs_main_cancel_cb(GtkWidget *cancel_bt, gpointer parent_w)
659 {
660   gboolean must_redissect = FALSE;
661
662   /* Free up the current preferences and copy the saved preferences to the
663      current preferences. */
664   free_prefs(&prefs);
665   copy_prefs(&prefs, &saved_prefs);
666
667   /* Now revert the registered preferences. */
668   prefs_module_foreach(module_prefs_revert, &must_redissect);
669
670   /* Now apply the reverted-to preferences. */
671   printer_prefs_apply(gtk_object_get_data(GTK_OBJECT(parent_w), E_PRINT_PAGE_KEY));
672   column_prefs_apply(gtk_object_get_data(GTK_OBJECT(parent_w), E_COLUMN_PAGE_KEY));
673   stream_prefs_apply(gtk_object_get_data(GTK_OBJECT(parent_w), E_STREAM_PAGE_KEY));
674   gui_prefs_apply(gtk_object_get_data(GTK_OBJECT(parent_w), E_GUI_PAGE_KEY));
675   prefs_apply_all();
676
677   gtk_widget_destroy(GTK_WIDGET(parent_w));
678
679   if (must_redissect) {
680     /* Redissect all the packets, and re-evaluate the display filter. */
681     redissect_packets(&cfile);
682   }
683 }
684
685 /* Treat this as a cancel, by calling "prefs_main_cancel_cb()".
686    XXX - that'll destroy the Preferences dialog; will that upset
687    a higher-level handler that says "OK, we've been asked to delete
688    this, so destroy it"? */
689 static gboolean
690 prefs_main_delete_cb(GtkWidget *prefs_w, gpointer dummy)
691 {
692   prefs_main_cancel_cb(NULL, prefs_w);
693   return FALSE;
694 }
695
696 static void
697 prefs_main_destroy_cb(GtkWidget *win, gpointer user_data)
698 {
699   /* Let the preference tabs clean up anything they've done. */
700   printer_prefs_destroy(gtk_object_get_data(GTK_OBJECT(prefs_w), E_PRINT_PAGE_KEY));
701   column_prefs_destroy(gtk_object_get_data(GTK_OBJECT(prefs_w), E_COLUMN_PAGE_KEY));
702   stream_prefs_destroy(gtk_object_get_data(GTK_OBJECT(prefs_w), E_STREAM_PAGE_KEY));
703   gui_prefs_destroy(gtk_object_get_data(GTK_OBJECT(prefs_w), E_GUI_PAGE_KEY));
704
705   /* Free up the saved preferences (both for "prefs" and for registered
706      preferences). */
707   free_prefs(&saved_prefs);
708   prefs_module_foreach(module_prefs_clean, NULL);
709
710   /* Note that we no longer have a "Preferences" dialog box. */
711   prefs_w = NULL;
712 }
713
714 struct properties_data {
715   GtkWidget *w;
716   int page_num;
717   char *title;
718 };
719
720 /* XXX this way of searching the correct page number is really ugly ... */
721 static void
722 module_search_properties(module_t *module, gpointer user_data)
723 {
724   struct properties_data *p = (struct properties_data *)user_data;
725
726   if (p->title == NULL) return;
727   if (strcmp(module->title, p->title) == 0) {
728     /* found it */
729     gtk_notebook_set_page(GTK_NOTEBOOK(p->w), p->page_num);
730     p->title = NULL;
731   } else {
732     p->page_num++;
733   }
734 }
735
736 void
737 properties_cb(GtkWidget *w, gpointer dummy) 
738 {
739   gchar *title = NULL;
740   struct properties_data p;
741
742   if (finfo_selected) {
743     header_field_info *hfinfo = finfo_selected->hfinfo;
744     if (hfinfo->parent == -1) {
745       title = (gchar *)prefs_get_title_by_name(hfinfo->abbrev);
746     } else {
747       title = (gchar *)
748         prefs_get_title_by_name(proto_registrar_get_abbrev(hfinfo->parent));
749     }
750   } else {
751     return;
752   }
753   
754   if (!title) return;
755   
756   if (prefs_w != NULL) {
757     reactivate_window(prefs_w);
758   } else {
759     prefs_cb(w, dummy);
760   }
761
762   p.w = notebook;
763   p.page_num = FIRST_PROTO_PREFS_PAGE;
764   p.title = title;
765
766   prefs_module_foreach(module_search_properties, &p);
767
768 }