style cleanup: follow style guide for formatting of if/for/while loops, and else...
[blender.git] / source / blender / editors / space_buttons / buttons_texture.c
1 /*
2  * ***** BEGIN GPL LICENSE BLOCK *****
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version. 
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software Foundation,
16  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17  *
18  * The Original Code is Copyright (C) 2009 Blender Foundation.
19  * All rights reserved.
20  *
21  * Contributor(s): Blender Foundation
22  *
23  * ***** END GPL LICENSE BLOCK *****
24  */
25
26 /** \file blender/editors/space_buttons/buttons_texture.c
27  *  \ingroup spbuttons
28  */
29
30
31 #include <stdlib.h>
32 #include <string.h>
33
34 #include "MEM_guardedalloc.h"
35
36 #include "BLI_listbase.h"
37 #include "BLI_string.h"
38 #include "BLI_utildefines.h"
39
40 #include "DNA_brush_types.h"
41 #include "DNA_ID.h"
42 #include "DNA_lamp_types.h"
43 #include "DNA_material_types.h"
44 #include "DNA_node_types.h"
45 #include "DNA_object_types.h"
46 #include "DNA_object_force.h"
47 #include "DNA_particle_types.h"
48 #include "DNA_scene_types.h"
49 #include "DNA_screen_types.h"
50 #include "DNA_space_types.h"
51 #include "DNA_world_types.h"
52
53 #include "BKE_context.h"
54 #include "BKE_material.h"
55 #include "BKE_modifier.h"
56 #include "BKE_node.h"
57 #include "BKE_paint.h"
58 #include "BKE_particle.h"
59 #include "BKE_scene.h"
60
61 #include "RNA_access.h"
62
63 #include "UI_interface.h"
64 #include "UI_resources.h"
65
66 #include "ED_node.h"
67 #include "ED_screen.h"
68
69 #include "../interface/interface_intern.h"
70
71 #include "buttons_intern.h"     // own include
72
73 /************************* Texture User **************************/
74
75 static void buttons_texture_user_property_add(ListBase *users, ID *id, 
76         PointerRNA ptr, PropertyRNA *prop,
77         const char *category, int icon, const char *name)
78 {
79         ButsTextureUser *user = MEM_callocN(sizeof(ButsTextureUser), "ButsTextureUser");
80
81         user->id= id;
82         user->ptr = ptr;
83         user->prop = prop;
84         user->category = category;
85         user->icon = icon;
86         user->name = name;
87         user->index = BLI_countlist(users);
88
89         BLI_addtail(users, user);
90 }
91
92 static void buttons_texture_user_node_add(ListBase *users, ID *id, 
93         bNodeTree *ntree, bNode *node,
94         const char *category, int icon, const char *name)
95 {
96         ButsTextureUser *user = MEM_callocN(sizeof(ButsTextureUser), "ButsTextureUser");
97
98         user->id= id;
99         user->ntree = ntree;
100         user->node = node;
101         user->category = category;
102         user->icon = icon;
103         user->name = name;
104         user->index = BLI_countlist(users);
105
106         BLI_addtail(users, user);
107 }
108
109 static void buttons_texture_users_find_nodetree(ListBase *users, ID *id,
110         bNodeTree *ntree, const char *category)
111 {
112         bNode *node;
113
114         if (ntree) {
115                 for (node=ntree->nodes.first; node; node=node->next) {
116                         if (node->typeinfo->nclass == NODE_CLASS_TEXTURE) {
117                                 PointerRNA ptr;
118                                 /* PropertyRNA *prop; */ /* UNUSED */
119
120                                 RNA_pointer_create(&ntree->id, &RNA_Node, node, &ptr);
121                                 /* prop = RNA_struct_find_property(&ptr, "texture"); */ /* UNUSED */
122
123                                 buttons_texture_user_node_add(users, id, ntree, node,
124                                         category, RNA_struct_ui_icon(ptr.type), node->name);
125                         }
126                         else if (node->type == NODE_GROUP && node->id) {
127                                 buttons_texture_users_find_nodetree(users, id, (bNodeTree*)node->id, category);
128                         }
129                 }
130         }
131 }
132
133 static void buttons_texture_modifier_foreach(void *userData, Object *ob, ModifierData *md, const char *propname)
134 {
135         PointerRNA ptr;
136         PropertyRNA *prop;
137         ListBase *users = userData;
138
139         RNA_pointer_create(&ob->id, &RNA_Modifier, md, &ptr);
140         prop = RNA_struct_find_property(&ptr, propname);
141
142         buttons_texture_user_property_add(users, &ob->id, ptr, prop,
143                 "Modifiers", RNA_struct_ui_icon(ptr.type), md->name);
144 }
145
146 static void buttons_texture_users_from_context(ListBase *users, const bContext *C, SpaceButs *sbuts)
147 {
148         Scene *scene= NULL;
149         Object *ob= NULL;
150         Material *ma= NULL;
151         Lamp *la= NULL;
152         World *wrld= NULL;
153         Brush *brush= NULL;
154         ID *pinid = sbuts->pinid;
155
156         /* get data from context */
157         if (pinid) {
158                 if (GS(pinid->name) == ID_SCE)
159                         scene= (Scene*)pinid;
160                 else if (GS(pinid->name) == ID_OB)
161                         ob= (Object*)pinid;
162                 else if (GS(pinid->name) == ID_LA)
163                         la= (Lamp*)pinid;
164                 else if (GS(pinid->name) == ID_WO)
165                         wrld= (World*)pinid;
166                 else if (GS(pinid->name) == ID_MA)
167                         ma= (Material*)pinid;
168                 else if (GS(pinid->name) == ID_BR)
169                         brush= (Brush*)pinid;
170         }
171
172         if (!scene)
173                 scene= CTX_data_scene(C);
174         
175         if (!(pinid || pinid == &scene->id)) {
176                 ob= (scene->basact)? scene->basact->object: NULL;
177                 wrld= scene->world;
178                 brush= paint_brush(paint_get_active(scene));
179         }
180
181         if (ob && ob->type == OB_LAMP && !la)
182                 la= ob->data;
183         if (ob && !ma)
184                 ma= give_current_material(ob, ob->actcol);
185
186         /* fill users */
187         users->first = users->last = NULL;
188
189         if (ma)
190                 buttons_texture_users_find_nodetree(users, &ma->id, ma->nodetree, "Material");
191         if (la)
192                 buttons_texture_users_find_nodetree(users, &la->id, la->nodetree, "Lamp");
193         if (wrld)
194                 buttons_texture_users_find_nodetree(users, &wrld->id, wrld->nodetree, "World");
195
196         if (ob) {
197                 ParticleSystem *psys= psys_get_current(ob);
198                 MTex *mtex;
199                 int a;
200
201                 /* modifiers */
202                 modifiers_foreachTexLink(ob, buttons_texture_modifier_foreach, users);
203
204                 /* particle systems */
205                 if (psys) {
206                         /* todo: these slots are not in the UI */
207                         for (a=0; a<MAX_MTEX; a++) {
208                                 mtex = psys->part->mtex[a];
209
210                                 if (mtex) {
211                                         PointerRNA ptr;
212                                         PropertyRNA *prop;
213
214                                         RNA_pointer_create(&psys->part->id, &RNA_ParticleSettingsTextureSlot, mtex, &ptr);
215                                         prop = RNA_struct_find_property(&ptr, "texture");
216
217                                         buttons_texture_user_property_add(users, &psys->part->id, ptr, prop,
218                                                 "Particles", RNA_struct_ui_icon(&RNA_ParticleSettings), psys->name);
219                                 }
220                         }
221                 }
222
223                 /* field */
224                 if (ob->pd && ob->pd->forcefield == PFIELD_TEXTURE) {
225                         PointerRNA ptr;
226                         PropertyRNA *prop;
227
228                         RNA_pointer_create(&ob->id, &RNA_FieldSettings, ob->pd, &ptr);
229                         prop = RNA_struct_find_property(&ptr, "texture");
230
231                         buttons_texture_user_property_add(users, &ob->id, ptr, prop,
232                                 "Fields", ICON_FORCE_TEXTURE, "Texture Field");
233                 }
234         }
235
236         /* brush */
237         if (brush) {
238                 PointerRNA ptr;
239                 PropertyRNA *prop;
240
241                 RNA_pointer_create(&brush->id, &RNA_BrushTextureSlot, &brush->mtex, &ptr);
242                 prop= RNA_struct_find_property(&ptr, "texture");
243
244                 buttons_texture_user_property_add(users, &brush->id, ptr, prop,
245                         "Brush", ICON_BRUSH_DATA, brush->id.name+2);
246         }
247 }
248
249 void buttons_texture_context_compute(const bContext *C, SpaceButs *sbuts)
250 {
251         /* gatheravailable texture users in context. runs on every draw of
252          * properties editor, before the buttons are created. */
253         ButsContextTexture *ct= sbuts->texuser;
254         Scene *scene= CTX_data_scene(C);
255
256         if (!scene_use_new_shading_nodes(scene)) {
257                 if (ct) {
258                         BLI_freelistN(&ct->users);
259                         MEM_freeN(ct);
260                         sbuts->texuser= NULL;
261                 }
262                 
263                 return;
264         }
265
266         if (!ct) {
267                 ct= MEM_callocN(sizeof(ButsContextTexture), "ButsContextTexture");
268                 sbuts->texuser= ct;
269         }
270         else {
271                 BLI_freelistN(&ct->users);
272         }
273
274         buttons_texture_users_from_context(&ct->users, C, sbuts);
275
276         /* set one user as active based on active index */
277         if (ct->index >= BLI_countlist(&ct->users))
278                 ct->index= 0;
279
280         ct->user = BLI_findlink(&ct->users, ct->index);
281         ct->texture = NULL;
282
283         if (ct->user) {
284                 if (ct->user->ptr.data) {
285                         PointerRNA texptr;
286                         Tex *tex;
287
288                         /* get texture datablock pointer if it's a property */
289                         texptr = RNA_property_pointer_get(&ct->user->ptr, ct->user->prop);
290                         tex = (RNA_struct_is_a(texptr.type, &RNA_Texture))? texptr.data: NULL;
291
292                         ct->texture = tex;
293                 }
294                 else if (ct->user->node && !(ct->user->node->flag & NODE_ACTIVE_TEXTURE)) {
295                         ButsTextureUser *user;
296
297                         /* detect change of active texture node in same node tree, in that
298                          * case we also automatically switch to the other node */
299                         for (user=ct->users.first; user; user=user->next) {
300                                 if (user->ntree == ct->user->ntree && user->node != ct->user->node) {
301                                         if (user->node->flag & NODE_ACTIVE_TEXTURE) {
302                                                 ct->user = user;
303                                                 ct->index = BLI_findindex(&ct->users, user);
304                                                 break;
305                                         }
306                                 }
307                         }
308                 }
309         }
310 }
311
312 static void template_texture_select(bContext *C, void *user_p, void *UNUSED(arg))
313 {
314         /* callback when selecting a texture user in the menu */
315         SpaceButs *sbuts = CTX_wm_space_buts(C);
316         ButsContextTexture *ct= (sbuts)? sbuts->texuser: NULL;
317         ButsTextureUser *user = (ButsTextureUser*)user_p;
318         PointerRNA texptr;
319         Tex *tex;
320
321         if (!ct)
322                 return;
323
324         /* set user as active */
325         if (user->node) {
326                 ED_node_set_active(CTX_data_main(C), user->ntree, user->node);
327                 ct->texture = NULL;
328         }
329         else {
330                 texptr = RNA_property_pointer_get(&user->ptr, user->prop);
331                 tex = (RNA_struct_is_a(texptr.type, &RNA_Texture))? texptr.data: NULL;
332
333                 ct->texture = tex;
334         }
335
336         ct->user = user;
337         ct->index = user->index;
338 }
339
340 static void template_texture_user_menu(bContext *C, uiLayout *layout, void *UNUSED(arg))
341 {
342         /* callback when opening texture user selection menu, to create buttons. */
343         SpaceButs *sbuts = CTX_wm_space_buts(C);
344         ButsContextTexture *ct= (sbuts)? sbuts->texuser: NULL;
345         ButsTextureUser *user;
346         uiBlock *block = uiLayoutGetBlock(layout);
347         const char *last_category = NULL;
348
349         for (user=ct->users.first; user; user=user->next) {
350                 uiBut *but;
351                 char name[UI_MAX_NAME_STR];
352
353                 /* add label per category */
354                 if (!last_category || strcmp(last_category, user->category) != 0) {
355                         uiItemL(layout, user->category, ICON_NONE);
356                         but= block->buttons.last;
357                         but->flag= UI_TEXT_LEFT;
358                 }
359
360                 /* create button */
361                 BLI_snprintf(name, UI_MAX_NAME_STR, "  %s", user->name);
362
363                 but = uiDefIconTextBut(block, BUT, 0, user->icon, name, 0, 0, UI_UNIT_X*4, UI_UNIT_Y,
364                         NULL, 0.0, 0.0, 0.0, 0.0, "");
365                 uiButSetNFunc(but, template_texture_select, MEM_dupallocN(user), NULL);
366
367                 last_category = user->category;
368         }
369 }
370
371 void uiTemplateTextureUser(uiLayout *layout, bContext *C)
372 {
373         /* texture user selection dropdown menu. the available users have been
374          * gathered before drawing in ButsContextTexture, we merely need to
375          * display the current item. */
376         SpaceButs *sbuts = CTX_wm_space_buts(C);
377         ButsContextTexture *ct= (sbuts)? sbuts->texuser: NULL;
378         uiBlock *block = uiLayoutGetBlock(layout);
379         uiBut *but;
380         ButsTextureUser *user;
381         char name[UI_MAX_NAME_STR];
382
383         if (!ct)
384                 return;
385
386         /* get current user */
387         user= ct->user;
388
389         if (!user) {
390                 uiItemL(layout, "No textures in context.", ICON_NONE);
391                 return;
392         }
393
394         /* create button */
395         BLI_snprintf(name, UI_MAX_NAME_STR, "%s", user->name);
396
397         if (user->icon) {
398                 but = uiDefIconTextMenuBut(block, template_texture_user_menu, NULL,
399                         user->icon, name, 0, 0, UI_UNIT_X*4, UI_UNIT_Y, "");
400         }
401         else {
402                 but = uiDefMenuBut(block, template_texture_user_menu, NULL,
403                         name, 0, 0, UI_UNIT_X*4, UI_UNIT_Y, "");
404         }
405
406         /* some cosmetic tweaks */
407         but->type= MENU;
408         but->flag |= UI_TEXT_LEFT;
409         but->flag &= ~UI_ICON_SUBMENU;
410 }
411
412 /************************* Texture Show **************************/
413
414 static void template_texture_show(bContext *C, void *data_p, void *prop_p)
415 {
416         SpaceButs *sbuts = CTX_wm_space_buts(C);
417         ButsContextTexture *ct= (sbuts)? sbuts->texuser: NULL;
418         ButsTextureUser *user;
419
420         if (!ct)
421                 return;
422
423         for (user=ct->users.first; user; user=user->next)
424                 if (user->ptr.data == data_p && user->prop == prop_p)
425                         break;
426         
427         if (user) {
428                 /* select texture */
429                 template_texture_select(C, user, NULL);
430
431                 /* change context */
432                 sbuts->mainb= BCONTEXT_TEXTURE;
433                 sbuts->mainbuser= sbuts->mainb;
434                 sbuts->preview= 1;
435
436                 /* redraw editor */
437                 ED_area_tag_redraw(CTX_wm_area(C));
438         }
439 }
440
441 void uiTemplateTextureShow(uiLayout *layout, bContext *C, PointerRNA *ptr, PropertyRNA *prop)
442 {
443         /* button to quickly show texture in texture tab */
444         SpaceButs *sbuts = CTX_wm_space_buts(C);
445         ButsContextTexture *ct= (sbuts)? sbuts->texuser: NULL;
446         ButsTextureUser *user;
447
448         /* only show button in other tabs in properties editor */
449         if (!ct || sbuts->mainb == BCONTEXT_TEXTURE)
450                 return;
451
452         /* find corresponding texture user */
453         for (user=ct->users.first; user; user=user->next)
454                 if (user->ptr.data == ptr->data && user->prop == prop)
455                         break;
456         
457         /* draw button */
458         if (user) {
459                 uiBlock *block = uiLayoutGetBlock(layout);
460                 uiBut *but;
461                 
462                 but = uiDefIconBut(block, BUT, 0, ICON_BUTS, 0, 0, UI_UNIT_X, UI_UNIT_Y,
463                         NULL, 0.0, 0.0, 0.0, 0.0, "Show texture in texture tab");
464                 uiButSetFunc(but, template_texture_show, user->ptr.data, user->prop);
465         }
466 }
467