diff --git a/source/blender/editors/object/object_edit.cc b/source/blender/editors/object/object_edit.cc index a768664f9f2..e80d18eb6c2 100644 --- a/source/blender/editors/object/object_edit.cc +++ b/source/blender/editors/object/object_edit.cc @@ -1915,22 +1915,39 @@ static bool move_to_collection_poll(bContext *C) } return ED_operator_objectmode(C); } +Collection *collection_from_enum_or_scene(const bContext *C, const wmOperator *op) +{ + Main *bmain = CTX_data_main(C); + PropertyRNA *prop; + + prop = RNA_struct_find_property(op->ptr, "collection"); + if (RNA_property_is_set(op->ptr, prop)) { + return static_cast( + BLI_findlink(&bmain->collections, RNA_enum_get(op->ptr, "collection"))); + } + + prop = RNA_struct_find_property(op->ptr, "scene"); + if (RNA_property_is_set(op->ptr, prop)) { + return static_cast(BLI_findlink(&bmain->scenes, RNA_enum_get(op->ptr, "scene"))) + ->master_collection; + } + return nullptr; +} 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"); - if (!RNA_property_is_set(op->ptr, prop)) { + Collection *collection = collection_from_enum_or_scene(C, op); + if (!collection) { BKE_report(op->reports, RPT_ERROR, "No collection selected"); return OPERATOR_CANCELLED; } - int collection_index = RNA_property_int_get(op->ptr, prop); - Collection *collection = BKE_collection_from_index(scene, collection_index); if (collection == nullptr) { BKE_report(op->reports, RPT_ERROR, "Unexpected error, collection not found"); return OPERATOR_CANCELLED; @@ -2013,104 +2030,97 @@ static int move_to_collection_exec(bContext *C, wmOperator *op) return OPERATOR_FINISHED; } +/* Name of the active move operation. Used only for menu drawing. */ +static const char *operation_name = nullptr; + +void move_to_collection_menu_new_item(uiLayout *layout, + const bool scene_collection, + const int enum_index) +{ + wmOperatorType *ot = WM_operatortype_find(operation_name, false); -struct MoveToCollectionData { - MoveToCollectionData *next, *prev; - int index; - Collection *collection; - ListBase submenus; PointerRNA ptr; - wmOperatorType *ot; -}; + WM_operator_properties_create_ptr(&ptr, ot); -static int move_to_collection_menus_create(wmOperator *op, MoveToCollectionData *menu) -{ - int index = menu->index; - LISTBASE_FOREACH (CollectionChild *, child, &menu->collection->children) { - Collection *collection = child->collection; - MoveToCollectionData *submenu = MEM_cnew(__func__); - 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; -} + RNA_boolean_set(&ptr, "is_new", true); -static void move_to_collection_menus_free_recursive(MoveToCollectionData *menu) -{ - LISTBASE_FOREACH (MoveToCollectionData *, submenu, &menu->submenus) { - move_to_collection_menus_free_recursive(submenu); - } - BLI_freelistN(&menu->submenus); -} - -static void move_to_collection_menus_free(MoveToCollectionData **menu) -{ - if (*menu == nullptr) { - return; - } - - move_to_collection_menus_free_recursive(*menu); - MEM_freeN(*menu); - *menu = nullptr; -} - -static void move_to_collection_menu_create(bContext *C, uiLayout *layout, void *menu_v) -{ - MoveToCollectionData *menu = static_cast(menu_v); - const char *name = BKE_collection_ui_name_get(menu->collection); - - 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_enum_set(&ptr, scene_collection ? "scene" : "collection", enum_index); uiItemFullO_ptr(layout, - menu->ot, + ot, CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "New Collection"), ICON_ADD, - static_cast(menu->ptr.data), + static_cast(ptr.data), WM_OP_INVOKE_DEFAULT, UI_ITEM_NONE, nullptr); - - uiItemS(layout); - - Scene *scene = CTX_data_scene(C); - const int icon = (menu->collection == scene->master_collection) ? - ICON_SCENE_DATA : - UI_icon_color_from_collection(menu->collection); - uiItemIntO(layout, name, icon, menu->ot->idname, "collection_index", menu->index); - - LISTBASE_FOREACH (MoveToCollectionData *, submenu, &menu->submenus) { - move_to_collection_menus_items(layout, submenu); - } } -static void move_to_collection_menus_items(uiLayout *layout, MoveToCollectionData *menu) +void move_to_collection_menu_item(uiLayout *layout, + Collection *collection, + const bool scene_collection, + const int enum_index) { - const int icon = UI_icon_color_from_collection(menu->collection); + const int icon = scene_collection ? ICON_SCENE_DATA : UI_icon_color_from_collection(collection); + const char *name = BKE_collection_ui_name_get(collection); + uiItemEnumO( + layout, operation_name, name, icon, scene_collection ? "scene" : "collection", enum_index); +} - if (BLI_listbase_is_empty(&menu->submenus)) { - uiItemIntO(layout, - menu->collection->id.name + 2, - icon, - menu->ot->idname, - "collection_index", - menu->index); +void move_to_collection_menu_items(bContext *C, uiLayout *layout, void *arg) +{ + Collection *collection = static_cast(arg); + const Scene *scene = CTX_data_scene(C); + Main *bmain = CTX_data_main(C); + + int enum_index = 0; + bool scene_collection = scene->master_collection == collection; + if (scene_collection) { + enum_index = BLI_findindex(&bmain->scenes, scene); } else { - uiItemMenuF(layout, menu->collection->id.name + 2, icon, move_to_collection_menu_create, menu); + enum_index = BLI_findindex(&bmain->collections, collection); + } + + move_to_collection_menu_item(layout, collection, scene_collection, enum_index); + + uiItemS(layout); + move_to_collection_menu_new_item(layout, scene_collection, enum_index); + if (collection->children.first) { + uiItemS(layout); + } + LISTBASE_FOREACH (CollectionChild *, child, &collection->children) { + const char *name = BKE_collection_ui_name_get(child->collection); + const int icon = UI_icon_color_from_collection(child->collection); + uiItemMenuF(layout, name, icon, move_to_collection_menu_items, child->collection); } } -/* This is allocated statically because we need this available for the menus creation callback. */ -static MoveToCollectionData *master_collection_menu = nullptr; +static int move_to_collection_menu(bContext *C, wmOperator *op) +{ + Scene *scene = CTX_data_scene(C); + Collection *master_collection = scene->master_collection; + uiPopupMenu *pup; + uiLayout *layout; + + /* 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); + + uiLayoutSetOperatorContext(layout, WM_OP_INVOKE_DEFAULT); + + uiItemS(layout); + operation_name = op->type->idname; + move_to_collection_menu_items(C, layout, static_cast(master_collection)); + + UI_popup_menu_end(C, pup); + return OPERATOR_INTERFACE; +} static int move_to_collection_invoke(bContext *C, wmOperator *op, const wmEvent * /*event*/) { - Scene *scene = CTX_data_scene(C); ListBase objects = selected_objects_get(C); if (BLI_listbase_is_empty(&objects)) { @@ -2119,21 +2129,15 @@ static int move_to_collection_invoke(bContext *C, wmOperator *op, const wmEvent } BLI_freelistN(&objects); - /* Reset the menus data for the current master collection, and free previously allocated data. */ - move_to_collection_menus_free(&master_collection_menu); - 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); + Collection *collection = collection_from_enum_or_scene(C, op); + if (collection) { 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; - collection = BKE_collection_from_index(scene, collection_index); BKE_collection_new_name_get(collection, name); RNA_property_string_set(op->ptr, prop, name); @@ -2142,38 +2146,7 @@ static int move_to_collection_invoke(bContext *C, wmOperator *op, const wmEvent } return move_to_collection_exec(C, op); } - - Collection *master_collection = scene->master_collection; - - /* 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 == nullptr) { - master_collection_menu = MEM_cnew( - "MoveToCollectionData menu - expected eventual memleak"); - } - - master_collection_menu->collection = master_collection; - master_collection_menu->ot = op->type; - move_to_collection_menus_create(op, master_collection_menu); - - uiPopupMenu *pup; - uiLayout *layout; - - /* 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); - - uiLayoutSetOperatorContext(layout, WM_OP_INVOKE_DEFAULT); - - move_to_collection_menu_create(C, layout, master_collection_menu); - - UI_popup_menu_end(C, pup); - - return OPERATOR_INTERFACE; + return move_to_collection_menu(C, op); } void OBJECT_OT_move_to_collection(wmOperatorType *ot) @@ -2193,15 +2166,21 @@ void OBJECT_OT_move_to_collection(wmOperatorType *ot) /* 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); + prop = RNA_def_enum(ot->srna, "collection", rna_enum_dummy_NULL_items, 0, "Collection", ""); + RNA_def_enum_funcs(prop, RNA_collection_itemf); + RNA_def_property_flag(prop, PropertyFlag(PROP_SKIP_SAVE | PROP_HIDDEN)); + RNA_def_property_flag(prop, PROP_ENUM_NO_TRANSLATE); + + prop = RNA_def_enum(ot->srna, + "scene", + rna_enum_dummy_NULL_items, + 0, + "Scene", + "Uses scenes master collection as target collection"); + RNA_def_property_flag(prop, PropertyFlag(PROP_SKIP_SAVE | PROP_HIDDEN)); + RNA_def_enum_funcs(prop, RNA_scene_itemf); + RNA_def_property_flag(prop, PROP_ENUM_NO_TRANSLATE); + RNA_def_property_flag(prop, PropertyFlag(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, PropertyFlag(PROP_SKIP_SAVE | PROP_HIDDEN)); @@ -2217,41 +2196,11 @@ void OBJECT_OT_move_to_collection(wmOperatorType *ot) void OBJECT_OT_link_to_collection(wmOperatorType *ot) { - PropertyRNA *prop; - /* identifiers */ + OBJECT_OT_move_to_collection(ot); ot->name = "Link to Collection"; ot->description = "Link objects to a collection"; ot->idname = "OBJECT_OT_link_to_collection"; - - /* api callbacks */ - 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; - - 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, PropertyFlag(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, PropertyFlag(PROP_SKIP_SAVE | PROP_HIDDEN)); - prop = RNA_def_string(ot->srna, - "new_collection_name", - nullptr, - MAX_NAME, - "Name", - "Name of the newly added collection"); - RNA_def_property_flag(prop, PROP_SKIP_SAVE); - ot->prop = prop; } /** \} */