Remove invalid assert
[blender.git] / source / blender / editors / undo / ed_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  * The Original Code is Copyright (C) 2004 Blender Foundation
19  * All rights reserved.
20  *
21  * The Original Code is: all of this file.
22  *
23  * Contributor(s): none yet.
24  *
25  * ***** END GPL LICENSE BLOCK *****
26  */
27
28 /** \file blender/editors/undo/ed_undo.c
29  *  \ingroup edundo
30  */
31
32 #include <string.h>
33
34 #include "MEM_guardedalloc.h"
35
36 #include "CLG_log.h"
37
38 #include "DNA_scene_types.h"
39
40 #include "BLI_utildefines.h"
41 #include "BLI_callbacks.h"
42 #include "BLI_listbase.h"
43
44 #include "BLT_translation.h"
45
46 #include "BKE_blender_undo.h"
47 #include "BKE_context.h"
48 #include "BKE_global.h"
49 #include "BKE_main.h"
50 #include "BKE_screen.h"
51 #include "BKE_undo_system.h"
52
53 #include "ED_gpencil.h"
54 #include "ED_render.h"
55 #include "ED_screen.h"
56 #include "ED_undo.h"
57
58 #include "WM_api.h"
59 #include "WM_types.h"
60
61 #include "RNA_access.h"
62 #include "RNA_define.h"
63
64 #include "UI_interface.h"
65 #include "UI_resources.h"
66
67 /** We only need this locally. */
68 static CLG_LogRef LOG = {"ed.undo"};
69
70 /* -------------------------------------------------------------------- */
71 /** \name Generic Undo System Access
72  *
73  * Non-operator undo editor functions.
74  * \{ */
75
76 void ED_undo_push(bContext *C, const char *str)
77 {
78         CLOG_INFO(&LOG, 1, "name='%s'", str);
79
80         const int steps = U.undosteps;
81
82         if (steps <= 0) {
83                 return;
84         }
85
86         wmWindowManager *wm = CTX_wm_manager(C);
87
88         /* Only apply limit if this is the last undo step. */
89         if (wm->undo_stack->step_active && (wm->undo_stack->step_active->next == NULL)) {
90                 BKE_undosys_stack_limit_steps_and_memory(wm->undo_stack, steps - 1, 0);
91         }
92
93         BKE_undosys_step_push(wm->undo_stack, C, str);
94
95         if (U.undomemory != 0) {
96                 const size_t memory_limit = (size_t)U.undomemory * 1024 * 1024;
97                 BKE_undosys_stack_limit_steps_and_memory(wm->undo_stack, 0, memory_limit);
98         }
99
100         WM_file_tag_modified();
101 }
102
103 /* note: also check undo_history_exec() in bottom if you change notifiers */
104 static int ed_undo_step(bContext *C, int step, const char *undoname)
105 {
106         CLOG_INFO(&LOG, 1, "name='%s', step=%d", undoname, step);
107         wmWindowManager *wm = CTX_wm_manager(C);
108         wmWindow *win = CTX_wm_window(C);
109         // Main *bmain = CTX_data_main(C);
110         Scene *scene = CTX_data_scene(C);
111
112         /* undo during jobs are running can easily lead to freeing data using by jobs,
113          * or they can just lead to freezing job in some other cases */
114         if (WM_jobs_test(wm, scene, WM_JOB_TYPE_ANY)) {
115                 return OPERATOR_CANCELLED;
116         }
117
118         /* TODO(campbell): undo_system: use undo system */
119         /* grease pencil can be can be used in plenty of spaces, so check it first */
120         if (ED_gpencil_session_active()) {
121                 return ED_undo_gpencil_step(C, step, undoname);
122         }
123
124         UndoStep *step_data_from_name = NULL;
125         int step_for_callback = step;
126         if (undoname != NULL) {
127                 step_data_from_name = BKE_undosys_step_find_by_name(wm->undo_stack, undoname);
128                 if (step_data_from_name == NULL) {
129                         return OPERATOR_CANCELLED;
130                 }
131
132                 /* TODO(campbell), could use simple optimization. */
133                 /* Pointers match on redo. */
134                 step_for_callback = (
135                         BLI_findindex(&wm->undo_stack->steps, step_data_from_name) <
136                         BLI_findindex(&wm->undo_stack->steps, wm->undo_stack->step_active)) ? 1 : -1;
137         }
138
139         /* App-Handlers (pre). */
140         {
141                 /* Note: ignore grease pencil for now. */
142                 Main *bmain = CTX_data_main(C);
143                 wm->op_undo_depth++;
144                 BLI_callback_exec(bmain, &scene->id, (step_for_callback > 0) ? BLI_CB_EVT_UNDO_PRE : BLI_CB_EVT_REDO_PRE);
145                 wm->op_undo_depth--;
146         }
147
148
149         /* Undo System */
150         {
151                 if (undoname) {
152                         BKE_undosys_step_undo_with_data(wm->undo_stack, C, step_data_from_name);
153                 }
154                 else {
155                         BKE_undosys_step_undo_compat_only(wm->undo_stack, C, step);
156                 }
157         }
158
159         /* App-Handlers (post). */
160         {
161                 Main *bmain = CTX_data_main(C);
162                 scene = CTX_data_scene(C);
163                 wm->op_undo_depth++;
164                 BLI_callback_exec(bmain, &scene->id, step_for_callback > 0 ? BLI_CB_EVT_UNDO_PRE : BLI_CB_EVT_REDO_PRE);
165                 wm->op_undo_depth--;
166         }
167
168         WM_event_add_notifier(C, NC_WINDOW, NULL);
169         WM_event_add_notifier(C, NC_WM | ND_UNDO, NULL);
170
171         if (win) {
172                 win->addmousemove = true;
173         }
174
175         return OPERATOR_FINISHED;
176 }
177
178 void ED_undo_grouped_push(bContext *C, const char *str)
179 {
180         /* do nothing if previous undo task is the same as this one (or from the same undo group) */
181         wmWindowManager *wm = CTX_wm_manager(C);
182         const UndoStep *us = wm->undo_stack->step_active;
183         if (us && STREQ(str, us->name)) {
184                 BKE_undosys_stack_clear_active(wm->undo_stack);
185         }
186
187         /* push as usual */
188         ED_undo_push(C, str);
189 }
190
191 void ED_undo_pop(bContext *C)
192 {
193         ed_undo_step(C, 1, NULL);
194 }
195 void ED_undo_redo(bContext *C)
196 {
197         ed_undo_step(C, -1, NULL);
198 }
199
200 void ED_undo_push_op(bContext *C, wmOperator *op)
201 {
202         /* in future, get undo string info? */
203         ED_undo_push(C, op->type->name);
204 }
205
206 void ED_undo_grouped_push_op(bContext *C, wmOperator *op)
207 {
208         if (op->type->undo_group[0] != '\0') {
209                 ED_undo_grouped_push(C, op->type->undo_group);
210         }
211         else {
212                 ED_undo_grouped_push(C, op->type->name);
213         }
214 }
215
216 void ED_undo_pop_op(bContext *C, wmOperator *op)
217 {
218         /* search back a couple of undo's, in case something else added pushes */
219         ed_undo_step(C, 0, op->type->name);
220 }
221
222 /* name optionally, function used to check for operator redo panel */
223 bool ED_undo_is_valid(const bContext *C, const char *undoname)
224 {
225         wmWindowManager *wm = CTX_wm_manager(C);
226         return BKE_undosys_stack_has_undo(wm->undo_stack, undoname);
227 }
228
229 /**
230  * Ideally we wont access the stack directly,
231  * this is needed for modes which handle undo themselves (bypassing #ED_undo_push).
232  *
233  * Using global isn't great, this just avoids doing inline,
234  * causing 'BKE_global.h' & 'BKE_main.h' includes.
235  */
236 UndoStack *ED_undo_stack_get(void)
237 {
238         wmWindowManager *wm = G_MAIN->wm.first;
239         return wm->undo_stack;
240 }
241
242 /** \} */
243
244 /* -------------------------------------------------------------------- */
245 /** \name Undo, Undo Push & Redo Operators
246  * \{ */
247
248 static int ed_undo_exec(bContext *C, wmOperator *UNUSED(op))
249 {
250         /* "last operator" should disappear, later we can tie this with undo stack nicer */
251         WM_operator_stack_clear(CTX_wm_manager(C));
252         return ed_undo_step(C, 1, NULL);
253 }
254
255 static int ed_undo_push_exec(bContext *C, wmOperator *op)
256 {
257         char str[BKE_UNDO_STR_MAX];
258         RNA_string_get(op->ptr, "message", str);
259         ED_undo_push(C, str);
260         return OPERATOR_FINISHED;
261 }
262
263 static int ed_redo_exec(bContext *C, wmOperator *UNUSED(op))
264 {
265         return ed_undo_step(C, -1, NULL);
266 }
267
268 static int ed_undo_redo_exec(bContext *C, wmOperator *UNUSED(op))
269 {
270         wmOperator *last_op = WM_operator_last_redo(C);
271         const int ret = ED_undo_operator_repeat(C, last_op);
272         return ret ? OPERATOR_FINISHED : OPERATOR_CANCELLED;
273 }
274
275 static bool ed_undo_redo_poll(bContext *C)
276 {
277         wmOperator *last_op = WM_operator_last_redo(C);
278         return last_op && ED_operator_screenactive(C) &&
279                 WM_operator_check_ui_enabled(C, last_op->type->name);
280 }
281
282 void ED_OT_undo(wmOperatorType *ot)
283 {
284         /* identifiers */
285         ot->name = "Undo";
286         ot->description = "Undo previous action";
287         ot->idname = "ED_OT_undo";
288
289         /* api callbacks */
290         ot->exec = ed_undo_exec;
291         ot->poll = ED_operator_screenactive;
292 }
293
294 void ED_OT_undo_push(wmOperatorType *ot)
295 {
296         /* identifiers */
297         ot->name = "Undo Push";
298         ot->description = "Add an undo state (internal use only)";
299         ot->idname = "ED_OT_undo_push";
300
301         /* api callbacks */
302         ot->exec = ed_undo_push_exec;
303
304         ot->flag = OPTYPE_INTERNAL;
305
306         RNA_def_string(ot->srna, "message", "Add an undo step *function may be moved*", BKE_UNDO_STR_MAX, "Undo Message", "");
307 }
308
309 void ED_OT_redo(wmOperatorType *ot)
310 {
311         /* identifiers */
312         ot->name = "Redo";
313         ot->description = "Redo previous action";
314         ot->idname = "ED_OT_redo";
315
316         /* api callbacks */
317         ot->exec = ed_redo_exec;
318         ot->poll = ED_operator_screenactive;
319 }
320
321 void ED_OT_undo_redo(wmOperatorType *ot)
322 {
323         /* identifiers */
324         ot->name = "Undo and Redo";
325         ot->description = "Undo and redo previous action";
326         ot->idname = "ED_OT_undo_redo";
327
328         /* api callbacks */
329         ot->exec = ed_undo_redo_exec;
330         ot->poll = ed_undo_redo_poll;
331 }
332
333 /** \} */
334
335 /* -------------------------------------------------------------------- */
336 /** \name Operator Repeat
337  * \{ */
338
339 /* ui callbacks should call this rather than calling WM_operator_repeat() themselves */
340 int ED_undo_operator_repeat(bContext *C, struct wmOperator *op)
341 {
342         int ret = 0;
343
344         if (op) {
345                 CLOG_INFO(&LOG, 1, "idname='%s'", op->type->idname);
346                 wmWindowManager *wm = CTX_wm_manager(C);
347                 struct Scene *scene = CTX_data_scene(C);
348
349                 /* keep in sync with logic in view3d_panel_operator_redo() */
350                 ARegion *ar = CTX_wm_region(C);
351                 ARegion *ar1 = BKE_area_find_region_active_win(CTX_wm_area(C));
352
353                 if (ar1)
354                         CTX_wm_region_set(C, ar1);
355
356                 if ((WM_operator_repeat_check(C, op)) &&
357                     (WM_operator_poll(C, op->type)) &&
358                      /* note, undo/redo cant run if there are jobs active,
359                       * check for screen jobs only so jobs like material/texture/world preview
360                       * (which copy their data), wont stop redo, see [#29579]],
361                       *
362                       * note, - WM_operator_check_ui_enabled() jobs test _must_ stay in sync with this */
363                     (WM_jobs_test(wm, scene, WM_JOB_TYPE_ANY) == 0))
364                 {
365                         int retval;
366
367                         ED_viewport_render_kill_jobs(wm, CTX_data_main(C), true);
368
369                         if (G.debug & G_DEBUG)
370                                 printf("redo_cb: operator redo %s\n", op->type->name);
371
372                         WM_operator_free_all_after(wm, op);
373
374                         ED_undo_pop_op(C, op);
375
376                         if (op->type->check) {
377                                 if (op->type->check(C, op)) {
378                                         /* check for popup and re-layout buttons */
379                                         ARegion *ar_menu = CTX_wm_menu(C);
380                                         if (ar_menu) {
381                                                 ED_region_tag_refresh_ui(ar_menu);
382                                         }
383                                 }
384                         }
385
386                         retval = WM_operator_repeat(C, op);
387                         if ((retval & OPERATOR_FINISHED) == 0) {
388                                 if (G.debug & G_DEBUG)
389                                         printf("redo_cb: operator redo failed: %s, return %d\n", op->type->name, retval);
390                                 ED_undo_redo(C);
391                         }
392                         else {
393                                 ret = 1;
394                         }
395                 }
396                 else {
397                         if (G.debug & G_DEBUG) {
398                                 printf("redo_cb: WM_operator_repeat_check returned false %s\n", op->type->name);
399                         }
400                 }
401
402                 /* set region back */
403                 CTX_wm_region_set(C, ar);
404         }
405         else {
406                 CLOG_WARN(&LOG, "called with NULL 'op'");
407         }
408
409         return ret;
410 }
411
412
413 void ED_undo_operator_repeat_cb(bContext *C, void *arg_op, void *UNUSED(arg_unused))
414 {
415         ED_undo_operator_repeat(C, (wmOperator *)arg_op);
416 }
417
418 void ED_undo_operator_repeat_cb_evt(bContext *C, void *arg_op, int UNUSED(arg_event))
419 {
420         ED_undo_operator_repeat(C, (wmOperator *)arg_op);
421 }
422
423 /** \} */
424
425 /* -------------------------------------------------------------------- */
426 /** \name Undo History Operator
427  * \{ */
428
429 /* create enum based on undo items */
430 static const EnumPropertyItem *rna_undo_itemf(bContext *C, int *totitem)
431 {
432         EnumPropertyItem item_tmp = {0}, *item = NULL;
433         int i = 0;
434
435         wmWindowManager *wm = CTX_wm_manager(C);
436         if (wm->undo_stack == NULL) {
437                 return NULL;
438         }
439
440         for (UndoStep *us = wm->undo_stack->steps.first; us; us = us->next, i++) {
441                 if (us->skip == false) {
442                         item_tmp.identifier = us->name;
443                         item_tmp.name = IFACE_(us->name);
444                         if (us == wm->undo_stack->step_active) {
445                                 item_tmp.icon = ICON_RESTRICT_VIEW_OFF;
446                         }
447                         else {
448                                 item_tmp.icon = ICON_NONE;
449                         }
450                         item_tmp.value = i;
451                         RNA_enum_item_add(&item, totitem, &item_tmp);
452                 }
453         }
454         RNA_enum_item_end(&item, totitem);
455
456         return item;
457 }
458
459
460 static int undo_history_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
461 {
462         int totitem = 0;
463
464         {
465                 const EnumPropertyItem *item = rna_undo_itemf(C, &totitem);
466
467                 if (totitem > 0) {
468                         uiPopupMenu *pup = UI_popup_menu_begin(C, RNA_struct_ui_name(op->type->srna), ICON_NONE);
469                         uiLayout *layout = UI_popup_menu_layout(pup);
470                         uiLayout *split = uiLayoutSplit(layout, 0.0f, false);
471                         uiLayout *column = NULL;
472                         const int col_size = 20 + totitem / 12;
473                         int i, c;
474                         bool add_col = true;
475
476                         for (c = 0, i = totitem; i--;) {
477                                 if (add_col && !(c % col_size)) {
478                                         column = uiLayoutColumn(split, false);
479                                         add_col = false;
480                                 }
481                                 if (item[i].identifier) {
482                                         uiItemIntO(column, item[i].name, item[i].icon, op->type->idname, "item", item[i].value);
483                                         ++c;
484                                         add_col = true;
485                                 }
486                         }
487
488                         MEM_freeN((void *)item);
489
490                         UI_popup_menu_end(C, pup);
491                 }
492
493         }
494         return OPERATOR_CANCELLED;
495 }
496
497 /* note: also check ed_undo_step() in top if you change notifiers */
498 static int undo_history_exec(bContext *C, wmOperator *op)
499 {
500         PropertyRNA *prop = RNA_struct_find_property(op->ptr, "item");
501         if (RNA_property_is_set(op->ptr, prop)) {
502                 int item = RNA_property_int_get(op->ptr, prop);
503                 wmWindowManager *wm = CTX_wm_manager(C);
504                 BKE_undosys_step_undo_from_index(wm->undo_stack, C, item);
505                 WM_event_add_notifier(C, NC_WINDOW, NULL);
506                 return OPERATOR_FINISHED;
507         }
508         return OPERATOR_CANCELLED;
509 }
510
511 void ED_OT_undo_history(wmOperatorType *ot)
512 {
513         /* identifiers */
514         ot->name = "Undo History";
515         ot->description = "Redo specific action in history";
516         ot->idname = "ED_OT_undo_history";
517
518         /* api callbacks */
519         ot->invoke = undo_history_invoke;
520         ot->exec = undo_history_exec;
521         ot->poll = ED_operator_screenactive;
522
523         RNA_def_int(ot->srna, "item", 0, 0, INT_MAX, "Item", "", 0, INT_MAX);
524
525 }
526
527 /** \} */