aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorThomas White <taw@bitwiz.org.uk>2017-01-25 00:01:45 +0100
committerThomas White <taw@bitwiz.org.uk>2017-01-25 00:11:37 +0100
commitc921eed7f3a10b6e403d5d5863df82c652ca5db9 (patch)
tree1dafdca921ff731f43b6049a97656713cc7d7cfe /src
parentd6afe0601a4d3e35ab492074b7fcde5bfec70875 (diff)
Implement text selection
Diffstat (limited to 'src')
-rw-r--r--src/frame.c80
-rw-r--r--src/frame.h19
-rw-r--r--src/render.c4
-rw-r--r--src/sc_editor.c100
-rw-r--r--src/sc_editor.h8
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 <taw@bitwiz.org.uk>
+ * Copyright © 2013-2017 Thomas White <taw@bitwiz.org.uk>
*
* 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; i<fr->n_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 <taw@bitwiz.org.uk>
+ * Copyright © 2013-2017 Thomas White <taw@bitwiz.org.uk>
*
* 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 <taw@bitwiz.org.uk>
+ * Copyright © 2013-2017 Thomas White <taw@bitwiz.org.uk>
*
* 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; i<fr->n_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 <taw@bitwiz.org.uk>
+ * Copyright © 2014-2017 Thomas White <taw@bitwiz.org.uk>
*
* 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;