2013-08-24 03:17:28 +00:00
|
|
|
/*
|
|
|
|
|
* This program is free software; you can redistribute it and/or
|
|
|
|
|
* modify it under the terms of the GNU General Public License
|
|
|
|
|
* as published by the Free Software Foundation; either version 2
|
|
|
|
|
* of the License, or (at your option) any later version.
|
|
|
|
|
*
|
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
|
*
|
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
|
* along with this program; if not, write to the Free Software Foundation,
|
|
|
|
|
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
|
|
|
*/
|
|
|
|
|
|
2019-02-18 08:08:12 +11:00
|
|
|
/** \file
|
|
|
|
|
* \ingroup bke
|
2013-08-24 03:17:28 +00:00
|
|
|
*
|
|
|
|
|
* Tree hash for the outliner space.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#include <stdlib.h>
|
Outliner: Delete all selected collections, not just active one
There were some issues with how we store outliner tree elements:
Apparently the only removable elements have been data-blocks so far.
When recreating the TreeElements, their TreeStoreElem instances were
mainly identified by their ID pointer. However non-data-blocks mostly
depend on an index. For collections, such an index isn't a reliable
measure though if we want to allow removing items. Depending on it for
identifying the TreeStoreElem instance would cause some quite noticeable
glitches (wrong highlights, two elements sharing highlight, etc).
For now I've solved that by actually removing the TreeStoreElem that
represents the removed element. A little limitation of this is that
after undoing the removal, some information might get lost, like
flags to store selection, or opened/closed state.
A better solution that would also fix this issue would be having a real
unique identifier for each non-data-block element, like an idname or even
its data-pointer. Not sure if we can get those to work reliable with
file read/write though, would have to investigate...
Also added a general Outliner tree traversal utility.
2017-02-28 20:37:14 +01:00
|
|
|
#include <string.h>
|
2013-08-24 03:17:28 +00:00
|
|
|
|
2013-08-23 20:35:00 +00:00
|
|
|
#include "BLI_ghash.h"
|
|
|
|
|
#include "BLI_mempool.h"
|
2020-03-19 09:33:03 +01:00
|
|
|
#include "BLI_utildefines.h"
|
2013-08-23 20:35:00 +00:00
|
|
|
|
|
|
|
|
#include "DNA_outliner_types.h"
|
|
|
|
|
|
2017-11-14 17:23:40 +11:00
|
|
|
#include "BKE_outliner_treehash.h"
|
|
|
|
|
|
2013-08-23 20:35:00 +00:00
|
|
|
#include "MEM_guardedalloc.h"
|
|
|
|
|
|
2015-04-07 11:25:42 +10:00
|
|
|
typedef struct TseGroup {
|
2013-08-23 20:35:00 +00:00
|
|
|
TreeStoreElem **elems;
|
2018-05-25 13:34:49 +02:00
|
|
|
int lastused;
|
2013-08-23 20:35:00 +00:00
|
|
|
int size;
|
|
|
|
|
int allocated;
|
|
|
|
|
} TseGroup;
|
|
|
|
|
|
|
|
|
|
/* Allocate structure for TreeStoreElements;
|
|
|
|
|
* Most of elements in treestore have no duplicates,
|
|
|
|
|
* so there is no need to preallocate memory for more than one pointer */
|
|
|
|
|
static TseGroup *tse_group_create(void)
|
|
|
|
|
{
|
|
|
|
|
TseGroup *tse_group = MEM_mallocN(sizeof(TseGroup), "TseGroup");
|
|
|
|
|
tse_group->elems = MEM_mallocN(sizeof(TreeStoreElem *), "TseGroupElems");
|
|
|
|
|
tse_group->size = 0;
|
|
|
|
|
tse_group->allocated = 1;
|
2018-05-25 13:34:49 +02:00
|
|
|
tse_group->lastused = 0;
|
2013-08-23 20:35:00 +00:00
|
|
|
return tse_group;
|
|
|
|
|
}
|
|
|
|
|
|
Outliner: Delete all selected collections, not just active one
There were some issues with how we store outliner tree elements:
Apparently the only removable elements have been data-blocks so far.
When recreating the TreeElements, their TreeStoreElem instances were
mainly identified by their ID pointer. However non-data-blocks mostly
depend on an index. For collections, such an index isn't a reliable
measure though if we want to allow removing items. Depending on it for
identifying the TreeStoreElem instance would cause some quite noticeable
glitches (wrong highlights, two elements sharing highlight, etc).
For now I've solved that by actually removing the TreeStoreElem that
represents the removed element. A little limitation of this is that
after undoing the removal, some information might get lost, like
flags to store selection, or opened/closed state.
A better solution that would also fix this issue would be having a real
unique identifier for each non-data-block element, like an idname or even
its data-pointer. Not sure if we can get those to work reliable with
file read/write though, would have to investigate...
Also added a general Outliner tree traversal utility.
2017-02-28 20:37:14 +01:00
|
|
|
static void tse_group_add_element(TseGroup *tse_group, TreeStoreElem *elem)
|
2013-08-23 20:35:00 +00:00
|
|
|
{
|
2014-08-14 11:07:33 +10:00
|
|
|
if (UNLIKELY(tse_group->size == tse_group->allocated)) {
|
2013-08-23 20:35:00 +00:00
|
|
|
tse_group->allocated *= 2;
|
|
|
|
|
tse_group->elems = MEM_reallocN(tse_group->elems,
|
|
|
|
|
sizeof(TreeStoreElem *) * tse_group->allocated);
|
|
|
|
|
}
|
|
|
|
|
tse_group->elems[tse_group->size] = elem;
|
|
|
|
|
tse_group->size++;
|
|
|
|
|
}
|
|
|
|
|
|
Outliner: Delete all selected collections, not just active one
There were some issues with how we store outliner tree elements:
Apparently the only removable elements have been data-blocks so far.
When recreating the TreeElements, their TreeStoreElem instances were
mainly identified by their ID pointer. However non-data-blocks mostly
depend on an index. For collections, such an index isn't a reliable
measure though if we want to allow removing items. Depending on it for
identifying the TreeStoreElem instance would cause some quite noticeable
glitches (wrong highlights, two elements sharing highlight, etc).
For now I've solved that by actually removing the TreeStoreElem that
represents the removed element. A little limitation of this is that
after undoing the removal, some information might get lost, like
flags to store selection, or opened/closed state.
A better solution that would also fix this issue would be having a real
unique identifier for each non-data-block element, like an idname or even
its data-pointer. Not sure if we can get those to work reliable with
file read/write though, would have to investigate...
Also added a general Outliner tree traversal utility.
2017-02-28 20:37:14 +01:00
|
|
|
static void tse_group_remove_element(TseGroup *tse_group, TreeStoreElem *elem)
|
|
|
|
|
{
|
|
|
|
|
int min_allocated = MAX2(1, tse_group->allocated / 2);
|
|
|
|
|
BLI_assert(tse_group->allocated == 1 || (tse_group->allocated % 2) == 0);
|
|
|
|
|
|
|
|
|
|
tse_group->size--;
|
|
|
|
|
BLI_assert(tse_group->size >= 0);
|
|
|
|
|
for (int i = 0; i < tse_group->size; i++) {
|
|
|
|
|
if (tse_group->elems[i] == elem) {
|
|
|
|
|
memcpy(tse_group->elems[i],
|
|
|
|
|
tse_group->elems[i + 1],
|
|
|
|
|
(tse_group->size - (i + 1)) * sizeof(TreeStoreElem *));
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (UNLIKELY(tse_group->size > 0 && tse_group->size <= min_allocated)) {
|
|
|
|
|
tse_group->allocated = min_allocated;
|
|
|
|
|
tse_group->elems = MEM_reallocN(tse_group->elems,
|
|
|
|
|
sizeof(TreeStoreElem *) * tse_group->allocated);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2013-08-23 20:35:00 +00:00
|
|
|
static void tse_group_free(TseGroup *tse_group)
|
|
|
|
|
{
|
|
|
|
|
MEM_freeN(tse_group->elems);
|
|
|
|
|
MEM_freeN(tse_group);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static unsigned int tse_hash(const void *ptr)
|
|
|
|
|
{
|
|
|
|
|
const TreeStoreElem *tse = ptr;
|
2014-04-26 03:06:36 +10:00
|
|
|
union {
|
|
|
|
|
short h_pair[2];
|
|
|
|
|
unsigned int u_int;
|
|
|
|
|
} hash;
|
|
|
|
|
|
2013-08-23 20:35:00 +00:00
|
|
|
BLI_assert(tse->type || !tse->nr);
|
2014-04-26 03:06:36 +10:00
|
|
|
|
|
|
|
|
hash.h_pair[0] = tse->type;
|
|
|
|
|
hash.h_pair[1] = tse->nr;
|
|
|
|
|
|
|
|
|
|
hash.u_int ^= BLI_ghashutil_ptrhash(tse->id);
|
|
|
|
|
|
|
|
|
|
return hash.u_int;
|
2013-08-23 20:35:00 +00:00
|
|
|
}
|
|
|
|
|
|
2014-09-25 06:15:52 +10:00
|
|
|
static bool tse_cmp(const void *a, const void *b)
|
2013-08-23 20:35:00 +00:00
|
|
|
{
|
|
|
|
|
const TreeStoreElem *tse_a = a;
|
|
|
|
|
const TreeStoreElem *tse_b = b;
|
|
|
|
|
return tse_a->type != tse_b->type || tse_a->nr != tse_b->nr || tse_a->id != tse_b->id;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void fill_treehash(void *treehash, BLI_mempool *treestore)
|
|
|
|
|
{
|
|
|
|
|
TreeStoreElem *tselem;
|
|
|
|
|
BLI_mempool_iter iter;
|
|
|
|
|
BLI_mempool_iternew(treestore, &iter);
|
2015-05-15 15:52:24 +02:00
|
|
|
|
|
|
|
|
BLI_assert(treehash);
|
|
|
|
|
|
2013-08-23 20:35:00 +00:00
|
|
|
while ((tselem = BLI_mempool_iterstep(&iter))) {
|
2015-04-07 11:01:47 +10:00
|
|
|
BKE_outliner_treehash_add_element(treehash, tselem);
|
2013-08-23 20:35:00 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-04-07 11:01:47 +10:00
|
|
|
void *BKE_outliner_treehash_create_from_treestore(BLI_mempool *treestore)
|
2013-08-23 20:35:00 +00:00
|
|
|
{
|
2018-02-15 23:36:11 +11:00
|
|
|
GHash *treehash = BLI_ghash_new_ex(tse_hash, tse_cmp, "treehash", BLI_mempool_len(treestore));
|
2013-08-23 20:35:00 +00:00
|
|
|
fill_treehash(treehash, treestore);
|
|
|
|
|
return treehash;
|
|
|
|
|
}
|
|
|
|
|
|
2013-08-27 21:30:33 +00:00
|
|
|
static void free_treehash_group(void *key)
|
|
|
|
|
{
|
2013-08-23 20:35:00 +00:00
|
|
|
tse_group_free(key);
|
|
|
|
|
}
|
|
|
|
|
|
2018-05-28 17:07:52 +02:00
|
|
|
void BKE_outliner_treehash_clear_used(void *treehash)
|
|
|
|
|
{
|
|
|
|
|
GHashIterator gh_iter;
|
|
|
|
|
|
|
|
|
|
GHASH_ITER (gh_iter, treehash) {
|
|
|
|
|
TseGroup *group = BLI_ghashIterator_getValue(&gh_iter);
|
|
|
|
|
group->lastused = 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-04-07 11:01:47 +10:00
|
|
|
void *BKE_outliner_treehash_rebuild_from_treestore(void *treehash, BLI_mempool *treestore)
|
2013-08-23 20:35:00 +00:00
|
|
|
{
|
2015-05-15 15:52:24 +02:00
|
|
|
BLI_assert(treehash);
|
|
|
|
|
|
2018-02-15 23:36:11 +11:00
|
|
|
BLI_ghash_clear_ex(treehash, NULL, free_treehash_group, BLI_mempool_len(treestore));
|
2013-08-23 20:35:00 +00:00
|
|
|
fill_treehash(treehash, treestore);
|
|
|
|
|
return treehash;
|
|
|
|
|
}
|
|
|
|
|
|
2015-04-07 11:01:47 +10:00
|
|
|
void BKE_outliner_treehash_add_element(void *treehash, TreeStoreElem *elem)
|
2013-08-23 20:35:00 +00:00
|
|
|
{
|
2015-04-06 20:03:49 +10:00
|
|
|
TseGroup *group;
|
|
|
|
|
void **val_p;
|
|
|
|
|
|
|
|
|
|
if (!BLI_ghash_ensure_p(treehash, elem, &val_p)) {
|
|
|
|
|
*val_p = tse_group_create();
|
2013-08-23 20:35:00 +00:00
|
|
|
}
|
2015-04-06 20:03:49 +10:00
|
|
|
group = *val_p;
|
2018-05-28 17:07:52 +02:00
|
|
|
group->lastused = 0;
|
Outliner: Delete all selected collections, not just active one
There were some issues with how we store outliner tree elements:
Apparently the only removable elements have been data-blocks so far.
When recreating the TreeElements, their TreeStoreElem instances were
mainly identified by their ID pointer. However non-data-blocks mostly
depend on an index. For collections, such an index isn't a reliable
measure though if we want to allow removing items. Depending on it for
identifying the TreeStoreElem instance would cause some quite noticeable
glitches (wrong highlights, two elements sharing highlight, etc).
For now I've solved that by actually removing the TreeStoreElem that
represents the removed element. A little limitation of this is that
after undoing the removal, some information might get lost, like
flags to store selection, or opened/closed state.
A better solution that would also fix this issue would be having a real
unique identifier for each non-data-block element, like an idname or even
its data-pointer. Not sure if we can get those to work reliable with
file read/write though, would have to investigate...
Also added a general Outliner tree traversal utility.
2017-02-28 20:37:14 +01:00
|
|
|
tse_group_add_element(group, elem);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void BKE_outliner_treehash_remove_element(void *treehash, TreeStoreElem *elem)
|
|
|
|
|
{
|
|
|
|
|
TseGroup *group = BLI_ghash_lookup(treehash, elem);
|
|
|
|
|
|
|
|
|
|
BLI_assert(group != NULL);
|
|
|
|
|
if (group->size <= 1) {
|
|
|
|
|
/* one element -> remove group completely */
|
|
|
|
|
BLI_ghash_remove(treehash, elem, NULL, free_treehash_group);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
tse_group_remove_element(group, elem);
|
|
|
|
|
}
|
2013-08-23 20:35:00 +00:00
|
|
|
}
|
|
|
|
|
|
2015-04-07 11:01:47 +10:00
|
|
|
static TseGroup *BKE_outliner_treehash_lookup_group(GHash *th, short type, short nr, struct ID *id)
|
2013-08-23 20:35:00 +00:00
|
|
|
{
|
|
|
|
|
TreeStoreElem tse_template;
|
|
|
|
|
tse_template.type = type;
|
|
|
|
|
tse_template.nr = type ? nr : 0; /* we're picky! :) */
|
|
|
|
|
tse_template.id = id;
|
2015-05-15 15:52:24 +02:00
|
|
|
|
|
|
|
|
BLI_assert(th);
|
|
|
|
|
|
2013-08-23 20:35:00 +00:00
|
|
|
return BLI_ghash_lookup(th, &tse_template);
|
|
|
|
|
}
|
|
|
|
|
|
2015-04-07 11:01:47 +10:00
|
|
|
TreeStoreElem *BKE_outliner_treehash_lookup_unused(void *treehash,
|
|
|
|
|
short type,
|
|
|
|
|
short nr,
|
|
|
|
|
struct ID *id)
|
2013-08-23 20:35:00 +00:00
|
|
|
{
|
2015-05-15 15:52:24 +02:00
|
|
|
TseGroup *group;
|
|
|
|
|
|
|
|
|
|
BLI_assert(treehash);
|
|
|
|
|
|
|
|
|
|
group = BKE_outliner_treehash_lookup_group(treehash, type, nr, id);
|
2013-08-23 20:35:00 +00:00
|
|
|
if (group) {
|
2018-05-25 13:34:49 +02:00
|
|
|
/* Find unused element, with optimization to start from previously
|
|
|
|
|
* found element assuming we do repeated lookups. */
|
|
|
|
|
int size = group->size;
|
|
|
|
|
int offset = group->lastused;
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < size; i++, offset++) {
|
|
|
|
|
if (offset >= size) {
|
|
|
|
|
offset = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!group->elems[offset]->used) {
|
|
|
|
|
group->lastused = offset;
|
|
|
|
|
return group->elems[offset];
|
2013-08-23 20:35:00 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
2015-04-07 11:01:47 +10:00
|
|
|
TreeStoreElem *BKE_outliner_treehash_lookup_any(void *treehash,
|
|
|
|
|
short type,
|
|
|
|
|
short nr,
|
|
|
|
|
struct ID *id)
|
2013-08-23 20:35:00 +00:00
|
|
|
{
|
2015-05-15 15:52:24 +02:00
|
|
|
TseGroup *group;
|
|
|
|
|
|
|
|
|
|
BLI_assert(treehash);
|
|
|
|
|
|
|
|
|
|
group = BKE_outliner_treehash_lookup_group(treehash, type, nr, id);
|
2013-08-23 20:35:00 +00:00
|
|
|
return group ? group->elems[0] : NULL;
|
|
|
|
|
}
|
|
|
|
|
|
2015-04-07 11:01:47 +10:00
|
|
|
void BKE_outliner_treehash_free(void *treehash)
|
2013-08-23 20:35:00 +00:00
|
|
|
{
|
2015-05-15 15:52:24 +02:00
|
|
|
BLI_assert(treehash);
|
|
|
|
|
|
2013-08-23 20:35:00 +00:00
|
|
|
BLI_ghash_free(treehash, NULL, free_treehash_group);
|
|
|
|
|
}
|