/* SPDX-FileCopyrightText: 2023 Blender Authors * * SPDX-License-Identifier: GPL-2.0-or-later */ /** \file * \ingroup bke */ #include #include #include #include "MEM_guardedalloc.h" #include "DNA_collection_types.h" #include "DNA_object_types.h" #include "DNA_scene_types.h" #include "DNA_screen_types.h" #include "DNA_space_types.h" #include "DNA_view3d_types.h" #include "DNA_windowmanager_types.h" #include "DNA_workspace_types.h" #include "DEG_depsgraph.hh" #include "BLI_listbase.h" #include "BLI_threads.h" #include "BLI_utildefines.h" #include "BLT_translation.hh" #include "BKE_context.hh" #include "BKE_layer.hh" #include "BKE_main.hh" #include "BKE_scene.hh" #include "BKE_screen.hh" #include "BKE_sound.h" #include "BKE_wm_runtime.hh" #include "BKE_workspace.hh" #include "RE_engine.h" #include "RNA_access.hh" #include "RNA_prototypes.hh" #include "CLG_log.h" #ifdef WITH_PYTHON # include "BPY_extern.hh" #endif using blender::Vector; static CLG_LogRef LOG = {"bke.context"}; /* struct */ struct bContext { int thread; /* windowmanager context */ struct { wmWindowManager *manager; wmWindow *window; WorkSpace *workspace; bScreen *screen; ScrArea *area; ARegion *region; ARegion *region_popup; wmGizmoGroup *gizmo_group; const bContextStore *store; /* Operator poll. */ /** * Store the reason the poll function fails (static string, not allocated). * For more advanced formatting use `operator_poll_msg_dyn_params`. */ const char *operator_poll_msg; /** * Store values to dynamically to create the string (called when a tool-tip is shown). */ bContextPollMsgDyn_Params operator_poll_msg_dyn_params; } wm; /* data context */ struct { Main *main; Scene *scene; int recursion; /** True if python is initialized. */ bool py_init; void *py_context; /** * If we need to remove members, do so in a copy * (keep this to check if the copy needs freeing). */ void *py_context_orig; } data; }; /* context */ bContext *CTX_create() { bContext *C = MEM_callocN(__func__); return C; } bContext *CTX_copy(const bContext *C) { bContext *newC = MEM_callocN(__func__); *newC = *C; memset(&newC->wm.operator_poll_msg_dyn_params, 0, sizeof(newC->wm.operator_poll_msg_dyn_params)); return newC; } void CTX_free(bContext *C) { /* This may contain a dynamically allocated message, free. */ CTX_wm_operator_poll_msg_clear(C); MEM_freeN(C); } /* store */ /** * Append a new context store to \a contexts, copying entries from the previous one if any. */ static bContextStore *ctx_store_extend(Vector> &contexts) { /* ensure we have a context to put the entry in, if it was already used * we have to copy the context to ensure */ if (contexts.is_empty()) { contexts.append(std::make_unique()); } else if (contexts.last()->used) { auto new_ctx = std::make_unique(bContextStore{contexts.last()->entries, false}); contexts.append(std::move(new_ctx)); } return contexts.last().get(); } bContextStore *CTX_store_add(Vector> &contexts, const blender::StringRef name, const PointerRNA *ptr) { bContextStore *ctx = ctx_store_extend(contexts); ctx->entries.append(bContextStoreEntry{name, *ptr}); return ctx; } bContextStore *CTX_store_add(Vector> &contexts, const blender::StringRef name, const blender::StringRef str) { bContextStore *ctx = ctx_store_extend(contexts); ctx->entries.append(bContextStoreEntry{name, std::string{str}}); return ctx; } bContextStore *CTX_store_add(Vector> &contexts, const blender::StringRef name, const int64_t value) { bContextStore *ctx = ctx_store_extend(contexts); ctx->entries.append(bContextStoreEntry{name, value}); return ctx; } bContextStore *CTX_store_add_all(Vector> &contexts, const bContextStore *context) { bContextStore *ctx = ctx_store_extend(contexts); for (const bContextStoreEntry &src_entry : context->entries) { ctx->entries.append(src_entry); } return ctx; } const bContextStore *CTX_store_get(const bContext *C) { return C->wm.store; } void CTX_store_set(bContext *C, const bContextStore *store) { C->wm.store = store; } const PointerRNA *CTX_store_ptr_lookup(const bContextStore *store, const blender::StringRef name, const StructRNA *type) { for (auto entry = store->entries.rbegin(); entry != store->entries.rend(); ++entry) { if (entry->name == name && std::holds_alternative(entry->value)) { const PointerRNA &ptr = std::get(entry->value); if (!type || RNA_struct_is_a(ptr.type, type)) { return &ptr; } } } return nullptr; } template const T *ctx_store_lookup_impl(const bContextStore *store, const blender::StringRef name) { for (auto entry = store->entries.rbegin(); entry != store->entries.rend(); ++entry) { if (entry->name == name && std::holds_alternative(entry->value)) { return &std::get(entry->value); } } return nullptr; } std::optional CTX_store_string_lookup(const bContextStore *store, const blender::StringRef name) { if (const std::string *value = ctx_store_lookup_impl(store, name)) { return *value; } return {}; } std::optional CTX_store_int_lookup(const bContextStore *store, const blender::StringRef name) { if (const int64_t *value = ctx_store_lookup_impl(store, name)) { return *value; } return {}; } /* is python initialized? */ bool CTX_py_init_get(bContext *C) { return C->data.py_init; } void CTX_py_init_set(bContext *C, bool value) { C->data.py_init = value; } void *CTX_py_dict_get(const bContext *C) { return C->data.py_context; } void *CTX_py_dict_get_orig(const bContext *C) { return C->data.py_context_orig; } void CTX_py_state_push(bContext *C, bContext_PyState *pystate, void *value) { pystate->py_context = C->data.py_context; pystate->py_context_orig = C->data.py_context_orig; C->data.py_context = value; C->data.py_context_orig = value; } void CTX_py_state_pop(bContext *C, bContext_PyState *pystate) { C->data.py_context = pystate->py_context; C->data.py_context_orig = pystate->py_context_orig; } /* data context utility functions */ struct bContextDataResult { PointerRNA ptr; Vector list; PropertyRNA *prop; int index; blender::StringRefNull str; std::optional int_value; const char **dir; short type; /* 0: normal, 1: seq */ }; static void *ctx_wm_python_context_get(const bContext *C, const char *member, const StructRNA *member_type, void *fall_through) { #ifdef WITH_PYTHON if (UNLIKELY(C && CTX_py_dict_get(C))) { bContextDataResult result{}; BPY_context_member_get((bContext *)C, member, &result); if (result.ptr.data) { if (RNA_struct_is_a(result.ptr.type, member_type)) { return result.ptr.data; } CLOG_WARN(&LOG, "PyContext '%s' is a '%s', expected a '%s'", member, RNA_struct_identifier(result.ptr.type), RNA_struct_identifier(member_type)); } } #else UNUSED_VARS(C, member, member_type); #endif /* don't allow UI context access from non-main threads */ if (!BLI_thread_is_main()) { return nullptr; } return fall_through; } static eContextResult ctx_data_get(bContext *C, const char *member, bContextDataResult *result) { bScreen *screen; ScrArea *area; ARegion *region; int done = 0, recursion = C->data.recursion; int ret = 0; *result = {}; #ifdef WITH_PYTHON if (CTX_py_dict_get(C)) { if (BPY_context_member_get(C, member, result)) { return CTX_RESULT_OK; } } #endif /* don't allow UI context access from non-main threads */ if (!BLI_thread_is_main()) { return CTX_RESULT_MEMBER_NOT_FOUND; } /* we check recursion to ensure that we do not get infinite * loops requesting data from ourselves in a context callback */ /* Ok, this looks evil... * if (ret) done = -(-ret | -done); * * Values in order of importance * (0, -1, 1) - Where 1 is highest priority */ if (done != 1 && recursion < 1 && C->wm.store) { C->data.recursion = 1; if (const PointerRNA *ptr = CTX_store_ptr_lookup(C->wm.store, member, nullptr)) { result->ptr = *ptr; done = 1; } else if (std::optional str = CTX_store_string_lookup(C->wm.store, member)) { result->str = *str; result->type = CTX_DATA_TYPE_STRING; done = 1; } else if (std::optional int_value = CTX_store_int_lookup(C->wm.store, member)) { result->int_value = int_value; result->type = CTX_DATA_TYPE_INT64; done = 1; } } if (done != 1 && recursion < 2 && (region = CTX_wm_region(C))) { C->data.recursion = 2; if (region->runtime->type && region->runtime->type->context) { ret = region->runtime->type->context(C, member, result); if (ret) { done = -(-ret | -done); } } } if (done != 1 && recursion < 3 && (area = CTX_wm_area(C))) { C->data.recursion = 3; if (area->type && area->type->context) { ret = area->type->context(C, member, result); if (ret) { done = -(-ret | -done); } } } if (done != 1 && recursion < 4 && (screen = CTX_wm_screen(C))) { bContextDataCallback cb = reinterpret_cast(screen->context); C->data.recursion = 4; if (cb) { ret = cb(C, member, result); if (ret) { done = -(-ret | -done); } } } C->data.recursion = recursion; return eContextResult(done); } static void *ctx_data_pointer_get(const bContext *C, const char *member) { bContextDataResult result; if (C && ctx_data_get((bContext *)C, member, &result) == CTX_RESULT_OK) { BLI_assert(result.type == CTX_DATA_TYPE_POINTER); return result.ptr.data; } return nullptr; } static bool ctx_data_pointer_verify(const bContext *C, const char *member, void **pointer) { /* if context is nullptr, pointer must be nullptr too and that is a valid return */ if (C == nullptr) { *pointer = nullptr; return true; } bContextDataResult result; if (ctx_data_get((bContext *)C, member, &result) == CTX_RESULT_OK) { BLI_assert(result.type == CTX_DATA_TYPE_POINTER); *pointer = result.ptr.data; return true; } *pointer = nullptr; return false; } static bool ctx_data_collection_get(const bContext *C, const char *member, Vector *list) { bContextDataResult result; if (ctx_data_get((bContext *)C, member, &result) == CTX_RESULT_OK) { BLI_assert(result.type == CTX_DATA_TYPE_COLLECTION); *list = std::move(result.list); return true; } list->clear(); return false; } static bool ctx_data_base_collection_get(const bContext *C, const char *member, Vector *list) { Vector ctx_object_list; if ((ctx_data_collection_get(C, member, &ctx_object_list) == false) || ctx_object_list.is_empty()) { list->clear(); return false; } bContextDataResult result{}; Scene *scene = CTX_data_scene(C); ViewLayer *view_layer = CTX_data_view_layer(C); BKE_view_layer_synced_ensure(scene, view_layer); bool ok = false; for (PointerRNA &ctx_object : ctx_object_list) { Object *ob = static_cast(ctx_object.data); Base *base = BKE_view_layer_base_find(view_layer, ob); if (base != nullptr) { CTX_data_list_add(&result, &scene->id, &RNA_ObjectBase, base); ok = true; } } CTX_data_type_set(&result, CTX_DATA_TYPE_COLLECTION); *list = std::move(result.list); return ok; } PointerRNA CTX_data_pointer_get(const bContext *C, const char *member) { bContextDataResult result; if (ctx_data_get((bContext *)C, member, &result) == CTX_RESULT_OK) { BLI_assert(result.type == CTX_DATA_TYPE_POINTER); return result.ptr; } return PointerRNA_NULL; } PointerRNA CTX_data_pointer_get_type(const bContext *C, const char *member, StructRNA *type) { PointerRNA ptr = CTX_data_pointer_get(C, member); if (ptr.data) { if (RNA_struct_is_a(ptr.type, type)) { return ptr; } CLOG_WARN(&LOG, "member '%s' is '%s', not '%s'", member, RNA_struct_identifier(ptr.type), RNA_struct_identifier(type)); } return PointerRNA_NULL; } PointerRNA CTX_data_pointer_get_type_silent(const bContext *C, const char *member, StructRNA *type) { PointerRNA ptr = CTX_data_pointer_get(C, member); if (ptr.data && RNA_struct_is_a(ptr.type, type)) { return ptr; } return PointerRNA_NULL; } Vector CTX_data_collection_get(const bContext *C, const char *member) { bContextDataResult result; if (ctx_data_get((bContext *)C, member, &result) == CTX_RESULT_OK) { BLI_assert(result.type == CTX_DATA_TYPE_COLLECTION); return result.list; } return {}; } void CTX_data_collection_remap_property(blender::MutableSpan collection_pointers, const char *propname) { for (PointerRNA &ptr : collection_pointers) { ptr = RNA_pointer_get(&ptr, propname); } } std::optional CTX_data_string_get(const bContext *C, const char *member) { bContextDataResult result; if (ctx_data_get((bContext *)C, member, &result) == CTX_RESULT_OK) { BLI_assert(result.type == CTX_DATA_TYPE_STRING); return result.str; } return {}; } std::optional CTX_data_int_get(const bContext *C, const char *member) { bContextDataResult result; if (ctx_data_get((bContext *)C, member, &result) == CTX_RESULT_OK) { BLI_assert(result.type == CTX_DATA_TYPE_INT64); return result.int_value; } return {}; } int /*eContextResult*/ CTX_data_get(const bContext *C, const char *member, PointerRNA *r_ptr, Vector *r_lb, PropertyRNA **r_prop, int *r_index, blender::StringRef *r_str, std::optional *r_int_value, short *r_type) { bContextDataResult result; eContextResult ret = ctx_data_get((bContext *)C, member, &result); if (ret == CTX_RESULT_OK) { *r_ptr = result.ptr; *r_lb = result.list; *r_prop = result.prop; *r_index = result.index; *r_str = result.str; *r_int_value = result.int_value; *r_type = result.type; } else { *r_ptr = {}; r_lb->clear(); *r_str = ""; *r_int_value = {}; *r_type = 0; } return ret; } static void data_dir_add(ListBase *lb, const char *member, const bool use_all) { LinkData *link; if ((use_all == false) && STREQ(member, "scene")) { /* exception */ return; } if (BLI_findstring(lb, member, offsetof(LinkData, data))) { return; } link = MEM_callocN(__func__); link->data = (void *)member; BLI_addtail(lb, link); } ListBase CTX_data_dir_get_ex(const bContext *C, const bool use_store, const bool use_rna, const bool use_all) { bContextDataResult result{}; ListBase lb; bScreen *screen; ScrArea *area; ARegion *region; int a; memset(&lb, 0, sizeof(lb)); if (use_rna) { char name_buf[256], *name; int namelen; PropertyRNA *iterprop; PointerRNA ctx_ptr = RNA_pointer_create_discrete(nullptr, &RNA_Context, (void *)C); iterprop = RNA_struct_iterator_property(ctx_ptr.type); RNA_PROP_BEGIN (&ctx_ptr, itemptr, iterprop) { name = RNA_struct_name_get_alloc(&itemptr, name_buf, sizeof(name_buf), &namelen); data_dir_add(&lb, name, use_all); if (name != name_buf) { MEM_freeN(name); } } RNA_PROP_END; } if (use_store && C->wm.store) { for (const bContextStoreEntry &entry : C->wm.store->entries) { data_dir_add(&lb, entry.name.c_str(), use_all); } } if ((region = CTX_wm_region(C)) && region->runtime->type && region->runtime->type->context) { region->runtime->type->context(C, "", &result); if (result.dir) { for (a = 0; result.dir[a]; a++) { data_dir_add(&lb, result.dir[a], use_all); } } } if ((area = CTX_wm_area(C)) && area->type && area->type->context) { area->type->context(C, "", &result); if (result.dir) { for (a = 0; result.dir[a]; a++) { data_dir_add(&lb, result.dir[a], use_all); } } } if ((screen = CTX_wm_screen(C)) && screen->context) { bContextDataCallback cb = reinterpret_cast(screen->context); cb(C, "", &result); if (result.dir) { for (a = 0; result.dir[a]; a++) { data_dir_add(&lb, result.dir[a], use_all); } } } return lb; } ListBase CTX_data_dir_get(const bContext *C) { return CTX_data_dir_get_ex(C, true, false, false); } bool CTX_data_equals(const char *member, const char *str) { return STREQ(member, str); } bool CTX_data_dir(const char *member) { return member[0] == '\0'; } void CTX_data_id_pointer_set(bContextDataResult *result, ID *id) { result->ptr = RNA_id_pointer_create(id); } void CTX_data_pointer_set(bContextDataResult *result, ID *id, StructRNA *type, void *data) { result->ptr = RNA_pointer_create_discrete(id, type, data); } void CTX_data_pointer_set_ptr(bContextDataResult *result, const PointerRNA *ptr) { result->ptr = *ptr; } void CTX_data_id_list_add(bContextDataResult *result, ID *id) { result->list.append(RNA_id_pointer_create(id)); } void CTX_data_list_add(bContextDataResult *result, ID *id, StructRNA *type, void *data) { result->list.append(RNA_pointer_create_discrete(id, type, data)); } void CTX_data_list_add_ptr(bContextDataResult *result, const PointerRNA *ptr) { result->list.append(*ptr); } int ctx_data_list_count(const bContext *C, bool (*func)(const bContext *, blender::Vector *)) { blender::Vector list; if (func(C, &list)) { return list.size(); } return 0; } void CTX_data_prop_set(bContextDataResult *result, PropertyRNA *prop, int index) { result->prop = prop; result->index = index; } void CTX_data_dir_set(bContextDataResult *result, const char **dir) { result->dir = dir; } void CTX_data_type_set(bContextDataResult *result, short type) { result->type = type; } short CTX_data_type_get(bContextDataResult *result) { return result->type; } /* window manager context */ wmWindowManager *CTX_wm_manager(const bContext *C) { return C->wm.manager; } bool CTX_wm_interface_locked(const bContext *C) { return C->wm.manager->runtime->is_interface_locked; } wmWindow *CTX_wm_window(const bContext *C) { return static_cast( ctx_wm_python_context_get(C, "window", &RNA_Window, C->wm.window)); } WorkSpace *CTX_wm_workspace(const bContext *C) { return static_cast( ctx_wm_python_context_get(C, "workspace", &RNA_WorkSpace, C->wm.workspace)); } bScreen *CTX_wm_screen(const bContext *C) { return static_cast(ctx_wm_python_context_get(C, "screen", &RNA_Screen, C->wm.screen)); } ScrArea *CTX_wm_area(const bContext *C) { return static_cast(ctx_wm_python_context_get(C, "area", &RNA_Area, C->wm.area)); } SpaceLink *CTX_wm_space_data(const bContext *C) { ScrArea *area = CTX_wm_area(C); return (area) ? static_cast(area->spacedata.first) : nullptr; } ARegion *CTX_wm_region(const bContext *C) { return static_cast(ctx_wm_python_context_get(C, "region", &RNA_Region, C->wm.region)); } void *CTX_wm_region_data(const bContext *C) { ARegion *region = CTX_wm_region(C); return (region) ? region->regiondata : nullptr; } ARegion *CTX_wm_region_popup(const bContext *C) { return C->wm.region_popup; } wmGizmoGroup *CTX_wm_gizmo_group(const bContext *C) { return C->wm.gizmo_group; } wmMsgBus *CTX_wm_message_bus(const bContext *C) { return C->wm.manager ? C->wm.manager->message_bus : nullptr; } ReportList *CTX_wm_reports(const bContext *C) { if (C->wm.manager) { return &(C->wm.manager->runtime->reports); } return nullptr; } View3D *CTX_wm_view3d(const bContext *C) { ScrArea *area = CTX_wm_area(C); if (area && area->spacetype == SPACE_VIEW3D) { return static_cast(area->spacedata.first); } return nullptr; } RegionView3D *CTX_wm_region_view3d(const bContext *C) { ScrArea *area = CTX_wm_area(C); ARegion *region = CTX_wm_region(C); if (area && area->spacetype == SPACE_VIEW3D) { if (region && region->regiontype == RGN_TYPE_WINDOW) { return static_cast(region->regiondata); } } return nullptr; } SpaceText *CTX_wm_space_text(const bContext *C) { ScrArea *area = CTX_wm_area(C); if (area && area->spacetype == SPACE_TEXT) { return static_cast(area->spacedata.first); } return nullptr; } SpaceConsole *CTX_wm_space_console(const bContext *C) { ScrArea *area = CTX_wm_area(C); if (area && area->spacetype == SPACE_CONSOLE) { return static_cast(area->spacedata.first); } return nullptr; } SpaceImage *CTX_wm_space_image(const bContext *C) { ScrArea *area = CTX_wm_area(C); if (area && area->spacetype == SPACE_IMAGE) { return static_cast(area->spacedata.first); } return nullptr; } SpaceProperties *CTX_wm_space_properties(const bContext *C) { ScrArea *area = CTX_wm_area(C); if (area && area->spacetype == SPACE_PROPERTIES) { return static_cast(area->spacedata.first); } return nullptr; } SpaceFile *CTX_wm_space_file(const bContext *C) { ScrArea *area = CTX_wm_area(C); if (area && area->spacetype == SPACE_FILE) { return static_cast(area->spacedata.first); } return nullptr; } SpaceSeq *CTX_wm_space_seq(const bContext *C) { ScrArea *area = CTX_wm_area(C); if (area && area->spacetype == SPACE_SEQ) { return static_cast(area->spacedata.first); } return nullptr; } SpaceOutliner *CTX_wm_space_outliner(const bContext *C) { ScrArea *area = CTX_wm_area(C); if (area && area->spacetype == SPACE_OUTLINER) { return static_cast(area->spacedata.first); } return nullptr; } SpaceNla *CTX_wm_space_nla(const bContext *C) { ScrArea *area = CTX_wm_area(C); if (area && area->spacetype == SPACE_NLA) { return static_cast(area->spacedata.first); } return nullptr; } SpaceNode *CTX_wm_space_node(const bContext *C) { ScrArea *area = CTX_wm_area(C); if (area && area->spacetype == SPACE_NODE) { return static_cast(area->spacedata.first); } return nullptr; } SpaceGraph *CTX_wm_space_graph(const bContext *C) { ScrArea *area = CTX_wm_area(C); if (area && area->spacetype == SPACE_GRAPH) { return static_cast(area->spacedata.first); } return nullptr; } SpaceAction *CTX_wm_space_action(const bContext *C) { ScrArea *area = CTX_wm_area(C); if (area && area->spacetype == SPACE_ACTION) { return static_cast(area->spacedata.first); } return nullptr; } SpaceInfo *CTX_wm_space_info(const bContext *C) { ScrArea *area = CTX_wm_area(C); if (area && area->spacetype == SPACE_INFO) { return static_cast(area->spacedata.first); } return nullptr; } SpaceUserPref *CTX_wm_space_userpref(const bContext *C) { ScrArea *area = CTX_wm_area(C); if (area && area->spacetype == SPACE_USERPREF) { return static_cast(area->spacedata.first); } return nullptr; } SpaceClip *CTX_wm_space_clip(const bContext *C) { ScrArea *area = CTX_wm_area(C); if (area && area->spacetype == SPACE_CLIP) { return static_cast(area->spacedata.first); } return nullptr; } SpaceTopBar *CTX_wm_space_topbar(const bContext *C) { ScrArea *area = CTX_wm_area(C); if (area && area->spacetype == SPACE_TOPBAR) { return static_cast(area->spacedata.first); } return nullptr; } SpaceSpreadsheet *CTX_wm_space_spreadsheet(const bContext *C) { ScrArea *area = CTX_wm_area(C); if (area && area->spacetype == SPACE_SPREADSHEET) { return static_cast(area->spacedata.first); } return nullptr; } void CTX_wm_manager_set(bContext *C, wmWindowManager *wm) { C->wm.manager = wm; C->wm.window = nullptr; C->wm.screen = nullptr; C->wm.area = nullptr; C->wm.region = nullptr; } #ifdef WITH_PYTHON # define PYCTX_REGION_MEMBERS "region", "region_data" # define PYCTX_AREA_MEMBERS "area", "space_data", PYCTX_REGION_MEMBERS # define PYCTX_SCREEN_MEMBERS "screen", PYCTX_AREA_MEMBERS # define PYCTX_WINDOW_MEMBERS "window", "scene", "workspace", PYCTX_SCREEN_MEMBERS #endif void CTX_wm_window_set(bContext *C, wmWindow *win) { C->wm.window = win; if (win) { C->data.scene = win->scene; } C->wm.workspace = (win) ? BKE_workspace_active_get(win->workspace_hook) : nullptr; C->wm.screen = (win) ? BKE_workspace_active_screen_get(win->workspace_hook) : nullptr; C->wm.area = nullptr; C->wm.region = nullptr; #ifdef WITH_PYTHON if (C->data.py_context != nullptr) { const char *members[] = {PYCTX_WINDOW_MEMBERS}; BPY_context_dict_clear_members_array( &C->data.py_context, C->data.py_context_orig, members, ARRAY_SIZE(members)); } #endif } void CTX_wm_screen_set(bContext *C, bScreen *screen) { C->wm.screen = screen; C->wm.area = nullptr; C->wm.region = nullptr; #ifdef WITH_PYTHON if (C->data.py_context != nullptr) { const char *members[] = {PYCTX_SCREEN_MEMBERS}; BPY_context_dict_clear_members_array( &C->data.py_context, C->data.py_context_orig, members, ARRAY_SIZE(members)); } #endif } void CTX_wm_area_set(bContext *C, ScrArea *area) { C->wm.area = area; C->wm.region = nullptr; #ifdef WITH_PYTHON if (C->data.py_context != nullptr) { const char *members[] = {PYCTX_AREA_MEMBERS}; BPY_context_dict_clear_members_array( &C->data.py_context, C->data.py_context_orig, members, ARRAY_SIZE(members)); } #endif } void CTX_wm_region_set(bContext *C, ARegion *region) { C->wm.region = region; #ifdef WITH_PYTHON if (C->data.py_context != nullptr) { const char *members[] = {PYCTX_REGION_MEMBERS}; BPY_context_dict_clear_members_array( &C->data.py_context, C->data.py_context_orig, members, ARRAY_SIZE(members)); } #endif } void CTX_wm_region_popup_set(bContext *C, ARegion *region_popup) { BLI_assert(region_popup == nullptr || region_popup->regiontype == RGN_TYPE_TEMPORARY); C->wm.region_popup = region_popup; } void CTX_wm_gizmo_group_set(bContext *C, wmGizmoGroup *gzgroup) { C->wm.gizmo_group = gzgroup; } void CTX_wm_operator_poll_msg_clear(bContext *C) { bContextPollMsgDyn_Params *params = &C->wm.operator_poll_msg_dyn_params; if (params->free_fn != nullptr) { params->free_fn(C, params->user_data); } params->get_fn = nullptr; params->free_fn = nullptr; params->user_data = nullptr; C->wm.operator_poll_msg = nullptr; } void CTX_wm_operator_poll_msg_set(bContext *C, const char *msg) { CTX_wm_operator_poll_msg_clear(C); C->wm.operator_poll_msg = msg; } void CTX_wm_operator_poll_msg_set_dynamic(bContext *C, const bContextPollMsgDyn_Params *params) { CTX_wm_operator_poll_msg_clear(C); C->wm.operator_poll_msg_dyn_params = *params; } const char *CTX_wm_operator_poll_msg_get(bContext *C, bool *r_free) { bContextPollMsgDyn_Params *params = &C->wm.operator_poll_msg_dyn_params; if (params->get_fn != nullptr) { char *msg = params->get_fn(C, params->user_data); if (msg != nullptr) { *r_free = true; } return msg; } *r_free = false; return IFACE_(C->wm.operator_poll_msg); } /* data context */ Main *CTX_data_main(const bContext *C) { Main *bmain; if (ctx_data_pointer_verify(C, "blend_data", (void **)&bmain)) { return bmain; } return C->data.main; } void CTX_data_main_set(bContext *C, Main *bmain) { C->data.main = bmain; BKE_sound_refresh_callback_bmain(bmain); } Scene *CTX_data_scene(const bContext *C) { Scene *scene; if (ctx_data_pointer_verify(C, "scene", (void **)&scene)) { return scene; } return C->data.scene; } ViewLayer *CTX_data_view_layer(const bContext *C) { ViewLayer *view_layer; if (ctx_data_pointer_verify(C, "view_layer", (void **)&view_layer)) { return view_layer; } wmWindow *win = CTX_wm_window(C); Scene *scene = CTX_data_scene(C); if (win) { view_layer = BKE_view_layer_find(scene, win->view_layer_name); if (view_layer) { return view_layer; } } return BKE_view_layer_default_view(scene); } RenderEngineType *CTX_data_engine_type(const bContext *C) { Scene *scene = CTX_data_scene(C); return RE_engines_find(scene->r.engine); } LayerCollection *CTX_data_layer_collection(const bContext *C) { ViewLayer *view_layer = CTX_data_view_layer(C); LayerCollection *layer_collection; if (ctx_data_pointer_verify(C, "layer_collection", (void **)&layer_collection)) { if (BKE_view_layer_has_collection(view_layer, layer_collection->collection)) { return layer_collection; } } /* fallback */ return BKE_layer_collection_get_active(view_layer); } Collection *CTX_data_collection(const bContext *C) { Collection *collection; if (ctx_data_pointer_verify(C, "collection", (void **)&collection)) { return collection; } LayerCollection *layer_collection = CTX_data_layer_collection(C); if (layer_collection) { return layer_collection->collection; } /* fallback */ Scene *scene = CTX_data_scene(C); return scene->master_collection; } enum eContextObjectMode CTX_data_mode_enum_ex(const Object *obedit, const Object *ob, const eObjectMode object_mode) { // Object *obedit = CTX_data_edit_object(C); if (obedit) { switch (obedit->type) { case OB_MESH: return CTX_MODE_EDIT_MESH; case OB_CURVES_LEGACY: return CTX_MODE_EDIT_CURVE; case OB_SURF: return CTX_MODE_EDIT_SURFACE; case OB_FONT: return CTX_MODE_EDIT_TEXT; case OB_ARMATURE: return CTX_MODE_EDIT_ARMATURE; case OB_MBALL: return CTX_MODE_EDIT_METABALL; case OB_LATTICE: return CTX_MODE_EDIT_LATTICE; case OB_CURVES: return CTX_MODE_EDIT_CURVES; case OB_GREASE_PENCIL: return CTX_MODE_EDIT_GREASE_PENCIL; case OB_POINTCLOUD: return CTX_MODE_EDIT_POINTCLOUD; } } else { // Object *ob = CTX_data_active_object(C); if (ob) { if (object_mode & OB_MODE_POSE) { return CTX_MODE_POSE; } if (object_mode & OB_MODE_SCULPT) { return CTX_MODE_SCULPT; } if (object_mode & OB_MODE_WEIGHT_PAINT) { return CTX_MODE_PAINT_WEIGHT; } if (object_mode & OB_MODE_VERTEX_PAINT) { return CTX_MODE_PAINT_VERTEX; } if (object_mode & OB_MODE_TEXTURE_PAINT) { return CTX_MODE_PAINT_TEXTURE; } if (object_mode & OB_MODE_PARTICLE_EDIT) { return CTX_MODE_PARTICLE; } if (object_mode & OB_MODE_PAINT_GREASE_PENCIL) { if (ob->type == OB_GREASE_PENCIL) { return CTX_MODE_PAINT_GREASE_PENCIL; } } if (object_mode & OB_MODE_EDIT_GPENCIL_LEGACY) { return CTX_MODE_EDIT_GPENCIL_LEGACY; } if (object_mode & OB_MODE_SCULPT_GREASE_PENCIL) { if (ob->type == OB_GREASE_PENCIL) { return CTX_MODE_SCULPT_GREASE_PENCIL; } } if (object_mode & OB_MODE_WEIGHT_GREASE_PENCIL) { if (ob->type == OB_GREASE_PENCIL) { return CTX_MODE_WEIGHT_GREASE_PENCIL; } } if (object_mode & OB_MODE_VERTEX_GREASE_PENCIL) { if (ob->type == OB_GREASE_PENCIL) { return CTX_MODE_VERTEX_GREASE_PENCIL; } } if (object_mode & OB_MODE_SCULPT_CURVES) { return CTX_MODE_SCULPT_CURVES; } } } return CTX_MODE_OBJECT; } enum eContextObjectMode CTX_data_mode_enum(const bContext *C) { Object *obedit = CTX_data_edit_object(C); Object *obact = obedit ? nullptr : CTX_data_active_object(C); return CTX_data_mode_enum_ex(obedit, obact, obact ? eObjectMode(obact->mode) : OB_MODE_OBJECT); } /** * Would prefer if we can use the enum version below over this one - Campbell. * * \note Must be aligned with above enum. */ static const char *data_mode_strings[] = { "mesh_edit", "curve_edit", "surface_edit", "text_edit", "armature_edit", "mball_edit", "lattice_edit", "curves_edit", "grease_pencil_edit", "pointcloud_edit", "posemode", "sculpt_mode", "weightpaint", "vertexpaint", "imagepaint", "particlemode", "objectmode", "greasepencil_paint", "greasepencil_edit", "greasepencil_sculpt", "greasepencil_weight", "greasepencil_vertex", "curves_sculpt", "grease_pencil_paint", "grease_pencil_sculpt", "grease_pencil_weight", "grease_pencil_vertex", nullptr, }; BLI_STATIC_ASSERT(ARRAY_SIZE(data_mode_strings) == CTX_MODE_NUM + 1, "Must have a string for each context mode") const char *CTX_data_mode_string(const bContext *C) { return data_mode_strings[CTX_data_mode_enum(C)]; } void CTX_data_scene_set(bContext *C, Scene *scene) { C->data.scene = scene; #ifdef WITH_PYTHON if (C->data.py_context != nullptr) { const char *members[] = {"scene"}; BPY_context_dict_clear_members_array(&C->data.py_context, C->data.py_context_orig, members, 1); } #endif } ToolSettings *CTX_data_tool_settings(const bContext *C) { Scene *scene = CTX_data_scene(C); if (scene) { return scene->toolsettings; } return nullptr; } bool CTX_data_selected_ids(const bContext *C, blender::Vector *list) { return ctx_data_collection_get(C, "selected_ids", list); } bool CTX_data_selected_nodes(const bContext *C, blender::Vector *list) { return ctx_data_collection_get(C, "selected_nodes", list); } bool CTX_data_selected_editable_objects(const bContext *C, blender::Vector *list) { return ctx_data_collection_get(C, "selected_editable_objects", list); } bool CTX_data_selected_editable_bases(const bContext *C, blender::Vector *list) { return ctx_data_base_collection_get(C, "selected_editable_objects", list); } bool CTX_data_editable_objects(const bContext *C, blender::Vector *list) { return ctx_data_collection_get(C, "editable_objects", list); } bool CTX_data_editable_bases(const bContext *C, blender::Vector *list) { return ctx_data_base_collection_get(C, "editable_objects", list); } bool CTX_data_selected_objects(const bContext *C, blender::Vector *list) { return ctx_data_collection_get(C, "selected_objects", list); } bool CTX_data_selected_bases(const bContext *C, blender::Vector *list) { return ctx_data_base_collection_get(C, "selected_objects", list); } bool CTX_data_visible_objects(const bContext *C, blender::Vector *list) { return ctx_data_collection_get(C, "visible_objects", list); } bool CTX_data_visible_bases(const bContext *C, blender::Vector *list) { return ctx_data_base_collection_get(C, "visible_objects", list); } bool CTX_data_selectable_objects(const bContext *C, blender::Vector *list) { return ctx_data_collection_get(C, "selectable_objects", list); } bool CTX_data_selectable_bases(const bContext *C, blender::Vector *list) { return ctx_data_base_collection_get(C, "selectable_objects", list); } Object *CTX_data_active_object(const bContext *C) { return static_cast(ctx_data_pointer_get(C, "active_object")); } Base *CTX_data_active_base(const bContext *C) { Object *ob = CTX_data_active_object(C); if (ob == nullptr) { return nullptr; } const Scene *scene = CTX_data_scene(C); ViewLayer *view_layer = CTX_data_view_layer(C); BKE_view_layer_synced_ensure(scene, view_layer); return BKE_view_layer_base_find(view_layer, ob); } Object *CTX_data_edit_object(const bContext *C) { return static_cast(ctx_data_pointer_get(C, "edit_object")); } Image *CTX_data_edit_image(const bContext *C) { return static_cast(ctx_data_pointer_get(C, "edit_image")); } Text *CTX_data_edit_text(const bContext *C) { return static_cast(ctx_data_pointer_get(C, "edit_text")); } MovieClip *CTX_data_edit_movieclip(const bContext *C) { return static_cast(ctx_data_pointer_get(C, "edit_movieclip")); } Mask *CTX_data_edit_mask(const bContext *C) { return static_cast(ctx_data_pointer_get(C, "edit_mask")); } EditBone *CTX_data_active_bone(const bContext *C) { return static_cast(ctx_data_pointer_get(C, "active_bone")); } CacheFile *CTX_data_edit_cachefile(const bContext *C) { return static_cast(ctx_data_pointer_get(C, "edit_cachefile")); } bool CTX_data_selected_bones(const bContext *C, blender::Vector *list) { return ctx_data_collection_get(C, "selected_bones", list); } bool CTX_data_selected_editable_bones(const bContext *C, blender::Vector *list) { return ctx_data_collection_get(C, "selected_editable_bones", list); } bool CTX_data_visible_bones(const bContext *C, blender::Vector *list) { return ctx_data_collection_get(C, "visible_bones", list); } bool CTX_data_editable_bones(const bContext *C, blender::Vector *list) { return ctx_data_collection_get(C, "editable_bones", list); } bPoseChannel *CTX_data_active_pose_bone(const bContext *C) { return static_cast(ctx_data_pointer_get(C, "active_pose_bone")); } bool CTX_data_selected_pose_bones(const bContext *C, blender::Vector *list) { return ctx_data_collection_get(C, "selected_pose_bones", list); } bool CTX_data_selected_pose_bones_from_active_object(const bContext *C, blender::Vector *list) { return ctx_data_collection_get(C, "selected_pose_bones_from_active_object", list); } bool CTX_data_visible_pose_bones(const bContext *C, blender::Vector *list) { return ctx_data_collection_get(C, "visible_pose_bones", list); } const AssetLibraryReference *CTX_wm_asset_library_ref(const bContext *C) { return static_cast(ctx_data_pointer_get(C, "asset_library_reference")); } static AssetHandle ctx_wm_asset_handle(const bContext *C, bool *r_is_valid) { AssetHandle *asset_handle_p = (AssetHandle *)CTX_data_pointer_get_type(C, "asset_handle", &RNA_AssetHandle).data; if (asset_handle_p) { *r_is_valid = true; return *asset_handle_p; } /* If the asset handle was not found in context directly, try if there's an active file with * asset data there instead. Not nice to have this here, would be better to have this in * `ED_asset.hh`, but we can't include that in BKE. Even better would be not needing this at all * and being able to have editors return this in the usual `context` callback. But that would * require returning a non-owning pointer, which we don't have in the Asset Browser (yet). */ FileDirEntry *file = (FileDirEntry *)CTX_data_pointer_get_type(C, "active_file", &RNA_FileSelectEntry).data; if (file && file->asset) { *r_is_valid = true; return AssetHandle{file}; } *r_is_valid = false; return AssetHandle{nullptr}; } blender::asset_system::AssetRepresentation *CTX_wm_asset(const bContext *C) { if (auto *asset = static_cast( ctx_data_pointer_get(C, "asset"))) { return asset; } /* Expose the asset representation from the asset-handle. * TODO(Julian): #AssetHandle should be properly replaced by #AssetRepresentation. */ bool is_valid; if (AssetHandle handle = ctx_wm_asset_handle(C, &is_valid); is_valid) { return handle.file_data->asset; } return nullptr; } Depsgraph *CTX_data_depsgraph_pointer(const bContext *C) { Main *bmain = CTX_data_main(C); Scene *scene = CTX_data_scene(C); ViewLayer *view_layer = CTX_data_view_layer(C); Depsgraph *depsgraph = BKE_scene_ensure_depsgraph(bmain, scene, view_layer); /* Dependency graph might have been just allocated, and hence it will not be marked. * This confuses redo system due to the lack of flushing changes back to the original data. * In the future we would need to check whether the CTX_wm_window(C) is in editing mode (as an * opposite of playback-preview-only) and set active flag based on that. */ DEG_make_active(depsgraph); return depsgraph; } Depsgraph *CTX_data_expect_evaluated_depsgraph(const bContext *C) { Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C); /* TODO(sergey): Assert that the dependency graph is fully evaluated. * Note that first the depsgraph and scene post-evaluation hooks needs to run extra round of * updates first to make check here really reliable. */ return depsgraph; } Depsgraph *CTX_data_ensure_evaluated_depsgraph(const bContext *C) { Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C); Main *bmain = CTX_data_main(C); BKE_scene_graph_evaluated_ensure(depsgraph, bmain); return depsgraph; } Depsgraph *CTX_data_depsgraph_on_load(const bContext *C) { Scene *scene = CTX_data_scene(C); ViewLayer *view_layer = CTX_data_view_layer(C); return BKE_scene_get_depsgraph(scene, view_layer); }