Blender V4.5
outliner_utils.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2017 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
8
9#include <algorithm>
10#include <cstring>
11
12#include "BLI_listbase.h"
13#include "BLI_utildefines.h"
14
15#include "DNA_action_types.h"
16#include "DNA_screen_types.h"
17#include "DNA_space_types.h"
18
19#include "RNA_access.hh"
20#include "RNA_prototypes.hh"
21
22#include "BKE_context.hh"
23#include "BKE_layer.hh"
24#include "BKE_object.hh"
25
26#include "ED_outliner.hh"
27#include "ED_screen.hh"
28
29#include "UI_interface.hh"
30#include "UI_view2d.hh"
31
32#include "outliner_intern.hh"
33#include "tree/tree_display.hh"
34
35namespace blender::ed::outliner {
36
37/* -------------------------------------------------------------------- */
40
42{
43 memset(tvc, 0, sizeof(*tvc));
44
45 /* Scene level. */
46 tvc->scene = CTX_data_scene(C);
49
50 /* Objects. */
53 if (tvc->obact != nullptr) {
54 tvc->ob_edit = OBEDIT_FROM_OBACT(tvc->obact);
55
56 if ((tvc->obact->type == OB_ARMATURE) ||
57 /* This could be made into its own function. */
58 ((tvc->obact->type == OB_MESH) && tvc->obact->mode & OB_MODE_WEIGHT_PAINT))
59 {
61 }
62 }
63}
64
66
68 const ListBase *tree,
69 float view_co_y)
70{
71 LISTBASE_FOREACH (TreeElement *, te_iter, tree) {
72 if (view_co_y < (te_iter->ys + UI_UNIT_Y)) {
73 if (view_co_y >= te_iter->ys) {
74 /* co_y is inside this element */
75 return te_iter;
76 }
77
78 if (BLI_listbase_is_empty(&te_iter->subtree) ||
79 !TSELEM_OPEN(TREESTORE(te_iter), space_outliner))
80 {
81 /* No need for recursion. */
82 continue;
83 }
84
85 /* If the coordinate is lower than the next element, we can continue with that one and skip
86 * recursion too. */
87 const TreeElement *te_next = te_iter->next;
88 if (te_next && (view_co_y < (te_next->ys + UI_UNIT_Y))) {
89 continue;
90 }
91
92 /* co_y is lower than current element (but not lower than the next one), possibly inside
93 * children */
94 TreeElement *te_sub = outliner_find_item_at_y(space_outliner, &te_iter->subtree, view_co_y);
95 if (te_sub) {
96 return te_sub;
97 }
98 }
99 }
100
101 return nullptr;
102}
103
105 float view_co_x,
106 bool *r_is_merged_icon)
107{
108 TreeElement *child_te = static_cast<TreeElement *>(parent_te->subtree.first);
109
110 while (child_te) {
111 const bool over_element = (view_co_x > child_te->xs) && (view_co_x < child_te->xend);
112 if ((child_te->flag & TE_ICONROW) && over_element) {
113 return child_te;
114 }
115 if ((child_te->flag & TE_ICONROW_MERGED) && over_element) {
116 if (r_is_merged_icon) {
117 *r_is_merged_icon = true;
118 }
119 return child_te;
120 }
121
123 child_te, view_co_x, r_is_merged_icon);
124 if (te != child_te) {
125 return te;
126 }
127
128 child_te = child_te->next;
129 }
130
131 /* return parent if no child is hovered */
132 return (TreeElement *)parent_te;
133}
134
136 TreeElement *parent_te,
137 float view_co_x,
138 bool *r_is_merged_icon,
139 bool *r_is_over_icon)
140{
141 TreeStoreElem *parent_tselem = TREESTORE(parent_te);
142 TreeElement *te = parent_te;
143
144 /* If parent_te is opened, or it is a ViewLayer, it doesn't show children in row. */
145 if (!TSELEM_OPEN(parent_tselem, space_outliner) && parent_tselem->type != TSE_R_LAYER) {
146 te = outliner_find_item_at_x_in_row_recursive(parent_te, view_co_x, r_is_merged_icon);
147 }
148
149 if ((te != parent_te) || outliner_item_is_co_over_icon(parent_te, view_co_x)) {
150 *r_is_over_icon = true;
151 }
152
153 return te;
154}
155
157{
158 LISTBASE_FOREACH (TreeElement *, te, lb) {
159 if (te->store_elem == store_elem) {
160 return te;
161 }
162 TreeElement *tes = outliner_find_tree_element(&te->subtree, store_elem);
163 if (tes) {
164 return tes;
165 }
166 }
167 return nullptr;
168}
169
171 TreeElement *parent_te,
172 const TreeElement *child_te)
173{
174 LISTBASE_FOREACH (TreeElement *, te, lb) {
175 if (te == child_te) {
176 return parent_te;
177 }
178
179 TreeElement *find_te = outliner_find_parent_element(&te->subtree, te, child_te);
180 if (find_te) {
181 return find_te;
182 }
183 }
184 return nullptr;
185}
186
187TreeElement *outliner_find_id(SpaceOutliner *space_outliner, ListBase *lb, const ID *id)
188{
189 LISTBASE_FOREACH (TreeElement *, te, lb) {
190 TreeStoreElem *tselem = TREESTORE(te);
191 if (tselem->type == TSE_SOME_ID) {
192 if (tselem->id == id) {
193 return te;
194 }
195 }
196
197 TreeElement *tes = outliner_find_id(space_outliner, &te->subtree, id);
198 if (tes) {
199 return tes;
200 }
201 }
202 return nullptr;
203}
204
206{
207 LISTBASE_FOREACH (TreeElement *, te, lb) {
208 if (te->directdata == pchan) {
209 return te;
210 }
211
212 TreeStoreElem *tselem = TREESTORE(te);
213 if (ELEM(tselem->type, TSE_POSE_BASE, TSE_POSE_CHANNEL)) {
214 TreeElement *tes = outliner_find_posechannel(&te->subtree, pchan);
215 if (tes) {
216 return tes;
217 }
218 }
219 }
220 return nullptr;
221}
222
224{
225 LISTBASE_FOREACH (TreeElement *, te, lb) {
226 if (te->directdata == ebone) {
227 return te;
228 }
229
230 TreeStoreElem *tselem = TREESTORE(te);
231 if (ELEM(tselem->type, TSE_SOME_ID, TSE_EBONE)) {
232 TreeElement *tes = outliner_find_editbone(&te->subtree, ebone);
233 if (tes) {
234 return tes;
235 }
236 }
237 }
238 return nullptr;
239}
240
242{
243 TreeStoreElem *tselem;
244 te = te->parent;
245
246 while (te) {
247 tselem = TREESTORE(te);
248 if ((tselem->type == TSE_SOME_ID) && (te->idcode == idcode)) {
249 return te;
250 }
251 te = te->parent;
252 }
253 return nullptr;
254}
255
257{
258 TreeElement *search_te;
259 TreeStoreElem *tselem;
260
261 search_te = outliner_search_back_te(te, idcode);
262 if (search_te) {
263 tselem = TREESTORE(search_te);
264 return tselem->id;
265 }
266 return nullptr;
267}
268
269bool outliner_tree_traverse(const SpaceOutliner *space_outliner,
270 ListBase *tree,
271 int filter_te_flag,
272 int filter_tselem_flag,
274 void *customdata)
275{
276 for (TreeElement *te = static_cast<TreeElement *>(tree->first), *te_next; te; te = te_next) {
278 /* in case te is freed in callback */
279 TreeStoreElem *tselem = TREESTORE(te);
280 ListBase subtree = te->subtree;
281 te_next = te->next;
282
283 if (filter_te_flag && (te->flag & filter_te_flag) == 0) {
284 /* skip */
285 }
286 else if (filter_tselem_flag && (tselem->flag & filter_tselem_flag) == 0) {
287 /* skip */
288 }
289 else {
290 func_retval = func(te, customdata);
291 }
292 /* Don't access te or tselem from now on! Might've been freed... */
293
294 if (func_retval == TRAVERSE_BREAK) {
295 return false;
296 }
297
298 if (func_retval == TRAVERSE_SKIP_CHILDS) {
299 /* skip */
300 }
301 else if (!outliner_tree_traverse(
302 space_outliner, &subtree, filter_te_flag, filter_tselem_flag, func, customdata))
303 {
304 return false;
305 }
306 }
307
308 return true;
309}
310
311float outliner_right_columns_width(const SpaceOutliner *space_outliner)
312{
313 int num_columns = 0;
314
315 switch (space_outliner->outlinevis) {
316 case SO_DATA_API:
317 case SO_SEQUENCE:
318 case SO_LIBRARIES:
319 return 0.0f;
323 num_columns = OL_RNA_COL_SIZEX / UI_UNIT_X;
324 break;
326 num_columns = 1;
327 break;
328 }
329 break;
330 case SO_ID_ORPHANS:
331 num_columns = 3;
332 break;
333 case SO_VIEW_LAYER:
334 if (space_outliner->show_restrict_flags & SO_RESTRICT_ENABLE) {
335 num_columns++;
336 }
337 if (space_outliner->show_restrict_flags & SO_RESTRICT_HOLDOUT) {
338 num_columns++;
339 }
340 if (space_outliner->show_restrict_flags & SO_RESTRICT_INDIRECT_ONLY) {
341 num_columns++;
342 }
344 case SO_SCENES:
345 if (space_outliner->show_restrict_flags & SO_RESTRICT_SELECT) {
346 num_columns++;
347 }
348 if (space_outliner->show_restrict_flags & SO_RESTRICT_HIDE) {
349 num_columns++;
350 }
351 if (space_outliner->show_restrict_flags & SO_RESTRICT_VIEWPORT) {
352 num_columns++;
353 }
354 if (space_outliner->show_restrict_flags & SO_RESTRICT_RENDER) {
355 num_columns++;
356 }
357 break;
358 }
359 return (num_columns * UI_UNIT_X + V2D_SCROLL_WIDTH);
360}
361
363{
364 LISTBASE_FOREACH (TreeElement *, te, lb) {
365 if ((TREESTORE(te)->flag & flag) == flag) {
366 return te;
367 }
368 TreeElement *active_element = outliner_find_element_with_flag(&te->subtree, flag);
369 if (active_element) {
370 return active_element;
371 }
372 }
373 return nullptr;
374}
375
377{
378 TreeStoreElem *tselem;
379
380 while (te->parent) {
381 tselem = TREESTORE(te->parent);
382
383 if (tselem->flag & TSE_CLOSED) {
384 return false;
385 }
386 te = te->parent;
387 }
388
389 return true;
390}
391
393{
394 return ((te->ys + UI_UNIT_Y) >= v2d->cur.ymin) && (te->ys <= v2d->cur.ymax);
395}
396
397bool outliner_item_is_co_over_name_icons(const TreeElement *te, float view_co_x)
398{
399 /* Special case: count area left of Scene Collection as empty space */
400 bool outside_left = (TREESTORE(te)->type == TSE_VIEW_COLLECTION_BASE) ?
401 (view_co_x > te->xs + UI_UNIT_X) :
402 (view_co_x > te->xs);
403
404 return outside_left && (view_co_x < te->xend);
405}
406
407bool outliner_item_is_co_over_icon(const TreeElement *te, float view_co_x)
408{
409 return (view_co_x > (te->xs + UI_UNIT_X)) && (view_co_x < (te->xs + UI_UNIT_X * 2));
410}
411
412bool outliner_item_is_co_over_name(const TreeElement *te, float view_co_x)
413{
414 return (view_co_x > (te->xs + UI_UNIT_X * 2)) && (view_co_x < te->xend);
415}
416
418{
419 return (view_co_x > te->xs) && (view_co_x < te->xs + UI_UNIT_X);
420}
421
422void outliner_scroll_view(SpaceOutliner *space_outliner, ARegion *region, int delta_y)
423{
424 int tree_width, tree_height;
425 outliner_tree_dimensions(space_outliner, &tree_width, &tree_height);
426 int y_min = std::min(int(region->v2d.cur.ymin), -tree_height);
427
428 region->v2d.cur.ymax += delta_y;
429 region->v2d.cur.ymin += delta_y;
430
431 /* Adjust view if delta placed view outside total area */
432 int offset;
433 if (region->v2d.cur.ymax > -UI_UNIT_Y) {
434 offset = region->v2d.cur.ymax;
435 region->v2d.cur.ymax -= offset;
436 region->v2d.cur.ymin -= offset;
437 }
438 else if (region->v2d.cur.ymin < y_min) {
439 offset = y_min - region->v2d.cur.ymin;
440 region->v2d.cur.ymax += offset;
441 region->v2d.cur.ymin += offset;
442 }
443}
444
446 ARegion *region)
447{
448 /* Avoid rebuild if possible. */
449 if (space_outliner->runtime->tree_display->is_lazy_built()) {
450 ED_region_tag_redraw(region);
451 }
452 else {
454 }
455}
456
457} // namespace blender::ed::outliner
458
459using namespace blender::ed::outliner;
460
462{
463 ARegion *region = CTX_wm_region(C);
464 const Scene *scene = CTX_data_scene(C);
465 ViewLayer *view_layer = CTX_data_view_layer(C);
466 SpaceOutliner *space_outliner = CTX_wm_space_outliner(C);
467 TreeElement *te;
468 Base *base = nullptr;
469 float view_mval[2];
470
471 UI_view2d_region_to_view(&region->v2d, mval[0], mval[1], &view_mval[0], &view_mval[1]);
472
473 te = outliner_find_item_at_y(space_outliner, &space_outliner->tree, view_mval[1]);
474 if (te) {
475 TreeStoreElem *tselem = TREESTORE(te);
476 if ((tselem->type == TSE_SOME_ID) && (te->idcode == ID_OB)) {
477 Object *ob = (Object *)tselem->id;
478 BKE_view_layer_synced_ensure(scene, view_layer);
479 base = (te->directdata) ? (Base *)te->directdata : BKE_view_layer_base_find(view_layer, ob);
480 }
481 }
482
483 return base;
484}
485
487{
488 ARegion *region = CTX_wm_region(C);
489 SpaceOutliner *space_outliner = CTX_wm_space_outliner(C);
490
491 float view_mval[2];
492 UI_view2d_region_to_view(&region->v2d, mval[0], mval[1], &view_mval[0], &view_mval[1]);
493
494 TreeElement *te = outliner_find_item_at_y(space_outliner, &space_outliner->tree, view_mval[1]);
495 if (!te) {
496 return false;
497 }
498
499 bool success = true;
500 TreeStoreElem *tselem = TREESTORE(te);
501 switch (tselem->type) {
502 case TSE_BONE: {
503 Bone *bone = (Bone *)te->directdata;
504 *r_ptr = RNA_pointer_create_discrete(tselem->id, &RNA_Bone, bone);
505 break;
506 }
507 case TSE_POSE_CHANNEL: {
508 bPoseChannel *pchan = (bPoseChannel *)te->directdata;
509 *r_ptr = RNA_pointer_create_discrete(tselem->id, &RNA_PoseBone, pchan);
510 break;
511 }
512 case TSE_EBONE: {
513 EditBone *bone = (EditBone *)te->directdata;
514 *r_ptr = RNA_pointer_create_discrete(tselem->id, &RNA_EditBone, bone);
515 break;
516 }
517
518 default:
519 success = false;
520 break;
521 }
522 return success;
523}
LayerCollection * CTX_data_layer_collection(const bContext *C)
SpaceOutliner * CTX_wm_space_outliner(const bContext *C)
Scene * CTX_data_scene(const bContext *C)
ARegion * CTX_wm_region(const bContext *C)
ViewLayer * CTX_data_view_layer(const bContext *C)
void BKE_view_layer_synced_ensure(const Scene *scene, ViewLayer *view_layer)
Object * BKE_view_layer_active_object_get(const ViewLayer *view_layer)
Base * BKE_view_layer_base_find(ViewLayer *view_layer, Object *ob)
General operations, lookup, etc. for blender objects.
Object * BKE_object_pose_armature_get(Object *ob)
#define ATTR_FALLTHROUGH
#define LISTBASE_FOREACH(type, var, list)
BLI_INLINE bool BLI_listbase_is_empty(const ListBase *lb)
#define ELEM(...)
@ ID_OB
@ OB_MODE_WEIGHT_PAINT
@ OB_ARMATURE
@ OB_MESH
@ TSE_POSE_CHANNEL
@ TSE_VIEW_COLLECTION_BASE
@ TSE_EBONE
@ TSE_BONE
@ TSE_SOME_ID
@ TSE_R_LAYER
@ TSE_POSE_BASE
@ TSE_CLOSED
#define OBEDIT_FROM_OBACT(ob)
eSpaceOutliner_LibOverrideViewMode
@ SO_LIB_OVERRIDE_VIEW_HIERARCHIES
@ SO_LIB_OVERRIDE_VIEW_PROPERTIES
@ SO_RESTRICT_HIDE
@ SO_RESTRICT_RENDER
@ SO_RESTRICT_INDIRECT_ONLY
@ SO_RESTRICT_VIEWPORT
@ SO_RESTRICT_ENABLE
@ SO_RESTRICT_HOLDOUT
@ SO_RESTRICT_SELECT
@ SO_OVERRIDES_LIBRARY
@ SO_SEQUENCE
@ SO_DATA_API
@ SO_LIBRARIES
@ SO_VIEW_LAYER
@ SO_SCENES
@ SO_ID_ORPHANS
void ED_region_tag_redraw_no_rebuild(ARegion *region)
Definition area.cc:659
void ED_region_tag_redraw(ARegion *region)
Definition area.cc:639
#define C
Definition RandGen.cpp:29
#define UI_UNIT_Y
#define UI_UNIT_X
#define V2D_SCROLL_WIDTH
Definition UI_view2d.hh:54
void UI_view2d_region_to_view(const View2D *v2d, float x, float y, float *r_view_x, float *r_view_y) ATTR_NONNULL()
Definition view2d.cc:1667
KDTree_3d * tree
bool outliner_item_is_co_within_close_toggle(const TreeElement *te, float view_co_x)
TreeTraversalAction(*)(TreeElement *te, void *customdata) TreeTraversalFunc
bool outliner_item_is_co_over_icon(const TreeElement *te, float view_co_x)
void outliner_tag_redraw_avoid_rebuild_on_open_change(const SpaceOutliner *space_outliner, ARegion *region)
TreeElement * outliner_find_posechannel(ListBase *lb, const bPoseChannel *pchan)
TreeElement * outliner_find_item_at_x_in_row(const SpaceOutliner *space_outliner, TreeElement *parent_te, float view_co_x, bool *r_is_merged_icon, bool *r_is_over_icon)
TreeElement * outliner_find_id(SpaceOutliner *space_outliner, ListBase *lb, const ID *id)
static TreeElement * outliner_find_item_at_x_in_row_recursive(const TreeElement *parent_te, float view_co_x, bool *r_is_merged_icon)
float outliner_right_columns_width(const SpaceOutliner *space_outliner)
TreeElement * outliner_search_back_te(TreeElement *te, short idcode)
TreeElement * outliner_find_item_at_y(const SpaceOutliner *space_outliner, const ListBase *tree, float view_co_y)
void outliner_viewcontext_init(const bContext *C, TreeViewContext *tvc)
bool outliner_is_element_in_view(const TreeElement *te, const View2D *v2d)
TreeElement * outliner_find_editbone(ListBase *lb, const EditBone *ebone)
void outliner_scroll_view(SpaceOutliner *space_outliner, ARegion *region, int delta_y)
bool outliner_is_element_visible(const TreeElement *te)
bool outliner_tree_traverse(const SpaceOutliner *space_outliner, ListBase *tree, int filter_te_flag, int filter_tselem_flag, TreeTraversalFunc func, void *customdata)
ID * outliner_search_back(TreeElement *te, short idcode)
TreeElement * outliner_find_tree_element(ListBase *lb, const TreeStoreElem *store_elem)
TreeElement * outliner_find_parent_element(ListBase *lb, TreeElement *parent_te, const TreeElement *child_te)
bool outliner_item_is_co_over_name(const TreeElement *te, float view_co_x)
bool outliner_item_is_co_over_name_icons(const TreeElement *te, float view_co_x)
TreeElement * outliner_find_element_with_flag(const ListBase *lb, short flag)
void outliner_tree_dimensions(SpaceOutliner *space_outliner, int *r_width, int *r_height)
#define TREESTORE(a)
#define OL_RNA_COL_SIZEX
#define TSELEM_OPEN(telm, sv)
Base * ED_outliner_give_base_under_cursor(bContext *C, const int mval[2])
bool ED_outliner_give_rna_under_cursor(bContext *C, const int mval[2], PointerRNA *r_ptr)
PointerRNA RNA_pointer_create_discrete(ID *id, StructRNA *type, void *data)
Definition DNA_ID.h:404
void * first
short lib_override_view_mode
SpaceOutliner_Runtime * runtime
std::unique_ptr< AbstractTreeDisplay > tree_display
float ymax
float ymin
Establish and manage Outliner trees for different display modes.
uint8_t flag
Definition wm_window.cc:139