From 6b60cafe0c2689531459f1cffd704da16ed2aec3 Mon Sep 17 00:00:00 2001 From: Thomas White Date: Thu, 28 Mar 2019 15:05:10 +0100 Subject: Restore slideshow and clock --- libstorycode/gtk/gtknarrativeview.c | 47 +++- libstorycode/gtk/gtknarrativeview.h | 1 + libstorycode/narrative.c | 14 ++ libstorycode/narrative.h | 2 + meson.build | 4 +- src-old/pr_clock.c | 438 ----------------------------------- src-old/pr_clock.h | 37 --- src-old/slideshow.c | 240 -------------------- src-old/slideshow.h | 83 ------- src/narrative_window.c | 245 ++++++++++---------- src/pr_clock.c | 439 ++++++++++++++++++++++++++++++++++++ src/pr_clock.h | 37 +++ src/slideshow.c | 222 ++++++++++++++++++ src/slideshow.h | 79 +++++++ 14 files changed, 967 insertions(+), 921 deletions(-) delete mode 100644 src-old/pr_clock.c delete mode 100644 src-old/pr_clock.h delete mode 100644 src-old/slideshow.c delete mode 100644 src-old/slideshow.h create mode 100644 src/pr_clock.c create mode 100644 src/pr_clock.h create mode 100644 src/slideshow.c create mode 100644 src/slideshow.h diff --git a/libstorycode/gtk/gtknarrativeview.c b/libstorycode/gtk/gtknarrativeview.c index 5bf45dd..1d0a1c2 100644 --- a/libstorycode/gtk/gtknarrativeview.c +++ b/libstorycode/gtk/gtknarrativeview.c @@ -487,11 +487,28 @@ static double para_top(Narrative *n, int pnum) static void draw_para_highlight(cairo_t *cr, Narrative *n, int cursor_para) { double cx, cy, cw, ch; + struct narrative_item *item; + + cx = 0.0; + cy = para_top(n, cursor_para); - cx = n->space_l; - cy = n->space_t + para_top(n, cursor_para); - cw = n->items[cursor_para].slide_w; - ch = n->items[cursor_para].slide_h; + item = &n->items[cursor_para]; + + if ( item->type == NARRATIVE_ITEM_SLIDE ) { + cw = item->slide_w; + ch = item->slide_h; + } else { + if ( item->layout != NULL ) { + PangoRectangle rect; + pango_layout_get_extents(item->layout, NULL, &rect); + cw = pango_units_to_double(rect.width) + item->space_r + item->space_l; + ch = pango_units_to_double(rect.height) + item->space_b + item->space_t; + } else { + cw = 0.0; + ch = 0.0; + fprintf(stderr, "No layout when drawing highlight box\n"); + } + } cairo_new_path(cr); cairo_rectangle(cr, cx, cy, cw, ch); @@ -1176,3 +1193,25 @@ GtkWidget *gtk_narrative_view_new(Presentation *p) return GTK_WIDGET(nview); } + + +void gtk_narrative_view_set_para_highlight(GtkNarrativeView *e, int para_highlight) +{ + e->para_highlight = para_highlight; + redraw(e); +} + + +int gtk_narrative_view_get_cursor_para(GtkNarrativeView *e) +{ + return e->cpos.para; +} + + +void gtk_narrative_view_set_cursor_para(GtkNarrativeView *e, signed int pos) +{ + e->cpos.para = pos; + e->cpos.pos = 0; + e->cpos.trail = 0; + redraw(e); +} diff --git a/libstorycode/gtk/gtknarrativeview.h b/libstorycode/gtk/gtknarrativeview.h index fdcfed6..9cbdcbd 100644 --- a/libstorycode/gtk/gtknarrativeview.h +++ b/libstorycode/gtk/gtknarrativeview.h @@ -100,6 +100,7 @@ struct _gtknarrativeviewclass typedef struct _gtknarrativeview GtkNarrativeView; typedef struct _gtknarrativeviewclass GtkNarrativeViewClass; +extern GType gtk_narrative_view_get_type(void); extern GtkWidget *gtk_narrative_view_new(Presentation *p); extern void gtk_narrative_view_set_logical_size(GtkNarrativeView *e, double w, double h); diff --git a/libstorycode/narrative.c b/libstorycode/narrative.c index 81b55f0..c0ae0d7 100644 --- a/libstorycode/narrative.c +++ b/libstorycode/narrative.c @@ -248,3 +248,17 @@ void narrative_split_item(Narrative *n, int i1, size_t o1) item2->type = NARRATIVE_ITEM_TEXT; } + + +int narrative_get_num_items(Narrative *n) +{ + return n->n_items; +} + + +Slide *narrative_get_slide(Narrative *n, int para) +{ + if ( para >= n->n_items ) return NULL; + if ( n->items[para].type != NARRATIVE_ITEM_SLIDE ) return NULL; + return n->items[para].slide; +} diff --git a/libstorycode/narrative.h b/libstorycode/narrative.h index 5b8af4a..3ee215f 100644 --- a/libstorycode/narrative.h +++ b/libstorycode/narrative.h @@ -41,6 +41,8 @@ extern void narrative_add_slide(Narrative *n, Slide *slide); extern void narrative_delete_block(Narrative *n, int i1, size_t o1, int i2, size_t o2); extern void narrative_split_item(Narrative *n, int i1, size_t o1); +extern int narrative_get_num_items(Narrative *n); +extern Slide *narrative_get_slide(Narrative *n, int para); #endif /* NARRATIVE_H */ diff --git a/meson.build b/meson.build index 6c3b29b..569b8bb 100644 --- a/meson.build +++ b/meson.build @@ -110,8 +110,8 @@ executable('pdfstorycode', executable('colloquium', ['src/colloquium.c', 'src/narrative_window.c', -# 'src/slideshow.c', -# 'src/pr_clock.c', + 'src/slideshow.c', + 'src/pr_clock.c', 'src/slide_window.c', 'src/testcard.c', # 'src/stylesheet_editor.c', diff --git a/src-old/pr_clock.c b/src-old/pr_clock.c deleted file mode 100644 index 8085c89..0000000 --- a/src-old/pr_clock.c +++ /dev/null @@ -1,438 +0,0 @@ -/* - * pr_clock.c - * - * Copyright © 2013-2018 Thomas White - * - * This file is part of Colloquium. - * - * Colloquium is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - */ - - -#ifdef HAVE_CONFIG_H -#include -#endif - -#include -#include -#include -#include - -#include "presentation.h" -#include "pr_clock.h" -#include "utils.h" - - -struct pr_clock -{ - int open; - - GtkWidget *window; - GtkWidget *entry; - GtkWidget *startbutton; - GtkWidget *da; - GtkWidget *wallclock; - GtkWidget *elapsed; - GtkWidget *remaining; - GtkWidget *status; - GTimeZone *tz; - - GDateTime *start; - double time_elapsed_at_start; - guint timer_id; - - int running; - double time_allowed; - double time_elapsed; - int pos; - int end; - int pos_reached; - - double t; - double tf; -}; - - -static char *format_span(int n) -{ - char tmp[32]; - int hours, mins, sec; - char *s; - - if ( n < 0 ) { - s = "-"; - n = -n; - } else { - s = ""; - } - - sec = n % 60; - mins = ((n-sec) % (60*60))/60; - hours = (n-sec-mins) / (60*60); - - snprintf(tmp, 31, "%s%i:%02i:%02i", s, hours, mins, sec); - - return strdup(tmp); -} - - -static char *format_span_nice(int n) -{ - char tmp[64]; - int hours, mins, sec; - char *s; - - if ( n < 0 ) { - s = "behind"; - n = -n; - } else { - s = "ahead"; - } - - sec = n % 60; - mins = ((n-sec) % (60*60))/60; - hours = (n-sec-mins) / (60*60); - - if ( n <= 60 ) { - snprintf(tmp, 63, "%i seconds %s", n, s); - return strdup(tmp); - } - - if ( n < 60*60 ) { - snprintf(tmp, 63, "%i min %i seconds %s", mins, sec, s); - return strdup(tmp); - } - - snprintf(tmp, 63, "%i hours, %i min, %i seconds %s", - hours, mins, sec, s); - return strdup(tmp); -} - - -static gboolean update_clock(gpointer data) -{ - struct pr_clock *n = data; - gchar *d; - GDateTime *dt; - GTimeSpan sp; - double time_remaining; - double delta; - gint w, h; - char *tmp; - - if ( !n->open ) { - g_date_time_unref(n->start); - g_time_zone_unref(n->tz); - free(n); - return FALSE; - } - - dt = g_date_time_new_now(n->tz); - - if ( n->running ) { - - sp = g_date_time_difference(dt, n->start); - n->time_elapsed = n->time_elapsed_at_start + - sp / G_TIME_SPAN_SECOND; - - time_remaining = n->time_allowed - n->time_elapsed; - - tmp = format_span(n->time_elapsed); - gtk_label_set_text(GTK_LABEL(n->elapsed), tmp); - free(tmp); - - tmp = format_span(time_remaining); - gtk_label_set_text(GTK_LABEL(n->remaining), tmp); - free(tmp); - - } else { - - n->time_elapsed = n->time_elapsed_at_start; - - time_remaining = n->time_allowed - n->time_elapsed; - - tmp = format_span(n->time_elapsed); - gtk_label_set_text(GTK_LABEL(n->elapsed), tmp); - free(tmp); - - tmp = format_span(time_remaining); - gtk_label_set_text(GTK_LABEL(n->remaining), tmp); - free(tmp); - - } - - d = g_date_time_format(dt, "%H:%M:%S"); - g_date_time_unref(dt); - - gtk_label_set_text(GTK_LABEL(n->wallclock), d); - free(d); - - n->t = n->time_elapsed / n->time_allowed; - - if ( n->time_allowed == 0.0 ) n->t = 0.0; - if ( n->time_elapsed > n->time_allowed ) n->t = 1.0; - - if ( n->end > 0 ) { - n->tf = (double)n->pos_reached / (n->end-1); - } else { - n->tf = 0.0; - } - - delta = (n->tf - n->t)*n->time_allowed; - tmp = format_span_nice(delta); - gtk_label_set_text(GTK_LABEL(n->status), tmp); - free(tmp); - - w = gtk_widget_get_allocated_width(GTK_WIDGET(n->da)); - h = gtk_widget_get_allocated_height(GTK_WIDGET(n->da)); - gtk_widget_queue_draw_area(n->da, 0, 0, w, h); - - return TRUE; -} - - -void pr_clock_set_pos(PRClock *n, int pos, int end) -{ - if ( n == NULL ) return; - n->pos = pos; - if ( n->pos > n->pos_reached ) { - n->pos_reached = pos; - } - n->end = end; - update_clock(n); -} - - -static gint close_clock_sig(GtkWidget *w, PRClock *n) -{ - g_source_remove(n->timer_id); - free(n); - return FALSE; -} - - -static gboolean clock_draw_sig(GtkWidget *da, cairo_t *cr, struct pr_clock *n) -{ - int width, height; - double s; - double ff; - - width = gtk_widget_get_allocated_width(GTK_WIDGET(da)); - height = gtk_widget_get_allocated_height(GTK_WIDGET(da)); - s = width-20.0; - - /* Overall background */ - cairo_rectangle(cr, 10.0, 0.0, s, height); - cairo_set_source_rgb(cr, 1.0, 1.0, 1.0); - cairo_fill(cr); - - cairo_rectangle(cr, 10.0, 0.0, s*n->t, height); - cairo_set_source_rgb(cr, 0.0, 0.0, 1.0); - cairo_fill(cr); - - if ( n->tf > n->t ) { - cairo_rectangle(cr, 10.0+s*n->t, 0.0, (n->tf - n->t)*s, height); - cairo_set_source_rgb(cr, 0.0, 1.0, 0.0); - cairo_fill(cr); - } else { - cairo_rectangle(cr, 10.0+s*n->t, 0.0, (n->tf - n->t)*s, height); - cairo_set_source_rgb(cr, 1.0, 0.0, 0.0); - cairo_fill(cr); - } - - ff = (double)n->pos / (n->end-1); - if ( n->end == 1 ) ff = 0.0; - cairo_move_to(cr, 10.0+ff*s, 0.0); - cairo_line_to(cr, 10.0+ff*s, height); - cairo_set_line_width(cr, 2.0); - cairo_set_source_rgb(cr, 1.0, 0.0, 1.0); - cairo_stroke(cr); - - if ( !n->running ) { - cairo_move_to(cr, 10.0, height*0.8); - cairo_set_font_size(cr, height*0.8); - cairo_select_font_face(cr, "sans-serif", - CAIRO_FONT_SLANT_NORMAL, - CAIRO_FONT_WEIGHT_BOLD); - cairo_set_source_rgb(cr, 0.0, 0.0, 0.0); - cairo_show_text(cr, _("Timer is NOT running!")); - } - - return FALSE; -} - - -static void set_sig(GtkEditable *w, struct pr_clock *n) -{ - const gchar *t; - char *check; - - t = gtk_entry_get_text(GTK_ENTRY(n->entry)); - n->time_allowed = 60.0 * strtod(t, &check); - if ( check == t ) { - fprintf(stderr, "Invalid time '%s'\n", t); - n->time_allowed = 0.0; - } - - update_clock(n); -} - - -static gboolean reset_sig(GtkWidget *w, gpointer data) -{ - struct pr_clock *n = data; - - n->time_elapsed = 0; - n->time_elapsed_at_start = 0; - - if ( n->start != NULL ) { - g_date_time_unref(n->start); - } - - n->start = g_date_time_new_now(n->tz); - - update_clock(n); - - return FALSE; -} - - -static gboolean setpos_sig(GtkWidget *w, gpointer data) -{ - struct pr_clock *n = data; - n->pos_reached = n->pos; - update_clock(n); - return FALSE; -} - - -static gboolean start_sig(GtkWidget *w, gpointer data) -{ - struct pr_clock *n = data; - - if ( n->running ) { - n->running = 0; - n->time_elapsed_at_start = n->time_elapsed; - gtk_label_set_text(GTK_LABEL(gtk_bin_get_child(GTK_BIN(w))), - _("Start")); - } else { - n->time_elapsed_at_start = n->time_elapsed; - if ( n->start != NULL ) { - g_date_time_unref(n->start); - } - n->start = g_date_time_new_now(n->tz); - n->running = 1; - gtk_label_set_text(GTK_LABEL(gtk_bin_get_child(GTK_BIN(w))), - _("Stop")); - } - - update_clock(n); - - return FALSE; -} - - -PRClock *pr_clock_new() -{ - struct pr_clock *n; - GtkWidget *vbox; - GtkWidget *hbox; - GtkWidget *resetbutton; - GtkWidget *setposbutton; - GtkWidget *grid; - GtkWidget *label; - - n = malloc(sizeof(struct pr_clock)); - if ( n == NULL ) return NULL; - n->open = 1; - - n->tz = g_time_zone_new_local(); - - n->window = gtk_window_new(GTK_WINDOW_TOPLEVEL); - gtk_window_set_default_size(GTK_WINDOW(n->window), 600, 150); - - vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0); - gtk_container_add(GTK_CONTAINER(n->window), vbox); - - hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0); - gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 10); - - label = gtk_label_new(_("Length (mins):")); - gtk_label_set_markup(GTK_LABEL(label), _("Length (mins):")); - g_object_set(G_OBJECT(label), "halign", GTK_ALIGN_END, NULL); - gtk_box_pack_start(GTK_BOX(hbox), label, TRUE, TRUE, 10); - - n->entry = gtk_entry_new(); - gtk_box_pack_start(GTK_BOX(hbox), n->entry, TRUE, TRUE, 0); - - n->startbutton = gtk_button_new_with_label(_("Start")); - gtk_box_pack_start(GTK_BOX(hbox), n->startbutton, TRUE, TRUE, 10); - - resetbutton = gtk_button_new_with_label(_("Reset")); - gtk_box_pack_start(GTK_BOX(hbox), resetbutton, TRUE, TRUE, 10); - - setposbutton = gtk_button_new_with_label(_("Set position")); - gtk_box_pack_start(GTK_BOX(hbox), setposbutton, TRUE, TRUE, 10); - - n->da = gtk_drawing_area_new(); - gtk_box_pack_start(GTK_BOX(vbox), n->da, TRUE, TRUE, 0); - g_signal_connect(G_OBJECT(n->da), "draw", G_CALLBACK(clock_draw_sig), n); - g_signal_connect(G_OBJECT(n->window), "destroy", - G_CALLBACK(close_clock_sig), n); /* FIXME: Uniqueness */ - - grid = gtk_grid_new(); - gtk_grid_set_row_spacing(GTK_GRID(grid), 10); - gtk_grid_set_column_homogeneous(GTK_GRID(grid), TRUE); - gtk_box_pack_start(GTK_BOX(vbox), grid, FALSE, FALSE, 10); - label = gtk_label_new(_("Time elapsed")); - gtk_label_set_markup(GTK_LABEL(label), _("Time elapsed")); - gtk_grid_attach(GTK_GRID(grid), label, 0, 0, 1, 1); - label = gtk_label_new(_("Time remaining")); - gtk_label_set_markup(GTK_LABEL(label), _("Time remaining")); - gtk_grid_attach(GTK_GRID(grid), label, 1, 0, 1, 1); - n->status = gtk_label_new(""); - gtk_grid_attach(GTK_GRID(grid), n->status, 2, 0, 1, 1); - n->elapsed = gtk_label_new(""); - gtk_grid_attach(GTK_GRID(grid), n->elapsed, 0, 1, 1, 1); - n->remaining = gtk_label_new(""); - gtk_grid_attach(GTK_GRID(grid), n->remaining, 1, 1, 1, 1); - n->wallclock = gtk_label_new(""); - gtk_grid_attach(GTK_GRID(grid), n->wallclock, 2, 1, 1, 1); - - g_signal_connect(G_OBJECT(n->startbutton), "clicked", - G_CALLBACK(start_sig), n); - g_signal_connect(G_OBJECT(resetbutton), "clicked", - G_CALLBACK(reset_sig), n); - g_signal_connect(G_OBJECT(setposbutton), "clicked", - G_CALLBACK(setpos_sig), n); - g_signal_connect(G_OBJECT(n->entry), "changed", - G_CALLBACK(set_sig), n); - - n->running = 0; - n->time_allowed = 0; - n->time_elapsed = 0; - n->time_elapsed_at_start = 0; - n->pos = 0; - n->pos_reached = 0; - n->end = 0; - n->start = NULL; - update_clock(n); - n->timer_id = g_timeout_add_seconds(1, update_clock, n); - - gtk_window_set_title(GTK_WINDOW(n->window), _("Presentation clock")); - - gtk_widget_show_all(n->window); - return n; -} diff --git a/src-old/pr_clock.h b/src-old/pr_clock.h deleted file mode 100644 index 97d2d0d..0000000 --- a/src-old/pr_clock.h +++ /dev/null @@ -1,37 +0,0 @@ -/* - * pr_clock.h - * - * Copyright © 2013-2018 Thomas White - * - * This file is part of Colloquium. - * - * Colloquium is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - */ - -#ifndef CLOCK_H -#define CLOCK_H - -#ifdef HAVE_CONFIG_H -#include -#endif - -typedef struct pr_clock PRClock; - -extern PRClock *pr_clock_new(void); - -extern void pr_clock_set_pos(PRClock *n, int pos, int end); - - -#endif /* CLOCK_H */ diff --git a/src-old/slideshow.c b/src-old/slideshow.c deleted file mode 100644 index 6e1c49d..0000000 --- a/src-old/slideshow.c +++ /dev/null @@ -1,240 +0,0 @@ -/* - * slideshow.c - * - * Copyright © 2013-2018 Thomas White - * - * This file is part of Colloquium. - * - * Colloquium is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - */ - - -#ifdef HAVE_CONFIG_H -#include -#endif - -#include -#include -#include -#include -#include - -#include "colloquium.h" -#include "presentation.h" -#include "render.h" -#include "pr_clock.h" -#include "frame.h" -#include "utils.h" - -G_DEFINE_TYPE_WITH_CODE(SCSlideshow, sc_slideshow, GTK_TYPE_WINDOW, NULL) - - -static void sc_slideshow_init(SCSlideshow *ss) -{ -} - - -void sc_slideshow_class_init(SCSlideshowClass *klass) -{ -} - - -static void slideshow_rerender(SCSlideshow *ss) -{ - int n; - gint w, h; - - if ( ss->cur_slide == NULL ) return; - - if ( ss->surface != NULL ) { - cairo_surface_destroy(ss->surface); - } - - n = slide_number(ss->p, ss->cur_slide); - ss->surface = render_sc(ss->cur_slide, - ss->slide_width, ss->slide_height, - ss->p->slide_width, ss->p->slide_height, - ss->p->stylesheet, NULL, ss->p->is, n, - &ss->top, ss->p->lang); - - w = gtk_widget_get_allocated_width(GTK_WIDGET(ss->drawingarea)); - h = gtk_widget_get_allocated_height(GTK_WIDGET(ss->drawingarea)); - - gtk_widget_queue_draw_area(ss->drawingarea, 0, 0, w, h); -} - - -static gint ssh_destroy_sig(GtkWidget *widget, SCSlideshow *ss) -{ - if ( ss->blank_cursor != NULL ) { - g_object_unref(ss->blank_cursor); - } - if ( ss->surface != NULL ) { - cairo_surface_destroy(ss->surface); - } - if ( ss->inhibit_cookie ) { - gtk_application_uninhibit(ss->app, ss->inhibit_cookie); - } - return FALSE; -} - - -static gboolean ss_draw_sig(GtkWidget *da, cairo_t *cr, SCSlideshow *ss) -{ - double width, height; - - width = gtk_widget_get_allocated_width(GTK_WIDGET(da)); - height = gtk_widget_get_allocated_height(GTK_WIDGET(da)); - - /* Overall background */ - cairo_rectangle(cr, 0.0, 0.0, width, height); - cairo_set_source_rgb(cr, 0.0, 0.0, 0.0); - cairo_fill(cr); - - if ( !ss->blank ) { - - /* Draw the slide from the cache */ - cairo_rectangle(cr, ss->xoff, ss->yoff, - ss->slide_width, ss->slide_height); - cairo_set_source_surface(cr, ss->surface, ss->xoff, ss->yoff); - cairo_fill(cr); - - } - - return FALSE; -} - - -static gboolean ss_realize_sig(GtkWidget *w, SCSlideshow *ss) -{ - if ( (ss->app == NULL) || colloquium_get_hidepointer(COLLOQUIUM(ss->app)) ) { - - /* Hide the pointer */ - GdkWindow *win; - win = gtk_widget_get_window(w); - ss->blank_cursor = gdk_cursor_new_for_display(gdk_display_get_default(), - GDK_BLANK_CURSOR); - gdk_window_set_cursor(GDK_WINDOW(win), ss->blank_cursor); - - } else { - ss->blank_cursor = NULL; - } - - slideshow_rerender(ss); - - return FALSE; -} - - -static void ss_size_sig(GtkWidget *widget, GdkRectangle *rect, SCSlideshow *ss) -{ - const double sw = ss->p->slide_width; - const double sh = ss->p->slide_height; - - if ( sw/sh > (double)rect->width/rect->height ) { - /* Slide is too wide. Letterboxing top/bottom */ - ss->slide_width = rect->width; - ss->slide_height = rect->width * sh/sw; - } else { - /* Letterboxing at sides */ - ss->slide_width = rect->height * sw/sh; - ss->slide_height = rect->height; - } - - ss->xoff = (rect->width - ss->slide_width)/2.0; - ss->yoff = (rect->height - ss->slide_height)/2.0; - - printf("screen %i %i\n", rect->width, rect->height); - printf("slide %f %f\n", sw, sh); - printf("rendering slide at %i %i\n", ss->slide_width, ss->slide_height); - printf("offset %i %i\n", ss->xoff, ss->yoff); - - slideshow_rerender(ss); -} - - -void sc_slideshow_set_slide(SCSlideshow *ss, SCBlock *ns) -{ - ss->cur_slide = ns; - slideshow_rerender(ss); -} - - -SCSlideshow *sc_slideshow_new(struct presentation *p, GtkApplication *app) -{ - GdkDisplay *display; - int n_monitors; - SCSlideshow *ss; - - ss = g_object_new(SC_TYPE_SLIDESHOW, NULL); - if ( ss == NULL ) return NULL; - - ss->blank = 0; - ss->p = p; - ss->cur_slide = NULL; - ss->blank_cursor = NULL; - ss->surface = NULL; - ss->app = app; - - ss->drawingarea = gtk_drawing_area_new(); - gtk_container_add(GTK_CONTAINER(ss), ss->drawingarea); - - gtk_widget_set_can_focus(GTK_WIDGET(ss->drawingarea), TRUE); - gtk_widget_add_events(GTK_WIDGET(ss->drawingarea), - GDK_KEY_PRESS_MASK); - - g_signal_connect(G_OBJECT(ss), "destroy", - G_CALLBACK(ssh_destroy_sig), ss); - g_signal_connect(G_OBJECT(ss), "realize", - G_CALLBACK(ss_realize_sig), ss); - g_signal_connect(G_OBJECT(ss), "size-allocate", - G_CALLBACK(ss_size_sig), ss); - - g_signal_connect(G_OBJECT(ss->drawingarea), "draw", - G_CALLBACK(ss_draw_sig), ss); - - gtk_widget_grab_focus(GTK_WIDGET(ss->drawingarea)); - - display = gdk_display_get_default(); - n_monitors = gdk_display_get_n_monitors(display); - - GdkMonitor *mon_ss; - if ( n_monitors == 1 ) { - mon_ss = gdk_display_get_primary_monitor(display); - printf(_("Single monitor mode\n")); - ss->single_monitor = 1; - } else { - mon_ss = gdk_display_get_monitor(display, 1); - printf(_("Dual monitor mode\n")); - ss->single_monitor = 0; - } - - /* Workaround because gtk_window_fullscreen_on_monitor doesn't work */ - GdkRectangle rect; - gdk_monitor_get_geometry(mon_ss, &rect); - gtk_window_move(GTK_WINDOW(ss), rect.x, rect.y); - gtk_window_fullscreen(GTK_WINDOW(ss)); - - ss->linked = 1; - - if ( app != NULL ) { - ss->inhibit_cookie = gtk_application_inhibit(app, GTK_WINDOW(ss), - GTK_APPLICATION_INHIBIT_IDLE, - _("Presentation slide show is running")); - } - - return ss; -} - diff --git a/src-old/slideshow.h b/src-old/slideshow.h deleted file mode 100644 index ff16d73..0000000 --- a/src-old/slideshow.h +++ /dev/null @@ -1,83 +0,0 @@ -/* - * slideshow.h - * - * Copyright © 2013-2018 Thomas White - * - * This file is part of Colloquium. - * - * Colloquium is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - */ - -#ifndef SLIDESHOW_H -#define SLIDESHOW_H - -#ifdef HAVE_CONFIG_H -#include -#endif - -#include - -#define SC_TYPE_SLIDESHOW (sc_slideshow_get_type()) - -#define SC_SLIDESHOW(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), \ - SC_TYPE_SLIDESHOW, SCEditor)) - -#define SC_IS_SLIDESHOW(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), \ - SC_TYPE_SLIDESHOW)) - -#define SC_SLIDESHOW_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((obj), \ - SC_TYPE_SLIDESHOW, SCEditorClass)) - -#define SC_IS_SLIDESHOW_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((obj), \ - SC_TYPE_SLIDESHOW)) - -#define SC_SLIDESHOW_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), \ - SC_TYPE_SLIDESHOW, SCSlideShowClass)) - -struct _scslideshow -{ - GtkWindow parent_instance; - - /* */ - struct presentation *p; - SCBlock *cur_slide; - GtkWidget *drawingarea; - GdkCursor *blank_cursor; - int blank; - int slide_width; - int slide_height; - int xoff; - int yoff; - int linked; - cairo_surface_t *surface; - struct frame *top; - int single_monitor; - uint inhibit_cookie; - GtkApplication *app; -}; - - -struct _scslideshowclass -{ - GtkWindowClass parent_class; -}; - -typedef struct _scslideshow SCSlideshow; -typedef struct _scslideshowclass SCSlideshowClass; - -extern SCSlideshow *sc_slideshow_new(struct presentation *p, GtkApplication *app); -extern void sc_slideshow_set_slide(SCSlideshow *ss, SCBlock *ns); - -#endif /* SLIDESHOW_H */ diff --git a/src/narrative_window.c b/src/narrative_window.c index c51d3a3..a6e71af 100644 --- a/src/narrative_window.c +++ b/src/narrative_window.c @@ -40,11 +40,10 @@ #include "narrative_window.h" #include "slide_window.h" #include "testcard.h" -//#include "pr_clock.h" +#include "pr_clock.h" +#include "slideshow.h" //#include "print.h" //#include "stylesheet_editor.h" -typedef struct _ss SCSlideshow; /* FIXME placeholder */ -typedef struct _pc PRClock; /* FIXME placeholder */ struct _narrative_window { @@ -109,25 +108,25 @@ static void update_titlebar(NarrativeWindow *nw) static void update_toolbar(NarrativeWindow *nw) { -// int cur_para; + int cur_para, n_para; - /* FIXME */ -// cur_para = sc_editor_get_cursor_para(nw->nv); -// if ( cur_para == 0 ) { -// gtk_widget_set_sensitive(GTK_WIDGET(nw->bfirst), FALSE); -// gtk_widget_set_sensitive(GTK_WIDGET(nw->bprev), FALSE); -// } else { -// gtk_widget_set_sensitive(GTK_WIDGET(nw->bfirst), TRUE); -// gtk_widget_set_sensitive(GTK_WIDGET(nw->bprev), TRUE); -// } -// -// if ( cur_para == sc_editor_get_num_paras(nw->nv)-1 ) { -// gtk_widget_set_sensitive(GTK_WIDGET(nw->bnext), FALSE); -// gtk_widget_set_sensitive(GTK_WIDGET(nw->blast), FALSE); -// } else { -// gtk_widget_set_sensitive(GTK_WIDGET(nw->bnext), TRUE); -// gtk_widget_set_sensitive(GTK_WIDGET(nw->blast), TRUE); -// } + cur_para = gtk_narrative_view_get_cursor_para(GTK_NARRATIVE_VIEW(nw->nv)); + if ( cur_para == 0 ) { + gtk_widget_set_sensitive(GTK_WIDGET(nw->bfirst), FALSE); + gtk_widget_set_sensitive(GTK_WIDGET(nw->bprev), FALSE); + } else { + gtk_widget_set_sensitive(GTK_WIDGET(nw->bfirst), TRUE); + gtk_widget_set_sensitive(GTK_WIDGET(nw->bprev), TRUE); + } + + n_para = narrative_get_num_items(presentation_get_narrative(nw->p)); + if ( cur_para == n_para - 1 ) { + gtk_widget_set_sensitive(GTK_WIDGET(nw->bnext), FALSE); + gtk_widget_set_sensitive(GTK_WIDGET(nw->blast), FALSE); + } else { + gtk_widget_set_sensitive(GTK_WIDGET(nw->bnext), TRUE); + gtk_widget_set_sensitive(GTK_WIDGET(nw->blast), TRUE); + } } @@ -318,87 +317,98 @@ static void add_slide_sig(GSimpleAction *action, GVariant *parameter, static void first_para_sig(GSimpleAction *action, GVariant *parameter, gpointer vp) { -// NarrativeWindow *nw = vp; -// sc_editor_set_cursor_para(nw->nv, 0); -// pr_clock_set_pos(nw->pr_clock, sc_editor_get_cursor_para(nw->nv), -// sc_editor_get_num_paras(nw->nv)); -// update_toolbar(nw); + NarrativeWindow *nw = vp; + int n_paras = narrative_get_num_items(presentation_get_narrative(nw->p)); + gtk_narrative_view_set_cursor_para(GTK_NARRATIVE_VIEW(nw->nv), 0); + pr_clock_set_pos(nw->pr_clock, + gtk_narrative_view_get_cursor_para(GTK_NARRATIVE_VIEW(nw->nv)), + n_paras); + update_toolbar(nw); } static void ss_prev_para(SCSlideshow *ss, void *vp) { -// NarrativeWindow *nw = vp; -// if ( sc_editor_get_cursor_para(nw->nv) == 0 ) return; -// sc_editor_set_cursor_para(nw->nv, -// sc_editor_get_cursor_para(nw->nv)-1); -// pr_clock_set_pos(nw->pr_clock, sc_editor_get_cursor_para(nw->nv), -// sc_editor_get_num_paras(nw->nv)); -// update_toolbar(nw); + NarrativeWindow *nw = vp; + int n_paras = narrative_get_num_items(presentation_get_narrative(nw->p)); + if ( gtk_narrative_view_get_cursor_para(GTK_NARRATIVE_VIEW(nw->nv)) == 0 ) return; + gtk_narrative_view_set_cursor_para(GTK_NARRATIVE_VIEW(nw->nv), + gtk_narrative_view_get_cursor_para(GTK_NARRATIVE_VIEW(nw->nv))-1); + pr_clock_set_pos(nw->pr_clock, + gtk_narrative_view_get_cursor_para(GTK_NARRATIVE_VIEW(nw->nv)), + n_paras); + update_toolbar(nw); } static void prev_para_sig(GSimpleAction *action, GVariant *parameter, gpointer vp) { -// NarrativeWindow *nw = vp; -// ss_prev_para(nw->show, nw); + NarrativeWindow *nw = vp; + ss_prev_para(nw->show, nw); } static void ss_next_para(SCSlideshow *ss, void *vp) { -// NarrativeWindow *nw = vp; -// SCBlock *ns; -// -// sc_editor_set_cursor_para(nw->nv, -// sc_editor_get_cursor_para(nw->nv)+1); -// -// /* If we only have one monitor, don't try to do paragraph counting */ -// if ( ss->single_monitor && !nw->show_no_slides ) { -// int i, max; -// max = sc_editor_get_num_paras(nw->nv); -// for ( i=sc_editor_get_cursor_para(nw->nv); inv, i); -// ns = sc_editor_get_cursor_bvp(nw->nv); -// if ( ns != NULL ) break; -// } -// } -// -// pr_clock_set_pos(nw->pr_clock, sc_editor_get_cursor_para(nw->nv), -// sc_editor_get_num_paras(nw->nv)); -// ns = sc_editor_get_cursor_bvp(nw->nv); -// if ( ns != NULL ) { -// sc_slideshow_set_slide(nw->show, ns); -// } -// update_toolbar(nw); + NarrativeWindow *nw = vp; + Slide *ns; + Narrative *narr; + GtkNarrativeView *nv; + int n_paras; + + narr = presentation_get_narrative(nw->p); + n_paras = narrative_get_num_items(narr); + nv = GTK_NARRATIVE_VIEW(nw->nv); + + gtk_narrative_view_set_cursor_para(nv, gtk_narrative_view_get_cursor_para(nv)+1); + + /* If we only have one monitor, skip to next slide */ + if ( ss->single_monitor && !nw->show_no_slides ) { + int i; + for ( i=gtk_narrative_view_get_cursor_para(nv); ipr_clock, gtk_narrative_view_get_cursor_para(nv), n_paras); + ns = narrative_get_slide(narr, gtk_narrative_view_get_cursor_para(nv)); + if ( ns != NULL ) { + sc_slideshow_set_slide(nw->show, ns); + } + update_toolbar(nw); } static void next_para_sig(GSimpleAction *action, GVariant *parameter, gpointer vp) { -// NarrativeWindow *nw = vp; -// ss_next_para(nw->show, nw); + NarrativeWindow *nw = vp; + ss_next_para(nw->show, nw); } static void last_para_sig(GSimpleAction *action, GVariant *parameter, gpointer vp) { -// NarrativeWindow *nw = vp; -// sc_editor_set_cursor_para(nw->nv, -1); -// pr_clock_set_pos(nw->pr_clock, sc_editor_get_cursor_para(nw->nv), -// sc_editor_get_num_paras(nw->nv)); -// update_toolbar(nw); + NarrativeWindow *nw = vp; + int n_paras = narrative_get_num_items(presentation_get_narrative(nw->p)); + gtk_narrative_view_set_cursor_para(GTK_NARRATIVE_VIEW(nw->nv), -1); + pr_clock_set_pos(nw->pr_clock, + gtk_narrative_view_get_cursor_para(GTK_NARRATIVE_VIEW(nw->nv)), + n_paras); + update_toolbar(nw); } static void open_clock_sig(GSimpleAction *action, GVariant *parameter, gpointer vp) { - //NarrativeWindow *nw = vp; -// nw->pr_clock = pr_clock_new(); + NarrativeWindow *nw = vp; + nw->pr_clock = pr_clock_new(); } @@ -547,7 +557,7 @@ static gboolean nw_key_press_sig(GtkWidget *da, GdkEventKey *event, static gboolean ss_destroy_sig(GtkWidget *da, NarrativeWindow *nw) { nw->show = NULL; - //sc_editor_set_para_highlight(nw->nv, 0); FIXME + gtk_narrative_view_set_para_highlight(GTK_NARRATIVE_VIEW(nw->nv), 0); gtk_widget_set_sensitive(GTK_WIDGET(nw->bfirst), FALSE); gtk_widget_set_sensitive(GTK_WIDGET(nw->bprev), FALSE); @@ -561,74 +571,75 @@ static gboolean ss_destroy_sig(GtkWidget *da, NarrativeWindow *nw) static void start_slideshow_here_sig(GSimpleAction *action, GVariant *parameter, gpointer vp) { - //NarrativeWindow *nw = vp; - //void *bvp; + NarrativeWindow *nw = vp; + Slide *slide; - //if ( num_slides(nw->p) == 0 ) return; + if ( presentation_get_num_slides(nw->p) == 0 ) return; - //bvp = sc_editor_get_cursor_bvp(nw->nv); - //if ( bvp == NULL ) return; + slide = narrative_get_slide(presentation_get_narrative(nw->p), + gtk_narrative_view_get_cursor_para(GTK_NARRATIVE_VIEW(nw->nv))); + if ( slide == NULL ) return; - //nw->show = sc_slideshow_new(nw->p, GTK_APPLICATION(nw->app)); - //if ( nw->show == NULL ) return; + nw->show = sc_slideshow_new(nw->p, GTK_APPLICATION(nw->app)); + if ( nw->show == NULL ) return; - //nw->show_no_slides = 0; + nw->show_no_slides = 0; - //g_signal_connect(G_OBJECT(nw->show), "key-press-event", - // G_CALLBACK(nw_key_press_sig), nw); - //g_signal_connect(G_OBJECT(nw->show), "destroy", - // G_CALLBACK(ss_destroy_sig), nw); - //sc_slideshow_set_slide(nw->show, bvp); - //sc_editor_set_para_highlight(nw->nv, 1); - //gtk_widget_show_all(GTK_WIDGET(nw->show)); - //update_toolbar(nw); + g_signal_connect(G_OBJECT(nw->show), "key-press-event", + G_CALLBACK(nw_key_press_sig), nw); + g_signal_connect(G_OBJECT(nw->show), "destroy", + G_CALLBACK(ss_destroy_sig), nw); + sc_slideshow_set_slide(nw->show, slide); + gtk_narrative_view_set_para_highlight(GTK_NARRATIVE_VIEW(nw->nv), 1); + gtk_widget_show_all(GTK_WIDGET(nw->show)); + update_toolbar(nw); } static void start_slideshow_noslides_sig(GSimpleAction *action, GVariant *parameter, gpointer vp) { - //NarrativeWindow *nw = vp; + NarrativeWindow *nw = vp; - //if ( num_slides(nw->p) == 0 ) return; + if ( presentation_get_num_slides(nw->p) == 0 ) return; - //nw->show = sc_slideshow_new(nw->p, GTK_APPLICATION(nw->app)); - //if ( nw->show == NULL ) return; + nw->show = sc_slideshow_new(nw->p, GTK_APPLICATION(nw->app)); + if ( nw->show == NULL ) return; - //nw->show_no_slides = 1; + nw->show_no_slides = 1; - //g_signal_connect(G_OBJECT(nw->show), "key-press-event", - // G_CALLBACK(nw_key_press_sig), nw); - //g_signal_connect(G_OBJECT(nw->show), "destroy", - // G_CALLBACK(ss_destroy_sig), nw); - //sc_slideshow_set_slide(nw->show, first_slide(nw->p)); - //sc_editor_set_para_highlight(nw->nv, 1); - //sc_editor_set_cursor_para(nw->nv, 0); - //update_toolbar(nw); + g_signal_connect(G_OBJECT(nw->show), "key-press-event", + G_CALLBACK(nw_key_press_sig), nw); + g_signal_connect(G_OBJECT(nw->show), "destroy", + G_CALLBACK(ss_destroy_sig), nw); + sc_slideshow_set_slide(nw->show, presentation_get_slide_by_number(nw->p, 0)); + gtk_narrative_view_set_para_highlight(GTK_NARRATIVE_VIEW(nw->nv), 1); + gtk_narrative_view_set_cursor_para(GTK_NARRATIVE_VIEW(nw->nv), 0); + update_toolbar(nw); } static void start_slideshow_sig(GSimpleAction *action, GVariant *parameter, gpointer vp) { -// NarrativeWindow *nw = vp; -// -// if ( num_slides(nw->p) == 0 ) return; -// -// nw->show = sc_slideshow_new(nw->p, GTK_APPLICATION(nw->app)); -// if ( nw->show == NULL ) return; -// -// nw->show_no_slides = 0; -// -// g_signal_connect(G_OBJECT(nw->show), "key-press-event", -// G_CALLBACK(nw_key_press_sig), nw); -// g_signal_connect(G_OBJECT(nw->show), "destroy", -// G_CALLBACK(ss_destroy_sig), nw); -// sc_slideshow_set_slide(nw->show, first_slide(nw->p)); -// sc_editor_set_para_highlight(nw->nv, 1); -// sc_editor_set_cursor_para(nw->nv, 0); -// gtk_widget_show_all(GTK_WIDGET(nw->show)); -// update_toolbar(nw); + NarrativeWindow *nw = vp; + + if ( presentation_get_num_slides(nw->p) == 0 ) return; + + nw->show = sc_slideshow_new(nw->p, GTK_APPLICATION(nw->app)); + if ( nw->show == NULL ) return; + + nw->show_no_slides = 0; + + g_signal_connect(G_OBJECT(nw->show), "key-press-event", + G_CALLBACK(nw_key_press_sig), nw); + g_signal_connect(G_OBJECT(nw->show), "destroy", + G_CALLBACK(ss_destroy_sig), nw); + sc_slideshow_set_slide(nw->show, presentation_get_slide_by_number(nw->p, 0)); + gtk_narrative_view_set_para_highlight(GTK_NARRATIVE_VIEW(nw->nv), 1); + gtk_narrative_view_set_cursor_para(GTK_NARRATIVE_VIEW(nw->nv), 0); + gtk_widget_show_all(GTK_WIDGET(nw->show)); + update_toolbar(nw); } diff --git a/src/pr_clock.c b/src/pr_clock.c new file mode 100644 index 0000000..aa1348e --- /dev/null +++ b/src/pr_clock.c @@ -0,0 +1,439 @@ +/* + * pr_clock.c + * + * Copyright © 2013-2018 Thomas White + * + * This file is part of Colloquium. + * + * Colloquium is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#define _(x) gettext(x) + +#include "presentation.h" +#include "pr_clock.h" + + +struct pr_clock +{ + int open; + + GtkWidget *window; + GtkWidget *entry; + GtkWidget *startbutton; + GtkWidget *da; + GtkWidget *wallclock; + GtkWidget *elapsed; + GtkWidget *remaining; + GtkWidget *status; + GTimeZone *tz; + + GDateTime *start; + double time_elapsed_at_start; + guint timer_id; + + int running; + double time_allowed; + double time_elapsed; + int pos; + int end; + int pos_reached; + + double t; + double tf; +}; + + +static char *format_span(int n) +{ + char tmp[32]; + int hours, mins, sec; + char *s; + + if ( n < 0 ) { + s = "-"; + n = -n; + } else { + s = ""; + } + + sec = n % 60; + mins = ((n-sec) % (60*60))/60; + hours = (n-sec-mins) / (60*60); + + snprintf(tmp, 31, "%s%i:%02i:%02i", s, hours, mins, sec); + + return strdup(tmp); +} + + +static char *format_span_nice(int n) +{ + char tmp[64]; + int hours, mins, sec; + char *s; + + if ( n < 0 ) { + s = "behind"; + n = -n; + } else { + s = "ahead"; + } + + sec = n % 60; + mins = ((n-sec) % (60*60))/60; + hours = (n-sec-mins) / (60*60); + + if ( n <= 60 ) { + snprintf(tmp, 63, "%i seconds %s", n, s); + return strdup(tmp); + } + + if ( n < 60*60 ) { + snprintf(tmp, 63, "%i min %i seconds %s", mins, sec, s); + return strdup(tmp); + } + + snprintf(tmp, 63, "%i hours, %i min, %i seconds %s", + hours, mins, sec, s); + return strdup(tmp); +} + + +static gboolean update_clock(gpointer data) +{ + struct pr_clock *n = data; + gchar *d; + GDateTime *dt; + GTimeSpan sp; + double time_remaining; + double delta; + gint w, h; + char *tmp; + + if ( !n->open ) { + g_date_time_unref(n->start); + g_time_zone_unref(n->tz); + free(n); + return FALSE; + } + + dt = g_date_time_new_now(n->tz); + + if ( n->running ) { + + sp = g_date_time_difference(dt, n->start); + n->time_elapsed = n->time_elapsed_at_start + + sp / G_TIME_SPAN_SECOND; + + time_remaining = n->time_allowed - n->time_elapsed; + + tmp = format_span(n->time_elapsed); + gtk_label_set_text(GTK_LABEL(n->elapsed), tmp); + free(tmp); + + tmp = format_span(time_remaining); + gtk_label_set_text(GTK_LABEL(n->remaining), tmp); + free(tmp); + + } else { + + n->time_elapsed = n->time_elapsed_at_start; + + time_remaining = n->time_allowed - n->time_elapsed; + + tmp = format_span(n->time_elapsed); + gtk_label_set_text(GTK_LABEL(n->elapsed), tmp); + free(tmp); + + tmp = format_span(time_remaining); + gtk_label_set_text(GTK_LABEL(n->remaining), tmp); + free(tmp); + + } + + d = g_date_time_format(dt, "%H:%M:%S"); + g_date_time_unref(dt); + + gtk_label_set_text(GTK_LABEL(n->wallclock), d); + free(d); + + n->t = n->time_elapsed / n->time_allowed; + + if ( n->time_allowed == 0.0 ) n->t = 0.0; + if ( n->time_elapsed > n->time_allowed ) n->t = 1.0; + + if ( n->end > 0 ) { + n->tf = (double)n->pos_reached / (n->end-1); + } else { + n->tf = 0.0; + } + + delta = (n->tf - n->t)*n->time_allowed; + tmp = format_span_nice(delta); + gtk_label_set_text(GTK_LABEL(n->status), tmp); + free(tmp); + + w = gtk_widget_get_allocated_width(GTK_WIDGET(n->da)); + h = gtk_widget_get_allocated_height(GTK_WIDGET(n->da)); + gtk_widget_queue_draw_area(n->da, 0, 0, w, h); + + return TRUE; +} + + +void pr_clock_set_pos(PRClock *n, int pos, int end) +{ + if ( n == NULL ) return; + n->pos = pos; + if ( n->pos > n->pos_reached ) { + n->pos_reached = pos; + } + n->end = end; + update_clock(n); +} + + +static gint close_clock_sig(GtkWidget *w, PRClock *n) +{ + g_source_remove(n->timer_id); + free(n); + return FALSE; +} + + +static gboolean clock_draw_sig(GtkWidget *da, cairo_t *cr, struct pr_clock *n) +{ + int width, height; + double s; + double ff; + + width = gtk_widget_get_allocated_width(GTK_WIDGET(da)); + height = gtk_widget_get_allocated_height(GTK_WIDGET(da)); + s = width-20.0; + + /* Overall background */ + cairo_rectangle(cr, 10.0, 0.0, s, height); + cairo_set_source_rgb(cr, 1.0, 1.0, 1.0); + cairo_fill(cr); + + cairo_rectangle(cr, 10.0, 0.0, s*n->t, height); + cairo_set_source_rgb(cr, 0.0, 0.0, 1.0); + cairo_fill(cr); + + if ( n->tf > n->t ) { + cairo_rectangle(cr, 10.0+s*n->t, 0.0, (n->tf - n->t)*s, height); + cairo_set_source_rgb(cr, 0.0, 1.0, 0.0); + cairo_fill(cr); + } else { + cairo_rectangle(cr, 10.0+s*n->t, 0.0, (n->tf - n->t)*s, height); + cairo_set_source_rgb(cr, 1.0, 0.0, 0.0); + cairo_fill(cr); + } + + ff = (double)n->pos / (n->end-1); + if ( n->end == 1 ) ff = 0.0; + cairo_move_to(cr, 10.0+ff*s, 0.0); + cairo_line_to(cr, 10.0+ff*s, height); + cairo_set_line_width(cr, 2.0); + cairo_set_source_rgb(cr, 1.0, 0.0, 1.0); + cairo_stroke(cr); + + if ( !n->running ) { + cairo_move_to(cr, 10.0, height*0.8); + cairo_set_font_size(cr, height*0.8); + cairo_select_font_face(cr, "sans-serif", + CAIRO_FONT_SLANT_NORMAL, + CAIRO_FONT_WEIGHT_BOLD); + cairo_set_source_rgb(cr, 0.0, 0.0, 0.0); + cairo_show_text(cr, _("Timer is NOT running!")); + } + + return FALSE; +} + + +static void set_sig(GtkEditable *w, struct pr_clock *n) +{ + const gchar *t; + char *check; + + t = gtk_entry_get_text(GTK_ENTRY(n->entry)); + n->time_allowed = 60.0 * strtod(t, &check); + if ( check == t ) { + fprintf(stderr, "Invalid time '%s'\n", t); + n->time_allowed = 0.0; + } + + update_clock(n); +} + + +static gboolean reset_sig(GtkWidget *w, gpointer data) +{ + struct pr_clock *n = data; + + n->time_elapsed = 0; + n->time_elapsed_at_start = 0; + + if ( n->start != NULL ) { + g_date_time_unref(n->start); + } + + n->start = g_date_time_new_now(n->tz); + + update_clock(n); + + return FALSE; +} + + +static gboolean setpos_sig(GtkWidget *w, gpointer data) +{ + struct pr_clock *n = data; + n->pos_reached = n->pos; + update_clock(n); + return FALSE; +} + + +static gboolean start_sig(GtkWidget *w, gpointer data) +{ + struct pr_clock *n = data; + + if ( n->running ) { + n->running = 0; + n->time_elapsed_at_start = n->time_elapsed; + gtk_label_set_text(GTK_LABEL(gtk_bin_get_child(GTK_BIN(w))), + _("Start")); + } else { + n->time_elapsed_at_start = n->time_elapsed; + if ( n->start != NULL ) { + g_date_time_unref(n->start); + } + n->start = g_date_time_new_now(n->tz); + n->running = 1; + gtk_label_set_text(GTK_LABEL(gtk_bin_get_child(GTK_BIN(w))), + _("Stop")); + } + + update_clock(n); + + return FALSE; +} + + +PRClock *pr_clock_new() +{ + struct pr_clock *n; + GtkWidget *vbox; + GtkWidget *hbox; + GtkWidget *resetbutton; + GtkWidget *setposbutton; + GtkWidget *grid; + GtkWidget *label; + + n = malloc(sizeof(struct pr_clock)); + if ( n == NULL ) return NULL; + n->open = 1; + + n->tz = g_time_zone_new_local(); + + n->window = gtk_window_new(GTK_WINDOW_TOPLEVEL); + gtk_window_set_default_size(GTK_WINDOW(n->window), 600, 150); + + vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0); + gtk_container_add(GTK_CONTAINER(n->window), vbox); + + hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0); + gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 10); + + label = gtk_label_new(_("Length (mins):")); + gtk_label_set_markup(GTK_LABEL(label), _("Length (mins):")); + g_object_set(G_OBJECT(label), "halign", GTK_ALIGN_END, NULL); + gtk_box_pack_start(GTK_BOX(hbox), label, TRUE, TRUE, 10); + + n->entry = gtk_entry_new(); + gtk_box_pack_start(GTK_BOX(hbox), n->entry, TRUE, TRUE, 0); + + n->startbutton = gtk_button_new_with_label(_("Start")); + gtk_box_pack_start(GTK_BOX(hbox), n->startbutton, TRUE, TRUE, 10); + + resetbutton = gtk_button_new_with_label(_("Reset")); + gtk_box_pack_start(GTK_BOX(hbox), resetbutton, TRUE, TRUE, 10); + + setposbutton = gtk_button_new_with_label(_("Set position")); + gtk_box_pack_start(GTK_BOX(hbox), setposbutton, TRUE, TRUE, 10); + + n->da = gtk_drawing_area_new(); + gtk_box_pack_start(GTK_BOX(vbox), n->da, TRUE, TRUE, 0); + g_signal_connect(G_OBJECT(n->da), "draw", G_CALLBACK(clock_draw_sig), n); + g_signal_connect(G_OBJECT(n->window), "destroy", + G_CALLBACK(close_clock_sig), n); /* FIXME: Uniqueness */ + + grid = gtk_grid_new(); + gtk_grid_set_row_spacing(GTK_GRID(grid), 10); + gtk_grid_set_column_homogeneous(GTK_GRID(grid), TRUE); + gtk_box_pack_start(GTK_BOX(vbox), grid, FALSE, FALSE, 10); + label = gtk_label_new(_("Time elapsed")); + gtk_label_set_markup(GTK_LABEL(label), _("Time elapsed")); + gtk_grid_attach(GTK_GRID(grid), label, 0, 0, 1, 1); + label = gtk_label_new(_("Time remaining")); + gtk_label_set_markup(GTK_LABEL(label), _("Time remaining")); + gtk_grid_attach(GTK_GRID(grid), label, 1, 0, 1, 1); + n->status = gtk_label_new(""); + gtk_grid_attach(GTK_GRID(grid), n->status, 2, 0, 1, 1); + n->elapsed = gtk_label_new(""); + gtk_grid_attach(GTK_GRID(grid), n->elapsed, 0, 1, 1, 1); + n->remaining = gtk_label_new(""); + gtk_grid_attach(GTK_GRID(grid), n->remaining, 1, 1, 1, 1); + n->wallclock = gtk_label_new(""); + gtk_grid_attach(GTK_GRID(grid), n->wallclock, 2, 1, 1, 1); + + g_signal_connect(G_OBJECT(n->startbutton), "clicked", + G_CALLBACK(start_sig), n); + g_signal_connect(G_OBJECT(resetbutton), "clicked", + G_CALLBACK(reset_sig), n); + g_signal_connect(G_OBJECT(setposbutton), "clicked", + G_CALLBACK(setpos_sig), n); + g_signal_connect(G_OBJECT(n->entry), "changed", + G_CALLBACK(set_sig), n); + + n->running = 0; + n->time_allowed = 0; + n->time_elapsed = 0; + n->time_elapsed_at_start = 0; + n->pos = 0; + n->pos_reached = 0; + n->end = 0; + n->start = NULL; + update_clock(n); + n->timer_id = g_timeout_add_seconds(1, update_clock, n); + + gtk_window_set_title(GTK_WINDOW(n->window), _("Presentation clock")); + + gtk_widget_show_all(n->window); + return n; +} diff --git a/src/pr_clock.h b/src/pr_clock.h new file mode 100644 index 0000000..97d2d0d --- /dev/null +++ b/src/pr_clock.h @@ -0,0 +1,37 @@ +/* + * pr_clock.h + * + * Copyright © 2013-2018 Thomas White + * + * This file is part of Colloquium. + * + * Colloquium is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#ifndef CLOCK_H +#define CLOCK_H + +#ifdef HAVE_CONFIG_H +#include +#endif + +typedef struct pr_clock PRClock; + +extern PRClock *pr_clock_new(void); + +extern void pr_clock_set_pos(PRClock *n, int pos, int end); + + +#endif /* CLOCK_H */ diff --git a/src/slideshow.c b/src/slideshow.c new file mode 100644 index 0000000..1bd1930 --- /dev/null +++ b/src/slideshow.c @@ -0,0 +1,222 @@ +/* + * slideshow.c + * + * Copyright © 2013-2018 Thomas White + * + * This file is part of Colloquium. + * + * Colloquium is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#define _(x) gettext(x) + +#include + +#include "slide_render_cairo.h" +#include "slideshow.h" +#include "colloquium.h" +#include "pr_clock.h" + +G_DEFINE_TYPE_WITH_CODE(SCSlideshow, sc_slideshow, GTK_TYPE_WINDOW, NULL) + + +static void sc_slideshow_init(SCSlideshow *ss) +{ +} + + +void sc_slideshow_class_init(SCSlideshowClass *klass) +{ +} + + +static void redraw(SCSlideshow *ss) +{ + gint w, h; + w = gtk_widget_get_allocated_width(GTK_WIDGET(ss->drawingarea)); + h = gtk_widget_get_allocated_height(GTK_WIDGET(ss->drawingarea)); + gtk_widget_queue_draw_area(ss->drawingarea, 0, 0, w, h); +} + + +static gint ssh_destroy_sig(GtkWidget *widget, SCSlideshow *ss) +{ + if ( ss->blank_cursor != NULL ) { + g_object_unref(ss->blank_cursor); + } + if ( ss->inhibit_cookie ) { + gtk_application_uninhibit(ss->app, ss->inhibit_cookie); + } + return FALSE; +} + + +static gboolean ss_draw_sig(GtkWidget *da, cairo_t *cr, SCSlideshow *ss) +{ + double dw, dh; /* Size of drawing area */ + double lw, lh; /* Logical size of slide */ + double sw, sh; /* Size of slide on screen */ + double xoff, yoff; + + dw = gtk_widget_get_allocated_width(GTK_WIDGET(da)); + dh = gtk_widget_get_allocated_height(GTK_WIDGET(da)); + + /* Overall background */ + cairo_rectangle(cr, 0.0, 0.0, dw, dh); + cairo_set_source_rgb(cr, 0.0, 0.0, 0.0); + cairo_fill(cr); + + slide_get_logical_size(ss->cur_slide, + presentation_get_stylesheet(ss->p), &lw, &lh); + + if ( lw/lh > (double)dw/dh ) { + /* Slide is too wide. Letterboxing top/bottom */ + sw = dw; + sh = dw * lh/lw; + } else { + /* Letterboxing at sides */ + sw = dh * lw/lh; + sh = dh; + } + + xoff = (dw - sw)/2.0; + yoff = (dh - sh)/2.0; + + if ( !ss->blank ) { + + PangoContext *pc; + int n; + struct slide_pos sel; + + cairo_save(cr); + cairo_translate(cr, xoff, yoff); + cairo_scale(cr, sw/lw, sh/lh); + + sel.para = 0; sel.pos = 0; sel.trail = 0; + n = presentation_get_slide_number(ss->p, ss->cur_slide); + pc = pango_cairo_create_context(cr); + + slide_render_cairo(ss->cur_slide, cr, + presentation_get_imagestore(ss->p), + presentation_get_stylesheet(ss->p), + n, pango_language_get_default(), pc, + NULL, sel, sel); + + g_object_unref(pc); + cairo_restore(cr); + + } + + return FALSE; +} + + +static gboolean ss_realize_sig(GtkWidget *w, SCSlideshow *ss) +{ + if ( (ss->app == NULL) || colloquium_get_hidepointer(COLLOQUIUM(ss->app)) ) { + + /* Hide the pointer */ + GdkWindow *win; + win = gtk_widget_get_window(w); + ss->blank_cursor = gdk_cursor_new_for_display(gdk_display_get_default(), + GDK_BLANK_CURSOR); + gdk_window_set_cursor(GDK_WINDOW(win), ss->blank_cursor); + + } else { + ss->blank_cursor = NULL; + } + + return FALSE; +} + + +void sc_slideshow_set_slide(SCSlideshow *ss, Slide *ns) +{ + ss->cur_slide = ns; + redraw(ss); +} + + +SCSlideshow *sc_slideshow_new(Presentation *p, GtkApplication *app) +{ + GdkDisplay *display; + int n_monitors; + SCSlideshow *ss; + + ss = g_object_new(SC_TYPE_SLIDESHOW, NULL); + if ( ss == NULL ) return NULL; + + ss->blank = 0; + ss->p = p; + ss->cur_slide = NULL; + ss->blank_cursor = NULL; + ss->app = app; + + ss->drawingarea = gtk_drawing_area_new(); + gtk_container_add(GTK_CONTAINER(ss), ss->drawingarea); + + gtk_widget_set_can_focus(GTK_WIDGET(ss->drawingarea), TRUE); + gtk_widget_add_events(GTK_WIDGET(ss->drawingarea), + GDK_KEY_PRESS_MASK); + + g_signal_connect(G_OBJECT(ss), "destroy", + G_CALLBACK(ssh_destroy_sig), ss); + g_signal_connect(G_OBJECT(ss), "realize", + G_CALLBACK(ss_realize_sig), ss); + g_signal_connect(G_OBJECT(ss->drawingarea), "draw", + G_CALLBACK(ss_draw_sig), ss); + + gtk_widget_grab_focus(GTK_WIDGET(ss->drawingarea)); + + display = gdk_display_get_default(); + n_monitors = gdk_display_get_n_monitors(display); + + GdkMonitor *mon_ss; + if ( n_monitors == 1 ) { + mon_ss = gdk_display_get_primary_monitor(display); + printf(_("Single monitor mode\n")); + ss->single_monitor = 1; + } else { + mon_ss = gdk_display_get_monitor(display, 1); + printf(_("Dual monitor mode\n")); + ss->single_monitor = 0; + } + + /* Workaround because gtk_window_fullscreen_on_monitor doesn't work */ + GdkRectangle rect; + gdk_monitor_get_geometry(mon_ss, &rect); + gtk_window_move(GTK_WINDOW(ss), rect.x, rect.y); + gtk_window_fullscreen(GTK_WINDOW(ss)); + + if ( app != NULL ) { + ss->inhibit_cookie = gtk_application_inhibit(app, GTK_WINDOW(ss), + GTK_APPLICATION_INHIBIT_IDLE, + _("Presentation slide show is running")); + } + + return ss; +} + diff --git a/src/slideshow.h b/src/slideshow.h new file mode 100644 index 0000000..777b9f2 --- /dev/null +++ b/src/slideshow.h @@ -0,0 +1,79 @@ +/* + * slideshow.h + * + * Copyright © 2013-2019 Thomas White + * + * This file is part of Colloquium. + * + * Colloquium is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#ifndef SLIDESHOW_H +#define SLIDESHOW_H + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include + +#define SC_TYPE_SLIDESHOW (sc_slideshow_get_type()) + +#define SC_SLIDESHOW(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), \ + SC_TYPE_SLIDESHOW, SCEditor)) + +#define SC_IS_SLIDESHOW(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), \ + SC_TYPE_SLIDESHOW)) + +#define SC_SLIDESHOW_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((obj), \ + SC_TYPE_SLIDESHOW, SCEditorClass)) + +#define SC_IS_SLIDESHOW_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((obj), \ + SC_TYPE_SLIDESHOW)) + +#define SC_SLIDESHOW_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), \ + SC_TYPE_SLIDESHOW, SCSlideShowClass)) + +struct _scslideshow +{ + GtkWindow parent_instance; + + /* */ + Presentation *p; + Slide *cur_slide; + GtkWidget *drawingarea; + GdkCursor *blank_cursor; + int blank; + int xoff; + int yoff; + int single_monitor; + GtkApplication *app; + gint inhibit_cookie; +}; + + +struct _scslideshowclass +{ + GtkWindowClass parent_class; +}; + +typedef struct _scslideshow SCSlideshow; +typedef struct _scslideshowclass SCSlideshowClass; + +extern SCSlideshow *sc_slideshow_new(Presentation *p, GtkApplication *app); +extern void sc_slideshow_set_slide(SCSlideshow *ss, Slide *ns); +extern Slide *sc_slideshow_get_slide(SCSlideshow *ss); + +#endif /* SLIDESHOW_H */ -- cgit v1.2.3