/*
* Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
- * Copyright (C) 1999-2001 Hiroyuki Yamamoto
+ * 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
- * the Free Software Foundation; either version 2 of the License, or
+ * the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
*/
/* code ported from gedit */
#ifdef HAVE_CONFIG_H
# include "config.h"
+#include "claws-features.h"
#endif
#include <glib.h>
#include "undo.h"
#include "utils.h"
#include "prefs_common.h"
-#include "gtkstext.h"
typedef struct _UndoInfo 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,
gint end_pos,
UndoAction action,
UndoMain *undostruct);
-static gint undo_get_selection (GtkEditable *text,
+static gint undo_get_selection (GtkTextView *textview,
guint *start,
guint *end);
-static void undo_insert_text_cb (GtkEditable *editable,
+static void undo_insert_text_cb (GtkTextBuffer *textbuf,
+ GtkTextIter *iter,
gchar *new_text,
- gint new_text_length,
- gint *position,
+ gint new_text_length,
UndoMain *undostruct);
-static void undo_delete_text_cb (GtkEditable *editable,
- gint start_pos,
- gint end_pos,
+static void undo_delete_text_cb (GtkTextBuffer *textbuf,
+ GtkTextIter *start,
+ GtkTextIter *end,
UndoMain *undostruct);
-static void undo_paste_clipboard_cb (GtkEditable *editable,
+static void undo_paste_clipboard_cb (GtkTextView *textview,
UndoMain *undostruct);
void undo_undo (UndoMain *undostruct);
UndoMain *undo_init(GtkWidget *text)
{
UndoMain *undostruct;
-
- g_return_val_if_fail(text != NULL, NULL);
+ GtkTextView *textview = GTK_TEXT_VIEW(text);
+ GtkTextBuffer *textbuf = gtk_text_view_get_buffer(textview);
+
+ cm_return_val_if_fail(text != NULL, NULL);
- undostruct = g_new(UndoMain, 1);
- undostruct->text = text;
+ undostruct = g_new0(UndoMain, 1);
+ undostruct->textview = textview;
undostruct->undo = NULL;
undostruct->redo = NULL;
undostruct->paste = 0;
undostruct->undo_state = FALSE;
undostruct->redo_state = FALSE;
- gtk_signal_connect(GTK_OBJECT(text), "insert-text",
- GTK_SIGNAL_FUNC(undo_insert_text_cb), undostruct);
- gtk_signal_connect(GTK_OBJECT(text), "delete-text",
- GTK_SIGNAL_FUNC(undo_delete_text_cb), undostruct);
- gtk_signal_connect(GTK_OBJECT(text), "paste-clipboard",
- GTK_SIGNAL_FUNC(undo_paste_clipboard_cb), undostruct);
+ g_signal_connect(G_OBJECT(textbuf), "insert-text",
+ G_CALLBACK(undo_insert_text_cb), undostruct);
+ g_signal_connect(G_OBJECT(textbuf), "delete-range",
+ G_CALLBACK(undo_delete_text_cb), undostruct);
+ g_signal_connect(G_OBJECT(textview), "paste-clipboard",
+ G_CALLBACK(undo_paste_clipboard_cb), undostruct);
return undostruct;
}
void undo_set_change_state_func(UndoMain *undostruct, UndoChangeStateFunc func,
gpointer data)
{
- g_return_if_fail(undostruct != NULL);
+ cm_return_if_fail(undostruct != NULL);
undostruct->change_state_func = func;
undostruct->change_state_data = data;
undostruct->undo = g_list_remove(undostruct->undo, last_undo);
undo_object_free(last_undo);
}
- debug_print("g_list_length(undostruct->undo): %d\n", length);
}
/**
}
if (action == UNDO_ACTION_DELETE) {
- gboolean checkit = TRUE;
-
if (last_undo->start_pos != end_pos &&
last_undo->start_pos != start_pos) {
last_undo->mergeable = FALSE;
return FALSE;
} else if (last_undo->start_pos == start_pos) {
/* Deleted with the delete key */
- if (text[0] != ' ' && text[0] != '\t' &&
- (last_undo->text[last_undo->end_pos - last_undo->start_pos - 1] == ' ' ||
- last_undo->text[last_undo->end_pos - last_undo->start_pos - 1] == '\t'))
- checkit = FALSE;
-
temp_string = g_strdup_printf("%s%s", last_undo->text, text);
last_undo->end_pos++;
g_free(last_undo->text);
last_undo->text = temp_string;
} else {
/* Deleted with the backspace key */
- if (text[0] != ' ' && text[0] != '\t' &&
- (last_undo->text[0] == ' ' ||
- last_undo->text[0] == '\t'))
- checkit = FALSE;
-
temp_string = g_strdup_printf("%s%s", text, last_undo->text);
last_undo->start_pos = start_pos;
g_free(last_undo->text);
last_undo->text = temp_string;
}
-
- if (!checkit) {
- last_undo->mergeable = FALSE;
- return FALSE;
- }
} else if (action == UNDO_ACTION_INSERT) {
if (last_undo->end_pos != start_pos) {
last_undo->mergeable = FALSE;
} else
debug_print("Unknown action [%i] inside undo merge encountered", action);
- debug_print("Merged: %s\n", text);
return TRUE;
}
UndoAction action, UndoMain *undostruct)
{
UndoInfo *undoinfo;
+ GtkAdjustment *vadj;
- debug_print("undo_add(%i)*%s*\n", strlen (text), text);
-
- g_return_if_fail(text != NULL);
- g_return_if_fail(end_pos >= start_pos);
+ cm_return_if_fail(text != NULL);
+ cm_return_if_fail(end_pos >= start_pos);
undo_free_list(&undostruct->redo);
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);
- debug_print("New: %s Action: %d Paste: %d\n", text, action, undostruct->paste);
-
+ 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,
- GTK_ADJUSTMENT(GTK_STEXT(undostruct->text)->vadj)->value);
+ gtk_adjustment_get_value(vadj));
if (end_pos - start_pos != 1 || text[0] == '\n')
undoinfo->mergeable = FALSE;
void undo_undo(UndoMain *undostruct)
{
UndoInfo *undoinfo;
- guint start_pos, end_pos;
+ GtkTextView *textview;
+ GtkTextBuffer *buffer;
+ GtkTextIter iter, start_iter, end_iter;
+ GtkTextMark *mark;
- g_return_if_fail(undostruct != NULL);
+ cm_return_if_fail(undostruct != NULL);
if (undostruct->undo == NULL) return;
/* The undo data we need is always at the top op the
stack. So, therefore, the first one */
undoinfo = (UndoInfo *)undostruct->undo->data;
- g_return_if_fail(undoinfo != NULL);
+ cm_return_if_fail(undoinfo != NULL);
undoinfo->mergeable = FALSE;
undostruct->redo = g_list_prepend(undostruct->redo, undoinfo);
undostruct->undo = g_list_remove(undostruct->undo, undoinfo);
+ textview = undostruct->textview;
+ buffer = gtk_text_view_get_buffer(textview);
+
+ undo_block(undostruct);
+
/* Check if there is a selection active */
- start_pos = GTK_EDITABLE(undostruct->text)->selection_start_pos;
- end_pos = GTK_EDITABLE(undostruct->text)->selection_end_pos;
- if ((start_pos > 0 || end_pos > 0) && (start_pos != end_pos))
- gtk_editable_select_region(GTK_EDITABLE(undostruct->text),
- 0, 0);
+ mark = gtk_text_buffer_get_insert(buffer);
+ gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
+ gtk_text_buffer_place_cursor(buffer, &iter);
/* Move the view (scrollbars) to the correct position */
gtk_adjustment_set_value
- (GTK_ADJUSTMENT(GTK_STEXT(undostruct->text)->vadj),
+ (GTK_ADJUSTMENT(gtk_text_view_get_vadjustment(textview)),
undoinfo->window_position);
-
+
switch (undoinfo->action) {
case UNDO_ACTION_DELETE:
- gtk_stext_set_point(GTK_STEXT(undostruct->text), undoinfo->start_pos);
- gtk_stext_insert(GTK_STEXT(undostruct->text), NULL, NULL, NULL, undoinfo->text, -1);
- debug_print("UNDO_ACTION_DELETE %s\n", undoinfo->text);
+ gtk_text_buffer_get_iter_at_offset(buffer, &iter, undoinfo->start_pos);
+ gtk_text_buffer_insert(buffer, &iter, undoinfo->text, -1);
break;
case UNDO_ACTION_INSERT:
- gtk_stext_set_point(GTK_STEXT(undostruct->text), undoinfo->end_pos);
- gtk_stext_backward_delete(GTK_STEXT(undostruct->text), undoinfo->end_pos-undoinfo->start_pos);
- debug_print("UNDO_ACTION_INSERT %d\n", undoinfo->end_pos-undoinfo->start_pos);
+ 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;
case UNDO_ACTION_REPLACE_INSERT:
- gtk_stext_set_point(GTK_STEXT(undostruct->text), undoinfo->end_pos);
- gtk_stext_backward_delete(GTK_STEXT(undostruct->text), undoinfo->end_pos-undoinfo->start_pos);
- debug_print("UNDO_ACTION_REPLACE %s\n", undoinfo->text);
- /* "pull" another data structure from the list */
- undoinfo = (UndoInfo *)undostruct->undo->data;
- g_return_if_fail(undoinfo != NULL);
- undostruct->redo = g_list_prepend(undostruct->redo, undoinfo);
- undostruct->undo = g_list_remove(undostruct->undo, undoinfo);
- g_return_if_fail(undoinfo->action == UNDO_ACTION_REPLACE_DELETE);
- gtk_stext_set_point(GTK_STEXT(undostruct->text), undoinfo->start_pos);
- gtk_stext_insert(GTK_STEXT(undostruct->text), NULL, NULL, NULL, undoinfo->text, -1);
- debug_print("UNDO_ACTION_REPLACE %s\n", undoinfo->text);
+ 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" previous matching DELETE 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_DELETE);
+ gtk_text_buffer_insert(buffer, &start_iter, undoinfo->text, -1);
+ }
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();
break;
}
-
+
undostruct->change_state_func(undostruct,
UNDO_STATE_UNCHANGED, UNDO_STATE_TRUE,
undostruct->change_state_data);
UNDO_STATE_FALSE,
UNDO_STATE_UNCHANGED,
undostruct->change_state_data);
+
+ undo_unblock(undostruct);
}
/**
void undo_redo(UndoMain *undostruct)
{
UndoInfo *redoinfo;
- guint start_pos, end_pos;
+ GtkTextView *textview;
+ GtkTextBuffer *buffer;
+ GtkTextIter iter, start_iter, end_iter;
+ GtkTextMark *mark;
- g_return_if_fail(undostruct != NULL);
+ cm_return_if_fail(undostruct != NULL);
if (undostruct->redo == NULL) return;
redoinfo = (UndoInfo *)undostruct->redo->data;
- g_return_if_fail (redoinfo != NULL);
+ cm_return_if_fail (redoinfo != NULL);
undostruct->undo = g_list_prepend(undostruct->undo, redoinfo);
undostruct->redo = g_list_remove(undostruct->redo, redoinfo);
+ textview = undostruct->textview;
+ buffer = gtk_text_view_get_buffer(textview);
+
+ undo_block(undostruct);
+
/* Check if there is a selection active */
- start_pos = GTK_EDITABLE(undostruct->text)->selection_start_pos;
- end_pos = GTK_EDITABLE(undostruct->text)->selection_end_pos;
- if ((start_pos > 0 || end_pos > 0) && (start_pos != end_pos))
- gtk_editable_select_region(GTK_EDITABLE(undostruct->text), 0, 0);
+ mark = gtk_text_buffer_get_insert(buffer);
+ gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
+ gtk_text_buffer_place_cursor(buffer, &iter);
/* Move the view to the right position. */
- gtk_adjustment_set_value(GTK_ADJUSTMENT(GTK_STEXT(undostruct->text)->vadj),
+ gtk_adjustment_set_value(gtk_text_view_get_vadjustment(textview),
redoinfo->window_position);
switch (redoinfo->action) {
case UNDO_ACTION_INSERT:
- gtk_stext_set_point(GTK_STEXT(undostruct->text),
- redoinfo->start_pos);
- gtk_stext_insert(GTK_STEXT(undostruct->text), NULL, NULL,
- NULL, redoinfo->text, -1);
- debug_print("UNDO_ACTION_DELETE %s\n",redoinfo->text);
+ gtk_text_buffer_get_iter_at_offset(buffer, &iter, redoinfo->start_pos);
+ gtk_text_buffer_insert(buffer, &iter, redoinfo->text, -1);
break;
case UNDO_ACTION_DELETE:
- gtk_stext_set_point(GTK_STEXT(undostruct->text),
- redoinfo->end_pos);
- gtk_stext_backward_delete
- (GTK_STEXT(undostruct->text),
- redoinfo->end_pos - redoinfo->start_pos);
- debug_print("UNDO_ACTION_INSERT %d\n",
- redoinfo->end_pos-redoinfo->start_pos);
+ 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;
case UNDO_ACTION_REPLACE_DELETE:
- gtk_stext_set_point(GTK_STEXT(undostruct->text),
- redoinfo->end_pos);
- gtk_stext_backward_delete
- (GTK_STEXT(undostruct->text),
- redoinfo->end_pos - redoinfo->start_pos);
- /* "pull" another data structure from the list */
+ 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);
+ debug_print("UNDO_ACTION_REPLACE %s\n", redoinfo->text);
+ /* "pull" previous matching INSERT data structure from the list */
redoinfo = (UndoInfo *)undostruct->redo->data;
- g_return_if_fail(redoinfo != NULL);
+ cm_return_if_fail(redoinfo != NULL);
undostruct->undo = g_list_prepend(undostruct->undo, redoinfo);
undostruct->redo = g_list_remove(undostruct->redo, redoinfo);
- g_return_if_fail(redoinfo->action == UNDO_ACTION_REPLACE_INSERT);
- gtk_stext_set_point(GTK_STEXT(undostruct->text),
- redoinfo->start_pos);
- gtk_stext_insert(GTK_STEXT(undostruct->text), NULL, NULL,
- NULL, redoinfo->text, -1);
+ cm_return_if_fail(redoinfo->action == UNDO_ACTION_REPLACE_INSERT);
+ gtk_text_buffer_insert(buffer, &start_iter, redoinfo->text, -1);
break;
case UNDO_ACTION_REPLACE_INSERT:
- g_warning("This should not happen. Redo: UNDO_REPLACE_INSERT");
+ 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();
UNDO_STATE_UNCHANGED,
UNDO_STATE_FALSE,
undostruct->change_state_data);
+
+ undo_unblock(undostruct);
+}
+
+void undo_block(UndoMain *undostruct)
+{
+ GtkTextBuffer *buffer;
+
+ cm_return_if_fail(GTK_IS_TEXT_VIEW(undostruct->textview));
+
+ buffer = gtk_text_view_get_buffer(undostruct->textview);
+ g_signal_handlers_block_by_func(buffer, undo_insert_text_cb, undostruct);
+ g_signal_handlers_block_by_func(buffer, undo_delete_text_cb, undostruct);
+ g_signal_handlers_block_by_func(buffer, undo_paste_clipboard_cb,
+ undostruct);
}
-void undo_insert_text_cb(GtkEditable *editable, gchar *new_text,
- gint new_text_length, gint *position,
+void undo_unblock(UndoMain *undostruct)
+{
+ GtkTextBuffer *buffer;
+
+ cm_return_if_fail(GTK_IS_TEXT_VIEW(undostruct->textview));
+
+ buffer = gtk_text_view_get_buffer(undostruct->textview);
+ g_signal_handlers_unblock_by_func(buffer, undo_insert_text_cb, undostruct);
+ g_signal_handlers_unblock_by_func(buffer, undo_delete_text_cb, undostruct);
+ g_signal_handlers_unblock_by_func(buffer, undo_paste_clipboard_cb,
+ 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)
+{
+ 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 *new_text, gint new_text_length,
UndoMain *undostruct)
{
gchar *text_to_insert;
- size_t wlen;
+ gint pos;
+ glong utf8_len;
if (prefs_common.undolevels <= 0) return;
+ pos = gtk_text_iter_get_offset(iter);
Xstrndup_a(text_to_insert, new_text, new_text_length, return);
- if (MB_CUR_MAX > 1) {
- wchar_t *wstr;
+ utf8_len = g_utf8_strlen(text_to_insert, -1);
- wstr = g_new(wchar_t, new_text_length + 1);
- wlen = mbstowcs(wstr, text_to_insert, new_text_length + 1);
- g_free(wstr);
- if (wlen < 0) return;
- } else
- wlen = new_text_length;
+ if (undostruct->wrap_info != NULL) {
+ update_wrap_undo(undostruct, text_to_insert,
+ pos, pos + utf8_len, UNDO_ACTION_INSERT);
+ return;
+ }
- undo_add(text_to_insert, *position, *position + wlen,
+ debug_print("add:undo add %d-%ld\n", pos, utf8_len);
+ undo_add(text_to_insert, pos, pos + utf8_len,
UNDO_ACTION_INSERT, undostruct);
}
-void undo_delete_text_cb(GtkEditable *editable, gint start_pos,
- gint end_pos, UndoMain *undostruct)
+void undo_delete_text_cb(GtkTextBuffer *textbuf, GtkTextIter *start,
+ GtkTextIter *end, UndoMain *undostruct)
{
gchar *text_to_delete;
+ gint start_pos, end_pos;
if (prefs_common.undolevels <= 0) return;
- if (start_pos == end_pos) return;
- text_to_delete = gtk_editable_get_chars(GTK_EDITABLE(editable),
- start_pos, end_pos);
+ text_to_delete = gtk_text_buffer_get_text(textbuf, start, end, FALSE);
+ if (!text_to_delete || !*text_to_delete) return;
+
+ start_pos = gtk_text_iter_get_offset(start);
+ end_pos = gtk_text_iter_get_offset(end);
+
+ 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,
undostruct);
g_free(text_to_delete);
}
-void undo_paste_clipboard_cb(GtkEditable *editable, UndoMain *undostruct)
+void undo_paste_clipboard(GtkTextView *textview, UndoMain *undostruct)
{
- if (editable->clipboard_text == NULL) return;
+ undo_paste_clipboard_cb(textview, undostruct);
+}
- debug_print("before Paste: %d\n", undostruct->paste);
+static void undo_paste_clipboard_cb(GtkTextView *textview, UndoMain *undostruct)
+{
if (prefs_common.undolevels > 0)
- if (undo_get_selection(editable, NULL, NULL))
+ if (undo_get_selection(textview, NULL, NULL))
undostruct->paste = TRUE;
- debug_print("after Paste: %d\n", undostruct->paste);
}
/**
*
* Return Value: TRUE if there is a selection active, FALSE if not
**/
-static gint undo_get_selection(GtkEditable *text, guint *start, guint *end)
+static gint undo_get_selection(GtkTextView *textview, guint *start, guint *end)
{
+ GtkTextBuffer *buffer;
+ GtkTextIter start_iter, end_iter;
guint start_pos, end_pos;
- start_pos = text->selection_start_pos;
- end_pos = text->selection_end_pos;
+ buffer = gtk_text_view_get_buffer(textview);
+ gtk_text_buffer_get_selection_bounds(buffer, &start_iter, &end_iter);
+
+ start_pos = gtk_text_iter_get_offset(&start_iter);
+ end_pos = gtk_text_iter_get_offset(&end_iter);
/* The user can select from end to start too. If so, swap it*/
if (end_pos < start_pos) {