From a4a3ad0ee82b0731c540e90c3c137a240acb10f2 Mon Sep 17 00:00:00 2001 From: Thomas White Date: Sun, 24 Mar 2019 13:53:03 +0100 Subject: Implement text selection in slides --- libstorycode/gtk/gtkslideview.c | 34 ++++------- libstorycode/narrative_render_cairo.c | 6 +- libstorycode/slide_render_cairo.c | 112 +++++++++++++++++++++++++++++++--- libstorycode/slide_render_cairo.h | 5 +- src/pdfstorycode.c | 6 +- 5 files changed, 131 insertions(+), 32 deletions(-) diff --git a/libstorycode/gtk/gtkslideview.c b/libstorycode/gtk/gtkslideview.c index f80d1b5..e9abfb5 100644 --- a/libstorycode/gtk/gtkslideview.c +++ b/libstorycode/gtk/gtkslideview.c @@ -325,7 +325,8 @@ static gboolean draw_sig(GtkWidget *da, cairo_t *cr, GtkSlideView *e) slide_render_cairo(e->slide, cr, presentation_get_imagestore(e->p), presentation_get_stylesheet(e->p), presentation_get_slide_number(e->p, e->slide), - pango_language_get_default(), pc); + pango_language_get_default(), pc, + e->cursor_frame, e->sel_start, e->sel_end); g_object_unref(pc); /* Editing overlay */ @@ -564,15 +565,6 @@ static int find_cursor(SlideItem *item, Stylesheet *stylesheet, static void unset_selection(GtkSlideView *e) { - int a, b; - - a = e->sel_start.para; - b = e->sel_end.para; - if ( a > b ) { - a = e->sel_end.para; - b = e->sel_start.para; - } - e->sel_start.para = 0; e->sel_start.pos = 0; e->sel_start.trail = 0; @@ -750,12 +742,17 @@ static gboolean motion_sig(GtkWidget *da, GdkEventMotion *event, GtkSlideView *e { gdouble x, y; double frx, fry, frw, frh; + Stylesheet *stylesheet; + double slide_w, slide_h; - x = event->x + e->h_scroll_pos; - y = event->y + e->v_scroll_pos; + x = event->x - e->border_offs_x + e->h_scroll_pos; + y = event->y - e->border_offs_y + e->v_scroll_pos; x /= e->view_scale; y /= e->view_scale; + stylesheet = presentation_get_stylesheet(e->p); + slide_get_logical_size(e->slide, stylesheet, &slide_w, &slide_h); + if ( e->drag_status == DRAG_STATUS_COULD_DRAG ) { /* We just got a motion signal, and the status was "could drag", @@ -765,9 +762,6 @@ static gboolean motion_sig(GtkWidget *da, GdkEventMotion *event, GtkSlideView *e } if ( e->cursor_frame != NULL ) { - double slide_w, slide_h; - Stylesheet *stylesheet = presentation_get_stylesheet(e->p); - slide_get_logical_size(e->slide, stylesheet, &slide_w, &slide_h); slide_item_get_geom(e->cursor_frame, stylesheet, &frx, &fry, &frw, &frh, slide_w, slide_h); } @@ -803,13 +797,9 @@ static gboolean motion_sig(GtkWidget *da, GdkEventMotion *event, GtkSlideView *e break; case DRAG_REASON_TEXTSEL : - //unset_selection(e); - //find_cursor(fr, x-fr->x, y-fr->y, &e->sel_end); - //rewrap_paragraph_range(fr, e->sel_start.para, e->sel_end.para, - // e->sel_start, e->sel_end, 1); - //find_cursor(fr, x-fr->x, y-fr->y, &e->cpos); - //e->sel_active = !positions_equal(e->sel_start, e->sel_end); - //sc_editor_redraw(e); + find_cursor(e->cursor_frame, stylesheet, x-frx, y-fry, &e->sel_end, slide_w, slide_h); + e->cpos = e->sel_end; + redraw(e); break; } diff --git a/libstorycode/narrative_render_cairo.c b/libstorycode/narrative_render_cairo.c index b4f29cb..6da1db1 100644 --- a/libstorycode/narrative_render_cairo.c +++ b/libstorycode/narrative_render_cairo.c @@ -149,10 +149,13 @@ static cairo_surface_t *render_thumbnail(Slide *s, Stylesheet *ss, ImageStore *i PangoContext *pc; const int rh = 1024; /* "reasonably big" height for slide */ int rw; + struct slide_pos sel; slide_get_logical_size(s, ss, &logical_w, &logical_h); rw = rh*(logical_w/logical_h); + sel.para = 0; sel.pos = 0; sel.trail = 0; + /* Render at a reasonably big size. Rendering to a small surface makes * rounding of text positions (due to font hinting) cause significant * differences between the thumbnail and "normal" rendering. */ @@ -160,7 +163,8 @@ static cairo_surface_t *render_thumbnail(Slide *s, Stylesheet *ss, ImageStore *i cr = cairo_create(full_surf); cairo_scale(cr, (double)rw/logical_w, (double)rh/logical_h); pc = pango_cairo_create_context(cr); - slide_render_cairo(s, cr, is, ss, 0, pango_language_get_default(), pc); + slide_render_cairo(s, cr, is, ss, 0, pango_language_get_default(), pc, + NULL, sel, sel); g_object_unref(pc); cairo_destroy(cr); diff --git a/libstorycode/slide_render_cairo.c b/libstorycode/slide_render_cairo.c index 16be90f..c428fad 100644 --- a/libstorycode/slide_render_cairo.c +++ b/libstorycode/slide_render_cairo.c @@ -38,6 +38,7 @@ #include "narrative.h" #include "stylesheet.h" #include "imagestore.h" +#include "slide_render_cairo.h" #include "slide_priv.h" @@ -63,9 +64,33 @@ static PangoAlignment to_pangoalignment(enum alignment align) } +static int slide_positions_equal(struct slide_pos a, struct slide_pos b) +{ + if ( a.para != b.para ) return 0; + if ( a.pos != b.pos ) return 0; + if ( a.trail != b.trail ) return 0; + return 1; +} + + +static size_t pos_trail_to_offset(SlideItem *item, int para, + size_t offs, int trail) +{ + glong char_offs; + char *ptr; + + char_offs = g_utf8_pointer_to_offset(item->paras[para].text, + item->paras[para].text+offs); + char_offs += trail; + ptr = g_utf8_offset_to_pointer(item->paras[para].text, char_offs); + return ptr - item->paras[para].text; +} + + static void render_text(SlideItem *item, cairo_t *cr, PangoContext *pc, Stylesheet *ss, enum style_element el, - double parent_w, double parent_h) + double parent_w, double parent_h, + struct slide_pos sel_start, struct slide_pos sel_end) { int i; double x, y, w; @@ -77,6 +102,7 @@ static void render_text(SlideItem *item, cairo_t *cr, PangoContext *pc, PangoRectangle rect; PangoFontDescription *fontdesc; PangoAlignment palignment; + size_t sel_s, sel_e; x = lcalc(item->geom.x, parent_w); y = lcalc(item->geom.y, parent_h); @@ -100,6 +126,14 @@ static void render_text(SlideItem *item, cairo_t *cr, PangoContext *pc, palignment = to_pangoalignment(item->align); } + if ( !slide_positions_equal(sel_start, sel_end) ) { + sel_s = pos_trail_to_offset(item, sel_start.para, sel_start.pos, sel_start.trail); + sel_e = pos_trail_to_offset(item, sel_end.para, sel_end.pos, sel_end.trail); + } else { + sel_s = 0; + sel_e = 0; + } + /* FIXME: Apply background */ cairo_save(cr); @@ -108,6 +142,28 @@ static void render_text(SlideItem *item, cairo_t *cr, PangoContext *pc, for ( i=0; in_paras; i++ ) { + PangoAttrList *attrs; + + size_t srt, end; + if ( i >= sel_start.para && i <= sel_end.para ) { + if ( i == sel_start.para ) { + srt = sel_s; + } else { + srt = 0; + } + if ( i == sel_end.para ) { + end = sel_e; + } else { + end = G_MAXUINT; + } + if ( i > sel_start.para && i < sel_end.para ) { + end = G_MAXUINT; + } + } else { + srt = 0; + end = 0; + } + if ( item->paras[i].layout == NULL ) { item->paras[i].layout = pango_layout_new(pc); } @@ -119,9 +175,20 @@ static void render_text(SlideItem *item, cairo_t *cr, PangoContext *pc, pango_layout_set_font_description(item->paras[i].layout, fontdesc); + attrs = pango_attr_list_new(); + + if ( srt > 0 || end > 0 ) { + PangoAttribute *attr; + attr = pango_attr_background_new(42919, 58853, 65535); + attr->start_index = srt; + attr->end_index = end; + pango_attr_list_insert(attrs, attr); + } + /* FIXME: Handle *bold*, _underline_, /italic/ etc. */ - //pango_layout_set_attributes(item->layouts[i], attrs); - //pango_attr_list_unref(attrs); + + pango_layout_set_attributes(item->paras[i].layout, attrs); + pango_attr_list_unref(attrs); /* FIXME: Clip to w,h */ @@ -174,8 +241,29 @@ static void render_image(SlideItem *item, cairo_t *cr, } +static void sort_slide_positions(struct slide_pos *a, struct slide_pos *b) +{ + if ( a->para > b->para ) { + size_t tpos; + int tpara, ttrail; + tpara = b->para; tpos = b->pos; ttrail = b->trail; + b->para = a->para; b->pos = a->pos; b->trail = a->trail; + a->para = tpara; a->pos = tpos; a->trail = ttrail; + } + + if ( (a->para == b->para) && (a->pos > b->pos) ) + { + size_t tpos = b->pos; + int ttrail = b->trail; + b->pos = a->pos; b->trail = a->trail; + a->pos = tpos; a->trail = ttrail; + } +} + + int slide_render_cairo(Slide *s, cairo_t *cr, ImageStore *is, Stylesheet *stylesheet, - int slide_number, PangoLanguage *lang, PangoContext *pc) + int slide_number, PangoLanguage *lang, PangoContext *pc, + SlideItem *sel_item, struct slide_pos sel_start, struct slide_pos sel_end) { int i, r; enum gradient bg; @@ -188,6 +276,7 @@ int slide_render_cairo(Slide *s, cairo_t *cr, ImageStore *is, Stylesheet *styles if ( r ) return 1; slide_get_logical_size(s, stylesheet, &w, &h); + sort_slide_positions(&sel_start, &sel_end); /* Overall background */ cairo_rectangle(cr, 0.0, 0.0, w, h); @@ -220,11 +309,20 @@ int slide_render_cairo(Slide *s, cairo_t *cr, ImageStore *is, Stylesheet *styles for ( i=0; in_items; i++ ) { + struct slide_pos srt, end; + + if ( &s->items[i] != sel_item ) { + srt.para = 0; srt.pos = 0; srt.trail = 0; + end.para = 0; end.pos = 0; end.trail = 0; + } else { + srt = sel_start; end = sel_end; + } + switch ( s->items[i].type ) { case SLIDE_ITEM_TEXT : render_text(&s->items[i], cr, pc, stylesheet, STYEL_SLIDE_TEXT, - w, h); + w, h, srt, end); break; case SLIDE_ITEM_IMAGE : @@ -234,12 +332,12 @@ int slide_render_cairo(Slide *s, cairo_t *cr, ImageStore *is, Stylesheet *styles case SLIDE_ITEM_SLIDETITLE : render_text(&s->items[i], cr, pc, stylesheet, STYEL_SLIDE_SLIDETITLE, - w, h); + w, h, srt, end); break; case SLIDE_ITEM_PRESTITLE : render_text(&s->items[i], cr, pc, stylesheet, STYEL_SLIDE_PRESTITLE, - w, h); + w, h, srt, end); break; default : diff --git a/libstorycode/slide_render_cairo.h b/libstorycode/slide_render_cairo.h index 52bf43f..cd6e03b 100644 --- a/libstorycode/slide_render_cairo.h +++ b/libstorycode/slide_render_cairo.h @@ -38,6 +38,9 @@ struct slide_pos }; extern int slide_render_cairo(Slide *s, cairo_t *cr, ImageStore *is, Stylesheet *stylesheet, - int slide_number, PangoLanguage *lang, PangoContext *pc); + int slide_number, PangoLanguage *lang, PangoContext *pc, + SlideItem *sel_item, struct slide_pos sel_start, + struct slide_pos sel_end); + #endif /* RENDER_H */ diff --git a/src/pdfstorycode.c b/src/pdfstorycode.c index 566f924..421839d 100644 --- a/src/pdfstorycode.c +++ b/src/pdfstorycode.c @@ -51,6 +51,7 @@ static int render_slides_to_pdf(Presentation *p, ImageStore *is, const char *fil cairo_t *cr; int i; PangoContext *pc; + struct slide_pos sel; surf = cairo_pdf_surface_create(filename, w, w); if ( cairo_surface_status(surf) != CAIRO_STATUS_SUCCESS ) { @@ -61,6 +62,8 @@ static int render_slides_to_pdf(Presentation *p, ImageStore *is, const char *fil cr = cairo_create(surf); pc = pango_cairo_create_context(cr); + sel.para = 0; sel.pos = 0; sel.trail = 0; + for ( i=0; i