WM: refactor gestures for use as tools
[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_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 region (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         }
479         return OL_DRAWSEL_NONE;
480 }
481
482 static eOLDrawState tree_element_active_posegroup(
483         bContext *C, Scene *scene, TreeElement *te, TreeStoreElem *tselem, const eOLSetState set)
484 {
485         Object *ob = (Object *)tselem->id;
486         
487         if (set != OL_SETSEL_NONE) {
488                 if (ob->pose) {
489                         ob->pose->active_group = te->index + 1;
490                         WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob);
491                 }
492         }
493         else {
494                 if (ob == OBACT && ob->pose) {
495                         if (ob->pose->active_group == te->index + 1) {
496                                 return OL_DRAWSEL_NORMAL;
497                         }
498                 }
499         }
500         return OL_DRAWSEL_NONE;
501 }
502
503 static eOLDrawState tree_element_active_posechannel(
504         bContext *C, Scene *scene, TreeElement *te, TreeStoreElem *tselem, const eOLSetState set, bool recursive)
505 {
506         Object *ob = (Object *)tselem->id;
507         bArmature *arm = ob->data;
508         bPoseChannel *pchan = te->directdata;
509         
510         if (set != OL_SETSEL_NONE) {
511                 if (!(pchan->bone->flag & BONE_HIDDEN_P)) {
512                         
513                         if (set != OL_SETSEL_EXTEND) {
514                                 bPoseChannel *pchannel;
515                                 /* single select forces all other bones to get unselected */
516                                 for (pchannel = ob->pose->chanbase.first; pchannel; pchannel = pchannel->next)
517                                         pchannel->bone->flag &= ~(BONE_TIPSEL | BONE_SELECTED | BONE_ROOTSEL);
518                         }
519
520                         if ((set == OL_SETSEL_EXTEND) && (pchan->bone->flag & BONE_SELECTED)) {
521                                 pchan->bone->flag &= ~BONE_SELECTED;
522                         }
523                         else {
524                                 pchan->bone->flag |= BONE_SELECTED;
525                                 arm->act_bone = pchan->bone;
526                         }
527
528                         if (recursive) {
529                                 /* Recursive select/deselect */
530                                 do_outliner_bone_select_recursive(arm, pchan->bone, (pchan->bone->flag & BONE_SELECTED) != 0);
531                         }
532
533                         WM_event_add_notifier(C, NC_OBJECT | ND_BONE_ACTIVE, ob);
534
535                 }
536         }
537         else {
538                 if (ob == OBACT && ob->pose) {
539                         if (pchan->bone->flag & BONE_SELECTED) {
540                                 return OL_DRAWSEL_NORMAL;
541                         }
542                 }
543         }
544         return OL_DRAWSEL_NONE;
545 }
546
547 static eOLDrawState tree_element_active_bone(
548         bContext *C, Scene *scene, TreeElement *te, TreeStoreElem *tselem, const eOLSetState set, bool recursive)
549 {
550         bArmature *arm = (bArmature *)tselem->id;
551         Bone *bone = te->directdata;
552         
553         if (set != OL_SETSEL_NONE) {
554                 if (!(bone->flag & BONE_HIDDEN_P)) {
555                         Object *ob = OBACT;
556                         if (ob) {
557                                 if (set != OL_SETSEL_EXTEND) {
558                                         /* single select forces all other bones to get unselected */
559                                         for (Bone *bone_iter = arm->bonebase.first; bone_iter != NULL; bone_iter = bone_iter->next) {
560                                                 bone_iter->flag &= ~(BONE_TIPSEL | BONE_SELECTED | BONE_ROOTSEL);
561                                                 do_outliner_bone_select_recursive(arm, bone_iter, false);
562                                         }
563                                 }
564                         }
565                         
566                         if (set == OL_SETSEL_EXTEND && (bone->flag & BONE_SELECTED)) {
567                                 bone->flag &= ~BONE_SELECTED;
568                         }
569                         else {
570                                 bone->flag |= BONE_SELECTED;
571                                 arm->act_bone = bone;
572                         }
573
574                         if (recursive) {
575                                 /* Recursive select/deselect */
576                                 do_outliner_bone_select_recursive(arm, bone, (bone->flag & BONE_SELECTED) != 0);
577                         }
578
579                         
580                         WM_event_add_notifier(C, NC_OBJECT | ND_BONE_ACTIVE, ob);
581                 }
582         }
583         else {
584                 Object *ob = OBACT;
585                 
586                 if (ob && ob->data == arm) {
587                         if (bone->flag & BONE_SELECTED) {
588                                 return OL_DRAWSEL_NORMAL;
589                         }
590                 }
591         }
592         return OL_DRAWSEL_NONE;
593 }
594
595
596 /* ebones only draw in editmode armature */
597 static void tree_element_active_ebone__sel(bContext *C, Scene *scene, bArmature *arm, EditBone *ebone, short sel)
598 {
599         if (sel) {
600                 ebone->flag |= BONE_SELECTED | BONE_ROOTSEL | BONE_TIPSEL;
601                 arm->act_edbone = ebone;
602                 // flush to parent?
603                 if (ebone->parent && (ebone->flag & BONE_CONNECTED)) ebone->parent->flag |= BONE_TIPSEL;
604         }
605         else {
606                 ebone->flag &= ~(BONE_SELECTED | BONE_ROOTSEL | BONE_TIPSEL);
607                 // flush to parent?
608                 if (ebone->parent && (ebone->flag & BONE_CONNECTED)) ebone->parent->flag &= ~BONE_TIPSEL;
609         }
610
611         WM_event_add_notifier(C, NC_OBJECT | ND_BONE_ACTIVE, scene->obedit);
612 }
613 static eOLDrawState tree_element_active_ebone(
614         bContext *C, Scene *scene, TreeElement *te, TreeStoreElem *UNUSED(tselem), const eOLSetState set, bool recursive)
615 {
616         BLI_assert(scene->obedit != NULL);
617
618         bArmature *arm = scene->obedit->data;
619         EditBone *ebone = te->directdata;
620         eOLDrawState status = OL_DRAWSEL_NONE;
621
622         if (set != OL_SETSEL_NONE) {
623                 if (set == OL_SETSEL_NORMAL) {
624                         if (!(ebone->flag & BONE_HIDDEN_A)) {
625                                 ED_armature_deselect_all(scene->obedit);
626                                 tree_element_active_ebone__sel(C, scene, arm, ebone, true);
627                                 status = OL_DRAWSEL_NORMAL;
628                         }
629                 }
630                 else if (set == OL_SETSEL_EXTEND) {
631                         if (!(ebone->flag & BONE_HIDDEN_A)) {
632                                 if (!(ebone->flag & BONE_SELECTED)) {
633                                         tree_element_active_ebone__sel(C, scene, arm, ebone, true);
634                                         status = OL_DRAWSEL_NORMAL;
635                                 }
636                                 else {
637                                         /* entirely selected, so de-select */
638                                         tree_element_active_ebone__sel(C, scene, arm, ebone, false);
639                                         status = OL_DRAWSEL_NONE;
640                                 }
641                         }
642                 }
643
644                 if (recursive) {
645                         /* Recursive select/deselect */
646                         do_outliner_ebone_select_recursive(arm, ebone, (ebone->flag & BONE_SELECTED) != 0);
647                 }
648         }
649         else if (ebone->flag & BONE_SELECTED) {
650                 status = OL_DRAWSEL_NORMAL;
651         }
652
653         return status;
654 }
655
656 static eOLDrawState tree_element_active_modifier(
657         bContext *C, TreeElement *UNUSED(te), TreeStoreElem *tselem, const eOLSetState set)
658 {
659         if (set != OL_SETSEL_NONE) {
660                 Object *ob = (Object *)tselem->id;
661                 
662                 WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER, ob);
663
664 // XXX          extern_set_butspace(F9KEY, 0);
665         }
666         
667         return OL_DRAWSEL_NONE;
668 }
669
670 static eOLDrawState tree_element_active_psys(
671         bContext *C, Scene *UNUSED(scene), TreeElement *UNUSED(te), TreeStoreElem *tselem, const eOLSetState set)
672 {
673         if (set != OL_SETSEL_NONE) {
674                 Object *ob = (Object *)tselem->id;
675                 
676                 WM_event_add_notifier(C, NC_OBJECT | ND_PARTICLE | NA_EDITED, ob);
677                 
678 // XXX          extern_set_butspace(F7KEY, 0);
679         }
680         
681         return OL_DRAWSEL_NONE;
682 }
683
684 static int tree_element_active_constraint(
685         bContext *C, TreeElement *UNUSED(te), TreeStoreElem *tselem, const eOLSetState set)
686 {
687         if (set != OL_SETSEL_NONE) {
688                 Object *ob = (Object *)tselem->id;
689                 
690                 WM_event_add_notifier(C, NC_OBJECT | ND_CONSTRAINT, ob);
691 // XXX          extern_set_butspace(F7KEY, 0);
692         }
693         
694         return OL_DRAWSEL_NONE;
695 }
696
697 static eOLDrawState tree_element_active_text(
698         bContext *UNUSED(C), Scene *UNUSED(scene), SpaceOops *UNUSED(soops),
699         TreeElement *UNUSED(te), int UNUSED(set))
700 {
701         // XXX removed
702         return OL_DRAWSEL_NONE;
703 }
704
705 static eOLDrawState tree_element_active_pose(
706         bContext *C, Scene *scene, TreeElement *UNUSED(te), TreeStoreElem *tselem, const eOLSetState set)
707 {
708         Object *ob = (Object *)tselem->id;
709         Base *base = BKE_scene_base_find(scene, ob);
710
711         if (base == NULL) {
712                 /* Armature not instantiated in current scene (e.g. inside an appended group...). */
713                 return OL_DRAWSEL_NONE;
714         }
715
716         if (set != OL_SETSEL_NONE) {
717                 if (scene->obedit)
718                         ED_object_editmode_exit(C, EM_FREEDATA | EM_FREEUNDO | EM_WAITCURSOR | EM_DO_UNDO);
719                 
720                 if (ob->mode & OB_MODE_POSE)
721                         ED_armature_exit_posemode(C, base);
722                 else 
723                         ED_armature_enter_posemode(C, base);
724         }
725         else {
726                 if (ob->mode & OB_MODE_POSE) {
727                         return OL_DRAWSEL_NORMAL;
728                 }
729         }
730         return OL_DRAWSEL_NONE;
731 }
732
733 static eOLDrawState tree_element_active_sequence(
734         bContext *C, Scene *scene, TreeElement *te, TreeStoreElem *UNUSED(tselem), const eOLSetState set)
735 {
736         Sequence *seq = (Sequence *) te->directdata;
737         Editing *ed = BKE_sequencer_editing_get(scene, false);
738
739         if (set != OL_SETSEL_NONE) {
740                 /* only check on setting */
741                 if (BLI_findindex(ed->seqbasep, seq) != -1) {
742                         if (set == OL_SETSEL_EXTEND) {
743                                 BKE_sequencer_active_set(scene, NULL);
744                         }
745                         ED_sequencer_deselect_all(scene);
746
747                         if ((set == OL_SETSEL_EXTEND) && seq->flag & SELECT) {
748                                 seq->flag &= ~SELECT;
749                         }
750                         else {
751                                 seq->flag |= SELECT;
752                                 BKE_sequencer_active_set(scene, seq);
753                         }
754                 }
755
756                 WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER | NA_SELECTED, scene);
757         }
758         else {
759                 if (ed->act_seq == seq && seq->flag & SELECT) {
760                         return OL_DRAWSEL_NORMAL;
761                 }
762         }
763         return OL_DRAWSEL_NONE;
764 }
765
766 static eOLDrawState tree_element_active_sequence_dup(
767         Scene *scene, TreeElement *te, TreeStoreElem *UNUSED(tselem), const eOLSetState set)
768 {
769         Sequence *seq, *p;
770         Editing *ed = BKE_sequencer_editing_get(scene, false);
771
772         seq = (Sequence *)te->directdata;
773         if (set == OL_SETSEL_NONE) {
774                 if (seq->flag & SELECT)
775                         return OL_DRAWSEL_NORMAL;
776                 return OL_DRAWSEL_NONE;
777         }
778
779 // XXX  select_single_seq(seq, 1);
780         p = ed->seqbasep->first;
781         while (p) {
782                 if ((!p->strip) || (!p->strip->stripdata) || (p->strip->stripdata->name[0] == '\0')) {
783                         p = p->next;
784                         continue;
785                 }
786
787 //              if (STREQ(p->strip->stripdata->name, seq->strip->stripdata->name))
788 // XXX                  select_single_seq(p, 0);
789                 p = p->next;
790         }
791         return OL_DRAWSEL_NONE;
792 }
793
794 static eOLDrawState tree_element_active_keymap_item(
795         bContext *UNUSED(C), TreeElement *te, TreeStoreElem *UNUSED(tselem), const eOLSetState set)
796 {
797         wmKeyMapItem *kmi = te->directdata;
798         
799         if (set == OL_SETSEL_NONE) {
800                 if (kmi->flag & KMI_INACTIVE) {
801                         return OL_DRAWSEL_NONE;
802                 }
803                 return OL_DRAWSEL_NORMAL;
804         }
805         else {
806                 kmi->flag ^= KMI_INACTIVE;
807         }
808         return OL_DRAWSEL_NONE;
809 }
810
811 /* ---------------------------------------------- */
812
813 /* generic call for ID data check or make/check active in UI */
814 eOLDrawState tree_element_active(bContext *C, Scene *scene, SpaceOops *soops, TreeElement *te,
815                                  const eOLSetState set, const bool handle_all_types)
816 {
817         switch (te->idcode) {
818                 /* Note: ID_OB only if handle_all_type is true, else objects are handled specially to allow multiple
819                  * selection. See do_outliner_item_activate. */
820                 case ID_OB:
821                         if (handle_all_types) {
822                                 return tree_element_set_active_object(C, scene, soops, te, set, false);
823                         }
824                         break;
825                 case ID_MA:
826                         return tree_element_active_material(C, scene, soops, te, set);
827                 case ID_WO:
828                         return tree_element_active_world(C, scene, soops, te, set);
829                 case ID_LA:
830                         return tree_element_active_lamp(C, scene, soops, te, set);
831                 case ID_TE:
832                         return tree_element_active_texture(C, scene, soops, te, set);
833                 case ID_TXT:
834                         return tree_element_active_text(C, scene, soops, te, set);
835                 case ID_CA:
836                         return tree_element_active_camera(C, scene, soops, te, set);
837         }
838         return OL_DRAWSEL_NONE;
839 }
840
841 /**
842  * Generic call for non-id data to make/check active in UI
843  *
844  * \note Context can be NULL when ``(set == OL_SETSEL_NONE)``
845  */
846 eOLDrawState tree_element_type_active(
847         bContext *C, Scene *scene, SpaceOops *soops,
848         TreeElement *te, TreeStoreElem *tselem, const eOLSetState set, bool recursive)
849 {
850         switch (tselem->type) {
851                 case TSE_DEFGROUP:
852                         return tree_element_active_defgroup(C, scene, te, tselem, set);
853                 case TSE_BONE:
854                         return tree_element_active_bone(C, scene, te, tselem, set, recursive);
855                 case TSE_EBONE:
856                         return tree_element_active_ebone(C, scene, te, tselem, set, recursive);
857                 case TSE_MODIFIER:
858                         return tree_element_active_modifier(C, te, tselem, set);
859                 case TSE_LINKED_OB:
860                         if (set != OL_SETSEL_NONE) {
861                                 tree_element_set_active_object(C, scene, soops, te, set, false);
862                         }
863                         else if (tselem->id == (ID *)OBACT) {
864                                 return OL_DRAWSEL_NORMAL;
865                         }
866                         break;
867                 case TSE_LINKED_PSYS:
868                         return tree_element_active_psys(C, scene, te, tselem, set);
869                 case TSE_POSE_BASE:
870                         return tree_element_active_pose(C, scene, te, tselem, set);
871                 case TSE_POSE_CHANNEL:
872                         return tree_element_active_posechannel(C, scene, te, tselem, set, recursive);
873                 case TSE_CONSTRAINT:
874                         return tree_element_active_constraint(C, te, tselem, set);
875                 case TSE_R_LAYER:
876                         return tree_element_active_renderlayer(C, te, tselem, set);
877                 case TSE_POSEGRP:
878                         return tree_element_active_posegroup(C, scene, te, tselem, set);
879                 case TSE_SEQUENCE:
880                         return tree_element_active_sequence(C, scene, te, tselem, set);
881                 case TSE_SEQUENCE_DUP:
882                         return tree_element_active_sequence_dup(scene, te, tselem, set);
883                 case TSE_KEYMAP_ITEM:
884                         return tree_element_active_keymap_item(C, te, tselem, set);
885                 case TSE_GP_LAYER:
886                         //return tree_element_active_gplayer(C, scene, te, tselem, set);
887                         break;
888                         
889         }
890         return OL_DRAWSEL_NONE;
891 }
892
893 /* ================================================ */
894
895 static bool do_outliner_item_activate(bContext *C, Scene *scene, ARegion *ar, SpaceOops *soops,
896                                       TreeElement *te, bool extend, bool recursive, const float mval[2])
897 {
898         
899         if (mval[1] > te->ys && mval[1] < te->ys + UI_UNIT_Y) {
900                 TreeStoreElem *tselem = TREESTORE(te);
901                 bool openclose = false;
902                 
903                 /* open close icon */
904                 if ((te->flag & TE_ICONROW) == 0) {               // hidden icon, no open/close
905                         if (mval[0] > te->xs && mval[0] < te->xs + UI_UNIT_X)
906                                 openclose = true;
907                 }
908                 
909                 if (openclose) {
910                         /* all below close/open? */
911                         if (extend) {
912                                 tselem->flag &= ~TSE_CLOSED;
913                                 outliner_set_flag(&te->subtree, TSE_CLOSED, !outliner_has_one_flag(&te->subtree, TSE_CLOSED, 1));
914                         }
915                         else {
916                                 if (tselem->flag & TSE_CLOSED) tselem->flag &= ~TSE_CLOSED;
917                                 else tselem->flag |= TSE_CLOSED;
918                                 
919                         }
920                         
921                         return true;
922                 }
923                 /* name and first icon */
924                 else if (mval[0] > te->xs + UI_UNIT_X && mval[0] < te->xend) {
925                         
926                         /* always makes active object, except for some specific types.
927                          * Note about TSE_EBONE: In case of a same ID_AR datablock shared among several objects, we do not want
928                          * to switch out of edit mode (see T48328 for details). */
929                         if (!ELEM(tselem->type, TSE_SEQUENCE, TSE_SEQ_STRIP, TSE_SEQUENCE_DUP, TSE_EBONE)) {
930                                 tree_element_set_active_object(C, scene, soops, te,
931                                                                (extend && tselem->type == 0) ? OL_SETSEL_EXTEND : OL_SETSEL_NORMAL,
932                                                                recursive && tselem->type == 0);
933                         }
934                         
935                         if (tselem->type == 0) { // the lib blocks
936                                 /* editmode? */
937                                 if (te->idcode == ID_SCE) {
938                                         if (scene != (Scene *)tselem->id) {
939                                                 ED_screen_set_scene(C, CTX_wm_screen(C), (Scene *)tselem->id);
940                                         }
941                                 }
942                                 else if (te->idcode == ID_GR) {
943                                         Group *gr = (Group *)tselem->id;
944                                         GroupObject *gob;
945                                         
946                                         if (extend) {
947                                                 int sel = BA_SELECT;
948                                                 for (gob = gr->gobject.first; gob; gob = gob->next) {
949                                                         if (gob->ob->flag & SELECT) {
950                                                                 sel = BA_DESELECT;
951                                                                 break;
952                                                         }
953                                                 }
954                                                 
955                                                 for (gob = gr->gobject.first; gob; gob = gob->next) {
956                                                         ED_base_object_select(BKE_scene_base_find(scene, gob->ob), sel);
957                                                 }
958                                         }
959                                         else {
960                                                 BKE_scene_base_deselect_all(scene);
961                                                 
962                                                 for (gob = gr->gobject.first; gob; gob = gob->next) {
963                                                         if ((gob->ob->flag & SELECT) == 0)
964                                                                 ED_base_object_select(BKE_scene_base_find(scene, gob->ob), BA_SELECT);
965                                                 }
966                                         }
967                                         
968                                         WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, scene);
969                                 }
970                                 else if (ELEM(te->idcode, ID_ME, ID_CU, ID_MB, ID_LT, ID_AR)) {
971                                         WM_operator_name_call(C, "OBJECT_OT_editmode_toggle", WM_OP_INVOKE_REGION_WIN, NULL);
972                                 }
973                                 else {  // rest of types
974                                         tree_element_active(C, scene, soops, te, OL_SETSEL_NORMAL, false);
975                                 }
976
977                         }
978                         else {
979                                 tree_element_type_active(C, scene, soops, te, tselem,
980                                                          extend ? OL_SETSEL_EXTEND : OL_SETSEL_NORMAL,
981                                                          recursive);
982                         }
983                         
984                         return true;
985                 }
986         }
987         
988         for (te = te->subtree.first; te; te = te->next) {
989                 if (do_outliner_item_activate(C, scene, ar, soops, te, extend, recursive, mval)) {
990                         return true;
991                 }
992         }
993         return false;
994 }
995
996 int outliner_item_do_activate(bContext *C, int x, int y, bool extend, bool recursive)
997 {
998         Scene *scene = CTX_data_scene(C);
999         ARegion *ar = CTX_wm_region(C);
1000         SpaceOops *soops = CTX_wm_space_outliner(C);
1001         TreeElement *te;
1002         float fmval[2];
1003
1004         UI_view2d_region_to_view(&ar->v2d, x, y, &fmval[0], &fmval[1]);
1005
1006         if (!ELEM(soops->outlinevis, SO_DATABLOCKS, SO_USERDEF) &&
1007             !(soops->flag & SO_HIDE_RESTRICTCOLS) &&
1008             (fmval[0] > ar->v2d.cur.xmax - OL_TOG_RESTRICT_VIEWX))
1009         {
1010                 return OPERATOR_CANCELLED;
1011         }
1012
1013         for (te = soops->tree.first; te; te = te->next) {
1014                 if (do_outliner_item_activate(C, scene, ar, soops, te, extend, recursive, fmval)) break;
1015         }
1016         
1017         if (te) {
1018                 ED_undo_push(C, "Outliner click event");
1019         }
1020         else {
1021                 short selecting = -1;
1022                 int row;
1023                 
1024                 /* get row number - 100 here is just a dummy value since we don't need the column */
1025                 UI_view2d_listview_view_to_cell(&ar->v2d, 1000, UI_UNIT_Y, 0.0f, OL_Y_OFFSET, 
1026                                                 fmval[0], fmval[1], NULL, &row);
1027                 
1028                 /* select relevant row */
1029                 if (outliner_select(soops, &soops->tree, &row, &selecting)) {
1030                 
1031                         soops->storeflag |= SO_TREESTORE_REDRAW;
1032                 
1033                         /* no need for undo push here, only changing outliner data which is
1034                          * scene level - campbell */
1035                         /* ED_undo_push(C, "Outliner selection event"); */
1036                 }
1037         }
1038         
1039         ED_region_tag_redraw(ar);
1040
1041         return OPERATOR_FINISHED;
1042 }
1043
1044 /* event can enterkey, then it opens/closes */
1045 static int outliner_item_activate(bContext *C, wmOperator *op, const wmEvent *event)
1046 {
1047         bool extend    = RNA_boolean_get(op->ptr, "extend");
1048         bool recursive = RNA_boolean_get(op->ptr, "recursive");
1049         int x = event->mval[0];
1050         int y = event->mval[1];
1051         return outliner_item_do_activate(C, x, y, extend, recursive);
1052 }
1053
1054 void OUTLINER_OT_item_activate(wmOperatorType *ot)
1055 {
1056         ot->name = "Activate Item";
1057         ot->idname = "OUTLINER_OT_item_activate";
1058         ot->description = "Handle mouse clicks to activate/select items";
1059         
1060         ot->invoke = outliner_item_activate;
1061         
1062         ot->poll = ED_operator_outliner_active;
1063         
1064         RNA_def_boolean(ot->srna, "extend", true, "Extend", "Extend selection for activation");
1065         RNA_def_boolean(ot->srna, "recursive", false, "Recursive", "Select Objects and their children");
1066 }
1067
1068 /* ****************************************************** */
1069
1070 /* **************** Border Select Tool ****************** */
1071 static void outliner_item_border_select(Scene *scene, rctf *rectf, TreeElement *te, bool select)
1072 {
1073         TreeStoreElem *tselem = TREESTORE(te);
1074
1075         if (te->ys <= rectf->ymax && te->ys + UI_UNIT_Y >= rectf->ymin) {
1076                 if (select) {
1077                         tselem->flag |= TSE_SELECTED;
1078                 }
1079                 else {
1080                         tselem->flag &= ~TSE_SELECTED;
1081                 }
1082         }
1083
1084         /* Look at its children. */
1085         if ((tselem->flag & TSE_CLOSED) == 0) {
1086                 for (te = te->subtree.first; te; te = te->next) {
1087                         outliner_item_border_select(scene, rectf, te, select);
1088                 }
1089         }
1090 }
1091
1092 static int outliner_border_select_exec(bContext *C, wmOperator *op)
1093 {
1094         Scene *scene = CTX_data_scene(C);
1095         SpaceOops *soops = CTX_wm_space_outliner(C);
1096         ARegion *ar = CTX_wm_region(C);
1097         TreeElement *te;
1098         rctf rectf;
1099         bool select = !RNA_boolean_get(op->ptr, "deselect");
1100
1101         WM_operator_properties_border_to_rctf(op, &rectf);
1102         UI_view2d_region_to_view_rctf(&ar->v2d, &rectf, &rectf);
1103
1104         for (te = soops->tree.first; te; te = te->next) {
1105                 outliner_item_border_select(scene, &rectf, te, select);
1106         }
1107
1108         WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, scene);
1109         ED_region_tag_redraw(ar);
1110
1111         return OPERATOR_FINISHED;
1112 }
1113
1114 void OUTLINER_OT_select_border(wmOperatorType *ot)
1115 {
1116         /* identifiers */
1117         ot->name = "Border Select";
1118         ot->idname = "OUTLINER_OT_select_border";
1119         ot->description = "Use box selection to select tree elements";
1120
1121         /* api callbacks */
1122         ot->invoke = WM_gesture_border_invoke;
1123         ot->exec = outliner_border_select_exec;
1124         ot->modal = WM_gesture_border_modal;
1125         ot->cancel = WM_gesture_border_cancel;
1126
1127         ot->poll = ED_operator_outliner_active;
1128
1129         /* flags */
1130         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1131
1132         /* rna */
1133         WM_operator_properties_gesture_border_ex(ot, true, false);
1134 }
1135
1136 /* ****************************************************** */