Undo System: remove accumulate/store modes
[blender.git] / source / blender / editors / space_text / text_undo.c
1 /*
2  * This program is free software; you can redistribute it and/or
3  * modify it under the terms of the GNU General Public License
4  * as published by the Free Software Foundation; either version 2
5  * of the License, or (at your option) any later version.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * along with this program; if not, write to the Free Software Foundation,
14  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15  */
16
17 /** \file blender/editors/space_text/text_undo.c
18  *  \ingroup sptext
19  */
20
21 #include <string.h>
22 #include <errno.h>
23
24 #include "MEM_guardedalloc.h"
25
26 #include "DNA_text_types.h"
27
28 #include "BLI_array_utils.h"
29
30 #include "BLT_translation.h"
31
32 #include "PIL_time.h"
33
34 #include "BKE_context.h"
35 #include "BKE_report.h"
36 #include "BKE_text.h"
37 #include "BKE_undo_system.h"
38
39 #include "WM_api.h"
40 #include "WM_types.h"
41
42 #include "ED_text.h"
43 #include "ED_curve.h"
44 #include "ED_screen.h"
45 #include "ED_undo.h"
46
47 #include "UI_interface.h"
48 #include "UI_resources.h"
49
50 #include "RNA_access.h"
51 #include "RNA_define.h"
52
53 #include "text_intern.h"
54 #include "text_format.h"
55
56 /* TODO(campbell): undo_system: move text undo out of text block. */
57
58 /* -------------------------------------------------------------------- */
59 /** \name Implements ED Undo System
60  * \{ */
61
62 typedef struct TextUndoStep {
63         UndoStep step;
64         UndoRefID_Text text_ref;
65         TextUndoBuf data;
66 } TextUndoStep;
67
68 static bool text_undosys_poll(bContext *C)
69 {
70         Text *text = CTX_data_edit_text(C);
71         if (text == NULL) {
72                 return false;
73         }
74         if (ID_IS_LINKED(text)) {
75                 return false;
76         }
77         return true;
78 }
79
80 static void text_undosys_step_encode_init(struct bContext *C, UndoStep *us_p)
81 {
82         TextUndoStep *us = (TextUndoStep *)us_p;
83         BLI_assert(BLI_array_is_zeroed(&us->data, 1));
84
85         UNUSED_VARS(C);
86         /* XXX, use to set the undo type only. */
87
88         us->data.buf = NULL;
89         us->data.len = 0;
90         us->data.pos = -1;
91 }
92
93 static bool text_undosys_step_encode(struct bContext *C, struct Main *UNUSED(bmain), UndoStep *us_p)
94 {
95         TextUndoStep *us = (TextUndoStep *)us_p;
96
97         Text *text = CTX_data_edit_text(C);
98
99         /* No undo data was generated. Hint, use global undo here. */
100         if ((us->data.pos == -1) || (us->data.buf == NULL)) {
101                 return false;
102         }
103
104         us_p->is_applied = true;
105
106         us->text_ref.ptr = text;
107
108         us->step.data_size = us->data.len;
109
110         return true;
111 }
112
113
114 static void text_undosys_step_decode_undo_impl(Text *text, TextUndoStep *us)
115 {
116         BLI_assert(us->step.is_applied == true);
117         TextUndoBuf data = us->data;
118         while (data.pos > -1) {
119                 txt_do_undo(text, &data);
120         }
121         BLI_assert(data.pos == -1);
122         us->step.is_applied = false;
123 }
124
125 static void text_undosys_step_decode_redo_impl(Text *text, TextUndoStep *us)
126 {
127         BLI_assert(us->step.is_applied == false);
128         TextUndoBuf data = us->data;
129         data.pos = -1;
130         while (data.pos < us->data.pos) {
131                 txt_do_redo(text, &data);
132         }
133         BLI_assert(data.pos == us->data.pos);
134         us->step.is_applied = true;
135 }
136
137 static void text_undosys_step_decode_undo(Text *text, TextUndoStep *us)
138 {
139         TextUndoStep *us_iter = us;
140         while (us_iter->step.next && (us_iter->step.next->type == us_iter->step.type)) {
141                 if (us_iter->step.next->is_applied == false) {
142                         break;
143                 }
144                 us_iter = (TextUndoStep *)us_iter->step.next;
145         }
146         while (us_iter != us) {
147                 text_undosys_step_decode_undo_impl(text, us_iter);
148                 us_iter = (TextUndoStep *)us_iter->step.prev;
149         }
150 }
151
152 static void text_undosys_step_decode_redo(Text *text, TextUndoStep *us)
153 {
154         TextUndoStep *us_iter = us;
155         while (us_iter->step.prev && (us_iter->step.prev->type == us_iter->step.type)) {
156                 if (us_iter->step.prev->is_applied == true) {
157                         break;
158                 }
159                 us_iter = (TextUndoStep *)us_iter->step.prev;
160         }
161         while (us_iter && (us_iter->step.is_applied == false)) {
162                 text_undosys_step_decode_redo_impl(text, us_iter);
163                 if (us_iter == us) {
164                         break;
165                 }
166                 us_iter = (TextUndoStep *)us_iter->step.next;
167         }
168 }
169
170 static void text_undosys_step_decode(struct bContext *C, struct Main *UNUSED(bmain), UndoStep *us_p, int dir)
171 {
172         TextUndoStep *us = (TextUndoStep *)us_p;
173         Text *text = us->text_ref.ptr;
174
175         if (dir < 0) {
176                 text_undosys_step_decode_undo(text, us);
177         }
178         else {
179                 text_undosys_step_decode_redo(text, us);
180         }
181
182         SpaceText *st = CTX_wm_space_text(C);
183         if (st) {
184                 /* Not essential, always show text being undo where possible. */
185                 st->text = text;
186         }
187         text_update_edited(text);
188         text_update_cursor_moved(C);
189         text_drawcache_tag_update(st, 1);
190         WM_event_add_notifier(C, NC_TEXT | NA_EDITED, text);
191 }
192
193 static void text_undosys_step_free(UndoStep *us_p)
194 {
195         TextUndoStep *us = (TextUndoStep *)us_p;
196         MEM_SAFE_FREE(us->data.buf);
197 }
198
199 static void text_undosys_foreach_ID_ref(
200         UndoStep *us_p, UndoTypeForEachIDRefFn foreach_ID_ref_fn, void *user_data)
201 {
202         TextUndoStep *us = (TextUndoStep *)us_p;
203         foreach_ID_ref_fn(user_data, ((UndoRefID *)&us->text_ref));
204 }
205
206 /* Export for ED_undo_sys. */
207
208 void ED_text_undosys_type(UndoType *ut)
209 {
210         ut->name = "Text";
211         ut->poll = text_undosys_poll;
212         ut->step_encode_init = text_undosys_step_encode_init;
213         ut->step_encode = text_undosys_step_encode;
214         ut->step_decode = text_undosys_step_decode;
215         ut->step_free = text_undosys_step_free;
216
217         ut->step_foreach_ID_ref = text_undosys_foreach_ID_ref;
218
219         ut->use_context = false;
220
221         ut->step_size = sizeof(TextUndoStep);
222 }
223
224 /** \} */
225
226 /* -------------------------------------------------------------------- */
227 /** \name Utilities
228  * \{ */
229
230 /* Use operator system to finish the undo step. */
231 TextUndoBuf *ED_text_undo_push_init(bContext *C)
232 {
233         UndoStack *ustack = ED_undo_stack_get();
234         UndoStep *us_p = BKE_undosys_step_push_init_with_type(ustack, C, NULL, BKE_UNDOSYS_TYPE_TEXT);
235         TextUndoStep *us = (TextUndoStep *)us_p;
236         return &us->data;
237 }
238
239 /** \} */