talons

Fork of Claws Mail https://www.claws-mail
Log | Files | Refs | README | LICENSE

commit 0dbb415b36c512aa23ef1eb71d48cb7d78c72be5
parent eba3ff6f6f06cf91fe0016a706073696d3839531
Author: Colin Leroy <colin@colino.net>
Date:   Thu, 22 May 2014 20:57:01 +0200

Fix Undo after wrapping a paragraph, which undid every line wrap one by one, and make it atomic.

Diffstat:
Msrc/compose.c | 5+++++
Msrc/undo.c | 201++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------------
Msrc/undo.h | 4+++-
3 files changed, 171 insertions(+), 39 deletions(-)

diff --git a/src/compose.c b/src/compose.c @@ -4695,9 +4695,14 @@ static void compose_wrap_all_full(Compose *compose, gboolean force) buffer = gtk_text_view_get_buffer(text); gtk_text_buffer_get_start_iter(buffer, &iter); + + undo_wrapping(compose->undostruct, TRUE); + while (!gtk_text_iter_is_end(&iter) && modified) modified = compose_beautify_paragraph(compose, &iter, force); + undo_wrapping(compose->undostruct, FALSE); + } static void compose_set_title(Compose *compose) diff --git a/src/undo.c b/src/undo.c @@ -46,6 +46,15 @@ struct _UndoInfo gint mergeable; }; +struct _UndoWrap +{ + gint lock; + gchar *pre_wrap_content; + gint start_pos; + gint end_pos; + gint len_change; +}; + static void undo_free_list (GList **list_pointer); static void undo_check_size (UndoMain *undostruct); static gint undo_merge (GList *list, @@ -526,10 +535,147 @@ void undo_unblock(UndoMain *undostruct) undostruct); } +/* Init the WrapInfo structure */ +static void init_wrap_undo(UndoMain *undostruct) +{ + GtkTextBuffer *buffer; + GtkTextIter start, end; + + cm_return_if_fail(undostruct != NULL); + cm_return_if_fail(undostruct->wrap_info == NULL); + + undostruct->wrap_info = g_new0(UndoWrap, 1); + + /* Save the whole buffer as original contents. We'll retain the + * changed region when exiting wrap mode. + */ + buffer = gtk_text_view_get_buffer(undostruct->textview); + gtk_text_buffer_get_start_iter(buffer, &start); + gtk_text_buffer_get_end_iter(buffer, &end); + undostruct->wrap_info->pre_wrap_content + = gtk_text_buffer_get_text(buffer, &start, &end, FALSE); + + undostruct->wrap_info->lock = 0; + + /* start_pos == -1 means nothing changed yet. */ + undostruct->wrap_info->start_pos = -1; + undostruct->wrap_info->end_pos = -1; + undostruct->wrap_info->len_change = 0; +} + +static void end_wrap_undo(UndoMain *undostruct) +{ + GtkTextBuffer *buffer; + GtkTextIter start, end; + gchar *old_contents = NULL; + gchar *cur_contents = NULL; + gchar *new_contents = NULL; + + cm_return_if_fail(undostruct != NULL); + cm_return_if_fail(undostruct->wrap_info != NULL); + + /* If start_pos is still == -1, it means nothing changed. */ + if (undostruct->wrap_info->start_pos == -1) + goto cleanup; + + /* get the whole new (wrapped) contents */ + buffer = gtk_text_view_get_buffer(undostruct->textview); + gtk_text_buffer_get_start_iter(buffer, &start); + gtk_text_buffer_get_end_iter(buffer, &end); + cur_contents = gtk_text_buffer_get_text(buffer, &start, &end, FALSE); + + debug_print("wrapping done from %d to %d, len change: %d\n", + undostruct->wrap_info->start_pos, + undostruct->wrap_info->end_pos, + undostruct->wrap_info->len_change); + + /* keep the relevant old unwrapped part, which is what + * was between start_pos & end_pos - len_change + */ + old_contents = g_utf8_substring( + undostruct->wrap_info->pre_wrap_content, + undostruct->wrap_info->start_pos, + undostruct->wrap_info->end_pos + - undostruct->wrap_info->len_change); + + /* and get the changed contents, from start_pos to end_pos. */ + new_contents = g_utf8_substring( + cur_contents, + undostruct->wrap_info->start_pos, + undostruct->wrap_info->end_pos); + + /* add the deleted (unwrapped) text to the undo pile */ + undo_add(old_contents, + undostruct->wrap_info->start_pos, + undostruct->wrap_info->end_pos + - undostruct->wrap_info->len_change, + UNDO_ACTION_REPLACE_DELETE, + undostruct); + + /* add the inserted (wrapped) text to the undo pile */ + undo_add(new_contents, + undostruct->wrap_info->start_pos, + undostruct->wrap_info->end_pos, + UNDO_ACTION_REPLACE_INSERT, + undostruct); + + g_free(old_contents); + g_free(cur_contents); + g_free(new_contents); +cleanup: + g_free(undostruct->wrap_info->pre_wrap_content); + g_free(undostruct->wrap_info); + undostruct->wrap_info = NULL; +} + +static void update_wrap_undo(UndoMain *undostruct, const gchar *text, int start, + int end, UndoAction action) +{ + gint len = end - start; + + /* If we don't yet have a start position, or farther than + * current, store it. + */ + if (undostruct->wrap_info->start_pos == -1 + || start < undostruct->wrap_info->start_pos) { + undostruct->wrap_info->start_pos = start; + } + + if (action == UNDO_ACTION_INSERT) { + /* If inserting, the end of the region is at the end of the + * change, and the total length of the changed region + * increases. + */ + undostruct->wrap_info->end_pos = end; + undostruct->wrap_info->len_change += len; + } else if (action == UNDO_ACTION_DELETE) { + /* If deleting, the end of the region is at the start of the + * change, and the total length of the changed region + * decreases. + */ + undostruct->wrap_info->end_pos = start; + undostruct->wrap_info->len_change -= len; + } +} + +/* Set wrapping mode, in which changes are agglomerated until + * the end of wrapping mode. + */ void undo_wrapping(UndoMain *undostruct, gboolean wrap) { -// debug_print("undo wrapping now %d\n", wrap); - undostruct->wrap = wrap; + if (wrap) { + /* Start (or go deeper in) wrapping mode */ + if (undostruct->wrap_info == NULL) + init_wrap_undo(undostruct); + undostruct->wrap_info->lock++; + } else if (undostruct->wrap_info != NULL) { + /* exit (& possible stop) one level of wrapping mode */ + undostruct->wrap_info->lock--; + if (undostruct->wrap_info->lock == 0) + end_wrap_undo(undostruct); + } else { + g_warning("undo already out of wrap mode"); + } } void undo_insert_text_cb(GtkTextBuffer *textbuf, GtkTextIter *iter, @@ -538,29 +684,22 @@ void undo_insert_text_cb(GtkTextBuffer *textbuf, GtkTextIter *iter, { gchar *text_to_insert; gint pos; + glong utf8_len; + if (prefs_common.undolevels <= 0) return; pos = gtk_text_iter_get_offset(iter); - if (undostruct->wrap && undostruct->undo) { - UndoInfo *last_undo = undostruct->undo->data; - if (last_undo && (last_undo->action == UNDO_ACTION_INSERT - || last_undo->action == UNDO_ACTION_REPLACE_INSERT) - && last_undo->start_pos < pos && last_undo->end_pos > pos) { - GtkTextIter start,end; - last_undo->end_pos += g_utf8_strlen(new_text, -1); - gtk_text_buffer_get_iter_at_offset(textbuf, &start, last_undo->start_pos); - gtk_text_buffer_get_iter_at_offset(textbuf, &end, last_undo->end_pos); - g_free(last_undo->text); - last_undo->text = gtk_text_buffer_get_text(textbuf, &start, &end, FALSE); - debug_print("add:undo upd %d-%d\n", last_undo->start_pos, last_undo->end_pos); - return; - } else if (last_undo) - debug_print("add:last: %d, %d-%d (%d)\n", last_undo->action, - last_undo->start_pos, last_undo->end_pos, pos); - } Xstrndup_a(text_to_insert, new_text, new_text_length, return); - debug_print("add:undo add %d-%ld\n", pos, pos + g_utf8_strlen(text_to_insert, -1)); - undo_add(text_to_insert, pos, pos + g_utf8_strlen(text_to_insert, -1), + utf8_len = g_utf8_strlen(text_to_insert, -1); + + if (undostruct->wrap_info != NULL) { + update_wrap_undo(undostruct, text_to_insert, + pos, pos + utf8_len, UNDO_ACTION_INSERT); + return; + } + + debug_print("add:undo add %d-%ld\n", pos, utf8_len); + undo_add(text_to_insert, pos, pos + utf8_len, UNDO_ACTION_INSERT, undostruct); } @@ -578,23 +717,9 @@ void undo_delete_text_cb(GtkTextBuffer *textbuf, GtkTextIter *start, start_pos = gtk_text_iter_get_offset(start); end_pos = gtk_text_iter_get_offset(end); - if (undostruct->wrap && undostruct->undo) { - UndoInfo *last_undo = undostruct->undo->data; - if (last_undo && (last_undo->action == UNDO_ACTION_INSERT - || last_undo->action == UNDO_ACTION_REPLACE_INSERT) - && last_undo->start_pos < start_pos && last_undo->end_pos > end_pos) { - GtkTextIter start,end; - last_undo->end_pos -= g_utf8_strlen(text_to_delete, -1); - gtk_text_buffer_get_iter_at_offset(textbuf, &start, last_undo->start_pos); - gtk_text_buffer_get_iter_at_offset(textbuf, &end, last_undo->end_pos); - g_free(last_undo->text); - last_undo->text = gtk_text_buffer_get_text(textbuf, &start, &end, FALSE); - debug_print("del:undo upd %d-%d\n", last_undo->start_pos, last_undo->end_pos); - return; - } else if (last_undo) - debug_print("del:last: %d, %d-%d (%d)\n", last_undo->action, - last_undo->start_pos, last_undo->end_pos, start_pos); - + if (undostruct->wrap_info != NULL) { + update_wrap_undo(undostruct, text_to_delete, start_pos, end_pos, UNDO_ACTION_DELETE); + return; } debug_print("del:undo add %d-%d\n", start_pos, end_pos); undo_add(text_to_delete, start_pos, end_pos, UNDO_ACTION_DELETE, diff --git a/src/undo.h b/src/undo.h @@ -42,6 +42,7 @@ typedef enum } UndoState; typedef struct _UndoMain UndoMain; +typedef struct _UndoWrap UndoWrap; typedef void (*UndoChangeStateFunc) (UndoMain *undostruct, gint undo_state, @@ -62,7 +63,8 @@ struct _UndoMain gboolean redo_state : 1; gint paste; - gint wrap; + + UndoWrap *wrap_info; }; UndoMain *undo_init (GtkWidget *text);