[gtksourceview/wip/undo-redo-restore-selection: 38/38] UndoManager: restore restore selection (wip)
- From: Sébastien Wilmet <swilmet src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gtksourceview/wip/undo-redo-restore-selection: 38/38] UndoManager: restore restore selection (wip)
- Date: Thu, 20 Nov 2014 22:30:44 +0000 (UTC)
commit 6d3bb64181fb9089682bdd44cf7e62b82de45bd2
Author: Sébastien Wilmet <swilmet gnome org>
Date: Sat Oct 11 15:10:46 2014 +0200
UndoManager: restore restore selection (wip)
Restore the restore selection behavior (but enhanced).
gtksourceview/gtksourceundomanagerdefault.c | 179 ++++++++++++++++++++++++---
1 files changed, 163 insertions(+), 16 deletions(-)
---
diff --git a/gtksourceview/gtksourceundomanagerdefault.c b/gtksourceview/gtksourceundomanagerdefault.c
index 6d61fbf..aa4027f 100644
--- a/gtksourceview/gtksourceundomanagerdefault.c
+++ b/gtksourceview/gtksourceundomanagerdefault.c
@@ -39,6 +39,19 @@ typedef enum
ACTION_TYPE_DELETE
} ActionType;
+/* A more precise deletion type. But currently it's only a guess, we are not
+ * 100% sure of the deletion type. To be sure, we would need to listen to key
+ * events on the GtkSourceView widget, which is more complicated than simply
+ * listening to the insert-text and delete-range GtkTextBuffer signals.
+ */
+typedef enum
+{
+ DELETION_TYPE_SELECTION_DELETED,
+ DELETION_TYPE_BACKSPACE_KEY,
+ DELETION_TYPE_DELETE_KEY,
+ DELETION_TYPE_PROGRAMMATICALLY
+} DeletionType;
+
struct _Action
{
ActionType type;
@@ -58,11 +71,21 @@ struct _Action
*/
gchar *text;
- /* Used only for a deletion. If forward is TRUE, the Delete key was
- * probably used. If forward is FALSE, the Backspace key was probably
- * used.
+ /* Character offsets of the insert and selection bound marks.
+ * They are both -1 or they both match @start or @end.
+ * If the text cursor or the selected text is not related to the action,
+ * the selection is not stored (i.e. -1).
+ * If not -1, when undoing or redoing an action, the insert and
+ * selection bound marks are restored to where they were.
+ * For an insert, @selection_insert and @selection_bound must match
+ * @start, otherwise the selection or cursor position is unrelated to
+ * the insertion.
+ * For a deletion, if @selection_insert and @selection_bound are -1, it
+ * corresponds to DELETION_TYPE_PROGRAMMATICALLY. For all the other
+ * deletion types, the selection is stored.
*/
- guint forward : 1;
+ gint selection_insert;
+ gint selection_bound;
};
struct _ActionGroup
@@ -162,7 +185,14 @@ G_DEFINE_TYPE_WITH_CODE (GtkSourceUndoManagerDefault,
static Action *
action_new (void)
{
- return g_slice_new0 (Action);
+ Action *action;
+
+ action = g_slice_new0 (Action);
+
+ action->selection_insert = -1;
+ action->selection_bound = -1;
+
+ return action;
}
static void
@@ -706,12 +736,45 @@ action_delete_redo (GtkTextBuffer *buffer,
delete_text (buffer, action->start, action->end);
}
+static DeletionType
+get_deletion_type (Action *action)
+{
+ g_assert_cmpint (action->type, ==, ACTION_TYPE_DELETE);
+
+ if (action->selection_insert == -1)
+ {
+ g_assert_cmpint (action->selection_bound, ==, -1);
+ return DELETION_TYPE_PROGRAMMATICALLY;
+ }
+
+ if (action->selection_insert == action->end &&
+ action->selection_bound == action->end)
+ {
+ return DELETION_TYPE_BACKSPACE_KEY;
+ }
+
+ if (action->selection_insert == action->start &&
+ action->selection_bound == action->start)
+ {
+ return DELETION_TYPE_DELETE_KEY;
+ }
+
+ g_assert ((action->selection_insert == action->start ||
+ action->selection_insert == action->end));
+ g_assert ((action->selection_bound == action->start ||
+ action->selection_bound == action->end));
+
+ return DELETION_TYPE_SELECTION_DELETED;
+}
+
static gboolean
action_delete_merge (Action *action,
Action *new_action)
{
gint new_text_length;
gunichar new_char;
+ DeletionType deletion_type;
+ DeletionType new_deletion_type;
g_assert_cmpint (action->type, ==, ACTION_TYPE_DELETE);
g_assert_cmpint (new_action->type, ==, ACTION_TYPE_DELETE);
@@ -722,21 +785,56 @@ action_delete_merge (Action *action,
new_char = g_utf8_get_char (new_action->text);
g_assert (new_char != '\n');
- /* A Backspace can not be merged with a Delete.
- * Two Backspaces or two Deletes must follow each other.
+ /* Two Backspaces or two Deletes must follow each other.
* In "abc", if the cursor is at offset 2 and I press the Backspace key,
* then move the cursor after 'c' and press Backspace again, the two
* deletes won't be merged, since there was a cursor movement in
* between.
*/
- if ((action->forward != new_action->forward) ||
- (action->forward && action->start != new_action->start) ||
- (!action->forward && action->start != new_action->end))
+ deletion_type = get_deletion_type (action);
+ new_deletion_type = get_deletion_type (new_action);
+
+ if (deletion_type != new_deletion_type)
{
return FALSE;
}
- /* forward, Delete key pressed several times */
+ switch (deletion_type)
+ {
+ /* If the user has selected some text and then has deleted it,
+ * it should be seen as a single action group, not mergeable. A
+ * good reason for that is to correctly restore the selection.
+ */
+ case DELETION_TYPE_SELECTION_DELETED:
+ return FALSE;
+
+ /* For memory use it would be better to take it into account,
+ * but the code is simpler like that.
+ */
+ case DELETION_TYPE_PROGRAMMATICALLY:
+ return FALSE;
+
+ case DELETION_TYPE_DELETE_KEY:
+ /* Not consecutive deletes. */
+ if (action->start != new_action->start)
+ {
+ return FALSE;
+ }
+ break;
+
+ case DELETION_TYPE_BACKSPACE_KEY:
+ /* Not consecutive backspaces. */
+ if (action->start != new_action->end)
+ {
+ return FALSE;
+ }
+ break;
+
+ default:
+ g_assert_not_reached ();
+ }
+
+ /* Delete key pressed several times. */
if (action->start == new_action->start)
{
gunichar last_char;
@@ -758,10 +856,16 @@ action_delete_merge (Action *action,
action->end += new_text_length;
+ /* No need to update the selection, action->start is not
+ * modified.
+ */
+ g_assert_cmpint (action->selection_insert, ==, action->start);
+ g_assert_cmpint (action->selection_bound, ==, action->start);
+
return TRUE;
}
- /* backward, Backspace key pressed several times */
+ /* Backspace key pressed several times. */
if (action->start == new_action->end)
{
gunichar last_char;
@@ -786,6 +890,10 @@ action_delete_merge (Action *action,
action->start = new_action->start;
+ /* No need to update the selection, action->end is not modified. */
+ g_assert_cmpint (action->selection_insert, ==, action->end);
+ g_assert_cmpint (action->selection_bound, ==, action->end);
+
return TRUE;
}
@@ -923,6 +1031,25 @@ action_set_cursor_position (GtkTextBuffer *buffer,
/* Buffer signal handlers */
static void
+set_selection_bounds (GtkTextBuffer *buffer,
+ Action *action)
+{
+ GtkTextMark *insert_mark;
+ GtkTextMark *selection_mark;
+ GtkTextIter insert_iter;
+ GtkTextIter selection_iter;
+
+ insert_mark = gtk_text_buffer_get_insert (buffer);
+ selection_mark = gtk_text_buffer_get_selection_bound (buffer);
+
+ gtk_text_buffer_get_iter_at_mark (buffer, &insert_iter, insert_mark);
+ gtk_text_buffer_get_iter_at_mark (buffer, &selection_iter, selection_mark);
+
+ action->selection_insert = gtk_text_iter_get_offset (&insert_iter);
+ action->selection_bound = gtk_text_iter_get_offset (&selection_iter);
+}
+
+static void
insert_text_cb (GtkTextBuffer *buffer,
GtkTextIter *location,
const gchar *text,
@@ -936,6 +1063,21 @@ insert_text_cb (GtkTextBuffer *buffer,
action->text = g_strndup (text, length);
action->end = action->start + g_utf8_strlen (action->text, -1);
+ set_selection_bounds (buffer, action);
+
+ if (action->selection_insert != action->selection_bound ||
+ action->selection_insert != action->start)
+ {
+ action->selection_insert = -1;
+ action->selection_bound = -1;
+ }
+ else
+ {
+ /* The insertion occurred at the cursor. */
+ g_assert_cmpint (action->selection_insert, ==, action->start);
+ g_assert_cmpint (action->selection_bound, ==, action->start);
+ }
+
insert_action (manager, action);
}
@@ -946,7 +1088,6 @@ delete_range_cb (GtkTextBuffer *buffer,
GtkSourceUndoManagerDefault *manager)
{
Action *action = action_new ();
- GtkTextIter cursor_pos;
action->type = ACTION_TYPE_DELETE;
action->start = gtk_text_iter_get_offset (start);
@@ -955,10 +1096,16 @@ delete_range_cb (GtkTextBuffer *buffer,
g_assert_cmpint (action->start, <, action->end);
- gtk_text_buffer_get_iter_at_mark (buffer, &cursor_pos,
- gtk_text_buffer_get_insert (buffer));
+ set_selection_bounds (buffer, action);
- action->forward = gtk_text_iter_equal (&cursor_pos, start);
+ if ((action->selection_insert != action->start &&
+ action->selection_insert != action->end) ||
+ (action->selection_bound != action->start &&
+ action->selection_bound != action->end))
+ {
+ action->selection_insert = -1;
+ action->selection_bound = -1;
+ }
insert_action (manager, action);
}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]