r69: Tidied up the options system a bit. Added drag-and-drop section (with
[rox-filer.git] / ROX-Filer / src / options.c
blobff9b7235c591269b4c1b56451d715bcab688eec0
1 /* vi: set cindent:
2 * $Id$
4 * ROX-Filer, filer for the ROX desktop project
5 * By Thomas Leonard, <tal197@ecs.soton.ac.uk>.
6 */
8 /* options.c - code for handling user choices */
10 #include <stdio.h>
11 #include <string.h>
12 #include <errno.h>
13 #include <glib.h>
14 #include <gtk/gtk.h>
16 #include "gui_support.h"
17 #include "choices.h"
18 #include "options.h"
20 /* Add OptionsSection structs to this list in your _init() functions */
21 GSList *options_sections = NULL;
23 static GtkWidget *window, *sections_vbox;
24 static FILE *save_file = NULL;
25 static GHashTable *option_hash = NULL;
27 /* Static prototypes */
28 static void save_options(GtkWidget *widget, gpointer data);
29 static char *process_option_line(char *line);
31 void options_init()
33 GtkWidget *tl_vbox, *scrolled_area;
34 GtkWidget *border, *label;
35 GtkWidget *actions, *button;
36 char *string, *save_path;
38 window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
39 gtk_window_set_title(GTK_WINDOW(window), "ROX-Filer options");
40 gtk_signal_connect(GTK_OBJECT(window), "delete_event",
41 GTK_SIGNAL_FUNC(hide_dialog_event), window);
42 gtk_container_set_border_width(GTK_CONTAINER(window), 4);
43 gtk_window_set_default_size(GTK_WINDOW(window), 400, 400);
45 tl_vbox = gtk_vbox_new(FALSE, 4);
46 gtk_container_add(GTK_CONTAINER(window), tl_vbox);
48 scrolled_area = gtk_scrolled_window_new(NULL, NULL);
49 gtk_container_set_border_width(GTK_CONTAINER(scrolled_area), 4);
50 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_area),
51 GTK_POLICY_NEVER, GTK_POLICY_ALWAYS);
52 gtk_box_pack_start(GTK_BOX(tl_vbox), scrolled_area, TRUE, TRUE, 0);
54 border = gtk_frame_new(NULL);
55 gtk_frame_set_shadow_type(GTK_FRAME(border), GTK_SHADOW_NONE);
56 gtk_container_set_border_width(GTK_CONTAINER(border), 4);
57 gtk_scrolled_window_add_with_viewport(
58 GTK_SCROLLED_WINDOW(scrolled_area), border);
60 sections_vbox = gtk_vbox_new(FALSE, 4);
61 gtk_container_add(GTK_CONTAINER(border), sections_vbox);
63 save_path = choices_find_path_save("...");
64 if (save_path)
66 string = g_strconcat("Choices will be saved as ",
67 save_path,
68 NULL);
69 label = gtk_label_new(string);
70 g_free(string);
72 else
73 label = gtk_label_new("Choices saving is disabled by "
74 "CHOICESPATH variable");
75 gtk_box_pack_start(GTK_BOX(tl_vbox), label, FALSE, TRUE, 0);
77 actions = gtk_hbox_new(TRUE, 16);
78 gtk_box_pack_start(GTK_BOX(tl_vbox), actions, FALSE, TRUE, 0);
80 button = gtk_button_new_with_label("Save");
81 gtk_box_pack_start(GTK_BOX(actions), button, FALSE, TRUE, 0);
82 if (!save_path)
83 gtk_widget_set_sensitive(button, FALSE);
84 gtk_signal_connect(GTK_OBJECT(button), "clicked",
85 GTK_SIGNAL_FUNC(save_options), (gpointer) TRUE);
87 button = gtk_button_new_with_label("OK");
88 gtk_box_pack_start(GTK_BOX(actions), button, FALSE, TRUE, 0);
89 gtk_signal_connect(GTK_OBJECT(button), "clicked",
90 GTK_SIGNAL_FUNC(save_options), (gpointer) FALSE);
92 button = gtk_button_new_with_label("Cancel");
93 gtk_box_pack_start(GTK_BOX(actions), button, FALSE, TRUE, 0);
94 gtk_signal_connect_object(GTK_OBJECT(button), "clicked",
95 GTK_SIGNAL_FUNC(gtk_widget_hide), GTK_OBJECT(window));
98 void options_load(void)
100 static gboolean need_init = TRUE;
101 char *path;
103 if (need_init)
105 GtkWidget *group;
106 GSList *next = options_sections;
108 while (next)
110 OptionsSection *section = (OptionsSection *) next->data;
112 group = gtk_frame_new(section->name);
113 gtk_box_pack_start(GTK_BOX(sections_vbox), group,
114 FALSE, TRUE, 0);
115 gtk_container_add(GTK_CONTAINER(group),
116 section->create());
117 next = next->next;
120 need_init = FALSE;
123 path = choices_find_path_load("options");
124 if (!path)
125 return; /* Nothing to load */
127 parse_file(path, process_option_line);
130 void parse_file(char *path, ParseFunc *parse_line)
132 char *data;
133 long length;
135 if (load_file(path, &data, &length))
137 char *eol, *error;
138 char *line = data;
139 int line_number = 1;
141 while (line && *line)
143 eol = strchr(line, '\n');
144 if (eol)
145 *eol = '\0';
147 error = parse_line(line);
149 if (error)
151 GString *message;
153 message = g_string_new(NULL);
154 g_string_sprintf(message,
155 "Error in options file at "
156 "line %d: %s", line_number,
157 error);
158 delayed_error("ROX-Filer", message->str);
159 g_string_free(message, TRUE);
160 break;
163 if (!eol)
164 break;
165 line = eol + 1;
166 line_number++;
168 g_free(data);
172 /* Call this on init to register a handler for a key.
173 * The function returns a pointer to an error messages (which will
174 * NOT be free()d, or NULL on success.
176 void option_register(char *key, OptionFunc *func)
178 if (!option_hash)
179 option_hash = g_hash_table_new(g_str_hash, g_str_equal);
180 g_hash_table_insert(option_hash, key, func);
183 /* Process one line from the options file (\0 term'd).
184 * Returns NULL on success, or a pointer to an error message.
185 * The line is modified.
187 static char *process_option_line(char *line)
189 char *eq, *c;
190 OptionFunc *func;
192 g_return_val_if_fail(option_hash != NULL, "No registered functions!");
194 eq = strchr(line, '=');
195 if (!eq)
196 return "Missing '='";
198 c = eq - 1;
199 while (c > line && (*c == ' ' || *c == '\t'))
200 c--;
201 c[1] = '\0';
202 c = eq + 1;
203 while (*c == ' ' || *c == '\t')
204 c++;
206 func = (OptionFunc *) g_hash_table_lookup(option_hash, line);
207 if (!func)
208 return "Bad key (no such option name)";
210 return func(c);
213 static void save_options(GtkWidget *widget, gpointer data)
215 gboolean save = (gboolean) data;
216 GSList *next = options_sections;
218 while (next)
220 OptionsSection *section = (OptionsSection *) next->data;
221 section->set();
222 next = next->next;
225 if (save)
227 char *path;
229 path = choices_find_path_save("options");
230 g_return_if_fail(path != NULL);
232 save_file = fopen(path, "wb");
233 if (!save_file)
235 char *str;
236 str = g_strconcat("Unable to open '", path,
237 "' for writing: ",
238 g_strerror(errno),
239 NULL);
240 report_error("ROX-Filer", str);
241 g_free(str);
242 return;
245 next = options_sections;
246 while (next)
248 OptionsSection *section = (OptionsSection *) next->data;
249 section->save();
250 next = next->next;
253 if (save_file && fclose(save_file) == EOF)
255 report_error("ROX-Filer", g_strerror(errno));
256 return;
260 gtk_widget_hide(window);
263 void options_show(FilerWindow *filer_window)
265 GSList *next = options_sections;
267 if (GTK_WIDGET_MAPPED(window))
268 gtk_widget_hide(window);
270 while (next)
272 OptionsSection *section = (OptionsSection *) next->data;
273 section->update();
274 next = next->next;
277 gtk_widget_show_all(window);
280 void option_write(char *name, char *value)
282 char *string;
283 int len;
285 if (!save_file)
286 return; /* Error already reported hopefully */
288 string = g_strconcat(name, " = ", value, "\n", NULL);
289 len = strlen(string);
290 if (fwrite(string, sizeof(char), len, save_file) < len)
292 delayed_error("Saving options", g_strerror(errno));
293 fclose(save_file);
294 save_file = NULL;
296 g_free(string);