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