Adding upstream version 0.6.2.
[debian/lxappearance.git] / src / color-scheme.c
1 // color-scheme.c
2 //
3 // Copyright 2010 Hong Jen Yee (PCMan) <pcman.tw@gmail.com>
4 //
5 // This program is free software; you can redistribute it and/or modify
6 // it under the terms of the GNU General Public License as published by
7 // the Free Software Foundation; either version 2 of the License, or
8 // (at your option) any later version.
9 //
10 // This program is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 // GNU General Public License for more details.
14 //
15 // You should have received a copy of the GNU General Public License
16 // along with this program; if not, write to the Free Software
17 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
18 // MA 02110-1301, USA.
19
20 #include "lxappearance.h"
21 #include "color-scheme.h"
22 #include <string.h>
23 #include <glib/gi18n.h>
24
25 static GRegex* gtkrc_include_reg = NULL;
26 static GRegex* gtkrc_color_scheme_reg = NULL;
27
28 /* http://live.gnome.org/GnomeArt/Tutorials/GtkThemes/SymbolicColors/#Default_colors_in_GNOME */
29 static const char* gnome_color_names[] = {
30 "fg_color", /* The base for the foreground colors. */
31 "bg_color", /* Color to generate the background colors from. */
32 "base_color", /* The base color. */
33 "text_color", /* The text color in input widgets. */
34 "selected_bg_color", /* Color for the background of selected text. */
35 "selected_fg_color", /* Color of selected text. */
36 "tooltip_bg_color", /* Background color of tooltips. */
37 "tooltip_fg_color", /* Text color for text in tooltips. */
38 };
39
40 char* color_scheme_hash_to_str(GHashTable* hash)
41 {
42 GHashTableIter it;
43 char* key, *val;
44 GString* ret = g_string_sized_new(100);
45 g_hash_table_iter_init (&it, hash);
46 while(g_hash_table_iter_next(&it, (gpointer*)&key, (gpointer*)&val))
47 g_string_append_printf(ret, "%s:%s\n", key, val);
48 return g_string_free(ret, FALSE);
49 }
50
51 void color_scheme_str_to_hash(GHashTable* hash, const char* color_str)
52 {
53 /* g_debug("color_str: %s", color_str); */
54 /* split color scheme string into key/value pairs */
55 char** pairs = g_strsplit_set(color_str, "\n;", -1);
56 char** pair;
57 for(pair = pairs; *pair; ++pair)
58 {
59 char* name = strtok(*pair, ": \t");
60 /* g_debug("color_name = %s", name); */
61 if(name)
62 {
63 char* val = strtok(NULL, " \t");
64 if(val)
65 g_hash_table_replace(hash, g_strdup(name), g_strdup(val));
66 }
67 }
68 g_strfreev(pairs);
69 }
70
71 static void on_color_set(GtkColorButton* btn, const char* color_name)
72 {
73 GdkColor clr;
74 char* color_str;
75 gtk_color_button_get_color(btn, &clr);
76 color_str = gdk_color_to_string(&clr);
77
78 g_hash_table_replace(app.color_scheme_hash, g_strdup(color_name), color_str);
79 g_free(app.color_scheme);
80 app.color_scheme = color_scheme_hash_to_str(app.color_scheme_hash);
81
82 g_object_set(gtk_settings_get_default(), "gtk-color-scheme", app.color_scheme, NULL);
83
84 lxappearance_changed();
85 }
86
87 static void update_color_buttons()
88 {
89 int i;
90 /* set the color to buttons */
91 GHashTable* hash;
92
93 /* if custom color scheme is not used, use the default one. */
94 if(app.color_scheme)
95 hash = app.color_scheme_hash;
96 else
97 hash = app.default_color_scheme_hash;
98
99 for(i = 0; i < 8; ++i)
100 {
101 GtkWidget* btn = app.color_btns[i];
102 const char* color_name = gnome_color_names[i];
103 const char* color_str = (const char*)g_hash_table_lookup(hash, color_name);
104 /* g_debug("%s ='%s'", gnome_color_names[i], color_str); */
105 if(color_str)
106 {
107 GdkColor clr;
108 if(gdk_color_parse(color_str, &clr))
109 {
110 /* prevent invoking color-set handlers here. */
111 g_signal_handlers_block_by_func(btn, on_color_set, (gpointer)color_name);
112 gtk_color_button_set_color(GTK_COLOR_BUTTON(btn), &clr);
113 g_signal_handlers_unblock_by_func(btn, on_color_set, (gpointer)color_name);
114 }
115 gtk_widget_set_sensitive(btn, TRUE);
116 }
117 else
118 gtk_widget_set_sensitive(btn, FALSE);
119 }
120 }
121
122 static void hash_table_copy(GHashTable* dest, GHashTable* src)
123 {
124 GHashTableIter it;
125 char* key, *val;
126 g_hash_table_remove_all(dest);
127 g_hash_table_iter_init(&it, src);
128 while(g_hash_table_iter_next(&it, (gpointer*)&key, (gpointer*)&val))
129 g_hash_table_insert(dest, g_strdup(key), g_strdup(val));
130 }
131
132 static void on_custom_color_toggled(GtkToggleButton* btn, gpointer user_data)
133 {
134 g_free(app.color_scheme);
135 if(gtk_toggle_button_get_active(btn)) /* use customized color scheme. */
136 {
137 gtk_widget_set_sensitive(app.color_table, TRUE);
138 /* copy default colors to custom color hash table */
139 hash_table_copy(app.color_scheme_hash, app.default_color_scheme_hash);
140 app.color_scheme = color_scheme_hash_to_str(app.color_scheme_hash);
141 g_object_set(gtk_settings_get_default(), "gtk-color-scheme", app.color_scheme, NULL);
142 }
143 else /* use default colors provided by the theme. */
144 {
145 char* color_scheme_str;
146 gtk_widget_set_sensitive(app.color_table, FALSE);
147 /* restore default colors */
148 app.color_scheme = NULL;
149 g_hash_table_remove_all(app.color_scheme_hash);
150 if(g_hash_table_size(app.default_color_scheme_hash) > 0)
151 color_scheme_str = color_scheme_hash_to_str(app.default_color_scheme_hash);
152 else
153 color_scheme_str = g_strdup("");
154 g_object_set(gtk_settings_get_default(), "gtk-color-scheme", color_scheme_str, NULL);
155 g_free(color_scheme_str);
156 }
157 update_color_buttons();
158
159 lxappearance_changed();
160 }
161
162 void color_scheme_init(GtkBuilder* b)
163 {
164 int i;
165
166 app.color_table = GTK_WIDGET(gtk_builder_get_object(b, "color_table"));
167 app.custom_colors = GTK_WIDGET(gtk_builder_get_object(b, "custom_colors"));
168 app.no_custom_colors = GTK_WIDGET(gtk_builder_get_object(b, "no_custom_colors"));
169
170 /* toggle the check box if we have custom color scheme */
171 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(app.custom_colors), app.color_scheme != NULL);
172 g_signal_connect(app.custom_colors, "toggled", G_CALLBACK(on_custom_color_toggled), NULL);
173
174 /* hash table of the default color scheme of currently selected theme. */
175 app.default_color_scheme_hash = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
176
177 for(i = 0; i < 8; ++i)
178 app.color_btns[i] = GTK_WIDGET(gtk_builder_get_object(b, gnome_color_names[i]));;
179
180 /* update color scheme page for currently selected gtk theme. */
181 color_scheme_update();
182
183 for(i = 0; i < 8; ++i)
184 g_signal_connect(app.color_btns[i], "color-set", G_CALLBACK(on_color_set), (gpointer)gnome_color_names[i]);
185 }
186
187 /* return FALSE when the gtkrc file does not exists. */
188 gboolean gtkrc_file_get_color_scheme(const char* gtkrc_file, GHashTable* hash)
189 {
190 char* content;
191 if(G_UNLIKELY(!gtkrc_include_reg)) /* if regexp object is not yet created */
192 {
193 /* regular expressions used to parse gtkrc files */
194 gtkrc_include_reg = g_regex_new(
195 "[\\s]*include[\\s]+(\"([^\"]+)\"|'([^']+)')",
196 G_REGEX_MULTILINE|G_REGEX_OPTIMIZE, 0, NULL);
197
198 gtkrc_color_scheme_reg = g_regex_new(
199 "[\\s]*(gtk-color-scheme|gtk_color_scheme)[\\s]*=[\\s]*(\"([^\"]+)\"|'([^']+)')",
200 G_REGEX_MULTILINE|G_REGEX_OPTIMIZE, 0, NULL);
201 }
202
203 /* g_debug("check: %s", gtkrc_file); */
204 if(g_file_get_contents(gtkrc_file, &content, NULL, NULL))
205 {
206 GMatchInfo* match_info;
207
208 /* find gtkrc files included in this file. */
209 g_regex_match(gtkrc_include_reg, content, 0, &match_info);
210 while(g_match_info_matches (match_info))
211 {
212 gchar* include = g_match_info_fetch(match_info, 2);
213 /* try to load color schemes in every included gtkrc file. */
214 if(!g_path_is_absolute(include)) /* make a full path when needed. */
215 {
216 char* dirname = g_path_get_dirname(gtkrc_file);
217 char* file = g_build_filename(dirname, include, NULL);
218 g_free(dirname);
219 g_free(include);
220 include = file;
221 }
222 gtkrc_file_get_color_scheme(include, hash);
223 g_free(include);
224 g_match_info_next(match_info, NULL);
225 }
226
227 /* try to extract gtk-color-scheme from the gtkrc file. */
228 g_regex_match(gtkrc_color_scheme_reg, content, 0, &match_info);
229 while(g_match_info_matches (match_info))
230 {
231 char *color_scheme_str = g_match_info_fetch(match_info, 3);
232 /* need to unescape the string to replace "\\n" with "\n" */
233 char* unescaped = g_strcompress(color_scheme_str);
234 g_free (color_scheme_str);
235 color_scheme_str_to_hash(hash, unescaped);
236 g_free(unescaped);
237 g_match_info_next(match_info, NULL);
238 }
239 g_match_info_free(match_info);
240 g_free(content);
241 }
242 else
243 return FALSE;
244 return TRUE;
245 }
246
247 /* update the color scheme page for currently selected gtk theme.
248 * called when currently selected gtk theme gets changed. */
249 void color_scheme_update()
250 {
251 /* the current gtk theme gets changed.
252 * reload the default color scheme of current theme. */
253 g_hash_table_remove_all(app.default_color_scheme_hash);
254
255 if(app.widget_theme)
256 {
257 gboolean file_found;
258 char *gtkrc;
259
260 /* search in userdata theme dir first */
261 gtkrc = g_build_filename(g_get_user_data_dir(), "themes", app.widget_theme, "gtk-2.0/gtkrc", NULL);
262 file_found = gtkrc_file_get_color_scheme(gtkrc, app.default_color_scheme_hash);
263 g_free(gtkrc);
264 if (!file_found)
265 {
266 /* search in the home dir as old-style fallback */
267 gtkrc = g_build_filename(g_get_home_dir(), ".themes", app.widget_theme, "gtk-2.0/gtkrc", NULL);
268 /* if the theme is found in user-custom theme dir */
269 file_found = gtkrc_file_get_color_scheme(gtkrc, app.default_color_scheme_hash);
270 g_free(gtkrc);
271 }
272
273 if(!file_found)
274 {
275 /* if the theme is found in system-wide theme dir */
276 gtkrc = g_build_filename(gtk_rc_get_theme_dir(), app.widget_theme, "gtk-2.0/gtkrc", NULL);
277 gtkrc_file_get_color_scheme(gtkrc, app.default_color_scheme_hash);
278 g_free(gtkrc);
279 }
280 app.color_scheme_supported = (g_hash_table_size(app.default_color_scheme_hash) > 0);
281 }
282 else
283 app.color_scheme_supported = FALSE;
284
285 /* unfortunately we cannot set colors without XSETTINGS daemon,
286 themes will override any custom settings in .gtkrc-2.0 file */
287 /* FIXME: we should support other xsettings daemons too */
288 if(app.color_scheme_supported && app.use_lxsession)
289 {
290 gtk_widget_set_sensitive(app.custom_colors, TRUE);
291 gtk_widget_set_sensitive(app.color_table, app.color_scheme != NULL);
292 gtk_widget_hide(app.no_custom_colors);
293
294 /* if customized color scheme is not used,
295 * use default colors of the theme. */
296 if(!app.color_scheme)
297 {
298 char* color_scheme_str = color_scheme_hash_to_str(app.default_color_scheme_hash);
299 g_object_set(gtk_settings_get_default(), "gtk-color-scheme", color_scheme_str, NULL);
300 g_free(color_scheme_str);
301 }
302 }
303 else
304 {
305 gtk_widget_set_sensitive(app.color_table, FALSE);
306 gtk_widget_set_sensitive(app.custom_colors, FALSE);
307 if (app.color_scheme_supported)
308 gtk_label_set_text(GTK_LABEL(app.no_custom_colors),
309 _("Setting color scheme is not available without lxsession as session manager."));
310 else
311 gtk_label_set_text(GTK_LABEL(app.no_custom_colors),
312 _("Color scheme is not supported by currently selected widget theme."));
313 gtk_widget_show(app.no_custom_colors);
314 app.color_scheme_supported = FALSE;
315 }
316 /* set the color to buttons */
317 update_color_buttons();
318 }
319