2 * Routines for handling preferences
4 * $Id: prefs_dlg.c,v 1.15 2000/07/09 03:29:42 guy Exp $
6 * Ethereal - Network traffic analyzer
7 * By Gerald Combs <gerald@zing.org>
8 * Copyright 1998 Gerald Combs
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.
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.
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.
30 #ifdef HAVE_SYS_TYPES_H
31 #include <sys/types.h>
51 #include "column_prefs.h"
53 #include "prefs_dlg.h"
54 #include "print_prefs.h"
55 #include "stream_prefs.h"
56 #include "gui_prefs.h"
59 #include "dlg_utils.h"
60 #include "simple_dialog.h"
62 #include "prefs-int.h"
64 static void prefs_main_ok_cb(GtkWidget *, gpointer);
65 static void prefs_main_save_cb(GtkWidget *, gpointer);
66 static void prefs_main_cancel_cb(GtkWidget *, gpointer);
67 static gboolean prefs_main_delete_cb(GtkWidget *, gpointer);
68 static void prefs_main_destroy_cb(GtkWidget *, gpointer);
70 #define E_PRINT_PAGE_KEY "printer_options_page"
71 #define E_COLUMN_PAGE_KEY "column_options_page"
72 #define E_STREAM_PAGE_KEY "tcp_stream_options_page"
73 #define E_GUI_PAGE_KEY "gui_options_page"
76 * Keep a static pointer to the current "Preferences" window, if any, so that
77 * if somebody tries to do "Edit:Preferences" while there's already a
78 * "Preferences" window up, we just pop up the existing one, rather than
81 static GtkWidget *prefs_w;
84 pref_show(pref_t *pref, gpointer user_data)
86 GtkWidget *main_tb = user_data;
89 GtkWidget *label, *menu, *menu_item, *widget, *button;
92 const enum_val *enum_valp;
93 int menu_index, index;
95 /* Give this preference a label which is its title, followed by a colon,
98 label_string = g_malloc(strlen(title) + 2);
99 strcpy(label_string, title);
100 strcat(label_string, ":");
101 label = gtk_label_new(label_string);
102 g_free(label_string);
103 gtk_misc_set_alignment(GTK_MISC(label), 1.0, 0.5);
105 /* Attach it to the table. */
106 gtk_table_attach_defaults(GTK_TABLE(main_tb), label, 0, 1, pref->ordinal,
109 /* Save the current value of the preference, so that we can revert it if
110 the user does "Apply" and then "Cancel", and create the control for
111 editing the preference. */
112 switch (pref->type) {
115 pref->saved_val.uint = *pref->varp.uint;
117 /* XXX - there are no uint spinbuttons, so we can't use a spinbutton.
118 Even more annoyingly, even if there were, GLib doesn't define
119 G_MAXUINT - but I think ANSI C may define UINT_MAX, so we could
121 widget = gtk_entry_new();
122 switch (pref->info.base) {
125 sprintf(uint_str, "%u", pref->saved_val.uint);
129 sprintf(uint_str, "%o", pref->saved_val.uint);
133 sprintf(uint_str, "%x", pref->saved_val.uint);
136 gtk_entry_set_text(GTK_ENTRY(widget), uint_str);
137 pref->control = widget;
141 pref->saved_val.bool = *pref->varp.bool;
142 widget = gtk_check_button_new();
143 gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(widget), pref->saved_val.bool);
144 pref->control = widget;
148 pref->saved_val.enumval = *pref->varp.enump;
149 if (pref->info.enum_info.radio_buttons) {
150 /* Show it as radio buttons. */
151 widget = gtk_hbox_new(FALSE, 0);
153 for (enum_valp = pref->info.enum_info.enumvals, index = 0;
154 enum_valp->name != NULL; enum_valp++, index++) {
155 button = gtk_radio_button_new_with_label(rb_group, enum_valp->name);
156 if (rb_group == NULL)
157 rb_group = gtk_radio_button_group(GTK_RADIO_BUTTON(button));
158 gtk_box_pack_start(GTK_BOX(widget), button, FALSE, FALSE, 10);
159 if (enum_valp->value == pref->saved_val.enumval)
160 gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(button), TRUE);
161 pref->control = button;
164 /* Show it as an option menu. */
165 menu = gtk_menu_new();
167 for (enum_valp = pref->info.enum_info.enumvals, index = 0;
168 enum_valp->name != NULL; enum_valp++, index++) {
169 menu_item = gtk_menu_item_new_with_label(enum_valp->name);
170 gtk_menu_append(GTK_MENU(menu), menu_item);
171 if (enum_valp->value == pref->saved_val.enumval)
175 /* Create the option menu from the option */
176 widget = gtk_option_menu_new();
177 gtk_option_menu_set_menu(GTK_OPTION_MENU(widget), menu);
179 /* Set its current value to the variable's current value */
180 if (menu_index != -1)
181 gtk_option_menu_set_history(GTK_OPTION_MENU(widget), menu_index);
182 pref->control = widget;
187 widget = gtk_entry_new();
188 if (pref->saved_val.string != NULL)
189 g_free(pref->saved_val.string);
190 pref->saved_val.string = g_strdup(*pref->varp.string);
191 gtk_entry_set_text(GTK_ENTRY(widget), pref->saved_val.string);
192 pref->control = widget;
196 g_assert_not_reached();
201 gtk_table_attach_defaults(GTK_TABLE(main_tb), widget, 1, 2, pref->ordinal,
206 module_prefs_show(module_t *module, gpointer user_data)
208 GtkWidget *prefs_nb = user_data;
209 GtkWidget *main_vb, *main_tb, *label;
211 /* Main vertical box */
212 main_vb = gtk_vbox_new(FALSE, 5);
213 gtk_container_border_width(GTK_CONTAINER(main_vb), 5);
216 main_tb = gtk_table_new(module->numprefs, 2, FALSE);
217 gtk_box_pack_start(GTK_BOX(main_vb), main_tb, FALSE, FALSE, 0);
218 gtk_table_set_row_spacings(GTK_TABLE(main_tb), 10);
219 gtk_table_set_col_spacings(GTK_TABLE(main_tb), 15);
221 /* Add items for each of the preferences */
222 prefs_pref_foreach(module, pref_show, main_tb);
224 label = gtk_label_new(module->title);
225 gtk_notebook_append_page(GTK_NOTEBOOK(prefs_nb), main_vb, label);
227 /* Show 'em what we got */
228 gtk_widget_show_all(main_vb);
232 prefs_cb(GtkWidget *w, gpointer dummy) {
233 GtkWidget *main_vb, *top_hb, *bbox, *prefs_nb,
234 *ok_bt, *save_bt, *cancel_bt;
235 GtkWidget *print_pg, *column_pg, *stream_pg, *gui_pg, *label;
237 if (prefs_w != NULL) {
238 /* There's already a "Preferences" dialog box; reactivate it. */
239 reactivate_window(prefs_w);
243 prefs_w = dlg_window_new();
244 gtk_window_set_title(GTK_WINDOW(prefs_w), "Ethereal: Preferences");
245 gtk_signal_connect(GTK_OBJECT(prefs_w), "delete-event",
246 GTK_SIGNAL_FUNC(prefs_main_delete_cb), NULL);
247 gtk_signal_connect(GTK_OBJECT(prefs_w), "destroy",
248 GTK_SIGNAL_FUNC(prefs_main_destroy_cb), NULL);
250 /* Container for each row of widgets */
251 main_vb = gtk_vbox_new(FALSE, 5);
252 gtk_container_border_width(GTK_CONTAINER(main_vb), 5);
253 gtk_container_add(GTK_CONTAINER(prefs_w), main_vb);
254 gtk_widget_show(main_vb);
256 /* Top row: Preferences notebook */
257 top_hb = gtk_hbox_new(FALSE, 1);
258 gtk_container_add(GTK_CONTAINER(main_vb), top_hb);
259 gtk_widget_show(top_hb);
261 prefs_nb = gtk_notebook_new();
262 gtk_container_add(GTK_CONTAINER(main_vb), prefs_nb);
263 gtk_widget_show(prefs_nb);
266 print_pg = printer_prefs_show();
267 gtk_object_set_data(GTK_OBJECT(prefs_w), E_PRINT_PAGE_KEY, print_pg);
268 label = gtk_label_new ("Printing");
269 gtk_notebook_append_page (GTK_NOTEBOOK(prefs_nb), print_pg, label);
272 column_pg = column_prefs_show();
273 gtk_object_set_data(GTK_OBJECT(prefs_w), E_COLUMN_PAGE_KEY, column_pg);
274 label = gtk_label_new ("Columns");
275 gtk_notebook_append_page (GTK_NOTEBOOK(prefs_nb), column_pg, label);
277 /* TCP Streams prefs */
278 stream_pg = stream_prefs_show();
279 gtk_object_set_data(GTK_OBJECT(prefs_w), E_STREAM_PAGE_KEY, stream_pg);
280 label = gtk_label_new ("TCP Streams");
281 gtk_notebook_append_page (GTK_NOTEBOOK(prefs_nb), stream_pg, label);
284 gui_pg = gui_prefs_show();
285 gtk_object_set_data(GTK_OBJECT(prefs_w), E_GUI_PAGE_KEY, gui_pg);
286 label = gtk_label_new ("GUI");
287 gtk_notebook_append_page (GTK_NOTEBOOK(prefs_nb), gui_pg, label);
289 /* Registered prefs */
290 prefs_module_foreach(module_prefs_show, prefs_nb);
292 /* Button row: OK and cancel buttons */
293 bbox = gtk_hbutton_box_new();
294 gtk_button_box_set_layout (GTK_BUTTON_BOX (bbox), GTK_BUTTONBOX_END);
295 gtk_button_box_set_spacing(GTK_BUTTON_BOX(bbox), 5);
296 gtk_container_add(GTK_CONTAINER(main_vb), bbox);
297 gtk_widget_show(bbox);
299 ok_bt = gtk_button_new_with_label ("OK");
300 gtk_signal_connect(GTK_OBJECT(ok_bt), "clicked",
301 GTK_SIGNAL_FUNC(prefs_main_ok_cb), GTK_OBJECT(prefs_w));
302 GTK_WIDGET_SET_FLAGS(ok_bt, GTK_CAN_DEFAULT);
303 gtk_box_pack_start (GTK_BOX (bbox), ok_bt, TRUE, TRUE, 0);
304 gtk_widget_grab_default(ok_bt);
305 gtk_widget_show(ok_bt);
307 save_bt = gtk_button_new_with_label ("Save");
308 gtk_signal_connect(GTK_OBJECT(save_bt), "clicked",
309 GTK_SIGNAL_FUNC(prefs_main_save_cb), GTK_OBJECT(prefs_w));
310 GTK_WIDGET_SET_FLAGS(save_bt, GTK_CAN_DEFAULT);
311 gtk_box_pack_start (GTK_BOX (bbox), save_bt, TRUE, TRUE, 0);
312 gtk_widget_show(save_bt);
314 cancel_bt = gtk_button_new_with_label ("Cancel");
315 gtk_signal_connect(GTK_OBJECT(cancel_bt), "clicked",
316 GTK_SIGNAL_FUNC(prefs_main_cancel_cb), GTK_OBJECT(prefs_w));
317 GTK_WIDGET_SET_FLAGS(cancel_bt, GTK_CAN_DEFAULT);
318 gtk_box_pack_start (GTK_BOX (bbox), cancel_bt, TRUE, TRUE, 0);
319 gtk_widget_show(cancel_bt);
321 /* Catch the "key_press_event" signal in the window, so that we can catch
322 the ESC key being pressed and act as if the "Cancel" button had
324 dlg_set_cancel(prefs_w, cancel_bt);
326 gtk_widget_show(prefs_w);
330 pref_fetch(pref_t *pref, gpointer user_data)
341 gboolean *pref_changed_p = user_data;
343 /* Fetch the value of the preference, and set the appropriate variable
345 switch (pref->type) {
348 str_val = gtk_entry_get_text(GTK_ENTRY(pref->control));
349 uval = strtoul(str_val, &p, pref->info.base);
351 if (p == value || *p != '\0')
352 return PREFS_SET_SYNTAX_ERR; /* number was bad */
354 if (*pref->varp.uint != uval) {
355 *pref_changed_p = TRUE;
356 *pref->varp.uint = uval;
361 bval = GTK_TOGGLE_BUTTON(pref->control)->active;
362 if (*pref->varp.bool != bval) {
363 *pref_changed_p = TRUE;
364 *pref->varp.bool = bval;
369 if (pref->info.enum_info.radio_buttons) {
370 /* Go through the list of of radio buttons in the group, and find
371 the first one that's active. */
373 for (rb_entry = gtk_radio_button_group(GTK_RADIO_BUTTON(pref->control));
375 rb_entry = g_slist_next(rb_entry)) {
376 button = rb_entry->data;
377 if (GTK_TOGGLE_BUTTON(button)->active)
380 /* OK, now find that button's label. */
381 label = GTK_BIN(button)->child;
383 /* Get the label for the currently active entry in the option menu.
384 Yes, this is how you do it. See FAQ 6.8 in the GTK+ FAQ. */
385 label = GTK_BIN(pref->control)->child;
388 /* Get the label, and translate it to a value. */
389 gtk_label_get(GTK_LABEL(label), &label_string);
390 enumval = find_val_for_string(label_string,
391 pref->info.enum_info.enumvals, 1);
392 if (*pref->varp.enump != enumval) {
393 *pref_changed_p = TRUE;
394 *pref->varp.enump = enumval;
399 str_val = gtk_entry_get_text(GTK_ENTRY(pref->control));
400 if (*pref->varp.string == NULL || strcmp(*pref->varp.string, str_val) != 0) {
401 *pref_changed_p = TRUE;
402 if (*pref->varp.string != NULL)
403 g_free(*pref->varp.string);
404 *pref->varp.string = g_strdup(str_val);
411 module_prefs_fetch(module_t *module, gpointer user_data)
413 gboolean *must_redissect_p = user_data;
415 /* For all preferences in this module, fetch its value from this
416 module's notebook page. Find out whether any of them changed. */
417 module->prefs_changed = FALSE; /* assume none of them changed */
418 prefs_pref_foreach(module, pref_fetch, &module->prefs_changed);
420 /* If any of them changed, indicate that we must redissect and refilter
421 the current capture (if we have one), as the preference change
422 could cause packets to be dissected differently. */
423 if (module->prefs_changed)
424 *must_redissect_p = TRUE;
428 pref_clean(pref_t *pref, gpointer user_data)
430 switch (pref->type) {
442 if (pref->saved_val.string != NULL) {
443 g_free(pref->saved_val.string);
444 pref->saved_val.string = NULL;
451 module_prefs_clean(module_t *module, gpointer user_data)
453 /* For all preferences in this module, clean up any cruft allocated for
454 use by the GUI code. */
455 prefs_pref_foreach(module, pref_clean, NULL);
459 prefs_main_ok_cb(GtkWidget *ok_bt, gpointer parent_w)
461 gboolean must_redissect = FALSE;
463 printer_prefs_ok(gtk_object_get_data(GTK_OBJECT(parent_w), E_PRINT_PAGE_KEY));
464 column_prefs_ok(gtk_object_get_data(GTK_OBJECT(parent_w), E_COLUMN_PAGE_KEY));
465 stream_prefs_ok(gtk_object_get_data(GTK_OBJECT(parent_w), E_STREAM_PAGE_KEY));
466 gui_prefs_ok(gtk_object_get_data(GTK_OBJECT(parent_w), E_GUI_PAGE_KEY));
467 prefs_module_foreach(module_prefs_fetch, &must_redissect);
469 prefs_module_foreach(module_prefs_clean, NULL);
470 gtk_widget_destroy(GTK_WIDGET(parent_w));
472 if (must_redissect) {
473 /* Redissect all the packets, and re-evaluate the display filter. */
474 redissect_packets(&cfile);
479 prefs_main_save_cb(GtkWidget *save_bt, gpointer parent_w)
484 printer_prefs_save(gtk_object_get_data(GTK_OBJECT(parent_w), E_PRINT_PAGE_KEY));
485 column_prefs_save(gtk_object_get_data(GTK_OBJECT(parent_w), E_COLUMN_PAGE_KEY));
486 stream_prefs_save(gtk_object_get_data(GTK_OBJECT(parent_w), E_STREAM_PAGE_KEY));
487 gui_prefs_save(gtk_object_get_data(GTK_OBJECT(parent_w), E_GUI_PAGE_KEY));
488 prefs_module_foreach(module_prefs_fetch, NULL);
489 err = write_prefs(&pf_path);
491 simple_dialog(ESD_TYPE_WARN, NULL,
492 "Can't open preferences file\n\"%s\": %s.", pf_path,
498 pref_revert(pref_t *pref, gpointer user_data)
500 /* Fetch the value of the preference, and set the appropriate variable
502 switch (pref->type) {
505 *pref->varp.uint = pref->saved_val.uint;
509 *pref->varp.bool = pref->saved_val.bool;
513 *pref->varp.enump = pref->saved_val.enumval;
517 if (*pref->varp.string != NULL)
518 g_free(*pref->varp.string);
519 *pref->varp.string = g_strdup(pref->saved_val.string);
525 module_prefs_revert(module_t *module, gpointer user_data)
527 /* For all preferences in this module, revert its value to the value
528 it had when we popped up the Preferences dialog. */
529 prefs_pref_foreach(module, pref_revert, NULL);
533 prefs_main_cancel_cb(GtkWidget *cancel_bt, gpointer parent_w)
535 printer_prefs_cancel(gtk_object_get_data(GTK_OBJECT(parent_w), E_PRINT_PAGE_KEY));
536 column_prefs_cancel(gtk_object_get_data(GTK_OBJECT(parent_w), E_COLUMN_PAGE_KEY));
537 stream_prefs_cancel(gtk_object_get_data(GTK_OBJECT(parent_w), E_STREAM_PAGE_KEY));
538 gui_prefs_cancel(gtk_object_get_data(GTK_OBJECT(parent_w), E_GUI_PAGE_KEY));
539 prefs_module_foreach(module_prefs_revert, NULL);
540 gtk_widget_destroy(GTK_WIDGET(parent_w));
544 prefs_main_delete_cb(GtkWidget *prefs_w, gpointer dummy)
546 printer_prefs_delete(gtk_object_get_data(GTK_OBJECT(prefs_w), E_PRINT_PAGE_KEY));
547 column_prefs_delete(gtk_object_get_data(GTK_OBJECT(prefs_w), E_COLUMN_PAGE_KEY));
548 stream_prefs_delete(gtk_object_get_data(GTK_OBJECT(prefs_w), E_STREAM_PAGE_KEY));
549 gui_prefs_delete(gtk_object_get_data(GTK_OBJECT(prefs_w), E_GUI_PAGE_KEY));
554 prefs_main_destroy_cb(GtkWidget *win, gpointer user_data)
556 /* XXX - call the delete callback? Or move its stuff here and
559 /* Note that we no longer have a "Preferences" dialog box. */