diff options
Diffstat (limited to 'source/blender/editors/object/object_edit.c')
-rw-r--r-- | source/blender/editors/object/object_edit.c | 1394 |
1 files changed, 641 insertions, 753 deletions
diff --git a/source/blender/editors/object/object_edit.c b/source/blender/editors/object/object_edit.c index 056ce6b03c6..19bcb14f395 100644 --- a/source/blender/editors/object/object_edit.c +++ b/source/blender/editors/object/object_edit.c @@ -51,7 +51,6 @@ #include "DNA_group_types.h" #include "DNA_material_types.h" #include "DNA_meta_types.h" -#include "DNA_property_types.h" #include "DNA_scene_types.h" #include "DNA_object_types.h" #include "DNA_object_force_types.h" @@ -59,15 +58,16 @@ #include "DNA_vfont_types.h" #include "DNA_mesh_types.h" #include "DNA_lattice_types.h" +#include "DNA_workspace_types.h" #include "IMB_imbuf_types.h" #include "BKE_anim.h" +#include "BKE_collection.h" #include "BKE_constraint.h" #include "BKE_context.h" #include "BKE_curve.h" #include "BKE_effect.h" -#include "BKE_depsgraph.h" #include "BKE_global.h" #include "BKE_image.h" #include "BKE_lattice.h" @@ -77,15 +77,18 @@ #include "BKE_mball.h" #include "BKE_mesh.h" #include "BKE_object.h" +#include "BKE_paint.h" #include "BKE_pointcache.h" -#include "BKE_property.h" -#include "BKE_sca.h" #include "BKE_softbody.h" #include "BKE_modifier.h" #include "BKE_editlattice.h" #include "BKE_editmesh.h" #include "BKE_report.h" -#include "BKE_undo_system.h" +#include "BKE_workspace.h" +#include "BKE_layer.h" + +#include "DEG_depsgraph.h" +#include "DEG_depsgraph_build.h" #include "ED_armature.h" #include "ED_curve.h" @@ -93,6 +96,7 @@ #include "ED_mball.h" #include "ED_lattice.h" #include "ED_object.h" +#include "ED_outliner.h" #include "ED_screen.h" #include "ED_undo.h" #include "ED_image.h" @@ -104,11 +108,19 @@ /* for menu/popup icons etc etc*/ #include "UI_interface.h" +#include "UI_resources.h" + #include "WM_api.h" #include "WM_types.h" +#include "WM_message.h" +#include "WM_toolsystem.h" #include "object_intern.h" // own include +/* prototypes */ +typedef struct MoveToCollectionData MoveToCollectionData; +static void move_to_collection_menus_items(struct uiLayout *layout, struct MoveToCollectionData *menu); + /* ************* XXX **************** */ static void error(const char *UNUSED(arg)) {} static void waitcursor(int UNUSED(val)) {} @@ -134,95 +146,109 @@ Object *ED_object_active_context(bContext *C) return ob; } +/* ********************** object hiding *************************** */ + +static int object_hide_poll(bContext *C) +{ + if (CTX_wm_space_outliner(C) != NULL) { + return ED_outliner_collections_editor_poll(C); + } + else { + return ED_operator_view3d_active(C); + } +} -/* ********* clear/set restrict view *********/ static int object_hide_view_clear_exec(bContext *C, wmOperator *op) { - Main *bmain = CTX_data_main(C); - ScrArea *sa = CTX_wm_area(C); - View3D *v3d = sa->spacedata.first; Scene *scene = CTX_data_scene(C); - Base *base; - bool changed = false; + ViewLayer *view_layer = CTX_data_view_layer(C); const bool select = RNA_boolean_get(op->ptr, "select"); + bool changed = false; - /* XXX need a context loop to handle such cases */ - for (base = FIRSTBASE; base; base = base->next) { - if ((base->lay & v3d->lay) && base->object->restrictflag & OB_RESTRICT_VIEW) { - if (!(base->object->restrictflag & OB_RESTRICT_SELECT)) { - SET_FLAG_FROM_TEST(base->flag, select, SELECT); - } - base->object->flag = base->flag; - base->object->restrictflag &= ~OB_RESTRICT_VIEW; + for (Base *base = view_layer->object_bases.first; base; base = base->next) { + if (base->flag & BASE_HIDE) { + base->flag &= ~BASE_HIDE; changed = true; + + if (select) { + ED_object_base_select(base, BA_SELECT); + } } } - if (changed) { - DAG_id_type_tag(bmain, ID_OB); - DAG_relations_tag_update(bmain); - WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, scene); + + if (!changed) { + return OPERATOR_CANCELLED; } + BKE_layer_collection_sync(scene, view_layer); + DEG_id_tag_update(&scene->id, DEG_TAG_BASE_FLAGS_UPDATE); + WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, scene); + return OPERATOR_FINISHED; } void OBJECT_OT_hide_view_clear(wmOperatorType *ot) { - /* identifiers */ - ot->name = "Clear Restrict View"; - ot->description = "Reveal the object by setting the hide flag"; + ot->name = "Show Hidden Objects"; + ot->description = "Reveal temporarily hidden objects"; ot->idname = "OBJECT_OT_hide_view_clear"; /* api callbacks */ ot->exec = object_hide_view_clear_exec; - ot->poll = ED_operator_view3d_active; + ot->poll = object_hide_poll; /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - RNA_def_boolean(ot->srna, "select", true, "Select", ""); + PropertyRNA *prop = RNA_def_boolean(ot->srna, "select", false, "Select", ""); + RNA_def_property_flag(prop, PROP_SKIP_SAVE | PROP_HIDDEN); } static int object_hide_view_set_exec(bContext *C, wmOperator *op) { - Main *bmain = CTX_data_main(C); Scene *scene = CTX_data_scene(C); - bool changed = false; + ViewLayer *view_layer = CTX_data_view_layer(C); const bool unselected = RNA_boolean_get(op->ptr, "unselected"); - CTX_DATA_BEGIN(C, Base *, base, visible_bases) - { + /* Do nothing if no objects was selected. */ + bool have_selected = false; + for (Base *base = view_layer->object_bases.first; base; base = base->next) { + if (base->flag & BASE_VISIBLED) { + if (base->flag & BASE_SELECTED) { + have_selected = true; + break; + } + } + } + + if (!have_selected) { + return OPERATOR_CANCELLED; + } + + /* Hide selected or unselected objects. */ + for (Base *base = view_layer->object_bases.first; base; base = base->next) { + if (!(base->flag & BASE_VISIBLED)) { + continue; + } + if (!unselected) { - if (base->flag & SELECT) { - base->flag &= ~SELECT; - base->object->flag = base->flag; - base->object->restrictflag |= OB_RESTRICT_VIEW; - changed = true; - if (base == BASACT) { - ED_base_object_activate(C, NULL); - } + if (base->flag & BASE_SELECTED) { + ED_object_base_select(base, BA_DESELECT); + base->flag |= BASE_HIDE; } } else { - if (!(base->flag & SELECT)) { - base->object->restrictflag |= OB_RESTRICT_VIEW; - changed = true; - if (base == BASACT) { - ED_base_object_activate(C, NULL); - } + if (!(base->flag & BASE_SELECTED)) { + ED_object_base_select(base, BA_DESELECT); + base->flag |= BASE_HIDE; } } } - CTX_DATA_END; - - if (changed) { - DAG_id_type_tag(bmain, ID_OB); - DAG_relations_tag_update(bmain); - - WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, scene); - } + BKE_layer_collection_sync(scene, view_layer); + DEG_id_tag_update(&scene->id, DEG_TAG_BASE_FLAGS_UPDATE); + WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, scene); return OPERATOR_FINISHED; } @@ -230,95 +256,127 @@ static int object_hide_view_set_exec(bContext *C, wmOperator *op) void OBJECT_OT_hide_view_set(wmOperatorType *ot) { /* identifiers */ - ot->name = "Set Restrict View"; - ot->description = "Hide the object by setting the hide flag"; + ot->name = "Hide Objects"; + ot->description = "Temporarily hide objects from the viewport"; ot->idname = "OBJECT_OT_hide_view_set"; /* api callbacks */ ot->exec = object_hide_view_set_exec; - ot->poll = ED_operator_view3d_active; + ot->poll = object_hide_poll; /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - RNA_def_boolean(ot->srna, "unselected", 0, "Unselected", "Hide unselected rather than selected objects"); - + PropertyRNA *prop; + prop = RNA_def_boolean(ot->srna, "unselected", 0, "Unselected", "Hide unselected rather than selected objects"); + RNA_def_property_flag(prop, PROP_SKIP_SAVE | PROP_HIDDEN); } -/* 99% same as above except no need for scene refreshing (TODO, update render preview) */ -static int object_hide_render_clear_exec(bContext *C, wmOperator *UNUSED(op)) +static int object_hide_collection_exec(bContext *C, wmOperator *op) { - bool changed = false; + int index = RNA_int_get(op->ptr, "collection_index"); + bool extend = (CTX_wm_window(C)->eventstate->shift != 0); - /* XXX need a context loop to handle such cases */ - CTX_DATA_BEGIN(C, Object *, ob, selected_editable_objects) - { - if (ob->restrictflag & OB_RESTRICT_RENDER) { - ob->restrictflag &= ~OB_RESTRICT_RENDER; - changed = true; - } + if (CTX_wm_window(C)->eventstate->alt != 0) { + index += 10; } - CTX_DATA_END; - if (changed) - WM_event_add_notifier(C, NC_SPACE | ND_SPACE_OUTLINER, NULL); + Scene *scene = CTX_data_scene(C); + ViewLayer *view_layer = CTX_data_view_layer(C); + LayerCollection *lc = BKE_layer_collection_from_index(view_layer, index); + + if (!lc) { + return OPERATOR_CANCELLED; + } + + BKE_layer_collection_set_visible(scene, view_layer, lc, extend); + + DEG_id_tag_update(&scene->id, DEG_TAG_BASE_FLAGS_UPDATE); + WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, scene); return OPERATOR_FINISHED; } -void OBJECT_OT_hide_render_clear(wmOperatorType *ot) +#define COLLECTION_INVALID_INDEX -1 + +void ED_hide_collections_menu_draw(const bContext *C, uiLayout *layout) { + ViewLayer *view_layer = CTX_data_view_layer(C); + LayerCollection *lc_scene = view_layer->layer_collections.first; - /* identifiers */ - ot->name = "Clear Restrict Render"; - ot->description = "Reveal the render object by setting the hide render flag"; - ot->idname = "OBJECT_OT_hide_render_clear"; + uiLayoutSetOperatorContext(layout, WM_OP_EXEC_REGION_WIN); - /* api callbacks */ - ot->exec = object_hide_render_clear_exec; - ot->poll = ED_operator_view3d_active; + for (LayerCollection *lc = lc_scene->layer_collections.first; lc; lc = lc->next) { + int index = BKE_layer_collection_findindex(view_layer, lc); + uiLayout *row = uiLayoutRow(layout, false); - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; -} + if (lc->collection->flag & COLLECTION_RESTRICT_VIEW) { + continue; + } -static int object_hide_render_set_exec(bContext *C, wmOperator *op) -{ - const bool unselected = RNA_boolean_get(op->ptr, "unselected"); + if ((view_layer->runtime_flag & VIEW_LAYER_HAS_HIDE) && + !(lc->runtime_flag & LAYER_COLLECTION_HAS_VISIBLE_OBJECTS)) + { + uiLayoutSetActive(row, false); + } - CTX_DATA_BEGIN(C, Base *, base, visible_bases) - { - if (!unselected) { - if (base->flag & SELECT) { - base->object->restrictflag |= OB_RESTRICT_RENDER; - } + int icon = ICON_NONE; + if (BKE_layer_collection_has_selected_objects(view_layer, lc)) { + icon = ICON_LAYER_ACTIVE; } - else { - if (!(base->flag & SELECT)) { - base->object->restrictflag |= OB_RESTRICT_RENDER; - } + else if (lc->runtime_flag & LAYER_COLLECTION_HAS_OBJECTS) { + icon = ICON_LAYER_USED; } + + uiItemIntO(row, + lc->collection->id.name + 2, + icon, + "OBJECT_OT_hide_collection", + "collection_index", + index); } - CTX_DATA_END; - WM_event_add_notifier(C, NC_SPACE | ND_SPACE_OUTLINER, NULL); - return OPERATOR_FINISHED; } -void OBJECT_OT_hide_render_set(wmOperatorType *ot) +static int object_hide_collection_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) +{ + /* Immediately execute if collection index was specified. */ + int index = RNA_int_get(op->ptr, "collection_index"); + if (index != COLLECTION_INVALID_INDEX) { + return object_hide_collection_exec(C, op); + } + + /* Open popup menu. */ + const char *title = CTX_IFACE_(op->type->translation_context, op->type->name); + uiPopupMenu *pup = UI_popup_menu_begin(C, title, ICON_GROUP); + uiLayout *layout = UI_popup_menu_layout(pup); + + ED_hide_collections_menu_draw(C, layout); + + UI_popup_menu_end(C, pup); + + return OPERATOR_INTERFACE; +} + +void OBJECT_OT_hide_collection(wmOperatorType *ot) { /* identifiers */ - ot->name = "Set Restrict Render"; - ot->description = "Hide the render object by setting the hide render flag"; - ot->idname = "OBJECT_OT_hide_render_set"; + ot->name = "Hide Objects By Collection"; + ot->description = "Show only objects in collection (Shift to extend)"; + ot->idname = "OBJECT_OT_hide_collection"; /* api callbacks */ - ot->exec = object_hide_render_set_exec; + ot->exec = object_hide_collection_exec; + ot->invoke = object_hide_collection_invoke; ot->poll = ED_operator_view3d_active; /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - RNA_def_boolean(ot->srna, "unselected", 0, "Unselected", "Hide unselected rather than selected objects"); + /* Properties. */ + PropertyRNA *prop; + prop = RNA_def_int(ot->srna, "collection_index", COLLECTION_INVALID_INDEX, COLLECTION_INVALID_INDEX, INT_MAX, + "Collection Index", "Index of the collection to change visibility", 0, INT_MAX); + RNA_def_property_flag(prop, PROP_SKIP_SAVE | PROP_HIDDEN); } /* ******************* toggle editmode operator ***************** */ @@ -391,7 +449,7 @@ static bool ED_object_editmode_load_ex(Main *bmain, Object *obedit, const bool f * to inform dependency graph about this. But is it really the * best place to do this? */ - DAG_relations_tag_update(bmain); + DEG_relations_tag_update(bmain); } else if (ELEM(obedit->type, OB_CURVE, OB_SURF)) { const Curve *cu = obedit->data; @@ -455,8 +513,8 @@ bool ED_object_editmode_exit_ex(Main *bmain, Scene *scene, Object *obedit, int f if (ED_object_editmode_load_ex(bmain, obedit, freedata) == false) { /* in rare cases (background mode) its possible active object * is flagged for editmode, without 'obedit' being set [#35489] */ - if (UNLIKELY(scene->basact && (scene->basact->object->mode & OB_MODE_EDIT))) { - scene->basact->object->mode &= ~OB_MODE_EDIT; + if (UNLIKELY(obedit && obedit->mode & OB_MODE_EDIT)) { + obedit->mode &= ~OB_MODE_EDIT; } if (flag & EM_WAITCURSOR) waitcursor(0); return true; @@ -467,11 +525,8 @@ bool ED_object_editmode_exit_ex(Main *bmain, Scene *scene, Object *obedit, int f ListBase pidlist; PTCacheID *pid; - /* for example; displist make is different in editmode */ - scene->obedit = NULL; // XXX for context - /* flag object caches as outdated */ - BKE_ptcache_ids_from_object(bmain, &pidlist, obedit, scene, 0); + BKE_ptcache_ids_from_object(&pidlist, obedit, scene, 0); for (pid = pidlist.first; pid; pid = pid->next) { if (pid->type != PTCACHE_TYPE_PARTICLES) /* particles don't need reset on geometry change */ pid->cache->flag |= PTCACHE_OUTDATED; @@ -481,7 +536,7 @@ bool ED_object_editmode_exit_ex(Main *bmain, Scene *scene, Object *obedit, int f BKE_ptcache_object_reset(scene, obedit, PTCACHE_RESET_OUTDATED); /* also flush ob recalc, doesn't take much overhead, but used for particles */ - DAG_id_tag_update(&obedit->id, OB_RECALC_OB | OB_RECALC_DATA); + DEG_id_tag_update(&obedit->id, OB_RECALC_OB | OB_RECALC_DATA); WM_main_add_notifier(NC_SCENE | ND_MODE | NS_MODE_OBJECT, scene); @@ -501,43 +556,14 @@ bool ED_object_editmode_exit(bContext *C, int flag) return ED_object_editmode_exit_ex(bmain, scene, obedit, flag); } -bool ED_object_editmode_enter(bContext *C, int flag) +bool ED_object_editmode_enter_ex(Main *bmain, Scene *scene, Object *ob, int flag) { - Main *bmain = CTX_data_main(C); - Scene *scene = CTX_data_scene(C); - Base *base = NULL; - Object *ob; - ScrArea *sa = CTX_wm_area(C); - View3D *v3d = NULL; bool ok = false; - if (ID_IS_LINKED(scene)) { - return false; - } - - if (sa && sa->spacetype == SPACE_VIEW3D) - v3d = sa->spacedata.first; - - if ((flag & EM_IGNORE_LAYER) == 0) { - base = CTX_data_active_base(C); /* active layer checked here for view3d */ - - if ((base == NULL) || - (v3d && (base->lay & v3d->lay) == 0) || - (!v3d && (base->lay & scene->lay) == 0)) - { - return false; - } - } - else { - base = scene->basact; - } - - if (ELEM(NULL, base, base->object, base->object->data)) { + if (ELEM(NULL, ob, ob->data) || ID_IS_LINKED(ob)) { return false; } - ob = base->object; - /* this checks actual object->data, for cases when other scenes have it in editmode context */ if (BKE_object_is_in_editmode(ob)) { return true; @@ -552,17 +578,11 @@ bool ED_object_editmode_enter(bContext *C, int flag) ob->restore_mode = ob->mode; - /* note, when switching scenes the object can have editmode data but - * not be scene->obedit: bug 22954, this avoids calling self eternally */ - if ((ob->restore_mode & OB_MODE_EDIT) == 0) - ED_object_mode_toggle(C, ob->mode); - ob->mode = OB_MODE_EDIT; if (ob->type == OB_MESH) { BMEditMesh *em; ok = 1; - scene->obedit = ob; /* context sees this */ const bool use_key_index = mesh_needs_keyindex(bmain, ob->data); @@ -575,53 +595,49 @@ bool ED_object_editmode_enter(bContext *C, int flag) BKE_editmesh_tessface_calc(em); } - WM_event_add_notifier(C, NC_SCENE | ND_MODE | NS_EDITMODE_MESH, scene); + WM_main_add_notifier(NC_SCENE | ND_MODE | NS_EDITMODE_MESH, NULL); } else if (ob->type == OB_ARMATURE) { ok = 1; - scene->obedit = ob; ED_armature_to_edit(ob->data); /* to ensure all goes in restposition and without striding */ - DAG_id_tag_update(&ob->id, OB_RECALC_OB | OB_RECALC_DATA | OB_RECALC_TIME); /* XXX: should this be OB_RECALC_DATA? */ + DEG_id_tag_update(&ob->id, OB_RECALC_OB | OB_RECALC_DATA | OB_RECALC_TIME); /* XXX: should this be OB_RECALC_DATA? */ - WM_event_add_notifier(C, NC_SCENE | ND_MODE | NS_EDITMODE_ARMATURE, scene); + WM_main_add_notifier(NC_SCENE | ND_MODE | NS_EDITMODE_ARMATURE, scene); } else if (ob->type == OB_FONT) { - scene->obedit = ob; /* XXX for context */ ok = 1; ED_curve_editfont_make(ob); - WM_event_add_notifier(C, NC_SCENE | ND_MODE | NS_EDITMODE_TEXT, scene); + WM_main_add_notifier(NC_SCENE | ND_MODE | NS_EDITMODE_TEXT, scene); } else if (ob->type == OB_MBALL) { - scene->obedit = ob; /* XXX for context */ ok = 1; ED_mball_editmball_make(ob); - WM_event_add_notifier(C, NC_SCENE | ND_MODE | NS_EDITMODE_MBALL, scene); + WM_main_add_notifier(NC_SCENE | ND_MODE | NS_EDITMODE_MBALL, scene); } else if (ob->type == OB_LATTICE) { - scene->obedit = ob; /* XXX for context */ ok = 1; BKE_editlattice_make(ob); - WM_event_add_notifier(C, NC_SCENE | ND_MODE | NS_EDITMODE_LATTICE, scene); + WM_main_add_notifier(NC_SCENE | ND_MODE | NS_EDITMODE_LATTICE, scene); } else if (ob->type == OB_SURF || ob->type == OB_CURVE) { ok = 1; - scene->obedit = ob; /* XXX for context */ ED_curve_editnurb_make(ob); - WM_event_add_notifier(C, NC_SCENE | ND_MODE | NS_EDITMODE_CURVE, scene); + WM_main_add_notifier(NC_SCENE | ND_MODE | NS_EDITMODE_CURVE, scene); } if (ok) { - DAG_id_tag_update(&ob->id, OB_RECALC_DATA); + DEG_id_tag_update(&ob->id, OB_RECALC_DATA); } else { - scene->obedit = NULL; /* XXX for context */ - ob->mode &= ~OB_MODE_EDIT; - WM_event_add_notifier(C, NC_SCENE | ND_MODE | NS_MODE_OBJECT, scene); + if ((flag & EM_NO_CONTEXT) == 0) { + ob->mode &= ~OB_MODE_EDIT; + } + WM_main_add_notifier(NC_SCENE | ND_MODE | NS_MODE_OBJECT, scene); } if (flag & EM_WAITCURSOR) waitcursor(0); @@ -629,28 +645,72 @@ bool ED_object_editmode_enter(bContext *C, int flag) return (ob->mode & OB_MODE_EDIT) != 0; } -static int editmode_toggle_exec(bContext *C, wmOperator *op) +bool ED_object_editmode_enter(bContext *C, int flag) { Main *bmain = CTX_data_main(C); + Scene *scene = CTX_data_scene(C); + ViewLayer *view_layer = CTX_data_view_layer(C); + Object *ob; + + if ((flag & EM_IGNORE_LAYER) == 0) { + ob = CTX_data_active_object(C); /* active layer checked here for view3d */ + } + else { + ob = view_layer->basact->object; + } + if ((ob == NULL) || ID_IS_LINKED(ob)) { + return false; + } + return ED_object_editmode_enter_ex(bmain, scene, ob, flag); +} + +static int editmode_toggle_exec(bContext *C, wmOperator *op) +{ + struct wmMsgBus *mbus = CTX_wm_message_bus(C); const int mode_flag = OB_MODE_EDIT; const bool is_mode_set = (CTX_data_edit_object(C) != NULL); + Main *bmain = CTX_data_main(C); Scene *scene = CTX_data_scene(C); + ViewLayer *view_layer = CTX_data_view_layer(C); + Object *obact = OBACT(view_layer); if (!is_mode_set) { - Object *ob = CTX_data_active_object(C); - if (!ED_object_mode_compat_set(C, ob, mode_flag, op->reports)) { + if (!ED_object_mode_compat_set(C, obact, mode_flag, op->reports)) { return OPERATOR_CANCELLED; } } if (!is_mode_set) { ED_object_editmode_enter(C, EM_WAITCURSOR); + if (obact->mode & mode_flag) { + FOREACH_SELECTED_OBJECT_BEGIN(view_layer, ob) + { + if ((ob != obact) && (ob->type == obact->type)) { + ED_object_editmode_enter_ex(bmain, scene, ob, EM_WAITCURSOR | EM_NO_CONTEXT); + } + } + FOREACH_SELECTED_OBJECT_END; + } } else { ED_object_editmode_exit(C, EM_FREEDATA | EM_WAITCURSOR); + if ((obact->mode & mode_flag) == 0) { + FOREACH_OBJECT_BEGIN(view_layer, ob) + { + if ((ob != obact) && (ob->type == obact->type)) { + ED_object_editmode_exit_ex(bmain, scene, ob, EM_FREEDATA | EM_WAITCURSOR); + } + } + FOREACH_OBJECT_END; + } } + ED_space_image_uv_sculpt_update(bmain, CTX_wm_manager(C), scene); + WM_msg_publish_rna_prop(mbus, &obact->id, obact, Object, mode); + + WM_toolsystem_update_from_context_view3d(C); + return OPERATOR_FINISHED; } @@ -663,8 +723,9 @@ static int editmode_toggle_poll(bContext *C) return 0; /* if hidden but in edit mode, we still display */ - if ((ob->restrictflag & OB_RESTRICT_VIEW) && !(ob->mode & OB_MODE_EDIT)) + if ((ob->restrictflag & OB_RESTRICT_VIEW) && !(ob->mode & OB_MODE_EDIT)) { return 0; + } return OB_TYPE_SUPPORT_EDITMODE(ob->type); } @@ -689,34 +750,68 @@ void OBJECT_OT_editmode_toggle(wmOperatorType *ot) static int posemode_exec(bContext *C, wmOperator *op) { + struct wmMsgBus *mbus = CTX_wm_message_bus(C); Base *base = CTX_data_active_base(C); - Object *ob = base->object; + Object *obact = base->object; const int mode_flag = OB_MODE_POSE; - bool is_mode_set = (ob->mode & mode_flag) != 0; + bool is_mode_set = (obact->mode & mode_flag) != 0; if (!is_mode_set) { - if (!ED_object_mode_compat_set(C, ob, mode_flag, op->reports)) { + if (!ED_object_mode_compat_set(C, obact, mode_flag, op->reports)) { return OPERATOR_CANCELLED; } } - if (ob->type == OB_ARMATURE) { - if (ob == CTX_data_edit_object(C)) { - ED_object_editmode_exit(C, EM_FREEDATA); - is_mode_set = false; - } + if (obact->type != OB_ARMATURE) { + return OPERATOR_PASS_THROUGH; + } + + if (obact == CTX_data_edit_object(C)) { + ED_object_editmode_exit(C, EM_FREEDATA); + is_mode_set = false; + } - if (is_mode_set) { - ED_object_posemode_exit(C, ob); + if (is_mode_set) { + bool ok = ED_object_posemode_exit(C, obact); + if (ok) { + struct Main *bmain = CTX_data_main(C); + ViewLayer *view_layer = CTX_data_view_layer(C); + FOREACH_OBJECT_BEGIN(view_layer, ob) + { + if ((ob != obact) && + (ob->type == OB_ARMATURE) && + (ob->mode & mode_flag)) + { + ED_object_posemode_exit_ex(bmain, ob); + } + } + FOREACH_OBJECT_END; } - else { - ED_object_posemode_enter(C, ob); + } + else { + bool ok = ED_object_posemode_enter(C, obact); + if (ok) { + struct Main *bmain = CTX_data_main(C); + ViewLayer *view_layer = CTX_data_view_layer(C); + FOREACH_SELECTED_OBJECT_BEGIN(view_layer, ob) + { + if ((ob != obact) && + (ob->type == OB_ARMATURE) && + (ob->mode == OB_MODE_OBJECT) && + (!ID_IS_LINKED(ob))) + { + ED_object_posemode_enter_ex(bmain, ob); + } + } + FOREACH_SELECTED_OBJECT_END; } - - return OPERATOR_FINISHED; } - return OPERATOR_PASS_THROUGH; + WM_msg_publish_rna_prop(mbus, &obact->id, obact, Object, mode); + + WM_toolsystem_update_from_context_view3d(C); + + return OPERATOR_FINISHED; } void OBJECT_OT_posemode_toggle(wmOperatorType *ot) @@ -734,102 +829,6 @@ void OBJECT_OT_posemode_toggle(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } -static void copymenu_properties(Scene *scene, View3D *v3d, Object *ob) -{ -//XXX no longer used - to be removed - replaced by game_properties_copy_exec - bProperty *prop; - Base *base; - int nr, tot = 0; - char *str; - - prop = ob->prop.first; - while (prop) { - tot++; - prop = prop->next; - } - - str = MEM_callocN(50 + 33 * tot, "copymenu prop"); - - if (tot) - strcpy(str, "Copy Property %t|Replace All|Merge All|%l"); - else - strcpy(str, "Copy Property %t|Clear All (no properties on active)"); - - tot = 0; - prop = ob->prop.first; - while (prop) { - tot++; - strcat(str, "|"); - strcat(str, prop->name); - prop = prop->next; - } - - nr = pupmenu(str); - - if (nr == 1 || nr == 2) { - for (base = FIRSTBASE; base; base = base->next) { - if ((base != BASACT) && (TESTBASELIB(v3d, base))) { - if (nr == 1) { /* replace */ - BKE_bproperty_copy_list(&base->object->prop, &ob->prop); - } - else { - for (prop = ob->prop.first; prop; prop = prop->next) { - BKE_bproperty_object_set(base->object, prop); - } - } - } - } - } - else if (nr > 0) { - prop = BLI_findlink(&ob->prop, nr - 4); /* account for first 3 menu items & menu index starting at 1*/ - - if (prop) { - for (base = FIRSTBASE; base; base = base->next) { - if ((base != BASACT) && (TESTBASELIB(v3d, base))) { - BKE_bproperty_object_set(base->object, prop); - } - } - } - } - MEM_freeN(str); - -} - -static void copymenu_logicbricks(Scene *scene, View3D *v3d, Object *ob) -{ -//XXX no longer used - to be removed - replaced by logicbricks_copy_exec - Base *base; - - for (base = FIRSTBASE; base; base = base->next) { - if (base->object != ob) { - if (TESTBASELIB(v3d, base)) { - - /* first: free all logic */ - free_sensors(&base->object->sensors); - unlink_controllers(&base->object->controllers); - free_controllers(&base->object->controllers); - unlink_actuators(&base->object->actuators); - free_actuators(&base->object->actuators); - - /* now copy it, this also works without logicbricks! */ - clear_sca_new_poins_ob(ob); - copy_sensors(&base->object->sensors, &ob->sensors, 0); - copy_controllers(&base->object->controllers, &ob->controllers, 0); - copy_actuators(&base->object->actuators, &ob->actuators, 0); - set_sca_new_poins_ob(base->object); - - /* some menu settings */ - base->object->scavisflag = ob->scavisflag; - base->object->scaflag = ob->scaflag; - - /* set the initial state */ - base->object->state = ob->state; - base->object->init_state = ob->init_state; - } - } - } -} - /* both pointers should exist */ static void copy_texture_space(Object *to, Object *ob) { @@ -881,40 +880,32 @@ static void copy_texture_space(Object *to, Object *ob) } /* UNUSED, keep in case we want to copy functionality for use elsewhere */ -static void copy_attr(Main *bmain, Scene *scene, View3D *v3d, short event) +static void copy_attr(Main *bmain, Scene *scene, ViewLayer *view_layer, short event) { Object *ob; Base *base; Curve *cu, *cu1; Nurb *nu; - bool do_depgraph_update = false; if (ID_IS_LINKED(scene)) return; - if (!(ob = OBACT)) return; + if (!(ob = OBACT(view_layer))) return; - if (scene->obedit) { // XXX get from context + if (BKE_object_is_in_editmode(ob)) { /* obedit_copymenu(); */ return; } - if (event == 9) { - copymenu_properties(scene, v3d, ob); - return; - } - else if (event == 10) { - copymenu_logicbricks(scene, v3d, ob); - return; - } - else if (event == 24) { + + if (event == 24) { /* moved to BKE_object_link_modifiers */ /* copymenu_modifiers(bmain, scene, v3d, ob); */ return; } - for (base = FIRSTBASE; base; base = base->next) { - if (base != BASACT) { - if (TESTBASELIB(v3d, base)) { - DAG_id_tag_update(&base->object->id, OB_RECALC_DATA); + for (base = FIRSTBASE(view_layer); base; base = base->next) { + if (base != BASACT(view_layer)) { + if (TESTBASELIB(base)) { + DEG_id_tag_update(&base->object->id, OB_RECALC_DATA); if (event == 1) { /* loc */ copy_v3_v3(base->object->loc, ob->loc); @@ -953,30 +944,6 @@ static void copy_attr(Main *bmain, Scene *scene, View3D *v3d, short event) if (ob->dup_group) id_us_plus(&ob->dup_group->id); } - else if (event == 7) { /* mass */ - base->object->mass = ob->mass; - } - else if (event == 8) { /* damping */ - base->object->damping = ob->damping; - base->object->rdamping = ob->rdamping; - } - else if (event == 11) { /* all physical attributes */ - base->object->gameflag = ob->gameflag; - base->object->inertia = ob->inertia; - base->object->formfactor = ob->formfactor; - base->object->damping = ob->damping; - base->object->rdamping = ob->rdamping; - base->object->min_vel = ob->min_vel; - base->object->max_vel = ob->max_vel; - base->object->min_angvel = ob->min_angvel; - base->object->max_angvel = ob->max_angvel; - if (ob->gameflag & OB_BOUNDS) { - base->object->collision_boundtype = ob->collision_boundtype; - } - base->object->margin = ob->margin; - base->object->bsoft = copy_bulletsoftbody(ob->bsoft, 0); - - } else if (event == 17) { /* tex space */ copy_texture_space(base->object, ob); } @@ -1017,7 +984,7 @@ static void copy_attr(Main *bmain, Scene *scene, View3D *v3d, short event) BLI_strncpy(cu1->family, cu->family, sizeof(cu1->family)); - DAG_id_tag_update(&base->object->id, OB_RECALC_DATA); + DEG_id_tag_update(&base->object->id, OB_RECALC_DATA); } } else if (event == 19) { /* bevel settings */ @@ -1033,7 +1000,7 @@ static void copy_attr(Main *bmain, Scene *scene, View3D *v3d, short event) cu1->ext1 = cu->ext1; cu1->ext2 = cu->ext2; - DAG_id_tag_update(&base->object->id, OB_RECALC_DATA); + DEG_id_tag_update(&base->object->id, OB_RECALC_DATA); } } else if (event == 25) { /* curve resolution */ @@ -1052,7 +1019,7 @@ static void copy_attr(Main *bmain, Scene *scene, View3D *v3d, short event) nu = nu->next; } - DAG_id_tag_update(&base->object->id, OB_RECALC_DATA); + DEG_id_tag_update(&base->object->id, OB_RECALC_DATA); } } else if (event == 21) { @@ -1068,15 +1035,15 @@ static void copy_attr(Main *bmain, Scene *scene, View3D *v3d, short event) } modifier_copyData(md, tmd); - DAG_id_tag_update(&base->object->id, OB_RECALC_DATA); + DEG_id_tag_update(&base->object->id, OB_RECALC_DATA); } } } else if (event == 22) { /* Copy the constraint channels over */ BKE_constraints_copy(&base->object->constraints, &ob->constraints, true); - - do_depgraph_update = true; + DEG_id_tag_update(&base->object->id, DEG_TAG_COPY_ON_WRITE); + DEG_relations_tag_update(bmain); } else if (event == 23) { base->object->softflag = ob->softflag; @@ -1087,6 +1054,9 @@ static void copy_attr(Main *bmain, Scene *scene, View3D *v3d, short event) if (!modifiers_findByType(base->object, eModifierType_Softbody)) { BLI_addhead(&base->object->modifiers, modifier_new(eModifierType_Softbody)); } + + DEG_id_tag_update(&base->object->id, DEG_TAG_COPY_ON_WRITE); + DEG_relations_tag_update(bmain); } else if (event == 26) { #if 0 // XXX old animation system @@ -1127,20 +1097,17 @@ static void copy_attr(Main *bmain, Scene *scene, View3D *v3d, short event) } } } - - if (do_depgraph_update) - DAG_relations_tag_update(bmain); } -static void UNUSED_FUNCTION(copy_attr_menu) (Main *bmain, Scene *scene, View3D *v3d) +static void UNUSED_FUNCTION(copy_attr_menu) (Main *bmain, Scene *scene, ViewLayer *view_layer, Object *obedit) { Object *ob; short event; char str[512]; - if (!(ob = OBACT)) return; + if (!(ob = OBACT(view_layer))) return; - if (scene->obedit) { /* XXX get from context */ + if (obedit) { /* if (ob->type == OB_MESH) */ /* XXX mesh_copy_menu(); */ return; @@ -1186,7 +1153,7 @@ static void UNUSED_FUNCTION(copy_attr_menu) (Main *bmain, Scene *scene, View3D * event = pupmenu(str); if (event <= 0) return; - copy_attr(bmain, scene, v3d, event); + copy_attr(bmain, scene, view_layer, event); } /* ******************* force field toggle operator ***************** */ @@ -1255,7 +1222,8 @@ void OBJECT_OT_forcefield_toggle(wmOperatorType *ot) */ void ED_objects_recalculate_paths(bContext *C, Scene *scene) { - Main *bmain = CTX_data_main(C); + struct Main *bmain = CTX_data_main(C); + Depsgraph *depsgraph = CTX_data_depsgraph(C); ListBase targets = {NULL, NULL}; /* loop over objects in scene */ @@ -1268,8 +1236,17 @@ void ED_objects_recalculate_paths(bContext *C, Scene *scene) CTX_DATA_END; /* recalculate paths, then free */ - animviz_calc_motionpaths(bmain, scene, &targets); + animviz_calc_motionpaths(depsgraph, bmain, scene, &targets); BLI_freelistN(&targets); + + /* tag objects for copy on write - so paths will draw/redraw */ + CTX_DATA_BEGIN(C, Object *, ob, selected_editable_objects) + { + if (ob->mpath) { + DEG_id_tag_update(&ob->id, DEG_TAG_COPY_ON_WRITE); + } + } + CTX_DATA_END; } @@ -1489,7 +1466,8 @@ static int shade_smooth_exec(bContext *C, wmOperator *op) if (ob->type == OB_MESH) { BKE_mesh_smooth_flag_set(ob, !clear); - DAG_id_tag_update(&ob->id, OB_RECALC_DATA); + BKE_mesh_batch_cache_dirty(ob->data, BKE_MESH_BATCH_DIRTY_ALL); + DEG_id_tag_update(&ob->id, OB_RECALC_DATA); WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob); done = true; @@ -1502,7 +1480,7 @@ static int shade_smooth_exec(bContext *C, wmOperator *op) else nu->flag &= ~ME_SMOOTH; } - DAG_id_tag_update(&ob->id, OB_RECALC_DATA); + DEG_id_tag_update(&ob->id, OB_RECALC_DATA); WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob); done = true; @@ -1553,68 +1531,6 @@ void OBJECT_OT_shade_smooth(wmOperatorType *ot) /* ********************** */ -static void UNUSED_FUNCTION(image_aspect) (Scene *scene, View3D *v3d) -{ - /* all selected objects with an image map: scale in image aspect */ - Base *base; - Object *ob; - Material *ma; - Tex *tex; - float x, y, space; - int a, b, done; - - if (scene->obedit) return; // XXX get from context - if (ID_IS_LINKED(scene)) return; - - for (base = FIRSTBASE; base; base = base->next) { - if (TESTBASELIB(v3d, base)) { - ob = base->object; - done = false; - - for (a = 1; a <= ob->totcol; a++) { - ma = give_current_material(ob, a); - if (ma) { - for (b = 0; b < MAX_MTEX; b++) { - if (ma->mtex[b] && ma->mtex[b]->tex) { - tex = ma->mtex[b]->tex; - if (tex->type == TEX_IMAGE && tex->ima) { - ImBuf *ibuf = BKE_image_acquire_ibuf(tex->ima, NULL, NULL); - - /* texturespace */ - space = 1.0; - if (ob->type == OB_MESH) { - float size[3]; - BKE_mesh_texspace_get(ob->data, NULL, NULL, size); - space = size[0] / size[1]; - } - else if (ELEM(ob->type, OB_CURVE, OB_FONT, OB_SURF)) { - float size[3]; - BKE_curve_texspace_get(ob->data, NULL, NULL, size); - space = size[0] / size[1]; - } - - x = ibuf->x / space; - y = ibuf->y; - - if (x > y) ob->size[0] = ob->size[1] * x / y; - else ob->size[1] = ob->size[0] * y / x; - - done = true; - DAG_id_tag_update(&ob->id, OB_RECALC_OB); - - BKE_image_release_ibuf(tex->ima, ibuf, NULL); - } - } - if (done) break; - } - } - if (done) break; - } - } - } - -} - static const EnumPropertyItem *object_mode_set_itemsf( bContext *C, PointerRNA *UNUSED(ptr), PropertyRNA *UNUSED(prop), bool *r_free) { @@ -1680,12 +1596,28 @@ static int object_mode_set_poll(bContext *C) static int object_mode_set_exec(bContext *C, wmOperator *op) { + bool use_submode = STREQ(op->idname, "OBJECT_OT_mode_set_or_submode"); Object *ob = CTX_data_active_object(C); bGPdata *gpd = CTX_data_gpencil_data(C); eObjectMode mode = RNA_enum_get(op->ptr, "mode"); eObjectMode restore_mode = (ob) ? ob->mode : OB_MODE_OBJECT; const bool toggle = RNA_boolean_get(op->ptr, "toggle"); + if (use_submode) { + /* When not changing modes use submodes, see: T55162. */ + if (toggle == false) { + if (mode == restore_mode) { + switch (mode) { + case OB_MODE_EDIT: + WM_menu_name_call(C, "VIEW3D_MT_edit_mesh_select_mode", WM_OP_INVOKE_REGION_WIN); + return OPERATOR_INTERFACE; + default: + break; + } + } + } + } + if (gpd) { /* GP Mode is not bound to a specific object. Therefore, * we don't want it to be actually saved on any objects, @@ -1760,453 +1692,409 @@ void OBJECT_OT_mode_set(wmOperatorType *ot) RNA_def_property_flag(prop, PROP_SKIP_SAVE); } -/************************ Game Properties ***********************/ - -static int game_property_new_exec(bContext *C, wmOperator *op) +void OBJECT_OT_mode_set_or_submode(wmOperatorType *ot) { - Object *ob = CTX_data_active_object(C); - bProperty *prop; - char name[MAX_NAME]; - int type = RNA_enum_get(op->ptr, "type"); - - prop = BKE_bproperty_new(type); - BLI_addtail(&ob->prop, prop); - - RNA_string_get(op->ptr, "name", name); - if (name[0] != '\0') { - BLI_strncpy(prop->name, name, sizeof(prop->name)); - } - - BLI_uniquename(&ob->prop, prop, DATA_("Property"), '.', offsetof(bProperty, name), sizeof(prop->name)); - - WM_event_add_notifier(C, NC_LOGIC, NULL); - return OPERATOR_FINISHED; -} - + PropertyRNA *prop; -void OBJECT_OT_game_property_new(wmOperatorType *ot) -{ /* identifiers */ - ot->name = "New Game Property"; - ot->description = "Create a new property available to the game engine"; - ot->idname = "OBJECT_OT_game_property_new"; + ot->name = "Set Object Mode or Submode"; + ot->description = "Sets the object interaction mode"; + ot->idname = "OBJECT_OT_mode_set_or_submode"; /* api callbacks */ - ot->exec = game_property_new_exec; - ot->poll = ED_operator_object_active_editable; + ot->exec = object_mode_set_exec; + + ot->poll = object_mode_set_poll; //ED_operator_object_active_editable; /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + ot->flag = 0; /* no register/undo here, leave it to operators being called */ + + ot->prop = RNA_def_enum(ot->srna, "mode", rna_enum_object_mode_items, OB_MODE_OBJECT, "Mode", ""); + RNA_def_enum_funcs(ot->prop, object_mode_set_itemsf); + RNA_def_property_flag(ot->prop, PROP_SKIP_SAVE); - RNA_def_enum(ot->srna, "type", rna_enum_gameproperty_type_items, GPROP_FLOAT, "Type", "Type of game property to add"); - RNA_def_string(ot->srna, "name", NULL, MAX_NAME, "Name", "Name of the game property to add"); + prop = RNA_def_boolean(ot->srna, "toggle", 0, "Toggle", ""); + RNA_def_property_flag(prop, PROP_SKIP_SAVE); } -static int game_property_remove_exec(bContext *C, wmOperator *op) +bool ED_object_editmode_calc_active_center(Object *obedit, const bool select_only, float r_center[3]) { - Object *ob = CTX_data_active_object(C); - bProperty *prop; - int index = RNA_int_get(op->ptr, "index"); - - if (!ob) - return OPERATOR_CANCELLED; + switch (obedit->type) { + case OB_MESH: + { + BMEditMesh *em = BKE_editmesh_from_object(obedit); + BMEditSelection ese; - prop = BLI_findlink(&ob->prop, index); + if (BM_select_history_active_get(em->bm, &ese)) { + BM_editselection_center(&ese, r_center); + return true; + } + break; + } + case OB_ARMATURE: + { + bArmature *arm = obedit->data; + EditBone *ebo = arm->act_edbone; - if (prop) { - BLI_remlink(&ob->prop, prop); - BKE_bproperty_free(prop); + if (ebo && (!select_only || (ebo->flag & (BONE_SELECTED | BONE_ROOTSEL)))) { + copy_v3_v3(r_center, ebo->head); + return true; + } - WM_event_add_notifier(C, NC_LOGIC, NULL); - return OPERATOR_FINISHED; - } - else { - return OPERATOR_CANCELLED; - } -} + break; + } + case OB_CURVE: + case OB_SURF: + { + Curve *cu = obedit->data; -void OBJECT_OT_game_property_remove(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Remove Game Property"; - ot->description = "Remove game property"; - ot->idname = "OBJECT_OT_game_property_remove"; + if (ED_curve_active_center(cu, r_center)) { + return true; + } + break; + } + case OB_MBALL: + { + MetaBall *mb = obedit->data; + MetaElem *ml_act = mb->lastelem; - /* api callbacks */ - ot->exec = game_property_remove_exec; - ot->poll = ED_operator_object_active_editable; + if (ml_act && (!select_only || (ml_act->flag & SELECT))) { + copy_v3_v3(r_center, &ml_act->x); + return true; + } + break; + } + case OB_LATTICE: + { + BPoint *actbp = BKE_lattice_active_point_get(obedit->data); - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + if (actbp) { + copy_v3_v3(r_center, actbp->vec); + return true; + } + break; + } + } - RNA_def_int(ot->srna, "index", 0, 0, INT_MAX, "Index", "Property index to remove ", 0, INT_MAX); + return false; } -#define GAME_PROPERTY_MOVE_UP 1 -#define GAME_PROPERTY_MOVE_DOWN -1 - -static int game_property_move(bContext *C, wmOperator *op) +static int move_to_collection_poll(bContext *C) { - Object *ob = CTX_data_active_object(C); - bProperty *prop; - bProperty *otherprop = NULL; - const int index = RNA_int_get(op->ptr, "index"); - const int dir = RNA_enum_get(op->ptr, "direction"); + if (CTX_wm_space_outliner(C) != NULL) { + return ED_outliner_collections_editor_poll(C); + } + else { + return ED_operator_object_active_editable(C); + } +} - if (ob == NULL) +static int move_to_collection_exec(bContext *C, wmOperator *op) +{ + Main *bmain = CTX_data_main(C); + Scene *scene = CTX_data_scene(C); + PropertyRNA *prop = RNA_struct_find_property(op->ptr, "collection_index"); + const bool is_link = STREQ(op->idname, "OBJECT_OT_link_to_collection"); + const bool is_new = RNA_boolean_get(op->ptr, "is_new"); + Collection *collection; + ListBase objects = {NULL}; + + if (!RNA_property_is_set(op->ptr, prop)) { + BKE_report(op->reports, RPT_ERROR, "No collection selected"); return OPERATOR_CANCELLED; + } - prop = BLI_findlink(&ob->prop, index); - /* invalid index */ - if (prop == NULL) + int collection_index = RNA_property_int_get(op->ptr, prop); + collection = BKE_collection_from_index(CTX_data_scene(C), collection_index); + if (collection == NULL) { + BKE_report(op->reports, RPT_ERROR, "Unexpected error, collection not found"); return OPERATOR_CANCELLED; - - if (dir == GAME_PROPERTY_MOVE_UP) { - otherprop = prop->prev; } - else if (dir == GAME_PROPERTY_MOVE_DOWN) { - otherprop = prop->next; + + if (CTX_wm_space_outliner(C) != NULL) { + ED_outliner_selected_objects_get(C, &objects); } else { - BLI_assert(0); + CTX_DATA_BEGIN (C, Object *, ob, selected_objects) + { + BLI_addtail(&objects, BLI_genericNodeN(ob)); + } + CTX_DATA_END; } - if (prop && otherprop) { - BLI_listbase_swaplinks(&ob->prop, prop, otherprop); - - WM_event_add_notifier(C, NC_LOGIC, NULL); - return OPERATOR_FINISHED; + if (is_new) { + char new_collection_name[MAX_NAME]; + RNA_string_get(op->ptr, "new_collection_name", new_collection_name); + collection = BKE_collection_add(bmain, collection, new_collection_name); } - else { + + Object *single_object = BLI_listbase_is_single(&objects) ? + ((LinkData *)objects.first)->data : NULL; + + if ((single_object != NULL) && + is_link && + BLI_findptr(&collection->gobject, single_object, offsetof(CollectionObject, ob))) + { + BKE_reportf(op->reports, RPT_ERROR, "%s already in %s", single_object->id.name + 2, collection->id.name + 2); + BLI_freelistN(&objects); return OPERATOR_CANCELLED; } -} -void OBJECT_OT_game_property_move(wmOperatorType *ot) -{ - static const EnumPropertyItem direction_property_move[] = { - {GAME_PROPERTY_MOVE_UP, "UP", 0, "Up", ""}, - {GAME_PROPERTY_MOVE_DOWN, "DOWN", 0, "Down", ""}, - {0, NULL, 0, NULL, NULL} - }; - PropertyRNA *prop; - - /* identifiers */ - ot->name = "Move Game Property"; - ot->description = "Move game property"; - ot->idname = "OBJECT_OT_game_property_move"; + for (LinkData *link = objects.first; link; link = link->next) { + Object *ob = link->data; - /* api callbacks */ - ot->exec = game_property_move; - ot->poll = ED_operator_object_active_editable; + if (!is_link) { + BKE_collection_object_move(bmain, scene, collection, NULL, ob); + } + else { + BKE_collection_object_add(bmain, collection, ob); + } + } + BLI_freelistN(&objects); - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + BKE_reportf(op->reports, + RPT_INFO, + "%s %s to %s", + (single_object != NULL) ? single_object->id.name + 2 : "Objects", + is_link ? "linked" : "moved", + collection->id.name + 2); - /* properties */ - prop = RNA_def_int(ot->srna, "index", 0, 0, INT_MAX, "Index", "Property index to move", 0, INT_MAX); - RNA_def_property_flag(prop, PROP_HIDDEN); - RNA_def_enum(ot->srna, "direction", direction_property_move, 0, "Direction", - "Direction for moving the property"); -} + DEG_relations_tag_update(bmain); + DEG_id_tag_update(&scene->id, DEG_TAG_COPY_ON_WRITE | DEG_TAG_SELECT_UPDATE); -#undef GAME_PROPERTY_MOVE_UP -#undef GAME_PROPERTY_MOVE_DOWN + WM_event_add_notifier(C, NC_SCENE | ND_LAYER, scene); + WM_event_add_notifier(C, NC_SCENE | ND_OB_ACTIVE, scene); + WM_event_add_notifier(C, NC_SCENE | ND_LAYER_CONTENT, scene); -#define COPY_PROPERTIES_REPLACE 1 -#define COPY_PROPERTIES_MERGE 2 -#define COPY_PROPERTIES_COPY 3 + return OPERATOR_FINISHED; +} -static const EnumPropertyItem game_properties_copy_operations[] = { - {COPY_PROPERTIES_REPLACE, "REPLACE", 0, "Replace Properties", ""}, - {COPY_PROPERTIES_MERGE, "MERGE", 0, "Merge Properties", ""}, - {COPY_PROPERTIES_COPY, "COPY", 0, "Copy a Property", ""}, - {0, NULL, 0, NULL, NULL} +struct MoveToCollectionData { + struct MoveToCollectionData *next, *prev; + int index; + struct Collection *collection; + struct ListBase submenus; + PointerRNA ptr; + struct wmOperatorType *ot; }; -static const EnumPropertyItem *gameprops_itemf(bContext *C, PointerRNA *UNUSED(ptr), PropertyRNA *UNUSED(prop), bool *r_free) +static int move_to_collection_menus_create(wmOperator *op, MoveToCollectionData *menu) { - Object *ob = ED_object_active_context(C); - EnumPropertyItem tmp = {0, "", 0, "", ""}; - EnumPropertyItem *item = NULL; - bProperty *prop; - int a, totitem = 0; - - if (!ob) - return DummyRNA_NULL_items; + int index = menu->index; + for (CollectionChild *child = menu->collection->children.first; + child != NULL; + child = child->next) + { + Collection *collection = child->collection; + MoveToCollectionData *submenu = MEM_callocN(sizeof(MoveToCollectionData), + "MoveToCollectionData submenu - expected memleak"); + BLI_addtail(&menu->submenus, submenu); + submenu->collection = collection; + submenu->index = ++index; + index = move_to_collection_menus_create(op, submenu); + submenu->ot = op->type; + } + return index; +} - for (a = 1, prop = ob->prop.first; prop; prop = prop->next, a++) { - tmp.value = a; - tmp.identifier = prop->name; - tmp.name = prop->name; - RNA_enum_item_add(&item, &totitem, &tmp); +static void move_to_collection_menus_free_recursive(MoveToCollectionData *menu) +{ + for (MoveToCollectionData *submenu = menu->submenus.first; + submenu != NULL; + submenu = submenu->next) + { + move_to_collection_menus_free_recursive(submenu); } + BLI_freelistN(&menu->submenus); +} - RNA_enum_item_end(&item, &totitem); - *r_free = true; +static void move_to_collection_menus_free(MoveToCollectionData **menu) +{ + if (*menu == NULL) { + return; + } - return item; + move_to_collection_menus_free_recursive(*menu); + MEM_freeN(*menu); + *menu = NULL; } -static int game_property_copy_exec(bContext *C, wmOperator *op) +static void move_to_collection_menu_create(bContext *UNUSED(C), uiLayout *layout, void *menu_v) { - Object *ob = ED_object_active_context(C); - bProperty *prop; - int type = RNA_enum_get(op->ptr, "operation"); - int propid = RNA_enum_get(op->ptr, "property"); - - if (propid > 0) { /* copy */ - prop = BLI_findlink(&ob->prop, propid - 1); + MoveToCollectionData *menu = menu_v; + const char *name; - if (prop) { - CTX_DATA_BEGIN(C, Object *, ob_iter, selected_editable_objects) - { - if (ob != ob_iter) - BKE_bproperty_object_set(ob_iter, prop); - } CTX_DATA_END; - } + if (menu->collection->flag & COLLECTION_IS_MASTER) { + name = IFACE_("Scene Collection"); } - else { - CTX_DATA_BEGIN(C, Object *, ob_iter, selected_editable_objects) - { - if (ob != ob_iter) { - if (type == COPY_PROPERTIES_REPLACE) { - BKE_bproperty_copy_list(&ob_iter->prop, &ob->prop); - } - else { - /* merge - the default when calling with no argument */ - for (prop = ob->prop.first; prop; prop = prop->next) { - BKE_bproperty_object_set(ob_iter, prop); - } - } - } - } - CTX_DATA_END; + name = menu->collection->id.name + 2; } - return OPERATOR_FINISHED; -} + uiItemIntO(layout, + name, + ICON_NONE, + menu->ot->idname, + "collection_index", + menu->index); + uiItemS(layout); -void OBJECT_OT_game_property_copy(wmOperatorType *ot) -{ - PropertyRNA *prop; - /* identifiers */ - ot->name = "Copy Game Property"; - ot->idname = "OBJECT_OT_game_property_copy"; - ot->description = "Copy/merge/replace a game property from active object to all selected objects"; + for (MoveToCollectionData *submenu = menu->submenus.first; + submenu != NULL; + submenu = submenu->next) + { + move_to_collection_menus_items(layout, submenu); + } - /* api callbacks */ - ot->exec = game_property_copy_exec; - ot->poll = ED_operator_object_active_editable; + uiItemS(layout); - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + WM_operator_properties_create_ptr(&menu->ptr, menu->ot); + RNA_int_set(&menu->ptr, "collection_index", menu->index); + RNA_boolean_set(&menu->ptr, "is_new", true); - RNA_def_enum(ot->srna, "operation", game_properties_copy_operations, 3, "Operation", ""); - prop = RNA_def_enum(ot->srna, "property", DummyRNA_NULL_items, 0, "Property", "Properties to copy"); - RNA_def_property_flag(prop, PROP_SKIP_SAVE | PROP_ENUM_NO_TRANSLATE); - RNA_def_enum_funcs(prop, gameprops_itemf); - ot->prop = prop; + uiItemFullO_ptr(layout, + menu->ot, + "New Collection", + ICON_ZOOMIN, + menu->ptr.data, + WM_OP_INVOKE_DEFAULT, + 0, + NULL); } -static int game_property_clear_exec(bContext *C, wmOperator *UNUSED(op)) +static void move_to_collection_menus_items(uiLayout *layout, MoveToCollectionData *menu) { - CTX_DATA_BEGIN(C, Object *, ob_iter, selected_editable_objects) - { - BKE_bproperty_free_list(&ob_iter->prop); + if (BLI_listbase_is_empty(&menu->submenus)) { + uiItemIntO(layout, + menu->collection->id.name + 2, + ICON_NONE, + menu->ot->idname, + "collection_index", + menu->index); + } + else { + uiItemMenuF(layout, + menu->collection->id.name + 2, + ICON_NONE, + move_to_collection_menu_create, + menu); } - CTX_DATA_END; - - WM_event_add_notifier(C, NC_LOGIC, NULL); - return OPERATOR_FINISHED; } -void OBJECT_OT_game_property_clear(wmOperatorType *ot) + +/* This is allocated statically because we need this available for the menus creation callback. */ +static MoveToCollectionData *master_collection_menu = NULL; + +static int move_to_collection_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) { - /* identifiers */ - ot->name = "Clear Game Properties"; - ot->idname = "OBJECT_OT_game_property_clear"; - ot->description = "Remove all game properties from all selected objects"; + Scene *scene = CTX_data_scene(C); - /* api callbacks */ - ot->exec = game_property_clear_exec; - ot->poll = ED_operator_object_active_editable; + /* Reset the menus data for the current master collection, and free previously allocated data. */ + move_to_collection_menus_free(&master_collection_menu); - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; -} + PropertyRNA *prop; + prop = RNA_struct_find_property(op->ptr, "collection_index"); + if (RNA_property_is_set(op->ptr, prop)) { + int collection_index = RNA_property_int_get(op->ptr, prop); -/************************ Copy Logic Bricks ***********************/ + if (RNA_boolean_get(op->ptr, "is_new")) { + prop = RNA_struct_find_property(op->ptr, "new_collection_name"); + if (!RNA_property_is_set(op->ptr, prop)) { + char name[MAX_NAME]; + Collection *collection; -static int logicbricks_copy_exec(bContext *C, wmOperator *UNUSED(op)) -{ - Object *ob = ED_object_active_context(C); + collection = BKE_collection_from_index(scene, collection_index); + BKE_collection_new_name_get(collection, name); - CTX_DATA_BEGIN(C, Object *, ob_iter, selected_editable_objects) - { - if (ob != ob_iter) { - /* first: free all logic */ - free_sensors(&ob_iter->sensors); - unlink_controllers(&ob_iter->controllers); - free_controllers(&ob_iter->controllers); - unlink_actuators(&ob_iter->actuators); - free_actuators(&ob_iter->actuators); - - /* now copy it, this also works without logicbricks! */ - clear_sca_new_poins_ob(ob); - copy_sensors(&ob_iter->sensors, &ob->sensors, 0); - copy_controllers(&ob_iter->controllers, &ob->controllers, 0); - copy_actuators(&ob_iter->actuators, &ob->actuators, 0); - set_sca_new_poins_ob(ob_iter); - - /* some menu settings */ - ob_iter->scavisflag = ob->scavisflag; - ob_iter->scaflag = ob->scaflag; - - /* set the initial state */ - ob_iter->state = ob->state; - ob_iter->init_state = ob->init_state; - - if (ob_iter->totcol == ob->totcol) { - ob_iter->actcol = ob->actcol; - WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob_iter); + RNA_property_string_set(op->ptr, prop, name); + return WM_operator_props_dialog_popup(C, op, 10 * UI_UNIT_X, 5 * UI_UNIT_Y); } } + return move_to_collection_exec(C, op); } - CTX_DATA_END; - WM_event_add_notifier(C, NC_LOGIC, NULL); + Collection *master_collection = BKE_collection_master(scene); - return OPERATOR_FINISHED; -} + /* We need the data to be allocated so it's available during menu drawing. + * Technically we could use wmOperator->customdata. However there is no free callback + * called to an operator that exit with OPERATOR_INTERFACE to launch a menu. + * + * So we are left with a memory that will necessarily leak. It's a small leak though.*/ + if (master_collection_menu == NULL) { + master_collection_menu = MEM_callocN(sizeof(MoveToCollectionData), + "MoveToCollectionData menu - expected eventual memleak"); + } -void OBJECT_OT_logic_bricks_copy(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Copy Logic Bricks to Selected"; - ot->description = "Copy logic bricks to other selected objects"; - ot->idname = "OBJECT_OT_logic_bricks_copy"; + master_collection_menu->collection = master_collection; + master_collection_menu->ot = op->type; + move_to_collection_menus_create(op, master_collection_menu); - /* api callbacks */ - ot->exec = logicbricks_copy_exec; - ot->poll = ED_operator_object_active_editable; + uiPopupMenu *pup; + uiLayout *layout; - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; -} + /* Build the menus. */ + const char *title = CTX_IFACE_(op->type->translation_context, op->type->name); + pup = UI_popup_menu_begin(C, title, ICON_NONE); + layout = UI_popup_menu_layout(pup); -static int game_physics_copy_exec(bContext *C, wmOperator *UNUSED(op)) -{ - Object *ob = ED_object_active_context(C); + uiLayoutSetOperatorContext(layout, WM_OP_INVOKE_DEFAULT); - CTX_DATA_BEGIN(C, Object *, ob_iter, selected_editable_objects) - { - if (ob != ob_iter) { - ob_iter->gameflag = ob->gameflag; - ob_iter->gameflag2 = ob->gameflag2; - ob_iter->inertia = ob->inertia; - ob_iter->formfactor = ob->formfactor; - ob_iter->damping = ob->damping; - ob_iter->rdamping = ob->rdamping; - ob_iter->min_vel = ob->min_vel; - ob_iter->max_vel = ob->max_vel; - ob_iter->min_angvel = ob->min_angvel; - ob_iter->max_angvel = ob->max_angvel; - ob_iter->obstacleRad = ob->obstacleRad; - ob_iter->mass = ob->mass; - copy_v3_v3(ob_iter->anisotropicFriction, ob->anisotropicFriction); - ob_iter->collision_boundtype = ob->collision_boundtype; - ob_iter->margin = ob->margin; - ob_iter->bsoft = copy_bulletsoftbody(ob->bsoft, 0); - if (ob->restrictflag & OB_RESTRICT_RENDER) - ob_iter->restrictflag |= OB_RESTRICT_RENDER; - else - ob_iter->restrictflag &= ~OB_RESTRICT_RENDER; - - ob_iter->col_group = ob->col_group; - ob_iter->col_mask = ob->col_mask; - } - } - CTX_DATA_END; + move_to_collection_menu_create(C, layout, master_collection_menu); - return OPERATOR_FINISHED; + UI_popup_menu_end(C, pup); + + return OPERATOR_INTERFACE; } -void OBJECT_OT_game_physics_copy(struct wmOperatorType *ot) +void OBJECT_OT_move_to_collection(wmOperatorType *ot) { + PropertyRNA *prop; + /* identifiers */ - ot->name = "Copy Game Physics Properties to Selected"; - ot->description = "Copy game physics properties to other selected objects"; - ot->idname = "OBJECT_OT_game_physics_copy"; + ot->name = "Move to Collection"; + ot->description = "Move objects to a scene collection"; + ot->idname = "OBJECT_OT_move_to_collection"; /* api callbacks */ - ot->exec = game_physics_copy_exec; - ot->poll = ED_operator_object_active_editable; + ot->exec = move_to_collection_exec; + ot->invoke = move_to_collection_invoke; + ot->poll = move_to_collection_poll; /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; -} -/* generic utility function */ + prop = RNA_def_int(ot->srna, "collection_index", COLLECTION_INVALID_INDEX, COLLECTION_INVALID_INDEX, INT_MAX, + "Collection Index", "Index of the collection to move to", 0, INT_MAX); + RNA_def_property_flag(prop, PROP_SKIP_SAVE | PROP_HIDDEN); + prop = RNA_def_boolean(ot->srna, "is_new", false, "New", "Move objects to a new collection"); + RNA_def_property_flag(prop, PROP_SKIP_SAVE | PROP_HIDDEN); + prop = RNA_def_string(ot->srna, "new_collection_name", NULL, MAX_NAME, "Name", + "Name of the newly added collection"); + RNA_def_property_flag(prop, PROP_SKIP_SAVE); +} -bool ED_object_editmode_calc_active_center(Object *obedit, const bool select_only, float r_center[3]) +void OBJECT_OT_link_to_collection(wmOperatorType *ot) { - switch (obedit->type) { - case OB_MESH: - { - BMEditMesh *em = BKE_editmesh_from_object(obedit); - BMEditSelection ese; - - if (BM_select_history_active_get(em->bm, &ese)) { - BM_editselection_center(&ese, r_center); - return true; - } - break; - } - case OB_ARMATURE: - { - bArmature *arm = obedit->data; - EditBone *ebo = arm->act_edbone; - - if (ebo && (!select_only || (ebo->flag & (BONE_SELECTED | BONE_ROOTSEL)))) { - copy_v3_v3(r_center, ebo->head); - return true; - } - - break; - } - case OB_CURVE: - case OB_SURF: - { - Curve *cu = obedit->data; + PropertyRNA *prop; - if (ED_curve_active_center(cu, r_center)) { - return true; - } - break; - } - case OB_MBALL: - { - MetaBall *mb = obedit->data; - MetaElem *ml_act = mb->lastelem; + /* identifiers */ + ot->name = "Link to Collection"; + ot->description = "Link objects to a collection"; + ot->idname = "OBJECT_OT_link_to_collection"; - if (ml_act && (!select_only || (ml_act->flag & SELECT))) { - copy_v3_v3(r_center, &ml_act->x); - return true; - } - break; - } - case OB_LATTICE: - { - BPoint *actbp = BKE_lattice_active_point_get(obedit->data); + /* api callbacks */ + ot->exec = move_to_collection_exec; + ot->invoke = move_to_collection_invoke; + ot->poll = move_to_collection_poll; - if (actbp) { - copy_v3_v3(r_center, actbp->vec); - return true; - } - break; - } - } + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - return false; + prop = RNA_def_int(ot->srna, "collection_index", COLLECTION_INVALID_INDEX, COLLECTION_INVALID_INDEX, INT_MAX, + "Collection Index", "Index of the collection to move to", 0, INT_MAX); + RNA_def_property_flag(prop, PROP_SKIP_SAVE | PROP_HIDDEN); + prop = RNA_def_boolean(ot->srna, "is_new", false, "New", "Move objects to a new collection"); + RNA_def_property_flag(prop, PROP_SKIP_SAVE | PROP_HIDDEN); + prop = RNA_def_string(ot->srna, "new_collection_name", NULL, MAX_NAME, "Name", + "Name of the newly added collection"); + RNA_def_property_flag(prop, PROP_SKIP_SAVE); } |