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