diff options
Diffstat (limited to 'source/blender/editors/space_outliner/outliner_tree.c')
-rw-r--r-- | source/blender/editors/space_outliner/outliner_tree.c | 447 |
1 files changed, 374 insertions, 73 deletions
diff --git a/source/blender/editors/space_outliner/outliner_tree.c b/source/blender/editors/space_outliner/outliner_tree.c index c8f38e5282b..851e19a3650 100644 --- a/source/blender/editors/space_outliner/outliner_tree.c +++ b/source/blender/editors/space_outliner/outliner_tree.c @@ -82,6 +82,8 @@ #include "RNA_access.h" +#include "UI_interface.h" + #include "outliner_intern.h" #ifdef WIN32 @@ -1751,6 +1753,305 @@ static void outliner_sort(ListBase *lb) /* Filtering ----------------------------------------------- */ +typedef struct OutlinerTreeElementFocus { + TreeStoreElem *tselem; + int ys; +} OutlinerTreeElementFocus; + +/** + * Bring the outliner scrolling back to where it was in relation to the original focus element + * Caller is expected to handle redrawing of ARegion. + */ +static void outliner_restore_scrolling_position(SpaceOops *soops, ARegion *ar, OutlinerTreeElementFocus *focus) +{ + View2D *v2d = &ar->v2d; + int ytop; + + if (focus->tselem != NULL) { + outliner_set_coordinates(ar, soops); + + TreeElement *te_new = outliner_find_tree_element(&soops->tree, focus->tselem); + + if (te_new != NULL) { + int ys_new, ys_old; + + ys_new = te_new->ys; + ys_old = focus->ys; + + ytop = v2d->cur.ymax + (ys_new - ys_old) -1; + if (ytop > 0) ytop = 0; + + v2d->cur.ymax = (float)ytop; + v2d->cur.ymin = (float)(ytop - BLI_rcti_size_y(&v2d->mask)); + } + else { + return; + } + + soops->storeflag |= SO_TREESTORE_REDRAW; + } +} + +static bool test_collection_callback(TreeElement *te) +{ + TreeStoreElem *tselem = TREESTORE(te); + return ELEM(tselem->type, TSE_LAYER_COLLECTION, TSE_SCENE_COLLECTION); +} + +static bool test_object_callback(TreeElement *te) +{ + TreeStoreElem *tselem = TREESTORE(te); + return ((tselem->type == 0) && (te->idcode == ID_OB)); +} + +/** + * See if TreeElement or any of its children pass the callback_test. + */ +static TreeElement *outliner_find_first_desired_element_at_y_recursive( + const SpaceOops *soops, + TreeElement *te, + const float limit, + bool (*callback_test)(TreeElement *)) +{ + if (callback_test(te)) { + return te; + } + + if (TSELEM_OPEN(te->store_elem, soops)) { + TreeElement *te_iter, *te_sub; + for (te_iter = te->subtree.first; te_iter; te_iter = te_iter->next) { + te_sub = outliner_find_first_desired_element_at_y_recursive(soops, te_iter, limit, callback_test); + if (te_sub != NULL) { + return te_sub; + } + } + } + + return NULL; +} + +/** + * Find the first element that passes a test starting from a reference vertical coordinate + * + * If the element that is in the position is not what we are looking for, keep looking for its + * children, siblings, and eventually, aunts, cousins, disntant families, ... + * + * Basically we keep going up and down the outliner tree from that point forward, until we find + * what we are looking for. If we are past the visible range and we can't find a valid element + * we return NULL. + */ +static TreeElement *outliner_find_first_desired_element_at_y( + const SpaceOops *soops, + const float view_co, + const float view_co_limit) +{ + TreeElement *te, *te_sub; + te = outliner_find_item_at_y(soops, &soops->tree, view_co); + + bool (*callback_test)(TreeElement *); + if (soops->filter & SO_FILTER_NO_COLLECTION) { + callback_test = test_object_callback; + } + else { + callback_test = test_collection_callback; + } + + while (te != NULL) { + te_sub = outliner_find_first_desired_element_at_y_recursive(soops, te, view_co_limit, callback_test); + if (te_sub != NULL) { + /* Skip the element if it was not visible to start with. */ + if (te->ys + UI_UNIT_Y > view_co_limit) { + return te_sub; + } + else { + return NULL; + } + } + + if (te->next) { + te = te->next; + continue; + } + + if (te->parent == NULL) { + break; + } + + while (te->parent) { + if (te->parent->next) { + te = te->parent->next; + break; + } + te = te->parent; + } + } + + return NULL; +} + +/** + * Store information of current outliner scrolling status to be restored later + * + * Finds the top-most collection visible in the outliner and populates the OutlinerTreeElementFocus + * struct to retrieve this element later to make sure it is in the same original position as before filtering + */ +static void outliner_store_scrolling_position(SpaceOops *soops, ARegion *ar, OutlinerTreeElementFocus *focus) +{ + TreeElement *te; + float limit = ar->v2d.cur.ymin; + + outliner_set_coordinates(ar, soops); + + te = outliner_find_first_desired_element_at_y(soops, ar->v2d.cur.ymax, limit); + + if (te != NULL) { + focus->tselem = TREESTORE(te); + focus->ys = te->ys; + } + else { + focus->tselem = NULL; + } +} + +static int outliner_exclude_filter_get(SpaceOops *soops) +{ + int exclude_filter = soops->filter & ~(SO_FILTER_OB_STATE_VISIBLE | + SO_FILTER_OB_STATE_SELECTED | + SO_FILTER_OB_STATE_ACTIVE); + + if (soops->filter & SO_FILTER_SEARCH) { + if (soops->search_string[0] == 0) { + exclude_filter &= ~SO_FILTER_SEARCH; + } + } + + /* Let's have this for the collection options at first. */ + if (!SUPPORT_FILTER_OUTLINER(soops)) { + return (exclude_filter & SO_FILTER_SEARCH); + } + + if ((exclude_filter & SO_FILTER_NO_OB_ALL) == 0) { + exclude_filter &= ~SO_FILTER_OB_TYPE; + } + + if (exclude_filter & SO_FILTER_OB_STATE) { + switch (soops->filter_state) { + case SO_FILTER_OB_VISIBLE: + exclude_filter |= SO_FILTER_OB_STATE_VISIBLE; + break; + case SO_FILTER_OB_SELECTED: + exclude_filter |= SO_FILTER_OB_STATE_SELECTED; + break; + case SO_FILTER_OB_ACTIVE: + exclude_filter |= SO_FILTER_OB_STATE_ACTIVE; + break; + } + } + + if ((exclude_filter & SO_FILTER_ANY) == 0) { + exclude_filter &= ~(SO_FILTER_OB_STATE); + } + + return exclude_filter; +} + +static bool outliner_element_visible_get(ViewLayer *view_layer, TreeElement *te, const int exclude_filter) +{ + if ((exclude_filter & SO_FILTER_ENABLE) == 0) { + return true; + } + + TreeStoreElem *tselem = TREESTORE(te); + if ((tselem->type == 0) && (te->idcode == ID_OB)) { + if ((exclude_filter & SO_FILTER_NO_OBJECT)) { + return false; + } + + Object *ob = (Object *)tselem->id; + Base *base = (Base *)te->directdata; + BLI_assert((base == NULL) || (base->object == ob)); + + if (exclude_filter & SO_FILTER_OB_TYPE) { + switch (ob->type) { + case OB_MESH: + if (exclude_filter & SO_FILTER_NO_OB_MESH) { + return false; + } + break; + case OB_ARMATURE: + if (exclude_filter & SO_FILTER_NO_OB_ARMATURE) { + return false; + } + break; + case OB_EMPTY: + if (exclude_filter & SO_FILTER_NO_OB_EMPTY) { + return false; + } + break; + case OB_LAMP: + if (exclude_filter & SO_FILTER_NO_OB_LAMP) { + return false; + } + break; + case OB_CAMERA: + if (exclude_filter & SO_FILTER_NO_OB_CAMERA) { + return false; + } + break; + default: + if (exclude_filter & SO_FILTER_NO_OB_OTHERS) { + return false; + } + break; + } + } + + if (exclude_filter & SO_FILTER_OB_STATE) { + if (base == NULL) { + base = BKE_view_layer_base_find(view_layer, ob); + + if (base == NULL) { + return false; + } + } + + if (exclude_filter & SO_FILTER_OB_STATE_VISIBLE) { + if ((base->flag & BASE_VISIBLED) == 0) { + return false; + } + } + else if (exclude_filter & SO_FILTER_OB_STATE_SELECTED) { + if ((base->flag & BASE_SELECTED) == 0) { + return false; + } + } + else { + BLI_assert(exclude_filter & SO_FILTER_OB_STATE_ACTIVE); + if (base != BASACT(view_layer)) { + return false; + } + } + } + + if ((te->parent != NULL) && + (TREESTORE(te->parent)->type == 0) && (te->parent->idcode == ID_OB)) + { + if (exclude_filter & SO_FILTER_NO_CHILDREN) { + return false; + } + } + } + else if (te->parent != NULL && + TREESTORE(te->parent)->type == 0 && te->parent->idcode == ID_OB) + { + if (exclude_filter & SO_FILTER_NO_OB_CONTENT) { + return false; + } + } + + return true; +} + static bool outliner_filter_has_name(TreeElement *te, const char *name, int flags) { int fn_flag = 0; @@ -1761,31 +2062,25 @@ static bool outliner_filter_has_name(TreeElement *te, const char *name, int flag return fnmatch(name, te->name, fn_flag) == 0; } -static int outliner_filter_tree(SpaceOops *soops, ListBase *lb) +static int outliner_filter_subtree( + SpaceOops *soops, ViewLayer *view_layer, ListBase *lb, const char *search_string, const int exclude_filter) { - TreeElement *te, *ten; + TreeElement *te, *te_next; TreeStoreElem *tselem; - char search_buff[sizeof(((struct SpaceOops *)NULL)->search_string) + 2]; - char *search_string; - /* although we don't have any search string, we return true - * since the entire tree is ok then... - */ - if (soops->search_string[0] == 0) - return 1; + for (te = lb->first; te; te = te_next) { + te_next = te->next; - if (soops->search_flags & SO_FIND_COMPLETE) { - search_string = soops->search_string; - } - else { - /* Implicitly add heading/trailing wildcards if needed. */ - BLI_strncpy_ensure_pad(search_buff, soops->search_string, '*', sizeof(search_buff)); - search_string = search_buff; - } + if ((outliner_element_visible_get(view_layer, te, exclude_filter) == false)) { + outliner_free_tree_element(te, lb); + continue; + } + else if ((exclude_filter & SO_FILTER_SEARCH) == 0) { + /* Filter subtree too. */ + outliner_filter_subtree(soops, view_layer, &te->subtree, search_string, exclude_filter); + continue; + } - for (te = lb->first; te; te = ten) { - ten = te->next; - if (!outliner_filter_has_name(te, search_string, soops->search_flags)) { /* item isn't something we're looking for, but... * - if the subtree is expanded, check if there are any matches that can be easily found @@ -1794,35 +2089,60 @@ static int outliner_filter_tree(SpaceOops *soops, ListBase *lb) * so these can be safely ignored (i.e. the subtree can get freed) */ tselem = TREESTORE(te); - + /* flag as not a found item */ tselem->flag &= ~TSE_SEARCHMATCH; - if ((!TSELEM_OPEN(tselem, soops)) || outliner_filter_tree(soops, &te->subtree) == 0) { + if ((!TSELEM_OPEN(tselem, soops)) || + outliner_filter_subtree(soops, view_layer, &te->subtree, search_string, exclude_filter) == 0) + { outliner_free_tree_element(te, lb); } } else { tselem = TREESTORE(te); - + /* flag as a found item - we can then highlight it */ tselem->flag |= TSE_SEARCHMATCH; - + /* filter subtree too */ - outliner_filter_tree(soops, &te->subtree); + outliner_filter_subtree(soops, view_layer, &te->subtree, search_string, exclude_filter); } } - + /* if there are still items in the list, that means that there were still some matches */ return (BLI_listbase_is_empty(lb) == false); } +static void outliner_filter_tree(SpaceOops *soops, ViewLayer *view_layer) +{ + char search_buff[sizeof(((struct SpaceOops *)NULL)->search_string) + 2]; + char *search_string; + + const int exclude_filter = outliner_exclude_filter_get(soops); + + if (exclude_filter == 0) { + return; + } + + if (soops->search_flags & SO_FIND_COMPLETE) { + search_string = soops->search_string; + } + else { + /* Implicitly add heading/trailing wildcards if needed. */ + BLI_strncpy_ensure_pad(search_buff, soops->search_string, '*', sizeof(search_buff)); + search_string = search_buff; + } + + outliner_filter_subtree(soops, view_layer, &soops->tree, search_string, exclude_filter); +} + /* ======================================================= */ /* Main Tree Building API */ /* Main entry point for building the tree data-structure that the outliner represents */ // TODO: split each mode into its own function? -void outliner_build_tree(Main *mainvar, Scene *scene, ViewLayer *view_layer, SpaceOops *soops) +void outliner_build_tree(Main *mainvar, Scene *scene, ViewLayer *view_layer, SpaceOops *soops, ARegion *ar) { TreeElement *te = NULL, *ten; TreeStoreElem *tselem; @@ -1844,6 +2164,9 @@ void outliner_build_tree(Main *mainvar, Scene *scene, ViewLayer *view_layer, Spa if (soops->tree.first && (soops->storeflag & SO_TREESTORE_REDRAW)) return; + OutlinerTreeElementFocus focus; + outliner_store_scrolling_position(soops, ar, &focus); + outliner_free_tree(&soops->tree); outliner_storage_cleanup(soops); @@ -1921,27 +2244,6 @@ void outliner_build_tree(Main *mainvar, Scene *scene, ViewLayer *view_layer, Spa FOREACH_SCENE_OBJECT_END } } - else if (soops->outlinevis == SO_CUR_SCENE) { - - outliner_add_scene_contents(soops, &soops->tree, scene, NULL); - - FOREACH_SCENE_OBJECT(scene, ob) - { - outliner_add_element(soops, &soops->tree, ob, NULL, 0, 0); - } - FOREACH_SCENE_OBJECT_END - outliner_make_hierarchy(&soops->tree); - } - else if (soops->outlinevis == SO_VISIBLE) { - FOREACH_VISIBLE_BASE(view_layer, base) - { - ten = outliner_add_element(soops, &soops->tree, base->object, NULL, 0, 0); - ten->directdata = base; - - } - FOREACH_VISIBLE_BASE_END - outliner_make_hierarchy(&soops->tree); - } else if (soops->outlinevis == SO_GROUPS) { Group *group; for (group = mainvar->group.first; group; group = group->id.next) { @@ -1949,28 +2251,6 @@ void outliner_build_tree(Main *mainvar, Scene *scene, ViewLayer *view_layer, Spa outliner_make_hierarchy(&te->subtree); } } - else if (soops->outlinevis == SO_SAME_TYPE) { - Object *ob_active = OBACT(view_layer); - if (ob_active) { - FOREACH_SCENE_OBJECT(scene, ob) - { - if (ob->type == ob_active->type) { - outliner_add_element(soops, &soops->tree, ob, NULL, 0, 0); - } - } - FOREACH_SCENE_OBJECT_END - outliner_make_hierarchy(&soops->tree); - } - } - else if (soops->outlinevis == SO_SELECTED) { - FOREACH_SELECTED_BASE(view_layer, base) - { - ten = outliner_add_element(soops, &soops->tree, base->object, NULL, 0, 0); - ten->directdata = base; - } - FOREACH_SELECTED_BASE_END - outliner_make_hierarchy(&soops->tree); - } else if (soops->outlinevis == SO_SEQUENCE) { Sequence *seq; Editing *ed = BKE_sequencer_editing_get(scene, false); @@ -2023,10 +2303,29 @@ void outliner_build_tree(Main *mainvar, Scene *scene, ViewLayer *view_layer, Spa outliner_add_orphaned_datablocks(mainvar, soops); } else if (soops->outlinevis == SO_VIEW_LAYER) { - outliner_add_view_layer(soops, scene, view_layer); + if ((soops->filter & SO_FILTER_ENABLE) && (soops->filter & SO_FILTER_NO_COLLECTION)) { + for (Base *base = view_layer->object_bases.first; base; base = base->next) { + TreeElement *te_object = outliner_add_element(soops, &soops->tree, base->object, NULL, 0, 0); + te_object->directdata = base; + } + outliner_make_hierarchy(&soops->tree); + } + else { + outliner_add_view_layer(soops, scene, view_layer); + } } else if (soops->outlinevis == SO_COLLECTIONS) { - outliner_add_collections(soops, scene); + if ((soops->filter & SO_FILTER_ENABLE) && (soops->filter & SO_FILTER_NO_COLLECTION)) { + FOREACH_SCENE_OBJECT(scene, ob) + { + outliner_add_element(soops, &soops->tree, ob, NULL, 0, 0); + } + FOREACH_SCENE_OBJECT_END + outliner_make_hierarchy(&soops->tree); + } + else { + outliner_add_collections(soops, scene); + } } else { if (BASACT(view_layer)) { @@ -2038,7 +2337,9 @@ void outliner_build_tree(Main *mainvar, Scene *scene, ViewLayer *view_layer, Spa if ((soops->flag & SO_SKIP_SORT_ALPHA) == 0) { outliner_sort(&soops->tree); } - outliner_filter_tree(soops, &soops->tree); + + outliner_filter_tree(soops, view_layer); + outliner_restore_scrolling_position(soops, ar, &focus); BKE_main_id_clear_newpoins(mainvar); } |