Merge branch 'master' into blender2.8
[blender.git] / source / blender / editors / object / object_edit.c
index 056ce6b..45ad4bf 100644 (file)
@@ -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"
 #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"
 #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"
 /* 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)) {}
@@ -135,192 +147,6 @@ Object *ED_object_active_context(bContext *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;
-       const bool select = RNA_boolean_get(op->ptr, "select");
-
-       /* 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;
-                       changed = true;
-               }
-       }
-       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);
-       }
-
-       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->idname = "OBJECT_OT_hide_view_clear";
-
-       /* api callbacks */
-       ot->exec = object_hide_view_clear_exec;
-       ot->poll = ED_operator_view3d_active;
-
-       /* flags */
-       ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
-
-       RNA_def_boolean(ot->srna, "select", true, "Select", "");
-}
-
-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;
-       const bool unselected = RNA_boolean_get(op->ptr, "unselected");
-
-       CTX_DATA_BEGIN(C, Base *, base, visible_bases)
-       {
-               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);
-                               }
-                       }
-               }
-               else {
-                       if (!(base->flag & SELECT)) {
-                               base->object->restrictflag |= OB_RESTRICT_VIEW;
-                               changed = true;
-                               if (base == BASACT) {
-                                       ED_base_object_activate(C, NULL);
-                               }
-                       }
-               }
-       }
-       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);
-
-       }
-
-       return OPERATOR_FINISHED;
-}
-
-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->idname = "OBJECT_OT_hide_view_set";
-
-       /* api callbacks */
-       ot->exec = object_hide_view_set_exec;
-       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");
-
-}
-
-/* 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))
-{
-       bool changed = false;
-
-       /* 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;
-               }
-       }
-       CTX_DATA_END;
-
-       if (changed)
-               WM_event_add_notifier(C, NC_SPACE | ND_SPACE_OUTLINER, NULL);
-
-       return OPERATOR_FINISHED;
-}
-
-void OBJECT_OT_hide_render_clear(wmOperatorType *ot)
-{
-
-       /* 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";
-
-       /* api callbacks */
-       ot->exec = object_hide_render_clear_exec;
-       ot->poll = ED_operator_view3d_active;
-
-       /* flags */
-       ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
-}
-
-static int object_hide_render_set_exec(bContext *C, wmOperator *op)
-{
-       const bool unselected = RNA_boolean_get(op->ptr, "unselected");
-
-       CTX_DATA_BEGIN(C, Base *, base, visible_bases)
-       {
-               if (!unselected) {
-                       if (base->flag & SELECT) {
-                               base->object->restrictflag |= OB_RESTRICT_RENDER;
-                       }
-               }
-               else {
-                       if (!(base->flag & SELECT)) {
-                               base->object->restrictflag |= OB_RESTRICT_RENDER;
-                       }
-               }
-       }
-       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)
-{
-       /* 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";
-
-       /* api callbacks */
-       ot->exec = object_hide_render_set_exec;
-       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");
-}
-
 /* ******************* toggle editmode operator  ***************** */
 
 static bool mesh_needs_keyindex(Main *bmain, const Mesh *me)
@@ -391,7 +217,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 +281,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 +293,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 +304,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 +324,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 +346,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 +363,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 +413,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 +491,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 +518,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 +597,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,7 +648,7 @@ 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;
@@ -891,30 +658,23 @@ static void copy_attr(Main *bmain, Scene *scene, View3D *v3d, short event)
 
        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 +713,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 +753,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 +769,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 +788,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,7 +804,7 @@ 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);
                                                }
                                        }
                                }
@@ -1129,18 +865,18 @@ static void copy_attr(Main *bmain, Scene *scene, View3D *v3d, short event)
        }
 
        if (do_depgraph_update)
-               DAG_relations_tag_update(bmain);
+               DEG_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 +922,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 +991,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 +1005,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 +1235,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 +1249,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 +1300,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 +1365,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 +1461,413 @@ 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
+#define COLLECTION_INVALID_INDEX -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(CTX_data_main(C));
+       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");
+       MoveToCollectionData *menu = menu_v;
+       const char *name;
 
-       if (propid > 0) { /* copy */
-               prop = BLI_findlink(&ob->prop, propid - 1);
-
-               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);
 }
+
+#undef COLLECTION_INVALID_INDEX