Releasing debian version 0.5.3-1.
[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
24 static GRegex* gtkrc_include_reg = NULL;
25 static GRegex* gtkrc_color_scheme_reg = NULL;
26
27 /* http://live.gnome.org/GnomeArt/Tutorials/GtkThemes/SymbolicColors/#Default_colors_in_GNOME */
28 static const char* gnome_color_names[] = {
29 "fg_color", /* The base for the foreground colors. */
30 "bg_color", /* Color to generate the background colors from. */
31 "base_color", /* The base color. */
32 "text_color", /* The text color in input widgets. */
33 "selected_bg_color", /* Color for the background of selected text. */
34 "selected_fg_color", /* Color of selected text. */
35 "tooltip_bg_color", /* Background color of tooltips. */
36 "tooltip_fg_color", /* Text color for text in tooltips. */
37 };
38
39 char* color_scheme_hash_to_str(GHashTable* hash)
40 {
41 GHashTableIter it;
42 char* key, *val;
43 GString* ret = g_string_sized_new(100);
44 g_hash_table_iter_init (&it, hash);
45 while(g_hash_table_iter_next(&it, (gpointer*)&key, (gpointer*)&val))
46 g_string_append_printf(ret, "%s:%s\n", key, val);
47 return g_string_free(ret, FALSE);
48 }
49
50 void color_scheme_str_to_hash(GHashTable* hash, const char* color_str)
51 {
52 /* g_debug("color_str: %s", color_str); */
53 /* split color scheme string into key/value pairs */
54 char** pairs = g_strsplit_set(color_str, "\n;", -1);
55 char** pair;
56 for(pair = pairs; *pair; ++pair)
57 {
58 char* name = strtok(*pair, ": \t");
59 /* g_debug("color_name = %s", name); */
60 if(name)
61 {
62 char* val = strtok(NULL, " \t");
63 if(val)
64 g_hash_table_replace(hash, g_strdup(name), g_strdup(val));
65 }
66 }
67 g_strfreev(pairs);
68 }
69
70 static void on_color_set(GtkColorButton* btn, const char* color_name)
71 {
72 GdkColor clr;
73 char* color_str;
74 gtk_color_button_get_color(btn, &clr);
75 color_str = gdk_color_to_string(&clr);
76
77 g_hash_table_replace(app.color_scheme_hash, g_strdup(color_name), color_str);
78 g_free(app.color_scheme);
79 app.color_scheme = color_scheme_hash_to_str(app.color_scheme_hash);
80
81 g_object_set(gtk_settings_get_default(), "gtk-color-scheme", app.color_scheme, NULL);
82
83 lxappearance_changed();
84 }
85
86 static void update_color_buttons()
87 {
88 int i;
89 /* set the color to buttons */
90 GHashTable* hash;
91
92 /* if custom color scheme is not used, use the default one. */
93 if(app.color_scheme)
94 hash = app.color_scheme_hash;
95 else
96 hash = app.default_color_scheme_hash;
97
98 for(i = 0; i < 8; ++i)
99 {
100 GtkWidget* btn = app.color_btns[i];
101 const char* color_name = gnome_color_names[i];
102 const char* color_str = (const char*)g_hash_table_lookup(hash, color_name);
103 /* g_debug("%s ='%s'", gnome_color_names[i], color_str); */
104 if(color_str)
105 {
106 GdkColor clr;
107 if(gdk_color_parse(color_str, &clr))
108 {
109 /* prevent invoking color-set handlers here. */
110 g_signal_handlers_block_by_func(btn, on_color_set, (gpointer)color_name);
111 gtk_color_button_set_color(GTK_COLOR_BUTTON(btn), &clr);
112 g_signal_handlers_unblock_by_func(btn, on_color_set, (gpointer)color_name);
113 }
114 gtk_widget_set_sensitive(btn, TRUE);
115 }
116 else
117 gtk_widget_set_sensitive(btn, FALSE);
118 }
119 }
120
121 static void hash_table_copy(GHashTable* dest, GHashTable* src)
122 {
123 GHashTableIter it;
124 char* key, *val;
125 g_hash_table_remove_all(dest);
126 g_hash_table_iter_init(&it, src);
127 while(g_hash_table_iter_next(&it, (gpointer*)&key, (gpointer*)&val))
128 g_hash_table_insert(dest, g_strdup(key), g_strdup(val));
129 }
130
131 static void on_custom_color_toggled(GtkToggleButton* btn, gpointer user_data)
132 {
133 g_free(app.color_scheme);
134 if(gtk_toggle_button_get_active(btn)) /* use customized color scheme. */
135 {
136 gtk_widget_set_sensitive(app.color_table, TRUE);
137 /* copy default colors to custom color hash table */
138 hash_table_copy(app.color_scheme_hash, app.default_color_scheme_hash);
139 app.color_scheme = color_scheme_hash_to_str(app.color_scheme_hash);
140 g_object_set(gtk_settings_get_default(), "gtk-color-scheme", app.color_scheme, NULL);
141 }
142 else /* use default colors provided by the theme. */
143 {
144 char* color_scheme_str;
145 gtk_widget_set_sensitive(app.color_table, FALSE);
146 /* restore default colors */
147 app.color_scheme = NULL;
148 g_hash_table_remove_all(app.color_scheme_hash);
149 if(g_hash_table_size(app.default_color_scheme_hash) > 0)
150 color_scheme_str = color_scheme_hash_to_str(app.default_color_scheme_hash);
151 else
152 color_scheme_str = g_strdup("");
153 g_object_set(gtk_settings_get_default(), "gtk-color-scheme", color_scheme_str, NULL);
154 g_free(color_scheme_str);
155 }
156 update_color_buttons();
157
158 lxappearance_changed();
159 }
160
161 void color_scheme_init(GtkBuilder* b)
162 {
163 int i;
164
165 app.color_table = GTK_WIDGET(gtk_builder_get_object(b, "color_table"));
166 app.custom_colors = GTK_WIDGET(gtk_builder_get_object(b, "custom_colors"));
167 app.no_custom_colors = GTK_WIDGET(gtk_builder_get_object(b, "no_custom_colors"));
168
169 /* toggle the check box if we have custom color scheme */
170 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(app.custom_colors), app.color_scheme != NULL);
171 g_signal_connect(app.custom_colors, "toggled", G_CALLBACK(on_custom_color_toggled), NULL);
172
173 /* hash table of the default color scheme of currently selected theme. */
174 app.default_color_scheme_hash = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
175
176 for(i = 0; i < 8; ++i)
177 app.color_btns[i] = GTK_WIDGET(gtk_builder_get_object(b, gnome_color_names[i]));;
178
179 /* update color scheme page for currently selected gtk theme. */
180 color_scheme_update();
181
182 for(i = 0; i < 8; ++i)
183 g_signal_connect(app.color_btns[i], "color-set", G_CALLBACK(on_color_set), (gpointer)gnome_color_names[i]);
184 }
185
186 /* return FALSE when the gtkrc file does not exists. */
187 gboolean gtkrc_file_get_color_scheme(const char* gtkrc_file, GHashTable* hash)
188 {
189 char* content;
190 if(G_UNLIKELY(!gtkrc_include_reg)) /* if regexp object is not yet created */
191 {
192 /* regular expressions used to parse gtkrc files */
193 gtkrc_include_reg = g_regex_new(
194 "[\\s]*include[\\s]+(\"([^\"]+)\"|'([^']+)')",
195 G_REGEX_MULTILINE|G_REGEX_OPTIMIZE, 0, NULL);
196
197 gtkrc_color_scheme_reg = g_regex_new(
198 "[\\s]*(gtk-color-scheme|gtk_color_scheme)[\\s]*=[\\s]*(\"([^\"]+)\"|'([^']+)')",
199 G_REGEX_MULTILINE|G_REGEX_OPTIMIZE, 0, NULL);
200 }
201
202 /* g_debug("check: %s", gtkrc_file); */
203 if(g_file_get_contents(gtkrc_file, &content, NULL, NULL))
204 {
205 GMatchInfo* match_info;
206
207 /* find gtkrc files included in this file. */
208 g_regex_match(gtkrc_include_reg, content, 0, &match_info);
209 while(g_match_info_matches (match_info))
210 {
211 gchar* include = g_match_info_fetch(match_info, 2);
212 /* try to load color schemes in every included gtkrc file. */
213 if(!g_path_is_absolute(include)) /* make a full path when needed. */
214 {
215 char* dirname = g_path_get_dirname(gtkrc_file);
216 char* file = g_build_filename(dirname, include, NULL);
217 g_free(dirname);
218 g_free(include);
219 include = file;
220 }
221 gtkrc_file_get_color_scheme(include, hash);
222 g_free(include);
223 g_match_info_next(match_info, NULL);
224 }
225
226 /* try to extract gtk-color-scheme from the gtkrc file. */
227 g_regex_match(gtkrc_color_scheme_reg, content, 0, &match_info);
228 while(g_match_info_matches (match_info))
229 {
230 char *color_scheme_str = g_match_info_fetch(match_info, 3);
231 /* need to unescape the string to replace "\\n" with "\n" */
232 char* unescaped = g_strcompress(color_scheme_str);
233 g_free (color_scheme_str);
234 color_scheme_str_to_hash(hash, unescaped);
235 g_free(unescaped);
236 g_match_info_next(match_info, NULL);
237 }
238 g_match_info_free(match_info);
239 g_free(content);
240 }
241 else
242 return FALSE;
243 return TRUE;
244 }
245
246 /* update the color scheme page for currently selected gtk theme.
247 * called when currently selected gtk theme gets changed. */
248 void color_scheme_update()
249 {
250 /* the current gtk theme gets changed.
251 * reload the default color scheme of current theme. */
252 g_hash_table_remove_all(app.default_color_scheme_hash);
253
254 if(app.widget_theme)
255 {
256 gboolean file_found;
257 char* gtkrc = g_build_filename(g_get_home_dir(), ".themes", app.widget_theme, "gtk-2.0/gtkrc", NULL);
258 /* if the theme is found in user-custom theme dir */
259 file_found = gtkrc_file_get_color_scheme(gtkrc, app.default_color_scheme_hash);
260 g_free(gtkrc);
261
262 if(!file_found)
263 {
264 /* if the theme is found in system-wide theme dir */
265 gtkrc = g_build_filename(gtk_rc_get_theme_dir(), app.widget_theme, "gtk-2.0/gtkrc", NULL);
266 gtkrc_file_get_color_scheme(gtkrc, app.default_color_scheme_hash);
267 g_free(gtkrc);
268 }
269 app.color_scheme_supported = (g_hash_table_size(app.default_color_scheme_hash) > 0);
270 }
271 else
272 app.color_scheme_supported = FALSE;
273
274 if(app.color_scheme_supported)
275 {
276 gtk_widget_set_sensitive(app.custom_colors, TRUE);
277 gtk_widget_set_sensitive(app.color_table, app.color_scheme != NULL);
278 gtk_widget_hide(app.no_custom_colors);
279
280 /* if customized color scheme is not used,
281 * use default colors of the theme. */
282 if(!app.color_scheme)
283 {
284 char* color_scheme_str = color_scheme_hash_to_str(app.default_color_scheme_hash);
285 g_object_set(gtk_settings_get_default(), "gtk-color-scheme", color_scheme_str, NULL);
286 g_free(color_scheme_str);
287 }
288 }
289 else
290 {
291 gtk_widget_set_sensitive(app.color_table, FALSE);
292 gtk_widget_set_sensitive(app.custom_colors, FALSE);
293 gtk_widget_show(app.no_custom_colors);
294 }
295 /* set the color to buttons */
296 update_color_buttons();
297 }
298