1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3  * gtksourceundomanagerdefault.c
4  * This file is part of GtkSourceView
5  *
6  * Copyright (C) 1998, 1999 - Alex Roberts, Evan Lawrence
7  * Copyright (C) 2000, 2001 - Chema Celorio, Paolo Maggi
8  * Copyright (C) 2002-2005  - Paolo Maggi
9  * Copyright (C) 2014, 2015 - Sébastien Wilmet <swilmet@gnome.org>
10  *
11  * This library is free software; you can redistribute it and/or
12  * modify it under the terms of the GNU Lesser General Public
13  * License as published by the Free Software Foundation; either
14  * version 2.1 of the License, or (at your option) any later version.
15  *
16  * This library is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
19  * Lesser General Public License for more details.
20  *
21  * You should have received a copy of the GNU Lesser General Public
22  * License along with this library; if not, write to the Free Software
23  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
24  */
25 
26 #include "gtksourceundomanagerdefault.h"
27 #include <string.h>
28 #include "gtksourceundomanager.h"
29 
30 /* unlimited by default */
31 #define DEFAULT_MAX_UNDO_LEVELS -1
32 
33 typedef struct _Action		Action;
34 typedef struct _ActionGroup	ActionGroup;
35 
36 typedef enum _ActionType
37 {
38 	ACTION_TYPE_INSERT,
39 	ACTION_TYPE_DELETE
40 } ActionType;
41 
42 /* A more precise deletion type. But currently it's only a guess, we are not
43  * 100% sure of the deletion type. To be sure, we would need to listen to key
44  * events on the GtkSourceView widget, which is more complicated than simply
45  * listening to the insert-text and delete-range GtkTextBuffer signals.
46  */
47 typedef enum _DeletionType
48 {
49 	DELETION_TYPE_SELECTION_DELETED,
50 	DELETION_TYPE_BACKSPACE_KEY,
51 	DELETION_TYPE_DELETE_KEY,
52 	DELETION_TYPE_PROGRAMMATICALLY
53 } DeletionType;
54 
55 struct _Action
56 {
57 	ActionType type;
58 
59 	/* Character offset for the start of @text in the GtkTextBuffer. */
60 	gint start;
61 
62 	/* Character offset for the end of @text in the GtkTextBuffer. */
63 	gint end;
64 
65 	/* Nul-terminated text.
66 	 * TODO A possible memory optimization is to store the text only when
67 	 * needed. For an insertion that is located in the history on the undo
68 	 * side, the text is not needed since it is already present in the
69 	 * buffer. The same for a deletion on the redo side. But the last action
70 	 * text is needed for the merging.
71 	 */
72 	gchar *text;
73 
74 	/* Character offsets of the insert and selection bound marks.
75 	 * They are both -1 or they both match @start or @end.
76 	 * If the text cursor or the selected text is not related to the action,
77 	 * the selection is not stored (i.e. -1).
78 	 * If not -1, when undoing or redoing an action, the insert and
79 	 * selection bound marks are restored to where they were.
80 	 * For an insert, @selection_insert and @selection_bound must match
81 	 * @start, otherwise the selection or cursor position is unrelated to
82 	 * the insertion.
83 	 * For a deletion, if @selection_insert and @selection_bound are -1, it
84 	 * corresponds to DELETION_TYPE_PROGRAMMATICALLY. For all the other
85 	 * deletion types, the selection is stored.
86 	 */
87 	gint selection_insert;
88 	gint selection_bound;
89 };
90 
91 struct _ActionGroup
92 {
93 	/* One or several Action's that forms a single undo or redo step. The
94 	 * most recent action is at the end of the list.
95 	 * In fact, actions can be grouped with
96 	 * gtk_text_buffer_begin_user_action() and
97 	 * gtk_text_buffer_end_user_action().
98 	 */
99 	GQueue *actions;
100 
101 	/* If force_not_mergeable is FALSE, there are dynamic checks to see if
102 	 * the action group is mergeable. For example if the saved_location is
103 	 * just after the action group, the action group is not mergeable, so
104 	 * the saved_location isn't lost.
105 	 */
106 	guint force_not_mergeable : 1;
107 };
108 
109 struct _GtkSourceUndoManagerDefaultPrivate
110 {
111 	/* Weak ref to the buffer. */
112 	GtkTextBuffer *buffer;
113 
114 	/* List of ActionGroup's. The most recent ActionGroup is at the end of
115 	 * the list.
116 	 */
117 	GQueue *action_groups;
118 
119 	/* Current location in 'action_groups', where we are located in the
120 	 * history. The redo steps are on the right of the pointer, and the undo
121 	 * steps are on the left. In other words, the next redo step is
122 	 * location->data. The next undo step is location->prev->data. But the
123 	 * location should not be seen as a node, it should be seen as a
124 	 * vertical bar between two nodes, like a GtkTextIter between two
125 	 * characters.
126 	 */
127 	GList *location;
128 
129 	/* A new ActionGroup that is created when some text is inserted or
130 	 * deleted in the buffer. As long as a user action is running (when
131 	 * 'running_user_action' is TRUE) the new actions are inserted into
132 	 * 'new_action_group'. When the user action ends, we try to merge
133 	 * 'new_action_group' with the previous ActionGroup in 'action_groups'
134 	 * (the node on the left of 'location'). If the merging fails, a new
135 	 * node is inserted on the left of 'location'.
136 	 */
137 	ActionGroup *new_action_group;
138 
139 	/* The number of nested calls to
140 	 * gtk_source_buffer_begin_not_undoable_action().
141 	 */
142 	guint running_not_undoable_actions;
143 
144 	/* Max number of action groups. */
145 	gint max_undo_levels;
146 
147 	/* The location in 'action_groups' where the buffer is saved. I.e. when
148 	 * gtk_text_buffer_set_modified (buffer, FALSE) was called for the last
149 	 * time.
150 	 * NULL is for the end of 'action_groups'.
151 	 * 'has_saved_location' is FALSE if the history doesn't contain a saved
152 	 * location.
153 	 */
154 	GList *saved_location;
155 	guint has_saved_location : 1;
156 
157 	guint can_undo : 1;
158 	guint can_redo : 1;
159 
160 	/* Whether we are between a begin-user-action and a end-user-action.
161 	 * Some operations, like undo and redo, are not allowed during a user
162 	 * action (it would screw up the history).
163 	 * At the beginning of a user action, a new action group is created. At
164 	 * the end of the user action, we try to merge the group with the
165 	 * previous one. So when an insertion or deletion occurs when
166 	 * running_user_action is TRUE, we don't need to create a new group. But
167 	 * when running_user_action is FALSE, we need to put the insertion or
168 	 * deletion into a new group and try to merge it directly with the
169 	 * previous group.
170 	 */
171 	guint running_user_action : 1;
172 };
173 
174 enum
175 {
176 	PROP_0,
177 	PROP_BUFFER,
178 	PROP_MAX_UNDO_LEVELS
179 };
180 
181 static void gtk_source_undo_manager_iface_init (GtkSourceUndoManagerIface *iface);
182 
183 static gboolean action_merge (Action *action,
184 			      Action *new_action);
185 
G_DEFINE_TYPE_WITH_CODE(GtkSourceUndoManagerDefault,gtk_source_undo_manager_default,G_TYPE_OBJECT,G_ADD_PRIVATE (GtkSourceUndoManagerDefault)G_IMPLEMENT_INTERFACE (GTK_SOURCE_TYPE_UNDO_MANAGER,gtk_source_undo_manager_iface_init))186 G_DEFINE_TYPE_WITH_CODE (GtkSourceUndoManagerDefault,
187 			 gtk_source_undo_manager_default,
188 			 G_TYPE_OBJECT,
189 			 G_ADD_PRIVATE (GtkSourceUndoManagerDefault)
190                          G_IMPLEMENT_INTERFACE (GTK_SOURCE_TYPE_UNDO_MANAGER,
191                                                 gtk_source_undo_manager_iface_init))
192 
193 /* Utilities functions */
194 
195 static Action *
196 action_new (void)
197 {
198 	Action *action;
199 
200 	action = g_slice_new0 (Action);
201 
202 	action->selection_insert = -1;
203 	action->selection_bound = -1;
204 
205 	return action;
206 }
207 
208 static void
action_free(Action * action)209 action_free (Action *action)
210 {
211 	if (action != NULL)
212 	{
213 		g_free (action->text);
214 		g_slice_free (Action, action);
215 	}
216 }
217 
218 static ActionGroup *
action_group_new(void)219 action_group_new (void)
220 {
221 	ActionGroup *group;
222 
223 	group = g_slice_new (ActionGroup);
224 	group->actions = g_queue_new ();
225 	group->force_not_mergeable = FALSE;
226 
227 	return group;
228 }
229 
230 static void
action_group_free(ActionGroup * group)231 action_group_free (ActionGroup *group)
232 {
233 	if (group != NULL)
234 	{
235 		g_queue_free_full (group->actions, (GDestroyNotify) action_free);
236 		g_slice_free (ActionGroup, group);
237 	}
238 }
239 
240 static void
update_can_undo_can_redo(GtkSourceUndoManagerDefault * manager)241 update_can_undo_can_redo (GtkSourceUndoManagerDefault *manager)
242 {
243 	gboolean can_undo;
244 	gboolean can_redo;
245 
246 	if (manager->priv->running_user_action)
247 	{
248 		can_undo = FALSE;
249 		can_redo = FALSE;
250 	}
251 	else if (manager->priv->location != NULL)
252 	{
253 		can_undo = manager->priv->location->prev != NULL;
254 		can_redo = TRUE;
255 	}
256 	else
257 	{
258 		can_undo = manager->priv->action_groups->tail != NULL;
259 		can_redo = FALSE;
260 	}
261 
262 	if (manager->priv->can_undo != can_undo)
263 	{
264 		manager->priv->can_undo = can_undo;
265 		gtk_source_undo_manager_can_undo_changed (GTK_SOURCE_UNDO_MANAGER (manager));
266 	}
267 
268 	if (manager->priv->can_redo != can_redo)
269 	{
270 		manager->priv->can_redo = can_redo;
271 		gtk_source_undo_manager_can_redo_changed (GTK_SOURCE_UNDO_MANAGER (manager));
272 	}
273 }
274 
275 static void
clear_all(GtkSourceUndoManagerDefault * manager)276 clear_all (GtkSourceUndoManagerDefault *manager)
277 {
278 	GList *l;
279 
280 	if (manager->priv->has_saved_location &&
281 	    manager->priv->saved_location != manager->priv->location)
282 	{
283 		manager->priv->has_saved_location = FALSE;
284 	}
285 
286 	for (l = manager->priv->action_groups->head; l != NULL; l = l->next)
287 	{
288 		ActionGroup *group = l->data;
289 		action_group_free (group);
290 	}
291 
292 	g_queue_clear (manager->priv->action_groups);
293 	manager->priv->location = NULL;
294 	manager->priv->saved_location = NULL;
295 
296 	action_group_free (manager->priv->new_action_group);
297 	manager->priv->new_action_group = NULL;
298 
299 	update_can_undo_can_redo (manager);
300 }
301 
302 static void
remove_last_action_group(GtkSourceUndoManagerDefault * manager)303 remove_last_action_group (GtkSourceUndoManagerDefault *manager)
304 {
305 	ActionGroup *group;
306 
307 	if (manager->priv->action_groups->length == 0)
308 	{
309 		return;
310 	}
311 
312 	if (manager->priv->location == manager->priv->action_groups->tail)
313 	{
314 		manager->priv->location = NULL;
315 	}
316 
317 	if (manager->priv->has_saved_location)
318 	{
319 		if (manager->priv->saved_location == NULL)
320 		{
321 			manager->priv->has_saved_location = FALSE;
322 		}
323 		else if (manager->priv->saved_location == manager->priv->action_groups->tail)
324 		{
325 			manager->priv->saved_location = NULL;
326 		}
327 	}
328 
329 	group = g_queue_pop_tail (manager->priv->action_groups);
330 	action_group_free (group);
331 }
332 
333 static void
remove_first_action_group(GtkSourceUndoManagerDefault * manager)334 remove_first_action_group (GtkSourceUndoManagerDefault *manager)
335 {
336 	GList *first_node;
337 	ActionGroup *group;
338 
339 	first_node = manager->priv->action_groups->head;
340 
341 	if (first_node == NULL)
342 	{
343 		return;
344 	}
345 
346 	if (manager->priv->location == first_node)
347 	{
348 		manager->priv->location = first_node->next;
349 	}
350 
351 	if (manager->priv->has_saved_location &&
352 	    manager->priv->saved_location == first_node)
353 	{
354 		manager->priv->has_saved_location = FALSE;
355 	}
356 
357 	group = g_queue_pop_head (manager->priv->action_groups);
358 	action_group_free (group);
359 }
360 
361 static void
check_history_size(GtkSourceUndoManagerDefault * manager)362 check_history_size (GtkSourceUndoManagerDefault *manager)
363 {
364 	if (manager->priv->max_undo_levels == -1)
365 	{
366 		return;
367 	}
368 
369 	if (manager->priv->max_undo_levels == 0)
370 	{
371 		clear_all (manager);
372 		return;
373 	}
374 
375 	g_return_if_fail (manager->priv->max_undo_levels > 0);
376 
377 	while (manager->priv->action_groups->length > (guint)manager->priv->max_undo_levels)
378 	{
379 		/* Strip redo action groups first. */
380 		if (manager->priv->location != NULL)
381 		{
382 			remove_last_action_group (manager);
383 		}
384 		else
385 		{
386 			remove_first_action_group (manager);
387 		}
388 	}
389 
390 	update_can_undo_can_redo (manager);
391 }
392 
393 static void
remove_redo_action_groups(GtkSourceUndoManagerDefault * manager)394 remove_redo_action_groups (GtkSourceUndoManagerDefault *manager)
395 {
396 	while (manager->priv->location != NULL)
397 	{
398 		remove_last_action_group (manager);
399 	}
400 }
401 
402 /* Try to merge @new_group into @group. Returns TRUE if merged. It is up to the
403  * caller to free @new_group.
404  */
405 static gboolean
action_group_merge(ActionGroup * group,ActionGroup * new_group)406 action_group_merge (ActionGroup *group,
407 		    ActionGroup *new_group)
408 {
409 	Action *action;
410 	Action *new_action;
411 
412 	g_assert (group != NULL);
413 	g_assert (new_group != NULL);
414 
415 	if (new_group->actions->length == 0)
416 	{
417 		return TRUE;
418 	}
419 
420 	if (group->force_not_mergeable ||
421 	    new_group->force_not_mergeable ||
422 	    group->actions->length > 1 ||
423 	    new_group->actions->length > 1)
424 	{
425 		return FALSE;
426 	}
427 
428 	action = g_queue_peek_head (group->actions);
429 	new_action = g_queue_peek_head (new_group->actions);
430 
431 	return action_merge (action, new_action);
432 }
433 
434 /* Try to merge the new action group with the previous one (the one located on
435  * the left of priv->location). If the merge fails, a new node is inserted into
436  * the history.
437  */
438 static void
insert_new_action_group(GtkSourceUndoManagerDefault * manager)439 insert_new_action_group (GtkSourceUndoManagerDefault *manager)
440 {
441 	GList *prev_node = NULL;
442 	ActionGroup *prev_group = NULL;
443 	ActionGroup *new_group = manager->priv->new_action_group;
444 	gboolean can_merge = TRUE;
445 
446 	if (new_group == NULL || new_group->actions->length == 0)
447 	{
448 		return;
449 	}
450 
451 	remove_redo_action_groups (manager);
452 	g_assert (manager->priv->location == NULL);
453 
454 	prev_node = manager->priv->action_groups->tail;
455 
456 	if (prev_node != NULL)
457 	{
458 		prev_group = prev_node->data;
459 
460 		/* If the previous group is empty, it means that it was not correctly
461 		 * inserted into the history.
462 		 */
463 		g_assert_cmpuint (prev_group->actions->length, >, 0);
464 	}
465 
466 	/* If the saved_location is equal to the current location, the two
467 	 * ActionGroups cannot be merged, to not lose the saved_location.
468 	 */
469 	if (manager->priv->has_saved_location &&
470 	    manager->priv->saved_location == manager->priv->location)
471 	{
472 		g_assert (manager->priv->saved_location == NULL);
473 		can_merge = FALSE;
474 	}
475 
476 	if (can_merge &&
477 	    prev_group != NULL &&
478 	    action_group_merge (prev_group, new_group))
479 	{
480 		/* new_group merged into prev_group */
481 		action_group_free (manager->priv->new_action_group);
482 		manager->priv->new_action_group = NULL;
483 
484 		update_can_undo_can_redo (manager);
485 		return;
486 	}
487 
488 	g_queue_push_tail (manager->priv->action_groups, new_group);
489 	manager->priv->new_action_group = NULL;
490 
491 	if (manager->priv->has_saved_location &&
492 	    manager->priv->saved_location == NULL)
493 	{
494 		manager->priv->saved_location = manager->priv->action_groups->tail;
495 	}
496 
497 	/* "Archive" prev_group. It will never be mergeable again. If the user
498 	 * does some undo's to return to this location, a new action won't be
499 	 * merged with an "archived" action group.
500 	 */
501 	if (prev_group != NULL)
502 	{
503 		prev_group->force_not_mergeable = TRUE;
504 	}
505 
506 	check_history_size (manager);
507 	update_can_undo_can_redo (manager);
508 }
509 
510 static void
insert_action(GtkSourceUndoManagerDefault * manager,Action * new_action)511 insert_action (GtkSourceUndoManagerDefault *manager,
512 	       Action                      *new_action)
513 {
514 	ActionGroup *new_group;
515 
516 	g_assert (new_action != NULL);
517 
518 	if (manager->priv->new_action_group == NULL)
519 	{
520 		manager->priv->new_action_group = action_group_new ();
521 	}
522 
523 	new_group = manager->priv->new_action_group;
524 
525 	/* Inside a group, don't try to merge the actions. It is needed to keep
526 	 * them separate so when undoing or redoing, the cursor position is set
527 	 * at the right place.
528 	 * For example with the search and replace, we replace all occurrences
529 	 * of 'a' by '' (i.e. delete all a's). The text "aaba" becomes "b". On
530 	 * undo, the cursor position should be placed at "a|aba", not "aa|ba"
531 	 * (but it's a detail).
532 	 */
533 	g_queue_push_tail (new_group->actions, new_action);
534 
535 	/* An action is mergeable only for an insertion or deletion of a single
536 	 * character. If the text contains several characters, the new_action
537 	 * can for example come from a copy/paste.
538 	 */
539 	if (new_action->end - new_action->start > 1 ||
540 	    g_str_equal (new_action->text, "\n"))
541 	{
542 		new_group->force_not_mergeable = TRUE;
543 	}
544 
545 	if (!manager->priv->running_user_action)
546 	{
547 		insert_new_action_group (manager);
548 	}
549 }
550 
551 static void
delete_text(GtkTextBuffer * buffer,gint start,gint end)552 delete_text (GtkTextBuffer *buffer,
553 	     gint           start,
554 	     gint           end)
555 {
556 	GtkTextIter start_iter;
557 	GtkTextIter end_iter;
558 
559 	gtk_text_buffer_get_iter_at_offset (buffer, &start_iter, start);
560 	gtk_text_buffer_get_iter_at_offset (buffer, &end_iter, end);
561 
562 	gtk_text_buffer_begin_user_action (buffer);
563 	gtk_text_buffer_delete (buffer, &start_iter, &end_iter);
564 	gtk_text_buffer_end_user_action (buffer);
565 }
566 
567 static void
insert_text(GtkTextBuffer * buffer,gint offset,const gchar * text)568 insert_text (GtkTextBuffer *buffer,
569 	     gint           offset,
570 	     const gchar   *text)
571 {
572 	GtkTextIter iter;
573 
574 	gtk_text_buffer_get_iter_at_offset (buffer, &iter, offset);
575 
576 	gtk_text_buffer_begin_user_action (buffer);
577 	gtk_text_buffer_insert (buffer, &iter, text, -1);
578 	gtk_text_buffer_end_user_action (buffer);
579 }
580 
581 static gunichar
get_last_char(const gchar * text)582 get_last_char (const gchar *text)
583 {
584 	gchar *pos;
585 
586 	pos = g_utf8_find_prev_char (text, text + strlen (text));
587 
588 	if (pos == NULL)
589 	{
590 		return '\0';
591 	}
592 
593 	return g_utf8_get_char (pos);
594 }
595 
596 /* ActionInsert implementation */
597 
598 static void
action_insert_undo(GtkTextBuffer * buffer,Action * action)599 action_insert_undo (GtkTextBuffer *buffer,
600 		    Action        *action)
601 {
602 	g_assert_cmpint (action->type, ==, ACTION_TYPE_INSERT);
603 
604 	delete_text (buffer, action->start, action->end);
605 }
606 
607 static void
action_insert_redo(GtkTextBuffer * buffer,Action * action)608 action_insert_redo (GtkTextBuffer *buffer,
609 		    Action        *action)
610 {
611 	g_assert_cmpint (action->type, ==, ACTION_TYPE_INSERT);
612 
613 	insert_text (buffer, action->start, action->text);
614 }
615 
616 static gboolean
action_insert_merge(Action * action,Action * new_action)617 action_insert_merge (Action *action,
618 		     Action *new_action)
619 {
620 	gint new_text_length;
621 	gunichar new_char;
622 	gunichar last_char;
623 	gchar *merged_text;
624 
625 	g_assert_cmpint (action->type, ==, ACTION_TYPE_INSERT);
626 	g_assert_cmpint (new_action->type, ==, ACTION_TYPE_INSERT);
627 
628 	new_text_length = new_action->end - new_action->start;
629 	g_assert_cmpint (new_text_length, ==, 1);
630 
631 	new_char = g_utf8_get_char (new_action->text);
632 	g_assert (new_char != '\n');
633 
634 	if (action->end != new_action->start)
635 	{
636 		return FALSE;
637 	}
638 
639 	last_char = get_last_char (action->text);
640 
641 	/* If I type character by character the text "hello world", there will
642 	 * be two actions: "hello" and " world". If I click on undo, only
643 	 * "hello" remains, not the space. The space makes sense only when
644 	 * a second word is present.
645 	 * Note that the spaces or tabs at the beginning of a line (for code
646 	 * indentation) are removed with the first word of the line. For example
647 	 * if I type character by character "  return FALSE;", there are two
648 	 * actions: "  return" and " FALSE;". If I undo two times, maybe I still
649 	 * want the indentation. But with auto-indent, when we press Enter to
650 	 * create a newline, the indentation is part of the action that adds the
651 	 * newline, i.e. we have the three actions "\n  ", "return" and
652 	 * " FALSE;".
653 	 */
654 	if ((new_char == ' ' || new_char == '\t') &&
655 	    (last_char != ' ' && last_char != '\t'))
656 	{
657 		return FALSE;
658 	}
659 
660 	merged_text = g_strdup_printf ("%s%s", action->text, new_action->text);
661 
662 	g_free (action->text);
663 	action->text = merged_text;
664 
665 	action->end = new_action->end;
666 
667 	/* No need to update the selection, action->start is not modified. */
668 	g_assert ((action->selection_insert == -1 &&
669 		   action->selection_bound == -1) ||
670 		  (action->selection_insert == action->start &&
671 		   action->selection_bound == action->start));
672 
673 	return TRUE;
674 }
675 
676 static void
action_insert_restore_selection(GtkTextBuffer * buffer,Action * action,gboolean undo)677 action_insert_restore_selection (GtkTextBuffer *buffer,
678 				 Action        *action,
679 				 gboolean       undo)
680 {
681 	GtkTextIter iter;
682 
683 	g_assert_cmpint (action->type, ==, ACTION_TYPE_INSERT);
684 
685 	/* No need to take into account action->selection_insert and
686 	 * action->selection_bound, because:
687 	 * - If they are both -1, we still want to place the cursor correctly,
688 	 *   as done below, because if the cursor is not moved the user won't
689 	 *   see the modification.
690 	 * - If they are set, their values are both action->start, so the undo
691 	 *   works as expeceted in this case. The redo is also the expected
692 	 *   behavior because after inserting a character the cursor is _after_
693 	 *   the character, not before.
694 	 */
695 
696 	if (undo)
697 	{
698 		gtk_text_buffer_get_iter_at_offset (buffer, &iter, action->start);
699 	}
700 	else /* redo */
701 	{
702 		gtk_text_buffer_get_iter_at_offset (buffer, &iter, action->end);
703 	}
704 
705 	gtk_text_buffer_place_cursor (buffer, &iter);
706 }
707 
708 /* ActionDelete implementation */
709 
710 static void
action_delete_undo(GtkTextBuffer * buffer,Action * action)711 action_delete_undo (GtkTextBuffer *buffer,
712 		    Action        *action)
713 {
714 	g_assert_cmpint (action->type, ==, ACTION_TYPE_DELETE);
715 
716 	insert_text (buffer, action->start, action->text);
717 }
718 
719 static void
action_delete_redo(GtkTextBuffer * buffer,Action * action)720 action_delete_redo (GtkTextBuffer *buffer,
721 		    Action        *action)
722 {
723 	g_assert_cmpint (action->type, ==, ACTION_TYPE_DELETE);
724 
725 	delete_text (buffer, action->start, action->end);
726 }
727 
728 static DeletionType
get_deletion_type(Action * action)729 get_deletion_type (Action *action)
730 {
731 	g_assert_cmpint (action->type, ==, ACTION_TYPE_DELETE);
732 
733 	if (action->selection_insert == -1)
734 	{
735 		g_assert_cmpint (action->selection_bound, ==, -1);
736 		return DELETION_TYPE_PROGRAMMATICALLY;
737 	}
738 
739 	if (action->selection_insert == action->end &&
740 	    action->selection_bound == action->end)
741 	{
742 		return DELETION_TYPE_BACKSPACE_KEY;
743 	}
744 
745 	if (action->selection_insert == action->start &&
746 	    action->selection_bound == action->start)
747 	{
748 		return DELETION_TYPE_DELETE_KEY;
749 	}
750 
751 	g_assert (action->selection_insert == action->start ||
752 		  action->selection_insert == action->end);
753 	g_assert (action->selection_bound == action->start ||
754 		  action->selection_bound == action->end);
755 
756 	return DELETION_TYPE_SELECTION_DELETED;
757 }
758 
759 static gboolean
action_delete_merge(Action * action,Action * new_action)760 action_delete_merge (Action *action,
761 		     Action *new_action)
762 {
763 	gint new_text_length;
764 	gunichar new_char;
765 	DeletionType deletion_type;
766 	DeletionType new_deletion_type;
767 
768 	g_assert_cmpint (action->type, ==, ACTION_TYPE_DELETE);
769 	g_assert_cmpint (new_action->type, ==, ACTION_TYPE_DELETE);
770 
771 	new_text_length = new_action->end - new_action->start;
772 	g_assert_cmpint (new_text_length, ==, 1);
773 
774 	new_char = g_utf8_get_char (new_action->text);
775 	g_assert (new_char != '\n');
776 
777 	deletion_type = get_deletion_type (action);
778 	new_deletion_type = get_deletion_type (new_action);
779 
780 	if (deletion_type != new_deletion_type)
781 	{
782 		return FALSE;
783 	}
784 
785 	switch (deletion_type)
786 	{
787 		/* If the user has selected some text and then has deleted it,
788 		 * it should be seen as a single action group, not mergeable.  A
789 		 * good reason for that is to correctly restore the selection.
790 		 */
791 		case DELETION_TYPE_SELECTION_DELETED:
792 			return FALSE;
793 
794 		/* For memory use it would be better to take it into account,
795 		 * but the code is simpler like that.
796 		 */
797 		case DELETION_TYPE_PROGRAMMATICALLY:
798 			return FALSE;
799 
800 		/* Two Backspaces or two Deletes must follow each other. In
801 		 * "abc", if the cursor is at offset 2 and I press the Backspace
802 		 * key, then move the cursor after 'c' and press Backspace
803 		 * again, the two deletes won't be merged, since there was a
804 		 * cursor movement in between.
805 		 */
806 
807 		case DELETION_TYPE_DELETE_KEY:
808 			/* Not consecutive deletes. */
809 			if (action->start != new_action->start)
810 			{
811 				return FALSE;
812 			}
813 			break;
814 
815 		case DELETION_TYPE_BACKSPACE_KEY:
816 			/* Not consecutive backspaces. */
817 			if (action->start != new_action->end)
818 			{
819 				return FALSE;
820 			}
821 			break;
822 
823 		default:
824 			g_assert_not_reached ();
825 	}
826 
827 	/* Delete key pressed several times. */
828 	if (action->start == new_action->start)
829 	{
830 		gunichar last_char;
831 		gchar *merged_text;
832 
833 		last_char = get_last_char (action->text);
834 
835 		/* Same as action_insert_merge(). */
836 		if ((new_char == ' ' || new_char == '\t') &&
837 		    (last_char != ' ' && last_char != '\t'))
838 		{
839 			return FALSE;
840 		}
841 
842 		merged_text = g_strdup_printf ("%s%s", action->text, new_action->text);
843 
844 		g_free (action->text);
845 		action->text = merged_text;
846 
847 		action->end += new_text_length;
848 
849 		/* No need to update the selection, action->start is not
850 		 * modified.
851 		 */
852 		g_assert_cmpint (action->selection_insert, ==, action->start);
853 		g_assert_cmpint (action->selection_bound, ==, action->start);
854 
855 		return TRUE;
856 	}
857 
858 	/* Backspace key pressed several times. */
859 	if (action->start == new_action->end)
860 	{
861 		gunichar last_char;
862 		gchar *merged_text;
863 
864 		/* The last char deleted, but since it's with the Backspace key,
865 		 * it's the first char in action->text.
866 		 */
867 		last_char = g_utf8_get_char (action->text);
868 
869 		/* Same as action_insert_merge(). */
870 		if ((new_char != ' ' && new_char != '\t') &&
871 		    (last_char == ' ' || last_char == '\t'))
872 		{
873 			return FALSE;
874 		}
875 
876 		merged_text = g_strdup_printf ("%s%s", new_action->text, action->text);
877 
878 		g_free (action->text);
879 		action->text = merged_text;
880 
881 		action->start = new_action->start;
882 
883 		/* No need to update the selection, action->end is not modified. */
884 		g_assert_cmpint (action->selection_insert, ==, action->end);
885 		g_assert_cmpint (action->selection_bound, ==, action->end);
886 
887 		return TRUE;
888 	}
889 
890 	g_assert_not_reached ();
891 	return FALSE;
892 }
893 
894 static void
action_delete_restore_selection(GtkTextBuffer * buffer,Action * action,gboolean undo)895 action_delete_restore_selection (GtkTextBuffer *buffer,
896 				 Action        *action,
897 				 gboolean       undo)
898 {
899 
900 	g_assert_cmpint (action->type, ==, ACTION_TYPE_DELETE);
901 
902 	if (undo)
903 	{
904 		if (action->selection_insert == -1)
905 		{
906 			GtkTextIter iter;
907 
908 			g_assert_cmpint (action->selection_bound, ==, -1);
909 
910 			gtk_text_buffer_get_iter_at_offset (buffer, &iter, action->end);
911 			gtk_text_buffer_place_cursor (buffer, &iter);
912 		}
913 		else
914 		{
915 			GtkTextIter insert_iter;
916 			GtkTextIter bound_iter;
917 
918 			gtk_text_buffer_get_iter_at_offset (buffer,
919 							    &insert_iter,
920 							    action->selection_insert);
921 
922 			gtk_text_buffer_get_iter_at_offset (buffer,
923 							    &bound_iter,
924 							    action->selection_bound);
925 
926 			gtk_text_buffer_select_range (buffer, &insert_iter, &bound_iter);
927 		}
928 	}
929 	else /* redo */
930 	{
931 		GtkTextIter iter;
932 
933 		gtk_text_buffer_get_iter_at_offset (buffer, &iter, action->start);
934 		gtk_text_buffer_place_cursor (buffer, &iter);
935 	}
936 }
937 
938 /* Action interface.
939  * The Action struct can be seen as an interface. All the explicit case analysis
940  * on the action type are grouped in this code section. This can easily be
941  * modified as an object-oriented architecture with polymorphism.
942  */
943 
944 static void
action_undo(GtkTextBuffer * buffer,Action * action)945 action_undo (GtkTextBuffer *buffer,
946 	     Action        *action)
947 {
948 	g_assert (action != NULL);
949 
950 	switch (action->type)
951 	{
952 		case ACTION_TYPE_INSERT:
953 			action_insert_undo (buffer, action);
954 			break;
955 
956 		case ACTION_TYPE_DELETE:
957 			action_delete_undo (buffer, action);
958 			break;
959 
960 		default:
961 			g_return_if_reached ();
962 			break;
963 	}
964 }
965 
966 static void
action_redo(GtkTextBuffer * buffer,Action * action)967 action_redo (GtkTextBuffer *buffer,
968 	     Action        *action)
969 {
970 	g_assert (action != NULL);
971 
972 	switch (action->type)
973 	{
974 		case ACTION_TYPE_INSERT:
975 			action_insert_redo (buffer, action);
976 			break;
977 
978 		case ACTION_TYPE_DELETE:
979 			action_delete_redo (buffer, action);
980 			break;
981 
982 		default:
983 			g_return_if_reached ();
984 			break;
985 	}
986 }
987 
988 /* Try to merge @new_action into @action. Returns TRUE if merged. It is up to
989  * the caller to free @new_action if needed.
990  */
991 static gboolean
action_merge(Action * action,Action * new_action)992 action_merge (Action *action,
993 	      Action *new_action)
994 {
995 	g_assert (action != NULL);
996 	g_assert (new_action != NULL);
997 
998 	if (action->type != new_action->type)
999 	{
1000 		return FALSE;
1001 	}
1002 
1003 	switch (action->type)
1004 	{
1005 		case ACTION_TYPE_INSERT:
1006 			return action_insert_merge (action, new_action);
1007 
1008 		case ACTION_TYPE_DELETE:
1009 			return action_delete_merge (action, new_action);
1010 
1011 		default:
1012 			g_return_val_if_reached (FALSE);
1013 			break;
1014 	}
1015 }
1016 
1017 /* Restore the selection (or cursor position) according to @action.
1018  * If @undo is TRUE, @action has just been undone. If @undo is FALSE, @action
1019  * has just been redone.
1020  */
1021 static void
action_restore_selection(GtkTextBuffer * buffer,Action * action,gboolean undo)1022 action_restore_selection (GtkTextBuffer *buffer,
1023 			  Action        *action,
1024 			  gboolean       undo)
1025 {
1026 	g_assert (action != NULL);
1027 
1028 	switch (action->type)
1029 	{
1030 		case ACTION_TYPE_INSERT:
1031 			action_insert_restore_selection (buffer, action, undo);
1032 			break;
1033 
1034 		case ACTION_TYPE_DELETE:
1035 			action_delete_restore_selection (buffer, action, undo);
1036 			break;
1037 
1038 		default:
1039 			g_return_if_reached ();
1040 			break;
1041 	}
1042 }
1043 
1044 /* Buffer signal handlers */
1045 
1046 static void
set_selection_bounds(GtkTextBuffer * buffer,Action * action)1047 set_selection_bounds (GtkTextBuffer *buffer,
1048 		      Action        *action)
1049 {
1050 	GtkTextMark *insert_mark;
1051 	GtkTextMark *bound_mark;
1052 	GtkTextIter insert_iter;
1053 	GtkTextIter bound_iter;
1054 
1055 	insert_mark = gtk_text_buffer_get_insert (buffer);
1056 	bound_mark = gtk_text_buffer_get_selection_bound (buffer);
1057 
1058 	gtk_text_buffer_get_iter_at_mark (buffer, &insert_iter, insert_mark);
1059 	gtk_text_buffer_get_iter_at_mark (buffer, &bound_iter, bound_mark);
1060 
1061 	action->selection_insert = gtk_text_iter_get_offset (&insert_iter);
1062 	action->selection_bound = gtk_text_iter_get_offset (&bound_iter);
1063 }
1064 
1065 static void
insert_text_cb(GtkTextBuffer * buffer,GtkTextIter * location,const gchar * text,gint length,GtkSourceUndoManagerDefault * manager)1066 insert_text_cb (GtkTextBuffer               *buffer,
1067 		GtkTextIter                 *location,
1068 		const gchar                 *text,
1069 		gint                         length,
1070 		GtkSourceUndoManagerDefault *manager)
1071 {
1072 	Action *action = action_new ();
1073 
1074 	action->type = ACTION_TYPE_INSERT;
1075 	action->start = gtk_text_iter_get_offset (location);
1076 	action->text = g_strndup (text, length);
1077 	action->end = action->start + g_utf8_strlen (action->text, -1);
1078 
1079 	set_selection_bounds (buffer, action);
1080 
1081 	if (action->selection_insert != action->selection_bound ||
1082 	    action->selection_insert != action->start)
1083 	{
1084 		action->selection_insert = -1;
1085 		action->selection_bound = -1;
1086 	}
1087 	else
1088 	{
1089 		/* The insertion occurred at the cursor. */
1090 		g_assert_cmpint (action->selection_insert, ==, action->start);
1091 		g_assert_cmpint (action->selection_bound, ==, action->start);
1092 	}
1093 
1094 	insert_action (manager, action);
1095 }
1096 
1097 static void
delete_range_cb(GtkTextBuffer * buffer,GtkTextIter * start,GtkTextIter * end,GtkSourceUndoManagerDefault * manager)1098 delete_range_cb (GtkTextBuffer               *buffer,
1099 		 GtkTextIter                 *start,
1100 		 GtkTextIter                 *end,
1101 		 GtkSourceUndoManagerDefault *manager)
1102 {
1103 	Action *action = action_new ();
1104 
1105 	action->type = ACTION_TYPE_DELETE;
1106 	action->start = gtk_text_iter_get_offset (start);
1107 	action->end = gtk_text_iter_get_offset (end);
1108 	action->text = gtk_text_buffer_get_slice (buffer, start, end, TRUE);
1109 
1110 	g_assert_cmpint (action->start, <, action->end);
1111 
1112 	set_selection_bounds (buffer, action);
1113 
1114 	if ((action->selection_insert != action->start &&
1115 	     action->selection_insert != action->end) ||
1116 	    (action->selection_bound != action->start &&
1117 	     action->selection_bound != action->end))
1118 	{
1119 		action->selection_insert = -1;
1120 		action->selection_bound = -1;
1121 	}
1122 
1123 	insert_action (manager, action);
1124 }
1125 
1126 static void
begin_user_action_cb(GtkTextBuffer * buffer,GtkSourceUndoManagerDefault * manager)1127 begin_user_action_cb (GtkTextBuffer               *buffer,
1128 		      GtkSourceUndoManagerDefault *manager)
1129 {
1130 	manager->priv->running_user_action = TRUE;
1131 	update_can_undo_can_redo (manager);
1132 }
1133 
1134 static void
end_user_action_cb(GtkTextBuffer * buffer,GtkSourceUndoManagerDefault * manager)1135 end_user_action_cb (GtkTextBuffer               *buffer,
1136 		    GtkSourceUndoManagerDefault *manager)
1137 {
1138 	insert_new_action_group (manager);
1139 
1140 	manager->priv->running_user_action = FALSE;
1141 	update_can_undo_can_redo (manager);
1142 }
1143 
1144 static void
modified_changed_cb(GtkTextBuffer * buffer,GtkSourceUndoManagerDefault * manager)1145 modified_changed_cb (GtkTextBuffer               *buffer,
1146 		     GtkSourceUndoManagerDefault *manager)
1147 {
1148 	if (gtk_text_buffer_get_modified (buffer))
1149 	{
1150 		/* It can happen for example when the file on disk has been
1151 		 * deleted.
1152 		 */
1153 		if (manager->priv->has_saved_location &&
1154 		    manager->priv->saved_location == manager->priv->location &&
1155 		    (manager->priv->new_action_group == NULL ||
1156 		     manager->priv->new_action_group->actions->length == 0))
1157 		{
1158 			manager->priv->has_saved_location = FALSE;
1159 		}
1160 	}
1161 
1162 	/* saved */
1163 	else
1164 	{
1165 		/* Saving a buffer during a user action is allowed, the user
1166 		 * action is split.
1167 		 * FIXME and/or a warning should be printed?
1168 		 */
1169 		if (manager->priv->running_user_action)
1170 		{
1171 			insert_new_action_group (manager);
1172 		}
1173 
1174 		manager->priv->saved_location = manager->priv->location;
1175 		manager->priv->has_saved_location = TRUE;
1176 	}
1177 }
1178 
1179 static void
block_signal_handlers(GtkSourceUndoManagerDefault * manager)1180 block_signal_handlers (GtkSourceUndoManagerDefault *manager)
1181 {
1182 	if (manager->priv->buffer == NULL)
1183 	{
1184 		return;
1185 	}
1186 
1187 	g_signal_handlers_block_by_func (manager->priv->buffer,
1188 					 insert_text_cb,
1189 					 manager);
1190 
1191 	g_signal_handlers_block_by_func (manager->priv->buffer,
1192 					 delete_range_cb,
1193 					 manager);
1194 
1195 	g_signal_handlers_block_by_func (manager->priv->buffer,
1196 					 modified_changed_cb,
1197 					 manager);
1198 }
1199 
1200 static void
unblock_signal_handlers(GtkSourceUndoManagerDefault * manager)1201 unblock_signal_handlers (GtkSourceUndoManagerDefault *manager)
1202 {
1203 	if (manager->priv->buffer == NULL)
1204 	{
1205 		return;
1206 	}
1207 
1208 	g_signal_handlers_unblock_by_func (manager->priv->buffer,
1209 					   insert_text_cb,
1210 					   manager);
1211 
1212 	g_signal_handlers_unblock_by_func (manager->priv->buffer,
1213 					   delete_range_cb,
1214 					   manager);
1215 
1216 	g_signal_handlers_unblock_by_func (manager->priv->buffer,
1217 					   modified_changed_cb,
1218 					   manager);
1219 }
1220 
1221 static void
set_buffer(GtkSourceUndoManagerDefault * manager,GtkTextBuffer * buffer)1222 set_buffer (GtkSourceUndoManagerDefault *manager,
1223             GtkTextBuffer               *buffer)
1224 {
1225 	g_assert (manager->priv->buffer == NULL);
1226 
1227 	if (buffer == NULL)
1228 	{
1229 		return;
1230 	}
1231 
1232 	manager->priv->buffer = buffer;
1233 
1234 	g_object_add_weak_pointer (G_OBJECT (buffer),
1235 				   (gpointer *)&manager->priv->buffer);
1236 
1237 	g_signal_connect_object (buffer,
1238 				 "insert-text",
1239 				 G_CALLBACK (insert_text_cb),
1240 				 manager,
1241 				 0);
1242 
1243 	g_signal_connect_object (buffer,
1244 				 "delete-range",
1245 				 G_CALLBACK (delete_range_cb),
1246 				 manager,
1247 				 0);
1248 
1249 	g_signal_connect_object (buffer,
1250 				 "begin-user-action",
1251 				 G_CALLBACK (begin_user_action_cb),
1252 				 manager,
1253 				 0);
1254 
1255 	g_signal_connect_object (buffer,
1256 				 "end-user-action",
1257 				 G_CALLBACK (end_user_action_cb),
1258 				 manager,
1259 				 0);
1260 
1261 	g_signal_connect_object (buffer,
1262 				 "modified-changed",
1263 				 G_CALLBACK (modified_changed_cb),
1264 				 manager,
1265 				 0);
1266 
1267 	modified_changed_cb (manager->priv->buffer, manager);
1268 }
1269 
1270 /* GObject construction, destruction and properties */
1271 
1272 static void
gtk_source_undo_manager_default_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)1273 gtk_source_undo_manager_default_set_property (GObject      *object,
1274                                               guint         prop_id,
1275                                               const GValue *value,
1276                                               GParamSpec   *pspec)
1277 {
1278 	GtkSourceUndoManagerDefault *manager = GTK_SOURCE_UNDO_MANAGER_DEFAULT (object);
1279 
1280 	switch (prop_id)
1281 	{
1282 		case PROP_BUFFER:
1283 			set_buffer (manager, g_value_get_object (value));
1284 			break;
1285 
1286 		case PROP_MAX_UNDO_LEVELS:
1287 			gtk_source_undo_manager_default_set_max_undo_levels (manager, g_value_get_int (value));
1288 			break;
1289 
1290 		default:
1291 			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1292 			break;
1293 	}
1294 }
1295 
1296 static void
gtk_source_undo_manager_default_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)1297 gtk_source_undo_manager_default_get_property (GObject    *object,
1298                                               guint       prop_id,
1299                                               GValue     *value,
1300                                               GParamSpec *pspec)
1301 {
1302 	GtkSourceUndoManagerDefault *manager = GTK_SOURCE_UNDO_MANAGER_DEFAULT (object);
1303 
1304 	switch (prop_id)
1305 	{
1306 		case PROP_BUFFER:
1307 			g_value_set_object (value, manager->priv->buffer);
1308 			break;
1309 
1310 		case PROP_MAX_UNDO_LEVELS:
1311 			g_value_set_int (value, manager->priv->max_undo_levels);
1312 			break;
1313 
1314 		default:
1315 			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1316 			break;
1317 	}
1318 }
1319 
1320 static void
gtk_source_undo_manager_default_dispose(GObject * object)1321 gtk_source_undo_manager_default_dispose (GObject *object)
1322 {
1323 	GtkSourceUndoManagerDefault *manager = GTK_SOURCE_UNDO_MANAGER_DEFAULT (object);
1324 
1325 	if (manager->priv->buffer != NULL)
1326 	{
1327 		g_object_remove_weak_pointer (G_OBJECT (manager->priv->buffer),
1328 					      (gpointer *)&manager->priv->buffer);
1329 
1330 		manager->priv->buffer = NULL;
1331 	}
1332 
1333 	G_OBJECT_CLASS (gtk_source_undo_manager_default_parent_class)->dispose (object);
1334 }
1335 
1336 static void
gtk_source_undo_manager_default_finalize(GObject * object)1337 gtk_source_undo_manager_default_finalize (GObject *object)
1338 {
1339 	GtkSourceUndoManagerDefault *manager = GTK_SOURCE_UNDO_MANAGER_DEFAULT (object);
1340 
1341 	g_queue_free_full (manager->priv->action_groups,
1342 			   (GDestroyNotify) action_group_free);
1343 
1344 	action_group_free (manager->priv->new_action_group);
1345 
1346 	G_OBJECT_CLASS (gtk_source_undo_manager_default_parent_class)->finalize (object);
1347 }
1348 
1349 static void
gtk_source_undo_manager_default_class_init(GtkSourceUndoManagerDefaultClass * klass)1350 gtk_source_undo_manager_default_class_init (GtkSourceUndoManagerDefaultClass *klass)
1351 {
1352 	GObjectClass *object_class = G_OBJECT_CLASS (klass);
1353 
1354 	object_class->set_property = gtk_source_undo_manager_default_set_property;
1355 	object_class->get_property = gtk_source_undo_manager_default_get_property;
1356 	object_class->dispose = gtk_source_undo_manager_default_dispose;
1357 	object_class->finalize = gtk_source_undo_manager_default_finalize;
1358 
1359 	g_object_class_install_property (object_class,
1360 	                                 PROP_BUFFER,
1361 	                                 g_param_spec_object ("buffer",
1362 	                                                      "Buffer",
1363 	                                                      "The text buffer to add undo support on",
1364 	                                                      GTK_TYPE_TEXT_BUFFER,
1365 	                                                      G_PARAM_READWRITE |
1366 							      G_PARAM_CONSTRUCT_ONLY |
1367 							      G_PARAM_STATIC_STRINGS));
1368 
1369 	g_object_class_install_property (object_class,
1370 	                                 PROP_MAX_UNDO_LEVELS,
1371 	                                 g_param_spec_int ("max-undo-levels",
1372 	                                                   "Max Undo Levels",
1373 	                                                   "Number of undo levels for the buffer",
1374 	                                                   -1,
1375 	                                                   G_MAXINT,
1376 	                                                   DEFAULT_MAX_UNDO_LEVELS,
1377 	                                                   G_PARAM_READWRITE |
1378 							   G_PARAM_STATIC_STRINGS));
1379 }
1380 
1381 static void
gtk_source_undo_manager_default_init(GtkSourceUndoManagerDefault * manager)1382 gtk_source_undo_manager_default_init (GtkSourceUndoManagerDefault *manager)
1383 {
1384 	manager->priv = gtk_source_undo_manager_default_get_instance_private (manager);
1385 
1386 	manager->priv->action_groups = g_queue_new ();
1387 	manager->priv->max_undo_levels = DEFAULT_MAX_UNDO_LEVELS;
1388 }
1389 
1390 /* Interface implementation */
1391 
1392 static gboolean
gtk_source_undo_manager_can_undo_impl(GtkSourceUndoManager * undo_manager)1393 gtk_source_undo_manager_can_undo_impl (GtkSourceUndoManager *undo_manager)
1394 {
1395 	GtkSourceUndoManagerDefault *manager = GTK_SOURCE_UNDO_MANAGER_DEFAULT (undo_manager);
1396 	return manager->priv->can_undo;
1397 }
1398 
1399 static gboolean
gtk_source_undo_manager_can_redo_impl(GtkSourceUndoManager * undo_manager)1400 gtk_source_undo_manager_can_redo_impl (GtkSourceUndoManager *undo_manager)
1401 {
1402 	GtkSourceUndoManagerDefault *manager = GTK_SOURCE_UNDO_MANAGER_DEFAULT (undo_manager);
1403 	return manager->priv->can_redo;
1404 }
1405 
1406 static void
restore_modified_state(GtkSourceUndoManagerDefault * manager,GList * old_location,GList * new_location)1407 restore_modified_state (GtkSourceUndoManagerDefault *manager,
1408 			GList                       *old_location,
1409 			GList                       *new_location)
1410 {
1411 	if (manager->priv->has_saved_location)
1412 	{
1413 		if (old_location == manager->priv->saved_location)
1414 		{
1415 			gtk_text_buffer_set_modified (manager->priv->buffer, TRUE);
1416 		}
1417 		else if (new_location == manager->priv->saved_location)
1418 		{
1419 			gtk_text_buffer_set_modified (manager->priv->buffer, FALSE);
1420 		}
1421 	}
1422 }
1423 
1424 static void
gtk_source_undo_manager_undo_impl(GtkSourceUndoManager * undo_manager)1425 gtk_source_undo_manager_undo_impl (GtkSourceUndoManager *undo_manager)
1426 {
1427 	GtkSourceUndoManagerDefault *manager = GTK_SOURCE_UNDO_MANAGER_DEFAULT (undo_manager);
1428 	GList *old_location;
1429 	GList *new_location;
1430 	ActionGroup *group;
1431 	Action *action;
1432 	GList *l;
1433 
1434 	g_return_if_fail (manager->priv->can_undo);
1435 
1436 	old_location = manager->priv->location;
1437 
1438 	if (old_location != NULL)
1439 	{
1440 		new_location = manager->priv->location->prev;
1441 	}
1442 	else
1443 	{
1444 		new_location = manager->priv->action_groups->tail;
1445 	}
1446 
1447 	g_assert (new_location != NULL);
1448 
1449 	group = new_location->data;
1450 	g_assert_cmpuint (group->actions->length, >, 0);
1451 
1452 	block_signal_handlers (manager);
1453 
1454 	for (l = group->actions->tail; l != NULL; l = l->prev)
1455 	{
1456 		action = l->data;
1457 		action_undo (manager->priv->buffer, action);
1458 	}
1459 
1460 	restore_modified_state (manager, old_location, new_location);
1461 
1462 	/* After an undo, place the cursor at the first action in the group. For
1463 	 * a search and replace, it will be the first occurrence in the buffer.
1464 	 */
1465 	action = g_queue_peek_head (group->actions);
1466 	action_restore_selection (manager->priv->buffer, action, TRUE);
1467 
1468 	unblock_signal_handlers (manager);
1469 
1470 	manager->priv->location = new_location;
1471 	update_can_undo_can_redo (manager);
1472 }
1473 
1474 static void
gtk_source_undo_manager_redo_impl(GtkSourceUndoManager * undo_manager)1475 gtk_source_undo_manager_redo_impl (GtkSourceUndoManager *undo_manager)
1476 {
1477 	GtkSourceUndoManagerDefault *manager = GTK_SOURCE_UNDO_MANAGER_DEFAULT (undo_manager);
1478 	GList *old_location;
1479 	GList *new_location;
1480 	ActionGroup *group;
1481 	GList *l;
1482 
1483 	g_return_if_fail (manager->priv->can_redo);
1484 
1485 	old_location = manager->priv->location;
1486 	g_assert (old_location != NULL);
1487 
1488 	new_location = old_location->next;
1489 
1490 	group = old_location->data;
1491 
1492 	block_signal_handlers (manager);
1493 
1494 	for (l = group->actions->head; l != NULL; l = l->next)
1495 	{
1496 		Action *action = l->data;
1497 		action_redo (manager->priv->buffer, action);
1498 
1499 		/* For a redo, place the cursor at the first action in the
1500 		 * group. For an undo the first action is also chosen, so when
1501 		 * undoing/redoing a search and replace, the cursor position
1502 		 * stays at the first occurrence and the user can see the
1503 		 * replacement easily.
1504 		 * For a redo, if we choose the last action in the group, when
1505 		 * undoing/redoing a search and replace, the cursor position
1506 		 * will jump between the first occurrence and the last
1507 		 * occurrence. Staying at the same place is probably better.
1508 		 */
1509 		if (l == group->actions->head)
1510 		{
1511 			action_restore_selection (manager->priv->buffer, action, FALSE);
1512 		}
1513 	}
1514 
1515 	restore_modified_state (manager, old_location, new_location);
1516 
1517 	unblock_signal_handlers (manager);
1518 
1519 	manager->priv->location = new_location;
1520 	update_can_undo_can_redo (manager);
1521 }
1522 
1523 static void
gtk_source_undo_manager_begin_not_undoable_action_impl(GtkSourceUndoManager * undo_manager)1524 gtk_source_undo_manager_begin_not_undoable_action_impl (GtkSourceUndoManager *undo_manager)
1525 {
1526 	GtkSourceUndoManagerDefault *manager = GTK_SOURCE_UNDO_MANAGER_DEFAULT (undo_manager);
1527 	manager->priv->running_not_undoable_actions++;
1528 
1529 	if (manager->priv->running_not_undoable_actions == 1)
1530 	{
1531 		block_signal_handlers (manager);
1532 	}
1533 }
1534 
1535 static void
gtk_source_undo_manager_end_not_undoable_action_impl(GtkSourceUndoManager * undo_manager)1536 gtk_source_undo_manager_end_not_undoable_action_impl (GtkSourceUndoManager *undo_manager)
1537 {
1538 	GtkSourceUndoManagerDefault *manager = GTK_SOURCE_UNDO_MANAGER_DEFAULT (undo_manager);
1539 
1540 	g_return_if_fail (manager->priv->running_not_undoable_actions > 0);
1541 
1542 	manager->priv->running_not_undoable_actions--;
1543 
1544 	if (manager->priv->running_not_undoable_actions == 0)
1545 	{
1546 		unblock_signal_handlers (manager);
1547 		clear_all (manager);
1548 		modified_changed_cb (manager->priv->buffer, manager);
1549 	}
1550 }
1551 
1552 static void
gtk_source_undo_manager_iface_init(GtkSourceUndoManagerIface * iface)1553 gtk_source_undo_manager_iface_init (GtkSourceUndoManagerIface *iface)
1554 {
1555 	iface->can_undo = gtk_source_undo_manager_can_undo_impl;
1556 	iface->can_redo = gtk_source_undo_manager_can_redo_impl;
1557 	iface->undo = gtk_source_undo_manager_undo_impl;
1558 	iface->redo = gtk_source_undo_manager_redo_impl;
1559 	iface->begin_not_undoable_action = gtk_source_undo_manager_begin_not_undoable_action_impl;
1560 	iface->end_not_undoable_action = gtk_source_undo_manager_end_not_undoable_action_impl;
1561 }
1562 
1563 /* Public functions */
1564 
1565 void
gtk_source_undo_manager_default_set_max_undo_levels(GtkSourceUndoManagerDefault * manager,gint max_undo_levels)1566 gtk_source_undo_manager_default_set_max_undo_levels (GtkSourceUndoManagerDefault *manager,
1567                                                      gint                         max_undo_levels)
1568 {
1569 	g_return_if_fail (GTK_SOURCE_IS_UNDO_MANAGER_DEFAULT (manager));
1570 	g_return_if_fail (max_undo_levels >= -1);
1571 
1572 	if (manager->priv->max_undo_levels != max_undo_levels)
1573 	{
1574 		if (max_undo_levels == 0)
1575 		{
1576 			/* disable the undo manager */
1577 			block_signal_handlers (manager);
1578 		}
1579 		else if (manager->priv->max_undo_levels == 0)
1580 		{
1581 			unblock_signal_handlers (manager);
1582 			modified_changed_cb (manager->priv->buffer, manager);
1583 		}
1584 
1585 		manager->priv->max_undo_levels = max_undo_levels;
1586 		check_history_size (manager);
1587 
1588 		g_object_notify (G_OBJECT (manager), "max-undo-levels");
1589 	}
1590 }
1591