From 153125149d217d866d6347f76f65345db46257c2 Mon Sep 17 00:00:00 2001 From: defanor Date: Sun, 25 Aug 2019 09:30:35 +0300 Subject: Merge search and selection This reduces code and state duplication, though it may be useful to refactor them further, possibly using a model similar to Emacs marks. --- src/documentbox.c | 150 ++++++++++++++++++++++++++++-------------------------- src/documentbox.h | 6 +-- src/inlinebox.c | 36 +++++-------- src/inlinebox.h | 3 +- 4 files changed, 96 insertions(+), 99 deletions(-) diff --git a/src/documentbox.c b/src/documentbox.c index f8a8e2b..0f4d1df 100644 --- a/src/documentbox.c +++ b/src/documentbox.c @@ -187,58 +187,56 @@ static gint compare_positions(GtkAllocation *a1, guint i1, } } - +/* Updates InlineBox widgets to render selection appropriately. */ static void selection_update (GtkWidget *widget, SelectionState *st) { - GtkAllocation alloc; + if (st->selection_start == NULL || + st->selection_end == NULL || + st->selection_prev == NULL) { + return; + } + GtkAllocation alloc, alloc_start, alloc_end, alloc_prev; gtk_widget_get_allocation(widget, &alloc); - if (widget_is_affected(&alloc, &st->selection_start->alloc, - &st->selection_end->alloc) || - widget_is_affected(&alloc, &st->selection_start->alloc, - &st->selection_prev->alloc) || - widget_is_affected(&alloc, &st->selection_end->alloc, - &st->selection_prev->alloc)) { + gtk_widget_get_allocation(GTK_WIDGET(st->selection_start), &alloc_start); + gtk_widget_get_allocation(GTK_WIDGET(st->selection_end), &alloc_end); + gtk_widget_get_allocation(GTK_WIDGET(st->selection_prev), &alloc_prev); + if (widget_is_affected(&alloc, &alloc_start, &alloc_end) || + widget_is_affected(&alloc, &alloc_start, &alloc_prev) || + widget_is_affected(&alloc, &alloc_end, &alloc_prev)) { if (IS_INLINE_BOX(widget)) { InlineBox *ib = INLINE_BOX(widget); ib->selection_end = 0; ib->selection_start = 0; - GList *ti; - guint text_position = 0; - - for (ti = ib->children; ti; ti = ti->next) { - if (IS_IB_TEXT(ti->data)) { - IBText *ibt = IB_TEXT(ti->data); - gint direction = compare_positions(&st->selection_start->alloc, - st->selection_start_index, - &st->selection_end->alloc, - st->selection_end_index); - if (direction == -1) { - if (st->selection_start == ibt) { - ib->selection_start = st->selection_start_index + text_position; - st->selecting = TRUE; - } - if (st->selecting && st->selection_end == ibt) { - ib->selection_end = st->selection_end_index + text_position; - st->selecting = FALSE; - } - } else if (direction == 1) { - if (st->selection_end == ibt) { - ib->selection_start = st->selection_end_index + text_position; - st->selecting = TRUE; - } - if (st->selecting && st->selection_start == ibt) { - ib->selection_end = st->selection_start_index + text_position; - st->selecting = FALSE; - } - } - text_position += strlen(pango_layout_get_text(ibt->layout)); - gtk_widget_queue_draw (widget); + gint direction = compare_positions(&alloc_start, + st->selection_start_index, + &alloc_end, + st->selection_end_index); + if (direction == -1) { + if (st->selection_start == ib) { + ib->selection_start = st->selection_start_index; + st->selecting = TRUE; + } + if (st->selecting && st->selection_end == ib) { + ib->selection_end = st->selection_end_index; + st->selecting = FALSE; + } + } else if (direction == 1) { + if (st->selection_end == ib) { + ib->selection_start = st->selection_end_index; + st->selecting = TRUE; + } + if (st->selecting && st->selection_start == ib) { + ib->selection_end = st->selection_start_index; + st->selecting = FALSE; } } + if (st->selecting) { - ib->selection_end = text_position; + ib->selection_end = inline_box_get_text_length(ib); } + + gtk_widget_queue_draw (widget); } else if (GTK_IS_CONTAINER(widget)) { gtk_container_foreach(GTK_CONTAINER(widget), (GtkCallback)selection_update, st); @@ -352,12 +350,12 @@ button_press_event_cb (GtkWidget *widget, selection_update(widget, &db->sel); } - if (ss.ibt) { + if (ss.ib) { db->sel.selection_active = TRUE; - db->sel.selection_start = ss.ibt; - db->sel.selection_start_index = ss.index; - db->sel.selection_end = ss.ibt; - db->sel.selection_end_index = ss.index; + db->sel.selection_start = ss.ib; + db->sel.selection_start_index = ss.ib_index; + db->sel.selection_end = ss.ib; + db->sel.selection_end_index = ss.ib_index; /* todo: grab focus when any non-widget space is clicked, not just texts */ gtk_widget_grab_focus(GTK_WIDGET(db)); @@ -380,11 +378,11 @@ motion_notify_event_cb (GtkWidget *widget, ss.x = ev_orig_x - orig_x + event->x; ss.y = ev_orig_y - orig_y + event->y; text_at_position(widget, &ss); - if (ss.ibt && db->sel.selection_active) { + if (ss.ib && db->sel.selection_active) { db->sel.selection_prev = db->sel.selection_end; db->sel.selection_prev_index = db->sel.selection_end_index; - db->sel.selection_end = ss.ibt; - db->sel.selection_end_index = ss.index; + db->sel.selection_end = ss.ib; + db->sel.selection_end_index = ss.ib_index; db->sel.selecting = FALSE; selection_update(widget, &db->sel); } @@ -495,51 +493,61 @@ DocumentBox *document_box_new () } static void -document_box_search (GtkWidget *widget, TextSearchState *tss) { +document_box_search (GtkWidget *widget, DocumentBox *db) { /* todo: backwards search */ - if (tss->state == FOUND) { + if (db->search.state == FOUND) { return; } - if (tss->state == START && (tss->ib == NULL || GTK_WIDGET(tss->ib) == widget)) { + if (db->search.state == START && + (db->search.ib == NULL || GTK_WIDGET(db->search.ib) == widget)) { /* No previous position or found the widget */ - tss->state = LOOKING; - if (tss->ib != NULL) { - tss->ib->match_start = 0; - tss->ib->match_end = 0; - gtk_widget_queue_draw(GTK_WIDGET(tss->ib)); + db->search.state = LOOKING; + if (db->search.ib != NULL) { + db->sel.selection_prev = db->search.ib; } } - if (tss->state == LOOKING && - (tss->ib == NULL || GTK_WIDGET(tss->ib) != widget)) { - tss->start = 0; - tss->end = -1; + if (db->search.state == LOOKING && + (db->search.ib == NULL || GTK_WIDGET(db->search.ib) != widget)) { + db->search.start = 0; + db->search.end = -1; } - if (tss->state == LOOKING && IS_INLINE_BOX(widget)) { + if (db->search.state == LOOKING && IS_INLINE_BOX(widget)) { InlineBox *ib = INLINE_BOX(widget); - gint pos = inline_box_search(ib, tss->start, tss->end, tss->str); + gint pos = inline_box_search(ib, db->search.start, db->search.end, db->search.str); if (pos != -1) { - tss->state = FOUND; - tss->ib = ib; - tss->start = pos; - tss->end = pos + strlen(tss->str); - ib->match_start = tss->start; - ib->match_end = tss->end; + db->search.state = FOUND; + db->search.ib = ib; + db->search.start = pos; + db->search.end = pos + strlen(db->search.str); + + db->sel.selection_start = db->search.ib; + db->sel.selection_start_index = db->search.start; + db->sel.selection_end = db->search.ib; + db->sel.selection_end_index = db->search.end; + selection_update(widget, &db->sel); gtk_widget_queue_draw(widget); } - } else if (tss->state != FOUND && GTK_IS_CONTAINER(widget)) { + } else if (db->search.state != FOUND && GTK_IS_CONTAINER(widget)) { gtk_container_foreach(GTK_CONTAINER(widget), - (GtkCallback)document_box_search, tss); + (GtkCallback)document_box_search, db); } } gboolean document_box_find (DocumentBox *db, const gchar *str) { + /* Cleanup selection */ + db->sel.selection_prev = db->sel.selection_end; + db->sel.selection_prev_index = db->sel.selection_end_index + 1; + db->sel.selection_end = db->sel.selection_start; + db->sel.selection_end_index = db->sel.selection_start_index; + selection_update(GTK_WIDGET(db), &db->sel); + /* todo: backwards search */ db->search.str = str; db->search.state = START; db->search.end = -1; - document_box_search(GTK_WIDGET(db), &(db->search)); + document_box_search(GTK_WIDGET(db), db); if (db->search.state == FOUND) { gtk_widget_grab_focus(GTK_WIDGET(db->search.ib)); return TRUE; diff --git a/src/documentbox.h b/src/documentbox.h index 64a2320..37b2909 100644 --- a/src/documentbox.h +++ b/src/documentbox.h @@ -36,11 +36,11 @@ typedef struct _DocumentBoxClass DocumentBoxClass; typedef struct _SelectionState SelectionState; struct _SelectionState { - IBText *selection_start; + InlineBox *selection_start; guint selection_start_index; - IBText *selection_end; + InlineBox *selection_end; guint selection_end_index; - IBText *selection_prev; + InlineBox *selection_prev; guint selection_prev_index; gboolean selection_active; gboolean selecting; diff --git a/src/inlinebox.c b/src/inlinebox.c index 7eeaeae..f500114 100644 --- a/src/inlinebox.c +++ b/src/inlinebox.c @@ -165,29 +165,6 @@ inline_box_draw (GtkWidget *widget, sel_width, ibt->alloc.height); gtk_style_context_remove_class(styleCtx, "rubberband"); } - /* duplication here (todo) */ - if (ib->match_start <= text_position + text_len && - ib->match_end >= text_position) { - guint sel_start = ibt->alloc.x, sel_width = ibt->alloc.width; - gint x_pos; - if (ib->match_start > text_position) { - pango_layout_index_to_line_x(ibt->layout, - ib->match_start - text_position, - FALSE, NULL, &x_pos); - sel_start += x_pos / PANGO_SCALE; - sel_width -= x_pos / PANGO_SCALE; - } - if (ib->match_end < text_position + text_len) { - pango_layout_index_to_line_x(ibt->layout, - ib->match_end - text_position, - FALSE, NULL, &x_pos); - sel_width -= ibt->alloc.width - x_pos / PANGO_SCALE; - } - gtk_style_context_add_class(styleCtx, "rubberband"); - gtk_render_background(styleCtx, cr, sel_start, ibt->alloc.y, - sel_width, ibt->alloc.height); - gtk_style_context_remove_class(styleCtx, "rubberband"); - } gtk_render_layout(styleCtx, cr, ibt->alloc.x, ibt->alloc.y, ibt->layout); @@ -624,6 +601,19 @@ inline_box_get_text (InlineBox *ib) return result; } +guint +inline_box_get_text_length (InlineBox *ib) +{ + GList *child; + guint len = 0; + for (child = ib->children; child; child = child->next) { + if (IS_IB_TEXT(child->data)) { + len += strlen(pango_layout_get_text(IB_TEXT(child->data)->layout)); + } + } + return len; +} + gint inline_box_search (InlineBox *ib, guint start, diff --git a/src/inlinebox.h b/src/inlinebox.h index 24c11d3..e3009f2 100644 --- a/src/inlinebox.h +++ b/src/inlinebox.h @@ -101,8 +101,6 @@ struct _InlineBox GObject *focused_object; guint selection_start; guint selection_end; - guint match_start; - guint match_end; gboolean wrap; }; @@ -117,6 +115,7 @@ void inline_box_add_text (InlineBox *container, IBText *text); void inline_box_break (InlineBox *container); gchar *inline_box_get_text (InlineBox *ib); gint inline_box_search (InlineBox *ib, guint start, gint end, const gchar *str); +guint inline_box_get_text_length (InlineBox *ib); G_END_DECLS -- cgit v1.2.3