From 24c20239779d0ec616adde651c594c7bf08d58c7 Mon Sep 17 00:00:00 2001 From: Thomas White Date: Tue, 19 Feb 2019 18:17:56 +0100 Subject: WIP --- libstorycode/scparse_priv.h | 4 + libstorycode/slide.c | 12 +- libstorycode/slide.h | 2 +- libstorycode/storycode.y | 51 +- meson.build | 55 +- src-old/colloquium.c | 476 ++++++++++ src-old/colloquium.h | 45 + src-old/debugger.c | 229 +++++ src-old/debugger.h | 32 + src-old/frame.c | 1820 +++++++++++++++++++++++++++++++++++ src-old/frame.h | 194 ++++ src-old/imagestore.c | 369 ++++++++ src-old/imagestore.h | 43 + src-old/narrative_window.c | 927 ++++++++++++++++++ src-old/narrative_window.h | 42 + src-old/pr_clock.c | 438 +++++++++ src-old/pr_clock.h | 37 + src-old/presentation.c | 362 +++++++ src-old/presentation.h | 83 ++ src-old/print.c | 314 +++++++ src-old/print.h | 32 + src-old/render.c | 332 +++++++ src-old/render.h | 62 ++ src-old/sc_editor.c | 2197 +++++++++++++++++++++++++++++++++++++++++++ src-old/sc_editor.h | 199 ++++ src-old/sc_interp.c | 1148 ++++++++++++++++++++++ src-old/sc_interp.h | 80 ++ src-old/sc_parse.c | 856 +++++++++++++++++ src-old/sc_parse.h | 85 ++ src-old/slide_window.c | 282 ++++++ src-old/slide_window.h | 37 + src-old/slideshow.c | 240 +++++ src-old/slideshow.h | 83 ++ src-old/stylesheet.c | 288 ++++++ src-old/stylesheet.h | 54 ++ src-old/stylesheet_editor.c | 794 ++++++++++++++++ src-old/stylesheet_editor.h | 128 +++ src-old/testcard.c | 288 ++++++ src-old/testcard.h | 32 + src-old/utils.c | 137 +++ src-old/utils.h | 47 + src/colloquium.c | 476 ---------- src/colloquium.h | 45 - src/debugger.c | 229 ----- src/debugger.h | 32 - src/frame.c | 1820 ----------------------------------- src/frame.h | 194 ---- src/imagestore.c | 369 -------- src/imagestore.h | 43 - src/narrative_window.c | 927 ------------------ src/narrative_window.h | 42 - src/pdfstorycode.c | 55 ++ src/pr_clock.c | 438 --------- src/pr_clock.h | 37 - src/presentation.c | 362 ------- src/presentation.h | 83 -- src/print.c | 314 ------- src/print.h | 32 - src/render.c | 332 ------- src/render.h | 62 -- src/sc2_test.c | 50 - src/sc_editor.c | 2197 ------------------------------------------- src/sc_editor.h | 199 ---- src/sc_interp.c | 1148 ---------------------- src/sc_interp.h | 80 -- src/sc_parse.c | 856 ----------------- src/sc_parse.h | 85 -- src/slide_window.c | 282 ------ src/slide_window.h | 37 - src/slideshow.c | 240 ----- src/slideshow.h | 83 -- src/stylesheet.c | 288 ------ src/stylesheet.h | 54 -- src/stylesheet_editor.c | 794 ---------------- src/stylesheet_editor.h | 128 --- src/testcard.c | 288 ------ src/testcard.h | 32 - src/utils.c | 137 --- src/utils.h | 47 - tests/meson.build | 42 +- 80 files changed, 12970 insertions(+), 12925 deletions(-) create mode 100644 src-old/colloquium.c create mode 100644 src-old/colloquium.h create mode 100644 src-old/debugger.c create mode 100644 src-old/debugger.h create mode 100644 src-old/frame.c create mode 100644 src-old/frame.h create mode 100644 src-old/imagestore.c create mode 100644 src-old/imagestore.h create mode 100644 src-old/narrative_window.c create mode 100644 src-old/narrative_window.h create mode 100644 src-old/pr_clock.c create mode 100644 src-old/pr_clock.h create mode 100644 src-old/presentation.c create mode 100644 src-old/presentation.h create mode 100644 src-old/print.c create mode 100644 src-old/print.h create mode 100644 src-old/render.c create mode 100644 src-old/render.h create mode 100644 src-old/sc_editor.c create mode 100644 src-old/sc_editor.h create mode 100644 src-old/sc_interp.c create mode 100644 src-old/sc_interp.h create mode 100644 src-old/sc_parse.c create mode 100644 src-old/sc_parse.h create mode 100644 src-old/slide_window.c create mode 100644 src-old/slide_window.h create mode 100644 src-old/slideshow.c create mode 100644 src-old/slideshow.h create mode 100644 src-old/stylesheet.c create mode 100644 src-old/stylesheet.h create mode 100644 src-old/stylesheet_editor.c create mode 100644 src-old/stylesheet_editor.h create mode 100644 src-old/testcard.c create mode 100644 src-old/testcard.h create mode 100644 src-old/utils.c create mode 100644 src-old/utils.h delete mode 100644 src/colloquium.c delete mode 100644 src/colloquium.h delete mode 100644 src/debugger.c delete mode 100644 src/debugger.h delete mode 100644 src/frame.c delete mode 100644 src/frame.h delete mode 100644 src/imagestore.c delete mode 100644 src/imagestore.h delete mode 100644 src/narrative_window.c delete mode 100644 src/narrative_window.h create mode 100644 src/pdfstorycode.c delete mode 100644 src/pr_clock.c delete mode 100644 src/pr_clock.h delete mode 100644 src/presentation.c delete mode 100644 src/presentation.h delete mode 100644 src/print.c delete mode 100644 src/print.h delete mode 100644 src/render.c delete mode 100644 src/render.h delete mode 100644 src/sc2_test.c delete mode 100644 src/sc_editor.c delete mode 100644 src/sc_editor.h delete mode 100644 src/sc_interp.c delete mode 100644 src/sc_interp.h delete mode 100644 src/sc_parse.c delete mode 100644 src/sc_parse.h delete mode 100644 src/slide_window.c delete mode 100644 src/slide_window.h delete mode 100644 src/slideshow.c delete mode 100644 src/slideshow.h delete mode 100644 src/stylesheet.c delete mode 100644 src/stylesheet.h delete mode 100644 src/stylesheet_editor.c delete mode 100644 src/stylesheet_editor.h delete mode 100644 src/testcard.c delete mode 100644 src/testcard.h delete mode 100644 src/utils.c delete mode 100644 src/utils.h diff --git a/libstorycode/scparse_priv.h b/libstorycode/scparse_priv.h index 51d0d8a..54dca40 100644 --- a/libstorycode/scparse_priv.h +++ b/libstorycode/scparse_priv.h @@ -34,6 +34,10 @@ struct scpctx Stylesheet *ss; Slide *s; + int n_str; + int max_str; + char **str; + /* Frame options */ struct frame_geom geom; }; diff --git a/libstorycode/slide.c b/libstorycode/slide.c index 28ca159..6ff5691 100644 --- a/libstorycode/slide.c +++ b/libstorycode/slide.c @@ -27,12 +27,17 @@ #include #include +#include #include "slide.h" enum slide_item_type { SLIDE_ITEM_TEXT, + SLIDE_ITEM_IMAGE, + SLIDE_ITEM_FOOTER, + SLIDE_ITEM_SLIDETITLE, + SLIDE_ITEM_PRESTITLE, }; @@ -82,8 +87,13 @@ int slide_add_image(Slide *s, char *filename, struct frame_geom geom) } -int slide_add_text(Slide *s, char *text, struct frame_geom geom) +int slide_add_text(Slide *s, char **text, int n_text, struct frame_geom geom) { + int i; + printf("got text:\n"); + for ( i=0; i + #include extern int sclex(); extern int scparse(); @@ -75,10 +76,8 @@ %type stylesheet %type prestitle %type STRING -%type textframe %type imageframe %type bulletpoint -%type multi_line_string %type frameopt %type geometry /* FIXME: Should have its own type */ %type slidetitle @@ -93,12 +92,33 @@ ctx->n = narrative_new(); ctx->ss = stylesheet_new(); ctx->s = slide_new(); + + ctx->n_str = 0; + ctx->max_str = 32; + ctx->str = malloc(ctx->max_str*sizeof(char *)); + if ( ctx->str == NULL ) ctx->max_str = 0; } %{ void frameopts_reset(struct scpctx *ctx) { } + + void str_reset(struct scpctx *ctx) + { + ctx->n_str = 0; + } + + void add_str(struct scpctx *ctx, char *str) + { + if ( ctx->n_str == ctx->max_str ) { + char **nstr = realloc(ctx->str, (ctx->max_str+32)*sizeof(char *)); + if ( nstr == NULL ) return; + ctx->max_str += 32; + } + + ctx->str[ctx->n_str++] = str; + } %} %% @@ -148,11 +168,15 @@ slide_parts: ; slide_part: - prestitle { slide_add_prestitle(ctx->s, $1); } -| imageframe { slide_add_image(ctx->s, $1, ctx->geom); frameopts_reset(ctx); } -| textframe { slide_add_text(ctx->s, $1, ctx->geom); frameopts_reset(ctx); } + prestitle { slide_add_prestitle(ctx->s, $1); str_reset(ctx); } +| imageframe { slide_add_image(ctx->s, $1, ctx->geom); + frameopts_reset(ctx); + str_reset(ctx); } +| textframe { slide_add_text(ctx->s, ctx->str, ctx->n_str, ctx->geom); + frameopts_reset(ctx); + str_reset(ctx); } | FOOTER { slide_add_footer(ctx->s); } -| slidetitle { slide_add_slidetitle(ctx->s, $1); } +| slidetitle { slide_add_slidetitle(ctx->s, $1); str_reset(ctx); } ; imageframe: @@ -160,14 +184,15 @@ imageframe: ; textframe: - TEXTFRAME frame_options multi_line_string { printf("text frame '%s'\n", $3); } -| TEXTFRAME frame_options OPENBRACE multi_line_string CLOSEBRACE { printf("text frame m\n"); } + TEXTFRAME frame_options multi_line_string { } +| TEXTFRAME frame_options OPENBRACE multi_line_string CLOSEBRACE { } +; multi_line_string: - STRING { printf("string '%s'\n", $1); } -| multi_line_string STRING { printf("more string '%s'\n", $2); } -| bulletpoint { printf("string *%s\n", $1); } -| multi_line_string bulletpoint { printf("more string *%s\n", $1); } + STRING { add_str(ctx, $1); } +| multi_line_string STRING { add_str(ctx, $2); } +| bulletpoint { add_str(ctx, $1); } +| multi_line_string bulletpoint { add_str(ctx, $2); } ; /* There can be any number of options */ @@ -178,7 +203,7 @@ frame_options: /* Each option is enclosed in square brackets */ frame_option: - SQOPEN frameopt SQCLOSE { printf("got an option: '%s'\n", $2); } + SQOPEN frameopt SQCLOSE { } ; frameopt: diff --git a/meson.build b/meson.build index c99fb01..ee790d0 100644 --- a/meson.build +++ b/meson.build @@ -19,7 +19,8 @@ subdir('po') # Dependencies gtkdep = dependency('gtk+-3.0') -jsondep = dependency('json-glib-1.0') +glib_dep = dependency('glib-2.0') +gio_dep = dependency('gio-2.0') cc = meson.get_compiler('c') mdep = cc.find_library('m', required : false) @@ -66,36 +67,36 @@ libstorycode_dep = declare_dependency(include_directories : libstorycode_include link_with : libstorycode) -executable('sc2_test', - ['src/sc2_test.c', +executable('pdfstorycode', + ['src/pdfstorycode.c', ], gresources, - dependencies : [gtkdep, libstorycode_dep]) + dependencies : [glib_dep, gio_dep, libstorycode_dep]) # Main program -executable('colloquium', - ['src/colloquium.c', - 'src/narrative_window.c', - 'src/render.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) +#executable('colloquium', +# ['src/colloquium.c', +# 'src/narrative_window.c', +# 'src/render.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) # Desktop file install_data(['data/colloquium.desktop'], diff --git a/src-old/colloquium.c b/src-old/colloquium.c new file mode 100644 index 0000000..02ffa0a --- /dev/null +++ b/src-old/colloquium.c @@ -0,0 +1,476 @@ +/* + * colloquium.c + * + * Copyright © 2013-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 +#include +#include + +#include "colloquium.h" +#include "presentation.h" +#include "narrative_window.h" +#include "utils.h" + + +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 ) { + struct presentation *p; + p = new_presentation(app->imagestore); + 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-2018 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; iimagestore); + if ( load_presentation(p, files[i]) == 0 ) { + 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 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] []\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-old/colloquium.h b/src-old/colloquium.h new file mode 100644 index 0000000..89f600f --- /dev/null +++ b/src-old/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-old/debugger.c b/src-old/debugger.c new file mode 100644 index 0000000..f15478a --- /dev/null +++ b/src-old/debugger.c @@ -0,0 +1,229 @@ +/* + * print.c + * + * Copyright © 2017-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 "presentation.h" +#include "narrative_window.h" +#include "render.h" +#include "frame.h" +#include "utils.h" + + +#define MAX_DEBUG_RUNS (1024) + +struct debugwindow +{ + GtkWidget *window; + GtkWidget *drawingarea; + struct frame *fr; + guint timeout; +}; + + +static void plot_hr(cairo_t *cr, double *ypos, double width) +{ + cairo_move_to(cr, 10.0, *ypos+5.5); + cairo_line_to(cr, width-20.0, *ypos+5.5); + cairo_set_source_rgba(cr, 0.0, 0.0, 0.0, 1.0); + cairo_set_line_width(cr, 1.0); + cairo_stroke(cr); + *ypos += 10.0; +} + + +static void plot_text(cairo_t *cr, double *ypos, PangoFontDescription *fontdesc, + const char *tmp) +{ + PangoLayout *layout; + PangoRectangle ext; + + cairo_move_to(cr, 10.0, *ypos); + + layout = pango_cairo_create_layout(cr); + pango_layout_set_text(layout, tmp, -1); + pango_layout_set_font_description(layout, fontdesc); + + pango_layout_get_extents(layout, NULL, &ext); + *ypos += ext.height/PANGO_SCALE; + + cairo_set_source_rgba(cr, 0.0, 0.0, 0.0, 1.0); + pango_cairo_show_layout(cr, layout); + + g_object_unref(layout); +} + + +static const char *str_type(enum para_type t) +{ + switch ( t ) { + + /* Text paragraph */ + case PARA_TYPE_TEXT : return "text"; + + /* Callback paragraph */ + case PARA_TYPE_CALLBACK : return "callback"; + + /* Unknown paragraph type */ + default : return "unknown"; + } +} + +static void debug_text_para(Paragraph *para, cairo_t *cr, double *ypos, + PangoFontDescription *fontdesc) +{ + int i, nrun; + char tmp[256]; + + nrun = para_debug_num_runs(para); + /* How many text runs */ + snprintf(tmp, 255, " %i runs", nrun); + plot_text(cr, ypos, fontdesc, tmp); + + for ( i=0; ifr == NULL ) return FALSE; + + fontdesc = pango_font_description_new(); + pango_font_description_set_family_static(fontdesc, "Serif"); + pango_font_description_set_style(fontdesc, PANGO_STYLE_ITALIC); + pango_font_description_set_absolute_size(fontdesc, 15*PANGO_SCALE); + + snprintf(tmp, 255, "Frame %p has %i paragraphs", dbgw->fr, dbgw->fr->n_paras); + plot_text(cr, &ypos, fontdesc, tmp); + + for ( i=0; ifr->n_paras; i++ ) { + + enum para_type t = para_type(dbgw->fr->paras[i]); + SCBlock *scblock = para_scblock(dbgw->fr->paras[i]); + + plot_hr(cr, &ypos, width); + snprintf(tmp, 255, "Paragraph %i: type %s", i, str_type(t)); + plot_text(cr, &ypos, fontdesc, tmp); + + snprintf(tmp, 255, "SCBlock %p", scblock); + plot_text(cr, &ypos, fontdesc, tmp); + + if ( t == PARA_TYPE_TEXT ) { + debug_text_para(dbgw->fr->paras[i], cr, &ypos, fontdesc); + } + + plot_text(cr, &ypos, fontdesc, ""); + + } + + pango_font_description_free(fontdesc); + + return FALSE; +} + + +static gboolean queue_redraw(void *vp) +{ + struct debugwindow *dbgw = vp; + gint w, h; + w = gtk_widget_get_allocated_width(GTK_WIDGET(dbgw->drawingarea)); + h = gtk_widget_get_allocated_height(GTK_WIDGET(dbgw->drawingarea)); + gtk_widget_queue_draw_area(GTK_WIDGET(dbgw->drawingarea), 0, 0, w, h); + return TRUE; +} + + +static gboolean close_sig(GtkWidget *widget, GdkEvent *event, struct debugwindow *dbgw) +{ + g_source_remove(dbgw->timeout); + free(dbgw); + return FALSE; +} + + +void open_debugger(struct frame *fr) +{ + struct debugwindow *dbgw; + GtkWidget *scroll; + + if ( fr == NULL ) return; + + dbgw = calloc(1, sizeof(struct debugwindow)); + if ( dbgw == NULL ) return; + + dbgw->fr = fr; + + dbgw->window = gtk_window_new(GTK_WINDOW_TOPLEVEL); + gtk_window_set_role(GTK_WINDOW(dbgw->window), "debugger"); + gtk_window_set_title(GTK_WINDOW(dbgw->window), "Colloquium debugger"); + + scroll = gtk_scrolled_window_new(NULL, NULL); + gtk_container_add(GTK_CONTAINER(dbgw->window), scroll); + + dbgw->drawingarea = gtk_drawing_area_new(); + gtk_container_add(GTK_CONTAINER(scroll), dbgw->drawingarea); + gtk_widget_set_size_request(dbgw->drawingarea, 100, 8000); + + g_signal_connect(G_OBJECT(dbgw->drawingarea), "draw", + G_CALLBACK(dbg_draw_sig), dbgw); + + g_signal_connect(G_OBJECT(dbgw->window), "delete-event", + G_CALLBACK(close_sig), dbgw); + + dbgw->timeout = g_timeout_add(1000, queue_redraw, dbgw); + + gtk_widget_show_all(dbgw->window); +} diff --git a/src-old/debugger.h b/src-old/debugger.h new file mode 100644 index 0000000..d6191bf --- /dev/null +++ b/src-old/debugger.h @@ -0,0 +1,32 @@ +/* + * debugger.h + * + * Copyright © 2017-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 DEBUGGER_H +#define DEBUGGER_H + +#ifdef HAVE_CONFIG_H +#include +#endif + +extern void open_debugger(struct frame *fr); + +#endif /* DEBUGGER_H */ diff --git a/src-old/frame.c b/src-old/frame.c new file mode 100644 index 0000000..08b8fd9 --- /dev/null +++ b/src-old/frame.c @@ -0,0 +1,1820 @@ +/* + * frame.c + * + * Copyright © 2013-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 + +#include "sc_parse.h" +#include "frame.h" +#include "imagestore.h" +#include "utils.h" + +struct text_run +{ + SCBlock *scblock; + char *real_text; /* Usually NULL */ + PangoFontDescription *fontdesc; + double col[4]; +}; + +struct _paragraph +{ + enum para_type type; + double height; + float space[4]; + SCBlock *newline_at_end; + int empty; + + /* For PARA_TYPE_TEXT */ + int n_runs; + struct text_run *runs; + PangoLayout *layout; + PangoAlignment alignment; + + /* For anything other than PARA_TYPE_TEXT + * (for text paragraphs, these things are in the runs) */ + SCBlock *scblock; + + /* For PARA_TYPE_IMAGE */ + char *filename; + double image_w; + double image_h; + int image_real_w; + int image_real_h; + + /* For PARA_TYPE_CALLBACK */ + double cb_w; + double cb_h; + SCCallbackDrawFunc draw_func; + SCCallbackClickFunc click_func; + void *bvp; + void *vp; +}; + + +/* Returns the height of the paragraph including all spacing, padding etc */ +double paragraph_height(Paragraph *para) +{ + return para->height + para->space[2] + para->space[3]; +} + + +static int alloc_ro(struct frame *fr) +{ + struct frame **new_ro; + + new_ro = realloc(fr->children, + fr->max_children*sizeof(struct frame *)); + if ( new_ro == NULL ) return 1; + + fr->children = new_ro; + + return 0; +} + + +struct frame *frame_new() +{ + struct frame *n; + + n = calloc(1, sizeof(struct frame)); + if ( n == NULL ) return NULL; + + n->children = NULL; + n->max_children = 32; + if ( alloc_ro(n) ) { + fprintf(stderr, "Couldn't allocate children\n"); + free(n); + return NULL; + } + n->num_children = 0; + + n->scblocks = NULL; + n->n_paras = 0; + n->paras = NULL; + + return n; +} + + +static void free_paragraph(Paragraph *para) +{ + int i; + + for ( i=0; in_runs; i++ ) { + pango_font_description_free(para->runs[i].fontdesc); + free(para->runs[i].real_text); /* free(NULL) is OK */ + } + free(para->runs); + if ( para->layout != NULL ) g_object_unref(para->layout); + free(para); +} + + +void frame_free(struct frame *fr) +{ + int i; + + if ( fr == NULL ) return; + + /* Free paragraphs */ + if ( fr->paras != NULL ) { + for ( i=0; in_paras; i++ ) { + free_paragraph(fr->paras[i]); + } + free(fr->paras); + } + + /* Free all children */ + for ( i=0; inum_children; i++ ) { + frame_free(fr->children[i]); + } + free(fr->children); + + free(fr); +} + + +struct frame *add_subframe(struct frame *fr) +{ + struct frame *n; + + n = frame_new(); + if ( n == NULL ) return NULL; + + if ( fr->num_children == fr->max_children ) { + fr->max_children += 32; + if ( alloc_ro(fr) ) return NULL; + } + + fr->children[fr->num_children++] = n; + + return n; +} + + +void show_frame_hierarchy(struct frame *fr, const char *t) +{ + int i; + char tn[1024]; + + strcpy(tn, t); + strcat(tn, " "); + + printf("%s%p (%.2f x %.2f)\n", t, fr, fr->w, fr->h); + + for ( i=0; inum_children; i++ ) { + show_frame_hierarchy(fr->children[i], tn); + } + +} + + +struct frame *find_frame_with_scblocks(struct frame *fr, SCBlock *scblocks) +{ + int i; + + if ( fr->scblocks == scblocks ) return fr; + + for ( i=0; inum_children; i++ ) { + struct frame *tt; + tt = find_frame_with_scblocks(fr->children[i], scblocks); + if ( tt != NULL ) return tt; + } + + return NULL; +} + + +static const char *text_for_run(const struct text_run *run) +{ + if ( run == NULL ) { + fprintf(stderr, "NULL run passed to text_for_run\n"); + return 0; + } + + if ( run->scblock == NULL ) { + fprintf(stderr, "NULL scblock in text_for_run\n"); + return 0; + } + + if ( run->real_text != NULL ) { + return run->real_text; + } + + return sc_block_contents(run->scblock); +} + + +static size_t run_text_len(const struct text_run *run) +{ + if ( run == NULL ) { + fprintf(stderr, "NULL run passed to run_text_len\n"); + return 0; + } + + if ( run->scblock == NULL ) { + fprintf(stderr, "NULL scblock in run_text_len\n"); + return 0; + } + + if ( run->real_text != NULL ) { + return strlen(run->real_text); + } + + if ( sc_block_contents(run->scblock) == NULL ) { + fprintf(stderr, "NULL scblock contents in run_text_len\n"); + return 0; + } + + return strlen(sc_block_contents(run->scblock)); +} + + +void wrap_paragraph(Paragraph *para, PangoContext *pc, double w, + size_t sel_start, size_t sel_end) +{ + size_t total_len = 0; + int i; + char *text; + PangoAttrList *attrs; + PangoRectangle rect; + size_t pos = 0; + + w -= para->space[0] + para->space[1]; + + if ( para->type == PARA_TYPE_IMAGE ) { + if ( para->image_w < 0.0 ) { + para->image_w = w; + para->image_h = w*((float)para->image_real_h/para->image_real_w); + } + para->height = para->image_h; + return; + } + + if ( para->type != PARA_TYPE_TEXT ) return; + + for ( i=0; in_runs; i++ ) { + total_len += run_text_len(¶->runs[i]); + } + + /* Allocate the complete text */ + text = malloc(total_len+1); + if ( text == NULL ) { + fprintf(stderr, "Couldn't allocate combined text (%lli)\n", + (long long int)total_len); + return; + } + + /* Allocate the attributes */ + attrs = pango_attr_list_new(); + + /* Put all of the text together */ + text[0] = '\0'; + for ( i=0; in_runs; i++ ) { + + PangoAttribute *attr; + const char *run_text; + size_t run_len; + guint16 r, g, b; + + run_text = text_for_run(¶->runs[i]); + run_len = strlen(run_text); + + attr = pango_attr_font_desc_new(para->runs[i].fontdesc); + attr->start_index = pos; + attr->end_index = pos + run_len; + pango_attr_list_insert(attrs, attr); + + r = para->runs[i].col[0] * 65535; + g = para->runs[i].col[1] * 65535; + b = para->runs[i].col[2] * 65535; + attr = pango_attr_foreground_new(r, g, b); + attr->start_index = pos; + attr->end_index = pos + run_len; + pango_attr_list_insert(attrs, attr); + + pos += run_len; + strncat(text, run_text, run_len); + + } + + /* Add attributes for selected text */ + if ( sel_start > 0 || sel_end > 0 ) { + PangoAttribute *attr; + attr = pango_attr_background_new(42919, 58853, 65535); + attr->start_index = sel_start; + attr->end_index = sel_end; + pango_attr_list_insert(attrs, attr); + } + + if ( para->layout == NULL ) { + para->layout = pango_layout_new(pc); + } + pango_layout_set_width(para->layout, pango_units_from_double(w)); + pango_layout_set_text(para->layout, text, total_len); + pango_layout_set_alignment(para->layout, para->alignment); + pango_layout_set_attributes(para->layout, attrs); + free(text); + pango_attr_list_unref(attrs); + + pango_layout_get_extents(para->layout, NULL, &rect); + para->height = pango_units_to_double(rect.height); +} + +static SCBlock *get_newline_at_end(Paragraph *para) +{ + return para->newline_at_end; +} + + +SCBlock *para_debug_get_newline_at_end(Paragraph *para) +{ + return get_newline_at_end(para); +} + + +void set_newline_at_end(Paragraph *para, SCBlock *bl) +{ + para->newline_at_end = bl; +} + + +void add_run(Paragraph *para, SCBlock *scblock, + PangoFontDescription *fdesc, double col[4], const char *real_text) +{ + struct text_run *runs_new; + + runs_new = realloc(para->runs, + (para->n_runs+1)*sizeof(struct text_run)); + if ( runs_new == NULL ) { + fprintf(stderr, "Failed to add run.\n"); + return; + } + + para->runs = runs_new; + para->runs[para->n_runs].scblock = scblock; + if ( real_text != NULL ) { + para->runs[para->n_runs].real_text = strdup(real_text); + } else { + para->runs[para->n_runs].real_text = NULL; + } + para->runs[para->n_runs].fontdesc = pango_font_description_copy(fdesc); + para->runs[para->n_runs].col[0] = col[0]; + para->runs[para->n_runs].col[1] = col[1]; + para->runs[para->n_runs].col[2] = col[2]; + para->runs[para->n_runs].col[3] = col[3]; + para->empty = 0; + para->n_runs++; +} + + +Paragraph *create_paragraph(struct frame *fr, SCBlock *bl) +{ + Paragraph **paras_new; + Paragraph *pnew; + + paras_new = realloc(fr->paras, (fr->n_paras+1)*sizeof(Paragraph *)); + if ( paras_new == NULL ) return NULL; + + pnew = calloc(1, sizeof(struct _paragraph)); + if ( pnew == NULL ) return NULL; + + fr->paras = paras_new; + fr->paras[fr->n_paras++] = pnew; + + /* For now, assume the paragraph is going to be for text. + * However, this can easily be changed */ + pnew->type = PARA_TYPE_TEXT; + pnew->scblock = bl; + pnew->n_runs = 0; + pnew->runs = NULL; + pnew->layout = NULL; + pnew->height = 0.0; + pnew->alignment = PANGO_ALIGN_LEFT; + pnew->empty = 1; + + return pnew; +} + + +/* Create a new paragraph in 'fr' just after paragraph 'pos' */ +Paragraph *insert_paragraph(struct frame *fr, int pos) +{ + Paragraph **paras_new; + Paragraph *pnew; + int i; + + if ( pos >= fr->n_paras ) { + fprintf(stderr, "insert_paragraph(): pos too high!\n"); + return NULL; + } + + paras_new = realloc(fr->paras, (fr->n_paras+1)*sizeof(Paragraph *)); + if ( paras_new == NULL ) return NULL; + + pnew = calloc(1, sizeof(struct _paragraph)); + if ( pnew == NULL ) return NULL; + + fr->paras = paras_new; + fr->n_paras++; + + for ( i=fr->n_paras-1; i>pos; i-- ) { + fr->paras[i] = fr->paras[i-1]; + } + fr->paras[pos+1] = pnew; + + return pnew; +} + + +Paragraph *add_callback_para(struct frame *fr, SCBlock *bl, double w, double h, + SCCallbackDrawFunc draw_func, + SCCallbackClickFunc click_func, void *bvp, + void *vp) +{ + Paragraph *pnew; + + if ( (fr->n_paras > 0) && (fr->paras[fr->n_paras-1]->empty) ) { + pnew = fr->paras[fr->n_paras-1]; + } else { + pnew = create_paragraph(fr, bl); + if ( pnew == NULL ) { + fprintf(stderr, "Failed to add callback paragraph\n"); + return NULL; + } + } + + pnew->type = PARA_TYPE_CALLBACK; + pnew->scblock = bl; + pnew->cb_w = w; + pnew->cb_h = h; + pnew->draw_func = draw_func; + pnew->click_func = click_func; + pnew->bvp = bvp; + pnew->vp = vp; + pnew->height = h; + pnew->empty = 0; + + return pnew; +} + + +void add_image_para(struct frame *fr, SCBlock *scblock, + const char *filename, + ImageStore *is, double w, double h, int editable) +{ + Paragraph *pnew; + int wi, hi; + + if ( is == NULL ) { + fprintf(stderr, "Adding image without ImageStore!\n"); + return; + } + + if ( (fr->n_paras > 0) && (fr->paras[fr->n_paras-1]->empty) ) { + pnew = fr->paras[fr->n_paras-1]; + } else { + pnew = create_paragraph(fr, scblock); + if ( pnew == NULL ) { + fprintf(stderr, "Failed to add image paragraph\n"); + return; + } + } + + if ( imagestore_get_size(is, filename, &wi, &hi) ) { + fprintf(stderr, _("Couldn't determine size of image '%s'\n"), + filename); + wi = 100; + hi = 100; + } + + pnew->type = PARA_TYPE_IMAGE; + pnew->scblock = scblock; + pnew->filename = strdup(filename); + pnew->image_w = w; + pnew->image_h = h; + pnew->image_real_w = wi; + pnew->image_real_h = hi; + pnew->height = h; + pnew->space[0] = 0.0; + pnew->space[1] = 0.0; + pnew->space[2] = 0.0; + pnew->space[3] = 0.0; + pnew->empty = 0; +} + + +double total_height(struct frame *fr) +{ + int i; + double t = 0.0; + for ( i=0; in_paras; i++ ) { + t += paragraph_height(fr->paras[i]); + } + return t; +} + + +Paragraph *last_para(struct frame *fr) +{ + if ( fr == NULL ) return NULL; + if ( fr->paras == NULL ) return NULL; + return fr->paras[fr->n_paras-1]; +} + + +static void render_from_surf(cairo_surface_t *surf, cairo_t *cr, + double w, double h, int border) +{ + double x, y; + int sw, sh; + + x = 0.0; y = 0.0; + cairo_user_to_device(cr, &x, &y); + x = rint(x); y = rint(y); + cairo_device_to_user(cr, &x, &y); + + sw = cairo_image_surface_get_width(surf); + sh = cairo_image_surface_get_height(surf); + + cairo_save(cr); + cairo_scale(cr, w/sw, h/sh); + cairo_new_path(cr); + cairo_rectangle(cr, x, y, sw, sh); + cairo_set_source_surface(cr, surf, 0.0, 0.0); + cairo_pattern_t *patt = cairo_get_source(cr); + cairo_pattern_set_extend(patt, CAIRO_EXTEND_PAD); + cairo_pattern_set_filter(patt, CAIRO_FILTER_BEST); + cairo_fill(cr); + cairo_restore(cr); + + if ( border ) { + cairo_new_path(cr); + cairo_rectangle(cr, x+0.5, y+0.5, w, h); + cairo_set_source_rgb(cr, 0.0, 0.0, 0.0); + cairo_set_line_width(cr, 1.0); + cairo_stroke(cr); + } +} + + +void render_paragraph(cairo_t *cr, Paragraph *para, ImageStore *is) +{ + cairo_surface_t *surf; + cairo_surface_type_t type; + double w, h; + + cairo_translate(cr, para->space[0], para->space[2]); + + type = cairo_surface_get_type(cairo_get_target(cr)); + + switch ( para->type ) { + + case PARA_TYPE_TEXT : + cairo_set_source_rgba(cr, 0.0, 0.0, 0.0, 1.0); + pango_cairo_update_layout(cr, para->layout); + pango_cairo_show_layout(cr, para->layout); + cairo_fill(cr); + break; + + case PARA_TYPE_IMAGE : + w = para->image_w; + h = para->image_h; + cairo_user_to_device_distance(cr, &w, &h); + surf = lookup_image(is, para->filename, w); + if ( surf != NULL ) { + render_from_surf(surf, cr, para->image_w, para->image_h, 0); + } else { + printf("surf = NULL!\n"); + } + break; + + case PARA_TYPE_CALLBACK : + w = para->cb_w; + h = para->cb_h; + cairo_user_to_device_distance(cr, &w, &h); + if ( type == CAIRO_SURFACE_TYPE_PDF ) { + w *= 6; h *= 6; + } + surf = para->draw_func(w, h, + para->bvp, para->vp); + render_from_surf(surf, cr, para->cb_w, para->cb_h, 1); + cairo_surface_destroy(surf); /* FIXME: Cache like crazy */ + break; + + } +} + + +static size_t end_offset_of_para(struct frame *fr, int pn) +{ + int i; + size_t total = 0; + for ( i=0; iparas[pn]->n_runs; i++ ) { + total += run_text_len(&fr->paras[pn]->runs[i]); + } + return total; +} + + +/* Local x,y in paragraph -> text offset */ +static size_t text_para_pos(Paragraph *para, double x, double y, int *ptrail) +{ + int idx; + pango_layout_xy_to_index(para->layout, pango_units_from_double(x), + pango_units_from_double(y), &idx, ptrail); + return idx; +} + + +void show_edit_pos(struct edit_pos a) +{ + printf("para %i, pos %li, trail %i\n", a.para, (long int)a.pos, a.trail); +} + + +int positions_equal(struct edit_pos a, struct edit_pos b) +{ + if ( a.para != b.para ) return 0; + if ( a.pos != b.pos ) return 0; + if ( a.trail != b.trail ) return 0; + return 1; +} + + +void sort_positions(struct edit_pos *a, struct edit_pos *b) +{ + if ( a->para > b->para ) { + size_t tpos; + int tpara, ttrail; + tpara = b->para; tpos = b->pos; ttrail = b->trail; + b->para = a->para; b->pos = a->pos; b->trail = a->trail; + a->para = tpara; a->pos = tpos; a->trail = ttrail; + } + + if ( (a->para == b->para) && (a->pos > b->pos) ) + { + size_t tpos = b->pos; + int ttrail = b->trail; + b->pos = a->pos; b->trail = a->trail; + a->pos = tpos; a->trail = ttrail; + } +} + + +static PangoFontDescription *last_font_desc_and_col(struct frame *fr, int pn, double *col) +{ + int i; + + for ( i=pn-1; i>=0; i-- ) { + if ( fr->paras[i]->type != PARA_TYPE_TEXT ) continue; + if ( fr->paras[i]->n_runs == 0 ) continue; + col[0] = fr->paras[i]->runs[fr->paras[i]->n_runs-1].col[0]; + col[1] = fr->paras[i]->runs[fr->paras[i]->n_runs-1].col[1]; + col[2] = fr->paras[i]->runs[fr->paras[i]->n_runs-1].col[2]; + col[3] = fr->paras[i]->runs[fr->paras[i]->n_runs-1].col[3]; + return fr->paras[i]->runs[fr->paras[i]->n_runs-1].fontdesc; + } + + /* No previous text at all, so use the default text style + * (which is valid for new text in the frame, so this is OK) */ + col[0] = fr->col[0]; + col[1] = fr->col[1]; + col[2] = fr->col[2]; + col[3] = fr->col[3]; + return fr->fontdesc; +} + + +void ensure_run(struct frame *fr, struct edit_pos cpos) +{ + SCBlock *bl; + Paragraph *para; + PangoFontDescription *fontdesc; + double col[4]; + + para = fr->paras[cpos.para]; + + if ( para->n_runs > 0 ) return; + + if ( para->type != PARA_TYPE_TEXT ) return; + + if ( para->scblock != NULL ) { + + bl = sc_block_prepend(para->scblock, fr->scblocks); + if ( bl == NULL ) { + fprintf(stderr, "Couldn't prepend block\n"); + return; + } + sc_block_set_contents(bl, strdup("")); + + } else { + + /* If the paragraph's SCBlock is NULL, it means this paragraph + * is right at the end of the document. The last thing in the + * document is something like \newpara. */ + bl = sc_block_append_inside(fr->scblocks, NULL, NULL, strdup("")); + + } + + para->scblock = bl; + + fontdesc = last_font_desc_and_col(fr, cpos.para, col); + add_run(para, bl, fontdesc, col, NULL); + wrap_paragraph(para, NULL, fr->w - fr->pad_l - fr->pad_r, 0, 0); +} + + +int find_cursor(struct frame *fr, double x, double y, struct edit_pos *pos) +{ + double pad; + int i; + + if ( fr == NULL ) { + fprintf(stderr, "Cursor frame is NULL.\n"); + return 1; + } + + pad = fr->pad_t; + + for ( i=0; in_paras; i++ ) { + double npos = pad + paragraph_height(fr->paras[i]); + if ( npos > y ) { + pos->para = i; + if ( fr->paras[i]->type == PARA_TYPE_TEXT ) { + pos->pos = text_para_pos(fr->paras[i], + x - fr->pad_l - fr->paras[i]->space[0], + y - pad - fr->paras[i]->space[2], + &pos->trail); + } else { + pos->pos = 0; + } + return 0; + } + pad = npos; + } + + if ( fr->n_paras == 0 ) { + printf("No paragraphs in frame.\n"); + return 1; + } + + /* Pretend it's in the last paragraph */ + pad -= fr->paras[fr->n_paras-1]->height; + pos->para = fr->n_paras - 1; + pos->pos = text_para_pos(fr->paras[fr->n_paras-1], + x - fr->pad_l, y - pad, &pos->trail); + return 0; +} + + +int get_para_highlight(struct frame *fr, int cursor_para, + double *cx, double *cy, double *cw, double *ch) +{ + Paragraph *para; + int i; + double py = 0.0; + + if ( fr == NULL ) { + fprintf(stderr, "Cursor frame is NULL.\n"); + return 1; + } + + if ( cursor_para >= fr->n_paras ) { + fprintf(stderr, "Highlight paragraph number is too high!\n"); + return 1; + } + + para = fr->paras[cursor_para]; + for ( i=0; iparas[i]); + } + + *cx = fr->pad_l; + *cy = fr->pad_t + py; + *cw = fr->w - fr->pad_l - fr->pad_r; + *ch = paragraph_height(para); + return 0; +} + + +int get_cursor_pos(struct frame *fr, int cursor_para, int cursor_pos, + double *cx, double *cy, double *ch) +{ + Paragraph *para; + PangoRectangle rect; + int i; + double py = 0.0; + + if ( fr == NULL ) { + fprintf(stderr, "Cursor frame is NULL.\n"); + return 1; + } + + if ( cursor_para >= fr->n_paras ) { + fprintf(stderr, "Cursor paragraph number is too high!\n"); + return 1; + } + + para = fr->paras[cursor_para]; + for ( i=0; iparas[i]); + } + + if ( para->type != PARA_TYPE_TEXT ) { + return 1; + } + + pango_layout_get_cursor_pos(para->layout, cursor_pos, &rect, NULL); + + *cx = pango_units_to_double(rect.x) + fr->pad_l + para->space[0]; + *cy = pango_units_to_double(rect.y) + fr->pad_t + py + para->space[2]; + *ch = pango_units_to_double(rect.height); + return 0; +} + + +//void cursor_moveh(struct frame *fr, int *cpara, size_t *cpos, int *ctrail, +void cursor_moveh(struct frame *fr, struct edit_pos *cp, signed int dir) +{ + Paragraph *para = fr->paras[cp->para]; + int np = cp->pos; + + pango_layout_move_cursor_visually(para->layout, 1, cp->pos, cp->trail, + dir, &np, &cp->trail); + if ( np == -1 ) { + if ( cp->para > 0 ) { + size_t end_offs; + cp->para--; + end_offs = end_offset_of_para(fr, cp->para); + if ( end_offs > 0 ) { + cp->pos = end_offs - 1; + cp->trail = 1; + } else { + /* Jumping into an empty paragraph */ + cp->pos = 0; + cp->trail = 0; + } + return; + } else { + /* Can't move any further */ + return; + } + } + + if ( np == G_MAXINT ) { + if ( cp->para < fr->n_paras-1 ) { + cp->para++; + cp->pos = 0; + cp->trail = 0; + return; + } else { + /* Can't move any further */ + return; + } + } + + cp->pos = np; +} + + +void check_callback_click(struct frame *fr, int para) +{ + Paragraph *p = fr->paras[para]; + if ( p->type == PARA_TYPE_CALLBACK ) { + p->click_func(0.0, 0.0, p->bvp, p->vp); + } +} + + +static int get_paragraph_offset(Paragraph *para, int nrun) +{ + int i; + size_t t = 0; + + for ( i=0; iruns[i]; + t += run_text_len(run); + } + return t; +} + + +static int which_run(Paragraph *para, size_t offs) +{ + int i; + size_t t = 0; + + for ( i=0; in_runs; i++ ) { + struct text_run *run = ¶->runs[i]; + t += run_text_len(run); + if ( t > offs ) return i; + } + + /* Maybe offs points exactly to the end of the last run? */ + if ( t == offs ) return para->n_runs-1; + + return para->n_runs; +} + + +size_t pos_trail_to_offset(Paragraph *para, size_t offs, int trail) +{ + glong char_offs; + size_t run_offs; + const char *run_text; + struct text_run *run; + int nrun; + char *ptr; + size_t para_offset_of_run; + + nrun = which_run(para, offs); + + if ( nrun == para->n_runs ) { + fprintf(stderr, "pos_trail_to_offset: Offset too high\n"); + return 0; + } + + if ( para->n_runs == 0 ) { + return 0; + } + + run = ¶->runs[nrun]; + + if ( para->type != PARA_TYPE_TEXT ) return 0; + + if ( run == NULL ) { + fprintf(stderr, "pos_trail_to_offset: No run\n"); + return 0; + } + + if ( run->scblock == NULL ) { + fprintf(stderr, "pos_trail_to_offset: SCBlock = NULL?\n"); + return 0; + } + + /* Get the text for the run */ + run_text = text_for_run(run); + if ( run_text == NULL ) { + fprintf(stderr, "pos_trail_to_offset: No contents " + "(%p name=%s, options=%s)\n", + run->scblock, sc_block_name(run->scblock), + sc_block_options(run->scblock)); + return 0; + } + + + /* Turn the paragraph offset into a run offset */ + para_offset_of_run = get_paragraph_offset(para, nrun); + run_offs = offs - para_offset_of_run; + + char_offs = g_utf8_pointer_to_offset(run_text, run_text+run_offs); + char_offs += trail; + + if ( char_offs > g_utf8_strlen(run_text, -1) ) { + printf("Offset outside string! '%s'\n" + "char_offs %li offs %li len %li\n", + run_text, (long int)char_offs, (long int)offs, + (long int)g_utf8_strlen(run_text, -1)); + } + + ptr = g_utf8_offset_to_pointer(run_text, char_offs); + return ptr - run_text + para_offset_of_run; +} + + +int position_editable(struct frame *fr, struct edit_pos cp) +{ + Paragraph *para; + int run; + size_t paraoffs; + + if ( fr == NULL ) { + fprintf(stderr, "Frame is NULL.\n"); + return 0; + } + + if ( cp.para >= fr->n_paras ) { + fprintf(stderr, "Paragraph number is too high!\n"); + return 0; + } + + para = fr->paras[cp.para]; + + if ( para->type != PARA_TYPE_TEXT ) { + fprintf(stderr, "Paragraph is not text.\n"); + return 0; + } + + paraoffs = pos_trail_to_offset(para, cp.pos, cp.trail); + run = which_run(para, paraoffs); + if ( run == para->n_runs ) { + fprintf(stderr, "Couldn't find run!\n"); + return 0; + } + + if ( para->runs[run].real_text != NULL ) return 0; + + return 1; +} + + +void insert_text_in_paragraph(Paragraph *para, size_t offs, const char *t) +{ + int nrun; + + /* Find which run we are in */ + nrun = which_run(para, offs); + if ( nrun == para->n_runs ) { + fprintf(stderr, "Couldn't find run to insert into.\n"); + return; + } + + if ( para->n_runs == 0 ) { + printf("No runs in paragraph?\n"); + } else { + struct text_run *run; + size_t run_offs; + run = ¶->runs[nrun]; + run_offs = offs - get_paragraph_offset(para, nrun); + sc_insert_text(run->scblock, run_offs, t); + } +} + + +static SCBlock *pos_to_scblock(struct frame *fr, struct edit_pos p, + enum para_type *type) +{ + int run; + size_t paraoffs; + Paragraph *para; + + para = fr->paras[p.para]; + if ( type != NULL ) { + *type = para->type; + } + + if ( para->type != PARA_TYPE_TEXT ) { + return para->scblock; + } + + paraoffs = pos_trail_to_offset(para, p.pos, p.trail); + + run = which_run(para, paraoffs); + assert(run < para->n_runs); + + return para->runs[run].scblock; +} + + +static size_t pos_to_offset(struct frame *fr, struct edit_pos p) +{ + int run; + size_t paraoffs; + Paragraph *para; + + para = fr->paras[p.para]; + if ( para->type != PARA_TYPE_TEXT ) { + return 0; + } + + /* Offset of this position into the paragraph */ + paraoffs = pos_trail_to_offset(para, p.pos, p.trail); + + run = which_run(para, paraoffs); + assert(run < para->n_runs); + + /* Offset of this position into the run + * (and therefore into the SCBlock) */ + return paraoffs - get_paragraph_offset(para, run); +} + + +static int pos_to_run_number(struct frame *fr, struct edit_pos p) +{ + int run; + size_t paraoffs; + Paragraph *para; + + para = fr->paras[p.para]; + if ( para->type != PARA_TYPE_TEXT ) { + return 0; + } + + paraoffs = pos_trail_to_offset(para, p.pos, p.trail); + + run = which_run(para, paraoffs); + assert(run < para->n_runs); + + return run; +} + + +static void delete_run(Paragraph *para, int nrun) +{ + printf("deleting run %i of %i from para %p\n", nrun, para->n_runs, para); + memmove(¶->runs[nrun], ¶->runs[nrun+1], + (para->n_runs-nrun-1)*sizeof(struct text_run)); + para->n_runs--; +} + + +static Paragraph *scan_runs_for_scblock(struct frame *fr, int pn1, int pn2, + SCBlock *bl, int *run) +{ + int i; + + for ( i=pn1; i<=pn2; i++ ) { + + int j; + + /* Non-text paragraph - just one thing to check */ + if ( fr->paras[i]->scblock == bl ) { + *run = 0; + return fr->paras[i]; + } + + /* Check all runs */ + for ( j=0; jparas[i]->n_runs; j++ ) { + if ( fr->paras[i]->runs[j].scblock == bl ) { + *run = j; + return fr->paras[i]; + } + } + } + return NULL; +} + + +static Paragraph *find_run_for_scblock_next(struct frame *fr, int pn1, int pn2, + SCBlock *bl, int *run) +{ + if ( sc_block_child(bl) != NULL ) { + Paragraph *para; + para = find_run_for_scblock_next(fr, pn1, pn2, + sc_block_child(bl), run); + if ( para != NULL ) return para; + } + + do { + Paragraph *para; + para = scan_runs_for_scblock(fr, pn1, pn2, bl, run); + if ( para != NULL ) return para; + bl = sc_block_next(bl); + } while ( bl != NULL ); + + return NULL; +} + + +/* Find the run which contains the text from "bl", + * taking into account that it might be a child block, for example: + * {some text} + * \italic <---- bl points here + * {more text} <---- but this block is referenced by the run + * {final text} + */ +static Paragraph *find_run_for_scblock(struct frame *fr, int pn1, int pn2, + SCBlock *bl, int *run) +{ + Paragraph *para; + + show_sc_block(bl, "searching "); + para = scan_runs_for_scblock(fr, pn1, pn2, bl, run); + if ( para != NULL ) return para; + + if ( sc_block_child(bl) != NULL ) { + para = find_run_for_scblock_next(fr, pn1, pn2, sc_block_child(bl), run); + if ( para != NULL ) return para; + } + + return NULL; +} + + +static int paragraph_number(struct frame *fr, Paragraph *p, int *err) +{ + int i; + for ( i=0; in_paras; i++ ) { + if ( fr->paras[i] == p ) return i; + } + fprintf(stderr, "Couldn't find paragraph %p\n", p); + *err = 1; + return 0; +} + + +static int find_para(struct frame *fr, Paragraph *para) +{ + int i; + + for ( i=0; in_paras; i++ ) { + if ( fr->paras[i] == para ) return i; + } + + return fr->n_paras; +} + + +static void delete_paragraph(struct frame *fr, Paragraph *para, int *pnp) +{ + int pn = find_para(fr, para); + if ( pn == fr->n_paras ) { + fprintf(stderr, "Couldn't find paragraph to delete (%p)\n", para); + return; + } + + printf("deleting paragraph %i (%p)\n", pn, para); + + memmove(&fr->paras[pn], &fr->paras[pn+1], + (fr->n_paras-pn-1)*sizeof(Paragraph *)); + fr->n_paras--; + + if ( (pnp != NULL) && (*pnp > pn) ) { + (*pnp)--; + } +} + + +static void delete_run_for_scblock(struct frame *fr, + Paragraph *p1, Paragraph *p2, SCBlock *bl, + int *pnp) +{ + int pn1, pn2; + int err = 0; + Paragraph *para; + int run; + + pn1 = paragraph_number(fr, p1, &err); + pn2 = paragraph_number(fr, p2, &err); + if ( err ) return; + + para = find_run_for_scblock(fr, pn1, pn2, bl, &run); + if ( para == NULL ) { + fprintf(stderr, "Couldn't find block %p between paragraphs %p and %p\n", + bl, p1, p2); + return; + } + + if ( (run==0) && (para->scblock == bl ) ) { + delete_paragraph(fr, para, pnp); + } else { + delete_run(para, run); + } +} + + +static signed int merge_paragraph_runs(Paragraph *p1, Paragraph *p2) +{ + struct text_run *runs_new; + int i, spos; + + /* All the runs from p2 get added to p1 */ + runs_new = realloc(p1->runs, + (p1->n_runs+p2->n_runs)*sizeof(struct text_run)); + if ( runs_new == NULL ) { + fprintf(stderr, "Failed to allocate merged runs.\n"); + return -1; + } + p1->runs = runs_new; + + spos = p1->n_runs; + + /* The end of the united paragraph should now be the end of the + * second one */ + set_newline_at_end(p1, get_newline_at_end(p2)); + + for ( i=0; in_runs; i++ ) { + p1->runs[p1->n_runs] = p2->runs[i]; + p1->n_runs++; + } + free(p2->runs); + free(p2); + + return spos; +} + + +static void merge_paragraphs_by_newpara(struct frame *fr, SCBlock *np) +{ + int i; + Paragraph *p1; + Paragraph *p2; + + for ( i=0; in_paras-1; i++ ) { + if ( fr->paras[i]->newline_at_end == np ) { + + int j; + signed int spos; + + p1 = fr->paras[i]; + p2 = fr->paras[i+1]; + + printf("-------------------------------\n"); + show_para(p1); + printf("---x--------x------------------\n"); + show_para(p2); + spos = merge_paragraph_runs(p1, p2); + if ( spos < 0 ) { + fprintf(stderr, "Failed to merge paragraphs\n"); + return; + } + printf("-------------------------------\n"); + show_para(p1); + + for ( j=i+1; jn_paras-1; j++ ) { + fr->paras[j] = fr->paras[j+1]; + } + fr->n_paras--; + + return; + + } + } + + fprintf(stderr, "Couldn't find paragraphs to merge by newpara\n"); +} + + +static int find_block_inside(SCBlock *needle, SCBlock *bl) +{ + if ( needle == bl ) return 1; + + if ( sc_block_child(bl) != NULL ) { + if ( find_block_inside(needle, sc_block_child(bl)) ) return 1; + } + + if ( sc_block_next(bl) != NULL ) { + if ( find_block_inside(needle, sc_block_next(bl)) ) return 1; + } + + return 0; +} + + +/* Return true if "top" either IS "child", or contains "child" somewhere + * underneath, even if via a macro expansion */ +static int block_is_under(SCBlock *needle, SCBlock *top) +{ + if ( needle == top ) return 1; + + if ( sc_block_child(top) != NULL ) { + if ( find_block_inside(needle, sc_block_child(top)) ) return 1; + } + + /* Do not look at top->next here */ + + return 0; +} + + +void delete_text_from_frame(struct frame *fr, struct edit_pos p1, struct edit_pos p2, + double wrapw) +{ + int i; + SCBlock *p1scblock, *p2scblock; + enum para_type type1, type2; + size_t p2offs; + SCBlock *scblock; + int wrap_end; + + sort_positions(&p1, &p2); + + /* To make sure there are no nasty surprises ahead, run through the + * paragraphs we're about to touch, and make sure they all have at least + * an empty dummy run */ + for ( i=p1.para; i<=p2.para; i++ ) { + struct edit_pos ep; + ep.para = i; + ep.pos = 0; + ep.trail = 0; + ensure_run(fr, ep); + } + + if ( !position_editable(fr, p1) || !position_editable(fr, p2) ) { + fprintf(stderr, "Delete outside editable region\n"); + return; + } + + /* Find SC positions for start and end */ + p1scblock = pos_to_scblock(fr, p1, &type1); + p2scblock = pos_to_scblock(fr, p2, &type2); + p2offs = pos_to_offset(fr, p2); + wrap_end = p2.para; + + printf("SCBlocks %p to %p\n", p1scblock, p2scblock); + if ( p1scblock == NULL ) { + fprintf(stderr, "Starting block NULL. Not deleting.\n"); + return; + } + if ( p2scblock == NULL ) { + fprintf(stderr, "Ending block NULL. Not deleting.\n"); + return; + } + //show_sc_blocks(p1scblock); + + if ( (p1scblock == p2scblock) && (type1 == PARA_TYPE_TEXT) ) { + + size_t p1offs; + printf("Simple case, one SCBlock\n"); + + assert(type1 == type2); + + /* Remove the text and update the run length */ + p1offs = pos_to_offset(fr, p1); + scblock_delete_text(p1scblock, p1offs, p2offs); + + wrap_paragraph(fr->paras[p1.para], NULL, wrapw, 0, 0); + + return; + } + + /* Starting point for iteration over blocks in middle of range. + * Record this now, because p1scblock might be about to get deleted */ + scblock = sc_block_next(p1scblock); + + /* First SCBlock in range: delete whole thing or second half */ + printf("First block %p\n", p1scblock); + if ( type1 == PARA_TYPE_TEXT ) { + + size_t p1offs = pos_to_offset(fr, p1); + int p1run = pos_to_run_number(fr, p1); + printf(" offs %li\n", (long int)p1offs); + if ( p1offs != 0 ) { + printf("Partial delete\n"); + printf("contents '%s'\n", sc_block_contents(p1scblock)); + printf("from offs %li\n", (long int)p1offs); + scblock_delete_text(p1scblock, p1offs, -1); + } else { + printf("Deleting the whole text SCBlock\n"); + sc_block_delete(&fr->scblocks, p1scblock); + delete_run(fr->paras[p1.para], p1run); + } + + } else { + printf("Deleting the whole non-text SCBlock\n"); + sc_block_delete(&fr->scblocks, p1scblock); + } + + /* Delete all the complete SCBlocks in the middle of the range */ + if ( !block_is_under(p2scblock, scblock) ) { + do { + + SCBlock *next; + + /* For each SC block in middle of range: */ + printf("Deleting %p\n", scblock); + if ( scblock == NULL ) { + fprintf(stderr, "nothing?\n"); + break; + } + printf("name is '%s'\n", sc_block_name(scblock)); + + if ( (sc_block_name(scblock) != NULL) + && (strcmp(sc_block_name(scblock), "newpara") == 0) ) + { + /* Deleting newpara block, merge the paragraphs */ + merge_paragraphs_by_newpara(fr, scblock); + p2.para--; + } + + next = sc_block_next(scblock); + delete_run_for_scblock(fr, fr->paras[p1.para], + fr->paras[p2.para], scblock, + &p2.para); + sc_block_delete(&fr->scblocks, scblock); + + scblock = next; + + } while ( !block_is_under(p2scblock, scblock) ); + } + + /* Last SCBlock in range: delete whole thing or first half */ + printf("Last block %p (%s)\n", p2scblock, sc_block_name(p2scblock)); + if ( type2 == PARA_TYPE_TEXT ) { + size_t len; + printf(" offs %li\n", (long int)p2offs); + if ( sc_block_contents(p2scblock) != NULL ) { + len = strlen(sc_block_contents(p2scblock)); + } else { + len = 0; + } + printf(" len %li\n", (long int)len); + if ( (len > 0) && (p2offs == len) ) { + printf("Deleting the whole text SCBlock\n"); + printf("deleting block %p\n", p2scblock); + show_sc_block(p2scblock, ""); + sc_block_delete(&fr->scblocks, p2scblock); + delete_run_for_scblock(fr, fr->paras[p1.para], + fr->paras[p2.para], p2scblock, + NULL); + } else if ( p2offs > 0 ) { + printf("Partial delete\n"); + printf("contents '%s'\n", sc_block_contents(p2scblock)); + printf("up to offs %li\n", (long int)p2offs); + scblock_delete_text(p2scblock, 0, p2offs); + } /* else do nothing */ + } else { + printf("Deleting the whole non-text SCBlock\n"); + sc_block_delete(&fr->scblocks, p2scblock); + } + + /* If any paragraphs have been deleted, this will wrap too many + * paragraphs, but it doesn't matter as long as we don't wrap + * past the end of the frame's contents. */ + for ( i=p1.para; i<=wrap_end; i++ ) { + if ( i >= fr->n_paras ) break; + printf("Wrapping para %i (%p)\n", i, fr->paras[i]); + wrap_paragraph(fr->paras[i], NULL, wrapw, 0, 0); + } + printf("All done.\n"); +} + + +void show_para(Paragraph *p) +{ + printf("Paragraph %p\n", p); + + if ( p->type == PARA_TYPE_TEXT ) { + + int i; + + printf("%i runs:\n", p->n_runs); + for ( i=0; in_runs; i++ ) { + printf(" Run %2i: SCBlock %p %s '%s'\n", + i, p->runs[i].scblock, + pango_font_description_to_string(p->runs[i].fontdesc), + sc_block_contents(p->runs[i].scblock)); + } + + } else if ( p->type == PARA_TYPE_IMAGE ) { + printf(" Image: %s\n", p->filename); + } else { + printf(" Other paragraph type\n"); + } +} + + +static SCBlock *split_text_paragraph(struct frame *fr, int pn, size_t pos, + PangoContext *pc) +{ + Paragraph *pnew; + int i; + SCBlock *nnp; + size_t run_offs; + int run; + Paragraph *para = fr->paras[pn]; + struct text_run *rr; + + pnew = insert_paragraph(fr, pn); + if ( pnew == NULL ) { + fprintf(stderr, "Failed to insert paragraph\n"); + return NULL; + } + + /* Determine which run the cursor is in */ + run = which_run(para, pos); + + /* Create the new (second) paragraph */ + pnew->type = PARA_TYPE_TEXT; + pnew->n_runs = 0; + pnew->runs = NULL; + for ( i=0; i<4; i++ ) pnew->space[i] = para->space[i]; + + rr = ¶->runs[run]; + run_offs = pos - get_paragraph_offset(para, run); + printf("split at run %i\n", run); + + /* Easy case: splitting at a run boundary */ + if ( run_offs == run_text_len(rr) ) { + + /* Even easier case: splitting at the end of the paragraph */ + if ( run == para->n_runs-1 ) { + + SCBlock *np; + SCBlock *end; + + printf("Simple new para\n"); + + if ( get_newline_at_end(para) == NULL ) { + + /* The current paragraph doesn't have + * a \newpara yet */ + + np = sc_block_append(rr->scblock, + strdup("newpara"), NULL, + NULL, NULL); + set_newline_at_end(para, np); + + } else { + + SCBlock *op; + + /* If the current paragraph did have \newpara, + * then the new one needs one too */ + np = sc_block_append(rr->scblock, + strdup("newpara"), + NULL, NULL, NULL); + op = get_newline_at_end(para); + set_newline_at_end(para, np); + set_newline_at_end(pnew, op); + + + } + + /* Add an empty run + SCBlock to type into */ + end = sc_block_append(np, NULL, NULL, strdup(""), NULL); + + pnew->n_runs = 0; + add_run(pnew, end, rr->fontdesc, rr->col, NULL); + pnew->scblock = end; + + wrap_paragraph(pnew, pc, fr->w - fr->pad_l - fr->pad_r, 0, 0); + + return end; + + } + + } else { + + /* Split the run (and SCBlock) into two */ + double col[4] = {0,0,0,0}; + struct text_run *rn; + + printf("Splitting run %i. Before:\n", run); + show_para(para); + + add_run(para, NULL, NULL, col, NULL); + /* -2 here because add_run increased para->n_runs by 1 */ + memmove(¶->runs[run+2], ¶->runs[run+1], + (para->n_runs - run - 2)*sizeof(struct text_run)); + + rr = ¶->runs[run]; /* Because add_run realloced the runs */ + rn = ¶->runs[run+1]; + + rn->scblock = sc_block_split(rr->scblock, run_offs); + + rn->fontdesc = pango_font_description_copy(rr->fontdesc); + rn->col[0] = rr->col[0]; + rn->col[1] = rr->col[1]; + rn->col[2] = rr->col[2]; + rn->col[3] = rr->col[3]; + + printf("After:\n"); + show_para(para); + + } + + /* All later runs just get moved to the new paragraph */ + for ( i=run+1; in_runs; i++ ) { + double col[4] = {0,0,0,0}; + printf("Moving run %i to pos %i\n", i, pnew->n_runs); + add_run(pnew, NULL, NULL, col, NULL); + pnew->runs[pnew->n_runs-1] = para->runs[i]; + } + pnew->scblock = pnew->runs[0].scblock; + + /* Truncate the first paragraph at the appropriate position */ + para->n_runs = run+1; + + printf("Final paragraphs:\n"); + printf("First:\n"); + show_para(para); + printf("Second:\n"); + show_para(pnew); + + /* Add a \newpara after the end of the first paragraph's SC */ + nnp = sc_block_append(rr->scblock, strdup("newpara"), NULL, NULL, NULL); + set_newline_at_end(pnew, get_newline_at_end(para)); + set_newline_at_end(para, nnp); + + wrap_paragraph(para, pc, fr->w - fr->pad_l - fr->pad_r, 0, 0); + wrap_paragraph(pnew, pc, fr->w - fr->pad_l - fr->pad_r, 0, 0); + + return sc_block_next(nnp); +} + + +SCBlock *split_paragraph(struct frame *fr, int pn, size_t pos, PangoContext *pc) +{ + Paragraph *para = fr->paras[pn]; + + if ( para->type == PARA_TYPE_TEXT ) { + return split_text_paragraph(fr, pn, pos, pc); + } else { + /* Other types can't be split */ + return NULL; + } +} + + +SCBlock *block_at_cursor(struct frame *fr, int pn, size_t pos) +{ + Paragraph *para = fr->paras[pn]; + + if ( para->type != PARA_TYPE_CALLBACK ) return NULL; + + return para->scblock; +} + + +int get_sc_pos(struct frame *fr, int pn, size_t pos, + SCBlock **bl, size_t *ppos) +{ + Paragraph *para = fr->paras[pn]; + int nrun; + struct text_run *run; + + nrun = which_run(para, pos); + if ( nrun == para->n_runs ) { + fprintf(stderr, "Couldn't find run to insert into.\n"); + return 1; + } + run = ¶->runs[nrun]; + + *ppos = pos - get_paragraph_offset(para, nrun); + *bl = run->scblock; + + return 0; +} + + +void set_para_spacing(Paragraph *para, float space[4]) +{ + if ( para == NULL ) return; + para->space[0] = space[0]; + para->space[1] = space[1]; + para->space[2] = space[2]; + para->space[3] = space[3]; +} + + +void set_para_alignment(Paragraph *para, PangoAlignment align) +{ + if ( para == NULL ) return; + para->alignment = align; +} + + +void *get_para_bvp(Paragraph *para) +{ + if ( para->type != PARA_TYPE_CALLBACK ) return NULL; + return para->bvp; +} + + +SCBlock *para_scblock(Paragraph *para) +{ + return para->scblock; +} + + +enum para_type para_type(Paragraph *para) +{ + return para->type; +} + + +int para_debug_num_runs(Paragraph *para) +{ + if ( para->type != PARA_TYPE_TEXT ) return 0; + return para->n_runs; +} + + +int para_debug_run_info(Paragraph *para, int i, SCBlock **scblock) +{ + if ( para->type != PARA_TYPE_TEXT ) return 1; + if ( i >= para->n_runs ) return 1; + + *scblock = para->runs[i].scblock; + return 0; +} diff --git a/src-old/frame.h b/src-old/frame.h new file mode 100644 index 0000000..d0525f8 --- /dev/null +++ b/src-old/frame.h @@ -0,0 +1,194 @@ +/* + * frame.h + * + * Copyright © 2013-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 FRAME_H +#define FRAME_H + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include + + +typedef enum +{ + GRAD_NONE, + GRAD_HORIZ, + GRAD_VERT +} GradientType; + +enum para_type +{ + PARA_TYPE_TEXT, + PARA_TYPE_IMAGE, + PARA_TYPE_CALLBACK +}; + +typedef struct _paragraph Paragraph; + +#include "sc_parse.h" +#include "sc_interp.h" +#include "imagestore.h" + + +struct edit_pos +{ + int para; /* Paragraph number */ + size_t pos; /* Byte position within paragraph + * Yes, really. See pango_layout_xy_to_index */ + int trail; +}; + + +struct frame +{ + struct frame **children; + int num_children; + int max_children; + + SCBlock *scblocks; + + Paragraph **paras; + int n_paras; + int max_paras; + + /* The font which will be used by default for this frame */ + PangoFontDescription *fontdesc; + double col[4]; + + /* The rectangle allocated to this frame, determined by the renderer */ + double x; + double y; + double w; + double h; + + double pad_t; + double pad_b; + double pad_l; + double pad_r; + + /* Background properties for this frame */ + double bgcol[4]; + double bgcol2[4]; + GradientType grad; + + /* True if this frame should be deleted on the next mouse click */ + int empty; + + /* True if the aspect ratio of this frame should be maintained */ + int is_image; + + /* True if this frame can be resized and moved */ + int resizable; +}; + + +extern struct frame *frame_new(void); +extern void frame_free(struct frame *fr); +extern struct frame *add_subframe(struct frame *fr); +extern struct frame *find_frame_with_scblocks(struct frame *top, + SCBlock *scblocks); + +extern double total_height(struct frame *fr); + +extern Paragraph *last_para(struct frame *fr); +extern void show_para(Paragraph *p); +extern void set_para_spacing(Paragraph *para, float space[4]); +extern void set_para_alignment(Paragraph *para, PangoAlignment align); + +extern double paragraph_height(Paragraph *para); +extern void render_paragraph(cairo_t *cr, Paragraph *para, ImageStore *is); + +extern void set_newline_at_end(Paragraph *para, SCBlock *bl); +extern void show_edit_pos(struct edit_pos a); + +extern void add_run(Paragraph *para, SCBlock *scblock, + PangoFontDescription *fdesc, double col[4], + const char *real_text); + +extern Paragraph *insert_paragraph(struct frame *fr, int pos); + +extern Paragraph *add_callback_para(struct frame *fr, SCBlock *scblock, + double w, double h, + SCCallbackDrawFunc draw_func, + SCCallbackClickFunc click_func, void *bvp, + void *vp); + +extern void add_image_para(struct frame *fr, SCBlock *scblock, + const char *filename, + ImageStore *is, double w, double h, int editable); + +extern void wrap_paragraph(Paragraph *para, PangoContext *pc, double w, + size_t sel_start, size_t sel_end); + +extern int find_cursor(struct frame *fr, double x, double y, + struct edit_pos *pos); + +extern void sort_positions(struct edit_pos *a, struct edit_pos *b); + +extern void ensure_run(struct frame *fr, struct edit_pos cpos); + +extern int positions_equal(struct edit_pos a, struct edit_pos b); + +extern int get_para_highlight(struct frame *fr, int cursor_para, + double *cx, double *cy, double *cw, double *ch); + +extern int position_editable(struct frame *fr, struct edit_pos cp); + +extern int get_cursor_pos(struct frame *fr, int cursor_para, int cursor_pos, + double *cx, double *cy, double *ch); + +extern void cursor_moveh(struct frame *fr, struct edit_pos *cp, signed int dir); + +extern void check_callback_click(struct frame *fr, int para); + +extern size_t pos_trail_to_offset(Paragraph *para, size_t offs, int trail); + +extern void insert_text_in_paragraph(Paragraph *para, size_t offs, + const char *t); + +extern void delete_text_from_frame(struct frame *fr, struct edit_pos p1, struct edit_pos p2, + double wrap_w); + +extern SCBlock *split_paragraph(struct frame *fr, int pn, size_t pos, + PangoContext *pc); +extern SCBlock *block_at_cursor(struct frame *fr, int para, size_t pos); + +extern void show_frame_hierarchy(struct frame *fr, const char *t); + +extern int get_sc_pos(struct frame *fr, int pn, size_t pos, + SCBlock **bl, size_t *ppos); + +extern void *get_para_bvp(Paragraph *para); + +extern Paragraph *create_paragraph(struct frame *fr, SCBlock *bl); + +extern enum para_type para_type(Paragraph *para); +extern SCBlock *para_scblock(Paragraph *para); + +extern SCBlock *para_debug_get_newline_at_end(Paragraph *para); +extern int para_debug_num_runs(Paragraph *para); +extern int para_debug_run_info(Paragraph *para, int i, SCBlock **scblock); + +#endif /* FRAME_H */ diff --git a/src-old/imagestore.c b/src-old/imagestore.c new file mode 100644 index 0000000..61ebcfb --- /dev/null +++ b/src-old/imagestore.c @@ -0,0 +1,369 @@ +/* + * imagestore.c + * + * Copyright © 2013-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 +#include +#include +#include +#include + +#include "imagestore.h" +#include "utils.h" + +#define MAX_SIZES (32) + +struct image_record +{ + char *filename; + cairo_surface_t *surf[MAX_SIZES]; + int w[MAX_SIZES]; + int last_used[MAX_SIZES]; + int n_sizes; + int h_last_used; + int full_width; + int full_height; +}; + + +struct _imagestore +{ + int n_images; + struct image_record *images; + int max_images; + GFile *pparent; + GFile *iparent; + const char *storename; +}; + + +static int alloc_images(ImageStore *is, int new_max_images) +{ + struct image_record *images_new; + + images_new = realloc(is->images, + sizeof(struct image_record)*new_max_images); + if ( images_new == NULL ) return 1; + + is->images = images_new; + is->max_images = new_max_images; + return 0; +} + + +ImageStore *imagestore_new(const char *storename) +{ + ImageStore *is; + + is = calloc(1, sizeof(ImageStore)); + if ( is == NULL ) return NULL; + + is->images = NULL; + is->pparent = NULL; + if ( storename != NULL ) { + is->iparent = g_file_new_for_uri(storename); + } else { + is->iparent = NULL; + } + is->n_images = 0; + is->max_images = 0; + if ( alloc_images(is, 32) ) { + free(is); + return NULL; + } + + return is; +} + + +void imagestore_set_parent(ImageStore *is, GFile *parent) +{ + if ( is->pparent != NULL ) { + g_object_unref(is->pparent); + } + is->pparent = parent; +} + + +void imagestore_destroy(ImageStore *is) +{ + int i; + + for ( i=0; in_images; i++ ) { + int j; + free(is->images[i].filename); + for ( j=0; jimages[i].n_sizes; j++ ) { + if ( is->images[i].surf[j] != NULL ) { + g_object_unref(is->images[i].surf[j]); + } + } + } + g_object_unref(is->pparent); + g_object_unref(is->iparent); + free(is->images); + free(is); +} + + +static cairo_surface_t *pixbuf_to_surface(GdkPixbuf *t) +{ + cairo_surface_t *surf; + cairo_t *cr; + int w, h; + + if ( t == NULL ) return NULL; + + w = gdk_pixbuf_get_width(t); + h = gdk_pixbuf_get_height(t); + + surf = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, w, h); + if ( surf == NULL ) return NULL; + + cr = cairo_create(surf); + + gdk_cairo_set_source_pixbuf(cr, t, 0, 0); + cairo_pattern_t *patt = cairo_get_source(cr); + cairo_pattern_set_extend(patt, CAIRO_EXTEND_PAD); + cairo_pattern_set_filter(patt, CAIRO_FILTER_BEST); + cairo_paint(cr); + + cairo_destroy(cr); + + return surf; +} + + +static int try_load(GFile *file, GdkPixbuf **pixbuf, gint w, gint h) +{ + GFileInputStream *stream; + GError *error = NULL; + + stream = g_file_read(file, NULL, &error); + if ( stream != NULL ) { + GError *pberr = NULL; + *pixbuf = gdk_pixbuf_new_from_stream_at_scale(G_INPUT_STREAM(stream), + w, h, TRUE, + NULL, &pberr); + g_object_unref(stream); + g_object_unref(file); + return 1; + } + + return 0; +} + + +static GdkPixbuf *load_image(const char *uri, GFile *pparent, GFile *iparent, + gint w, gint h) +{ + GFile *file; + GdkPixbuf *pixbuf; + + /* Literal pathname */ + file = g_file_new_for_path(uri); + if ( try_load(file, &pixbuf, w, h) ) return pixbuf; + + /* Try the file prefixed with the directory the presentation is in */ + if ( pparent != NULL ) { + file = g_file_get_child(pparent, uri); + if ( try_load(file, &pixbuf, w, h) ) return pixbuf; + } + + /* Try prefixing with imagestore folder */ + if ( iparent != NULL ) { + file = g_file_get_child(iparent, uri); + if ( try_load(file, &pixbuf, w, h) ) return pixbuf; + } + + return NULL; +} + + +static struct image_record *add_image_record(ImageStore *is, + const char *filename) +{ + int idx; + + if ( is->n_images == is->max_images ) { + if ( alloc_images(is, is->max_images+32) ) { + fprintf(stderr, "Couldn't allocate memory for image " + "records.\n"); + return NULL; + } + } + + idx = is->n_images++; + + is->images[idx].n_sizes = 0; + is->images[idx].filename = strdup(filename); + is->images[idx].h_last_used = 0; + + return &is->images[idx]; +} + + + +int imagestore_get_size(ImageStore *is, const char *filename, + int *w, int *h) +{ + GdkPixbuf *pixbuf; + struct image_record *ir; + int i; + + for ( i=0; in_images; i++ ) { + if ( strcmp(is->images[i].filename, filename) == 0 ) { + *w = is->images[i].full_width; + *h = is->images[i].full_height; + return 0; + } + } + + pixbuf = load_image(filename, is->pparent, is->iparent, -1, -1); + if ( pixbuf == NULL ) return 1; + + + *w = gdk_pixbuf_get_width(pixbuf); + *h = gdk_pixbuf_get_height(pixbuf); + + ir = add_image_record(is, filename); + if ( ir != NULL ) { + ir->full_width = *w; + ir->full_height = *h; + } /* otherwise can't cache, too bad */ + + g_object_unref(pixbuf); + + return 0; +} + + +static int find_earliest(struct image_record *im) +{ + int i, earliest, l; + + earliest = im->h_last_used; + l = im->n_sizes; + + for ( i=0; in_sizes; i++ ) { + if ( im->last_used[i] < earliest ) { + earliest = im->last_used[i]; + l = i; + } + } + + assert(l != im->n_sizes); + + cairo_surface_destroy(im->surf[l]); + + return l; +} + + +static cairo_surface_t *add_image_size(struct image_record *im, + const char *filename, + GFile *pparent, GFile *iparent, + int w) +{ + cairo_surface_t *surf; + GdkPixbuf *t; + int pos; + + t = load_image(filename, pparent, iparent, w, -1); + if ( t == NULL ) return NULL; + surf = pixbuf_to_surface(t); + g_object_unref(t); + + /* Add surface to list */ + if ( im->n_sizes == MAX_SIZES ) { + pos = find_earliest(im); + } else { + pos = im->n_sizes++; + } + + im->w[pos] = w; + im->surf[pos] = surf; + im->last_used[pos] = im->h_last_used++; + + return surf; +} + + +static cairo_surface_t *add_new_image(ImageStore *is, const char *filename, int w) +{ + struct image_record *ir; + ir = add_image_record(is, filename); + if ( ir == NULL ) return NULL; + return add_image_size(ir, filename, is->pparent, is->iparent, w); +} + + +void show_imagestore(ImageStore *is) +{ + int i; + + printf("Store %p contains %i records.\n", is, is->n_images); + + for ( i=0; in_images; i++ ) { + + printf("%s :\n", is->images[i].filename); + printf("\n"); + + } +} + + +cairo_surface_t *lookup_image(ImageStore *is, const char *filename, int w) +{ + int i, j; + int found = 0; + cairo_surface_t *surf; + + for ( i=0; in_images; i++ ) { + if ( strcmp(is->images[i].filename, filename) == 0 ) { + found = 1; + break; + } + } + if ( !found ) { + return add_new_image(is, filename, w); + } + + for ( j=0; jimages[i].n_sizes; j++ ) { + if ( is->images[i].w[j] == w ) { + is->images[i].last_used[j] = is->images[i].h_last_used++; + return is->images[i].surf[j]; + } + } + + /* We don't have this size yet */ + surf = add_image_size(&is->images[i], filename, is->pparent, + is->iparent, w); + return surf; +} diff --git a/src-old/imagestore.h b/src-old/imagestore.h new file mode 100644 index 0000000..0d4df24 --- /dev/null +++ b/src-old/imagestore.h @@ -0,0 +1,43 @@ +/* + * imagestore.h + * + * Copyright © 2013-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 IMAGESTORE_H +#define IMAGESTORE_H + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include + +typedef struct _imagestore ImageStore; + +extern ImageStore *imagestore_new(const char *storename); +extern void imagestore_destroy(ImageStore *is); +extern void imagestore_set_parent(ImageStore *is, GFile *parent); +extern void show_imagestore(ImageStore *is); + +extern cairo_surface_t *lookup_image(ImageStore *is, const char *uri, int w); +extern int imagestore_get_size(ImageStore *is, const char *uri, int *w, int *h); + +#endif /* IMAGESTORE_H */ diff --git a/src-old/narrative_window.c b/src-old/narrative_window.c new file mode 100644 index 0000000..e0c59ef --- /dev/null +++ b/src-old/narrative_window.c @@ -0,0 +1,927 @@ +/* + * 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 new file mode 100644 index 0000000..24b4a4b --- /dev/null +++ b/src-old/narrative_window.h @@ -0,0 +1,42 @@ +/* + * 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-old/pr_clock.c b/src-old/pr_clock.c new file mode 100644 index 0000000..8085c89 --- /dev/null +++ b/src-old/pr_clock.c @@ -0,0 +1,438 @@ +/* + * pr_clock.c + * + * Copyright © 2013-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 "presentation.h" +#include "pr_clock.h" +#include "utils.h" + + +struct pr_clock +{ + int open; + + GtkWidget *window; + GtkWidget *entry; + GtkWidget *startbutton; + GtkWidget *da; + GtkWidget *wallclock; + GtkWidget *elapsed; + GtkWidget *remaining; + GtkWidget *status; + GTimeZone *tz; + + GDateTime *start; + double time_elapsed_at_start; + guint timer_id; + + int running; + double time_allowed; + double time_elapsed; + int pos; + int end; + int pos_reached; + + double t; + double tf; +}; + + +static char *format_span(int n) +{ + char tmp[32]; + int hours, mins, sec; + char *s; + + if ( n < 0 ) { + s = "-"; + n = -n; + } else { + s = ""; + } + + sec = n % 60; + mins = ((n-sec) % (60*60))/60; + hours = (n-sec-mins) / (60*60); + + snprintf(tmp, 31, "%s%i:%02i:%02i", s, hours, mins, sec); + + return strdup(tmp); +} + + +static char *format_span_nice(int n) +{ + char tmp[64]; + int hours, mins, sec; + char *s; + + if ( n < 0 ) { + s = "behind"; + n = -n; + } else { + s = "ahead"; + } + + sec = n % 60; + mins = ((n-sec) % (60*60))/60; + hours = (n-sec-mins) / (60*60); + + if ( n <= 60 ) { + snprintf(tmp, 63, "%i seconds %s", n, s); + return strdup(tmp); + } + + if ( n < 60*60 ) { + snprintf(tmp, 63, "%i min %i seconds %s", mins, sec, s); + return strdup(tmp); + } + + snprintf(tmp, 63, "%i hours, %i min, %i seconds %s", + hours, mins, sec, s); + return strdup(tmp); +} + + +static gboolean update_clock(gpointer data) +{ + struct pr_clock *n = data; + gchar *d; + GDateTime *dt; + GTimeSpan sp; + double time_remaining; + double delta; + gint w, h; + char *tmp; + + if ( !n->open ) { + g_date_time_unref(n->start); + g_time_zone_unref(n->tz); + free(n); + return FALSE; + } + + dt = g_date_time_new_now(n->tz); + + if ( n->running ) { + + sp = g_date_time_difference(dt, n->start); + n->time_elapsed = n->time_elapsed_at_start + + sp / G_TIME_SPAN_SECOND; + + time_remaining = n->time_allowed - n->time_elapsed; + + tmp = format_span(n->time_elapsed); + gtk_label_set_text(GTK_LABEL(n->elapsed), tmp); + free(tmp); + + tmp = format_span(time_remaining); + gtk_label_set_text(GTK_LABEL(n->remaining), tmp); + free(tmp); + + } else { + + n->time_elapsed = n->time_elapsed_at_start; + + time_remaining = n->time_allowed - n->time_elapsed; + + tmp = format_span(n->time_elapsed); + gtk_label_set_text(GTK_LABEL(n->elapsed), tmp); + free(tmp); + + tmp = format_span(time_remaining); + gtk_label_set_text(GTK_LABEL(n->remaining), tmp); + free(tmp); + + } + + d = g_date_time_format(dt, "%H:%M:%S"); + g_date_time_unref(dt); + + gtk_label_set_text(GTK_LABEL(n->wallclock), d); + free(d); + + n->t = n->time_elapsed / n->time_allowed; + + if ( n->time_allowed == 0.0 ) n->t = 0.0; + if ( n->time_elapsed > n->time_allowed ) n->t = 1.0; + + if ( n->end > 0 ) { + n->tf = (double)n->pos_reached / (n->end-1); + } else { + n->tf = 0.0; + } + + delta = (n->tf - n->t)*n->time_allowed; + tmp = format_span_nice(delta); + gtk_label_set_text(GTK_LABEL(n->status), tmp); + free(tmp); + + w = gtk_widget_get_allocated_width(GTK_WIDGET(n->da)); + h = gtk_widget_get_allocated_height(GTK_WIDGET(n->da)); + gtk_widget_queue_draw_area(n->da, 0, 0, w, h); + + return TRUE; +} + + +void pr_clock_set_pos(PRClock *n, int pos, int end) +{ + if ( n == NULL ) return; + n->pos = pos; + if ( n->pos > n->pos_reached ) { + n->pos_reached = pos; + } + n->end = end; + update_clock(n); +} + + +static gint close_clock_sig(GtkWidget *w, PRClock *n) +{ + g_source_remove(n->timer_id); + free(n); + return FALSE; +} + + +static gboolean clock_draw_sig(GtkWidget *da, cairo_t *cr, struct pr_clock *n) +{ + int width, height; + double s; + double ff; + + width = gtk_widget_get_allocated_width(GTK_WIDGET(da)); + height = gtk_widget_get_allocated_height(GTK_WIDGET(da)); + s = width-20.0; + + /* Overall background */ + cairo_rectangle(cr, 10.0, 0.0, s, height); + cairo_set_source_rgb(cr, 1.0, 1.0, 1.0); + cairo_fill(cr); + + cairo_rectangle(cr, 10.0, 0.0, s*n->t, height); + cairo_set_source_rgb(cr, 0.0, 0.0, 1.0); + cairo_fill(cr); + + if ( n->tf > n->t ) { + cairo_rectangle(cr, 10.0+s*n->t, 0.0, (n->tf - n->t)*s, height); + cairo_set_source_rgb(cr, 0.0, 1.0, 0.0); + cairo_fill(cr); + } else { + cairo_rectangle(cr, 10.0+s*n->t, 0.0, (n->tf - n->t)*s, height); + cairo_set_source_rgb(cr, 1.0, 0.0, 0.0); + cairo_fill(cr); + } + + ff = (double)n->pos / (n->end-1); + if ( n->end == 1 ) ff = 0.0; + cairo_move_to(cr, 10.0+ff*s, 0.0); + cairo_line_to(cr, 10.0+ff*s, height); + cairo_set_line_width(cr, 2.0); + cairo_set_source_rgb(cr, 1.0, 0.0, 1.0); + cairo_stroke(cr); + + if ( !n->running ) { + cairo_move_to(cr, 10.0, height*0.8); + cairo_set_font_size(cr, height*0.8); + cairo_select_font_face(cr, "sans-serif", + CAIRO_FONT_SLANT_NORMAL, + CAIRO_FONT_WEIGHT_BOLD); + cairo_set_source_rgb(cr, 0.0, 0.0, 0.0); + cairo_show_text(cr, _("Timer is NOT running!")); + } + + return FALSE; +} + + +static void set_sig(GtkEditable *w, struct pr_clock *n) +{ + const gchar *t; + char *check; + + t = gtk_entry_get_text(GTK_ENTRY(n->entry)); + n->time_allowed = 60.0 * strtod(t, &check); + if ( check == t ) { + fprintf(stderr, "Invalid time '%s'\n", t); + n->time_allowed = 0.0; + } + + update_clock(n); +} + + +static gboolean reset_sig(GtkWidget *w, gpointer data) +{ + struct pr_clock *n = data; + + n->time_elapsed = 0; + n->time_elapsed_at_start = 0; + + if ( n->start != NULL ) { + g_date_time_unref(n->start); + } + + n->start = g_date_time_new_now(n->tz); + + update_clock(n); + + return FALSE; +} + + +static gboolean setpos_sig(GtkWidget *w, gpointer data) +{ + struct pr_clock *n = data; + n->pos_reached = n->pos; + update_clock(n); + return FALSE; +} + + +static gboolean start_sig(GtkWidget *w, gpointer data) +{ + struct pr_clock *n = data; + + if ( n->running ) { + n->running = 0; + n->time_elapsed_at_start = n->time_elapsed; + gtk_label_set_text(GTK_LABEL(gtk_bin_get_child(GTK_BIN(w))), + _("Start")); + } else { + n->time_elapsed_at_start = n->time_elapsed; + if ( n->start != NULL ) { + g_date_time_unref(n->start); + } + n->start = g_date_time_new_now(n->tz); + n->running = 1; + gtk_label_set_text(GTK_LABEL(gtk_bin_get_child(GTK_BIN(w))), + _("Stop")); + } + + update_clock(n); + + return FALSE; +} + + +PRClock *pr_clock_new() +{ + struct pr_clock *n; + GtkWidget *vbox; + GtkWidget *hbox; + GtkWidget *resetbutton; + GtkWidget *setposbutton; + GtkWidget *grid; + GtkWidget *label; + + n = malloc(sizeof(struct pr_clock)); + if ( n == NULL ) return NULL; + n->open = 1; + + n->tz = g_time_zone_new_local(); + + n->window = gtk_window_new(GTK_WINDOW_TOPLEVEL); + gtk_window_set_default_size(GTK_WINDOW(n->window), 600, 150); + + vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0); + gtk_container_add(GTK_CONTAINER(n->window), vbox); + + hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0); + gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 10); + + label = gtk_label_new(_("Length (mins):")); + gtk_label_set_markup(GTK_LABEL(label), _("Length (mins):")); + g_object_set(G_OBJECT(label), "halign", GTK_ALIGN_END, NULL); + gtk_box_pack_start(GTK_BOX(hbox), label, TRUE, TRUE, 10); + + n->entry = gtk_entry_new(); + gtk_box_pack_start(GTK_BOX(hbox), n->entry, TRUE, TRUE, 0); + + n->startbutton = gtk_button_new_with_label(_("Start")); + gtk_box_pack_start(GTK_BOX(hbox), n->startbutton, TRUE, TRUE, 10); + + resetbutton = gtk_button_new_with_label(_("Reset")); + gtk_box_pack_start(GTK_BOX(hbox), resetbutton, TRUE, TRUE, 10); + + setposbutton = gtk_button_new_with_label(_("Set position")); + gtk_box_pack_start(GTK_BOX(hbox), setposbutton, TRUE, TRUE, 10); + + n->da = gtk_drawing_area_new(); + gtk_box_pack_start(GTK_BOX(vbox), n->da, TRUE, TRUE, 0); + g_signal_connect(G_OBJECT(n->da), "draw", G_CALLBACK(clock_draw_sig), n); + g_signal_connect(G_OBJECT(n->window), "destroy", + G_CALLBACK(close_clock_sig), n); /* FIXME: Uniqueness */ + + grid = gtk_grid_new(); + gtk_grid_set_row_spacing(GTK_GRID(grid), 10); + gtk_grid_set_column_homogeneous(GTK_GRID(grid), TRUE); + gtk_box_pack_start(GTK_BOX(vbox), grid, FALSE, FALSE, 10); + label = gtk_label_new(_("Time elapsed")); + gtk_label_set_markup(GTK_LABEL(label), _("Time elapsed")); + gtk_grid_attach(GTK_GRID(grid), label, 0, 0, 1, 1); + label = gtk_label_new(_("Time remaining")); + gtk_label_set_markup(GTK_LABEL(label), _("Time remaining")); + gtk_grid_attach(GTK_GRID(grid), label, 1, 0, 1, 1); + n->status = gtk_label_new(""); + gtk_grid_attach(GTK_GRID(grid), n->status, 2, 0, 1, 1); + n->elapsed = gtk_label_new(""); + gtk_grid_attach(GTK_GRID(grid), n->elapsed, 0, 1, 1, 1); + n->remaining = gtk_label_new(""); + gtk_grid_attach(GTK_GRID(grid), n->remaining, 1, 1, 1, 1); + n->wallclock = gtk_label_new(""); + gtk_grid_attach(GTK_GRID(grid), n->wallclock, 2, 1, 1, 1); + + g_signal_connect(G_OBJECT(n->startbutton), "clicked", + G_CALLBACK(start_sig), n); + g_signal_connect(G_OBJECT(resetbutton), "clicked", + G_CALLBACK(reset_sig), n); + g_signal_connect(G_OBJECT(setposbutton), "clicked", + G_CALLBACK(setpos_sig), n); + g_signal_connect(G_OBJECT(n->entry), "changed", + G_CALLBACK(set_sig), n); + + n->running = 0; + n->time_allowed = 0; + n->time_elapsed = 0; + n->time_elapsed_at_start = 0; + n->pos = 0; + n->pos_reached = 0; + n->end = 0; + n->start = NULL; + update_clock(n); + n->timer_id = g_timeout_add_seconds(1, update_clock, n); + + gtk_window_set_title(GTK_WINDOW(n->window), _("Presentation clock")); + + gtk_widget_show_all(n->window); + return n; +} diff --git a/src-old/pr_clock.h b/src-old/pr_clock.h new file mode 100644 index 0000000..97d2d0d --- /dev/null +++ b/src-old/pr_clock.h @@ -0,0 +1,37 @@ +/* + * pr_clock.h + * + * Copyright © 2013-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 CLOCK_H +#define CLOCK_H + +#ifdef HAVE_CONFIG_H +#include +#endif + +typedef struct pr_clock PRClock; + +extern PRClock *pr_clock_new(void); + +extern void pr_clock_set_pos(PRClock *n, int pos, int end); + + +#endif /* CLOCK_H */ diff --git a/src-old/presentation.c b/src-old/presentation.c new file mode 100644 index 0000000..d7d9c08 --- /dev/null +++ b/src-old/presentation.c @@ -0,0 +1,362 @@ +/* + * presentation.c + * + * Copyright © 2013-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 "presentation.h" +#include "slide_window.h" +#include "frame.h" +#include "imagestore.h" +#include "render.h" +#include "sc_interp.h" +#include "utils.h" + + +void free_presentation(struct presentation *p) +{ + /* FIXME: Loads of stuff leaks here */ + g_object_unref(p->file); + g_object_unref(p->stylesheet_from); + imagestore_destroy(p->is); + free(p); +} + + +char *get_titlebar_string(struct presentation *p) +{ + if ( p == NULL || p->file == NULL ) { + return strdup(_("(untitled)")); + } else { + char *bn = g_file_get_basename(p->file); + return bn; + } +} + + +static void find_and_load_stylesheet(struct presentation *p, GFile *file) +{ + GFile *ssfile; + + if ( file != NULL ) { + + /* First choice: /same/directory/.ss */ + gchar *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); + p->stylesheet = stylesheet_load(ssfile); + p->stylesheet_from = ssfile; + g_free(ssuri); + } + } + + /* Second choice: /same/directory/stylesheet.ss */ + if ( p->stylesheet == NULL ) { + GFile *parent = g_file_get_parent(file); + if ( parent != NULL ) { + ssfile = g_file_get_child(parent, "stylesheet.ss"); + if ( ssfile != NULL ) { + p->stylesheet = stylesheet_load(ssfile); + p->stylesheet_from = ssfile; + } + g_object_unref(parent); + } + } + + } + + /* Third choice: /stylesheet.ss */ + if ( p->stylesheet == NULL ) { + ssfile = g_file_new_for_path("./stylesheet.ss"); + p->stylesheet = stylesheet_load(ssfile); + p->stylesheet_from = ssfile; + } + + /* Fourth choice: internal default stylesheet */ + if ( p->stylesheet == NULL ) { + ssfile = g_file_new_for_uri("resource:///uk/me/bitwiz/Colloquium/default.ss"); + p->stylesheet = stylesheet_load(ssfile); + p->stylesheet_from = NULL; + g_object_unref(ssfile); + } + + /* Last resort is NULL stylesheet and SCInterpreter's defaults */ + /* We keep a reference to the GFile */ +} + + +struct presentation *new_presentation(const char *imagestore) +{ + struct presentation *new; + + new = calloc(1, sizeof(struct presentation)); + if ( new == NULL ) return NULL; + + new->file = NULL; + new->stylesheet_from = NULL; + + new->scblocks = NULL; + + /* Default slide size */ + new->slide_width = 1024.0; + new->slide_height = 768.0; + + new->completely_empty = 1; + new->saved = 1; + new->stylesheet = NULL; + new->is = imagestore_new(imagestore); + + new->lang = pango_language_get_default(); + + find_and_load_stylesheet(new, NULL); + + return new; +} + + +int save_presentation(struct presentation *p, GFile *file, GFile *ssfile) +{ + GFileOutputStream *fh; + int r; + int sr; + GError *error = NULL; + + fh = g_file_replace(file, NULL, FALSE, G_FILE_CREATE_NONE, NULL, &error); + if ( fh == NULL ) { + fprintf(stderr, _("Open failed: %s\n"), error->message); + return 1; + } + r = save_sc_block(G_OUTPUT_STREAM(fh), p->scblocks); + if ( r ) { + fprintf(stderr, _("Couldn't save presentation\n")); + } + g_object_unref(fh); + + if ( ssfile != NULL ) { + char *uri = g_file_get_uri(ssfile); + printf(_("Saving stylesheet to %s\n"), uri); + g_free(uri); + sr = stylesheet_save(p->stylesheet, ssfile); + if ( sr ) { + fprintf(stderr, _("Couldn't save stylesheet\n")); + } + if ( p->stylesheet_from != ssfile ) { + if ( p->stylesheet_from != NULL ) { + g_object_unref(p->stylesheet_from); + } + p->stylesheet_from = ssfile; + g_object_ref(p->stylesheet_from); + } + } else { + fprintf(stderr, _("Not saving the stylesheet\n")); + sr = 0; + } + + if ( r || sr ) return 1; + + imagestore_set_parent(p->is, g_file_get_parent(file)); + + if ( p->file != file ) { + if ( p->file != NULL ) g_object_unref(p->file); + p->file = file; + g_object_ref(p->file); + } + p->saved = 1; + update_titlebar(p->narrative_window); + return 0; +} + + +int slide_number(struct presentation *p, SCBlock *sl) +{ + SCBlock *bl = p->scblocks; + int n = 0; + + while ( bl != NULL ) { + if ( safe_strcmp(sc_block_name(bl), "slide") == 0 ) { + n++; + if ( bl == sl ) return n; + } + bl = sc_block_next(bl); + } + + return 0; +} + + +int num_slides(struct presentation *p) +{ + SCBlock *bl = p->scblocks; + int n = 0; + + while ( bl != NULL ) { + if ( safe_strcmp(sc_block_name(bl), "slide") == 0 ) { + n++; + } + bl = sc_block_next(bl); + } + + return n; +} + + +SCBlock *first_slide(struct presentation *p) +{ + SCBlock *bl = p->scblocks; + + while ( bl != NULL ) { + if ( safe_strcmp(sc_block_name(bl), "slide") == 0 ) { + return bl; + } + bl = sc_block_next(bl); + } + + fprintf(stderr, "Couldn't find first slide!\n"); + return NULL; +} + + +SCBlock *last_slide(struct presentation *p) +{ + SCBlock *bl = p->scblocks; + SCBlock *l = NULL; + + while ( bl != NULL ) { + if ( safe_strcmp(sc_block_name(bl), "slide") == 0 ) { + l = bl; + } + bl = sc_block_next(bl); + } + + if ( l == NULL ) { + fprintf(stderr, "Couldn't find last slide!\n"); + } + return l; +} + + +SCBlock *next_slide(struct presentation *p, SCBlock *sl) +{ + SCBlock *bl = sl; + int found = 0; + + while ( bl != NULL ) { + if ( safe_strcmp(sc_block_name(bl), "slide") == 0 ) { + if ( found ) return bl; + } + if ( bl == sl ) { + found = 1; + } + bl = sc_block_next(bl); + } + + fprintf(stderr, "Couldn't find next slide!\n"); + return NULL; +} + + +SCBlock *prev_slide(struct presentation *p, SCBlock *sl) +{ + SCBlock *bl = p->scblocks; + SCBlock *l = NULL; + + while ( bl != NULL ) { + if ( bl == sl ) { + if ( l == NULL ) return sl; /* Already on first slide */ + return l; + } + if ( safe_strcmp(sc_block_name(bl), "slide") == 0 ) { + l = bl; + } + bl = sc_block_next(bl); + } + + fprintf(stderr, "Couldn't find prev slide!\n"); + return NULL; +} + + +static void set_slide_size_from_stylesheet(struct presentation *p) +{ + char *result; + + result = stylesheet_lookup(p->stylesheet, "$.slide", "size"); + if ( result != NULL ) { + float v[2]; + if ( parse_double(result, v) == 0 ) { + p->slide_width = v[0]; + p->slide_height = v[1]; + } + } +} + + +int load_presentation(struct presentation *p, GFile *file) +{ + int r = 0; + char *everything; + + assert(p->completely_empty); + + if ( !g_file_load_contents(file, NULL, &everything, NULL, NULL, NULL) ) { + fprintf(stderr, _("Failed to load '%s'\n"), g_file_get_uri(file)); + return 1; + } + + p->scblocks = sc_parse(everything); + g_free(everything); + + p->lang = pango_language_get_default(); + + if ( p->scblocks == NULL ) r = 1; + + if ( r ) { + p->completely_empty = 1; + fprintf(stderr, _("Parse error.\n")); + return r; /* Error */ + } + + p->stylesheet = NULL; + + find_and_load_stylesheet(p, file); + + set_slide_size_from_stylesheet(p); + + assert(p->file == NULL); + p->file = file; + g_object_ref(file); + + imagestore_set_parent(p->is, g_file_get_parent(file)); + + return 0; +} + diff --git a/src-old/presentation.h b/src-old/presentation.h new file mode 100644 index 0000000..b288d8e --- /dev/null +++ b/src-old/presentation.h @@ -0,0 +1,83 @@ +/* + * presentation.h + * + * Copyright © 2013-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 PRESENTATION_H +#define PRESENTATION_H + +#ifdef HAVE_CONFIG_H +#include +#endif + +struct presentation; + +#include "imagestore.h" +#include "sc_parse.h" +#include "slideshow.h" +#include "narrative_window.h" +#include "slide_window.h" +#include "stylesheet.h" + +struct menu_pl; + +struct presentation +{ + GFile *file; + GFile *stylesheet_from; + int completely_empty; + int saved; + PangoLanguage *lang; + + ImageStore *is; + + NarrativeWindow *narrative_window; + + struct pr_clock *clock; + + /* This is the "native" size of the slide. It only exists to give + * font size some meaning in the context of a somewhat arbitrary DPI */ + double slide_width; + double slide_height; + + SCBlock *scblocks; + Stylesheet *stylesheet; +}; + + +extern struct presentation *new_presentation(const char *imagestore); +extern void free_presentation(struct presentation *p); + +extern char *get_titlebar_string(struct presentation *p); + +extern int slide_number(struct presentation *p, SCBlock *sl); +extern int num_slides(struct presentation *p); +extern SCBlock *first_slide(struct presentation *p); +extern SCBlock *last_slide(struct presentation *p); +extern SCBlock *next_slide(struct presentation *p, SCBlock *sl); +extern SCBlock *prev_slide(struct presentation *p, SCBlock *sl); + +extern int load_presentation(struct presentation *p, GFile *file); +extern int save_presentation(struct presentation *p, GFile *file, GFile *ssfile); + +#define UNUSED __attribute__((unused)) + + +#endif /* PRESENTATION_H */ diff --git a/src-old/print.c b/src-old/print.c new file mode 100644 index 0000000..43c967e --- /dev/null +++ b/src-old/print.c @@ -0,0 +1,314 @@ +/* + * print.c + * + * Copyright © 2016-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 "presentation.h" +#include "narrative_window.h" +#include "render.h" +#include "utils.h" + + +static GtkPrintSettings *print_settings = NULL; + +struct print_stuff +{ + struct presentation *p; + + /* Printing config */ + GtkWidget *combo; + int slides_only; + + /* When printing slides only */ + SCBlock *slide; + + /* When printing narrative */ + int nar_line; + struct frame *top; + int start_paras[256]; + int slide_number; +}; + + +static void print_widget_apply(GtkPrintOperation *op, GtkWidget *widget, + void *vp) +{ + const char *id; + struct print_stuff *ps = vp; + + id = gtk_combo_box_get_active_id(GTK_COMBO_BOX(ps->combo)); + if ( strcmp(id, "slides") == 0 ) { + ps->slides_only = 1; + } else { + ps->slides_only = 0; + } +} + + +static GObject *print_widget(GtkPrintOperation *op, void *vp) +{ + GtkWidget *vbox; + GtkWidget *cbox; + struct print_stuff *ps = vp; + + vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 10); + gtk_container_set_border_width(GTK_CONTAINER(vbox), 10); + + /* What do you want to print? */ + cbox = gtk_combo_box_text_new(); + gtk_combo_box_text_append(GTK_COMBO_BOX_TEXT(cbox), "slides", + _("Print the slides only")); + gtk_combo_box_text_append(GTK_COMBO_BOX_TEXT(cbox), "narrative", + _("Print the narrative")); + gtk_box_pack_start(GTK_BOX(vbox), cbox, FALSE, FALSE, 10); + gtk_combo_box_set_active(GTK_COMBO_BOX(cbox), 1); + ps->combo = cbox; + + gtk_widget_show_all(vbox); + return G_OBJECT(vbox); + +} + + +static void print_slide_only(GtkPrintOperation *op, GtkPrintContext *ctx, + struct print_stuff *ps, int page) +{ + cairo_t *cr; + PangoContext *pc; + double w, h; + struct frame *top; + const double sw = ps->p->slide_width; + const double sh = ps->p->slide_height; + double slide_width, slide_height; + + cr = gtk_print_context_get_cairo_context(ctx); + pc = gtk_print_context_create_pango_context(ctx); + w = gtk_print_context_get_width(ctx); + h = gtk_print_context_get_height(ctx); + + cairo_rectangle(cr, 0.0, 0.0, w, h); + cairo_set_source_rgb(cr, 1.0, 1.0, 1.0); + cairo_fill(cr); + + if ( sw/sh > w/h ) { + /* Slide is too wide. Letterboxing top/bottom */ + slide_width = w; + slide_height = w * sh/sw; + } else { + /* Letterboxing at sides */ + slide_width = h * sw/sh; + slide_height = h; + } + + printf("%f x %f ---> %f x %f\n", w, h, slide_width, slide_height); + + top = interp_and_shape(ps->slide, ps->p->stylesheet, NULL, + ps->p->is, page+1, pc, sw, sh, ps->p->lang); + + recursive_wrap(top, pc); + + cairo_scale(cr, slide_width/sw, slide_width/sw); + + recursive_draw(top, cr, ps->p->is, + 0.0, ps->p->slide_height); + + ps->slide = next_slide(ps->p, ps->slide); +} + + +static int print_create_thumbnail(SCInterpreter *scin, SCBlock *bl, + double *w, double *h, void **bvp, void *vp) +{ + struct print_stuff *ps = vp; + struct presentation *p = ps->p; + SCBlock *b; + + *w = 270.0*(p->slide_width / p->slide_height); + *h = 270.0; + b = bl; + + *bvp = b; + + return 1; +} + + +static cairo_surface_t *print_render_thumbnail(int w, int h, void *bvp, void *vp) +{ + struct print_stuff *ps = vp; + struct presentation *p = ps->p; + SCBlock *scblocks = bvp; + cairo_surface_t *surf; + struct frame *top; + + surf = render_sc(scblocks, w, h, p->slide_width, p->slide_height, + p->stylesheet, NULL, p->is, ps->slide_number++, + &top, p->lang); + frame_free(top); + + return surf; +} + + +static void begin_narrative_print(GtkPrintOperation *op, GtkPrintContext *ctx, + struct print_stuff *ps) +{ + SCCallbackList *cbl; + PangoContext *pc; + int i, n_pages; + double h, page_height; + SCBlock *dummy_top; + + cbl = sc_callback_list_new(); + ps->slide_number = 1; + sc_callback_list_add_callback(cbl, "slide", print_create_thumbnail, + print_render_thumbnail, NULL, ps); + + pc = gtk_print_context_create_pango_context(ctx); + + dummy_top = sc_block_new_parent(ps->p->scblocks, "presentation"); + ps->top = interp_and_shape(dummy_top, ps->p->stylesheet, cbl, + ps->p->is, 0, pc, + gtk_print_context_get_width(ctx), + gtk_print_context_get_height(ctx), + ps->p->lang); + recursive_wrap(ps->top, pc); + + /* Count pages */ + page_height = gtk_print_context_get_height(ctx); + h = 0.0; + n_pages = 1; + ps->start_paras[0] = 0; + for ( i=0; itop->n_paras; i++ ) { + if ( h + paragraph_height(ps->top->paras[i]) > page_height ) { + /* Paragraph does not fit on page */ + ps->start_paras[n_pages] = i; + n_pages++; + h = 0.0; + } + h += paragraph_height(ps->top->paras[i]); + } + gtk_print_operation_set_n_pages(op, n_pages); + g_object_unref(pc); +} + + +static void print_narrative(GtkPrintOperation *op, GtkPrintContext *ctx, + struct print_stuff *ps, gint page) +{ + int i; + double h, page_height; + cairo_t *cr; + + page_height = gtk_print_context_get_height(ctx); + cr = gtk_print_context_get_cairo_context(ctx); + + h = 0.0; + for ( i=ps->start_paras[page]; itop->n_paras; i++ ) { + + /* Will this paragraph fit? */ + h += paragraph_height(ps->top->paras[i]); + if ( h > page_height ) return; + + cairo_save(cr); + render_paragraph(cr, ps->top->paras[i], ps->p->is); + cairo_restore(cr); + + cairo_translate(cr, 0.0, paragraph_height(ps->top->paras[i])); + + } + + +} + + + +static void print_begin(GtkPrintOperation *op, GtkPrintContext *ctx, void *vp) +{ + struct print_stuff *ps = vp; + + if ( ps->slides_only ) { + gtk_print_operation_set_n_pages(op, num_slides(ps->p)); + ps->slide = first_slide(ps->p); + } else { + begin_narrative_print(op, ctx, ps); + } +} + + +static void print_draw(GtkPrintOperation *op, GtkPrintContext *ctx, gint page, + void *vp) +{ + struct print_stuff *ps = vp; + if ( ps->slides_only ) { + print_slide_only(op, ctx, ps, page); + } else { + print_narrative(op, ctx, ps, page); + } +} + + +void run_printing(struct presentation *p, GtkWidget *parent) +{ + GtkPrintOperation *print; + GtkPrintOperationResult res; + struct print_stuff *ps; + + ps = malloc(sizeof(struct print_stuff)); + if ( ps == NULL ) return; + ps->p = p; + ps->nar_line = 0; + + print = gtk_print_operation_new(); + if ( print_settings != NULL ) { + gtk_print_operation_set_print_settings(print, print_settings); + } + + g_signal_connect(print, "create-custom-widget", + G_CALLBACK(print_widget), ps); + g_signal_connect(print, "custom-widget-apply", + G_CALLBACK(print_widget_apply), ps); + g_signal_connect(print, "begin_print", G_CALLBACK(print_begin), ps); + g_signal_connect(print, "draw_page", G_CALLBACK(print_draw), ps); + + res = gtk_print_operation_run(print, + GTK_PRINT_OPERATION_ACTION_PRINT_DIALOG, + GTK_WINDOW(parent), NULL); + + if ( res == GTK_PRINT_OPERATION_RESULT_APPLY ) { + if ( print_settings != NULL ) { + g_object_unref(print_settings); + } + print_settings = g_object_ref( + gtk_print_operation_get_print_settings(print)); + } + g_object_unref(print); +} + diff --git a/src-old/print.h b/src-old/print.h new file mode 100644 index 0000000..265b7c1 --- /dev/null +++ b/src-old/print.h @@ -0,0 +1,32 @@ +/* + * print.h + * + * Copyright © 2016-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 PRINT_H +#define PRINT_H + +#ifdef HAVE_CONFIG_H +#include +#endif + +extern void run_printing(struct presentation *p, GtkWidget *parent); + +#endif /* PRINT_H */ diff --git a/src-old/render.c b/src-old/render.c new file mode 100644 index 0000000..6ac09fc --- /dev/null +++ b/src-old/render.c @@ -0,0 +1,332 @@ +/* + * render.c + * + * Copyright © 2013-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 +#include +#include +#include +#include + +#include "sc_parse.h" +#include "sc_interp.h" +#include "presentation.h" +#include "frame.h" +#include "render.h" +#include "imagestore.h" +#include "utils.h" + + +static void do_background(cairo_t *cr, struct frame *fr) +{ + cairo_pattern_t *patt = NULL; + + cairo_new_path(cr); + cairo_rectangle(cr, 0.0, 0.0, fr->w, fr->h); + + switch ( fr->grad ) { + + case GRAD_NONE: + cairo_set_source_rgba(cr, fr->bgcol[0], + fr->bgcol[1], + fr->bgcol[2], + fr->bgcol[3]); + break; + + case GRAD_VERT: + patt = cairo_pattern_create_linear(0.0, 0.0, + 0.0, fr->h); + cairo_pattern_add_color_stop_rgba(patt, 0.0, fr->bgcol[0], + fr->bgcol[1], + fr->bgcol[2], + fr->bgcol[3]); + cairo_pattern_add_color_stop_rgba(patt, 1.0, fr->bgcol2[0], + fr->bgcol2[1], + fr->bgcol2[2], + fr->bgcol2[3]); + cairo_set_source(cr, patt); + break; + + case GRAD_HORIZ: + patt = cairo_pattern_create_linear(0.0, 0.0, + fr->w, 0.0); + cairo_pattern_add_color_stop_rgba(patt, 0.0, fr->bgcol[0], + fr->bgcol[1], + fr->bgcol[2], + fr->bgcol[3]); + cairo_pattern_add_color_stop_rgba(patt, 1.0, fr->bgcol2[0], + fr->bgcol2[1], + fr->bgcol2[2], + fr->bgcol2[3]); + cairo_set_source(cr, patt); + break; + + } + + cairo_fill(cr); + if ( patt != NULL ) cairo_pattern_destroy(patt); +} + + +static int draw_frame(cairo_t *cr, struct frame *fr, ImageStore *is, + double min_y, double max_y) +{ + int i; + double hpos = 0.0; + + cairo_save(cr); + do_background(cr, fr); + + /* Actually render the contents */ + cairo_translate(cr, fr->pad_l, fr->pad_t); + for ( i=0; in_paras; i++ ) { + + double cur_h = paragraph_height(fr->paras[i]); + + cairo_save(cr); + cairo_translate(cr, 0.0, hpos); + + if ( (hpos + cur_h > min_y) && (hpos < max_y) ) { + render_paragraph(cr, fr->paras[i], is); + } /* else paragraph is not visible */ + + hpos += cur_h; + cairo_restore(cr); + + } + cairo_restore(cr); + + return 0; +} + + +int recursive_draw(struct frame *fr, cairo_t *cr, + ImageStore *is, + double min_y, double max_y) +{ + int i; + + draw_frame(cr, fr, is, min_y, max_y); + + for ( i=0; inum_children; i++ ) { + cairo_save(cr); + cairo_translate(cr, fr->children[i]->x, fr->children[i]->y); + recursive_draw(fr->children[i], cr, is, + min_y - fr->children[i]->y, + max_y - fr->children[i]->y); + cairo_restore(cr); + } + + return 0; +} + + +void wrap_frame(struct frame *fr, PangoContext *pc) +{ + int i; + double w; + + w = fr->w - fr->pad_l - fr->pad_r; + + for ( i=0; in_paras; i++ ) { + wrap_paragraph(fr->paras[i], pc, w, 0, 0); + } +} + + +int recursive_wrap(struct frame *fr, PangoContext *pc) +{ + int i; + + wrap_frame(fr, pc); + + for ( i=0; inum_children; i++ ) { + recursive_wrap(fr->children[i], pc); + } + + return 0; +} + + +struct frame *interp_and_shape(SCBlock *scblocks, Stylesheet *stylesheet, + SCCallbackList *cbl, ImageStore *is, + int slide_number, + PangoContext *pc, double w, double h, + PangoLanguage *lang) +{ + SCInterpreter *scin; + char snum[64]; + struct frame *top; + + top = frame_new(); + top->resizable = 0; + top->x = 0.0; + top->y = 0.0; + top->w = w; + top->h = h; + top->scblocks = scblocks; + + scin = sc_interp_new(pc, lang, is, top); + if ( scin == NULL ) { + fprintf(stderr, "Failed to set up interpreter.\n"); + frame_free(top); + return NULL; + } + + sc_interp_set_callbacks(scin, cbl); + + snprintf(snum, 63, "%i", slide_number); + sc_interp_set_constant(scin, SCCONST_SLIDENUMBER, snum); + + top->fontdesc = pango_font_description_copy(sc_interp_get_fontdesc(scin)); + top->col[0] = sc_interp_get_fgcol(scin)[0]; + top->col[1] = sc_interp_get_fgcol(scin)[1]; + top->col[2] = sc_interp_get_fgcol(scin)[2]; + top->col[3] = sc_interp_get_fgcol(scin)[3]; + + sc_interp_add_block(scin, scblocks, stylesheet); + + sc_interp_destroy(scin); + + return top; +} + + +static struct frame *render_sc_with_context(SCBlock *scblocks, + cairo_t *cr, double log_w, double log_h, + Stylesheet *stylesheet, SCCallbackList *cbl, + ImageStore *is, + int slide_number, PangoLanguage *lang, + PangoContext *pc) +{ + struct frame *top; + + cairo_rectangle(cr, 0.0, 0.0, log_w, log_h); + cairo_set_source_rgb(cr, 1.0, 1.0, 1.0); + cairo_fill(cr); + + top = interp_and_shape(scblocks, stylesheet, cbl, is, + slide_number, pc, log_w, log_h, lang); + + recursive_wrap(top, pc); + + recursive_draw(top, cr, is, 0.0, log_h); + + return top; +} + + +cairo_surface_t *render_sc(SCBlock *scblocks, int w, int h, + double log_w, double log_h, + Stylesheet *stylesheet, SCCallbackList *cbl, + ImageStore *is, + int slide_number, struct frame **ptop, + PangoLanguage *lang) +{ + cairo_surface_t *surf; + cairo_t *cr; + struct frame *top; + PangoContext *pc; + + surf = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, w, h); + cr = cairo_create(surf); + pc = pango_cairo_create_context(cr); + cairo_scale(cr, w/log_w, h/log_h); + top = render_sc_with_context(scblocks, cr, log_w, log_h, + stylesheet, cbl, is, slide_number, + lang, pc); + g_object_unref(pc); + cairo_destroy(cr); + + *ptop = top; + + return surf; +} + + +int export_pdf(struct presentation *p, const char *filename) +{ + double r; + double w = 2048.0; + double scale; + cairo_surface_t *surf; + cairo_t *cr; + SCBlock *bl; + int i; + PangoContext *pc; + + r = p->slide_height / p->slide_width; + + surf = cairo_pdf_surface_create(filename, w, w*r); + if ( cairo_surface_status(surf) != CAIRO_STATUS_SUCCESS ) { + fprintf(stderr, _("Couldn't create Cairo surface\n")); + return 1; + } + + cr = cairo_create(surf); + scale = w / p->slide_width; + pc = pango_cairo_create_context(cr); + + i = 1; + bl = p->scblocks; + while ( bl != NULL ) { + + if ( safe_strcmp(sc_block_name(bl), "slide") != 0 ) { + bl = sc_block_next(bl); + continue; + } + + cairo_save(cr); + + cairo_scale(cr, scale, scale); + + cairo_rectangle(cr, 0.0, 0.0, p->slide_width, p->slide_height); + cairo_set_source_rgb(cr, 1.0, 1.0, 1.0); + cairo_fill(cr); + + render_sc_with_context(bl, cr, p->slide_width, + p->slide_height, p->stylesheet, NULL, + p->is, i, p->lang, pc); + + cairo_restore(cr); + + cairo_show_page(cr); + + bl = sc_block_next(bl); + i++; + + } + + g_object_unref(pc); + cairo_surface_finish(surf); + cairo_destroy(cr); + + return 0; +} diff --git a/src-old/render.h b/src-old/render.h new file mode 100644 index 0000000..0cfae26 --- /dev/null +++ b/src-old/render.h @@ -0,0 +1,62 @@ +/* + * render.h + * + * Copyright © 2013-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 RENDER_H +#define RENDER_H + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include "presentation.h" +#include "imagestore.h" +#include "sc_interp.h" +#include "frame.h" + +/* Convienience function to run the entire pipeline */ +extern cairo_surface_t *render_sc(SCBlock *scblocks, int w, int h, + double log_w, double log_h, + Stylesheet *stylesheet, SCCallbackList *cbl, + ImageStore *is, + int slide_number, struct frame **ptop, + PangoLanguage *lang); + +/* Interpret StoryCode and measure boxes. + * Needs to be followed by: wrap_contents() (recursively) + * recursive_draw() + */ +extern struct frame *interp_and_shape(SCBlock *scblocks, Stylesheet *stylesheet, + SCCallbackList *cbl, + ImageStore *is, + int slide_number, PangoContext *pc, + double w, double h, PangoLanguage *lang); + +extern void wrap_frame(struct frame *fr, PangoContext *pc); +extern int recursive_wrap(struct frame *fr, PangoContext *pc); + +extern int export_pdf(struct presentation *p, const char *filename); + +extern int recursive_draw(struct frame *fr, cairo_t *cr, + ImageStore *is, + double min_y, double max_y); + +#endif /* RENDER_H */ diff --git a/src-old/sc_editor.c b/src-old/sc_editor.c new file mode 100644 index 0000000..8e79fd8 --- /dev/null +++ b/src-old/sc_editor.c @@ -0,0 +1,2197 @@ +/* + * sc_editor.c + * + * Copyright © 2013-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 +#include +#include + +#include "colloquium.h" +#include "presentation.h" +#include "slide_window.h" +#include "render.h" +#include "frame.h" +#include "sc_parse.h" +#include "sc_interp.h" +#include "sc_editor.h" +#include "slideshow.h" +#include "debugger.h" +#include "utils.h" + + +static void scroll_interface_init(GtkScrollable *iface) +{ +} + + +enum +{ + SCEDITOR_0, + SCEDITOR_VADJ, + SCEDITOR_HADJ, + SCEDITOR_VPOL, + SCEDITOR_HPOL, +}; + + +G_DEFINE_TYPE_WITH_CODE(SCEditor, sc_editor, GTK_TYPE_DRAWING_AREA, + G_IMPLEMENT_INTERFACE(GTK_TYPE_SCROLLABLE, + scroll_interface_init)) + +static void debug_paragraphs(SCEditor *e) +{ + struct frame *fr = e->cursor_frame; + int i; + + printf("Paragraphs in current frame:\n"); + for ( i=0; in_paras; i++ ) { + show_para(fr->paras[i]); + } +} + + +static void horizontal_adjust(GtkAdjustment *adj, SCEditor *e) +{ + e->h_scroll_pos = gtk_adjustment_get_value(adj); + sc_editor_redraw(e); +} + + +static void set_horizontal_params(SCEditor *e) +{ + if ( e->hadj == NULL ) return; + gtk_adjustment_configure(e->hadj, e->h_scroll_pos, 0, e->w, 100, + e->visible_width, e->visible_width); +} + + +static void vertical_adjust(GtkAdjustment *adj, SCEditor *e) +{ + e->scroll_pos = gtk_adjustment_get_value(adj); + sc_editor_redraw(e); +} + + +static void set_vertical_params(SCEditor *e) +{ + double page; + + if ( e->vadj == NULL ) return; + + /* Ensure we do not scroll off the top of the document */ + if ( e->scroll_pos < 0.0 ) e->scroll_pos = 0.0; + + /* Ensure we do not scroll off the bottom of the document */ + if ( e->scroll_pos > e->h - e->visible_height ) { + e->scroll_pos = e->h - e->visible_height; + } + + /* If we can show the whole document, show it at the top */ + if ( e->h < e->visible_height ) { + e->scroll_pos = 0.0; + } + + if ( e->h > e->visible_height ) { + page = e->visible_height; + } else { + page = e->h; + } + + gtk_adjustment_configure(e->vadj, e->scroll_pos, 0, e->h, 100, + e->visible_height, page); +} + + +static void update_size(SCEditor *e) +{ + if ( e->flow ) { + + double total = total_height(e->top); + + e->w = e->top->w; + e->h = total + e->top->pad_t + e->top->pad_b; + + e->log_w = e->w; + e->log_h = e->h; + e->top->h = e->h; + } else { + e->top->w = e->log_w; + e->top->h = e->log_h; + } + + if ( e->flow && (e->top->h < e->visible_height) ) { + e->top->h = e->visible_height; + } + + set_vertical_params(e); + set_horizontal_params(e); +} + + +static gboolean resize_sig(GtkWidget *widget, GdkEventConfigure *event, + SCEditor *e) +{ + PangoContext *pc; + + pc = gdk_pango_context_get(); + + if ( e->scale ) { + + double sx, sy; + double aw, ah; + + e->w = event->width; + e->h = event->height; + sx = (double)e->w / e->log_w; + sy = (double)e->h / e->log_h; + e->view_scale = (sx < sy) ? sx : sy; + + /* Actual size (in device units) */ + aw = e->view_scale * e->log_w; + ah = e->view_scale * e->log_h; + + e->border_offs_x = (event->width - aw)/2.0; + e->border_offs_y = (event->height - ah)/2.0; + + } + + e->visible_height = event->height; + e->visible_width = event->width; + + /* Interpret and shape, if not already done */ + if ( e->top == NULL ) { + double w, h; + if ( e->flow ) { + w = event->width; + h = 0.0; + } else { + w = e->log_w; + h = e->log_h; + } + e->top = interp_and_shape(e->scblocks, e->stylesheet, e->cbl, + e->is, e->slidenum, pc, + w, h, e->lang); + e->top->scblocks = e->scblocks; + recursive_wrap(e->top, pc); + } + + if ( e->flow ) { + /* Wrap using current width */ + e->top->w = event->width; + e->top->h = 0.0; /* To be updated in a moment */ + e->top->x = 0.0; + e->top->y = 0.0; + /* Only the top level needs to be wrapped */ + wrap_frame(e->top, pc); + } + + update_size(e); + + g_object_unref(pc); + + return FALSE; +} + + +static void emit_change_sig(SCEditor *e) +{ + g_signal_emit_by_name(e, "changed"); +} + + +void sc_editor_set_flow(SCEditor *e, int flow) +{ + e->flow = flow; +} + + +static void sc_editor_set_property(GObject *obj, guint id, const GValue *val, + GParamSpec *spec) +{ + SCEditor *e = SC_EDITOR(obj); + + switch ( id ) { + + case SCEDITOR_VPOL : + e->vpol = g_value_get_enum(val); + break; + + case SCEDITOR_HPOL : + e->hpol = g_value_get_enum(val); + break; + + case SCEDITOR_VADJ : + e->vadj = g_value_get_object(val); + set_vertical_params(e); + if ( e->vadj != NULL ) { + g_signal_connect(G_OBJECT(e->vadj), "value-changed", + G_CALLBACK(vertical_adjust), e); + } + break; + + case SCEDITOR_HADJ : + e->hadj = g_value_get_object(val); + set_horizontal_params(e); + if ( e->hadj != NULL ) { + g_signal_connect(G_OBJECT(e->hadj), "value-changed", + G_CALLBACK(horizontal_adjust), e); + } + break; + + default : + printf("setting %i\n", id); + break; + + } +} + + +static void sc_editor_get_property(GObject *obj, guint id, GValue *val, + GParamSpec *spec) +{ + SCEditor *e = SC_EDITOR(obj); + + switch ( id ) { + + case SCEDITOR_VADJ : + g_value_set_object(val, e->vadj); + break; + + case SCEDITOR_HADJ : + g_value_set_object(val, e->hadj); + break; + + case SCEDITOR_VPOL : + g_value_set_enum(val, e->vpol); + break; + + case SCEDITOR_HPOL : + g_value_set_enum(val, e->hpol); + break; + + default : + G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, id, spec); + break; + + } +} + + +static GtkSizeRequestMode get_request_mode(GtkWidget *widget) +{ + return GTK_SIZE_REQUEST_CONSTANT_SIZE; +} + + +static void get_preferred_width(GtkWidget *widget, gint *min, gint *natural) +{ + SCEditor *e = SC_EDITOR(widget); + if ( e->flow ) { + *min = 100; + *natural = 640; + } else { + *min = e->w; + *natural = e->w; + } +} + + +static void get_preferred_height(GtkWidget *widget, gint *min, gint *natural) +{ + SCEditor *e = SC_EDITOR(widget); + if ( e->flow ) { + *min = 1000; + *natural = 1000; + } else { + *min = e->h; + *natural = e->h; + } +} + + +static void sc_editor_class_init(SCEditorClass *klass) +{ + GObjectClass *goc = G_OBJECT_CLASS(klass); + goc->set_property = sc_editor_set_property; + goc->get_property = sc_editor_get_property; + g_object_class_override_property(goc, SCEDITOR_VADJ, "vadjustment"); + g_object_class_override_property(goc, SCEDITOR_HADJ, "hadjustment"); + g_object_class_override_property(goc, SCEDITOR_VPOL, "vscroll-policy"); + g_object_class_override_property(goc, SCEDITOR_HPOL, "hscroll-policy"); + + GTK_WIDGET_CLASS(klass)->get_request_mode = get_request_mode; + GTK_WIDGET_CLASS(klass)->get_preferred_width = get_preferred_width; + GTK_WIDGET_CLASS(klass)->get_preferred_height = get_preferred_height; + GTK_WIDGET_CLASS(klass)->get_preferred_height_for_width = NULL; + + g_signal_new("changed", SC_TYPE_EDITOR, G_SIGNAL_RUN_LAST, 0, + NULL, NULL, NULL, G_TYPE_NONE, 0); +} + + +static void sc_editor_init(SCEditor *e) +{ + e->vpol = GTK_SCROLL_NATURAL; + e->hpol = GTK_SCROLL_NATURAL; + e->vadj = gtk_adjustment_new(0, 0, 100, 1, 10, 10); + e->hadj = gtk_adjustment_new(0, 0, 100, 1, 10, 10); +} + + +void sc_editor_set_background(SCEditor *e, double r, double g, double b) +{ + e->bgcol[0] = r; + e->bgcol[1] = g; + e->bgcol[2] = b; +} + + +void sc_editor_ensure_cursor(SCEditor *e) +{ + if ( e->cursor_frame != NULL ) return; + e->cursor_frame = e->top; + e->cpos.para = 0; + e->cpos.pos = 0; + e->cpos.trail = 0; + e->selection = NULL; +} + + +static void sc_editor_remove_cursor(SCEditor *e) +{ + e->cursor_frame = NULL; + e->cpos.para = 0; + e->cpos.pos = 0; + e->cpos.trail = 0; + e->selection = NULL; +} + + +/* (Re-)run the entire rendering pipeline. + * NB "full" means "full". All frame, line and box handles will become + * invalid. The cursor position will be unset. */ +static void full_rerender(SCEditor *e) +{ + PangoContext *pc; + + frame_free(e->top); + sc_editor_remove_cursor(e); + + pc = gdk_pango_context_get(); + + e->top = interp_and_shape(e->scblocks, e->stylesheet, e->cbl, + e->is, e->slidenum, + pc, e->log_w, 0.0, e->lang); + + e->top->x = 0.0; + e->top->y = 0.0; + e->top->w = e->w; + e->top->h = 0.0; /* To be updated in a moment */ + + recursive_wrap(e->top, pc); + update_size(e); + + sc_editor_redraw(e); + + g_object_unref(pc); +} + + +void sc_editor_redraw(SCEditor *e) +{ + gint w, h; + + w = gtk_widget_get_allocated_width(GTK_WIDGET(e)); + h = gtk_widget_get_allocated_height(GTK_WIDGET(e)); + + gtk_widget_queue_draw_area(GTK_WIDGET(e), 0, 0, w, h); +} + + +static void paste_storycode_received(GtkClipboard *cb, GtkSelectionData *seldata, + gpointer vp) +{ + SCEditor *e = vp; + SCBlock *nf; + const guchar *t; + + t = gtk_selection_data_get_data(seldata); + + printf("received storycode paste\n"); + printf("'%s'\n", t); + if ( t == NULL ) return; + + /* FIXME: It might not be a new frame */ + nf = sc_parse((char *)t); + show_sc_blocks(nf); + sc_block_append_block(sc_block_child(e->scblocks), nf); + full_rerender(e); +} + + +static void paste_text_received(GtkClipboard *cb, GtkSelectionData *seldata, + gpointer vp) +{ + SCEditor *e = vp; + SCBlock *bl; + guchar *t; + SCBlock *cur_bl; + size_t cur_sc_pos; + size_t offs; + Paragraph *para; + + t = gtk_selection_data_get_text(seldata); + + printf("received text paste\n"); + printf("'%s'\n", t); + if ( t == NULL ) return; + + bl = sc_parse((char *)t); + + if ( e->cursor_frame == NULL ) { + fprintf(stderr, _("No frame selected for paste\n")); + return; + } + + para = e->cursor_frame->paras[e->cpos.para]; + offs = pos_trail_to_offset(para, e->cpos.pos, e->cpos.trail); + + get_sc_pos(e->cursor_frame, e->cpos.para, offs, &cur_bl, &cur_sc_pos); + sc_insert_block(cur_bl, cur_sc_pos, bl); + full_rerender(e); +} + + +static void paste_targets_received(GtkClipboard *cb, GdkAtom *targets, + gint n_targets, gpointer vp) +{ + SCEditor *e = vp; + int i; + int have_sc = 0; + int index_sc, index_text; + int have_text = 0; + + if ( targets == NULL ) { + fprintf(stderr, "No paste targets offered.\n"); + return; + } + + for ( i=0; iscblocks), nf); + full_rerender(e); +} + + +static void clipboard_get(GtkClipboard *cb, GtkSelectionData *seldata, + guint info, gpointer data) +{ + char *t = data; + + printf("clipboard get\n"); + + if ( info == 0 ) { + printf("sending SC frame\n"); + gtk_selection_data_set(seldata, + gtk_selection_data_get_target(seldata), + 8, (const guchar *)t, strlen(t)+1); + } else { + GdkAtom target; + gchar *name; + target = gtk_selection_data_get_target(seldata); + name = gdk_atom_name(target); + fprintf(stderr, "Don't know what to send for %s\n", name); + g_free(name); + } +} + + +static void clipboard_clear(GtkClipboard *cb, gpointer data) +{ + free(data); +} + + +void sc_editor_copy_selected_frame(SCEditor *e) +{ + char *t; + GtkClipboard *cb; + GdkAtom atom; + GtkTargetEntry targets[1]; + + if ( e->selection == NULL ) return; + + atom = gdk_atom_intern("CLIPBOARD", FALSE); + if ( atom == GDK_NONE ) return; + + cb = gtk_clipboard_get(atom); + + targets[0].target = "text/x-storycode"; + targets[0].flags = 0; + targets[0].info = 0; + + /* FIXME: Offer image, PDF etc? */ + + printf("copying frame\n"); + + t = serialise_sc_block(e->selection->scblocks); + + gtk_clipboard_set_with_data(cb, targets, 1, + clipboard_get, clipboard_clear, t); +} + + +static void copy_selection(SCEditor *e) +{ + char *t; + GtkClipboard *cb; + GdkAtom atom; + GtkTargetEntry targets[1]; + SCBlock *bl; + + if ( e->selection == NULL ) return; + + atom = gdk_atom_intern("CLIPBOARD", FALSE); + if ( atom == GDK_NONE ) return; + + cb = gtk_clipboard_get(atom); + + + targets[0].target = "text/x-storycode"; + targets[0].flags = 0; + targets[0].info = 0; + + printf("copying selection\n"); + + bl = block_at_cursor(e->cursor_frame, e->cpos.para, 0); + if ( bl == NULL ) return; + + t = serialise_sc_block(bl); + + gtk_clipboard_set_with_data(cb, targets, 1, + clipboard_get, clipboard_clear, t); +} + + +void sc_editor_delete_selected_frame(SCEditor *e) +{ + SCBlock *scb_old = e->scblocks; + sc_block_delete(&e->scblocks, e->selection->scblocks); + assert(scb_old == e->scblocks); + full_rerender(e); + emit_change_sig(e); +} + + +static gint destroy_sig(GtkWidget *window, SCEditor *e) +{ + return 0; +} + + +static void draw_editing_box(cairo_t *cr, struct frame *fr) +{ + const double dash[] = {2.0, 2.0}; + double xmin, ymin, width, height; + double ptot_w, ptot_h; + + xmin = fr->x; + ymin = fr->y; + width = fr->w; + height = fr->h; + + cairo_new_path(cr); + cairo_rectangle(cr, xmin, ymin, width, height); + cairo_set_source_rgb(cr, 0.0, 0.69, 1.0); + cairo_set_line_width(cr, 0.5); + cairo_stroke(cr); + + cairo_new_path(cr); + ptot_w = fr->pad_l + fr->pad_r; + ptot_h = fr->pad_t + fr->pad_b; + cairo_rectangle(cr, xmin+fr->pad_l, ymin+fr->pad_t, + width-ptot_w, height-ptot_h); + cairo_set_dash(cr, dash, 2, 0.0); + cairo_set_source_rgb(cr, 0.0, 0.0, 0.0); + cairo_set_line_width(cr, 0.1); + cairo_stroke(cr); + + cairo_set_dash(cr, NULL, 0, 0.0); +} + + +static void draw_para_highlight(cairo_t *cr, struct frame *fr, int cursor_para) +{ + double cx, cy, w, h; + + if ( get_para_highlight(fr, cursor_para, &cx, &cy, &w, &h) != 0 ) { + return; + } + + cairo_new_path(cr); + cairo_rectangle(cr, cx+fr->x, cy+fr->y, w, h); + cairo_set_source_rgba(cr, 0.7, 0.7, 1.0, 0.5); + cairo_set_line_width(cr, 5.0); + cairo_stroke(cr); +} + + +static void draw_caret(cairo_t *cr, struct frame *fr, struct edit_pos cpos, + int hgh) +{ + double cx, clow, chigh, h; + const double t = 1.8; + size_t offs; + Paragraph *para; + + if ( hgh ) { + draw_para_highlight(cr, fr, cpos.para); + return; + } + + assert(fr != NULL); + + para = fr->paras[cpos.para]; + if ( para_type(para) != PARA_TYPE_TEXT ) { + draw_para_highlight(cr, fr, cpos.para); + return; + } + + offs = pos_trail_to_offset(para, cpos.pos, cpos.trail); + get_cursor_pos(fr, cpos.para, offs, &cx, &clow, &h); + + cx += fr->x; + clow += fr->y; + chigh = clow + h; + + cairo_move_to(cr, cx, clow); + cairo_line_to(cr, cx, chigh); + + cairo_move_to(cr, cx-t, clow-t); + cairo_line_to(cr, cx, clow); + cairo_move_to(cr, cx+t, clow-t); + cairo_line_to(cr, cx, clow); + + cairo_move_to(cr, cx-t, chigh+t); + cairo_line_to(cr, cx, chigh); + cairo_move_to(cr, cx+t, chigh+t); + cairo_line_to(cr, cx, chigh); + + cairo_set_source_rgb(cr, 0.86, 0.0, 0.0); + cairo_set_line_width(cr, 1.0); + cairo_stroke(cr); +} + + +static void draw_resize_handle(cairo_t *cr, double x, double y) +{ + cairo_new_path(cr); + cairo_rectangle(cr, x, y, 20.0, 20.0); + cairo_set_source_rgba(cr, 0.9, 0.9, 0.9, 0.5); + cairo_fill(cr); +} + + +static void draw_overlay(cairo_t *cr, SCEditor *e) +{ + if ( e->selection != NULL ) { + + double x, y, w, h; + + draw_editing_box(cr, e->selection); + + x = e->selection->x; + y = e->selection->y; + w = e->selection->w; + h = e->selection->h; + + if ( e->selection->resizable ) { + /* Draw resize handles */ + draw_resize_handle(cr, x, y+h-20.0); + draw_resize_handle(cr, x+w-20.0, y); + draw_resize_handle(cr, x, y); + draw_resize_handle(cr, x+w-20.0, y+h-20.0); + } + + draw_caret(cr, e->cursor_frame, e->cpos, e->para_highlight); + + } + + if ( (e->drag_status == DRAG_STATUS_DRAGGING) + && ((e->drag_reason == DRAG_REASON_CREATE) + || (e->drag_reason == DRAG_REASON_IMPORT)) ) + { + cairo_new_path(cr); + cairo_rectangle(cr, e->start_corner_x, e->start_corner_y, + e->drag_corner_x - e->start_corner_x, + e->drag_corner_y - e->start_corner_y); + cairo_set_source_rgb(cr, 0.5, 0.5, 0.5); + cairo_set_line_width(cr, 0.5); + cairo_stroke(cr); + } + + if ( (e->drag_status == DRAG_STATUS_DRAGGING) + && ((e->drag_reason == DRAG_REASON_RESIZE) + || (e->drag_reason == DRAG_REASON_MOVE)) ) + { + cairo_new_path(cr); + cairo_rectangle(cr, e->box_x, e->box_y, + e->box_width, e->box_height); + cairo_set_source_rgb(cr, 0.5, 0.5, 0.5); + cairo_set_line_width(cr, 0.5); + cairo_stroke(cr); + } +} + + +static gboolean draw_sig(GtkWidget *da, cairo_t *cr, SCEditor *e) +{ + /* Ultimate background */ + if ( e->bg_pixbuf != NULL ) { + gdk_cairo_set_source_pixbuf(cr, e->bg_pixbuf, 0.0, 0.0); + cairo_pattern_t *patt = cairo_get_source(cr); + cairo_pattern_set_extend(patt, CAIRO_EXTEND_REPEAT); + cairo_paint(cr); + } else { + cairo_set_source_rgba(cr, 0.8, 0.8, 1.0, 1.0); + cairo_paint(cr); + } + + cairo_translate(cr, e->border_offs_x, e->border_offs_y); + cairo_translate(cr, -e->h_scroll_pos, -e->scroll_pos); + cairo_scale(cr, e->view_scale, e->view_scale); + + /* Rendering background */ + cairo_set_source_rgba(cr, 1.0, 1.0, 1.0, 1.0); + cairo_rectangle(cr, 0.0, 0.0, e->log_w, e->log_h); + cairo_fill(cr); + + /* Contents */ + recursive_draw(e->top, cr, e->is, + e->scroll_pos/e->view_scale, + (e->scroll_pos + e->visible_height)/e->view_scale); + + /* Editing overlay */ + draw_overlay(cr, e); + + return FALSE; +} + + +SCBlock *split_paragraph_at_cursor(SCEditor *e) +{ + size_t offs; + Paragraph *para; + + if ( e->cursor_frame == NULL ) return NULL; + + para = e->cursor_frame->paras[e->cpos.para]; + offs = pos_trail_to_offset(para, e->cpos.pos, e->cpos.trail); + return split_paragraph(e->cursor_frame, e->cpos.para, offs, e->pc); +} + + +static void check_cursor_visible(SCEditor *e) +{ + double x, y, h; + size_t offs; + Paragraph *para; + + if ( e->cursor_frame == NULL ) return; + + para = e->cursor_frame->paras[e->cpos.para]; + offs = pos_trail_to_offset(para, e->cpos.pos, e->cpos.trail); + get_cursor_pos(e->cursor_frame, e->cpos.para, offs, &x, &y, &h); + + /* Off the bottom? */ + if ( y - e->scroll_pos + h > e->visible_height ) { + e->scroll_pos = y + h - e->visible_height; + e->scroll_pos += e->cursor_frame->pad_b; + } + + /* Off the top? */ + if ( y < e->scroll_pos ) { + e->scroll_pos = y - e->cursor_frame->pad_t; + } +} + + +static void do_backspace(struct frame *fr, SCEditor *e) +{ + double wrapw = e->cursor_frame->w - e->cursor_frame->pad_l - e->cursor_frame->pad_r; + + if ( e->sel_active ) { + + /* Delete the selected block */ + delete_text_from_frame(e->cursor_frame, e->sel_start, e->sel_end, wrapw); + + /* Cursor goes at start of deletion */ + sort_positions(&e->sel_start, &e->sel_end); + e->cpos = e->sel_start; + e->sel_active = 0; + + } else { + + if ( para_type(e->cursor_frame->paras[e->cpos.para]) == PARA_TYPE_TEXT ) { + + /* Delete one character */ + struct edit_pos p1, p2; + + p1 = e->cpos; + + p2 = p1; + + cursor_moveh(e->cursor_frame, &p2, -1); + show_edit_pos(p1); + show_edit_pos(p2); + + delete_text_from_frame(e->cursor_frame, p1, p2, wrapw); + e->cpos = p2; + + } else { + + /* FIXME: Implement this */ + fprintf(stderr, "Deleting non-text paragraph\n"); + + } + + } + + emit_change_sig(e); + sc_editor_redraw(e); +} + + +static void insert_text(char *t, SCEditor *e) +{ + Paragraph *para; + + if ( e->cursor_frame == NULL ) return; + + if ( e->sel_active ) { + do_backspace(e->cursor_frame, e); + } + + if ( strcmp(t, "\n") == 0 ) { + split_paragraph_at_cursor(e); + if ( e->flow ) update_size(e); + cursor_moveh(e->cursor_frame, &e->cpos, +1); + check_cursor_visible(e); + emit_change_sig(e); + sc_editor_redraw(e); + return; + } + + para = e->cursor_frame->paras[e->cpos.para]; + + /* Is this paragraph even a text one? */ + if ( para_type(para) == PARA_TYPE_TEXT ) { + + size_t off; + + /* Yes. The "easy" case */ + + if ( !position_editable(e->cursor_frame, e->cpos) ) { + fprintf(stderr, "Position not editable\n"); + return; + } + + off = pos_trail_to_offset(para, e->cpos.pos, e->cpos.trail); + insert_text_in_paragraph(para, off, t); + wrap_paragraph(para, NULL, + e->cursor_frame->w - e->cursor_frame->pad_l + - e->cursor_frame->pad_r, 0, 0); + if ( e->flow ) update_size(e); + + cursor_moveh(e->cursor_frame, &e->cpos, +1); + + } else { + + SCBlock *bd; + SCBlock *ad; + Paragraph *pnew; + + bd = para_scblock(para); + if ( bd == NULL ) { + fprintf(stderr, "No SCBlock for para\n"); + return; + } + + /* No. Create a new text paragraph straight afterwards */ + ad = sc_block_insert_after(bd, NULL, NULL, strdup(t)); + if ( ad == NULL ) { + fprintf(stderr, "Failed to add SCBlock\n"); + return; + } + + pnew = insert_paragraph(e->cursor_frame, e->cpos.para); + if ( pnew == NULL ) { + fprintf(stderr, "Failed to insert paragraph\n"); + return; + } + add_run(pnew, ad, e->cursor_frame->fontdesc, + e->cursor_frame->col, NULL); + + wrap_frame(e->cursor_frame, e->pc); + + e->cpos.para += 1; + e->cpos.pos = 0; + e->cpos.trail = 1; + + } + + emit_change_sig(e); + check_cursor_visible(e); + sc_editor_redraw(e); +} + + +static gboolean im_commit_sig(GtkIMContext *im, gchar *str, + SCEditor *e) +{ + insert_text(str, e); + return FALSE; +} + + +static int within_frame(struct frame *fr, double x, double y) +{ + if ( fr == NULL ) return 0; + if ( x < fr->x ) return 0; + if ( y < fr->y ) return 0; + if ( x > fr->x + fr->w ) return 0; + if ( y > fr->y + fr->h ) return 0; + return 1; +} + + +static struct frame *find_frame_at_position(struct frame *fr, + double x, double y) +{ + int i; + + for ( i=0; inum_children; i++ ) { + + if ( within_frame(fr->children[i], x, y) ) { + return find_frame_at_position(fr->children[i], x, y); + } + + } + + if ( within_frame(fr, x, y) ) return fr; + return NULL; +} + + +static enum corner which_corner(double xp, double yp, struct frame *fr) +{ + double x, y; /* Relative to object position */ + + x = xp - fr->x; + y = yp - fr->y; + + if ( x < 0.0 ) return CORNER_NONE; + if ( y < 0.0 ) return CORNER_NONE; + if ( x > fr->w ) return CORNER_NONE; + if ( y > fr->h ) return CORNER_NONE; + + /* Top left? */ + if ( (x<20.0) && (y<20.0) ) return CORNER_TL; + if ( (x>fr->w-20.0) && (y<20.0) ) return CORNER_TR; + if ( (x<20.0) && (y>fr->h-20.0) ) return CORNER_BL; + if ( (x>fr->w-20.0) && (y>fr->h-20.0) ) return CORNER_BR; + + return CORNER_NONE; +} + + +static void calculate_box_size(struct frame *fr, SCEditor *e, + double x, double y) +{ + double ddx, ddy, dlen, mult; + double vx, vy, dbx, dby; + + ddx = x - e->start_corner_x; + ddy = y - e->start_corner_y; + + if ( !fr->is_image ) { + + switch ( e->drag_corner ) { + + case CORNER_BR : + e->box_x = fr->x; + e->box_y = fr->y; + e->box_width = fr->w + ddx; + e->box_height = fr->h + ddy; + break; + + case CORNER_BL : + e->box_x = fr->x + ddx; + e->box_y = fr->y; + e->box_width = fr->w - ddx; + e->box_height = fr->h + ddy; + break; + + case CORNER_TL : + e->box_x = fr->x + ddx; + e->box_y = fr->y + ddy; + e->box_width = fr->w - ddx; + e->box_height = fr->h - ddy; + break; + + case CORNER_TR : + e->box_x = fr->x; + e->box_y = fr->y + ddy; + e->box_width = fr->w + ddx; + e->box_height = fr->h - ddy; + break; + + case CORNER_NONE : + break; + + } + return; + + + } + + switch ( e->drag_corner ) { + + case CORNER_BR : + vx = fr->w; + vy = fr->h; + break; + + case CORNER_BL : + vx = -fr->w; + vy = fr->h; + break; + + case CORNER_TL : + vx = -fr->w; + vy = -fr->h; + break; + + case CORNER_TR : + vx = fr->w; + vy = -fr->h; + break; + + case CORNER_NONE : + default: + vx = 0.0; + vy = 0.0; + break; + + } + + dlen = (ddx*vx + ddy*vy) / e->diagonal_length; + mult = (dlen+e->diagonal_length) / e->diagonal_length; + + e->box_width = fr->w * mult; + e->box_height = fr->h * mult; + dbx = e->box_width - fr->w; + dby = e->box_height - fr->h; + + if ( e->box_width < 40.0 ) { + mult = 40.0 / fr->w; + } + if ( e->box_height < 40.0 ) { + mult = 40.0 / fr->h; + } + e->box_width = fr->w * mult; + e->box_height = fr->h * mult; + dbx = e->box_width - fr->w; + dby = e->box_height - fr->h; + + switch ( e->drag_corner ) { + + case CORNER_BR : + e->box_x = fr->x; + e->box_y = fr->y; + break; + + case CORNER_BL : + e->box_x = fr->x - dbx; + e->box_y = fr->y; + break; + + case CORNER_TL : + e->box_x = fr->x - dbx; + e->box_y = fr->y - dby; + break; + + case CORNER_TR : + e->box_x = fr->x; + e->box_y = fr->y - dby; + break; + + case CORNER_NONE : + break; + + } +} + + +static void check_paragraph(struct frame *fr, PangoContext *pc, + SCBlock *scblocks) +{ + if ( fr->n_paras > 0 ) return; + Paragraph *para = last_para(fr); + + if ( scblocks == NULL ) { + /* We have no SCBlocks at all! Better create one... */ + scblocks = sc_parse(""); + fr->scblocks = scblocks; + } + + /* We are creating the first paragraph. It uses the last SCBlock + * in the chain */ + while ( sc_block_next(scblocks) != NULL ) { + scblocks = sc_block_next(scblocks); + } + scblocks = sc_block_append(scblocks, NULL, NULL, strdup(""), NULL); + + add_run(para, scblocks, fr->fontdesc, fr->col, NULL); + wrap_paragraph(para, pc, fr->w - fr->pad_l - fr->pad_r, 0, 0); +} + + +static void rewrap_paragraph_range(struct frame *fr, int a, int b, + struct edit_pos sel_start, + struct edit_pos sel_end, + int sel_active) +{ + int i; + int sel_s, sel_e; + Paragraph *para; + + if ( a > b ) { + int t = a; + a = b; b = t; + } + + if ( fr == NULL ) return; + if ( fr->paras == NULL ) return; + + sort_positions(&sel_start, &sel_end); + + //printf("frame %p\n", fr); + //printf("start: "); + //show_edit_pos(sel_start); + //printf(" end: "); + //show_edit_pos(sel_end); + + para = fr->paras[sel_start.para]; + sel_s = pos_trail_to_offset(para, sel_start.pos, sel_start.trail); + para = fr->paras[sel_end.para]; + sel_e = pos_trail_to_offset(para, sel_end.pos, sel_end.trail); + + for ( i=a; i<=b; i++ ) { + size_t srt, end; + if ( sel_active ) { + if ( i == sel_start.para ) { + srt = sel_s; + } else { + srt = 0; + } + if ( i == sel_end.para ) { + end = sel_e; + } else { + end = G_MAXUINT; + } + if ( i > sel_start.para && i < sel_end.para ) { + end = G_MAXUINT; + } + } else { + srt = 0; + end = 0; + } + wrap_paragraph(fr->paras[i], NULL, + fr->w - fr->pad_l - fr->pad_r, srt, end); + } +} + + +static void unset_selection(SCEditor *e) +{ + int a, b; + + if ( !e->sel_active ) return; + + a = e->sel_start.para; + b = e->sel_end.para; + if ( a > b ) { + a = e->sel_end.para; + b = e->sel_start.para; + } + e->sel_active = 0; + rewrap_paragraph_range(e->cursor_frame, a, b, e->sel_start, e->sel_end, 0); +} + + +static gboolean button_press_sig(GtkWidget *da, GdkEventButton *event, + SCEditor *e) +{ + enum corner c; + gdouble x, y; + struct frame *clicked; + int shift; + + x = event->x - e->border_offs_x; + y = event->y - e->border_offs_y + e->scroll_pos; + x /= e->view_scale; + y /= e->view_scale; + shift = event->state & GDK_SHIFT_MASK; + + if ( within_frame(e->selection, x, y) ) { + clicked = e->selection; + } else { + clicked = find_frame_at_position(e->top, x, y); + } + + /* Clicked within the currently selected frame + * -> resize, move or select text */ + if ( (e->selection != NULL) && (clicked == e->selection) ) { + + struct frame *fr; + + fr = e->selection; + + /* Within the resizing region? */ + c = which_corner(x, y, fr); + if ( (c != CORNER_NONE) && fr->resizable && shift ) { + + e->drag_reason = DRAG_REASON_RESIZE; + e->drag_corner = c; + + e->start_corner_x = x; + e->start_corner_y = y; + e->diagonal_length = pow(fr->w, 2.0); + e->diagonal_length += pow(fr->h, 2.0); + e->diagonal_length = sqrt(e->diagonal_length); + + calculate_box_size(fr, e, x, y); + + e->drag_status = DRAG_STATUS_COULD_DRAG; + e->drag_reason = DRAG_REASON_RESIZE; + + } else { + + /* Position cursor and prepare for possible drag */ + e->cursor_frame = clicked; + check_paragraph(e->cursor_frame, e->pc, sc_block_child(fr->scblocks)); + find_cursor(clicked, x-fr->x, y-fr->y, &e->cpos); + ensure_run(e->cursor_frame, e->cpos); + + e->start_corner_x = x; + e->start_corner_y = y; + + if ( event->type == GDK_2BUTTON_PRESS ) { + check_callback_click(e->cursor_frame, e->cpos.para); + } + + if ( fr->resizable && shift ) { + e->drag_status = DRAG_STATUS_COULD_DRAG; + e->drag_reason = DRAG_REASON_MOVE; + } else if ( !e->para_highlight ) { + e->drag_status = DRAG_STATUS_COULD_DRAG; + e->drag_reason = DRAG_REASON_TEXTSEL; + unset_selection(e); + find_cursor(clicked, x-fr->x, y-fr->y, &e->sel_start); + } + + } + + } else if ( (clicked == NULL) + || ( !e->top_editable && (clicked == e->top) ) ) + { + /* Clicked no object. Deselect old object. + * If shift held, set up for creating a new one. */ + e->selection = NULL; + unset_selection(e); + + if ( shift ) { + e->start_corner_x = x; + e->start_corner_y = y; + e->drag_status = DRAG_STATUS_COULD_DRAG; + e->drag_reason = DRAG_REASON_CREATE; + } else { + e->drag_status = DRAG_STATUS_NONE; + e->drag_reason = DRAG_REASON_NONE; + } + + } else { + + /* Clicked an existing frame, no immediate dragging */ + e->drag_status = DRAG_STATUS_COULD_DRAG; + e->drag_reason = DRAG_REASON_TEXTSEL; + unset_selection(e); + find_cursor(clicked, x-clicked->x, y-clicked->y, + &e->sel_start); + find_cursor(clicked, x-clicked->x, y-clicked->y, + &e->sel_end); + e->selection = clicked; + e->cursor_frame = clicked; + if ( clicked == e->top ) { + check_paragraph(e->cursor_frame, e->pc, clicked->scblocks); + } else { + check_paragraph(e->cursor_frame, e->pc, + sc_block_child(clicked->scblocks)); + } + find_cursor(clicked, x-clicked->x, y-clicked->y, &e->cpos); + ensure_run(e->cursor_frame, e->cpos); + + } + + gtk_widget_grab_focus(GTK_WIDGET(da)); + sc_editor_redraw(e); + return FALSE; +} + + +static gboolean motion_sig(GtkWidget *da, GdkEventMotion *event, + SCEditor *e) +{ + struct frame *fr = e->selection; + gdouble x, y; + + x = event->x - e->border_offs_x; + y = event->y - e->border_offs_y + e->scroll_pos; + x /= e->view_scale; + y /= e->view_scale; + + if ( e->drag_status == DRAG_STATUS_COULD_DRAG ) { + + /* We just got a motion signal, and the status was "could drag", + * therefore the drag has started. */ + e->drag_status = DRAG_STATUS_DRAGGING; + + } + + switch ( e->drag_reason ) { + + case DRAG_REASON_NONE : + break; + + case DRAG_REASON_CREATE : + e->drag_corner_x = x; + e->drag_corner_y = y; + sc_editor_redraw(e); + break; + + case DRAG_REASON_IMPORT : + /* Do nothing, handled by dnd_motion() */ + break; + + case DRAG_REASON_RESIZE : + calculate_box_size(fr, e, x, y); + sc_editor_redraw(e); + break; + + case DRAG_REASON_MOVE : + e->box_x = (fr->x - e->start_corner_x) + x; + e->box_y = (fr->y - e->start_corner_y) + y; + e->box_width = fr->w; + e->box_height = fr->h; + sc_editor_redraw(e); + break; + + case DRAG_REASON_TEXTSEL : + unset_selection(e); + find_cursor(fr, x-fr->x, y-fr->y, &e->sel_end); + rewrap_paragraph_range(fr, e->sel_start.para, e->sel_end.para, + e->sel_start, e->sel_end, 1); + find_cursor(fr, x-fr->x, y-fr->y, &e->cpos); + e->sel_active = !positions_equal(e->sel_start, e->sel_end); + sc_editor_redraw(e); + break; + + } + + gdk_event_request_motions(event); + return FALSE; +} + + +static struct frame *create_frame(SCEditor *e, double x, double y, + double w, double h) +{ + struct frame *parent; + struct frame *fr; + SCBlock *scblocks; + + parent = e->top; + + if ( w < 0.0 ) { + x += w; + w = -w; + } + + if ( h < 0.0 ) { + y += h; + h = -h; + } + + /* Add to frame structure */ + fr = add_subframe(parent); + + /* Add to SC */ + scblocks = sc_block_append_end(sc_block_child(e->scblocks), "f", NULL, NULL); + fr->scblocks = scblocks; + sc_block_append_inside(scblocks, NULL, NULL, strdup("")); + + fr->x = x; + fr->y = y; + fr->w = w; + fr->h = h; + fr->is_image = 0; + fr->empty = 1; + fr->resizable = 1; + + update_geom(fr); + + full_rerender(e); + return find_frame_with_scblocks(e->top, scblocks); +} + + +static void do_resize(SCEditor *e, double x, double y, double w, double h) +{ + struct frame *fr; + + assert(e->selection != NULL); + + if ( w < 0.0 ) { + w = -w; + x -= w; + } + + if ( h < 0.0 ) { + h = -h; + y -= h; + } + + fr = e->selection; + fr->x = x; + fr->y = y; + fr->w = w; + fr->h = h; + update_geom(fr); + + full_rerender(e); + sc_editor_redraw(e); +} + + +static gboolean button_release_sig(GtkWidget *da, GdkEventButton *event, + SCEditor *e) +{ + gdouble x, y; + struct frame *fr; + + x = event->x - e->border_offs_x; + y = event->y - e->border_offs_y; + x /= e->view_scale; + y /= e->view_scale; + + /* Not dragging? Then I don't care. */ + if ( e->drag_status != DRAG_STATUS_DRAGGING ) return FALSE; + + e->drag_corner_x = x; + e->drag_corner_y = y; + e->drag_status = DRAG_STATUS_NONE; + + switch ( e->drag_reason ) + { + + case DRAG_REASON_NONE : + printf("Release on pointless drag.\n"); + break; + + case DRAG_REASON_CREATE : + fr = create_frame(e, e->start_corner_x, e->start_corner_y, + e->drag_corner_x - e->start_corner_x, + e->drag_corner_y - e->start_corner_y); + if ( fr != NULL ) { + check_paragraph(fr, e->pc, sc_block_child(fr->scblocks)); + e->selection = fr; + e->cursor_frame = fr; + e->cpos.para = 0; + e->cpos.pos = 0; + e->cpos.trail = 0; + } else { + fprintf(stderr, _("Failed to create frame!\n")); + } + break; + + case DRAG_REASON_IMPORT : + /* Do nothing, handled in dnd_drop() or dnd_leave() */ + break; + + case DRAG_REASON_RESIZE : + do_resize(e, e->box_x, e->box_y, e->box_width, e->box_height); + break; + + case DRAG_REASON_MOVE : + do_resize(e, e->box_x, e->box_y, e->box_width, e->box_height); + break; + + case DRAG_REASON_TEXTSEL : + /* Do nothing (text is already selected) */ + break; + + } + + e->drag_reason = DRAG_REASON_NONE; + + gtk_widget_grab_focus(GTK_WIDGET(da)); + sc_editor_redraw(e); + return FALSE; +} + + +static gboolean key_press_sig(GtkWidget *da, GdkEventKey *event, + SCEditor *e) +{ + gboolean r; + int claim = 0; + + /* Throw the event to the IM context and let it sort things out */ + r = gtk_im_context_filter_keypress(GTK_IM_CONTEXT(e->im_context), + event); + if ( r ) return FALSE; /* IM ate it */ + + switch ( event->keyval ) { + + case GDK_KEY_Escape : + if ( !e->para_highlight ) { + sc_editor_remove_cursor(e); + sc_editor_redraw(e); + claim = 1; + } + break; + + case GDK_KEY_Left : + if ( e->selection != NULL ) { + cursor_moveh(e->cursor_frame, &e->cpos, -1); + sc_editor_redraw(e); + } + claim = 1; + break; + + case GDK_KEY_Right : + if ( e->selection != NULL ) { + cursor_moveh(e->cursor_frame, &e->cpos, +1); + sc_editor_redraw(e); + } + claim = 1; + break; + + case GDK_KEY_Up : + if ( e->selection != NULL ) { + cursor_moveh(e->cursor_frame, &e->cpos, -1); + sc_editor_redraw(e); + } + claim = 1; + break; + + case GDK_KEY_Down : + if ( e->selection != NULL ) { + cursor_moveh(e->cursor_frame, &e->cpos, +1); + sc_editor_redraw(e); + } + claim = 1; + break; + + + case GDK_KEY_Return : + im_commit_sig(NULL, "\n", e); + claim = 1; + break; + + case GDK_KEY_BackSpace : + if ( e->selection != NULL ) { + do_backspace(e->selection, e); + claim = 1; + } + break; + + case GDK_KEY_F5 : + full_rerender(e); + break; + + case GDK_KEY_F6 : + show_edit_pos(e->cpos); + break; + + case GDK_KEY_F7 : + if ( e->cursor_frame != NULL ) { + if ( event->state & GDK_CONTROL_MASK ) { + debug_paragraphs(e); + } else if ( event->state & GDK_SHIFT_MASK ) { + printf("Cursor frame block = %p\n", e->cursor_frame->scblocks); + printf("Editor top block = %p\n", e->scblocks); + show_sc_block(e->cursor_frame->scblocks, ""); + } else { + open_debugger(e->cursor_frame); + } + } else { + if ( event->state & GDK_SHIFT_MASK ) { + printf("Debugging the top frame:\n"); + printf("Editor top block = %p\n", e->scblocks); + show_sc_block(e->top->scblocks, ""); + } + } + break; + + case GDK_KEY_C : + case GDK_KEY_c : + if ( event->state == GDK_CONTROL_MASK ) { + copy_selection(e); + } + break; + + case GDK_KEY_V : + case GDK_KEY_v : + if ( event->state == GDK_CONTROL_MASK ) { + sc_editor_paste(e); + } + break; + + + } + + if ( claim ) return TRUE; + return FALSE; +} + + +static gboolean dnd_motion(GtkWidget *widget, GdkDragContext *drag_context, + gint x, gint y, guint time, SCEditor *e) +{ + GdkAtom target; + + /* If we haven't already requested the data, do so now */ + if ( !e->drag_preview_pending && !e->have_drag_data ) { + + target = gtk_drag_dest_find_target(widget, drag_context, NULL); + + if ( target != GDK_NONE ) { + gtk_drag_get_data(widget, drag_context, target, time); + e->drag_preview_pending = 1; + } else { + e->import_acceptable = 0; + gdk_drag_status(drag_context, 0, time); + } + + } + + if ( e->have_drag_data && e->import_acceptable ) { + + gdk_drag_status(drag_context, GDK_ACTION_LINK, time); + e->start_corner_x = x - e->import_width/2.0; + e->start_corner_y = y - e->import_height/2.0; + e->drag_corner_x = x + e->import_width/2.0; + e->drag_corner_y = y + e->import_height/2.0; + + sc_editor_redraw(e); + + } + + return TRUE; +} + + +static gboolean dnd_drop(GtkWidget *widget, GdkDragContext *drag_context, + gint x, gint y, guint time, SCEditor *e) +{ + GdkAtom target; + + target = gtk_drag_dest_find_target(widget, drag_context, NULL); + + if ( target == GDK_NONE ) { + gtk_drag_finish(drag_context, FALSE, FALSE, time); + } else { + gtk_drag_get_data(widget, drag_context, target, time); + } + + return TRUE; +} + + +/* Scale the image down if it's a silly size */ +static void check_import_size(SCEditor *e) +{ + if ( e->import_width > e->w ) { + + int new_import_width; + + new_import_width = e->w/2; + e->import_height = (new_import_width * e->import_height) / + e->import_width; + e->import_width = new_import_width; + + } + + if ( e->import_height > e->h ) { + + int new_import_height; + + new_import_height = e->w/2; + e->import_width = (new_import_height*e->import_width) / + e->import_height; + e->import_height = new_import_height; + + } +} + + +static void dnd_receive(GtkWidget *widget, GdkDragContext *drag_context, + gint x, gint y, GtkSelectionData *seldata, + guint info, guint time, SCEditor *e) +{ + if ( e->drag_preview_pending ) { + + gchar *filename = NULL; + GdkPixbufFormat *f; + gchar **uris; + int w, h; + + e->have_drag_data = 1; + e->drag_preview_pending = 0; + uris = gtk_selection_data_get_uris(seldata); + if ( uris != NULL ) { + filename = g_filename_from_uri(uris[0], NULL, NULL); + } + g_strfreev(uris); + + if ( filename == NULL ) { + + /* This doesn't even look like a sensible URI. + * Bail out. */ + gdk_drag_status(drag_context, 0, time); + if ( e->drag_highlight ) { + gtk_drag_unhighlight(widget); + e->drag_highlight = 0; + } + e->import_acceptable = 0; + return; + + } + chomp(filename); + + f = gdk_pixbuf_get_file_info(filename, &w, &h); + g_free(filename); + + e->import_width = w; + e->import_height = h; + + if ( f == NULL ) { + + gdk_drag_status(drag_context, 0, time); + if ( e->drag_highlight ) { + gtk_drag_unhighlight(widget); + e->drag_highlight = 0; + } + e->drag_status = DRAG_STATUS_NONE; + e->drag_reason = DRAG_REASON_NONE; + e->import_acceptable = 0; + + } else { + + /* Looks like a sensible image */ + gdk_drag_status(drag_context, GDK_ACTION_PRIVATE, time); + e->import_acceptable = 1; + + if ( !e->drag_highlight ) { + gtk_drag_highlight(widget); + e->drag_highlight = 1; + } + + check_import_size(e); + e->drag_reason = DRAG_REASON_IMPORT; + e->drag_status = DRAG_STATUS_DRAGGING; + + } + + } else { + + gchar **uris; + char *filename = NULL; + + uris = gtk_selection_data_get_uris(seldata); + if ( uris != NULL ) { + filename = g_filename_from_uri(uris[0], NULL, NULL); + } + g_strfreev(uris); + + if ( filename != NULL ) { + + struct frame *fr; + char *opts; + size_t len; + int w, h; + + gtk_drag_finish(drag_context, TRUE, FALSE, time); + chomp(filename); + + w = e->drag_corner_x - e->start_corner_x; + h = e->drag_corner_y - e->start_corner_y; + + len = strlen(filename)+64; + opts = malloc(len); + if ( opts == NULL ) { + free(filename); + fprintf(stderr, "Failed to allocate SC\n"); + return; + } + snprintf(opts, len, "1fx1f+0+0,filename=\"%s\"", + filename); + + fr = create_frame(e, e->start_corner_x, + e->start_corner_y, w, h); + fr->is_image = 1; + fr->empty = 0; + sc_block_set_name(sc_block_child(fr->scblocks), strdup("image")); + sc_block_set_options(sc_block_child(fr->scblocks), opts); + full_rerender(e); + sc_editor_remove_cursor(e); + sc_editor_redraw(e); + free(filename); + + } else { + + gtk_drag_finish(drag_context, FALSE, FALSE, time); + + } + + } +} + + +static void dnd_leave(GtkWidget *widget, GdkDragContext *drag_context, + guint time, SCEditor *sceditor) +{ + if ( sceditor->drag_highlight ) { + gtk_drag_unhighlight(widget); + } + sceditor->have_drag_data = 0; + sceditor->drag_highlight = 0; + sceditor->drag_status = DRAG_STATUS_NONE; + sceditor->drag_reason = DRAG_REASON_NONE; +} + + +static gint realise_sig(GtkWidget *da, SCEditor *e) +{ + GdkWindow *win; + + /* Keyboard and input method stuff */ + e->im_context = gtk_im_multicontext_new(); + win = gtk_widget_get_window(GTK_WIDGET(e)); + gtk_im_context_set_client_window(GTK_IM_CONTEXT(e->im_context), win); + gdk_window_set_accept_focus(win, TRUE); + g_signal_connect(G_OBJECT(e->im_context), "commit", G_CALLBACK(im_commit_sig), e); + g_signal_connect(G_OBJECT(e), "key-press-event", G_CALLBACK(key_press_sig), e); + + /* FIXME: Can do this "properly" by setting up a separate font map */ + e->pc = gtk_widget_get_pango_context(GTK_WIDGET(e)); + + return FALSE; +} + + +void sc_editor_set_scblock(SCEditor *e, SCBlock *scblocks) +{ + e->scblocks = scblocks; + full_rerender(e); +} + + +static void update_size_request(SCEditor *e) +{ + gtk_widget_set_size_request(GTK_WIDGET(e), 0, e->h + 2.0*e->min_border); +} + + +void sc_editor_set_logical_size(SCEditor *e, double w, double h) +{ + e->log_w = w; + e->log_h = h; + if ( gtk_widget_get_mapped(GTK_WIDGET(e)) ) { + full_rerender(e); + sc_editor_redraw(e); + } +} + + +void sc_editor_set_slidenum(SCEditor *e, int slidenum) +{ + e->slidenum = slidenum; +} + + +void sc_editor_set_min_border(SCEditor *e, double min_border) +{ + e->min_border = min_border; + update_size_request(e); +} + + +void sc_editor_set_top_frame_editable(SCEditor *e, int top_frame_editable) +{ + e->top_editable = top_frame_editable; +} + + +void sc_editor_set_stylesheet(SCEditor *e, Stylesheet *stylesheet) +{ + e->stylesheet = stylesheet; +} + + +void sc_editor_set_callbacks(SCEditor *e, SCCallbackList *cbl) +{ + if ( e->cbl != NULL ) sc_callback_list_free(e->cbl); + e->cbl = cbl; +} + + +void sc_editor_set_para_highlight(SCEditor *e, int para_highlight) +{ + e->para_highlight = para_highlight; + sc_editor_redraw(e); +} + +int sc_editor_get_cursor_para(SCEditor *e) +{ + if ( e->cursor_frame == NULL ) return 0; + return e->cpos.para; +} + + +void *sc_editor_get_cursor_bvp(SCEditor *e) +{ + Paragraph *para; + if ( e->cursor_frame == NULL ) return 0; + para = e->cursor_frame->paras[e->cpos.para]; + return get_para_bvp(para); +} + + +void sc_editor_set_cursor_para(SCEditor *e, signed int pos) +{ + double h; + int i; + + if ( e->cursor_frame == NULL ) { + e->cursor_frame = e->top; + e->selection = e->top; + } + + if ( pos < 0 ) { + e->cpos.para = e->cursor_frame->n_paras - 1; + } else if ( pos >= e->cursor_frame->n_paras ) { + e->cpos.para = e->cursor_frame->n_paras - 1; + } else { + e->cpos.para = pos; + } + e->cpos.pos = 0; + e->cpos.trail = 0; + + h = 0; + for ( i=0; icpos.para; i++ ) { + h += paragraph_height(e->cursor_frame->paras[i]); + } + h += (paragraph_height(e->cursor_frame->paras[e->cpos.para]))/2; + e->scroll_pos = h - (e->visible_height/2); + set_vertical_params(e); + + sc_editor_redraw(e); +} + + +int sc_editor_get_num_paras(SCEditor *e) +{ + if ( e->cursor_frame == NULL ) return 1; + return e->cursor_frame->n_paras; +} + + +void sc_editor_set_scale(SCEditor *e, int scale) +{ + e->scale = scale; + if ( !scale ) { + e->view_scale = 1.0; + } +} + + +void sc_editor_set_imagestore(SCEditor *e, ImageStore *is) +{ + if ( e->is != NULL ) { + fprintf(stderr, "WARNING: Changing imagestore\n"); + } + e->is = is; +} + + +SCEditor *sc_editor_new(SCBlock *scblocks, Stylesheet *stylesheet, + PangoLanguage *lang, const char *storename) +{ + SCEditor *sceditor; + GtkTargetEntry targets[1]; + GError *err; + + sceditor = g_object_new(SC_TYPE_EDITOR, NULL); + + sceditor->scblocks = scblocks; + sceditor->w = 100; + sceditor->h = 100; + sceditor->log_w = 100; + sceditor->log_h = 100; + sceditor->border_offs_x = 0; + sceditor->border_offs_y = 0; + sceditor->is = NULL; + sceditor->slidenum = 0; + sceditor->min_border = 0.0; + sceditor->top_editable = 0; + sceditor->cbl = NULL; + sceditor->scroll_pos = 0; + sceditor->flow = 0; + sceditor->scale = 0; + sceditor->view_scale = 1.0; + sceditor->lang = lang; + + sceditor->para_highlight = 0; + sc_editor_remove_cursor(sceditor); + + sceditor->stylesheet = stylesheet; + + err = NULL; + sceditor->bg_pixbuf = gdk_pixbuf_new_from_resource("/uk/me/bitwiz/Colloquium/sky.png", + &err); + if ( sceditor->bg_pixbuf == NULL ) { + fprintf(stderr, _("Failed to load background: %s\n"), + err->message); + } + + gtk_widget_set_size_request(GTK_WIDGET(sceditor), + sceditor->w, sceditor->h); + + g_signal_connect(G_OBJECT(sceditor), "destroy", + G_CALLBACK(destroy_sig), sceditor); + g_signal_connect(G_OBJECT(sceditor), "realize", + G_CALLBACK(realise_sig), sceditor); + g_signal_connect(G_OBJECT(sceditor), "button-press-event", + G_CALLBACK(button_press_sig), sceditor); + g_signal_connect(G_OBJECT(sceditor), "button-release-event", + G_CALLBACK(button_release_sig), sceditor); + g_signal_connect(G_OBJECT(sceditor), "motion-notify-event", + G_CALLBACK(motion_sig), sceditor); + g_signal_connect(G_OBJECT(sceditor), "configure-event", + G_CALLBACK(resize_sig), sceditor); + + /* Drag and drop */ + targets[0].target = "text/uri-list"; + targets[0].flags = 0; + targets[0].info = 1; + gtk_drag_dest_set(GTK_WIDGET(sceditor), 0, targets, 1, + GDK_ACTION_PRIVATE); + g_signal_connect(sceditor, "drag-data-received", + G_CALLBACK(dnd_receive), sceditor); + g_signal_connect(sceditor, "drag-motion", + G_CALLBACK(dnd_motion), sceditor); + g_signal_connect(sceditor, "drag-drop", + G_CALLBACK(dnd_drop), sceditor); + g_signal_connect(sceditor, "drag-leave", + G_CALLBACK(dnd_leave), sceditor); + + gtk_widget_set_can_focus(GTK_WIDGET(sceditor), TRUE); + gtk_widget_add_events(GTK_WIDGET(sceditor), + GDK_POINTER_MOTION_HINT_MASK + | GDK_BUTTON1_MOTION_MASK + | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + | GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK + | GDK_SCROLL_MASK); + + g_signal_connect(G_OBJECT(sceditor), "draw", + G_CALLBACK(draw_sig), sceditor); + + gtk_widget_grab_focus(GTK_WIDGET(sceditor)); + + gtk_widget_show(GTK_WIDGET(sceditor)); + + return sceditor; +} diff --git a/src-old/sc_editor.h b/src-old/sc_editor.h new file mode 100644 index 0000000..d3c111b --- /dev/null +++ b/src-old/sc_editor.h @@ -0,0 +1,199 @@ +/* + * sc_editor.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 SC_EDITOR_H +#define SC_EDITOR_H + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include + +#include "frame.h" +#include "sc_interp.h" +#include "stylesheet.h" + +struct presentation; + + +#define SC_TYPE_EDITOR (sc_editor_get_type()) + +#define SC_EDITOR(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), \ + SC_TYPE_EDITOR, SCEditor)) + +#define SC_IS_EDITOR(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), \ + SC_TYPE_EDITOR)) + +#define SC_EDITOR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((obj), \ + SC_TYPE_EDITOR, SCEditorClass)) + +#define SC_IS_EDITOR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((obj), \ + SC_TYPE_EDITOR)) + +#define SC_EDITOR_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), \ + SC_TYPE_EDITOR, SCEditorClass)) + +enum drag_reason +{ + DRAG_REASON_NONE, + DRAG_REASON_CREATE, + DRAG_REASON_IMPORT, + DRAG_REASON_RESIZE, + DRAG_REASON_MOVE, + DRAG_REASON_TEXTSEL +}; + + +enum corner +{ + CORNER_NONE, + CORNER_TL, + CORNER_TR, + CORNER_BL, + CORNER_BR +}; + + +enum drag_status +{ + DRAG_STATUS_NONE, + DRAG_STATUS_COULD_DRAG, + DRAG_STATUS_DRAGGING, +}; + + +struct _sceditor +{ + GtkDrawingArea parent_instance; + PangoLanguage *lang; + + /*< private >*/ + GtkIMContext *im_context; + int w; /* Surface size in pixels */ + int h; + double log_w; /* Size of surface in "SC units" */ + double log_h; + SCBlock *scblocks; + Stylesheet *stylesheet; + ImageStore *is; + SCCallbackList *cbl; + struct frame *top; + int para_highlight; + + /* Redraw/scroll stuff */ + GtkScrollablePolicy hpol; + GtkScrollablePolicy vpol; + GtkAdjustment *hadj; + GtkAdjustment *vadj; + double scroll_pos; + double h_scroll_pos; + int visible_height; + int visible_width; + int flow; + int scale; /* Whether the SCEditor should scale to fit */ + double view_scale; /* The scale factor, if scale=1 */ + + /* Pointers to the frame currently being edited */ + struct frame *selection; + int top_editable; + + PangoContext *pc; + + /* Location of the cursor */ + struct frame *cursor_frame; + struct edit_pos cpos; + + /* Border surrounding actual slide within drawingarea */ + double border_offs_x; + double border_offs_y; + double min_border; + double bgcol[3]; + GdkPixbuf *bg_pixbuf; + + /* Rubber band boxes and related stuff */ + double start_corner_x; + double start_corner_y; + double drag_corner_x; + double drag_corner_y; + double diagonal_length; + double box_x; + double box_y; + double box_width; + double box_height; + enum drag_reason drag_reason; + enum drag_status drag_status; + enum corner drag_corner; + int sel_active; + struct edit_pos sel_start; /* Where the user dragged from */ + struct edit_pos sel_end; + + /* Stuff to do with drag and drop import of "content" */ + int drag_preview_pending; + int have_drag_data; + int drag_highlight; + double import_width; + double import_height; + int import_acceptable; + + /* Stuff that doesn't really belong here */ + int slidenum; +}; + +struct _sceditorclass +{ + GtkDrawingAreaClass parent_class; +}; + +typedef struct _sceditor SCEditor; +typedef struct _sceditorclass SCEditorClass; + +extern void sc_editor_set_scblock(SCEditor *e, SCBlock *scblocks); +extern void sc_editor_set_stylesheet(SCEditor *e, Stylesheet *stylesheet); +extern SCEditor *sc_editor_new(SCBlock *scblocks, Stylesheet *stylesheet, + PangoLanguage *lang, const char *storename); +extern void sc_editor_set_logical_size(SCEditor *e, double w, double h); +extern void sc_editor_set_flow(SCEditor *e, int flow); +extern void sc_editor_set_scale(SCEditor *e, int scale); +extern void sc_editor_redraw(SCEditor *e); +extern void sc_editor_set_background(SCEditor *e, double r, double g, double b); +extern void sc_editor_set_slidenum(SCEditor *e, int slidenum); +extern void sc_editor_set_min_border(SCEditor *e, double min_border); +extern void sc_editor_set_top_frame_editable(SCEditor *e, + int top_frame_editable); +extern void sc_editor_set_callbacks(SCEditor *e, SCCallbackList *cbl); +extern void sc_editor_paste(SCEditor *e); +extern void sc_editor_add_storycode(SCEditor *e, const char *sc); +extern void sc_editor_copy_selected_frame(SCEditor *e); +extern void sc_editor_delete_selected_frame(SCEditor *e); +extern void sc_editor_ensure_cursor(SCEditor *e); +extern SCBlock *split_paragraph_at_cursor(SCEditor *e); + +extern void sc_editor_set_imagestore(SCEditor *e, ImageStore *is); +extern void sc_editor_set_para_highlight(SCEditor *e, int para_highlight); +extern int sc_editor_get_cursor_para(SCEditor *e); +extern void *sc_editor_get_cursor_bvp(SCEditor *e); +extern void sc_editor_set_cursor_para(SCEditor *e, signed int pos); +extern int sc_editor_get_num_paras(SCEditor *e); + +#endif /* SC_EDITOR_H */ diff --git a/src-old/sc_interp.c b/src-old/sc_interp.c new file mode 100644 index 0000000..07f09a5 --- /dev/null +++ b/src-old/sc_interp.c @@ -0,0 +1,1148 @@ +/* + * sc_interp.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 + +#include "imagestore.h" +#include "sc_parse.h" +#include "sc_interp.h" +#include "presentation.h" +#include "utils.h" + + +struct sc_state +{ + PangoFontDescription *fontdesc; + PangoFont *font; + PangoAlignment alignment; + double col[4]; + int ascent; + int height; + float paraspace[4]; + char *constants[NUM_SC_CONSTANTS]; + + struct frame *fr; /* The current frame */ +}; + +struct _scinterp +{ + PangoContext *pc; + PangoLanguage *lang; + ImageStore *is; + + struct slide_constants *s_constants; + struct presentation_constants *p_constants; + + struct sc_state *state; + int j; /* Index of the current state */ + int max_state; + + SCCallbackList *cbl; +}; + +struct _sccallbacklist +{ + int n_callbacks; + int max_callbacks; + char **names; + SCCallbackBoxFunc *box_funcs; + SCCallbackDrawFunc *draw_funcs; + SCCallbackClickFunc *click_funcs; + void **vps; +}; + + +static int sc_interp_add_blocks(SCInterpreter *scin, SCBlock *bl, Stylesheet *ss) +{ + while ( bl != NULL ) { + if ( sc_interp_add_block(scin, bl, ss) ) return 1; + bl = sc_block_next(bl); + } + + return 0; +} + + +SCCallbackList *sc_callback_list_new() +{ + SCCallbackList *cbl; + + cbl = malloc(sizeof(struct _sccallbacklist)); + if ( cbl == NULL ) return NULL; + + cbl->names = calloc(8, sizeof(char *)); + if ( cbl->names == NULL ) { + free(cbl); + return NULL; + } + + cbl->box_funcs = calloc(8, sizeof(cbl->box_funcs[0])); + if ( cbl->box_funcs == NULL ) { + free(cbl->names); + free(cbl); + return NULL; + } + + cbl->draw_funcs = calloc(8, sizeof(cbl->draw_funcs[0])); + if ( cbl->draw_funcs == NULL ) { + free(cbl->box_funcs); + free(cbl->names); + free(cbl); + return NULL; + } + + cbl->click_funcs = calloc(8, sizeof(cbl->click_funcs[0])); + if ( cbl->click_funcs == NULL ) { + free(cbl->draw_funcs); + free(cbl->box_funcs); + free(cbl->names); + free(cbl); + return NULL; + } + + cbl->vps = calloc(8, sizeof(cbl->vps[0])); + if ( cbl->vps == NULL ) { + free(cbl->click_funcs); + free(cbl->draw_funcs); + free(cbl->box_funcs); + free(cbl->names); + free(cbl); + return NULL; + } + + cbl->max_callbacks = 8; + cbl->n_callbacks = 0; + + return cbl; +} + + +void sc_callback_list_free(SCCallbackList *cbl) +{ + int i; + + if ( cbl == NULL ) return; + + for ( i=0; in_callbacks; i++ ) { + free(cbl->names[i]); + } + + free(cbl->names); + free(cbl->box_funcs); + free(cbl->draw_funcs); + free(cbl->vps); + free(cbl); + +} + + +void sc_callback_list_add_callback(SCCallbackList *cbl, const char *name, + SCCallbackBoxFunc box_func, + SCCallbackDrawFunc draw_func, + SCCallbackClickFunc click_func, + void *vp) +{ + if ( cbl->n_callbacks == cbl->max_callbacks ) { + + SCCallbackBoxFunc *box_funcs_new; + SCCallbackDrawFunc *draw_funcs_new; + SCCallbackClickFunc *click_funcs_new; + char **names_new; + void **vps_new; + int mcn = cbl->max_callbacks + 8; + + names_new = realloc(cbl->names, mcn*sizeof(char *)); + box_funcs_new = realloc(cbl->box_funcs, + mcn*sizeof(SCCallbackBoxFunc)); + draw_funcs_new = realloc(cbl->draw_funcs, + mcn*sizeof(SCCallbackDrawFunc)); + click_funcs_new = realloc(cbl->click_funcs, + mcn*sizeof(SCCallbackClickFunc)); + vps_new = realloc(cbl->vps, mcn*sizeof(void *)); + + if ( (names_new == NULL) || (box_funcs_new == NULL) + || (vps_new == NULL) || (draw_funcs_new == NULL) + || (click_funcs_new == NULL) ) { + fprintf(stderr, "Failed to grow callback list\n"); + return; + } + + cbl->names = names_new; + cbl->box_funcs = box_funcs_new; + cbl->draw_funcs = draw_funcs_new; + cbl->click_funcs = click_funcs_new; + cbl->vps = vps_new; + cbl->max_callbacks = mcn; + + } + + cbl->names[cbl->n_callbacks] = strdup(name); + cbl->box_funcs[cbl->n_callbacks] = box_func; + cbl->draw_funcs[cbl->n_callbacks] = draw_func; + cbl->click_funcs[cbl->n_callbacks] = click_func; + cbl->vps[cbl->n_callbacks] = vp; + cbl->n_callbacks++; +} + + +void sc_interp_set_callbacks(SCInterpreter *scin, SCCallbackList *cbl) +{ + if ( scin->cbl != NULL ) { + fprintf(stderr, "WARNING: Interpreter already has a callback " + "list.\n"); + } + scin->cbl = cbl; +} + + +static int check_callback(SCInterpreter *scin, SCBlock *bl) +{ + int i; + const char *name = sc_block_name(bl); + SCCallbackList *cbl = scin->cbl; + + /* No callback list -> easy */ + if ( cbl == NULL ) return 0; + + /* No name -> definitely not a callback */ + if ( name == NULL ) return 0; + + for ( i=0; in_callbacks; i++ ) { + + double w, h; + int r; + void *bvp; + + if ( strcmp(cbl->names[i], name) != 0 ) continue; + r = cbl->box_funcs[i](scin, bl, &w, &h, &bvp, cbl->vps[i]); + if ( r ) { + struct sc_state *st = &scin->state[scin->j]; + Paragraph *pnew; + pnew = add_callback_para(sc_interp_get_frame(scin), + bl, w, h, + cbl->draw_funcs[i], + cbl->click_funcs[i], + bvp, cbl->vps[i]); + if ( pnew != NULL ) { + set_para_spacing(pnew, st->paraspace); + } + + } + return 1; + + } + + return 0; +} + + +PangoFontDescription *sc_interp_get_fontdesc(SCInterpreter *scin) +{ + struct sc_state *st = &scin->state[scin->j]; + return st->fontdesc; +} + + +double *sc_interp_get_fgcol(SCInterpreter *scin) +{ + struct sc_state *st = &scin->state[scin->j]; + return st->col; +} + + +static void set_frame_default_style(struct frame *fr, SCInterpreter *scin) +{ + if ( fr == NULL ) return; + + if ( fr->fontdesc != NULL ) { + pango_font_description_free(fr->fontdesc); + } + fr->fontdesc = pango_font_description_copy(sc_interp_get_fontdesc(scin)); + fr->col[0] = sc_interp_get_fgcol(scin)[0]; + fr->col[1] = sc_interp_get_fgcol(scin)[1]; + fr->col[2] = sc_interp_get_fgcol(scin)[2]; + fr->col[3] = sc_interp_get_fgcol(scin)[3]; +} + + +static void update_font(SCInterpreter *scin) +{ + PangoFontMetrics *metrics; + struct sc_state *st = &scin->state[scin->j]; + + if ( scin->pc == NULL ) return; + + st->font = pango_font_map_load_font(pango_context_get_font_map(scin->pc), + scin->pc, st->fontdesc); + if ( st->font == NULL ) { + char *f = pango_font_description_to_string(st->fontdesc); + fprintf(stderr, "Couldn't load font '%s' (font map %p, pc %p)\n", + f, pango_context_get_font_map(scin->pc), scin->pc); + g_free(f); + return; + } + + /* FIXME: Language for box */ + metrics = pango_font_get_metrics(st->font, NULL); + st->ascent = pango_font_metrics_get_ascent(metrics); + st->height = st->ascent + pango_font_metrics_get_descent(metrics); + pango_font_metrics_unref(metrics); + set_frame_default_style(sc_interp_get_frame(scin), scin); +} + + +static void set_font(SCInterpreter *scin, const char *font_name) +{ + struct sc_state *st = &scin->state[scin->j]; + + st->fontdesc = pango_font_description_from_string(font_name); + if ( st->fontdesc == NULL ) { + fprintf(stderr, "Couldn't describe font.\n"); + return; + } + + update_font(scin); +} + + +static void copy_top_fontdesc(SCInterpreter *scin) +{ + struct sc_state *st = &scin->state[scin->j]; + + /* If this is the first stack frame, don't even check */ + if ( scin->j == 0 ) return; + + /* If the fontdesc at the top of the stack is the same as the one + * below, make a copy because we're about to do something to it (which + * should not affect the next level up). */ + if ( st->fontdesc == scin->state[scin->j-1].fontdesc ) { + st->fontdesc = pango_font_description_copy(st->fontdesc); + } +} + + +static void set_fontsize(SCInterpreter *scin, const char *size_str) +{ + struct sc_state *st = &scin->state[scin->j]; + int size; + char *end; + + if ( size_str[0] == '\0' ) return; + + size = strtoul(size_str, &end, 10); + if ( end[0] != '\0' ) { + fprintf(stderr, _("Invalid font size '%s'\n"), size_str); + return; + } + + copy_top_fontdesc(scin); + pango_font_description_set_size(st->fontdesc, size*PANGO_SCALE); + update_font(scin); +} + + +static void set_bold(SCInterpreter *scin) +{ + struct sc_state *st = &scin->state[scin->j]; + copy_top_fontdesc(scin); + pango_font_description_set_weight(st->fontdesc, PANGO_WEIGHT_BOLD); + update_font(scin); +} + + +static void set_oblique(SCInterpreter *scin) +{ + struct sc_state *st = &scin->state[scin->j]; + copy_top_fontdesc(scin); + pango_font_description_set_style(st->fontdesc, PANGO_STYLE_OBLIQUE); + update_font(scin); +} + + +static void set_italic(SCInterpreter *scin) +{ + struct sc_state *st = &scin->state[scin->j]; + copy_top_fontdesc(scin); + pango_font_description_set_style(st->fontdesc, PANGO_STYLE_ITALIC); + update_font(scin); +} + + +static void set_alignment(SCInterpreter *scin, PangoAlignment align) +{ + struct sc_state *st = &scin->state[scin->j]; + st->alignment = align; +} + + +/* This sets the colour for the font at the top of the stack */ +static void set_colour(SCInterpreter *scin, const char *colour) +{ + GdkRGBA col; + struct sc_state *st = &scin->state[scin->j]; + + if ( colour == NULL ) { + printf(_("Invalid colour\n")); + st->col[0] = 0.0; + st->col[1] = 0.0; + st->col[2] = 0.0; + st->col[3] = 1.0; + return; + } + + gdk_rgba_parse(&col, colour); + + st->col[0] = col.red; + st->col[1] = col.green; + st->col[2] = col.blue; + st->col[3] = col.alpha; + set_frame_default_style(sc_interp_get_frame(scin), scin); +} + + +static void set_bgcol(SCInterpreter *scin, const char *colour) +{ + GdkRGBA col; + struct frame *fr = sc_interp_get_frame(scin); + + if ( fr == NULL ) return; + + if ( colour == NULL ) { + printf(_("Invalid colour\n")); + return; + } + + gdk_rgba_parse(&col, colour); + + fr->bgcol[0] = col.red; + fr->bgcol[1] = col.green; + fr->bgcol[2] = col.blue; + fr->bgcol[3] = col.alpha; + fr->grad = GRAD_NONE; +} + + +static void set_bggrad(SCInterpreter *scin, const char *options, + GradientType grad) +{ + struct frame *fr = sc_interp_get_frame(scin); + GdkRGBA col1, col2; + + if ( fr == NULL ) return; + + if ( options == NULL ) { + printf(_("Invalid colour\n")); + return; + } + + if ( parse_colour_duo(options, &col1, &col2) == 0 ) { + + fr->bgcol[0] = col1.red; + fr->bgcol[1] = col1.green; + fr->bgcol[2] = col1.blue; + fr->bgcol[3] = col1.alpha; + + fr->bgcol2[0] = col2.red; + fr->bgcol2[1] = col2.green; + fr->bgcol2[2] = col2.blue; + fr->bgcol2[3] = col2.alpha; + + fr->grad = grad; + + } +} + + +static char *get_constant(SCInterpreter *scin, unsigned int constant) +{ + struct sc_state *st = &scin->state[scin->j]; + if ( constant >= NUM_SC_CONSTANTS ) return NULL; + return st->constants[constant]; +} + + +void sc_interp_set_constant(SCInterpreter *scin, unsigned int constant, + const char *val) +{ + struct sc_state *st = &scin->state[scin->j]; + if ( constant >= NUM_SC_CONSTANTS ) return; + if ( val == NULL ) return; + st->constants[constant] = strdup(val); +} + + +void sc_interp_save(SCInterpreter *scin) +{ + if ( scin->j+1 == scin->max_state ) { + + struct sc_state *stack_new; + + stack_new = realloc(scin->state, sizeof(struct sc_state) + * (scin->max_state+8)); + if ( stack_new == NULL ) { + fprintf(stderr, "Failed to add to stack.\n"); + return; + } + + scin->state = stack_new; + scin->max_state += 8; + + } + + /* When n_fonts=0, we leave the first font uninitialised. This allows + * the stack to be "bootstrapped", but requires the first caller to do + * set_font and set_colour straight away. */ + scin->state[scin->j+1] = scin->state[scin->j]; + + scin->j++; +} + + +void sc_interp_restore(SCInterpreter *scin) +{ + struct sc_state *st = &scin->state[scin->j]; + + if ( scin->j > 0 ) { + + int i; + + if ( st->fontdesc != scin->state[scin->j-1].fontdesc ) + { + pango_font_description_free(st->fontdesc); + } /* else the font is the same as the previous one, and we + * don't need to free it just yet */ + + for ( i=0; iconstants[i] != scin->state[scin->j-1].constants[i] ) { + free(st->constants[i]); + } /* same logic as above */ + } + } + + scin->j--; +} + + +struct frame *sc_interp_get_frame(SCInterpreter *scin) +{ + struct sc_state *st = &scin->state[scin->j]; + return st->fr; +} + + +static void set_frame(SCInterpreter *scin, struct frame *fr) +{ + struct sc_state *st = &scin->state[scin->j]; + st->fr = fr; +} + + +SCInterpreter *sc_interp_new(PangoContext *pc, PangoLanguage *lang, + ImageStore *is, struct frame *top) +{ + SCInterpreter *scin; + struct sc_state *st; + int i; + + scin = malloc(sizeof(SCInterpreter)); + if ( scin == NULL ) return NULL; + + scin->state = malloc(8*sizeof(struct sc_state)); + if ( scin->state == NULL ) { + free(scin); + return NULL; + } + scin->j = 0; + scin->max_state = 8; + + scin->pc = pc; + scin->is = is; + scin->s_constants = NULL; + scin->p_constants = NULL; + scin->cbl = NULL; + scin->lang = lang; + + /* Initial state */ + st = &scin->state[0]; + st->fr = NULL; + st->paraspace[0] = 0.0; + st->paraspace[1] = 0.0; + st->paraspace[2] = 0.0; + st->paraspace[3] = 0.0; + st->fontdesc = NULL; + st->col[0] = 0.0; + st->col[1] = 0.0; + st->col[2] = 0.0; + st->col[3] = 1.0; + st->alignment = PANGO_ALIGN_LEFT; + for ( i=0; iconstants[i] = NULL; + } + + /* The "ultimate" default font */ + if ( scin->pc != NULL ) { + set_frame(scin, top); + set_font(scin, "Cantarell Regular 14"); + set_colour(scin, "#000000"); + } + + return scin; +} + + +void sc_interp_destroy(SCInterpreter *scin) +{ + /* Empty the stack */ + while ( scin->j > 0 ) { + sc_interp_restore(scin); + } + + if ( scin->state[0].fontdesc != NULL ) { + pango_font_description_free(scin->state[0].fontdesc); + } + + free(scin->state); + free(scin); +} + + +static void set_padding(struct frame *fr, const char *opts) +{ + float p[4]; + + if ( parse_tuple(opts, p) ) return; + + if ( fr == NULL ) return; + + fr->pad_l = p[0]; + fr->pad_r = p[1]; + fr->pad_t = p[2]; + fr->pad_b = p[3]; +} + + +static void set_paraspace(SCInterpreter *scin, const char *opts) +{ + float p[4]; + struct sc_state *st = &scin->state[scin->j]; + + if ( parse_tuple(opts, p) ) return; + + st->paraspace[0] = p[0]; + st->paraspace[1] = p[1]; + st->paraspace[2] = p[2]; + st->paraspace[3] = p[3]; + + set_para_spacing(last_para(sc_interp_get_frame(scin)), p); +} + + +void update_geom(struct frame *fr) +{ + char geom[256]; + snprintf(geom, 255, "%.1fux%.1fu+%.1f+%.1f", + fr->w, fr->h, fr->x, fr->y); + + /* FIXME: What if there are other options? */ + sc_block_set_options(fr->scblocks, strdup(geom)); +} + + +static int calculate_dims(const char *opt, struct frame *parent, + double *wp, double *hp, double *xp, double *yp) +{ + LengthUnits h_units, w_units; + + if ( parse_dims(opt, wp, hp, &w_units, &h_units, xp, yp) ) { + return 1; + } + + if ( w_units == UNITS_FRAC ) { + if ( parent != NULL ) { + double pw = parent->w; + pw -= parent->pad_l; + pw -= parent->pad_r; + *wp = pw * *wp; + } else { + *wp = -1.0; + } + + } + if ( h_units == UNITS_FRAC ) { + if ( parent != NULL ) { + double ph = parent->h; + ph -= parent->pad_t; + ph -= parent->pad_b; + *hp = ph * *hp; + } else { + *hp = -1.0; + } + } + + return 0; +} + + +static int parse_frame_option(const char *opt, struct frame *fr, + struct frame *parent) +{ + if ( (index(opt, 'x') != NULL) && (index(opt, '+') != NULL) + && (index(opt, '+') != rindex(opt, '+')) ) { + return calculate_dims(opt, parent, &fr->w, &fr->h, &fr->x, &fr->y); + } + + fprintf(stderr, _("Unrecognised frame option '%s'\n"), opt); + + return 1; +} + + +static int parse_frame_options(struct frame *fr, struct frame *parent, + const char *opth) +{ + int i; + size_t len; + size_t start; + char *opt; + + if ( opth == NULL ) return 1; + + opt = strdup(opth); + + len = strlen(opt); + start = 0; + + for ( i=0; istate[scin->j]; + struct frame *fr = sc_interp_get_frame(scin); + + if ( fr->paras == NULL ) return; + last_para = fr->paras[fr->n_paras-1]; + + set_newline_at_end(last_para, bl); + + /* The block after the \newpara will always be the first one of the + * next paragraph, by definition, even if it's \f or another \newpara */ + para = create_paragraph(fr, sc_block_next(bl)); + set_para_alignment(para, st->alignment); + set_para_spacing(para, st->paraspace); +} + + +/* Add the SCBlock to the text in 'frame', at the end */ +static int add_text(struct frame *fr, PangoContext *pc, SCBlock *bl, + PangoLanguage *lang, int editable, SCInterpreter *scin, + const char *real_text) +{ + const char *text = sc_block_contents(bl); + PangoFontDescription *fontdesc; + double *col; + struct sc_state *st = &scin->state[scin->j]; + Paragraph *para; + + /* Empty block? */ + if ( text == NULL && real_text == NULL ) return 1; + + fontdesc = sc_interp_get_fontdesc(scin); + col = sc_interp_get_fgcol(scin); + + para = last_para(fr); + if ( (para == NULL) || (para_type(para) != PARA_TYPE_TEXT) ) { + /* Last paragraph is not text. + * or: no paragraphs yet. + * Either way: Create the first one */ + para = create_paragraph(fr, bl); + } + + set_para_alignment(para, st->alignment); + add_run(para, bl, fontdesc, col, real_text); + set_para_spacing(para, st->paraspace); + + return 0; +} + + +static void apply_style(SCInterpreter *scin, Stylesheet *ss, const char *path) +{ + char *result; + + if ( ss == NULL ) return; + + /* Font */ + result = stylesheet_lookup(ss, path, "font"); + if ( result != NULL ) set_font(scin, result); + + /* Foreground colour */ + result = stylesheet_lookup(ss, path, "fgcol"); + if ( result != NULL ) set_colour(scin, result); + + /* Background (vertical gradient) */ + result = stylesheet_lookup(ss, path, "bggradv"); + if ( result != NULL ) set_bggrad(scin, result, GRAD_VERT); + + /* Background (horizontal gradient) */ + result = stylesheet_lookup(ss, path, "bggradh"); + if ( result != NULL ) set_bggrad(scin, result, GRAD_HORIZ); + + /* Background (solid colour) */ + result = stylesheet_lookup(ss, path, "bgcol"); + if ( result != NULL ) set_bgcol(scin, result); + + /* Padding */ + result = stylesheet_lookup(ss, path, "pad"); + if ( result != NULL ) set_padding(sc_interp_get_frame(scin), result); + + /* Paragraph spacing */ + result = stylesheet_lookup(ss, path, "paraspace"); + if ( result != NULL ) set_paraspace(scin, result); + + /* Alignment */ + result = stylesheet_lookup(ss, path, "alignment"); + if ( result != NULL ) { + if ( strcmp(result, "center") == 0 ) { + set_alignment(scin, PANGO_ALIGN_CENTER); + } + if ( strcmp(result, "left") == 0 ) { + set_alignment(scin, PANGO_ALIGN_LEFT); + } + if ( strcmp(result, "right") == 0 ) { + set_alignment(scin, PANGO_ALIGN_RIGHT); + } + } +} + + +static void output_frame(SCInterpreter *scin, SCBlock *bl, Stylesheet *ss, + const char *stylename) +{ + struct frame *fr; + SCBlock *child = sc_block_child(bl); + const char *options = sc_block_options(bl); + char *result; + + fr = add_subframe(sc_interp_get_frame(scin)); + if ( fr == NULL ) { + fprintf(stderr, _("Failed to add frame.\n")); + return; + } + + fr->scblocks = bl; + fr->resizable = 1; + + /* Lowest priority: current state of interpreter */ + set_frame_default_style(fr, scin); + + /* Next priority: geometry from stylesheet */ + result = stylesheet_lookup(ss, stylename, "geometry"); + if ( result != NULL ) { + parse_frame_options(fr, sc_interp_get_frame(scin), result); + } + + /* Highest priority: parameters to \f (or \slidetitle etc) */ + parse_frame_options(fr, sc_interp_get_frame(scin), options); + + maybe_recurse_before(scin, child); + set_frame(scin, fr); + apply_style(scin, ss, stylename); + maybe_recurse_after(scin, child, ss); +} + + +static int check_outputs(SCBlock *bl, SCInterpreter *scin, Stylesheet *ss) +{ + const char *name = sc_block_name(bl); + const char *options = sc_block_options(bl); + + if ( name == NULL ) { + add_text(sc_interp_get_frame(scin), + scin->pc, bl, scin->lang, 1, scin, NULL); + + } else if ( strcmp(name, "image")==0 ) { + double w, h; + char *filename; + if ( parse_image_options(options, sc_interp_get_frame(scin), + &w, &h, &filename) == 0 ) + { + add_image_para(sc_interp_get_frame(scin), bl, + filename, scin->is, w, h, 1); + free(filename); + } else { + fprintf(stderr, _("Invalid image options '%s'\n"), + options); + } + + } else if ( strcmp(name, "f")==0 ) { + output_frame(scin, bl, ss, "$.slide.frame"); + + } else if ( strcmp(name, "slidetitle")==0 ) { + output_frame(scin, bl, ss, "$.slide.slidetitle"); + + } else if ( strcmp(name, "prestitle")==0 ) { + output_frame(scin, bl, ss, "$.slide.prestitle"); + + } else if ( strcmp(name, "author")==0 ) { + output_frame(scin, bl, ss, "$.slide.author"); + + } else if ( strcmp(name, "footer")==0 ) { + output_frame(scin, bl, ss, "$.slide.footer"); + + } else if ( strcmp(name, "newpara")==0 ) { + add_newpara(bl, scin); + + } else if ( strcmp(name, "slidenumber")==0 ) { + char *con = get_constant(scin, SCCONST_SLIDENUMBER); + if ( con != NULL ) { + add_text(sc_interp_get_frame(scin), scin->pc, bl, + scin->lang, 1, scin, con); + } + + } else { + return 0; + } + + return 1; /* handled */ +} + + +int sc_interp_add_block(SCInterpreter *scin, SCBlock *bl, Stylesheet *ss) +{ + const char *name = sc_block_name(bl); + const char *options = sc_block_options(bl); + SCBlock *child = sc_block_child(bl); + + //printf("Running this --------->\n"); + //show_sc_blocks(bl); + //printf("<------------\n"); + + if ( check_callback(scin, bl) ) { + /* Handled in check_callback, don't do anything else */ + + } else if ((sc_interp_get_frame(scin) != NULL) + && check_outputs(bl, scin, ss) ) { + /* Block handled as output thing */ + + } else if ( name == NULL ) { + /* Dummy to ensure name != NULL below */ + + } else if ( strcmp(name, "presentation") == 0 ) { + maybe_recurse_before(scin, child); + apply_style(scin, ss, "$.narrative"); + maybe_recurse_after(scin, child, ss); + + } else if ( strcmp(name, "slide") == 0 ) { + maybe_recurse_before(scin, child); + apply_style(scin, ss, "$.slide"); + maybe_recurse_after(scin, child, ss); + + } else if ( strcmp(name, "font") == 0 ) { + maybe_recurse_before(scin, child); + set_font(scin, options); + maybe_recurse_after(scin, child, ss); + + } else if ( strcmp(name, "fontsize") == 0 ) { + maybe_recurse_before(scin, child); + set_fontsize(scin, options); + maybe_recurse_after(scin, child, ss); + + } else if ( strcmp(name, "bold") == 0 ) { + maybe_recurse_before(scin, child); + set_bold(scin); + maybe_recurse_after(scin, child, ss); + + } else if ( strcmp(name, "oblique") == 0 ) { + maybe_recurse_before(scin, child); + set_oblique(scin); + maybe_recurse_after(scin, child, ss); + + } else if ( strcmp(name, "italic") == 0 ) { + maybe_recurse_before(scin, child); + set_italic(scin); + maybe_recurse_after(scin, child, ss); + + } else if ( strcmp(name, "lalign") == 0 ) { + maybe_recurse_before(scin, child); + set_alignment(scin, PANGO_ALIGN_LEFT); + maybe_recurse_after(scin, child, ss); + + } else if ( strcmp(name, "ralign") == 0 ) { + maybe_recurse_before(scin, child); + set_alignment(scin, PANGO_ALIGN_RIGHT); + maybe_recurse_after(scin, child, ss); + + } else if ( strcmp(name, "center") == 0 ) { + maybe_recurse_before(scin, child); + set_alignment(scin, PANGO_ALIGN_CENTER); + maybe_recurse_after(scin, child, ss); + + } else if ( strcmp(name, "fgcol") == 0 ) { + maybe_recurse_before(scin, child); + set_colour(scin, options); + maybe_recurse_after(scin, child, ss); + + } else if ( strcmp(name, "pad") == 0 ) { + maybe_recurse_before(scin, child); + set_padding(sc_interp_get_frame(scin), options); + maybe_recurse_after(scin, child, ss); + + } else if ( strcmp(name, "bgcol") == 0 ) { + set_bgcol(scin, options); + + } else if ( strcmp(name, "bggradh") == 0 ) { + set_bggrad(scin, options, GRAD_HORIZ); + + } else if ( strcmp(name, "bggradv") == 0 ) { + set_bggrad(scin, options, GRAD_VERT); + + } else if ( strcmp(name, "paraspace") == 0 ) { + maybe_recurse_before(scin, child); + set_paraspace(scin, options); + maybe_recurse_after(scin, child, ss); + + } else { + + fprintf(stderr, "Don't know what to do with this:\n"); + show_sc_block(bl, ""); + + } + + return 0; +} + diff --git a/src-old/sc_interp.h b/src-old/sc_interp.h new file mode 100644 index 0000000..764b532 --- /dev/null +++ b/src-old/sc_interp.h @@ -0,0 +1,80 @@ +/* + * sc_interp.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 SC_INTERP_H +#define SC_INTERP_H + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include + +struct frame; + +#define SCCONST_SLIDENUMBER (0) +#define NUM_SC_CONSTANTS (1) + +struct presentation; +typedef struct _scinterp SCInterpreter; +typedef struct _sccallbacklist SCCallbackList; +typedef int (*SCCallbackBoxFunc)(SCInterpreter *scin, SCBlock *bl, + double *w, double *h, void **, void *); +typedef cairo_surface_t *(*SCCallbackDrawFunc)(int w, int h, void *, void *); +typedef int (*SCCallbackClickFunc)(double x, double y, void *, void *); + +#include "frame.h" +#include "imagestore.h" +#include "stylesheet.h" + +extern SCInterpreter *sc_interp_new(PangoContext *pc, PangoLanguage *lang, + ImageStore *is, struct frame *top); +extern void sc_interp_destroy(SCInterpreter *scin); + +extern void sc_interp_save(SCInterpreter *scin); +extern void sc_interp_restore(SCInterpreter *scin); + +extern int sc_interp_add_block(SCInterpreter *scin, SCBlock *bl, Stylesheet *ss); + +extern void sc_interp_set_constant(SCInterpreter *scin, unsigned int constant, + const char *val); + +/* Callback lists */ +extern SCCallbackList *sc_callback_list_new(); +extern void sc_callback_list_free(SCCallbackList *cbl); +extern void sc_callback_list_add_callback(SCCallbackList *cbl, const char *name, + SCCallbackBoxFunc box_func, + SCCallbackDrawFunc draw_func, + SCCallbackClickFunc click_func, + void *vp); +extern void sc_interp_set_callbacks(SCInterpreter *scin, SCCallbackList *cbl); + +/* Get the current state of the interpreter */ +extern struct frame *sc_interp_get_frame(SCInterpreter *scin); +extern PangoFont *sc_interp_get_font(SCInterpreter *scin); +extern PangoFontDescription *sc_interp_get_fontdesc(SCInterpreter *scin); +extern double *sc_interp_get_fgcol(SCInterpreter *scin); + +extern void update_geom(struct frame *fr); + + +#endif /* SC_INTERP_H */ diff --git a/src-old/sc_parse.c b/src-old/sc_parse.c new file mode 100644 index 0000000..78f5799 --- /dev/null +++ b/src-old/sc_parse.c @@ -0,0 +1,856 @@ +/* + * sc_parse.c + * + * Copyright © 2013-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 +#include +#include + +#include "sc_parse.h" +#include "utils.h" + +struct _scblock +{ + char *name; + char *options; + char *contents; + + SCBlock *next; + SCBlock *child; +}; + + +SCBlock *sc_block_new() +{ + SCBlock *bl; + + bl = calloc(1, sizeof(SCBlock)); + if ( bl == NULL ) return NULL; + + return bl; +} + + +SCBlock *sc_block_next(const SCBlock *bl) +{ + assert(bl != NULL); + return bl->next; +} + + +SCBlock *sc_block_child(const SCBlock *bl) +{ + return bl->child; +} + + +const char *sc_block_name(const SCBlock *bl) +{ + return bl->name; +} + + +const char *sc_block_options(const SCBlock *bl) +{ + return bl->options; +} + + +const char *sc_block_contents(const SCBlock *bl) +{ + return bl->contents; +} + + +static SCBlock *sc_find_previous(SCBlock *top, SCBlock *find) +{ + if ( top->next == find ) return top; + + if ( top->child != NULL ) { + SCBlock *t = sc_find_previous(top->child, find); + if ( t != NULL ) return t; + } + if ( top->next != NULL ) { + SCBlock *t = sc_find_previous(top->next, find); + if ( t != NULL ) return t; + } + return NULL; +} + + +/* Add a new block before "bl" */ +SCBlock *sc_block_prepend(SCBlock *bl, SCBlock *top) +{ + SCBlock *bln; + SCBlock *prev; + + prev = sc_find_previous(top, bl); + if ( prev == NULL ) { + fprintf(stderr, "Couldn't find previous\n"); + return NULL; + } + + bln = sc_block_new(); + if ( bln == NULL ) return NULL; + + prev->next = bln; + bln->next = bl; + return bln; +} + + +/* Append "bln" after "bl" */ +void sc_block_append_p(SCBlock *bl, SCBlock *bln) +{ + if ( bl != NULL ) { + bln->next = bl->next; + bl->next = bln; + } +} + + +/* Insert a new block after "bl". "name", "options" and "contents" + * will not be copied. Returns the block just created, or NULL on error. + * If *blfp points to NULL, it will updated to point at the new block. */ +SCBlock *sc_block_append(SCBlock *bl, char *name, char *opt, char *contents, + SCBlock **blfp) +{ + SCBlock *bln = sc_block_new(); + + if ( bln == NULL ) return NULL; + + bln->name = name; + bln->options = opt; + bln->contents = contents; + bln->child = NULL; + bln->next = NULL; + + sc_block_append_p(bl, bln); + + if ( (blfp != NULL) && (*blfp == NULL) ) { + *blfp = bln; + } + + return bln; +} + + +/* Insert a new block at the end of the chain starting 'bl'. + * "name", "options" and "contents" will not be copied. Returns the block just + * created, or NULL on error. */ +SCBlock *sc_block_append_end(SCBlock *bl, char *name, char *opt, char *contents) +{ + SCBlock *bln = sc_block_new(); + + if ( bln == NULL ) return NULL; + + while ( bl->next != NULL ) { + bln->next = bl->next; + bl = bl->next; + }; + + return sc_block_append(bl, name, opt, contents, NULL); +} + + +void sc_block_append_block(SCBlock *bl, SCBlock *bln) +{ + + if ( bl == NULL ) return; + + while ( bl->next != NULL ) bl = bl->next; + + bl->next = bln; + bln->next = NULL; +} + + +/* Append a new block to the chain inside "parent". + * "name", "options" and "contents" will not be copied. Returns the block just + * created, or NULL on error. */ +SCBlock *sc_block_append_inside(SCBlock *parent, + char *name, char *opt, char *contents) +{ + SCBlock *bln; + SCBlock *bl; + SCBlock **ptr; + + bl = parent->child; + if ( bl != NULL ) { + while ( bl->next != NULL ) bl = bl->next; + ptr = &bl->next; + } else { + ptr = &parent->child; + } + + bln = sc_block_new(); + if ( bln == NULL ) return NULL; + + bln->name = name; + bln->options = opt; + bln->contents = contents; + bln->child = NULL; + bln->next = NULL; + + *ptr = bln; + + return bln; +} + + +/* Insert a new block to the chain, just after "afterme". + * "name", "options" and "contents" will not be copied. Returns the block just + * created, or NULL on error. */ +SCBlock *sc_block_insert_after(SCBlock *afterme, + char *name, char *opt, char *contents) +{ + SCBlock *bl; + + bl = sc_block_new(); + if ( bl == NULL ) return NULL; + + bl->name = name; + bl->options = opt; + bl->contents = contents; + bl->child = NULL; + bl->next = afterme->next; + afterme->next = bl; + + return bl; +} + + +static SCBlock *sc_find_parent(SCBlock *top, SCBlock *find) +{ + if ( top->child == find ) return top; + if ( top->next == find ) return top; + + if ( top->child != NULL ) { + SCBlock *t = sc_find_parent(top->child, find); + if ( t != NULL ) return t; + } + if ( top->next != NULL ) { + SCBlock *t = sc_find_parent(top->next, find); + if ( t != NULL ) return t; + } + return NULL; +} + + +/* Unlink "deleteme", which is somewhere under "top" */ +static int sc_block_unlink(SCBlock **top, SCBlock *deleteme) +{ + SCBlock *parent = sc_find_parent(*top, deleteme); + if ( parent == NULL ) { + /* Maybe it's the first block? */ + if ( *top == deleteme ) { + fprintf(stderr, "Unlinking at top\n"); + *top = (*top)->next; + return 0; + } else { + fprintf(stderr, "Couldn't find block parent!\n"); + return 1; + } + } + + if ( parent->next == deleteme ) { + parent->next = deleteme->next; + } + + if ( parent->child == deleteme ) { + parent->child = deleteme->next; + } + return 0; +} + + +/* Delete "deleteme", which is somewhere under "top" */ +int sc_block_delete(SCBlock **top, SCBlock *deleteme) +{ + int r; + r = sc_block_unlink(top, deleteme); + if ( !r ) { + sc_block_free(deleteme); + } + return r; +} + + +/* Frees "bl" and all its children (but not the blocks following it) */ +void sc_block_free(SCBlock *bl) +{ + if ( bl == NULL ) return; + + if ( bl->child != NULL ) { + SCBlock *ch = bl->child; + while ( ch != NULL ) { + SCBlock *next = ch->next; + sc_block_free(ch); + ch = next; + } + } + + free(bl); +} + + +/* Serialise one block (including children) */ +char *serialise_sc_block(const SCBlock *bl) +{ + char *a; + SCBlock *ch; + size_t len = 3; + + if ( bl == NULL ) return strdup(""); + + if ( bl->name != NULL ) len += 1+strlen(bl->name); + if ( bl->options != NULL ) len += 2+strlen(bl->options); + if ( bl->contents != NULL ) len += 2+strlen(bl->contents); + a = malloc(len); + if ( a == NULL ) return NULL; + a[0] = '\0'; + + if ( bl->name == NULL ) { + strcat(a, bl->contents); + } else if ( strcmp(bl->name, "newpara") == 0 ) { + strcat(a, "\n"); + + } else { + + strcat(a, "\\"); + strcat(a, bl->name); + if ( bl->options != NULL ) { + strcat(a, "["); + strcat(a, bl->options); + strcat(a, "]"); + } + if ( (bl->contents != NULL) || (bl->child != NULL) ) { + strcat(a, "{"); + } + if ( bl->contents != NULL ) { + strcat(a, bl->contents); + } + + /* Special case to prevent "\somethingSome text" */ + if ( (bl->name != NULL) && (bl->options == NULL) + && (bl->contents == NULL) && (bl->next != NULL) + && (bl->next->name == NULL) && (bl->child == NULL) ) + { + strcat(a, "{}"); + } + + } + + /* Add ALL child blocks of this one */ + ch = bl->child; + while ( ch != NULL ) { + + char *anew; + char *c = serialise_sc_block(ch); + if ( c == NULL ) { + free(a); + return NULL; + } + + len += strlen(c); + + anew = realloc(a, len); + if ( anew == NULL ) { + return NULL; + } else { + a = anew; + } + + strcat(a, c); + free(c); + + ch = ch->next; + + } + + if ( (bl->name != NULL) && + ((bl->contents != NULL) || (bl->child != NULL)) ) { + strcat(a, "}"); + } + + return a; +} + + +int save_sc_block(GOutputStream *fh, const SCBlock *bl) +{ + while ( bl != NULL ) { + GError *error = NULL; + char *a = serialise_sc_block(bl); + gssize r; + if ( a == NULL ) { + fprintf(stderr, "Failed to serialise block\n"); + return 1; + } + r = g_output_stream_write(fh, a, strlen(a), NULL, &error); + if ( r == -1 ) { + fprintf(stderr, "Write failed: %s\n", error->message); + return 1; + } + free(a); + bl = bl->next; + } + return 0; +} + + +static void recursive_show_sc_blocks(const char *prefix, const SCBlock *bl) +{ + while ( bl != NULL ) { + show_sc_block(bl, prefix); + bl = bl->next; + } +} + + +void show_sc_block(const SCBlock *bl, const char *prefix) +{ + printf("%s (%p) ", prefix, bl); + if ( bl == NULL ) return; + if ( bl->name != NULL ) printf("\\%s ", bl->name); + if ( bl->options != NULL ) printf("[%s] ", bl->options); + if ( bl->contents != NULL ) printf("{%s} ", bl->contents); + printf("\n"); + + if ( bl->child != NULL ) { + char new_prefix[strlen(prefix)+3]; + strcpy(new_prefix, " "); + strcat(new_prefix, prefix); + recursive_show_sc_blocks(new_prefix, bl->child); + } +} + + +void show_sc_blocks(const SCBlock *bl) +{ + recursive_show_sc_blocks("", bl); +} + + +static int get_subexpr(const char *sc, char *bk, char **pcontents, int *err) +{ + size_t ml; + int i; + int bct = 1; + int found = 0; + char *contents; + + *err = 0; + + ml = strlen(sc); + contents = malloc(ml+1); + if ( contents == NULL ) { + *err = -1; + return 0; + } + *pcontents = contents; + + for ( i=0; iname = strdup("newpara"); + bl->contents = NULL; + nb = bl; + } else { + sc_block_append(bl, strdup("newpara"), + NULL, NULL, &nb); + } + + /* Add any text after the \n */ + if ( strlen(npos+1) > 0 ) { + sc_block_append(nb, NULL, NULL, + strdup(npos+1), &nb); + } + npos[0] = '\0'; + } + } + + if ( sc_block_child(bl) != NULL ) { + separate_newlines(sc_block_child(bl)); + } + + bl = sc_block_next(bl); + + } +} + + +SCBlock *sc_parse(const char *sc) +{ + SCBlock *bl; + SCBlock *blf = NULL; + char *tbuf; + size_t len, i, j; + + if ( sc == NULL ) return NULL; + + if ( strlen(sc) == 0 ) { + SCBlock *bl = sc_block_new(); + sc_block_set_contents(bl, g_strdup("")); + return bl; + } + + bl = NULL; + + len = strlen(sc); + tbuf = malloc(len+1); + if ( tbuf == NULL ) { + sc_block_free(bl); + return NULL; + } + + i = 0; j = 0; + do { + + if ( sc[i] == '\\' ) { + + int err; + char *name = NULL; + char *opt = NULL; + char *contents = NULL; + + /* Is this an escaped backslash? */ + if ( sc[i+1] == '\\' ) { + tbuf[j++] = '\\'; + i += 2; /* Skip both backslashes */ + continue; + } + + /* No, it's a real block. Dispatch the previous block */ + if ( j != 0 ) { + tbuf[j] = '\0'; + bl = sc_block_append(bl, NULL, NULL, + strdup(tbuf), &blf); + if ( bl == NULL ) { + fprintf(stderr, "Block add failed.\n"); + sc_block_free(blf); + free(tbuf); + return NULL; + } + j = 0; + } + + i += read_block(sc+i+1, &name, &opt, &contents, &err); + if ( err ) { + printf(_("Parse error\n")); + sc_block_free(blf); + free(tbuf); + return NULL; + } + + bl = sc_block_append(bl, name, opt, contents, &blf); + if ( bl == NULL ) { + fprintf(stderr, "Block add failed.\n"); + sc_block_free(blf); + free(tbuf); + return NULL; + } + bl->child = sc_parse(contents); + free(bl->contents); + bl->contents = NULL; + + } else { + + tbuf[j++] = sc[i++]; + } + + } while ( i 0 ) { + + /* Leftover buffer is empty? */ + if ( (j==1) && (tbuf[0]=='\0') ) return bl; + + tbuf[j] = '\0'; + bl = sc_block_append(bl, NULL, NULL, tbuf, &blf); + if ( bl == NULL ) { + fprintf(stderr, "Block add failed.\n"); + sc_block_free(blf); + free(tbuf); + return NULL; + } + j = 0; + } + + separate_newlines(blf); + + return blf; +} + + +void sc_block_set_name(SCBlock *bl, char *nam) +{ + if ( bl == NULL ) { + fprintf(stderr, "sc_block_set_name: NULL block\n"); + return; + } + free(bl->name); + bl->name = nam; +} + + +void sc_block_set_options(SCBlock *bl, char *opt) +{ + free(bl->options); + bl->options = opt; +} + + +void sc_block_set_contents(SCBlock *bl, char *con) +{ + g_free(bl->contents); + bl->contents = con; +} + + +void sc_insert_text(SCBlock *b1, size_t o1, const char *t) +{ + size_t len; + char *cnew; + char *tmp; + char *p1; + + if ( b1->contents == NULL ) { + b1->contents = strdup(t); + return; + } + len = strlen(b1->contents)+1+strlen(t); + + cnew = realloc(b1->contents, len); + if ( cnew == NULL ) return; + + tmp = malloc(len); + if ( tmp == NULL ) { + free(cnew); + return; + } + + p1 = cnew + o1; + strcpy(tmp, p1); + strcpy(p1, t); + strcpy(p1+strlen(t), tmp); + free(tmp); + b1->contents = cnew; +} + + +void sc_insert_block(SCBlock *b1, int o1, SCBlock *ins) +{ + SCBlock *second; + char *p1 = g_utf8_offset_to_pointer(b1->contents, o1); + + /* Create a new block containing the second half of b1 */ + second = sc_block_new(); + sc_block_set_contents(second, g_strdup(p1)); + + /* Chop off b1 at the insertion point */ + sc_block_set_contents(b1, g_utf8_substring(b1->contents, 0, o1)); + + /* Link the new block into the chain */ + SCBlock *old_next = b1->next; + b1->next = ins; + ins->next = second; + second->next = old_next; +} + + +/* Delete text from SCBlock contents. o2=-1 means "to the end". + * Returns the number of bytes deleted. */ +size_t scblock_delete_text(SCBlock *b, ssize_t o1, ssize_t o2) +{ + size_t len; + + if ( b->contents == NULL ) { + fprintf(stderr, "Deleting text from block \\%s\n", b->name); + return 0; + } + + if ( (o2 != -1) && (o1 > o2) ) { + ssize_t t = o2; + o2 = o1; + o1 = t; + } + + len = strlen(b->contents); + if ( o2 < 0 ) o2 = len; + if ( (o1 >= o2) || (o1 > len) || (o2 > len) ) { + fprintf(stderr, "Invalid delete: %i %i %i\n", + (int)o1, (int)o2, (int)len); + return 0; + } + memmove(b->contents+o1, b->contents+o2, len-o2+1); + + return o2-o1; +} + + +static char *s_strdup(const char *a) +{ + if ( a == NULL ) return NULL; + return strdup(a); +} + + +SCBlock *sc_block_split(SCBlock *bl, size_t pos) +{ + SCBlock *n = sc_block_new(); + + if ( bl->child != NULL ) { + fprintf(stderr, "Splitting a block with a child!\n"); + return NULL; + } + + /* Second block */ + n->name = s_strdup(bl->name); + n->options = s_strdup(bl->options); + if ( bl->contents != NULL ) { + n->contents = strdup(bl->contents+pos); + /* Truncate the first block */ + bl->contents[pos] = '\0'; + } else { + n->contents = NULL; + } + + n->next = bl->next; + bl->next = n; + + return n; +} + + +/* Return a new block which is the parent for "bl" */ +SCBlock *sc_block_new_parent(SCBlock *bl, const char *name) +{ + SCBlock *n = sc_block_new(); + if ( n == NULL ) return NULL; + n->name = s_strdup(name); + n->child = bl; + return n; +} diff --git a/src-old/sc_parse.h b/src-old/sc_parse.h new file mode 100644 index 0000000..17ce2dd --- /dev/null +++ b/src-old/sc_parse.h @@ -0,0 +1,85 @@ +/* + * sc_parse.h + * + * Copyright © 2013-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 SC_PARSE_H +#define SC_PARSE_H + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include + +typedef struct _scblock SCBlock; + +extern SCBlock *sc_parse(const char *sc); + +extern SCBlock *sc_block_new(void); +extern void sc_block_free(SCBlock *bl); + +extern SCBlock *sc_block_next(const SCBlock *bl); +extern SCBlock *sc_block_child(const SCBlock *bl); +extern const char *sc_block_name(const SCBlock *bl); +extern const char *sc_block_options(const SCBlock *bl); +extern const char *sc_block_contents(const SCBlock *bl); + +extern SCBlock *sc_block_append(SCBlock *bl, + char *name, char *opt, char *contents, + SCBlock **blfp); + +extern SCBlock *sc_block_new_parent(SCBlock *bl, const char *name); + +extern SCBlock *sc_block_prepend(SCBlock *bl, SCBlock *top); + +extern void sc_block_append_p(SCBlock *bl, SCBlock *bln); + +extern void sc_block_append_block(SCBlock *bl, SCBlock *bln); + +extern SCBlock *sc_block_append_end(SCBlock *bl, + char *name, char *opt, char *contents); + +extern SCBlock *sc_block_append_inside(SCBlock *parent, + char *name, char *opt, char *contents); + +extern SCBlock *sc_block_insert_after(SCBlock *afterme, + char *name, char *opt, char *contents); + +extern int sc_block_delete(SCBlock **top, SCBlock *deleteme); + + +extern void sc_block_set_name(SCBlock *bl, char *nam); +extern void sc_block_set_options(SCBlock *bl, char *opt); +extern void sc_block_set_contents(SCBlock *bl, char *con); +extern void sc_insert_text(SCBlock *b1, size_t o1, const char *t); +extern void sc_insert_block(SCBlock *b1, int o1, SCBlock *ins); +extern SCBlock *sc_block_split(SCBlock *bl, size_t pos); + +extern void show_sc_blocks(const SCBlock *bl); +extern void show_sc_block(const SCBlock *bl, const char *prefix); + +extern char *serialise_sc_block(const SCBlock *bl); +extern int save_sc_block(GOutputStream *fh, const SCBlock *bl); + +extern size_t scblock_delete_text(SCBlock *b, ssize_t o1, ssize_t o2); + +#endif /* SC_PARSE_H */ diff --git a/src-old/slide_window.c b/src-old/slide_window.c new file mode 100644 index 0000000..ed37a50 --- /dev/null +++ b/src-old/slide_window.c @@ -0,0 +1,282 @@ +/* + * slide_window.c + * + * Copyright © 2013-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 +#include +#include + +#include "colloquium.h" +#include "presentation.h" +#include "slide_window.h" +#include "render.h" +#include "frame.h" +#include "slideshow.h" +#include "pr_clock.h" +#include "sc_parse.h" +#include "sc_interp.h" +#include "sc_editor.h" +#include "utils.h" + + +struct _slidewindow +{ + struct presentation *p; + GtkWidget *window; + GtkToolItem *bfirst; + GtkToolItem *bprev; + GtkToolItem *bnext; + GtkToolItem *blast; + + /* The slide being displayed. Note that the SCEditor is using the + * child of this block, but we need to keep a record of it for changing + * the slide. */ + SCBlock *scblocks; + + SCEditor *sceditor; +}; + + +static void insert_slidetitle_sig(GSimpleAction *action, GVariant *parameter, + gpointer vp) +{ + SlideWindow *sw = vp; + sc_editor_add_storycode(sw->sceditor, "\\slidetitle{Slide title}"); +} + + +static void paste_sig(GSimpleAction *action, GVariant *parameter, + gpointer vp) +{ + SlideWindow *sw = vp; + sc_editor_paste(sw->sceditor); +} + + +static void copy_frame_sig(GSimpleAction *action, GVariant *parameter, + gpointer vp) +{ + SlideWindow *sw = vp; + sc_editor_copy_selected_frame(sw->sceditor); +} + + +static void delete_frame_sig(GSimpleAction *action, GVariant *parameter, + gpointer vp) +{ + SlideWindow *sw = vp; + sc_editor_delete_selected_frame(sw->sceditor); +} + + +void slide_window_update(SlideWindow *sw) +{ + sc_editor_set_scblock(sw->sceditor, sw->scblocks); +} + + +/* Change the editor's slide to "np" */ +static void change_edit_slide(SlideWindow *sw, SCBlock *np) +{ + sc_editor_set_slidenum(sw->sceditor, slide_number(sw->p, np)); + sc_editor_set_scblock(sw->sceditor, np); + sw->scblocks = np; +} + + +static void change_slide_first(SlideWindow *sw) +{ + change_edit_slide(sw, first_slide(sw->p)); +} + + +static void change_slide_backwards(SlideWindow *sw) +{ + SCBlock *tt; + tt = prev_slide(sw->p, sw->scblocks); + if ( tt == NULL ) return; + change_edit_slide(sw, tt); +} + + +static void change_slide_forwards(SlideWindow *sw) +{ + SCBlock *tt; + tt = next_slide(sw->p, sw->scblocks); + if ( tt == NULL ) return; + change_edit_slide(sw, tt); +} + + +static void change_slide_last(SlideWindow *sw) +{ + change_edit_slide(sw, last_slide(sw->p)); +} + + +static void first_slide_sig(GSimpleAction *action, GVariant *parameter, + gpointer vp) +{ + SlideWindow *sw = vp; + change_slide_first(sw); +} + + +static void prev_slide_sig(GSimpleAction *action, GVariant *parameter, + gpointer vp) +{ + SlideWindow *sw = vp; + change_slide_backwards(sw); +} + + +static void next_slide_sig(GSimpleAction *action, GVariant *parameter, + gpointer vp) +{ + SlideWindow *sw = vp; + change_slide_forwards(sw); +} + + +static void last_slide_sig(GSimpleAction *action, GVariant *parameter, + gpointer vp) +{ + SlideWindow *sw = vp; + change_slide_last(sw); +} + + +static gboolean sw_close_sig(GtkWidget *w, SlideWindow *sw) +{ + narrative_window_sw_closed(sw->p->narrative_window, sw); + return FALSE; +} + + +static gboolean sw_key_press_sig(GtkWidget *da, GdkEventKey *event, + SlideWindow *sw) +{ + switch ( event->keyval ) { + + case GDK_KEY_Page_Up : + change_slide_backwards(sw); + break; + + case GDK_KEY_Page_Down : + change_slide_forwards(sw); + break; + + } + + return FALSE; +} + + +static void about_sig(GSimpleAction *action, GVariant *parameter, gpointer vp) +{ + SlideWindow *sw = vp; + open_about_dialog(sw->window); +} + + +GActionEntry sw_entries[] = { + + { "about", about_sig, NULL, NULL, NULL }, + { "paste", paste_sig, NULL, NULL, NULL }, + { "copyframe", copy_frame_sig, NULL, NULL, NULL }, + { "deleteframe", delete_frame_sig, NULL, NULL, NULL }, + { "first", first_slide_sig, NULL, NULL, NULL }, + { "prev", prev_slide_sig, NULL, NULL, NULL }, + { "next", next_slide_sig, NULL, NULL, NULL }, + { "last", last_slide_sig, NULL, NULL, NULL }, + { "slidetitle", insert_slidetitle_sig, NULL, NULL, NULL }, +}; + + +SlideWindow *slide_window_open(struct presentation *p, SCBlock *scblocks, + GApplication *papp) +{ + GtkWidget *window; + SlideWindow *sw; + Colloquium *app = COLLOQUIUM(papp); + + sw = calloc(1, sizeof(SlideWindow)); + if ( sw == NULL ) return NULL; + + window = gtk_application_window_new(GTK_APPLICATION(app)); + gtk_window_set_role(GTK_WINDOW(window), "slide"); + sw->window = window; + sw->p = p; + + g_action_map_add_action_entries(G_ACTION_MAP(window), sw_entries, + G_N_ELEMENTS(sw_entries), sw); + + g_signal_connect(G_OBJECT(window), "destroy", + G_CALLBACK(sw_close_sig), sw); + + sw->scblocks = scblocks; + sw->sceditor = sc_editor_new(scblocks, p->stylesheet, p->lang, + colloquium_get_imagestore(app)); + sc_editor_set_slidenum(sw->sceditor, slide_number(sw->p, scblocks)); + sc_editor_set_scale(sw->sceditor, 1); + sc_editor_set_imagestore(sw->sceditor, p->is); + +// scroll = gtk_scrolled_window_new(NULL, NULL); +// gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), +// GTK_POLICY_AUTOMATIC, +// GTK_POLICY_AUTOMATIC); +// gtk_container_add(GTK_CONTAINER(scroll), GTK_WIDGET(sw->sceditor)); +// gtk_window_set_focus(GTK_WINDOW(window), GTK_WIDGET(sw->sceditor)); + g_signal_connect(G_OBJECT(sw->sceditor), "key-press-event", + G_CALLBACK(sw_key_press_sig), sw); + + sc_editor_set_logical_size(sw->sceditor, + p->slide_width, p->slide_height); + + gtk_window_set_default_size(GTK_WINDOW(window), + p->slide_width, p->slide_height); + + gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(sw->sceditor)); + + /* Default size */ +// gtk_scrolled_window_set_min_content_width(GTK_SCROLLED_WINDOW(scroll), +// 1024); +// gtk_scrolled_window_set_min_content_height(GTK_SCROLLED_WINDOW(scroll), +// 768); + gtk_window_set_resizable(GTK_WINDOW(sw->window), TRUE); + + gtk_widget_show_all(window); +// gtk_scrolled_window_set_min_content_width(GTK_SCROLLED_WINDOW(scroll), +// 100); +// gtk_scrolled_window_set_min_content_height(GTK_SCROLLED_WINDOW(scroll), +// 100); + + return sw; +} diff --git a/src-old/slide_window.h b/src-old/slide_window.h new file mode 100644 index 0000000..681ec39 --- /dev/null +++ b/src-old/slide_window.h @@ -0,0 +1,37 @@ +/* + * slide_window.h + * + * Copyright © 2013-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 SLIDEWINDOW_H +#define SLIDEWINDOW_H + +#ifdef HAVE_CONFIG_H +#include +#endif + +typedef struct _slidewindow SlideWindow; + +extern SlideWindow *slide_window_open(struct presentation *p, SCBlock *scblocks, + GApplication *app); + +extern void slide_window_update(SlideWindow *sw); + +#endif /* SLIDEWINDOW_H */ diff --git a/src-old/slideshow.c b/src-old/slideshow.c new file mode 100644 index 0000000..6e1c49d --- /dev/null +++ b/src-old/slideshow.c @@ -0,0 +1,240 @@ +/* + * slideshow.c + * + * Copyright © 2013-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 + +#include "colloquium.h" +#include "presentation.h" +#include "render.h" +#include "pr_clock.h" +#include "frame.h" +#include "utils.h" + +G_DEFINE_TYPE_WITH_CODE(SCSlideshow, sc_slideshow, GTK_TYPE_WINDOW, NULL) + + +static void sc_slideshow_init(SCSlideshow *ss) +{ +} + + +void sc_slideshow_class_init(SCSlideshowClass *klass) +{ +} + + +static void slideshow_rerender(SCSlideshow *ss) +{ + int n; + gint w, h; + + if ( ss->cur_slide == NULL ) return; + + if ( ss->surface != NULL ) { + cairo_surface_destroy(ss->surface); + } + + n = slide_number(ss->p, ss->cur_slide); + ss->surface = render_sc(ss->cur_slide, + ss->slide_width, ss->slide_height, + ss->p->slide_width, ss->p->slide_height, + ss->p->stylesheet, NULL, ss->p->is, n, + &ss->top, ss->p->lang); + + w = gtk_widget_get_allocated_width(GTK_WIDGET(ss->drawingarea)); + h = gtk_widget_get_allocated_height(GTK_WIDGET(ss->drawingarea)); + + gtk_widget_queue_draw_area(ss->drawingarea, 0, 0, w, h); +} + + +static gint ssh_destroy_sig(GtkWidget *widget, SCSlideshow *ss) +{ + if ( ss->blank_cursor != NULL ) { + g_object_unref(ss->blank_cursor); + } + if ( ss->surface != NULL ) { + cairo_surface_destroy(ss->surface); + } + if ( ss->inhibit_cookie ) { + gtk_application_uninhibit(ss->app, ss->inhibit_cookie); + } + return FALSE; +} + + +static gboolean ss_draw_sig(GtkWidget *da, cairo_t *cr, SCSlideshow *ss) +{ + double width, height; + + width = gtk_widget_get_allocated_width(GTK_WIDGET(da)); + height = gtk_widget_get_allocated_height(GTK_WIDGET(da)); + + /* Overall background */ + cairo_rectangle(cr, 0.0, 0.0, width, height); + cairo_set_source_rgb(cr, 0.0, 0.0, 0.0); + cairo_fill(cr); + + if ( !ss->blank ) { + + /* Draw the slide from the cache */ + cairo_rectangle(cr, ss->xoff, ss->yoff, + ss->slide_width, ss->slide_height); + cairo_set_source_surface(cr, ss->surface, ss->xoff, ss->yoff); + cairo_fill(cr); + + } + + return FALSE; +} + + +static gboolean ss_realize_sig(GtkWidget *w, SCSlideshow *ss) +{ + if ( (ss->app == NULL) || colloquium_get_hidepointer(COLLOQUIUM(ss->app)) ) { + + /* Hide the pointer */ + GdkWindow *win; + win = gtk_widget_get_window(w); + ss->blank_cursor = gdk_cursor_new_for_display(gdk_display_get_default(), + GDK_BLANK_CURSOR); + gdk_window_set_cursor(GDK_WINDOW(win), ss->blank_cursor); + + } else { + ss->blank_cursor = NULL; + } + + slideshow_rerender(ss); + + return FALSE; +} + + +static void ss_size_sig(GtkWidget *widget, GdkRectangle *rect, SCSlideshow *ss) +{ + const double sw = ss->p->slide_width; + const double sh = ss->p->slide_height; + + if ( sw/sh > (double)rect->width/rect->height ) { + /* Slide is too wide. Letterboxing top/bottom */ + ss->slide_width = rect->width; + ss->slide_height = rect->width * sh/sw; + } else { + /* Letterboxing at sides */ + ss->slide_width = rect->height * sw/sh; + ss->slide_height = rect->height; + } + + ss->xoff = (rect->width - ss->slide_width)/2.0; + ss->yoff = (rect->height - ss->slide_height)/2.0; + + printf("screen %i %i\n", rect->width, rect->height); + printf("slide %f %f\n", sw, sh); + printf("rendering slide at %i %i\n", ss->slide_width, ss->slide_height); + printf("offset %i %i\n", ss->xoff, ss->yoff); + + slideshow_rerender(ss); +} + + +void sc_slideshow_set_slide(SCSlideshow *ss, SCBlock *ns) +{ + ss->cur_slide = ns; + slideshow_rerender(ss); +} + + +SCSlideshow *sc_slideshow_new(struct presentation *p, GtkApplication *app) +{ + GdkDisplay *display; + int n_monitors; + SCSlideshow *ss; + + ss = g_object_new(SC_TYPE_SLIDESHOW, NULL); + if ( ss == NULL ) return NULL; + + ss->blank = 0; + ss->p = p; + ss->cur_slide = NULL; + ss->blank_cursor = NULL; + ss->surface = NULL; + ss->app = app; + + ss->drawingarea = gtk_drawing_area_new(); + gtk_container_add(GTK_CONTAINER(ss), ss->drawingarea); + + gtk_widget_set_can_focus(GTK_WIDGET(ss->drawingarea), TRUE); + gtk_widget_add_events(GTK_WIDGET(ss->drawingarea), + GDK_KEY_PRESS_MASK); + + g_signal_connect(G_OBJECT(ss), "destroy", + G_CALLBACK(ssh_destroy_sig), ss); + g_signal_connect(G_OBJECT(ss), "realize", + G_CALLBACK(ss_realize_sig), ss); + g_signal_connect(G_OBJECT(ss), "size-allocate", + G_CALLBACK(ss_size_sig), ss); + + g_signal_connect(G_OBJECT(ss->drawingarea), "draw", + G_CALLBACK(ss_draw_sig), ss); + + gtk_widget_grab_focus(GTK_WIDGET(ss->drawingarea)); + + display = gdk_display_get_default(); + n_monitors = gdk_display_get_n_monitors(display); + + GdkMonitor *mon_ss; + if ( n_monitors == 1 ) { + mon_ss = gdk_display_get_primary_monitor(display); + printf(_("Single monitor mode\n")); + ss->single_monitor = 1; + } else { + mon_ss = gdk_display_get_monitor(display, 1); + printf(_("Dual monitor mode\n")); + ss->single_monitor = 0; + } + + /* Workaround because gtk_window_fullscreen_on_monitor doesn't work */ + GdkRectangle rect; + gdk_monitor_get_geometry(mon_ss, &rect); + gtk_window_move(GTK_WINDOW(ss), rect.x, rect.y); + gtk_window_fullscreen(GTK_WINDOW(ss)); + + ss->linked = 1; + + if ( app != NULL ) { + ss->inhibit_cookie = gtk_application_inhibit(app, GTK_WINDOW(ss), + GTK_APPLICATION_INHIBIT_IDLE, + _("Presentation slide show is running")); + } + + return ss; +} + diff --git a/src-old/slideshow.h b/src-old/slideshow.h new file mode 100644 index 0000000..ff16d73 --- /dev/null +++ b/src-old/slideshow.h @@ -0,0 +1,83 @@ +/* + * slideshow.h + * + * Copyright © 2013-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 SLIDESHOW_H +#define SLIDESHOW_H + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include + +#define SC_TYPE_SLIDESHOW (sc_slideshow_get_type()) + +#define SC_SLIDESHOW(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), \ + SC_TYPE_SLIDESHOW, SCEditor)) + +#define SC_IS_SLIDESHOW(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), \ + SC_TYPE_SLIDESHOW)) + +#define SC_SLIDESHOW_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((obj), \ + SC_TYPE_SLIDESHOW, SCEditorClass)) + +#define SC_IS_SLIDESHOW_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((obj), \ + SC_TYPE_SLIDESHOW)) + +#define SC_SLIDESHOW_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), \ + SC_TYPE_SLIDESHOW, SCSlideShowClass)) + +struct _scslideshow +{ + GtkWindow parent_instance; + + /* */ + struct presentation *p; + SCBlock *cur_slide; + GtkWidget *drawingarea; + GdkCursor *blank_cursor; + int blank; + int slide_width; + int slide_height; + int xoff; + int yoff; + int linked; + cairo_surface_t *surface; + struct frame *top; + int single_monitor; + uint inhibit_cookie; + GtkApplication *app; +}; + + +struct _scslideshowclass +{ + GtkWindowClass parent_class; +}; + +typedef struct _scslideshow SCSlideshow; +typedef struct _scslideshowclass SCSlideshowClass; + +extern SCSlideshow *sc_slideshow_new(struct presentation *p, GtkApplication *app); +extern void sc_slideshow_set_slide(SCSlideshow *ss, SCBlock *ns); + +#endif /* SLIDESHOW_H */ diff --git a/src-old/stylesheet.c b/src-old/stylesheet.c new file mode 100644 index 0000000..a6e2531 --- /dev/null +++ b/src-old/stylesheet.c @@ -0,0 +1,288 @@ +/* + * stylesheet.c + * + * Copyright © 2013-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 +#include + +#include "stylesheet.h" +#include "utils.h" + + +struct _stylesheet { + JsonNode *root; +}; + + +static int find_comma(const char *a) +{ + int i = 0; + int in_brackets = 0; + size_t len = strlen(a); + + do { + if ( (a[i] == ',') && !in_brackets ) return i; + if ( a[i] == '(' ) in_brackets++; + if ( a[i] == ')' ) in_brackets--; + i++; + } while ( i < len ); + return 0; +} + + +int parse_colour_duo(const char *a, GdkRGBA *col1, GdkRGBA *col2) +{ + char *acopy; + int cpos; + + acopy = strdup(a); + if ( acopy == NULL ) return 1; + + cpos = find_comma(acopy); + if ( cpos == 0 ) { + fprintf(stderr, _("Invalid background gradient '%s'\n"), a); + return 1; + } + + acopy[cpos] = '\0'; + + if ( gdk_rgba_parse(col1, acopy) != TRUE ) { + fprintf(stderr, _("Failed to parse colour: %s\n"), acopy); + } + if ( gdk_rgba_parse(col2, &acopy[cpos+1]) != TRUE ) { + fprintf(stderr, _("Failed to parse colour: %s\n"), &acopy[cpos+1]); + } + + free(acopy); + return 0; +} + + +Stylesheet *stylesheet_load(GFile *file) +{ + JsonParser *parser; + gboolean r; + GError *err = NULL; + Stylesheet *ss; + char *everything; + gsize len; + + printf("Trying stylesheet '%s'\n", g_file_get_uri(file)); + + ss = calloc(1, sizeof(Stylesheet)); + if ( ss == NULL ) return NULL; + + parser = json_parser_new(); + + if ( !g_file_load_contents(file, NULL, &everything, &len, NULL, NULL) ) { + fprintf(stderr, _("Failed to load stylesheet '%s'\n"), + g_file_get_uri(file)); + free(ss); + return NULL; + } + + r = json_parser_load_from_data(parser, everything, len, &err); + if ( r == FALSE ) { + fprintf(stderr, _("Failed to load stylesheet: %s\n"), + err->message); + free(ss); + return NULL; + } + + ss->root = json_parser_steal_root(parser); + g_object_unref(parser); + + return ss; +} + + +static JsonObject *find_stylesheet_object(Stylesheet *ss, const char *path, + JsonNode **freeme) +{ + JsonNode *node; + JsonObject *obj; + JsonArray *array; + GError *err = NULL; + + node = json_path_query(path, ss->root, &err); + array = json_node_get_array(node); + + if ( json_array_get_length(array) == 0 ) { + json_node_unref(node); + return NULL; + } + + if ( json_array_get_length(array) > 1 ) { + json_node_unref(node); + fprintf(stderr, "More than one result in SS lookup (%s)!\n", path); + return NULL; + } + + obj = json_array_get_object_element(array, 0); + if ( obj == NULL ) { + printf("%s not a JSON object\n", path); + json_node_unref(node); + return NULL; + } + + *freeme = node; + return obj; +} + + +char *stylesheet_lookup(Stylesheet *ss, const char *path, const char *key) +{ + JsonObject *obj; + char *ret = NULL; + JsonNode *node = NULL; + + if ( ss == NULL ) { + fprintf(stderr, "No stylesheet!\n"); + return NULL; + } + + obj = find_stylesheet_object(ss, path, &node); + if ( obj == NULL ) return NULL; + + if ( json_object_has_member(obj, key) ) { + + const gchar *v; + v = json_object_get_string_member(obj, key); + if ( v != NULL ) { + ret = strdup(v); + } else { + fprintf(stderr, "Error retrieving %s.%s\n", path, key); + } + + } /* else not found, too bad */ + + if ( node != NULL ) json_node_unref(node); + return ret; +} + + +int stylesheet_set(Stylesheet *ss, const char *path, const char *key, + const char *new_val) +{ + JsonObject *obj; + JsonNode *node = NULL; + int r = 1; + + if ( ss == NULL ) { + fprintf(stderr, "No stylesheet!\n"); + return 1; + } + + obj = find_stylesheet_object(ss, path, &node); + if ( obj != NULL ) { + json_object_set_string_member(obj, key, new_val); + r = 0; + } /* else most likely the object (e.g. "$.slide", "$.slide.frame", + * "$.narrative" etc doesn't exist */ + + if ( node != NULL ) json_node_unref(node); + return r; +} + + +int stylesheet_delete(Stylesheet *ss, const char *path, const char *key) +{ + JsonObject *obj; + JsonNode *node = NULL; + int r = 1; + + if ( ss == NULL ) { + fprintf(stderr, "No stylesheet!\n"); + return 1; + } + + obj = find_stylesheet_object(ss, path, &node); + if ( obj != NULL ) { + json_object_remove_member(obj, key); + r = 0; + } /* else most likely the object (e.g. "$.slide", "$.slide.frame", + * "$.narrative" etc doesn't exist */ + + if ( node != NULL ) json_node_unref(node); + return r; +} + + +void stylesheet_free(Stylesheet *ss) +{ + json_node_unref(ss->root); + free(ss); +} + + +int stylesheet_save(Stylesheet *ss, GFile *file) +{ + JsonGenerator *gen; + GError *error = NULL; + GFileOutputStream *fh; + + fh = g_file_replace(file, NULL, FALSE, G_FILE_CREATE_NONE, NULL, &error); + if ( fh == NULL ) { + fprintf(stderr, _("Open failed: %s\n"), error->message); + return 1; + } + + gen = json_generator_new(); + json_generator_set_root(gen, ss->root); + json_generator_set_pretty(gen, TRUE); + json_generator_set_indent(gen, 1); + json_generator_set_indent_char(gen, '\t'); + if ( !json_generator_to_stream(gen, G_OUTPUT_STREAM(fh), NULL, &error) ) { + fprintf(stderr, _("Open failed: %s\n"), error->message); + return 1; + } + g_object_unref(fh); + return 0; +} + + +char *stylesheet_data(Stylesheet *ss) +{ + return json_to_string(ss->root, FALSE); +} + + +void stylesheet_set_data(Stylesheet *ss, const char *data) +{ + JsonNode *new_root; + GError *err = NULL; + new_root = json_from_string(data, &err); + if ( new_root == NULL ) { + fprintf(stderr, "Internal parse error: %s\n", err->message); + return; + } + json_node_unref(ss->root); + ss->root = new_root; +} diff --git a/src-old/stylesheet.h b/src-old/stylesheet.h new file mode 100644 index 0000000..16d0a0a --- /dev/null +++ b/src-old/stylesheet.h @@ -0,0 +1,54 @@ +/* + * stylesheet.h + * + * Copyright © 2013-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 STYLESHEET_H +#define STYLESHEET_H + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include + +typedef struct _stylesheet Stylesheet; + +extern Stylesheet *stylesheet_load(GFile *file); + +extern int stylesheet_save(Stylesheet *ss, GFile *file); + +extern char *stylesheet_data(Stylesheet *ss); + +extern void stylesheet_set_data(Stylesheet *ss, const char *data); + +extern int parse_colour_duo(const char *a, GdkRGBA *col1, GdkRGBA *col2); + +extern char *stylesheet_lookup(Stylesheet *ss, const char *path, const char *key); + +extern int stylesheet_set(Stylesheet *ss, const char *path, const char *key, + const char *new_val); + +extern int stylesheet_delete(Stylesheet *ss, const char *path, const char *key); + +extern void stylesheet_free(Stylesheet *ss); + +#endif /* STYLESHEET_H */ diff --git a/src-old/stylesheet_editor.c b/src-old/stylesheet_editor.c new file mode 100644 index 0000000..292bbf1 --- /dev/null +++ b/src-old/stylesheet_editor.c @@ -0,0 +1,794 @@ +/* + * stylesheet_editor.c + * + * Copyright © 2013-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 "stylesheet_editor.h" +#include "presentation.h" +#include "sc_interp.h" +#include "stylesheet.h" +#include "utils.h" + + +G_DEFINE_TYPE_WITH_CODE(StylesheetEditor, stylesheet_editor, + GTK_TYPE_DIALOG, NULL) + + +struct _sspriv +{ + struct presentation *p; + const gchar *furniture; + char *ssdata; +}; + + +static void set_font_from_ss(Stylesheet *ss, const char *path, GtkWidget *w) +{ + char *result = stylesheet_lookup(ss, path, "font"); + if ( result != NULL ) { + gtk_font_button_set_font_name(GTK_FONT_BUTTON(w), result); + } +} + + +static void set_col_from_ss(Stylesheet *ss, const char *path, GtkWidget *w) +{ + char *result = stylesheet_lookup(ss, path, "fgcol"); + if ( result != NULL ) { + GdkRGBA rgba; + if ( gdk_rgba_parse(&rgba, result) == TRUE ) { + gtk_color_chooser_set_rgba(GTK_COLOR_CHOOSER(w), &rgba); + } + } +} + + +static void set_vals_from_ss(Stylesheet *ss, const char *path, const char *key, + GtkWidget *wl, GtkWidget *wr, + GtkWidget *wt, GtkWidget *wb) +{ + char *result = stylesheet_lookup(ss, path, key); + if ( result != NULL ) { + float v[4]; + if ( parse_tuple(result, v) == 0 ) { + gtk_spin_button_set_value(GTK_SPIN_BUTTON(wl), v[0]); + gtk_spin_button_set_value(GTK_SPIN_BUTTON(wr), v[1]); + gtk_spin_button_set_value(GTK_SPIN_BUTTON(wt), v[2]); + gtk_spin_button_set_value(GTK_SPIN_BUTTON(wb), v[3]); + } else { + fprintf(stderr, _("Failed to parse quad: %s\n"), result); + } + } else { + printf("Not found %s.%s\n", path, key); + } +} + + +static void set_geom_from_ss(Stylesheet *ss, const char *path, const char *key, + GtkWidget *ww, GtkWidget *wh, + GtkWidget *wx, GtkWidget *wy, + GtkWidget *wwu, GtkWidget *whu) +{ + char *result = stylesheet_lookup(ss, path, key); + if ( result != NULL ) { + double x, y, w, h; + LengthUnits wu, hu; + if ( parse_dims(result, &w, &h, &wu, &hu, &x, &y) == 0 ) { + if ( wu == UNITS_FRAC ) { + w *= 100; + gtk_combo_box_set_active_id(GTK_COMBO_BOX(wwu), "percent"); + } else { + gtk_combo_box_set_active_id(GTK_COMBO_BOX(wwu), "units"); + } + if ( hu == UNITS_FRAC ) { + h *= 100; + gtk_combo_box_set_active_id(GTK_COMBO_BOX(whu), "percent"); + } else { + gtk_combo_box_set_active_id(GTK_COMBO_BOX(whu), "units"); + } + gtk_spin_button_set_value(GTK_SPIN_BUTTON(ww), w); + gtk_spin_button_set_value(GTK_SPIN_BUTTON(wh), h); + gtk_spin_button_set_value(GTK_SPIN_BUTTON(wx), x); + gtk_spin_button_set_value(GTK_SPIN_BUTTON(wy), y); + } else { + fprintf(stderr, _("Failed to parse dims: %s\n"), result); + } + } else { + printf("Not found %s.%s\n", path, key); + } +} + +static void set_size_from_ss(Stylesheet *ss, const char *path, + GtkWidget *ww, GtkWidget *wh) +{ + char *result = stylesheet_lookup(ss, path, "size"); + if ( result != NULL ) { + float v[2]; + if ( parse_double(result, v) == 0 ) { + gtk_spin_button_set_value(GTK_SPIN_BUTTON(ww), v[0]); + gtk_spin_button_set_value(GTK_SPIN_BUTTON(wh), v[1]); + } else { + fprintf(stderr, _("Failed to parse double: %s\n"), result); + } + } else { + printf("Not found %s.size\n", path); + } +} + + +static int alignment_ok(const char *a) +{ + if ( a == NULL ) return 0; + if ( strcmp(a, "left") == 0 ) return 1; + if ( strcmp(a, "center") == 0 ) return 1; + if ( strcmp(a, "right") == 0 ) return 1; + return 0; +} + + +static void set_alignment_from_ss(Stylesheet *ss, const char *path, + GtkWidget *d) +{ + char *result = stylesheet_lookup(ss, path, "alignment"); + if ( alignment_ok(result) ) { + gtk_combo_box_set_active_id(GTK_COMBO_BOX(d), result); + } +} + + +static void set_bg_from_ss(Stylesheet *ss, const char *path, GtkWidget *wcol, + GtkWidget *wcol2, GtkWidget *wgrad) +{ + char *result; + int found = 0; + + result = stylesheet_lookup(ss, path, "bgcol"); + if ( result != NULL ) { + GdkRGBA rgba; + found = 1; + if ( gdk_rgba_parse(&rgba, result) == TRUE ) { + gtk_color_chooser_set_rgba(GTK_COLOR_CHOOSER(wcol), &rgba); + gtk_combo_box_set_active_id(GTK_COMBO_BOX(wgrad), "flat"); + gtk_widget_set_sensitive(wcol, TRUE); + gtk_widget_set_sensitive(wcol2, FALSE); + } else { + fprintf(stderr, _("Failed to parse colour: %s\n"), result); + } + } + + result = stylesheet_lookup(ss, path, "bggradv"); + if ( result != NULL ) { + GdkRGBA rgba1, rgba2; + found = 1; + if ( parse_colour_duo(result, &rgba1, &rgba2) == 0 ) { + gtk_color_chooser_set_rgba(GTK_COLOR_CHOOSER(wcol), &rgba1); + gtk_color_chooser_set_rgba(GTK_COLOR_CHOOSER(wcol2), &rgba2); + gtk_combo_box_set_active_id(GTK_COMBO_BOX(wgrad), "vert"); + gtk_widget_set_sensitive(wcol, TRUE); + gtk_widget_set_sensitive(wcol2, TRUE); + } + } + + result = stylesheet_lookup(ss, path, "bggradh"); + if ( result != NULL ) { + GdkRGBA rgba1, rgba2; + found = 1; + if ( parse_colour_duo(result, &rgba1, &rgba2) == 0 ) { + gtk_color_chooser_set_rgba(GTK_COLOR_CHOOSER(wcol), &rgba1); + gtk_color_chooser_set_rgba(GTK_COLOR_CHOOSER(wcol2), &rgba2); + gtk_combo_box_set_active_id(GTK_COMBO_BOX(wgrad), "horiz"); + gtk_widget_set_sensitive(wcol, TRUE); + gtk_widget_set_sensitive(wcol2, TRUE); + } + } + + if ( !found ) { + GdkRGBA rgba; + rgba.red = 1.0; + rgba.green = 1.0; + rgba.blue = 1.0; + rgba.alpha = 0.0; + gtk_combo_box_set_active_id(GTK_COMBO_BOX(wgrad), "flat"); + gtk_color_chooser_set_rgba(GTK_COLOR_CHOOSER(wcol), &rgba); + gtk_widget_set_sensitive(wcol, TRUE); + gtk_widget_set_sensitive(wcol2, FALSE); + } +} + + +static void set_furniture(StylesheetEditor *se, const char *furniture) +{ + set_geom_from_ss(se->priv->p->stylesheet, furniture, "geometry", + se->furniture_w, + se->furniture_h, + se->furniture_x, + se->furniture_y, + se->furniture_w_units, + se->furniture_h_units); + + set_vals_from_ss(se->priv->p->stylesheet, furniture, "pad", + se->furniture_padding_l, + se->furniture_padding_r, + se->furniture_padding_t, + se->furniture_padding_b); + + set_vals_from_ss(se->priv->p->stylesheet, furniture, "paraspace", + se->furniture_paraspace_l, + se->furniture_paraspace_r, + se->furniture_paraspace_t, + se->furniture_paraspace_b); + + set_font_from_ss(se->priv->p->stylesheet, furniture, se->furniture_font); + set_col_from_ss(se->priv->p->stylesheet, furniture, se->furniture_fgcol); + set_alignment_from_ss(se->priv->p->stylesheet, furniture, + se->furniture_alignment); + set_bg_from_ss(se->priv->p->stylesheet, furniture, se->furniture_bgcol, + se->furniture_bgcol2, se->furniture_bggrad); +} + + +static void set_values_from_presentation(StylesheetEditor *se) +{ + Stylesheet *ss = se->priv->p->stylesheet; + + /* Narrative */ + set_font_from_ss(ss, "$.narrative", se->narrative_style_font); + set_col_from_ss(ss, "$.narrative", se->narrative_style_fgcol); + set_alignment_from_ss(ss, "$.narrative", se->narrative_style_alignment); + set_bg_from_ss(ss, "$.narrative", se->narrative_style_bgcol, + se->narrative_style_bgcol2, + se->narrative_style_bggrad); + set_vals_from_ss(ss, "$.narrative", "pad", se->narrative_style_padding_l, + se->narrative_style_padding_r, + se->narrative_style_padding_t, + se->narrative_style_padding_b); + set_vals_from_ss(ss, "$.narrative", "paraspace", se->narrative_style_paraspace_l, + se->narrative_style_paraspace_r, + se->narrative_style_paraspace_t, + se->narrative_style_paraspace_b); + + /* Slides */ + set_size_from_ss(ss, "$.slide", se->slide_size_w, se->slide_size_h); + set_bg_from_ss(ss, "$.slide", se->slide_style_bgcol, + se->slide_style_bgcol2, + se->slide_style_bggrad); + + + /* Frames */ + set_font_from_ss(ss, "$.slide.frame", se->frame_style_font); + set_col_from_ss(ss, "$.slide.frame", se->frame_style_fgcol); + set_alignment_from_ss(ss, "$.slide.frame", se->frame_style_alignment); + set_bg_from_ss(ss, "$.slide.frame", se->frame_style_bgcol, + se->frame_style_bgcol2, + se->frame_style_bggrad); + set_vals_from_ss(ss, "$.slide.frame", "pad", se->frame_style_padding_l, + se->frame_style_padding_r, + se->frame_style_padding_t, + se->frame_style_padding_b); + set_vals_from_ss(ss, "$.slide.frame", "paraspace", se->frame_style_paraspace_l, + se->frame_style_paraspace_r, + se->frame_style_paraspace_t, + se->frame_style_paraspace_b); + + set_furniture(se, se->priv->furniture); +} + + +static GradientType id_to_gradtype(const gchar *id) +{ + assert(id != NULL); + if ( strcmp(id, "flat") == 0 ) return GRAD_NONE; + if ( strcmp(id, "horiz") == 0 ) return GRAD_HORIZ; + if ( strcmp(id, "vert") == 0 ) return GRAD_VERT; + return GRAD_NONE; +} + + +static void update_bg(struct presentation *p, const char *style_name, + GtkWidget *bggradw, GtkWidget *col1w, GtkWidget*col2w) +{ + GradientType g; + const gchar *id; + GdkRGBA rgba; + gchar *col1; + gchar *col2; + gchar *gradient; + + id = gtk_combo_box_get_active_id(GTK_COMBO_BOX(bggradw)); + g = id_to_gradtype(id); + + gtk_color_chooser_get_rgba(GTK_COLOR_CHOOSER(col1w), &rgba); + if ( rgba.alpha < 0.000001 ) rgba.alpha = 0.0; + col1 = gdk_rgba_to_string(&rgba); + + gtk_color_chooser_get_rgba(GTK_COLOR_CHOOSER(col2w), &rgba); + col2 = gdk_rgba_to_string(&rgba); + + gradient = g_strconcat(col1, ",", col2, NULL); + + switch ( g ) { + + case GRAD_NONE : + stylesheet_set(p->stylesheet, style_name, "bgcol", + col1); + stylesheet_delete(p->stylesheet, style_name, "bggradv"); + stylesheet_delete(p->stylesheet, style_name, "bggradh"); + break; + + case GRAD_HORIZ : + stylesheet_set(p->stylesheet, style_name, "bggradh", + gradient); + stylesheet_delete(p->stylesheet, style_name, "bggradv"); + stylesheet_delete(p->stylesheet, style_name, "bgcol"); + break; + + case GRAD_VERT : + stylesheet_set(p->stylesheet, style_name, "bggradv", + gradient); + stylesheet_delete(p->stylesheet, style_name, "bggradh"); + stylesheet_delete(p->stylesheet, style_name, "bgcol"); + break; + + } + + g_free(gradient); + g_free(col1); + g_free(col2); +} + + +static void update_spacing(struct presentation *p, const char *style_name, + const char *key, GtkWidget *wl, GtkWidget *wr, + GtkWidget *wt, GtkWidget *wb) +{ + int v[4]; + char tmp[256]; + + v[0] = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(wl)); + v[1] = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(wr)); + v[2] = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(wt)); + v[3] = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(wb)); + + if ( snprintf(tmp, 256, "%i,%i,%i,%i", v[0], v[1], v[2], v[3]) >= 256 ) { + fprintf(stderr, "Spacing too long\n"); + } else { + stylesheet_set(p->stylesheet, style_name, key, tmp); + } +} + + +static char units_id_to_char(const char *id) +{ + if ( strcmp(id, "units") == 0 ) return 'u'; + if ( strcmp(id, "percent") == 0 ) return 'f'; + return 'u'; +} + + +static void update_ss_dims(struct presentation *p, const char *style_name, + const char *key, GtkWidget *ww, GtkWidget *wh, + GtkWidget *wx, GtkWidget *wy, + GtkWidget *wwu, GtkWidget *whu) +{ + float w, h, x, y; + char w_units, h_units; + const gchar *uid; + char tmp[256]; + + w = gtk_spin_button_get_value(GTK_SPIN_BUTTON(ww)); + h = gtk_spin_button_get_value(GTK_SPIN_BUTTON(wh)); + x = gtk_spin_button_get_value(GTK_SPIN_BUTTON(wx)); + y = gtk_spin_button_get_value(GTK_SPIN_BUTTON(wy)); + uid = gtk_combo_box_get_active_id(GTK_COMBO_BOX(wwu)); + w_units = units_id_to_char(uid); + uid = gtk_combo_box_get_active_id(GTK_COMBO_BOX(whu)); + h_units = units_id_to_char(uid); + + if ( w_units == 'f' ) w /= 100.0; + if ( h_units == 'f' ) h /= 100.0; + + if ( snprintf(tmp, 256, "%.2f%cx%.2f%c+%.0f+%0.f", + w, w_units, h, h_units, x, y) >= 256 ) + { + fprintf(stderr, "Spacing too long\n"); + } else { + stylesheet_set(p->stylesheet, style_name, key, tmp); + } +} + + +static void revert_sig(GtkButton *button, StylesheetEditor *se) +{ + stylesheet_set_data(se->priv->p->stylesheet, + se->priv->ssdata); + set_values_from_presentation(se); + g_signal_emit_by_name(se, "changed"); +} + + +static void set_font(GtkFontButton *widget, StylesheetEditor *se, + const char *style_name) +{ + const gchar *font; + font = gtk_font_button_get_font_name(GTK_FONT_BUTTON(widget)); + + stylesheet_set(se->priv->p->stylesheet, style_name, "font", font); + set_values_from_presentation(se); + g_signal_emit_by_name(se, "changed"); +} + + +static void set_col(GtkColorButton *widget, StylesheetEditor *se, + const char *style_name, const char *col_name) +{ + GdkRGBA rgba; + gchar *col; + gtk_color_chooser_get_rgba(GTK_COLOR_CHOOSER(widget), &rgba); + col = gdk_rgba_to_string(&rgba); + stylesheet_set(se->priv->p->stylesheet, style_name, "fgcol", col); + g_free(col); + set_values_from_presentation(se); + g_signal_emit_by_name(se, "changed"); +} + + +static void narrative_font_sig(GtkFontButton *widget, StylesheetEditor *se) +{ + set_font(widget, se, "$.narrative"); +} + + +static void narrative_fgcol_sig(GtkColorButton *widget, StylesheetEditor *se) +{ + set_col(widget, se, "$.narrative", "fgcol"); +} + + +static void narrative_bg_sig(GtkColorButton *widget, StylesheetEditor *se) +{ + update_bg(se->priv->p, "$.narrative", + se->narrative_style_bggrad, + se->narrative_style_bgcol, + se->narrative_style_bgcol2); + set_values_from_presentation(se); + g_signal_emit_by_name(se, "changed"); +} + + +static void narrative_alignment_sig(GtkComboBoxText *widget, StylesheetEditor *se) +{ + const gchar *id = gtk_combo_box_get_active_id(GTK_COMBO_BOX(widget)); + stylesheet_set(se->priv->p->stylesheet, "$.narrative", "alignment", id); + set_values_from_presentation(se); + g_signal_emit_by_name(se, "changed"); +} + + +static void slide_size_sig(GtkSpinButton *widget, StylesheetEditor *se) +{ + int w, h; + char tmp[256]; + + w = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(se->slide_size_w)); + h = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(se->slide_size_h)); + + if ( snprintf(tmp, 256, "%ix%i", w, h) >= 256 ) { + fprintf(stderr, _("Slide size too long\n")); + } else { + stylesheet_set(se->priv->p->stylesheet, "$.slide", "size", tmp); + se->priv->p->slide_width = w; + se->priv->p->slide_height = h; + } + + set_values_from_presentation(se); + g_signal_emit_by_name(se, "changed"); +} + + +static void slide_bg_sig(GtkColorButton *widget, StylesheetEditor *se) +{ + update_bg(se->priv->p, "$.slide", + se->slide_style_bggrad, + se->slide_style_bgcol, + se->slide_style_bgcol2); + set_values_from_presentation(se); + g_signal_emit_by_name(se, "changed"); +} + + +static void frame_font_sig(GtkFontButton *widget, StylesheetEditor *se) +{ + set_font(widget, se, "$.slide.frame"); +} + + +static void frame_fgcol_sig(GtkColorButton *widget, StylesheetEditor *se) +{ + set_col(widget, se, "$.slide.frame", "fgcol"); +} + + +static void frame_bg_sig(GtkColorButton *widget, StylesheetEditor *se) +{ + update_bg(se->priv->p, "$.slide.frame", + se->frame_style_bggrad, + se->frame_style_bgcol, + se->frame_style_bgcol2); + set_values_from_presentation(se); + g_signal_emit_by_name(se, "changed"); +} + + +static void frame_padding_sig(GtkSpinButton *widget, StylesheetEditor *se) +{ + update_spacing(se->priv->p, "$.slide.frame", "pad", + se->frame_style_padding_l, + se->frame_style_padding_r, + se->frame_style_padding_t, + se->frame_style_padding_b); + set_values_from_presentation(se); + g_signal_emit_by_name(se, "changed"); +} + + +static void frame_paraspace_sig(GtkSpinButton *widget, StylesheetEditor *se) +{ + update_spacing(se->priv->p, "$.slide.frame", "paraspace", + se->frame_style_paraspace_l, + se->frame_style_paraspace_r, + se->frame_style_paraspace_t, + se->frame_style_paraspace_b); + set_values_from_presentation(se); + g_signal_emit_by_name(se, "changed"); +} + + +static void frame_alignment_sig(GtkComboBoxText *widget, StylesheetEditor *se) +{ + const gchar *id = gtk_combo_box_get_active_id(GTK_COMBO_BOX(widget)); + stylesheet_set(se->priv->p->stylesheet, "$.slide.frame", "alignment", id); + set_values_from_presentation(se); + g_signal_emit_by_name(se, "changed"); +} + + +static void narrative_padding_sig(GtkSpinButton *widget, StylesheetEditor *se) +{ + update_spacing(se->priv->p, "$.narrative", "pad", + se->narrative_style_padding_l, + se->narrative_style_padding_r, + se->narrative_style_padding_t, + se->narrative_style_padding_b); + set_values_from_presentation(se); + g_signal_emit_by_name(se, "changed"); +} + + +static void narrative_paraspace_sig(GtkSpinButton *widget, StylesheetEditor *se) +{ + update_spacing(se->priv->p, "$.narrative", "paraspace", + se->narrative_style_paraspace_l, + se->narrative_style_paraspace_r, + se->narrative_style_paraspace_t, + se->narrative_style_paraspace_b); + set_values_from_presentation(se); + g_signal_emit_by_name(se, "changed"); +} + + +static void furniture_selector_change_sig(GtkComboBoxText *widget, StylesheetEditor *se) +{ + se->priv->furniture = gtk_combo_box_get_active_id(GTK_COMBO_BOX(widget)); + set_furniture(se, se->priv->furniture); +} + + +static void furniture_font_sig(GtkFontButton *widget, StylesheetEditor *se) +{ + set_font(widget, se, se->priv->furniture); +} + + +static void furniture_fgcol_sig(GtkColorButton *widget, StylesheetEditor *se) +{ + set_col(widget, se,se->priv->furniture, "fgcol"); +} + + +static void furniture_bg_sig(GtkColorButton *widget, StylesheetEditor *se) +{ + update_bg(se->priv->p, se->priv->furniture, se->furniture_bggrad, + se->furniture_bgcol, se->furniture_bgcol2); + set_values_from_presentation(se); + g_signal_emit_by_name(se, "changed"); +} + + +static void furniture_paraspace_sig(GtkSpinButton *widget, StylesheetEditor *se) +{ + update_spacing(se->priv->p, se->priv->furniture, "pad", + se->furniture_padding_l, + se->furniture_padding_r, + se->furniture_padding_t, + se->furniture_padding_b); + set_values_from_presentation(se); + g_signal_emit_by_name(se, "changed"); +} + + +static void furniture_padding_sig(GtkSpinButton *widget, StylesheetEditor *se) +{ + update_spacing(se->priv->p, se->priv->furniture, "pad", + se->furniture_padding_l, + se->furniture_padding_r, + se->furniture_padding_t, + se->furniture_padding_b); + set_values_from_presentation(se); + g_signal_emit_by_name(se, "changed"); +} + + +static void furniture_alignment_sig(GtkComboBoxText *widget, StylesheetEditor *se) +{ + const gchar *id = gtk_combo_box_get_active_id(GTK_COMBO_BOX(widget)); + stylesheet_set(se->priv->p->stylesheet, se->priv->furniture, + "alignment", id); + set_values_from_presentation(se); + g_signal_emit_by_name(se, "changed"); +} + + +static void furniture_dims_sig(GtkSpinButton *widget, StylesheetEditor *se) +{ + update_ss_dims(se->priv->p, se->priv->furniture, "geometry", + se->furniture_w, se->furniture_h, + se->furniture_x, se->furniture_y, + se->furniture_w_units, se->furniture_h_units); + set_values_from_presentation(se); + g_signal_emit_by_name(se, "changed"); +} + + +static void stylesheet_editor_finalize(GObject *obj) +{ + StylesheetEditor *se = COLLOQUIUM_STYLESHEET_EDITOR(obj); + free(se->priv->ssdata); + G_OBJECT_CLASS(stylesheet_editor_parent_class)->finalize(obj); +} + + +static void stylesheet_editor_init(StylesheetEditor *se) +{ + se->priv = G_TYPE_INSTANCE_GET_PRIVATE(se, COLLOQUIUM_TYPE_STYLESHEET_EDITOR, + StylesheetEditorPrivate); + gtk_widget_init_template(GTK_WIDGET(se)); +} + + +#define SE_BIND_CHILD(a, b) \ + gtk_widget_class_bind_template_child(widget_class, StylesheetEditor, a); \ + gtk_widget_class_bind_template_callback(widget_class, b); + +void stylesheet_editor_class_init(StylesheetEditorClass *klass) +{ + GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(klass); + GObjectClass *gobject_class = G_OBJECT_CLASS(klass); + + gtk_widget_class_set_template_from_resource(widget_class, + "/uk/me/bitwiz/Colloquium/stylesheeteditor.ui"); + + g_type_class_add_private(gobject_class, sizeof(StylesheetEditorPrivate)); + gobject_class->finalize = stylesheet_editor_finalize; + + /* Narrative style */ + SE_BIND_CHILD(narrative_style_font, narrative_font_sig); + SE_BIND_CHILD(narrative_style_fgcol, narrative_fgcol_sig); + SE_BIND_CHILD(narrative_style_bgcol, narrative_bg_sig); + SE_BIND_CHILD(narrative_style_bgcol2, narrative_bg_sig); + SE_BIND_CHILD(narrative_style_bggrad, narrative_bg_sig); + SE_BIND_CHILD(narrative_style_paraspace_l, narrative_paraspace_sig); + SE_BIND_CHILD(narrative_style_paraspace_r, narrative_paraspace_sig); + SE_BIND_CHILD(narrative_style_paraspace_t, narrative_paraspace_sig); + SE_BIND_CHILD(narrative_style_paraspace_b, narrative_paraspace_sig); + SE_BIND_CHILD(narrative_style_padding_l, narrative_padding_sig); + SE_BIND_CHILD(narrative_style_padding_r, narrative_padding_sig); + SE_BIND_CHILD(narrative_style_padding_t, narrative_padding_sig); + SE_BIND_CHILD(narrative_style_padding_b, narrative_padding_sig); + SE_BIND_CHILD(narrative_style_alignment, narrative_alignment_sig); + + /* Slide style */ + SE_BIND_CHILD(slide_size_w, slide_size_sig); + SE_BIND_CHILD(slide_size_h, slide_size_sig); + SE_BIND_CHILD(slide_style_bgcol, slide_bg_sig); + SE_BIND_CHILD(slide_style_bgcol2, slide_bg_sig); + SE_BIND_CHILD(slide_style_bggrad, slide_bg_sig); + + /* Slide->frame style */ + SE_BIND_CHILD(frame_style_font, frame_font_sig); + SE_BIND_CHILD(frame_style_fgcol, frame_fgcol_sig); + SE_BIND_CHILD(frame_style_bgcol, frame_bg_sig); + SE_BIND_CHILD(frame_style_bgcol2, frame_bg_sig); + SE_BIND_CHILD(frame_style_bggrad, frame_bg_sig); + SE_BIND_CHILD(frame_style_paraspace_l, frame_paraspace_sig); + SE_BIND_CHILD(frame_style_paraspace_r, frame_paraspace_sig); + SE_BIND_CHILD(frame_style_paraspace_t, frame_paraspace_sig); + SE_BIND_CHILD(frame_style_paraspace_b, frame_paraspace_sig); + SE_BIND_CHILD(frame_style_padding_l, frame_padding_sig); + SE_BIND_CHILD(frame_style_padding_r, frame_padding_sig); + SE_BIND_CHILD(frame_style_padding_t, frame_padding_sig); + SE_BIND_CHILD(frame_style_padding_b, frame_padding_sig); + SE_BIND_CHILD(frame_style_alignment, frame_alignment_sig); + + /* Furniture */ + SE_BIND_CHILD(furniture_selector, furniture_selector_change_sig); + SE_BIND_CHILD(furniture_paraspace_l, furniture_paraspace_sig); + SE_BIND_CHILD(furniture_paraspace_r, furniture_paraspace_sig); + SE_BIND_CHILD(furniture_paraspace_t, furniture_paraspace_sig); + SE_BIND_CHILD(furniture_paraspace_b, furniture_paraspace_sig); + SE_BIND_CHILD(furniture_padding_l, furniture_padding_sig); + SE_BIND_CHILD(furniture_padding_r, furniture_padding_sig); + SE_BIND_CHILD(furniture_padding_t, furniture_padding_sig); + SE_BIND_CHILD(furniture_padding_b, furniture_padding_sig); + SE_BIND_CHILD(furniture_font, furniture_font_sig); + SE_BIND_CHILD(furniture_fgcol, furniture_fgcol_sig); + SE_BIND_CHILD(furniture_bgcol, furniture_bg_sig); + SE_BIND_CHILD(furniture_bgcol2, furniture_bg_sig); + SE_BIND_CHILD(furniture_bggrad, furniture_bg_sig); + SE_BIND_CHILD(furniture_alignment, furniture_alignment_sig); + SE_BIND_CHILD(furniture_w, furniture_dims_sig); + SE_BIND_CHILD(furniture_h, furniture_dims_sig); + SE_BIND_CHILD(furniture_x, furniture_dims_sig); + SE_BIND_CHILD(furniture_y, furniture_dims_sig); + SE_BIND_CHILD(furniture_w_units, furniture_dims_sig); + SE_BIND_CHILD(furniture_h_units, furniture_dims_sig); + + gtk_widget_class_bind_template_callback(widget_class, revert_sig); + + g_signal_new("changed", COLLOQUIUM_TYPE_STYLESHEET_EDITOR, + G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 0); +} + + +StylesheetEditor *stylesheet_editor_new(struct presentation *p) +{ + StylesheetEditor *se; + + se = g_object_new(COLLOQUIUM_TYPE_STYLESHEET_EDITOR, NULL); + if ( se == NULL ) return NULL; + + se->priv->p = p; + se->priv->furniture = gtk_combo_box_get_active_id(GTK_COMBO_BOX(se->furniture_selector)); + set_values_from_presentation(se); + + se->priv->ssdata = stylesheet_data(p->stylesheet); + + return se; +} + diff --git a/src-old/stylesheet_editor.h b/src-old/stylesheet_editor.h new file mode 100644 index 0000000..a7c77b6 --- /dev/null +++ b/src-old/stylesheet_editor.h @@ -0,0 +1,128 @@ +/* + * stylesheet_editor.h + * + * Copyright © 2013-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 STYLESHEET_EDITOR_H +#define STYLESHEET_EDITOR_H + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include + +#include "presentation.h" +#include "frame.h" + +#define COLLOQUIUM_TYPE_STYLESHEET_EDITOR (stylesheet_editor_get_type()) + +#define COLLOQUIUM_STYLESHEET_EDITOR(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), \ + COLLOQUIUM_TYPE_STYLESHEET_EDITOR, \ + StylesheetEditor)) + +#define COLLOQUIUM_IS_STYLESHEET_EDITOR(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), \ + COLLOQUIUM_TYPE_STYLESHEET_EDITOR)) + +#define COLLOQUIUM_STYLESHEET_EDITOR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((obj), \ + COLLOQUIUM_TYPE_STYLESHEET_EDITOR, \ + StylesheetEditorClass)) + +#define COLLOQUIUM_IS_STYLESHEET_EDITOR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((obj), \ + COLLOQUIUM_TYPE_STYLESHEET_EDITOR)) + +#define COLLOQUIUM_STYLESHEET_EDITOR_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), \ + COLLOQUIUM_TYPE_STYLESHEET_EDITOR, \ + StylesheetEditorClass)) + + +typedef struct _sspriv StylesheetEditorPrivate; + +struct _stylesheeteditor +{ + GtkDialog parent_instance; + GtkWidget *narrative_style_font; + GtkWidget *narrative_style_fgcol; + GtkWidget *narrative_style_bgcol; + GtkWidget *narrative_style_bgcol2; + GtkWidget *narrative_style_bggrad; + GtkWidget *narrative_style_paraspace_l; + GtkWidget *narrative_style_paraspace_r; + GtkWidget *narrative_style_paraspace_t; + GtkWidget *narrative_style_paraspace_b; + GtkWidget *narrative_style_padding_l; + GtkWidget *narrative_style_padding_r; + GtkWidget *narrative_style_padding_t; + GtkWidget *narrative_style_padding_b; + GtkWidget *narrative_style_alignment; + GtkWidget *slide_size_w; + GtkWidget *slide_size_h; + GtkWidget *slide_style_bgcol; + GtkWidget *slide_style_bgcol2; + GtkWidget *slide_style_bggrad; + GtkWidget *frame_style_font; + GtkWidget *frame_style_fgcol; + GtkWidget *frame_style_bgcol; + GtkWidget *frame_style_bgcol2; + GtkWidget *frame_style_bggrad; + GtkWidget *frame_style_paraspace_l; + GtkWidget *frame_style_paraspace_r; + GtkWidget *frame_style_paraspace_t; + GtkWidget *frame_style_paraspace_b; + GtkWidget *frame_style_padding_l; + GtkWidget *frame_style_padding_r; + GtkWidget *frame_style_padding_t; + GtkWidget *frame_style_padding_b; + GtkWidget *frame_style_alignment; + GtkWidget *furniture_selector; + GtkWidget *furniture_paraspace_l; + GtkWidget *furniture_paraspace_r; + GtkWidget *furniture_paraspace_t; + GtkWidget *furniture_paraspace_b; + GtkWidget *furniture_padding_l; + GtkWidget *furniture_padding_r; + GtkWidget *furniture_padding_t; + GtkWidget *furniture_padding_b; + GtkWidget *furniture_font; + GtkWidget *furniture_fgcol; + GtkWidget *furniture_bgcol; + GtkWidget *furniture_bgcol2; + GtkWidget *furniture_bggrad; + GtkWidget *furniture_alignment; + GtkWidget *furniture_w; + GtkWidget *furniture_h; + GtkWidget *furniture_x; + GtkWidget *furniture_y; + GtkWidget *furniture_w_units; + GtkWidget *furniture_h_units; + StylesheetEditorPrivate *priv; +}; + +struct _stylesheeteditorclass +{ + GtkDialogClass parent_class; +}; + +typedef struct _stylesheeteditor StylesheetEditor; +typedef struct _stylesheeteditorclass StylesheetEditorClass; + +extern StylesheetEditor *stylesheet_editor_new(struct presentation *p); + +#endif /* STYLESHEET_EDITOR_H */ diff --git a/src-old/testcard.c b/src-old/testcard.c new file mode 100644 index 0000000..2b6598f --- /dev/null +++ b/src-old/testcard.c @@ -0,0 +1,288 @@ +/* + * testcard.c + * + * Copyright © 2013-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 + +#include "presentation.h" +#include "utils.h" + + +struct testcard +{ + GtkWidget *window; + char geom[256]; + int slide_width; + int slide_height; + GtkWidget *drawingarea; + struct presentation *p; +}; + +static gint tc_destroy_sig(GtkWidget *widget, struct testcard *tc) +{ + free(tc); + return FALSE; +} + + +static void arrow_left(cairo_t *cr, double size) +{ + cairo_rel_line_to(cr, size, size); + cairo_rel_line_to(cr, 0.0, -2*size); + cairo_rel_line_to(cr, -size, size); +} + + +static void arrow_right(cairo_t *cr, double size) +{ + cairo_rel_line_to(cr, -size, size); + cairo_rel_line_to(cr, 0.0, -2*size); + cairo_rel_line_to(cr, size, size); +} + + +static void arrow_down(cairo_t *cr, double size) +{ + cairo_rel_line_to(cr, -size, -size); + cairo_rel_line_to(cr, 2*size, 0.0); + cairo_rel_line_to(cr, -size, size); +} + + +static void arrow_up(cairo_t *cr, double size) +{ + cairo_rel_line_to(cr, -size, size); + cairo_rel_line_to(cr, 2*size, 0.0); + cairo_rel_line_to(cr, -size, -size); +} + + +static void colour_box(cairo_t *cr, double x, double y, + double r, double g, double b, const char *label) +{ + const double w = 50.0; + const double h = 50.0; + cairo_text_extents_t size; + + cairo_rectangle(cr, x+0.5, y+0.5, w, h); + cairo_set_source_rgb(cr, r, g, b); + cairo_fill_preserve(cr); + cairo_set_source_rgb(cr, 0.0, 0.0, 0.0); + cairo_set_line_width(cr, 1.0); + cairo_stroke(cr); + + cairo_set_font_size(cr, 24.0); + cairo_text_extents(cr, label, &size); + cairo_move_to(cr, x+(w/2.0)-(size.width/2.0), y-10.0); + cairo_show_text(cr, label); + +} + + +static gboolean tc_draw_sig(GtkWidget *da, cairo_t *cr, struct testcard *tc) +{ + double xoff, yoff; + double width, height; + int h; + PangoLayout *pl; + PangoFontDescription *desc; + char tmp[1024]; + int plw, plh; + double xp, yp; + + width = gtk_widget_get_allocated_width(GTK_WIDGET(da)); + height = gtk_widget_get_allocated_height(GTK_WIDGET(da)); + + /* Overall background */ + cairo_rectangle(cr, 0.0, 0.0, width, height); + cairo_set_source_rgb(cr, 0.0, 0.0, 1.0); + cairo_fill(cr); + + /* FIXME: Assumes that monitor and slide sizes are such that + * letterboxing at sides. This needn't be the case. */ + h = tc->slide_width * tc->p->slide_height / tc->p->slide_width; + + /* Get the overall size */ + xoff = (width - tc->slide_width)/2.0; + yoff = (height - h)/2.0; + + /* Background of slide */ + cairo_rectangle(cr, xoff, yoff, tc->slide_width, h); + cairo_set_source_rgb(cr, 1.0, 1.0, 1.0); + cairo_fill(cr); + + /* Arrows showing edges of screen */ + cairo_set_source_rgb(cr, 0.0, 0.0, 0.0); + cairo_move_to(cr, 0.0, height/2); + arrow_left(cr, 100.0); + cairo_fill(cr); + cairo_move_to(cr, width, height/2); + arrow_right(cr, 100.0); + cairo_fill(cr); + cairo_move_to(cr, width/2, height); + arrow_down(cr, 100.0); + cairo_fill(cr); + cairo_move_to(cr, width/2, 0.0); + arrow_up(cr, 100.0); + cairo_fill(cr); + + /* Arrows showing edges of slide */ + cairo_translate(cr, xoff, yoff); + cairo_set_source_rgb(cr, 0.5, 0.0, 0.0); + cairo_move_to(cr, 0.0, 100+h/2); + arrow_left(cr, 80.0); + cairo_fill(cr); + cairo_move_to(cr, tc->slide_width, 100+h/2); + arrow_right(cr, 80.0); + cairo_fill(cr); + cairo_move_to(cr, 100+tc->slide_width/2, h); + arrow_down(cr, 80.0); + cairo_fill(cr); + cairo_move_to(cr, 100+tc->slide_width/2, 0.0); + arrow_up(cr, 80.0); + cairo_fill(cr); + + /* Stuff in the middle */ + yp = (tc->slide_height-400)/2.0; + cairo_save(cr); + cairo_translate(cr, 0.0, yp); + + snprintf(tmp, 1024, _("Colloquium %s test card\n" + "Screen resolution %.0f × %.0f\n" + "Slide resolution %i × %i"), + PACKAGE_VERSION, width, height, + tc->slide_width, h); + + pl = pango_cairo_create_layout(cr); + desc = pango_font_description_from_string("Sans 24"); + pango_layout_set_font_description(pl, desc); + pango_layout_set_text(pl, tmp, -1); + + pango_layout_get_size(pl, &plw, &plh); + plw = pango_units_to_double(plw); + plh = pango_units_to_double(plh); + cairo_move_to(cr, (tc->slide_width-plw)/2, 0.0); + cairo_set_source_rgb(cr, 0.0, 0.0, 0.0); + pango_cairo_show_layout(cr, pl); + + /* Colour boxes */ + xp = (tc->slide_width-450)/2.0; + colour_box(cr, xp+0, 200, 1.0, 0.0, 0.0, _("Red")); + colour_box(cr, xp+80, 200, 0.0, 1.0, 0.0, _("Green")); + colour_box(cr, xp+160, 200, 0.0, 0.0, 1.0, _("Blue")); + colour_box(cr, xp+240, 200, 1.0, 1.0, 0.0, _("Yellow")); + colour_box(cr, xp+320, 200, 1.0, 0.0, 1.0, _("Pink")); + colour_box(cr, xp+400, 200, 0.0, 1.0, 1.0, _("Cyan")); + + /* Shades of grey */ + double i; + for ( i=0; i<=1.0; i+=0.2 ) { + char label[32]; + snprintf(label, 31, "%.0f%%", i*100.0); + colour_box(cr, xp+(i*5*80), 300, i, i, i, label); + } + cairo_restore(cr); + + return FALSE; +} + + +static void size_sig(GtkWidget *widget, GdkRectangle *rect, struct testcard *ss) +{ + int w; + + w = rect->height * ss->p->slide_width/ss->p->slide_height; + if ( w > rect->width ) w = rect->width; + ss->slide_width = w; + ss->slide_height = rect->height; +} + + + +static gboolean tc_key_press_sig(GtkWidget *da, GdkEventKey *event, + struct testcard *tc) +{ + if ( !event->is_modifier ) gtk_widget_destroy(tc->window); + return FALSE; +} + + +void show_testcard(struct presentation *p) +{ + GdkDisplay *display; + int n_monitors; + struct testcard *tc; + + tc = calloc(1, sizeof(struct testcard)); + if ( tc == NULL ) return; + + tc->p = p; + + tc->window = gtk_window_new(GTK_WINDOW_TOPLEVEL); + + tc->drawingarea = gtk_drawing_area_new(); + gtk_container_add(GTK_CONTAINER(tc->window), tc->drawingarea); + + gtk_widget_set_can_focus(GTK_WIDGET(tc->drawingarea), TRUE); + gtk_widget_add_events(GTK_WIDGET(tc->drawingarea), GDK_KEY_PRESS_MASK); + + g_signal_connect(G_OBJECT(tc->drawingarea), "key-press-event", + G_CALLBACK(tc_key_press_sig), tc); + g_signal_connect(G_OBJECT(tc->window), "destroy", + G_CALLBACK(tc_destroy_sig), tc); + g_signal_connect(G_OBJECT(tc->window), "size-allocate", + G_CALLBACK(size_sig), tc); + g_signal_connect(G_OBJECT(tc->drawingarea), "draw", + G_CALLBACK(tc_draw_sig), tc); + + gtk_widget_grab_focus(GTK_WIDGET(tc->drawingarea)); + + display = gdk_display_get_default(); + n_monitors = gdk_display_get_n_monitors(display); + + GdkMonitor *mon_ss; + if ( n_monitors == 1 ) { + mon_ss = gdk_display_get_primary_monitor(display); + printf(_("Single monitor mode\n")); + } else { + mon_ss = gdk_display_get_monitor(display, 1); + printf(_("Dual monitor mode\n")); + } + + /* Workaround because gtk_window_fullscreen_on_monitor doesn't work */ + GdkRectangle rect; + gdk_monitor_get_geometry(mon_ss, &rect); + gtk_window_move(GTK_WINDOW(tc->window), rect.x, rect.y); + gtk_window_fullscreen(GTK_WINDOW(tc->window)); + + gtk_widget_show_all(GTK_WIDGET(tc->window)); + +} + diff --git a/src-old/testcard.h b/src-old/testcard.h new file mode 100644 index 0000000..bb1ce2b --- /dev/null +++ b/src-old/testcard.h @@ -0,0 +1,32 @@ +/* + * testcard.h + * + * Copyright © 2013-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 TESTCARD_H +#define TESTCARD_H + +#ifdef HAVE_CONFIG_H +#include +#endif + +extern void show_testcard(struct presentation *p); + +#endif /* TESTCARD_H */ diff --git a/src-old/utils.c b/src-old/utils.c new file mode 100644 index 0000000..b41f344 --- /dev/null +++ b/src-old/utils.c @@ -0,0 +1,137 @@ +/* + * utils.c + * + * Copyright © 2013-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 "utils.h" + +void chomp(char *s) +{ + size_t i; + + if ( !s ) return; + + for ( i=0; i + * + * 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 UTILS_H +#define UTILS_H + +#ifdef HAVE_CONFIG_H +#include +#endif + +typedef enum +{ + UNITS_SLIDE, + UNITS_FRAC +} LengthUnits; + +extern void chomp(char *s); +extern int safe_strcmp(const char *a, const char *b); +extern int parse_double(const char *a, float v[2]); +extern int parse_tuple(const char *a, float v[4]); +extern int parse_dims(const char *opt, double *wp, double *hp, + LengthUnits *wup, LengthUnits *hup, + double *xp, double *yp); + +#include +#define _(x) gettext(x) + +#endif /* UTILS_H */ diff --git a/src/colloquium.c b/src/colloquium.c deleted file mode 100644 index 02ffa0a..0000000 --- a/src/colloquium.c +++ /dev/null @@ -1,476 +0,0 @@ -/* - * colloquium.c - * - * Copyright © 2013-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 -#include -#include - -#include "colloquium.h" -#include "presentation.h" -#include "narrative_window.h" -#include "utils.h" - - -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 ) { - struct presentation *p; - p = new_presentation(app->imagestore); - 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-2018 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; iimagestore); - if ( load_presentation(p, files[i]) == 0 ) { - 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 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] []\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 deleted file mode 100644 index 89f600f..0000000 --- a/src/colloquium.h +++ /dev/null @@ -1,45 +0,0 @@ -/* - * 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/debugger.c b/src/debugger.c deleted file mode 100644 index f15478a..0000000 --- a/src/debugger.c +++ /dev/null @@ -1,229 +0,0 @@ -/* - * print.c - * - * Copyright © 2017-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 "presentation.h" -#include "narrative_window.h" -#include "render.h" -#include "frame.h" -#include "utils.h" - - -#define MAX_DEBUG_RUNS (1024) - -struct debugwindow -{ - GtkWidget *window; - GtkWidget *drawingarea; - struct frame *fr; - guint timeout; -}; - - -static void plot_hr(cairo_t *cr, double *ypos, double width) -{ - cairo_move_to(cr, 10.0, *ypos+5.5); - cairo_line_to(cr, width-20.0, *ypos+5.5); - cairo_set_source_rgba(cr, 0.0, 0.0, 0.0, 1.0); - cairo_set_line_width(cr, 1.0); - cairo_stroke(cr); - *ypos += 10.0; -} - - -static void plot_text(cairo_t *cr, double *ypos, PangoFontDescription *fontdesc, - const char *tmp) -{ - PangoLayout *layout; - PangoRectangle ext; - - cairo_move_to(cr, 10.0, *ypos); - - layout = pango_cairo_create_layout(cr); - pango_layout_set_text(layout, tmp, -1); - pango_layout_set_font_description(layout, fontdesc); - - pango_layout_get_extents(layout, NULL, &ext); - *ypos += ext.height/PANGO_SCALE; - - cairo_set_source_rgba(cr, 0.0, 0.0, 0.0, 1.0); - pango_cairo_show_layout(cr, layout); - - g_object_unref(layout); -} - - -static const char *str_type(enum para_type t) -{ - switch ( t ) { - - /* Text paragraph */ - case PARA_TYPE_TEXT : return "text"; - - /* Callback paragraph */ - case PARA_TYPE_CALLBACK : return "callback"; - - /* Unknown paragraph type */ - default : return "unknown"; - } -} - -static void debug_text_para(Paragraph *para, cairo_t *cr, double *ypos, - PangoFontDescription *fontdesc) -{ - int i, nrun; - char tmp[256]; - - nrun = para_debug_num_runs(para); - /* How many text runs */ - snprintf(tmp, 255, " %i runs", nrun); - plot_text(cr, ypos, fontdesc, tmp); - - for ( i=0; ifr == NULL ) return FALSE; - - fontdesc = pango_font_description_new(); - pango_font_description_set_family_static(fontdesc, "Serif"); - pango_font_description_set_style(fontdesc, PANGO_STYLE_ITALIC); - pango_font_description_set_absolute_size(fontdesc, 15*PANGO_SCALE); - - snprintf(tmp, 255, "Frame %p has %i paragraphs", dbgw->fr, dbgw->fr->n_paras); - plot_text(cr, &ypos, fontdesc, tmp); - - for ( i=0; ifr->n_paras; i++ ) { - - enum para_type t = para_type(dbgw->fr->paras[i]); - SCBlock *scblock = para_scblock(dbgw->fr->paras[i]); - - plot_hr(cr, &ypos, width); - snprintf(tmp, 255, "Paragraph %i: type %s", i, str_type(t)); - plot_text(cr, &ypos, fontdesc, tmp); - - snprintf(tmp, 255, "SCBlock %p", scblock); - plot_text(cr, &ypos, fontdesc, tmp); - - if ( t == PARA_TYPE_TEXT ) { - debug_text_para(dbgw->fr->paras[i], cr, &ypos, fontdesc); - } - - plot_text(cr, &ypos, fontdesc, ""); - - } - - pango_font_description_free(fontdesc); - - return FALSE; -} - - -static gboolean queue_redraw(void *vp) -{ - struct debugwindow *dbgw = vp; - gint w, h; - w = gtk_widget_get_allocated_width(GTK_WIDGET(dbgw->drawingarea)); - h = gtk_widget_get_allocated_height(GTK_WIDGET(dbgw->drawingarea)); - gtk_widget_queue_draw_area(GTK_WIDGET(dbgw->drawingarea), 0, 0, w, h); - return TRUE; -} - - -static gboolean close_sig(GtkWidget *widget, GdkEvent *event, struct debugwindow *dbgw) -{ - g_source_remove(dbgw->timeout); - free(dbgw); - return FALSE; -} - - -void open_debugger(struct frame *fr) -{ - struct debugwindow *dbgw; - GtkWidget *scroll; - - if ( fr == NULL ) return; - - dbgw = calloc(1, sizeof(struct debugwindow)); - if ( dbgw == NULL ) return; - - dbgw->fr = fr; - - dbgw->window = gtk_window_new(GTK_WINDOW_TOPLEVEL); - gtk_window_set_role(GTK_WINDOW(dbgw->window), "debugger"); - gtk_window_set_title(GTK_WINDOW(dbgw->window), "Colloquium debugger"); - - scroll = gtk_scrolled_window_new(NULL, NULL); - gtk_container_add(GTK_CONTAINER(dbgw->window), scroll); - - dbgw->drawingarea = gtk_drawing_area_new(); - gtk_container_add(GTK_CONTAINER(scroll), dbgw->drawingarea); - gtk_widget_set_size_request(dbgw->drawingarea, 100, 8000); - - g_signal_connect(G_OBJECT(dbgw->drawingarea), "draw", - G_CALLBACK(dbg_draw_sig), dbgw); - - g_signal_connect(G_OBJECT(dbgw->window), "delete-event", - G_CALLBACK(close_sig), dbgw); - - dbgw->timeout = g_timeout_add(1000, queue_redraw, dbgw); - - gtk_widget_show_all(dbgw->window); -} diff --git a/src/debugger.h b/src/debugger.h deleted file mode 100644 index d6191bf..0000000 --- a/src/debugger.h +++ /dev/null @@ -1,32 +0,0 @@ -/* - * debugger.h - * - * Copyright © 2017-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 DEBUGGER_H -#define DEBUGGER_H - -#ifdef HAVE_CONFIG_H -#include -#endif - -extern void open_debugger(struct frame *fr); - -#endif /* DEBUGGER_H */ diff --git a/src/frame.c b/src/frame.c deleted file mode 100644 index 08b8fd9..0000000 --- a/src/frame.c +++ /dev/null @@ -1,1820 +0,0 @@ -/* - * frame.c - * - * Copyright © 2013-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 - -#include "sc_parse.h" -#include "frame.h" -#include "imagestore.h" -#include "utils.h" - -struct text_run -{ - SCBlock *scblock; - char *real_text; /* Usually NULL */ - PangoFontDescription *fontdesc; - double col[4]; -}; - -struct _paragraph -{ - enum para_type type; - double height; - float space[4]; - SCBlock *newline_at_end; - int empty; - - /* For PARA_TYPE_TEXT */ - int n_runs; - struct text_run *runs; - PangoLayout *layout; - PangoAlignment alignment; - - /* For anything other than PARA_TYPE_TEXT - * (for text paragraphs, these things are in the runs) */ - SCBlock *scblock; - - /* For PARA_TYPE_IMAGE */ - char *filename; - double image_w; - double image_h; - int image_real_w; - int image_real_h; - - /* For PARA_TYPE_CALLBACK */ - double cb_w; - double cb_h; - SCCallbackDrawFunc draw_func; - SCCallbackClickFunc click_func; - void *bvp; - void *vp; -}; - - -/* Returns the height of the paragraph including all spacing, padding etc */ -double paragraph_height(Paragraph *para) -{ - return para->height + para->space[2] + para->space[3]; -} - - -static int alloc_ro(struct frame *fr) -{ - struct frame **new_ro; - - new_ro = realloc(fr->children, - fr->max_children*sizeof(struct frame *)); - if ( new_ro == NULL ) return 1; - - fr->children = new_ro; - - return 0; -} - - -struct frame *frame_new() -{ - struct frame *n; - - n = calloc(1, sizeof(struct frame)); - if ( n == NULL ) return NULL; - - n->children = NULL; - n->max_children = 32; - if ( alloc_ro(n) ) { - fprintf(stderr, "Couldn't allocate children\n"); - free(n); - return NULL; - } - n->num_children = 0; - - n->scblocks = NULL; - n->n_paras = 0; - n->paras = NULL; - - return n; -} - - -static void free_paragraph(Paragraph *para) -{ - int i; - - for ( i=0; in_runs; i++ ) { - pango_font_description_free(para->runs[i].fontdesc); - free(para->runs[i].real_text); /* free(NULL) is OK */ - } - free(para->runs); - if ( para->layout != NULL ) g_object_unref(para->layout); - free(para); -} - - -void frame_free(struct frame *fr) -{ - int i; - - if ( fr == NULL ) return; - - /* Free paragraphs */ - if ( fr->paras != NULL ) { - for ( i=0; in_paras; i++ ) { - free_paragraph(fr->paras[i]); - } - free(fr->paras); - } - - /* Free all children */ - for ( i=0; inum_children; i++ ) { - frame_free(fr->children[i]); - } - free(fr->children); - - free(fr); -} - - -struct frame *add_subframe(struct frame *fr) -{ - struct frame *n; - - n = frame_new(); - if ( n == NULL ) return NULL; - - if ( fr->num_children == fr->max_children ) { - fr->max_children += 32; - if ( alloc_ro(fr) ) return NULL; - } - - fr->children[fr->num_children++] = n; - - return n; -} - - -void show_frame_hierarchy(struct frame *fr, const char *t) -{ - int i; - char tn[1024]; - - strcpy(tn, t); - strcat(tn, " "); - - printf("%s%p (%.2f x %.2f)\n", t, fr, fr->w, fr->h); - - for ( i=0; inum_children; i++ ) { - show_frame_hierarchy(fr->children[i], tn); - } - -} - - -struct frame *find_frame_with_scblocks(struct frame *fr, SCBlock *scblocks) -{ - int i; - - if ( fr->scblocks == scblocks ) return fr; - - for ( i=0; inum_children; i++ ) { - struct frame *tt; - tt = find_frame_with_scblocks(fr->children[i], scblocks); - if ( tt != NULL ) return tt; - } - - return NULL; -} - - -static const char *text_for_run(const struct text_run *run) -{ - if ( run == NULL ) { - fprintf(stderr, "NULL run passed to text_for_run\n"); - return 0; - } - - if ( run->scblock == NULL ) { - fprintf(stderr, "NULL scblock in text_for_run\n"); - return 0; - } - - if ( run->real_text != NULL ) { - return run->real_text; - } - - return sc_block_contents(run->scblock); -} - - -static size_t run_text_len(const struct text_run *run) -{ - if ( run == NULL ) { - fprintf(stderr, "NULL run passed to run_text_len\n"); - return 0; - } - - if ( run->scblock == NULL ) { - fprintf(stderr, "NULL scblock in run_text_len\n"); - return 0; - } - - if ( run->real_text != NULL ) { - return strlen(run->real_text); - } - - if ( sc_block_contents(run->scblock) == NULL ) { - fprintf(stderr, "NULL scblock contents in run_text_len\n"); - return 0; - } - - return strlen(sc_block_contents(run->scblock)); -} - - -void wrap_paragraph(Paragraph *para, PangoContext *pc, double w, - size_t sel_start, size_t sel_end) -{ - size_t total_len = 0; - int i; - char *text; - PangoAttrList *attrs; - PangoRectangle rect; - size_t pos = 0; - - w -= para->space[0] + para->space[1]; - - if ( para->type == PARA_TYPE_IMAGE ) { - if ( para->image_w < 0.0 ) { - para->image_w = w; - para->image_h = w*((float)para->image_real_h/para->image_real_w); - } - para->height = para->image_h; - return; - } - - if ( para->type != PARA_TYPE_TEXT ) return; - - for ( i=0; in_runs; i++ ) { - total_len += run_text_len(¶->runs[i]); - } - - /* Allocate the complete text */ - text = malloc(total_len+1); - if ( text == NULL ) { - fprintf(stderr, "Couldn't allocate combined text (%lli)\n", - (long long int)total_len); - return; - } - - /* Allocate the attributes */ - attrs = pango_attr_list_new(); - - /* Put all of the text together */ - text[0] = '\0'; - for ( i=0; in_runs; i++ ) { - - PangoAttribute *attr; - const char *run_text; - size_t run_len; - guint16 r, g, b; - - run_text = text_for_run(¶->runs[i]); - run_len = strlen(run_text); - - attr = pango_attr_font_desc_new(para->runs[i].fontdesc); - attr->start_index = pos; - attr->end_index = pos + run_len; - pango_attr_list_insert(attrs, attr); - - r = para->runs[i].col[0] * 65535; - g = para->runs[i].col[1] * 65535; - b = para->runs[i].col[2] * 65535; - attr = pango_attr_foreground_new(r, g, b); - attr->start_index = pos; - attr->end_index = pos + run_len; - pango_attr_list_insert(attrs, attr); - - pos += run_len; - strncat(text, run_text, run_len); - - } - - /* Add attributes for selected text */ - if ( sel_start > 0 || sel_end > 0 ) { - PangoAttribute *attr; - attr = pango_attr_background_new(42919, 58853, 65535); - attr->start_index = sel_start; - attr->end_index = sel_end; - pango_attr_list_insert(attrs, attr); - } - - if ( para->layout == NULL ) { - para->layout = pango_layout_new(pc); - } - pango_layout_set_width(para->layout, pango_units_from_double(w)); - pango_layout_set_text(para->layout, text, total_len); - pango_layout_set_alignment(para->layout, para->alignment); - pango_layout_set_attributes(para->layout, attrs); - free(text); - pango_attr_list_unref(attrs); - - pango_layout_get_extents(para->layout, NULL, &rect); - para->height = pango_units_to_double(rect.height); -} - -static SCBlock *get_newline_at_end(Paragraph *para) -{ - return para->newline_at_end; -} - - -SCBlock *para_debug_get_newline_at_end(Paragraph *para) -{ - return get_newline_at_end(para); -} - - -void set_newline_at_end(Paragraph *para, SCBlock *bl) -{ - para->newline_at_end = bl; -} - - -void add_run(Paragraph *para, SCBlock *scblock, - PangoFontDescription *fdesc, double col[4], const char *real_text) -{ - struct text_run *runs_new; - - runs_new = realloc(para->runs, - (para->n_runs+1)*sizeof(struct text_run)); - if ( runs_new == NULL ) { - fprintf(stderr, "Failed to add run.\n"); - return; - } - - para->runs = runs_new; - para->runs[para->n_runs].scblock = scblock; - if ( real_text != NULL ) { - para->runs[para->n_runs].real_text = strdup(real_text); - } else { - para->runs[para->n_runs].real_text = NULL; - } - para->runs[para->n_runs].fontdesc = pango_font_description_copy(fdesc); - para->runs[para->n_runs].col[0] = col[0]; - para->runs[para->n_runs].col[1] = col[1]; - para->runs[para->n_runs].col[2] = col[2]; - para->runs[para->n_runs].col[3] = col[3]; - para->empty = 0; - para->n_runs++; -} - - -Paragraph *create_paragraph(struct frame *fr, SCBlock *bl) -{ - Paragraph **paras_new; - Paragraph *pnew; - - paras_new = realloc(fr->paras, (fr->n_paras+1)*sizeof(Paragraph *)); - if ( paras_new == NULL ) return NULL; - - pnew = calloc(1, sizeof(struct _paragraph)); - if ( pnew == NULL ) return NULL; - - fr->paras = paras_new; - fr->paras[fr->n_paras++] = pnew; - - /* For now, assume the paragraph is going to be for text. - * However, this can easily be changed */ - pnew->type = PARA_TYPE_TEXT; - pnew->scblock = bl; - pnew->n_runs = 0; - pnew->runs = NULL; - pnew->layout = NULL; - pnew->height = 0.0; - pnew->alignment = PANGO_ALIGN_LEFT; - pnew->empty = 1; - - return pnew; -} - - -/* Create a new paragraph in 'fr' just after paragraph 'pos' */ -Paragraph *insert_paragraph(struct frame *fr, int pos) -{ - Paragraph **paras_new; - Paragraph *pnew; - int i; - - if ( pos >= fr->n_paras ) { - fprintf(stderr, "insert_paragraph(): pos too high!\n"); - return NULL; - } - - paras_new = realloc(fr->paras, (fr->n_paras+1)*sizeof(Paragraph *)); - if ( paras_new == NULL ) return NULL; - - pnew = calloc(1, sizeof(struct _paragraph)); - if ( pnew == NULL ) return NULL; - - fr->paras = paras_new; - fr->n_paras++; - - for ( i=fr->n_paras-1; i>pos; i-- ) { - fr->paras[i] = fr->paras[i-1]; - } - fr->paras[pos+1] = pnew; - - return pnew; -} - - -Paragraph *add_callback_para(struct frame *fr, SCBlock *bl, double w, double h, - SCCallbackDrawFunc draw_func, - SCCallbackClickFunc click_func, void *bvp, - void *vp) -{ - Paragraph *pnew; - - if ( (fr->n_paras > 0) && (fr->paras[fr->n_paras-1]->empty) ) { - pnew = fr->paras[fr->n_paras-1]; - } else { - pnew = create_paragraph(fr, bl); - if ( pnew == NULL ) { - fprintf(stderr, "Failed to add callback paragraph\n"); - return NULL; - } - } - - pnew->type = PARA_TYPE_CALLBACK; - pnew->scblock = bl; - pnew->cb_w = w; - pnew->cb_h = h; - pnew->draw_func = draw_func; - pnew->click_func = click_func; - pnew->bvp = bvp; - pnew->vp = vp; - pnew->height = h; - pnew->empty = 0; - - return pnew; -} - - -void add_image_para(struct frame *fr, SCBlock *scblock, - const char *filename, - ImageStore *is, double w, double h, int editable) -{ - Paragraph *pnew; - int wi, hi; - - if ( is == NULL ) { - fprintf(stderr, "Adding image without ImageStore!\n"); - return; - } - - if ( (fr->n_paras > 0) && (fr->paras[fr->n_paras-1]->empty) ) { - pnew = fr->paras[fr->n_paras-1]; - } else { - pnew = create_paragraph(fr, scblock); - if ( pnew == NULL ) { - fprintf(stderr, "Failed to add image paragraph\n"); - return; - } - } - - if ( imagestore_get_size(is, filename, &wi, &hi) ) { - fprintf(stderr, _("Couldn't determine size of image '%s'\n"), - filename); - wi = 100; - hi = 100; - } - - pnew->type = PARA_TYPE_IMAGE; - pnew->scblock = scblock; - pnew->filename = strdup(filename); - pnew->image_w = w; - pnew->image_h = h; - pnew->image_real_w = wi; - pnew->image_real_h = hi; - pnew->height = h; - pnew->space[0] = 0.0; - pnew->space[1] = 0.0; - pnew->space[2] = 0.0; - pnew->space[3] = 0.0; - pnew->empty = 0; -} - - -double total_height(struct frame *fr) -{ - int i; - double t = 0.0; - for ( i=0; in_paras; i++ ) { - t += paragraph_height(fr->paras[i]); - } - return t; -} - - -Paragraph *last_para(struct frame *fr) -{ - if ( fr == NULL ) return NULL; - if ( fr->paras == NULL ) return NULL; - return fr->paras[fr->n_paras-1]; -} - - -static void render_from_surf(cairo_surface_t *surf, cairo_t *cr, - double w, double h, int border) -{ - double x, y; - int sw, sh; - - x = 0.0; y = 0.0; - cairo_user_to_device(cr, &x, &y); - x = rint(x); y = rint(y); - cairo_device_to_user(cr, &x, &y); - - sw = cairo_image_surface_get_width(surf); - sh = cairo_image_surface_get_height(surf); - - cairo_save(cr); - cairo_scale(cr, w/sw, h/sh); - cairo_new_path(cr); - cairo_rectangle(cr, x, y, sw, sh); - cairo_set_source_surface(cr, surf, 0.0, 0.0); - cairo_pattern_t *patt = cairo_get_source(cr); - cairo_pattern_set_extend(patt, CAIRO_EXTEND_PAD); - cairo_pattern_set_filter(patt, CAIRO_FILTER_BEST); - cairo_fill(cr); - cairo_restore(cr); - - if ( border ) { - cairo_new_path(cr); - cairo_rectangle(cr, x+0.5, y+0.5, w, h); - cairo_set_source_rgb(cr, 0.0, 0.0, 0.0); - cairo_set_line_width(cr, 1.0); - cairo_stroke(cr); - } -} - - -void render_paragraph(cairo_t *cr, Paragraph *para, ImageStore *is) -{ - cairo_surface_t *surf; - cairo_surface_type_t type; - double w, h; - - cairo_translate(cr, para->space[0], para->space[2]); - - type = cairo_surface_get_type(cairo_get_target(cr)); - - switch ( para->type ) { - - case PARA_TYPE_TEXT : - cairo_set_source_rgba(cr, 0.0, 0.0, 0.0, 1.0); - pango_cairo_update_layout(cr, para->layout); - pango_cairo_show_layout(cr, para->layout); - cairo_fill(cr); - break; - - case PARA_TYPE_IMAGE : - w = para->image_w; - h = para->image_h; - cairo_user_to_device_distance(cr, &w, &h); - surf = lookup_image(is, para->filename, w); - if ( surf != NULL ) { - render_from_surf(surf, cr, para->image_w, para->image_h, 0); - } else { - printf("surf = NULL!\n"); - } - break; - - case PARA_TYPE_CALLBACK : - w = para->cb_w; - h = para->cb_h; - cairo_user_to_device_distance(cr, &w, &h); - if ( type == CAIRO_SURFACE_TYPE_PDF ) { - w *= 6; h *= 6; - } - surf = para->draw_func(w, h, - para->bvp, para->vp); - render_from_surf(surf, cr, para->cb_w, para->cb_h, 1); - cairo_surface_destroy(surf); /* FIXME: Cache like crazy */ - break; - - } -} - - -static size_t end_offset_of_para(struct frame *fr, int pn) -{ - int i; - size_t total = 0; - for ( i=0; iparas[pn]->n_runs; i++ ) { - total += run_text_len(&fr->paras[pn]->runs[i]); - } - return total; -} - - -/* Local x,y in paragraph -> text offset */ -static size_t text_para_pos(Paragraph *para, double x, double y, int *ptrail) -{ - int idx; - pango_layout_xy_to_index(para->layout, pango_units_from_double(x), - pango_units_from_double(y), &idx, ptrail); - return idx; -} - - -void show_edit_pos(struct edit_pos a) -{ - printf("para %i, pos %li, trail %i\n", a.para, (long int)a.pos, a.trail); -} - - -int positions_equal(struct edit_pos a, struct edit_pos b) -{ - if ( a.para != b.para ) return 0; - if ( a.pos != b.pos ) return 0; - if ( a.trail != b.trail ) return 0; - return 1; -} - - -void sort_positions(struct edit_pos *a, struct edit_pos *b) -{ - if ( a->para > b->para ) { - size_t tpos; - int tpara, ttrail; - tpara = b->para; tpos = b->pos; ttrail = b->trail; - b->para = a->para; b->pos = a->pos; b->trail = a->trail; - a->para = tpara; a->pos = tpos; a->trail = ttrail; - } - - if ( (a->para == b->para) && (a->pos > b->pos) ) - { - size_t tpos = b->pos; - int ttrail = b->trail; - b->pos = a->pos; b->trail = a->trail; - a->pos = tpos; a->trail = ttrail; - } -} - - -static PangoFontDescription *last_font_desc_and_col(struct frame *fr, int pn, double *col) -{ - int i; - - for ( i=pn-1; i>=0; i-- ) { - if ( fr->paras[i]->type != PARA_TYPE_TEXT ) continue; - if ( fr->paras[i]->n_runs == 0 ) continue; - col[0] = fr->paras[i]->runs[fr->paras[i]->n_runs-1].col[0]; - col[1] = fr->paras[i]->runs[fr->paras[i]->n_runs-1].col[1]; - col[2] = fr->paras[i]->runs[fr->paras[i]->n_runs-1].col[2]; - col[3] = fr->paras[i]->runs[fr->paras[i]->n_runs-1].col[3]; - return fr->paras[i]->runs[fr->paras[i]->n_runs-1].fontdesc; - } - - /* No previous text at all, so use the default text style - * (which is valid for new text in the frame, so this is OK) */ - col[0] = fr->col[0]; - col[1] = fr->col[1]; - col[2] = fr->col[2]; - col[3] = fr->col[3]; - return fr->fontdesc; -} - - -void ensure_run(struct frame *fr, struct edit_pos cpos) -{ - SCBlock *bl; - Paragraph *para; - PangoFontDescription *fontdesc; - double col[4]; - - para = fr->paras[cpos.para]; - - if ( para->n_runs > 0 ) return; - - if ( para->type != PARA_TYPE_TEXT ) return; - - if ( para->scblock != NULL ) { - - bl = sc_block_prepend(para->scblock, fr->scblocks); - if ( bl == NULL ) { - fprintf(stderr, "Couldn't prepend block\n"); - return; - } - sc_block_set_contents(bl, strdup("")); - - } else { - - /* If the paragraph's SCBlock is NULL, it means this paragraph - * is right at the end of the document. The last thing in the - * document is something like \newpara. */ - bl = sc_block_append_inside(fr->scblocks, NULL, NULL, strdup("")); - - } - - para->scblock = bl; - - fontdesc = last_font_desc_and_col(fr, cpos.para, col); - add_run(para, bl, fontdesc, col, NULL); - wrap_paragraph(para, NULL, fr->w - fr->pad_l - fr->pad_r, 0, 0); -} - - -int find_cursor(struct frame *fr, double x, double y, struct edit_pos *pos) -{ - double pad; - int i; - - if ( fr == NULL ) { - fprintf(stderr, "Cursor frame is NULL.\n"); - return 1; - } - - pad = fr->pad_t; - - for ( i=0; in_paras; i++ ) { - double npos = pad + paragraph_height(fr->paras[i]); - if ( npos > y ) { - pos->para = i; - if ( fr->paras[i]->type == PARA_TYPE_TEXT ) { - pos->pos = text_para_pos(fr->paras[i], - x - fr->pad_l - fr->paras[i]->space[0], - y - pad - fr->paras[i]->space[2], - &pos->trail); - } else { - pos->pos = 0; - } - return 0; - } - pad = npos; - } - - if ( fr->n_paras == 0 ) { - printf("No paragraphs in frame.\n"); - return 1; - } - - /* Pretend it's in the last paragraph */ - pad -= fr->paras[fr->n_paras-1]->height; - pos->para = fr->n_paras - 1; - pos->pos = text_para_pos(fr->paras[fr->n_paras-1], - x - fr->pad_l, y - pad, &pos->trail); - return 0; -} - - -int get_para_highlight(struct frame *fr, int cursor_para, - double *cx, double *cy, double *cw, double *ch) -{ - Paragraph *para; - int i; - double py = 0.0; - - if ( fr == NULL ) { - fprintf(stderr, "Cursor frame is NULL.\n"); - return 1; - } - - if ( cursor_para >= fr->n_paras ) { - fprintf(stderr, "Highlight paragraph number is too high!\n"); - return 1; - } - - para = fr->paras[cursor_para]; - for ( i=0; iparas[i]); - } - - *cx = fr->pad_l; - *cy = fr->pad_t + py; - *cw = fr->w - fr->pad_l - fr->pad_r; - *ch = paragraph_height(para); - return 0; -} - - -int get_cursor_pos(struct frame *fr, int cursor_para, int cursor_pos, - double *cx, double *cy, double *ch) -{ - Paragraph *para; - PangoRectangle rect; - int i; - double py = 0.0; - - if ( fr == NULL ) { - fprintf(stderr, "Cursor frame is NULL.\n"); - return 1; - } - - if ( cursor_para >= fr->n_paras ) { - fprintf(stderr, "Cursor paragraph number is too high!\n"); - return 1; - } - - para = fr->paras[cursor_para]; - for ( i=0; iparas[i]); - } - - if ( para->type != PARA_TYPE_TEXT ) { - return 1; - } - - pango_layout_get_cursor_pos(para->layout, cursor_pos, &rect, NULL); - - *cx = pango_units_to_double(rect.x) + fr->pad_l + para->space[0]; - *cy = pango_units_to_double(rect.y) + fr->pad_t + py + para->space[2]; - *ch = pango_units_to_double(rect.height); - return 0; -} - - -//void cursor_moveh(struct frame *fr, int *cpara, size_t *cpos, int *ctrail, -void cursor_moveh(struct frame *fr, struct edit_pos *cp, signed int dir) -{ - Paragraph *para = fr->paras[cp->para]; - int np = cp->pos; - - pango_layout_move_cursor_visually(para->layout, 1, cp->pos, cp->trail, - dir, &np, &cp->trail); - if ( np == -1 ) { - if ( cp->para > 0 ) { - size_t end_offs; - cp->para--; - end_offs = end_offset_of_para(fr, cp->para); - if ( end_offs > 0 ) { - cp->pos = end_offs - 1; - cp->trail = 1; - } else { - /* Jumping into an empty paragraph */ - cp->pos = 0; - cp->trail = 0; - } - return; - } else { - /* Can't move any further */ - return; - } - } - - if ( np == G_MAXINT ) { - if ( cp->para < fr->n_paras-1 ) { - cp->para++; - cp->pos = 0; - cp->trail = 0; - return; - } else { - /* Can't move any further */ - return; - } - } - - cp->pos = np; -} - - -void check_callback_click(struct frame *fr, int para) -{ - Paragraph *p = fr->paras[para]; - if ( p->type == PARA_TYPE_CALLBACK ) { - p->click_func(0.0, 0.0, p->bvp, p->vp); - } -} - - -static int get_paragraph_offset(Paragraph *para, int nrun) -{ - int i; - size_t t = 0; - - for ( i=0; iruns[i]; - t += run_text_len(run); - } - return t; -} - - -static int which_run(Paragraph *para, size_t offs) -{ - int i; - size_t t = 0; - - for ( i=0; in_runs; i++ ) { - struct text_run *run = ¶->runs[i]; - t += run_text_len(run); - if ( t > offs ) return i; - } - - /* Maybe offs points exactly to the end of the last run? */ - if ( t == offs ) return para->n_runs-1; - - return para->n_runs; -} - - -size_t pos_trail_to_offset(Paragraph *para, size_t offs, int trail) -{ - glong char_offs; - size_t run_offs; - const char *run_text; - struct text_run *run; - int nrun; - char *ptr; - size_t para_offset_of_run; - - nrun = which_run(para, offs); - - if ( nrun == para->n_runs ) { - fprintf(stderr, "pos_trail_to_offset: Offset too high\n"); - return 0; - } - - if ( para->n_runs == 0 ) { - return 0; - } - - run = ¶->runs[nrun]; - - if ( para->type != PARA_TYPE_TEXT ) return 0; - - if ( run == NULL ) { - fprintf(stderr, "pos_trail_to_offset: No run\n"); - return 0; - } - - if ( run->scblock == NULL ) { - fprintf(stderr, "pos_trail_to_offset: SCBlock = NULL?\n"); - return 0; - } - - /* Get the text for the run */ - run_text = text_for_run(run); - if ( run_text == NULL ) { - fprintf(stderr, "pos_trail_to_offset: No contents " - "(%p name=%s, options=%s)\n", - run->scblock, sc_block_name(run->scblock), - sc_block_options(run->scblock)); - return 0; - } - - - /* Turn the paragraph offset into a run offset */ - para_offset_of_run = get_paragraph_offset(para, nrun); - run_offs = offs - para_offset_of_run; - - char_offs = g_utf8_pointer_to_offset(run_text, run_text+run_offs); - char_offs += trail; - - if ( char_offs > g_utf8_strlen(run_text, -1) ) { - printf("Offset outside string! '%s'\n" - "char_offs %li offs %li len %li\n", - run_text, (long int)char_offs, (long int)offs, - (long int)g_utf8_strlen(run_text, -1)); - } - - ptr = g_utf8_offset_to_pointer(run_text, char_offs); - return ptr - run_text + para_offset_of_run; -} - - -int position_editable(struct frame *fr, struct edit_pos cp) -{ - Paragraph *para; - int run; - size_t paraoffs; - - if ( fr == NULL ) { - fprintf(stderr, "Frame is NULL.\n"); - return 0; - } - - if ( cp.para >= fr->n_paras ) { - fprintf(stderr, "Paragraph number is too high!\n"); - return 0; - } - - para = fr->paras[cp.para]; - - if ( para->type != PARA_TYPE_TEXT ) { - fprintf(stderr, "Paragraph is not text.\n"); - return 0; - } - - paraoffs = pos_trail_to_offset(para, cp.pos, cp.trail); - run = which_run(para, paraoffs); - if ( run == para->n_runs ) { - fprintf(stderr, "Couldn't find run!\n"); - return 0; - } - - if ( para->runs[run].real_text != NULL ) return 0; - - return 1; -} - - -void insert_text_in_paragraph(Paragraph *para, size_t offs, const char *t) -{ - int nrun; - - /* Find which run we are in */ - nrun = which_run(para, offs); - if ( nrun == para->n_runs ) { - fprintf(stderr, "Couldn't find run to insert into.\n"); - return; - } - - if ( para->n_runs == 0 ) { - printf("No runs in paragraph?\n"); - } else { - struct text_run *run; - size_t run_offs; - run = ¶->runs[nrun]; - run_offs = offs - get_paragraph_offset(para, nrun); - sc_insert_text(run->scblock, run_offs, t); - } -} - - -static SCBlock *pos_to_scblock(struct frame *fr, struct edit_pos p, - enum para_type *type) -{ - int run; - size_t paraoffs; - Paragraph *para; - - para = fr->paras[p.para]; - if ( type != NULL ) { - *type = para->type; - } - - if ( para->type != PARA_TYPE_TEXT ) { - return para->scblock; - } - - paraoffs = pos_trail_to_offset(para, p.pos, p.trail); - - run = which_run(para, paraoffs); - assert(run < para->n_runs); - - return para->runs[run].scblock; -} - - -static size_t pos_to_offset(struct frame *fr, struct edit_pos p) -{ - int run; - size_t paraoffs; - Paragraph *para; - - para = fr->paras[p.para]; - if ( para->type != PARA_TYPE_TEXT ) { - return 0; - } - - /* Offset of this position into the paragraph */ - paraoffs = pos_trail_to_offset(para, p.pos, p.trail); - - run = which_run(para, paraoffs); - assert(run < para->n_runs); - - /* Offset of this position into the run - * (and therefore into the SCBlock) */ - return paraoffs - get_paragraph_offset(para, run); -} - - -static int pos_to_run_number(struct frame *fr, struct edit_pos p) -{ - int run; - size_t paraoffs; - Paragraph *para; - - para = fr->paras[p.para]; - if ( para->type != PARA_TYPE_TEXT ) { - return 0; - } - - paraoffs = pos_trail_to_offset(para, p.pos, p.trail); - - run = which_run(para, paraoffs); - assert(run < para->n_runs); - - return run; -} - - -static void delete_run(Paragraph *para, int nrun) -{ - printf("deleting run %i of %i from para %p\n", nrun, para->n_runs, para); - memmove(¶->runs[nrun], ¶->runs[nrun+1], - (para->n_runs-nrun-1)*sizeof(struct text_run)); - para->n_runs--; -} - - -static Paragraph *scan_runs_for_scblock(struct frame *fr, int pn1, int pn2, - SCBlock *bl, int *run) -{ - int i; - - for ( i=pn1; i<=pn2; i++ ) { - - int j; - - /* Non-text paragraph - just one thing to check */ - if ( fr->paras[i]->scblock == bl ) { - *run = 0; - return fr->paras[i]; - } - - /* Check all runs */ - for ( j=0; jparas[i]->n_runs; j++ ) { - if ( fr->paras[i]->runs[j].scblock == bl ) { - *run = j; - return fr->paras[i]; - } - } - } - return NULL; -} - - -static Paragraph *find_run_for_scblock_next(struct frame *fr, int pn1, int pn2, - SCBlock *bl, int *run) -{ - if ( sc_block_child(bl) != NULL ) { - Paragraph *para; - para = find_run_for_scblock_next(fr, pn1, pn2, - sc_block_child(bl), run); - if ( para != NULL ) return para; - } - - do { - Paragraph *para; - para = scan_runs_for_scblock(fr, pn1, pn2, bl, run); - if ( para != NULL ) return para; - bl = sc_block_next(bl); - } while ( bl != NULL ); - - return NULL; -} - - -/* Find the run which contains the text from "bl", - * taking into account that it might be a child block, for example: - * {some text} - * \italic <---- bl points here - * {more text} <---- but this block is referenced by the run - * {final text} - */ -static Paragraph *find_run_for_scblock(struct frame *fr, int pn1, int pn2, - SCBlock *bl, int *run) -{ - Paragraph *para; - - show_sc_block(bl, "searching "); - para = scan_runs_for_scblock(fr, pn1, pn2, bl, run); - if ( para != NULL ) return para; - - if ( sc_block_child(bl) != NULL ) { - para = find_run_for_scblock_next(fr, pn1, pn2, sc_block_child(bl), run); - if ( para != NULL ) return para; - } - - return NULL; -} - - -static int paragraph_number(struct frame *fr, Paragraph *p, int *err) -{ - int i; - for ( i=0; in_paras; i++ ) { - if ( fr->paras[i] == p ) return i; - } - fprintf(stderr, "Couldn't find paragraph %p\n", p); - *err = 1; - return 0; -} - - -static int find_para(struct frame *fr, Paragraph *para) -{ - int i; - - for ( i=0; in_paras; i++ ) { - if ( fr->paras[i] == para ) return i; - } - - return fr->n_paras; -} - - -static void delete_paragraph(struct frame *fr, Paragraph *para, int *pnp) -{ - int pn = find_para(fr, para); - if ( pn == fr->n_paras ) { - fprintf(stderr, "Couldn't find paragraph to delete (%p)\n", para); - return; - } - - printf("deleting paragraph %i (%p)\n", pn, para); - - memmove(&fr->paras[pn], &fr->paras[pn+1], - (fr->n_paras-pn-1)*sizeof(Paragraph *)); - fr->n_paras--; - - if ( (pnp != NULL) && (*pnp > pn) ) { - (*pnp)--; - } -} - - -static void delete_run_for_scblock(struct frame *fr, - Paragraph *p1, Paragraph *p2, SCBlock *bl, - int *pnp) -{ - int pn1, pn2; - int err = 0; - Paragraph *para; - int run; - - pn1 = paragraph_number(fr, p1, &err); - pn2 = paragraph_number(fr, p2, &err); - if ( err ) return; - - para = find_run_for_scblock(fr, pn1, pn2, bl, &run); - if ( para == NULL ) { - fprintf(stderr, "Couldn't find block %p between paragraphs %p and %p\n", - bl, p1, p2); - return; - } - - if ( (run==0) && (para->scblock == bl ) ) { - delete_paragraph(fr, para, pnp); - } else { - delete_run(para, run); - } -} - - -static signed int merge_paragraph_runs(Paragraph *p1, Paragraph *p2) -{ - struct text_run *runs_new; - int i, spos; - - /* All the runs from p2 get added to p1 */ - runs_new = realloc(p1->runs, - (p1->n_runs+p2->n_runs)*sizeof(struct text_run)); - if ( runs_new == NULL ) { - fprintf(stderr, "Failed to allocate merged runs.\n"); - return -1; - } - p1->runs = runs_new; - - spos = p1->n_runs; - - /* The end of the united paragraph should now be the end of the - * second one */ - set_newline_at_end(p1, get_newline_at_end(p2)); - - for ( i=0; in_runs; i++ ) { - p1->runs[p1->n_runs] = p2->runs[i]; - p1->n_runs++; - } - free(p2->runs); - free(p2); - - return spos; -} - - -static void merge_paragraphs_by_newpara(struct frame *fr, SCBlock *np) -{ - int i; - Paragraph *p1; - Paragraph *p2; - - for ( i=0; in_paras-1; i++ ) { - if ( fr->paras[i]->newline_at_end == np ) { - - int j; - signed int spos; - - p1 = fr->paras[i]; - p2 = fr->paras[i+1]; - - printf("-------------------------------\n"); - show_para(p1); - printf("---x--------x------------------\n"); - show_para(p2); - spos = merge_paragraph_runs(p1, p2); - if ( spos < 0 ) { - fprintf(stderr, "Failed to merge paragraphs\n"); - return; - } - printf("-------------------------------\n"); - show_para(p1); - - for ( j=i+1; jn_paras-1; j++ ) { - fr->paras[j] = fr->paras[j+1]; - } - fr->n_paras--; - - return; - - } - } - - fprintf(stderr, "Couldn't find paragraphs to merge by newpara\n"); -} - - -static int find_block_inside(SCBlock *needle, SCBlock *bl) -{ - if ( needle == bl ) return 1; - - if ( sc_block_child(bl) != NULL ) { - if ( find_block_inside(needle, sc_block_child(bl)) ) return 1; - } - - if ( sc_block_next(bl) != NULL ) { - if ( find_block_inside(needle, sc_block_next(bl)) ) return 1; - } - - return 0; -} - - -/* Return true if "top" either IS "child", or contains "child" somewhere - * underneath, even if via a macro expansion */ -static int block_is_under(SCBlock *needle, SCBlock *top) -{ - if ( needle == top ) return 1; - - if ( sc_block_child(top) != NULL ) { - if ( find_block_inside(needle, sc_block_child(top)) ) return 1; - } - - /* Do not look at top->next here */ - - return 0; -} - - -void delete_text_from_frame(struct frame *fr, struct edit_pos p1, struct edit_pos p2, - double wrapw) -{ - int i; - SCBlock *p1scblock, *p2scblock; - enum para_type type1, type2; - size_t p2offs; - SCBlock *scblock; - int wrap_end; - - sort_positions(&p1, &p2); - - /* To make sure there are no nasty surprises ahead, run through the - * paragraphs we're about to touch, and make sure they all have at least - * an empty dummy run */ - for ( i=p1.para; i<=p2.para; i++ ) { - struct edit_pos ep; - ep.para = i; - ep.pos = 0; - ep.trail = 0; - ensure_run(fr, ep); - } - - if ( !position_editable(fr, p1) || !position_editable(fr, p2) ) { - fprintf(stderr, "Delete outside editable region\n"); - return; - } - - /* Find SC positions for start and end */ - p1scblock = pos_to_scblock(fr, p1, &type1); - p2scblock = pos_to_scblock(fr, p2, &type2); - p2offs = pos_to_offset(fr, p2); - wrap_end = p2.para; - - printf("SCBlocks %p to %p\n", p1scblock, p2scblock); - if ( p1scblock == NULL ) { - fprintf(stderr, "Starting block NULL. Not deleting.\n"); - return; - } - if ( p2scblock == NULL ) { - fprintf(stderr, "Ending block NULL. Not deleting.\n"); - return; - } - //show_sc_blocks(p1scblock); - - if ( (p1scblock == p2scblock) && (type1 == PARA_TYPE_TEXT) ) { - - size_t p1offs; - printf("Simple case, one SCBlock\n"); - - assert(type1 == type2); - - /* Remove the text and update the run length */ - p1offs = pos_to_offset(fr, p1); - scblock_delete_text(p1scblock, p1offs, p2offs); - - wrap_paragraph(fr->paras[p1.para], NULL, wrapw, 0, 0); - - return; - } - - /* Starting point for iteration over blocks in middle of range. - * Record this now, because p1scblock might be about to get deleted */ - scblock = sc_block_next(p1scblock); - - /* First SCBlock in range: delete whole thing or second half */ - printf("First block %p\n", p1scblock); - if ( type1 == PARA_TYPE_TEXT ) { - - size_t p1offs = pos_to_offset(fr, p1); - int p1run = pos_to_run_number(fr, p1); - printf(" offs %li\n", (long int)p1offs); - if ( p1offs != 0 ) { - printf("Partial delete\n"); - printf("contents '%s'\n", sc_block_contents(p1scblock)); - printf("from offs %li\n", (long int)p1offs); - scblock_delete_text(p1scblock, p1offs, -1); - } else { - printf("Deleting the whole text SCBlock\n"); - sc_block_delete(&fr->scblocks, p1scblock); - delete_run(fr->paras[p1.para], p1run); - } - - } else { - printf("Deleting the whole non-text SCBlock\n"); - sc_block_delete(&fr->scblocks, p1scblock); - } - - /* Delete all the complete SCBlocks in the middle of the range */ - if ( !block_is_under(p2scblock, scblock) ) { - do { - - SCBlock *next; - - /* For each SC block in middle of range: */ - printf("Deleting %p\n", scblock); - if ( scblock == NULL ) { - fprintf(stderr, "nothing?\n"); - break; - } - printf("name is '%s'\n", sc_block_name(scblock)); - - if ( (sc_block_name(scblock) != NULL) - && (strcmp(sc_block_name(scblock), "newpara") == 0) ) - { - /* Deleting newpara block, merge the paragraphs */ - merge_paragraphs_by_newpara(fr, scblock); - p2.para--; - } - - next = sc_block_next(scblock); - delete_run_for_scblock(fr, fr->paras[p1.para], - fr->paras[p2.para], scblock, - &p2.para); - sc_block_delete(&fr->scblocks, scblock); - - scblock = next; - - } while ( !block_is_under(p2scblock, scblock) ); - } - - /* Last SCBlock in range: delete whole thing or first half */ - printf("Last block %p (%s)\n", p2scblock, sc_block_name(p2scblock)); - if ( type2 == PARA_TYPE_TEXT ) { - size_t len; - printf(" offs %li\n", (long int)p2offs); - if ( sc_block_contents(p2scblock) != NULL ) { - len = strlen(sc_block_contents(p2scblock)); - } else { - len = 0; - } - printf(" len %li\n", (long int)len); - if ( (len > 0) && (p2offs == len) ) { - printf("Deleting the whole text SCBlock\n"); - printf("deleting block %p\n", p2scblock); - show_sc_block(p2scblock, ""); - sc_block_delete(&fr->scblocks, p2scblock); - delete_run_for_scblock(fr, fr->paras[p1.para], - fr->paras[p2.para], p2scblock, - NULL); - } else if ( p2offs > 0 ) { - printf("Partial delete\n"); - printf("contents '%s'\n", sc_block_contents(p2scblock)); - printf("up to offs %li\n", (long int)p2offs); - scblock_delete_text(p2scblock, 0, p2offs); - } /* else do nothing */ - } else { - printf("Deleting the whole non-text SCBlock\n"); - sc_block_delete(&fr->scblocks, p2scblock); - } - - /* If any paragraphs have been deleted, this will wrap too many - * paragraphs, but it doesn't matter as long as we don't wrap - * past the end of the frame's contents. */ - for ( i=p1.para; i<=wrap_end; i++ ) { - if ( i >= fr->n_paras ) break; - printf("Wrapping para %i (%p)\n", i, fr->paras[i]); - wrap_paragraph(fr->paras[i], NULL, wrapw, 0, 0); - } - printf("All done.\n"); -} - - -void show_para(Paragraph *p) -{ - printf("Paragraph %p\n", p); - - if ( p->type == PARA_TYPE_TEXT ) { - - int i; - - printf("%i runs:\n", p->n_runs); - for ( i=0; in_runs; i++ ) { - printf(" Run %2i: SCBlock %p %s '%s'\n", - i, p->runs[i].scblock, - pango_font_description_to_string(p->runs[i].fontdesc), - sc_block_contents(p->runs[i].scblock)); - } - - } else if ( p->type == PARA_TYPE_IMAGE ) { - printf(" Image: %s\n", p->filename); - } else { - printf(" Other paragraph type\n"); - } -} - - -static SCBlock *split_text_paragraph(struct frame *fr, int pn, size_t pos, - PangoContext *pc) -{ - Paragraph *pnew; - int i; - SCBlock *nnp; - size_t run_offs; - int run; - Paragraph *para = fr->paras[pn]; - struct text_run *rr; - - pnew = insert_paragraph(fr, pn); - if ( pnew == NULL ) { - fprintf(stderr, "Failed to insert paragraph\n"); - return NULL; - } - - /* Determine which run the cursor is in */ - run = which_run(para, pos); - - /* Create the new (second) paragraph */ - pnew->type = PARA_TYPE_TEXT; - pnew->n_runs = 0; - pnew->runs = NULL; - for ( i=0; i<4; i++ ) pnew->space[i] = para->space[i]; - - rr = ¶->runs[run]; - run_offs = pos - get_paragraph_offset(para, run); - printf("split at run %i\n", run); - - /* Easy case: splitting at a run boundary */ - if ( run_offs == run_text_len(rr) ) { - - /* Even easier case: splitting at the end of the paragraph */ - if ( run == para->n_runs-1 ) { - - SCBlock *np; - SCBlock *end; - - printf("Simple new para\n"); - - if ( get_newline_at_end(para) == NULL ) { - - /* The current paragraph doesn't have - * a \newpara yet */ - - np = sc_block_append(rr->scblock, - strdup("newpara"), NULL, - NULL, NULL); - set_newline_at_end(para, np); - - } else { - - SCBlock *op; - - /* If the current paragraph did have \newpara, - * then the new one needs one too */ - np = sc_block_append(rr->scblock, - strdup("newpara"), - NULL, NULL, NULL); - op = get_newline_at_end(para); - set_newline_at_end(para, np); - set_newline_at_end(pnew, op); - - - } - - /* Add an empty run + SCBlock to type into */ - end = sc_block_append(np, NULL, NULL, strdup(""), NULL); - - pnew->n_runs = 0; - add_run(pnew, end, rr->fontdesc, rr->col, NULL); - pnew->scblock = end; - - wrap_paragraph(pnew, pc, fr->w - fr->pad_l - fr->pad_r, 0, 0); - - return end; - - } - - } else { - - /* Split the run (and SCBlock) into two */ - double col[4] = {0,0,0,0}; - struct text_run *rn; - - printf("Splitting run %i. Before:\n", run); - show_para(para); - - add_run(para, NULL, NULL, col, NULL); - /* -2 here because add_run increased para->n_runs by 1 */ - memmove(¶->runs[run+2], ¶->runs[run+1], - (para->n_runs - run - 2)*sizeof(struct text_run)); - - rr = ¶->runs[run]; /* Because add_run realloced the runs */ - rn = ¶->runs[run+1]; - - rn->scblock = sc_block_split(rr->scblock, run_offs); - - rn->fontdesc = pango_font_description_copy(rr->fontdesc); - rn->col[0] = rr->col[0]; - rn->col[1] = rr->col[1]; - rn->col[2] = rr->col[2]; - rn->col[3] = rr->col[3]; - - printf("After:\n"); - show_para(para); - - } - - /* All later runs just get moved to the new paragraph */ - for ( i=run+1; in_runs; i++ ) { - double col[4] = {0,0,0,0}; - printf("Moving run %i to pos %i\n", i, pnew->n_runs); - add_run(pnew, NULL, NULL, col, NULL); - pnew->runs[pnew->n_runs-1] = para->runs[i]; - } - pnew->scblock = pnew->runs[0].scblock; - - /* Truncate the first paragraph at the appropriate position */ - para->n_runs = run+1; - - printf("Final paragraphs:\n"); - printf("First:\n"); - show_para(para); - printf("Second:\n"); - show_para(pnew); - - /* Add a \newpara after the end of the first paragraph's SC */ - nnp = sc_block_append(rr->scblock, strdup("newpara"), NULL, NULL, NULL); - set_newline_at_end(pnew, get_newline_at_end(para)); - set_newline_at_end(para, nnp); - - wrap_paragraph(para, pc, fr->w - fr->pad_l - fr->pad_r, 0, 0); - wrap_paragraph(pnew, pc, fr->w - fr->pad_l - fr->pad_r, 0, 0); - - return sc_block_next(nnp); -} - - -SCBlock *split_paragraph(struct frame *fr, int pn, size_t pos, PangoContext *pc) -{ - Paragraph *para = fr->paras[pn]; - - if ( para->type == PARA_TYPE_TEXT ) { - return split_text_paragraph(fr, pn, pos, pc); - } else { - /* Other types can't be split */ - return NULL; - } -} - - -SCBlock *block_at_cursor(struct frame *fr, int pn, size_t pos) -{ - Paragraph *para = fr->paras[pn]; - - if ( para->type != PARA_TYPE_CALLBACK ) return NULL; - - return para->scblock; -} - - -int get_sc_pos(struct frame *fr, int pn, size_t pos, - SCBlock **bl, size_t *ppos) -{ - Paragraph *para = fr->paras[pn]; - int nrun; - struct text_run *run; - - nrun = which_run(para, pos); - if ( nrun == para->n_runs ) { - fprintf(stderr, "Couldn't find run to insert into.\n"); - return 1; - } - run = ¶->runs[nrun]; - - *ppos = pos - get_paragraph_offset(para, nrun); - *bl = run->scblock; - - return 0; -} - - -void set_para_spacing(Paragraph *para, float space[4]) -{ - if ( para == NULL ) return; - para->space[0] = space[0]; - para->space[1] = space[1]; - para->space[2] = space[2]; - para->space[3] = space[3]; -} - - -void set_para_alignment(Paragraph *para, PangoAlignment align) -{ - if ( para == NULL ) return; - para->alignment = align; -} - - -void *get_para_bvp(Paragraph *para) -{ - if ( para->type != PARA_TYPE_CALLBACK ) return NULL; - return para->bvp; -} - - -SCBlock *para_scblock(Paragraph *para) -{ - return para->scblock; -} - - -enum para_type para_type(Paragraph *para) -{ - return para->type; -} - - -int para_debug_num_runs(Paragraph *para) -{ - if ( para->type != PARA_TYPE_TEXT ) return 0; - return para->n_runs; -} - - -int para_debug_run_info(Paragraph *para, int i, SCBlock **scblock) -{ - if ( para->type != PARA_TYPE_TEXT ) return 1; - if ( i >= para->n_runs ) return 1; - - *scblock = para->runs[i].scblock; - return 0; -} diff --git a/src/frame.h b/src/frame.h deleted file mode 100644 index d0525f8..0000000 --- a/src/frame.h +++ /dev/null @@ -1,194 +0,0 @@ -/* - * frame.h - * - * Copyright © 2013-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 FRAME_H -#define FRAME_H - -#ifdef HAVE_CONFIG_H -#include -#endif - -#include -#include - - -typedef enum -{ - GRAD_NONE, - GRAD_HORIZ, - GRAD_VERT -} GradientType; - -enum para_type -{ - PARA_TYPE_TEXT, - PARA_TYPE_IMAGE, - PARA_TYPE_CALLBACK -}; - -typedef struct _paragraph Paragraph; - -#include "sc_parse.h" -#include "sc_interp.h" -#include "imagestore.h" - - -struct edit_pos -{ - int para; /* Paragraph number */ - size_t pos; /* Byte position within paragraph - * Yes, really. See pango_layout_xy_to_index */ - int trail; -}; - - -struct frame -{ - struct frame **children; - int num_children; - int max_children; - - SCBlock *scblocks; - - Paragraph **paras; - int n_paras; - int max_paras; - - /* The font which will be used by default for this frame */ - PangoFontDescription *fontdesc; - double col[4]; - - /* The rectangle allocated to this frame, determined by the renderer */ - double x; - double y; - double w; - double h; - - double pad_t; - double pad_b; - double pad_l; - double pad_r; - - /* Background properties for this frame */ - double bgcol[4]; - double bgcol2[4]; - GradientType grad; - - /* True if this frame should be deleted on the next mouse click */ - int empty; - - /* True if the aspect ratio of this frame should be maintained */ - int is_image; - - /* True if this frame can be resized and moved */ - int resizable; -}; - - -extern struct frame *frame_new(void); -extern void frame_free(struct frame *fr); -extern struct frame *add_subframe(struct frame *fr); -extern struct frame *find_frame_with_scblocks(struct frame *top, - SCBlock *scblocks); - -extern double total_height(struct frame *fr); - -extern Paragraph *last_para(struct frame *fr); -extern void show_para(Paragraph *p); -extern void set_para_spacing(Paragraph *para, float space[4]); -extern void set_para_alignment(Paragraph *para, PangoAlignment align); - -extern double paragraph_height(Paragraph *para); -extern void render_paragraph(cairo_t *cr, Paragraph *para, ImageStore *is); - -extern void set_newline_at_end(Paragraph *para, SCBlock *bl); -extern void show_edit_pos(struct edit_pos a); - -extern void add_run(Paragraph *para, SCBlock *scblock, - PangoFontDescription *fdesc, double col[4], - const char *real_text); - -extern Paragraph *insert_paragraph(struct frame *fr, int pos); - -extern Paragraph *add_callback_para(struct frame *fr, SCBlock *scblock, - double w, double h, - SCCallbackDrawFunc draw_func, - SCCallbackClickFunc click_func, void *bvp, - void *vp); - -extern void add_image_para(struct frame *fr, SCBlock *scblock, - const char *filename, - ImageStore *is, double w, double h, int editable); - -extern void wrap_paragraph(Paragraph *para, PangoContext *pc, double w, - size_t sel_start, size_t sel_end); - -extern int find_cursor(struct frame *fr, double x, double y, - struct edit_pos *pos); - -extern void sort_positions(struct edit_pos *a, struct edit_pos *b); - -extern void ensure_run(struct frame *fr, struct edit_pos cpos); - -extern int positions_equal(struct edit_pos a, struct edit_pos b); - -extern int get_para_highlight(struct frame *fr, int cursor_para, - double *cx, double *cy, double *cw, double *ch); - -extern int position_editable(struct frame *fr, struct edit_pos cp); - -extern int get_cursor_pos(struct frame *fr, int cursor_para, int cursor_pos, - double *cx, double *cy, double *ch); - -extern void cursor_moveh(struct frame *fr, struct edit_pos *cp, signed int dir); - -extern void check_callback_click(struct frame *fr, int para); - -extern size_t pos_trail_to_offset(Paragraph *para, size_t offs, int trail); - -extern void insert_text_in_paragraph(Paragraph *para, size_t offs, - const char *t); - -extern void delete_text_from_frame(struct frame *fr, struct edit_pos p1, struct edit_pos p2, - double wrap_w); - -extern SCBlock *split_paragraph(struct frame *fr, int pn, size_t pos, - PangoContext *pc); -extern SCBlock *block_at_cursor(struct frame *fr, int para, size_t pos); - -extern void show_frame_hierarchy(struct frame *fr, const char *t); - -extern int get_sc_pos(struct frame *fr, int pn, size_t pos, - SCBlock **bl, size_t *ppos); - -extern void *get_para_bvp(Paragraph *para); - -extern Paragraph *create_paragraph(struct frame *fr, SCBlock *bl); - -extern enum para_type para_type(Paragraph *para); -extern SCBlock *para_scblock(Paragraph *para); - -extern SCBlock *para_debug_get_newline_at_end(Paragraph *para); -extern int para_debug_num_runs(Paragraph *para); -extern int para_debug_run_info(Paragraph *para, int i, SCBlock **scblock); - -#endif /* FRAME_H */ diff --git a/src/imagestore.c b/src/imagestore.c deleted file mode 100644 index 61ebcfb..0000000 --- a/src/imagestore.c +++ /dev/null @@ -1,369 +0,0 @@ -/* - * imagestore.c - * - * Copyright © 2013-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 -#include -#include -#include -#include - -#include "imagestore.h" -#include "utils.h" - -#define MAX_SIZES (32) - -struct image_record -{ - char *filename; - cairo_surface_t *surf[MAX_SIZES]; - int w[MAX_SIZES]; - int last_used[MAX_SIZES]; - int n_sizes; - int h_last_used; - int full_width; - int full_height; -}; - - -struct _imagestore -{ - int n_images; - struct image_record *images; - int max_images; - GFile *pparent; - GFile *iparent; - const char *storename; -}; - - -static int alloc_images(ImageStore *is, int new_max_images) -{ - struct image_record *images_new; - - images_new = realloc(is->images, - sizeof(struct image_record)*new_max_images); - if ( images_new == NULL ) return 1; - - is->images = images_new; - is->max_images = new_max_images; - return 0; -} - - -ImageStore *imagestore_new(const char *storename) -{ - ImageStore *is; - - is = calloc(1, sizeof(ImageStore)); - if ( is == NULL ) return NULL; - - is->images = NULL; - is->pparent = NULL; - if ( storename != NULL ) { - is->iparent = g_file_new_for_uri(storename); - } else { - is->iparent = NULL; - } - is->n_images = 0; - is->max_images = 0; - if ( alloc_images(is, 32) ) { - free(is); - return NULL; - } - - return is; -} - - -void imagestore_set_parent(ImageStore *is, GFile *parent) -{ - if ( is->pparent != NULL ) { - g_object_unref(is->pparent); - } - is->pparent = parent; -} - - -void imagestore_destroy(ImageStore *is) -{ - int i; - - for ( i=0; in_images; i++ ) { - int j; - free(is->images[i].filename); - for ( j=0; jimages[i].n_sizes; j++ ) { - if ( is->images[i].surf[j] != NULL ) { - g_object_unref(is->images[i].surf[j]); - } - } - } - g_object_unref(is->pparent); - g_object_unref(is->iparent); - free(is->images); - free(is); -} - - -static cairo_surface_t *pixbuf_to_surface(GdkPixbuf *t) -{ - cairo_surface_t *surf; - cairo_t *cr; - int w, h; - - if ( t == NULL ) return NULL; - - w = gdk_pixbuf_get_width(t); - h = gdk_pixbuf_get_height(t); - - surf = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, w, h); - if ( surf == NULL ) return NULL; - - cr = cairo_create(surf); - - gdk_cairo_set_source_pixbuf(cr, t, 0, 0); - cairo_pattern_t *patt = cairo_get_source(cr); - cairo_pattern_set_extend(patt, CAIRO_EXTEND_PAD); - cairo_pattern_set_filter(patt, CAIRO_FILTER_BEST); - cairo_paint(cr); - - cairo_destroy(cr); - - return surf; -} - - -static int try_load(GFile *file, GdkPixbuf **pixbuf, gint w, gint h) -{ - GFileInputStream *stream; - GError *error = NULL; - - stream = g_file_read(file, NULL, &error); - if ( stream != NULL ) { - GError *pberr = NULL; - *pixbuf = gdk_pixbuf_new_from_stream_at_scale(G_INPUT_STREAM(stream), - w, h, TRUE, - NULL, &pberr); - g_object_unref(stream); - g_object_unref(file); - return 1; - } - - return 0; -} - - -static GdkPixbuf *load_image(const char *uri, GFile *pparent, GFile *iparent, - gint w, gint h) -{ - GFile *file; - GdkPixbuf *pixbuf; - - /* Literal pathname */ - file = g_file_new_for_path(uri); - if ( try_load(file, &pixbuf, w, h) ) return pixbuf; - - /* Try the file prefixed with the directory the presentation is in */ - if ( pparent != NULL ) { - file = g_file_get_child(pparent, uri); - if ( try_load(file, &pixbuf, w, h) ) return pixbuf; - } - - /* Try prefixing with imagestore folder */ - if ( iparent != NULL ) { - file = g_file_get_child(iparent, uri); - if ( try_load(file, &pixbuf, w, h) ) return pixbuf; - } - - return NULL; -} - - -static struct image_record *add_image_record(ImageStore *is, - const char *filename) -{ - int idx; - - if ( is->n_images == is->max_images ) { - if ( alloc_images(is, is->max_images+32) ) { - fprintf(stderr, "Couldn't allocate memory for image " - "records.\n"); - return NULL; - } - } - - idx = is->n_images++; - - is->images[idx].n_sizes = 0; - is->images[idx].filename = strdup(filename); - is->images[idx].h_last_used = 0; - - return &is->images[idx]; -} - - - -int imagestore_get_size(ImageStore *is, const char *filename, - int *w, int *h) -{ - GdkPixbuf *pixbuf; - struct image_record *ir; - int i; - - for ( i=0; in_images; i++ ) { - if ( strcmp(is->images[i].filename, filename) == 0 ) { - *w = is->images[i].full_width; - *h = is->images[i].full_height; - return 0; - } - } - - pixbuf = load_image(filename, is->pparent, is->iparent, -1, -1); - if ( pixbuf == NULL ) return 1; - - - *w = gdk_pixbuf_get_width(pixbuf); - *h = gdk_pixbuf_get_height(pixbuf); - - ir = add_image_record(is, filename); - if ( ir != NULL ) { - ir->full_width = *w; - ir->full_height = *h; - } /* otherwise can't cache, too bad */ - - g_object_unref(pixbuf); - - return 0; -} - - -static int find_earliest(struct image_record *im) -{ - int i, earliest, l; - - earliest = im->h_last_used; - l = im->n_sizes; - - for ( i=0; in_sizes; i++ ) { - if ( im->last_used[i] < earliest ) { - earliest = im->last_used[i]; - l = i; - } - } - - assert(l != im->n_sizes); - - cairo_surface_destroy(im->surf[l]); - - return l; -} - - -static cairo_surface_t *add_image_size(struct image_record *im, - const char *filename, - GFile *pparent, GFile *iparent, - int w) -{ - cairo_surface_t *surf; - GdkPixbuf *t; - int pos; - - t = load_image(filename, pparent, iparent, w, -1); - if ( t == NULL ) return NULL; - surf = pixbuf_to_surface(t); - g_object_unref(t); - - /* Add surface to list */ - if ( im->n_sizes == MAX_SIZES ) { - pos = find_earliest(im); - } else { - pos = im->n_sizes++; - } - - im->w[pos] = w; - im->surf[pos] = surf; - im->last_used[pos] = im->h_last_used++; - - return surf; -} - - -static cairo_surface_t *add_new_image(ImageStore *is, const char *filename, int w) -{ - struct image_record *ir; - ir = add_image_record(is, filename); - if ( ir == NULL ) return NULL; - return add_image_size(ir, filename, is->pparent, is->iparent, w); -} - - -void show_imagestore(ImageStore *is) -{ - int i; - - printf("Store %p contains %i records.\n", is, is->n_images); - - for ( i=0; in_images; i++ ) { - - printf("%s :\n", is->images[i].filename); - printf("\n"); - - } -} - - -cairo_surface_t *lookup_image(ImageStore *is, const char *filename, int w) -{ - int i, j; - int found = 0; - cairo_surface_t *surf; - - for ( i=0; in_images; i++ ) { - if ( strcmp(is->images[i].filename, filename) == 0 ) { - found = 1; - break; - } - } - if ( !found ) { - return add_new_image(is, filename, w); - } - - for ( j=0; jimages[i].n_sizes; j++ ) { - if ( is->images[i].w[j] == w ) { - is->images[i].last_used[j] = is->images[i].h_last_used++; - return is->images[i].surf[j]; - } - } - - /* We don't have this size yet */ - surf = add_image_size(&is->images[i], filename, is->pparent, - is->iparent, w); - return surf; -} diff --git a/src/imagestore.h b/src/imagestore.h deleted file mode 100644 index 0d4df24..0000000 --- a/src/imagestore.h +++ /dev/null @@ -1,43 +0,0 @@ -/* - * imagestore.h - * - * Copyright © 2013-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 IMAGESTORE_H -#define IMAGESTORE_H - -#ifdef HAVE_CONFIG_H -#include -#endif - -#include -#include - -typedef struct _imagestore ImageStore; - -extern ImageStore *imagestore_new(const char *storename); -extern void imagestore_destroy(ImageStore *is); -extern void imagestore_set_parent(ImageStore *is, GFile *parent); -extern void show_imagestore(ImageStore *is); - -extern cairo_surface_t *lookup_image(ImageStore *is, const char *uri, int w); -extern int imagestore_get_size(ImageStore *is, const char *uri, int *w, int *h); - -#endif /* IMAGESTORE_H */ diff --git a/src/narrative_window.c b/src/narrative_window.c deleted file mode 100644 index e0c59ef..0000000 --- a/src/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/narrative_window.h b/src/narrative_window.h deleted file mode 100644 index 24b4a4b..0000000 --- a/src/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/pdfstorycode.c b/src/pdfstorycode.c new file mode 100644 index 0000000..276a381 --- /dev/null +++ b/src/pdfstorycode.c @@ -0,0 +1,55 @@ +/* + * pdfstorycode.c + * + * Copyright © 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 . + * + */ + + +#include +#include +#include +#include +#include + +#include "storycode.h" +#include "presentation.h" + +int main(int argc, char *argv[]) +{ + GFile *file; + GBytes *bytes; + const char *text; + size_t len; + Presentation *p; + int i; + + file = g_file_new_for_commandline_arg(argv[1]); + bytes = g_file_load_bytes(file, NULL, NULL, NULL); + text = g_bytes_get_data(bytes, &len); + p = storycode_parse_presentation(text); + g_bytes_unref(bytes); + + /* Render each slide to PDF */ +// for ( i=0; i - * - * 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 "presentation.h" -#include "pr_clock.h" -#include "utils.h" - - -struct pr_clock -{ - int open; - - GtkWidget *window; - GtkWidget *entry; - GtkWidget *startbutton; - GtkWidget *da; - GtkWidget *wallclock; - GtkWidget *elapsed; - GtkWidget *remaining; - GtkWidget *status; - GTimeZone *tz; - - GDateTime *start; - double time_elapsed_at_start; - guint timer_id; - - int running; - double time_allowed; - double time_elapsed; - int pos; - int end; - int pos_reached; - - double t; - double tf; -}; - - -static char *format_span(int n) -{ - char tmp[32]; - int hours, mins, sec; - char *s; - - if ( n < 0 ) { - s = "-"; - n = -n; - } else { - s = ""; - } - - sec = n % 60; - mins = ((n-sec) % (60*60))/60; - hours = (n-sec-mins) / (60*60); - - snprintf(tmp, 31, "%s%i:%02i:%02i", s, hours, mins, sec); - - return strdup(tmp); -} - - -static char *format_span_nice(int n) -{ - char tmp[64]; - int hours, mins, sec; - char *s; - - if ( n < 0 ) { - s = "behind"; - n = -n; - } else { - s = "ahead"; - } - - sec = n % 60; - mins = ((n-sec) % (60*60))/60; - hours = (n-sec-mins) / (60*60); - - if ( n <= 60 ) { - snprintf(tmp, 63, "%i seconds %s", n, s); - return strdup(tmp); - } - - if ( n < 60*60 ) { - snprintf(tmp, 63, "%i min %i seconds %s", mins, sec, s); - return strdup(tmp); - } - - snprintf(tmp, 63, "%i hours, %i min, %i seconds %s", - hours, mins, sec, s); - return strdup(tmp); -} - - -static gboolean update_clock(gpointer data) -{ - struct pr_clock *n = data; - gchar *d; - GDateTime *dt; - GTimeSpan sp; - double time_remaining; - double delta; - gint w, h; - char *tmp; - - if ( !n->open ) { - g_date_time_unref(n->start); - g_time_zone_unref(n->tz); - free(n); - return FALSE; - } - - dt = g_date_time_new_now(n->tz); - - if ( n->running ) { - - sp = g_date_time_difference(dt, n->start); - n->time_elapsed = n->time_elapsed_at_start + - sp / G_TIME_SPAN_SECOND; - - time_remaining = n->time_allowed - n->time_elapsed; - - tmp = format_span(n->time_elapsed); - gtk_label_set_text(GTK_LABEL(n->elapsed), tmp); - free(tmp); - - tmp = format_span(time_remaining); - gtk_label_set_text(GTK_LABEL(n->remaining), tmp); - free(tmp); - - } else { - - n->time_elapsed = n->time_elapsed_at_start; - - time_remaining = n->time_allowed - n->time_elapsed; - - tmp = format_span(n->time_elapsed); - gtk_label_set_text(GTK_LABEL(n->elapsed), tmp); - free(tmp); - - tmp = format_span(time_remaining); - gtk_label_set_text(GTK_LABEL(n->remaining), tmp); - free(tmp); - - } - - d = g_date_time_format(dt, "%H:%M:%S"); - g_date_time_unref(dt); - - gtk_label_set_text(GTK_LABEL(n->wallclock), d); - free(d); - - n->t = n->time_elapsed / n->time_allowed; - - if ( n->time_allowed == 0.0 ) n->t = 0.0; - if ( n->time_elapsed > n->time_allowed ) n->t = 1.0; - - if ( n->end > 0 ) { - n->tf = (double)n->pos_reached / (n->end-1); - } else { - n->tf = 0.0; - } - - delta = (n->tf - n->t)*n->time_allowed; - tmp = format_span_nice(delta); - gtk_label_set_text(GTK_LABEL(n->status), tmp); - free(tmp); - - w = gtk_widget_get_allocated_width(GTK_WIDGET(n->da)); - h = gtk_widget_get_allocated_height(GTK_WIDGET(n->da)); - gtk_widget_queue_draw_area(n->da, 0, 0, w, h); - - return TRUE; -} - - -void pr_clock_set_pos(PRClock *n, int pos, int end) -{ - if ( n == NULL ) return; - n->pos = pos; - if ( n->pos > n->pos_reached ) { - n->pos_reached = pos; - } - n->end = end; - update_clock(n); -} - - -static gint close_clock_sig(GtkWidget *w, PRClock *n) -{ - g_source_remove(n->timer_id); - free(n); - return FALSE; -} - - -static gboolean clock_draw_sig(GtkWidget *da, cairo_t *cr, struct pr_clock *n) -{ - int width, height; - double s; - double ff; - - width = gtk_widget_get_allocated_width(GTK_WIDGET(da)); - height = gtk_widget_get_allocated_height(GTK_WIDGET(da)); - s = width-20.0; - - /* Overall background */ - cairo_rectangle(cr, 10.0, 0.0, s, height); - cairo_set_source_rgb(cr, 1.0, 1.0, 1.0); - cairo_fill(cr); - - cairo_rectangle(cr, 10.0, 0.0, s*n->t, height); - cairo_set_source_rgb(cr, 0.0, 0.0, 1.0); - cairo_fill(cr); - - if ( n->tf > n->t ) { - cairo_rectangle(cr, 10.0+s*n->t, 0.0, (n->tf - n->t)*s, height); - cairo_set_source_rgb(cr, 0.0, 1.0, 0.0); - cairo_fill(cr); - } else { - cairo_rectangle(cr, 10.0+s*n->t, 0.0, (n->tf - n->t)*s, height); - cairo_set_source_rgb(cr, 1.0, 0.0, 0.0); - cairo_fill(cr); - } - - ff = (double)n->pos / (n->end-1); - if ( n->end == 1 ) ff = 0.0; - cairo_move_to(cr, 10.0+ff*s, 0.0); - cairo_line_to(cr, 10.0+ff*s, height); - cairo_set_line_width(cr, 2.0); - cairo_set_source_rgb(cr, 1.0, 0.0, 1.0); - cairo_stroke(cr); - - if ( !n->running ) { - cairo_move_to(cr, 10.0, height*0.8); - cairo_set_font_size(cr, height*0.8); - cairo_select_font_face(cr, "sans-serif", - CAIRO_FONT_SLANT_NORMAL, - CAIRO_FONT_WEIGHT_BOLD); - cairo_set_source_rgb(cr, 0.0, 0.0, 0.0); - cairo_show_text(cr, _("Timer is NOT running!")); - } - - return FALSE; -} - - -static void set_sig(GtkEditable *w, struct pr_clock *n) -{ - const gchar *t; - char *check; - - t = gtk_entry_get_text(GTK_ENTRY(n->entry)); - n->time_allowed = 60.0 * strtod(t, &check); - if ( check == t ) { - fprintf(stderr, "Invalid time '%s'\n", t); - n->time_allowed = 0.0; - } - - update_clock(n); -} - - -static gboolean reset_sig(GtkWidget *w, gpointer data) -{ - struct pr_clock *n = data; - - n->time_elapsed = 0; - n->time_elapsed_at_start = 0; - - if ( n->start != NULL ) { - g_date_time_unref(n->start); - } - - n->start = g_date_time_new_now(n->tz); - - update_clock(n); - - return FALSE; -} - - -static gboolean setpos_sig(GtkWidget *w, gpointer data) -{ - struct pr_clock *n = data; - n->pos_reached = n->pos; - update_clock(n); - return FALSE; -} - - -static gboolean start_sig(GtkWidget *w, gpointer data) -{ - struct pr_clock *n = data; - - if ( n->running ) { - n->running = 0; - n->time_elapsed_at_start = n->time_elapsed; - gtk_label_set_text(GTK_LABEL(gtk_bin_get_child(GTK_BIN(w))), - _("Start")); - } else { - n->time_elapsed_at_start = n->time_elapsed; - if ( n->start != NULL ) { - g_date_time_unref(n->start); - } - n->start = g_date_time_new_now(n->tz); - n->running = 1; - gtk_label_set_text(GTK_LABEL(gtk_bin_get_child(GTK_BIN(w))), - _("Stop")); - } - - update_clock(n); - - return FALSE; -} - - -PRClock *pr_clock_new() -{ - struct pr_clock *n; - GtkWidget *vbox; - GtkWidget *hbox; - GtkWidget *resetbutton; - GtkWidget *setposbutton; - GtkWidget *grid; - GtkWidget *label; - - n = malloc(sizeof(struct pr_clock)); - if ( n == NULL ) return NULL; - n->open = 1; - - n->tz = g_time_zone_new_local(); - - n->window = gtk_window_new(GTK_WINDOW_TOPLEVEL); - gtk_window_set_default_size(GTK_WINDOW(n->window), 600, 150); - - vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0); - gtk_container_add(GTK_CONTAINER(n->window), vbox); - - hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0); - gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 10); - - label = gtk_label_new(_("Length (mins):")); - gtk_label_set_markup(GTK_LABEL(label), _("Length (mins):")); - g_object_set(G_OBJECT(label), "halign", GTK_ALIGN_END, NULL); - gtk_box_pack_start(GTK_BOX(hbox), label, TRUE, TRUE, 10); - - n->entry = gtk_entry_new(); - gtk_box_pack_start(GTK_BOX(hbox), n->entry, TRUE, TRUE, 0); - - n->startbutton = gtk_button_new_with_label(_("Start")); - gtk_box_pack_start(GTK_BOX(hbox), n->startbutton, TRUE, TRUE, 10); - - resetbutton = gtk_button_new_with_label(_("Reset")); - gtk_box_pack_start(GTK_BOX(hbox), resetbutton, TRUE, TRUE, 10); - - setposbutton = gtk_button_new_with_label(_("Set position")); - gtk_box_pack_start(GTK_BOX(hbox), setposbutton, TRUE, TRUE, 10); - - n->da = gtk_drawing_area_new(); - gtk_box_pack_start(GTK_BOX(vbox), n->da, TRUE, TRUE, 0); - g_signal_connect(G_OBJECT(n->da), "draw", G_CALLBACK(clock_draw_sig), n); - g_signal_connect(G_OBJECT(n->window), "destroy", - G_CALLBACK(close_clock_sig), n); /* FIXME: Uniqueness */ - - grid = gtk_grid_new(); - gtk_grid_set_row_spacing(GTK_GRID(grid), 10); - gtk_grid_set_column_homogeneous(GTK_GRID(grid), TRUE); - gtk_box_pack_start(GTK_BOX(vbox), grid, FALSE, FALSE, 10); - label = gtk_label_new(_("Time elapsed")); - gtk_label_set_markup(GTK_LABEL(label), _("Time elapsed")); - gtk_grid_attach(GTK_GRID(grid), label, 0, 0, 1, 1); - label = gtk_label_new(_("Time remaining")); - gtk_label_set_markup(GTK_LABEL(label), _("Time remaining")); - gtk_grid_attach(GTK_GRID(grid), label, 1, 0, 1, 1); - n->status = gtk_label_new(""); - gtk_grid_attach(GTK_GRID(grid), n->status, 2, 0, 1, 1); - n->elapsed = gtk_label_new(""); - gtk_grid_attach(GTK_GRID(grid), n->elapsed, 0, 1, 1, 1); - n->remaining = gtk_label_new(""); - gtk_grid_attach(GTK_GRID(grid), n->remaining, 1, 1, 1, 1); - n->wallclock = gtk_label_new(""); - gtk_grid_attach(GTK_GRID(grid), n->wallclock, 2, 1, 1, 1); - - g_signal_connect(G_OBJECT(n->startbutton), "clicked", - G_CALLBACK(start_sig), n); - g_signal_connect(G_OBJECT(resetbutton), "clicked", - G_CALLBACK(reset_sig), n); - g_signal_connect(G_OBJECT(setposbutton), "clicked", - G_CALLBACK(setpos_sig), n); - g_signal_connect(G_OBJECT(n->entry), "changed", - G_CALLBACK(set_sig), n); - - n->running = 0; - n->time_allowed = 0; - n->time_elapsed = 0; - n->time_elapsed_at_start = 0; - n->pos = 0; - n->pos_reached = 0; - n->end = 0; - n->start = NULL; - update_clock(n); - n->timer_id = g_timeout_add_seconds(1, update_clock, n); - - gtk_window_set_title(GTK_WINDOW(n->window), _("Presentation clock")); - - gtk_widget_show_all(n->window); - return n; -} diff --git a/src/pr_clock.h b/src/pr_clock.h deleted file mode 100644 index 97d2d0d..0000000 --- a/src/pr_clock.h +++ /dev/null @@ -1,37 +0,0 @@ -/* - * pr_clock.h - * - * Copyright © 2013-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 CLOCK_H -#define CLOCK_H - -#ifdef HAVE_CONFIG_H -#include -#endif - -typedef struct pr_clock PRClock; - -extern PRClock *pr_clock_new(void); - -extern void pr_clock_set_pos(PRClock *n, int pos, int end); - - -#endif /* CLOCK_H */ diff --git a/src/presentation.c b/src/presentation.c deleted file mode 100644 index d7d9c08..0000000 --- a/src/presentation.c +++ /dev/null @@ -1,362 +0,0 @@ -/* - * presentation.c - * - * Copyright © 2013-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 "presentation.h" -#include "slide_window.h" -#include "frame.h" -#include "imagestore.h" -#include "render.h" -#include "sc_interp.h" -#include "utils.h" - - -void free_presentation(struct presentation *p) -{ - /* FIXME: Loads of stuff leaks here */ - g_object_unref(p->file); - g_object_unref(p->stylesheet_from); - imagestore_destroy(p->is); - free(p); -} - - -char *get_titlebar_string(struct presentation *p) -{ - if ( p == NULL || p->file == NULL ) { - return strdup(_("(untitled)")); - } else { - char *bn = g_file_get_basename(p->file); - return bn; - } -} - - -static void find_and_load_stylesheet(struct presentation *p, GFile *file) -{ - GFile *ssfile; - - if ( file != NULL ) { - - /* First choice: /same/directory/.ss */ - gchar *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); - p->stylesheet = stylesheet_load(ssfile); - p->stylesheet_from = ssfile; - g_free(ssuri); - } - } - - /* Second choice: /same/directory/stylesheet.ss */ - if ( p->stylesheet == NULL ) { - GFile *parent = g_file_get_parent(file); - if ( parent != NULL ) { - ssfile = g_file_get_child(parent, "stylesheet.ss"); - if ( ssfile != NULL ) { - p->stylesheet = stylesheet_load(ssfile); - p->stylesheet_from = ssfile; - } - g_object_unref(parent); - } - } - - } - - /* Third choice: /stylesheet.ss */ - if ( p->stylesheet == NULL ) { - ssfile = g_file_new_for_path("./stylesheet.ss"); - p->stylesheet = stylesheet_load(ssfile); - p->stylesheet_from = ssfile; - } - - /* Fourth choice: internal default stylesheet */ - if ( p->stylesheet == NULL ) { - ssfile = g_file_new_for_uri("resource:///uk/me/bitwiz/Colloquium/default.ss"); - p->stylesheet = stylesheet_load(ssfile); - p->stylesheet_from = NULL; - g_object_unref(ssfile); - } - - /* Last resort is NULL stylesheet and SCInterpreter's defaults */ - /* We keep a reference to the GFile */ -} - - -struct presentation *new_presentation(const char *imagestore) -{ - struct presentation *new; - - new = calloc(1, sizeof(struct presentation)); - if ( new == NULL ) return NULL; - - new->file = NULL; - new->stylesheet_from = NULL; - - new->scblocks = NULL; - - /* Default slide size */ - new->slide_width = 1024.0; - new->slide_height = 768.0; - - new->completely_empty = 1; - new->saved = 1; - new->stylesheet = NULL; - new->is = imagestore_new(imagestore); - - new->lang = pango_language_get_default(); - - find_and_load_stylesheet(new, NULL); - - return new; -} - - -int save_presentation(struct presentation *p, GFile *file, GFile *ssfile) -{ - GFileOutputStream *fh; - int r; - int sr; - GError *error = NULL; - - fh = g_file_replace(file, NULL, FALSE, G_FILE_CREATE_NONE, NULL, &error); - if ( fh == NULL ) { - fprintf(stderr, _("Open failed: %s\n"), error->message); - return 1; - } - r = save_sc_block(G_OUTPUT_STREAM(fh), p->scblocks); - if ( r ) { - fprintf(stderr, _("Couldn't save presentation\n")); - } - g_object_unref(fh); - - if ( ssfile != NULL ) { - char *uri = g_file_get_uri(ssfile); - printf(_("Saving stylesheet to %s\n"), uri); - g_free(uri); - sr = stylesheet_save(p->stylesheet, ssfile); - if ( sr ) { - fprintf(stderr, _("Couldn't save stylesheet\n")); - } - if ( p->stylesheet_from != ssfile ) { - if ( p->stylesheet_from != NULL ) { - g_object_unref(p->stylesheet_from); - } - p->stylesheet_from = ssfile; - g_object_ref(p->stylesheet_from); - } - } else { - fprintf(stderr, _("Not saving the stylesheet\n")); - sr = 0; - } - - if ( r || sr ) return 1; - - imagestore_set_parent(p->is, g_file_get_parent(file)); - - if ( p->file != file ) { - if ( p->file != NULL ) g_object_unref(p->file); - p->file = file; - g_object_ref(p->file); - } - p->saved = 1; - update_titlebar(p->narrative_window); - return 0; -} - - -int slide_number(struct presentation *p, SCBlock *sl) -{ - SCBlock *bl = p->scblocks; - int n = 0; - - while ( bl != NULL ) { - if ( safe_strcmp(sc_block_name(bl), "slide") == 0 ) { - n++; - if ( bl == sl ) return n; - } - bl = sc_block_next(bl); - } - - return 0; -} - - -int num_slides(struct presentation *p) -{ - SCBlock *bl = p->scblocks; - int n = 0; - - while ( bl != NULL ) { - if ( safe_strcmp(sc_block_name(bl), "slide") == 0 ) { - n++; - } - bl = sc_block_next(bl); - } - - return n; -} - - -SCBlock *first_slide(struct presentation *p) -{ - SCBlock *bl = p->scblocks; - - while ( bl != NULL ) { - if ( safe_strcmp(sc_block_name(bl), "slide") == 0 ) { - return bl; - } - bl = sc_block_next(bl); - } - - fprintf(stderr, "Couldn't find first slide!\n"); - return NULL; -} - - -SCBlock *last_slide(struct presentation *p) -{ - SCBlock *bl = p->scblocks; - SCBlock *l = NULL; - - while ( bl != NULL ) { - if ( safe_strcmp(sc_block_name(bl), "slide") == 0 ) { - l = bl; - } - bl = sc_block_next(bl); - } - - if ( l == NULL ) { - fprintf(stderr, "Couldn't find last slide!\n"); - } - return l; -} - - -SCBlock *next_slide(struct presentation *p, SCBlock *sl) -{ - SCBlock *bl = sl; - int found = 0; - - while ( bl != NULL ) { - if ( safe_strcmp(sc_block_name(bl), "slide") == 0 ) { - if ( found ) return bl; - } - if ( bl == sl ) { - found = 1; - } - bl = sc_block_next(bl); - } - - fprintf(stderr, "Couldn't find next slide!\n"); - return NULL; -} - - -SCBlock *prev_slide(struct presentation *p, SCBlock *sl) -{ - SCBlock *bl = p->scblocks; - SCBlock *l = NULL; - - while ( bl != NULL ) { - if ( bl == sl ) { - if ( l == NULL ) return sl; /* Already on first slide */ - return l; - } - if ( safe_strcmp(sc_block_name(bl), "slide") == 0 ) { - l = bl; - } - bl = sc_block_next(bl); - } - - fprintf(stderr, "Couldn't find prev slide!\n"); - return NULL; -} - - -static void set_slide_size_from_stylesheet(struct presentation *p) -{ - char *result; - - result = stylesheet_lookup(p->stylesheet, "$.slide", "size"); - if ( result != NULL ) { - float v[2]; - if ( parse_double(result, v) == 0 ) { - p->slide_width = v[0]; - p->slide_height = v[1]; - } - } -} - - -int load_presentation(struct presentation *p, GFile *file) -{ - int r = 0; - char *everything; - - assert(p->completely_empty); - - if ( !g_file_load_contents(file, NULL, &everything, NULL, NULL, NULL) ) { - fprintf(stderr, _("Failed to load '%s'\n"), g_file_get_uri(file)); - return 1; - } - - p->scblocks = sc_parse(everything); - g_free(everything); - - p->lang = pango_language_get_default(); - - if ( p->scblocks == NULL ) r = 1; - - if ( r ) { - p->completely_empty = 1; - fprintf(stderr, _("Parse error.\n")); - return r; /* Error */ - } - - p->stylesheet = NULL; - - find_and_load_stylesheet(p, file); - - set_slide_size_from_stylesheet(p); - - assert(p->file == NULL); - p->file = file; - g_object_ref(file); - - imagestore_set_parent(p->is, g_file_get_parent(file)); - - return 0; -} - diff --git a/src/presentation.h b/src/presentation.h deleted file mode 100644 index b288d8e..0000000 --- a/src/presentation.h +++ /dev/null @@ -1,83 +0,0 @@ -/* - * presentation.h - * - * Copyright © 2013-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 PRESENTATION_H -#define PRESENTATION_H - -#ifdef HAVE_CONFIG_H -#include -#endif - -struct presentation; - -#include "imagestore.h" -#include "sc_parse.h" -#include "slideshow.h" -#include "narrative_window.h" -#include "slide_window.h" -#include "stylesheet.h" - -struct menu_pl; - -struct presentation -{ - GFile *file; - GFile *stylesheet_from; - int completely_empty; - int saved; - PangoLanguage *lang; - - ImageStore *is; - - NarrativeWindow *narrative_window; - - struct pr_clock *clock; - - /* This is the "native" size of the slide. It only exists to give - * font size some meaning in the context of a somewhat arbitrary DPI */ - double slide_width; - double slide_height; - - SCBlock *scblocks; - Stylesheet *stylesheet; -}; - - -extern struct presentation *new_presentation(const char *imagestore); -extern void free_presentation(struct presentation *p); - -extern char *get_titlebar_string(struct presentation *p); - -extern int slide_number(struct presentation *p, SCBlock *sl); -extern int num_slides(struct presentation *p); -extern SCBlock *first_slide(struct presentation *p); -extern SCBlock *last_slide(struct presentation *p); -extern SCBlock *next_slide(struct presentation *p, SCBlock *sl); -extern SCBlock *prev_slide(struct presentation *p, SCBlock *sl); - -extern int load_presentation(struct presentation *p, GFile *file); -extern int save_presentation(struct presentation *p, GFile *file, GFile *ssfile); - -#define UNUSED __attribute__((unused)) - - -#endif /* PRESENTATION_H */ diff --git a/src/print.c b/src/print.c deleted file mode 100644 index 43c967e..0000000 --- a/src/print.c +++ /dev/null @@ -1,314 +0,0 @@ -/* - * print.c - * - * Copyright © 2016-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 "presentation.h" -#include "narrative_window.h" -#include "render.h" -#include "utils.h" - - -static GtkPrintSettings *print_settings = NULL; - -struct print_stuff -{ - struct presentation *p; - - /* Printing config */ - GtkWidget *combo; - int slides_only; - - /* When printing slides only */ - SCBlock *slide; - - /* When printing narrative */ - int nar_line; - struct frame *top; - int start_paras[256]; - int slide_number; -}; - - -static void print_widget_apply(GtkPrintOperation *op, GtkWidget *widget, - void *vp) -{ - const char *id; - struct print_stuff *ps = vp; - - id = gtk_combo_box_get_active_id(GTK_COMBO_BOX(ps->combo)); - if ( strcmp(id, "slides") == 0 ) { - ps->slides_only = 1; - } else { - ps->slides_only = 0; - } -} - - -static GObject *print_widget(GtkPrintOperation *op, void *vp) -{ - GtkWidget *vbox; - GtkWidget *cbox; - struct print_stuff *ps = vp; - - vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 10); - gtk_container_set_border_width(GTK_CONTAINER(vbox), 10); - - /* What do you want to print? */ - cbox = gtk_combo_box_text_new(); - gtk_combo_box_text_append(GTK_COMBO_BOX_TEXT(cbox), "slides", - _("Print the slides only")); - gtk_combo_box_text_append(GTK_COMBO_BOX_TEXT(cbox), "narrative", - _("Print the narrative")); - gtk_box_pack_start(GTK_BOX(vbox), cbox, FALSE, FALSE, 10); - gtk_combo_box_set_active(GTK_COMBO_BOX(cbox), 1); - ps->combo = cbox; - - gtk_widget_show_all(vbox); - return G_OBJECT(vbox); - -} - - -static void print_slide_only(GtkPrintOperation *op, GtkPrintContext *ctx, - struct print_stuff *ps, int page) -{ - cairo_t *cr; - PangoContext *pc; - double w, h; - struct frame *top; - const double sw = ps->p->slide_width; - const double sh = ps->p->slide_height; - double slide_width, slide_height; - - cr = gtk_print_context_get_cairo_context(ctx); - pc = gtk_print_context_create_pango_context(ctx); - w = gtk_print_context_get_width(ctx); - h = gtk_print_context_get_height(ctx); - - cairo_rectangle(cr, 0.0, 0.0, w, h); - cairo_set_source_rgb(cr, 1.0, 1.0, 1.0); - cairo_fill(cr); - - if ( sw/sh > w/h ) { - /* Slide is too wide. Letterboxing top/bottom */ - slide_width = w; - slide_height = w * sh/sw; - } else { - /* Letterboxing at sides */ - slide_width = h * sw/sh; - slide_height = h; - } - - printf("%f x %f ---> %f x %f\n", w, h, slide_width, slide_height); - - top = interp_and_shape(ps->slide, ps->p->stylesheet, NULL, - ps->p->is, page+1, pc, sw, sh, ps->p->lang); - - recursive_wrap(top, pc); - - cairo_scale(cr, slide_width/sw, slide_width/sw); - - recursive_draw(top, cr, ps->p->is, - 0.0, ps->p->slide_height); - - ps->slide = next_slide(ps->p, ps->slide); -} - - -static int print_create_thumbnail(SCInterpreter *scin, SCBlock *bl, - double *w, double *h, void **bvp, void *vp) -{ - struct print_stuff *ps = vp; - struct presentation *p = ps->p; - SCBlock *b; - - *w = 270.0*(p->slide_width / p->slide_height); - *h = 270.0; - b = bl; - - *bvp = b; - - return 1; -} - - -static cairo_surface_t *print_render_thumbnail(int w, int h, void *bvp, void *vp) -{ - struct print_stuff *ps = vp; - struct presentation *p = ps->p; - SCBlock *scblocks = bvp; - cairo_surface_t *surf; - struct frame *top; - - surf = render_sc(scblocks, w, h, p->slide_width, p->slide_height, - p->stylesheet, NULL, p->is, ps->slide_number++, - &top, p->lang); - frame_free(top); - - return surf; -} - - -static void begin_narrative_print(GtkPrintOperation *op, GtkPrintContext *ctx, - struct print_stuff *ps) -{ - SCCallbackList *cbl; - PangoContext *pc; - int i, n_pages; - double h, page_height; - SCBlock *dummy_top; - - cbl = sc_callback_list_new(); - ps->slide_number = 1; - sc_callback_list_add_callback(cbl, "slide", print_create_thumbnail, - print_render_thumbnail, NULL, ps); - - pc = gtk_print_context_create_pango_context(ctx); - - dummy_top = sc_block_new_parent(ps->p->scblocks, "presentation"); - ps->top = interp_and_shape(dummy_top, ps->p->stylesheet, cbl, - ps->p->is, 0, pc, - gtk_print_context_get_width(ctx), - gtk_print_context_get_height(ctx), - ps->p->lang); - recursive_wrap(ps->top, pc); - - /* Count pages */ - page_height = gtk_print_context_get_height(ctx); - h = 0.0; - n_pages = 1; - ps->start_paras[0] = 0; - for ( i=0; itop->n_paras; i++ ) { - if ( h + paragraph_height(ps->top->paras[i]) > page_height ) { - /* Paragraph does not fit on page */ - ps->start_paras[n_pages] = i; - n_pages++; - h = 0.0; - } - h += paragraph_height(ps->top->paras[i]); - } - gtk_print_operation_set_n_pages(op, n_pages); - g_object_unref(pc); -} - - -static void print_narrative(GtkPrintOperation *op, GtkPrintContext *ctx, - struct print_stuff *ps, gint page) -{ - int i; - double h, page_height; - cairo_t *cr; - - page_height = gtk_print_context_get_height(ctx); - cr = gtk_print_context_get_cairo_context(ctx); - - h = 0.0; - for ( i=ps->start_paras[page]; itop->n_paras; i++ ) { - - /* Will this paragraph fit? */ - h += paragraph_height(ps->top->paras[i]); - if ( h > page_height ) return; - - cairo_save(cr); - render_paragraph(cr, ps->top->paras[i], ps->p->is); - cairo_restore(cr); - - cairo_translate(cr, 0.0, paragraph_height(ps->top->paras[i])); - - } - - -} - - - -static void print_begin(GtkPrintOperation *op, GtkPrintContext *ctx, void *vp) -{ - struct print_stuff *ps = vp; - - if ( ps->slides_only ) { - gtk_print_operation_set_n_pages(op, num_slides(ps->p)); - ps->slide = first_slide(ps->p); - } else { - begin_narrative_print(op, ctx, ps); - } -} - - -static void print_draw(GtkPrintOperation *op, GtkPrintContext *ctx, gint page, - void *vp) -{ - struct print_stuff *ps = vp; - if ( ps->slides_only ) { - print_slide_only(op, ctx, ps, page); - } else { - print_narrative(op, ctx, ps, page); - } -} - - -void run_printing(struct presentation *p, GtkWidget *parent) -{ - GtkPrintOperation *print; - GtkPrintOperationResult res; - struct print_stuff *ps; - - ps = malloc(sizeof(struct print_stuff)); - if ( ps == NULL ) return; - ps->p = p; - ps->nar_line = 0; - - print = gtk_print_operation_new(); - if ( print_settings != NULL ) { - gtk_print_operation_set_print_settings(print, print_settings); - } - - g_signal_connect(print, "create-custom-widget", - G_CALLBACK(print_widget), ps); - g_signal_connect(print, "custom-widget-apply", - G_CALLBACK(print_widget_apply), ps); - g_signal_connect(print, "begin_print", G_CALLBACK(print_begin), ps); - g_signal_connect(print, "draw_page", G_CALLBACK(print_draw), ps); - - res = gtk_print_operation_run(print, - GTK_PRINT_OPERATION_ACTION_PRINT_DIALOG, - GTK_WINDOW(parent), NULL); - - if ( res == GTK_PRINT_OPERATION_RESULT_APPLY ) { - if ( print_settings != NULL ) { - g_object_unref(print_settings); - } - print_settings = g_object_ref( - gtk_print_operation_get_print_settings(print)); - } - g_object_unref(print); -} - diff --git a/src/print.h b/src/print.h deleted file mode 100644 index 265b7c1..0000000 --- a/src/print.h +++ /dev/null @@ -1,32 +0,0 @@ -/* - * print.h - * - * Copyright © 2016-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 PRINT_H -#define PRINT_H - -#ifdef HAVE_CONFIG_H -#include -#endif - -extern void run_printing(struct presentation *p, GtkWidget *parent); - -#endif /* PRINT_H */ diff --git a/src/render.c b/src/render.c deleted file mode 100644 index 6ac09fc..0000000 --- a/src/render.c +++ /dev/null @@ -1,332 +0,0 @@ -/* - * render.c - * - * Copyright © 2013-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 -#include -#include -#include -#include - -#include "sc_parse.h" -#include "sc_interp.h" -#include "presentation.h" -#include "frame.h" -#include "render.h" -#include "imagestore.h" -#include "utils.h" - - -static void do_background(cairo_t *cr, struct frame *fr) -{ - cairo_pattern_t *patt = NULL; - - cairo_new_path(cr); - cairo_rectangle(cr, 0.0, 0.0, fr->w, fr->h); - - switch ( fr->grad ) { - - case GRAD_NONE: - cairo_set_source_rgba(cr, fr->bgcol[0], - fr->bgcol[1], - fr->bgcol[2], - fr->bgcol[3]); - break; - - case GRAD_VERT: - patt = cairo_pattern_create_linear(0.0, 0.0, - 0.0, fr->h); - cairo_pattern_add_color_stop_rgba(patt, 0.0, fr->bgcol[0], - fr->bgcol[1], - fr->bgcol[2], - fr->bgcol[3]); - cairo_pattern_add_color_stop_rgba(patt, 1.0, fr->bgcol2[0], - fr->bgcol2[1], - fr->bgcol2[2], - fr->bgcol2[3]); - cairo_set_source(cr, patt); - break; - - case GRAD_HORIZ: - patt = cairo_pattern_create_linear(0.0, 0.0, - fr->w, 0.0); - cairo_pattern_add_color_stop_rgba(patt, 0.0, fr->bgcol[0], - fr->bgcol[1], - fr->bgcol[2], - fr->bgcol[3]); - cairo_pattern_add_color_stop_rgba(patt, 1.0, fr->bgcol2[0], - fr->bgcol2[1], - fr->bgcol2[2], - fr->bgcol2[3]); - cairo_set_source(cr, patt); - break; - - } - - cairo_fill(cr); - if ( patt != NULL ) cairo_pattern_destroy(patt); -} - - -static int draw_frame(cairo_t *cr, struct frame *fr, ImageStore *is, - double min_y, double max_y) -{ - int i; - double hpos = 0.0; - - cairo_save(cr); - do_background(cr, fr); - - /* Actually render the contents */ - cairo_translate(cr, fr->pad_l, fr->pad_t); - for ( i=0; in_paras; i++ ) { - - double cur_h = paragraph_height(fr->paras[i]); - - cairo_save(cr); - cairo_translate(cr, 0.0, hpos); - - if ( (hpos + cur_h > min_y) && (hpos < max_y) ) { - render_paragraph(cr, fr->paras[i], is); - } /* else paragraph is not visible */ - - hpos += cur_h; - cairo_restore(cr); - - } - cairo_restore(cr); - - return 0; -} - - -int recursive_draw(struct frame *fr, cairo_t *cr, - ImageStore *is, - double min_y, double max_y) -{ - int i; - - draw_frame(cr, fr, is, min_y, max_y); - - for ( i=0; inum_children; i++ ) { - cairo_save(cr); - cairo_translate(cr, fr->children[i]->x, fr->children[i]->y); - recursive_draw(fr->children[i], cr, is, - min_y - fr->children[i]->y, - max_y - fr->children[i]->y); - cairo_restore(cr); - } - - return 0; -} - - -void wrap_frame(struct frame *fr, PangoContext *pc) -{ - int i; - double w; - - w = fr->w - fr->pad_l - fr->pad_r; - - for ( i=0; in_paras; i++ ) { - wrap_paragraph(fr->paras[i], pc, w, 0, 0); - } -} - - -int recursive_wrap(struct frame *fr, PangoContext *pc) -{ - int i; - - wrap_frame(fr, pc); - - for ( i=0; inum_children; i++ ) { - recursive_wrap(fr->children[i], pc); - } - - return 0; -} - - -struct frame *interp_and_shape(SCBlock *scblocks, Stylesheet *stylesheet, - SCCallbackList *cbl, ImageStore *is, - int slide_number, - PangoContext *pc, double w, double h, - PangoLanguage *lang) -{ - SCInterpreter *scin; - char snum[64]; - struct frame *top; - - top = frame_new(); - top->resizable = 0; - top->x = 0.0; - top->y = 0.0; - top->w = w; - top->h = h; - top->scblocks = scblocks; - - scin = sc_interp_new(pc, lang, is, top); - if ( scin == NULL ) { - fprintf(stderr, "Failed to set up interpreter.\n"); - frame_free(top); - return NULL; - } - - sc_interp_set_callbacks(scin, cbl); - - snprintf(snum, 63, "%i", slide_number); - sc_interp_set_constant(scin, SCCONST_SLIDENUMBER, snum); - - top->fontdesc = pango_font_description_copy(sc_interp_get_fontdesc(scin)); - top->col[0] = sc_interp_get_fgcol(scin)[0]; - top->col[1] = sc_interp_get_fgcol(scin)[1]; - top->col[2] = sc_interp_get_fgcol(scin)[2]; - top->col[3] = sc_interp_get_fgcol(scin)[3]; - - sc_interp_add_block(scin, scblocks, stylesheet); - - sc_interp_destroy(scin); - - return top; -} - - -static struct frame *render_sc_with_context(SCBlock *scblocks, - cairo_t *cr, double log_w, double log_h, - Stylesheet *stylesheet, SCCallbackList *cbl, - ImageStore *is, - int slide_number, PangoLanguage *lang, - PangoContext *pc) -{ - struct frame *top; - - cairo_rectangle(cr, 0.0, 0.0, log_w, log_h); - cairo_set_source_rgb(cr, 1.0, 1.0, 1.0); - cairo_fill(cr); - - top = interp_and_shape(scblocks, stylesheet, cbl, is, - slide_number, pc, log_w, log_h, lang); - - recursive_wrap(top, pc); - - recursive_draw(top, cr, is, 0.0, log_h); - - return top; -} - - -cairo_surface_t *render_sc(SCBlock *scblocks, int w, int h, - double log_w, double log_h, - Stylesheet *stylesheet, SCCallbackList *cbl, - ImageStore *is, - int slide_number, struct frame **ptop, - PangoLanguage *lang) -{ - cairo_surface_t *surf; - cairo_t *cr; - struct frame *top; - PangoContext *pc; - - surf = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, w, h); - cr = cairo_create(surf); - pc = pango_cairo_create_context(cr); - cairo_scale(cr, w/log_w, h/log_h); - top = render_sc_with_context(scblocks, cr, log_w, log_h, - stylesheet, cbl, is, slide_number, - lang, pc); - g_object_unref(pc); - cairo_destroy(cr); - - *ptop = top; - - return surf; -} - - -int export_pdf(struct presentation *p, const char *filename) -{ - double r; - double w = 2048.0; - double scale; - cairo_surface_t *surf; - cairo_t *cr; - SCBlock *bl; - int i; - PangoContext *pc; - - r = p->slide_height / p->slide_width; - - surf = cairo_pdf_surface_create(filename, w, w*r); - if ( cairo_surface_status(surf) != CAIRO_STATUS_SUCCESS ) { - fprintf(stderr, _("Couldn't create Cairo surface\n")); - return 1; - } - - cr = cairo_create(surf); - scale = w / p->slide_width; - pc = pango_cairo_create_context(cr); - - i = 1; - bl = p->scblocks; - while ( bl != NULL ) { - - if ( safe_strcmp(sc_block_name(bl), "slide") != 0 ) { - bl = sc_block_next(bl); - continue; - } - - cairo_save(cr); - - cairo_scale(cr, scale, scale); - - cairo_rectangle(cr, 0.0, 0.0, p->slide_width, p->slide_height); - cairo_set_source_rgb(cr, 1.0, 1.0, 1.0); - cairo_fill(cr); - - render_sc_with_context(bl, cr, p->slide_width, - p->slide_height, p->stylesheet, NULL, - p->is, i, p->lang, pc); - - cairo_restore(cr); - - cairo_show_page(cr); - - bl = sc_block_next(bl); - i++; - - } - - g_object_unref(pc); - cairo_surface_finish(surf); - cairo_destroy(cr); - - return 0; -} diff --git a/src/render.h b/src/render.h deleted file mode 100644 index 0cfae26..0000000 --- a/src/render.h +++ /dev/null @@ -1,62 +0,0 @@ -/* - * render.h - * - * Copyright © 2013-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 RENDER_H -#define RENDER_H - -#ifdef HAVE_CONFIG_H -#include -#endif - -#include "presentation.h" -#include "imagestore.h" -#include "sc_interp.h" -#include "frame.h" - -/* Convienience function to run the entire pipeline */ -extern cairo_surface_t *render_sc(SCBlock *scblocks, int w, int h, - double log_w, double log_h, - Stylesheet *stylesheet, SCCallbackList *cbl, - ImageStore *is, - int slide_number, struct frame **ptop, - PangoLanguage *lang); - -/* Interpret StoryCode and measure boxes. - * Needs to be followed by: wrap_contents() (recursively) - * recursive_draw() - */ -extern struct frame *interp_and_shape(SCBlock *scblocks, Stylesheet *stylesheet, - SCCallbackList *cbl, - ImageStore *is, - int slide_number, PangoContext *pc, - double w, double h, PangoLanguage *lang); - -extern void wrap_frame(struct frame *fr, PangoContext *pc); -extern int recursive_wrap(struct frame *fr, PangoContext *pc); - -extern int export_pdf(struct presentation *p, const char *filename); - -extern int recursive_draw(struct frame *fr, cairo_t *cr, - ImageStore *is, - double min_y, double max_y); - -#endif /* RENDER_H */ diff --git a/src/sc2_test.c b/src/sc2_test.c deleted file mode 100644 index 1dc4099..0000000 --- a/src/sc2_test.c +++ /dev/null @@ -1,50 +0,0 @@ -/* - * sc2_test.c - * - * Copyright © 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 . - * - */ - - -#include -#include -#include -#include -#include - -#include "storycode.h" -#include "presentation.h" - -//int scdebug = 1; - -int main(int argc, char *argv[]) -{ - GFile *file; - GBytes *bytes; - const char *text; - size_t len; - Presentation *p; - - file = g_file_new_for_uri("resource:///uk/me/bitwiz/Colloquium/demo.sc"); - bytes = g_file_load_bytes(file, NULL, NULL, NULL); - text = g_bytes_get_data(bytes, &len); - p = storycode_parse_presentation(text); - g_bytes_unref(bytes); - - return 0; -} diff --git a/src/sc_editor.c b/src/sc_editor.c deleted file mode 100644 index 8e79fd8..0000000 --- a/src/sc_editor.c +++ /dev/null @@ -1,2197 +0,0 @@ -/* - * sc_editor.c - * - * Copyright © 2013-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 -#include -#include - -#include "colloquium.h" -#include "presentation.h" -#include "slide_window.h" -#include "render.h" -#include "frame.h" -#include "sc_parse.h" -#include "sc_interp.h" -#include "sc_editor.h" -#include "slideshow.h" -#include "debugger.h" -#include "utils.h" - - -static void scroll_interface_init(GtkScrollable *iface) -{ -} - - -enum -{ - SCEDITOR_0, - SCEDITOR_VADJ, - SCEDITOR_HADJ, - SCEDITOR_VPOL, - SCEDITOR_HPOL, -}; - - -G_DEFINE_TYPE_WITH_CODE(SCEditor, sc_editor, GTK_TYPE_DRAWING_AREA, - G_IMPLEMENT_INTERFACE(GTK_TYPE_SCROLLABLE, - scroll_interface_init)) - -static void debug_paragraphs(SCEditor *e) -{ - struct frame *fr = e->cursor_frame; - int i; - - printf("Paragraphs in current frame:\n"); - for ( i=0; in_paras; i++ ) { - show_para(fr->paras[i]); - } -} - - -static void horizontal_adjust(GtkAdjustment *adj, SCEditor *e) -{ - e->h_scroll_pos = gtk_adjustment_get_value(adj); - sc_editor_redraw(e); -} - - -static void set_horizontal_params(SCEditor *e) -{ - if ( e->hadj == NULL ) return; - gtk_adjustment_configure(e->hadj, e->h_scroll_pos, 0, e->w, 100, - e->visible_width, e->visible_width); -} - - -static void vertical_adjust(GtkAdjustment *adj, SCEditor *e) -{ - e->scroll_pos = gtk_adjustment_get_value(adj); - sc_editor_redraw(e); -} - - -static void set_vertical_params(SCEditor *e) -{ - double page; - - if ( e->vadj == NULL ) return; - - /* Ensure we do not scroll off the top of the document */ - if ( e->scroll_pos < 0.0 ) e->scroll_pos = 0.0; - - /* Ensure we do not scroll off the bottom of the document */ - if ( e->scroll_pos > e->h - e->visible_height ) { - e->scroll_pos = e->h - e->visible_height; - } - - /* If we can show the whole document, show it at the top */ - if ( e->h < e->visible_height ) { - e->scroll_pos = 0.0; - } - - if ( e->h > e->visible_height ) { - page = e->visible_height; - } else { - page = e->h; - } - - gtk_adjustment_configure(e->vadj, e->scroll_pos, 0, e->h, 100, - e->visible_height, page); -} - - -static void update_size(SCEditor *e) -{ - if ( e->flow ) { - - double total = total_height(e->top); - - e->w = e->top->w; - e->h = total + e->top->pad_t + e->top->pad_b; - - e->log_w = e->w; - e->log_h = e->h; - e->top->h = e->h; - } else { - e->top->w = e->log_w; - e->top->h = e->log_h; - } - - if ( e->flow && (e->top->h < e->visible_height) ) { - e->top->h = e->visible_height; - } - - set_vertical_params(e); - set_horizontal_params(e); -} - - -static gboolean resize_sig(GtkWidget *widget, GdkEventConfigure *event, - SCEditor *e) -{ - PangoContext *pc; - - pc = gdk_pango_context_get(); - - if ( e->scale ) { - - double sx, sy; - double aw, ah; - - e->w = event->width; - e->h = event->height; - sx = (double)e->w / e->log_w; - sy = (double)e->h / e->log_h; - e->view_scale = (sx < sy) ? sx : sy; - - /* Actual size (in device units) */ - aw = e->view_scale * e->log_w; - ah = e->view_scale * e->log_h; - - e->border_offs_x = (event->width - aw)/2.0; - e->border_offs_y = (event->height - ah)/2.0; - - } - - e->visible_height = event->height; - e->visible_width = event->width; - - /* Interpret and shape, if not already done */ - if ( e->top == NULL ) { - double w, h; - if ( e->flow ) { - w = event->width; - h = 0.0; - } else { - w = e->log_w; - h = e->log_h; - } - e->top = interp_and_shape(e->scblocks, e->stylesheet, e->cbl, - e->is, e->slidenum, pc, - w, h, e->lang); - e->top->scblocks = e->scblocks; - recursive_wrap(e->top, pc); - } - - if ( e->flow ) { - /* Wrap using current width */ - e->top->w = event->width; - e->top->h = 0.0; /* To be updated in a moment */ - e->top->x = 0.0; - e->top->y = 0.0; - /* Only the top level needs to be wrapped */ - wrap_frame(e->top, pc); - } - - update_size(e); - - g_object_unref(pc); - - return FALSE; -} - - -static void emit_change_sig(SCEditor *e) -{ - g_signal_emit_by_name(e, "changed"); -} - - -void sc_editor_set_flow(SCEditor *e, int flow) -{ - e->flow = flow; -} - - -static void sc_editor_set_property(GObject *obj, guint id, const GValue *val, - GParamSpec *spec) -{ - SCEditor *e = SC_EDITOR(obj); - - switch ( id ) { - - case SCEDITOR_VPOL : - e->vpol = g_value_get_enum(val); - break; - - case SCEDITOR_HPOL : - e->hpol = g_value_get_enum(val); - break; - - case SCEDITOR_VADJ : - e->vadj = g_value_get_object(val); - set_vertical_params(e); - if ( e->vadj != NULL ) { - g_signal_connect(G_OBJECT(e->vadj), "value-changed", - G_CALLBACK(vertical_adjust), e); - } - break; - - case SCEDITOR_HADJ : - e->hadj = g_value_get_object(val); - set_horizontal_params(e); - if ( e->hadj != NULL ) { - g_signal_connect(G_OBJECT(e->hadj), "value-changed", - G_CALLBACK(horizontal_adjust), e); - } - break; - - default : - printf("setting %i\n", id); - break; - - } -} - - -static void sc_editor_get_property(GObject *obj, guint id, GValue *val, - GParamSpec *spec) -{ - SCEditor *e = SC_EDITOR(obj); - - switch ( id ) { - - case SCEDITOR_VADJ : - g_value_set_object(val, e->vadj); - break; - - case SCEDITOR_HADJ : - g_value_set_object(val, e->hadj); - break; - - case SCEDITOR_VPOL : - g_value_set_enum(val, e->vpol); - break; - - case SCEDITOR_HPOL : - g_value_set_enum(val, e->hpol); - break; - - default : - G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, id, spec); - break; - - } -} - - -static GtkSizeRequestMode get_request_mode(GtkWidget *widget) -{ - return GTK_SIZE_REQUEST_CONSTANT_SIZE; -} - - -static void get_preferred_width(GtkWidget *widget, gint *min, gint *natural) -{ - SCEditor *e = SC_EDITOR(widget); - if ( e->flow ) { - *min = 100; - *natural = 640; - } else { - *min = e->w; - *natural = e->w; - } -} - - -static void get_preferred_height(GtkWidget *widget, gint *min, gint *natural) -{ - SCEditor *e = SC_EDITOR(widget); - if ( e->flow ) { - *min = 1000; - *natural = 1000; - } else { - *min = e->h; - *natural = e->h; - } -} - - -static void sc_editor_class_init(SCEditorClass *klass) -{ - GObjectClass *goc = G_OBJECT_CLASS(klass); - goc->set_property = sc_editor_set_property; - goc->get_property = sc_editor_get_property; - g_object_class_override_property(goc, SCEDITOR_VADJ, "vadjustment"); - g_object_class_override_property(goc, SCEDITOR_HADJ, "hadjustment"); - g_object_class_override_property(goc, SCEDITOR_VPOL, "vscroll-policy"); - g_object_class_override_property(goc, SCEDITOR_HPOL, "hscroll-policy"); - - GTK_WIDGET_CLASS(klass)->get_request_mode = get_request_mode; - GTK_WIDGET_CLASS(klass)->get_preferred_width = get_preferred_width; - GTK_WIDGET_CLASS(klass)->get_preferred_height = get_preferred_height; - GTK_WIDGET_CLASS(klass)->get_preferred_height_for_width = NULL; - - g_signal_new("changed", SC_TYPE_EDITOR, G_SIGNAL_RUN_LAST, 0, - NULL, NULL, NULL, G_TYPE_NONE, 0); -} - - -static void sc_editor_init(SCEditor *e) -{ - e->vpol = GTK_SCROLL_NATURAL; - e->hpol = GTK_SCROLL_NATURAL; - e->vadj = gtk_adjustment_new(0, 0, 100, 1, 10, 10); - e->hadj = gtk_adjustment_new(0, 0, 100, 1, 10, 10); -} - - -void sc_editor_set_background(SCEditor *e, double r, double g, double b) -{ - e->bgcol[0] = r; - e->bgcol[1] = g; - e->bgcol[2] = b; -} - - -void sc_editor_ensure_cursor(SCEditor *e) -{ - if ( e->cursor_frame != NULL ) return; - e->cursor_frame = e->top; - e->cpos.para = 0; - e->cpos.pos = 0; - e->cpos.trail = 0; - e->selection = NULL; -} - - -static void sc_editor_remove_cursor(SCEditor *e) -{ - e->cursor_frame = NULL; - e->cpos.para = 0; - e->cpos.pos = 0; - e->cpos.trail = 0; - e->selection = NULL; -} - - -/* (Re-)run the entire rendering pipeline. - * NB "full" means "full". All frame, line and box handles will become - * invalid. The cursor position will be unset. */ -static void full_rerender(SCEditor *e) -{ - PangoContext *pc; - - frame_free(e->top); - sc_editor_remove_cursor(e); - - pc = gdk_pango_context_get(); - - e->top = interp_and_shape(e->scblocks, e->stylesheet, e->cbl, - e->is, e->slidenum, - pc, e->log_w, 0.0, e->lang); - - e->top->x = 0.0; - e->top->y = 0.0; - e->top->w = e->w; - e->top->h = 0.0; /* To be updated in a moment */ - - recursive_wrap(e->top, pc); - update_size(e); - - sc_editor_redraw(e); - - g_object_unref(pc); -} - - -void sc_editor_redraw(SCEditor *e) -{ - gint w, h; - - w = gtk_widget_get_allocated_width(GTK_WIDGET(e)); - h = gtk_widget_get_allocated_height(GTK_WIDGET(e)); - - gtk_widget_queue_draw_area(GTK_WIDGET(e), 0, 0, w, h); -} - - -static void paste_storycode_received(GtkClipboard *cb, GtkSelectionData *seldata, - gpointer vp) -{ - SCEditor *e = vp; - SCBlock *nf; - const guchar *t; - - t = gtk_selection_data_get_data(seldata); - - printf("received storycode paste\n"); - printf("'%s'\n", t); - if ( t == NULL ) return; - - /* FIXME: It might not be a new frame */ - nf = sc_parse((char *)t); - show_sc_blocks(nf); - sc_block_append_block(sc_block_child(e->scblocks), nf); - full_rerender(e); -} - - -static void paste_text_received(GtkClipboard *cb, GtkSelectionData *seldata, - gpointer vp) -{ - SCEditor *e = vp; - SCBlock *bl; - guchar *t; - SCBlock *cur_bl; - size_t cur_sc_pos; - size_t offs; - Paragraph *para; - - t = gtk_selection_data_get_text(seldata); - - printf("received text paste\n"); - printf("'%s'\n", t); - if ( t == NULL ) return; - - bl = sc_parse((char *)t); - - if ( e->cursor_frame == NULL ) { - fprintf(stderr, _("No frame selected for paste\n")); - return; - } - - para = e->cursor_frame->paras[e->cpos.para]; - offs = pos_trail_to_offset(para, e->cpos.pos, e->cpos.trail); - - get_sc_pos(e->cursor_frame, e->cpos.para, offs, &cur_bl, &cur_sc_pos); - sc_insert_block(cur_bl, cur_sc_pos, bl); - full_rerender(e); -} - - -static void paste_targets_received(GtkClipboard *cb, GdkAtom *targets, - gint n_targets, gpointer vp) -{ - SCEditor *e = vp; - int i; - int have_sc = 0; - int index_sc, index_text; - int have_text = 0; - - if ( targets == NULL ) { - fprintf(stderr, "No paste targets offered.\n"); - return; - } - - for ( i=0; iscblocks), nf); - full_rerender(e); -} - - -static void clipboard_get(GtkClipboard *cb, GtkSelectionData *seldata, - guint info, gpointer data) -{ - char *t = data; - - printf("clipboard get\n"); - - if ( info == 0 ) { - printf("sending SC frame\n"); - gtk_selection_data_set(seldata, - gtk_selection_data_get_target(seldata), - 8, (const guchar *)t, strlen(t)+1); - } else { - GdkAtom target; - gchar *name; - target = gtk_selection_data_get_target(seldata); - name = gdk_atom_name(target); - fprintf(stderr, "Don't know what to send for %s\n", name); - g_free(name); - } -} - - -static void clipboard_clear(GtkClipboard *cb, gpointer data) -{ - free(data); -} - - -void sc_editor_copy_selected_frame(SCEditor *e) -{ - char *t; - GtkClipboard *cb; - GdkAtom atom; - GtkTargetEntry targets[1]; - - if ( e->selection == NULL ) return; - - atom = gdk_atom_intern("CLIPBOARD", FALSE); - if ( atom == GDK_NONE ) return; - - cb = gtk_clipboard_get(atom); - - targets[0].target = "text/x-storycode"; - targets[0].flags = 0; - targets[0].info = 0; - - /* FIXME: Offer image, PDF etc? */ - - printf("copying frame\n"); - - t = serialise_sc_block(e->selection->scblocks); - - gtk_clipboard_set_with_data(cb, targets, 1, - clipboard_get, clipboard_clear, t); -} - - -static void copy_selection(SCEditor *e) -{ - char *t; - GtkClipboard *cb; - GdkAtom atom; - GtkTargetEntry targets[1]; - SCBlock *bl; - - if ( e->selection == NULL ) return; - - atom = gdk_atom_intern("CLIPBOARD", FALSE); - if ( atom == GDK_NONE ) return; - - cb = gtk_clipboard_get(atom); - - - targets[0].target = "text/x-storycode"; - targets[0].flags = 0; - targets[0].info = 0; - - printf("copying selection\n"); - - bl = block_at_cursor(e->cursor_frame, e->cpos.para, 0); - if ( bl == NULL ) return; - - t = serialise_sc_block(bl); - - gtk_clipboard_set_with_data(cb, targets, 1, - clipboard_get, clipboard_clear, t); -} - - -void sc_editor_delete_selected_frame(SCEditor *e) -{ - SCBlock *scb_old = e->scblocks; - sc_block_delete(&e->scblocks, e->selection->scblocks); - assert(scb_old == e->scblocks); - full_rerender(e); - emit_change_sig(e); -} - - -static gint destroy_sig(GtkWidget *window, SCEditor *e) -{ - return 0; -} - - -static void draw_editing_box(cairo_t *cr, struct frame *fr) -{ - const double dash[] = {2.0, 2.0}; - double xmin, ymin, width, height; - double ptot_w, ptot_h; - - xmin = fr->x; - ymin = fr->y; - width = fr->w; - height = fr->h; - - cairo_new_path(cr); - cairo_rectangle(cr, xmin, ymin, width, height); - cairo_set_source_rgb(cr, 0.0, 0.69, 1.0); - cairo_set_line_width(cr, 0.5); - cairo_stroke(cr); - - cairo_new_path(cr); - ptot_w = fr->pad_l + fr->pad_r; - ptot_h = fr->pad_t + fr->pad_b; - cairo_rectangle(cr, xmin+fr->pad_l, ymin+fr->pad_t, - width-ptot_w, height-ptot_h); - cairo_set_dash(cr, dash, 2, 0.0); - cairo_set_source_rgb(cr, 0.0, 0.0, 0.0); - cairo_set_line_width(cr, 0.1); - cairo_stroke(cr); - - cairo_set_dash(cr, NULL, 0, 0.0); -} - - -static void draw_para_highlight(cairo_t *cr, struct frame *fr, int cursor_para) -{ - double cx, cy, w, h; - - if ( get_para_highlight(fr, cursor_para, &cx, &cy, &w, &h) != 0 ) { - return; - } - - cairo_new_path(cr); - cairo_rectangle(cr, cx+fr->x, cy+fr->y, w, h); - cairo_set_source_rgba(cr, 0.7, 0.7, 1.0, 0.5); - cairo_set_line_width(cr, 5.0); - cairo_stroke(cr); -} - - -static void draw_caret(cairo_t *cr, struct frame *fr, struct edit_pos cpos, - int hgh) -{ - double cx, clow, chigh, h; - const double t = 1.8; - size_t offs; - Paragraph *para; - - if ( hgh ) { - draw_para_highlight(cr, fr, cpos.para); - return; - } - - assert(fr != NULL); - - para = fr->paras[cpos.para]; - if ( para_type(para) != PARA_TYPE_TEXT ) { - draw_para_highlight(cr, fr, cpos.para); - return; - } - - offs = pos_trail_to_offset(para, cpos.pos, cpos.trail); - get_cursor_pos(fr, cpos.para, offs, &cx, &clow, &h); - - cx += fr->x; - clow += fr->y; - chigh = clow + h; - - cairo_move_to(cr, cx, clow); - cairo_line_to(cr, cx, chigh); - - cairo_move_to(cr, cx-t, clow-t); - cairo_line_to(cr, cx, clow); - cairo_move_to(cr, cx+t, clow-t); - cairo_line_to(cr, cx, clow); - - cairo_move_to(cr, cx-t, chigh+t); - cairo_line_to(cr, cx, chigh); - cairo_move_to(cr, cx+t, chigh+t); - cairo_line_to(cr, cx, chigh); - - cairo_set_source_rgb(cr, 0.86, 0.0, 0.0); - cairo_set_line_width(cr, 1.0); - cairo_stroke(cr); -} - - -static void draw_resize_handle(cairo_t *cr, double x, double y) -{ - cairo_new_path(cr); - cairo_rectangle(cr, x, y, 20.0, 20.0); - cairo_set_source_rgba(cr, 0.9, 0.9, 0.9, 0.5); - cairo_fill(cr); -} - - -static void draw_overlay(cairo_t *cr, SCEditor *e) -{ - if ( e->selection != NULL ) { - - double x, y, w, h; - - draw_editing_box(cr, e->selection); - - x = e->selection->x; - y = e->selection->y; - w = e->selection->w; - h = e->selection->h; - - if ( e->selection->resizable ) { - /* Draw resize handles */ - draw_resize_handle(cr, x, y+h-20.0); - draw_resize_handle(cr, x+w-20.0, y); - draw_resize_handle(cr, x, y); - draw_resize_handle(cr, x+w-20.0, y+h-20.0); - } - - draw_caret(cr, e->cursor_frame, e->cpos, e->para_highlight); - - } - - if ( (e->drag_status == DRAG_STATUS_DRAGGING) - && ((e->drag_reason == DRAG_REASON_CREATE) - || (e->drag_reason == DRAG_REASON_IMPORT)) ) - { - cairo_new_path(cr); - cairo_rectangle(cr, e->start_corner_x, e->start_corner_y, - e->drag_corner_x - e->start_corner_x, - e->drag_corner_y - e->start_corner_y); - cairo_set_source_rgb(cr, 0.5, 0.5, 0.5); - cairo_set_line_width(cr, 0.5); - cairo_stroke(cr); - } - - if ( (e->drag_status == DRAG_STATUS_DRAGGING) - && ((e->drag_reason == DRAG_REASON_RESIZE) - || (e->drag_reason == DRAG_REASON_MOVE)) ) - { - cairo_new_path(cr); - cairo_rectangle(cr, e->box_x, e->box_y, - e->box_width, e->box_height); - cairo_set_source_rgb(cr, 0.5, 0.5, 0.5); - cairo_set_line_width(cr, 0.5); - cairo_stroke(cr); - } -} - - -static gboolean draw_sig(GtkWidget *da, cairo_t *cr, SCEditor *e) -{ - /* Ultimate background */ - if ( e->bg_pixbuf != NULL ) { - gdk_cairo_set_source_pixbuf(cr, e->bg_pixbuf, 0.0, 0.0); - cairo_pattern_t *patt = cairo_get_source(cr); - cairo_pattern_set_extend(patt, CAIRO_EXTEND_REPEAT); - cairo_paint(cr); - } else { - cairo_set_source_rgba(cr, 0.8, 0.8, 1.0, 1.0); - cairo_paint(cr); - } - - cairo_translate(cr, e->border_offs_x, e->border_offs_y); - cairo_translate(cr, -e->h_scroll_pos, -e->scroll_pos); - cairo_scale(cr, e->view_scale, e->view_scale); - - /* Rendering background */ - cairo_set_source_rgba(cr, 1.0, 1.0, 1.0, 1.0); - cairo_rectangle(cr, 0.0, 0.0, e->log_w, e->log_h); - cairo_fill(cr); - - /* Contents */ - recursive_draw(e->top, cr, e->is, - e->scroll_pos/e->view_scale, - (e->scroll_pos + e->visible_height)/e->view_scale); - - /* Editing overlay */ - draw_overlay(cr, e); - - return FALSE; -} - - -SCBlock *split_paragraph_at_cursor(SCEditor *e) -{ - size_t offs; - Paragraph *para; - - if ( e->cursor_frame == NULL ) return NULL; - - para = e->cursor_frame->paras[e->cpos.para]; - offs = pos_trail_to_offset(para, e->cpos.pos, e->cpos.trail); - return split_paragraph(e->cursor_frame, e->cpos.para, offs, e->pc); -} - - -static void check_cursor_visible(SCEditor *e) -{ - double x, y, h; - size_t offs; - Paragraph *para; - - if ( e->cursor_frame == NULL ) return; - - para = e->cursor_frame->paras[e->cpos.para]; - offs = pos_trail_to_offset(para, e->cpos.pos, e->cpos.trail); - get_cursor_pos(e->cursor_frame, e->cpos.para, offs, &x, &y, &h); - - /* Off the bottom? */ - if ( y - e->scroll_pos + h > e->visible_height ) { - e->scroll_pos = y + h - e->visible_height; - e->scroll_pos += e->cursor_frame->pad_b; - } - - /* Off the top? */ - if ( y < e->scroll_pos ) { - e->scroll_pos = y - e->cursor_frame->pad_t; - } -} - - -static void do_backspace(struct frame *fr, SCEditor *e) -{ - double wrapw = e->cursor_frame->w - e->cursor_frame->pad_l - e->cursor_frame->pad_r; - - if ( e->sel_active ) { - - /* Delete the selected block */ - delete_text_from_frame(e->cursor_frame, e->sel_start, e->sel_end, wrapw); - - /* Cursor goes at start of deletion */ - sort_positions(&e->sel_start, &e->sel_end); - e->cpos = e->sel_start; - e->sel_active = 0; - - } else { - - if ( para_type(e->cursor_frame->paras[e->cpos.para]) == PARA_TYPE_TEXT ) { - - /* Delete one character */ - struct edit_pos p1, p2; - - p1 = e->cpos; - - p2 = p1; - - cursor_moveh(e->cursor_frame, &p2, -1); - show_edit_pos(p1); - show_edit_pos(p2); - - delete_text_from_frame(e->cursor_frame, p1, p2, wrapw); - e->cpos = p2; - - } else { - - /* FIXME: Implement this */ - fprintf(stderr, "Deleting non-text paragraph\n"); - - } - - } - - emit_change_sig(e); - sc_editor_redraw(e); -} - - -static void insert_text(char *t, SCEditor *e) -{ - Paragraph *para; - - if ( e->cursor_frame == NULL ) return; - - if ( e->sel_active ) { - do_backspace(e->cursor_frame, e); - } - - if ( strcmp(t, "\n") == 0 ) { - split_paragraph_at_cursor(e); - if ( e->flow ) update_size(e); - cursor_moveh(e->cursor_frame, &e->cpos, +1); - check_cursor_visible(e); - emit_change_sig(e); - sc_editor_redraw(e); - return; - } - - para = e->cursor_frame->paras[e->cpos.para]; - - /* Is this paragraph even a text one? */ - if ( para_type(para) == PARA_TYPE_TEXT ) { - - size_t off; - - /* Yes. The "easy" case */ - - if ( !position_editable(e->cursor_frame, e->cpos) ) { - fprintf(stderr, "Position not editable\n"); - return; - } - - off = pos_trail_to_offset(para, e->cpos.pos, e->cpos.trail); - insert_text_in_paragraph(para, off, t); - wrap_paragraph(para, NULL, - e->cursor_frame->w - e->cursor_frame->pad_l - - e->cursor_frame->pad_r, 0, 0); - if ( e->flow ) update_size(e); - - cursor_moveh(e->cursor_frame, &e->cpos, +1); - - } else { - - SCBlock *bd; - SCBlock *ad; - Paragraph *pnew; - - bd = para_scblock(para); - if ( bd == NULL ) { - fprintf(stderr, "No SCBlock for para\n"); - return; - } - - /* No. Create a new text paragraph straight afterwards */ - ad = sc_block_insert_after(bd, NULL, NULL, strdup(t)); - if ( ad == NULL ) { - fprintf(stderr, "Failed to add SCBlock\n"); - return; - } - - pnew = insert_paragraph(e->cursor_frame, e->cpos.para); - if ( pnew == NULL ) { - fprintf(stderr, "Failed to insert paragraph\n"); - return; - } - add_run(pnew, ad, e->cursor_frame->fontdesc, - e->cursor_frame->col, NULL); - - wrap_frame(e->cursor_frame, e->pc); - - e->cpos.para += 1; - e->cpos.pos = 0; - e->cpos.trail = 1; - - } - - emit_change_sig(e); - check_cursor_visible(e); - sc_editor_redraw(e); -} - - -static gboolean im_commit_sig(GtkIMContext *im, gchar *str, - SCEditor *e) -{ - insert_text(str, e); - return FALSE; -} - - -static int within_frame(struct frame *fr, double x, double y) -{ - if ( fr == NULL ) return 0; - if ( x < fr->x ) return 0; - if ( y < fr->y ) return 0; - if ( x > fr->x + fr->w ) return 0; - if ( y > fr->y + fr->h ) return 0; - return 1; -} - - -static struct frame *find_frame_at_position(struct frame *fr, - double x, double y) -{ - int i; - - for ( i=0; inum_children; i++ ) { - - if ( within_frame(fr->children[i], x, y) ) { - return find_frame_at_position(fr->children[i], x, y); - } - - } - - if ( within_frame(fr, x, y) ) return fr; - return NULL; -} - - -static enum corner which_corner(double xp, double yp, struct frame *fr) -{ - double x, y; /* Relative to object position */ - - x = xp - fr->x; - y = yp - fr->y; - - if ( x < 0.0 ) return CORNER_NONE; - if ( y < 0.0 ) return CORNER_NONE; - if ( x > fr->w ) return CORNER_NONE; - if ( y > fr->h ) return CORNER_NONE; - - /* Top left? */ - if ( (x<20.0) && (y<20.0) ) return CORNER_TL; - if ( (x>fr->w-20.0) && (y<20.0) ) return CORNER_TR; - if ( (x<20.0) && (y>fr->h-20.0) ) return CORNER_BL; - if ( (x>fr->w-20.0) && (y>fr->h-20.0) ) return CORNER_BR; - - return CORNER_NONE; -} - - -static void calculate_box_size(struct frame *fr, SCEditor *e, - double x, double y) -{ - double ddx, ddy, dlen, mult; - double vx, vy, dbx, dby; - - ddx = x - e->start_corner_x; - ddy = y - e->start_corner_y; - - if ( !fr->is_image ) { - - switch ( e->drag_corner ) { - - case CORNER_BR : - e->box_x = fr->x; - e->box_y = fr->y; - e->box_width = fr->w + ddx; - e->box_height = fr->h + ddy; - break; - - case CORNER_BL : - e->box_x = fr->x + ddx; - e->box_y = fr->y; - e->box_width = fr->w - ddx; - e->box_height = fr->h + ddy; - break; - - case CORNER_TL : - e->box_x = fr->x + ddx; - e->box_y = fr->y + ddy; - e->box_width = fr->w - ddx; - e->box_height = fr->h - ddy; - break; - - case CORNER_TR : - e->box_x = fr->x; - e->box_y = fr->y + ddy; - e->box_width = fr->w + ddx; - e->box_height = fr->h - ddy; - break; - - case CORNER_NONE : - break; - - } - return; - - - } - - switch ( e->drag_corner ) { - - case CORNER_BR : - vx = fr->w; - vy = fr->h; - break; - - case CORNER_BL : - vx = -fr->w; - vy = fr->h; - break; - - case CORNER_TL : - vx = -fr->w; - vy = -fr->h; - break; - - case CORNER_TR : - vx = fr->w; - vy = -fr->h; - break; - - case CORNER_NONE : - default: - vx = 0.0; - vy = 0.0; - break; - - } - - dlen = (ddx*vx + ddy*vy) / e->diagonal_length; - mult = (dlen+e->diagonal_length) / e->diagonal_length; - - e->box_width = fr->w * mult; - e->box_height = fr->h * mult; - dbx = e->box_width - fr->w; - dby = e->box_height - fr->h; - - if ( e->box_width < 40.0 ) { - mult = 40.0 / fr->w; - } - if ( e->box_height < 40.0 ) { - mult = 40.0 / fr->h; - } - e->box_width = fr->w * mult; - e->box_height = fr->h * mult; - dbx = e->box_width - fr->w; - dby = e->box_height - fr->h; - - switch ( e->drag_corner ) { - - case CORNER_BR : - e->box_x = fr->x; - e->box_y = fr->y; - break; - - case CORNER_BL : - e->box_x = fr->x - dbx; - e->box_y = fr->y; - break; - - case CORNER_TL : - e->box_x = fr->x - dbx; - e->box_y = fr->y - dby; - break; - - case CORNER_TR : - e->box_x = fr->x; - e->box_y = fr->y - dby; - break; - - case CORNER_NONE : - break; - - } -} - - -static void check_paragraph(struct frame *fr, PangoContext *pc, - SCBlock *scblocks) -{ - if ( fr->n_paras > 0 ) return; - Paragraph *para = last_para(fr); - - if ( scblocks == NULL ) { - /* We have no SCBlocks at all! Better create one... */ - scblocks = sc_parse(""); - fr->scblocks = scblocks; - } - - /* We are creating the first paragraph. It uses the last SCBlock - * in the chain */ - while ( sc_block_next(scblocks) != NULL ) { - scblocks = sc_block_next(scblocks); - } - scblocks = sc_block_append(scblocks, NULL, NULL, strdup(""), NULL); - - add_run(para, scblocks, fr->fontdesc, fr->col, NULL); - wrap_paragraph(para, pc, fr->w - fr->pad_l - fr->pad_r, 0, 0); -} - - -static void rewrap_paragraph_range(struct frame *fr, int a, int b, - struct edit_pos sel_start, - struct edit_pos sel_end, - int sel_active) -{ - int i; - int sel_s, sel_e; - Paragraph *para; - - if ( a > b ) { - int t = a; - a = b; b = t; - } - - if ( fr == NULL ) return; - if ( fr->paras == NULL ) return; - - sort_positions(&sel_start, &sel_end); - - //printf("frame %p\n", fr); - //printf("start: "); - //show_edit_pos(sel_start); - //printf(" end: "); - //show_edit_pos(sel_end); - - para = fr->paras[sel_start.para]; - sel_s = pos_trail_to_offset(para, sel_start.pos, sel_start.trail); - para = fr->paras[sel_end.para]; - sel_e = pos_trail_to_offset(para, sel_end.pos, sel_end.trail); - - for ( i=a; i<=b; i++ ) { - size_t srt, end; - if ( sel_active ) { - if ( i == sel_start.para ) { - srt = sel_s; - } else { - srt = 0; - } - if ( i == sel_end.para ) { - end = sel_e; - } else { - end = G_MAXUINT; - } - if ( i > sel_start.para && i < sel_end.para ) { - end = G_MAXUINT; - } - } else { - srt = 0; - end = 0; - } - wrap_paragraph(fr->paras[i], NULL, - fr->w - fr->pad_l - fr->pad_r, srt, end); - } -} - - -static void unset_selection(SCEditor *e) -{ - int a, b; - - if ( !e->sel_active ) return; - - a = e->sel_start.para; - b = e->sel_end.para; - if ( a > b ) { - a = e->sel_end.para; - b = e->sel_start.para; - } - e->sel_active = 0; - rewrap_paragraph_range(e->cursor_frame, a, b, e->sel_start, e->sel_end, 0); -} - - -static gboolean button_press_sig(GtkWidget *da, GdkEventButton *event, - SCEditor *e) -{ - enum corner c; - gdouble x, y; - struct frame *clicked; - int shift; - - x = event->x - e->border_offs_x; - y = event->y - e->border_offs_y + e->scroll_pos; - x /= e->view_scale; - y /= e->view_scale; - shift = event->state & GDK_SHIFT_MASK; - - if ( within_frame(e->selection, x, y) ) { - clicked = e->selection; - } else { - clicked = find_frame_at_position(e->top, x, y); - } - - /* Clicked within the currently selected frame - * -> resize, move or select text */ - if ( (e->selection != NULL) && (clicked == e->selection) ) { - - struct frame *fr; - - fr = e->selection; - - /* Within the resizing region? */ - c = which_corner(x, y, fr); - if ( (c != CORNER_NONE) && fr->resizable && shift ) { - - e->drag_reason = DRAG_REASON_RESIZE; - e->drag_corner = c; - - e->start_corner_x = x; - e->start_corner_y = y; - e->diagonal_length = pow(fr->w, 2.0); - e->diagonal_length += pow(fr->h, 2.0); - e->diagonal_length = sqrt(e->diagonal_length); - - calculate_box_size(fr, e, x, y); - - e->drag_status = DRAG_STATUS_COULD_DRAG; - e->drag_reason = DRAG_REASON_RESIZE; - - } else { - - /* Position cursor and prepare for possible drag */ - e->cursor_frame = clicked; - check_paragraph(e->cursor_frame, e->pc, sc_block_child(fr->scblocks)); - find_cursor(clicked, x-fr->x, y-fr->y, &e->cpos); - ensure_run(e->cursor_frame, e->cpos); - - e->start_corner_x = x; - e->start_corner_y = y; - - if ( event->type == GDK_2BUTTON_PRESS ) { - check_callback_click(e->cursor_frame, e->cpos.para); - } - - if ( fr->resizable && shift ) { - e->drag_status = DRAG_STATUS_COULD_DRAG; - e->drag_reason = DRAG_REASON_MOVE; - } else if ( !e->para_highlight ) { - e->drag_status = DRAG_STATUS_COULD_DRAG; - e->drag_reason = DRAG_REASON_TEXTSEL; - unset_selection(e); - find_cursor(clicked, x-fr->x, y-fr->y, &e->sel_start); - } - - } - - } else if ( (clicked == NULL) - || ( !e->top_editable && (clicked == e->top) ) ) - { - /* Clicked no object. Deselect old object. - * If shift held, set up for creating a new one. */ - e->selection = NULL; - unset_selection(e); - - if ( shift ) { - e->start_corner_x = x; - e->start_corner_y = y; - e->drag_status = DRAG_STATUS_COULD_DRAG; - e->drag_reason = DRAG_REASON_CREATE; - } else { - e->drag_status = DRAG_STATUS_NONE; - e->drag_reason = DRAG_REASON_NONE; - } - - } else { - - /* Clicked an existing frame, no immediate dragging */ - e->drag_status = DRAG_STATUS_COULD_DRAG; - e->drag_reason = DRAG_REASON_TEXTSEL; - unset_selection(e); - find_cursor(clicked, x-clicked->x, y-clicked->y, - &e->sel_start); - find_cursor(clicked, x-clicked->x, y-clicked->y, - &e->sel_end); - e->selection = clicked; - e->cursor_frame = clicked; - if ( clicked == e->top ) { - check_paragraph(e->cursor_frame, e->pc, clicked->scblocks); - } else { - check_paragraph(e->cursor_frame, e->pc, - sc_block_child(clicked->scblocks)); - } - find_cursor(clicked, x-clicked->x, y-clicked->y, &e->cpos); - ensure_run(e->cursor_frame, e->cpos); - - } - - gtk_widget_grab_focus(GTK_WIDGET(da)); - sc_editor_redraw(e); - return FALSE; -} - - -static gboolean motion_sig(GtkWidget *da, GdkEventMotion *event, - SCEditor *e) -{ - struct frame *fr = e->selection; - gdouble x, y; - - x = event->x - e->border_offs_x; - y = event->y - e->border_offs_y + e->scroll_pos; - x /= e->view_scale; - y /= e->view_scale; - - if ( e->drag_status == DRAG_STATUS_COULD_DRAG ) { - - /* We just got a motion signal, and the status was "could drag", - * therefore the drag has started. */ - e->drag_status = DRAG_STATUS_DRAGGING; - - } - - switch ( e->drag_reason ) { - - case DRAG_REASON_NONE : - break; - - case DRAG_REASON_CREATE : - e->drag_corner_x = x; - e->drag_corner_y = y; - sc_editor_redraw(e); - break; - - case DRAG_REASON_IMPORT : - /* Do nothing, handled by dnd_motion() */ - break; - - case DRAG_REASON_RESIZE : - calculate_box_size(fr, e, x, y); - sc_editor_redraw(e); - break; - - case DRAG_REASON_MOVE : - e->box_x = (fr->x - e->start_corner_x) + x; - e->box_y = (fr->y - e->start_corner_y) + y; - e->box_width = fr->w; - e->box_height = fr->h; - sc_editor_redraw(e); - break; - - case DRAG_REASON_TEXTSEL : - unset_selection(e); - find_cursor(fr, x-fr->x, y-fr->y, &e->sel_end); - rewrap_paragraph_range(fr, e->sel_start.para, e->sel_end.para, - e->sel_start, e->sel_end, 1); - find_cursor(fr, x-fr->x, y-fr->y, &e->cpos); - e->sel_active = !positions_equal(e->sel_start, e->sel_end); - sc_editor_redraw(e); - break; - - } - - gdk_event_request_motions(event); - return FALSE; -} - - -static struct frame *create_frame(SCEditor *e, double x, double y, - double w, double h) -{ - struct frame *parent; - struct frame *fr; - SCBlock *scblocks; - - parent = e->top; - - if ( w < 0.0 ) { - x += w; - w = -w; - } - - if ( h < 0.0 ) { - y += h; - h = -h; - } - - /* Add to frame structure */ - fr = add_subframe(parent); - - /* Add to SC */ - scblocks = sc_block_append_end(sc_block_child(e->scblocks), "f", NULL, NULL); - fr->scblocks = scblocks; - sc_block_append_inside(scblocks, NULL, NULL, strdup("")); - - fr->x = x; - fr->y = y; - fr->w = w; - fr->h = h; - fr->is_image = 0; - fr->empty = 1; - fr->resizable = 1; - - update_geom(fr); - - full_rerender(e); - return find_frame_with_scblocks(e->top, scblocks); -} - - -static void do_resize(SCEditor *e, double x, double y, double w, double h) -{ - struct frame *fr; - - assert(e->selection != NULL); - - if ( w < 0.0 ) { - w = -w; - x -= w; - } - - if ( h < 0.0 ) { - h = -h; - y -= h; - } - - fr = e->selection; - fr->x = x; - fr->y = y; - fr->w = w; - fr->h = h; - update_geom(fr); - - full_rerender(e); - sc_editor_redraw(e); -} - - -static gboolean button_release_sig(GtkWidget *da, GdkEventButton *event, - SCEditor *e) -{ - gdouble x, y; - struct frame *fr; - - x = event->x - e->border_offs_x; - y = event->y - e->border_offs_y; - x /= e->view_scale; - y /= e->view_scale; - - /* Not dragging? Then I don't care. */ - if ( e->drag_status != DRAG_STATUS_DRAGGING ) return FALSE; - - e->drag_corner_x = x; - e->drag_corner_y = y; - e->drag_status = DRAG_STATUS_NONE; - - switch ( e->drag_reason ) - { - - case DRAG_REASON_NONE : - printf("Release on pointless drag.\n"); - break; - - case DRAG_REASON_CREATE : - fr = create_frame(e, e->start_corner_x, e->start_corner_y, - e->drag_corner_x - e->start_corner_x, - e->drag_corner_y - e->start_corner_y); - if ( fr != NULL ) { - check_paragraph(fr, e->pc, sc_block_child(fr->scblocks)); - e->selection = fr; - e->cursor_frame = fr; - e->cpos.para = 0; - e->cpos.pos = 0; - e->cpos.trail = 0; - } else { - fprintf(stderr, _("Failed to create frame!\n")); - } - break; - - case DRAG_REASON_IMPORT : - /* Do nothing, handled in dnd_drop() or dnd_leave() */ - break; - - case DRAG_REASON_RESIZE : - do_resize(e, e->box_x, e->box_y, e->box_width, e->box_height); - break; - - case DRAG_REASON_MOVE : - do_resize(e, e->box_x, e->box_y, e->box_width, e->box_height); - break; - - case DRAG_REASON_TEXTSEL : - /* Do nothing (text is already selected) */ - break; - - } - - e->drag_reason = DRAG_REASON_NONE; - - gtk_widget_grab_focus(GTK_WIDGET(da)); - sc_editor_redraw(e); - return FALSE; -} - - -static gboolean key_press_sig(GtkWidget *da, GdkEventKey *event, - SCEditor *e) -{ - gboolean r; - int claim = 0; - - /* Throw the event to the IM context and let it sort things out */ - r = gtk_im_context_filter_keypress(GTK_IM_CONTEXT(e->im_context), - event); - if ( r ) return FALSE; /* IM ate it */ - - switch ( event->keyval ) { - - case GDK_KEY_Escape : - if ( !e->para_highlight ) { - sc_editor_remove_cursor(e); - sc_editor_redraw(e); - claim = 1; - } - break; - - case GDK_KEY_Left : - if ( e->selection != NULL ) { - cursor_moveh(e->cursor_frame, &e->cpos, -1); - sc_editor_redraw(e); - } - claim = 1; - break; - - case GDK_KEY_Right : - if ( e->selection != NULL ) { - cursor_moveh(e->cursor_frame, &e->cpos, +1); - sc_editor_redraw(e); - } - claim = 1; - break; - - case GDK_KEY_Up : - if ( e->selection != NULL ) { - cursor_moveh(e->cursor_frame, &e->cpos, -1); - sc_editor_redraw(e); - } - claim = 1; - break; - - case GDK_KEY_Down : - if ( e->selection != NULL ) { - cursor_moveh(e->cursor_frame, &e->cpos, +1); - sc_editor_redraw(e); - } - claim = 1; - break; - - - case GDK_KEY_Return : - im_commit_sig(NULL, "\n", e); - claim = 1; - break; - - case GDK_KEY_BackSpace : - if ( e->selection != NULL ) { - do_backspace(e->selection, e); - claim = 1; - } - break; - - case GDK_KEY_F5 : - full_rerender(e); - break; - - case GDK_KEY_F6 : - show_edit_pos(e->cpos); - break; - - case GDK_KEY_F7 : - if ( e->cursor_frame != NULL ) { - if ( event->state & GDK_CONTROL_MASK ) { - debug_paragraphs(e); - } else if ( event->state & GDK_SHIFT_MASK ) { - printf("Cursor frame block = %p\n", e->cursor_frame->scblocks); - printf("Editor top block = %p\n", e->scblocks); - show_sc_block(e->cursor_frame->scblocks, ""); - } else { - open_debugger(e->cursor_frame); - } - } else { - if ( event->state & GDK_SHIFT_MASK ) { - printf("Debugging the top frame:\n"); - printf("Editor top block = %p\n", e->scblocks); - show_sc_block(e->top->scblocks, ""); - } - } - break; - - case GDK_KEY_C : - case GDK_KEY_c : - if ( event->state == GDK_CONTROL_MASK ) { - copy_selection(e); - } - break; - - case GDK_KEY_V : - case GDK_KEY_v : - if ( event->state == GDK_CONTROL_MASK ) { - sc_editor_paste(e); - } - break; - - - } - - if ( claim ) return TRUE; - return FALSE; -} - - -static gboolean dnd_motion(GtkWidget *widget, GdkDragContext *drag_context, - gint x, gint y, guint time, SCEditor *e) -{ - GdkAtom target; - - /* If we haven't already requested the data, do so now */ - if ( !e->drag_preview_pending && !e->have_drag_data ) { - - target = gtk_drag_dest_find_target(widget, drag_context, NULL); - - if ( target != GDK_NONE ) { - gtk_drag_get_data(widget, drag_context, target, time); - e->drag_preview_pending = 1; - } else { - e->import_acceptable = 0; - gdk_drag_status(drag_context, 0, time); - } - - } - - if ( e->have_drag_data && e->import_acceptable ) { - - gdk_drag_status(drag_context, GDK_ACTION_LINK, time); - e->start_corner_x = x - e->import_width/2.0; - e->start_corner_y = y - e->import_height/2.0; - e->drag_corner_x = x + e->import_width/2.0; - e->drag_corner_y = y + e->import_height/2.0; - - sc_editor_redraw(e); - - } - - return TRUE; -} - - -static gboolean dnd_drop(GtkWidget *widget, GdkDragContext *drag_context, - gint x, gint y, guint time, SCEditor *e) -{ - GdkAtom target; - - target = gtk_drag_dest_find_target(widget, drag_context, NULL); - - if ( target == GDK_NONE ) { - gtk_drag_finish(drag_context, FALSE, FALSE, time); - } else { - gtk_drag_get_data(widget, drag_context, target, time); - } - - return TRUE; -} - - -/* Scale the image down if it's a silly size */ -static void check_import_size(SCEditor *e) -{ - if ( e->import_width > e->w ) { - - int new_import_width; - - new_import_width = e->w/2; - e->import_height = (new_import_width * e->import_height) / - e->import_width; - e->import_width = new_import_width; - - } - - if ( e->import_height > e->h ) { - - int new_import_height; - - new_import_height = e->w/2; - e->import_width = (new_import_height*e->import_width) / - e->import_height; - e->import_height = new_import_height; - - } -} - - -static void dnd_receive(GtkWidget *widget, GdkDragContext *drag_context, - gint x, gint y, GtkSelectionData *seldata, - guint info, guint time, SCEditor *e) -{ - if ( e->drag_preview_pending ) { - - gchar *filename = NULL; - GdkPixbufFormat *f; - gchar **uris; - int w, h; - - e->have_drag_data = 1; - e->drag_preview_pending = 0; - uris = gtk_selection_data_get_uris(seldata); - if ( uris != NULL ) { - filename = g_filename_from_uri(uris[0], NULL, NULL); - } - g_strfreev(uris); - - if ( filename == NULL ) { - - /* This doesn't even look like a sensible URI. - * Bail out. */ - gdk_drag_status(drag_context, 0, time); - if ( e->drag_highlight ) { - gtk_drag_unhighlight(widget); - e->drag_highlight = 0; - } - e->import_acceptable = 0; - return; - - } - chomp(filename); - - f = gdk_pixbuf_get_file_info(filename, &w, &h); - g_free(filename); - - e->import_width = w; - e->import_height = h; - - if ( f == NULL ) { - - gdk_drag_status(drag_context, 0, time); - if ( e->drag_highlight ) { - gtk_drag_unhighlight(widget); - e->drag_highlight = 0; - } - e->drag_status = DRAG_STATUS_NONE; - e->drag_reason = DRAG_REASON_NONE; - e->import_acceptable = 0; - - } else { - - /* Looks like a sensible image */ - gdk_drag_status(drag_context, GDK_ACTION_PRIVATE, time); - e->import_acceptable = 1; - - if ( !e->drag_highlight ) { - gtk_drag_highlight(widget); - e->drag_highlight = 1; - } - - check_import_size(e); - e->drag_reason = DRAG_REASON_IMPORT; - e->drag_status = DRAG_STATUS_DRAGGING; - - } - - } else { - - gchar **uris; - char *filename = NULL; - - uris = gtk_selection_data_get_uris(seldata); - if ( uris != NULL ) { - filename = g_filename_from_uri(uris[0], NULL, NULL); - } - g_strfreev(uris); - - if ( filename != NULL ) { - - struct frame *fr; - char *opts; - size_t len; - int w, h; - - gtk_drag_finish(drag_context, TRUE, FALSE, time); - chomp(filename); - - w = e->drag_corner_x - e->start_corner_x; - h = e->drag_corner_y - e->start_corner_y; - - len = strlen(filename)+64; - opts = malloc(len); - if ( opts == NULL ) { - free(filename); - fprintf(stderr, "Failed to allocate SC\n"); - return; - } - snprintf(opts, len, "1fx1f+0+0,filename=\"%s\"", - filename); - - fr = create_frame(e, e->start_corner_x, - e->start_corner_y, w, h); - fr->is_image = 1; - fr->empty = 0; - sc_block_set_name(sc_block_child(fr->scblocks), strdup("image")); - sc_block_set_options(sc_block_child(fr->scblocks), opts); - full_rerender(e); - sc_editor_remove_cursor(e); - sc_editor_redraw(e); - free(filename); - - } else { - - gtk_drag_finish(drag_context, FALSE, FALSE, time); - - } - - } -} - - -static void dnd_leave(GtkWidget *widget, GdkDragContext *drag_context, - guint time, SCEditor *sceditor) -{ - if ( sceditor->drag_highlight ) { - gtk_drag_unhighlight(widget); - } - sceditor->have_drag_data = 0; - sceditor->drag_highlight = 0; - sceditor->drag_status = DRAG_STATUS_NONE; - sceditor->drag_reason = DRAG_REASON_NONE; -} - - -static gint realise_sig(GtkWidget *da, SCEditor *e) -{ - GdkWindow *win; - - /* Keyboard and input method stuff */ - e->im_context = gtk_im_multicontext_new(); - win = gtk_widget_get_window(GTK_WIDGET(e)); - gtk_im_context_set_client_window(GTK_IM_CONTEXT(e->im_context), win); - gdk_window_set_accept_focus(win, TRUE); - g_signal_connect(G_OBJECT(e->im_context), "commit", G_CALLBACK(im_commit_sig), e); - g_signal_connect(G_OBJECT(e), "key-press-event", G_CALLBACK(key_press_sig), e); - - /* FIXME: Can do this "properly" by setting up a separate font map */ - e->pc = gtk_widget_get_pango_context(GTK_WIDGET(e)); - - return FALSE; -} - - -void sc_editor_set_scblock(SCEditor *e, SCBlock *scblocks) -{ - e->scblocks = scblocks; - full_rerender(e); -} - - -static void update_size_request(SCEditor *e) -{ - gtk_widget_set_size_request(GTK_WIDGET(e), 0, e->h + 2.0*e->min_border); -} - - -void sc_editor_set_logical_size(SCEditor *e, double w, double h) -{ - e->log_w = w; - e->log_h = h; - if ( gtk_widget_get_mapped(GTK_WIDGET(e)) ) { - full_rerender(e); - sc_editor_redraw(e); - } -} - - -void sc_editor_set_slidenum(SCEditor *e, int slidenum) -{ - e->slidenum = slidenum; -} - - -void sc_editor_set_min_border(SCEditor *e, double min_border) -{ - e->min_border = min_border; - update_size_request(e); -} - - -void sc_editor_set_top_frame_editable(SCEditor *e, int top_frame_editable) -{ - e->top_editable = top_frame_editable; -} - - -void sc_editor_set_stylesheet(SCEditor *e, Stylesheet *stylesheet) -{ - e->stylesheet = stylesheet; -} - - -void sc_editor_set_callbacks(SCEditor *e, SCCallbackList *cbl) -{ - if ( e->cbl != NULL ) sc_callback_list_free(e->cbl); - e->cbl = cbl; -} - - -void sc_editor_set_para_highlight(SCEditor *e, int para_highlight) -{ - e->para_highlight = para_highlight; - sc_editor_redraw(e); -} - -int sc_editor_get_cursor_para(SCEditor *e) -{ - if ( e->cursor_frame == NULL ) return 0; - return e->cpos.para; -} - - -void *sc_editor_get_cursor_bvp(SCEditor *e) -{ - Paragraph *para; - if ( e->cursor_frame == NULL ) return 0; - para = e->cursor_frame->paras[e->cpos.para]; - return get_para_bvp(para); -} - - -void sc_editor_set_cursor_para(SCEditor *e, signed int pos) -{ - double h; - int i; - - if ( e->cursor_frame == NULL ) { - e->cursor_frame = e->top; - e->selection = e->top; - } - - if ( pos < 0 ) { - e->cpos.para = e->cursor_frame->n_paras - 1; - } else if ( pos >= e->cursor_frame->n_paras ) { - e->cpos.para = e->cursor_frame->n_paras - 1; - } else { - e->cpos.para = pos; - } - e->cpos.pos = 0; - e->cpos.trail = 0; - - h = 0; - for ( i=0; icpos.para; i++ ) { - h += paragraph_height(e->cursor_frame->paras[i]); - } - h += (paragraph_height(e->cursor_frame->paras[e->cpos.para]))/2; - e->scroll_pos = h - (e->visible_height/2); - set_vertical_params(e); - - sc_editor_redraw(e); -} - - -int sc_editor_get_num_paras(SCEditor *e) -{ - if ( e->cursor_frame == NULL ) return 1; - return e->cursor_frame->n_paras; -} - - -void sc_editor_set_scale(SCEditor *e, int scale) -{ - e->scale = scale; - if ( !scale ) { - e->view_scale = 1.0; - } -} - - -void sc_editor_set_imagestore(SCEditor *e, ImageStore *is) -{ - if ( e->is != NULL ) { - fprintf(stderr, "WARNING: Changing imagestore\n"); - } - e->is = is; -} - - -SCEditor *sc_editor_new(SCBlock *scblocks, Stylesheet *stylesheet, - PangoLanguage *lang, const char *storename) -{ - SCEditor *sceditor; - GtkTargetEntry targets[1]; - GError *err; - - sceditor = g_object_new(SC_TYPE_EDITOR, NULL); - - sceditor->scblocks = scblocks; - sceditor->w = 100; - sceditor->h = 100; - sceditor->log_w = 100; - sceditor->log_h = 100; - sceditor->border_offs_x = 0; - sceditor->border_offs_y = 0; - sceditor->is = NULL; - sceditor->slidenum = 0; - sceditor->min_border = 0.0; - sceditor->top_editable = 0; - sceditor->cbl = NULL; - sceditor->scroll_pos = 0; - sceditor->flow = 0; - sceditor->scale = 0; - sceditor->view_scale = 1.0; - sceditor->lang = lang; - - sceditor->para_highlight = 0; - sc_editor_remove_cursor(sceditor); - - sceditor->stylesheet = stylesheet; - - err = NULL; - sceditor->bg_pixbuf = gdk_pixbuf_new_from_resource("/uk/me/bitwiz/Colloquium/sky.png", - &err); - if ( sceditor->bg_pixbuf == NULL ) { - fprintf(stderr, _("Failed to load background: %s\n"), - err->message); - } - - gtk_widget_set_size_request(GTK_WIDGET(sceditor), - sceditor->w, sceditor->h); - - g_signal_connect(G_OBJECT(sceditor), "destroy", - G_CALLBACK(destroy_sig), sceditor); - g_signal_connect(G_OBJECT(sceditor), "realize", - G_CALLBACK(realise_sig), sceditor); - g_signal_connect(G_OBJECT(sceditor), "button-press-event", - G_CALLBACK(button_press_sig), sceditor); - g_signal_connect(G_OBJECT(sceditor), "button-release-event", - G_CALLBACK(button_release_sig), sceditor); - g_signal_connect(G_OBJECT(sceditor), "motion-notify-event", - G_CALLBACK(motion_sig), sceditor); - g_signal_connect(G_OBJECT(sceditor), "configure-event", - G_CALLBACK(resize_sig), sceditor); - - /* Drag and drop */ - targets[0].target = "text/uri-list"; - targets[0].flags = 0; - targets[0].info = 1; - gtk_drag_dest_set(GTK_WIDGET(sceditor), 0, targets, 1, - GDK_ACTION_PRIVATE); - g_signal_connect(sceditor, "drag-data-received", - G_CALLBACK(dnd_receive), sceditor); - g_signal_connect(sceditor, "drag-motion", - G_CALLBACK(dnd_motion), sceditor); - g_signal_connect(sceditor, "drag-drop", - G_CALLBACK(dnd_drop), sceditor); - g_signal_connect(sceditor, "drag-leave", - G_CALLBACK(dnd_leave), sceditor); - - gtk_widget_set_can_focus(GTK_WIDGET(sceditor), TRUE); - gtk_widget_add_events(GTK_WIDGET(sceditor), - GDK_POINTER_MOTION_HINT_MASK - | GDK_BUTTON1_MOTION_MASK - | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - | GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK - | GDK_SCROLL_MASK); - - g_signal_connect(G_OBJECT(sceditor), "draw", - G_CALLBACK(draw_sig), sceditor); - - gtk_widget_grab_focus(GTK_WIDGET(sceditor)); - - gtk_widget_show(GTK_WIDGET(sceditor)); - - return sceditor; -} diff --git a/src/sc_editor.h b/src/sc_editor.h deleted file mode 100644 index d3c111b..0000000 --- a/src/sc_editor.h +++ /dev/null @@ -1,199 +0,0 @@ -/* - * sc_editor.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 SC_EDITOR_H -#define SC_EDITOR_H - -#ifdef HAVE_CONFIG_H -#include -#endif - -#include -#include - -#include "frame.h" -#include "sc_interp.h" -#include "stylesheet.h" - -struct presentation; - - -#define SC_TYPE_EDITOR (sc_editor_get_type()) - -#define SC_EDITOR(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), \ - SC_TYPE_EDITOR, SCEditor)) - -#define SC_IS_EDITOR(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), \ - SC_TYPE_EDITOR)) - -#define SC_EDITOR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((obj), \ - SC_TYPE_EDITOR, SCEditorClass)) - -#define SC_IS_EDITOR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((obj), \ - SC_TYPE_EDITOR)) - -#define SC_EDITOR_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), \ - SC_TYPE_EDITOR, SCEditorClass)) - -enum drag_reason -{ - DRAG_REASON_NONE, - DRAG_REASON_CREATE, - DRAG_REASON_IMPORT, - DRAG_REASON_RESIZE, - DRAG_REASON_MOVE, - DRAG_REASON_TEXTSEL -}; - - -enum corner -{ - CORNER_NONE, - CORNER_TL, - CORNER_TR, - CORNER_BL, - CORNER_BR -}; - - -enum drag_status -{ - DRAG_STATUS_NONE, - DRAG_STATUS_COULD_DRAG, - DRAG_STATUS_DRAGGING, -}; - - -struct _sceditor -{ - GtkDrawingArea parent_instance; - PangoLanguage *lang; - - /*< private >*/ - GtkIMContext *im_context; - int w; /* Surface size in pixels */ - int h; - double log_w; /* Size of surface in "SC units" */ - double log_h; - SCBlock *scblocks; - Stylesheet *stylesheet; - ImageStore *is; - SCCallbackList *cbl; - struct frame *top; - int para_highlight; - - /* Redraw/scroll stuff */ - GtkScrollablePolicy hpol; - GtkScrollablePolicy vpol; - GtkAdjustment *hadj; - GtkAdjustment *vadj; - double scroll_pos; - double h_scroll_pos; - int visible_height; - int visible_width; - int flow; - int scale; /* Whether the SCEditor should scale to fit */ - double view_scale; /* The scale factor, if scale=1 */ - - /* Pointers to the frame currently being edited */ - struct frame *selection; - int top_editable; - - PangoContext *pc; - - /* Location of the cursor */ - struct frame *cursor_frame; - struct edit_pos cpos; - - /* Border surrounding actual slide within drawingarea */ - double border_offs_x; - double border_offs_y; - double min_border; - double bgcol[3]; - GdkPixbuf *bg_pixbuf; - - /* Rubber band boxes and related stuff */ - double start_corner_x; - double start_corner_y; - double drag_corner_x; - double drag_corner_y; - double diagonal_length; - double box_x; - double box_y; - double box_width; - double box_height; - enum drag_reason drag_reason; - enum drag_status drag_status; - enum corner drag_corner; - int sel_active; - struct edit_pos sel_start; /* Where the user dragged from */ - struct edit_pos sel_end; - - /* Stuff to do with drag and drop import of "content" */ - int drag_preview_pending; - int have_drag_data; - int drag_highlight; - double import_width; - double import_height; - int import_acceptable; - - /* Stuff that doesn't really belong here */ - int slidenum; -}; - -struct _sceditorclass -{ - GtkDrawingAreaClass parent_class; -}; - -typedef struct _sceditor SCEditor; -typedef struct _sceditorclass SCEditorClass; - -extern void sc_editor_set_scblock(SCEditor *e, SCBlock *scblocks); -extern void sc_editor_set_stylesheet(SCEditor *e, Stylesheet *stylesheet); -extern SCEditor *sc_editor_new(SCBlock *scblocks, Stylesheet *stylesheet, - PangoLanguage *lang, const char *storename); -extern void sc_editor_set_logical_size(SCEditor *e, double w, double h); -extern void sc_editor_set_flow(SCEditor *e, int flow); -extern void sc_editor_set_scale(SCEditor *e, int scale); -extern void sc_editor_redraw(SCEditor *e); -extern void sc_editor_set_background(SCEditor *e, double r, double g, double b); -extern void sc_editor_set_slidenum(SCEditor *e, int slidenum); -extern void sc_editor_set_min_border(SCEditor *e, double min_border); -extern void sc_editor_set_top_frame_editable(SCEditor *e, - int top_frame_editable); -extern void sc_editor_set_callbacks(SCEditor *e, SCCallbackList *cbl); -extern void sc_editor_paste(SCEditor *e); -extern void sc_editor_add_storycode(SCEditor *e, const char *sc); -extern void sc_editor_copy_selected_frame(SCEditor *e); -extern void sc_editor_delete_selected_frame(SCEditor *e); -extern void sc_editor_ensure_cursor(SCEditor *e); -extern SCBlock *split_paragraph_at_cursor(SCEditor *e); - -extern void sc_editor_set_imagestore(SCEditor *e, ImageStore *is); -extern void sc_editor_set_para_highlight(SCEditor *e, int para_highlight); -extern int sc_editor_get_cursor_para(SCEditor *e); -extern void *sc_editor_get_cursor_bvp(SCEditor *e); -extern void sc_editor_set_cursor_para(SCEditor *e, signed int pos); -extern int sc_editor_get_num_paras(SCEditor *e); - -#endif /* SC_EDITOR_H */ diff --git a/src/sc_interp.c b/src/sc_interp.c deleted file mode 100644 index 07f09a5..0000000 --- a/src/sc_interp.c +++ /dev/null @@ -1,1148 +0,0 @@ -/* - * sc_interp.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 - -#include "imagestore.h" -#include "sc_parse.h" -#include "sc_interp.h" -#include "presentation.h" -#include "utils.h" - - -struct sc_state -{ - PangoFontDescription *fontdesc; - PangoFont *font; - PangoAlignment alignment; - double col[4]; - int ascent; - int height; - float paraspace[4]; - char *constants[NUM_SC_CONSTANTS]; - - struct frame *fr; /* The current frame */ -}; - -struct _scinterp -{ - PangoContext *pc; - PangoLanguage *lang; - ImageStore *is; - - struct slide_constants *s_constants; - struct presentation_constants *p_constants; - - struct sc_state *state; - int j; /* Index of the current state */ - int max_state; - - SCCallbackList *cbl; -}; - -struct _sccallbacklist -{ - int n_callbacks; - int max_callbacks; - char **names; - SCCallbackBoxFunc *box_funcs; - SCCallbackDrawFunc *draw_funcs; - SCCallbackClickFunc *click_funcs; - void **vps; -}; - - -static int sc_interp_add_blocks(SCInterpreter *scin, SCBlock *bl, Stylesheet *ss) -{ - while ( bl != NULL ) { - if ( sc_interp_add_block(scin, bl, ss) ) return 1; - bl = sc_block_next(bl); - } - - return 0; -} - - -SCCallbackList *sc_callback_list_new() -{ - SCCallbackList *cbl; - - cbl = malloc(sizeof(struct _sccallbacklist)); - if ( cbl == NULL ) return NULL; - - cbl->names = calloc(8, sizeof(char *)); - if ( cbl->names == NULL ) { - free(cbl); - return NULL; - } - - cbl->box_funcs = calloc(8, sizeof(cbl->box_funcs[0])); - if ( cbl->box_funcs == NULL ) { - free(cbl->names); - free(cbl); - return NULL; - } - - cbl->draw_funcs = calloc(8, sizeof(cbl->draw_funcs[0])); - if ( cbl->draw_funcs == NULL ) { - free(cbl->box_funcs); - free(cbl->names); - free(cbl); - return NULL; - } - - cbl->click_funcs = calloc(8, sizeof(cbl->click_funcs[0])); - if ( cbl->click_funcs == NULL ) { - free(cbl->draw_funcs); - free(cbl->box_funcs); - free(cbl->names); - free(cbl); - return NULL; - } - - cbl->vps = calloc(8, sizeof(cbl->vps[0])); - if ( cbl->vps == NULL ) { - free(cbl->click_funcs); - free(cbl->draw_funcs); - free(cbl->box_funcs); - free(cbl->names); - free(cbl); - return NULL; - } - - cbl->max_callbacks = 8; - cbl->n_callbacks = 0; - - return cbl; -} - - -void sc_callback_list_free(SCCallbackList *cbl) -{ - int i; - - if ( cbl == NULL ) return; - - for ( i=0; in_callbacks; i++ ) { - free(cbl->names[i]); - } - - free(cbl->names); - free(cbl->box_funcs); - free(cbl->draw_funcs); - free(cbl->vps); - free(cbl); - -} - - -void sc_callback_list_add_callback(SCCallbackList *cbl, const char *name, - SCCallbackBoxFunc box_func, - SCCallbackDrawFunc draw_func, - SCCallbackClickFunc click_func, - void *vp) -{ - if ( cbl->n_callbacks == cbl->max_callbacks ) { - - SCCallbackBoxFunc *box_funcs_new; - SCCallbackDrawFunc *draw_funcs_new; - SCCallbackClickFunc *click_funcs_new; - char **names_new; - void **vps_new; - int mcn = cbl->max_callbacks + 8; - - names_new = realloc(cbl->names, mcn*sizeof(char *)); - box_funcs_new = realloc(cbl->box_funcs, - mcn*sizeof(SCCallbackBoxFunc)); - draw_funcs_new = realloc(cbl->draw_funcs, - mcn*sizeof(SCCallbackDrawFunc)); - click_funcs_new = realloc(cbl->click_funcs, - mcn*sizeof(SCCallbackClickFunc)); - vps_new = realloc(cbl->vps, mcn*sizeof(void *)); - - if ( (names_new == NULL) || (box_funcs_new == NULL) - || (vps_new == NULL) || (draw_funcs_new == NULL) - || (click_funcs_new == NULL) ) { - fprintf(stderr, "Failed to grow callback list\n"); - return; - } - - cbl->names = names_new; - cbl->box_funcs = box_funcs_new; - cbl->draw_funcs = draw_funcs_new; - cbl->click_funcs = click_funcs_new; - cbl->vps = vps_new; - cbl->max_callbacks = mcn; - - } - - cbl->names[cbl->n_callbacks] = strdup(name); - cbl->box_funcs[cbl->n_callbacks] = box_func; - cbl->draw_funcs[cbl->n_callbacks] = draw_func; - cbl->click_funcs[cbl->n_callbacks] = click_func; - cbl->vps[cbl->n_callbacks] = vp; - cbl->n_callbacks++; -} - - -void sc_interp_set_callbacks(SCInterpreter *scin, SCCallbackList *cbl) -{ - if ( scin->cbl != NULL ) { - fprintf(stderr, "WARNING: Interpreter already has a callback " - "list.\n"); - } - scin->cbl = cbl; -} - - -static int check_callback(SCInterpreter *scin, SCBlock *bl) -{ - int i; - const char *name = sc_block_name(bl); - SCCallbackList *cbl = scin->cbl; - - /* No callback list -> easy */ - if ( cbl == NULL ) return 0; - - /* No name -> definitely not a callback */ - if ( name == NULL ) return 0; - - for ( i=0; in_callbacks; i++ ) { - - double w, h; - int r; - void *bvp; - - if ( strcmp(cbl->names[i], name) != 0 ) continue; - r = cbl->box_funcs[i](scin, bl, &w, &h, &bvp, cbl->vps[i]); - if ( r ) { - struct sc_state *st = &scin->state[scin->j]; - Paragraph *pnew; - pnew = add_callback_para(sc_interp_get_frame(scin), - bl, w, h, - cbl->draw_funcs[i], - cbl->click_funcs[i], - bvp, cbl->vps[i]); - if ( pnew != NULL ) { - set_para_spacing(pnew, st->paraspace); - } - - } - return 1; - - } - - return 0; -} - - -PangoFontDescription *sc_interp_get_fontdesc(SCInterpreter *scin) -{ - struct sc_state *st = &scin->state[scin->j]; - return st->fontdesc; -} - - -double *sc_interp_get_fgcol(SCInterpreter *scin) -{ - struct sc_state *st = &scin->state[scin->j]; - return st->col; -} - - -static void set_frame_default_style(struct frame *fr, SCInterpreter *scin) -{ - if ( fr == NULL ) return; - - if ( fr->fontdesc != NULL ) { - pango_font_description_free(fr->fontdesc); - } - fr->fontdesc = pango_font_description_copy(sc_interp_get_fontdesc(scin)); - fr->col[0] = sc_interp_get_fgcol(scin)[0]; - fr->col[1] = sc_interp_get_fgcol(scin)[1]; - fr->col[2] = sc_interp_get_fgcol(scin)[2]; - fr->col[3] = sc_interp_get_fgcol(scin)[3]; -} - - -static void update_font(SCInterpreter *scin) -{ - PangoFontMetrics *metrics; - struct sc_state *st = &scin->state[scin->j]; - - if ( scin->pc == NULL ) return; - - st->font = pango_font_map_load_font(pango_context_get_font_map(scin->pc), - scin->pc, st->fontdesc); - if ( st->font == NULL ) { - char *f = pango_font_description_to_string(st->fontdesc); - fprintf(stderr, "Couldn't load font '%s' (font map %p, pc %p)\n", - f, pango_context_get_font_map(scin->pc), scin->pc); - g_free(f); - return; - } - - /* FIXME: Language for box */ - metrics = pango_font_get_metrics(st->font, NULL); - st->ascent = pango_font_metrics_get_ascent(metrics); - st->height = st->ascent + pango_font_metrics_get_descent(metrics); - pango_font_metrics_unref(metrics); - set_frame_default_style(sc_interp_get_frame(scin), scin); -} - - -static void set_font(SCInterpreter *scin, const char *font_name) -{ - struct sc_state *st = &scin->state[scin->j]; - - st->fontdesc = pango_font_description_from_string(font_name); - if ( st->fontdesc == NULL ) { - fprintf(stderr, "Couldn't describe font.\n"); - return; - } - - update_font(scin); -} - - -static void copy_top_fontdesc(SCInterpreter *scin) -{ - struct sc_state *st = &scin->state[scin->j]; - - /* If this is the first stack frame, don't even check */ - if ( scin->j == 0 ) return; - - /* If the fontdesc at the top of the stack is the same as the one - * below, make a copy because we're about to do something to it (which - * should not affect the next level up). */ - if ( st->fontdesc == scin->state[scin->j-1].fontdesc ) { - st->fontdesc = pango_font_description_copy(st->fontdesc); - } -} - - -static void set_fontsize(SCInterpreter *scin, const char *size_str) -{ - struct sc_state *st = &scin->state[scin->j]; - int size; - char *end; - - if ( size_str[0] == '\0' ) return; - - size = strtoul(size_str, &end, 10); - if ( end[0] != '\0' ) { - fprintf(stderr, _("Invalid font size '%s'\n"), size_str); - return; - } - - copy_top_fontdesc(scin); - pango_font_description_set_size(st->fontdesc, size*PANGO_SCALE); - update_font(scin); -} - - -static void set_bold(SCInterpreter *scin) -{ - struct sc_state *st = &scin->state[scin->j]; - copy_top_fontdesc(scin); - pango_font_description_set_weight(st->fontdesc, PANGO_WEIGHT_BOLD); - update_font(scin); -} - - -static void set_oblique(SCInterpreter *scin) -{ - struct sc_state *st = &scin->state[scin->j]; - copy_top_fontdesc(scin); - pango_font_description_set_style(st->fontdesc, PANGO_STYLE_OBLIQUE); - update_font(scin); -} - - -static void set_italic(SCInterpreter *scin) -{ - struct sc_state *st = &scin->state[scin->j]; - copy_top_fontdesc(scin); - pango_font_description_set_style(st->fontdesc, PANGO_STYLE_ITALIC); - update_font(scin); -} - - -static void set_alignment(SCInterpreter *scin, PangoAlignment align) -{ - struct sc_state *st = &scin->state[scin->j]; - st->alignment = align; -} - - -/* This sets the colour for the font at the top of the stack */ -static void set_colour(SCInterpreter *scin, const char *colour) -{ - GdkRGBA col; - struct sc_state *st = &scin->state[scin->j]; - - if ( colour == NULL ) { - printf(_("Invalid colour\n")); - st->col[0] = 0.0; - st->col[1] = 0.0; - st->col[2] = 0.0; - st->col[3] = 1.0; - return; - } - - gdk_rgba_parse(&col, colour); - - st->col[0] = col.red; - st->col[1] = col.green; - st->col[2] = col.blue; - st->col[3] = col.alpha; - set_frame_default_style(sc_interp_get_frame(scin), scin); -} - - -static void set_bgcol(SCInterpreter *scin, const char *colour) -{ - GdkRGBA col; - struct frame *fr = sc_interp_get_frame(scin); - - if ( fr == NULL ) return; - - if ( colour == NULL ) { - printf(_("Invalid colour\n")); - return; - } - - gdk_rgba_parse(&col, colour); - - fr->bgcol[0] = col.red; - fr->bgcol[1] = col.green; - fr->bgcol[2] = col.blue; - fr->bgcol[3] = col.alpha; - fr->grad = GRAD_NONE; -} - - -static void set_bggrad(SCInterpreter *scin, const char *options, - GradientType grad) -{ - struct frame *fr = sc_interp_get_frame(scin); - GdkRGBA col1, col2; - - if ( fr == NULL ) return; - - if ( options == NULL ) { - printf(_("Invalid colour\n")); - return; - } - - if ( parse_colour_duo(options, &col1, &col2) == 0 ) { - - fr->bgcol[0] = col1.red; - fr->bgcol[1] = col1.green; - fr->bgcol[2] = col1.blue; - fr->bgcol[3] = col1.alpha; - - fr->bgcol2[0] = col2.red; - fr->bgcol2[1] = col2.green; - fr->bgcol2[2] = col2.blue; - fr->bgcol2[3] = col2.alpha; - - fr->grad = grad; - - } -} - - -static char *get_constant(SCInterpreter *scin, unsigned int constant) -{ - struct sc_state *st = &scin->state[scin->j]; - if ( constant >= NUM_SC_CONSTANTS ) return NULL; - return st->constants[constant]; -} - - -void sc_interp_set_constant(SCInterpreter *scin, unsigned int constant, - const char *val) -{ - struct sc_state *st = &scin->state[scin->j]; - if ( constant >= NUM_SC_CONSTANTS ) return; - if ( val == NULL ) return; - st->constants[constant] = strdup(val); -} - - -void sc_interp_save(SCInterpreter *scin) -{ - if ( scin->j+1 == scin->max_state ) { - - struct sc_state *stack_new; - - stack_new = realloc(scin->state, sizeof(struct sc_state) - * (scin->max_state+8)); - if ( stack_new == NULL ) { - fprintf(stderr, "Failed to add to stack.\n"); - return; - } - - scin->state = stack_new; - scin->max_state += 8; - - } - - /* When n_fonts=0, we leave the first font uninitialised. This allows - * the stack to be "bootstrapped", but requires the first caller to do - * set_font and set_colour straight away. */ - scin->state[scin->j+1] = scin->state[scin->j]; - - scin->j++; -} - - -void sc_interp_restore(SCInterpreter *scin) -{ - struct sc_state *st = &scin->state[scin->j]; - - if ( scin->j > 0 ) { - - int i; - - if ( st->fontdesc != scin->state[scin->j-1].fontdesc ) - { - pango_font_description_free(st->fontdesc); - } /* else the font is the same as the previous one, and we - * don't need to free it just yet */ - - for ( i=0; iconstants[i] != scin->state[scin->j-1].constants[i] ) { - free(st->constants[i]); - } /* same logic as above */ - } - } - - scin->j--; -} - - -struct frame *sc_interp_get_frame(SCInterpreter *scin) -{ - struct sc_state *st = &scin->state[scin->j]; - return st->fr; -} - - -static void set_frame(SCInterpreter *scin, struct frame *fr) -{ - struct sc_state *st = &scin->state[scin->j]; - st->fr = fr; -} - - -SCInterpreter *sc_interp_new(PangoContext *pc, PangoLanguage *lang, - ImageStore *is, struct frame *top) -{ - SCInterpreter *scin; - struct sc_state *st; - int i; - - scin = malloc(sizeof(SCInterpreter)); - if ( scin == NULL ) return NULL; - - scin->state = malloc(8*sizeof(struct sc_state)); - if ( scin->state == NULL ) { - free(scin); - return NULL; - } - scin->j = 0; - scin->max_state = 8; - - scin->pc = pc; - scin->is = is; - scin->s_constants = NULL; - scin->p_constants = NULL; - scin->cbl = NULL; - scin->lang = lang; - - /* Initial state */ - st = &scin->state[0]; - st->fr = NULL; - st->paraspace[0] = 0.0; - st->paraspace[1] = 0.0; - st->paraspace[2] = 0.0; - st->paraspace[3] = 0.0; - st->fontdesc = NULL; - st->col[0] = 0.0; - st->col[1] = 0.0; - st->col[2] = 0.0; - st->col[3] = 1.0; - st->alignment = PANGO_ALIGN_LEFT; - for ( i=0; iconstants[i] = NULL; - } - - /* The "ultimate" default font */ - if ( scin->pc != NULL ) { - set_frame(scin, top); - set_font(scin, "Cantarell Regular 14"); - set_colour(scin, "#000000"); - } - - return scin; -} - - -void sc_interp_destroy(SCInterpreter *scin) -{ - /* Empty the stack */ - while ( scin->j > 0 ) { - sc_interp_restore(scin); - } - - if ( scin->state[0].fontdesc != NULL ) { - pango_font_description_free(scin->state[0].fontdesc); - } - - free(scin->state); - free(scin); -} - - -static void set_padding(struct frame *fr, const char *opts) -{ - float p[4]; - - if ( parse_tuple(opts, p) ) return; - - if ( fr == NULL ) return; - - fr->pad_l = p[0]; - fr->pad_r = p[1]; - fr->pad_t = p[2]; - fr->pad_b = p[3]; -} - - -static void set_paraspace(SCInterpreter *scin, const char *opts) -{ - float p[4]; - struct sc_state *st = &scin->state[scin->j]; - - if ( parse_tuple(opts, p) ) return; - - st->paraspace[0] = p[0]; - st->paraspace[1] = p[1]; - st->paraspace[2] = p[2]; - st->paraspace[3] = p[3]; - - set_para_spacing(last_para(sc_interp_get_frame(scin)), p); -} - - -void update_geom(struct frame *fr) -{ - char geom[256]; - snprintf(geom, 255, "%.1fux%.1fu+%.1f+%.1f", - fr->w, fr->h, fr->x, fr->y); - - /* FIXME: What if there are other options? */ - sc_block_set_options(fr->scblocks, strdup(geom)); -} - - -static int calculate_dims(const char *opt, struct frame *parent, - double *wp, double *hp, double *xp, double *yp) -{ - LengthUnits h_units, w_units; - - if ( parse_dims(opt, wp, hp, &w_units, &h_units, xp, yp) ) { - return 1; - } - - if ( w_units == UNITS_FRAC ) { - if ( parent != NULL ) { - double pw = parent->w; - pw -= parent->pad_l; - pw -= parent->pad_r; - *wp = pw * *wp; - } else { - *wp = -1.0; - } - - } - if ( h_units == UNITS_FRAC ) { - if ( parent != NULL ) { - double ph = parent->h; - ph -= parent->pad_t; - ph -= parent->pad_b; - *hp = ph * *hp; - } else { - *hp = -1.0; - } - } - - return 0; -} - - -static int parse_frame_option(const char *opt, struct frame *fr, - struct frame *parent) -{ - if ( (index(opt, 'x') != NULL) && (index(opt, '+') != NULL) - && (index(opt, '+') != rindex(opt, '+')) ) { - return calculate_dims(opt, parent, &fr->w, &fr->h, &fr->x, &fr->y); - } - - fprintf(stderr, _("Unrecognised frame option '%s'\n"), opt); - - return 1; -} - - -static int parse_frame_options(struct frame *fr, struct frame *parent, - const char *opth) -{ - int i; - size_t len; - size_t start; - char *opt; - - if ( opth == NULL ) return 1; - - opt = strdup(opth); - - len = strlen(opt); - start = 0; - - for ( i=0; istate[scin->j]; - struct frame *fr = sc_interp_get_frame(scin); - - if ( fr->paras == NULL ) return; - last_para = fr->paras[fr->n_paras-1]; - - set_newline_at_end(last_para, bl); - - /* The block after the \newpara will always be the first one of the - * next paragraph, by definition, even if it's \f or another \newpara */ - para = create_paragraph(fr, sc_block_next(bl)); - set_para_alignment(para, st->alignment); - set_para_spacing(para, st->paraspace); -} - - -/* Add the SCBlock to the text in 'frame', at the end */ -static int add_text(struct frame *fr, PangoContext *pc, SCBlock *bl, - PangoLanguage *lang, int editable, SCInterpreter *scin, - const char *real_text) -{ - const char *text = sc_block_contents(bl); - PangoFontDescription *fontdesc; - double *col; - struct sc_state *st = &scin->state[scin->j]; - Paragraph *para; - - /* Empty block? */ - if ( text == NULL && real_text == NULL ) return 1; - - fontdesc = sc_interp_get_fontdesc(scin); - col = sc_interp_get_fgcol(scin); - - para = last_para(fr); - if ( (para == NULL) || (para_type(para) != PARA_TYPE_TEXT) ) { - /* Last paragraph is not text. - * or: no paragraphs yet. - * Either way: Create the first one */ - para = create_paragraph(fr, bl); - } - - set_para_alignment(para, st->alignment); - add_run(para, bl, fontdesc, col, real_text); - set_para_spacing(para, st->paraspace); - - return 0; -} - - -static void apply_style(SCInterpreter *scin, Stylesheet *ss, const char *path) -{ - char *result; - - if ( ss == NULL ) return; - - /* Font */ - result = stylesheet_lookup(ss, path, "font"); - if ( result != NULL ) set_font(scin, result); - - /* Foreground colour */ - result = stylesheet_lookup(ss, path, "fgcol"); - if ( result != NULL ) set_colour(scin, result); - - /* Background (vertical gradient) */ - result = stylesheet_lookup(ss, path, "bggradv"); - if ( result != NULL ) set_bggrad(scin, result, GRAD_VERT); - - /* Background (horizontal gradient) */ - result = stylesheet_lookup(ss, path, "bggradh"); - if ( result != NULL ) set_bggrad(scin, result, GRAD_HORIZ); - - /* Background (solid colour) */ - result = stylesheet_lookup(ss, path, "bgcol"); - if ( result != NULL ) set_bgcol(scin, result); - - /* Padding */ - result = stylesheet_lookup(ss, path, "pad"); - if ( result != NULL ) set_padding(sc_interp_get_frame(scin), result); - - /* Paragraph spacing */ - result = stylesheet_lookup(ss, path, "paraspace"); - if ( result != NULL ) set_paraspace(scin, result); - - /* Alignment */ - result = stylesheet_lookup(ss, path, "alignment"); - if ( result != NULL ) { - if ( strcmp(result, "center") == 0 ) { - set_alignment(scin, PANGO_ALIGN_CENTER); - } - if ( strcmp(result, "left") == 0 ) { - set_alignment(scin, PANGO_ALIGN_LEFT); - } - if ( strcmp(result, "right") == 0 ) { - set_alignment(scin, PANGO_ALIGN_RIGHT); - } - } -} - - -static void output_frame(SCInterpreter *scin, SCBlock *bl, Stylesheet *ss, - const char *stylename) -{ - struct frame *fr; - SCBlock *child = sc_block_child(bl); - const char *options = sc_block_options(bl); - char *result; - - fr = add_subframe(sc_interp_get_frame(scin)); - if ( fr == NULL ) { - fprintf(stderr, _("Failed to add frame.\n")); - return; - } - - fr->scblocks = bl; - fr->resizable = 1; - - /* Lowest priority: current state of interpreter */ - set_frame_default_style(fr, scin); - - /* Next priority: geometry from stylesheet */ - result = stylesheet_lookup(ss, stylename, "geometry"); - if ( result != NULL ) { - parse_frame_options(fr, sc_interp_get_frame(scin), result); - } - - /* Highest priority: parameters to \f (or \slidetitle etc) */ - parse_frame_options(fr, sc_interp_get_frame(scin), options); - - maybe_recurse_before(scin, child); - set_frame(scin, fr); - apply_style(scin, ss, stylename); - maybe_recurse_after(scin, child, ss); -} - - -static int check_outputs(SCBlock *bl, SCInterpreter *scin, Stylesheet *ss) -{ - const char *name = sc_block_name(bl); - const char *options = sc_block_options(bl); - - if ( name == NULL ) { - add_text(sc_interp_get_frame(scin), - scin->pc, bl, scin->lang, 1, scin, NULL); - - } else if ( strcmp(name, "image")==0 ) { - double w, h; - char *filename; - if ( parse_image_options(options, sc_interp_get_frame(scin), - &w, &h, &filename) == 0 ) - { - add_image_para(sc_interp_get_frame(scin), bl, - filename, scin->is, w, h, 1); - free(filename); - } else { - fprintf(stderr, _("Invalid image options '%s'\n"), - options); - } - - } else if ( strcmp(name, "f")==0 ) { - output_frame(scin, bl, ss, "$.slide.frame"); - - } else if ( strcmp(name, "slidetitle")==0 ) { - output_frame(scin, bl, ss, "$.slide.slidetitle"); - - } else if ( strcmp(name, "prestitle")==0 ) { - output_frame(scin, bl, ss, "$.slide.prestitle"); - - } else if ( strcmp(name, "author")==0 ) { - output_frame(scin, bl, ss, "$.slide.author"); - - } else if ( strcmp(name, "footer")==0 ) { - output_frame(scin, bl, ss, "$.slide.footer"); - - } else if ( strcmp(name, "newpara")==0 ) { - add_newpara(bl, scin); - - } else if ( strcmp(name, "slidenumber")==0 ) { - char *con = get_constant(scin, SCCONST_SLIDENUMBER); - if ( con != NULL ) { - add_text(sc_interp_get_frame(scin), scin->pc, bl, - scin->lang, 1, scin, con); - } - - } else { - return 0; - } - - return 1; /* handled */ -} - - -int sc_interp_add_block(SCInterpreter *scin, SCBlock *bl, Stylesheet *ss) -{ - const char *name = sc_block_name(bl); - const char *options = sc_block_options(bl); - SCBlock *child = sc_block_child(bl); - - //printf("Running this --------->\n"); - //show_sc_blocks(bl); - //printf("<------------\n"); - - if ( check_callback(scin, bl) ) { - /* Handled in check_callback, don't do anything else */ - - } else if ((sc_interp_get_frame(scin) != NULL) - && check_outputs(bl, scin, ss) ) { - /* Block handled as output thing */ - - } else if ( name == NULL ) { - /* Dummy to ensure name != NULL below */ - - } else if ( strcmp(name, "presentation") == 0 ) { - maybe_recurse_before(scin, child); - apply_style(scin, ss, "$.narrative"); - maybe_recurse_after(scin, child, ss); - - } else if ( strcmp(name, "slide") == 0 ) { - maybe_recurse_before(scin, child); - apply_style(scin, ss, "$.slide"); - maybe_recurse_after(scin, child, ss); - - } else if ( strcmp(name, "font") == 0 ) { - maybe_recurse_before(scin, child); - set_font(scin, options); - maybe_recurse_after(scin, child, ss); - - } else if ( strcmp(name, "fontsize") == 0 ) { - maybe_recurse_before(scin, child); - set_fontsize(scin, options); - maybe_recurse_after(scin, child, ss); - - } else if ( strcmp(name, "bold") == 0 ) { - maybe_recurse_before(scin, child); - set_bold(scin); - maybe_recurse_after(scin, child, ss); - - } else if ( strcmp(name, "oblique") == 0 ) { - maybe_recurse_before(scin, child); - set_oblique(scin); - maybe_recurse_after(scin, child, ss); - - } else if ( strcmp(name, "italic") == 0 ) { - maybe_recurse_before(scin, child); - set_italic(scin); - maybe_recurse_after(scin, child, ss); - - } else if ( strcmp(name, "lalign") == 0 ) { - maybe_recurse_before(scin, child); - set_alignment(scin, PANGO_ALIGN_LEFT); - maybe_recurse_after(scin, child, ss); - - } else if ( strcmp(name, "ralign") == 0 ) { - maybe_recurse_before(scin, child); - set_alignment(scin, PANGO_ALIGN_RIGHT); - maybe_recurse_after(scin, child, ss); - - } else if ( strcmp(name, "center") == 0 ) { - maybe_recurse_before(scin, child); - set_alignment(scin, PANGO_ALIGN_CENTER); - maybe_recurse_after(scin, child, ss); - - } else if ( strcmp(name, "fgcol") == 0 ) { - maybe_recurse_before(scin, child); - set_colour(scin, options); - maybe_recurse_after(scin, child, ss); - - } else if ( strcmp(name, "pad") == 0 ) { - maybe_recurse_before(scin, child); - set_padding(sc_interp_get_frame(scin), options); - maybe_recurse_after(scin, child, ss); - - } else if ( strcmp(name, "bgcol") == 0 ) { - set_bgcol(scin, options); - - } else if ( strcmp(name, "bggradh") == 0 ) { - set_bggrad(scin, options, GRAD_HORIZ); - - } else if ( strcmp(name, "bggradv") == 0 ) { - set_bggrad(scin, options, GRAD_VERT); - - } else if ( strcmp(name, "paraspace") == 0 ) { - maybe_recurse_before(scin, child); - set_paraspace(scin, options); - maybe_recurse_after(scin, child, ss); - - } else { - - fprintf(stderr, "Don't know what to do with this:\n"); - show_sc_block(bl, ""); - - } - - return 0; -} - diff --git a/src/sc_interp.h b/src/sc_interp.h deleted file mode 100644 index 764b532..0000000 --- a/src/sc_interp.h +++ /dev/null @@ -1,80 +0,0 @@ -/* - * sc_interp.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 SC_INTERP_H -#define SC_INTERP_H - -#ifdef HAVE_CONFIG_H -#include -#endif - -#include - -struct frame; - -#define SCCONST_SLIDENUMBER (0) -#define NUM_SC_CONSTANTS (1) - -struct presentation; -typedef struct _scinterp SCInterpreter; -typedef struct _sccallbacklist SCCallbackList; -typedef int (*SCCallbackBoxFunc)(SCInterpreter *scin, SCBlock *bl, - double *w, double *h, void **, void *); -typedef cairo_surface_t *(*SCCallbackDrawFunc)(int w, int h, void *, void *); -typedef int (*SCCallbackClickFunc)(double x, double y, void *, void *); - -#include "frame.h" -#include "imagestore.h" -#include "stylesheet.h" - -extern SCInterpreter *sc_interp_new(PangoContext *pc, PangoLanguage *lang, - ImageStore *is, struct frame *top); -extern void sc_interp_destroy(SCInterpreter *scin); - -extern void sc_interp_save(SCInterpreter *scin); -extern void sc_interp_restore(SCInterpreter *scin); - -extern int sc_interp_add_block(SCInterpreter *scin, SCBlock *bl, Stylesheet *ss); - -extern void sc_interp_set_constant(SCInterpreter *scin, unsigned int constant, - const char *val); - -/* Callback lists */ -extern SCCallbackList *sc_callback_list_new(); -extern void sc_callback_list_free(SCCallbackList *cbl); -extern void sc_callback_list_add_callback(SCCallbackList *cbl, const char *name, - SCCallbackBoxFunc box_func, - SCCallbackDrawFunc draw_func, - SCCallbackClickFunc click_func, - void *vp); -extern void sc_interp_set_callbacks(SCInterpreter *scin, SCCallbackList *cbl); - -/* Get the current state of the interpreter */ -extern struct frame *sc_interp_get_frame(SCInterpreter *scin); -extern PangoFont *sc_interp_get_font(SCInterpreter *scin); -extern PangoFontDescription *sc_interp_get_fontdesc(SCInterpreter *scin); -extern double *sc_interp_get_fgcol(SCInterpreter *scin); - -extern void update_geom(struct frame *fr); - - -#endif /* SC_INTERP_H */ diff --git a/src/sc_parse.c b/src/sc_parse.c deleted file mode 100644 index 78f5799..0000000 --- a/src/sc_parse.c +++ /dev/null @@ -1,856 +0,0 @@ -/* - * sc_parse.c - * - * Copyright © 2013-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 -#include -#include - -#include "sc_parse.h" -#include "utils.h" - -struct _scblock -{ - char *name; - char *options; - char *contents; - - SCBlock *next; - SCBlock *child; -}; - - -SCBlock *sc_block_new() -{ - SCBlock *bl; - - bl = calloc(1, sizeof(SCBlock)); - if ( bl == NULL ) return NULL; - - return bl; -} - - -SCBlock *sc_block_next(const SCBlock *bl) -{ - assert(bl != NULL); - return bl->next; -} - - -SCBlock *sc_block_child(const SCBlock *bl) -{ - return bl->child; -} - - -const char *sc_block_name(const SCBlock *bl) -{ - return bl->name; -} - - -const char *sc_block_options(const SCBlock *bl) -{ - return bl->options; -} - - -const char *sc_block_contents(const SCBlock *bl) -{ - return bl->contents; -} - - -static SCBlock *sc_find_previous(SCBlock *top, SCBlock *find) -{ - if ( top->next == find ) return top; - - if ( top->child != NULL ) { - SCBlock *t = sc_find_previous(top->child, find); - if ( t != NULL ) return t; - } - if ( top->next != NULL ) { - SCBlock *t = sc_find_previous(top->next, find); - if ( t != NULL ) return t; - } - return NULL; -} - - -/* Add a new block before "bl" */ -SCBlock *sc_block_prepend(SCBlock *bl, SCBlock *top) -{ - SCBlock *bln; - SCBlock *prev; - - prev = sc_find_previous(top, bl); - if ( prev == NULL ) { - fprintf(stderr, "Couldn't find previous\n"); - return NULL; - } - - bln = sc_block_new(); - if ( bln == NULL ) return NULL; - - prev->next = bln; - bln->next = bl; - return bln; -} - - -/* Append "bln" after "bl" */ -void sc_block_append_p(SCBlock *bl, SCBlock *bln) -{ - if ( bl != NULL ) { - bln->next = bl->next; - bl->next = bln; - } -} - - -/* Insert a new block after "bl". "name", "options" and "contents" - * will not be copied. Returns the block just created, or NULL on error. - * If *blfp points to NULL, it will updated to point at the new block. */ -SCBlock *sc_block_append(SCBlock *bl, char *name, char *opt, char *contents, - SCBlock **blfp) -{ - SCBlock *bln = sc_block_new(); - - if ( bln == NULL ) return NULL; - - bln->name = name; - bln->options = opt; - bln->contents = contents; - bln->child = NULL; - bln->next = NULL; - - sc_block_append_p(bl, bln); - - if ( (blfp != NULL) && (*blfp == NULL) ) { - *blfp = bln; - } - - return bln; -} - - -/* Insert a new block at the end of the chain starting 'bl'. - * "name", "options" and "contents" will not be copied. Returns the block just - * created, or NULL on error. */ -SCBlock *sc_block_append_end(SCBlock *bl, char *name, char *opt, char *contents) -{ - SCBlock *bln = sc_block_new(); - - if ( bln == NULL ) return NULL; - - while ( bl->next != NULL ) { - bln->next = bl->next; - bl = bl->next; - }; - - return sc_block_append(bl, name, opt, contents, NULL); -} - - -void sc_block_append_block(SCBlock *bl, SCBlock *bln) -{ - - if ( bl == NULL ) return; - - while ( bl->next != NULL ) bl = bl->next; - - bl->next = bln; - bln->next = NULL; -} - - -/* Append a new block to the chain inside "parent". - * "name", "options" and "contents" will not be copied. Returns the block just - * created, or NULL on error. */ -SCBlock *sc_block_append_inside(SCBlock *parent, - char *name, char *opt, char *contents) -{ - SCBlock *bln; - SCBlock *bl; - SCBlock **ptr; - - bl = parent->child; - if ( bl != NULL ) { - while ( bl->next != NULL ) bl = bl->next; - ptr = &bl->next; - } else { - ptr = &parent->child; - } - - bln = sc_block_new(); - if ( bln == NULL ) return NULL; - - bln->name = name; - bln->options = opt; - bln->contents = contents; - bln->child = NULL; - bln->next = NULL; - - *ptr = bln; - - return bln; -} - - -/* Insert a new block to the chain, just after "afterme". - * "name", "options" and "contents" will not be copied. Returns the block just - * created, or NULL on error. */ -SCBlock *sc_block_insert_after(SCBlock *afterme, - char *name, char *opt, char *contents) -{ - SCBlock *bl; - - bl = sc_block_new(); - if ( bl == NULL ) return NULL; - - bl->name = name; - bl->options = opt; - bl->contents = contents; - bl->child = NULL; - bl->next = afterme->next; - afterme->next = bl; - - return bl; -} - - -static SCBlock *sc_find_parent(SCBlock *top, SCBlock *find) -{ - if ( top->child == find ) return top; - if ( top->next == find ) return top; - - if ( top->child != NULL ) { - SCBlock *t = sc_find_parent(top->child, find); - if ( t != NULL ) return t; - } - if ( top->next != NULL ) { - SCBlock *t = sc_find_parent(top->next, find); - if ( t != NULL ) return t; - } - return NULL; -} - - -/* Unlink "deleteme", which is somewhere under "top" */ -static int sc_block_unlink(SCBlock **top, SCBlock *deleteme) -{ - SCBlock *parent = sc_find_parent(*top, deleteme); - if ( parent == NULL ) { - /* Maybe it's the first block? */ - if ( *top == deleteme ) { - fprintf(stderr, "Unlinking at top\n"); - *top = (*top)->next; - return 0; - } else { - fprintf(stderr, "Couldn't find block parent!\n"); - return 1; - } - } - - if ( parent->next == deleteme ) { - parent->next = deleteme->next; - } - - if ( parent->child == deleteme ) { - parent->child = deleteme->next; - } - return 0; -} - - -/* Delete "deleteme", which is somewhere under "top" */ -int sc_block_delete(SCBlock **top, SCBlock *deleteme) -{ - int r; - r = sc_block_unlink(top, deleteme); - if ( !r ) { - sc_block_free(deleteme); - } - return r; -} - - -/* Frees "bl" and all its children (but not the blocks following it) */ -void sc_block_free(SCBlock *bl) -{ - if ( bl == NULL ) return; - - if ( bl->child != NULL ) { - SCBlock *ch = bl->child; - while ( ch != NULL ) { - SCBlock *next = ch->next; - sc_block_free(ch); - ch = next; - } - } - - free(bl); -} - - -/* Serialise one block (including children) */ -char *serialise_sc_block(const SCBlock *bl) -{ - char *a; - SCBlock *ch; - size_t len = 3; - - if ( bl == NULL ) return strdup(""); - - if ( bl->name != NULL ) len += 1+strlen(bl->name); - if ( bl->options != NULL ) len += 2+strlen(bl->options); - if ( bl->contents != NULL ) len += 2+strlen(bl->contents); - a = malloc(len); - if ( a == NULL ) return NULL; - a[0] = '\0'; - - if ( bl->name == NULL ) { - strcat(a, bl->contents); - } else if ( strcmp(bl->name, "newpara") == 0 ) { - strcat(a, "\n"); - - } else { - - strcat(a, "\\"); - strcat(a, bl->name); - if ( bl->options != NULL ) { - strcat(a, "["); - strcat(a, bl->options); - strcat(a, "]"); - } - if ( (bl->contents != NULL) || (bl->child != NULL) ) { - strcat(a, "{"); - } - if ( bl->contents != NULL ) { - strcat(a, bl->contents); - } - - /* Special case to prevent "\somethingSome text" */ - if ( (bl->name != NULL) && (bl->options == NULL) - && (bl->contents == NULL) && (bl->next != NULL) - && (bl->next->name == NULL) && (bl->child == NULL) ) - { - strcat(a, "{}"); - } - - } - - /* Add ALL child blocks of this one */ - ch = bl->child; - while ( ch != NULL ) { - - char *anew; - char *c = serialise_sc_block(ch); - if ( c == NULL ) { - free(a); - return NULL; - } - - len += strlen(c); - - anew = realloc(a, len); - if ( anew == NULL ) { - return NULL; - } else { - a = anew; - } - - strcat(a, c); - free(c); - - ch = ch->next; - - } - - if ( (bl->name != NULL) && - ((bl->contents != NULL) || (bl->child != NULL)) ) { - strcat(a, "}"); - } - - return a; -} - - -int save_sc_block(GOutputStream *fh, const SCBlock *bl) -{ - while ( bl != NULL ) { - GError *error = NULL; - char *a = serialise_sc_block(bl); - gssize r; - if ( a == NULL ) { - fprintf(stderr, "Failed to serialise block\n"); - return 1; - } - r = g_output_stream_write(fh, a, strlen(a), NULL, &error); - if ( r == -1 ) { - fprintf(stderr, "Write failed: %s\n", error->message); - return 1; - } - free(a); - bl = bl->next; - } - return 0; -} - - -static void recursive_show_sc_blocks(const char *prefix, const SCBlock *bl) -{ - while ( bl != NULL ) { - show_sc_block(bl, prefix); - bl = bl->next; - } -} - - -void show_sc_block(const SCBlock *bl, const char *prefix) -{ - printf("%s (%p) ", prefix, bl); - if ( bl == NULL ) return; - if ( bl->name != NULL ) printf("\\%s ", bl->name); - if ( bl->options != NULL ) printf("[%s] ", bl->options); - if ( bl->contents != NULL ) printf("{%s} ", bl->contents); - printf("\n"); - - if ( bl->child != NULL ) { - char new_prefix[strlen(prefix)+3]; - strcpy(new_prefix, " "); - strcat(new_prefix, prefix); - recursive_show_sc_blocks(new_prefix, bl->child); - } -} - - -void show_sc_blocks(const SCBlock *bl) -{ - recursive_show_sc_blocks("", bl); -} - - -static int get_subexpr(const char *sc, char *bk, char **pcontents, int *err) -{ - size_t ml; - int i; - int bct = 1; - int found = 0; - char *contents; - - *err = 0; - - ml = strlen(sc); - contents = malloc(ml+1); - if ( contents == NULL ) { - *err = -1; - return 0; - } - *pcontents = contents; - - for ( i=0; iname = strdup("newpara"); - bl->contents = NULL; - nb = bl; - } else { - sc_block_append(bl, strdup("newpara"), - NULL, NULL, &nb); - } - - /* Add any text after the \n */ - if ( strlen(npos+1) > 0 ) { - sc_block_append(nb, NULL, NULL, - strdup(npos+1), &nb); - } - npos[0] = '\0'; - } - } - - if ( sc_block_child(bl) != NULL ) { - separate_newlines(sc_block_child(bl)); - } - - bl = sc_block_next(bl); - - } -} - - -SCBlock *sc_parse(const char *sc) -{ - SCBlock *bl; - SCBlock *blf = NULL; - char *tbuf; - size_t len, i, j; - - if ( sc == NULL ) return NULL; - - if ( strlen(sc) == 0 ) { - SCBlock *bl = sc_block_new(); - sc_block_set_contents(bl, g_strdup("")); - return bl; - } - - bl = NULL; - - len = strlen(sc); - tbuf = malloc(len+1); - if ( tbuf == NULL ) { - sc_block_free(bl); - return NULL; - } - - i = 0; j = 0; - do { - - if ( sc[i] == '\\' ) { - - int err; - char *name = NULL; - char *opt = NULL; - char *contents = NULL; - - /* Is this an escaped backslash? */ - if ( sc[i+1] == '\\' ) { - tbuf[j++] = '\\'; - i += 2; /* Skip both backslashes */ - continue; - } - - /* No, it's a real block. Dispatch the previous block */ - if ( j != 0 ) { - tbuf[j] = '\0'; - bl = sc_block_append(bl, NULL, NULL, - strdup(tbuf), &blf); - if ( bl == NULL ) { - fprintf(stderr, "Block add failed.\n"); - sc_block_free(blf); - free(tbuf); - return NULL; - } - j = 0; - } - - i += read_block(sc+i+1, &name, &opt, &contents, &err); - if ( err ) { - printf(_("Parse error\n")); - sc_block_free(blf); - free(tbuf); - return NULL; - } - - bl = sc_block_append(bl, name, opt, contents, &blf); - if ( bl == NULL ) { - fprintf(stderr, "Block add failed.\n"); - sc_block_free(blf); - free(tbuf); - return NULL; - } - bl->child = sc_parse(contents); - free(bl->contents); - bl->contents = NULL; - - } else { - - tbuf[j++] = sc[i++]; - } - - } while ( i 0 ) { - - /* Leftover buffer is empty? */ - if ( (j==1) && (tbuf[0]=='\0') ) return bl; - - tbuf[j] = '\0'; - bl = sc_block_append(bl, NULL, NULL, tbuf, &blf); - if ( bl == NULL ) { - fprintf(stderr, "Block add failed.\n"); - sc_block_free(blf); - free(tbuf); - return NULL; - } - j = 0; - } - - separate_newlines(blf); - - return blf; -} - - -void sc_block_set_name(SCBlock *bl, char *nam) -{ - if ( bl == NULL ) { - fprintf(stderr, "sc_block_set_name: NULL block\n"); - return; - } - free(bl->name); - bl->name = nam; -} - - -void sc_block_set_options(SCBlock *bl, char *opt) -{ - free(bl->options); - bl->options = opt; -} - - -void sc_block_set_contents(SCBlock *bl, char *con) -{ - g_free(bl->contents); - bl->contents = con; -} - - -void sc_insert_text(SCBlock *b1, size_t o1, const char *t) -{ - size_t len; - char *cnew; - char *tmp; - char *p1; - - if ( b1->contents == NULL ) { - b1->contents = strdup(t); - return; - } - len = strlen(b1->contents)+1+strlen(t); - - cnew = realloc(b1->contents, len); - if ( cnew == NULL ) return; - - tmp = malloc(len); - if ( tmp == NULL ) { - free(cnew); - return; - } - - p1 = cnew + o1; - strcpy(tmp, p1); - strcpy(p1, t); - strcpy(p1+strlen(t), tmp); - free(tmp); - b1->contents = cnew; -} - - -void sc_insert_block(SCBlock *b1, int o1, SCBlock *ins) -{ - SCBlock *second; - char *p1 = g_utf8_offset_to_pointer(b1->contents, o1); - - /* Create a new block containing the second half of b1 */ - second = sc_block_new(); - sc_block_set_contents(second, g_strdup(p1)); - - /* Chop off b1 at the insertion point */ - sc_block_set_contents(b1, g_utf8_substring(b1->contents, 0, o1)); - - /* Link the new block into the chain */ - SCBlock *old_next = b1->next; - b1->next = ins; - ins->next = second; - second->next = old_next; -} - - -/* Delete text from SCBlock contents. o2=-1 means "to the end". - * Returns the number of bytes deleted. */ -size_t scblock_delete_text(SCBlock *b, ssize_t o1, ssize_t o2) -{ - size_t len; - - if ( b->contents == NULL ) { - fprintf(stderr, "Deleting text from block \\%s\n", b->name); - return 0; - } - - if ( (o2 != -1) && (o1 > o2) ) { - ssize_t t = o2; - o2 = o1; - o1 = t; - } - - len = strlen(b->contents); - if ( o2 < 0 ) o2 = len; - if ( (o1 >= o2) || (o1 > len) || (o2 > len) ) { - fprintf(stderr, "Invalid delete: %i %i %i\n", - (int)o1, (int)o2, (int)len); - return 0; - } - memmove(b->contents+o1, b->contents+o2, len-o2+1); - - return o2-o1; -} - - -static char *s_strdup(const char *a) -{ - if ( a == NULL ) return NULL; - return strdup(a); -} - - -SCBlock *sc_block_split(SCBlock *bl, size_t pos) -{ - SCBlock *n = sc_block_new(); - - if ( bl->child != NULL ) { - fprintf(stderr, "Splitting a block with a child!\n"); - return NULL; - } - - /* Second block */ - n->name = s_strdup(bl->name); - n->options = s_strdup(bl->options); - if ( bl->contents != NULL ) { - n->contents = strdup(bl->contents+pos); - /* Truncate the first block */ - bl->contents[pos] = '\0'; - } else { - n->contents = NULL; - } - - n->next = bl->next; - bl->next = n; - - return n; -} - - -/* Return a new block which is the parent for "bl" */ -SCBlock *sc_block_new_parent(SCBlock *bl, const char *name) -{ - SCBlock *n = sc_block_new(); - if ( n == NULL ) return NULL; - n->name = s_strdup(name); - n->child = bl; - return n; -} diff --git a/src/sc_parse.h b/src/sc_parse.h deleted file mode 100644 index 17ce2dd..0000000 --- a/src/sc_parse.h +++ /dev/null @@ -1,85 +0,0 @@ -/* - * sc_parse.h - * - * Copyright © 2013-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 SC_PARSE_H -#define SC_PARSE_H - -#ifdef HAVE_CONFIG_H -#include -#endif - -#include -#include - -typedef struct _scblock SCBlock; - -extern SCBlock *sc_parse(const char *sc); - -extern SCBlock *sc_block_new(void); -extern void sc_block_free(SCBlock *bl); - -extern SCBlock *sc_block_next(const SCBlock *bl); -extern SCBlock *sc_block_child(const SCBlock *bl); -extern const char *sc_block_name(const SCBlock *bl); -extern const char *sc_block_options(const SCBlock *bl); -extern const char *sc_block_contents(const SCBlock *bl); - -extern SCBlock *sc_block_append(SCBlock *bl, - char *name, char *opt, char *contents, - SCBlock **blfp); - -extern SCBlock *sc_block_new_parent(SCBlock *bl, const char *name); - -extern SCBlock *sc_block_prepend(SCBlock *bl, SCBlock *top); - -extern void sc_block_append_p(SCBlock *bl, SCBlock *bln); - -extern void sc_block_append_block(SCBlock *bl, SCBlock *bln); - -extern SCBlock *sc_block_append_end(SCBlock *bl, - char *name, char *opt, char *contents); - -extern SCBlock *sc_block_append_inside(SCBlock *parent, - char *name, char *opt, char *contents); - -extern SCBlock *sc_block_insert_after(SCBlock *afterme, - char *name, char *opt, char *contents); - -extern int sc_block_delete(SCBlock **top, SCBlock *deleteme); - - -extern void sc_block_set_name(SCBlock *bl, char *nam); -extern void sc_block_set_options(SCBlock *bl, char *opt); -extern void sc_block_set_contents(SCBlock *bl, char *con); -extern void sc_insert_text(SCBlock *b1, size_t o1, const char *t); -extern void sc_insert_block(SCBlock *b1, int o1, SCBlock *ins); -extern SCBlock *sc_block_split(SCBlock *bl, size_t pos); - -extern void show_sc_blocks(const SCBlock *bl); -extern void show_sc_block(const SCBlock *bl, const char *prefix); - -extern char *serialise_sc_block(const SCBlock *bl); -extern int save_sc_block(GOutputStream *fh, const SCBlock *bl); - -extern size_t scblock_delete_text(SCBlock *b, ssize_t o1, ssize_t o2); - -#endif /* SC_PARSE_H */ diff --git a/src/slide_window.c b/src/slide_window.c deleted file mode 100644 index ed37a50..0000000 --- a/src/slide_window.c +++ /dev/null @@ -1,282 +0,0 @@ -/* - * slide_window.c - * - * Copyright © 2013-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 -#include -#include - -#include "colloquium.h" -#include "presentation.h" -#include "slide_window.h" -#include "render.h" -#include "frame.h" -#include "slideshow.h" -#include "pr_clock.h" -#include "sc_parse.h" -#include "sc_interp.h" -#include "sc_editor.h" -#include "utils.h" - - -struct _slidewindow -{ - struct presentation *p; - GtkWidget *window; - GtkToolItem *bfirst; - GtkToolItem *bprev; - GtkToolItem *bnext; - GtkToolItem *blast; - - /* The slide being displayed. Note that the SCEditor is using the - * child of this block, but we need to keep a record of it for changing - * the slide. */ - SCBlock *scblocks; - - SCEditor *sceditor; -}; - - -static void insert_slidetitle_sig(GSimpleAction *action, GVariant *parameter, - gpointer vp) -{ - SlideWindow *sw = vp; - sc_editor_add_storycode(sw->sceditor, "\\slidetitle{Slide title}"); -} - - -static void paste_sig(GSimpleAction *action, GVariant *parameter, - gpointer vp) -{ - SlideWindow *sw = vp; - sc_editor_paste(sw->sceditor); -} - - -static void copy_frame_sig(GSimpleAction *action, GVariant *parameter, - gpointer vp) -{ - SlideWindow *sw = vp; - sc_editor_copy_selected_frame(sw->sceditor); -} - - -static void delete_frame_sig(GSimpleAction *action, GVariant *parameter, - gpointer vp) -{ - SlideWindow *sw = vp; - sc_editor_delete_selected_frame(sw->sceditor); -} - - -void slide_window_update(SlideWindow *sw) -{ - sc_editor_set_scblock(sw->sceditor, sw->scblocks); -} - - -/* Change the editor's slide to "np" */ -static void change_edit_slide(SlideWindow *sw, SCBlock *np) -{ - sc_editor_set_slidenum(sw->sceditor, slide_number(sw->p, np)); - sc_editor_set_scblock(sw->sceditor, np); - sw->scblocks = np; -} - - -static void change_slide_first(SlideWindow *sw) -{ - change_edit_slide(sw, first_slide(sw->p)); -} - - -static void change_slide_backwards(SlideWindow *sw) -{ - SCBlock *tt; - tt = prev_slide(sw->p, sw->scblocks); - if ( tt == NULL ) return; - change_edit_slide(sw, tt); -} - - -static void change_slide_forwards(SlideWindow *sw) -{ - SCBlock *tt; - tt = next_slide(sw->p, sw->scblocks); - if ( tt == NULL ) return; - change_edit_slide(sw, tt); -} - - -static void change_slide_last(SlideWindow *sw) -{ - change_edit_slide(sw, last_slide(sw->p)); -} - - -static void first_slide_sig(GSimpleAction *action, GVariant *parameter, - gpointer vp) -{ - SlideWindow *sw = vp; - change_slide_first(sw); -} - - -static void prev_slide_sig(GSimpleAction *action, GVariant *parameter, - gpointer vp) -{ - SlideWindow *sw = vp; - change_slide_backwards(sw); -} - - -static void next_slide_sig(GSimpleAction *action, GVariant *parameter, - gpointer vp) -{ - SlideWindow *sw = vp; - change_slide_forwards(sw); -} - - -static void last_slide_sig(GSimpleAction *action, GVariant *parameter, - gpointer vp) -{ - SlideWindow *sw = vp; - change_slide_last(sw); -} - - -static gboolean sw_close_sig(GtkWidget *w, SlideWindow *sw) -{ - narrative_window_sw_closed(sw->p->narrative_window, sw); - return FALSE; -} - - -static gboolean sw_key_press_sig(GtkWidget *da, GdkEventKey *event, - SlideWindow *sw) -{ - switch ( event->keyval ) { - - case GDK_KEY_Page_Up : - change_slide_backwards(sw); - break; - - case GDK_KEY_Page_Down : - change_slide_forwards(sw); - break; - - } - - return FALSE; -} - - -static void about_sig(GSimpleAction *action, GVariant *parameter, gpointer vp) -{ - SlideWindow *sw = vp; - open_about_dialog(sw->window); -} - - -GActionEntry sw_entries[] = { - - { "about", about_sig, NULL, NULL, NULL }, - { "paste", paste_sig, NULL, NULL, NULL }, - { "copyframe", copy_frame_sig, NULL, NULL, NULL }, - { "deleteframe", delete_frame_sig, NULL, NULL, NULL }, - { "first", first_slide_sig, NULL, NULL, NULL }, - { "prev", prev_slide_sig, NULL, NULL, NULL }, - { "next", next_slide_sig, NULL, NULL, NULL }, - { "last", last_slide_sig, NULL, NULL, NULL }, - { "slidetitle", insert_slidetitle_sig, NULL, NULL, NULL }, -}; - - -SlideWindow *slide_window_open(struct presentation *p, SCBlock *scblocks, - GApplication *papp) -{ - GtkWidget *window; - SlideWindow *sw; - Colloquium *app = COLLOQUIUM(papp); - - sw = calloc(1, sizeof(SlideWindow)); - if ( sw == NULL ) return NULL; - - window = gtk_application_window_new(GTK_APPLICATION(app)); - gtk_window_set_role(GTK_WINDOW(window), "slide"); - sw->window = window; - sw->p = p; - - g_action_map_add_action_entries(G_ACTION_MAP(window), sw_entries, - G_N_ELEMENTS(sw_entries), sw); - - g_signal_connect(G_OBJECT(window), "destroy", - G_CALLBACK(sw_close_sig), sw); - - sw->scblocks = scblocks; - sw->sceditor = sc_editor_new(scblocks, p->stylesheet, p->lang, - colloquium_get_imagestore(app)); - sc_editor_set_slidenum(sw->sceditor, slide_number(sw->p, scblocks)); - sc_editor_set_scale(sw->sceditor, 1); - sc_editor_set_imagestore(sw->sceditor, p->is); - -// scroll = gtk_scrolled_window_new(NULL, NULL); -// gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), -// GTK_POLICY_AUTOMATIC, -// GTK_POLICY_AUTOMATIC); -// gtk_container_add(GTK_CONTAINER(scroll), GTK_WIDGET(sw->sceditor)); -// gtk_window_set_focus(GTK_WINDOW(window), GTK_WIDGET(sw->sceditor)); - g_signal_connect(G_OBJECT(sw->sceditor), "key-press-event", - G_CALLBACK(sw_key_press_sig), sw); - - sc_editor_set_logical_size(sw->sceditor, - p->slide_width, p->slide_height); - - gtk_window_set_default_size(GTK_WINDOW(window), - p->slide_width, p->slide_height); - - gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(sw->sceditor)); - - /* Default size */ -// gtk_scrolled_window_set_min_content_width(GTK_SCROLLED_WINDOW(scroll), -// 1024); -// gtk_scrolled_window_set_min_content_height(GTK_SCROLLED_WINDOW(scroll), -// 768); - gtk_window_set_resizable(GTK_WINDOW(sw->window), TRUE); - - gtk_widget_show_all(window); -// gtk_scrolled_window_set_min_content_width(GTK_SCROLLED_WINDOW(scroll), -// 100); -// gtk_scrolled_window_set_min_content_height(GTK_SCROLLED_WINDOW(scroll), -// 100); - - return sw; -} diff --git a/src/slide_window.h b/src/slide_window.h deleted file mode 100644 index 681ec39..0000000 --- a/src/slide_window.h +++ /dev/null @@ -1,37 +0,0 @@ -/* - * slide_window.h - * - * Copyright © 2013-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 SLIDEWINDOW_H -#define SLIDEWINDOW_H - -#ifdef HAVE_CONFIG_H -#include -#endif - -typedef struct _slidewindow SlideWindow; - -extern SlideWindow *slide_window_open(struct presentation *p, SCBlock *scblocks, - GApplication *app); - -extern void slide_window_update(SlideWindow *sw); - -#endif /* SLIDEWINDOW_H */ diff --git a/src/slideshow.c b/src/slideshow.c deleted file mode 100644 index 6e1c49d..0000000 --- a/src/slideshow.c +++ /dev/null @@ -1,240 +0,0 @@ -/* - * slideshow.c - * - * Copyright © 2013-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 - -#include "colloquium.h" -#include "presentation.h" -#include "render.h" -#include "pr_clock.h" -#include "frame.h" -#include "utils.h" - -G_DEFINE_TYPE_WITH_CODE(SCSlideshow, sc_slideshow, GTK_TYPE_WINDOW, NULL) - - -static void sc_slideshow_init(SCSlideshow *ss) -{ -} - - -void sc_slideshow_class_init(SCSlideshowClass *klass) -{ -} - - -static void slideshow_rerender(SCSlideshow *ss) -{ - int n; - gint w, h; - - if ( ss->cur_slide == NULL ) return; - - if ( ss->surface != NULL ) { - cairo_surface_destroy(ss->surface); - } - - n = slide_number(ss->p, ss->cur_slide); - ss->surface = render_sc(ss->cur_slide, - ss->slide_width, ss->slide_height, - ss->p->slide_width, ss->p->slide_height, - ss->p->stylesheet, NULL, ss->p->is, n, - &ss->top, ss->p->lang); - - w = gtk_widget_get_allocated_width(GTK_WIDGET(ss->drawingarea)); - h = gtk_widget_get_allocated_height(GTK_WIDGET(ss->drawingarea)); - - gtk_widget_queue_draw_area(ss->drawingarea, 0, 0, w, h); -} - - -static gint ssh_destroy_sig(GtkWidget *widget, SCSlideshow *ss) -{ - if ( ss->blank_cursor != NULL ) { - g_object_unref(ss->blank_cursor); - } - if ( ss->surface != NULL ) { - cairo_surface_destroy(ss->surface); - } - if ( ss->inhibit_cookie ) { - gtk_application_uninhibit(ss->app, ss->inhibit_cookie); - } - return FALSE; -} - - -static gboolean ss_draw_sig(GtkWidget *da, cairo_t *cr, SCSlideshow *ss) -{ - double width, height; - - width = gtk_widget_get_allocated_width(GTK_WIDGET(da)); - height = gtk_widget_get_allocated_height(GTK_WIDGET(da)); - - /* Overall background */ - cairo_rectangle(cr, 0.0, 0.0, width, height); - cairo_set_source_rgb(cr, 0.0, 0.0, 0.0); - cairo_fill(cr); - - if ( !ss->blank ) { - - /* Draw the slide from the cache */ - cairo_rectangle(cr, ss->xoff, ss->yoff, - ss->slide_width, ss->slide_height); - cairo_set_source_surface(cr, ss->surface, ss->xoff, ss->yoff); - cairo_fill(cr); - - } - - return FALSE; -} - - -static gboolean ss_realize_sig(GtkWidget *w, SCSlideshow *ss) -{ - if ( (ss->app == NULL) || colloquium_get_hidepointer(COLLOQUIUM(ss->app)) ) { - - /* Hide the pointer */ - GdkWindow *win; - win = gtk_widget_get_window(w); - ss->blank_cursor = gdk_cursor_new_for_display(gdk_display_get_default(), - GDK_BLANK_CURSOR); - gdk_window_set_cursor(GDK_WINDOW(win), ss->blank_cursor); - - } else { - ss->blank_cursor = NULL; - } - - slideshow_rerender(ss); - - return FALSE; -} - - -static void ss_size_sig(GtkWidget *widget, GdkRectangle *rect, SCSlideshow *ss) -{ - const double sw = ss->p->slide_width; - const double sh = ss->p->slide_height; - - if ( sw/sh > (double)rect->width/rect->height ) { - /* Slide is too wide. Letterboxing top/bottom */ - ss->slide_width = rect->width; - ss->slide_height = rect->width * sh/sw; - } else { - /* Letterboxing at sides */ - ss->slide_width = rect->height * sw/sh; - ss->slide_height = rect->height; - } - - ss->xoff = (rect->width - ss->slide_width)/2.0; - ss->yoff = (rect->height - ss->slide_height)/2.0; - - printf("screen %i %i\n", rect->width, rect->height); - printf("slide %f %f\n", sw, sh); - printf("rendering slide at %i %i\n", ss->slide_width, ss->slide_height); - printf("offset %i %i\n", ss->xoff, ss->yoff); - - slideshow_rerender(ss); -} - - -void sc_slideshow_set_slide(SCSlideshow *ss, SCBlock *ns) -{ - ss->cur_slide = ns; - slideshow_rerender(ss); -} - - -SCSlideshow *sc_slideshow_new(struct presentation *p, GtkApplication *app) -{ - GdkDisplay *display; - int n_monitors; - SCSlideshow *ss; - - ss = g_object_new(SC_TYPE_SLIDESHOW, NULL); - if ( ss == NULL ) return NULL; - - ss->blank = 0; - ss->p = p; - ss->cur_slide = NULL; - ss->blank_cursor = NULL; - ss->surface = NULL; - ss->app = app; - - ss->drawingarea = gtk_drawing_area_new(); - gtk_container_add(GTK_CONTAINER(ss), ss->drawingarea); - - gtk_widget_set_can_focus(GTK_WIDGET(ss->drawingarea), TRUE); - gtk_widget_add_events(GTK_WIDGET(ss->drawingarea), - GDK_KEY_PRESS_MASK); - - g_signal_connect(G_OBJECT(ss), "destroy", - G_CALLBACK(ssh_destroy_sig), ss); - g_signal_connect(G_OBJECT(ss), "realize", - G_CALLBACK(ss_realize_sig), ss); - g_signal_connect(G_OBJECT(ss), "size-allocate", - G_CALLBACK(ss_size_sig), ss); - - g_signal_connect(G_OBJECT(ss->drawingarea), "draw", - G_CALLBACK(ss_draw_sig), ss); - - gtk_widget_grab_focus(GTK_WIDGET(ss->drawingarea)); - - display = gdk_display_get_default(); - n_monitors = gdk_display_get_n_monitors(display); - - GdkMonitor *mon_ss; - if ( n_monitors == 1 ) { - mon_ss = gdk_display_get_primary_monitor(display); - printf(_("Single monitor mode\n")); - ss->single_monitor = 1; - } else { - mon_ss = gdk_display_get_monitor(display, 1); - printf(_("Dual monitor mode\n")); - ss->single_monitor = 0; - } - - /* Workaround because gtk_window_fullscreen_on_monitor doesn't work */ - GdkRectangle rect; - gdk_monitor_get_geometry(mon_ss, &rect); - gtk_window_move(GTK_WINDOW(ss), rect.x, rect.y); - gtk_window_fullscreen(GTK_WINDOW(ss)); - - ss->linked = 1; - - if ( app != NULL ) { - ss->inhibit_cookie = gtk_application_inhibit(app, GTK_WINDOW(ss), - GTK_APPLICATION_INHIBIT_IDLE, - _("Presentation slide show is running")); - } - - return ss; -} - diff --git a/src/slideshow.h b/src/slideshow.h deleted file mode 100644 index ff16d73..0000000 --- a/src/slideshow.h +++ /dev/null @@ -1,83 +0,0 @@ -/* - * slideshow.h - * - * Copyright © 2013-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 SLIDESHOW_H -#define SLIDESHOW_H - -#ifdef HAVE_CONFIG_H -#include -#endif - -#include - -#define SC_TYPE_SLIDESHOW (sc_slideshow_get_type()) - -#define SC_SLIDESHOW(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), \ - SC_TYPE_SLIDESHOW, SCEditor)) - -#define SC_IS_SLIDESHOW(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), \ - SC_TYPE_SLIDESHOW)) - -#define SC_SLIDESHOW_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((obj), \ - SC_TYPE_SLIDESHOW, SCEditorClass)) - -#define SC_IS_SLIDESHOW_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((obj), \ - SC_TYPE_SLIDESHOW)) - -#define SC_SLIDESHOW_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), \ - SC_TYPE_SLIDESHOW, SCSlideShowClass)) - -struct _scslideshow -{ - GtkWindow parent_instance; - - /* */ - struct presentation *p; - SCBlock *cur_slide; - GtkWidget *drawingarea; - GdkCursor *blank_cursor; - int blank; - int slide_width; - int slide_height; - int xoff; - int yoff; - int linked; - cairo_surface_t *surface; - struct frame *top; - int single_monitor; - uint inhibit_cookie; - GtkApplication *app; -}; - - -struct _scslideshowclass -{ - GtkWindowClass parent_class; -}; - -typedef struct _scslideshow SCSlideshow; -typedef struct _scslideshowclass SCSlideshowClass; - -extern SCSlideshow *sc_slideshow_new(struct presentation *p, GtkApplication *app); -extern void sc_slideshow_set_slide(SCSlideshow *ss, SCBlock *ns); - -#endif /* SLIDESHOW_H */ diff --git a/src/stylesheet.c b/src/stylesheet.c deleted file mode 100644 index a6e2531..0000000 --- a/src/stylesheet.c +++ /dev/null @@ -1,288 +0,0 @@ -/* - * stylesheet.c - * - * Copyright © 2013-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 -#include - -#include "stylesheet.h" -#include "utils.h" - - -struct _stylesheet { - JsonNode *root; -}; - - -static int find_comma(const char *a) -{ - int i = 0; - int in_brackets = 0; - size_t len = strlen(a); - - do { - if ( (a[i] == ',') && !in_brackets ) return i; - if ( a[i] == '(' ) in_brackets++; - if ( a[i] == ')' ) in_brackets--; - i++; - } while ( i < len ); - return 0; -} - - -int parse_colour_duo(const char *a, GdkRGBA *col1, GdkRGBA *col2) -{ - char *acopy; - int cpos; - - acopy = strdup(a); - if ( acopy == NULL ) return 1; - - cpos = find_comma(acopy); - if ( cpos == 0 ) { - fprintf(stderr, _("Invalid background gradient '%s'\n"), a); - return 1; - } - - acopy[cpos] = '\0'; - - if ( gdk_rgba_parse(col1, acopy) != TRUE ) { - fprintf(stderr, _("Failed to parse colour: %s\n"), acopy); - } - if ( gdk_rgba_parse(col2, &acopy[cpos+1]) != TRUE ) { - fprintf(stderr, _("Failed to parse colour: %s\n"), &acopy[cpos+1]); - } - - free(acopy); - return 0; -} - - -Stylesheet *stylesheet_load(GFile *file) -{ - JsonParser *parser; - gboolean r; - GError *err = NULL; - Stylesheet *ss; - char *everything; - gsize len; - - printf("Trying stylesheet '%s'\n", g_file_get_uri(file)); - - ss = calloc(1, sizeof(Stylesheet)); - if ( ss == NULL ) return NULL; - - parser = json_parser_new(); - - if ( !g_file_load_contents(file, NULL, &everything, &len, NULL, NULL) ) { - fprintf(stderr, _("Failed to load stylesheet '%s'\n"), - g_file_get_uri(file)); - free(ss); - return NULL; - } - - r = json_parser_load_from_data(parser, everything, len, &err); - if ( r == FALSE ) { - fprintf(stderr, _("Failed to load stylesheet: %s\n"), - err->message); - free(ss); - return NULL; - } - - ss->root = json_parser_steal_root(parser); - g_object_unref(parser); - - return ss; -} - - -static JsonObject *find_stylesheet_object(Stylesheet *ss, const char *path, - JsonNode **freeme) -{ - JsonNode *node; - JsonObject *obj; - JsonArray *array; - GError *err = NULL; - - node = json_path_query(path, ss->root, &err); - array = json_node_get_array(node); - - if ( json_array_get_length(array) == 0 ) { - json_node_unref(node); - return NULL; - } - - if ( json_array_get_length(array) > 1 ) { - json_node_unref(node); - fprintf(stderr, "More than one result in SS lookup (%s)!\n", path); - return NULL; - } - - obj = json_array_get_object_element(array, 0); - if ( obj == NULL ) { - printf("%s not a JSON object\n", path); - json_node_unref(node); - return NULL; - } - - *freeme = node; - return obj; -} - - -char *stylesheet_lookup(Stylesheet *ss, const char *path, const char *key) -{ - JsonObject *obj; - char *ret = NULL; - JsonNode *node = NULL; - - if ( ss == NULL ) { - fprintf(stderr, "No stylesheet!\n"); - return NULL; - } - - obj = find_stylesheet_object(ss, path, &node); - if ( obj == NULL ) return NULL; - - if ( json_object_has_member(obj, key) ) { - - const gchar *v; - v = json_object_get_string_member(obj, key); - if ( v != NULL ) { - ret = strdup(v); - } else { - fprintf(stderr, "Error retrieving %s.%s\n", path, key); - } - - } /* else not found, too bad */ - - if ( node != NULL ) json_node_unref(node); - return ret; -} - - -int stylesheet_set(Stylesheet *ss, const char *path, const char *key, - const char *new_val) -{ - JsonObject *obj; - JsonNode *node = NULL; - int r = 1; - - if ( ss == NULL ) { - fprintf(stderr, "No stylesheet!\n"); - return 1; - } - - obj = find_stylesheet_object(ss, path, &node); - if ( obj != NULL ) { - json_object_set_string_member(obj, key, new_val); - r = 0; - } /* else most likely the object (e.g. "$.slide", "$.slide.frame", - * "$.narrative" etc doesn't exist */ - - if ( node != NULL ) json_node_unref(node); - return r; -} - - -int stylesheet_delete(Stylesheet *ss, const char *path, const char *key) -{ - JsonObject *obj; - JsonNode *node = NULL; - int r = 1; - - if ( ss == NULL ) { - fprintf(stderr, "No stylesheet!\n"); - return 1; - } - - obj = find_stylesheet_object(ss, path, &node); - if ( obj != NULL ) { - json_object_remove_member(obj, key); - r = 0; - } /* else most likely the object (e.g. "$.slide", "$.slide.frame", - * "$.narrative" etc doesn't exist */ - - if ( node != NULL ) json_node_unref(node); - return r; -} - - -void stylesheet_free(Stylesheet *ss) -{ - json_node_unref(ss->root); - free(ss); -} - - -int stylesheet_save(Stylesheet *ss, GFile *file) -{ - JsonGenerator *gen; - GError *error = NULL; - GFileOutputStream *fh; - - fh = g_file_replace(file, NULL, FALSE, G_FILE_CREATE_NONE, NULL, &error); - if ( fh == NULL ) { - fprintf(stderr, _("Open failed: %s\n"), error->message); - return 1; - } - - gen = json_generator_new(); - json_generator_set_root(gen, ss->root); - json_generator_set_pretty(gen, TRUE); - json_generator_set_indent(gen, 1); - json_generator_set_indent_char(gen, '\t'); - if ( !json_generator_to_stream(gen, G_OUTPUT_STREAM(fh), NULL, &error) ) { - fprintf(stderr, _("Open failed: %s\n"), error->message); - return 1; - } - g_object_unref(fh); - return 0; -} - - -char *stylesheet_data(Stylesheet *ss) -{ - return json_to_string(ss->root, FALSE); -} - - -void stylesheet_set_data(Stylesheet *ss, const char *data) -{ - JsonNode *new_root; - GError *err = NULL; - new_root = json_from_string(data, &err); - if ( new_root == NULL ) { - fprintf(stderr, "Internal parse error: %s\n", err->message); - return; - } - json_node_unref(ss->root); - ss->root = new_root; -} diff --git a/src/stylesheet.h b/src/stylesheet.h deleted file mode 100644 index 16d0a0a..0000000 --- a/src/stylesheet.h +++ /dev/null @@ -1,54 +0,0 @@ -/* - * stylesheet.h - * - * Copyright © 2013-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 STYLESHEET_H -#define STYLESHEET_H - -#ifdef HAVE_CONFIG_H -#include -#endif - -#include -#include - -typedef struct _stylesheet Stylesheet; - -extern Stylesheet *stylesheet_load(GFile *file); - -extern int stylesheet_save(Stylesheet *ss, GFile *file); - -extern char *stylesheet_data(Stylesheet *ss); - -extern void stylesheet_set_data(Stylesheet *ss, const char *data); - -extern int parse_colour_duo(const char *a, GdkRGBA *col1, GdkRGBA *col2); - -extern char *stylesheet_lookup(Stylesheet *ss, const char *path, const char *key); - -extern int stylesheet_set(Stylesheet *ss, const char *path, const char *key, - const char *new_val); - -extern int stylesheet_delete(Stylesheet *ss, const char *path, const char *key); - -extern void stylesheet_free(Stylesheet *ss); - -#endif /* STYLESHEET_H */ diff --git a/src/stylesheet_editor.c b/src/stylesheet_editor.c deleted file mode 100644 index 292bbf1..0000000 --- a/src/stylesheet_editor.c +++ /dev/null @@ -1,794 +0,0 @@ -/* - * stylesheet_editor.c - * - * Copyright © 2013-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 "stylesheet_editor.h" -#include "presentation.h" -#include "sc_interp.h" -#include "stylesheet.h" -#include "utils.h" - - -G_DEFINE_TYPE_WITH_CODE(StylesheetEditor, stylesheet_editor, - GTK_TYPE_DIALOG, NULL) - - -struct _sspriv -{ - struct presentation *p; - const gchar *furniture; - char *ssdata; -}; - - -static void set_font_from_ss(Stylesheet *ss, const char *path, GtkWidget *w) -{ - char *result = stylesheet_lookup(ss, path, "font"); - if ( result != NULL ) { - gtk_font_button_set_font_name(GTK_FONT_BUTTON(w), result); - } -} - - -static void set_col_from_ss(Stylesheet *ss, const char *path, GtkWidget *w) -{ - char *result = stylesheet_lookup(ss, path, "fgcol"); - if ( result != NULL ) { - GdkRGBA rgba; - if ( gdk_rgba_parse(&rgba, result) == TRUE ) { - gtk_color_chooser_set_rgba(GTK_COLOR_CHOOSER(w), &rgba); - } - } -} - - -static void set_vals_from_ss(Stylesheet *ss, const char *path, const char *key, - GtkWidget *wl, GtkWidget *wr, - GtkWidget *wt, GtkWidget *wb) -{ - char *result = stylesheet_lookup(ss, path, key); - if ( result != NULL ) { - float v[4]; - if ( parse_tuple(result, v) == 0 ) { - gtk_spin_button_set_value(GTK_SPIN_BUTTON(wl), v[0]); - gtk_spin_button_set_value(GTK_SPIN_BUTTON(wr), v[1]); - gtk_spin_button_set_value(GTK_SPIN_BUTTON(wt), v[2]); - gtk_spin_button_set_value(GTK_SPIN_BUTTON(wb), v[3]); - } else { - fprintf(stderr, _("Failed to parse quad: %s\n"), result); - } - } else { - printf("Not found %s.%s\n", path, key); - } -} - - -static void set_geom_from_ss(Stylesheet *ss, const char *path, const char *key, - GtkWidget *ww, GtkWidget *wh, - GtkWidget *wx, GtkWidget *wy, - GtkWidget *wwu, GtkWidget *whu) -{ - char *result = stylesheet_lookup(ss, path, key); - if ( result != NULL ) { - double x, y, w, h; - LengthUnits wu, hu; - if ( parse_dims(result, &w, &h, &wu, &hu, &x, &y) == 0 ) { - if ( wu == UNITS_FRAC ) { - w *= 100; - gtk_combo_box_set_active_id(GTK_COMBO_BOX(wwu), "percent"); - } else { - gtk_combo_box_set_active_id(GTK_COMBO_BOX(wwu), "units"); - } - if ( hu == UNITS_FRAC ) { - h *= 100; - gtk_combo_box_set_active_id(GTK_COMBO_BOX(whu), "percent"); - } else { - gtk_combo_box_set_active_id(GTK_COMBO_BOX(whu), "units"); - } - gtk_spin_button_set_value(GTK_SPIN_BUTTON(ww), w); - gtk_spin_button_set_value(GTK_SPIN_BUTTON(wh), h); - gtk_spin_button_set_value(GTK_SPIN_BUTTON(wx), x); - gtk_spin_button_set_value(GTK_SPIN_BUTTON(wy), y); - } else { - fprintf(stderr, _("Failed to parse dims: %s\n"), result); - } - } else { - printf("Not found %s.%s\n", path, key); - } -} - -static void set_size_from_ss(Stylesheet *ss, const char *path, - GtkWidget *ww, GtkWidget *wh) -{ - char *result = stylesheet_lookup(ss, path, "size"); - if ( result != NULL ) { - float v[2]; - if ( parse_double(result, v) == 0 ) { - gtk_spin_button_set_value(GTK_SPIN_BUTTON(ww), v[0]); - gtk_spin_button_set_value(GTK_SPIN_BUTTON(wh), v[1]); - } else { - fprintf(stderr, _("Failed to parse double: %s\n"), result); - } - } else { - printf("Not found %s.size\n", path); - } -} - - -static int alignment_ok(const char *a) -{ - if ( a == NULL ) return 0; - if ( strcmp(a, "left") == 0 ) return 1; - if ( strcmp(a, "center") == 0 ) return 1; - if ( strcmp(a, "right") == 0 ) return 1; - return 0; -} - - -static void set_alignment_from_ss(Stylesheet *ss, const char *path, - GtkWidget *d) -{ - char *result = stylesheet_lookup(ss, path, "alignment"); - if ( alignment_ok(result) ) { - gtk_combo_box_set_active_id(GTK_COMBO_BOX(d), result); - } -} - - -static void set_bg_from_ss(Stylesheet *ss, const char *path, GtkWidget *wcol, - GtkWidget *wcol2, GtkWidget *wgrad) -{ - char *result; - int found = 0; - - result = stylesheet_lookup(ss, path, "bgcol"); - if ( result != NULL ) { - GdkRGBA rgba; - found = 1; - if ( gdk_rgba_parse(&rgba, result) == TRUE ) { - gtk_color_chooser_set_rgba(GTK_COLOR_CHOOSER(wcol), &rgba); - gtk_combo_box_set_active_id(GTK_COMBO_BOX(wgrad), "flat"); - gtk_widget_set_sensitive(wcol, TRUE); - gtk_widget_set_sensitive(wcol2, FALSE); - } else { - fprintf(stderr, _("Failed to parse colour: %s\n"), result); - } - } - - result = stylesheet_lookup(ss, path, "bggradv"); - if ( result != NULL ) { - GdkRGBA rgba1, rgba2; - found = 1; - if ( parse_colour_duo(result, &rgba1, &rgba2) == 0 ) { - gtk_color_chooser_set_rgba(GTK_COLOR_CHOOSER(wcol), &rgba1); - gtk_color_chooser_set_rgba(GTK_COLOR_CHOOSER(wcol2), &rgba2); - gtk_combo_box_set_active_id(GTK_COMBO_BOX(wgrad), "vert"); - gtk_widget_set_sensitive(wcol, TRUE); - gtk_widget_set_sensitive(wcol2, TRUE); - } - } - - result = stylesheet_lookup(ss, path, "bggradh"); - if ( result != NULL ) { - GdkRGBA rgba1, rgba2; - found = 1; - if ( parse_colour_duo(result, &rgba1, &rgba2) == 0 ) { - gtk_color_chooser_set_rgba(GTK_COLOR_CHOOSER(wcol), &rgba1); - gtk_color_chooser_set_rgba(GTK_COLOR_CHOOSER(wcol2), &rgba2); - gtk_combo_box_set_active_id(GTK_COMBO_BOX(wgrad), "horiz"); - gtk_widget_set_sensitive(wcol, TRUE); - gtk_widget_set_sensitive(wcol2, TRUE); - } - } - - if ( !found ) { - GdkRGBA rgba; - rgba.red = 1.0; - rgba.green = 1.0; - rgba.blue = 1.0; - rgba.alpha = 0.0; - gtk_combo_box_set_active_id(GTK_COMBO_BOX(wgrad), "flat"); - gtk_color_chooser_set_rgba(GTK_COLOR_CHOOSER(wcol), &rgba); - gtk_widget_set_sensitive(wcol, TRUE); - gtk_widget_set_sensitive(wcol2, FALSE); - } -} - - -static void set_furniture(StylesheetEditor *se, const char *furniture) -{ - set_geom_from_ss(se->priv->p->stylesheet, furniture, "geometry", - se->furniture_w, - se->furniture_h, - se->furniture_x, - se->furniture_y, - se->furniture_w_units, - se->furniture_h_units); - - set_vals_from_ss(se->priv->p->stylesheet, furniture, "pad", - se->furniture_padding_l, - se->furniture_padding_r, - se->furniture_padding_t, - se->furniture_padding_b); - - set_vals_from_ss(se->priv->p->stylesheet, furniture, "paraspace", - se->furniture_paraspace_l, - se->furniture_paraspace_r, - se->furniture_paraspace_t, - se->furniture_paraspace_b); - - set_font_from_ss(se->priv->p->stylesheet, furniture, se->furniture_font); - set_col_from_ss(se->priv->p->stylesheet, furniture, se->furniture_fgcol); - set_alignment_from_ss(se->priv->p->stylesheet, furniture, - se->furniture_alignment); - set_bg_from_ss(se->priv->p->stylesheet, furniture, se->furniture_bgcol, - se->furniture_bgcol2, se->furniture_bggrad); -} - - -static void set_values_from_presentation(StylesheetEditor *se) -{ - Stylesheet *ss = se->priv->p->stylesheet; - - /* Narrative */ - set_font_from_ss(ss, "$.narrative", se->narrative_style_font); - set_col_from_ss(ss, "$.narrative", se->narrative_style_fgcol); - set_alignment_from_ss(ss, "$.narrative", se->narrative_style_alignment); - set_bg_from_ss(ss, "$.narrative", se->narrative_style_bgcol, - se->narrative_style_bgcol2, - se->narrative_style_bggrad); - set_vals_from_ss(ss, "$.narrative", "pad", se->narrative_style_padding_l, - se->narrative_style_padding_r, - se->narrative_style_padding_t, - se->narrative_style_padding_b); - set_vals_from_ss(ss, "$.narrative", "paraspace", se->narrative_style_paraspace_l, - se->narrative_style_paraspace_r, - se->narrative_style_paraspace_t, - se->narrative_style_paraspace_b); - - /* Slides */ - set_size_from_ss(ss, "$.slide", se->slide_size_w, se->slide_size_h); - set_bg_from_ss(ss, "$.slide", se->slide_style_bgcol, - se->slide_style_bgcol2, - se->slide_style_bggrad); - - - /* Frames */ - set_font_from_ss(ss, "$.slide.frame", se->frame_style_font); - set_col_from_ss(ss, "$.slide.frame", se->frame_style_fgcol); - set_alignment_from_ss(ss, "$.slide.frame", se->frame_style_alignment); - set_bg_from_ss(ss, "$.slide.frame", se->frame_style_bgcol, - se->frame_style_bgcol2, - se->frame_style_bggrad); - set_vals_from_ss(ss, "$.slide.frame", "pad", se->frame_style_padding_l, - se->frame_style_padding_r, - se->frame_style_padding_t, - se->frame_style_padding_b); - set_vals_from_ss(ss, "$.slide.frame", "paraspace", se->frame_style_paraspace_l, - se->frame_style_paraspace_r, - se->frame_style_paraspace_t, - se->frame_style_paraspace_b); - - set_furniture(se, se->priv->furniture); -} - - -static GradientType id_to_gradtype(const gchar *id) -{ - assert(id != NULL); - if ( strcmp(id, "flat") == 0 ) return GRAD_NONE; - if ( strcmp(id, "horiz") == 0 ) return GRAD_HORIZ; - if ( strcmp(id, "vert") == 0 ) return GRAD_VERT; - return GRAD_NONE; -} - - -static void update_bg(struct presentation *p, const char *style_name, - GtkWidget *bggradw, GtkWidget *col1w, GtkWidget*col2w) -{ - GradientType g; - const gchar *id; - GdkRGBA rgba; - gchar *col1; - gchar *col2; - gchar *gradient; - - id = gtk_combo_box_get_active_id(GTK_COMBO_BOX(bggradw)); - g = id_to_gradtype(id); - - gtk_color_chooser_get_rgba(GTK_COLOR_CHOOSER(col1w), &rgba); - if ( rgba.alpha < 0.000001 ) rgba.alpha = 0.0; - col1 = gdk_rgba_to_string(&rgba); - - gtk_color_chooser_get_rgba(GTK_COLOR_CHOOSER(col2w), &rgba); - col2 = gdk_rgba_to_string(&rgba); - - gradient = g_strconcat(col1, ",", col2, NULL); - - switch ( g ) { - - case GRAD_NONE : - stylesheet_set(p->stylesheet, style_name, "bgcol", - col1); - stylesheet_delete(p->stylesheet, style_name, "bggradv"); - stylesheet_delete(p->stylesheet, style_name, "bggradh"); - break; - - case GRAD_HORIZ : - stylesheet_set(p->stylesheet, style_name, "bggradh", - gradient); - stylesheet_delete(p->stylesheet, style_name, "bggradv"); - stylesheet_delete(p->stylesheet, style_name, "bgcol"); - break; - - case GRAD_VERT : - stylesheet_set(p->stylesheet, style_name, "bggradv", - gradient); - stylesheet_delete(p->stylesheet, style_name, "bggradh"); - stylesheet_delete(p->stylesheet, style_name, "bgcol"); - break; - - } - - g_free(gradient); - g_free(col1); - g_free(col2); -} - - -static void update_spacing(struct presentation *p, const char *style_name, - const char *key, GtkWidget *wl, GtkWidget *wr, - GtkWidget *wt, GtkWidget *wb) -{ - int v[4]; - char tmp[256]; - - v[0] = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(wl)); - v[1] = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(wr)); - v[2] = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(wt)); - v[3] = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(wb)); - - if ( snprintf(tmp, 256, "%i,%i,%i,%i", v[0], v[1], v[2], v[3]) >= 256 ) { - fprintf(stderr, "Spacing too long\n"); - } else { - stylesheet_set(p->stylesheet, style_name, key, tmp); - } -} - - -static char units_id_to_char(const char *id) -{ - if ( strcmp(id, "units") == 0 ) return 'u'; - if ( strcmp(id, "percent") == 0 ) return 'f'; - return 'u'; -} - - -static void update_ss_dims(struct presentation *p, const char *style_name, - const char *key, GtkWidget *ww, GtkWidget *wh, - GtkWidget *wx, GtkWidget *wy, - GtkWidget *wwu, GtkWidget *whu) -{ - float w, h, x, y; - char w_units, h_units; - const gchar *uid; - char tmp[256]; - - w = gtk_spin_button_get_value(GTK_SPIN_BUTTON(ww)); - h = gtk_spin_button_get_value(GTK_SPIN_BUTTON(wh)); - x = gtk_spin_button_get_value(GTK_SPIN_BUTTON(wx)); - y = gtk_spin_button_get_value(GTK_SPIN_BUTTON(wy)); - uid = gtk_combo_box_get_active_id(GTK_COMBO_BOX(wwu)); - w_units = units_id_to_char(uid); - uid = gtk_combo_box_get_active_id(GTK_COMBO_BOX(whu)); - h_units = units_id_to_char(uid); - - if ( w_units == 'f' ) w /= 100.0; - if ( h_units == 'f' ) h /= 100.0; - - if ( snprintf(tmp, 256, "%.2f%cx%.2f%c+%.0f+%0.f", - w, w_units, h, h_units, x, y) >= 256 ) - { - fprintf(stderr, "Spacing too long\n"); - } else { - stylesheet_set(p->stylesheet, style_name, key, tmp); - } -} - - -static void revert_sig(GtkButton *button, StylesheetEditor *se) -{ - stylesheet_set_data(se->priv->p->stylesheet, - se->priv->ssdata); - set_values_from_presentation(se); - g_signal_emit_by_name(se, "changed"); -} - - -static void set_font(GtkFontButton *widget, StylesheetEditor *se, - const char *style_name) -{ - const gchar *font; - font = gtk_font_button_get_font_name(GTK_FONT_BUTTON(widget)); - - stylesheet_set(se->priv->p->stylesheet, style_name, "font", font); - set_values_from_presentation(se); - g_signal_emit_by_name(se, "changed"); -} - - -static void set_col(GtkColorButton *widget, StylesheetEditor *se, - const char *style_name, const char *col_name) -{ - GdkRGBA rgba; - gchar *col; - gtk_color_chooser_get_rgba(GTK_COLOR_CHOOSER(widget), &rgba); - col = gdk_rgba_to_string(&rgba); - stylesheet_set(se->priv->p->stylesheet, style_name, "fgcol", col); - g_free(col); - set_values_from_presentation(se); - g_signal_emit_by_name(se, "changed"); -} - - -static void narrative_font_sig(GtkFontButton *widget, StylesheetEditor *se) -{ - set_font(widget, se, "$.narrative"); -} - - -static void narrative_fgcol_sig(GtkColorButton *widget, StylesheetEditor *se) -{ - set_col(widget, se, "$.narrative", "fgcol"); -} - - -static void narrative_bg_sig(GtkColorButton *widget, StylesheetEditor *se) -{ - update_bg(se->priv->p, "$.narrative", - se->narrative_style_bggrad, - se->narrative_style_bgcol, - se->narrative_style_bgcol2); - set_values_from_presentation(se); - g_signal_emit_by_name(se, "changed"); -} - - -static void narrative_alignment_sig(GtkComboBoxText *widget, StylesheetEditor *se) -{ - const gchar *id = gtk_combo_box_get_active_id(GTK_COMBO_BOX(widget)); - stylesheet_set(se->priv->p->stylesheet, "$.narrative", "alignment", id); - set_values_from_presentation(se); - g_signal_emit_by_name(se, "changed"); -} - - -static void slide_size_sig(GtkSpinButton *widget, StylesheetEditor *se) -{ - int w, h; - char tmp[256]; - - w = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(se->slide_size_w)); - h = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(se->slide_size_h)); - - if ( snprintf(tmp, 256, "%ix%i", w, h) >= 256 ) { - fprintf(stderr, _("Slide size too long\n")); - } else { - stylesheet_set(se->priv->p->stylesheet, "$.slide", "size", tmp); - se->priv->p->slide_width = w; - se->priv->p->slide_height = h; - } - - set_values_from_presentation(se); - g_signal_emit_by_name(se, "changed"); -} - - -static void slide_bg_sig(GtkColorButton *widget, StylesheetEditor *se) -{ - update_bg(se->priv->p, "$.slide", - se->slide_style_bggrad, - se->slide_style_bgcol, - se->slide_style_bgcol2); - set_values_from_presentation(se); - g_signal_emit_by_name(se, "changed"); -} - - -static void frame_font_sig(GtkFontButton *widget, StylesheetEditor *se) -{ - set_font(widget, se, "$.slide.frame"); -} - - -static void frame_fgcol_sig(GtkColorButton *widget, StylesheetEditor *se) -{ - set_col(widget, se, "$.slide.frame", "fgcol"); -} - - -static void frame_bg_sig(GtkColorButton *widget, StylesheetEditor *se) -{ - update_bg(se->priv->p, "$.slide.frame", - se->frame_style_bggrad, - se->frame_style_bgcol, - se->frame_style_bgcol2); - set_values_from_presentation(se); - g_signal_emit_by_name(se, "changed"); -} - - -static void frame_padding_sig(GtkSpinButton *widget, StylesheetEditor *se) -{ - update_spacing(se->priv->p, "$.slide.frame", "pad", - se->frame_style_padding_l, - se->frame_style_padding_r, - se->frame_style_padding_t, - se->frame_style_padding_b); - set_values_from_presentation(se); - g_signal_emit_by_name(se, "changed"); -} - - -static void frame_paraspace_sig(GtkSpinButton *widget, StylesheetEditor *se) -{ - update_spacing(se->priv->p, "$.slide.frame", "paraspace", - se->frame_style_paraspace_l, - se->frame_style_paraspace_r, - se->frame_style_paraspace_t, - se->frame_style_paraspace_b); - set_values_from_presentation(se); - g_signal_emit_by_name(se, "changed"); -} - - -static void frame_alignment_sig(GtkComboBoxText *widget, StylesheetEditor *se) -{ - const gchar *id = gtk_combo_box_get_active_id(GTK_COMBO_BOX(widget)); - stylesheet_set(se->priv->p->stylesheet, "$.slide.frame", "alignment", id); - set_values_from_presentation(se); - g_signal_emit_by_name(se, "changed"); -} - - -static void narrative_padding_sig(GtkSpinButton *widget, StylesheetEditor *se) -{ - update_spacing(se->priv->p, "$.narrative", "pad", - se->narrative_style_padding_l, - se->narrative_style_padding_r, - se->narrative_style_padding_t, - se->narrative_style_padding_b); - set_values_from_presentation(se); - g_signal_emit_by_name(se, "changed"); -} - - -static void narrative_paraspace_sig(GtkSpinButton *widget, StylesheetEditor *se) -{ - update_spacing(se->priv->p, "$.narrative", "paraspace", - se->narrative_style_paraspace_l, - se->narrative_style_paraspace_r, - se->narrative_style_paraspace_t, - se->narrative_style_paraspace_b); - set_values_from_presentation(se); - g_signal_emit_by_name(se, "changed"); -} - - -static void furniture_selector_change_sig(GtkComboBoxText *widget, StylesheetEditor *se) -{ - se->priv->furniture = gtk_combo_box_get_active_id(GTK_COMBO_BOX(widget)); - set_furniture(se, se->priv->furniture); -} - - -static void furniture_font_sig(GtkFontButton *widget, StylesheetEditor *se) -{ - set_font(widget, se, se->priv->furniture); -} - - -static void furniture_fgcol_sig(GtkColorButton *widget, StylesheetEditor *se) -{ - set_col(widget, se,se->priv->furniture, "fgcol"); -} - - -static void furniture_bg_sig(GtkColorButton *widget, StylesheetEditor *se) -{ - update_bg(se->priv->p, se->priv->furniture, se->furniture_bggrad, - se->furniture_bgcol, se->furniture_bgcol2); - set_values_from_presentation(se); - g_signal_emit_by_name(se, "changed"); -} - - -static void furniture_paraspace_sig(GtkSpinButton *widget, StylesheetEditor *se) -{ - update_spacing(se->priv->p, se->priv->furniture, "pad", - se->furniture_padding_l, - se->furniture_padding_r, - se->furniture_padding_t, - se->furniture_padding_b); - set_values_from_presentation(se); - g_signal_emit_by_name(se, "changed"); -} - - -static void furniture_padding_sig(GtkSpinButton *widget, StylesheetEditor *se) -{ - update_spacing(se->priv->p, se->priv->furniture, "pad", - se->furniture_padding_l, - se->furniture_padding_r, - se->furniture_padding_t, - se->furniture_padding_b); - set_values_from_presentation(se); - g_signal_emit_by_name(se, "changed"); -} - - -static void furniture_alignment_sig(GtkComboBoxText *widget, StylesheetEditor *se) -{ - const gchar *id = gtk_combo_box_get_active_id(GTK_COMBO_BOX(widget)); - stylesheet_set(se->priv->p->stylesheet, se->priv->furniture, - "alignment", id); - set_values_from_presentation(se); - g_signal_emit_by_name(se, "changed"); -} - - -static void furniture_dims_sig(GtkSpinButton *widget, StylesheetEditor *se) -{ - update_ss_dims(se->priv->p, se->priv->furniture, "geometry", - se->furniture_w, se->furniture_h, - se->furniture_x, se->furniture_y, - se->furniture_w_units, se->furniture_h_units); - set_values_from_presentation(se); - g_signal_emit_by_name(se, "changed"); -} - - -static void stylesheet_editor_finalize(GObject *obj) -{ - StylesheetEditor *se = COLLOQUIUM_STYLESHEET_EDITOR(obj); - free(se->priv->ssdata); - G_OBJECT_CLASS(stylesheet_editor_parent_class)->finalize(obj); -} - - -static void stylesheet_editor_init(StylesheetEditor *se) -{ - se->priv = G_TYPE_INSTANCE_GET_PRIVATE(se, COLLOQUIUM_TYPE_STYLESHEET_EDITOR, - StylesheetEditorPrivate); - gtk_widget_init_template(GTK_WIDGET(se)); -} - - -#define SE_BIND_CHILD(a, b) \ - gtk_widget_class_bind_template_child(widget_class, StylesheetEditor, a); \ - gtk_widget_class_bind_template_callback(widget_class, b); - -void stylesheet_editor_class_init(StylesheetEditorClass *klass) -{ - GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(klass); - GObjectClass *gobject_class = G_OBJECT_CLASS(klass); - - gtk_widget_class_set_template_from_resource(widget_class, - "/uk/me/bitwiz/Colloquium/stylesheeteditor.ui"); - - g_type_class_add_private(gobject_class, sizeof(StylesheetEditorPrivate)); - gobject_class->finalize = stylesheet_editor_finalize; - - /* Narrative style */ - SE_BIND_CHILD(narrative_style_font, narrative_font_sig); - SE_BIND_CHILD(narrative_style_fgcol, narrative_fgcol_sig); - SE_BIND_CHILD(narrative_style_bgcol, narrative_bg_sig); - SE_BIND_CHILD(narrative_style_bgcol2, narrative_bg_sig); - SE_BIND_CHILD(narrative_style_bggrad, narrative_bg_sig); - SE_BIND_CHILD(narrative_style_paraspace_l, narrative_paraspace_sig); - SE_BIND_CHILD(narrative_style_paraspace_r, narrative_paraspace_sig); - SE_BIND_CHILD(narrative_style_paraspace_t, narrative_paraspace_sig); - SE_BIND_CHILD(narrative_style_paraspace_b, narrative_paraspace_sig); - SE_BIND_CHILD(narrative_style_padding_l, narrative_padding_sig); - SE_BIND_CHILD(narrative_style_padding_r, narrative_padding_sig); - SE_BIND_CHILD(narrative_style_padding_t, narrative_padding_sig); - SE_BIND_CHILD(narrative_style_padding_b, narrative_padding_sig); - SE_BIND_CHILD(narrative_style_alignment, narrative_alignment_sig); - - /* Slide style */ - SE_BIND_CHILD(slide_size_w, slide_size_sig); - SE_BIND_CHILD(slide_size_h, slide_size_sig); - SE_BIND_CHILD(slide_style_bgcol, slide_bg_sig); - SE_BIND_CHILD(slide_style_bgcol2, slide_bg_sig); - SE_BIND_CHILD(slide_style_bggrad, slide_bg_sig); - - /* Slide->frame style */ - SE_BIND_CHILD(frame_style_font, frame_font_sig); - SE_BIND_CHILD(frame_style_fgcol, frame_fgcol_sig); - SE_BIND_CHILD(frame_style_bgcol, frame_bg_sig); - SE_BIND_CHILD(frame_style_bgcol2, frame_bg_sig); - SE_BIND_CHILD(frame_style_bggrad, frame_bg_sig); - SE_BIND_CHILD(frame_style_paraspace_l, frame_paraspace_sig); - SE_BIND_CHILD(frame_style_paraspace_r, frame_paraspace_sig); - SE_BIND_CHILD(frame_style_paraspace_t, frame_paraspace_sig); - SE_BIND_CHILD(frame_style_paraspace_b, frame_paraspace_sig); - SE_BIND_CHILD(frame_style_padding_l, frame_padding_sig); - SE_BIND_CHILD(frame_style_padding_r, frame_padding_sig); - SE_BIND_CHILD(frame_style_padding_t, frame_padding_sig); - SE_BIND_CHILD(frame_style_padding_b, frame_padding_sig); - SE_BIND_CHILD(frame_style_alignment, frame_alignment_sig); - - /* Furniture */ - SE_BIND_CHILD(furniture_selector, furniture_selector_change_sig); - SE_BIND_CHILD(furniture_paraspace_l, furniture_paraspace_sig); - SE_BIND_CHILD(furniture_paraspace_r, furniture_paraspace_sig); - SE_BIND_CHILD(furniture_paraspace_t, furniture_paraspace_sig); - SE_BIND_CHILD(furniture_paraspace_b, furniture_paraspace_sig); - SE_BIND_CHILD(furniture_padding_l, furniture_padding_sig); - SE_BIND_CHILD(furniture_padding_r, furniture_padding_sig); - SE_BIND_CHILD(furniture_padding_t, furniture_padding_sig); - SE_BIND_CHILD(furniture_padding_b, furniture_padding_sig); - SE_BIND_CHILD(furniture_font, furniture_font_sig); - SE_BIND_CHILD(furniture_fgcol, furniture_fgcol_sig); - SE_BIND_CHILD(furniture_bgcol, furniture_bg_sig); - SE_BIND_CHILD(furniture_bgcol2, furniture_bg_sig); - SE_BIND_CHILD(furniture_bggrad, furniture_bg_sig); - SE_BIND_CHILD(furniture_alignment, furniture_alignment_sig); - SE_BIND_CHILD(furniture_w, furniture_dims_sig); - SE_BIND_CHILD(furniture_h, furniture_dims_sig); - SE_BIND_CHILD(furniture_x, furniture_dims_sig); - SE_BIND_CHILD(furniture_y, furniture_dims_sig); - SE_BIND_CHILD(furniture_w_units, furniture_dims_sig); - SE_BIND_CHILD(furniture_h_units, furniture_dims_sig); - - gtk_widget_class_bind_template_callback(widget_class, revert_sig); - - g_signal_new("changed", COLLOQUIUM_TYPE_STYLESHEET_EDITOR, - G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 0); -} - - -StylesheetEditor *stylesheet_editor_new(struct presentation *p) -{ - StylesheetEditor *se; - - se = g_object_new(COLLOQUIUM_TYPE_STYLESHEET_EDITOR, NULL); - if ( se == NULL ) return NULL; - - se->priv->p = p; - se->priv->furniture = gtk_combo_box_get_active_id(GTK_COMBO_BOX(se->furniture_selector)); - set_values_from_presentation(se); - - se->priv->ssdata = stylesheet_data(p->stylesheet); - - return se; -} - diff --git a/src/stylesheet_editor.h b/src/stylesheet_editor.h deleted file mode 100644 index a7c77b6..0000000 --- a/src/stylesheet_editor.h +++ /dev/null @@ -1,128 +0,0 @@ -/* - * stylesheet_editor.h - * - * Copyright © 2013-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 STYLESHEET_EDITOR_H -#define STYLESHEET_EDITOR_H - -#ifdef HAVE_CONFIG_H -#include -#endif - -#include - -#include "presentation.h" -#include "frame.h" - -#define COLLOQUIUM_TYPE_STYLESHEET_EDITOR (stylesheet_editor_get_type()) - -#define COLLOQUIUM_STYLESHEET_EDITOR(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), \ - COLLOQUIUM_TYPE_STYLESHEET_EDITOR, \ - StylesheetEditor)) - -#define COLLOQUIUM_IS_STYLESHEET_EDITOR(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), \ - COLLOQUIUM_TYPE_STYLESHEET_EDITOR)) - -#define COLLOQUIUM_STYLESHEET_EDITOR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((obj), \ - COLLOQUIUM_TYPE_STYLESHEET_EDITOR, \ - StylesheetEditorClass)) - -#define COLLOQUIUM_IS_STYLESHEET_EDITOR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((obj), \ - COLLOQUIUM_TYPE_STYLESHEET_EDITOR)) - -#define COLLOQUIUM_STYLESHEET_EDITOR_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), \ - COLLOQUIUM_TYPE_STYLESHEET_EDITOR, \ - StylesheetEditorClass)) - - -typedef struct _sspriv StylesheetEditorPrivate; - -struct _stylesheeteditor -{ - GtkDialog parent_instance; - GtkWidget *narrative_style_font; - GtkWidget *narrative_style_fgcol; - GtkWidget *narrative_style_bgcol; - GtkWidget *narrative_style_bgcol2; - GtkWidget *narrative_style_bggrad; - GtkWidget *narrative_style_paraspace_l; - GtkWidget *narrative_style_paraspace_r; - GtkWidget *narrative_style_paraspace_t; - GtkWidget *narrative_style_paraspace_b; - GtkWidget *narrative_style_padding_l; - GtkWidget *narrative_style_padding_r; - GtkWidget *narrative_style_padding_t; - GtkWidget *narrative_style_padding_b; - GtkWidget *narrative_style_alignment; - GtkWidget *slide_size_w; - GtkWidget *slide_size_h; - GtkWidget *slide_style_bgcol; - GtkWidget *slide_style_bgcol2; - GtkWidget *slide_style_bggrad; - GtkWidget *frame_style_font; - GtkWidget *frame_style_fgcol; - GtkWidget *frame_style_bgcol; - GtkWidget *frame_style_bgcol2; - GtkWidget *frame_style_bggrad; - GtkWidget *frame_style_paraspace_l; - GtkWidget *frame_style_paraspace_r; - GtkWidget *frame_style_paraspace_t; - GtkWidget *frame_style_paraspace_b; - GtkWidget *frame_style_padding_l; - GtkWidget *frame_style_padding_r; - GtkWidget *frame_style_padding_t; - GtkWidget *frame_style_padding_b; - GtkWidget *frame_style_alignment; - GtkWidget *furniture_selector; - GtkWidget *furniture_paraspace_l; - GtkWidget *furniture_paraspace_r; - GtkWidget *furniture_paraspace_t; - GtkWidget *furniture_paraspace_b; - GtkWidget *furniture_padding_l; - GtkWidget *furniture_padding_r; - GtkWidget *furniture_padding_t; - GtkWidget *furniture_padding_b; - GtkWidget *furniture_font; - GtkWidget *furniture_fgcol; - GtkWidget *furniture_bgcol; - GtkWidget *furniture_bgcol2; - GtkWidget *furniture_bggrad; - GtkWidget *furniture_alignment; - GtkWidget *furniture_w; - GtkWidget *furniture_h; - GtkWidget *furniture_x; - GtkWidget *furniture_y; - GtkWidget *furniture_w_units; - GtkWidget *furniture_h_units; - StylesheetEditorPrivate *priv; -}; - -struct _stylesheeteditorclass -{ - GtkDialogClass parent_class; -}; - -typedef struct _stylesheeteditor StylesheetEditor; -typedef struct _stylesheeteditorclass StylesheetEditorClass; - -extern StylesheetEditor *stylesheet_editor_new(struct presentation *p); - -#endif /* STYLESHEET_EDITOR_H */ diff --git a/src/testcard.c b/src/testcard.c deleted file mode 100644 index 2b6598f..0000000 --- a/src/testcard.c +++ /dev/null @@ -1,288 +0,0 @@ -/* - * testcard.c - * - * Copyright © 2013-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 - -#include "presentation.h" -#include "utils.h" - - -struct testcard -{ - GtkWidget *window; - char geom[256]; - int slide_width; - int slide_height; - GtkWidget *drawingarea; - struct presentation *p; -}; - -static gint tc_destroy_sig(GtkWidget *widget, struct testcard *tc) -{ - free(tc); - return FALSE; -} - - -static void arrow_left(cairo_t *cr, double size) -{ - cairo_rel_line_to(cr, size, size); - cairo_rel_line_to(cr, 0.0, -2*size); - cairo_rel_line_to(cr, -size, size); -} - - -static void arrow_right(cairo_t *cr, double size) -{ - cairo_rel_line_to(cr, -size, size); - cairo_rel_line_to(cr, 0.0, -2*size); - cairo_rel_line_to(cr, size, size); -} - - -static void arrow_down(cairo_t *cr, double size) -{ - cairo_rel_line_to(cr, -size, -size); - cairo_rel_line_to(cr, 2*size, 0.0); - cairo_rel_line_to(cr, -size, size); -} - - -static void arrow_up(cairo_t *cr, double size) -{ - cairo_rel_line_to(cr, -size, size); - cairo_rel_line_to(cr, 2*size, 0.0); - cairo_rel_line_to(cr, -size, -size); -} - - -static void colour_box(cairo_t *cr, double x, double y, - double r, double g, double b, const char *label) -{ - const double w = 50.0; - const double h = 50.0; - cairo_text_extents_t size; - - cairo_rectangle(cr, x+0.5, y+0.5, w, h); - cairo_set_source_rgb(cr, r, g, b); - cairo_fill_preserve(cr); - cairo_set_source_rgb(cr, 0.0, 0.0, 0.0); - cairo_set_line_width(cr, 1.0); - cairo_stroke(cr); - - cairo_set_font_size(cr, 24.0); - cairo_text_extents(cr, label, &size); - cairo_move_to(cr, x+(w/2.0)-(size.width/2.0), y-10.0); - cairo_show_text(cr, label); - -} - - -static gboolean tc_draw_sig(GtkWidget *da, cairo_t *cr, struct testcard *tc) -{ - double xoff, yoff; - double width, height; - int h; - PangoLayout *pl; - PangoFontDescription *desc; - char tmp[1024]; - int plw, plh; - double xp, yp; - - width = gtk_widget_get_allocated_width(GTK_WIDGET(da)); - height = gtk_widget_get_allocated_height(GTK_WIDGET(da)); - - /* Overall background */ - cairo_rectangle(cr, 0.0, 0.0, width, height); - cairo_set_source_rgb(cr, 0.0, 0.0, 1.0); - cairo_fill(cr); - - /* FIXME: Assumes that monitor and slide sizes are such that - * letterboxing at sides. This needn't be the case. */ - h = tc->slide_width * tc->p->slide_height / tc->p->slide_width; - - /* Get the overall size */ - xoff = (width - tc->slide_width)/2.0; - yoff = (height - h)/2.0; - - /* Background of slide */ - cairo_rectangle(cr, xoff, yoff, tc->slide_width, h); - cairo_set_source_rgb(cr, 1.0, 1.0, 1.0); - cairo_fill(cr); - - /* Arrows showing edges of screen */ - cairo_set_source_rgb(cr, 0.0, 0.0, 0.0); - cairo_move_to(cr, 0.0, height/2); - arrow_left(cr, 100.0); - cairo_fill(cr); - cairo_move_to(cr, width, height/2); - arrow_right(cr, 100.0); - cairo_fill(cr); - cairo_move_to(cr, width/2, height); - arrow_down(cr, 100.0); - cairo_fill(cr); - cairo_move_to(cr, width/2, 0.0); - arrow_up(cr, 100.0); - cairo_fill(cr); - - /* Arrows showing edges of slide */ - cairo_translate(cr, xoff, yoff); - cairo_set_source_rgb(cr, 0.5, 0.0, 0.0); - cairo_move_to(cr, 0.0, 100+h/2); - arrow_left(cr, 80.0); - cairo_fill(cr); - cairo_move_to(cr, tc->slide_width, 100+h/2); - arrow_right(cr, 80.0); - cairo_fill(cr); - cairo_move_to(cr, 100+tc->slide_width/2, h); - arrow_down(cr, 80.0); - cairo_fill(cr); - cairo_move_to(cr, 100+tc->slide_width/2, 0.0); - arrow_up(cr, 80.0); - cairo_fill(cr); - - /* Stuff in the middle */ - yp = (tc->slide_height-400)/2.0; - cairo_save(cr); - cairo_translate(cr, 0.0, yp); - - snprintf(tmp, 1024, _("Colloquium %s test card\n" - "Screen resolution %.0f × %.0f\n" - "Slide resolution %i × %i"), - PACKAGE_VERSION, width, height, - tc->slide_width, h); - - pl = pango_cairo_create_layout(cr); - desc = pango_font_description_from_string("Sans 24"); - pango_layout_set_font_description(pl, desc); - pango_layout_set_text(pl, tmp, -1); - - pango_layout_get_size(pl, &plw, &plh); - plw = pango_units_to_double(plw); - plh = pango_units_to_double(plh); - cairo_move_to(cr, (tc->slide_width-plw)/2, 0.0); - cairo_set_source_rgb(cr, 0.0, 0.0, 0.0); - pango_cairo_show_layout(cr, pl); - - /* Colour boxes */ - xp = (tc->slide_width-450)/2.0; - colour_box(cr, xp+0, 200, 1.0, 0.0, 0.0, _("Red")); - colour_box(cr, xp+80, 200, 0.0, 1.0, 0.0, _("Green")); - colour_box(cr, xp+160, 200, 0.0, 0.0, 1.0, _("Blue")); - colour_box(cr, xp+240, 200, 1.0, 1.0, 0.0, _("Yellow")); - colour_box(cr, xp+320, 200, 1.0, 0.0, 1.0, _("Pink")); - colour_box(cr, xp+400, 200, 0.0, 1.0, 1.0, _("Cyan")); - - /* Shades of grey */ - double i; - for ( i=0; i<=1.0; i+=0.2 ) { - char label[32]; - snprintf(label, 31, "%.0f%%", i*100.0); - colour_box(cr, xp+(i*5*80), 300, i, i, i, label); - } - cairo_restore(cr); - - return FALSE; -} - - -static void size_sig(GtkWidget *widget, GdkRectangle *rect, struct testcard *ss) -{ - int w; - - w = rect->height * ss->p->slide_width/ss->p->slide_height; - if ( w > rect->width ) w = rect->width; - ss->slide_width = w; - ss->slide_height = rect->height; -} - - - -static gboolean tc_key_press_sig(GtkWidget *da, GdkEventKey *event, - struct testcard *tc) -{ - if ( !event->is_modifier ) gtk_widget_destroy(tc->window); - return FALSE; -} - - -void show_testcard(struct presentation *p) -{ - GdkDisplay *display; - int n_monitors; - struct testcard *tc; - - tc = calloc(1, sizeof(struct testcard)); - if ( tc == NULL ) return; - - tc->p = p; - - tc->window = gtk_window_new(GTK_WINDOW_TOPLEVEL); - - tc->drawingarea = gtk_drawing_area_new(); - gtk_container_add(GTK_CONTAINER(tc->window), tc->drawingarea); - - gtk_widget_set_can_focus(GTK_WIDGET(tc->drawingarea), TRUE); - gtk_widget_add_events(GTK_WIDGET(tc->drawingarea), GDK_KEY_PRESS_MASK); - - g_signal_connect(G_OBJECT(tc->drawingarea), "key-press-event", - G_CALLBACK(tc_key_press_sig), tc); - g_signal_connect(G_OBJECT(tc->window), "destroy", - G_CALLBACK(tc_destroy_sig), tc); - g_signal_connect(G_OBJECT(tc->window), "size-allocate", - G_CALLBACK(size_sig), tc); - g_signal_connect(G_OBJECT(tc->drawingarea), "draw", - G_CALLBACK(tc_draw_sig), tc); - - gtk_widget_grab_focus(GTK_WIDGET(tc->drawingarea)); - - display = gdk_display_get_default(); - n_monitors = gdk_display_get_n_monitors(display); - - GdkMonitor *mon_ss; - if ( n_monitors == 1 ) { - mon_ss = gdk_display_get_primary_monitor(display); - printf(_("Single monitor mode\n")); - } else { - mon_ss = gdk_display_get_monitor(display, 1); - printf(_("Dual monitor mode\n")); - } - - /* Workaround because gtk_window_fullscreen_on_monitor doesn't work */ - GdkRectangle rect; - gdk_monitor_get_geometry(mon_ss, &rect); - gtk_window_move(GTK_WINDOW(tc->window), rect.x, rect.y); - gtk_window_fullscreen(GTK_WINDOW(tc->window)); - - gtk_widget_show_all(GTK_WIDGET(tc->window)); - -} - diff --git a/src/testcard.h b/src/testcard.h deleted file mode 100644 index bb1ce2b..0000000 --- a/src/testcard.h +++ /dev/null @@ -1,32 +0,0 @@ -/* - * testcard.h - * - * Copyright © 2013-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 TESTCARD_H -#define TESTCARD_H - -#ifdef HAVE_CONFIG_H -#include -#endif - -extern void show_testcard(struct presentation *p); - -#endif /* TESTCARD_H */ diff --git a/src/utils.c b/src/utils.c deleted file mode 100644 index b41f344..0000000 --- a/src/utils.c +++ /dev/null @@ -1,137 +0,0 @@ -/* - * utils.c - * - * Copyright © 2013-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 "utils.h" - -void chomp(char *s) -{ - size_t i; - - if ( !s ) return; - - for ( i=0; i - * - * 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 UTILS_H -#define UTILS_H - -#ifdef HAVE_CONFIG_H -#include -#endif - -typedef enum -{ - UNITS_SLIDE, - UNITS_FRAC -} LengthUnits; - -extern void chomp(char *s); -extern int safe_strcmp(const char *a, const char *b); -extern int parse_double(const char *a, float v[2]); -extern int parse_tuple(const char *a, float v[4]); -extern int parse_dims(const char *opt, double *wp, double *hp, - LengthUnits *wup, LengthUnits *hup, - double *xp, double *yp); - -#include -#define _(x) gettext(x) - -#endif /* UTILS_H */ diff --git a/tests/meson.build b/tests/meson.build index d1f66b9..940f3b0 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -1,21 +1,21 @@ -basic_rendering = ['../src/render.c', '../src/frame.c', - '../src/sc_parse.c', '../src/imagestore.c', - '../src/sc_interp.c', '../src/utils.c', - '../src/stylesheet.c'] - -e = executable('storycode_test', 'storycode_test.c', '../src/sc_parse.c', - dependencies : [gtkdep]) -test('Simple StoryCode parsing', e) - -e = executable('render_basic', 'render_basic.c', basic_rendering, - dependencies : [gtkdep, mdep, jsondep]) -test('Simple rendering', e) - -e = executable('render_subframe', 'render_subframe.c', basic_rendering, - dependencies : [gtkdep, mdep, jsondep]) -test('Rendering with subframes', e) - -e = executable('json_test', 'json_test.c', '../src/sc_parse.c', - '../src/stylesheet.c', - gresources, dependencies : [mdep, jsondep, gtkdep]) -test('JSON parsing', e) +#basic_rendering = ['../src/render.c', '../src/frame.c', +# '../src/sc_parse.c', '../src/imagestore.c', +# '../src/sc_interp.c', '../src/utils.c', +# '../src/stylesheet.c'] +# +#e = executable('storycode_test', 'storycode_test.c', '../src/sc_parse.c', +# dependencies : [gtkdep]) +#test('Simple StoryCode parsing', e) +# +#e = executable('render_basic', 'render_basic.c', basic_rendering, +# dependencies : [gtkdep, mdep, jsondep]) +#test('Simple rendering', e) +# +#e = executable('render_subframe', 'render_subframe.c', basic_rendering, +# dependencies : [gtkdep, mdep, jsondep]) +#test('Rendering with subframes', e) +# +#e = executable('json_test', 'json_test.c', '../src/sc_parse.c', +# '../src/stylesheet.c', +# gresources, dependencies : [mdep, jsondep, gtkdep]) +#test('JSON parsing', e) -- cgit v1.2.3