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