Fix bug #3574: Template addressing
[claws.git] / src / undo.c
blob92c1f442ae668a58a9cceeaf50f1a3f348351613
1 /*
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 */
22 #ifdef HAVE_CONFIG_H
23 # include "config.h"
24 #include "claws-features.h"
25 #endif
27 #include <glib.h>
29 #include <string.h> /* for strlen */
30 #include <stdlib.h> /* for mbstowcs */
32 #include "undo.h"
33 #include "utils.h"
34 #include "prefs_common.h"
36 typedef struct _UndoInfo UndoInfo;
38 struct _UndoInfo
40 UndoAction action;
41 gchar *text;
42 gint start_pos;
43 gint end_pos;
44 gfloat window_position;
45 gint mergeable;
48 struct _UndoWrap
50 gint lock;
51 gchar *pre_wrap_content;
52 gint start_pos;
53 gint end_pos;
54 gint len_change;
57 static void undo_free_list (GList **list_pointer);
58 static void undo_check_size (UndoMain *undostruct);
59 static gint undo_merge (GList *list,
60 guint start_pos,
61 guint end_pos,
62 gint action,
63 const guchar *text);
64 static void undo_add (const gchar *text,
65 gint start_pos,
66 gint end_pos,
67 UndoAction action,
68 UndoMain *undostruct);
69 static gint undo_get_selection (GtkTextView *textview,
70 guint *start,
71 guint *end);
72 static void undo_insert_text_cb (GtkTextBuffer *textbuf,
73 GtkTextIter *iter,
74 gchar *new_text,
75 gint new_text_length,
76 UndoMain *undostruct);
77 static void undo_delete_text_cb (GtkTextBuffer *textbuf,
78 GtkTextIter *start,
79 GtkTextIter *end,
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)
91 UndoMain *undostruct;
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);
112 return undostruct;
115 void undo_destroy (UndoMain *undostruct)
117 undo_free_list(&undostruct->undo);
118 undo_free_list(&undostruct->redo);
119 g_free(undostruct);
122 static UndoInfo *undo_object_new(gchar *text, gint start_pos, gint end_pos,
123 UndoAction action, gfloat window_position)
125 UndoInfo *undoinfo;
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;
132 return undoinfo;
135 static void undo_object_free(UndoInfo *undo)
137 g_free (undo->text);
138 g_free (undo);
142 * undo_free_list:
143 * @list_pointer: list to be freed
145 * frees and undo structure list
147 static void undo_free_list(GList **list_pointer)
149 UndoInfo *undo;
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);
159 g_list_free(list);
160 *list_pointer = NULL;
163 void undo_set_change_state_func(UndoMain *undostruct, UndoChangeStateFunc func,
164 gpointer data)
166 cm_return_if_fail(undostruct != NULL);
168 undostruct->change_state_func = func;
169 undostruct->change_state_data = data;
173 * undo_check_size:
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)
182 UndoInfo *last_undo;
183 guint length;
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);
198 * undo_merge:
199 * @last_undo:
200 * @start_pos:
201 * @end_pos:
202 * @action:
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)
213 guchar *temp_string;
214 UndoInfo *last_undo;
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
227 Chema */
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 ||
236 text[0] == '\n' ||
237 action != last_undo->action ||
238 action == UNDO_ACTION_REPLACE_INSERT ||
239 action == UNDO_ACTION_REPLACE_DELETE) {
240 last_undo->mergeable = FALSE;
241 return 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;
248 return 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;
255 } else {
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;
265 return FALSE;
266 } else {
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;
272 } else
273 debug_print("Unknown action [%i] inside undo merge encountered\n", action);
275 return TRUE;
279 * compose_undo_add:
280 * @text:
281 * @start_pos:
282 * @end_pos:
283 * @action: either UNDO_ACTION_INSERT or UNDO_ACTION_DELETE
284 * @compose:
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)
295 UndoInfo *undoinfo;
296 GtkAdjustment *vadj;
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;
311 else
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))
319 return;
321 undo_check_size(undostruct);
323 vadj = GTK_ADJUSTMENT(gtk_text_view_get_vadjustment(
324 GTK_TEXT_VIEW(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;
330 else
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);
341 * undo_undo:
342 * @w: not used
343 * @data: not used
345 * Executes an undo request on the current document
347 void undo_undo(UndoMain *undostruct)
349 UndoInfo *undoinfo;
350 GtkTextView *textview;
351 GtkTextBuffer *buffer;
352 GtkTextIter iter, start_iter, end_iter;
353 GtkTextMark *mark;
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_text_view_get_vadjustment(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);
386 break;
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);
391 break;
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);
405 break;
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);
420 break;
421 default:
422 g_assert_not_reached();
423 break;
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,
432 UNDO_STATE_FALSE,
433 UNDO_STATE_UNCHANGED,
434 undostruct->change_state_data);
436 undo_unblock(undostruct);
440 * undo_redo:
441 * @w: not used
442 * @data: not used
444 * executes a redo request on the current document
446 void undo_redo(UndoMain *undostruct)
448 UndoInfo *redoinfo;
449 GtkTextView *textview;
450 GtkTextBuffer *buffer;
451 GtkTextIter iter, start_iter, end_iter;
452 GtkTextMark *mark;
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_text_view_get_vadjustment(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);
481 break;
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);
486 break;
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);
499 break;
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);
514 break;
515 default:
516 g_assert_not_reached();
517 break;
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,
527 UNDO_STATE_FALSE,
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,
543 undostruct);
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,
556 undostruct);
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)
600 goto cleanup;
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(
627 cur_contents,
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,
637 undostruct);
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,
644 undostruct);
646 g_free(old_contents);
647 g_free(cur_contents);
648 g_free(new_contents);
649 cleanup:
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
661 * current, store it.
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
671 * increases.
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
680 * decreases.
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)
694 if (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);
704 } else {
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;
714 gint pos;
715 glong utf8_len;
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);
726 return;
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);
750 return;
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,
754 undostruct);
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) {
794 guint swap_pos;
795 swap_pos = end_pos;
796 end_pos = start_pos;
797 start_pos = swap_pos;
800 if (start != NULL)
801 *start = start_pos;
803 if (end != NULL)
804 *end = end_pos;
806 if ((start_pos > 0 || end_pos > 0) && (start_pos != end_pos))
807 return TRUE;
808 else
809 return FALSE;