diff options
Diffstat (limited to 'source/blender/editors/space_outliner')
31 files changed, 2102 insertions, 1179 deletions
diff --git a/source/blender/editors/space_outliner/CMakeLists.txt b/source/blender/editors/space_outliner/CMakeLists.txt index 97d2957eed2..d29028dad63 100644 --- a/source/blender/editors/space_outliner/CMakeLists.txt +++ b/source/blender/editors/space_outliner/CMakeLists.txt @@ -13,7 +13,6 @@ set(INC ../../sequencer ../../windowmanager ../../../../intern/clog - ../../../../intern/glew-mx ../../../../intern/guardedalloc # RNA_prototypes.h ${CMAKE_BINARY_DIR}/source/blender/makesrna @@ -52,6 +51,7 @@ set(SRC tree/tree_element_id.cc tree/tree_element_id_library.cc tree/tree_element_id_scene.cc + tree/tree_element_label.cc tree/tree_element_nla.cc tree/tree_element_overrides.cc tree/tree_element_rna.cc @@ -71,6 +71,7 @@ set(SRC tree/tree_element_id.hh tree/tree_element_id_library.hh tree/tree_element_id_scene.hh + tree/tree_element_label.hh tree/tree_element_nla.hh tree/tree_element_overrides.hh tree/tree_element_rna.hh diff --git a/source/blender/editors/space_outliner/outliner_collections.cc b/source/blender/editors/space_outliner/outliner_collections.cc index 8ca2ffe6a9c..0ded4654c80 100644 --- a/source/blender/editors/space_outliner/outliner_collections.cc +++ b/source/blender/editors/space_outliner/outliner_collections.cc @@ -38,6 +38,8 @@ #include "outliner_intern.hh" /* own include */ +namespace blender::ed::outliner { + /* -------------------------------------------------------------------- */ /** \name Utility API * \{ */ @@ -72,7 +74,7 @@ Collection *outliner_collection_from_tree_element(const TreeElement *te) } if (tselem->type == TSE_LAYER_COLLECTION) { - LayerCollection *lc = reinterpret_cast<LayerCollection *>(te->directdata); + LayerCollection *lc = static_cast<LayerCollection *>(te->directdata); return lc->collection; } if (ELEM(tselem->type, TSE_SCENE_COLLECTION_BASE, TSE_VIEW_COLLECTION_BASE)) { @@ -86,9 +88,9 @@ Collection *outliner_collection_from_tree_element(const TreeElement *te) return nullptr; } -TreeTraversalAction outliner_find_selected_collections(TreeElement *te, void *customdata) +TreeTraversalAction outliner_collect_selected_collections(TreeElement *te, void *customdata) { - struct IDsSelectedData *data = reinterpret_cast<IDsSelectedData *>(customdata); + struct IDsSelectedData *data = static_cast<IDsSelectedData *>(customdata); TreeStoreElem *tselem = TREESTORE(te); if (outliner_is_collection_tree_element(te)) { @@ -103,9 +105,9 @@ TreeTraversalAction outliner_find_selected_collections(TreeElement *te, void *cu return TRAVERSE_CONTINUE; } -TreeTraversalAction outliner_find_selected_objects(TreeElement *te, void *customdata) +TreeTraversalAction outliner_collect_selected_objects(TreeElement *te, void *customdata) { - struct IDsSelectedData *data = reinterpret_cast<IDsSelectedData *>(customdata); + struct IDsSelectedData *data = static_cast<IDsSelectedData *>(customdata); TreeStoreElem *tselem = TREESTORE(te); if (outliner_is_collection_tree_element(te)) { @@ -122,15 +124,19 @@ TreeTraversalAction outliner_find_selected_objects(TreeElement *te, void *custom return TRAVERSE_CONTINUE; } +} // namespace blender::ed::outliner + void ED_outliner_selected_objects_get(const bContext *C, ListBase *objects) { + using namespace blender::ed::outliner; + SpaceOutliner *space_outliner = CTX_wm_space_outliner(C); struct IDsSelectedData data = {{nullptr}}; outliner_tree_traverse(space_outliner, &space_outliner->tree, 0, TSE_SELECTED, - outliner_find_selected_objects, + outliner_collect_selected_objects, &data); LISTBASE_FOREACH (LinkData *, link, &data.selected_array) { TreeElement *ten_selected = (TreeElement *)link->data; @@ -140,12 +146,16 @@ void ED_outliner_selected_objects_get(const bContext *C, ListBase *objects) BLI_freelistN(&data.selected_array); } +namespace blender::ed::outliner { + /** \} */ /* -------------------------------------------------------------------- */ /** \name Poll Functions * \{ */ +} // namespace blender::ed::outliner + bool ED_outliner_collections_editor_poll(bContext *C) { SpaceOutliner *space_outliner = CTX_wm_space_outliner(C); @@ -153,6 +163,8 @@ bool ED_outliner_collections_editor_poll(bContext *C) ELEM(space_outliner->outlinevis, SO_VIEW_LAYER, SO_SCENES, SO_LIBRARIES); } +namespace blender::ed::outliner { + static bool outliner_view_layer_collections_editor_poll(bContext *C) { SpaceOutliner *space_outliner = CTX_wm_space_outliner(C); @@ -184,7 +196,7 @@ struct CollectionNewData { static TreeTraversalAction collection_find_selected_to_add(TreeElement *te, void *customdata) { - struct CollectionNewData *data = reinterpret_cast<CollectionNewData *>(customdata); + struct CollectionNewData *data = static_cast<CollectionNewData *>(customdata); Collection *collection = outliner_collection_from_tree_element(te); if (!collection) { @@ -284,9 +296,9 @@ struct CollectionEditData { bool is_liboverride_hierarchy_root_allowed; }; -static TreeTraversalAction collection_find_data_to_edit(TreeElement *te, void *customdata) +static TreeTraversalAction collection_collect_data_to_edit(TreeElement *te, void *customdata) { - CollectionEditData *data = reinterpret_cast<CollectionEditData *>(customdata); + CollectionEditData *data = static_cast<CollectionEditData *>(customdata); Collection *collection = outliner_collection_from_tree_element(te); if (!collection) { @@ -333,13 +345,17 @@ void outliner_collection_delete( /* We first walk over and find the Collections we actually want to delete * (ignoring duplicates). */ - outliner_tree_traverse( - space_outliner, &space_outliner->tree, 0, TSE_SELECTED, collection_find_data_to_edit, &data); + outliner_tree_traverse(space_outliner, + &space_outliner->tree, + 0, + TSE_SELECTED, + collection_collect_data_to_edit, + &data); /* Effectively delete the collections. */ GSetIterator collections_to_edit_iter; GSET_ITER (collections_to_edit_iter, data.collections_to_edit) { - Collection *collection = reinterpret_cast<Collection *>( + Collection *collection = static_cast<Collection *>( BLI_gsetIterator_getKey(&collections_to_edit_iter)); /* Test in case collection got deleted as part of another one. */ @@ -364,7 +380,7 @@ void outliner_collection_delete( const IDTypeInfo *id_type = BKE_idtype_get_info_from_id(&parent->id); BLI_assert(id_type->owner_get != nullptr); - ID *scene_owner = id_type->owner_get(bmain, &parent->id); + ID *scene_owner = id_type->owner_get(&parent->id); BLI_assert(GS(scene_owner->name) == ID_SCE); if (ID_IS_LINKED(scene_owner) || ID_IS_OVERRIDE_LIBRARY(scene_owner)) { skip = true; @@ -397,7 +413,7 @@ static int collection_hierarchy_delete_exec(bContext *C, wmOperator *op) Scene *scene = CTX_data_scene(C); ViewLayer *view_layer = CTX_data_view_layer(C); struct wmMsgBus *mbus = CTX_wm_message_bus(C); - const Base *basact_prev = BASACT(view_layer); + const Base *basact_prev = view_layer->basact; outliner_collection_delete(C, bmain, scene, op->reports, true); @@ -406,7 +422,7 @@ static int collection_hierarchy_delete_exec(bContext *C, wmOperator *op) WM_main_add_notifier(NC_SCENE | ND_LAYER, nullptr); - if (basact_prev != BASACT(view_layer)) { + if (basact_prev != view_layer->basact) { WM_msg_publish_rna_prop(mbus, &scene->id, view_layer, LayerObjects, active); } @@ -444,12 +460,12 @@ struct CollectionObjectsSelectData { static TreeTraversalAction outliner_find_first_selected_layer_collection(TreeElement *te, void *customdata) { - CollectionObjectsSelectData *data = reinterpret_cast<CollectionObjectsSelectData *>(customdata); + CollectionObjectsSelectData *data = static_cast<CollectionObjectsSelectData *>(customdata); TreeStoreElem *tselem = TREESTORE(te); switch (tselem->type) { case TSE_LAYER_COLLECTION: - data->layer_collection = reinterpret_cast<LayerCollection *>(te->directdata); + data->layer_collection = static_cast<LayerCollection *>(te->directdata); return TRAVERSE_BREAK; case TSE_R_LAYER: case TSE_SCENE_COLLECTION_BASE: @@ -538,7 +554,7 @@ struct CollectionDuplicateData { static TreeTraversalAction outliner_find_first_selected_collection(TreeElement *te, void *customdata) { - CollectionDuplicateData *data = reinterpret_cast<CollectionDuplicateData *>(customdata); + CollectionDuplicateData *data = static_cast<CollectionDuplicateData *>(customdata); TreeStoreElem *tselem = TREESTORE(te); switch (tselem->type) { @@ -597,7 +613,7 @@ static int collection_duplicate_exec(bContext *C, wmOperator *op) const IDTypeInfo *id_type = BKE_idtype_get_info_from_id(&parent->id); BLI_assert(id_type->owner_get != nullptr); - Scene *scene_owner = (Scene *)id_type->owner_get(bmain, &parent->id); + Scene *scene_owner = (Scene *)id_type->owner_get(&parent->id); BLI_assert(scene_owner != nullptr); BLI_assert(GS(scene_owner->id.name) == ID_SCE); @@ -695,13 +711,17 @@ static int collection_link_exec(bContext *C, wmOperator *op) data.collections_to_edit = BLI_gset_ptr_new(__func__); /* We first walk over and find the Collections we actually want to link (ignoring duplicates). */ - outliner_tree_traverse( - space_outliner, &space_outliner->tree, 0, TSE_SELECTED, collection_find_data_to_edit, &data); + outliner_tree_traverse(space_outliner, + &space_outliner->tree, + 0, + TSE_SELECTED, + collection_collect_data_to_edit, + &data); /* Effectively link the collections. */ GSetIterator collections_to_edit_iter; GSET_ITER (collections_to_edit_iter, data.collections_to_edit) { - Collection *collection = reinterpret_cast<Collection *>( + Collection *collection = static_cast<Collection *>( BLI_gsetIterator_getKey(&collections_to_edit_iter)); BKE_collection_child_add(bmain, active_collection, collection); id_fake_user_clear(&collection->id); @@ -754,15 +774,19 @@ static int collection_instance_exec(bContext *C, wmOperator *UNUSED(op)) /* We first walk over and find the Collections we actually want to instance * (ignoring duplicates). */ - outliner_tree_traverse( - space_outliner, &space_outliner->tree, 0, TSE_SELECTED, collection_find_data_to_edit, &data); + outliner_tree_traverse(space_outliner, + &space_outliner->tree, + 0, + TSE_SELECTED, + collection_collect_data_to_edit, + &data); /* Find an active collection to add to, that doesn't give dependency cycles. */ LayerCollection *active_lc = BKE_layer_collection_get_active(view_layer); GSetIterator collections_to_edit_iter; GSET_ITER (collections_to_edit_iter, data.collections_to_edit) { - Collection *collection = reinterpret_cast<Collection *>( + Collection *collection = static_cast<Collection *>( BLI_gsetIterator_getKey(&collections_to_edit_iter)); while (BKE_collection_cycle_find(active_lc->collection, collection)) { @@ -772,7 +796,7 @@ static int collection_instance_exec(bContext *C, wmOperator *UNUSED(op)) /* Effectively instance the collections. */ GSET_ITER (collections_to_edit_iter, data.collections_to_edit) { - Collection *collection = reinterpret_cast<Collection *>( + Collection *collection = static_cast<Collection *>( BLI_gsetIterator_getKey(&collections_to_edit_iter)); Object *ob = ED_object_add_type( C, OB_EMPTY, collection->id.name + 2, scene->cursor.location, nullptr, false, 0); @@ -812,16 +836,16 @@ void OUTLINER_OT_collection_instance(wmOperatorType *ot) /** \name Exclude Collection * \{ */ -static TreeTraversalAction layer_collection_find_data_to_edit(TreeElement *te, void *customdata) +static TreeTraversalAction layer_collection_collect_data_to_edit(TreeElement *te, void *customdata) { - CollectionEditData *data = reinterpret_cast<CollectionEditData *>(customdata); + CollectionEditData *data = static_cast<CollectionEditData *>(customdata); TreeStoreElem *tselem = TREESTORE(te); if (!(tselem && tselem->type == TSE_LAYER_COLLECTION)) { return TRAVERSE_CONTINUE; } - LayerCollection *lc = reinterpret_cast<LayerCollection *>(te->directdata); + LayerCollection *lc = static_cast<LayerCollection *>(te->directdata); if (lc->collection->flag & COLLECTION_IS_MASTER) { /* skip - showing warning/error message might be misleading @@ -857,12 +881,12 @@ static bool collections_view_layer_poll(bContext *C, bool clear, int flag) &space_outliner->tree, 0, TSE_SELECTED, - layer_collection_find_data_to_edit, + layer_collection_collect_data_to_edit, &data); GSetIterator collections_to_edit_iter; GSET_ITER (collections_to_edit_iter, data.collections_to_edit) { - LayerCollection *lc = reinterpret_cast<LayerCollection *>( + LayerCollection *lc = static_cast<LayerCollection *>( BLI_gsetIterator_getKey(&collections_to_edit_iter)); if (clear && (lc->flag & flag)) { @@ -929,12 +953,12 @@ static int collection_view_layer_exec(bContext *C, wmOperator *op) &space_outliner->tree, 0, TSE_SELECTED, - layer_collection_find_data_to_edit, + layer_collection_collect_data_to_edit, &data); GSetIterator collections_to_edit_iter; GSET_ITER (collections_to_edit_iter, data.collections_to_edit) { - LayerCollection *lc = reinterpret_cast<LayerCollection *>( + LayerCollection *lc = static_cast<LayerCollection *>( BLI_gsetIterator_getKey(&collections_to_edit_iter)); BKE_layer_collection_set_flag(lc, flag, !clear); } @@ -1063,12 +1087,12 @@ static int collection_isolate_exec(bContext *C, wmOperator *op) &space_outliner->tree, 0, TSE_SELECTED, - layer_collection_find_data_to_edit, + layer_collection_collect_data_to_edit, &data); GSetIterator collections_to_edit_iter; GSET_ITER (collections_to_edit_iter, data.collections_to_edit) { - LayerCollection *layer_collection = reinterpret_cast<LayerCollection *>( + LayerCollection *layer_collection = static_cast<LayerCollection *>( BLI_gsetIterator_getKey(&collections_to_edit_iter)); if (extend) { @@ -1163,12 +1187,12 @@ static int collection_visibility_exec(bContext *C, wmOperator *op) &space_outliner->tree, 0, TSE_SELECTED, - layer_collection_find_data_to_edit, + layer_collection_collect_data_to_edit, &data); GSetIterator collections_to_edit_iter; GSET_ITER (collections_to_edit_iter, data.collections_to_edit) { - LayerCollection *layer_collection = reinterpret_cast<LayerCollection *>( + LayerCollection *layer_collection = static_cast<LayerCollection *>( BLI_gsetIterator_getKey(&collections_to_edit_iter)); BKE_layer_collection_set_visible(view_layer, layer_collection, show, is_inside); } @@ -1315,11 +1339,11 @@ static int collection_flag_exec(bContext *C, wmOperator *op) &space_outliner->tree, 0, TSE_SELECTED, - layer_collection_find_data_to_edit, + layer_collection_collect_data_to_edit, &data); GSetIterator collections_to_edit_iter; GSET_ITER (collections_to_edit_iter, data.collections_to_edit) { - LayerCollection *layer_collection = reinterpret_cast<LayerCollection *>( + LayerCollection *layer_collection = static_cast<LayerCollection *>( BLI_gsetIterator_getKey(&collections_to_edit_iter)); Collection *collection = layer_collection->collection; if (!BKE_id_is_editable(bmain, &collection->id)) { @@ -1344,11 +1368,11 @@ static int collection_flag_exec(bContext *C, wmOperator *op) &space_outliner->tree, 0, TSE_SELECTED, - collection_find_data_to_edit, + collection_collect_data_to_edit, &data); GSetIterator collections_to_edit_iter; GSET_ITER (collections_to_edit_iter, data.collections_to_edit) { - Collection *collection = reinterpret_cast<Collection *>( + Collection *collection = static_cast<Collection *>( BLI_gsetIterator_getKey(&collections_to_edit_iter)); if (!BKE_id_is_editable(bmain, &collection->id)) { continue; @@ -1449,9 +1473,9 @@ struct OutlinerHideEditData { /** \name Visibility for Collection & Object Operators * \{ */ -static TreeTraversalAction outliner_hide_find_data_to_edit(TreeElement *te, void *customdata) +static TreeTraversalAction outliner_hide_collect_data_to_edit(TreeElement *te, void *customdata) { - OutlinerHideEditData *data = reinterpret_cast<OutlinerHideEditData *>(customdata); + OutlinerHideEditData *data = static_cast<OutlinerHideEditData *>(customdata); TreeStoreElem *tselem = TREESTORE(te); if (tselem == nullptr) { @@ -1459,7 +1483,7 @@ static TreeTraversalAction outliner_hide_find_data_to_edit(TreeElement *te, void } if (tselem->type == TSE_LAYER_COLLECTION) { - LayerCollection *lc = reinterpret_cast<LayerCollection *>(te->directdata); + LayerCollection *lc = static_cast<LayerCollection *>(te->directdata); if (lc->collection->flag & COLLECTION_IS_MASTER) { /* Skip - showing warning/error message might be misleading @@ -1496,12 +1520,12 @@ static int outliner_hide_exec(bContext *C, wmOperator *UNUSED(op)) &space_outliner->tree, 0, TSE_SELECTED, - outliner_hide_find_data_to_edit, + outliner_hide_collect_data_to_edit, &data); GSetIterator collections_to_edit_iter; GSET_ITER (collections_to_edit_iter, data.collections_to_edit) { - LayerCollection *layer_collection = reinterpret_cast<LayerCollection *>( + LayerCollection *layer_collection = static_cast<LayerCollection *>( BLI_gsetIterator_getKey(&collections_to_edit_iter)); BKE_layer_collection_set_visible(view_layer, layer_collection, false, false); } @@ -1509,7 +1533,7 @@ static int outliner_hide_exec(bContext *C, wmOperator *UNUSED(op)) GSetIterator bases_to_edit_iter; GSET_ITER (bases_to_edit_iter, data.bases_to_edit) { - Base *base = reinterpret_cast<Base *>(BLI_gsetIterator_getKey(&bases_to_edit_iter)); + Base *base = static_cast<Base *>(BLI_gsetIterator_getKey(&bases_to_edit_iter)); base->flag |= BASE_HIDDEN; } BLI_gset_free(data.bases_to_edit, nullptr); @@ -1542,8 +1566,7 @@ static int outliner_unhide_all_exec(bContext *C, wmOperator *UNUSED(op)) ViewLayer *view_layer = CTX_data_view_layer(C); /* Unhide all the collections. */ - LayerCollection *lc_master = reinterpret_cast<LayerCollection *>( - view_layer->layer_collections.first); + LayerCollection *lc_master = static_cast<LayerCollection *>(view_layer->layer_collections.first); LISTBASE_FOREACH (LayerCollection *, lc_iter, &lc_master->layer_collections) { BKE_layer_collection_set_flag(lc_iter, LAYER_COLLECTION_HIDE, false); } @@ -1593,7 +1616,7 @@ static int outliner_color_tag_set_exec(bContext *C, wmOperator *op) &space_outliner->tree, 0, TSE_SELECTED, - outliner_find_selected_collections, + outliner_collect_selected_collections, &selected); LISTBASE_FOREACH (LinkData *, link, &selected.selected_array) { @@ -1637,3 +1660,5 @@ void OUTLINER_OT_collection_color_tag_set(wmOperatorType *ot) } /** \} */ + +} // namespace blender::ed::outliner diff --git a/source/blender/editors/space_outliner/outliner_context.cc b/source/blender/editors/space_outliner/outliner_context.cc index 1a804cb58b8..001bda57fa2 100644 --- a/source/blender/editors/space_outliner/outliner_context.cc +++ b/source/blender/editors/space_outliner/outliner_context.cc @@ -14,7 +14,7 @@ #include "outliner_intern.hh" #include "tree/tree_iterator.hh" -using namespace blender::ed::outliner; +namespace blender::ed::outliner { static void outliner_context_selected_ids_recursive(const SpaceOutliner &space_outliner, bContextDataResult *result) @@ -55,3 +55,5 @@ int /*eContextResult*/ outliner_context(const bContext *C, return CTX_RESULT_MEMBER_NOT_FOUND; } + +} // namespace blender::ed::outliner diff --git a/source/blender/editors/space_outliner/outliner_dragdrop.cc b/source/blender/editors/space_outliner/outliner_dragdrop.cc index e20958c1b1e..4a0e00b8bf1 100644 --- a/source/blender/editors/space_outliner/outliner_dragdrop.cc +++ b/source/blender/editors/space_outliner/outliner_dragdrop.cc @@ -45,6 +45,8 @@ #include "outliner_intern.hh" +namespace blender::ed::outliner { + static Collection *collection_parent_from_ID(ID *id); /* -------------------------------------------------------------------- */ @@ -144,7 +146,7 @@ static TreeElement *outliner_drop_insert_find(bContext *C, return te_hovered; } *r_insert_type = TE_INSERT_BEFORE; - return reinterpret_cast<TreeElement *>(te_hovered->subtree.first); + return static_cast<TreeElement *>(te_hovered->subtree.first); } *r_insert_type = TE_INSERT_AFTER; return te_hovered; @@ -159,8 +161,8 @@ static TreeElement *outliner_drop_insert_find(bContext *C, /* Mouse doesn't hover any item (ignoring x-axis), * so it's either above list bounds or below. */ - TreeElement *first = reinterpret_cast<TreeElement *>(space_outliner->tree.first); - TreeElement *last = reinterpret_cast<TreeElement *>(space_outliner->tree.last); + TreeElement *first = static_cast<TreeElement *>(space_outliner->tree.first); + TreeElement *last = static_cast<TreeElement *>(space_outliner->tree.last); if (view_mval[1] < last->ys) { *r_insert_type = TE_INSERT_AFTER; @@ -422,12 +424,12 @@ static int parent_drop_invoke(bContext *C, wmOperator *op, const wmEvent *event) return OPERATOR_CANCELLED; } - ListBase *lb = reinterpret_cast<ListBase *>(event->customdata); - wmDrag *drag = reinterpret_cast<wmDrag *>(lb->first); + ListBase *lb = static_cast<ListBase *>(event->customdata); + wmDrag *drag = static_cast<wmDrag *>(lb->first); parent_drop_set_parents(C, op->reports, - reinterpret_cast<wmDragID *>(drag->ids.first), + static_cast<wmDragID *>(drag->ids.first), par, PAR_OBJECT, event->modifier & KM_ALT); @@ -505,8 +507,8 @@ static int parent_clear_invoke(bContext *C, wmOperator *UNUSED(op), const wmEven return OPERATOR_CANCELLED; } - ListBase *lb = reinterpret_cast<ListBase *>(event->customdata); - wmDrag *drag = reinterpret_cast<wmDrag *>(lb->first); + ListBase *lb = static_cast<ListBase *>(event->customdata); + wmDrag *drag = static_cast<wmDrag *>(lb->first); LISTBASE_FOREACH (wmDragID *, drag_id, &drag->ids) { if (GS(drag_id->id->name) == ID_OB) { @@ -849,7 +851,7 @@ static bool datastack_drop_poll(bContext *C, wmDrag *drag, const wmEvent *event) ARegion *region = CTX_wm_region(C); bool changed = outliner_flag_set(*space_outliner, TSE_HIGHLIGHTED_ANY | TSE_DRAG_ANY, false); - StackDropData *drop_data = reinterpret_cast<StackDropData *>(drag->poin); + StackDropData *drop_data = static_cast<StackDropData *>(drag->poin); if (!drop_data) { return false; } @@ -887,7 +889,7 @@ static char *datastack_drop_tooltip(bContext *UNUSED(C), const int UNUSED(xy[2]), struct wmDropBox *UNUSED(drop)) { - StackDropData *drop_data = reinterpret_cast<StackDropData *>(drag->poin); + StackDropData *drop_data = static_cast<StackDropData *>(drag->poin); switch (drop_data->drop_action) { case DATA_STACK_DROP_REORDER: return BLI_strdup(TIP_("Reorder")); @@ -965,14 +967,13 @@ static void datastack_drop_copy(bContext *C, StackDropData *drop_data) case TSE_MODIFIER: if (drop_data->ob_parent->type == OB_GPENCIL && ob_dst->type == OB_GPENCIL) { ED_object_gpencil_modifier_copy_to_object( - ob_dst, reinterpret_cast<GpencilModifierData *>(drop_data->drag_directdata)); + ob_dst, static_cast<GpencilModifierData *>(drop_data->drag_directdata)); } else if (drop_data->ob_parent->type != OB_GPENCIL && ob_dst->type != OB_GPENCIL) { - ED_object_modifier_copy_to_object( - C, - ob_dst, - drop_data->ob_parent, - reinterpret_cast<ModifierData *>(drop_data->drag_directdata)); + ED_object_modifier_copy_to_object(C, + ob_dst, + drop_data->ob_parent, + static_cast<ModifierData *>(drop_data->drag_directdata)); } break; case TSE_CONSTRAINT: @@ -980,12 +981,12 @@ static void datastack_drop_copy(bContext *C, StackDropData *drop_data) ED_object_constraint_copy_for_pose( bmain, ob_dst, - reinterpret_cast<bPoseChannel *>(drop_data->drop_te->directdata), - reinterpret_cast<bConstraint *>(drop_data->drag_directdata)); + static_cast<bPoseChannel *>(drop_data->drop_te->directdata), + static_cast<bConstraint *>(drop_data->drag_directdata)); } else { ED_object_constraint_copy_for_object( - bmain, ob_dst, reinterpret_cast<bConstraint *>(drop_data->drag_directdata)); + bmain, ob_dst, static_cast<bConstraint *>(drop_data->drag_directdata)); } break; case TSE_GPENCIL_EFFECT: { @@ -993,8 +994,7 @@ static void datastack_drop_copy(bContext *C, StackDropData *drop_data) return; } - ED_object_shaderfx_copy(ob_dst, - reinterpret_cast<ShaderFxData *>(drop_data->drag_directdata)); + ED_object_shaderfx_copy(ob_dst, static_cast<ShaderFxData *>(drop_data->drag_directdata)); break; } } @@ -1021,15 +1021,12 @@ static void datastack_drop_reorder(bContext *C, ReportList *reports, StackDropDa index = outliner_get_insert_index( drag_te, drop_te, insert_type, &ob->greasepencil_modifiers); ED_object_gpencil_modifier_move_to_index( - reports, - ob, - reinterpret_cast<GpencilModifierData *>(drop_data->drag_directdata), - index); + reports, ob, static_cast<GpencilModifierData *>(drop_data->drag_directdata), index); } else { index = outliner_get_insert_index(drag_te, drop_te, insert_type, &ob->modifiers); ED_object_modifier_move_to_index( - reports, ob, reinterpret_cast<ModifierData *>(drop_data->drag_directdata), index); + reports, ob, static_cast<ModifierData *>(drop_data->drag_directdata), index); } break; case TSE_CONSTRAINT: @@ -1041,13 +1038,13 @@ static void datastack_drop_reorder(bContext *C, ReportList *reports, StackDropDa index = outliner_get_insert_index(drag_te, drop_te, insert_type, &ob->constraints); } ED_object_constraint_move_to_index( - ob, reinterpret_cast<bConstraint *>(drop_data->drag_directdata), index); + ob, static_cast<bConstraint *>(drop_data->drag_directdata), index); break; case TSE_GPENCIL_EFFECT: index = outliner_get_insert_index(drag_te, drop_te, insert_type, &ob->shader_fx); ED_object_shaderfx_move_to_index( - reports, ob, reinterpret_cast<ShaderFxData *>(drop_data->drag_directdata), index); + reports, ob, static_cast<ShaderFxData *>(drop_data->drag_directdata), index); } } @@ -1057,9 +1054,9 @@ static int datastack_drop_invoke(bContext *C, wmOperator *op, const wmEvent *eve return OPERATOR_CANCELLED; } - ListBase *lb = reinterpret_cast<ListBase *>(event->customdata); - wmDrag *drag = reinterpret_cast<wmDrag *>(lb->first); - StackDropData *drop_data = reinterpret_cast<StackDropData *>(drag->poin); + ListBase *lb = static_cast<ListBase *>(event->customdata); + wmDrag *drag = static_cast<wmDrag *>(lb->first); + StackDropData *drop_data = static_cast<StackDropData *>(drag->poin); switch (drop_data->drop_action) { case DATA_STACK_DROP_LINK: @@ -1124,8 +1121,7 @@ static Collection *collection_parent_from_ID(ID *id) return nullptr; } -static bool collection_drop_init( - bContext *C, wmDrag *drag, const int xy[2], const bool is_link, CollectionDrop *data) +static bool collection_drop_init(bContext *C, wmDrag *drag, const int xy[2], CollectionDrop *data) { /* Get collection to drop into. */ TreeElementInsertType insert_type; @@ -1144,7 +1140,7 @@ static bool collection_drop_init( return false; } - wmDragID *drag_id = reinterpret_cast<wmDragID *>(drag->ids.first); + wmDragID *drag_id = static_cast<wmDragID *>(drag->ids.first); if (drag_id == nullptr) { return false; } @@ -1157,9 +1153,6 @@ static bool collection_drop_init( /* Get collection to drag out of. */ ID *parent = drag_id->from_parent; Collection *from_collection = collection_parent_from_ID(parent); - if (is_link) { - from_collection = nullptr; - } /* Currently this should not be allowed, cannot edit items in an override of a Collection. */ if (from_collection != nullptr && ID_IS_OVERRIDE_LIBRARY(from_collection)) { @@ -1197,29 +1190,22 @@ static bool collection_drop_poll(bContext *C, wmDrag *drag, const wmEvent *event bool changed = outliner_flag_set(*space_outliner, TSE_HIGHLIGHTED_ANY | TSE_DRAG_ANY, false); CollectionDrop data; - if (((event->modifier & KM_SHIFT) == 0) && - collection_drop_init(C, drag, event->xy, event->modifier & KM_CTRL, &data)) { + if (((event->modifier & KM_SHIFT) == 0) && collection_drop_init(C, drag, event->xy, &data)) { TreeElement *te = data.te; TreeStoreElem *tselem = TREESTORE(te); - if (!data.from || event->modifier & KM_CTRL) { - tselem->flag |= TSE_DRAG_INTO; - changed = true; - } - else { - switch (data.insert_type) { - case TE_INSERT_BEFORE: - tselem->flag |= TSE_DRAG_BEFORE; - changed = true; - break; - case TE_INSERT_AFTER: - tselem->flag |= TSE_DRAG_AFTER; - changed = true; - break; - case TE_INSERT_INTO: { - tselem->flag |= TSE_DRAG_INTO; - changed = true; - break; - } + switch (data.insert_type) { + case TE_INSERT_BEFORE: + tselem->flag |= TSE_DRAG_BEFORE; + changed = true; + break; + case TE_INSERT_AFTER: + tselem->flag |= TSE_DRAG_AFTER; + changed = true; + break; + case TE_INSERT_INTO: { + tselem->flag |= TSE_DRAG_INTO; + changed = true; + break; } } if (changed) { @@ -1242,30 +1228,49 @@ static char *collection_drop_tooltip(bContext *C, const wmEvent *event = win ? win->eventstate : nullptr; CollectionDrop data; - if (event && ((event->modifier & KM_SHIFT) == 0) && - collection_drop_init(C, drag, xy, event->modifier & KM_CTRL, &data)) { - TreeElement *te = data.te; - if (!data.from || event->modifier & KM_CTRL) { - return BLI_strdup(TIP_("Link inside Collection")); + if (event && ((event->modifier & KM_SHIFT) == 0) && collection_drop_init(C, drag, xy, &data)) { + const bool is_link = !data.from || (event->modifier & KM_CTRL); + + /* Test if we are moving within same parent collection. */ + bool same_level = false; + LISTBASE_FOREACH (CollectionParent *, parent, &data.to->parents) { + if (data.from == parent->collection) { + same_level = true; + } } + + /* Tooltips when not moving directly into another collection i.e. mouse on border of + * collections. Later we will decide which tooltip to return. */ + const bool tooltip_link = (is_link && !same_level); + const char *tooltip_before = tooltip_link ? TIP_("Link before collection") : + TIP_("Move before collection"); + const char *tooltip_between = tooltip_link ? TIP_("Link between collections") : + TIP_("Move between collections"); + const char *tooltip_after = tooltip_link ? TIP_("Link after collection") : + TIP_("Move after collection"); + + TreeElement *te = data.te; switch (data.insert_type) { case TE_INSERT_BEFORE: if (te->prev && outliner_is_collection_tree_element(te->prev)) { - return BLI_strdup(TIP_("Move between collections")); + return BLI_strdup(tooltip_between); } else { - return BLI_strdup(TIP_("Move before collection")); + return BLI_strdup(tooltip_before); } break; case TE_INSERT_AFTER: if (te->next && outliner_is_collection_tree_element(te->next)) { - return BLI_strdup(TIP_("Move between collections")); + return BLI_strdup(tooltip_between); } else { - return BLI_strdup(TIP_("Move after collection")); + return BLI_strdup(tooltip_after); } break; case TE_INSERT_INTO: { + if (is_link) { + return BLI_strdup(TIP_("Link inside collection")); + } /* Check the type of the drag IDs to avoid the incorrect "Shift to parent" * for collections. Checking the type of the first ID works fine here since @@ -1292,11 +1297,11 @@ static int collection_drop_invoke(bContext *C, wmOperator *UNUSED(op), const wmE return OPERATOR_CANCELLED; } - ListBase *lb = reinterpret_cast<ListBase *>(event->customdata); - wmDrag *drag = reinterpret_cast<wmDrag *>(lb->first); + ListBase *lb = static_cast<ListBase *>(event->customdata); + wmDrag *drag = static_cast<wmDrag *>(lb->first); CollectionDrop data; - if (!collection_drop_init(C, drag, event->xy, event->modifier & KM_CTRL, &data)) { + if (!collection_drop_init(C, drag, event->xy, &data)) { return OPERATOR_CANCELLED; } @@ -1447,7 +1452,7 @@ static int outliner_item_drag_drop_invoke(bContext *C, TSE_GPENCIL_EFFECT_BASE); const int wm_drag_type = use_datastack_drag ? WM_DRAG_DATASTACK : WM_DRAG_ID; - wmDrag *drag = WM_event_start_drag(C, data.icon, wm_drag_type, nullptr, 0.0, WM_DRAG_NOP); + wmDrag *drag = WM_drag_data_create(C, data.icon, wm_drag_type, nullptr, 0.0, WM_DRAG_NOP); if (use_datastack_drag) { TreeElement *te_bone = nullptr; @@ -1471,7 +1476,7 @@ static int outliner_item_drag_drop_invoke(bContext *C, &space_outliner->tree, 0, TSE_SELECTED, - outliner_find_selected_objects, + outliner_collect_selected_objects, &selected); } else { @@ -1479,7 +1484,7 @@ static int outliner_item_drag_drop_invoke(bContext *C, &space_outliner->tree, 0, TSE_SELECTED, - outliner_find_selected_collections, + outliner_collect_selected_collections, &selected); } @@ -1537,6 +1542,8 @@ static int outliner_item_drag_drop_invoke(bContext *C, WM_drag_add_local_ID(drag, data.drag_id, data.drag_parent); } + WM_event_start_prepared_drag(C, drag); + ED_outliner_select_sync_from_outliner(C, space_outliner); return (OPERATOR_FINISHED | OPERATOR_PASS_THROUGH); @@ -1587,3 +1594,5 @@ void outliner_dropboxes(void) } /** \} */ + +} // namespace blender::ed::outliner diff --git a/source/blender/editors/space_outliner/outliner_draw.cc b/source/blender/editors/space_outliner/outliner_draw.cc index 753de83a10d..3f99b19cd16 100644 --- a/source/blender/editors/space_outliner/outliner_draw.cc +++ b/source/blender/editors/space_outliner/outliner_draw.cc @@ -37,6 +37,7 @@ #include "BKE_lib_override.h" #include "BKE_library.h" #include "BKE_main.h" +#include "BKE_main_namemap.h" #include "BKE_modifier.h" #include "BKE_node.h" #include "BKE_object.h" @@ -73,8 +74,7 @@ #include "tree/tree_element_rna.hh" #include "tree/tree_iterator.hh" -using namespace blender; -using namespace blender::ed::outliner; +namespace blender::ed::outliner { /* -------------------------------------------------------------------- */ /** \name Tree Size Functions @@ -276,8 +276,8 @@ static void outliner_object_set_flag_recursive_fn(bContext *C, Object *ob_parent = ob ? ob : base->object; - for (Object *ob_iter = reinterpret_cast<Object *>(bmain->objects.first); ob_iter; - ob_iter = reinterpret_cast<Object *>(ob_iter->id.next)) { + for (Object *ob_iter = static_cast<Object *>(bmain->objects.first); ob_iter; + ob_iter = static_cast<Object *>(ob_iter->id.next)) { if (BKE_object_is_child_recursive(ob_parent, ob_iter)) { if (ob) { RNA_id_pointer_create(&ob_iter->id, &ptr); @@ -311,8 +311,8 @@ static void outliner_object_set_flag_recursive_fn(bContext *C, */ static void outliner__object_set_flag_recursive_fn(bContext *C, void *poin, void *poin2) { - Object *ob = reinterpret_cast<Object *>(poin); - char *propname = reinterpret_cast<char *>(poin2); + Object *ob = static_cast<Object *>(poin); + char *propname = static_cast<char *>(poin2); outliner_object_set_flag_recursive_fn(C, nullptr, ob, propname); } @@ -321,8 +321,8 @@ static void outliner__object_set_flag_recursive_fn(bContext *C, void *poin, void */ static void outliner__base_set_flag_recursive_fn(bContext *C, void *poin, void *poin2) { - Base *base = reinterpret_cast<Base *>(poin); - char *propname = reinterpret_cast<char *>(poin2); + Base *base = static_cast<Base *>(poin); + char *propname = static_cast<char *>(poin2); outliner_object_set_flag_recursive_fn(C, base, nullptr, propname); } @@ -487,7 +487,7 @@ void outliner_collection_isolate_flag(Scene *scene, const bool is_hide = strstr(propname, "hide_") != nullptr; LayerCollection *top_layer_collection = layer_collection ? - reinterpret_cast<LayerCollection *>( + static_cast<LayerCollection *>( view_layer->layer_collections.first) : nullptr; Collection *top_collection = collection ? scene->master_collection : nullptr; @@ -558,7 +558,7 @@ void outliner_collection_isolate_flag(Scene *scene, else { CollectionParent *parent; Collection *child = collection; - while ((parent = reinterpret_cast<CollectionParent *>(child->parents.first))) { + while ((parent = static_cast<CollectionParent *>(child->parents.first))) { if (parent->collection->flag & COLLECTION_IS_MASTER) { break; } @@ -637,8 +637,8 @@ static void view_layer__layer_collection_set_flag_recursive_fn(bContext *C, void *poin, void *poin2) { - LayerCollection *layer_collection = reinterpret_cast<LayerCollection *>(poin); - char *propname = reinterpret_cast<char *>(poin2); + LayerCollection *layer_collection = static_cast<LayerCollection *>(poin); + char *propname = static_cast<char *>(poin2); outliner_collection_set_flag_recursive_fn(C, layer_collection, nullptr, propname); } @@ -648,8 +648,8 @@ static void view_layer__layer_collection_set_flag_recursive_fn(bContext *C, */ static void view_layer__collection_set_flag_recursive_fn(bContext *C, void *poin, void *poin2) { - LayerCollection *layer_collection = reinterpret_cast<LayerCollection *>(poin); - char *propname = reinterpret_cast<char *>(poin2); + LayerCollection *layer_collection = static_cast<LayerCollection *>(poin); + char *propname = static_cast<char *>(poin2); outliner_collection_set_flag_recursive_fn( C, layer_collection, layer_collection->collection, propname); } @@ -660,8 +660,8 @@ static void view_layer__collection_set_flag_recursive_fn(bContext *C, void *poin */ static void scenes__collection_set_flag_recursive_fn(bContext *C, void *poin, void *poin2) { - Collection *collection = reinterpret_cast<Collection *>(poin); - char *propname = reinterpret_cast<char *>(poin2); + Collection *collection = static_cast<Collection *>(poin); + char *propname = static_cast<char *>(poin2); outliner_collection_set_flag_recursive_fn(C, nullptr, collection, propname); } @@ -671,12 +671,13 @@ static void namebutton_fn(bContext *C, void *tsep, char *oldname) SpaceOutliner *space_outliner = CTX_wm_space_outliner(C); struct wmMsgBus *mbus = CTX_wm_message_bus(C); BLI_mempool *ts = space_outliner->treestore; - TreeStoreElem *tselem = reinterpret_cast<TreeStoreElem *>(tsep); + TreeStoreElem *tselem = static_cast<TreeStoreElem *>(tsep); if (ts && tselem) { TreeElement *te = outliner_find_tree_element(&space_outliner->tree, tselem); if (tselem->type == TSE_SOME_ID) { + BKE_main_namemap_remove_name(bmain, tselem->id, oldname); BLI_libblock_ensure_unique_name(bmain, tselem->id->name); WM_msg_publish_rna_prop(mbus, tselem->id, tselem->id, ID, name); @@ -699,7 +700,6 @@ static void namebutton_fn(bContext *C, void *tsep, char *oldname) if (ob->type == OB_MBALL) { DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY); } - DEG_id_tag_update(&ob->id, ID_RECALC_COPY_ON_WRITE); break; } default: @@ -730,26 +730,31 @@ static void namebutton_fn(bContext *C, void *tsep, char *oldname) lib->id.tag &= ~LIB_TAG_MISSING; } } + + DEG_id_tag_update(tselem->id, ID_RECALC_COPY_ON_WRITE); } else { switch (tselem->type) { case TSE_DEFGROUP: { Object *ob = (Object *)tselem->id; - bDeformGroup *vg = reinterpret_cast<bDeformGroup *>(te->directdata); + bDeformGroup *vg = static_cast<bDeformGroup *>(te->directdata); BKE_object_defgroup_unique_name(vg, ob); WM_msg_publish_rna_prop(mbus, &ob->id, vg, VertexGroup, name); + DEG_id_tag_update(tselem->id, ID_RECALC_COPY_ON_WRITE); break; } case TSE_NLA_ACTION: { bAction *act = (bAction *)tselem->id; + BKE_main_namemap_remove_name(bmain, &act->id, oldname); BLI_libblock_ensure_unique_name(bmain, act->id.name); WM_msg_publish_rna_prop(mbus, &act->id, &act->id, ID, name); + DEG_id_tag_update(tselem->id, ID_RECALC_COPY_ON_WRITE); break; } case TSE_EBONE: { bArmature *arm = (bArmature *)tselem->id; if (arm->edbo) { - EditBone *ebone = reinterpret_cast<EditBone *>(te->directdata); + EditBone *ebone = static_cast<EditBone *>(te->directdata); char newname[sizeof(ebone->name)]; /* restore bone name */ @@ -758,6 +763,7 @@ static void namebutton_fn(bContext *C, void *tsep, char *oldname) ED_armature_bone_rename(bmain, arm, oldname, newname); WM_msg_publish_rna_prop(mbus, &arm->id, ebone, EditBone, name); WM_event_add_notifier(C, NC_OBJECT | ND_POSE, nullptr); + DEG_id_tag_update(tselem->id, ID_RECALC_COPY_ON_WRITE); } break; } @@ -767,7 +773,7 @@ static void namebutton_fn(bContext *C, void *tsep, char *oldname) outliner_viewcontext_init(C, &tvc); bArmature *arm = (bArmature *)tselem->id; - Bone *bone = reinterpret_cast<Bone *>(te->directdata); + Bone *bone = static_cast<Bone *>(te->directdata); char newname[sizeof(bone->name)]; /* always make current object active */ @@ -779,6 +785,7 @@ static void namebutton_fn(bContext *C, void *tsep, char *oldname) ED_armature_bone_rename(bmain, arm, oldname, newname); WM_msg_publish_rna_prop(mbus, &arm->id, bone, Bone, name); WM_event_add_notifier(C, NC_OBJECT | ND_POSE, nullptr); + DEG_id_tag_update(tselem->id, ID_RECALC_COPY_ON_WRITE); break; } case TSE_POSE_CHANNEL: { @@ -787,7 +794,7 @@ static void namebutton_fn(bContext *C, void *tsep, char *oldname) Object *ob = (Object *)tselem->id; bArmature *arm = (bArmature *)ob->data; - bPoseChannel *pchan = reinterpret_cast<bPoseChannel *>(te->directdata); + bPoseChannel *pchan = static_cast<bPoseChannel *>(te->directdata); char newname[sizeof(pchan->name)]; /* always make current pose-bone active */ @@ -798,15 +805,16 @@ static void namebutton_fn(bContext *C, void *tsep, char *oldname) /* restore bone name */ BLI_strncpy(newname, pchan->name, sizeof(pchan->name)); BLI_strncpy(pchan->name, oldname, sizeof(pchan->name)); - ED_armature_bone_rename( - bmain, reinterpret_cast<bArmature *>(ob->data), oldname, newname); + ED_armature_bone_rename(bmain, static_cast<bArmature *>(ob->data), oldname, newname); WM_msg_publish_rna_prop(mbus, &arm->id, pchan->bone, Bone, name); WM_event_add_notifier(C, NC_OBJECT | ND_POSE, nullptr); + DEG_id_tag_update(tselem->id, ID_RECALC_COPY_ON_WRITE); + DEG_id_tag_update(&arm->id, ID_RECALC_COPY_ON_WRITE); break; } case TSE_POSEGRP: { Object *ob = (Object *)tselem->id; /* id = object. */ - bActionGroup *grp = reinterpret_cast<bActionGroup *>(te->directdata); + bActionGroup *grp = static_cast<bActionGroup *>(te->directdata); BLI_uniquename(&ob->pose->agroups, grp, @@ -816,11 +824,12 @@ static void namebutton_fn(bContext *C, void *tsep, char *oldname) sizeof(grp->name)); WM_msg_publish_rna_prop(mbus, &ob->id, grp, ActionGroup, name); WM_event_add_notifier(C, NC_OBJECT | ND_POSE, ob); + DEG_id_tag_update(tselem->id, ID_RECALC_COPY_ON_WRITE); break; } case TSE_GP_LAYER: { bGPdata *gpd = (bGPdata *)tselem->id; /* id = GP Datablock */ - bGPDlayer *gpl = reinterpret_cast<bGPDlayer *>(te->directdata); + bGPDlayer *gpl = static_cast<bGPDlayer *>(te->directdata); /* always make layer active */ BKE_gpencil_layer_active_set(gpd, gpl); @@ -832,11 +841,12 @@ static void namebutton_fn(bContext *C, void *tsep, char *oldname) WM_msg_publish_rna_prop(mbus, &gpd->id, gpl, GPencilLayer, info); DEG_id_tag_update(&gpd->id, ID_RECALC_GEOMETRY); WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_SELECTED, gpd); + DEG_id_tag_update(tselem->id, ID_RECALC_COPY_ON_WRITE); break; } case TSE_R_LAYER: { Scene *scene = (Scene *)tselem->id; - ViewLayer *view_layer = reinterpret_cast<ViewLayer *>(te->directdata); + ViewLayer *view_layer = static_cast<ViewLayer *>(te->directdata); /* Restore old name. */ char newname[sizeof(view_layer->name)]; @@ -847,14 +857,17 @@ static void namebutton_fn(bContext *C, void *tsep, char *oldname) BKE_view_layer_rename(bmain, scene, view_layer, newname); WM_msg_publish_rna_prop(mbus, &scene->id, view_layer, ViewLayer, name); WM_event_add_notifier(C, NC_ID | NA_RENAME, nullptr); + DEG_id_tag_update(tselem->id, ID_RECALC_COPY_ON_WRITE); break; } case TSE_LAYER_COLLECTION: { /* The ID is a #Collection, not a #LayerCollection */ Collection *collection = (Collection *)tselem->id; + BKE_main_namemap_remove_name(bmain, &collection->id, oldname); BLI_libblock_ensure_unique_name(bmain, collection->id.name); WM_msg_publish_rna_prop(mbus, &collection->id, &collection->id, ID, name); WM_event_add_notifier(C, NC_ID | NA_RENAME, nullptr); + DEG_id_tag_update(tselem->id, ID_RECALC_COPY_ON_WRITE); break; } } @@ -987,7 +1000,7 @@ static bool outliner_restrict_properties_collection_set(Scene *scene, { TreeStoreElem *tselem = TREESTORE(te); LayerCollection *layer_collection = (tselem->type == TSE_LAYER_COLLECTION) ? - reinterpret_cast<LayerCollection *>(te->directdata) : + static_cast<LayerCollection *>(te->directdata) : nullptr; Collection *collection = outliner_collection_from_tree_element(te); @@ -1101,7 +1114,7 @@ static void outliner_draw_restrictbuts(uiBlock *block, ELEM(space_outliner->outlinevis, SO_SCENES, SO_VIEW_LAYER)) { if (space_outliner->show_restrict_flags & SO_RESTRICT_RENDER) { /* View layer render toggle. */ - ViewLayer *layer = reinterpret_cast<ViewLayer *>(te->directdata); + ViewLayer *layer = static_cast<ViewLayer *>(te->directdata); bt = uiDefIconButBitS(block, UI_BTYPE_ICON_TOGGLE_N, @@ -1325,7 +1338,7 @@ static void outliner_draw_restrictbuts(uiBlock *block, bPoseChannel *pchan = (bPoseChannel *)te->directdata; Bone *bone = pchan->bone; Object *ob = (Object *)tselem->id; - bArmature *arm = reinterpret_cast<bArmature *>(ob->data); + bArmature *arm = static_cast<bArmature *>(ob->data); RNA_pointer_create(&arm->id, &RNA_Bone, bone, &ptr); @@ -1475,8 +1488,7 @@ static void outliner_draw_restrictbuts(uiBlock *block, scene, te, &collection_ptr, &layer_collection_ptr, &props, &props_active)) { LayerCollection *layer_collection = (tselem->type == TSE_LAYER_COLLECTION) ? - reinterpret_cast<LayerCollection *>( - te->directdata) : + static_cast<LayerCollection *>(te->directdata) : nullptr; Collection *collection = outliner_collection_from_tree_element(te); @@ -1802,18 +1814,17 @@ static void outliner_draw_overrides_rna_buts(uiBlock *block, if (!outliner_is_element_in_view(te, ®ion->v2d)) { continue; } - if (tselem->type != TSE_LIBRARY_OVERRIDE) { + TreeElementOverridesProperty *override_elem = tree_element_cast<TreeElementOverridesProperty>( + te); + if (!override_elem) { continue; } - TreeElementOverridesProperty &override_elem = *tree_element_cast<TreeElementOverridesProperty>( - te); - - if (!override_elem.is_rna_path_valid) { + if (!override_elem->is_rna_path_valid) { uiBut *but = uiDefBut(block, UI_BTYPE_LABEL, 0, - override_elem.rna_path.c_str(), + override_elem->rna_path.c_str(), x + pad_x, te->ys + pad_y, item_max_width, @@ -1828,8 +1839,28 @@ static void outliner_draw_overrides_rna_buts(uiBlock *block, continue; } - PointerRNA *ptr = &override_elem.override_rna_ptr; - PropertyRNA *prop = &override_elem.override_rna_prop; + if (const TreeElementOverridesPropertyOperation *override_op_elem = + tree_element_cast<TreeElementOverridesPropertyOperation>(te)) { + StringRefNull op_label = override_op_elem->getOverrideOperationLabel(); + uiDefBut(block, + UI_BTYPE_LABEL, + 0, + op_label.c_str(), + x + pad_x, + te->ys + pad_y, + item_max_width, + item_height, + nullptr, + 0, + 0, + 0, + 0, + ""); + continue; + } + + PointerRNA *ptr = &override_elem->override_rna_ptr; + PropertyRNA *prop = &override_elem->override_rna_prop; const PropertyType prop_type = RNA_property_type(prop); uiBut *auto_but = uiDefAutoButR(block, @@ -1927,7 +1958,7 @@ static void outliner_draw_separator(ARegion *region, const int x) GPU_line_width(1.0f); uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); - immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); + immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR); immUniformThemeColorShadeAlpha(TH_BACK, -15, -200); immBegin(GPU_PRIM_LINES, 2); @@ -2495,7 +2526,7 @@ TreeElementIcon tree_element_get_icon(TreeStoreElem *tselem, TreeElement *te) data.drag_id = tselem->id; break; case TSE_CONSTRAINT: { - bConstraint *con = reinterpret_cast<bConstraint *>(te->directdata); + bConstraint *con = static_cast<bConstraint *>(te->directdata); data.drag_id = tselem->id; switch ((eBConstraint_Types)con->type) { case CONSTRAINT_TYPE_CAMERASOLVER: @@ -2612,9 +2643,8 @@ TreeElementIcon tree_element_get_icon(TreeStoreElem *tselem, TreeElement *te) data.drag_id = tselem->id; if (ob->type != OB_GPENCIL) { - ModifierData *md = reinterpret_cast<ModifierData *>( - BLI_findlink(&ob->modifiers, tselem->nr)); - const ModifierTypeInfo *modifier_type = reinterpret_cast<const ModifierTypeInfo *>( + ModifierData *md = static_cast<ModifierData *>(BLI_findlink(&ob->modifiers, tselem->nr)); + const ModifierTypeInfo *modifier_type = static_cast<const ModifierTypeInfo *>( BKE_modifier_get_info((ModifierType)md->type)); if (modifier_type != nullptr) { data.icon = modifier_type->icon; @@ -2625,7 +2655,7 @@ TreeElementIcon tree_element_get_icon(TreeStoreElem *tselem, TreeElement *te) } else { /* grease pencil modifiers */ - GpencilModifierData *md = reinterpret_cast<GpencilModifierData *>( + GpencilModifierData *md = static_cast<GpencilModifierData *>( BLI_findlink(&ob->greasepencil_modifiers, tselem->nr)); switch ((GpencilModifierType)md->type) { case eGpencilModifierType_Noise: @@ -2784,7 +2814,7 @@ TreeElementIcon tree_element_get_icon(TreeStoreElem *tselem, TreeElement *te) const PointerRNA &ptr = te_rna_struct->getPointerRNA(); if (RNA_struct_is_ID(ptr.type)) { - data.drag_id = reinterpret_cast<ID *>(ptr.data); + data.drag_id = static_cast<ID *>(ptr.data); data.icon = RNA_struct_ui_icon(ptr.type); } else { @@ -2824,10 +2854,20 @@ TreeElementIcon tree_element_get_icon(TreeStoreElem *tselem, TreeElement *te) data.icon = tree_element_get_icon_from_id(tselem->id); } + if (!te->abstract_element) { + /* Pass */ + } + else if (auto icon = te->abstract_element->getIcon()) { + data.icon = *icon; + } + return data; } -static void tselem_draw_icon(uiBlock *block, +/** + * \return true if the element has an icon that was drawn, false if it doesn't have an icon. + */ +static bool tselem_draw_icon(uiBlock *block, int xmax, float x, float y, @@ -2838,7 +2878,7 @@ static void tselem_draw_icon(uiBlock *block, { TreeElementIcon data = tree_element_get_icon(tselem, te); if (data.icon == 0) { - return; + return false; } const bool is_collection = outliner_is_collection_tree_element(te); @@ -2862,7 +2902,7 @@ static void tselem_draw_icon(uiBlock *block, 0.0f, btheme->collection_color[collection->color_tag].color, true); - return; + return true; } } @@ -2894,6 +2934,8 @@ static void tselem_draw_icon(uiBlock *block, alpha, (data.drag_id && ID_IS_LINKED(data.drag_id)) ? data.drag_id->lib->filepath : ""); } + + return true; } /** @@ -3088,6 +3130,7 @@ static void outliner_draw_iconrow(bContext *C, TSE_GP_LAYER, TSE_LIBRARY_OVERRIDE_BASE, TSE_LIBRARY_OVERRIDE, + TSE_LIBRARY_OVERRIDE_OPERATION, TSE_BONE, TSE_EBONE, TSE_POSE_CHANNEL, @@ -3292,7 +3335,7 @@ static void outliner_draw_tree_element(bContext *C, /* Scene collection in view layer can't expand/collapse. */ } else if (te->subtree.first || ((tselem->type == TSE_SOME_ID) && (te->idcode == ID_SCE)) || - (te->flag & TE_LAZY_CLOSED)) { + (te->flag & TE_PRETEND_HAS_CHILDREN)) { /* Open/close icon, only when sub-levels, except for scene. */ int icon_x = startx; @@ -3313,15 +3356,15 @@ static void outliner_draw_tree_element(bContext *C, offsx += UI_UNIT_X; /* Data-type icon. */ - if (!(ELEM(tselem->type, TSE_RNA_PROPERTY, TSE_RNA_ARRAY_ELEM, TSE_ID_BASE))) { - tselem_draw_icon(block, - xmax, - (float)startx + offsx, - (float)*starty, - tselem, - te, - (tselem->flag & TSE_HIGHLIGHTED_ICON) ? alpha_fac + 0.5f : alpha_fac, - true); + if (!(ELEM(tselem->type, TSE_RNA_PROPERTY, TSE_RNA_ARRAY_ELEM, TSE_ID_BASE)) && + tselem_draw_icon(block, + xmax, + (float)startx + offsx, + (float)*starty, + tselem, + te, + (tselem->flag & TSE_HIGHLIGHTED_ICON) ? alpha_fac + 0.5f : alpha_fac, + true)) { offsx += UI_UNIT_X + 4 * ufac; } else { @@ -3508,7 +3551,7 @@ static void outliner_draw_hierarchy_lines(SpaceOutliner *space_outliner, uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); uchar col[4]; - immBindBuiltinProgram(GPU_SHADER_2D_LINE_DASHED_UNIFORM_COLOR); + immBindBuiltinProgram(GPU_SHADER_3D_LINE_DASHED_UNIFORM_COLOR); float viewport_size[4]; GPU_viewport_size_get_f(viewport_size); @@ -3539,7 +3582,7 @@ static void outliner_draw_struct_marks(ARegion *region, if (tselem->type == TSE_RNA_STRUCT) { GPUVertFormat *format = immVertexFormat(); uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_I32, 2, GPU_FETCH_INT_TO_FLOAT); - immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); + immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR); immThemeColorShadeAlpha(TH_BACK, -15, -200); immRecti(pos, 0, *starty + 1, (int)region->v2d.cur.xmax, *starty + UI_UNIT_Y - 1); immUnbindProgram(); @@ -3552,7 +3595,7 @@ static void outliner_draw_struct_marks(ARegion *region, if (tselem->type == TSE_RNA_STRUCT) { GPUVertFormat *format = immVertexFormat(); uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); - immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); + immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR); immThemeColorShadeAlpha(TH_BACK, -15, -200); immBegin(GPU_PRIM_LINES, 2); @@ -3657,7 +3700,7 @@ static void outliner_draw_highlights(ARegion *region, GPU_blend(GPU_BLEND_ALPHA); GPUVertFormat *format = immVertexFormat(); uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_I32, 2, GPU_FETCH_INT_TO_FLOAT); - immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); + immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR); outliner_draw_highlights(pos, region, space_outliner, @@ -3758,7 +3801,7 @@ static void outliner_back(ARegion *region) GPUVertFormat *format = immVertexFormat(); uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); - immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); + immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR); float col_alternating[4]; UI_GetThemeColor4fv(TH_ROW_ALTERNATE, col_alternating); @@ -3950,3 +3993,5 @@ void draw_outliner(const bContext *C) } /** \} */ + +} // namespace blender::ed::outliner diff --git a/source/blender/editors/space_outliner/outliner_edit.cc b/source/blender/editors/space_outliner/outliner_edit.cc index c4a9398a5f7..8618c2999c2 100644 --- a/source/blender/editors/space_outliner/outliner_edit.cc +++ b/source/blender/editors/space_outliner/outliner_edit.cc @@ -29,6 +29,7 @@ #include "BKE_blender_copybuffer.h" #include "BKE_context.h" #include "BKE_idtype.h" +#include "BKE_layer.h" #include "BKE_lib_id.h" #include "BKE_lib_override.h" #include "BKE_lib_query.h" @@ -55,6 +56,7 @@ #include "RNA_access.h" #include "RNA_define.h" #include "RNA_enum_types.h" +#include "RNA_path.h" #include "GPU_material.h" @@ -64,6 +66,8 @@ using namespace blender::ed::outliner; +namespace blender::ed::outliner { + static void outliner_show_active(SpaceOutliner *space_outliner, ARegion *region, TreeElement *te, @@ -143,14 +147,10 @@ void OUTLINER_OT_highlight_update(wmOperatorType *ot) /** \name Toggle Open/Closed Operator * \{ */ -void outliner_item_openclose(SpaceOutliner *space_outliner, - TreeElement *te, - bool open, - bool toggle_all) +void outliner_item_openclose(TreeElement *te, bool open, bool toggle_all) { - /* Prevent opening leaf elements in the tree unless in the Data API display mode because in that - * mode subtrees are empty unless expanded. */ - if (space_outliner->outlinevis != SO_DATA_API && BLI_listbase_is_empty(&te->subtree)) { + /* Only allow opening elements with children. */ + if (!(te->flag & TE_PRETEND_HAS_CHILDREN) && BLI_listbase_is_empty(&te->subtree)) { return; } @@ -197,7 +197,7 @@ static int outliner_item_openclose_modal(bContext *C, wmOperator *op, const wmEv /* Only toggle openclose on the same level as the first clicked element */ if (te->xs == data->x_location) { - outliner_item_openclose(space_outliner, te, data->open, false); + outliner_item_openclose(te, data->open, false); outliner_tag_redraw_avoid_rebuild_on_open_change(space_outliner, region); } @@ -241,7 +241,7 @@ static int outliner_item_openclose_invoke(bContext *C, wmOperator *op, const wmE const bool open = (tselem->flag & TSE_CLOSED) || (toggle_all && (outliner_flag_is_any_test(&te->subtree, TSE_CLOSED, 1))); - outliner_item_openclose(space_outliner, te, open, toggle_all); + outliner_item_openclose(te, open, toggle_all); outliner_tag_redraw_avoid_rebuild_on_open_change(space_outliner, region); /* Only toggle once for single click toggling */ @@ -447,7 +447,7 @@ void OUTLINER_OT_item_rename(wmOperatorType *ot) /** \name ID Delete Operator * \{ */ -static void id_delete(bContext *C, ReportList *reports, TreeElement *te, TreeStoreElem *tselem) +static void id_delete_tag(bContext *C, ReportList *reports, TreeElement *te, TreeStoreElem *tselem) { Main *bmain = CTX_data_main(C); ID *id = tselem->id; @@ -484,35 +484,39 @@ static void id_delete(bContext *C, ReportList *reports, TreeElement *te, TreeSto return; } if (te->idcode == ID_WS) { - BKE_workspace_id_tag_all_visible(bmain, LIB_TAG_DOIT); - if (id->tag & LIB_TAG_DOIT) { + BKE_workspace_id_tag_all_visible(bmain, LIB_TAG_PRE_EXISTING); + if (id->tag & LIB_TAG_PRE_EXISTING) { BKE_reportf( reports, RPT_WARNING, "Cannot delete currently visible workspace id '%s'", id->name); + BKE_main_id_tag_idcode(bmain, ID_WS, LIB_TAG_PRE_EXISTING, false); return; } + BKE_main_id_tag_idcode(bmain, ID_WS, LIB_TAG_PRE_EXISTING, false); } - BKE_id_delete(bmain, id); + id->tag |= LIB_TAG_DOIT; WM_event_add_notifier(C, NC_WINDOW, nullptr); } -void id_delete_fn(bContext *C, - ReportList *reports, - Scene *UNUSED(scene), - TreeElement *te, - TreeStoreElem *UNUSED(tsep), - TreeStoreElem *tselem, - void *UNUSED(user_data)) +void id_delete_tag_fn(bContext *C, + ReportList *reports, + Scene *UNUSED(scene), + TreeElement *te, + TreeStoreElem *UNUSED(tsep), + TreeStoreElem *tselem, + void *UNUSED(user_data)) { - id_delete(C, reports, te, tselem); + id_delete_tag(C, reports, te, tselem); } -static int outliner_id_delete_invoke_do(bContext *C, - ReportList *reports, - TreeElement *te, - const float mval[2]) +static int outliner_id_delete_tag(bContext *C, + ReportList *reports, + TreeElement *te, + const float mval[2]) { + int id_tagged_num = 0; + if (mval[1] > te->ys && mval[1] < te->ys + UI_UNIT_Y) { TreeStoreElem *tselem = TREESTORE(te); @@ -522,26 +526,27 @@ static int outliner_id_delete_invoke_do(bContext *C, RPT_ERROR_INVALID_INPUT, "Cannot delete indirectly linked library '%s'", ((Library *)tselem->id)->filepath_abs); - return OPERATOR_CANCELLED; } - id_delete(C, reports, te, tselem); - return OPERATOR_FINISHED; + else { + id_delete_tag(C, reports, te, tselem); + id_tagged_num++; + } } } else { LISTBASE_FOREACH (TreeElement *, te_sub, &te->subtree) { - int ret; - if ((ret = outliner_id_delete_invoke_do(C, reports, te_sub, mval))) { - return ret; + if ((id_tagged_num += outliner_id_delete_tag(C, reports, te_sub, mval)) != 0) { + break; } } } - return 0; + return id_tagged_num; } static int outliner_id_delete_invoke(bContext *C, wmOperator *op, const wmEvent *event) { + Main *bmain = CTX_data_main(C); ARegion *region = CTX_wm_region(C); SpaceOutliner *space_outliner = CTX_wm_space_outliner(C); float fmval[2]; @@ -550,15 +555,21 @@ static int outliner_id_delete_invoke(bContext *C, wmOperator *op, const wmEvent UI_view2d_region_to_view(®ion->v2d, event->mval[0], event->mval[1], &fmval[0], &fmval[1]); + int id_tagged_num = 0; + BKE_main_id_tag_all(bmain, LIB_TAG_DOIT, false); LISTBASE_FOREACH (TreeElement *, te, &space_outliner->tree) { - int ret; - - if ((ret = outliner_id_delete_invoke_do(C, op->reports, te, fmval))) { - return ret; + if ((id_tagged_num += outliner_id_delete_tag(C, op->reports, te, fmval)) != 0) { + break; } } + if (id_tagged_num == 0) { + BKE_main_id_tag_all(bmain, LIB_TAG_DOIT, false); + return OPERATOR_CANCELLED; + } - return OPERATOR_CANCELLED; + BKE_id_multi_tagged_delete(bmain); + BKE_main_id_tag_all(bmain, LIB_TAG_DOIT, false); + return OPERATOR_FINISHED; } void OUTLINER_OT_id_delete(wmOperatorType *ot) @@ -586,9 +597,9 @@ static int outliner_id_remap_exec(bContext *C, wmOperator *op) SpaceOutliner *space_outliner = CTX_wm_space_outliner(C); const short id_type = (short)RNA_enum_get(op->ptr, "id_type"); - ID *old_id = reinterpret_cast<ID *>( + ID *old_id = static_cast<ID *>( BLI_findlink(which_libbase(CTX_data_main(C), id_type), RNA_enum_get(op->ptr, "old_id"))); - ID *new_id = reinterpret_cast<ID *>( + ID *new_id = static_cast<ID *>( BLI_findlink(which_libbase(CTX_data_main(C), id_type), RNA_enum_get(op->ptr, "new_id"))); /* check for invalid states */ @@ -682,9 +693,9 @@ static const EnumPropertyItem *outliner_id_itemf(bContext *C, int i = 0; short id_type = (short)RNA_enum_get(ptr, "id_type"); - ID *id = reinterpret_cast<ID *>(which_libbase(CTX_data_main(C), id_type)->first); + ID *id = static_cast<ID *>(which_libbase(CTX_data_main(C), id_type)->first); - for (; id; id = reinterpret_cast<ID *>(id->next)) { + for (; id; id = static_cast<ID *>(id->next)) { item_tmp.identifier = item_tmp.name = id->name + 2; item_tmp.value = i++; RNA_enum_item_add(&item, &totitem, &item_tmp); @@ -1255,7 +1266,7 @@ static TreeElement *outliner_show_active_get_element(bContext *C, { TreeElement *te; - Object *obact = OBACT(view_layer); + Object *obact = BKE_view_layer_active_object_get(view_layer); if (!obact) { return nullptr; @@ -1398,129 +1409,6 @@ void OUTLINER_OT_scroll_page(wmOperatorType *ot) /** \} */ -#if 0 /* TODO: probably obsolete now with filtering? */ - -/* -------------------------------------------------------------------- */ -/** \name Search - * \{ */ - - -/* find next element that has this name */ -static TreeElement *outliner_find_name( - SpaceOutliner *space_outliner, ListBase *lb, char *name, int flags, TreeElement *prev, int *prevFound) -{ - TreeElement *te, *tes; - - for (te = lb->first; te; te = te->next) { - int found = outliner_filter_has_name(te, name, flags); - - if (found) { - /* name is right, but is element the previous one? */ - if (prev) { - if ((te != prev) && (*prevFound)) { - return te; - } - if (te == prev) { - *prevFound = 1; - } - } - else { - return te; - } - } - - tes = outliner_find_name(space_outliner, &te->subtree, name, flags, prev, prevFound); - if (tes) { - return tes; - } - } - - /* nothing valid found */ - return nullptr; -} - -static void outliner_find_panel( - Scene *UNUSED(scene), ARegion *region, SpaceOutliner *space_outliner, int again, int flags) -{ - ReportList *reports = nullptr; /* CTX_wm_reports(C); */ - TreeElement *te = nullptr; - TreeElement *last_find; - TreeStoreElem *tselem; - int ytop, xdelta, prevFound = 0; - char name[sizeof(space_outliner->search_string)]; - - /* get last found tree-element based on stored search_tse */ - last_find = outliner_find_tse(space_outliner, &space_outliner->search_tse); - - /* determine which type of search to do */ - if (again && last_find) { - /* no popup panel - previous + user wanted to search for next after previous */ - BLI_strncpy(name, space_outliner->search_string, sizeof(name)); - flags = space_outliner->search_flags; - - /* try to find matching element */ - te = outliner_find_name(space_outliner, &space_outliner->tree, name, flags, last_find, &prevFound); - if (te == nullptr) { - /* no more matches after previous, start from beginning again */ - prevFound = 1; - te = outliner_find_name(space_outliner, &space_outliner->tree, name, flags, last_find, &prevFound); - } - } - else { - /* pop up panel - no previous, or user didn't want search after previous */ - name[0] = '\0'; - // XXX if (sbutton(name, 0, sizeof(name) - 1, "Find: ") && name[0]) { - // te = outliner_find_name(space_outliner, &space_outliner->tree, name, flags, nullptr, &prevFound); - // } - // else return; XXX RETURN! XXX - } - - /* do selection and reveal */ - if (te) { - tselem = TREESTORE(te); - if (tselem) { - /* expand branches so that it will be visible, we need to get correct coordinates */ - if (outliner_open_back(space_outliner, te)) { - outliner_set_coordinates(region, space_outliner); - } - - /* deselect all visible, and select found element */ - outliner_flag_set(space_outliner, &space_outliner->tree, TSE_SELECTED, 0); - tselem->flag |= TSE_SELECTED; - - /* Make `te->ys` center of view. */ - ytop = (int)(te->ys + BLI_rctf_size_y(®ion->v2d.mask) / 2); - if (ytop > 0) { - ytop = 0; - } - region->v2d.cur.ymax = (float)ytop; - region->v2d.cur.ymin = (float)(ytop - BLI_rctf_size_y(®ion->v2d.mask)); - - /* Make `te->xs` ==> `te->xend` center of view. */ - xdelta = (int)(te->xs - region->v2d.cur.xmin); - region->v2d.cur.xmin += xdelta; - region->v2d.cur.xmax += xdelta; - - /* store selection */ - space_outliner->search_tse = *tselem; - - BLI_strncpy(space_outliner->search_string, name, sizeof(space_outliner->search_string)); - space_outliner->search_flags = flags; - - /* redraw */ - ED_region_tag_redraw_no_rebuild(region); - } - } - else { - /* no tree-element found */ - BKE_reportf(reports, RPT_WARNING, "Not found: %s", name); - } -} - -/** \} */ - -#endif /* if 0 */ - /* -------------------------------------------------------------------- */ /** \name Show One Level Operator * \{ */ @@ -1806,7 +1694,7 @@ static void tree_element_to_path(TreeElement *te, /* ptr->data not ptr->owner_id seems to be the one we want, * since ptr->data is sometimes the owner of this ID? */ if (RNA_struct_is_ID(ptr.type)) { - *id = reinterpret_cast<ID *>(ptr.data); + *id = static_cast<ID *>(ptr.data); /* clear path */ if (*path) { @@ -2041,8 +1929,7 @@ static KeyingSet *verify_active_keyingset(Scene *scene, short add) /* try to find one from scene */ if (scene->active_keyingset > 0) { - ks = reinterpret_cast<KeyingSet *>( - BLI_findlink(&scene->keyingsets, scene->active_keyingset - 1)); + ks = static_cast<KeyingSet *>(BLI_findlink(&scene->keyingsets, scene->active_keyingset - 1)); } /* Add if none found */ @@ -2346,3 +2233,5 @@ void OUTLINER_OT_orphans_purge(wmOperatorType *ot) } /** \} */ + +} // namespace blender::ed::outliner diff --git a/source/blender/editors/space_outliner/outliner_intern.hh b/source/blender/editors/space_outliner/outliner_intern.hh index a0dcb49aa43..ad5d653949c 100644 --- a/source/blender/editors/space_outliner/outliner_intern.hh +++ b/source/blender/editors/space_outliner/outliner_intern.hh @@ -14,10 +14,6 @@ /* Needed for `tree_element_cast()`. */ #include "tree/tree_element.hh" -#ifdef __cplusplus -extern "C" { -#endif - /* internal exports only */ struct ARegion; @@ -27,7 +23,6 @@ struct ListBase; struct Main; struct Object; struct Scene; -struct TreeElement; struct TreeStoreElem; struct ViewLayer; struct bContext; @@ -37,47 +32,52 @@ struct View2D; struct wmKeyConfig; struct wmOperatorType; +namespace blender::bke::outliner::treehash { +class TreeHash; +} + namespace blender::ed::outliner { + class AbstractTreeDisplay; class AbstractTreeElement; -} // namespace blender::ed::outliner -namespace outliner = blender::ed::outliner; +namespace treehash = blender::bke::outliner::treehash; + +struct TreeElement; struct SpaceOutliner_Runtime { /** Object to create and manage the tree for a specific display type (View Layers, Scenes, * Blender File, etc.). */ - std::unique_ptr<outliner::AbstractTreeDisplay> tree_display; + std::unique_ptr<AbstractTreeDisplay> tree_display; - /** Pointers to tree-store elements, grouped by `(id, type, nr)` - * in hash-table for faster searching. */ - struct GHash *treehash; + /* Hash table for tree-store elements, using `(id, type, index)` as key. */ + std::unique_ptr<treehash::TreeHash> tree_hash; SpaceOutliner_Runtime() = default; /** Used for copying runtime data to a duplicated space. */ SpaceOutliner_Runtime(const SpaceOutliner_Runtime &); - ~SpaceOutliner_Runtime(); + ~SpaceOutliner_Runtime() = default; }; -typedef enum TreeElementInsertType { +enum TreeElementInsertType { TE_INSERT_BEFORE, TE_INSERT_AFTER, TE_INSERT_INTO, -} TreeElementInsertType; +}; -typedef enum TreeTraversalAction { +enum TreeTraversalAction { /** Continue traversal regularly, don't skip children. */ TRAVERSE_CONTINUE = 0, /** Stop traversal. */ TRAVERSE_BREAK, /** Continue traversal, but skip children of traversed element. */ TRAVERSE_SKIP_CHILDS, -} TreeTraversalAction; +}; -typedef TreeTraversalAction (*TreeTraversalFunc)(struct TreeElement *te, void *customdata); +typedef TreeTraversalAction (*TreeTraversalFunc)(TreeElement *te, void *customdata); -typedef struct TreeElement { - struct TreeElement *next, *prev, *parent; +struct TreeElement { + TreeElement *next, *prev, *parent; /** * The new inheritance based representation of the element (a derived type of base @@ -85,7 +85,7 @@ typedef struct TreeElement { * be moved to it and operations based on the type should become virtual methods of the class * hierarchy. */ - std::unique_ptr<outliner::AbstractTreeElement> abstract_element; + std::unique_ptr<AbstractTreeElement> abstract_element; ListBase subtree; int xs, ys; /* Do selection. */ @@ -96,12 +96,12 @@ typedef struct TreeElement { short xend; /* Width of item display, for select. */ const char *name; void *directdata; /* Armature Bones, Base, ... */ -} TreeElement; +}; -typedef struct TreeElementIcon { +struct TreeElementIcon { struct ID *drag_id, *drag_parent; int icon; -} TreeElementIcon; +}; #define TREESTORE_ID_TYPE(_id) \ (ELEM(GS((_id)->name), \ @@ -153,7 +153,10 @@ enum { /* Closed items display their children as icon within the row. TE_ICONROW is for * these child-items that are visible but only within the row of the closed parent. */ TE_ICONROW = (1 << 1), - TE_LAZY_CLOSED = (1 << 2), + /** Treat the element as if it had children, e.g. draw an icon to un-collapse it, even if it + * doesn't. Used where children are lazy-built only if the parent isn't collapsed (see + * #AbstractTreeDisplay::is_lazy_built()). */ + TE_PRETEND_HAS_CHILDREN = (1 << 2), TE_FREE_NAME = (1 << 3), TE_DRAGGING = (1 << 4), TE_CHILD_NOT_IN_COLLECTION = (1 << 6), @@ -165,17 +168,17 @@ enum { /* button events */ #define OL_NAMEBUTTON 1 -typedef enum { +enum eOLDrawState { OL_DRAWSEL_NONE = 0, /* inactive (regular black text) */ OL_DRAWSEL_NORMAL = 1, /* active object (draws white text) */ OL_DRAWSEL_ACTIVE = 2, /* active obdata (draws a circle around the icon) */ -} eOLDrawState; +}; -typedef enum { +enum eOLSetState { OL_SETSEL_NONE = 0, /* don't change the selection state */ OL_SETSEL_NORMAL = 1, /* select the item */ OL_SETSEL_EXTEND = 2, /* select the item and extend (also toggles selection) */ -} eOLSetState; +}; /* get TreeStoreElem associated with a TreeElement * < a: (TreeElement) tree element to find stored element for @@ -225,29 +228,29 @@ typedef enum { * Container to avoid passing around these variables to many functions. * Also so we can have one place to assign these variables. */ -typedef struct TreeViewContext { +struct TreeViewContext { /* Scene level. */ struct Scene *scene; struct ViewLayer *view_layer; /* Object level. */ - /** Avoid OBACT macro everywhere. */ + /** Avoid `BKE_view_layer_active_object_get` everywhere. */ Object *obact; Object *ob_edit; /** * The pose object may not be the active object (when in weight paint mode). * Checking this in draw loops isn't efficient, so set only once. */ Object *ob_pose; -} TreeViewContext; +}; -typedef enum TreeItemSelectAction { +enum TreeItemSelectAction { OL_ITEM_DESELECT = 0, /* Deselect the item */ OL_ITEM_SELECT = (1 << 0), /* Select the item */ OL_ITEM_SELECT_DATA = (1 << 1), /* Select object data */ OL_ITEM_ACTIVATE = (1 << 2), /* Activate the item */ OL_ITEM_EXTEND = (1 << 3), /* Extend the current selection */ OL_ITEM_RECURSIVE = (1 << 4), /* Select recursively */ -} TreeItemSelectAction; +}; /* outliner_tree.c ----------------------------------------------- */ @@ -270,24 +273,19 @@ void outliner_build_tree(struct Main *mainvar, struct SpaceOutliner *space_outliner, struct ARegion *region); -struct TreeElement *outliner_add_collection_recursive(SpaceOutliner *space_outliner, - struct Collection *collection, - TreeElement *ten); +TreeElement *outliner_add_collection_recursive(SpaceOutliner *space_outliner, + struct Collection *collection, + TreeElement *ten); bool outliner_requires_rebuild_on_select_or_active_change( const struct SpaceOutliner *space_outliner); -/** - * Check if a display mode needs a full rebuild if the open/collapsed state changes. - * Element types in these modes don't actually add children if collapsed, so the rebuild is needed. - */ -bool outliner_requires_rebuild_on_open_change(const struct SpaceOutliner *space_outliner); typedef struct IDsSelectedData { struct ListBase selected_array; } IDsSelectedData; -TreeTraversalAction outliner_find_selected_collections(struct TreeElement *te, void *customdata); -TreeTraversalAction outliner_find_selected_objects(struct TreeElement *te, void *customdata); +TreeTraversalAction outliner_collect_selected_collections(TreeElement *te, void *customdata); +TreeTraversalAction outliner_collect_selected_objects(TreeElement *te, void *customdata); /* outliner_draw.c ---------------------------------------------- */ @@ -349,7 +347,7 @@ struct bPoseChannel *outliner_find_parent_bone(TreeElement *te, TreeElement **r_ */ void outliner_item_select(struct bContext *C, struct SpaceOutliner *space_outliner, - struct TreeElement *te, + TreeElement *te, short select_flag); /** @@ -379,7 +377,7 @@ void outliner_item_mode_toggle(struct bContext *C, typedef void (*outliner_operation_fn)(struct bContext *C, struct ReportList *, struct Scene *scene, - struct TreeElement *, + TreeElement *, struct TreeStoreElem *, TreeStoreElem *, void *); @@ -408,12 +406,10 @@ int outliner_flag_is_any_test(ListBase *lb, short flag, int curlevel); * Set or unset \a flag for all outliner elements in \a lb and sub-trees. * \return if any flag was modified. */ -extern "C++" { bool outliner_flag_set(const SpaceOutliner &space_outliner, short flag, short set); bool outliner_flag_set(const ListBase &lb, short flag, short set); bool outliner_flag_flip(const SpaceOutliner &space_outliner, short flag); bool outliner_flag_flip(const ListBase &lb, short flag); -} void item_rename_fn(struct bContext *C, struct ReportList *reports, @@ -425,29 +421,29 @@ void item_rename_fn(struct bContext *C, void lib_relocate_fn(struct bContext *C, struct ReportList *reports, struct Scene *scene, - struct TreeElement *te, + TreeElement *te, struct TreeStoreElem *tsep, struct TreeStoreElem *tselem, void *user_data); void lib_reload_fn(struct bContext *C, struct ReportList *reports, struct Scene *scene, - struct TreeElement *te, + TreeElement *te, struct TreeStoreElem *tsep, struct TreeStoreElem *tselem, void *user_data); -void id_delete_fn(struct bContext *C, - struct ReportList *reports, - struct Scene *scene, - struct TreeElement *te, - struct TreeStoreElem *tsep, - struct TreeStoreElem *tselem, - void *user_data); +void id_delete_tag_fn(struct bContext *C, + struct ReportList *reports, + struct Scene *scene, + TreeElement *te, + struct TreeStoreElem *tsep, + struct TreeStoreElem *tselem, + void *user_data); void id_remap_fn(struct bContext *C, struct ReportList *reports, struct Scene *scene, - struct TreeElement *te, + TreeElement *te, struct TreeStoreElem *tsep, struct TreeStoreElem *tselem, void *user_data); @@ -461,10 +457,7 @@ void outliner_set_coordinates(const struct ARegion *region, /** * Open or close a tree element, optionally toggling all children recursively. */ -void outliner_item_openclose(struct SpaceOutliner *space_outliner, - TreeElement *te, - bool open, - bool toggle_all); +void outliner_item_openclose(TreeElement *te, bool open, bool toggle_all); /* outliner_dragdrop.c */ @@ -530,6 +523,8 @@ void OUTLINER_OT_operation(struct wmOperatorType *ot); void OUTLINER_OT_scene_operation(struct wmOperatorType *ot); void OUTLINER_OT_object_operation(struct wmOperatorType *ot); void OUTLINER_OT_lib_operation(struct wmOperatorType *ot); +void OUTLINER_OT_liboverride_operation(struct wmOperatorType *ot); +void OUTLINER_OT_liboverride_troubleshoot_operation(struct wmOperatorType *ot); void OUTLINER_OT_id_operation(struct wmOperatorType *ot); void OUTLINER_OT_id_remap(struct wmOperatorType *ot); void OUTLINER_OT_id_copy(struct wmOperatorType *ot); @@ -610,10 +605,6 @@ TreeElement *outliner_find_item_at_x_in_row(const SpaceOutliner *space_outliner, bool *r_is_merged_icon, bool *r_is_over_icon); /** - * `tse` is not in the tree-store, we use its contents to find a match. - */ -TreeElement *outliner_find_tse(struct SpaceOutliner *space_outliner, const TreeStoreElem *tse); -/** * Find specific item from the trees-tore. */ TreeElement *outliner_find_tree_element(ListBase *lb, const TreeStoreElem *store_elem); @@ -689,12 +680,6 @@ int outliner_context(const struct bContext *C, const char *member, struct bContextDataResult *result); -#ifdef __cplusplus -} -#endif - -namespace blender::ed::outliner { - /** * Helper to safely "cast" a #TreeElement to its new C++ #AbstractTreeElement, if possible. * \return nullptr if the tree-element doesn't match the requested type \a TreeElementT or the diff --git a/source/blender/editors/space_outliner/outliner_ops.cc b/source/blender/editors/space_outliner/outliner_ops.cc index 8baac45666e..cf9c4834667 100644 --- a/source/blender/editors/space_outliner/outliner_ops.cc +++ b/source/blender/editors/space_outliner/outliner_ops.cc @@ -11,6 +11,7 @@ #include "outliner_intern.hh" +namespace blender::ed::outliner { /* -------------------------------------------------------------------- */ /** \name Registration * \{ */ @@ -29,6 +30,8 @@ void outliner_operatortypes(void) WM_operatortype_append(OUTLINER_OT_object_operation); WM_operatortype_append(OUTLINER_OT_lib_operation); WM_operatortype_append(OUTLINER_OT_lib_relocate); + WM_operatortype_append(OUTLINER_OT_liboverride_operation); + WM_operatortype_append(OUTLINER_OT_liboverride_troubleshoot_operation); WM_operatortype_append(OUTLINER_OT_id_operation); WM_operatortype_append(OUTLINER_OT_id_delete); WM_operatortype_append(OUTLINER_OT_id_remap); @@ -101,3 +104,5 @@ void outliner_keymap(wmKeyConfig *keyconf) } /** \} */ + +} // namespace blender::ed::outliner diff --git a/source/blender/editors/space_outliner/outliner_query.cc b/source/blender/editors/space_outliner/outliner_query.cc index d6483c44fce..11929cbe2f0 100644 --- a/source/blender/editors/space_outliner/outliner_query.cc +++ b/source/blender/editors/space_outliner/outliner_query.cc @@ -13,7 +13,7 @@ #include "outliner_intern.hh" #include "tree/tree_display.hh" -using namespace blender::ed::outliner; +namespace blender::ed::outliner { bool outliner_shows_mode_column(const SpaceOutliner &space_outliner) { @@ -46,3 +46,5 @@ bool outliner_has_element_warnings(const SpaceOutliner &space_outliner) return recursive_fn(space_outliner.tree); } + +} // namespace blender::ed::outliner diff --git a/source/blender/editors/space_outliner/outliner_select.cc b/source/blender/editors/space_outliner/outliner_select.cc index 877e0fc325c..17e78ece941 100644 --- a/source/blender/editors/space_outliner/outliner_select.cc +++ b/source/blender/editors/space_outliner/outliner_select.cc @@ -70,7 +70,7 @@ #include "tree/tree_element_seq.hh" #include "tree/tree_iterator.hh" -using namespace blender::ed::outliner; +namespace blender::ed::outliner { /* -------------------------------------------------------------------- */ /** \name Internal Utilities @@ -220,7 +220,7 @@ static void tree_element_viewlayer_activate(bContext *C, TreeElement *te) return; } - ViewLayer *view_layer = reinterpret_cast<ViewLayer *>(te->directdata); + ViewLayer *view_layer = static_cast<ViewLayer *>(te->directdata); wmWindow *win = CTX_wm_window(C); Scene *scene = WM_window_get_active_scene(win); @@ -237,9 +237,7 @@ static void do_outliner_object_select_recursive(ViewLayer *view_layer, Object *ob_parent, bool select) { - Base *base; - - for (base = reinterpret_cast<Base *>(FIRSTBASE(view_layer)); base; base = base->next) { + LISTBASE_FOREACH (Base *, base, &view_layer->object_bases) { Object *ob = base->object; if ((((base->flag & BASE_VISIBLE_DEPSGRAPH) != 0) && BKE_object_is_child_recursive(ob_parent, ob))) { @@ -301,7 +299,7 @@ static void tree_element_object_activate(bContext *C, ob = (Object *)parent_tselem->id; /* Don't return when activating children of the previous active object. */ - if (ob == OBACT(view_layer) && set == OL_SETSEL_NONE) { + if (ob == BKE_view_layer_active_object_get(view_layer) && set == OL_SETSEL_NONE) { return; } } @@ -321,7 +319,7 @@ static void tree_element_object_activate(bContext *C, if (scene->toolsettings->object_flag & SCE_OBJECT_MODE_LOCK) { if (base != nullptr) { - Object *obact = OBACT(view_layer); + Object *obact = BKE_view_layer_active_object_get(view_layer); const eObjectMode object_mode = obact ? (eObjectMode)obact->mode : OB_MODE_OBJECT; if (base && !BKE_object_is_mode_compat(base->object, object_mode)) { if (object_mode == OB_MODE_OBJECT) { @@ -388,7 +386,8 @@ static void tree_element_material_activate(bContext *C, ViewLayer *view_layer, T /* we search for the object parent */ Object *ob = (Object *)outliner_search_back(te, ID_OB); /* Note : ob->matbits can be nullptr when a local object points to a library mesh. */ - if (ob == nullptr || ob != OBACT(view_layer) || ob->matbits == nullptr) { + if (ob == nullptr || ob != BKE_view_layer_active_object_get(view_layer) || + ob->matbits == nullptr) { return; /* just paranoia */ } @@ -418,7 +417,7 @@ static void tree_element_camera_activate(bContext *C, Scene *scene, TreeElement scene->camera = ob; Main *bmain = CTX_data_main(C); - wmWindowManager *wm = reinterpret_cast<wmWindowManager *>(bmain->wm.first); + wmWindowManager *wm = static_cast<wmWindowManager *>(bmain->wm.first); WM_windows_scene_data_sync(&wm->windows, scene); DEG_id_tag_update(&scene->id, ID_RECALC_COPY_ON_WRITE); @@ -458,7 +457,7 @@ static void tree_element_defgroup_activate(bContext *C, TreeElement *te, TreeSto static void tree_element_gplayer_activate(bContext *C, TreeElement *te, TreeStoreElem *tselem) { bGPdata *gpd = (bGPdata *)tselem->id; - bGPDlayer *gpl = reinterpret_cast<bGPDlayer *>(te->directdata); + bGPDlayer *gpl = static_cast<bGPDlayer *>(te->directdata); /* We can only have a single "active" layer at a time * and there must always be an active layer... */ @@ -486,8 +485,8 @@ static void tree_element_posechannel_activate(bContext *C, bool recursive) { Object *ob = (Object *)tselem->id; - bArmature *arm = reinterpret_cast<bArmature *>(ob->data); - bPoseChannel *pchan = reinterpret_cast<bPoseChannel *>(te->directdata); + bArmature *arm = static_cast<bArmature *>(ob->data); + bPoseChannel *pchan = static_cast<bPoseChannel *>(te->directdata); if (!(pchan->bone->flag & BONE_HIDDEN_P)) { if (set != OL_SETSEL_EXTEND) { @@ -508,7 +507,7 @@ static void tree_element_posechannel_activate(bContext *C, } if (ob != ob_iter) { - DEG_id_tag_update(reinterpret_cast<ID *>(ob_iter->data), ID_RECALC_SELECT); + DEG_id_tag_update(static_cast<ID *>(ob_iter->data), ID_RECALC_SELECT); } } MEM_freeN(objects); @@ -541,14 +540,14 @@ static void tree_element_bone_activate(bContext *C, bool recursive) { bArmature *arm = (bArmature *)tselem->id; - Bone *bone = reinterpret_cast<Bone *>(te->directdata); + Bone *bone = static_cast<Bone *>(te->directdata); if (!(bone->flag & BONE_HIDDEN_P)) { - Object *ob = OBACT(view_layer); + Object *ob = BKE_view_layer_active_object_get(view_layer); if (ob) { if (set != OL_SETSEL_EXTEND) { /* single select forces all other bones to get unselected */ - for (Bone *bone_iter = reinterpret_cast<Bone *>(arm->bonebase.first); bone_iter != nullptr; + for (Bone *bone_iter = static_cast<Bone *>(arm->bonebase.first); bone_iter != nullptr; bone_iter = bone_iter->next) { bone_iter->flag &= ~(BONE_TIPSEL | BONE_SELECTED | BONE_ROOTSEL); do_outliner_bone_select_recursive(arm, bone_iter, false); @@ -590,7 +589,7 @@ static void tree_element_ebone_activate(bContext *C, bool recursive) { bArmature *arm = (bArmature *)tselem->id; - EditBone *ebone = reinterpret_cast<EditBone *>(te->directdata); + EditBone *ebone = static_cast<EditBone *>(te->directdata); if (set == OL_SETSEL_NORMAL) { if (!(ebone->flag & BONE_HIDDEN_A)) { @@ -703,7 +702,7 @@ static void tree_element_sequence_dup_activate(Scene *scene, TreeElement *UNUSED #if 0 select_single_seq(seq, 1); #endif - Sequence *p = reinterpret_cast<Sequence *>(ed->seqbasep->first); + Sequence *p = static_cast<Sequence *>(ed->seqbasep->first); while (p) { if ((!p->strip) || (!p->strip->stripdata) || (p->strip->stripdata->name[0] == '\0')) { p = p->next; @@ -722,7 +721,7 @@ static void tree_element_sequence_dup_activate(Scene *scene, TreeElement *UNUSED static void tree_element_master_collection_activate(const bContext *C) { ViewLayer *view_layer = CTX_data_view_layer(C); - LayerCollection *layer_collection = reinterpret_cast<LayerCollection *>( + LayerCollection *layer_collection = static_cast<LayerCollection *>( view_layer->layer_collections.first); BKE_layer_collection_activate(view_layer, layer_collection); /* A very precise notifier - ND_LAYER alone is quite vague, we want to avoid unnecessary work @@ -733,7 +732,7 @@ static void tree_element_master_collection_activate(const bContext *C) static void tree_element_layer_collection_activate(bContext *C, TreeElement *te) { Scene *scene = CTX_data_scene(C); - LayerCollection *layer_collection = reinterpret_cast<LayerCollection *>(te->directdata); + LayerCollection *layer_collection = static_cast<LayerCollection *>(te->directdata); ViewLayer *view_layer = BKE_view_layer_find_from_collection(scene, layer_collection); BKE_layer_collection_activate(view_layer, layer_collection); /* A very precise notifier - ND_LAYER alone is quite vague, we want to avoid unnecessary work @@ -844,7 +843,7 @@ static eOLDrawState tree_element_defgroup_state_get(const ViewLayer *view_layer, const TreeStoreElem *tselem) { const Object *ob = (const Object *)tselem->id; - if (ob == OBACT(view_layer)) { + if (ob == BKE_view_layer_active_object_get(view_layer)) { if (BKE_object_defgroup_active_index_get(ob) == te->index + 1) { return OL_DRAWSEL_NORMAL; } @@ -857,8 +856,8 @@ static eOLDrawState tree_element_bone_state_get(const ViewLayer *view_layer, const TreeStoreElem *tselem) { const bArmature *arm = (const bArmature *)tselem->id; - const Bone *bone = reinterpret_cast<Bone *>(te->directdata); - const Object *ob = OBACT(view_layer); + const Bone *bone = static_cast<Bone *>(te->directdata); + const Object *ob = BKE_view_layer_active_object_get(view_layer); if (ob && ob->data == arm) { if (bone->flag & BONE_SELECTED) { return OL_DRAWSEL_NORMAL; @@ -869,7 +868,7 @@ static eOLDrawState tree_element_bone_state_get(const ViewLayer *view_layer, static eOLDrawState tree_element_ebone_state_get(const TreeElement *te) { - const EditBone *ebone = reinterpret_cast<EditBone *>(te->directdata); + const EditBone *ebone = static_cast<EditBone *>(te->directdata); if (ebone->flag & BONE_SELECTED) { return OL_DRAWSEL_NORMAL; } @@ -913,7 +912,7 @@ static eOLDrawState tree_element_posechannel_state_get(const Object *ob_pose, const TreeStoreElem *tselem) { const Object *ob = (const Object *)tselem->id; - const bPoseChannel *pchan = reinterpret_cast<bPoseChannel *>(te->directdata); + const bPoseChannel *pchan = static_cast<bPoseChannel *>(te->directdata); if (ob == ob_pose && ob->pose) { if (pchan->bone->flag & BONE_SELECTED) { return OL_DRAWSEL_NORMAL; @@ -929,7 +928,7 @@ static eOLDrawState tree_element_viewlayer_state_get(const bContext *C, const Tr return OL_DRAWSEL_NONE; } - const ViewLayer *view_layer = reinterpret_cast<ViewLayer *>(te->directdata); + const ViewLayer *view_layer = static_cast<ViewLayer *>(te->directdata); if (CTX_data_view_layer(C) == view_layer) { return OL_DRAWSEL_NORMAL; @@ -943,7 +942,7 @@ static eOLDrawState tree_element_posegroup_state_get(const ViewLayer *view_layer { const Object *ob = (const Object *)tselem->id; - if (ob == OBACT(view_layer) && ob->pose) { + if (ob == BKE_view_layer_active_object_get(view_layer) && ob->pose) { if (ob->pose->active_group == te->index + 1) { return OL_DRAWSEL_NORMAL; } @@ -1009,7 +1008,8 @@ static eOLDrawState tree_element_active_material_get(const ViewLayer *view_layer /* we search for the object parent */ const Object *ob = (const Object *)outliner_search_back((TreeElement *)te, ID_OB); /* Note : ob->matbits can be nullptr when a local object points to a library mesh. */ - if (ob == nullptr || ob != OBACT(view_layer) || ob->matbits == nullptr) { + if (ob == nullptr || ob != BKE_view_layer_active_object_get(view_layer) || + ob->matbits == nullptr) { return OL_DRAWSEL_NONE; /* just paranoia */ } @@ -1229,7 +1229,7 @@ static void outliner_set_properties_tab(bContext *C, TreeElement *te, TreeStoreE /* Expand the selected constraint in the properties editor. */ if (tselem->type != TSE_CONSTRAINT_BASE) { - BKE_constraint_panel_expand(reinterpret_cast<bConstraint *>(te->directdata)); + BKE_constraint_panel_expand(static_cast<bConstraint *>(te->directdata)); } break; } @@ -1242,8 +1242,7 @@ static void outliner_set_properties_tab(bContext *C, TreeElement *te, TreeStoreE Object *ob = (Object *)tselem->id; if (ob->type == OB_GPENCIL) { - BKE_gpencil_modifier_panel_expand( - reinterpret_cast<GpencilModifierData *>(te->directdata)); + BKE_gpencil_modifier_panel_expand(static_cast<GpencilModifierData *>(te->directdata)); } else { ModifierData *md = (ModifierData *)te->directdata; @@ -1276,12 +1275,12 @@ static void outliner_set_properties_tab(bContext *C, TreeElement *te, TreeStoreE context = BCONTEXT_SHADERFX; if (tselem->type != TSE_GPENCIL_EFFECT_BASE) { - BKE_shaderfx_panel_expand(reinterpret_cast<ShaderFxData *>(te->directdata)); + BKE_shaderfx_panel_expand(static_cast<ShaderFxData *>(te->directdata)); } break; case TSE_BONE: { bArmature *arm = (bArmature *)tselem->id; - Bone *bone = reinterpret_cast<Bone *>(te->directdata); + Bone *bone = static_cast<Bone *>(te->directdata); RNA_pointer_create(&arm->id, &RNA_Bone, bone, &ptr); context = BCONTEXT_BONE; @@ -1289,7 +1288,7 @@ static void outliner_set_properties_tab(bContext *C, TreeElement *te, TreeStoreE } case TSE_EBONE: { bArmature *arm = (bArmature *)tselem->id; - EditBone *ebone = reinterpret_cast<EditBone *>(te->directdata); + EditBone *ebone = static_cast<EditBone *>(te->directdata); RNA_pointer_create(&arm->id, &RNA_EditBone, ebone, &ptr); context = BCONTEXT_BONE; @@ -1297,8 +1296,8 @@ static void outliner_set_properties_tab(bContext *C, TreeElement *te, TreeStoreE } case TSE_POSE_CHANNEL: { Object *ob = (Object *)tselem->id; - bArmature *arm = reinterpret_cast<bArmature *>(ob->data); - bPoseChannel *pchan = reinterpret_cast<bPoseChannel *>(te->directdata); + bArmature *arm = static_cast<bArmature *>(ob->data); + bPoseChannel *pchan = static_cast<bPoseChannel *>(te->directdata); RNA_pointer_create(&arm->id, &RNA_PoseBone, pchan, &ptr); context = BCONTEXT_BONE; @@ -1306,7 +1305,7 @@ static void outliner_set_properties_tab(bContext *C, TreeElement *te, TreeStoreE } case TSE_POSE_BASE: { Object *ob = (Object *)tselem->id; - bArmature *arm = reinterpret_cast<bArmature *>(ob->data); + bArmature *arm = static_cast<bArmature *>(ob->data); RNA_pointer_create(&arm->id, &RNA_Armature, arm, &ptr); context = BCONTEXT_DATA; @@ -1314,7 +1313,7 @@ static void outliner_set_properties_tab(bContext *C, TreeElement *te, TreeStoreE } case TSE_R_LAYER_BASE: case TSE_R_LAYER: { - ViewLayer *view_layer = reinterpret_cast<ViewLayer *>(te->directdata); + ViewLayer *view_layer = static_cast<ViewLayer *>(te->directdata); RNA_pointer_create(tselem->id, &RNA_ViewLayer, view_layer, &ptr); context = BCONTEXT_VIEW_LAYER; @@ -1323,7 +1322,7 @@ static void outliner_set_properties_tab(bContext *C, TreeElement *te, TreeStoreE case TSE_POSEGRP_BASE: case TSE_POSEGRP: { Object *ob = (Object *)tselem->id; - bArmature *arm = reinterpret_cast<bArmature *>(ob->data); + bArmature *arm = static_cast<bArmature *>(ob->data); RNA_pointer_create(&arm->id, &RNA_Armature, arm, &ptr); context = BCONTEXT_DATA; @@ -1571,7 +1570,7 @@ static bool outliner_is_co_within_active_mode_column(bContext *C, const float view_mval[2]) { ViewLayer *view_layer = CTX_data_view_layer(C); - Object *obact = OBACT(view_layer); + Object *obact = BKE_view_layer_active_object_get(view_layer); return outliner_is_co_within_mode_column(space_outliner, view_mval) && obact && obact->mode != OB_MODE_OBJECT; @@ -1823,7 +1822,7 @@ static TreeElement *outliner_find_rightmost_visible_child(SpaceOutliner *space_o { while (te->subtree.last) { if (TSELEM_OPEN(TREESTORE(te), space_outliner)) { - te = reinterpret_cast<TreeElement *>(te->subtree.last); + te = static_cast<TreeElement *>(te->subtree.last); } else { break; @@ -1867,7 +1866,7 @@ static TreeElement *outliner_find_next_element(SpaceOutliner *space_outliner, Tr TreeStoreElem *tselem = TREESTORE(te); if (TSELEM_OPEN(tselem, space_outliner) && te->subtree.first) { - te = reinterpret_cast<TreeElement *>(te->subtree.first); + te = static_cast<TreeElement *>(te->subtree.first); } else if (te->next) { te = te->next; @@ -1886,7 +1885,7 @@ static TreeElement *outliner_walk_left(SpaceOutliner *space_outliner, TreeStoreElem *tselem = TREESTORE(te); if (TSELEM_OPEN(tselem, space_outliner)) { - outliner_item_openclose(space_outliner, te, false, toggle_all); + outliner_item_openclose(te, false, toggle_all); } /* Only walk up a level if the element is closed and not toggling expand */ else if (!toggle_all && te->parent) { @@ -1904,10 +1903,10 @@ static TreeElement *outliner_walk_right(SpaceOutliner *space_outliner, /* Only walk down a level if the element is open and not toggling expand */ if (!toggle_all && TSELEM_OPEN(tselem, space_outliner) && !BLI_listbase_is_empty(&te->subtree)) { - te = reinterpret_cast<TreeElement *>(te->subtree.first); + te = static_cast<TreeElement *>(te->subtree.first); } else { - outliner_item_openclose(space_outliner, te, true, toggle_all); + outliner_item_openclose(te, true, toggle_all); } return te; @@ -1955,7 +1954,7 @@ static TreeElement *find_walk_select_start_element(SpaceOutliner *space_outliner /* If no active element exists, use the first element in the tree */ if (!active_te) { - active_te = reinterpret_cast<TreeElement *>(space_outliner->tree.first); + active_te = static_cast<TreeElement *>(space_outliner->tree.first); *changed = true; } @@ -2041,3 +2040,5 @@ void OUTLINER_OT_select_walk(wmOperatorType *ot) } /** \} */ + +} // namespace blender::ed::outliner diff --git a/source/blender/editors/space_outliner/outliner_sync.cc b/source/blender/editors/space_outliner/outliner_sync.cc index 36bc7c31b91..8f1c15873b4 100644 --- a/source/blender/editors/space_outliner/outliner_sync.cc +++ b/source/blender/editors/space_outliner/outliner_sync.cc @@ -77,8 +77,8 @@ void ED_outliner_select_sync_flag_outliners(const bContext *C) Main *bmain = CTX_data_main(C); wmWindowManager *wm = CTX_wm_manager(C); - for (bScreen *screen = reinterpret_cast<bScreen *>(bmain->screens.first); screen; - screen = reinterpret_cast<bScreen *>(screen->id.next)) { + for (bScreen *screen = static_cast<bScreen *>(bmain->screens.first); screen; + screen = static_cast<bScreen *>(screen->id.next)) { LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) { LISTBASE_FOREACH (SpaceLink *, sl, &area->spacedata) { if (sl->spacetype == SPACE_OUTLINER) { @@ -94,6 +94,8 @@ void ED_outliner_select_sync_flag_outliners(const bContext *C) wm->outliner_sync_select_dirty = 0; } +namespace blender::ed::outliner { + /** * Outliner sync select dirty flags are not enough to determine which types to sync, * outliner display mode also needs to be considered. This stores the types of data @@ -248,7 +250,7 @@ static void outliner_select_sync_to_edit_bone(ViewLayer *view_layer, /* Tag if selection changed */ if (bone_flag != ebone->flag) { - Object *obedit = OBEDIT_FROM_VIEW_LAYER(view_layer); + Object *obedit = BKE_view_layer_edit_object_get(view_layer); DEG_id_tag_update(&arm->id, ID_RECALC_SELECT); WM_main_add_notifier(NC_OBJECT | ND_BONE_SELECT, obedit); } @@ -259,7 +261,7 @@ static void outliner_select_sync_to_pose_bone(TreeElement *te, GSet *selected_pbones) { Object *ob = (Object *)tselem->id; - bArmature *arm = reinterpret_cast<bArmature *>(ob->data); + bArmature *arm = static_cast<bArmature *>(ob->data); bPoseChannel *pchan = (bPoseChannel *)te->directdata; short bone_flag = pchan->bone->flag; @@ -335,8 +337,12 @@ static void outliner_sync_selection_from_outliner(Scene *scene, } } +} // namespace blender::ed::outliner + void ED_outliner_select_sync_from_outliner(bContext *C, SpaceOutliner *space_outliner) { + using namespace blender::ed::outliner; + /* Don't sync if not checked or in certain outliner display modes */ if (!(space_outliner->flag & SO_SYNC_SELECT) || ELEM(space_outliner->outlinevis, SO_LIBRARIES, @@ -380,6 +386,8 @@ void ED_outliner_select_sync_from_outliner(bContext *C, SpaceOutliner *space_out } } +namespace blender::ed::outliner { + static void outliner_select_sync_from_object(ViewLayer *view_layer, Object *obact, TreeElement *te, @@ -523,7 +531,7 @@ static void get_sync_select_active_data(const bContext *C, SyncSelectActiveData { Scene *scene = CTX_data_scene(C); ViewLayer *view_layer = CTX_data_view_layer(C); - active_data->object = OBACT(view_layer); + active_data->object = BKE_view_layer_active_object_get(view_layer); active_data->edit_bone = CTX_data_active_bone(C); active_data->pose_channel = CTX_data_active_pose_bone(C); active_data->sequence = SEQ_select_active_get(scene); @@ -561,3 +569,5 @@ void outliner_sync_selection(const bContext *C, SpaceOutliner *space_outliner) } } } + +} // namespace blender::ed::outliner diff --git a/source/blender/editors/space_outliner/outliner_tools.cc b/source/blender/editors/space_outliner/outliner_tools.cc index ec19e8d5e5b..b0d24c88eea 100644 --- a/source/blender/editors/space_outliner/outliner_tools.cc +++ b/source/blender/editors/space_outliner/outliner_tools.cc @@ -31,8 +31,11 @@ #include "BLI_blenlib.h" #include "BLI_ghash.h" +#include "BLI_linklist.h" +#include "BLI_map.hh" #include "BLI_set.hh" #include "BLI_utildefines.h" +#include "BLI_vector.hh" #include "BKE_anim_data.h" #include "BKE_animsys.h" @@ -86,11 +89,15 @@ #include "tree/tree_element_seq.hh" #include "tree/tree_iterator.hh" +namespace blender::ed::outliner { + static CLG_LogRef LOG = {"ed.outliner.tools"}; using namespace blender::ed::outliner; +using blender::Map; using blender::Set; +using blender::Vector; /* -------------------------------------------------------------------- */ /** \name ID/Library/Data Set/Un-link Utilities @@ -448,6 +455,99 @@ static void outliner_do_libdata_operation(bContext *C, }); } +enum eOutlinerLibOpSelectionSet { + /* Only selected items. */ + OUTLINER_LIB_SELECTIONSET_SELECTED, + /* Only content 'inside' selected items (their sub-tree). */ + OUTLINER_LIB_LIB_SELECTIONSET_CONTENT, + /* Combining both options above. */ + OUTLINER_LIB_LIB_SELECTIONSET_SELECTED_AND_CONTENT, +}; + +static const EnumPropertyItem prop_lib_op_selection_set[] = { + {OUTLINER_LIB_SELECTIONSET_SELECTED, + "SELECTED", + 0, + "Selected", + "Apply the operation over selected data-blocks only"}, + {OUTLINER_LIB_LIB_SELECTIONSET_CONTENT, + "CONTENT", + 0, + "Content", + "Apply the operation over content of the selected items only (the data-blocks in their " + "sub-tree)"}, + {OUTLINER_LIB_LIB_SELECTIONSET_SELECTED_AND_CONTENT, + "SELECTED_AND_CONTENT", + 0, + "Selected & Content", + "Apply the operation over selected data-blocks and all their dependencies"}, + {0, nullptr, 0, nullptr, nullptr}, +}; + +static void outliner_do_libdata_operation_selection_set(bContext *C, + ReportList *reports, + Scene *scene, + SpaceOutliner *space_outliner, + const ListBase &subtree, + const bool has_parent_selected, + outliner_operation_fn operation_fn, + eOutlinerLibOpSelectionSet selection_set, + void *user_data) +{ + const bool do_selected = ELEM(selection_set, + OUTLINER_LIB_SELECTIONSET_SELECTED, + OUTLINER_LIB_LIB_SELECTIONSET_SELECTED_AND_CONTENT); + const bool do_content = ELEM(selection_set, + OUTLINER_LIB_LIB_SELECTIONSET_CONTENT, + OUTLINER_LIB_LIB_SELECTIONSET_SELECTED_AND_CONTENT); + + LISTBASE_FOREACH_MUTABLE (TreeElement *, element, &subtree) { + /* Get needed data out in case element gets freed. */ + TreeStoreElem *tselem = TREESTORE(element); + const ListBase subtree = element->subtree; + + bool is_selected = tselem->flag & TSE_SELECTED; + if ((is_selected && do_selected) || (has_parent_selected && do_content)) { + if (((tselem->type == TSE_SOME_ID) && (element->idcode != 0)) || + tselem->type == TSE_LAYER_COLLECTION) { + TreeStoreElem *tsep = element->parent ? TREESTORE(element->parent) : nullptr; + operation_fn(C, reports, scene, element, tsep, tselem, user_data); + } + } + + /* Don't access element from now on, it may be freed. Note that the open/collapsed state may + * also have been changed in the visitor callback. */ + outliner_do_libdata_operation_selection_set(C, + reports, + scene, + space_outliner, + subtree, + is_selected || has_parent_selected, + operation_fn, + selection_set, + user_data); + } +} + +static void outliner_do_libdata_operation_selection_set(bContext *C, + ReportList *reports, + Scene *scene, + SpaceOutliner *space_outliner, + outliner_operation_fn operation_fn, + eOutlinerLibOpSelectionSet selection_set, + void *user_data) +{ + outliner_do_libdata_operation_selection_set(C, + reports, + scene, + space_outliner, + space_outliner->tree, + false, + operation_fn, + selection_set, + user_data); +} + /** \} */ /* -------------------------------------------------------------------- */ @@ -777,6 +877,25 @@ static void id_local_fn(bContext *C, } } +struct OutlinerLiboverrideDataIDRoot { + /** The linked ID that was selected for override. */ + ID *id_root_reference; + + /** The root of the override hierarchy to which the override of `id_root` belongs, once + * known/created. */ + ID *id_hierarchy_root_override; + + /** The ID that was detected as being a good candidate as instantiation hint for newly overridden + * objects, may be null. + * + * \note Typically currently only used when the root ID to override is a collection instanced by + * an empty object. */ + ID *id_instance_hint; + + /** If this override comes from an instancing object (which would be `id_instance_hint` then). */ + bool is_override_instancing_object; +}; + struct OutlinerLibOverrideData { bool do_hierarchy; @@ -789,35 +908,82 @@ struct OutlinerLibOverrideData { * solving broken overrides while not losing *all* of your overrides. */ bool do_resync_hierarchy_enforce; - /** The override hierarchy root, when known/created. */ - ID *id_hierarchy_root_override; - - /** A hash of the selected tree elements' ID 'uuid'. Used to clear 'system override' flags on + /** A set of the selected tree elements' ID 'uuid'. Used to clear 'system override' flags on * their newly-created liboverrides in post-process step of override hierarchy creation. */ Set<uint> selected_id_uid; + + /** A mapping from the found hierarchy roots to a linked list of IDs to override for each of + * these roots. + * + * \note the key may be either linked (in which case it will be replaced by the newly created + * override), or an actual already existing override. */ + Map<ID *, Vector<OutlinerLiboverrideDataIDRoot>> id_hierarchy_roots; + + /** All 'session_uuid' of all hierarchy root IDs used or created by the operation. */ + Set<uint> id_hierarchy_roots_uid; + + void id_root_add(ID *id_hierarchy_root_reference, + ID *id_root_reference, + ID *id_instance_hint, + const bool is_override_instancing_object) + { + OutlinerLiboverrideDataIDRoot id_root_data; + id_root_data.id_root_reference = id_root_reference; + id_root_data.id_hierarchy_root_override = nullptr; + id_root_data.id_instance_hint = id_instance_hint; + id_root_data.is_override_instancing_object = is_override_instancing_object; + + Vector<OutlinerLiboverrideDataIDRoot> &value = id_hierarchy_roots.lookup_or_add_default( + id_hierarchy_root_reference); + value.append(id_root_data); + } + void id_root_set(ID *id_hierarchy_root_reference) + { + OutlinerLiboverrideDataIDRoot id_root_data; + id_root_data.id_root_reference = nullptr; + id_root_data.id_hierarchy_root_override = nullptr; + id_root_data.id_instance_hint = nullptr; + id_root_data.is_override_instancing_object = false; + + Vector<OutlinerLiboverrideDataIDRoot> &value = id_hierarchy_roots.lookup_or_add_default( + id_hierarchy_root_reference); + if (value.is_empty()) { + value.append(id_root_data); + } + } }; /* Store 'UUID' of IDs of selected elements in the Outliner tree, before generating the override * hierarchy. */ -static void id_override_library_create_hierarchy_pre_process_fn(bContext *UNUSED(C), - ReportList *UNUSED(reports), +static void id_override_library_create_hierarchy_pre_process_fn(bContext *C, + ReportList *reports, Scene *UNUSED(scene), - TreeElement *UNUSED(te), - TreeStoreElem *UNUSED(tsep), + TreeElement *te, + TreeStoreElem *tsep, TreeStoreElem *tselem, void *user_data) { BLI_assert(TSE_IS_REAL_ID(tselem)); - OutlinerLibOverrideData *data = reinterpret_cast<OutlinerLibOverrideData *>(user_data); + OutlinerLibOverrideData *data = static_cast<OutlinerLibOverrideData *>(user_data); const bool do_hierarchy = data->do_hierarchy; ID *id_root_reference = tselem->id; + if (!BKE_idtype_idcode_is_linkable(GS(id_root_reference->name)) || + (id_root_reference->flag & (LIB_EMBEDDED_DATA | LIB_EMBEDDED_DATA_LIB_OVERRIDE)) != 0) { + return; + } + BLI_assert(do_hierarchy); UNUSED_VARS_NDEBUG(do_hierarchy); data->selected_id_uid.add(id_root_reference->session_uuid); + if (ID_IS_OVERRIDE_LIBRARY_REAL(id_root_reference) && !ID_IS_LINKED(id_root_reference)) { + id_root_reference->override_library->flag &= ~IDOVERRIDE_LIBRARY_FLAG_SYSTEM_DEFINED; + return; + } + if (GS(id_root_reference->name) == ID_GR && (tselem->flag & TSE_CLOSED) != 0) { /* If selected element is a (closed) collection, check all of its objects recursively, and also * consider the armature ones as 'selected' (i.e. to not become system overrides). */ @@ -829,28 +995,6 @@ static void id_override_library_create_hierarchy_pre_process_fn(bContext *UNUSED } FOREACH_COLLECTION_OBJECT_RECURSIVE_END; } -} - -static void id_override_library_create_fn(bContext *C, - ReportList *reports, - Scene *scene, - TreeElement *te, - TreeStoreElem *tsep, - TreeStoreElem *tselem, - void *user_data) -{ - BLI_assert(TSE_IS_REAL_ID(tselem)); - - /* We can only safely apply this operation on one item at a time, so only do it on the active - * one. */ - if ((tselem->flag & TSE_ACTIVE) == 0) { - return; - } - - ID *id_root_reference = tselem->id; - OutlinerLibOverrideData *data = reinterpret_cast<OutlinerLibOverrideData *>(user_data); - const bool do_hierarchy = data->do_hierarchy; - bool success = false; ID *id_instance_hint = nullptr; bool is_override_instancing_object = false; @@ -866,195 +1010,259 @@ static void id_override_library_create_fn(bContext *C, } } - if (ID_IS_OVERRIDABLE_LIBRARY(id_root_reference) || - (ID_IS_LINKED(id_root_reference) && do_hierarchy)) { - Main *bmain = CTX_data_main(C); + if (!ID_IS_OVERRIDABLE_LIBRARY(id_root_reference) && + !(ID_IS_LINKED(id_root_reference) && do_hierarchy)) { + return; + } - id_root_reference->tag |= LIB_TAG_DOIT; + Main *bmain = CTX_data_main(C); - /* For now, remap all local usages of linked ID to local override one here. */ - ID *id_iter; - FOREACH_MAIN_ID_BEGIN (bmain, id_iter) { - if (ID_IS_LINKED(id_iter)) { - id_iter->tag &= ~LIB_TAG_DOIT; - } - else { - id_iter->tag |= LIB_TAG_DOIT; + if (do_hierarchy) { + /* Tag all linked parents in tree hierarchy to be also overridden. */ + ID *id_hierarchy_root_reference = id_root_reference; + while ((te = te->parent) != nullptr) { + if (!TSE_IS_REAL_ID(te->store_elem)) { + continue; } - } - FOREACH_MAIN_ID_END; - if (do_hierarchy) { - /* Tag all linked parents in tree hierarchy to be also overridden. */ - ID *id_hierarchy_root_reference = id_root_reference; - while ((te = te->parent) != nullptr) { - if (!TSE_IS_REAL_ID(te->store_elem)) { + /* Tentative hierarchy root. */ + ID *id_current_hierarchy_root = te->store_elem->id; + + /* If the parent ID is from a different library than the reference root one, we are done + * with upwards tree processing in any case. */ + if (id_current_hierarchy_root->lib != id_root_reference->lib) { + if (ID_IS_OVERRIDE_LIBRARY_VIRTUAL(id_current_hierarchy_root)) { + /* Virtual overrides (i.e. embedded IDs), we can simply keep processing their parent to + * get an actual real override. */ continue; } - /* Tentative hierarchy root. */ - ID *id_current_hierarchy_root = te->store_elem->id; - - /* If the parent ID is from a different library than the reference root one, we are done - * with upwards tree processing in any case. */ - if (id_current_hierarchy_root->lib != id_root_reference->lib) { - if (ID_IS_OVERRIDE_LIBRARY_VIRTUAL(id_current_hierarchy_root)) { - /* Virtual overrides (i.e. embedded IDs), we can simply keep processing their parent to - * get an actual real override. */ - continue; - } - - /* If the parent ID is already an override, and is valid (i.e. local override), we can - * access its hierarchy root directly. */ - if (!ID_IS_LINKED(id_current_hierarchy_root) && - ID_IS_OVERRIDE_LIBRARY_REAL(id_current_hierarchy_root) && - id_current_hierarchy_root->override_library->reference->lib == - id_root_reference->lib) { - id_hierarchy_root_reference = - id_current_hierarchy_root->override_library->hierarchy_root; - BLI_assert(ID_IS_OVERRIDE_LIBRARY_REAL(id_hierarchy_root_reference)); - break; - } - - if (ID_IS_LINKED(id_current_hierarchy_root)) { - /* No local 'anchor' was found for the hierarchy to override, do not proceed, as this - * would most likely generate invisible/confusing/hard to use and manage overrides. */ - BKE_main_id_tag_all(bmain, LIB_TAG_DOIT, false); - BKE_reportf(reports, - RPT_WARNING, - "Invalid anchor ('%s') found, needed to create library override from " - "data-block '%s'", - id_current_hierarchy_root->name, - id_root_reference->name); - return; - } - - /* In all other cases, `id_current_hierarchy_root` cannot be a valid hierarchy root, so - * current `id_hierarchy_root_reference` is our best candidate. */ - + /* If the parent ID is already an override, and is valid (i.e. local override), we can + * access its hierarchy root directly. */ + if (!ID_IS_LINKED(id_current_hierarchy_root) && + ID_IS_OVERRIDE_LIBRARY_REAL(id_current_hierarchy_root) && + id_current_hierarchy_root->override_library->reference->lib == + id_root_reference->lib) { + id_hierarchy_root_reference = + id_current_hierarchy_root->override_library->hierarchy_root; + BLI_assert(ID_IS_OVERRIDE_LIBRARY_REAL(id_hierarchy_root_reference)); break; } - /* If some element in the tree needs to be overridden, but its ID is not overridable, - * abort. */ - if (!ID_IS_OVERRIDABLE_LIBRARY_HIERARCHY(id_current_hierarchy_root)) { + if (ID_IS_LINKED(id_current_hierarchy_root)) { + /* No local 'anchor' was found for the hierarchy to override, do not proceed, as this + * would most likely generate invisible/confusing/hard to use and manage overrides. */ BKE_main_id_tag_all(bmain, LIB_TAG_DOIT, false); BKE_reportf(reports, RPT_WARNING, - "Could not create library override from data-block '%s', one of its parents " - "is not overridable ('%s')", - id_root_reference->name, - id_current_hierarchy_root->name); + "Invalid anchor ('%s') found, needed to create library override from " + "data-block '%s'", + id_current_hierarchy_root->name, + id_root_reference->name); return; } - id_current_hierarchy_root->tag |= LIB_TAG_DOIT; - id_hierarchy_root_reference = id_current_hierarchy_root; + + /* In all other cases, `id_current_hierarchy_root` cannot be a valid hierarchy root, so + * current `id_hierarchy_root_reference` is our best candidate. */ + + break; } - /* That case can happen when linked data is a complex mix involving several libraries and/or - * linked overrides. E.g. a mix of overrides from one library, and indirectly linked data - * from another library. Do not try to support such cases for now. */ - if (!((id_hierarchy_root_reference->lib == id_root_reference->lib) || - (!ID_IS_LINKED(id_hierarchy_root_reference) && - ID_IS_OVERRIDE_LIBRARY_REAL(id_hierarchy_root_reference) && - id_hierarchy_root_reference->override_library->reference->lib == - id_root_reference->lib))) { + /* If some element in the tree needs to be overridden, but its ID is not overridable, + * abort. */ + if (!ID_IS_OVERRIDABLE_LIBRARY_HIERARCHY(id_current_hierarchy_root)) { BKE_main_id_tag_all(bmain, LIB_TAG_DOIT, false); BKE_reportf(reports, RPT_WARNING, - "Invalid hierarchy root ('%s') found, needed to create library override from " - "data-block '%s'", - id_hierarchy_root_reference->name, - id_root_reference->name); + "Could not create library override from data-block '%s', one of its parents " + "is not overridable ('%s')", + id_root_reference->name, + id_current_hierarchy_root->name); return; } + id_current_hierarchy_root->tag |= LIB_TAG_DOIT; + id_hierarchy_root_reference = id_current_hierarchy_root; + } + + /* That case can happen when linked data is a complex mix involving several libraries and/or + * linked overrides. E.g. a mix of overrides from one library, and indirectly linked data + * from another library. Do not try to support such cases for now. */ + if (!((id_hierarchy_root_reference->lib == id_root_reference->lib) || + (!ID_IS_LINKED(id_hierarchy_root_reference) && + ID_IS_OVERRIDE_LIBRARY_REAL(id_hierarchy_root_reference) && + id_hierarchy_root_reference->override_library->reference->lib == + id_root_reference->lib))) { + BKE_main_id_tag_all(bmain, LIB_TAG_DOIT, false); + BKE_reportf(reports, + RPT_WARNING, + "Invalid hierarchy root ('%s') found, needed to create library override from " + "data-block '%s'", + id_hierarchy_root_reference->name, + id_root_reference->name); + return; + } + + /* While ideally this should not be needed, in practice user almost _never_ wants to actually + * create liboverrides for all data under a selected hierarchy node, and this has currently a + * dreadful consequences over performances (since it would call + * #BKE_lib_override_library_create over _all_ items in the hierarchy). So only the clearing of + * the system override flag is supported for non-selected items for now. + */ + const bool is_selected = tselem->flag & TSE_SELECTED; + if (!is_selected && data->id_hierarchy_roots.contains(id_hierarchy_root_reference)) { + return; + } + + data->id_root_add(id_hierarchy_root_reference, + id_root_reference, + id_instance_hint, + is_override_instancing_object); + } + else if (ID_IS_OVERRIDABLE_LIBRARY(id_root_reference)) { + data->id_root_add( + id_root_reference, id_root_reference, id_instance_hint, is_override_instancing_object); + } +} + +static void id_override_library_create_hierarchy( + Main &bmain, + Scene *scene, + ViewLayer *view_layer, + OutlinerLibOverrideData &data, + ID *id_hierarchy_root_reference, + Vector<OutlinerLiboverrideDataIDRoot> &data_idroots, + bool &r_aggregated_success) +{ + BLI_assert(ID_IS_LINKED(id_hierarchy_root_reference) || + ID_IS_OVERRIDE_LIBRARY_REAL(id_hierarchy_root_reference)); + + const bool do_hierarchy = data.do_hierarchy; + + /* NOTE: This process is not the most efficient, but allows to re-use existing code. + * If this becomes a bottle-neck at some point, we need to implement a new + * `BKE_lib_override_library_hierarchy_create()` function able to process several roots inside of + * a same hierarchy in a single call. */ + for (OutlinerLiboverrideDataIDRoot &data_idroot : data_idroots) { + /* For now, remap all local usages of linked ID to local override one here. */ + ID *id_iter; + FOREACH_MAIN_ID_BEGIN (&bmain, id_iter) { + if (ID_IS_LINKED(id_iter) || ID_IS_OVERRIDE_LIBRARY(id_iter)) { + id_iter->tag &= ~LIB_TAG_DOIT; + } + else { + id_iter->tag |= LIB_TAG_DOIT; + } + } + FOREACH_MAIN_ID_END; + bool success = false; + if (do_hierarchy) { ID *id_root_override = nullptr; - success = BKE_lib_override_library_create(bmain, - CTX_data_scene(C), - CTX_data_view_layer(C), + success = BKE_lib_override_library_create(&bmain, + scene, + view_layer, nullptr, - id_root_reference, + data_idroot.id_root_reference, id_hierarchy_root_reference, - id_instance_hint, + data_idroot.id_instance_hint, &id_root_override, - data->do_fully_editable); - - BLI_assert(id_root_override != nullptr); - BLI_assert(!ID_IS_LINKED(id_root_override)); - BLI_assert(ID_IS_OVERRIDE_LIBRARY_REAL(id_root_override)); - if (ID_IS_LINKED(id_hierarchy_root_reference)) { - BLI_assert( - id_root_override->override_library->hierarchy_root->override_library->reference == - id_hierarchy_root_reference); - data->id_hierarchy_root_override = id_root_override->override_library->hierarchy_root; - } - else { - BLI_assert(id_root_override->override_library->hierarchy_root == - id_hierarchy_root_reference); - data->id_hierarchy_root_override = id_root_override->override_library->hierarchy_root; + data.do_fully_editable); + + if (success) { + BLI_assert(id_root_override != nullptr); + BLI_assert(!ID_IS_LINKED(id_root_override)); + BLI_assert(ID_IS_OVERRIDE_LIBRARY_REAL(id_root_override)); + + ID *id_hierarchy_root_override = id_root_override->override_library->hierarchy_root; + BLI_assert(ID_IS_OVERRIDE_LIBRARY_REAL(id_hierarchy_root_override)); + if (ID_IS_LINKED(id_hierarchy_root_reference)) { + BLI_assert(id_hierarchy_root_override->override_library->reference == + id_hierarchy_root_reference); + /* If the hierarchy root reference was a linked data, after the first iteration there is + * now a matching override, which shall be used for all further partial overrides with + * this same hierarchy. */ + id_hierarchy_root_reference = id_hierarchy_root_override; + } + else { + BLI_assert(id_hierarchy_root_override == id_hierarchy_root_reference); + } + data_idroot.id_hierarchy_root_override = id_hierarchy_root_override; + data.id_hierarchy_roots_uid.add(id_hierarchy_root_override->session_uuid); } } - else if (ID_IS_OVERRIDABLE_LIBRARY(id_root_reference)) { - success = BKE_lib_override_library_create_from_id(bmain, id_root_reference, true) != nullptr; + else if (ID_IS_OVERRIDABLE_LIBRARY(data_idroot.id_root_reference)) { + ID *id_root_override = BKE_lib_override_library_create_from_id( + &bmain, data_idroot.id_root_reference, true); + success = id_root_override != nullptr; + if (success) { + BLI_assert(ID_IS_OVERRIDE_LIBRARY_REAL(id_root_override)); + id_root_override->override_library->flag &= ~IDOVERRIDE_LIBRARY_FLAG_SYSTEM_DEFINED; + } /* Cleanup. */ - BKE_main_id_newptr_and_tag_clear(bmain); - BKE_main_id_tag_all(bmain, LIB_TAG_DOIT, false); + BKE_main_id_newptr_and_tag_clear(&bmain); + BKE_main_id_tag_all(&bmain, LIB_TAG_DOIT, false); + } + else { + BLI_assert_unreachable(); } /* Remove the instance empty from this scene, the items now have an overridden collection * instead. */ - if (success && is_override_instancing_object) { - ED_object_base_free_and_unlink(bmain, scene, (Object *)id_instance_hint); + if (success && data_idroot.is_override_instancing_object) { + BLI_assert(GS(data_idroot.id_instance_hint) == ID_OB); + ED_object_base_free_and_unlink( + &bmain, scene, reinterpret_cast<Object *>(data_idroot.id_instance_hint)); } - } - if (!success) { - BKE_reportf(reports, - RPT_WARNING, - "Could not create library override from data-block '%s'", - id_root_reference->name); + + r_aggregated_success = r_aggregated_success && success; } } /* Clear system override flag from newly created overrides which linked reference were previously * selected in the Outliner tree. */ -static void id_override_library_create_hierarchy_post_process(bContext *C, - OutlinerLibOverrideData *data) +static void id_override_library_create_hierarchy_process(bContext *C, + ReportList *reports, + OutlinerLibOverrideData &data) { Main *bmain = CTX_data_main(C); - ID *id_hierarchy_root_override = data->id_hierarchy_root_override; + Scene *scene = CTX_data_scene(C); + ViewLayer *view_layer = CTX_data_view_layer(C); + const bool do_hierarchy = data.do_hierarchy; + + bool success = true; + for (auto &&[id_hierarchy_root_reference, data_idroots] : data.id_hierarchy_roots.items()) { + id_override_library_create_hierarchy( + *bmain, scene, view_layer, data, id_hierarchy_root_reference, data_idroots, success); + } + + if (!success) { + BKE_reportf(reports, + RPT_WARNING, + "Could not create library override from one or more of the selected data-blocks"); + } + + if (!do_hierarchy) { + return; + } ID *id_iter; FOREACH_MAIN_ID_BEGIN (bmain, id_iter) { - if (ID_IS_LINKED(id_iter) || !ID_IS_OVERRIDE_LIBRARY_REAL(id_iter) || - id_iter->override_library->hierarchy_root != id_hierarchy_root_override) { + if (ID_IS_LINKED(id_iter) || !ID_IS_OVERRIDE_LIBRARY_REAL(id_iter)) { + continue; + } + if (!data.id_hierarchy_roots_uid.contains( + id_iter->override_library->hierarchy_root->session_uuid)) { continue; } - if (data->selected_id_uid.contains(id_iter->override_library->reference->session_uuid)) { + if (data.selected_id_uid.contains(id_iter->override_library->reference->session_uuid) || + data.selected_id_uid.contains(id_iter->session_uuid)) { id_iter->override_library->flag &= ~IDOVERRIDE_LIBRARY_FLAG_SYSTEM_DEFINED; } } FOREACH_MAIN_ID_END; } -static void id_override_library_toggle_flag_fn(bContext *UNUSED(C), - ReportList *UNUSED(reports), - Scene *UNUSED(scene), - TreeElement *UNUSED(te), - TreeStoreElem *UNUSED(tsep), - TreeStoreElem *tselem, - void *user_data) -{ - BLI_assert(TSE_IS_REAL_ID(tselem)); - ID *id = tselem->id; - - if (ID_IS_OVERRIDE_LIBRARY_REAL(id)) { - const uint flag = POINTER_AS_UINT(user_data); - id->override_library->flag ^= flag; - } -} - static void id_override_library_reset_fn(bContext *C, ReportList *UNUSED(reports), Scene *UNUSED(scene), @@ -1065,137 +1273,157 @@ static void id_override_library_reset_fn(bContext *C, { BLI_assert(TSE_IS_REAL_ID(tselem)); ID *id_root = tselem->id; - OutlinerLibOverrideData *data = reinterpret_cast<OutlinerLibOverrideData *>(user_data); + OutlinerLibOverrideData *data = static_cast<OutlinerLibOverrideData *>(user_data); const bool do_hierarchy = data->do_hierarchy; - if (ID_IS_OVERRIDE_LIBRARY_REAL(id_root)) { - Main *bmain = CTX_data_main(C); + if (!ID_IS_OVERRIDE_LIBRARY_REAL(id_root) || ID_IS_LINKED(id_root)) { + CLOG_WARN(&LOG, "Could not reset library override of data block '%s'", id_root->name); + return; + } - if (do_hierarchy) { - BKE_lib_override_library_id_hierarchy_reset(bmain, id_root, false); - } - else { - BKE_lib_override_library_id_reset(bmain, id_root, false); - } + Main *bmain = CTX_data_main(C); - WM_event_add_notifier(C, NC_WM | ND_DATACHANGED, nullptr); - WM_event_add_notifier(C, NC_SPACE | ND_SPACE_VIEW3D, nullptr); + if (do_hierarchy) { + BKE_lib_override_library_id_hierarchy_reset(bmain, id_root, false); } else { - CLOG_WARN(&LOG, "Could not reset library override of data block '%s'", id_root->name); + BKE_lib_override_library_id_reset(bmain, id_root, false); } } -static void id_override_library_resync_fn(bContext *C, - ReportList *reports, - Scene *scene, - TreeElement *te, - TreeStoreElem *UNUSED(tsep), - TreeStoreElem *tselem, - void *user_data) +static void id_override_library_clear_single_fn(bContext *C, + ReportList *reports, + Scene *scene, + TreeElement *UNUSED(te), + TreeStoreElem *UNUSED(tsep), + TreeStoreElem *tselem, + void *UNUSED(user_data)) { BLI_assert(TSE_IS_REAL_ID(tselem)); - ID *id_root = tselem->id; - OutlinerLibOverrideData *data = reinterpret_cast<OutlinerLibOverrideData *>(user_data); - const bool do_hierarchy_enforce = data->do_resync_hierarchy_enforce; - - if (ID_IS_OVERRIDE_LIBRARY_REAL(id_root)) { - Main *bmain = CTX_data_main(C); + Main *bmain = CTX_data_main(C); + ViewLayer *view_layer = CTX_data_view_layer(C); + ID *id = tselem->id; - id_root->tag |= LIB_TAG_DOIT; + if (!ID_IS_OVERRIDE_LIBRARY_REAL(id) || ID_IS_LINKED(id)) { + BKE_reportf(reports, + RPT_WARNING, + "Cannot clear embedded library override id '%s', only overrides of real " + "data-blocks can be directly deleted", + id->name); + return; + } - /* Tag all linked parents in tree hierarchy to be also overridden. */ - while ((te = te->parent) != nullptr) { - if (!TSE_IS_REAL_ID(te->store_elem)) { - continue; - } - if (!ID_IS_OVERRIDE_LIBRARY_REAL(te->store_elem->id)) { - break; + /* If given ID is not using any other override (it's a 'leaf' in the override hierarchy), + * delete it and remap its usages to its linked reference. Otherwise, keep it as a reset system + * override. */ + if (BKE_lib_override_library_is_hierarchy_leaf(bmain, id)) { + bool do_remap_active = false; + if (BKE_view_layer_active_object_get(view_layer) == reinterpret_cast<Object *>(id)) { + BLI_assert(GS(id->name) == ID_OB); + do_remap_active = true; + } + BKE_libblock_remap(bmain, id, id->override_library->reference, ID_REMAP_SKIP_INDIRECT_USAGE); + if (do_remap_active) { + Object *ref_object = reinterpret_cast<Object *>(id->override_library->reference); + Base *basact = BKE_view_layer_base_find(view_layer, ref_object); + if (basact != nullptr) { + view_layer->basact = basact; } - te->store_elem->id->tag |= LIB_TAG_DOIT; + DEG_id_tag_update(&scene->id, ID_RECALC_SELECT); } - - BlendFileReadReport report{}; - report.reports = reports; - BKE_lib_override_library_resync( - bmain, scene, CTX_data_view_layer(C), id_root, nullptr, do_hierarchy_enforce, &report); - - WM_event_add_notifier(C, NC_WINDOW, nullptr); + BKE_id_delete(bmain, id); } else { - CLOG_WARN(&LOG, "Could not resync library override of data block '%s'", id_root->name); + BKE_lib_override_library_id_reset(bmain, id, true); } + + DEG_id_tag_update(&scene->id, ID_RECALC_BASE_FLAGS | ID_RECALC_COPY_ON_WRITE); } -static void id_override_library_clear_hierarchy_fn(bContext *C, - ReportList *UNUSED(reports), - Scene *UNUSED(scene), - TreeElement *te, - TreeStoreElem *UNUSED(tsep), - TreeStoreElem *tselem, - void *UNUSED(user_data)) +static void id_override_library_resync_fn(bContext *UNUSED(C), + ReportList *UNUSED(reports), + Scene *UNUSED(scene), + TreeElement *UNUSED(te), + TreeStoreElem *UNUSED(tsep), + TreeStoreElem *tselem, + void *user_data) { BLI_assert(TSE_IS_REAL_ID(tselem)); ID *id_root = tselem->id; + OutlinerLibOverrideData *data = static_cast<OutlinerLibOverrideData *>(user_data); - if (!ID_IS_OVERRIDE_LIBRARY_REAL(id_root)) { - CLOG_WARN(&LOG, "Could not delete library override of data block '%s'", id_root->name); + if (!ID_IS_OVERRIDE_LIBRARY_REAL(id_root) || ID_IS_LINKED(id_root)) { + CLOG_WARN(&LOG, "Could not resync library override of data block '%s'", id_root->name); return; } + if (id_root->override_library->hierarchy_root != nullptr) { + id_root = id_root->override_library->hierarchy_root; + } + + data->id_root_set(id_root); +} + +/* Resync a hierarchy of library overrides. */ +static void id_override_library_resync_hierarchy_process(bContext *C, + ReportList *reports, + OutlinerLibOverrideData &data) +{ Main *bmain = CTX_data_main(C); + Scene *scene = CTX_data_scene(C); + const bool do_hierarchy_enforce = data.do_resync_hierarchy_enforce; - id_root->tag |= LIB_TAG_DOIT; + BlendFileReadReport report{}; + report.reports = reports; - /* Tag all override parents in tree hierarchy to be also processed. */ - while ((te = te->parent) != nullptr) { - if (!TSE_IS_REAL_ID(te->store_elem)) { - continue; - } - if (!ID_IS_OVERRIDE_LIBRARY_REAL(te->store_elem->id)) { - break; - } - te->store_elem->id->tag |= LIB_TAG_DOIT; + for (auto &&id_hierarchy_root : data.id_hierarchy_roots.keys()) { + BKE_lib_override_library_resync(bmain, + scene, + CTX_data_view_layer(C), + id_hierarchy_root, + nullptr, + do_hierarchy_enforce, + &report); } - BKE_lib_override_library_delete(bmain, id_root); - WM_event_add_notifier(C, NC_WINDOW, nullptr); } -static void id_override_library_clear_single_fn(bContext *C, - ReportList *reports, - Scene *UNUSED(scene), - TreeElement *UNUSED(te), - TreeStoreElem *UNUSED(tsep), - TreeStoreElem *tselem, - void *UNUSED(user_data)) +static void id_override_library_delete_hierarchy_fn(bContext *UNUSED(C), + ReportList *UNUSED(reports), + Scene *UNUSED(scene), + TreeElement *UNUSED(te), + TreeStoreElem *UNUSED(tsep), + TreeStoreElem *tselem, + void *user_data) { + OutlinerLibOverrideData *data = reinterpret_cast<OutlinerLibOverrideData *>(user_data); + BLI_assert(TSE_IS_REAL_ID(tselem)); - Main *bmain = CTX_data_main(C); - ID *id = tselem->id; + ID *id_root = tselem->id; - if (!ID_IS_OVERRIDE_LIBRARY_REAL(id)) { - BKE_reportf(reports, - RPT_WARNING, - "Cannot clear embedded library override id '%s', only overrides of real " - "data-blocks can be directly deleted", - id->name); + if (!ID_IS_OVERRIDE_LIBRARY_REAL(id_root) || ID_IS_LINKED(id_root)) { + CLOG_WARN(&LOG, "Could not delete library override of data block '%s'", id_root->name); return; } - /* If given ID is not using any other override (it's a 'leaf' in the override hierarchy), - * delete it and remap its usages to its linked reference. Otherwise, keep it as a reset system - * override. */ - if (BKE_lib_override_library_is_hierarchy_leaf(bmain, id)) { - BKE_libblock_remap(bmain, id, id->override_library->reference, ID_REMAP_SKIP_INDIRECT_USAGE); - BKE_id_delete(bmain, id); - } - else { - BKE_lib_override_library_id_reset(bmain, id, true); + if (id_root->override_library->hierarchy_root != nullptr) { + id_root = id_root->override_library->hierarchy_root; } - WM_event_add_notifier(C, NC_WINDOW, nullptr); + data->id_root_set(id_root); +} + +/* Clear (delete) a hierarchy of library overrides. */ +static void id_override_library_delete_hierarchy_process(bContext *C, + ReportList *UNUSED(reports), + OutlinerLibOverrideData &data) +{ + Main *bmain = CTX_data_main(C); + + for (auto &&id_hierarchy_root : data.id_hierarchy_roots.keys()) { + BKE_lib_override_library_delete(bmain, id_hierarchy_root); + } } static void id_fake_user_set_fn(bContext *UNUSED(C), @@ -1399,6 +1627,251 @@ static void refreshdrivers_animdata_fn(int UNUSED(event), /** \} */ /* -------------------------------------------------------------------- */ +/** \name Library Overrides Operation Menu. + * \{ */ + +enum eOutlinerLibOverrideOpTypes { + OUTLINER_LIBOVERRIDE_OP_INVALID = 0, + + OUTLINER_LIBOVERRIDE_OP_CREATE_HIERARCHY, + OUTLINER_LIBOVERRIDE_OP_RESET, + OUTLINER_LIBOVERRIDE_OP_CLEAR_SINGLE, + + OUTLINER_LIBOVERRIDE_OP_RESYNC_HIERARCHY, + OUTLINER_LIBOVERRIDE_OP_RESYNC_HIERARCHY_ENFORCE, + OUTLINER_LIBOVERRIDE_OP_DELETE_HIERARCHY, +}; + +static const EnumPropertyItem prop_liboverride_op_types[] = { + {OUTLINER_LIBOVERRIDE_OP_CREATE_HIERARCHY, + "OVERRIDE_LIBRARY_CREATE_HIERARCHY", + 0, + "Make", + "Create a local override of the selected linked data-blocks, and their hierarchy of " + "dependencies"}, + {OUTLINER_LIBOVERRIDE_OP_RESET, + "OVERRIDE_LIBRARY_RESET", + 0, + "Reset", + "Reset the selected local overrides to their linked references values"}, + {OUTLINER_LIBOVERRIDE_OP_CLEAR_SINGLE, + "OVERRIDE_LIBRARY_CLEAR_SINGLE", + 0, + "Clear", + "Delete the selected local overrides and relink their usages to the linked data-blocks if " + "possible, else reset them and mark them as non editable"}, + {0, nullptr, 0, nullptr, nullptr}, +}; + +static const EnumPropertyItem prop_liboverride_troubleshoot_op_types[] = { + {OUTLINER_LIBOVERRIDE_OP_RESYNC_HIERARCHY, + "OVERRIDE_LIBRARY_RESYNC_HIERARCHY", + 0, + "Resync", + "Rebuild the selected local overrides from their linked references, as well as their " + "hierarchies of dependencies"}, + {OUTLINER_LIBOVERRIDE_OP_RESYNC_HIERARCHY_ENFORCE, + "OVERRIDE_LIBRARY_RESYNC_HIERARCHY_ENFORCE", + 0, + "Resync Enforce", + "Rebuild the selected local overrides from their linked references, as well as their " + "hierarchies of dependencies, enforcing these hierarchies to match the linked data (i.e. " + "ignoring existing overrides on data-blocks pointer properties)"}, + RNA_ENUM_ITEM_SEPR, + {OUTLINER_LIBOVERRIDE_OP_DELETE_HIERARCHY, + "OVERRIDE_LIBRARY_DELETE_HIERARCHY", + 0, + "Delete", + "Delete the selected local overrides (including their hierarchies of override dependencies) " + "and relink their usages to the linked data-blocks"}, + {0, nullptr, 0, nullptr, nullptr}, +}; + +static bool outliner_liboverride_operation_poll(bContext *C) +{ + if (!outliner_operation_tree_element_poll(C)) { + return false; + } + return true; +} + +static int outliner_liboverride_operation_exec(bContext *C, wmOperator *op) +{ + Scene *scene = CTX_data_scene(C); + SpaceOutliner *space_outliner = CTX_wm_space_outliner(C); + int scenelevel = 0, objectlevel = 0, idlevel = 0, datalevel = 0; + + /* check for invalid states */ + if (space_outliner == nullptr) { + return OPERATOR_CANCELLED; + } + + TreeElement *te = get_target_element(space_outliner); + get_element_operation_type(te, &scenelevel, &objectlevel, &idlevel, &datalevel); + + const eOutlinerLibOpSelectionSet selection_set = static_cast<eOutlinerLibOpSelectionSet>( + RNA_enum_get(op->ptr, "selection_set")); + const eOutlinerLibOverrideOpTypes event = static_cast<eOutlinerLibOverrideOpTypes>( + RNA_enum_get(op->ptr, "type")); + switch (event) { + case OUTLINER_LIBOVERRIDE_OP_CREATE_HIERARCHY: { + OutlinerLibOverrideData override_data{}; + override_data.do_hierarchy = true; + override_data.do_fully_editable = false; + + outliner_do_libdata_operation_selection_set( + C, + op->reports, + scene, + space_outliner, + id_override_library_create_hierarchy_pre_process_fn, + selection_set, + &override_data); + + id_override_library_create_hierarchy_process(C, op->reports, override_data); + + ED_undo_push(C, "Overridden Data Hierarchy"); + break; + } + case OUTLINER_LIBOVERRIDE_OP_RESET: { + OutlinerLibOverrideData override_data{}; + outliner_do_libdata_operation_selection_set(C, + op->reports, + scene, + space_outliner, + id_override_library_reset_fn, + selection_set, + &override_data); + ED_undo_push(C, "Reset Overridden Data"); + break; + } + case OUTLINER_LIBOVERRIDE_OP_CLEAR_SINGLE: { + outliner_do_libdata_operation_selection_set(C, + op->reports, + scene, + space_outliner, + id_override_library_clear_single_fn, + selection_set, + nullptr); + ED_undo_push(C, "Clear Overridden Data"); + break; + } + + case OUTLINER_LIBOVERRIDE_OP_RESYNC_HIERARCHY: { + OutlinerLibOverrideData override_data{}; + override_data.do_hierarchy = true; + outliner_do_libdata_operation_selection_set(C, + op->reports, + scene, + space_outliner, + id_override_library_resync_fn, + OUTLINER_LIB_SELECTIONSET_SELECTED, + &override_data); + + id_override_library_resync_hierarchy_process(C, op->reports, override_data); + + ED_undo_push(C, "Resync Overridden Data Hierarchy"); + break; + } + case OUTLINER_LIBOVERRIDE_OP_RESYNC_HIERARCHY_ENFORCE: { + OutlinerLibOverrideData override_data{}; + override_data.do_hierarchy = true; + override_data.do_resync_hierarchy_enforce = true; + outliner_do_libdata_operation_selection_set(C, + op->reports, + scene, + space_outliner, + id_override_library_resync_fn, + OUTLINER_LIB_SELECTIONSET_SELECTED, + &override_data); + + id_override_library_resync_hierarchy_process(C, op->reports, override_data); + + ED_undo_push(C, "Resync Overridden Data Hierarchy Enforce"); + break; + } + case OUTLINER_LIBOVERRIDE_OP_DELETE_HIERARCHY: { + OutlinerLibOverrideData override_data{}; + override_data.do_hierarchy = true; + outliner_do_libdata_operation_selection_set(C, + op->reports, + scene, + space_outliner, + id_override_library_delete_hierarchy_fn, + OUTLINER_LIB_SELECTIONSET_SELECTED, + nullptr); + + id_override_library_delete_hierarchy_process(C, op->reports, override_data); + + ED_undo_push(C, "Delete Overridden Data Hierarchy"); + break; + } + default: + /* Invalid - unhandled. */ + break; + } + + WM_event_add_notifier(C, NC_WINDOW, nullptr); + WM_event_add_notifier(C, NC_WM | ND_LIB_OVERRIDE_CHANGED, nullptr); + WM_event_add_notifier(C, NC_SPACE | ND_SPACE_VIEW3D, nullptr); + + return OPERATOR_FINISHED; +} + +void OUTLINER_OT_liboverride_operation(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Outliner Library Override Operation"; + ot->idname = "OUTLINER_OT_liboverride_operation"; + ot->description = "Create, reset or clear library override hierarchies"; + + /* callbacks */ + ot->invoke = WM_menu_invoke; + ot->exec = outliner_liboverride_operation_exec; + ot->poll = outliner_liboverride_operation_poll; + + ot->flag = 0; + + RNA_def_enum(ot->srna, "type", prop_liboverride_op_types, 0, "Library Override Operation", ""); + ot->prop = RNA_def_enum(ot->srna, + "selection_set", + prop_lib_op_selection_set, + 0, + "Selection Set", + "Over which part of the tree items to apply the operation"); +} + +void OUTLINER_OT_liboverride_troubleshoot_operation(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Outliner Library Override Troubleshoot Operation"; + ot->idname = "OUTLINER_OT_liboverride_troubleshoot_operation"; + ot->description = "Advanced operations over library override to help fix broken hierarchies"; + + /* callbacks */ + ot->invoke = WM_menu_invoke; + ot->exec = outliner_liboverride_operation_exec; + ot->poll = outliner_liboverride_operation_poll; + + ot->flag = 0; + + ot->prop = RNA_def_enum(ot->srna, + "type", + prop_liboverride_troubleshoot_op_types, + 0, + "Library Override Troubleshoot Operation", + ""); + RNA_def_enum(ot->srna, + "selection_set", + prop_lib_op_selection_set, + 0, + "Selection Set", + "Over which part of the tree items to apply the operation"); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ /** \name Object Operation Utilities * \{ */ @@ -1542,7 +2015,7 @@ static void data_select_linked_fn(int event, const PointerRNA &ptr = te_rna_struct->getPointerRNA(); if (RNA_struct_is_ID(ptr.type)) { bContext *C = (bContext *)C_v; - ID *id = reinterpret_cast<ID *>(ptr.data); + ID *id = static_cast<ID *>(ptr.data); ED_object_select_linked_by_id(C, id); } @@ -1551,7 +2024,7 @@ static void data_select_linked_fn(int event, static void constraint_fn(int event, TreeElement *te, TreeStoreElem *UNUSED(tselem), void *C_v) { - bContext *C = reinterpret_cast<bContext *>(C_v); + bContext *C = static_cast<bContext *>(C_v); Main *bmain = CTX_data_main(C); bConstraint *constraint = (bConstraint *)te->directdata; Object *ob = (Object *)outliner_search_back(te, ID_OB); @@ -1642,7 +2115,7 @@ static Base *outliner_batch_delete_hierarchy( } object = base->object; - for (child_base = reinterpret_cast<Base *>(view_layer->object_bases.first); child_base; + for (child_base = static_cast<Base *>(view_layer->object_bases.first); child_base; child_base = base_next) { base_next = child_base->next; for (parent = child_base->object->parent; parent && (parent != object); @@ -1863,9 +2336,9 @@ static void outliner_do_object_delete(bContext *C, } } -static TreeTraversalAction outliner_find_objects_to_delete(TreeElement *te, void *customdata) +static TreeTraversalAction outliner_collect_objects_to_delete(TreeElement *te, void *customdata) { - ObjectEditData *data = reinterpret_cast<ObjectEditData *>(customdata); + ObjectEditData *data = static_cast<ObjectEditData *>(customdata); GSet *objects_to_delete = data->objects_set; TreeStoreElem *tselem = TREESTORE(te); @@ -1878,6 +2351,17 @@ static TreeTraversalAction outliner_find_objects_to_delete(TreeElement *te, void return TRAVERSE_SKIP_CHILDS; } + /* Do not allow to delete children objects of an override collection. */ + TreeElement *te_parent = te->parent; + if (outliner_is_collection_tree_element(te_parent)) { + TreeStoreElem *tselem_parent = TREESTORE(te_parent); + ID *id_parent = tselem_parent->id; + BLI_assert(GS(id_parent->name) == ID_GR); + if (ID_IS_OVERRIDE_LIBRARY_REAL(id_parent)) { + return TRAVERSE_SKIP_CHILDS; + } + } + ID *id = tselem->id; if (ID_IS_OVERRIDE_LIBRARY_REAL(id)) { @@ -1905,7 +2389,7 @@ static int outliner_delete_exec(bContext *C, wmOperator *op) SpaceOutliner *space_outliner = CTX_wm_space_outliner(C); struct wmMsgBus *mbus = CTX_wm_message_bus(C); ViewLayer *view_layer = CTX_data_view_layer(C); - const Base *basact_prev = BASACT(view_layer); + const Base *basact_prev = view_layer->basact; const bool delete_hierarchy = RNA_boolean_get(op->ptr, "hierarchy"); @@ -1919,7 +2403,7 @@ static int outliner_delete_exec(bContext *C, wmOperator *op) &space_outliner->tree, 0, TSE_SELECTED, - outliner_find_objects_to_delete, + outliner_collect_objects_to_delete, &object_delete_data); if (delete_hierarchy) { @@ -1949,7 +2433,7 @@ static int outliner_delete_exec(bContext *C, wmOperator *op) DEG_id_tag_update(&scene->id, ID_RECALC_COPY_ON_WRITE); DEG_relations_tag_update(bmain); - if (basact_prev != BASACT(view_layer)) { + if (basact_prev != view_layer->basact) { WM_event_add_notifier(C, NC_SCENE | ND_OB_ACTIVE, scene); WM_msg_publish_rna_prop(mbus, &scene->id, view_layer, LayerObjects, active); } @@ -1993,16 +2477,6 @@ enum eOutlinerIdOpTypes { OUTLINER_IDOP_UNLINK, OUTLINER_IDOP_LOCAL, - OUTLINER_IDOP_OVERRIDE_LIBRARY_CREATE, - OUTLINER_IDOP_OVERRIDE_LIBRARY_CREATE_HIERARCHY, - OUTLINER_IDOP_OVERRIDE_LIBRARY_CREATE_HIERARCHY_FULLY_EDITABLE, - OUTLINER_IDOP_OVERRIDE_LIBRARY_MAKE_EDITABLE, - OUTLINER_IDOP_OVERRIDE_LIBRARY_RESET, - OUTLINER_IDOP_OVERRIDE_LIBRARY_RESET_HIERARCHY, - OUTLINER_IDOP_OVERRIDE_LIBRARY_RESYNC_HIERARCHY, - OUTLINER_IDOP_OVERRIDE_LIBRARY_RESYNC_HIERARCHY_ENFORCE, - OUTLINER_IDOP_OVERRIDE_LIBRARY_CLEAR_HIERARCHY, - OUTLINER_IDOP_OVERRIDE_LIBRARY_CLEAR_SINGLE, OUTLINER_IDOP_SINGLE, OUTLINER_IDOP_DELETE, OUTLINER_IDOP_REMAP, @@ -2029,65 +2503,6 @@ static const EnumPropertyItem prop_id_op_types[] = { "Remap Users", "Make all users of selected data-blocks to use instead current (clicked) one"}, RNA_ENUM_ITEM_SEPR, - {OUTLINER_IDOP_OVERRIDE_LIBRARY_CREATE, - "OVERRIDE_LIBRARY_CREATE", - 0, - "Make Library Override Single", - "Make a single, out-of-hierarchy local override of this linked data-block - only applies to " - "active Outliner item"}, - {OUTLINER_IDOP_OVERRIDE_LIBRARY_CREATE_HIERARCHY, - "OVERRIDE_LIBRARY_CREATE_HIERARCHY", - 0, - "Make Library Override Hierarchy", - "Make a local override of this linked data-block, and its hierarchy of dependencies - only " - "applies to active Outliner item"}, - {OUTLINER_IDOP_OVERRIDE_LIBRARY_CREATE_HIERARCHY_FULLY_EDITABLE, - "OVERRIDE_LIBRARY_CREATE_HIERARCHY_FULLY_EDITABLE", - 0, - "Make Library Override Hierarchy Fully Editable", - "Make a local override of this linked data-block, and its hierarchy of dependencies, making " - "them all fully user-editable - only applies to active Outliner item"}, - {OUTLINER_IDOP_OVERRIDE_LIBRARY_MAKE_EDITABLE, - "OVERRIDE_LIBRARY_MAKE_EDITABLE", - 0, - "Make Library Override Editable", - "Make the library override data-block editable"}, - {OUTLINER_IDOP_OVERRIDE_LIBRARY_RESET, - "OVERRIDE_LIBRARY_RESET", - 0, - "Reset Library Override Single", - "Reset this local override to its linked values"}, - {OUTLINER_IDOP_OVERRIDE_LIBRARY_RESET_HIERARCHY, - "OVERRIDE_LIBRARY_RESET_HIERARCHY", - 0, - "Reset Library Override Hierarchy", - "Reset this local override to its linked values, as well as its hierarchy of dependencies"}, - {OUTLINER_IDOP_OVERRIDE_LIBRARY_RESYNC_HIERARCHY, - "OVERRIDE_LIBRARY_RESYNC_HIERARCHY", - 0, - "Resync Library Override Hierarchy", - "Rebuild this local override from its linked reference, as well as its hierarchy of " - "dependencies"}, - {OUTLINER_IDOP_OVERRIDE_LIBRARY_RESYNC_HIERARCHY_ENFORCE, - "OVERRIDE_LIBRARY_RESYNC_HIERARCHY_ENFORCE", - 0, - "Resync Library Override Hierarchy Enforce", - "Rebuild this local override from its linked reference, as well as its hierarchy of " - "dependencies, enforcing that hierarchy to match the linked data (i.e. ignoring exiting " - "overrides on data-blocks pointer properties)"}, - {OUTLINER_IDOP_OVERRIDE_LIBRARY_CLEAR_SINGLE, - "OVERRIDE_LIBRARY_CLEAR_SINGLE", - 0, - "Clear Library Override Single", - "Delete this local override and relink its usages to the linked data-blocks if possible, " - "else reset it and mark it as non editable"}, - {OUTLINER_IDOP_OVERRIDE_LIBRARY_CLEAR_HIERARCHY, - "OVERRIDE_LIBRARY_CLEAR_HIERARCHY", - 0, - "Clear Library Override Hierarchy", - "Delete this local override (including its hierarchy of override dependencies) and relink " - "its usages to the linked data-blocks"}, - RNA_ENUM_ITEM_SEPR, {OUTLINER_IDOP_COPY, "COPY", ICON_COPYDOWN, "Copy", ""}, {OUTLINER_IDOP_PASTE, "PASTE", ICON_PASTEDOWN, "Paste", ""}, RNA_ENUM_ITEM_SEPR, @@ -2120,34 +2535,6 @@ static bool outliner_id_operation_item_poll(bContext *C, } switch (enum_value) { - case OUTLINER_IDOP_OVERRIDE_LIBRARY_CREATE: - if (ID_IS_OVERRIDABLE_LIBRARY(tselem->id)) { - return true; - } - return false; - case OUTLINER_IDOP_OVERRIDE_LIBRARY_CREATE_HIERARCHY: - case OUTLINER_IDOP_OVERRIDE_LIBRARY_CREATE_HIERARCHY_FULLY_EDITABLE: - if (ID_IS_OVERRIDABLE_LIBRARY(tselem->id) || (ID_IS_LINKED(tselem->id))) { - return true; - } - return false; - case OUTLINER_IDOP_OVERRIDE_LIBRARY_MAKE_EDITABLE: - if (ID_IS_OVERRIDE_LIBRARY_REAL(tselem->id) && !ID_IS_LINKED(tselem->id)) { - if (tselem->id->override_library->flag & IDOVERRIDE_LIBRARY_FLAG_SYSTEM_DEFINED) { - return true; - } - } - return false; - case OUTLINER_IDOP_OVERRIDE_LIBRARY_RESET: - case OUTLINER_IDOP_OVERRIDE_LIBRARY_RESET_HIERARCHY: - case OUTLINER_IDOP_OVERRIDE_LIBRARY_RESYNC_HIERARCHY: - case OUTLINER_IDOP_OVERRIDE_LIBRARY_RESYNC_HIERARCHY_ENFORCE: - case OUTLINER_IDOP_OVERRIDE_LIBRARY_CLEAR_HIERARCHY: - case OUTLINER_IDOP_OVERRIDE_LIBRARY_CLEAR_SINGLE: - if (ID_IS_OVERRIDE_LIBRARY_REAL(tselem->id) && !ID_IS_LINKED(tselem->id)) { - return true; - } - return false; case OUTLINER_IDOP_SINGLE: if (ELEM(space_outliner->outlinevis, SO_SCENES, SO_VIEW_LAYER)) { return true; @@ -2185,6 +2572,7 @@ static const EnumPropertyItem *outliner_id_operation_itemf(bContext *C, static int outliner_id_operation_exec(bContext *C, wmOperator *op) { + Main *bmain = CTX_data_main(C); wmWindowManager *wm = CTX_wm_manager(C); Scene *scene = CTX_data_scene(C); SpaceOutliner *space_outliner = CTX_wm_space_outliner(C); @@ -2259,101 +2647,6 @@ static int outliner_id_operation_exec(bContext *C, wmOperator *op) ED_undo_push(C, "Localized Data"); break; } - case OUTLINER_IDOP_OVERRIDE_LIBRARY_CREATE: { - OutlinerLibOverrideData override_data{}; - outliner_do_libdata_operation( - C, op->reports, scene, space_outliner, id_override_library_create_fn, &override_data); - ED_undo_push(C, "Overridden Data"); - break; - } - case OUTLINER_IDOP_OVERRIDE_LIBRARY_CREATE_HIERARCHY: { - OutlinerLibOverrideData override_data{}; - override_data.do_hierarchy = true; - outliner_do_libdata_operation(C, - op->reports, - scene, - space_outliner, - id_override_library_create_hierarchy_pre_process_fn, - &override_data); - outliner_do_libdata_operation( - C, op->reports, scene, space_outliner, id_override_library_create_fn, &override_data); - id_override_library_create_hierarchy_post_process(C, &override_data); - - ED_undo_push(C, "Overridden Data Hierarchy"); - break; - } - case OUTLINER_IDOP_OVERRIDE_LIBRARY_CREATE_HIERARCHY_FULLY_EDITABLE: { - OutlinerLibOverrideData override_data{}; - override_data.do_hierarchy = true; - override_data.do_fully_editable = true; - outliner_do_libdata_operation(C, - op->reports, - scene, - space_outliner, - id_override_library_create_hierarchy_pre_process_fn, - &override_data); - outliner_do_libdata_operation( - C, op->reports, scene, space_outliner, id_override_library_create_fn, &override_data); - id_override_library_create_hierarchy_post_process(C, &override_data); - - ED_undo_push(C, "Overridden Data Hierarchy Fully Editable"); - break; - } - case OUTLINER_IDOP_OVERRIDE_LIBRARY_MAKE_EDITABLE: { - outliner_do_libdata_operation(C, - op->reports, - scene, - space_outliner, - id_override_library_toggle_flag_fn, - POINTER_FROM_UINT(IDOVERRIDE_LIBRARY_FLAG_SYSTEM_DEFINED)); - - ED_undo_push(C, "Make Overridden Data Editable"); - break; - } - case OUTLINER_IDOP_OVERRIDE_LIBRARY_RESET: { - OutlinerLibOverrideData override_data{}; - outliner_do_libdata_operation( - C, op->reports, scene, space_outliner, id_override_library_reset_fn, &override_data); - ED_undo_push(C, "Reset Overridden Data"); - break; - } - case OUTLINER_IDOP_OVERRIDE_LIBRARY_RESET_HIERARCHY: { - OutlinerLibOverrideData override_data{}; - override_data.do_hierarchy = true; - outliner_do_libdata_operation( - C, op->reports, scene, space_outliner, id_override_library_reset_fn, &override_data); - ED_undo_push(C, "Reset Overridden Data Hierarchy"); - break; - } - case OUTLINER_IDOP_OVERRIDE_LIBRARY_RESYNC_HIERARCHY: { - OutlinerLibOverrideData override_data{}; - override_data.do_hierarchy = true; - outliner_do_libdata_operation( - C, op->reports, scene, space_outliner, id_override_library_resync_fn, &override_data); - ED_undo_push(C, "Resync Overridden Data Hierarchy"); - break; - } - case OUTLINER_IDOP_OVERRIDE_LIBRARY_RESYNC_HIERARCHY_ENFORCE: { - OutlinerLibOverrideData override_data{}; - override_data.do_hierarchy = true; - override_data.do_resync_hierarchy_enforce = true; - outliner_do_libdata_operation( - C, op->reports, scene, space_outliner, id_override_library_resync_fn, &override_data); - ED_undo_push(C, "Resync Overridden Data Hierarchy"); - break; - } - case OUTLINER_IDOP_OVERRIDE_LIBRARY_CLEAR_HIERARCHY: { - outliner_do_libdata_operation( - C, op->reports, scene, space_outliner, id_override_library_clear_hierarchy_fn, nullptr); - ED_undo_push(C, "Clear Overridden Data Hierarchy"); - break; - } - case OUTLINER_IDOP_OVERRIDE_LIBRARY_CLEAR_SINGLE: { - outliner_do_libdata_operation( - C, op->reports, scene, space_outliner, id_override_library_clear_single_fn, nullptr); - ED_undo_push(C, "Clear Overridden Data Hierarchy"); - break; - } case OUTLINER_IDOP_SINGLE: { /* make single user */ switch (idlevel) { @@ -2381,8 +2674,10 @@ static int outliner_id_operation_exec(bContext *C, wmOperator *op) } case OUTLINER_IDOP_DELETE: { if (idlevel > 0) { + BKE_main_id_tag_all(bmain, LIB_TAG_DOIT, false); outliner_do_libdata_operation( - C, op->reports, scene, space_outliner, id_delete_fn, nullptr); + C, op->reports, scene, space_outliner, id_delete_tag_fn, nullptr); + BKE_id_multi_tagged_delete(bmain); ED_undo_push(C, "Delete"); } break; @@ -2463,6 +2758,7 @@ void OUTLINER_OT_id_operation(wmOperatorType *ot) /* identifiers */ ot->name = "Outliner ID Data Operation"; ot->idname = "OUTLINER_OT_id_operation"; + ot->description = "General data-block management operations"; /* callbacks */ ot->invoke = WM_menu_invoke; @@ -2507,6 +2803,7 @@ static const EnumPropertyItem outliner_lib_op_type_items[] = { static int outliner_lib_operation_exec(bContext *C, wmOperator *op) { + Main *bmain = CTX_data_main(C); Scene *scene = CTX_data_scene(C); SpaceOutliner *space_outliner = CTX_wm_space_outliner(C); int scenelevel = 0, objectlevel = 0, idlevel = 0, datalevel = 0; @@ -2522,7 +2819,10 @@ static int outliner_lib_operation_exec(bContext *C, wmOperator *op) eOutlinerLibOpTypes event = (eOutlinerLibOpTypes)RNA_enum_get(op->ptr, "type"); switch (event) { case OL_LIB_DELETE: { - outliner_do_libdata_operation(C, op->reports, scene, space_outliner, id_delete_fn, nullptr); + BKE_main_id_tag_all(bmain, LIB_TAG_DOIT, false); + outliner_do_libdata_operation( + C, op->reports, scene, space_outliner, id_delete_tag_fn, nullptr); + BKE_id_multi_tagged_delete(bmain); ED_undo_push(C, "Delete Library"); break; } @@ -2623,8 +2923,7 @@ static int outliner_action_set_exec(bContext *C, wmOperator *op) get_element_operation_type(te, &scenelevel, &objectlevel, &idlevel, &datalevel); /* get action to use */ - act = reinterpret_cast<bAction *>( - BLI_findlink(&bmain->actions, RNA_enum_get(op->ptr, "action"))); + act = static_cast<bAction *>(BLI_findlink(&bmain->actions, RNA_enum_get(op->ptr, "action"))); if (act == nullptr) { BKE_report(op->reports, RPT_ERROR, "No valid action to add"); @@ -3163,3 +3462,5 @@ void OUTLINER_OT_operation(wmOperatorType *ot) } /** \} */ + +} // namespace blender::ed::outliner diff --git a/source/blender/editors/space_outliner/outliner_tree.cc b/source/blender/editors/space_outliner/outliner_tree.cc index aa739758ecb..070df284c6f 100644 --- a/source/blender/editors/space_outliner/outliner_tree.cc +++ b/source/blender/editors/space_outliner/outliner_tree.cc @@ -41,6 +41,7 @@ #include "BLI_fnmatch.h" #include "BLI_listbase.h" #include "BLI_mempool.h" +#include "BLI_timeit.hh" #include "BLI_utildefines.h" #include "BLT_translation.h" @@ -51,7 +52,7 @@ #include "BKE_lib_id.h" #include "BKE_main.h" #include "BKE_modifier.h" -#include "BKE_outliner_treehash.h" +#include "BKE_outliner_treehash.hh" #include "ED_screen.h" @@ -69,7 +70,7 @@ # include "BLI_math_base.h" /* M_PI */ #endif -using namespace blender::ed::outliner; +namespace blender::ed::outliner { /* prototypes */ static int outliner_exclude_filter_get(const SpaceOutliner *space_outliner); @@ -90,7 +91,7 @@ static void outliner_storage_cleanup(SpaceOutliner *space_outliner) BLI_mempool_iter iter; BLI_mempool_iternew(ts, &iter); - while ((tselem = reinterpret_cast<TreeStoreElem *>(BLI_mempool_iterstep(&iter)))) { + while ((tselem = static_cast<TreeStoreElem *>(BLI_mempool_iterstep(&iter)))) { tselem->used = 0; } @@ -100,7 +101,7 @@ static void outliner_storage_cleanup(SpaceOutliner *space_outliner) space_outliner->storeflag &= ~SO_TREESTORE_CLEANUP; BLI_mempool_iternew(ts, &iter); - while ((tselem = reinterpret_cast<TreeStoreElem *>(BLI_mempool_iterstep(&iter)))) { + while ((tselem = static_cast<TreeStoreElem *>(BLI_mempool_iterstep(&iter)))) { if (tselem->id == nullptr) { unused++; } @@ -110,34 +111,30 @@ static void outliner_storage_cleanup(SpaceOutliner *space_outliner) if (BLI_mempool_len(ts) == unused) { BLI_mempool_destroy(ts); space_outliner->treestore = nullptr; - if (space_outliner->runtime->treehash) { - BKE_outliner_treehash_free(space_outliner->runtime->treehash); - space_outliner->runtime->treehash = nullptr; - } + space_outliner->runtime->tree_hash = nullptr; } else { TreeStoreElem *tsenew; BLI_mempool *new_ts = BLI_mempool_create( sizeof(TreeStoreElem), BLI_mempool_len(ts) - unused, 512, BLI_MEMPOOL_ALLOW_ITER); BLI_mempool_iternew(ts, &iter); - while ((tselem = reinterpret_cast<TreeStoreElem *>(BLI_mempool_iterstep(&iter)))) { + while ((tselem = static_cast<TreeStoreElem *>(BLI_mempool_iterstep(&iter)))) { if (tselem->id) { - tsenew = reinterpret_cast<TreeStoreElem *>(BLI_mempool_alloc(new_ts)); + tsenew = static_cast<TreeStoreElem *>(BLI_mempool_alloc(new_ts)); *tsenew = *tselem; } } BLI_mempool_destroy(ts); space_outliner->treestore = new_ts; - if (space_outliner->runtime->treehash) { + if (space_outliner->runtime->tree_hash) { /* update hash table to fix broken pointers */ - BKE_outliner_treehash_rebuild_from_treestore(space_outliner->runtime->treehash, - space_outliner->treestore); + space_outliner->runtime->tree_hash->rebuild_from_treestore(*space_outliner->treestore); } } } } - else if (space_outliner->runtime->treehash) { - BKE_outliner_treehash_clear_used(space_outliner->runtime->treehash); + else if (space_outliner->runtime->tree_hash) { + space_outliner->runtime->tree_hash->clear_used(); } } } @@ -150,15 +147,14 @@ static void check_persistent( space_outliner->treestore = BLI_mempool_create( sizeof(TreeStoreElem), 1, 512, BLI_MEMPOOL_ALLOW_ITER); } - if (space_outliner->runtime->treehash == nullptr) { - space_outliner->runtime->treehash = reinterpret_cast<GHash *>( - BKE_outliner_treehash_create_from_treestore(space_outliner->treestore)); + if (space_outliner->runtime->tree_hash == nullptr) { + space_outliner->runtime->tree_hash = treehash::TreeHash::create_from_treestore( + *space_outliner->treestore); } /* find any unused tree element in treestore and mark it as used * (note that there may be multiple unused elements in case of linked objects) */ - TreeStoreElem *tselem = BKE_outliner_treehash_lookup_unused( - space_outliner->runtime->treehash, type, nr, id); + TreeStoreElem *tselem = space_outliner->runtime->tree_hash->lookup_unused(type, nr, id); if (tselem) { te->store_elem = tselem; tselem->used = 1; @@ -166,14 +162,14 @@ static void check_persistent( } /* add 1 element to treestore */ - tselem = reinterpret_cast<TreeStoreElem *>(BLI_mempool_alloc(space_outliner->treestore)); + tselem = static_cast<TreeStoreElem *>(BLI_mempool_alloc(space_outliner->treestore)); tselem->type = type; tselem->nr = type ? nr : 0; tselem->id = id; tselem->used = 0; tselem->flag = TSE_CLOSED; te->store_elem = tselem; - BKE_outliner_treehash_add_element(space_outliner->runtime->treehash, tselem); + space_outliner->runtime->tree_hash->add_element(*tselem); } /** \} */ @@ -221,11 +217,6 @@ bool outliner_requires_rebuild_on_select_or_active_change(const SpaceOutliner *s return exclude_flags & (SO_FILTER_OB_STATE_SELECTED | SO_FILTER_OB_STATE_ACTIVE); } -bool outliner_requires_rebuild_on_open_change(const SpaceOutliner *space_outliner) -{ - return ELEM(space_outliner->outlinevis, SO_DATA_API); -} - /* special handling of hierarchical non-lib data */ static void outliner_add_bone(SpaceOutliner *space_outliner, ListBase *lb, @@ -293,7 +284,7 @@ static void outliner_add_object_contents(SpaceOutliner *space_outliner, outliner_add_element(space_outliner, &te->subtree, ob->data, te, TSE_SOME_ID, 0); if (ob->pose) { - bArmature *arm = reinterpret_cast<bArmature *>(ob->data); + bArmature *arm = static_cast<bArmature *>(ob->data); TreeElement *tenla = outliner_add_element( space_outliner, &te->subtree, ob, te, TSE_POSE_BASE, 0); tenla->name = IFACE_("Pose"); @@ -339,7 +330,7 @@ static void outliner_add_object_contents(SpaceOutliner *space_outliner, } } /* make hierarchy */ - TreeElement *ten = reinterpret_cast<TreeElement *>(tenla->subtree.first); + TreeElement *ten = static_cast<TreeElement *>(tenla->subtree.first); while (ten) { TreeElement *nten = ten->next, *par; tselem = TREESTORE(ten); @@ -694,15 +685,15 @@ static void outliner_add_id_contents(SpaceOutliner *space_outliner, ebone->temp.p = ten; } /* make hierarchy */ - TreeElement *ten = arm->edbo->first ? reinterpret_cast<TreeElement *>( - ((EditBone *)arm->edbo->first)->temp.p) : - nullptr; + TreeElement *ten = arm->edbo->first ? + static_cast<TreeElement *>(((EditBone *)arm->edbo->first)->temp.p) : + nullptr; while (ten) { TreeElement *nten = ten->next, *par; EditBone *ebone = (EditBone *)ten->directdata; if (ebone->parent) { BLI_remlink(&te->subtree, ten); - par = reinterpret_cast<TreeElement *>(ebone->parent->temp.p); + par = static_cast<TreeElement *>(ebone->parent->temp.p); BLI_addtail(&par->subtree, ten); ten->parent = par; } @@ -795,8 +786,6 @@ static void outliner_add_id_contents(SpaceOutliner *space_outliner, } } -namespace blender::ed::outliner { - TreeElement *outliner_add_element(SpaceOutliner *space_outliner, ListBase *lb, void *idv, @@ -805,21 +794,24 @@ TreeElement *outliner_add_element(SpaceOutliner *space_outliner, short index, const bool expand) { - ID *id = reinterpret_cast<ID *>(idv); + ID *id = static_cast<ID *>(idv); if (ELEM(type, TSE_RNA_STRUCT, TSE_RNA_PROPERTY, TSE_RNA_ARRAY_ELEM)) { id = ((PointerRNA *)idv)->owner_id; if (!id) { - id = reinterpret_cast<ID *>(((PointerRNA *)idv)->data); + id = static_cast<ID *>(((PointerRNA *)idv)->data); } } else if (type == TSE_GP_LAYER) { /* idv is the layer itself */ id = TREESTORE(parent)->id; } + else if (ELEM(type, TSE_GENERIC_LABEL)) { + id = nullptr; + } /* exceptions */ - if (type == TSE_ID_BASE) { + if (ELEM(type, TSE_ID_BASE, TSE_GENERIC_LABEL)) { /* pass */ } else if (id == nullptr) { @@ -869,7 +861,7 @@ TreeElement *outliner_add_element(SpaceOutliner *space_outliner, else if (ELEM(type, TSE_LAYER_COLLECTION, TSE_SCENE_COLLECTION_BASE, TSE_VIEW_COLLECTION_BASE)) { /* pass */ } - else if (type == TSE_ID_BASE) { + else if (ELEM(type, TSE_ID_BASE, TSE_GENERIC_LABEL)) { /* pass */ } else if (type == TSE_SOME_ID) { @@ -877,7 +869,10 @@ TreeElement *outliner_add_element(SpaceOutliner *space_outliner, BLI_assert_msg(0, "Expected this ID type to be ported to new Outliner tree-element design"); } } - else if (ELEM(type, TSE_LIBRARY_OVERRIDE_BASE, TSE_LIBRARY_OVERRIDE)) { + else if (ELEM(type, + TSE_LIBRARY_OVERRIDE_BASE, + TSE_LIBRARY_OVERRIDE, + TSE_LIBRARY_OVERRIDE_OPERATION)) { if (!te->abstract_element) { BLI_assert_msg(0, "Expected override types to be ported to new Outliner tree-element design"); @@ -895,10 +890,13 @@ TreeElement *outliner_add_element(SpaceOutliner *space_outliner, te->idcode = GS(id->name); } - if (expand && te->abstract_element && te->abstract_element->isExpandValid()) { + if (!expand) { + /* Pass */ + } + else if (te->abstract_element && te->abstract_element->isExpandValid()) { tree_element_expand(*te->abstract_element, *space_outliner); } - else if (expand && (type == TSE_SOME_ID)) { + else if (type == TSE_SOME_ID) { /* ID types not (fully) ported to new design yet. */ if (te->abstract_element->expandPoll(*space_outliner)) { outliner_add_id_contents(space_outliner, te, tselem, id); @@ -916,15 +914,14 @@ TreeElement *outliner_add_element(SpaceOutliner *space_outliner, TSE_RNA_ARRAY_ELEM, TSE_SEQUENCE, TSE_SEQ_STRIP, - TSE_SEQUENCE_DUP)) { + TSE_SEQUENCE_DUP, + TSE_GENERIC_LABEL)) { BLI_assert_msg(false, "Element type should already use new AbstractTreeElement design"); } return te; } -} // namespace blender::ed::outliner - /* ======================================================= */ BLI_INLINE void outliner_add_collection_init(TreeElement *te, Collection *collection) @@ -980,8 +977,8 @@ struct tTreeSort { /* alphabetical comparator, trying to put objects first */ static int treesort_alpha_ob(const void *v1, const void *v2) { - const tTreeSort *x1 = reinterpret_cast<const tTreeSort *>(v1); - const tTreeSort *x2 = reinterpret_cast<const tTreeSort *>(v2); + const tTreeSort *x1 = static_cast<const tTreeSort *>(v1); + const tTreeSort *x2 = static_cast<const tTreeSort *>(v2); /* first put objects last (hierarchy) */ int comp = (x1->idcode == ID_OB); @@ -1019,8 +1016,8 @@ static int treesort_alpha_ob(const void *v1, const void *v2) /* Move children that are not in the collection to the end of the list. */ static int treesort_child_not_in_collection(const void *v1, const void *v2) { - const tTreeSort *x1 = reinterpret_cast<const tTreeSort *>(v1); - const tTreeSort *x2 = reinterpret_cast<const tTreeSort *>(v2); + const tTreeSort *x1 = static_cast<const tTreeSort *>(v1); + const tTreeSort *x2 = static_cast<const tTreeSort *>(v2); /* Among objects first come the ones in the collection, followed by the ones not on it. * This way we can have the dashed lines in a separate style connecting the former. */ @@ -1033,8 +1030,8 @@ static int treesort_child_not_in_collection(const void *v1, const void *v2) /* alphabetical comparator */ static int treesort_alpha(const void *v1, const void *v2) { - const tTreeSort *x1 = reinterpret_cast<const tTreeSort *>(v1); - const tTreeSort *x2 = reinterpret_cast<const tTreeSort *>(v2); + const tTreeSort *x1 = static_cast<const tTreeSort *>(v1); + const tTreeSort *x2 = static_cast<const tTreeSort *>(v2); int comp = BLI_strcasecmp_natural(x1->name, x2->name); @@ -1091,7 +1088,7 @@ static int treesort_obtype_alpha(const void *v1, const void *v2) /* sort happens on each subtree individual */ static void outliner_sort(ListBase *lb) { - TreeElement *last_te = reinterpret_cast<TreeElement *>(lb->last); + TreeElement *last_te = static_cast<TreeElement *>(lb->last); if (last_te == nullptr) { return; } @@ -1103,7 +1100,7 @@ static void outliner_sort(ListBase *lb) int totelem = BLI_listbase_count(lb); if (totelem > 1) { - tTreeSort *tear = reinterpret_cast<tTreeSort *>( + tTreeSort *tear = static_cast<tTreeSort *>( MEM_mallocN(totelem * sizeof(tTreeSort), "tree sort array")); tTreeSort *tp = tear; int skip = 0; @@ -1159,7 +1156,7 @@ static void outliner_sort(ListBase *lb) static void outliner_collections_children_sort(ListBase *lb) { - TreeElement *last_te = reinterpret_cast<TreeElement *>(lb->last); + TreeElement *last_te = static_cast<TreeElement *>(lb->last); if (last_te == nullptr) { return; } @@ -1170,7 +1167,7 @@ static void outliner_collections_children_sort(ListBase *lb) int totelem = BLI_listbase_count(lb); if (totelem > 1) { - tTreeSort *tear = reinterpret_cast<tTreeSort *>( + tTreeSort *tear = static_cast<tTreeSort *>( MEM_mallocN(totelem * sizeof(tTreeSort), "tree sort array")); tTreeSort *tp = tear; @@ -1478,7 +1475,7 @@ static bool outliner_element_visible_get(ViewLayer *view_layer, } else { BLI_assert(exclude_filter & SO_FILTER_OB_STATE_ACTIVE); - if (base != BASACT(view_layer)) { + if (base != view_layer->basact) { is_visible = false; } } @@ -1541,8 +1538,7 @@ static TreeElement *outliner_extract_children_from_subtree(TreeElement *element, if (outliner_element_is_collection_or_object(element)) { TreeElement *te_prev = nullptr; - for (TreeElement *te = reinterpret_cast<TreeElement *>(element->subtree.last); te; - te = te_prev) { + for (TreeElement *te = static_cast<TreeElement *>(element->subtree.last); te; te = te_prev) { te_prev = te->prev; if (!outliner_element_is_collection_or_object(te)) { @@ -1569,7 +1565,7 @@ static int outliner_filter_subtree(SpaceOutliner *space_outliner, TreeElement *te, *te_next; TreeStoreElem *tselem; - for (te = reinterpret_cast<TreeElement *>(lb->first); te; te = te_next) { + for (te = static_cast<TreeElement *>(lb->first); te; te = te_next) { te_next = te->next; if ((outliner_element_visible_get(view_layer, te, exclude_filter) == false)) { /* Don't free the tree, but extract the children from the parent and add to this tree. */ @@ -1675,10 +1671,9 @@ void outliner_build_tree(Main *mainvar, space_outliner->search_flags &= ~SO_SEARCH_RECURSIVE; } - if (space_outliner->runtime->treehash && (space_outliner->storeflag & SO_TREESTORE_REBUILD) && + if (space_outliner->runtime->tree_hash && (space_outliner->storeflag & SO_TREESTORE_REBUILD) && space_outliner->treestore) { - BKE_outliner_treehash_rebuild_from_treestore(space_outliner->runtime->treehash, - space_outliner->treestore); + space_outliner->runtime->tree_hash->rebuild_from_treestore(*space_outliner->treestore); } space_outliner->storeflag &= ~SO_TREESTORE_REBUILD; @@ -1689,6 +1684,10 @@ void outliner_build_tree(Main *mainvar, return; } + /* Enable for benchmarking. Starts a timer, results will be printed on function exit. */ + // SCOPED_TIMER("Outliner Rebuild"); + // SCOPED_TIMER_AVERAGED("Outliner Rebuild"); + OutlinerTreeElementFocus focus; outliner_store_scrolling_position(space_outliner, region, &focus); @@ -1724,3 +1723,5 @@ void outliner_build_tree(Main *mainvar, } /** \} */ + +} // namespace blender::ed::outliner diff --git a/source/blender/editors/space_outliner/outliner_utils.cc b/source/blender/editors/space_outliner/outliner_utils.cc index 0db612ce6db..ff5292e2883 100644 --- a/source/blender/editors/space_outliner/outliner_utils.cc +++ b/source/blender/editors/space_outliner/outliner_utils.cc @@ -18,7 +18,7 @@ #include "BKE_context.h" #include "BKE_layer.h" #include "BKE_object.h" -#include "BKE_outliner_treehash.h" +#include "BKE_outliner_treehash.hh" #include "ED_outliner.h" #include "ED_screen.h" @@ -27,9 +27,10 @@ #include "UI_view2d.h" #include "outliner_intern.hh" +#include "tree/tree_display.hh" #include "tree/tree_iterator.hh" -using namespace blender::ed::outliner; +namespace blender::ed::outliner { /* -------------------------------------------------------------------- */ /** \name Tree View Context @@ -44,7 +45,7 @@ void outliner_viewcontext_init(const bContext *C, TreeViewContext *tvc) tvc->view_layer = CTX_data_view_layer(C); /* Objects. */ - tvc->obact = OBACT(tvc->view_layer); + tvc->obact = BKE_view_layer_active_object_get(tvc->view_layer); if (tvc->obact != nullptr) { tvc->ob_edit = OBEDIT_FROM_OBACT(tvc->obact); @@ -98,7 +99,7 @@ static TreeElement *outliner_find_item_at_x_in_row_recursive(const TreeElement * float view_co_x, bool *r_is_merged_icon) { - TreeElement *child_te = reinterpret_cast<TreeElement *>(parent_te->subtree.first); + TreeElement *child_te = static_cast<TreeElement *>(parent_te->subtree.first); while (child_te) { const bool over_element = (view_co_x > child_te->xs) && (view_co_x < child_te->xend); @@ -175,24 +176,6 @@ TreeElement *outliner_find_parent_element(ListBase *lb, return nullptr; } -TreeElement *outliner_find_tse(SpaceOutliner *space_outliner, const TreeStoreElem *tse) -{ - TreeStoreElem *tselem; - - if (tse->id == nullptr) { - return nullptr; - } - - /* Check if 'tse' is in tree-store. */ - tselem = BKE_outliner_treehash_lookup_any( - space_outliner->runtime->treehash, tse->type, tse->nr, tse->id); - if (tselem) { - return outliner_find_tree_element(&space_outliner->tree, tselem); - } - - return nullptr; -} - TreeElement *outliner_find_id(SpaceOutliner *space_outliner, ListBase *lb, const ID *id) { LISTBASE_FOREACH (TreeElement *, te, lb) { @@ -282,8 +265,7 @@ bool outliner_tree_traverse(const SpaceOutliner *space_outliner, TreeTraversalFunc func, void *customdata) { - for (TreeElement *te = reinterpret_cast<TreeElement *>(tree->first), *te_next; te; - te = te_next) { + for (TreeElement *te = static_cast<TreeElement *>(tree->first), *te_next; te; te = te_next) { TreeTraversalAction func_retval = TRAVERSE_CONTINUE; /* in case te is freed in callback */ TreeStoreElem *tselem = TREESTORE(te); @@ -455,7 +437,7 @@ void outliner_tag_redraw_avoid_rebuild_on_open_change(const SpaceOutliner *space ARegion *region) { /* Avoid rebuild if possible. */ - if (outliner_requires_rebuild_on_open_change(space_outliner)) { + if (space_outliner->runtime->tree_display->is_lazy_built()) { ED_region_tag_redraw(region); } else { @@ -463,6 +445,10 @@ void outliner_tag_redraw_avoid_rebuild_on_open_change(const SpaceOutliner *space } } +} // namespace blender::ed::outliner + +using namespace blender::ed::outliner; + Base *ED_outliner_give_base_under_cursor(bContext *C, const int mval[2]) { ARegion *region = CTX_wm_region(C); diff --git a/source/blender/editors/space_outliner/space_outliner.cc b/source/blender/editors/space_outliner/space_outliner.cc index 5bcd1edebc0..76b7197b86a 100644 --- a/source/blender/editors/space_outliner/space_outliner.cc +++ b/source/blender/editors/space_outliner/space_outliner.cc @@ -16,7 +16,7 @@ #include "BKE_context.h" #include "BKE_lib_remap.h" -#include "BKE_outliner_treehash.h" +#include "BKE_outliner_treehash.hh" #include "BKE_screen.h" #include "ED_screen.h" @@ -37,16 +37,11 @@ #include "outliner_intern.hh" #include "tree/tree_display.hh" -SpaceOutliner_Runtime::SpaceOutliner_Runtime(const SpaceOutliner_Runtime & /*other*/) - : tree_display(nullptr), treehash(nullptr) -{ -} +namespace blender::ed::outliner { -SpaceOutliner_Runtime::~SpaceOutliner_Runtime() +SpaceOutliner_Runtime::SpaceOutliner_Runtime(const SpaceOutliner_Runtime & /*other*/) + : tree_display(nullptr), tree_hash(nullptr) { - if (treehash) { - BKE_outliner_treehash_free(treehash); - } } static void outliner_main_region_init(wmWindowManager *wm, ARegion *region) @@ -100,8 +95,8 @@ static void outliner_main_region_listener(const wmRegionListenerParams *params) { ScrArea *area = params->area; ARegion *region = params->region; - wmNotifier *wmn = params->notifier; - SpaceOutliner *space_outliner = reinterpret_cast<SpaceOutliner *>(area->spacedata.first); + const wmNotifier *wmn = params->notifier; + SpaceOutliner *space_outliner = static_cast<SpaceOutliner *>(area->spacedata.first); /* context changes */ switch (wmn->category) { @@ -191,7 +186,7 @@ static void outliner_main_region_listener(const wmRegionListenerParams *params) } break; case NC_ID: - if (ELEM(wmn->action, NA_RENAME, NA_ADDED)) { + if (ELEM(wmn->action, NA_RENAME, NA_ADDED, NA_REMOVED)) { ED_region_tag_redraw(region); } break; @@ -264,7 +259,7 @@ static void outliner_main_region_message_subscribe(const wmRegionMessageSubscrib struct wmMsgBus *mbus = params->message_bus; ScrArea *area = params->area; ARegion *region = params->region; - SpaceOutliner *space_outliner = reinterpret_cast<SpaceOutliner *>(area->spacedata.first); + SpaceOutliner *space_outliner = static_cast<SpaceOutliner *>(area->spacedata.first); wmMsgSubscribeValue msg_sub_value_region_tag_redraw{}; msg_sub_value_region_tag_redraw.owner = region; @@ -296,7 +291,7 @@ static void outliner_header_region_free(ARegion *UNUSED(region)) static void outliner_header_region_listener(const wmRegionListenerParams *params) { ARegion *region = params->region; - wmNotifier *wmn = params->notifier; + const wmNotifier *wmn = params->notifier; /* context changes */ switch (wmn->category) { @@ -361,7 +356,7 @@ static void outliner_free(SpaceLink *sl) /* spacetype; init callback */ static void outliner_init(wmWindowManager *UNUSED(wm), ScrArea *area) { - SpaceOutliner *space_outliner = reinterpret_cast<SpaceOutliner *>(area->spacedata.first); + SpaceOutliner *space_outliner = static_cast<SpaceOutliner *>(area->spacedata.first); if (space_outliner->runtime == nullptr) { space_outliner->runtime = MEM_new<SpaceOutliner_Runtime>("SpaceOutliner_Runtime"); @@ -391,8 +386,6 @@ static void outliner_id_remap(ScrArea *area, SpaceLink *slink, const struct IDRe { SpaceOutliner *space_outliner = (SpaceOutliner *)slink; - BKE_id_remapper_apply(mappings, (ID **)&space_outliner->search_tse.id, ID_REMAP_APPLY_DEFAULT); - if (!space_outliner->treestore) { return; } @@ -420,7 +413,7 @@ static void outliner_id_remap(ScrArea *area, SpaceLink *slink, const struct IDRe /* Note that the Outliner may not be the active editor of the area, and hence not initialized. * So runtime data might not have been created yet. */ - if (space_outliner->runtime && space_outliner->runtime->treehash && changed) { + if (space_outliner->runtime && space_outliner->runtime->tree_hash && changed) { /* rebuild hash table, because it depends on ids too */ /* postpone a full rebuild because this can be called many times on-free */ space_outliner->storeflag |= SO_TREESTORE_REBUILD; @@ -437,13 +430,17 @@ static void outliner_id_remap(ScrArea *area, SpaceLink *slink, const struct IDRe static void outliner_deactivate(struct ScrArea *area) { /* Remove hover highlights */ - SpaceOutliner *space_outliner = reinterpret_cast<SpaceOutliner *>(area->spacedata.first); + SpaceOutliner *space_outliner = static_cast<SpaceOutliner *>(area->spacedata.first); outliner_flag_set(*space_outliner, TSE_HIGHLIGHTED_ANY, false); ED_region_tag_redraw_no_rebuild(BKE_area_find_region_type(area, RGN_TYPE_WINDOW)); } +} // namespace blender::ed::outliner + void ED_spacetype_outliner(void) { + using namespace blender::ed::outliner; + SpaceType *st = MEM_cnew<SpaceType>("spacetype time"); ARegionType *art; diff --git a/source/blender/editors/space_outliner/tree/common.cc b/source/blender/editors/space_outliner/tree/common.cc index 349d36e2fe6..199c80f021a 100644 --- a/source/blender/editors/space_outliner/tree/common.cc +++ b/source/blender/editors/space_outliner/tree/common.cc @@ -21,6 +21,8 @@ #include "common.hh" #include "tree_display.hh" +namespace blender::ed::outliner { + /* -------------------------------------------------------------------- */ /** \name ID Helpers. * \{ */ @@ -38,7 +40,7 @@ void outliner_make_object_parent_hierarchy(ListBase *lb) { /* build hierarchy */ /* XXX also, set extents here... */ - TreeElement *te = reinterpret_cast<TreeElement *>(lb->first); + TreeElement *te = static_cast<TreeElement *>(lb->first); while (te) { TreeElement *ten = te->next; TreeStoreElem *tselem = TREESTORE(te); @@ -63,3 +65,5 @@ bool outliner_animdata_test(const AnimData *adt) } return false; } + +} // namespace blender::ed::outliner diff --git a/source/blender/editors/space_outliner/tree/common.hh b/source/blender/editors/space_outliner/tree/common.hh index 96c1eb34354..ba2d1c3fab6 100644 --- a/source/blender/editors/space_outliner/tree/common.hh +++ b/source/blender/editors/space_outliner/tree/common.hh @@ -8,7 +8,11 @@ struct ListBase; +namespace blender::ed::outliner { + const char *outliner_idcode_to_plural(short idcode); void outliner_make_object_parent_hierarchy(ListBase *lb); bool outliner_animdata_test(const struct AnimData *adt); + +} // namespace blender::ed::outliner diff --git a/source/blender/editors/space_outliner/tree/tree_display.cc b/source/blender/editors/space_outliner/tree/tree_display.cc index 6ab497b3fbb..fe4937829d6 100644 --- a/source/blender/editors/space_outliner/tree/tree_display.cc +++ b/source/blender/editors/space_outliner/tree/tree_display.cc @@ -50,4 +50,9 @@ bool AbstractTreeDisplay::supportsModeColumn() const return false; } +bool AbstractTreeDisplay::is_lazy_built() const +{ + return false; +} + } // namespace blender::ed::outliner diff --git a/source/blender/editors/space_outliner/tree/tree_display.hh b/source/blender/editors/space_outliner/tree/tree_display.hh index 190e35c81d6..295eeb59eaa 100644 --- a/source/blender/editors/space_outliner/tree/tree_display.hh +++ b/source/blender/editors/space_outliner/tree/tree_display.hh @@ -30,11 +30,11 @@ struct Main; struct Scene; struct Sequence; struct SpaceOutliner; -struct TreeElement; struct ViewLayer; namespace blender::ed::outliner { +struct TreeElement; class TreeElementID; /** @@ -84,6 +84,15 @@ class AbstractTreeDisplay { */ virtual bool supportsModeColumn() const; + /** + * Some trees may want to skip building children of collapsed parents. This should be done if the + * tree type may become very complex, which could cause noticeable slowdowns. + * Problem: This doesn't address performance issues while searching, since all elements are + * constructed for that. Trees of this type have to be rebuilt for any change to the collapsed + * state of any element. + */ + virtual bool is_lazy_built() const; + protected: /** All derived classes will need a handle to this, so storing it in the base for convenience. */ SpaceOutliner &space_outliner_; @@ -157,11 +166,12 @@ class TreeDisplayOverrideLibraryHierarchies final : public AbstractTreeDisplay { ListBase buildTree(const TreeSourceData &source_data) override; + bool is_lazy_built() const override; + private: ListBase build_hierarchy_for_lib_or_main(Main *bmain, TreeElement &parent_te, Library *lib = nullptr); - void build_hierarchy_for_ID(Main *bmain, ID &override_root_id, TreeElementID &te_id) const; }; /* -------------------------------------------------------------------- */ @@ -233,6 +243,8 @@ class TreeDisplayDataAPI final : public AbstractTreeDisplay { TreeDisplayDataAPI(SpaceOutliner &space_outliner); ListBase buildTree(const TreeSourceData &source_data) override; + + bool is_lazy_built() const override; }; } // namespace blender::ed::outliner diff --git a/source/blender/editors/space_outliner/tree/tree_display_data.cc b/source/blender/editors/space_outliner/tree/tree_display_data.cc index bfeb8ce2bdc..3d9b927fbf1 100644 --- a/source/blender/editors/space_outliner/tree/tree_display_data.cc +++ b/source/blender/editors/space_outliner/tree/tree_display_data.cc @@ -42,4 +42,9 @@ ListBase TreeDisplayDataAPI::buildTree(const TreeSourceData &source_data) return tree; } +bool TreeDisplayDataAPI::is_lazy_built() const +{ + return true; +} + } // namespace blender::ed::outliner diff --git a/source/blender/editors/space_outliner/tree/tree_display_override_library_hierarchies.cc b/source/blender/editors/space_outliner/tree/tree_display_override_library_hierarchies.cc index 67798e978ab..fa4479d0d9d 100644 --- a/source/blender/editors/space_outliner/tree/tree_display_override_library_hierarchies.cc +++ b/source/blender/editors/space_outliner/tree/tree_display_override_library_hierarchies.cc @@ -4,25 +4,24 @@ * \ingroup spoutliner */ -#include "DNA_ID.h" -#include "DNA_collection_types.h" #include "DNA_key_types.h" #include "DNA_space_types.h" -#include "BLI_listbase.h" +#include "BLI_function_ref.hh" +#include "BLI_ghash.h" #include "BLI_map.hh" + #include "BLI_set.hh" #include "BLT_translation.h" -#include "BKE_collection.h" +#include "BKE_lib_override.h" #include "BKE_lib_query.h" #include "BKE_main.h" #include "../outliner_intern.hh" #include "common.hh" #include "tree_display.hh" -#include "tree_element_id.hh" namespace blender::ed::outliner { @@ -42,8 +41,8 @@ ListBase TreeDisplayOverrideLibraryHierarchies::buildTree(const TreeSourceData & TreeElement *current_file_te = outliner_add_element( &space_outliner_, &tree, source_data.bmain, nullptr, TSE_ID_BASE, -1); current_file_te->name = IFACE_("Current File"); + AbstractTreeElement::uncollapse_by_default(current_file_te); { - AbstractTreeElement::uncollapse_by_default(current_file_te); build_hierarchy_for_lib_or_main(source_data.bmain, *current_file_te); /* Add dummy child if there's nothing to display. */ @@ -76,11 +75,57 @@ ListBase TreeDisplayOverrideLibraryHierarchies::buildTree(const TreeSourceData & return tree; } +bool TreeDisplayOverrideLibraryHierarchies::is_lazy_built() const +{ + return true; +} + +/* -------------------------------------------------------------------- */ +/** \name Library override hierarchy building + * \{ */ + +class OverrideIDHierarchyBuilder { + SpaceOutliner &space_outliner_; + Main &bmain_; + MainIDRelations &id_relations_; + + struct HierarchyBuildData { + const ID &override_root_id_; + /* The ancestor IDs leading to the current ID, to avoid IDs recursing into themselves. Changes + * with every level of recursion. */ + Set<const ID *> parent_ids{}; + /* The IDs that were already added to #parent_te, to avoid duplicates. Entirely new set with + * every level of recursion. */ + Set<const ID *> sibling_ids{}; + }; + + public: + OverrideIDHierarchyBuilder(SpaceOutliner &space_outliner, + Main &bmain, + MainIDRelations &id_relations) + : space_outliner_(space_outliner), bmain_(bmain), id_relations_(id_relations) + { + } + + void build_hierarchy_for_ID(ID &root_id, TreeElement &te_to_expand); + + private: + void build_hierarchy_for_ID_recursive(const ID &parent_id, + HierarchyBuildData &build_data, + TreeElement &te_to_expand); +}; + ListBase TreeDisplayOverrideLibraryHierarchies::build_hierarchy_for_lib_or_main( Main *bmain, TreeElement &parent_te, Library *lib) { ListBase tree = {nullptr}; + /* Ensure #Main.relations contains the latest mapping of relations. Must be freed before + * returning. */ + BKE_main_relations_create(bmain, 0); + + OverrideIDHierarchyBuilder builder(space_outliner_, *bmain, *bmain->relations); + /* Keep track over which ID base elements were already added, and expand them once added. */ Map<ID_Type, TreeElement *> id_base_te_map; /* Index for the ID base elements ("Objects", "Materials", etc). */ @@ -109,108 +154,194 @@ ListBase TreeDisplayOverrideLibraryHierarchies::build_hierarchy_for_lib_or_main( TreeElement *new_id_te = outliner_add_element( &space_outliner_, &new_base_te->subtree, iter_id, new_base_te, TSE_SOME_ID, 0, false); - build_hierarchy_for_ID(bmain, *iter_id, *tree_element_cast<TreeElementID>(new_id_te)); + builder.build_hierarchy_for_ID(*iter_id, *new_id_te); } FOREACH_MAIN_ID_END; + BKE_main_relations_free(bmain); + return tree; } -struct BuildHierarchyForeachIDCbData { - /* Don't allow copies, the sets below would need deep copying. */ - BuildHierarchyForeachIDCbData(const BuildHierarchyForeachIDCbData &) = delete; - - Main &bmain; - SpaceOutliner &space_outliner; - ID &override_root_id; - - /* The tree element to expand. Changes with every level of recursion. */ - TreeElementID *parent_te; - /* The ancestor IDs leading to the current ID, to avoid IDs recursing into themselves. Changes - * with every level of recursion. */ - Set<ID *> parent_ids{}; - /* The IDs that were already added to #parent_te, to avoid duplicates. Entirely new set with - * every level of recursion. */ - Set<ID *> sibling_ids{}; +void OverrideIDHierarchyBuilder::build_hierarchy_for_ID(ID &override_root_id, + TreeElement &te_to_expand) +{ + HierarchyBuildData build_data{override_root_id}; + build_hierarchy_for_ID_recursive(override_root_id, build_data, te_to_expand); +} + +enum ForeachChildReturn { + FOREACH_CONTINUE, + FOREACH_BREAK, }; +/* Helpers (defined below). */ +static void foreach_natural_hierarchy_child(const MainIDRelations &id_relations, + const ID &parent_id, + FunctionRef<ForeachChildReturn(ID &)> fn); +static bool id_is_in_override_hierarchy(const Main &bmain, + const ID &id, + const ID &relationship_parent_id, + const ID &override_root_id); + +void OverrideIDHierarchyBuilder::build_hierarchy_for_ID_recursive(const ID &parent_id, + HierarchyBuildData &build_data, + TreeElement &te_to_expand) +{ + /* In case this isn't added to the parents yet (does nothing if already there). */ + build_data.parent_ids.add(&parent_id); + + foreach_natural_hierarchy_child(id_relations_, parent_id, [&](ID &id) { + /* Some IDs can use themselves, early abort. */ + if (&id == &parent_id) { + return FOREACH_CONTINUE; + } + if (!id_is_in_override_hierarchy(bmain_, id, parent_id, build_data.override_root_id_)) { + return FOREACH_CONTINUE; + } + + /* Avoid endless recursion: If there is an ancestor for this ID already, it recurses into + * itself. */ + if (build_data.parent_ids.lookup_key_default(&id, nullptr)) { + return FOREACH_CONTINUE; + } + + /* Avoid duplicates: If there is a sibling for this ID already, the same ID is just used + * multiple times by the same parent. */ + if (build_data.sibling_ids.lookup_key_default(&id, nullptr)) { + return FOREACH_CONTINUE; + } + + /* We only want to add children whose parent isn't collapsed. Otherwise, in complex scenes with + * thousands of relationships, the building can slow down tremendously. Tag the parent to allow + * un-collapsing, but don't actually add the children. */ + if (!TSELEM_OPEN(TREESTORE(&te_to_expand), &space_outliner_)) { + te_to_expand.flag |= TE_PRETEND_HAS_CHILDREN; + return FOREACH_BREAK; + } + + TreeElement *new_te = outliner_add_element( + &space_outliner_, &te_to_expand.subtree, &id, &te_to_expand, TSE_SOME_ID, 0, false); + + build_data.sibling_ids.add(&id); + + /* Recurse into this ID. */ + HierarchyBuildData child_build_data{build_data.override_root_id_}; + child_build_data.parent_ids = build_data.parent_ids; + child_build_data.parent_ids.add(&id); + child_build_data.sibling_ids.reserve(10); + build_hierarchy_for_ID_recursive(id, child_build_data, *new_te); -static int build_hierarchy_foreach_ID_cb(LibraryIDLinkCallbackData *cb_data) + return FOREACH_CONTINUE; + }); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Helpers for library override hierarchy building + * \{ */ + +/** + * Iterate over the IDs \a parent_id uses. E.g. the child collections and contained objects of a + * parent collection. Also does special handling for object parenting, so that: + * - When iterating over a child object, \a fn is executed for the parent instead. + * - When iterating over a parent object, \a fn is _additionally_ executed for all children. Given + * that the parent object isn't skipped, the caller has to ensure it's not added in the hierarchy + * twice. + * This allows us to build the hierarchy in the expected ("natural") order, where parent objects + * are actual parent elements in the hierarchy, even though in data, the relation goes the other + * way around (children point to or "use" the parent). + * + * Only handles regular object parenting, not cases like the "Child of" constraint. Other Outliner + * display modes don't show this as parent in the hierarchy either. + */ +static void foreach_natural_hierarchy_child(const MainIDRelations &id_relations, + const ID &parent_id, + FunctionRef<ForeachChildReturn(ID &)> fn) { - if (!*cb_data->id_pointer) { - return IDWALK_RET_NOP; + const MainIDRelationsEntry *relations_of_id = static_cast<MainIDRelationsEntry *>( + BLI_ghash_lookup(id_relations.relations_from_pointers, &parent_id)); + + /* Iterate over all IDs used by the parent ID (e.g. the child-collections of a collection). */ + for (MainIDRelationsEntryItem *to_id_entry = relations_of_id->to_ids; to_id_entry; + to_id_entry = to_id_entry->next) { + /* An ID pointed to (used) by the ID to recurse into. */ + ID &target_id = **to_id_entry->id_pointer.to; + + /* Don't walk up the hierarchy, e.g. ignore pointers to parent collections. */ + if (to_id_entry->usage_flag & IDWALK_CB_LOOPBACK) { + continue; + } + + /* Special case for objects: Process the parent object instead of the child object. Below the + * parent will add the child objects then. */ + if (GS(target_id.name) == ID_OB) { + const Object &potential_child_ob = reinterpret_cast<const Object &>(target_id); + if (potential_child_ob.parent) { + if (fn(potential_child_ob.parent->id) == FOREACH_BREAK) { + return; + } + continue; + } + } + + if (fn(target_id) == FOREACH_BREAK) { + return; + } + } + + /* If the ID is an object, find and iterate over any child objects. */ + if (GS(parent_id.name) == ID_OB) { + for (MainIDRelationsEntryItem *from_id_entry = relations_of_id->from_ids; from_id_entry; + from_id_entry = from_id_entry->next) { + ID &potential_child_id = *from_id_entry->id_pointer.from; + + if (GS(potential_child_id.name) != ID_OB) { + continue; + } + + const Object &potential_child_ob = reinterpret_cast<Object &>(potential_child_id); + if (!potential_child_ob.parent || &potential_child_ob.parent->id != &parent_id) { + continue; + } + + if (fn(potential_child_id) == FOREACH_BREAK) { + return; + } + } } +} - BuildHierarchyForeachIDCbData &build_data = *reinterpret_cast<BuildHierarchyForeachIDCbData *>( - cb_data->user_data); - /* Note that this may be an embedded ID (see #real_override_id). */ - ID &id = **cb_data->id_pointer; +static bool id_is_in_override_hierarchy(const Main &bmain, + const ID &id, + const ID &relationship_parent_id, + const ID &override_root_id) +{ /* If #id is an embedded ID, this will be set to the owner, which is a real ID and contains the * override data. So queries of override data should be done via this, but the actual tree * element we add is the embedded ID. */ const ID *real_override_id = &id; if (ID_IS_OVERRIDE_LIBRARY_VIRTUAL(&id)) { - if (GS(id.name) == ID_KE) { - Key *key = (Key *)&id; - real_override_id = key->from; - } - else if (id.flag & LIB_EMBEDDED_DATA) { - /* TODO Needs double-checking if this handles all embedded IDs correctly. */ - real_override_id = cb_data->id_owner; - } + /* In many cases, `relationship_parent_id` is the owner, but not always (e.g. there can be + * drivers directly between an object and a shapekey). */ + BKE_lib_override_library_get(const_cast<Main *>(&bmain), + const_cast<ID *>(&id), + const_cast<ID *>(&relationship_parent_id), + const_cast<ID **>(&real_override_id)); } if (!ID_IS_OVERRIDE_LIBRARY(real_override_id)) { - return IDWALK_RET_NOP; + return false; } /* Is this ID part of the same override hierarchy? */ - if (real_override_id->override_library->hierarchy_root != &build_data.override_root_id) { - return IDWALK_RET_NOP; + if (real_override_id->override_library->hierarchy_root != &override_root_id) { + return false; } - /* Avoid endless recursion: If there is an ancestor for this ID already, it recurses into itself. - */ - if (build_data.parent_ids.lookup_key_default(&id, nullptr)) { - return IDWALK_RET_NOP; - } - - /* Avoid duplicates: If there is a sibling for this ID already, the same ID is just used multiple - * times by the same parent. */ - if (build_data.sibling_ids.lookup_key_default(&id, nullptr)) { - return IDWALK_RET_NOP; - } - - TreeElement *new_te = outliner_add_element(&build_data.space_outliner, - &build_data.parent_te->getLegacyElement().subtree, - &id, - &build_data.parent_te->getLegacyElement(), - TSE_SOME_ID, - 0, - false); - build_data.sibling_ids.add(&id); - - BuildHierarchyForeachIDCbData child_build_data{build_data.bmain, - build_data.space_outliner, - build_data.override_root_id, - tree_element_cast<TreeElementID>(new_te)}; - child_build_data.parent_ids = build_data.parent_ids; - child_build_data.parent_ids.add(&id); - child_build_data.sibling_ids.reserve(10); - BKE_library_foreach_ID_link( - &build_data.bmain, &id, build_hierarchy_foreach_ID_cb, &child_build_data, IDWALK_READONLY); - - return IDWALK_RET_NOP; + return true; } -void TreeDisplayOverrideLibraryHierarchies::build_hierarchy_for_ID(Main *bmain, - ID &override_root_id, - TreeElementID &te_id) const -{ - BuildHierarchyForeachIDCbData build_data{*bmain, space_outliner_, override_root_id, &te_id}; - build_data.parent_ids.add(&override_root_id); - - BKE_library_foreach_ID_link( - bmain, &te_id.get_ID(), build_hierarchy_foreach_ID_cb, &build_data, IDWALK_READONLY); -} +/** \} */ } // namespace blender::ed::outliner diff --git a/source/blender/editors/space_outliner/tree/tree_element.cc b/source/blender/editors/space_outliner/tree/tree_element.cc index 94d55b70e3c..4a540c3ce87 100644 --- a/source/blender/editors/space_outliner/tree/tree_element.cc +++ b/source/blender/editors/space_outliner/tree/tree_element.cc @@ -4,6 +4,9 @@ * \ingroup spoutliner */ +#include <string> +#include <string_view> + #include "DNA_anim_types.h" #include "DNA_listBase.h" #include "DNA_space_types.h" @@ -17,6 +20,7 @@ #include "tree_element_driver.hh" #include "tree_element_gpencil_layer.hh" #include "tree_element_id.hh" +#include "tree_element_label.hh" #include "tree_element_nla.hh" #include "tree_element_overrides.hh" #include "tree_element_rna.hh" @@ -52,9 +56,11 @@ std::unique_ptr<AbstractTreeElement> AbstractTreeElement::createFromType(const i switch (type) { case TSE_SOME_ID: return TreeElementID::createFromID(legacy_te, *static_cast<ID *>(idv)); + case TSE_GENERIC_LABEL: + return std::make_unique<TreeElementLabel>(legacy_te, static_cast<const char *>(idv)); case TSE_ANIM_DATA: return std::make_unique<TreeElementAnimData>(legacy_te, - *reinterpret_cast<IdAdtTemplate *>(idv)->adt); + *static_cast<IdAdtTemplate *>(idv)->adt); case TSE_DRIVER_BASE: return std::make_unique<TreeElementDriverBase>(legacy_te, *static_cast<AnimData *>(idv)); case TSE_NLA: @@ -76,23 +82,24 @@ std::unique_ptr<AbstractTreeElement> AbstractTreeElement::createFromType(const i case TSE_LIBRARY_OVERRIDE: return std::make_unique<TreeElementOverridesProperty>( legacy_te, *static_cast<TreeElementOverridesData *>(idv)); + case TSE_LIBRARY_OVERRIDE_OPERATION: + return std::make_unique<TreeElementOverridesPropertyOperation>( + legacy_te, *static_cast<TreeElementOverridesData *>(idv)); case TSE_RNA_STRUCT: - return std::make_unique<TreeElementRNAStruct>(legacy_te, - *reinterpret_cast<PointerRNA *>(idv)); + return std::make_unique<TreeElementRNAStruct>(legacy_te, *static_cast<PointerRNA *>(idv)); case TSE_RNA_PROPERTY: return std::make_unique<TreeElementRNAProperty>( - legacy_te, *reinterpret_cast<PointerRNA *>(idv), legacy_te.index); + legacy_te, *static_cast<PointerRNA *>(idv), legacy_te.index); case TSE_RNA_ARRAY_ELEM: return std::make_unique<TreeElementRNAArrayElement>( - legacy_te, *reinterpret_cast<PointerRNA *>(idv), legacy_te.index); + legacy_te, *static_cast<PointerRNA *>(idv), legacy_te.index); case TSE_SEQUENCE: - return std::make_unique<TreeElementSequence>(legacy_te, *reinterpret_cast<Sequence *>(idv)); + return std::make_unique<TreeElementSequence>(legacy_te, *static_cast<Sequence *>(idv)); case TSE_SEQ_STRIP: - return std::make_unique<TreeElementSequenceStrip>(legacy_te, - *reinterpret_cast<Strip *>(idv)); + return std::make_unique<TreeElementSequenceStrip>(legacy_te, *static_cast<Strip *>(idv)); case TSE_SEQUENCE_DUP: - return std::make_unique<TreeElementSequenceStripDuplicate>( - legacy_te, *reinterpret_cast<Sequence *>(idv)); + return std::make_unique<TreeElementSequenceStripDuplicate>(legacy_te, + *static_cast<Sequence *>(idv)); default: break; } @@ -105,6 +112,22 @@ StringRefNull AbstractTreeElement::getWarning() const return ""; } +std::optional<BIFIconID> AbstractTreeElement::getIcon() const +{ + return {}; +} + +void AbstractTreeElement::print_path() +{ + std::string path = legacy_te_.name; + + for (TreeElement *parent = legacy_te_.parent; parent; parent = parent->parent) { + path = parent->name + std::string_view("/") + path; + } + + std::cout << path << std::endl; +} + void AbstractTreeElement::uncollapse_by_default(TreeElement *legacy_te) { if (!TREESTORE(legacy_te)->used) { diff --git a/source/blender/editors/space_outliner/tree/tree_element.hh b/source/blender/editors/space_outliner/tree/tree_element.hh index 1098068d628..1b145a48daa 100644 --- a/source/blender/editors/space_outliner/tree/tree_element.hh +++ b/source/blender/editors/space_outliner/tree/tree_element.hh @@ -7,15 +7,18 @@ #pragma once #include <memory> +#include <optional> #include "BLI_string_ref.hh" +#include "UI_resources.h" struct ListBase; struct SpaceOutliner; -struct TreeElement; namespace blender::ed::outliner { +struct TreeElement; + /* -------------------------------------------------------------------- */ /* Tree-Display Interface */ @@ -64,6 +67,25 @@ class AbstractTreeElement { virtual StringRefNull getWarning() const; /** + * Define the icon to be displayed for this element. If this returns an icon, this will be + * displayed. Otherwise, #tree_element_get_icon() may still determine an icon. By default no + * value is returned (#std::nullopt). + * + * All elements should be ported to use this over #tree_element_get_icon(). + */ + virtual std::optional<BIFIconID> getIcon() const; + + /** + * Debugging helper: Print effective path of this tree element, constructed out of the + * #TreeElement.name of each element. E.g.: + * - Lorem + * - ipsum dolor sit + * - amet + * will print: Lorem/ipsum dolor sit/amet. + */ + void print_path(); + + /** * Expand this tree element if it is displayed for the first time (as identified by its * tree-store element). * diff --git a/source/blender/editors/space_outliner/tree/tree_element_anim_data.hh b/source/blender/editors/space_outliner/tree/tree_element_anim_data.hh index 956cf3dec48..f3372329dd1 100644 --- a/source/blender/editors/space_outliner/tree/tree_element_anim_data.hh +++ b/source/blender/editors/space_outliner/tree/tree_element_anim_data.hh @@ -8,8 +8,6 @@ #include "tree_element.hh" -struct TreeElement; - namespace blender::ed::outliner { class TreeElementAnimData final : public AbstractTreeElement { diff --git a/source/blender/editors/space_outliner/tree/tree_element_driver.hh b/source/blender/editors/space_outliner/tree/tree_element_driver.hh index 053217e18ec..f0213dd39f2 100644 --- a/source/blender/editors/space_outliner/tree/tree_element_driver.hh +++ b/source/blender/editors/space_outliner/tree/tree_element_driver.hh @@ -8,8 +8,6 @@ #include "tree_element.hh" -struct TreeElement; - namespace blender::ed::outliner { class TreeElementDriverBase final : public AbstractTreeElement { diff --git a/source/blender/editors/space_outliner/tree/tree_element_label.cc b/source/blender/editors/space_outliner/tree/tree_element_label.cc new file mode 100644 index 00000000000..32fa62c5f5e --- /dev/null +++ b/source/blender/editors/space_outliner/tree/tree_element_label.cc @@ -0,0 +1,36 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +/** \file + * \ingroup spoutliner + */ + +#include "DNA_listBase.h" + +#include "DNA_outliner_types.h" + +#include "../outliner_intern.hh" + +#include "tree_element_label.hh" + +namespace blender::ed::outliner { + +TreeElementLabel::TreeElementLabel(TreeElement &legacy_te, const char *label) + : AbstractTreeElement(legacy_te), label_(label) +{ + BLI_assert(legacy_te_.store_elem->type == TSE_GENERIC_LABEL); + /* The draw string is actually accessed via #TreeElement.name, so make sure this always points to + * our string. */ + legacy_te_.name = label_.c_str(); +} + +void TreeElementLabel::setIcon(const BIFIconID icon) +{ + icon_ = icon; +} + +std::optional<BIFIconID> TreeElementLabel::getIcon() const +{ + return icon_; +} + +} // namespace blender::ed::outliner diff --git a/source/blender/editors/space_outliner/tree/tree_element_label.hh b/source/blender/editors/space_outliner/tree/tree_element_label.hh new file mode 100644 index 00000000000..fc730c7b8f4 --- /dev/null +++ b/source/blender/editors/space_outliner/tree/tree_element_label.hh @@ -0,0 +1,36 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +/** \file + * \ingroup spoutliner + */ + +#pragma once + +#include <string> + +#include "UI_resources.h" + +#include "tree_element.hh" + +namespace blender::ed::outliner { + +/** + * A basic, general purpose tree element to just display a label and an icon. Can be used to group + * together items underneath as well of course. + * + * Make sure to give this a unique index, so the element can be identified uniquely. Otherwise + * glitches like multiple highlighted elements happen, that share all state (e.g. collapsed, + * selected, etc.). + */ +class TreeElementLabel final : public AbstractTreeElement { + const std::string label_; + BIFIconID icon_ = ICON_NONE; + + public: + TreeElementLabel(TreeElement &legacy_te, const char *label); + + void setIcon(BIFIconID icon); + std::optional<BIFIconID> getIcon() const override; +}; + +} // namespace blender::ed::outliner diff --git a/source/blender/editors/space_outliner/tree/tree_element_overrides.cc b/source/blender/editors/space_outliner/tree/tree_element_overrides.cc index 871de39b1dd..11067d37966 100644 --- a/source/blender/editors/space_outliner/tree/tree_element_overrides.cc +++ b/source/blender/editors/space_outliner/tree/tree_element_overrides.cc @@ -7,30 +7,60 @@ #include "BKE_collection.h" #include "BKE_lib_override.h" -#include "BLI_utildefines.h" - +#include "BLI_function_ref.hh" #include "BLI_listbase_wrapper.hh" +#include "BLI_map.hh" +#include "BLI_utildefines.h" #include "BLT_translation.h" #include "DNA_space_types.h" #include "RNA_access.h" +#include "RNA_path.h" #include "../outliner_intern.hh" +#include "tree_element_label.hh" #include "tree_element_overrides.hh" namespace blender::ed::outliner { +class OverrideRNAPathTreeBuilder { + SpaceOutliner &space_outliner_; + Map<std::string, TreeElement *> path_te_map; + + public: + OverrideRNAPathTreeBuilder(SpaceOutliner &space_outliner); + void build_path(TreeElement &parent, TreeElementOverridesData &override_data, short &index); + + private: + TreeElement &ensure_label_element_for_prop( + TreeElement &parent, StringRef elem_path, PointerRNA &ptr, PropertyRNA &prop, short &index); + TreeElement &ensure_label_element_for_ptr(TreeElement &parent, + StringRef elem_path, + PointerRNA &ptr, + short &index); + void ensure_entire_collection(TreeElement &te_to_expand, + const TreeElementOverridesData &override_data, + const char *coll_prop_path, + short &index); +}; + +/* -------------------------------------------------------------------- */ +/** \name Base Element + * + * Represents an ID that has overridden properties. The expanding will invoke building of tree + * elements for the full RNA path of the property. + * + * \{ */ + TreeElementOverridesBase::TreeElementOverridesBase(TreeElement &legacy_te, ID &id) : AbstractTreeElement(legacy_te), id(id) { BLI_assert(legacy_te.store_elem->type == TSE_LIBRARY_OVERRIDE_BASE); if (legacy_te.parent != nullptr && - ELEM(legacy_te.parent->store_elem->type, TSE_SOME_ID, TSE_LAYER_COLLECTION)) - - { + ELEM(legacy_te.parent->store_elem->type, TSE_SOME_ID, TSE_LAYER_COLLECTION)) { legacy_te.name = IFACE_("Library Overrides"); } else { @@ -51,21 +81,17 @@ StringRefNull TreeElementOverridesBase::getWarning() const return {}; } -void TreeElementOverridesBase::expand(SpaceOutliner &space_outliner) const +static void iterate_properties_to_display(ID &id, + const bool show_system_overrides, + FunctionRef<void(TreeElementOverridesData &data)> fn) { - BLI_assert(id.override_library != nullptr); + PointerRNA override_rna_ptr; + PropertyRNA *override_rna_prop; - const bool show_system_overrides = (SUPPORT_FILTER_OUTLINER(&space_outliner) && - (space_outliner.filter & SO_FILTER_SHOW_SYSTEM_OVERRIDES) != - 0); PointerRNA idpoin; RNA_id_pointer_create(&id, &idpoin); - PointerRNA override_rna_ptr; - PropertyRNA *override_rna_prop; - short index = 0; - - for (auto *override_prop : + for (IDOverrideLibraryProperty *override_prop : ListBaseWrapper<IDOverrideLibraryProperty>(id.override_library->properties)) { int rnaprop_index = 0; const bool is_rna_path_valid = BKE_lib_override_rna_property_find( @@ -80,15 +106,13 @@ void TreeElementOverridesBase::expand(SpaceOutliner &space_outliner) const /* Matching ID pointers are considered as system overrides. */ if (ELEM(override_prop->rna_prop_type, PROP_POINTER, PROP_COLLECTION) && RNA_struct_is_ID(RNA_property_pointer_type(&override_rna_ptr, override_rna_prop))) { - for (auto *override_prop_op : + for (IDOverrideLibraryPropertyOperation *override_prop_op : ListBaseWrapper<IDOverrideLibraryPropertyOperation>(override_prop->operations)) { if ((override_prop_op->flag & IDOVERRIDE_LIBRARY_FLAG_IDPOINTER_MATCH_REFERENCE) == 0) { do_skip = false; break; } - else { - is_system_override = true; - } + is_system_override = true; } } @@ -105,11 +129,36 @@ void TreeElementOverridesBase::expand(SpaceOutliner &space_outliner) const TreeElementOverridesData data = { id, *override_prop, override_rna_ptr, *override_rna_prop, is_rna_path_valid}; - outliner_add_element( - &space_outliner, &legacy_te_.subtree, &data, &legacy_te_, TSE_LIBRARY_OVERRIDE, index++); + + fn(data); } } +void TreeElementOverridesBase::expand(SpaceOutliner &space_outliner) const +{ + BLI_assert(id.override_library != nullptr); + + const bool show_system_overrides = (SUPPORT_FILTER_OUTLINER(&space_outliner) && + (space_outliner.filter & SO_FILTER_SHOW_SYSTEM_OVERRIDES) != + 0); + + OverrideRNAPathTreeBuilder path_builder(space_outliner); + short index = 0; + + iterate_properties_to_display(id, show_system_overrides, [&](TreeElementOverridesData &data) { + path_builder.build_path(legacy_te_, data, index); + }); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Overridden Property + * + * Represents an RNA property that was overridden. + * + * \{ */ + TreeElementOverridesProperty::TreeElementOverridesProperty(TreeElement &legacy_te, TreeElementOverridesData &override_data) : AbstractTreeElement(legacy_te), @@ -118,9 +167,10 @@ TreeElementOverridesProperty::TreeElementOverridesProperty(TreeElement &legacy_t rna_path(override_data.override_property.rna_path), is_rna_path_valid(override_data.is_rna_path_valid) { - BLI_assert(legacy_te.store_elem->type == TSE_LIBRARY_OVERRIDE); + BLI_assert( + ELEM(legacy_te.store_elem->type, TSE_LIBRARY_OVERRIDE, TSE_LIBRARY_OVERRIDE_OPERATION)); - legacy_te.name = override_data.override_property.rna_path; + legacy_te.name = RNA_property_ui_name(&override_data.override_rna_prop); } StringRefNull TreeElementOverridesProperty::getWarning() const @@ -134,4 +184,305 @@ StringRefNull TreeElementOverridesProperty::getWarning() const return {}; } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Overridden Property Operation + * + * See #TreeElementOverridesPropertyOperation. + * \{ */ + +TreeElementOverridesPropertyOperation::TreeElementOverridesPropertyOperation( + TreeElement &legacy_te, TreeElementOverridesData &override_data) + : TreeElementOverridesProperty(legacy_te, override_data) +{ + BLI_assert(legacy_te.store_elem->type == TSE_LIBRARY_OVERRIDE_OPERATION); + BLI_assert_msg(RNA_property_type(&override_rna_prop) == PROP_COLLECTION, + "Override operations are only supported for collections right now"); + /* Quiet Clang Static Analyzer warning by throwing instead of asserting (possible + * null-dereference). */ + if (!override_data.operation) { + throw std::invalid_argument("missing operation"); + } + + operation_ = std::make_unique<IDOverrideLibraryPropertyOperation>(*override_data.operation); + /* Just for extra sanity. */ + operation_->next = operation_->prev = nullptr; + + if (std::optional<PointerRNA> col_item_ptr = get_collection_ptr()) { + const char *dyn_name = RNA_struct_name_get_alloc(&*col_item_ptr, nullptr, 0, nullptr); + if (dyn_name) { + legacy_te.name = dyn_name; + legacy_te.flag |= TE_FREE_NAME; + } + else { + legacy_te.name = RNA_struct_ui_name(col_item_ptr->type); + } + } +} + +StringRefNull TreeElementOverridesPropertyOperation::getOverrideOperationLabel() const +{ + if (ELEM(operation_->operation, + IDOVERRIDE_LIBRARY_OP_INSERT_AFTER, + IDOVERRIDE_LIBRARY_OP_INSERT_BEFORE)) { + return TIP_("Added through override"); + } + + BLI_assert_unreachable(); + return {}; +} + +std::optional<BIFIconID> TreeElementOverridesPropertyOperation::getIcon() const +{ + if (const std::optional<PointerRNA> col_item_ptr = get_collection_ptr()) { + return (BIFIconID)RNA_struct_ui_icon(col_item_ptr->type); + } + + return {}; +} + +std::optional<PointerRNA> TreeElementOverridesPropertyOperation::get_collection_ptr() const +{ + PointerRNA col_item_ptr; + if (RNA_property_collection_lookup_int(const_cast<PointerRNA *>(&override_rna_ptr), + &override_rna_prop, + operation_->subitem_local_index, + &col_item_ptr)) { + return col_item_ptr; + } + + return {}; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Helper to build a hierarchy from an RNA path. + * + * Builds a nice hierarchy representing the nested structs of the override property's RNA path + * using UI names and icons. For example `animation_visualization_mothion_path.frame_end` becomes: + * - Animation Visualization + * - Motion Paths + * - End Frame + * + * Paths are merged so that each RNA sub-path is only represented once in the tree. So there is + * some finicky path building going on to create a path -> tree-element map. + * + * This is more complicated than you'd think it needs to be. Mostly because of RNA collection + * overrides: + * - A single override may add (and in future remove) multiple collection items. So all operations + * of the override have to be considered. + * - The order of collection items may matter (e.g. for modifiers), so if collection items are + * added/removed, we want to show all other collection items too, in the right order. + * + * - If the override is inside some collection item, the collection item has to be built, but the + * RNA path iterator doesn't + * \{ */ + +OverrideRNAPathTreeBuilder::OverrideRNAPathTreeBuilder(SpaceOutliner &space_outliner) + : space_outliner_(space_outliner) +{ +} + +void OverrideRNAPathTreeBuilder::build_path(TreeElement &parent, + TreeElementOverridesData &override_data, + short &index) +{ + PointerRNA idpoin; + RNA_id_pointer_create(&override_data.id, &idpoin); + + ListBase path_elems = {nullptr}; + if (!RNA_path_resolve_elements(&idpoin, override_data.override_property.rna_path, &path_elems)) { + return; + } + + const char *elem_path = nullptr; + TreeElement *te_to_expand = &parent; + + LISTBASE_FOREACH (PropertyElemRNA *, elem, &path_elems) { + if (!elem->next) { + /* The last element is added as #TSE_LIBRARY_OVERRIDE below. */ + break; + } + const char *previous_path = elem_path; + const char *new_path = RNA_path_append(previous_path, &elem->ptr, elem->prop, -1, nullptr); + + te_to_expand = &ensure_label_element_for_prop( + *te_to_expand, new_path, elem->ptr, *elem->prop, index); + + /* Above the collection property was added (e.g. "Modifiers"), to get the actual collection + * item the path refers to, we have to peek at the following path element and add a tree + * element for its pointer (e.g. "My Subdiv Modifier"). */ + if (RNA_property_type(elem->prop) == PROP_COLLECTION) { + const int coll_item_idx = RNA_property_collection_lookup_index( + &elem->ptr, elem->prop, &elem->next->ptr); + const char *coll_item_path = RNA_path_append( + previous_path, &elem->ptr, elem->prop, coll_item_idx, nullptr); + + te_to_expand = &ensure_label_element_for_ptr( + *te_to_expand, coll_item_path, elem->next->ptr, index); + + MEM_delete(new_path); + new_path = coll_item_path; + } + + if (new_path) { + MEM_delete(elem_path); + elem_path = new_path; + } + } + BLI_freelistN(&path_elems); + + /* Special case: Overriding collections, e.g. adding or removing items. In this case we add + * elements for all collection items to show full context, and indicate which ones were + * added/removed (currently added only). Note that a single collection override may add/remove + * multiple items. */ + if (RNA_property_type(&override_data.override_rna_prop) == PROP_COLLECTION) { + /* Tree element for the actual collection item (e.g. "Modifiers"). Can just use the override + * ptr & prop here, since they point to the collection property (e.g. `modifiers`). */ + te_to_expand = &ensure_label_element_for_prop(*te_to_expand, + override_data.override_property.rna_path, + override_data.override_rna_ptr, + override_data.override_rna_prop, + index); + + ensure_entire_collection(*te_to_expand, override_data, elem_path, index); + } + /* Some properties have multiple operations (e.g. an array property with multiple changed + * values), so the element may already be present. At this point they are displayed as a single + * property in the tree, so don't add it multiple times here. */ + else if (!path_te_map.contains(override_data.override_property.rna_path)) { + outliner_add_element(&space_outliner_, + &te_to_expand->subtree, + &override_data, + te_to_expand, + TSE_LIBRARY_OVERRIDE, + index++); + } + + MEM_delete(elem_path); +} + +void OverrideRNAPathTreeBuilder::ensure_entire_collection( + TreeElement &te_to_expand, + const TreeElementOverridesData &override_data, + /* The path of the owning collection property. */ + const char *coll_prop_path, + short &index) +{ + BLI_assert(tree_element_cast<AbstractTreeElement>(&te_to_expand) != nullptr); + + TreeElement *previous_te = nullptr; + int item_idx = 0; + RNA_PROP_BEGIN (&override_data.override_rna_ptr, itemptr, &override_data.override_rna_prop) { + const char *coll_item_path = RNA_path_append(coll_prop_path, + &override_data.override_rna_ptr, + &override_data.override_rna_prop, + item_idx, + nullptr); + IDOverrideLibraryPropertyOperation *item_operation = + BKE_lib_override_library_property_operation_find( + &override_data.override_property, nullptr, nullptr, -1, item_idx, false, nullptr); + TreeElement *current_te = nullptr; + + TreeElement *existing_te = path_te_map.lookup_default(coll_item_path, nullptr); + + if (existing_te) { + /* Reinsert the element to make sure the order is right. It may have been inserted by a + * previous override. */ + BLI_remlink(&te_to_expand.subtree, existing_te); + BLI_insertlinkafter(&te_to_expand.subtree, previous_te, existing_te); + current_te = existing_te; + } + /* Is there an operation for this item (added or removed the item to/from the collection)? If + * so indicate it as override using #TSE_LIBRARY_OVERRIDE_OPERATION. Otherwise it's just a + * regular collection we display for context. */ + else if (item_operation) { + TreeElementOverridesData override_op_data = override_data; + override_op_data.operation = item_operation; + + current_te = outliner_add_element(&space_outliner_, + &te_to_expand.subtree, + /* Element will store a copy. */ + &override_op_data, + &te_to_expand, + TSE_LIBRARY_OVERRIDE_OPERATION, + index++); + } + else { + current_te = &ensure_label_element_for_ptr(te_to_expand, coll_item_path, itemptr, index); + } + + MEM_delete(coll_item_path); + item_idx++; + previous_te = current_te; + } + RNA_PROP_END; +} + +static BIFIconID get_property_icon(PointerRNA &ptr, PropertyRNA &prop) +{ + BIFIconID icon = (BIFIconID)RNA_property_ui_icon(&prop); + if (icon) { + return icon; + } + + /* Try if the collection item type has a dedicated icon (e.g. #ICON_MODIFIER for the + * #Object.modifiers property). */ + if (RNA_property_type(&prop) == PROP_COLLECTION) { + const StructRNA *coll_ptr_type = RNA_property_pointer_type(&ptr, &prop); + icon = (BIFIconID)RNA_struct_ui_icon(coll_ptr_type); + if (icon != ICON_DOT) { + return icon; + } + } + + return ICON_NONE; +} + +TreeElement &OverrideRNAPathTreeBuilder::ensure_label_element_for_prop( + TreeElement &parent, StringRef elem_path, PointerRNA &ptr, PropertyRNA &prop, short &index) +{ + return *path_te_map.lookup_or_add_cb(elem_path, [&]() { + TreeElement *new_te = outliner_add_element(&space_outliner_, + &parent.subtree, + (void *)RNA_property_ui_name(&prop), + &parent, + TSE_GENERIC_LABEL, + index++, + false); + TreeElementLabel *te_label = tree_element_cast<TreeElementLabel>(new_te); + + te_label->setIcon(get_property_icon(ptr, prop)); + return new_te; + }); +} + +TreeElement &OverrideRNAPathTreeBuilder::ensure_label_element_for_ptr(TreeElement &parent, + StringRef elem_path, + PointerRNA &ptr, + short &index) +{ + return *path_te_map.lookup_or_add_cb(elem_path, [&]() { + const char *dyn_name = RNA_struct_name_get_alloc(&ptr, nullptr, 0, nullptr); + + TreeElement *new_te = outliner_add_element( + &space_outliner_, + &parent.subtree, + (void *)(dyn_name ? dyn_name : RNA_struct_ui_name(ptr.type)), + &parent, + TSE_GENERIC_LABEL, + index++); + TreeElementLabel *te_label = tree_element_cast<TreeElementLabel>(new_te); + te_label->setIcon((BIFIconID)RNA_struct_ui_icon(ptr.type)); + + MEM_delete(dyn_name); + + return new_te; + }); +} + +/** \} */ + } // namespace blender::ed::outliner diff --git a/source/blender/editors/space_outliner/tree/tree_element_overrides.hh b/source/blender/editors/space_outliner/tree/tree_element_overrides.hh index 1db46d9af1d..f8ca146a4ea 100644 --- a/source/blender/editors/space_outliner/tree/tree_element_overrides.hh +++ b/source/blender/editors/space_outliner/tree/tree_element_overrides.hh @@ -14,6 +14,7 @@ struct ID; struct IDOverrideLibraryProperty; +struct IDOverrideLibraryPropertyOperation; namespace blender::ed::outliner { @@ -24,6 +25,11 @@ struct TreeElementOverridesData { PropertyRNA &override_rna_prop; bool is_rna_path_valid; + + /* In case the property references a specific operation. Only used for collection overrides + * currently, where a single override may add/remove multiple collection items (only add + * currently). */ + IDOverrideLibraryPropertyOperation *operation = nullptr; }; class TreeElementOverridesBase final : public AbstractTreeElement { @@ -38,7 +44,12 @@ class TreeElementOverridesBase final : public AbstractTreeElement { StringRefNull getWarning() const override; }; -class TreeElementOverridesProperty final : public AbstractTreeElement { +/** + * Represent a single overridden property. Collection properties may support multiple override + * operations, e.g. to insert/remove multiple collection items. For these multiple operation cases, + * use #TreeElementOverridesPropertyOperation. + */ +class TreeElementOverridesProperty : public AbstractTreeElement { public: PointerRNA override_rna_ptr; PropertyRNA &override_rna_prop; @@ -50,6 +61,33 @@ class TreeElementOverridesProperty final : public AbstractTreeElement { TreeElementOverridesProperty(TreeElement &legacy_te, TreeElementOverridesData &override_data); StringRefNull getWarning() const override; + + bool isCollectionOperation() const; +}; + +/** + * Represent a single operation within an overridden property. While usually a single override + * property represents a single operation (changing the value), a single overridden collection + * property may have multiple operations, e.g. to insert or remove collection items. + * + * Inherits from the override property class since it should look/behave mostly the same. + */ +class TreeElementOverridesPropertyOperation final : public TreeElementOverridesProperty { + /** See #TreeElementOverridesData::operation. Operations are recreated as part of the diffing + * (e.g. on undo pushes) so store a copy of the data here. */ + std::unique_ptr<IDOverrideLibraryPropertyOperation> operation_; + + public: + TreeElementOverridesPropertyOperation(TreeElement &legacy_te, + TreeElementOverridesData &override_data); + + /** Return a short string to display in the right column of the properties mode, indicating what + * the override operation did (e.g. added or removed a collection item). */ + StringRefNull getOverrideOperationLabel() const; + std::optional<BIFIconID> getIcon() const override; + + private: + std::optional<PointerRNA> get_collection_ptr() const; }; } // namespace blender::ed::outliner diff --git a/source/blender/editors/space_outliner/tree/tree_element_rna.cc b/source/blender/editors/space_outliner/tree/tree_element_rna.cc index 914104f1f06..9e1f22b49d6 100644 --- a/source/blender/editors/space_outliner/tree/tree_element_rna.cc +++ b/source/blender/editors/space_outliner/tree/tree_element_rna.cc @@ -117,14 +117,14 @@ void TreeElementRNAStruct::expand(SpaceOutliner &space_outliner) const for (int index = 0; index < tot; index++) { PointerRNA propptr; RNA_property_collection_lookup_int(&ptr, iterprop, index, &propptr); - if (!(RNA_property_flag(reinterpret_cast<PropertyRNA *>(propptr.data)) & PROP_HIDDEN)) { + if (!(RNA_property_flag(static_cast<PropertyRNA *>(propptr.data)) & PROP_HIDDEN)) { outliner_add_element( &space_outliner, &legacy_te_.subtree, &ptr, &legacy_te_, TSE_RNA_PROPERTY, index); } } } else if (tot) { - legacy_te_.flag |= TE_LAZY_CLOSED; + legacy_te_.flag |= TE_PRETEND_HAS_CHILDREN; } } @@ -146,7 +146,7 @@ TreeElementRNAProperty::TreeElementRNAProperty(TreeElement &legacy_te, PropertyRNA *iterprop = RNA_struct_iterator_property(rna_ptr.type); RNA_property_collection_lookup_int(&rna_ptr, iterprop, index, &propptr); - PropertyRNA *prop = reinterpret_cast<PropertyRNA *>(propptr.data); + PropertyRNA *prop = static_cast<PropertyRNA *>(propptr.data); legacy_te_.name = RNA_property_ui_name(prop); rna_prop_ = prop; @@ -172,7 +172,7 @@ void TreeElementRNAProperty::expand(SpaceOutliner &space_outliner) const &space_outliner, &legacy_te_.subtree, &pptr, &legacy_te_, TSE_RNA_STRUCT, -1); } else { - legacy_te_.flag |= TE_LAZY_CLOSED; + legacy_te_.flag |= TE_PRETEND_HAS_CHILDREN; } } } @@ -189,7 +189,7 @@ void TreeElementRNAProperty::expand(SpaceOutliner &space_outliner) const } } else if (tot) { - legacy_te_.flag |= TE_LAZY_CLOSED; + legacy_te_.flag |= TE_PRETEND_HAS_CHILDREN; } } else if (ELEM(proptype, PROP_BOOLEAN, PROP_INT, PROP_FLOAT)) { @@ -207,7 +207,7 @@ void TreeElementRNAProperty::expand(SpaceOutliner &space_outliner) const } } else if (tot) { - legacy_te_.flag |= TE_LAZY_CLOSED; + legacy_te_.flag |= TE_PRETEND_HAS_CHILDREN; } } } @@ -232,8 +232,7 @@ TreeElementRNAArrayElement::TreeElementRNAArrayElement(TreeElement &legacy_te, char c = RNA_property_array_item_char(TreeElementRNAArrayElement::getPropertyRNA(), index); - legacy_te_.name = reinterpret_cast<char *>( - MEM_callocN(sizeof(char[20]), "OutlinerRNAArrayName")); + legacy_te_.name = static_cast<char *>(MEM_callocN(sizeof(char[20]), "OutlinerRNAArrayName")); if (c) { sprintf((char *)legacy_te_.name, " %c", c); } diff --git a/source/blender/editors/space_outliner/tree/tree_iterator.hh b/source/blender/editors/space_outliner/tree/tree_iterator.hh index de5bcd2c462..0c94c2f95cf 100644 --- a/source/blender/editors/space_outliner/tree/tree_iterator.hh +++ b/source/blender/editors/space_outliner/tree/tree_iterator.hh @@ -10,9 +10,11 @@ struct ListBase; struct SpaceOutliner; -struct TreeElement; namespace blender::ed::outliner { + +struct TreeElement; + namespace tree_iterator { using VisitorFn = FunctionRef<void(TreeElement *)>; |