Save stuff
[colloquium.git] / src / presentation.c
1 /*
2  * presentation.c
3  *
4  * Copyright © 2013 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 "stylesheet.h"
35 #include "loadsave.h"
36 #include "mainwindow.h"
37 #include "frame.h"
38 #include "imagestore.h"
39
40
41 static int num_presentations = 0;
42
43
44 void free_presentation(struct presentation *p)
45 {
46         int i;
47         int final = 0;
48
49         for ( i=0; i<p->num_slides; i++ ) {
50                 free_slide(p->slides[i]);
51         }
52
53         (*p->num_presentations)--;
54         if ( *p->num_presentations == 0 ) final = 1;
55
56         /* FIXME: Loads of stuff leaks here */
57         free(p->filename);
58         imagestore_destroy(p->is);
59         free(p);
60
61         if ( final ) {
62                 gtk_main_quit();
63         }
64 }
65
66
67 int insert_slide(struct presentation *p, struct slide *new, int pos)
68 {
69         struct slide **try;
70
71         try = realloc(p->slides, (1+p->num_slides)*sizeof(struct slide *));
72         if ( try == NULL ) {
73                 free(new);
74                 return 1;
75         }
76         p->slides = try;
77         p->completely_empty = 0;
78
79         /* Insert into list.  Yuk yuk yuk etc. */
80         if ( (p->num_slides>1) && (pos<p->num_slides-1) ) {
81
82                 int i;
83
84                 for ( i=p->num_slides; i>pos+1; i-- ) {
85                         p->slides[i] = p->slides[i-1];
86                 }
87                 p->slides[pos+1] = new;
88
89         } else if ( pos == p->num_slides-1 ) {
90
91                 p->slides[pos+1] = new;
92
93         } else {
94                 assert(pos == 0);
95                 p->slides[pos] = new;
96         }
97
98         new->parent = p;
99         p->num_slides++;
100
101         return 0;
102 }
103
104
105 struct slide *new_slide()
106 {
107         struct slide *new;
108
109         new = calloc(1, sizeof(struct slide));
110         if ( new == NULL ) return NULL;
111
112         new->rendered_edit = NULL;
113         new->rendered_proj = NULL;
114         new->rendered_thumb = NULL;
115
116         new->top = frame_new();
117         /* FIXME: Set zero margins etc on top level frame */
118
119         new->notes = strdup("");
120
121         return new;
122 }
123
124
125 void free_slide(struct slide *s)
126 {
127         free(s);
128 }
129
130
131 struct slide *add_slide(struct presentation *p, int pos)
132 {
133         struct slide *s = new_slide();
134         if ( insert_slide(p, s, pos) ) {
135                 free_slide(s);
136                 return NULL;
137         }
138
139         return s;
140 }
141
142
143 static char *safe_basename(const char *in)
144 {
145         int i;
146         char *cpy;
147         char *res;
148
149         cpy = strdup(in);
150
151         /* Get rid of any trailing slashes */
152         for ( i=strlen(cpy)-1; i>0; i-- ) {
153                 if ( cpy[i] == '/' ) {
154                         cpy[i] = '\0';
155                 } else {
156                         break;
157                 }
158         }
159
160         /* Find the base name */
161         for ( i=strlen(cpy)-1; i>0; i-- ) {
162                 if ( cpy[i] == '/' ) {
163                         i++;
164                         break;
165                 }
166         }
167
168         res = strdup(cpy+i);
169         /* If we didn't find a previous slash, i==0 so res==cpy */
170
171         free(cpy);
172
173         return res;
174 }
175
176
177 void get_titlebar_string(struct presentation *p)
178 {
179         free(p->titlebar);
180
181         if ( p->filename == NULL ) {
182                 p->titlebar = strdup("(untitled)");
183         } else {
184                 p->titlebar = safe_basename(p->filename);
185         }
186 }
187
188
189 int slide_number(struct presentation *p, struct slide *s)
190 {
191         int i;
192
193         for ( i=0; i<p->num_slides; i++ ) {
194                 if ( p->slides[i] == s ) return i;
195         }
196
197         return p->num_slides;
198 }
199
200
201 static int alloc_selection(struct presentation *p)
202 {
203         struct frame **new_selection;
204         new_selection = realloc(p->selection,
205                                 p->max_selection*sizeof(struct frame *));
206         if ( new_selection == NULL ) return 1;
207         p->selection = new_selection;
208         return 0;
209 }
210
211
212 struct presentation *new_presentation()
213 {
214         struct presentation *new;
215
216         new = calloc(1, sizeof(struct presentation));
217
218         num_presentations++;
219         new->num_presentations = &num_presentations;
220
221         new->filename = NULL;
222         new->titlebar = NULL;
223         get_titlebar_string(new);
224
225         new->window = NULL;
226         new->ui = NULL;
227         new->action_group = NULL;
228         new->slideshow = NULL;
229
230         new->slide_width = 1024.0;
231         new->slide_height = 768.0;
232
233         /* Add one blank slide and view it */
234         new->num_slides = 0;
235         new->slides = NULL;
236         new->cur_edit_slide = NULL;
237         new->cur_proj_slide = NULL;
238
239         new->completely_empty = 1;
240
241         new->ss = new_stylesheet();
242         default_stylesheet(new->ss);
243
244         new->n_menu_rebuild = 0;
245         new->menu_rebuild_list = NULL;
246
247         new->selection = NULL;
248         new->n_selection = 0;
249         new->max_selection = 64;
250         if ( alloc_selection(new) ) return NULL;
251
252         new->is = imagestore_new();
253
254         return new;
255 }
256
257
258 static char *packed_sc(struct frame *fr)
259 {
260         char *sc;
261         int i;
262         size_t len = 0;
263
264         if ( fr->sc != NULL ) {
265                 len += strlen(fr->sc)+1;
266                 sc = malloc(len);
267                 memcpy(sc, fr->sc, len+1);
268         } else {
269                 len = 0;
270                 sc = malloc(1);
271                 sc[0] = '\0';
272         }
273
274         for ( i=0; i<fr->num_children; i++ ) {
275
276                 char *ch_sc;
277                 char *scn;
278                 size_t ch_len;
279
280                 ch_sc = packed_sc(fr->children[i]);
281                 ch_len = strlen(ch_sc);
282
283                 len += ch_len + 64;
284                 scn = malloc(len + ch_len);
285                 snprintf(scn, len, "%s\\f[%.1fx%.1f+%.1f+%.1f]{%s}", sc,
286                          fr->children[i]->lop.w, fr->children[i]->lop.h,
287                          fr->children[i]->lop.x, fr->children[i]->lop.y,
288                          ch_sc);
289                 free(ch_sc);
290                 free(sc);
291                 sc = scn;
292
293         }
294
295         return sc;
296 }
297
298
299 int save_presentation(struct presentation *p, const char *filename)
300 {
301         FILE *fh;
302         int i;
303         struct serializer ser;
304         char *old_fn;
305
306         //grab_current_notes(p);
307
308         fh = fopen(filename, "w");
309         if ( fh == NULL ) return 1;
310
311         /* Set up the serializer */
312         ser.fh = fh;
313         ser.stack_depth = 0;
314         ser.prefix = NULL;
315
316         fprintf(fh, "# Colloquium presentation file\n");
317         serialize_f(&ser, "version", 0.1);
318
319         serialize_start(&ser, "slide-properties");
320         serialize_f(&ser, "width", p->slide_width);
321         serialize_f(&ser, "height", p->slide_height);
322         serialize_end(&ser);
323
324         serialize_start(&ser, "stylesheet");
325         write_stylesheet(p->ss, &ser);
326         serialize_end(&ser);
327
328         serialize_start(&ser, "slides");
329         for ( i=0; i<p->num_slides; i++ ) {
330
331                 struct slide *s;
332                 char s_id[32];
333                 char *sc;
334
335                 s = p->slides[i];
336
337                 snprintf(s_id, 31, "%i", i);
338                 serialize_start(&ser, s_id);
339
340                 sc = packed_sc(s->top);
341                 serialize_s(&ser, "sc", sc);
342                 free(sc);
343
344                 serialize_end(&ser);
345
346         }
347         serialize_end(&ser);
348
349         /* Slightly fiddly because someone might
350          * do save_presentation(p, p->filename) */
351         old_fn = p->filename;
352         p->filename = strdup(filename);
353         if ( old_fn != NULL ) free(old_fn);
354         update_titlebar(p);
355
356         fclose(fh);
357         return 0;
358 }
359
360
361 static struct slide *tree_to_slide(struct presentation *p, struct ds_node *root)
362 {
363         struct slide *s;
364
365         s = new_slide();
366         s->parent = p;
367
368         /* FIXME: Load stuff */
369
370         return s;
371 }
372
373
374 static int tree_to_slides(struct ds_node *root, struct presentation *p)
375 {
376         int i;
377
378         for ( i=0; i<root->n_children; i++ ) {
379
380                 struct slide *s;
381
382                 s = tree_to_slide(p, root->children[i]);
383                 if ( s != NULL ) {
384                         insert_slide(p, s, p->num_slides-1);
385                 }
386
387         }
388
389         return 0;
390 }
391
392
393 int tree_to_presentation(struct ds_node *root, struct presentation *p)
394 {
395         struct ds_node *node;
396         char *check;
397         int i;
398
399         p->cur_edit_slide = NULL;
400         p->cur_proj_slide = NULL;
401
402         node = find_node(root, "slide-properties/width", 0);
403         if ( node == NULL ) return 1;
404         p->slide_width = strtod(node->value, &check);
405         if ( check == node->value ) {
406                 fprintf(stderr, "Invalid slide width\n");
407                 return 1;
408         }
409
410         node = find_node(root, "slide-properties/height", 0);
411         if ( node == NULL ) return 1;
412         p->slide_height = strtod(node->value, &check);
413         if ( check == node->value ) {
414                 fprintf(stderr, "Invalid slide height\n");
415                 return 1;
416         }
417
418         node = find_node(root, "stylesheet", 0);
419         if ( node != NULL ) {
420                 free_stylesheet(p->ss);
421                 p->ss = tree_to_stylesheet(node);
422                 if ( p->ss == NULL ) {
423                         fprintf(stderr, "Invalid style sheet\n");
424                         return 1;
425                 }
426         }
427
428         for ( i=0; i<p->num_slides; i++ ) {
429                 free_slide(p->slides[i]);
430                 p->num_slides = 0;
431         }
432
433         node = find_node(root, "slides", 0);
434         if ( node != NULL ) {
435                 tree_to_slides(node, p);
436                 if ( p->num_slides == 0 ) {
437                         fprintf(stderr, "Failed to load any slides\n");
438                         p->cur_edit_slide = add_slide(p, 0);
439                         return 1;
440                 }
441         }
442
443         return 0;
444 }
445
446
447 int load_presentation(struct presentation *p, const char *filename)
448 {
449         FILE *fh;
450         struct ds_node *root;
451         int r;
452
453         assert(p->completely_empty);
454
455         fh = fopen(filename, "r");
456         if ( fh == NULL ) return 1;
457
458         root = new_ds_node("root");
459         if ( root == NULL ) return 1;
460
461         if ( deserialize_file(fh, root) ) {
462                 fclose(fh);
463                 return 1;
464         }
465
466         r = tree_to_presentation(root, p);
467         free_ds_tree(root);
468
469         fclose(fh);
470
471         if ( r ) {
472                 p->cur_edit_slide = new_slide();
473                 insert_slide(p, p->cur_edit_slide, 0);
474                 p->completely_empty = 1;
475                 return r;  /* Error */
476         }
477
478         assert(p->filename == NULL);
479         p->filename = strdup(filename);
480         update_titlebar(p);
481
482         p->cur_edit_slide = p->slides[0];
483
484         return 0;
485 }
486
487
488 void set_edit(struct presentation *p, struct slide *s)
489 {
490         p->cur_edit_slide = s;
491 }
492
493
494 void set_selection(struct presentation *p, struct frame *fr)
495 {
496         p->selection[0] = fr;
497         p->n_selection = 1;
498
499         if ( fr == NULL ) p->n_selection = 0;
500 }
501
502
503 void add_selection(struct presentation *p, struct frame *fr)
504 {
505         if ( p->n_selection == p->max_selection ) {
506                 p->max_selection += 64;
507                 if ( alloc_selection(p) ) {
508                         fprintf(stderr, "Not enough memory for selection.\n");
509                         return;
510                 }
511         }
512
513         p->selection[p->n_selection++] = fr;
514 }