/* SPDX-License-Identifier: GPL-2.0-or-later */ /** \file * \ingroup edutil * * Utility operators for UI data or for the UI to use. */ #include #include "DNA_space_types.h" #include "DNA_windowmanager_types.h" #include "BLI_fileops.h" #include "BLI_utildefines.h" #include "BKE_context.h" #include "BKE_icons.h" #include "BKE_lib_id.h" #include "BKE_lib_override.h" #include "BKE_main.h" #include "BKE_report.h" #include "BLT_translation.h" #include "ED_asset.h" #include "ED_render.h" #include "ED_undo.h" #include "ED_util.h" #include "RNA_access.h" #include "RNA_prototypes.h" #include "UI_interface.h" #include "WM_api.h" #include "WM_types.h" /* -------------------------------------------------------------------- */ /** \name ID Previews * \{ */ static bool lib_id_preview_editing_poll(bContext *C) { const PointerRNA idptr = CTX_data_pointer_get(C, "id"); BLI_assert(!idptr.data || RNA_struct_is_ID(idptr.type)); const ID *id = (ID *)idptr.data; if (!id) { return false; } if (ID_IS_LINKED(id)) { CTX_wm_operator_poll_msg_set(C, TIP_("Can't edit external library data")); return false; } if (ID_IS_OVERRIDE_LIBRARY(id)) { CTX_wm_operator_poll_msg_set(C, TIP_("Can't edit previews of overridden library data")); return false; } if (!BKE_previewimg_id_get_p(id)) { CTX_wm_operator_poll_msg_set(C, TIP_("Data-block does not support previews")); return false; } return true; } static int lib_id_load_custom_preview_exec(bContext *C, wmOperator *op) { char filepath[FILE_MAX]; RNA_string_get(op->ptr, "filepath", filepath); if (!BLI_is_file(filepath)) { BKE_reportf(op->reports, RPT_ERROR, "File not found '%s'", filepath); return OPERATOR_CANCELLED; } PointerRNA idptr = CTX_data_pointer_get(C, "id"); ID *id = (ID *)idptr.data; BKE_previewimg_id_custom_set(id, filepath); WM_event_add_notifier(C, NC_ASSET | NA_EDITED, nullptr); return OPERATOR_FINISHED; } static void ED_OT_lib_id_load_custom_preview(wmOperatorType *ot) { ot->name = "Load Custom Preview"; ot->description = "Choose an image to help identify the data-block visually"; ot->idname = "ED_OT_lib_id_load_custom_preview"; /* api callbacks */ ot->poll = lib_id_preview_editing_poll; ot->exec = lib_id_load_custom_preview_exec; ot->invoke = WM_operator_filesel; /* flags */ ot->flag = OPTYPE_UNDO | OPTYPE_INTERNAL; WM_operator_properties_filesel(ot, FILE_TYPE_FOLDER | FILE_TYPE_IMAGE, FILE_SPECIAL, FILE_OPENFILE, WM_FILESEL_FILEPATH, FILE_DEFAULTDISPLAY, FILE_SORT_DEFAULT); } static bool lib_id_generate_preview_poll(bContext *C) { if (!lib_id_preview_editing_poll(C)) { return false; } const PointerRNA idptr = CTX_data_pointer_get(C, "id"); const ID *id = (ID *)idptr.data; if (GS(id->name) == ID_NT) { CTX_wm_operator_poll_msg_set(C, TIP_("Can't generate automatic preview for node group")); return false; } return true; } static int lib_id_generate_preview_exec(bContext *C, wmOperator * /*op*/) { PointerRNA idptr = CTX_data_pointer_get(C, "id"); ID *id = (ID *)idptr.data; ED_preview_kill_jobs(CTX_wm_manager(C), CTX_data_main(C)); PreviewImage *preview = BKE_previewimg_id_get(id); if (preview) { BKE_previewimg_clear(preview); } UI_icon_render_id(C, nullptr, id, ICON_SIZE_PREVIEW, true); WM_event_add_notifier(C, NC_ASSET | NA_EDITED, nullptr); ED_assetlist_storage_tag_main_data_dirty(); return OPERATOR_FINISHED; } static void ED_OT_lib_id_generate_preview(wmOperatorType *ot) { ot->name = "Generate Preview"; ot->description = "Create an automatic preview for the selected data-block"; ot->idname = "ED_OT_lib_id_generate_preview"; /* api callbacks */ ot->poll = lib_id_generate_preview_poll; ot->exec = lib_id_generate_preview_exec; /* flags */ ot->flag = OPTYPE_INTERNAL | OPTYPE_REGISTER | OPTYPE_UNDO; } static bool lib_id_generate_preview_from_object_poll(bContext *C) { if (!lib_id_preview_editing_poll(C)) { return false; } if (CTX_data_active_object(C) == nullptr) { return false; } return true; } static int lib_id_generate_preview_from_object_exec(bContext *C, wmOperator * /*op*/) { PointerRNA idptr = CTX_data_pointer_get(C, "id"); ID *id = (ID *)idptr.data; ED_preview_kill_jobs(CTX_wm_manager(C), CTX_data_main(C)); Object *object_to_render = CTX_data_active_object(C); BKE_previewimg_id_free(id); PreviewImage *preview_image = BKE_previewimg_id_ensure(id); UI_icon_render_id_ex(C, nullptr, &object_to_render->id, ICON_SIZE_PREVIEW, true, preview_image); WM_event_add_notifier(C, NC_ASSET | NA_EDITED, nullptr); ED_assetlist_storage_tag_main_data_dirty(); return OPERATOR_FINISHED; } static void ED_OT_lib_id_generate_preview_from_object(wmOperatorType *ot) { ot->name = "Generate Preview from Object"; ot->description = "Create a preview for this asset by rendering the active object"; ot->idname = "ED_OT_lib_id_generate_preview_from_object"; /* api callbacks */ ot->poll = lib_id_generate_preview_from_object_poll; ot->exec = lib_id_generate_preview_from_object_exec; /* flags */ ot->flag = OPTYPE_INTERNAL | OPTYPE_REGISTER | OPTYPE_UNDO; } /** \} */ /* -------------------------------------------------------------------- */ /** \name Generic ID Operators * \{ */ static int lib_id_fake_user_toggle_exec(bContext *C, wmOperator *op) { PropertyPointerRNA pprop; PointerRNA idptr = PointerRNA_NULL; UI_context_active_but_prop_get_templateID(C, &pprop.ptr, &pprop.prop); if (pprop.prop) { idptr = RNA_property_pointer_get(&pprop.ptr, pprop.prop); } if ((pprop.prop == nullptr) || RNA_pointer_is_null(&idptr) || !RNA_struct_is_ID(idptr.type)) { BKE_report( op->reports, RPT_ERROR, "Incorrect context for running data-block fake user toggling"); return OPERATOR_CANCELLED; } ID *id = (ID *)idptr.data; if (!BKE_id_is_editable(CTX_data_main(C), id) || ELEM(GS(id->name), ID_GR, ID_SCE, ID_SCR, ID_TXT, ID_OB, ID_WS)) { BKE_report(op->reports, RPT_ERROR, "Data-block type does not support fake user"); return OPERATOR_CANCELLED; } if (ID_FAKE_USERS(id)) { id_fake_user_clear(id); } else { id_fake_user_set(id); } return OPERATOR_FINISHED; } static void ED_OT_lib_id_fake_user_toggle(wmOperatorType *ot) { /* identifiers */ ot->name = "Toggle Fake User"; ot->description = "Save this data-block even if it has no users"; ot->idname = "ED_OT_lib_id_fake_user_toggle"; /* api callbacks */ ot->exec = lib_id_fake_user_toggle_exec; /* flags */ ot->flag = OPTYPE_UNDO | OPTYPE_INTERNAL; } static int lib_id_unlink_exec(bContext *C, wmOperator *op) { PropertyPointerRNA pprop; PointerRNA idptr; UI_context_active_but_prop_get_templateID(C, &pprop.ptr, &pprop.prop); if (pprop.prop) { idptr = RNA_property_pointer_get(&pprop.ptr, pprop.prop); } if ((pprop.prop == nullptr) || RNA_pointer_is_null(&idptr) || !RNA_struct_is_ID(idptr.type)) { BKE_report( op->reports, RPT_ERROR, "Incorrect context for running data-block fake user toggling"); return OPERATOR_CANCELLED; } memset(&idptr, 0, sizeof(idptr)); RNA_property_pointer_set(&pprop.ptr, pprop.prop, idptr, nullptr); RNA_property_update(C, &pprop.ptr, pprop.prop); return OPERATOR_FINISHED; } static void ED_OT_lib_id_unlink(wmOperatorType *ot) { /* identifiers */ ot->name = "Unlink Data-Block"; ot->description = "Remove a usage of a data-block, clearing the assignment"; ot->idname = "ED_OT_lib_id_unlink"; /* api callbacks */ ot->exec = lib_id_unlink_exec; /* flags */ ot->flag = OPTYPE_UNDO | OPTYPE_INTERNAL; } static bool lib_id_override_editable_toggle_poll(bContext *C) { const PointerRNA id_ptr = CTX_data_pointer_get_type(C, "id", &RNA_ID); const ID *id = static_cast(id_ptr.data); return id && ID_IS_OVERRIDE_LIBRARY_REAL(id) && !ID_IS_LINKED(id); } static int lib_id_override_editable_toggle_exec(bContext *C, wmOperator * /*op*/) { Main *bmain = CTX_data_main(C); const PointerRNA id_ptr = CTX_data_pointer_get_type(C, "id", &RNA_ID); ID *id = static_cast(id_ptr.data); const bool is_system_override = BKE_lib_override_library_is_system_defined(bmain, id); if (is_system_override) { /* A system override is not editable. Make it an editable (non-system-defined) one. */ id->override_library->flag &= ~IDOVERRIDE_LIBRARY_FLAG_SYSTEM_DEFINED; } else { /* Reset override, which makes it non-editable (i.e. a system define override). */ BKE_lib_override_library_id_reset(bmain, id, true); } WM_main_add_notifier(NC_WM | ND_LIB_OVERRIDE_CHANGED, nullptr); return OPERATOR_FINISHED; } static void ED_OT_lib_id_override_editable_toggle(wmOperatorType *ot) { /* identifiers */ ot->name = "Toggle Library Override Editable"; ot->description = "Set if this library override data-block can be edited"; ot->idname = "ED_OT_lib_id_override_editable_toggle"; /* api callbacks */ ot->poll = lib_id_override_editable_toggle_poll; ot->exec = lib_id_override_editable_toggle_exec; /* flags */ ot->flag = OPTYPE_UNDO | OPTYPE_INTERNAL; } /** \} */ /* -------------------------------------------------------------------- */ /** \name General editor utils. * \{ */ static int ed_flush_edits_exec(bContext *C, wmOperator * /*op*/) { Main *bmain = CTX_data_main(C); ED_editors_flush_edits(bmain); return OPERATOR_FINISHED; } static void ED_OT_flush_edits(wmOperatorType *ot) { /* identifiers */ ot->name = "Flush Edits"; ot->description = "Flush edit data from active editing modes"; ot->idname = "ED_OT_flush_edits"; /* api callbacks */ ot->exec = ed_flush_edits_exec; /* flags */ ot->flag = OPTYPE_INTERNAL; } /** \} */ void ED_operatortypes_edutils() { WM_operatortype_append(ED_OT_lib_id_load_custom_preview); WM_operatortype_append(ED_OT_lib_id_generate_preview); WM_operatortype_append(ED_OT_lib_id_generate_preview_from_object); WM_operatortype_append(ED_OT_lib_id_fake_user_toggle); WM_operatortype_append(ED_OT_lib_id_unlink); WM_operatortype_append(ED_OT_lib_id_override_editable_toggle); WM_operatortype_append(ED_OT_flush_edits); WM_operatortype_append(ED_OT_undo); WM_operatortype_append(ED_OT_undo_push); WM_operatortype_append(ED_OT_redo); WM_operatortype_append(ED_OT_undo_redo); WM_operatortype_append(ED_OT_undo_history); }