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