LibOverride: Rework Outliner contextual menu.

Follow-up to design discussions here at the studio, add liboverride
operations into their own sub-menu, with three main entries:
- Create: Create, or enable for user editing, override hierarchies.
- Reset: Keep overrides data, but reset all local changes to the
  reference linked data values.
- Clear: like reset, but also turn editable overrides back to system
  overrides (aka non user editable).

Those three options can all operate either on the selected items, their
content only, or both.

Advanced operations are moved into a "Troubleshoot Hierarchy" sub-menu,
where one can resync, resync enforced, and fully delete library
overrides. Those operations always affect a whole override hierarchy,
regardless of which items are selected or not.
This commit is contained in:
Bastien Montagne 2022-08-12 15:06:18 +02:00
parent 4dc9a8a21e
commit c73cc15e07
4 changed files with 452 additions and 241 deletions

View File

@ -97,6 +97,10 @@ class OUTLINER_MT_context_menu(Menu):
layout.separator()
layout.menu("OUTLINER_MT_liboverride")
layout.separator()
layout.menu("OUTLINER_MT_context_menu_view")
layout.separator()
@ -320,6 +324,19 @@ class OUTLINER_MT_asset(Menu):
layout.operator("asset.clear", text="Clear Asset (Set Fake User)").set_fake_user = True
class OUTLINER_MT_liboverride(Menu):
bl_label = "Library Override"
def draw(self, _context):
layout = self.layout
layout.operator_menu_enum("outliner.liboverride_operation", "selection_set", text="Create").type = 'OVERRIDE_LIBRARY_CREATE_HIERARCHY'
layout.operator_menu_enum("outliner.liboverride_operation", "selection_set", text="Reset").type = 'OVERRIDE_LIBRARY_RESET'
layout.operator_menu_enum("outliner.liboverride_operation", "selection_set", text="Clear").type = 'OVERRIDE_LIBRARY_CLEAR_SINGLE'
layout.operator_menu_enum("outliner.liboverride_troubleshoot_operation", "type", text="Troubleshoot Hierarchy").selection_set = 'SELECTED'
class OUTLINER_PT_filter(Panel):
bl_space_type = 'OUTLINER'
bl_region_type = 'HEADER'
@ -457,6 +474,7 @@ classes = (
OUTLINER_MT_collection_view_layer,
OUTLINER_MT_object,
OUTLINER_MT_asset,
OUTLINER_MT_liboverride,
OUTLINER_MT_context_menu,
OUTLINER_MT_context_menu_view,
OUTLINER_MT_view_pie,

View File

@ -530,6 +530,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);

View File

@ -29,6 +29,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);

View File

@ -453,6 +453,99 @@ static void outliner_do_libdata_operation(bContext *C,
});
}
typedef 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,
} eOutlinerLibOpSelectionSet;
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);
}
/** \} */
/* -------------------------------------------------------------------- */
@ -842,6 +935,20 @@ struct OutlinerLibOverrideData {
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
@ -860,17 +967,29 @@ static void id_override_library_create_hierarchy_pre_process_fn(bContext *C,
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);
printf("Adding %s as selected item to get editable override\n", id_root_reference->name);
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). */
Collection *root_collection = reinterpret_cast<Collection *>(id_root_reference);
FOREACH_COLLECTION_OBJECT_RECURSIVE_BEGIN (root_collection, object_iter) {
if (id_root_reference->lib == object_iter->id.lib && object_iter->type == OB_ARMATURE) {
printf("Adding %s as selected item to get editable override\n", object_iter->id.name);
data->selected_id_uid.add(object_iter->id.session_uuid);
}
}
@ -983,6 +1102,17 @@ static void id_override_library_create_hierarchy_pre_process_fn(bContext *C,
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,
@ -1133,23 +1263,6 @@ static void id_override_library_create_hierarchy_process(bContext *C,
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),
@ -1181,10 +1294,10 @@ static void id_override_library_reset_fn(bContext *C,
}
}
static void id_override_library_resync_fn(bContext *C,
ReportList *reports,
Scene *scene,
TreeElement *te,
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)
@ -1192,44 +1305,53 @@ static void id_override_library_resync_fn(bContext *C,
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);
id_root->tag |= LIB_TAG_DOIT;
/* 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;
}
te->store_elem->id->tag |= LIB_TAG_DOIT;
}
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);
}
else {
if (!ID_IS_OVERRIDE_LIBRARY_REAL(id_root)) {
CLOG_WARN(&LOG, "Could not resync library override of data block '%s'", id_root->name);
}
if (id_root->override_library->hierarchy_root != nullptr) {
id_root = id_root->override_library->hierarchy_root;
}
data->id_root_set(id_root);
}
static void id_override_library_clear_hierarchy_fn(bContext *C,
/* 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;
BlendFileReadReport report{};
report.reports = reports;
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);
}
WM_event_add_notifier(C, NC_WINDOW, nullptr);
}
static void id_override_library_clear_hierarchy_fn(bContext *UNUSED(C),
ReportList *UNUSED(reports),
Scene *UNUSED(scene),
TreeElement *te,
TreeElement *UNUSED(te),
TreeStoreElem *UNUSED(tsep),
TreeStoreElem *tselem,
void *UNUSED(user_data))
void *user_data)
{
OutlinerLibOverrideData *data = reinterpret_cast<OutlinerLibOverrideData *>(user_data);
BLI_assert(TSE_IS_REAL_ID(tselem));
ID *id_root = tselem->id;
@ -1238,22 +1360,23 @@ static void id_override_library_clear_hierarchy_fn(bContext *C,
return;
}
Main *bmain = CTX_data_main(C);
id_root->tag |= LIB_TAG_DOIT;
/* 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;
if (id_root->override_library->hierarchy_root != nullptr) {
id_root = id_root->override_library->hierarchy_root;
}
BKE_lib_override_library_delete(bmain, id_root);
data->id_root_set(id_root);
}
/* Clear (delete) a hierarchy of library overrides. */
static void id_override_library_clear_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);
}
WM_event_add_notifier(C, NC_WINDOW, nullptr);
}
@ -1493,6 +1616,250 @@ 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,
"Create",
"Make 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 override 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)"},
{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_clear_hierarchy_fn,
OUTLINER_LIB_SELECTIONSET_SELECTED,
nullptr);
id_override_library_clear_hierarchy_process(C, op->reports, override_data);
ED_undo_push(C, "Delete Overridden Data Hierarchy");
break;
}
default:
/* Invalid - unhandled. */
break;
}
/* wrong notifier still... */
WM_event_add_notifier(C, NC_ID | NA_EDITED, nullptr);
/* XXX: this is just so that outliner is always up to date. */
WM_event_add_notifier(C, NC_SPACE | ND_SPACE_OUTLINER, nullptr);
return OPERATOR_FINISHED;
}
void OUTLINER_OT_liboverride_operation(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Outliner Library Override Operation";
ot->idname = "OUTLINER_OT_liboverride_operation";
/* 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";
/* 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_troubleshoot_op_types,
0,
"Library Override Troubleshoot 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");
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Object Operation Utilities
* \{ */
@ -2088,15 +2455,6 @@ enum eOutlinerIdOpTypes {
OUTLINER_IDOP_UNLINK,
OUTLINER_IDOP_LOCAL,
OUTLINER_IDOP_OVERRIDE_LIBRARY_CREATE,
OUTLINER_IDOP_OVERRIDE_LIBRARY_CREATE_HIERARCHY,
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,
@ -2123,59 +2481,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_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,
@ -2208,33 +2513,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:
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;
@ -2347,95 +2625,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{};
override_data.do_hierarchy = false;
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);
id_override_library_create_hierarchy_process(C, op->reports, override_data);
ED_undo_push(C, "Overridden Data");
break;
}
case OUTLINER_IDOP_OVERRIDE_LIBRARY_CREATE_HIERARCHY: {
OutlinerLibOverrideData override_data{};
override_data.do_hierarchy = true;
override_data.do_fully_editable = U.experimental.use_override_new_fully_editable;
outliner_do_libdata_operation(C,
op->reports,
scene,
space_outliner,
id_override_library_create_hierarchy_pre_process_fn,
&override_data);
id_override_library_create_hierarchy_process(C, op->reports, override_data);
ED_undo_push(C, "Overridden Data Hierarchy");
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) {