aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThomas White <taw@bitwiz.me.uk>2019-02-26 21:21:36 +0100
committerThomas White <taw@bitwiz.me.uk>2019-02-26 21:21:36 +0100
commit4904182915c1a02c8ac6fb26397cb12e0aab51b9 (patch)
treea84cf5506c2ddd7426d23008dd47379c2f8da5bf
parent237f336f9bb0d2d34784612d59cf1622a9edd952 (diff)
Skeleton of main program
-rw-r--r--libstorycode/presentation.c32
-rw-r--r--libstorycode/presentation.h4
-rw-r--r--meson.build25
-rw-r--r--src-old/narrative_window.c927
-rw-r--r--src/colloquium.c493
-rw-r--r--src/colloquium.h45
-rw-r--r--src/narrative_window.c787
-rw-r--r--src/narrative_window.h (renamed from src-old/narrative_window.h)11
8 files changed, 1370 insertions, 954 deletions
diff --git a/libstorycode/presentation.c b/libstorycode/presentation.c
index 6a4d019..6eb7a50 100644
--- a/libstorycode/presentation.c
+++ b/libstorycode/presentation.c
@@ -29,11 +29,14 @@
#include <string.h>
#include <assert.h>
#include <stdio.h>
+#include <gio/gio.h>
#include "presentation.h"
#include "stylesheet.h"
#include "slide.h"
#include "narrative.h"
+#include "imagestore.h"
+#include "storycode.h"
struct _presentation
{
@@ -59,6 +62,35 @@ Presentation *presentation_new()
}
+Presentation *presentation_load(GFile *file)
+{
+ GBytes *bytes;
+ const char *text;
+ size_t len;
+ Presentation *p;
+ ImageStore *is;
+
+ bytes = g_file_load_bytes(file, NULL, NULL, NULL);
+ if ( bytes == NULL ) return NULL;
+
+ text = g_bytes_get_data(bytes, &len);
+ p = storycode_parse_presentation(text);
+ g_bytes_unref(bytes);
+ if ( p == NULL ) return NULL;
+
+ is = imagestore_new(".");
+ imagestore_set_parent(is, g_file_get_parent(file));
+ return p;
+}
+
+
+int presentation_save(Presentation *p, GFile *file)
+{
+ /* FIXME: Implementation */
+ return 1;
+}
+
+
void presentation_free(Presentation *p)
{
free(p);
diff --git a/libstorycode/presentation.h b/libstorycode/presentation.h
index 80c6186..250d7a6 100644
--- a/libstorycode/presentation.h
+++ b/libstorycode/presentation.h
@@ -27,12 +27,16 @@
#include <config.h>
#endif
+#include <gio/gio.h>
+
typedef struct _presentation Presentation;
#include "stylesheet.h"
#include "narrative.h"
extern Presentation *presentation_new(void);
+extern Presentation *presentation_load(GFile *file);
+extern int presentation_save(Presentation *p, GFile *file);
extern void presentation_free(Presentation *p);
extern void presentation_add_stylesheet(Presentation *p, Stylesheet *ss);
diff --git a/meson.build b/meson.build
index 6a57efc..a7d517e 100644
--- a/meson.build
+++ b/meson.build
@@ -108,29 +108,18 @@ executable('pdfstorycode',
# Main program
-#executable('colloquium',
-# ['src/colloquium.c',
-# 'src/narrative_window.c',
-# 'src/render.c',
+executable('colloquium',
+ ['src/colloquium.c',
+ 'src/narrative_window.c',
# 'src/slideshow.c',
-# 'src/debugger.c',
# 'src/pr_clock.c',
-# 'src/sc_editor.c',
# 'src/slide_window.c',
-# 'src/frame.c',
-# 'src/presentation.c',
-# 'src/sc_interp.c',
# 'src/testcard.c',
-# 'src/imagestore.c',
-# 'src/print.c',
-# 'src/sc_parse.c',
-# 'src/utils.c',
-# 'src/stylesheet.c',
# 'src/stylesheet_editor.c',
-# ],
-# gresources,
-# dependencies : [gtkdep, mdep, jsondep],
-# install : true)
+ ],
+ gresources,
+ dependencies : [gtk_dep, mdep, libstorycode_dep],
+ install : true)
# Desktop file
diff --git a/src-old/narrative_window.c b/src-old/narrative_window.c
deleted file mode 100644
index e0c59ef..0000000
--- a/src-old/narrative_window.c
+++ /dev/null
@@ -1,927 +0,0 @@
-/*
- * narrative_window.c
- *
- * Copyright © 2014-2018 Thomas White <taw@bitwiz.org.uk>
- *
- * This file is part of Colloquium.
- *
- * Colloquium is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- */
-
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <gtk/gtk.h>
-#include <assert.h>
-#include <string.h>
-#include <stdlib.h>
-
-#include "colloquium.h"
-#include "presentation.h"
-#include "narrative_window.h"
-#include "sc_editor.h"
-#include "sc_parse.h"
-#include "render.h"
-#include "testcard.h"
-#include "pr_clock.h"
-#include "print.h"
-#include "utils.h"
-#include "stylesheet_editor.h"
-
-
-struct _narrative_window
-{
- GtkWidget *window;
- GtkToolItem *bfirst;
- GtkToolItem *bprev;
- GtkToolItem *bnext;
- GtkToolItem *blast;
- SCEditor *sceditor;
- GApplication *app;
- struct presentation *p;
- SCBlock *dummy_top;
- SCSlideshow *show;
- int show_no_slides;
- PRClock *pr_clock;
- SlideWindow *slidewindows[16];
- int n_slidewindows;
-};
-
-
-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;
-
- cur_para = sc_editor_get_cursor_para(nw->sceditor);
- if ( cur_para == 0 ) {
- gtk_widget_set_sensitive(GTK_WIDGET(nw->bfirst), FALSE);
- gtk_widget_set_sensitive(GTK_WIDGET(nw->bprev), FALSE);
- } else {
- gtk_widget_set_sensitive(GTK_WIDGET(nw->bfirst), TRUE);
- gtk_widget_set_sensitive(GTK_WIDGET(nw->bprev), TRUE);
- }
-
- if ( cur_para == sc_editor_get_num_paras(nw->sceditor)-1 ) {
- gtk_widget_set_sensitive(GTK_WIDGET(nw->bnext), FALSE);
- gtk_widget_set_sensitive(GTK_WIDGET(nw->blast), FALSE);
- } else {
- gtk_widget_set_sensitive(GTK_WIDGET(nw->bnext), TRUE);
- gtk_widget_set_sensitive(GTK_WIDGET(nw->blast), TRUE);
- }
-}
-
-
-struct saveas_info
-{
- NarrativeWindow *nw;
-
- /* Radio buttons for how to save stylesheet */
- GtkWidget *privatess;
- GtkWidget *folderss;
- GtkWidget *noss;
-};
-
-
-static gint saveas_response_sig(GtkWidget *d, gint response,
- struct saveas_info *si)
-{
- if ( response == GTK_RESPONSE_ACCEPT ) {
-
- GFile *file = gtk_file_chooser_get_file(GTK_FILE_CHOOSER(d));
- 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 where to save stylesheet!\n");
- }
-
- if ( save_presentation(si->nw->p, file, ssfile) ) {
- show_error(si->nw, _("Failed to save presentation"));
- }
-
- /* save_presentation keeps a reference to both of these */
- g_object_unref(file);
- if ( ssfile != NULL ) g_object_unref(ssfile);
-
- }
-
- gtk_widget_destroy(d);
- free(si);
-
- return 0;
-}
-
-
-static void saveas_sig(GSimpleAction *action, GVariant *parameter, gpointer vp)
-{
- GtkWidget *d;
- GtkWidget *box;
- NarrativeWindow *nw = vp;
- struct saveas_info *si;
-
- si = malloc(sizeof(struct saveas_info));
- if ( si == NULL ) return;
-
- si->nw = nw;
-
- d = gtk_file_chooser_dialog_new(_("Save presentation"),
- GTK_WINDOW(nw->window),
- GTK_FILE_CHOOSER_ACTION_SAVE,
- _("_Cancel"), GTK_RESPONSE_CANCEL,
- _("_Save"), GTK_RESPONSE_ACCEPT,
- NULL);
- gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(d),
- TRUE);
-
- box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 8);
- gtk_file_chooser_set_extra_widget(GTK_FILE_CHOOSER(d), box);
- si->privatess = gtk_radio_button_new_with_label(NULL,
- _("Create/update private stylesheet for this presentation"));
- gtk_box_pack_start(GTK_BOX(box), si->privatess, FALSE, FALSE, 0);
- si->folderss = gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON(si->privatess),
- _("Create/update default stylesheet for presentations in folder"));
- gtk_box_pack_start(GTK_BOX(box), si->folderss, FALSE, FALSE, 0);
- si->noss = gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON(si->privatess),
- _("Don't save stylesheet at all"));
- gtk_box_pack_start(GTK_BOX(box), si->noss, FALSE, FALSE, 0);
- gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(si->privatess), TRUE);
-
- g_signal_connect(G_OBJECT(d), "response",
- G_CALLBACK(saveas_response_sig), si);
-
- 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;
-
- if ( nw->p->file == NULL ) {
- return saveas_sig(NULL, NULL, nw);
- }
-
- save_presentation(nw->p, nw->p->file, nw->p->stylesheet_from);
-}
-
-
-static void delete_slide_sig(GSimpleAction *action, GVariant *parameter,
- gpointer vp)
-{
- SCBlock *ns;
- NarrativeWindow *nw = vp;
-
- /* Get the SCBlock corresponding to the slide */
- ns = sc_editor_get_cursor_bvp(nw->sceditor);
- if ( ns == NULL ) {
- fprintf(stderr, "Not a slide!\n");
- return;
- }
-
- sc_block_delete(&nw->dummy_top, ns);
-
- /* Full rerender */
- sc_editor_set_scblock(nw->sceditor, nw->dummy_top);
- nw->p->saved = 0;
- update_titlebar(nw);
-}
-
-
-static gint load_ss_response_sig(GtkWidget *d, gint response,
- NarrativeWindow *nw)
-{
- if ( response == GTK_RESPONSE_ACCEPT ) {
-
- GFile *file;
- Stylesheet *new_ss;
-
- file = gtk_file_chooser_get_file(GTK_FILE_CHOOSER(d));
-
- new_ss = stylesheet_load(file);
- if ( new_ss != NULL ) {
-
- stylesheet_free(nw->p->stylesheet);
- nw->p->stylesheet = new_ss;
- sc_editor_set_stylesheet(nw->sceditor, new_ss);
-
- /* Full rerender */
- sc_editor_set_scblock(nw->sceditor, nw->dummy_top);
-
- } else {
- fprintf(stderr, _("Failed to load stylesheet\n"));
- }
-
- g_object_unref(file);
-
- }
-
- gtk_widget_destroy(d);
-
- return 0;
-}
-
-
-static void stylesheet_changed_sig(GtkWidget *da, NarrativeWindow *nw)
-{
- int i;
-
- /* It might have changed (been created) since last time */
- sc_editor_set_stylesheet(nw->sceditor, nw->p->stylesheet);
-
- /* Full rerender, first block may have changed */
- sc_editor_set_scblock(nw->sceditor, nw->dummy_top);
-
- /* Full rerender of all slide windows */
- for ( i=0; i<nw->n_slidewindows; i++ ) {
- slide_window_update(nw->slidewindows[i]);
- }
-}
-
-
-static void edit_ss_sig(GSimpleAction *action, GVariant *parameter,
- gpointer vp)
-{
- NarrativeWindow *nw = vp;
- StylesheetEditor *se;
-
- 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));
-}
-
-
-static void load_ss_sig(GSimpleAction *action, GVariant *parameter,
- gpointer vp)
-{
- //SCBlock *nsblock;
- //SCBlock *templ;
- NarrativeWindow *nw = vp;
- GtkWidget *d;
-
- 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,
- NULL);
-
- g_signal_connect(G_OBJECT(d), "response",
- G_CALLBACK(load_ss_response_sig), nw);
-
- gtk_widget_show_all(d);
-}
-
-
-static void add_slide_sig(GSimpleAction *action, GVariant *parameter,
- gpointer vp)
-{
- SCBlock *nsblock;
- SCBlock *templ;
- NarrativeWindow *nw = vp;
-
- sc_editor_ensure_cursor(nw->sceditor);
-
- /* Split the current paragraph */
- nsblock = split_paragraph_at_cursor(nw->sceditor);
-
- /* 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");
- }
-
- sc_editor_set_scblock(nw->sceditor, nw->dummy_top);
- nw->p->saved = 0;
- update_titlebar(nw);
-}
-
-
-static void first_para_sig(GSimpleAction *action, GVariant *parameter,
- gpointer vp)
-{
- NarrativeWindow *nw = vp;
- sc_editor_set_cursor_para(nw->sceditor, 0);
- pr_clock_set_pos(nw->pr_clock, sc_editor_get_cursor_para(nw->sceditor),
- sc_editor_get_num_paras(nw->sceditor));
- update_toolbar(nw);
-}
-
-
-static void ss_prev_para(SCSlideshow *ss, void *vp)
-{
- NarrativeWindow *nw = vp;
- if ( sc_editor_get_cursor_para(nw->sceditor) == 0 ) return;
- sc_editor_set_cursor_para(nw->sceditor,
- sc_editor_get_cursor_para(nw->sceditor)-1);
- pr_clock_set_pos(nw->pr_clock, sc_editor_get_cursor_para(nw->sceditor),
- sc_editor_get_num_paras(nw->sceditor));
- update_toolbar(nw);
-}
-
-
-static void prev_para_sig(GSimpleAction *action, GVariant *parameter,
- gpointer vp)
-{
- NarrativeWindow *nw = vp;
- ss_prev_para(nw->show, nw);
-}
-
-
-static void ss_next_para(SCSlideshow *ss, void *vp)
-{
- NarrativeWindow *nw = vp;
- SCBlock *ns;
-
- sc_editor_set_cursor_para(nw->sceditor,
- 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 && !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++ ) {
- SCBlock *ns;
- sc_editor_set_cursor_para(nw->sceditor, i);
- ns = sc_editor_get_cursor_bvp(nw->sceditor);
- if ( ns != NULL ) break;
- }
- }
-
- pr_clock_set_pos(nw->pr_clock, sc_editor_get_cursor_para(nw->sceditor),
- sc_editor_get_num_paras(nw->sceditor));
- ns = sc_editor_get_cursor_bvp(nw->sceditor);
- if ( ns != NULL ) {
- sc_slideshow_set_slide(nw->show, ns);
- }
- update_toolbar(nw);
-}
-
-
-static void next_para_sig(GSimpleAction *action, GVariant *parameter,
- gpointer vp)
-{
- NarrativeWindow *nw = vp;
- ss_next_para(nw->show, nw);
-}
-
-
-static void last_para_sig(GSimpleAction *action, GVariant *parameter,
- gpointer vp)
-{
- NarrativeWindow *nw = vp;
- sc_editor_set_cursor_para(nw->sceditor, -1);
- pr_clock_set_pos(nw->pr_clock, sc_editor_get_cursor_para(nw->sceditor),
- sc_editor_get_num_paras(nw->sceditor));
- update_toolbar(nw);
-}
-
-
-static void open_clock_sig(GSimpleAction *action, GVariant *parameter, gpointer vp)
-{
- NarrativeWindow *nw = vp;
- nw->pr_clock = pr_clock_new();
-}
-
-
-static void testcard_sig(GSimpleAction *action, GVariant *parameter,
- gpointer vp)
-{
- NarrativeWindow *nw = vp;
- show_testcard(nw->p);
-}
-
-
-static gint export_pdf_response_sig(GtkWidget *d, gint response,
- struct presentation *p)
-{
- if ( response == GTK_RESPONSE_ACCEPT ) {
- char *filename;
- filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(d));
- export_pdf(p, filename);
- g_free(filename);
- }
-
- gtk_widget_destroy(d);
-
- return 0;
-}
-
-
-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)
-{
- NarrativeWindow *nw = vp;
- GtkWidget *d;
-
- d = gtk_file_chooser_dialog_new(_("Export PDF"),
- NULL,
- GTK_FILE_CHOOSER_ACTION_SAVE,
- _("_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), nw->p);
-
- gtk_widget_show_all(d);
-}
-
-
-
-static gboolean nw_button_press_sig(GtkWidget *da, GdkEventButton *event,
- NarrativeWindow *nw)
-{
- return 0;
-}
-
-
-static void changed_sig(GtkWidget *da, NarrativeWindow *nw)
-{
- nw->p->saved = 0;
- update_titlebar(nw);
-}
-
-
-static void scroll_down(NarrativeWindow *nw)
-{
- gdouble inc, val;
- GtkAdjustment *vadj;
-
- vadj = gtk_scrollable_get_vadjustment(GTK_SCROLLABLE(nw->sceditor));
- inc = gtk_adjustment_get_step_increment(GTK_ADJUSTMENT(vadj));
- val = gtk_adjustment_get_value(GTK_ADJUSTMENT(vadj));
- gtk_adjustment_set_value(GTK_ADJUSTMENT(vadj), inc+val);
-}
-
-
-static gboolean nw_destroy_sig(GtkWidget *da, NarrativeWindow *nw)
-{
- g_application_release(nw->app);
- return FALSE;
-}
-
-
-static gboolean nw_key_press_sig(GtkWidget *da, GdkEventKey *event,
- NarrativeWindow *nw)
-{
- switch ( event->keyval ) {
-
- case GDK_KEY_B :
- case GDK_KEY_b :
- if ( nw->show != NULL ) {
- scroll_down(nw);
- return TRUE;
- }
- break;
-
- case GDK_KEY_Page_Up :
- if ( nw->show != NULL ) {
- ss_prev_para(nw->show, nw);
- return TRUE;
- }
- break;
-
- case GDK_KEY_Page_Down :
- if ( nw->show != NULL) {
- ss_next_para(nw->show, nw);
- return TRUE;
- }
- break;
-
- case GDK_KEY_Escape :
- if ( nw->show != NULL ) {
- gtk_widget_destroy(GTK_WIDGET(nw->show));
- return TRUE;
- }
- break;
-
- case GDK_KEY_F5 :
- if ( nw->show != NULL ) {
- /* Trap F5 so that full rerender does NOT happen */
- return TRUE;
- }
-
- }
-
- return FALSE;
-}
-
-
-static gboolean ss_destroy_sig(GtkWidget *da, NarrativeWindow *nw)
-{
- nw->show = NULL;
- sc_editor_set_para_highlight(nw->sceditor, 0);
-
- gtk_widget_set_sensitive(GTK_WIDGET(nw->bfirst), FALSE);
- gtk_widget_set_sensitive(GTK_WIDGET(nw->bprev), FALSE);
- gtk_widget_set_sensitive(GTK_WIDGET(nw->bnext), FALSE);
- gtk_widget_set_sensitive(GTK_WIDGET(nw->blast), FALSE);
-
- return FALSE;
-}
-
-
-static void start_slideshow_here_sig(GSimpleAction *action, GVariant *parameter,
- gpointer vp)
-{
- NarrativeWindow *nw = vp;
- void *bvp;
-
- if ( num_slides(nw->p) == 0 ) return;
-
- bvp = sc_editor_get_cursor_bvp(nw->sceditor);
- if ( bvp == NULL ) return;
-
- 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(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);
- sc_editor_set_para_highlight(nw->sceditor, 1);
- gtk_widget_show_all(GTK_WIDGET(nw->show));
- update_toolbar(nw);
-}
-
-
-static void start_slideshow_noslides_sig(GSimpleAction *action, GVariant *parameter,
- gpointer vp)
-{
- NarrativeWindow *nw = vp;
-
- if ( num_slides(nw->p) == 0 ) return;
-
- 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(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));
- sc_editor_set_para_highlight(nw->sceditor, 1);
- sc_editor_set_cursor_para(nw->sceditor, 0);
- update_toolbar(nw);
-}
-
-
-static void start_slideshow_sig(GSimpleAction *action, GVariant *parameter,
- gpointer vp)
-{
- NarrativeWindow *nw = vp;
-
- if ( num_slides(nw->p) == 0 ) return;
-
- 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(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));
- sc_editor_set_para_highlight(nw->sceditor, 1);
- sc_editor_set_cursor_para(nw->sceditor, 0);
- gtk_widget_show_all(GTK_WIDGET(nw->show));
- update_toolbar(nw);
-}
-
-
-static int create_thumbnail(SCInterpreter *scin, SCBlock *bl,
- double *w, double *h, void **bvp, void *vp)
-{
- struct presentation *p = vp;
-
- *w = 270.0*(p->slide_width / p->slide_height);
- *h = 270.0;
- *bvp = bl;
-
- return 1;
-}
-
-
-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;
- struct frame *top;
- int sn = slide_number(p, scblocks);
-
- /* FIXME: Cache like crazy here */
- surf = render_sc(scblocks, w, h, p->slide_width, p->slide_height,
- p->stylesheet, NULL, p->is, sn, &top, p->lang);
- frame_free(top);
-
- return surf;
-}
-
-
-static int click_thumbnail(double x, double y, void *bvp, void *vp)
-{
- struct presentation *p = vp;
- SCBlock *scblocks = bvp;
- NarrativeWindow *nw = p->narrative_window;
-
- if ( p->narrative_window->show != NULL ) {
- sc_slideshow_set_slide(nw->show, scblocks);
- } else {
- if ( nw->n_slidewindows >= 16 ) {
- show_error(nw, _("Too many open slide windows"));
- } else {
- nw->slidewindows[nw->n_slidewindows++] = slide_window_open(p, scblocks,
- p->narrative_window->app);
- }
- }
-
- return 0;
-}
-
-
-GActionEntry nw_entries[] = {
-
- { "about", about_sig, NULL, NULL, NULL },
- { "save", save_sig, NULL, NULL, NULL },
- { "saveas", saveas_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 },
- { "clock", open_clock_sig, NULL, NULL, NULL },
- { "testcard", testcard_sig, NULL, NULL, NULL },
- { "first", first_para_sig, NULL, NULL, NULL },
- { "prev", prev_para_sig, NULL, NULL, NULL },
- { "next", next_para_sig, NULL, NULL, NULL },
- { "last", last_para_sig, NULL, NULL, NULL },
- { "print", print_sig, NULL, NULL, NULL },
- { "exportpdf", exportpdf_sig, NULL, NULL, NULL },
-};
-
-
-void update_titlebar(NarrativeWindow *nw)
-{
- char *title;
- char *title_new;
-
- title = get_titlebar_string(nw->p);
- title_new = realloc(title, strlen(title)+16);
- if ( title_new == NULL ) {
- free(title);
- return;
- } else {
- title = title_new;
- }
-
- strcat(title, " - Colloquium");
- if ( !nw->p->saved ) {
- strcat(title, " *");
- }
- gtk_window_set_title(GTK_WINDOW(nw->window), title);
-
- /* FIXME: Update all slide windows belonging to this NW */
-
- free(title);
-}
-
-
-void narrative_window_sw_closed(NarrativeWindow *nw, SlideWindow *sw)
-{
- int i;
- int found = 0;
-
- for ( i=0; i<nw->n_slidewindows; i++ ) {
- if ( nw->slidewindows[i] == sw ) {
-
- int j;
- for ( j=i; j<nw->n_slidewindows-1; j++ ) {
- nw->slidewindows[j] = nw->slidewindows[j+1];
- }
- nw->n_slidewindows--;
- found = 1;
- }
- }
-
- if ( !found ) {
- fprintf(stderr, "Couldn't find slide window in narrative record\n");
- }
-}
-
-
-NarrativeWindow *narrative_window_new(struct presentation *p, GApplication *papp)
-{
- NarrativeWindow *nw;
- GtkWidget *vbox;
- GtkWidget *scroll;
- GtkWidget *toolbar;
- GtkToolItem *button;
- SCCallbackList *cbl;
- GtkWidget *image;
- Colloquium *app = COLLOQUIUM(papp);
-
- if ( p->narrative_window != NULL ) {
- fprintf(stderr, "Narrative window is already open!\n");
- return NULL;
- }
-
- nw = calloc(1, sizeof(NarrativeWindow));
- if ( nw == NULL ) return NULL;
-
- nw->app = papp;
- nw->p = p;
- nw->n_slidewindows = 0;
-
- nw->window = gtk_application_window_new(GTK_APPLICATION(app));
- p->narrative_window = nw;
- update_titlebar(nw);
-
- g_action_map_add_action_entries(G_ACTION_MAP(nw->window), nw_entries,
- G_N_ELEMENTS(nw_entries), nw);
-
- vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
- gtk_container_add(GTK_CONTAINER(nw->window), vbox);
-
- /* If the presentation is completely empty, give ourselves at least
- * something to work with */
- if ( nw->p->scblocks == NULL ) {
- nw->p->scblocks = sc_parse("");
- }
-
- /* 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));
- cbl = sc_callback_list_new();
- 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);
- gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(toolbar), FALSE, FALSE, 0);
-
- /* Fullscreen */
- image = gtk_image_new_from_icon_name("view-fullscreen",
- GTK_ICON_SIZE_LARGE_TOOLBAR);
- 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));
-
- button = gtk_separator_tool_item_new();
- gtk_container_add(GTK_CONTAINER(toolbar), GTK_WIDGET(button));
-
- /* Add slide */
- image = gtk_image_new_from_icon_name("list-add",
- GTK_ICON_SIZE_LARGE_TOOLBAR);
- 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));
-
- button = gtk_separator_tool_item_new();
- gtk_container_add(GTK_CONTAINER(toolbar), GTK_WIDGET(button));
-
- image = gtk_image_new_from_icon_name("go-top",
- GTK_ICON_SIZE_LARGE_TOOLBAR);
- 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"));
- 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"));
- 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"));
- gtk_container_add(GTK_CONTAINER(toolbar), GTK_WIDGET(nw->blast));
- gtk_actionable_set_action_name(GTK_ACTIONABLE(nw->blast),
- "win.last");
-
- update_toolbar(nw);
-
- scroll = gtk_scrolled_window_new(NULL, NULL);
- gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll),
- GTK_POLICY_NEVER, GTK_POLICY_ALWAYS);
- gtk_container_add(GTK_CONTAINER(scroll), GTK_WIDGET(nw->sceditor));
-
- sc_editor_set_flow(nw->sceditor, 1);
- sc_editor_set_background(nw->sceditor, 0.9, 0.9, 0.9);
- sc_editor_set_min_border(nw->sceditor, 0.0);
- sc_editor_set_top_frame_editable(nw->sceditor, 1);
-
- g_signal_connect(G_OBJECT(nw->sceditor), "button-press-event",
- 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(nw_key_press_sig), nw);
- g_signal_connect(G_OBJECT(nw->window), "destroy",
- 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);
- gtk_container_set_focus_child(GTK_CONTAINER(nw->window),
- GTK_WIDGET(nw->sceditor));
-
- gtk_widget_show_all(nw->window);
- g_application_hold(papp);
-
- return nw;
-}
diff --git a/src/colloquium.c b/src/colloquium.c
new file mode 100644
index 0000000..7ce0dd8
--- /dev/null
+++ b/src/colloquium.c
@@ -0,0 +1,493 @@
+/*
+ * colloquium.c
+ *
+ * Copyright © 2013-2019 Thomas White <taw@bitwiz.org.uk>
+ *
+ * This file is part of Colloquium.
+ *
+ * Colloquium is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <gtk/gtk.h>
+#include <getopt.h>
+#include <string.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <glib.h>
+#include <glib/gstdio.h>
+
+#include <presentation.h>
+//#include <gtk/gtknarrativeview.h>
+
+#include "colloquium.h"
+
+#include <libintl.h>
+#define _(x) gettext(x)
+
+
+struct _colloquium
+{
+ GtkApplication parent_instance;
+ GtkBuilder *builder;
+ char *mydir;
+ int first_run;
+ char *imagestore;
+ int hidepointer;
+};
+
+
+typedef GtkApplicationClass ColloquiumClass;
+
+
+G_DEFINE_TYPE(Colloquium, colloquium, GTK_TYPE_APPLICATION)
+
+
+static void colloquium_activate(GApplication *papp)
+{
+ Colloquium *app = COLLOQUIUM(papp);
+ if ( !app->first_run ) {
+ Presentation *p;
+ p = presentation_new();
+ narrative_window_new(p, papp);
+ }
+}
+
+
+static void new_sig(GSimpleAction *action, GVariant *parameter, gpointer vp)
+{
+ GApplication *app = vp;
+ g_application_activate(app);
+}
+
+
+static void open_intro_doc(Colloquium *app)
+{
+ GFile *file = g_file_new_for_uri("resource:///uk/me/bitwiz/Colloquium/demo.sc");
+ g_application_open(G_APPLICATION(app), &file, 1, "");
+ g_object_unref(file);
+}
+
+
+static void intro_sig(GSimpleAction *action, GVariant *parameter, gpointer vp)
+{
+ GApplication *app = vp;
+ open_intro_doc(COLLOQUIUM(app));
+}
+
+
+void open_about_dialog(GtkWidget *parent)
+{
+ GtkWidget *window;
+
+ const gchar *authors[] = {
+ "Thomas White <taw@bitwiz.org.uk>",
+ NULL
+ };
+
+ window = gtk_about_dialog_new();
+ gtk_window_set_transient_for(GTK_WINDOW(window), GTK_WINDOW(parent));
+
+ gtk_about_dialog_set_program_name(GTK_ABOUT_DIALOG(window),
+ "Colloquium");
+ gtk_about_dialog_set_logo_icon_name(GTK_ABOUT_DIALOG(window),
+ "colloquium");
+ gtk_about_dialog_set_version(GTK_ABOUT_DIALOG(window),
+ PACKAGE_VERSION);
+ gtk_about_dialog_set_copyright(GTK_ABOUT_DIALOG(window),
+ "© 2017-2019 Thomas White <taw@bitwiz.me.uk>");
+ gtk_about_dialog_set_comments(GTK_ABOUT_DIALOG(window),
+ /* Description of the program */
+ _("Narrative-based presentation system"));
+ gtk_about_dialog_set_license_type(GTK_ABOUT_DIALOG(window), GTK_LICENSE_GPL_3_0);
+ gtk_about_dialog_set_website(GTK_ABOUT_DIALOG(window),
+ "https://www.bitwiz.me.uk/");
+ gtk_about_dialog_set_website_label(GTK_ABOUT_DIALOG(window),
+ "https://www.bitwiz.me.uk/");
+ gtk_about_dialog_set_authors(GTK_ABOUT_DIALOG(window), authors);
+ gtk_about_dialog_set_translator_credits(GTK_ABOUT_DIALOG(window),
+ _("translator-credits"));
+
+ g_signal_connect(window, "response", G_CALLBACK(gtk_widget_destroy),
+ NULL);
+
+ gtk_widget_show_all(window);
+}
+
+
+static void quit_sig(GSimpleAction *action, GVariant *parameter, gpointer vp)
+{
+ GApplication *app = vp;
+ g_application_quit(app);
+}
+
+
+static GFile **gslist_to_array(GSList *item, int *n)
+{
+ int i = 0;
+ int len = g_slist_length(item);
+ GFile **files = malloc(len * sizeof(GFile *));
+
+ if ( files == NULL ) return NULL;
+
+ while ( item != NULL ) {
+ if ( i == len ) {
+ fprintf(stderr, "WTF? Too many files\n");
+ break;
+ }
+ files[i++] = item->data;
+ item = item->next;
+ }
+
+ *n = len;
+ return files;
+}
+
+
+static gint open_response_sig(GtkWidget *d, gint response, GApplication *papp)
+{
+ if ( response == GTK_RESPONSE_ACCEPT ) {
+
+ GSList *files;
+ int n_files = 0;
+ GFile **files_array;
+ int i;
+
+ files = gtk_file_chooser_get_files(GTK_FILE_CHOOSER(d));
+ files_array = gslist_to_array(files, &n_files);
+ if ( files_array == NULL ) {
+ fprintf(stderr, "Failed to convert file list\n");
+ return 0;
+ }
+ g_slist_free(files);
+ g_application_open(papp, files_array, n_files, "");
+
+ for ( i=0; i<n_files; i++ ) {
+ g_object_unref(files_array[i]);
+ }
+
+ }
+
+ gtk_widget_destroy(d);
+
+ return 0;
+}
+
+
+static void open_sig(GSimpleAction *action, GVariant *parameter, gpointer vp)
+{
+ GtkWidget *d;
+ GApplication *app = vp;
+
+ d = gtk_file_chooser_dialog_new(_("Open Presentation"),
+ gtk_application_get_active_window(GTK_APPLICATION(app)),
+ GTK_FILE_CHOOSER_ACTION_OPEN,
+ _("_Cancel"), GTK_RESPONSE_CANCEL,
+ _("_Open"), GTK_RESPONSE_ACCEPT,
+ NULL);
+
+ gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(d), TRUE);
+
+ g_signal_connect(G_OBJECT(d), "response",
+ G_CALLBACK(open_response_sig), app);
+
+ gtk_widget_show_all(d);
+}
+
+
+GActionEntry app_entries[] = {
+
+ { "new", new_sig, NULL, NULL, NULL },
+ { "open", open_sig, NULL, NULL, NULL },
+ { "intro", intro_sig, NULL, NULL, NULL },
+ { "quit", quit_sig, NULL, NULL, NULL },
+};
+
+
+static void colloquium_open(GApplication *papp, GFile **files, gint n_files,
+ const gchar *hint)
+{
+ int i;
+
+ for ( i=0; i<n_files; i++ ) {
+ Presentation *p;
+ p = presentation_load(files[i]);
+ if ( p != NULL ) {
+ narrative_window_new(p, papp);
+ } else {
+ char *uri = g_file_get_uri(files[i]);
+ fprintf(stderr, _("Failed to load presentation '%s'\n"),
+ uri);
+ g_free(uri);
+ }
+ }
+}
+
+
+static void colloquium_finalize(GObject *object)
+{
+ G_OBJECT_CLASS(colloquium_parent_class)->finalize(object);
+}
+
+
+static void create_config(const char *filename)
+{
+
+ FILE *fh;
+ fh = fopen(filename, "w");
+ if ( fh == NULL ) {
+ fprintf(stderr, _("Failed to create config\n"));
+ return;
+ }
+
+ fprintf(fh, "imagestore: %s\n",
+ g_get_user_special_dir(G_USER_DIRECTORY_PICTURES));
+ fprintf(fh, "hidepointer: no\n");
+
+ fclose(fh);
+}
+
+
+static int yesno(const char *a)
+{
+ if ( a == NULL ) return 0;
+
+ if ( strcmp(a, "1") == 0 ) return 1;
+ if ( strcasecmp(a, "yes") == 0 ) return 1;
+ if ( strcasecmp(a, "true") == 0 ) return 1;
+
+ if ( strcasecmp(a, "0") == 0 ) return 0;
+ if ( strcasecmp(a, "no") == 0 ) return 0;
+ if ( strcasecmp(a, "false") == 0 ) return 0;
+
+ fprintf(stderr, "Don't understand '%s', assuming false\n", a);
+ return 0;
+}
+
+
+static void chomp(char *s)
+{
+ size_t i;
+
+ if ( !s ) return;
+
+ for ( i=0; i<strlen(s); i++ ) {
+ if ( (s[i] == '\n') || (s[i] == '\r') ) {
+ s[i] = '\0';
+ return;
+ }
+ }
+}
+
+
+static void read_config(const char *filename, Colloquium *app)
+{
+ FILE *fh;
+ char line[1024];
+
+ fh = fopen(filename, "r");
+ if ( fh == NULL ) {
+ fprintf(stderr, _("Failed to open config %s\n"), filename);
+ return;
+ }
+
+ do {
+
+ if ( fgets(line, 1024, fh) == NULL ) break;
+ chomp(line);
+
+ if ( strncmp(line, "imagestore: ", 11) == 0 ) {
+ app->imagestore = strdup(line+12);
+ }
+
+ if ( strncmp(line, "hidepointer: ", 12) == 0 ) {
+ app->hidepointer = yesno(line+13);
+ }
+ } while ( !feof(fh) );
+
+ fclose(fh);
+}
+
+
+const char *colloquium_get_imagestore(Colloquium *app)
+{
+ return app->imagestore;
+}
+
+
+int colloquium_get_hidepointer(Colloquium *app)
+{
+ return app->hidepointer;
+}
+
+
+static void colloquium_startup(GApplication *papp)
+{
+ Colloquium *app = COLLOQUIUM(papp);
+ const char *configdir;
+ char *tmp;
+
+ G_APPLICATION_CLASS(colloquium_parent_class)->startup(papp);
+
+ g_action_map_add_action_entries(G_ACTION_MAP(app), app_entries,
+ G_N_ELEMENTS(app_entries), app);
+
+ app->builder = gtk_builder_new_from_resource("/uk/me/bitwiz/Colloquium/menus.ui");
+ gtk_builder_add_from_resource(app->builder, "/uk/me/bitwiz/Colloquium/windows.ui", NULL);
+ gtk_application_set_menubar(GTK_APPLICATION(app),
+ G_MENU_MODEL(gtk_builder_get_object(app->builder, "menubar")));
+
+ if ( gtk_application_prefers_app_menu(GTK_APPLICATION(app)) ) {
+ /* Set the application menu only if it will be shown by the
+ * desktop environment. All the entries are already in the
+ * normal menus, so don't let GTK create a fallback menu in the
+ * menu bar. */
+ GMenuModel *mmodel = G_MENU_MODEL(gtk_builder_get_object(app->builder, "app-menu"));
+ gtk_application_set_app_menu(GTK_APPLICATION(app), mmodel);
+ }
+
+ configdir = g_get_user_config_dir();
+ app->mydir = malloc(strlen(configdir)+14);
+ strcpy(app->mydir, configdir);
+ strcat(app->mydir, "/colloquium");
+
+ if ( !g_file_test(app->mydir, G_FILE_TEST_IS_DIR) ) {
+
+ /* Folder not created yet */
+ open_intro_doc(app);
+ app->first_run = 1;
+
+ if ( g_mkdir(app->mydir, S_IRUSR | S_IWUSR | S_IXUSR) ) {
+ fprintf(stderr, _("Failed to create config folder\n"));
+ }
+ }
+
+ /* Read config file */
+ tmp = malloc(strlen(app->mydir)+32);
+ if ( tmp != NULL ) {
+
+ tmp[0] = '\0';
+ strcat(tmp, app->mydir);
+ strcat(tmp, "/config");
+
+ /* Create default config file if it doesn't exist */
+ if ( !g_file_test(tmp, G_FILE_TEST_EXISTS) ) {
+ create_config(tmp);
+ }
+
+ read_config(tmp, app);
+ free(tmp);
+ }
+}
+
+
+static void colloquium_shutdown(GApplication *app)
+{
+ G_APPLICATION_CLASS(colloquium_parent_class)->shutdown(app);
+}
+
+
+static void colloquium_class_init(ColloquiumClass *class)
+{
+ GApplicationClass *app_class = G_APPLICATION_CLASS(class);
+ GObjectClass *object_class = G_OBJECT_CLASS(class);
+
+ app_class->startup = colloquium_startup;
+ app_class->shutdown = colloquium_shutdown;
+ app_class->activate = colloquium_activate;
+ app_class->open = colloquium_open;
+
+ object_class->finalize = colloquium_finalize;
+}
+
+
+static void colloquium_init(Colloquium *app)
+{
+ app->imagestore = NULL;
+ app->hidepointer = 0;
+}
+
+
+static Colloquium *colloquium_new()
+{
+ Colloquium *app;
+
+ g_set_application_name("Colloquium");
+ app = g_object_new(colloquium_get_type(),
+ "application-id", "uk.org.bitwiz.Colloquium",
+ "flags", G_APPLICATION_HANDLES_OPEN,
+ "register-session", TRUE,
+ NULL);
+
+ app->first_run = 0; /* Will be updated at "startup" if appropriate */
+
+ return app;
+}
+
+
+static void show_help(const char *s)
+{
+ printf(_("Syntax: %s [options] [<file.sc>]\n\n"), s);
+ printf(_("Narrative-based presentation system.\n\n"
+ " -h, --help Display this help message.\n"));
+}
+
+
+int main(int argc, char *argv[])
+{
+ int c;
+ int status;
+ Colloquium *app;
+
+ /* Long options */
+ const struct option longopts[] = {
+ {"help", 0, NULL, 'h'},
+ {0, 0, NULL, 0}
+ };
+
+ /* Short options */
+ while ((c = getopt_long(argc, argv, "h", longopts, NULL)) != -1) {
+
+ switch (c)
+ {
+ case 'h' :
+ show_help(argv[0]);
+ return 0;
+
+ case 0 :
+ break;
+
+ default :
+ return 1;
+ }
+
+ }
+
+#if !GLIB_CHECK_VERSION(2,36,0)
+ g_type_init();
+#endif
+
+ bindtextdomain("colloquium", LOCALEDIR);
+ textdomain("colloquium");
+
+ app = colloquium_new();
+ status = g_application_run(G_APPLICATION(app), argc, argv);
+ g_object_unref(app);
+ return status;
+}
diff --git a/src/colloquium.h b/src/colloquium.h
new file mode 100644
index 0000000..89f600f
--- /dev/null
+++ b/src/colloquium.h
@@ -0,0 +1,45 @@
+/*
+ * colloquium.h
+ *
+ * Copyright © 2014-2018 Thomas White <taw@bitwiz.org.uk>
+ *
+ * This file is part of Colloquium.
+ *
+ * Colloquium is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef COLLOQUIUM_H
+#define COLLOQUIUM_H
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <glib-object.h>
+
+
+typedef struct _colloquium Colloquium;
+
+#define COLLOQUIUM(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), \
+ GTK_TYPE_APPLICATION, Colloquium))
+
+
+extern const char *colloquium_get_imagestore(Colloquium *app);
+extern int colloquium_get_hidepointer(Colloquium *app);
+
+extern void open_about_dialog(GtkWidget *parent);
+
+
+#endif /* COLLOQUIUM_H */
diff --git a/src/narrative_window.c b/src/narrative_window.c
new file mode 100644
index 0000000..f864fa2
--- /dev/null
+++ b/src/narrative_window.c
@@ -0,0 +1,787 @@
+/*
+ * narrative_window.c
+ *
+ * Copyright © 2014-2019 Thomas White <taw@bitwiz.org.uk>
+ *
+ * This file is part of Colloquium.
+ *
+ * Colloquium is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <gtk/gtk.h>
+#include <assert.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include <libintl.h>
+#define _(x) gettext(x)
+
+#include <presentation.h>
+
+#include "colloquium.h"
+#include "narrative_window.h"
+//#include "testcard.h"
+//#include "pr_clock.h"
+//#include "print.h"
+//#include "stylesheet_editor.h"
+typedef struct _nw GtkNarrativeView; /* FIXME placeholder */
+typedef struct _ss SCSlideshow; /* FIXME placeholder */
+typedef struct _sw SlideWindow; /* FIXME placeholder */
+typedef struct _pc PRClock; /* FIXME placeholder */
+
+struct _narrative_window
+{
+ GtkWidget *window;
+ GtkToolItem *bfirst;
+ GtkToolItem *bprev;
+ GtkToolItem *bnext;
+ GtkToolItem *blast;
+ GtkNarrativeView *nv;
+ GApplication *app;
+ Presentation *p;
+ GFile *file;
+ SCSlideshow *show;
+ int show_no_slides;
+ PRClock *pr_clock;
+ SlideWindow *slidewindows[16];
+ int n_slidewindows;
+};
+
+
+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;
+
+ /* FIXME */
+// cur_para = sc_editor_get_cursor_para(nw->nv);
+// if ( cur_para == 0 ) {
+// gtk_widget_set_sensitive(GTK_WIDGET(nw->bfirst), FALSE);
+// gtk_widget_set_sensitive(GTK_WIDGET(nw->bprev), FALSE);
+// } else {
+// gtk_widget_set_sensitive(GTK_WIDGET(nw->bfirst), TRUE);
+// gtk_widget_set_sensitive(GTK_WIDGET(nw->bprev), TRUE);
+// }
+//
+// if ( cur_para == sc_editor_get_num_paras(nw->nv)-1 ) {
+// gtk_widget_set_sensitive(GTK_WIDGET(nw->bnext), FALSE);
+// gtk_widget_set_sensitive(GTK_WIDGET(nw->blast), FALSE);
+// } else {
+// gtk_widget_set_sensitive(GTK_WIDGET(nw->bnext), TRUE);
+// gtk_widget_set_sensitive(GTK_WIDGET(nw->blast), TRUE);
+// }
+}
+
+
+static gint saveas_response_sig(GtkWidget *d, gint response,
+ NarrativeWindow *nw)
+{
+ if ( response == GTK_RESPONSE_ACCEPT ) {
+
+ GFile *file = gtk_file_chooser_get_file(GTK_FILE_CHOOSER(d));
+
+ if ( presentation_save(nw->p, file) ) {
+ show_error(nw, _("Failed to save presentation"));
+ }
+
+ /* save_presentation keeps a reference to both of these */
+ g_object_unref(file);
+
+ }
+ gtk_widget_destroy(d);
+ return 0;
+}
+
+
+static void saveas_sig(GSimpleAction *action, GVariant *parameter, gpointer vp)
+{
+ GtkWidget *d;
+ GtkWidget *box;
+ NarrativeWindow *nw = vp;
+
+ d = gtk_file_chooser_dialog_new(_("Save presentation"),
+ GTK_WINDOW(nw->window),
+ GTK_FILE_CHOOSER_ACTION_SAVE,
+ _("_Cancel"), GTK_RESPONSE_CANCEL,
+ _("_Save"), GTK_RESPONSE_ACCEPT,
+ NULL);
+ gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(d),
+ TRUE);
+
+ box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 8);
+ gtk_file_chooser_set_extra_widget(GTK_FILE_CHOOSER(d), box);
+
+ g_signal_connect(G_OBJECT(d), "response",
+ G_CALLBACK(saveas_response_sig), nw);
+
+ 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;
+
+ if ( nw->file == NULL ) {
+ return saveas_sig(NULL, NULL, nw);
+ }
+
+ presentation_save(nw->p, nw->file);
+}
+
+
+static void delete_slide_sig(GSimpleAction *action, GVariant *parameter,
+ gpointer vp)
+{
+ /* FIXME: GtkNarrativeView hooks */
+// SCBlock *ns;
+// NarrativeWindow *nw = vp;
+//
+// /* Get the SCBlock corresponding to the slide */
+// ns = sc_editor_get_cursor_bvp(nw->nv);
+// if ( ns == NULL ) {
+// fprintf(stderr, "Not a slide!\n");
+// return;
+// }
+//
+// sc_block_delete(&nw->dummy_top, ns);
+//
+// /* Full rerender */
+// sc_editor_set_scblock(nw->nv, nw->dummy_top);
+// nw->p->saved = 0;
+// update_titlebar(nw);
+}
+
+
+static gint load_ss_response_sig(GtkWidget *d, gint response,
+ NarrativeWindow *nw)
+{
+// if ( response == GTK_RESPONSE_ACCEPT ) {
+//
+// GFile *file;
+// Stylesheet *new_ss;
+//
+// file = gtk_file_chooser_get_file(GTK_FILE_CHOOSER(d));
+//
+// new_ss = stylesheet_load(file);
+// if ( new_ss != NULL ) {
+//
+// stylesheet_free(nw->p->stylesheet);
+// nw->p->stylesheet = new_ss;
+// sc_editor_set_stylesheet(nw->nv, new_ss);
+//
+// /* Full rerender */
+// sc_editor_set_scblock(nw->nv, nw->dummy_top);
+//
+// } else {
+// fprintf(stderr, _("Failed to load stylesheet\n"));
+// }
+//
+// g_object_unref(file);
+//
+// }
+//
+// gtk_widget_destroy(d);
+
+ return 0;
+}
+
+
+static void stylesheet_changed_sig(GtkWidget *da, NarrativeWindow *nw)
+{
+// int i;
+//
+// /* It might have changed (been created) since last time */
+// sc_editor_set_stylesheet(nw->nv, nw->p->stylesheet);
+//
+// /* Full rerender, first block may have changed */
+// sc_editor_set_scblock(nw->nv, nw->dummy_top);
+//
+// /* Full rerender of all slide windows */
+// for ( i=0; i<nw->n_slidewindows; i++ ) {
+// slide_window_update(nw->slidewindows[i]);
+// }
+}
+
+
+static void edit_ss_sig(GSimpleAction *action, GVariant *parameter,
+ gpointer vp)
+{
+// NarrativeWindow *nw = vp;
+// StylesheetEditor *se;
+//
+// 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));
+}
+
+
+static void load_ss_sig(GSimpleAction *action, GVariant *parameter,
+ gpointer vp)
+{
+// //SCBlock *nsblock;
+// //SCBlock *templ;
+// NarrativeWindow *nw = vp;
+// GtkWidget *d;
+//
+// 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,
+// NULL);
+//
+// g_signal_connect(G_OBJECT(d), "response",
+// G_CALLBACK(load_ss_response_sig), nw);
+//
+// gtk_widget_show_all(d);
+}
+
+
+static void add_slide_sig(GSimpleAction *action, GVariant *parameter,
+ gpointer vp)
+{
+ //NarrativeWindow *nw = vp;
+
+ /* FIXME: implementation */
+
+ //nw->p->saved = 0;
+ //update_titlebar(nw);
+}
+
+
+static void first_para_sig(GSimpleAction *action, GVariant *parameter,
+ gpointer vp)
+{
+// NarrativeWindow *nw = vp;
+// sc_editor_set_cursor_para(nw->nv, 0);
+// pr_clock_set_pos(nw->pr_clock, sc_editor_get_cursor_para(nw->nv),
+// sc_editor_get_num_paras(nw->nv));
+// update_toolbar(nw);
+}
+
+
+static void ss_prev_para(SCSlideshow *ss, void *vp)
+{
+// NarrativeWindow *nw = vp;
+// if ( sc_editor_get_cursor_para(nw->nv) == 0 ) return;
+// sc_editor_set_cursor_para(nw->nv,
+// sc_editor_get_cursor_para(nw->nv)-1);
+// pr_clock_set_pos(nw->pr_clock, sc_editor_get_cursor_para(nw->nv),
+// sc_editor_get_num_paras(nw->nv));
+// update_toolbar(nw);
+}
+
+
+static void prev_para_sig(GSimpleAction *action, GVariant *parameter,
+ gpointer vp)
+{
+// NarrativeWindow *nw = vp;
+// ss_prev_para(nw->show, nw);
+}
+
+
+static void ss_next_para(SCSlideshow *ss, void *vp)
+{
+// NarrativeWindow *nw = vp;
+// SCBlock *ns;
+//
+// sc_editor_set_cursor_para(nw->nv,
+// sc_editor_get_cursor_para(nw->nv)+1);
+//
+// /* If we only have one monitor, don't try to do paragraph counting */
+// if ( ss->single_monitor && !nw->show_no_slides ) {
+// int i, max;
+// max = sc_editor_get_num_paras(nw->nv);
+// for ( i=sc_editor_get_cursor_para(nw->nv); i<max; i++ ) {
+// SCBlock *ns;
+// sc_editor_set_cursor_para(nw->nv, i);
+// ns = sc_editor_get_cursor_bvp(nw->nv);
+// if ( ns != NULL ) break;
+// }
+// }
+//
+// pr_clock_set_pos(nw->pr_clock, sc_editor_get_cursor_para(nw->nv),
+// sc_editor_get_num_paras(nw->nv));
+// ns = sc_editor_get_cursor_bvp(nw->nv);
+// if ( ns != NULL ) {
+// sc_slideshow_set_slide(nw->show, ns);
+// }
+// update_toolbar(nw);
+}
+
+
+static void next_para_sig(GSimpleAction *action, GVariant *parameter,
+ gpointer vp)
+{
+// NarrativeWindow *nw = vp;
+// ss_next_para(nw->show, nw);
+}
+
+
+static void last_para_sig(GSimpleAction *action, GVariant *parameter,
+ gpointer vp)
+{
+// NarrativeWindow *nw = vp;
+// sc_editor_set_cursor_para(nw->nv, -1);
+// pr_clock_set_pos(nw->pr_clock, sc_editor_get_cursor_para(nw->nv),
+// sc_editor_get_num_paras(nw->nv));
+// update_toolbar(nw);
+}
+
+
+static void open_clock_sig(GSimpleAction *action, GVariant *parameter, gpointer vp)
+{
+ //NarrativeWindow *nw = vp;
+// nw->pr_clock = pr_clock_new();
+}
+
+
+static void testcard_sig(GSimpleAction *action, GVariant *parameter,
+ gpointer vp)
+{
+ //NarrativeWindow *nw = vp;
+// show_testcard(nw->p);
+}
+
+
+static gint export_pdf_response_sig(GtkWidget *d, gint response,
+ Presentation *p)
+{
+// if ( response == GTK_RESPONSE_ACCEPT ) {
+// char *filename;
+// filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(d));
+// export_pdf(p, filename);
+// g_free(filename);
+// }
+//
+// gtk_widget_destroy(d);
+//
+ return 0;
+}
+
+
+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)
+{
+// NarrativeWindow *nw = vp;
+// GtkWidget *d;
+//
+// d = gtk_file_chooser_dialog_new(_("Export PDF"),
+// NULL,
+// GTK_FILE_CHOOSER_ACTION_SAVE,
+// _("_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), nw->p);
+//
+// gtk_widget_show_all(d);
+}
+
+
+
+static gboolean nw_button_press_sig(GtkWidget *da, GdkEventButton *event,
+ NarrativeWindow *nw)
+{
+ return 0;
+}
+
+
+static void changed_sig(GtkWidget *da, NarrativeWindow *nw)
+{
+ //nw->p->saved = 0;
+ //update_titlebar(nw);
+}
+
+
+static void scroll_down(NarrativeWindow *nw)
+{
+ gdouble inc, val;
+ GtkAdjustment *vadj;
+
+ vadj = gtk_scrollable_get_vadjustment(GTK_SCROLLABLE(nw->nv));
+ inc = gtk_adjustment_get_step_increment(GTK_ADJUSTMENT(vadj));
+ val = gtk_adjustment_get_value(GTK_ADJUSTMENT(vadj));
+ gtk_adjustment_set_value(GTK_ADJUSTMENT(vadj), inc+val);
+}
+
+
+static gboolean nw_destroy_sig(GtkWidget *da, NarrativeWindow *nw)
+{
+ g_application_release(nw->app);
+ return FALSE;
+}
+
+
+static gboolean nw_key_press_sig(GtkWidget *da, GdkEventKey *event,
+ NarrativeWindow *nw)
+{
+ switch ( event->keyval ) {
+
+ case GDK_KEY_B :
+ case GDK_KEY_b :
+ if ( nw->show != NULL ) {
+ scroll_down(nw);
+ return TRUE;
+ }
+ break;
+
+ case GDK_KEY_Page_Up :
+ if ( nw->show != NULL ) {
+ ss_prev_para(nw->show, nw);
+ return TRUE;
+ }
+ break;
+
+ case GDK_KEY_Page_Down :
+ if ( nw->show != NULL) {
+ ss_next_para(nw->show, nw);
+ return TRUE;
+ }
+ break;
+
+ case GDK_KEY_Escape :
+ if ( nw->show != NULL ) {
+ gtk_widget_destroy(GTK_WIDGET(nw->show));
+ return TRUE;
+ }
+ break;
+
+ case GDK_KEY_F5 :
+ if ( nw->show != NULL ) {
+ /* Trap F5 so that full rerender does NOT happen */
+ return TRUE;
+ }
+
+ }
+
+ return FALSE;
+}
+
+
+static gboolean ss_destroy_sig(GtkWidget *da, NarrativeWindow *nw)
+{
+ nw->show = NULL;
+ //sc_editor_set_para_highlight(nw->nv, 0); FIXME
+
+ gtk_widget_set_sensitive(GTK_WIDGET(nw->bfirst), FALSE);
+ gtk_widget_set_sensitive(GTK_WIDGET(nw->bprev), FALSE);
+ gtk_widget_set_sensitive(GTK_WIDGET(nw->bnext), FALSE);
+ gtk_widget_set_sensitive(GTK_WIDGET(nw->blast), FALSE);
+
+ return FALSE;
+}
+
+
+static void start_slideshow_here_sig(GSimpleAction *action, GVariant *parameter,
+ gpointer vp)
+{
+ //NarrativeWindow *nw = vp;
+ //void *bvp;
+
+ //if ( num_slides(nw->p) == 0 ) return;
+
+ //bvp = sc_editor_get_cursor_bvp(nw->nv);
+ //if ( bvp == NULL ) return;
+
+ //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(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);
+ //sc_editor_set_para_highlight(nw->nv, 1);
+ //gtk_widget_show_all(GTK_WIDGET(nw->show));
+ //update_toolbar(nw);
+}
+
+
+static void start_slideshow_noslides_sig(GSimpleAction *action, GVariant *parameter,
+ gpointer vp)
+{
+ //NarrativeWindow *nw = vp;
+
+ //if ( num_slides(nw->p) == 0 ) return;
+
+ //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(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));
+ //sc_editor_set_para_highlight(nw->nv, 1);
+ //sc_editor_set_cursor_para(nw->nv, 0);
+ //update_toolbar(nw);
+}
+
+
+static void start_slideshow_sig(GSimpleAction *action, GVariant *parameter,
+ gpointer vp)
+{
+// NarrativeWindow *nw = vp;
+//
+// if ( num_slides(nw->p) == 0 ) return;
+//
+// 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(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));
+// sc_editor_set_para_highlight(nw->nv, 1);
+// sc_editor_set_cursor_para(nw->nv, 0);
+// gtk_widget_show_all(GTK_WIDGET(nw->show));
+// update_toolbar(nw);
+}
+
+
+GActionEntry nw_entries[] = {
+
+ { "about", about_sig, NULL, NULL, NULL },
+ { "save", save_sig, NULL, NULL, NULL },
+ { "saveas", saveas_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 },
+ { "clock", open_clock_sig, NULL, NULL, NULL },
+ { "testcard", testcard_sig, NULL, NULL, NULL },
+ { "first", first_para_sig, NULL, NULL, NULL },
+ { "prev", prev_para_sig, NULL, NULL, NULL },
+ { "next", next_para_sig, NULL, NULL, NULL },
+ { "last", last_para_sig, NULL, NULL, NULL },
+ { "print", print_sig, NULL, NULL, NULL },
+ { "exportpdf", exportpdf_sig, NULL, NULL, NULL },
+};
+
+
+void update_titlebar(NarrativeWindow *nw)
+{
+ char *title;
+ char *title_new;
+
+ title = strdup("test"); // FIXME get_titlebar_string(nw->p);
+ title_new = realloc(title, strlen(title)+16);
+ if ( title_new == NULL ) {
+ free(title);
+ return;
+ } else {
+ title = title_new;
+ }
+
+ strcat(title, " - Colloquium");
+//FIXME if ( !nw->p->saved ) {
+// strcat(title, " *");
+// }
+ gtk_window_set_title(GTK_WINDOW(nw->window), title);
+
+ /* FIXME: Update all slide windows belonging to this NW */
+
+ free(title);
+}
+
+
+//void narrative_window_sw_closed(NarrativeWindow *nw, SlideWindow *sw)
+//{
+// int i;
+// int found = 0;
+//
+// for ( i=0; i<nw->n_slidewindows; i++ ) {
+// if ( nw->slidewindows[i] == sw ) {
+//
+// int j;
+// for ( j=i; j<nw->n_slidewindows-1; j++ ) {
+// nw->slidewindows[j] = nw->slidewindows[j+1];
+// }
+// nw->n_slidewindows--;
+// found = 1;
+// }
+// }
+//
+// if ( !found ) {
+// fprintf(stderr, "Couldn't find slide window in narrative record\n");
+// }
+//}
+
+
+NarrativeWindow *narrative_window_new(Presentation *p, GApplication *papp)
+{
+ NarrativeWindow *nw;
+ GtkWidget *vbox;
+ GtkWidget *scroll;
+ GtkWidget *toolbar;
+ GtkToolItem *button;
+ GtkWidget *image;
+ Colloquium *app = COLLOQUIUM(papp);
+
+// if ( p->narrative_window != NULL ) {
+// fprintf(stderr, "Narrative window is already open!\n");
+// return NULL;
+// }
+
+ nw = calloc(1, sizeof(NarrativeWindow));
+ if ( nw == NULL ) return NULL;
+
+ nw->app = papp;
+ nw->p = p;
+ nw->n_slidewindows = 0;
+
+ nw->window = gtk_application_window_new(GTK_APPLICATION(app));
+// p->narrative_window = nw;
+ update_titlebar(nw);
+
+ g_action_map_add_action_entries(G_ACTION_MAP(nw->window), nw_entries,
+ G_N_ELEMENTS(nw_entries), nw);
+
+ vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
+ gtk_container_add(GTK_CONTAINER(nw->window), vbox);
+
+ nw->nv = NULL; //sc_editor_new(nw->dummy_top, p->stylesheet, p->lang, colloquium_get_imagestore(app));
+
+ toolbar = gtk_toolbar_new();
+ gtk_toolbar_set_style(GTK_TOOLBAR(toolbar), GTK_TOOLBAR_ICONS);
+ gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(toolbar), FALSE, FALSE, 0);
+
+ /* Fullscreen */
+ image = gtk_image_new_from_icon_name("view-fullscreen",
+ GTK_ICON_SIZE_LARGE_TOOLBAR);
+ 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));
+
+ button = gtk_separator_tool_item_new();
+ gtk_container_add(GTK_CONTAINER(toolbar), GTK_WIDGET(button));
+
+ /* Add slide */
+ image = gtk_image_new_from_icon_name("list-add",
+ GTK_ICON_SIZE_LARGE_TOOLBAR);
+ 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));
+
+ button = gtk_separator_tool_item_new();
+ gtk_container_add(GTK_CONTAINER(toolbar), GTK_WIDGET(button));
+
+ image = gtk_image_new_from_icon_name("go-top",
+ GTK_ICON_SIZE_LARGE_TOOLBAR);
+ 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"));
+ 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"));
+ 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"));
+ gtk_container_add(GTK_CONTAINER(toolbar), GTK_WIDGET(nw->blast));
+ gtk_actionable_set_action_name(GTK_ACTIONABLE(nw->blast),
+ "win.last");
+
+ update_toolbar(nw);
+
+ scroll = gtk_scrolled_window_new(NULL, NULL);
+ gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll),
+ GTK_POLICY_NEVER, GTK_POLICY_ALWAYS);
+ gtk_container_add(GTK_CONTAINER(scroll), GTK_WIDGET(nw->nv));
+
+ g_signal_connect(G_OBJECT(nw->nv), "button-press-event",
+ G_CALLBACK(nw_button_press_sig), nw);
+ g_signal_connect(G_OBJECT(nw->nv), "changed",
+ G_CALLBACK(changed_sig), nw);
+ g_signal_connect(G_OBJECT(nw->nv), "key-press-event",
+ G_CALLBACK(nw_key_press_sig), nw);
+ g_signal_connect(G_OBJECT(nw->window), "destroy",
+ 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);
+ gtk_container_set_focus_child(GTK_CONTAINER(nw->window),
+ GTK_WIDGET(nw->nv));
+
+ gtk_widget_show_all(nw->window);
+ g_application_hold(papp);
+
+ return nw;
+}
diff --git a/src-old/narrative_window.h b/src/narrative_window.h
index 24b4a4b..51c7dcc 100644
--- a/src-old/narrative_window.h
+++ b/src/narrative_window.h
@@ -1,7 +1,7 @@
/*
* narrative_window.h
*
- * Copyright © 2014-2018 Thomas White <taw@bitwiz.org.uk>
+ * Copyright © 2014-2019 Thomas White <taw@bitwiz.org.uk>
*
* This file is part of Colloquium.
*
@@ -27,16 +27,9 @@
#include <config.h>
#endif
-
typedef struct _narrative_window NarrativeWindow;
-#include "slide_window.h"
-
-extern NarrativeWindow *narrative_window_new(struct presentation *p,
+extern NarrativeWindow *narrative_window_new(Presentation *p,
GApplication *app);
-extern void update_titlebar(NarrativeWindow *nw);
-
-extern void narrative_window_sw_closed(NarrativeWindow *nw, SlideWindow *sw);
-
#endif /* NARRATIVE_WINDOW_H */