Add a FIXME
[colloquium.git] / src / stylesheet.c
1 /*
2  * stylesheet.c
3  *
4  * Colloquium - A tiny presentation program
5  *
6  * Copyright (c) 2011 Thomas White <taw@bitwiz.org.uk>
7  *
8  * This program 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 <gtk/gtk.h>
31 #include <assert.h>
32
33 #include "presentation.h"
34 #include "stylesheet.h"
35 #include "objects.h"
36 #include "loadsave.h"
37
38
39 struct _stylesheetwindow
40 {
41         struct presentation *p;  /* Presentation to update when user alters
42                                   * something in this window */
43         GtkWidget           *window;
44         StyleSheet          *ss; /* Style sheet this window corresponds to */
45
46         GtkWidget           *text_font;
47         GtkWidget           *text_colour;
48
49         GtkWidget           *margin_left;
50         GtkWidget           *margin_right;
51         GtkWidget           *margin_top;
52         GtkWidget           *margin_bottom;
53         GtkWidget           *offset_x;
54         GtkWidget           *offset_y;
55         GtkWidget           *halign;
56         GtkWidget           *valign;
57         GtkWidget           *max_width;
58         GtkWidget           *use_max;
59
60         struct style        *cur_style;
61 };
62
63
64 static void text_font_set_sig(GtkFontButton *widget,
65                               struct _stylesheetwindow *s)
66 {
67         const gchar *font;
68
69         font = gtk_font_button_get_font_name(widget);
70         free(s->cur_style->font);
71         s->cur_style->font = strdup(font);
72
73         notify_style_update(s->p, s->cur_style);
74 }
75
76
77 static void text_colour_set_sig(GtkColorButton *widget,
78                               struct _stylesheetwindow *s)
79 {
80         GdkColor col;
81         guint16 al;
82
83         gtk_color_button_get_color(widget, &col);
84         free(s->cur_style->colour);
85         s->cur_style->colour = gdk_color_to_string(&col);
86         al = gtk_color_button_get_alpha(widget);
87         s->cur_style->alpha = (double)al / 65535.0;
88
89         notify_style_update(s->p, s->cur_style);
90 }
91
92
93 static void margin_left_changed_sig(GtkSpinButton *spin,
94                                     struct _stylesheetwindow *s)
95 {
96         s->cur_style->margin_left = gtk_spin_button_get_value(spin);
97         notify_style_update(s->p, s->cur_style);
98 }
99
100
101 static void margin_right_changed_sig(GtkSpinButton *spin,
102                                      struct _stylesheetwindow *s)
103 {
104         s->cur_style->margin_right = gtk_spin_button_get_value(spin);
105         notify_style_update(s->p, s->cur_style);
106 }
107
108
109 static void margin_top_changed_sig(GtkSpinButton *spin,
110                                    struct _stylesheetwindow *s)
111 {
112         s->cur_style->margin_top = gtk_spin_button_get_value(spin);
113         notify_style_update(s->p, s->cur_style);
114 }
115
116
117 static void margin_bottom_changed_sig(GtkSpinButton *spin,
118                                       struct _stylesheetwindow *s)
119 {
120         s->cur_style->margin_bottom = gtk_spin_button_get_value(spin);
121         notify_style_update(s->p, s->cur_style);
122 }
123
124
125 static void offset_x_changed_sig(GtkSpinButton *spin,
126                                  struct _stylesheetwindow *s)
127 {
128         s->cur_style->offset_x = gtk_spin_button_get_value(spin);
129         notify_style_update(s->p, s->cur_style);
130 }
131
132
133 static void offset_y_changed_sig(GtkSpinButton *spin,
134                                  struct _stylesheetwindow *s)
135 {
136         s->cur_style->offset_y = gtk_spin_button_get_value(spin);
137         notify_style_update(s->p, s->cur_style);
138 }
139
140
141 static void halign_changed_sig(GtkComboBox *combo,
142                                  struct _stylesheetwindow *s)
143 {
144         s->cur_style->halign = gtk_combo_box_get_active(combo);
145         notify_style_update(s->p, s->cur_style);
146 }
147
148
149 static void valign_changed_sig(GtkComboBox *combo,
150                                  struct _stylesheetwindow *s)
151 {
152         s->cur_style->valign = gtk_combo_box_get_active(combo);
153         notify_style_update(s->p, s->cur_style);
154 }
155
156
157 static void max_changed_sig(GtkSpinButton *spin,
158                             struct _stylesheetwindow *s)
159 {
160         s->cur_style->max_width = gtk_spin_button_get_value(spin);
161         notify_style_update(s->p, s->cur_style);
162 }
163
164
165 static void use_max_toggled_sig(GtkToggleButton *combo,
166                                  struct _stylesheetwindow *s)
167 {
168         int v;
169
170         v = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(s->use_max));
171         s->cur_style->use_max_width = v;
172         gtk_widget_set_sensitive(s->max_width,
173                                  s->cur_style->use_max_width);
174         notify_style_update(s->p, s->cur_style);
175 }
176
177
178 static void style_changed_sig(GtkComboBox *combo,
179                                struct _stylesheetwindow *s)
180 {
181         int n;
182         GdkColor col;
183
184         n = gtk_combo_box_get_active(GTK_COMBO_BOX(combo));
185         s->cur_style = s->ss->styles[n];
186
187         if ( n == 0 ) {
188                 gtk_widget_set_sensitive(s->offset_x, FALSE);
189                 gtk_widget_set_sensitive(s->offset_y, FALSE);
190                 gtk_widget_set_sensitive(s->use_max, FALSE);
191                 gtk_widget_set_sensitive(s->max_width, FALSE);
192                 gtk_widget_set_sensitive(s->halign, FALSE);
193                 gtk_widget_set_sensitive(s->valign, FALSE);
194         } else {
195                 gtk_widget_set_sensitive(s->offset_x, TRUE);
196                 gtk_widget_set_sensitive(s->offset_y, TRUE);
197                 gtk_widget_set_sensitive(s->use_max, TRUE);
198                 gtk_widget_set_sensitive(s->max_width, TRUE);
199                 gtk_widget_set_sensitive(s->halign, TRUE);
200                 gtk_widget_set_sensitive(s->valign, TRUE);
201         }
202
203         gtk_spin_button_set_value(GTK_SPIN_BUTTON(s->margin_left),
204                                   s->cur_style->margin_left);
205         gtk_spin_button_set_value(GTK_SPIN_BUTTON(s->margin_right),
206                                   s->cur_style->margin_right);
207         gtk_spin_button_set_value(GTK_SPIN_BUTTON(s->margin_bottom),
208                                   s->cur_style->margin_bottom);
209         gtk_spin_button_set_value(GTK_SPIN_BUTTON(s->margin_top),
210                                   s->cur_style->margin_top);
211
212         gtk_spin_button_set_value(GTK_SPIN_BUTTON(s->offset_x),
213                                   s->cur_style->offset_x);
214         gtk_spin_button_set_value(GTK_SPIN_BUTTON(s->offset_y),
215                                   s->cur_style->offset_y);
216
217         gtk_combo_box_set_active(GTK_COMBO_BOX(s->halign),
218                                  s->cur_style->halign);
219         gtk_combo_box_set_active(GTK_COMBO_BOX(s->valign),
220                                  s->cur_style->valign);
221
222         gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(s->use_max),
223                                      s->cur_style->use_max_width);
224         gtk_widget_set_sensitive(s->max_width, s->cur_style->use_max_width);
225
226         gtk_spin_button_set_value(GTK_SPIN_BUTTON(s->max_width),
227                                   s->cur_style->max_width);
228
229         n = gtk_combo_box_get_active(GTK_COMBO_BOX(combo));
230         s->cur_style = s->ss->styles[n];
231
232         gtk_font_button_set_font_name(GTK_FONT_BUTTON(s->text_font),
233                                       s->cur_style->font);
234
235         gdk_color_parse(s->cur_style->colour, &col);
236         gtk_color_button_set_color(GTK_COLOR_BUTTON(s->text_colour), &col);
237 }
238
239
240 static void do_layout(struct _stylesheetwindow *s, GtkWidget *b)
241 {
242         GtkWidget *table;
243         GtkWidget *line;
244         GtkWidget *label;
245         GtkWidget *combo;
246         GtkWidget *box;
247         GtkWidget *vbox;
248         int i;
249
250         box = gtk_hbox_new(FALSE, 5);
251         gtk_box_pack_start(GTK_BOX(b), box, FALSE, FALSE, 5);
252         label = gtk_label_new("Style:");
253         gtk_misc_set_alignment(GTK_MISC(label), 1.0, 0.5);
254         gtk_box_pack_start(GTK_BOX(box), label, FALSE, FALSE, 0);
255         combo = gtk_combo_box_new_text();
256         for ( i=0; i<s->ss->n_styles; i++ ) {
257                 gtk_combo_box_append_text(GTK_COMBO_BOX(combo),
258                                           s->ss->styles[i]->name);
259         }
260         gtk_combo_box_set_active(GTK_COMBO_BOX(combo), 0);
261         g_signal_connect(G_OBJECT(combo), "changed",
262                          G_CALLBACK(style_changed_sig), s);
263         gtk_box_pack_start(GTK_BOX(box), combo, TRUE, TRUE, 0);
264
265         line = gtk_hseparator_new();
266         gtk_box_pack_start(GTK_BOX(b), line, FALSE, FALSE, 5);
267
268         box = gtk_hbox_new(TRUE, 30);
269         gtk_box_pack_start(GTK_BOX(b), box, FALSE, FALSE, 5);
270
271         vbox = gtk_vbox_new(FALSE, 0);
272         gtk_box_pack_start(GTK_BOX(box), vbox, FALSE, FALSE, 0);
273         label = gtk_label_new("Margins:");
274         gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
275         gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 5);
276         table = gtk_table_new(3, 3, TRUE);
277         gtk_table_set_row_spacings(GTK_TABLE(table), 5.0);
278         gtk_table_set_col_spacings(GTK_TABLE(table), 5.0);
279         gtk_box_pack_start(GTK_BOX(vbox), table, TRUE, TRUE, 0);
280
281         /* Left */
282         s->margin_left = gtk_spin_button_new_with_range(0.0, 1024.0, 1.0);
283         gtk_table_attach_defaults(GTK_TABLE(table), s->margin_left,
284                                   0, 1, 1, 2);
285         g_signal_connect(G_OBJECT(s->margin_left), "value-changed",
286                          G_CALLBACK(margin_left_changed_sig), s);
287
288         /* Up */
289         s->margin_top = gtk_spin_button_new_with_range(0.0, 1024.0, 1.0);
290         gtk_table_attach_defaults(GTK_TABLE(table), s->margin_top,
291                                   1, 2, 0, 1);
292         g_signal_connect(G_OBJECT(s->margin_top), "value-changed",
293                          G_CALLBACK(margin_top_changed_sig), s);
294
295         /* Right */
296         s->margin_right = gtk_spin_button_new_with_range(0.0, 1024.0, 1.0);
297         gtk_table_attach_defaults(GTK_TABLE(table), s->margin_right,
298                                   2, 3, 1, 2);
299         g_signal_connect(G_OBJECT(s->margin_right), "value-changed",
300                          G_CALLBACK(margin_right_changed_sig), s);
301
302         /* Down */
303         s->margin_bottom = gtk_spin_button_new_with_range(0.0, 1024.0, 1.0);
304         gtk_table_attach_defaults(GTK_TABLE(table), s->margin_bottom,
305                                   1, 2, 2, 3);
306         g_signal_connect(G_OBJECT(s->margin_bottom), "value-changed",
307                          G_CALLBACK(margin_bottom_changed_sig), s);
308
309         vbox = gtk_vbox_new(FALSE, 0);
310         gtk_box_pack_start(GTK_BOX(box), vbox, FALSE, FALSE, 0);
311         label = gtk_label_new("Offset from centre:");
312         gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
313         gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 5);
314         table = gtk_table_new(2, 2, TRUE);
315         gtk_table_set_row_spacings(GTK_TABLE(table), 5.0);
316         gtk_table_set_col_spacings(GTK_TABLE(table), 5.0);
317         gtk_box_pack_start(GTK_BOX(vbox), table, TRUE, TRUE, 0);
318
319         /* Up */
320         label = gtk_label_new("Upwards:");
321         gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 1, 0, 1);
322         s->offset_y = gtk_spin_button_new_with_range(-s->p->slide_height,
323                                                      +s->p->slide_height, 1.0);
324         gtk_table_attach_defaults(GTK_TABLE(table), s->offset_y, 1, 2, 0, 1);
325         g_signal_connect(G_OBJECT(s->offset_y), "value-changed",
326                          G_CALLBACK(offset_y_changed_sig), s);
327
328         /* Right */
329         label = gtk_label_new("Across:");
330         gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 1, 1, 2);
331         s->offset_x = gtk_spin_button_new_with_range(-s->p->slide_width,
332                                                      +s->p->slide_width, 1.0);
333         gtk_table_attach_defaults(GTK_TABLE(table), s->offset_x, 1, 2, 1, 2);
334         g_signal_connect(G_OBJECT(s->offset_x), "value-changed",
335                          G_CALLBACK(offset_x_changed_sig), s);
336
337         table = gtk_table_new(3, 2, TRUE);
338         gtk_table_set_row_spacings(GTK_TABLE(table), 5.0);
339         gtk_table_set_col_spacings(GTK_TABLE(table), 5.0);
340         gtk_box_pack_start(GTK_BOX(b), table, FALSE, FALSE, 5);
341         label = gtk_label_new("Horizontal alignment:");
342         gtk_misc_set_alignment(GTK_MISC(label), 1.0, 0.5);
343         gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 1, 0, 1);
344         s->halign = gtk_combo_box_new_text();
345         gtk_table_attach_defaults(GTK_TABLE(table), s->halign, 1, 2, 0, 1);
346         gtk_combo_box_append_text(GTK_COMBO_BOX(s->halign), "Left");
347         gtk_combo_box_append_text(GTK_COMBO_BOX(s->halign), "Centre");
348         gtk_combo_box_append_text(GTK_COMBO_BOX(s->halign), "Right");
349         g_signal_connect(G_OBJECT(s->halign), "changed",
350                          G_CALLBACK(halign_changed_sig), s);
351
352         label = gtk_label_new("Vertical alignment:");
353         gtk_misc_set_alignment(GTK_MISC(label), 1.0, 0.5);
354         gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 1, 1, 2);
355         s->valign = gtk_combo_box_new_text();
356         gtk_table_attach_defaults(GTK_TABLE(table), s->valign, 1, 2, 1, 2);
357         gtk_combo_box_append_text(GTK_COMBO_BOX(s->valign), "Top");
358         gtk_combo_box_append_text(GTK_COMBO_BOX(s->valign), "Centre");
359         gtk_combo_box_append_text(GTK_COMBO_BOX(s->valign), "Bottom");
360         g_signal_connect(G_OBJECT(s->valign), "changed",
361                          G_CALLBACK(valign_changed_sig), s);
362
363         label = gtk_label_new("Maximum width:");
364         gtk_misc_set_alignment(GTK_MISC(label), 1.0, 0.5);
365         gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 1, 2, 3);
366         box = gtk_hbox_new(FALSE, 5.0);
367         gtk_table_attach_defaults(GTK_TABLE(table), box, 1, 2, 2, 3);
368         s->use_max = gtk_check_button_new();
369         gtk_box_pack_start(GTK_BOX(box), s->use_max, FALSE, FALSE, 0);
370         s->max_width = gtk_spin_button_new_with_range(0.0, 1024.0, 1.0);
371         gtk_box_pack_start(GTK_BOX(box), s->max_width, FALSE, FALSE, 0);
372         g_signal_connect(G_OBJECT(s->use_max), "toggled",
373                          G_CALLBACK(use_max_toggled_sig), s);
374         g_signal_connect(G_OBJECT(s->max_width), "value-changed",
375                          G_CALLBACK(max_changed_sig), s);
376
377         /* Font/colour stuff */
378         label = gtk_label_new("Font:");
379         gtk_misc_set_alignment(GTK_MISC(label), 1.0, 0.5);
380         gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 1, 3, 4);
381         s->text_font = gtk_font_button_new_with_font("Sans 12");
382         box = gtk_hbox_new(FALSE, 0);
383         gtk_table_attach_defaults(GTK_TABLE(table), box, 1, 2, 3, 4);
384         gtk_box_pack_start(GTK_BOX(box), s->text_font, FALSE, FALSE, 0);
385         g_signal_connect(G_OBJECT(s->text_font), "font-set",
386                          G_CALLBACK(text_font_set_sig), s);
387
388         label = gtk_label_new("Colour:");
389         gtk_misc_set_alignment(GTK_MISC(label), 1.0, 0.5);
390         gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 1, 4, 5);
391         s->text_colour = gtk_color_button_new();
392         box = gtk_hbox_new(FALSE, 0);
393         gtk_table_attach_defaults(GTK_TABLE(table), box, 1, 2, 4, 5);
394         gtk_box_pack_start(GTK_BOX(box), s->text_colour, FALSE, FALSE, 0);
395         g_signal_connect(G_OBJECT(s->text_colour), "color-set",
396                          G_CALLBACK(text_colour_set_sig), s);
397
398         /* Force first update */
399         style_changed_sig(GTK_COMBO_BOX(combo), s);
400 }
401
402
403
404 static gint destroy_stylesheet_sig(GtkWidget *w, struct _stylesheetwindow *s)
405 {
406         s->p->stylesheetwindow = NULL;
407         free(s);
408         return FALSE;
409 }
410
411
412 struct style *new_style(StyleSheet *ss, const char *name)
413 {
414         struct style *sty;
415         int n;
416         struct style **styles_new;
417
418         sty = calloc(1, sizeof(*sty));
419         if ( sty == NULL ) return NULL;
420
421         sty->name = strdup(name);
422         sty->role = S_ROLE_NONE;
423
424         n = ss->n_styles;
425         styles_new = realloc(ss->styles, (n+1)*sizeof(sty));
426         if ( styles_new == NULL ) {
427                 free(sty->name);
428                 free(sty);
429                 return NULL;
430         }
431         ss->styles = styles_new;
432         ss->styles[n] = sty;
433         ss->n_styles = n+1;
434
435         return sty;
436 }
437
438
439 void free_stylesheet(StyleSheet *ss)
440 {
441         int i;
442
443         for ( i=0; i<ss->n_styles; i++ ) {
444                 free(ss->styles[i]->name);
445                 free(ss->styles[i]);
446         }
447
448         free(ss->styles);
449         free(ss);
450 }
451
452
453 void default_stylesheet(StyleSheet *ss)
454 {
455         struct style *sty;
456
457         /* Default style must be first */
458         sty = new_style(ss, "Default");
459         sty->font = strdup("Sans 18");
460         sty->colour = strdup("#000000000000");  /* Black */
461         sty->alpha = 1.0;
462         sty->margin_left = 20.0;
463         sty->margin_right = 20.0;
464         sty->margin_top = 20.0;
465         sty->margin_bottom = 20.0;
466         sty->halign = J_CENTER;  /* Ignored */
467         sty->valign = V_CENTER;  /* Ignored */
468         sty->offset_x = 0.0;  /* Ignored */
469         sty->offset_y = 0.0;  /* Ignored */
470
471         sty = new_style(ss, "Slide title");
472         sty->font = strdup("Sans 40");
473         sty->colour = strdup("#000000000000");  /* Black */
474         sty->alpha = 1.0;
475         sty->margin_left = 20.0;
476         sty->margin_right = 20.0;
477         sty->margin_top = 20.0;
478         sty->margin_bottom = 20.0;
479         sty->halign = J_CENTER;
480         sty->valign = V_TOP;
481         sty->offset_x = 0.0;
482         sty->offset_y = 0.0;  /* irrelevant */
483
484         sty = new_style(ss, "Slide credit");
485         sty->font = strdup("Sans 14");
486         sty->colour = strdup("#000000000000");  /* Black */
487         sty->alpha = 1.0;
488         sty->margin_left = 20.0;
489         sty->margin_right = 20.0;
490         sty->margin_top = 20.0;
491         sty->margin_bottom = 35.0;
492         sty->halign = J_RIGHT;
493         sty->valign = V_BOTTOM;
494         sty->offset_x = 0.0;
495         sty->offset_y = 0.0;
496
497         sty = new_style(ss, "Slide date");
498         sty->font = strdup("Sans 12");
499         sty->colour = strdup("#999999999999");  /* Grey */
500         sty->alpha = 1.0;
501         sty->margin_left = 600.0;
502         sty->margin_right = 100.0;
503         sty->margin_top = 745.0;
504         sty->margin_bottom = 5.0;
505         sty->halign = J_RIGHT;
506         sty->valign = V_BOTTOM;
507         sty->offset_x = 0.0;
508         sty->offset_y = 0.0;
509         sty->role = S_ROLE_PDATE;
510
511         sty = new_style(ss, "Slide number");
512         sty->font = strdup("Sans 12");
513         sty->colour = strdup("#999999999999");  /* Grey */
514         sty->alpha = 1.0;
515         sty->margin_left = 600.0;
516         sty->margin_right = 5.0;
517         sty->margin_top = 745.0;
518         sty->margin_bottom = 5.0;
519         sty->halign = J_RIGHT;
520         sty->valign = V_BOTTOM;
521         sty->offset_x = 0.0;
522         sty->offset_y = 0.0;
523         sty->role = S_ROLE_SLIDENUMBER;
524
525         sty = new_style(ss, "Presentation title on slide");
526         sty->font = strdup("Sans 12");
527         sty->colour = strdup("#999999999999");  /* Grey */
528         sty->alpha = 1.0;
529         sty->margin_left = 5.0;
530         sty->margin_right = 600.0;
531         sty->margin_top = 745.0;
532         sty->margin_bottom = 5.0;
533         sty->halign = J_LEFT;
534         sty->valign = V_BOTTOM;
535         sty->offset_x = 0.0;
536         sty->offset_y = 0.0;
537         sty->role = S_ROLE_PTITLE;
538
539         sty = new_style(ss, "Presentation title");
540         sty->font = strdup("Sans 50");
541         sty->colour = strdup("#000000000000");  /* Black */
542         sty->alpha = 1.0;
543         sty->margin_left = 20.0;
544         sty->margin_right = 20.0;
545         sty->margin_top = 20.0;
546         sty->margin_bottom = 20.0;
547         sty->halign = J_CENTER;
548         sty->valign = V_CENTER;
549         sty->offset_x = -200.0;
550         sty->offset_y = -200.0;
551         sty->role = S_ROLE_PTITLE_REF;
552
553         sty = new_style(ss, "Presentation author");
554         sty->font = strdup("Sans 30");
555         sty->colour = strdup("#000000000000");  /* Black */
556         sty->alpha = 1.0;
557         sty->margin_left = 20.0;
558         sty->margin_right = 20.0;
559         sty->margin_top = 20.0;
560         sty->margin_bottom = 20.0;
561         sty->halign = J_CENTER;
562         sty->valign = V_CENTER;
563         sty->offset_x = +200.0;
564         sty->offset_y = -300.0;
565         sty->role = S_ROLE_PAUTHOR_REF;
566
567         sty = new_style(ss, "Presentation date");
568         sty->font = strdup("Sans 30");
569         sty->colour = strdup("#000000000000");  /* Black */
570         sty->alpha = 1.0;
571         sty->margin_left = 20.0;
572         sty->margin_right = 20.0;
573         sty->margin_top = 20.0;
574         sty->margin_bottom = 20.0;
575         sty->halign = J_CENTER;
576         sty->valign = V_CENTER;
577         sty->offset_x = +200.0;
578         sty->offset_y = -300.0;
579         sty->role = S_ROLE_PDATE_REF;
580
581         ss->bgblocks = malloc(sizeof(struct bgblock));
582         ss->n_bgblocks = 1;
583         ss->bgblocks[0].type = BGBLOCK_SOLID;
584         ss->bgblocks[0].min_x = 0.0;
585         ss->bgblocks[0].max_x = 1024.0;
586         ss->bgblocks[0].min_y = 0.0;
587         ss->bgblocks[0].max_y = 768.0;
588         ss->bgblocks[0].colour1 = strdup("#ffffffffffff");
589         ss->bgblocks[0].alpha1 = 1.0;
590 }
591
592
593 static const char *str_halign(enum justify halign)
594 {
595         switch ( halign ) {
596                 case J_LEFT   : return "left";
597                 case J_RIGHT  : return "right";
598                 case J_CENTER : return "center";
599                 default : return "???";
600         }
601 }
602
603
604 enum justify str_to_halign(char *halign)
605 {
606         if ( strcmp(halign, "left") == 0 ) return J_LEFT;
607         if ( strcmp(halign, "right") == 0 ) return J_RIGHT;
608         if ( strcmp(halign, "center") == 0 ) return J_CENTER;
609
610         return J_LEFT;
611 }
612
613
614 static const char *str_valign(enum vert_pos valign)
615 {
616         switch ( valign ) {
617                 case V_TOP    : return "top";
618                 case V_BOTTOM : return "bottom";
619                 case V_CENTER : return "center";
620                 default : return "???";
621         }
622 }
623
624
625 enum vert_pos str_to_valign(char *valign)
626 {
627         if ( strcmp(valign, "top") == 0 ) return V_TOP;
628         if ( strcmp(valign, "bottom") == 0 ) return V_BOTTOM;
629         if ( strcmp(valign, "center") == 0 ) return V_CENTER;
630
631         return J_LEFT;
632 }
633
634
635 static const char *str_role(enum object_role r)
636 {
637         switch ( r ) {
638                 case S_ROLE_NONE        : return "none";
639                 case S_ROLE_SLIDENUMBER : return "slidenumber";
640                 case S_ROLE_PTITLE      : return "ptitle";
641                 case S_ROLE_PTITLE_REF  : return "ptitle-ref";
642                 case S_ROLE_PAUTHOR     : return "pauthor";
643                 case S_ROLE_PAUTHOR_REF : return "pauthor-ref";
644                 case S_ROLE_PDATE       : return "pdate";
645                 case S_ROLE_PDATE_REF   : return "pdate-ref";
646                 default : return "???";
647         }
648 }
649
650
651 enum object_role str_to_role(const char *s)
652 {
653         if ( strcmp(s, "slidenumber") == 0 ) return S_ROLE_SLIDENUMBER;
654         if ( strcmp(s, "ptitle") == 0 )      return S_ROLE_PTITLE;
655         if ( strcmp(s, "ptitle-ref") == 0 )  return S_ROLE_PTITLE_REF;
656         if ( strcmp(s, "pauthor") == 0 )     return S_ROLE_PAUTHOR;
657         if ( strcmp(s, "pauthor-ref") == 0 ) return S_ROLE_PAUTHOR_REF;
658         if ( strcmp(s, "pdate") == 0 )       return S_ROLE_PDATE;
659         if ( strcmp(s, "pdate-ref") == 0 )   return S_ROLE_PDATE_REF;
660
661         return S_ROLE_NONE;
662 }
663
664
665 static const char *str_bgtype(enum bgblocktype t)
666 {
667         switch ( t ) {
668                 case BGBLOCK_SOLID             : return "solid";
669                 case BGBLOCK_GRADIENT_X        : return "gradient_x";
670                 case BGBLOCK_GRADIENT_Y        : return "gradient_y";
671                 case BGBLOCK_GRADIENT_CIRCULAR : return "gradient_circular";
672                 case BGBLOCK_IMAGE             : return "image";
673                 default : return "???";
674         }
675 }
676
677
678 static enum bgblocktype str_to_bgtype(char *t)
679 {
680         if ( strcmp(t, "solid") == 0 ) return BGBLOCK_SOLID;
681         if ( strcmp(t, "gradient_x") == 0 ) return BGBLOCK_GRADIENT_X;
682         if ( strcmp(t, "gradient_y") == 0 ) return BGBLOCK_GRADIENT_Y;
683         if ( strcmp(t, "gradient_ciruclar") == 0 ) {
684                 return BGBLOCK_GRADIENT_CIRCULAR;
685         }
686         if ( strcmp(t, "image") == 0 ) return BGBLOCK_IMAGE;
687
688         return BGBLOCK_SOLID;
689 }
690
691
692 static int read_style(struct style *sty, struct ds_node *root)
693 {
694         char *align;
695         char *role;
696
697         get_field_s(root, "role", &role);
698         if ( role != NULL ) {
699                 sty->role = str_to_role(role);
700                 free(role);
701         } else {
702                 sty->role = S_ROLE_NONE;
703         }
704
705         get_field_f(root, "margin_left",   &sty->margin_left);
706         get_field_f(root, "margin_right",  &sty->margin_right);
707         get_field_f(root, "margin_top",    &sty->margin_top);
708         get_field_f(root, "margin_bottom", &sty->margin_bottom);
709
710         get_field_i(root, "use_max_width", &sty->use_max_width);
711         get_field_f(root, "max_width",     &sty->max_width);
712
713         get_field_f(root, "offset_x",      &sty->offset_x);
714         get_field_f(root, "offset_y",      &sty->offset_y);
715
716         get_field_s(root, "font",          &sty->font);
717         get_field_s(root, "colour",        &sty->colour);
718         get_field_f(root, "alpha",         &sty->alpha);
719
720         get_field_s(root, "halign",        &align);
721         sty->halign = str_to_halign(align);
722         free(align);
723         get_field_s(root, "valign",        &align);
724         sty->valign = str_to_valign(align);
725         free(align);
726
727         return 0;
728 }
729
730
731 static int read_bgblock(struct bgblock *b, struct ds_node *root)
732 {
733         char *type;
734
735         get_field_s(root, "type",  &type);
736         b->type = str_to_bgtype(type);
737
738         get_field_f(root, "min_x",  &b->min_x);
739         get_field_f(root, "max_x",  &b->max_x);
740         get_field_f(root, "min_y",  &b->min_y);
741         get_field_f(root, "max_y",  &b->max_y);
742
743         switch ( b->type ) {
744
745                 case BGBLOCK_SOLID :
746                 get_field_s(root, "colour1",  &b->colour1);
747                 get_field_f(root, "alpha1",  &b->alpha1);
748
749                 case BGBLOCK_GRADIENT_X :
750                 case BGBLOCK_GRADIENT_Y :
751                 case BGBLOCK_GRADIENT_CIRCULAR :
752                 get_field_s(root, "colour1",  &b->colour1);
753                 get_field_f(root, "alpha1",  &b->alpha1);
754                 get_field_s(root, "colour2",  &b->colour2);
755                 get_field_f(root, "alpha2",  &b->alpha2);
756
757                 default:
758                 break;
759
760         }
761
762         return 0;
763 }
764
765
766 StyleSheet *tree_to_stylesheet(struct ds_node *root)
767 {
768         StyleSheet *ss;
769         struct ds_node *node;
770         int i;
771
772         ss = new_stylesheet();
773         if ( ss == NULL ) return NULL;
774
775         node = find_node(root, "styles", 0);
776         if ( node == NULL ) {
777                 fprintf(stderr, "Couldn't find styles\n");
778                 free_stylesheet(ss);
779                 return NULL;
780         }
781
782         for ( i=0; i<node->n_children; i++ ) {
783
784                 struct style *ns;
785                 char *v;
786
787                 get_field_s(node->children[i], "name", &v);
788                 if ( v == NULL ) {
789                         fprintf(stderr, "No name for style '%s'\n",
790                                 node->children[i]->key);
791                         continue;
792                 }
793
794                 ns = new_style(ss, v);
795                 if ( ns == NULL ) {
796                         fprintf(stderr, "Couldn't create style for '%s'\n",
797                                 node->children[i]->key);
798                         continue;
799                 }
800
801                 if ( read_style(ns, node->children[i]) ) {
802                         fprintf(stderr, "Couldn't read style '%s'\n", v);
803                         continue;
804                 }
805
806         }
807
808         node = find_node(root, "bgblocks", 0);
809         if ( node == NULL ) {
810                 fprintf(stderr, "Couldn't find bgblocks\n");
811                 free_stylesheet(ss);
812                 return NULL;
813         }
814
815         ss->bgblocks = malloc(node->n_children * sizeof(struct bgblock));
816         if ( ss->bgblocks == NULL ) {
817                 fprintf(stderr, "Couldn't allocate bgblocks\n");
818                 free_stylesheet(ss);
819                 return NULL;
820         }
821         ss->n_bgblocks = node->n_children;
822
823         for ( i=0; i<node->n_children; i++ ) {
824
825                 struct bgblock *b;
826
827                 b = &ss->bgblocks[i];
828
829                 if ( read_bgblock(b, node->children[i]) ) {
830                         fprintf(stderr, "Couldn't read bgblock %i\n", i);
831                         continue;
832                 }
833
834         }
835
836         return ss;
837 }
838
839
840 StyleSheet *new_stylesheet()
841 {
842         StyleSheet *ss;
843
844         ss = calloc(1, sizeof(struct _stylesheet));
845         if ( ss == NULL ) return NULL;
846
847         ss->n_styles = 0;
848         ss->styles = NULL;
849
850         return ss;
851 }
852
853
854 int save_stylesheet(StyleSheet *ss, const char *filename)
855 {
856         FILE *fh;
857         struct serializer ser;
858
859         fh = fopen(filename, "w");
860         if ( fh == NULL ) return 1;
861
862         /* Set up the serializer */
863         ser.fh = fh;
864         ser.stack_depth = 0;
865         ser.prefix = NULL;
866
867         fprintf(fh, "# Colloquium style sheet file\n");
868         serialize_f(&ser, "version", 0.1);
869
870         serialize_start(&ser, "stylesheet");
871         write_stylesheet(ss, &ser);
872         serialize_end(&ser);
873
874         return 0;
875 }
876
877
878 StyleSheet *load_stylesheet(const char *filename)
879 {
880         StyleSheet *ss;
881
882         ss = new_stylesheet();
883         if ( ss == NULL ) return NULL;
884
885         /* FIXME: Implement this */
886
887         return ss;
888 }
889
890
891 StylesheetWindow *open_stylesheet(struct presentation *p)
892 {
893         struct _stylesheetwindow *s;
894         GtkWidget *nb;
895         GtkWidget *text_box;
896         GtkWidget *background_box;
897
898         s = malloc(sizeof(*s));
899         if ( s == NULL ) return NULL;
900
901         s->p = p;
902         s->ss = p->ss;
903         s->cur_style = NULL;
904         s->cur_style = NULL;
905
906         s->window = gtk_dialog_new_with_buttons("Stylesheet",
907                                            GTK_WINDOW(p->window), 0,
908                                            GTK_STOCK_CLOSE, GTK_RESPONSE_ACCEPT,
909                                            NULL);
910         gtk_dialog_set_has_separator(GTK_DIALOG(s->window), FALSE);
911
912         nb = gtk_notebook_new();
913         gtk_notebook_set_tab_pos(GTK_NOTEBOOK(nb), GTK_POS_TOP);
914         gtk_box_pack_start(GTK_BOX(GTK_DIALOG(s->window)->vbox), nb,
915                            TRUE, TRUE, 0);
916
917         text_box = gtk_vbox_new(FALSE, 0);
918         gtk_container_set_border_width(GTK_CONTAINER(text_box), 12);
919         gtk_notebook_append_page(GTK_NOTEBOOK(nb), text_box,
920                                  gtk_label_new("Styles"));
921         do_layout(s, text_box);
922
923         background_box = gtk_vbox_new(FALSE, 0);
924         gtk_container_set_border_width(GTK_CONTAINER(background_box), 12);
925         gtk_notebook_append_page(GTK_NOTEBOOK(nb), background_box,
926                                  gtk_label_new("Background"));
927
928         g_signal_connect(G_OBJECT(s->window), "destroy",
929                          G_CALLBACK(destroy_stylesheet_sig), s);
930         g_signal_connect(G_OBJECT(s->window), "response",
931                          G_CALLBACK(gtk_widget_destroy), NULL);
932
933         gtk_widget_show_all(s->window);
934
935         return s;
936 }
937
938
939 void write_stylesheet(StyleSheet *ss, struct serializer *ser)
940 {
941         int i;
942
943         serialize_start(ser, "bgblocks");
944         for ( i=0; i<ss->n_bgblocks; i++ ) {
945
946                 struct bgblock *b = &ss->bgblocks[i];
947                 char id[32];
948
949                 snprintf(id, 31, "%i", i);
950
951                 serialize_start(ser, id);
952                 serialize_s(ser, "type", str_bgtype(b->type));
953                 serialize_f(ser, "min_x", b->min_x);
954                 serialize_f(ser, "min_y", b->min_y);
955                 serialize_f(ser, "max_x", b->max_x);
956                 serialize_f(ser, "max_y", b->max_y);
957
958                 switch ( b->type ) {
959
960                         case BGBLOCK_SOLID :
961                         serialize_s(ser, "colour1", b->colour1);
962                         serialize_f(ser, "alpha1", b->alpha1);
963                         break;
964
965                         case BGBLOCK_GRADIENT_X :
966                         case BGBLOCK_GRADIENT_Y :
967                         case BGBLOCK_GRADIENT_CIRCULAR :
968                         serialize_s(ser, "colour1", b->colour1);
969                         serialize_f(ser, "alpha1", b->alpha1);
970                         serialize_s(ser, "colour2", b->colour2);
971                         serialize_f(ser, "alpha2", b->alpha2);
972                         break;
973
974                         default:
975                         break;
976
977                 }
978
979                 serialize_end(ser);
980
981         }
982         serialize_end(ser);
983
984         serialize_start(ser, "styles");
985         for ( i=0; i<ss->n_styles; i++ ) {
986
987                 struct style *s = ss->styles[i];
988                 char id[32];
989
990                 snprintf(id, 31, "%i", i);
991
992                 serialize_start(ser, id);
993                 serialize_s(ser, "name", s->name);
994                 serialize_s(ser, "role", str_role(s->role));
995                 serialize_f(ser, "margin_left", s->margin_left);
996                 serialize_f(ser, "margin_right", s->margin_right);
997                 serialize_f(ser, "margin_top", s->margin_top);
998                 serialize_f(ser, "margin_bottom", s->margin_bottom);
999                 serialize_b(ser, "use_max_width", s->use_max_width);
1000                 serialize_f(ser, "max_width", s->max_width);
1001                 serialize_f(ser, "offset_x", s->offset_x);
1002                 serialize_f(ser, "offset_y", s->offset_y);
1003                 serialize_s(ser, "font", s->font);
1004                 serialize_s(ser, "colour", s->colour);
1005                 serialize_f(ser, "alpha", s->alpha);
1006                 serialize_s(ser, "halign", str_halign(s->halign));
1007                 serialize_s(ser, "valign", str_valign(s->valign));
1008                 serialize_end(ser);
1009
1010         }
1011         serialize_end(ser);
1012 }
1013
1014
1015 struct style *find_style(StyleSheet *ss, const char *name)
1016 {
1017         int i;
1018         for ( i=0; i<ss->n_styles; i++ ) {
1019                 if ( strcmp(ss->styles[i]->name, name) == 0 ) {
1020                         return ss->styles[i];
1021                 }
1022         }
1023
1024         return NULL;
1025 }