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