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