4 * ROX-Filer, filer for the ROX desktop project
5 * Copyright (C) 2005 Thomas Leonard
7 * This program is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License as published by the Free
9 * Software Foundation; either version 2 of the License, or (at your option)
12 * This program is distributed in the hope that it will be useful, but WITHOUT
13 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
17 * You should have received a copy of the GNU General Public License along with
18 * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
19 * Place, Suite 330, Boston, MA 02111-1307 USA
22 /* options.c - code for handling user choices */
28 * - The <Choices>/PROJECT/Options file is read in, which contains a list of
29 * name/value pairs, and these are stored in the 'loading' hash table.
31 * - Each part of the filer then calls option_add_int(), or a related function,
32 * supplying the name for each option and a default value. Once an option is
33 * registered, it is removed from the loading table.
35 * - If things need to happen when values change, modules register with
36 * option_add_notify().
38 * - option_register_widget() can be used during initialisation (any time
39 * before the Options box is displayed) to tell the system how to render a
40 * particular type of option.
42 * - Finally, all notify callbacks are called. Use the Option->has_changed
43 * field to work out what has changed from the defaults.
45 * When the user opens the Options box:
47 * - The Options.xml file is read and used to create the Options dialog box.
48 * Each element in the file has a key corresponding to an option named
51 * - For each widget in the box, the current value of the option is used to
52 * set the widget's state.
54 * - All current values are saved for a possible Revert later.
56 * When the user changes an option or clicks on Revert:
58 * - The option values are updated.
60 * - All notify callbacks are called. Use the Option->has_changed field
61 * to see what changed.
65 * - If anything changed then:
66 * - All the options are written to the filesystem
67 * - The saver_callbacks are called.
78 #include <libxml/parser.h>
85 #include "gui_support.h"
88 /* Add all option tooltips to this group */
89 static GtkTooltips
*option_tooltips
= NULL
;
90 #define OPTION_TIP(widget, tip) \
91 gtk_tooltips_set_tip(option_tooltips, widget, tip, NULL)
93 /* The Options window. NULL if not yet created. */
94 static GtkWidget
*window
= NULL
;
96 /* "filer_unique" -> (Option *) */
97 static GHashTable
*option_hash
= NULL
;
99 /* A mapping (name -> value) for options which have been loaded by not
100 * yet registered. The options in this table cannot be used until
101 * option_add_*() is called to move them into option_hash.
103 static GHashTable
*loading
= NULL
;
105 /* A mapping (XML name -> OptionBuildFn). When reading the Options.xml
106 * file, this table gives the function used to create the widgets.
108 static GHashTable
*widget_builder
= NULL
;
110 /* A mapping (name -> GtkSizeGroup) of size groups used by the widgets
111 * in the options box. This hash table is created/destroyed every time
112 * the box is opened/destroyed.
114 static GHashTable
*size_groups
= NULL
;
116 /* List of functions to call after all option values are updated */
117 static GList
*notify_callbacks
= NULL
;
119 /* List of functions to call after all options are saved */
120 static GList
*saver_callbacks
= NULL
;
122 static int updating_widgets
= 0; /* Ignore change signals when set */
124 static GtkWidget
*revert_widget
= NULL
;
126 /* Static prototypes */
127 static void save_options(void);
128 static void revert_options(GtkWidget
*widget
, gpointer data
);
129 static void build_options_window(void);
130 static GtkWidget
*build_window_frame(GtkTreeView
**tree_view
);
131 static void update_option_widgets(void);
132 static void button_patch_set_colour(GtkWidget
*button
, GdkColor
*color
);
133 static void option_add(Option
*option
, const gchar
*key
);
134 static void set_not_changed(gpointer key
, gpointer value
, gpointer data
);
135 static void load_options(xmlDoc
*doc
);
136 static gboolean
check_anything_changed(void);
137 static int get_int(xmlNode
*node
, guchar
*attr
);
138 static void may_add_tip(GtkWidget
*widget
, xmlNode
*element
);
139 static void add_to_size_group(xmlNode
*node
, GtkWidget
*widget
);
141 static const char *process_option_line(gchar
*line
);
143 static GList
*build_label(Option
*option
, xmlNode
*node
, guchar
*label
);
144 static GList
*build_spacer(Option
*option
, xmlNode
*node
, guchar
*label
);
145 static GList
*build_frame(Option
*option
, xmlNode
*node
, guchar
*label
);
147 static GList
*build_toggle(Option
*option
, xmlNode
*node
, guchar
*label
);
148 static GList
*build_slider(Option
*option
, xmlNode
*node
, guchar
*label
);
149 static GList
*build_entry(Option
*option
, xmlNode
*node
, guchar
*label
);
150 static GList
*build_radio_group(Option
*option
, xmlNode
*node
, guchar
*label
);
151 static GList
*build_colour(Option
*option
, xmlNode
*node
, guchar
*label
);
152 static GList
*build_menu(Option
*option
, xmlNode
*node
, guchar
*label
);
153 static GList
*build_font(Option
*option
, xmlNode
*node
, guchar
*label
);
154 static GList
*build_numentry(Option
*option
, xmlNode
*node
, guchar
*label
);
155 static void update_numentry(Option
*option
);
156 static guchar
*read_numentry(Option
*option
);
158 static gboolean updating_file_format
= FALSE
;
160 /****************************************************************
161 * EXTERNAL INTERFACE *
162 ****************************************************************/
164 void options_init(void)
169 loading
= g_hash_table_new(g_str_hash
, g_str_equal
);
170 option_hash
= g_hash_table_new(g_str_hash
, g_str_equal
);
171 widget_builder
= g_hash_table_new(g_str_hash
, g_str_equal
);
173 path
= choices_find_xdg_path_load("Options", PROJECT
, SITE
);
176 /* Load in all the options set in the filer, storing them
177 * temporarily in the loading hash table.
178 * They get moved to option_hash when they're registered.
180 doc
= xmlParseFile(path
);
188 parse_file(path
, process_option_line
);
189 updating_file_format
= TRUE
;
195 option_register_widget("label", build_label
);
196 option_register_widget("spacer", build_spacer
);
197 option_register_widget("frame", build_frame
);
199 option_register_widget("toggle", build_toggle
);
200 option_register_widget("slider", build_slider
);
201 option_register_widget("entry", build_entry
);
202 option_register_widget("numentry", build_numentry
);
203 option_register_widget("radio-group", build_radio_group
);
204 option_register_widget("colour", build_colour
);
205 option_register_widget("menu", build_menu
);
206 option_register_widget("font", build_font
);
209 /* When parsing the XML file, process an element named 'name' by
210 * calling 'builder(option, xml_node, label)'.
211 * builder returns the new widgets to add to the options box.
212 * 'name' should be a static string. Call 'option_check_widget' when
213 * the widget's value is modified.
215 * Functions to set or get the widget's state can be stored in 'option'.
216 * If the option doesn't have a name attribute in Options.xml then
217 * ui will be NULL on entry (this is used for buttons).
219 void option_register_widget(char *name
, OptionBuildFn builder
)
221 g_hash_table_insert(widget_builder
, name
, builder
);
224 /* This is called when the widget's value is modified by the user.
225 * Reads the new value of the widget into the option and calls
226 * the notify callbacks.
228 void option_check_widget(Option
*option
)
232 if (updating_widgets
)
233 return; /* Not caused by the user... */
235 g_return_if_fail(option
->read_widget
!= NULL
);
237 new = option
->read_widget(option
);
239 g_return_if_fail(new != NULL
);
241 g_hash_table_foreach(option_hash
, set_not_changed
, NULL
);
243 option
->has_changed
= strcmp(option
->value
, new) != 0;
245 if (!option
->has_changed
)
251 g_free(option
->value
);
253 option
->int_value
= atoi(new);
258 /* Call all the notify callbacks. This should happen after any options
259 * have their values changed.
260 * Set each option->has_changed flag before calling this function.
262 void options_notify(void)
266 for (next
= notify_callbacks
; next
; next
= next
->next
)
268 OptionNotify
*cb
= (OptionNotify
*) next
->data
;
273 if (updating_file_format
)
275 updating_file_format
= FALSE
;
277 info_message(_("ROX-Filer has converted your Options file "
278 "to the new XML format"));
282 gtk_widget_set_sensitive(revert_widget
,
283 check_anything_changed());
286 /* Store values used by Revert */
287 static void store_backup(gpointer key
, gpointer value
, gpointer data
)
289 Option
*option
= (Option
*) value
;
291 g_free(option
->backup
);
292 option
->backup
= g_strdup(option
->value
);
295 /* Allow the user to edit the options. Returns the window widget (you don't
296 * normally need this). NULL if already open.
298 GtkWidget
*options_show(void)
300 if (!option_tooltips
)
301 option_tooltips
= gtk_tooltips_new();
303 if (g_hash_table_size(loading
) != 0)
305 g_print(PROJECT
": Some options loaded but not used:\n");
306 g_hash_table_foreach(loading
, (GHFunc
) puts
, NULL
);
311 gtk_window_present(GTK_WINDOW(window
));
315 g_hash_table_foreach(option_hash
, store_backup
, NULL
);
317 build_options_window();
319 update_option_widgets();
321 gtk_widget_show_all(window
);
326 /* Initialise and register a new integer option */
327 void option_add_int(Option
*option
, const gchar
*key
, int value
)
329 option
->value
= g_strdup_printf("%d", value
);
330 option
->int_value
= value
;
331 option_add(option
, key
);
334 void option_add_string(Option
*option
, const gchar
*key
, const gchar
*value
)
336 option
->value
= g_strdup(value
);
337 option
->int_value
= atoi(value
);
338 option_add(option
, key
);
341 /* Add a callback which will be called after any options have changed their
342 * values. If several options change at once, this is called after all
345 void option_add_notify(OptionNotify
*callback
)
347 g_return_if_fail(callback
!= NULL
);
349 notify_callbacks
= g_list_append(notify_callbacks
, callback
);
352 /* Call 'callback' after all the options have been saved */
353 void option_add_saver(OptionNotify
*callback
)
355 g_return_if_fail(callback
!= NULL
);
357 saver_callbacks
= g_list_append(saver_callbacks
, callback
);
360 /* Base class for building numentry widgets with particular ranges */
361 GList
*build_numentry_base(Option
*option
, xmlNode
*node
,
362 guchar
*label
, GtkAdjustment
*adj
)
366 GtkWidget
*label_wid
;
370 width
= get_int(node
, "width");
371 unit
= xmlGetProp(node
, "unit");
373 hbox
= gtk_hbox_new(FALSE
, 4);
377 label_wid
= gtk_label_new(_(label
));
378 gtk_misc_set_alignment(GTK_MISC(label_wid
), 1.0, 0.5);
379 gtk_box_pack_start(GTK_BOX(hbox
), label_wid
, FALSE
, TRUE
, 0);
380 add_to_size_group(node
, label_wid
);
383 spin
= gtk_spin_button_new(adj
, adj
->step_increment
, 0);
384 gtk_entry_set_width_chars(GTK_ENTRY(spin
),
385 width
> 1 ? width
+ 1 : 2);
386 gtk_box_pack_start(GTK_BOX(hbox
), spin
, FALSE
, TRUE
, 0);
387 may_add_tip(spin
, node
);
391 gtk_box_pack_start(GTK_BOX(hbox
), gtk_label_new(_(unit
)),
396 option
->update_widget
= update_numentry
;
397 option
->read_widget
= read_numentry
;
398 option
->widget
= spin
;
400 g_signal_connect_swapped(spin
, "value-changed",
401 G_CALLBACK(option_check_widget
), option
);
403 return g_list_append(NULL
, hbox
);
406 /****************************************************************
407 * INTERNAL FUNCTIONS *
408 ****************************************************************/
410 /* Option should contain the default value.
411 * It must never be destroyed after being registered (Options are typically
412 * statically allocated).
413 * The key corresponds to the option's name in Options.xml, and to the key
414 * in the saved options file.
416 * On exit, the value will have been updated to the loaded value, if
417 * different to the default.
419 static void option_add(Option
*option
, const gchar
*key
)
421 gpointer okey
, value
;
423 g_return_if_fail(option_hash
!= NULL
);
424 g_return_if_fail(g_hash_table_lookup(option_hash
, key
) == NULL
);
425 g_return_if_fail(option
->value
!= NULL
);
427 option
->has_changed
= FALSE
;
429 option
->widget
= NULL
;
430 option
->update_widget
= NULL
;
431 option
->read_widget
= NULL
;
432 option
->backup
= NULL
;
434 g_hash_table_insert(option_hash
, (gchar
*) key
, option
);
436 /* Use the value loaded from the file, if any */
437 if (g_hash_table_lookup_extended(loading
, key
, &okey
, &value
))
439 option
->has_changed
= strcmp(option
->value
, value
) != 0;
441 g_free(option
->value
);
442 option
->value
= value
;
443 option
->int_value
= atoi(value
);
444 g_hash_table_remove(loading
, key
);
449 static GtkColorSelectionDialog
*current_csel_box
= NULL
;
450 static GtkFontSelectionDialog
*current_fontsel_box
= NULL
;
452 static void get_new_colour(GtkWidget
*ok
, Option
*option
)
457 g_return_if_fail(current_csel_box
!= NULL
);
459 csel
= current_csel_box
->colorsel
;
461 gtk_color_selection_get_current_color(GTK_COLOR_SELECTION(csel
), &c
);
463 button_patch_set_colour(option
->widget
, &c
);
465 gtk_widget_destroy(GTK_WIDGET(current_csel_box
));
467 option_check_widget(option
);
470 static void open_coloursel(GtkWidget
*button
, Option
*option
)
472 GtkColorSelectionDialog
*csel
;
473 GtkWidget
*dialog
, *patch
;
475 if (current_csel_box
)
476 gtk_widget_destroy(GTK_WIDGET(current_csel_box
));
478 dialog
= gtk_color_selection_dialog_new(NULL
);
479 csel
= GTK_COLOR_SELECTION_DIALOG(dialog
);
480 current_csel_box
= csel
;
481 gtk_window_set_position(GTK_WINDOW(csel
), GTK_WIN_POS_MOUSE
);
483 g_signal_connect(dialog
, "destroy",
484 G_CALLBACK(gtk_widget_destroyed
), ¤t_csel_box
);
485 gtk_widget_hide(csel
->help_button
);
486 g_signal_connect_swapped(csel
->cancel_button
, "clicked",
487 G_CALLBACK(gtk_widget_destroy
), dialog
);
488 g_signal_connect(csel
->ok_button
, "clicked",
489 G_CALLBACK(get_new_colour
), option
);
491 patch
= GTK_BIN(button
)->child
;
493 gtk_color_selection_set_current_color(
494 GTK_COLOR_SELECTION(csel
->colorsel
),
495 &patch
->style
->bg
[GTK_STATE_NORMAL
]);
497 gtk_widget_show(dialog
);
500 static void font_chosen(GtkWidget
*dialog
, gint response
, Option
*option
)
504 if (response
!= GTK_RESPONSE_OK
)
507 font
= gtk_font_selection_dialog_get_font_name(
508 GTK_FONT_SELECTION_DIALOG(dialog
));
510 gtk_label_set_text(GTK_LABEL(option
->widget
), font
);
514 option_check_widget(option
);
517 gtk_widget_destroy(dialog
);
521 static void toggle_active_font(GtkToggleButton
*toggle
, Option
*option
)
523 if (current_fontsel_box
)
524 gtk_widget_destroy(GTK_WIDGET(current_fontsel_box
));
526 if (gtk_toggle_button_get_active(toggle
))
528 gtk_widget_set_sensitive(option
->widget
->parent
, TRUE
);
529 gtk_label_set_text(GTK_LABEL(option
->widget
), "Sans 12");
533 gtk_widget_set_sensitive(option
->widget
->parent
, FALSE
);
534 gtk_label_set_text(GTK_LABEL(option
->widget
),
538 option_check_widget(option
);
541 static void open_fontsel(GtkWidget
*button
, Option
*option
)
543 if (current_fontsel_box
)
544 gtk_widget_destroy(GTK_WIDGET(current_fontsel_box
));
546 current_fontsel_box
= GTK_FONT_SELECTION_DIALOG(
547 gtk_font_selection_dialog_new(PROJECT
));
549 gtk_window_set_position(GTK_WINDOW(current_fontsel_box
),
552 g_signal_connect(current_fontsel_box
, "destroy",
553 G_CALLBACK(gtk_widget_destroyed
), ¤t_fontsel_box
);
555 gtk_font_selection_dialog_set_font_name(current_fontsel_box
,
558 g_signal_connect(current_fontsel_box
, "response",
559 G_CALLBACK(font_chosen
), option
);
561 gtk_widget_show(GTK_WIDGET(current_fontsel_box
));
564 /* These are used during parsing... */
565 static xmlDocPtr options_doc
= NULL
;
567 #define DATA(node) (xmlNodeListGetString(options_doc, node->xmlChildrenNode, 1))
569 static void may_add_tip(GtkWidget
*widget
, xmlNode
*element
)
573 data
= DATA(element
);
577 tip
= g_strstrip(g_strdup(data
));
580 OPTION_TIP(widget
, _(tip
));
584 /* Returns zero if attribute is not present */
585 static int get_int(xmlNode
*node
, guchar
*attr
)
590 txt
= xmlGetProp(node
, attr
);
600 /* Adds 'widget' to the GtkSizeGroup selected by 'index'. This function
601 * does nothing if 'node' has no "sizegroup" attribute.
602 * The value of "sizegroup" is either a key. All widgets with the same
603 * key request the same size.
604 * Size groups are created on the fly and get destroyed when the options
607 static void add_to_size_group(xmlNode
*node
, GtkWidget
*widget
)
612 g_return_if_fail(node
!= NULL
);
613 g_return_if_fail(widget
!= NULL
);
615 name
= xmlGetProp(node
, "sizegroup");
619 if (size_groups
== NULL
)
620 size_groups
= g_hash_table_new_full(g_str_hash
, g_str_equal
,
623 sg
= (GtkSizeGroup
*) g_hash_table_lookup(size_groups
, name
);
627 sg
= (GtkSizeGroup
*) gtk_size_group_new(
628 GTK_SIZE_GROUP_HORIZONTAL
);
629 g_hash_table_insert(size_groups
, name
, sg
);
630 gtk_size_group_add_widget(sg
, widget
);
631 g_object_unref(G_OBJECT(sg
));
635 gtk_size_group_add_widget(sg
, widget
);
640 static GtkWidget
*build_radio(xmlNode
*radio
, GtkWidget
*prev
)
643 GtkRadioButton
*prev_button
= (GtkRadioButton
*) prev
;
646 label
= xmlGetProp(radio
, "label");
648 button
= gtk_radio_button_new_with_label(
649 prev_button
? gtk_radio_button_get_group(prev_button
)
654 may_add_tip(button
, radio
);
656 g_object_set_data(G_OBJECT(button
), "value",
657 xmlGetProp(radio
, "value"));
662 static void build_menu_item(xmlNode
*node
, GtkWidget
*option_menu
)
664 GtkWidget
*item
, *menu
;
667 g_return_if_fail(strcmp(node
->name
, "item") == 0);
669 label
= xmlGetProp(node
, "label");
670 item
= gtk_menu_item_new_with_label(_(label
));
673 menu
= gtk_option_menu_get_menu(GTK_OPTION_MENU(option_menu
));
674 gtk_menu_shell_append(GTK_MENU_SHELL(menu
), item
);
675 gtk_widget_show_all(menu
);
677 g_object_set_data(G_OBJECT(item
), "value", xmlGetProp(node
, "value"));
680 static void build_widget(xmlNode
*widget
, GtkWidget
*box
)
682 const char *name
= widget
->name
;
683 OptionBuildFn builder
;
688 label
= xmlGetProp(widget
, "label");
690 if (strcmp(name
, "hbox") == 0 || strcmp(name
, "vbox") == 0)
696 nbox
= gtk_hbox_new(FALSE
, 4);
698 nbox
= gtk_vbox_new(FALSE
, 0);
701 gtk_box_pack_start(GTK_BOX(nbox
),
702 gtk_label_new(_(label
)), FALSE
, TRUE
, 4);
703 gtk_box_pack_start(GTK_BOX(box
), nbox
, FALSE
, TRUE
, 0);
705 for (hw
= widget
->xmlChildrenNode
; hw
; hw
= hw
->next
)
707 if (hw
->type
== XML_ELEMENT_NODE
)
708 build_widget(hw
, nbox
);
715 oname
= xmlGetProp(widget
, "name");
719 option
= g_hash_table_lookup(option_hash
, oname
);
723 g_warning("No Option for '%s'!\n", oname
);
733 builder
= g_hash_table_lookup(widget_builder
, name
);
736 GList
*widgets
, *next
;
738 if (option
&& option
->widget
)
739 g_warning("Widget for option already exists!");
741 widgets
= builder(option
, widget
, label
);
743 for (next
= widgets
; next
; next
= next
->next
)
745 GtkWidget
*w
= (GtkWidget
*) next
->data
;
746 gtk_box_pack_start(GTK_BOX(box
), w
, FALSE
, TRUE
, 0);
748 g_list_free(widgets
);
751 g_warning("Unknown option type '%s'\n", name
);
756 static void build_section(xmlNode
*section
, GtkWidget
*notebook
,
757 GtkTreeStore
*tree_store
, GtkTreeIter
*parent
)
759 guchar
*title
= NULL
;
764 title
= xmlGetProp(section
, "title");
765 page
= gtk_vbox_new(FALSE
, 4);
766 gtk_container_set_border_width(GTK_CONTAINER(page
), 4);
767 gtk_notebook_append_page(GTK_NOTEBOOK(notebook
), page
, NULL
);
769 gtk_tree_store_append(tree_store
, &iter
, parent
);
770 gtk_tree_store_set(tree_store
, &iter
, 0, _(title
), 1, page
, -1);
773 widget
= section
->xmlChildrenNode
;
774 for (; widget
; widget
= widget
->next
)
776 if (widget
->type
== XML_ELEMENT_NODE
)
778 if (strcmp(widget
->name
, "section") == 0)
779 build_section(widget
, notebook
,
782 build_widget(widget
, page
);
787 /* Parse <app_dir>/Options.xml to create the options window.
788 * Sets the global 'window' variable.
790 static void build_options_window(void)
795 xmlDocPtr options_doc
;
796 xmlNode
*options
, *section
;
799 notebook
= build_window_frame(&tree
);
801 path
= g_strconcat(app_dir
, "/Options.xml", NULL
);
802 options_doc
= xmlParseFile(path
);
806 report_error(_("Internal error: %s unreadable"), path
);
813 options
= xmlDocGetRootElement(options_doc
);
814 if (strcmp(options
->name
, "options") == 0)
816 GtkTreePath
*treepath
;
818 store
= (GtkTreeStore
*) gtk_tree_view_get_model(tree
);
819 section
= options
->xmlChildrenNode
;
820 for (; section
; section
= section
->next
)
821 if (section
->type
== XML_ELEMENT_NODE
)
822 build_section(section
, notebook
, store
, NULL
);
824 gtk_tree_view_expand_all(tree
);
825 treepath
= gtk_tree_path_new_first();
828 gtk_tree_view_set_cursor(tree
, treepath
, NULL
, FALSE
);
829 gtk_tree_path_free(treepath
);
833 xmlFreeDoc(options_doc
);
837 static void null_widget(gpointer key
, gpointer value
, gpointer data
)
839 Option
*option
= (Option
*) value
;
841 g_return_if_fail(option
->widget
!= NULL
);
843 option
->widget
= NULL
;
846 static void options_destroyed(GtkWidget
*widget
, gpointer data
)
848 if (current_csel_box
)
849 gtk_widget_destroy(GTK_WIDGET(current_csel_box
));
850 if (current_fontsel_box
)
851 gtk_widget_destroy(GTK_WIDGET(current_fontsel_box
));
853 revert_widget
= NULL
;
855 if (check_anything_changed())
858 if (widget
== window
)
862 g_hash_table_foreach(option_hash
, null_widget
, NULL
);
866 g_hash_table_destroy(size_groups
);
873 /* The cursor has been changed in the tree view, so switch to the new
874 * page in the notebook.
876 static void tree_cursor_changed(GtkTreeView
*tv
, gpointer data
)
878 GtkTreePath
*path
= NULL
;
879 GtkNotebook
*nbook
= GTK_NOTEBOOK(data
);
881 GtkWidget
*page
= NULL
;
884 gtk_tree_view_get_cursor(tv
, &path
, NULL
);
888 model
= gtk_tree_view_get_model(tv
);
889 gtk_tree_model_get_iter(model
, &iter
, path
);
890 gtk_tree_path_free(path
);
891 gtk_tree_model_get(model
, &iter
, 1, &page
, -1);
895 gtk_notebook_set_current_page(nbook
,
896 gtk_notebook_page_num(nbook
, page
));
897 g_object_unref(page
);
901 /* Creates the window and adds the various buttons to it.
902 * Returns the notebook to add sections to and sets the global
903 * 'window'. If 'tree_view' is non-NULL, it stores the address
904 * of the tree view widget there.
906 static GtkWidget
*build_window_frame(GtkTreeView
**tree_view
)
909 GtkWidget
*tl_vbox
, *hbox
, *frame
, *tv
;
910 GtkWidget
*actions
, *button
;
912 char *string
, *save_path
;
914 window
= gtk_window_new(GTK_WINDOW_TOPLEVEL
);
916 gtk_window_set_position(GTK_WINDOW(window
), GTK_WIN_POS_CENTER
);
917 gtk_window_set_title(GTK_WINDOW(window
), _("Options"));
918 g_signal_connect(window
, "destroy",
919 G_CALLBACK(options_destroyed
), NULL
);
920 gtk_container_set_border_width(GTK_CONTAINER(window
), 4);
922 tl_vbox
= gtk_vbox_new(FALSE
, 4);
923 gtk_container_add(GTK_CONTAINER(window
), tl_vbox
);
925 hbox
= gtk_hbox_new(FALSE
, 4);
926 gtk_box_pack_start(GTK_BOX(tl_vbox
), hbox
, TRUE
, TRUE
, 0);
928 frame
= gtk_frame_new(NULL
);
929 gtk_frame_set_shadow_type(GTK_FRAME(frame
), GTK_SHADOW_IN
);
930 gtk_box_pack_end(GTK_BOX(hbox
), frame
, TRUE
, TRUE
, 0);
932 notebook
= gtk_notebook_new();
933 gtk_notebook_set_show_tabs(GTK_NOTEBOOK(notebook
), FALSE
);
934 gtk_notebook_set_show_border(GTK_NOTEBOOK(notebook
), FALSE
);
935 gtk_container_add(GTK_CONTAINER(frame
), notebook
);
937 frame
= gtk_frame_new(NULL
);
938 gtk_frame_set_shadow_type(GTK_FRAME(frame
), GTK_SHADOW_IN
);
939 gtk_box_pack_start(GTK_BOX(hbox
), frame
, FALSE
, TRUE
, 0);
942 model
= gtk_tree_store_new(2, G_TYPE_STRING
, GTK_TYPE_WIDGET
);
943 tv
= gtk_tree_view_new_with_model(GTK_TREE_MODEL(model
));
944 g_object_unref(model
);
945 gtk_tree_selection_set_mode(
946 gtk_tree_view_get_selection(GTK_TREE_VIEW(tv
)),
947 GTK_SELECTION_BROWSE
);
948 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(tv
), FALSE
);
949 gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(tv
), -1,
950 NULL
, gtk_cell_renderer_text_new(), "text", 0, NULL
);
951 gtk_container_add(GTK_CONTAINER(frame
), tv
);
952 g_signal_connect(tv
, "cursor_changed",
953 G_CALLBACK(tree_cursor_changed
), notebook
);
955 actions
= gtk_hbutton_box_new();
956 gtk_button_box_set_layout(GTK_BUTTON_BOX(actions
),
958 gtk_box_set_spacing(GTK_BOX(actions
), 10);
960 gtk_box_pack_start(GTK_BOX(tl_vbox
), actions
, FALSE
, TRUE
, 0);
962 revert_widget
= button_new_mixed(GTK_STOCK_UNDO
, _("_Revert"));
963 GTK_WIDGET_SET_FLAGS(revert_widget
, GTK_CAN_DEFAULT
);
964 gtk_box_pack_start(GTK_BOX(actions
), revert_widget
, FALSE
, TRUE
, 0);
965 g_signal_connect(revert_widget
, "clicked",
966 G_CALLBACK(revert_options
), NULL
);
967 gtk_tooltips_set_tip(option_tooltips
, revert_widget
,
968 _("Restore all choices to how they were when the "
969 "Options box was opened."), NULL
);
970 gtk_widget_set_sensitive(revert_widget
, check_anything_changed());
972 button
= gtk_button_new_from_stock(GTK_STOCK_OK
);
973 GTK_WIDGET_SET_FLAGS(button
, GTK_CAN_DEFAULT
);
974 gtk_box_pack_start(GTK_BOX(actions
), button
, FALSE
, TRUE
, 0);
975 g_signal_connect_swapped(button
, "clicked",
976 G_CALLBACK(gtk_widget_destroy
), window
);
977 gtk_widget_grab_default(button
);
978 gtk_widget_grab_focus(button
);
980 save_path
= choices_find_xdg_path_save("...", PROJECT
, SITE
, FALSE
);
983 string
= g_strdup_printf(_("Choices will be saved as:\n%s"),
985 gtk_tooltips_set_tip(option_tooltips
, button
, string
, NULL
);
990 gtk_tooltips_set_tip(option_tooltips
, button
,
991 _("(saving disabled by CHOICESPATH)"), NULL
);
994 *tree_view
= GTK_TREE_VIEW(tv
);
999 /* Given the last radio button in the group, select whichever
1000 * radio button matches the given value.
1002 static void radio_group_set_value(GtkRadioButton
*last
, guchar
*value
)
1006 for (next
= gtk_radio_button_get_group(last
); next
; next
= next
->next
)
1008 GtkToggleButton
*button
= (GtkToggleButton
*) next
->data
;
1011 val
= g_object_get_data(G_OBJECT(button
), "value");
1012 g_return_if_fail(val
!= NULL
);
1014 if (strcmp(val
, value
) == 0)
1016 gtk_toggle_button_set_active(button
, TRUE
);
1021 g_warning("Can't find radio button with value %s\n", value
);
1024 /* Given the last radio button in the group, return a copy of the
1025 * value for the selected radio item.
1027 static guchar
*radio_group_get_value(GtkRadioButton
*last
)
1031 for (next
= gtk_radio_button_get_group(last
); next
; next
= next
->next
)
1033 GtkToggleButton
*button
= (GtkToggleButton
*) next
->data
;
1035 if (gtk_toggle_button_get_active(button
))
1039 val
= g_object_get_data(G_OBJECT(button
), "value");
1040 g_return_val_if_fail(val
!= NULL
, NULL
);
1042 return g_strdup(val
);
1049 /* Select this item with this value */
1050 static void option_menu_set(GtkOptionMenu
*om
, guchar
*value
)
1056 menu
= gtk_option_menu_get_menu(om
);
1057 list
= gtk_container_get_children(GTK_CONTAINER(menu
));
1059 for (next
= list
; next
; next
= next
->next
)
1061 GObject
*item
= (GObject
*) next
->data
;
1064 data
= g_object_get_data(item
, "value");
1065 g_return_if_fail(data
!= NULL
);
1067 if (strcmp(data
, value
) == 0)
1069 gtk_option_menu_set_history(om
, i
);
1079 /* Get the value (static) of the selected item */
1080 static guchar
*option_menu_get(GtkOptionMenu
*om
)
1082 GtkWidget
*menu
, *item
;
1084 menu
= gtk_option_menu_get_menu(om
);
1085 item
= gtk_menu_get_active(GTK_MENU(menu
));
1087 return g_object_get_data(G_OBJECT(item
), "value");
1090 static void restore_backup(gpointer key
, gpointer value
, gpointer data
)
1092 Option
*option
= (Option
*) value
;
1094 g_return_if_fail(option
->backup
!= NULL
);
1096 option
->has_changed
= strcmp(option
->value
, option
->backup
) != 0;
1097 if (!option
->has_changed
)
1100 g_free(option
->value
);
1101 option
->value
= g_strdup(option
->backup
);
1102 option
->int_value
= atoi(option
->value
);
1105 static void revert_options(GtkWidget
*widget
, gpointer data
)
1107 g_hash_table_foreach(option_hash
, restore_backup
, NULL
);
1109 update_option_widgets();
1112 static void check_changed_cb(gpointer key
, gpointer value
, gpointer data
)
1114 Option
*option
= (Option
*) value
;
1115 gboolean
*changed
= (gboolean
*) data
;
1117 g_return_if_fail(option
->backup
!= NULL
);
1122 if (strcmp(option
->value
, option
->backup
) != 0)
1126 static gboolean
check_anything_changed(void)
1128 gboolean retval
= FALSE
;
1130 g_hash_table_foreach(option_hash
, check_changed_cb
, &retval
);
1135 static void write_option(gpointer key
, gpointer value
, gpointer data
)
1137 xmlNodePtr doc
= (xmlNodePtr
) data
;
1138 Option
*option
= (Option
*) value
;
1141 tree
= xmlNewTextChild(doc
, NULL
, "Option", option
->value
);
1142 xmlSetProp(tree
, "name", (gchar
*) key
);
1145 static void save_options(void)
1149 guchar
*save
, *save_new
;
1151 save
= choices_find_xdg_path_save("Options", PROJECT
, SITE
, TRUE
);
1155 save_new
= g_strconcat(save
, ".new", NULL
);
1157 doc
= xmlNewDoc("1.0");
1158 xmlDocSetRootElement(doc
, xmlNewDocNode(doc
, NULL
, "Options", NULL
));
1160 g_hash_table_foreach(option_hash
, write_option
,
1161 xmlDocGetRootElement(doc
));
1163 if (save_xml_file(doc
, save_new
) || rename(save_new
, save
))
1164 report_error(_("Error saving %s: %s"), save
, g_strerror(errno
));
1170 for (next
= saver_callbacks
; next
; next
= next
->next
)
1172 OptionNotify
*cb
= (OptionNotify
*) next
->data
;
1178 gtk_widget_destroy(window
);
1181 /* Make the widget reflect the current value of the option */
1182 static void update_cb(gpointer key
, gpointer value
, gpointer data
)
1184 Option
*option
= (Option
*) value
;
1186 g_return_if_fail(option
!= NULL
);
1187 g_return_if_fail(option
->widget
!= NULL
);
1191 if (option
->update_widget
)
1192 option
->update_widget(option
);
1197 /* Reflect the values in the Option structures by changing the widgets
1198 * in the Options window.
1200 static void update_option_widgets(void)
1202 g_hash_table_foreach(option_hash
, update_cb
, NULL
);
1205 /* Each of the following update the widget to make it show the current
1206 * value of the option.
1209 static void update_toggle(Option
*option
)
1211 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(option
->widget
),
1215 static void update_entry(Option
*option
)
1217 gtk_entry_set_text(GTK_ENTRY(option
->widget
), option
->value
);
1220 static void update_numentry(Option
*option
)
1222 gtk_spin_button_set_value(GTK_SPIN_BUTTON(option
->widget
),
1226 static void update_radio_group(Option
*option
)
1228 radio_group_set_value(GTK_RADIO_BUTTON(option
->widget
), option
->value
);
1231 static void update_slider(Option
*option
)
1233 gtk_adjustment_set_value(
1234 gtk_range_get_adjustment(GTK_RANGE(option
->widget
)),
1238 static void update_menu(Option
*option
)
1240 option_menu_set(GTK_OPTION_MENU(option
->widget
), option
->value
);
1243 static void update_font(Option
*option
)
1245 GtkToggleButton
*active
;
1246 gboolean have_font
= option
->value
[0] != '\0';
1248 active
= g_object_get_data(G_OBJECT(option
->widget
), "rox_override");
1252 gtk_toggle_button_set_active(active
, have_font
);
1253 gtk_widget_set_sensitive(option
->widget
->parent
, have_font
);
1256 gtk_label_set_text(GTK_LABEL(option
->widget
),
1257 have_font
? option
->value
1258 : (guchar
*) _("(use default)"));
1261 static void update_colour(Option
*option
)
1265 gdk_color_parse(option
->value
, &colour
);
1266 button_patch_set_colour(option
->widget
, &colour
);
1269 /* Each of these read_* calls get the new (string) value of an option
1273 static guchar
*read_toggle(Option
*option
)
1275 GtkToggleButton
*toggle
= GTK_TOGGLE_BUTTON(option
->widget
);
1277 return g_strdup_printf("%d", gtk_toggle_button_get_active(toggle
));
1280 static guchar
*read_entry(Option
*option
)
1282 return gtk_editable_get_chars(GTK_EDITABLE(option
->widget
), 0, -1);
1285 static guchar
*read_numentry(Option
*option
)
1287 return g_strdup_printf("%d", (int)
1288 gtk_spin_button_get_value(GTK_SPIN_BUTTON(option
->widget
)));
1291 static guchar
*read_slider(Option
*option
)
1293 return g_strdup_printf("%d", (int)
1294 gtk_range_get_adjustment(GTK_RANGE(option
->widget
))->value
);
1297 static guchar
*read_radio_group(Option
*option
)
1299 return radio_group_get_value(GTK_RADIO_BUTTON(option
->widget
));
1302 static guchar
*read_menu(Option
*option
)
1304 return g_strdup(option_menu_get(GTK_OPTION_MENU(option
->widget
)));
1307 static guchar
*read_font(Option
*option
)
1309 GtkToggleButton
*active
;
1311 active
= g_object_get_data(G_OBJECT(option
->widget
), "rox_override");
1312 if (active
&& !gtk_toggle_button_get_active(active
))
1313 return g_strdup("");
1315 return g_strdup(gtk_label_get_text(GTK_LABEL(option
->widget
)));
1318 static guchar
*read_colour(Option
*option
)
1320 GtkStyle
*style
= GTK_BIN(option
->widget
)->child
->style
;
1322 return g_strdup_printf("#%04x%04x%04x",
1323 style
->bg
[GTK_STATE_NORMAL
].red
,
1324 style
->bg
[GTK_STATE_NORMAL
].green
,
1325 style
->bg
[GTK_STATE_NORMAL
].blue
);
1328 static void set_not_changed(gpointer key
, gpointer value
, gpointer data
)
1330 Option
*option
= (Option
*) value
;
1332 option
->has_changed
= FALSE
;
1335 /* Builders for decorations (no corresponding option) */
1337 static GList
*build_label(Option
*option
, xmlNode
*node
, guchar
*label
)
1343 g_return_val_if_fail(option
== NULL
, NULL
);
1344 g_return_val_if_fail(label
== NULL
, NULL
);
1347 widget
= gtk_label_new(_(text
));
1350 help
= get_int(node
, "help");
1352 gtk_misc_set_alignment(GTK_MISC(widget
), 0, help
? 0.5 : 1);
1353 gtk_label_set_justify(GTK_LABEL(widget
), GTK_JUSTIFY_LEFT
);
1354 gtk_label_set_line_wrap(GTK_LABEL(widget
), TRUE
);
1358 GtkWidget
*hbox
, *image
, *align
, *spacer
;
1360 hbox
= gtk_hbox_new(FALSE
, 4);
1361 image
= gtk_image_new_from_stock(GTK_STOCK_DIALOG_INFO
,
1362 GTK_ICON_SIZE_BUTTON
);
1363 align
= gtk_alignment_new(0, 0, 0, 0);
1365 gtk_container_add(GTK_CONTAINER(align
), image
);
1366 gtk_box_pack_start(GTK_BOX(hbox
), align
, FALSE
, TRUE
, 0);
1367 gtk_box_pack_start(GTK_BOX(hbox
), widget
, FALSE
, TRUE
, 0);
1369 spacer
= gtk_event_box_new();
1370 gtk_widget_set_size_request(spacer
, 6, 6);
1372 return g_list_append(g_list_append(NULL
, hbox
), spacer
);
1375 return g_list_append(NULL
, widget
);
1378 static GList
*build_spacer(Option
*option
, xmlNode
*node
, guchar
*label
)
1382 g_return_val_if_fail(option
== NULL
, NULL
);
1383 g_return_val_if_fail(label
== NULL
, NULL
);
1385 eb
= gtk_event_box_new();
1386 gtk_widget_set_size_request(eb
, 12, 12);
1388 return g_list_append(NULL
, eb
);
1391 static GList
*build_frame(Option
*option
, xmlNode
*node
, guchar
*label
)
1393 GtkWidget
*nbox
, *frame
, *label_widget
;
1395 PangoAttrList
*list
;
1396 PangoAttribute
*attr
;
1398 g_return_val_if_fail(option
== NULL
, NULL
);
1399 g_return_val_if_fail(label
!= NULL
, NULL
);
1401 frame
= gtk_frame_new(_(label
));
1402 gtk_frame_set_shadow_type(GTK_FRAME(frame
), GTK_SHADOW_NONE
);
1404 /* Make the title bold */
1405 label_widget
= gtk_frame_get_label_widget(GTK_FRAME(frame
));
1406 attr
= pango_attr_weight_new(PANGO_WEIGHT_BOLD
);
1408 attr
->start_index
= 0;
1409 attr
->end_index
= -1;
1410 list
= pango_attr_list_new();
1411 pango_attr_list_insert(list
, attr
);
1412 gtk_label_set_attributes(GTK_LABEL(label_widget
), list
);
1414 nbox
= gtk_vbox_new(FALSE
, 0);
1415 gtk_container_set_border_width(GTK_CONTAINER(nbox
), 12);
1416 gtk_container_add(GTK_CONTAINER(frame
), nbox
);
1418 for (hw
= node
->xmlChildrenNode
; hw
; hw
= hw
->next
)
1419 if (hw
->type
== XML_ELEMENT_NODE
)
1420 build_widget(hw
, nbox
);
1422 return g_list_append(NULL
, frame
);
1425 /* These create new widgets in the options window and set the appropriate
1429 static GList
*build_toggle(Option
*option
, xmlNode
*node
, guchar
*label
)
1433 g_return_val_if_fail(option
!= NULL
, NULL
);
1435 toggle
= gtk_check_button_new_with_label(_(label
));
1437 may_add_tip(toggle
, node
);
1439 option
->update_widget
= update_toggle
;
1440 option
->read_widget
= read_toggle
;
1441 option
->widget
= toggle
;
1443 g_signal_connect_swapped(toggle
, "toggled",
1444 G_CALLBACK(option_check_widget
), option
);
1446 return g_list_append(NULL
, toggle
);
1449 static GList
*build_slider(Option
*option
, xmlNode
*node
, guchar
*label
)
1452 GtkWidget
*hbox
, *slide
, *label_wid
;
1458 g_return_val_if_fail(option
!= NULL
, NULL
);
1460 min
= get_int(node
, "min");
1461 max
= get_int(node
, "max");
1462 fixed
= get_int(node
, "fixed");
1463 showvalue
= get_int(node
, "showvalue");
1465 adj
= GTK_ADJUSTMENT(gtk_adjustment_new(0,
1466 min
, max
, 1, 10, 0));
1468 hbox
= gtk_hbox_new(FALSE
, 4);
1472 label_wid
= gtk_label_new(_(label
));
1473 gtk_misc_set_alignment(GTK_MISC(label_wid
), 0, 0.5);
1474 gtk_box_pack_start(GTK_BOX(hbox
), label_wid
, FALSE
, TRUE
, 0);
1475 add_to_size_group(node
, label_wid
);
1478 end
= xmlGetProp(node
, "end");
1481 gtk_box_pack_end(GTK_BOX(hbox
), gtk_label_new(_(end
)),
1486 slide
= gtk_hscale_new(adj
);
1489 gtk_widget_set_size_request(slide
, adj
->upper
, 24);
1492 gtk_scale_set_draw_value(GTK_SCALE(slide
), TRUE
);
1493 gtk_scale_set_value_pos(GTK_SCALE(slide
),
1495 gtk_scale_set_digits(GTK_SCALE(slide
), 0);
1498 gtk_scale_set_draw_value(GTK_SCALE(slide
), FALSE
);
1499 GTK_WIDGET_UNSET_FLAGS(slide
, GTK_CAN_FOCUS
);
1501 may_add_tip(slide
, node
);
1503 gtk_box_pack_start(GTK_BOX(hbox
), slide
, !fixed
, TRUE
, 0);
1505 option
->update_widget
= update_slider
;
1506 option
->read_widget
= read_slider
;
1507 option
->widget
= slide
;
1509 g_signal_connect_swapped(adj
, "value-changed",
1510 G_CALLBACK(option_check_widget
), option
);
1512 return g_list_append(NULL
, hbox
);
1515 static GList
*build_entry(Option
*option
, xmlNode
*node
, guchar
*label
)
1519 GtkWidget
*label_wid
;
1521 g_return_val_if_fail(option
!= NULL
, NULL
);
1523 hbox
= gtk_hbox_new(FALSE
, 4);
1527 label_wid
= gtk_label_new(_(label
));
1528 gtk_misc_set_alignment(GTK_MISC(label_wid
), 1.0, 0.5);
1529 gtk_box_pack_start(GTK_BOX(hbox
), label_wid
, FALSE
, TRUE
, 0);
1532 entry
= gtk_entry_new();
1533 gtk_box_pack_start(GTK_BOX(hbox
), entry
, TRUE
, TRUE
, 0);
1534 add_to_size_group(node
, entry
);
1535 may_add_tip(entry
, node
);
1537 option
->update_widget
= update_entry
;
1538 option
->read_widget
= read_entry
;
1539 option
->widget
= entry
;
1541 g_signal_connect_data(entry
, "changed",
1542 G_CALLBACK(option_check_widget
), option
,
1543 NULL
, G_CONNECT_AFTER
| G_CONNECT_SWAPPED
);
1545 return g_list_append(NULL
, hbox
);
1548 static GList
*build_numentry(Option
*option
, xmlNode
*node
, guchar
*label
)
1553 g_return_val_if_fail(option
!= NULL
, NULL
);
1555 min
= get_int(node
, "min");
1556 max
= get_int(node
, "max");
1557 step
= MAX(1, get_int(node
, "step"));
1559 adj
= gtk_adjustment_new(min
, min
, max
, step
, step
* 10, 1);
1561 return build_numentry_base(option
, node
, label
, GTK_ADJUSTMENT(adj
));
1564 static GList
*build_radio_group(Option
*option
, xmlNode
*node
, guchar
*label
)
1567 GtkWidget
*button
= NULL
;
1571 g_return_val_if_fail(option
!= NULL
, NULL
);
1573 for (rn
= node
->xmlChildrenNode
; rn
; rn
= rn
->next
)
1575 if (rn
->type
== XML_ELEMENT_NODE
)
1577 button
= build_radio(rn
, button
);
1578 g_signal_connect_swapped(button
, "toggled",
1579 G_CALLBACK(option_check_widget
), option
);
1580 list
= g_list_append(list
, button
);
1584 option
->update_widget
= update_radio_group
;
1585 option
->read_widget
= read_radio_group
;
1586 option
->widget
= button
;
1588 cols
= get_int(node
, "columns");
1596 n
= g_list_length(list
);
1597 rows
= (n
+ cols
- 1) / cols
;
1599 table
= gtk_table_new(rows
, cols
, FALSE
);
1602 for (next
= list
; next
; next
= next
->next
)
1604 GtkWidget
*button
= GTK_WIDGET(next
->data
);
1605 int left
= i
/ rows
;
1608 gtk_table_attach_defaults(GTK_TABLE(table
), button
,
1609 left
, left
+ 1, top
, top
+ 1);
1615 list
= g_list_prepend(NULL
, table
);
1621 static GList
*build_colour(Option
*option
, xmlNode
*node
, guchar
*label
)
1623 GtkWidget
*hbox
, *da
, *button
, *label_wid
;
1625 g_return_val_if_fail(option
!= NULL
, NULL
);
1627 hbox
= gtk_hbox_new(FALSE
, 4);
1631 label_wid
= gtk_label_new(_(label
));
1632 gtk_misc_set_alignment(GTK_MISC(label_wid
), 1.0, 0.5);
1633 gtk_box_pack_start(GTK_BOX(hbox
), label_wid
, TRUE
, TRUE
, 0);
1636 button
= gtk_button_new();
1637 da
= gtk_drawing_area_new();
1638 gtk_widget_set_size_request(da
, 64, 12);
1639 gtk_container_add(GTK_CONTAINER(button
), da
);
1640 g_signal_connect(button
, "clicked", G_CALLBACK(open_coloursel
), option
);
1642 may_add_tip(button
, node
);
1644 gtk_box_pack_start(GTK_BOX(hbox
), button
, FALSE
, TRUE
, 0);
1646 option
->update_widget
= update_colour
;
1647 option
->read_widget
= read_colour
;
1648 option
->widget
= button
;
1650 return g_list_append(NULL
, hbox
);
1653 static GList
*build_menu(Option
*option
, xmlNode
*node
, guchar
*label
)
1655 GtkWidget
*hbox
, *om
, *option_menu
, *label_wid
;
1658 g_return_val_if_fail(option
!= NULL
, NULL
);
1660 hbox
= gtk_hbox_new(FALSE
, 4);
1662 label_wid
= gtk_label_new(_(label
));
1663 gtk_misc_set_alignment(GTK_MISC(label_wid
), 1.0, 0.5);
1664 gtk_box_pack_start(GTK_BOX(hbox
), label_wid
, TRUE
, TRUE
, 0);
1666 option_menu
= gtk_option_menu_new();
1667 gtk_box_pack_start(GTK_BOX(hbox
), option_menu
, FALSE
, TRUE
, 0);
1669 om
= gtk_menu_new();
1670 gtk_option_menu_set_menu(GTK_OPTION_MENU(option_menu
), om
);
1672 add_to_size_group(node
, option_menu
);
1674 for (item
= node
->xmlChildrenNode
; item
; item
= item
->next
)
1676 if (item
->type
== XML_ELEMENT_NODE
)
1677 build_menu_item(item
, option_menu
);
1680 option
->update_widget
= update_menu
;
1681 option
->read_widget
= read_menu
;
1682 option
->widget
= option_menu
;
1684 g_signal_connect_data(option_menu
, "changed",
1685 G_CALLBACK(option_check_widget
), option
,
1686 NULL
, G_CONNECT_AFTER
| G_CONNECT_SWAPPED
);
1688 return g_list_append(NULL
, hbox
);
1691 static GList
*build_font(Option
*option
, xmlNode
*node
, guchar
*label
)
1693 GtkWidget
*hbox
, *button
;
1694 GtkWidget
*active
= NULL
;
1697 g_return_val_if_fail(option
!= NULL
, NULL
);
1699 override
= get_int(node
, "override");
1701 hbox
= gtk_hbox_new(FALSE
, 4);
1705 /* Add a check button to enable the font chooser. If off,
1706 * the option's value is "".
1708 active
= gtk_check_button_new_with_label(_(label
));
1709 gtk_box_pack_start(GTK_BOX(hbox
), active
, FALSE
, TRUE
, 0);
1710 g_signal_connect(active
, "toggled",
1711 G_CALLBACK(toggle_active_font
), option
);
1714 gtk_box_pack_start(GTK_BOX(hbox
), gtk_label_new(_(label
)),
1717 button
= gtk_button_new_with_label("");
1718 gtk_box_pack_start(GTK_BOX(hbox
), button
, FALSE
, TRUE
, 0);
1720 option
->update_widget
= update_font
;
1721 option
->read_widget
= read_font
;
1722 option
->widget
= GTK_BIN(button
)->child
;
1723 may_add_tip(button
, node
);
1725 g_object_set_data(G_OBJECT(option
->widget
), "rox_override", active
);
1727 g_signal_connect(button
, "clicked", G_CALLBACK(open_fontsel
), option
);
1729 return g_list_append(NULL
, hbox
);
1732 static void button_patch_set_colour(GtkWidget
*button
, GdkColor
*colour
)
1737 patch
= GTK_BIN(button
)->child
;
1739 style
= gtk_style_copy(GTK_WIDGET(patch
)->style
);
1740 style
->bg
[GTK_STATE_NORMAL
].red
= colour
->red
;
1741 style
->bg
[GTK_STATE_NORMAL
].green
= colour
->green
;
1742 style
->bg
[GTK_STATE_NORMAL
].blue
= colour
->blue
;
1743 gtk_widget_set_style(patch
, style
);
1744 g_object_unref(G_OBJECT(style
));
1746 if (GTK_WIDGET_REALIZED(patch
))
1747 gdk_window_clear(patch
->window
);
1750 static void load_options(xmlDoc
*doc
)
1752 xmlNode
*root
, *node
;
1754 root
= xmlDocGetRootElement(doc
);
1756 g_return_if_fail(strcmp(root
->name
, "Options") == 0);
1758 for (node
= root
->xmlChildrenNode
; node
; node
= node
->next
)
1760 gchar
*value
, *name
;
1762 if (node
->type
!= XML_ELEMENT_NODE
)
1764 if (strcmp(node
->name
, "Option") != 0)
1766 name
= xmlGetProp(node
, "name");
1770 value
= xmlNodeGetContent(node
);
1772 if (g_hash_table_lookup(loading
, name
))
1773 g_warning("Duplicate option found!");
1775 g_hash_table_insert(loading
, name
, value
);
1777 /* (don't need to free name or value) */
1781 /* Process one line from the options file (\0 term'd).
1782 * Returns NULL on success, or a pointer to an error message.
1783 * The line is modified.
1785 static const char *process_option_line(gchar
*line
)
1790 g_return_val_if_fail(option_hash
!= NULL
, "No registered options!");
1792 eq
= strchr(line
, '=');
1794 return _("Missing '='");
1797 while (c
> line
&& (*c
== ' ' || *c
== '\t'))
1801 while (*c
== ' ' || *c
== '\t')
1804 if (g_hash_table_lookup(loading
, name
))
1805 return "Duplicate option found!";
1807 g_hash_table_insert(loading
, g_strdup(name
), g_strdup(g_strstrip(c
)));