Changeset View
Changeset View
Standalone View
Standalone View
source/blender/editors/space_outliner/outliner_tree.c
| Show First 20 Lines • Show All 1,845 Lines • ▼ Show 20 Lines | if (comp > 0) { | ||||
| return 1; | return 1; | ||||
| } | } | ||||
| if (comp < 0) { | if (comp < 0) { | ||||
| return -1; | return -1; | ||||
| } | } | ||||
| return 0; | return 0; | ||||
| } | } | ||||
| /* this is nice option for later? doesn't look too useful... */ | static int treesort_type(const void *v1, const void *v2) | ||||
| #if 0 | |||||
| static int treesort_obtype_alpha(const void *v1, const void *v2) | |||||
| { | { | ||||
| const tTreeSort *x1 = v1, *x2 = v2; | const tTreeSort *x1 = v1; | ||||
| const tTreeSort *x2 = v2; | |||||
| /* first put objects last (hierarchy) */ | |||||
| if (x1->idcode == ID_OB && x2->idcode != ID_OB) { | |||||
| return 1; | |||||
| } | |||||
| else if (x2->idcode == ID_OB && x1->idcode != ID_OB) { | |||||
| return -1; | |||||
| } | |||||
| else { | |||||
| /* 2nd we check ob type */ | |||||
| if (x1->idcode == ID_OB && x2->idcode == ID_OB) { | |||||
| if (((Object *)x1->id)->type > ((Object *)x2->id)->type) { | if (((Object *)x1->id)->type > ((Object *)x2->id)->type) { | ||||
| return 1; | return 1; | ||||
| } | } | ||||
| else if (((Object *)x1->id)->type > ((Object *)x2->id)->type) { | else if (((Object *)x2->id)->type > ((Object *)x1->id)->type) { | ||||
| return -1; | return -1; | ||||
| } | } | ||||
| else { | else { | ||||
| return 0; | /* Compare by name */ | ||||
| return treesort_alpha(v1, v2); | |||||
| } | } | ||||
| } | } | ||||
| else { | |||||
| int comp = BLI_strcasecmp_natural(x1->name, x2->name); | |||||
| if (comp > 0) { | /* TODO (Nathan): Should children still be sorted? */ | ||||
| return 1; | static void outliner_collections_children_sort(ListBase *lb) | ||||
| } | |||||
| else if (comp < 0) { | |||||
| return -1; | |||||
| } | |||||
| return 0; | |||||
| } | |||||
| } | |||||
| } | |||||
| #endif | |||||
| /* sort happens on each subtree individual */ | |||||
| static void outliner_sort(ListBase *lb) | |||||
| { | { | ||||
| TreeElement *te; | TreeElement *te; | ||||
| TreeStoreElem *tselem; | TreeStoreElem *tselem; | ||||
| te = lb->last; | te = lb->last; | ||||
| if (te == NULL) { | if (te == NULL) { | ||||
| return; | return; | ||||
| } | } | ||||
| tselem = TREESTORE(te); | tselem = TREESTORE(te); | ||||
| /* sorting rules; only object lists, ID lists, or deformgroups */ | /* Sorting rules: only object lists. */ | ||||
| if (ELEM(tselem->type, TSE_DEFGROUP, TSE_ID_BASE) || | if (tselem->type == 0 && te->idcode == ID_OB) { | ||||
| (tselem->type == 0 && te->idcode == ID_OB)) { | |||||
| int totelem = BLI_listbase_count(lb); | int totelem = BLI_listbase_count(lb); | ||||
| if (totelem > 1) { | if (totelem > 1) { | ||||
| tTreeSort *tear = MEM_mallocN(totelem * sizeof(tTreeSort), "tree sort array"); | tTreeSort *tear = MEM_mallocN(totelem * sizeof(tTreeSort), "tree sort array"); | ||||
| tTreeSort *tp = tear; | tTreeSort *tp = tear; | ||||
| int skip = 0; | |||||
| for (te = lb->first; te; te = te->next, tp++) { | for (te = lb->first; te; te = te->next, tp++) { | ||||
| tselem = TREESTORE(te); | tselem = TREESTORE(te); | ||||
| tp->te = te; | tp->te = te; | ||||
| tp->name = te->name; | tp->name = te->name; | ||||
| tp->idcode = te->idcode; | tp->idcode = te->idcode; | ||||
| if (tselem->type && tselem->type != TSE_DEFGROUP) { | |||||
| tp->idcode = 0; /* Don't sort this. */ | |||||
| } | |||||
| if (tselem->type == TSE_ID_BASE) { | |||||
| tp->idcode = 1; /* Do sort this. */ | |||||
| } | |||||
| tp->id = tselem->id; | tp->id = tselem->id; | ||||
| } | } | ||||
| /* just sort alphabetically */ | qsort(tear, totelem, sizeof(tTreeSort), treesort_child_not_in_collection); | ||||
| if (tear->idcode == 1) { | |||||
| qsort(tear, totelem, sizeof(tTreeSort), treesort_alpha); | |||||
| } | |||||
| else { | |||||
| /* keep beginning of list */ | |||||
| for (tp = tear, skip = 0; skip < totelem; skip++, tp++) { | |||||
| if (tp->idcode) { | |||||
| break; | |||||
| } | |||||
| } | |||||
| if (skip < totelem) { | |||||
| qsort(tear + skip, totelem - skip, sizeof(tTreeSort), treesort_alpha_ob); | |||||
| } | |||||
| } | |||||
| BLI_listbase_clear(lb); | BLI_listbase_clear(lb); | ||||
| tp = tear; | tp = tear; | ||||
| while (totelem--) { | while (totelem--) { | ||||
| BLI_addtail(lb, tp->te); | BLI_addtail(lb, tp->te); | ||||
| tp++; | tp++; | ||||
| } | } | ||||
| MEM_freeN(tear); | MEM_freeN(tear); | ||||
| } | } | ||||
| } | } | ||||
| for (te = lb->first; te; te = te->next) { | for (te = lb->first; te; te = te->next) { | ||||
| outliner_sort(&te->subtree); | outliner_collections_children_sort(&te->subtree); | ||||
| } | } | ||||
| } | } | ||||
| static void outliner_collections_children_sort(ListBase *lb) | static bool outliner_is_sort_item(TreeElement *te) | ||||
| { | { | ||||
| TreeElement *te; | TreeStoreElem *tselem = TREESTORE(te); | ||||
| TreeStoreElem *tselem; | return (tselem->type == 0 && te->idcode == ID_OB) || outliner_is_collection_tree_element(te) || | ||||
| (ELEM(tselem->type, TSE_BONE, TSE_EBONE, TSE_POSE_CHANNEL)); | |||||
| } | |||||
| te = lb->last; | /* Sort collections and objects separately on each outliner subtree. */ | ||||
| if (te == NULL) { | static void outliner_tree_sort(SpaceOutliner *space_outliner, ListBase *tree) | ||||
| { | |||||
| if (BLI_listbase_is_empty(tree)) { | |||||
| return; | return; | ||||
| } | } | ||||
| tselem = TREESTORE(te); | |||||
| /* Sorting rules: only object lists. */ | TreeElement *te_last = tree->last; | ||||
| if (tselem->type == 0 && te->idcode == ID_OB) { | TreeStoreElem *tselem; | ||||
| int totelem = BLI_listbase_count(lb); | |||||
| if (totelem > 1) { | /* Only sort collections and objects */ | ||||
| tTreeSort *tear = MEM_mallocN(totelem * sizeof(tTreeSort), "tree sort array"); | if (outliner_is_sort_item(te_last)) { | ||||
| tTreeSort *tp = tear; | int num_elems = BLI_listbase_count(tree); | ||||
| if (num_elems > 1) { | |||||
| tTreeSort *tree_sort = MEM_mallocN(num_elems * sizeof(tTreeSort), "tree sort array"); | |||||
| tTreeSort *tree_sort_p = tree_sort; | |||||
| int num_collections = 0; | |||||
| for (te = lb->first; te; te = te->next, tp++) { | for (TreeElement *te = tree->first; te; te = te->next, tree_sort_p++) { | ||||
| tselem = TREESTORE(te); | tselem = TREESTORE(te); | ||||
| tp->te = te; | tree_sort_p->te = te; | ||||
| tp->name = te->name; | tree_sort_p->name = te->name; | ||||
| tp->idcode = te->idcode; | tree_sort_p->idcode = te->idcode; | ||||
| tp->id = tselem->id; | |||||
| if (tselem->type && tselem->type != TSE_DEFGROUP) { | |||||
| tree_sort_p->idcode = 0; /* Don't sort this. */ | |||||
| } | |||||
| if (tselem->type == TSE_ID_BASE) { | |||||
| tree_sort_p->idcode = 1; /* Do sort this. */ | |||||
| } | } | ||||
| qsort(tear, totelem, sizeof(tTreeSort), treesort_child_not_in_collection); | if (outliner_is_collection_tree_element(te)) { | ||||
| num_collections++; | |||||
| } | |||||
| BLI_listbase_clear(lb); | tree_sort_p->id = tselem->id; | ||||
| tp = tear; | } | ||||
| while (totelem--) { | |||||
| BLI_addtail(lb, tp->te); | /* Skip beginning of list */ | ||||
| tp++; | int skip = 0; | ||||
| if (!outliner_is_sort_item(tree_sort->te)) { | |||||
| for (tree_sort_p = tree_sort, skip = 0; skip < num_elems; skip++, tree_sort_p++) { | |||||
| if (outliner_is_sort_item(tree_sort_p->te)) { | |||||
| break; | |||||
| } | } | ||||
| MEM_freeN(tear); | |||||
| } | } | ||||
| } | } | ||||
| for (te = lb->first; te; te = te->next) { | /* Sort collections. */ | ||||
| outliner_collections_children_sort(&te->subtree); | if (num_collections > 0) { | ||||
| switch (space_outliner->sort_method) { | |||||
| case SO_SORT_ALPHA: | |||||
| qsort(tree_sort + skip, num_collections - skip, sizeof(tTreeSort), treesort_alpha); | |||||
| break; | |||||
| case SO_SORT_TYPE: | |||||
| qsort(tree_sort + skip, num_collections - skip, sizeof(tTreeSort), treesort_alpha); | |||||
| break; | |||||
| } | |||||
| } | |||||
| /* Sort objects. */ | |||||
| if (num_elems - num_collections - skip > 0) { | |||||
| switch (space_outliner->sort_method) { | |||||
| case SO_SORT_ALPHA: | |||||
| qsort(tree_sort + skip + num_collections, | |||||
| num_elems - num_collections - skip, | |||||
| sizeof(tTreeSort), | |||||
| treesort_alpha); | |||||
| break; | |||||
| case SO_SORT_TYPE: | |||||
| qsort(tree_sort + skip + num_collections, | |||||
| num_elems - num_collections - skip, | |||||
| sizeof(tTreeSort), | |||||
| treesort_type); | |||||
| break; | |||||
| } | |||||
| } | |||||
| /* Copy sorted list back into tree */ | |||||
| BLI_listbase_clear(tree); | |||||
| tree_sort_p = tree_sort; | |||||
| while (num_elems--) { | |||||
| BLI_addtail(tree, tree_sort_p->te); | |||||
| tree_sort_p++; | |||||
| } | |||||
| MEM_freeN(tree_sort); | |||||
| } | |||||
| } | |||||
Severin: There's quite some logic here, some lower level some higher level. So I'd suggest refactoring… | |||||
| LISTBASE_FOREACH (TreeElement *, te, tree) { | |||||
| outliner_tree_sort(space_outliner, &te->subtree); | |||||
| } | |||||
| } | |||||
| #if 0 | |||||
| void f(SpaceOutliner *space_outliner) | |||||
| { | |||||
| if (space_outliner->sort_method == SO_SORT_ALPHA) { | |||||
| outliner_sort(tree); | |||||
| } | } | ||||
| else if (space_outliner->sort_method == SO_SORT_TYPE) { | |||||
| } | } | ||||
| else if ((space_outliner->filter & SO_FILTER_NO_CHILDREN) == 0) { | |||||
| /* We group the children that are in the collection before the ones that are not. | |||||
| * This way we can try to draw them in a different style altogether. | |||||
| * We also have to respect the original order of the elements in case alphabetical | |||||
| * sorting is not enabled. This keep object data and modifiers before its children. */ | |||||
| outliner_collections_children_sort(tree); | |||||
| } | |||||
| } | |||||
| #endif | |||||
| /* Filtering ----------------------------------------------- */ | /* Filtering ----------------------------------------------- */ | ||||
| typedef struct OutlinerTreeElementFocus { | typedef struct OutlinerTreeElementFocus { | ||||
| TreeStoreElem *tselem; | TreeStoreElem *tselem; | ||||
| int ys; | int ys; | ||||
| } OutlinerTreeElementFocus; | } OutlinerTreeElementFocus; | ||||
| ▲ Show 20 Lines • Show All 607 Lines • ▼ Show 20 Lines | else { | ||||
| outliner_make_object_parent_hierarchy_collections(space_outliner, | outliner_make_object_parent_hierarchy_collections(space_outliner, | ||||
| object_tree_elements_hash); | object_tree_elements_hash); | ||||
| outliner_object_tree_elements_lookup_free(object_tree_elements_hash); | outliner_object_tree_elements_lookup_free(object_tree_elements_hash); | ||||
| BLI_ghash_free(object_tree_elements_hash, NULL, NULL); | BLI_ghash_free(object_tree_elements_hash, NULL, NULL); | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| if ((space_outliner->flag & SO_SKIP_SORT_ALPHA) == 0) { | if (space_outliner->sort_method != SO_SORT_CUSTOM) { | ||||
| outliner_sort(&space_outliner->tree); | outliner_tree_sort(space_outliner, &space_outliner->tree); | ||||
| } | |||||
| else if ((space_outliner->filter & SO_FILTER_NO_CHILDREN) == 0) { | |||||
| /* We group the children that are in the collection before the ones that are not. | |||||
| * This way we can try to draw them in a different style altogether. | |||||
| * We also have to respect the original order of the elements in case alphabetical | |||||
| * sorting is not enabled. This keep object data and modifiers before its children. */ | |||||
| outliner_collections_children_sort(&space_outliner->tree); | |||||
| } | } | ||||
| outliner_filter_tree(space_outliner, view_layer); | outliner_filter_tree(space_outliner, view_layer); | ||||
| outliner_restore_scrolling_position(space_outliner, region, &focus); | outliner_restore_scrolling_position(space_outliner, region, &focus); | ||||
| BKE_main_id_clear_newpoins(mainvar); | BKE_main_id_clear_newpoins(mainvar); | ||||
| } | } | ||||
There's quite some logic here, some lower level some higher level. So I'd suggest refactoring this into one or more functions, so that you can roughly understand the steps outliner_tree_sort() is taking just by reading a few function names, and without getting distracted by all the details.