Add a preferences page for the name resolution flags.
[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.38 2002/01/13 20:35:12 guy Exp $
5  *
6  * Ethereal - Network traffic analyzer
7  * By Gerald Combs <gerald@ethereal.com>
8  * Copyright 1998 Gerald Combs
9  * 
10  * This program is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU General Public License
12  * as published by the Free Software Foundation; either version 2
13  * of the License, or (at your option) any later version.
14  * 
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  * 
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
23  */
24
25 #ifdef HAVE_CONFIG_H
26 # include "config.h"
27 #endif
28
29 #ifdef HAVE_SYS_TYPES_H
30 #include <sys/types.h>
31 #endif
32
33 #include <gtk/gtk.h>
34
35 #include <stdlib.h>
36 #include <string.h>
37 #include <ctype.h>
38 #include <errno.h>
39
40 #ifdef HAVE_UNISTD_H
41 #include <unistd.h>
42 #endif
43
44 #ifdef HAVE_SYS_STAT_H
45 #include <sys/stat.h>
46 #endif
47
48 #include <epan/filesystem.h>
49
50 #include "main.h"
51 #include "packet.h"
52 #include "file.h"
53 #include "prefs.h"
54 #include "column_prefs.h"
55 #include "print.h"
56 #include "prefs_dlg.h"
57 #include "print_prefs.h"
58 #include "stream_prefs.h"
59 #include "gui_prefs.h"
60 #include "capture_prefs.h"
61 #include "nameres_prefs.h"
62 #include "ui_util.h"
63 #include "dlg_utils.h"
64 #include "simple_dialog.h"
65
66 #include "prefs-int.h"
67
68 static void     prefs_main_ok_cb(GtkWidget *, gpointer);
69 static void     prefs_main_apply_cb(GtkWidget *, gpointer);
70 static void     prefs_main_save_cb(GtkWidget *, gpointer);
71 static void     prefs_main_cancel_cb(GtkWidget *, gpointer);
72 static gboolean prefs_main_delete_cb(GtkWidget *, gpointer);
73 static void     prefs_main_destroy_cb(GtkWidget *, gpointer);
74 static void     prefs_tree_select_cb(GtkCTree *, GtkCTreeNode *, gint, gpointer);
75
76 #define E_PRINT_PAGE_KEY   "printer_options_page"
77 #define E_COLUMN_PAGE_KEY  "column_options_page"
78 #define E_STREAM_PAGE_KEY  "tcp_stream_options_page"
79 #define E_GUI_PAGE_KEY     "gui_options_page"
80 #define E_CAPTURE_PAGE_KEY "capture_options_page"
81 #define E_NAMERES_PAGE_KEY "nameres_options_page"
82
83 #define FIRST_PROTO_PREFS_PAGE  4
84
85 /* 
86  * Keep a static pointer to the notebook to be able to choose the 
87  * displayed page.
88  */
89 static GtkWidget *notebook;
90
91 /*
92  * Keep a static pointer to the current "Preferences" window, if any, so that
93  * if somebody tries to do "Edit:Preferences" while there's already a
94  * "Preferences" window up, we just pop up the existing one, rather than
95  * creating a new one.
96  */
97 static GtkWidget *prefs_w;
98
99 /*
100  * Save the value of the preferences as of when the preferences dialog
101  * box was first popped up, so we can revert to those values if the
102  * user selects "Cancel".
103  */
104 static e_prefs saved_prefs;
105
106 struct ct_struct {
107   GtkWidget    *notebook;
108   GtkWidget    *ctree;
109   GtkCTreeNode *node;
110   gint         page;
111 };
112
113 static void
114 pref_show(pref_t *pref, gpointer user_data)
115 {
116   GtkWidget *main_tb = user_data;
117   const char *title;
118   char *label_string;
119   char uint_str[10+1];
120
121   /* Give this preference a label which is its title, followed by a colon,
122      and left-align it. */
123   title = pref->title;
124   label_string = g_malloc(strlen(title) + 2);
125   strcpy(label_string, title);
126   strcat(label_string, ":");
127
128   /* Save the current value of the preference, so that we can revert it if
129      the user does "Apply" and then "Cancel", and create the control for
130      editing the preference. */
131   switch (pref->type) {
132
133   case PREF_UINT:
134     pref->saved_val.uint = *pref->varp.uint;
135
136     /* XXX - there are no uint spinbuttons, so we can't use a spinbutton.
137        Even more annoyingly, even if there were, GLib doesn't define
138        G_MAXUINT - but I think ANSI C may define UINT_MAX, so we could
139        use that. */
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     pref->control = create_preference_entry(main_tb, pref->ordinal,
155                                             label_string, uint_str);
156     break;
157
158   case PREF_BOOL:
159     pref->control = create_preference_check_button(main_tb, pref->ordinal,
160                                                label_string,
161                                                pref->saved_val.bool);
162     break;
163
164   case PREF_ENUM:
165     pref->saved_val.enumval = *pref->varp.enump;
166     if (pref->info.enum_info.radio_buttons) {
167       /* Show it as radio buttons. */
168       pref->control = create_preference_radio_buttons(main_tb, pref->ordinal,
169                                                   label_string,
170                                                   pref->info.enum_info.enumvals,
171                                                   pref->saved_val.enumval);
172     } else {
173       /* Show it as an option menu. */
174       pref->control = create_preference_option_menu(main_tb, pref->ordinal,
175                                          label_string,
176                                          pref->info.enum_info.enumvals,
177                                          pref->saved_val.enumval);
178     }
179     break;
180
181   case PREF_STRING:
182     if (pref->saved_val.string != NULL)
183       g_free(pref->saved_val.string);
184     pref->saved_val.string = g_strdup(*pref->varp.string);
185     pref->control = create_preference_entry(main_tb, pref->ordinal,
186                                             label_string,
187                                             pref->saved_val.string);
188     break;
189
190   case PREF_OBSOLETE:
191     g_assert_not_reached();
192     break;
193   }
194   g_free(label_string);
195 }
196
197 #define MAX_TREE_NODE_NAME_LEN 64
198 static void
199 module_prefs_show(module_t *module, gpointer user_data)
200 {
201   struct ct_struct *cts = user_data;
202   GtkWidget        *main_vb, *main_tb, *frame;
203   gchar            label_str[MAX_TREE_NODE_NAME_LEN], *label_ptr = label_str;
204   GtkCTreeNode     *ct_node;
205
206   /* Frame */
207   frame = gtk_frame_new(module->title);
208   gtk_widget_show(frame);
209
210   /* Main vertical box */
211   main_vb = gtk_vbox_new(FALSE, 5);
212   gtk_container_border_width(GTK_CONTAINER(main_vb), 5);
213   gtk_container_add(GTK_CONTAINER(frame), main_vb);
214
215   /* Main table */
216   main_tb = gtk_table_new(module->numprefs, 2, FALSE);
217   gtk_box_pack_start(GTK_BOX(main_vb), main_tb, FALSE, FALSE, 0);
218   gtk_table_set_row_spacings(GTK_TABLE(main_tb), 10);
219   gtk_table_set_col_spacings(GTK_TABLE(main_tb), 15);
220
221   /* Add items for each of the preferences */
222   prefs_pref_foreach(module, pref_show, main_tb);
223
224   gtk_notebook_append_page(GTK_NOTEBOOK(cts->notebook), frame, NULL);
225   strcpy(label_str, module->title);
226   ct_node = gtk_ctree_insert_node(GTK_CTREE(cts->ctree), cts->node, NULL, 
227                 &label_ptr, 5, NULL, NULL, NULL, NULL, TRUE, TRUE);
228   gtk_ctree_node_set_row_data(GTK_CTREE(cts->ctree), ct_node,
229                 GINT_TO_POINTER(cts->page));
230   cts->page++;
231
232   /* Show 'em what we got */
233   gtk_widget_show_all(main_vb);
234 }
235
236 void
237 prefs_cb(GtkWidget *w, gpointer dummy) {
238   GtkWidget        *main_vb, *top_hb, *bbox, *prefs_nb, *ct_sb, *frame,
239                    *ok_bt, *apply_bt, *save_bt, *cancel_bt;
240   GtkWidget        *print_pg, *column_pg, *stream_pg, *gui_pg, *capture_pg;
241   GtkWidget        *nameres_pg;
242   gchar            label_str[MAX_TREE_NODE_NAME_LEN], *label_ptr = label_str;
243   GtkCTreeNode     *ct_node;
244   struct ct_struct cts;
245
246
247   if (prefs_w != NULL) {
248     /* There's already a "Preferences" dialog box; reactivate it. */
249     reactivate_window(prefs_w);
250     return;
251   }
252
253   /* Save the current preferences, so we can revert to those values
254      if the user presses "Cancel". */
255   copy_prefs(&saved_prefs, &prefs);
256
257   prefs_w = dlg_window_new("Ethereal: Preferences");
258   gtk_signal_connect(GTK_OBJECT(prefs_w), "delete_event",
259     GTK_SIGNAL_FUNC(prefs_main_delete_cb), NULL);
260   gtk_signal_connect(GTK_OBJECT(prefs_w), "destroy",
261         GTK_SIGNAL_FUNC(prefs_main_destroy_cb), NULL);
262   
263   /* Container for each row of widgets */
264   main_vb = gtk_vbox_new(FALSE, 5);
265   gtk_container_border_width(GTK_CONTAINER(main_vb), 5);
266   gtk_container_add(GTK_CONTAINER(prefs_w), main_vb);
267   gtk_widget_show(main_vb);
268   
269   /* Top row: Preferences tree and notebook */
270   top_hb = gtk_hbox_new(FALSE, 10);
271   gtk_container_add(GTK_CONTAINER(main_vb), top_hb);
272   gtk_widget_show(top_hb);
273
274   /* Place a Ctree on the left for preference categories */
275   ct_sb = scrolled_window_new(NULL, NULL);
276   gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(ct_sb),
277         GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
278   gtk_container_add(GTK_CONTAINER(top_hb), ct_sb);
279   gtk_widget_show(ct_sb);
280
281   cts.ctree = ctree_new(1, 0);
282   cts.page  = 0;
283   gtk_container_add(GTK_CONTAINER(ct_sb), cts.ctree);
284
285   gtk_clist_set_column_auto_resize(GTK_CLIST(cts.ctree), 0, TRUE);
286   gtk_signal_connect(GTK_OBJECT(cts.ctree), "tree-select-row",
287     GTK_SIGNAL_FUNC(prefs_tree_select_cb), NULL);
288   gtk_widget_show(cts.ctree);
289   
290   /* A notebook widget sans tabs is used to flip between prefs */
291   notebook = prefs_nb = gtk_notebook_new();
292   gtk_notebook_set_show_tabs(GTK_NOTEBOOK(notebook), FALSE);
293   gtk_notebook_set_show_border(GTK_NOTEBOOK(notebook), FALSE);
294   gtk_container_add(GTK_CONTAINER(top_hb), prefs_nb);
295   gtk_widget_show(prefs_nb);
296   
297   /* Printing prefs */
298   frame = gtk_frame_new("Printing");
299   gtk_widget_show(GTK_WIDGET(frame));
300   print_pg = printer_prefs_show();
301   gtk_container_add(GTK_CONTAINER(frame), print_pg);
302   gtk_object_set_data(GTK_OBJECT(prefs_w), E_PRINT_PAGE_KEY, print_pg);
303   gtk_notebook_append_page (GTK_NOTEBOOK(prefs_nb), frame, NULL);
304   strcpy(label_str, "Printing");
305   ct_node = gtk_ctree_insert_node(GTK_CTREE(cts.ctree), NULL, NULL, 
306                 &label_ptr, 5, NULL, NULL, NULL, NULL, TRUE, TRUE);
307   gtk_ctree_node_set_row_data(GTK_CTREE(cts.ctree), ct_node,
308                 GINT_TO_POINTER(cts.page));
309   cts.page++;
310     
311   /* Column prefs */
312   frame = gtk_frame_new("Columns");
313   gtk_widget_show(GTK_WIDGET(frame));
314   column_pg = column_prefs_show();
315   gtk_container_add(GTK_CONTAINER(frame), column_pg);
316   gtk_object_set_data(GTK_OBJECT(prefs_w), E_COLUMN_PAGE_KEY, column_pg);
317   gtk_notebook_append_page (GTK_NOTEBOOK(prefs_nb), frame, NULL);
318   strcpy(label_str, "Columns");
319   ct_node = gtk_ctree_insert_node(GTK_CTREE(cts.ctree), NULL, NULL, 
320                 &label_ptr, 5, NULL, NULL, NULL, NULL, TRUE, TRUE);
321   gtk_ctree_node_set_row_data(GTK_CTREE(cts.ctree), ct_node,
322                 GINT_TO_POINTER(cts.page));
323   cts.page++;
324   
325   /* TCP Streams prefs */
326   frame = gtk_frame_new("TCP Streams");
327   gtk_widget_show(GTK_WIDGET(frame));
328   stream_pg = stream_prefs_show();
329   gtk_container_add(GTK_CONTAINER(frame), stream_pg);
330   gtk_object_set_data(GTK_OBJECT(prefs_w), E_STREAM_PAGE_KEY, stream_pg);
331   gtk_notebook_append_page (GTK_NOTEBOOK(prefs_nb), frame, NULL);
332   strcpy(label_str, "TCP Streams");
333   ct_node = gtk_ctree_insert_node(GTK_CTREE(cts.ctree), NULL, NULL, 
334                 &label_ptr, 5, NULL, NULL, NULL, NULL, TRUE, TRUE);
335   gtk_ctree_node_set_row_data(GTK_CTREE(cts.ctree), ct_node,
336                 GINT_TO_POINTER(cts.page));
337   cts.page++;
338
339   /* GUI prefs */
340   frame = gtk_frame_new("User Interface");
341   gtk_widget_show(GTK_WIDGET(frame));
342   gui_pg = gui_prefs_show();
343   gtk_container_add(GTK_CONTAINER(frame), gui_pg);
344   gtk_object_set_data(GTK_OBJECT(prefs_w), E_GUI_PAGE_KEY, gui_pg);
345   gtk_notebook_append_page (GTK_NOTEBOOK(prefs_nb), frame, NULL);
346   strcpy(label_str, "User Interface");
347   ct_node = gtk_ctree_insert_node(GTK_CTREE(cts.ctree), NULL, NULL, 
348                 &label_ptr, 5, NULL, NULL, NULL, NULL, TRUE, TRUE);
349   gtk_ctree_node_set_row_data(GTK_CTREE(cts.ctree), ct_node,
350                 GINT_TO_POINTER(cts.page));
351   cts.page++;
352
353 #ifdef HAVE_LIBPCAP
354   /* capture prefs */
355   frame = gtk_frame_new("Capture");
356   gtk_widget_show(GTK_WIDGET(frame));
357   capture_pg = capture_prefs_show();
358   gtk_container_add(GTK_CONTAINER(frame), capture_pg);
359   gtk_object_set_data(GTK_OBJECT(prefs_w), E_CAPTURE_PAGE_KEY, capture_pg);
360   gtk_notebook_append_page (GTK_NOTEBOOK(prefs_nb), frame, NULL);
361   strcpy(label_str, "Capture");
362   ct_node = gtk_ctree_insert_node(GTK_CTREE(cts.ctree), NULL, NULL, 
363                 &label_ptr, 5, NULL, NULL, NULL, NULL, TRUE, TRUE);
364   gtk_ctree_node_set_row_data(GTK_CTREE(cts.ctree), ct_node,
365                 GINT_TO_POINTER(cts.page));
366   cts.page++;
367 #endif
368
369   /* Name resolution prefs */
370   frame = gtk_frame_new("Name resolution");
371   gtk_widget_show(GTK_WIDGET(frame));
372   nameres_pg = nameres_prefs_show();
373   gtk_container_add(GTK_CONTAINER(frame), nameres_pg);
374   gtk_object_set_data(GTK_OBJECT(prefs_w), E_NAMERES_PAGE_KEY, nameres_pg);
375   gtk_notebook_append_page (GTK_NOTEBOOK(prefs_nb), frame, NULL);
376   strcpy(label_str, "Name resolution");
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   /* Registered prefs */
384   cts.notebook = prefs_nb;
385   strcpy(label_str, "Protocols");
386   cts.node = gtk_ctree_insert_node(GTK_CTREE(cts.ctree), NULL, NULL,
387                 &label_ptr, 5, NULL, NULL, NULL, NULL, FALSE, FALSE);
388   gtk_ctree_node_set_row_data(GTK_CTREE(cts.ctree), cts.node,
389                 GINT_TO_POINTER(-1));
390   gtk_ctree_node_set_selectable(GTK_CTREE(cts.ctree), cts.node, FALSE);
391
392   prefs_module_foreach(module_prefs_show, &cts);
393
394
395   /* Button row: OK and cancel buttons */
396   bbox = gtk_hbutton_box_new();
397   gtk_button_box_set_layout (GTK_BUTTON_BOX (bbox), GTK_BUTTONBOX_END);
398   gtk_button_box_set_spacing(GTK_BUTTON_BOX(bbox), 5);
399   gtk_container_add(GTK_CONTAINER(main_vb), bbox);
400   gtk_widget_show(bbox);
401   
402   ok_bt = gtk_button_new_with_label ("OK");
403   gtk_signal_connect(GTK_OBJECT(ok_bt), "clicked",
404     GTK_SIGNAL_FUNC(prefs_main_ok_cb), GTK_OBJECT(prefs_w));
405   GTK_WIDGET_SET_FLAGS(ok_bt, GTK_CAN_DEFAULT);
406   gtk_box_pack_start (GTK_BOX (bbox), ok_bt, TRUE, TRUE, 0);
407   gtk_widget_grab_default(ok_bt);
408   gtk_widget_show(ok_bt);
409
410   apply_bt = gtk_button_new_with_label ("Apply");
411   gtk_signal_connect(GTK_OBJECT(apply_bt), "clicked",
412     GTK_SIGNAL_FUNC(prefs_main_apply_cb), GTK_OBJECT(prefs_w));
413   GTK_WIDGET_SET_FLAGS(apply_bt, GTK_CAN_DEFAULT);
414   gtk_box_pack_start(GTK_BOX (bbox), apply_bt, TRUE, TRUE, 0);
415   gtk_widget_show(apply_bt);
416
417   save_bt = gtk_button_new_with_label ("Save");
418   gtk_signal_connect(GTK_OBJECT(save_bt), "clicked",
419     GTK_SIGNAL_FUNC(prefs_main_save_cb), GTK_OBJECT(prefs_w));
420   GTK_WIDGET_SET_FLAGS(save_bt, GTK_CAN_DEFAULT);
421   gtk_box_pack_start (GTK_BOX (bbox), save_bt, TRUE, TRUE, 0);
422   gtk_widget_show(save_bt);
423   
424   cancel_bt = gtk_button_new_with_label ("Cancel");
425   gtk_signal_connect(GTK_OBJECT(cancel_bt), "clicked",
426     GTK_SIGNAL_FUNC(prefs_main_cancel_cb), GTK_OBJECT(prefs_w));
427   GTK_WIDGET_SET_FLAGS(cancel_bt, GTK_CAN_DEFAULT);
428   gtk_box_pack_start (GTK_BOX (bbox), cancel_bt, TRUE, TRUE, 0);
429   gtk_widget_show(cancel_bt);
430
431   /* Catch the "key_press_event" signal in the window, so that we can catch
432      the ESC key being pressed and act as if the "Cancel" button had
433      been selected. */
434   dlg_set_cancel(prefs_w, cancel_bt);
435
436   gtk_widget_show(prefs_w);
437 }
438
439 static void
440 set_option_label(GtkWidget *main_tb, int table_position,
441     const gchar *label_text)
442 {
443         GtkWidget *label;
444
445         label = gtk_label_new(label_text);
446         gtk_misc_set_alignment(GTK_MISC(label), 1.0, 0.5);
447         gtk_table_attach_defaults(GTK_TABLE(main_tb), label, 0, 1,
448             table_position, table_position + 1);
449         gtk_widget_show(label);
450 }
451
452 GtkWidget *
453 create_preference_check_button(GtkWidget *main_tb, int table_position,
454     const gchar *label_text, gboolean active)
455 {
456         GtkWidget *check_box;
457
458         set_option_label(main_tb, table_position, label_text);
459
460         check_box = gtk_check_button_new();
461         gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check_box), active);
462         gtk_table_attach_defaults(GTK_TABLE(main_tb), check_box, 1, 2,
463             table_position, table_position + 1);
464
465         return check_box;
466 }
467
468 GtkWidget *
469 create_preference_radio_buttons(GtkWidget *main_tb, int table_position,
470     const gchar *label_text, const enum_val_t *enumvals, gint current_val)
471 {
472         GtkWidget *radio_button_hbox, *button = NULL;
473         GSList *rb_group;
474         int index;
475         const enum_val_t *enum_valp;
476
477         set_option_label(main_tb, table_position, label_text);
478
479         radio_button_hbox = gtk_hbox_new(FALSE, 0);
480         rb_group = NULL;
481         for (enum_valp = enumvals, index = 0; enum_valp->name != NULL;
482             enum_valp++, index++) {
483                 button = gtk_radio_button_new_with_label(rb_group,
484                     enum_valp->name);
485                 gtk_widget_show(button);
486                 if (rb_group == NULL)
487                         rb_group = gtk_radio_button_group(GTK_RADIO_BUTTON(button));
488                 gtk_box_pack_start(GTK_BOX(radio_button_hbox), button, FALSE,
489                     FALSE, 10);
490                 if (enum_valp->value == current_val) {
491                         gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(button),
492                             TRUE);
493                 }
494         }
495         gtk_widget_show(radio_button_hbox);
496         gtk_table_attach_defaults(GTK_TABLE(main_tb), radio_button_hbox, 1, 2,
497             table_position, table_position+1);
498
499         /*
500          * It doesn't matter which of the buttons we return - we fetch
501          * the value by looking at the entire radio button group to
502          * which it belongs, and we can get that from any button.
503          */
504         return button;
505 }
506
507 static gint
508 label_to_enum_val(GtkWidget *label, const enum_val_t *enumvals)
509 {
510         char *label_string;
511         gint enumval;
512
513         /* Get the label's text, and translate it to a value. */
514         gtk_label_get(GTK_LABEL(label), &label_string);
515         enumval = find_val_for_string(label_string, enumvals, 1);
516
517         return enumval;
518 }
519
520 gint
521 fetch_preference_radio_buttons_val(GtkWidget *button,
522     const enum_val_t *enumvals)
523 {
524         GSList *rb_group;
525         GSList *rb_entry;
526
527         /*
528          * Go through the list of of radio buttons in the button's group,
529          * and find the first one that's active.
530          */
531         rb_group = gtk_radio_button_group(GTK_RADIO_BUTTON(button));
532         button = NULL;
533         for (rb_entry = rb_group; rb_entry != NULL;
534             rb_entry = g_slist_next(rb_entry)) {
535                 button = rb_entry->data;
536                 if (GTK_TOGGLE_BUTTON(button)->active)
537                         break;
538         }
539
540         /* OK, now return the value corresponding to that button's label. */
541         return label_to_enum_val(GTK_BIN(button)->child, enumvals);
542 }
543
544 GtkWidget *
545 create_preference_option_menu(GtkWidget *main_tb, int table_position,
546     const gchar *label_text, const enum_val_t *enumvals, gint current_val)
547 {
548         GtkWidget *label, *menu_box, *menu, *menu_item, *option_menu;
549         int menu_index, index;
550         const enum_val_t *enum_valp;
551
552         set_option_label(main_tb, table_position, label_text);
553
554         /* Create a menu from the enumvals */
555         menu = gtk_menu_new();
556         menu_index = -1;
557         for (enum_valp = enumvals, index = 0; enum_valp->name != NULL;
558             enum_valp++, index++) {
559                 menu_item = gtk_menu_item_new_with_label(enum_valp->name);
560                 gtk_menu_append(GTK_MENU(menu), menu_item);
561                 if (enum_valp->value == current_val)
562                         menu_index = index;
563                 gtk_widget_show(menu_item);
564         }
565
566         /* Create the option menu from the menu */
567         option_menu = gtk_option_menu_new();
568         gtk_option_menu_set_menu(GTK_OPTION_MENU(option_menu), menu);
569
570         /* Set its current value to the variable's current value */
571         if (menu_index != -1)
572                 gtk_option_menu_set_history(GTK_OPTION_MENU(option_menu),
573                     menu_index);
574
575         /*
576          * Put the option menu in an hbox, so that it's only as wide
577          * as the widest entry, rather than being as wide as the table
578          * space.
579          */
580         menu_box = gtk_hbox_new(FALSE, 0);
581         gtk_table_attach_defaults(GTK_TABLE(main_tb), menu_box,
582             1, 2, table_position, table_position + 1);
583         gtk_box_pack_start(GTK_BOX(menu_box), option_menu, FALSE, FALSE, 0);
584         return option_menu;
585 }
586
587 gint
588 fetch_preference_option_menu_val(GtkWidget *optmenu, const enum_val_t *enumvals)
589 {
590         /*
591          * OK, now return the value corresponding to the label for the
592          * currently active entry in the option menu.
593          *
594          * Yes, this is how you get the label for that entry.  See FAQ
595          * 6.8 in the GTK+ FAQ.
596          */
597         return label_to_enum_val(GTK_BIN(optmenu)->child, enumvals);
598 }
599
600 GtkWidget *
601 create_preference_entry(GtkWidget *main_tb, int table_position,
602     const gchar *label_text, char *value)
603 {
604         GtkWidget *entry;
605
606         set_option_label(main_tb, table_position, label_text);
607
608         entry = gtk_entry_new();
609         if (value != NULL)
610                 gtk_entry_set_text(GTK_ENTRY(entry), value);
611         gtk_table_attach_defaults(GTK_TABLE(main_tb), entry, 1, 2,
612             table_position, table_position + 1);
613         gtk_widget_show(entry);
614
615         return entry;
616 }
617
618 static void
619 pref_fetch(pref_t *pref, gpointer user_data)
620 {
621   GtkWidget *label;
622   char *label_string;
623   char *str_val;
624   char *p;
625   guint uval;
626   gboolean bval;
627   GSList *rb_entry;
628   GtkWidget *button;
629   gint enumval;
630   gboolean *pref_changed_p = user_data;
631
632   /* Fetch the value of the preference, and set the appropriate variable
633      to it. */
634   switch (pref->type) {
635
636   case PREF_UINT:
637     str_val = gtk_entry_get_text(GTK_ENTRY(pref->control));
638     uval = strtoul(str_val, &p, pref->info.base);
639 #if 0
640     if (p == value || *p != '\0')
641       return PREFS_SET_SYNTAX_ERR;      /* number was bad */
642 #endif
643     if (*pref->varp.uint != uval) {
644       *pref_changed_p = TRUE;
645       *pref->varp.uint = uval;
646     }
647     break;
648
649   case PREF_BOOL:
650     bval = GTK_TOGGLE_BUTTON(pref->control)->active;
651     if (*pref->varp.bool != bval) {
652       *pref_changed_p = TRUE;
653       *pref->varp.bool = bval;
654     }
655     break;
656
657   case PREF_ENUM:
658     if (pref->info.enum_info.radio_buttons) {
659       enumval = fetch_preference_radio_buttons_val(pref->control,
660           pref->info.enum_info.enumvals);
661     } else {
662       enumval = fetch_preference_option_menu_val(pref->control,
663           pref->info.enum_info.enumvals);
664     }
665
666     if (*pref->varp.enump != enumval) {
667       *pref_changed_p = TRUE;
668       *pref->varp.enump = enumval;
669     }
670     break;
671
672   case PREF_STRING:
673     str_val = gtk_entry_get_text(GTK_ENTRY(pref->control));
674     if (*pref->varp.string == NULL || strcmp(*pref->varp.string, str_val) != 0) {
675       *pref_changed_p = TRUE;
676       if (*pref->varp.string != NULL)
677         g_free(*pref->varp.string);
678       *pref->varp.string = g_strdup(str_val);
679     }
680     break;
681
682   case PREF_OBSOLETE:
683     g_assert_not_reached();
684     break;
685   }
686 }
687
688 static void
689 module_prefs_fetch(module_t *module, gpointer user_data)
690 {
691   gboolean *must_redissect_p = user_data;
692
693   /* For all preferences in this module, fetch its value from this
694      module's notebook page.  Find out whether any of them changed. */
695   module->prefs_changed = FALSE;        /* assume none of them changed */
696   prefs_pref_foreach(module, pref_fetch, &module->prefs_changed);
697
698   /* If any of them changed, indicate that we must redissect and refilter
699      the current capture (if we have one), as the preference change
700      could cause packets to be dissected differently. */
701   if (module->prefs_changed)
702     *must_redissect_p = TRUE;
703 }
704
705 static void
706 pref_clean(pref_t *pref, gpointer user_data)
707 {
708   switch (pref->type) {
709
710   case PREF_UINT:
711     break;
712
713   case PREF_BOOL:
714     break;
715
716   case PREF_ENUM:
717     break;
718
719   case PREF_STRING:
720     if (pref->saved_val.string != NULL) {
721       g_free(pref->saved_val.string);
722       pref->saved_val.string = NULL;
723     }
724     break;
725
726   case PREF_OBSOLETE:
727     g_assert_not_reached();
728     break;
729   }
730 }
731
732 static void
733 module_prefs_clean(module_t *module, gpointer user_data)
734 {
735   /* For all preferences in this module, clean up any cruft allocated for
736      use by the GUI code. */
737   prefs_pref_foreach(module, pref_clean, NULL);
738 }
739
740 static void
741 prefs_main_ok_cb(GtkWidget *ok_bt, gpointer parent_w)
742 {
743   gboolean must_redissect = FALSE;
744
745   /* Fetch the preferences (i.e., make sure all the values set in all of
746      the preferences panes have been copied to "prefs" and the registered
747      preferences). */
748   printer_prefs_fetch(gtk_object_get_data(GTK_OBJECT(parent_w), E_PRINT_PAGE_KEY));
749   column_prefs_fetch(gtk_object_get_data(GTK_OBJECT(parent_w), E_COLUMN_PAGE_KEY));
750   stream_prefs_fetch(gtk_object_get_data(GTK_OBJECT(parent_w), E_STREAM_PAGE_KEY));
751   gui_prefs_fetch(gtk_object_get_data(GTK_OBJECT(parent_w), E_GUI_PAGE_KEY));
752   capture_prefs_fetch(gtk_object_get_data(GTK_OBJECT(parent_w), E_CAPTURE_PAGE_KEY));
753   nameres_prefs_fetch(gtk_object_get_data(GTK_OBJECT(parent_w), E_NAMERES_PAGE_KEY));
754   prefs_module_foreach(module_prefs_fetch, &must_redissect);
755
756   /* Now apply those preferences. */
757   printer_prefs_apply(gtk_object_get_data(GTK_OBJECT(parent_w), E_PRINT_PAGE_KEY));
758   column_prefs_apply(gtk_object_get_data(GTK_OBJECT(parent_w), E_COLUMN_PAGE_KEY));
759   stream_prefs_apply(gtk_object_get_data(GTK_OBJECT(parent_w), E_STREAM_PAGE_KEY));
760   gui_prefs_apply(gtk_object_get_data(GTK_OBJECT(parent_w), E_GUI_PAGE_KEY));
761   capture_prefs_apply(gtk_object_get_data(GTK_OBJECT(parent_w), E_CAPTURE_PAGE_KEY));
762   nameres_prefs_apply(gtk_object_get_data(GTK_OBJECT(parent_w), E_NAMERES_PAGE_KEY));
763   prefs_apply_all();
764
765   /* Now destroy the "Preferences" dialog. */
766   gtk_widget_destroy(GTK_WIDGET(parent_w));
767
768   if (must_redissect) {
769     /* Redissect all the packets, and re-evaluate the display filter. */
770     redissect_packets(&cfile);
771   }
772 }
773
774 static void
775 prefs_main_apply_cb(GtkWidget *apply_bt, gpointer parent_w)
776 {
777   gboolean must_redissect = FALSE;
778
779   /* Fetch the preferences (i.e., make sure all the values set in all of
780      the preferences panes have been copied to "prefs" and the registered
781      preferences). */
782   printer_prefs_fetch(gtk_object_get_data(GTK_OBJECT(parent_w), E_PRINT_PAGE_KEY));
783   column_prefs_fetch(gtk_object_get_data(GTK_OBJECT(parent_w), E_COLUMN_PAGE_KEY));
784   stream_prefs_fetch(gtk_object_get_data(GTK_OBJECT(parent_w), E_STREAM_PAGE_KEY));
785   gui_prefs_fetch(gtk_object_get_data(GTK_OBJECT(parent_w), E_GUI_PAGE_KEY));
786   capture_prefs_fetch(gtk_object_get_data(GTK_OBJECT(parent_w), E_CAPTURE_PAGE_KEY));
787   nameres_prefs_fetch(gtk_object_get_data(GTK_OBJECT(parent_w), E_NAMERES_PAGE_KEY));
788   prefs_module_foreach(module_prefs_fetch, &must_redissect);
789
790   /* Now apply those preferences. */
791   printer_prefs_apply(gtk_object_get_data(GTK_OBJECT(parent_w), E_PRINT_PAGE_KEY));
792   column_prefs_apply(gtk_object_get_data(GTK_OBJECT(parent_w), E_COLUMN_PAGE_KEY));
793   stream_prefs_apply(gtk_object_get_data(GTK_OBJECT(parent_w), E_STREAM_PAGE_KEY));
794   gui_prefs_apply(gtk_object_get_data(GTK_OBJECT(parent_w), E_GUI_PAGE_KEY));
795   capture_prefs_apply(gtk_object_get_data(GTK_OBJECT(parent_w), E_CAPTURE_PAGE_KEY));
796   nameres_prefs_apply(gtk_object_get_data(GTK_OBJECT(parent_w), E_NAMERES_PAGE_KEY));
797   prefs_apply_all();
798
799   if (must_redissect) {
800     /* Redissect all the packets, and re-evaluate the display filter. */
801     redissect_packets(&cfile);
802   }
803 }
804
805 static void
806 prefs_main_save_cb(GtkWidget *save_bt, gpointer parent_w)
807 {
808   gboolean must_redissect = FALSE;
809   int err;
810   char *pf_dir_path;
811   const char *pf_path;
812
813   /* Fetch the preferences (i.e., make sure all the values set in all of
814      the preferences panes have been copied to "prefs" and the registered
815      preferences). */
816   printer_prefs_fetch(gtk_object_get_data(GTK_OBJECT(parent_w), E_PRINT_PAGE_KEY));
817   column_prefs_fetch(gtk_object_get_data(GTK_OBJECT(parent_w), E_COLUMN_PAGE_KEY));
818   stream_prefs_fetch(gtk_object_get_data(GTK_OBJECT(parent_w), E_STREAM_PAGE_KEY));
819   gui_prefs_fetch(gtk_object_get_data(GTK_OBJECT(parent_w), E_GUI_PAGE_KEY));
820   capture_prefs_fetch(gtk_object_get_data(GTK_OBJECT(parent_w), E_CAPTURE_PAGE_KEY));
821   nameres_prefs_fetch(gtk_object_get_data(GTK_OBJECT(parent_w), E_NAMERES_PAGE_KEY));
822   prefs_module_foreach(module_prefs_fetch, &must_redissect);
823
824   /* Create the directory that holds personal configuration files, if
825      necessary.  */
826   if (create_persconffile_dir(&pf_dir_path) == -1) {
827      simple_dialog(ESD_TYPE_WARN, NULL,
828       "Can't create directory\n\"%s\"\nfor preferences file: %s.", pf_dir_path,
829       strerror(errno));
830      g_free(pf_dir_path);
831   } else {
832     /* Write the preferencs out. */
833     err = write_prefs(&pf_path);
834     if (err != 0) {
835        simple_dialog(ESD_TYPE_WARN, NULL,
836         "Can't open preferences file\n\"%s\": %s.", pf_path,
837         strerror(err));
838     }
839   }
840
841   /* Now apply those preferences.
842      XXX - should we do this?  The user didn't click "OK" or "Apply".
843      However:
844
845         1) by saving the preferences they presumably indicate that they
846            like them;
847
848         2) the next time they fire Ethereal up, those preferences will
849            apply;
850
851         3) we'd have to buffer "must_redissect" so that if they do
852            "Apply" after this, we know we have to redissect;
853
854         4) we did apply the protocol preferences, at least, in the past. */
855   printer_prefs_apply(gtk_object_get_data(GTK_OBJECT(parent_w), E_PRINT_PAGE_KEY));
856   column_prefs_apply(gtk_object_get_data(GTK_OBJECT(parent_w), E_COLUMN_PAGE_KEY));
857   stream_prefs_apply(gtk_object_get_data(GTK_OBJECT(parent_w), E_STREAM_PAGE_KEY));
858   gui_prefs_apply(gtk_object_get_data(GTK_OBJECT(parent_w), E_GUI_PAGE_KEY));
859   capture_prefs_apply(gtk_object_get_data(GTK_OBJECT(parent_w), E_CAPTURE_PAGE_KEY));
860   nameres_prefs_apply(gtk_object_get_data(GTK_OBJECT(parent_w), E_NAMERES_PAGE_KEY));
861   prefs_apply_all();
862
863   if (must_redissect) {
864     /* Redissect all the packets, and re-evaluate the display filter. */
865     redissect_packets(&cfile);
866   }
867 }
868
869 static void
870 pref_revert(pref_t *pref, gpointer user_data)
871 {
872   gboolean *pref_changed_p = user_data;
873
874   /* Revert the preference to its saved value. */
875   switch (pref->type) {
876
877   case PREF_UINT:
878     if (*pref->varp.uint != pref->saved_val.uint) {
879       *pref_changed_p = TRUE;
880       *pref->varp.uint = pref->saved_val.uint;
881     }
882     break;
883
884   case PREF_BOOL:
885     if (*pref->varp.bool != pref->saved_val.bool) {
886       *pref_changed_p = TRUE;
887       *pref->varp.bool = pref->saved_val.bool;
888     }
889     break;
890
891   case PREF_ENUM:
892     if (*pref->varp.enump != pref->saved_val.enumval) {
893       *pref_changed_p = TRUE;
894       *pref->varp.enump = pref->saved_val.enumval;
895     }
896     break;
897
898   case PREF_STRING:
899     if (*pref->varp.string != pref->saved_val.string &&
900         (*pref->varp.string == NULL ||
901          pref->saved_val.string == NULL ||
902          strcmp(*pref->varp.string, pref->saved_val.string) != 0)) {
903       *pref_changed_p = TRUE;
904       if (*pref->varp.string != NULL)
905         g_free(*pref->varp.string);
906       *pref->varp.string = g_strdup(pref->saved_val.string);
907     }
908     break;
909
910   case PREF_OBSOLETE:
911     g_assert_not_reached();
912     break;
913   }
914 }
915
916 static void
917 module_prefs_revert(module_t *module, gpointer user_data)
918 {
919   gboolean *must_redissect_p = user_data;
920
921   /* For all preferences in this module, revert its value to the value
922      it had when we popped up the Preferences dialog.  Find out whether
923      this changes any of them. */
924   module->prefs_changed = FALSE;        /* assume none of them changed */
925   prefs_pref_foreach(module, pref_revert, &module->prefs_changed);
926
927   /* If any of them changed, indicate that we must redissect and refilter
928      the current capture (if we have one), as the preference change
929      could cause packets to be dissected differently. */
930   if (module->prefs_changed)
931     *must_redissect_p = TRUE;
932 }
933
934 static void
935 prefs_main_cancel_cb(GtkWidget *cancel_bt, gpointer parent_w)
936 {
937   gboolean must_redissect = FALSE;
938
939   /* Free up the current preferences and copy the saved preferences to the
940      current preferences. */
941   free_prefs(&prefs);
942   copy_prefs(&prefs, &saved_prefs);
943
944   /* Now revert the registered preferences. */
945   prefs_module_foreach(module_prefs_revert, &must_redissect);
946
947   /* Now apply the reverted-to preferences. */
948   printer_prefs_apply(gtk_object_get_data(GTK_OBJECT(parent_w), E_PRINT_PAGE_KEY));
949   column_prefs_apply(gtk_object_get_data(GTK_OBJECT(parent_w), E_COLUMN_PAGE_KEY));
950   stream_prefs_apply(gtk_object_get_data(GTK_OBJECT(parent_w), E_STREAM_PAGE_KEY));
951   gui_prefs_apply(gtk_object_get_data(GTK_OBJECT(parent_w), E_GUI_PAGE_KEY));
952   nameres_prefs_apply(gtk_object_get_data(GTK_OBJECT(parent_w), E_NAMERES_PAGE_KEY));
953   prefs_apply_all();
954
955   gtk_widget_destroy(GTK_WIDGET(parent_w));
956
957   if (must_redissect) {
958     /* Redissect all the packets, and re-evaluate the display filter. */
959     redissect_packets(&cfile);
960   }
961 }
962
963 /* Treat this as a cancel, by calling "prefs_main_cancel_cb()".
964    XXX - that'll destroy the Preferences dialog; will that upset
965    a higher-level handler that says "OK, we've been asked to delete
966    this, so destroy it"? */
967 static gboolean
968 prefs_main_delete_cb(GtkWidget *prefs_w, gpointer dummy)
969 {
970   prefs_main_cancel_cb(NULL, prefs_w);
971   return FALSE;
972 }
973
974 static void
975 prefs_main_destroy_cb(GtkWidget *win, gpointer user_data)
976 {
977   /* Let the preference tabs clean up anything they've done. */
978   printer_prefs_destroy(gtk_object_get_data(GTK_OBJECT(prefs_w), E_PRINT_PAGE_KEY));
979   column_prefs_destroy(gtk_object_get_data(GTK_OBJECT(prefs_w), E_COLUMN_PAGE_KEY));
980   stream_prefs_destroy(gtk_object_get_data(GTK_OBJECT(prefs_w), E_STREAM_PAGE_KEY));
981   gui_prefs_destroy(gtk_object_get_data(GTK_OBJECT(prefs_w), E_GUI_PAGE_KEY));
982   capture_prefs_destroy(gtk_object_get_data(GTK_OBJECT(prefs_w), E_CAPTURE_PAGE_KEY));
983   nameres_prefs_destroy(gtk_object_get_data(GTK_OBJECT(prefs_w), E_NAMERES_PAGE_KEY));
984
985   /* Free up the saved preferences (both for "prefs" and for registered
986      preferences). */
987   free_prefs(&saved_prefs);
988   prefs_module_foreach(module_prefs_clean, NULL);
989
990   /* Note that we no longer have a "Preferences" dialog box. */
991   prefs_w = NULL;
992 }
993
994 struct properties_data {
995   GtkWidget *w;
996   int page_num;
997   char *title;
998 };
999
1000 /* XXX this way of searching the correct page number is really ugly ... */
1001 static void
1002 module_search_properties(module_t *module, gpointer user_data)
1003 {
1004   struct properties_data *p = (struct properties_data *)user_data;
1005
1006   if (p->title == NULL) return;
1007   if (strcmp(module->title, p->title) == 0) {
1008     /* found it */
1009     gtk_notebook_set_page(GTK_NOTEBOOK(p->w), p->page_num);
1010     p->title = NULL;
1011   } else {
1012     p->page_num++;
1013   }
1014 }
1015
1016 void
1017 properties_cb(GtkWidget *w, gpointer dummy) 
1018 {
1019   gchar *title = NULL;
1020   struct properties_data p;
1021
1022   if (finfo_selected) {
1023     header_field_info *hfinfo = finfo_selected->hfinfo;
1024     if (hfinfo->parent == -1) {
1025       title = (gchar *)prefs_get_title_by_name(hfinfo->abbrev);
1026     } else {
1027       title = (gchar *)
1028         prefs_get_title_by_name(proto_registrar_get_abbrev(hfinfo->parent));
1029     }
1030   } else {
1031     return;
1032   }
1033   
1034   if (!title) return;
1035   
1036   if (prefs_w != NULL) {
1037     reactivate_window(prefs_w);
1038   } else {
1039     prefs_cb(w, dummy);
1040   }
1041
1042   p.w = notebook;
1043   p.page_num = FIRST_PROTO_PREFS_PAGE;
1044   p.title = title;
1045
1046   prefs_module_foreach(module_search_properties, &p);
1047
1048 }
1049
1050 /* Prefs tree selection callback.  The node data has been loaded with 
1051    the proper notebook page to load. */
1052 static void
1053 prefs_tree_select_cb(GtkCTree *ct, GtkCTreeNode *node, gint col, gpointer dummy)
1054 {
1055   gint page = GPOINTER_TO_INT(gtk_ctree_node_get_row_data(ct, node));
1056   
1057   if (page >= 0) 
1058     gtk_notebook_set_page(GTK_NOTEBOOK(notebook), page);
1059 }