UI:
[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
54 #include "RNA_access.h"
55
56 #include "UI_interface.h"
57 #include "UI_resources.h"
58
59 #include "buttons_intern.h"     // own include
60
61 typedef struct ButsContextPath {
62         PointerRNA ptr[8];
63         int len;
64 } ButsContextPath;
65
66 /************************* Creating the Path ************************/
67
68 static int buttons_context_path_scene(ButsContextPath *path)
69 {
70         PointerRNA *ptr= &path->ptr[path->len-1];
71
72         /* this one just verifies */
73         return RNA_struct_is_a(ptr->type, &RNA_Scene);
74 }
75
76 static int buttons_context_path_world(ButsContextPath *path)
77 {
78         Scene *scene;
79         PointerRNA *ptr= &path->ptr[path->len-1];
80
81         /* if we already have a (pinned) world, we're done */
82         if(RNA_struct_is_a(ptr->type, &RNA_World)) {
83                 return 1;
84         }
85         /* if we have a scene, use the scene's world */
86         else if(buttons_context_path_scene(path)) {
87                 scene= path->ptr[path->len-1].data;
88
89                 RNA_id_pointer_create(&scene->world->id, &path->ptr[path->len]);
90                 path->len++;
91
92                 return 1;
93         }
94
95         /* no path to a world possible */
96         return 0;
97 }
98
99 static int buttons_context_path_object(ButsContextPath *path)
100 {
101         Scene *scene;
102         Object *ob;
103         PointerRNA *ptr= &path->ptr[path->len-1];
104
105         /* if we already have a (pinned) object, we're done */
106         if(RNA_struct_is_a(ptr->type, &RNA_Object)) {
107                 return 1;
108         }
109         /* if we have a scene, use the scene's active object */
110         else if(buttons_context_path_scene(path)) {
111                 scene= path->ptr[path->len-1].data;
112                 ob= (scene->basact)? scene->basact->object: NULL;
113
114                 if(ob) {
115                         RNA_id_pointer_create(&ob->id, &path->ptr[path->len]);
116                         path->len++;
117
118                         return 1;
119                 }
120         }
121
122         /* no path to a object possible */
123         return 0;
124 }
125
126 static int buttons_context_path_data(ButsContextPath *path, int type)
127 {
128         Object *ob;
129
130         /* try to get an object in the path, no pinning supported here */
131         if(buttons_context_path_object(path)) {
132                 ob= path->ptr[path->len-1].data;
133
134                 if(type == -1 || type == ob->type) {
135                         RNA_id_pointer_create(ob->data, &path->ptr[path->len]);
136                         path->len++;
137
138                         return 1;
139                 }
140         }
141
142         /* no path to data possible */
143         return 0;
144 }
145
146 static int buttons_context_path_material(ButsContextPath *path)
147 {
148         Object *ob;
149         PointerRNA *ptr= &path->ptr[path->len-1];
150
151         /* if we already have a (pinned) material, we're done */
152         if(RNA_struct_is_a(ptr->type, &RNA_Material)) {
153                 return 1;
154         }
155         /* if we have an object, use the object material slot */
156         else if(buttons_context_path_object(path)) {
157                 ob= path->ptr[path->len-1].data;
158
159                 if(ob && ob->type && (ob->type<OB_LAMP)) {
160                         RNA_pointer_create(&ob->id, &RNA_MaterialSlot, ob->mat+ob->actcol-1, &path->ptr[path->len]);
161                         path->len++;
162                         return 1;
163                 }
164         }
165
166         /* no path to a material possible */
167         return 0;
168 }
169
170 static Bone *find_active_bone(Bone *bone)
171 {
172         Bone *active;
173
174         for(; bone; bone=bone->next) {
175                 if(bone->flag & BONE_ACTIVE)
176                         return bone;
177
178                 active= find_active_bone(bone->childbase.first);
179                 if(active)
180                         return active;
181         }
182
183         return NULL;
184 }
185
186 static int buttons_context_path_bone(ButsContextPath *path)
187 {
188         bArmature *arm;
189         Bone *bone;
190
191         /* if we have an armature, get the active bone */
192         if(buttons_context_path_data(path, OB_ARMATURE)) {
193                 arm= path->ptr[path->len-1].data;
194                 bone= find_active_bone(arm->bonebase.first);
195
196                 if(bone) {
197                         RNA_pointer_create(&arm->id, &RNA_Bone, bone, &path->ptr[path->len]);
198                         path->len++;
199                         return 1;
200                 }
201         }
202
203         /* no path to a bone possible */
204         return 0;
205 }
206
207 static int buttons_context_path_particle(ButsContextPath *path)
208 {
209         Object *ob;
210         ParticleSystem *psys;
211
212         /* if we have an object, get the active particle system */
213         if(buttons_context_path_object(path)) {
214                 ob= path->ptr[path->len-1].data;
215                 psys= psys_get_current(ob);
216
217                 RNA_pointer_create(&ob->id, &RNA_ParticleSystem, psys, &path->ptr[path->len]);
218                 path->len++;
219                 return 1;
220         }
221
222         /* no path to a particle system possible */
223         return 0;
224 }
225
226 static int buttons_context_path_texture(ButsContextPath *path)
227 {
228         Object *ob;
229         Lamp *la;
230         Material *ma;
231         PointerRNA *ptr= &path->ptr[path->len-1];
232
233         /* if we already have a (pinned) texture, we're done */
234         if(RNA_struct_is_a(ptr->type, &RNA_Texture)) {
235                 return 1;
236         }
237         /* try to get the active material */
238         else if(buttons_context_path_material(path)) {
239                 ptr= &path->ptr[path->len-1];
240
241                 if(RNA_struct_is_a(ptr->type, &RNA_Material)) {
242                         ma= ptr->data;
243                 }
244                 else if(RNA_struct_is_a(ptr->type, &RNA_MaterialSlot)) {
245                         ob= ptr->id.data;
246                         ma= give_current_material(ob, (Material**)ptr->data - ob->mat);
247                 }
248                 else
249                         ma= NULL;
250
251                 if(ma) {
252                         RNA_pointer_create(&ma->id, &RNA_TextureSlot, ma->mtex[(int)ma->texact], &path->ptr[path->len]);
253                         path->len++;
254                         return 1;
255                 }
256         }
257         /* try to get the active lamp */
258         else if(buttons_context_path_data(path, OB_LAMP)) {
259                 la= path->ptr[path->len-1].data;
260
261                 if(la) {
262                         RNA_pointer_create(&la->id, &RNA_TextureSlot, la->mtex[(int)la->texact], &path->ptr[path->len]);
263                         path->len++;
264                         return 1;
265                 }
266         }
267         /* TODO: world, brush */
268
269         /* no path to a particle system possible */
270         return 0;
271 }
272
273 static int buttons_context_path(const bContext *C, ButsContextPath *path)
274 {
275         SpaceButs *sbuts= (SpaceButs*)CTX_wm_space_data(C);
276         ID *id;
277         int found;
278
279         memset(path, 0, sizeof(*path));
280
281         /* if some ID datablock is pinned, set the root pointer */
282         if(sbuts->pinid) {
283                 id= sbuts->pinid;
284
285                 RNA_id_pointer_create(id, &path->ptr[0]);
286                 path->len++;
287         }
288
289         /* no pinned root, use scene as root */
290         if(path->len == 0) {
291                 id= (ID*)CTX_data_scene(C);
292                 RNA_id_pointer_create(id, &path->ptr[0]);
293                 path->len++;
294         }
295
296         /* now for each buttons context type, we try to construct a path,
297          * tracing back recursively */
298         switch(sbuts->mainb) {
299                 case BCONTEXT_SCENE:
300                         found= buttons_context_path_scene(path);
301                         break;
302                 case BCONTEXT_WORLD:
303                         found= buttons_context_path_world(path);
304                         break;
305                 case BCONTEXT_OBJECT:
306                 case BCONTEXT_PHYSICS:
307                 case BCONTEXT_MODIFIER:
308                         found= buttons_context_path_object(path);
309                         break;
310                 case BCONTEXT_DATA:
311                         found= buttons_context_path_data(path, -1);
312                         break;
313                 case BCONTEXT_PARTICLE:
314                         found= buttons_context_path_particle(path);
315                         break;
316                 case BCONTEXT_MATERIAL:
317                         found= buttons_context_path_material(path);
318                         break;
319                 case BCONTEXT_TEXTURE:
320                         found= buttons_context_path_texture(path);
321                         break;
322                 case BCONTEXT_BONE:
323                         found= buttons_context_path_bone(path);
324                         break;
325                 default:
326                         found= 0;
327                         break;
328         }
329
330         return found;
331 }
332
333 void buttons_context_compute(const bContext *C, SpaceButs *sbuts)
334 {
335         if(!sbuts->path)
336                 sbuts->path= MEM_callocN(sizeof(ButsContextPath), "ButsContextPath");
337         
338         buttons_context_path(C, sbuts->path);
339 }
340
341 /************************* Context Callback ************************/
342
343 static int set_pointer_type(ButsContextPath *path, bContextDataResult *result, StructRNA *type)
344 {
345         PointerRNA *ptr;
346         int a;
347
348         for(a=0; a<path->len; a++) {
349                 ptr= &path->ptr[a];
350
351                 if(RNA_struct_is_a(ptr->type, type)) {
352                         CTX_data_pointer_set(result, ptr->id.data, ptr->type, ptr->data);
353                         return 1;
354                 }
355         }
356
357         return 0;
358 }
359
360 static PointerRNA *get_pointer_type(ButsContextPath *path, StructRNA *type)
361 {
362         PointerRNA *ptr;
363         int a;
364
365         for(a=0; a<path->len; a++) {
366                 ptr= &path->ptr[a];
367
368                 if(RNA_struct_is_a(ptr->type, type))
369                         return ptr;
370         }
371
372         return NULL;
373 }
374
375 int buttons_context(const bContext *C, const char *member, bContextDataResult *result)
376 {
377         SpaceButs *sbuts= (SpaceButs*)CTX_wm_space_data(C);
378         ButsContextPath *path= sbuts->path;
379
380         if(!path)
381                 return 0;
382
383         /* here we handle context, getting data from precomputed path */
384
385         if(CTX_data_equals(member, "world")) {
386                 set_pointer_type(path, result, &RNA_World);
387                 return 1;
388         }
389         else if(CTX_data_equals(member, "object")) {
390                 set_pointer_type(path, result, &RNA_Object);
391                 return 1;
392         }
393         else if(CTX_data_equals(member, "mesh")) {
394                 set_pointer_type(path, result, &RNA_Mesh);
395                 return 1;
396         }
397         else if(CTX_data_equals(member, "armature")) {
398                 set_pointer_type(path, result, &RNA_Armature);
399                 return 1;
400         }
401         else if(CTX_data_equals(member, "lattice")) {
402                 set_pointer_type(path, result, &RNA_Lattice);
403                 return 1;
404         }
405         else if(CTX_data_equals(member, "curve")) {
406                 set_pointer_type(path, result, &RNA_Curve);
407                 return 1;
408         }
409         else if(CTX_data_equals(member, "meta_ball")) {
410                 set_pointer_type(path, result, &RNA_MetaBall);
411                 return 1;
412         }
413         else if(CTX_data_equals(member, "lamp")) {
414                 set_pointer_type(path, result, &RNA_Lamp);
415                 return 1;
416         }
417         else if(CTX_data_equals(member, "camera")) {
418                 set_pointer_type(path, result, &RNA_Camera);
419                 return 1;
420         }
421         else if(CTX_data_equals(member, "material")) {
422                 if(!set_pointer_type(path, result, &RNA_Material)) {
423                         PointerRNA *ptr= get_pointer_type(path, &RNA_MaterialSlot);
424
425                         if(ptr && ptr->data) {
426                                 Object *ob= ptr->id.data;
427                                 Material *ma= give_current_material(ob, (Material**)ptr->data - ob->mat);
428                                 CTX_data_id_pointer_set(result, &ma->id);
429                         }
430                 }
431
432                 return 1;
433         }
434         else if(CTX_data_equals(member, "texture")) {
435                 if(!set_pointer_type(path, result, &RNA_Texture)) {
436                         PointerRNA *ptr= get_pointer_type(path, &RNA_TextureSlot);
437
438                         if(ptr && ptr->data)
439                                 CTX_data_id_pointer_set(result, &((MTex*)ptr->data)->tex->id);
440                 }
441
442                 return 1;
443         }
444         else if(CTX_data_equals(member, "material_slot")) {
445                 set_pointer_type(path, result, &RNA_MaterialSlot);
446                 return 1;
447         }
448         else if(CTX_data_equals(member, "texture_slot")) {
449                 set_pointer_type(path, result, &RNA_TextureSlot);
450                 return 1;
451         }
452         else if(CTX_data_equals(member, "bone")) {
453                 set_pointer_type(path, result, &RNA_Bone);
454                 return 1;
455         }
456         else if(CTX_data_equals(member, "particle_system")) {
457                 set_pointer_type(path, result, &RNA_ParticleSystem);
458                 return 1;
459         }
460         else if(CTX_data_equals(member, "cloth")) {
461                 set_pointer_type(path, result, &RNA_ClothModifier);
462                 return 1;
463         }
464         else if(CTX_data_equals(member, "soft_body")) {
465                 PointerRNA *ptr= get_pointer_type(path, &RNA_Object);
466
467                 if(ptr && ptr->data) {
468                         Object *ob= ptr->data;
469                         CTX_data_pointer_set(result, &ob->id, &RNA_SoftBodySettings, ob->soft);
470                         return 1;
471                 }
472         }
473         else if(CTX_data_equals(member, "fluid")) {
474                 PointerRNA *ptr= get_pointer_type(path, &RNA_Object);
475
476                 if(ptr && ptr->data) {
477                         Object *ob= ptr->data;
478                         ModifierData *md= modifiers_findByType(ob, eModifierType_Fluidsim);
479                         CTX_data_pointer_set(result, &ob->id, &RNA_FluidSimulationModifier, md);
480                         return 1;
481                 }
482         }
483
484         return 0;
485 }
486
487 /************************* Drawing the Path ************************/
488
489 static void buttons_panel_context(const bContext *C, Panel *pa)
490 {
491         SpaceButs *sbuts= (SpaceButs*)CTX_wm_space_data(C);
492         ButsContextPath *path= sbuts->path;
493         uiLayout *row;
494         PointerRNA *ptr;
495         PropertyRNA *nameprop;
496         char namebuf[128], *name;
497         int a, icon;
498
499         if(!path)
500                 return;
501
502         row= uiLayoutRow(pa->layout, 0);
503         uiLayoutSetAlignment(row, UI_LAYOUT_ALIGN_LEFT);
504
505         for(a=0; a<path->len; a++) {
506                 ptr= &path->ptr[a];
507
508                 if(ptr->data) {
509                         icon= RNA_struct_ui_icon(ptr->type);
510                         nameprop= RNA_struct_name_property(ptr->type);
511
512                         if(nameprop) {
513                                 name= RNA_property_string_get_alloc(ptr, nameprop, namebuf, sizeof(namebuf));
514
515                                 uiItemL(row, name, icon);
516
517                                 if(name != namebuf)
518                                         MEM_freeN(name);
519                         }
520                         else
521                                 uiItemL(row, "", icon);
522                 }
523         }
524 }
525
526 void buttons_context_register(ARegionType *art)
527 {
528         PanelType *pt;
529
530         pt= MEM_callocN(sizeof(PanelType), "spacetype buttons panel context");
531         strcpy(pt->idname, "BUTTONS_PT_context");
532         strcpy(pt->label, "Context");
533         pt->draw= buttons_panel_context;
534         BLI_addtail(&art->paneltypes, pt);
535 }
536