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