Cleanup: General cleanup of Outliner Blender File display mode building
See https://developer.blender.org/D9499. * Turn functions into member functions (makes API for a type more obvious & local, allows implicitly sharing data through member variables, enables order independend definition of functions, allows more natural language for function names because of the obvious context). * Prefer references over pointers for passing by reference (makes clear that NULL is not a valid value and that the current scope is not the owner). * Reduce indentation levels, use `continue` in loops to ensure preconditions are met. * Add asserts for sanity checks.
This commit is contained in:
parent
44d8fafd7f
commit
43b4570dcf
|
@ -26,6 +26,7 @@
|
|||
#include "DNA_space_types.h"
|
||||
|
||||
struct ListBase;
|
||||
struct Main;
|
||||
struct SpaceOutliner;
|
||||
struct TreeElement;
|
||||
struct TreeSourceData;
|
||||
|
@ -100,6 +101,9 @@ class TreeViewLibraries final : public AbstractTreeView {
|
|||
ListBase buildTree(const TreeSourceData &source_data) override;
|
||||
|
||||
private:
|
||||
TreeElement *add_library_contents(Main &, ListBase &, Library *) const;
|
||||
bool library_id_filter_poll(Library *lib, ID *id) const;
|
||||
short id_filter_get() const;
|
||||
};
|
||||
|
||||
} // namespace outliner
|
||||
|
|
|
@ -41,13 +41,158 @@ TreeViewLibraries::TreeViewLibraries(SpaceOutliner &space_outliner)
|
|||
{
|
||||
}
|
||||
|
||||
static bool outliner_library_id_show(Library *lib, ID *id, short filter_id_type)
|
||||
ListBase TreeViewLibraries::buildTree(const TreeSourceData &source_data)
|
||||
{
|
||||
ListBase tree = {nullptr};
|
||||
|
||||
{
|
||||
/* current file first - mainvar provides tselem with unique pointer - not used */
|
||||
TreeElement *ten = add_library_contents(*source_data.bmain, tree, nullptr);
|
||||
TreeStoreElem *tselem;
|
||||
|
||||
if (ten) {
|
||||
tselem = TREESTORE(ten);
|
||||
if (!tselem->used) {
|
||||
tselem->flag &= ~TSE_CLOSED;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (ID *id : List<ID>(source_data.bmain->libraries)) {
|
||||
Library *lib = reinterpret_cast<Library *>(id);
|
||||
TreeElement *ten = add_library_contents(*source_data.bmain, tree, lib);
|
||||
/* NULL-check matters, due to filtering there may not be a new element. */
|
||||
if (ten) {
|
||||
lib->id.newid = (ID *)ten;
|
||||
}
|
||||
}
|
||||
|
||||
/* make hierarchy */
|
||||
for (TreeElement *ten : List<TreeElement>(tree)) {
|
||||
if (ten == tree.first) {
|
||||
/* First item is main, skip. */
|
||||
continue;
|
||||
}
|
||||
|
||||
TreeStoreElem *tselem = TREESTORE(ten);
|
||||
Library *lib = (Library *)tselem->id;
|
||||
BLI_assert(!lib || (GS(lib->id.name) == ID_LI));
|
||||
if (!lib || !lib->parent) {
|
||||
continue;
|
||||
}
|
||||
|
||||
TreeElement *parent = (TreeElement *)lib->parent->id.newid;
|
||||
|
||||
if (tselem->id->tag & LIB_TAG_INDIRECT) {
|
||||
/* Only remove from 'first level' if lib is not also directly used. */
|
||||
BLI_remlink(&tree, ten);
|
||||
BLI_addtail(&parent->subtree, ten);
|
||||
ten->parent = parent;
|
||||
}
|
||||
else {
|
||||
/* Else, make a new copy of the libtree for our parent. */
|
||||
TreeElement *dupten = add_library_contents(*source_data.bmain, parent->subtree, lib);
|
||||
if (dupten) {
|
||||
dupten->parent = parent;
|
||||
}
|
||||
}
|
||||
}
|
||||
/* restore newid pointers */
|
||||
for (ID *library_id : List<ID>(source_data.bmain->libraries)) {
|
||||
library_id->newid = nullptr;
|
||||
}
|
||||
|
||||
return tree;
|
||||
}
|
||||
|
||||
TreeElement *TreeViewLibraries::add_library_contents(Main &mainvar,
|
||||
ListBase &lb,
|
||||
Library *lib) const
|
||||
{
|
||||
const short filter_id_type = id_filter_get();
|
||||
|
||||
ListBase *lbarray[MAX_LIBARRAY];
|
||||
int tot;
|
||||
if (filter_id_type) {
|
||||
lbarray[0] = which_libbase(&mainvar, _space_outliner.filter_id_type);
|
||||
tot = 1;
|
||||
}
|
||||
else {
|
||||
tot = set_listbasepointers(&mainvar, lbarray);
|
||||
}
|
||||
|
||||
TreeElement *tenlib = nullptr;
|
||||
for (int a = 0; a < tot; a++) {
|
||||
if (!lbarray[a] || !lbarray[a]->first) {
|
||||
continue;
|
||||
}
|
||||
|
||||
ID *id = static_cast<ID *>(lbarray[a]->first);
|
||||
const bool is_library = (GS(id->name) == ID_LI) && (lib != nullptr);
|
||||
|
||||
/* check if there's data in current lib */
|
||||
for (ID *id_iter : List<ID>(lbarray[a])) {
|
||||
if (id_iter->lib == lib) {
|
||||
id = id_iter;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* We always want to create an entry for libraries, even if/when we have no more IDs from
|
||||
* them. This invalid state is important to show to user as well.*/
|
||||
if (id != nullptr || is_library) {
|
||||
if (!tenlib) {
|
||||
/* Create library tree element on demand, depending if there are any data-blocks. */
|
||||
if (lib) {
|
||||
tenlib = outliner_add_element(&_space_outliner, &lb, lib, nullptr, 0, 0);
|
||||
}
|
||||
else {
|
||||
tenlib = outliner_add_element(&_space_outliner, &lb, &mainvar, nullptr, TSE_ID_BASE, 0);
|
||||
tenlib->name = IFACE_("Current File");
|
||||
}
|
||||
}
|
||||
|
||||
/* Create data-block list parent element on demand. */
|
||||
if (id != nullptr) {
|
||||
TreeElement *ten;
|
||||
|
||||
if (filter_id_type) {
|
||||
ten = tenlib;
|
||||
}
|
||||
else {
|
||||
ten = outliner_add_element(
|
||||
&_space_outliner, &tenlib->subtree, lbarray[a], nullptr, TSE_ID_BASE, 0);
|
||||
ten->directdata = lbarray[a];
|
||||
ten->name = outliner_idcode_to_plural(GS(id->name));
|
||||
}
|
||||
|
||||
for (ID *id : List<ID>(lbarray[a])) {
|
||||
if (library_id_filter_poll(lib, id)) {
|
||||
outliner_add_element(&_space_outliner, &ten->subtree, id, ten, 0, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return tenlib;
|
||||
}
|
||||
|
||||
short TreeViewLibraries::id_filter_get() const
|
||||
{
|
||||
if (_space_outliner.filter & SO_FILTER_ID_TYPE) {
|
||||
return _space_outliner.filter_id_type;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool TreeViewLibraries::library_id_filter_poll(Library *lib, ID *id) const
|
||||
{
|
||||
if (id->lib != lib) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (filter_id_type == ID_GR) {
|
||||
if (id_filter_get() == ID_GR) {
|
||||
/* Don't show child collections of non-scene master collection,
|
||||
* they are already shown as children. */
|
||||
Collection *collection = (Collection *)id;
|
||||
|
@ -67,138 +212,6 @@ static bool outliner_library_id_show(Library *lib, ID *id, short filter_id_type)
|
|||
return true;
|
||||
}
|
||||
|
||||
static TreeElement *outliner_add_library_contents(Main *mainvar,
|
||||
SpaceOutliner *space_outliner,
|
||||
ListBase *lb,
|
||||
Library *lib)
|
||||
{
|
||||
TreeElement *ten, *tenlib = nullptr;
|
||||
ListBase *lbarray[MAX_LIBARRAY];
|
||||
int a, tot;
|
||||
short filter_id_type = (space_outliner->filter & SO_FILTER_ID_TYPE) ?
|
||||
space_outliner->filter_id_type :
|
||||
0;
|
||||
|
||||
if (filter_id_type) {
|
||||
lbarray[0] = which_libbase(mainvar, space_outliner->filter_id_type);
|
||||
tot = 1;
|
||||
}
|
||||
else {
|
||||
tot = set_listbasepointers(mainvar, lbarray);
|
||||
}
|
||||
|
||||
for (a = 0; a < tot; a++) {
|
||||
if (lbarray[a] && lbarray[a]->first) {
|
||||
ID *id = static_cast<ID *>(lbarray[a]->first);
|
||||
const bool is_library = (GS(id->name) == ID_LI) && (lib != nullptr);
|
||||
|
||||
/* check if there's data in current lib */
|
||||
for (ID *id_iter : List<ID>(lbarray[a])) {
|
||||
if (id_iter->lib == lib) {
|
||||
id = id_iter;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* We always want to create an entry for libraries, even if/when we have no more IDs from
|
||||
* them. This invalid state is important to show to user as well.*/
|
||||
if (id != nullptr || is_library) {
|
||||
if (!tenlib) {
|
||||
/* Create library tree element on demand, depending if there are any data-blocks. */
|
||||
if (lib) {
|
||||
tenlib = outliner_add_element(space_outliner, lb, lib, nullptr, 0, 0);
|
||||
}
|
||||
else {
|
||||
tenlib = outliner_add_element(space_outliner, lb, mainvar, nullptr, TSE_ID_BASE, 0);
|
||||
tenlib->name = IFACE_("Current File");
|
||||
}
|
||||
}
|
||||
|
||||
/* Create data-block list parent element on demand. */
|
||||
if (id != nullptr) {
|
||||
if (filter_id_type) {
|
||||
ten = tenlib;
|
||||
}
|
||||
else {
|
||||
ten = outliner_add_element(
|
||||
space_outliner, &tenlib->subtree, lbarray[a], nullptr, TSE_ID_BASE, 0);
|
||||
ten->directdata = lbarray[a];
|
||||
ten->name = outliner_idcode_to_plural(GS(id->name));
|
||||
}
|
||||
|
||||
for (ID *id : List<ID>(lbarray[a])) {
|
||||
if (outliner_library_id_show(lib, id, filter_id_type)) {
|
||||
outliner_add_element(space_outliner, &ten->subtree, id, ten, 0, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return tenlib;
|
||||
}
|
||||
|
||||
ListBase TreeViewLibraries::buildTree(const TreeSourceData &source_data)
|
||||
{
|
||||
ListBase tree = {nullptr};
|
||||
|
||||
/* current file first - mainvar provides tselem with unique pointer - not used */
|
||||
TreeElement *ten = outliner_add_library_contents(
|
||||
source_data.bmain, &_space_outliner, &tree, nullptr);
|
||||
TreeStoreElem *tselem;
|
||||
|
||||
if (ten) {
|
||||
tselem = TREESTORE(ten);
|
||||
if (!tselem->used) {
|
||||
tselem->flag &= ~TSE_CLOSED;
|
||||
}
|
||||
}
|
||||
|
||||
for (ID *id : List<ID>(source_data.bmain->libraries)) {
|
||||
Library *lib = reinterpret_cast<Library *>(id);
|
||||
ten = outliner_add_library_contents(source_data.bmain, &_space_outliner, &tree, lib);
|
||||
/* NULL-check matters, due to filtering there may not be a new element. */
|
||||
if (ten) {
|
||||
lib->id.newid = (ID *)ten;
|
||||
}
|
||||
}
|
||||
/* make hierarchy */
|
||||
ten = static_cast<TreeElement *>(tree.first);
|
||||
if (ten != nullptr) {
|
||||
ten = ten->next; /* first one is main */
|
||||
while (ten) {
|
||||
TreeElement *nten = ten->next, *par;
|
||||
tselem = TREESTORE(ten);
|
||||
Library *lib = (Library *)tselem->id;
|
||||
if (lib && lib->parent) {
|
||||
par = (TreeElement *)lib->parent->id.newid;
|
||||
if (tselem->id->tag & LIB_TAG_INDIRECT) {
|
||||
/* Only remove from 'first level' if lib is not also directly used. */
|
||||
BLI_remlink(&tree, ten);
|
||||
BLI_addtail(&par->subtree, ten);
|
||||
ten->parent = par;
|
||||
}
|
||||
else {
|
||||
/* Else, make a new copy of the libtree for our parent. */
|
||||
TreeElement *dupten = outliner_add_library_contents(
|
||||
source_data.bmain, &_space_outliner, &par->subtree, lib);
|
||||
if (dupten) {
|
||||
dupten->parent = par;
|
||||
}
|
||||
}
|
||||
}
|
||||
ten = nten;
|
||||
}
|
||||
}
|
||||
/* restore newid pointers */
|
||||
for (ID *library_id : List<ID>(source_data.bmain->libraries)) {
|
||||
library_id->newid = nullptr;
|
||||
}
|
||||
|
||||
return tree;
|
||||
}
|
||||
|
||||
} // namespace outliner
|
||||
} // namespace ed
|
||||
} // namespace blender
|
||||
|
|
Loading…
Reference in New Issue