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