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.
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"
37 #include "framework/shadermanager.hpp"
40 #define GDASH_KEYSIM_WHAT_FOR "gdash-keysim-what-for"
41 #define GDASH_RESTAT_BOOL "gdash-restart-bool"
45 GtkWidget
*themecombo
;
46 std::vector
<std::string
> themes
;
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
))
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
);
85 /* file open dialog, with filters for image types gdk-pixbuf recognizes. */
87 gd_select_image_file(const char *title
, GtkWidget
*parent
)
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
));
109 gtk_widget_destroy (dialog
);
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
)
122 GTKPixbufFactory gpf
;
123 bool ok
= CellRenderer::is_image_ok_for_theme(gpf
, filename
);
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 */
133 GError
*error
= NULL
;
134 GdkPixbuf
*pixbuf
= gdk_pixbuf_new_from_file(filename
, NULL
);
137 gdk_pixbuf_save(pixbuf
, new_filename
, "png", &error
, "compression", "9", NULL
);
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
);
154 gd_errormessage(_("The selected image cannot be used as a GDash theme."), NULL
);
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
));
188 /* if positive, it must be a keyval. gtk_response_cancel and gtk_response delete is negative. */
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
);
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. */
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. */
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
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]"));
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
);
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
;
302 load_themes_list(pf
, themestuff
.themes
, themenum
);
303 gd_settings_array_prepare(settings
, TypeTheme
, themestuff
.themes
, &themenum
);
306 std::vector
<std::string
> shaders
;
308 load_shaders_list(shaders
, shadernum
);
309 gd_settings_array_prepare(settings
, TypeShader
, shaders
, &shadernum
);
313 GtkWidget
*table
= NULL
;
314 for (unsigned i
= 0; settings
[i
].name
!= NULL
; i
++) {
315 GtkWidget
*widget
= NULL
;
317 switch (settings
[i
].type
) {
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
)));
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
]);
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);
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);
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);
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);
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
;
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
);
395 gd_shader
= shaders
[shadernum
];
396 gd_settings_array_unprepare(settings
, TypeShader
);
399 return request_restart
;