1 /* logjam - a GTK client for LiveJournal.
2 * Copyright (C) 2000-2003 Evan Martin <evan@livejournal.com>
3 * Ported to LogJam by Ari Pollak <ari@debian.org> in May 2003
5 * This file is based on gedit-undo-manager.c from gEdit 2.2.1.
7 * Copyright (C) 1998, 1999 Alex Roberts, Evan Lawrence
8 * Copyright (C) 2000, 2001 Chema Celorio, Paolo Maggi
17 typedef struct _UndoAction UndoAction
;
18 typedef struct _UndoInsertAction UndoInsertAction
;
19 typedef struct _UndoDeleteAction UndoDeleteAction
;
27 * We use offsets instead of GtkTextIters because the last ones
28 * require to much memory in this context without giving us any advantage.
31 struct _UndoInsertAction
39 struct _UndoDeleteAction
48 UndoActionType action_type
;
51 UndoInsertAction insert
;
52 UndoDeleteAction
delete;
62 struct _UndoMgrPrivate
64 GList
* widgets
; /* queue of all widgets which are attached */
69 gint actions_in_current_group
;
74 gint running_not_undoable_actions
;
85 static void undomgr_class_init (UndoMgrClass
*klass
);
86 static void undomgr_init (UndoMgr
*um
);
87 static void undomgr_finalize (GObject
*object
);
89 static void undomgr_textbuffer_insert_text_handler (GtkTextBuffer
*buffer
, GtkTextIter
*pos
,
90 const gchar
*text
, gint length
,
92 static void undomgr_textbuffer_delete_range_handler (GtkTextBuffer
*buffer
, GtkTextIter
*start
,
93 GtkTextIter
*end
, UndoMgr
*um
);
94 static void undomgr_textbuffer_begin_user_action_handler (GtkTextBuffer
*buffer
, UndoMgr
*um
);
95 static void undomgr_textbuffer_end_user_action_handler (GtkTextBuffer
*buffer
, UndoMgr
*um
);
97 static void undomgr_free_action_list (UndoMgr
*um
);
99 static void undomgr_free_widget (UndoMgr
*um
, GtkWidget
*w
);
100 static void undomgr_free_widget_list (UndoMgr
*um
);
102 static void undomgr_add_textbuffer_action (UndoMgr
*um
,
103 UndoAction undo_action
,
104 GtkTextBuffer
*buffer
);
106 static gboolean
undomgr_merge_textbuffer_action (UndoMgr
*um
,
107 UndoAction
*undo_action
,
108 GtkTextBuffer
*buffer
);
110 static void undomgr_free_first_n_actions (UndoMgr
*um
, gint n
);
111 static void undomgr_check_list_size (UndoMgr
*um
);
115 static GObjectClass
*parent_class
= NULL
;
116 static guint undomgr_signals
[LAST_SIGNAL
] = { 0 };
119 undomgr_get_type (void)
121 static GType undomgr_type
= 0;
123 if (undomgr_type
== 0)
125 static const GTypeInfo our_info
=
127 sizeof (UndoMgrClass
),
128 NULL
, /* base_init */
129 NULL
, /* base_finalize */
130 (GClassInitFunc
) undomgr_class_init
,
131 NULL
, /* class_finalize */
132 NULL
, /* class_data */
135 (GInstanceInitFunc
) undomgr_init
138 undomgr_type
= g_type_register_static (G_TYPE_OBJECT
,
148 undomgr_class_init (UndoMgrClass
*klass
)
150 GObjectClass
*object_class
= G_OBJECT_CLASS (klass
);
152 parent_class
= g_type_class_peek_parent (klass
);
154 object_class
->finalize
= undomgr_finalize
;
156 klass
->can_undo
= NULL
;
157 klass
->can_redo
= NULL
;
159 undomgr_signals
[CAN_UNDO
] =
160 g_signal_new ("can_undo",
161 G_OBJECT_CLASS_TYPE (object_class
),
163 G_STRUCT_OFFSET (UndoMgrClass
, can_undo
),
165 g_cclosure_marshal_VOID__BOOLEAN
,
170 undomgr_signals
[CAN_REDO
] =
171 g_signal_new ("can_redo",
172 G_OBJECT_CLASS_TYPE (object_class
),
174 G_STRUCT_OFFSET (UndoMgrClass
, can_redo
),
176 g_cclosure_marshal_VOID__BOOLEAN
,
184 undomgr_init (UndoMgr
*um
)
186 um
->priv
= g_new0 (UndoMgrPrivate
, 1);
188 um
->priv
->actions
= NULL
;
189 um
->priv
->widgets
= NULL
;
190 um
->priv
->next_redo
= 0;
192 um
->priv
->can_undo
= FALSE
;
193 um
->priv
->can_redo
= FALSE
;
195 um
->priv
->running_not_undoable_actions
= 0;
197 um
->priv
->num_of_groups
= 0;
201 undomgr_finalize (GObject
*object
)
205 g_return_if_fail (object
!= NULL
);
206 g_return_if_fail (IS_UNDOMGR (object
));
208 um
= UNDOMGR (object
);
210 g_return_if_fail (um
->priv
!= NULL
);
212 if (um
->priv
->actions
!= NULL
)
213 undomgr_free_action_list (um
);
215 if (um
->priv
->actions
!= NULL
)
216 undomgr_free_widget_list (um
);
220 G_OBJECT_CLASS (parent_class
)->finalize (object
);
228 um
= UNDOMGR (g_object_new (TYPE_UNDOMGR
, NULL
));
230 g_return_val_if_fail (um
->priv
!= NULL
, NULL
);
236 undomgr_attach (UndoMgr
*um
, GtkWidget
*widget
) {
238 /* Return if the widget is already in the list */
239 if(g_list_find(um
->priv
->widgets
, widget
) != NULL
)
242 /* FIXME for all widget types! */
244 if(GTK_IS_TEXT_VIEW(widget
)) {
245 GtkTextBuffer
*buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(widget
));
247 g_signal_connect (G_OBJECT (buffer
), "insert_text",
248 G_CALLBACK (undomgr_textbuffer_insert_text_handler
),
251 g_signal_connect (G_OBJECT (buffer
), "delete_range",
252 G_CALLBACK (undomgr_textbuffer_delete_range_handler
),
255 g_signal_connect (G_OBJECT (buffer
), "begin_user_action",
256 G_CALLBACK (undomgr_textbuffer_begin_user_action_handler
),
259 g_signal_connect (G_OBJECT (buffer
), "end_user_action",
260 G_CALLBACK (undomgr_textbuffer_end_user_action_handler
),
263 um
->priv
->widgets
= g_list_append(um
->priv
->widgets
, widget
);
264 /*g_print("Added widget textbuffer\n");*/
266 g_printerr("Unable to handle widget type in undomgr_attach.\n Widget: %s\n", G_OBJECT_TYPE_NAME(G_OBJECT(widget
)));
270 void undomgr_detach(UndoMgr
*um
, GtkWidget
*widget
) {
271 g_return_if_fail (IS_UNDOMGR (um
));
272 g_return_if_fail (um
->priv
!= NULL
);
273 g_return_if_fail (um
->priv
->widgets
!= NULL
);
275 g_return_if_fail (widget
!= NULL
);
276 g_return_if_fail (g_list_find(um
->priv
->widgets
, widget
));
279 undomgr_free_widget(um
, widget
);
280 um
->priv
->widgets
= g_list_remove(um
->priv
->widgets
, widget
);
284 undomgr_begin_not_undoable_action (UndoMgr
*um
)
286 g_return_if_fail (IS_UNDOMGR (um
));
287 g_return_if_fail (um
->priv
!= NULL
);
289 ++um
->priv
->running_not_undoable_actions
;
293 undomgr_end_not_undoable_action_internal (UndoMgr
*um
)
295 g_return_if_fail (IS_UNDOMGR (um
));
296 g_return_if_fail (um
->priv
!= NULL
);
298 g_return_if_fail (um
->priv
->running_not_undoable_actions
> 0);
300 --um
->priv
->running_not_undoable_actions
;
304 undomgr_end_not_undoable_action (UndoMgr
*um
)
306 g_return_if_fail (IS_UNDOMGR (um
));
307 g_return_if_fail (um
->priv
!= NULL
);
309 undomgr_end_not_undoable_action_internal (um
);
311 if (um
->priv
->running_not_undoable_actions
== 0)
313 undomgr_free_action_list (um
);
315 um
->priv
->next_redo
= -1;
317 if (um
->priv
->can_undo
)
319 um
->priv
->can_undo
= FALSE
;
320 g_signal_emit (G_OBJECT (um
), undomgr_signals
[CAN_UNDO
], 0, FALSE
);
323 if (um
->priv
->can_redo
)
325 um
->priv
->can_redo
= FALSE
;
326 g_signal_emit (G_OBJECT (um
), undomgr_signals
[CAN_REDO
], 0, FALSE
);
333 undomgr_can_undo (const UndoMgr
*um
)
335 g_return_val_if_fail (IS_UNDOMGR (um
), FALSE
);
336 g_return_val_if_fail (um
->priv
!= NULL
, FALSE
);
338 return um
->priv
->can_undo
;
342 undomgr_can_redo (const UndoMgr
*um
)
344 g_return_val_if_fail (IS_UNDOMGR (um
), FALSE
);
345 g_return_val_if_fail (um
->priv
!= NULL
, FALSE
);
347 return um
->priv
->can_redo
;
351 undomgr_undo (UndoMgr
*um
)
353 UndoAction
*undo_action
;
355 g_return_if_fail (IS_UNDOMGR (um
));
356 g_return_if_fail (um
->priv
!= NULL
);
357 g_return_if_fail (um
->priv
->can_undo
);
359 undomgr_begin_not_undoable_action (um
);
363 ++um
->priv
->next_redo
;
365 undo_action
= g_list_nth_data (um
->priv
->actions
, um
->priv
->next_redo
);
366 g_return_if_fail (undo_action
!= NULL
);
368 /* FIXME for more widget types! */
369 if(GTK_IS_TEXT_BUFFER(undo_action
->object
)) {
370 GtkTextBuffer
*buffer
= GTK_TEXT_BUFFER(undo_action
->object
);
371 GtkTextIter start
, end
;
373 switch (undo_action
->action_type
)
375 case UNDO_ACTION_DELETE
:
376 gtk_text_buffer_get_iter_at_offset(
379 undo_action
->action
.delete.start
);
381 gtk_text_buffer_place_cursor(buffer
, &start
);
383 gtk_text_buffer_insert(
386 undo_action
->action
.delete.text
,
387 (int)strlen(undo_action
->action
.delete.text
));
391 case UNDO_ACTION_INSERT
:
392 gtk_text_buffer_get_iter_at_offset(
395 undo_action
->action
.insert
.pos
);
396 gtk_text_buffer_get_iter_at_offset(
399 undo_action
->action
.insert
.pos
+
400 undo_action
->action
.insert
.chars
);
402 gtk_text_buffer_delete(buffer
, &start
, &end
);
404 gtk_text_buffer_place_cursor(buffer
, &start
);
409 g_warning ("This should not happen.");
413 g_warning("Don't know how to handle action.");
415 } while (undo_action
->order_in_group
> 1);
417 undomgr_end_not_undoable_action_internal (um
);
419 if (!um
->priv
->can_redo
)
421 um
->priv
->can_redo
= TRUE
;
422 g_signal_emit (G_OBJECT (um
), undomgr_signals
[CAN_REDO
], 0, TRUE
);
425 if (um
->priv
->next_redo
>= (gint
)(g_list_length (um
->priv
->actions
) - 1))
427 um
->priv
->can_undo
= FALSE
;
428 g_signal_emit (G_OBJECT (um
), undomgr_signals
[CAN_UNDO
], 0, FALSE
);
433 undomgr_redo (UndoMgr
*um
)
435 UndoAction
*undo_action
;
437 g_return_if_fail (IS_UNDOMGR (um
));
438 g_return_if_fail (um
->priv
!= NULL
);
439 g_return_if_fail (um
->priv
->can_redo
);
441 undo_action
= g_list_nth_data (um
->priv
->actions
, um
->priv
->next_redo
);
442 g_return_if_fail (undo_action
!= NULL
);
444 undomgr_begin_not_undoable_action (um
);
448 /* FIXME for multiple widget types! */
449 if(GTK_IS_TEXT_BUFFER(undo_action
->object
)) {
450 GtkTextBuffer
*buffer
= GTK_TEXT_BUFFER(undo_action
->object
);
451 GtkTextIter start
, end
;
453 switch (undo_action
->action_type
)
455 case UNDO_ACTION_DELETE
:
456 gtk_text_buffer_get_iter_at_offset(
459 undo_action
->action
.delete.start
);
460 gtk_text_buffer_get_iter_at_offset(
463 undo_action
->action
.delete.end
);
465 gtk_text_buffer_delete(buffer
, &start
, &end
);
467 gtk_text_buffer_place_cursor(buffer
, &start
);
471 case UNDO_ACTION_INSERT
:
472 gtk_text_buffer_get_iter_at_offset(
475 undo_action
->action
.insert
.pos
);
477 gtk_text_buffer_place_cursor(buffer
, &start
);
479 gtk_text_buffer_insert(buffer
,
481 undo_action
->action
.insert
.text
,
482 undo_action
->action
.insert
.length
);
487 g_warning ("This should not happen.");
491 g_warning("Redoing unknown widget type.");
493 --um
->priv
->next_redo
;
495 if (um
->priv
->next_redo
< 0)
498 undo_action
= g_list_nth_data (um
->priv
->actions
, um
->priv
->next_redo
);
500 } while ((undo_action
!= NULL
) && (undo_action
->order_in_group
> 1));
502 undomgr_end_not_undoable_action_internal (um
);
504 if (um
->priv
->next_redo
< 0)
506 um
->priv
->can_redo
= FALSE
;
507 g_signal_emit (G_OBJECT (um
), undomgr_signals
[CAN_REDO
], 0, FALSE
);
510 if (!um
->priv
->can_undo
)
512 um
->priv
->can_undo
= TRUE
;
513 g_signal_emit (G_OBJECT (um
), undomgr_signals
[CAN_UNDO
], 0, TRUE
);
519 undomgr_free_action_list (UndoMgr
*um
)
523 g_return_if_fail (IS_UNDOMGR (um
));
524 g_return_if_fail (um
->priv
!= NULL
);
526 if (um
->priv
->actions
== NULL
)
530 len
= g_list_length (um
->priv
->actions
);
532 for (n
= 0; n
< len
; n
++)
534 UndoAction
*undo_action
=
535 (UndoAction
*)(g_list_nth_data (um
->priv
->actions
, n
));
537 /* gedit_debug (DEBUG_UNDO, "Free action (type %s) %d/%d",
538 (undo_action->action_type == GEDIT_UNDO_ACTION_INSERT) ? "insert":
539 "delete", n, len); */
541 if (undo_action
->action_type
== UNDO_ACTION_INSERT
)
542 g_free (undo_action
->action
.insert
.text
);
543 else if (undo_action
->action_type
== UNDO_ACTION_DELETE
)
544 g_free (undo_action
->action
.delete.text
);
546 g_return_if_fail (FALSE
);
548 if (undo_action
->order_in_group
== 1)
549 --um
->priv
->num_of_groups
;
551 g_free (undo_action
);
554 g_list_free (um
->priv
->actions
);
555 um
->priv
->actions
= NULL
;
559 undomgr_free_widget(UndoMgr
*um
, GtkWidget
*w
)
561 /* FIXME for all widget types! */
562 if(GTK_IS_TEXT_VIEW(w
)) {
563 GtkTextBuffer
*buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(w
));
565 g_signal_handlers_disconnect_by_func (
567 G_CALLBACK (undomgr_textbuffer_delete_range_handler
),
570 g_signal_handlers_disconnect_by_func (
572 G_CALLBACK (undomgr_textbuffer_insert_text_handler
),
575 g_signal_handlers_disconnect_by_func (
577 G_CALLBACK (undomgr_textbuffer_begin_user_action_handler
),
580 g_signal_handlers_disconnect_by_func (
582 G_CALLBACK (undomgr_textbuffer_end_user_action_handler
),
585 g_printerr("Unable to handle widget type in undomgr_free_widget_list.\n Widget: %s\n", G_OBJECT_TYPE_NAME(G_OBJECT(w
)));
590 undomgr_free_widget_list (UndoMgr
*um
)
594 g_return_if_fail (IS_UNDOMGR (um
));
595 g_return_if_fail (um
->priv
!= NULL
);
596 g_return_if_fail (um
->priv
->widgets
!= NULL
);
598 len
= g_list_length (um
->priv
->actions
);
599 for(n
= 0; n
< len
; n
++) {
600 GtkWidget
*w
= (GtkWidget
*)(g_list_nth_data (um
->priv
->actions
, n
));
602 undomgr_free_widget(um
, w
);
605 g_list_free (um
->priv
->actions
);
606 um
->priv
->actions
= NULL
;
610 undomgr_textbuffer_insert_text_handler (GtkTextBuffer
*buffer
,
612 const gchar
*text
, gint length
, UndoMgr
*um
)
614 UndoAction undo_action
;
616 if (um
->priv
->running_not_undoable_actions
> 0)
619 g_return_if_fail (strlen (text
) == (guint
)length
);
621 undo_action
.action_type
= UNDO_ACTION_INSERT
;
623 undo_action
.action
.insert
.pos
= gtk_text_iter_get_offset (pos
);
624 undo_action
.action
.insert
.text
= (gchar
*) text
;
625 undo_action
.action
.insert
.length
= length
;
626 undo_action
.action
.insert
.chars
= g_utf8_strlen (text
, length
);
628 if ((undo_action
.action
.insert
.chars
> 1) || (g_utf8_get_char (text
) == '\n'))
630 undo_action
.mergeable
= FALSE
;
632 undo_action
.mergeable
= TRUE
;
634 undomgr_add_textbuffer_action (um
, undo_action
, buffer
);
638 undomgr_textbuffer_delete_range_handler (GtkTextBuffer
*buffer
,
640 GtkTextIter
*end
, UndoMgr
*um
)
642 UndoAction undo_action
;
644 if (um
->priv
->running_not_undoable_actions
> 0)
647 undo_action
.action_type
= UNDO_ACTION_DELETE
;
649 gtk_text_iter_order (start
, end
);
651 undo_action
.action
.delete.start
= gtk_text_iter_get_offset (start
);
652 undo_action
.action
.delete.end
= gtk_text_iter_get_offset (end
);
654 undo_action
.action
.delete.text
= gtk_text_buffer_get_slice (
660 if (((undo_action
.action
.delete.end
- undo_action
.action
.delete.start
) > 1) ||
661 (g_utf8_get_char (undo_action
.action
.delete.text
) == '\n'))
662 undo_action
.mergeable
= FALSE
;
664 undo_action
.mergeable
= TRUE
;
666 /* g_print ("START: %d\n", undo_action.action.delete.start);
667 g_print ("END: %d\n", undo_action.action.delete.end);
668 g_print ("TEXT: %s\n", undo_action.action.delete.text); */
670 undomgr_add_textbuffer_action (um
, undo_action
, buffer
);
672 g_free (undo_action
.action
.delete.text
);
676 undomgr_textbuffer_begin_user_action_handler (GtkTextBuffer
*buffer
,
679 g_return_if_fail (IS_UNDOMGR (um
));
680 g_return_if_fail (um
->priv
!= NULL
);
682 if (um
->priv
->running_not_undoable_actions
> 0)
685 um
->priv
->actions_in_current_group
= 0;
689 undomgr_textbuffer_end_user_action_handler (GtkTextBuffer
*buffer
,
692 if (um
->priv
->running_not_undoable_actions
> 0)
695 /* TODO: is it needed ? */
698 /* FIXME: change prototype to use UndoAction *undo_action : Paolo */
700 undomgr_add_textbuffer_action (UndoMgr
*um
, UndoAction undo_action
,
701 GtkTextBuffer
*buffer
)
705 g_return_if_fail(GTK_IS_TEXT_BUFFER(buffer
));
707 if (um
->priv
->next_redo
>= 0)
709 undomgr_free_first_n_actions (um
, um
->priv
->next_redo
+ 1);
712 um
->priv
->next_redo
= -1;
714 if (!undomgr_merge_textbuffer_action (um
, &undo_action
, buffer
))
716 action
= g_new (UndoAction
, 1);
717 *action
= undo_action
;
719 action
->object
= G_OBJECT(buffer
);
721 if (action
->action_type
== UNDO_ACTION_INSERT
)
722 action
->action
.insert
.text
= g_strdup (undo_action
.action
.insert
.text
);
723 else if (action
->action_type
== UNDO_ACTION_DELETE
)
724 action
->action
.delete.text
= g_strdup (undo_action
.action
.delete.text
);
728 g_return_if_fail (FALSE
);
731 ++um
->priv
->actions_in_current_group
;
732 action
->order_in_group
= um
->priv
->actions_in_current_group
;
734 if (action
->order_in_group
== 1)
735 ++um
->priv
->num_of_groups
;
737 um
->priv
->actions
= g_list_prepend (um
->priv
->actions
, action
);
740 undomgr_check_list_size (um
);
742 if (!um
->priv
->can_undo
)
744 um
->priv
->can_undo
= TRUE
;
745 g_signal_emit (G_OBJECT (um
), undomgr_signals
[CAN_UNDO
], 0, TRUE
);
748 if (um
->priv
->can_redo
)
750 um
->priv
->can_redo
= FALSE
;
751 g_signal_emit (G_OBJECT (um
), undomgr_signals
[CAN_REDO
], 0, FALSE
);
756 undomgr_free_first_n_actions (UndoMgr
*um
, gint n
)
760 g_return_if_fail (IS_UNDOMGR (um
));
761 g_return_if_fail (um
->priv
!= NULL
);
763 if (um
->priv
->actions
== NULL
)
766 for (i
= 0; i
< n
; i
++)
768 UndoAction
*undo_action
=
769 (UndoAction
*)(g_list_first (um
->priv
->actions
)->data
);
771 if (undo_action
->action_type
== UNDO_ACTION_INSERT
)
772 g_free (undo_action
->action
.insert
.text
);
773 else if (undo_action
->action_type
== UNDO_ACTION_DELETE
)
774 g_free (undo_action
->action
.delete.text
);
776 g_return_if_fail (FALSE
);
778 if (undo_action
->order_in_group
== 1)
779 --um
->priv
->num_of_groups
;
781 g_free (undo_action
);
783 um
->priv
->actions
= g_list_delete_link (um
->priv
->actions
, um
->priv
->actions
);
785 if (um
->priv
->actions
== NULL
)
791 undomgr_check_list_size (UndoMgr
*um
)
795 g_return_if_fail (IS_UNDOMGR (um
));
796 g_return_if_fail (um
->priv
!= NULL
);
798 /* FIXME: should this be a preference? */
804 if (um
->priv
->num_of_groups
> undo_levels
)
806 UndoAction
*undo_action
;
809 last
= g_list_last (um
->priv
->actions
);
810 undo_action
= (UndoAction
*) last
->data
;
814 if (undo_action
->action_type
== UNDO_ACTION_INSERT
)
815 g_free (undo_action
->action
.insert
.text
);
816 else if (undo_action
->action_type
== UNDO_ACTION_DELETE
)
817 g_free (undo_action
->action
.delete.text
);
819 g_return_if_fail (FALSE
);
821 if (undo_action
->order_in_group
== 1)
822 --um
->priv
->num_of_groups
;
824 g_free (undo_action
);
826 um
->priv
->actions
= g_list_delete_link (um
->priv
->actions
, last
);
827 g_return_if_fail (um
->priv
->actions
!= NULL
);
829 last
= g_list_last (um
->priv
->actions
);
830 undo_action
= (UndoAction
*) last
->data
;
832 } while ((undo_action
->order_in_group
> 1) ||
833 (um
->priv
->num_of_groups
> undo_levels
));
838 * undomgr_merge_textbuffer_action:
843 * This function tries to merge the undo action at the top of
844 * the stack with a new undo action. So when we undo for example
845 * typing, we can undo the whole word and not each letter by itself
847 * Return Value: TRUE is merge was sucessful, FALSE otherwise
850 undomgr_merge_textbuffer_action (UndoMgr
*um
, UndoAction
*undo_action
,
851 GtkTextBuffer
*buffer
)
853 UndoAction
*last_action
;
855 g_return_val_if_fail (GTK_IS_TEXT_BUFFER(buffer
), FALSE
);
856 g_return_val_if_fail (IS_UNDOMGR (um
), FALSE
);
857 g_return_val_if_fail (um
->priv
!= NULL
, FALSE
);
859 if (um
->priv
->actions
== NULL
)
862 last_action
= (UndoAction
*) g_list_nth_data (um
->priv
->actions
, 0);
864 if (!last_action
->mergeable
)
867 if ((!undo_action
->mergeable
) ||
868 (undo_action
->action_type
!= last_action
->action_type
))
870 last_action
->mergeable
= FALSE
;
874 if (undo_action
->action_type
== UNDO_ACTION_DELETE
)
876 if ((last_action
->action
.delete.start
!= undo_action
->action
.delete.start
) &&
877 (last_action
->action
.delete.start
!= undo_action
->action
.delete.end
))
879 last_action
->mergeable
= FALSE
;
883 if (last_action
->action
.delete.start
== undo_action
->action
.delete.start
)
887 #define L (last_action->action.delete.end - last_action->action.delete.start - 1)
888 #define g_utf8_get_char_at(p,i) g_utf8_get_char(g_utf8_offset_to_pointer((p),(i)))
890 /* Deleted with the delete key */
891 if ((g_utf8_get_char (undo_action
->action
.delete.text
) != ' ') &&
892 (g_utf8_get_char (undo_action
->action
.delete.text
) != '\t') &&
893 ((g_utf8_get_char_at (last_action
->action
.delete.text
, L
) == ' ') ||
894 (g_utf8_get_char_at (last_action
->action
.delete.text
, L
) == '\t')))
896 last_action
->mergeable
= FALSE
;
900 str
= g_strdup_printf ("%s%s",
901 last_action
->action
.delete.text
,
902 undo_action
->action
.delete.text
);
904 g_free (last_action
->action
.delete.text
);
905 last_action
->action
.delete.end
+=
906 (undo_action
->action
.delete.end
-
907 undo_action
->action
.delete.start
);
908 last_action
->action
.delete.text
= str
;
914 /* Deleted with the backspace key */
915 if ((g_utf8_get_char (undo_action
->action
.delete.text
) != ' ') &&
916 (g_utf8_get_char (undo_action
->action
.delete.text
) != '\t') &&
917 ((g_utf8_get_char (last_action
->action
.delete.text
) == ' ') ||
918 (g_utf8_get_char (last_action
->action
.delete.text
) == '\t')))
920 last_action
->mergeable
= FALSE
;
924 str
= g_strdup_printf ("%s%s",
925 undo_action
->action
.delete.text
,
926 last_action
->action
.delete.text
);
928 g_free (last_action
->action
.delete.text
);
929 last_action
->action
.delete.start
= undo_action
->action
.delete.start
;
930 last_action
->action
.delete.text
= str
;
935 else if (undo_action
->action_type
== UNDO_ACTION_INSERT
)
939 #define I (last_action->action.insert.chars - 1)
941 if ((undo_action
->action
.insert
.pos
!=
942 (last_action
->action
.insert
.pos
+ last_action
->action
.insert
.chars
)) ||
943 ((g_utf8_get_char (undo_action
->action
.insert
.text
) != ' ') &&
944 (g_utf8_get_char (undo_action
->action
.insert
.text
) != '\t') &&
945 ((g_utf8_get_char_at (last_action
->action
.insert
.text
, I
) == ' ') ||
946 (g_utf8_get_char_at (last_action
->action
.insert
.text
, I
) == '\t')))
949 last_action
->mergeable
= FALSE
;
953 str
= g_strdup_printf ("%s%s", last_action
->action
.insert
.text
,
954 undo_action
->action
.insert
.text
);
956 g_free (last_action
->action
.insert
.text
);
957 last_action
->action
.insert
.length
+= undo_action
->action
.insert
.length
;
958 last_action
->action
.insert
.text
= str
;
959 last_action
->action
.insert
.chars
+= undo_action
->action
.insert
.chars
;
963 g_warning ("Unknown action inside undo merge encountered");
969 undomgr_reset (UndoMgr
*um
) {
970 undomgr_free_action_list(um
);
972 um
->priv
->can_undo
= FALSE
;
973 g_signal_emit(G_OBJECT(um
), undomgr_signals
[CAN_UNDO
], 0, FALSE
);
975 um
->priv
->can_redo
= FALSE
;
976 g_signal_emit(G_OBJECT(um
), undomgr_signals
[CAN_REDO
], 0, FALSE
);