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