Save the stylesheet
[colloquium.git] / src / stylesheet.c
1 /*
2  * stylesheet.c
3  *
4  * Copyright © 2013-2018 Thomas White <taw@bitwiz.org.uk>
5  *
6  * This file is part of Colloquium.
7  *
8  * Colloquium is free software: you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation, either version 3 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
20  *
21  */
22
23
24 #ifdef HAVE_CONFIG_H
25 #include <config.h>
26 #endif
27
28 #include <json-glib/json-glib.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <stdio.h>
32 #include <gio/gio.h>
33 #include <gdk/gdk.h>
34
35 #include "stylesheet.h"
36 #include "utils.h"
37
38
39 struct _stylesheet {
40         JsonNode *root;
41 };
42
43
44 static int find_comma(const char *a)
45 {
46         int i = 0;
47         int in_brackets = 0;
48         size_t len = strlen(a);
49
50         do {
51                 if ( (a[i] == ',') && !in_brackets ) return i;
52                 if ( a[i] == '(' ) in_brackets++;
53                 if ( a[i] == ')' ) in_brackets--;
54                 i++;
55         } while ( i < len );
56         return 0;
57 }
58
59
60 int parse_colour_duo(const char *a, GdkRGBA *col1, GdkRGBA *col2)
61 {
62         char *acopy;
63         int cpos;
64
65         acopy = strdup(a);
66         if ( acopy == NULL ) return 1;
67
68         cpos = find_comma(acopy);
69         if ( cpos == 0 ) {
70                 fprintf(stderr, _("Invalid bg gradient spec '%s'\n"), a);
71                 return 1;
72         }
73
74         acopy[cpos] = '\0';
75
76         if ( gdk_rgba_parse(col1, acopy) != TRUE ) {
77                 fprintf(stderr, _("Failed to parse colour: %s\n"), acopy);
78         }
79         if ( gdk_rgba_parse(col2, &acopy[cpos+1]) != TRUE ) {
80                 fprintf(stderr, _("Failed to parse colour: %s\n"), &acopy[cpos+1]);
81         }
82
83         free(acopy);
84         return 0;
85 }
86
87
88 Stylesheet *stylesheet_load(GFile *file)
89 {
90         JsonParser *parser;
91         gboolean r;
92         GError *err = NULL;
93         Stylesheet *ss;
94         char *everything;
95         gsize len;
96
97         printf("Trying stylesheet '%s'\n", g_file_get_uri(file));
98
99         ss = calloc(1, sizeof(Stylesheet));
100         if ( ss == NULL ) return NULL;
101
102         parser = json_parser_new();
103
104         if ( !g_file_load_contents(file, NULL, &everything, &len, NULL, NULL) ) {
105                 fprintf(stderr, _("Failed to load stylesheet '%s'\n"),
106                         g_file_get_uri(file));
107                 return NULL;
108         }
109
110         r = json_parser_load_from_data(parser, everything, len, &err);
111         if ( r == FALSE ) {
112                 fprintf(stderr, "Failed to load style sheet: '%s'\n", err->message);
113                 return NULL;
114         }
115
116         ss->root = json_parser_steal_root(parser);
117         g_object_unref(parser);
118
119         return ss;
120 }
121
122
123 static JsonObject *find_stylesheet_object(Stylesheet *ss, const char *path,
124                                           JsonNode **freeme)
125 {
126         JsonNode *node;
127         JsonObject *obj;
128         JsonArray *array;
129         GError *err = NULL;
130
131         node = json_path_query(path, ss->root, &err);
132         array = json_node_get_array(node);
133
134         if ( json_array_get_length(array) != 1 ) {
135                 json_node_unref(node);
136                 fprintf(stderr, "More than one result in SS lookup (%s)!\n", path);
137                 return NULL;
138         }
139
140         obj = json_array_get_object_element(array, 0);
141         if ( obj == NULL ) {
142                 printf("%s not a JSON object\n", path);
143                 json_node_unref(node);
144                 return NULL;
145         }
146
147         *freeme = node;
148         return obj;
149 }
150
151
152 char *stylesheet_lookup(Stylesheet *ss, const char *path, const char *key)
153 {
154         JsonObject *obj;
155         char *ret = NULL;
156         JsonNode *node = NULL;
157
158         if ( ss == NULL ) {
159                 fprintf(stderr, _("No stylesheet!\n"));
160                 return NULL;
161         }
162
163         obj = find_stylesheet_object(ss, path, &node);
164
165         if ( json_object_has_member(obj, key) ) {
166
167                 const gchar *v;
168                 v = json_object_get_string_member(obj, key);
169                 if ( v != NULL ) {
170                         ret = strdup(v);
171                 } else {
172                         fprintf(stderr, "Error retrieving %s.%s\n", path, key);
173                 }
174
175         } /* else not found, too bad */
176
177         if ( node != NULL ) json_node_unref(node);
178         return ret;
179 }
180
181
182 int stylesheet_set(Stylesheet *ss, const char *path, const char *key,
183                     const char *new_val)
184 {
185         JsonObject *obj;
186         JsonNode *node = NULL;
187         int r = 1;
188
189         if ( ss == NULL ) {
190                 fprintf(stderr, _("No stylesheet!\n"));
191                 return 1;
192         }
193
194         obj = find_stylesheet_object(ss, path, &node);
195         if ( obj != NULL ) {
196                 json_object_set_string_member(obj, key, new_val);
197                 r = 0;
198         } /* else most likely the object (e.g. "$.slide", "$.slide.frame",
199            * "$.narrative" etc doesn't exist */
200
201         if ( node != NULL ) json_node_unref(node);
202         return r;
203 }
204
205
206 int stylesheet_delete(Stylesheet *ss, const char *path, const char *key)
207 {
208         JsonObject *obj;
209         JsonNode *node = NULL;
210         int r = 1;
211
212         if ( ss == NULL ) {
213                 fprintf(stderr, _("No stylesheet!\n"));
214                 return 1;
215         }
216
217         obj = find_stylesheet_object(ss, path, &node);
218         if ( obj != NULL ) {
219                 json_object_remove_member(obj, key);
220                 r = 0;
221         } /* else most likely the object (e.g. "$.slide", "$.slide.frame",
222            * "$.narrative" etc doesn't exist */
223
224         if ( node != NULL ) json_node_unref(node);
225         return r;
226 }
227
228
229 void stylesheet_free(Stylesheet *ss)
230 {
231         g_object_unref(ss->root);
232         free(ss);
233 }
234
235
236 int stylesheet_save(Stylesheet *ss, GFile *file)
237 {
238         JsonGenerator *gen;
239         GError *error = NULL;
240         GFileOutputStream *fh;
241
242         fh = g_file_replace(file, NULL, FALSE, G_FILE_CREATE_NONE, NULL, &error);
243         if ( fh == NULL ) {
244                 fprintf(stderr, _("Open failed: %s\n"), error->message);
245                 return 1;
246         }
247
248         gen = json_generator_new();
249         json_generator_set_root(gen, ss->root);
250         json_generator_set_pretty(gen, TRUE);
251         json_generator_set_indent(gen, 1);
252         json_generator_set_indent_char(gen, '\t');
253         if ( !json_generator_to_stream(gen, G_OUTPUT_STREAM(fh), NULL, &error) ) {
254                 fprintf(stderr, _("Open failed: %s\n"), error->message);
255                 return 1;
256         }
257         g_object_unref(fh);
258         return 0;
259 }