Add support for modal message boxes ("simple dialog").
[metze/wireshark/wip.git] / gtk / gui_prefs.c
1 /* gui_prefs.c
2  * Dialog box for GUI preferences
3  *
4  * $Id: gui_prefs.c,v 1.21 2000/10/09 06:38:36 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 #include <errno.h>
31
32 #include "globals.h"
33 #include "gui_prefs.h"
34 #include "gtkglobals.h"
35 #include "prefs_dlg.h"
36 #include "follow_dlg.h"
37 #include "help_dlg.h"
38 #include "prefs.h"
39 #include "prefs-int.h"
40 #include "ui_util.h"
41 #include "simple_dialog.h"
42 #include "dlg_utils.h"
43 #include "proto_draw.h"
44 #include "main.h"
45
46 static void create_option_menu(GtkWidget *main_vb, const gchar *key,
47     GtkWidget *main_tb, int table_position,
48     const gchar *label_text, const enum_val *enumvals, gint current_val);
49 static void font_browse_cb(GtkWidget *w, gpointer data);
50 static void font_browse_ok_cb(GtkWidget *w, GtkFontSelectionDialog *fs);
51 static void font_browse_destroy(GtkWidget *win, gpointer data);
52 static gint fetch_enum_value(gpointer control, const enum_val *enumvals);
53 static void color_browse_cb(GtkWidget *w, gpointer data);
54 static void update_text_color(GtkWidget *w, gpointer data);
55 static void update_current_color(GtkWidget *w, gpointer data);
56 static void color_ok_cb(GtkWidget *w, gpointer data);
57 static void color_cancel_cb(GtkWidget *w, gpointer data);
58 static gboolean color_delete_cb(GtkWidget *prefs_w, gpointer dummy);
59 static void color_destroy_cb(GtkWidget *w, gpointer data);
60 static void fetch_colors(void);
61
62 #define SCROLLBAR_PLACEMENT_KEY         "scrollbar_placement"
63 #define PLIST_SEL_BROWSE_KEY            "plist_sel_browse"
64 #define PTREE_SEL_BROWSE_KEY            "ptree_sel_browse"
65 #define PTREE_LINE_STYLE_KEY            "ptree_line_style"
66 #define PTREE_EXPANDER_STYLE_KEY        "ptree_expander_style"
67 #define HEX_DUMP_HIGHLIGHT_STYLE_KEY    "hex_dump_highlight_style"
68
69 #define FONT_DIALOG_PTR_KEY     "font_dialog_ptr"
70 #define FONT_CALLER_PTR_KEY     "font_caller_ptr"
71 #define COLOR_DIALOG_PTR_KEY    "color_dialog_ptr"
72 #define COLOR_CALLER_PTR_KEY    "color_caller_ptr"
73 #define COLOR_SAMPLE_PTR_KEY    "color_sample_ptr"
74 #define COLOR_SELECTION_PTR_KEY "color_selection_ptr"
75
76 static const enum_val scrollbar_placement_vals[] = {
77         { "Left",  FALSE },
78         { "Right", TRUE },
79         { NULL,    0 }
80 };
81
82 static const enum_val selection_mode_vals[] = {
83         { "Selects", FALSE },
84         { "Browses", TRUE },
85         { NULL,      0 }
86 };
87
88 static const enum_val line_style_vals[] = {
89         { "None",   0 },
90         { "Solid",  1 },
91         { "Dotted", 2 },
92         { "Tabbed", 3 },
93         { NULL,     0 }
94 };
95
96 static const enum_val expander_style_vals[] = {
97         { "None",     0 },
98         { "Square",   1 },
99         { "Triangle", 2 },
100         { "Circular", 3 },
101         { NULL,       0 }
102 };
103
104 static const enum_val highlight_style_vals[] = {
105         { "Bold",     0 },
106         { "Inverse",  1 },
107         { NULL,       0 }
108 };
109
110 /* Set to FALSE initially; set to TRUE if the user ever hits "OK" on
111    the "Colors..." dialog, so that we know that they (probably) changed
112    colors, and therefore that the "apply" function needs to recolor
113    any marked packets. */
114 static gboolean colors_changed;
115
116 /* Set to FALSE initially; set to TRUE if the user ever hits "OK" on
117    the "Font..." dialog, so that we know that they (probably) changed
118    the font, and therefore that the "apply" function needs to take care
119    of that */
120 static gboolean font_changed;
121
122 /* Font name from the font dialog box; if "font_changed" is TRUE, this
123    has been set to the name of the font the user selected. */
124 static gchar *new_font_name;
125
126 GtkWidget*
127 gui_prefs_show(void)
128 {
129         GtkWidget       *main_tb, *main_vb, *font_bt, *color_bt;
130
131         /* The colors or font haven't been changed yet. */
132         colors_changed = FALSE;
133         font_changed = FALSE;
134
135         /* Main vertical box */
136         main_vb = gtk_vbox_new(FALSE, 7);
137         gtk_container_border_width( GTK_CONTAINER(main_vb), 5 );
138
139         /* Main table */
140         main_tb = gtk_table_new(7, 2, FALSE);
141         gtk_box_pack_start( GTK_BOX(main_vb), main_tb, FALSE, FALSE, 0 );
142         gtk_table_set_row_spacings( GTK_TABLE(main_tb), 10 );
143         gtk_table_set_col_spacings( GTK_TABLE(main_tb), 15 );
144
145         /* Scrollbar placement */
146         create_option_menu(main_vb, SCROLLBAR_PLACEMENT_KEY, main_tb, 0,
147             "Vertical Scrollbar Placement:", scrollbar_placement_vals,
148             prefs.gui_scrollbar_on_right);
149
150         /* Packet list selection browseable */
151         create_option_menu(main_vb, PLIST_SEL_BROWSE_KEY, main_tb, 1,
152             "Packet-list selection bar movement:", selection_mode_vals,
153             prefs.gui_plist_sel_browse);
154
155         /* Proto tree selection browseable */
156         create_option_menu(main_vb, PTREE_SEL_BROWSE_KEY, main_tb, 2,
157             "Protocol-tree selection bar movement:", selection_mode_vals,
158             prefs.gui_ptree_sel_browse);
159
160         /* Proto tree line style */
161         create_option_menu(main_vb, PTREE_LINE_STYLE_KEY, main_tb, 3,
162             "Protocol-tree line style:", line_style_vals,
163             prefs.gui_ptree_line_style);
164
165         /* Proto tree expander style */
166         create_option_menu(main_vb, PTREE_EXPANDER_STYLE_KEY, main_tb, 4,
167             "Protocol-tree expander style:", expander_style_vals,
168             prefs.gui_ptree_expander_style);
169
170         /* Hex Dump highlight style */
171         create_option_menu(main_vb, HEX_DUMP_HIGHLIGHT_STYLE_KEY, main_tb, 5,
172             "Hex dump highlight style:", highlight_style_vals,
173             prefs.gui_hex_dump_highlight_style);
174
175         /* "Font..." button - click to open a font selection dialog box. */
176         font_bt = gtk_button_new_with_label("Font...");
177         gtk_signal_connect(GTK_OBJECT(font_bt), "clicked",
178             GTK_SIGNAL_FUNC(font_browse_cb), NULL);
179         gtk_table_attach_defaults( GTK_TABLE(main_tb), font_bt, 1, 2, 6, 7 );
180
181         /* "Colors..." button - click to open a color selection dialog box. */
182         color_bt = gtk_button_new_with_label("Colors...");
183         gtk_signal_connect(GTK_OBJECT(color_bt), "clicked",
184             GTK_SIGNAL_FUNC(color_browse_cb), NULL);
185         gtk_table_attach_defaults( GTK_TABLE(main_tb), color_bt, 1, 2, 7, 8 );
186
187         /* Show 'em what we got */
188         gtk_widget_show_all(main_vb);
189
190         return(main_vb);
191 }
192
193 static void
194 create_option_menu(GtkWidget *main_vb, const gchar *key,
195     GtkWidget *main_tb, int table_position,
196     const gchar *label_text, const enum_val *enumvals, gint current_val)
197 {
198         GtkWidget *label, *menu, *menu_item, *option_menu;
199         int menu_index, index;
200         const enum_val *enum_valp;
201
202         label = gtk_label_new(label_text);
203         gtk_misc_set_alignment(GTK_MISC(label), 1.0, 0.5);
204         gtk_table_attach_defaults(GTK_TABLE(main_tb), label, 0, 1,
205             table_position, table_position + 1);
206
207         /* Create a menu from the enumvals */
208         menu = gtk_menu_new();
209         menu_index = -1;
210         for (enum_valp = enumvals, index = 0;
211             enum_valp->name != NULL; enum_valp++, index++) {
212                 menu_item = gtk_menu_item_new_with_label(enum_valp->name);
213                 gtk_menu_append(GTK_MENU(menu), menu_item);
214                 if (enum_valp->value == current_val)
215                         menu_index = index;
216                 gtk_widget_show(menu_item);
217         }
218
219         /* Create the option menu from the menu */
220         option_menu = gtk_option_menu_new();
221         gtk_option_menu_set_menu(GTK_OPTION_MENU(option_menu), menu);
222
223         /* Set its current value to the variable's current value */
224         if (menu_index != -1)
225                 gtk_option_menu_set_history(GTK_OPTION_MENU(option_menu),
226                     menu_index);
227
228         gtk_table_attach_defaults(GTK_TABLE(main_tb), option_menu,
229             1, 2, table_position, table_position + 1);
230
231         gtk_object_set_data(GTK_OBJECT(main_vb), key, option_menu);
232 }
233
234 /* Create a font dialog for browsing. */
235 static void
236 font_browse_cb(GtkWidget *w, gpointer data)
237 {
238         GtkWidget *caller = gtk_widget_get_toplevel(w);
239         GtkWidget *font_browse_w;
240         static gchar *fixedwidths[] = { "c", "m", NULL };
241
242         /* Has a font dialog box already been opened for that top-level
243            widget? */
244         font_browse_w = gtk_object_get_data(GTK_OBJECT(caller),
245             FONT_DIALOG_PTR_KEY);
246
247         if (font_browse_w != NULL) {
248                 /* Yes.  Just re-activate that dialog box. */
249                 reactivate_window(font_browse_w);
250                 return;
251         }
252
253         /* Now create a new dialog. */
254         font_browse_w = gtk_font_selection_dialog_new("Ethereal: Select Font");
255         gtk_window_set_transient_for(GTK_WINDOW(font_browse_w),
256             GTK_WINDOW(top_level));
257
258         /* Call a handler when we're destroyed, so we can inform
259            our caller, if any, that we've been destroyed. */
260         gtk_signal_connect(GTK_OBJECT(font_browse_w), "destroy",
261             GTK_SIGNAL_FUNC(font_browse_destroy), NULL);
262
263         /* Set its filter to show only fixed_width fonts. */
264         gtk_font_selection_dialog_set_filter(
265             GTK_FONT_SELECTION_DIALOG(font_browse_w),
266             GTK_FONT_FILTER_BASE,       /* user can't change the filter */
267             GTK_FONT_ALL,               /* bitmap or scalable are fine */
268             NULL,                       /* all foundries are OK */
269             NULL,                       /* all weights are OK (XXX - normal only?) */
270             NULL,                       /* all slants are OK (XXX - Roman only?) */
271             NULL,                       /* all setwidths are OK */
272             fixedwidths,                /* ONLY fixed-width fonts */
273             NULL);                      /* all charsets are OK (XXX - ISO 8859/1 only?) */
274
275         /* Set the font to the current font.
276            XXX - GTK+ 1.2.8, and probably earlier versions, have a bug
277            wherein that doesn't necessarily cause that font to be
278            selected in the dialog box.  I've sent to the GTK+ folk
279            a fix; hopefully, it'll show up in 1.2.9 if, as, and when
280            they put out a 1.2.9 release. */
281         gtk_font_selection_dialog_set_font_name(
282             GTK_FONT_SELECTION_DIALOG(font_browse_w), prefs.gui_font_name);
283
284         /* Set the FONT_CALLER_PTR_KEY for the new dialog to point to
285            our caller. */
286         gtk_object_set_data(GTK_OBJECT(font_browse_w), FONT_CALLER_PTR_KEY,
287             caller);
288
289         /* Set the FONT_DIALOG_PTR_KEY for the caller to point to us */
290         gtk_object_set_data(GTK_OBJECT(caller), FONT_DIALOG_PTR_KEY,
291             font_browse_w);
292   
293         /* Connect the ok_button to font_browse_ok_cb function and pass along a
294            pointer to the font selection box widget */
295         gtk_signal_connect(
296             GTK_OBJECT(GTK_FONT_SELECTION_DIALOG(font_browse_w)->ok_button),
297             "clicked", (GtkSignalFunc)font_browse_ok_cb, font_browse_w);
298
299         /* Connect the cancel_button to destroy the widget */
300         gtk_signal_connect_object(
301             GTK_OBJECT(GTK_FONT_SELECTION_DIALOG(font_browse_w)->cancel_button),
302             "clicked", (GtkSignalFunc)gtk_widget_destroy,
303             GTK_OBJECT(font_browse_w));
304
305         /* Catch the "key_press_event" signal in the window, so that we can
306            catch the ESC key being pressed and act as if the "Cancel" button
307            had been selected. */
308         dlg_set_cancel(font_browse_w,
309             GTK_FONT_SELECTION_DIALOG(font_browse_w)->cancel_button);
310
311         gtk_widget_show(font_browse_w);
312 }
313
314 static void
315 font_browse_ok_cb(GtkWidget *w, GtkFontSelectionDialog *fs)
316 {
317         gchar *font_name, *bold_font_name;
318         GdkFont *new_r_font, *new_b_font;
319
320         font_name = g_strdup(gtk_font_selection_dialog_get_font_name(
321               GTK_FONT_SELECTION_DIALOG(fs)));
322         if (font_name == NULL) {
323                 /* No font was selected; let the user know, but don't
324                    tear down the font selection dialog, so they can
325                    try again. */
326                 simple_dialog(ESD_TYPE_CRIT | ESD_TYPE_MODAL, NULL,
327                    "You have not selected a font.");
328                 return;
329         }
330
331         /* Get the name that the boldface version of that font would have. */
332         bold_font_name = boldify(font_name);
333
334         /* Now load those fonts, just to make sure we can. */
335         new_r_font = gdk_font_load(font_name);
336         if (new_r_font == NULL) {
337                 /* Oops, that font didn't work.
338                    Tell the user, but don't tear down the font selection
339                    dialog, so that they can try again. */
340                 simple_dialog(ESD_TYPE_CRIT | ESD_TYPE_MODAL, NULL,
341                    "The font you selected cannot be loaded.");
342
343                 g_free(font_name);
344                 g_free(bold_font_name);
345                 return;
346         }
347
348         new_b_font = gdk_font_load(bold_font_name);
349         if (new_b_font == NULL) {
350                 /* Oops, that font didn't work.
351                    Tell the user, but don't tear down the font selection
352                    dialog, so that they can try again. */
353                 simple_dialog(ESD_TYPE_CRIT | ESD_TYPE_MODAL, NULL,
354                    "The font you selected doesn't have a boldface version.");
355
356                 g_free(font_name);
357                 g_free(bold_font_name);
358                 gdk_font_unref(new_r_font);
359                 return;
360         }
361
362         font_changed = TRUE;
363         new_font_name = font_name;
364
365         gtk_widget_hide(GTK_WIDGET(fs));
366         gtk_widget_destroy(GTK_WIDGET(fs));
367 }
368
369 static void
370 font_browse_destroy(GtkWidget *win, gpointer data)
371 {
372         GtkWidget *caller;
373
374         /* Get the widget that requested that we be popped up, if any.
375            (It should arrange to destroy us if it's destroyed, so
376            that we don't get a pointer to a non-existent window here.) */
377         caller = gtk_object_get_data(GTK_OBJECT(win), FONT_CALLER_PTR_KEY);
378
379         if (caller != NULL) {
380                 /* Tell it we no longer exist. */
381                 gtk_object_set_data(GTK_OBJECT(caller), FONT_DIALOG_PTR_KEY,
382                     NULL);
383         }
384
385         /* Now nuke this window. */
386         gtk_grab_remove(GTK_WIDGET(win));
387         gtk_widget_destroy(GTK_WIDGET(win));
388 }
389
390 static gint
391 fetch_enum_value(gpointer control, const enum_val *enumvals)
392 {
393         GtkWidget *label;
394         char *label_string;
395
396         /* Get the label for the currently active entry in the option menu.
397            Yes, this is how you do it.  See FAQ 6.8 in the GTK+ FAQ. */
398         label = GTK_BIN(control)->child;
399
400         /* Get the label string, and translate it to a value. */
401         gtk_label_get(GTK_LABEL(label), &label_string);
402         return find_val_for_string(label_string, enumvals, 1);
403 }
404
405 void
406 gui_prefs_fetch(GtkWidget *w)
407 {
408         prefs.gui_scrollbar_on_right = fetch_enum_value(
409             gtk_object_get_data(GTK_OBJECT(w), SCROLLBAR_PLACEMENT_KEY),
410             scrollbar_placement_vals);
411         prefs.gui_plist_sel_browse = fetch_enum_value(
412             gtk_object_get_data(GTK_OBJECT(w), PLIST_SEL_BROWSE_KEY),
413             selection_mode_vals);
414         prefs.gui_ptree_sel_browse = fetch_enum_value(
415             gtk_object_get_data(GTK_OBJECT(w), PTREE_SEL_BROWSE_KEY),
416             selection_mode_vals);
417         prefs.gui_ptree_line_style = fetch_enum_value(
418             gtk_object_get_data(GTK_OBJECT(w), PTREE_LINE_STYLE_KEY),
419             line_style_vals);
420         prefs.gui_ptree_expander_style = fetch_enum_value(
421             gtk_object_get_data(GTK_OBJECT(w), PTREE_EXPANDER_STYLE_KEY),
422             expander_style_vals);
423         prefs.gui_hex_dump_highlight_style = fetch_enum_value(
424             gtk_object_get_data(GTK_OBJECT(w), HEX_DUMP_HIGHLIGHT_STYLE_KEY),
425             highlight_style_vals);
426
427         if (font_changed) {
428                 if (prefs.gui_font_name != NULL)
429                         g_free(prefs.gui_font_name);
430                 prefs.gui_font_name = g_strdup(new_font_name);
431         }
432
433         if (colors_changed)
434             fetch_colors();
435 }
436
437 void
438 gui_prefs_apply(GtkWidget *w)
439 {
440         GdkFont *new_r_font, *new_b_font;
441         char *bold_font_name;
442         GdkFont *old_r_font = NULL, *old_b_font = NULL;
443
444         if (font_changed) {
445                 /* XXX - what if the world changed out from under
446                    us, so that one or both of these fonts cannot
447                    be loaded? */
448                 new_r_font = gdk_font_load(prefs.gui_font_name);
449                 bold_font_name = boldify(prefs.gui_font_name);
450                 new_b_font = gdk_font_load(bold_font_name);
451                 set_plist_font(new_r_font);
452                 set_ptree_font_all(new_r_font);
453                 old_r_font = m_r_font;
454                 old_b_font = m_b_font;
455                 m_r_font = new_r_font;
456                 m_b_font = new_b_font;
457                 g_free(bold_font_name);
458         }
459
460         /* Redraw the hex dump windows, in case either the font or the
461            highlight style changed. */
462         redraw_hex_dump_all();
463
464         /* Redraw the help window. */
465         help_redraw();
466
467         /* Redraw the "Follow TCP Stream" windows, in case either the font
468            or the colors to use changed. */
469         follow_redraw_all();
470
471         set_scrollbar_placement_all(prefs.gui_scrollbar_on_right);
472         set_plist_sel_browse(prefs.gui_plist_sel_browse);
473         set_ptree_sel_browse_all(prefs.gui_ptree_sel_browse);
474         set_ptree_line_style_all(prefs.gui_ptree_line_style);
475         set_ptree_expander_style_all(prefs.gui_ptree_expander_style);
476         if (colors_changed)
477                 update_marked_frames();
478
479         /* We're no longer using the old fonts; unreference them. */
480         if (old_r_font != NULL)
481                 gdk_font_unref(old_r_font);
482         if (old_b_font != NULL)
483                 gdk_font_unref(old_b_font);
484 }
485
486 void
487 gui_prefs_destroy(GtkWidget *w)
488 {
489         GtkWidget *caller = gtk_widget_get_toplevel(w);
490         GtkWidget *fs;
491
492         /* Is there a font selection dialog associated with this
493            Preferences dialog? */
494         fs = gtk_object_get_data(GTK_OBJECT(caller), FONT_DIALOG_PTR_KEY);
495
496         if (fs != NULL) {
497                 /* Yes.  Destroy it. */
498                 gtk_widget_destroy(fs);
499         }
500
501         /* Is there a color selection dialog associated with this
502            Preferences dialog? */
503         fs = gtk_object_get_data(GTK_OBJECT(caller), COLOR_DIALOG_PTR_KEY);
504
505         if (fs != NULL) {
506                 /* Yes.  Destroy it. */
507                 gtk_widget_destroy(fs);
508         }
509
510         /* Free up any saved font name. */
511         if (new_font_name != NULL)
512                 g_free(new_font_name);
513 }
514
515 /* color selection part */
516
517 #define MAX_HANDLED_COL         2
518
519 typedef struct {
520   GdkColor color;
521   char    *label;
522 } color_info_t;
523
524 static color_info_t color_info[MAX_HANDLED_COL] = {
525 #define MFG_IDX                 0
526   { {0.0, 0.0, 0.0, 0.0},       "Marked frame foreground" },
527 #define MBG_IDX                 1
528   { {0.0, 0.0, 0.0, 0.0},       "Marked frame background" }
529 };
530
531 #define SAMPLE_MARKED_TEXT      "Sample marked frame text\n"
532
533 #define CS_RED                  0
534 #define CS_GREEN                1
535 #define CS_BLUE                 2
536 #define CS_OPACITY              3
537
538 static GdkColor *curcolor = NULL;
539
540 static void
541 color_browse_cb(GtkWidget *w, gpointer data)
542 {
543
544   GtkWidget *main_vb, *main_tb, *label, *optmenu, *menu, *menuitem;
545   GtkWidget *sample, *colorsel, *bbox, *cancel_bt, *ok_bt, *color_w;
546   int        width, height, i;
547   gdouble    scolor[4]; 
548   GtkWidget *caller = gtk_widget_get_toplevel(w);
549  
550   /* Has a color dialog box already been opened for that top-level
551      widget? */
552   color_w = gtk_object_get_data(GTK_OBJECT(caller),
553                                 COLOR_DIALOG_PTR_KEY);
554
555   if (color_w != NULL) {
556     /* Yes.  Just re-activate that dialog box. */
557     reactivate_window(color_w);
558     return;
559   }
560
561   color_info[MFG_IDX].color = prefs.gui_marked_fg;
562   color_info[MBG_IDX].color = prefs.gui_marked_bg;
563   curcolor = &color_info[MFG_IDX].color;
564   scolor[CS_RED]     = (gdouble) (curcolor->red)   / 65535.0;
565   scolor[CS_GREEN]   = (gdouble) (curcolor->green) / 65535.0;
566   scolor[CS_BLUE]    = (gdouble) (curcolor->blue)  / 65535.0;
567   scolor[CS_OPACITY] = 1.0;
568
569   /* Now create a new dialog.
570      You can't put your own extra widgets into a color selection
571      dialog, as you can with a file selection dialog, so we have to
572      construct our own dialog and put a color selection widget
573      into it. */
574   color_w = dlg_window_new("Ethereal: Select Color");
575
576   gtk_signal_connect(GTK_OBJECT(color_w), "delete_event",
577     GTK_SIGNAL_FUNC(color_delete_cb), NULL);
578
579   /* Call a handler when we're destroyed, so we can inform our caller,
580      if any, that we've been destroyed. */
581   gtk_signal_connect(GTK_OBJECT(color_w), "destroy",
582                      GTK_SIGNAL_FUNC(color_destroy_cb), NULL);
583   
584   main_vb = gtk_vbox_new(FALSE, 5);
585   gtk_container_border_width(GTK_CONTAINER(main_vb), 5);
586   gtk_container_add (GTK_CONTAINER (color_w), main_vb);
587   main_tb = gtk_table_new(3, 3, FALSE);
588   gtk_box_pack_start(GTK_BOX(main_vb), main_tb, FALSE, FALSE, 0);
589   gtk_table_set_row_spacings(GTK_TABLE(main_tb), 10);
590   gtk_table_set_col_spacings(GTK_TABLE(main_tb), 15);
591   gtk_widget_show(main_tb);
592   label = gtk_label_new("Set:");
593   gtk_misc_set_alignment(GTK_MISC(label), 1.0, 0.5);
594   gtk_table_attach_defaults(GTK_TABLE(main_tb), label, 0, 1, 0, 1);
595   gtk_widget_show(label);
596
597   colorsel = gtk_color_selection_new();
598   optmenu = gtk_option_menu_new();
599   menu = gtk_menu_new();
600   for (i = 0; i < MAX_HANDLED_COL; i++){
601     menuitem = gtk_menu_item_new_with_label(color_info[i].label);
602     gtk_object_set_data(GTK_OBJECT(menuitem), COLOR_SELECTION_PTR_KEY, 
603                         (gpointer) colorsel);
604     gtk_signal_connect(GTK_OBJECT(menuitem), "activate",
605                        GTK_SIGNAL_FUNC(update_current_color),
606                        &color_info[i].color);
607     gtk_widget_show(menuitem);
608     gtk_menu_append(GTK_MENU (menu), menuitem);
609   }
610   gtk_option_menu_set_menu (GTK_OPTION_MENU (optmenu), menu);
611   gtk_table_attach_defaults(GTK_TABLE(main_tb), optmenu, 1, 2, 0, 1);
612   gtk_widget_show(optmenu);
613
614   sample = gtk_text_new(FALSE, FALSE);
615   height = sample->style->font->ascent + sample->style->font->descent;
616   width = gdk_string_width(sample->style->font, SAMPLE_MARKED_TEXT);
617   gtk_widget_set_usize(GTK_WIDGET(sample), width, height);
618   gtk_text_set_editable(GTK_TEXT(sample), FALSE);
619   gtk_text_insert(GTK_TEXT(sample), NULL, 
620                   &color_info[MFG_IDX].color, 
621                   &color_info[MBG_IDX].color,
622                   SAMPLE_MARKED_TEXT, -1);
623   gtk_table_attach_defaults(GTK_TABLE(main_tb), sample, 2, 3, 0, 2);
624   gtk_widget_show(sample);
625   gtk_color_selection_set_color(GTK_COLOR_SELECTION(colorsel), 
626                                 &scolor[CS_RED]);
627   gtk_table_attach_defaults(GTK_TABLE(main_tb), colorsel, 0, 3, 2, 3);
628   gtk_object_set_data(GTK_OBJECT(colorsel), COLOR_SAMPLE_PTR_KEY,
629                       (gpointer) sample);
630   gtk_signal_connect(GTK_OBJECT(colorsel), "color-changed", 
631                      GTK_SIGNAL_FUNC(update_text_color), NULL);
632   gtk_widget_show(colorsel);
633   gtk_widget_show(main_vb);
634
635   gtk_object_set_data(GTK_OBJECT(color_w), COLOR_CALLER_PTR_KEY, caller);
636   gtk_object_set_data(GTK_OBJECT(caller), COLOR_DIALOG_PTR_KEY, color_w);
637
638   /* Ok, Cancel Buttons */  
639   bbox = gtk_hbutton_box_new();
640   gtk_button_box_set_layout(GTK_BUTTON_BOX(bbox), GTK_BUTTONBOX_END);
641   gtk_button_box_set_spacing(GTK_BUTTON_BOX(bbox), 5);
642   gtk_container_add(GTK_CONTAINER(main_vb), bbox);
643   gtk_widget_show(bbox);
644
645   ok_bt = gtk_button_new_with_label ("OK");
646   gtk_signal_connect(GTK_OBJECT(ok_bt), "clicked",
647                      GTK_SIGNAL_FUNC(color_ok_cb), color_w);
648   GTK_WIDGET_SET_FLAGS(ok_bt, GTK_CAN_DEFAULT);
649   gtk_box_pack_start(GTK_BOX (bbox), ok_bt, TRUE, TRUE, 0);
650   gtk_widget_grab_default(ok_bt);
651   gtk_widget_show(ok_bt);
652   cancel_bt = gtk_button_new_with_label ("Cancel");
653   gtk_signal_connect_object(GTK_OBJECT(cancel_bt), "clicked", 
654                             (GtkSignalFunc)gtk_widget_destroy,
655                             GTK_OBJECT(color_w));
656   gtk_box_pack_start(GTK_BOX (bbox), cancel_bt, TRUE, TRUE, 0);
657   gtk_widget_show(cancel_bt);
658   dlg_set_cancel(color_w, cancel_bt);
659
660   gtk_widget_show(color_w);
661 }
662
663 static void
664 update_text_color(GtkWidget *w, gpointer data) {
665   GtkText  *sample = gtk_object_get_data(GTK_OBJECT(w), COLOR_SAMPLE_PTR_KEY);
666   gdouble   scolor[4];
667
668   gtk_color_selection_get_color(GTK_COLOR_SELECTION(w), &scolor[CS_RED]);
669   
670   curcolor->red   = (gushort) (scolor[CS_RED]   * 65535.0);
671   curcolor->green = (gushort) (scolor[CS_GREEN] * 65535.0);
672   curcolor->blue  = (gushort) (scolor[CS_BLUE]  * 65535.0);
673   
674   gtk_text_freeze(sample);
675   gtk_text_set_point(sample, 0);
676   gtk_text_forward_delete(sample, gtk_text_get_length(sample));
677   gtk_text_insert(GTK_TEXT(sample), NULL, 
678                   &color_info[MFG_IDX].color, 
679                   &color_info[MBG_IDX].color,
680                   SAMPLE_MARKED_TEXT, -1);
681   gtk_text_thaw(sample);
682 }
683
684 static void
685 update_current_color(GtkWidget *w, gpointer data)
686 {
687   GtkColorSelection *colorsel;    
688   gdouble            scolor[4];
689
690   colorsel = GTK_COLOR_SELECTION(gtk_object_get_data(GTK_OBJECT(w),
691                                                      COLOR_SELECTION_PTR_KEY));
692   curcolor = (GdkColor *)data;
693   scolor[CS_RED]     = (gdouble) (curcolor->red)   / 65535.0;
694   scolor[CS_GREEN]   = (gdouble) (curcolor->green) / 65535.0;
695   scolor[CS_BLUE]    = (gdouble) (curcolor->blue)  / 65535.0;
696   scolor[CS_OPACITY] = 1.0;
697   
698   gtk_color_selection_set_color(colorsel, &scolor[CS_RED]);
699 }
700
701 static void
702 color_ok_cb(GtkWidget *w, gpointer data)
703 {
704   /* We assume the user actually changed a color here. */
705   colors_changed = TRUE;
706
707   gtk_widget_hide(GTK_WIDGET(data));
708   gtk_widget_destroy(GTK_WIDGET(data));
709 }
710
711 static void
712 color_cancel_cb(GtkWidget *w, gpointer data)
713 {
714   /* Revert the colors to the current preference settings. */
715   color_info[MFG_IDX].color = prefs.gui_marked_fg;
716   color_info[MBG_IDX].color = prefs.gui_marked_bg;
717   gtk_widget_hide(GTK_WIDGET(data));
718   gtk_widget_destroy(GTK_WIDGET(data));
719 }
720
721 /* Treat this as a cancel, by calling "color_cancel_cb()".
722    XXX - that'll destroy the Select Color dialog; will that upset
723    a higher-level handler that says "OK, we've been asked to delete
724    this, so destroy it"? */
725 static gboolean
726 color_delete_cb(GtkWidget *prefs_w, gpointer dummy)
727 {
728   color_cancel_cb(NULL, NULL);
729   return FALSE;
730 }
731
732 static void
733 color_destroy_cb(GtkWidget *w, gpointer data)
734 {
735   GtkWidget *caller = gtk_object_get_data(GTK_OBJECT(w), 
736                                           COLOR_CALLER_PTR_KEY);
737   if (caller != NULL) {
738     gtk_object_set_data(GTK_OBJECT(caller), COLOR_DIALOG_PTR_KEY, NULL);
739   }
740   gtk_grab_remove(GTK_WIDGET(w));
741   gtk_widget_destroy(GTK_WIDGET(w));
742 }
743
744 static void
745 fetch_colors(void)
746 {
747         prefs.gui_marked_fg = color_info[MFG_IDX].color;
748         prefs.gui_marked_bg = color_info[MBG_IDX].color;
749 }