From c921eed7f3a10b6e403d5d5863df82c652ca5db9 Mon Sep 17 00:00:00 2001 From: Thomas White Date: Wed, 25 Jan 2017 00:01:45 +0100 Subject: Implement text selection --- src/frame.c | 80 +++++++++++++++++++++++++++++++++++---------- src/frame.h | 19 +++++++++-- src/render.c | 4 +-- src/sc_editor.c | 100 ++++++++++++++++++++++++++++++++++++++++++++++++++++---- src/sc_editor.h | 8 +++-- 5 files changed, 180 insertions(+), 31 deletions(-) diff --git a/src/frame.c b/src/frame.c index d654965..36a7983 100644 --- a/src/frame.c +++ b/src/frame.c @@ -1,7 +1,7 @@ /* * frame.c * - * Copyright © 2013-2016 Thomas White + * Copyright © 2013-2017 Thomas White * * This file is part of Colloquium. * @@ -270,7 +270,8 @@ struct frame *find_frame_with_scblocks(struct frame *fr, SCBlock *scblocks) } -void wrap_paragraph(Paragraph *para, PangoContext *pc, double w) +void wrap_paragraph(Paragraph *para, PangoContext *pc, double w, + size_t sel_start, size_t sel_end) { size_t total_len = 0; int i; @@ -327,6 +328,15 @@ void wrap_paragraph(Paragraph *para, PangoContext *pc, double w) } + /* Add attributes for selected text */ + if ( sel_start > 0 || sel_end > 0 ) { + PangoAttribute *attr; + attr = pango_attr_background_new(42919, 58853, 65535); + attr->start_index = sel_start; + attr->end_index = sel_end; + pango_attr_list_insert(attrs, attr); + } + if ( para->layout == NULL ) { para->layout = pango_layout_new(pc); } @@ -593,10 +603,30 @@ static size_t text_para_pos(Paragraph *para, double x, double y, int *ptrail) } -int find_cursor(struct frame *fr, double x, double y, - int *ppara, size_t *ppos, int *ptrail) +void sort_positions(struct edit_pos *a, struct edit_pos *b) +{ + if ( a->para > b->para ) { + size_t tpos; + int tpara, ttrail; + tpara = b->para; tpos = b->pos; ttrail = b->trail; + b->para = a->para; b->pos = a->pos; b->trail = a->trail; + a->para = tpara; a->pos = tpos; a->trail = ttrail; + } + + if ( (a->para == b->para) && (a->pos > b->pos) ) + { + size_t tpos = b->pos; + int ttrail = b->trail; + b->pos = a->pos; b->trail = a->trail; + a->pos = tpos; a->trail = ttrail; + } +} + + +int find_cursor_2(struct frame *fr, double x, double y, + struct edit_pos *pos) { - double pos = fr->pad_t; + double pad = fr->pad_t; int i; if ( fr == NULL ) { @@ -605,20 +635,20 @@ int find_cursor(struct frame *fr, double x, double y, } for ( i=0; in_paras; i++ ) { - double npos = pos + fr->paras[i]->height; + double npos = pad + fr->paras[i]->height; if ( npos > y ) { - *ppara = i; + pos->para = i; if ( fr->paras[i]->type == PARA_TYPE_TEXT ) { - *ppos = text_para_pos(fr->paras[i], + pos->pos = text_para_pos(fr->paras[i], x - fr->pad_l - fr->paras[i]->space[0], - y - pos - fr->paras[i]->space[2], - ptrail); + y - pad - fr->paras[i]->space[2], + &pos->trail); } else { - *ppos = 0; + pos->pos = 0; } return 0; } - pos = npos; + pad = npos; } if ( fr->n_paras == 0 ) { @@ -627,10 +657,24 @@ int find_cursor(struct frame *fr, double x, double y, } /* Pretend it's in the last paragraph */ - pos -= fr->paras[fr->n_paras-1]->height; - *ppara = fr->n_paras - 1; - *ppos = text_para_pos(fr->paras[fr->n_paras-1], - x - fr->pad_l, y - pos, ptrail); + pad -= fr->paras[fr->n_paras-1]->height; + pos->para = fr->n_paras - 1; + pos->pos = text_para_pos(fr->paras[fr->n_paras-1], + x - fr->pad_l, y - pad, &pos->trail); + return 0; +} + + +int find_cursor(struct frame *fr, double x, double y, + int *ppara, size_t *ppos, int *ptrail) +{ + struct edit_pos p; + int r; + r = find_cursor_2(fr, x, y, &p); + if ( r ) return r; + *ppara = p.para; + *ppos = p.pos; + *ptrail = p.trail; return 0; } @@ -1092,8 +1136,8 @@ static SCBlock *split_text_paragraph(struct frame *fr, int pn, size_t pos, pnew->open = para->open; para->open = 0; - wrap_paragraph(para, pc, fr->w - fr->pad_l - fr->pad_r); - wrap_paragraph(pnew, pc, fr->w - fr->pad_l - fr->pad_r); + wrap_paragraph(para, pc, fr->w - fr->pad_l - fr->pad_r, 0, 0); + wrap_paragraph(pnew, pc, fr->w - fr->pad_l - fr->pad_r, 0, 0); return sc_block_next(rr->scblock); } diff --git a/src/frame.h b/src/frame.h index 4f8be33..787a2ec 100644 --- a/src/frame.h +++ b/src/frame.h @@ -1,7 +1,7 @@ /* * frame.h * - * Copyright © 2013-2016 Thomas White + * Copyright © 2013-2017 Thomas White * * This file is part of Colloquium. * @@ -58,6 +58,15 @@ enum para_type typedef struct _paragraph Paragraph; + +struct edit_pos +{ + int para; + size_t pos; + int trail; +}; + + struct frame { struct frame **children; @@ -135,13 +144,19 @@ extern void add_image_para(struct frame *fr, SCBlock *scblock, const char *filename, double w, double h, int editable); -extern void wrap_paragraph(Paragraph *para, PangoContext *pc, double w); +extern void wrap_paragraph(Paragraph *para, PangoContext *pc, double w, + size_t sel_start, size_t sel_end); extern size_t end_offset_of_para(struct frame *fr, int pn); extern int find_cursor(struct frame *fr, double x, double y, int *ppara, size_t *ppos, int *ptrail); +extern int find_cursor_2(struct frame *fr, double x, double y, + struct edit_pos *pos); + +extern void sort_positions(struct edit_pos *a, struct edit_pos *b); + extern int get_para_highlight(struct frame *fr, int cursor_para, double *cx, double *cy, double *cw, double *ch); diff --git a/src/render.c b/src/render.c index a8ff7ea..f87c5b9 100644 --- a/src/render.c +++ b/src/render.c @@ -1,7 +1,7 @@ /* * render.c * - * Copyright © 2013-2016 Thomas White + * Copyright © 2013-2017 Thomas White * * This file is part of Colloquium. * @@ -151,7 +151,7 @@ void wrap_frame(struct frame *fr, PangoContext *pc) w = fr->w - fr->pad_l - fr->pad_r; for ( i=0; in_paras; i++ ) { - wrap_paragraph(fr->paras[i], pc, w); + wrap_paragraph(fr->paras[i], pc, w, 0, 0); } } diff --git a/src/sc_editor.c b/src/sc_editor.c index fe52ce8..4d9a817 100644 --- a/src/sc_editor.c +++ b/src/sc_editor.c @@ -675,7 +675,7 @@ static void insert_text(char *t, SCEditor *e) insert_text_in_paragraph(para, off, t); wrap_paragraph(para, NULL, e->cursor_frame->w - e->cursor_frame->pad_l - - e->cursor_frame->pad_r); + - e->cursor_frame->pad_r, 0, 0); if ( e->flow ) update_size(e); cursor_moveh(e->cursor_frame, &e->cursor_para, @@ -733,7 +733,8 @@ static void do_backspace(struct frame *fr, SCEditor *e) &e->cursor_trail, -1); if ( e->cursor_para != old_para ) { merge_paragraphs(e->cursor_frame, e->cursor_para); - wrap_paragraph(e->cursor_frame->paras[new_para], NULL, wrapw); + wrap_paragraph(e->cursor_frame->paras[new_para], NULL, wrapw, + 0, 0); } else { size_t offs_new, offs_old; @@ -743,7 +744,7 @@ static void do_backspace(struct frame *fr, SCEditor *e) offs_old = pos_trail_to_offset(para, old_pos, old_trail); delete_text_in_paragraph(para, offs_new, offs_old); - wrap_paragraph(para, NULL, wrapw); + wrap_paragraph(para, NULL, wrapw, 0, 0); } @@ -962,7 +963,69 @@ static void check_paragraph(struct frame *fr, PangoContext *pc, } add_run(para, scblocks, NULL, 0, 0, fr->fontdesc, fr->col); - wrap_paragraph(para, pc, fr->w - fr->pad_l - fr->pad_r); + wrap_paragraph(para, pc, fr->w - fr->pad_l - fr->pad_r, 0, 0); +} + + +static void rewrap_paragraph_range(struct frame *fr, int a, int b, + struct edit_pos sel_start, + struct edit_pos sel_end, + int sel_active) +{ + int i; + int sel_s, sel_e; + Paragraph *para; + + if ( a > b ) { + int t = a; + a = b; b = t; + } + + if ( fr->paras == NULL ) return; + + sort_positions(&sel_start, &sel_end); + + para = fr->paras[sel_start.para]; + sel_s = pos_trail_to_offset(para, sel_start.pos, sel_start.trail); + para = fr->paras[sel_end.para]; + sel_e = pos_trail_to_offset(para, sel_end.pos, sel_end.trail); + + for ( i=a; i<=b; i++ ) { + size_t srt, end; + if ( sel_active ) { + if ( i == sel_start.para ) { + srt = sel_s; + } else { + srt = 0; + } + if ( i == sel_end.para ) { + end = sel_e; + } else { + end = G_MAXUINT; + } + if ( i > sel_start.para && i < sel_end.para ) { + end = G_MAXUINT; + } + } else { + srt = 0; + end = 0; + } + wrap_paragraph(fr->paras[i], NULL, + fr->w - fr->pad_l - fr->pad_r, srt, end); + } +} + + +static void unset_selection(SCEditor *e) +{ + int a, b; + a = e->sel_start.para; + b = e->sel_end.para; + if ( a > b ) { + a = e->sel_end.para; + b = e->sel_start.para; + } + rewrap_paragraph_range(e->top, a, b, e->sel_start, e->sel_end, 0); } @@ -1029,6 +1092,12 @@ static gboolean button_press_sig(GtkWidget *da, GdkEventButton *event, if ( fr->resizable ) { e->drag_status = DRAG_STATUS_COULD_DRAG; e->drag_reason = DRAG_REASON_MOVE; + } else { + e->drag_status = DRAG_STATUS_COULD_DRAG; + e->drag_reason = DRAG_REASON_TEXTSEL; + unset_selection(e); + find_cursor_2(clicked, x-fr->x, y-fr->y, + &e->sel_start); } } @@ -1047,8 +1116,11 @@ static gboolean button_press_sig(GtkWidget *da, GdkEventButton *event, } else { /* Clicked an existing frame, no immediate dragging */ - e->drag_status = DRAG_STATUS_NONE; - e->drag_reason = DRAG_REASON_NONE; + e->drag_status = DRAG_STATUS_COULD_DRAG; + e->drag_reason = DRAG_REASON_TEXTSEL; + unset_selection(e); + find_cursor_2(clicked, x-clicked->x, y-clicked->y, + &e->sel_start); e->selection = clicked; e->cursor_frame = clicked; if ( clicked == e->top ) { @@ -1075,7 +1147,7 @@ static gboolean motion_sig(GtkWidget *da, GdkEventMotion *event, gdouble x, y; x = event->x - e->border_offs_x; - y = event->y - e->border_offs_y; + y = event->y - e->border_offs_y + e->scroll_pos; if ( e->drag_status == DRAG_STATUS_COULD_DRAG ) { @@ -1113,6 +1185,16 @@ static gboolean motion_sig(GtkWidget *da, GdkEventMotion *event, sc_editor_redraw(e); break; + case DRAG_REASON_TEXTSEL : + unset_selection(e); + find_cursor_2(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->cursor_para, + &e->cursor_pos, &e->cursor_trail); + sc_editor_redraw(e); + break; + } gdk_event_request_motions(event); @@ -1237,6 +1319,10 @@ static gboolean button_release_sig(GtkWidget *da, GdkEventButton *event, do_resize(e, e->box_x, e->box_y, e->box_width, e->box_height); break; + case DRAG_REASON_TEXTSEL : + /* Do nothing (text is already selected) */ + break; + } e->drag_reason = DRAG_REASON_NONE; diff --git a/src/sc_editor.h b/src/sc_editor.h index e5e415d..4b55e0e 100644 --- a/src/sc_editor.h +++ b/src/sc_editor.h @@ -1,7 +1,7 @@ /* * sc_editor.h * - * Copyright © 2014-2016 Thomas White + * Copyright © 2014-2017 Thomas White * * This file is part of Colloquium. * @@ -59,7 +59,8 @@ enum drag_reason DRAG_REASON_CREATE, DRAG_REASON_IMPORT, DRAG_REASON_RESIZE, - DRAG_REASON_MOVE + DRAG_REASON_MOVE, + DRAG_REASON_TEXTSEL }; @@ -142,6 +143,9 @@ struct _sceditor enum drag_reason drag_reason; enum drag_status drag_status; enum corner drag_corner; + int sel_active; + struct edit_pos sel_start; /* Where the user dragged from */ + struct edit_pos sel_end; /* Stuff to do with drag and drop import of "content" */ int drag_preview_pending; -- cgit v1.2.3