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