2 * Routines for handling preferences
4 * $Id: prefs_dlg.c,v 1.80 2004/03/13 15:15:25 ulfl Exp $
6 * Ethereal - Network traffic analyzer
7 * By Gerald Combs <gerald@ethereal.com>
8 * Copyright 1998 Gerald Combs
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.
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.
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.
33 #include <epan/filesystem.h>
36 #include <epan/packet.h>
39 #include "column_prefs.h"
41 #include "prefs_dlg.h"
42 #include "print_prefs.h"
43 #include "stream_prefs.h"
44 #include "gui_prefs.h"
45 #include "capture_prefs.h"
46 #include "nameres_prefs.h"
48 #include "dlg_utils.h"
49 #include "simple_dialog.h"
50 #include "compat_macros.h"
52 #include "prefs-int.h"
56 #include "capture-wpcap.h"
58 #endif /* HAVE_LIBPCAP */
60 static void prefs_main_ok_cb(GtkWidget *, gpointer);
61 static void prefs_main_apply_cb(GtkWidget *, gpointer);
62 static void prefs_main_save_cb(GtkWidget *, gpointer);
63 static void prefs_main_cancel_cb(GtkWidget *, gpointer);
64 static gboolean prefs_main_delete_cb(GtkWidget *, gpointer);
65 static void prefs_main_destroy_cb(GtkWidget *, gpointer);
66 #if GTK_MAJOR_VERSION < 2
67 static void prefs_tree_select_cb(GtkCTree *, GtkCTreeNode *, gint,
70 static void prefs_tree_select_cb(GtkTreeSelection *, gpointer);
73 #define E_GUI_PAGE_KEY "gui_options_page"
74 #define E_GUI_COLUMN_PAGE_KEY "gui_column_options_page"
75 #define E_GUI_FONT_PAGE_KEY "gui_font_options_page"
76 #define E_GUI_STREAM_PAGE_KEY "gui_tcp_stream_options_page"
77 #define E_CAPTURE_PAGE_KEY "capture_options_page"
78 #define E_PRINT_PAGE_KEY "printer_options_page"
79 #define E_NAMERES_PAGE_KEY "nameres_options_page"
80 #define E_TOOLTIPS_KEY "tooltips"
81 #define E_PAGE_MODULE_KEY "page_module"
84 * Keep a static pointer to the notebook to be able to choose the
87 static GtkWidget *notebook;
90 * Keep a static pointer to the current "Preferences" window, if any, so that
91 * if somebody tries to do "Edit:Preferences" while there's already a
92 * "Preferences" window up, we just pop up the existing one, rather than
95 static GtkWidget *prefs_w;
98 * Save the value of the preferences as of when the preferences dialog
99 * box was first popped up, so we can revert to those values if the
100 * user selects "Cancel".
102 static e_prefs saved_prefs;
108 #if GTK_MAJOR_VERSION < 2
113 GtkTooltips *tooltips;
115 gboolean is_protocol;
119 pref_exists(pref_t *pref _U_, gpointer user_data _U_)
125 pref_show(pref_t *pref, gpointer user_data)
127 GtkWidget *main_tb = user_data;
132 /* Give this preference a label which is its title, followed by a colon,
133 and left-align it. */
135 label_string = g_malloc(strlen(title) + 2);
136 strcpy(label_string, title);
137 strcat(label_string, ":");
139 /* Save the current value of the preference, so that we can revert it if
140 the user does "Apply" and then "Cancel", and create the control for
141 editing the preference. */
142 switch (pref->type) {
145 pref->saved_val.uint = *pref->varp.uint;
147 /* XXX - there are no uint spinbuttons, so we can't use a spinbutton.
148 Even more annoyingly, even if there were, GLib doesn't define
149 G_MAXUINT - but I think ANSI C may define UINT_MAX, so we could
151 switch (pref->info.base) {
154 g_snprintf(uint_str, 10+1, "%u", pref->saved_val.uint);
158 g_snprintf(uint_str, 10+1, "%o", pref->saved_val.uint);
162 g_snprintf(uint_str, 10+1, "%x", pref->saved_val.uint);
165 pref->control = create_preference_entry(main_tb, pref->ordinal,
166 label_string, pref->description,
171 pref->saved_val.boolval = *pref->varp.boolp;
172 pref->control = create_preference_check_button(main_tb, pref->ordinal,
173 label_string, pref->description,
174 pref->saved_val.boolval);
178 pref->saved_val.enumval = *pref->varp.enump;
179 if (pref->info.enum_info.radio_buttons) {
180 /* Show it as radio buttons. */
181 pref->control = create_preference_radio_buttons(main_tb, pref->ordinal,
182 label_string, pref->description,
183 pref->info.enum_info.enumvals,
184 pref->saved_val.enumval);
186 /* Show it as an option menu. */
187 pref->control = create_preference_option_menu(main_tb, pref->ordinal,
188 label_string, pref->description,
189 pref->info.enum_info.enumvals,
190 pref->saved_val.enumval);
195 if (pref->saved_val.string != NULL)
196 g_free(pref->saved_val.string);
197 pref->saved_val.string = g_strdup(*pref->varp.string);
198 pref->control = create_preference_entry(main_tb, pref->ordinal,
199 label_string, pref->description,
200 pref->saved_val.string);
204 g_assert_not_reached();
207 g_free(label_string);
212 #define MAX_TREE_NODE_NAME_LEN 64
214 module_prefs_show(module_t *module, gpointer user_data)
216 struct ct_struct *cts = user_data;
217 struct ct_struct child_cts;
218 GtkWidget *main_vb, *main_tb, *frame;
219 gchar label_str[MAX_TREE_NODE_NAME_LEN];
220 #if GTK_MAJOR_VERSION < 2
221 gchar *label_ptr = label_str;
222 GtkCTreeNode *ct_node;
229 * Is this module a subtree, with modules underneath it?
231 if (!module->is_subtree) {
234 * Does it have any preferences (other than possibly obsolete ones)?
236 if (prefs_pref_foreach(module, pref_exists, NULL) == 0) {
238 * No. Don't put the module into the preferences window.
239 * XXX - we should do the same for subtrees; if a subtree has
240 * nothing under it that will be displayed, don't put it into
248 * Add this module to the tree.
250 strcpy(label_str, module->title);
251 #if GTK_MAJOR_VERSION < 2
252 ct_node = gtk_ctree_insert_node(GTK_CTREE(cts->tree), cts->node, NULL,
253 &label_ptr, 5, NULL, NULL, NULL, NULL, !module->is_subtree,
256 model = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(cts->tree)));
257 if (module->is_subtree)
258 gtk_tree_store_append(model, &iter, NULL);
260 gtk_tree_store_append(model, &iter, &cts->iter);
266 if (module->is_subtree) {
271 /* Note that there's no page attached to this item */
272 #if GTK_MAJOR_VERSION < 2
273 gtk_ctree_node_set_row_data(GTK_CTREE(cts->tree), ct_node,
274 GINT_TO_POINTER(-1));
276 gtk_tree_store_set(model, &iter, 0, label_str, 1, -1, -1);
280 * Walk the subtree and attach stuff to it.
283 #if GTK_MAJOR_VERSION < 2
284 child_cts.node = ct_node;
286 child_cts.iter = iter;
288 if (module == protocols_module)
289 child_cts.is_protocol = TRUE;
290 prefs_module_list_foreach(module->prefs, module_prefs_show, &child_cts);
293 * No. Create a notebook page for it.
297 frame = gtk_frame_new(module->title);
298 gtk_widget_show(frame);
300 /* Main vertical box */
301 main_vb = gtk_vbox_new(FALSE, 5);
302 gtk_container_border_width(GTK_CONTAINER(main_vb), 5);
303 gtk_container_add(GTK_CONTAINER(frame), main_vb);
306 main_tb = gtk_table_new(module->numprefs, 2, FALSE);
307 gtk_box_pack_start(GTK_BOX(main_vb), main_tb, FALSE, FALSE, 0);
308 gtk_table_set_row_spacings(GTK_TABLE(main_tb), 10);
309 gtk_table_set_col_spacings(GTK_TABLE(main_tb), 15);
310 OBJECT_SET_DATA(main_tb, E_TOOLTIPS_KEY, cts->tooltips);
312 /* Add items for each of the preferences */
313 prefs_pref_foreach(module, pref_show, main_tb);
315 /* Associate this module with the page's frame. */
316 OBJECT_SET_DATA(frame, E_PAGE_MODULE_KEY, module);
318 /* Add the page to the notebook */
319 gtk_notebook_append_page(GTK_NOTEBOOK(cts->notebook), frame, NULL);
321 /* Attach the page to the tree item */
322 #if GTK_MAJOR_VERSION < 2
323 gtk_ctree_node_set_row_data(GTK_CTREE(cts->tree), ct_node,
324 GINT_TO_POINTER(cts->page));
326 gtk_tree_store_set(model, &iter, 0, label_str, 1, cts->page, -1);
331 /* Show 'em what we got */
332 gtk_widget_show_all(main_vb);
337 prefs_cb(GtkWidget *w _U_, gpointer dummy _U_)
339 GtkWidget *top_hb, *bbox, *prefs_nb, *ct_sb, *frame,
340 *ok_bt, *apply_bt, *save_bt, *cancel_bt;
341 GtkWidget *gui_pg, *gui_font_pg, *gui_column_pg, *gui_stream_pg;
343 GtkWidget *capture_pg;
345 GtkWidget *print_pg, *nameres_pg;
346 gchar label_str[MAX_TREE_NODE_NAME_LEN];
347 struct ct_struct cts;
348 #if GTK_MAJOR_VERSION < 2
349 gchar *label_ptr = label_str;
350 GtkCTreeNode *ct_node;
351 GtkCTreeNode *ct_base_node;
354 GtkTreeSelection *selection;
355 GtkCellRenderer *renderer;
356 GtkTreeViewColumn *column;
359 GtkTreeIter base_iter;
361 #if GTK_MAJOR_VERSION < 2
362 static gchar *fixedwidths[] = { "c", "m", NULL };
365 if (prefs_w != NULL) {
366 /* There's already a "Preferences" dialog box; reactivate it. */
367 reactivate_window(prefs_w);
371 /* Save the current preferences, so we can revert to those values
372 if the user presses "Cancel". */
373 copy_prefs(&saved_prefs, &prefs);
375 prefs_w = dlg_window_new("Ethereal: Preferences");
376 SIGNAL_CONNECT(prefs_w, "delete_event", prefs_main_delete_cb, NULL);
377 SIGNAL_CONNECT(prefs_w, "destroy", prefs_main_destroy_cb, NULL);
380 * Unfortunately, we can't arrange that a GtkTable widget wrap an event box
381 * around a table row, so the spacing between the preference item's label
382 * and its control widgets is inactive and the tooltip doesn't pop up when
383 * the mouse is over it.
385 cts.tooltips = gtk_tooltips_new();
387 /* Container for each row of widgets */
388 cts.main_vb = gtk_vbox_new(FALSE, 5);
389 gtk_container_border_width(GTK_CONTAINER(cts.main_vb), 5);
390 gtk_container_add(GTK_CONTAINER(prefs_w), cts.main_vb);
391 gtk_widget_show(cts.main_vb);
393 /* Top row: Preferences tree and notebook */
394 top_hb = gtk_hbox_new(FALSE, 10);
395 gtk_container_add(GTK_CONTAINER(cts.main_vb), top_hb);
396 gtk_widget_show(top_hb);
398 /* Place a Ctree on the left for preference categories */
399 ct_sb = scrolled_window_new(NULL, NULL);
400 #if GTK_MAJOR_VERSION >= 2
401 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(ct_sb),
404 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(ct_sb),
405 GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
406 gtk_container_add(GTK_CONTAINER(top_hb), ct_sb);
407 gtk_widget_show(ct_sb);
409 #if GTK_MAJOR_VERSION < 2
410 cts.tree = ctree_new(1, 0);
413 store = gtk_tree_store_new(2, G_TYPE_STRING, G_TYPE_INT);
414 cts.tree = tree_view_new(GTK_TREE_MODEL(store));
415 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(cts.tree), FALSE);
416 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(cts.tree));
417 gtk_tree_selection_set_mode(selection, GTK_SELECTION_SINGLE);
418 renderer = gtk_cell_renderer_text_new();
419 col_offset = gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(cts.tree),
420 -1, "Name", renderer,
422 column = gtk_tree_view_get_column(GTK_TREE_VIEW(cts.tree),
424 gtk_tree_view_column_set_sizing(GTK_TREE_VIEW_COLUMN(column),
425 GTK_TREE_VIEW_COLUMN_AUTOSIZE);
428 gtk_container_add(GTK_CONTAINER(ct_sb), cts.tree);
430 #if GTK_MAJOR_VERSION < 2
431 gtk_clist_set_column_auto_resize(GTK_CLIST(cts.tree), 0, TRUE);
432 SIGNAL_CONNECT(cts.tree, "tree-select-row", prefs_tree_select_cb, NULL);
434 SIGNAL_CONNECT(selection, "changed", prefs_tree_select_cb, NULL);
436 gtk_widget_show(cts.tree);
438 /* A notebook widget sans tabs is used to flip between prefs */
439 notebook = prefs_nb = gtk_notebook_new();
440 gtk_notebook_set_show_tabs(GTK_NOTEBOOK(prefs_nb), FALSE);
441 gtk_notebook_set_show_border(GTK_NOTEBOOK(prefs_nb), FALSE);
442 gtk_container_add(GTK_CONTAINER(top_hb), prefs_nb);
443 gtk_widget_show(prefs_nb);
446 frame = gtk_frame_new("User Interface");
447 gtk_widget_show(GTK_WIDGET(frame));
448 gui_pg = gui_prefs_show();
449 gtk_container_add(GTK_CONTAINER(frame), gui_pg);
450 OBJECT_SET_DATA(prefs_w, E_GUI_PAGE_KEY, gui_pg);
451 gtk_notebook_append_page (GTK_NOTEBOOK(prefs_nb), frame, NULL);
452 strcpy(label_str, "User Interface");
453 #if GTK_MAJOR_VERSION < 2
454 ct_node = gtk_ctree_insert_node(GTK_CTREE(cts.tree), NULL, NULL,
455 &label_ptr, 5, NULL, NULL, NULL, NULL, FALSE /*TRUE*/, TRUE);
456 gtk_ctree_node_set_row_data(GTK_CTREE(cts.tree), ct_node,
457 GINT_TO_POINTER(cts.page));
458 ct_base_node = ct_node;
459 gtk_ctree_select(GTK_CTREE(cts.tree), ct_node);
461 gtk_tree_store_append(store, &iter, NULL);
462 gtk_tree_store_set(store, &iter, 0, label_str, 1, cts.page, -1);
464 gtk_tree_selection_select_iter(selection, &iter);
468 /* GUI Column prefs */
469 frame = gtk_frame_new("Columns");
470 gtk_widget_show(GTK_WIDGET(frame));
471 gui_column_pg = column_prefs_show();
472 gtk_container_add(GTK_CONTAINER(frame), gui_column_pg);
473 OBJECT_SET_DATA(prefs_w, E_GUI_COLUMN_PAGE_KEY, gui_column_pg);
474 gtk_notebook_append_page (GTK_NOTEBOOK(prefs_nb), frame, NULL);
475 strcpy(label_str, "Columns");
476 #if GTK_MAJOR_VERSION < 2
477 ct_node = gtk_ctree_insert_node(GTK_CTREE(cts.tree), ct_base_node, NULL,
478 &label_ptr, 5, NULL, NULL, NULL, NULL, TRUE, TRUE);
479 gtk_ctree_node_set_row_data(GTK_CTREE(cts.tree), ct_node,
480 GINT_TO_POINTER(cts.page));
482 gtk_tree_store_append(store, &iter, &base_iter);
483 gtk_tree_store_set(store, &iter, 0, label_str, 1, cts.page, -1);
484 /* expand the parent */
485 gtk_tree_view_expand_all(GTK_TREE_VIEW(cts.tree));
490 frame = gtk_frame_new("Font");
491 gtk_widget_show(GTK_WIDGET(frame));
492 gui_font_pg = gui_font_prefs_show();
493 gtk_container_add(GTK_CONTAINER(frame), gui_font_pg);
494 OBJECT_SET_DATA(prefs_w, E_GUI_FONT_PAGE_KEY, gui_font_pg);
495 gtk_notebook_append_page (GTK_NOTEBOOK(prefs_nb), frame, NULL);
497 /* We set the current font and, for GTK+ 1.2[.x], the font filter
498 now, because they appear not to work when run before appending
499 the frame to the notebook. */
501 /* Set the font to the current font.
502 XXX - GTK+ 1.2.8, and probably earlier versions, have a bug
503 wherein that doesn't necessarily cause that font to be
504 selected in the dialog box. I've sent to the GTK+ folk
505 a fix; hopefully, it'll show up in 1.2.9 if, as, and when
506 they put out a 1.2.9 release. */
507 gtk_font_selection_set_font_name(
508 GTK_FONT_SELECTION(gui_font_pg), prefs.PREFS_GUI_FONT_NAME);
510 #if GTK_MAJOR_VERSION < 2
511 /* Set its filter to show only fixed_width fonts. */
512 gtk_font_selection_set_filter(
513 GTK_FONT_SELECTION(gui_font_pg),
514 GTK_FONT_FILTER_BASE, /* user can't change the filter */
515 GTK_FONT_ALL, /* bitmap or scalable are fine */
516 NULL, /* all foundries are OK */
517 NULL, /* all weights are OK (XXX - normal only?) */
518 NULL, /* all slants are OK (XXX - Roman only?) */
519 NULL, /* all setwidths are OK */
520 fixedwidths, /* ONLY fixed-width fonts */
521 NULL); /* all charsets are OK (XXX - ISO 8859/1 only?) */
524 strcpy(label_str, "Font");
525 #if GTK_MAJOR_VERSION < 2
526 ct_node = gtk_ctree_insert_node(GTK_CTREE(cts.tree), ct_base_node, NULL,
527 &label_ptr, 5, NULL, NULL, NULL, NULL, TRUE, TRUE);
528 gtk_ctree_node_set_row_data(GTK_CTREE(cts.tree), ct_node,
529 GINT_TO_POINTER(cts.page));
531 gtk_tree_store_append(store, &iter, &base_iter);
532 gtk_tree_store_set(store, &iter, 0, label_str, 1, cts.page, -1);
536 /* GUI Colors prefs */
537 frame = gtk_frame_new("Colors");
538 gtk_widget_show(GTK_WIDGET(frame));
539 gui_stream_pg = stream_prefs_show();
540 gtk_container_add(GTK_CONTAINER(frame), gui_stream_pg);
541 OBJECT_SET_DATA(prefs_w, E_GUI_STREAM_PAGE_KEY, gui_stream_pg);
542 gtk_notebook_append_page (GTK_NOTEBOOK(prefs_nb), frame, NULL);
543 strcpy(label_str, "Colors");
544 #if GTK_MAJOR_VERSION < 2
545 ct_node = gtk_ctree_insert_node(GTK_CTREE(cts.tree), ct_base_node, NULL,
546 &label_ptr, 5, NULL, NULL, NULL, NULL, TRUE, TRUE);
547 gtk_ctree_node_set_row_data(GTK_CTREE(cts.tree), ct_node,
548 GINT_TO_POINTER(cts.page));
550 gtk_tree_store_append(store, &iter, &base_iter);
551 gtk_tree_store_set(store, &iter, 0, label_str, 1, cts.page, -1);
557 /* Is WPcap loaded? */
561 frame = gtk_frame_new("Capture");
562 gtk_widget_show(GTK_WIDGET(frame));
563 capture_pg = capture_prefs_show();
564 gtk_container_add(GTK_CONTAINER(frame), capture_pg);
565 OBJECT_SET_DATA(prefs_w, E_CAPTURE_PAGE_KEY, capture_pg);
566 gtk_notebook_append_page (GTK_NOTEBOOK(prefs_nb), frame, NULL);
567 strcpy(label_str, "Capture");
568 #if GTK_MAJOR_VERSION < 2
569 ct_node = gtk_ctree_insert_node(GTK_CTREE(cts.tree), NULL, NULL,
570 &label_ptr, 5, NULL, NULL, NULL, NULL, TRUE, TRUE);
571 gtk_ctree_node_set_row_data(GTK_CTREE(cts.tree), ct_node,
572 GINT_TO_POINTER(cts.page));
574 gtk_tree_store_append(store, &iter, NULL);
575 gtk_tree_store_set(store, &iter, 0, label_str, 1, cts.page, -1);
581 #endif /* HAVE_LIBPCAP */
584 frame = gtk_frame_new("Printing");
585 gtk_widget_show(GTK_WIDGET(frame));
586 print_pg = printer_prefs_show();
587 gtk_container_add(GTK_CONTAINER(frame), print_pg);
588 OBJECT_SET_DATA(prefs_w, E_PRINT_PAGE_KEY, print_pg);
589 gtk_notebook_append_page (GTK_NOTEBOOK(prefs_nb), frame, NULL);
590 strcpy(label_str, "Printing");
591 #if GTK_MAJOR_VERSION < 2
592 ct_node = gtk_ctree_insert_node(GTK_CTREE(cts.tree), NULL, NULL,
593 &label_ptr, 5, NULL, NULL, NULL, NULL, TRUE, TRUE);
594 gtk_ctree_node_set_row_data(GTK_CTREE(cts.tree), ct_node,
595 GINT_TO_POINTER(cts.page));
597 gtk_tree_store_append(store, &iter, NULL);
598 gtk_tree_store_set(store, &iter, 0, label_str, 1, cts.page, -1);
602 /* Name resolution prefs */
603 frame = gtk_frame_new("Name Resolution");
604 gtk_widget_show(GTK_WIDGET(frame));
605 nameres_pg = nameres_prefs_show();
606 gtk_container_add(GTK_CONTAINER(frame), nameres_pg);
607 OBJECT_SET_DATA(prefs_w, E_NAMERES_PAGE_KEY, nameres_pg);
608 gtk_notebook_append_page (GTK_NOTEBOOK(prefs_nb), frame, NULL);
609 strcpy(label_str, "Name Resolution");
610 #if GTK_MAJOR_VERSION < 2
611 ct_node = gtk_ctree_insert_node(GTK_CTREE(cts.tree), NULL, NULL,
612 &label_ptr, 5, NULL, NULL, NULL, NULL, TRUE, TRUE);
613 gtk_ctree_node_set_row_data(GTK_CTREE(cts.tree), ct_node,
614 GINT_TO_POINTER(cts.page));
616 gtk_tree_store_append(store, &iter, NULL);
617 gtk_tree_store_set(store, &iter, 0, label_str, 1, cts.page, -1);
621 /* Registered prefs */
622 cts.notebook = prefs_nb;
623 cts.is_protocol = FALSE;
624 prefs_module_list_foreach(NULL, module_prefs_show, &cts);
626 /* Button row: OK and cancel buttons */
627 bbox = dlg_button_row_new(GTK_STOCK_OK, GTK_STOCK_APPLY, GTK_STOCK_SAVE, GTK_STOCK_CANCEL, NULL);
628 gtk_box_pack_start(GTK_BOX(cts.main_vb), bbox, FALSE, FALSE, 0);
629 gtk_widget_show(bbox);
631 ok_bt = OBJECT_GET_DATA(bbox, GTK_STOCK_OK);
632 SIGNAL_CONNECT(ok_bt, "clicked", prefs_main_ok_cb, prefs_w);
633 gtk_widget_grab_default(ok_bt);
635 apply_bt = OBJECT_GET_DATA(bbox, GTK_STOCK_APPLY);
636 SIGNAL_CONNECT(apply_bt, "clicked", prefs_main_apply_cb, prefs_w);
638 save_bt = OBJECT_GET_DATA(bbox, GTK_STOCK_SAVE);
639 SIGNAL_CONNECT(save_bt, "clicked", prefs_main_save_cb, prefs_w);
641 cancel_bt = OBJECT_GET_DATA(bbox, GTK_STOCK_CANCEL);
642 SIGNAL_CONNECT(cancel_bt, "clicked", prefs_main_cancel_cb, prefs_w);
644 /* Catch the "key_press_event" signal in the window, so that we can catch
645 the ESC key being pressed and act as if the "Cancel" button had
647 dlg_set_cancel(prefs_w, cancel_bt);
649 gtk_widget_show(prefs_w);
651 #if GTK_MAJOR_VERSION >= 2
652 g_object_unref(G_OBJECT(store));
657 set_option_label(GtkWidget *main_tb, int table_position,
658 const gchar *label_text, const gchar *tooltip_text, GtkTooltips *tooltips)
661 GtkWidget *event_box;
663 label = gtk_label_new(label_text);
664 gtk_misc_set_alignment(GTK_MISC(label), 1.0, 0.5);
665 gtk_widget_show(label);
667 event_box = gtk_event_box_new();
668 gtk_table_attach_defaults(GTK_TABLE(main_tb), event_box, 0, 1,
669 table_position, table_position + 1);
670 if (tooltip_text != NULL && tooltips != NULL)
671 gtk_tooltips_set_tip(tooltips, event_box, tooltip_text, NULL);
672 gtk_container_add(GTK_CONTAINER(event_box), label);
673 gtk_widget_show(event_box);
677 create_preference_check_button(GtkWidget *main_tb, int table_position,
678 const gchar *label_text, const gchar *tooltip_text, gboolean active)
680 GtkTooltips *tooltips;
681 GtkWidget *check_box;
683 tooltips = OBJECT_GET_DATA(main_tb, E_TOOLTIPS_KEY);
685 set_option_label(main_tb, table_position, label_text, tooltip_text,
688 check_box = gtk_check_button_new();
689 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check_box), active);
690 gtk_table_attach_defaults(GTK_TABLE(main_tb), check_box, 1, 2,
691 table_position, table_position + 1);
692 if (tooltip_text != NULL && tooltips != NULL)
693 gtk_tooltips_set_tip(tooltips, check_box, tooltip_text, NULL);
699 create_preference_radio_buttons(GtkWidget *main_tb, int table_position,
700 const gchar *label_text, const gchar *tooltip_text,
701 const enum_val_t *enumvals, gint current_val)
703 GtkTooltips *tooltips;
704 GtkWidget *radio_button_hbox, *button = NULL;
707 const enum_val_t *enum_valp;
708 GtkWidget *event_box;
710 tooltips = OBJECT_GET_DATA(main_tb, E_TOOLTIPS_KEY);
712 set_option_label(main_tb, table_position, label_text, tooltip_text,
715 radio_button_hbox = gtk_hbox_new(FALSE, 0);
717 for (enum_valp = enumvals, index = 0; enum_valp->name != NULL;
718 enum_valp++, index++) {
719 button = gtk_radio_button_new_with_label(rb_group,
721 gtk_widget_show(button);
722 rb_group = gtk_radio_button_group(GTK_RADIO_BUTTON(button));
723 gtk_box_pack_start(GTK_BOX(radio_button_hbox), button, FALSE,
725 if (enum_valp->value == current_val) {
726 gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(button),
730 gtk_widget_show(radio_button_hbox);
732 event_box = gtk_event_box_new();
733 gtk_container_add(GTK_CONTAINER(event_box), radio_button_hbox);
734 gtk_table_attach_defaults(GTK_TABLE(main_tb), event_box, 1, 2,
735 table_position, table_position+1);
736 if (tooltip_text != NULL && tooltips != NULL)
737 gtk_tooltips_set_tip(tooltips, event_box, tooltip_text, NULL);
738 gtk_widget_show(event_box);
741 * It doesn't matter which of the buttons we return - we fetch
742 * the value by looking at the entire radio button group to
743 * which it belongs, and we can get that from any button.
749 label_to_enum_val(GtkWidget *label, const enum_val_t *enumvals)
754 /* Get the label's text, and translate it to a value. */
755 gtk_label_get(GTK_LABEL(label), &label_string);
756 enumval = find_val_for_string(label_string, enumvals, 1);
762 fetch_preference_radio_buttons_val(GtkWidget *button,
763 const enum_val_t *enumvals)
769 * Go through the list of of radio buttons in the button's group,
770 * and find the first one that's active.
772 rb_group = gtk_radio_button_group(GTK_RADIO_BUTTON(button));
774 for (rb_entry = rb_group; rb_entry != NULL;
775 rb_entry = g_slist_next(rb_entry)) {
776 button = rb_entry->data;
777 if (GTK_TOGGLE_BUTTON(button)->active)
781 /* OK, now return the value corresponding to that button's label. */
782 return label_to_enum_val(GTK_BIN(button)->child, enumvals);
786 create_preference_option_menu(GtkWidget *main_tb, int table_position,
787 const gchar *label_text, const gchar *tooltip_text,
788 const enum_val_t *enumvals, gint current_val)
790 GtkTooltips *tooltips;
791 GtkWidget *menu_box, *menu, *menu_item, *option_menu;
792 int menu_index, index;
793 const enum_val_t *enum_valp;
794 GtkWidget *event_box;
796 tooltips = OBJECT_GET_DATA(main_tb, E_TOOLTIPS_KEY);
798 set_option_label(main_tb, table_position, label_text, tooltip_text,
801 /* Create a menu from the enumvals */
802 menu = gtk_menu_new();
803 if (tooltip_text != NULL && tooltips != NULL)
804 gtk_tooltips_set_tip(tooltips, menu, tooltip_text, NULL);
806 for (enum_valp = enumvals, index = 0; enum_valp->name != NULL;
807 enum_valp++, index++) {
808 menu_item = gtk_menu_item_new_with_label(enum_valp->name);
809 gtk_menu_append(GTK_MENU(menu), menu_item);
810 if (enum_valp->value == current_val)
812 gtk_widget_show(menu_item);
815 /* Create the option menu from the menu */
816 option_menu = gtk_option_menu_new();
817 gtk_option_menu_set_menu(GTK_OPTION_MENU(option_menu), menu);
819 /* Set its current value to the variable's current value */
820 if (menu_index != -1)
821 gtk_option_menu_set_history(GTK_OPTION_MENU(option_menu),
825 * Put the option menu in an hbox, so that it's only as wide
826 * as the widest entry, rather than being as wide as the table
829 menu_box = gtk_hbox_new(FALSE, 0);
830 gtk_box_pack_start(GTK_BOX(menu_box), option_menu, FALSE, FALSE, 0);
832 event_box = gtk_event_box_new();
833 gtk_table_attach_defaults(GTK_TABLE(main_tb), event_box,
834 1, 2, table_position, table_position + 1);
835 if (tooltip_text != NULL && tooltips != NULL)
836 gtk_tooltips_set_tip(tooltips, event_box, tooltip_text, NULL);
837 gtk_container_add(GTK_CONTAINER(event_box), menu_box);
843 fetch_preference_option_menu_val(GtkWidget *optmenu, const enum_val_t *enumvals)
846 * OK, now return the value corresponding to the label for the
847 * currently active entry in the option menu.
849 * Yes, this is how you get the label for that entry. See FAQ
850 * 6.8 in the GTK+ FAQ.
852 return label_to_enum_val(GTK_BIN(optmenu)->child, enumvals);
856 create_preference_entry(GtkWidget *main_tb, int table_position,
857 const gchar *label_text, const gchar *tooltip_text, char *value)
859 GtkTooltips *tooltips;
862 tooltips = OBJECT_GET_DATA(main_tb, E_TOOLTIPS_KEY);
864 set_option_label(main_tb, table_position, label_text, tooltip_text,
867 entry = gtk_entry_new();
869 gtk_entry_set_text(GTK_ENTRY(entry), value);
870 gtk_table_attach_defaults(GTK_TABLE(main_tb), entry, 1, 2,
871 table_position, table_position + 1);
872 if (tooltip_text != NULL && tooltips != NULL)
873 gtk_tooltips_set_tip(tooltips, entry, tooltip_text, NULL);
874 gtk_widget_show(entry);
880 pref_fetch(pref_t *pref, gpointer user_data)
887 gboolean *pref_changed_p = user_data;
889 /* Fetch the value of the preference, and set the appropriate variable
891 switch (pref->type) {
894 str_val = gtk_entry_get_text(GTK_ENTRY(pref->control));
895 uval = strtoul(str_val, &p, pref->info.base);
897 if (p == value || *p != '\0')
898 return PREFS_SET_SYNTAX_ERR; /* number was bad */
900 if (*pref->varp.uint != uval) {
901 *pref_changed_p = TRUE;
902 *pref->varp.uint = uval;
907 bval = GTK_TOGGLE_BUTTON(pref->control)->active;
908 if (*pref->varp.boolp != bval) {
909 *pref_changed_p = TRUE;
910 *pref->varp.boolp = bval;
915 if (pref->info.enum_info.radio_buttons) {
916 enumval = fetch_preference_radio_buttons_val(pref->control,
917 pref->info.enum_info.enumvals);
919 enumval = fetch_preference_option_menu_val(pref->control,
920 pref->info.enum_info.enumvals);
923 if (*pref->varp.enump != enumval) {
924 *pref_changed_p = TRUE;
925 *pref->varp.enump = enumval;
930 str_val = gtk_entry_get_text(GTK_ENTRY(pref->control));
931 if (strcmp(*pref->varp.string, str_val) != 0) {
932 *pref_changed_p = TRUE;
933 g_free(*pref->varp.string);
934 *pref->varp.string = g_strdup(str_val);
939 g_assert_not_reached();
946 module_prefs_fetch(module_t *module, gpointer user_data)
948 gboolean *must_redissect_p = user_data;
950 /* For all preferences in this module, fetch its value from this
951 module's notebook page. Find out whether any of them changed. */
952 module->prefs_changed = FALSE; /* assume none of them changed */
953 prefs_pref_foreach(module, pref_fetch, &module->prefs_changed);
955 /* If any of them changed, indicate that we must redissect and refilter
956 the current capture (if we have one), as the preference change
957 could cause packets to be dissected differently. */
958 if (module->prefs_changed)
959 *must_redissect_p = TRUE;
963 pref_clean(pref_t *pref, gpointer user_data _U_)
965 switch (pref->type) {
977 if (pref->saved_val.string != NULL) {
978 g_free(pref->saved_val.string);
979 pref->saved_val.string = NULL;
984 g_assert_not_reached();
991 module_prefs_clean(module_t *module, gpointer user_data _U_)
993 /* For all preferences in this module, clean up any cruft allocated for
994 use by the GUI code. */
995 prefs_pref_foreach(module, pref_clean, NULL);
999 prefs_main_ok_cb(GtkWidget *ok_bt _U_, gpointer parent_w)
1001 gboolean must_redissect = FALSE;
1003 /* Fetch the preferences (i.e., make sure all the values set in all of
1004 the preferences panes have been copied to "prefs" and the registered
1006 printer_prefs_fetch(OBJECT_GET_DATA(parent_w, E_PRINT_PAGE_KEY));
1007 column_prefs_fetch(OBJECT_GET_DATA(parent_w, E_GUI_COLUMN_PAGE_KEY));
1008 stream_prefs_fetch(OBJECT_GET_DATA(parent_w, E_GUI_STREAM_PAGE_KEY));
1009 gui_prefs_fetch(OBJECT_GET_DATA(parent_w, E_GUI_PAGE_KEY));
1012 /* Is WPcap loaded? */
1015 capture_prefs_fetch(OBJECT_GET_DATA(parent_w, E_CAPTURE_PAGE_KEY));
1019 #endif /* HAVE_LIBPCAP */
1020 nameres_prefs_fetch(OBJECT_GET_DATA(parent_w, E_NAMERES_PAGE_KEY));
1021 prefs_modules_foreach(module_prefs_fetch, &must_redissect);
1023 /* Now apply those preferences. */
1024 printer_prefs_apply(OBJECT_GET_DATA(parent_w, E_PRINT_PAGE_KEY));
1025 column_prefs_apply(OBJECT_GET_DATA(parent_w, E_GUI_COLUMN_PAGE_KEY));
1026 stream_prefs_apply(OBJECT_GET_DATA(parent_w, E_GUI_STREAM_PAGE_KEY));
1027 gui_prefs_apply(OBJECT_GET_DATA(parent_w, E_GUI_PAGE_KEY));
1030 /* Is WPcap loaded? */
1033 capture_prefs_apply(OBJECT_GET_DATA(parent_w, E_CAPTURE_PAGE_KEY));
1037 #endif /* HAVE_LIBPCAP */
1038 nameres_prefs_apply(OBJECT_GET_DATA(parent_w, E_NAMERES_PAGE_KEY));
1041 /* Now destroy the "Preferences" dialog. */
1042 gtk_widget_destroy(GTK_WIDGET(parent_w));
1044 if (must_redissect) {
1045 /* Redissect all the packets, and re-evaluate the display filter. */
1046 redissect_packets(&cfile);
1051 prefs_main_apply_cb(GtkWidget *apply_bt _U_, gpointer parent_w)
1053 gboolean must_redissect = FALSE;
1055 /* Fetch the preferences (i.e., make sure all the values set in all of
1056 the preferences panes have been copied to "prefs" and the registered
1058 printer_prefs_fetch(OBJECT_GET_DATA(parent_w, E_PRINT_PAGE_KEY));
1059 column_prefs_fetch(OBJECT_GET_DATA(parent_w, E_GUI_COLUMN_PAGE_KEY));
1060 stream_prefs_fetch(OBJECT_GET_DATA(parent_w, E_GUI_STREAM_PAGE_KEY));
1061 gui_prefs_fetch(OBJECT_GET_DATA(parent_w, E_GUI_PAGE_KEY));
1064 /* Is WPcap loaded? */
1067 capture_prefs_fetch(OBJECT_GET_DATA(parent_w, E_CAPTURE_PAGE_KEY));
1071 #endif /* HAVE_LIBPCAP */
1072 nameres_prefs_fetch(OBJECT_GET_DATA(parent_w, E_NAMERES_PAGE_KEY));
1073 prefs_modules_foreach(module_prefs_fetch, &must_redissect);
1075 /* Now apply those preferences. */
1076 printer_prefs_apply(OBJECT_GET_DATA(parent_w, E_PRINT_PAGE_KEY));
1077 column_prefs_apply(OBJECT_GET_DATA(parent_w, E_GUI_COLUMN_PAGE_KEY));
1078 stream_prefs_apply(OBJECT_GET_DATA(parent_w, E_GUI_STREAM_PAGE_KEY));
1079 gui_prefs_apply(OBJECT_GET_DATA(parent_w, E_GUI_PAGE_KEY));
1082 /* Is WPcap loaded? */
1085 capture_prefs_apply(OBJECT_GET_DATA(parent_w, E_CAPTURE_PAGE_KEY));
1089 #endif /* HAVE_LIBPCAP */
1090 nameres_prefs_apply(OBJECT_GET_DATA(parent_w, E_NAMERES_PAGE_KEY));
1093 if (must_redissect) {
1094 /* Redissect all the packets, and re-evaluate the display filter. */
1095 redissect_packets(&cfile);
1100 prefs_main_save_cb(GtkWidget *save_bt _U_, gpointer parent_w)
1102 gboolean must_redissect = FALSE;
1107 /* Fetch the preferences (i.e., make sure all the values set in all of
1108 the preferences panes have been copied to "prefs" and the registered
1110 printer_prefs_fetch(OBJECT_GET_DATA(parent_w, E_PRINT_PAGE_KEY));
1111 column_prefs_fetch(OBJECT_GET_DATA(parent_w, E_GUI_COLUMN_PAGE_KEY));
1112 stream_prefs_fetch(OBJECT_GET_DATA(parent_w, E_GUI_STREAM_PAGE_KEY));
1113 gui_prefs_fetch(OBJECT_GET_DATA(parent_w, E_GUI_PAGE_KEY));
1116 /* Is WPcap loaded? */
1119 capture_prefs_fetch(OBJECT_GET_DATA(parent_w, E_CAPTURE_PAGE_KEY));
1123 #endif /* HAVE_LIBPCAP */
1124 nameres_prefs_fetch(OBJECT_GET_DATA(parent_w, E_NAMERES_PAGE_KEY));
1125 prefs_modules_foreach(module_prefs_fetch, &must_redissect);
1127 /* Create the directory that holds personal configuration files, if
1129 if (create_persconffile_dir(&pf_dir_path) == -1) {
1130 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
1131 "Can't create directory\n\"%s\"\nfor preferences file: %s.", pf_dir_path,
1133 g_free(pf_dir_path);
1135 /* Write the preferencs out. */
1136 err = write_prefs(&pf_path);
1138 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
1139 "Can't open preferences file\n\"%s\": %s.", pf_path,
1145 /* Now apply those preferences.
1146 XXX - should we do this? The user didn't click "OK" or "Apply".
1149 1) by saving the preferences they presumably indicate that they
1152 2) the next time they fire Ethereal up, those preferences will
1155 3) we'd have to buffer "must_redissect" so that if they do
1156 "Apply" after this, we know we have to redissect;
1158 4) we did apply the protocol preferences, at least, in the past. */
1159 printer_prefs_apply(OBJECT_GET_DATA(parent_w, E_PRINT_PAGE_KEY));
1160 column_prefs_apply(OBJECT_GET_DATA(parent_w, E_GUI_COLUMN_PAGE_KEY));
1161 stream_prefs_apply(OBJECT_GET_DATA(parent_w, E_GUI_STREAM_PAGE_KEY));
1162 gui_prefs_apply(OBJECT_GET_DATA(parent_w, E_GUI_PAGE_KEY));
1165 /* Is WPcap loaded? */
1168 capture_prefs_apply(OBJECT_GET_DATA(parent_w, E_CAPTURE_PAGE_KEY));
1172 #endif /* HAVE_LIBPCAP */
1173 nameres_prefs_apply(OBJECT_GET_DATA(parent_w, E_NAMERES_PAGE_KEY));
1176 if (must_redissect) {
1177 /* Redissect all the packets, and re-evaluate the display filter. */
1178 redissect_packets(&cfile);
1183 pref_revert(pref_t *pref, gpointer user_data)
1185 gboolean *pref_changed_p = user_data;
1187 /* Revert the preference to its saved value. */
1188 switch (pref->type) {
1191 if (*pref->varp.uint != pref->saved_val.uint) {
1192 *pref_changed_p = TRUE;
1193 *pref->varp.uint = pref->saved_val.uint;
1198 if (*pref->varp.boolp != pref->saved_val.boolval) {
1199 *pref_changed_p = TRUE;
1200 *pref->varp.boolp = pref->saved_val.boolval;
1205 if (*pref->varp.enump != pref->saved_val.enumval) {
1206 *pref_changed_p = TRUE;
1207 *pref->varp.enump = pref->saved_val.enumval;
1212 if (strcmp(*pref->varp.string, pref->saved_val.string) != 0) {
1213 *pref_changed_p = TRUE;
1214 g_free(*pref->varp.string);
1215 *pref->varp.string = g_strdup(pref->saved_val.string);
1220 g_assert_not_reached();
1227 module_prefs_revert(module_t *module, gpointer user_data)
1229 gboolean *must_redissect_p = user_data;
1231 /* For all preferences in this module, revert its value to the value
1232 it had when we popped up the Preferences dialog. Find out whether
1233 this changes any of them. */
1234 module->prefs_changed = FALSE; /* assume none of them changed */
1235 prefs_pref_foreach(module, pref_revert, &module->prefs_changed);
1237 /* If any of them changed, indicate that we must redissect and refilter
1238 the current capture (if we have one), as the preference change
1239 could cause packets to be dissected differently. */
1240 if (module->prefs_changed)
1241 *must_redissect_p = TRUE;
1245 prefs_main_cancel_cb(GtkWidget *cancel_bt _U_, gpointer parent_w)
1247 gboolean must_redissect = FALSE;
1249 /* Free up the current preferences and copy the saved preferences to the
1250 current preferences. */
1252 copy_prefs(&prefs, &saved_prefs);
1254 /* Now revert the registered preferences. */
1255 prefs_modules_foreach(module_prefs_revert, &must_redissect);
1257 /* Now apply the reverted-to preferences. */
1258 printer_prefs_apply(OBJECT_GET_DATA(parent_w, E_PRINT_PAGE_KEY));
1259 column_prefs_apply(OBJECT_GET_DATA(parent_w, E_GUI_COLUMN_PAGE_KEY));
1260 stream_prefs_apply(OBJECT_GET_DATA(parent_w, E_GUI_STREAM_PAGE_KEY));
1261 gui_prefs_apply(OBJECT_GET_DATA(parent_w, E_GUI_PAGE_KEY));
1262 nameres_prefs_apply(OBJECT_GET_DATA(parent_w, E_NAMERES_PAGE_KEY));
1265 gtk_widget_destroy(GTK_WIDGET(parent_w));
1267 if (must_redissect) {
1268 /* Redissect all the packets, and re-evaluate the display filter. */
1269 redissect_packets(&cfile);
1273 /* Treat this as a cancel, by calling "prefs_main_cancel_cb()".
1274 XXX - that'll destroy the Preferences dialog; will that upset
1275 a higher-level handler that says "OK, we've been asked to delete
1276 this, so destroy it"? */
1278 prefs_main_delete_cb(GtkWidget *prefs_w, gpointer dummy _U_)
1280 prefs_main_cancel_cb(NULL, prefs_w);
1285 prefs_main_destroy_cb(GtkWidget *win _U_, gpointer user_data _U_)
1287 /* Let the preference tabs clean up anything they've done. */
1288 printer_prefs_destroy(OBJECT_GET_DATA(prefs_w, E_PRINT_PAGE_KEY));
1289 column_prefs_destroy(OBJECT_GET_DATA(prefs_w, E_GUI_COLUMN_PAGE_KEY));
1290 stream_prefs_destroy(OBJECT_GET_DATA(prefs_w, E_GUI_STREAM_PAGE_KEY));
1291 gui_prefs_destroy(OBJECT_GET_DATA(prefs_w, E_GUI_PAGE_KEY));
1294 /* Is WPcap loaded? */
1297 capture_prefs_destroy(OBJECT_GET_DATA(prefs_w, E_CAPTURE_PAGE_KEY));
1301 #endif /* HAVE_LIBPCAP */
1302 nameres_prefs_destroy(OBJECT_GET_DATA(prefs_w, E_NAMERES_PAGE_KEY));
1304 /* Free up the saved preferences (both for "prefs" and for registered
1306 free_prefs(&saved_prefs);
1307 prefs_modules_foreach(module_prefs_clean, NULL);
1309 /* Note that we no longer have a "Preferences" dialog box. */
1313 struct properties_data {
1319 module_search_properties(module_t *module, gpointer user_data)
1321 struct properties_data *p = (struct properties_data *)user_data;
1323 /* If this module has the specified title, remember it. */
1324 if (strcmp(module->title, p->title) == 0)
1329 properties_cb(GtkWidget *w, gpointer dummy)
1331 header_field_info *hfinfo;
1333 struct properties_data p;
1336 module_t *page_module;
1338 if (cfile.finfo_selected == NULL) {
1339 /* There is no field selected */
1343 /* Find the title for the protocol for the selected field. */
1344 hfinfo = cfile.finfo_selected->hfinfo;
1345 if (hfinfo->parent == -1)
1346 title = prefs_get_title_by_name(hfinfo->abbrev);
1348 title = prefs_get_title_by_name(proto_registrar_get_abbrev(hfinfo->parent));
1350 return; /* Couldn't find it. XXX - just crash? "Can't happen"? */
1352 /* Find the module for that protocol by searching for one with that title.
1353 XXX - should we just associate protocols with modules directly? */
1356 prefs_module_list_foreach(protocols_module->prefs, module_search_properties,
1358 if (p.module == NULL) {
1359 /* We didn't find it - that protocol probably has no preferences. */
1363 /* Create a preferences window, or pop up an existing one. */
1364 if (prefs_w != NULL) {
1365 reactivate_window(prefs_w);
1370 /* Search all the pages in that window for the one with the specified
1373 (frame = gtk_notebook_get_nth_page(GTK_NOTEBOOK(notebook), page_num)) != NULL;
1375 /* Get the module for this page. */
1376 page_module = OBJECT_GET_DATA(frame, E_PAGE_MODULE_KEY);
1377 if (page_module == NULL)
1378 continue; /* It doesn't have one. */
1379 if (page_module == p.module) {
1380 /* We found it. Select that page. */
1381 gtk_notebook_set_page(GTK_NOTEBOOK(notebook), page_num);
1387 /* Prefs tree selection callback. The node data has been loaded with
1388 the proper notebook page to load. */
1389 #if GTK_MAJOR_VERSION < 2
1391 prefs_tree_select_cb(GtkCTree *ct, GtkCTreeNode *node, gint col _U_,
1395 prefs_tree_select_cb(GtkTreeSelection *sel, gpointer dummy _U_)
1399 #if GTK_MAJOR_VERSION >= 2
1400 GtkTreeModel *model;
1404 #if GTK_MAJOR_VERSION < 2
1405 page = GPOINTER_TO_INT(gtk_ctree_node_get_row_data(ct, node));
1408 gtk_notebook_set_page(GTK_NOTEBOOK(notebook), page);
1410 if (gtk_tree_selection_get_selected(sel, &model, &iter))
1412 gtk_tree_model_get(model, &iter, 1, &page, -1);
1414 gtk_notebook_set_page(GTK_NOTEBOOK(notebook), page);