Eanble plugin support.
[lxde/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, color_name);
111 gtk_color_button_set_color(GTK_COLOR_BUTTON(btn), &clr);
112 g_signal_handlers_unblock_by_func(btn, on_color_set, 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 /* regular expressions used to parse gtkrc files */
165 gtkrc_include_reg = g_regex_new(
166 "[\\s]*include[\\s]+(\"([^\"]+)\"|'([^']+)')",
167 G_REGEX_MULTILINE|G_REGEX_OPTIMIZE, 0, NULL);
168
169 gtkrc_color_scheme_reg = g_regex_new(
170 "[\\s]*(gtk-color-scheme|gtk_color_scheme)[\\s]*=[\\s]*(\"([^\"]+)\"|'([^']+)')",
171 G_REGEX_MULTILINE|G_REGEX_OPTIMIZE, 0, NULL);
172
173 app.color_table = GTK_WIDGET(gtk_builder_get_object(b, "color_table"));
174 app.custom_colors = GTK_WIDGET(gtk_builder_get_object(b, "custom_colors"));
175 app.no_custom_colors = GTK_WIDGET(gtk_builder_get_object(b, "no_custom_colors"));
176
177 /* toggle the check box if we have custom color scheme */
178 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(app.custom_colors), app.color_scheme != NULL);
179 g_signal_connect(app.custom_colors, "toggled", G_CALLBACK(on_custom_color_toggled), NULL);
180
181 /* hash table of the default color scheme of currently selected theme. */
182 app.default_color_scheme_hash = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
183
184 for(i = 0; i < 8; ++i)
185 app.color_btns[i] = GTK_WIDGET(gtk_builder_get_object(b, gnome_color_names[i]));;
186
187 /* update color scheme page for currently selected gtk theme. */
188 color_scheme_update();
189
190 for(i = 0; i < 8; ++i)
191 g_signal_connect(app.color_btns[i], "color-set", G_CALLBACK(on_color_set), gnome_color_names[i]);
192 }
193
194 /* return FALSE when the gtkrc file does not exists. */
195 gboolean gtkrc_file_get_color_scheme(const char* gtkrc_file, GHashTable* hash)
196 {
197 char* content;
198 /* g_debug("check: %s", gtkrc_file); */
199 if(g_file_get_contents(gtkrc_file, &content, NULL, NULL))
200 {
201 GMatchInfo* match_info;
202
203 /* find gtkrc files included in this file. */
204 g_regex_match(gtkrc_include_reg, content, 0, &match_info);
205 while(g_match_info_matches (match_info))
206 {
207 gchar* include = g_match_info_fetch(match_info, 2);
208 /* try to load color schemes in every included gtkrc file. */
209 if(!g_path_is_absolute(include)) /* make a full path when needed. */
210 {
211 char* dirname = g_path_get_dirname(gtkrc_file);
212 char* file = g_build_filename(dirname, include, NULL);
213 g_free(dirname);
214 g_free(include);
215 include = file;
216 }
217 gtkrc_file_get_color_scheme(include, hash);
218 g_free(include);
219 g_match_info_next(match_info, NULL);
220 }
221
222 /* try to extract gtk-color-scheme from the gtkrc file. */
223 g_regex_match(gtkrc_color_scheme_reg, content, 0, &match_info);
224 while(g_match_info_matches (match_info))
225 {
226 char *color_scheme_str = g_match_info_fetch(match_info, 3);
227 /* need to unescape the string to replace "\\n" with "\n" */
228 char* unescaped = g_strcompress(color_scheme_str);
229 g_free (color_scheme_str);
230 color_scheme_str_to_hash(hash, unescaped);
231 g_free(unescaped);
232 g_match_info_next(match_info, NULL);
233 }
234 g_match_info_free(match_info);
235 g_free(content);
236 }
237 else
238 return FALSE;
239 return TRUE;
240 }
241
242 /* update the color scheme page for currently selected gtk theme.
243 * called when currently selected gtk theme gets changed. */
244 void color_scheme_update()
245 {
246 /* the current gtk theme gets changed.
247 * reload the default color scheme of current theme. */
248 g_hash_table_remove_all(app.default_color_scheme_hash);
249
250 if(app.widget_theme)
251 {
252 gboolean file_found;
253 char* gtkrc = g_build_filename(g_get_home_dir(), ".themes", app.widget_theme, "gtk-2.0/gtkrc", NULL);
254 /* if the theme is found in user-custom theme dir */
255 file_found = gtkrc_file_get_color_scheme(gtkrc, app.default_color_scheme_hash);
256 g_free(gtkrc);
257
258 if(!file_found)
259 {
260 /* if the theme is found in system-wide theme dir */
261 gtkrc = g_build_filename(gtk_rc_get_theme_dir(), app.widget_theme, "gtk-2.0/gtkrc", NULL);
262 gtkrc_file_get_color_scheme(gtkrc, app.default_color_scheme_hash);
263 g_free(gtkrc);
264 }
265 app.color_scheme_supported = (g_hash_table_size(app.default_color_scheme_hash) > 0);
266 }
267 else
268 app.color_scheme_supported = FALSE;
269
270 if(app.color_scheme_supported)
271 {
272 gtk_widget_set_sensitive(app.custom_colors, TRUE);
273 gtk_widget_set_sensitive(app.color_table, app.color_scheme != NULL);
274 gtk_widget_hide(app.no_custom_colors);
275
276 /* if customized color scheme is not used,
277 * use default colors of the theme. */
278 if(!app.color_scheme)
279 {
280 char* color_scheme_str = color_scheme_hash_to_str(app.default_color_scheme_hash);
281 g_object_set(gtk_settings_get_default(), "gtk-color-scheme", color_scheme_str, NULL);
282 g_free(color_scheme_str);
283 }
284 }
285 else
286 {
287 gtk_widget_set_sensitive(app.color_table, FALSE);
288 gtk_widget_set_sensitive(app.custom_colors, FALSE);
289 gtk_widget_show(app.no_custom_colors);
290 }
291 /* set the color to buttons */
292 update_color_buttons();
293 }
294