Selecing a pose bone in outliner deselect others
[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_light_types.h"
31 #include "DNA_material_types.h"
32 #include "DNA_object_types.h"
33 #include "DNA_scene_types.h"
34 #include "DNA_sequence_types.h"
35 #include "DNA_world_types.h"
36 #include "DNA_gpencil_types.h"
37
38 #include "BLI_utildefines.h"
39 #include "BLI_listbase.h"
40
41 #include "BKE_armature.h"
42 #include "BKE_collection.h"
43 #include "BKE_context.h"
44 #include "BKE_gpencil.h"
45 #include "BKE_layer.h"
46 #include "BKE_main.h"
47 #include "BKE_object.h"
48 #include "BKE_paint.h"
49 #include "BKE_scene.h"
50 #include "BKE_sequencer.h"
51 #include "BKE_workspace.h"
52
53 #include "DEG_depsgraph.h"
54
55 #include "ED_armature.h"
56 #include "ED_object.h"
57 #include "ED_screen.h"
58 #include "ED_select_utils.h"
59 #include "ED_sequencer.h"
60 #include "ED_undo.h"
61 #include "ED_gpencil.h"
62
63 #include "WM_api.h"
64 #include "WM_types.h"
65
66
67 #include "UI_interface.h"
68 #include "UI_view2d.h"
69
70 #include "RNA_access.h"
71 #include "RNA_define.h"
72
73 #include "outliner_intern.h"
74
75 static bool do_outliner_activate_common(
76         bContext *C,
77         Main *bmain,
78         Depsgraph *depsgraph,
79         Scene *scene,
80         ViewLayer *view_layer,
81         Base *base,
82         const bool extend,
83         const bool do_exit)
84 {
85         bool use_all = false;
86
87         if (do_exit) {
88                 FOREACH_OBJECT_BEGIN(view_layer, ob_iter)
89                 {
90                         ED_object_mode_generic_exit(bmain, depsgraph, scene, ob_iter);
91                 }
92                 FOREACH_OBJECT_END;
93         }
94
95         /* Just like clicking in the object changes the active object,
96          * clicking on the object data should change it as well. */
97         ED_object_base_activate(C, base);
98
99         if (extend) {
100                 use_all = true;
101         }
102         else {
103                 ED_object_base_deselect_all(view_layer, NULL, SEL_DESELECT);
104         }
105
106         return use_all;
107 }
108
109 /**
110  * Bring the newly selected object into edit mode.
111  *
112  * If extend is used, we try to have the other compatible selected objects in the new mode as well.
113  * Otherwise only the new object will be active, selected and in the edit mode.
114  */
115 static void do_outliner_activate_obdata(bContext *C, Scene *scene, ViewLayer *view_layer, Base *base, const bool extend)
116 {
117         Main *bmain = CTX_data_main(C);
118         Depsgraph *depsgraph = CTX_data_depsgraph(C);
119         Object *obact = OBACT(view_layer);
120         Object *ob = base->object;
121         bool use_all = false;
122
123         if (obact == NULL) {
124                 ED_object_base_activate(C, base);
125                 DEG_id_tag_update(&scene->id, ID_RECALC_SELECT);
126                 WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, scene);
127                 obact = ob;
128                 use_all = true;
129         }
130         else if (obact->data == ob->data) {
131                 use_all = true;
132         }
133         else if (obact->mode == OB_MODE_OBJECT) {
134                 use_all = do_outliner_activate_common(C, bmain, depsgraph, scene, view_layer, base, extend, false);
135         }
136         else if ((ob->type != obact->type) ||
137                  ((obact->mode & OB_MODE_EDIT) == 0) ||
138                  ((obact->mode & OB_MODE_POSE) && ELEM(OB_ARMATURE, ob->type, obact->type)) ||
139                  !extend)
140         {
141                 use_all = do_outliner_activate_common(C, bmain, depsgraph, scene, view_layer, base, extend, true);
142         }
143
144         if (use_all) {
145                 WM_operator_name_call(C, "OBJECT_OT_editmode_toggle", WM_OP_INVOKE_REGION_WIN, NULL);
146         }
147         else {
148                 bool ok;
149                 if (BKE_object_is_in_editmode(ob)) {
150                         ok = ED_object_editmode_exit_ex(bmain, scene, ob, EM_FREEDATA);
151                 }
152                 else {
153                         ok = ED_object_editmode_enter_ex(CTX_data_main(C), scene, ob, EM_NO_CONTEXT);
154                 }
155                 if (ok) {
156                         ED_object_base_select(base, (ob->mode & OB_MODE_EDIT) ? BA_SELECT : BA_DESELECT);
157                         DEG_id_tag_update(&scene->id, ID_RECALC_SELECT);
158                         WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, scene);
159                 }
160         }
161 }
162
163 static void do_outliner_activate_pose(bContext *C, Scene *scene, ViewLayer *view_layer, Base *base, const bool extend)
164 {
165         Main *bmain = CTX_data_main(C);
166         Depsgraph *depsgraph = CTX_data_depsgraph(C);
167         Object *obact = OBACT(view_layer);
168         Object *ob = base->object;
169         bool use_all = false;
170
171         if (obact == NULL) {
172                 ED_object_base_activate(C, base);
173                 DEG_id_tag_update(&scene->id, ID_RECALC_SELECT);
174                 WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, scene);
175                 obact = ob;
176                 use_all = true;
177         }
178         else if (obact->data == ob->data) {
179                 use_all = true;
180         }
181         else if (obact->mode == OB_MODE_OBJECT) {
182                 use_all = do_outliner_activate_common(C, bmain, depsgraph, scene, view_layer, base, extend, false);
183         }
184         else if ((!ELEM(ob->type, obact->type)) ||
185                  ((obact->mode & OB_MODE_EDIT) && ELEM(OB_ARMATURE, ob->type, obact->type)))
186         {
187                 use_all = do_outliner_activate_common(C, bmain, depsgraph, scene, view_layer, base, extend, true);
188         }
189
190         if (use_all) {
191                 WM_operator_name_call(C, "OBJECT_OT_posemode_toggle", WM_OP_INVOKE_REGION_WIN, NULL);
192         }
193         else {
194                 bool ok = false;
195                 if (ob->mode & OB_MODE_POSE) {
196                         ok = ED_object_posemode_exit_ex(bmain, ob);
197                 }
198                 else {
199                         ok = ED_object_posemode_enter_ex(bmain, ob);
200                 }
201                 if (ok) {
202                         ED_object_base_select(base, (ob->mode & OB_MODE_POSE) ? BA_SELECT : BA_DESELECT);
203
204                         DEG_id_tag_update(&scene->id, ID_RECALC_SELECT);
205                         WM_event_add_notifier(C, NC_SCENE | ND_MODE | NS_MODE_OBJECT, NULL);
206                         WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, scene);
207                 }
208         }
209 }
210
211 /* For draw callback to run mode switching */
212 void outliner_object_mode_toggle(
213         bContext *C, Scene *scene, ViewLayer *view_layer,
214         Base *base)
215 {
216         Object *obact = OBACT(view_layer);
217         if (obact->mode & OB_MODE_EDIT) {
218                 do_outliner_activate_obdata(C, scene, view_layer, base, true);
219         }
220         else if (obact->mode & OB_MODE_POSE) {
221                 do_outliner_activate_pose(C, scene, view_layer, base, true);
222         }
223 }
224
225 /* ****************************************************** */
226 /* Outliner Element Selection/Activation on Click */
227
228 static eOLDrawState active_viewlayer(
229         bContext *C, Scene *UNUSED(scene), ViewLayer *UNUSED(sl), TreeElement *te, const eOLSetState set)
230 {
231         /* paranoia check */
232         if (te->idcode != ID_SCE) {
233                 return OL_DRAWSEL_NONE;
234         }
235
236         ViewLayer *view_layer = te->directdata;
237
238         if (set != OL_SETSEL_NONE) {
239                 wmWindow *win = CTX_wm_window(C);
240                 Scene *scene = WM_window_get_active_scene(win);
241
242                 if (BLI_findindex(&scene->view_layers, view_layer) != -1) {
243                         WM_window_set_active_view_layer(win, view_layer);
244                         WM_event_add_notifier(C, NC_SCREEN | ND_LAYER, NULL);
245                 }
246         }
247         else {
248                 return CTX_data_view_layer(C) == view_layer;
249         }
250         return OL_DRAWSEL_NONE;
251 }
252
253 /**
254  * Select object tree:
255  * CTRL+LMB: Select/Deselect object and all children.
256  * CTRL+SHIFT+LMB: Add/Remove object and all children.
257  */
258 static void do_outliner_object_select_recursive(ViewLayer *view_layer, Object *ob_parent, bool select)
259 {
260         Base *base;
261
262         for (base = FIRSTBASE(view_layer); base; base = base->next) {
263                 Object *ob = base->object;
264                 if ((((base->flag & BASE_VISIBLE) != 0) && BKE_object_is_child_recursive(ob_parent, ob))) {
265                         ED_object_base_select(base, select ? BA_SELECT : BA_DESELECT);
266                 }
267         }
268 }
269
270 static void do_outliner_bone_select_recursive(bArmature *arm, Bone *bone_parent, bool select)
271 {
272         Bone *bone;
273         for (bone = bone_parent->childbase.first; bone; bone = bone->next) {
274                 if (select && PBONE_SELECTABLE(arm, bone)) {
275                         bone->flag |= BONE_SELECTED;
276                 }
277                 else {
278                         bone->flag &= ~(BONE_TIPSEL | BONE_SELECTED | BONE_ROOTSEL);
279                 }
280                 do_outliner_bone_select_recursive(arm, bone, select);
281         }
282 }
283
284 static void do_outliner_ebone_select_recursive(bArmature *arm, EditBone *ebone_parent, bool select)
285 {
286         EditBone *ebone;
287         for (ebone = ebone_parent->next; ebone; ebone = ebone->next) {
288                 if (ED_armature_ebone_is_child_recursive(ebone_parent, ebone)) {
289                         if (select && EBONE_SELECTABLE(arm, ebone)) {
290                                 ebone->flag |= BONE_TIPSEL | BONE_SELECTED | BONE_ROOTSEL;
291                         }
292                         else {
293                                 ebone->flag &= ~(BONE_TIPSEL | BONE_SELECTED | BONE_ROOTSEL);
294                         }
295                 }
296         }
297 }
298
299 static eOLDrawState tree_element_set_active_object(
300         bContext *C, Scene *scene, ViewLayer *view_layer, SpaceOutliner *soops,
301         TreeElement *te, const eOLSetState set, bool recursive)
302 {
303         TreeStoreElem *tselem = TREESTORE(te);
304         Scene *sce;
305         Base *base;
306         Object *ob = NULL;
307
308         /* if id is not object, we search back */
309         if (te->idcode == ID_OB) {
310                 ob = (Object *)tselem->id;
311         }
312         else {
313                 ob = (Object *)outliner_search_back(soops, te, ID_OB);
314                 if (ob == OBACT(view_layer)) {
315                         return OL_DRAWSEL_NONE;
316                 }
317         }
318         if (ob == NULL) {
319                 return OL_DRAWSEL_NONE;
320         }
321
322         sce = (Scene *)outliner_search_back(soops, te, ID_SCE);
323         if (sce && scene != sce) {
324                 WM_window_set_active_scene(CTX_data_main(C), C, CTX_wm_window(C), sce);
325                 scene = sce;
326         }
327
328         /* find associated base in current scene */
329         base = BKE_view_layer_base_find(view_layer, ob);
330
331         if (scene->toolsettings->object_flag & SCE_OBJECT_MODE_LOCK) {
332                 if (base != NULL) {
333                         Object *obact = OBACT(view_layer);
334                         const eObjectMode object_mode = obact ? obact->mode : OB_MODE_OBJECT;
335                         if (base && !BKE_object_is_mode_compat(base->object, object_mode)) {
336                                 if (object_mode == OB_MODE_OBJECT) {
337                                         struct Main *bmain = CTX_data_main(C);
338                                         Depsgraph *depsgraph = CTX_data_depsgraph(C);
339                                         ED_object_mode_generic_exit(bmain, depsgraph, scene, base->object);
340                                 }
341                                 if (!BKE_object_is_mode_compat(base->object, object_mode)) {
342                                         base = NULL;
343                                 }
344                         }
345                 }
346         }
347
348         if (base) {
349                 if (set == OL_SETSEL_EXTEND) {
350                         /* swap select */
351                         if (base->flag & BASE_SELECTED) {
352                                 ED_object_base_select(base, BA_DESELECT);
353                         }
354                         else {
355                                 ED_object_base_select(base, BA_SELECT);
356                         }
357                 }
358                 else {
359                         /* deleselect all */
360
361                         /* Only in object mode so we can switch the active object,
362                          * keeping all objects in the current 'mode' selected, useful for multi-pose/edit mode.
363                          * This keeps the convention that all objects in the current mode are also selected.
364                          * see T55246. */
365                         if ((scene->toolsettings->object_flag & SCE_OBJECT_MODE_LOCK) ? (ob->mode == OB_MODE_OBJECT) : true) {
366                                 BKE_view_layer_base_deselect_all(view_layer);
367                         }
368                         ED_object_base_select(base, BA_SELECT);
369                 }
370
371                 if (recursive) {
372                         /* Recursive select/deselect for Object hierarchies */
373                         do_outliner_object_select_recursive(view_layer, ob, (base->flag & BASE_SELECTED) != 0);
374                 }
375
376                 if (set != OL_SETSEL_NONE) {
377                         ED_object_base_activate(C, base); /* adds notifier */
378                         DEG_id_tag_update(&scene->id, ID_RECALC_SELECT);
379                         WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, scene);
380                 }
381
382                 if (ob != OBEDIT_FROM_VIEW_LAYER(view_layer)) {
383                         ED_object_editmode_exit(C, EM_FREEDATA);
384                 }
385         }
386         return OL_DRAWSEL_NORMAL;
387 }
388
389 static eOLDrawState tree_element_active_material(
390         bContext *C, Scene *UNUSED(scene), ViewLayer *view_layer, SpaceOutliner *soops,
391         TreeElement *te, const eOLSetState set)
392 {
393         TreeElement *tes;
394         Object *ob;
395
396         /* we search for the object parent */
397         ob = (Object *)outliner_search_back(soops, te, ID_OB);
398         // note: ob->matbits can be NULL when a local object points to a library mesh.
399         if (ob == NULL || ob != OBACT(view_layer) || ob->matbits == NULL) {
400                 return OL_DRAWSEL_NONE;  /* just paranoia */
401         }
402
403         /* searching in ob mat array? */
404         tes = te->parent;
405         if (tes->idcode == ID_OB) {
406                 if (set != OL_SETSEL_NONE) {
407                         ob->actcol = te->index + 1;
408                         ob->matbits[te->index] = 1;  // make ob material active too
409                 }
410                 else {
411                         if (ob->actcol == te->index + 1) {
412                                 if (ob->matbits[te->index]) {
413                                         return OL_DRAWSEL_NORMAL;
414                                 }
415                         }
416                 }
417         }
418         /* or we search for obdata material */
419         else {
420                 if (set != OL_SETSEL_NONE) {
421                         ob->actcol = te->index + 1;
422                         ob->matbits[te->index] = 0;  // make obdata material active too
423                 }
424                 else {
425                         if (ob->actcol == te->index + 1) {
426                                 if (ob->matbits[te->index] == 0) {
427                                         return OL_DRAWSEL_NORMAL;
428                                 }
429                         }
430                 }
431         }
432         if (set != OL_SETSEL_NONE) {
433                 /* Tagging object for update seems a bit stupid here, but looks like we have to do it
434                  * for render views to update. See T42973.
435                  * Note that RNA material update does it too, see e.g. rna_MaterialSlot_update(). */
436                 DEG_id_tag_update((ID *)ob, ID_RECALC_TRANSFORM);
437                 WM_event_add_notifier(C, NC_MATERIAL | ND_SHADING_LINKS, NULL);
438         }
439         return OL_DRAWSEL_NONE;
440 }
441
442 static eOLDrawState tree_element_active_light(
443         bContext *UNUSED(C), Scene *UNUSED(scene), ViewLayer *view_layer, SpaceOutliner *soops,
444         TreeElement *te, const eOLSetState set)
445 {
446         Object *ob;
447
448         /* we search for the object parent */
449         ob = (Object *)outliner_search_back(soops, te, ID_OB);
450         if (ob == NULL || ob != OBACT(view_layer)) {
451                 /* just paranoia */
452                 return OL_DRAWSEL_NONE;
453         }
454
455         if (set != OL_SETSEL_NONE) {
456 // XXX          extern_set_butspace(F5KEY, 0);
457         }
458         else {
459                 return OL_DRAWSEL_NORMAL;
460         }
461
462         return OL_DRAWSEL_NONE;
463 }
464
465 static eOLDrawState tree_element_active_camera(
466         bContext *UNUSED(C), Scene *scene, ViewLayer *UNUSED(sl), SpaceOutliner *soops,
467         TreeElement *te, const eOLSetState set)
468 {
469         Object *ob = (Object *)outliner_search_back(soops, te, ID_OB);
470
471         if (set != OL_SETSEL_NONE) {
472                 return OL_DRAWSEL_NONE;
473         }
474
475         return scene->camera == ob;
476 }
477
478 static eOLDrawState tree_element_active_world(
479         bContext *C, Scene *scene, ViewLayer *UNUSED(sl), SpaceOutliner *UNUSED(soops),
480         TreeElement *te, const eOLSetState set)
481 {
482         TreeElement *tep;
483         TreeStoreElem *tselem = NULL;
484         Scene *sce = NULL;
485
486         tep = te->parent;
487         if (tep) {
488                 tselem = TREESTORE(tep);
489                 if (tselem->type == 0) {
490                         sce = (Scene *)tselem->id;
491                 }
492         }
493
494         if (set != OL_SETSEL_NONE) {
495                 /* make new scene active */
496                 if (sce && scene != sce) {
497                         WM_window_set_active_scene(CTX_data_main(C), C, CTX_wm_window(C), sce);
498                 }
499         }
500
501         if (tep == NULL || tselem->id == (ID *)scene) {
502                 if (set != OL_SETSEL_NONE) {
503 // XXX                  extern_set_butspace(F8KEY, 0);
504                 }
505                 else {
506                         return OL_DRAWSEL_NORMAL;
507                 }
508         }
509         return OL_DRAWSEL_NONE;
510 }
511
512 static eOLDrawState tree_element_active_defgroup(
513         bContext *C, ViewLayer *view_layer, TreeElement *te, TreeStoreElem *tselem, const eOLSetState set)
514 {
515         Object *ob;
516
517         /* id in tselem is object */
518         ob = (Object *)tselem->id;
519         if (set != OL_SETSEL_NONE) {
520                 BLI_assert(te->index + 1 >= 0);
521                 ob->actdef = te->index + 1;
522
523                 DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
524                 WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, ob);
525         }
526         else {
527                 if (ob == OBACT(view_layer))
528                         if (ob->actdef == te->index + 1) {
529                                 return OL_DRAWSEL_NORMAL;
530                         }
531         }
532         return OL_DRAWSEL_NONE;
533 }
534
535 static eOLDrawState tree_element_active_gplayer(
536         bContext *C, Scene *UNUSED(scene), TreeElement *te, TreeStoreElem *tselem, const eOLSetState set)
537 {
538         bGPdata *gpd = (bGPdata *)tselem->id;
539         bGPDlayer *gpl = te->directdata;
540
541         /* We can only have a single "active" layer at a time
542          * and there must always be an active layer...
543          */
544         if (set != OL_SETSEL_NONE) {
545                 if (gpl) {
546                         BKE_gpencil_layer_setactive(gpd, gpl);
547                         WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_SELECTED, gpd);
548                 }
549         }
550         else {
551                 return OL_DRAWSEL_NORMAL;
552         }
553
554         return OL_DRAWSEL_NONE;
555 }
556
557 static eOLDrawState tree_element_active_posegroup(
558         bContext *C, Scene *UNUSED(scene), ViewLayer *view_layer, TreeElement *te, TreeStoreElem *tselem, const eOLSetState set)
559 {
560         Object *ob = (Object *)tselem->id;
561
562         if (set != OL_SETSEL_NONE) {
563                 if (ob->pose) {
564                         ob->pose->active_group = te->index + 1;
565                         WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob);
566                 }
567         }
568         else {
569                 if (ob == OBACT(view_layer) && ob->pose) {
570                         if (ob->pose->active_group == te->index + 1) {
571                                 return OL_DRAWSEL_NORMAL;
572                         }
573                 }
574         }
575         return OL_DRAWSEL_NONE;
576 }
577
578 static eOLDrawState tree_element_active_posechannel(
579         bContext *C, Scene *UNUSED(scene), ViewLayer *view_layer, TreeElement *te, TreeStoreElem *tselem, const eOLSetState set, bool recursive)
580 {
581         Object *ob = (Object *)tselem->id;
582         bArmature *arm = ob->data;
583         bPoseChannel *pchan = te->directdata;
584
585         if (set != OL_SETSEL_NONE) {
586                 if (!(pchan->bone->flag & BONE_HIDDEN_P)) {
587
588                         if (set != OL_SETSEL_EXTEND) {
589                                 /* Single select forces all other bones to get unselected. */
590                                 uint objects_len = 0;
591                                 Object **objects = BKE_view_layer_array_from_objects_in_mode_unique_data(view_layer, NULL, &objects_len, OB_MODE_POSE);
592                                 for (uint object_index = 0; object_index < objects_len; object_index++) {
593                                         Object *ob_iter = BKE_object_pose_armature_get(objects[object_index]);
594
595                                         /* Sanity checks. */
596                                         if (ELEM(NULL, ob_iter, ob_iter->pose, ob_iter->data)) {
597                                                 continue;
598                                         }
599
600                                         bPoseChannel *pchannel;
601                                         for (pchannel = ob_iter->pose->chanbase.first; pchannel; pchannel = pchannel->next) {
602                                                 pchannel->bone->flag &= ~(BONE_TIPSEL | BONE_SELECTED | BONE_ROOTSEL);
603                                         }
604
605                                         if (ob != ob_iter) {
606                                                 DEG_id_tag_update(ob_iter->data, ID_RECALC_SELECT);
607                                         }
608                                 }
609                                 MEM_freeN(objects);
610                         }
611
612                         if ((set == OL_SETSEL_EXTEND) && (pchan->bone->flag & BONE_SELECTED)) {
613                                 pchan->bone->flag &= ~BONE_SELECTED;
614                         }
615                         else {
616                                 pchan->bone->flag |= BONE_SELECTED;
617                                 arm->act_bone = pchan->bone;
618                         }
619
620                         if (recursive) {
621                                 /* Recursive select/deselect */
622                                 do_outliner_bone_select_recursive(arm, pchan->bone, (pchan->bone->flag & BONE_SELECTED) != 0);
623                         }
624
625                         WM_event_add_notifier(C, NC_OBJECT | ND_BONE_ACTIVE, ob);
626                         DEG_id_tag_update(&arm->id, ID_RECALC_SELECT);
627                 }
628         }
629         else {
630                 if (ob == OBACT(view_layer) && ob->pose) {
631                         if (pchan->bone->flag & BONE_SELECTED) {
632                                 return OL_DRAWSEL_NORMAL;
633                         }
634                 }
635         }
636         return OL_DRAWSEL_NONE;
637 }
638
639 static eOLDrawState tree_element_active_bone(
640         bContext *C, ViewLayer *view_layer, TreeElement *te, TreeStoreElem *tselem, const eOLSetState set, bool recursive)
641 {
642         bArmature *arm = (bArmature *)tselem->id;
643         Bone *bone = te->directdata;
644
645         if (set != OL_SETSEL_NONE) {
646                 if (!(bone->flag & BONE_HIDDEN_P)) {
647                         Object *ob = OBACT(view_layer);
648                         if (ob) {
649                                 if (set != OL_SETSEL_EXTEND) {
650                                         /* single select forces all other bones to get unselected */
651                                         for (Bone *bone_iter = arm->bonebase.first; bone_iter != NULL; bone_iter = bone_iter->next) {
652                                                 bone_iter->flag &= ~(BONE_TIPSEL | BONE_SELECTED | BONE_ROOTSEL);
653                                                 do_outliner_bone_select_recursive(arm, bone_iter, false);
654                                         }
655                                 }
656                         }
657
658                         if (set == OL_SETSEL_EXTEND && (bone->flag & BONE_SELECTED)) {
659                                 bone->flag &= ~BONE_SELECTED;
660                         }
661                         else {
662                                 bone->flag |= BONE_SELECTED;
663                                 arm->act_bone = bone;
664                         }
665
666                         if (recursive) {
667                                 /* Recursive select/deselect */
668                                 do_outliner_bone_select_recursive(arm, bone, (bone->flag & BONE_SELECTED) != 0);
669                         }
670
671
672                         WM_event_add_notifier(C, NC_OBJECT | ND_BONE_ACTIVE, ob);
673                 }
674         }
675         else {
676                 Object *ob = OBACT(view_layer);
677
678                 if (ob && ob->data == arm) {
679                         if (bone->flag & BONE_SELECTED) {
680                                 return OL_DRAWSEL_NORMAL;
681                         }
682                 }
683         }
684         return OL_DRAWSEL_NONE;
685 }
686
687
688 /* ebones only draw in editmode armature */
689 static void tree_element_active_ebone__sel(bContext *C, bArmature *arm, EditBone *ebone, short sel)
690 {
691         if (sel) {
692                 ebone->flag |= BONE_SELECTED | BONE_ROOTSEL | BONE_TIPSEL;
693                 arm->act_edbone = ebone;
694                 // flush to parent?
695                 if (ebone->parent && (ebone->flag & BONE_CONNECTED)) {
696                         ebone->parent->flag |= BONE_TIPSEL;
697                 }
698         }
699         else {
700                 ebone->flag &= ~(BONE_SELECTED | BONE_ROOTSEL | BONE_TIPSEL);
701                 // flush to parent?
702                 if (ebone->parent && (ebone->flag & BONE_CONNECTED)) {
703                         ebone->parent->flag &= ~BONE_TIPSEL;
704                 }
705         }
706         WM_event_add_notifier(C, NC_OBJECT | ND_BONE_ACTIVE, CTX_data_edit_object(C));
707 }
708 static eOLDrawState tree_element_active_ebone(
709         bContext *C, ViewLayer *view_layer, TreeElement *te, TreeStoreElem *tselem, const eOLSetState set, bool recursive)
710 {
711         bArmature *arm = (bArmature *)tselem->id;
712         EditBone *ebone = te->directdata;
713         eOLDrawState status = OL_DRAWSEL_NONE;
714
715         if (set != OL_SETSEL_NONE) {
716                 if (set == OL_SETSEL_NORMAL) {
717                         if (!(ebone->flag & BONE_HIDDEN_A)) {
718                                 uint bases_len = 0;
719                                 Base **bases = BKE_view_layer_array_from_bases_in_edit_mode_unique_data(view_layer, NULL, &bases_len);
720                                 ED_armature_edit_deselect_all_multi_ex(bases, bases_len);
721                                 MEM_freeN(bases);
722
723                                 tree_element_active_ebone__sel(C, arm, ebone, true);
724                                 status = OL_DRAWSEL_NORMAL;
725                         }
726                 }
727                 else if (set == OL_SETSEL_EXTEND) {
728                         if (!(ebone->flag & BONE_HIDDEN_A)) {
729                                 if (!(ebone->flag & BONE_SELECTED)) {
730                                         tree_element_active_ebone__sel(C, arm, ebone, true);
731                                         status = OL_DRAWSEL_NORMAL;
732                                 }
733                                 else {
734                                         /* entirely selected, so de-select */
735                                         tree_element_active_ebone__sel(C, arm, ebone, false);
736                                         status = OL_DRAWSEL_NONE;
737                                 }
738                         }
739                 }
740
741                 if (recursive) {
742                         /* Recursive select/deselect */
743                         do_outliner_ebone_select_recursive(arm, ebone, (ebone->flag & BONE_SELECTED) != 0);
744                 }
745         }
746         else if (ebone->flag & BONE_SELECTED) {
747                 status = OL_DRAWSEL_NORMAL;
748         }
749
750         return status;
751 }
752
753 static eOLDrawState tree_element_active_modifier(
754         bContext *C, Scene *UNUSED(scene), ViewLayer *UNUSED(sl), TreeElement *UNUSED(te), TreeStoreElem *tselem, const eOLSetState set)
755 {
756         if (set != OL_SETSEL_NONE) {
757                 Object *ob = (Object *)tselem->id;
758
759                 WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER, ob);
760
761 // XXX          extern_set_butspace(F9KEY, 0);
762         }
763
764         return OL_DRAWSEL_NONE;
765 }
766
767 static eOLDrawState tree_element_active_psys(
768         bContext *C, Scene *UNUSED(scene), TreeElement *UNUSED(te), TreeStoreElem *tselem, const eOLSetState set)
769 {
770         if (set != OL_SETSEL_NONE) {
771                 Object *ob = (Object *)tselem->id;
772
773                 WM_event_add_notifier(C, NC_OBJECT | ND_PARTICLE | NA_EDITED, ob);
774
775 // XXX          extern_set_butspace(F7KEY, 0);
776         }
777
778         return OL_DRAWSEL_NONE;
779 }
780
781 static int tree_element_active_constraint(
782         bContext *C, Scene *UNUSED(scene), ViewLayer *UNUSED(sl), TreeElement *UNUSED(te), TreeStoreElem *tselem, const eOLSetState set)
783 {
784         if (set != OL_SETSEL_NONE) {
785                 Object *ob = (Object *)tselem->id;
786
787                 WM_event_add_notifier(C, NC_OBJECT | ND_CONSTRAINT, ob);
788 // XXX          extern_set_butspace(F7KEY, 0);
789         }
790
791         return OL_DRAWSEL_NONE;
792 }
793
794 static eOLDrawState tree_element_active_text(
795         bContext *UNUSED(C), Scene *UNUSED(scene), ViewLayer *UNUSED(sl), SpaceOutliner *UNUSED(soops),
796         TreeElement *UNUSED(te), int UNUSED(set))
797 {
798         // XXX removed
799         return OL_DRAWSEL_NONE;
800 }
801
802 static eOLDrawState tree_element_active_pose(
803         bContext *C, Scene *scene, ViewLayer *view_layer, TreeElement *UNUSED(te), TreeStoreElem *tselem, const eOLSetState set)
804 {
805         Object *ob = (Object *)tselem->id;
806         Base *base = BKE_view_layer_base_find(view_layer, ob);
807
808         if (base == NULL) {
809                 /* Armature not instantiated in current scene (e.g. inside an appended group...). */
810                 return OL_DRAWSEL_NONE;
811         }
812
813         if (set != OL_SETSEL_NONE) {
814                 do_outliner_activate_pose(C, scene, view_layer, base, (set == OL_SETSEL_EXTEND));
815         }
816         else {
817                 if (ob->mode & OB_MODE_POSE) {
818                         return OL_DRAWSEL_NORMAL;
819                 }
820         }
821         return OL_DRAWSEL_NONE;
822 }
823
824 static eOLDrawState tree_element_active_sequence(
825         bContext *C, Scene *scene, TreeElement *te, TreeStoreElem *UNUSED(tselem), const eOLSetState set)
826 {
827         Sequence *seq = (Sequence *) te->directdata;
828         Editing *ed = BKE_sequencer_editing_get(scene, false);
829
830         if (set != OL_SETSEL_NONE) {
831                 /* only check on setting */
832                 if (BLI_findindex(ed->seqbasep, seq) != -1) {
833                         if (set == OL_SETSEL_EXTEND) {
834                                 BKE_sequencer_active_set(scene, NULL);
835                         }
836                         ED_sequencer_deselect_all(scene);
837
838                         if ((set == OL_SETSEL_EXTEND) && seq->flag & SELECT) {
839                                 seq->flag &= ~SELECT;
840                         }
841                         else {
842                                 seq->flag |= SELECT;
843                                 BKE_sequencer_active_set(scene, seq);
844                         }
845                 }
846
847                 WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER | NA_SELECTED, scene);
848         }
849         else {
850                 if (ed->act_seq == seq && seq->flag & SELECT) {
851                         return OL_DRAWSEL_NORMAL;
852                 }
853         }
854         return OL_DRAWSEL_NONE;
855 }
856
857 static eOLDrawState tree_element_active_sequence_dup(
858         Scene *scene, TreeElement *te, TreeStoreElem *UNUSED(tselem), const eOLSetState set)
859 {
860         Sequence *seq, *p;
861         Editing *ed = BKE_sequencer_editing_get(scene, false);
862
863         seq = (Sequence *)te->directdata;
864         if (set == OL_SETSEL_NONE) {
865                 if (seq->flag & SELECT) {
866                         return OL_DRAWSEL_NORMAL;
867                 }
868                 return OL_DRAWSEL_NONE;
869         }
870
871 // XXX  select_single_seq(seq, 1);
872         p = ed->seqbasep->first;
873         while (p) {
874                 if ((!p->strip) || (!p->strip->stripdata) || (p->strip->stripdata->name[0] == '\0')) {
875                         p = p->next;
876                         continue;
877                 }
878
879 //              if (STREQ(p->strip->stripdata->name, seq->strip->stripdata->name))
880 // XXX                  select_single_seq(p, 0);
881                 p = p->next;
882         }
883         return OL_DRAWSEL_NONE;
884 }
885
886 static eOLDrawState tree_element_active_keymap_item(
887         bContext *UNUSED(C), Scene *UNUSED(scene), ViewLayer *UNUSED(sl), TreeElement *te, TreeStoreElem *UNUSED(tselem), const eOLSetState set)
888 {
889         wmKeyMapItem *kmi = te->directdata;
890
891         if (set == OL_SETSEL_NONE) {
892                 if (kmi->flag & KMI_INACTIVE) {
893                         return OL_DRAWSEL_NONE;
894                 }
895                 return OL_DRAWSEL_NORMAL;
896         }
897         else {
898                 kmi->flag ^= KMI_INACTIVE;
899         }
900         return OL_DRAWSEL_NONE;
901 }
902
903 static eOLDrawState tree_element_active_master_collection(
904         bContext *C, TreeElement *UNUSED(te), const eOLSetState set)
905 {
906         if (set == OL_SETSEL_NONE) {
907                 ViewLayer *view_layer = CTX_data_view_layer(C);
908                 LayerCollection *active = CTX_data_layer_collection(C);
909
910                 if (active == view_layer->layer_collections.first) {
911                         return OL_DRAWSEL_NORMAL;
912                 }
913         }
914         else {
915                 ViewLayer *view_layer = CTX_data_view_layer(C);
916                 LayerCollection *layer_collection = view_layer->layer_collections.first;
917                 BKE_layer_collection_activate(view_layer, layer_collection);
918                 WM_main_add_notifier(NC_SCENE | ND_LAYER, NULL);
919         }
920
921         return OL_DRAWSEL_NONE;
922 }
923
924 static eOLDrawState tree_element_active_layer_collection(
925         bContext *C, TreeElement *te, const eOLSetState set)
926 {
927         if (set == OL_SETSEL_NONE) {
928                 LayerCollection *active = CTX_data_layer_collection(C);
929
930                 if (active == te->directdata) {
931                         return OL_DRAWSEL_NORMAL;
932                 }
933         }
934         else {
935                 Scene *scene = CTX_data_scene(C);
936                 LayerCollection *layer_collection = te->directdata;
937                 ViewLayer *view_layer = BKE_view_layer_find_from_collection(scene, layer_collection);
938                 BKE_layer_collection_activate(view_layer, layer_collection);
939                 WM_main_add_notifier(NC_SCENE | ND_LAYER, NULL);
940         }
941
942         return OL_DRAWSEL_NONE;
943 }
944
945 /* ---------------------------------------------- */
946
947 /* generic call for ID data check or make/check active in UI */
948 eOLDrawState tree_element_active(bContext *C, Scene *scene, ViewLayer *view_layer, SpaceOutliner *soops, TreeElement *te,
949                                  const eOLSetState set, const bool handle_all_types)
950 {
951         switch (te->idcode) {
952                 /* Note: ID_OB only if handle_all_type is true, else objects are handled specially to allow multiple
953                  * selection. See do_outliner_item_activate. */
954                 case ID_OB:
955                         if (handle_all_types) {
956                                 return tree_element_set_active_object(C, scene, view_layer, soops, te, set, false);
957                         }
958                         break;
959                 case ID_MA:
960                         return tree_element_active_material(C, scene, view_layer, soops, te, set);
961                 case ID_WO:
962                         return tree_element_active_world(C, scene, view_layer, soops, te, set);
963                 case ID_LA:
964                         return tree_element_active_light(C, scene, view_layer, soops, te, set);
965                 case ID_TXT:
966                         return tree_element_active_text(C, scene, view_layer, soops, te, set);
967                 case ID_CA:
968                         return tree_element_active_camera(C, scene, view_layer, soops, te, set);
969         }
970         return OL_DRAWSEL_NONE;
971 }
972
973 /**
974  * Generic call for non-id data to make/check active in UI
975  */
976 eOLDrawState tree_element_type_active(
977         bContext *C, Scene *scene, ViewLayer *view_layer, SpaceOutliner *soops,
978         TreeElement *te, TreeStoreElem *tselem, const eOLSetState set, bool recursive)
979 {
980         switch (tselem->type) {
981                 case TSE_DEFGROUP:
982                         return tree_element_active_defgroup(C, view_layer, te, tselem, set);
983                 case TSE_BONE:
984                         return tree_element_active_bone(C, view_layer, te, tselem, set, recursive);
985                 case TSE_EBONE:
986                         return tree_element_active_ebone(C, view_layer, te, tselem, set, recursive);
987                 case TSE_MODIFIER:
988                         return tree_element_active_modifier(C, scene, view_layer, te, tselem, set);
989                 case TSE_LINKED_OB:
990                         if (set != OL_SETSEL_NONE) {
991                                 tree_element_set_active_object(C, scene, view_layer, soops, te, set, false);
992                         }
993                         else if (tselem->id == (ID *)OBACT(view_layer)) {
994                                 return OL_DRAWSEL_NORMAL;
995                         }
996                         break;
997                 case TSE_LINKED_PSYS:
998                         return tree_element_active_psys(C, scene, te, tselem, set);
999                 case TSE_POSE_BASE:
1000                         return tree_element_active_pose(C, scene, view_layer, te, tselem, set);
1001                 case TSE_POSE_CHANNEL:
1002                         return tree_element_active_posechannel(C, scene, view_layer, te, tselem, set, recursive);
1003                 case TSE_CONSTRAINT:
1004                         return tree_element_active_constraint(C, scene, view_layer, te, tselem, set);
1005                 case TSE_R_LAYER:
1006                         return active_viewlayer(C, scene, view_layer, te, set);
1007                 case TSE_POSEGRP:
1008                         return tree_element_active_posegroup(C, scene, view_layer, te, tselem, set);
1009                 case TSE_SEQUENCE:
1010                         return tree_element_active_sequence(C, scene, te, tselem, set);
1011                 case TSE_SEQUENCE_DUP:
1012                         return tree_element_active_sequence_dup(scene, te, tselem, set);
1013                 case TSE_KEYMAP_ITEM:
1014                         return tree_element_active_keymap_item(C, scene, view_layer, te, tselem, set);
1015                 case TSE_GP_LAYER:
1016                         return tree_element_active_gplayer(C, scene, te, tselem, set);
1017                         break;
1018                 case TSE_VIEW_COLLECTION_BASE:
1019                         return tree_element_active_master_collection(C, te, set);
1020                 case TSE_LAYER_COLLECTION:
1021                         return tree_element_active_layer_collection(C, te, set);
1022         }
1023         return OL_DRAWSEL_NONE;
1024 }
1025
1026 /* ================================================ */
1027
1028 /**
1029  * Action when clicking to activate an item (typically under the mouse cursor),
1030  * but don't do any cursor intersection checks.
1031  *
1032  * Needed to run from operators accessed from a menu.
1033  */
1034 static void do_outliner_item_activate_tree_element(
1035         bContext *C, Scene *scene, ViewLayer *view_layer, SpaceOutliner *soops,
1036         TreeElement *te, TreeStoreElem *tselem,
1037         const bool extend, const bool recursive)
1038 {
1039         /* Always makes active object, except for some specific types. */
1040         if (ELEM(tselem->type, TSE_SEQUENCE, TSE_SEQ_STRIP, TSE_SEQUENCE_DUP, TSE_EBONE, TSE_LAYER_COLLECTION)) {
1041                 /* Note about TSE_EBONE: In case of a same ID_AR datablock shared among several objects, we do not want
1042                  * to switch out of edit mode (see T48328 for details). */
1043         }
1044         else if (tselem->id && OB_DATA_SUPPORT_EDITMODE(te->idcode)) {
1045                 /* Support edit-mode toggle, keeping the active object as is. */
1046         }
1047         else if (tselem->type == TSE_POSE_BASE) {
1048                 /* Support pose mode toggle, keeping the active object as is. */
1049         }
1050         else {
1051                 tree_element_set_active_object(
1052                         C, scene, view_layer, soops, te,
1053                         (extend && tselem->type == 0) ? OL_SETSEL_EXTEND : OL_SETSEL_NORMAL,
1054                         recursive && tselem->type == 0);
1055         }
1056
1057         if (tselem->type == 0) { // the lib blocks
1058                 /* editmode? */
1059                 if (te->idcode == ID_SCE) {
1060                         if (scene != (Scene *)tselem->id) {
1061                                 WM_window_set_active_scene(CTX_data_main(C), C, CTX_wm_window(C), (Scene *)tselem->id);
1062                         }
1063                 }
1064                 else if (te->idcode == ID_GR) {
1065                         Collection *gr = (Collection *)tselem->id;
1066
1067                         if (extend) {
1068                                 int sel = BA_SELECT;
1069                                 FOREACH_COLLECTION_OBJECT_RECURSIVE_BEGIN(gr, object)
1070                                 {
1071                                         Base *base = BKE_view_layer_base_find(view_layer, object);
1072                                         if (base && (base->flag & BASE_SELECTED)) {
1073                                                 sel = BA_DESELECT;
1074                                                 break;
1075                                         }
1076                                 }
1077                                 FOREACH_COLLECTION_OBJECT_RECURSIVE_END;
1078
1079                                 FOREACH_COLLECTION_OBJECT_RECURSIVE_BEGIN(gr, object)
1080                                 {
1081                                         Base *base = BKE_view_layer_base_find(view_layer, object);
1082                                         if (base) {
1083                                                 ED_object_base_select(base, sel);
1084                                         }
1085                                 }
1086                                 FOREACH_COLLECTION_OBJECT_RECURSIVE_END;
1087                         }
1088                         else {
1089                                 BKE_view_layer_base_deselect_all(view_layer);
1090
1091                                 FOREACH_COLLECTION_OBJECT_RECURSIVE_BEGIN(gr, object)
1092                                 {
1093                                         Base *base = BKE_view_layer_base_find(view_layer, object);
1094                                         /* Object may not be in this scene */
1095                                         if (base != NULL) {
1096                                                 if ((base->flag & BASE_SELECTED) == 0) {
1097                                                         ED_object_base_select(base, BA_SELECT);
1098                                                 }
1099                                         }
1100                                 }
1101                                 FOREACH_COLLECTION_OBJECT_RECURSIVE_END;
1102                         }
1103
1104                         DEG_id_tag_update(&scene->id, ID_RECALC_SELECT);
1105                         WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, scene);
1106                 }
1107                 else if (OB_DATA_SUPPORT_EDITMODE(te->idcode)) {
1108                         Object *ob = (Object *)outliner_search_back(soops, te, ID_OB);
1109                         if ((ob != NULL) && (ob->data == tselem->id)) {
1110                                 Base *base = BKE_view_layer_base_find(view_layer, ob);
1111                                 if ((base != NULL) && (base->flag & BASE_VISIBLE)) {
1112                                         do_outliner_activate_obdata(C, scene, view_layer, base, extend);
1113                                 }
1114                         }
1115                 }
1116                 else if (ELEM(te->idcode, ID_GD)) {
1117                         /* set grease pencil to object mode */
1118                         WM_operator_name_call(C, "GPENCIL_OT_editmode_toggle", WM_OP_INVOKE_REGION_WIN, NULL);
1119                 }
1120                 else {  // rest of types
1121                         tree_element_active(C, scene, view_layer, soops, te, OL_SETSEL_NORMAL, false);
1122                 }
1123
1124         }
1125         else {
1126                 tree_element_type_active(C, scene, view_layer, soops, te, tselem,
1127                                          extend ? OL_SETSEL_EXTEND : OL_SETSEL_NORMAL,
1128                                          recursive);
1129         }
1130 }
1131
1132 /**
1133  * \param extend: Don't deselect other items, only modify \a te.
1134  * \param toggle: Select \a te when not selected, deselect when selected.
1135  */
1136 void outliner_item_select(SpaceOutliner *soops, const TreeElement *te, const bool extend, const bool toggle)
1137 {
1138         TreeStoreElem *tselem = TREESTORE(te);
1139         const short new_flag = toggle ? (tselem->flag ^ TSE_SELECTED) : (tselem->flag | TSE_SELECTED);
1140
1141         if (extend == false) {
1142                 outliner_flag_set(&soops->tree, TSE_SELECTED, false);
1143         }
1144         tselem->flag = new_flag;
1145 }
1146
1147 static void outliner_item_toggle_closed(TreeElement *te, const bool toggle_children)
1148 {
1149         TreeStoreElem *tselem = TREESTORE(te);
1150         if (toggle_children) {
1151                 tselem->flag &= ~TSE_CLOSED;
1152
1153                 const bool all_opened = !outliner_flag_is_any_test(&te->subtree, TSE_CLOSED, 1);
1154                 outliner_flag_set(&te->subtree, TSE_CLOSED, all_opened);
1155         }
1156         else {
1157                 tselem->flag ^= TSE_CLOSED;
1158         }
1159 }
1160
1161 static bool outliner_item_is_co_within_close_toggle(TreeElement *te, float view_co_x)
1162 {
1163         return ((te->flag & TE_ICONROW) == 0) && (view_co_x > te->xs) && (view_co_x < te->xs + UI_UNIT_X);
1164 }
1165
1166 static bool outliner_is_co_within_restrict_columns(const SpaceOutliner *soops, const ARegion *ar, float view_co_x)
1167 {
1168         return ((soops->outlinevis != SO_DATA_API) &&
1169                 !(soops->flag & SO_HIDE_RESTRICTCOLS) &&
1170                 (view_co_x > ar->v2d.cur.xmax - OL_TOG_RESTRICT_VIEWX));
1171 }
1172
1173 /**
1174  * A version of #outliner_item_do_acticate_from_cursor that takes the tree element directly.
1175  * and doesn't depend on the pointer position.
1176  *
1177  * This allows us to simulate clicking on an item without dealing with the mouse cursor.
1178  */
1179 void outliner_item_do_activate_from_tree_element(
1180         bContext *C, TreeElement *te, TreeStoreElem *tselem,
1181         bool extend, bool recursive)
1182 {
1183         Scene *scene = CTX_data_scene(C);
1184         ViewLayer *view_layer = CTX_data_view_layer(C);
1185         SpaceOutliner *soops = CTX_wm_space_outliner(C);
1186
1187         do_outliner_item_activate_tree_element(
1188                 C, scene, view_layer, soops,
1189                 te, tselem,
1190                 extend, recursive);
1191 }
1192
1193 /**
1194  * Action to run when clicking in the outliner,
1195  *
1196  * May expend/collapse branches or activate items.
1197  * */
1198 int outliner_item_do_activate_from_cursor(
1199         bContext *C, const int mval[2],
1200         bool extend, bool recursive)
1201 {
1202         ARegion *ar = CTX_wm_region(C);
1203         SpaceOutliner *soops = CTX_wm_space_outliner(C);
1204         TreeElement *te;
1205         float view_mval[2];
1206         bool changed = false, rebuild_tree = false;
1207
1208         UI_view2d_region_to_view(&ar->v2d, mval[0], mval[1], &view_mval[0], &view_mval[1]);
1209
1210         if (outliner_is_co_within_restrict_columns(soops, ar, view_mval[0])) {
1211                 return OPERATOR_CANCELLED;
1212         }
1213
1214         if (!(te = outliner_find_item_at_y(soops, &soops->tree, view_mval[1]))) {
1215                 /* skip */
1216         }
1217         else if (outliner_item_is_co_within_close_toggle(te, view_mval[0])) {
1218                 outliner_item_toggle_closed(te, extend);
1219                 changed = true;
1220                 rebuild_tree = true;
1221         }
1222         else {
1223                 Scene *scene = CTX_data_scene(C);
1224                 ViewLayer *view_layer = CTX_data_view_layer(C);
1225                 /* the row may also contain children, if one is hovered we want this instead of current te */
1226                 TreeElement *activate_te = outliner_find_item_at_x_in_row(soops, te, view_mval[0]);
1227                 TreeStoreElem *activate_tselem = TREESTORE(activate_te);
1228
1229                 outliner_item_select(soops, activate_te, extend, extend);
1230                 do_outliner_item_activate_tree_element(C, scene, view_layer, soops, activate_te, activate_tselem, extend, recursive);
1231                 changed = true;
1232         }
1233
1234         if (changed) {
1235                 if (rebuild_tree) {
1236                         ED_region_tag_redraw(ar);
1237                 }
1238                 else {
1239                         ED_region_tag_redraw_no_rebuild(ar);
1240                 }
1241                 ED_undo_push(C, "Outliner selection change");
1242         }
1243
1244         return OPERATOR_FINISHED;
1245 }
1246
1247 /* event can enterkey, then it opens/closes */
1248 static int outliner_item_activate_invoke(bContext *C, wmOperator *op, const wmEvent *event)
1249 {
1250         bool extend    = RNA_boolean_get(op->ptr, "extend");
1251         bool recursive = RNA_boolean_get(op->ptr, "recursive");
1252         return outliner_item_do_activate_from_cursor(C, event->mval, extend, recursive);
1253 }
1254
1255 void OUTLINER_OT_item_activate(wmOperatorType *ot)
1256 {
1257         ot->name = "Select";
1258         ot->idname = "OUTLINER_OT_item_activate";
1259         ot->description = "Handle mouse clicks to select and activate items";
1260
1261         ot->invoke = outliner_item_activate_invoke;
1262
1263         ot->poll = ED_operator_outliner_active;
1264
1265         RNA_def_boolean(ot->srna, "extend", true, "Extend", "Extend selection for activation");
1266         RNA_def_boolean(ot->srna, "recursive", false, "Recursive", "Select Objects and their children");
1267 }
1268
1269 /* ****************************************************** */
1270
1271 /* **************** Box Select Tool ****************** */
1272 static void outliner_item_box_select(SpaceOutliner *soops, Scene *scene, rctf *rectf, TreeElement *te, bool select)
1273 {
1274         TreeStoreElem *tselem = TREESTORE(te);
1275
1276         if (te->ys <= rectf->ymax && te->ys + UI_UNIT_Y >= rectf->ymin) {
1277                 if (select) {
1278                         tselem->flag |= TSE_SELECTED;
1279                 }
1280                 else {
1281                         tselem->flag &= ~TSE_SELECTED;
1282                 }
1283         }
1284
1285         /* Look at its children. */
1286         if (TSELEM_OPEN(tselem, soops)) {
1287                 for (te = te->subtree.first; te; te = te->next) {
1288                         outliner_item_box_select(soops, scene, rectf, te, select);
1289                 }
1290         }
1291 }
1292
1293 static int outliner_box_select_exec(bContext *C, wmOperator *op)
1294 {
1295         Scene *scene = CTX_data_scene(C);
1296         SpaceOutliner *soops = CTX_wm_space_outliner(C);
1297         ARegion *ar = CTX_wm_region(C);
1298         rctf rectf;
1299
1300         const eSelectOp sel_op = RNA_enum_get(op->ptr, "mode");
1301         const bool select = (sel_op != SEL_OP_SUB);
1302         if (SEL_OP_USE_PRE_DESELECT(sel_op)) {
1303                 outliner_flag_set(&soops->tree, TSE_SELECTED, 0);
1304         }
1305
1306         WM_operator_properties_border_to_rctf(op, &rectf);
1307         UI_view2d_region_to_view_rctf(&ar->v2d, &rectf, &rectf);
1308
1309         for (TreeElement *te = soops->tree.first; te; te = te->next) {
1310                 outliner_item_box_select(soops, scene, &rectf, te, select);
1311         }
1312
1313         DEG_id_tag_update(&scene->id, ID_RECALC_SELECT);
1314         WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, scene);
1315         ED_region_tag_redraw(ar);
1316
1317         return OPERATOR_FINISHED;
1318 }
1319
1320 void OUTLINER_OT_select_box(wmOperatorType *ot)
1321 {
1322         /* identifiers */
1323         ot->name = "Box Select";
1324         ot->idname = "OUTLINER_OT_select_box";
1325         ot->description = "Use box selection to select tree elements";
1326
1327         /* api callbacks */
1328         ot->invoke = WM_gesture_box_invoke;
1329         ot->exec = outliner_box_select_exec;
1330         ot->modal = WM_gesture_box_modal;
1331         ot->cancel = WM_gesture_box_cancel;
1332
1333         ot->poll = ED_operator_outliner_active;
1334
1335         /* flags */
1336         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1337
1338         /* properties */
1339         WM_operator_properties_gesture_box(ot);
1340         WM_operator_properties_select_operation_simple(ot);
1341 }
1342
1343 /* ****************************************************** */