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