[evolution/449-support-markdown-in-composer] EMarkdownEditor: Implement search & replace
- From: Milan Crha <mcrha src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [evolution/449-support-markdown-in-composer] EMarkdownEditor: Implement search & replace
- Date: Fri, 11 Feb 2022 07:23:31 +0000 (UTC)
commit 8132e4d77dceee633bec3be70bfe62a743c1f745
Author: Milan Crha <mcrha redhat com>
Date: Fri Feb 11 08:23:04 2022 +0100
EMarkdownEditor: Implement search & replace
src/e-util/e-markdown-editor.c | 307 +++++++++++++++++++++++++++++++++++++++++
1 file changed, 307 insertions(+)
---
diff --git a/src/e-util/e-markdown-editor.c b/src/e-util/e-markdown-editor.c
index 160d7c5a24..1c9950b839 100644
--- a/src/e-util/e-markdown-editor.c
+++ b/src/e-util/e-markdown-editor.c
@@ -485,17 +485,224 @@ e_markdown_editor_select_all (EContentEditor *cnt_editor)
g_signal_emit_by_name (self->priv->text_view, "select-all", 0, TRUE, NULL);
}
+static gunichar *
+e_markdown_editor_prepare_search_text (const gchar *text,
+ guint32 *flags)
+{
+ gunichar *search_text;
+ guint ii;
+
+ if (!text || !*text)
+ return NULL;
+
+ /* Fine-tune the direction flags:
+ forward & next = forward
+ forward & previous = backward
+ backward & next = backward
+ backward & previous = forward
+ */
+ if ((*flags & E_CONTENT_EDITOR_FIND_MODE_BACKWARDS) == 0 &&
+ (*flags & E_CONTENT_EDITOR_FIND_PREVIOUS) != 0) {
+ *flags = ((*flags) & ~(E_CONTENT_EDITOR_FIND_MODE_BACKWARDS | E_CONTENT_EDITOR_FIND_PREVIOUS
| E_CONTENT_EDITOR_FIND_NEXT)) |
+ E_CONTENT_EDITOR_FIND_MODE_BACKWARDS;
+ } else if ((*flags & E_CONTENT_EDITOR_FIND_MODE_BACKWARDS) != 0 &&
+ (*flags & E_CONTENT_EDITOR_FIND_PREVIOUS) != 0) {
+ *flags = ((*flags) & ~(E_CONTENT_EDITOR_FIND_MODE_BACKWARDS | E_CONTENT_EDITOR_FIND_PREVIOUS
| E_CONTENT_EDITOR_FIND_NEXT));
+ }
+
+ search_text = g_utf8_to_ucs4 (text, -1, NULL, NULL, NULL);
+
+ if (search_text && (*flags & E_CONTENT_EDITOR_FIND_MODE_BACKWARDS) != 0) {
+ guint len;
+
+ for (len = 0; search_text[len]; len++) {
+ /* Just count them */
+ }
+
+ if (len) {
+ len--;
+
+ /* Swap the letters backwards */
+ for (ii = 0; ii < len; ii++) {
+ gunichar chr = search_text[ii];
+ search_text[ii] = search_text[len];
+ search_text[len] = chr;
+ len--;
+ }
+ }
+ }
+
+ if (search_text && (*flags & E_CONTENT_EDITOR_FIND_CASE_INSENSITIVE) != 0) {
+ for (ii = 0; search_text[ii]; ii++) {
+ search_text[ii] = g_unichar_tolower (search_text[ii]);
+ }
+ }
+
+ return search_text;
+}
+
+static gboolean
+e_markdown_editor_do_search_text (GtkTextBuffer *buffer,
+ const gunichar *search_text,
+ guint32 flags,
+ gboolean *did_wrap_around,
+ const GtkTextIter *from_iter,
+ const GtkTextIter *limit, /* used only after wrap around */
+ GtkTextIter *out_occur_start,
+ GtkTextIter *out_occur_end)
+{
+ GtkTextIter iter, from_cursor;
+ gboolean case_insensitive = (flags & E_CONTENT_EDITOR_FIND_CASE_INSENSITIVE) != 0;
+ gboolean wrap_around = (!*did_wrap_around) && (flags & E_CONTENT_EDITOR_FIND_WRAP_AROUND) != 0;
+ gboolean backwards = (flags & E_CONTENT_EDITOR_FIND_MODE_BACKWARDS) != 0;
+ gboolean found = FALSE;
+
+ if (from_iter) {
+ iter = *from_iter;
+ } else {
+ gtk_text_buffer_get_selection_bounds (buffer, &from_cursor, &iter);
+
+ if (!backwards)
+ from_cursor = iter;
+
+ if (!limit)
+ limit = &from_cursor;
+
+ iter = from_cursor;
+ }
+
+ if (backwards && !gtk_text_iter_backward_char (&iter)) {
+ if (wrap_around) {
+ wrap_around = FALSE;
+ gtk_text_buffer_get_end_iter (buffer, &iter);
+ if (!gtk_text_iter_backward_char (&iter))
+ return FALSE;
+ } else {
+ return FALSE;
+ }
+ }
+
+ while (!found) {
+ gunichar chr;
+
+ chr = gtk_text_iter_get_char (&iter);
+
+ if (chr) {
+ if ((case_insensitive && g_unichar_tolower (chr) == search_text[0]) ||
+ (!case_insensitive && chr == search_text[0])) {
+ GtkTextIter next = iter;
+ guint ii;
+
+ for (ii = 1; !found; ii++) {
+ if (!search_text[ii]) {
+ /* To have selected also the last character */
+ if (backwards)
+ gtk_text_iter_forward_char (&iter);
+ else
+ gtk_text_iter_forward_char (&next);
+
+ found = TRUE;
+ if (backwards) {
+ *out_occur_start = next;
+ *out_occur_end = iter;
+ } else {
+ *out_occur_start = iter;
+ *out_occur_end = next;
+ }
+ break;
+ }
+
+ if ((backwards && !gtk_text_iter_backward_char (&next)) ||
+ (!backwards && !gtk_text_iter_forward_char (&next)))
+ break;
+
+ if (*did_wrap_around && !gtk_text_iter_compare (&iter, limit))
+ break;
+
+ chr = gtk_text_iter_get_char (&next);
+
+ if (!chr)
+ break;
+
+ if ((case_insensitive && g_unichar_tolower (chr) == search_text[ii])
||
+ (!case_insensitive && chr == search_text[ii])) {
+ /* matched the next letter */
+ } else {
+ break;
+ }
+ }
+
+ if (found)
+ break;
+ }
+
+ if (!found && *did_wrap_around && !gtk_text_iter_compare (&iter, limit))
+ break;
+ }
+
+ if ((backwards && !gtk_text_iter_backward_char (&iter)) ||
+ (!backwards && !gtk_text_iter_forward_char (&iter))) {
+ if (!wrap_around)
+ break;
+
+ *did_wrap_around = TRUE;
+ wrap_around = FALSE;
+
+ if (backwards)
+ gtk_text_buffer_get_end_iter (buffer, &iter);
+ else
+ gtk_text_buffer_get_start_iter (buffer, &iter);
+
+ if (!gtk_text_iter_compare (&iter, limit))
+ break;
+ }
+ }
+
+ return found;
+}
+
static void
e_markdown_editor_find (EContentEditor *cnt_editor,
guint32 flags,
const gchar *text)
{
+ EMarkdownEditor *self = E_MARKDOWN_EDITOR (cnt_editor);
+ GtkTextBuffer *buffer;
+ GtkTextIter occur_start, occur_end;
+ gboolean did_wrap_around = FALSE;
+ gunichar *search_text;
+
+ search_text = e_markdown_editor_prepare_search_text (text, &flags);
+
+ if (!search_text) {
+ e_content_editor_emit_find_done (cnt_editor, 0);
+ return;
+ }
+
+ buffer = gtk_text_view_get_buffer (self->priv->text_view);
+
+ if (e_markdown_editor_do_search_text (buffer, search_text, flags, &did_wrap_around, NULL, NULL,
&occur_start, &occur_end)) {
+ gtk_text_buffer_select_range (buffer, &occur_start, &occur_end);
+ e_content_editor_emit_find_done (cnt_editor, 1);
+ } else {
+ e_content_editor_emit_find_done (cnt_editor, 0);
+ }
+
+ g_free (search_text);
}
static void
e_markdown_editor_replace (EContentEditor *cnt_editor,
const gchar *replacement)
{
+ EMarkdownEditor *self = E_MARKDOWN_EDITOR (cnt_editor);
+ GtkTextBuffer *buffer;
+ GtkTextIter start, end;
+
+ buffer = gtk_text_view_get_buffer (self->priv->text_view);
+ gtk_text_buffer_get_selection_bounds (buffer, &start, &end);
+ gtk_text_buffer_delete (buffer, &start, &end);
+ gtk_text_buffer_insert_at_cursor (buffer, replacement, -1);
}
static void
@@ -504,6 +711,78 @@ e_markdown_editor_replace_all (EContentEditor *cnt_editor,
const gchar *find_text,
const gchar *replace_with)
{
+ EMarkdownEditor *self = E_MARKDOWN_EDITOR (cnt_editor);
+ GtkTextBuffer *buffer;
+ GtkTextIter occur_start, occur_end, limit, last_replace;
+ gboolean did_wrap_around = FALSE;
+ gunichar *search_text;
+ guint count = 0, replace_len = 0;
+
+ search_text = e_markdown_editor_prepare_search_text (find_text, &flags);
+
+ if (!search_text) {
+ e_content_editor_emit_replace_all_done (cnt_editor, 0);
+ return;
+ }
+
+ buffer = gtk_text_view_get_buffer (self->priv->text_view);
+ gtk_text_buffer_get_selection_bounds (buffer, &occur_start, &occur_end);
+
+ /* Different bound than in search, to replace also current match */
+ if ((flags & E_CONTENT_EDITOR_FIND_MODE_BACKWARDS) != 0)
+ limit = occur_end;
+ else
+ limit = occur_start;
+
+ if (replace_with)
+ replace_len = g_utf8_strlen (replace_with, -1);
+
+ last_replace = limit;
+
+ while (e_markdown_editor_do_search_text (buffer, search_text, flags, &did_wrap_around, &last_replace,
&limit, &occur_start, &occur_end)) {
+ GtkTextMark *mark;
+ gboolean last_match;
+
+ last_match = did_wrap_around && !gtk_text_iter_compare (&occur_start, &limit);
+
+ if (last_match && !(flags & E_CONTENT_EDITOR_FIND_MODE_BACKWARDS))
+ break;
+
+ /* Remember where the limit was... */
+ mark = gtk_text_buffer_create_mark (buffer, NULL, &limit, !(flags &
E_CONTENT_EDITOR_FIND_MODE_BACKWARDS));
+
+ gtk_text_buffer_delete (buffer, &occur_start, &occur_end);
+
+ last_replace = occur_start;
+
+ if (replace_with && *replace_with) {
+ gtk_text_buffer_insert (buffer, &occur_start, replace_with, -1);
+
+ /* Get on the first letter of the replaced word */
+ if ((flags & E_CONTENT_EDITOR_FIND_MODE_BACKWARDS) != 0 &&
+ !gtk_text_iter_backward_chars (&occur_start, replace_len)) {
+ break;
+ }
+
+ last_replace = occur_start;
+ }
+
+ /* ... then restore the limit, after the buffer changed (which invalidated the iterator) */
+ gtk_text_buffer_get_iter_at_mark (buffer, &limit, mark);
+ gtk_text_buffer_delete_mark (buffer, mark);
+
+ count++;
+
+ if (last_match)
+ break;
+ }
+
+ g_free (search_text);
+
+ if (count)
+ gtk_text_buffer_select_range (buffer, &last_replace, &last_replace);
+
+ e_content_editor_emit_replace_all_done (cnt_editor, count);
}
static void
@@ -689,6 +968,32 @@ e_markdown_editor_insert_signature (EContentEditor *cnt_editor,
return g_strdup (self->priv->signature_uid);
}
+static void
+e_markdown_editor_on_dialog_open (EContentEditor *cnt_editor,
+ const gchar *name)
+{
+ if (g_strcmp0 (name, E_CONTENT_EDITOR_DIALOG_REPLACE) == 0) {
+ EMarkdownEditor *self = E_MARKDOWN_EDITOR (cnt_editor);
+ GtkTextBuffer *buffer;
+
+ buffer = gtk_text_view_get_buffer (self->priv->text_view);
+ gtk_text_buffer_begin_user_action (buffer);
+ }
+}
+
+static void
+e_markdown_editor_on_dialog_close (EContentEditor *cnt_editor,
+ const gchar *name)
+{
+ if (g_strcmp0 (name, E_CONTENT_EDITOR_DIALOG_REPLACE) == 0) {
+ EMarkdownEditor *self = E_MARKDOWN_EDITOR (cnt_editor);
+ GtkTextBuffer *buffer;
+
+ buffer = gtk_text_view_get_buffer (self->priv->text_view);
+ gtk_text_buffer_end_user_action (buffer);
+ }
+}
+
static gboolean
e_markdown_editor_can_copy (EMarkdownEditor *self)
{
@@ -1858,6 +2163,8 @@ e_markdown_editor_content_editor_init (EContentEditorInterface *iface)
iface->selection_restore = e_markdown_editor_selection_restore;
iface->get_current_signature_uid = e_markdown_editor_get_current_signature_uid;
iface->insert_signature = e_markdown_editor_insert_signature;
+ iface->on_dialog_open = e_markdown_editor_on_dialog_open;
+ iface->on_dialog_close = e_markdown_editor_on_dialog_close;
}
static void
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]