.
1 /*
2 Copyright (C) 2015 Johan Mattsson
3
4 This library is free software; you can redistribute it and/or modify
5 it under the terms of the GNU Lesser General Public License as
6 published by the Free Software Foundation; either version 3 of the
7 License, or (at your option) any later version.
8
9 This library is distributed in the hope that it will be useful, but
10 WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 Lesser General Public License for more details.
13 */
14
15 using Bird;
16 using Cairo;
17
18 namespace BirdFont {
19
20 public class Theme : GLib.Object {
21
22 static Gee.HashMap<string, Color> colors;
23 public static Gee.ArrayList<string> color_list;
24 public static Gee.ArrayList<string> themes;
25 public static string current_theme;
26
27 public static void text_color (Text text, string name) {
28 Color c;
29
30 if (unlikely (!colors.has_key (name))) {
31 warning (@"Theme does not have a color for $name");
32 return;
33 }
34
35 c = colors.get (name);
36 text.set_source_rgba (c.r, c.g, c.b, c.a);
37 }
38
39 public static void color (Context cr, string name) {
40 Color c;
41
42 if (unlikely (!colors.has_key (name))) {
43 warning (@"Theme does not have a color for $name");
44 return;
45 }
46
47 c = colors.get (name);
48 cr.set_source_rgba (c.r, c.g, c.b, c.a);
49 }
50
51 public static void color_opacity (Context cr, string name, double opacity) {
52 Color c;
53
54 if (unlikely (!colors.has_key (name))) {
55 warning (@"Theme does not have a color for $name");
56 return;
57 }
58
59 c = colors.get (name);
60 cr.set_source_rgba (c.r, c.g, c.b, opacity);
61 }
62
63 public static void text_color_opacity (Text text, string name, double opacity) {
64 Color c;
65
66 if (unlikely (!colors.has_key (name))) {
67 warning (@"Theme does not have a color for $name");
68 return;
69 }
70
71 c = colors.get (name);
72 text.set_source_rgba (c.r, c.g, c.b, opacity);
73 }
74
75 public static Color get_color (string name) {
76 Color c;
77
78 if (unlikely (!colors.has_key (name))) {
79 warning (@"Theme does not have a color for $name");
80 return new Color (0, 0, 0, 1);
81 }
82
83 return colors.get (name);
84 }
85
86 public static void set_default_colors () {
87 current_theme = "";
88
89 color_list = new Gee.ArrayList<string> ();
90 colors = new Gee.HashMap<string, Color> ();
91 themes = new Gee.ArrayList<string> ();
92
93 Theme.set_default_color ("Stroke Color", 0, 0, 0, 1);
94 Theme.set_default_color ("Handle Color", 0, 0, 0, 1);
95 Theme.set_default_color ("Fill Color", 0.5, 0.5, 0.5, 1);
96
97 Theme.set_default_color ("Background 1", 1, 1, 1, 1);
98 Theme.set_default_color ("Background 2", 101 / 255.0, 108 / 255.0, 116 / 255.0, 1);
99 Theme.set_default_color ("Background 3", 38 / 255.0, 39 / 255.0, 43 / 255.0, 1);
100 Theme.set_default_color ("Background 4", 51 / 255.0, 54 / 255.0, 59 / 255.0, 1);
101 Theme.set_default_color ("Background 5", 0.3, 0.3, 0.3, 1);
102 Theme.set_default_color ("Background 6", 224/255.0, 224/255.0, 224/255.0, 1);
103 Theme.set_default_color ("Background 7", 56 / 255.0, 59 / 255.0, 65 / 255.0, 1);
104 Theme.set_default_color ("Background 8", 55/255.0, 55/255.0, 55/255.0, 1);
105 Theme.set_default_color ("Background 9", 72/255.0, 72/255.0, 72/255.0, 1);
106
107 Theme.set_default_color ("Foreground 1", 0, 0, 0, 1);
108 Theme.set_default_color ("Foreground 2", 101 / 255.0, 108 / 255.0, 116 / 255.0, 1);
109 Theme.set_default_color ("Foreground 3", 26 / 255.0, 30 / 255.0, 32 / 255.0, 1);
110 Theme.set_default_color ("Foreground 4", 40 / 255.0, 57 / 255.0, 65 / 255.0, 1);
111 Theme.set_default_color ("Foreground 5", 70 / 255.0, 77 / 255.0, 83 / 255.0, 1);
112
113 Theme.set_default_color ("Highlighted 1", 234 / 255.0, 77 / 255.0, 26 / 255.0, 1);
114
115 Theme.set_default_color ("Highlighted Guide", 0, 0, 0.3, 1);
116 Theme.set_default_color ("Guide 1", 0.7, 0.7, 0.8, 1);
117 Theme.set_default_color ("Guide 2", 0.7, 0, 0, 0.5);
118 Theme.set_default_color ("Guide 3", 120 / 255.0, 68 / 255.0, 120 / 255.0, 120 / 255.0);
119
120 Theme.set_default_color ("Grid",0.2, 0.6, 0.2, 0.2);
121
122 Theme.set_default_color ("Background Glyph", 0.2, 0.2, 0.2, 0.5);
123
124 Theme.set_default_color ("Tool Border 1", 38 / 255.0, 39 / 255.0, 43 / 255.0, 1);
125 Theme.set_default_color ("Tool Background 1", 14 / 255.0, 16 / 255.0, 17 / 255.0, 1);
126
127 Theme.set_default_color ("Tool Border 2", 38 / 255.0, 39 / 255.0, 43 / 255.0, 1);
128 Theme.set_default_color ("Tool Background 2", 26 / 255.0, 30 / 255.0, 32 / 255.0, 1);
129
130 Theme.set_default_color ("Tool Border 3", 38 / 255.0, 39 / 255.0, 43 / 255.0, 1);
131 Theme.set_default_color ("Tool Background 3", 44 / 255.0, 47 / 255.0, 51 / 255.0, 1);
132
133 Theme.set_default_color ("Tool Border 4", 38 / 255.0, 39 / 255.0, 43 / 255.0, 1);
134 Theme.set_default_color ("Tool Background 4", 33 / 255.0, 36 / 255.0, 39 / 255.0, 1);
135
136 Theme.set_default_color ("Button Foreground", 101 / 255.0, 108 / 255.0, 116 / 255.0, 1);
137
138 N_("Stroke Color");
139 N_("Handle Color");
140 N_("Fill Color");
141
142 N_("Background 1");
143 N_("Background 2");
144 N_("Background 3");
145 N_("Background 4");
146 N_("Background 5");
147 N_("Background 6");
148 N_("Background 7");
149 N_("Background 8");
150 N_("Background 9");
151
152 N_("Foreground 1");
153 N_("Foreground 2");
154 N_("Foreground 3");
155 N_("Foreground 4");
156 N_("Foreground 5");
157
158 N_("Highlighted 1");
159 N_("Highlighted Guide");
160
161 N_("Grid");
162
163 N_("Guide 1");
164 N_("Guide 2");
165 N_("Guide 3");
166
167 N_("Tool Border 1");
168 N_("Tool Background 1");
169 N_("Tool Border 2");
170 N_("Tool Background 2");
171 N_("Tool Border 3");
172 N_("Tool Background 3");
173 N_("Tool Border 4");
174 N_("Tool Background 4");
175
176 N_("Button Foreground");
177
178 add_theme_files ();
179 }
180
181 static void add_theme_files () {
182 FileEnumerator enumerator;
183 FileInfo? file_info;
184 string file_name;
185 File dir;
186
187 dir = BirdFont.get_settings_directory ();
188
189 themes.clear ();
190 themes.add ("default.theme");
191 themes.add ("high_contrast.theme");
192
193 try {
194 enumerator = dir.enumerate_children (FileAttribute.STANDARD_NAME, 0);
195 while ((file_info = enumerator.next_file ()) != null) {
196 file_name = ((!) file_info).get_name ();
197
198 if (file_name.has_suffix (".theme")) {
199 themes.add (file_name);
200 }
201 }
202 } catch (Error e) {
203 warning (e.message);
204 }
205 }
206
207 public static void set_default_color (string name, double r, double g, double b, double a) {
208 color_list.add (name);
209 colors.set (name, new Color (r, g, b, a));
210 write_theme (); // FIXME: don't overwrite color
211 }
212
213 public static void save_color (string name, double r, double g, double b, double a) {
214 colors.set (name, new Color (r, g, b, a));
215 write_theme ();
216 }
217
218 public static void load_theme (string theme_file) {
219 File default_theme;
220 File user_theme;
221
222 user_theme = get_child (BirdFont.get_settings_directory (), theme_file);
223 if (user_theme.query_exists ()) {
224 current_theme = theme_file;
225 parse_theme (user_theme);
226 return;
227 }
228
229 default_theme = SearchPaths.find_file (null, theme_file);
230 if (default_theme.query_exists ()) {
231 current_theme = theme_file;
232 parse_theme (default_theme);
233 return;
234 }
235
236 warning (@"Theme not found: $theme_file");
237 }
238
239 public static void write_theme () {
240 DataOutputStream os;
241 File file;
242 int i;
243 string base_name;
244
245 if (current_theme == "") {
246 warning ("No name for theme.");
247 return;
248 }
249
250 if (current_theme == "default.theme" || current_theme == "high_contrast.theme") {
251 current_theme = "custom.theme";
252
253 file = get_child (BirdFont.get_settings_directory (), current_theme);
254 i = 2;
255 base_name = "custom";
256 while (file.query_exists ()) {
257 current_theme = @"$(base_name)_theme_$(i).theme";
258 file = get_child (BirdFont.get_settings_directory (), current_theme);
259 i++;
260 }
261 }
262
263 file = get_child (BirdFont.get_settings_directory (), current_theme);
264
265 try {
266 if (file.query_exists ()) {
267 file.delete ();
268 }
269 } catch (GLib.Error e) {
270 warning (e.message);
271 }
272
273 try {
274 os = new DataOutputStream (file.create (FileCreateFlags.REPLACE_DESTINATION));
275 os.put_string ("""<?xml version="1.0" encoding="utf-8" standalone="yes"?>""");
276 os.put_string ("\n");
277
278 os.put_string ("<theme>\n");
279 foreach (string name in colors.keys) {
280 Color color = colors.get (name);
281
282 os.put_string ("\t<color ");
283
284 os.put_string (@"name=\"$(Markup.escape_text (name))\" ");
285 os.put_string (@"red=\"$(color.r)\" ");
286 os.put_string (@"green=\"$(color.g)\" ");
287 os.put_string (@"blue=\"$(color.b)\" ");
288 os.put_string (@"alpha=\"$(color.a)\"");
289
290 os.put_string ("/>\n");
291 }
292 os.put_string ("</theme>\n");
293
294 os.close ();
295 } catch (GLib.Error e) {
296 warning (e.message);
297 }
298
299 add_theme_files ();
300 }
301
302 static void parse_theme (File f) {
303 string xml_data;
304 XmlParser parser;
305
306 try {
307 FileUtils.get_contents((!) f.get_path (), out xml_data);
308 parser = new XmlParser (xml_data);
309 parse_colors (parser.get_root_tag ());
310 } catch (GLib.Error e) {
311 warning (e.message);
312 }
313 }
314
315 static void parse_colors (Tag tag) {
316 foreach (Tag t in tag) {
317 if (t.get_name () == "color") {
318 parse_color (t.get_attributes ());
319 }
320 }
321 }
322
323 static void parse_color (Attributes attributes) {
324 string name = "";
325 double r = 0;
326 double g = 0;
327 double b = 0;
328 double a = 1;
329
330 foreach (Attribute attr in attributes) {
331 if (attr.get_name () == "name") {
332 name = attr.get_content ();
333 }
334
335 if (attr.get_name () == "red") {
336 r = double.parse (attr.get_content ());
337 }
338
339 if (attr.get_name () == "green") {
340 g = double.parse (attr.get_content ());
341 }
342
343 if (attr.get_name () == "blue") {
344 b = double.parse (attr.get_content ());
345 }
346
347 if (attr.get_name () == "alpha") {
348 a = double.parse (attr.get_content ());
349 }
350 }
351
352 colors.set (name, new Color (r, g, b, a));
353 }
354
355 public static void add_new_theme (SettingsDisplay d) {
356 TextListener listener;
357
358 listener = new TextListener (t_("New theme"), "", t_("Set"));
359
360 listener.signal_text_input.connect ((text) => {
361 if (text != "") {
362 current_theme = text + ".theme";
363 themes.add (current_theme);
364 }
365 });
366
367 listener.signal_submit.connect (() => {
368 TabContent.hide_text_input ();
369 write_theme ();
370 d.create_setting_items ();
371 });
372
373 TabContent.show_text_input (listener);
374 }
375 }
376
377 }
378