2.5
[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 // XXX - place holder, need to get this working
136 static int buttons_context_path_sequencer(ButsContextPath *path)
137 {
138         Scene *scene;
139
140         if(buttons_context_path_scene(path)) {
141                 scene= path->ptr[path->len-1].data;
142
143                 RNA_pointer_create(&scene->id, &RNA_SequenceEditor, scene->ed, &path->ptr[path->len]);
144                 path->len++;
145
146                 return 1;
147         }
148
149         return 0;
150 }
151
152 static int buttons_context_path_object(ButsContextPath *path)
153 {
154         Scene *scene;
155         Object *ob;
156         PointerRNA *ptr= &path->ptr[path->len-1];
157
158         /* if we already have a (pinned) object, we're done */
159         if(RNA_struct_is_a(ptr->type, &RNA_Object)) {
160                 return 1;
161         }
162         /* if we have a scene, use the scene's active object */
163         else if(buttons_context_path_scene(path)) {
164                 scene= path->ptr[path->len-1].data;
165                 ob= (scene->basact)? scene->basact->object: NULL;
166
167                 if(ob) {
168                         RNA_id_pointer_create(&ob->id, &path->ptr[path->len]);
169                         path->len++;
170
171                         return 1;
172                 }
173         }
174
175         /* no path to a object possible */
176         return 0;
177 }
178
179 static int buttons_context_path_data(ButsContextPath *path, int type)
180 {
181         Object *ob;
182         PointerRNA *ptr= &path->ptr[path->len-1];
183
184         /* if we already have a data, we're done */
185         if(RNA_struct_is_a(ptr->type, &RNA_Mesh) && (type == -1 || type == OB_MESH)) return 1;
186         else if(RNA_struct_is_a(ptr->type, &RNA_Curve) && (type == -1 || ELEM3(type, OB_CURVE, OB_SURF, OB_FONT))) return 1;
187         else if(RNA_struct_is_a(ptr->type, &RNA_Armature) && (type == -1 || type == OB_ARMATURE)) return 1;
188         else if(RNA_struct_is_a(ptr->type, &RNA_MetaBall) && (type == -1 || type == OB_MBALL)) return 1;
189         else if(RNA_struct_is_a(ptr->type, &RNA_Lattice) && (type == -1 || type == OB_LATTICE)) return 1;
190         else if(RNA_struct_is_a(ptr->type, &RNA_Camera) && (type == -1 || type == OB_CAMERA)) return 1;
191         else if(RNA_struct_is_a(ptr->type, &RNA_Lamp) && (type == -1 || type == OB_LAMP)) return 1;
192         /* try to get an object in the path, no pinning supported here */
193         else if(buttons_context_path_object(path)) {
194                 ob= path->ptr[path->len-1].data;
195
196                 if(ob && (type == -1 || type == ob->type)) {
197                         RNA_id_pointer_create(ob->data, &path->ptr[path->len]);
198                         path->len++;
199
200                         return 1;
201                 }
202         }
203
204         /* no path to data possible */
205         return 0;
206 }
207
208 static int buttons_context_path_modifier(ButsContextPath *path)
209 {
210         Object *ob;
211
212         if(buttons_context_path_object(path)) {
213                 ob= path->ptr[path->len-1].data;
214
215                 if(ob && ELEM4(ob->type, OB_MESH, OB_CURVE, OB_FONT, OB_SURF))
216                         return 1;
217         }
218
219         return 0;
220 }
221
222 static int buttons_context_path_material(ButsContextPath *path)
223 {
224         Object *ob;
225         PointerRNA *ptr= &path->ptr[path->len-1];
226         Material *ma;
227
228         /* if we already have a (pinned) material, we're done */
229         if(RNA_struct_is_a(ptr->type, &RNA_Material)) {
230                 return 1;
231         }
232         /* if we have an object, use the object material slot */
233         else if(buttons_context_path_object(path)) {
234                 ob= path->ptr[path->len-1].data;
235
236                 if(ob && ob->type && (ob->type<OB_LAMP)) {
237                         ma= give_current_material(ob, ob->actcol);
238                         RNA_id_pointer_create(&ma->id, &path->ptr[path->len]);
239                         path->len++;
240                         return 1;
241                 }
242         }
243
244         /* no path to a material possible */
245         return 0;
246 }
247
248 static Bone *find_active_bone(Bone *bone)
249 {
250         Bone *active;
251
252         for(; bone; bone=bone->next) {
253                 if(bone->flag & BONE_ACTIVE)
254                         return bone;
255
256                 active= find_active_bone(bone->childbase.first);
257                 if(active)
258                         return active;
259         }
260
261         return NULL;
262 }
263
264 static int buttons_context_path_bone(ButsContextPath *path)
265 {
266         bArmature *arm;
267         Bone *bone;
268
269         /* if we have an armature, get the active bone */
270         if(buttons_context_path_data(path, OB_ARMATURE)) {
271                 arm= path->ptr[path->len-1].data;
272                 bone= find_active_bone(arm->bonebase.first);
273
274                 if(bone) {
275                         RNA_pointer_create(&arm->id, &RNA_Bone, bone, &path->ptr[path->len]);
276                         path->len++;
277                         return 1;
278                 }
279         }
280
281         /* no path to a bone possible */
282         return 0;
283 }
284
285 static int buttons_context_path_particle(ButsContextPath *path)
286 {
287         Object *ob;
288         ParticleSystem *psys;
289
290         /* if we have an object, get the active particle system */
291         if(buttons_context_path_object(path)) {
292                 ob= path->ptr[path->len-1].data;
293
294                 if(ob && ob->type == OB_MESH) {
295                         psys= psys_get_current(ob);
296
297                         RNA_pointer_create(&ob->id, &RNA_ParticleSystem, psys, &path->ptr[path->len]);
298                         path->len++;
299                         return 1;
300                 }
301         }
302
303         /* no path to a particle system possible */
304         return 0;
305 }
306
307 static int buttons_context_path_texture(ButsContextPath *path)
308 {
309         Material *ma;
310         Lamp *la;
311         World *wo;
312         MTex *mtex;
313         Tex *tex;
314         PointerRNA *ptr= &path->ptr[path->len-1];
315
316         /* if we already have a (pinned) texture, we're done */
317         if(RNA_struct_is_a(ptr->type, &RNA_Texture)) {
318                 return 1;
319         }
320         /* try world */
321         else if(path->worldtex && buttons_context_path_world(path)) {
322                 wo= path->ptr[path->len-1].data;
323
324                 if(wo) {
325                         mtex= wo->mtex[(int)wo->texact];
326                         tex= (mtex)? mtex->tex: NULL;
327
328                         RNA_id_pointer_create(&tex->id, &path->ptr[path->len]);
329                         path->len++;
330                         return 1;
331                 }
332         }
333         /* try material */
334         else if(buttons_context_path_material(path)) {
335                 ma= path->ptr[path->len-1].data;
336
337                 if(ma) {
338                         mtex= ma->mtex[(int)ma->texact];
339                         tex= (mtex)? mtex->tex: NULL;
340
341                         RNA_id_pointer_create(&tex->id, &path->ptr[path->len]);
342                         path->len++;
343                         return 1;
344                 }
345         }
346         /* try lamp */
347         else if(buttons_context_path_data(path, OB_LAMP)) {
348                 la= path->ptr[path->len-1].data;
349
350                 if(la) {
351                         mtex= la->mtex[(int)la->texact];
352                         tex= (mtex)? mtex->tex: NULL;
353
354                         RNA_id_pointer_create(&tex->id, &path->ptr[path->len]);
355                         path->len++;
356                         return 1;
357                 }
358         }
359         /* TODO: material nodes, brush */
360
361         /* no path to a particle system possible */
362         return 0;
363 }
364
365 static int buttons_context_path(const bContext *C, ButsContextPath *path, int mainb, int worldtex)
366 {
367         SpaceButs *sbuts= (SpaceButs*)CTX_wm_space_data(C);
368         ID *id;
369         int found;
370
371         memset(path, 0, sizeof(*path));
372         path->worldtex= worldtex;
373
374         /* if some ID datablock is pinned, set the root pointer */
375         if(sbuts->pinid) {
376                 id= sbuts->pinid;
377
378                 RNA_id_pointer_create(id, &path->ptr[0]);
379                 path->len++;
380         }
381
382         /* no pinned root, use scene as root */
383         if(path->len == 0) {
384                 id= (ID*)CTX_data_scene(C);
385                 RNA_id_pointer_create(id, &path->ptr[0]);
386                 path->len++;
387         }
388
389         /* now for each buttons context type, we try to construct a path,
390          * tracing back recursively */
391         switch(mainb) {
392                 case BCONTEXT_SCENE:
393                         found= buttons_context_path_scene(path);
394                         break;
395                 case BCONTEXT_WORLD:
396                         found= buttons_context_path_world(path);
397                         break;
398                 case BCONTEXT_SEQUENCER:
399                         found= buttons_context_path_sequencer(path); // XXX - place holder
400                         break;
401                 case BCONTEXT_OBJECT:
402                 case BCONTEXT_PHYSICS:
403                 case BCONTEXT_CONSTRAINT:
404                         found= buttons_context_path_object(path);
405                         break;
406                 case BCONTEXT_MODIFIER:
407                         found= buttons_context_path_modifier(path);
408                         break;
409                 case BCONTEXT_DATA:
410                         found= buttons_context_path_data(path, -1);
411                         break;
412                 case BCONTEXT_PARTICLE:
413                         found= buttons_context_path_particle(path);
414                         break;
415                 case BCONTEXT_MATERIAL:
416                         found= buttons_context_path_material(path);
417                         break;
418                 case BCONTEXT_TEXTURE:
419                         found= buttons_context_path_texture(path);
420                         break;
421                 case BCONTEXT_BONE:
422                         found= buttons_context_path_bone(path);
423                         break;
424                 default:
425                         found= 0;
426                         break;
427         }
428
429         return found;
430 }
431
432 void buttons_context_compute(const bContext *C, SpaceButs *sbuts)
433 {
434         ButsContextPath *path;
435         PointerRNA *ptr;
436         int a, worldtex, flag= 0;
437
438         if(!sbuts->path)
439                 sbuts->path= MEM_callocN(sizeof(ButsContextPath), "ButsContextPath");
440         
441         path= sbuts->path;
442         worldtex= (sbuts->flag & SB_WORLD_TEX);
443         
444         /* for each context, see if we can compute a valid path to it, if
445          * this is the case, we know we have to display the button */
446         for(a=0; a<BCONTEXT_TOT; a++) {
447                 if(buttons_context_path(C, path, a, worldtex)) {
448                         flag |= (1<<a);
449
450                         /* setting icon for data context */
451                         if(a == BCONTEXT_DATA) {
452                                 ptr= &path->ptr[path->len-1];
453
454                                 if(ptr->type)
455                                         sbuts->dataicon= RNA_struct_ui_icon(ptr->type);
456                                 else
457                                         sbuts->dataicon= ICON_EMPTY_DATA;
458                         }
459                 }
460         }
461
462         /* in case something becomes invalid, change */
463         if((flag & (1 << sbuts->mainb)) == 0) {
464                 if(flag & BCONTEXT_OBJECT) {
465                         sbuts->mainb= BCONTEXT_OBJECT;
466                 }
467                 else {
468                         for(a=0; a<BCONTEXT_TOT; a++) {
469                                 if(flag & (1 << a)) {
470                                         sbuts->mainb= a;
471                                         break;
472                                 }
473                         }
474                 }
475         }
476
477         buttons_context_path(C, path, sbuts->mainb, worldtex);
478
479         if(!(flag & (1 << sbuts->mainb))) {
480                 if(flag & (1 << BCONTEXT_OBJECT))
481                         sbuts->mainb= BCONTEXT_OBJECT;
482                 else
483                         sbuts->mainb= BCONTEXT_SCENE;
484         }
485
486         sbuts->pathflag= flag;
487 }
488
489 /************************* Context Callback ************************/
490
491 int buttons_context(const bContext *C, const char *member, bContextDataResult *result)
492 {
493         SpaceButs *sbuts= (SpaceButs*)CTX_wm_space_data(C);
494         ButsContextPath *path= sbuts?sbuts->path:NULL;
495
496         if(!path)
497                 return 0;
498
499         /* here we handle context, getting data from precomputed path */
500
501         if(CTX_data_equals(member, "world")) {
502                 set_pointer_type(path, result, &RNA_World);
503                 return 1;
504         }
505         else if(CTX_data_equals(member, "object")) {
506                 set_pointer_type(path, result, &RNA_Object);
507                 return 1;
508         }
509         else if(CTX_data_equals(member, "mesh")) {
510                 set_pointer_type(path, result, &RNA_Mesh);
511                 return 1;
512         }
513         else if(CTX_data_equals(member, "armature")) {
514                 set_pointer_type(path, result, &RNA_Armature);
515                 return 1;
516         }
517         else if(CTX_data_equals(member, "lattice")) {
518                 set_pointer_type(path, result, &RNA_Lattice);
519                 return 1;
520         }
521         else if(CTX_data_equals(member, "curve")) {
522                 set_pointer_type(path, result, &RNA_Curve);
523                 return 1;
524         }
525         else if(CTX_data_equals(member, "meta_ball")) {
526                 set_pointer_type(path, result, &RNA_MetaBall);
527                 return 1;
528         }
529         else if(CTX_data_equals(member, "lamp")) {
530                 set_pointer_type(path, result, &RNA_Lamp);
531                 return 1;
532         }
533         else if(CTX_data_equals(member, "camera")) {
534                 set_pointer_type(path, result, &RNA_Camera);
535                 return 1;
536         }
537         else if(CTX_data_equals(member, "material")) {
538                 set_pointer_type(path, result, &RNA_Material);
539                 return 1;
540         }
541         else if(CTX_data_equals(member, "texture")) {
542                 set_pointer_type(path, result, &RNA_Texture);
543                 return 1;
544         }
545         else if(CTX_data_equals(member, "material_slot")) {
546                 PointerRNA *ptr= get_pointer_type(path, &RNA_Object);
547
548                 if(ptr) {
549                         Object *ob= ptr->data;
550
551                         if(ob && ob->type && (ob->type<OB_LAMP))
552                                 CTX_data_pointer_set(result, &ob->id, &RNA_MaterialSlot, ob->mat+ob->actcol-1);
553                 }
554
555                 return 1;
556         }
557         else if(CTX_data_equals(member, "texture_slot")) {
558                 PointerRNA *ptr;
559
560                 if((ptr=get_pointer_type(path, &RNA_Material))) {
561                         Material *ma= ptr->data;
562
563                         if(ma)
564                                 CTX_data_pointer_set(result, &ma->id, &RNA_TextureSlot, ma->mtex[(int)ma->texact]);
565                 }
566                 else if((ptr=get_pointer_type(path, &RNA_Lamp))) {
567                         Lamp *la= ptr->data;
568
569                         if(la)
570                                 CTX_data_pointer_set(result, &la->id, &RNA_TextureSlot, la->mtex[(int)la->texact]);
571                 }
572                 else if((ptr=get_pointer_type(path, &RNA_World))) {
573                         World *wo= ptr->data;
574
575                         if(wo)
576                                 CTX_data_pointer_set(result, &wo->id, &RNA_TextureSlot, wo->mtex[(int)wo->texact]);
577                 }
578                 else if((ptr=get_pointer_type(path, &RNA_Brush))) { /* how to get this into context? */
579                         Brush *br= ptr->data;
580
581                         if(br)
582                                 CTX_data_pointer_set(result, &br->id, &RNA_TextureSlot, br->mtex[(int)br->texact]);
583                 }
584
585                 return 1;
586         }
587         else if(CTX_data_equals(member, "bone")) {
588                 set_pointer_type(path, result, &RNA_Bone);
589                 return 1;
590         }
591         else if(CTX_data_equals(member, "particle_system")) {
592                 set_pointer_type(path, result, &RNA_ParticleSystem);
593                 return 1;
594         }
595         else if(CTX_data_equals(member, "cloth")) {
596                 set_pointer_type(path, result, &RNA_ClothModifier);
597                 return 1;
598         }
599         else if(CTX_data_equals(member, "soft_body")) {
600                 PointerRNA *ptr= get_pointer_type(path, &RNA_Object);
601
602                 if(ptr && ptr->data) {
603                         Object *ob= ptr->data;
604                         CTX_data_pointer_set(result, &ob->id, &RNA_SoftBodySettings, ob->soft);
605                         return 1;
606                 }
607         }
608         else if(CTX_data_equals(member, "fluid")) {
609                 PointerRNA *ptr= get_pointer_type(path, &RNA_Object);
610
611                 if(ptr && ptr->data) {
612                         Object *ob= ptr->data;
613                         ModifierData *md= modifiers_findByType(ob, eModifierType_Fluidsim);
614                         CTX_data_pointer_set(result, &ob->id, &RNA_FluidSimulationModifier, md);
615                         return 1;
616                 }
617         }
618
619         return 0;
620 }
621
622 /************************* Drawing the Path ************************/
623
624 static void pin_cb(bContext *C, void *arg1, void *arg2)
625 {
626         SpaceButs *sbuts= (SpaceButs*)CTX_wm_space_data(C);
627         ButsContextPath *path= sbuts->path;
628         PointerRNA *ptr;
629         int a;
630
631         if(sbuts->flag & SB_PIN_CONTEXT) {
632                 if(path->len) {
633                         for(a=path->len-1; a>=0; a--) {
634                                 ptr= &path->ptr[a];
635
636                                 if(ptr->id.data) {
637                                         sbuts->pinid= ptr->id.data;
638                                         break;
639                                 }
640                         }
641                 }
642         }
643         else
644                 sbuts->pinid= NULL;
645         
646         ED_area_tag_redraw(CTX_wm_area(C));
647 }
648
649 void buttons_context_draw(const bContext *C, uiLayout *layout)
650 {
651         SpaceButs *sbuts= (SpaceButs*)CTX_wm_space_data(C);
652         ButsContextPath *path= sbuts->path;
653         uiLayout *row;
654         uiBlock *block;
655         uiBut *but;
656         PointerRNA *ptr;
657         PropertyRNA *nameprop;
658         char namebuf[128], *name;
659         int a, icon;
660
661         if(!path)
662                 return;
663
664         row= uiLayoutRow(layout, 0);
665         uiLayoutSetAlignment(row, UI_LAYOUT_ALIGN_LEFT);
666
667         block= uiLayoutGetBlock(row);
668         uiBlockSetEmboss(block, UI_EMBOSSN);
669         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.");
670         uiButSetFunc(but, pin_cb, NULL, NULL);
671
672         for(a=0; a<path->len; a++) {
673                 ptr= &path->ptr[a];
674
675                 if(ptr->data) {
676                         icon= RNA_struct_ui_icon(ptr->type);
677                         nameprop= RNA_struct_name_property(ptr->type);
678
679 #if 0
680                         if(sbuts->mainb != BCONTEXT_SCENE && ptr->type == &RNA_Scene) {
681                                 uiItemL(row, "", icon); /* save some space */
682                         }
683                         else
684 #endif
685                         if(nameprop) {
686                                 name= RNA_property_string_get_alloc(ptr, nameprop, namebuf, sizeof(namebuf));
687
688                                 uiItemL(row, name, icon);
689
690                                 if(name != namebuf)
691                                         MEM_freeN(name);
692                         }
693                         else
694                                 uiItemL(row, "", icon);
695                 }
696         }
697 }
698
699 static void buttons_panel_context(const bContext *C, Panel *pa)
700 {
701         buttons_context_draw(C, pa->layout);
702 }
703
704 void buttons_context_register(ARegionType *art)
705 {
706         PanelType *pt;
707
708         pt= MEM_callocN(sizeof(PanelType), "spacetype buttons panel context");
709         strcpy(pt->idname, "BUTTONS_PT_context");
710         strcpy(pt->label, "Context");
711         pt->draw= buttons_panel_context;
712         BLI_addtail(&art->paneltypes, pt);
713 }
714