4 * ***** BEGIN GPL LICENSE BLOCK *****
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.
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.
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.
20 * The Original Code is Copyright (C) 2004 Blender Foundation.
21 * All rights reserved.
23 * The Original Code is: all of this file.
25 * Contributor(s): none yet.
27 * ***** END GPL LICENSE BLOCK *****
34 #include "MEM_guardedalloc.h"
36 #include "DNA_anim_types.h"
37 #include "DNA_action_types.h"
38 #include "DNA_armature_types.h"
39 #include "DNA_constraint_types.h"
40 #include "DNA_curve_types.h"
41 #include "DNA_camera_types.h"
42 #include "DNA_image_types.h"
43 #include "DNA_ipo_types.h"
44 #include "DNA_group_types.h"
45 #include "DNA_key_types.h"
46 #include "DNA_lamp_types.h"
47 #include "DNA_material_types.h"
48 #include "DNA_mesh_types.h"
49 #include "DNA_meta_types.h"
50 #include "DNA_modifier_types.h"
51 #include "DNA_nla_types.h"
52 #include "DNA_object_types.h"
53 #include "DNA_outliner_types.h"
54 #include "DNA_particle_types.h"
55 #include "DNA_scene_types.h"
56 #include "DNA_screen_types.h"
57 #include "DNA_space_types.h"
58 #include "DNA_texture_types.h"
59 #include "DNA_text_types.h"
60 #include "DNA_world_types.h"
61 #include "DNA_sequence_types.h"
63 #include "BLI_blenlib.h"
65 #include "IMB_imbuf_types.h"
67 #include "BKE_animsys.h"
68 #include "BKE_constraint.h"
69 #include "BKE_context.h"
70 #include "BKE_deform.h"
71 #include "BKE_depsgraph.h"
72 #include "BKE_global.h"
73 #include "BKE_group.h"
74 #include "BKE_library.h"
76 #include "BKE_material.h"
77 #include "BKE_modifier.h"
78 #include "BKE_object.h"
79 #include "BKE_report.h"
80 #include "BKE_screen.h"
81 #include "BKE_scene.h"
82 #include "BKE_sequence.h"
83 #include "BKE_utildefines.h"
85 #include "ED_screen.h"
93 #include "BIF_glutil.h"
95 #include "UI_interface.h"
96 #include "UI_interface_icons.h"
97 #include "UI_resources.h"
98 #include "UI_view2d.h"
100 #include "RNA_access.h"
101 #include "RNA_define.h"
103 #include "ED_armature.h"
104 #include "ED_keyframing.h"
105 #include "ED_object.h"
106 #include "ED_screen.h"
108 #include "outliner_intern.h"
110 #include "PIL_time.h"
116 #define OL_TOG_RESTRICT_VIEWX 54
117 #define OL_TOG_RESTRICT_SELECTX 36
118 #define OL_TOG_RESTRICT_RENDERX 18
120 #define OL_TOGW OL_TOG_RESTRICT_VIEWX
122 #define OL_RNA_COLX 300
123 #define OL_RNA_COL_SIZEX 150
124 #define OL_RNA_COL_SPACEX 50
128 #define TREESTORE(a) ((a)?soops->treestore->data+(a)->store_index:NULL)
130 /* ************* XXX **************** */
132 static void BIF_undo_push() {}
133 static void BIF_preview_changed() {}
134 static void error() {}
135 static int pupmenu() {return 0;}
137 /* ********************************** */
140 /* ******************** PROTOTYPES ***************** */
141 static void outliner_draw_tree_element(Scene *scene, ARegion *ar, SpaceOops *soops, TreeElement *te, int startx, int *starty);
142 static void outliner_do_object_operation(Scene *scene, SpaceOops *soops, ListBase *lb,
143 void (*operation_cb)(TreeElement *, TreeStoreElem *, TreeStoreElem *));
146 /* ******************** PERSISTANT DATA ***************** */
148 static void outliner_storage_cleanup(SpaceOops *soops)
150 TreeStore *ts= soops->treestore;
153 TreeStoreElem *tselem;
156 /* each element used once, for ID blocks with more users to have each a treestore */
157 for(a=0, tselem= ts->data; a<ts->usedelem; a++, tselem++) tselem->used= 0;
159 /* cleanup only after reading file or undo step, and always for
160 * RNA datablocks view in order to save memory */
161 if(soops->storeflag & SO_TREESTORE_CLEANUP) {
163 for(a=0, tselem= ts->data; a<ts->usedelem; a++, tselem++) {
164 if(tselem->id==NULL) unused++;
168 if(ts->usedelem == unused) {
171 ts->usedelem= ts->totelem= 0;
174 TreeStoreElem *tsnewar, *tsnew;
176 tsnew=tsnewar= MEM_mallocN((ts->usedelem-unused)*sizeof(TreeStoreElem), "new tselem");
177 for(a=0, tselem= ts->data; a<ts->usedelem; a++, tselem++) {
185 ts->usedelem-= unused;
186 ts->totelem= ts->usedelem;
193 static void check_persistant(SpaceOops *soops, TreeElement *te, ID *id, short type, short nr)
196 TreeStoreElem *tselem;
199 /* case 1; no TreeStore */
200 if(soops->treestore==NULL) {
201 ts= soops->treestore= MEM_callocN(sizeof(TreeStore), "treestore");
203 ts= soops->treestore;
205 /* check if 'te' is in treestore */
207 for(a=0; a<ts->usedelem; a++, tselem++) {
208 if(tselem->id==id && tselem->used==0) {
209 if((type==0 && tselem->type==0) ||(tselem->type==type && tselem->nr==nr)) {
217 /* add 1 element to treestore */
218 if(ts->usedelem==ts->totelem) {
219 TreeStoreElem *tsnew;
221 tsnew= MEM_mallocN((ts->totelem+TS_CHUNK)*sizeof(TreeStoreElem), "treestore data");
223 memcpy(tsnew, ts->data, ts->totelem*sizeof(TreeStoreElem));
227 ts->totelem+= TS_CHUNK;
230 tselem= ts->data+ts->usedelem;
233 if(type) tselem->nr= nr; // we're picky! :)
237 tselem->flag= TSE_CLOSED;
238 te->store_index= ts->usedelem;
243 /* ******************** TREE MANAGEMENT ****************** */
245 void outliner_free_tree(ListBase *lb)
249 TreeElement *te= lb->first;
251 outliner_free_tree(&te->subtree);
254 if(te->flag & TE_FREE_NAME) MEM_freeN(te->name);
259 static void outliner_height(SpaceOops *soops, ListBase *lb, int *h)
261 TreeElement *te= lb->first;
263 TreeStoreElem *tselem= TREESTORE(te);
264 if((tselem->flag & TSE_CLOSED)==0)
265 outliner_height(soops, &te->subtree, h);
271 #if 0 // XXX this is currently disabled until te->xend is set correctly
272 static void outliner_width(SpaceOops *soops, ListBase *lb, int *w)
274 TreeElement *te= lb->first;
276 // TreeStoreElem *tselem= TREESTORE(te);
278 // XXX fixme... te->xend is not set yet
279 if(tselem->flag & TSE_CLOSED) {
283 outliner_width(soops, &te->subtree, w);
289 static void outliner_rna_width(SpaceOops *soops, ListBase *lb, int *w, int startx)
291 TreeElement *te= lb->first;
293 TreeStoreElem *tselem= TREESTORE(te);
294 // XXX fixme... (currently, we're using a fixed length of 100)!
302 if((tselem->flag & TSE_CLOSED)==0)
303 outliner_rna_width(soops, &te->subtree, w, startx+OL_X);
308 static TreeElement *outliner_find_tree_element(ListBase *lb, int store_index)
310 TreeElement *te= lb->first, *tes;
312 if(te->store_index==store_index) return te;
313 tes= outliner_find_tree_element(&te->subtree, store_index);
322 static ID *outliner_search_back(SpaceOops *soops, TreeElement *te, short idcode)
324 TreeStoreElem *tselem;
328 tselem= TREESTORE(te);
329 if(tselem->type==0 && te->idcode==idcode) return tselem->id;
342 static int treesort_alpha(const void *v1, const void *v2)
344 const struct treesort *x1= v1, *x2= v2;
347 /* first put objects last (hierarchy) */
348 comp= (x1->idcode==ID_OB);
349 if(x2->idcode==ID_OB) comp+=2;
351 if(comp==1) return 1;
352 else if(comp==2) return -1;
354 int comp= strcmp(x1->name, x2->name);
356 if( comp>0 ) return 1;
357 else if( comp<0) return -1;
363 /* this is nice option for later? doesnt look too useful... */
365 static int treesort_obtype_alpha(const void *v1, const void *v2)
367 const struct treesort *x1= v1, *x2= v2;
369 /* first put objects last (hierarchy) */
370 if(x1->idcode==ID_OB && x2->idcode!=ID_OB) return 1;
371 else if(x2->idcode==ID_OB && x1->idcode!=ID_OB) return -1;
373 /* 2nd we check ob type */
374 if(x1->idcode==ID_OB && x2->idcode==ID_OB) {
375 if( ((Object *)x1->id)->type > ((Object *)x2->id)->type) return 1;
376 else if( ((Object *)x1->id)->type > ((Object *)x2->id)->type) return -1;
380 int comp= strcmp(x1->name, x2->name);
382 if( comp>0 ) return 1;
383 else if( comp<0) return -1;
390 /* sort happens on each subtree individual */
391 static void outliner_sort(SpaceOops *soops, ListBase *lb)
394 TreeStoreElem *tselem;
399 tselem= TREESTORE(te);
401 /* sorting rules; only object lists or deformgroups */
402 if( (tselem->type==TSE_DEFGROUP) || (tselem->type==0 && te->idcode==ID_OB)) {
405 for(te= lb->first; te; te= te->next) totelem++;
408 struct treesort *tear= MEM_mallocN(totelem*sizeof(struct treesort), "tree sort array");
409 struct treesort *tp=tear;
412 for(te= lb->first; te; te= te->next, tp++) {
413 tselem= TREESTORE(te);
416 tp->idcode= te->idcode;
417 if(tselem->type && tselem->type!=TSE_DEFGROUP) tp->idcode= 0; // dont sort this
420 /* keep beginning of list */
421 for(tp= tear, skip=0; skip<totelem; skip++, tp++)
422 if(tp->idcode) break;
425 qsort(tear+skip, totelem-skip, sizeof(struct treesort), treesort_alpha);
427 lb->first=lb->last= NULL;
430 BLI_addtail(lb, tp->te);
437 for(te= lb->first; te; te= te->next) {
438 outliner_sort(soops, &te->subtree);
442 /* Prototype, see functions below */
443 static TreeElement *outliner_add_element(SpaceOops *soops, ListBase *lb, void *idv,
444 TreeElement *parent, short type, short index);
447 static void outliner_add_passes(SpaceOops *soops, TreeElement *tenla, ID *id, SceneRenderLayer *srl)
449 TreeStoreElem *tselem= TREESTORE(tenla);
452 te= outliner_add_element(soops, &tenla->subtree, id, tenla, TSE_R_PASS, SCE_PASS_COMBINED);
453 te->name= "Combined";
454 te->directdata= &srl->passflag;
456 /* save cpu cycles, but we add the first to invoke an open/close triangle */
457 if(tselem->flag & TSE_CLOSED)
460 te= outliner_add_element(soops, &tenla->subtree, id, tenla, TSE_R_PASS, SCE_PASS_Z);
462 te->directdata= &srl->passflag;
464 te= outliner_add_element(soops, &tenla->subtree, id, tenla, TSE_R_PASS, SCE_PASS_VECTOR);
466 te->directdata= &srl->passflag;
468 te= outliner_add_element(soops, &tenla->subtree, id, tenla, TSE_R_PASS, SCE_PASS_NORMAL);
470 te->directdata= &srl->passflag;
472 te= outliner_add_element(soops, &tenla->subtree, id, tenla, TSE_R_PASS, SCE_PASS_UV);
474 te->directdata= &srl->passflag;
476 te= outliner_add_element(soops, &tenla->subtree, id, tenla, TSE_R_PASS, SCE_PASS_MIST);
478 te->directdata= &srl->passflag;
480 te= outliner_add_element(soops, &tenla->subtree, id, tenla, TSE_R_PASS, SCE_PASS_INDEXOB);
481 te->name= "Index Object";
482 te->directdata= &srl->passflag;
484 te= outliner_add_element(soops, &tenla->subtree, id, tenla, TSE_R_PASS, SCE_PASS_RGBA);
486 te->directdata= &srl->passflag;
488 te= outliner_add_element(soops, &tenla->subtree, id, tenla, TSE_R_PASS, SCE_PASS_DIFFUSE);
490 te->directdata= &srl->passflag;
492 te= outliner_add_element(soops, &tenla->subtree, id, tenla, TSE_R_PASS, SCE_PASS_SPEC);
493 te->name= "Specular";
494 te->directdata= &srl->passflag;
496 te= outliner_add_element(soops, &tenla->subtree, id, tenla, TSE_R_PASS, SCE_PASS_SHADOW);
498 te->directdata= &srl->passflag;
500 te= outliner_add_element(soops, &tenla->subtree, id, tenla, TSE_R_PASS, SCE_PASS_AO);
502 te->directdata= &srl->passflag;
504 te= outliner_add_element(soops, &tenla->subtree, id, tenla, TSE_R_PASS, SCE_PASS_REFLECT);
505 te->name= "Reflection";
506 te->directdata= &srl->passflag;
508 te= outliner_add_element(soops, &tenla->subtree, id, tenla, TSE_R_PASS, SCE_PASS_REFRACT);
509 te->name= "Refraction";
510 te->directdata= &srl->passflag;
512 te= outliner_add_element(soops, &tenla->subtree, id, tenla, TSE_R_PASS, SCE_PASS_RADIO);
513 te->name= "Radiosity";
514 te->directdata= &srl->passflag;
519 /* special handling of hierarchical non-lib data */
520 static void outliner_add_bone(SpaceOops *soops, ListBase *lb, ID *id, Bone *curBone,
521 TreeElement *parent, int *a)
523 TreeElement *te= outliner_add_element(soops, lb, id, parent, TSE_BONE, *a);
526 te->name= curBone->name;
527 te->directdata= curBone;
529 for(curBone= curBone->childbase.first; curBone; curBone=curBone->next) {
530 outliner_add_bone(soops, &te->subtree, id, curBone, te, a);
534 static void outliner_add_scene_contents(SpaceOops *soops, ListBase *lb, Scene *sce, TreeElement *te)
536 SceneRenderLayer *srl;
537 TreeElement *tenla= outliner_add_element(soops, lb, sce, te, TSE_R_LAYER_BASE, 0);
540 tenla->name= "RenderLayers";
541 for(a=0, srl= sce->r.layers.first; srl; srl= srl->next, a++) {
542 TreeElement *tenlay= outliner_add_element(soops, &tenla->subtree, sce, te, TSE_R_LAYER, a);
543 tenlay->name= srl->name;
544 tenlay->directdata= &srl->passflag;
546 if(srl->light_override)
547 outliner_add_element(soops, &tenlay->subtree, srl->light_override, tenlay, TSE_LINKED_LAMP, 0);
548 if(srl->mat_override)
549 outliner_add_element(soops, &tenlay->subtree, srl->mat_override, tenlay, TSE_LINKED_MAT, 0);
551 outliner_add_passes(soops, tenlay, &sce->id, srl);
554 outliner_add_element(soops, lb, sce->world, te, 0, 0);
557 static TreeElement *outliner_add_element(SpaceOops *soops, ListBase *lb, void *idv,
558 TreeElement *parent, short type, short index)
561 TreeStoreElem *tselem;
565 if(ELEM3(type, TSE_RNA_STRUCT, TSE_RNA_PROPERTY, TSE_RNA_ARRAY_ELEM)) {
566 id= ((PointerRNA*)idv)->id.data;
567 if(!id) id= ((PointerRNA*)idv)->data;
570 if(id==NULL) return NULL;
572 te= MEM_callocN(sizeof(TreeElement), "tree elem");
573 /* add to the visual tree */
575 /* add to the storage */
576 check_persistant(soops, te, id, type, index);
577 tselem= TREESTORE(te);
580 te->index= index; // for data arays
581 if(ELEM3(type, TSE_SEQUENCE, TSE_SEQ_STRIP, TSE_SEQUENCE_DUP));
582 else if(ELEM3(type, TSE_RNA_STRUCT, TSE_RNA_PROPERTY, TSE_RNA_ARRAY_ELEM));
583 else if(type==TSE_ANIM_DATA);
585 te->name= id->name+2; // default, can be overridden by Library or non-ID data
586 te->idcode= GS(id->name);
591 /* tuck pointer back in object, to construct hierarchy */
592 if(GS(id->name)==ID_OB) id->newid= (ID *)te;
594 /* expand specific data always */
595 switch(GS(id->name)) {
597 te->name= ((Library *)id)->name;
600 outliner_add_scene_contents(soops, &te->subtree, (Scene *)id, te);
604 Object *ob= (Object *)id;
606 outliner_add_element(soops, &te->subtree, ob->adt, te, TSE_ANIM_DATA, 0);
607 outliner_add_element(soops, &te->subtree, ob->poselib, te, 0, 0); // XXX FIXME.. add a special type for this
609 if(ob->proxy && ob->id.lib==NULL)
610 outliner_add_element(soops, &te->subtree, ob->proxy, te, TSE_PROXY, 0);
612 outliner_add_element(soops, &te->subtree, ob->data, te, 0, 0);
615 bArmature *arm= ob->data;
618 TreeElement *tenla= outliner_add_element(soops, &te->subtree, ob, te, TSE_POSE_BASE, 0);
622 if(arm->edbo==NULL && (ob->flag & OB_POSEMODE)) { // channels undefined in editmode, but we want the 'tenla' pose icon itself
623 int a= 0, const_index= 1000; /* ensure unique id for bone constraints */
625 for(pchan= ob->pose->chanbase.first; pchan; pchan= pchan->next, a++) {
626 ten= outliner_add_element(soops, &tenla->subtree, ob, tenla, TSE_POSE_CHANNEL, a);
627 ten->name= pchan->name;
628 ten->directdata= pchan;
629 pchan->prev= (bPoseChannel *)ten;
631 if(pchan->constraints.first) {
635 TreeElement *tenla1= outliner_add_element(soops, &ten->subtree, ob, ten, TSE_CONSTRAINT_BASE, 0);
638 tenla1->name= "Constraints";
639 for(con= pchan->constraints.first; con; con= con->next, const_index++) {
640 ten1= outliner_add_element(soops, &tenla1->subtree, ob, tenla1, TSE_CONSTRAINT, const_index);
641 #if 0 /* disabled as it needs to be reworked for recoded constraints system */
642 target= get_constraint_target(con, &str);
643 if(str && str[0]) ten1->name= str;
644 else if(target) ten1->name= target->id.name+2;
645 else ten1->name= con->name;
647 ten1->name= con->name;
648 ten1->directdata= con;
649 /* possible add all other types links? */
654 ten= tenla->subtree.first;
656 TreeElement *nten= ten->next, *par;
657 tselem= TREESTORE(ten);
658 if(tselem->type==TSE_POSE_CHANNEL) {
659 pchan= (bPoseChannel *)ten->directdata;
661 BLI_remlink(&tenla->subtree, ten);
662 par= (TreeElement *)pchan->parent->prev;
663 BLI_addtail(&par->subtree, ten);
669 /* restore prev pointers */
670 pchan= ob->pose->chanbase.first;
671 if(pchan) pchan->prev= NULL;
672 for(; pchan; pchan= pchan->next) {
673 if(pchan->next) pchan->next->prev= pchan;
678 if(ob->pose->agroups.first) {
681 TreeElement *tenla= outliner_add_element(soops, &te->subtree, ob, te, TSE_POSEGRP_BASE, 0);
684 tenla->name= "Bone Groups";
685 for (agrp=ob->pose->agroups.first; agrp; agrp=agrp->next, a++) {
686 ten= outliner_add_element(soops, &tenla->subtree, ob, tenla, TSE_POSEGRP, a);
687 ten->name= agrp->name;
688 ten->directdata= agrp;
693 for(a=0; a<ob->totcol; a++)
694 outliner_add_element(soops, &te->subtree, ob->mat[a], te, 0, a);
696 if(ob->constraints.first) {
700 TreeElement *tenla= outliner_add_element(soops, &te->subtree, ob, te, TSE_CONSTRAINT_BASE, 0);
704 tenla->name= "Constraints";
705 for(con= ob->constraints.first; con; con= con->next, a++) {
706 ten= outliner_add_element(soops, &tenla->subtree, ob, tenla, TSE_CONSTRAINT, a);
707 #if 0 /* disabled due to constraints system targets recode... code here needs review */
708 target= get_constraint_target(con, &str);
709 if(str && str[0]) ten->name= str;
710 else if(target) ten->name= target->id.name+2;
711 else ten->name= con->name;
713 ten->name= con->name;
714 ten->directdata= con;
715 /* possible add all other types links? */
719 if(ob->modifiers.first) {
721 TreeElement *temod = outliner_add_element(soops, &te->subtree, ob, te, TSE_MODIFIER_BASE, 0);
724 temod->name = "Modifiers";
725 for (index=0,md=ob->modifiers.first; md; index++,md=md->next) {
726 TreeElement *te = outliner_add_element(soops, &temod->subtree, ob, temod, TSE_MODIFIER, index);
730 if (md->type==eModifierType_Lattice) {
731 outliner_add_element(soops, &te->subtree, ((LatticeModifierData*) md)->object, te, TSE_LINKED_OB, 0);
732 } else if (md->type==eModifierType_Curve) {
733 outliner_add_element(soops, &te->subtree, ((CurveModifierData*) md)->object, te, TSE_LINKED_OB, 0);
734 } else if (md->type==eModifierType_Armature) {
735 outliner_add_element(soops, &te->subtree, ((ArmatureModifierData*) md)->object, te, TSE_LINKED_OB, 0);
736 } else if (md->type==eModifierType_Hook) {
737 outliner_add_element(soops, &te->subtree, ((HookModifierData*) md)->object, te, TSE_LINKED_OB, 0);
738 } else if (md->type==eModifierType_ParticleSystem) {
740 ParticleSystem *psys= ((ParticleSystemModifierData*) md)->psys;
742 ten = outliner_add_element(soops, &te->subtree, ob, te, TSE_LINKED_PSYS, 0);
743 ten->directdata = psys;
744 ten->name = psys->part->id.name+2;
748 if(ob->defbase.first) {
749 bDeformGroup *defgroup;
751 TreeElement *tenla= outliner_add_element(soops, &te->subtree, ob, te, TSE_DEFGROUP_BASE, 0);
754 tenla->name= "Vertex Groups";
755 for (defgroup=ob->defbase.first; defgroup; defgroup=defgroup->next, a++) {
756 ten= outliner_add_element(soops, &tenla->subtree, ob, tenla, TSE_DEFGROUP, a);
757 ten->name= defgroup->name;
758 ten->directdata= defgroup;
763 outliner_add_element(soops, &te->subtree, ob->dup_group, te, 0, 0);
769 Mesh *me= (Mesh *)id;
771 //outliner_add_element(soops, &te->subtree, me->adt, te, TSE_ANIM_DATA, 0);
773 outliner_add_element(soops, &te->subtree, me->key, te, 0, 0);
774 for(a=0; a<me->totcol; a++)
775 outliner_add_element(soops, &te->subtree, me->mat[a], te, 0, a);
776 /* could do tfaces with image links, but the images are not grouped nicely.
777 would require going over all tfaces, sort images in use. etc... */
782 Curve *cu= (Curve *)id;
784 outliner_add_element(soops, &te->subtree, cu->adt, te, TSE_ANIM_DATA, 0);
786 for(a=0; a<cu->totcol; a++)
787 outliner_add_element(soops, &te->subtree, cu->mat[a], te, 0, a);
792 MetaBall *mb= (MetaBall *)id;
793 for(a=0; a<mb->totcol; a++)
794 outliner_add_element(soops, &te->subtree, mb->mat[a], te, 0, a);
799 Material *ma= (Material *)id;
801 outliner_add_element(soops, &te->subtree, ma->adt, te, TSE_ANIM_DATA, 0);
803 for(a=0; a<MAX_MTEX; a++) {
804 if(ma->mtex[a]) outliner_add_element(soops, &te->subtree, ma->mtex[a]->tex, te, 0, a);
812 outliner_add_element(soops, &te->subtree, tex->adt, te, TSE_ANIM_DATA, 0);
813 outliner_add_element(soops, &te->subtree, tex->ima, te, 0, 0);
818 Camera *ca= (Camera *)id;
819 outliner_add_element(soops, &te->subtree, ca->adt, te, TSE_ANIM_DATA, 0);
824 Lamp *la= (Lamp *)id;
826 outliner_add_element(soops, &te->subtree, la->adt, te, TSE_ANIM_DATA, 0);
828 for(a=0; a<MAX_MTEX; a++) {
829 if(la->mtex[a]) outliner_add_element(soops, &te->subtree, la->mtex[a]->tex, te, 0, a);
835 World *wrld= (World *)id;
837 outliner_add_element(soops, &te->subtree, wrld->adt, te, TSE_ANIM_DATA, 0);
839 for(a=0; a<MAX_MTEX; a++) {
840 if(wrld->mtex[a]) outliner_add_element(soops, &te->subtree, wrld->mtex[a]->tex, te, 0, a);
848 outliner_add_element(soops, &te->subtree, key->adt, te, TSE_ANIM_DATA, 0);
853 // XXX do we want to be exposing the F-Curves here?
854 //bAction *act= (bAction *)id;
859 bArmature *arm= (bArmature *)id;
866 for (ebone = arm->edbo->first; ebone; ebone=ebone->next, a++) {
867 ten= outliner_add_element(soops, &te->subtree, id, te, TSE_EBONE, a);
868 ten->directdata= ebone;
869 ten->name= ebone->name;
873 ten= te->subtree.first;
875 TreeElement *nten= ten->next, *par;
876 ebone= (EditBone *)ten->directdata;
878 BLI_remlink(&te->subtree, ten);
879 par= ebone->parent->temp;
880 BLI_addtail(&par->subtree, ten);
887 /* do not extend Armature when we have posemode */
888 tselem= TREESTORE(te->parent);
889 if( GS(tselem->id->name)==ID_OB && ((Object *)tselem->id)->flag & OB_POSEMODE);
892 for (curBone=arm->bonebase.first; curBone; curBone=curBone->next){
893 outliner_add_bone(soops, &te->subtree, id, curBone, te, &a);
901 else if(type==TSE_ANIM_DATA) {
902 AnimData *adt= (AnimData *)idv;
904 /* this element's info */
905 te->name= "Animation";
908 outliner_add_element(soops, &te->subtree, adt->action, te, 0, 0);
911 if (adt->drivers.first) {
912 TreeElement *ted= outliner_add_element(soops, &te->subtree, adt, te, TSE_DRIVER_BASE, 0);
917 ted->name= "Drivers";
919 for (fcu= adt->drivers.first; fcu; fcu= fcu->next) {
920 if (fcu->driver && fcu->driver->targets.first) {
921 for (dtar= fcu->driver->targets.first; dtar; dtar= dtar->next) {
922 if (lastadded != dtar->id) {
923 // XXX this lastadded check is rather lame, and also fails quite badly...
924 outliner_add_element(soops, &ted->subtree, dtar->id, ted, TSE_LINKED_OB, 0);
933 if (adt->nla_tracks.first) {
934 TreeElement *tenla= outliner_add_element(soops, &te->subtree, adt, te, TSE_NLA, 0);
938 tenla->name= "NLA Tracks";
940 for (nlt= adt->nla_tracks.first; nlt; nlt= nlt->next) {
941 TreeElement *tenlt= outliner_add_element(soops, &tenla->subtree, nlt, tenla, TSE_NLA_TRACK, a);
946 tenlt->name= nlt->name;
948 for (strip=nlt->strips.first; strip; strip=strip->next, b++) {
949 ten= outliner_add_element(soops, &tenlt->subtree, strip->act, tenlt, TSE_NLA_ACTION, b);
950 if(ten) ten->directdata= strip;
955 else if(type==TSE_SEQUENCE) {
956 Sequence *seq= (Sequence*) idv;
960 * The idcode is a little hack, but the outliner
961 * only check te->idcode if te->type is equal to zero,
964 te->idcode= seq->type;
969 * This work like the sequence.
970 * If the sequence have a name (not default name)
971 * show it, in other case put the filename.
973 if(strcmp(seq->name, "SQ"))
976 if((seq->strip) && (seq->strip->stripdata))
977 te->name= seq->strip->stripdata->name;
978 else if((seq->strip) && (seq->strip->tstripdata) && (seq->strip->tstripdata->ibuf))
979 te->name= seq->strip->tstripdata->ibuf->name;
984 if(seq->type==SEQ_META) {
985 te->name= "Meta Strip";
986 p= seq->seqbase.first;
988 outliner_add_element(soops, &te->subtree, (void*)p, te, TSE_SEQUENCE, index);
993 outliner_add_element(soops, &te->subtree, (void*)seq->strip, te, TSE_SEQ_STRIP, index);
998 else if(type==TSE_SEQ_STRIP) {
999 Strip *strip= (Strip *)idv;
1002 te->name= strip->dir;
1004 te->name= "Strip None";
1005 te->directdata= strip;
1007 else if(type==TSE_SEQUENCE_DUP) {
1008 Sequence *seq= (Sequence*)idv;
1010 te->idcode= seq->type;
1011 te->directdata= seq;
1012 te->name= seq->strip->stripdata->name;
1014 else if(ELEM3(type, TSE_RNA_STRUCT, TSE_RNA_PROPERTY, TSE_RNA_ARRAY_ELEM)) {
1015 PointerRNA pptr, propptr, *ptr= (PointerRNA*)idv;
1016 PropertyRNA *prop, *iterprop;
1017 PropertyType proptype;
1018 PropertySubType propsubtype;
1021 /* we do lazy build, for speed and to avoid infinite recusion */
1023 if(ptr->data == NULL) {
1024 te->name= "(empty)";
1026 else if(type == TSE_RNA_STRUCT) {
1028 te->name= RNA_struct_name_get_alloc(ptr, NULL, 0);
1031 te->flag |= TE_FREE_NAME;
1033 te->name= (char*)RNA_struct_ui_name(ptr->type);
1035 iterprop= RNA_struct_iterator_property(ptr->type);
1036 tot= RNA_property_collection_length(ptr, iterprop);
1038 /* auto open these cases */
1039 if(!parent || (RNA_property_type(parent->directdata)) == PROP_POINTER)
1041 tselem->flag &= ~TSE_CLOSED;
1043 if(!(tselem->flag & TSE_CLOSED)) {
1044 for(a=0; a<tot; a++)
1045 outliner_add_element(soops, &te->subtree, (void*)ptr, te, TSE_RNA_PROPERTY, a);
1048 te->flag |= TE_LAZY_CLOSED;
1052 else if(type == TSE_RNA_PROPERTY) {
1054 iterprop= RNA_struct_iterator_property(ptr->type);
1055 RNA_property_collection_lookup_int(ptr, iterprop, index, &propptr);
1058 proptype= RNA_property_type(prop);
1060 te->name= (char*)RNA_property_ui_name(prop);
1061 te->directdata= prop;
1064 if(proptype == PROP_POINTER) {
1065 pptr= RNA_property_pointer_get(ptr, prop);
1068 if(!(tselem->flag & TSE_CLOSED))
1069 outliner_add_element(soops, &te->subtree, (void*)&pptr, te, TSE_RNA_STRUCT, -1);
1071 te->flag |= TE_LAZY_CLOSED;
1074 else if(proptype == PROP_COLLECTION) {
1075 tot= RNA_property_collection_length(ptr, prop);
1077 if(!(tselem->flag & TSE_CLOSED)) {
1078 for(a=0; a<tot; a++) {
1079 RNA_property_collection_lookup_int(ptr, prop, a, &pptr);
1080 outliner_add_element(soops, &te->subtree, (void*)&pptr, te, TSE_RNA_STRUCT, -1);
1084 te->flag |= TE_LAZY_CLOSED;
1086 else if(ELEM3(proptype, PROP_BOOLEAN, PROP_INT, PROP_FLOAT)) {
1087 tot= RNA_property_array_length(prop);
1089 if(!(tselem->flag & TSE_CLOSED)) {
1090 for(a=0; a<tot; a++)
1091 outliner_add_element(soops, &te->subtree, (void*)ptr, te, TSE_RNA_ARRAY_ELEM, a);
1094 te->flag |= TE_LAZY_CLOSED;
1097 else if(type == TSE_RNA_ARRAY_ELEM) {
1098 /* array property element */
1099 static char *vectoritem[4]= {" x", " y", " z", " w"};
1100 static char *quatitem[4]= {" w", " x", " y", " z"};
1101 static char *coloritem[4]= {" r", " g", " b", " a"};
1103 prop= parent->directdata;
1104 proptype= RNA_property_type(prop);
1105 propsubtype= RNA_property_subtype(prop);
1106 tot= RNA_property_array_length(prop);
1108 te->directdata= prop;
1112 if(tot == 4 && propsubtype == PROP_ROTATION)
1113 te->name= quatitem[index];
1114 else if(tot <= 4 && (propsubtype == PROP_VECTOR || propsubtype == PROP_ROTATION))
1115 te->name= vectoritem[index];
1116 else if(tot <= 4 && propsubtype == PROP_COLOR)
1117 te->name= coloritem[index];
1119 te->name= MEM_callocN(sizeof(char)*20, "OutlinerRNAArrayName");
1120 sprintf(te->name, " %d", index+1);
1121 te->flag |= TE_FREE_NAME;
1129 static void outliner_make_hierarchy(SpaceOops *soops, ListBase *lb)
1131 TreeElement *te, *ten, *tep;
1132 TreeStoreElem *tselem;
1134 /* build hierarchy */
1135 // XXX also, set extents here...
1139 tselem= TREESTORE(te);
1141 if(tselem->type==0 && te->idcode==ID_OB) {
1142 Object *ob= (Object *)tselem->id;
1143 if(ob->parent && ob->parent->id.newid) {
1144 BLI_remlink(lb, te);
1145 tep= (TreeElement *)ob->parent->id.newid;
1146 BLI_addtail(&tep->subtree, te);
1147 // set correct parent pointers
1148 for(te=tep->subtree.first; te; te= te->next) te->parent= tep;
1155 /* Helped function to put duplicate sequence in the same tree. */
1156 int need_add_seq_dup(Sequence *seq)
1160 if((!seq->strip) || (!seq->strip->stripdata) || (!seq->strip->stripdata->name))
1164 * First check backward, if we found a duplicate
1165 * sequence before this, don't need it, just return.
1169 if((!p->strip) || (!p->strip->stripdata) || (!p->strip->stripdata->name)) {
1174 if(!strcmp(p->strip->stripdata->name, seq->strip->stripdata->name))
1181 if((!p->strip) || (!p->strip->stripdata) || (!p->strip->stripdata->name)) {
1186 if(!strcmp(p->strip->stripdata->name, seq->strip->stripdata->name))
1193 void add_seq_dup(SpaceOops *soops, Sequence *seq, TreeElement *te, short index)
1200 if((!p->strip) || (!p->strip->stripdata) || (!p->strip->stripdata->name)) {
1205 if(!strcmp(p->strip->stripdata->name, seq->strip->stripdata->name))
1206 ch= outliner_add_element(soops, &te->subtree, (void*)p, te, TSE_SEQUENCE, index);
1211 static void outliner_build_tree(Main *mainvar, Scene *scene, SpaceOops *soops)
1215 TreeElement *te=NULL, *ten;
1216 TreeStoreElem *tselem;
1217 int show_opened= (soops->treestore==NULL); /* on first view, we open scenes */
1219 if(soops->tree.first && (soops->storeflag & SO_TREESTORE_REDRAW))
1222 outliner_free_tree(&soops->tree);
1223 outliner_storage_cleanup(soops);
1225 /* clear ob id.new flags */
1226 for(ob= mainvar->object.first; ob; ob= ob->id.next) ob->id.newid= NULL;
1229 if(soops->outlinevis == SO_LIBRARIES) {
1232 for(lib= mainvar->library.first; lib; lib= lib->id.next) {
1233 ten= outliner_add_element(soops, &soops->tree, lib, NULL, 0, 0);
1234 lib->id.newid= (ID *)ten;
1236 /* make hierarchy */
1237 ten= soops->tree.first;
1239 TreeElement *nten= ten->next, *par;
1240 tselem= TREESTORE(ten);
1241 lib= (Library *)tselem->id;
1243 BLI_remlink(&soops->tree, ten);
1244 par= (TreeElement *)lib->parent->id.newid;
1245 BLI_addtail(&par->subtree, ten);
1250 /* restore newid pointers */
1251 for(lib= mainvar->library.first; lib; lib= lib->id.next)
1252 lib->id.newid= NULL;
1255 else if(soops->outlinevis == SO_ALL_SCENES) {
1257 for(sce= mainvar->scene.first; sce; sce= sce->id.next) {
1258 te= outliner_add_element(soops, &soops->tree, sce, NULL, 0, 0);
1259 tselem= TREESTORE(te);
1260 if(sce==scene && show_opened)
1261 tselem->flag &= ~TSE_CLOSED;
1263 for(base= sce->base.first; base; base= base->next) {
1264 ten= outliner_add_element(soops, &te->subtree, base->object, te, 0, 0);
1265 ten->directdata= base;
1267 outliner_make_hierarchy(soops, &te->subtree);
1268 /* clear id.newid, to prevent objects be inserted in wrong scenes (parent in other scene) */
1269 for(base= sce->base.first; base; base= base->next) base->object->id.newid= NULL;
1272 else if(soops->outlinevis == SO_CUR_SCENE) {
1274 outliner_add_scene_contents(soops, &soops->tree, scene, NULL);
1276 for(base= scene->base.first; base; base= base->next) {
1277 ten= outliner_add_element(soops, &soops->tree, base->object, NULL, 0, 0);
1278 ten->directdata= base;
1280 outliner_make_hierarchy(soops, &soops->tree);
1282 else if(soops->outlinevis == SO_VISIBLE) {
1283 for(base= scene->base.first; base; base= base->next) {
1284 if(base->lay & scene->lay)
1285 outliner_add_element(soops, &soops->tree, base->object, NULL, 0, 0);
1287 outliner_make_hierarchy(soops, &soops->tree);
1289 else if(soops->outlinevis == SO_GROUPS) {
1293 for(group= mainvar->group.first; group; group= group->id.next) {
1295 te= outliner_add_element(soops, &soops->tree, group, NULL, 0, 0);
1296 tselem= TREESTORE(te);
1298 for(go= group->gobject.first; go; go= go->next) {
1299 ten= outliner_add_element(soops, &te->subtree, go->ob, te, 0, 0);
1300 ten->directdata= NULL; /* eh, why? */
1302 outliner_make_hierarchy(soops, &te->subtree);
1303 /* clear id.newid, to prevent objects be inserted in wrong scenes (parent in other scene) */
1304 for(go= group->gobject.first; go; go= go->next) go->ob->id.newid= NULL;
1308 else if(soops->outlinevis == SO_SAME_TYPE) {
1311 for(base= scene->base.first; base; base= base->next) {
1312 if(base->object->type==ob->type) {
1313 ten= outliner_add_element(soops, &soops->tree, base->object, NULL, 0, 0);
1314 ten->directdata= base;
1317 outliner_make_hierarchy(soops, &soops->tree);
1320 else if(soops->outlinevis == SO_SELECTED) {
1321 for(base= scene->base.first; base; base= base->next) {
1322 if(base->lay & scene->lay) {
1323 if(base==BASACT || (base->flag & SELECT)) {
1324 ten= outliner_add_element(soops, &soops->tree, base->object, NULL, 0, 0);
1325 ten->directdata= base;
1329 outliner_make_hierarchy(soops, &soops->tree);
1331 else if(soops->outlinevis==SO_SEQUENCE) {
1333 Editing *ed= seq_give_editing(scene, FALSE);
1339 seq= ed->seqbasep->first;
1344 op= need_add_seq_dup(seq);
1346 ten= outliner_add_element(soops, &soops->tree, (void*)seq, NULL, TSE_SEQUENCE, 0);
1348 ten= outliner_add_element(soops, &soops->tree, (void*)seq, NULL, TSE_SEQUENCE_DUP, 0);
1349 add_seq_dup(soops, seq, ten, 0);
1354 else if(soops->outlinevis==SO_DATABLOCKS) {
1357 RNA_main_pointer_create(mainvar, &mainptr);
1359 ten= outliner_add_element(soops, &soops->tree, (void*)&mainptr, NULL, TSE_RNA_STRUCT, -1);
1362 tselem= TREESTORE(ten);
1363 tselem->flag &= ~TSE_CLOSED;
1366 else if(soops->outlinevis==SO_USERDEF) {
1367 PointerRNA userdefptr;
1369 RNA_pointer_create(NULL, &RNA_UserPreferences, &U, &userdefptr);
1371 ten= outliner_add_element(soops, &soops->tree, (void*)&userdefptr, NULL, TSE_RNA_STRUCT, -1);
1374 tselem= TREESTORE(ten);
1375 tselem->flag &= ~TSE_CLOSED;
1379 ten= outliner_add_element(soops, &soops->tree, OBACT, NULL, 0, 0);
1380 if(ten) ten->directdata= BASACT;
1383 outliner_sort(soops, &soops->tree);
1386 /* **************** INTERACTIVE ************* */
1388 static int outliner_count_levels(SpaceOops *soops, ListBase *lb, int curlevel)
1391 int level=curlevel, lev;
1393 for(te= lb->first; te; te= te->next) {
1395 lev= outliner_count_levels(soops, &te->subtree, curlevel+1);
1396 if(lev>level) level= lev;
1401 static int outliner_has_one_flag(SpaceOops *soops, ListBase *lb, short flag, short curlevel)
1404 TreeStoreElem *tselem;
1407 for(te= lb->first; te; te= te->next) {
1408 tselem= TREESTORE(te);
1409 if(tselem->flag & flag) return curlevel;
1411 level= outliner_has_one_flag(soops, &te->subtree, flag, curlevel+1);
1412 if(level) return level;
1417 static void outliner_set_flag(SpaceOops *soops, ListBase *lb, short flag, short set)
1420 TreeStoreElem *tselem;
1422 for(te= lb->first; te; te= te->next) {
1423 tselem= TREESTORE(te);
1424 if(set==0) tselem->flag &= ~flag;
1425 else tselem->flag |= flag;
1426 outliner_set_flag(soops, &te->subtree, flag, set);
1432 void object_toggle_visibility_cb(TreeElement *te, TreeStoreElem *tsep, TreeStoreElem *tselem)
1434 Scene *scene= NULL; // XXX
1435 Base *base= (Base *)te->directdata;
1437 if(base==NULL) base= object_in_scene((Object *)tselem->id, scene);
1439 base->object->restrictflag^=OB_RESTRICT_VIEW;
1443 static int outliner_toggle_visibility_exec(bContext *C, wmOperator *op)
1445 SpaceOops *soops= (SpaceOops *)CTX_wm_space_data(C);
1446 Scene *scene= CTX_data_scene(C);
1447 ARegion *ar= CTX_wm_region(C);
1449 outliner_do_object_operation(scene, soops, &soops->tree, object_toggle_visibility_cb);
1451 // XXX need proper notifiers here instead
1452 ED_region_tag_redraw(ar);
1454 return OPERATOR_FINISHED;
1457 void OUTLINER_OT_visibility_toggle(wmOperatorType *ot)
1460 ot->name= "Toggle Visability";
1461 ot->idname= "OUTLINER_OT_visibility_toggle";
1462 ot->description= "Toggle the visibility of selected items.";
1465 ot->exec= outliner_toggle_visibility_exec;
1466 ot->poll= ED_operator_outliner_active;
1468 ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
1473 static void object_toggle_selectability_cb(TreeElement *te, TreeStoreElem *tsep, TreeStoreElem *tselem)
1475 Scene *scene= NULL; // XXX
1476 Base *base= (Base *)te->directdata;
1478 if(base==NULL) base= object_in_scene((Object *)tselem->id, scene);
1480 base->object->restrictflag^=OB_RESTRICT_SELECT;
1484 static int outliner_toggle_selectability_exec(bContext *C, wmOperator *op)
1486 SpaceOops *soops= (SpaceOops *)CTX_wm_space_data(C);
1487 Scene *scene= CTX_data_scene(C);
1488 ARegion *ar= CTX_wm_region(C);
1490 outliner_do_object_operation(scene, soops, &soops->tree, object_toggle_selectability_cb);
1492 // XXX need proper notifiers here instead
1493 ED_region_tag_redraw(ar);
1495 return OPERATOR_FINISHED;
1498 void OUTLINER_OT_selectability_toggle(wmOperatorType *ot)
1501 ot->name= "Toggle Selectability";
1502 ot->idname= "OUTLINER_OT_selectability_toggle";
1503 ot->description= "Toggle the selectability";
1506 ot->exec= outliner_toggle_selectability_exec;
1507 ot->poll= ED_operator_outliner_active;
1509 ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
1514 void object_toggle_renderability_cb(TreeElement *te, TreeStoreElem *tsep, TreeStoreElem *tselem)
1516 Scene *scene= NULL; // XXX
1517 Base *base= (Base *)te->directdata;
1519 if(base==NULL) base= object_in_scene((Object *)tselem->id, scene);
1521 base->object->restrictflag^=OB_RESTRICT_RENDER;
1525 static int outliner_toggle_renderability_exec(bContext *C, wmOperator *op)
1527 SpaceOops *soops= (SpaceOops *)CTX_wm_space_data(C);
1528 Scene *scene= CTX_data_scene(C);
1529 ARegion *ar= CTX_wm_region(C);
1531 outliner_do_object_operation(scene, soops, &soops->tree, object_toggle_renderability_cb);
1533 // XXX need proper notifiers here instead
1534 ED_region_tag_redraw(ar);
1536 return OPERATOR_FINISHED;
1539 void OUTLINER_OT_renderability_toggle(wmOperatorType *ot)
1542 ot->name= "Toggle Renderability";
1543 ot->idname= "OUTLINER_OT_renderability_toggle";
1544 ot->description= "Toggle the renderbility of selected items.";
1547 ot->exec= outliner_toggle_renderability_exec;
1548 ot->poll= ED_operator_outliner_active;
1550 ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
1555 static int outliner_toggle_expanded_exec(bContext *C, wmOperator *op)
1557 SpaceOops *soops= (SpaceOops *)CTX_wm_space_data(C);
1558 ARegion *ar= CTX_wm_region(C);
1560 if (outliner_has_one_flag(soops, &soops->tree, TSE_CLOSED, 1))
1561 outliner_set_flag(soops, &soops->tree, TSE_CLOSED, 0);
1563 outliner_set_flag(soops, &soops->tree, TSE_CLOSED, 1);
1565 // XXX need proper notifiers here instead
1566 ED_region_tag_redraw(ar);
1568 return OPERATOR_FINISHED;
1571 void OUTLINER_OT_expanded_toggle(wmOperatorType *ot)
1574 ot->name= "Expand/Collapse All";
1575 ot->idname= "OUTLINER_OT_expanded_toggle";
1576 ot->description= "Expand/Collapse all items.";
1579 ot->exec= outliner_toggle_expanded_exec;
1580 ot->poll= ED_operator_outliner_active;
1582 ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
1587 static int outliner_toggle_selected_exec(bContext *C, wmOperator *op)
1589 SpaceOops *soops= (SpaceOops *)CTX_wm_space_data(C);
1590 ARegion *ar= CTX_wm_region(C);
1592 if (outliner_has_one_flag(soops, &soops->tree, TSE_SELECTED, 1))
1593 outliner_set_flag(soops, &soops->tree, TSE_SELECTED, 0);
1595 outliner_set_flag(soops, &soops->tree, TSE_SELECTED, 1);
1597 soops->storeflag |= SO_TREESTORE_REDRAW;
1599 // XXX need proper notifiers here instead
1600 ED_region_tag_redraw(ar);
1602 return OPERATOR_FINISHED;
1605 void OUTLINER_OT_selected_toggle(wmOperatorType *ot)
1608 ot->name= "Toggle Selected";
1609 ot->idname= "OUTLINER_OT_selected_toggle";
1610 ot->description= "Toggle the Outliner selection of items.";
1613 ot->exec= outliner_toggle_selected_exec;
1614 ot->poll= ED_operator_outliner_active;
1616 ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
1621 /* helper function for Show/Hide one level operator */
1622 static void outliner_openclose_level(SpaceOops *soops, ListBase *lb, int curlevel, int level, int open)
1625 TreeStoreElem *tselem;
1627 for(te= lb->first; te; te= te->next) {
1628 tselem= TREESTORE(te);
1631 if(curlevel<=level) tselem->flag &= ~TSE_CLOSED;
1634 if(curlevel>=level) tselem->flag |= TSE_CLOSED;
1637 outliner_openclose_level(soops, &te->subtree, curlevel+1, level, open);
1641 static int outliner_one_level_exec(bContext *C, wmOperator *op)
1643 SpaceOops *soops= (SpaceOops *)CTX_wm_space_data(C);
1644 ARegion *ar= CTX_wm_region(C);
1645 int add= RNA_boolean_get(op->ptr, "open");
1648 level= outliner_has_one_flag(soops, &soops->tree, TSE_CLOSED, 1);
1650 if(level) outliner_openclose_level(soops, &soops->tree, 1, level, 1);
1653 if(level==0) level= outliner_count_levels(soops, &soops->tree, 0);
1654 if(level) outliner_openclose_level(soops, &soops->tree, 1, level-1, 0);
1657 // XXX need proper notifiers here instead
1658 ED_region_tag_redraw(ar);
1660 return OPERATOR_FINISHED;
1663 void OUTLINER_OT_show_one_level(wmOperatorType *ot)
1666 ot->name= "Show/Hide One Level";
1667 ot->idname= "OUTLINER_OT_show_one_level";
1670 ot->exec= outliner_one_level_exec;
1671 ot->poll= ED_operator_outliner_active;
1673 ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
1676 RNA_def_boolean(ot->srna, "open", 1, "Open", "Expand all entries one level deep.");
1679 /* return 1 when levels were opened */
1680 static int outliner_open_back(SpaceOops *soops, TreeElement *te)
1682 TreeStoreElem *tselem;
1685 for (te= te->parent; te; te= te->parent) {
1686 tselem= TREESTORE(te);
1687 if (tselem->flag & TSE_CLOSED) {
1688 tselem->flag &= ~TSE_CLOSED;
1695 /* This is not used anywhere at the moment */
1697 static void outliner_open_reveal(SpaceOops *soops, ListBase *lb, TreeElement *teFind, int *found)
1700 TreeStoreElem *tselem;
1702 for (te= lb->first; te; te= te->next) {
1703 /* check if this tree-element was the one we're seeking */
1709 /* try to see if sub-tree contains it then */
1710 outliner_open_reveal(soops, &te->subtree, teFind, found);
1712 tselem= TREESTORE(te);
1713 if (tselem->flag & TSE_CLOSED)
1714 tselem->flag &= ~TSE_CLOSED;
1721 // XXX just use View2D ops for this?
1722 void outliner_page_up_down(Scene *scene, ARegion *ar, SpaceOops *soops, int up)
1724 int dy= ar->v2d.mask.ymax-ar->v2d.mask.ymin;
1726 if(up == -1) dy= -dy;
1727 ar->v2d.cur.ymin+= dy;
1728 ar->v2d.cur.ymax+= dy;
1730 soops->storeflag |= SO_TREESTORE_REDRAW;
1733 /* **** do clicks on items ******* */
1735 static int tree_element_active_renderlayer(TreeElement *te, TreeStoreElem *tselem, int set)
1739 /* paranoia check */
1740 if(te->idcode!=ID_SCE)
1742 sce= (Scene *)tselem->id;
1745 sce->r.actlay= tselem->nr;
1748 return sce->r.actlay==tselem->nr;
1753 static void tree_element_set_active_object(bContext *C, Scene *scene, SpaceOops *soops, TreeElement *te)
1755 TreeStoreElem *tselem= TREESTORE(te);
1759 int shift= 0; // XXX
1761 /* if id is not object, we search back */
1762 if(te->idcode==ID_OB) ob= (Object *)tselem->id;
1764 ob= (Object *)outliner_search_back(soops, te, ID_OB);
1765 if(ob==OBACT) return;
1767 if(ob==NULL) return;
1769 sce= (Scene *)outliner_search_back(soops, te, ID_SCE);
1770 if(sce && scene != sce) {
1771 ED_screen_set_scene(C, sce);
1774 /* find associated base in current scene */
1775 for(base= FIRSTBASE; base; base= base->next)
1776 if(base->object==ob) break;
1780 if(base->flag & SELECT)
1781 ED_base_object_select(base, BA_DESELECT);
1783 ED_base_object_select(base, BA_SELECT);
1787 /* deleselect all */
1788 for(b= FIRSTBASE; b; b= b->next) {
1790 b->object->flag= b->flag;
1792 ED_base_object_select(base, BA_SELECT);
1795 ED_base_object_activate(C, base); /* adds notifier */
1798 // XXX if(ob!=obedit) exit_editmode(EM_FREEDATA|EM_FREEUNDO|EM_WAITCURSOR);
1799 // else countall(); /* exit_editmode calls f() */
1802 static int tree_element_active_material(Scene *scene, SpaceOops *soops, TreeElement *te, int set)
1807 /* we search for the object parent */
1808 ob= (Object *)outliner_search_back(soops, te, ID_OB);
1809 if(ob==NULL || ob!=OBACT) return 0; // just paranoia
1811 /* searching in ob mat array? */
1813 if(tes->idcode==ID_OB) {
1815 ob->actcol= te->index+1;
1816 ob->matbits[te->index]= 1; // make ob material active too
1817 ob->colbits |= (1<<te->index);
1820 if(ob->actcol == te->index+1)
1821 if(ob->matbits[te->index]) return 1;
1824 /* or we search for obdata material */
1827 ob->actcol= te->index+1;
1828 ob->matbits[te->index]= 0; // make obdata material active too
1829 ob->colbits &= ~(1<<te->index);
1832 if(ob->actcol == te->index+1)
1833 if(ob->matbits[te->index]==0) return 1;
1837 // XXX extern_set_butspace(F5KEY, 0); // force shading buttons
1838 BIF_preview_changed(ID_MA);
1843 static int tree_element_active_texture(Scene *scene, SpaceOops *soops, TreeElement *te, int set)
1846 TreeStoreElem *tselem, *tselemp;
1848 SpaceButs *sbuts=NULL;
1850 if(ob==NULL) return 0; // no active object
1852 tselem= TREESTORE(te);
1854 /* find buttons area (note, this is undefined really still, needs recode in blender) */
1855 /* XXX removed finding sbuts */
1857 /* where is texture linked to? */
1859 tselemp= TREESTORE(tep);
1861 if(tep->idcode==ID_WO) {
1862 World *wrld= (World *)tselemp->id;
1866 // XXX sbuts->tabo= TAB_SHADING_TEX; // hack from header_buttonswin.c
1867 // XXX sbuts->texfrom= 1;
1869 // XXX extern_set_butspace(F6KEY, 0); // force shading buttons texture
1870 wrld->texact= te->index;
1872 else if(tselemp->id == (ID *)(scene->world)) {
1873 if(wrld->texact==te->index) return 1;
1876 else if(tep->idcode==ID_LA) {
1877 Lamp *la= (Lamp *)tselemp->id;
1880 // XXX sbuts->tabo= TAB_SHADING_TEX; // hack from header_buttonswin.c
1881 // XXX sbuts->texfrom= 2;
1883 // XXX extern_set_butspace(F6KEY, 0); // force shading buttons texture
1884 la->texact= te->index;
1887 if(tselemp->id == ob->data) {
1888 if(la->texact==te->index) return 1;
1892 else if(tep->idcode==ID_MA) {
1893 Material *ma= (Material *)tselemp->id;
1896 //sbuts->tabo= TAB_SHADING_TEX; // hack from header_buttonswin.c
1897 // XXX sbuts->texfrom= 0;
1899 // XXX extern_set_butspace(F6KEY, 0); // force shading buttons texture
1900 ma->texact= (char)te->index;
1902 /* also set active material */
1903 ob->actcol= tep->index+1;
1905 else if(tep->flag & TE_ACTIVE) { // this is active material
1906 if(ma->texact==te->index) return 1;
1914 static int tree_element_active_lamp(Scene *scene, SpaceOops *soops, TreeElement *te, int set)
1918 /* we search for the object parent */
1919 ob= (Object *)outliner_search_back(soops, te, ID_OB);
1920 if(ob==NULL || ob!=OBACT) return 0; // just paranoia
1923 // XXX extern_set_butspace(F5KEY, 0);
1924 BIF_preview_changed(ID_LA);
1931 static int tree_element_active_world(Scene *scene, SpaceOops *soops, TreeElement *te, int set)
1934 TreeStoreElem *tselem=NULL;
1939 tselem= TREESTORE(tep);
1940 sce= (Scene *)tselem->id;
1943 if(set) { // make new scene active
1944 if(sce && scene != sce) {
1945 // XXX ED_screen_set_scene(C, sce);
1949 if(tep==NULL || tselem->id == (ID *)scene) {
1951 // XXX extern_set_butspace(F8KEY, 0);
1960 static int tree_element_active_defgroup(Scene *scene, TreeElement *te, TreeStoreElem *tselem, int set)
1964 /* id in tselem is object */
1965 ob= (Object *)tselem->id;
1967 ob->actdef= te->index+1;
1968 DAG_object_flush_update(scene, ob, OB_RECALC_DATA);
1972 if(ob->actdef== te->index+1) return 1;
1977 static int tree_element_active_posegroup(Scene *scene, TreeElement *te, TreeStoreElem *tselem, int set)
1979 Object *ob= (Object *)tselem->id;
1983 ob->pose->active_group= te->index+1;
1987 if(ob==OBACT && ob->pose) {
1988 if (ob->pose->active_group== te->index+1) return 1;
1994 static int tree_element_active_posechannel(Scene *scene, TreeElement *te, TreeStoreElem *tselem, int set)
1996 Object *ob= (Object *)tselem->id;
1997 bPoseChannel *pchan= te->directdata;
2000 if(!(pchan->bone->flag & BONE_HIDDEN_P)) {
2002 // XXX if(G.qual & LR_SHIFTKEY) deselectall_posearmature(ob, 2, 0); // 2 = clear active tag
2003 // else deselectall_posearmature(ob, 0, 0); // 0 = deselect
2004 pchan->bone->flag |= BONE_SELECTED|BONE_ACTIVE;
2009 if(ob==OBACT && ob->pose) {
2010 if (pchan->bone->flag & BONE_SELECTED) return 1;
2016 static int tree_element_active_bone(Scene *scene, TreeElement *te, TreeStoreElem *tselem, int set)
2018 bArmature *arm= (bArmature *)tselem->id;
2019 Bone *bone= te->directdata;
2022 if(!(bone->flag & BONE_HIDDEN_P)) {
2023 // XXX if(G.qual & LR_SHIFTKEY) deselectall_posearmature(OBACT, 2, 0); // 2 is clear active tag
2024 // else deselectall_posearmature(OBACT, 0, 0);
2025 bone->flag |= BONE_SELECTED|BONE_ACTIVE;
2032 if(ob && ob->data==arm) {
2033 if (bone->flag & BONE_SELECTED) return 1;
2040 /* ebones only draw in editmode armature */
2041 static int tree_element_active_ebone(TreeElement *te, TreeStoreElem *tselem, int set)
2043 EditBone *ebone= te->directdata;
2044 // int shift= 0; // XXX
2047 if(!(ebone->flag & BONE_HIDDEN_A)) {
2049 // XXX if(shift) deselectall_armature(2, 0); // only clear active tag
2050 // else deselectall_armature(0, 0); // deselect
2052 ebone->flag |= BONE_SELECTED|BONE_ROOTSEL|BONE_TIPSEL|BONE_ACTIVE;
2054 if(ebone->parent && (ebone->flag & BONE_CONNECTED)) ebone->parent->flag |= BONE_TIPSEL;
2059 if (ebone->flag & BONE_SELECTED) return 1;
2064 static int tree_element_active_modifier(TreeElement *te, TreeStoreElem *tselem, int set)
2067 // XXX extern_set_butspace(F9KEY, 0);
2073 static int tree_element_active_psys(TreeElement *te, TreeStoreElem *tselem, int set)
2076 // Object *ob= (Object *)tselem->id;
2077 // ParticleSystem *psys= te->directdata;
2079 // XXX PE_change_act_psys(ob, psys);
2080 // XXX extern_set_butspace(F7KEY, 0);
2086 static int tree_element_active_constraint(TreeElement *te, TreeStoreElem *tselem, int set)
2089 // XXX extern_set_butspace(F7KEY, 0);
2095 static int tree_element_active_text(Scene *scene, SpaceOops *soops, TreeElement *te, int set)
2101 /* generic call for ID data check or make/check active in UI */
2102 static int tree_element_active(Scene *scene, SpaceOops *soops, TreeElement *te, int set)
2105 switch(te->idcode) {
2107 return tree_element_active_material(scene, soops, te, set);
2109 return tree_element_active_world(scene, soops, te, set);
2111 return tree_element_active_lamp(scene, soops, te, set);
2113 return tree_element_active_texture(scene, soops, te, set);
2115 return tree_element_active_text(scene, soops, te, set);
2120 static int tree_element_active_pose(TreeElement *te, TreeStoreElem *tselem, int set)
2122 Object *ob= (Object *)tselem->id;
2125 // XXX if(obedit) exit_editmode(EM_FREEDATA|EM_FREEUNDO|EM_WAITCURSOR);
2126 // if(ob->flag & OB_POSEMODE) exit_posemode();
2127 // else enter_posemode();
2130 if(ob->flag & OB_POSEMODE) return 1;
2135 static int tree_element_active_sequence(TreeElement *te, TreeStoreElem *tselem, int set)
2137 Sequence *seq= (Sequence*) te->directdata;
2140 // XXX select_single_seq(seq, 1);
2143 if(seq->flag & SELECT)
2149 static int tree_element_active_sequence_dup(Scene *scene, TreeElement *te, TreeStoreElem *tselem, int set)
2152 Editing *ed= seq_give_editing(scene, FALSE);
2154 seq= (Sequence*)te->directdata;
2156 if(seq->flag & SELECT)
2161 // XXX select_single_seq(seq, 1);
2162 p= ed->seqbasep->first;
2164 if((!p->strip) || (!p->strip->stripdata) || (!p->strip->stripdata->name)) {
2169 // if(!strcmp(p->strip->stripdata->name, seq->strip->stripdata->name))
2170 // XXX select_single_seq(p, 0);
2176 /* generic call for non-id data to make/check active in UI */
2177 /* Context can be NULL when set==0 */
2178 static int tree_element_type_active(bContext *C, Scene *scene, SpaceOops *soops, TreeElement *te, TreeStoreElem *tselem, int set)
2181 switch(tselem->type) {
2183 return tree_element_active_defgroup(scene, te, tselem, set);
2185 return tree_element_active_bone(scene, te, tselem, set);
2187 return tree_element_active_ebone(te, tselem, set);
2189 return tree_element_active_modifier(te, tselem, set);
2191 if(set) tree_element_set_active_object(C, scene, soops, te);
2192 else if(tselem->id==(ID *)OBACT) return 1;
2194 case TSE_LINKED_PSYS:
2195 return tree_element_active_psys(te, tselem, set);
2198 return tree_element_active_pose(te, tselem, set);
2200 case TSE_POSE_CHANNEL:
2201 return tree_element_active_posechannel(scene, te, tselem, set);
2202 case TSE_CONSTRAINT:
2203 return tree_element_active_constraint(te, tselem, set);
2205 return tree_element_active_renderlayer(te, tselem, set);
2207 return tree_element_active_posegroup(scene, te, tselem, set);
2209 return tree_element_active_sequence(te, tselem, set);
2211 case TSE_SEQUENCE_DUP:
2212 return tree_element_active_sequence_dup(scene, te, tselem, set);
2218 static int do_outliner_mouse_event(bContext *C, Scene *scene, ARegion *ar, SpaceOops *soops, TreeElement *te, short event, float *mval)
2220 int shift= 0, ctrl= 0; // XXX
2222 if(mval[1]>te->ys && mval[1]<te->ys+OL_H) {
2223 TreeStoreElem *tselem= TREESTORE(te);
2226 /* open close icon, three things to check */
2227 if(event==RETKEY || event==PADENTER) openclose= 1; // enter opens/closes always
2228 else if((te->flag & TE_ICONROW)==0) { // hidden icon, no open/close
2229 if( mval[0]>te->xs && mval[0]<te->xs+OL_X) openclose= 1;
2234 /* all below close/open? */
2236 tselem->flag &= ~TSE_CLOSED;
2237 outliner_set_flag(soops, &te->subtree, TSE_CLOSED, !outliner_has_one_flag(soops, &te->subtree, TSE_CLOSED, 1));
2240 if(tselem->flag & TSE_CLOSED) tselem->flag &= ~TSE_CLOSED;
2241 else tselem->flag |= TSE_CLOSED;
2247 /* name and first icon */
2248 else if(mval[0]>te->xs && mval[0]<te->xend) {
2250 /* activate a name button? */
2251 if(event==LEFTMOUSE) {
2254 if(ELEM10(tselem->type, TSE_ANIM_DATA, TSE_NLA, TSE_DEFGROUP_BASE, TSE_CONSTRAINT_BASE, TSE_MODIFIER_BASE, TSE_SCRIPT_BASE, TSE_POSE_BASE, TSE_POSEGRP_BASE, TSE_R_LAYER_BASE, TSE_R_PASS))
2255 error("Cannot edit builtin name");
2256 else if(ELEM3(tselem->type, TSE_SEQUENCE, TSE_SEQ_STRIP, TSE_SEQUENCE_DUP))
2257 error("Cannot edit sequence name");
2258 else if(tselem->id->lib) {
2259 // XXX error_libdata();
2260 } else if(te->idcode == ID_LI && te->parent) {
2261 error("Cannot edit the path of an indirectly linked library");
2263 tselem->flag |= TSE_TEXTBUT;
2266 /* always makes active object */
2267 if(tselem->type!=TSE_SEQUENCE && tselem->type!=TSE_SEQ_STRIP && tselem->type!=TSE_SEQUENCE_DUP)
2268 tree_element_set_active_object(C, scene, soops, te);
2270 if(tselem->type==0) { // the lib blocks
2272 if(te->idcode==ID_SCE) {
2273 if(scene!=(Scene *)tselem->id) {
2274 ED_screen_set_scene(C, (Scene *)tselem->id);
2277 else if(ELEM5(te->idcode, ID_ME, ID_CU, ID_MB, ID_LT, ID_AR)) {
2278 // XXX if(obedit) exit_editmode(EM_FREEDATA|EM_FREEUNDO|EM_WAITCURSOR);
2280 // enter_editmode(EM_WAITCURSOR);
2281 // extern_set_butspace(F9KEY, 0);
2283 } else { // rest of types
2284 tree_element_active(scene, soops, te, 1);
2288 else tree_element_type_active(C, scene, soops, te, tselem, 1);
2291 else if(event==RIGHTMOUSE) {
2292 /* select object that's clicked on and popup context menu */
2293 if (!(tselem->flag & TSE_SELECTED)) {
2295 if ( outliner_has_one_flag(soops, &soops->tree, TSE_SELECTED, 1) )
2296 outliner_set_flag(soops, &soops->tree, TSE_SELECTED, 0);
2298 tselem->flag |= TSE_SELECTED;
2299 /* redraw, same as outliner_select function */
2300 soops->storeflag |= SO_TREESTORE_REDRAW;
2301 // XXX screen_swapbuffers();
2304 outliner_operation_menu(scene, ar, soops);
2310 for(te= te->subtree.first; te; te= te->next) {
2311 if(do_outliner_mouse_event(C, scene, ar, soops, te, event, mval)) return 1;
2316 /* event can enterkey, then it opens/closes */
2317 static int outliner_activate_click(bContext *C, wmOperator *op, wmEvent *event)
2319 Scene *scene= CTX_data_scene(C);
2320 ARegion *ar= CTX_wm_region(C);
2321 SpaceOops *soops= (SpaceOops*)CTX_wm_space_data(C);
2325 UI_view2d_region_to_view(&ar->v2d, event->x - ar->winrct.xmin, event->y - ar->winrct.ymin, fmval, fmval+1);
2327 for(te= soops->tree.first; te; te= te->next) {
2328 if(do_outliner_mouse_event(C, scene, ar, soops, te, event->type, fmval)) break;
2332 BIF_undo_push("Outliner click event");
2335 short selecting= -1;
2338 /* get row number - 100 here is just a dummy value since we don't need the column */
2339 UI_view2d_listview_view_to_cell(&ar->v2d, 1000, OL_H, 0.0f, 0.0f,
2340 fmval[0], fmval[1], NULL, &row);
2342 /* select relevant row */
2343 outliner_select(soops, &soops->tree, &row, &selecting);
2345 // XXX old flag found in old code, do we still use this?
2346 //soops->storeflag |= SO_TREESTORE_REDRAW;
2348 BIF_undo_push("Outliner selection event");
2351 // XXX need proper notifiers here instead
2352 ED_region_tag_redraw(ar);
2354 return OPERATOR_FINISHED;
2357 void OUTLINER_OT_activate_click(wmOperatorType *ot)
2359 ot->name= "Activate Click";
2360 ot->idname= "OUTLINER_OT_activate_click";
2362 ot->invoke= outliner_activate_click;
2364 ot->poll= ED_operator_outliner_active;
2369 /* recursive helper for function below */
2370 static void outliner_set_coordinates_element(SpaceOops *soops, TreeElement *te, int startx, int *starty)
2372 TreeStoreElem *tselem= TREESTORE(te);
2374 /* store coord and continue, we need coordinates for elements outside view too */
2375 te->xs= (float)startx;
2376 te->ys= (float)(*starty);
2379 if((tselem->flag & TSE_CLOSED)==0) {
2381 for(ten= te->subtree.first; ten; ten= ten->next) {
2382 outliner_set_coordinates_element(soops, ten, startx+OL_X, starty);
2388 /* to retrieve coordinates with redrawing the entire tree */
2389 static void outliner_set_coordinates(ARegion *ar, SpaceOops *soops)
2392 int starty= (int)(ar->v2d.tot.ymax)-OL_H;
2395 for(te= soops->tree.first; te; te= te->next) {
2396 outliner_set_coordinates_element(soops, te, startx, &starty);
2400 static TreeElement *outliner_find_id(SpaceOops *soops, ListBase *lb, ID *id)
2402 TreeElement *te, *tes;
2403 TreeStoreElem *tselem;
2405 for(te= lb->first; te; te= te->next) {
2406 tselem= TREESTORE(te);
2407 if(tselem->type==0) {
2408 if(tselem->id==id) return te;
2409 /* only deeper on scene or object */
2410 if( te->idcode==ID_OB || te->idcode==ID_SCE) {
2411 tes= outliner_find_id(soops, &te->subtree, id);
2419 static int outliner_show_active_exec(bContext *C, wmOperator *op)
2421 SpaceOops *so= (SpaceOops *)CTX_wm_space_data(C);
2422 Scene *scene= CTX_data_scene(C);
2423 ARegion *ar= CTX_wm_region(C);
2424 View2D *v2d= &ar->v2d;
2429 // TODO: make this get this info from context instead...
2431 return OPERATOR_CANCELLED;
2433 te= outliner_find_id(so, &so->tree, (ID *)OBACT);
2435 /* make te->ys center of view */
2436 ytop= (int)(te->ys + (v2d->mask.ymax - v2d->mask.ymin)/2);
2437 if (ytop>0) ytop= 0;
2439 v2d->cur.ymax= (float)ytop;
2440 v2d->cur.ymin= (float)(ytop-(v2d->mask.ymax - v2d->mask.ymin));
2442 /* make te->xs ==> te->xend center of view */
2443 xdelta = (int)(te->xs - v2d->cur.xmin);
2444 v2d->cur.xmin += xdelta;
2445 v2d->cur.xmax += xdelta;
2447 so->storeflag |= SO_TREESTORE_REDRAW;
2450 // XXX need proper notifiers here instead
2451 ED_region_tag_redraw(ar);
2453 return OPERATOR_FINISHED;
2456 void OUTLINER_OT_show_active(wmOperatorType *ot)
2459 ot->name= "Show Active";
2460 ot->idname= "OUTLINER_OT_show_active";
2461 ot->description= "Adjust the view so that the active Object is shown centered.";
2464 ot->exec= outliner_show_active_exec;
2465 ot->poll= ED_operator_outliner_active;
2467 ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
2470 /* find next element that has this name */
2471 static TreeElement *outliner_find_named(SpaceOops *soops, ListBase *lb, char *name, int flags, TreeElement *prev, int *prevFound)
2473 TreeElement *te, *tes;
2475 for (te= lb->first; te; te= te->next) {
2478 /* determine if match */
2480 found= BLI_strcasestr(te->name, name)!=NULL;
2481 else if(flags==OL_FIND_CASE)
2482 found= strstr(te->name, name)!=NULL;
2483 else if(flags==OL_FIND_COMPLETE)
2484 found= BLI_strcasecmp(te->name, name)==0;
2486 found= strcmp(te->name, name)==0;
2489 /* name is right, but is element the previous one? */
2491 if ((te != prev) && (*prevFound))
2501 tes= outliner_find_named(soops, &te->subtree, name, flags, prev, prevFound);
2505 /* nothing valid found */
2509 /* tse is not in the treestore, we use its contents to find a match */
2510 static TreeElement *outliner_find_tse(SpaceOops *soops, TreeStoreElem *tse)
2512 TreeStore *ts= soops->treestore;
2513 TreeStoreElem *tselem;
2516 if(tse->id==NULL) return NULL;
2518 /* check if 'tse' is in treestore */
2520 for(a=0; a<ts->usedelem; a++, tselem++) {
2521 if((tse->type==0 && tselem->type==0) || (tselem->type==tse->type && tselem->nr==tse->nr)) {
2522 if(tselem->id==tse->id) {
2528 return outliner_find_tree_element(&soops->tree, a);
2534 /* Called to find an item based on name.
2536 void outliner_find_panel(Scene *scene, ARegion *ar, SpaceOops *soops, int again, int flags)
2538 TreeElement *te= NULL;
2539 TreeElement *last_find;
2540 TreeStoreElem *tselem;
2541 int ytop, xdelta, prevFound=0;
2544 /* get last found tree-element based on stored search_tse */
2545 last_find= outliner_find_tse(soops, &soops->search_tse);
2547 /* determine which type of search to do */
2548 if (again && last_find) {
2549 /* no popup panel - previous + user wanted to search for next after previous */
2550 BLI_strncpy(name, soops->search_string, 33);
2551 flags= soops->search_flags;
2553 /* try to find matching element */
2554 te= outliner_find_named(soops, &soops->tree, name, flags, last_find, &prevFound);
2556 /* no more matches after previous, start from beginning again */
2558 te= outliner_find_named(soops, &soops->tree, name, flags, last_find, &prevFound);
2562 /* pop up panel - no previous, or user didn't want search after previous */
2564 // XXX if (sbutton(name, 0, sizeof(name)-1, "Find: ") && name[0]) {
2565 // te= outliner_find_named(soops, &soops->tree, name, flags, NULL, &prevFound);
2567 // else return; /* XXX RETURN! XXX */
2570 /* do selection and reveal */
2572 tselem= TREESTORE(te);
2574 /* expand branches so that it will be visible, we need to get correct coordinates */
2575 if( outliner_open_back(soops, te))
2576 outliner_set_coordinates(ar, soops);
2578 /* deselect all visible, and select found element */
2579 outliner_set_flag(soops, &soops->tree, TSE_SELECTED, 0);
2580 tselem->flag |= TSE_SELECTED;
2582 /* make te->ys center of view */
2583 ytop= (int)(te->ys + (ar->v2d.mask.ymax-ar->v2d.mask.ymin)/2);
2585 ar->v2d.cur.ymax= (float)ytop;
2586 ar->v2d.cur.ymin= (float)(ytop-(ar->v2d.mask.ymax-ar->v2d.mask.ymin));
2588 /* make te->xs ==> te->xend center of view */
2589 xdelta = (int)(te->xs - ar->v2d.cur.xmin);
2590 ar->v2d.cur.xmin += xdelta;
2591 ar->v2d.cur.xmax += xdelta;
2593 /* store selection */
2594 soops->search_tse= *tselem;
2596 BLI_strncpy(soops->search_string, name, 33);
2597 soops->search_flags= flags;
2600 soops->storeflag |= SO_TREESTORE_REDRAW;
2604 /* no tree-element found */
2605 error("Not found: %s", name);
2609 /* helper function for tree_element_shwo_hierarchy() - recursively checks whether subtrees have any objects*/
2610 static int subtree_has_objects(SpaceOops *soops, ListBase *lb)
2613 TreeStoreElem *tselem;
2615 for(te= lb->first; te; te= te->next) {
2616 tselem= TREESTORE(te);
2617 if(tselem->type==0 && te->idcode==ID_OB) return 1;
2618 if( subtree_has_objects(soops, &te->subtree)) return 1;
2623 /* recursive helper function for Show Hierarchy operator */
2624 static void tree_element_show_hierarchy(Scene *scene, SpaceOops *soops, ListBase *lb)
2627 TreeStoreElem *tselem;
2629 /* open all object elems, close others */
2630 for(te= lb->first; te; te= te->next) {
2631 tselem= TREESTORE(te);
2633 if(tselem->type==0) {
2634 if(te->idcode==ID_SCE) {
2635 if(tselem->id!=(ID *)scene) tselem->flag |= TSE_CLOSED;
2636 else tselem->flag &= ~TSE_CLOSED;
2638 else if(te->idcode==ID_OB) {
2639 if(subtree_has_objects(soops, &te->subtree)) tselem->flag &= ~TSE_CLOSED;
2640 else tselem->flag |= TSE_CLOSED;
2643 else tselem->flag |= TSE_CLOSED;
2645 if(tselem->flag & TSE_CLOSED); else tree_element_show_hierarchy(scene, soops, &te->subtree);
2649 /* show entire object level hierarchy */
2650 static int outliner_show_hierarchy_exec(bContext *C, wmOperator *op)
2652 SpaceOops *soops= (SpaceOops *)CTX_wm_space_data(C);
2653 ARegion *ar= CTX_wm_region(C);
2654 Scene *scene= CTX_data_scene(C);
2656 /* recursively open/close levels */
2657 tree_element_show_hierarchy(scene, soops, &soops->tree);
2659 // XXX need proper notifiers here instead
2660 ED_region_tag_redraw(ar);
2662 return OPERATOR_FINISHED;
2665 void OUTLINER_OT_show_hierarchy(wmOperatorType *ot)
2668 ot->name= "Show Hierarchy";
2669 ot->idname= "OUTLINER_OT_show_hierarchy";
2670 ot->description= "Open all object entries and close all others.";
2673 ot->exec= outliner_show_hierarchy_exec;
2674 ot->poll= ED_operator_outliner_active; // TODO: shouldn't be allowed in RNA views...
2676 ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
2679 void outliner_select(SpaceOops *soops, ListBase *lb, int *index, short *selecting)
2682 TreeStoreElem *tselem;
2684 for (te= lb->first; te && *index >= 0; te=te->next, (*index)--) {
2685 tselem= TREESTORE(te);
2687 /* if we've encountered the right item, set its 'Outliner' selection status */
2689 /* this should be the last one, so no need to do anything with index */
2690 if ((te->flag & TE_ICONROW)==0) {
2691 /* -1 value means toggle testing for now... */
2692 if (*selecting == -1) {
2693 if (tselem->flag & TSE_SELECTED)
2701 tselem->flag |= TSE_SELECTED;
2703 tselem->flag &= ~TSE_SELECTED;
2706 else if ((tselem->flag & TSE_CLOSED)==0) {
2707 /* Only try selecting sub-elements if we haven't hit the right element yet
2710 * Index must be reduced before supplying it to the sub-tree to try to do
2711 * selection, however, we need to increment it again for the next loop to
2712 * function correctly
2715 outliner_select(soops, &te->subtree, index, selecting);
2721 /* ************ SELECTION OPERATIONS ********* */
2723 static void set_operation_types(SpaceOops *soops, ListBase *lb,
2730 TreeStoreElem *tselem;
2732 for(te= lb->first; te; te= te->next) {
2733 tselem= TREESTORE(te);
2734 if(tselem->flag & TSE_SELECTED) {
2736 if(tselem->type==TSE_SEQUENCE)
2737 *datalevel= TSE_SEQUENCE;
2738 else if(tselem->type==TSE_SEQ_STRIP)
2739 *datalevel= TSE_SEQ_STRIP;
2740 else if(tselem->type==TSE_SEQUENCE_DUP)
2741 *datalevel= TSE_SEQUENCE_DUP;
2742 else if(*datalevel!=tselem->type) *datalevel= -1;
2745 int idcode= GS(tselem->id->name);
2754 case ID_ME: case ID_CU: case ID_MB: case ID_LT:
2755 case ID_LA: case ID_AR: case ID_CA:
2756 case ID_MA: case ID_TE: case ID_IP: case ID_IM:
2757 case ID_SO: case ID_KE: case ID_WO: case ID_AC:
2758 case ID_NLA: case ID_TXT: case ID_GR:
2759 if(*idlevel==0) *idlevel= idcode;
2760 else if(*idlevel!=idcode) *idlevel= -1;
2765 if((tselem->flag & TSE_CLOSED)==0) {
2766 set_operation_types(soops, &te->subtree,
2767 scenelevel, objectlevel, idlevel, datalevel);
2772 static void unlink_material_cb(TreeElement *te, TreeStoreElem *tsep, TreeStoreElem *tselem)
2774 Material **matar=NULL;
2777 if( GS(tsep->id->name)==ID_OB) {
2778 Object *ob= (Object *)tsep->id;
2782 else if( GS(tsep->id->name)==ID_ME) {
2783 Mesh *me= (Mesh *)tsep->id;
2787 else if( GS(tsep->id->name)==ID_CU) {
2788 Curve *cu= (Curve *)tsep->id;
2792 else if( GS(tsep->id->name)==ID_MB) {
2793 MetaBall *mb= (MetaBall *)tsep->id;
2798 for(a=0; a<totcol; a++) {
2799 if(a==te->index && matar[a]) {
2806 static void unlink_texture_cb(TreeElement *te, TreeStoreElem *tsep, TreeStoreElem *tselem)
2811 if( GS(tsep->id->name)==ID_MA) {
2812 Material *ma= (Material *)tsep->id;
2815 else if( GS(tsep->id->name)==ID_LA) {
2816 Lamp *la= (Lamp *)tsep->id;
2819 else if( GS(tsep->id->name)==ID_WO) {
2820 World *wrld= (World *)tsep->id;
2825 for(a=0; a<MAX_MTEX; a++) {
2826 if(a==te->index && mtex[a]) {
2828 mtex[a]->tex->id.us--;
2835 static void unlink_group_cb(TreeElement *te, TreeStoreElem *tsep, TreeStoreElem *tselem)
2837 Group *group= (Group *)tselem->id;
2840 if( GS(tsep->id->name)==ID_OB) {
2841 Object *ob= (Object *)tsep->id;
2842 ob->dup_group= NULL;
2847 unlink_group(group);
2851 static void outliner_do_libdata_operation(SpaceOops *soops, ListBase *lb,
2852 void (*operation_cb)(TreeElement *, TreeStoreElem *, TreeStoreElem *))
2855 TreeStoreElem *tselem;
2857 for(te=lb->first; te; te= te->next) {
2858 tselem= TREESTORE(te);
2859 if(tselem->flag & TSE_SELECTED) {
2860 if(tselem->type==0) {
2861 TreeStoreElem *tsep= TREESTORE(te->parent);
2862 operation_cb(te, tsep, tselem);
2865 if((tselem->flag & TSE_CLOSED)==0) {