Remove struct print_stuff->{is,storename}
[colloquium.git] / src / print.c
1 /*
2  * print.c
3  *
4  * Copyright © 2016-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 <gtk/gtk.h>
29 #include <assert.h>
30 #include <stdlib.h>
31 #include <string.h>
32
33 #include "presentation.h"
34 #include "narrative_window.h"
35 #include "render.h"
36 #include "utils.h"
37
38
39 static GtkPrintSettings *print_settings = NULL;
40
41 struct print_stuff
42 {
43         struct presentation *p;
44
45         /* Printing config */
46         GtkWidget *combo;
47         int slides_only;
48
49         /* When printing slides only */
50         SCBlock *slide;
51
52         /* When printing narrative */
53         int nar_line;
54         struct frame *top;
55         int start_paras[256];
56         int slide_number;
57 };
58
59
60 static void print_widget_apply(GtkPrintOperation *op, GtkWidget *widget,
61                                void *vp)
62 {
63         const char *id;
64         struct print_stuff *ps = vp;
65
66         id = gtk_combo_box_get_active_id(GTK_COMBO_BOX(ps->combo));
67         if ( strcmp(id, "slides") == 0 ) {
68                 ps->slides_only = 1;
69         } else {
70                 ps->slides_only = 0;
71         }
72 }
73
74
75 static GObject *print_widget(GtkPrintOperation *op, void *vp)
76 {
77         GtkWidget *vbox;
78         GtkWidget *cbox;
79         struct print_stuff *ps = vp;
80
81         vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 10);
82         gtk_container_set_border_width(GTK_CONTAINER(vbox), 10);
83
84         /* What do you want to print? */
85         cbox = gtk_combo_box_text_new();
86         gtk_combo_box_text_append(GTK_COMBO_BOX_TEXT(cbox), "slides",
87                                   _("Print the slides only"));
88         gtk_combo_box_text_append(GTK_COMBO_BOX_TEXT(cbox), "narrative",
89                                   _("Print the narrative"));
90         gtk_box_pack_start(GTK_BOX(vbox), cbox, FALSE, FALSE, 10);
91         gtk_combo_box_set_active(GTK_COMBO_BOX(cbox), 1);
92         ps->combo = cbox;
93
94         gtk_widget_show_all(vbox);
95         return G_OBJECT(vbox);
96
97 }
98
99
100 static void print_slide_only(GtkPrintOperation *op, GtkPrintContext *ctx,
101                              struct print_stuff *ps, int page)
102 {
103         cairo_t *cr;
104         PangoContext *pc;
105         double w, h;
106         struct frame *top;
107         const double sw = ps->p->slide_width;
108         const double sh = ps->p->slide_height;
109         double slide_width, slide_height;
110
111         cr = gtk_print_context_get_cairo_context(ctx);
112         pc = gtk_print_context_create_pango_context(ctx);
113         w = gtk_print_context_get_width(ctx);
114         h = gtk_print_context_get_height(ctx);
115
116         cairo_rectangle(cr, 0.0, 0.0, w, h);
117         cairo_set_source_rgb(cr, 1.0, 1.0, 1.0);
118         cairo_fill(cr);
119
120         if ( sw/sh > w/h ) {
121                 /* Slide is too wide.  Letterboxing top/bottom */
122                 slide_width = w;
123                 slide_height = w * sh/sw;
124         } else {
125                 /* Letterboxing at sides */
126                 slide_width = h * sw/sh;
127                 slide_height = h;
128         }
129
130         printf("%f x %f ---> %f x %f\n", w, h, slide_width, slide_height);
131
132         top = interp_and_shape(ps->slide, ps->p->stylesheet, NULL,
133                                ps->p->is, page+1, pc, sw, sh, ps->p->lang);
134
135         recursive_wrap(top, pc);
136
137         cairo_scale(cr, slide_width/sw, slide_width/sw);
138
139         recursive_draw(top, cr, ps->p->is,
140                        0.0, ps->p->slide_height);
141
142         ps->slide = next_slide(ps->p, ps->slide);
143 }
144
145
146 static int print_create_thumbnail(SCInterpreter *scin, SCBlock *bl,
147                                   double *w, double *h, void **bvp, void *vp)
148 {
149         struct print_stuff *ps = vp;
150         struct presentation *p = ps->p;
151         SCBlock *b;
152
153         *w = 270.0*(p->slide_width / p->slide_height);
154         *h = 270.0;
155         b = bl;
156
157         *bvp = b;
158
159         return 1;
160 }
161
162
163 static cairo_surface_t *print_render_thumbnail(int w, int h, void *bvp, void *vp)
164 {
165         struct print_stuff *ps = vp;
166         struct presentation *p = ps->p;
167         SCBlock *scblocks = bvp;
168         cairo_surface_t *surf;
169         struct frame *top;
170
171         surf = render_sc(scblocks, w, h, p->slide_width, p->slide_height,
172                          p->stylesheet, NULL, p->is, ps->slide_number++,
173                          &top, p->lang);
174         frame_free(top);
175
176         return surf;
177 }
178
179
180 static void begin_narrative_print(GtkPrintOperation *op, GtkPrintContext *ctx,
181                                   struct print_stuff *ps)
182 {
183         SCCallbackList *cbl;
184         PangoContext *pc;
185         int i, n_pages;
186         double h, page_height;
187         SCBlock *dummy_top;
188
189         cbl = sc_callback_list_new();
190         ps->slide_number = 1;
191         sc_callback_list_add_callback(cbl, "slide", print_create_thumbnail,
192                                       print_render_thumbnail, NULL, ps);
193
194         pc = gtk_print_context_create_pango_context(ctx);
195
196         dummy_top = sc_block_new_parent(ps->p->scblocks, "presentation");
197         ps->top = interp_and_shape(dummy_top, ps->p->stylesheet, cbl,
198                                    ps->p->is, 0, pc,
199                                    gtk_print_context_get_width(ctx),
200                                    gtk_print_context_get_height(ctx),
201                                    ps->p->lang);
202         recursive_wrap(ps->top, pc);
203
204         /* Count pages */
205         page_height = gtk_print_context_get_height(ctx);
206         h = 0.0;
207         n_pages = 1;
208         ps->start_paras[0] = 0;
209         for ( i=0; i<ps->top->n_paras; i++ ) {
210                 if ( h + paragraph_height(ps->top->paras[i]) > page_height ) {
211                         /* Paragraph does not fit on page */
212                         ps->start_paras[n_pages] = i;
213                         n_pages++;
214                         h = 0.0;
215                 }
216                 h += paragraph_height(ps->top->paras[i]);
217         }
218         gtk_print_operation_set_n_pages(op, n_pages);
219         g_object_unref(pc);
220 }
221
222
223 static void print_narrative(GtkPrintOperation *op, GtkPrintContext *ctx,
224                             struct print_stuff *ps, gint page)
225 {
226         int i;
227         double h, page_height;
228         cairo_t *cr;
229
230         page_height = gtk_print_context_get_height(ctx);
231         cr = gtk_print_context_get_cairo_context(ctx);
232
233         h = 0.0;
234         for ( i=ps->start_paras[page]; i<ps->top->n_paras; i++ ) {
235
236                 /* Will this paragraph fit? */
237                 h += paragraph_height(ps->top->paras[i]);
238                 if ( h > page_height ) return;
239
240                 render_paragraph(cr, ps->top->paras[i], ps->p->is);
241                 cairo_translate(cr, 0.0, paragraph_height(ps->top->paras[i]));
242
243         }
244
245
246 }
247
248
249
250 static void print_begin(GtkPrintOperation *op, GtkPrintContext *ctx, void *vp)
251 {
252         struct print_stuff *ps = vp;
253
254         if ( ps->slides_only ) {
255                 gtk_print_operation_set_n_pages(op, num_slides(ps->p));
256                 ps->slide = first_slide(ps->p);
257         } else {
258                 begin_narrative_print(op, ctx, ps);
259         }
260 }
261
262
263 static void print_draw(GtkPrintOperation *op, GtkPrintContext *ctx, gint page,
264                        void *vp)
265 {
266         struct print_stuff *ps = vp;
267         if ( ps->slides_only ) {
268                 print_slide_only(op, ctx, ps, page);
269         } else {
270                 print_narrative(op, ctx, ps, page);
271         }
272 }
273
274
275 void run_printing(struct presentation *p, GtkWidget *parent)
276 {
277         GtkPrintOperation *print;
278         GtkPrintOperationResult res;
279         struct print_stuff *ps;
280
281         ps = malloc(sizeof(struct print_stuff));
282         if ( ps == NULL ) return;
283         ps->p = p;
284         ps->nar_line = 0;
285
286         print = gtk_print_operation_new();
287         if ( print_settings != NULL ) {
288                 gtk_print_operation_set_print_settings(print, print_settings);
289         }
290
291         g_signal_connect(print, "create-custom-widget",
292                          G_CALLBACK(print_widget), ps);
293         g_signal_connect(print, "custom-widget-apply",
294                          G_CALLBACK(print_widget_apply), ps);
295         g_signal_connect(print, "begin_print", G_CALLBACK(print_begin), ps);
296         g_signal_connect(print, "draw_page", G_CALLBACK(print_draw), ps);
297
298         res = gtk_print_operation_run(print,
299                                       GTK_PRINT_OPERATION_ACTION_PRINT_DIALOG,
300                                       GTK_WINDOW(parent), NULL);
301
302         if ( res == GTK_PRINT_OPERATION_RESULT_APPLY ) {
303                 if ( print_settings != NULL ) {
304                         g_object_unref(print_settings);
305                 }
306                 print_settings = g_object_ref(
307                                  gtk_print_operation_get_print_settings(print));
308         }
309         g_object_unref(print);
310 }
311