Changeset View
Changeset View
Standalone View
Standalone View
source/blender/editors/undo/ed_undo.c
| Show First 20 Lines • Show All 71 Lines • ▼ Show 20 Lines | |||||
| /** | /** | ||||
| * \warning Values are used in #ED_undo_gpencil_step, | * \warning Values are used in #ED_undo_gpencil_step, | ||||
| * which should eventually be replaced with the undo-system. | * which should eventually be replaced with the undo-system. | ||||
| */ | */ | ||||
| enum eUndoStepDir { | enum eUndoStepDir { | ||||
| STEP_REDO = 1, | STEP_REDO = 1, | ||||
| STEP_UNDO = -1, | STEP_UNDO = -1, | ||||
| /** Only used when the undo step name or index is passed to #ed_undo_step_impl. */ | |||||
| STEP_NONE = 0, | |||||
| }; | }; | ||||
| /* -------------------------------------------------------------------- */ | /* -------------------------------------------------------------------- */ | ||||
| /** \name Generic Undo System Access | /** \name Generic Undo System Access | ||||
| * | * | ||||
| * Non-operator undo editor functions. | * Non-operator undo editor functions. | ||||
| * \{ */ | * \{ */ | ||||
| ▲ Show 20 Lines • Show All 85 Lines • ▼ Show 20 Lines | void ED_undo_push(bContext *C, const char *str) | ||||
| } | } | ||||
| if (push_retval & UNDO_PUSH_RET_OVERRIDE_CHANGED) { | if (push_retval & UNDO_PUSH_RET_OVERRIDE_CHANGED) { | ||||
| WM_main_add_notifier(NC_WM | ND_LIB_OVERRIDE_CHANGED, NULL); | WM_main_add_notifier(NC_WM | ND_LIB_OVERRIDE_CHANGED, NULL); | ||||
| } | } | ||||
| } | } | ||||
| /** | /** | ||||
| * \note Also check #undo_history_exec in bottom if you change notifiers. | * Common pre management of undo/redo (killing all running jobs, calling pre handlers, etc.). | ||||
| */ | */ | ||||
| static int ed_undo_step_impl(bContext *C, | static void ed_undo_step_pre(bContext *C, | ||||
| enum eUndoStepDir step, | wmWindowManager *wm, | ||||
| const char *undo_name, | const enum eUndoStepDir undo_dir, | ||||
| const int undo_index, | |||||
| ReportList *reports) | ReportList *reports) | ||||
| { | { | ||||
| /* Mutually exclusives, ensure correct input. */ | BLI_assert(ELEM(undo_dir, STEP_UNDO, STEP_REDO)); | ||||
| BLI_assert(((undo_name || undo_index != -1) && (step == STEP_NONE)) || | |||||
| (!(undo_name || undo_index != -1) && (step != STEP_NONE))); | Main *bmain = CTX_data_main(C); | ||||
| CLOG_INFO(&LOG, 1, "name='%s', index=%d, step=%d", undo_name, undo_index, step); | |||||
| wmWindowManager *wm = CTX_wm_manager(C); | |||||
| Scene *scene = CTX_data_scene(C); | Scene *scene = CTX_data_scene(C); | ||||
| ScrArea *area = CTX_wm_area(C); | ScrArea *area = CTX_wm_area(C); | ||||
| /* undo during jobs are running can easily lead to freeing data using by jobs, | /* undo during jobs are running can easily lead to freeing data using by jobs, | ||||
| * or they can just lead to freezing job in some other cases */ | * or they can just lead to freezing job in some other cases */ | ||||
| WM_jobs_kill_all(wm); | WM_jobs_kill_all(wm); | ||||
| if (G.debug & G_DEBUG_IO) { | if (G.debug & G_DEBUG_IO) { | ||||
| Main *bmain = CTX_data_main(C); | |||||
| if (bmain->lock != NULL) { | if (bmain->lock != NULL) { | ||||
| BKE_report(reports, RPT_INFO, "Checking sanity of current .blend file *BEFORE* undo step"); | BKE_report(reports, RPT_INFO, "Checking sanity of current .blend file *BEFORE* undo step"); | ||||
| BLO_main_validate_libraries(bmain, reports); | BLO_main_validate_libraries(bmain, reports); | ||||
| } | } | ||||
| } | } | ||||
| /* TODO(campbell): undo_system: use undo system */ | |||||
| /* grease pencil can be can be used in plenty of spaces, so check it first */ | |||||
| /* FIXME: This gpencil undo effectively only supports the one step undo/redo, undo based on name | |||||
| * or index is fully not implemented. | |||||
| * FIXME: However, it seems to never be used in current code (`ED_gpencil_session_active` seems | |||||
| * to always return false). */ | |||||
| if (ED_gpencil_session_active()) { | |||||
| return ED_undo_gpencil_step(C, (int)step); | |||||
| } | |||||
| if (area && (area->spacetype == SPACE_VIEW3D)) { | if (area && (area->spacetype == SPACE_VIEW3D)) { | ||||
| Object *obact = CTX_data_active_object(C); | Object *obact = CTX_data_active_object(C); | ||||
| if (obact && (obact->type == OB_GPENCIL)) { | if (obact && (obact->type == OB_GPENCIL)) { | ||||
| ED_gpencil_toggle_brush_cursor(C, false, NULL); | ED_gpencil_toggle_brush_cursor(C, false, NULL); | ||||
| } | } | ||||
| } | } | ||||
| UndoStep *step_data_from_name = NULL; | |||||
| enum eUndoStepDir step_for_callback = step; | |||||
| if (undo_name != NULL) { | |||||
| step_data_from_name = BKE_undosys_step_find_by_name(wm->undo_stack, undo_name); | |||||
| if (step_data_from_name == NULL) { | |||||
| return OPERATOR_CANCELLED; | |||||
| } | |||||
| /* TODO(campbell), could use simple optimization. */ | |||||
| /* Pointers match on redo. */ | |||||
| step_for_callback = (BLI_findindex(&wm->undo_stack->steps, step_data_from_name) < | |||||
| BLI_findindex(&wm->undo_stack->steps, wm->undo_stack->step_active)) ? | |||||
| STEP_UNDO : | |||||
| STEP_REDO; | |||||
| } | |||||
| else if (undo_index != -1) { | |||||
| step_for_callback = (undo_index < | |||||
| BLI_findindex(&wm->undo_stack->steps, wm->undo_stack->step_active)) ? | |||||
| STEP_UNDO : | |||||
| STEP_REDO; | |||||
| } | |||||
| /* App-Handlers (pre). */ | /* App-Handlers (pre). */ | ||||
| { | { | ||||
| /* Note: ignore grease pencil for now. */ | /* Note: ignore grease pencil for now. */ | ||||
| Main *bmain = CTX_data_main(C); | |||||
| wm->op_undo_depth++; | wm->op_undo_depth++; | ||||
| BKE_callback_exec_id( | BKE_callback_exec_id( | ||||
| bmain, &scene->id, (step_for_callback > 0) ? BKE_CB_EVT_UNDO_PRE : BKE_CB_EVT_REDO_PRE); | bmain, &scene->id, (undo_dir == STEP_UNDO) ? BKE_CB_EVT_UNDO_PRE : BKE_CB_EVT_REDO_PRE); | ||||
| wm->op_undo_depth--; | wm->op_undo_depth--; | ||||
| } | } | ||||
| } | |||||
| /* Undo System */ | /** | ||||
| * Common post management of undo/redo (calling post handlers, adding notifiers etc.). | |||||
| * | |||||
| * \note Also check #undo_history_exec in bottom if you change notifiers. | |||||
| */ | |||||
| static void ed_undo_step_post(bContext *C, | |||||
| wmWindowManager *wm, | |||||
| const enum eUndoStepDir undo_dir, | |||||
| ReportList *reports) | |||||
| { | { | ||||
| if (undo_name) { | BLI_assert(ELEM(undo_dir, STEP_UNDO, STEP_REDO)); | ||||
| BKE_undosys_step_undo_with_data(wm->undo_stack, C, step_data_from_name); | |||||
| } | |||||
| else if (undo_index != -1) { | |||||
| BKE_undosys_step_undo_from_index(wm->undo_stack, C, undo_index); | |||||
| } | |||||
| else { | |||||
| if (step == STEP_UNDO) { | |||||
| BKE_undosys_step_undo(wm->undo_stack, C); | |||||
| } | |||||
| else { | |||||
| BKE_undosys_step_redo(wm->undo_stack, C); | |||||
| } | |||||
| } | |||||
| /* Set special modes for grease pencil */ | Main *bmain = CTX_data_main(C); | ||||
| if (area && (area->spacetype == SPACE_VIEW3D)) { | Scene *scene = CTX_data_scene(C); | ||||
| Object *obact = CTX_data_active_object(C); | |||||
| if (obact && (obact->type == OB_GPENCIL)) { | |||||
| /* set cursor */ | |||||
| if (ELEM(obact->mode, | |||||
| OB_MODE_PAINT_GPENCIL, | |||||
| OB_MODE_SCULPT_GPENCIL, | |||||
| OB_MODE_WEIGHT_GPENCIL, | |||||
| OB_MODE_VERTEX_GPENCIL)) { | |||||
| ED_gpencil_toggle_brush_cursor(C, true, NULL); | |||||
| } | |||||
| else { | |||||
| ED_gpencil_toggle_brush_cursor(C, false, NULL); | |||||
| } | |||||
| /* set workspace mode */ | |||||
| Base *basact = CTX_data_active_base(C); | |||||
| ED_object_base_activate(C, basact); | |||||
| } | |||||
| } | |||||
| } | |||||
| /* App-Handlers (post). */ | /* App-Handlers (post). */ | ||||
| { | { | ||||
| Main *bmain = CTX_data_main(C); | |||||
| scene = CTX_data_scene(C); | |||||
| wm->op_undo_depth++; | wm->op_undo_depth++; | ||||
| BKE_callback_exec_id( | BKE_callback_exec_id( | ||||
| bmain, &scene->id, step_for_callback > 0 ? BKE_CB_EVT_UNDO_POST : BKE_CB_EVT_REDO_POST); | bmain, &scene->id, (undo_dir == STEP_UNDO) ? BKE_CB_EVT_UNDO_POST : BKE_CB_EVT_REDO_POST); | ||||
| wm->op_undo_depth--; | wm->op_undo_depth--; | ||||
| } | } | ||||
| if (G.debug & G_DEBUG_IO) { | if (G.debug & G_DEBUG_IO) { | ||||
| Main *bmain = CTX_data_main(C); | |||||
| if (bmain->lock != NULL) { | if (bmain->lock != NULL) { | ||||
| BKE_report(reports, RPT_INFO, "Checking sanity of current .blend file *AFTER* undo step"); | BKE_report(reports, RPT_INFO, "Checking sanity of current .blend file *AFTER* undo step"); | ||||
| BLO_main_validate_libraries(bmain, reports); | BLO_main_validate_libraries(bmain, reports); | ||||
| } | } | ||||
| } | } | ||||
| WM_event_add_notifier(C, NC_WINDOW, NULL); | WM_event_add_notifier(C, NC_WINDOW, NULL); | ||||
| WM_event_add_notifier(C, NC_WM | ND_UNDO, NULL); | WM_event_add_notifier(C, NC_WM | ND_UNDO, NULL); | ||||
| WM_toolsystem_refresh_active(C); | WM_toolsystem_refresh_active(C); | ||||
| Main *bmain = CTX_data_main(C); | |||||
| WM_toolsystem_refresh_screen_all(bmain); | WM_toolsystem_refresh_screen_all(bmain); | ||||
| if (CLOG_CHECK(&LOG, 1)) { | if (CLOG_CHECK(&LOG, 1)) { | ||||
| BKE_undosys_print(wm->undo_stack); | BKE_undosys_print(wm->undo_stack); | ||||
| } | } | ||||
| return OPERATOR_FINISHED; | |||||
| } | } | ||||
| /** Undo or redo one step from current active one. | |||||
| * May undo or redo several steps at once only if the target step is a 'skipped' one. | |||||
| * The target step will be the one immediately before or after the active one. */ | |||||
| static int ed_undo_step_direction(bContext *C, enum eUndoStepDir step, ReportList *reports) | static int ed_undo_step_direction(bContext *C, enum eUndoStepDir step, ReportList *reports) | ||||
| { | { | ||||
| return ed_undo_step_impl(C, step, NULL, -1, reports); | BLI_assert(ELEM(step, STEP_UNDO, STEP_REDO)); | ||||
| CLOG_INFO(&LOG, 1, "direction=%s", (step == STEP_UNDO) ? "STEP_UNDO" : "STEP_REDO"); | |||||
| /* TODO(campbell): undo_system: use undo system */ | |||||
| /* grease pencil can be can be used in plenty of spaces, so check it first */ | |||||
| /* FIXME: This gpencil undo effectively only supports the one step undo/redo, undo based on name | |||||
| * or index is fully not implemented. | |||||
| * FIXME: However, it seems to never be used in current code (`ED_gpencil_session_active` seems | |||||
| * to always return false). */ | |||||
| if (ED_gpencil_session_active()) { | |||||
| return ED_undo_gpencil_step(C, (int)step); | |||||
| } | } | ||||
| wmWindowManager *wm = CTX_wm_manager(C); | |||||
| ed_undo_step_pre(C, wm, step, reports); | |||||
| if (step == STEP_UNDO) { | |||||
| BKE_undosys_step_undo(wm->undo_stack, C); | |||||
| } | |||||
| else { | |||||
| BKE_undosys_step_redo(wm->undo_stack, C); | |||||
| } | |||||
| ed_undo_step_post(C, wm, step, reports); | |||||
| return OPERATOR_FINISHED; | |||||
| } | |||||
| /** Undo the step matching given name. | |||||
| * May undo or redo several steps at once. | |||||
| * The target step will be the one immediately before given named one. */ | |||||
| static int ed_undo_step_by_name(bContext *C, const char *undo_name, ReportList *reports) | static int ed_undo_step_by_name(bContext *C, const char *undo_name, ReportList *reports) | ||||
| { | { | ||||
| return ed_undo_step_impl(C, STEP_NONE, undo_name, -1, reports); | BLI_assert(undo_name != NULL); | ||||
| /* FIXME: See comments in `ed_undo_step_direction`. */ | |||||
| if (ED_gpencil_session_active()) { | |||||
| BLI_assert(!"Not implemented currently."); | |||||
| } | } | ||||
| wmWindowManager *wm = CTX_wm_manager(C); | |||||
| UndoStep *undo_step_from_name = BKE_undosys_step_find_by_name(wm->undo_stack, undo_name); | |||||
| if (undo_step_from_name == NULL) { | |||||
| CLOG_ERROR(&LOG, "Step name='%s' not found in current undo stack", undo_name); | |||||
| return OPERATOR_CANCELLED; | |||||
| } | |||||
| /* TODO(campbell), could use simple optimization. */ | |||||
| /* Pointers match on redo. */ | |||||
| const int target_step_index = BLI_findindex(&wm->undo_stack->steps, undo_step_from_name); | |||||
| const int active_step_index = BLI_findindex(&wm->undo_stack->steps, wm->undo_stack->step_active); | |||||
| const enum eUndoStepDir undo_dir = (target_step_index < active_step_index) ? STEP_UNDO : | |||||
| STEP_REDO; | |||||
| CLOG_INFO(&LOG, | |||||
| 1, | |||||
| "name='%s', found direction=%s, index=%d", | |||||
| undo_name, | |||||
| (undo_dir == STEP_UNDO) ? "STEP_UNDO" : "STEP_REDO", | |||||
| target_step_index); | |||||
| /* This function is currently not supposed to redo ever. | |||||
| * TODO: Will be fixed in future in continuing undo code refactor effort. */ | |||||
| BLI_assert(undo_dir == STEP_UNDO); | |||||
| ed_undo_step_pre(C, wm, undo_dir, reports); | |||||
| BKE_undosys_step_undo_with_data(wm->undo_stack, C, undo_step_from_name); | |||||
| ed_undo_step_post(C, wm, undo_dir, reports); | |||||
| return OPERATOR_FINISHED; | |||||
| } | |||||
| /** Load the step matching given index in the stack. | |||||
| * May undo or redo several steps at once. | |||||
| * The target step will be the one indicated by the given index. */ | |||||
| static int ed_undo_step_by_index(bContext *C, const int undo_index, ReportList *reports) | static int ed_undo_step_by_index(bContext *C, const int undo_index, ReportList *reports) | ||||
| { | { | ||||
| return ed_undo_step_impl(C, STEP_NONE, NULL, undo_index, reports); | BLI_assert(undo_index >= 0); | ||||
| /* FIXME: See comments in `ed_undo_step_direction`. */ | |||||
| if (ED_gpencil_session_active()) { | |||||
| BLI_assert(!"Not implemented currently."); | |||||
| } | |||||
| wmWindowManager *wm = CTX_wm_manager(C); | |||||
| const int active_step_index = BLI_findindex(&wm->undo_stack->steps, wm->undo_stack->step_active); | |||||
| const enum eUndoStepDir undo_dir = (undo_index < active_step_index) ? STEP_UNDO : STEP_REDO; | |||||
| CLOG_INFO(&LOG, | |||||
| 1, | |||||
| "index='%d', found direction=%s", | |||||
| undo_index, | |||||
| (undo_dir == STEP_UNDO) ? "STEP_UNDO" : "STEP_REDO"); | |||||
| ed_undo_step_pre(C, wm, undo_dir, reports); | |||||
| BKE_undosys_step_undo_from_index(wm->undo_stack, C, undo_index); | |||||
| ed_undo_step_post(C, wm, undo_dir, reports); | |||||
| return OPERATOR_FINISHED; | |||||
| } | } | ||||
| void ED_undo_grouped_push(bContext *C, const char *str) | void ED_undo_grouped_push(bContext *C, const char *str) | ||||
| { | { | ||||
| /* do nothing if previous undo task is the same as this one (or from the same undo group) */ | /* do nothing if previous undo task is the same as this one (or from the same undo group) */ | ||||
| wmWindowManager *wm = CTX_wm_manager(C); | wmWindowManager *wm = CTX_wm_manager(C); | ||||
| const UndoStep *us = wm->undo_stack->step_active; | const UndoStep *us = wm->undo_stack->step_active; | ||||
| if (us && STREQ(str, us->name)) { | if (us && STREQ(str, us->name)) { | ||||
| ▲ Show 20 Lines • Show All 630 Lines • Show Last 20 Lines | |||||