Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(83)

Side by Side Diff: third_party/undoview/undo_manager.c

Issue 3429025: Landing http://codereview.chromium.org/3387008 for sadrul: (Closed) Base URL: git://git.chromium.org/chromium.git
Patch Set: fix deps issue Created 10 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « third_party/undoview/undo_manager.h ('k') | third_party/undoview/undo_view.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 /*
2 * Copyright (C) 1998, 1999 Alex Roberts, Evan Lawrence
3 * Copyright (C) 2000, 2001 Chema Celorio, Paolo Maggi
4 * Copyright (C) 2002-2005 Paolo Maggi
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19 */
20
21 #ifdef HAVE_CONFIG_H
22 #include <config.h>
23 #endif
24
25 #include <glib.h>
26 #include <stdlib.h>
27 #include <string.h>
28
29 #include "undo_manager.h"
30
31 #define DEFAULT_MAX_UNDO_LEVELS 25
32
33 typedef struct _GtkSourceUndoAction GtkSourceUndoAction;
34 typedef struct _GtkSourceUndoInsertAction GtkSourceUndoInsertAction;
35 typedef struct _GtkSourceUndoDeleteAction GtkSourceUndoDeleteAction;
36
37 typedef enum {
38 GTK_SOURCE_UNDO_ACTION_INSERT,
39 GTK_SOURCE_UNDO_ACTION_DELETE,
40 } GtkSourceUndoActionType;
41
42 /*
43 * We use offsets instead of GtkTextIters because the last ones
44 * require to much memory in this context without giving us any advantage.
45 */
46
47 struct _GtkSourceUndoInsertAction {
48 gint pos;
49 gchar *text;
50 gint length;
51 gint chars;
52 };
53
54 struct _GtkSourceUndoDeleteAction {
55 gint start;
56 gint end;
57 gchar *text;
58 gboolean forward;
59 };
60
61 struct _GtkSourceUndoAction {
62 GtkSourceUndoActionType action_type;
63
64 union {
65 GtkSourceUndoInsertAction insert;
66 GtkSourceUndoDeleteAction delete;
67 } action;
68
69 gint order_in_group;
70
71 /* It is TRUE whether the action can be merged with the following action. */
72 guint mergeable : 1;
73
74 /* It is TRUE whether the action is marked as "modified".
75 * An action is marked as "modified" if it changed the
76 * state of the buffer from "not modified" to "modified". Only the first
77 * action of a group can be marked as modified.
78 * There can be a single action marked as "modified" in the actions list.
79 */
80 guint modified : 1;
81 };
82
83 /* INVALID is a pointer to an invalid action */
84 #define INVALID ((void *) "IA")
85
86 struct _GtkSourceUndoManagerPrivate {
87 GtkTextBuffer *document;
88
89 GList* actions;
90 gint next_redo;
91
92 gint actions_in_current_group;
93
94 gint running_not_undoable_actions;
95
96 gint num_of_groups;
97
98 gint max_undo_levels;
99
100 guint can_undo : 1;
101 guint can_redo : 1;
102
103 /* It is TRUE whether, while undoing an action of the current group (with orde r_in_group > 1),
104 * the state of the buffer changed from "not modified" to "modified".
105 */
106 guint modified_undoing_group : 1;
107
108 /* Pointer to the action (in the action list) marked as "modified".
109 * It is NULL when no action is marked as "modified".
110 * It is INVALID when the action marked as "modified" has been removed
111 * from the action list (freeing the list or resizing it) */
112 GtkSourceUndoAction *modified_action;
113 };
114
115 enum {
116 CAN_UNDO,
117 CAN_REDO,
118 LAST_SIGNAL
119 };
120
121 static void gtk_source_undo_manager_class_init(GtkSourceUndoManagerClass *klass) ;
122 static void gtk_source_undo_manager_init(GtkSourceUndoManager *um);
123 static void gtk_source_undo_manager_finalize(GObject *object);
124
125 static void gtk_source_undo_manager_insert_text_handler(GtkTextBuffer *buffer,
126 GtkTextIter *pos,
127 const gchar *text,
128 gint length,
129 GtkSourceUndoManager *um);
130 static void gtk_source_undo_manager_delete_range_handler(GtkTextBuffer *buffer,
131 GtkTextIter *start,
132 GtkTextIter *end,
133 GtkSourceUndoManager *um);
134 static void gtk_source_undo_manager_begin_user_action_handler(GtkTextBuffer *buf fer,
135 GtkSourceUndoManager *um);
136 static void gtk_source_undo_manager_modified_changed_handler(GtkTextBuffer *buff er,
137 GtkSourceUndoManager *um);
138
139 static void gtk_source_undo_manager_free_action_list(GtkSourceUndoManager *um);
140
141 static void gtk_source_undo_manager_add_action(GtkSourceUndoManager *um,
142 const GtkSourceUndoAction *undo_ action);
143 static void gtk_source_undo_manager_free_first_n_actions(GtkSourceUndoManager *u m,
144 gint n);
145 static void gtk_source_undo_manager_check_list_size(GtkSourceUndoManager *um);
146
147 static gboolean gtk_source_undo_manager_merge_action(GtkSourceUndoManager *um,
148 const GtkSourceUndoAction *undo_a ction);
149
150 static GObjectClass *parent_class = NULL;
151 static guint undo_manager_signals [LAST_SIGNAL] = { 0 };
152
153 GType
154 gtk_source_undo_manager_get_type(void) {
155 static GType undo_manager_type = 0;
156
157 if(undo_manager_type == 0)
158 {
159 static const GTypeInfo our_info =
160 {
161 sizeof(GtkSourceUndoManagerClass),
162 NULL, /* base_init */
163 NULL, /* base_finalize */
164 (GClassInitFunc) gtk_source_undo_manager_class_init,
165 NULL, /* class_finalize */
166 NULL, /* class_data */
167 sizeof(GtkSourceUndoManager),
168 0, /* n_preallocs */
169 (GInstanceInitFunc) gtk_source_undo_manager_init,
170 NULL /* value_table */
171 };
172
173 undo_manager_type = g_type_register_static(G_TYPE_OBJECT,
174 "GtkSourceUndoManager",
175 &our_info,
176 0);
177 }
178
179 return undo_manager_type;
180 }
181
182 static void
183 gtk_source_undo_manager_class_init(GtkSourceUndoManagerClass *klass) {
184 GObjectClass *object_class = G_OBJECT_CLASS(klass);
185
186 parent_class = g_type_class_peek_parent(klass);
187
188 object_class->finalize = gtk_source_undo_manager_finalize;
189
190 klass->can_undo = NULL;
191 klass->can_redo = NULL;
192
193 undo_manager_signals[CAN_UNDO] =
194 g_signal_new("can_undo",
195 G_OBJECT_CLASS_TYPE(object_class),
196 G_SIGNAL_RUN_LAST,
197 G_STRUCT_OFFSET(GtkSourceUndoManagerClass, can_undo),
198 NULL, NULL,
199 g_cclosure_marshal_VOID__BOOLEAN,
200 G_TYPE_NONE,
201 1,
202 G_TYPE_BOOLEAN);
203
204 undo_manager_signals[CAN_REDO] =
205 g_signal_new("can_redo",
206 G_OBJECT_CLASS_TYPE(object_class),
207 G_SIGNAL_RUN_LAST,
208 G_STRUCT_OFFSET(GtkSourceUndoManagerClass, can_redo),
209 NULL, NULL,
210 g_cclosure_marshal_VOID__BOOLEAN,
211 G_TYPE_NONE,
212 1,
213 G_TYPE_BOOLEAN);
214 }
215
216 static void
217 gtk_source_undo_manager_init(GtkSourceUndoManager *um) {
218 um->priv = g_new0(GtkSourceUndoManagerPrivate, 1);
219
220 um->priv->actions = NULL;
221 um->priv->next_redo = 0;
222
223 um->priv->can_undo = FALSE;
224 um->priv->can_redo = FALSE;
225
226 um->priv->running_not_undoable_actions = 0;
227
228 um->priv->num_of_groups = 0;
229
230 um->priv->max_undo_levels = DEFAULT_MAX_UNDO_LEVELS;
231
232 um->priv->modified_action = NULL;
233
234 um->priv->modified_undoing_group = FALSE;
235 }
236
237 static void
238 gtk_source_undo_manager_finalize(GObject *object) {
239 GtkSourceUndoManager *um;
240
241 g_return_if_fail(object != NULL);
242 g_return_if_fail(GTK_SOURCE_IS_UNDO_MANAGER(object));
243
244 um = GTK_SOURCE_UNDO_MANAGER(object);
245
246 g_return_if_fail(um->priv != NULL);
247
248 if(um->priv->actions != NULL)
249 gtk_source_undo_manager_free_action_list(um);
250
251 g_signal_handlers_disconnect_by_func(G_OBJECT(um->priv->document),
252 G_CALLBACK(gtk_source_undo_manager_delete_range_handler),
253 um);
254
255 g_signal_handlers_disconnect_by_func(G_OBJECT(um->priv->document),
256 G_CALLBACK(gtk_source_undo_manager_insert_text_handler),
257 um);
258
259 g_signal_handlers_disconnect_by_func(G_OBJECT(um->priv->document),
260 G_CALLBACK(gtk_source_undo_manager_begin_user_action_handler),
261 um);
262
263 g_signal_handlers_disconnect_by_func(G_OBJECT(um->priv->document),
264 G_CALLBACK(gtk_source_undo_manager_modified_changed_handler),
265 um);
266
267 g_free(um->priv);
268
269 G_OBJECT_CLASS(parent_class)->finalize(object);
270 }
271
272 GtkSourceUndoManager*
273 gtk_source_undo_manager_new(GtkTextBuffer* buffer) {
274 GtkSourceUndoManager *um;
275
276 um = GTK_SOURCE_UNDO_MANAGER(g_object_new(GTK_SOURCE_TYPE_UNDO_MANAGER, NULL)) ;
277
278 g_return_val_if_fail(um->priv != NULL, NULL);
279 um->priv->document = buffer;
280
281 g_signal_connect(G_OBJECT(buffer), "insert_text",
282 G_CALLBACK(gtk_source_undo_manager_insert_text_handler),
283 um);
284
285 g_signal_connect(G_OBJECT(buffer), "delete_range",
286 G_CALLBACK(gtk_source_undo_manager_delete_range_handler),
287 um);
288
289 g_signal_connect(G_OBJECT(buffer), "begin_user_action",
290 G_CALLBACK(gtk_source_undo_manager_begin_user_action_handler),
291 um);
292
293 g_signal_connect(G_OBJECT(buffer), "modified_changed",
294 G_CALLBACK(gtk_source_undo_manager_modified_changed_handler),
295 um);
296 return um;
297 }
298
299 void
300 gtk_source_undo_manager_begin_not_undoable_action(GtkSourceUndoManager *um) {
301 g_return_if_fail(GTK_SOURCE_IS_UNDO_MANAGER(um));
302 g_return_if_fail(um->priv != NULL);
303
304 ++um->priv->running_not_undoable_actions;
305 }
306
307 static void
308 gtk_source_undo_manager_end_not_undoable_action_internal(GtkSourceUndoManager *u m) {
309 g_return_if_fail(GTK_SOURCE_IS_UNDO_MANAGER(um));
310 g_return_if_fail(um->priv != NULL);
311
312 g_return_if_fail(um->priv->running_not_undoable_actions > 0);
313
314 --um->priv->running_not_undoable_actions;
315 }
316
317 void
318 gtk_source_undo_manager_end_not_undoable_action(GtkSourceUndoManager *um) {
319 g_return_if_fail(GTK_SOURCE_IS_UNDO_MANAGER(um));
320 g_return_if_fail(um->priv != NULL);
321
322 gtk_source_undo_manager_end_not_undoable_action_internal(um);
323
324 if(um->priv->running_not_undoable_actions == 0)
325 {
326 gtk_source_undo_manager_free_action_list(um);
327
328 um->priv->next_redo = -1;
329
330 if(um->priv->can_undo)
331 {
332 um->priv->can_undo = FALSE;
333 g_signal_emit(G_OBJECT(um),
334 undo_manager_signals [CAN_UNDO],
335 0,
336 FALSE);
337 }
338
339 if(um->priv->can_redo)
340 {
341 um->priv->can_redo = FALSE;
342 g_signal_emit(G_OBJECT(um),
343 undo_manager_signals [CAN_REDO],
344 0,
345 FALSE);
346 }
347 }
348 }
349
350 gboolean
351 gtk_source_undo_manager_can_undo(const GtkSourceUndoManager *um) {
352 g_return_val_if_fail(GTK_SOURCE_IS_UNDO_MANAGER(um), FALSE);
353 g_return_val_if_fail(um->priv != NULL, FALSE);
354
355 return um->priv->can_undo;
356 }
357
358 gboolean
359 gtk_source_undo_manager_can_redo(const GtkSourceUndoManager *um) {
360 g_return_val_if_fail(GTK_SOURCE_IS_UNDO_MANAGER(um), FALSE);
361 g_return_val_if_fail(um->priv != NULL, FALSE);
362
363 return um->priv->can_redo;
364 }
365
366 static void
367 set_cursor(GtkTextBuffer *buffer, gint cursor) {
368 GtkTextIter iter;
369
370 /* Place the cursor at the requested position */
371 gtk_text_buffer_get_iter_at_offset(buffer, &iter, cursor);
372 gtk_text_buffer_place_cursor(buffer, &iter);
373 }
374
375 static void
376 insert_text(GtkTextBuffer *buffer, gint pos, const gchar *text, gint len) {
377 GtkTextIter iter;
378
379 gtk_text_buffer_get_iter_at_offset(buffer, &iter, pos);
380 gtk_text_buffer_insert(buffer, &iter, text, len);
381 }
382
383 static void
384 delete_text(GtkTextBuffer *buffer, gint start, gint end) {
385 GtkTextIter start_iter;
386 GtkTextIter end_iter;
387
388 gtk_text_buffer_get_iter_at_offset(buffer, &start_iter, start);
389
390 if(end < 0)
391 gtk_text_buffer_get_end_iter(buffer, &end_iter);
392 else
393 gtk_text_buffer_get_iter_at_offset(buffer, &end_iter, end);
394
395 gtk_text_buffer_delete(buffer, &start_iter, &end_iter);
396 }
397
398 static gchar*
399 get_chars(GtkTextBuffer *buffer, gint start, gint end) {
400 GtkTextIter start_iter;
401 GtkTextIter end_iter;
402
403 gtk_text_buffer_get_iter_at_offset(buffer, &start_iter, start);
404
405 if(end < 0)
406 gtk_text_buffer_get_end_iter(buffer, &end_iter);
407 else
408 gtk_text_buffer_get_iter_at_offset(buffer, &end_iter, end);
409
410 return gtk_text_buffer_get_slice(buffer, &start_iter, &end_iter, TRUE);
411 }
412
413 void
414 gtk_source_undo_manager_undo(GtkSourceUndoManager *um) {
415 GtkSourceUndoAction *undo_action;
416 gboolean modified = FALSE;
417
418 g_return_if_fail(GTK_SOURCE_IS_UNDO_MANAGER(um));
419 g_return_if_fail(um->priv != NULL);
420 g_return_if_fail(um->priv->can_undo);
421
422 um->priv->modified_undoing_group = FALSE;
423
424 gtk_source_undo_manager_begin_not_undoable_action(um);
425
426 do
427 {
428 undo_action = g_list_nth_data(um->priv->actions, um->priv->next_redo + 1);
429 g_return_if_fail(undo_action != NULL);
430
431 /* undo_action->modified can be TRUE only if undo_action->order_in_group <= 1 */
432 g_return_if_fail((undo_action->order_in_group <= 1) ||
433 ((undo_action->order_in_group > 1) && !undo_action->modified));
434
435 if(undo_action->order_in_group <= 1)
436 {
437 /* Set modified to TRUE only if the buffer did not change its state from
438 * "not modified" to "modified" undoing an action(with order_in_group > 1)
439 * in current group. */
440 modified =(undo_action->modified && !um->priv->modified_undoing_group);
441 }
442
443 switch(undo_action->action_type)
444 {
445 case GTK_SOURCE_UNDO_ACTION_DELETE:
446 insert_text(
447 um->priv->document,
448 undo_action->action.delete.start,
449 undo_action->action.delete.text,
450 strlen(undo_action->action.delete.text));
451
452 if(undo_action->action.delete.forward)
453 set_cursor(
454 um->priv->document,
455 undo_action->action.delete.start);
456 else
457 set_cursor(
458 um->priv->document,
459 undo_action->action.delete.end);
460
461 break;
462
463 case GTK_SOURCE_UNDO_ACTION_INSERT:
464 delete_text(
465 um->priv->document,
466 undo_action->action.insert.pos,
467 undo_action->action.insert.pos +
468 undo_action->action.insert.chars);
469
470 set_cursor(
471 um->priv->document,
472 undo_action->action.insert.pos);
473 break;
474
475 default:
476 /* Unknown action type. */
477 g_return_if_reached();
478 }
479
480 ++um->priv->next_redo;
481
482 } while(undo_action->order_in_group > 1);
483
484 if(modified)
485 {
486 --um->priv->next_redo;
487 gtk_text_buffer_set_modified(um->priv->document, FALSE);
488 ++um->priv->next_redo;
489 }
490
491 gtk_source_undo_manager_end_not_undoable_action_internal(um);
492
493 um->priv->modified_undoing_group = FALSE;
494
495 if(!um->priv->can_redo)
496 {
497 um->priv->can_redo = TRUE;
498 g_signal_emit(G_OBJECT(um),
499 undo_manager_signals [CAN_REDO],
500 0,
501 TRUE);
502 }
503
504 if(um->priv->next_redo >=(gint)(g_list_length(um->priv->actions) - 1))
505 {
506 um->priv->can_undo = FALSE;
507 g_signal_emit(G_OBJECT(um),
508 undo_manager_signals [CAN_UNDO],
509 0,
510 FALSE);
511 }
512 }
513
514 void
515 gtk_source_undo_manager_redo(GtkSourceUndoManager *um) {
516 GtkSourceUndoAction *undo_action;
517 gboolean modified = FALSE;
518
519 g_return_if_fail(GTK_SOURCE_IS_UNDO_MANAGER(um));
520 g_return_if_fail(um->priv != NULL);
521 g_return_if_fail(um->priv->can_redo);
522
523 undo_action = g_list_nth_data(um->priv->actions, um->priv->next_redo);
524 g_return_if_fail(undo_action != NULL);
525
526 gtk_source_undo_manager_begin_not_undoable_action(um);
527
528 do
529 {
530 if(undo_action->modified)
531 {
532 g_return_if_fail(undo_action->order_in_group <= 1);
533 modified = TRUE;
534 }
535
536 --um->priv->next_redo;
537
538 switch(undo_action->action_type)
539 {
540 case GTK_SOURCE_UNDO_ACTION_DELETE:
541 delete_text(
542 um->priv->document,
543 undo_action->action.delete.start,
544 undo_action->action.delete.end);
545
546 set_cursor(
547 um->priv->document,
548 undo_action->action.delete.start);
549
550 break;
551
552 case GTK_SOURCE_UNDO_ACTION_INSERT:
553 set_cursor(
554 um->priv->document,
555 undo_action->action.insert.pos);
556
557 insert_text(
558 um->priv->document,
559 undo_action->action.insert.pos,
560 undo_action->action.insert.text,
561 undo_action->action.insert.length);
562
563 break;
564
565 default:
566 /* Unknown action type */
567 ++um->priv->next_redo;
568 g_return_if_reached();
569 }
570
571 if(um->priv->next_redo < 0)
572 undo_action = NULL;
573 else
574 undo_action = g_list_nth_data(um->priv->actions, um->priv->next_redo);
575
576 } while((undo_action != NULL) &&(undo_action->order_in_group > 1));
577
578 if(modified)
579 {
580 ++um->priv->next_redo;
581 gtk_text_buffer_set_modified(um->priv->document, FALSE);
582 --um->priv->next_redo;
583 }
584
585 gtk_source_undo_manager_end_not_undoable_action_internal(um);
586
587 if(um->priv->next_redo < 0)
588 {
589 um->priv->can_redo = FALSE;
590 g_signal_emit(G_OBJECT(um), undo_manager_signals [CAN_REDO], 0, FALSE);
591 }
592
593 if(!um->priv->can_undo)
594 {
595 um->priv->can_undo = TRUE;
596 g_signal_emit(G_OBJECT(um), undo_manager_signals [CAN_UNDO], 0, TRUE);
597 }
598 }
599
600 static void
601 gtk_source_undo_action_free(GtkSourceUndoAction *action) {
602 if(action == NULL)
603 return;
604
605 if(action->action_type == GTK_SOURCE_UNDO_ACTION_INSERT)
606 g_free(action->action.insert.text);
607 else if(action->action_type == GTK_SOURCE_UNDO_ACTION_DELETE)
608 g_free(action->action.delete.text);
609 else
610 g_return_if_reached();
611
612 g_free(action);
613 }
614
615 static void
616 gtk_source_undo_manager_free_action_list(GtkSourceUndoManager *um) {
617 GList *l;
618
619 l = um->priv->actions;
620
621 while(l != NULL)
622 {
623 GtkSourceUndoAction *action = l->data;
624
625 if(action->order_in_group == 1)
626 --um->priv->num_of_groups;
627
628 if(action->modified)
629 um->priv->modified_action = INVALID;
630
631 gtk_source_undo_action_free(action);
632
633 l = g_list_next(l);
634 }
635
636 g_list_free(um->priv->actions);
637 um->priv->actions = NULL;
638 }
639
640 static void
641 gtk_source_undo_manager_insert_text_handler(GtkTextBuffer *buffer,
642 GtkTextIter *pos,
643 const gchar *text,
644 gint length,
645 GtkSourceUndoManager *um) {
646 GtkSourceUndoAction undo_action;
647
648 if(um->priv->running_not_undoable_actions > 0)
649 return;
650
651 undo_action.action_type = GTK_SOURCE_UNDO_ACTION_INSERT;
652
653 undo_action.action.insert.pos = gtk_text_iter_get_offset(pos);
654 undo_action.action.insert.text =(gchar*) text;
655 undo_action.action.insert.length = length;
656 undo_action.action.insert.chars = g_utf8_strlen(text, length);
657
658 if((undo_action.action.insert.chars > 1) ||(g_utf8_get_char(text) == '\n'))
659
660 undo_action.mergeable = FALSE;
661 else
662 undo_action.mergeable = TRUE;
663
664 undo_action.modified = FALSE;
665
666 gtk_source_undo_manager_add_action(um, &undo_action);
667 }
668
669 static void
670 gtk_source_undo_manager_delete_range_handler(GtkTextBuffer *buffer,
671 GtkTextIter *start,
672 GtkTextIter *end,
673 GtkSourceUndoManager *um) {
674 GtkSourceUndoAction undo_action;
675 GtkTextIter insert_iter;
676
677 if(um->priv->running_not_undoable_actions > 0)
678 return;
679
680 undo_action.action_type = GTK_SOURCE_UNDO_ACTION_DELETE;
681
682 gtk_text_iter_order(start, end);
683
684 undo_action.action.delete.start = gtk_text_iter_get_offset(start);
685 undo_action.action.delete.end = gtk_text_iter_get_offset(end);
686
687 undo_action.action.delete.text = get_chars(
688 buffer,
689 undo_action.action.delete.start,
690 undo_action.action.delete.end);
691
692 /* figure out if the user used the Delete or the Backspace key */
693 gtk_text_buffer_get_iter_at_mark(buffer, &insert_iter,
694 gtk_text_buffer_get_insert(buffer));
695 if(gtk_text_iter_get_offset(&insert_iter) <= undo_action.action.delete.start)
696 undo_action.action.delete.forward = TRUE;
697 else
698 undo_action.action.delete.forward = FALSE;
699
700 if(((undo_action.action.delete.end - undo_action.action.delete.start) > 1) ||
701 (g_utf8_get_char(undo_action.action.delete.text ) == '\n'))
702 undo_action.mergeable = FALSE;
703 else
704 undo_action.mergeable = TRUE;
705
706 undo_action.modified = FALSE;
707
708 gtk_source_undo_manager_add_action(um, &undo_action);
709
710 g_free(undo_action.action.delete.text);
711
712 }
713
714 static void
715 gtk_source_undo_manager_begin_user_action_handler(GtkTextBuffer *buffer, GtkSour ceUndoManager *um) {
716 g_return_if_fail(GTK_SOURCE_IS_UNDO_MANAGER(um));
717 g_return_if_fail(um->priv != NULL);
718
719 if(um->priv->running_not_undoable_actions > 0)
720 return;
721
722 um->priv->actions_in_current_group = 0;
723 }
724
725 static void
726 gtk_source_undo_manager_add_action(GtkSourceUndoManager *um,
727 const GtkSourceUndoAction *undo_action) {
728 GtkSourceUndoAction* action;
729
730 if(um->priv->next_redo >= 0)
731 {
732 gtk_source_undo_manager_free_first_n_actions(um, um->priv->next_redo + 1);
733 }
734
735 um->priv->next_redo = -1;
736
737 if(!gtk_source_undo_manager_merge_action(um, undo_action))
738 {
739 action = g_new(GtkSourceUndoAction, 1);
740 *action = *undo_action;
741
742 if(action->action_type == GTK_SOURCE_UNDO_ACTION_INSERT)
743 action->action.insert.text = g_strndup(undo_action->action.insert.text, un do_action->action.insert.length);
744 else if(action->action_type == GTK_SOURCE_UNDO_ACTION_DELETE)
745 action->action.delete.text = g_strdup(undo_action->action.delete.text);
746 else
747 {
748 g_free(action);
749 g_return_if_reached();
750 }
751
752 ++um->priv->actions_in_current_group;
753 action->order_in_group = um->priv->actions_in_current_group;
754
755 if(action->order_in_group == 1)
756 ++um->priv->num_of_groups;
757
758 um->priv->actions = g_list_prepend(um->priv->actions, action);
759 }
760
761 gtk_source_undo_manager_check_list_size(um);
762
763 if(!um->priv->can_undo)
764 {
765 um->priv->can_undo = TRUE;
766 g_signal_emit(G_OBJECT(um), undo_manager_signals [CAN_UNDO], 0, TRUE);
767 }
768
769 if(um->priv->can_redo)
770 {
771 um->priv->can_redo = FALSE;
772 g_signal_emit(G_OBJECT(um), undo_manager_signals [CAN_REDO], 0, FALSE);
773 }
774 }
775
776 static void
777 gtk_source_undo_manager_free_first_n_actions(GtkSourceUndoManager *um,
778 gint n) {
779 gint i;
780
781 if(um->priv->actions == NULL)
782 return;
783
784 for(i = 0; i < n; i++)
785 {
786 GtkSourceUndoAction *action = g_list_first(um->priv->actions)->data;
787
788 if(action->order_in_group == 1)
789 --um->priv->num_of_groups;
790
791 if(action->modified)
792 um->priv->modified_action = INVALID;
793
794 gtk_source_undo_action_free(action);
795
796 um->priv->actions = g_list_delete_link(um->priv->actions,
797 um->priv->actions);
798
799 if(um->priv->actions == NULL)
800 return;
801 }
802 }
803
804 static void
805 gtk_source_undo_manager_check_list_size(GtkSourceUndoManager *um) {
806 gint undo_levels;
807
808 g_return_if_fail(GTK_SOURCE_IS_UNDO_MANAGER(um));
809 g_return_if_fail(um->priv != NULL);
810
811 undo_levels = gtk_source_undo_manager_get_max_undo_levels(um);
812
813 if(undo_levels < 1)
814 return;
815
816 if(um->priv->num_of_groups > undo_levels)
817 {
818 GtkSourceUndoAction *undo_action;
819 GList *last;
820
821 last = g_list_last(um->priv->actions);
822 undo_action =(GtkSourceUndoAction*) last->data;
823
824 do
825 {
826 GList *tmp;
827
828 if(undo_action->order_in_group == 1)
829 --um->priv->num_of_groups;
830
831 if(undo_action->modified)
832 um->priv->modified_action = INVALID;
833
834 gtk_source_undo_action_free(undo_action);
835
836 tmp = g_list_previous(last);
837 um->priv->actions = g_list_delete_link(um->priv->actions, last);
838 last = tmp;
839 g_return_if_fail(last != NULL);
840
841 undo_action =(GtkSourceUndoAction*) last->data;
842
843 } while((undo_action->order_in_group > 1) ||
844 (um->priv->num_of_groups > undo_levels));
845 }
846 }
847
848 /**
849 * gtk_source_undo_manager_merge_action:
850 * @um: a #GtkSourceUndoManager.
851 * @undo_action: a #GtkSourceUndoAction.
852 *
853 * This function tries to merge the undo action at the top of
854 * the stack with a new undo action. So when we undo for example
855 * typing, we can undo the whole word and not each letter by itself.
856 *
857 * Return Value: %TRUE is merge was successful, %FALSE otherwise.
858 **/
859 static gboolean
860 gtk_source_undo_manager_merge_action(GtkSourceUndoManager *um,
861 const GtkSourceUndoAction *undo_action) {
862 GtkSourceUndoAction *last_action;
863
864 g_return_val_if_fail(GTK_SOURCE_IS_UNDO_MANAGER(um), FALSE);
865 g_return_val_if_fail(um->priv != NULL, FALSE);
866
867 if(um->priv->actions == NULL)
868 return FALSE;
869
870 last_action =(GtkSourceUndoAction*) g_list_nth_data(um->priv->actions, 0);
871
872 if(!last_action->mergeable)
873 return FALSE;
874
875 if((!undo_action->mergeable) ||
876 (undo_action->action_type != last_action->action_type))
877 {
878 last_action->mergeable = FALSE;
879 return FALSE;
880 }
881
882 if(undo_action->action_type == GTK_SOURCE_UNDO_ACTION_DELETE)
883 {
884 if((last_action->action.delete.forward != undo_action->action.delete.forward ) ||
885 ((last_action->action.delete.start != undo_action->action.delete.start) & &
886 (last_action->action.delete.start != undo_action->action.delete.end)))
887 {
888 last_action->mergeable = FALSE;
889 return FALSE;
890 }
891
892 if(last_action->action.delete.start == undo_action->action.delete.start)
893 {
894 gchar *str;
895
896 #define L (last_action->action.delete.end - last_action->action.delete.start - 1 )
897 #define g_utf8_get_char_at(p,i) g_utf8_get_char(g_utf8_offset_to_pointer((p),(i) ))
898
899 /* Deleted with the delete key */
900 if((g_utf8_get_char(undo_action->action.delete.text) != ' ') &&
901 (g_utf8_get_char(undo_action->action.delete.text) != '\t') &&
902 ((g_utf8_get_char_at(last_action->action.delete.text, L) == ' ') ||
903 (g_utf8_get_char_at(last_action->action.delete.text, L) == '\t')))
904 {
905 last_action->mergeable = FALSE;
906 return FALSE;
907 }
908
909 str = g_strdup_printf("%s%s", last_action->action.delete.text,
910 undo_action->action.delete.text);
911
912 g_free(last_action->action.delete.text);
913 last_action->action.delete.end +=(undo_action->action.delete.end -
914 undo_action->action.delete.start);
915 last_action->action.delete.text = str;
916 }
917 else
918 {
919 gchar *str;
920
921 /* Deleted with the backspace key */
922 if((g_utf8_get_char(undo_action->action.delete.text) != ' ') &&
923 (g_utf8_get_char(undo_action->action.delete.text) != '\t') &&
924 ((g_utf8_get_char(last_action->action.delete.text) == ' ') ||
925 (g_utf8_get_char(last_action->action.delete.text) == '\t')))
926 {
927 last_action->mergeable = FALSE;
928 return FALSE;
929 }
930
931 str = g_strdup_printf("%s%s", undo_action->action.delete.text,
932 last_action->action.delete.text);
933
934 g_free(last_action->action.delete.text);
935 last_action->action.delete.start = undo_action->action.delete.start;
936 last_action->action.delete.text = str;
937 }
938 }
939 else if(undo_action->action_type == GTK_SOURCE_UNDO_ACTION_INSERT)
940 {
941 gchar* str;
942
943 #define I (last_action->action.insert.chars - 1)
944
945 if((undo_action->action.insert.pos !=
946 (last_action->action.insert.pos + last_action->action.insert.chars)) | |
947 ((g_utf8_get_char(undo_action->action.insert.text) != ' ') &&
948 (g_utf8_get_char(undo_action->action.insert.text) != '\t') &&
949 ((g_utf8_get_char_at(last_action->action.insert.text, I) == ' ') ||
950 (g_utf8_get_char_at(last_action->action.insert.text, I) == '\t')))
951 )
952 {
953 last_action->mergeable = FALSE;
954 return FALSE;
955 }
956
957 str = g_strdup_printf("%s%s", last_action->action.insert.text,
958 undo_action->action.insert.text);
959
960 g_free(last_action->action.insert.text);
961 last_action->action.insert.length += undo_action->action.insert.length;
962 last_action->action.insert.text = str;
963 last_action->action.insert.chars += undo_action->action.insert.chars;
964
965 }
966 else
967 /* Unknown action inside undo merge encountered */
968 g_return_val_if_reached(TRUE);
969
970 return TRUE;
971 }
972
973 gint
974 gtk_source_undo_manager_get_max_undo_levels(GtkSourceUndoManager *um) {
975 g_return_val_if_fail(um != NULL, 0);
976 g_return_val_if_fail(GTK_SOURCE_IS_UNDO_MANAGER(um), 0);
977
978 return um->priv->max_undo_levels;
979 }
980
981 void
982 gtk_source_undo_manager_set_max_undo_levels(GtkSourceUndoManager *um,
983 gint max_undo_levels) {
984 gint old_levels;
985
986 g_return_if_fail(um != NULL);
987 g_return_if_fail(GTK_SOURCE_IS_UNDO_MANAGER(um));
988
989 old_levels = um->priv->max_undo_levels;
990 um->priv->max_undo_levels = max_undo_levels;
991
992 if(max_undo_levels < 1)
993 return;
994
995 if(old_levels > max_undo_levels)
996 {
997 /* strip redo actions first */
998 while(um->priv->next_redo >= 0 &&(um->priv->num_of_groups > max_undo_levels) )
999 {
1000 gtk_source_undo_manager_free_first_n_actions(um, 1);
1001 um->priv->next_redo--;
1002 }
1003
1004 /* now remove undo actions if necessary */
1005 gtk_source_undo_manager_check_list_size(um);
1006
1007 /* emit "can_undo" and/or "can_redo" if appropiate */
1008 if(um->priv->next_redo < 0 && um->priv->can_redo)
1009 {
1010 um->priv->can_redo = FALSE;
1011 g_signal_emit(G_OBJECT(um), undo_manager_signals [CAN_REDO], 0, FALSE);
1012 }
1013
1014 if(um->priv->can_undo &&
1015 um->priv->next_redo >=(gint)(g_list_length(um->priv->actions) - 1))
1016 {
1017 um->priv->can_undo = FALSE;
1018 g_signal_emit(G_OBJECT(um), undo_manager_signals [CAN_UNDO], 0, FALSE);
1019 }
1020 }
1021 }
1022
1023 static void
1024 gtk_source_undo_manager_modified_changed_handler(GtkTextBuffer *buffer,
1025 GtkSourceUndoManager *um) {
1026 GtkSourceUndoAction *action;
1027 GList *list;
1028
1029 g_return_if_fail(GTK_SOURCE_IS_UNDO_MANAGER(um));
1030 g_return_if_fail(um->priv != NULL);
1031
1032 if(um->priv->actions == NULL)
1033 return;
1034
1035 list = g_list_nth(um->priv->actions, um->priv->next_redo + 1);
1036
1037 if(list != NULL)
1038 action =(GtkSourceUndoAction*) list->data;
1039 else
1040 action = NULL;
1041
1042 if(gtk_text_buffer_get_modified(buffer) == FALSE)
1043 {
1044 if(action != NULL)
1045 action->mergeable = FALSE;
1046
1047 if(um->priv->modified_action != NULL)
1048 {
1049 if(um->priv->modified_action != INVALID)
1050 um->priv->modified_action->modified = FALSE;
1051
1052 um->priv->modified_action = NULL;
1053 }
1054
1055 return;
1056 }
1057
1058 if(action == NULL)
1059 {
1060 g_return_if_fail(um->priv->running_not_undoable_actions > 0);
1061
1062 return;
1063 }
1064
1065 /* gtk_text_buffer_get_modified(buffer) == TRUE */
1066
1067 g_return_if_fail(um->priv->modified_action == NULL);
1068
1069 if(action->order_in_group > 1)
1070 um->priv->modified_undoing_group = TRUE;
1071
1072 while(action->order_in_group > 1)
1073 {
1074 list = g_list_next(list);
1075 g_return_if_fail(list != NULL);
1076
1077 action =(GtkSourceUndoAction*) list->data;
1078 g_return_if_fail(action != NULL);
1079 }
1080
1081 action->modified = TRUE;
1082 um->priv->modified_action = action;
1083 }
1084
OLDNEW
« no previous file with comments | « third_party/undoview/undo_manager.h ('k') | third_party/undoview/undo_view.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698