Statistics
| Revision:

root / src / undo.c @ 1

History | View | Annotate | Download (18.1 KB)

1
/*
2
 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3
 * Copyright (C) 1999-2001 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 <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;
86

    
87
        g_return_val_if_fail(text != NULL, NULL);
88

    
89
        textbuf = gtk_text_view_get_buffer(textview);
90

    
91
        undostruct = g_new(UndoMain, 1);
92
        undostruct->textview = textview;
93
        undostruct->undo = NULL;
94
        undostruct->redo = NULL;
95
        undostruct->paste = 0;
96
        undostruct->undo_state = FALSE;
97
        undostruct->redo_state = FALSE;
98

    
99
        g_signal_connect(G_OBJECT(text), "insert-text",
100
                         G_CALLBACK(undo_insert_text_cb), undostruct);
101
        g_signal_connect(G_OBJECT(text), "delete-text",
102
                         G_CALLBACK(undo_delete_text_cb), undostruct);
103
        g_signal_connect(G_OBJECT(text), "paste-clipboard",
104
                         G_CALLBACK(undo_paste_clipboard_cb), undostruct);
105

    
106
        return undostruct;
107
}
108

    
109
void undo_destroy (UndoMain *undostruct) 
110
{
111
        undo_free_list(&undostruct->undo);
112
        undo_free_list(&undostruct->redo);
113
        g_free(undostruct);
114
}
115

    
116
static UndoInfo *undo_object_new(gchar *text, gint start_pos, gint end_pos, 
117
                                 UndoAction action, gfloat window_position) 
118
{
119
        UndoInfo *undoinfo;
120
        undoinfo = g_new (UndoInfo, 1);
121
        undoinfo->text      = text;
122
        undoinfo->start_pos = start_pos;
123
        undoinfo->end_pos   = end_pos;
124
        undoinfo->action    = action;
125
        undoinfo->window_position = window_position;
126
        return undoinfo;
127
}
128

    
129
static void undo_object_free(UndoInfo *undo) 
130
{
131
        g_free (undo->text);
132
        g_free (undo);
133
}
134

    
135
/**
136
 * undo_free_list:
137
 * @list_pointer: list to be freed
138
 *
139
 * frees and undo structure list
140
 **/
141
static void undo_free_list(GList **list_pointer) 
142
{
143
        UndoInfo *undo;
144
        GList *cur, *list = *list_pointer;
145

    
146
        if (list == NULL) return;
147

    
148
        for (cur = list; cur != NULL; cur = cur->next) {
149
                undo = (UndoInfo *)cur->data;
150
                undo_object_free(undo);
151
        }
152

    
153
        g_list_free(list);
154
        *list_pointer = NULL;
155
}
156

    
157
void undo_set_change_state_func(UndoMain *undostruct, UndoChangeStateFunc func,
158
                                gpointer data)
159
{
160
        g_return_if_fail(undostruct != NULL);
161

    
162
        undostruct->change_state_func = func;
163
        undostruct->change_state_data = data;
164
}
165

    
166
/**
167
 * undo_check_size:
168
 * @compose: document to check
169
 *
170
 * Checks that the size of compose->undo does not excede settings->undo_levels and
171
 * frees any undo level above sett->undo_level.
172
 *
173
 **/
174
static void undo_check_size(UndoMain *undostruct) 
175
{
176
        UndoInfo *last_undo;
177
        guint length;
178

    
179
        if (prefs_common.undolevels < 1) return;
180

    
181
        /* No need to check for the redo list size since the undo
182
           list gets freed on any call to compose_undo_add */
183
        length = g_list_length(undostruct->undo);
184
        if (length >= prefs_common.undolevels && prefs_common.undolevels > 0) {
185
                last_undo = (UndoInfo *)g_list_last(undostruct->undo)->data;
186
                undostruct->undo = g_list_remove(undostruct->undo, last_undo);
187
                undo_object_free(last_undo);
188
        }
189
}
190

    
191
/**
192
 * undo_merge:
193
 * @last_undo:
194
 * @start_pos:
195
 * @end_pos:
196
 * @action:
197
 *
198
 * This function tries to merge the undo object at the top of
199
 * the stack with a new set of data. So when we undo for example
200
 * typing, we can undo the whole word and not each letter by itself
201
 *
202
 * Return Value: TRUE is merge was sucessful, FALSE otherwise
203
 **/
204
static gint undo_merge(GList *list, guint start_pos, guint end_pos,
205
                       gint action, const guchar *text) 
206
{
207
        guchar *temp_string;
208
        UndoInfo *last_undo;
209

    
210
        /* This are the cases in which we will NOT merge :
211
           1. if (last_undo->mergeable == FALSE)
212
           [mergeable = FALSE when the size of the undo data was not 1.
213
           or if the data was size = 1 but = '\n' or if the undo object
214
           has been "undone" already ]
215
           2. The size of text is not 1
216
           3. If the new merging data is a '\n'
217
           4. If the last char of the undo_last data is a space/tab
218
           and the new char is not a space/tab ( so that we undo
219
           words and not chars )
220
           5. If the type (action) of undo is different from the last one
221
           Chema */
222

    
223
        if (list == NULL) return FALSE;
224

    
225
        last_undo = list->data;
226

    
227
        if (!last_undo->mergeable) return FALSE;
228

    
229
        if (end_pos - start_pos != 1 ||
230
            text[0] == '\n' ||
231
            action != last_undo->action ||
232
            action == UNDO_ACTION_REPLACE_INSERT ||
233
            action == UNDO_ACTION_REPLACE_DELETE) {
234
                last_undo->mergeable = FALSE;
235
                return FALSE;
236
        }
237

    
238
        if (action == UNDO_ACTION_DELETE) {
239
                gboolean checkit = TRUE;
240

    
241
                if (last_undo->start_pos != end_pos &&
242
                    last_undo->start_pos != start_pos) {
243
                        last_undo->mergeable = FALSE;
244
                        return FALSE;
245
                } else if (last_undo->start_pos == start_pos) {
246
                        /* Deleted with the delete key */
247
                        if (text[0] != ' ' && text[0] != '\t' &&
248
                            (last_undo->text[last_undo->end_pos - last_undo->start_pos - 1] == ' ' ||
249
                             last_undo->text[last_undo->end_pos - last_undo->start_pos - 1] == '\t'))
250
                                checkit = FALSE;
251

    
252
                        temp_string = g_strdup_printf("%s%s", last_undo->text, text);
253
                        last_undo->end_pos++;
254
                        g_free(last_undo->text);
255
                        last_undo->text = temp_string;
256
                } else {
257
                        /* Deleted with the backspace key */
258
                        if (text[0] != ' ' && text[0] != '\t' &&
259
                            (last_undo->text[0] == ' ' ||
260
                             last_undo->text[0] == '\t'))
261
                                checkit = FALSE;
262

    
263
                        temp_string = g_strdup_printf("%s%s", text, last_undo->text);
264
                        last_undo->start_pos = start_pos;
265
                        g_free(last_undo->text);
266
                        last_undo->text = temp_string;
267
                }
268

    
269
                if (!checkit) {
270
                        last_undo->mergeable = FALSE;
271
                        return FALSE;
272
                }
273
        } else if (action == UNDO_ACTION_INSERT) {
274
                if (last_undo->end_pos != start_pos) {
275
                        last_undo->mergeable = FALSE;
276
                        return FALSE;
277
                } else {
278
                        temp_string = g_strdup_printf("%s%s", last_undo->text, text);
279
                        g_free(last_undo->text);
280
                        last_undo->end_pos = end_pos;
281
                        last_undo->text = temp_string;
282
                }
283
        } else
284
                debug_print("Unknown action [%i] inside undo merge encountered", action);
285

    
286
        return TRUE;
287
}
288

    
289
/**
290
 * compose_undo_add:
291
 * @text:
292
 * @start_pos:
293
 * @end_pos:
294
 * @action: either UNDO_ACTION_INSERT or UNDO_ACTION_DELETE
295
 * @compose:
296
 * @view: The view so that we save the scroll bar position.
297
 *
298
 * Adds text to the undo stack. It also performs test to limit the number
299
 * of undo levels and deltes the redo list
300
 **/
301

    
302
static void undo_add(const gchar *text, 
303
                     gint start_pos, gint end_pos,
304
                     UndoAction action, UndoMain *undostruct) 
305
{
306
        UndoInfo *undoinfo;
307
        GtkAdjustment *vadj;
308

    
309
        g_return_if_fail(text != NULL);
310
        g_return_if_fail(end_pos >= start_pos);
311

    
312
        undo_free_list(&undostruct->redo);
313

    
314
        /* Set the redo sensitivity */
315
        undostruct->change_state_func(undostruct,
316
                                      UNDO_STATE_UNCHANGED, UNDO_STATE_FALSE,
317
                                      undostruct->change_state_data);
318

    
319
        if (undostruct->paste != 0) {
320
                if (action == UNDO_ACTION_INSERT) 
321
                        action = UNDO_ACTION_REPLACE_INSERT;
322
                else 
323
                        action = UNDO_ACTION_REPLACE_DELETE;
324
                undostruct->paste = undostruct->paste + 1;
325
                if (undostruct->paste == 3) 
326
                        undostruct->paste = 0;
327
        }
328

    
329
        if (undo_merge(undostruct->undo, start_pos, end_pos, action, text))
330
                return;
331

    
332
        undo_check_size(undostruct);
333

    
334
        vadj = GTK_ADJUSTMENT(GTK_TEXT_VIEW(undostruct->textview)->vadjustment);
335
        undoinfo = undo_object_new(g_strdup(text), start_pos, end_pos, action,
336
                                   vadj->value);
337

    
338
        if (end_pos - start_pos != 1 || text[0] == '\n')
339
                undoinfo->mergeable = FALSE;
340
        else
341
                undoinfo->mergeable = TRUE;
342

    
343
        undostruct->undo = g_list_prepend(undostruct->undo, undoinfo);
344

    
345
        undostruct->change_state_func(undostruct,
346
                                      UNDO_STATE_TRUE, UNDO_STATE_UNCHANGED,
347
                                      undostruct->change_state_data);
348
}
349

    
350
/**
351
 * undo_undo:
352
 * @w: not used
353
 * @data: not used
354
 *
355
 * Executes an undo request on the current document
356
 **/
357
void undo_undo(UndoMain *undostruct) 
358
{
359
        UndoInfo *undoinfo;
360
        GtkTextView *textview;
361
        GtkTextBuffer *buffer;
362
        GtkTextIter iter, start_iter, end_iter;
363
        GtkTextMark *mark;
364

    
365
        g_return_if_fail(undostruct != NULL);
366

    
367
        if (undostruct->undo == NULL) return;
368

    
369
        /* The undo data we need is always at the top op the
370
           stack. So, therefore, the first one */
371
        undoinfo = (UndoInfo *)undostruct->undo->data;
372
        g_return_if_fail(undoinfo != NULL);
373
        undoinfo->mergeable = FALSE;
374
        undostruct->redo = g_list_prepend(undostruct->redo, undoinfo);
375
        undostruct->undo = g_list_remove(undostruct->undo, undoinfo);
376

    
377
        textview = undostruct->textview;
378
        buffer = gtk_text_view_get_buffer(textview);
379

    
380
        undo_block(undostruct);
381

    
382
        /* Check if there is a selection active */
383
        mark = gtk_text_buffer_get_insert(buffer);
384
        gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
385
        gtk_text_buffer_place_cursor(buffer, &iter);
386

    
387
        /* Move the view (scrollbars) to the correct position */
388
        gtk_adjustment_set_value(GTK_ADJUSTMENT(textview->vadjustment),
389
                                 undoinfo->window_position);
390

    
391
        switch (undoinfo->action) {
392
        case UNDO_ACTION_DELETE:
393
                gtk_text_buffer_get_iter_at_offset
394
                        (buffer, &iter, undoinfo->start_pos);
395
                gtk_text_buffer_insert(buffer, &iter, undoinfo->text, -1);
396
                debug_print("UNDO_ACTION_DELETE %s\n", undoinfo->text);
397
                break;
398
        case UNDO_ACTION_INSERT:
399
                gtk_text_buffer_get_iter_at_offset
400
                        (buffer, &start_iter, undoinfo->start_pos);
401
                gtk_text_buffer_get_iter_at_offset
402
                        (buffer, &end_iter, undoinfo->end_pos);
403
                gtk_text_buffer_delete(buffer, &start_iter, &end_iter);
404
                debug_print("UNDO_ACTION_INSERT %d\n", undoinfo->end_pos-undoinfo->start_pos);
405
                break;
406
        case UNDO_ACTION_REPLACE_INSERT:
407
                gtk_text_buffer_get_iter_at_offset
408
                        (buffer, &start_iter, undoinfo->start_pos);
409
                gtk_text_buffer_get_iter_at_offset
410
                        (buffer, &end_iter, undoinfo->end_pos);
411
                debug_print("UNDO_ACTION_REPLACE %s\n", undoinfo->text);
412
                /* "pull" another data structure from the list */
413
                undoinfo = (UndoInfo *)undostruct->undo->data;
414
                g_return_if_fail(undoinfo != NULL);
415
                undostruct->redo = g_list_prepend(undostruct->redo, undoinfo);
416
                undostruct->undo = g_list_remove(undostruct->undo, undoinfo);
417
                g_return_if_fail(undoinfo->action == UNDO_ACTION_REPLACE_DELETE);
418
                gtk_text_buffer_insert(buffer, &start_iter, undoinfo->text, -1);
419
                debug_print("UNDO_ACTION_REPLACE %s\n", undoinfo->text);
420
                break;
421
        case UNDO_ACTION_REPLACE_DELETE:
422
                g_warning("This should not happen. UNDO_REPLACE_DELETE");
423
                break;
424
        default:
425
                g_assert_not_reached();
426
                break;
427
        }
428

    
429
        undostruct->change_state_func(undostruct,
430
                                      UNDO_STATE_UNCHANGED, UNDO_STATE_TRUE,
431
                                      undostruct->change_state_data);
432

    
433
        if (undostruct->undo == NULL)
434
                undostruct->change_state_func(undostruct,
435
                                              UNDO_STATE_FALSE,
436
                                              UNDO_STATE_UNCHANGED,
437
                                              undostruct->change_state_data);
438

    
439
        undo_unblock(undostruct);
440
}
441

    
442
/**
443
 * undo_redo:
444
 * @w: not used
445
 * @data: not used
446
 *
447
 * executes a redo request on the current document
448
 **/
449
void undo_redo(UndoMain *undostruct) 
450
{
451
        UndoInfo *redoinfo;
452
        GtkTextView *textview;
453
        GtkTextBuffer *buffer;
454
        GtkTextIter iter, start_iter, end_iter;
455
        GtkTextMark *mark;
456

    
457
        g_return_if_fail(undostruct != NULL);
458

    
459
        if (undostruct->redo == NULL) return;
460

    
461
        redoinfo = (UndoInfo *)undostruct->redo->data;
462
        g_return_if_fail (redoinfo != NULL);
463
        undostruct->undo = g_list_prepend(undostruct->undo, redoinfo);
464
        undostruct->redo = g_list_remove(undostruct->redo, redoinfo);
465

    
466
        textview = undostruct->textview;
467
        buffer = gtk_text_view_get_buffer(textview);
468

    
469
        undo_block(undostruct);
470

    
471
        /* Check if there is a selection active */
472
        mark = gtk_text_buffer_get_insert(buffer);
473
        gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
474
        gtk_text_buffer_place_cursor(buffer, &iter);
475

    
476
        /* Move the view to the right position. */
477
        gtk_adjustment_set_value(textview->vadjustment, 
478
                                 redoinfo->window_position);
479

    
480
        switch (redoinfo->action) {
481
        case UNDO_ACTION_INSERT:
482
                gtk_text_buffer_get_iter_at_offset
483
                        (buffer, &iter, redoinfo->start_pos);
484
                gtk_text_buffer_insert(buffer, &iter, redoinfo->text, -1);
485
                debug_print("UNDO_ACTION_DELETE %s\n",redoinfo->text);
486
                break;
487
        case UNDO_ACTION_DELETE:
488
                gtk_text_buffer_get_iter_at_offset
489
                        (buffer, &start_iter, redoinfo->start_pos);
490
                gtk_text_buffer_get_iter_at_offset
491
                        (buffer, &end_iter, redoinfo->end_pos);
492
                gtk_text_buffer_delete(buffer, &start_iter, &end_iter);
493
                debug_print("UNDO_ACTION_INSERT %d\n", 
494
                            redoinfo->end_pos-redoinfo->start_pos);
495
                break;
496
        case UNDO_ACTION_REPLACE_DELETE:
497
                gtk_text_buffer_get_iter_at_offset
498
                        (buffer, &start_iter, redoinfo->start_pos);
499
                gtk_text_buffer_get_iter_at_offset
500
                        (buffer, &end_iter, redoinfo->end_pos);
501
                gtk_text_buffer_delete(buffer, &start_iter, &end_iter);
502
                debug_print("UNDO_ACTION_REPLACE %s\n", redoinfo->text);
503
                /* "pull" another data structure from the list */
504
                redoinfo = (UndoInfo *)undostruct->redo->data;
505
                g_return_if_fail(redoinfo != NULL);
506
                undostruct->undo = g_list_prepend(undostruct->undo, redoinfo);
507
                undostruct->redo = g_list_remove(undostruct->redo, redoinfo);
508
                g_return_if_fail(redoinfo->action == UNDO_ACTION_REPLACE_INSERT);
509
                gtk_text_buffer_insert(buffer, &start_iter, redoinfo->text, -1);
510
                break;
511
        case UNDO_ACTION_REPLACE_INSERT:
512
                g_warning("This should not happen. Redo: UNDO_REPLACE_INSERT");
513
                break;
514
        default:
515
                g_assert_not_reached();
516
                break;
517
        }
518

    
519
        undostruct->change_state_func(undostruct,
520
                                      UNDO_STATE_TRUE, UNDO_STATE_UNCHANGED, 
521
                                      undostruct->change_state_data);
522

    
523
        if (undostruct->redo == NULL)
524
                undostruct->change_state_func(undostruct,
525
                                              UNDO_STATE_UNCHANGED,
526
                                              UNDO_STATE_FALSE,
527
                                              undostruct->change_state_data);
528

    
529
        undo_unblock(undostruct);
530
}
531

    
532
void undo_block(UndoMain *undostruct)
533
{
534
        GtkTextBuffer *buffer;
535

    
536
        g_return_if_fail(GTK_IS_TEXT_VIEW(undostruct->textview));
537

    
538
        buffer = gtk_text_view_get_buffer(undostruct->textview);
539
        g_signal_handlers_block_by_func
540
                (buffer, undo_insert_text_cb, undostruct);
541
        g_signal_handlers_block_by_func
542
                (buffer, undo_delete_text_cb, undostruct);
543
        g_signal_handlers_block_by_func
544
                (buffer, undo_paste_clipboard_cb, undostruct);
545
}
546

    
547
void undo_unblock(UndoMain *undostruct)
548
{
549
        GtkTextBuffer *buffer;
550

    
551
        g_return_if_fail(GTK_IS_TEXT_VIEW(undostruct->textview));
552

    
553
        buffer = gtk_text_view_get_buffer(undostruct->textview);
554
        g_signal_handlers_unblock_by_func
555
                (buffer, undo_insert_text_cb, undostruct);
556
        g_signal_handlers_unblock_by_func
557
                (buffer, undo_delete_text_cb, undostruct);
558
        g_signal_handlers_unblock_by_func
559
                (buffer, undo_paste_clipboard_cb, undostruct);
560
}
561

    
562
void undo_insert_text_cb(GtkTextBuffer *textbuf, GtkTextIter *iter,
563
                         gchar *new_text, gint new_text_length,
564
                         UndoMain *undostruct) 
565
{
566
        gchar *text_to_insert;
567
        gint pos;
568

    
569
        if (prefs_common.undolevels <= 0) return;
570

    
571
        pos = gtk_text_iter_get_offset(iter);
572

    
573
        Xstrndup_a(text_to_insert, new_text, new_text_length, return);
574
        undo_add(text_to_insert, pos, pos + g_utf8_strlen(text_to_insert, -1),
575
                 UNDO_ACTION_INSERT, undostruct);
576
}
577

    
578
void undo_delete_text_cb(GtkTextBuffer *textbuf, GtkTextIter *start,
579
                         GtkTextIter *end, UndoMain *undostruct) 
580
{
581
        gchar *text_to_delete;
582
        gint start_pos, end_pos;
583

    
584
        if (prefs_common.undolevels <= 0) return;
585

    
586
        text_to_delete = gtk_text_buffer_get_text(textbuf, start, end, FALSE);
587
        if (!text_to_delete || !*text_to_delete) return;
588

    
589
        start_pos = gtk_text_iter_get_offset(start);
590
        end_pos   = gtk_text_iter_get_offset(end);
591

    
592
        undo_add(text_to_delete, start_pos, end_pos, UNDO_ACTION_DELETE,
593
                 undostruct);
594
        g_free(text_to_delete);
595
}
596

    
597
void undo_paste_clipboard_cb(GtkTextView *textview, UndoMain *undostruct)
598
{
599
        debug_print("before Paste: %d\n", undostruct->paste);
600
        if (prefs_common.undolevels > 0)
601
                if (undo_get_selection(textview, NULL, NULL))
602
                        undostruct->paste = TRUE;
603
        debug_print("after Paste: %d\n", undostruct->paste);
604
}
605

    
606
/**
607
 * undo_get_selection:
608
 * @text: Text to get the selection from
609
 * @start: return here the start position of the selection
610
 * @end: return here the end position of the selection
611
 *
612
 * Gets the current selection for View
613
 *
614
 * Return Value: TRUE if there is a selection active, FALSE if not
615
 **/
616
static gint undo_get_selection(GtkTextView *textview, guint *start, guint *end) 
617
{
618
        GtkTextBuffer *buffer;
619
        GtkTextIter start_iter, end_iter;
620
        guint start_pos, end_pos;
621

    
622
        buffer = gtk_text_view_get_buffer(textview);
623
        gtk_text_buffer_get_selection_bounds(buffer, &start_iter, &end_iter);
624

    
625
        start_pos = gtk_text_iter_get_offset(&start_iter);
626
        end_pos   = gtk_text_iter_get_offset(&end_iter);
627

    
628
        /* The user can select from end to start too. If so, swap it*/
629
        if (end_pos < start_pos) {
630
                guint swap_pos;
631
                swap_pos  = end_pos;
632
                end_pos   = start_pos;
633
                start_pos = swap_pos;
634
        }
635

    
636
        if (start != NULL)
637
                *start = start_pos;
638
                
639
        if (end != NULL)
640
                *end = end_pos;
641

    
642
        if ((start_pos > 0 || end_pos > 0) && (start_pos != end_pos))
643
                return TRUE;
644
        else
645
                return FALSE;
646
}