Bugfix #7631
[blender.git] / source / blender / src / outliner.c
1 /**
2  * $Id$
3  *
4  * ***** BEGIN GPL LICENSE BLOCK *****
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * as published by the Free Software Foundation; either version 2
9  * of the License, or (at your option) any later version. 
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software Foundation,
18  * Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
19  *
20  * The Original Code is Copyright (C) 2004 Blender Foundation.
21  * All rights reserved.
22  *
23  * The Original Code is: all of this file.
24  *
25  * Contributor(s): none yet.
26  *
27  * ***** END GPL LICENSE BLOCK *****
28  */
29
30 #include <string.h>
31 #include <stdlib.h>
32
33 #include "MEM_guardedalloc.h"
34
35 #include "DNA_action_types.h"
36 #include "DNA_armature_types.h"
37 #include "DNA_constraint_types.h"
38 #include "DNA_curve_types.h"
39 #include "DNA_camera_types.h"
40 #include "DNA_image_types.h"
41 #include "DNA_ipo_types.h"
42 #include "DNA_group_types.h"
43 #include "DNA_key_types.h"
44 #include "DNA_lamp_types.h"
45 #include "DNA_material_types.h"
46 #include "DNA_mesh_types.h"
47 #include "DNA_meta_types.h"
48 #include "DNA_modifier_types.h"
49 #include "DNA_nla_types.h"
50 #include "DNA_object_types.h"
51 #include "DNA_oops_types.h"
52 #include "DNA_scene_types.h"
53 #include "DNA_screen_types.h"
54 #include "DNA_space_types.h"
55 #include "DNA_texture_types.h"
56 #include "DNA_text_types.h"
57 #include "DNA_world_types.h"
58
59 #include "BLI_blenlib.h"
60
61 #include "BKE_constraint.h"
62 #include "BKE_deform.h"
63 #include "BKE_depsgraph.h"
64 #include "BKE_global.h"
65 #include "BKE_group.h"
66 #include "BKE_library.h"
67 #include "BKE_main.h"
68 #include "BKE_material.h"
69 #include "BKE_modifier.h"
70 #include "BKE_screen.h"
71 #include "BKE_scene.h"
72 #include "BKE_utildefines.h"
73
74 #ifdef WITH_VERSE
75 #include "BKE_verse.h"
76 #endif
77
78 #include "BIF_butspace.h"
79 #include "BIF_drawscene.h"
80 #include "BIF_drawtext.h"
81 #include "BIF_editaction.h"
82 #include "BIF_editarmature.h"
83 #include "BIF_editdeform.h"
84 #include "BIF_editnla.h"
85 #include "BIF_editview.h"
86 #include "BIF_editconstraint.h"
87 #include "BIF_gl.h"
88 #include "BIF_graphics.h"
89 #include "BIF_interface.h"
90 #include "BIF_interface_icons.h"
91 #include "BIF_mywindow.h"
92 #include "BIF_outliner.h"
93 #include "BIF_language.h"
94 #include "BIF_mainqueue.h"
95 #include "BIF_poseobject.h"
96 #include "BIF_previewrender.h"
97 #include "BIF_resources.h"
98 #include "BIF_screen.h"
99 #include "BIF_space.h"
100 #include "BIF_toolbox.h"
101
102 #ifdef WITH_VERSE
103 #include "BIF_verse.h"
104 #endif
105
106 #ifdef INTERNATIONAL
107 #include "FTF_Api.h"
108 #endif
109
110 #include "BDR_editobject.h"
111 #include "BSE_drawipo.h"
112 #include "BSE_edit.h"
113 #include "BSE_view.h"
114
115 #include "PIL_time.h" 
116
117 #include "blendef.h"
118 #include "mydevice.h"
119
120 #define OL_H    19
121 #define OL_X    18
122
123 #define OL_TOG_RESTRICT_VIEWX   54
124 #define OL_TOG_RESTRICT_SELECTX 36
125 #define OL_TOG_RESTRICT_RENDERX 18
126
127 #define OL_TOGW                         OL_TOG_RESTRICT_VIEWX
128
129 #define TS_CHUNK        128
130
131 #define TREESTORE(a) ((a)?soops->treestore->data+(a)->store_index:NULL)
132
133 #ifdef WITH_VERSE
134 extern ListBase session_list;
135 extern ListBase server_list;
136 #endif
137
138
139 /* ******************** PROTOTYPES ***************** */
140 static void outliner_draw_tree_element(SpaceOops *soops, TreeElement *te, int startx, int *starty);
141
142
143 /* ******************** PERSISTANT DATA ***************** */
144
145 static void outliner_storage_cleanup(SpaceOops *soops)
146 {
147         TreeStore *ts= soops->treestore;
148         
149         if(ts) {
150                 TreeStoreElem *tselem;
151                 int a, unused= 0;
152                 
153                 /* each element used once, for ID blocks with more users to have each a treestore */
154                 for(a=0, tselem= ts->data; a<ts->usedelem; a++, tselem++) tselem->used= 0;
155
156                 /* cleanup only after reading file or undo step */
157                 if(soops->storeflag & SO_TREESTORE_CLEANUP) {
158                         
159                         for(a=0, tselem= ts->data; a<ts->usedelem; a++, tselem++) {
160                                 if(tselem->id==NULL) unused++;
161                         }
162
163                         if(unused) {
164                                 if(ts->usedelem == unused) {
165                                         MEM_freeN(ts->data);
166                                         ts->data= NULL;
167                                         ts->usedelem= ts->totelem= 0;
168                                 }
169                                 else {
170                                         TreeStoreElem *tsnewar, *tsnew;
171                                         
172                                         tsnew=tsnewar= MEM_mallocN((ts->usedelem-unused)*sizeof(TreeStoreElem), "new tselem");
173                                         for(a=0, tselem= ts->data; a<ts->usedelem; a++, tselem++) {
174                                                 if(tselem->id) {
175                                                         *tsnew= *tselem;
176                                                         tsnew++;
177                                                 }
178                                         }
179                                         MEM_freeN(ts->data);
180                                         ts->data= tsnewar;
181                                         ts->usedelem-= unused;
182                                         ts->totelem= ts->usedelem;
183                                 }
184                         }
185                 }
186         }
187 }
188
189 static void check_persistant(SpaceOops *soops, TreeElement *te, ID *id, short type, short nr)
190 {
191         TreeStore *ts;
192         TreeStoreElem *tselem;
193         int a;
194         
195         /* case 1; no TreeStore */
196         if(soops->treestore==NULL) {
197                 ts= soops->treestore= MEM_callocN(sizeof(TreeStore), "treestore");
198         }
199         ts= soops->treestore;
200         
201         /* check if 'te' is in treestore */
202         tselem= ts->data;
203         for(a=0; a<ts->usedelem; a++, tselem++) {
204                 if(tselem->id==id && tselem->used==0) {
205                         if((type==0 && tselem->type==0) ||(tselem->type==type && tselem->nr==nr)) {
206                                 te->store_index= a;
207                                 tselem->used= 1;
208                                 return;
209                         }
210                 }
211         }
212         
213         /* add 1 element to treestore */
214         if(ts->usedelem==ts->totelem) {
215                 TreeStoreElem *tsnew;
216                 
217                 tsnew= MEM_mallocN((ts->totelem+TS_CHUNK)*sizeof(TreeStoreElem), "treestore data");
218                 if(ts->data) {
219                         memcpy(tsnew, ts->data, ts->totelem*sizeof(TreeStoreElem));
220                         MEM_freeN(ts->data);
221                 }
222                 ts->data= tsnew;
223                 ts->totelem+= TS_CHUNK;
224         }
225         
226         tselem= ts->data+ts->usedelem;
227         
228         tselem->type= type;
229         if(type) tselem->nr= nr; // we're picky! :)
230         else tselem->nr= 0;
231         tselem->id= id;
232         tselem->flag= TSE_CLOSED;
233         te->store_index= ts->usedelem;
234         
235         ts->usedelem++;
236 }
237
238 /* ******************** TREE MANAGEMENT ****************** */
239
240 void outliner_free_tree(ListBase *lb)
241 {
242         
243         while(lb->first) {
244                 TreeElement *te= lb->first;
245                 
246                 outliner_free_tree(&te->subtree);
247                 BLI_remlink(lb, te);
248                 MEM_freeN(te);
249         }
250 }
251
252 static void outliner_height(SpaceOops *soops, ListBase *lb, int *h)
253 {
254         TreeElement *te= lb->first;
255         while(te) {
256                 TreeStoreElem *tselem= TREESTORE(te);
257                 if((tselem->flag & TSE_CLOSED)==0) 
258                         outliner_height(soops, &te->subtree, h);
259                 (*h)++;
260                 te= te->next;
261         }
262 }
263
264 static void outliner_width(SpaceOops *soops, ListBase *lb, int *w)
265 {
266         TreeElement *te= lb->first;
267         while(te) {
268                 TreeStoreElem *tselem= TREESTORE(te);
269                 if(tselem->flag & TSE_CLOSED) {
270                         if (te->xend > *w)
271                                 *w = te->xend;
272                 }
273                 outliner_width(soops, &te->subtree, w);
274                 te= te->next;
275         }
276 }
277
278 static TreeElement *outliner_find_tree_element(ListBase *lb, int store_index)
279 {
280         TreeElement *te= lb->first, *tes;
281         while(te) {
282                 if(te->store_index==store_index) return te;
283                 tes= outliner_find_tree_element(&te->subtree, store_index);
284                 if(tes) return tes;
285                 te= te->next;
286         }
287         return NULL;
288 }
289
290
291
292 static ID *outliner_search_back(SpaceOops *soops, TreeElement *te, short idcode)
293 {
294         TreeStoreElem *tselem;
295         te= te->parent;
296         
297         while(te) {
298                 tselem= TREESTORE(te);
299                 if(te->idcode==idcode && tselem->type==0) return tselem->id;
300                 te= te->parent;
301         }
302         return NULL;
303 }
304
305 struct treesort {
306         TreeElement *te;
307         ID *id;
308         char *name;
309         short idcode;
310 };
311
312 static int treesort_alpha(const void *v1, const void *v2)
313 {
314         const struct treesort *x1= v1, *x2= v2;
315         int comp;
316         
317         /* first put objects last (hierarchy) */
318         comp= (x1->idcode==ID_OB);
319         if(x2->idcode==ID_OB) comp+=2;
320         
321         if(comp==1) return 1;
322         else if(comp==2) return -1;
323         else if(comp==3) {
324                 int comp= strcmp(x1->name, x2->name);
325                 
326                 if( comp>0 ) return 1;
327                 else if( comp<0) return -1;
328                 return 0;
329         }
330         return 0;
331 }
332
333 /* this is nice option for later? doesnt look too useful... */
334 #if 0
335 static int treesort_obtype_alpha(const void *v1, const void *v2)
336 {
337         const struct treesort *x1= v1, *x2= v2;
338         
339         /* first put objects last (hierarchy) */
340         if(x1->idcode==ID_OB && x2->idcode!=ID_OB) return 1;
341         else if(x2->idcode==ID_OB && x1->idcode!=ID_OB) return -1;
342         else {
343                 /* 2nd we check ob type */
344                 if(x1->idcode==ID_OB && x2->idcode==ID_OB) {
345                         if( ((Object *)x1->id)->type > ((Object *)x2->id)->type) return 1;
346                         else if( ((Object *)x1->id)->type > ((Object *)x2->id)->type) return -1;
347                         else return 0;
348                 }
349                 else {
350                         int comp= strcmp(x1->name, x2->name);
351                         
352                         if( comp>0 ) return 1;
353                         else if( comp<0) return -1;
354                         return 0;
355                 }
356         }
357 }
358 #endif
359
360 /* sort happens on each subtree individual */
361 static void outliner_sort(SpaceOops *soops, ListBase *lb)
362 {
363         TreeElement *te;
364         TreeStoreElem *tselem;
365         int totelem=0;
366         
367         te= lb->last;
368         if(te==NULL) return;
369         tselem= TREESTORE(te);
370         
371         /* sorting rules; only object lists or deformgroups */
372         if( (tselem->type==TSE_DEFGROUP) || (tselem->type==0 && te->idcode==ID_OB)) {
373                 
374                 /* count first */
375                 for(te= lb->first; te; te= te->next) totelem++;
376                 
377                 if(totelem>1) {
378                         struct treesort *tear= MEM_mallocN(totelem*sizeof(struct treesort), "tree sort array");
379                         struct treesort *tp=tear;
380                         int skip= 0;
381                         
382                         for(te= lb->first; te; te= te->next, tp++) {
383                                 tselem= TREESTORE(te);
384                                 tp->te= te;
385                                 tp->name= te->name;
386                                 tp->idcode= te->idcode;
387                                 if(tselem->type && tselem->type!=TSE_DEFGROUP) tp->idcode= 0;   // dont sort this
388                                 tp->id= tselem->id;
389                         }
390                         /* keep beginning of list */
391                         for(tp= tear, skip=0; skip<totelem; skip++, tp++)
392                                 if(tp->idcode) break;
393                         
394                         if(skip<totelem)
395                                 qsort(tear+skip, totelem-skip, sizeof(struct treesort), treesort_alpha);
396                         
397                         lb->first=lb->last= NULL;
398                         tp= tear;
399                         while(totelem--) {
400                                 BLI_addtail(lb, tp->te);
401                                 tp++;
402                         }
403                         MEM_freeN(tear);
404                 }
405         }
406         
407         for(te= lb->first; te; te= te->next) {
408                 outliner_sort(soops, &te->subtree);
409         }
410 }
411
412 /* Prototype, see functions below */
413 static TreeElement *outliner_add_element(SpaceOops *soops, ListBase *lb, void *idv, 
414                                                                                  TreeElement *parent, short type, short index);
415
416
417 static void outliner_add_passes(SpaceOops *soops, TreeElement *tenla, ID *id, SceneRenderLayer *srl)
418 {
419         TreeStoreElem *tselem= TREESTORE(tenla);
420         TreeElement *te;
421         
422         te= outliner_add_element(soops, &tenla->subtree, id, tenla, TSE_R_PASS, SCE_PASS_COMBINED);
423         te->name= "Combined";
424         te->directdata= &srl->passflag;
425         
426         /* save cpu cycles, but we add the first to invoke an open/close triangle */
427         if(tselem->flag & TSE_CLOSED)
428                 return;
429         
430         te= outliner_add_element(soops, &tenla->subtree, id, tenla, TSE_R_PASS, SCE_PASS_Z);
431         te->name= "Z";
432         te->directdata= &srl->passflag;
433         
434         te= outliner_add_element(soops, &tenla->subtree, id, tenla, TSE_R_PASS, SCE_PASS_VECTOR);
435         te->name= "Vector";
436         te->directdata= &srl->passflag;
437         
438         te= outliner_add_element(soops, &tenla->subtree, id, tenla, TSE_R_PASS, SCE_PASS_NORMAL);
439         te->name= "Normal";
440         te->directdata= &srl->passflag;
441         
442         te= outliner_add_element(soops, &tenla->subtree, id, tenla, TSE_R_PASS, SCE_PASS_UV);
443         te->name= "UV";
444         te->directdata= &srl->passflag;
445         
446         te= outliner_add_element(soops, &tenla->subtree, id, tenla, TSE_R_PASS, SCE_PASS_INDEXOB);
447         te->name= "Index Object";
448         te->directdata= &srl->passflag;
449         
450         te= outliner_add_element(soops, &tenla->subtree, id, tenla, TSE_R_PASS, SCE_PASS_RGBA);
451         te->name= "Color";
452         te->directdata= &srl->passflag;
453         
454         te= outliner_add_element(soops, &tenla->subtree, id, tenla, TSE_R_PASS, SCE_PASS_DIFFUSE);
455         te->name= "Diffuse";
456         te->directdata= &srl->passflag;
457         
458         te= outliner_add_element(soops, &tenla->subtree, id, tenla, TSE_R_PASS, SCE_PASS_SPEC);
459         te->name= "Specular";
460         te->directdata= &srl->passflag;
461         
462         te= outliner_add_element(soops, &tenla->subtree, id, tenla, TSE_R_PASS, SCE_PASS_SHADOW);
463         te->name= "Shadow";
464         te->directdata= &srl->passflag;
465         
466         te= outliner_add_element(soops, &tenla->subtree, id, tenla, TSE_R_PASS, SCE_PASS_AO);
467         te->name= "AO";
468         te->directdata= &srl->passflag;
469         
470         te= outliner_add_element(soops, &tenla->subtree, id, tenla, TSE_R_PASS, SCE_PASS_REFLECT);
471         te->name= "Reflection";
472         te->directdata= &srl->passflag;
473         
474         te= outliner_add_element(soops, &tenla->subtree, id, tenla, TSE_R_PASS, SCE_PASS_REFRACT);
475         te->name= "Refraction";
476         te->directdata= &srl->passflag;
477         
478         te= outliner_add_element(soops, &tenla->subtree, id, tenla, TSE_R_PASS, SCE_PASS_RADIO);
479         te->name= "Radiosity";
480         te->directdata= &srl->passflag;
481         
482 }
483
484
485 /* special handling of hierarchical non-lib data */
486 static void outliner_add_bone(SpaceOops *soops, ListBase *lb, ID *id, Bone *curBone, 
487                                                           TreeElement *parent, int *a)
488 {
489         TreeElement *te= outliner_add_element(soops, lb, id, parent, TSE_BONE, *a);
490         
491         (*a)++;
492         te->name= curBone->name;
493         te->directdata= curBone;
494         
495         for(curBone= curBone->childbase.first; curBone; curBone=curBone->next) {
496                 outliner_add_bone(soops, &te->subtree, id, curBone, te, a);
497         }
498 }
499
500 static void outliner_add_scene_contents(SpaceOops *soops, ListBase *lb, Scene *sce, TreeElement *te)
501 {
502         SceneRenderLayer *srl;
503         TreeElement *tenla= outliner_add_element(soops, lb, sce, te, TSE_R_LAYER_BASE, 0);
504         int a;
505         
506         tenla->name= "RenderLayers";
507         for(a=0, srl= sce->r.layers.first; srl; srl= srl->next, a++) {
508                 TreeElement *tenlay= outliner_add_element(soops, &tenla->subtree, sce, te, TSE_R_LAYER, a);
509                 tenlay->name= srl->name;
510                 tenlay->directdata= &srl->passflag;
511                 
512                 if(srl->light_override)
513                         outliner_add_element(soops, &tenlay->subtree, srl->light_override, tenlay, TSE_LINKED_LAMP, 0);
514                 if(srl->mat_override)
515                         outliner_add_element(soops, &tenlay->subtree, srl->mat_override, tenlay, TSE_LINKED_MAT, 0);
516                 
517                 outliner_add_passes(soops, tenlay, &sce->id, srl);
518         }
519         
520         outliner_add_element(soops,  lb, sce->world, te, 0, 0);
521         
522         if(sce->scriptlink.scripts) {
523                 int a= 0;
524                 tenla= outliner_add_element(soops,  lb, sce, te, TSE_SCRIPT_BASE, 0);
525                 tenla->name= "Scripts";
526                 for (a=0; a<sce->scriptlink.totscript; a++) {
527                         outliner_add_element(soops, &tenla->subtree, sce->scriptlink.scripts[a], tenla, 0, 0);
528                 }
529         }
530
531 }
532
533 static TreeElement *outliner_add_element(SpaceOops *soops, ListBase *lb, void *idv, 
534                                                                                  TreeElement *parent, short type, short index)
535 {
536         TreeElement *te;
537         TreeStoreElem *tselem;
538         ID *id= idv;
539         int a;
540         
541         if(id==NULL) return NULL;
542
543         te= MEM_callocN(sizeof(TreeElement), "tree elem");
544         /* add to the visual tree */
545         BLI_addtail(lb, te);
546         /* add to the storage */
547         check_persistant(soops, te, id, type, index);
548         tselem= TREESTORE(te);  
549         
550         te->parent= parent;
551         te->index= index;       // for data arays
552         te->name= id->name+2; // default, can be overridden by Library or non-ID data
553         te->idcode= GS(id->name);
554         
555         if(type==0) {
556
557                 /* tuck pointer back in object, to construct hierarchy */
558                 if(GS(id->name)==ID_OB) id->newid= (ID *)te;
559                 
560                 /* expand specific data always */
561                 switch(GS(id->name)) {
562                 case ID_LI:
563                         te->name= ((Library *)id)->name;
564                         break;
565                 case ID_SCE:
566                         outliner_add_scene_contents(soops, &te->subtree, (Scene *)id, te);
567                         break;
568                 case ID_OB:
569                         {
570                                 Object *ob= (Object *)id;
571                                 
572                                 if(ob->proxy && ob->id.lib==NULL)
573                                         outliner_add_element(soops, &te->subtree, ob->proxy, te, TSE_PROXY, 0);
574                                 
575                                 outliner_add_element(soops, &te->subtree, ob->data, te, 0, 0);
576                                 
577                                 if(ob->pose) {
578                                         bPoseChannel *pchan;
579                                         TreeElement *ten;
580                                         TreeElement *tenla= outliner_add_element(soops, &te->subtree, ob, te, TSE_POSE_BASE, 0);
581                                         
582                                         tenla->name= "Pose";
583                                         
584                                         if(ob!=G.obedit && (ob->flag & OB_POSEMODE)) {  // channels undefined in editmode, but we want the 'tenla' pose icon itself
585                                                 int a= 0, const_index= 1000;    /* ensure unique id for bone constraints */
586                                                 
587                                                 for(pchan= ob->pose->chanbase.first; pchan; pchan= pchan->next, a++) {
588                                                         ten= outliner_add_element(soops, &tenla->subtree, ob, tenla, TSE_POSE_CHANNEL, a);
589                                                         ten->name= pchan->name;
590                                                         ten->directdata= pchan;
591                                                         pchan->prev= (bPoseChannel *)ten;
592                                                         
593                                                         if(pchan->constraints.first) {
594                                                                 //Object *target;
595                                                                 bConstraint *con;
596                                                                 TreeElement *ten1;
597                                                                 TreeElement *tenla1= outliner_add_element(soops, &ten->subtree, ob, ten, TSE_CONSTRAINT_BASE, 0);
598                                                                 //char *str;
599                                                                 
600                                                                 tenla1->name= "Constraints";
601                                                                 for(con= pchan->constraints.first; con; con= con->next, const_index++) {
602                                                                         ten1= outliner_add_element(soops, &tenla1->subtree, ob, tenla1, TSE_CONSTRAINT, const_index);
603 #if 0 /* disabled as it needs to be reworked for recoded constraints system */
604                                                                         target= get_constraint_target(con, &str);
605                                                                         if(str && str[0]) ten1->name= str;
606                                                                         else if(target) ten1->name= target->id.name+2;
607                                                                         else ten1->name= con->name;
608 #endif
609                                                                         ten1->name= con->name;
610                                                                         ten1->directdata= con;
611                                                                         /* possible add all other types links? */
612                                                                 }
613                                                         }
614                                                 }
615                                                 /* make hierarchy */
616                                                 ten= tenla->subtree.first;
617                                                 while(ten) {
618                                                         TreeElement *nten= ten->next, *par;
619                                                         tselem= TREESTORE(ten);
620                                                         if(tselem->type==TSE_POSE_CHANNEL) {
621                                                                 pchan= (bPoseChannel *)ten->directdata;
622                                                                 if(pchan->parent) {
623                                                                         BLI_remlink(&tenla->subtree, ten);
624                                                                         par= (TreeElement *)pchan->parent->prev;
625                                                                         BLI_addtail(&par->subtree, ten);
626                                                                         ten->parent= par;
627                                                                 }
628                                                         }
629                                                         ten= nten;
630                                                 }
631                                                 /* restore prev pointers */
632                                                 pchan= ob->pose->chanbase.first;
633                                                 if(pchan) pchan->prev= NULL;
634                                                 for(; pchan; pchan= pchan->next) {
635                                                         if(pchan->next) pchan->next->prev= pchan;
636                                                 }
637                                         }
638                                 }
639                                 
640                                 outliner_add_element(soops, &te->subtree, ob->ipo, te, 0, 0);
641                                 outliner_add_element(soops, &te->subtree, ob->action, te, 0, 0);
642                                 
643                                 for(a=0; a<ob->totcol; a++) 
644                                         outliner_add_element(soops, &te->subtree, ob->mat[a], te, 0, a);
645                                 
646                                 if(ob->constraints.first) {
647                                         //Object *target;
648                                         bConstraint *con;
649                                         TreeElement *ten;
650                                         TreeElement *tenla= outliner_add_element(soops, &te->subtree, ob, te, TSE_CONSTRAINT_BASE, 0);
651                                         int a= 0;
652                                         //char *str;
653                                         
654                                         tenla->name= "Constraints";
655                                         for(con= ob->constraints.first; con; con= con->next, a++) {
656                                                 ten= outliner_add_element(soops, &tenla->subtree, ob, tenla, TSE_CONSTRAINT, a);
657 #if 0 /* disabled due to constraints system targets recode... code here needs review */
658                                                 target= get_constraint_target(con, &str);
659                                                 if(str && str[0]) ten->name= str;
660                                                 else if(target) ten->name= target->id.name+2;
661                                                 else ten->name= con->name;
662 #endif
663                                                 ten->name= con->name;
664                                                 ten->directdata= con;
665                                                 /* possible add all other types links? */
666                                         }
667                                 }
668                                 
669                                 if(ob->modifiers.first) {
670                                         ModifierData *md;
671                                         TreeElement *temod = outliner_add_element(soops, &te->subtree, ob, te, TSE_MODIFIER_BASE, 0);
672                                         int index;
673
674                                         temod->name = "Modifiers";
675                                         for (index=0,md=ob->modifiers.first; md; index++,md=md->next) {
676                                                 TreeElement *te = outliner_add_element(soops, &temod->subtree, ob, temod, TSE_MODIFIER, index);
677                                                 te->name= md->name;
678
679                                                 if (md->type==eModifierType_Lattice) {
680                                                         outliner_add_element(soops, &te->subtree, ((LatticeModifierData*) md)->object, te, TSE_LINKED_OB, 0);
681                                                 } else if (md->type==eModifierType_Curve) {
682                                                         outliner_add_element(soops, &te->subtree, ((CurveModifierData*) md)->object, te, TSE_LINKED_OB, 0);
683                                                 } else if (md->type==eModifierType_Armature) {
684                                                         outliner_add_element(soops, &te->subtree, ((ArmatureModifierData*) md)->object, te, TSE_LINKED_OB, 0);
685                                                 } else if (md->type==eModifierType_Hook) {
686                                                         outliner_add_element(soops, &te->subtree, ((HookModifierData*) md)->object, te, TSE_LINKED_OB, 0);
687                                                 }
688                                         }
689                                 }
690                                 if(ob->defbase.first) {
691                                         bDeformGroup *defgroup;
692                                         TreeElement *ten;
693                                         TreeElement *tenla= outliner_add_element(soops, &te->subtree, ob, te, TSE_DEFGROUP_BASE, 0);
694                                         int a= 0;
695                                         
696                                         tenla->name= "Vertex Groups";
697                                         for (defgroup=ob->defbase.first; defgroup; defgroup=defgroup->next, a++) {
698                                                 ten= outliner_add_element(soops, &tenla->subtree, ob, tenla, TSE_DEFGROUP, a);
699                                                 ten->name= defgroup->name;
700                                                 ten->directdata= defgroup;
701                                         }
702                                 }
703                                 if(ob->scriptlink.scripts) {
704                                         TreeElement *tenla= outliner_add_element(soops, &te->subtree, ob, te, TSE_SCRIPT_BASE, 0);
705                                         int a= 0;
706                                         
707                                         tenla->name= "Scripts";
708                                         for (a=0; a<ob->scriptlink.totscript; a++) {                                                    /*  ** */
709                                                 outliner_add_element(soops, &tenla->subtree, ob->scriptlink.scripts[a], te, 0, 0);
710                                         }
711                                 }
712                                 
713                                 if(ob->dup_group)
714                                         outliner_add_element(soops, &te->subtree, ob->dup_group, te, 0, 0);
715
716                                 if(ob->nlastrips.first) {
717                                         bActionStrip *strip;
718                                         TreeElement *ten;
719                                         TreeElement *tenla= outliner_add_element(soops, &te->subtree, ob, te, TSE_NLA, 0);
720                                         int a= 0;
721                                         
722                                         tenla->name= "NLA strips";
723                                         for (strip=ob->nlastrips.first; strip; strip=strip->next, a++) {
724                                                 ten= outliner_add_element(soops, &tenla->subtree, strip->act, tenla, TSE_NLA_ACTION, a);
725                                                 if(ten) ten->directdata= strip;
726                                         }
727                                 }
728                                 
729                         }
730                         break;
731                 case ID_ME:
732                         {
733                                 Mesh *me= (Mesh *)id;
734                                 outliner_add_element(soops, &te->subtree, me->ipo, te, 0, 0);
735                                 outliner_add_element(soops, &te->subtree, me->key, te, 0, 0);
736                                 for(a=0; a<me->totcol; a++) 
737                                         outliner_add_element(soops, &te->subtree, me->mat[a], te, 0, a);
738                                 /* could do tfaces with image links, but the images are not grouped nicely.
739                                    would require going over all tfaces, sort images in use. etc... */
740                         }
741                         break;
742                 case ID_CU:
743                         {
744                                 Curve *cu= (Curve *)id;
745                                 for(a=0; a<cu->totcol; a++) 
746                                         outliner_add_element(soops, &te->subtree, cu->mat[a], te, 0, a);
747                         }
748                         break;
749                 case ID_MB:
750                         {
751                                 MetaBall *mb= (MetaBall *)id;
752                                 for(a=0; a<mb->totcol; a++) 
753                                         outliner_add_element(soops, &te->subtree, mb->mat[a], te, 0, a);
754                         }
755                         break;
756                 case ID_MA:
757                 {
758                         Material *ma= (Material *)id;
759                         
760                         outliner_add_element(soops, &te->subtree, ma->ipo, te, 0, 0);
761                         for(a=0; a<MAX_MTEX; a++) {
762                                 if(ma->mtex[a]) outliner_add_element(soops, &te->subtree, ma->mtex[a]->tex, te, 0, a);
763                         }
764                 }
765                         break;
766                 case ID_TE:
767                         {
768                                 Tex *tex= (Tex *)id;
769                                 
770                                 outliner_add_element(soops, &te->subtree, tex->ipo, te, 0, 0);
771                                 outliner_add_element(soops, &te->subtree, tex->ima, te, 0, 0);
772                         }
773                         break;
774                 case ID_CA:
775                         {
776                                 Camera *ca= (Camera *)id;
777                                 outliner_add_element(soops, &te->subtree, ca->ipo, te, 0, 0);
778                         }
779                         break;
780                 case ID_LA:
781                         {
782                                 Lamp *la= (Lamp *)id;
783                                 outliner_add_element(soops, &te->subtree, la->ipo, te, 0, 0);
784                                 for(a=0; a<MAX_MTEX; a++) {
785                                         if(la->mtex[a]) outliner_add_element(soops, &te->subtree, la->mtex[a]->tex, te, 0, a);
786                                 }
787                         }
788                         break;
789                 case ID_WO:
790                         {
791                                 World *wrld= (World *)id;
792                                 outliner_add_element(soops, &te->subtree, wrld->ipo, te, 0, 0);
793                                 for(a=0; a<MAX_MTEX; a++) {
794                                         if(wrld->mtex[a]) outliner_add_element(soops, &te->subtree, wrld->mtex[a]->tex, te, 0, a);
795                                 }
796                         }
797                         break;
798                 case ID_KE:
799                         {
800                                 Key *key= (Key *)id;
801                                 outliner_add_element(soops, &te->subtree, key->ipo, te, 0, 0);
802                         }
803                         break;
804                 case ID_IP:
805                         {
806                                 Ipo *ipo= (Ipo *)id;
807                                 IpoCurve *icu;
808                                 Object *lastadded= NULL;
809                                 
810                                 for(icu= ipo->curve.first; icu; icu= icu->next) {
811                                         if(icu->driver && icu->driver->ob) {
812                                                 if(lastadded!=icu->driver->ob) {
813                                                         outliner_add_element(soops, &te->subtree, icu->driver->ob, te, TSE_LINKED_OB, 0);
814                                                         lastadded= icu->driver->ob;
815                                                 }
816                                         }
817                                 }
818                         }
819                 case ID_AC:
820                         {
821                                 bAction *act= (bAction *)id;
822                                 bActionChannel *chan;
823                                 int a= 0;
824                                 
825                                 tselem= TREESTORE(parent);
826                                 for (chan=act->chanbase.first; chan; chan=chan->next, a++) {
827                                         outliner_add_element(soops, &te->subtree, chan->ipo, te, 0, a);
828                                 }
829                         }
830                         break;
831                 case ID_AR:
832                         {
833                                 bArmature *arm= (bArmature *)id;
834                                 int a= 0;
835                                 
836                                 if(G.obedit && G.obedit->data==arm) {
837                                         EditBone *ebone;
838                                         TreeElement *ten;
839                                         
840                                         for (ebone = G.edbo.first; ebone; ebone=ebone->next, a++) {
841                                                 ten= outliner_add_element(soops, &te->subtree, id, te, TSE_EBONE, a);
842                                                 ten->directdata= ebone;
843                                                 ten->name= ebone->name;
844                                                 ebone->temp= ten;
845                                         }
846                                         /* make hierarchy */
847                                         ten= te->subtree.first;
848                                         while(ten) {
849                                                 TreeElement *nten= ten->next, *par;
850                                                 ebone= (EditBone *)ten->directdata;
851                                                 if(ebone->parent) {
852                                                         BLI_remlink(&te->subtree, ten);
853                                                         par= ebone->parent->temp;
854                                                         BLI_addtail(&par->subtree, ten);
855                                                         ten->parent= par;
856                                                 }
857                                                 ten= nten;
858                                         }
859                                 }
860                                 else {
861                                         /* do not extend Armature when we have posemode */
862                                         tselem= TREESTORE(te->parent);
863                                         if( GS(tselem->id->name)==ID_OB && ((Object *)tselem->id)->flag & OB_POSEMODE);
864                                         else {
865                                                 Bone *curBone;
866                                                 for (curBone=arm->bonebase.first; curBone; curBone=curBone->next){
867                                                         outliner_add_bone(soops, &te->subtree, id, curBone, te, &a);
868                                                 }
869                                         }
870                                 }
871                         }
872                         break;
873                 }
874         }
875 #ifdef WITH_VERSE
876         else if(type==ID_VS) {
877                 struct VerseSession *session = (VerseSession*)idv;
878                 te->name = session->address;
879                 te->directdata = (void*)session;
880                 te->idcode = ID_VS;
881         }
882         else if(type==ID_MS) {
883                 te->name = "Available Verse Servers";
884                 te->idcode = ID_MS;
885         }
886         else if(type==ID_SS) {
887                 struct VerseServer *server = (VerseServer *)idv;
888                 te->name = server->name;
889                 te->directdata = (void *)server;
890                 te->idcode = ID_SS;
891         }
892         else if(type==ID_VN) {
893                 struct VNode *vnode = (VNode*)idv;
894                 te->name = vnode->name;
895                 te->idcode = ID_VN;
896                 if(vnode->type==V_NT_OBJECT) {
897                         struct TreeElement *ten;
898                         struct VNode *child_node;
899                         struct VLink *vlink;
900                         
901                         vlink = ((VObjectData*)vnode->data)->links.lb.first;
902                         while(vlink) {
903                                 child_node = vlink->target;
904                                 if(child_node && child_node->type==V_NT_GEOMETRY) {
905                                         ten = outliner_add_element(soops, &te->subtree, child_node, te, ID_VN, 0);
906                                         ten->directdata = child_node;
907                                 }
908                                 vlink = vlink->next;
909                         }
910                 }
911         }
912 #endif
913         return te;
914 }
915
916 static void outliner_make_hierarchy(SpaceOops *soops, ListBase *lb)
917 {
918         TreeElement *te, *ten, *tep;
919         TreeStoreElem *tselem;
920
921         /* build hierarchy */
922         te= lb->first;
923         while(te) {
924                 ten= te->next;
925                 tselem= TREESTORE(te);
926                 
927                 if(tselem->type==0 && te->idcode==ID_OB) {
928                         Object *ob= (Object *)tselem->id;
929                         if(ob->parent && ob->parent->id.newid) {
930                                 BLI_remlink(lb, te);
931                                 tep= (TreeElement *)ob->parent->id.newid;
932                                 BLI_addtail(&tep->subtree, te);
933                                 // set correct parent pointers
934                                 for(te=tep->subtree.first; te; te= te->next) te->parent= tep;
935                         }
936                 }
937                 te= ten;
938         }
939 }
940
941 static void outliner_build_tree(SpaceOops *soops)
942 {
943         Base *base;
944         Object *ob;
945         TreeElement *te, *ten;
946         TreeStoreElem *tselem;
947         int show_opened= soops->treestore==NULL; /* on first view, we open scenes */
948 #ifdef WITH_VERSE
949         struct VerseSession *session;
950 #endif
951
952         if(soops->tree.first && (soops->storeflag & SO_TREESTORE_REDRAW))
953            return;
954            
955         outliner_free_tree(&soops->tree);
956         outliner_storage_cleanup(soops);
957                                                    
958         /* clear ob id.new flags */
959         for(ob= G.main->object.first; ob; ob= ob->id.next) ob->id.newid= NULL;
960         
961         /* options */
962         if(soops->outlinevis == SO_LIBRARIES) {
963                 Library *lib;
964                 
965                 for(lib= G.main->library.first; lib; lib= lib->id.next) {
966                         ten= outliner_add_element(soops, &soops->tree, lib, NULL, 0, 0);
967                         lib->id.newid= (ID *)ten;
968                 }
969                 /* make hierarchy */
970                 ten= soops->tree.first;
971                 while(ten) {
972                         TreeElement *nten= ten->next, *par;
973                         tselem= TREESTORE(ten);
974                         lib= (Library *)tselem->id;
975                         if(lib->parent) {
976                                 BLI_remlink(&soops->tree, ten);
977                                 par= (TreeElement *)lib->parent->id.newid;
978                                 BLI_addtail(&par->subtree, ten);
979                                 ten->parent= par;
980                         }
981                         ten= nten;
982                 }
983                 /* restore newid pointers */
984                 for(lib= G.main->library.first; lib; lib= lib->id.next)
985                         lib->id.newid= NULL;
986                 
987         }
988         else if(soops->outlinevis == SO_ALL_SCENES) {
989                 Scene *sce;
990                 for(sce= G.main->scene.first; sce; sce= sce->id.next) {
991                         te= outliner_add_element(soops, &soops->tree, sce, NULL, 0, 0);
992                         tselem= TREESTORE(te);
993                         if(sce==G.scene && show_opened) 
994                                 tselem->flag &= ~TSE_CLOSED;
995                         
996                         for(base= sce->base.first; base; base= base->next) {
997                                 ten= outliner_add_element(soops, &te->subtree, base->object, te, 0, 0);
998                                 ten->directdata= base;
999                         }
1000                         outliner_make_hierarchy(soops, &te->subtree);
1001                         /* clear id.newid, to prevent objects be inserted in wrong scenes (parent in other scene) */
1002                         for(base= sce->base.first; base; base= base->next) base->object->id.newid= NULL;
1003                 }
1004         }
1005         else if(soops->outlinevis == SO_CUR_SCENE) {
1006                 
1007                 outliner_add_scene_contents(soops, &soops->tree, G.scene, NULL);
1008                 
1009                 for(base= G.scene->base.first; base; base= base->next) {
1010                         ten= outliner_add_element(soops, &soops->tree, base->object, NULL, 0, 0);
1011                         ten->directdata= base;
1012                 }
1013                 outliner_make_hierarchy(soops, &soops->tree);
1014         }
1015         else if(soops->outlinevis == SO_VISIBLE) {
1016                 for(base= G.scene->base.first; base; base= base->next) {
1017                         if(base->lay & G.scene->lay)
1018                                 outliner_add_element(soops, &soops->tree, base->object, NULL, 0, 0);
1019                 }
1020                 outliner_make_hierarchy(soops, &soops->tree);
1021         }
1022         else if(soops->outlinevis == SO_GROUPS) {
1023                 Group *group;
1024                 GroupObject *go;
1025                 
1026                 for(group= G.main->group.first; group; group= group->id.next) {
1027                         if(group->id.us) {
1028                                 te= outliner_add_element(soops, &soops->tree, group, NULL, 0, 0);
1029                                 tselem= TREESTORE(te);
1030                                 
1031                                 for(go= group->gobject.first; go; go= go->next) {
1032                                         ten= outliner_add_element(soops, &te->subtree, go->ob, te, 0, 0);
1033                                         ten->directdata= NULL; /* eh, why? */
1034                                 }
1035                                 outliner_make_hierarchy(soops, &te->subtree);
1036                                 /* clear id.newid, to prevent objects be inserted in wrong scenes (parent in other scene) */
1037                                 for(go= group->gobject.first; go; go= go->next) go->ob->id.newid= NULL;
1038                         }
1039                 }
1040         }
1041         else if(soops->outlinevis == SO_SAME_TYPE) {
1042                 Object *ob= OBACT;
1043                 if(ob) {
1044                         for(base= G.scene->base.first; base; base= base->next) {
1045                                 if(base->object->type==ob->type) {
1046                                         ten= outliner_add_element(soops, &soops->tree, base->object, NULL, 0, 0);
1047                                         ten->directdata= base;
1048                                 }
1049                         }
1050                         outliner_make_hierarchy(soops, &soops->tree);
1051                 }
1052         }
1053         else if(soops->outlinevis == SO_SELECTED) {
1054                 for(base= G.scene->base.first; base; base= base->next) {
1055                         if(base->lay & G.scene->lay) {
1056                                 if(base==BASACT || (base->flag & SELECT)) {
1057                                         ten= outliner_add_element(soops, &soops->tree, base->object, NULL, 0, 0);
1058                                         ten->directdata= base;
1059                                 }
1060                         }
1061                 }
1062                 outliner_make_hierarchy(soops, &soops->tree);
1063         }
1064 #ifdef WITH_VERSE
1065         else if(soops->outlinevis == SO_VERSE_SESSION) {
1066                 /* add all session to the "root" of hierarchy */
1067                 for(session=session_list.first; session; session = session->next) {
1068                         struct VNode *vnode;
1069                         if(session->flag & VERSE_CONNECTED) {
1070                                 te= outliner_add_element(soops, &soops->tree, session, NULL, ID_VS, 0);
1071                                 /* add all object nodes as childreen of session */
1072                                 for(vnode=session->nodes.lb.first; vnode; vnode=vnode->next) {
1073                                         if(vnode->type==V_NT_OBJECT) {
1074                                                 ten= outliner_add_element(soops, &te->subtree, vnode, te, ID_VN, 0);
1075                                                 ten->directdata= vnode;
1076                                         }
1077                                         else if(vnode->type==V_NT_BITMAP) {
1078                                                 ten= outliner_add_element(soops, &te->subtree, vnode, te, ID_VN, 0);
1079                                                 ten->directdata= vnode;
1080                                         }
1081                                 }
1082                         }
1083                 }
1084         }
1085         else if(soops->outlinevis == SO_VERSE_MS) {
1086                 te= outliner_add_element(soops, &soops->tree, "MS", NULL, ID_MS, 0);
1087                 if(server_list.first!=NULL) {
1088                         struct VerseServer *server;
1089                         /* add one main entry to root of hierarchy */
1090                         for(server=server_list.first; server; server=server->next) {
1091                                 ten= outliner_add_element(soops, &te->subtree, server, te, ID_SS, 0);
1092                                 ten->directdata= server;
1093                         }
1094                 }
1095         }
1096 #endif
1097         else {
1098                 ten= outliner_add_element(soops, &soops->tree, OBACT, NULL, 0, 0);
1099                 if(ten) ten->directdata= BASACT;
1100         }
1101
1102
1103         outliner_sort(soops, &soops->tree);
1104 }
1105
1106 /* **************** INTERACTIVE ************* */
1107
1108 static int outliner_count_levels(SpaceOops *soops, ListBase *lb, int curlevel)
1109 {
1110         TreeElement *te;
1111         int level=curlevel, lev;
1112         
1113         for(te= lb->first; te; te= te->next) {
1114                 
1115                 lev= outliner_count_levels(soops, &te->subtree, curlevel+1);
1116                 if(lev>level) level= lev;
1117         }
1118         return level;
1119 }
1120
1121 static int outliner_has_one_flag(SpaceOops *soops, ListBase *lb, short flag, short curlevel)
1122 {
1123         TreeElement *te;
1124         TreeStoreElem *tselem;
1125         int level;
1126         
1127         for(te= lb->first; te; te= te->next) {
1128                 tselem= TREESTORE(te);
1129                 if(tselem->flag & flag) return curlevel;
1130                 
1131                 level= outliner_has_one_flag(soops, &te->subtree, flag, curlevel+1);
1132                 if(level) return level;
1133         }
1134         return 0;
1135 }
1136
1137 static void outliner_set_flag(SpaceOops *soops, ListBase *lb, short flag, short set)
1138 {
1139         TreeElement *te;
1140         TreeStoreElem *tselem;
1141         
1142         for(te= lb->first; te; te= te->next) {
1143                 tselem= TREESTORE(te);
1144                 if(set==0) tselem->flag &= ~flag;
1145                 else tselem->flag |= flag;
1146                 outliner_set_flag(soops, &te->subtree, flag, set);
1147         }
1148 }
1149
1150 void outliner_toggle_visible(struct ScrArea *sa)
1151 {
1152         SpaceOops *soops= sa->spacedata.first;
1153         
1154         if( outliner_has_one_flag(soops, &soops->tree, TSE_CLOSED, 1))
1155                 outliner_set_flag(soops, &soops->tree, TSE_CLOSED, 0);
1156         else 
1157                 outliner_set_flag(soops, &soops->tree, TSE_CLOSED, 1);
1158
1159         BIF_undo_push("Outliner toggle visible");
1160         scrarea_queue_redraw(sa);
1161 }
1162
1163 void outliner_toggle_selected(struct ScrArea *sa)
1164 {
1165         SpaceOops *soops= sa->spacedata.first;
1166         
1167         if( outliner_has_one_flag(soops, &soops->tree, TSE_SELECTED, 1))
1168                 outliner_set_flag(soops, &soops->tree, TSE_SELECTED, 0);
1169         else 
1170                 outliner_set_flag(soops, &soops->tree, TSE_SELECTED, 1);
1171         
1172         BIF_undo_push("Outliner toggle selected");
1173         soops->storeflag |= SO_TREESTORE_REDRAW;
1174         scrarea_queue_redraw(sa);
1175 }
1176
1177
1178 static void outliner_openclose_level(SpaceOops *soops, ListBase *lb, int curlevel, int level, int open)
1179 {
1180         TreeElement *te;
1181         TreeStoreElem *tselem;
1182         
1183         for(te= lb->first; te; te= te->next) {
1184                 tselem= TREESTORE(te);
1185                 
1186                 if(open) {
1187                         if(curlevel<=level) tselem->flag &= ~TSE_CLOSED;
1188                 }
1189                 else {
1190                         if(curlevel>=level) tselem->flag |= TSE_CLOSED;
1191                 }
1192                 
1193                 outliner_openclose_level(soops, &te->subtree, curlevel+1, level, open);
1194         }
1195 }
1196
1197 /* return 1 when levels were opened */
1198 static int outliner_open_back(SpaceOops *soops, TreeElement *te)
1199 {
1200         TreeStoreElem *tselem;
1201         int retval= 0;
1202         
1203         for (te= te->parent; te; te= te->parent) {
1204                 tselem= TREESTORE(te);
1205                 if (tselem->flag & TSE_CLOSED) { 
1206                         tselem->flag &= ~TSE_CLOSED;
1207                         retval= 1;
1208                 }
1209         }
1210         return retval;
1211 }
1212
1213 static void outliner_open_reveal(SpaceOops *soops, ListBase *lb, TreeElement *teFind, int *found)
1214 {
1215         TreeElement *te;
1216         TreeStoreElem *tselem;
1217         
1218         for (te= lb->first; te; te= te->next) {
1219                 /* check if this tree-element was the one we're seeking */
1220                 if (te == teFind) {
1221                         *found= 1;
1222                         return;
1223                 }
1224                 
1225                 /* try to see if sub-tree contains it then */
1226                 outliner_open_reveal(soops, &te->subtree, teFind, found);
1227                 if (*found) {
1228                         tselem= TREESTORE(te);
1229                         if (tselem->flag & TSE_CLOSED) 
1230                                 tselem->flag &= ~TSE_CLOSED;
1231                         return;
1232                 }
1233         }
1234 }
1235
1236
1237 void outliner_one_level(struct ScrArea *sa, int add)
1238 {
1239         SpaceOops *soops= sa->spacedata.first;
1240         int level;
1241         
1242         level= outliner_has_one_flag(soops, &soops->tree, TSE_CLOSED, 1);
1243         if(add==1) {
1244                 if(level) outliner_openclose_level(soops, &soops->tree, 1, level, 1);
1245         }
1246         else {
1247                 if(level==0) level= outliner_count_levels(soops, &soops->tree, 0);
1248                 if(level) outliner_openclose_level(soops, &soops->tree, 1, level-1, 0);
1249         }
1250         
1251         BIF_undo_push("Outliner show/hide one level");
1252         scrarea_queue_redraw(sa);
1253 }
1254
1255 void outliner_page_up_down(ScrArea *sa, int up)
1256 {
1257         SpaceOops *soops= sa->spacedata.first;
1258         int dy= soops->v2d.mask.ymax-soops->v2d.mask.ymin;
1259         
1260         if(up == -1) dy= -dy;
1261         soops->v2d.cur.ymin+= dy;
1262         soops->v2d.cur.ymax+= dy;
1263         
1264         soops->storeflag |= SO_TREESTORE_REDRAW;
1265         scrarea_queue_redraw(sa);
1266 }
1267
1268 /* **** do clicks on items ******* */
1269
1270 static int tree_element_active_renderlayer(TreeElement *te, TreeStoreElem *tselem, int set)
1271 {
1272         Scene *sce;
1273         
1274         /* paranoia check */
1275         if(te->idcode!=ID_SCE)
1276                 return 0;
1277         sce= (Scene *)tselem->id;
1278         
1279         if(set) {
1280                 sce->r.actlay= tselem->nr;
1281                 allqueue(REDRAWBUTSSCENE, 0);
1282         }
1283         else {
1284                 return sce->r.actlay==tselem->nr;
1285         }
1286         return 0;
1287 }
1288
1289 static void tree_element_active_object(SpaceOops *soops, TreeElement *te)
1290 {
1291         TreeStoreElem *tselem= TREESTORE(te);
1292         Scene *sce;
1293         Base *base;
1294         Object *ob= NULL;
1295         
1296         /* if id is not object, we search back */
1297         if(te->idcode==ID_OB) ob= (Object *)tselem->id;
1298         else {
1299                 ob= (Object *)outliner_search_back(soops, te, ID_OB);
1300                 if(ob==OBACT) return;
1301         }
1302         if(ob==NULL) return;
1303         
1304         sce= (Scene *)outliner_search_back(soops, te, ID_SCE);
1305         if(sce && G.scene != sce) {
1306                 if(G.obedit) exit_editmode(EM_FREEDATA|EM_FREEUNDO|EM_WAITCURSOR);
1307                 set_scene(sce);
1308         }
1309         
1310         /* find associated base in current scene */
1311         for(base= FIRSTBASE; base; base= base->next) 
1312                 if(base->object==ob) break;
1313         if(base) {
1314                 if(G.qual & LR_SHIFTKEY) {
1315                         /* swap select */
1316                         if(base->flag & SELECT) base->flag &= ~SELECT;
1317                         else if ((base->object->restrictflag & OB_RESTRICT_VIEW)==0) base->flag |= SELECT;
1318                         base->object->flag= base->flag;
1319                 }
1320                 else {
1321                         Base *b;
1322                         /* deleselect all */
1323                         for(b= FIRSTBASE; b; b= b->next) {
1324                                 b->flag &= ~SELECT;
1325                                 b->object->flag= b->flag;
1326                         }
1327                         if ((base->object->restrictflag & OB_RESTRICT_VIEW)==0) {
1328                                 base->flag |= SELECT;
1329                                 base->object->flag |= SELECT;
1330                         }
1331                 }
1332                 set_active_base(base);  /* editview.c */
1333                 
1334                 allqueue(REDRAWVIEW3D, 1);
1335                 allqueue(REDRAWOOPS, 0);
1336                 allqueue(REDRAWINFO, 1);
1337         }
1338         
1339         if(ob!=G.obedit) exit_editmode(EM_FREEDATA|EM_FREEUNDO|EM_WAITCURSOR);
1340 }
1341
1342 static int tree_element_active_material(SpaceOops *soops, TreeElement *te, int set)
1343 {
1344         TreeElement *tes;
1345         Object *ob;
1346         
1347         /* we search for the object parent */
1348         ob= (Object *)outliner_search_back(soops, te, ID_OB);
1349         if(ob==NULL || ob!=OBACT) return 0;     // just paranoia
1350         
1351         /* searching in ob mat array? */
1352         tes= te->parent;
1353         if(tes->idcode==ID_OB) {
1354                 if(set) {
1355                         ob->actcol= te->index+1;
1356                         ob->colbits |= (1<<te->index);  // make ob material active too
1357                 }
1358                 else {
1359                         if(ob->actcol == te->index+1) 
1360                                 if(ob->colbits & (1<<te->index)) return 1;
1361                 }
1362         }
1363         /* or we search for obdata material */
1364         else {
1365                 if(set) {
1366                         ob->actcol= te->index+1;
1367                         ob->colbits &= ~(1<<te->index); // make obdata material active too
1368                 }
1369                 else {
1370                         if(ob->actcol == te->index+1)
1371                                 if( (ob->colbits & (1<<te->index))==0 ) return 1;
1372                 }
1373         }
1374         if(set) {
1375                 extern_set_butspace(F5KEY, 0);  // force shading buttons
1376                 BIF_preview_changed(ID_MA);
1377                 allqueue(REDRAWBUTSSHADING, 1);
1378                 allqueue(REDRAWNODE, 0);
1379                 allqueue(REDRAWOOPS, 0);
1380                 allqueue(REDRAWIPO, 0);
1381         }
1382         return 0;
1383 }
1384
1385 static int tree_element_active_texture(SpaceOops *soops, TreeElement *te, int set)
1386 {
1387         TreeElement *tep;
1388         TreeStoreElem *tselem, *tselemp;
1389         Object *ob=OBACT;
1390         ScrArea *sa;
1391         SpaceButs *sbuts=NULL;
1392         
1393         if(ob==NULL) return 0; // no active object
1394         
1395         tselem= TREESTORE(te);
1396         
1397         /* find buttons area (note, this is undefined really still, needs recode in blender) */
1398         sa= G.curscreen->areabase.first;
1399         while(sa) {
1400                 if(sa->spacetype==SPACE_BUTS) break;
1401                 sa= sa->next;
1402         }
1403         if(sa) sbuts= sa->spacedata.first;
1404         
1405         /* where is texture linked to? */
1406         tep= te->parent;
1407         tselemp= TREESTORE(tep);
1408         
1409         if(tep->idcode==ID_WO) {
1410                 World *wrld= (World *)tselemp->id;
1411
1412                 if(set) {
1413                         if(sbuts) {
1414                                 sbuts->tabo= TAB_SHADING_TEX;   // hack from header_buttonswin.c
1415                                 sbuts->texfrom= 1;
1416                         }
1417                         extern_set_butspace(F6KEY, 0);  // force shading buttons texture
1418                         wrld->texact= te->index;
1419                 }
1420                 else if(tselemp->id == (ID *)(G.scene->world)) {
1421                         if(wrld->texact==te->index) return 1;
1422                 }
1423         }
1424         else if(tep->idcode==ID_LA) {
1425                 Lamp *la= (Lamp *)tselemp->id;
1426                 if(set) {
1427                         if(sbuts) {
1428                                 sbuts->tabo= TAB_SHADING_TEX;   // hack from header_buttonswin.c
1429                                 sbuts->texfrom= 2;
1430                         }
1431                         extern_set_butspace(F6KEY, 0);  // force shading buttons texture
1432                         la->texact= te->index;
1433                 }
1434                 else {
1435                         if(tselemp->id == ob->data) {
1436                                 if(la->texact==te->index) return 1;
1437                         }
1438                 }
1439         }
1440         else if(tep->idcode==ID_MA) {
1441                 Material *ma= (Material *)tselemp->id;
1442                 if(set) {
1443                         if(sbuts) {
1444                                 //sbuts->tabo= TAB_SHADING_TEX; // hack from header_buttonswin.c
1445                                 sbuts->texfrom= 0;
1446                         }
1447                         extern_set_butspace(F6KEY, 0);  // force shading buttons texture
1448                         ma->texact= te->index;
1449                         
1450                         /* also set active material */
1451                         ob->actcol= tep->index+1;
1452                 }
1453                 else if(tep->flag & TE_ACTIVE) {        // this is active material
1454                         if(ma->texact==te->index) return 1;
1455                 }
1456         }
1457         
1458         return 0;
1459 }
1460
1461
1462 static int tree_element_active_lamp(SpaceOops *soops, TreeElement *te, int set)
1463 {
1464         Object *ob;
1465         
1466         /* we search for the object parent */
1467         ob= (Object *)outliner_search_back(soops, te, ID_OB);
1468         if(ob==NULL || ob!=OBACT) return 0;     // just paranoia
1469         
1470         if(set) {
1471                 extern_set_butspace(F5KEY, 0);
1472                 BIF_preview_changed(ID_LA);
1473                 allqueue(REDRAWBUTSSHADING, 1);
1474                 allqueue(REDRAWOOPS, 0);
1475                 allqueue(REDRAWIPO, 0);
1476         }
1477         else return 1;
1478         
1479         return 0;
1480 }
1481
1482 static int tree_element_active_world(SpaceOops *soops, TreeElement *te, int set)
1483 {
1484         TreeElement *tep;
1485         TreeStoreElem *tselem=NULL;
1486         Scene *sce=NULL;
1487         
1488         tep= te->parent;
1489         if(tep) {
1490                 tselem= TREESTORE(tep);
1491                 sce= (Scene *)tselem->id;
1492         }
1493         
1494         if(set) {       // make new scene active
1495                 if(sce && G.scene != sce) {
1496                         if(G.obedit) exit_editmode(EM_FREEDATA|EM_FREEUNDO|EM_WAITCURSOR);
1497                         set_scene(sce);
1498                 }
1499         }
1500         
1501         if(tep==NULL || tselem->id == (ID *)G.scene) {
1502                 if(set) {
1503                         extern_set_butspace(F8KEY, 0);
1504                 }
1505                 else {
1506                         return 1;
1507                 }
1508         }
1509         return 0;
1510 }
1511
1512 static int tree_element_active_ipo(SpaceOops *soops, TreeElement *te, int set)
1513 {
1514         TreeElement *tes;
1515         TreeStoreElem *tselems=NULL;
1516         Object *ob;
1517         
1518         /* we search for the object parent */
1519         ob= (Object *)outliner_search_back(soops, te, ID_OB);
1520         if(ob==NULL || ob!=OBACT) return 0;     // just paranoia
1521         
1522         /* the parent of ipo */
1523         tes= te->parent;
1524         tselems= TREESTORE(tes);
1525         
1526         if(set) {
1527                 if(tes->idcode==ID_AC) {
1528                         if(ob->ipoflag & OB_ACTION_OB)
1529                                 ob->ipowin= ID_OB;
1530                         else if(ob->ipoflag & OB_ACTION_KEY)
1531                                 ob->ipowin= ID_KE;
1532                         else 
1533                                 ob->ipowin= ID_PO;
1534                 }
1535                 else ob->ipowin= tes->idcode;
1536                 
1537                 if(ob->ipowin==ID_MA) tree_element_active_material(soops, tes, 1);
1538                 else if(ob->ipowin==ID_AC) {
1539                         bActionChannel *chan;
1540                         short a=0;
1541                         for(chan=ob->action->chanbase.first; chan; chan= chan->next) {
1542                                 if(a==te->index) break;
1543                                 if(chan->ipo) a++;
1544                         }
1545                         deselect_actionchannels(ob->action, 0);
1546                         select_channel(ob->action, chan, SELECT_ADD);
1547                         allqueue(REDRAWACTION, ob->ipowin);
1548                         allqueue(REDRAWVIEW3D, ob->ipowin);
1549                 }
1550                 
1551                 allqueue(REDRAWIPO, ob->ipowin);
1552         }
1553         else {
1554                 if(tes->idcode==ID_AC) {
1555                         if(ob->ipoflag & OB_ACTION_OB)
1556                                 return ob->ipowin==ID_OB;
1557                         else if(ob->ipoflag & OB_ACTION_KEY)
1558                                 return ob->ipowin==ID_KE;
1559                         else if(ob->ipowin==ID_AC) {
1560                                 bActionChannel *chan;
1561                                 short a=0;
1562                                 for(chan=ob->action->chanbase.first; chan; chan= chan->next) {
1563                                         if(a==te->index) break;
1564                                         if(chan->ipo) a++;
1565                                 }
1566                                 if(chan==get_hilighted_action_channel(ob->action)) return 1;
1567                         }
1568                 }
1569                 else if(ob->ipowin==tes->idcode) {
1570                         if(ob->ipowin==ID_MA) {
1571                                 Material *ma= give_current_material(ob, ob->actcol);
1572                                 if(ma==(Material *)tselems->id) return 1;
1573                         }
1574                         else return 1;
1575                 }
1576         }
1577         return 0;
1578 }       
1579
1580 static int tree_element_active_defgroup(TreeElement *te, TreeStoreElem *tselem, int set)
1581 {
1582         Object *ob;
1583         
1584         /* id in tselem is object */
1585         ob= (Object *)tselem->id;
1586         if(set) {
1587                 ob->actdef= te->index+1;
1588                 DAG_object_flush_update(G.scene, ob, OB_RECALC_DATA);
1589                 allqueue(REDRAWVIEW3D, ob->ipowin);
1590         }
1591         else {
1592                 if(ob==OBACT)
1593                         if(ob->actdef== te->index+1) return 1;
1594         }
1595         return 0;
1596 }
1597
1598 static int tree_element_active_nla_action(TreeElement *te, TreeStoreElem *tselem, int set)
1599 {
1600         if(set) {
1601                 bActionStrip *strip= te->directdata;
1602                 if(strip) {
1603                         deselect_nlachannel_keys(0);
1604                         strip->flag |= ACTSTRIP_SELECT;
1605                         allqueue(REDRAWNLA, 0);
1606                 }
1607         }
1608         else {
1609                 /* id in tselem is action */
1610                 bActionStrip *strip= te->directdata;
1611                 if(strip) {
1612                         if(strip->flag & ACTSTRIP_SELECT) return 1;
1613                 }
1614         }
1615         return 0;
1616 }
1617
1618 static int tree_element_active_posechannel(TreeElement *te, TreeStoreElem *tselem, int set)
1619 {
1620         Object *ob= (Object *)tselem->id;
1621         bPoseChannel *pchan= te->directdata;
1622         
1623         if(set) {
1624                 if(!(pchan->bone->flag & BONE_HIDDEN_P)) {
1625                         
1626                         if(G.qual & LR_SHIFTKEY) deselectall_posearmature(ob, 2, 0);    // 2 = clear active tag
1627                         else deselectall_posearmature(ob, 0, 0);        // 0 = deselect 
1628                         pchan->bone->flag |= BONE_SELECTED|BONE_ACTIVE;
1629                         
1630                         allqueue(REDRAWVIEW3D, 0);
1631                         allqueue(REDRAWOOPS, 0);
1632                         allqueue(REDRAWACTION, 0);
1633                 }
1634         }
1635         else {
1636                 if(ob==OBACT && ob->pose) {
1637                         if (pchan->bone->flag & BONE_SELECTED) return 1;
1638                 }
1639         }
1640         return 0;
1641 }
1642
1643 static int tree_element_active_bone(TreeElement *te, TreeStoreElem *tselem, int set)
1644 {
1645         bArmature *arm= (bArmature *)tselem->id;
1646         Bone *bone= te->directdata;
1647         
1648         if(set) {
1649                 if(!(bone->flag & BONE_HIDDEN_P)) {
1650                         if(G.qual & LR_SHIFTKEY) deselectall_posearmature(OBACT, 2, 0); // 2 is clear active tag
1651                         else deselectall_posearmature(OBACT, 0, 0);
1652                         bone->flag |= BONE_SELECTED|BONE_ACTIVE;
1653                         
1654                         allqueue(REDRAWVIEW3D, 0);
1655                         allqueue(REDRAWOOPS, 0);
1656                         allqueue(REDRAWACTION, 0);
1657                 }
1658         }
1659         else {
1660                 Object *ob= OBACT;
1661                 
1662                 if(ob && ob->data==arm) {
1663                         if (bone->flag & BONE_SELECTED) return 1;
1664                 }
1665         }
1666         return 0;
1667 }
1668
1669
1670 /* ebones only draw in editmode armature */
1671 static int tree_element_active_ebone(TreeElement *te, TreeStoreElem *tselem, int set)
1672 {
1673         EditBone *ebone= te->directdata;
1674         
1675         if(set) {
1676                 if(!(ebone->flag & BONE_HIDDEN_A)) {
1677                         
1678                         if(G.qual & LR_SHIFTKEY) deselectall_armature(2, 0);    // only clear active tag
1679                         else deselectall_armature(0, 0);        // deselect
1680
1681                         ebone->flag |= BONE_SELECTED|BONE_ROOTSEL|BONE_TIPSEL|BONE_ACTIVE;
1682                         // flush to parent?
1683                         if(ebone->parent && (ebone->flag & BONE_CONNECTED)) ebone->parent->flag |= BONE_TIPSEL;
1684                         
1685                         allqueue(REDRAWVIEW3D, 0);
1686                         allqueue(REDRAWOOPS, 0);
1687                         allqueue(REDRAWACTION, 0);
1688                 }
1689         }
1690         else {
1691                 if (ebone->flag & BONE_SELECTED) return 1;
1692         }
1693         return 0;
1694 }
1695
1696 static int tree_element_active_modifier(TreeElement *te, TreeStoreElem *tselem, int set)
1697 {
1698         if(set) {
1699                 extern_set_butspace(F9KEY, 0);
1700         }
1701         
1702         return 0;
1703 }
1704
1705 static int tree_element_active_constraint(TreeElement *te, TreeStoreElem *tselem, int set)
1706 {
1707         if(set) {
1708                 extern_set_butspace(F7KEY, 0);
1709         }
1710         
1711         return 0;
1712 }
1713
1714 static int tree_element_active_text(SpaceOops *soops, TreeElement *te, int set)
1715 {
1716         ScrArea *sa=NULL;
1717         
1718         for(sa= G.curscreen->areabase.first; sa; sa= sa->next) {
1719                 if(sa->spacetype==SPACE_TEXT) break;
1720         }
1721         if(sa) {
1722                 SpaceText *st= sa->spacedata.first;
1723                 TreeStoreElem *tselem= TREESTORE(te);
1724                 
1725                 if(set) {
1726                         st->text= (Text *)tselem->id;
1727                         st->top= 0;
1728                         scrarea_queue_redraw(sa);
1729                 }
1730                 else if(st->text==(Text *)tselem->id) return 1;
1731         }
1732         return 0;
1733 }
1734
1735 /* generic call for ID data check or make/check active in UI */
1736 static int tree_element_active(SpaceOops *soops, TreeElement *te, int set)
1737 {
1738
1739         switch(te->idcode) {
1740                 case ID_MA:
1741                         return tree_element_active_material(soops, te, set);
1742                 case ID_WO:
1743                         return tree_element_active_world(soops, te, set);
1744                 case ID_LA:
1745                         return tree_element_active_lamp(soops, te, set);
1746                 case ID_IP:
1747                         return tree_element_active_ipo(soops, te, set);
1748                 case ID_TE:
1749                         return tree_element_active_texture(soops, te, set);
1750                 case ID_TXT:
1751                         return tree_element_active_text(soops, te, set);
1752         }
1753         return 0;
1754 }
1755
1756 static int tree_element_active_pose(TreeElement *te, TreeStoreElem *tselem, int set)
1757 {
1758         Object *ob= (Object *)tselem->id;
1759         
1760         if(set) {
1761                 if(G.obedit) exit_editmode(EM_FREEDATA|EM_FREEUNDO|EM_WAITCURSOR);
1762                 if(ob->flag & OB_POSEMODE) exit_posemode();
1763                 else enter_posemode();
1764         }
1765         else {
1766                 if(ob->flag & OB_POSEMODE) return 1;
1767         }
1768         return 0;
1769 }
1770
1771 /* generic call for non-id data to make/check active in UI */
1772 static int tree_element_type_active(SpaceOops *soops, TreeElement *te, TreeStoreElem *tselem, int set)
1773 {
1774         
1775         switch(tselem->type) {
1776                 case TSE_NLA_ACTION:
1777                         return tree_element_active_nla_action(te, tselem, set);
1778                 case TSE_DEFGROUP:
1779                         return tree_element_active_defgroup(te, tselem, set);
1780                 case TSE_BONE:
1781                         return tree_element_active_bone(te, tselem, set);
1782                 case TSE_EBONE:
1783                         return tree_element_active_ebone(te, tselem, set);
1784                 case TSE_MODIFIER:
1785                         return tree_element_active_modifier(te, tselem, set);
1786                 case TSE_LINKED_OB:
1787                         if(set) tree_element_active_object(soops, te);
1788                         else if(tselem->id==(ID *)OBACT) return 1;
1789                         break;
1790                 case TSE_POSE_BASE:
1791                         return tree_element_active_pose(te, tselem, set);
1792                         break;
1793                 case TSE_POSE_CHANNEL:
1794                         return tree_element_active_posechannel(te, tselem, set);
1795                 case TSE_CONSTRAINT:
1796                         return tree_element_active_constraint(te, tselem, set);
1797                 case TSE_R_LAYER:
1798                         return tree_element_active_renderlayer(te, tselem, set);
1799         }
1800         return 0;
1801 }
1802
1803 #ifdef WITH_VERSE
1804 static void verse_operation_menu(TreeElement *te)
1805 {
1806         short event=0;
1807         if(te->idcode==ID_VS) {
1808                 struct VerseSession *session = (VerseSession*)te->directdata;
1809                 struct VNode *vnode;
1810                 if(!(session->flag & VERSE_AUTOSUBSCRIBE)) {
1811                         event = pupmenu("VerseSession %t| End Session %x1| Subscribe to All Nodes %x2| Start Autosubscribe %x3");
1812                 }
1813                 else {
1814                         event = pupmenu("VerseSession %t| End Session %x1| Subscribe to All Nodes %x2| Stop Autosubscribe %x4");
1815                 }
1816                 switch(event) {
1817                         case 1:
1818                                 end_verse_session(session);
1819                                 break;
1820                         case 2:
1821                                 vnode = session->nodes.lb.first;
1822                                 while(vnode) {
1823                                         b_verse_pop_node(vnode);
1824                                         vnode = vnode->next;
1825                                 }
1826                                 break;
1827                         case 3:
1828                                 vnode = session->nodes.lb.first;
1829                                 while(vnode) {
1830                                         b_verse_pop_node(vnode);
1831                                         vnode = vnode->next;
1832                                 }
1833                                 session->flag |= VERSE_AUTOSUBSCRIBE;
1834                                 break;
1835                         case 4:
1836                                 session->flag &= ~VERSE_AUTOSUBSCRIBE;
1837                                 break;
1838                 }
1839         }
1840         else if(te->idcode==ID_VN) {
1841                 struct VNode *vnode = (VNode*)te->directdata;
1842                 event = pupmenu("VerseNode %t| Subscribe %x1| Unsubscribe %x2");
1843                 switch(event) {
1844                         case 1:
1845                                 b_verse_pop_node(vnode);
1846                                 break;
1847                         case 2:
1848                                 /* Global */
1849                                 b_verse_unsubscribe(vnode);
1850                                 break;
1851                 }
1852         }
1853         else if(te->idcode==ID_MS) {
1854                         event = pupmenu("Verse Master Server %t| Refresh %x1");
1855                         b_verse_ms_get();
1856         }
1857         else if(te->idcode==ID_SS) {
1858                 struct VerseServer *vserver = (VerseServer*)te->directdata;
1859
1860                 if(!(vserver->flag & VERSE_CONNECTING) && !(vserver->flag & VERSE_CONNECTED)) {
1861                         event = pupmenu("VerseServer %t| Connect %x1");
1862                 } else if((vserver->flag & VERSE_CONNECTING) && !(vserver->flag & VERSE_CONNECTED)) {
1863                         event = pupmenu("VerseServer %t| Connecting %x2");
1864                 } else if(!(vserver->flag & VERSE_CONNECTING) && (vserver->flag & VERSE_CONNECTED)) {
1865                         event = pupmenu("VerseServer %t| Disconnect %x3");
1866                 }
1867                 switch(event) {
1868                         case 1:
1869                                 b_verse_connect(vserver->ip);
1870                                 vserver->flag |= VERSE_CONNECTING;
1871                                 break;
1872                         case 2:
1873                                 break;
1874                         case 3:
1875                                 end_verse_session(vserver->session);
1876                                 break;
1877                 }
1878         }
1879 }
1880 #endif
1881
1882
1883 static int do_outliner_mouse_event(SpaceOops *soops, TreeElement *te, short event, float *mval)
1884 {
1885         
1886         if(mval[1]>te->ys && mval[1]<te->ys+OL_H) {
1887                 TreeStoreElem *tselem= TREESTORE(te);
1888                 int openclose= 0;
1889
1890                 /* open close icon, three things to check */
1891                 if(event==RETKEY || event==PADENTER) openclose= 1; // enter opens/closes always
1892                 else if((te->flag & TE_ICONROW)==0) {                           // hidden icon, no open/close
1893                         if( mval[0]>te->xs && mval[0]<te->xs+OL_X) openclose= 1;
1894                 }
1895
1896                 if(openclose) {
1897                         
1898                         /* all below close/open? */
1899                         if( (G.qual & LR_SHIFTKEY) ) {
1900                                 tselem->flag &= ~TSE_CLOSED;
1901                                 outliner_set_flag(soops, &te->subtree, TSE_CLOSED, !outliner_has_one_flag(soops, &te->subtree, TSE_CLOSED, 1));
1902                         }
1903                         else {
1904                                 if(tselem->flag & TSE_CLOSED) tselem->flag &= ~TSE_CLOSED;
1905                                 else tselem->flag |= TSE_CLOSED;
1906                                 
1907                         }
1908                         
1909                         return 1;
1910                 }
1911                 /* name and first icon */
1912                 else if(mval[0]>te->xs && mval[0]<te->xend) {
1913                         
1914                         /* activate a name button? */
1915                         if(event==LEFTMOUSE) {
1916                         
1917                                 if (G.qual & LR_CTRLKEY) {
1918                                         if(ELEM8(tselem->type, TSE_NLA, TSE_DEFGROUP_BASE, TSE_CONSTRAINT_BASE, TSE_MODIFIER_BASE, TSE_SCRIPT_BASE, TSE_POSE_BASE, TSE_R_LAYER_BASE, TSE_R_PASS)) 
1919                                                 error("Cannot edit builtin name");
1920                                         else if(tselem->id->lib)
1921                                                 error_libdata();
1922                                         else {
1923                                                 tselem->flag |= TSE_TEXTBUT;
1924                                         }
1925                                 } else {
1926                                         
1927                                         if (G.qual & LR_SHIFTKEY) {
1928                                                 if(tselem->id->lib && tselem->type==0) {
1929                                                         notice(tselem->id->lib->name);
1930                                                 }
1931                                         }
1932                                         /* always makes active object */
1933                                         tree_element_active_object(soops, te);
1934                                         
1935                                         if(tselem->type==0) { // the lib blocks
1936                                                 /* editmode? */
1937                                                 if(te->idcode==ID_SCE) {
1938                                                         if(G.scene!=(Scene *)tselem->id) {
1939                                                                 if(G.obedit) exit_editmode(EM_FREEDATA|EM_FREEUNDO|EM_WAITCURSOR);
1940                                                                 set_scene((Scene *)tselem->id);
1941                                                         }
1942                                                 }
1943                                                 else if(ELEM5(te->idcode, ID_ME, ID_CU, ID_MB, ID_LT, ID_AR)) {
1944                                                         if(G.obedit) exit_editmode(EM_FREEDATA|EM_FREEUNDO|EM_WAITCURSOR);
1945                                                         else {
1946                                                                 enter_editmode(EM_WAITCURSOR);
1947                                                                 extern_set_butspace(F9KEY, 0);
1948                                                         }
1949                                                 } else {        // rest of types
1950                                                         tree_element_active(soops, te, 1);
1951                                                 }
1952                                                 
1953                                         }
1954                                         else tree_element_type_active(soops, te, tselem, 1);
1955                                 }
1956                         }
1957                         else if(event==RIGHTMOUSE) {
1958 #ifdef WITH_VERSE
1959                                 if(ELEM4(te->idcode, ID_VS, ID_VN, ID_MS, ID_SS))
1960                                         verse_operation_menu(te);
1961                                 else
1962 #endif
1963                                 /* select object that's clicked on and popup context menu */
1964                                 if (!(tselem->flag & TSE_SELECTED)) {
1965                                 
1966                                         if ( outliner_has_one_flag(soops, &soops->tree, TSE_SELECTED, 1) )
1967                                                 outliner_set_flag(soops, &soops->tree, TSE_SELECTED, 0);
1968                                 
1969                                         tselem->flag |= TSE_SELECTED;
1970                                         /* redraw, same as outliner_select function */
1971                                         soops->storeflag |= SO_TREESTORE_REDRAW;
1972                                         scrarea_do_windraw(soops->area);
1973                                         screen_swapbuffers();
1974                                 }
1975                                 
1976                                 outliner_operation_menu(soops->area);
1977                         }
1978                         return 1;
1979                 }
1980         }
1981         
1982         for(te= te->subtree.first; te; te= te->next) {
1983                 if(do_outliner_mouse_event(soops, te, event, mval)) return 1;
1984         }
1985         return 0;
1986 }
1987
1988 /* event can enterkey, then it opens/closes */
1989 void outliner_mouse_event(ScrArea *sa, short event)
1990 {
1991         SpaceOops *soops= sa->spacedata.first;
1992         TreeElement *te;
1993         float fmval[2];
1994         short mval[2];
1995         
1996         getmouseco_areawin(mval);
1997         areamouseco_to_ipoco(&soops->v2d, mval, fmval, fmval+1);
1998         
1999         for(te= soops->tree.first; te; te= te->next) {
2000                 if(do_outliner_mouse_event(soops, te, event, fmval)) break;
2001         }
2002         
2003         if(te) {
2004                 BIF_undo_push("Outliner click event");
2005                 allqueue(REDRAWOOPS, 0);
2006         }
2007         else 
2008                 outliner_select(sa);
2009
2010 }
2011 /* recursive helper for function below */
2012 static void outliner_set_coordinates_element(SpaceOops *soops, TreeElement *te, int startx, int *starty)
2013 {
2014         TreeStoreElem *tselem= TREESTORE(te);
2015         
2016         /* store coord and continue, we need coordinates for elements outside view too */
2017         te->xs= startx;
2018         te->ys= *starty;
2019         *starty-= OL_H;
2020         
2021         if((tselem->flag & TSE_CLOSED)==0) {
2022                 TreeElement *ten;
2023                 for(ten= te->subtree.first; ten; ten= ten->next) {
2024                         outliner_set_coordinates_element(soops, ten, startx+OL_X, starty);
2025                 }
2026         }
2027         
2028 }
2029
2030 /* to retrieve coordinates with redrawing the entire tree */
2031 static void outliner_set_coordinates(SpaceOops *soops)
2032 {
2033         TreeElement *te;
2034         int starty= soops->v2d.tot.ymax-OL_H;
2035         int startx= 0;
2036         
2037         for(te= soops->tree.first; te; te= te->next) {
2038                 outliner_set_coordinates_element(soops, te, startx, &starty);
2039         }
2040 }
2041
2042 static TreeElement *outliner_find_id(SpaceOops *soops, ListBase *lb, ID *id)
2043 {
2044         TreeElement *te, *tes;
2045         TreeStoreElem *tselem;
2046         
2047         for(te= lb->first; te; te= te->next) {
2048                 tselem= TREESTORE(te);
2049                 if(tselem->type==0) {
2050                         if(tselem->id==id) return te;
2051                         /* only deeper on scene or object */
2052                         if( te->idcode==ID_OB || te->idcode==ID_SCE) { 
2053                                 tes= outliner_find_id(soops, &te->subtree, id);
2054                                 if(tes) return tes;
2055                         }
2056                 }
2057         }
2058         return NULL;
2059 }
2060
2061 void outliner_show_active(struct ScrArea *sa)
2062 {
2063         SpaceOops *so= sa->spacedata.first;
2064         TreeElement *te;
2065         int xdelta, ytop;
2066         
2067         if(OBACT == NULL) return;
2068         
2069         te= outliner_find_id(so, &so->tree, (ID *)OBACT);
2070         if(te) {
2071                 /* make te->ys center of view */
2072                 ytop= te->ys + (so->v2d.mask.ymax-so->v2d.mask.ymin)/2;
2073                 if(ytop>0) ytop= 0;
2074                 so->v2d.cur.ymax= ytop;
2075                 so->v2d.cur.ymin= ytop-(so->v2d.mask.ymax-so->v2d.mask.ymin);
2076                 
2077                 /* make te->xs ==> te->xend center of view */
2078                 xdelta = te->xs - so->v2d.cur.xmin;
2079                 so->v2d.cur.xmin += xdelta;
2080                 so->v2d.cur.xmax += xdelta;
2081                 
2082                 so->storeflag |= SO_TREESTORE_REDRAW;
2083                 scrarea_queue_redraw(sa);
2084         }
2085 }
2086
2087 void outliner_show_selected(struct ScrArea *sa)
2088 {
2089         SpaceOops *so= sa->spacedata.first;
2090         TreeElement *te;
2091         int xdelta, ytop;
2092         
2093         te= outliner_find_id(so, &so->tree, (ID *)OBACT);
2094         if(te) {
2095                 /* make te->ys center of view */
2096                 ytop= te->ys + (so->v2d.mask.ymax-so->v2d.mask.ymin)/2;
2097                 if(ytop>0) ytop= 0;
2098                 so->v2d.cur.ymax= ytop;
2099                 so->v2d.cur.ymin= ytop-(so->v2d.mask.ymax-so->v2d.mask.ymin);
2100                 
2101                 /* make te->xs ==> te->xend center of view */
2102                 xdelta = te->xs - so->v2d.cur.xmin;
2103                 so->v2d.cur.xmin += xdelta;
2104                 so->v2d.cur.xmax += xdelta;
2105                 
2106                 so->storeflag |= SO_TREESTORE_REDRAW;
2107                 scrarea_queue_redraw(sa);
2108         }
2109 }
2110
2111
2112 /* find next element that has this name */
2113 static TreeElement *outliner_find_named(SpaceOops *soops, ListBase *lb, char *name, int flags, TreeElement *prev, int *prevFound)
2114 {
2115         TreeElement *te, *tes;
2116         
2117         for (te= lb->first; te; te= te->next) {
2118                 int found;
2119                 
2120                 /* determine if match */
2121                 if(flags==OL_FIND)
2122                         found= BLI_strcasestr(te->name, name)!=NULL;
2123                 else if(flags==OL_FIND_CASE)
2124                         found= strstr(te->name, name)!=NULL;
2125                 else if(flags==OL_FIND_COMPLETE)
2126                         found= BLI_strcasecmp(te->name, name)==0;
2127                 else
2128                         found= strcmp(te->name, name)==0;
2129                 
2130                 if(found) {
2131                         /* name is right, but is element the previous one? */
2132                         if (prev) {
2133                                 if ((te != prev) && (*prevFound)) 
2134                                         return te;
2135                                 if (te == prev) {
2136                                         *prevFound = 1;
2137                                 }
2138                         }
2139                         else
2140                                 return te;
2141                 }
2142                 
2143                 tes= outliner_find_named(soops, &te->subtree, name, flags, prev, prevFound);
2144                 if(tes) return tes;
2145         }
2146
2147         /* nothing valid found */
2148         return NULL;
2149 }
2150
2151 /* tse is not in the treestore, we use its contents to find a match */
2152 static TreeElement *outliner_find_tse(SpaceOops *soops, TreeStoreElem *tse)
2153 {
2154         TreeStore *ts= soops->treestore;
2155         TreeStoreElem *tselem;
2156         int a;
2157         
2158         if(tse->id==NULL) return NULL;
2159         
2160         /* check if 'tse' is in treestore */
2161         tselem= ts->data;
2162         for(a=0; a<ts->usedelem; a++, tselem++) {
2163                 if(tselem->id==tse->id) {
2164                         if((tse->type==0 && tselem->type==0) || (tselem->type==tse->type && tselem->nr==tse->nr)) {
2165                                 break;
2166                         }
2167                 }
2168         }
2169         if(tselem) 
2170                 return outliner_find_tree_element(&soops->tree, a);
2171         
2172         return NULL;
2173 }
2174
2175
2176 /* Called to find an item based on name.
2177  */
2178 void outliner_find_panel(struct ScrArea *sa, int again, int flags) 
2179 {
2180         SpaceOops *soops= sa->spacedata.first;
2181         TreeElement *te= NULL;
2182         TreeElement *last_find;
2183         TreeStoreElem *tselem;
2184         int ytop, xdelta, prevFound=0;
2185         char name[33];
2186         
2187         /* get last found tree-element based on stored search_tse */
2188         last_find= outliner_find_tse(soops, &soops->search_tse);
2189         
2190         /* determine which type of search to do */
2191         if (again && last_find) {
2192                 /* no popup panel - previous + user wanted to search for next after previous */         
2193                 BLI_strncpy(name, soops->search_string, 33);
2194                 flags= soops->search_flags;
2195                 
2196                 /* try to find matching element */
2197                 te= outliner_find_named(soops, &soops->tree, name, flags, last_find, &prevFound);
2198                 if (te==NULL) {
2199                         /* no more matches after previous, start from beginning again */
2200                         prevFound= 1;
2201                         te= outliner_find_named(soops, &soops->tree, name, flags, last_find, &prevFound);
2202                 }
2203         }
2204         else {
2205                 /* pop up panel - no previous, or user didn't want search after previous */
2206                 strcpy(name, "");
2207                 if (sbutton(name, 0, sizeof(name)-1, "Find: ") && name[0]) {
2208                         te= outliner_find_named(soops, &soops->tree, name, flags, NULL, &prevFound);
2209                 }
2210                 else return; /* XXX RETURN! XXX */
2211         }
2212
2213         /* do selection and reveil */
2214         if (te) {
2215                 tselem= TREESTORE(te);
2216                 if (tselem) {
2217                         /* expand branches so that it will be visible, we need to get correct coordinates */
2218                         if( outliner_open_back(soops, te))
2219                                 outliner_set_coordinates(soops);
2220                         
2221                         /* deselect all visible, and select found element */
2222                         outliner_set_flag(soops, &soops->tree, TSE_SELECTED, 0);
2223                         tselem->flag |= TSE_SELECTED;
2224                         
2225                         /* make te->ys center of view */
2226                         ytop= te->ys + (soops->v2d.mask.ymax-soops->v2d.mask.ymin)/2;
2227                         if(ytop>0) ytop= 0;
2228                         soops->v2d.cur.ymax= ytop;
2229                         soops->v2d.cur.ymin= ytop-(soops->v2d.mask.ymax-soops->v2d.mask.ymin);
2230                         
2231                         /* make te->xs ==> te->xend center of view */
2232                         xdelta = te->xs - soops->v2d.cur.xmin;
2233                         soops->v2d.cur.xmin += xdelta;
2234                         soops->v2d.cur.xmax += xdelta;
2235                         
2236                         /* store selection */
2237                         soops->search_tse= *tselem;
2238                         
2239                         BLI_strncpy(soops->search_string, name, 33);
2240                         soops->search_flags= flags;
2241                         
2242                         /* redraw */
2243                         soops->storeflag |= SO_TREESTORE_REDRAW;
2244                         scrarea_queue_redraw(sa);
2245                 }
2246         }
2247         else {
2248                 /* no tree-element found */
2249                 error("Not found: %s", name);
2250         }
2251 }
2252
2253 static int subtree_has_objects(SpaceOops *soops, ListBase *lb)
2254 {
2255         TreeElement *te;
2256         TreeStoreElem *tselem;
2257         
2258         for(te= lb->first; te; te= te->next) {
2259                 tselem= TREESTORE(te);
2260                 if(tselem->type==0 && te->idcode==ID_OB) return 1;
2261                 if( subtree_has_objects(soops, &te->subtree)) return 1;
2262         }
2263         return 0;
2264 }
2265
2266 static void tree_element_show_hierarchy(SpaceOops *soops, ListBase *lb)
2267 {
2268         TreeElement *te;
2269         TreeStoreElem *tselem;
2270
2271         /* open all object elems, close others */
2272         for(te= lb->first; te; te= te->next) {
2273                 tselem= TREESTORE(te);
2274                 
2275                 if(tselem->type==0) {
2276                         if(te->idcode==ID_SCE) {
2277                                 if(tselem->id!=(ID *)G.scene) tselem->flag |= TSE_CLOSED;
2278                                         else tselem->flag &= ~TSE_CLOSED;
2279                         }
2280                         else if(te->idcode==ID_OB) {
2281                                 if(subtree_has_objects(soops, &te->subtree)) tselem->flag &= ~TSE_CLOSED;
2282                                 else tselem->flag |= TSE_CLOSED;
2283                         }
2284                 }
2285                 else tselem->flag |= TSE_CLOSED;
2286
2287                 if(tselem->flag & TSE_CLOSED); else tree_element_show_hierarchy(soops, &te->subtree);
2288         }
2289         
2290 }
2291
2292 /* show entire object level hierarchy */
2293 void outliner_show_hierarchy(struct ScrArea *sa)
2294 {
2295         SpaceOops *so= sa->spacedata.first;
2296         
2297         tree_element_show_hierarchy(so, &so->tree);
2298         scrarea_queue_redraw(sa);
2299         
2300         BIF_undo_push("Outliner show hierarchy");
2301 }
2302
2303 static void do_outliner_select(SpaceOops *soops, ListBase *lb, float y1, float y2, short *selecting)
2304 {
2305         TreeElement *te;
2306         TreeStoreElem *tselem;
2307         
2308         if(y1>y2) SWAP(float, y1, y2);
2309         
2310         for(te= lb->first; te; te= te->next) {
2311                 tselem= TREESTORE(te);
2312                 
2313                 if(te->ys + OL_H < y1) return;
2314                 if(te->ys < y2) {
2315                         if((te->flag & TE_ICONROW)==0) {
2316                                 if(*selecting == -1) {
2317                                         if( tselem->flag & TSE_SELECTED) *selecting= 0;
2318                                         else *selecting= 1;
2319                                 }
2320                                 if(*selecting) tselem->flag |= TSE_SELECTED;
2321                                 else tselem->flag &= ~TSE_SELECTED;
2322                         }
2323                 }
2324                 if((tselem->flag & TSE_CLOSED)==0) do_outliner_select(soops, &te->subtree, y1, y2, selecting);
2325         }
2326 }
2327
2328 /* its own redraw loop... urm */
2329 void outliner_select(struct ScrArea *sa )
2330 {
2331         SpaceOops *so= sa->spacedata.first;
2332         float fmval[2], y1, y2;
2333         short mval[2], yo=-1, selecting= -1;
2334         
2335         getmouseco_areawin(mval);
2336         areamouseco_to_ipoco(&so->v2d, mval, fmval, fmval+1);
2337         y1= fmval[1];
2338
2339         while (get_mbut() & (L_MOUSE|R_MOUSE)) {
2340                 getmouseco_areawin(mval);
2341                 areamouseco_to_ipoco(&so->v2d, mval, fmval, fmval+1);
2342                 y2= fmval[1];
2343                 
2344                 if(yo!=mval[1]) {
2345                         /* select the 'ouliner row' */
2346                         do_outliner_select(so, &so->tree, y1, y2, &selecting);
2347                         yo= mval[1];
2348                         
2349                         so->storeflag |= SO_TREESTORE_REDRAW;
2350                         scrarea_do_windraw(sa);
2351                         screen_swapbuffers();
2352                 
2353                         y1= y2;
2354                 }
2355                 else PIL_sleep_ms(30);
2356         }
2357         
2358         BIF_undo_push("Outliner selection");
2359
2360 }
2361
2362 /* ************ SELECTION OPERATIONS ********* */
2363
2364 static int scenelevel=0, objectlevel=0, idlevel=0, datalevel=0; // globals, euh... you can do better
2365
2366 static void set_operation_types(SpaceOops *soops, ListBase *lb)
2367 {
2368         TreeElement *te;
2369         TreeStoreElem *tselem;
2370         
2371         for(te= lb->first; te; te= te->next) {
2372                 tselem= TREESTORE(te);
2373                 if(tselem->flag & TSE_SELECTED) {
2374                         if(tselem->type) {
2375 #ifdef WITH_VERSE
2376                                 if(te->idcode==ID_VS) datalevel= TSE_VERSE_SESSION;
2377                                 else if(te->idcode==ID_VN) datalevel= TSE_VERSE_OBJ_NODE;
2378                                 else if(datalevel==0) datalevel= tselem->type;
2379 #else
2380                                 if(datalevel==0) datalevel= tselem->type;
2381 #endif
2382                                 else if(datalevel!=tselem->type) datalevel= -1;
2383                         }
2384                         else {
2385                                 int idcode= GS(tselem->id->name);
2386                                 switch(idcode) {
2387                                         case ID_SCE:
2388                                                 scenelevel= 1;
2389                                                 break;
2390                                         case ID_OB:
2391                                                 objectlevel= 1;
2392                                                 break;
2393                                                 
2394                                         case ID_ME: case ID_CU: case ID_MB: case ID_LT:
2395                                         case ID_LA: case ID_AR: case ID_CA:
2396                                         case ID_MA: case ID_TE: case ID_IP: case ID_IM:
2397                                         case ID_SO: case ID_KE: case ID_WO: case ID_AC:
2398                                         case ID_NLA: case ID_TXT: case ID_GR:
2399                                                 if(idlevel==0) idlevel= idcode;
2400                                                 else if(idlevel!=idcode) idlevel= -1;
2401                                                         break;
2402                                 }
2403                         }
2404                 }
2405                 if((tselem->flag & TSE_CLOSED)==0) set_operation_types(soops, &te->subtree);
2406         }
2407 }
2408
2409 static void unlink_material_cb(TreeElement *te, TreeStoreElem *tsep, TreeStoreElem *tselem)
2410 {
2411         Material **matar=NULL;
2412         int a, totcol=0;
2413         
2414         if( GS(tsep->id->name)==ID_OB) {
2415                 Object *ob= (Object *)tsep->id;
2416                 totcol= ob->totcol;
2417                 matar= ob->mat;
2418         }
2419         else if( GS(tsep->id->name)==ID_ME) {
2420                 Mesh *me= (Mesh *)tsep->id;
2421                 totcol= me->totcol;
2422                 matar= me->mat;
2423         }
2424         else if( GS(tsep->id->name)==ID_CU) {
2425                 Curve *cu= (Curve *)tsep->id;
2426                 totcol= cu->totcol;
2427                 matar= cu->mat;
2428         }
2429         else if( GS(tsep->id->name)==ID_MB) {
2430                 MetaBall *mb= (MetaBall *)tsep->id;
2431                 totcol= mb->totcol;
2432                 matar= mb->mat;
2433         }
2434
2435         for(a=0; a<totcol; a++) {
2436                 if(a==te->index && matar[a]) {
2437                         matar[a]->id.us--;
2438                         matar[a]= NULL;
2439                 }
2440         }
2441 }
2442
2443 static void unlink_texture_cb(TreeElement *te, TreeStoreElem *tsep, TreeStoreElem *tselem)
2444 {
2445         MTex **mtex= NULL;
2446         int a;
2447         
2448         if( GS(tsep->id->name)==ID_MA) {
2449                 Material *ma= (Material *)tsep->id;
2450                 mtex= ma->mtex;
2451         }
2452         else if( GS(tsep->id->name)==ID_LA) {
2453                 Lamp *la= (Lamp *)tsep->id;
2454                 mtex= la->mtex;
2455         }
2456         else if( GS(tsep->id->name)==ID_WO) {
2457                 World *wrld= (World *)tsep->id;
2458                 mtex= wrld->mtex;
2459         }
2460         else return;
2461         
2462         for(a=0; a<MAX_MTEX; a++) {
2463                 if(a==te->index && mtex[a]) {
2464                         if(mtex[a]->tex) {
2465                                 mtex[a]->tex->id.us--;
2466                                 mtex[a]->tex= NULL;
2467                         }
2468                 }
2469         }
2470 }
2471
2472 static void unlink_group_cb(TreeElement *te, TreeStoreElem *tsep, TreeStoreElem *tselem)
2473 {
2474         Group *group= (Group *)tselem->id;
2475         
2476         if(tsep) {
2477                 if( GS(tsep->id->name)==ID_OB) {
2478                         Object *ob= (Object *)tsep->id;
2479                         ob->dup_group= NULL;
2480                         group->id.us--;
2481                 }
2482         }
2483         else {
2484                 unlink_group(group);
2485         }
2486 }
2487
2488 static void outliner_do_libdata_operation(SpaceOops *soops, ListBase *lb, 
2489                                                                                  void (*operation_cb)(TreeElement *, TreeStoreElem *, TreeStoreElem *))
2490 {
2491         TreeElement *te;
2492         TreeStoreElem *tselem;
2493         
2494         for(te=lb->first; te; te= te->next) {
2495                 tselem= TREESTORE(te);
2496                 if(tselem->flag & TSE_SELECTED) {
2497                         if(tselem->type==0) {
2498                                 TreeStoreElem *tsep= TREESTORE(te->parent);
2499                                 operation_cb(te, tsep, tselem);
2500                         }
2501                 }
2502                 if((tselem->flag & TSE_CLOSED)==0) {
2503                         outliner_do_libdata_operation(soops, &te->subtree, operation_cb);
2504                 }
2505         }
2506 }
2507
2508 /* */
2509
2510 static void object_select_cb(TreeElement *te, TreeStoreElem *tsep, TreeStoreElem *tselem)
2511 {
2512         Base *base= (Base *)te->directdata;
2513         
2514         if(base==NULL) base= object_in_scene((Object *)tselem->id, G.scene);
2515         if(base && ((base->object->restrictflag & OB_RESTRICT_VIEW)==0)) {
2516                 base->flag |= SELECT;
2517                 base->object->flag |= SELECT;
2518         }
2519 }
2520
2521 static void object_deselect_cb(TreeElement *te, TreeStoreElem *tsep, TreeStoreElem *tselem)
2522 {
2523         Base *base= (Base *)te->directdata;
2524         
2525         if(base==NULL) base= object_in_scene((Object *)tselem->id, G.scene);
2526         if(base) {
2527                 base->flag &= ~SELECT;
2528                 base->object->flag &= ~SELECT;
2529         }
2530 }
2531
2532 static void object_delete_cb(TreeElement *te, TreeStoreElem *tsep, TreeStoreElem *tselem)
2533 {
2534         Base *base= (Base *)te->directdata;
2535         
2536         if(base==NULL) base= object_in_scene((Object *)tselem->id, G.scene);
2537         if(base) {
2538                 // check also library later
2539                 if(G.obedit==base->object) exit_editmode(EM_FREEDATA|EM_FREEUNDO|EM_WAITCURSOR);
2540                 
2541                 if(base==BASACT) {
2542                         G.f &= ~(G_VERTEXPAINT+G_TEXTUREPAINT+G_WEIGHTPAINT);
2543                         setcursor_space(SPACE_VIEW3D, CURSOR_STD);
2544                 }
2545                 
2546                 free_and_unlink_base(base);
2547                 te->directdata= NULL;
2548                 tselem->id= NULL;
2549         }
2550 }
2551
2552 static void id_local_cb(TreeElement *te, TreeStoreElem *tsep, TreeStoreElem *tselem)
2553 {
2554         if(tselem->id->lib && (tselem->id->flag & LIB_EXTERN)) {
2555                 tselem->id->lib= NULL;
2556                 tselem->id->flag= LIB_LOCAL;
2557                 new_id(0, tselem->id, 0);
2558         }
2559 }
2560
2561 static void outliner_do_object_operation(SpaceOops *soops, ListBase *lb, 
2562                                                                                  void (*operation_cb)(TreeElement *, TreeStoreElem *, TreeStoreElem *))
2563 {
2564         TreeElement *te;
2565         TreeStoreElem *tselem;
2566         
2567         for(te=lb->first; te; te= te->next) {
2568                 tselem= TREESTORE(te);
2569                 if(tselem->flag & TSE_SELECTED) {
2570                         if(tselem->type==0 && te->idcode==ID_OB) {
2571                                 // when objects selected in other scenes... dunno if that should be allowed
2572                                 Scene *sce= (Scene *)outliner_search_back(soops, te, ID_SCE);
2573                                 if(sce && G.scene != sce) set_scene(sce);
2574                                 
2575                                 operation_cb(te, NULL, tselem);
2576                         }
2577                 }
2578                 if((tselem->flag & TSE_CLOSED)==0) {
2579                         outliner_do_object_operation(soops, &te->subtree, operation_cb);
2580                 }
2581         }
2582 }
2583
2584 static void pchan_cb(int event, TreeElement *te, TreeStoreElem *tselem)
2585 {
2586         bPoseChannel *pchan= (bPoseChannel *)te->directdata;
2587         
2588         if(event==1)
2589                 pchan->bone->flag |= BONE_SELECTED;
2590         else if(event==2)
2591                 pchan->bone->flag &= ~BONE_SELECTED;
2592         else if(event==3) {
2593                 pchan->bone->flag |= BONE_HIDDEN_P;
2594                 pchan->bone->flag &= ~BONE_SELECTED;
2595         }
2596         else if(event==4)
2597                 pchan->bone->flag &= ~BONE_HIDDEN_P;
2598 }
2599
2600 static void bone_cb(int event, TreeElement *te, TreeStoreElem *tselem)
2601 {
2602         Bone *bone= (Bone *)te->directdata;
2603         
2604         if(event==1)
2605                 bone->flag |= BONE_SELECTED;
2606         else if(event==2)
2607                 bone->flag &= ~BONE_SELECTED;
2608         else if(event==3) {
2609                 bone->flag |= BONE_HIDDEN_P;
2610                 bone->flag &= ~BONE_SELECTED;
2611         }
2612         else if(event==4)
2613                 bone->flag &= ~BONE_HIDDEN_P;
2614 }
2615
2616 static void ebone_cb(int event, TreeElement *te, TreeStoreElem *tselem)
2617 {
2618         EditBone *ebone= (EditBone *)te->directdata;
2619         
2620         if(event==1)
2621                 ebone->flag |= BONE_SELECTED;
2622         else if(event==2)
2623                 ebone->flag &= ~BONE_SELECTED;
2624         else if(event==3) {
2625                 ebone->flag |= BONE_HIDDEN_A;
2626                 ebone->flag &= ~BONE_SELECTED|BONE_TIPSEL|BONE_ROOTSEL;
2627         }
2628         else if(event==4)
2629                 ebone->flag &= ~BONE_HIDDEN_A;
2630 }
2631
2632 #ifdef WITH_VERSE
2633 static void vsession_cb(int event, TreeElement *te, TreeStoreElem *tselem)
2634 {
2635 /*      struct VerseSession *vsession =(VerseSession*)te->directdata;*/
2636
2637         if(event==1) {
2638                 printf("\tending verse session\n");
2639         }
2640 }
2641 #endif
2642
2643 static void outliner_do_data_operation(SpaceOops *soops, int type, int event, ListBase *lb, 
2644                                                                                  void (*operation_cb)(int, TreeElement *, TreeStoreElem *))
2645 {
2646         TreeElement *te;
2647         TreeStoreElem *tselem;
2648         
2649         for(te=lb->first; te; te= te->next) {
2650                 tselem= TREESTORE(te);
2651                 if(tselem->flag & TSE_SELECTED) {
2652                         if(tselem->type==type) {
2653                                 operation_cb(event, te, tselem);
2654                         }
2655                 }
2656                 if((tselem->flag & TSE_CLOSED)==0) {
2657                         outliner_do_data_operation(soops, type, event, &te->subtree, operation_cb);
2658                 }
2659         }
2660 }
2661
2662 void outliner_del(ScrArea *sa)
2663 {
2664         SpaceOops *soops= sa->spacedata.first;
2665         outliner_do_object_operation(soops, &soops->tree, object_delete_cb);
2666         DAG_scene_sort(G.scene);
2667         countall();     
2668         BIF_undo_push("Delete Objects");
2669         allqueue(REDRAWALL, 0); 
2670 }
2671
2672
2673 void outliner_operation_menu(ScrArea *sa)
2674 {
2675         SpaceOops *soops= sa->spacedata.first;
2676         
2677         // bad globals
2678         scenelevel= objectlevel= idlevel= datalevel=0;
2679         
2680         set_operation_types(soops, &soops->tree);
2681         
2682         if(scenelevel) {
2683                 if(objectlevel || datalevel || idlevel) error("Mixed selection");
2684                 //else pupmenu("Scene Operations%t|Delete");
2685         }
2686         else if(objectlevel) {
2687                 short event= pupmenu("Select%x1|Deselect%x2|Delete%x4");        /* make local: does not work... it doesn't set lib_extern flags... so data gets lost */
2688                 if(event>0) {
2689                         char *str="";
2690                         
2691                         if(event==1) {
2692                                 Scene *sce= G.scene;    // to be able to delete, scenes are set...
2693                                 outliner_do_object_operation(soops, &soops->tree, object_select_cb);
2694                                 if(G.scene != sce) set_scene(sce);
2695                                 
2696                                 str= "Select Objects";
2697                         }
2698                         else if(event==2) {
2699                                 outliner_do_object_operation(soops, &soops->tree, object_deselect_cb);
2700                                 str= "Deselect Objects";
2701                         }
2702                         else if(event==4) {
2703                                 outliner_do_object_operation(soops, &soops->tree, object_delete_cb);
2704                                 DAG_scene_sort(G.scene);
2705                                 str= "Delete Objects";
2706                         }
2707                         else if(event==5) {     /* disabled, see above (ton) */
2708                                 outliner_do_object_operation(soops, &soops->tree, id_local_cb);
2709                                 str= "Localized Objects";
2710                         }
2711                         
2712                         countall();
2713                         
2714                         BIF_undo_push(str);
2715                         allqueue(REDRAWALL, 0);
2716                 }
2717         }
2718         else if(idlevel) {
2719                 if(idlevel==-1 || datalevel) error("Mixed selection");
2720                 else {
2721                         short event= pupmenu("Unlink %x1|Make Local %x2");
2722                         
2723                         if(event==1) {
2724                                 switch(idlevel) {
2725                                         case ID_MA:
2726                                                 outliner_do_libdata_operation(soops, &soops->tree, unlink_material_cb);
2727                                                 BIF_undo_push("Unlink material");
2728                                                 allqueue(REDRAWBUTSSHADING, 1);
2729                                                 break;
2730                                         case ID_TE:
2731                                                 outliner_do_libdata_operation(soops, &soops->tree, unlink_texture_cb);
2732                                                 allqueue(REDRAWBUTSSHADING, 1);
2733                                                 BIF_undo_push("Unlink texture");
2734                                                 break;
2735                                         case ID_GR:
2736                                                 outliner_do_libdata_operation(soops, &soops->tree, unlink_group_cb);
2737                                                 BIF_undo_push("Unlink group");
2738                                                 break;
2739                                         default:
2740                                                 error("Not yet...");
2741                                 }
2742                                 allqueue(REDRAWALL, 0);
2743                         }
2744                         else if(event==2) {
2745                                 outliner_do_libdata_operation(soops, &soops->tree, id_local_cb);
2746                                 BIF_undo_push("Localized Data");
2747                                 allqueue(REDRAWALL, 0); 
2748                         }
2749                 }
2750         }
2751         else if(datalevel) {
2752                 if(datalevel==-1) error("Mixed selection");
2753                 else {
2754                         if(datalevel==TSE_POSE_CHANNEL) {
2755                                 short event= pupmenu("PoseChannel Operations%t|Select%x1|Deselect%x2|Hide%x3|Unhide%x4");
2756                                 if(event>0) {
2757                                         outliner_do_data_operation(soops, datalevel, event, &soops->tree, pchan_cb);
2758                                         BIF_undo_push("PoseChannel operation");
2759                                 }
2760                         }
2761                         else if(datalevel==TSE_BONE) {
2762                                 short event= pupmenu("Bone Operations%t|Select%x1|Deselect%x2|Hide%x3|Unhide%x4");
2763                                 if(event>0) {
2764                                         outliner_do_data_operation(soops, datalevel, event, &soops->tree, bone_cb);
2765                                         BIF_undo_push("Bone operation");
2766                                 }
2767                         }
2768                         else if(datalevel==TSE_EBONE) {
2769                                 short event= pupmenu("EditBone Operations%t|Select%x1|Deselect%x2|Hide%x3|Unhide%x4");
2770                                 if(event>0) {
2771                                         outliner_do_data_operation(soops, datalevel, event, &soops->tree, ebone_cb);
2772                                         BIF_undo_push("EditBone operation");
2773                                 }
2774                         }
2775 #ifdef WITH_VERSE
2776                         else if(datalevel==TSE_VERSE_SESSION) {
2777                                 short event= pupmenu("VerseSession %t| End %x1");
2778                                 if(event>0) {
2779                                         outliner_do_data_operation(soops, datalevel, event, &soops->tree, vsession_cb);
2780                                 }
2781                         }
2782 #endif
2783                         
2784                         allqueue(REDRAWOOPS, 0);
2785                         allqueue(REDRAWBUTSALL, 0);
2786                         allqueue(REDRAWVIEW3D, 0);
2787                 }
2788         }
2789 }
2790
2791
2792 /* ***************** DRAW *************** */
2793
2794 static void tselem_draw_icon(float x, float y, TreeStoreElem *tselem, TreeElement *te)
2795 {
2796         if(tselem->type) {
2797                 switch( tselem->type) {
2798                         case TSE_NLA:
2799                                 BIF_icon_draw(x, y, ICON_NLA); break;
2800                         case TSE_NLA_ACTION:
2801                                 BIF_icon_draw(x, y, ICON_ACTION); break;
2802                         case TSE_DEFGROUP_BASE:
2803                                 BIF_icon_draw(x, y, ICON_VERTEXSEL); break;
2804                         case TSE_BONE:
2805                         case TSE_EBONE:
2806                                 BIF_icon_draw(x, y, ICON_WPAINT_DEHLT); break;
2807                         case TSE_CONSTRAINT_BASE:
2808                                 BIF_icon_draw(x, y, ICON_CONSTRAINT); break;
2809                         case TSE_MODIFIER_BASE:
2810                                 BIF_icon_draw(x, y, ICON_MODIFIER); break;
2811                         case TSE_LINKED_OB:
2812                                 BIF_icon_draw(x, y, ICON_OBJECT); break;
2813                         case TSE_MODIFIER:
2814                         {
2815                                 Object *ob= (Object *)tselem->id;
2816                                 ModifierData *md= BLI_findlink(&ob->modifiers, tselem->nr);
2817                                 switch(md->type) {
2818                                         case eModifierType_Subsurf: 
2819                                                 BIF_icon_draw(x, y, ICON_MOD_SUBSURF); break;
2820                                         case eModifierType_Armature: 
2821                                                 BIF_icon_draw(x, y, ICON_ARMATURE); break;
2822                                         case eModifierType_Lattice: 
2823                                                 BIF_icon_draw(x, y, ICON_LATTICE); break;
2824                                         case eModifierType_Curve: 
2825                                                 BIF_icon_draw(x, y, ICON_CURVE); break;
2826                                         case eModifierType_Build: 
2827                                                 BIF_icon_draw(x, y, ICON_MOD_BUILD); break;
2828                                         case eModifierType_Mirror: 
2829                                                 BIF_icon_draw(x, y, ICON_MOD_MIRROR); break;
2830                                         case eModifierType_Decimate: 
2831                                                 BIF_icon_draw(x, y, ICON_MOD_DECIM); break;
2832                                         case eModifierType_Wave: 
2833                                                 BIF_icon_draw(x, y, ICON_MOD_WAVE); break;
2834                                         case eModifierType_Hook: 
2835                                                 BIF_icon_draw(x, y, ICON_HOOK); break;
2836                                         case eModifierType_Softbody: 
2837                                                 BIF_icon_draw(x, y, ICON_MOD_SOFT); break;
2838                                         case eModifierType_Boolean: 
2839                                                 BIF_icon_draw(x, y, ICON_MOD_BOOLEAN); break;
2840                                         default:
2841                                                 BIF_icon_draw(x, y, ICON_DOT); break;
2842                                 }
2843                                 break;
2844                         }
2845                         case TSE_SCRIPT_BASE:
2846                                 BIF_icon_draw(x, y, ICON_TEXT); break;
2847                         case TSE_POSE_BASE:
2848                                 BIF_icon_draw(x, y, ICON_ARMATURE_DEHLT); break;
2849                         case TSE_POSE_CHANNEL:
2850                                 BIF_icon_draw(x, y, ICON_WPAINT_DEHLT); break;
2851                         case TSE_PROXY:
2852                                 BIF_icon_draw(x, y, ICON_GHOST); break;
2853                         case TSE_R_LAYER_BASE:
2854                                 BIF_icon_draw(x, y, ICON_RESTRICT_RENDER_OFF); break;
2855                         case TSE_R_LAYER:
2856                                 BIF_icon_draw(x, y, ICON_IMAGE_DEHLT); break;
2857                         case TSE_LINKED_LAMP:
2858                                 BIF_icon_draw(x, y, ICON_LAMP_DEHLT); break;
2859                         case TSE_LINKED_MAT:
2860                                 BIF_icon_draw(x, y, ICON_MATERIAL_DEHLT); break;
2861                                 
2862 #ifdef WITH_VERSE
2863                         case ID_VS:
2864                         case ID_MS:
2865                         case ID_SS:
2866                                 BIF_icon_draw(x, y, ICON_VERSE); break;
2867                         case ID_VN:
2868                                 BIF_icon_draw(x, y, ICON_VERSE); break;
2869 #endif
2870                         default:
2871                                 BIF_icon_draw(x, y, ICON_DOT); break;
2872                 }
2873         }
2874         else {
2875                 switch( GS(tselem->id->name)) {
2876                         case ID_SCE:
2877                                 BIF_icon_draw(x, y, ICON_SCENE_DEHLT); break;
2878                         case ID_OB:
2879                                 BIF_icon_draw(x, y, ICON_OBJECT); break;
2880                         case ID_ME:
2881                                 BIF_icon_draw(x, y, ICON_MESH); break;
2882                         case ID_CU:
2883                                 BIF_icon_draw(x, y, ICON_CURVE); break;
2884                         case ID_MB:
2885                                 BIF_icon_draw(x, y, ICON_MBALL); break;
2886                         case ID_LT:
2887                                 BIF_icon_draw(x, y, ICON_LATTICE); break;
2888                         case ID_LA:
2889                                 BIF_icon_draw(x, y, ICON_LAMP_DEHLT); break;
2890                         case ID_MA:
2891                                 BIF_icon_draw(x, y, ICON_MATERIAL_DEHLT); break;
2892                         case ID_TE:
2893                                 BIF_icon_draw(x, y, ICON_TEXTURE_DEHLT); break;
2894                         case ID_IP:
2895                                 BIF_icon_draw(x, y, ICON_IPO_DEHLT); break;
2896                         case ID_IM:
2897                                 BIF_icon_draw(x, y, ICON_IMAGE_DEHLT); break;
2898                         case ID_SO:
2899                                 BIF_icon_draw(x, y, ICON_SPEAKER); break;
2900                         case ID_AR:
2901                                 BIF_icon_draw(x, y, ICON_ARMATURE); break;
2902                         case ID_CA:
2903                                 BIF_icon_draw(x, y, ICON_CAMERA_DEHLT); break;
2904                         case ID_KE:
2905                                 BIF_icon_draw(x, y, ICON_EDIT_DEHLT); break;
2906                         case ID_WO:
2907                                 BIF_icon_draw(x, y, ICON_WORLD_DEHLT); break;
2908                         case ID_AC:
2909                                 BIF_icon_draw(x, y, ICON_ACTION); break;
2910                         case ID_NLA:
2911                                 BIF_icon_draw(x, y, ICON_NLA); break;
2912                         case ID_TXT:
2913                                 BIF_icon_draw(x, y, ICON_SCRIPT); break;
2914                         case ID_GR:
2915                                 BIF_icon_draw(x, y, ICON_CIRCLE_DEHLT); break;
2916                         case ID_LI:
2917                                 BIF_icon_draw(x, y, ICON_LIBRARY_DEHLT); break;
2918                 }
2919         }
2920 }
2921
2922 static void outliner_draw_iconrow(SpaceOops *soops, ListBase *lb, int level, int *offsx, int ys)
2923 {
2924         TreeElement *te;
2925         TreeStoreElem *tselem;
2926         int active;
2927
2928         for(te= lb->first; te; te= te->next) {
2929                 tselem= TREESTORE(te);
2930                 
2931                 /* object hierarchy always, further constrained on level */
2932                 if(level<1 || (tselem->type==0 && te->idcode==ID_OB)) {
2933
2934                         /* active blocks get white circle */
2935                         active= 0;
2936                         if(tselem->type==0) {
2937                                 if(te->idcode==ID_OB) active= (OBACT==(Object *)tselem->id);
2938                                 else if(G.obedit && G.obedit->data==tselem->id) active= 1;
2939                                 else active= tree_element_active(soops, te, 0);
2940                         }
2941                         else active= tree_element_type_active(soops, te, tselem, 0);
2942                         
2943                         if(active) {
2944                                 uiSetRoundBox(15);
2945                                 glColor4ub(255, 255, 255, 100);
2946                                 uiRoundBox( (float)*offsx-0.5, (float)ys-1.0, (float)*offsx+OL_H-3.0, (float)ys+OL_H-3.0, OL_H/2.0-2.0);
2947                                 glEnable(GL_BLEND);
2948                         }
2949                         
2950                         tselem_draw_icon(*offsx, ys, tselem, te);
2951                         te->xs= *offsx;
2952                         te->ys= ys;
2953                         te->xend= *offsx+OL_X;
2954                         te->flag |= TE_ICONROW; // for click
2955                         
2956                         (*offsx) += OL_X;
2957                 }
2958                 
2959                 /* this tree element always has same amount of branches, so dont draw */
2960                 if(tselem->type!=TSE_R_LAYER)
2961                         outliner_draw_iconrow(soops, &te->subtree, level+1, offsx, ys);
2962         }
2963         
2964 }
2965
2966 static void outliner_draw_tree_element(SpaceOops *soops, TreeElement *te, int startx, int *starty)
2967 {
2968         TreeElement *ten;
2969         TreeStoreElem *tselem;
2970         int offsx= 0, active=0; // active=1 active obj, else active data
2971         
2972         tselem= TREESTORE(te);
2973
2974         if(*starty >= soops->v2d.cur.ymin && *starty<= soops->v2d.cur.ymax) {
2975         
2976                 glEnable(GL_BLEND);
2977
2978                 /* colors for active/selected data */
2979                 if(tselem->type==0) {
2980                         if(te->idcode==ID_SCE) {
2981                                 if(tselem->id == (ID *)G.scene) {
2982                                         glColor4ub(255, 255, 255, 100);
2983                                         active= 2;
2984                                 }
2985                         }
2986                         else if(te->idcode==ID_OB) {
2987                                 Object *ob= (Object *)tselem->id;
2988                                 
2989                                 if(ob==OBACT || (ob->flag & SELECT)) {
2990                                         char col[4];
2991                                         
2992                                         active= 2;
2993                                         if(ob==OBACT) {
2994                                                 BIF_GetThemeColorType4ubv(TH_ACTIVE, SPACE_VIEW3D, col);
2995                                                 /* so black text is drawn when active and not selected */
2996                                                 if (ob->flag & SELECT) active= 1;
2997                                         }
2998                                         else BIF_GetThemeColorType4ubv(TH_SELECT, SPACE_VIEW3D, col);
2999                                         col[3]= 100;
3000                                         glColor4ubv((GLubyte *)col);
3001                                 }
3002
3003 #ifdef WITH_VERSE
3004                                 if(ob->vnode) {
3005                                         if (active==0) active=2;
3006                                         if (ob==OBACT)
3007                                                 glColor4ub(0,255,0,100);
3008                                         else
3009                                                 glColor4ub(0,128,0,100);
3010                                 }
3011 #endif
3012                         }
3013                         else if(G.obedit && G.obedit->data==tselem->id) {
3014                                 glColor4ub(255, 255, 255, 100);
3015                                 active= 2;
3016                         }
3017                         else {
3018                                 if(tree_element_active(soops, te, 0)) {
3019                                         glColor4ub(220, 220, 255, 100);
3020                                         active= 2;
3021                                 }
3022                         }
3023                 } else if (tselem->type==ID_SS) {
3024 #ifdef WITH_VERSE