20130427
[gdash.git] / src / gtk / gtkuisettings.cpp
blob33d8dc76d16a7ab281334a109a6dfd3c3c25b3c6
1 /*
2 * Copyright (c) 2007-2013, Czirkos Zoltan http://code.google.com/p/gdash/
4 * Permission is hereby granted, free of charge, to any person obtaining
5 * a copy of this software and associated documentation files (the
6 * "Software"), to deal in the Software without restriction, including
7 * without limitation the rights to use, copy, modify, merge, publish,
8 * distribute, sublicense, and/or sell copies of the Software, and to
9 * permit persons to whom the Software is furnished to do so, subject to
10 * the following conditions:
12 * The above copyright notice and this permission notice shall be
13 * included in all copies or substantial portions of the Software.
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
18 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
19 * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
20 * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24 #include "config.h"
26 #include <gtk/gtk.h>
27 #include <glib/gi18n.h>
28 #include "gtk/gtkui.hpp"
29 #include "gfx/cellrenderer.hpp"
30 #include "gtk/gtkuisettings.hpp"
31 #include "gtk/gtkpixbuffactory.hpp"
32 #include "settings.hpp"
33 #include "misc/printf.hpp"
34 #include "misc/autogfreeptr.hpp"
35 #include "framework/thememanager.hpp"
36 #ifdef HAVE_SDL
37 #include "framework/shadermanager.hpp"
38 #endif
40 #define GDASH_KEYSIM_WHAT_FOR "gdash-keysim-what-for"
41 #define GDASH_RESTAT_BOOL "gdash-restart-bool"
44 struct ThemeStuff {
45 GtkWidget *themecombo;
46 std::vector<std::string> themes;
47 } themestuff;
50 /* return a list of image gtk_image_filter's. */
51 /* they have floating reference. */
52 /* the list is to be freed by the caller. */
53 static GList *image_load_filters()
55 GSList *formats = gdk_pixbuf_get_formats();
56 GList *filters = NULL; /* new list of filters */
58 GtkFileFilter *all_filter = gtk_file_filter_new();
59 gtk_file_filter_set_name(all_filter, _("All image files"));
61 /* iterate the list of formats given by gdk. create file filters for each. */
62 for (GSList *iter = formats; iter!=NULL; iter=iter->next) {
63 GdkPixbufFormat *frm = (GdkPixbufFormat *) iter->data;
64 if (gdk_pixbuf_format_is_disabled(frm))
65 continue;
66 GtkFileFilter *filter=gtk_file_filter_new();
67 gtk_file_filter_set_name(filter, gdk_pixbuf_format_get_description(frm));
68 char **extensions=gdk_pixbuf_format_get_extensions(frm);
69 for (int i=0; extensions[i]!=NULL; i++) {
70 std::string pattern = SPrintf("*.%s") % extensions[i];
71 gtk_file_filter_add_pattern(filter, pattern.c_str());
72 gtk_file_filter_add_pattern(all_filter, pattern.c_str());
74 g_strfreev(extensions);
75 filters = g_list_append(filters, filter);
77 g_slist_free(formats);
79 /* add "all image files" filter */
80 filters = g_list_prepend(filters, all_filter);
82 return filters;
85 /* file open dialog, with filters for image types gdk-pixbuf recognizes. */
86 char *
87 gd_select_image_file(const char *title, GtkWidget *parent)
89 GtkWidget *dialog;
90 GList *filters;
91 GList *iter;
92 int result;
93 char *filename;
95 dialog=gtk_file_chooser_dialog_new (title, GTK_WINDOW(parent), GTK_FILE_CHOOSER_ACTION_OPEN, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT, NULL);
96 gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_ACCEPT);
98 /* obtain list of image filters, and add all to the window */
99 filters=image_load_filters();
100 for (iter=filters; iter!=NULL; iter=iter->next)
101 gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(dialog), GTK_FILE_FILTER(iter->data));
102 g_list_free(filters);
104 result=gtk_dialog_run(GTK_DIALOG(dialog));
105 if (result==GTK_RESPONSE_ACCEPT)
106 filename=gtk_file_chooser_get_filename (GTK_FILE_CHOOSER(dialog));
107 else
108 filename=NULL;
109 gtk_widget_destroy (dialog);
111 return filename;
114 static void add_theme_cb(GtkWidget *widget, gpointer data) {
115 ThemeStuff *themestuff = (ThemeStuff *) data;
117 char *filename = gd_select_image_file(_("Add Theme from Image File"), gtk_widget_get_toplevel(widget));
119 if (filename == NULL)
120 return;
122 GTKPixbufFactory gpf;
123 bool ok = CellRenderer::is_image_ok_for_theme(gpf, filename);
124 if (ok) {
125 /* make up new filename */
126 AutoGFreePtr<char> basename(g_path_get_basename(filename));
127 AutoGFreePtr<char> new_filename(g_build_path(G_DIR_SEPARATOR_S, gd_user_config_dir.c_str(), (char*) basename, NULL));
129 /* if file not exists, or exists BUT overwrite allowed */
130 if (!g_file_test(new_filename, G_FILE_TEST_EXISTS) || gd_question_yesno(_("Overwrite file?"), new_filename)) {
131 /* copy theme to user config directory */
132 try {
133 GError *error = NULL;
134 GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file(filename, NULL);
135 if (error)
136 throw error;
137 gdk_pixbuf_save(pixbuf, new_filename, "png", &error, "compression", "9", NULL);
138 if (error)
139 throw error;
140 g_object_unref(pixbuf);
141 AutoGFreePtr<char> thm(g_filename_display_basename(new_filename));
142 if (strrchr(thm, '.')) /* remove extension */
143 *strrchr(thm, '.') = '\0';
144 gtk_combo_box_append_text(GTK_COMBO_BOX(themestuff->themecombo), thm);
145 themestuff->themes.push_back(std::string(new_filename));
146 gd_infomessage(_("The new theme is installed."), thm);
147 } catch (GError *error) {
148 gd_errormessage(error->message, NULL);
149 g_error_free(error);
153 else
154 gd_errormessage(_("The selected image cannot be used as a GDash theme."), NULL);
155 g_free(filename);
159 gboolean SettingsWindow::keysim_button_keypress_event(GtkWidget *widget, GdkEventKey *event, gpointer data) {
160 g_assert(event->type == GDK_KEY_PRESS); /* must be true. */
161 gtk_dialog_response(GTK_DIALOG(widget), event->keyval);
162 return TRUE; /* and say that we processed the key. */
166 void SettingsWindow::keysim_button_clicked_cb(GtkWidget *button, gpointer data) {
167 const char *what_for = (const char *)g_object_get_data(G_OBJECT(button), GDASH_KEYSIM_WHAT_FOR);
168 int *keyval = (int *) data;
170 /* dialog which has its keypress event connected to the handler above */
171 // TRANSLATORS: Title text capitalization in English
172 GtkWidget *dialog = gtk_dialog_new_with_buttons(_("Select Key"), GTK_WINDOW(gtk_widget_get_toplevel(button)),
173 GtkDialogFlags(GTK_DIALOG_MODAL | GTK_DIALOG_NO_SEPARATOR),
174 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, NULL);
175 GtkWidget *table = gtk_table_new(1, 1, FALSE);
176 gtk_table_set_row_spacings(GTK_TABLE(table), 6);
177 gtk_table_set_col_spacings(GTK_TABLE(table), 6);
178 gtk_container_set_border_width(GTK_CONTAINER(table), 6);
179 gtk_box_pack_start_defaults(GTK_BOX(GTK_DIALOG(dialog)->vbox), table);
180 gtk_table_attach_defaults(GTK_TABLE(table), gd_label_new_leftaligned(_("Press key for action:")), 0, 1, 0, 1);
181 gtk_table_attach_defaults(GTK_TABLE(table), gd_label_new_leftaligned(CPrintf("<b>%s</b>") % what_for), 0, 1, 1, 2);
182 g_signal_connect(G_OBJECT(dialog), "key_press_event", G_CALLBACK(keysim_button_keypress_event), dialog);
184 gtk_window_set_resizable(GTK_WINDOW(dialog), FALSE);
185 gtk_widget_show_all(dialog);
186 int result = gtk_dialog_run(GTK_DIALOG(dialog));
187 if (result >= 0) {
188 /* if positive, it must be a keyval. gtk_response_cancel and gtk_response delete is negative. */
189 *keyval = result;
190 gtk_button_set_label(GTK_BUTTON(button), gdk_keyval_name(*keyval));
192 gtk_widget_destroy(dialog);
196 GtkWidget *SettingsWindow::gd_keysim_button(Setting *setting) {
197 char const *what_for = setting->name;
198 int *keyval = (int *) setting->var;
199 g_assert(keyval != NULL);
201 /* the button shows the current value in its name */
202 GtkWidget *button = gtk_button_new_with_label(gdk_keyval_name(*keyval));
203 g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(keysim_button_clicked_cb), keyval);
204 g_object_set_data(G_OBJECT(button), GDASH_KEYSIM_WHAT_FOR, (gpointer) what_for);
205 gtk_widget_set_tooltip_text(button, CPrintf(_("Click here to set the key for action: %s")) % what_for);
207 return button;
211 /* settings window */
212 void SettingsWindow::bool_toggle(GtkWidget *widget, gpointer data) {
213 bool *restart_bool = (bool *)g_object_get_data(G_OBJECT(widget), GDASH_RESTAT_BOOL);
214 Setting *setting = static_cast<Setting *>(data);
215 bool *bl = (bool *) setting->var;
217 *bl = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget));
218 if (setting->restart && restart_bool)
219 *restart_bool = true;
223 void SettingsWindow::int_change(GtkWidget *widget, gpointer data) {
224 bool *restart_bool = (bool *)g_object_get_data(G_OBJECT(widget), GDASH_RESTAT_BOOL);
225 Setting *setting = static_cast<Setting *>(data);
226 int *value = (int *) setting->var;
227 *value = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(widget));
228 if (setting->restart && restart_bool)
229 *restart_bool = true;
233 void SettingsWindow::stringv_change(GtkWidget *widget, gpointer data) {
234 bool *restart_bool = (bool *)g_object_get_data(G_OBJECT(widget), GDASH_RESTAT_BOOL);
235 Setting *setting = static_cast<Setting *>(data);
236 int *ptr = (int *) setting->var;
237 *ptr = gtk_combo_box_get_active(GTK_COMBO_BOX(widget));
238 /* if nothing selected (for some reason), set to zero. */
239 if (*ptr == -1)
240 *ptr = 0;
241 if (setting->restart && restart_bool)
242 *restart_bool = true;
246 void SettingsWindow::theme_change(GtkWidget *widget, gpointer data) {
247 bool *restart_bool = (bool *)g_object_get_data(G_OBJECT(widget), GDASH_RESTAT_BOOL);
248 int *ptr = (int *) data;
249 *ptr = gtk_combo_box_get_active(GTK_COMBO_BOX(widget));
250 /* if nothing selected (for some reason), set to zero. */
251 if (*ptr == -1)
252 *ptr = 0;
253 if (restart_bool)
254 *restart_bool = true;
258 GtkWidget *SettingsWindow::combo_box_new_from_stringv(const char **str) {
259 GtkWidget *combo = gtk_combo_box_new_text();
260 for (unsigned i = 0; str[i] != NULL; i++)
261 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _(str[i])); // also translate
262 return combo;
266 GtkWidget *SettingsWindow::combo_box_new_from_themelist(std::vector<std::string> const &strings) {
267 GtkWidget *combo = gtk_combo_box_new_text();
268 for (unsigned i = 0; i != strings.size(); i++) {
269 if (strings[i] == "")
270 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("[Default]"));
271 else {
272 AutoGFreePtr<char> thm(g_filename_display_basename(strings[i].c_str()));
273 if (strrchr(thm, '.')) /* remove extension */
274 *strrchr(thm, '.') = '\0';
275 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), thm);
278 return combo;
284 * @return true, if a restart is required by some setting changed
286 bool SettingsWindow::do_settings_dialog(Setting *settings, PixbufFactory &pf) {
287 bool request_restart = false;
289 // TRANSLATORS: Title text capitalization in English
290 GtkWidget *dialog = gtk_dialog_new_with_buttons(_("GDash Preferences"), guess_active_toplevel(),
291 GTK_DIALOG_DESTROY_WITH_PARENT, NULL);
292 gtk_dialog_set_has_separator(GTK_DIALOG(dialog), FALSE);
294 /* notebook with tabs */
295 GtkWidget *notebook = gtk_notebook_new();
296 gtk_container_set_border_width(GTK_CONTAINER(notebook), 9);
297 gtk_box_pack_start_defaults(GTK_BOX(GTK_DIALOG(dialog)->vbox), notebook);
299 ThemeStuff themestuff;
300 themestuff.themecombo = NULL;
301 int themenum;
302 load_themes_list(pf, themestuff.themes, themenum);
303 gd_settings_array_prepare(settings, TypeTheme, themestuff.themes, &themenum);
305 #ifdef HAVE_SDL
306 std::vector<std::string> shaders;
307 int shadernum;
308 load_shaders_list(shaders, shadernum);
309 gd_settings_array_prepare(settings, TypeShader, shaders, &shadernum);
310 #endif
312 int row = 0;
313 GtkWidget *table = NULL;
314 for (unsigned i = 0; settings[i].name != NULL; i++) {
315 GtkWidget *widget = NULL;
317 switch (settings[i].type) {
318 case TypePage:
319 table = gtk_table_new(1, 1, FALSE);
320 gtk_container_set_border_width(GTK_CONTAINER(table), 9);
321 gtk_table_set_row_spacings(GTK_TABLE(table), 6);
322 gtk_table_set_col_spacings(GTK_TABLE(table), 12);
323 gtk_notebook_append_page(GTK_NOTEBOOK(notebook), table, gd_label_new_leftaligned(_(settings[i].name)));
324 row = 0;
325 break;
327 case TypeBoolean:
328 widget = gtk_check_button_new();
329 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(widget), *(bool *)settings[i].var);
330 gtk_table_attach(GTK_TABLE(table), widget, 1, 2, row, row + 1, GtkAttachOptions(GTK_EXPAND | GTK_FILL), GtkAttachOptions(0), 0, 0);
331 g_signal_connect(G_OBJECT(widget), "toggled", G_CALLBACK(SettingsWindow::bool_toggle), &settings[i]);
332 break;
334 case TypePercent:
335 widget = gtk_spin_button_new_with_range(0, 100, 5);
336 gtk_spin_button_set_value(GTK_SPIN_BUTTON(widget), *(int *)settings[i].var);
337 g_signal_connect(G_OBJECT(widget), "value-changed", G_CALLBACK(SettingsWindow::int_change), &settings[i]);
338 gtk_table_attach(GTK_TABLE(table), widget, 1, 2, row, row + 1, GtkAttachOptions(GTK_EXPAND | GTK_FILL), GtkAttachOptions(0), 0, 0);
339 break;
341 case TypeInteger:
342 widget = gtk_spin_button_new_with_range(settings[i].min, settings[i].max, 1);
343 gtk_spin_button_set_value(GTK_SPIN_BUTTON(widget), *(int *)settings[i].var);
344 g_signal_connect(G_OBJECT(widget), "value-changed", G_CALLBACK(SettingsWindow::int_change), &settings[i]);
345 gtk_table_attach(GTK_TABLE(table), widget, 1, 2, row, row + 1, GtkAttachOptions(GTK_EXPAND | GTK_FILL), GtkAttachOptions(0), 0, 0);
346 break;
348 case TypeTheme:
349 case TypeStringv:
350 case TypeShader:
351 widget = SettingsWindow::combo_box_new_from_stringv(settings[i].stringv);
352 gtk_combo_box_set_active(GTK_COMBO_BOX(widget), *(int *)settings[i].var);
353 g_signal_connect(G_OBJECT(widget), "changed", G_CALLBACK(SettingsWindow::stringv_change), &settings[i]);
354 gtk_table_attach(GTK_TABLE(table), widget, 1, 2, row, row + 1, GtkAttachOptions(GTK_EXPAND | GTK_FILL), GtkAttachOptions(0), 0, 0);
355 break;
357 case TypeKey:
358 widget = gd_keysim_button(&settings[i]);
359 gtk_table_attach(GTK_TABLE(table), widget, 1, 2, row, row + 1, GtkAttachOptions(GTK_EXPAND | GTK_FILL), GtkAttachOptions(0), 0, 0);
360 break;
363 if (widget) {
364 gtk_widget_set_tooltip_text(widget, _(settings[i].description));
365 g_object_set_data(G_OBJECT(widget), GDASH_RESTAT_BOOL, &request_restart);
366 GtkWidget *label = gd_label_new_leftaligned(_(settings[i].name));
367 gtk_table_attach(GTK_TABLE(table), label, 0, 1, row, row + 1, GtkAttachOptions(GTK_EXPAND | GTK_FILL), GtkAttachOptions(0), 0, 0);
368 gtk_label_set_mnemonic_widget(GTK_LABEL(label), widget);
371 if (settings[i].type == TypeTheme)
372 themestuff.themecombo = widget;
374 row ++;
377 /* add theme button */
378 if (themestuff.themecombo != NULL) {
379 GtkWidget *button = gtk_button_new_with_mnemonic(_("_Add theme"));
380 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->action_area), button, FALSE, FALSE, 0);
381 g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(add_theme_cb), &themestuff);
384 /* add close button */
385 gtk_dialog_add_button(GTK_DIALOG(dialog), GTK_STOCK_CLOSE, GTK_RESPONSE_ACCEPT);
387 gtk_widget_show_all(dialog);
388 gtk_dialog_run(GTK_DIALOG(dialog));
390 gtk_widget_destroy(dialog);
392 gd_theme = themestuff.themes[themenum];
393 gd_settings_array_unprepare(settings, TypeTheme);
394 #ifdef HAVE_SDL
395 gd_shader = shaders[shadernum];
396 gd_settings_array_unprepare(settings, TypeShader);
397 #endif
399 return request_restart;