/* SPDX-License-Identifier: GPL-2.0-or-later * Copyright 2008 Blender Foundation. All rights reserved. */ /** \file * \ingroup edscr */ #include #include #include #include "MEM_guardedalloc.h" #include "DNA_anim_types.h" #include "DNA_armature_types.h" #include "DNA_gpencil_types.h" #include "DNA_object_types.h" #include "DNA_scene_types.h" #include "DNA_screen_types.h" #include "DNA_sequence_types.h" #include "DNA_space_types.h" #include "DNA_windowmanager_types.h" #include "DNA_workspace_types.h" #include "BLI_ghash.h" #include "BLI_listbase.h" #include "BLI_utildefines.h" #include "BKE_action.h" #include "BKE_armature.h" #include "BKE_blender.h" #include "BKE_context.h" #include "BKE_gpencil.h" #include "BKE_layer.h" #include "BKE_object.h" #include "BKE_tracking.h" #include "RNA_access.h" #include "RNA_prototypes.h" #include "ED_anim_api.h" #include "ED_armature.h" #include "ED_clip.h" #include "ED_gpencil.h" #include "SEQ_channels.h" #include "SEQ_select.h" #include "SEQ_sequencer.h" #include "SEQ_transform.h" #include "UI_interface.h" #include "WM_api.h" #include "screen_intern.h" const char *screen_context_dir[] = { "scene", "view_layer", "visible_objects", "selectable_objects", "selected_objects", "editable_objects", "selected_editable_objects", "objects_in_mode", "objects_in_mode_unique_data", "visible_bones", "editable_bones", "selected_bones", "selected_editable_bones", "visible_pose_bones", "selected_pose_bones", "selected_pose_bones_from_active_object", "active_bone", "active_pose_bone", "active_object", "object", "edit_object", "sculpt_object", "vertex_paint_object", "weight_paint_object", "image_paint_object", "particle_edit_object", "pose_object", "active_sequence_strip", "sequences", "selected_sequences", "selected_editable_sequences", /* sequencer */ "active_nla_track", "active_nla_strip", "selected_nla_strips", /* nla editor */ "selected_movieclip_tracks", "gpencil_data", "gpencil_data_owner", /* grease pencil data */ "annotation_data", "annotation_data_owner", "visible_gpencil_layers", "editable_gpencil_layers", "editable_gpencil_strokes", "active_gpencil_layer", "active_gpencil_frame", "active_annotation_layer", "active_operator", "active_action", "selected_visible_actions", "selected_editable_actions", "visible_fcurves", "editable_fcurves", "selected_visible_fcurves", "selected_editable_fcurves", "active_editable_fcurve", "selected_editable_keyframes", "ui_list", "asset_library_ref", NULL, }; /* Each function `screen_ctx_XXX()` will be called when the screen context "XXX" is requested. * ensure_ed_screen_context_functions() is responsible for creating the hash map from context * member name to function. */ static eContextResult screen_ctx_scene(const bContext *C, bContextDataResult *result) { wmWindow *win = CTX_wm_window(C); Scene *scene = WM_window_get_active_scene(win); CTX_data_id_pointer_set(result, &scene->id); return CTX_RESULT_OK; } static eContextResult screen_ctx_visible_objects(const bContext *C, bContextDataResult *result) { wmWindow *win = CTX_wm_window(C); View3D *v3d = CTX_wm_view3d(C); /* This may be NULL in a lot of cases. */ ViewLayer *view_layer = WM_window_get_active_view_layer(win); LISTBASE_FOREACH (Base *, base, &view_layer->object_bases) { if (BASE_VISIBLE(v3d, base)) { CTX_data_id_list_add(result, &base->object->id); } } CTX_data_type_set(result, CTX_DATA_TYPE_COLLECTION); return CTX_RESULT_OK; } static eContextResult screen_ctx_selectable_objects(const bContext *C, bContextDataResult *result) { wmWindow *win = CTX_wm_window(C); View3D *v3d = CTX_wm_view3d(C); /* This may be NULL in a lot of cases. */ ViewLayer *view_layer = WM_window_get_active_view_layer(win); LISTBASE_FOREACH (Base *, base, &view_layer->object_bases) { if (BASE_SELECTABLE(v3d, base)) { CTX_data_id_list_add(result, &base->object->id); } } CTX_data_type_set(result, CTX_DATA_TYPE_COLLECTION); return CTX_RESULT_OK; } static eContextResult screen_ctx_selected_objects(const bContext *C, bContextDataResult *result) { wmWindow *win = CTX_wm_window(C); View3D *v3d = CTX_wm_view3d(C); /* This may be NULL in a lot of cases. */ ViewLayer *view_layer = WM_window_get_active_view_layer(win); LISTBASE_FOREACH (Base *, base, &view_layer->object_bases) { if (BASE_SELECTED(v3d, base)) { CTX_data_id_list_add(result, &base->object->id); } } CTX_data_type_set(result, CTX_DATA_TYPE_COLLECTION); return CTX_RESULT_OK; } static eContextResult screen_ctx_selected_editable_objects(const bContext *C, bContextDataResult *result) { wmWindow *win = CTX_wm_window(C); View3D *v3d = CTX_wm_view3d(C); /* This may be NULL in a lot of cases. */ ViewLayer *view_layer = WM_window_get_active_view_layer(win); LISTBASE_FOREACH (Base *, base, &view_layer->object_bases) { if (BASE_SELECTED_EDITABLE(v3d, base)) { CTX_data_id_list_add(result, &base->object->id); } } CTX_data_type_set(result, CTX_DATA_TYPE_COLLECTION); return CTX_RESULT_OK; } static eContextResult screen_ctx_editable_objects(const bContext *C, bContextDataResult *result) { wmWindow *win = CTX_wm_window(C); View3D *v3d = CTX_wm_view3d(C); /* This may be NULL in a lot of cases. */ ViewLayer *view_layer = WM_window_get_active_view_layer(win); /* Visible + Editable, but not necessarily selected */ LISTBASE_FOREACH (Base *, base, &view_layer->object_bases) { if (BASE_EDITABLE(v3d, base)) { CTX_data_id_list_add(result, &base->object->id); } } CTX_data_type_set(result, CTX_DATA_TYPE_COLLECTION); return CTX_RESULT_OK; } static eContextResult screen_ctx_objects_in_mode(const bContext *C, bContextDataResult *result) { wmWindow *win = CTX_wm_window(C); View3D *v3d = CTX_wm_view3d(C); /* This may be NULL in a lot of cases. */ ViewLayer *view_layer = WM_window_get_active_view_layer(win); Object *obact = view_layer->basact ? view_layer->basact->object : NULL; if (obact && (obact->mode != OB_MODE_OBJECT)) { FOREACH_OBJECT_IN_MODE_BEGIN (view_layer, v3d, obact->type, obact->mode, ob_iter) { CTX_data_id_list_add(result, &ob_iter->id); } FOREACH_OBJECT_IN_MODE_END; } CTX_data_type_set(result, CTX_DATA_TYPE_COLLECTION); return CTX_RESULT_OK; } static eContextResult screen_ctx_objects_in_mode_unique_data(const bContext *C, bContextDataResult *result) { wmWindow *win = CTX_wm_window(C); View3D *v3d = CTX_wm_view3d(C); /* This may be NULL in a lot of cases. */ ViewLayer *view_layer = WM_window_get_active_view_layer(win); Object *obact = view_layer->basact ? view_layer->basact->object : NULL; if (obact && (obact->mode != OB_MODE_OBJECT)) { FOREACH_OBJECT_IN_MODE_BEGIN (view_layer, v3d, obact->type, obact->mode, ob_iter) { ob_iter->id.tag |= LIB_TAG_DOIT; } FOREACH_OBJECT_IN_MODE_END; FOREACH_OBJECT_IN_MODE_BEGIN (view_layer, v3d, obact->type, obact->mode, ob_iter) { if (ob_iter->id.tag & LIB_TAG_DOIT) { ob_iter->id.tag &= ~LIB_TAG_DOIT; CTX_data_id_list_add(result, &ob_iter->id); } } FOREACH_OBJECT_IN_MODE_END; } CTX_data_type_set(result, CTX_DATA_TYPE_COLLECTION); return CTX_RESULT_OK; } static eContextResult screen_ctx_visible_or_editable_bones_(const bContext *C, bContextDataResult *result, const bool editable_bones) { wmWindow *win = CTX_wm_window(C); ViewLayer *view_layer = WM_window_get_active_view_layer(win); Object *obedit = OBEDIT_FROM_VIEW_LAYER(view_layer); bArmature *arm = (obedit && obedit->type == OB_ARMATURE) ? obedit->data : NULL; EditBone *flipbone = NULL; if (arm && arm->edbo) { uint objects_len; Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data( view_layer, CTX_wm_view3d(C), &objects_len); for (uint i = 0; i < objects_len; i++) { Object *ob = objects[i]; arm = ob->data; /* Attention: X-Axis Mirroring is also handled here... */ LISTBASE_FOREACH (EditBone *, ebone, arm->edbo) { /* first and foremost, bone must be visible and selected */ if (EBONE_VISIBLE(arm, ebone)) { /* Get 'x-axis mirror equivalent' bone if the X-Axis Mirroring option is enabled * so that most users of this data don't need to explicitly check for it themselves. * * We need to make sure that these mirrored copies are not selected, otherwise some * bones will be operated on twice. */ if (arm->flag & ARM_MIRROR_EDIT) { flipbone = ED_armature_ebone_get_mirrored(arm->edbo, ebone); } /* if we're filtering for editable too, use the check for that instead, * as it has selection check too */ if (editable_bones) { /* only selected + editable */ if (EBONE_EDITABLE(ebone)) { CTX_data_list_add(result, &arm->id, &RNA_EditBone, ebone); if ((flipbone) && !(flipbone->flag & BONE_SELECTED)) { CTX_data_list_add(result, &arm->id, &RNA_EditBone, flipbone); } } } else { /* only include bones if visible */ CTX_data_list_add(result, &arm->id, &RNA_EditBone, ebone); if ((flipbone) && EBONE_VISIBLE(arm, flipbone) == 0) { CTX_data_list_add(result, &arm->id, &RNA_EditBone, flipbone); } } } } } MEM_freeN(objects); CTX_data_type_set(result, CTX_DATA_TYPE_COLLECTION); return CTX_RESULT_OK; } return CTX_RESULT_NO_DATA; } static eContextResult screen_ctx_visible_bones(const bContext *C, bContextDataResult *result) { return screen_ctx_visible_or_editable_bones_(C, result, false); } static eContextResult screen_ctx_editable_bones(const bContext *C, bContextDataResult *result) { return screen_ctx_visible_or_editable_bones_(C, result, true); } static eContextResult screen_ctx_selected_bones_(const bContext *C, bContextDataResult *result, const bool selected_editable_bones) { wmWindow *win = CTX_wm_window(C); ViewLayer *view_layer = WM_window_get_active_view_layer(win); Object *obedit = OBEDIT_FROM_VIEW_LAYER(view_layer); bArmature *arm = (obedit && obedit->type == OB_ARMATURE) ? obedit->data : NULL; EditBone *flipbone = NULL; if (arm && arm->edbo) { uint objects_len; Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data( view_layer, CTX_wm_view3d(C), &objects_len); for (uint i = 0; i < objects_len; i++) { Object *ob = objects[i]; arm = ob->data; /* Attention: X-Axis Mirroring is also handled here... */ LISTBASE_FOREACH (EditBone *, ebone, arm->edbo) { /* first and foremost, bone must be visible and selected */ if (EBONE_VISIBLE(arm, ebone) && (ebone->flag & BONE_SELECTED)) { /* Get 'x-axis mirror equivalent' bone if the X-Axis Mirroring option is enabled * so that most users of this data don't need to explicitly check for it themselves. * * We need to make sure that these mirrored copies are not selected, otherwise some * bones will be operated on twice. */ if (arm->flag & ARM_MIRROR_EDIT) { flipbone = ED_armature_ebone_get_mirrored(arm->edbo, ebone); } /* if we're filtering for editable too, use the check for that instead, * as it has selection check too */ if (selected_editable_bones) { /* only selected + editable */ if (EBONE_EDITABLE(ebone)) { CTX_data_list_add(result, &arm->id, &RNA_EditBone, ebone); if ((flipbone) && !(flipbone->flag & BONE_SELECTED)) { CTX_data_list_add(result, &arm->id, &RNA_EditBone, flipbone); } } } else { /* only include bones if selected */ CTX_data_list_add(result, &arm->id, &RNA_EditBone, ebone); if ((flipbone) && !(flipbone->flag & BONE_SELECTED)) { CTX_data_list_add(result, &arm->id, &RNA_EditBone, flipbone); } } } } } MEM_freeN(objects); CTX_data_type_set(result, CTX_DATA_TYPE_COLLECTION); return CTX_RESULT_OK; } return CTX_RESULT_NO_DATA; } static eContextResult screen_ctx_selected_bones(const bContext *C, bContextDataResult *result) { return screen_ctx_selected_bones_(C, result, false); } static eContextResult screen_ctx_selected_editable_bones(const bContext *C, bContextDataResult *result) { return screen_ctx_selected_bones_(C, result, true); } static eContextResult screen_ctx_visible_pose_bones(const bContext *C, bContextDataResult *result) { wmWindow *win = CTX_wm_window(C); View3D *v3d = CTX_wm_view3d(C); /* This may be NULL in a lot of cases. */ ViewLayer *view_layer = WM_window_get_active_view_layer(win); Object *obact = view_layer->basact ? view_layer->basact->object : NULL; Object *obpose = BKE_object_pose_armature_get(obact); if (obpose && obpose->pose && obpose->data) { if (obpose != obact) { FOREACH_PCHAN_VISIBLE_IN_OBJECT_BEGIN (obpose, pchan) { CTX_data_list_add(result, &obpose->id, &RNA_PoseBone, pchan); } FOREACH_PCHAN_SELECTED_IN_OBJECT_END; } else if (obact->mode & OB_MODE_POSE) { FOREACH_OBJECT_IN_MODE_BEGIN (view_layer, v3d, OB_ARMATURE, OB_MODE_POSE, ob_iter) { FOREACH_PCHAN_VISIBLE_IN_OBJECT_BEGIN (ob_iter, pchan) { CTX_data_list_add(result, &ob_iter->id, &RNA_PoseBone, pchan); } FOREACH_PCHAN_VISIBLE_IN_OBJECT_END; } FOREACH_OBJECT_IN_MODE_END; } CTX_data_type_set(result, CTX_DATA_TYPE_COLLECTION); return CTX_RESULT_OK; } return CTX_RESULT_NO_DATA; } static eContextResult screen_ctx_selected_pose_bones(const bContext *C, bContextDataResult *result) { wmWindow *win = CTX_wm_window(C); View3D *v3d = CTX_wm_view3d(C); /* This may be NULL in a lot of cases. */ ViewLayer *view_layer = WM_window_get_active_view_layer(win); Object *obact = view_layer->basact ? view_layer->basact->object : NULL; Object *obpose = BKE_object_pose_armature_get(obact); if (obpose && obpose->pose && obpose->data) { if (obpose != obact) { FOREACH_PCHAN_SELECTED_IN_OBJECT_BEGIN (obpose, pchan) { CTX_data_list_add(result, &obpose->id, &RNA_PoseBone, pchan); } FOREACH_PCHAN_SELECTED_IN_OBJECT_END; } else if (obact->mode & OB_MODE_POSE) { FOREACH_OBJECT_IN_MODE_BEGIN (view_layer, v3d, OB_ARMATURE, OB_MODE_POSE, ob_iter) { FOREACH_PCHAN_SELECTED_IN_OBJECT_BEGIN (ob_iter, pchan) { CTX_data_list_add(result, &ob_iter->id, &RNA_PoseBone, pchan); } FOREACH_PCHAN_SELECTED_IN_OBJECT_END; } FOREACH_OBJECT_IN_MODE_END; } CTX_data_type_set(result, CTX_DATA_TYPE_COLLECTION); return CTX_RESULT_OK; } return CTX_RESULT_NO_DATA; } static eContextResult screen_ctx_selected_pose_bones_from_active_object(const bContext *C, bContextDataResult *result) { wmWindow *win = CTX_wm_window(C); ViewLayer *view_layer = WM_window_get_active_view_layer(win); Object *obact = view_layer->basact ? view_layer->basact->object : NULL; Object *obpose = BKE_object_pose_armature_get(obact); if (obpose && obpose->pose && obpose->data) { if (obpose != obact) { FOREACH_PCHAN_SELECTED_IN_OBJECT_BEGIN (obpose, pchan) { CTX_data_list_add(result, &obpose->id, &RNA_PoseBone, pchan); } FOREACH_PCHAN_SELECTED_IN_OBJECT_END; } else if (obact->mode & OB_MODE_POSE) { FOREACH_PCHAN_SELECTED_IN_OBJECT_BEGIN (obact, pchan) { CTX_data_list_add(result, &obact->id, &RNA_PoseBone, pchan); } FOREACH_PCHAN_SELECTED_IN_OBJECT_END; } CTX_data_type_set(result, CTX_DATA_TYPE_COLLECTION); return CTX_RESULT_OK; } return CTX_RESULT_NO_DATA; } static eContextResult screen_ctx_active_bone(const bContext *C, bContextDataResult *result) { wmWindow *win = CTX_wm_window(C); ViewLayer *view_layer = WM_window_get_active_view_layer(win); Object *obact = view_layer->basact ? view_layer->basact->object : NULL; if (obact && obact->type == OB_ARMATURE) { bArmature *arm = obact->data; if (arm->edbo) { if (arm->act_edbone) { CTX_data_pointer_set(result, &arm->id, &RNA_EditBone, arm->act_edbone); return CTX_RESULT_OK; } } else { if (arm->act_bone) { CTX_data_pointer_set(result, &arm->id, &RNA_Bone, arm->act_bone); return CTX_RESULT_OK; } } } return CTX_RESULT_NO_DATA; } static eContextResult screen_ctx_active_pose_bone(const bContext *C, bContextDataResult *result) { wmWindow *win = CTX_wm_window(C); ViewLayer *view_layer = WM_window_get_active_view_layer(win); Object *obact = view_layer->basact ? view_layer->basact->object : NULL; Object *obpose = BKE_object_pose_armature_get(obact); bPoseChannel *pchan = BKE_pose_channel_active_if_layer_visible(obpose); if (pchan) { CTX_data_pointer_set(result, &obpose->id, &RNA_PoseBone, pchan); return CTX_RESULT_OK; } return CTX_RESULT_NO_DATA; } static eContextResult screen_ctx_active_object(const bContext *C, bContextDataResult *result) { wmWindow *win = CTX_wm_window(C); ViewLayer *view_layer = WM_window_get_active_view_layer(win); Object *obact = view_layer->basact ? view_layer->basact->object : NULL; if (obact) { CTX_data_id_pointer_set(result, &obact->id); } return CTX_RESULT_OK; } static eContextResult screen_ctx_object(const bContext *C, bContextDataResult *result) { wmWindow *win = CTX_wm_window(C); ViewLayer *view_layer = WM_window_get_active_view_layer(win); Object *obact = view_layer->basact ? view_layer->basact->object : NULL; if (obact) { CTX_data_id_pointer_set(result, &obact->id); } return CTX_RESULT_OK; } static eContextResult screen_ctx_edit_object(const bContext *C, bContextDataResult *result) { wmWindow *win = CTX_wm_window(C); ViewLayer *view_layer = WM_window_get_active_view_layer(win); Object *obedit = OBEDIT_FROM_VIEW_LAYER(view_layer); /* convenience for now, 1 object per scene in editmode */ if (obedit) { CTX_data_id_pointer_set(result, &obedit->id); } return CTX_RESULT_OK; } static eContextResult screen_ctx_sculpt_object(const bContext *C, bContextDataResult *result) { wmWindow *win = CTX_wm_window(C); ViewLayer *view_layer = WM_window_get_active_view_layer(win); Object *obact = view_layer->basact ? view_layer->basact->object : NULL; if (obact && (obact->mode & OB_MODE_SCULPT)) { CTX_data_id_pointer_set(result, &obact->id); } return CTX_RESULT_OK; } static eContextResult screen_ctx_vertex_paint_object(const bContext *C, bContextDataResult *result) { wmWindow *win = CTX_wm_window(C); ViewLayer *view_layer = WM_window_get_active_view_layer(win); Object *obact = view_layer->basact ? view_layer->basact->object : NULL; if (obact && (obact->mode & OB_MODE_VERTEX_PAINT)) { CTX_data_id_pointer_set(result, &obact->id); } return CTX_RESULT_OK; } static eContextResult screen_ctx_weight_paint_object(const bContext *C, bContextDataResult *result) { wmWindow *win = CTX_wm_window(C); ViewLayer *view_layer = WM_window_get_active_view_layer(win); Object *obact = view_layer->basact ? view_layer->basact->object : NULL; if (obact && (obact->mode & OB_MODE_ALL_WEIGHT_PAINT)) { CTX_data_id_pointer_set(result, &obact->id); } return CTX_RESULT_OK; } static eContextResult screen_ctx_image_paint_object(const bContext *C, bContextDataResult *result) { wmWindow *win = CTX_wm_window(C); ViewLayer *view_layer = WM_window_get_active_view_layer(win); Object *obact = view_layer->basact ? view_layer->basact->object : NULL; if (obact && (obact->mode & OB_MODE_TEXTURE_PAINT)) { CTX_data_id_pointer_set(result, &obact->id); } return CTX_RESULT_OK; } static eContextResult screen_ctx_particle_edit_object(const bContext *C, bContextDataResult *result) { wmWindow *win = CTX_wm_window(C); ViewLayer *view_layer = WM_window_get_active_view_layer(win); Object *obact = view_layer->basact ? view_layer->basact->object : NULL; if (obact && (obact->mode & OB_MODE_PARTICLE_EDIT)) { CTX_data_id_pointer_set(result, &obact->id); } return CTX_RESULT_OK; } static eContextResult screen_ctx_pose_object(const bContext *C, bContextDataResult *result) { wmWindow *win = CTX_wm_window(C); ViewLayer *view_layer = WM_window_get_active_view_layer(win); Object *obact = view_layer->basact ? view_layer->basact->object : NULL; Object *obpose = BKE_object_pose_armature_get(obact); if (obpose) { CTX_data_id_pointer_set(result, &obpose->id); } return CTX_RESULT_OK; } static eContextResult screen_ctx_active_sequence_strip(const bContext *C, bContextDataResult *result) { wmWindow *win = CTX_wm_window(C); Scene *scene = WM_window_get_active_scene(win); Sequence *seq = SEQ_select_active_get(scene); if (seq) { CTX_data_pointer_set(result, &scene->id, &RNA_Sequence, seq); return CTX_RESULT_OK; } return CTX_RESULT_NO_DATA; } static eContextResult screen_ctx_sequences(const bContext *C, bContextDataResult *result) { wmWindow *win = CTX_wm_window(C); Scene *scene = WM_window_get_active_scene(win); Editing *ed = SEQ_editing_get(scene); if (ed) { LISTBASE_FOREACH (Sequence *, seq, ed->seqbasep) { CTX_data_list_add(result, &scene->id, &RNA_Sequence, seq); } CTX_data_type_set(result, CTX_DATA_TYPE_COLLECTION); return CTX_RESULT_OK; } return CTX_RESULT_NO_DATA; } static eContextResult screen_ctx_selected_sequences(const bContext *C, bContextDataResult *result) { wmWindow *win = CTX_wm_window(C); Scene *scene = WM_window_get_active_scene(win); Editing *ed = SEQ_editing_get(scene); if (ed) { LISTBASE_FOREACH (Sequence *, seq, ed->seqbasep) { if (seq->flag & SELECT) { CTX_data_list_add(result, &scene->id, &RNA_Sequence, seq); } } CTX_data_type_set(result, CTX_DATA_TYPE_COLLECTION); return CTX_RESULT_OK; } return CTX_RESULT_NO_DATA; } static eContextResult screen_ctx_selected_editable_sequences(const bContext *C, bContextDataResult *result) { wmWindow *win = CTX_wm_window(C); Scene *scene = WM_window_get_active_scene(win); Editing *ed = SEQ_editing_get(scene); if (ed == NULL) { return CTX_RESULT_NO_DATA; } ListBase *channels = SEQ_channels_displayed_get(ed); LISTBASE_FOREACH (Sequence *, seq, ed->seqbasep) { if (seq->flag & SELECT && !SEQ_transform_is_locked(channels, seq)) { CTX_data_list_add(result, &scene->id, &RNA_Sequence, seq); } } CTX_data_type_set(result, CTX_DATA_TYPE_COLLECTION); return CTX_RESULT_OK; } static eContextResult screen_ctx_active_nla_track(const bContext *C, bContextDataResult *result) { PointerRNA ptr; if (ANIM_nla_context_track_ptr(C, &ptr)) { CTX_data_pointer_set_ptr(result, &ptr); return CTX_RESULT_OK; } return CTX_RESULT_NO_DATA; } static eContextResult screen_ctx_active_nla_strip(const bContext *C, bContextDataResult *result) { PointerRNA ptr; if (ANIM_nla_context_strip_ptr(C, &ptr)) { CTX_data_pointer_set_ptr(result, &ptr); return CTX_RESULT_OK; } return CTX_RESULT_NO_DATA; } static eContextResult screen_ctx_selected_nla_strips(const bContext *C, bContextDataResult *result) { bAnimContext ac; if (ANIM_animdata_get_context(C, &ac) != 0) { ListBase anim_data = {NULL, NULL}; ANIM_animdata_filter(&ac, &anim_data, ANIMFILTER_DATA_VISIBLE, ac.data, ac.datatype); LISTBASE_FOREACH (bAnimListElem *, ale, &anim_data) { if (ale->datatype != ALE_NLASTRIP) { continue; } NlaTrack *nlt = (NlaTrack *)ale->data; LISTBASE_FOREACH (NlaStrip *, strip, &nlt->strips) { if (strip->flag & NLASTRIP_FLAG_SELECT) { CTX_data_list_add(result, ale->id, &RNA_NlaStrip, strip); } } } ANIM_animdata_freelist(&anim_data); CTX_data_type_set(result, CTX_DATA_TYPE_COLLECTION); return CTX_RESULT_OK; } return CTX_RESULT_NO_DATA; } static eContextResult screen_ctx_selected_movieclip_tracks(const bContext *C, bContextDataResult *result) { SpaceClip *space_clip = CTX_wm_space_clip(C); if (space_clip == NULL) { return CTX_RESULT_NO_DATA; } MovieClip *clip = ED_space_clip_get_clip(space_clip); if (clip == NULL) { return CTX_RESULT_NO_DATA; } MovieTracking *tracking = &clip->tracking; if (tracking == NULL) { return CTX_RESULT_NO_DATA; } ListBase *tracks_list = BKE_tracking_get_active_tracks(tracking); LISTBASE_FOREACH (MovieTrackingTrack *, track, tracks_list) { if (!TRACK_SELECTED(track)) { continue; } CTX_data_list_add(result, &clip->id, &RNA_MovieTrackingTrack, track); } CTX_data_type_set(result, CTX_DATA_TYPE_COLLECTION); return CTX_RESULT_OK; } static eContextResult screen_ctx_gpencil_data(const bContext *C, bContextDataResult *result) { wmWindow *win = CTX_wm_window(C); ScrArea *area = CTX_wm_area(C); ViewLayer *view_layer = WM_window_get_active_view_layer(win); Object *obact = view_layer->basact ? view_layer->basact->object : NULL; /* FIXME: for some reason, CTX_data_active_object(C) returns NULL when called from these * situations (as outlined above - see Campbell's #ifdefs). * That causes the get_active function to fail when called from context. * For that reason, we end up using an alternative where we pass everything in! */ bGPdata *gpd = ED_gpencil_data_get_active_direct(area, obact); if (gpd) { CTX_data_id_pointer_set(result, &gpd->id); return CTX_RESULT_OK; } return CTX_RESULT_NO_DATA; } static eContextResult screen_ctx_gpencil_data_owner(const bContext *C, bContextDataResult *result) { wmWindow *win = CTX_wm_window(C); ScrArea *area = CTX_wm_area(C); ViewLayer *view_layer = WM_window_get_active_view_layer(win); Object *obact = view_layer->basact ? view_layer->basact->object : NULL; /* Pointer to which data/datablock owns the reference to the Grease Pencil data being used * (as gpencil_data). */ PointerRNA ptr; bGPdata **gpd_ptr = ED_gpencil_data_get_pointers_direct(area, obact, &ptr); if (gpd_ptr) { CTX_data_pointer_set_ptr(result, &ptr); return CTX_RESULT_OK; } return CTX_RESULT_NO_DATA; } static eContextResult screen_ctx_annotation_data(const bContext *C, bContextDataResult *result) { wmWindow *win = CTX_wm_window(C); bScreen *screen = CTX_wm_screen(C); ScrArea *area = CTX_wm_area(C); Scene *scene = WM_window_get_active_scene(win); bGPdata *gpd = ED_annotation_data_get_active_direct((ID *)screen, area, scene); if (gpd) { CTX_data_id_pointer_set(result, &gpd->id); return CTX_RESULT_OK; } return CTX_RESULT_NO_DATA; } static eContextResult screen_ctx_annotation_data_owner(const bContext *C, bContextDataResult *result) { wmWindow *win = CTX_wm_window(C); bScreen *screen = CTX_wm_screen(C); ScrArea *area = CTX_wm_area(C); Scene *scene = WM_window_get_active_scene(win); /* Pointer to which data/datablock owns the reference to the Grease Pencil data being used. */ PointerRNA ptr; bGPdata **gpd_ptr = ED_annotation_data_get_pointers_direct((ID *)screen, area, scene, &ptr); if (gpd_ptr) { CTX_data_pointer_set_ptr(result, &ptr); return CTX_RESULT_OK; } return CTX_RESULT_NO_DATA; } static eContextResult screen_ctx_active_gpencil_layer(const bContext *C, bContextDataResult *result) { wmWindow *win = CTX_wm_window(C); ScrArea *area = CTX_wm_area(C); ViewLayer *view_layer = WM_window_get_active_view_layer(win); Object *obact = view_layer->basact ? view_layer->basact->object : NULL; bGPdata *gpd = ED_gpencil_data_get_active_direct(area, obact); if (gpd) { bGPDlayer *gpl = BKE_gpencil_layer_active_get(gpd); if (gpl) { CTX_data_pointer_set(result, &gpd->id, &RNA_GPencilLayer, gpl); return CTX_RESULT_OK; } } return CTX_RESULT_NO_DATA; } static eContextResult screen_ctx_active_annotation_layer(const bContext *C, bContextDataResult *result) { wmWindow *win = CTX_wm_window(C); bScreen *screen = CTX_wm_screen(C); ScrArea *area = CTX_wm_area(C); Scene *scene = WM_window_get_active_scene(win); bGPdata *gpd = ED_annotation_data_get_active_direct((ID *)screen, area, scene); if (gpd) { bGPDlayer *gpl = BKE_gpencil_layer_active_get(gpd); if (gpl) { CTX_data_pointer_set(result, &gpd->id, &RNA_GPencilLayer, gpl); return CTX_RESULT_OK; } } return CTX_RESULT_NO_DATA; } static eContextResult screen_ctx_active_gpencil_frame(const bContext *C, bContextDataResult *result) { wmWindow *win = CTX_wm_window(C); ScrArea *area = CTX_wm_area(C); ViewLayer *view_layer = WM_window_get_active_view_layer(win); Object *obact = view_layer->basact ? view_layer->basact->object : NULL; bGPdata *gpd = ED_gpencil_data_get_active_direct(area, obact); if (gpd) { bGPDlayer *gpl = BKE_gpencil_layer_active_get(gpd); if (gpl) { CTX_data_pointer_set(result, &gpd->id, &RNA_GPencilLayer, gpl->actframe); return CTX_RESULT_OK; } } return CTX_RESULT_NO_DATA; } static eContextResult screen_ctx_visible_gpencil_layers(const bContext *C, bContextDataResult *result) { wmWindow *win = CTX_wm_window(C); ScrArea *area = CTX_wm_area(C); ViewLayer *view_layer = WM_window_get_active_view_layer(win); Object *obact = view_layer->basact ? view_layer->basact->object : NULL; bGPdata *gpd = ED_gpencil_data_get_active_direct(area, obact); if (gpd) { LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) { if ((gpl->flag & GP_LAYER_HIDE) == 0) { CTX_data_list_add(result, &gpd->id, &RNA_GPencilLayer, gpl); } } CTX_data_type_set(result, CTX_DATA_TYPE_COLLECTION); return CTX_RESULT_OK; } return CTX_RESULT_NO_DATA; } static eContextResult screen_ctx_editable_gpencil_layers(const bContext *C, bContextDataResult *result) { wmWindow *win = CTX_wm_window(C); ScrArea *area = CTX_wm_area(C); ViewLayer *view_layer = WM_window_get_active_view_layer(win); Object *obact = view_layer->basact ? view_layer->basact->object : NULL; bGPdata *gpd = ED_gpencil_data_get_active_direct(area, obact); if (gpd) { LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) { if (BKE_gpencil_layer_is_editable(gpl)) { CTX_data_list_add(result, &gpd->id, &RNA_GPencilLayer, gpl); } } CTX_data_type_set(result, CTX_DATA_TYPE_COLLECTION); return CTX_RESULT_OK; } return CTX_RESULT_NO_DATA; } static eContextResult screen_ctx_editable_gpencil_strokes(const bContext *C, bContextDataResult *result) { wmWindow *win = CTX_wm_window(C); ScrArea *area = CTX_wm_area(C); ViewLayer *view_layer = WM_window_get_active_view_layer(win); Object *obact = view_layer->basact ? view_layer->basact->object : NULL; bGPdata *gpd = ED_gpencil_data_get_active_direct(area, obact); const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd); if (gpd == NULL) { return CTX_RESULT_NO_DATA; } LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) { if (BKE_gpencil_layer_is_editable(gpl) && (gpl->actframe)) { bGPDframe *gpf; bGPDframe *init_gpf = gpl->actframe; if (is_multiedit) { init_gpf = gpl->frames.first; } for (gpf = init_gpf; gpf; gpf = gpf->next) { if ((gpf == gpl->actframe) || ((gpf->flag & GP_FRAME_SELECT) && (is_multiedit))) { LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) { if (ED_gpencil_stroke_can_use_direct(area, gps)) { /* check if the color is editable */ if (ED_gpencil_stroke_material_editable(obact, gpl, gps) == false) { continue; } CTX_data_list_add(result, &gpd->id, &RNA_GPencilStroke, gps); } } } /* If not multi-edit out of loop. */ if (!is_multiedit) { break; } } } } CTX_data_type_set(result, CTX_DATA_TYPE_COLLECTION); return CTX_RESULT_OK; } static eContextResult screen_ctx_active_operator(const bContext *C, bContextDataResult *result) { wmOperator *op = NULL; SpaceFile *sfile = CTX_wm_space_file(C); if (sfile) { op = sfile->op; } else if ((op = UI_context_active_operator_get(C))) { /* do nothing */ } else { /* NOTE: this checks poll, could be a problem, but this also * happens for the toolbar */ op = WM_operator_last_redo(C); } /* TODO: get the operator from popup's. */ if (op && op->ptr) { CTX_data_pointer_set(result, NULL, &RNA_Operator, op); return CTX_RESULT_OK; } return CTX_RESULT_NO_DATA; } static eContextResult screen_ctx_sel_actions_impl(const bContext *C, bContextDataResult *result, bool active_only, bool editable) { bAnimContext ac; if (ANIM_animdata_get_context(C, &ac) && ELEM(ac.spacetype, SPACE_ACTION, SPACE_GRAPH)) { /* In the Action and Shape Key editor always use the action field at the top. */ if (ac.spacetype == SPACE_ACTION) { SpaceAction *saction = (SpaceAction *)ac.sl; if (ELEM(saction->mode, SACTCONT_ACTION, SACTCONT_SHAPEKEY)) { if (active_only) { CTX_data_id_pointer_set(result, (ID *)saction->action); } else { if (saction->action && !(editable && ID_IS_LINKED(saction->action))) { CTX_data_id_list_add(result, &saction->action->id); } CTX_data_type_set(result, CTX_DATA_TYPE_COLLECTION); } return CTX_RESULT_OK; } } /* Search for selected animation data items. */ ListBase anim_data = {NULL, NULL}; int filter = ANIMFILTER_DATA_VISIBLE; bool check_selected = false; switch (ac.spacetype) { case SPACE_GRAPH: filter |= ANIMFILTER_FCURVESONLY | ANIMFILTER_CURVE_VISIBLE | (active_only ? ANIMFILTER_ACTIVE : ANIMFILTER_SEL); break; case SPACE_ACTION: filter |= ANIMFILTER_LIST_VISIBLE | ANIMFILTER_LIST_CHANNELS; check_selected = true; break; } ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype); GSet *seen_set = active_only ? NULL : BLI_gset_ptr_new("seen actions"); LISTBASE_FOREACH (bAnimListElem *, ale, &anim_data) { /* In dopesheet check selection status of individual items, skipping * if not selected or has no selection flag. This is needed so that * selecting action or group rows without any channels works. */ if (check_selected && ANIM_channel_setting_get(&ac, ale, ACHANNEL_SETTING_SELECT) <= 0) { continue; } bAction *action = ANIM_channel_action_get(ale); if (action) { if (active_only) { CTX_data_id_pointer_set(result, (ID *)action); break; } else { if (editable && ID_IS_LINKED(action)) { continue; } /* Add the action to the output list if not already added. */ if (BLI_gset_add(seen_set, action)) { CTX_data_id_list_add(result, &action->id); } } } } ANIM_animdata_freelist(&anim_data); if (!active_only) { BLI_gset_free(seen_set, NULL); CTX_data_type_set(result, CTX_DATA_TYPE_COLLECTION); } return CTX_RESULT_OK; } return CTX_RESULT_NO_DATA; } static eContextResult screen_ctx_active_action(const bContext *C, bContextDataResult *result) { return screen_ctx_sel_actions_impl(C, result, true, false); } static eContextResult screen_ctx_selected_visible_actions(const bContext *C, bContextDataResult *result) { return screen_ctx_sel_actions_impl(C, result, false, false); } static eContextResult screen_ctx_selected_editable_actions(const bContext *C, bContextDataResult *result) { return screen_ctx_sel_actions_impl(C, result, false, true); } static eContextResult screen_ctx_sel_edit_fcurves_(const bContext *C, bContextDataResult *result, const int extra_filter) { bAnimContext ac; if (ANIM_animdata_get_context(C, &ac) && ELEM(ac.spacetype, SPACE_ACTION, SPACE_GRAPH)) { ListBase anim_data = {NULL, NULL}; int filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_NODUPLIS) | (ac.spacetype == SPACE_GRAPH ? (ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_FCURVESONLY) : ANIMFILTER_LIST_VISIBLE) | extra_filter; ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype); LISTBASE_FOREACH (bAnimListElem *, ale, &anim_data) { if (ELEM(ale->type, ANIMTYPE_FCURVE, ANIMTYPE_NLACURVE)) { CTX_data_list_add(result, ale->fcurve_owner_id, &RNA_FCurve, ale->data); } } ANIM_animdata_freelist(&anim_data); CTX_data_type_set(result, CTX_DATA_TYPE_COLLECTION); return CTX_RESULT_OK; } return CTX_RESULT_NO_DATA; } static eContextResult screen_ctx_editable_fcurves(const bContext *C, bContextDataResult *result) { return screen_ctx_sel_edit_fcurves_(C, result, ANIMFILTER_FOREDIT); } static eContextResult screen_ctx_visible_fcurves(const bContext *C, bContextDataResult *result) { return screen_ctx_sel_edit_fcurves_(C, result, 0); } static eContextResult screen_ctx_selected_editable_fcurves(const bContext *C, bContextDataResult *result) { return screen_ctx_sel_edit_fcurves_(C, result, ANIMFILTER_SEL | ANIMFILTER_FOREDIT); } static eContextResult screen_ctx_selected_visible_fcurves(const bContext *C, bContextDataResult *result) { return screen_ctx_sel_edit_fcurves_(C, result, ANIMFILTER_SEL); } static eContextResult screen_ctx_active_editable_fcurve(const bContext *C, bContextDataResult *result) { bAnimContext ac; if (ANIM_animdata_get_context(C, &ac) && ELEM(ac.spacetype, SPACE_GRAPH)) { ListBase anim_data = {NULL, NULL}; int filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_ACTIVE | ANIMFILTER_FOREDIT | ANIMFILTER_FCURVESONLY | ANIMFILTER_CURVE_VISIBLE); ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype); LISTBASE_FOREACH (bAnimListElem *, ale, &anim_data) { if (ELEM(ale->type, ANIMTYPE_FCURVE, ANIMTYPE_NLACURVE)) { CTX_data_pointer_set(result, ale->fcurve_owner_id, &RNA_FCurve, ale->data); break; } } ANIM_animdata_freelist(&anim_data); return CTX_RESULT_OK; } return CTX_RESULT_NO_DATA; } static eContextResult screen_ctx_selected_editable_keyframes(const bContext *C, bContextDataResult *result) { bAnimContext ac; if (ANIM_animdata_get_context(C, &ac) && ELEM(ac.spacetype, SPACE_ACTION, SPACE_GRAPH)) { ListBase anim_data = {NULL, NULL}; /* Use keyframes from editable selected FCurves. */ int filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_NODUPLIS | ANIMFILTER_FOREDIT | ANIMFILTER_SEL) | (ac.spacetype == SPACE_GRAPH ? (ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_FCURVESONLY) : ANIMFILTER_LIST_VISIBLE); ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype); int i; FCurve *fcurve; BezTriple *bezt; LISTBASE_FOREACH (bAnimListElem *, ale, &anim_data) { if (!ELEM(ale->type, ANIMTYPE_FCURVE, ANIMTYPE_NLACURVE)) { continue; } fcurve = (FCurve *)ale->data; if (fcurve->bezt == NULL) { /* Skip baked FCurves. */ continue; } for (i = 0, bezt = fcurve->bezt; i < fcurve->totvert; i++, bezt++) { if ((bezt->f2 & SELECT) == 0) { continue; } CTX_data_list_add(result, ale->fcurve_owner_id, &RNA_Keyframe, bezt); } } ANIM_animdata_freelist(&anim_data); CTX_data_type_set(result, CTX_DATA_TYPE_COLLECTION); return CTX_RESULT_OK; } return CTX_RESULT_NO_DATA; } static eContextResult screen_ctx_asset_library(const bContext *C, bContextDataResult *result) { WorkSpace *workspace = CTX_wm_workspace(C); CTX_data_pointer_set( result, &workspace->id, &RNA_AssetLibraryReference, &workspace->asset_library_ref); return CTX_RESULT_OK; } static eContextResult screen_ctx_ui_list(const bContext *C, bContextDataResult *result) { wmWindow *win = CTX_wm_window(C); ARegion *region = CTX_wm_region(C); if (region) { uiList *list = UI_list_find_mouse_over(region, win->eventstate); if (list) { CTX_data_pointer_set(result, NULL, &RNA_UIList, list); return CTX_RESULT_OK; } } return CTX_RESULT_NO_DATA; } /* Registry of context callback functions. */ typedef eContextResult (*context_callback)(const bContext *C, bContextDataResult *result); static GHash *ed_screen_context_functions = NULL; static void free_context_function_ghash(void *UNUSED(user_data)) { BLI_ghash_free(ed_screen_context_functions, NULL, NULL); } static inline void register_context_function(const char *member, context_callback function) { BLI_ghash_insert(ed_screen_context_functions, (void *)member, function); } static void ensure_ed_screen_context_functions(void) { if (ed_screen_context_functions != NULL) { return; } /* Murmur hash is faster for smaller strings (according to BLI_hash_mm2). */ ed_screen_context_functions = BLI_ghash_new( BLI_ghashutil_strhash_p_murmur, BLI_ghashutil_strcmp, __func__); BKE_blender_atexit_register(free_context_function_ghash, NULL); register_context_function("scene", screen_ctx_scene); register_context_function("visible_objects", screen_ctx_visible_objects); register_context_function("selectable_objects", screen_ctx_selectable_objects); register_context_function("selected_objects", screen_ctx_selected_objects); register_context_function("selected_editable_objects", screen_ctx_selected_editable_objects); register_context_function("editable_objects", screen_ctx_editable_objects); register_context_function("objects_in_mode", screen_ctx_objects_in_mode); register_context_function("objects_in_mode_unique_data", screen_ctx_objects_in_mode_unique_data); register_context_function("visible_bones", screen_ctx_visible_bones); register_context_function("editable_bones", screen_ctx_editable_bones); register_context_function("selected_bones", screen_ctx_selected_bones); register_context_function("selected_editable_bones", screen_ctx_selected_editable_bones); register_context_function("visible_pose_bones", screen_ctx_visible_pose_bones); register_context_function("selected_pose_bones", screen_ctx_selected_pose_bones); register_context_function("selected_pose_bones_from_active_object", screen_ctx_selected_pose_bones_from_active_object); register_context_function("active_bone", screen_ctx_active_bone); register_context_function("active_pose_bone", screen_ctx_active_pose_bone); register_context_function("active_object", screen_ctx_active_object); register_context_function("object", screen_ctx_object); register_context_function("edit_object", screen_ctx_edit_object); register_context_function("sculpt_object", screen_ctx_sculpt_object); register_context_function("vertex_paint_object", screen_ctx_vertex_paint_object); register_context_function("weight_paint_object", screen_ctx_weight_paint_object); register_context_function("image_paint_object", screen_ctx_image_paint_object); register_context_function("particle_edit_object", screen_ctx_particle_edit_object); register_context_function("pose_object", screen_ctx_pose_object); register_context_function("active_sequence_strip", screen_ctx_active_sequence_strip); register_context_function("sequences", screen_ctx_sequences); register_context_function("selected_sequences", screen_ctx_selected_sequences); register_context_function("selected_editable_sequences", screen_ctx_selected_editable_sequences); register_context_function("active_nla_track", screen_ctx_active_nla_track); register_context_function("active_nla_strip", screen_ctx_active_nla_strip); register_context_function("selected_nla_strips", screen_ctx_selected_nla_strips); register_context_function("selected_movieclip_tracks", screen_ctx_selected_movieclip_tracks); register_context_function("gpencil_data", screen_ctx_gpencil_data); register_context_function("gpencil_data_owner", screen_ctx_gpencil_data_owner); register_context_function("annotation_data", screen_ctx_annotation_data); register_context_function("annotation_data_owner", screen_ctx_annotation_data_owner); register_context_function("active_gpencil_layer", screen_ctx_active_gpencil_layer); register_context_function("active_annotation_layer", screen_ctx_active_annotation_layer); register_context_function("active_gpencil_frame", screen_ctx_active_gpencil_frame); register_context_function("visible_gpencil_layers", screen_ctx_visible_gpencil_layers); register_context_function("editable_gpencil_layers", screen_ctx_editable_gpencil_layers); register_context_function("editable_gpencil_strokes", screen_ctx_editable_gpencil_strokes); register_context_function("active_operator", screen_ctx_active_operator); register_context_function("active_action", screen_ctx_active_action); register_context_function("selected_visible_actions", screen_ctx_selected_visible_actions); register_context_function("selected_editable_actions", screen_ctx_selected_editable_actions); register_context_function("editable_fcurves", screen_ctx_editable_fcurves); register_context_function("visible_fcurves", screen_ctx_visible_fcurves); register_context_function("selected_editable_fcurves", screen_ctx_selected_editable_fcurves); register_context_function("selected_visible_fcurves", screen_ctx_selected_visible_fcurves); register_context_function("active_editable_fcurve", screen_ctx_active_editable_fcurve); register_context_function("selected_editable_keyframes", screen_ctx_selected_editable_keyframes); register_context_function("asset_library_ref", screen_ctx_asset_library); register_context_function("ui_list", screen_ctx_ui_list); } int ed_screen_context(const bContext *C, const char *member, bContextDataResult *result) { if (CTX_data_dir(member)) { CTX_data_dir_set(result, screen_context_dir); return CTX_RESULT_OK; } ensure_ed_screen_context_functions(); context_callback callback = BLI_ghash_lookup(ed_screen_context_functions, member); if (callback == NULL) { return CTX_RESULT_MEMBER_NOT_FOUND; } return callback(C, result); }