diff options
-rw-r--r-- | Makefile.am | 8 | ||||
-rw-r--r-- | src/colloquium.c | 141 | ||||
-rw-r--r-- | src/mainwindow.c | 2008 | ||||
-rw-r--r-- | src/narrative_window.c | 62 | ||||
-rw-r--r-- | src/narrative_window.h | 37 | ||||
-rw-r--r-- | src/presentation.c | 111 | ||||
-rw-r--r-- | src/presentation.h | 116 | ||||
-rw-r--r-- | src/render.c | 25 | ||||
-rw-r--r-- | src/render.h | 2 | ||||
-rw-r--r-- | src/sc_editor.c | 1452 | ||||
-rw-r--r-- | src/sc_editor.h | 40 | ||||
-rw-r--r-- | src/slide_sorter.c | 27 | ||||
-rw-r--r-- | src/slide_window.c | 620 | ||||
-rw-r--r-- | src/slide_window.h (renamed from src/mainwindow.h) | 9 | ||||
-rw-r--r-- | src/slideshow.c | 216 | ||||
-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 | 2 |
19 files changed, 2492 insertions, 2554 deletions
diff --git a/Makefile.am b/Makefile.am index 7aa90c6..a215a59 100644 --- a/Makefile.am +++ b/Makefile.am @@ -11,19 +11,21 @@ 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 diff --git a/src/colloquium.c b/src/colloquium.c index 1f372d8..ce23be5 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. * @@ -29,12 +29,95 @@ #include <getopt.h> #include "presentation.h" -#include "mainwindow.h" +#include "narrative_window.h" + + +static void colloquium_activate(GApplication *app) +{ + printf("activate!\n"); +} + + +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]); + printf("open %s\n", uri); + p = new_presentation(); + load_presentation(p, uri); + narrative_window_new(p, app); + g_free(uri); + } +} + +typedef struct +{ + GtkApplication parent_instance; +} Colloquium; + +typedef GtkApplicationClass ColloquiumClass; + +G_DEFINE_TYPE(Colloquium, colloquium, GTK_TYPE_APPLICATION) + +static void colloquium_finalize(GObject *object) +{ + G_OBJECT_CLASS(colloquium_parent_class)->finalize(object); +} + + +static void colloquium_startup(GApplication *app) +{ + G_APPLICATION_CLASS(colloquium_parent_class)->startup(app); +} + + +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 +129,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 +138,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.c b/src/mainwindow.c deleted file mode 100644 index 4c6809d..0000000 --- a/src/mainwindow.c +++ /dev/null @@ -1,2008 +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 wrap_box *sbox; - - sbox = &p->cursor_frame->lines[p->cursor_line].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); - 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..7fd51a3 --- /dev/null +++ b/src/narrative_window.c @@ -0,0 +1,62 @@ +/* + * 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 <stdlib.h> + +#include "presentation.h" +#include "narrative_window.h" + + +struct _narrative_window +{ + GtkWidget *window; +}; + + +NarrativeWindow *narrative_window_new(struct presentation *p, GApplication *app) +{ + NarrativeWindow *nw; + + 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->window = gtk_application_window_new(GTK_APPLICATION(app)); + p->narrative_window = nw; + +// update_titlebar(nw); + + 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/presentation.c b/src/presentation.c index 56bbbd6..8cd39ce 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,23 +41,20 @@ #include "sc_interp.h" -static int num_presentations = 0; - - void free_presentation(struct presentation *p) { int i; int final = 0; + if ( p->slideshow != NULL ) { + end_slideshow(p->slideshow); + p->slideshow = NULL; + } + for ( i=0; i<p->num_slides; i++ ) { 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); @@ -198,14 +195,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 +217,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 +224,19 @@ 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->titlebar = get_titlebar_string(new); - new->window = NULL; - new->ui = NULL; - new->action_group = NULL; new->slideshow = NULL; 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; 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; @@ -361,7 +321,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; @@ -400,8 +359,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 */ } @@ -440,16 +397,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; } @@ -506,41 +453,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..f3b11b4 100644 --- a/src/presentation.h +++ b/src/presentation.h @@ -30,9 +30,14 @@ #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 { @@ -52,134 +57,46 @@ struct slide 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; + SlideShow *slideshow; + struct notes *notes; struct pr_clock *clock; struct slide_sorter *slide_sorter; - /* Pointers to the current "editing" and "projection" slides */ + /* Pointers to the current "editing" slide */ 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); @@ -188,7 +105,6 @@ extern void delete_slide(struct presentation *p, struct slide *s); extern void delete_subframe(struct slide *s, struct frame *fr); -extern void get_titlebar_string(struct presentation *p); extern char *packed_sc(struct frame *fr); @@ -198,8 +114,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..dbc40d9 100644 --- a/src/render.c +++ b/src/render.c @@ -343,31 +343,6 @@ 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, diff --git a/src/render.h b/src/render.h index 3099d35..aa77043 100644 --- a/src/render.h +++ b/src/render.h @@ -33,8 +33,6 @@ 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 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..3a398c5 --- /dev/null +++ b/src/sc_editor.c @@ -0,0 +1,1452 @@ +/* + * 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 "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" + + +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 +{ + GtkWidget *drawingarea; + GtkIMContext *im_context; + int slide_width; + struct presentation *p; + cairo_surface_t *surface; + + /* Pointers to the frame currently being edited */ + struct frame *selection; + + struct slide *cur_slide; + + 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; + + /* 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; + +}; + + +/* Update a slide, once it's been edited in some way. */ +static void rerender_slide(SCEditor *e) +{ + struct slide *s = e->cur_slide; + int n = slide_number(e->p, s); + + if ( e->surface != NULL ) { + cairo_surface_destroy(e->surface); + } + + e->surface = render_slide(s, e->slide_width, + e->p->slide_width, e->p->slide_height, + e->p->is, ISZ_EDITOR, n); +} + + +/* Force a redraw of the editor window */ +void redraw_editor(SCEditor *e) +{ + gint w, h; + + w = gtk_widget_get_allocated_width(GTK_WIDGET(e->drawingarea)); + h = gtk_widget_get_allocated_height(GTK_WIDGET(e->drawingarea)); + + gtk_widget_queue_draw_area(e->drawingarea, 0, 0, w, h); +} + + +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; + + 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); + + /* If only one frame is selected, draw the caret */ + if ( e->selection != NULL ) { + 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; + int edit_slide_height; + double ratio; + + 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 ( slideshow_linked(e->p->slideshow) ) { + 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 */ + ratio = e->p->slide_height/e->p->slide_width; + edit_slide_height = ratio*e->slide_width; + xoff = (width - e->slide_width)/2.0; + yoff = (height - edit_slide_height)/2.0; + e->border_offs_x = xoff; e->border_offs_y = yoff; + + /* Draw the slide from the cache */ + if ( e->cur_slide->rendered_edit != NULL ) { + cairo_set_source_surface(cr, e->cur_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, e); + + return FALSE; +} + + +static void fixup_cursor(SCEditor *e) +{ + struct wrap_box *sbox; + + sbox = &e->cursor_frame->lines[e->cursor_line].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_slide(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_slide(e); + redraw_editor(e); +} + + +static gboolean im_commit_sig(GtkIMContext *im, gchar *str, + SCEditor *e) +{ + if ( e->selection == NULL ) { + if ( str[0] == 'b' ) { + check_toggle_blank(e->p->slideshow); + } else { + printf("IM keypress: %s\n", str); + } + return FALSE; + } + + 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(e->cur_slide->top, 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 == e->cur_slide->top) ) { + + /* 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 = e->cur_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(e->cur_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(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_slide(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_slide(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_Page_Up : + //prev_slide_sig(NULL, p); + claim = 1; + break; + + case GDK_KEY_Page_Down : + //next_slide_sig(NULL, p); FIXME! + claim = 1; + break; + + case GDK_KEY_Escape : + if ( e->p->slideshow != NULL ) end_slideshow(e->p->slideshow); + 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; + + case GDK_KEY_B : + case GDK_KEY_b : + if ( e->p->slideshow != NULL ) { + //if ( p->prefs->b_splits ) { + toggle_slideshow_link(e->p->slideshow); + //} 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, 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->slide_width ) { + + int new_import_width; + + new_import_width = e->slide_width/2; + e->import_height = (new_import_width *e->import_height) + / e->import_width; + e->import_width = new_import_width; + } + + if ( e->import_height > e->p->slide_height ) { + + int new_import_height; + + new_import_height = e->p->slide_height/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, ""); + show_hierarchy(e->cur_slide->top, ""); + rerender_slide(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(e->drawingarea); + 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->drawingarea), "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(e->drawingarea); + rerender_slide(e); + + return FALSE; +} + + +GtkWidget *sc_editor_get_widget(SCEditor *e) +{ + return e->drawingarea; +} + + +void sc_editor_set_slide(SCEditor *e, struct slide *s) +{ +} + + +/* FIXME: GObjectify this */ +SCEditor *sc_editor_new(struct presentation *p) +{ + SCEditor *sceditor; + GtkTargetEntry targets[1]; + + sceditor = calloc(1, sizeof(SCEditor)); + if ( sceditor == NULL ) return NULL; + + sceditor->drawingarea = gtk_drawing_area_new(); + + gtk_widget_set_size_request(GTK_WIDGET(sceditor->drawingarea), + p->slide_width + 20, + p->slide_height + 20); + + g_signal_connect(G_OBJECT(sceditor->drawingarea), "destroy", + G_CALLBACK(destroy_sig), sceditor); + g_signal_connect(G_OBJECT(sceditor->drawingarea), "realize", + G_CALLBACK(realise_sig), sceditor); + g_signal_connect(G_OBJECT(sceditor->drawingarea), "button-press-event", + G_CALLBACK(button_press_sig), sceditor); + g_signal_connect(G_OBJECT(sceditor->drawingarea), "button-release-event", + G_CALLBACK(button_release_sig), sceditor); + g_signal_connect(G_OBJECT(sceditor->drawingarea), "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(sceditor->drawingarea, 0, targets, 1, + GDK_ACTION_PRIVATE); + g_signal_connect(sceditor->drawingarea, "drag-data-received", + G_CALLBACK(dnd_receive), p); + g_signal_connect(sceditor->drawingarea, "drag-motion", + G_CALLBACK(dnd_motion), p); + g_signal_connect(sceditor->drawingarea, "drag-drop", + G_CALLBACK(dnd_drop), p); + g_signal_connect(sceditor->drawingarea, "drag-leave", + G_CALLBACK(dnd_leave), p); + + gtk_widget_set_can_focus(GTK_WIDGET(sceditor->drawingarea), TRUE); + gtk_widget_add_events(GTK_WIDGET(sceditor->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(sceditor->drawingarea), "draw", + G_CALLBACK(draw_sig), p); + + gtk_widget_grab_focus(GTK_WIDGET(sceditor->drawingarea)); + + gtk_widget_show(sceditor->drawingarea); + + return 0; +} diff --git a/src/sc_editor.h b/src/sc_editor.h new file mode 100644 index 0000000..d1c1338 --- /dev/null +++ b/src/sc_editor.h @@ -0,0 +1,40 @@ +/* + * 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> + + +struct presentation; +typedef struct _sceditor SCEditor; + +extern void sc_editor_set_slide(SCEditor *e, struct slide *s); +extern GtkWidget *sc_editor_get_widget(SCEditor *e); +extern SCEditor *sc_editor_new(struct presentation *p); + +#endif /* SC_EDITOR_H */ diff --git a/src/slide_sorter.c b/src/slide_sorter.c index c64e408..8130ffa 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" @@ -216,7 +216,7 @@ static gboolean motion_sig(GtkWidget *da, GdkEventMotion *event, 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; @@ -329,15 +329,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); } @@ -373,8 +365,7 @@ static void dnd_receive(GtkWidget *widget, GdkDragContext *drag_context, /* FIXME: Do something */ int sn = slide_number(n->p, s); - s->rendered_thumb = render_slide(s, - n->p->thumb_slide_width, + s->rendered_thumb = render_slide(s, n->tw, n->p->slide_width, n->p->slide_height, n->p->is, @@ -392,7 +383,7 @@ static void dnd_receive(GtkWidget *widget, GdkDragContext *drag_context, if ( n->dragging_cur_proj_slide ) { fixup_proj(n->p, s); - change_proj_slide(n->p, s); + change_proj_slide(n->p->slideshow, s); } redraw_slidesorter(n); @@ -484,7 +475,7 @@ static void dnd_delete(GtkWidget *widget, GdkDragContext *drag_context, } - if ( n->p->cur_proj_slide == n->selected_slide ) { + if ( slideshow_slide(n->p->slideshow) == n->selected_slide ) { if ( same ) { @@ -501,7 +492,7 @@ 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]); } @@ -527,8 +518,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..bd64538 --- /dev/null +++ b/src/slide_window.c @@ -0,0 +1,620 @@ +/* + * 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; + GtkUIManager *ui; + GtkActionGroup *action_group; + + SCEditor *sceditor; + + struct menu_pl *style_menu; + int n_style_menu; + + struct slide *cur_slide; /* FIXME: SPOT inside SCEditor */ +}; + + +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); + } +} + +void update_toolbar(SlideWindow *sw) +{ + GtkWidget *d; + int cur_slide_number; + + d = gtk_ui_manager_get_widget(sw->ui, "/ui/displaywindowtoolbar/first"); + gtk_widget_set_sensitive(GTK_WIDGET(d), TRUE); + d = gtk_ui_manager_get_widget(sw->ui, "/ui/displaywindowtoolbar/prev"); + gtk_widget_set_sensitive(GTK_WIDGET(d), TRUE); + d = gtk_ui_manager_get_widget(sw->ui, "/ui/displaywindowtoolbar/next"); + gtk_widget_set_sensitive(GTK_WIDGET(d), TRUE); + d = gtk_ui_manager_get_widget(sw->ui, "/ui/displaywindowtoolbar/last"); + gtk_widget_set_sensitive(GTK_WIDGET(d), TRUE); + + cur_slide_number = slide_number(sw->p, sw->cur_slide); + if ( cur_slide_number == 0 ) { + + d = gtk_ui_manager_get_widget(sw->ui, + "/ui/displaywindowtoolbar/first"); + gtk_widget_set_sensitive(GTK_WIDGET(d), FALSE); + d = gtk_ui_manager_get_widget(sw->ui, + "/ui/displaywindowtoolbar/prev"); + gtk_widget_set_sensitive(GTK_WIDGET(d), FALSE); + + } + + if ( cur_slide_number == sw->p->num_slides-1 ) { + + d = gtk_ui_manager_get_widget(sw->ui, + "/ui/displaywindowtoolbar/next"); + gtk_widget_set_sensitive(GTK_WIDGET(d), FALSE); + d = gtk_ui_manager_get_widget(sw->ui, + "/ui/displaywindowtoolbar/last"); + gtk_widget_set_sensitive(GTK_WIDGET(d), FALSE); + + } + + +} + + +/* 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; + + /* 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); +} + + +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 gint saveas_sig(GtkWidget *widget, SlideWindow *sw) +{ + GtkWidget *d; + + 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); + + return 0; +} + + +static gint save_sig(GtkWidget *widget, SlideWindow *sw) +{ + if ( sw->p->filename == NULL ) { + return saveas_sig(widget, sw); + } + + save_presentation(sw->p, sw->p->filename); + + return 0; +} + + +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 gint export_pdf_sig(GtkWidget *widget, SlideWindow *sw) +{ + 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); + + return 0; +} + + +static gint about_sig(GtkWidget *widget, SlideWindow *sw) +{ + 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(sw->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), + "© 2014 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), + "© 2014 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, SlideWindow *sw) +{ + sw->p->slideshow = try_start_slideshow(sw->p); + return FALSE; +} + + +/* 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_slide(sw->sceditor, np); + + notify_notes_slide_changed(sw->p, np); + + if ( slideshow_linked(sw->p->slideshow) ) { + change_proj_slide(sw->p->slideshow, np); + } /* else leave the slideshow alone */ +} + + +static gint add_slide_sig(GtkWidget *widget, SlideWindow *sw) +{ + 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); + + return FALSE; +} + + +static gint first_slide_sig(GtkWidget *widget, SlideWindow *sw) +{ + change_edit_slide(sw, sw->p->slides[0]); + return FALSE; +} + + +static gint prev_slide_sig(GtkWidget *widget, SlideWindow *sw) +{ + int cur_slide_number; + + cur_slide_number = slide_number(sw->p, sw->cur_slide); + if ( cur_slide_number == 0 ) return FALSE; + + change_edit_slide(sw, sw->p->slides[cur_slide_number-1]); + + return FALSE; +} + + +static gint next_slide_sig(GtkWidget *widget, 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 FALSE; + + change_edit_slide(sw, sw->p->slides[cur_slide_number+1]); + + return FALSE; +} + + +static gint last_slide_sig(GtkWidget *widget, SlideWindow *sw) +{ + change_edit_slide(sw, sw->p->slides[sw->p->num_slides-1]); + + return FALSE; +} + + +static gint open_notes_sig(GtkWidget *widget, SlideWindow *sw) +{ + open_notes(sw->p); + return FALSE; +} + + +static gint open_clock_sig(GtkWidget *widget, SlideWindow *sw) +{ + open_clock(sw->p); + return FALSE; +} + + +static gint open_slidesorter_sig(GtkWidget *widget, SlideWindow *sw) +{ + open_slidesorter(sw->p); + return FALSE; +} + +static gint delete_frame_sig(GtkWidget *widget, SlideWindow *sw) +{ +#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 */ + return FALSE; +} + + +static void add_menu_bar(SlideWindow *sw, 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); + + sw->action_group = gtk_action_group_new("mainwindow"); + gtk_action_group_add_actions(sw->action_group, entries, n_entries, sw); + + sw->ui = gtk_ui_manager_new(); + gtk_ui_manager_insert_action_group(sw->ui, sw->action_group, 0); + g_signal_connect(sw->ui, "add_widget", G_CALLBACK(add_ui_sig), vbox); + if ( gtk_ui_manager_add_ui_from_file(sw->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(sw->window), + gtk_ui_manager_get_accel_group(sw->ui)); + gtk_ui_manager_ensure_update(sw->ui); + + toolbar = gtk_ui_manager_get_widget(sw->ui, "/displaywindowtoolbar"); + gtk_toolbar_insert(GTK_TOOLBAR(toolbar), + gtk_separator_tool_item_new(), -1); + + menu = gtk_ui_manager_get_widget(sw->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(sw); + update_toolbar(sw); +} + + +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); + + } + +} + + +SlideWindow *slide_window_open(struct presentation *p, GApplication *app) +{ + GtkWidget *window; + GtkWidget *vbox; + GtkWidget *scroll; + 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)); + sw->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); + + sw->sceditor = sc_editor_new(p); + 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), + sc_editor_get_widget(sw->sceditor)); + + add_menu_bar(sw, vbox); + 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); + + gtk_widget_show_all(window); + + return sw; +} diff --git a/src/mainwindow.h b/src/slide_window.h index b406492..51f9840 100644 --- a/src/mainwindow.h +++ b/src/slide_window.h @@ -27,14 +27,11 @@ #include <config.h> #endif +typedef struct _slidewindow SlideWindow; -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 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 update_toolbar(struct presentation *p); #endif /* MAINWINDOW_H */ diff --git a/src/slideshow.c b/src/slideshow.c index 66e4994..c0a9200 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,64 @@ #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" +struct _slideshow +{ + struct presentation *p; + struct slide *cur_slide; + GtkWidget *slideshow; + GtkWidget *drawingarea; + GdkCursor *blank_cursor; + int blank; + char geom[256]; + int slide_width; + struct inhibit_sys *inhibit; + int linked; +}; + + /* 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(ss->drawingarea, 0, 0, w, h); +} + - gtk_widget_queue_draw_area(p->ss_drawingarea, 0, 0, w, h); +void slideshow_rerender(SlideShow *ss) +{ + int n; + + if ( ss->cur_slide->rendered_proj != NULL ) return; + + n = slide_number(ss->p, ss->cur_slide); + ss->cur_slide->rendered_proj = render_slide(ss->cur_slide, + ss->slide_width, + ss->p->slide_width, + ss->p->slide_height, + ss->p->is, ISZ_SLIDESHOW, + n); } static gint ss_destroy_sig(GtkWidget *widget, struct presentation *p) { p->slideshow = NULL; - g_object_unref(p->blank_cursor); + g_object_unref(p->slideshow->blank_cursor); 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,21 +102,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, + cairo_rectangle(cr, xoff, yoff, ss->slide_width, h); + cairo_set_source_surface(cr, ss->cur_slide->rendered_proj, xoff, yoff); cairo_fill(cr); @@ -95,70 +126,71 @@ 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); - } - - p->cur_proj_slide = np; + ss->cur_slide = np; - notify_clock_slide_changed(p, np); + notify_clock_slide_changed(ss->p, np); /* The slide is already rendered, because the editor always gets there * first, so we only need to do this: */ - redraw_slideshow(p); + 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); + cur_slide_number = slide_number(ss->p, ss->p->cur_edit_slide); if ( cur_slide_number == 0 ) return FALSE; - change_edit_slide(p, p->slides[cur_slide_number-1]); + change_edit_slide(ss->p, ss->p->slides[cur_slide_number-1]); 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]); + cur_slide_number = slide_number(ss->p, ss->p->cur_edit_slide); + if ( cur_slide_number == ss->p->num_slides-1 ) return FALSE; + change_edit_slide(ss->p, ss->p->slides[cur_slide_number+1]); 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->slideshow); + free(ss); + ss->p->slideshow = NULL; + redraw_editor(ss->p); } -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, ss->p->cur_edit_slide); } - redraw_editor(p); + redraw_editor(ss->p); } -void check_toggle_blank(struct presentation *p) +int slideshow_linked(SlideShow *ss) { - if ( p->slideshow != NULL ) { + if ( ss == NULL ) return 0; + return ss->linked; +} + + +void check_toggle_blank(SlideShow *ss) +{ + if ( ss != NULL ) { //if ( p->prefs->b_splits ) { - toggle_slideshow_link(p); + toggle_slideshow_link(ss); //} else { // p->ss_blank = 1-p->ss_blank; // gdk_window_invalidate_rect(p->ss_drawingarea->window, @@ -169,27 +201,28 @@ void check_toggle_blank(struct presentation *p) 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 +231,67 @@ 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); 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(struct presentation *p) { GtkWidget *n; GdkScreen *screen; int n_monitors; int i; + SlideShow *ss; /* Presentation already running? */ - if ( p->slideshow != NULL ) return; + if ( p->slideshow != NULL ) return p->slideshow; + + ss = calloc(1, sizeof(SlideShow)); + if ( ss == NULL ) return NULL; - p->ss_blank = 0; + ss->p = p; + ss->blank = 0; - if ( p->inhibit == NULL ) { - p->inhibit = inhibit_prepare(); + 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); + ss->drawingarea = gtk_drawing_area_new(); + gtk_container_add(GTK_CONTAINER(n), ss->drawingarea); - gtk_widget_set_can_focus(GTK_WIDGET(p->ss_drawingarea), TRUE); - gtk_widget_add_events(GTK_WIDGET(p->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_signal_connect(G_OBJECT(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(n), "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 +301,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; if ( w > rect.width ) w = rect.width; - p->proj_slide_width = w; + ss->slide_width = w; } /* FIXME: Sensible (configurable) choice of monitor */ - rerender_slide(p); - - p->slideshow = n; - p->slideshow_linked = 1; + ss->linked = 1; gtk_window_fullscreen(GTK_WINDOW(n)); gtk_widget_show_all(GTK_WIDGET(n)); - if ( p->inhibit != NULL ) do_inhibit(p->inhibit, 1); - - //if ( p->prefs->open_notes ) open_notes(p); FIXME! + if ( ss->inhibit != NULL ) do_inhibit(ss->inhibit, 1); - p->cur_proj_slide = p->cur_edit_slide; + ss->cur_slide = p->cur_edit_slide; + notify_clock_slide_changed(p, ss->cur_slide); - notify_clock_slide_changed(p, p->cur_proj_slide); + return ss; } + diff --git a/src/slideshow.h b/src/slideshow.h index 66f3560..62a54e0 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,20 @@ #include <config.h> #endif +/* Opaque data structure representing a slideshow */ +typedef struct _slideshow SlideShow; -extern void try_start_slideshow(struct presentation *p); +extern SlideShow *try_start_slideshow(struct presentation *p); +extern void end_slideshow(SlideShow *ss); -extern void change_proj_slide(struct presentation *p, struct slide *np); +extern void change_proj_slide(SlideShow *ss, struct slide *np); +extern struct slide *slideshow_slide(SlideShow *ss); -extern void toggle_slideshow_link(struct presentation *p); -extern void check_toggle_blank(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 redraw_slideshow(struct presentation *p); - -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..eec2be2 100644 --- a/tests/render_test.c +++ b/tests/render_test.c @@ -117,7 +117,5 @@ int main(int argc, char *argv[]) gtk_widget_show_all(window); gtk_main(); - free_render_buffers(&s); - return 0; } |