09c91ba9c6c76256f63a324e261787522ff9a93d
[colloquium.git] / libstorycode / slide.c
1 /*
2  * slide.c
3  *
4  * Copyright © 2019 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 <stdio.h>
31 #include <assert.h>
32
33 #ifdef HAVE_PANGO
34 #include <pango/pangocairo.h>
35 #endif
36
37 #include "slide.h"
38 #include "slide_priv.h"
39
40
41 Slide *slide_new()
42 {
43         Slide *s;
44         s = malloc(sizeof(*s));
45         if ( s == NULL ) return NULL;
46         s->n_items = 0;
47         s->items = NULL;
48         s->logical_w = -1.0;
49         s->logical_h = -1.0;
50         return s;
51 }
52
53 void slide_free(Slide *s)
54 {
55         free(s->items);
56         free(s);
57 }
58
59
60 void slide_delete_item(Slide *s, SlideItem *item)
61 {
62         int i;
63         for ( i=0; i<s->n_items; i++ ) {
64                 if ( &s->items[i] == item ) {
65                         memmove(&s->items[i], &s->items[i+1],
66                                 (s->n_items-i-1)*sizeof(SlideItem));
67                         s->n_items--;
68                         return;
69                 }
70         }
71         fprintf(stderr, "Didn't find slide item to delete!\n");
72 }
73
74
75 static SlideItem *slide_item_new()
76 {
77         SlideItem *item;
78         item = malloc(sizeof(struct _slideitem));
79         if ( item == NULL ) return NULL;
80         item->paras = NULL;
81         return item;
82 }
83
84
85 void slide_add_item(Slide *s, SlideItem *item)
86 {
87         SlideItem *new_items;
88
89         new_items = realloc(s->items, (s->n_items+1)*sizeof(SlideItem));
90         if ( new_items == NULL ) return;
91         s->items = new_items;
92
93         /* Copy contents and free top-level */
94         s->items[s->n_items++] = *item;
95         free(item);
96 }
97
98
99 SlideItem *slide_item_image(char *filename, struct frame_geom geom)
100 {
101         SlideItem *item;
102
103         item = slide_item_new();
104         if ( item == NULL ) return NULL;
105
106         item->type = SLIDE_ITEM_IMAGE;
107         item->geom = geom;
108         item->filename = filename;
109
110         return item;
111 }
112
113
114 /* paras: array of arrays of text runs
115  * n_runs: array of numbers of runs in each paragraph
116  * n_paras: the number of paragraphs
117  *
118  * Will take ownership of the arrays of text runs, but not the array of arrays
119  * Will NOT take ownership of the array of numbers of runs
120  */
121 static SlideItem *text_item_new(struct text_run **paras, int *n_runs, int n_paras,
122                                 struct frame_geom geom, enum alignment alignment,
123                                 enum slide_item_type slide_item)
124 {
125         int i;
126         SlideItem *item;
127
128         item = slide_item_new();
129         if ( item == NULL ) return NULL;
130
131         item->type = slide_item;
132         item->paras = malloc(n_paras*sizeof(struct slide_text_paragraph));
133         if ( item->paras == NULL ) {
134                 free(item);
135                 return NULL;
136         }
137         item->n_paras = n_paras;
138
139         for ( i=0; i<n_paras; i++ ) {
140                 item->paras[i].runs = paras[i];
141                 item->paras[i].n_runs = n_runs[i];
142                 item->paras[i].layout = NULL;
143         }
144
145         item->geom = geom;
146         item->align = alignment;
147
148         return item;
149 }
150
151
152 SlideItem *slide_item_footer()
153 {
154         SlideItem *item = slide_item_new();
155         if ( item == NULL ) return NULL;
156         item->type = SLIDE_ITEM_FOOTER;
157         return item;
158 }
159
160
161 SlideItem *slide_item_text(struct text_run **paras, int *n_runs, int n_paras,
162                            struct frame_geom geom, enum alignment alignment)
163 {
164         return text_item_new(paras, n_runs, n_paras, geom, alignment,
165                              SLIDE_ITEM_TEXT);
166 }
167
168
169 SlideItem *slide_item_slidetitle(struct text_run **paras, int *n_runs, int n_paras)
170 {
171         struct frame_geom geom;
172
173         /* This geometry should never get used by the renderer */
174         geom.x.len = 0.0;
175         geom.x.unit = LENGTH_FRAC;
176         geom.y.len = 0.0;
177         geom.y.unit = LENGTH_FRAC;
178         geom.w.len = 1.0;
179         geom.w.unit = LENGTH_FRAC;
180         geom.h.len = 1.0;
181         geom.h.unit = LENGTH_FRAC;
182
183         return text_item_new(paras, n_runs, n_paras, geom, ALIGN_INHERIT,
184                              SLIDE_ITEM_SLIDETITLE);
185 }
186
187
188 SlideItem *slide_item_prestitle(struct text_run **paras, int *n_runs, int n_paras)
189 {
190         struct frame_geom geom;
191
192         /* This geometry should never get used by the renderer */
193         geom.x.len = 0.0;
194         geom.x.unit = LENGTH_FRAC;
195         geom.y.len = 0.0;
196         geom.y.unit = LENGTH_FRAC;
197         geom.w.len = 1.0;
198         geom.w.unit = LENGTH_FRAC;
199         geom.h.len = 1.0;
200         geom.h.unit = LENGTH_FRAC;
201
202         return text_item_new(paras, n_runs, n_paras, geom, ALIGN_INHERIT,
203                              SLIDE_ITEM_PRESTITLE);
204 }
205
206
207 static char units(enum length_unit u)
208 {
209         if ( u == LENGTH_UNIT ) return 'u';
210         if ( u == LENGTH_FRAC ) return 'f';
211         return '?';
212 }
213
214
215 void describe_slide(Slide *s)
216 {
217         int i;
218
219         printf("  %i items\n", s->n_items);
220         for ( i=0; i<s->n_items; i++ ) {
221                 printf("item %i: %i\n", i, s->items[i].type);
222                 printf("geom %f %c x %f %c + %f %c + %f %c\n",
223                        s->items[i].geom.x.len, units(s->items[i].geom.x.unit),
224                        s->items[i].geom.y.len, units(s->items[i].geom.y.unit),
225                        s->items[i].geom.w.len, units(s->items[i].geom.w.unit),
226                        s->items[i].geom.h.len, units(s->items[i].geom.h.unit));
227         }
228 }
229
230
231 int slide_set_logical_size(Slide *s, double w, double h)
232 {
233         if ( s == NULL ) return 1;
234         s->logical_w = w;
235         s->logical_h = h;
236         return 0;
237 }
238
239
240 int slide_get_logical_size(Slide *s, Stylesheet *ss, double *w, double *h)
241 {
242         if ( s == NULL ) return 1;
243
244         if ( s->logical_w < 0.0 ) {
245                 /* Slide-specific value not set, use stylesheet */
246                 return stylesheet_get_slide_default_size(ss, w, h);
247         }
248
249         *w = s->logical_w;
250         *h = s->logical_h;
251         return 0;
252 }
253
254
255 static const char *style_name_for_slideitem(enum slide_item_type t)
256 {
257         switch ( t ) {
258
259                 case SLIDE_ITEM_TEXT :
260                 return "SLIDE.TEXT";
261
262                 case SLIDE_ITEM_IMAGE :
263                 return "SLIDE.IMAGE";
264
265                 case SLIDE_ITEM_PRESTITLE :
266                 return "SLIDE.PRESTITLE";
267
268                 case SLIDE_ITEM_SLIDETITLE :
269                 return "SLIDE.SLIDETITLE";
270
271                 case SLIDE_ITEM_FOOTER :
272                 return "SLIDE.FOOTER";
273
274         }
275
276         fprintf(stderr, "Invalid slide item %i\n", t);
277         return "SLIDE.TEXT";
278 }
279
280
281 static double lcalc(struct length l, double pd)
282 {
283         if ( l.unit == LENGTH_UNIT ) {
284                 return l.len;
285         } else {
286                 return l.len * pd;
287         }
288 }
289
290
291 void slide_item_get_geom(SlideItem *item, Stylesheet *ss,
292                          double *x, double *y, double *w, double *h,
293                          double slide_w, double slide_h)
294 {
295         struct frame_geom geom;
296
297         if ( (item->type == SLIDE_ITEM_TEXT)
298           || (item->type == SLIDE_ITEM_IMAGE) )
299         {
300                 geom = item->geom;
301         } else {
302                 if ( stylesheet_get_geometry(ss, style_name_for_slideitem(item->type), &geom) ) {
303                         *x = 0.0;  *y = 0.0;
304                         *w = 0.0;  *h = 0.0;
305                         return;
306                 }
307         }
308
309         *x = lcalc(geom.x, slide_w);
310         *y = lcalc(geom.y, slide_h);
311         *w = lcalc(geom.w, slide_w);
312         *h = lcalc(geom.h, slide_h);
313 }
314
315
316 void slide_item_get_padding(SlideItem *item, Stylesheet *ss,
317                             double *l, double *r, double *t, double *b,
318                             double slide_w, double slide_h)
319 {
320         struct length padding[4];
321         double frx, fry, frw, frh;
322
323         if ( stylesheet_get_padding(ss, style_name_for_slideitem(item->type), padding) ) {
324                 *l = 0.0;  *r = 0.0;  *t = 0.0;  *b = 0.0;
325                 return;
326         }
327
328         slide_item_get_geom(item, ss, &frx, &fry, &frw, &frh, slide_w, slide_h);
329
330         *l = lcalc(padding[0], frw);
331         *r = lcalc(padding[1], frh);
332         *t = lcalc(padding[2], frw);
333         *b = lcalc(padding[3], frh);
334 }
335
336
337 void slide_item_split_text_paragraph(SlideItem *item, int para, size_t off)
338 {
339         /* FIXME */
340 #if 0
341         struct slide_text_paragraph *np;
342
343         np = realloc(item->paras, (item->n_paras+1)*sizeof(struct slide_text_paragraph));
344         if ( np == NULL ) return;
345
346         item->paras = np;
347         item->n_paras++;
348
349         memmove(&item->paras[para+1], &item->paras[para],
350                 (item->n_paras - para - 1)*sizeof(struct slide_text_paragraph));
351
352         item->paras[para+1].text = strdup(&item->paras[para].text[off]);
353         item->paras[para+1].layout = NULL;
354         item->paras[para].text[off] = '\0';
355 #endif
356 }
357
358
359 static void delete_paragraph(SlideItem *item, int del)
360 {
361         /* FIXME */
362 #if 0
363         int i;
364
365 #ifdef HAVE_PANGO
366         g_object_unref(item->paras[del].layout);
367 #endif
368         free(item->paras[del].text);
369
370         for ( i=del; i<item->n_paras-1; i++ ) {
371                 item->paras[i] = item->paras[i+1];
372         }
373         item->n_paras--;
374 #endif
375 }
376
377
378 void slide_item_delete_text(SlideItem *item, int i1, size_t o1, int i2, size_t o2)
379 {
380         /* FIXME */
381 #if 0
382         int i;
383         int n_del = 0;
384
385         /* Starting item */
386         if ( i1 == i2 ) {
387                 memmove(&item->paras[i1].text[o1],
388                         &item->paras[i1].text[o2],
389                         strlen(item->paras[i1].text)-o2+1);
390                 return;  /* easy case */
391         } else {
392                 item->paras[i1].text[o1] = '\0';
393         }
394
395         /* Middle items */
396         for ( i=i1+1; i<i2; i++ ) {
397                 /* Deleting the item moves all the subsequent items up, so the
398                  * index to be deleted doesn't change. */
399                 delete_paragraph(item, i1+1);
400                 n_del++;
401         }
402         i2 -= n_del;
403
404         /* Last item */
405         memmove(&item->paras[i2].text[0],
406                 &item->paras[i2].text[o2],
407                 strlen(&item->paras[i2].text[o2])+1);
408
409         assert(i1 != i2);
410         char *new_text;
411         size_t len = strlen(item->paras[i1].text);
412         len += strlen(item->paras[i2].text);
413         new_text = malloc(len+1);
414         if ( new_text == NULL ) return;
415         strcpy(new_text, item->paras[i1].text);
416         strcat(new_text, item->paras[i2].text);
417         free(item->paras[i1].text);
418         item->paras[i1].text = new_text;
419         delete_paragraph(item, i2);
420 #endif
421 }