Turn the code of "colorize_packet()" into a static routine that is given
[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.15 2000/07/09 03:29:42 guy Exp $
5  *
6  * Ethereal - Network traffic analyzer
7  * By Gerald Combs <gerald@zing.org>
8  * Copyright 1998 Gerald Combs
9  *
10  * 
11  * This program is free software; you can redistribute it and/or
12  * modify it under the terms of the GNU General Public License
13  * as published by the Free Software Foundation; either version 2
14  * of the License, or (at your option) any later version.
15  * 
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  * GNU General Public License for more details.
20  * 
21  * You should have received a copy of the GNU General Public License
22  * along with this program; if not, write to the Free Software
23  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
24  */
25
26 #ifdef HAVE_CONFIG_H
27 # include "config.h"
28 #endif
29
30 #ifdef HAVE_SYS_TYPES_H
31 #include <sys/types.h>
32 #endif
33
34 #include <gtk/gtk.h>
35
36 #include <stdlib.h>
37 #include <string.h>
38 #include <ctype.h>
39 #include <errno.h>
40
41 #ifdef HAVE_UNISTD_H
42 #include <unistd.h>
43 #endif
44
45 #include <sys/stat.h>
46
47 #include "main.h"
48 #include "packet.h"
49 #include "file.h"
50 #include "prefs.h"
51 #include "column_prefs.h"
52 #include "print.h"
53 #include "prefs_dlg.h"
54 #include "print_prefs.h"
55 #include "stream_prefs.h"
56 #include "gui_prefs.h"
57 #include "util.h"
58 #include "ui_util.h"
59 #include "dlg_utils.h"
60 #include "simple_dialog.h"
61
62 #include "prefs-int.h"
63
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);
69
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"
74
75 /*
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
79  * creating a new one.
80  */
81 static GtkWidget *prefs_w;
82
83 static void
84 pref_show(pref_t *pref, gpointer user_data)
85 {
86   GtkWidget *main_tb = user_data;
87   const char *title;
88   char *label_string;
89   GtkWidget *label, *menu, *menu_item, *widget, *button;
90   GSList *rb_group;
91   char uint_str[10+1];
92   const enum_val *enum_valp;
93   int menu_index, index;
94
95   /* Give this preference a label which is its title, followed by a colon,
96      and left-align it. */
97   title = pref->title;
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);
104
105   /* Attach it to the table. */
106   gtk_table_attach_defaults(GTK_TABLE(main_tb), label, 0, 1, pref->ordinal,
107                                 pref->ordinal+1);
108
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) {
113
114   case PREF_UINT:
115     pref->saved_val.uint = *pref->varp.uint;
116
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
120        use that. */
121     widget = gtk_entry_new();
122     switch (pref->info.base) {
123
124     case 10:
125       sprintf(uint_str, "%u", pref->saved_val.uint);
126       break;
127
128     case 8:
129       sprintf(uint_str, "%o", pref->saved_val.uint);
130       break;
131
132     case 16:
133       sprintf(uint_str, "%x", pref->saved_val.uint);
134       break;
135     }
136     gtk_entry_set_text(GTK_ENTRY(widget), uint_str);
137     pref->control = widget;
138     break;
139
140   case PREF_BOOL:
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;
145     break;
146
147   case PREF_ENUM:
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);
152       rb_group = NULL;
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;
162       }
163     } else {
164       /* Show it as an option menu. */
165       menu = gtk_menu_new();
166       menu_index = -1;
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)
172           menu_index = index;
173       }
174
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);
178
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;
183     }
184     break;
185
186   case PREF_STRING:
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;
193     break;
194
195   default:
196     g_assert_not_reached();
197     widget = NULL;
198     break;
199   }
200
201   gtk_table_attach_defaults(GTK_TABLE(main_tb), widget, 1, 2, pref->ordinal,
202                                 pref->ordinal+1);
203 }
204
205 static void
206 module_prefs_show(module_t *module, gpointer user_data)
207 {
208   GtkWidget *prefs_nb = user_data;
209   GtkWidget *main_vb, *main_tb, *label;
210
211   /* Main vertical box */
212   main_vb = gtk_vbox_new(FALSE, 5);
213   gtk_container_border_width(GTK_CONTAINER(main_vb), 5);
214
215   /* Main table */
216   main_tb = gtk_table_new(module->numprefs, 2, FALSE);
217   gtk_box_pack_start(GTK_BOX(main_vb), main_tb, FALSE, FALSE, 0);
218   gtk_table_set_row_spacings(GTK_TABLE(main_tb), 10);
219   gtk_table_set_col_spacings(GTK_TABLE(main_tb), 15);
220
221   /* Add items for each of the preferences */
222   prefs_pref_foreach(module, pref_show, main_tb);
223
224   label = gtk_label_new(module->title);
225   gtk_notebook_append_page(GTK_NOTEBOOK(prefs_nb), main_vb, label);
226
227   /* Show 'em what we got */
228   gtk_widget_show_all(main_vb);
229 }
230
231 void
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;
236
237   if (prefs_w != NULL) {
238     /* There's already a "Preferences" dialog box; reactivate it. */
239     reactivate_window(prefs_w);
240     return;
241   }
242
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);
249   
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);
255   
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);
260   
261   prefs_nb = gtk_notebook_new();
262   gtk_container_add(GTK_CONTAINER(main_vb), prefs_nb);
263   gtk_widget_show(prefs_nb);
264   
265   /* Printing prefs */
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);
270     
271   /* Column prefs */
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);
276   
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);
282
283   /* GUI prefs */
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);
288
289   /* Registered prefs */
290   prefs_module_foreach(module_prefs_show, prefs_nb);
291
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);
298   
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);
306
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);
313   
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);
320
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
323      been selected. */
324   dlg_set_cancel(prefs_w, cancel_bt);
325
326   gtk_widget_show(prefs_w);
327 }
328
329 static void
330 pref_fetch(pref_t *pref, gpointer user_data)
331 {
332   GtkWidget *label;
333   char *label_string;
334   char *str_val;
335   char *p;
336   guint uval;
337   gboolean bval;
338   GSList *rb_entry;
339   GtkWidget *button;
340   gint enumval;
341   gboolean *pref_changed_p = user_data;
342
343   /* Fetch the value of the preference, and set the appropriate variable
344      to it. */
345   switch (pref->type) {
346
347   case PREF_UINT:
348     str_val = gtk_entry_get_text(GTK_ENTRY(pref->control));
349     uval = strtoul(str_val, &p, pref->info.base);
350 #if 0
351     if (p == value || *p != '\0')
352       return PREFS_SET_SYNTAX_ERR;      /* number was bad */
353 #endif
354     if (*pref->varp.uint != uval) {
355       *pref_changed_p = TRUE;
356       *pref->varp.uint = uval;
357     }
358     break;
359
360   case PREF_BOOL:
361     bval = GTK_TOGGLE_BUTTON(pref->control)->active;
362     if (*pref->varp.bool != bval) {
363       *pref_changed_p = TRUE;
364       *pref->varp.bool = bval;
365     }
366     break;
367
368   case PREF_ENUM:
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. */
372       button = NULL;
373       for (rb_entry = gtk_radio_button_group(GTK_RADIO_BUTTON(pref->control));
374                 rb_entry != NULL;
375                 rb_entry = g_slist_next(rb_entry)) {
376         button = rb_entry->data;
377         if (GTK_TOGGLE_BUTTON(button)->active)
378           break;
379       }
380       /* OK, now find that button's label. */
381       label = GTK_BIN(button)->child;
382     } else {
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;
386     }
387
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;
395     }
396     break;
397
398   case PREF_STRING:
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);
405     }
406     break;
407   }
408 }
409
410 static void
411 module_prefs_fetch(module_t *module, gpointer user_data)
412 {
413   gboolean *must_redissect_p = user_data;
414
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);
419
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;
425 }
426
427 static void
428 pref_clean(pref_t *pref, gpointer user_data)
429 {
430   switch (pref->type) {
431
432   case PREF_UINT:
433     break;
434
435   case PREF_BOOL:
436     break;
437
438   case PREF_ENUM:
439     break;
440
441   case PREF_STRING:
442     if (pref->saved_val.string != NULL) {
443       g_free(pref->saved_val.string);
444       pref->saved_val.string = NULL;
445     }
446     break;
447   }
448 }
449
450 static void
451 module_prefs_clean(module_t *module, gpointer user_data)
452 {
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);
456 }
457
458 static void
459 prefs_main_ok_cb(GtkWidget *ok_bt, gpointer parent_w)
460 {
461   gboolean must_redissect = FALSE;
462
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);
468   prefs_apply_all();
469   prefs_module_foreach(module_prefs_clean, NULL);
470   gtk_widget_destroy(GTK_WIDGET(parent_w));
471
472   if (must_redissect) {
473     /* Redissect all the packets, and re-evaluate the display filter. */
474     redissect_packets(&cfile);
475   }
476 }
477
478 static void
479 prefs_main_save_cb(GtkWidget *save_bt, gpointer parent_w)
480 {
481   int err;
482   char *pf_path;
483
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);
490   if (err != 0) {
491      simple_dialog(ESD_TYPE_WARN, NULL,
492       "Can't open preferences file\n\"%s\": %s.", pf_path,
493       strerror(err));
494   }
495 }
496
497 static void
498 pref_revert(pref_t *pref, gpointer user_data)
499 {
500   /* Fetch the value of the preference, and set the appropriate variable
501      to it. */
502   switch (pref->type) {
503
504   case PREF_UINT:
505     *pref->varp.uint = pref->saved_val.uint;
506     break;
507
508   case PREF_BOOL:
509     *pref->varp.bool = pref->saved_val.bool;
510     break;
511
512   case PREF_ENUM:
513     *pref->varp.enump = pref->saved_val.enumval;
514     break;
515
516   case PREF_STRING:
517     if (*pref->varp.string != NULL)
518       g_free(*pref->varp.string);
519     *pref->varp.string = g_strdup(pref->saved_val.string);
520     break;
521   }
522 }
523
524 static void
525 module_prefs_revert(module_t *module, gpointer user_data)
526 {
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);
530 }
531
532 static void
533 prefs_main_cancel_cb(GtkWidget *cancel_bt, gpointer parent_w)
534 {
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));
541 }
542
543 static gboolean
544 prefs_main_delete_cb(GtkWidget *prefs_w, gpointer dummy)
545 {
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));
550   return FALSE;
551 }
552
553 static void
554 prefs_main_destroy_cb(GtkWidget *win, gpointer user_data)
555 {
556   /* XXX - call the delete callback?  Or move its stuff here and
557      get rid of it? */
558
559   /* Note that we no longer have a "Preferences" dialog box. */
560   prefs_w = NULL;
561 }