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