From 4904182915c1a02c8ac6fb26397cb12e0aab51b9 Mon Sep 17 00:00:00 2001 From: Thomas White Date: Tue, 26 Feb 2019 21:21:36 +0100 Subject: Skeleton of main program --- libstorycode/presentation.c | 32 ++ libstorycode/presentation.h | 4 + meson.build | 25 +- src-old/narrative_window.c | 927 -------------------------------------------- src-old/narrative_window.h | 42 -- src/colloquium.c | 493 +++++++++++++++++++++++ src/colloquium.h | 45 +++ src/narrative_window.c | 787 +++++++++++++++++++++++++++++++++++++ src/narrative_window.h | 35 ++ 9 files changed, 1403 insertions(+), 987 deletions(-) delete mode 100644 src-old/narrative_window.c delete mode 100644 src-old/narrative_window.h create mode 100644 src/colloquium.c create mode 100644 src/colloquium.h create mode 100644 src/narrative_window.c create mode 100644 src/narrative_window.h 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 #include #include +#include #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 #endif +#include + 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 - * - * 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 . - * - */ - - -#ifdef HAVE_CONFIG_H -#include -#endif - -#include -#include -#include -#include - -#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; in_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); isceditor, 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; in_slidewindows; i++ ) { - if ( nw->slidewindows[i] == sw ) { - - int j; - for ( j=i; jn_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-old/narrative_window.h b/src-old/narrative_window.h deleted file mode 100644 index 24b4a4b..0000000 --- a/src-old/narrative_window.h +++ /dev/null @@ -1,42 +0,0 @@ -/* - * narrative_window.h - * - * Copyright © 2014-2018 Thomas White - * - * 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 . - * - */ - -#ifndef NARRATIVE_WINDOW_H -#define NARRATIVE_WINDOW_H - -#ifdef HAVE_CONFIG_H -#include -#endif - - -typedef struct _narrative_window NarrativeWindow; - -#include "slide_window.h" - -extern NarrativeWindow *narrative_window_new(struct presentation *p, - GApplication *app); - -extern void update_titlebar(NarrativeWindow *nw); - -extern void narrative_window_sw_closed(NarrativeWindow *nw, SlideWindow *sw); - -#endif /* NARRATIVE_WINDOW_H */ 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 + * + * 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 . + * + */ + + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include + +#include +//#include + +#include "colloquium.h" + +#include +#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 ", + 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 "); + 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; ifinalize(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; iimagestore = 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] []\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 + * + * 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 . + * + */ + +#ifndef COLLOQUIUM_H +#define COLLOQUIUM_H + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include + + +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 + * + * 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 . + * + */ + + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include + +#include +#define _(x) gettext(x) + +#include + +#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; in_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); inv, 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; in_slidewindows; i++ ) { +// if ( nw->slidewindows[i] == sw ) { +// +// int j; +// for ( j=i; jn_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/narrative_window.h b/src/narrative_window.h new file mode 100644 index 0000000..51c7dcc --- /dev/null +++ b/src/narrative_window.h @@ -0,0 +1,35 @@ +/* + * narrative_window.h + * + * Copyright © 2014-2019 Thomas White + * + * 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 . + * + */ + +#ifndef NARRATIVE_WINDOW_H +#define NARRATIVE_WINDOW_H + +#ifdef HAVE_CONFIG_H +#include +#endif + +typedef struct _narrative_window NarrativeWindow; + +extern NarrativeWindow *narrative_window_new(Presentation *p, + GApplication *app); + +#endif /* NARRATIVE_WINDOW_H */ -- cgit v1.2.3