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

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

Issue 9289019: Move undoview to deps/third_party/ (Closed) Base URL: http://src.chromium.org/svn/trunk/deps/
Patch Set: '' Created 8 years, 10 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 | Annotate | Revision Log
« 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')
Property Changes:
Added: svn:eol-style
+ LF
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 #if !defined(NDEBUG)
122 static void
123 print_state(GtkSourceUndoManager* um)
124 {
125 fprintf(stderr, "\n***\n");
126 GList* actions = um->priv->actions;
127
128 for (; actions; actions = g_list_next(actions)) {
129 GtkSourceUndoAction* act = actions->data;
130 fprintf(stderr, "* type = %s\n", act->action_type == GTK_SOURCE_UNDO_ACTION_ DELETE ?
131 "delete" : "insert");
132
133 fprintf(stderr, "\ttext = %s\n", act->action_type == GTK_SOURCE_UNDO_ACTION_ DELETE
134 ? act->action.delete.text : act->action.insert.text);
135 fprintf(stderr, "\torder = %d\n", act->order_in_group);
136 }
137
138 fprintf(stderr, "* next redo: %d\n", um->priv->next_redo);
139 fprintf(stderr, "* num of groups: %d\n", um->priv->num_of_groups);
140 fprintf(stderr, "* actions in group: %d\n", um->priv->actions_in_current_group );
141 }
142 #endif
143
144 static void gtk_source_undo_manager_class_init(GtkSourceUndoManagerClass *klass) ;
145 static void gtk_source_undo_manager_init(GtkSourceUndoManager *um);
146 static void gtk_source_undo_manager_finalize(GObject *object);
147
148 static void gtk_source_undo_manager_insert_text_handler(GtkTextBuffer *buffer,
149 GtkTextIter *pos,
150 const gchar *text,
151 gint length,
152 GtkSourceUndoManager *um);
153 static void gtk_source_undo_manager_delete_range_handler(GtkTextBuffer *buffer,
154 GtkTextIter *start,
155 GtkTextIter *end,
156 GtkSourceUndoManager *um);
157 static void gtk_source_undo_manager_begin_user_action_handler(GtkTextBuffer *buf fer,
158 GtkSourceUndoManager *um);
159 static void gtk_source_undo_manager_modified_changed_handler(GtkTextBuffer *buff er,
160 GtkSourceUndoManager *um);
161
162 static void gtk_source_undo_manager_free_action_list(GtkSourceUndoManager *um);
163
164 static void gtk_source_undo_manager_add_action(GtkSourceUndoManager *um,
165 const GtkSourceUndoAction *undo_ action);
166 static void gtk_source_undo_manager_free_first_n_actions(GtkSourceUndoManager *u m,
167 gint n);
168 static void gtk_source_undo_manager_check_list_size(GtkSourceUndoManager *um);
169
170 static gboolean gtk_source_undo_manager_merge_action(GtkSourceUndoManager *um,
171 const GtkSourceUndoAction *undo_a ction);
172
173 static GObjectClass *parent_class = NULL;
174 static guint undo_manager_signals [LAST_SIGNAL] = { 0 };
175
176 GType
177 gtk_source_undo_manager_get_type(void) {
178 static GType undo_manager_type = 0;
179
180 if(undo_manager_type == 0)
181 {
182 static const GTypeInfo our_info =
183 {
184 sizeof(GtkSourceUndoManagerClass),
185 NULL, /* base_init */
186 NULL, /* base_finalize */
187 (GClassInitFunc) gtk_source_undo_manager_class_init,
188 NULL, /* class_finalize */
189 NULL, /* class_data */
190 sizeof(GtkSourceUndoManager),
191 0, /* n_preallocs */
192 (GInstanceInitFunc) gtk_source_undo_manager_init,
193 NULL /* value_table */
194 };
195
196 undo_manager_type = g_type_register_static(G_TYPE_OBJECT,
197 "GtkSourceUndoManager",
198 &our_info,
199 0);
200 }
201
202 return undo_manager_type;
203 }
204
205 static void
206 gtk_source_undo_manager_class_init(GtkSourceUndoManagerClass *klass) {
207 GObjectClass *object_class = G_OBJECT_CLASS(klass);
208
209 parent_class = g_type_class_peek_parent(klass);
210
211 object_class->finalize = gtk_source_undo_manager_finalize;
212
213 klass->can_undo = NULL;
214 klass->can_redo = NULL;
215
216 undo_manager_signals[CAN_UNDO] =
217 g_signal_new("can_undo",
218 G_OBJECT_CLASS_TYPE(object_class),
219 G_SIGNAL_RUN_LAST,
220 G_STRUCT_OFFSET(GtkSourceUndoManagerClass, can_undo),
221 NULL, NULL,
222 g_cclosure_marshal_VOID__BOOLEAN,
223 G_TYPE_NONE,
224 1,
225 G_TYPE_BOOLEAN);
226
227 undo_manager_signals[CAN_REDO] =
228 g_signal_new("can_redo",
229 G_OBJECT_CLASS_TYPE(object_class),
230 G_SIGNAL_RUN_LAST,
231 G_STRUCT_OFFSET(GtkSourceUndoManagerClass, can_redo),
232 NULL, NULL,
233 g_cclosure_marshal_VOID__BOOLEAN,
234 G_TYPE_NONE,
235 1,
236 G_TYPE_BOOLEAN);
237 }
238
239 static void
240 gtk_source_undo_manager_init(GtkSourceUndoManager *um) {
241 um->priv = g_new0(GtkSourceUndoManagerPrivate, 1);
242
243 um->priv->actions = NULL;
244 um->priv->next_redo = 0;
245
246 um->priv->can_undo = FALSE;
247 um->priv->can_redo = FALSE;
248
249 um->priv->running_not_undoable_actions = 0;
250
251 um->priv->num_of_groups = 0;
252
253 um->priv->max_undo_levels = DEFAULT_MAX_UNDO_LEVELS;
254
255 um->priv->modified_action = NULL;
256
257 um->priv->modified_undoing_group = FALSE;
258 }
259
260 static void
261 gtk_source_undo_manager_finalize(GObject *object) {
262 GtkSourceUndoManager *um;
263
264 g_return_if_fail(object != NULL);
265 g_return_if_fail(GTK_SOURCE_IS_UNDO_MANAGER(object));
266
267 um = GTK_SOURCE_UNDO_MANAGER(object);
268
269 g_return_if_fail(um->priv != NULL);
270
271 if(um->priv->actions != NULL)
272 gtk_source_undo_manager_free_action_list(um);
273
274 g_signal_handlers_disconnect_by_func(G_OBJECT(um->priv->document),
275 G_CALLBACK(gtk_source_undo_manager_delete_range_handler),
276 um);
277
278 g_signal_handlers_disconnect_by_func(G_OBJECT(um->priv->document),
279 G_CALLBACK(gtk_source_undo_manager_insert_text_handler),
280 um);
281
282 g_signal_handlers_disconnect_by_func(G_OBJECT(um->priv->document),
283 G_CALLBACK(gtk_source_undo_manager_begin_user_action_handler),
284 um);
285
286 g_signal_handlers_disconnect_by_func(G_OBJECT(um->priv->document),
287 G_CALLBACK(gtk_source_undo_manager_modified_changed_handler),
288 um);
289
290 g_free(um->priv);
291
292 G_OBJECT_CLASS(parent_class)->finalize(object);
293 }
294
295 GtkSourceUndoManager*
296 gtk_source_undo_manager_new(GtkTextBuffer* buffer) {
297 GtkSourceUndoManager *um;
298
299 um = GTK_SOURCE_UNDO_MANAGER(g_object_new(GTK_SOURCE_TYPE_UNDO_MANAGER, NULL)) ;
300
301 g_return_val_if_fail(um->priv != NULL, NULL);
302 um->priv->document = buffer;
303
304 g_signal_connect(G_OBJECT(buffer), "insert_text",
305 G_CALLBACK(gtk_source_undo_manager_insert_text_handler),
306 um);
307
308 g_signal_connect(G_OBJECT(buffer), "delete_range",
309 G_CALLBACK(gtk_source_undo_manager_delete_range_handler),
310 um);
311
312 g_signal_connect(G_OBJECT(buffer), "begin_user_action",
313 G_CALLBACK(gtk_source_undo_manager_begin_user_action_handler),
314 um);
315
316 g_signal_connect(G_OBJECT(buffer), "modified_changed",
317 G_CALLBACK(gtk_source_undo_manager_modified_changed_handler),
318 um);
319 return um;
320 }
321
322 void
323 gtk_source_undo_manager_begin_not_undoable_action(GtkSourceUndoManager *um) {
324 g_return_if_fail(GTK_SOURCE_IS_UNDO_MANAGER(um));
325 g_return_if_fail(um->priv != NULL);
326
327 ++um->priv->running_not_undoable_actions;
328 }
329
330 static void
331 gtk_source_undo_manager_end_not_undoable_action_internal(GtkSourceUndoManager *u m) {
332 g_return_if_fail(GTK_SOURCE_IS_UNDO_MANAGER(um));
333 g_return_if_fail(um->priv != NULL);
334
335 g_return_if_fail(um->priv->running_not_undoable_actions > 0);
336
337 --um->priv->running_not_undoable_actions;
338 }
339
340 void
341 gtk_source_undo_manager_end_not_undoable_action(GtkSourceUndoManager *um) {
342 g_return_if_fail(GTK_SOURCE_IS_UNDO_MANAGER(um));
343 g_return_if_fail(um->priv != NULL);
344
345 gtk_source_undo_manager_end_not_undoable_action_internal(um);
346
347 if(um->priv->running_not_undoable_actions == 0)
348 {
349 gtk_source_undo_manager_free_action_list(um);
350
351 um->priv->next_redo = -1;
352
353 if(um->priv->can_undo)
354 {
355 um->priv->can_undo = FALSE;
356 g_signal_emit(G_OBJECT(um),
357 undo_manager_signals [CAN_UNDO],
358 0,
359 FALSE);
360 }
361
362 if(um->priv->can_redo)
363 {
364 um->priv->can_redo = FALSE;
365 g_signal_emit(G_OBJECT(um),
366 undo_manager_signals [CAN_REDO],
367 0,
368 FALSE);
369 }
370 }
371 }
372
373 gboolean
374 gtk_source_undo_manager_can_undo(const GtkSourceUndoManager *um) {
375 g_return_val_if_fail(GTK_SOURCE_IS_UNDO_MANAGER(um), FALSE);
376 g_return_val_if_fail(um->priv != NULL, FALSE);
377
378 return um->priv->can_undo;
379 }
380
381 gboolean
382 gtk_source_undo_manager_can_redo(const GtkSourceUndoManager *um) {
383 g_return_val_if_fail(GTK_SOURCE_IS_UNDO_MANAGER(um), FALSE);
384 g_return_val_if_fail(um->priv != NULL, FALSE);
385
386 return um->priv->can_redo;
387 }
388
389 static void
390 set_cursor(GtkTextBuffer *buffer, gint cursor) {
391 GtkTextIter iter;
392
393 /* Place the cursor at the requested position */
394 gtk_text_buffer_get_iter_at_offset(buffer, &iter, cursor);
395 gtk_text_buffer_place_cursor(buffer, &iter);
396 }
397
398 static void
399 insert_text(GtkTextBuffer *buffer, gint pos, const gchar *text, gint len) {
400 GtkTextIter iter;
401
402 gtk_text_buffer_get_iter_at_offset(buffer, &iter, pos);
403 gtk_text_buffer_insert(buffer, &iter, text, len);
404 }
405
406 static void
407 delete_text(GtkTextBuffer *buffer, gint start, gint end) {
408 GtkTextIter start_iter;
409 GtkTextIter end_iter;
410
411 gtk_text_buffer_get_iter_at_offset(buffer, &start_iter, start);
412
413 if(end < 0)
414 gtk_text_buffer_get_end_iter(buffer, &end_iter);
415 else
416 gtk_text_buffer_get_iter_at_offset(buffer, &end_iter, end);
417
418 gtk_text_buffer_delete(buffer, &start_iter, &end_iter);
419 }
420
421 static gchar*
422 get_chars(GtkTextBuffer *buffer, gint start, gint end) {
423 GtkTextIter start_iter;
424 GtkTextIter end_iter;
425
426 gtk_text_buffer_get_iter_at_offset(buffer, &start_iter, start);
427
428 if(end < 0)
429 gtk_text_buffer_get_end_iter(buffer, &end_iter);
430 else
431 gtk_text_buffer_get_iter_at_offset(buffer, &end_iter, end);
432
433 return gtk_text_buffer_get_slice(buffer, &start_iter, &end_iter, TRUE);
434 }
435
436 void
437 gtk_source_undo_manager_undo(GtkSourceUndoManager *um) {
438 GtkSourceUndoAction *undo_action;
439 gboolean modified = FALSE;
440
441 g_return_if_fail(GTK_SOURCE_IS_UNDO_MANAGER(um));
442 g_return_if_fail(um->priv != NULL);
443 g_return_if_fail(um->priv->can_undo);
444
445 um->priv->modified_undoing_group = FALSE;
446
447 gtk_source_undo_manager_begin_not_undoable_action(um);
448
449 do
450 {
451 undo_action = g_list_nth_data(um->priv->actions, um->priv->next_redo + 1);
452 g_return_if_fail(undo_action != NULL);
453
454 /* undo_action->modified can be TRUE only if undo_action->order_in_group <= 1 */
455 g_return_if_fail((undo_action->order_in_group <= 1) ||
456 ((undo_action->order_in_group > 1) && !undo_action->modified));
457
458 if(undo_action->order_in_group <= 1)
459 {
460 /* Set modified to TRUE only if the buffer did not change its state from
461 * "not modified" to "modified" undoing an action(with order_in_group > 1)
462 * in current group. */
463 modified =(undo_action->modified && !um->priv->modified_undoing_group);
464 }
465
466 switch(undo_action->action_type)
467 {
468 case GTK_SOURCE_UNDO_ACTION_DELETE:
469 insert_text(
470 um->priv->document,
471 undo_action->action.delete.start,
472 undo_action->action.delete.text,
473 strlen(undo_action->action.delete.text));
474
475 if(undo_action->action.delete.forward)
476 set_cursor(
477 um->priv->document,
478 undo_action->action.delete.start);
479 else
480 set_cursor(
481 um->priv->document,
482 undo_action->action.delete.end);
483
484 break;
485
486 case GTK_SOURCE_UNDO_ACTION_INSERT:
487 delete_text(
488 um->priv->document,
489 undo_action->action.insert.pos,
490 undo_action->action.insert.pos +
491 undo_action->action.insert.chars);
492
493 set_cursor(
494 um->priv->document,
495 undo_action->action.insert.pos);
496 break;
497
498 default:
499 /* Unknown action type. */
500 g_return_if_reached();
501 }
502
503 ++um->priv->next_redo;
504
505 } while(undo_action->order_in_group > 1);
506
507 if(modified)
508 {
509 --um->priv->next_redo;
510 gtk_text_buffer_set_modified(um->priv->document, FALSE);
511 ++um->priv->next_redo;
512 }
513
514 gtk_source_undo_manager_end_not_undoable_action_internal(um);
515
516 um->priv->modified_undoing_group = FALSE;
517
518 if(!um->priv->can_redo)
519 {
520 um->priv->can_redo = TRUE;
521 g_signal_emit(G_OBJECT(um),
522 undo_manager_signals [CAN_REDO],
523 0,
524 TRUE);
525 }
526
527 if(um->priv->next_redo >=(gint)(g_list_length(um->priv->actions) - 1))
528 {
529 um->priv->can_undo = FALSE;
530 g_signal_emit(G_OBJECT(um),
531 undo_manager_signals [CAN_UNDO],
532 0,
533 FALSE);
534 }
535 }
536
537 void
538 gtk_source_undo_manager_redo(GtkSourceUndoManager *um) {
539 GtkSourceUndoAction *undo_action;
540 gboolean modified = FALSE;
541
542 g_return_if_fail(GTK_SOURCE_IS_UNDO_MANAGER(um));
543 g_return_if_fail(um->priv != NULL);
544 g_return_if_fail(um->priv->can_redo);
545
546 undo_action = g_list_nth_data(um->priv->actions, um->priv->next_redo);
547 g_return_if_fail(undo_action != NULL);
548
549 gtk_source_undo_manager_begin_not_undoable_action(um);
550
551 do
552 {
553 if(undo_action->modified)
554 {
555 g_return_if_fail(undo_action->order_in_group <= 1);
556 modified = TRUE;
557 }
558
559 --um->priv->next_redo;
560
561 switch(undo_action->action_type)
562 {
563 case GTK_SOURCE_UNDO_ACTION_DELETE:
564 delete_text(
565 um->priv->document,
566 undo_action->action.delete.start,
567 undo_action->action.delete.end);
568
569 set_cursor(
570 um->priv->document,
571 undo_action->action.delete.start);
572
573 break;
574
575 case GTK_SOURCE_UNDO_ACTION_INSERT:
576 set_cursor(
577 um->priv->document,
578 undo_action->action.insert.pos);
579
580 insert_text(
581 um->priv->document,
582 undo_action->action.insert.pos,
583 undo_action->action.insert.text,
584 undo_action->action.insert.length);
585
586 break;
587
588 default:
589 /* Unknown action type */
590 ++um->priv->next_redo;
591 g_return_if_reached();
592 }
593
594 if(um->priv->next_redo < 0)
595 undo_action = NULL;
596 else
597 undo_action = g_list_nth_data(um->priv->actions, um->priv->next_redo);
598
599 } while((undo_action != NULL) &&(undo_action->order_in_group > 1));
600
601 if(modified)
602 {
603 ++um->priv->next_redo;
604 gtk_text_buffer_set_modified(um->priv->document, FALSE);
605 --um->priv->next_redo;
606 }
607
608 gtk_source_undo_manager_end_not_undoable_action_internal(um);
609
610 if(um->priv->next_redo < 0)
611 {
612 um->priv->can_redo = FALSE;
613 g_signal_emit(G_OBJECT(um), undo_manager_signals [CAN_REDO], 0, FALSE);
614 }
615
616 if(!um->priv->can_undo)
617 {
618 um->priv->can_undo = TRUE;
619 g_signal_emit(G_OBJECT(um), undo_manager_signals [CAN_UNDO], 0, TRUE);
620 }
621 }
622
623 static void
624 gtk_source_undo_action_free(GtkSourceUndoAction *action) {
625 if(action == NULL)
626 return;
627
628 if(action->action_type == GTK_SOURCE_UNDO_ACTION_INSERT)
629 g_free(action->action.insert.text);
630 else if(action->action_type == GTK_SOURCE_UNDO_ACTION_DELETE)
631 g_free(action->action.delete.text);
632 else
633 g_return_if_reached();
634
635 g_free(action);
636 }
637
638 static void
639 gtk_source_undo_manager_free_action_list(GtkSourceUndoManager *um) {
640 GList *l;
641
642 l = um->priv->actions;
643
644 while(l != NULL)
645 {
646 GtkSourceUndoAction *action = l->data;
647
648 if(action->order_in_group == 1)
649 --um->priv->num_of_groups;
650 um->priv->actions_in_current_group = action->order_in_group - 1;
651
652 if(action->modified)
653 um->priv->modified_action = INVALID;
654
655 gtk_source_undo_action_free(action);
656
657 l = g_list_next(l);
658 }
659
660 g_list_free(um->priv->actions);
661 um->priv->actions = NULL;
662 }
663
664 static void
665 gtk_source_undo_manager_insert_text_handler(GtkTextBuffer *buffer,
666 GtkTextIter *pos,
667 const gchar *text,
668 gint length,
669 GtkSourceUndoManager *um) {
670 GtkSourceUndoAction undo_action;
671
672 if(um->priv->running_not_undoable_actions > 0)
673 return;
674
675 undo_action.action_type = GTK_SOURCE_UNDO_ACTION_INSERT;
676
677 undo_action.action.insert.pos = gtk_text_iter_get_offset(pos);
678 undo_action.action.insert.text =(gchar*) text;
679 undo_action.action.insert.length = length;
680 undo_action.action.insert.chars = g_utf8_strlen(text, length);
681
682 if((undo_action.action.insert.chars > 1) ||(g_utf8_get_char(text) == '\n'))
683
684 undo_action.mergeable = FALSE;
685 else
686 undo_action.mergeable = TRUE;
687
688 undo_action.modified = FALSE;
689
690 gtk_source_undo_manager_add_action(um, &undo_action);
691 }
692
693 static void
694 gtk_source_undo_manager_delete_range_handler(GtkTextBuffer *buffer,
695 GtkTextIter *start,
696 GtkTextIter *end,
697 GtkSourceUndoManager *um) {
698 GtkSourceUndoAction undo_action;
699 GtkTextIter insert_iter;
700
701 if(um->priv->running_not_undoable_actions > 0)
702 return;
703
704 undo_action.action_type = GTK_SOURCE_UNDO_ACTION_DELETE;
705
706 gtk_text_iter_order(start, end);
707
708 undo_action.action.delete.start = gtk_text_iter_get_offset(start);
709 undo_action.action.delete.end = gtk_text_iter_get_offset(end);
710
711 undo_action.action.delete.text = get_chars(
712 buffer,
713 undo_action.action.delete.start,
714 undo_action.action.delete.end);
715
716 /* figure out if the user used the Delete or the Backspace key */
717 gtk_text_buffer_get_iter_at_mark(buffer, &insert_iter,
718 gtk_text_buffer_get_insert(buffer));
719 if(gtk_text_iter_get_offset(&insert_iter) <= undo_action.action.delete.start)
720 undo_action.action.delete.forward = TRUE;
721 else
722 undo_action.action.delete.forward = FALSE;
723
724 if(((undo_action.action.delete.end - undo_action.action.delete.start) > 1) ||
725 (g_utf8_get_char(undo_action.action.delete.text ) == '\n'))
726 undo_action.mergeable = FALSE;
727 else
728 undo_action.mergeable = TRUE;
729
730 undo_action.modified = FALSE;
731
732 gtk_source_undo_manager_add_action(um, &undo_action);
733
734 g_free(undo_action.action.delete.text);
735
736 }
737
738 static void
739 gtk_source_undo_manager_begin_user_action_handler(GtkTextBuffer *buffer, GtkSour ceUndoManager *um) {
740 g_return_if_fail(GTK_SOURCE_IS_UNDO_MANAGER(um));
741 g_return_if_fail(um->priv != NULL);
742
743 if(um->priv->running_not_undoable_actions > 0)
744 return;
745
746 um->priv->actions_in_current_group = 0;
747 }
748
749 static void
750 gtk_source_undo_manager_add_action(GtkSourceUndoManager *um,
751 const GtkSourceUndoAction *undo_action) {
752 GtkSourceUndoAction* action;
753
754 if(um->priv->next_redo >= 0)
755 {
756 gtk_source_undo_manager_free_first_n_actions(um, um->priv->next_redo + 1);
757 }
758
759 um->priv->next_redo = -1;
760
761 if(!gtk_source_undo_manager_merge_action(um, undo_action))
762 {
763 action = g_new(GtkSourceUndoAction, 1);
764 *action = *undo_action;
765
766 if(action->action_type == GTK_SOURCE_UNDO_ACTION_INSERT)
767 action->action.insert.text = g_strndup(undo_action->action.insert.text, un do_action->action.insert.length);
768 else if(action->action_type == GTK_SOURCE_UNDO_ACTION_DELETE)
769 action->action.delete.text = g_strdup(undo_action->action.delete.text);
770 else
771 {
772 g_free(action);
773 g_return_if_reached();
774 }
775
776 ++um->priv->actions_in_current_group;
777 action->order_in_group = um->priv->actions_in_current_group;
778
779 if(action->order_in_group == 1)
780 ++um->priv->num_of_groups;
781
782 um->priv->actions = g_list_prepend(um->priv->actions, action);
783 }
784
785 gtk_source_undo_manager_check_list_size(um);
786
787 if(!um->priv->can_undo)
788 {
789 um->priv->can_undo = TRUE;
790 g_signal_emit(G_OBJECT(um), undo_manager_signals [CAN_UNDO], 0, TRUE);
791 }
792
793 if(um->priv->can_redo)
794 {
795 um->priv->can_redo = FALSE;
796 g_signal_emit(G_OBJECT(um), undo_manager_signals [CAN_REDO], 0, FALSE);
797 }
798 }
799
800 static void
801 gtk_source_undo_manager_free_first_n_actions(GtkSourceUndoManager *um,
802 gint n) {
803 gint i;
804
805 if(um->priv->actions == NULL)
806 return;
807
808 for(i = 0; i < n; i++)
809 {
810 GtkSourceUndoAction *action = g_list_first(um->priv->actions)->data;
811
812 if(action->order_in_group == 1)
813 --um->priv->num_of_groups;
814 um->priv->actions_in_current_group = action->order_in_group - 1;
815
816 if(action->modified)
817 um->priv->modified_action = INVALID;
818
819 gtk_source_undo_action_free(action);
820
821 um->priv->actions = g_list_delete_link(um->priv->actions,
822 um->priv->actions);
823
824 if(um->priv->actions == NULL)
825 return;
826 }
827 }
828
829 static void
830 gtk_source_undo_manager_check_list_size(GtkSourceUndoManager *um) {
831 gint undo_levels;
832
833 g_return_if_fail(GTK_SOURCE_IS_UNDO_MANAGER(um));
834 g_return_if_fail(um->priv != NULL);
835
836 undo_levels = gtk_source_undo_manager_get_max_undo_levels(um);
837
838 if(undo_levels < 1)
839 return;
840
841 if(um->priv->num_of_groups > undo_levels)
842 {
843 GtkSourceUndoAction *undo_action;
844 GList *last;
845
846 last = g_list_last(um->priv->actions);
847 undo_action =(GtkSourceUndoAction*) last->data;
848
849 do
850 {
851 GList *tmp;
852
853 if(undo_action->order_in_group == 1)
854 --um->priv->num_of_groups;
855 um->priv->actions_in_current_group = undo_action->order_in_group - 1;
856
857 if(undo_action->modified)
858 um->priv->modified_action = INVALID;
859
860 gtk_source_undo_action_free(undo_action);
861
862 tmp = g_list_previous(last);
863 um->priv->actions = g_list_delete_link(um->priv->actions, last);
864 last = tmp;
865 g_return_if_fail(last != NULL);
866
867 undo_action =(GtkSourceUndoAction*) last->data;
868
869 } while((undo_action->order_in_group > 1) ||
870 (um->priv->num_of_groups > undo_levels));
871 }
872 }
873
874 /**
875 * gtk_source_undo_manager_merge_action:
876 * @um: a #GtkSourceUndoManager.
877 * @undo_action: a #GtkSourceUndoAction.
878 *
879 * This function tries to merge the undo action at the top of
880 * the stack with a new undo action. So when we undo for example
881 * typing, we can undo the whole word and not each letter by itself.
882 *
883 * Return Value: %TRUE is merge was successful, %FALSE otherwise.
884 **/
885 static gboolean
886 gtk_source_undo_manager_merge_action(GtkSourceUndoManager *um,
887 const GtkSourceUndoAction *undo_action) {
888 GtkSourceUndoAction *last_action;
889
890 g_return_val_if_fail(GTK_SOURCE_IS_UNDO_MANAGER(um), FALSE);
891 g_return_val_if_fail(um->priv != NULL, FALSE);
892
893 if(um->priv->actions == NULL)
894 return FALSE;
895
896 last_action =(GtkSourceUndoAction*) g_list_nth_data(um->priv->actions, 0);
897
898 if(!last_action->mergeable)
899 return FALSE;
900
901 if((!undo_action->mergeable) ||
902 (undo_action->action_type != last_action->action_type))
903 {
904 last_action->mergeable = FALSE;
905 return FALSE;
906 }
907
908 if(undo_action->action_type == GTK_SOURCE_UNDO_ACTION_DELETE)
909 {
910 if((last_action->action.delete.forward != undo_action->action.delete.forward ) ||
911 ((last_action->action.delete.start != undo_action->action.delete.start) & &
912 (last_action->action.delete.start != undo_action->action.delete.end)))
913 {
914 last_action->mergeable = FALSE;
915 return FALSE;
916 }
917
918 if(last_action->action.delete.start == undo_action->action.delete.start)
919 {
920 gchar *str;
921
922 #define L (last_action->action.delete.end - last_action->action.delete.start - 1 )
923 #define g_utf8_get_char_at(p,i) g_utf8_get_char(g_utf8_offset_to_pointer((p),(i) ))
924
925 /* Deleted with the delete key */
926 if((g_utf8_get_char(undo_action->action.delete.text) != ' ') &&
927 (g_utf8_get_char(undo_action->action.delete.text) != '\t') &&
928 ((g_utf8_get_char_at(last_action->action.delete.text, L) == ' ') ||
929 (g_utf8_get_char_at(last_action->action.delete.text, L) == '\t')))
930 {
931 last_action->mergeable = FALSE;
932 return FALSE;
933 }
934
935 str = g_strdup_printf("%s%s", last_action->action.delete.text,
936 undo_action->action.delete.text);
937
938 g_free(last_action->action.delete.text);
939 last_action->action.delete.end +=(undo_action->action.delete.end -
940 undo_action->action.delete.start);
941 last_action->action.delete.text = str;
942 }
943 else
944 {
945 gchar *str;
946
947 /* Deleted with the backspace key */
948 if((g_utf8_get_char(undo_action->action.delete.text) != ' ') &&
949 (g_utf8_get_char(undo_action->action.delete.text) != '\t') &&
950 ((g_utf8_get_char(last_action->action.delete.text) == ' ') ||
951 (g_utf8_get_char(last_action->action.delete.text) == '\t')))
952 {
953 last_action->mergeable = FALSE;
954 return FALSE;
955 }
956
957 str = g_strdup_printf("%s%s", undo_action->action.delete.text,
958 last_action->action.delete.text);
959
960 g_free(last_action->action.delete.text);
961 last_action->action.delete.start = undo_action->action.delete.start;
962 last_action->action.delete.text = str;
963 }
964 }
965 else if(undo_action->action_type == GTK_SOURCE_UNDO_ACTION_INSERT)
966 {
967 gchar* str;
968
969 #define I (last_action->action.insert.chars - 1)
970
971 if((undo_action->action.insert.pos !=
972 (last_action->action.insert.pos + last_action->action.insert.chars)) | |
973 ((g_utf8_get_char(undo_action->action.insert.text) != ' ') &&
974 (g_utf8_get_char(undo_action->action.insert.text) != '\t') &&
975 ((g_utf8_get_char_at(last_action->action.insert.text, I) == ' ') ||
976 (g_utf8_get_char_at(last_action->action.insert.text, I) == '\t')))
977 )
978 {
979 last_action->mergeable = FALSE;
980 return FALSE;
981 }
982
983 str = g_strdup_printf("%s%s", last_action->action.insert.text,
984 undo_action->action.insert.text);
985
986 g_free(last_action->action.insert.text);
987 last_action->action.insert.length += undo_action->action.insert.length;
988 last_action->action.insert.text = str;
989 last_action->action.insert.chars += undo_action->action.insert.chars;
990
991 }
992 else
993 /* Unknown action inside undo merge encountered */
994 g_return_val_if_reached(TRUE);
995
996 return TRUE;
997 }
998
999 gint
1000 gtk_source_undo_manager_get_max_undo_levels(GtkSourceUndoManager *um) {
1001 g_return_val_if_fail(um != NULL, 0);
1002 g_return_val_if_fail(GTK_SOURCE_IS_UNDO_MANAGER(um), 0);
1003
1004 return um->priv->max_undo_levels;
1005 }
1006
1007 void
1008 gtk_source_undo_manager_set_max_undo_levels(GtkSourceUndoManager *um,
1009 gint max_undo_levels) {
1010 gint old_levels;
1011
1012 g_return_if_fail(um != NULL);
1013 g_return_if_fail(GTK_SOURCE_IS_UNDO_MANAGER(um));
1014
1015 old_levels = um->priv->max_undo_levels;
1016 um->priv->max_undo_levels = max_undo_levels;
1017
1018 if(max_undo_levels < 1)
1019 return;
1020
1021 if(old_levels > max_undo_levels)
1022 {
1023 /* strip redo actions first */
1024 while(um->priv->next_redo >= 0 &&(um->priv->num_of_groups > max_undo_levels) )
1025 {
1026 gtk_source_undo_manager_free_first_n_actions(um, 1);
1027 um->priv->next_redo--;
1028 }
1029
1030 /* now remove undo actions if necessary */
1031 gtk_source_undo_manager_check_list_size(um);
1032
1033 /* emit "can_undo" and/or "can_redo" if appropiate */
1034 if(um->priv->next_redo < 0 && um->priv->can_redo)
1035 {
1036 um->priv->can_redo = FALSE;
1037 g_signal_emit(G_OBJECT(um), undo_manager_signals [CAN_REDO], 0, FALSE);
1038 }
1039
1040 if(um->priv->can_undo &&
1041 um->priv->next_redo >=(gint)(g_list_length(um->priv->actions) - 1))
1042 {
1043 um->priv->can_undo = FALSE;
1044 g_signal_emit(G_OBJECT(um), undo_manager_signals [CAN_UNDO], 0, FALSE);
1045 }
1046 }
1047 }
1048
1049 static void
1050 gtk_source_undo_manager_modified_changed_handler(GtkTextBuffer *buffer,
1051 GtkSourceUndoManager *um) {
1052 GtkSourceUndoAction *action;
1053 GList *list;
1054
1055 g_return_if_fail(GTK_SOURCE_IS_UNDO_MANAGER(um));
1056 g_return_if_fail(um->priv != NULL);
1057
1058 if(um->priv->actions == NULL)
1059 return;
1060
1061 list = g_list_nth(um->priv->actions, um->priv->next_redo + 1);
1062
1063 if(list != NULL)
1064 action =(GtkSourceUndoAction*) list->data;
1065 else
1066 action = NULL;
1067
1068 if(gtk_text_buffer_get_modified(buffer) == FALSE)
1069 {
1070 if(action != NULL)
1071 action->mergeable = FALSE;
1072
1073 if(um->priv->modified_action != NULL)
1074 {
1075 if(um->priv->modified_action != INVALID)
1076 um->priv->modified_action->modified = FALSE;
1077
1078 um->priv->modified_action = NULL;
1079 }
1080
1081 return;
1082 }
1083
1084 if(action == NULL)
1085 {
1086 g_return_if_fail(um->priv->running_not_undoable_actions > 0);
1087
1088 return;
1089 }
1090
1091 /* gtk_text_buffer_get_modified(buffer) == TRUE */
1092
1093 g_return_if_fail(um->priv->modified_action == NULL);
1094
1095 if(action->order_in_group > 1)
1096 um->priv->modified_undoing_group = TRUE;
1097
1098 while(action->order_in_group > 1)
1099 {
1100 list = g_list_next(list);
1101 g_return_if_fail(list != NULL);
1102
1103 action =(GtkSourceUndoAction*) list->data;
1104 g_return_if_fail(action != NULL);
1105 }
1106
1107 action->modified = TRUE;
1108 um->priv->modified_action = action;
1109 }
1110
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