Implement Insert->Image
[colloquium.git] / src / slide_window.c
1 /*
2  * slide_window.c
3  *
4  * Copyright © 2013-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 <gtk/gtk.h>
31 #include <assert.h>
32 #include <gdk/gdkkeysyms.h>
33 #include <gdk-pixbuf/gdk-pixbuf.h>
34 #include <math.h>
35
36 #include <libintl.h>
37 #define _(x) gettext(x)
38
39 #include <narrative.h>
40 #include <slide.h>
41 #include <gtkslideview.h>
42
43 #include "colloquium.h"
44 #include "slide_window.h"
45 #include "narrative_window.h"
46
47
48 struct _slidewindow
49 {
50         GtkWidget           *window;
51         Narrative           *n;
52         Slide               *slide;
53         GtkWidget           *sv;
54         NarrativeWindow     *parent;
55 };
56
57
58 static void insert_slidetitle_sig(GSimpleAction *action, GVariant *parameter,
59                                   gpointer vp)
60 {
61         SlideWindow *sw = vp;
62         char **text = malloc(sizeof(char *));
63         *text = strdup("Slide title");
64         slide_add_slidetitle(sw->slide, text, 1);
65         gtk_slide_view_set_slide(sw->sv, sw->slide);
66 }
67
68
69 static gint insert_image_response_sig(GtkWidget *d, gint response, SlideWindow *sw)
70 {
71         GtkWidget *cb;
72         const char *size_str;
73
74         cb = gtk_file_chooser_get_extra_widget(GTK_FILE_CHOOSER(d));
75         size_str = gtk_combo_box_get_active_id(GTK_COMBO_BOX(cb));
76
77         if ( response == GTK_RESPONSE_ACCEPT ) {
78
79                 char *filename;
80                 struct frame_geom geom;
81                 char *fn;
82                 double slide_w, slide_h;
83                 gint image_w, image_h;
84                 double aspect;
85                 GdkPixbufFormat *f;
86
87                 filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(d));
88                 fn = strdup(filename);
89                 if ( fn == NULL ) return 0;
90
91                 if ( slide_get_logical_size(sw->slide, narrative_get_stylesheet(sw->n),
92                                             &slide_w, &slide_h) ) return 0;
93
94                 f = gdk_pixbuf_get_file_info(filename, &image_w, &image_h);
95                 if ( f == NULL ) return 0;
96                 aspect = (double)image_h / image_w;
97                 g_free(filename);
98
99                 if ( strcmp(size_str, "normal") == 0 ) {
100                         geom.x.len = slide_w/4.0;  geom.x.unit = LENGTH_UNIT;
101                         geom.y.len = slide_h/4.0;  geom.y.unit = LENGTH_UNIT;
102                         geom.w.len = slide_w/2.0;  geom.w.unit = LENGTH_UNIT;
103                         geom.h.len = geom.w.len*aspect;  geom.h.unit = LENGTH_UNIT;
104                 }
105
106                 if ( strcmp(size_str, "fillentire") == 0 ) {
107                         geom.x.len = 0.0;  geom.x.unit = LENGTH_UNIT;
108                         geom.y.len = 0.0;  geom.y.unit = LENGTH_UNIT;
109                         geom.w.len = 1.0;  geom.w.unit = LENGTH_FRAC;
110                         geom.h.len = 1.0;  geom.h.unit = LENGTH_FRAC;
111                 }
112
113                 slide_add_image(sw->slide, fn, geom);
114         }
115
116         gtk_widget_destroy(d);
117
118         return 0;
119 }
120
121
122 static void insert_image_sig(GSimpleAction *action, GVariant *parameter,
123                              gpointer vp)
124 {
125         SlideWindow *sw = vp;
126         GtkWidget *d;
127         GtkWidget *cb;
128
129         d = gtk_file_chooser_dialog_new(_("Insert image"),
130                                         NULL,
131                                         GTK_FILE_CHOOSER_ACTION_OPEN,
132                                         _("_Cancel"), GTK_RESPONSE_CANCEL,
133                                         _("_Insert"), GTK_RESPONSE_ACCEPT,
134                                         NULL);
135
136         cb = gtk_combo_box_text_new();
137
138         gtk_combo_box_text_append(GTK_COMBO_BOX_TEXT(cb), "normal",
139                                   _("Make the image about half the width of the slide"));
140         gtk_combo_box_text_append(GTK_COMBO_BOX_TEXT(cb), "fillentire",
141                                   _("Fill the entire slide, even the title and footer regions"));
142
143         gtk_combo_box_set_active_id(GTK_COMBO_BOX(cb), "normal");
144
145         gtk_file_chooser_set_extra_widget(GTK_FILE_CHOOSER(d), cb);
146
147         g_signal_connect(G_OBJECT(d), "response",
148                          G_CALLBACK(insert_image_response_sig), sw);
149
150         gtk_widget_show_all(d);
151 }
152
153
154 static void paste_sig(GSimpleAction *action, GVariant *parameter,
155                       gpointer vp)
156 {
157         //SlideWindow *sw = vp;
158         //sc_editor_paste(sw->sceditor);
159 }
160
161
162 static void copy_frame_sig(GSimpleAction *action, GVariant *parameter,
163                              gpointer vp)
164 {
165         //SlideWindow *sw = vp;
166         //sc_editor_copy_selected_frame(sw->sceditor);
167 }
168
169
170 static void delete_frame_sig(GSimpleAction *action, GVariant *parameter,
171                              gpointer vp)
172 {
173         //SlideWindow *sw = vp;
174         //sc_editor_delete_selected_frame(sw->sceditor);
175 }
176
177
178 /* Change the editor's slide to "np" */
179 static void change_edit_slide(SlideWindow *sw, Slide *np)
180 {
181         gtk_slide_view_set_slide(sw->sv, np);
182         sw->slide = np;
183         slide_window_update_titlebar(sw);
184 }
185
186
187 static void change_slide_first(SlideWindow *sw)
188 {
189         Slide *s = narrative_get_slide_by_number(sw->n, 0);
190         if ( s != NULL ) change_edit_slide(sw, s);
191 }
192
193
194 static void change_slide_backwards(SlideWindow *sw)
195 {
196         int slide_n = narrative_get_slide_number_for_slide(sw->n, sw->slide);
197         if ( slide_n > 0 ) {
198                 Slide *s = narrative_get_slide_by_number(sw->n, slide_n-1);
199                 change_edit_slide(sw, s);
200         }
201 }
202
203
204 static void change_slide_forwards(SlideWindow *sw)
205 {
206         int slide_n = narrative_get_slide_number_for_slide(sw->n, sw->slide);
207         Slide *s = narrative_get_slide_by_number(sw->n, slide_n+1);
208         if ( s != NULL ) change_edit_slide(sw, s);
209 }
210
211
212 static void change_slide_last(SlideWindow *sw)
213 {
214         int slide_n = narrative_get_num_slides(sw->n);
215         Slide *s = narrative_get_slide_by_number(sw->n, slide_n);
216         if ( s != NULL ) change_edit_slide(sw, s);
217 }
218
219
220 static void first_slide_sig(GSimpleAction *action, GVariant *parameter,
221                            gpointer vp)
222 {
223         SlideWindow *sw = vp;
224         change_slide_first(sw);
225 }
226
227
228 static void prev_slide_sig(GSimpleAction *action, GVariant *parameter,
229                            gpointer vp)
230 {
231         SlideWindow *sw = vp;
232         change_slide_backwards(sw);
233 }
234
235
236 static void next_slide_sig(GSimpleAction *action, GVariant *parameter,
237                            gpointer vp)
238 {
239         SlideWindow *sw = vp;
240         change_slide_forwards(sw);
241 }
242
243
244 static void last_slide_sig(GSimpleAction *action, GVariant *parameter,
245                            gpointer vp)
246 {
247         SlideWindow *sw = vp;
248         change_slide_last(sw);
249 }
250
251
252 static gboolean sw_close_sig(GtkWidget *w, SlideWindow *sw)
253 {
254         narrative_window_sw_closed(sw->parent, sw);
255         return FALSE;
256 }
257
258
259 static gboolean sw_key_press_sig(GtkWidget *da, GdkEventKey *event,
260                                  SlideWindow *sw)
261 {
262         switch ( event->keyval ) {
263
264                 case GDK_KEY_Page_Up :
265                 change_slide_backwards(sw);
266                 break;
267
268                 case GDK_KEY_Page_Down :
269                 change_slide_forwards(sw);
270                 break;
271
272         }
273
274         return FALSE;
275 }
276
277
278 static void about_sig(GSimpleAction *action, GVariant *parameter, gpointer vp)
279 {
280         SlideWindow *sw = vp;
281         open_about_dialog(sw->window);
282 }
283
284
285 GActionEntry sw_entries[] = {
286
287         { "about", about_sig, NULL, NULL, NULL },
288         { "paste", paste_sig, NULL, NULL, NULL },
289         { "copyframe", copy_frame_sig, NULL, NULL, NULL },
290         { "deleteframe", delete_frame_sig, NULL, NULL, NULL },
291         { "first", first_slide_sig, NULL, NULL, NULL },
292         { "prev", prev_slide_sig, NULL, NULL, NULL },
293         { "next", next_slide_sig, NULL, NULL, NULL },
294         { "last", last_slide_sig, NULL, NULL, NULL },
295         { "slidetitle", insert_slidetitle_sig, NULL, NULL, NULL },
296         { "image", insert_image_sig, NULL, NULL, NULL },
297 };
298
299
300 SlideWindow *slide_window_open(Narrative *n, Slide *slide,
301                                NarrativeWindow *nw, GApplication *papp)
302 {
303         GtkWidget *window;
304         SlideWindow *sw;
305         double w, h;
306         Colloquium *app = COLLOQUIUM(papp);
307
308         sw = calloc(1, sizeof(SlideWindow));
309         if ( sw == NULL ) return NULL;
310
311         window = gtk_application_window_new(GTK_APPLICATION(app));
312         gtk_window_set_role(GTK_WINDOW(window), "slide");
313         sw->window = window;
314         sw->n = n;
315         sw->slide = slide;
316         sw->parent = nw;
317
318         slide_window_update_titlebar(sw);
319
320         g_action_map_add_action_entries(G_ACTION_MAP(window), sw_entries,
321                                         G_N_ELEMENTS(sw_entries), sw);
322
323         g_signal_connect(G_OBJECT(window), "destroy",
324                          G_CALLBACK(sw_close_sig), sw);
325
326         sw->sv = gtk_slide_view_new(n, slide);
327
328         g_signal_connect(G_OBJECT(sw->sv), "key-press-event",
329                          G_CALLBACK(sw_key_press_sig), sw);
330
331         slide_get_logical_size(slide, narrative_get_stylesheet(n), &w, &h);
332         gtk_window_set_default_size(GTK_WINDOW(window), w, h);
333
334         gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(sw->sv));
335
336         gtk_window_set_resizable(GTK_WINDOW(sw->window), TRUE);
337
338         gtk_widget_show_all(window);
339
340         return sw;
341 }
342
343
344 void slide_window_update(SlideWindow *sw)
345 {
346         gint w, h;
347         w = gtk_widget_get_allocated_width(GTK_WIDGET(sw->sv));
348         h = gtk_widget_get_allocated_height(GTK_WIDGET(sw->sv));
349         gtk_widget_queue_draw_area(GTK_WIDGET(sw->sv), 0, 0, w, h);
350 }
351
352
353 void slide_window_update_titlebar(SlideWindow *sw)
354 {
355         char title[1026];
356         char *filename;
357
358         filename = narrative_window_get_filename(sw->parent);
359         snprintf(title, 1024, "%s (slide %i) - Colloquium", filename,
360                  1+narrative_get_slide_number_for_slide(sw->n, sw->slide));
361         if ( narrative_get_unsaved(sw->n) ) strcat(title, " *");
362
363         gtk_window_set_title(GTK_WINDOW(sw->window), title);
364 }
365
366
367 void slide_window_destroy(SlideWindow *sw)
368 {
369         gtk_widget_destroy(sw->window);
370 }