Merge branch 'master' into blender2.8
[blender.git] / source / blender / editors / space_outliner / outliner_select.c
1 /*
2  * ***** BEGIN GPL LICENSE BLOCK *****
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version. 
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software Foundation,
16  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17  *
18  * The Original Code is Copyright (C) 2004 Blender Foundation.
19  * All rights reserved.
20  *
21  * The Original Code is: all of this file.
22  *
23  * Contributor(s): Joshua Leung
24  *
25  * ***** END GPL LICENSE BLOCK *****
26  */
27
28 /** \file blender/editors/space_outliner/outliner_select.c
29  *  \ingroup spoutliner
30  */
31
32 #include <stdlib.h>
33
34 #include "DNA_armature_types.h"
35 #include "DNA_group_types.h"
36 #include "DNA_lamp_types.h"
37 #include "DNA_material_types.h"
38 #include "DNA_object_types.h"
39 #include "DNA_scene_types.h"
40 #include "DNA_sequence_types.h"
41 #include "DNA_world_types.h"
42
43 #include "BLI_utildefines.h"
44 #include "BLI_listbase.h"
45
46 #include "BKE_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), SceneLayer *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->r.actlay = tselem->nr;
89                 WM_event_add_notifier(C, NC_SCENE | ND_RENDER_OPTIONS, sce);
90         }
91         else {
92                 return sce->r.actlay == 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(SceneLayer *sl, Object *ob_parent, bool select)
103 {
104         Base *base;
105
106         for (base = FIRSTBASE_NEW; 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, SceneLayer *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_NEW) {
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_scene_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_scene_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), SceneLayer *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_NEW || 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, SceneLayer *sl, SpaceOops *UNUSED(soops),
257         TreeElement *te, const eOLSetState set)
258 {
259         TreeElement *tep;
260         TreeStoreElem /* *tselem,*/ *tselemp;
261         Object *ob = OBACT_NEW;
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), SceneLayer *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_NEW) {
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, SceneLayer *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, SceneLayer *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, SceneLayer *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_NEW)
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), SceneLayer *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_NEW && 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), SceneLayer *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_NEW && 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, SceneLayer *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_NEW;
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_NEW;
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), SceneLayer *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), SceneLayer *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), SceneLayer *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, SceneLayer *sl, TreeElement *UNUSED(te), TreeStoreElem *tselem, const eOLSetState set)
659 {
660         Object *ob = (Object *)tselem->id;
661         Base *base = BKE_scene_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), SceneLayer *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                 SceneLayer *sl = CTX_data_scene_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, SceneLayer *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, SceneLayer *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_NEW) {
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 static void outliner_item_activate(
880         bContext *C, SpaceOops *soops, TreeElement *te,
881         const bool extend, const bool recursive)
882 {
883         Scene *scene = CTX_data_scene(C);
884         SceneLayer *sl = CTX_data_scene_layer(C);
885         TreeStoreElem *tselem = TREESTORE(te);
886
887         /* always makes active object, except for some specific types.
888          * Note about TSE_EBONE: In case of a same ID_AR datablock shared among several objects, we do not want
889          * to switch out of edit mode (see T48328 for details). */
890         if (!ELEM(tselem->type, TSE_SEQUENCE, TSE_SEQ_STRIP, TSE_SEQUENCE_DUP, TSE_EBONE, TSE_LAYER_COLLECTION)) {
891                 tree_element_set_active_object(C, scene, sl, soops, te,
892                                                (extend && tselem->type == 0) ? OL_SETSEL_EXTEND : OL_SETSEL_NORMAL,
893                                                recursive && tselem->type == 0);
894         }
895
896         if (tselem->type == 0) { // the lib blocks
897                 /* editmode? */
898                 if (te->idcode == ID_SCE) {
899                         if (scene != (Scene *)tselem->id) {
900                                 WM_window_change_active_scene(CTX_data_main(C), C, CTX_wm_window(C), (Scene *)tselem->id);
901                         }
902                 }
903                 else if (te->idcode == ID_GR) {
904                         Group *gr = (Group *)tselem->id;
905                         GroupObject *gob;
906                         
907                         if (extend) {
908                                 int sel = BA_SELECT;
909                                 for (gob = gr->gobject.first; gob; gob = gob->next) {
910                                         if (gob->ob->flag & SELECT) {
911                                                 sel = BA_DESELECT;
912                                                 break;
913                                         }
914                                 }
915
916                                 for (gob = gr->gobject.first; gob; gob = gob->next) {
917                                         ED_object_base_select(BKE_scene_layer_base_find(sl, gob->ob), sel);
918                                 }
919                         }
920                         else {
921                                 BKE_scene_layer_base_deselect_all(sl);
922
923                                 for (gob = gr->gobject.first; gob; gob = gob->next) {
924                                         Base *base = BKE_scene_layer_base_find(sl, gob->ob);
925                                         /* Object may not be in this scene */
926                                         if (base != NULL) {
927                                                 if ((base->flag & BASE_SELECTED) == 0) {
928                                                         ED_object_base_select(base, BA_SELECT);
929                                                 }
930                                         }
931                                 }
932                         }
933                         
934                         WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, scene);
935                 }
936                 else if (ELEM(te->idcode, ID_ME, ID_CU, ID_MB, ID_LT, ID_AR)) {
937                         WM_operator_name_call(C, "OBJECT_OT_editmode_toggle", WM_OP_INVOKE_REGION_WIN, NULL);
938                 }
939                 else {  // rest of types
940                         tree_element_active(C, scene, sl, soops, te, OL_SETSEL_NORMAL, false);
941                 }
942
943         }
944         else {
945                 tree_element_type_active(C, scene, sl, soops, te, tselem,
946                                          extend ? OL_SETSEL_EXTEND : OL_SETSEL_NORMAL,
947                                          recursive);
948         }
949 }
950
951 /**
952  * \param extend: Don't deselect other items, only modify \a te.
953  * \param toggle: Select \a te when not selected, deselect when selected.
954  */
955 static void outliner_item_select(SpaceOops *soops, const TreeElement *te, const bool extend, const bool toggle)
956 {
957         TreeStoreElem *tselem = TREESTORE(te);
958         const short new_flag = toggle ? (tselem->flag ^ TSE_SELECTED) : (tselem->flag | TSE_SELECTED);
959
960         if (extend == false) {
961                 outliner_set_flag(&soops->tree, TSE_SELECTED, false);
962         }
963         tselem->flag = new_flag;
964 }
965
966 static void outliner_item_toggle_closed(TreeElement *te, const bool toggle_children)
967 {
968         TreeStoreElem *tselem = TREESTORE(te);
969         if (toggle_children) {
970                 tselem->flag &= ~TSE_CLOSED;
971
972                 const bool all_opened = !outliner_has_one_flag(&te->subtree, TSE_CLOSED, 1);
973                 outliner_set_flag(&te->subtree, TSE_CLOSED, all_opened);
974         }
975         else {
976                 tselem->flag ^= TSE_CLOSED;
977         }
978 }
979
980 static bool outliner_item_is_co_within_close_toggle(TreeElement *te, float view_co_x)
981 {
982         return ((te->flag & TE_ICONROW) == 0) && (view_co_x > te->xs) && (view_co_x < te->xs + UI_UNIT_X);
983 }
984
985 static bool outliner_is_co_within_restrict_columns(const SpaceOops *soops, const ARegion *ar, float view_co_x)
986 {
987         return (!ELEM(soops->outlinevis, SO_DATABLOCKS, SO_USERDEF) &&
988                 !(soops->flag & SO_HIDE_RESTRICTCOLS) &&
989                 (view_co_x > ar->v2d.cur.xmax - OL_TOG_RESTRICT_VIEWX));
990 }
991
992 int outliner_item_activate_or_toggle_closed(bContext *C, int x, int y, bool extend, bool recursive)
993 {
994         ARegion *ar = CTX_wm_region(C);
995         SpaceOops *soops = CTX_wm_space_outliner(C);
996         TreeElement *te;
997         float view_mval[2];
998         bool changed = false, rebuild_tree = false;
999
1000         UI_view2d_region_to_view(&ar->v2d, x, y, &view_mval[0], &view_mval[1]);
1001
1002         if (outliner_is_co_within_restrict_columns(soops, ar, view_mval[0])) {
1003                 return OPERATOR_CANCELLED;
1004         }
1005
1006         if (!(te = outliner_find_item_at_y(soops, &soops->tree, view_mval[1]))) {
1007                 /* skip */
1008         }
1009         else if (outliner_item_is_co_within_close_toggle(te, view_mval[0])) {
1010                 outliner_item_toggle_closed(te, extend);
1011                 changed = true;
1012                 rebuild_tree = true;
1013         }
1014         else {
1015                 /* the row may also contain children, if one is hovered we want this instead of current te */
1016                 TreeElement *activate_te = outliner_find_item_at_x_in_row(soops, te, view_mval[0]);
1017
1018                 outliner_item_select(soops, activate_te, extend, extend);
1019                 outliner_item_activate(C, soops, activate_te, extend, recursive);
1020                 changed = true;
1021         }
1022
1023         if (changed) {
1024                 if (!rebuild_tree) {
1025                         /* only needs to redraw, no rebuild */
1026                         soops->storeflag |= SO_TREESTORE_REDRAW;
1027                 }
1028                 ED_undo_push(C, "Outliner selection change");
1029                 ED_region_tag_redraw(ar);
1030         }
1031
1032         return OPERATOR_FINISHED;
1033 }
1034
1035 /* event can enterkey, then it opens/closes */
1036 static int outliner_item_activate_invoke(bContext *C, wmOperator *op, const wmEvent *event)
1037 {
1038         bool extend    = RNA_boolean_get(op->ptr, "extend");
1039         bool recursive = RNA_boolean_get(op->ptr, "recursive");
1040         int x = event->mval[0];
1041         int y = event->mval[1];
1042         return outliner_item_activate_or_toggle_closed(C, x, y, extend, recursive);
1043 }
1044
1045 void OUTLINER_OT_item_activate(wmOperatorType *ot)
1046 {
1047         ot->name = "Activate Item";
1048         ot->idname = "OUTLINER_OT_item_activate";
1049         ot->description = "Handle mouse clicks to activate/select items";
1050         
1051         ot->invoke = outliner_item_activate_invoke;
1052         
1053         ot->poll = ED_operator_outliner_active;
1054         
1055         RNA_def_boolean(ot->srna, "extend", true, "Extend", "Extend selection for activation");
1056         RNA_def_boolean(ot->srna, "recursive", false, "Recursive", "Select Objects and their children");
1057 }
1058
1059 /* ****************************************************** */
1060
1061 /* **************** Border Select Tool ****************** */
1062 static void outliner_item_border_select(Scene *scene, rctf *rectf, TreeElement *te, int gesture_mode)
1063 {
1064         TreeStoreElem *tselem = TREESTORE(te);
1065
1066         if (te->ys <= rectf->ymax && te->ys + UI_UNIT_Y >= rectf->ymin) {
1067                 if (gesture_mode == GESTURE_MODAL_SELECT) {
1068                         tselem->flag |= TSE_SELECTED;
1069                 }
1070                 else {
1071                         tselem->flag &= ~TSE_SELECTED;
1072                 }
1073         }
1074
1075         /* Look at its children. */
1076         if ((tselem->flag & TSE_CLOSED) == 0) {
1077                 for (te = te->subtree.first; te; te = te->next) {
1078                         outliner_item_border_select(scene, rectf, te, gesture_mode);
1079                 }
1080         }
1081 }
1082
1083 static int outliner_border_select_exec(bContext *C, wmOperator *op)
1084 {
1085         Scene *scene = CTX_data_scene(C);
1086         SpaceOops *soops = CTX_wm_space_outliner(C);
1087         ARegion *ar = CTX_wm_region(C);
1088         TreeElement *te;
1089         rctf rectf;
1090         int gesture_mode = RNA_int_get(op->ptr, "gesture_mode");
1091
1092         WM_operator_properties_border_to_rctf(op, &rectf);
1093         UI_view2d_region_to_view_rctf(&ar->v2d, &rectf, &rectf);
1094
1095         for (te = soops->tree.first; te; te = te->next) {
1096                 outliner_item_border_select(scene, &rectf, te, gesture_mode);
1097         }
1098
1099         WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, scene);
1100         ED_region_tag_redraw(ar);
1101
1102         return OPERATOR_FINISHED;
1103 }
1104
1105 void OUTLINER_OT_select_border(wmOperatorType *ot)
1106 {
1107         /* identifiers */
1108         ot->name = "Border Select";
1109         ot->idname = "OUTLINER_OT_select_border";
1110         ot->description = "Use box selection to select tree elements";
1111
1112         /* api callbacks */
1113         ot->invoke = WM_border_select_invoke;
1114         ot->exec = outliner_border_select_exec;
1115         ot->modal = WM_border_select_modal;
1116         ot->cancel = WM_border_select_cancel;
1117
1118         ot->poll = ED_operator_outliner_active;
1119
1120         /* flags */
1121         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1122
1123         /* rna */
1124         WM_operator_properties_gesture_border(ot, false);
1125 }
1126
1127 /* ****************************************************** */