Save the stylesheet
[colloquium.git] / src / presentation.c
1 /*
2  * presentation.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 <stdlib.h>
29 #include <string.h>
30 #include <assert.h>
31 #include <gtk/gtk.h>
32
33 #include "presentation.h"
34 #include "slide_window.h"
35 #include "frame.h"
36 #include "imagestore.h"
37 #include "render.h"
38 #include "sc_interp.h"
39 #include "utils.h"
40
41
42 void free_presentation(struct presentation *p)
43 {
44         int final = 0;
45
46         /* FIXME: Loads of stuff leaks here */
47         free(p->uri);
48         imagestore_destroy(p->is);
49         free(p);
50
51         if ( final ) {
52                 gtk_main_quit();
53         }
54 }
55
56
57 char *get_titlebar_string(struct presentation *p)
58 {
59         if ( p == NULL || p->uri == NULL ) {
60                 return strdup(_("(untitled)"));
61         } else {
62                 GFile *f = g_file_new_for_uri(p->uri);
63                 char *bn = g_file_get_basename(f);
64                 g_object_unref(f);
65                 return bn;
66         }
67 }
68
69
70 static void find_and_load_stylesheet(struct presentation *p, GFile *file)
71 {
72         GFile *ssfile;
73         GFile *parent;
74         gchar *ssuri;
75
76         if ( file != NULL ) {
77
78                 /* First choice: /same/directory/<presentation>.ss */
79                 ssuri = g_file_get_uri(file);
80                 if ( ssuri != NULL ) {
81                         size_t l = strlen(ssuri);
82                         if ( ssuri[l-3] == '.' && ssuri[l-2] == 's' && ssuri[l-1] =='c' ) {
83                                 ssuri[l-1] = 's';
84                                 ssfile = g_file_new_for_uri(ssuri);
85                                 p->stylesheet = stylesheet_load(ssfile);
86                                 p->stylesheet_from = ssfile;
87                                 g_free(ssuri);
88                         }
89                 }
90
91                 /* Second choice: /same/directory/stylesheet.ss */
92                 if ( p->stylesheet == NULL ) {
93                         parent = g_file_get_parent(file);
94                         if ( parent != NULL ) {
95                                 ssfile = g_file_get_child(parent, "stylesheet.ss");
96                                 if ( ssfile != NULL ) {
97                                         p->stylesheet = stylesheet_load(ssfile);
98                                         p->stylesheet_from = ssfile;
99                                 }
100                                 g_object_unref(parent);
101                         }
102                 }
103
104         }
105
106         /* Third choice: <cwd>/stylesheet.ss */
107         if ( p->stylesheet == NULL ) {
108                 ssfile = g_file_new_for_path("./stylesheet.ss");
109                 p->stylesheet = stylesheet_load(ssfile);
110                 p->stylesheet_from = ssfile;
111         }
112
113         /* Fourth choice: internal default stylesheet */
114         if ( p->stylesheet == NULL ) {
115                 ssfile = g_file_new_for_uri("resource:///uk/me/bitwiz/Colloquium/default.ss");
116                 p->stylesheet = stylesheet_load(ssfile);
117                 p->stylesheet_from = NULL;
118                 g_object_unref(ssfile);
119         }
120
121         /* Last resort is NULL stylesheet and SCInterpreter's defaults */
122 }
123
124
125 struct presentation *new_presentation(const char *imagestore)
126 {
127         struct presentation *new;
128
129         new = calloc(1, sizeof(struct presentation));
130         if ( new == NULL ) return NULL;
131
132         new->uri = NULL;
133
134         new->scblocks = NULL;
135
136         /* Default slide size */
137         new->slide_width = 1024.0;
138         new->slide_height = 768.0;
139
140         new->completely_empty = 1;
141         new->saved = 1;
142         new->stylesheet = NULL;
143         new->is = imagestore_new(imagestore);
144
145         new->lang = pango_language_get_default();
146
147         find_and_load_stylesheet(new, NULL);
148
149         return new;
150 }
151
152
153 int save_presentation(struct presentation *p, GFile *file, GFile *ssfile)
154 {
155         GFileOutputStream *fh;
156         int r;
157         int sr;
158         GError *error = NULL;
159
160         fh = g_file_replace(file, NULL, FALSE, G_FILE_CREATE_NONE, NULL, &error);
161         if ( fh == NULL ) {
162                 fprintf(stderr, _("Open failed: %s\n"), error->message);
163                 return 1;
164         }
165         r = save_sc_block(G_OUTPUT_STREAM(fh), p->scblocks);
166         if ( r ) {
167                 fprintf(stderr, _("Couldn't save presentation\n"));
168         }
169         g_object_unref(fh);
170
171         if ( ssfile != NULL ) {
172                 sr = stylesheet_save(p->stylesheet, ssfile);
173                 if ( sr ) {
174                         fprintf(stderr, _("Couldn't save stylesheet\n"));
175                 }
176         } else {
177                 fprintf(stderr, _("Not updating default stylesheet\n"));
178                 sr = 0;
179         }
180
181         if ( r || sr ) return 1;
182
183         imagestore_set_parent(p->is, g_file_get_parent(file));
184         p->uri = g_file_get_uri(file);
185         p->saved = 1;
186         update_titlebar(p->narrative_window);
187         return 0;
188 }
189
190
191 int slide_number(struct presentation *p, SCBlock *sl)
192 {
193         SCBlock *bl = p->scblocks;
194         int n = 0;
195
196         while ( bl != NULL ) {
197                 if ( safe_strcmp(sc_block_name(bl), "slide") == 0 ) {
198                         n++;
199                         if ( bl == sl ) return n;
200                 }
201                 bl = sc_block_next(bl);
202         }
203
204         return 0;
205 }
206
207
208 int num_slides(struct presentation *p)
209 {
210         SCBlock *bl = p->scblocks;
211         int n = 0;
212
213         while ( bl != NULL ) {
214                 if ( safe_strcmp(sc_block_name(bl), "slide") == 0 ) {
215                         n++;
216                 }
217                 bl = sc_block_next(bl);
218         }
219
220         return n;
221 }
222
223
224 SCBlock *first_slide(struct presentation *p)
225 {
226         SCBlock *bl = p->scblocks;
227
228         while ( bl != NULL ) {
229                 if ( safe_strcmp(sc_block_name(bl), "slide") == 0 ) {
230                         return bl;
231                 }
232                 bl = sc_block_next(bl);
233         }
234
235         fprintf(stderr, _("Couldn't find first slide!\n"));
236         return NULL;
237 }
238
239
240 SCBlock *last_slide(struct presentation *p)
241 {
242         SCBlock *bl = p->scblocks;
243         SCBlock *l = NULL;
244
245         while ( bl != NULL ) {
246                 if ( safe_strcmp(sc_block_name(bl), "slide") == 0 ) {
247                         l = bl;
248                 }
249                 bl = sc_block_next(bl);
250         }
251
252         if ( l == NULL ) {
253                 fprintf(stderr, _("Couldn't find last slide!\n"));
254         }
255         return l;
256 }
257
258
259 SCBlock *next_slide(struct presentation *p, SCBlock *sl)
260 {
261         SCBlock *bl = sl;
262         int found = 0;
263
264         while ( bl != NULL ) {
265                 if ( safe_strcmp(sc_block_name(bl), "slide") == 0 ) {
266                         if ( found ) return bl;
267                 }
268                 if ( bl == sl ) {
269                         found = 1;
270                 }
271                 bl = sc_block_next(bl);
272         }
273
274         fprintf(stderr, _("Couldn't find next slide!\n"));
275         return NULL;
276 }
277
278
279 SCBlock *prev_slide(struct presentation *p, SCBlock *sl)
280 {
281         SCBlock *bl = p->scblocks;
282         SCBlock *l = NULL;
283
284         while ( bl != NULL ) {
285                 if ( bl == sl ) {
286                         if ( l == NULL ) return sl; /* Already on first slide */
287                         return l;
288                 }
289                 if ( safe_strcmp(sc_block_name(bl), "slide") == 0 ) {
290                         l = bl;
291                 }
292                 bl = sc_block_next(bl);
293         }
294
295         fprintf(stderr, _("Couldn't find prev slide!\n"));
296         return NULL;
297 }
298
299
300 static void set_slide_size_from_stylesheet(struct presentation *p)
301 {
302         char *result;
303
304         result = stylesheet_lookup(p->stylesheet, "$.slide", "size");
305         if ( result != NULL ) {
306                 float v[2];
307                 if ( parse_double(result, v) == 0 ) {
308                         p->slide_width = v[0];
309                         p->slide_height = v[1];
310                 }
311         }
312 }
313
314
315 int load_presentation(struct presentation *p, GFile *file)
316 {
317         int r = 0;
318         char *everything;
319
320         assert(p->completely_empty);
321
322         if ( !g_file_load_contents(file, NULL, &everything, NULL, NULL, NULL) ) {
323                 fprintf(stderr, _("Failed to load '%s'\n"), g_file_get_uri(file));
324                 return 1;
325         }
326
327         p->scblocks = sc_parse(everything);
328         g_free(everything);
329
330         p->lang = pango_language_get_default();
331
332         if ( p->scblocks == NULL ) r = 1;
333
334         if ( r ) {
335                 p->completely_empty = 1;
336                 fprintf(stderr, _("Parse error.\n"));
337                 return r;  /* Error */
338         }
339
340         p->stylesheet = NULL;
341
342         find_and_load_stylesheet(p, file);
343
344         set_slide_size_from_stylesheet(p);
345
346         assert(p->uri == NULL);
347         p->uri = g_file_get_uri(file);
348         imagestore_set_parent(p->is, g_file_get_parent(file));
349
350         return 0;
351 }
352