/*
* Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
- * Copyright (C) 1999-2009 Hiroyuki Yamamoto and the Claws Mail team
+ * Copyright (C) 1999-2012 Hiroyuki Yamamoto and the Claws Mail team
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
#ifdef HAVE_CONFIG_H
# include "config.h"
+#include "claws-features.h"
#endif
#include <glib.h>
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,
undostruct->change_state_data);
if (undostruct->paste != 0) {
- if (action == UNDO_ACTION_INSERT)
+ if (action == UNDO_ACTION_INSERT)
action = UNDO_ACTION_REPLACE_INSERT;
else
action = UNDO_ACTION_REPLACE_DELETE;
undo_check_size(undostruct);
- vadj = GTK_ADJUSTMENT(GTK_TEXT_VIEW(undostruct->textview)->vadjustment);
+ vadj = GTK_ADJUSTMENT(gtk_text_view_get_vadjustment(
+ GTK_TEXT_VIEW(undostruct->textview)));
undoinfo = undo_object_new(g_strdup(text), start_pos, end_pos, action,
- vadj->value);
+ gtk_adjustment_get_value(vadj));
if (end_pos - start_pos != 1 || text[0] == '\n')
undoinfo->mergeable = FALSE;
/* Move the view (scrollbars) to the correct position */
gtk_adjustment_set_value
- (GTK_ADJUSTMENT(textview->vadjustment),
+ (GTK_ADJUSTMENT(gtk_text_view_get_vadjustment(textview)),
undoinfo->window_position);
switch (undoinfo->action) {
gtk_text_buffer_get_iter_at_offset(buffer, &start_iter, undoinfo->start_pos);
gtk_text_buffer_get_iter_at_offset(buffer, &end_iter, undoinfo->end_pos);
gtk_text_buffer_delete(buffer, &start_iter, &end_iter);
- /* "pull" another data structure from the list */
+ /* "pull" previous matching DELETE data structure from the list */
if (undostruct->undo){
undoinfo = (UndoInfo *)undostruct->undo->data;
undostruct->redo = g_list_prepend(undostruct->redo, undoinfo);
}
break;
case UNDO_ACTION_REPLACE_DELETE:
- g_warning("This should not happen. UNDO_REPLACE_DELETE");
+ gtk_text_buffer_get_iter_at_offset(buffer, &start_iter, undoinfo->start_pos);
+ gtk_text_buffer_insert(buffer, &start_iter, undoinfo->text, -1);
+ /* "pull" previous matching INSERT data structure from the list */
+ if (undostruct->undo){
+ undoinfo = (UndoInfo *)undostruct->undo->data;
+ undostruct->redo = g_list_prepend(undostruct->redo, undoinfo);
+ undostruct->undo = g_list_remove(undostruct->undo, undoinfo);
+ cm_return_if_fail(undoinfo != NULL);
+ cm_return_if_fail(undoinfo->action == UNDO_ACTION_REPLACE_INSERT);
+ gtk_text_buffer_get_iter_at_offset(buffer, &start_iter, undoinfo->start_pos);
+ gtk_text_buffer_get_iter_at_offset(buffer, &end_iter, undoinfo->end_pos);
+ gtk_text_buffer_delete(buffer, &start_iter, &end_iter);
+ }
break;
default:
g_assert_not_reached();
gtk_text_buffer_place_cursor(buffer, &iter);
/* Move the view to the right position. */
- gtk_adjustment_set_value(textview->vadjustment,
+ gtk_adjustment_set_value(gtk_text_view_get_vadjustment(textview),
redoinfo->window_position);
switch (redoinfo->action) {
gtk_text_buffer_get_iter_at_offset(buffer, &end_iter, redoinfo->end_pos);
gtk_text_buffer_delete(buffer, &start_iter, &end_iter);
debug_print("UNDO_ACTION_REPLACE %s\n", redoinfo->text);
- /* "pull" another data structure from the list */
+ /* "pull" previous matching INSERT data structure from the list */
redoinfo = (UndoInfo *)undostruct->redo->data;
cm_return_if_fail(redoinfo != NULL);
undostruct->undo = g_list_prepend(undostruct->undo, redoinfo);
gtk_text_buffer_insert(buffer, &start_iter, redoinfo->text, -1);
break;
case UNDO_ACTION_REPLACE_INSERT:
- /* This is needed only if we redo from a middle-click button */
gtk_text_buffer_get_iter_at_offset(buffer, &iter, redoinfo->start_pos);
gtk_text_buffer_insert(buffer, &iter, redoinfo->text, -1);
+ /* "pull" previous matching DELETE structure from the list */
+ redoinfo = (UndoInfo *)undostruct->redo->data;
+ /* Do nothing if we redo from a middle-click button
+ * and next action is not UNDO_ACTION_REPLACE_DELETE */
+ if (redoinfo && redoinfo->action == UNDO_ACTION_REPLACE_DELETE) {
+ undostruct->undo = g_list_prepend(undostruct->undo, redoinfo);
+ undostruct->redo = g_list_remove(undostruct->redo, redoinfo);
+ gtk_text_buffer_get_iter_at_offset(buffer, &start_iter, redoinfo->start_pos);
+ gtk_text_buffer_get_iter_at_offset(buffer, &end_iter, redoinfo->end_pos);
+ gtk_text_buffer_delete(buffer, &start_iter, &end_iter);
+ }
break;
default:
g_assert_not_reached();
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;
+
+ cm_return_if_fail(undostruct->wrap_info->end_pos > undostruct->wrap_info->start_pos);
+ cm_return_if_fail(undostruct->wrap_info->end_pos - undostruct->wrap_info->len_change > undostruct->wrap_info->start_pos);
+
+ /* 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.
+ */
+ if (end > undostruct->wrap_info->end_pos) {
+ 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.
+ */
+ if (start > undostruct->wrap_info->end_pos) {
+ 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,
{
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);
}
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,