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