Save the stylesheet
[colloquium.git] / src / narrative_window.c
index f0f4d32..3ff369c 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * narrative_window.c
  *
- * Copyright © 2014-2016 Thomas White <taw@bitwiz.org.uk>
+ * Copyright © 2014-2018 Thomas White <taw@bitwiz.org.uk>
  *
  * This file is part of Colloquium.
  *
@@ -39,6 +39,8 @@
 #include "testcard.h"
 #include "pr_clock.h"
 #include "print.h"
+#include "utils.h"
+#include "stylesheet_editor.h"
 
 
 struct _narrative_window
@@ -51,11 +53,29 @@ struct _narrative_window
        SCEditor *sceditor;
        GApplication *app;
        struct presentation *p;
+       SCBlock             *dummy_top;
        SCSlideshow         *show;
+       int                  show_no_slides;
        PRClock             *pr_clock;
 };
 
 
+static void show_error(NarrativeWindow *nw, const char *err)
+{
+       GtkWidget *mw;
+
+       mw = gtk_message_dialog_new(GTK_WINDOW(nw->window),
+                                   GTK_DIALOG_DESTROY_WITH_PARENT,
+                                   GTK_MESSAGE_ERROR,
+                                   GTK_BUTTONS_CLOSE, "%s", err);
+
+       g_signal_connect_swapped(mw, "response",
+                                G_CALLBACK(gtk_widget_destroy), mw);
+
+       gtk_widget_show(mw);
+}
+
+
 static void update_toolbar(NarrativeWindow *nw)
 {
        int cur_para;
@@ -79,24 +99,61 @@ static void update_toolbar(NarrativeWindow *nw)
 }
 
 
-static gint saveas_response_sig(GtkWidget *d, gint response,
-                                NarrativeWindow *nw)
+struct saveas_info
 {
-       if ( response == GTK_RESPONSE_ACCEPT ) {
+       NarrativeWindow *nw;
+       GtkWidget *filechooser;
+
+       /* Radio buttons for how to save stylesheet */
+       GtkWidget *privatess;
+       GtkWidget *folderss;
+       GtkWidget *noss;
+};
 
-               char *filename;
 
-               filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(d));
+static gint saveas_response_sig(GtkWidget *d, gint response,
+                                struct saveas_info *si)
+{
+       if ( response == 1 ) {  /* hard-coded number in Glade file */
+
+               GFile *file = gtk_file_chooser_get_file(GTK_FILE_CHOOSER(si->filechooser));
+               GFile *ssfile = NULL;
+
+               if ( gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(si->privatess)) ) {
+                       gchar *ssuri;
+                       ssuri = g_file_get_uri(file);
+                       if ( ssuri != NULL ) {
+                               size_t l = strlen(ssuri);
+                               if ( ssuri[l-3] == '.' && ssuri[l-2] == 's' && ssuri[l-1] =='c' ) {
+                                       ssuri[l-1] = 's';
+                                       ssfile = g_file_new_for_uri(ssuri);
+                                       g_free(ssuri);
+                               }
+                       }
+               } else if ( gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(si->folderss)) ) {
+                       GFile *parent;
+                       parent = g_file_get_parent(file);
+                       if ( parent != NULL ) {
+                               ssfile = g_file_get_child(parent, "stylesheet.ss");
+                               g_object_unref(parent);
+                       }
+               } else if ( gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(si->noss)) ) {
+                       /* Do nothing */
+               } else {
+                       fprintf(stderr, _("Couldn't determine how to save stylesheet!\n"));
+               }
 
-               if ( save_presentation(nw->p, filename) ) {
-                       //show_error(sw, "Failed to save presentation");
+               if ( save_presentation(si->nw->p, file, ssfile) ) {
+                       show_error(si->nw, _("Failed to save presentation"));
                }
 
-               g_free(filename);
+               g_object_unref(file);
+               g_object_unref(ssfile);
 
        }
 
        gtk_widget_destroy(d);
+       free(si);
 
        return 0;
 }
@@ -105,39 +162,49 @@ static gint saveas_response_sig(GtkWidget *d, gint response,
 static void saveas_sig(GSimpleAction *action, GVariant *parameter, gpointer vp)
 {
        GtkWidget *d;
+       GtkBuilder *builder;
        NarrativeWindow *nw = vp;
+       struct saveas_info *si;
 
-       d = gtk_file_chooser_dialog_new("Save Presentation",
-                                       GTK_WINDOW(nw->window),
-                                       GTK_FILE_CHOOSER_ACTION_SAVE,
-                                       "_Cancel", GTK_RESPONSE_CANCEL,
-                                       "_Open", GTK_RESPONSE_ACCEPT,
-                                       NULL);
-       gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(d),
-                                                      TRUE);
+       si = malloc(sizeof(struct saveas_info));
+       if ( si == NULL ) return;
 
-       g_signal_connect(G_OBJECT(d), "response",
-                        G_CALLBACK(saveas_response_sig), nw);
+       si->nw = nw;
+
+       builder = gtk_builder_new_from_resource("/uk/me/bitwiz/Colloquium/savepresentation.ui");
+       gtk_builder_add_callback_symbol(builder, "saveas_response_sig",
+                                       G_CALLBACK(saveas_response_sig));
+       gtk_builder_connect_signals(builder, si);
+       d = GTK_WIDGET(gtk_builder_get_object(builder, "savepresentation"));
+       si->filechooser = GTK_WIDGET(gtk_builder_get_object(builder, "filechooser"));
+       si->privatess = GTK_WIDGET(gtk_builder_get_object(builder, "privatess"));
+       si->folderss = GTK_WIDGET(gtk_builder_get_object(builder, "folderss"));
+       si->noss = GTK_WIDGET(gtk_builder_get_object(builder, "noss"));
+       gtk_window_set_transient_for(GTK_WINDOW(d), GTK_WINDOW(nw->window));
 
        gtk_widget_show_all(d);
 }
 
 
+static void about_sig(GSimpleAction *action, GVariant *parameter, gpointer vp)
+{
+       NarrativeWindow *nw = vp;
+       open_about_dialog(nw->window);
+}
+
+
 static void save_sig(GSimpleAction *action, GVariant *parameter, gpointer vp)
 {
        NarrativeWindow *nw = vp;
+       GFile *file;
 
-       if ( nw->p->filename == NULL ) {
+       if ( nw->p->uri == NULL ) {
                return saveas_sig(NULL, NULL, nw);
        }
 
-       save_presentation(nw->p, nw->p->filename);
-}
-
-
-static void open_slidesorter_sig(GSimpleAction *action, GVariant *parameter,
-                                 gpointer vp)
-{
+       file = g_file_new_for_uri(nw->p->uri);
+       save_presentation(nw->p, file, nw->p->stylesheet_from);
+       g_object_unref(file);
 }
 
 
@@ -150,157 +217,74 @@ static void delete_slide_sig(GSimpleAction *action, GVariant *parameter,
        /* Get the SCBlock corresponding to the slide */
        ns = sc_editor_get_cursor_bvp(nw->sceditor);
        if ( ns == NULL ) {
-               fprintf(stderr, "Not a slide!\n");
+               fprintf(stderr, _("Not a slide!\n"));
                return;
        }
 
-       sc_block_delete(&nw->p->scblocks, ns);
+       sc_block_delete(&nw->dummy_top, ns);
 
        /* Full rerender */
-       sc_editor_set_scblock(nw->sceditor,
-                             sc_editor_get_scblock(nw->sceditor));
+       sc_editor_set_scblock(nw->sceditor, nw->dummy_top);
        nw->p->saved = 0;
        update_titlebar(nw);
 }
 
 
-static struct template_id *get_templates(SCBlock *ss, int *n)
-{
-       struct template_id *list;
-       SCInterpreter *scin;
-
-       scin = sc_interp_new(NULL, NULL, NULL, NULL);
-       sc_interp_run_stylesheet(scin, ss);  /* ss == NULL is OK */
-       list = sc_interp_get_templates(scin, n);
-       sc_interp_destroy(scin);
-       return list;
-}
-
-
-static void update_template_menus(NarrativeWindow *nw)
+static gint load_ss_response_sig(GtkWidget *d, gint response,
+                                 NarrativeWindow *nw)
 {
-       struct template_id *templates;
-       int i, n_templates;
-
-       templates = get_templates(nw->p->stylesheet, &n_templates);
+       if ( response == GTK_RESPONSE_ACCEPT ) {
 
-       for ( i=0; i<n_templates; i++ ) {
-               printf("%2i: %s %s\n", i, templates[i].name,
-                      templates[i].friendlyname);
-               free(templates[i].name);
-               free(templates[i].friendlyname);
-               sc_block_free(templates[i].scblock);
-       }
+               GFile *file;
+               Stylesheet *new_ss;
 
-       free(templates);
-}
+               file = gtk_file_chooser_get_file(GTK_FILE_CHOOSER(d));
 
+               new_ss = stylesheet_load(file);
+               if ( new_ss != NULL ) {
 
-static SCBlock *get_slide_template(SCBlock *ss)
-{
-       struct template_id *templates;
-       int i, n_templates;
-       SCBlock *ret = NULL;
+                       stylesheet_free(nw->p->stylesheet);
+                       nw->p->stylesheet = new_ss;
+                       sc_editor_set_stylesheet(nw->sceditor, new_ss);
 
-       templates = get_templates(ss, &n_templates);
+                       /* Full rerender */
+                       sc_editor_set_scblock(nw->sceditor, nw->dummy_top);
 
-       for ( i=0; i<n_templates; i++ ) {
-               if ( strcmp(templates[i].name, "slide") == 0 ) {
-                       ret = templates[i].scblock;
                } else {
-                       sc_block_free(templates[i].scblock);
+                       fprintf(stderr, _("Failed to load\n"));
                }
-               free(templates[i].name);
-               free(templates[i].friendlyname);
-       }
-       free(templates);
 
-        /* No template? */
-        if ( ret == NULL ) {
-               ret = sc_parse("\\slide{}");
-       }
+               g_object_unref(file);
 
-       return ret;  /* NB this is a copy of the one owned by the interpreter */
-}
+       }
 
+       gtk_widget_destroy(d);
 
-static SCBlock *narrative_stylesheet()
-{
-       return sc_parse("\\stylesheet{"
-                       "\\ss[slide]{\\callback[sthumb]}"
-                       "}");
+       return 0;
 }
 
 
-static SCBlock **get_ss_list(struct presentation *p)
+static void stylesheet_changed_sig(GtkWidget *da, NarrativeWindow *nw)
 {
-       SCBlock **stylesheets;
-
-       stylesheets = malloc(3 * sizeof(SCBlock *));
-       if ( stylesheets == NULL ) return NULL;
-
-       if ( p->stylesheet != NULL ) {
-               stylesheets[0] = p->stylesheet;
-               stylesheets[1] = narrative_stylesheet();
-               stylesheets[2] = NULL;
-       } else {
-               stylesheets[0] = narrative_stylesheet();
-               stylesheets[1] = NULL;
-       }
+       /* It might have changed (been created) since last time */
+       sc_editor_set_stylesheet(nw->sceditor, nw->p->stylesheet);
 
-       return stylesheets;
+       /* Full rerender, first block may have changed */
+       sc_editor_set_scblock(nw->sceditor, nw->dummy_top);
 }
 
 
-static gint load_ss_response_sig(GtkWidget *d, gint response,
-                                 NarrativeWindow *nw)
+static void edit_ss_sig(GSimpleAction *action, GVariant *parameter,
+                        gpointer vp)
 {
-       if ( response == GTK_RESPONSE_ACCEPT ) {
-
-               char *filename;
-               char *stext;
-
-               filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(d));
-               printf("Loading %s\n",filename);
-
-               stext = load_everything(filename);
-               if ( stext != NULL ) {
-                       SCBlock *bl;
-                       SCBlock *ss;
-                       bl = sc_parse(stext);
-                       free(stext);
-                       ss = find_stylesheet(bl);
-                       if ( ss != NULL ) {
-
-                               SCBlock **stylesheets;
-
-                               /* Substitute the style sheet */
-                               replace_stylesheet(nw->p, ss);
-
-                               stylesheets = get_ss_list(nw->p);
-                               sc_editor_set_stylesheets(nw->sceditor,
-                                                         stylesheets);
-                               free(stylesheets);
-
-                               /* Full rerender, first block may have
-                                * changed */
-                               sc_editor_set_scblock(nw->sceditor,
-                                                     nw->p->scblocks);
-
-                       } else {
-                               fprintf(stderr, "Not a style sheet\n");
-                       }
-               } else {
-                       fprintf(stderr, "Failed to load\n");
-               }
-
-               g_free(filename);
-
-       }
-
-       gtk_widget_destroy(d);
+       NarrativeWindow *nw = vp;
+       StylesheetEditor *se;
 
-       return 0;
+       se = stylesheet_editor_new(nw->p);
+       gtk_window_set_transient_for(GTK_WINDOW(se), GTK_WINDOW(nw->window));
+       g_signal_connect(G_OBJECT(se), "changed",
+                        G_CALLBACK(stylesheet_changed_sig), nw);
+       gtk_widget_show_all(GTK_WIDGET(se));
 }
 
 
@@ -312,11 +296,11 @@ static void load_ss_sig(GSimpleAction *action, GVariant *parameter,
        NarrativeWindow *nw = vp;
        GtkWidget *d;
 
-       d = gtk_file_chooser_dialog_new("Load stylesheet",
+       d = gtk_file_chooser_dialog_new(_("Load stylesheet"),
                                        GTK_WINDOW(nw->window),
                                        GTK_FILE_CHOOSER_ACTION_OPEN,
-                                       "_Cancel", GTK_RESPONSE_CANCEL,
-                                       "_Open", GTK_RESPONSE_ACCEPT,
+                                       _("_Cancel"), GTK_RESPONSE_CANCEL,
+                                       _("_Open"), GTK_RESPONSE_ACCEPT,
                                        NULL);
 
        g_signal_connect(G_OBJECT(d), "response",
@@ -333,22 +317,22 @@ static void add_slide_sig(GSimpleAction *action, GVariant *parameter,
        SCBlock *templ;
        NarrativeWindow *nw = vp;
 
+       sc_editor_ensure_cursor(nw->sceditor);
+
        /* Split the current paragraph */
        nsblock = split_paragraph_at_cursor(nw->sceditor);
 
-       /* Get the template */
-       templ = get_slide_template(nw->p->stylesheet); /* our copy */
-       show_sc_blocks(templ);
+       /* FIXME: Template from JSON */
+       templ = sc_parse("\\slide{}");
 
        /* Link the new SCBlock in */
        if ( nsblock != NULL ) {
                sc_block_append_p(nsblock, templ);
        } else {
-               fprintf(stderr, "Failed to split paragraph\n");
+               fprintf(stderr, _("Failed to split paragraph\n"));
        }
 
-       sc_editor_set_scblock(nw->sceditor,
-                             sc_editor_get_scblock(nw->sceditor));
+       sc_editor_set_scblock(nw->sceditor, nw->dummy_top);
        nw->p->saved = 0;
        update_titlebar(nw);
 }
@@ -394,7 +378,7 @@ static void ss_next_para(SCSlideshow *ss, void *vp)
                                  sc_editor_get_cursor_para(nw->sceditor)+1);
 
        /* If we only have one monitor, don't try to do paragraph counting */
-       if ( ss->single_monitor ) {
+       if ( ss->single_monitor && !nw->show_no_slides ) {
                int i, max;
                max = sc_editor_get_num_paras(nw->sceditor);
                for ( i=sc_editor_get_cursor_para(nw->sceditor); i<max; i++ ) {
@@ -465,31 +449,38 @@ static gint export_pdf_response_sig(GtkWidget *d, gint response,
 }
 
 
+static void print_sig(GSimpleAction *action, GVariant *parameter, gpointer vp)
+{
+       NarrativeWindow *nw = vp;
+       run_printing(nw->p, nw->window);
+}
+
+
 static void exportpdf_sig(GSimpleAction *action, GVariant *parameter,
                           gpointer vp)
 {
-       struct presentation *p = vp;
+       NarrativeWindow *nw = vp;
        GtkWidget *d;
 
-       d = gtk_file_chooser_dialog_new("Export PDF",
+       d = gtk_file_chooser_dialog_new(_("Export PDF"),
                                        NULL,
                                        GTK_FILE_CHOOSER_ACTION_SAVE,
-                                       "_Cancel", GTK_RESPONSE_CANCEL,
-                                       "_Export", GTK_RESPONSE_ACCEPT,
+                                       _("_Cancel"), GTK_RESPONSE_CANCEL,
+                                       _("_Export"), GTK_RESPONSE_ACCEPT,
                                        NULL);
        gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(d),
                                                       TRUE);
 
        g_signal_connect(G_OBJECT(d), "response",
-                        G_CALLBACK(export_pdf_response_sig), p);
+                        G_CALLBACK(export_pdf_response_sig), nw->p);
 
        gtk_widget_show_all(d);
 }
 
 
 
-static gboolean button_press_sig(GtkWidget *da, GdkEventButton *event,
-                                 NarrativeWindow *nw)
+static gboolean nw_button_press_sig(GtkWidget *da, GdkEventButton *event,
+                                    NarrativeWindow *nw)
 {
        return 0;
 }
@@ -514,15 +505,15 @@ static void scroll_down(NarrativeWindow *nw)
 }
 
 
-static gboolean destroy_sig(GtkWidget *da, NarrativeWindow *nw)
+static gboolean nw_destroy_sig(GtkWidget *da, NarrativeWindow *nw)
 {
        g_application_release(nw->app);
        return FALSE;
 }
 
 
-static gboolean key_press_sig(GtkWidget *da, GdkEventKey *event,
-                              NarrativeWindow *nw)
+static gboolean nw_key_press_sig(GtkWidget *da, GdkEventKey *event,
+                                 NarrativeWindow *nw)
 {
        switch ( event->keyval ) {
 
@@ -592,11 +583,13 @@ static void start_slideshow_here_sig(GSimpleAction *action, GVariant *parameter,
        bvp = sc_editor_get_cursor_bvp(nw->sceditor);
        if ( bvp == NULL ) return;
 
-       nw->show = sc_slideshow_new(nw->p);
+       nw->show = sc_slideshow_new(nw->p, GTK_APPLICATION(nw->app));
        if ( nw->show == NULL ) return;
 
+       nw->show_no_slides = 0;
+
        g_signal_connect(G_OBJECT(nw->show), "key-press-event",
-                G_CALLBACK(key_press_sig), nw);
+                G_CALLBACK(nw_key_press_sig), nw);
        g_signal_connect(G_OBJECT(nw->show), "destroy",
                 G_CALLBACK(ss_destroy_sig), nw);
        sc_slideshow_set_slide(nw->show, bvp);
@@ -613,11 +606,13 @@ static void start_slideshow_noslides_sig(GSimpleAction *action, GVariant *parame
 
        if ( num_slides(nw->p) == 0 ) return;
 
-       nw->show = sc_slideshow_new(nw->p);
+       nw->show = sc_slideshow_new(nw->p, GTK_APPLICATION(nw->app));
        if ( nw->show == NULL ) return;
 
+       nw->show_no_slides = 1;
+
        g_signal_connect(G_OBJECT(nw->show), "key-press-event",
-                G_CALLBACK(key_press_sig), nw);
+                G_CALLBACK(nw_key_press_sig), nw);
        g_signal_connect(G_OBJECT(nw->show), "destroy",
                 G_CALLBACK(ss_destroy_sig), nw);
        sc_slideshow_set_slide(nw->show, first_slide(nw->p));
@@ -634,11 +629,13 @@ static void start_slideshow_sig(GSimpleAction *action, GVariant *parameter,
 
        if ( num_slides(nw->p) == 0 ) return;
 
-       nw->show = sc_slideshow_new(nw->p);
+       nw->show = sc_slideshow_new(nw->p, GTK_APPLICATION(nw->app));
        if ( nw->show == NULL ) return;
 
+       nw->show_no_slides = 0;
+
        g_signal_connect(G_OBJECT(nw->show), "key-press-event",
-                G_CALLBACK(key_press_sig), nw);
+                G_CALLBACK(nw_key_press_sig), nw);
        g_signal_connect(G_OBJECT(nw->show), "destroy",
                 G_CALLBACK(ss_destroy_sig), nw);
        sc_slideshow_set_slide(nw->show, first_slide(nw->p));
@@ -651,14 +648,14 @@ static void start_slideshow_sig(GSimpleAction *action, GVariant *parameter,
 
 static void nw_update_titlebar(NarrativeWindow *nw)
 {
-       get_titlebar_string(nw->p);
+       char *tb = get_titlebar_string(nw->p);
 
        if ( nw->p->slidewindow != NULL ) {
 
                char *title;
 
-               title = malloc(strlen(nw->p->titlebar)+14);
-               sprintf(title, "%s - Colloquium", nw->p->titlebar);
+               title = malloc(strlen(tb)+14);
+               sprintf(title, "%s - Colloquium", tb);
                gtk_window_set_title(GTK_WINDOW(nw->window), title);
                free(title);
 
@@ -671,13 +668,10 @@ static int create_thumbnail(SCInterpreter *scin, SCBlock *bl,
                             double *w, double *h, void **bvp, void *vp)
 {
        struct presentation *p = vp;
-       SCBlock *b;
 
        *w = 270.0*(p->slide_width / p->slide_height);
        *h = 270.0;
-       b = sc_interp_get_macro_real_block(scin);
-
-       *bvp = b;
+       *bvp = bl;
 
        return 1;
 }
@@ -688,16 +682,12 @@ static cairo_surface_t *render_thumbnail(int w, int h, void *bvp, void *vp)
        struct presentation *p = vp;
        SCBlock *scblocks = bvp;
        cairo_surface_t *surf;
-       SCBlock *stylesheets[2];
        struct frame *top;
-
-       scblocks = sc_block_child(scblocks);
-       stylesheets[0] = p->stylesheet;
-       stylesheets[1] = NULL;
+       int sn = slide_number(p, scblocks);
 
        /* FIXME: Cache like crazy here */
        surf = render_sc(scblocks, w, h, p->slide_width, p->slide_height,
-                        stylesheets, NULL, p->is, 0, &top, p->lang);
+                        p->stylesheet, NULL, p->is, sn, &top, p->lang);
        frame_free(top);
 
        return surf;
@@ -721,12 +711,13 @@ static int click_thumbnail(double x, double y, void *bvp, void *vp)
 
 GActionEntry nw_entries[] = {
 
+       { "about", about_sig, NULL, NULL, NULL },
        { "save", save_sig, NULL, NULL, NULL },
        { "saveas", saveas_sig, NULL, NULL, NULL },
-       { "sorter", open_slidesorter_sig, NULL, NULL, NULL },
        { "deleteslide", delete_slide_sig, NULL, NULL, NULL },
        { "slide", add_slide_sig, NULL, NULL, NULL },
        { "loadstylesheet", load_ss_sig, NULL, NULL, NULL },
+       { "stylesheet", edit_ss_sig, NULL, NULL, NULL },
        { "startslideshow", start_slideshow_sig, NULL, NULL, NULL },
        { "startslideshowhere", start_slideshow_here_sig, NULL, NULL, NULL },
        { "startslideshownoslides", start_slideshow_noslides_sig, NULL, NULL, NULL },
@@ -736,10 +727,6 @@ GActionEntry nw_entries[] = {
        { "prev", prev_para_sig, NULL, NULL, NULL },
        { "next", next_para_sig, NULL, NULL, NULL },
        { "last", last_para_sig, NULL, NULL, NULL },
-};
-
-
-GActionEntry nw_entries_p[] = {
        { "print", print_sig, NULL, NULL, NULL  },
        { "exportpdf", exportpdf_sig, NULL, NULL, NULL  },
 };
@@ -769,13 +756,12 @@ NarrativeWindow *narrative_window_new(struct presentation *p, GApplication *papp
        GtkWidget *scroll;
        GtkWidget *toolbar;
        GtkToolItem *button;
-       SCBlock **stylesheets;
        SCCallbackList *cbl;
        GtkWidget *image;
        Colloquium *app = COLLOQUIUM(papp);
 
        if ( p->narrative_window != NULL ) {
-               fprintf(stderr, "Narrative window is already open!\n");
+               fprintf(stderr, _("Narrative window is already open!\n"));
                return NULL;
        }
 
@@ -791,27 +777,29 @@ NarrativeWindow *narrative_window_new(struct presentation *p, GApplication *papp
 
        g_action_map_add_action_entries(G_ACTION_MAP(nw->window), nw_entries,
                                        G_N_ELEMENTS(nw_entries), nw);
-       g_action_map_add_action_entries(G_ACTION_MAP(nw->window), nw_entries_p,
-                                       G_N_ELEMENTS(nw_entries_p), p);
 
        nw_update_titlebar(nw);
 
        vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
        gtk_container_add(GTK_CONTAINER(nw->window), vbox);
 
-       stylesheets = get_ss_list(p);
-
+       /* If the presentation is completely empty, give ourselves at least
+        * something to work with */
        if ( nw->p->scblocks == NULL ) {
                nw->p->scblocks = sc_parse("");
        }
 
-       nw->sceditor = sc_editor_new(nw->p->scblocks, stylesheets, p->lang,
+       /* Put everything we have inside \presentation{}.
+        * SCEditor will start processing one level down */
+       nw->dummy_top = sc_block_new_parent(nw->p->scblocks, "presentation");
+
+       nw->sceditor = sc_editor_new(nw->dummy_top, p->stylesheet, p->lang,
                                     colloquium_get_imagestore(app));
-       free(stylesheets);
        cbl = sc_callback_list_new();
-       sc_callback_list_add_callback(cbl, "sthumb", create_thumbnail,
+       sc_callback_list_add_callback(cbl, "slide", create_thumbnail,
                                      render_thumbnail, click_thumbnail, p);
        sc_editor_set_callbacks(nw->sceditor, cbl);
+       sc_editor_set_imagestore(nw->sceditor, p->is);
 
        toolbar = gtk_toolbar_new();
        gtk_toolbar_set_style(GTK_TOOLBAR(toolbar), GTK_TOOLBAR_ICONS);
@@ -820,7 +808,7 @@ NarrativeWindow *narrative_window_new(struct presentation *p, GApplication *papp
        /* Fullscreen */
        image = gtk_image_new_from_icon_name("view-fullscreen",
                                             GTK_ICON_SIZE_LARGE_TOOLBAR);
-       button = gtk_tool_button_new(image, "Start slideshow");
+       button = gtk_tool_button_new(image, _("Start slideshow"));
        gtk_actionable_set_action_name(GTK_ACTIONABLE(button),
                                       "win.startslideshow");
        gtk_container_add(GTK_CONTAINER(toolbar), GTK_WIDGET(button));
@@ -831,7 +819,7 @@ NarrativeWindow *narrative_window_new(struct presentation *p, GApplication *papp
        /* Add slide */
        image = gtk_image_new_from_icon_name("list-add",
                                             GTK_ICON_SIZE_LARGE_TOOLBAR);
-       button = gtk_tool_button_new(image, "Add slide");
+       button = gtk_tool_button_new(image, _("Add slide"));
        gtk_actionable_set_action_name(GTK_ACTIONABLE(button),
                                       "win.slide");
        gtk_container_add(GTK_CONTAINER(toolbar), GTK_WIDGET(button));
@@ -841,34 +829,33 @@ NarrativeWindow *narrative_window_new(struct presentation *p, GApplication *papp
 
        image = gtk_image_new_from_icon_name("go-top",
                                             GTK_ICON_SIZE_LARGE_TOOLBAR);
-       nw->bfirst = gtk_tool_button_new(image, "First slide");
+       nw->bfirst = gtk_tool_button_new(image, _("First slide"));
        gtk_container_add(GTK_CONTAINER(toolbar), GTK_WIDGET(nw->bfirst));
        gtk_actionable_set_action_name(GTK_ACTIONABLE(nw->bfirst),
                                       "win.first");
 
        image = gtk_image_new_from_icon_name("go-up",
                                             GTK_ICON_SIZE_LARGE_TOOLBAR);
-       nw->bprev = gtk_tool_button_new(image, "Previous slide");
+       nw->bprev = gtk_tool_button_new(image, _("Previous slide"));
        gtk_container_add(GTK_CONTAINER(toolbar), GTK_WIDGET(nw->bprev));
        gtk_actionable_set_action_name(GTK_ACTIONABLE(nw->bprev),
                                       "win.prev");
 
        image = gtk_image_new_from_icon_name("go-down",
                                             GTK_ICON_SIZE_LARGE_TOOLBAR);
-       nw->bnext = gtk_tool_button_new(image, "Next slide");
+       nw->bnext = gtk_tool_button_new(image, _("Next slide"));
        gtk_container_add(GTK_CONTAINER(toolbar), GTK_WIDGET(nw->bnext));
        gtk_actionable_set_action_name(GTK_ACTIONABLE(nw->bnext),
                                       "win.next");
 
        image = gtk_image_new_from_icon_name("go-bottom",
                                             GTK_ICON_SIZE_LARGE_TOOLBAR);
-       nw->blast = gtk_tool_button_new(image, "Last slide");
+       nw->blast = gtk_tool_button_new(image, _("Last slide"));
        gtk_container_add(GTK_CONTAINER(toolbar), GTK_WIDGET(nw->blast));
        gtk_actionable_set_action_name(GTK_ACTIONABLE(nw->blast),
                                       "win.last");
 
        update_toolbar(nw);
-       update_template_menus(nw);
 
        scroll = gtk_scrolled_window_new(NULL, NULL);
        gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll),
@@ -881,13 +868,13 @@ NarrativeWindow *narrative_window_new(struct presentation *p, GApplication *papp
        sc_editor_set_top_frame_editable(nw->sceditor, 1);
 
        g_signal_connect(G_OBJECT(nw->sceditor), "button-press-event",
-                        G_CALLBACK(button_press_sig), nw);
+                        G_CALLBACK(nw_button_press_sig), nw);
        g_signal_connect(G_OBJECT(nw->sceditor), "changed",
                         G_CALLBACK(changed_sig), nw);
        g_signal_connect(G_OBJECT(nw->sceditor), "key-press-event",
-                        G_CALLBACK(key_press_sig), nw);
+                        G_CALLBACK(nw_key_press_sig), nw);
        g_signal_connect(G_OBJECT(nw->window), "destroy",
-                        G_CALLBACK(destroy_sig), nw);
+                        G_CALLBACK(nw_destroy_sig), nw);
 
        gtk_window_set_default_size(GTK_WINDOW(nw->window), 768, 768);
        gtk_box_pack_start(GTK_BOX(vbox), scroll, TRUE, TRUE, 0);