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