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