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:
Julian Eisel 2020-11-09 00:24:14 +01:00
parent 44d8fafd7f
commit 43b4570dcf
2 changed files with 151 additions and 134 deletions

View File

@ -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

View File

@ -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