[Xfce4-commits] <mousepad:master> * mousepad/mousepad-undo.c: Improve the behavior of the undo manager. It now merges multiple spaces, new lines are a separate undo action and when you redo some steps and then start editing again we append the redo-ed steps in reversed order with an inverted action, so you can undo every thing afterwards. This consumes a bit more memory compared to the old behavior (which simply removed every redo-ed step), but it's much more consistent.
Nick Schermer
noreply at xfce.org
Sat May 5 21:30:21 CEST 2012
Updating branch refs/heads/master
to 44f28f94cfd8b18c76852726550c2fc476fb4801 (commit)
from 0210f46f4a9e604a52bda681c820e66d352303e5 (commit)
commit 44f28f94cfd8b18c76852726550c2fc476fb4801
Author: Nick Schermer <nick at xfce.org>
Date: Thu Apr 12 19:38:59 2007 +0000
* mousepad/mousepad-undo.c: Improve the behavior of the undo manager.
It now merges multiple spaces, new lines are a separate undo action and
when you redo some steps and then start editing again we append the redo-ed
steps in reversed order with an inverted action, so you can undo every thing
afterwards. This consumes a bit more memory compared to the old behavior
(which simply removed every redo-ed step), but it's much more consistent.
(Old svn revision: 25531)
ChangeLog | 11 +++
mousepad/mousepad-undo.c | 189 ++++++++++++++++++++++++++++++----------------
2 files changed, 134 insertions(+), 66 deletions(-)
diff --git a/ChangeLog b/ChangeLog
index e30174d..c966bb8 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,11 +1,22 @@
+2007-04-12 Nick Schermer <nick at xfce.org>
+ * mousepad/mousepad-undo.c: Improve the behavior of the undo manager.
+ It now merges multiple spaces, new lines are a separate undo action and
+ when you redo some steps and then start editing again we append the redo-ed
+ steps in reversed order with an inverted action, so you can undo every thing
+ afterwards. This consumes a bit more memory compared to the old behavior
+ (which simply removed every redo-ed step), but it's much more consistent.
+
+
2007-04-11 Nick Schermer <nick at xfce.org>
* mousepad/mousepad-{window,view}.c: Fix some potential leaks, I'm not
really sure, but it doesn't hurt...
+
2007-04-11 Nick Schermer <nick at xfce.org>
* mousepad/mousepad-statusbar.c: Destroy the tool items together with the
searchbar (memory leak from valgrind).
+
2007-04-11 Nick Schermer <nick at xfce.org>
* mousepad/mousepad-{document,window,statusbar}: You can now click the OVR
text in the statusbar to toggle the overwrite mode.
diff --git a/mousepad/mousepad-undo.c b/mousepad/mousepad-undo.c
index 874538f..51b094b 100644
--- a/mousepad/mousepad-undo.c
+++ b/mousepad/mousepad-undo.c
@@ -27,7 +27,7 @@
/* maximum number of steps in the undo manager */
-#define MAX_UNDO_STEPS 50
+#define MAX_UNDO_STEPS 75
@@ -54,7 +54,7 @@ struct _MousepadUndoClass
struct _MousepadUndo
{
- GObject __parent__;
+ GObject __parent__;
/* the text buffer we're monitoring */
GtkTextBuffer *buffer;
@@ -63,7 +63,7 @@ struct _MousepadUndo
guint locked;
/* list of undo steps */
- GSList *steps;
+ GList *steps;
/* position in the steps list */
gint steps_position;
@@ -75,6 +75,7 @@ struct _MousepadUndo
MousepadUndoAction step_action;
gint step_start;
gint step_end;
+ guint step_isspaces : 1;
/* whether we can undo and redo */
guint can_undo : 1;
@@ -99,7 +100,8 @@ struct _MousepadUndoInfo
static void mousepad_undo_class_init (MousepadUndoClass *klass);
static void mousepad_undo_init (MousepadUndo *undo);
static void mousepad_undo_finalize (GObject *object);
-static void mousepad_undo_free_step (MousepadUndoInfo *info);
+static void mousepad_undo_free_step (MousepadUndoInfo *info,
+ MousepadUndo *undo);
static void mousepad_undo_preform_step (MousepadUndo *undo,
gboolean undo_step);
static void mousepad_undo_new_step (MousepadUndo *undo);
@@ -182,7 +184,7 @@ mousepad_undo_init (MousepadUndo *undo)
undo->can_undo = FALSE;
undo->can_redo = FALSE;
- /* allocate the string buffer (we prealloc 15 characters to avoid mulitple reallocations ) */
+ /* allocate the string buffer (we prealloc 15 characters to avoid multiple reallocations) */
undo->step_buffer = g_string_sized_new (15);
}
@@ -192,14 +194,14 @@ static void
mousepad_undo_finalize (GObject *object)
{
MousepadUndo *undo = MOUSEPAD_UNDO (object);
- GSList *li;
+ GList *li;
/* cleanup the undo steps */
for (li = undo->steps; li != NULL; li = li->next)
- mousepad_undo_free_step (li->data);
+ mousepad_undo_free_step (li->data, undo);
/* free the list */
- g_slist_free (undo->steps);
+ g_list_free (undo->steps);
/* cleanup the monitor step */
g_string_free (undo->step_buffer, TRUE);
@@ -213,8 +215,12 @@ mousepad_undo_finalize (GObject *object)
static void
-mousepad_undo_free_step (MousepadUndoInfo *info)
+mousepad_undo_free_step (MousepadUndoInfo *info,
+ MousepadUndo *undo)
{
+ /* remove from the list */
+ undo->steps = g_list_remove (undo->steps, info);
+
/* free the string */
g_free (info->string);
@@ -246,7 +252,7 @@ mousepad_undo_preform_step (MousepadUndo *undo,
undo->steps_position--;
/* get the step we're going to undo */
- info = g_slist_nth_data (undo->steps, undo->steps_position);
+ info = g_list_nth_data (undo->steps, undo->steps_position);
if (G_LIKELY (info))
{
@@ -291,7 +297,7 @@ mousepad_undo_preform_step (MousepadUndo *undo,
/* set the can_undo boolean */
undo->can_undo = (undo->steps_position > 0);
- undo->can_redo = (undo->steps_position < g_slist_length (undo->steps));
+ undo->can_redo = (undo->steps_position < g_list_length (undo->steps));
/* emit the can-undo and can-redo signals */
g_signal_emit (G_OBJECT (undo), undo_signals[CAN_UNDO], 0, undo->can_undo);
@@ -306,45 +312,75 @@ mousepad_undo_preform_step (MousepadUndo *undo,
static void
mousepad_undo_new_step (MousepadUndo *undo)
{
- MousepadUndoInfo *info;
- gint i;
- GSList *item;
+ MousepadUndoInfo *info, *existing;
+ gint i, length;
/* leave when there is nothing todo */
if (undo->step_start == 0 && undo->step_end == 0)
return;
+ length = g_list_length (undo->steps);
+
+ /* when we're not at the end of the list, the user did some redo steps. so we actually
+ * reverted the steps after the current position. if we want to do proper undoing after
+ * appending the current step, we have to append the redo-ed steps in reversed order
+ * with an inverted action.
+ *
+ * there is also a lazy way to avoid weird undo's/redo's: remove all the steps after the
+ * current list position. this was the old behaviour of Mousepad. */
+
+ if (undo->steps_position != length)
+ for (i = length - 1; i >= undo->steps_position; i--)
+ {
+ /* get the existing step */
+ existing = g_list_nth_data (undo->steps, i);
+
+ /* allocate a new slice */
+ info = g_slice_new0 (MousepadUndoInfo);
+
+ /* copy the data from the existing step */
+ info->string = g_strdup (existing->string);
+ info->start = existing->start;
+ info->end = existing->end;
+
+ /* set the inverted action */
+ info->action = (existing->action == INSERT ? DELETE : INSERT);
+
+ /* append to the steps list */
+ undo->steps = g_list_append (undo->steps, info);
+ }
+
/* allocate the slice */
info = g_slice_new0 (MousepadUndoInfo);
/* set the info */
info->string = g_strdup (undo->step_buffer->str);
info->action = undo->step_action;
- info->start = undo->step_start;
- info->end = undo->step_end;
+ info->start = undo->step_start;
+ info->end = undo->step_end;
/* append to the steps list */
- undo->steps = g_slist_append (undo->steps, info);
-
- /* set the list position */
- undo->steps_position = g_slist_length (undo->steps);
+ undo->steps = g_list_append (undo->steps, info);
/* erase the buffer */
undo->step_buffer = g_string_erase (undo->step_buffer, 0, -1);
+ undo->step_start = undo->step_end = 0;
/* check the list length */
- if (G_UNLIKELY (g_slist_length (undo->steps) > MAX_UNDO_STEPS))
- for (i = g_slist_length (undo->steps); i > MAX_UNDO_STEPS; i--)
- {
- /* get the first item in the list */
- item = g_slist_nth (undo->steps, 0);
+ for (i = g_list_length (undo->steps); i > MAX_UNDO_STEPS; i--)
+ {
+ /* get the first item in the list */
+ info = g_list_first (undo->steps)->data;
- /* cleanup the data */
- mousepad_undo_free_step (item->data);
+ /* cleanup the data and remove from the list */
+ mousepad_undo_free_step (info, undo);
+ }
- /* delete the node in the list */
- undo->steps = g_slist_delete_link (undo->steps, item);
- }
+ /* set the new list position (end of the list) */
+ undo->steps_position = i;
+
+ /* we're at the end of the list, so we can't redo */
+ undo->can_redo = FALSE;
}
@@ -356,68 +392,89 @@ mousepad_undo_handle_step (const gchar *text,
MousepadUndoAction action,
MousepadUndo *undo)
{
+ gint length;
+ guchar c;
+ gboolean char_isspace = FALSE;
+ gboolean char_isnewline = FALSE;
gboolean create_new_step = FALSE;
+ gboolean clear_step_afterwards = FALSE;
- /* check if we need to update whether we can undo */
- if (undo->can_undo != TRUE)
+ /* length of the text */
+ length = ABS (end - start);
+
+ /* only do this if there is 1 character typed / deleted */
+ if (length == 1)
{
- undo->can_undo = TRUE;
+ /* get the character */
+ c = g_utf8_get_char (text);
- /* emit the can-undo signal */
- g_signal_emit (G_OBJECT (undo), undo_signals[CAN_UNDO], 0, undo->can_undo);
+ /* check if the charater is a space or a new line */
+ char_isspace = (c == ' ' || c == '\t');
+ char_isnewline = (c == '\n');
}
- /* check if we need to create a new step after we appended the data */
- if (ABS (end - start) == 1)
+ /* create a new step if we jump to a new line or the string contains only spaces, but the
+ * new char is not a space */
+ if (char_isnewline || (undo->step_isspaces && !char_isspace))
{
- switch (g_utf8_get_char (text))
- {
- case ' ':
- case '\t':
- case '\n':
- create_new_step = TRUE;
- break;
- }
+ create_new_step = clear_step_afterwards = TRUE;
+ goto new_step;
}
+ /* if the buffer does not start with a space, but the new char does, we flush the buffer
+ * afterwards */
+ else if (!undo->step_isspaces && char_isspace)
+ clear_step_afterwards = TRUE;
- /* try to append to the active step */
- if (undo->step_action == action
- && action == INSERT
- && undo->step_end == start)
+ /* check if we can append (insert action) */
+ if (undo->step_action == action && action == INSERT && undo->step_end == start)
{
- /* append the inserted string */
- undo->step_buffer = g_string_append_len (undo->step_buffer, text, (start - end));
+ /* append the inserted string */
+ undo->step_buffer = g_string_append_len (undo->step_buffer, text, length);
/* update the end position */
undo->step_end = end;
}
- else if (undo->step_action == action
- && action == DELETE
- && undo->step_start == end)
+ /* check if we can prepend (delete action) */
+ else if (undo->step_action == action && action == DELETE && undo->step_start == end)
{
- /* prepend the deleted text */
- undo->step_buffer = g_string_prepend_len (undo->step_buffer, text, (end - start));
+ /* prepend the deleted text */
+ undo->step_buffer = g_string_prepend_len (undo->step_buffer, text, length);
/* update the start position */
undo->step_start = start;
}
+ /* create a new step */
else
{
- /* we really need a new step */
- create_new_step = TRUE;
+ create_new_step = clear_step_afterwards = TRUE;
}
- /* create a new step if needed */
+new_step:
+ if (clear_step_afterwards)
+ mousepad_undo_new_step (undo);
+
+ /* only start a new step when the char was not a space */
if (create_new_step)
{
- /* create step */
- mousepad_undo_new_step (undo);
+ /* set the new info */
+ undo->step_buffer = g_string_append_len (undo->step_buffer, text, ABS (start - end));
+ undo->step_action = action;
+ undo->step_start = start;
+ undo->step_end = end;
+ undo->step_isspaces = char_isspace;
+
+ /* flush again when we had a new line */
+ if (char_isnewline)
+ mousepad_undo_new_step (undo);
+ }
- /* set the new info */
- undo->step_buffer = g_string_append_len (undo->step_buffer, text, ABS (start - end));
- undo->step_action = action;
- undo->step_start = start;
- undo->step_end = end;
+ /* check if we need to send the undo signal */
+ if (undo->can_undo != TRUE)
+ {
+ undo->can_undo = TRUE;
+
+ /* emit the can-undo signal */
+ g_signal_emit (G_OBJECT (undo), undo_signals[CAN_UNDO], 0, undo->can_undo);
}
}
More information about the Xfce4-commits
mailing list