svn merge -r 20651:20822 https://svn.blender.org/svnroot/bf-blender/branches/blender2...
[blender.git] / source / blender / editors / space_buttons / buttons_context.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) 2009 Blender Foundation.
21  * All rights reserved.
22  *
23  * Contributor(s): Blender Foundation
24  *
25  * ***** END GPL LICENSE BLOCK *****
26  */
27
28 #include <stdlib.h>
29 #include <string.h>
30
31 #include "MEM_guardedalloc.h"
32
33 #include "DNA_armature_types.h"
34 #include "DNA_lamp_types.h"
35 #include "DNA_material_types.h"
36 #include "DNA_modifier_types.h"
37 #include "DNA_object_types.h"
38 #include "DNA_scene_types.h"
39 #include "DNA_screen_types.h"
40 #include "DNA_space_types.h"
41 #include "DNA_particle_types.h"
42 #include "DNA_texture_types.h"
43 #include "DNA_world_types.h"
44
45 #include "BLI_listbase.h"
46
47 #include "BKE_context.h"
48 #include "BKE_material.h"
49 #include "BKE_modifier.h"
50 #include "BKE_particle.h"
51 #include "BKE_screen.h"
52 #include "BKE_utildefines.h"
53 #include "BKE_world.h"
54
55 #include "RNA_access.h"
56
57 #include "ED_screen.h"
58
59 #include "UI_interface.h"
60 #include "UI_resources.h"
61
62 #include "buttons_intern.h"     // own include
63
64 typedef struct ButsContextPath {
65         PointerRNA ptr[8];
66         int len;
67         int worldtex;
68 } ButsContextPath;
69
70 static int set_pointer_type(ButsContextPath *path, bContextDataResult *result, StructRNA *type)
71 {
72         PointerRNA *ptr;
73         int a;
74
75         for(a=0; a<path->len; a++) {
76                 ptr= &path->ptr[a];
77
78                 if(RNA_struct_is_a(ptr->type, type)) {
79                         CTX_data_pointer_set(result, ptr->id.data, ptr->type, ptr->data);
80                         return 1;
81                 }
82         }
83
84         return 0;
85 }
86
87 static PointerRNA *get_pointer_type(ButsContextPath *path, StructRNA *type)
88 {
89         PointerRNA *ptr;
90         int a;
91
92         for(a=0; a<path->len; a++) {
93                 ptr= &path->ptr[a];
94
95                 if(RNA_struct_is_a(ptr->type, type))
96                         return ptr;
97         }
98
99         return NULL;
100 }
101
102 /************************* Creating the Path ************************/
103
104 static int buttons_context_path_scene(ButsContextPath *path)
105 {
106         PointerRNA *ptr= &path->ptr[path->len-1];
107
108         /* this one just verifies */
109         return RNA_struct_is_a(ptr->type, &RNA_Scene);
110 }
111
112 static int buttons_context_path_world(ButsContextPath *path)
113 {
114         Scene *scene;
115         PointerRNA *ptr= &path->ptr[path->len-1];
116
117         /* if we already have a (pinned) world, we're done */
118         if(RNA_struct_is_a(ptr->type, &RNA_World)) {
119                 return 1;
120         }
121         /* if we have a scene, use the scene's world */
122         else if(buttons_context_path_scene(path)) {
123                 scene= path->ptr[path->len-1].data;
124
125                 RNA_id_pointer_create(&scene->world->id, &path->ptr[path->len]);
126                 path->len++;
127
128                 return 1;
129         }
130
131         /* no path to a world possible */
132         return 0;
133 }
134
135
136 static int buttons_context_path_object(ButsContextPath *path)
137 {
138         Scene *scene;
139         Object *ob;
140         PointerRNA *ptr= &path->ptr[path->len-1];
141
142         /* if we already have a (pinned) object, we're done */
143         if(RNA_struct_is_a(ptr->type, &RNA_Object)) {
144                 return 1;
145         }
146         /* if we have a scene, use the scene's active object */
147         else if(buttons_context_path_scene(path)) {
148                 scene= path->ptr[path->len-1].data;
149                 ob= (scene->basact)? scene->basact->object: NULL;
150
151                 if(ob) {
152                         RNA_id_pointer_create(&ob->id, &path->ptr[path->len]);
153                         path->len++;
154
155                         return 1;
156                 }
157         }
158
159         /* no path to a object possible */
160         return 0;
161 }
162
163 static int buttons_context_path_data(ButsContextPath *path, int type)
164 {
165         Object *ob;
166         PointerRNA *ptr= &path->ptr[path->len-1];
167
168         /* if we already have a data, we're done */
169         if(RNA_struct_is_a(ptr->type, &RNA_Mesh) && (type == -1 || type == OB_MESH)) return 1;
170         else if(RNA_struct_is_a(ptr->type, &RNA_Curve) && (type == -1 || ELEM3(type, OB_CURVE, OB_SURF, OB_FONT))) return 1;
171         else if(RNA_struct_is_a(ptr->type, &RNA_Armature) && (type == -1 || type == OB_ARMATURE)) return 1;
172         else if(RNA_struct_is_a(ptr->type, &RNA_MetaBall) && (type == -1 || type == OB_MBALL)) return 1;
173         else if(RNA_struct_is_a(ptr->type, &RNA_Lattice) && (type == -1 || type == OB_LATTICE)) return 1;
174         else if(RNA_struct_is_a(ptr->type, &RNA_Camera) && (type == -1 || type == OB_CAMERA)) return 1;
175         else if(RNA_struct_is_a(ptr->type, &RNA_Lamp) && (type == -1 || type == OB_LAMP)) return 1;
176         /* try to get an object in the path, no pinning supported here */
177         else if(buttons_context_path_object(path)) {
178                 ob= path->ptr[path->len-1].data;
179
180                 if(ob && (type == -1 || type == ob->type)) {
181                         RNA_id_pointer_create(ob->data, &path->ptr[path->len]);
182                         path->len++;
183
184                         return 1;
185                 }
186         }
187
188         /* no path to data possible */
189         return 0;
190 }
191
192 static int buttons_context_path_modifier(ButsContextPath *path)
193 {
194         Object *ob;
195
196         if(buttons_context_path_object(path)) {
197                 ob= path->ptr[path->len-1].data;
198
199                 if(ob && ELEM4(ob->type, OB_MESH, OB_CURVE, OB_FONT, OB_SURF))
200                         return 1;
201         }
202
203         return 0;
204 }
205
206 static int buttons_context_path_material(ButsContextPath *path)
207 {
208         Object *ob;
209         PointerRNA *ptr= &path->ptr[path->len-1];
210         Material *ma;
211
212         /* if we already have a (pinned) material, we're done */
213         if(RNA_struct_is_a(ptr->type, &RNA_Material)) {
214                 return 1;
215         }
216         /* if we have an object, use the object material slot */
217         else if(buttons_context_path_object(path)) {
218                 ob= path->ptr[path->len-1].data;
219
220                 if(ob && ob->type && (ob->type<OB_LAMP)) {
221                         ma= give_current_material(ob, ob->actcol);
222                         RNA_id_pointer_create(&ma->id, &path->ptr[path->len]);
223                         path->len++;
224                         return 1;
225                 }
226         }
227
228         /* no path to a material possible */
229         return 0;
230 }
231
232 static Bone *find_active_bone(Bone *bone)
233 {
234         Bone *active;
235
236         for(; bone; bone=bone->next) {
237                 if(bone->flag & BONE_ACTIVE)
238                         return bone;
239
240                 active= find_active_bone(bone->childbase.first);
241                 if(active)
242                         return active;
243         }
244
245         return NULL;
246 }
247
248 static int buttons_context_path_bone(ButsContextPath *path)
249 {
250         bArmature *arm;
251         Bone *bone;
252
253         /* if we have an armature, get the active bone */
254         if(buttons_context_path_data(path, OB_ARMATURE)) {
255                 arm= path->ptr[path->len-1].data;
256                 bone= find_active_bone(arm->bonebase.first);
257
258                 if(bone) {
259                         RNA_pointer_create(&arm->id, &RNA_Bone, bone, &path->ptr[path->len]);
260                         path->len++;
261                         return 1;
262                 }
263         }
264
265         /* no path to a bone possible */
266         return 0;
267 }
268
269 static int buttons_context_path_particle(ButsContextPath *path)
270 {
271         Object *ob;
272         ParticleSystem *psys;
273
274         /* if we have an object, get the active particle system */
275         if(buttons_context_path_object(path)) {
276                 ob= path->ptr[path->len-1].data;
277
278                 if(ob && ob->type == OB_MESH) {
279                         psys= psys_get_current(ob);
280
281                         RNA_pointer_create(&ob->id, &RNA_ParticleSystem, psys, &path->ptr[path->len]);
282                         path->len++;
283                         return 1;
284                 }
285         }
286
287         /* no path to a particle system possible */
288         return 0;
289 }
290
291 static int buttons_context_path_texture(ButsContextPath *path)
292 {
293         Material *ma;
294         Lamp *la;
295         World *wo;
296         MTex *mtex;
297         Tex *tex;
298         PointerRNA *ptr= &path->ptr[path->len-1];
299
300         /* if we already have a (pinned) texture, we're done */
301         if(RNA_struct_is_a(ptr->type, &RNA_Texture)) {
302                 return 1;
303         }
304         /* try world */
305         else if(path->worldtex && buttons_context_path_world(path)) {
306                 wo= path->ptr[path->len-1].data;
307
308                 if(wo) {
309                         mtex= wo->mtex[(int)wo->texact];
310                         tex= (mtex)? mtex->tex: NULL;
311
312                         RNA_id_pointer_create(&tex->id, &path->ptr[path->len]);
313                         path->len++;
314                         return 1;
315                 }
316         }
317         /* try material */
318         else if(buttons_context_path_material(path)) {
319                 ma= path->ptr[path->len-1].data;
320
321                 if(ma) {
322                         mtex= ma->mtex[(int)ma->texact];
323                         tex= (mtex)? mtex->tex: NULL;
324
325                         RNA_id_pointer_create(&tex->id, &path->ptr[path->len]);
326                         path->len++;
327                         return 1;
328                 }
329         }
330         /* try lamp */
331         else if(buttons_context_path_data(path, OB_LAMP)) {
332                 la= path->ptr[path->len-1].data;
333
334                 if(la) {
335                         mtex= la->mtex[(int)la->texact];
336                         tex= (mtex)? mtex->tex: NULL;
337
338                         RNA_id_pointer_create(&tex->id, &path->ptr[path->len]);
339                         path->len++;
340                         return 1;
341                 }
342         }
343         /* TODO: material nodes, brush */
344
345         /* no path to a particle system possible */
346         return 0;
347 }
348
349 static int buttons_context_path(const bContext *C, ButsContextPath *path, int mainb, int worldtex)
350 {
351         SpaceButs *sbuts= (SpaceButs*)CTX_wm_space_data(C);
352         ID *id;
353         int found;
354
355         memset(path, 0, sizeof(*path));
356         path->worldtex= worldtex;
357
358         /* if some ID datablock is pinned, set the root pointer */
359         if(sbuts->pinid) {
360                 id= sbuts->pinid;
361
362                 RNA_id_pointer_create(id, &path->ptr[0]);
363                 path->len++;
364         }
365
366         /* no pinned root, use scene as root */
367         if(path->len == 0) {
368                 id= (ID*)CTX_data_scene(C);
369                 RNA_id_pointer_create(id, &path->ptr[0]);
370                 path->len++;
371         }
372
373         /* now for each buttons context type, we try to construct a path,
374          * tracing back recursively */
375         switch(mainb) {
376                 case BCONTEXT_SCENE:
377                         found= buttons_context_path_scene(path);
378                         break;
379                 case BCONTEXT_WORLD:
380                         found= buttons_context_path_world(path);
381                         break;
382                 case BCONTEXT_OBJECT:
383                 case BCONTEXT_PHYSICS:
384                 case BCONTEXT_CONSTRAINT:
385                         found= buttons_context_path_object(path);
386                         break;
387                 case BCONTEXT_MODIFIER:
388                         found= buttons_context_path_modifier(path);
389                         break;
390                 case BCONTEXT_DATA:
391                         found= buttons_context_path_data(path, -1);
392                         break;
393                 case BCONTEXT_PARTICLE:
394                         found= buttons_context_path_particle(path);
395                         break;
396                 case BCONTEXT_MATERIAL:
397                         found= buttons_context_path_material(path);
398                         break;
399                 case BCONTEXT_TEXTURE:
400                         found= buttons_context_path_texture(path);
401                         break;
402                 case BCONTEXT_BONE:
403                         found= buttons_context_path_bone(path);
404                         break;
405                 default:
406                         found= 0;
407                         break;
408         }
409
410         return found;
411 }
412
413 void buttons_context_compute(const bContext *C, SpaceButs *sbuts)
414 {
415         ButsContextPath *path;
416         PointerRNA *ptr;
417         int a, worldtex, flag= 0;
418
419         if(!sbuts->path)
420                 sbuts->path= MEM_callocN(sizeof(ButsContextPath), "ButsContextPath");
421         
422         path= sbuts->path;
423         worldtex= (sbuts->flag & SB_WORLD_TEX);
424         
425         /* for each context, see if we can compute a valid path to it, if
426          * this is the case, we know we have to display the button */
427         for(a=0; a<BCONTEXT_TOT; a++) {
428                 if(buttons_context_path(C, path, a, worldtex)) {
429                         flag |= (1<<a);
430
431                         /* setting icon for data context */
432                         if(a == BCONTEXT_DATA) {
433                                 ptr= &path->ptr[path->len-1];
434
435                                 if(ptr->type)
436                                         sbuts->dataicon= RNA_struct_ui_icon(ptr->type);
437                                 else
438                                         sbuts->dataicon= ICON_EMPTY_DATA;
439                         }
440                 }
441         }
442
443         /* in case something becomes invalid, change */
444         if((flag & (1 << sbuts->mainb)) == 0) {
445                 if(flag & BCONTEXT_OBJECT) {
446                         sbuts->mainb= BCONTEXT_OBJECT;
447                 }
448                 else {
449                         for(a=0; a<BCONTEXT_TOT; a++) {
450                                 if(flag & (1 << a)) {
451                                         sbuts->mainb= a;
452                                         break;
453                                 }
454                         }
455                 }
456         }
457
458         buttons_context_path(C, path, sbuts->mainb, worldtex);
459
460         if(!(flag & (1 << sbuts->mainb))) {
461                 if(flag & (1 << BCONTEXT_OBJECT))
462                         sbuts->mainb= BCONTEXT_OBJECT;
463                 else
464                         sbuts->mainb= BCONTEXT_SCENE;
465         }
466
467         sbuts->pathflag= flag;
468 }
469
470 /************************* Context Callback ************************/
471
472 int buttons_context(const bContext *C, const char *member, bContextDataResult *result)
473 {
474         SpaceButs *sbuts= (SpaceButs*)CTX_wm_space_data(C);
475         ButsContextPath *path= sbuts?sbuts->path:NULL;
476
477         if(!path)
478                 return 0;
479
480         /* here we handle context, getting data from precomputed path */
481
482         if(CTX_data_equals(member, "world")) {
483                 set_pointer_type(path, result, &RNA_World);
484                 return 1;
485         }
486         else if(CTX_data_equals(member, "object")) {
487                 set_pointer_type(path, result, &RNA_Object);
488                 return 1;
489         }
490         else if(CTX_data_equals(member, "mesh")) {
491                 set_pointer_type(path, result, &RNA_Mesh);
492                 return 1;
493         }
494         else if(CTX_data_equals(member, "armature")) {
495                 set_pointer_type(path, result, &RNA_Armature);
496                 return 1;
497         }
498         else if(CTX_data_equals(member, "lattice")) {
499                 set_pointer_type(path, result, &RNA_Lattice);
500                 return 1;
501         }
502         else if(CTX_data_equals(member, "curve")) {
503                 set_pointer_type(path, result, &RNA_Curve);
504                 return 1;
505         }
506         else if(CTX_data_equals(member, "meta_ball")) {
507                 set_pointer_type(path, result, &RNA_MetaBall);
508                 return 1;
509         }
510         else if(CTX_data_equals(member, "lamp")) {
511                 set_pointer_type(path, result, &RNA_Lamp);
512                 return 1;
513         }
514         else if(CTX_data_equals(member, "camera")) {
515                 set_pointer_type(path, result, &RNA_Camera);
516                 return 1;
517         }
518         else if(CTX_data_equals(member, "material")) {
519                 set_pointer_type(path, result, &RNA_Material);
520                 return 1;
521         }
522         else if(CTX_data_equals(member, "texture")) {
523                 set_pointer_type(path, result, &RNA_Texture);
524                 return 1;
525         }
526         else if(CTX_data_equals(member, "material_slot")) {
527                 PointerRNA *ptr= get_pointer_type(path, &RNA_Object);
528
529                 if(ptr) {
530                         Object *ob= ptr->data;
531
532                         if(ob && ob->type && (ob->type<OB_LAMP))
533                                 CTX_data_pointer_set(result, &ob->id, &RNA_MaterialSlot, ob->mat+ob->actcol-1);
534                 }
535
536                 return 1;
537         }
538         else if(CTX_data_equals(member, "texture_slot")) {
539                 PointerRNA *ptr;
540
541                 if((ptr=get_pointer_type(path, &RNA_Material))) {
542                         Material *ma= ptr->data;
543
544                         if(ma)
545                                 CTX_data_pointer_set(result, &ma->id, &RNA_MaterialTextureSlot, ma->mtex[(int)ma->texact]);
546                 }
547                 else if((ptr=get_pointer_type(path, &RNA_Lamp))) {
548                         Lamp *la= ptr->data;
549
550                         if(la)
551                                 CTX_data_pointer_set(result, &la->id, &RNA_LampTextureSlot, la->mtex[(int)la->texact]);
552                 }
553                 else if((ptr=get_pointer_type(path, &RNA_World))) {
554                         World *wo= ptr->data;
555
556                         if(wo)
557                                 CTX_data_pointer_set(result, &wo->id, &RNA_WorldTextureSlot, wo->mtex[(int)wo->texact]);
558                 }
559                 else if((ptr=get_pointer_type(path, &RNA_Brush))) { /* how to get this into context? */
560                         Brush *br= ptr->data;
561
562                         if(br)
563                                 CTX_data_pointer_set(result, &br->id, &RNA_TextureSlot, br->mtex[(int)br->texact]);
564                 }
565
566                 return 1;
567         }
568         else if(CTX_data_equals(member, "bone")) {
569                 set_pointer_type(path, result, &RNA_Bone);
570                 return 1;
571         }
572         else if(CTX_data_equals(member, "particle_system")) {
573                 set_pointer_type(path, result, &RNA_ParticleSystem);
574                 return 1;
575         }
576         else if(CTX_data_equals(member, "cloth")) {
577                 set_pointer_type(path, result, &RNA_ClothModifier);
578                 return 1;
579         }
580         else if(CTX_data_equals(member, "soft_body")) {
581                 PointerRNA *ptr= get_pointer_type(path, &RNA_Object);
582
583                 if(ptr && ptr->data) {
584                         Object *ob= ptr->data;
585                         CTX_data_pointer_set(result, &ob->id, &RNA_SoftBodySettings, ob->soft);
586                         return 1;
587                 }
588         }
589         else if(CTX_data_equals(member, "fluid")) {
590                 PointerRNA *ptr= get_pointer_type(path, &RNA_Object);
591
592                 if(ptr && ptr->data) {
593                         Object *ob= ptr->data;
594                         ModifierData *md= modifiers_findByType(ob, eModifierType_Fluidsim);
595                         CTX_data_pointer_set(result, &ob->id, &RNA_FluidSimulationModifier, md);
596                         return 1;
597                 }
598         }
599
600         return 0;
601 }
602
603 /************************* Drawing the Path ************************/
604
605 static void pin_cb(bContext *C, void *arg1, void *arg2)
606 {
607         SpaceButs *sbuts= (SpaceButs*)CTX_wm_space_data(C);
608         ButsContextPath *path= sbuts->path;
609         PointerRNA *ptr;
610         int a;
611
612         if(sbuts->flag & SB_PIN_CONTEXT) {
613                 if(path->len) {
614                         for(a=path->len-1; a>=0; a--) {
615                                 ptr= &path->ptr[a];
616
617                                 if(ptr->id.data) {
618                                         sbuts->pinid= ptr->id.data;
619                                         break;
620                                 }
621                         }
622                 }
623         }
624         else
625                 sbuts->pinid= NULL;
626         
627         ED_area_tag_redraw(CTX_wm_area(C));
628 }
629
630 void buttons_context_draw(const bContext *C, uiLayout *layout)
631 {
632         SpaceButs *sbuts= (SpaceButs*)CTX_wm_space_data(C);
633         ButsContextPath *path= sbuts->path;
634         uiLayout *row;
635         uiBlock *block;
636         uiBut *but;
637         PointerRNA *ptr;
638         PropertyRNA *nameprop;
639         char namebuf[128], *name;
640         int a, icon;
641
642         if(!path)
643                 return;
644
645         row= uiLayoutRow(layout, 0);
646         uiLayoutSetAlignment(row, UI_LAYOUT_ALIGN_LEFT);
647
648         block= uiLayoutGetBlock(row);
649         uiBlockSetEmboss(block, UI_EMBOSSN);
650         but= uiDefIconButBitC(block, ICONTOG, SB_PIN_CONTEXT, 0, (sbuts->flag & SB_PIN_CONTEXT)? ICON_PINNED: ICON_UNPINNED, 0, 0, UI_UNIT_X, UI_UNIT_Y, &sbuts->flag, 0, 0, 0, 0, "Follow context or keep fixed datablock displayed.");
651         uiButSetFunc(but, pin_cb, NULL, NULL);
652
653         for(a=0; a<path->len; a++) {
654                 ptr= &path->ptr[a];
655
656                 if(ptr->data) {
657                         icon= RNA_struct_ui_icon(ptr->type);
658                         nameprop= RNA_struct_name_property(ptr->type);
659
660 #if 0
661                         if(sbuts->mainb != BCONTEXT_SCENE && ptr->type == &RNA_Scene) {
662                                 uiItemL(row, "", icon); /* save some space */
663                         }
664                         else
665 #endif
666                         if(nameprop) {
667                                 name= RNA_property_string_get_alloc(ptr, nameprop, namebuf, sizeof(namebuf));
668
669                                 uiItemL(row, name, icon);
670
671                                 if(name != namebuf)
672                                         MEM_freeN(name);
673                         }
674                         else
675                                 uiItemL(row, "", icon);
676                 }
677         }
678 }
679
680 static void buttons_panel_context(const bContext *C, Panel *pa)
681 {
682         buttons_context_draw(C, pa->layout);
683 }
684
685 void buttons_context_register(ARegionType *art)
686 {
687         PanelType *pt;
688
689         pt= MEM_callocN(sizeof(PanelType), "spacetype buttons panel context");
690         strcpy(pt->idname, "BUTTONS_PT_context");
691         strcpy(pt->label, "Context");
692         pt->draw= buttons_panel_context;
693         BLI_addtail(&art->paneltypes, pt);
694 }
695