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