2012-12-15 [paul] 3.9.0cvs48
[claws.git] / src / undo.c
1 /*
2  * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3  * Copyright (C) 1999-2012 Hiroyuki Yamamoto and the Claws Mail team
4  *
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.
9  *
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.
14  *
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/>.
17  * 
18  */
19
20 /* code ported from gedit */
21 /* This is for my patient girlfirend Regina */
22
23 #ifdef HAVE_CONFIG_H
24 #  include "config.h"
25 #include "claws-features.h"
26 #endif
27
28 #include <glib.h>
29
30 #include <string.h> /* for strlen */
31 #include <stdlib.h> /* for mbstowcs */
32
33 #include "undo.h"
34 #include "utils.h"
35 #include "prefs_common.h"
36
37 typedef struct _UndoInfo UndoInfo;
38
39 struct _UndoInfo 
40 {
41         UndoAction action;
42         gchar *text;
43         gint start_pos;
44         gint end_pos;
45         gfloat window_position;
46         gint mergeable;
47 };
48
49 static void undo_free_list      (GList         **list_pointer);
50 static void undo_check_size     (UndoMain       *undostruct);
51 static gint undo_merge          (GList          *list,
52                                  guint           start_pos,
53                                  guint           end_pos,
54                                  gint            action,
55                                  const guchar   *text);
56 static void undo_add            (const gchar    *text,
57                                  gint            start_pos,
58                                  gint            end_pos,
59                                  UndoAction      action,
60                                  UndoMain       *undostruct);
61 static gint undo_get_selection  (GtkTextView    *textview,
62                                  guint          *start,
63                                  guint          *end);
64 static void undo_insert_text_cb (GtkTextBuffer  *textbuf,
65                                  GtkTextIter    *iter,
66                                  gchar          *new_text,
67                                  gint           new_text_length,
68                                  UndoMain       *undostruct);
69 static void undo_delete_text_cb (GtkTextBuffer  *textbuf,
70                                  GtkTextIter    *start,
71                                  GtkTextIter    *end,
72                                  UndoMain       *undostruct);
73
74 static void undo_paste_clipboard_cb     (GtkTextView    *textview,
75                                          UndoMain       *undostruct);
76
77 void undo_undo                  (UndoMain       *undostruct);
78 void undo_redo                  (UndoMain       *undostruct);
79
80
81 UndoMain *undo_init(GtkWidget *text) 
82 {
83         UndoMain *undostruct;
84         GtkTextView *textview = GTK_TEXT_VIEW(text); 
85         GtkTextBuffer *textbuf = gtk_text_view_get_buffer(textview);
86
87         cm_return_val_if_fail(text != NULL, NULL);
88
89         undostruct = g_new0(UndoMain, 1);
90         undostruct->textview = textview;
91         undostruct->undo = NULL;
92         undostruct->redo = NULL;
93         undostruct->paste = 0;
94         undostruct->undo_state = FALSE;
95         undostruct->redo_state = FALSE;
96
97         g_signal_connect(G_OBJECT(textbuf), "insert-text",
98                          G_CALLBACK(undo_insert_text_cb), undostruct);
99         g_signal_connect(G_OBJECT(textbuf), "delete-range",
100                          G_CALLBACK(undo_delete_text_cb), undostruct);
101         g_signal_connect(G_OBJECT(textview), "paste-clipboard",
102                          G_CALLBACK(undo_paste_clipboard_cb), undostruct);
103
104         return undostruct;
105 }
106
107 void undo_destroy (UndoMain *undostruct) 
108 {
109         undo_free_list(&undostruct->undo);
110         undo_free_list(&undostruct->redo);
111         g_free(undostruct);
112 }
113
114 static UndoInfo *undo_object_new(gchar *text, gint start_pos, gint end_pos, 
115                                  UndoAction action, gfloat window_position) 
116 {
117         UndoInfo *undoinfo;
118         undoinfo = g_new (UndoInfo, 1);
119         undoinfo->text      = text;
120         undoinfo->start_pos = start_pos;
121         undoinfo->end_pos   = end_pos;
122         undoinfo->action    = action;
123         undoinfo->window_position = window_position;
124         return undoinfo;
125 }
126
127 static void undo_object_free(UndoInfo *undo) 
128 {
129         g_free (undo->text);
130         g_free (undo);
131 }
132
133 /**
134  * undo_free_list:
135  * @list_pointer: list to be freed
136  *
137  * frees and undo structure list
138  **/
139 static void undo_free_list(GList **list_pointer) 
140 {
141         UndoInfo *undo;
142         GList *cur, *list = *list_pointer;
143
144         if (list == NULL) return;
145
146         for (cur = list; cur != NULL; cur = cur->next) {
147                 undo = (UndoInfo *)cur->data;
148                 undo_object_free(undo);
149         }
150
151         g_list_free(list);
152         *list_pointer = NULL;
153 }
154
155 void undo_set_change_state_func(UndoMain *undostruct, UndoChangeStateFunc func,
156                                 gpointer data)
157 {
158         cm_return_if_fail(undostruct != NULL);
159
160         undostruct->change_state_func = func;
161         undostruct->change_state_data = data;
162 }
163
164 /**
165  * undo_check_size:
166  * @compose: document to check
167  *
168  * Checks that the size of compose->undo does not excede settings->undo_levels and
169  * frees any undo level above sett->undo_level.
170  *
171  **/
172 static void undo_check_size(UndoMain *undostruct) 
173 {
174         UndoInfo *last_undo;
175         guint length;
176
177         if (prefs_common.undolevels < 1) return;
178
179         /* No need to check for the redo list size since the undo
180            list gets freed on any call to compose_undo_add */
181         length = g_list_length(undostruct->undo);
182         if (length >= prefs_common.undolevels && prefs_common.undolevels > 0) {
183                 last_undo = (UndoInfo *)g_list_last(undostruct->undo)->data;
184                 undostruct->undo = g_list_remove(undostruct->undo, last_undo);
185                 undo_object_free(last_undo);
186         }
187 }
188
189 /**
190  * undo_merge:
191  * @last_undo:
192  * @start_pos:
193  * @end_pos:
194  * @action:
195  *
196  * This function tries to merge the undo object at the top of
197  * the stack with a new set of data. So when we undo for example
198  * typing, we can undo the whole word and not each letter by itself
199  *
200  * Return Value: TRUE is merge was sucessful, FALSE otherwise
201  **/
202 static gint undo_merge(GList *list, guint start_pos, guint end_pos,
203                        gint action, const guchar *text) 
204 {
205         guchar *temp_string;
206         UndoInfo *last_undo;
207
208         /* This are the cases in which we will NOT merge :
209            1. if (last_undo->mergeable == FALSE)
210            [mergeable = FALSE when the size of the undo data was not 1.
211            or if the data was size = 1 but = '\n' or if the undo object
212            has been "undone" already ]
213            2. The size of text is not 1
214            3. If the new merging data is a '\n'
215            4. If the last char of the undo_last data is a space/tab
216            and the new char is not a space/tab ( so that we undo
217            words and not chars )
218            5. If the type (action) of undo is different from the last one
219            Chema */
220
221         if (list == NULL) return FALSE;
222
223         last_undo = list->data;
224
225         if (!last_undo->mergeable) return FALSE;
226
227         if (end_pos - start_pos != 1 ||
228             text[0] == '\n' ||
229             action != last_undo->action ||
230             action == UNDO_ACTION_REPLACE_INSERT ||
231             action == UNDO_ACTION_REPLACE_DELETE) {
232                 last_undo->mergeable = FALSE;
233                 return FALSE;
234         }
235
236         if (action == UNDO_ACTION_DELETE) {
237                 if (last_undo->start_pos != end_pos &&
238                     last_undo->start_pos != start_pos) {
239                         last_undo->mergeable = FALSE;
240                         return FALSE;
241                 } else if (last_undo->start_pos == start_pos) {
242                         /* Deleted with the delete key */
243                         temp_string = g_strdup_printf("%s%s", last_undo->text, text);
244                         last_undo->end_pos++;
245                         g_free(last_undo->text);
246                         last_undo->text = temp_string;
247                 } else {
248                         /* Deleted with the backspace key */
249                         temp_string = g_strdup_printf("%s%s", text, last_undo->text);
250                         last_undo->start_pos = start_pos;
251                         g_free(last_undo->text);
252                         last_undo->text = temp_string;
253                 }
254         } else if (action == UNDO_ACTION_INSERT) {
255                 if (last_undo->end_pos != start_pos) {
256                         last_undo->mergeable = FALSE;
257                         return FALSE;
258                 } else {
259                         temp_string = g_strdup_printf("%s%s", last_undo->text, text);
260                         g_free(last_undo->text);
261                         last_undo->end_pos = end_pos;
262                         last_undo->text = temp_string;
263                 }
264         } else
265                 debug_print("Unknown action [%i] inside undo merge encountered", action);
266
267         return TRUE;
268 }
269
270 /**
271  * compose_undo_add:
272  * @text:
273  * @start_pos:
274  * @end_pos:
275  * @action: either UNDO_ACTION_INSERT or UNDO_ACTION_DELETE
276  * @compose:
277  * @view: The view so that we save the scroll bar position.
278  *
279  * Adds text to the undo stack. It also performs test to limit the number
280  * of undo levels and deltes the redo list
281  **/
282
283 static void undo_add(const gchar *text, 
284                      gint start_pos, gint end_pos,
285                      UndoAction action, UndoMain *undostruct) 
286 {
287         UndoInfo *undoinfo;
288         GtkAdjustment *vadj;
289
290         cm_return_if_fail(text != NULL);
291         cm_return_if_fail(end_pos >= start_pos);
292
293         undo_free_list(&undostruct->redo);
294
295         /* Set the redo sensitivity */
296         undostruct->change_state_func(undostruct,
297                                       UNDO_STATE_UNCHANGED, UNDO_STATE_FALSE,
298                                       undostruct->change_state_data);
299
300         if (undostruct->paste != 0) {
301                 if (action == UNDO_ACTION_INSERT) 
302                         action = UNDO_ACTION_REPLACE_INSERT;
303                 else 
304                         action = UNDO_ACTION_REPLACE_DELETE;
305                 undostruct->paste = undostruct->paste + 1;
306                 if (undostruct->paste == 3) 
307                         undostruct->paste = 0;
308         }
309
310         if (undo_merge(undostruct->undo, start_pos, end_pos, action, text))
311                 return;
312
313         undo_check_size(undostruct);
314
315         vadj = GTK_ADJUSTMENT(gtk_text_view_get_vadjustment(
316                                 GTK_TEXT_VIEW(undostruct->textview)));
317         undoinfo = undo_object_new(g_strdup(text), start_pos, end_pos, action,
318                                    gtk_adjustment_get_value(vadj));
319
320         if (end_pos - start_pos != 1 || text[0] == '\n')
321                 undoinfo->mergeable = FALSE;
322         else
323                 undoinfo->mergeable = TRUE;
324
325         undostruct->undo = g_list_prepend(undostruct->undo, undoinfo);
326
327         undostruct->change_state_func(undostruct,
328                                       UNDO_STATE_TRUE, UNDO_STATE_UNCHANGED,
329                                       undostruct->change_state_data);
330 }
331
332 /**
333  * undo_undo:
334  * @w: not used
335  * @data: not used
336  *
337  * Executes an undo request on the current document
338  **/
339 void undo_undo(UndoMain *undostruct) 
340 {
341         UndoInfo *undoinfo;
342         GtkTextView *textview;
343         GtkTextBuffer *buffer;
344         GtkTextIter iter, start_iter, end_iter;
345         GtkTextMark *mark;
346
347         cm_return_if_fail(undostruct != NULL);
348
349         if (undostruct->undo == NULL) return;
350
351         /* The undo data we need is always at the top op the
352            stack. So, therefore, the first one */
353         undoinfo = (UndoInfo *)undostruct->undo->data;
354         cm_return_if_fail(undoinfo != NULL);
355         undoinfo->mergeable = FALSE;
356         undostruct->redo = g_list_prepend(undostruct->redo, undoinfo);
357         undostruct->undo = g_list_remove(undostruct->undo, undoinfo);
358
359         textview = undostruct->textview;
360         buffer = gtk_text_view_get_buffer(textview);
361
362         undo_block(undostruct);
363
364         /* Check if there is a selection active */
365         mark = gtk_text_buffer_get_insert(buffer);
366         gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
367         gtk_text_buffer_place_cursor(buffer, &iter);
368
369         /* Move the view (scrollbars) to the correct position */
370         gtk_adjustment_set_value
371                 (GTK_ADJUSTMENT(gtk_text_view_get_vadjustment(textview)),
372                  undoinfo->window_position);
373         
374         switch (undoinfo->action) {
375         case UNDO_ACTION_DELETE:
376                 gtk_text_buffer_get_iter_at_offset(buffer, &iter, undoinfo->start_pos);
377                 gtk_text_buffer_insert(buffer, &iter, undoinfo->text, -1);
378                 break;
379         case UNDO_ACTION_INSERT:
380                 gtk_text_buffer_get_iter_at_offset(buffer, &start_iter, undoinfo->start_pos);
381                 gtk_text_buffer_get_iter_at_offset(buffer, &end_iter, undoinfo->end_pos);
382                 gtk_text_buffer_delete(buffer, &start_iter, &end_iter);
383                 break;
384         case UNDO_ACTION_REPLACE_INSERT:
385                 gtk_text_buffer_get_iter_at_offset(buffer, &start_iter, undoinfo->start_pos);
386                 gtk_text_buffer_get_iter_at_offset(buffer, &end_iter, undoinfo->end_pos);
387                 gtk_text_buffer_delete(buffer, &start_iter, &end_iter);
388                 /* "pull" another data structure from the list */
389                 if (undostruct->undo){
390                         undoinfo = (UndoInfo *)undostruct->undo->data;
391                         undostruct->redo = g_list_prepend(undostruct->redo, undoinfo);
392                         undostruct->undo = g_list_remove(undostruct->undo, undoinfo);
393                         cm_return_if_fail(undoinfo != NULL);
394                         cm_return_if_fail(undoinfo->action == UNDO_ACTION_REPLACE_DELETE);
395                         gtk_text_buffer_insert(buffer, &start_iter, undoinfo->text, -1);
396                 }
397                 break;
398         case UNDO_ACTION_REPLACE_DELETE:
399                 g_warning("This should not happen. UNDO_REPLACE_DELETE");
400                 break;
401         default:
402                 g_assert_not_reached();
403                 break;
404         }
405         
406         undostruct->change_state_func(undostruct,
407                                       UNDO_STATE_UNCHANGED, UNDO_STATE_TRUE,
408                                       undostruct->change_state_data);
409
410         if (undostruct->undo == NULL)
411                 undostruct->change_state_func(undostruct,
412                                               UNDO_STATE_FALSE,
413                                               UNDO_STATE_UNCHANGED,
414                                               undostruct->change_state_data);
415
416         undo_unblock(undostruct);
417 }
418
419 /**
420  * undo_redo:
421  * @w: not used
422  * @data: not used
423  *
424  * executes a redo request on the current document
425  **/
426 void undo_redo(UndoMain *undostruct) 
427 {
428         UndoInfo *redoinfo;
429         GtkTextView *textview;
430         GtkTextBuffer *buffer;
431         GtkTextIter iter, start_iter, end_iter;
432         GtkTextMark *mark;
433
434         cm_return_if_fail(undostruct != NULL);
435
436         if (undostruct->redo == NULL) return;
437
438         redoinfo = (UndoInfo *)undostruct->redo->data;
439         cm_return_if_fail (redoinfo != NULL);
440         undostruct->undo = g_list_prepend(undostruct->undo, redoinfo);
441         undostruct->redo = g_list_remove(undostruct->redo, redoinfo);
442
443         textview = undostruct->textview;
444         buffer = gtk_text_view_get_buffer(textview);
445
446         undo_block(undostruct);
447
448         /* Check if there is a selection active */
449         mark = gtk_text_buffer_get_insert(buffer);
450         gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
451         gtk_text_buffer_place_cursor(buffer, &iter);
452
453         /* Move the view to the right position. */
454         gtk_adjustment_set_value(gtk_text_view_get_vadjustment(textview), 
455                                  redoinfo->window_position);
456
457         switch (redoinfo->action) {
458         case UNDO_ACTION_INSERT:
459                 gtk_text_buffer_get_iter_at_offset(buffer, &iter, redoinfo->start_pos);
460                 gtk_text_buffer_insert(buffer, &iter, redoinfo->text, -1);
461                 break;
462         case UNDO_ACTION_DELETE:
463                 gtk_text_buffer_get_iter_at_offset(buffer, &start_iter, redoinfo->start_pos);
464                 gtk_text_buffer_get_iter_at_offset(buffer, &end_iter, redoinfo->end_pos);
465                 gtk_text_buffer_delete(buffer, &start_iter, &end_iter);
466                 break;
467         case UNDO_ACTION_REPLACE_DELETE:
468                 gtk_text_buffer_get_iter_at_offset(buffer, &start_iter, redoinfo->start_pos);
469                 gtk_text_buffer_get_iter_at_offset(buffer, &end_iter, redoinfo->end_pos);
470                 gtk_text_buffer_delete(buffer, &start_iter, &end_iter);
471                 debug_print("UNDO_ACTION_REPLACE %s\n", redoinfo->text);
472                 /* "pull" another data structure from the list */
473                 redoinfo = (UndoInfo *)undostruct->redo->data;
474                 cm_return_if_fail(redoinfo != NULL);
475                 undostruct->undo = g_list_prepend(undostruct->undo, redoinfo);
476                 undostruct->redo = g_list_remove(undostruct->redo, redoinfo);
477                 cm_return_if_fail(redoinfo->action == UNDO_ACTION_REPLACE_INSERT);
478                 gtk_text_buffer_insert(buffer, &start_iter, redoinfo->text, -1);
479                 break;
480         case UNDO_ACTION_REPLACE_INSERT:
481                 /* This is needed only if we redo from a middle-click button */
482                 gtk_text_buffer_get_iter_at_offset(buffer, &iter, redoinfo->start_pos);
483                 gtk_text_buffer_insert(buffer, &iter, redoinfo->text, -1);
484                 break;
485         default:
486                 g_assert_not_reached();
487                 break;
488         }
489
490         undostruct->change_state_func(undostruct,
491                                       UNDO_STATE_TRUE, UNDO_STATE_UNCHANGED, 
492                                       undostruct->change_state_data);
493
494         if (undostruct->redo == NULL)
495                 undostruct->change_state_func(undostruct,
496                                               UNDO_STATE_UNCHANGED,
497                                               UNDO_STATE_FALSE,
498                                               undostruct->change_state_data);
499
500         undo_unblock(undostruct);
501 }
502
503 void undo_block(UndoMain *undostruct)
504 {
505         GtkTextBuffer *buffer;
506
507         cm_return_if_fail(GTK_IS_TEXT_VIEW(undostruct->textview));
508
509         buffer = gtk_text_view_get_buffer(undostruct->textview);
510         g_signal_handlers_block_by_func(buffer, undo_insert_text_cb, undostruct);
511         g_signal_handlers_block_by_func(buffer, undo_delete_text_cb, undostruct);
512         g_signal_handlers_block_by_func(buffer, undo_paste_clipboard_cb,
513                                           undostruct);
514 }
515
516 void undo_unblock(UndoMain *undostruct)
517 {
518         GtkTextBuffer *buffer;
519
520         cm_return_if_fail(GTK_IS_TEXT_VIEW(undostruct->textview));
521
522         buffer = gtk_text_view_get_buffer(undostruct->textview);
523         g_signal_handlers_unblock_by_func(buffer, undo_insert_text_cb, undostruct);
524         g_signal_handlers_unblock_by_func(buffer, undo_delete_text_cb, undostruct);
525         g_signal_handlers_unblock_by_func(buffer, undo_paste_clipboard_cb,
526                                           undostruct);
527 }
528
529 void undo_wrapping(UndoMain *undostruct, gboolean wrap)
530 {
531 //      debug_print("undo wrapping now %d\n", wrap);
532         undostruct->wrap = wrap;
533 }
534
535 void undo_insert_text_cb(GtkTextBuffer *textbuf, GtkTextIter *iter,
536                          gchar *new_text, gint new_text_length,
537                          UndoMain *undostruct) 
538 {
539         gchar *text_to_insert;
540         gint pos;
541         if (prefs_common.undolevels <= 0) return;
542
543         pos = gtk_text_iter_get_offset(iter);
544         if (undostruct->wrap && undostruct->undo) {
545                 UndoInfo *last_undo = undostruct->undo->data;
546                 if (last_undo && (last_undo->action == UNDO_ACTION_INSERT
547                                   || last_undo->action == UNDO_ACTION_REPLACE_INSERT)
548                 &&  last_undo->start_pos < pos && last_undo->end_pos > pos) {
549                         GtkTextIter start,end;
550                         last_undo->end_pos += g_utf8_strlen(new_text, -1);
551                         gtk_text_buffer_get_iter_at_offset(textbuf, &start, last_undo->start_pos);
552                         gtk_text_buffer_get_iter_at_offset(textbuf, &end, last_undo->end_pos);
553                         g_free(last_undo->text);
554                         last_undo->text = gtk_text_buffer_get_text(textbuf, &start, &end, FALSE);
555                         debug_print("add:undo upd %d-%d\n", last_undo->start_pos, last_undo->end_pos);
556                         return;
557                 } else if (last_undo)
558                         debug_print("add:last: %d, %d-%d (%d)\n", last_undo->action,
559                                 last_undo->start_pos, last_undo->end_pos, pos);
560         } 
561         Xstrndup_a(text_to_insert, new_text, new_text_length, return);
562         debug_print("add:undo add %d-%ld\n", pos, pos + g_utf8_strlen(text_to_insert, -1));
563         undo_add(text_to_insert, pos, pos + g_utf8_strlen(text_to_insert, -1),
564                  UNDO_ACTION_INSERT, undostruct);
565 }
566
567 void undo_delete_text_cb(GtkTextBuffer *textbuf, GtkTextIter *start,
568                          GtkTextIter *end, UndoMain *undostruct) 
569 {
570         gchar *text_to_delete;
571         gint start_pos, end_pos;
572
573         if (prefs_common.undolevels <= 0) return;
574
575         text_to_delete = gtk_text_buffer_get_text(textbuf, start, end, FALSE);
576         if (!text_to_delete || !*text_to_delete) return;
577
578         start_pos = gtk_text_iter_get_offset(start);
579         end_pos   = gtk_text_iter_get_offset(end);
580
581         if (undostruct->wrap && undostruct->undo) {
582                 UndoInfo *last_undo = undostruct->undo->data;
583                 if (last_undo && (last_undo->action == UNDO_ACTION_INSERT
584                                   || last_undo->action == UNDO_ACTION_REPLACE_INSERT)
585                 &&  last_undo->start_pos < start_pos && last_undo->end_pos > end_pos) {
586                         GtkTextIter start,end;
587                         last_undo->end_pos -= g_utf8_strlen(text_to_delete, -1);
588                         gtk_text_buffer_get_iter_at_offset(textbuf, &start, last_undo->start_pos);
589                         gtk_text_buffer_get_iter_at_offset(textbuf, &end, last_undo->end_pos);
590                         g_free(last_undo->text);
591                         last_undo->text = gtk_text_buffer_get_text(textbuf, &start, &end, FALSE);
592                         debug_print("del:undo upd %d-%d\n", last_undo->start_pos, last_undo->end_pos);
593                         return;
594                 } else if (last_undo)
595                         debug_print("del:last: %d, %d-%d (%d)\n", last_undo->action,
596                                 last_undo->start_pos, last_undo->end_pos, start_pos);
597                 
598         } 
599         debug_print("del:undo add %d-%d\n", start_pos, end_pos);
600         undo_add(text_to_delete, start_pos, end_pos, UNDO_ACTION_DELETE,
601                  undostruct);
602         g_free(text_to_delete);
603 }
604
605 void undo_paste_clipboard(GtkTextView *textview, UndoMain *undostruct)
606 {
607         undo_paste_clipboard_cb(textview, undostruct);
608 }
609
610 static void undo_paste_clipboard_cb(GtkTextView *textview, UndoMain *undostruct)
611 {
612         if (prefs_common.undolevels > 0)
613                 if (undo_get_selection(textview, NULL, NULL))
614                         undostruct->paste = TRUE;
615 }
616
617 /**
618  * undo_get_selection:
619  * @text: Text to get the selection from
620  * @start: return here the start position of the selection
621  * @end: return here the end position of the selection
622  *
623  * Gets the current selection for View
624  *
625  * Return Value: TRUE if there is a selection active, FALSE if not
626  **/
627 static gint undo_get_selection(GtkTextView *textview, guint *start, guint *end) 
628 {
629         GtkTextBuffer *buffer;
630         GtkTextIter start_iter, end_iter;
631         guint start_pos, end_pos;
632
633         buffer = gtk_text_view_get_buffer(textview);
634         gtk_text_buffer_get_selection_bounds(buffer, &start_iter, &end_iter);
635
636         start_pos = gtk_text_iter_get_offset(&start_iter);
637         end_pos   = gtk_text_iter_get_offset(&end_iter);
638
639         /* The user can select from end to start too. If so, swap it*/
640         if (end_pos < start_pos) {
641                 guint swap_pos;
642                 swap_pos  = end_pos;
643                 end_pos   = start_pos;
644                 start_pos = swap_pos;
645         }
646
647         if (start != NULL)
648                 *start = start_pos;
649                 
650         if (end != NULL)
651                 *end = end_pos;
652
653         if ((start_pos > 0 || end_pos > 0) && (start_pos != end_pos))
654                 return TRUE;
655         else
656                 return FALSE;
657 }