Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--source/blender/blenkernel/intern/collection.c48
-rw-r--r--source/blender/editors/object/object_edit.c245
-rw-r--r--source/blender/editors/object/object_intern.h2
-rw-r--r--source/blender/editors/object/object_ops.c4
4 files changed, 287 insertions, 12 deletions
diff --git a/source/blender/blenkernel/intern/collection.c b/source/blender/blenkernel/intern/collection.c
index fb27249402b..880d28970c0 100644
--- a/source/blender/blenkernel/intern/collection.c
+++ b/source/blender/blenkernel/intern/collection.c
@@ -458,19 +458,11 @@ bool BKE_collection_object_remove(Main *bmain, ID *owner_id, SceneCollection *sc
}
/**
- * Move object from a collection into another
- */
-void BKE_collection_object_move(ID *owner_id, SceneCollection *sc_dst, SceneCollection *sc_src, Object *ob)
-{
- if (BKE_collection_object_add(owner_id, sc_dst, ob)) {
- BKE_collection_object_remove(NULL, owner_id, sc_src, ob, false);
- }
-}
-
-/**
* Remove object from all collections of scene
+ * \param scene_collection_skip: Don't remove base from this collection.
*/
-bool BKE_collections_object_remove(Main *bmain, ID *owner_id, Object *ob, const bool free_us)
+static bool collections_object_remove_ex(Main *bmain, ID *owner_id, Object *ob, const bool free_us,
+ SceneCollection *scene_collection_skip)
{
bool removed = false;
if (GS(owner_id->name) == ID_SCE) {
@@ -482,12 +474,44 @@ bool BKE_collections_object_remove(Main *bmain, ID *owner_id, Object *ob, const
FOREACH_SCENE_COLLECTION_BEGIN(owner_id, sc)
{
- removed |= BKE_collection_object_remove(bmain, owner_id, sc, ob, free_us);
+ if (sc != scene_collection_skip) {
+ removed |= BKE_collection_object_remove(bmain, owner_id, sc, ob, free_us);
+ }
}
FOREACH_SCENE_COLLECTION_END;
return removed;
}
+/**
+ * Remove object from all collections of scene
+ */
+bool BKE_collections_object_remove(Main *bmain, ID *owner_id, Object *ob, const bool free_us)
+{
+ return collections_object_remove_ex(bmain, owner_id, ob, free_us, NULL);
+}
+
+/**
+ * Move object from a collection into another
+ *
+ * If source collection is NULL move it from all the existing collections.
+ */
+void BKE_collection_object_move(ID *owner_id, SceneCollection *sc_dst, SceneCollection *sc_src, Object *ob)
+{
+ /* In both cases we first add the object, then remove it from the other collections.
+ * Otherwise we lose the original base and whether it was active and selected. */
+ if (sc_src != NULL) {
+ if (BKE_collection_object_add(owner_id, sc_dst, ob)) {
+ BKE_collection_object_remove(NULL, owner_id, sc_src, ob, false);
+ }
+ }
+ else {
+ /* Adding will fail if object is already in collection.
+ * However we still need to remove it from the other collections. */
+ BKE_collection_object_add(owner_id, sc_dst, ob);
+ collections_object_remove_ex(NULL, owner_id, ob, false, sc_dst);
+ }
+}
+
static void layer_collection_sync(LayerCollection *lc_dst, LayerCollection *lc_src)
{
lc_dst->flag = lc_src->flag;
diff --git a/source/blender/editors/object/object_edit.c b/source/blender/editors/object/object_edit.c
index 3e629c8d7f6..e60ddb98329 100644
--- a/source/blender/editors/object/object_edit.c
+++ b/source/blender/editors/object/object_edit.c
@@ -64,6 +64,7 @@
#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"
@@ -109,11 +110,16 @@
/* for menu/popup icons etc etc*/
#include "UI_interface.h"
+#include "UI_resources.h"
#include "WM_api.h"
#include "WM_types.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)) {}
@@ -2039,3 +2045,242 @@ bool ED_object_editmode_calc_active_center(Object *obedit, const bool select_onl
return false;
}
+
+#define COLLECTION_INVALID_INDEX -1
+
+static SceneCollection *scene_collection_from_index_recursive(SceneCollection *scene_collection, const int index, int *index_current)
+{
+ if (index == (*index_current)) {
+ return scene_collection;
+ }
+
+ (*index_current)++;
+
+ for (SceneCollection *scene_collection_iter = scene_collection->scene_collections.first;
+ scene_collection_iter != NULL;
+ scene_collection_iter = scene_collection_iter->next)
+ {
+ SceneCollection *nested = scene_collection_from_index_recursive(scene_collection_iter, index, index_current);
+ if (nested != NULL) {
+ return nested;
+ }
+ }
+ return NULL;
+}
+
+static SceneCollection *scene_collection_from_index(Scene *scene, const int index)
+{
+ int index_current = 0;
+ SceneCollection *master_collection = BKE_collection_master(&scene->id);
+ return scene_collection_from_index_recursive(master_collection, index, &index_current);
+}
+
+static int move_to_collection_exec(bContext *C, wmOperator *op)
+{
+ Scene *scene = CTX_data_scene(C);
+ PropertyRNA *prop = RNA_struct_find_property(op->ptr, "collection_index");
+ const bool is_add = RNA_boolean_get(op->ptr, "is_add");
+ SceneCollection *scene_collection;
+
+ if (!RNA_property_is_set(op->ptr, prop)) {
+ BKE_report(op->reports, RPT_ERROR, "No collection selected");
+ return OPERATOR_CANCELLED;
+ }
+
+ int collection_index = RNA_property_int_get(op->ptr, prop);
+ scene_collection = scene_collection_from_index(CTX_data_scene(C), collection_index);
+ if (scene_collection == NULL) {
+ BKE_report(op->reports, RPT_ERROR, "Unexpected error, collection not found");
+ return OPERATOR_CANCELLED;
+ }
+
+ Object *single_object = NULL;
+ CTX_DATA_BEGIN (C, Object *, ob, selected_objects)
+ {
+ if (single_object != NULL) {
+ single_object = NULL;
+ break;
+ }
+ else {
+ single_object = ob;
+ }
+ }
+ CTX_DATA_END;
+
+ if ((single_object != NULL) &&
+ is_add &&
+ BLI_findptr(&scene_collection->objects, single_object, offsetof(LinkData, data)))
+ {
+ BKE_reportf(op->reports, RPT_ERROR, "%s already in %s", single_object->id.name + 2, scene_collection->name);
+ return OPERATOR_CANCELLED;
+ }
+
+ CTX_DATA_BEGIN (C, Object *, ob, selected_objects)
+ {
+ if (!is_add) {
+ BKE_collection_object_move(&scene->id, scene_collection, NULL, ob);
+ }
+ else {
+ BKE_collection_object_add(&scene->id, scene_collection, ob);
+ }
+ }
+ CTX_DATA_END;
+
+ BKE_reportf(op->reports,
+ RPT_INFO,
+ "%s %s to %s",
+ (single_object != NULL) ? single_object->id.name + 2 : "Objects",
+ is_add ? "added" : "moved",
+ scene_collection->name);
+
+ DEG_relations_tag_update(CTX_data_main(C));
+ DEG_id_tag_update(&scene->id, 0);
+
+ 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);
+
+ return OPERATOR_FINISHED;
+}
+
+typedef struct MoveToCollectionData {
+ struct MoveToCollectionData *next, *prev;
+ int index;
+ struct SceneCollection *collection;
+ struct ListBase submenus;
+} MoveToCollectionData;
+
+static int move_to_collection_menus_create(MoveToCollectionData *menu)
+{
+ int index = menu->index;
+ for (SceneCollection *scene_collection = menu->collection->scene_collections.first;
+ scene_collection != NULL;
+ scene_collection = scene_collection->next)
+ {
+ MoveToCollectionData *submenu = MEM_callocN(sizeof(MoveToCollectionData),
+ "MoveToCollectionData submenu - expected memleak");
+ BLI_addtail(&menu->submenus, submenu);
+ submenu->collection = scene_collection;
+ submenu->index = ++index;
+ index = move_to_collection_menus_create(submenu);
+ }
+ return index;
+}
+
+static void move_to_collection_menus_free(MoveToCollectionData *menu)
+{
+ for (MoveToCollectionData *submenu = menu->submenus.first;
+ submenu != NULL;
+ submenu = submenu->next)
+ {
+ move_to_collection_menus_free(submenu);
+ }
+ BLI_freelistN(&menu->submenus);
+}
+
+static void move_to_collection_menu_create(bContext *UNUSED(C), uiLayout *layout, void *menu_v)
+{
+ MoveToCollectionData *menu = menu_v;
+
+ uiItemIntO(layout,
+ menu->collection->name,
+ ICON_NONE,
+ "OBJECT_OT_move_to_collection",
+ "collection_index",
+ menu->index);
+ uiItemS(layout);
+
+ for (MoveToCollectionData *submenu = menu->submenus.first;
+ submenu != NULL;
+ submenu = submenu->next)
+ {
+ move_to_collection_menus_items(layout, submenu);
+ }
+}
+
+static void move_to_collection_menus_items(uiLayout *layout, MoveToCollectionData *menu)
+{
+ if (BLI_listbase_is_empty(&menu->submenus)) {
+ uiItemIntO(layout,
+ menu->collection->name,
+ ICON_NONE,
+ "OBJECT_OT_move_to_collection",
+ "collection_index",
+ menu->index);
+ }
+ else {
+ uiItemMenuF(layout,
+ menu->collection->name,
+ ICON_NONE,
+ move_to_collection_menu_create,
+ menu);
+ }
+}
+
+static int move_to_collection_invoke(bContext *C, wmOperator *op, const wmEvent *event)
+{
+ PropertyRNA *prop = RNA_struct_find_property(op->ptr, "collection_index");
+ if (RNA_property_is_set(op->ptr, prop)) {
+ RNA_boolean_set(op->ptr, "is_add", event->ctrl);
+ return move_to_collection_exec(C, op);
+ }
+
+ SceneCollection *master_collection = BKE_collection_master(&CTX_data_scene(C)->id);
+
+ /* 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.*/
+ static MoveToCollectionData *master_collection_menu = NULL;
+
+ if (master_collection_menu == NULL) {
+ master_collection_menu = MEM_callocN(sizeof(MoveToCollectionData),
+ "MoveToCollectionData menu - expected memleak");
+ }
+
+ /* Reset the menus data for the current master collection, and free previously allocated data. */
+ move_to_collection_menus_free(master_collection_menu);
+ master_collection_menu->collection = master_collection;
+ move_to_collection_menus_create(master_collection_menu);
+
+ uiPopupMenu *pup;
+ uiLayout *layout;
+
+ /* Build the menus. */
+ pup = UI_popup_menu_begin(C, IFACE_("Move to Collection"), ICON_NONE);
+ layout = UI_popup_menu_layout(pup);
+ uiLayoutSetOperatorContext(layout, WM_OP_EXEC_DEFAULT);
+
+ move_to_collection_menus_items(layout, master_collection_menu);
+
+ UI_popup_menu_end(C, pup);
+
+ return OPERATOR_INTERFACE;
+}
+
+void OBJECT_OT_move_to_collection(wmOperatorType *ot)
+{
+ PropertyRNA *prop;
+
+ /* identifiers */
+ ot->name = "Move to Collection";
+ ot->description = "Move to a collection only (Ctrl to add)";
+ ot->idname = "OBJECT_OT_move_to_collection";
+
+ /* api callbacks */
+ ot->exec = move_to_collection_exec;
+ ot->invoke = move_to_collection_invoke;
+ ot->poll = ED_operator_object_active_editable;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+ 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 = RNA_def_boolean(ot->srna, "is_add", false, "Add", "Keep object in original collections as well");
+ RNA_def_property_flag(prop, PROP_SKIP_SAVE);
+}
+
+#undef COLLECTION_INVALID_INDEX
diff --git a/source/blender/editors/object/object_intern.h b/source/blender/editors/object/object_intern.h
index a3bb01b0f24..ef51452a983 100644
--- a/source/blender/editors/object/object_intern.h
+++ b/source/blender/editors/object/object_intern.h
@@ -95,6 +95,8 @@ void OBJECT_OT_game_property_move(struct wmOperatorType *ot);
void OBJECT_OT_logic_bricks_copy(struct wmOperatorType *ot);
void OBJECT_OT_game_physics_copy(struct wmOperatorType *ot);
+void OBJECT_OT_move_to_collection(struct wmOperatorType *ot);
+
/* object_select.c */
void OBJECT_OT_select_all(struct wmOperatorType *ot);
void OBJECT_OT_select_random(struct wmOperatorType *ot);
diff --git a/source/blender/editors/object/object_ops.c b/source/blender/editors/object/object_ops.c
index 59ba92eb1bf..9c198e996af 100644
--- a/source/blender/editors/object/object_ops.c
+++ b/source/blender/editors/object/object_ops.c
@@ -219,6 +219,8 @@ void ED_operatortypes_object(void)
WM_operatortype_append(OBJECT_OT_logic_bricks_copy);
WM_operatortype_append(OBJECT_OT_game_physics_copy);
+ WM_operatortype_append(OBJECT_OT_move_to_collection);
+
WM_operatortype_append(OBJECT_OT_shape_key_add);
WM_operatortype_append(OBJECT_OT_shape_key_remove);
WM_operatortype_append(OBJECT_OT_shape_key_clear);
@@ -423,6 +425,8 @@ void ED_keymap_object(wmKeyConfig *keyconf)
kmi = WM_keymap_add_item(keymap, "OBJECT_OT_subdivision_set", ZEROKEY + i, KM_PRESS, KM_CTRL, 0);
RNA_int_set(kmi->ptr, "level", i);
}
+
+ WM_keymap_add_item(keymap, "OBJECT_OT_move_to_collection", MKEY, KM_PRESS, 0, 0);
}
void ED_keymap_proportional_cycle(struct wmKeyConfig *UNUSED(keyconf), struct wmKeyMap *keymap)