Fix: Curve to mesh node assert when last profile segment is vector
[blender.git] / source / blender / editors / space_outliner / outliner_select.c
1 /*
2  * This program is free software; you can redistribute it and/or
3  * modify it under the terms of the GNU General Public License
4  * as published by the Free Software Foundation; either version 2
5  * of the License, or (at your option) any later version.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * along with this program; if not, write to the Free Software Foundation,
14  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15  *
16  * The Original Code is Copyright (C) 2004 Blender Foundation.
17  * All rights reserved.
18  */
19
20 /** \file
21  * \ingroup spoutliner
22  */
23
24 #include <stdlib.h>
25
26 #include "MEM_guardedalloc.h"
27
28 #include "DNA_armature_types.h"
29 #include "DNA_collection_types.h"
30 #include "DNA_gpencil_modifier_types.h"
31 #include "DNA_gpencil_types.h"
32 #include "DNA_modifier_types.h"
33 #include "DNA_object_types.h"
34 #include "DNA_scene_types.h"
35 #include "DNA_sequence_types.h"
36 #include "DNA_shader_fx_types.h"
37
38 #include "BLI_listbase.h"
39 #include "BLI_utildefines.h"
40
41 #include "BKE_armature.h"
42 #include "BKE_collection.h"
43 #include "BKE_constraint.h"
44 #include "BKE_context.h"
45 #include "BKE_gpencil.h"
46 #include "BKE_gpencil_modifier.h"
47 #include "BKE_layer.h"
48 #include "BKE_main.h"
49 #include "BKE_modifier.h"
50 #include "BKE_object.h"
51 #include "BKE_particle.h"
52 #include "BKE_report.h"
53 #include "BKE_shader_fx.h"
54
55 #include "DEG_depsgraph.h"
56 #include "DEG_depsgraph_build.h"
57
58 #include "ED_armature.h"
59 #include "ED_buttons.h"
60 #include "ED_object.h"
61 #include "ED_outliner.h"
62 #include "ED_screen.h"
63 #include "ED_select_utils.h"
64 #include "ED_sequencer.h"
65 #include "ED_undo.h"
66
67 #include "SEQ_select.h"
68 #include "SEQ_sequencer.h"
69
70 #include "WM_api.h"
71 #include "WM_types.h"
72
73 #include "UI_interface.h"
74 #include "UI_view2d.h"
75
76 #include "RNA_access.h"
77 #include "RNA_define.h"
78
79 #include "outliner_intern.h"
80
81 /**
82  * \note changes to selection are by convention and not essential.
83  *
84  * \note Handles own undo push.
85  */
86 static void do_outliner_item_editmode_toggle(bContext *C, Scene *scene, Base *base)
87 {
88   Main *bmain = CTX_data_main(C);
89   Object *ob = base->object;
90
91   bool changed = false;
92   if (BKE_object_is_in_editmode(ob)) {
93     changed = ED_object_editmode_exit_ex(bmain, scene, ob, EM_FREEDATA);
94     if (changed) {
95       ED_object_base_select(base, BA_DESELECT);
96       WM_event_add_notifier(C, NC_SCENE | ND_MODE | NS_MODE_OBJECT, NULL);
97     }
98   }
99   else {
100     changed = ED_object_editmode_enter_ex(CTX_data_main(C), scene, ob, EM_NO_CONTEXT);
101     if (changed) {
102       ED_object_base_select(base, BA_SELECT);
103       WM_event_add_notifier(C, NC_SCENE | ND_MODE, NULL);
104     }
105   }
106
107   if (changed) {
108     DEG_id_tag_update(&scene->id, ID_RECALC_SELECT);
109     ED_outliner_select_sync_from_object_tag(C);
110     ED_undo_push(C, "Outliner Edit Mode Toggle");
111   }
112 }
113
114 /**
115  * \note changes to selection are by convention and not essential.
116  *
117  * \note Handles own undo push.
118  */
119 static void do_outliner_item_posemode_toggle(bContext *C, Scene *scene, Base *base)
120 {
121   Main *bmain = CTX_data_main(C);
122   Object *ob = base->object;
123
124   if (ID_IS_LINKED(ob)) {
125     BKE_report(CTX_wm_reports(C), RPT_WARNING, "Cannot pose libdata");
126     return;
127   }
128
129   bool changed = false;
130   if (ob->mode & OB_MODE_POSE) {
131     changed = ED_object_posemode_exit_ex(bmain, ob);
132     if (changed) {
133       ED_object_base_select(base, BA_DESELECT);
134       WM_event_add_notifier(C, NC_SCENE | ND_MODE | NS_MODE_OBJECT, NULL);
135     }
136   }
137   else {
138     changed = ED_object_posemode_enter_ex(bmain, ob);
139     if (changed) {
140       ED_object_base_select(base, BA_SELECT);
141       WM_event_add_notifier(C, NC_SCENE | ND_MODE | NS_MODE_POSE, NULL);
142     }
143   }
144
145   if (changed) {
146     DEG_id_tag_update(&scene->id, ID_RECALC_SELECT);
147     ED_outliner_select_sync_from_object_tag(C);
148     ED_undo_push(C, "Outliner Pose Mode Toggle");
149   }
150 }
151
152 /**
153  * Swap the current active object from the interaction mode with the given base.
154  *
155  * \note Changes to selection _are_ needed in this case,
156  * since entering the object mode uses the selection.
157  *
158  * If we didn't want to touch selection we could add an option to the operators
159  * not to do multi-object editing.
160  *
161  * \note Handles own undo push.
162  */
163 static void do_outliner_item_mode_toggle_generic(bContext *C, TreeViewContext *tvc, Base *base)
164 {
165   const int active_mode = tvc->obact->mode;
166   ED_undo_group_begin(C);
167
168   if (ED_object_mode_set(C, OB_MODE_OBJECT)) {
169     Base *base_active = BKE_view_layer_base_find(tvc->view_layer, tvc->obact);
170     if (base_active != base) {
171       BKE_view_layer_base_deselect_all(tvc->view_layer);
172       BKE_view_layer_base_select_and_set_active(tvc->view_layer, base);
173       DEG_id_tag_update(&tvc->scene->id, ID_RECALC_SELECT);
174       ED_undo_push(C, "Change Active");
175
176       /* Operator call does undo push. */
177       ED_object_mode_set(C, active_mode);
178       ED_outliner_select_sync_from_object_tag(C);
179     }
180   }
181   ED_undo_group_end(C);
182 }
183
184 /* Toggle the item's interaction mode if supported */
185 void outliner_item_mode_toggle(bContext *C,
186                                TreeViewContext *tvc,
187                                TreeElement *te,
188                                const bool do_extend)
189 {
190   TreeStoreElem *tselem = TREESTORE(te);
191
192   if ((tselem->type == TSE_SOME_ID) && (te->idcode == ID_OB)) {
193     Object *ob = (Object *)tselem->id;
194     Base *base = BKE_view_layer_base_find(tvc->view_layer, ob);
195
196     /* Hidden objects can be removed from the mode. */
197     if (!base || (!(base->flag & BASE_VISIBLE_DEPSGRAPH) && (ob->mode != tvc->obact->mode))) {
198       return;
199     }
200
201     if (!do_extend) {
202       do_outliner_item_mode_toggle_generic(C, tvc, base);
203     }
204     else if (tvc->ob_edit && OB_TYPE_SUPPORT_EDITMODE(ob->type)) {
205       do_outliner_item_editmode_toggle(C, tvc->scene, base);
206     }
207     else if (tvc->ob_pose && ob->type == OB_ARMATURE) {
208       do_outliner_item_posemode_toggle(C, tvc->scene, base);
209     }
210   }
211 }
212
213 /* ****************************************************** */
214 /* Outliner Element Selection/Activation on Click */
215
216 static void tree_element_viewlayer_activate(bContext *C, TreeElement *te)
217 {
218   /* paranoia check */
219   if (te->idcode != ID_SCE) {
220     return;
221   }
222
223   ViewLayer *view_layer = te->directdata;
224   wmWindow *win = CTX_wm_window(C);
225   Scene *scene = WM_window_get_active_scene(win);
226
227   if (BLI_findindex(&scene->view_layers, view_layer) != -1) {
228     WM_window_set_active_view_layer(win, view_layer);
229     WM_event_add_notifier(C, NC_SCREEN | ND_LAYER, NULL);
230   }
231 }
232
233 /**
234  * Select object tree
235  */
236 static void do_outliner_object_select_recursive(ViewLayer *view_layer,
237                                                 Object *ob_parent,
238                                                 bool select)
239 {
240   Base *base;
241
242   for (base = FIRSTBASE(view_layer); base; base = base->next) {
243     Object *ob = base->object;
244     if ((((base->flag & BASE_VISIBLE_DEPSGRAPH) != 0) &&
245          BKE_object_is_child_recursive(ob_parent, ob))) {
246       ED_object_base_select(base, select ? BA_SELECT : BA_DESELECT);
247     }
248   }
249 }
250
251 static void do_outliner_bone_select_recursive(bArmature *arm, Bone *bone_parent, bool select)
252 {
253   Bone *bone;
254   for (bone = bone_parent->childbase.first; bone; bone = bone->next) {
255     if (select && PBONE_SELECTABLE(arm, bone)) {
256       bone->flag |= BONE_SELECTED;
257     }
258     else {
259       bone->flag &= ~(BONE_TIPSEL | BONE_SELECTED | BONE_ROOTSEL);
260     }
261     do_outliner_bone_select_recursive(arm, bone, select);
262   }
263 }
264
265 static void do_outliner_ebone_select_recursive(bArmature *arm, EditBone *ebone_parent, bool select)
266 {
267   EditBone *ebone;
268   for (ebone = ebone_parent->next; ebone; ebone = ebone->next) {
269     if (ED_armature_ebone_is_child_recursive(ebone_parent, ebone)) {
270       if (select && EBONE_SELECTABLE(arm, ebone)) {
271         ebone->flag |= BONE_TIPSEL | BONE_SELECTED | BONE_ROOTSEL;
272       }
273       else {
274         ebone->flag &= ~(BONE_TIPSEL | BONE_SELECTED | BONE_ROOTSEL);
275       }
276     }
277   }
278 }
279
280 static void tree_element_object_activate(bContext *C,
281                                          Scene *scene,
282                                          ViewLayer *view_layer,
283                                          TreeElement *te,
284                                          const eOLSetState set,
285                                          bool recursive)
286 {
287   TreeStoreElem *tselem = TREESTORE(te);
288   TreeStoreElem *parent_tselem = NULL;
289   TreeElement *parent_te = NULL;
290   Scene *sce;
291   Base *base;
292   Object *ob = NULL;
293
294   /* if id is not object, we search back */
295   if ((tselem->type == TSE_SOME_ID) && (te->idcode == ID_OB)) {
296     ob = (Object *)tselem->id;
297   }
298   else {
299     parent_te = outliner_search_back_te(te, ID_OB);
300     if (parent_te) {
301       parent_tselem = TREESTORE(parent_te);
302       ob = (Object *)parent_tselem->id;
303
304       /* Don't return when activating children of the previous active object. */
305       if (ob == OBACT(view_layer) && set == OL_SETSEL_NONE) {
306         return;
307       }
308     }
309   }
310   if (ob == NULL) {
311     return;
312   }
313
314   sce = (Scene *)outliner_search_back(te, ID_SCE);
315   if (sce && scene != sce) {
316     WM_window_set_active_scene(CTX_data_main(C), C, CTX_wm_window(C), sce);
317     scene = sce;
318   }
319
320   /* find associated base in current scene */
321   base = BKE_view_layer_base_find(view_layer, ob);
322
323   if (scene->toolsettings->object_flag & SCE_OBJECT_MODE_LOCK) {
324     if (base != NULL) {
325       Object *obact = OBACT(view_layer);
326       const eObjectMode object_mode = obact ? obact->mode : OB_MODE_OBJECT;
327       if (base && !BKE_object_is_mode_compat(base->object, object_mode)) {
328         if (object_mode == OB_MODE_OBJECT) {
329           struct Main *bmain = CTX_data_main(C);
330           Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
331           ED_object_mode_generic_exit(bmain, depsgraph, scene, base->object);
332         }
333         if (!BKE_object_is_mode_compat(base->object, object_mode)) {
334           base = NULL;
335         }
336       }
337     }
338   }
339
340   if (base) {
341     if (set == OL_SETSEL_EXTEND) {
342       /* swap select */
343       if (base->flag & BASE_SELECTED) {
344         ED_object_base_select(base, BA_DESELECT);
345         if (parent_tselem) {
346           parent_tselem->flag &= ~TSE_SELECTED;
347         }
348       }
349       else {
350         ED_object_base_select(base, BA_SELECT);
351         if (parent_tselem) {
352           parent_tselem->flag |= TSE_SELECTED;
353         }
354       }
355     }
356     else {
357       /* deleselect all */
358
359       /* Only in object mode so we can switch the active object,
360        * keeping all objects in the current 'mode' selected, useful for multi-pose/edit mode.
361        * This keeps the convention that all objects in the current mode are also selected.
362        * see T55246. */
363       if ((scene->toolsettings->object_flag & SCE_OBJECT_MODE_LOCK) ?
364               (ob->mode == OB_MODE_OBJECT) :
365               true) {
366         BKE_view_layer_base_deselect_all(view_layer);
367       }
368       ED_object_base_select(base, BA_SELECT);
369       if (parent_tselem) {
370         parent_tselem->flag |= TSE_SELECTED;
371       }
372     }
373
374     if (recursive) {
375       /* Recursive select/deselect for Object hierarchies */
376       do_outliner_object_select_recursive(view_layer, ob, (base->flag & BASE_SELECTED) != 0);
377     }
378
379     if (set != OL_SETSEL_NONE) {
380       ED_object_base_activate_with_mode_exit_if_needed(C, base); /* adds notifier */
381       DEG_id_tag_update(&scene->id, ID_RECALC_SELECT);
382       WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, scene);
383     }
384   }
385 }
386
387 static void tree_element_material_activate(bContext *C, ViewLayer *view_layer, TreeElement *te)
388 {
389   /* we search for the object parent */
390   Object *ob = (Object *)outliner_search_back(te, ID_OB);
391   /* Note : ob->matbits can be NULL when a local object points to a library mesh. */
392   if (ob == NULL || ob != OBACT(view_layer) || ob->matbits == NULL) {
393     return; /* just paranoia */
394   }
395
396   /* In ob mat array? */
397   TreeElement *tes = te->parent;
398   if (tes->idcode == ID_OB) {
399     ob->actcol = te->index + 1;
400     ob->matbits[te->index] = 1; /* Make ob material active too. */
401   }
402   else {
403     /* or in obdata material */
404     ob->actcol = te->index + 1;
405     ob->matbits[te->index] = 0; /* Make obdata material active too. */
406   }
407
408   /* Tagging object for update seems a bit stupid here, but looks like we have to do it
409    * for render views to update. See T42973.
410    * Note that RNA material update does it too, see e.g. rna_MaterialSlot_update(). */
411   DEG_id_tag_update((ID *)ob, ID_RECALC_TRANSFORM);
412   WM_event_add_notifier(C, NC_MATERIAL | ND_SHADING_LINKS, NULL);
413 }
414
415 static void tree_element_camera_activate(bContext *C, Scene *scene, TreeElement *te)
416 {
417   Object *ob = (Object *)outliner_search_back(te, ID_OB);
418
419   scene->camera = ob;
420
421   Main *bmain = CTX_data_main(C);
422   wmWindowManager *wm = bmain->wm.first;
423
424   WM_windows_scene_data_sync(&wm->windows, scene);
425   DEG_id_tag_update(&scene->id, ID_RECALC_COPY_ON_WRITE);
426   DEG_relations_tag_update(bmain);
427   WM_event_add_notifier(C, NC_SCENE | NA_EDITED, NULL);
428 }
429
430 static void tree_element_world_activate(bContext *C, Scene *scene, TreeElement *te)
431 {
432   Scene *sce = NULL;
433
434   TreeElement *tep = te->parent;
435   if (tep) {
436     TreeStoreElem *tselem = TREESTORE(tep);
437     if (tselem->type == TSE_SOME_ID) {
438       sce = (Scene *)tselem->id;
439     }
440   }
441
442   /* make new scene active */
443   if (sce && scene != sce) {
444     WM_window_set_active_scene(CTX_data_main(C), C, CTX_wm_window(C), sce);
445   }
446 }
447
448 static void tree_element_defgroup_activate(bContext *C, TreeElement *te, TreeStoreElem *tselem)
449 {
450   /* id in tselem is object */
451   Object *ob = (Object *)tselem->id;
452   BLI_assert(te->index + 1 >= 0);
453   ob->actdef = te->index + 1;
454
455   DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
456   WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, ob);
457 }
458
459 static void tree_element_gplayer_activate(bContext *C, TreeElement *te, TreeStoreElem *tselem)
460 {
461   bGPdata *gpd = (bGPdata *)tselem->id;
462   bGPDlayer *gpl = te->directdata;
463
464   /* We can only have a single "active" layer at a time
465    * and there must always be an active layer... */
466   if (gpl) {
467     BKE_gpencil_layer_active_set(gpd, gpl);
468     DEG_id_tag_update(&gpd->id, ID_RECALC_GEOMETRY);
469     WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_SELECTED, gpd);
470   }
471 }
472
473 static void tree_element_posegroup_activate(bContext *C, TreeElement *te, TreeStoreElem *tselem)
474 {
475   Object *ob = (Object *)tselem->id;
476   if (ob->pose) {
477     ob->pose->active_group = te->index + 1;
478     WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob);
479   }
480 }
481
482 static void tree_element_posechannel_activate(bContext *C,
483                                               ViewLayer *view_layer,
484                                               TreeElement *te,
485                                               TreeStoreElem *tselem,
486                                               const eOLSetState set,
487                                               bool recursive)
488 {
489   Object *ob = (Object *)tselem->id;
490   bArmature *arm = ob->data;
491   bPoseChannel *pchan = te->directdata;
492
493   if (!(pchan->bone->flag & BONE_HIDDEN_P)) {
494     if (set != OL_SETSEL_EXTEND) {
495       /* Single select forces all other bones to get unselected. */
496       uint objects_len = 0;
497       Object **objects = BKE_object_pose_array_get_unique(view_layer, NULL, &objects_len);
498
499       for (uint object_index = 0; object_index < objects_len; object_index++) {
500         Object *ob_iter = BKE_object_pose_armature_get(objects[object_index]);
501
502         /* Sanity checks. */
503         if (ELEM(NULL, ob_iter, ob_iter->pose, ob_iter->data)) {
504           continue;
505         }
506
507         bPoseChannel *pchannel;
508         for (pchannel = ob_iter->pose->chanbase.first; pchannel; pchannel = pchannel->next) {
509           pchannel->bone->flag &= ~(BONE_TIPSEL | BONE_SELECTED | BONE_ROOTSEL);
510         }
511
512         if (ob != ob_iter) {
513           DEG_id_tag_update(ob_iter->data, ID_RECALC_SELECT);
514         }
515       }
516       MEM_freeN(objects);
517     }
518
519     if ((set == OL_SETSEL_EXTEND) && (pchan->bone->flag & BONE_SELECTED)) {
520       pchan->bone->flag &= ~BONE_SELECTED;
521     }
522     else {
523       pchan->bone->flag |= BONE_SELECTED;
524       arm->act_bone = pchan->bone;
525     }
526
527     if (recursive) {
528       /* Recursive select/deselect */
529       do_outliner_bone_select_recursive(
530           arm, pchan->bone, (pchan->bone->flag & BONE_SELECTED) != 0);
531     }
532
533     WM_event_add_notifier(C, NC_OBJECT | ND_BONE_ACTIVE, ob);
534     DEG_id_tag_update(&arm->id, ID_RECALC_SELECT);
535   }
536 }
537
538 static void tree_element_bone_activate(bContext *C,
539                                        ViewLayer *view_layer,
540                                        TreeElement *te,
541                                        TreeStoreElem *tselem,
542                                        const eOLSetState set,
543                                        bool recursive)
544 {
545   bArmature *arm = (bArmature *)tselem->id;
546   Bone *bone = te->directdata;
547
548   if (!(bone->flag & BONE_HIDDEN_P)) {
549     Object *ob = OBACT(view_layer);
550     if (ob) {
551       if (set != OL_SETSEL_EXTEND) {
552         /* single select forces all other bones to get unselected */
553         for (Bone *bone_iter = arm->bonebase.first; bone_iter != NULL;
554              bone_iter = bone_iter->next) {
555           bone_iter->flag &= ~(BONE_TIPSEL | BONE_SELECTED | BONE_ROOTSEL);
556           do_outliner_bone_select_recursive(arm, bone_iter, false);
557         }
558       }
559     }
560
561     if (set == OL_SETSEL_EXTEND && (bone->flag & BONE_SELECTED)) {
562       bone->flag &= ~BONE_SELECTED;
563     }
564     else {
565       bone->flag |= BONE_SELECTED;
566       arm->act_bone = bone;
567     }
568
569     if (recursive) {
570       /* Recursive select/deselect */
571       do_outliner_bone_select_recursive(arm, bone, (bone->flag & BONE_SELECTED) != 0);
572     }
573
574     WM_event_add_notifier(C, NC_OBJECT | ND_BONE_ACTIVE, ob);
575   }
576 }
577
578 /** Edit-bones only draw in edit-mode armature. */
579 static void tree_element_active_ebone__sel(bContext *C, bArmature *arm, EditBone *ebone, short sel)
580 {
581   if (sel) {
582     arm->act_edbone = ebone;
583   }
584   ED_armature_ebone_select_set(ebone, sel);
585   WM_event_add_notifier(C, NC_OBJECT | ND_BONE_ACTIVE, CTX_data_edit_object(C));
586 }
587 static void tree_element_ebone_activate(bContext *C,
588                                         ViewLayer *view_layer,
589                                         TreeElement *te,
590                                         TreeStoreElem *tselem,
591                                         const eOLSetState set,
592                                         bool recursive)
593 {
594   bArmature *arm = (bArmature *)tselem->id;
595   EditBone *ebone = te->directdata;
596
597   if (set == OL_SETSEL_NORMAL) {
598     if (!(ebone->flag & BONE_HIDDEN_A)) {
599       uint bases_len = 0;
600       Base **bases = BKE_view_layer_array_from_bases_in_edit_mode_unique_data(
601           view_layer, NULL, &bases_len);
602       ED_armature_edit_deselect_all_multi_ex(bases, bases_len);
603       MEM_freeN(bases);
604
605       tree_element_active_ebone__sel(C, arm, ebone, true);
606     }
607   }
608   else if (set == OL_SETSEL_EXTEND) {
609     if (!(ebone->flag & BONE_HIDDEN_A)) {
610       if (!(ebone->flag & BONE_SELECTED)) {
611         tree_element_active_ebone__sel(C, arm, ebone, true);
612       }
613       else {
614         /* entirely selected, so de-select */
615         tree_element_active_ebone__sel(C, arm, ebone, false);
616       }
617     }
618   }
619
620   if (recursive) {
621     /* Recursive select/deselect */
622     do_outliner_ebone_select_recursive(arm, ebone, (ebone->flag & BONE_SELECTED) != 0);
623   }
624 }
625
626 static void tree_element_modifier_activate(bContext *C,
627                                            TreeElement *te,
628                                            TreeStoreElem *tselem,
629                                            const eOLSetState set)
630 {
631   Object *ob = (Object *)tselem->id;
632   ModifierData *md = (ModifierData *)te->directdata;
633
634   if (set == OL_SETSEL_NORMAL) {
635     BKE_object_modifier_set_active(ob, md);
636     WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER, ob);
637   }
638 }
639
640 static void tree_element_psys_activate(bContext *C, TreeStoreElem *tselem)
641 {
642   Object *ob = (Object *)tselem->id;
643
644   WM_event_add_notifier(C, NC_OBJECT | ND_PARTICLE | NA_EDITED, ob);
645 }
646
647 static void tree_element_constraint_activate(bContext *C,
648                                              ViewLayer *view_layer,
649                                              TreeElement *te,
650                                              TreeStoreElem *tselem,
651                                              const eOLSetState set)
652 {
653   Object *ob = (Object *)tselem->id;
654
655   /* Activate the parent bone if this is a bone constraint. */
656   te = te->parent;
657   while (te) {
658     tselem = TREESTORE(te);
659     if (tselem->type == TSE_POSE_CHANNEL) {
660       tree_element_posechannel_activate(C, view_layer, te, tselem, set, false);
661       return;
662     }
663     te = te->parent;
664   }
665
666   WM_event_add_notifier(C, NC_OBJECT | ND_CONSTRAINT, ob);
667 }
668
669 static void tree_element_sequence_activate(bContext *C,
670                                            Scene *scene,
671                                            TreeElement *te,
672                                            const eOLSetState set)
673 {
674   Sequence *seq = (Sequence *)te->directdata;
675   Editing *ed = SEQ_editing_get(scene, false);
676
677   if (BLI_findindex(ed->seqbasep, seq) != -1) {
678     if (set == OL_SETSEL_EXTEND) {
679       SEQ_select_active_set(scene, NULL);
680     }
681     ED_sequencer_deselect_all(scene);
682
683     if ((set == OL_SETSEL_EXTEND) && seq->flag & SELECT) {
684       seq->flag &= ~SELECT;
685     }
686     else {
687       seq->flag |= SELECT;
688       SEQ_select_active_set(scene, seq);
689     }
690   }
691
692   WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER | NA_SELECTED, scene);
693 }
694
695 static void tree_element_sequence_dup_activate(Scene *scene, TreeElement *UNUSED(te))
696 {
697   Editing *ed = SEQ_editing_get(scene, false);
698
699   /* XXX  select_single_seq(seq, 1); */
700   Sequence *p = ed->seqbasep->first;
701   while (p) {
702     if ((!p->strip) || (!p->strip->stripdata) || (p->strip->stripdata->name[0] == '\0')) {
703       p = p->next;
704       continue;
705     }
706
707     /* XXX: if (STREQ(p->strip->stripdata->name, seq->strip->stripdata->name)) select_single_seq(p,
708      * 0); */
709     p = p->next;
710   }
711 }
712
713 static void tree_element_master_collection_activate(const bContext *C)
714 {
715   ViewLayer *view_layer = CTX_data_view_layer(C);
716   LayerCollection *layer_collection = view_layer->layer_collections.first;
717   BKE_layer_collection_activate(view_layer, layer_collection);
718   /* A very precise notifier - ND_LAYER alone is quite vague, we want to avoid unnecessary work
719    * when only the active collection changes. */
720   WM_main_add_notifier(NC_SCENE | ND_LAYER | NS_LAYER_COLLECTION | NA_ACTIVATED, NULL);
721 }
722
723 static void tree_element_layer_collection_activate(bContext *C, TreeElement *te)
724 {
725   Scene *scene = CTX_data_scene(C);
726   LayerCollection *layer_collection = te->directdata;
727   ViewLayer *view_layer = BKE_view_layer_find_from_collection(scene, layer_collection);
728   BKE_layer_collection_activate(view_layer, layer_collection);
729   /* A very precise notifier - ND_LAYER alone is quite vague, we want to avoid unnecessary work
730    * when only the active collection changes. */
731   WM_main_add_notifier(NC_SCENE | ND_LAYER | NS_LAYER_COLLECTION | NA_ACTIVATED, NULL);
732 }
733
734 /* ---------------------------------------------- */
735
736 /* generic call for ID data check or make/check active in UI */
737 void tree_element_activate(bContext *C,
738                            const TreeViewContext *tvc,
739                            TreeElement *te,
740                            const eOLSetState set,
741                            const bool handle_all_types)
742 {
743   switch (te->idcode) {
744     /** \note #ID_OB only if handle_all_type is true,
745      * else objects are handled specially to allow multiple selection.
746      * See #do_outliner_item_activate. */
747     case ID_OB:
748       if (handle_all_types) {
749         tree_element_object_activate(C, tvc->scene, tvc->view_layer, te, set, false);
750       }
751       break;
752     case ID_MA:
753       tree_element_material_activate(C, tvc->view_layer, te);
754       break;
755     case ID_WO:
756       tree_element_world_activate(C, tvc->scene, te);
757       break;
758     case ID_CA:
759       tree_element_camera_activate(C, tvc->scene, te);
760       break;
761   }
762 }
763
764 /**
765  * Generic call for non-id data to make active in UI
766  */
767 void tree_element_type_active_set(bContext *C,
768                                   const TreeViewContext *tvc,
769                                   TreeElement *te,
770                                   TreeStoreElem *tselem,
771                                   const eOLSetState set,
772                                   bool recursive)
773 {
774   BLI_assert(set != OL_SETSEL_NONE);
775   switch (tselem->type) {
776     case TSE_DEFGROUP:
777       tree_element_defgroup_activate(C, te, tselem);
778       break;
779     case TSE_BONE:
780       tree_element_bone_activate(C, tvc->view_layer, te, tselem, set, recursive);
781       break;
782     case TSE_EBONE:
783       tree_element_ebone_activate(C, tvc->view_layer, te, tselem, set, recursive);
784       break;
785     case TSE_MODIFIER:
786       tree_element_modifier_activate(C, te, tselem, set);
787       break;
788     case TSE_LINKED_OB:
789       tree_element_object_activate(C, tvc->scene, tvc->view_layer, te, set, false);
790       break;
791     case TSE_LINKED_PSYS:
792       tree_element_psys_activate(C, tselem);
793       break;
794     case TSE_POSE_BASE:
795       return;
796     case TSE_POSE_CHANNEL:
797       tree_element_posechannel_activate(C, tvc->view_layer, te, tselem, set, recursive);
798       break;
799     case TSE_CONSTRAINT_BASE:
800     case TSE_CONSTRAINT:
801       tree_element_constraint_activate(C, tvc->view_layer, te, tselem, set);
802       break;
803     case TSE_R_LAYER:
804       tree_element_viewlayer_activate(C, te);
805       break;
806     case TSE_POSEGRP:
807       tree_element_posegroup_activate(C, te, tselem);
808       break;
809     case TSE_SEQUENCE:
810       tree_element_sequence_activate(C, tvc->scene, te, set);
811       break;
812     case TSE_SEQUENCE_DUP:
813       tree_element_sequence_dup_activate(tvc->scene, te);
814       break;
815     case TSE_GP_LAYER:
816       tree_element_gplayer_activate(C, te, tselem);
817       break;
818     case TSE_VIEW_COLLECTION_BASE:
819       tree_element_master_collection_activate(C);
820       break;
821     case TSE_LAYER_COLLECTION:
822       tree_element_layer_collection_activate(C, te);
823       break;
824   }
825 }
826
827 static eOLDrawState tree_element_defgroup_state_get(const ViewLayer *view_layer,
828                                                     const TreeElement *te,
829                                                     const TreeStoreElem *tselem)
830 {
831   const Object *ob = (const Object *)tselem->id;
832   if (ob == OBACT(view_layer)) {
833     if (ob->actdef == te->index + 1) {
834       return OL_DRAWSEL_NORMAL;
835     }
836   }
837   return OL_DRAWSEL_NONE;
838 }
839
840 static eOLDrawState tree_element_bone_state_get(const ViewLayer *view_layer,
841                                                 const TreeElement *te,
842                                                 const TreeStoreElem *tselem)
843 {
844   const bArmature *arm = (const bArmature *)tselem->id;
845   const Bone *bone = te->directdata;
846   const Object *ob = OBACT(view_layer);
847   if (ob && ob->data == arm) {
848     if (bone->flag & BONE_SELECTED) {
849       return OL_DRAWSEL_NORMAL;
850     }
851   }
852   return OL_DRAWSEL_NONE;
853 }
854
855 static eOLDrawState tree_element_ebone_state_get(const TreeElement *te)
856 {
857   const EditBone *ebone = te->directdata;
858   if (ebone->flag & BONE_SELECTED) {
859     return OL_DRAWSEL_NORMAL;
860   }
861   return OL_DRAWSEL_NONE;
862 }
863
864 static eOLDrawState tree_element_modifier_state_get(const TreeElement *te,
865                                                     const TreeStoreElem *tselem)
866 {
867   const Object *ob = (const Object *)tselem->id;
868   const ModifierData *md = (const ModifierData *)te->directdata;
869
870   return (BKE_object_active_modifier(ob) == md) ? OL_DRAWSEL_NORMAL : OL_DRAWSEL_NONE;
871 }
872
873 static eOLDrawState tree_element_object_state_get(const TreeViewContext *tvc,
874                                                   const TreeStoreElem *tselem)
875 {
876   return (tselem->id == (const ID *)tvc->obact) ? OL_DRAWSEL_NORMAL : OL_DRAWSEL_NONE;
877 }
878
879 static eOLDrawState tree_element_pose_state_get(const ViewLayer *view_layer,
880                                                 const TreeStoreElem *tselem)
881 {
882   const Object *ob = (const Object *)tselem->id;
883   /* This will just lookup in a cache, it will not change the arguments. */
884   const Base *base = BKE_view_layer_base_find((ViewLayer *)view_layer, (Object *)ob);
885   if (base == NULL) {
886     /* Armature not instantiated in current scene (e.g. inside an appended group). */
887     return OL_DRAWSEL_NONE;
888   }
889
890   if (ob->mode & OB_MODE_POSE) {
891     return OL_DRAWSEL_NORMAL;
892   }
893   return OL_DRAWSEL_NONE;
894 }
895
896 static eOLDrawState tree_element_posechannel_state_get(const Object *ob_pose,
897                                                        const TreeElement *te,
898                                                        const TreeStoreElem *tselem)
899 {
900   const Object *ob = (const Object *)tselem->id;
901   const bPoseChannel *pchan = te->directdata;
902   if (ob == ob_pose && ob->pose) {
903     if (pchan->bone->flag & BONE_SELECTED) {
904       return OL_DRAWSEL_NORMAL;
905     }
906   }
907   return OL_DRAWSEL_NONE;
908 }
909
910 static eOLDrawState tree_element_viewlayer_state_get(const bContext *C, const TreeElement *te)
911 {
912   /* paranoia check */
913   if (te->idcode != ID_SCE) {
914     return OL_DRAWSEL_NONE;
915   }
916
917   const ViewLayer *view_layer = te->directdata;
918
919   if (CTX_data_view_layer(C) == view_layer) {
920     return OL_DRAWSEL_NORMAL;
921   }
922   return OL_DRAWSEL_NONE;
923 }
924
925 static eOLDrawState tree_element_posegroup_state_get(const ViewLayer *view_layer,
926                                                      const TreeElement *te,
927                                                      const TreeStoreElem *tselem)
928 {
929   const Object *ob = (const Object *)tselem->id;
930
931   if (ob == OBACT(view_layer) && ob->pose) {
932     if (ob->pose->active_group == te->index + 1) {
933       return OL_DRAWSEL_NORMAL;
934     }
935   }
936   return OL_DRAWSEL_NONE;
937 }
938
939 static eOLDrawState tree_element_sequence_state_get(const Scene *scene, const TreeElement *te)
940 {
941   const Sequence *seq = (const Sequence *)te->directdata;
942   const Editing *ed = scene->ed;
943
944   if (ed && ed->act_seq == seq && seq->flag & SELECT) {
945     return OL_DRAWSEL_NORMAL;
946   }
947   return OL_DRAWSEL_NONE;
948 }
949
950 static eOLDrawState tree_element_sequence_dup_state_get(const TreeElement *te)
951 {
952   const Sequence *seq = (const Sequence *)te->directdata;
953   if (seq->flag & SELECT) {
954     return OL_DRAWSEL_NORMAL;
955   }
956   return OL_DRAWSEL_NONE;
957 }
958
959 static eOLDrawState tree_element_gplayer_state_get(const TreeElement *te)
960 {
961   if (((const bGPDlayer *)te->directdata)->flag & GP_LAYER_ACTIVE) {
962     return OL_DRAWSEL_NORMAL;
963   }
964   return OL_DRAWSEL_NONE;
965 }
966
967 static eOLDrawState tree_element_master_collection_state_get(const bContext *C)
968 {
969   const ViewLayer *view_layer = CTX_data_view_layer(C);
970   const LayerCollection *active = CTX_data_layer_collection(C);
971
972   if (active == view_layer->layer_collections.first) {
973     return OL_DRAWSEL_NORMAL;
974   }
975   return OL_DRAWSEL_NONE;
976 }
977
978 static eOLDrawState tree_element_layer_collection_state_get(const bContext *C,
979                                                             const TreeElement *te)
980 {
981   const LayerCollection *active = CTX_data_layer_collection(C);
982   if (active == te->directdata) {
983     return OL_DRAWSEL_NORMAL;
984   }
985   return OL_DRAWSEL_NONE;
986 }
987
988 static eOLDrawState tree_element_active_material_get(const ViewLayer *view_layer,
989                                                      const TreeElement *te)
990 {
991   /* we search for the object parent */
992   const Object *ob = (const Object *)outliner_search_back((TreeElement *)te, ID_OB);
993   /* Note : ob->matbits can be NULL when a local object points to a library mesh. */
994   if (ob == NULL || ob != OBACT(view_layer) || ob->matbits == NULL) {
995     return OL_DRAWSEL_NONE; /* just paranoia */
996   }
997
998   /* searching in ob mat array? */
999   const TreeElement *tes = te->parent;
1000   if (tes->idcode == ID_OB) {
1001     if (ob->actcol == te->index + 1) {
1002       if (ob->matbits[te->index]) {
1003         return OL_DRAWSEL_NORMAL;
1004       }
1005     }
1006   }
1007   /* or we search for obdata material */
1008   else {
1009     if (ob->actcol == te->index + 1) {
1010       if (ob->matbits[te->index] == 0) {
1011         return OL_DRAWSEL_NORMAL;
1012       }
1013     }
1014   }
1015   return OL_DRAWSEL_NONE;
1016 }
1017
1018 static eOLDrawState tree_element_active_scene_get(const TreeViewContext *tvc,
1019                                                   const TreeElement *te,
1020                                                   const TreeStoreElem *tselem)
1021 {
1022   if (te->idcode == ID_SCE) {
1023     if (tselem->id == (ID *)tvc->scene) {
1024       return OL_DRAWSEL_NORMAL;
1025     }
1026   }
1027   return OL_DRAWSEL_NONE;
1028 }
1029
1030 static eOLDrawState tree_element_active_world_get(const Scene *scene, const TreeElement *te)
1031 {
1032   const TreeElement *tep = te->parent;
1033   if (tep == NULL) {
1034     return OL_DRAWSEL_NORMAL;
1035   }
1036
1037   const TreeStoreElem *tselem = TREESTORE(tep);
1038   if (tselem->id == (const ID *)scene) {
1039     return OL_DRAWSEL_NORMAL;
1040   }
1041   return OL_DRAWSEL_NONE;
1042 }
1043
1044 static eOLDrawState tree_element_active_camera_get(const Scene *scene, const TreeElement *te)
1045 {
1046   const Object *ob = (const Object *)outliner_search_back((TreeElement *)te, ID_OB);
1047
1048   return (scene->camera == ob) ? OL_DRAWSEL_NORMAL : OL_DRAWSEL_NONE;
1049 }
1050
1051 eOLDrawState tree_element_active_state_get(const TreeViewContext *tvc,
1052                                            const TreeElement *te,
1053                                            const TreeStoreElem *tselem)
1054 {
1055   switch (te->idcode) {
1056     case ID_SCE:
1057       return tree_element_active_scene_get(tvc, te, tselem);
1058     case ID_OB:
1059       /* Objects are currently handled by the caller in order to also change text color. */
1060       return OL_DRAWSEL_NONE;
1061       break;
1062     case ID_MA:
1063       return tree_element_active_material_get(tvc->view_layer, te);
1064     case ID_WO:
1065       return tree_element_active_world_get(tvc->scene, te);
1066     case ID_CA:
1067       return tree_element_active_camera_get(tvc->scene, te);
1068   }
1069   return OL_DRAWSEL_NONE;
1070 }
1071
1072 /**
1073  * Generic call for non-id data to check the active state in UI.
1074  */
1075 eOLDrawState tree_element_type_active_state_get(const bContext *C,
1076                                                 const TreeViewContext *tvc,
1077                                                 const TreeElement *te,
1078                                                 const TreeStoreElem *tselem)
1079 {
1080   switch (tselem->type) {
1081     case TSE_DEFGROUP:
1082       return tree_element_defgroup_state_get(tvc->view_layer, te, tselem);
1083     case TSE_BONE:
1084       return tree_element_bone_state_get(tvc->view_layer, te, tselem);
1085     case TSE_EBONE:
1086       return tree_element_ebone_state_get(te);
1087     case TSE_MODIFIER:
1088       return tree_element_modifier_state_get(te, tselem);
1089     case TSE_LINKED_OB:
1090       return tree_element_object_state_get(tvc, tselem);
1091     case TSE_LINKED_PSYS:
1092       return OL_DRAWSEL_NONE;
1093     case TSE_POSE_BASE:
1094       return tree_element_pose_state_get(tvc->view_layer, tselem);
1095     case TSE_POSE_CHANNEL:
1096       return tree_element_posechannel_state_get(tvc->ob_pose, te, tselem);
1097     case TSE_CONSTRAINT_BASE:
1098     case TSE_CONSTRAINT:
1099       return OL_DRAWSEL_NONE;
1100     case TSE_R_LAYER:
1101       return tree_element_viewlayer_state_get(C, te);
1102     case TSE_POSEGRP:
1103       return tree_element_posegroup_state_get(tvc->view_layer, te, tselem);
1104     case TSE_SEQUENCE:
1105       return tree_element_sequence_state_get(tvc->scene, te);
1106     case TSE_SEQUENCE_DUP:
1107       return tree_element_sequence_dup_state_get(te);
1108     case TSE_GP_LAYER:
1109       return tree_element_gplayer_state_get(te);
1110     case TSE_VIEW_COLLECTION_BASE:
1111       return tree_element_master_collection_state_get(C);
1112     case TSE_LAYER_COLLECTION:
1113       return tree_element_layer_collection_state_get(C, te);
1114   }
1115   return OL_DRAWSEL_NONE;
1116 }
1117
1118 bPoseChannel *outliner_find_parent_bone(TreeElement *te, TreeElement **r_bone_te)
1119 {
1120   TreeStoreElem *tselem;
1121
1122   te = te->parent;
1123   while (te) {
1124     tselem = TREESTORE(te);
1125     if (tselem->type == TSE_POSE_CHANNEL) {
1126       *r_bone_te = te;
1127       return (bPoseChannel *)te->directdata;
1128     }
1129     te = te->parent;
1130   }
1131
1132   return NULL;
1133 }
1134
1135 static void outliner_sync_to_properties_editors(const bContext *C,
1136                                                 PointerRNA *ptr,
1137                                                 const int context)
1138 {
1139   bScreen *screen = CTX_wm_screen(C);
1140
1141   LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) {
1142     if (area->spacetype != SPACE_PROPERTIES) {
1143       continue;
1144     }
1145
1146     SpaceProperties *sbuts = (SpaceProperties *)area->spacedata.first;
1147     if (ED_buttons_should_sync_with_outliner(C, sbuts, area)) {
1148       ED_buttons_set_context(C, sbuts, ptr, context);
1149     }
1150   }
1151 }
1152
1153 static void outliner_set_properties_tab(bContext *C, TreeElement *te, TreeStoreElem *tselem)
1154 {
1155   PointerRNA ptr = {0};
1156   int context = 0;
1157
1158   /* ID Types */
1159   if (tselem->type == TSE_SOME_ID) {
1160     RNA_id_pointer_create(tselem->id, &ptr);
1161
1162     switch (te->idcode) {
1163       case ID_SCE:
1164         context = BCONTEXT_SCENE;
1165         break;
1166       case ID_OB:
1167         context = BCONTEXT_OBJECT;
1168         break;
1169       case ID_ME:
1170       case ID_CU:
1171       case ID_MB:
1172       case ID_IM:
1173       case ID_LT:
1174       case ID_LA:
1175       case ID_CA:
1176       case ID_KE:
1177       case ID_SPK:
1178       case ID_AR:
1179       case ID_GD:
1180       case ID_LP:
1181       case ID_HA:
1182       case ID_PT:
1183       case ID_VO:
1184         context = BCONTEXT_DATA;
1185         break;
1186       case ID_MA:
1187         context = BCONTEXT_MATERIAL;
1188         break;
1189       case ID_WO:
1190         context = BCONTEXT_WORLD;
1191         break;
1192     }
1193   }
1194   else {
1195     switch (tselem->type) {
1196       case TSE_DEFGROUP_BASE:
1197       case TSE_DEFGROUP:
1198         RNA_id_pointer_create(tselem->id, &ptr);
1199         context = BCONTEXT_DATA;
1200         break;
1201       case TSE_CONSTRAINT_BASE:
1202       case TSE_CONSTRAINT: {
1203         TreeElement *bone_te = NULL;
1204         bPoseChannel *pchan = outliner_find_parent_bone(te, &bone_te);
1205
1206         if (pchan) {
1207           RNA_pointer_create(TREESTORE(bone_te)->id, &RNA_PoseBone, pchan, &ptr);
1208           context = BCONTEXT_BONE_CONSTRAINT;
1209         }
1210         else {
1211           RNA_id_pointer_create(tselem->id, &ptr);
1212           context = BCONTEXT_CONSTRAINT;
1213         }
1214
1215         /* Expand the selected constraint in the properties editor. */
1216         if (tselem->type != TSE_CONSTRAINT_BASE) {
1217           BKE_constraint_panel_expand(te->directdata);
1218         }
1219         break;
1220       }
1221       case TSE_MODIFIER_BASE:
1222       case TSE_MODIFIER:
1223         RNA_id_pointer_create(tselem->id, &ptr);
1224         context = BCONTEXT_MODIFIER;
1225
1226         if (tselem->type != TSE_MODIFIER_BASE) {
1227           Object *ob = (Object *)tselem->id;
1228
1229           if (ob->type == OB_GPENCIL) {
1230             BKE_gpencil_modifier_panel_expand(te->directdata);
1231           }
1232           else {
1233             ModifierData *md = (ModifierData *)te->directdata;
1234
1235             switch ((ModifierType)md->type) {
1236               case eModifierType_ParticleSystem:
1237                 context = BCONTEXT_PARTICLE;
1238                 break;
1239               case eModifierType_Cloth:
1240               case eModifierType_Softbody:
1241               case eModifierType_Collision:
1242               case eModifierType_Fluidsim:
1243               case eModifierType_DynamicPaint:
1244               case eModifierType_Fluid:
1245                 context = BCONTEXT_PHYSICS;
1246                 break;
1247               default:
1248                 break;
1249             }
1250
1251             if (context == BCONTEXT_MODIFIER) {
1252               BKE_modifier_panel_expand(md);
1253             }
1254           }
1255         }
1256         break;
1257       case TSE_GPENCIL_EFFECT_BASE:
1258       case TSE_GPENCIL_EFFECT:
1259         RNA_id_pointer_create(tselem->id, &ptr);
1260         context = BCONTEXT_SHADERFX;
1261
1262         if (tselem->type != TSE_GPENCIL_EFFECT_BASE) {
1263           BKE_shaderfx_panel_expand(te->directdata);
1264         }
1265         break;
1266       case TSE_BONE: {
1267         bArmature *arm = (bArmature *)tselem->id;
1268         Bone *bone = te->directdata;
1269
1270         RNA_pointer_create(&arm->id, &RNA_Bone, bone, &ptr);
1271         context = BCONTEXT_BONE;
1272         break;
1273       }
1274       case TSE_EBONE: {
1275         bArmature *arm = (bArmature *)tselem->id;
1276         EditBone *ebone = te->directdata;
1277
1278         RNA_pointer_create(&arm->id, &RNA_EditBone, ebone, &ptr);
1279         context = BCONTEXT_BONE;
1280         break;
1281       }
1282       case TSE_POSE_CHANNEL: {
1283         Object *ob = (Object *)tselem->id;
1284         bArmature *arm = ob->data;
1285         bPoseChannel *pchan = te->directdata;
1286
1287         RNA_pointer_create(&arm->id, &RNA_PoseBone, pchan, &ptr);
1288         context = BCONTEXT_BONE;
1289         break;
1290       }
1291       case TSE_POSE_BASE: {
1292         Object *ob = (Object *)tselem->id;
1293         bArmature *arm = ob->data;
1294
1295         RNA_pointer_create(&arm->id, &RNA_Armature, arm, &ptr);
1296         context = BCONTEXT_DATA;
1297         break;
1298       }
1299       case TSE_R_LAYER_BASE:
1300       case TSE_R_LAYER: {
1301         ViewLayer *view_layer = te->directdata;
1302
1303         RNA_pointer_create(tselem->id, &RNA_ViewLayer, view_layer, &ptr);
1304         context = BCONTEXT_VIEW_LAYER;
1305         break;
1306       }
1307       case TSE_POSEGRP_BASE:
1308       case TSE_POSEGRP: {
1309         Object *ob = (Object *)tselem->id;
1310         bArmature *arm = ob->data;
1311
1312         RNA_pointer_create(&arm->id, &RNA_Armature, arm, &ptr);
1313         context = BCONTEXT_DATA;
1314         break;
1315       }
1316       case TSE_LINKED_PSYS: {
1317         Object *ob = (Object *)tselem->id;
1318         ParticleSystem *psys = psys_get_current(ob);
1319
1320         RNA_pointer_create(&ob->id, &RNA_ParticleSystem, psys, &ptr);
1321         context = BCONTEXT_PARTICLE;
1322         break;
1323       }
1324       case TSE_GP_LAYER:
1325         RNA_id_pointer_create(tselem->id, &ptr);
1326         context = BCONTEXT_DATA;
1327         break;
1328     }
1329   }
1330
1331   if (ptr.data) {
1332     outliner_sync_to_properties_editors(C, &ptr, context);
1333   }
1334 }
1335
1336 /* ================================================ */
1337
1338 /**
1339  * Action when clicking to activate an item (typically under the mouse cursor),
1340  * but don't do any cursor intersection checks.
1341  *
1342  * Needed to run from operators accessed from a menu.
1343  */
1344 static void do_outliner_item_activate_tree_element(bContext *C,
1345                                                    const TreeViewContext *tvc,
1346                                                    SpaceOutliner *space_outliner,
1347                                                    TreeElement *te,
1348                                                    TreeStoreElem *tselem,
1349                                                    const bool extend,
1350                                                    const bool recursive,
1351                                                    const bool do_activate_data)
1352 {
1353   /* Always makes active object, except for some specific types. */
1354   if (ELEM(tselem->type,
1355            TSE_SEQUENCE,
1356            TSE_SEQ_STRIP,
1357            TSE_SEQUENCE_DUP,
1358            TSE_EBONE,
1359            TSE_LAYER_COLLECTION)) {
1360     /* Note about TSE_EBONE: In case of a same ID_AR datablock shared among several
1361      * objects, we do not want to switch out of edit mode (see T48328 for details). */
1362   }
1363   else if (do_activate_data) {
1364     tree_element_object_activate(C,
1365                                  tvc->scene,
1366                                  tvc->view_layer,
1367                                  te,
1368                                  (extend && tselem->type == TSE_SOME_ID) ? OL_SETSEL_EXTEND :
1369                                                                            OL_SETSEL_NORMAL,
1370                                  recursive && tselem->type == TSE_SOME_ID);
1371   }
1372
1373   if (tselem->type == TSE_SOME_ID) { /* The lib blocks. */
1374     if (do_activate_data == false) {
1375       /* Only select in outliner. */
1376     }
1377     else if (te->idcode == ID_SCE) {
1378       if (tvc->scene != (Scene *)tselem->id) {
1379         WM_window_set_active_scene(CTX_data_main(C), C, CTX_wm_window(C), (Scene *)tselem->id);
1380       }
1381     }
1382     else if ((te->idcode == ID_GR) && (space_outliner->outlinevis != SO_VIEW_LAYER)) {
1383       Collection *gr = (Collection *)tselem->id;
1384
1385       if (extend) {
1386         int sel = BA_SELECT;
1387         FOREACH_COLLECTION_OBJECT_RECURSIVE_BEGIN (gr, object) {
1388           Base *base = BKE_view_layer_base_find(tvc->view_layer, object);
1389           if (base && (base->flag & BASE_SELECTED)) {
1390             sel = BA_DESELECT;
1391             break;
1392           }
1393         }
1394         FOREACH_COLLECTION_OBJECT_RECURSIVE_END;
1395
1396         FOREACH_COLLECTION_OBJECT_RECURSIVE_BEGIN (gr, object) {
1397           Base *base = BKE_view_layer_base_find(tvc->view_layer, object);
1398           if (base) {
1399             ED_object_base_select(base, sel);
1400           }
1401         }
1402         FOREACH_COLLECTION_OBJECT_RECURSIVE_END;
1403       }
1404       else {
1405         BKE_view_layer_base_deselect_all(tvc->view_layer);
1406
1407         FOREACH_COLLECTION_OBJECT_RECURSIVE_BEGIN (gr, object) {
1408           Base *base = BKE_view_layer_base_find(tvc->view_layer, object);
1409           /* Object may not be in this scene */
1410           if (base != NULL) {
1411             if ((base->flag & BASE_SELECTED) == 0) {
1412               ED_object_base_select(base, BA_SELECT);
1413             }
1414           }
1415         }
1416         FOREACH_COLLECTION_OBJECT_RECURSIVE_END;
1417       }
1418
1419       DEG_id_tag_update(&tvc->scene->id, ID_RECALC_SELECT);
1420       WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, tvc->scene);
1421     }
1422     else { /* Rest of types. */
1423       tree_element_activate(C, tvc, te, OL_SETSEL_NORMAL, false);
1424     }
1425   }
1426   else if (do_activate_data) {
1427     tree_element_type_active_set(
1428         C, tvc, te, tselem, extend ? OL_SETSEL_EXTEND : OL_SETSEL_NORMAL, recursive);
1429   }
1430 }
1431
1432 /* Select the item using the set flags */
1433 void outliner_item_select(bContext *C,
1434                           SpaceOutliner *space_outliner,
1435                           TreeElement *te,
1436                           const short select_flag)
1437 {
1438   TreeStoreElem *tselem = TREESTORE(te);
1439   const bool activate = select_flag & OL_ITEM_ACTIVATE;
1440   const bool extend = select_flag & OL_ITEM_EXTEND;
1441   const bool activate_data = select_flag & OL_ITEM_SELECT_DATA;
1442
1443   /* Clear previous active when activating and clear selection when not extending selection */
1444   const short clear_flag = (activate ? TSE_ACTIVE : 0) | (extend ? 0 : TSE_SELECTED);
1445   if (clear_flag) {
1446     outliner_flag_set(&space_outliner->tree, clear_flag, false);
1447   }
1448
1449   if (select_flag & OL_ITEM_SELECT) {
1450     tselem->flag |= TSE_SELECTED;
1451   }
1452   else {
1453     tselem->flag &= ~TSE_SELECTED;
1454   }
1455
1456   if (activate) {
1457     TreeViewContext tvc;
1458     outliner_viewcontext_init(C, &tvc);
1459
1460     tselem->flag |= TSE_ACTIVE;
1461     do_outliner_item_activate_tree_element(C,
1462                                            &tvc,
1463                                            space_outliner,
1464                                            te,
1465                                            tselem,
1466                                            extend,
1467                                            select_flag & OL_ITEM_RECURSIVE,
1468                                            activate_data || space_outliner->flag & SO_SYNC_SELECT);
1469   }
1470 }
1471
1472 static bool do_outliner_range_select_recursive(ListBase *lb,
1473                                                TreeElement *active,
1474                                                TreeElement *cursor,
1475                                                bool selecting)
1476 {
1477   LISTBASE_FOREACH (TreeElement *, te, lb) {
1478     TreeStoreElem *tselem = TREESTORE(te);
1479
1480     if (selecting) {
1481       tselem->flag |= TSE_SELECTED;
1482     }
1483
1484     /* Set state for selection */
1485     if (ELEM(te, active, cursor)) {
1486       selecting = !selecting;
1487     }
1488
1489     if (selecting) {
1490       tselem->flag |= TSE_SELECTED;
1491     }
1492
1493     /* Don't look inside closed elements */
1494     if (!(tselem->flag & TSE_CLOSED)) {
1495       selecting = do_outliner_range_select_recursive(&te->subtree, active, cursor, selecting);
1496     }
1497   }
1498
1499   return selecting;
1500 }
1501
1502 /* Select a range of items between cursor and active element */
1503 static void do_outliner_range_select(bContext *C,
1504                                      SpaceOutliner *space_outliner,
1505                                      TreeElement *cursor,
1506                                      const bool extend)
1507 {
1508   TreeElement *active = outliner_find_element_with_flag(&space_outliner->tree, TSE_ACTIVE);
1509
1510   /* If no active element exists, activate the element under the cursor */
1511   if (!active) {
1512     outliner_item_select(C, space_outliner, cursor, OL_ITEM_SELECT | OL_ITEM_ACTIVATE);
1513     return;
1514   }
1515
1516   TreeStoreElem *tselem = TREESTORE(active);
1517   const bool active_selected = (tselem->flag & TSE_SELECTED);
1518
1519   if (!extend) {
1520     outliner_flag_set(&space_outliner->tree, TSE_SELECTED, false);
1521   }
1522
1523   /* Select active if under cursor */
1524   if (active == cursor) {
1525     outliner_item_select(C, space_outliner, cursor, OL_ITEM_SELECT);
1526     return;
1527   }
1528
1529   /* If active is not selected or visible, select and activate the element under the cursor */
1530   if (!active_selected || !outliner_is_element_visible(active)) {
1531     outliner_item_select(C, space_outliner, cursor, OL_ITEM_SELECT | OL_ITEM_ACTIVATE);
1532     return;
1533   }
1534
1535   do_outliner_range_select_recursive(&space_outliner->tree, active, cursor, false);
1536 }
1537
1538 static bool outliner_is_co_within_restrict_columns(const SpaceOutliner *space_outliner,
1539                                                    const ARegion *region,
1540                                                    float view_co_x)
1541 {
1542   return (view_co_x > region->v2d.cur.xmax - outliner_restrict_columns_width(space_outliner));
1543 }
1544
1545 bool outliner_is_co_within_mode_column(SpaceOutliner *space_outliner, const float view_mval[2])
1546 {
1547   /* Mode toggles only show in View Layer and Scenes modes. */
1548   if (!ELEM(space_outliner->outlinevis, SO_VIEW_LAYER, SO_SCENES)) {
1549     return false;
1550   }
1551
1552   return space_outliner->flag & SO_MODE_COLUMN && view_mval[0] < UI_UNIT_X;
1553 }
1554
1555 static bool outliner_is_co_within_active_mode_column(bContext *C,
1556                                                      SpaceOutliner *space_outliner,
1557                                                      const float view_mval[2])
1558 {
1559   ViewLayer *view_layer = CTX_data_view_layer(C);
1560   Object *obact = OBACT(view_layer);
1561
1562   return outliner_is_co_within_mode_column(space_outliner, view_mval) && obact &&
1563          obact->mode != OB_MODE_OBJECT;
1564 }
1565
1566 /**
1567  * Action to run when clicking in the outliner,
1568  *
1569  * May expend/collapse branches or activate items.
1570  */
1571 static int outliner_item_do_activate_from_cursor(bContext *C,
1572                                                  const int mval[2],
1573                                                  const bool extend,
1574                                                  const bool use_range,
1575                                                  const bool deselect_all)
1576 {
1577   ARegion *region = CTX_wm_region(C);
1578   SpaceOutliner *space_outliner = CTX_wm_space_outliner(C);
1579   TreeElement *te;
1580   float view_mval[2];
1581   bool changed = false, rebuild_tree = false;
1582
1583   UI_view2d_region_to_view(&region->v2d, mval[0], mval[1], &view_mval[0], &view_mval[1]);
1584
1585   if (outliner_is_co_within_restrict_columns(space_outliner, region, view_mval[0])) {
1586     return OPERATOR_CANCELLED;
1587   }
1588   if (outliner_is_co_within_active_mode_column(C, space_outliner, view_mval)) {
1589     return OPERATOR_CANCELLED;
1590   }
1591
1592   if (!(te = outliner_find_item_at_y(space_outliner, &space_outliner->tree, view_mval[1]))) {
1593     if (deselect_all) {
1594       outliner_flag_set(&space_outliner->tree, TSE_SELECTED, false);
1595       changed = true;
1596     }
1597   }
1598   /* Don't allow toggle on scene collection */
1599   else if ((TREESTORE(te)->type != TSE_VIEW_COLLECTION_BASE) &&
1600            outliner_item_is_co_within_close_toggle(te, view_mval[0])) {
1601     return OPERATOR_CANCELLED | OPERATOR_PASS_THROUGH;
1602   }
1603   else {
1604     /* The row may also contain children, if one is hovered we want this instead of current te. */
1605     bool merged_elements = false;
1606     bool is_over_icon = false;
1607     TreeElement *activate_te = outliner_find_item_at_x_in_row(
1608         space_outliner, te, view_mval[0], &merged_elements, &is_over_icon);
1609
1610     /* If the selected icon was an aggregate of multiple elements, run the search popup */
1611     if (merged_elements) {
1612       merged_element_search_menu_invoke(C, te, activate_te);
1613       return OPERATOR_CANCELLED;
1614     }
1615
1616     TreeStoreElem *activate_tselem = TREESTORE(activate_te);
1617
1618     if (use_range) {
1619       do_outliner_range_select(C, space_outliner, activate_te, extend);
1620     }
1621     else {
1622       const bool is_over_name_icons = outliner_item_is_co_over_name_icons(activate_te,
1623                                                                           view_mval[0]);
1624       /* Always select unless already active and selected */
1625       const bool select = !extend || !(activate_tselem->flag & TSE_ACTIVE &&
1626                                        activate_tselem->flag & TSE_SELECTED);
1627
1628       const short select_flag = OL_ITEM_ACTIVATE | (select ? OL_ITEM_SELECT : OL_ITEM_DESELECT) |
1629                                 (is_over_name_icons ? OL_ITEM_SELECT_DATA : 0) |
1630                                 (extend ? OL_ITEM_EXTEND : 0);
1631
1632       outliner_item_select(C, space_outliner, activate_te, select_flag);
1633
1634       /* Only switch properties editor tabs when icons are selected. */
1635       if (is_over_icon) {
1636         outliner_set_properties_tab(C, activate_te, activate_tselem);
1637       }
1638     }
1639
1640     changed = true;
1641   }
1642
1643   if (changed) {
1644     if (rebuild_tree) {
1645       ED_region_tag_redraw(region);
1646     }
1647     else {
1648       ED_region_tag_redraw_no_rebuild(region);
1649     }
1650
1651     ED_outliner_select_sync_from_outliner(C, space_outliner);
1652   }
1653
1654   return OPERATOR_FINISHED;
1655 }
1656
1657 /* event can enterkey, then it opens/closes */
1658 static int outliner_item_activate_invoke(bContext *C, wmOperator *op, const wmEvent *event)
1659 {
1660   const bool extend = RNA_boolean_get(op->ptr, "extend");
1661   const bool use_range = RNA_boolean_get(op->ptr, "extend_range");
1662   const bool deselect_all = RNA_boolean_get(op->ptr, "deselect_all");
1663   return outliner_item_do_activate_from_cursor(C, event->mval, extend, use_range, deselect_all);
1664 }
1665
1666 void OUTLINER_OT_item_activate(wmOperatorType *ot)
1667 {
1668   ot->name = "Select";
1669   ot->idname = "OUTLINER_OT_item_activate";
1670   ot->description = "Handle mouse clicks to select and activate items";
1671
1672   ot->invoke = outliner_item_activate_invoke;
1673
1674   ot->poll = ED_operator_outliner_active;
1675
1676   ot->flag |= OPTYPE_REGISTER | OPTYPE_UNDO;
1677
1678   PropertyRNA *prop;
1679   RNA_def_boolean(ot->srna, "extend", true, "Extend", "Extend selection for activation");
1680   prop = RNA_def_boolean(
1681       ot->srna, "extend_range", false, "Extend Range", "Select a range from active element");
1682   RNA_def_property_flag(prop, PROP_SKIP_SAVE);
1683
1684   prop = RNA_def_boolean(ot->srna,
1685                          "deselect_all",
1686                          false,
1687                          "Deselect On Nothing",
1688                          "Deselect all when nothing under the cursor");
1689   RNA_def_property_flag(prop, PROP_SKIP_SAVE);
1690 }
1691
1692 /* ****************************************************** */
1693
1694 /* **************** Box Select Tool ****************** */
1695 static void outliner_item_box_select(bContext *C,
1696                                      SpaceOutliner *space_outliner,
1697                                      Scene *scene,
1698                                      rctf *rectf,
1699                                      TreeElement *te,
1700                                      bool select)
1701 {
1702   TreeStoreElem *tselem = TREESTORE(te);
1703
1704   if (te->ys <= rectf->ymax && te->ys + UI_UNIT_Y >= rectf->ymin) {
1705     outliner_item_select(
1706         C, space_outliner, te, (select ? OL_ITEM_SELECT : OL_ITEM_DESELECT) | OL_ITEM_EXTEND);
1707   }
1708
1709   /* Look at its children. */
1710   if (TSELEM_OPEN(tselem, space_outliner)) {
1711     LISTBASE_FOREACH (TreeElement *, te_sub, &te->subtree) {
1712       outliner_item_box_select(C, space_outliner, scene, rectf, te_sub, select);
1713     }
1714   }
1715 }
1716
1717 static int outliner_box_select_exec(bContext *C, wmOperator *op)
1718 {
1719   Scene *scene = CTX_data_scene(C);
1720   SpaceOutliner *space_outliner = CTX_wm_space_outliner(C);
1721   ARegion *region = CTX_wm_region(C);
1722   rctf rectf;
1723
1724   const eSelectOp sel_op = RNA_enum_get(op->ptr, "mode");
1725   const bool select = (sel_op != SEL_OP_SUB);
1726   if (SEL_OP_USE_PRE_DESELECT(sel_op)) {
1727     outliner_flag_set(&space_outliner->tree, TSE_SELECTED, 0);
1728   }
1729
1730   WM_operator_properties_border_to_rctf(op, &rectf);
1731   UI_view2d_region_to_view_rctf(&region->v2d, &rectf, &rectf);
1732
1733   LISTBASE_FOREACH (TreeElement *, te, &space_outliner->tree) {
1734     outliner_item_box_select(C, space_outliner, scene, &rectf, te, select);
1735   }
1736
1737   DEG_id_tag_update(&scene->id, ID_RECALC_SELECT);
1738   WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, scene);
1739   ED_region_tag_redraw_no_rebuild(region);
1740
1741   ED_outliner_select_sync_from_outliner(C, space_outliner);
1742
1743   return OPERATOR_FINISHED;
1744 }
1745
1746 static int outliner_box_select_invoke(bContext *C, wmOperator *op, const wmEvent *event)
1747 {
1748   SpaceOutliner *space_outliner = CTX_wm_space_outliner(C);
1749   ARegion *region = CTX_wm_region(C);
1750   float view_mval[2];
1751   const bool tweak = RNA_boolean_get(op->ptr, "tweak");
1752
1753   UI_view2d_region_to_view(
1754       &region->v2d, event->mval[0], event->mval[1], &view_mval[0], &view_mval[1]);
1755
1756   /* Find element clicked on */
1757   TreeElement *te = outliner_find_item_at_y(space_outliner, &space_outliner->tree, view_mval[1]);
1758
1759   /* Pass through if click is over name or icons, or not tweak event */
1760   if (te && tweak && outliner_item_is_co_over_name_icons(te, view_mval[0])) {
1761     return OPERATOR_CANCELLED | OPERATOR_PASS_THROUGH;
1762   }
1763
1764   if (outliner_is_co_within_active_mode_column(C, space_outliner, view_mval)) {
1765     return OPERATOR_CANCELLED | OPERATOR_PASS_THROUGH;
1766   }
1767
1768   return WM_gesture_box_invoke(C, op, event);
1769 }
1770
1771 void OUTLINER_OT_select_box(wmOperatorType *ot)
1772 {
1773   /* identifiers */
1774   ot->name = "Box Select";
1775   ot->idname = "OUTLINER_OT_select_box";
1776   ot->description = "Use box selection to select tree elements";
1777
1778   /* api callbacks */
1779   ot->invoke = outliner_box_select_invoke;
1780   ot->exec = outliner_box_select_exec;
1781   ot->modal = WM_gesture_box_modal;
1782   ot->cancel = WM_gesture_box_cancel;
1783
1784   ot->poll = ED_operator_outliner_active;
1785
1786   /* flags */
1787   ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1788
1789   /* properties */
1790   PropertyRNA *prop;
1791
1792   prop = RNA_def_boolean(
1793       ot->srna, "tweak", false, "Tweak", "Tweak gesture from empty space for box selection");
1794   RNA_def_property_flag(prop, PROP_SKIP_SAVE);
1795
1796   WM_operator_properties_gesture_box(ot);
1797   WM_operator_properties_select_operation_simple(ot);
1798 }
1799
1800 /* ****************************************************** */
1801
1802 /* **************** Walk Select Tool ****************** */
1803
1804 /* Given a tree element return the rightmost child that is visible in the outliner */
1805 static TreeElement *outliner_find_rightmost_visible_child(SpaceOutliner *space_outliner,
1806                                                           TreeElement *te)
1807 {
1808   while (te->subtree.last) {
1809     if (TSELEM_OPEN(TREESTORE(te), space_outliner)) {
1810       te = te->subtree.last;
1811     }
1812     else {
1813       break;
1814     }
1815   }
1816   return te;
1817 }
1818
1819 /* Find previous visible element in the tree  */
1820 static TreeElement *outliner_find_previous_element(SpaceOutliner *space_outliner, TreeElement *te)
1821 {
1822   if (te->prev) {
1823     te = outliner_find_rightmost_visible_child(space_outliner, te->prev);
1824   }
1825   else if (te->parent) {
1826     /* Use parent if at beginning of list */
1827     te = te->parent;
1828   }
1829
1830   return te;
1831 }
1832
1833 /* Recursively search up the tree until a successor to a given element is found */
1834 static TreeElement *outliner_element_find_successor_in_parents(TreeElement *te)
1835 {
1836   TreeElement *successor = te;
1837   while (successor->parent) {
1838     if (successor->parent->next) {
1839       te = successor->parent->next;
1840       break;
1841     }
1842     successor = successor->parent;
1843   }
1844
1845   return te;
1846 }
1847
1848 /* Find next visible element in the tree */
1849 static TreeElement *outliner_find_next_element(SpaceOutliner *space_outliner, TreeElement *te)
1850 {
1851   TreeStoreElem *tselem = TREESTORE(te);
1852
1853   if (TSELEM_OPEN(tselem, space_outliner) && te->subtree.first) {
1854     te = te->subtree.first;
1855   }
1856   else if (te->next) {
1857     te = te->next;
1858   }
1859   else {
1860     te = outliner_element_find_successor_in_parents(te);
1861   }
1862
1863   return te;
1864 }
1865
1866 static TreeElement *outliner_walk_left(SpaceOutliner *space_outliner,
1867                                        TreeElement *te,
1868                                        bool toggle_all)
1869 {
1870   TreeStoreElem *tselem = TREESTORE(te);
1871
1872   if (TSELEM_OPEN(tselem, space_outliner)) {
1873     outliner_item_openclose(space_outliner, te, false, toggle_all);
1874   }
1875   /* Only walk up a level if the element is closed and not toggling expand */
1876   else if (!toggle_all && te->parent) {
1877     te = te->parent;
1878   }
1879
1880   return te;
1881 }
1882
1883 static TreeElement *outliner_walk_right(SpaceOutliner *space_outliner,
1884                                         TreeElement *te,
1885                                         bool toggle_all)
1886 {
1887   TreeStoreElem *tselem = TREESTORE(te);
1888
1889   /* Only walk down a level if the element is open and not toggling expand */
1890   if (!toggle_all && TSELEM_OPEN(tselem, space_outliner) && !BLI_listbase_is_empty(&te->subtree)) {
1891     te = te->subtree.first;
1892   }
1893   else {
1894     outliner_item_openclose(space_outliner, te, true, toggle_all);
1895   }
1896
1897   return te;
1898 }
1899
1900 static TreeElement *do_outliner_select_walk(SpaceOutliner *space_outliner,
1901                                             TreeElement *te,
1902                                             const int direction,
1903                                             const bool extend,
1904                                             const bool toggle_all)
1905 {
1906   TreeStoreElem *tselem = TREESTORE(te);
1907
1908   switch (direction) {
1909     case UI_SELECT_WALK_UP:
1910       te = outliner_find_previous_element(space_outliner, te);
1911       break;
1912     case UI_SELECT_WALK_DOWN:
1913       te = outliner_find_next_element(space_outliner, te);
1914       break;
1915     case UI_SELECT_WALK_LEFT:
1916       te = outliner_walk_left(space_outliner, te, toggle_all);
1917       break;
1918     case UI_SELECT_WALK_RIGHT:
1919       te = outliner_walk_right(space_outliner, te, toggle_all);
1920       break;
1921   }
1922
1923   /* If new element is already selected, deselect the previous element */
1924   TreeStoreElem *tselem_new = TREESTORE(te);
1925   if (extend) {
1926     tselem->flag = (tselem_new->flag & TSE_SELECTED) ? (tselem->flag & ~TSE_SELECTED) :
1927                                                        (tselem->flag | TSE_SELECTED);
1928   }
1929
1930   return te;
1931 }
1932
1933 /* Find the active element to walk from, or set one if none exists.
1934  * Changed is set to true if the active element is found, or false if it was set */
1935 static TreeElement *find_walk_select_start_element(SpaceOutliner *space_outliner, bool *changed)
1936 {
1937   TreeElement *active_te = outliner_find_element_with_flag(&space_outliner->tree, TSE_ACTIVE);
1938   *changed = false;
1939
1940   /* If no active element exists, use the first element in the tree */
1941   if (!active_te) {
1942     active_te = space_outliner->tree.first;
1943     *changed = true;
1944   }
1945
1946   /* If the active element is not visible, activate the first visible parent element */
1947   if (!outliner_is_element_visible(active_te)) {
1948     while (!outliner_is_element_visible(active_te)) {
1949       active_te = active_te->parent;
1950     }
1951     *changed = true;
1952   }
1953
1954   return active_te;
1955 }
1956
1957 /* Scroll the outliner when the walk element reaches the top or bottom boundary */
1958 static void outliner_walk_scroll(SpaceOutliner *space_outliner, ARegion *region, TreeElement *te)
1959 {
1960   /* Account for the header height */
1961   int y_max = region->v2d.cur.ymax - UI_UNIT_Y;
1962   int y_min = region->v2d.cur.ymin;
1963
1964   /* Scroll if walked position is beyond the border */
1965   if (te->ys > y_max) {
1966     outliner_scroll_view(space_outliner, region, te->ys - y_max);
1967   }
1968   else if (te->ys < y_min) {
1969     outliner_scroll_view(space_outliner, region, -(y_min - te->ys));
1970   }
1971 }
1972
1973 static int outliner_walk_select_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
1974 {
1975   SpaceOutliner *space_outliner = CTX_wm_space_outliner(C);
1976   ARegion *region = CTX_wm_region(C);
1977
1978   const short direction = RNA_enum_get(op->ptr, "direction");
1979   const bool extend = RNA_boolean_get(op->ptr, "extend");
1980   const bool toggle_all = RNA_boolean_get(op->ptr, "toggle_all");
1981
1982   bool changed;
1983   TreeElement *active_te = find_walk_select_start_element(space_outliner, &changed);
1984
1985   /* If finding the active element did not modify the selection, proceed to walk */
1986   if (!changed) {
1987     active_te = do_outliner_select_walk(space_outliner, active_te, direction, extend, toggle_all);
1988   }
1989
1990   outliner_item_select(C,
1991                        space_outliner,
1992                        active_te,
1993                        OL_ITEM_SELECT | OL_ITEM_ACTIVATE | (extend ? OL_ITEM_EXTEND : 0));
1994
1995   /* Scroll outliner to focus on walk element */
1996   outliner_walk_scroll(space_outliner, region, active_te);
1997
1998   ED_outliner_select_sync_from_outliner(C, space_outliner);
1999   outliner_tag_redraw_avoid_rebuild_on_open_change(space_outliner, region);
2000
2001   return OPERATOR_FINISHED;
2002 }
2003
2004 void OUTLINER_OT_select_walk(wmOperatorType *ot)
2005 {
2006   /* identifiers */
2007   ot->name = "Walk Select";
2008   ot->idname = "OUTLINER_OT_select_walk";
2009   ot->description = "Use walk navigation to select tree elements";
2010
2011   /* api callbacks */
2012   ot->invoke = outliner_walk_select_invoke;
2013   ot->poll = ED_operator_outliner_active;
2014
2015   ot->flag |= OPTYPE_REGISTER | OPTYPE_UNDO;
2016
2017   /* properties */
2018   PropertyRNA *prop;
2019   WM_operator_properties_select_walk_direction(ot);
2020   prop = RNA_def_boolean(ot->srna, "extend", false, "Extend", "Extend selection on walk");
2021   RNA_def_property_flag(prop, PROP_SKIP_SAVE);
2022   prop = RNA_def_boolean(
2023       ot->srna, "toggle_all", false, "Toggle All", "Toggle open/close hierarchy");
2024   RNA_def_property_flag(prop, PROP_SKIP_SAVE);
2025 }
2026
2027 /* ****************************************************** */