diff options
author | Thomas White <taw@bitwiz.org.uk> | 2014-12-04 08:39:48 +0100 |
---|---|---|
committer | Thomas White <taw@bitwiz.org.uk> | 2014-12-04 08:39:48 +0100 |
commit | 96938310335d61b55e076d4b9b562c488f421a59 (patch) | |
tree | a1035c1f3468e6747e4fb368126734aaf66de383 | |
parent | 091dc6c4ac53e59f7700893de712a3b6a1caf9b1 (diff) | |
parent | 192220c6aeff3c0bfbeb643da41ce239e8c67609 (diff) |
Merge branch 'narrative'
Conflicts:
src/mainwindow.c
-rw-r--r-- | Makefile.am | 12 | ||||
-rw-r--r-- | data/colloquium.ui | 61 | ||||
-rw-r--r-- | src/colloquium.c | 350 | ||||
-rw-r--r-- | src/colloquium.h (renamed from src/mainwindow.h) | 18 | ||||
-rw-r--r-- | src/frame.c | 2 | ||||
-rw-r--r-- | src/mainwindow.c | 2039 | ||||
-rw-r--r-- | src/narrative_window.c | 230 | ||||
-rw-r--r-- | src/narrative_window.h | 37 | ||||
-rw-r--r-- | src/notes.c | 57 | ||||
-rw-r--r-- | src/notes.h | 7 | ||||
-rw-r--r-- | src/presentation.c | 125 | ||||
-rw-r--r-- | src/presentation.h | 127 | ||||
-rw-r--r-- | src/render.c | 102 | ||||
-rw-r--r-- | src/render.h | 11 | ||||
-rw-r--r-- | src/sc_editor.c | 1464 | ||||
-rw-r--r-- | src/sc_editor.h | 157 | ||||
-rw-r--r-- | src/sc_interp.c | 5 | ||||
-rw-r--r-- | src/slide_sorter.c | 75 | ||||
-rw-r--r-- | src/slide_window.c | 621 | ||||
-rw-r--r-- | src/slide_window.h | 46 | ||||
-rw-r--r-- | src/slideshow.c | 278 | ||||
-rw-r--r-- | src/slideshow.h | 20 | ||||
-rw-r--r-- | src/wrap.c | 146 | ||||
-rw-r--r-- | src/wrap.h | 4 | ||||
-rw-r--r-- | tests/render_test.c | 44 | ||||
-rw-r--r-- | tests/render_test_sc1.c | 44 |
26 files changed, 3197 insertions, 2885 deletions
diff --git a/Makefile.am b/Makefile.am index 7aa90c6..5d29aaf 100644 --- a/Makefile.am +++ b/Makefile.am @@ -11,27 +11,25 @@ LDADD = $(top_builddir)/lib/libgnu.la @IGNORE_UNUSED_LIBRARIES_CFLAGS@ \ harfatum/src/libharfatum.la src/default_stylesheet.o src_colloquium_SOURCES = src/colloquium.c src/render.c \ - src/mainwindow.c src/presentation.c \ + src/presentation.c \ src/frame.c src/sc_parse.c \ src/slideshow.c src/wrap.c src/sc_interp.c \ src/imagestore.c src/notes.c src/pr_clock.c \ src/inhibit_screensaver.c src/slide_sorter.c \ - src/shape.c + src/shape.c src/sc_editor.c src/narrative_window.c \ + src/slide_window.c INCLUDES = -Iharfatum/src EXTRA_DIST += src/presentation.h src/render.h src/wrap.h \ src/slideshow.h src/sc_parse.h src/sc_interp.h \ src/imagestore.h src/notes.h src/pr_clock.h \ - src/inhibit_screensaver.h src/slide_sorter.h src/shape.h + src/inhibit_screensaver.h src/slide_sorter.h src/shape.h \ + src/sc_editor.h src/slide_window.h src/narrative_window.c src/default_stylesheet.o: src/default_stylesheet.sty ld -r -b binary -o src/default_stylesheet.o src/default_stylesheet.sty -colloquiumdir = $(datadir)/colloquium -colloquium_DATA = data/colloquium.ui -EXTRA_DIST += $(colloquium_DATA) - iconsdir = $(datadir)/icons/hicolor/scalable/apps icons_DATA = data/colloquium.svg EXTRA_DIST += $(icons_DATA) diff --git a/data/colloquium.ui b/data/colloquium.ui deleted file mode 100644 index 76a9084..0000000 --- a/data/colloquium.ui +++ /dev/null @@ -1,61 +0,0 @@ -<ui> - <menubar name="displaywindow"> - - <menu name="file" action="FileAction"> - <menuitem name="new" action="NewAction" /> - <menuitem name="open" action="OpenAction" /> - <menuitem name="save" action="SaveAction" /> - <menuitem name="saveas" action="SaveAsAction" /> - <separator /> - <menuitem name="loadstyle" action="LoadStyleAction" /> - <menuitem name="savestyle" action="SaveStyleAction" /> - <menuitem name="exportpdf" action="ExportPDFAction" /> - <separator /> - <menuitem name="quit" action="QuitAction" /> - </menu> - - <menu name="edit" action="EditAction"> - <menuitem name="undo" action="UndoAction" /> - <menuitem name="redo" action="RedoAction" /> - <separator /> - <menuitem name="cut" action="CutAction" /> - <menuitem name="copy" action="CopyAction" /> - <menuitem name="paste" action="PasteAction" /> - <separator /> - <menuitem name="deleteframe" action="DeleteFrameAction" /> - <separator /> - <menuitem name="slidesorter" action="SorterAction" /> - <menuitem name="stylesheet" action="EditStyleAction" /> - </menu> - - <menu name="insert" action="InsertAction"> - <menuitem name="newslide" action="NewSlideAction" /> - </menu> - - <menu name="tools" action="ToolsAction"> - <menuitem name="slideshow" action="TSlideshowAction" /> - <menuitem name="notes" action="NotesAction" /> - <menuitem name="clock" action="ClockAction" /> - <menuitem name="preferences" action="PrefsAction" /> - </menu> - - <menu name="help" action="HelpAction"> - <menuitem name="about" action="AboutAction" /> - </menu> - - </menubar> - - <toolbar name="displaywindowtoolbar"> - - <toolitem name="slideshow" action="SlideshowAction" /> - <separator /> - <toolitem name="add" action="AddSlideAction" /> - <separator /> - <toolitem name="first" action="ButtonFirstSlideAction" /> - <toolitem name="prev" action="ButtonPrevSlideAction" /> - <toolitem name="next" action="ButtonNextSlideAction" /> - <toolitem name="last" action="ButtonLastSlideAction" /> - - </toolbar> - -</ui> diff --git a/src/colloquium.c b/src/colloquium.c index 1f372d8..588aea8 100644 --- a/src/colloquium.c +++ b/src/colloquium.c @@ -1,7 +1,7 @@ /* * colloquium.c * - * Copyright © 2013 Thomas White <taw@bitwiz.org.uk> + * Copyright © 2013-2014 Thomas White <taw@bitwiz.org.uk> * * This file is part of Colloquium. * @@ -28,13 +28,305 @@ #include <gtk/gtk.h> #include <getopt.h> +#include "colloquium.h" #include "presentation.h" -#include "mainwindow.h" +#include "narrative_window.h" + + +struct _colloquium +{ + GtkApplication parent_instance; +}; + + +typedef GtkApplicationClass ColloquiumClass; + + +G_DEFINE_TYPE(Colloquium, colloquium, GTK_TYPE_APPLICATION) + + +static void colloquium_activate(GApplication *app) +{ + printf("activate!\n"); +} + + +static void new_sig(GSimpleAction *action, GVariant *parameter, gpointer vp) +{ + GApplication *app = vp; + g_application_activate(app); +} + + +static void about_sig(GSimpleAction *action, GVariant *parameter, gpointer vp) +{ + GtkWidget *window; + + const gchar *authors[] = { + "Thomas White <taw@bitwiz.org.uk>", + NULL + }; + + window = gtk_about_dialog_new(); + + gtk_about_dialog_set_program_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), + "© 2013 Thomas White <taw@bitwiz.org.uk>"); + gtk_about_dialog_set_comments(GTK_ABOUT_DIALOG(window), + "A tiny presentation program"); + gtk_about_dialog_set_license(GTK_ABOUT_DIALOG(window), + "© 2013 Thomas White <taw@bitwiz.org.uk>\n"); + gtk_about_dialog_set_website(GTK_ABOUT_DIALOG(window), + "http://www.bitwiz.org.uk/"); + gtk_about_dialog_set_authors(GTK_ABOUT_DIALOG(window), authors); + + 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); +} + + +GActionEntry app_entries[] = { + + { "new", new_sig, NULL, NULL, NULL }, + { "about", about_sig, NULL, NULL, NULL }, + { "quit", quit_sig, NULL, NULL, NULL }, +}; + + +static void colloquium_open(GApplication *app, GFile **files, gint n_files, + const gchar *hint) +{ + int i; + + for ( i = 0; i<n_files; i++ ) { + struct presentation *p; + char *uri = g_file_get_path(files[i]); + p = new_presentation(); + load_presentation(p, uri); + narrative_window_new(p, app); + g_free(uri); + } +} + + +static void colloquium_finalize(GObject *object) +{ + G_OBJECT_CLASS(colloquium_parent_class)->finalize(object); +} + + +static void colloquium_startup(GApplication *app) +{ + GtkBuilder *builder; + + G_APPLICATION_CLASS(colloquium_parent_class)->startup(app); + + g_action_map_add_action_entries(G_ACTION_MAP(app), app_entries, + G_N_ELEMENTS(app_entries), app); + + builder = gtk_builder_new(); + gtk_builder_add_from_string(builder, + "<interface>" + + " <menu id='app-menu'>" + " <section>" + " <item>" + " <attribute name='label'>_New</attribute>" + " <attribute name='action'>app.new</attribute>" + " <attribute name='accel'><Primary>n</attribute>" + " </item>" + " <item>" + " <attribute name='label'>_Open</attribute>" + " <attribute name='action'>app.open</attribute>" + " <attribute name='accel'><Primary>o</attribute>" + " </item>" + " <item>" + " <attribute name='label'>Preferences...</attribute>" + " <attribute name='action'>app.prefs</attribute>" + " </item>" + " </section>" + " <section>" + " <item>" + " <attribute name='label'>_About</attribute>" + " <attribute name='action'>app.about</attribute>" + " </item>" + " <item>" + " <attribute name='label'>_Quit</attribute>" + " <attribute name='action'>app.quit</attribute>" + " <attribute name='accel'><Primary>q</attribute>" + " </item>" + " </section>" + " </menu>" + + " <menu id='menubar'>" + " <submenu>" + " <attribute name='label' translatable='yes'>File</attribute>" + " <section>" + " <item>" + " <attribute name='label'>_Save</attribute>" + " <attribute name='action'>win.save</attribute>" + " <attribute name='accel'><Primary>s</attribute>" + " </item>" + " <item>" + " <attribute name='label'>Save As...</attribute>" + " <attribute name='action'>win.saveas</attribute>" + " </item>" + " </section>" + " <section>" + " <item>" + " <attribute name='label'>Load stylesheet</attribute>" + " <attribute name='action'>win.loadstyle</attribute>" + " </item>" + " <item>" + " <attribute name='label'>Save stylesheet</attribute>" + " <attribute name='action'>win.savestyle</attribute>" + " </item>" + " <item>" + " <attribute name='label'>Export PDF</attribute>" + " <attribute name='action'>win.exportpdf</attribute>" + " </item>" + " </section>" + " </submenu>" + + " <submenu>" + " <attribute name='label' translatable='yes'>Edit</attribute>" + " <section>" + " <item>" + " <attribute name='label'>Undo</attribute>" + " <attribute name='action'>win.undo</attribute>" + " </item>" + " <item>" + " <attribute name='label'>Redo</attribute>" + " <attribute name='action'>win.redo</attribute>" + " </item>" + " </section>" + " <section>" + " <item>" + " <attribute name='label'>Cut</attribute>" + " <attribute name='action'>win.cut</attribute>" + " </item>" + " <item>" + " <attribute name='label'>Copy</attribute>" + " <attribute name='action'>win.copy</attribute>" + " </item>" + " <item>" + " <attribute name='label'>Paste</attribute>" + " <attribute name='action'>win.paste</attribute>" + " </item>" + " </section>" + " <section>" + " <item>" + " <attribute name='label'>Delete frame</attribute>" + " <attribute name='action'>win.deleteframe</attribute>" + " </item>" + " </section>" + " <section>" + " <item>" + " <attribute name='label'>Slide sorter...</attribute>" + " <attribute name='action'>win.slidesorter</attribute>" + " </item>" + " <item>" + " <attribute name='label'>Edit stylesheet...</attribute>" + " <attribute name='action'>win.stylesheet</attribute>" + " </item>" + " </section>" + " </submenu>" + + " <submenu>" + " <attribute name='label' translatable='yes'>Insert</attribute>" + " <section>" + " <item>" + " <attribute name='label'>Slide</attribute>" + " <attribute name='action'>win.slide</attribute>" + " </item>" + " </section>" + " </submenu>" + + " <submenu>" + " <attribute name='label' translatable='yes'>Tools</attribute>" + " <section>" + " <item>" + " <attribute name='label'>Start slideshow</attribute>" + " <attribute name='action'>win.startslideshow</attribute>" + " </item>" + " <item>" + " <attribute name='label'>Slide notes...</attribute>" + " <attribute name='action'>win.notes</attribute>" + " </item>" + " <item>" + " <attribute name='label'>Presentation clock...</attribute>" + " <attribute name='action'>win.clock</attribute>" + " </item>" + " </section>" + " </submenu>" + " </menu>" + + "</interface>", -1, NULL); + + gtk_application_set_app_menu(GTK_APPLICATION(app), + G_MENU_MODEL(gtk_builder_get_object(builder, "app-menu"))); + gtk_application_set_menubar(GTK_APPLICATION(app), + G_MENU_MODEL(gtk_builder_get_object(builder, "menubar"))); + + g_object_unref(builder); +} + + +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) +{ +} + + +static Colloquium *colloquium_new() +{ + Colloquium *app; + + g_type_init(); + 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); + return app; +} static void show_help(const char *s) { - printf("Syntax: %s [options] [<file.clq>]\n\n", s); + printf("Syntax: %s [options] [<file.sc>]\n\n", s); printf( "A tiny presentation program.\n" "\n" @@ -46,6 +338,8 @@ static void show_help(const char *s) int main(int argc, char *argv[]) { int c; + int status; + Colloquium *app; /* Long options */ const struct option longopts[] = { @@ -53,56 +347,26 @@ int main(int argc, char *argv[]) {0, 0, NULL, 0} }; - gtk_init(&argc, &argv); - /* Short options */ - while ((c = getopt_long(argc, argv, "h", - longopts, NULL)) != -1) { + while ((c = getopt_long(argc, argv, "h", longopts, NULL)) != -1) { - switch (c) { - case 'h' : + switch (c) + { + case 'h' : show_help(argv[0]); return 0; - case 0 : + case 0 : break; - default : - return 1; - } - - } - - if ( optind == argc ) { - - struct presentation *p; - - p = new_presentation(); - p->cur_edit_slide = add_slide(p, 0); - p->completely_empty = 1; - if ( open_mainwindow(p) ) { - fprintf(stderr, "Couldn't open main window.\n"); + default : return 1; } - } - - while ( optind < argc ) { - - char *filename; - struct presentation *p; - - filename = argv[optind++]; - - p = new_presentation(); - if ( load_presentation(p, filename) ) { - fprintf(stderr, "Failed to open presentation"); - } else { - open_mainwindow(p); - } } - gtk_main(); - - return 0; + app = colloquium_new(); + status = g_application_run(G_APPLICATION(app), argc, argv); + g_object_unref(app); + return status; } diff --git a/src/mainwindow.h b/src/colloquium.h index b406492..b927728 100644 --- a/src/mainwindow.h +++ b/src/colloquium.h @@ -1,7 +1,7 @@ /* - * presentation.h + * colloquium.h * - * Copyright © 2013 Thomas White <taw@bitwiz.org.uk> + * Copyright © 2014 Thomas White <taw@bitwiz.org.uk> * * This file is part of Colloquium. * @@ -20,21 +20,17 @@ * */ -#ifndef MAINWINDOW_H -#define MAINWINDOW_H +#ifndef COLLOQUIUM_H +#define COLLOQUIUM_H #ifdef HAVE_CONFIG_H #include <config.h> #endif +#include <glib-object.h> -extern void rerender_slide(struct presentation *p); -extern int open_mainwindow(struct presentation *p); -extern void change_edit_slide(struct presentation *p, struct slide *np); -extern void redraw_editor(struct presentation *p); -extern void update_titlebar(struct presentation *p); -extern void update_toolbar(struct presentation *p); +typedef struct _colloquium Colloquium; -#endif /* MAINWINDOW_H */ +#endif /* COLLOQUIUM_H */ diff --git a/src/frame.c b/src/frame.c index 9b3ab06..f7f0e83 100644 --- a/src/frame.c +++ b/src/frame.c @@ -85,7 +85,7 @@ void renew_frame(struct frame *fr) if ( fr == NULL ) return; for ( i=0; i<fr->n_lines; i++ ) { - // wrap_line_free(&fr->lines[i]); + wrap_line_free(&fr->lines[i]); } free(fr->lines); fr->lines = NULL; diff --git a/src/mainwindow.c b/src/mainwindow.c deleted file mode 100644 index 2794a31..0000000 --- a/src/mainwindow.c +++ /dev/null @@ -1,2039 +0,0 @@ -/* - * mainwindow.c - * - * Copyright © 2013-2014 Thomas White <taw@bitwiz.org.uk> - * - * This file is part of Colloquium. - * - * Colloquium is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * - */ - - -#ifdef HAVE_CONFIG_H -#include <config.h> -#endif - -#include <stdlib.h> -#include <string.h> -#include <gtk/gtk.h> -#include <assert.h> -#include <gdk/gdkkeysyms.h> -#include <gdk-pixbuf/gdk-pixbuf.h> -#include <math.h> - -#include "presentation.h" -#include "mainwindow.h" -#include "render.h" -#include "frame.h" -#include "slideshow.h" -#include "wrap.h" -#include "notes.h" -#include "pr_clock.h" -#include "slide_sorter.h" -#include "sc_parse.h" -#include "sc_interp.h" - - -/* Update a slide, once it's been edited in some way. */ -void rerender_slide(struct presentation *p) -{ - struct slide *s = p->cur_edit_slide; - int n = slide_number(p, s); - - free_render_buffers(s); - - s->rendered_thumb = render_slide(s, s->parent->thumb_slide_width, - p->slide_width, p->slide_height, p->is, - ISZ_THUMBNAIL, n); - - s->rendered_proj = render_slide(s, s->parent->proj_slide_width, - p->slide_width, p->slide_height, p->is, - ISZ_SLIDESHOW, n); - - s->rendered_edit = render_slide(s, s->parent->edit_slide_width, - p->slide_width, p->slide_height, p->is, - ISZ_EDITOR, n); -} - - -/* Ensure that "edit" and "proj" renderings are in order */ -static void render_edit_and_proj(struct presentation *p) -{ - struct slide *s = p->cur_edit_slide; - int n = slide_number(p, s); - - if ( s->rendered_proj == NULL ) { - s->rendered_proj = render_slide(s, s->parent->proj_slide_width, - p->slide_width, p->slide_height, - p->is, ISZ_SLIDESHOW, n); - } - - if ( s->rendered_edit == NULL ) { - s->rendered_edit = render_slide(s, s->parent->edit_slide_width, - p->slide_width, p->slide_height, - p->is, ISZ_EDITOR, n); - } -} - - - -/* Force a redraw of the editor window */ -void redraw_editor(struct presentation *p) -{ - gint w, h; - - w = gtk_widget_get_allocated_width(GTK_WIDGET(p->drawingarea)); - h = gtk_widget_get_allocated_height(GTK_WIDGET(p->drawingarea)); - - gtk_widget_queue_draw_area(p->drawingarea, 0, 0, w, h); -} - - -static void add_ui_sig(GtkUIManager *ui, GtkWidget *widget, - GtkContainer *container) -{ - gtk_box_pack_start(GTK_BOX(container), widget, FALSE, FALSE, 0); - if ( GTK_IS_TOOLBAR(widget) ) { - gtk_toolbar_set_show_arrow(GTK_TOOLBAR(widget), TRUE); - } -} - - -static gint quit_sig(GtkWidget *widget, struct presentation *p) -{ - return 0; -} - - -static void show_error(struct presentation *p, const char *message) -{ - GtkWidget *window; - - window = gtk_message_dialog_new(GTK_WINDOW(p->window), - GTK_DIALOG_DESTROY_WITH_PARENT, - GTK_MESSAGE_WARNING, - GTK_BUTTONS_CLOSE, message); - gtk_window_set_title(GTK_WINDOW(window), "Error"); - - g_signal_connect_swapped(window, "response", - G_CALLBACK(gtk_widget_destroy), window); - gtk_widget_show(window); -} - - -void update_toolbar(struct presentation *p) -{ - GtkWidget *d; - int cur_slide_number; - - d = gtk_ui_manager_get_widget(p->ui, "/ui/displaywindowtoolbar/first"); - gtk_widget_set_sensitive(GTK_WIDGET(d), TRUE); - d = gtk_ui_manager_get_widget(p->ui, "/ui/displaywindowtoolbar/prev"); - gtk_widget_set_sensitive(GTK_WIDGET(d), TRUE); - d = gtk_ui_manager_get_widget(p->ui, "/ui/displaywindowtoolbar/next"); - gtk_widget_set_sensitive(GTK_WIDGET(d), TRUE); - d = gtk_ui_manager_get_widget(p->ui, "/ui/displaywindowtoolbar/last"); - gtk_widget_set_sensitive(GTK_WIDGET(d), TRUE); - - cur_slide_number = slide_number(p, p->cur_edit_slide); - if ( cur_slide_number == 0 ) { - - d = gtk_ui_manager_get_widget(p->ui, - "/ui/displaywindowtoolbar/first"); - gtk_widget_set_sensitive(GTK_WIDGET(d), FALSE); - d = gtk_ui_manager_get_widget(p->ui, - "/ui/displaywindowtoolbar/prev"); - gtk_widget_set_sensitive(GTK_WIDGET(d), FALSE); - - } - - if ( cur_slide_number == p->num_slides-1 ) { - - d = gtk_ui_manager_get_widget(p->ui, - "/ui/displaywindowtoolbar/next"); - gtk_widget_set_sensitive(GTK_WIDGET(d), FALSE); - d = gtk_ui_manager_get_widget(p->ui, - "/ui/displaywindowtoolbar/last"); - gtk_widget_set_sensitive(GTK_WIDGET(d), FALSE); - - } - - -} - - -static void do_slide_update(struct presentation *p, PangoContext *pc) -{ - rerender_slide(p); - redraw_editor(p); - if ( (p->slideshow != NULL) - && (p->cur_edit_slide == p->cur_proj_slide) ) - { - redraw_slideshow(p); - } -} - - -/* Inelegance to make furniture selection menus work */ -struct menu_pl -{ - struct presentation *p; - char *style_name; - GtkWidget *widget; -}; - - -static gint add_furniture(GtkWidget *widget, struct menu_pl *pl) -{ - sc_block_append_end(pl->p->cur_edit_slide->scblocks, - strdup(pl->style_name), NULL, NULL); - - do_slide_update(pl->p, pl->p->pc); - - return 0; -} - - -static void update_style_menus(struct presentation *p) -{ - GtkWidget *menu; - SCInterpreter *scin; - struct style_id *styles; - int i, n_sty; - - /* Free old list */ - for ( i=0; i<p->n_style_menu; i++ ) { - gtk_widget_destroy(p->style_menu[i].widget); - free(p->style_menu[i].style_name); - } - free(p->style_menu); - - /* Get the list of styles from the style sheet */ - scin = sc_interp_new(NULL, NULL); - if ( scin == NULL ) { - fprintf(stderr, "Failed to set up interpreter.\n"); - return; - } - sc_interp_run_stylesheet(scin, p->stylesheet); - - styles = list_styles(scin, &n_sty); - if ( styles == NULL ) return; - - sc_interp_destroy(scin); - - /* Set up list for next time */ - p->style_menu = calloc(n_sty, sizeof(struct menu_pl)); - if ( p->style_menu == NULL ) return; - - /* Add the styles to the "Insert" menu */ - menu = gtk_ui_manager_get_widget(p->ui, "/displaywindow/insert"); - menu = gtk_menu_item_get_submenu(GTK_MENU_ITEM(menu)); - - for ( i=0; i<n_sty; i++ ) { - - GtkWidget *item; - - item = gtk_menu_item_new_with_label(styles[i].friendlyname); - gtk_menu_shell_append(GTK_MENU_SHELL(menu), item); - - p->style_menu[i].p = p; - p->style_menu[i].widget = item; - p->style_menu[i].style_name = styles[i].name; - - g_signal_connect(G_OBJECT(item), "activate", - G_CALLBACK(add_furniture), - &p->style_menu[i]); - - free(styles[i].friendlyname); - } - - gtk_widget_show_all(menu); - free(styles); -} - - -static gint open_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)); - - if ( p->completely_empty ) { - - if ( load_presentation(p, filename) ) { - show_error(p, "Failed to open presentation"); - } - p->cur_edit_slide = p->slides[0]; - rerender_slide(p); - update_toolbar(p); - update_style_menus(p); - if ( p->slideshow != NULL ) end_slideshow(p); - - } else { - - struct presentation *p; - - /* FIXME */ - p = new_presentation(); - if ( load_presentation(p, filename) ) { - show_error(p, "Failed to open presentation"); - } else { - open_mainwindow(p); - } - - } - - g_free(filename); - - } - - gtk_widget_destroy(d); - - return 0; -} - - -static gint open_sig(GtkWidget *widget, struct presentation *p) -{ - GtkWidget *d; - - d = gtk_file_chooser_dialog_new("Open Presentation", - GTK_WINDOW(p->window), - GTK_FILE_CHOOSER_ACTION_OPEN, - GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, - GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT, - NULL); - - g_signal_connect(G_OBJECT(d), "response", - G_CALLBACK(open_response_sig), p); - - gtk_widget_show_all(d); - - return 0; -} - - -static gint loadstyle_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)); - /* FIXME: Implement this (now easy) */ - //replace_stylesheet(p, filename); - g_free(filename); - update_style_menus(p); - rerender_slide(p); - - } - - gtk_widget_destroy(d); - - return 0; -} - - -static gint loadstyle_sig(GtkWidget *widget, struct presentation *p) -{ - GtkWidget *d; - - d = gtk_file_chooser_dialog_new("Load Stylesheet", - GTK_WINDOW(p->window), - GTK_FILE_CHOOSER_ACTION_OPEN, - GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, - GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT, - NULL); - - g_signal_connect(G_OBJECT(d), "response", - G_CALLBACK(loadstyle_response_sig), p); - - gtk_widget_show_all(d); - - return 0; -} - - -static gint new_sig(GtkWidget *widget, struct presentation *pnn) -{ - struct presentation *p; - - p = new_presentation(); - if ( p != NULL ) { - struct slide *new; - new = add_slide(p, 0); - p->completely_empty = 1; - /* FIXME: position */ - new->scblocks = sc_block_append_end(p->scblocks, "slide", - NULL, NULL); - attach_notes(new); - open_mainwindow(p); - } - - return 0; -} - - -static gint saveas_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)); - - if ( save_presentation(p, filename) ) { - show_error(p, "Failed to save presentation"); - } - - g_free(filename); - - } - - gtk_widget_destroy(d); - - return 0; -} - - -static gint saveas_sig(GtkWidget *widget, struct presentation *p) -{ - GtkWidget *d; - - d = gtk_file_chooser_dialog_new("Save Presentation", - GTK_WINDOW(p->window), - GTK_FILE_CHOOSER_ACTION_SAVE, - GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, - GTK_STOCK_SAVE, 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(saveas_response_sig), p); - - gtk_widget_show_all(d); - - return 0; -} - - -static gint save_sig(GtkWidget *widget, struct presentation *p) -{ - if ( p->filename == NULL ) { - return saveas_sig(widget, p); - } - - save_presentation(p, p->filename); - - return 0; -} - - -static gint save_ss_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)); - - /* FIXME: Implement this */ -// if ( save_stylesheet(p->ss, filename) ) { -// show_error(p, "Failed to save style sheet"); -// } - - g_free(filename); - - } - - gtk_widget_destroy(d); - - return 0; -} - - -static gint save_ss_sig(GtkWidget *widget, struct presentation *p) -{ - GtkWidget *d; - - d = gtk_file_chooser_dialog_new("Save Style sheet", - GTK_WINDOW(p->window), - GTK_FILE_CHOOSER_ACTION_SAVE, - GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, - GTK_STOCK_SAVE, 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(save_ss_response_sig), p); - - gtk_widget_show_all(d); - - return 0; -} - - -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)); - - if ( export_pdf(p, filename) ) { - show_error(p, "Failed to export as PDF"); - } - - g_free(filename); - - } - - gtk_widget_destroy(d); - - return 0; -} - - -static gint export_pdf_sig(GtkWidget *widget, struct presentation *p) -{ - GtkWidget *d; - - d = gtk_file_chooser_dialog_new("Export PDF", - GTK_WINDOW(p->window), - GTK_FILE_CHOOSER_ACTION_SAVE, - GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, - GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT, - NULL); - gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(d), - TRUE); - - g_signal_connect(G_OBJECT(d), "response", - G_CALLBACK(export_pdf_response_sig), p); - - gtk_widget_show_all(d); - - return 0; -} - - -static gint about_sig(GtkWidget *widget, struct presentation *p) -{ - GtkWidget *window; - - const gchar *authors[] = { - "Thomas White <taw@bitwiz.org.uk>", - NULL - }; - - window = gtk_about_dialog_new(); - gtk_window_set_transient_for(GTK_WINDOW(window), GTK_WINDOW(p->window)); - - gtk_about_dialog_set_program_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), - "© 2013 Thomas White <taw@bitwiz.org.uk>"); - gtk_about_dialog_set_comments(GTK_ABOUT_DIALOG(window), - "A tiny presentation program"); - gtk_about_dialog_set_license(GTK_ABOUT_DIALOG(window), - "© 2013 Thomas White <taw@bitwiz.org.uk>\n"); - gtk_about_dialog_set_website(GTK_ABOUT_DIALOG(window), - "http://www.bitwiz.org.uk/"); - gtk_about_dialog_set_authors(GTK_ABOUT_DIALOG(window), authors); - - g_signal_connect(window, "response", G_CALLBACK(gtk_widget_destroy), - NULL); - - gtk_widget_show_all(window); - - return 0; -} - - -static gint start_slideshow_sig(GtkWidget *widget, struct presentation *p) -{ - try_start_slideshow(p); - return FALSE; -} - - -/* Change the editor's slide to "np" */ -void change_edit_slide(struct presentation *p, struct slide *np) -{ - /* If this slide is not being shown on the projector, we can free the - * buffers */ - if ( p->cur_proj_slide != p->cur_edit_slide ) { - free_render_buffers_except_thumb(p->cur_edit_slide); - } - - p->cur_edit_slide = np; - render_edit_and_proj(p); - - set_selection(p, NULL); - update_toolbar(p); - redraw_editor(p); - - if ( p->notes != NULL ) { - notify_notes_slide_changed(p, np); - } - - if ( (p->slideshow != NULL) && p->slideshow_linked ) { - change_proj_slide(p, np); - } /* else leave the slideshow alone */ -} - - -static gint add_slide_sig(GtkWidget *widget, struct presentation *p) -{ - struct slide *new; - int cur_slide_number; - - cur_slide_number = slide_number(p, p->cur_edit_slide); - - new = add_slide(p, cur_slide_number+1); - new->scblocks = sc_block_insert_after(p->cur_edit_slide->scblocks, - "slide", NULL, NULL); - - change_edit_slide(p, new); - - return FALSE; -} - - -static gint first_slide_sig(GtkWidget *widget, struct presentation *p) -{ - change_edit_slide(p, p->slides[0]); - return FALSE; -} - - -static gint prev_slide_sig(GtkWidget *widget, struct presentation *p) -{ - int cur_slide_number; - - cur_slide_number = slide_number(p, p->cur_edit_slide); - if ( cur_slide_number == 0 ) return FALSE; - - change_edit_slide(p, p->slides[cur_slide_number-1]); - - return FALSE; -} - - -static gint next_slide_sig(GtkWidget *widget, struct presentation *p) -{ - int cur_slide_number; - - cur_slide_number = slide_number(p, p->cur_edit_slide); - if ( cur_slide_number == p->num_slides-1 ) return FALSE; - - change_edit_slide(p, p->slides[cur_slide_number+1]); - - return FALSE; -} - - -static gint last_slide_sig(GtkWidget *widget, struct presentation *p) -{ - change_edit_slide(p, p->slides[p->num_slides-1]); - - return FALSE; -} - - -static gint open_stylesheet_sig(GtkWidget *widget, struct presentation *p) -{ - /* FIXME */ - //if ( p->stylesheetwindow == NULL ) { - // p->stylesheetwindow = open_stylesheet(p); - //} /* else already open */ - - return FALSE; -} - - -static gint open_notes_sig(GtkWidget *widget, struct presentation *p) -{ - open_notes(p); - return FALSE; -} - - -static gint open_clock_sig(GtkWidget *widget, struct presentation *p) -{ - open_clock(p); - return FALSE; -} - - -static gint open_slidesorter_sig(GtkWidget *widget, struct presentation *p) -{ - open_slidesorter(p); - return FALSE; -} - -static gint delete_frame_sig(GtkWidget *widget, struct presentation *p) -{ - int i; - - for ( i=0; i<p->n_selection; i++ ) { - delete_subframe(p->cur_edit_slide, p->selection[i]); - } - p->n_selection = 0; - - rerender_slide(p); - redraw_editor(p); - - return FALSE; -} - - -static void add_menu_bar(struct presentation *p, GtkWidget *vbox) -{ - GError *error = NULL; - GtkWidget *toolbar; - GtkWidget *menu; - GtkWidget *item; - - GtkActionEntry entries[] = { - - { "FileAction", NULL, "_File", NULL, NULL, NULL }, - { "NewAction", GTK_STOCK_NEW, "_New", - NULL, NULL, G_CALLBACK(new_sig) }, - { "OpenAction", GTK_STOCK_OPEN, "_Open...", - NULL, NULL, G_CALLBACK(open_sig) }, - { "LoadStyleAction", NULL, "_Load Stylesheet...", - NULL, NULL, G_CALLBACK(loadstyle_sig) }, - { "SaveAction", GTK_STOCK_SAVE, "_Save", - NULL, NULL, G_CALLBACK(save_sig) }, - { "SaveAsAction", GTK_STOCK_SAVE_AS, "Save _As...", - NULL, NULL, G_CALLBACK(saveas_sig) }, - { "SaveStyleAction", NULL, "Save St_ylesheet", - NULL, NULL, G_CALLBACK(save_ss_sig) }, - { "ExportPDFAction", NULL, "Export PDF", - NULL, NULL, G_CALLBACK(export_pdf_sig) }, - { "QuitAction", GTK_STOCK_QUIT, "_Quit", - NULL, NULL, G_CALLBACK(quit_sig) }, - - { "EditAction", NULL, "_Edit", NULL, NULL, NULL }, - { "SorterAction", NULL, "_Open Slide Sorter...", - NULL, NULL, G_CALLBACK(open_slidesorter_sig) }, - { "UndoAction", GTK_STOCK_UNDO, "_Undo", - NULL, NULL, NULL }, - { "RedoAction", GTK_STOCK_REDO, "_Redo", - NULL, NULL, NULL }, - { "CutAction", GTK_STOCK_CUT, "Cut", - NULL, NULL, NULL }, - { "CopyAction", GTK_STOCK_COPY, "Copy", - NULL, NULL, NULL }, - { "PasteAction", GTK_STOCK_PASTE, "Paste", - NULL, NULL, NULL }, - { "DeleteFrameAction", GTK_STOCK_DELETE, "Delete Frame", - NULL, NULL, G_CALLBACK(delete_frame_sig) }, - { "EditStyleAction", NULL, "Stylesheet...", - NULL, NULL, G_CALLBACK(open_stylesheet_sig) }, - - { "InsertAction", NULL, "_Insert", NULL, NULL, NULL }, - { "NewSlideAction", GTK_STOCK_ADD, "_New Slide", - NULL, NULL, G_CALLBACK(add_slide_sig) }, - - { "ToolsAction", NULL, "_Tools", NULL, NULL, NULL }, - { "TSlideshowAction", GTK_STOCK_FULLSCREEN, "_Start Slideshow", - "F5", NULL, G_CALLBACK(start_slideshow_sig) }, - { "NotesAction", NULL, "_Open slide notes", - "F8", NULL, G_CALLBACK(open_notes_sig) }, - { "ClockAction", NULL, "_Open presentation clock", - "F9", NULL, G_CALLBACK(open_clock_sig) }, - { "PrefsAction", GTK_STOCK_PREFERENCES, "_Preferences", - NULL, NULL, NULL }, - - { "HelpAction", NULL, "_Help", NULL, NULL, NULL }, - { "AboutAction", GTK_STOCK_ABOUT, "_About...", - NULL, NULL, G_CALLBACK(about_sig) }, - - { "SlideshowAction", GTK_STOCK_FULLSCREEN, "Start Presentation", - NULL, NULL, G_CALLBACK(start_slideshow_sig) }, - { "AddSlideAction", GTK_STOCK_ADD, "Add Slide", - NULL, NULL, G_CALLBACK(add_slide_sig) }, - { "ButtonFirstSlideAction", GTK_STOCK_GOTO_FIRST, "First Slide", - NULL, NULL, G_CALLBACK(first_slide_sig) }, - { "ButtonPrevSlideAction", GTK_STOCK_GO_BACK, "Previous Slide", - NULL, NULL, G_CALLBACK(prev_slide_sig) }, - { "ButtonNextSlideAction", GTK_STOCK_GO_FORWARD, "Next Slide", - NULL, NULL, G_CALLBACK(next_slide_sig) }, - { "ButtonLastSlideAction", GTK_STOCK_GOTO_LAST, "Last Slide", - NULL, NULL, G_CALLBACK(last_slide_sig) }, - - }; - guint n_entries = G_N_ELEMENTS(entries); - - p->action_group = gtk_action_group_new("mainwindow"); - gtk_action_group_add_actions(p->action_group, entries, n_entries, p); - - p->ui = gtk_ui_manager_new(); - gtk_ui_manager_insert_action_group(p->ui, p->action_group, 0); - g_signal_connect(p->ui, "add_widget", G_CALLBACK(add_ui_sig), vbox); - if ( gtk_ui_manager_add_ui_from_file(p->ui, - DATADIR"/colloquium/colloquium.ui", &error) == 0 ) { - fprintf(stderr, "Error loading main window menu bar: %s\n", - error->message); - return; - } - - gtk_window_add_accel_group(GTK_WINDOW(p->window), - gtk_ui_manager_get_accel_group(p->ui)); - gtk_ui_manager_ensure_update(p->ui); - - toolbar = gtk_ui_manager_get_widget(p->ui, "/displaywindowtoolbar"); - gtk_toolbar_insert(GTK_TOOLBAR(toolbar), - gtk_separator_tool_item_new(), -1); - - menu = gtk_ui_manager_get_widget(p->ui, "/displaywindow/insert"); - menu = gtk_menu_item_get_submenu(GTK_MENU_ITEM(menu)); - item = gtk_separator_menu_item_new(); - gtk_menu_shell_append(GTK_MENU_SHELL(menu), item); - - update_style_menus(p); - update_toolbar(p); -} - - -static gint close_sig(GtkWidget *window, struct presentation *p) -{ - free_presentation(p); - 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_caret(cairo_t *cr, struct frame *fr, - int cursor_line, int cursor_box, int cursor_pos) -{ - double xposd, yposd, line_height; - double cx, clow, chigh; - const double t = 1.8; - struct wrap_box *box; - int i; - - if ( fr == NULL ) return; - if ( fr->n_lines == 0 ) return; - - /* Locate the cursor in a "logical" and "geographical" sense */ - box = &fr->lines[cursor_line].boxes[cursor_box]; - get_cursor_pos(box, cursor_pos, &xposd, &yposd, &line_height); - xposd += fr->pad_l; - yposd += fr->pad_t; - - for ( i=0; i<cursor_line; i++ ) { - yposd += pango_units_to_double(fr->lines[i].height); - } - - for ( i=0; i<cursor_box; i++ ) { - int w = fr->lines[cursor_line].boxes[i].width; - w += fr->lines[cursor_line].boxes[i].sp; - xposd += pango_units_to_double(w); - } - - cx = fr->x + xposd; - clow = fr->y + yposd; - chigh = clow + line_height; - - 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, struct presentation *p) -{ - int i; - - for ( i=0; i<p->n_selection; i++ ) { - - double x, y, w, h; - - draw_editing_box(cr, p->selection[i]); - - x = p->selection[i]->x; - y = p->selection[i]->y; - w = p->selection[i]->w; - h = p->selection[i]->h; - - /* Draw resize handles */ - /* FIXME: Not if this frame can't be resized */ - 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); - } - - /* If only one frame is selected, draw the caret */ - if ( p->n_selection == 1 ) { - draw_caret(cr, p->cursor_frame, p->cursor_line, p->cursor_box, - p->cursor_pos); - } - - if ( (p->drag_status == DRAG_STATUS_DRAGGING) - && ((p->drag_reason == DRAG_REASON_CREATE) - || (p->drag_reason == DRAG_REASON_IMPORT)) ) - { - cairo_new_path(cr); - cairo_rectangle(cr, p->start_corner_x, p->start_corner_y, - p->drag_corner_x - p->start_corner_x, - p->drag_corner_y - p->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 ( (p->drag_status == DRAG_STATUS_DRAGGING) - && ((p->drag_reason == DRAG_REASON_RESIZE) - || (p->drag_reason == DRAG_REASON_MOVE)) ) - { - cairo_new_path(cr); - cairo_rectangle(cr, p->box_x, p->box_y, - p->box_width, p->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, - struct presentation *p) -{ - double xoff, yoff; - int width, height; - int edit_slide_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); - if ( (p->slideshow != NULL) && !p->slideshow_linked ) { - cairo_set_source_rgb(cr, 1.0, 0.3, 0.2); - } else { - cairo_set_source_rgb(cr, 0.9, 0.9, 0.9); - } - cairo_fill(cr); - - /* Get the overall size */ - edit_slide_height = (p->slide_height/p->slide_width)*p->edit_slide_width; - xoff = (width - p->edit_slide_width)/2.0; - yoff = (height - edit_slide_height)/2.0; - p->border_offs_x = xoff; p->border_offs_y = yoff; - - /* Draw the slide from the cache */ - if ( p->cur_edit_slide->rendered_edit != NULL ) { - cairo_set_source_surface(cr, p->cur_edit_slide->rendered_edit, - xoff, yoff); - cairo_paint(cr); - } else { - fprintf(stderr, "Current slide not rendered yet!\n"); - } - - cairo_translate(cr, xoff, yoff); - draw_overlay(cr, p); - - return FALSE; -} - - -void update_titlebar(struct presentation *p) -{ - get_titlebar_string(p); - - if ( p->window != NULL ) { - - char *title; - - title = malloc(strlen(p->titlebar)+14); - sprintf(title, "%s - Colloquium", p->titlebar); - gtk_window_set_title(GTK_WINDOW(p->window), title); - free(title); - - } -} - - -static void fixup_cursor(struct presentation *p) -{ - struct frame *fr; - struct wrap_line *sline; - struct wrap_box *sbox; - - fr = p->cursor_frame; - - if ( p->cursor_line >= fr->n_lines ) { - /* We find ourselves on a line which doesn't exist */ - p->cursor_line = fr->n_lines-1; - p->cursor_box = fr->lines[fr->n_lines-1].n_boxes-1; - } - - sline = &fr->lines[p->cursor_line]; - - if ( p->cursor_box >= sline->n_boxes ) { - - /* We find ourselves in a box which doesn't exist */ - - if ( p->cursor_line > fr->n_lines-1 ) { - /* This isn't the last line, so go to the first box of - * the next line */ - p->cursor_line++; - p->cursor_box = 0; - sline = &p->cursor_frame->lines[p->cursor_line]; - } else { - /* There are no more lines, so just go to the end */ - p->cursor_line = fr->n_lines-1; - sline = &p->cursor_frame->lines[p->cursor_line]; - p->cursor_box = sline->n_boxes-1; - } - } - - sbox = &sline->boxes[p->cursor_box]; - - if ( p->cursor_pos > sbox->len_chars ) { - advance_cursor(p); - } -} - - -static void move_cursor(struct presentation *p, signed int x, signed int y) -{ - if ( x > 0 ) { - advance_cursor(p); - } else { - move_cursor_back(p); - } -} - - -static void insert_text(char *t, struct presentation *p) -{ - int sln, sbx, sps; - struct wrap_box *sbox; - struct frame *fr = p->cursor_frame; - - if ( fr == NULL ) return; - - /* If this is, say, the top level frame, do nothing */ - if ( fr->boxes == NULL ) return; - - sln = p->cursor_line; - sbx = p->cursor_box; - sps = p->cursor_pos; - sbox = &p->cursor_frame->lines[sln].boxes[sbx]; - - sc_insert_text(sbox->scblock, sps+sbox->offs_char, t); - - fr->empty = 0; - - rerender_slide(p); - - fixup_cursor(p); - advance_cursor(p); - - redraw_editor(p); -} - - -static void do_backspace(struct frame *fr, struct presentation *p) -{ - int sln, sbx, sps; - - if ( fr == NULL ) return; - - /* If this is, say, the top level frame, do nothing */ - if ( fr->n_lines == 0 ) return; - - sln = p->cursor_line; - sbx = p->cursor_box; - sps = p->cursor_pos; - struct wrap_box *sbox = &p->cursor_frame->lines[sln].boxes[sbx]; - - move_cursor_back(p); - - /* Delete may cross wrap boxes and maybe SCBlock boundaries */ - struct wrap_line *fline = &p->cursor_frame->lines[p->cursor_line]; - struct wrap_box *fbox = &fline->boxes[p->cursor_box]; - -// SCBlock *scbl = sbox->scblock; -// do { -// show_sc_blocks(scbl); -// scbl = sc_block_next(scbl); -// } while ( (scbl != fbox->scblock) && (scbl != NULL) ); - - if ( (fbox->scblock == NULL) || (sbox->scblock == NULL) ) return; - sc_delete_text(fbox->scblock, p->cursor_pos+fbox->offs_char, - sbox->scblock, sps+sbox->offs_char); - -// scbl = sbox->scblock; -// do { -// show_sc_blocks(scbl); -// scbl = sc_block_next(scbl); -// } while ( (scbl != fbox->scblock) && (scbl != NULL) ); - - rerender_slide(p); - fixup_cursor(p); - redraw_editor(p); -} - - -static gboolean im_commit_sig(GtkIMContext *im, gchar *str, - struct presentation *p) -{ - if ( p->n_selection == 0 ) { - if ( str[0] == 'b' ) { - check_toggle_blank(p); - } else { - printf("IM keypress: %s\n", str); - } - return FALSE; - } - - insert_text(str, p); - - return FALSE; -} - - -static int within_frame(struct frame *fr, double x, double y) -{ - 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; i<fr->num_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, struct presentation *p, - double x, double y) -{ - double ddx, ddy, dlen, mult; - double vx, vy, dbx, dby; - - ddx = x - p->start_corner_x; - ddy = y - p->start_corner_y; - - if ( !fr->is_image ) { - - switch ( p->drag_corner ) { - - case CORNER_BR : - p->box_x = fr->x; - p->box_y = fr->y; - p->box_width = fr->w + ddx; - p->box_height = fr->h + ddy; - break; - - case CORNER_BL : - p->box_x = fr->x + ddx; - p->box_y = fr->y; - p->box_width = fr->w - ddx; - p->box_height = fr->h + ddy; - break; - - case CORNER_TL : - p->box_x = fr->x + ddx; - p->box_y = fr->y + ddy; - p->box_width = fr->w - ddx; - p->box_height = fr->h - ddy; - break; - - case CORNER_TR : - p->box_x = fr->x; - p->box_y = fr->y + ddy; - p->box_width = fr->w + ddx; - p->box_height = fr->h - ddy; - break; - - case CORNER_NONE : - break; - - } - return; - - - } - - switch ( p->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) / p->diagonal_length; - mult = (dlen+p->diagonal_length) / p->diagonal_length; - - p->box_width = fr->w * mult; - p->box_height = fr->h * mult; - dbx = p->box_width - fr->w; - dby = p->box_height - fr->h; - - if ( p->box_width < 40.0 ) { - mult = 40.0 / fr->w; - } - if ( p->box_height < 40.0 ) { - mult = 40.0 / fr->h; - } - p->box_width = fr->w * mult; - p->box_height = fr->h * mult; - dbx = p->box_width - fr->w; - dby = p->box_height - fr->h; - - switch ( p->drag_corner ) { - - case CORNER_BR : - p->box_x = fr->x; - p->box_y = fr->y; - break; - - case CORNER_BL : - p->box_x = fr->x - dbx; - p->box_y = fr->y; - break; - - case CORNER_TL : - p->box_x = fr->x - dbx; - p->box_y = fr->y - dby; - break; - - case CORNER_TR : - p->box_x = fr->x; - p->box_y = fr->y - dby; - break; - - case CORNER_NONE : - break; - - } - -} - - -static gboolean button_press_sig(GtkWidget *da, GdkEventButton *event, - struct presentation *p) -{ - enum corner c; - gdouble x, y; - struct frame *clicked; - - x = event->x - p->border_offs_x; - y = event->y - p->border_offs_y; - - if ( (p->n_selection > 0) && within_frame(p->selection[0], x, y) ) { - clicked = p->selection[0]; - } else { - clicked = find_frame_at_position(p->cur_edit_slide->top, x, y); - } - - /* If the user clicked the currently selected frame, position cursor - * or possibly prepare for resize */ - if ( (p->n_selection > 0) && (clicked == p->selection[0]) ) { - - struct frame *fr; - - fr = p->selection[0]; - - /* Within the resizing region? */ - c = which_corner(x, y, fr); - if ( c != CORNER_NONE ) { - - p->drag_reason = DRAG_REASON_RESIZE; - p->drag_corner = c; - - p->start_corner_x = x; - p->start_corner_y = y; - p->diagonal_length = pow(fr->w, 2.0); - p->diagonal_length += pow(fr->h, 2.0); - p->diagonal_length = sqrt(p->diagonal_length); - - calculate_box_size(fr, p, x, y); - - p->drag_status = DRAG_STATUS_COULD_DRAG; - p->drag_reason = DRAG_REASON_RESIZE; - - } else { - - p->cursor_frame = clicked; - find_cursor(clicked, x-fr->x, y-fr->y, - &p->cursor_line, &p->cursor_box, - &p->cursor_pos); - - p->start_corner_x = event->x - p->border_offs_x; - p->start_corner_y = event->y - p->border_offs_y; - p->drag_status = DRAG_STATUS_COULD_DRAG; - p->drag_reason = DRAG_REASON_MOVE; - - } - - } else if ( (clicked == NULL) || (clicked == p->cur_edit_slide->top) ) { - - /* Clicked no object. Deselect old object and set up for - * (maybe) creating a new one. */ - set_selection(p, NULL); - p->start_corner_x = event->x - p->border_offs_x; - p->start_corner_y = event->y - p->border_offs_y; - p->drag_status = DRAG_STATUS_COULD_DRAG; - p->drag_reason = DRAG_REASON_CREATE; - - } else { - - /* Select new frame, no immediate dragging */ - p->drag_status = DRAG_STATUS_NONE; - p->drag_reason = DRAG_REASON_NONE; - set_selection(p, clicked); - - } - - gtk_widget_grab_focus(GTK_WIDGET(da)); - redraw_editor(p); - return FALSE; -} - - -static gboolean motion_sig(GtkWidget *da, GdkEventMotion *event, - struct presentation *p) -{ - struct frame *fr = p->selection[0]; - gdouble x, y; - - x = event->x - p->border_offs_x; - y = event->y - p->border_offs_y; - - if ( p->drag_status == DRAG_STATUS_COULD_DRAG ) { - - /* We just got a motion signal, and the status was "could drag", - * therefore the drag has started. */ - p->drag_status = DRAG_STATUS_DRAGGING; - - } - - switch ( p->drag_reason ) { - - case DRAG_REASON_NONE : - break; - - case DRAG_REASON_CREATE : - p->drag_corner_x = x; - p->drag_corner_y = y; - redraw_editor(p); - break; - - case DRAG_REASON_IMPORT : - /* Do nothing, handled by dnd_motion() */ - break; - - case DRAG_REASON_RESIZE : - calculate_box_size(fr, p, x, y); - redraw_editor(p); - break; - - case DRAG_REASON_MOVE : - p->box_x = (fr->x - p->start_corner_x) + x; - p->box_y = (fr->y - p->start_corner_y) + y; - p->box_width = fr->w; - p->box_height = fr->h; - redraw_editor(p); - break; - - } - - gdk_event_request_motions(event); - return FALSE; -} - - -static struct frame *create_frame(struct presentation *p, double x, double y, - double w, double h) -{ - struct frame *parent; - struct frame *fr; - - parent = p->cur_edit_slide->top; - - if ( w < 0.0 ) { - x += w; - w = -w; - } - - if ( h < 0.0 ) { - y += h; - h = -h; - } - - fr = add_subframe(parent); - - /* Add to SC */ - fr->scblocks = sc_block_append_end(p->cur_edit_slide->scblocks, - "f", NULL, NULL); - sc_block_set_frame(fr->scblocks, fr); - sc_block_append_inside(fr->scblocks, NULL, NULL, strdup("")); - - fr->x = x; - fr->y = y; - fr->w = w; - fr->h = h; - fr->is_image = 0; - fr->empty = 1; - - update_geom(fr); - - return fr; -} - - -static void do_resize(struct presentation *p, double x, double y, - double w, double h) -{ - struct frame *fr; - - assert(p->n_selection > 0); - - if ( w < 0.0 ) { - w = -w; - x -= w; - } - - if ( h < 0.0 ) { - h = -h; - y -= h; - } - - fr = p->selection[0]; - fr->x = x; - fr->y = y; - fr->w = w; - fr->h = h; - update_geom(fr); - - rerender_slide(p); - redraw_editor(p); -} - - -static gboolean button_release_sig(GtkWidget *da, GdkEventButton *event, - struct presentation *p) -{ - gdouble x, y; - struct frame *fr; - - x = event->x - p->border_offs_x; - y = event->y - p->border_offs_y; - - /* Not dragging? Then I don't care. */ - if ( p->drag_status != DRAG_STATUS_DRAGGING ) return FALSE; - - p->drag_corner_x = x; - p->drag_corner_y = y; - p->drag_status = DRAG_STATUS_NONE; - - switch ( p->drag_reason ) - { - - case DRAG_REASON_NONE : - printf("Release on pointless drag.\n"); - break; - - case DRAG_REASON_CREATE : - fr = create_frame(p, p->start_corner_x, p->start_corner_y, - p->drag_corner_x - p->start_corner_x, - p->drag_corner_y - p->start_corner_y); - rerender_slide(p); - set_selection(p, fr); - break; - - case DRAG_REASON_IMPORT : - /* Do nothing, handled in dnd_drop() or dnd_leave() */ - break; - - case DRAG_REASON_RESIZE : - do_resize(p, p->box_x, p->box_y, p->box_width, p->box_height); - break; - - case DRAG_REASON_MOVE : - do_resize(p, p->box_x, p->box_y, p->box_width, p->box_height); - break; - - } - - p->drag_reason = DRAG_REASON_NONE; - - gtk_widget_grab_focus(GTK_WIDGET(da)); - redraw_editor(p); - return FALSE; -} - - -static gboolean key_press_sig(GtkWidget *da, GdkEventKey *event, - struct presentation *p) -{ - 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(p->im_context), - event); - if ( r ) return FALSE; /* IM ate it */ - - switch ( event->keyval ) { - - case GDK_KEY_Page_Up : - prev_slide_sig(NULL, p); - claim = 1; - break; - - case GDK_KEY_Page_Down : - next_slide_sig(NULL, p); - claim = 1; - break; - - case GDK_KEY_Escape : - if ( p->slideshow != NULL ) end_slideshow(p); - set_selection(p, NULL); - redraw_editor(p); - claim = 1; - break; - - case GDK_KEY_Left : - if ( p->n_selection == 1 ) { - move_cursor(p, -1, 0); - redraw_editor(p); - } - claim = 1; - break; - - case GDK_KEY_Right : - if ( p->n_selection == 1 ) { - move_cursor(p, +1, 0); - redraw_editor(p); - } - claim = 1; - break; - - case GDK_KEY_Up : - if ( p->n_selection == 1 ) { - move_cursor(p, 0, -1); - redraw_editor(p); - } - claim = 1; - break; - - case GDK_KEY_Down : - if ( p->n_selection == 1 ) { - move_cursor(p, 0, +1); - redraw_editor(p); - } - claim = 1; - break; - - - case GDK_KEY_Return : - im_commit_sig(NULL, "\n", p); - claim = 1; - break; - - case GDK_KEY_BackSpace : - if ( p->n_selection == 1 ) { - do_backspace(p->selection[0], p); - claim = 1; - } - break; - - case GDK_KEY_B : - case GDK_KEY_b : - if ( p->slideshow != NULL ) { - //if ( p->prefs->b_splits ) { - toggle_slideshow_link(p); - //} else { - // p->ss_blank = 1-p->ss_blank; - // redraw_slideshow(p); - //} - } - claim = 1; - break; - - } - - if ( claim ) return TRUE; - return FALSE; -} - - -static gboolean dnd_motion(GtkWidget *widget, GdkDragContext *drag_context, - gint x, gint y, guint time, struct presentation *p) -{ - GdkAtom target; - - /* If we haven't already requested the data, do so now */ - if ( !p->drag_preview_pending && !p->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); - p->drag_preview_pending = 1; - } else { - p->import_acceptable = 0; - gdk_drag_status(drag_context, 0, time); - } - - } - - if ( p->have_drag_data && p->import_acceptable ) { - - gdk_drag_status(drag_context, GDK_ACTION_LINK, time); - p->start_corner_x = x - p->import_width/2.0; - p->start_corner_y = y - p->import_height/2.0; - p->drag_corner_x = x + p->import_width/2.0; - p->drag_corner_y = y + p->import_height/2.0; - - redraw_editor(p); - - } - - return TRUE; -} - - -static gboolean dnd_drop(GtkWidget *widget, GdkDragContext *drag_context, - gint x, gint y, guint time, struct presentation *p) -{ - 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; -} - - -static void chomp(char *s) -{ - size_t i; - - if ( !s ) return; - - for ( i=0; i<strlen(s); i++ ) { - if ( (s[i] == '\n') || (s[i] == '\r') ) { - s[i] = '\0'; - return; - } - } -} - - -/* Scale the image down if it's a silly size */ -static void check_import_size(struct presentation *p) -{ - if ( p->import_width > p->slide_width ) { - - int new_import_width; - - new_import_width = p->slide_width/2; - p->import_height = (new_import_width *p->import_height) - / p->import_width; - p->import_width = new_import_width; - } - - if ( p->import_height > p->slide_height ) { - - int new_import_height; - - new_import_height = p->slide_height/2; - p->import_width = (new_import_height*p->import_width) - / p->import_height; - p->import_height = new_import_height; - } -} - - -static void dnd_receive(GtkWidget *widget, GdkDragContext *drag_context, - gint x, gint y, GtkSelectionData *seldata, - guint info, guint time, struct presentation *p) -{ - if ( p->drag_preview_pending ) { - - gchar *filename = NULL; - GdkPixbufFormat *f; - gchar **uris; - int w, h; - - p->have_drag_data = 1; - p->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 ( p->drag_highlight ) { - gtk_drag_unhighlight(widget); - p->drag_highlight = 0; - } - p->import_acceptable = 0; - return; - - } - chomp(filename); - - f = gdk_pixbuf_get_file_info(filename, &w, &h); - g_free(filename); - - p->import_width = w; - p->import_height = h; - - if ( f == NULL ) { - - gdk_drag_status(drag_context, 0, time); - if ( p->drag_highlight ) { - gtk_drag_unhighlight(widget); - p->drag_highlight = 0; - } - p->drag_status = DRAG_STATUS_NONE; - p->drag_reason = DRAG_REASON_NONE; - p->import_acceptable = 0; - - } else { - - /* Looks like a sensible image */ - gdk_drag_status(drag_context, GDK_ACTION_PRIVATE, time); - p->import_acceptable = 1; - - if ( !p->drag_highlight ) { - gtk_drag_highlight(widget); - p->drag_highlight = 1; - } - - check_import_size(p); - p->drag_reason = DRAG_REASON_IMPORT; - p->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 = p->drag_corner_x - p->start_corner_x; - h = p->drag_corner_y - p->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(p, p->start_corner_x, - p->start_corner_y, w, h); - fr->is_image = 1; - fr->empty = 0; - sc_block_append_inside(fr->scblocks, "image", opts, ""); - show_hierarchy(p->cur_edit_slide->top, ""); - rerender_slide(p); - set_selection(p, fr); - redraw_editor(p); - free(filename); - - } else { - - gtk_drag_finish(drag_context, FALSE, FALSE, time); - - } - - } -} - - -static void dnd_leave(GtkWidget *widget, GdkDragContext *drag_context, - guint time, struct presentation *p) -{ - if ( p->drag_highlight ) { - gtk_drag_unhighlight(widget); - } - p->have_drag_data = 0; - p->drag_highlight = 0; - p->drag_status = DRAG_STATUS_NONE; - p->drag_reason = DRAG_REASON_NONE; -} - - -static gint realise_sig(GtkWidget *da, struct presentation *p) -{ - GdkWindow *win; - - /* Keyboard and input method stuff */ - p->im_context = gtk_im_multicontext_new(); - win = gtk_widget_get_window(p->drawingarea); - gtk_im_context_set_client_window(GTK_IM_CONTEXT(p->im_context), win); - gdk_window_set_accept_focus(win, TRUE); - g_signal_connect(G_OBJECT(p->im_context), "commit", - G_CALLBACK(im_commit_sig), p); - g_signal_connect(G_OBJECT(p->drawingarea), "key-press-event", - G_CALLBACK(key_press_sig), p); - - /* FIXME: Can do this "properly" by setting up a separate font map */ - p->pc = gtk_widget_get_pango_context(da); - rerender_slide(p); - - return FALSE; -} - - -int open_mainwindow(struct presentation *p) -{ - GtkWidget *window; - GtkWidget *vbox; - GtkWidget *sw; - GtkTargetEntry targets[1]; - - if ( p->window != NULL ) { - fprintf(stderr, "Presentation window is already open!\n"); - return 1; - } - - window = gtk_window_new(GTK_WINDOW_TOPLEVEL); - p->window = window; - - update_titlebar(p); - - g_signal_connect(G_OBJECT(window), "destroy", G_CALLBACK(close_sig), p); - - vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0); - gtk_container_add(GTK_CONTAINER(window), vbox); - - p->drawingarea = gtk_drawing_area_new(); - sw = gtk_scrolled_window_new(NULL, NULL); - gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw), - GTK_POLICY_AUTOMATIC, - GTK_POLICY_AUTOMATIC); - gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(sw), - p->drawingarea); - gtk_widget_set_size_request(GTK_WIDGET(p->drawingarea), - p->slide_width + 20, - p->slide_height + 20); - - add_menu_bar(p, vbox); - gtk_box_pack_start(GTK_BOX(vbox), sw, TRUE, TRUE, 0); - g_signal_connect(G_OBJECT(p->drawingarea), "realize", - G_CALLBACK(realise_sig), p); - g_signal_connect(G_OBJECT(p->drawingarea), "button-press-event", - G_CALLBACK(button_press_sig), p); - g_signal_connect(G_OBJECT(p->drawingarea), "button-release-event", - G_CALLBACK(button_release_sig), p); - g_signal_connect(G_OBJECT(p->drawingarea), "motion-notify-event", - G_CALLBACK(motion_sig), p); - - /* Drag and drop */ - targets[0].target = "text/uri-list"; - targets[0].flags = 0; - targets[0].info = 1; - gtk_drag_dest_set(p->drawingarea, 0, targets, 1, GDK_ACTION_PRIVATE); - g_signal_connect(p->drawingarea, "drag-data-received", - G_CALLBACK(dnd_receive), p); - g_signal_connect(p->drawingarea, "drag-motion", - G_CALLBACK(dnd_motion), p); - g_signal_connect(p->drawingarea, "drag-drop", - G_CALLBACK(dnd_drop), p); - g_signal_connect(p->drawingarea, "drag-leave", - G_CALLBACK(dnd_leave), p); - - gtk_widget_set_can_focus(GTK_WIDGET(p->drawingarea), TRUE); - gtk_widget_add_events(GTK_WIDGET(p->drawingarea), - 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); - - g_signal_connect(G_OBJECT(p->drawingarea), "draw", - G_CALLBACK(draw_sig), p); - - /* Default size */ - gtk_window_set_default_size(GTK_WINDOW(p->window), 1024+100, 768+150); - gtk_window_set_resizable(GTK_WINDOW(p->window), TRUE); - - assert(p->num_slides > 0); - - gtk_widget_grab_focus(GTK_WIDGET(p->drawingarea)); - - gtk_widget_show_all(window); - - return 0; -} diff --git a/src/narrative_window.c b/src/narrative_window.c new file mode 100644 index 0000000..9c58c86 --- /dev/null +++ b/src/narrative_window.c @@ -0,0 +1,230 @@ +/* + * narrative_window.c + * + * Copyright © 2014 Thomas White <taw@bitwiz.org.uk> + * + * This file is part of Colloquium. + * + * Colloquium is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <gtk/gtk.h> +#include <assert.h> +#include <string.h> +#include <stdlib.h> + +#include "presentation.h" +#include "narrative_window.h" +#include "sc_editor.h" + + +struct _narrative_window +{ + GtkWidget *window; + SCEditor *sceditor; + GApplication *app; + struct presentation *p; +}; + + +static gint saveas_response_sig(GtkWidget *d, gint response, + NarrativeWindow *nw) +{ + if ( response == GTK_RESPONSE_ACCEPT ) { + + char *filename; + + filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(d)); + + if ( save_presentation(nw->p, filename) ) { + //show_error(sw, "Failed to save presentation"); + } + + g_free(filename); + + } + + gtk_widget_destroy(d); + + return 0; +} + + +static void saveas_sig(GSimpleAction *action, GVariant *parameter, gpointer vp) +{ + GtkWidget *d; + NarrativeWindow *nw = vp; + + d = gtk_file_chooser_dialog_new("Save Presentation", + GTK_WINDOW(nw->window), + GTK_FILE_CHOOSER_ACTION_SAVE, + GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, + GTK_STOCK_SAVE, 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(saveas_response_sig), nw); + + gtk_widget_show_all(d); +} + + +static void save_sig(GSimpleAction *action, GVariant *parameter, gpointer vp) +{ + NarrativeWindow *nw = vp; + + if ( nw->p->filename == NULL ) { + return saveas_sig(NULL, NULL, nw); + } + + save_presentation(nw->p, nw->p->filename); +} + + +static void exportpdf_sig(GSimpleAction *action, GVariant *parameter, + gpointer vp) +{ +} + + +static void open_slidesorter_sig(GSimpleAction *action, GVariant *parameter, gpointer vp) +{ +} + + +static void delete_frame_sig(GSimpleAction *action, GVariant *parameter, gpointer vp) +{ +} + + +static void add_slide_sig(GSimpleAction *action, GVariant *parameter, gpointer vp) +{ +} + + +static void start_slideshow_sig(GSimpleAction *action, GVariant *parameter, gpointer vp) +{ +} + + +static void open_notes_sig(GSimpleAction *action, GVariant *parameter, gpointer vp) +{ +} + + +static void open_clock_sig(GSimpleAction *action, GVariant *parameter, gpointer vp) +{ +} + + +GActionEntry nw_entries[] = { + + { "save", save_sig, NULL, NULL, NULL }, + { "saveas", saveas_sig, NULL, NULL, NULL }, + { "exportpdf", exportpdf_sig, NULL, NULL, NULL }, + { "sorter", open_slidesorter_sig, NULL, NULL, NULL }, + { "deleteframe", delete_frame_sig, NULL, NULL, NULL }, + { "slide", add_slide_sig, NULL, NULL, NULL }, + { "startslideshow", start_slideshow_sig, NULL, NULL, NULL }, + { "notes", open_notes_sig, NULL, NULL, NULL }, + { "clock", open_clock_sig, NULL, NULL, NULL }, +}; + + +static gboolean button_press_sig(GtkWidget *da, GdkEventButton *event, + NarrativeWindow *nw) +{ + if ( event->type == GDK_2BUTTON_PRESS ) { + nw->p->slidewindow = slide_window_open(nw->p, nw->app); + } + + return 0; +} + + +static void nw_update_titlebar(NarrativeWindow *nw) +{ + get_titlebar_string(nw->p); + + if ( nw->p->slidewindow != NULL ) { + + char *title; + + title = malloc(strlen(nw->p->titlebar)+14); + sprintf(title, "%s - Colloquium", nw->p->titlebar); + gtk_window_set_title(GTK_WINDOW(nw->window), title); + free(title); + + } + +} + + +NarrativeWindow *narrative_window_new(struct presentation *p, GApplication *app) +{ + NarrativeWindow *nw; + GtkWidget *vbox; + GtkWidget *scroll; + + 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 = app; + nw->p = p; + + nw->window = gtk_application_window_new(GTK_APPLICATION(app)); + p->narrative_window = nw; + + g_action_map_add_action_entries(G_ACTION_MAP(nw->window), nw_entries, + G_N_ELEMENTS(nw_entries), nw); + + nw_update_titlebar(nw); + + vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0); + gtk_container_add(GTK_CONTAINER(nw->window), vbox); + + nw->sceditor = sc_editor_new(nw->p->scblocks, p->stylesheet); + scroll = gtk_scrolled_window_new(NULL, NULL); + gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), + GTK_POLICY_AUTOMATIC, + GTK_POLICY_ALWAYS); + gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scroll), + GTK_WIDGET(nw->sceditor)); + + sc_editor_set_size(nw->sceditor, 640, 1024); + sc_editor_set_logical_size(nw->sceditor, 640.0, 1024.0); + + g_signal_connect(G_OBJECT(nw->sceditor), "button-press-event", + G_CALLBACK(button_press_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_widget_show_all(nw->window); + + return nw; +} diff --git a/src/narrative_window.h b/src/narrative_window.h new file mode 100644 index 0000000..8c32c71 --- /dev/null +++ b/src/narrative_window.h @@ -0,0 +1,37 @@ +/* + * narrative_window.h + * + * Copyright © 2014 Thomas White <taw@bitwiz.org.uk> + * + * This file is part of Colloquium. + * + * Colloquium is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + +#ifndef NARRATIVE_WINDOW_H +#define NARRATIVE_WINDOW_H + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + + +typedef struct _narrative_window NarrativeWindow; + +extern NarrativeWindow *narrative_window_new(struct presentation *p, + GApplication *app); + + +#endif /* NARRATIVE_WINDOW_H */ diff --git a/src/notes.c b/src/notes.c index 2210f55..6ec0773 100644 --- a/src/notes.c +++ b/src/notes.c @@ -31,50 +31,44 @@ #include <gtk/gtk.h> #include "presentation.h" - +#include "notes.h" struct notes { GtkWidget *window; GtkWidget *v; - struct slide *slide; + struct slide *cur_slide; + SlideWindow *sw; }; -static void set_notes_title(struct presentation *p) -{ - gtk_window_set_title(GTK_WINDOW(p->notes->window), "Colloquium notes"); -} - - -static void update_notes(struct presentation *p) +static void update_notes(struct notes *notes) { GtkTextBuffer *tb; const char *ntext; SCBlock *ch; - if ( p->notes == NULL ) return; + if ( notes == NULL ) return; - ch = sc_block_child(p->cur_edit_slide->notes); + ch = sc_block_child(notes->cur_slide->notes); if ( ch != NULL ) { ntext = sc_block_contents(ch); } else { ntext = "NOTES ERROR"; } - tb = gtk_text_view_get_buffer(GTK_TEXT_VIEW(p->notes->v)); + tb = gtk_text_view_get_buffer(GTK_TEXT_VIEW(notes->v)); gtk_text_buffer_set_text(tb, ntext, -1); } -void grab_current_notes(struct presentation *p) +void grab_current_notes(struct notes *n) { gchar *text; GtkTextBuffer *tb; GtkTextIter i1, i2; SCBlock *ch; - struct notes *n = p->notes; if ( n == NULL ) return; @@ -83,7 +77,7 @@ void grab_current_notes(struct presentation *p) gtk_text_buffer_get_end_iter(tb, &i2); text = gtk_text_buffer_get_text(tb, &i1, &i2, TRUE); - ch = sc_block_child(n->slide->notes); + ch = sc_block_child(n->cur_slide->notes); if ( ch != NULL ) { sc_block_set_contents(ch, text); } else { @@ -92,35 +86,33 @@ void grab_current_notes(struct presentation *p) } -void notify_notes_slide_changed(struct presentation *p, struct slide *np) +void notes_set_slide(struct notes *notes, struct slide *np) { - grab_current_notes(p); - p->notes->slide = np; - update_notes(p); + if ( notes == NULL ) return; + grab_current_notes(notes); + notes->cur_slide = np; + update_notes(notes); } -static gint close_notes_sig(GtkWidget *w, struct presentation *p) +static gint close_notes_sig(GtkWidget *w, struct notes *notes) { - grab_current_notes(p); - p->notes = NULL; + grab_current_notes(notes); + slidewindow_notes_closed(notes->sw); return FALSE; } -void open_notes(struct presentation *p) +struct notes *open_notes(SlideWindow *sw, struct slide *slide) { struct notes *n; GtkWidget *sc; PangoFontDescription *desc; - if ( p->notes != NULL ) return; /* Already open */ - n = malloc(sizeof(struct notes)); - if ( n == NULL ) return; - p->notes = n; - - p->notes->slide = p->cur_edit_slide; + if ( n == NULL ) return NULL; + n->sw = sw; + n->cur_slide = slide; n->window = gtk_window_new(GTK_WINDOW_TOPLEVEL); gtk_window_set_default_size(GTK_WINDOW(n->window), 800, 256); @@ -137,12 +129,13 @@ void open_notes(struct presentation *p) gtk_container_add(GTK_CONTAINER(sc), n->v); g_signal_connect(G_OBJECT(n->v), "destroy", - G_CALLBACK(close_notes_sig), p); + G_CALLBACK(close_notes_sig), n); - set_notes_title(p); + gtk_window_set_title(GTK_WINDOW(n->window), "Colloquium notes"); gtk_widget_show_all(n->window); - update_notes(p); + update_notes(n); + return n; } diff --git a/src/notes.h b/src/notes.h index 31c5e3a..426cb7e 100644 --- a/src/notes.h +++ b/src/notes.h @@ -29,12 +29,11 @@ struct notes; -extern void open_notes(struct presentation *p); +extern struct notes *open_notes(SlideWindow *sw, struct slide *slide); -extern void notify_notes_slide_changed(struct presentation *p, - struct slide *np); +extern void notes_set_slide(struct notes *n, struct slide *np); -extern void grab_current_notes(struct presentation *p); +extern void grab_current_notes(struct notes *n); extern void attach_notes(struct slide *s); diff --git a/src/presentation.c b/src/presentation.c index 56bbbd6..d99ec55 100644 --- a/src/presentation.c +++ b/src/presentation.c @@ -31,7 +31,7 @@ #include <gtk/gtk.h> #include "presentation.h" -#include "mainwindow.h" +#include "slide_window.h" #include "frame.h" #include "imagestore.h" #include "wrap.h" @@ -41,9 +41,6 @@ #include "sc_interp.h" -static int num_presentations = 0; - - void free_presentation(struct presentation *p) { int i; @@ -53,11 +50,6 @@ void free_presentation(struct presentation *p) free_slide(p->slides[i]); } - (*p->num_presentations)--; - if ( *p->num_presentations == 0 ) final = 1; - - if ( p->inhibit != NULL ) inhibit_cleanup(p->inhibit); - /* FIXME: Loads of stuff leaks here */ free(p->filename); imagestore_destroy(p->is); @@ -132,13 +124,6 @@ struct slide *new_slide() new = calloc(1, sizeof(struct slide)); if ( new == NULL ) return NULL; - new->rendered_edit = NULL; - new->rendered_proj = NULL; - new->rendered_thumb = NULL; - - new->top = frame_new(); - /* FIXME: Set zero margins etc on top level frame */ - new->scblocks = NULL; new->notes = NULL; @@ -198,14 +183,12 @@ static char *safe_basename(const char *in) } -void get_titlebar_string(struct presentation *p) +char *get_titlebar_string(struct presentation *p) { - free(p->titlebar); - if ( p->filename == NULL ) { - p->titlebar = strdup("(untitled)"); + return strdup("(untitled)"); } else { - p->titlebar = safe_basename(p->filename); + return safe_basename(p->filename); } } @@ -222,17 +205,6 @@ int slide_number(struct presentation *p, struct slide *s) } -static int alloc_selection(struct presentation *p) -{ - struct frame **new_selection; - new_selection = realloc(p->selection, - p->max_selection*sizeof(struct frame *)); - if ( new_selection == NULL ) return 1; - p->selection = new_selection; - return 0; -} - - struct presentation *new_presentation() { struct presentation *new; @@ -240,43 +212,18 @@ struct presentation *new_presentation() new = calloc(1, sizeof(struct presentation)); if ( new == NULL ) return NULL; - num_presentations++; - new->num_presentations = &num_presentations; - new->filename = NULL; - new->titlebar = NULL; - get_titlebar_string(new); - - new->window = NULL; - new->ui = NULL; - new->action_group = NULL; - new->slideshow = NULL; + new->titlebar = get_titlebar_string(new); new->slide_width = 1024.0; new->slide_height = 768.0; - new->edit_slide_width = 1024; - new->proj_slide_width = 2048; - new->thumb_slide_width = 180; - - /* Add one blank slide and view it */ new->num_slides = 0; new->slides = NULL; - new->cur_edit_slide = NULL; - new->cur_proj_slide = NULL; + add_slide(new, 0); new->completely_empty = 1; - new->stylesheet = NULL; - - new->n_style_menu = 0; - new->style_menu = NULL; - - new->selection = NULL; - new->n_selection = 0; - new->max_selection = 64; - if ( alloc_selection(new) ) return NULL; - new->is = imagestore_new(); return new; @@ -288,7 +235,7 @@ int save_presentation(struct presentation *p, const char *filename) FILE *fh; char *old_fn; - grab_current_notes(p); + // FIXME grab_current_notes(p); fh = fopen(filename, "w"); if ( fh == NULL ) return 1; @@ -361,7 +308,6 @@ int load_presentation(struct presentation *p, const char *filename) { FILE *fh; int r = 0; - int i; char *everything; size_t el = 1; SCBlock *block; @@ -369,6 +315,7 @@ int load_presentation(struct presentation *p, const char *filename) everything = strdup(""); assert(p->completely_empty); + delete_slide(p, p->slides[0]); fh = fopen(filename, "r"); if ( fh == NULL ) return 1; @@ -400,8 +347,6 @@ int load_presentation(struct presentation *p, const char *filename) if ( p->scblocks == NULL ) r = 1; if ( r ) { - p->cur_edit_slide = new_slide(); - insert_slide(p, p->cur_edit_slide, 0); p->completely_empty = 1; return r; /* Error */ } @@ -422,8 +367,6 @@ int load_presentation(struct presentation *p, const char *filename) if ( s != NULL ) { s->scblocks = sc_block_child(block); - s->top = frame_new(); - s->top->scblocks = sc_block_child(block); attach_notes(s); } @@ -440,16 +383,6 @@ next: update_titlebar(p); imagestore_set_presentation_file(p->is, filename); - p->cur_edit_slide = p->slides[0]; - - for ( i=0; i<p->num_slides; i++ ) { - struct slide *s = p->slides[i]; - s->rendered_thumb = render_slide(s, p->thumb_slide_width, - p->slide_width, - p->slide_height, - p->is, ISZ_THUMBNAIL, i); - } - return 0; } @@ -474,12 +407,12 @@ static struct frame *find_parent(struct frame *fr, struct frame *search) } -void delete_subframe(struct slide *s, struct frame *fr) +void delete_subframe(struct frame *top, struct frame *fr) { struct frame *parent; int i, idx, found; - parent = find_parent(s->top, fr); + parent = find_parent(top, fr); if ( parent == NULL ) { fprintf(stderr, "Couldn't find parent when deleting frame.\n"); return; @@ -506,41 +439,3 @@ void delete_subframe(struct slide *s, struct frame *fr) parent->num_children--; } - -void set_edit(struct presentation *p, struct slide *s) -{ - p->cur_edit_slide = s; -} - - -void set_selection(struct presentation *p, struct frame *fr) -{ - if ( p->n_selection != 0 ) { - int i; - for ( i=0; i<p->n_selection; i++ ) { - if ( p->selection[i]->empty ) { - delete_subframe(p->cur_edit_slide, - p->selection[i]); - } - } - } - - p->selection[0] = fr; - p->n_selection = 1; - - if ( fr == NULL ) p->n_selection = 0; -} - - -void add_selection(struct presentation *p, struct frame *fr) -{ - if ( p->n_selection == p->max_selection ) { - p->max_selection += 64; - if ( alloc_selection(p) ) { - fprintf(stderr, "Not enough memory for selection.\n"); - return; - } - } - - p->selection[p->n_selection++] = fr; -} diff --git a/src/presentation.h b/src/presentation.h index a7bb0e9..fd78a11 100644 --- a/src/presentation.h +++ b/src/presentation.h @@ -30,165 +30,68 @@ #include <cairo.h> #include <gtk/gtk.h> +struct presentation; +struct slide; + #include "imagestore.h" #include "sc_parse.h" - +#include "slideshow.h" +#include "narrative_window.h" +#include "slide_window.h" struct slide { struct presentation *parent; struct slide_template *st; - /* Any of these may be NULL */ - cairo_surface_t *rendered_proj; - cairo_surface_t *rendered_edit; - - /* This should always be present (and up to date). */ - cairo_surface_t *rendered_thumb; - - struct frame *top; - SCBlock *scblocks; SCBlock *notes; }; - -enum drag_reason -{ - DRAG_REASON_NONE, - DRAG_REASON_CREATE, - DRAG_REASON_IMPORT, - DRAG_REASON_RESIZE, - DRAG_REASON_MOVE -}; - - -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 menu_pl; - struct presentation { - char *titlebar; char *filename; + char *titlebar; /* basename(filename) or "(untitled)" */ int completely_empty; - int *num_presentations; - - struct presentation_constants *constants; - - GtkWidget *window; - GtkWidget *drawingarea; - GtkUIManager *ui; - GtkActionGroup *action_group; - GtkIMContext *im_context; - struct menu_pl *style_menu; - int n_style_menu; - PangoContext *pc; + ImageStore *is; + NarrativeWindow *narrative_window; + SlideWindow *slidewindow; + struct notes *notes; struct pr_clock *clock; struct slide_sorter *slide_sorter; - /* Pointers to the current "editing" and "projection" slides */ - struct slide *cur_edit_slide; - struct slide *cur_proj_slide; - int slideshow_linked; - - /* Pointers to the frame currently being edited */ - struct frame **selection; - int n_selection; - int max_selection; - - /* Location of the cursor */ - struct frame *cursor_frame; - int cursor_line; - int cursor_box; - int cursor_pos; /* characters into box */ - /* 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; - /* Width of a slide in the editor, projector or thumbnail (pixels) */ - int edit_slide_width; - int proj_slide_width; - int thumb_slide_width; - - /* This is just to help with rendering the slides within the - * editing window. */ - double border_offs_x; - double border_offs_y; - - /* 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; - - /* 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; - - /* Slideshow stuff */ - GtkWidget *slideshow; - GtkWidget *ss_drawingarea; - GdkCursor *blank_cursor; - int ss_blank; - char ss_geom[256]; - unsigned int num_slides; struct slide **slides; SCBlock *stylesheet; SCBlock *scblocks; - struct inhibit_sys *inhibit; }; extern struct presentation *new_presentation(void); extern void free_presentation(struct presentation *p); +extern char *get_titlebar_string(struct presentation *p); + extern struct slide *new_slide(void); extern struct slide *add_slide(struct presentation *p, int pos); extern int insert_slide(struct presentation *p, struct slide *s, int pos); extern void free_slide(struct slide *s); extern void delete_slide(struct presentation *p, struct slide *s); -extern void delete_subframe(struct slide *s, struct frame *fr); +extern void delete_subframe(struct frame *top, struct frame *fr); -extern void get_titlebar_string(struct presentation *p); extern char *packed_sc(struct frame *fr); @@ -198,8 +101,6 @@ extern int load_presentation(struct presentation *p, const char *filename); extern int save_presentation(struct presentation *p, const char *filename); extern void set_edit(struct presentation *p, struct slide *s); -extern void set_selection(struct presentation *p, struct frame *fr); -extern void add_selection(struct presentation *p, struct frame *fr); #define UNUSED __attribute__((unused)) diff --git a/src/render.c b/src/render.c index 598a40f..719677a 100644 --- a/src/render.c +++ b/src/render.c @@ -343,45 +343,19 @@ static int recursive_wrap_and_draw(struct frame *fr, cairo_t *cr, } -void free_render_buffers(struct slide *s) -{ - if ( s->rendered_edit != NULL ) cairo_surface_destroy(s->rendered_edit); - if ( s->rendered_proj != NULL ) cairo_surface_destroy(s->rendered_proj); - if ( s->rendered_thumb != NULL ) { - cairo_surface_destroy(s->rendered_thumb); - } - - s->rendered_edit = NULL; - s->rendered_proj = NULL; - s->rendered_thumb = NULL; -} - - -void free_render_buffers_except_thumb(struct slide *s) -{ - if ( s->rendered_edit != NULL ) cairo_surface_destroy(s->rendered_edit); - if ( s->rendered_proj != NULL ) cairo_surface_destroy(s->rendered_proj); - - s->rendered_edit = NULL; - s->rendered_proj = NULL; -} - - - -static void render_slide_to_surface(struct slide *s, cairo_surface_t *surf, - cairo_t *cr, enum is_size isz, - double scale, - ImageStore *is, int slide_number) +static void render_sc_to_surface(SCBlock *scblocks, cairo_surface_t *surf, + cairo_t *cr, double log_w, double log_h, + SCBlock *stylesheet, + ImageStore *is, enum is_size isz, + int slide_number) { PangoFontMap *fontmap; PangoContext *pc; SCInterpreter *scin; char snum[64]; + struct frame *top; - cairo_scale(cr, scale, scale); - - cairo_rectangle(cr, 0.0, 0.0, - s->parent->slide_width, s->parent->slide_height); + 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); @@ -397,7 +371,17 @@ static void render_slide_to_surface(struct slide *s, cairo_surface_t *surf, pc = pango_font_map_create_context(fontmap); pango_cairo_update_context(cr, pc); - scin = sc_interp_new(pc, s->top); + top = sc_block_frame(scblocks); + if ( top == NULL ) { + top = frame_new(); + sc_block_set_frame(scblocks, top); + } + top->x = 0.0; + top->y = 0.0; + top->w = log_w; + top->h = log_h; + + scin = sc_interp_new(pc, top); if ( scin == NULL ) { fprintf(stderr, "Failed to set up interpreter.\n"); return; @@ -407,10 +391,10 @@ static void render_slide_to_surface(struct slide *s, cairo_surface_t *surf, add_macro(scin, "slidenumber", snum); /* "The rendering pipeline" */ - sc_interp_run_stylesheet(scin, s->parent->stylesheet); - renew_frame(s->top); - sc_interp_add_blocks(scin, s->scblocks); - recursive_wrap_and_draw(s->top, cr, is, isz); + if ( stylesheet != NULL ) sc_interp_run_stylesheet(scin, stylesheet); + renew_frame(top); + sc_interp_add_blocks(scin, scblocks); + recursive_wrap_and_draw(top, cr, is, isz); sc_interp_destroy(scin); cairo_font_options_destroy(fopts); @@ -421,33 +405,27 @@ static void render_slide_to_surface(struct slide *s, cairo_surface_t *surf, /** * render_slide: * @s: A slide. - * @w: Width of the bitmap to produce - * @ww: Width of the slide in Cairo units - * @hh: Height of the slide in Cairo units + * @w: Width of bitmap to output + * @h: Height of bitmap to produce + * @ww: Logical width of the rendering area + * @hh: Logical height of the rendering area * * Render the entire slide. */ -cairo_surface_t *render_slide(struct slide *s, int w, double ww, double hh, - ImageStore *is, enum is_size isz, - int slide_number) +cairo_surface_t *render_sc(SCBlock *scblocks, int w, int h, + double log_w, double log_h, + SCBlock *stylesheet, + ImageStore *is, enum is_size isz, + int slide_number) { cairo_surface_t *surf; cairo_t *cr; - int h; - double scale; - - h = (hh/ww)*w; - scale = w/ww; - - s->top->x = 0.0; - s->top->y = 0.0; - s->top->w = ww; - s->top->h = hh; surf = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, w, h); cr = cairo_create(surf); - render_slide_to_surface(s, surf, cr, isz, scale, is, - slide_number); + cairo_scale(cr, w/log_w, h/log_h); + render_sc_to_surface(scblocks, surf, cr, log_w, log_h, + stylesheet, is, isz,slide_number); cairo_destroy(cr); return surf; } @@ -481,17 +459,15 @@ int export_pdf(struct presentation *p, const char *filename) 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); - s->top->x = 0.0; - s->top->y = 0.0; - s->top->w = w; - s->top->h = w*r; - - render_slide_to_surface(s, surf, cr, ISZ_SLIDESHOW, scale, - p->is, i); + render_sc_to_surface(s->scblocks, surf, cr, p->slide_width, + p->slide_height, p->stylesheet, + p->is, ISZ_SLIDESHOW, i); cairo_restore(cr); diff --git a/src/render.h b/src/render.h index 3099d35..37e66b2 100644 --- a/src/render.h +++ b/src/render.h @@ -30,11 +30,12 @@ #include "presentation.h" #include "imagestore.h" -extern cairo_surface_t *render_slide(struct slide *s, int w, - double ww, double hh, ImageStore *is, - enum is_size isz, int slide_number); -extern void free_render_buffers(struct slide *s); -extern void free_render_buffers_except_thumb(struct slide *s); +extern cairo_surface_t *render_sc(SCBlock *scblocks, int w, int h, + double log_w, double log_h, + SCBlock *stylesheet, + ImageStore *is, enum is_size isz, + int slide_number); + extern int export_pdf(struct presentation *p, const char *filename); #endif /* RENDER_H */ diff --git a/src/sc_editor.c b/src/sc_editor.c new file mode 100644 index 0000000..f357514 --- /dev/null +++ b/src/sc_editor.c @@ -0,0 +1,1464 @@ +/* + * sc_editor.c + * + * Copyright © 2013-2014 Thomas White <taw@bitwiz.org.uk> + * + * This file is part of Colloquium. + * + * Colloquium is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <stdlib.h> +#include <string.h> +#include <gtk/gtk.h> +#include <assert.h> +#include <gdk/gdkkeysyms.h> +#include <gdk-pixbuf/gdk-pixbuf.h> +#include <math.h> + +#include "colloquium.h" +#include "presentation.h" +#include "slide_window.h" +#include "render.h" +#include "frame.h" +#include "wrap.h" +#include "sc_parse.h" +#include "sc_interp.h" +#include "sc_editor.h" +#include "slideshow.h" + + +G_DEFINE_TYPE(SCEditor, sc_editor, GTK_TYPE_DRAWING_AREA); + + +static void sc_editor_class_init(SCEditorClass *klass) +{ +} + + +static void sc_editor_init(SCEditor *self) +{ +} + + +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; +} + + +/* Update the view, once it's been edited in some way. */ +static void rerender(SCEditor *e) +{ + if ( e->surface != NULL ) { + cairo_surface_destroy(e->surface); + } + + e->surface = render_sc(e->scblocks, e->w, e->h, e->log_w, e->log_h, + e->stylesheet, e->is, ISZ_EDITOR, e->slidenum); +} + + +/* Force a redraw of the editor window */ +static void redraw_editor(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); +} + + +/* Force a redraw of the editor window */ +void sc_editor_redraw(SCEditor *e) +{ + redraw_editor(e); +} + + +static void move_cursor_back(SCEditor *e) +{ + int retreat = 0; + signed int cp, cb, cl; + struct wrap_line *line; + struct wrap_box *box; + + cp = e->cursor_pos; + cb = e->cursor_box; + cl = e->cursor_line; + + line = &e->cursor_frame->lines[e->cursor_line]; + box = &line->boxes[e->cursor_box]; + if ( box->type == WRAP_BOX_PANGO ) { + + if ( cp == 0 ) { + retreat = 1; + } else { + cp--; + } + + } else { + cp--; + if ( cp < 0 ) retreat = 1; + } + + if ( retreat ) { + + do { + + cb--; + + if ( cb < 0 ) { + cl--; + if ( cl < 0 ) return; + e->cursor_line = cl; + line = &e->cursor_frame->lines[cl]; + cb = line->n_boxes - 1; + } + + } while ( !line->boxes[cb].editable ); + + e->cursor_box = cb; + box = &line->boxes[cb]; + if ( box->type == WRAP_BOX_PANGO ) { + cp = box->len_chars; + if ( box->space == WRAP_SPACE_NONE ) { + cp--; + } + } else { + cp = 1; + } + + } + e->cursor_pos = cp; +} + + +void cur_box_diag(SCEditor *e) +{ + int sln, sbx, sps; + struct frame *fr; + + fr = e->cursor_frame; + sln = e->cursor_line; + sbx = e->cursor_box; + sps = e->cursor_pos; + + struct wrap_box *sbox = &e->cursor_frame->lines[sln].boxes[sbx]; + + printf("line/box/pos: [%i of %i]/[%i of %i]/[%i of %i]\n", + sln, fr->n_lines, + sbx, e->cursor_frame->lines[sln].n_boxes, + sps, sbox->len_chars); + printf("box type is %i, space type is %i\n", sbox->type, sbox->space); + if ( sbox->type == WRAP_BOX_NOTHING ) { + printf("Warning: in a nothing box!\n"); + } +} + + +void advance_cursor(SCEditor *e) +{ + int advance = 0; + signed int cp, cb, cl; + struct wrap_line *line = &e->cursor_frame->lines[e->cursor_line]; + struct wrap_box *box = &line->boxes[e->cursor_box]; + + cp = e->cursor_pos; + cb = e->cursor_box; + cl = e->cursor_line; + + switch ( box->type ) { + + case WRAP_BOX_PANGO: + if ( cp+1 > box->len_chars ) { + advance = 1; + } else { + cp++; + } + break; + + case WRAP_BOX_NOTHING: + case WRAP_BOX_SENTINEL: + advance = 1; + break; + + case WRAP_BOX_IMAGE: + cp++; + if ( cp > 1 ) advance = 1; + break; + + } + + if ( advance ) { + + do { + + cb++; + cp = 0; + + if ( box->space == WRAP_SPACE_NONE ) { + cp = 1; + } + + if ( cb >= line->n_boxes ) { + cl++; + if ( cl >= e->cursor_frame->n_lines ) { + /* Give up - could not move */ + return; + } + line = &e->cursor_frame->lines[cl]; + cb = 0; + cp = 0; + } + + } while ( !line->boxes[cb].editable ); + + e->cursor_line = cl; + e->cursor_box = cb; + + } + e->cursor_pos = cp; +} + + +static gint destroy_sig(GtkWidget *window, SCEditor *sceditor) +{ + /* FIXME: free stuff */ + 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_caret(cairo_t *cr, struct frame *fr, + int cursor_line, int cursor_box, int cursor_pos) +{ + double xposd, yposd, line_height; + double cx, clow, chigh; + const double t = 1.8; + struct wrap_box *box; + int i; + + if ( fr == NULL ) return; + if ( fr->n_lines == 0 ) return; + + /* Locate the cursor in a "logical" and "geographical" sense */ + box = &fr->lines[cursor_line].boxes[cursor_box]; + get_cursor_pos(box, cursor_pos, &xposd, &yposd, &line_height); + xposd += fr->pad_l; + yposd += fr->pad_t; + + for ( i=0; i<cursor_line; i++ ) { + yposd += pango_units_to_double(fr->lines[i].height); + } + + for ( i=0; i<cursor_box; i++ ) { + int w = fr->lines[cursor_line].boxes[i].width; + w += fr->lines[cursor_line].boxes[i].sp; + xposd += pango_units_to_double(w); + } + + cx = fr->x + xposd; + clow = fr->y + yposd; + chigh = clow + line_height; + + 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) +{ + double x, y, w, h; + + + if ( e->selection != NULL ) { + + draw_editing_box(cr, e->selection); + + x = e->selection->x; + y = e->selection->y; + w = e->selection->w; + h = e->selection->h; + + /* Draw resize handles */ + /* FIXME: Not if this frame can't be resized */ + 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->cursor_line, e->cursor_box, + e->cursor_pos); + + } + + 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) +{ + double xoff, yoff; + int 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, e->bgcol[0], e->bgcol[1], e->bgcol[2]); + cairo_fill(cr); + + /* Get the overall size */ + xoff = (width - e->w)/2.0; + yoff = (height - e->h)/2.0; + e->border_offs_x = xoff; + e->border_offs_y = yoff; + + /* Draw the slide from the cache */ + if ( e->surface != NULL ) { + cairo_set_source_surface(cr, e->surface, xoff, yoff); + cairo_paint(cr); + } else { + fprintf(stderr, "Current slide not rendered yet!\n"); + } + + cairo_translate(cr, xoff, yoff); + draw_overlay(cr, e); + + return FALSE; +} + + +static void fixup_cursor(SCEditor *e) +{ + struct frame *fr; + struct wrap_line *sline; + struct wrap_box *sbox; + + fr = e->cursor_frame; + + if ( e->cursor_line >= fr->n_lines ) { + /* We find ourselves on a line which doesn't exist */ + e->cursor_line = fr->n_lines-1; + e->cursor_box = fr->lines[fr->n_lines-1].n_boxes-1; + } + + sline = &fr->lines[e->cursor_line]; + + if ( e->cursor_box >= sline->n_boxes ) { + + /* We find ourselves in a box which doesn't exist */ + + if ( e->cursor_line > fr->n_lines-1 ) { + /* This isn't the last line, so go to the first box of + * the next line */ + e->cursor_line++; + e->cursor_box = 0; + sline = &e->cursor_frame->lines[e->cursor_line]; + } else { + /* There are no more lines, so just go to the end */ + e->cursor_line = fr->n_lines-1; + sline = &e->cursor_frame->lines[e->cursor_line]; + e->cursor_box = sline->n_boxes-1; + } + } + + sbox = &sline->boxes[e->cursor_box]; + + if ( e->cursor_pos > sbox->len_chars ) { + advance_cursor(e); + } +} + + +static void move_cursor(SCEditor *e, signed int x, signed int y) +{ + if ( x > 0 ) { + advance_cursor(e); + } else { + move_cursor_back(e); + } +} + + +static void insert_text(char *t, SCEditor *e) +{ + int sln, sbx, sps; + struct wrap_box *sbox; + struct frame *fr = e->cursor_frame; + + if ( fr == NULL ) return; + + /* If this is, say, the top level frame, do nothing */ + if ( fr->boxes == NULL ) return; + + sln = e->cursor_line; + sbx = e->cursor_box; + sps = e->cursor_pos; + sbox = &e->cursor_frame->lines[sln].boxes[sbx]; + + sc_insert_text(sbox->scblock, sps+sbox->offs_char, t); + + fr->empty = 0; + + rerender(e); + + fixup_cursor(e); + advance_cursor(e); + + redraw_editor(e); +} + + +static void do_backspace(struct frame *fr, SCEditor *e) +{ + int sln, sbx, sps; + + if ( fr == NULL ) return; + + /* If this is, say, the top level frame, do nothing */ + if ( fr->n_lines == 0 ) return; + + sln = e->cursor_line; + sbx = e->cursor_box; + sps = e->cursor_pos; + struct wrap_box *sbox = &e->cursor_frame->lines[sln].boxes[sbx]; + + move_cursor_back(e); + + /* Delete may cross wrap boxes and maybe SCBlock boundaries */ + struct wrap_line *fline = &e->cursor_frame->lines[e->cursor_line]; + struct wrap_box *fbox = &fline->boxes[e->cursor_box]; + +// SCBlock *scbl = sbox->scblock; +// do { +// show_sc_blocks(scbl); +// scbl = sc_block_next(scbl); +// } while ( (scbl != fbox->scblock) && (scbl != NULL) ); + + if ( (fbox->scblock == NULL) || (sbox->scblock == NULL) ) return; + sc_delete_text(fbox->scblock, e->cursor_pos+fbox->offs_char, + sbox->scblock, sps+sbox->offs_char); + +// scbl = sbox->scblock; +// do { +// show_sc_blocks(scbl); +// scbl = sc_block_next(scbl); +// } while ( (scbl != fbox->scblock) && (scbl != NULL) ); + + rerender(e); + fixup_cursor(e); + redraw_editor(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; i<fr->num_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 gboolean button_press_sig(GtkWidget *da, GdkEventButton *event, + SCEditor *e) +{ + enum corner c; + gdouble x, y; + struct frame *clicked; + + x = event->x - e->border_offs_x; + y = event->y - e->border_offs_y; + + if ( within_frame(e->selection, x, y) ) { + clicked = e->selection; + } else { + clicked = find_frame_at_position(sc_block_frame(e->scblocks), + x, y); + } + + /* If the user clicked the currently selected frame, position cursor + * or possibly prepare for resize */ + 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 ) { + + 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 { + + e->cursor_frame = clicked; + find_cursor(clicked, x-fr->x, y-fr->y, + &e->cursor_line, &e->cursor_box, + &e->cursor_pos); + + e->start_corner_x = event->x - e->border_offs_x; + e->start_corner_y = event->y - e->border_offs_y; + e->drag_status = DRAG_STATUS_COULD_DRAG; + e->drag_reason = DRAG_REASON_MOVE; + + } + + } else if ( (clicked == NULL) + || (clicked == sc_block_frame(e->scblocks)) ) + { + /* Clicked no object. Deselect old object and set up for + * (maybe) creating a new one. */ + e->selection = NULL; + e->start_corner_x = event->x - e->border_offs_x; + e->start_corner_y = event->y - e->border_offs_y; + e->drag_status = DRAG_STATUS_COULD_DRAG; + e->drag_reason = DRAG_REASON_CREATE; + + } else { + + /* Select new frame, no immediate dragging */ + e->drag_status = DRAG_STATUS_NONE; + e->drag_reason = DRAG_REASON_NONE; + e->selection = clicked; + + } + + gtk_widget_grab_focus(GTK_WIDGET(da)); + redraw_editor(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; + + 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; + redraw_editor(e); + break; + + case DRAG_REASON_IMPORT : + /* Do nothing, handled by dnd_motion() */ + break; + + case DRAG_REASON_RESIZE : + calculate_box_size(fr, e, x, y); + redraw_editor(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; + redraw_editor(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; + + parent = sc_block_frame(e->scblocks); + + if ( w < 0.0 ) { + x += w; + w = -w; + } + + if ( h < 0.0 ) { + y += h; + h = -h; + } + + fr = add_subframe(parent); + + /* Add to SC */ + fr->scblocks = sc_block_append_end(e->scblocks, + "f", NULL, NULL); + sc_block_set_frame(fr->scblocks, fr); + sc_block_append_inside(fr->scblocks, NULL, NULL, strdup("")); + + fr->x = x; + fr->y = y; + fr->w = w; + fr->h = h; + fr->is_image = 0; + fr->empty = 1; + + update_geom(fr); + + return fr; +} + + +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); + + rerender(e); + redraw_editor(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; + + /* 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); + rerender(e); + e->selection = fr; + 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; + + } + + e->drag_reason = DRAG_REASON_NONE; + + gtk_widget_grab_focus(GTK_WIDGET(da)); + redraw_editor(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 : + e->selection = NULL; + redraw_editor(e); + claim = 1; + break; + + case GDK_KEY_Left : + if ( e->selection != NULL ) { + move_cursor(e, -1, 0); + redraw_editor(e); + } + claim = 1; + break; + + case GDK_KEY_Right : + if ( e->selection != NULL ) { + move_cursor(e, +1, 0); + redraw_editor(e); + } + claim = 1; + break; + + case GDK_KEY_Up : + if ( e->selection != NULL ) { + move_cursor(e, 0, -1); + redraw_editor(e); + } + claim = 1; + break; + + case GDK_KEY_Down : + if ( e->selection != NULL ) { + move_cursor(e, 0, +1); + redraw_editor(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; + + } + + 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; + + redraw_editor(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; +} + + +static void chomp(char *s) +{ + size_t i; + + if ( !s ) return; + + for ( i=0; i<strlen(s); i++ ) { + if ( (s[i] == '\n') || (s[i] == '\r') ) { + s[i] = '\0'; + return; + } + } +} + + +/* 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->w; + 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_append_inside(fr->scblocks, "image", opts, ""); + rerender(e); + e->selection = fr; + redraw_editor(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)); + rerender(e); + + return FALSE; +} + + +static void unset_all_frames(SCBlock *bl) +{ + while ( bl != NULL ) { + sc_block_set_frame(bl, NULL); + if ( sc_block_child(bl) != NULL ) { + unset_all_frames(sc_block_child(bl)); + } + if ( sc_block_macro_child(bl) != NULL ) { + unset_all_frames(sc_block_macro_child(bl)); + } + bl = sc_block_next(bl); + } +} + + +static void free_frame_contents(struct frame *fr) +{ + int i; + + if ( fr == NULL ) return; + + for ( i=0; i<fr->n_lines; i++ ) { + wrap_line_free(&fr->lines[i]); + } + free(fr->lines); + fr->lines = NULL; + fr->n_lines = 0; + fr->max_lines = 0; + + if ( fr->boxes != NULL ) { + free(fr->boxes->boxes); + free(fr->boxes); + } + + fr->boxes = NULL; + + for ( i=0; i<fr->num_children; i++ ) { + free_frame_contents(fr->children[i]); + } + + fr->num_children = 0; +} + + +void sc_editor_set_scblock(SCEditor *e, SCBlock *scblocks) +{ + unset_all_frames(e->scblocks); + e->scblocks = scblocks; + + /* Free all subframes */ + free_frame_contents(sc_block_frame(e->scblocks)); + + rerender(e); + redraw_editor(e); +} + + +void sc_editor_set_size(SCEditor *e, int w, int h) +{ + e->w = w; + e->h = h; + gtk_widget_set_size_request(GTK_WIDGET(e), w, h); +} + + +void sc_editor_set_logical_size(SCEditor *e, double w, double h) +{ + e->log_w = w; + e->log_h = h; +} + + +void sc_editor_set_slidenum(SCEditor *e, int slidenum) +{ + e->slidenum = slidenum; +} + + +SCEditor *sc_editor_new(SCBlock *scblocks, SCBlock *stylesheet) +{ + SCEditor *sceditor; + GtkTargetEntry targets[1]; + + sceditor = g_object_new(SC_TYPE_EDITOR, NULL); + + sceditor->scblocks = scblocks; + sceditor->surface = NULL; + sceditor->w = 100; + sceditor->h = 100; + sceditor->log_w = 100; + sceditor->log_h = 100; + sceditor->is = imagestore_new(); + sceditor->stylesheet = stylesheet; + sceditor->slidenum = 0; + + rerender(sceditor); + + 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); + + /* 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); + + 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 new file mode 100644 index 0000000..8bfcd59 --- /dev/null +++ b/src/sc_editor.h @@ -0,0 +1,157 @@ +/* + * sc_editor.h + * + * Copyright © 2014 Thomas White <taw@bitwiz.org.uk> + * + * This file is part of Colloquium. + * + * Colloquium is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + +#ifndef SC_EDITOR_H +#define SC_EDITOR_H + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <gtk/gtk.h> +#include <glib-object.h> + +#include "frame.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 +}; + + +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; + + /*< 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; + cairo_surface_t *surface; + SCBlock *stylesheet; + ImageStore *is; + + /* Pointers to the frame currently being edited */ + struct frame *selection; + + PangoContext *pc; + + /* Location of the cursor */ + struct frame *cursor_frame; + int cursor_line; + int cursor_box; + int cursor_pos; /* characters into box */ + + /* Border surrounding actual slide within drawingarea */ + double border_offs_x; + double border_offs_y; + double bgcol[3]; + + /* 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; + + /* 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 GtkWidget *sc_editor_get_widget(SCEditor *e); +extern SCEditor *sc_editor_new(SCBlock *scblocks, SCBlock *stylesheet); +extern void sc_editor_set_size(SCEditor *e, int w, int h); +extern void sc_editor_set_logical_size(SCEditor *e, double w, double h); +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); + +#endif /* SC_EDITOR_H */ diff --git a/src/sc_interp.c b/src/sc_interp.c index 646a132..703b73f 100644 --- a/src/sc_interp.c +++ b/src/sc_interp.c @@ -390,6 +390,7 @@ void sc_interp_destroy(SCInterpreter *scin) pango_font_description_free(scin->state[0].fontdesc); + free(scin->state); free(scin); } @@ -788,10 +789,6 @@ int sc_interp_add_blocks(SCInterpreter *scin, SCBlock *bl) set_padding(sc_interp_get_frame(scin), options); maybe_recurse_after(scin, child); - } else if ( strcmp(name, "slide") == 0 ) { - maybe_recurse_before(scin, child); - maybe_recurse_after(scin, child); - } else if ( strcmp(name, "bgcol") == 0 ) { maybe_recurse_before(scin, child); set_frame_bgcolour(sc_interp_get_frame(scin), options); diff --git a/src/slide_sorter.c b/src/slide_sorter.c index c64e408..53bee9d 100644 --- a/src/slide_sorter.c +++ b/src/slide_sorter.c @@ -32,7 +32,7 @@ #include "presentation.h" #include "render.h" -#include "mainwindow.h" +#include "slide_window.h" #include "slideshow.h" @@ -99,7 +99,7 @@ static gboolean draw_sig(GtkWidget *da, cairo_t *cr, struct slide_sorter *n) int x = i % n->width; int y = i / n->width; - struct slide *s = n->p->slides[i]; + //struct slide *s = n->p->slides[i]; cairo_save(cr); @@ -120,17 +120,9 @@ static gboolean draw_sig(GtkWidget *da, cairo_t *cr, struct slide_sorter *n) cairo_rectangle(cr, 0.0, 0.0, n->tw, n->th); cairo_set_source_rgb(cr, 1.0, 1.0, 1.0); cairo_fill_preserve(cr); - if ( (s != NULL) && (s->rendered_thumb != NULL) ) { - cairo_set_source_surface(cr, s->rendered_thumb, - 0.0, 0.0); - } else { - printf("Slide %i: %p", i, s); - if ( s != NULL ) { - printf(" %p\n", s->rendered_thumb); - } else { - printf("\n"); - } - } + /* FIXME */ + //cairo_set_source_surface(cr, s->rendered_thumb, + // 0.0, 0.0); cairo_fill(cr); cairo_rectangle(cr, 0.5, 0.5, n->tw, n->th); @@ -210,18 +202,19 @@ static gboolean motion_sig(GtkWidget *da, GdkEventMotion *event, /* If we are dragging the current editor or projector slide, * we'd better remember to update when we're finished. */ +#if 0 /* FIXME! */ if ( n->p->cur_edit_slide == n->selected_slide ) { n->dragging_cur_edit_slide = 1; } else { n->dragging_cur_edit_slide = 0; } - if ( n->p->cur_proj_slide == n->selected_slide ) { + if ( slideshow_slide(n->p->slideshow) == n->selected_slide ) { n->dragging_cur_proj_slide = 1; } else { n->dragging_cur_proj_slide = 0; } - +#endif list = gtk_target_list_new(targets, 1); gtk_drag_begin(da, list, GDK_ACTION_COPY | GDK_ACTION_MOVE, 1, (GdkEvent *)event); @@ -329,15 +322,7 @@ static gboolean dnd_drop(GtkWidget *widget, GdkDragContext *drag_context, * gets there first. When re-arranging slides, this might not happen */ static void fixup_proj(struct presentation *p, struct slide *s) { - int n; - - if ( s->rendered_proj != NULL ) return; - - n = slide_number(p, s); - - s->rendered_proj = render_slide(s, s->parent->proj_slide_width, - p->slide_width, p->slide_height, - p->is, ISZ_SLIDESHOW, n); +// slideshow_rerender(p->slideshow); FIXME } @@ -356,10 +341,10 @@ static void dnd_receive(GtkWidget *widget, GdkDragContext *drag_context, } else { - const char *sc; + //const char *sc; struct slide *s = NULL; - sc = (const char *)gtk_selection_data_get_data(seldata); + //sc = (const char *)gtk_selection_data_get_data(seldata); n->dragging = 0; gtk_drag_finish(drag_context, TRUE, TRUE, time); @@ -371,28 +356,21 @@ static void dnd_receive(GtkWidget *widget, GdkDragContext *drag_context, if ( s != NULL ) { /* FIXME: Do something */ - int sn = slide_number(n->p, s); + //int sn = slide_number(n->p, s); - s->rendered_thumb = render_slide(s, - n->p->thumb_slide_width, - n->p->slide_width, - n->p->slide_height, - n->p->is, - ISZ_THUMBNAIL, sn); + //s->rendered_thumb = render_slide(s, n->tw, + // n->p->slide_width, + // n->p->slide_height, + // n->p->is, + // ISZ_THUMBNAIL, sn); /* FIXME: Transfer the notes as well */ - if ( n->dragging_cur_edit_slide ) { - change_edit_slide(n->p, s); - } else { - /* Slide order has changed, so slide change - * buttons might need to be greyed out */ - update_toolbar(n->p); - } + change_edit_slide(n->p->slidewindow, s); if ( n->dragging_cur_proj_slide ) { fixup_proj(n->p, s); - change_proj_slide(n->p, s); + // FIXME change_proj_slide(n->p->slideshow, s); } redraw_slidesorter(n); @@ -422,6 +400,7 @@ static void dnd_get(GtkWidget *widget, GdkDragContext *drag_context, char *sc; /* FIXME: packed sc */ //sc = packed_sc(n->p->slides[n->selection]->top, n->p->ss); + sc = NULL; gtk_selection_data_set(seldata, target, 8, (guchar *)sc, strlen(sc)); @@ -460,6 +439,7 @@ static void dnd_delete(GtkWidget *widget, GdkDragContext *drag_context, if ( sn < n->drop_here ) n->drop_here--; +#if 0 /* FIXME ! */ if ( n->p->cur_edit_slide == n->selected_slide ) { if ( same ) { @@ -477,14 +457,13 @@ static void dnd_delete(GtkWidget *widget, GdkDragContext *drag_context, ct = sn - 1; } - change_edit_slide(n->p, n->p->slides[ct]); - update_toolbar(n->p); + change_edit_slide(n->p->slidewindow, n->p->slides[ct]); } } - if ( n->p->cur_proj_slide == n->selected_slide ) { + if ( slideshow_slide(n->p->slideshow) == n->selected_slide ) { if ( same ) { @@ -501,12 +480,12 @@ static void dnd_delete(GtkWidget *widget, GdkDragContext *drag_context, ct = sn - 1; } - change_proj_slide(n->p, n->p->slides[ct]); + change_proj_slide(n->p->slideshow, n->p->slides[ct]); } } - +#endif delete_slide(n->p, n->selected_slide); } @@ -527,8 +506,8 @@ void open_slidesorter(struct presentation *p) n->width = 6; n->bw = 5; n->selection = 0; - n->tw = p->thumb_slide_width; - n->th = (p->slide_height/p->slide_width) * p->thumb_slide_width; + n->tw = 320; + n->th = (p->slide_height/p->slide_width) * n->tw; n->drag_preview_pending = 0; n->have_drag_data = 0; n->dragging = 0; diff --git a/src/slide_window.c b/src/slide_window.c new file mode 100644 index 0000000..f262875 --- /dev/null +++ b/src/slide_window.c @@ -0,0 +1,621 @@ +/* + * slide_window.c + * + * Copyright © 2013-2014 Thomas White <taw@bitwiz.org.uk> + * + * This file is part of Colloquium. + * + * Colloquium is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <stdlib.h> +#include <string.h> +#include <gtk/gtk.h> +#include <assert.h> +#include <gdk/gdkkeysyms.h> +#include <gdk-pixbuf/gdk-pixbuf.h> +#include <math.h> + +#include "presentation.h" +#include "slide_window.h" +#include "render.h" +#include "frame.h" +#include "slideshow.h" +#include "wrap.h" +#include "notes.h" +#include "pr_clock.h" +#include "slide_sorter.h" +#include "sc_parse.h" +#include "sc_interp.h" +#include "sc_editor.h" + + +struct _slidewindow +{ + struct presentation *p; + GtkWidget *window; + GtkToolItem *bfirst; + GtkToolItem *bprev; + GtkToolItem *bnext; + GtkToolItem *blast; + + SCEditor *sceditor; + + struct menu_pl *style_menu; + int n_style_menu; + + struct slide *cur_slide; /* FIXME: SPOT inside SCEditor */ + + SlideShow *show; + struct notes *notes; +}; + + + +void update_toolbar(SlideWindow *sw) +{ + int cur_slide_number; + + cur_slide_number = slide_number(sw->p, sw->cur_slide); + if ( cur_slide_number == 0 ) { + gtk_widget_set_sensitive(GTK_WIDGET(sw->bfirst), FALSE); + gtk_widget_set_sensitive(GTK_WIDGET(sw->bprev), FALSE); + } else { + gtk_widget_set_sensitive(GTK_WIDGET(sw->bfirst), TRUE); + gtk_widget_set_sensitive(GTK_WIDGET(sw->bprev), TRUE); + } + + if ( cur_slide_number == sw->p->num_slides-1 ) { + gtk_widget_set_sensitive(GTK_WIDGET(sw->bnext), FALSE); + gtk_widget_set_sensitive(GTK_WIDGET(sw->blast), FALSE); + } else { + gtk_widget_set_sensitive(GTK_WIDGET(sw->bnext), TRUE); + gtk_widget_set_sensitive(GTK_WIDGET(sw->blast), TRUE); + } + +} + + +/* Inelegance to make furniture selection menus work */ +struct menu_pl +{ + SlideWindow *sw; + char *style_name; + GtkWidget *widget; +}; + + +static gint add_furniture(GtkWidget *widget, struct menu_pl *pl) +{ + sc_block_append_end(pl->sw->cur_slide->scblocks, + strdup(pl->style_name), NULL, NULL); + + //do_slide_update(pl->p, pl->sw->pc); FIXME + + return 0; +} + + +static void update_style_menus(SlideWindow *sw) +{ + GtkWidget *menu; + SCInterpreter *scin; + struct style_id *styles; + int i, n_sty; + + /* Free old list */ + for ( i=0; i<sw->n_style_menu; i++ ) { + gtk_widget_destroy(sw->style_menu[i].widget); + free(sw->style_menu[i].style_name); + } + free(sw->style_menu); + + /* Get the list of styles from the style sheet */ + scin = sc_interp_new(NULL, NULL); + if ( scin == NULL ) { + fprintf(stderr, "Failed to set up interpreter.\n"); + return; + } + sc_interp_run_stylesheet(scin, sw->p->stylesheet); + + styles = list_styles(scin, &n_sty); + if ( styles == NULL ) return; + + sc_interp_destroy(scin); + + /* Set up list for next time */ + sw->style_menu = calloc(n_sty, sizeof(struct menu_pl)); + if ( sw->style_menu == NULL ) return; + +#if 0 // FIXME + /* Add the styles to the "Insert" menu */ + menu = gtk_ui_manager_get_widget(sw->ui, "/displaywindow/insert"); + menu = gtk_menu_item_get_submenu(GTK_MENU_ITEM(menu)); + + for ( i=0; i<n_sty; i++ ) { + + GtkWidget *item; + + item = gtk_menu_item_new_with_label(styles[i].friendlyname); + gtk_menu_shell_append(GTK_MENU_SHELL(menu), item); + + sw->style_menu[i].sw = sw; + sw->style_menu[i].widget = item; + sw->style_menu[i].style_name = styles[i].name; + + g_signal_connect(G_OBJECT(item), "activate", + G_CALLBACK(add_furniture), + &sw->style_menu[i]); + + free(styles[i].friendlyname); + } + + gtk_widget_show_all(menu); + free(styles); +#endif +} + + +static gint saveas_response_sig(GtkWidget *d, gint response, + SlideWindow *sw) +{ + if ( response == GTK_RESPONSE_ACCEPT ) { + + char *filename; + + filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(d)); + + if ( save_presentation(sw->p, filename) ) { + //show_error(sw, "Failed to save presentation"); + } + + g_free(filename); + + } + + gtk_widget_destroy(d); + + return 0; +} + + +static void saveas_sig(GSimpleAction *action, GVariant *parameter, gpointer vp) +{ + GtkWidget *d; + SlideWindow *sw = vp; + + d = gtk_file_chooser_dialog_new("Save Presentation", + GTK_WINDOW(sw->window), + GTK_FILE_CHOOSER_ACTION_SAVE, + GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, + GTK_STOCK_SAVE, 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(saveas_response_sig), sw); + + gtk_widget_show_all(d); +} + + +static void save_sig(GSimpleAction *action, GVariant *parameter, gpointer vp) +{ + SlideWindow *sw = vp; + + if ( sw->p->filename == NULL ) { + return saveas_sig(NULL, NULL, sw); + } + + save_presentation(sw->p, sw->p->filename); +} + + +static void open_slidesorter_sig(GSimpleAction *action, GVariant *parameter, gpointer vp) +{ + SlideWindow *sw = vp; + open_slidesorter(sw->p); +} + + +static void delete_frame_sig(GSimpleAction *action, GVariant *parameter, gpointer vp) +{ + SlideWindow *sw = vp; +#if 0 + int i; + + delete_subframe(sw->cur_slide, sw->p->selection); + p->n_selection = 0; + + rerender_slide(p); + redraw_editor(p); +#endif +/* FIXME: implement */ +} + + +static void add_slide_sig(GSimpleAction *action, GVariant *parameter, gpointer vp) +{ + SlideWindow *sw = vp; + struct slide *new; + int cur_slide_number; + + cur_slide_number = slide_number(sw->p, sw->cur_slide); + + new = add_slide(sw->p, cur_slide_number+1); + new->scblocks = sc_block_insert_after(sw->cur_slide->scblocks, + "slide", NULL, NULL); + + change_edit_slide(sw, new); + +} + + +static void start_slideshow_sig(GSimpleAction *action, GVariant *parameter, gpointer vp) +{ + SlideWindow *sw = vp; + sw->show = try_start_slideshow(sw, sw->p); +} + + +static gint export_pdf_response_sig(GtkWidget *d, gint response, + SlideWindow *sw) +{ + if ( response == GTK_RESPONSE_ACCEPT ) { + + char *filename; + + filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(d)); + + if ( export_pdf(sw->p, filename) ) { + //show_error(sw, "Failed to export as PDF"); + } + + g_free(filename); + + } + + gtk_widget_destroy(d); + + return 0; +} + + +static void exportpdf_sig(GSimpleAction *action, GVariant *parameter, + gpointer vp) +{ + SlideWindow *sw = vp; + GtkWidget *d; + + d = gtk_file_chooser_dialog_new("Export PDF", + GTK_WINDOW(sw->window), + GTK_FILE_CHOOSER_ACTION_SAVE, + GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, + GTK_STOCK_SAVE, 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), sw); + + gtk_widget_show_all(d); +} + + +void slidewindow_slideshow_ended(SlideWindow *sw) +{ + sw->show = NULL; +} + + +void slidewindow_notes_closed(SlideWindow *sw) +{ + sw->notes = NULL; +} + + +/* Change the editor's slide to "np" */ +void change_edit_slide(SlideWindow *sw, struct slide *np) +{ + sw->cur_slide = np; + + update_toolbar(sw); + + sc_editor_set_slidenum(sw->sceditor, slide_number(sw->p, np)); + sc_editor_set_scblock(sw->sceditor, np->scblocks); + + if ( sw->notes != NULL ) notes_set_slide(sw->notes, np); + + if ( slideshow_linked(sw->show) ) { + change_proj_slide(sw->show, np); + } /* else leave the slideshow alone */ +} + + +void change_slide_first(SlideWindow *sw) +{ + change_edit_slide(sw, sw->p->slides[0]); +} + + +void change_slide_backwards(SlideWindow *sw) +{ + int cur_slide_number; + + cur_slide_number = slide_number(sw->p, sw->cur_slide); + if ( cur_slide_number == 0 ) return; + + change_edit_slide(sw, sw->p->slides[cur_slide_number-1]); +} + + +void change_slide_forwards(SlideWindow *sw) +{ + int cur_slide_number; + + cur_slide_number = slide_number(sw->p, sw->cur_slide); + if ( cur_slide_number == sw->p->num_slides-1 ) return; + + change_edit_slide(sw, sw->p->slides[cur_slide_number+1]); +} + + +void change_slide_last(SlideWindow *sw) +{ + change_edit_slide(sw, sw->p->slides[sw->p->num_slides-1]); +} + + +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 void open_notes_sig(GSimpleAction *action, GVariant *parameter, + gpointer vp) +{ + SlideWindow *sw = vp; + sw->notes = open_notes(sw, sw->cur_slide); +} + + +static void open_clock_sig(GSimpleAction *action, GVariant *parameter, + gpointer vp) +{ + SlideWindow *sw = vp; + open_clock(sw->p); +} + + +static void slidewindow_set_background(SlideWindow *sw) +{ + if ( (sw->show != NULL) && !slideshow_linked(sw->show) ) { + sc_editor_set_background(sw->sceditor, 1.0, 0.3, 0.2); + } else { + sc_editor_set_background(sw->sceditor, 0.9, 0.9, 0.9); + } +} + + +void slidewindow_redraw(SlideWindow *sw) +{ + slidewindow_set_background(sw); + sc_editor_redraw(sw->sceditor); +} + + +struct slide *slidewindow_get_slide(SlideWindow *sw) +{ + return sw->cur_slide; +} + + +void update_titlebar(struct presentation *p) +{ + get_titlebar_string(p); + + if ( p->slidewindow != NULL ) { + + char *title; + + title = malloc(strlen(p->titlebar)+14); + sprintf(title, "%s - Colloquium", p->titlebar); + gtk_window_set_title(GTK_WINDOW(p->slidewindow->window), title); + free(title); + + } + +} + + +static gboolean 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; + + case GDK_KEY_B : + case GDK_KEY_b : + if ( sw->show != NULL ) { + toggle_slideshow_link(sw->show); + } + } + + return FALSE; +} + + +GActionEntry sw_entries[] = { + + { "save", save_sig, NULL, NULL, NULL }, + { "saveas", saveas_sig, NULL, NULL, NULL }, + { "exportpdf", exportpdf_sig, NULL, NULL, NULL }, + { "sorter", open_slidesorter_sig, NULL, NULL, NULL }, + { "deleteframe", delete_frame_sig, NULL, NULL, NULL }, + { "slide", add_slide_sig, NULL, NULL, NULL }, + { "startslideshow", start_slideshow_sig, NULL, NULL, NULL }, + { "notes", open_notes_sig, NULL, NULL, NULL }, + { "clock", open_clock_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 }, +}; + + +SlideWindow *slide_window_open(struct presentation *p, GApplication *app) +{ + GtkWidget *window; + GtkWidget *vbox; + GtkWidget *scroll; + GtkWidget *toolbar; + GtkToolItem *button; + SlideWindow *sw; + + if ( p->slidewindow != NULL ) { + fprintf(stderr, "Slide window is already open!\n"); + return p->slidewindow; + } + + sw = calloc(1, sizeof(SlideWindow)); + if ( sw == NULL ) return NULL; + + window = gtk_application_window_new(GTK_APPLICATION(app)); + g_action_map_add_action_entries(G_ACTION_MAP(window), sw_entries, + G_N_ELEMENTS(sw_entries), sw); + sw->window = window; + sw->p = p; + sw->cur_slide = p->slides[0]; + sw->show = NULL; + + update_titlebar(p); + +// g_signal_connect(G_OBJECT(window), "destroy", G_CALLBACK(close_sig), p); + + vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0); + gtk_container_add(GTK_CONTAINER(window), vbox); + + 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 */ + button = gtk_tool_button_new_from_stock(GTK_STOCK_FULLSCREEN); + 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 */ + button = gtk_tool_button_new_from_stock(GTK_STOCK_ADD); + 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)); + + /* Change slide */ + sw->bfirst = gtk_tool_button_new_from_stock(GTK_STOCK_GOTO_FIRST); + gtk_container_add(GTK_CONTAINER(toolbar), GTK_WIDGET(sw->bfirst)); + gtk_actionable_set_action_name(GTK_ACTIONABLE(sw->bfirst), + "win.first"); + sw->bprev = gtk_tool_button_new_from_stock(GTK_STOCK_GO_BACK); + gtk_container_add(GTK_CONTAINER(toolbar), GTK_WIDGET(sw->bprev)); + gtk_actionable_set_action_name(GTK_ACTIONABLE(sw->bprev), + "win.prev"); + sw->bnext = gtk_tool_button_new_from_stock(GTK_STOCK_GO_FORWARD); + gtk_container_add(GTK_CONTAINER(toolbar), GTK_WIDGET(sw->bnext)); + gtk_actionable_set_action_name(GTK_ACTIONABLE(sw->bnext), + "win.next"); + sw->blast = gtk_tool_button_new_from_stock(GTK_STOCK_GOTO_LAST); + gtk_container_add(GTK_CONTAINER(toolbar), GTK_WIDGET(sw->blast)); + gtk_actionable_set_action_name(GTK_ACTIONABLE(sw->blast), + "win.last"); + update_toolbar(sw); + + sw->sceditor = sc_editor_new(sw->cur_slide->scblocks, p->stylesheet); + scroll = gtk_scrolled_window_new(NULL, NULL); + gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), + GTK_POLICY_AUTOMATIC, + GTK_POLICY_AUTOMATIC); + gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scroll), + GTK_WIDGET(sw->sceditor)); + gtk_window_set_focus(GTK_WINDOW(window), GTK_WIDGET(sw->sceditor)); + + /* Size of SCEditor surface in pixels */ + g_signal_connect(G_OBJECT(sw->sceditor), "key-press-event", + G_CALLBACK(key_press_sig), sw); + + /* FIXME: Somewhat arbitrary. Should come from slide itself */ + sc_editor_set_size(sw->sceditor, 1024, 768); + sc_editor_set_logical_size(sw->sceditor, 1024.0, 768.0); + + gtk_box_pack_start(GTK_BOX(vbox), scroll, TRUE, TRUE, 0); + + /* Default size */ + gtk_window_set_default_size(GTK_WINDOW(sw->window), 1024+100, 768+150); + gtk_window_set_resizable(GTK_WINDOW(sw->window), TRUE); + + /* Initial background colour */ + slidewindow_set_background(sw); + + gtk_widget_show_all(window); + + return sw; +} diff --git a/src/slide_window.h b/src/slide_window.h new file mode 100644 index 0000000..72a2f24 --- /dev/null +++ b/src/slide_window.h @@ -0,0 +1,46 @@ +/* + * slide_window.h + * + * Copyright © 2013-2014 Thomas White <taw@bitwiz.org.uk> + * + * This file is part of Colloquium. + * + * Colloquium is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + +#ifndef SLIDEWINDOW_H +#define SLIDEWINDOW_H + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +typedef struct _slidewindow SlideWindow; + +extern SlideWindow *slide_window_open(struct presentation *p, GApplication *app); +extern void change_edit_slide(SlideWindow *sw, struct slide *np); +extern void update_titlebar(struct presentation *p); + +extern void change_slide_first(SlideWindow *sw); +extern void change_slide_backwards(SlideWindow *sw); +extern void change_slide_forwards(SlideWindow *sw); +extern void change_slide_last(SlideWindow *sw); + +extern struct slide *slidewindow_get_slide(SlideWindow *sw); +extern void slidewindow_redraw(SlideWindow *sw); +extern void slidewindow_slideshow_ended(SlideWindow *sw); +extern void slidewindow_notes_closed(SlideWindow *sw); + +#endif /* SLIDEWINDOW_H */ diff --git a/src/slideshow.c b/src/slideshow.c index 66e4994..4755ed6 100644 --- a/src/slideshow.c +++ b/src/slideshow.c @@ -1,7 +1,7 @@ /* * slideshow.c * - * Copyright © 2013 Thomas White <taw@bitwiz.org.uk> + * Copyright © 2013-2014 Thomas White <taw@bitwiz.org.uk> * * This file is part of Colloquium. * @@ -32,33 +32,67 @@ #include <gdk/gdkkeysyms.h> #include "presentation.h" -#include "mainwindow.h" +#include "slide_window.h" #include "render.h" #include "pr_clock.h" #include "inhibit_screensaver.h" +#include "frame.h" + +struct _slideshow +{ + struct presentation *p; + SlideWindow *slide_window; /* Slide window controlling us */ + struct slide *cur_slide; + GtkWidget *window; + GtkWidget *drawingarea; + GdkCursor *blank_cursor; + int blank; + char geom[256]; + int slide_width; + int slide_height; + struct inhibit_sys *inhibit; + int linked; + cairo_surface_t *surface; + struct frame top; +}; /* Force a redraw of the slideshow */ -void redraw_slideshow(struct presentation *p) +void redraw_slideshow(SlideShow *ss) { gint w, h; - w = gtk_widget_get_allocated_width(GTK_WIDGET(p->ss_drawingarea)); - h = gtk_widget_get_allocated_height(GTK_WIDGET(p->ss_drawingarea)); + 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(p->ss_drawingarea, 0, 0, w, h); + gtk_widget_queue_draw_area(ss->drawingarea, 0, 0, w, h); } -static gint ss_destroy_sig(GtkWidget *widget, struct presentation *p) +void slideshow_rerender(SlideShow *ss) { - p->slideshow = NULL; - g_object_unref(p->blank_cursor); + int n; + + n = slide_number(ss->p, ss->cur_slide); + ss->surface = render_sc(ss->cur_slide->scblocks, + ss->slide_width, ss->slide_height, + ss->p->slide_width, ss->p->slide_height, + ss->p->stylesheet, + ss->p->is, ISZ_SLIDESHOW, n); +} + + +static gint ss_destroy_sig(GtkWidget *widget, SlideShow *ss) +{ + g_object_unref(ss->blank_cursor); + slidewindow_slideshow_ended(ss->slide_window); + slidewindow_redraw(ss->slide_window); + free(ss); return FALSE; } -static gboolean ss_draw_sig(GtkWidget *da, cairo_t *cr, struct presentation *p) +static gboolean ss_draw_sig(GtkWidget *da, cairo_t *cr, SlideShow *ss) { double xoff, yoff; double width, height; @@ -71,22 +105,21 @@ static gboolean ss_draw_sig(GtkWidget *da, cairo_t *cr, struct presentation *p) cairo_set_source_rgb(cr, 0.0, 0.0, 0.0); cairo_fill(cr); - if ( !p->ss_blank ) { + if ( !ss->blank ) { int h; /* FIXME: Assumes that monitor and slide sizes are such that * letterboxing at sides. This needn't be the case. */ - h = p->proj_slide_width * p->slide_height / p->slide_width; + h = ss->slide_width * ss->p->slide_height / ss->p->slide_width; /* Get the overall size */ - xoff = (width - p->proj_slide_width)/2.0; + xoff = (width - ss->slide_width)/2.0; yoff = (height - h)/2.0; /* Draw the slide from the cache */ - cairo_rectangle(cr, xoff, yoff, p->proj_slide_width, h); - cairo_set_source_surface(cr, p->cur_proj_slide->rendered_proj, - xoff, yoff); + cairo_rectangle(cr, xoff, yoff, ss->slide_width, h); + cairo_set_source_surface(cr, ss->surface, xoff, yoff); cairo_fill(cr); } @@ -95,101 +128,85 @@ static gboolean ss_draw_sig(GtkWidget *da, cairo_t *cr, struct presentation *p) } -void change_proj_slide(struct presentation *p, struct slide *np) +void change_proj_slide(SlideShow *ss, struct slide *np) { - /* If this slide is not being shown on the editor, we can free the - * buffers */ - if ( p->cur_proj_slide != p->cur_edit_slide ) { - free_render_buffers_except_thumb(p->cur_proj_slide); - } + ss->cur_slide = np; - p->cur_proj_slide = np; + notify_clock_slide_changed(ss->p, np); - notify_clock_slide_changed(p, np); - - /* The slide is already rendered, because the editor always gets there - * first, so we only need to do this: */ - redraw_slideshow(p); + slideshow_rerender(ss); + redraw_slideshow(ss); } -static gint prev_slide_sig(GtkWidget *widget, struct presentation *p) +static gint prev_slide_sig(GtkWidget *widget, SlideShow *ss) { - int cur_slide_number; - cur_slide_number = slide_number(p, p->cur_edit_slide); - if ( cur_slide_number == 0 ) return FALSE; - change_edit_slide(p, p->slides[cur_slide_number-1]); + change_slide_backwards(ss->slide_window); return FALSE; } -static gint next_slide_sig(GtkWidget *widget, struct presentation *p) +static gint next_slide_sig(GtkWidget *widget, SlideShow *ss) { - int cur_slide_number; - cur_slide_number = slide_number(p, p->cur_edit_slide); - if ( cur_slide_number == p->num_slides-1 ) return FALSE; - change_edit_slide(p, p->slides[cur_slide_number+1]); + change_slide_forwards(ss->slide_window); return FALSE; } -void end_slideshow(struct presentation *p) +void end_slideshow(SlideShow *ss) { - if ( p->inhibit != NULL ) do_inhibit(p->inhibit, 0); - gtk_widget_destroy(p->ss_drawingarea); - gtk_widget_destroy(p->slideshow); - p->slideshow = NULL; - p->cur_proj_slide = NULL; - redraw_editor(p); + if ( ss->inhibit != NULL ) do_inhibit(ss->inhibit, 0); + gtk_widget_destroy(ss->drawingarea); + gtk_widget_destroy(ss->window); } -void toggle_slideshow_link(struct presentation *p) +void toggle_slideshow_link(SlideShow *ss) { - p->slideshow_linked = 1 - p->slideshow_linked; - if ( p->slideshow_linked ) { - change_proj_slide(p, p->cur_edit_slide); + ss->linked = 1 - ss->linked; + if ( ss->linked ) { + change_proj_slide(ss, slidewindow_get_slide(ss->slide_window)); } - redraw_editor(p); + slidewindow_redraw(ss->slide_window); } -void check_toggle_blank(struct presentation *p) +int slideshow_linked(SlideShow *ss) { - if ( p->slideshow != NULL ) { - //if ( p->prefs->b_splits ) { - toggle_slideshow_link(p); - //} else { - // p->ss_blank = 1-p->ss_blank; - // gdk_window_invalidate_rect(p->ss_drawingarea->window, - // NULL, FALSE); - //} FIXME! - } + if ( ss == NULL ) return 0; + return ss->linked; +} + + +void check_toggle_blank(SlideShow *ss) +{ + toggle_slideshow_link(ss); } static gboolean ss_key_press_sig(GtkWidget *da, GdkEventKey *event, - struct presentation *p) + SlideShow *ss) { - switch ( event->keyval ) { + switch ( event->keyval ) + { - case GDK_KEY_B : - case GDK_KEY_b : - check_toggle_blank(p); + case GDK_KEY_B : + case GDK_KEY_b : + check_toggle_blank(ss); break; - case GDK_KEY_Page_Up : - case GDK_KEY_Up : - prev_slide_sig(NULL, p); + case GDK_KEY_Page_Up : + case GDK_KEY_Up : + prev_slide_sig(NULL, ss); break; - case GDK_KEY_Page_Down : - case GDK_KEY_Down : - next_slide_sig(NULL, p); + case GDK_KEY_Page_Down : + case GDK_KEY_Down : + next_slide_sig(NULL, ss); break; - case GDK_KEY_Escape : - end_slideshow(p); + case GDK_KEY_Escape : + end_slideshow(ss); break; } @@ -198,55 +215,89 @@ static gboolean ss_key_press_sig(GtkWidget *da, GdkEventKey *event, } -static gboolean ss_realize_sig(GtkWidget *w, struct presentation *p) +static gboolean ss_realize_sig(GtkWidget *w, SlideShow *ss) { GdkWindow *win; win = gtk_widget_get_window(w); - p->blank_cursor = gdk_cursor_new(GDK_BLANK_CURSOR); - gdk_window_set_cursor(GDK_WINDOW(win), p->blank_cursor); + ss->blank_cursor = gdk_cursor_new(GDK_BLANK_CURSOR); + gdk_window_set_cursor(GDK_WINDOW(win), ss->blank_cursor); - gtk_window_parse_geometry(GTK_WINDOW(w), p->ss_geom); + gtk_window_parse_geometry(GTK_WINDOW(w), ss->geom); + slideshow_rerender(ss); return FALSE; } -void try_start_slideshow(struct presentation *p) +struct slide *slideshow_slide(SlideShow *ss) +{ + if ( ss == NULL ) return NULL; + return ss->cur_slide; +} + + +SlideShow *try_start_slideshow(SlideWindow *sw, struct presentation *p) { - GtkWidget *n; GdkScreen *screen; int n_monitors; int i; - - /* Presentation already running? */ - if ( p->slideshow != NULL ) return; - - p->ss_blank = 0; - - if ( p->inhibit == NULL ) { - p->inhibit = inhibit_prepare(); + SlideShow *ss; + double slide_width = 1024.0; /* Logical slide size */ + double slide_height = 768.0; /* FIXME: Should come from slide */ + ss = calloc(1, sizeof(SlideShow)); + if ( ss == NULL ) return NULL; + + ss->slide_window = sw; + ss->blank = 0; + ss->p = p; + ss->cur_slide = slidewindow_get_slide(sw); + + if ( ss->inhibit == NULL ) { + ss->inhibit = inhibit_prepare(); } - n = gtk_window_new(GTK_WINDOW_TOPLEVEL); - - p->ss_drawingarea = gtk_drawing_area_new(); - gtk_container_add(GTK_CONTAINER(n), p->ss_drawingarea); - - gtk_widget_set_can_focus(GTK_WIDGET(p->ss_drawingarea), TRUE); - gtk_widget_add_events(GTK_WIDGET(p->ss_drawingarea), + ss->top.children = NULL; + ss->top.num_children = 0; + ss->top.max_children = 0; + ss->top.lines = NULL; + ss->top.n_lines = 0; + ss->top.max_lines = 0; + ss->top.pad_l = 0; + ss->top.pad_r = 0; + ss->top.pad_t = 0; + ss->top.pad_b = 0; + ss->top.x = 0.0; + ss->top.y = 0.0; + ss->top.w = slide_width; + ss->top.h = slide_height; + ss->top.grad = GRAD_NONE; + ss->top.bgcol[0] = 1.0; + ss->top.bgcol[1] = 1.0; + ss->top.bgcol[2] = 1.0; + ss->top.bgcol[3] = 1.0; + + ss->window = gtk_window_new(GTK_WINDOW_TOPLEVEL); + + ss->drawingarea = gtk_drawing_area_new(); + gtk_container_add(GTK_CONTAINER(ss->window), 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(p->ss_drawingarea), "key-press-event", - G_CALLBACK(ss_key_press_sig), p); - g_signal_connect(G_OBJECT(n), "destroy", G_CALLBACK(ss_destroy_sig), p); - g_signal_connect(G_OBJECT(n), "realize", G_CALLBACK(ss_realize_sig), p); + g_signal_connect(G_OBJECT(ss->drawingarea), "key-press-event", + G_CALLBACK(ss_key_press_sig), ss); + g_signal_connect(G_OBJECT(ss->window), "destroy", + G_CALLBACK(ss_destroy_sig), ss); + g_signal_connect(G_OBJECT(ss->window), "realize", + G_CALLBACK(ss_realize_sig), ss); - g_signal_connect(G_OBJECT(p->ss_drawingarea), "draw", - G_CALLBACK(ss_draw_sig), p); + g_signal_connect(G_OBJECT(ss->drawingarea), "draw", + G_CALLBACK(ss_draw_sig), ss); - gtk_widget_grab_focus(GTK_WIDGET(p->ss_drawingarea)); + gtk_widget_grab_focus(GTK_WIDGET(ss->drawingarea)); screen = gdk_screen_get_default(); n_monitors = gdk_screen_get_n_monitors(screen); @@ -256,27 +307,24 @@ void try_start_slideshow(struct presentation *p) int w; gdk_screen_get_monitor_geometry(screen, i, &rect); - snprintf(p->ss_geom, 255, "%ix%i+%i+%i", + snprintf(ss->geom, 255, "%ix%i+%i+%i", rect.width, rect.height, rect.x, rect.y); - w = rect.height * p->slide_width/p->slide_height; + w = rect.height * slide_width/slide_height; if ( w > rect.width ) w = rect.width; - p->proj_slide_width = w; + ss->slide_width = w; + ss->slide_height = rect.height; } /* FIXME: Sensible (configurable) choice of monitor */ - rerender_slide(p); + ss->linked = 1; + gtk_window_fullscreen(GTK_WINDOW(ss->window)); + gtk_widget_show_all(GTK_WIDGET(ss->window)); - p->slideshow = n; - p->slideshow_linked = 1; - gtk_window_fullscreen(GTK_WINDOW(n)); - gtk_widget_show_all(GTK_WIDGET(n)); + if ( ss->inhibit != NULL ) do_inhibit(ss->inhibit, 1); - if ( p->inhibit != NULL ) do_inhibit(p->inhibit, 1); + //notify_clock_slide_changed(p, ss->cur_slide); FIXME - //if ( p->prefs->open_notes ) open_notes(p); FIXME! - - p->cur_proj_slide = p->cur_edit_slide; - - notify_clock_slide_changed(p, p->cur_proj_slide); + return ss; } + diff --git a/src/slideshow.h b/src/slideshow.h index 66f3560..16bd6e0 100644 --- a/src/slideshow.h +++ b/src/slideshow.h @@ -1,7 +1,7 @@ /* * slideshow.h * - * Copyright © 2013 Thomas White <taw@bitwiz.org.uk> + * Copyright © 2013-2014 Thomas White <taw@bitwiz.org.uk> * * This file is part of Colloquium. * @@ -27,16 +27,22 @@ #include <config.h> #endif +#include "slide_window.h" -extern void try_start_slideshow(struct presentation *p); +/* Opaque data structure representing a slideshow */ +typedef struct _slideshow SlideShow; -extern void change_proj_slide(struct presentation *p, struct slide *np); +extern SlideShow *try_start_slideshow(SlideWindow *sw, struct presentation *p); +extern void end_slideshow(SlideShow *ss); -extern void toggle_slideshow_link(struct presentation *p); -extern void check_toggle_blank(struct presentation *p); +extern void change_proj_slide(SlideShow *ss, struct slide *np); +extern struct slide *slideshow_slide(SlideShow *ss); -extern void redraw_slideshow(struct presentation *p); +extern void toggle_slideshow_link(SlideShow *ss); +extern int slideshow_linked(SlideShow *ss); +extern void check_toggle_blank(SlideShow *ss); -extern void end_slideshow(struct presentation *p); +extern void redraw_slideshow(SlideShow *ss); +extern void slideshow_rerender(SlideShow *ss); #endif /* SLIDESHOW_H */ @@ -167,152 +167,6 @@ void get_cursor_pos(struct wrap_box *box, int pos, } -void move_cursor_back(struct presentation *p) -{ - int retreat = 0; - signed int cp, cb, cl; - struct wrap_line *line; - struct wrap_box *box; - - cp = p->cursor_pos; - cb = p->cursor_box; - cl = p->cursor_line; - - line = &p->cursor_frame->lines[p->cursor_line]; - box = &line->boxes[p->cursor_box]; - if ( box->type == WRAP_BOX_PANGO ) { - - if ( cp == 0 ) { - retreat = 1; - } else { - cp--; - } - - } else { - cp--; - if ( cp < 0 ) retreat = 1; - } - - if ( retreat ) { - - do { - - cb--; - - if ( cb < 0 ) { - cl--; - if ( cl < 0 ) return; - p->cursor_line = cl; - line = &p->cursor_frame->lines[cl]; - cb = line->n_boxes - 1; - } - - } while ( !line->boxes[cb].editable ); - - p->cursor_box = cb; - box = &line->boxes[cb]; - if ( box->type == WRAP_BOX_PANGO ) { - cp = box->len_chars; - if ( box->space == WRAP_SPACE_NONE ) { - cp--; - } - } else { - cp = 1; - } - - } - p->cursor_pos = cp; -} - - -void cur_box_diag(struct presentation *p) -{ - int sln, sbx, sps; - struct frame *fr; - - fr = p->cursor_frame; - sln = p->cursor_line; - sbx = p->cursor_box; - sps = p->cursor_pos; - - struct wrap_box *sbox = &p->cursor_frame->lines[sln].boxes[sbx]; - - printf("line/box/pos: [%i of %i]/[%i of %i]/[%i of %i]\n", - sln, fr->n_lines, - sbx, p->cursor_frame->lines[sln].n_boxes, - sps, sbox->len_chars); - printf("box type is %i, space type is %i\n", sbox->type, sbox->space); - if ( sbox->type == WRAP_BOX_NOTHING ) { - printf("Warning: in a nothing box!\n"); - } -} - - -void advance_cursor(struct presentation *p) -{ - int advance = 0; - signed int cp, cb, cl; - struct wrap_line *line = &p->cursor_frame->lines[p->cursor_line]; - struct wrap_box *box = &line->boxes[p->cursor_box]; - - cp = p->cursor_pos; - cb = p->cursor_box; - cl = p->cursor_line; - - switch ( box->type ) { - - case WRAP_BOX_PANGO: - if ( cp+1 > box->len_chars ) { - advance = 1; - } else { - cp++; - } - break; - - case WRAP_BOX_NOTHING: - case WRAP_BOX_SENTINEL: - advance = 1; - break; - - case WRAP_BOX_IMAGE: - cp++; - if ( cp > 1 ) advance = 1; - break; - - } - - if ( advance ) { - - do { - - cb++; - cp = 0; - - if ( box->space == WRAP_SPACE_NONE ) { - cp = 1; - } - - if ( cb >= line->n_boxes ) { - cl++; - if ( cl >= p->cursor_frame->n_lines ) { - /* Give up - could not move */ - return; - } - line = &p->cursor_frame->lines[cl]; - cb = 0; - cp = 0; - } - - } while ( !line->boxes[cb].editable ); - - p->cursor_line = cl; - p->cursor_box = cb; - - } - p->cursor_pos = cp; -} - - static int find_cursor_line(struct frame *fr, double yposd, int *end) { int i; @@ -105,10 +105,6 @@ extern int wrap_contents(struct frame *fr); extern void get_cursor_pos(struct wrap_box *box, int pos, double *xposd, double *yposd, double *line_height); -extern void move_cursor_back(struct presentation *p); -extern void cur_box_diag(struct presentation *p); -extern void advance_cursor(struct presentation *p); - extern void find_cursor(struct frame *fr, double xposd, double yposd, int *line, int *box, int *pos); diff --git a/tests/render_test.c b/tests/render_test.c index 0e7c3c9..62bab25 100644 --- a/tests/render_test.c +++ b/tests/render_test.c @@ -49,21 +49,18 @@ static gint mw_destroy(GtkWidget *w, void *p) static gboolean draw_sig(GtkWidget *da, cairo_t *cr, gpointer data) { gint w, h; - struct slide *s = data; + cairo_surface_t *surface; + SCBlock *scblocks = data; w = gtk_widget_get_allocated_width(da); h = gtk_widget_get_allocated_height(da); - /* Overall background */ + surface = render_sc(scblocks, w, h, w, h, NULL, NULL, + ISZ_EDITOR, 1); cairo_rectangle(cr, 0.0, 0.0, w, h); - cairo_set_source_rgb(cr, 0.9, 0.9, 0.9); - cairo_fill(cr); - - if ( s->rendered_edit != NULL ) cairo_surface_destroy(s->rendered_edit); - s->rendered_edit = render_slide(s, w, w, h, NULL, ISZ_EDITOR, 1); - cairo_rectangle(cr, 0.0, 0.0, w, h); - cairo_set_source_surface(cr, s->rendered_edit, 0.0, 0.0); + cairo_set_source_surface(cr, surface, 0.0, 0.0); cairo_fill(cr); + cairo_surface_destroy(surface); return FALSE; } @@ -73,35 +70,16 @@ int main(int argc, char *argv[]) { GtkWidget *window; GtkWidget *drawingarea; - struct frame *fr; - struct slide s; - struct presentation p; + SCBlock *scblocks; gtk_init(&argc, &argv); - fr = frame_new(); - if ( fr == NULL ) return 1; - fr->scblocks = sc_parse(sc); - if ( fr->scblocks == NULL ) { + scblocks = sc_parse(sc); + if ( scblocks == NULL ) { fprintf(stderr, "SC parse failed.\n"); return 1; } - fr->pad_l = 20.0; - fr->pad_r = 20.0; - fr->pad_t = 20.0; - fr->pad_b = 20.0; - - s.top = fr; - s.rendered_edit = NULL; - s.rendered_proj = NULL; - s.rendered_thumb = NULL; - s.parent = &p; - s.scblocks = fr->scblocks; - - p.stylesheet = NULL; - p.scblocks = fr->scblocks; - window = gtk_window_new(GTK_WINDOW_TOPLEVEL); drawingarea = gtk_drawing_area_new(); @@ -112,12 +90,10 @@ int main(int argc, char *argv[]) NULL); g_signal_connect(G_OBJECT(drawingarea), "draw", - G_CALLBACK(draw_sig), &s); + G_CALLBACK(draw_sig), scblocks); gtk_widget_show_all(window); gtk_main(); - free_render_buffers(&s); - return 0; } diff --git a/tests/render_test_sc1.c b/tests/render_test_sc1.c index d8172cd..cf297e9 100644 --- a/tests/render_test_sc1.c +++ b/tests/render_test_sc1.c @@ -44,24 +44,22 @@ static gint mw_destroy(GtkWidget *w, void *p) exit(0); } + static gboolean draw_sig(GtkWidget *da, cairo_t *cr, gpointer data) { gint w, h; - struct slide *s = data; + cairo_surface_t *surface; + SCBlock *scblocks = data; w = gtk_widget_get_allocated_width(da); h = gtk_widget_get_allocated_height(da); - /* Overall background */ - cairo_rectangle(cr, 0.0, 0.0, w, h); - cairo_set_source_rgb(cr, 0.9, 0.9, 0.9); - cairo_fill(cr); - - if ( s->rendered_edit != NULL ) cairo_surface_destroy(s->rendered_edit); - s->rendered_edit = render_slide(s, w, w, h, NULL, ISZ_EDITOR, 1); + surface = render_sc(scblocks, w, h, w, h, NULL, NULL, + ISZ_EDITOR, 1); cairo_rectangle(cr, 0.0, 0.0, w, h); - cairo_set_source_surface(cr, s->rendered_edit, 0.0, 0.0); + cairo_set_source_surface(cr, surface, 0.0, 0.0); cairo_fill(cr); + cairo_surface_destroy(surface); return FALSE; } @@ -71,35 +69,15 @@ int main(int argc, char *argv[]) { GtkWidget *window; GtkWidget *drawingarea; - struct frame *fr; - struct slide s; - struct presentation p; + SCBlock *scblocks; gtk_init(&argc, &argv); - fr = frame_new(); - if ( fr == NULL ) return 1; - fr->scblocks = sc_parse(sc); - if ( fr->scblocks == NULL ) { + scblocks = sc_parse(sc); + if ( scblocks == NULL ) { fprintf(stderr, "SC parse failed.\n"); return 1; } - - fr->pad_l = 20.0; - fr->pad_r = 20.0; - fr->pad_t = 20.0; - fr->pad_b = 20.0; - - s.top = fr; - s.rendered_edit = NULL; - s.rendered_proj = NULL; - s.rendered_thumb = NULL; - s.parent = &p; - s.scblocks = fr->scblocks; - - p.scblocks = fr->scblocks; - p.stylesheet = NULL; - window = gtk_window_new(GTK_WINDOW_TOPLEVEL); drawingarea = gtk_drawing_area_new(); @@ -110,7 +88,7 @@ int main(int argc, char *argv[]) NULL); g_signal_connect(G_OBJECT(drawingarea), "draw", - G_CALLBACK(draw_sig), &s); + G_CALLBACK(draw_sig), scblocks); gtk_widget_show_all(window); gtk_main(); |