Orange branch: Revived hidden treasure, the Groups!
[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_depsgraph.h"
63 #include "BKE_global.h"
64 #include "BKE_library.h"
65 #include "BKE_main.h"
66 #include "BKE_material.h"
67 #include "BKE_modifier.h"
68 #include "BKE_screen.h"
69 #include "BKE_scene.h"
70 #include "BKE_utildefines.h"
71
72 #include "BIF_butspace.h"
73 #include "BIF_drawscene.h"
74 #include "BIF_drawtext.h"
75 #include "BIF_editaction.h"
76 #include "BIF_editarmature.h"
77 #include "BIF_editdeform.h"
78 #include "BIF_editnla.h"
79 #include "BIF_editview.h"
80 #include "BIF_editconstraint.h"
81 #include "BIF_gl.h"
82 #include "BIF_graphics.h"
83 #include "BIF_interface.h"
84 #include "BIF_mywindow.h"
85 #include "BIF_outliner.h"
86 #include "BIF_language.h"
87 #include "BIF_mainqueue.h"
88 #include "BIF_poseobject.h"
89 #include "BIF_previewrender.h"
90 #include "BIF_resources.h"
91 #include "BIF_screen.h"
92 #include "BIF_space.h"
93 #include "BIF_toolbox.h"
94
95 #ifdef INTERNATIONAL
96 #include "FTF_Api.h"
97 #endif
98
99 #include "BDR_editobject.h"
100 #include "BSE_drawipo.h"
101 #include "BSE_edit.h"
102
103 #include "PIL_time.h" 
104
105 #include "blendef.h"
106 #include "mydevice.h"
107
108 #define OL_H    19
109 #define OL_X    18
110
111 #define TS_CHUNK        128
112
113 #define TREESTORE(a) soops->treestore->data+(a)->store_index
114
115 /* ******************** PERSISTANT DATA ***************** */
116
117 static void outliner_storage_cleanup(SpaceOops *soops)
118 {
119         TreeStore *ts= soops->treestore;
120         
121         if(ts) {
122                 TreeStoreElem *tselem;
123                 int a, unused= 0;
124                 
125                 /* each element used once, for ID blocks with more users to have each a treestore */
126                 for(a=0, tselem= ts->data; a<ts->usedelem; a++, tselem++) tselem->used= 0;
127
128                 /* cleanup only after reading file or undo step */
129                 if(soops->storeflag & SO_TREESTORE_CLEANUP) {
130                         
131                         for(a=0, tselem= ts->data; a<ts->usedelem; a++, tselem++) {
132                                 if(tselem->id==NULL) unused++;
133                         }
134
135                         if(unused) {
136                                 if(ts->usedelem == unused) {
137                                         MEM_freeN(ts->data);
138                                         ts->data= NULL;
139                                         ts->usedelem= ts->totelem= 0;
140                                 }
141                                 else {
142                                         TreeStoreElem *tsnewar, *tsnew;
143                                         
144                                         tsnew=tsnewar= MEM_mallocN((ts->usedelem-unused)*sizeof(TreeStoreElem), "new tselem");
145                                         for(a=0, tselem= ts->data; a<ts->usedelem; a++, tselem++) {
146                                                 if(tselem->id) {
147                                                         *tsnew= *tselem;
148                                                         tsnew++;
149                                                 }
150                                         }
151                                         MEM_freeN(ts->data);
152                                         ts->data= tsnewar;
153                                         ts->usedelem-= unused;
154                                         ts->totelem= ts->usedelem;
155                                 }
156                         }
157                 }
158         }
159 }
160
161 static void check_persistant(SpaceOops *soops, TreeElement *te, ID *id, short type, short nr)
162 {
163         TreeStore *ts;
164         TreeStoreElem *tselem;
165         int a;
166         
167         /* case 1; no TreeStore */
168         if(soops->treestore==NULL) {
169                 ts= soops->treestore= MEM_callocN(sizeof(TreeStore), "treestore");
170         }
171         ts= soops->treestore;
172         
173         /* check if 'te' is in treestore */
174         tselem= ts->data;
175         for(a=0; a<ts->usedelem; a++, tselem++) {
176                 if(tselem->id==id && tselem->used==0) {
177                         if((type==0 && tselem->type==0) ||(tselem->type==type && tselem->nr==nr)) {
178                                 te->store_index= a;
179                                 tselem->used= 1;
180                                 return;
181                         }
182                 }
183         }
184         
185         /* add 1 element to treestore */
186         if(ts->usedelem==ts->totelem) {
187                 TreeStoreElem *tsnew;
188                 
189                 tsnew= MEM_mallocN((ts->totelem+TS_CHUNK)*sizeof(TreeStoreElem), "treestore data");
190                 if(ts->data) {
191                         memcpy(tsnew, ts->data, ts->totelem*sizeof(TreeStoreElem));
192                         MEM_freeN(ts->data);
193                 }
194                 ts->data= tsnew;
195                 ts->totelem+= TS_CHUNK;
196         }
197         
198         tselem= ts->data+ts->usedelem;
199         
200         tselem->type= type;
201         if(type) tselem->nr= nr; // we're picky! :)
202         else tselem->nr= 0;
203         tselem->id= id;
204         tselem->flag= TSE_CLOSED;
205         te->store_index= ts->usedelem;
206         
207         ts->usedelem++;
208 }
209
210 /* ******************** TREE MANAGEMENT ****************** */
211
212 void outliner_free_tree(ListBase *lb)
213 {
214         
215         while(lb->first) {
216                 TreeElement *te= lb->first;
217
218                 outliner_free_tree(&te->subtree);
219                 BLI_remlink(lb, te);
220                 MEM_freeN(te);
221         }
222 }
223
224 static void outliner_height(SpaceOops *soops, ListBase *lb, int *h)
225 {
226         TreeElement *te= lb->first;
227         while(te) {
228                 TreeStoreElem *tselem= TREESTORE(te);
229                 if((tselem->flag & TSE_CLOSED)==0) 
230                         outliner_height(soops, &te->subtree, h);
231                 (*h)++;
232                 te= te->next;
233         }
234 }
235
236 static TreeElement *outliner_find_tree_element(ListBase *lb, int store_index)
237 {
238         TreeElement *te= lb->first, *tes;
239         while(te) {
240                 if(te->store_index==store_index) return te;
241                 tes= outliner_find_tree_element(&te->subtree, store_index);
242                 if(tes) return tes;
243                 te= te->next;
244         }
245         return NULL;
246 }
247
248
249
250 static ID *outliner_search_back(SpaceOops *soops, TreeElement *te, short idcode)
251 {
252         TreeStoreElem *tselem;
253         te= te->parent;
254         
255         while(te) {
256                 tselem= TREESTORE(te);
257                 if(te->idcode==idcode && tselem->type==0) return tselem->id;
258                 te= te->parent;
259         }
260         return NULL;
261 }
262
263 struct treesort {
264         TreeElement *te;
265         ID *id;
266         char *name;
267         short idcode;
268 };
269
270 static int treesort_alpha(const void *v1, const void *v2)
271 {
272         const struct treesort *x1= v1, *x2= v2;
273         int comp;
274         
275         /* first put objects last (hierarchy) */
276         comp= (x1->idcode==ID_OB);
277         if(x2->idcode==ID_OB) comp+=2;
278         
279         if(comp==1) return 1;
280         else if(comp==2) return -1;
281         else if(comp==3) {
282                 int comp= strcmp(x1->name, x2->name);
283                 
284                 if( comp>0 ) return 1;
285                 else if( comp<0) return -1;
286                 return 0;
287         }
288         return 0;
289 }
290
291 /* this is nice option for later? doesnt look too useful... */
292 #if 0
293 static int treesort_obtype_alpha(const void *v1, const void *v2)
294 {
295         const struct treesort *x1= v1, *x2= v2;
296         
297         /* first put objects last (hierarchy) */
298         if(x1->idcode==ID_OB && x2->idcode!=ID_OB) return 1;
299         else if(x2->idcode==ID_OB && x1->idcode!=ID_OB) return -1;
300         else {
301                 /* 2nd we check ob type */
302                 if(x1->idcode==ID_OB && x2->idcode==ID_OB) {
303                         if( ((Object *)x1->id)->type > ((Object *)x2->id)->type) return 1;
304                         else if( ((Object *)x1->id)->type > ((Object *)x2->id)->type) return -1;
305                         else return 0;
306                 }
307                 else {
308                         int comp= strcmp(x1->name, x2->name);
309                         
310                         if( comp>0 ) return 1;
311                         else if( comp<0) return -1;
312                         return 0;
313                 }
314         }
315 }
316 #endif
317
318 /* sort happens on each subtree individual */
319 static void outliner_sort(SpaceOops *soops, ListBase *lb)
320 {
321         TreeElement *te;
322         TreeStoreElem *tselem;
323         int totelem=0;
324         
325         te= lb->last;
326         if(te==NULL) return;
327         tselem= TREESTORE(te);
328         
329         /* sorting rules; only object lists or deformgroups */
330         if( (tselem->type==TSE_DEFGROUP) || (tselem->type==0 && te->idcode==ID_OB)) {
331                 
332                 /* count first */
333                 for(te= lb->first; te; te= te->next) totelem++;
334                 
335                 if(totelem>1) {
336                         struct treesort *tear= MEM_mallocN(totelem*sizeof(struct treesort), "tree sort array");
337                         struct treesort *tp=tear;
338                         
339                         for(te= lb->first; te; te= te->next, tp++) {
340                                 tselem= TREESTORE(te);
341                                 tp->te= te;
342                                 tp->name= te->name;
343                                 tp->idcode= te->idcode;
344                                 if(tselem->type && tselem->type!=TSE_DEFGROUP) tp->idcode= 0;   // dont sort this
345                                 tp->id= tselem->id;
346                         }
347                         
348                         qsort(tear, totelem, sizeof(struct treesort), treesort_alpha);
349                         
350                         lb->first=lb->last= NULL;
351                         tp= tear;
352                         while(totelem--) {
353                                 BLI_addtail(lb, tp->te);
354                                 tp++;
355                         }
356                         MEM_freeN(tear);
357                 }
358         }
359         
360         for(te= lb->first; te; te= te->next) {
361                 outliner_sort(soops, &te->subtree);
362         }
363 }
364
365 /* Prototype, see function below */
366 static void outliner_add_bone(SpaceOops *soops, ListBase *lb, 
367                                                           ID *id, Bone *curBone, TreeElement *parent, int *a);
368
369 static TreeElement *outliner_add_element(SpaceOops *soops, ListBase *lb, void *idv, 
370                                                                                  TreeElement *parent, short type, short index)
371 {
372         TreeElement *te;
373         TreeStoreElem *tselem;
374         ID *id= idv;
375         int a;
376         
377         if(id==NULL) return NULL;
378
379         te= MEM_callocN(sizeof(TreeElement), "tree elem");
380         /* add to the visual tree */
381         BLI_addtail(lb, te);
382         /* add to the storage */
383         check_persistant(soops, te, id, type, index);
384         tselem= TREESTORE(te);  
385         
386         te->parent= parent;
387         te->index= index;       // for data arays
388         te->name= id->name+2; // default, can be overridden by non-ID data
389         te->idcode= GS(id->name);
390         
391         if(type==0) {
392
393                 /* tuck pointer back in object, to construct hierarchy */
394                 if(GS(id->name)==ID_OB) id->newid= (ID *)te;
395                 
396                 /* expand specific data always */
397                 switch(GS(id->name)) {
398                 case ID_SCE:
399                         {
400                                 Scene *sce= (Scene *)id;
401                                 outliner_add_element(soops, &te->subtree, sce->world, te, 0, 0);
402                                 if(sce->scriptlink.scripts) {
403                                         TreeElement *tenla= outliner_add_element(soops, &te->subtree, sce, te, TSE_SCRIPT_BASE, 0);
404                                         int a= 0;
405                                         tenla->name= "Scripts";
406                                         for (a=0; a<sce->scriptlink.totscript; a++) {
407                                                 outliner_add_element(soops, &tenla->subtree, sce->scriptlink.scripts[a], tenla, 0, 0);
408                                         }
409                                 }
410                         }
411                         break;
412                 case ID_OB:
413                         {
414                                 Object *ob= (Object *)id;
415                                 
416                                 outliner_add_element(soops, &te->subtree, ob->data, te, 0, 0);
417                                 
418                                 if(ob->pose) {
419                                         bPoseChannel *pchan;
420                                         TreeElement *ten;
421                                         TreeElement *tenla= outliner_add_element(soops, &te->subtree, ob, te, TSE_POSE_BASE, 0);
422                                         
423                                         tenla->name= "Pose";
424                                         
425                                         if(ob!=G.obedit) {      // channels undefined in editmode, but we want the 'tenla' pose icon itself
426                                                 int a= 0;
427                                                 for(pchan= ob->pose->chanbase.first; pchan; pchan= pchan->next, a++) {
428                                                         ten= outliner_add_element(soops, &tenla->subtree, ob, tenla, TSE_POSE_CHANNEL, a);
429                                                         ten->name= pchan->name;
430                                                         ten->directdata= pchan;
431                                                         pchan->prev= (bPoseChannel *)ten;
432                                                         
433                                                         if(pchan->constraints.first) {
434                                                                 Object *target;
435                                                                 bConstraint *con;
436                                                                 TreeElement *ten1;
437                                                                 TreeElement *tenla1= outliner_add_element(soops, &ten->subtree, ob, ten, TSE_CONSTRAINT_BASE, 0);
438                                                                 int a= 0;
439                                                                 char *str;
440                                                                 
441                                                                 tenla1->name= "Constraints";
442                                                                 for(con= pchan->constraints.first; con; con= con->next, a++) {
443                                                                         ten1= outliner_add_element(soops, &tenla1->subtree, ob, tenla1, TSE_CONSTRAINT, a);
444                                                                         target= get_constraint_target(con, &str);
445                                                                         if(str && str[0]) ten1->name= str;
446                                                                         else if(target) ten1->name= target->id.name+2;
447                                                                         else ten1->name= con->name;
448                                                                         ten1->directdata= con;
449                                                                         /* possible add all other types links? */
450                                                                 }
451                                                         }
452                                                 }
453                                                 /* make hierarchy */
454                                                 ten= tenla->subtree.first;
455                                                 while(ten) {
456                                                         TreeElement *nten= ten->next, *par;
457                                                         tselem= TREESTORE(ten);
458                                                         if(tselem->type==TSE_POSE_CHANNEL) {
459                                                                 pchan= (bPoseChannel *)ten->directdata;
460                                                                 if(pchan->parent) {
461                                                                         BLI_remlink(&tenla->subtree, ten);
462                                                                         par= (TreeElement *)pchan->parent->prev;
463                                                                         BLI_addtail(&par->subtree, ten);
464                                                                 }
465                                                         }
466                                                         ten= nten;
467                                                 }
468                                                 /* restore prev pointers */
469                                                 pchan= ob->pose->chanbase.first;
470                                                 if(pchan) pchan->prev= NULL;
471                                                 for(; pchan; pchan= pchan->next) {
472                                                         if(pchan->next) pchan->next->prev= pchan;
473                                                 }
474                                         }
475                                 }
476                                 
477                                 outliner_add_element(soops, &te->subtree, ob->ipo, te, 0, 0);
478                                 outliner_add_element(soops, &te->subtree, ob->action, te, 0, 0);
479                                 
480                                 for(a=0; a<ob->totcol; a++) 
481                                         outliner_add_element(soops, &te->subtree, ob->mat[a], te, 0, a);
482                                 
483                                 if(ob->constraints.first) {
484                                         Object *target;
485                                         bConstraint *con;
486                                         TreeElement *ten;
487                                         TreeElement *tenla= outliner_add_element(soops, &te->subtree, ob, te, TSE_CONSTRAINT_BASE, 0);
488                                         int a= 0;
489                                         char *str;
490                                         
491                                         tenla->name= "Constraints";
492                                         for(con= ob->constraints.first; con; con= con->next, a++) {
493                                                 ten= outliner_add_element(soops, &tenla->subtree, ob, tenla, TSE_CONSTRAINT, a);
494                                                 target= get_constraint_target(con, &str);
495                                                 if(str && str[0]) ten->name= str;
496                                                 else if(target) ten->name= target->id.name+2;
497                                                 else ten->name= con->name;
498                                                 ten->directdata= con;
499                                                 /* possible add all other types links? */
500                                         }
501                                 }
502                                 
503                                 if(ob->modifiers.first) {
504                                         ModifierData *md;
505                                         TreeElement *temod = outliner_add_element(soops, &te->subtree, ob, te, TSE_MODIFIER_BASE, 0);
506                                         int index;
507
508                                         temod->name = "Modifiers";
509                                         for (index=0,md=ob->modifiers.first; md; index++,md=md->next) {
510                                                 TreeElement *te = outliner_add_element(soops, &temod->subtree, ob, temod, TSE_MODIFIER, index);
511                                                 te->name= md->name;
512
513                                                 if (md->type==eModifierType_Lattice) {
514                                                         outliner_add_element(soops, &te->subtree, ((LatticeModifierData*) md)->object, te, TSE_MODIFIER_OB, 0);
515                                                 } else if (md->type==eModifierType_Curve) {
516                                                         outliner_add_element(soops, &te->subtree, ((CurveModifierData*) md)->object, te, TSE_MODIFIER_OB, 0);
517                                                 } else if (md->type==eModifierType_Armature) {
518                                                         outliner_add_element(soops, &te->subtree, ((ArmatureModifierData*) md)->object, te, TSE_MODIFIER_OB, 0);
519                                                 } else if (md->type==eModifierType_Hook) {
520                                                         outliner_add_element(soops, &te->subtree, ((HookModifierData*) md)->object, te, TSE_MODIFIER_OB, 0);
521                                                 }
522                                         }
523                                 }
524                                 if(ob->defbase.first) {
525                                         bDeformGroup *defgroup;
526                                         TreeElement *ten;
527                                         TreeElement *tenla= outliner_add_element(soops, &te->subtree, ob, te, TSE_DEFGROUP_BASE, 0);
528                                         int a= 0;
529                                         
530                                         tenla->name= "Vertex Groups";
531                                         for (defgroup=ob->defbase.first; defgroup; defgroup=defgroup->next, a++) {
532                                                 ten= outliner_add_element(soops, &tenla->subtree, ob, tenla, TSE_DEFGROUP, a);
533                                                 ten->name= defgroup->name;
534                                                 ten->directdata= defgroup;
535                                         }
536                                 }
537                                 if(ob->scriptlink.scripts) {
538                                         TreeElement *tenla= outliner_add_element(soops, &te->subtree, ob, te, TSE_SCRIPT_BASE, 0);
539                                         int a= 0;
540                                         
541                                         tenla->name= "Scripts";
542                                         for (a=0; a<ob->scriptlink.totscript; a++) {                                                    /*  ** */
543                                                 outliner_add_element(soops, &tenla->subtree, ob->scriptlink.scripts[a], te, 0, 0);
544                                         }
545                                 }
546                                 if(ob->nlastrips.first) {
547                                         bActionStrip *strip;
548                                         TreeElement *ten;
549                                         TreeElement *tenla= outliner_add_element(soops, &te->subtree, ob, te, TSE_NLA, 0);
550                                         int a= 0;
551                                         
552                                         tenla->name= "NLA strips";
553                                         for (strip=ob->nlastrips.first; strip; strip=strip->next, a++) {
554                                                 ten= outliner_add_element(soops, &tenla->subtree, strip->act, tenla, TSE_NLA_ACTION, a);
555                                                 if(ten) ten->directdata= strip;
556                                         }
557                                 }
558                                 
559                         }
560                         break;
561                 case ID_ME:
562                         {
563                                 Mesh *me= (Mesh *)id;
564                                 outliner_add_element(soops, &te->subtree, me->ipo, te, 0, 0);
565                                 outliner_add_element(soops, &te->subtree, me->key, te, 0, 0);
566                                 for(a=0; a<me->totcol; a++) 
567                                         outliner_add_element(soops, &te->subtree, me->mat[a], te, 0, a);
568                                 /* could do tfaces with image links, but the images are not grouped nicely.
569                                    would require going over all tfaces, sort images in use. etc... */
570                         }
571                         break;
572                 case ID_CU:
573                         {
574                                 Curve *cu= (Curve *)id;
575                                 for(a=0; a<cu->totcol; a++) 
576                                         outliner_add_element(soops, &te->subtree, cu->mat[a], te, 0, a);
577                         }
578                         break;
579                 case ID_MB:
580                         {
581                                 MetaBall *mb= (MetaBall *)id;
582                                 for(a=0; a<mb->totcol; a++) 
583                                         outliner_add_element(soops, &te->subtree, mb->mat[a], te, 0, a);
584                         }
585                         break;
586                 case ID_MA:
587                 {
588                         Material *ma= (Material *)id;
589                         
590                         outliner_add_element(soops, &te->subtree, ma->ipo, te, 0, 0);
591                         for(a=0; a<MAX_MTEX; a++) {
592                                 if(ma->mtex[a]) outliner_add_element(soops, &te->subtree, ma->mtex[a]->tex, te, 0, a);
593                         }
594                 }
595                         break;
596                 case ID_TE:
597                         {
598                                 Tex *tex= (Tex *)id;
599                                 
600                                 outliner_add_element(soops, &te->subtree, tex->ipo, te, 0, 0);
601                                 outliner_add_element(soops, &te->subtree, tex->ima, te, 0, 0);
602                         }
603                         break;
604                 case ID_CA:
605                         {
606                                 Camera *ca= (Camera *)id;
607                                 outliner_add_element(soops, &te->subtree, ca->ipo, te, 0, 0);
608                         }
609                         break;
610                 case ID_LA:
611                         {
612                                 Lamp *la= (Lamp *)id;
613                                 outliner_add_element(soops, &te->subtree, la->ipo, te, 0, 0);
614                                 for(a=0; a<MAX_MTEX; a++) {
615                                         if(la->mtex[a]) outliner_add_element(soops, &te->subtree, la->mtex[a]->tex, te, 0, a);
616                                 }
617                         }
618                         break;
619                 case ID_WO:
620                         {
621                                 World *wrld= (World *)id;
622                                 outliner_add_element(soops, &te->subtree, wrld->ipo, te, 0, 0);
623                                 for(a=0; a<MAX_MTEX; a++) {
624                                         if(wrld->mtex[a]) outliner_add_element(soops, &te->subtree, wrld->mtex[a]->tex, te, 0, a);
625                                 }
626                         }
627                         break;
628                 case ID_KE:
629                         {
630                                 Key *key= (Key *)id;
631                                 outliner_add_element(soops, &te->subtree, key->ipo, te, 0, 0);
632                         }
633                         break;
634                 case ID_AC:
635                         {
636                                 bAction *act= (bAction *)id;
637                                 bActionChannel *chan;
638                                 int a= 0;
639                                 
640                                 tselem= TREESTORE(parent);
641                                 for (chan=act->chanbase.first; chan; chan=chan->next, a++) {
642                                         outliner_add_element(soops, &te->subtree, chan->ipo, te, 0, a);
643                                 }
644                         }
645                         break;
646                 case ID_AR:
647                         {
648                                 bArmature *arm= (bArmature *)id;
649                                 int a= 0;
650                                 
651                                 if(G.obedit && G.obedit->data==arm) {
652                                         EditBone *ebone;
653                                         TreeElement *ten;
654                                         
655                                         for (ebone = G.edbo.first; ebone; ebone=ebone->next, a++) {
656                                                 ten= outliner_add_element(soops, &te->subtree, id, te, TSE_EBONE, a);
657                                                 ten->directdata= ebone;
658                                                 ten->name= ebone->name;
659                                                 ebone->temp= ten;
660                                         }
661                                         /* make hierarchy */
662                                         ten= te->subtree.first;
663                                         while(ten) {
664                                                 TreeElement *nten= ten->next, *par;
665                                                 ebone= (EditBone *)ten->directdata;
666                                                 if(ebone->parent) {
667                                                         BLI_remlink(&te->subtree, ten);
668                                                         par= ebone->parent->temp;
669                                                         BLI_addtail(&par->subtree, ten);
670                                                 }
671                                                 ten= nten;
672                                         }
673                                 }
674                                 else {
675                                         Bone *curBone;
676                                         for (curBone=arm->bonebase.first; curBone; curBone=curBone->next){
677                                                 outliner_add_bone(soops, &te->subtree, id, curBone, te, &a);
678                                         }
679                                 }
680                         }
681                         break;
682                 }
683         }
684         return te;
685 }
686
687
688 /* special handling of hierarchical non-lib data */
689 static void outliner_add_bone(SpaceOops *soops, ListBase *lb, ID *id, Bone *curBone, 
690                                                           TreeElement *parent, int *a)
691 {
692         TreeElement *te= outliner_add_element(soops, lb, id, parent, TSE_BONE, *a);
693         
694         (*a)++;
695         te->name= curBone->name;
696         te->directdata= curBone;
697         
698         for(curBone= curBone->childbase.first; curBone; curBone=curBone->next) {
699                 outliner_add_bone(soops, &te->subtree, id, curBone, te, a);
700         }
701 }
702
703 static void outliner_make_hierarchy(SpaceOops *soops, ListBase *lb)
704 {
705         TreeElement *te, *ten, *tep;
706         TreeStoreElem *tselem;
707
708         /* build hierarchy */
709         te= lb->first;
710         while(te) {
711                 ten= te->next;
712                 tselem= TREESTORE(te);
713                 
714                 if(tselem->type==0 && te->idcode==ID_OB) {
715                         Object *ob= (Object *)tselem->id;
716                         if(ob->parent && ob->parent->id.newid) {
717                                 BLI_remlink(lb, te);
718                                 tep= (TreeElement *)ob->parent->id.newid;
719                                 BLI_addtail(&tep->subtree, te);
720                                 // set correct parent pointers
721                                 for(te=tep->subtree.first; te; te= te->next) te->parent= tep;
722                         }
723                 }
724                 te= ten;
725         }
726 }
727
728 static void outliner_build_tree(SpaceOops *soops)
729 {
730         Scene *sce;
731         Base *base;
732         Object *ob;
733         TreeElement *te, *ten;
734         TreeStoreElem *tselem;
735         
736         outliner_free_tree(&soops->tree);
737         outliner_storage_cleanup(soops);
738                                                    
739         /* clear ob id.new flags */
740         for(ob= G.main->object.first; ob; ob= ob->id.next) ob->id.newid= NULL;
741         
742         /* option 1: all scenes */
743         if(soops->outlinevis == SO_ALL_SCENES) {
744                 for(sce= G.main->scene.first; sce; sce= sce->id.next) {
745                         te= outliner_add_element(soops, &soops->tree, sce, NULL, 0, 0);
746                         tselem= TREESTORE(te);
747
748                         for(base= sce->base.first; base; base= base->next) {
749                                 ten= outliner_add_element(soops, &te->subtree, base->object, te, 0, 0);
750                                 ten->directdata= base;
751                         }
752                         outliner_make_hierarchy(soops, &te->subtree);
753                         /* clear id.newid, to prevent objects be inserted in wrong scenes (parent in other scene) */
754                         for(base= sce->base.first; base; base= base->next) base->object->id.newid= NULL;
755                 }
756         }
757         else if(soops->outlinevis == SO_CUR_SCENE) {
758                 outliner_add_element(soops, &soops->tree, G.scene->world, NULL, 0, 0);
759                 
760                 for(base= G.scene->base.first; base; base= base->next) {
761                         ten= outliner_add_element(soops, &soops->tree, base->object, NULL, 0, 0);
762                         ten->directdata= base;
763                 }
764                 outliner_make_hierarchy(soops, &soops->tree);
765         }
766         else if(soops->outlinevis == SO_VISIBLE) {
767                 for(base= G.scene->base.first; base; base= base->next) {
768                         if(base->lay & G.scene->lay)
769                                 outliner_add_element(soops, &soops->tree, base->object, NULL, 0, 0);
770                 }
771                 outliner_make_hierarchy(soops, &soops->tree);
772         }
773         else if(soops->outlinevis == SO_GROUPS) {
774                 Group *group;
775                 GroupObject *go;
776                 
777                 for(group= G.main->group.first; group; group= group->id.next) {
778                         te= outliner_add_element(soops, &soops->tree, group, NULL, 0, 0);
779                         tselem= TREESTORE(te);
780                         
781                         for(go= group->gobject.first; go; go= go->next) {
782                                 ten= outliner_add_element(soops, &te->subtree, go->ob, te, 0, 0);
783                                 ten->directdata= NULL;
784                         }
785                         outliner_make_hierarchy(soops, &te->subtree);
786                         /* clear id.newid, to prevent objects be inserted in wrong scenes (parent in other scene) */
787                         for(go= group->gobject.first; go; go= go->next) go->ob->id.newid= NULL;
788                 }
789         }
790         else if(soops->outlinevis == SO_SAME_TYPE) {
791                 Object *ob= OBACT;
792                 if(ob) {
793                         for(base= G.scene->base.first; base; base= base->next) {
794                                 if(base->object->type==ob->type) {
795                                         ten= outliner_add_element(soops, &soops->tree, base->object, NULL, 0, 0);
796                                         ten->directdata= base;
797                                 }
798                         }
799                         outliner_make_hierarchy(soops, &soops->tree);
800                 }
801         }
802         else if(soops->outlinevis == SO_SELECTED) {
803                 for(base= G.scene->base.first; base; base= base->next) {
804                         if(base->lay & G.scene->lay) {
805                                 if(base==BASACT || (base->flag & SELECT)) {
806                                         ten= outliner_add_element(soops, &soops->tree, base->object, NULL, 0, 0);
807                                         ten->directdata= base;
808                                 }
809                         }
810                 }
811                 outliner_make_hierarchy(soops, &soops->tree);
812         }
813         else {
814                 ten= outliner_add_element(soops, &soops->tree, OBACT, NULL, 0, 0);
815                 if(ten) ten->directdata= BASACT;
816         }
817         
818         outliner_sort(soops, &soops->tree);
819 }
820
821 /* **************** INTERACTIVE ************* */
822
823 static int outliner_count_levels(SpaceOops *soops, ListBase *lb, int curlevel)
824 {
825         TreeElement *te;
826         int level=curlevel, lev;
827         
828         for(te= lb->first; te; te= te->next) {
829                 
830                 lev= outliner_count_levels(soops, &te->subtree, curlevel+1);
831                 if(lev>level) level= lev;
832         }
833         return level;
834 }
835
836 static int outliner_has_one_flag(SpaceOops *soops, ListBase *lb, short flag, short curlevel)
837 {
838         TreeElement *te;
839         TreeStoreElem *tselem;
840         int level;
841         
842         for(te= lb->first; te; te= te->next) {
843                 tselem= TREESTORE(te);
844                 if(tselem->flag & flag) return curlevel;
845                 
846                 level= outliner_has_one_flag(soops, &te->subtree, flag, curlevel+1);
847                 if(level) return level;
848         }
849         return 0;
850 }
851
852 static void outliner_set_flag(SpaceOops *soops, ListBase *lb, short flag, short set)
853 {
854         TreeElement *te;
855         TreeStoreElem *tselem;
856         
857         for(te= lb->first; te; te= te->next) {
858                 tselem= TREESTORE(te);
859                 if(set==0) tselem->flag &= ~flag;
860                 else tselem->flag |= flag;
861                 outliner_set_flag(soops, &te->subtree, flag, set);
862         }
863 }
864
865 void outliner_toggle_visible(struct ScrArea *sa)
866 {
867         SpaceOops *soops= sa->spacedata.first;
868         
869         if( outliner_has_one_flag(soops, &soops->tree, TSE_CLOSED, 1))
870                 outliner_set_flag(soops, &soops->tree, TSE_CLOSED, 0);
871         else 
872                 outliner_set_flag(soops, &soops->tree, TSE_CLOSED, 1);
873
874         BIF_undo_push("Outliner toggle visible");
875         scrarea_queue_redraw(sa);
876 }
877
878 void outliner_toggle_selected(struct ScrArea *sa)
879 {
880         SpaceOops *soops= sa->spacedata.first;
881         
882         if( outliner_has_one_flag(soops, &soops->tree, TSE_SELECTED, 1))
883                 outliner_set_flag(soops, &soops->tree, TSE_SELECTED, 0);
884         else 
885                 outliner_set_flag(soops, &soops->tree, TSE_SELECTED, 1);
886         
887         BIF_undo_push("Outliner toggle selected");
888         scrarea_queue_redraw(sa);
889 }
890
891
892 static void outliner_openclose_level(SpaceOops *soops, ListBase *lb, int curlevel, int level, int open)
893 {
894         TreeElement *te;
895         TreeStoreElem *tselem;
896         
897         for(te= lb->first; te; te= te->next) {
898                 tselem= TREESTORE(te);
899                 
900                 if(open) {
901                         if(curlevel<=level) tselem->flag &= ~TSE_CLOSED;
902                 }
903                 else {
904                         if(curlevel>=level) tselem->flag |= TSE_CLOSED;
905                 }
906                 
907                 outliner_openclose_level(soops, &te->subtree, curlevel+1, level, open);
908         }
909 }
910
911 void outliner_one_level(struct ScrArea *sa, int add)
912 {
913         SpaceOops *soops= sa->spacedata.first;
914         int level;
915         
916         level= outliner_has_one_flag(soops, &soops->tree, TSE_CLOSED, 1);
917         if(add==1) {
918                 if(level) outliner_openclose_level(soops, &soops->tree, 1, level, 1);
919         }
920         else {
921                 if(level==0) level= outliner_count_levels(soops, &soops->tree, 0);
922                 if(level) outliner_openclose_level(soops, &soops->tree, 1, level-1, 0);
923         }
924         
925         BIF_undo_push("Outliner show/hide one level");
926         scrarea_queue_redraw(sa);
927 }
928
929 void outliner_page_up_down(ScrArea *sa, int up)
930 {
931         SpaceOops *soops= sa->spacedata.first;
932         int dy= soops->v2d.mask.ymax-soops->v2d.mask.ymin;
933         
934         if(up == -1) dy= -dy;
935         soops->v2d.cur.ymin+= dy;
936         soops->v2d.cur.ymax+= dy;
937         
938         scrarea_queue_redraw(sa);
939 }
940
941 /* **** do clicks on items ******* */
942
943 static void tree_element_active_object(SpaceOops *soops, TreeElement *te)
944 {
945         TreeStoreElem *tselem= TREESTORE(te);
946         Scene *sce;
947         Base *base;
948         Object *ob= NULL;
949         
950         /* if id is not object, we search back */
951         if(te->idcode==ID_OB) ob= (Object *)tselem->id;
952         else {
953                 ob= (Object *)outliner_search_back(soops, te, ID_OB);
954                 if(ob==OBACT) return;
955         }
956         if(ob==NULL) return;
957         
958         sce= (Scene *)outliner_search_back(soops, te, ID_SCE);
959         if(sce && G.scene != sce) {
960                 if(G.obedit) exit_editmode(2);
961                 set_scene(sce);
962         }
963         
964         /* find associated base in current scene */
965         for(base= FIRSTBASE; base; base= base->next) 
966                 if(base->object==ob) break;
967         if(base) {
968                 if(G.qual & LR_SHIFTKEY) {
969                         /* swap select */
970                         if(base->flag & SELECT) base->flag &= ~SELECT;
971                         else base->flag |= SELECT;
972                         base->object->flag= base->flag;
973                 }
974                 else {
975                         Base *b;
976                         /* deleselect all */
977                         for(b= FIRSTBASE; b; b= b->next) {
978                                 b->flag &= ~SELECT;
979                                 b->object->flag= b->flag;
980                         }
981                         base->flag |= SELECT;
982                         base->object->flag |= SELECT;
983                 }
984                 set_active_base(base);  /* editview.c */
985                 
986                 allqueue(REDRAWVIEW3D, 1);
987                 allqueue(REDRAWOOPS, 0);
988                 allqueue(REDRAWINFO, 1);
989         }
990         
991         if(ob!=G.obedit) exit_editmode(2);
992 }
993
994 static int tree_element_active_material(SpaceOops *soops, TreeElement *te, int set)
995 {
996         TreeElement *tes;
997         Object *ob;
998         
999         /* we search for the object parent */
1000         ob= (Object *)outliner_search_back(soops, te, ID_OB);
1001         if(ob==NULL || ob!=OBACT) return 0;     // just paranoia
1002         
1003         /* searching in ob mat array? */
1004         tes= te->parent;
1005         if(tes->idcode==ID_OB) {
1006                 if(set) {
1007                         ob->actcol= te->index+1;
1008                         ob->colbits |= (1<<te->index);  // make ob material active too
1009                 }
1010                 else {
1011                         if(ob->actcol == te->index+1) 
1012                                 if(ob->colbits & (1<<te->index)) return 1;
1013                 }
1014         }
1015         /* or we search for obdata material */
1016         else {
1017                 if(set) {
1018                         ob->actcol= te->index+1;
1019                         ob->colbits &= ~(1<<te->index); // make obdata material active too
1020                 }
1021                 else {
1022                         if(ob->actcol == te->index+1)
1023                                 if( (ob->colbits & (1<<te->index))==0 ) return 1;
1024                 }
1025         }
1026         if(set) {
1027                 extern_set_butspace(F5KEY);     // force shading buttons
1028                 BIF_all_preview_changed();
1029                 allqueue(REDRAWBUTSSHADING, 1);
1030                 allqueue(REDRAWOOPS, 0);
1031                 allqueue(REDRAWIPO, 0);
1032         }
1033         return 0;
1034 }
1035
1036 static int tree_element_active_texture(SpaceOops *soops, TreeElement *te, int set)
1037 {
1038         TreeElement *tep;
1039         TreeStoreElem *tselem, *tselemp;
1040         Object *ob=OBACT;
1041         ScrArea *sa;
1042         SpaceButs *sbuts=NULL;
1043         
1044         if(ob==NULL) return 0; // no active object
1045         
1046         tselem= TREESTORE(te);
1047         
1048         /* find buttons area (note, this is undefined really still, needs recode in blender) */
1049         sa= G.curscreen->areabase.first;
1050         while(sa) {
1051                 if(sa->spacetype==SPACE_BUTS) break;
1052                 sa= sa->next;
1053         }
1054         if(sa) sbuts= sa->spacedata.first;
1055         
1056         /* where is texture linked to? */
1057         tep= te->parent;
1058         tselemp= TREESTORE(tep);
1059         
1060         if(tep->idcode==ID_WO) {
1061                 World *wrld= (World *)tselemp->id;
1062
1063                 if(set) {
1064                         if(sbuts) {
1065                                 sbuts->tabo= TAB_SHADING_TEX;   // hack from header_buttonswin.c
1066                                 sbuts->texfrom= 1;
1067                         }
1068                         extern_set_butspace(F6KEY);     // force shading buttons texture
1069                         wrld->texact= te->index;
1070                 }
1071                 else if(tselemp->id == (ID *)(G.scene->world)) {
1072                         if(wrld->texact==te->index) return 1;
1073                 }
1074         }
1075         else if(tep->idcode==ID_LA) {
1076                 Lamp *la= (Lamp *)tselemp->id;
1077                 if(set) {
1078                         if(sbuts) {
1079                                 sbuts->tabo= TAB_SHADING_TEX;   // hack from header_buttonswin.c
1080                                 sbuts->texfrom= 2;
1081                         }
1082                         extern_set_butspace(F6KEY);     // force shading buttons texture
1083                         la->texact= te->index;
1084                 }
1085                 else {
1086                         if(tselemp->id == ob->data) {
1087                                 if(la->texact==te->index) return 1;
1088                         }
1089                 }
1090         }
1091         else if(tep->idcode==ID_MA) {
1092                 Material *ma= (Material *)tselemp->id;
1093                 if(set) {
1094                         if(sbuts) {
1095                                 //sbuts->tabo= TAB_SHADING_TEX; // hack from header_buttonswin.c
1096                                 sbuts->texfrom= 0;
1097                         }
1098                         extern_set_butspace(F6KEY);     // force shading buttons texture
1099                         ma->texact= te->index;
1100                         
1101                         /* also set active material */
1102                         ob->actcol= tep->index+1;
1103                 }
1104                 else if(tep->flag & TE_ACTIVE) {        // this is active material
1105                         if(ma->texact==te->index) return 1;
1106                 }
1107         }
1108         
1109         return 0;
1110 }
1111
1112
1113 static int tree_element_active_lamp(SpaceOops *soops, TreeElement *te, int set)
1114 {
1115         Object *ob;
1116         
1117         /* we search for the object parent */
1118         ob= (Object *)outliner_search_back(soops, te, ID_OB);
1119         if(ob==NULL || ob!=OBACT) return 0;     // just paranoia
1120         
1121         if(set) {
1122                 extern_set_butspace(F5KEY);
1123                 BIF_all_preview_changed();
1124                 allqueue(REDRAWBUTSSHADING, 1);
1125                 allqueue(REDRAWOOPS, 0);
1126                 allqueue(REDRAWIPO, 0);
1127         }
1128         else return 1;
1129         
1130         return 0;
1131 }
1132
1133 static int tree_element_active_world(SpaceOops *soops, TreeElement *te, int set)
1134 {
1135         TreeElement *tep;
1136         TreeStoreElem *tselem=NULL;
1137         Scene *sce=NULL;
1138         
1139         tep= te->parent;
1140         if(tep) {
1141                 tselem= TREESTORE(tep);
1142                 sce= (Scene *)tselem->id;
1143         }
1144         
1145         if(set) {       // make new scene active
1146                 if(sce && G.scene != sce) {
1147                         if(G.obedit) exit_editmode(2);
1148                         set_scene(sce);
1149                 }
1150         }
1151         
1152         if(tep==NULL || tselem->id == (ID *)G.scene) {
1153                 if(set) {
1154                         extern_set_butspace(F8KEY);
1155                 }
1156                 else {
1157                         return 1;
1158                 }
1159         }
1160         return 0;
1161 }
1162
1163 static int tree_element_active_ipo(SpaceOops *soops, TreeElement *te, int set)
1164 {
1165         TreeElement *tes;
1166         TreeStoreElem *tselems=NULL;
1167         Object *ob;
1168         
1169         /* we search for the object parent */
1170         ob= (Object *)outliner_search_back(soops, te, ID_OB);
1171         if(ob==NULL || ob!=OBACT) return 0;     // just paranoia
1172         
1173         /* the parent of ipo */
1174         tes= te->parent;
1175         tselems= TREESTORE(tes);
1176         
1177         if(set) {
1178                 if(tes->idcode==ID_AC) {
1179                         if(ob->ipoflag & OB_ACTION_OB)
1180                                 ob->ipowin= ID_OB;
1181                         else if(ob->ipoflag & OB_ACTION_KEY)
1182                                 ob->ipowin= ID_KE;
1183                         else 
1184                                 ob->ipowin= ID_PO;
1185                 }
1186                 else ob->ipowin= tes->idcode;
1187                 
1188                 if(ob->ipowin==ID_MA) tree_element_active_material(soops, tes, 1);
1189                 else if(ob->ipowin==ID_AC) {
1190                         bActionChannel *chan;
1191                         short a=0;
1192                         for(chan=ob->action->chanbase.first; chan; chan= chan->next) {
1193                                 if(a==te->index) break;
1194                                 if(chan->ipo) a++;
1195                         }
1196                         deselect_actionchannels(ob->action, 0);
1197                         select_channel(ob->action, chan, SELECT_ADD);
1198                         allqueue(REDRAWACTION, ob->ipowin);
1199                         allqueue(REDRAWVIEW3D, ob->ipowin);
1200                 }
1201                 
1202                 allqueue(REDRAWIPO, ob->ipowin);
1203         }
1204         else {
1205                 if(tes->idcode==ID_AC) {
1206                         if(ob->ipoflag & OB_ACTION_OB)
1207                                 return ob->ipowin==ID_OB;
1208                         else if(ob->ipoflag & OB_ACTION_KEY)
1209                                 return ob->ipowin==ID_KE;
1210                         else if(ob->ipowin==ID_AC) {
1211                                 bActionChannel *chan;
1212                                 short a=0;
1213                                 for(chan=ob->action->chanbase.first; chan; chan= chan->next) {
1214                                         if(a==te->index) break;
1215                                         if(chan->ipo) a++;
1216                                 }
1217                                 if(chan==get_hilighted_action_channel(ob->action)) return 1;
1218                         }
1219                 }
1220                 else if(ob->ipowin==tes->idcode) {
1221                         if(ob->ipowin==ID_MA) {
1222                                 Material *ma= give_current_material(ob, ob->actcol);
1223                                 if(ma==(Material *)tselems->id) return 1;
1224                         }
1225                         else return 1;
1226                 }
1227         }
1228         return 0;
1229 }       
1230
1231 static int tree_element_active_defgroup(TreeElement *te, TreeStoreElem *tselem, int set)
1232 {
1233         Object *ob;
1234         
1235         /* id in tselem is object */
1236         ob= (Object *)tselem->id;
1237         if(set) {
1238                 ob->actdef= te->index+1;
1239                 DAG_object_flush_update(G.scene, ob, OB_RECALC_DATA);
1240                 allqueue(REDRAWVIEW3D, ob->ipowin);
1241         }
1242         else {
1243                 if(ob==OBACT)
1244                         if(ob->actdef== te->index+1) return 1;
1245         }
1246         return 0;
1247 }
1248
1249 static int tree_element_active_nla_action(TreeElement *te, TreeStoreElem *tselem, int set)
1250 {
1251         if(set) {
1252                 bActionStrip *strip= te->directdata;
1253                 if(strip) {
1254                         deselect_nlachannel_keys(0);
1255                         strip->flag |= ACTSTRIP_SELECT;
1256                         allqueue(REDRAWNLA, 0);
1257                 }
1258         }
1259         else {
1260                 /* id in tselem is action */
1261                 bActionStrip *strip= te->directdata;
1262                 if(strip) {
1263                         if(strip->flag & ACTSTRIP_SELECT) return 1;
1264                 }
1265         }
1266         return 0;
1267 }
1268
1269 static int tree_element_active_posechannel(TreeElement *te, TreeStoreElem *tselem, int set)
1270 {
1271         Object *ob= (Object *)tselem->id;
1272         bPoseChannel *pchan= te->directdata;
1273         
1274         if(set) {
1275                 if(!(pchan->bone->flag & BONE_HIDDEN_P)) {
1276                         
1277                         if(G.qual & LR_SHIFTKEY) deselectall_posearmature(ob, 2);       // 2 = clear active tag
1278                         else deselectall_posearmature(ob, 0);   // 0 = deselect 
1279                         pchan->bone->flag |= BONE_SELECTED|BONE_ACTIVE;
1280                         
1281                         allqueue(REDRAWVIEW3D, 0);
1282                         allqueue(REDRAWOOPS, 0);
1283                         allqueue(REDRAWACTION, 0);
1284                 }
1285         }
1286         else {
1287                 if(ob==OBACT && ob->pose) {
1288                         if (pchan->bone->flag & BONE_SELECTED) return 1;
1289                 }
1290         }
1291         return 0;
1292 }
1293
1294 static int tree_element_active_bone(TreeElement *te, TreeStoreElem *tselem, int set)
1295 {
1296         bArmature *arm= (bArmature *)tselem->id;
1297         Bone *bone= te->directdata;
1298         
1299         if(set) {
1300                 if(!(bone->flag & BONE_HIDDEN_P)) {
1301                         if(G.qual & LR_SHIFTKEY) deselectall_posearmature(OBACT, 2);    // 2 is clear active tag
1302                         else deselectall_posearmature(OBACT, 0);
1303                         bone->flag |= BONE_SELECTED|BONE_ACTIVE;
1304                         
1305                         allqueue(REDRAWVIEW3D, 0);
1306                         allqueue(REDRAWOOPS, 0);
1307                         allqueue(REDRAWACTION, 0);
1308                 }
1309         }
1310         else {
1311                 Object *ob= OBACT;
1312                 
1313                 if(ob && ob->data==arm) {
1314                         if (bone->flag & BONE_SELECTED) return 1;
1315                 }
1316         }
1317         return 0;
1318 }
1319
1320
1321 /* ebones only draw in editmode armature */
1322 static int tree_element_active_ebone(TreeElement *te, TreeStoreElem *tselem, int set)
1323 {
1324         EditBone *ebone= te->directdata;
1325         
1326         if(set) {
1327                 if(!(ebone->flag & BONE_HIDDEN_A)) {
1328                         
1329                         if(G.qual & LR_SHIFTKEY) deselectall_armature(2);       // only clear active tag
1330                         else deselectall_armature(0);   // deselect
1331
1332                         ebone->flag |= BONE_SELECTED|BONE_ROOTSEL|BONE_TIPSEL|BONE_ACTIVE;
1333                         // flush to parent?
1334                         if(ebone->parent && (ebone->flag & BONE_CONNECTED)) ebone->parent->flag |= BONE_TIPSEL;
1335                         
1336                         allqueue(REDRAWVIEW3D, 0);
1337                         allqueue(REDRAWOOPS, 0);
1338                         allqueue(REDRAWACTION, 0);
1339                 }
1340         }
1341         else {
1342                 if (ebone->flag & BONE_SELECTED) return 1;
1343         }
1344         return 0;
1345 }
1346
1347 static int tree_element_active_modifier(TreeElement *te, TreeStoreElem *tselem, int set)
1348 {
1349         if(set) {
1350                 extern_set_butspace(F9KEY);
1351         }
1352         
1353         return 0;
1354 }
1355
1356 static int tree_element_active_text(SpaceOops *soops, TreeElement *te, int set)
1357 {
1358         ScrArea *sa=NULL;
1359         
1360         for(sa= G.curscreen->areabase.first; sa; sa= sa->next) {
1361                 if(sa->spacetype==SPACE_TEXT) break;
1362         }
1363         if(sa) {
1364                 SpaceText *st= sa->spacedata.first;
1365                 TreeStoreElem *tselem= TREESTORE(te);
1366                 
1367                 if(set) {
1368                         st->text= (Text *)tselem->id;
1369                         st->top= 0;
1370                         scrarea_queue_redraw(sa);
1371                 }
1372                 else if(st->text==(Text *)tselem->id) return 1;
1373         }
1374         return 0;
1375 }
1376
1377 /* generic call for ID data check or make/check active in UI */
1378 static int tree_element_active(SpaceOops *soops, TreeElement *te, int set)
1379 {
1380
1381         switch(te->idcode) {
1382                 case ID_MA:
1383                         return tree_element_active_material(soops, te, set);
1384                 case ID_WO:
1385                         return tree_element_active_world(soops, te, set);
1386                 case ID_LA:
1387                         return tree_element_active_lamp(soops, te, set);
1388                 case ID_IP:
1389                         return tree_element_active_ipo(soops, te, set);
1390                 case ID_TE:
1391                         return tree_element_active_texture(soops, te, set);
1392                 case ID_TXT:
1393                         return tree_element_active_text(soops, te, set);
1394         }
1395         return 0;
1396 }
1397
1398 static int tree_element_active_pose(TreeElement *te, TreeStoreElem *tselem, int set)
1399 {
1400         Object *ob= (Object *)tselem->id;
1401         
1402         if(set) {
1403                 if(G.obedit) exit_editmode(2);
1404                 if(ob->flag & OB_POSEMODE) exit_posemode();
1405                 else enter_posemode();
1406         }
1407         else {
1408                 if(ob->flag & OB_POSEMODE) return 1;
1409         }
1410         return 0;
1411 }
1412
1413 /* generic call for non-id data to make/check active in UI */
1414 static int tree_element_type_active(SpaceOops *soops, TreeElement *te, TreeStoreElem *tselem, int set)
1415 {
1416         
1417         switch(tselem->type) {
1418                 case TSE_NLA_ACTION:
1419                         return tree_element_active_nla_action(te, tselem, set);
1420                 case TSE_DEFGROUP:
1421                         return tree_element_active_defgroup(te, tselem, set);
1422                 case TSE_BONE:
1423                         return tree_element_active_bone(te, tselem, set);
1424                 case TSE_EBONE:
1425                         return tree_element_active_ebone(te, tselem, set);
1426                 case TSE_MODIFIER:
1427                         return tree_element_active_modifier(te, tselem, set);
1428                 case TSE_MODIFIER_OB:
1429                         if(set) tree_element_active_object(soops, te);
1430                         else if(tselem->id==(ID *)OBACT) return 1;
1431                         break;
1432                 case TSE_POSE_BASE:
1433                         return tree_element_active_pose(te, tselem, set);
1434                         break;
1435                 case TSE_POSE_CHANNEL:
1436                         return tree_element_active_posechannel(te, tselem, set);
1437                         break;
1438         }
1439         return 0;
1440 }
1441
1442
1443 static int do_outliner_mouse_event(SpaceOops *soops, TreeElement *te, short event, float *mval)
1444 {
1445         
1446         if(mval[1]>te->ys && mval[1]<te->ys+OL_H) {
1447                 TreeStoreElem *tselem= TREESTORE(te);
1448                 int openclose= 0;
1449
1450                 /* open close icon, three things to check */
1451                 if(event==RETKEY || event==PADENTER) openclose= 1; // enter opens/closes always
1452                 else if((te->flag & TE_ICONROW)==0) {                           // hidden icon, no open/close
1453                         if( mval[0]>te->xs && mval[0]<te->xs+OL_X) openclose= 1;
1454                 }
1455
1456                 if(openclose) {
1457                         
1458                         /* all below close/open? */
1459                         if( (G.qual & LR_SHIFTKEY) ) {
1460                                 tselem->flag &= ~TSE_CLOSED;
1461                                 outliner_set_flag(soops, &te->subtree, TSE_CLOSED, !outliner_has_one_flag(soops, &te->subtree, TSE_CLOSED, 1));
1462                         }
1463                         else {
1464                                 if(tselem->flag & TSE_CLOSED) tselem->flag &= ~TSE_CLOSED;
1465                                 else tselem->flag |= TSE_CLOSED;
1466                                 
1467                         }
1468                         
1469                         return 1;
1470                 }
1471                 /* name and first icon */
1472                 else if(mval[0]>te->xs && mval[0]<te->xend) {
1473                         
1474                         /* activate a name button? */
1475                         if(G.qual & LR_CTRLKEY) {
1476                                 if(ELEM5(tselem->type, TSE_NLA, TSE_DEFGROUP_BASE, TSE_CONSTRAINT_BASE, TSE_MODIFIER_BASE, TSE_SCRIPT_BASE)) 
1477                                         error("Cannot edit builtin name");
1478                                 else {
1479                                         tselem->flag |= TSE_TEXTBUT;
1480                                 }
1481                         }
1482                         else {
1483                                 /* always makes active object */
1484                                 tree_element_active_object(soops, te);
1485                                 
1486                                 if(tselem->type==0) { // the lib blocks
1487                                         /* editmode? */
1488                                         if(te->idcode==ID_SCE) {
1489                                                 if(G.scene!=(Scene *)tselem->id) {
1490                                                         if(G.obedit) exit_editmode(2);
1491                                                         set_scene((Scene *)tselem->id);
1492                                                 }
1493                                         }
1494                                         else if(ELEM5(te->idcode, ID_ME, ID_CU, ID_MB, ID_LT, ID_AR)) {
1495                                                 if(G.obedit) exit_editmode(2);
1496                                                 else {
1497                                                         enter_editmode();
1498                                                         extern_set_butspace(F9KEY);
1499                                                 }
1500                                         }
1501                                         else {  // rest of types
1502                                                 tree_element_active(soops, te, 1);
1503                                         }
1504                                         
1505                                 }
1506                                 else tree_element_type_active(soops, te, tselem, 1);
1507                         }
1508                         return 1;
1509                 }
1510         }
1511         
1512         for(te= te->subtree.first; te; te= te->next) {
1513                 if(do_outliner_mouse_event(soops, te, event, mval)) return 1;
1514         }
1515         return 0;
1516 }
1517
1518 /* event can enterkey, then it opens/closes */
1519 void outliner_mouse_event(ScrArea *sa, short event)
1520 {
1521         SpaceOops *soops= sa->spacedata.first;
1522         TreeElement *te;
1523         float fmval[2];
1524         short mval[2];
1525         
1526         getmouseco_areawin(mval);
1527         areamouseco_to_ipoco(&soops->v2d, mval, fmval, fmval+1);
1528         
1529         for(te= soops->tree.first; te; te= te->next) {
1530                 if(do_outliner_mouse_event(soops, te, event, fmval)) break;
1531         }
1532         
1533         if(te) {
1534                 BIF_undo_push("Outliner click event");
1535                 allqueue(REDRAWOOPS, 0);
1536         }
1537         else 
1538                 outliner_select(sa);
1539
1540 }
1541
1542 static TreeElement *outliner_find_id(SpaceOops *soops, ListBase *lb, ID *id)
1543 {
1544         TreeElement *te, *tes;
1545         TreeStoreElem *tselem;
1546         
1547         for(te= lb->first; te; te= te->next) {
1548                 tselem= TREESTORE(te);
1549                 if(tselem->id==id) return te;
1550                 /* only deeper on scene or object */
1551                 if( te->idcode==ID_OB || te->idcode==ID_SCE) { 
1552                         tes= outliner_find_id(soops, &te->subtree, id);
1553                         if(tes) return tes;
1554                 }
1555         }
1556         return NULL;
1557 }
1558
1559 void outliner_show_active(struct ScrArea *sa)
1560 {
1561         SpaceOops *so= sa->spacedata.first;
1562         TreeElement *te;
1563         int ytop;
1564         
1565         if(OBACT == NULL) return;
1566         
1567         te= outliner_find_id(so, &so->tree, (ID *)OBACT);
1568         if(te) {
1569                 /* make te->ys center of view */
1570                 ytop= te->ys + (so->v2d.mask.ymax-so->v2d.mask.ymin)/2;
1571                 if(ytop>0) ytop= 0;
1572                 so->v2d.cur.ymax= ytop;
1573                 so->v2d.cur.ymin= ytop-(so->v2d.mask.ymax-so->v2d.mask.ymin);
1574                 scrarea_queue_redraw(sa);
1575         }
1576 }
1577
1578 static int subtree_has_objects(SpaceOops *soops, ListBase *lb)
1579 {
1580         TreeElement *te;
1581         TreeStoreElem *tselem;
1582         
1583         for(te= lb->first; te; te= te->next) {
1584                 tselem= TREESTORE(te);
1585                 if(tselem->type==0 && te->idcode==ID_OB) return 1;
1586                 if( subtree_has_objects(soops, &te->subtree)) return 1;
1587         }
1588         return 0;
1589 }
1590
1591 static void tree_element_show_hierarchy(SpaceOops *soops, ListBase *lb)
1592 {
1593         TreeElement *te;
1594         TreeStoreElem *tselem;
1595
1596         /* open all object elems, close others */
1597         for(te= lb->first; te; te= te->next) {
1598                 tselem= TREESTORE(te);
1599                 
1600                 if(tselem->type==0) {
1601                         if(te->idcode==ID_SCE) {
1602                                 if(tselem->id!=(ID *)G.scene) tselem->flag |= TSE_CLOSED;
1603                                         else tselem->flag &= ~TSE_CLOSED;
1604                         }
1605                         else if(te->idcode==ID_OB) {
1606                                 if(subtree_has_objects(soops, &te->subtree)) tselem->flag &= ~TSE_CLOSED;
1607                                 else tselem->flag |= TSE_CLOSED;
1608                         }
1609                 }
1610                 else tselem->flag |= TSE_CLOSED;
1611
1612                 if(tselem->flag & TSE_CLOSED); else tree_element_show_hierarchy(soops, &te->subtree);
1613         }
1614         
1615 }
1616
1617 /* show entire object level hierarchy */
1618 void outliner_show_hierarchy(struct ScrArea *sa)
1619 {
1620         SpaceOops *so= sa->spacedata.first;
1621         
1622         tree_element_show_hierarchy(so, &so->tree);
1623         scrarea_queue_redraw(sa);
1624         
1625         BIF_undo_push("Outliner show hierarchy");
1626 }
1627
1628 static void do_outliner_select(SpaceOops *soops, ListBase *lb, float y1, float y2, short *selecting)
1629 {
1630         TreeElement *te;
1631         TreeStoreElem *tselem;
1632         
1633         if(y1>y2) SWAP(float, y1, y2);
1634         
1635         for(te= lb->first; te; te= te->next) {
1636                 tselem= TREESTORE(te);
1637                 
1638                 if(te->ys + OL_H < y1) return;
1639                 if(te->ys < y2) {
1640                         if((te->flag & TE_ICONROW)==0) {
1641                                 if(*selecting == -1) {
1642                                         if( tselem->flag & TSE_SELECTED) *selecting= 0;
1643                                         else *selecting= 1;
1644                                 }
1645                                 if(*selecting) tselem->flag |= TSE_SELECTED;
1646                                 else tselem->flag &= ~TSE_SELECTED;
1647                         }
1648                 }
1649                 if((tselem->flag & TSE_CLOSED)==0) do_outliner_select(soops, &te->subtree, y1, y2, selecting);
1650         }
1651 }
1652
1653 /* its own redraw loop... urm */
1654 void outliner_select(struct ScrArea *sa )
1655 {
1656         SpaceOops *so= sa->spacedata.first;
1657         float fmval[2], y1, y2;
1658         short mval[2], yo=-1, selecting= -1;
1659         
1660         getmouseco_areawin(mval);
1661         areamouseco_to_ipoco(&so->v2d, mval, fmval, fmval+1);
1662         y1= fmval[1];
1663
1664         while (get_mbut() & (L_MOUSE|R_MOUSE)) {
1665                 getmouseco_areawin(mval);
1666                 areamouseco_to_ipoco(&so->v2d, mval, fmval, fmval+1);
1667                 y2= fmval[1];
1668                 if(yo!=mval[1]) {
1669                         do_outliner_select(so, &so->tree, y1, y2, &selecting);
1670                         yo= mval[1];
1671                         scrarea_do_windraw(sa);
1672                         screen_swapbuffers();
1673                 
1674                         y1= y2;
1675                 }
1676                 else PIL_sleep_ms(30);
1677         }
1678         
1679         BIF_undo_push("Outliner selection");
1680
1681 }
1682
1683 /* ************ SELECTION OPERATIONS ********* */
1684
1685 static int scenelevel=0, objectlevel=0, idlevel=0, datalevel=0; // globals, euh... you can do better
1686
1687 static void set_operation_types(SpaceOops *soops, ListBase *lb)
1688 {
1689         TreeElement *te;
1690         TreeStoreElem *tselem;
1691         
1692         for(te= lb->first; te; te= te->next) {
1693                 tselem= TREESTORE(te);
1694                 if(tselem->flag & TSE_SELECTED) {
1695                         if(tselem->type) {
1696                                 if(datalevel==0) datalevel= tselem->type;
1697                                 else if(datalevel!=tselem->type) datalevel= -1;
1698                         }
1699                         else {
1700                                 int idcode= GS(tselem->id->name);
1701                                 switch(idcode) {
1702                                         case ID_SCE:
1703                                                 scenelevel= 1;
1704                                                 break;
1705                                         case ID_OB:
1706                                                 objectlevel= 1;
1707                                                 break;
1708                                                 
1709                                         case ID_ME: case ID_CU: case ID_MB: case ID_LT:
1710                                         case ID_LA: case ID_AR: case ID_CA:
1711                                                 idlevel= -2;
1712                                                 break;
1713                                                 
1714                                         case ID_MA: case ID_TE: case ID_IP: case ID_IM:
1715                                         case ID_SO: case ID_KE: case ID_WO: case ID_AC:
1716                                         case ID_NLA: case ID_TXT:
1717                                                 if(idlevel==0) idlevel= idcode;
1718                                                 else if(idlevel!=idcode) idlevel= -1;
1719                                                         break;
1720                                 }
1721                         }
1722                 }
1723                 if((tselem->flag & TSE_CLOSED)==0) set_operation_types(soops, &te->subtree);
1724         }
1725 }
1726
1727 static void unlink_material_cb(TreeElement *te, TreeStoreElem *tsep)
1728 {
1729         Material **matar=NULL;
1730         int a, totcol=0;
1731         
1732         if( GS(tsep->id->name)==ID_OB) {
1733                 Object *ob= (Object *)tsep->id;
1734                 totcol= ob->totcol;
1735                 matar= ob->mat;
1736         }
1737         else if( GS(tsep->id->name)==ID_ME) {
1738                 Mesh *me= (Mesh *)tsep->id;
1739                 totcol= me->totcol;
1740                 matar= me->mat;
1741         }
1742         else if( GS(tsep->id->name)==ID_CU) {
1743                 Curve *cu= (Curve *)tsep->id;
1744                 totcol= cu->totcol;
1745                 matar= cu->mat;
1746         }
1747         else if( GS(tsep->id->name)==ID_MB) {
1748                 MetaBall *mb= (MetaBall *)tsep->id;
1749                 totcol= mb->totcol;
1750                 matar= mb->mat;
1751         }
1752
1753         for(a=0; a<totcol; a++) {
1754                 if(a==te->index && matar[a]) {
1755                         matar[a]->id.us--;
1756                         matar[a]= NULL;
1757                 }
1758         }
1759 }
1760
1761 static void unlink_texture_cb(TreeElement *te, TreeStoreElem *tsep)
1762 {
1763         MTex **mtex= NULL;
1764         int a;
1765         
1766         if( GS(tsep->id->name)==ID_MA) {
1767                 Material *ma= (Material *)tsep->id;
1768                 mtex= ma->mtex;
1769         }
1770         else if( GS(tsep->id->name)==ID_LA) {
1771                 Lamp *la= (Lamp *)tsep->id;
1772                 mtex= la->mtex;
1773         }
1774         else if( GS(tsep->id->name)==ID_WO) {
1775                 World *wrld= (World *)tsep->id;
1776                 mtex= wrld->mtex;
1777         }
1778         else return;
1779         
1780         for(a=0; a<MAX_MTEX; a++) {
1781                 if(a==te->index && mtex[a]) {
1782                         if(mtex[a]->tex) {
1783                                 mtex[a]->tex->id.us--;
1784                                 mtex[a]->tex= NULL;
1785                         }
1786                 }
1787         }
1788 }
1789
1790 static void outliner_do_libdata_operation(SpaceOops *soops, ListBase *lb, 
1791                                                                                  void (*operation_cb)(TreeElement *, TreeStoreElem *))
1792 {
1793         TreeElement *te;
1794         TreeStoreElem *tselem;
1795         
1796         for(te=lb->first; te; te= te->next) {
1797                 tselem= TREESTORE(te);
1798                 if(tselem->flag & TSE_SELECTED) {
1799                         if(tselem->type==0) {
1800                                 TreeStoreElem *tsep= TREESTORE(te->parent);
1801                                 operation_cb(te, tsep);
1802                         }
1803                 }
1804                 if((tselem->flag & TSE_CLOSED)==0) {
1805                         outliner_do_libdata_operation(soops, &te->subtree, operation_cb);
1806                 }
1807         }
1808 }
1809
1810 /* */
1811
1812 static void object_select_cb(TreeElement *te, TreeStoreElem *tselem)
1813 {
1814         Base *base= (Base *)te->directdata;
1815         
1816         if(base==NULL) base= object_in_scene((Object *)tselem->id, G.scene);
1817         if(base) {
1818                 base->flag |= SELECT;
1819                 base->object->flag |= SELECT;
1820         }
1821 }
1822
1823 static void object_deselect_cb(TreeElement *te, TreeStoreElem *tselem)
1824 {
1825         Base *base= (Base *)te->directdata;
1826         
1827         if(base==NULL) base= object_in_scene((Object *)tselem->id, G.scene);
1828         if(base) {
1829                 base->flag &= ~SELECT;
1830                 base->object->flag &= ~SELECT;
1831         }
1832 }
1833
1834 static void object_delete_cb(TreeElement *te, TreeStoreElem *tselem)
1835 {
1836         Base *base= (Base *)te->directdata;
1837         
1838         if(base==NULL) base= object_in_scene((Object *)tselem->id, G.scene);
1839         if(base) {
1840                 // check also library later
1841                 if(G.obedit==base->object) exit_editmode(2);
1842                 
1843                 if(base==BASACT) {
1844                         G.f &= ~(G_VERTEXPAINT+G_FACESELECT+G_TEXTUREPAINT+G_WEIGHTPAINT);
1845                         setcursor_space(SPACE_VIEW3D, CURSOR_STD);
1846                 }
1847                 
1848                 free_and_unlink_base(base);
1849                 te->directdata= NULL;
1850                 tselem->id= NULL;
1851         }
1852 }
1853
1854
1855 static void outliner_do_object_operation(SpaceOops *soops, ListBase *lb, 
1856                                                                                  void (*operation_cb)(TreeElement *, TreeStoreElem *))
1857 {
1858         TreeElement *te;
1859         TreeStoreElem *tselem;
1860         
1861         for(te=lb->first; te; te= te->next) {
1862                 tselem= TREESTORE(te);
1863                 if(tselem->flag & TSE_SELECTED) {
1864                         if(tselem->type==0 && te->idcode==ID_OB) {
1865                                 // when objects selected in other scenes... dunno if that should be allowed
1866                                 Scene *sce= (Scene *)outliner_search_back(soops, te, ID_SCE);
1867                                 if(sce && G.scene != sce) set_scene(sce);
1868                                 
1869                                 operation_cb(te, tselem);
1870                         }
1871                 }
1872                 if((tselem->flag & TSE_CLOSED)==0) {
1873                         outliner_do_object_operation(soops, &te->subtree, operation_cb);
1874                 }
1875         }
1876 }
1877
1878 static void pchan_cb(int event, TreeElement *te, TreeStoreElem *tselem)
1879 {
1880         bPoseChannel *pchan= (bPoseChannel *)te->directdata;
1881         
1882         if(event==1)
1883                 pchan->bone->flag |= BONE_SELECTED;
1884         else if(event==2)
1885                 pchan->bone->flag &= ~BONE_SELECTED;
1886         else if(event==3) {
1887                 pchan->bone->flag |= BONE_HIDDEN_P;
1888                 pchan->bone->flag &= ~BONE_SELECTED;
1889         }
1890         else if(event==4)
1891                 pchan->bone->flag &= ~BONE_HIDDEN_P;
1892 }
1893
1894 static void bone_cb(int event, TreeElement *te, TreeStoreElem *tselem)
1895 {
1896         Bone *bone= (Bone *)te->directdata;
1897         
1898         if(event==1)
1899                 bone->flag |= BONE_SELECTED;
1900         else if(event==2)
1901                 bone->flag &= ~BONE_SELECTED;
1902         else if(event==3) {
1903                 bone->flag |= BONE_HIDDEN_P;
1904                 bone->flag &= ~BONE_SELECTED;
1905         }
1906         else if(event==4)
1907                 bone->flag &= ~BONE_HIDDEN_P;
1908 }
1909
1910 static void ebone_cb(int event, TreeElement *te, TreeStoreElem *tselem)
1911 {
1912         EditBone *ebone= (EditBone *)te->directdata;
1913         
1914         if(event==1)
1915                 ebone->flag |= BONE_SELECTED;
1916         else if(event==2)
1917                 ebone->flag &= ~BONE_SELECTED;
1918         else if(event==3) {
1919                 ebone->flag |= BONE_HIDDEN_A;
1920                 ebone->flag &= ~BONE_SELECTED|BONE_TIPSEL|BONE_ROOTSEL;
1921         }
1922         else if(event==4)
1923                 ebone->flag &= ~BONE_HIDDEN_A;
1924 }
1925
1926 static void outliner_do_data_operation(SpaceOops *soops, int type, int event, ListBase *lb, 
1927                                                                                  void (*operation_cb)(int, TreeElement *, TreeStoreElem *))
1928 {
1929         TreeElement *te;
1930         TreeStoreElem *tselem;
1931         
1932         for(te=lb->first; te; te= te->next) {
1933                 tselem= TREESTORE(te);
1934                 if(tselem->flag & TSE_SELECTED) {
1935                         if(tselem->type==type) {
1936                                 operation_cb(event, te, tselem);
1937                         }
1938                 }
1939                 if((tselem->flag & TSE_CLOSED)==0) {
1940                         outliner_do_data_operation(soops, type, event, &te->subtree, operation_cb);
1941                 }
1942         }
1943 }
1944
1945
1946 void outliner_operation_menu(ScrArea *sa)
1947 {
1948         SpaceOops *soops= sa->spacedata.first;
1949         
1950         // globals
1951         scenelevel= objectlevel= idlevel= datalevel=0;
1952         
1953         set_operation_types(soops, &soops->tree);
1954         
1955         if(scenelevel) {
1956                 if(objectlevel || datalevel || idlevel) error("Mixed selection");
1957                 //else pupmenu("Scene Operations%t|Delete");
1958         }
1959         else if(objectlevel) {
1960                 short event= pupmenu("Object Operations%t|Select%x1|Deselect%x2|Delete%x4");
1961                 if(event>0) {
1962                         char *str="";
1963                         
1964                         if(event==1) {
1965                                 Scene *sce= G.scene;    // to be able to delete, scenes are set...
1966                                 outliner_do_object_operation(soops, &soops->tree, object_select_cb);
1967                                 if(G.scene != sce) set_scene(sce);
1968                                 
1969                                 str= "Select Objects";
1970                         }
1971                         else if(event==2) {
1972                                 outliner_do_object_operation(soops, &soops->tree, object_deselect_cb);
1973                                 str= "Deselect Objects";
1974                         }
1975                         else if(event==4) {
1976                                 outliner_do_object_operation(soops, &soops->tree, object_delete_cb);
1977                                 DAG_scene_sort(G.scene);
1978                                 str= "Delete Objects";
1979                         }
1980                         
1981                         countall();
1982                         
1983                         BIF_undo_push(str);
1984                         allqueue(REDRAWALL, 0); // yah... to be sure :)
1985                 }
1986         }
1987         else if(idlevel) {
1988                 if(idlevel==-1 || datalevel) error("Mixed selection");
1989                 else if(idlevel==-2) error("No operations available");
1990                 else {
1991                         short event= pupmenu("Data Operations%t|Unlink");
1992                         
1993                         if(event==1) {
1994                                 switch(idlevel) {
1995                                         case ID_MA:
1996                                                 outliner_do_libdata_operation(soops, &soops->tree, unlink_material_cb);
1997                                                 BIF_undo_push("Unlink material");
1998                                                 allqueue(REDRAWBUTSSHADING, 1);
1999                                                 break;
2000                                         case ID_TE:
2001                                                 outliner_do_libdata_operation(soops, &soops->tree, unlink_texture_cb);
2002                                                 allqueue(REDRAWBUTSSHADING, 1);
2003                                                 BIF_undo_push("Unlink texture");
2004                                                 break;
2005                                         default:
2006                                                 error("Not yet...");
2007                                 }
2008                                 allqueue(REDRAWOOPS, 0);
2009                                 allqueue(REDRAWBUTSALL, 0);
2010                                 allqueue(REDRAWVIEW3D, 0);
2011                         }
2012                 }
2013         }
2014         else if(datalevel) {
2015                 if(datalevel==-1) error("Mixed selection");
2016                 else {
2017                         if(datalevel==TSE_POSE_CHANNEL) {
2018                                 short event= pupmenu("PoseChannel Operations%t|Select%x1|Deselect%x2|Hide%x3|Unhide%x4");
2019                                 if(event>0) {
2020                                         outliner_do_data_operation(soops, datalevel, event, &soops->tree, pchan_cb);
2021                                         BIF_undo_push("PoseChannel operation");
2022                                 }
2023                         }
2024                         else if(datalevel==TSE_BONE) {
2025                                 short event= pupmenu("Bone Operations%t|Select%x1|Deselect%x2|Hide%x3|Unhide%x4");
2026                                 if(event>0) {
2027                                         outliner_do_data_operation(soops, datalevel, event, &soops->tree, bone_cb);
2028                                         BIF_undo_push("Bone operation");
2029                                 }
2030                         }
2031                         else if(datalevel==TSE_EBONE) {
2032                                 short event= pupmenu("EditBone Operations%t|Select%x1|Deselect%x2|Hide%x3|Unhide%x4");
2033                                 if(event>0) {
2034                                         outliner_do_data_operation(soops, datalevel, event, &soops->tree, ebone_cb);
2035                                         BIF_undo_push("EditBone operation");
2036                                 }
2037                         }
2038                         
2039                         allqueue(REDRAWOOPS, 0);
2040                         allqueue(REDRAWBUTSALL, 0);
2041                         allqueue(REDRAWVIEW3D, 0);
2042                 }
2043         }
2044 }
2045
2046
2047 /* ***************** DRAW *************** */
2048
2049 static void tselem_draw_icon(float x, float y, TreeStoreElem *tselem, TreeElement *te)
2050 {
2051         if(tselem->type) {
2052                 switch( tselem->type) {
2053                         case TSE_NLA:
2054                                 BIF_draw_icon(x, y, ICON_NLA); break;
2055                         case TSE_NLA_ACTION:
2056                                 BIF_draw_icon(x, y, ICON_ACTION); break;
2057                         case TSE_DEFGROUP_BASE:
2058                                 BIF_draw_icon(x, y, ICON_VERTEXSEL); break;
2059                         case TSE_BONE:
2060                         case TSE_EBONE:
2061                                 BIF_draw_icon(x, y, ICON_WPAINT_DEHLT); break;
2062                         case TSE_CONSTRAINT_BASE:
2063                                 BIF_draw_icon(x, y, ICON_CONSTRAINT); break;
2064                         case TSE_MODIFIER_BASE:
2065                                 BIF_draw_icon(x, y, ICON_MODIFIER); break;
2066                         case TSE_MODIFIER_OB:
2067                                 BIF_draw_icon(x, y, ICON_OBJECT); break;
2068                         case TSE_MODIFIER:
2069                         {
2070                                 Object *ob= (Object *)tselem->id;
2071                                 ModifierData *md= BLI_findlink(&ob->modifiers, tselem->nr);
2072                                 switch(md->type) {
2073                                         case eModifierType_Subsurf: 
2074                                                 BIF_draw_icon(x, y, ICON_MOD_SUBSURF); break;
2075                                         case eModifierType_Armature: 
2076                                                 BIF_draw_icon(x, y, ICON_ARMATURE); break;
2077                                         case eModifierType_Lattice: 
2078                                                 BIF_draw_icon(x, y, ICON_LATTICE); break;
2079                                         case eModifierType_Curve: 
2080                                                 BIF_draw_icon(x, y, ICON_CURVE); break;
2081                                         case eModifierType_Build: 
2082                                                 BIF_draw_icon(x, y, ICON_MOD_BUILD); break;
2083                                         case eModifierType_Mirror: 
2084                                                 BIF_draw_icon(x, y, ICON_MOD_MIRROR); break;
2085                                         case eModifierType_Decimate: 
2086                                                 BIF_draw_icon(x, y, ICON_MOD_DECIM); break;
2087                                         case eModifierType_Wave: 
2088                                                 BIF_draw_icon(x, y, ICON_MOD_WAVE); break;
2089                                         case eModifierType_Hook: 
2090                                                 BIF_draw_icon(x, y, ICON_HOOK); break;
2091                                         case eModifierType_Softbody: 
2092                                                 BIF_draw_icon(x, y, ICON_MOD_SOFT); break;
2093                                         case eModifierType_Boolean: 
2094                                                 BIF_draw_icon(x, y, ICON_MOD_BOOLEAN); break;
2095                                         default:
2096                                                 BIF_draw_icon(x, y, ICON_DOT); break;
2097                                 }
2098                                 break;
2099                         }
2100                         case TSE_SCRIPT_BASE:
2101                                 BIF_draw_icon(x, y, ICON_TEXT); break;
2102                         case TSE_POSE_BASE:
2103                                 BIF_draw_icon(x, y, ICON_ARMATURE_DEHLT); break;
2104                         case TSE_POSE_CHANNEL:
2105                                 BIF_draw_icon(x, y, ICON_WPAINT_DEHLT); break;
2106                         default:
2107                                 BIF_draw_icon(x, y, ICON_DOT); break;
2108                 }
2109         }
2110         else {
2111                 switch( GS(tselem->id->name)) {
2112                         case ID_SCE:
2113                                 BIF_draw_icon(x, y, ICON_SCENE_DEHLT); break;
2114                         case ID_OB:
2115                                 BIF_draw_icon(x, y, ICON_OBJECT); break;
2116                         case ID_ME:
2117                                 BIF_draw_icon(x, y, ICON_MESH); break;
2118                         case ID_CU:
2119                                 BIF_draw_icon(x, y, ICON_CURVE); break;
2120                         case ID_MB:
2121                                 BIF_draw_icon(x, y, ICON_MBALL); break;
2122                         case ID_LT:
2123                                 BIF_draw_icon(x, y, ICON_LATTICE); break;
2124                         case ID_LA:
2125                                 BIF_draw_icon(x, y, ICON_LAMP_DEHLT); break;
2126                         case ID_MA:
2127                                 BIF_draw_icon(x, y, ICON_MATERIAL_DEHLT); break;
2128                         case ID_TE:
2129                                 BIF_draw_icon(x, y, ICON_TEXTURE_DEHLT); break;
2130                         case ID_IP:
2131                                 BIF_draw_icon(x, y, ICON_IPO_DEHLT); break;
2132                         case ID_IM:
2133                                 BIF_draw_icon(x, y, ICON_IMAGE_DEHLT); break;
2134                         case ID_SO:
2135                                 BIF_draw_icon(x, y, ICON_SPEAKER); break;
2136                         case ID_AR:
2137                                 BIF_draw_icon(x, y, ICON_ARMATURE); break;
2138                         case ID_CA:
2139                                 BIF_draw_icon(x, y, ICON_CAMERA_DEHLT); break;
2140                         case ID_KE:
2141                                 BIF_draw_icon(x, y, ICON_EDIT_DEHLT); break;
2142                         case ID_WO:
2143                                 BIF_draw_icon(x, y, ICON_WORLD_DEHLT); break;
2144                         case ID_AC:
2145                                 BIF_draw_icon(x, y, ICON_ACTION); break;
2146                         case ID_NLA:
2147                                 BIF_draw_icon(x, y, ICON_NLA); break;
2148                         case ID_TXT:
2149                                 BIF_draw_icon(x, y, ICON_SCRIPT); break;
2150                         case ID_GR:
2151                                 BIF_draw_icon(x, y, ICON_CIRCLE_DEHLT); break;
2152                 }
2153         }
2154 }
2155
2156 static void outliner_draw_iconrow(SpaceOops *soops, ListBase *lb, int level, int *offsx, int ys)
2157 {
2158         TreeElement *te;
2159         TreeStoreElem *tselem;
2160         int active;
2161
2162         for(te= lb->first; te; te= te->next) {
2163                 tselem= TREESTORE(te);
2164                 
2165                 /* object hierarchy always, further constrained on level */
2166                 if(level<1 || (tselem->type==0 && te->idcode==ID_OB)) {
2167
2168                         /* active blocks get white circle */
2169                         active= 0;
2170                         if(tselem->type==0) {
2171                                 if(te->idcode==ID_OB) active= (OBACT==(Object *)tselem->id);
2172                                 else if(G.obedit && G.obedit->data==tselem->id) active= 1;
2173                                 else active= tree_element_active(soops, te, 0);
2174                         }
2175                         else active= tree_element_type_active(soops, te, tselem, 0);
2176                         
2177                         if(active) {
2178                                 uiSetRoundBox(15);
2179                                 glColor4ub(255, 255, 255, 100);
2180                                 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);
2181                                 glEnable(GL_BLEND);
2182                         }
2183                         
2184                         tselem_draw_icon(*offsx, ys, tselem, te);
2185                         te->xs= *offsx;
2186                         te->ys= ys;
2187                         te->xend= *offsx+OL_X;
2188                         te->flag |= TE_ICONROW; // for click
2189                         
2190                         (*offsx) += OL_X;
2191                 }
2192                 
2193                 outliner_draw_iconrow(soops, &te->subtree, level+1, offsx, ys);
2194         }
2195         
2196 }
2197
2198 static void outliner_draw_tree_element(SpaceOops *soops, TreeElement *te, int startx, int *starty)
2199 {
2200         TreeElement *ten;
2201         TreeStoreElem *tselem;
2202         int offsx= 0, active=0; // active=1 active obj, else active data
2203         
2204         tselem= TREESTORE(te);
2205
2206         if(*starty >= soops->v2d.cur.ymin && *starty<= soops->v2d.cur.ymax) {
2207                 
2208                 glEnable(GL_BLEND);
2209
2210                 /* colors for active/selected data */
2211                 if(tselem->type==0) {
2212                         if(te->idcode==ID_SCE) {
2213                                 if(tselem->id == (ID *)G.scene) {
2214                                         glColor4ub(255, 255, 255, 100);
2215                                         active= 2;
2216                                 }
2217                         }
2218                         else if(te->idcode==ID_OB) {
2219                                 Object *ob= (Object *)tselem->id;
2220                                 
2221                                 if(ob==OBACT || (ob->flag & SELECT)) {
2222                                         char col[4];
2223                                         
2224                                         active= 2;
2225                                         if(ob==OBACT) {
2226                                                 BIF_GetThemeColorType4ubv(TH_ACTIVE, SPACE_VIEW3D, col);
2227                                                 active= 1;
2228                                         }
2229                                         else BIF_GetThemeColorType4ubv(TH_SELECT, SPACE_VIEW3D, col);
2230                                         col[3]= 100;
2231                                         glColor4ubv(col);
2232                                 }
2233                         }
2234                         else if(G.obedit && G.obedit->data==tselem->id) {
2235                                 glColor4ub(255, 255, 255, 100);
2236                                 active= 2;
2237                         }
2238                         else {
2239                                 if(tree_element_active(soops, te, 0)) {
2240                                         glColor4ub(220, 220, 255, 100);
2241                                         active= 2;
2242                                 }
2243                         }
2244                 }
2245                 else {
2246                         if( tree_element_type_active(soops, te, tselem, 0) ) active= 2;
2247                         glColor4ub(220, 220, 255, 100);
2248                 }
2249                 
2250                 /* active circle */
2251                 if(active) {
2252                         uiSetRoundBox(15);
2253                         uiRoundBox( (float)startx+OL_H-1.5, (float)*starty+2.0, (float)startx+2*OL_H-4.0, (float)*starty+OL_H-1.0, OL_H/2.0-2.0);
2254                         glEnable(GL_BLEND);
2255                         
2256                         te->flag |= TE_ACTIVE; // for lookup in display hierarchies
2257                 }
2258                 
2259                 /* open/close icon, only when sublevels, except for scene */
2260                 if(te->subtree.first || te->idcode==ID_SCE) {
2261                         int icon_x;
2262                         if(tselem->type==0 && (te->idcode==ID_OB || te->idcode==ID_SCE))
2263                                 icon_x = startx;
2264                         else
2265                                 icon_x = startx+5;
2266
2267                                 // icons a bit higher
2268                         if(tselem->flag & TSE_CLOSED) 
2269                                 BIF_draw_icon(icon_x, *starty+2, ICON_TRIA_RIGHT);
2270                         else
2271                                 BIF_draw_icon(icon_x, *starty+2, ICON_TRIA_DOWN);
2272                 }
2273                 offsx+= OL_X;
2274                 
2275                 /* datatype icon */
2276                 
2277                         // icons a bit higher
2278                 tselem_draw_icon(startx+offsx, *starty+2, tselem, te);
2279                 offsx+= OL_X;
2280                 glDisable(GL_BLEND);
2281
2282                 /* name */
2283                 if(active==1) BIF_ThemeColor(TH_TEXT_HI); 
2284                 else BIF_ThemeColor(TH_TEXT);
2285                 glRasterPos2i(startx+offsx, *starty+5);
2286                 BIF_RasterPos(startx+offsx, *starty+5);
2287                 BIF_DrawString(G.font, te->name, 0);
2288                 
2289                 offsx+= OL_X + BIF_GetStringWidth(G.font, te->name, 0);
2290                 
2291                 /* closed item, we draw the icons, not when it's a scene though */
2292                 if(tselem->flag & TSE_CLOSED) {
2293                         if(te->subtree.first) {
2294                                 if(tselem->type==0 && te->idcode==ID_SCE);
2295                                 else {
2296                                         int tempx= startx+offsx;
2297                                         // divider
2298                                         BIF_ThemeColorShade(TH_BACK, -40);
2299                                         glRecti(tempx -10, *starty+4, tempx -8, *starty+OL_H-4);
2300
2301                                         glEnable(GL_BLEND);
2302                                         glPixelTransferf(GL_ALPHA_SCALE, 0.5);
2303                                         outliner_draw_iconrow(soops, &te->subtree, 0, &tempx, *starty+2);
2304                                         glPixelTransferf(GL_ALPHA_SCALE, 1.0);
2305                                         glDisable(GL_BLEND);
2306                                 }
2307                         }
2308                 }
2309         }       
2310         /* store coord and continue */
2311         te->xs= startx;
2312         te->ys= *starty;
2313         te->xend= startx+offsx;
2314                 
2315         *starty-= OL_H;
2316         
2317         if((tselem->flag & TSE_CLOSED)==0) {
2318                 for(ten= te->subtree.first; ten; ten= ten->next) {
2319                         outliner_draw_tree_element(soops, ten, startx+OL_X, starty);
2320                 }
2321         }
2322 }
2323
2324 static void outliner_draw_hierarchy(SpaceOops *soops, ListBase *lb, int startx, int *starty)
2325 {
2326         TreeElement *te;
2327         TreeStoreElem *tselem;
2328         int y1, y2;
2329         
2330         if(lb->first==NULL) return;
2331         
2332         y1=y2= *starty; /* for vertical lines between objects */
2333         for(te=lb->first; te; te= te->next) {
2334                 y2= *starty;
2335                 tselem= TREESTORE(te);
2336                 
2337                 /* horizontal line? */
2338                 if(tselem->type==0 && (te->idcode==ID_OB || te->idcode==ID_SCE)) 
2339                         glRecti(startx, *starty, startx+OL_X, *starty-1);
2340                         
2341                 *starty-= OL_H;
2342                 
2343                 if((tselem->flag & TSE_CLOSED)==0)
2344                         outliner_draw_hierarchy(soops, &te->subtree, startx+OL_X, starty);
2345         }
2346         
2347         /* vertical line */
2348         te= lb->last;
2349         if(te->parent || lb->first!=lb->last) {
2350                 tselem= TREESTORE(te);
2351                 if(tselem->type==0 && te->idcode==ID_OB) {
2352                         
2353                         glRecti(startx, y1+OL_H, startx+1, y2);
2354                 }
2355         }
2356 }
2357
2358 static void outliner_draw_selection(SpaceOops *soops, ListBase *lb, int *starty) 
2359 {
2360         TreeElement *te;
2361         TreeStoreElem *tselem;
2362         
2363         for(te= lb->first; te; te= te->next) {
2364                 tselem= TREESTORE(te);
2365                 
2366                 /* selection status */
2367                 if(tselem->flag & TSE_SELECTED) {
2368                         glRecti(0, *starty+1, (int)soops->v2d.mask.xmax, *starty+OL_H-1);
2369                 }
2370                 *starty-= OL_H;
2371                 if((tselem->flag & TSE_CLOSED)==0) outliner_draw_selection(soops, &te->subtree, starty);
2372         }
2373 }
2374
2375 static void outliner_draw_tree(SpaceOops *soops)
2376 {
2377         TreeElement *te;
2378         int starty, startx;
2379         char col[4];
2380         
2381 #ifdef INTERNATIONAL
2382         FTF_SetFontSize('l');
2383         BIF_SetScale(1.0);
2384 #endif
2385         
2386         glBlendFunc(GL_SRC_ALPHA,  GL_ONE_MINUS_SRC_ALPHA); // only once
2387
2388         // selection first
2389         BIF_GetThemeColor3ubv(TH_BACK, col);
2390         glColor3ub(col[0]+15, col[1]+20, col[2]+25);
2391         starty= soops->v2d.tot.ymax-OL_H;
2392         outliner_draw_selection(soops, &soops->tree, &starty);
2393         
2394         // grey hierarchy lines
2395         glColor3ub(110,110,110);
2396         BIF_ThemeColorBlend(TH_BACK, TH_TEXT, 0.5);
2397         starty= soops->v2d.tot.ymax-OL_H/2;
2398         startx= 6;
2399         outliner_draw_hierarchy(soops, &soops->tree, startx, &starty);
2400         
2401         // items themselves
2402         starty= soops->v2d.tot.ymax-OL_H;
2403         startx= 0;
2404         for(te= soops->tree.first; te; te= te->next) {
2405                 outliner_draw_tree_element(soops, te, startx, &starty);
2406         }
2407         
2408 }
2409
2410 static void outliner_back(SpaceOops *soops)
2411 {
2412         int ystart;
2413         
2414         BIF_ThemeColorShade(TH_BACK, 6);
2415         ystart= soops->v2d.tot.ymax;
2416         ystart= OL_H*(ystart/(OL_H));
2417         
2418         while(ystart > soops->v2d.cur.ymin) {
2419                 glRecti(0, ystart, (int)soops->v2d.mask.xmax, ystart+OL_H);
2420                 ystart-= 2*OL_H;
2421         }
2422 }
2423
2424 static void namebutton_cb(void *soopsp, void *oldnamep)
2425 {
2426         SpaceOops *soops= soopsp;
2427         TreeStore *ts= soops->treestore;
2428         TreeStoreElem *tselem;
2429         int a;
2430         
2431         if(ts) {
2432                 /* only one namebutton can exist */
2433                 for(a=0, tselem= ts->data; a<ts->usedelem; a++, tselem++) {
2434                         if(tselem->flag & TSE_TEXTBUT) {
2435                                 if(tselem->type==0) {
2436                                         test_idbutton(tselem->id->name+2);      // library.c, unique name and alpha sort
2437                                 }
2438                                 else {
2439                                         TreeElement *te= outliner_find_tree_element(&soops->tree, a);
2440                                         
2441                                         if(te) {
2442                                                 switch(tselem->type) {
2443                                                 case TSE_DEFGROUP:
2444                                                         unique_vertexgroup_name(te->directdata, (Object *)tselem->id); //       id = object
2445                                                         break;
2446                                                 case TSE_NLA_ACTION:
2447                                                         test_idbutton(tselem->id->name+2);
2448                                                         break;
2449                                                 case TSE_EBONE:
2450                                                         if(G.obedit && G.obedit->data==(ID *)tselem->id) {
2451                                                                 EditBone *ebone= te->directdata;
2452                                                                 char newname[32];
2453                                                                 
2454                                                                 /* restore bone name */
2455                                                                 BLI_strncpy(newname, ebone->name, 32);
2456                                                                 BLI_strncpy(ebone->name, oldnamep, 32);
2457                                                                 armature_bone_rename(G.obedit->data, oldnamep, newname);
2458                                                         }
2459                                                         allqueue(REDRAWOOPS, 0);
2460                                                         allqueue(REDRAWVIEW3D, 1);
2461                                                         allqueue(REDRAWBUTSEDIT, 0);
2462                                                         break;
2463
2464                                                 case TSE_BONE:
2465                                                         {
2466                                                                 Bone *bone= te->directdata;
2467                                                                 Object *ob;
2468                                                                 char newname[32];
2469                                                                 
2470                                                                 // always make current object active
2471                                                                 tree_element_active_object(soops, te);
2472                                                                 ob= OBACT;
2473                                                                 
2474                                                                 /* restore bone name */
2475                                                                 BLI_strncpy(newname, bone->name, 32);
2476                                                                 BLI_strncpy(bone->name, oldnamep, 32);
2477                                                                 armature_bone_rename(ob->data, oldnamep, newname);
2478                                                         }
2479                                                         allqueue(REDRAWOOPS, 0);
2480                                                         allqueue(REDRAWVIEW3D, 1);
2481                                                         allqueue(REDRAWBUTSEDIT, 0);
2482                                                         break;
2483                                                 case TSE_POSE_CHANNEL:
2484                                                         {
2485                                                                 bPoseChannel *pchan= te->directdata;
2486                                                                 Object *ob;
2487                                                                 char newname[32];
2488                                                                 
2489                                                                 // always make current object active
2490                                                                 tree_element_active_object(soops, te);
2491                                                                 ob= OBACT;
2492                                                                 
2493                                                                 /* restore bone name */
2494                                                                 BLI_strncpy(newname, pchan->name, 32);
2495                                                                 BLI_strncpy(pchan->name, oldnamep, 32);
2496                                                                 armature_bone_rename(ob->data, oldnamep, newname);
2497                                                         }
2498                                                         allqueue(REDRAWOOPS, 0);
2499                                                         allqueue(REDRAWVIEW3D, 1);
2500                                                         allqueue(REDRAWBUTSEDIT, 0);
2501                                                         break;
2502                                                         
2503                                                 }
2504                                         }
2505                                 }
2506                                 tselem->flag &= ~TSE_TEXTBUT;
2507                         }
2508                 }
2509                 scrarea_queue_redraw(curarea);
2510         }
2511 }
2512
2513 static void outliner_buttons(uiBlock *block, SpaceOops *soops, ListBase *lb)
2514 {
2515         uiBut *bt;
2516         TreeElement *te;
2517         TreeStoreElem *tselem;
2518         int dx, len;
2519         
2520         for(te= lb->first; te; te= te->next) {
2521                 tselem= TREESTORE(te);
2522                 if(tselem->flag & TSE_TEXTBUT) {
2523                         
2524                         if(tselem->type==TSE_EBONE) len = sizeof(((EditBone*) 0)->name);
2525                         else if (tselem->type==TSE_MODIFIER) len = sizeof(((ModifierData*) 0)->name);
2526                         else len= sizeof(((ID*) 0)->name)-2;
2527                         
2528                         dx= BIF_GetStringWidth(G.font, te->name, 0);
2529                         if(dx<50) dx= 50;
2530                         
2531                         bt= uiDefBut(block, TEX, OL_NAMEBUTTON, "",  te->xs+2*OL_X-4, te->ys, dx+10, OL_H-1, te->name, 1.0, (float)len-1, 0, 0, "");
2532                         uiButSetFunc(bt, namebutton_cb, soops, NULL);
2533
2534                         // signal for button to open
2535                         addqueue(curarea->win, BUT_ACTIVATE, OL_NAMEBUTTON);
2536                 }
2537                 if((tselem->flag & TSE_CLOSED)==0) outliner_buttons(block, soops, &te->subtree);
2538         }
2539 }
2540
2541 void draw_outliner(ScrArea *sa, SpaceOops *soops)
2542 {
2543         uiBlock *block;
2544         int sizey;
2545         short ofsx, ofsy;
2546         
2547         calc_scrollrcts(sa, G.v2d, sa->winx, sa->winy);
2548
2549         if(sa->winx>SCROLLB+10 && sa->winy>SCROLLH+10) {
2550                 if(G.v2d->scroll) {     
2551                         ofsx= sa->winrct.xmin;  /* because mywin */
2552                         ofsy= sa->winrct.ymin;
2553                         glViewport(ofsx+G.v2d->mask.xmin,  ofsy+G.v2d->mask.ymin, ( ofsx+G.v2d->mask.xmax-1)-(ofsx+G.v2d->mask.xmin)+1, ( ofsy+G.v2d->mask.ymax-1)-( ofsy+G.v2d->mask.ymin)+1); 
2554                         glScissor(ofsx+G.v2d->mask.xmin,  ofsy+G.v2d->mask.ymin, ( ofsx+G.v2d->mask.xmax-1)-(ofsx+G.v2d->mask.xmin)+1, ( ofsy+G.v2d->mask.ymax-1)-( ofsy+G.v2d->mask.ymin)+1);
2555                 }
2556         }
2557         
2558         outliner_build_tree(soops); // always 
2559         sizey= 0;
2560         outliner_height(soops, &soops->tree, &sizey);
2561         
2562         /* we init all tot rect vars, only really needed on window size change tho */
2563         G.v2d->tot.xmin= 0.0;
2564         G.v2d->tot.xmax= (G.v2d->mask.xmax-G.v2d->mask.xmin);
2565         G.v2d->tot.ymax= 0.0;
2566         G.v2d->tot.ymin= -sizey*OL_H;
2567         test_view2d(G.v2d, sa->winx, sa->winy);
2568
2569         // align on top window if cur bigger than tot
2570         if(G.v2d->cur.ymax-G.v2d->cur.ymin > sizey*OL_H) {
2571                 G.v2d->cur.ymax= 0.0;
2572                 G.v2d->cur.ymin= -(G.v2d->mask.ymax-G.v2d->mask.ymin);
2573         }
2574
2575         myortho2(G.v2d->cur.xmin-0.375, G.v2d->cur.xmax-0.375, G.v2d->cur.ymin-0.375, G.v2d->cur.ymax-0.375);
2576
2577         /* draw outliner stuff */
2578         outliner_back(soops);
2579         outliner_draw_tree(soops);
2580
2581         /* restore viewport */
2582         mywinset(sa->win);
2583         
2584         /* ortho corrected */
2585         myortho2(G.v2d->cur.xmin-SCROLLB-0.375, G.v2d->cur.xmax-0.375, G.v2d->cur.ymin-0.375, G.v2d->cur.ymax-0.375);
2586         
2587         block= uiNewBlock(&sa->uiblocks, "outliner buttons", UI_EMBOSS, UI_HELV, sa->win);
2588         outliner_buttons(block, soops, &soops->tree);
2589         uiDrawBlock(block);
2590         
2591         /* drawoopsspace handles sliders */
2592 }
2593
2594