2 * Claws Mail -- a GTK based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2016 Hiroyuki Yamamoto and the Claws Mail team
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 3 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
19 /* code ported from gedit */
20 /* This is for my patient girlfirend Regina */
24 #include "claws-features.h"
29 #include <string.h> /* for strlen */
30 #include <stdlib.h> /* for mbstowcs */
34 #include "prefs_common.h"
36 typedef struct _UndoInfo UndoInfo
;
44 gfloat window_position
;
51 gchar
*pre_wrap_content
;
57 static void undo_free_list (GList
**list_pointer
);
58 static void undo_check_size (UndoMain
*undostruct
);
59 static gint
undo_merge (GList
*list
,
64 static void undo_add (const gchar
*text
,
68 UndoMain
*undostruct
);
69 static gint
undo_get_selection (GtkTextView
*textview
,
72 static void undo_insert_text_cb (GtkTextBuffer
*textbuf
,
76 UndoMain
*undostruct
);
77 static void undo_delete_text_cb (GtkTextBuffer
*textbuf
,
80 UndoMain
*undostruct
);
82 static void undo_paste_clipboard_cb (GtkTextView
*textview
,
83 UndoMain
*undostruct
);
85 void undo_undo (UndoMain
*undostruct
);
86 void undo_redo (UndoMain
*undostruct
);
89 UndoMain
*undo_init(GtkWidget
*text
)
92 GtkTextView
*textview
= GTK_TEXT_VIEW(text
);
93 GtkTextBuffer
*textbuf
= gtk_text_view_get_buffer(textview
);
95 cm_return_val_if_fail(text
!= NULL
, NULL
);
97 undostruct
= g_new0(UndoMain
, 1);
98 undostruct
->textview
= textview
;
99 undostruct
->undo
= NULL
;
100 undostruct
->redo
= NULL
;
101 undostruct
->paste
= 0;
102 undostruct
->undo_state
= FALSE
;
103 undostruct
->redo_state
= FALSE
;
105 g_signal_connect(G_OBJECT(textbuf
), "insert-text",
106 G_CALLBACK(undo_insert_text_cb
), undostruct
);
107 g_signal_connect(G_OBJECT(textbuf
), "delete-range",
108 G_CALLBACK(undo_delete_text_cb
), undostruct
);
109 g_signal_connect(G_OBJECT(textview
), "paste-clipboard",
110 G_CALLBACK(undo_paste_clipboard_cb
), undostruct
);
115 void undo_destroy (UndoMain
*undostruct
)
117 undo_free_list(&undostruct
->undo
);
118 undo_free_list(&undostruct
->redo
);
122 static UndoInfo
*undo_object_new(gchar
*text
, gint start_pos
, gint end_pos
,
123 UndoAction action
, gfloat window_position
)
126 undoinfo
= g_new (UndoInfo
, 1);
127 undoinfo
->text
= text
;
128 undoinfo
->start_pos
= start_pos
;
129 undoinfo
->end_pos
= end_pos
;
130 undoinfo
->action
= action
;
131 undoinfo
->window_position
= window_position
;
135 static void undo_object_free(UndoInfo
*undo
)
143 * @list_pointer: list to be freed
145 * frees and undo structure list
147 static void undo_free_list(GList
**list_pointer
)
150 GList
*cur
, *list
= *list_pointer
;
152 if (list
== NULL
) return;
154 for (cur
= list
; cur
!= NULL
; cur
= cur
->next
) {
155 undo
= (UndoInfo
*)cur
->data
;
156 undo_object_free(undo
);
160 *list_pointer
= NULL
;
163 void undo_set_change_state_func(UndoMain
*undostruct
, UndoChangeStateFunc func
,
166 cm_return_if_fail(undostruct
!= NULL
);
168 undostruct
->change_state_func
= func
;
169 undostruct
->change_state_data
= data
;
174 * @compose: document to check
176 * Checks that the size of compose->undo does not excede settings->undo_levels and
177 * frees any undo level above sett->undo_level.
180 static void undo_check_size(UndoMain
*undostruct
)
185 if (prefs_common
.undolevels
< 1) return;
187 /* No need to check for the redo list size since the undo
188 list gets freed on any call to compose_undo_add */
189 length
= g_list_length(undostruct
->undo
);
190 if (length
>= prefs_common
.undolevels
&& prefs_common
.undolevels
> 0) {
191 last_undo
= (UndoInfo
*)g_list_last(undostruct
->undo
)->data
;
192 undostruct
->undo
= g_list_remove(undostruct
->undo
, last_undo
);
193 undo_object_free(last_undo
);
204 * This function tries to merge the undo object at the top of
205 * the stack with a new set of data. So when we undo for example
206 * typing, we can undo the whole word and not each letter by itself
208 * Return Value: TRUE is merge was sucessful, FALSE otherwise
210 static gint
undo_merge(GList
*list
, guint start_pos
, guint end_pos
,
211 gint action
, const guchar
*text
)
216 /* This are the cases in which we will NOT merge :
217 1. if (last_undo->mergeable == FALSE)
218 [mergeable = FALSE when the size of the undo data was not 1.
219 or if the data was size = 1 but = '\n' or if the undo object
220 has been "undone" already ]
221 2. The size of text is not 1
222 3. If the new merging data is a '\n'
223 4. If the last char of the undo_last data is a space/tab
224 and the new char is not a space/tab ( so that we undo
225 words and not chars )
226 5. If the type (action) of undo is different from the last one
229 if (list
== NULL
) return FALSE
;
231 last_undo
= list
->data
;
233 if (!last_undo
->mergeable
) return FALSE
;
235 if (end_pos
- start_pos
!= 1 ||
237 action
!= last_undo
->action
||
238 action
== UNDO_ACTION_REPLACE_INSERT
||
239 action
== UNDO_ACTION_REPLACE_DELETE
) {
240 last_undo
->mergeable
= FALSE
;
244 if (action
== UNDO_ACTION_DELETE
) {
245 if (last_undo
->start_pos
!= end_pos
&&
246 last_undo
->start_pos
!= start_pos
) {
247 last_undo
->mergeable
= FALSE
;
249 } else if (last_undo
->start_pos
== start_pos
) {
250 /* Deleted with the delete key */
251 temp_string
= g_strdup_printf("%s%s", last_undo
->text
, text
);
252 last_undo
->end_pos
++;
253 g_free(last_undo
->text
);
254 last_undo
->text
= temp_string
;
256 /* Deleted with the backspace key */
257 temp_string
= g_strdup_printf("%s%s", text
, last_undo
->text
);
258 last_undo
->start_pos
= start_pos
;
259 g_free(last_undo
->text
);
260 last_undo
->text
= temp_string
;
262 } else if (action
== UNDO_ACTION_INSERT
) {
263 if (last_undo
->end_pos
!= start_pos
) {
264 last_undo
->mergeable
= FALSE
;
267 temp_string
= g_strdup_printf("%s%s", last_undo
->text
, text
);
268 g_free(last_undo
->text
);
269 last_undo
->end_pos
= end_pos
;
270 last_undo
->text
= temp_string
;
273 debug_print("Unknown action [%i] inside undo merge encountered\n", action
);
283 * @action: either UNDO_ACTION_INSERT or UNDO_ACTION_DELETE
285 * @view: The view so that we save the scroll bar position.
287 * Adds text to the undo stack. It also performs test to limit the number
288 * of undo levels and deltes the redo list
291 static void undo_add(const gchar
*text
,
292 gint start_pos
, gint end_pos
,
293 UndoAction action
, UndoMain
*undostruct
)
298 cm_return_if_fail(text
!= NULL
);
299 cm_return_if_fail(end_pos
>= start_pos
);
301 undo_free_list(&undostruct
->redo
);
303 /* Set the redo sensitivity */
304 undostruct
->change_state_func(undostruct
,
305 UNDO_STATE_UNCHANGED
, UNDO_STATE_FALSE
,
306 undostruct
->change_state_data
);
308 if (undostruct
->paste
!= 0) {
309 if (action
== UNDO_ACTION_INSERT
)
310 action
= UNDO_ACTION_REPLACE_INSERT
;
312 action
= UNDO_ACTION_REPLACE_DELETE
;
313 undostruct
->paste
= undostruct
->paste
+ 1;
314 if (undostruct
->paste
== 3)
315 undostruct
->paste
= 0;
318 if (undo_merge(undostruct
->undo
, start_pos
, end_pos
, action
, text
))
321 undo_check_size(undostruct
);
323 vadj
= GTK_ADJUSTMENT(gtk_scrollable_get_vadjustment(
324 GTK_SCROLLABLE(undostruct
->textview
)));
325 undoinfo
= undo_object_new(g_strdup(text
), start_pos
, end_pos
, action
,
326 gtk_adjustment_get_value(vadj
));
328 if (end_pos
- start_pos
!= 1 || text
[0] == '\n')
329 undoinfo
->mergeable
= FALSE
;
331 undoinfo
->mergeable
= TRUE
;
333 undostruct
->undo
= g_list_prepend(undostruct
->undo
, undoinfo
);
335 undostruct
->change_state_func(undostruct
,
336 UNDO_STATE_TRUE
, UNDO_STATE_UNCHANGED
,
337 undostruct
->change_state_data
);
345 * Executes an undo request on the current document
347 void undo_undo(UndoMain
*undostruct
)
350 GtkTextView
*textview
;
351 GtkTextBuffer
*buffer
;
352 GtkTextIter iter
, start_iter
, end_iter
;
355 cm_return_if_fail(undostruct
!= NULL
);
357 if (undostruct
->undo
== NULL
) return;
359 /* The undo data we need is always at the top op the
360 stack. So, therefore, the first one */
361 undoinfo
= (UndoInfo
*)undostruct
->undo
->data
;
362 cm_return_if_fail(undoinfo
!= NULL
);
363 undoinfo
->mergeable
= FALSE
;
364 undostruct
->redo
= g_list_prepend(undostruct
->redo
, undoinfo
);
365 undostruct
->undo
= g_list_remove(undostruct
->undo
, undoinfo
);
367 textview
= undostruct
->textview
;
368 buffer
= gtk_text_view_get_buffer(textview
);
370 undo_block(undostruct
);
372 /* Check if there is a selection active */
373 mark
= gtk_text_buffer_get_insert(buffer
);
374 gtk_text_buffer_get_iter_at_mark(buffer
, &iter
, mark
);
375 gtk_text_buffer_place_cursor(buffer
, &iter
);
377 /* Move the view (scrollbars) to the correct position */
378 gtk_adjustment_set_value
379 (GTK_ADJUSTMENT(gtk_scrollable_get_vadjustment(GTK_SCROLLABLE(textview
))),
380 undoinfo
->window_position
);
382 switch (undoinfo
->action
) {
383 case UNDO_ACTION_DELETE
:
384 gtk_text_buffer_get_iter_at_offset(buffer
, &iter
, undoinfo
->start_pos
);
385 gtk_text_buffer_insert(buffer
, &iter
, undoinfo
->text
, -1);
387 case UNDO_ACTION_INSERT
:
388 gtk_text_buffer_get_iter_at_offset(buffer
, &start_iter
, undoinfo
->start_pos
);
389 gtk_text_buffer_get_iter_at_offset(buffer
, &end_iter
, undoinfo
->end_pos
);
390 gtk_text_buffer_delete(buffer
, &start_iter
, &end_iter
);
392 case UNDO_ACTION_REPLACE_INSERT
:
393 gtk_text_buffer_get_iter_at_offset(buffer
, &start_iter
, undoinfo
->start_pos
);
394 gtk_text_buffer_get_iter_at_offset(buffer
, &end_iter
, undoinfo
->end_pos
);
395 gtk_text_buffer_delete(buffer
, &start_iter
, &end_iter
);
396 /* "pull" previous matching DELETE data structure from the list */
397 if (undostruct
->undo
){
398 undoinfo
= (UndoInfo
*)undostruct
->undo
->data
;
399 undostruct
->redo
= g_list_prepend(undostruct
->redo
, undoinfo
);
400 undostruct
->undo
= g_list_remove(undostruct
->undo
, undoinfo
);
401 cm_return_if_fail(undoinfo
!= NULL
);
402 cm_return_if_fail(undoinfo
->action
== UNDO_ACTION_REPLACE_DELETE
);
403 gtk_text_buffer_insert(buffer
, &start_iter
, undoinfo
->text
, -1);
406 case UNDO_ACTION_REPLACE_DELETE
:
407 gtk_text_buffer_get_iter_at_offset(buffer
, &start_iter
, undoinfo
->start_pos
);
408 gtk_text_buffer_insert(buffer
, &start_iter
, undoinfo
->text
, -1);
409 /* "pull" previous matching INSERT data structure from the list */
410 if (undostruct
->undo
){
411 undoinfo
= (UndoInfo
*)undostruct
->undo
->data
;
412 undostruct
->redo
= g_list_prepend(undostruct
->redo
, undoinfo
);
413 undostruct
->undo
= g_list_remove(undostruct
->undo
, undoinfo
);
414 cm_return_if_fail(undoinfo
!= NULL
);
415 cm_return_if_fail(undoinfo
->action
== UNDO_ACTION_REPLACE_INSERT
);
416 gtk_text_buffer_get_iter_at_offset(buffer
, &start_iter
, undoinfo
->start_pos
);
417 gtk_text_buffer_get_iter_at_offset(buffer
, &end_iter
, undoinfo
->end_pos
);
418 gtk_text_buffer_delete(buffer
, &start_iter
, &end_iter
);
422 g_assert_not_reached();
426 undostruct
->change_state_func(undostruct
,
427 UNDO_STATE_UNCHANGED
, UNDO_STATE_TRUE
,
428 undostruct
->change_state_data
);
430 if (undostruct
->undo
== NULL
)
431 undostruct
->change_state_func(undostruct
,
433 UNDO_STATE_UNCHANGED
,
434 undostruct
->change_state_data
);
436 undo_unblock(undostruct
);
444 * executes a redo request on the current document
446 void undo_redo(UndoMain
*undostruct
)
449 GtkTextView
*textview
;
450 GtkTextBuffer
*buffer
;
451 GtkTextIter iter
, start_iter
, end_iter
;
454 cm_return_if_fail(undostruct
!= NULL
);
456 if (undostruct
->redo
== NULL
) return;
458 redoinfo
= (UndoInfo
*)undostruct
->redo
->data
;
459 cm_return_if_fail (redoinfo
!= NULL
);
460 undostruct
->undo
= g_list_prepend(undostruct
->undo
, redoinfo
);
461 undostruct
->redo
= g_list_remove(undostruct
->redo
, redoinfo
);
463 textview
= undostruct
->textview
;
464 buffer
= gtk_text_view_get_buffer(textview
);
466 undo_block(undostruct
);
468 /* Check if there is a selection active */
469 mark
= gtk_text_buffer_get_insert(buffer
);
470 gtk_text_buffer_get_iter_at_mark(buffer
, &iter
, mark
);
471 gtk_text_buffer_place_cursor(buffer
, &iter
);
473 /* Move the view to the right position. */
474 gtk_adjustment_set_value(gtk_scrollable_get_vadjustment(GTK_SCROLLABLE(textview
)),
475 redoinfo
->window_position
);
477 switch (redoinfo
->action
) {
478 case UNDO_ACTION_INSERT
:
479 gtk_text_buffer_get_iter_at_offset(buffer
, &iter
, redoinfo
->start_pos
);
480 gtk_text_buffer_insert(buffer
, &iter
, redoinfo
->text
, -1);
482 case UNDO_ACTION_DELETE
:
483 gtk_text_buffer_get_iter_at_offset(buffer
, &start_iter
, redoinfo
->start_pos
);
484 gtk_text_buffer_get_iter_at_offset(buffer
, &end_iter
, redoinfo
->end_pos
);
485 gtk_text_buffer_delete(buffer
, &start_iter
, &end_iter
);
487 case UNDO_ACTION_REPLACE_DELETE
:
488 gtk_text_buffer_get_iter_at_offset(buffer
, &start_iter
, redoinfo
->start_pos
);
489 gtk_text_buffer_get_iter_at_offset(buffer
, &end_iter
, redoinfo
->end_pos
);
490 gtk_text_buffer_delete(buffer
, &start_iter
, &end_iter
);
491 debug_print("UNDO_ACTION_REPLACE %s\n", redoinfo
->text
);
492 /* "pull" previous matching INSERT data structure from the list */
493 redoinfo
= (UndoInfo
*)undostruct
->redo
->data
;
494 cm_return_if_fail(redoinfo
!= NULL
);
495 undostruct
->undo
= g_list_prepend(undostruct
->undo
, redoinfo
);
496 undostruct
->redo
= g_list_remove(undostruct
->redo
, redoinfo
);
497 cm_return_if_fail(redoinfo
->action
== UNDO_ACTION_REPLACE_INSERT
);
498 gtk_text_buffer_insert(buffer
, &start_iter
, redoinfo
->text
, -1);
500 case UNDO_ACTION_REPLACE_INSERT
:
501 gtk_text_buffer_get_iter_at_offset(buffer
, &iter
, redoinfo
->start_pos
);
502 gtk_text_buffer_insert(buffer
, &iter
, redoinfo
->text
, -1);
503 /* "pull" previous matching DELETE structure from the list */
504 redoinfo
= (UndoInfo
*)undostruct
->redo
->data
;
505 /* Do nothing if we redo from a middle-click button
506 * and next action is not UNDO_ACTION_REPLACE_DELETE */
507 if (redoinfo
&& redoinfo
->action
== UNDO_ACTION_REPLACE_DELETE
) {
508 undostruct
->undo
= g_list_prepend(undostruct
->undo
, redoinfo
);
509 undostruct
->redo
= g_list_remove(undostruct
->redo
, redoinfo
);
510 gtk_text_buffer_get_iter_at_offset(buffer
, &start_iter
, redoinfo
->start_pos
);
511 gtk_text_buffer_get_iter_at_offset(buffer
, &end_iter
, redoinfo
->end_pos
);
512 gtk_text_buffer_delete(buffer
, &start_iter
, &end_iter
);
516 g_assert_not_reached();
520 undostruct
->change_state_func(undostruct
,
521 UNDO_STATE_TRUE
, UNDO_STATE_UNCHANGED
,
522 undostruct
->change_state_data
);
524 if (undostruct
->redo
== NULL
)
525 undostruct
->change_state_func(undostruct
,
526 UNDO_STATE_UNCHANGED
,
528 undostruct
->change_state_data
);
530 undo_unblock(undostruct
);
533 void undo_block(UndoMain
*undostruct
)
535 GtkTextBuffer
*buffer
;
537 cm_return_if_fail(GTK_IS_TEXT_VIEW(undostruct
->textview
));
539 buffer
= gtk_text_view_get_buffer(undostruct
->textview
);
540 g_signal_handlers_block_by_func(buffer
, undo_insert_text_cb
, undostruct
);
541 g_signal_handlers_block_by_func(buffer
, undo_delete_text_cb
, undostruct
);
542 g_signal_handlers_block_by_func(buffer
, undo_paste_clipboard_cb
,
546 void undo_unblock(UndoMain
*undostruct
)
548 GtkTextBuffer
*buffer
;
550 cm_return_if_fail(GTK_IS_TEXT_VIEW(undostruct
->textview
));
552 buffer
= gtk_text_view_get_buffer(undostruct
->textview
);
553 g_signal_handlers_unblock_by_func(buffer
, undo_insert_text_cb
, undostruct
);
554 g_signal_handlers_unblock_by_func(buffer
, undo_delete_text_cb
, undostruct
);
555 g_signal_handlers_unblock_by_func(buffer
, undo_paste_clipboard_cb
,
559 /* Init the WrapInfo structure */
560 static void init_wrap_undo(UndoMain
*undostruct
)
562 GtkTextBuffer
*buffer
;
563 GtkTextIter start
, end
;
565 cm_return_if_fail(undostruct
!= NULL
);
566 cm_return_if_fail(undostruct
->wrap_info
== NULL
);
568 undostruct
->wrap_info
= g_new0(UndoWrap
, 1);
570 /* Save the whole buffer as original contents. We'll retain the
571 * changed region when exiting wrap mode.
573 buffer
= gtk_text_view_get_buffer(undostruct
->textview
);
574 gtk_text_buffer_get_start_iter(buffer
, &start
);
575 gtk_text_buffer_get_end_iter(buffer
, &end
);
576 undostruct
->wrap_info
->pre_wrap_content
577 = gtk_text_buffer_get_text(buffer
, &start
, &end
, FALSE
);
579 undostruct
->wrap_info
->lock
= 0;
581 /* start_pos == -1 means nothing changed yet. */
582 undostruct
->wrap_info
->start_pos
= -1;
583 undostruct
->wrap_info
->end_pos
= -1;
584 undostruct
->wrap_info
->len_change
= 0;
587 static void end_wrap_undo(UndoMain
*undostruct
)
589 GtkTextBuffer
*buffer
;
590 GtkTextIter start
, end
;
591 gchar
*old_contents
= NULL
;
592 gchar
*cur_contents
= NULL
;
593 gchar
*new_contents
= NULL
;
595 cm_return_if_fail(undostruct
!= NULL
);
596 cm_return_if_fail(undostruct
->wrap_info
!= NULL
);
598 /* If start_pos is still == -1, it means nothing changed. */
599 if (undostruct
->wrap_info
->start_pos
== -1)
602 cm_return_if_fail(undostruct
->wrap_info
->end_pos
> undostruct
->wrap_info
->start_pos
);
603 cm_return_if_fail(undostruct
->wrap_info
->end_pos
- undostruct
->wrap_info
->len_change
> undostruct
->wrap_info
->start_pos
);
605 /* get the whole new (wrapped) contents */
606 buffer
= gtk_text_view_get_buffer(undostruct
->textview
);
607 gtk_text_buffer_get_start_iter(buffer
, &start
);
608 gtk_text_buffer_get_end_iter(buffer
, &end
);
609 cur_contents
= gtk_text_buffer_get_text(buffer
, &start
, &end
, FALSE
);
611 debug_print("wrapping done from %d to %d, len change: %d\n",
612 undostruct
->wrap_info
->start_pos
,
613 undostruct
->wrap_info
->end_pos
,
614 undostruct
->wrap_info
->len_change
);
616 /* keep the relevant old unwrapped part, which is what
617 * was between start_pos & end_pos - len_change
619 old_contents
= g_utf8_substring(
620 undostruct
->wrap_info
->pre_wrap_content
,
621 undostruct
->wrap_info
->start_pos
,
622 undostruct
->wrap_info
->end_pos
623 - undostruct
->wrap_info
->len_change
);
625 /* and get the changed contents, from start_pos to end_pos. */
626 new_contents
= g_utf8_substring(
628 undostruct
->wrap_info
->start_pos
,
629 undostruct
->wrap_info
->end_pos
);
631 /* add the deleted (unwrapped) text to the undo pile */
632 undo_add(old_contents
,
633 undostruct
->wrap_info
->start_pos
,
634 undostruct
->wrap_info
->end_pos
635 - undostruct
->wrap_info
->len_change
,
636 UNDO_ACTION_REPLACE_DELETE
,
639 /* add the inserted (wrapped) text to the undo pile */
640 undo_add(new_contents
,
641 undostruct
->wrap_info
->start_pos
,
642 undostruct
->wrap_info
->end_pos
,
643 UNDO_ACTION_REPLACE_INSERT
,
646 g_free(old_contents
);
647 g_free(cur_contents
);
648 g_free(new_contents
);
650 g_free(undostruct
->wrap_info
->pre_wrap_content
);
651 g_free(undostruct
->wrap_info
);
652 undostruct
->wrap_info
= NULL
;
655 static void update_wrap_undo(UndoMain
*undostruct
, const gchar
*text
, int start
,
656 int end
, UndoAction action
)
658 gint len
= end
- start
;
660 /* If we don't yet have a start position, or farther than
663 if (undostruct
->wrap_info
->start_pos
== -1
664 || start
< undostruct
->wrap_info
->start_pos
) {
665 undostruct
->wrap_info
->start_pos
= start
;
668 if (action
== UNDO_ACTION_INSERT
) {
669 /* If inserting, the end of the region is at the end of the
670 * change, and the total length of the changed region
673 if (end
> undostruct
->wrap_info
->end_pos
) {
674 undostruct
->wrap_info
->end_pos
= end
;
676 undostruct
->wrap_info
->len_change
+= len
;
677 } else if (action
== UNDO_ACTION_DELETE
) {
678 /* If deleting, the end of the region is at the start of the
679 * change, and the total length of the changed region
682 if (start
> undostruct
->wrap_info
->end_pos
) {
683 undostruct
->wrap_info
->end_pos
= start
;
685 undostruct
->wrap_info
->len_change
-= len
;
689 /* Set wrapping mode, in which changes are agglomerated until
690 * the end of wrapping mode.
692 void undo_wrapping(UndoMain
*undostruct
, gboolean wrap
)
695 /* Start (or go deeper in) wrapping mode */
696 if (undostruct
->wrap_info
== NULL
)
697 init_wrap_undo(undostruct
);
698 undostruct
->wrap_info
->lock
++;
699 } else if (undostruct
->wrap_info
!= NULL
) {
700 /* exit (& possible stop) one level of wrapping mode */
701 undostruct
->wrap_info
->lock
--;
702 if (undostruct
->wrap_info
->lock
== 0)
703 end_wrap_undo(undostruct
);
705 g_warning("undo already out of wrap mode");
709 void undo_insert_text_cb(GtkTextBuffer
*textbuf
, GtkTextIter
*iter
,
710 gchar
*new_text
, gint new_text_length
,
711 UndoMain
*undostruct
)
713 gchar
*text_to_insert
;
717 if (prefs_common
.undolevels
<= 0) return;
719 pos
= gtk_text_iter_get_offset(iter
);
720 Xstrndup_a(text_to_insert
, new_text
, new_text_length
, return);
721 utf8_len
= g_utf8_strlen(text_to_insert
, -1);
723 if (undostruct
->wrap_info
!= NULL
) {
724 update_wrap_undo(undostruct
, text_to_insert
,
725 pos
, pos
+ utf8_len
, UNDO_ACTION_INSERT
);
729 debug_print("add:undo add %d-%ld\n", pos
, utf8_len
);
730 undo_add(text_to_insert
, pos
, pos
+ utf8_len
,
731 UNDO_ACTION_INSERT
, undostruct
);
734 void undo_delete_text_cb(GtkTextBuffer
*textbuf
, GtkTextIter
*start
,
735 GtkTextIter
*end
, UndoMain
*undostruct
)
737 gchar
*text_to_delete
;
738 gint start_pos
, end_pos
;
740 if (prefs_common
.undolevels
<= 0) return;
742 text_to_delete
= gtk_text_buffer_get_text(textbuf
, start
, end
, FALSE
);
743 if (!text_to_delete
|| !*text_to_delete
) return;
745 start_pos
= gtk_text_iter_get_offset(start
);
746 end_pos
= gtk_text_iter_get_offset(end
);
748 if (undostruct
->wrap_info
!= NULL
) {
749 update_wrap_undo(undostruct
, text_to_delete
, start_pos
, end_pos
, UNDO_ACTION_DELETE
);
752 debug_print("del:undo add %d-%d\n", start_pos
, end_pos
);
753 undo_add(text_to_delete
, start_pos
, end_pos
, UNDO_ACTION_DELETE
,
755 g_free(text_to_delete
);
758 void undo_paste_clipboard(GtkTextView
*textview
, UndoMain
*undostruct
)
760 undo_paste_clipboard_cb(textview
, undostruct
);
763 static void undo_paste_clipboard_cb(GtkTextView
*textview
, UndoMain
*undostruct
)
765 if (prefs_common
.undolevels
> 0)
766 if (undo_get_selection(textview
, NULL
, NULL
))
767 undostruct
->paste
= TRUE
;
771 * undo_get_selection:
772 * @text: Text to get the selection from
773 * @start: return here the start position of the selection
774 * @end: return here the end position of the selection
776 * Gets the current selection for View
778 * Return Value: TRUE if there is a selection active, FALSE if not
780 static gint
undo_get_selection(GtkTextView
*textview
, guint
*start
, guint
*end
)
782 GtkTextBuffer
*buffer
;
783 GtkTextIter start_iter
, end_iter
;
784 guint start_pos
, end_pos
;
786 buffer
= gtk_text_view_get_buffer(textview
);
787 gtk_text_buffer_get_selection_bounds(buffer
, &start_iter
, &end_iter
);
789 start_pos
= gtk_text_iter_get_offset(&start_iter
);
790 end_pos
= gtk_text_iter_get_offset(&end_iter
);
792 /* The user can select from end to start too. If so, swap it*/
793 if (end_pos
< start_pos
) {
797 start_pos
= swap_pos
;
806 if ((start_pos
> 0 || end_pos
> 0) && (start_pos
!= end_pos
))