Fix [#36530] Texture tab refreshing problem
[blender-staging.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 "BLF_translation.h"
41
42 #include "DNA_brush_types.h"
43 #include "DNA_ID.h"
44 #include "DNA_lamp_types.h"
45 #include "DNA_material_types.h"
46 #include "DNA_node_types.h"
47 #include "DNA_object_types.h"
48 #include "DNA_object_force.h"
49 #include "DNA_particle_types.h"
50 #include "DNA_scene_types.h"
51 #include "DNA_screen_types.h"
52 #include "DNA_space_types.h"
53 #include "DNA_world_types.h"
54
55 #include "BKE_context.h"
56 #include "BKE_material.h"
57 #include "BKE_modifier.h"
58 #include "BKE_node.h"
59 #include "BKE_paint.h"
60 #include "BKE_particle.h"
61 #include "BKE_scene.h"
62
63 #include "RNA_access.h"
64
65 #include "UI_interface.h"
66 #include "UI_resources.h"
67
68 #include "ED_buttons.h"
69 #include "ED_node.h"
70 #include "ED_screen.h"
71
72 #include "../interface/interface_intern.h"
73
74 #include "buttons_intern.h" // own include
75
76 /****************** "Old Shading" Texture Context ****************/
77
78 bool ED_texture_context_check_world(const bContext *C)
79 {
80         Scene *scene = CTX_data_scene(C);
81         return (scene && scene->world);
82 }
83
84 bool ED_texture_context_check_material(const bContext *C)
85 {
86         Object *ob = CTX_data_active_object(C);
87         return (ob && (ob->totcol != 0));
88 }
89
90 bool ED_texture_context_check_lamp(const bContext *C)
91 {
92         Object *ob = CTX_data_active_object(C);
93         return (ob && (ob->type == OB_LAMP));
94 }
95
96 bool ED_texture_context_check_particles(const bContext *C)
97 {
98         Object *ob = CTX_data_active_object(C);
99         return (ob && ob->particlesystem.first);
100 }
101
102 static void texture_context_check_modifier_foreach(void *userData, Object *UNUSED(ob), ModifierData *UNUSED(md),
103                                                    const char *UNUSED(propname))
104 {
105         *((bool *)userData) = true;
106 }
107
108 bool ED_texture_context_check_others(const bContext *C)
109 {
110         /* We cannot rely on sbuts->texuser here, as it is NULL when in "old" tex handling, non-OTHERS tex context. */
111         Object *ob = CTX_data_active_object(C);
112
113         /* object */
114         if (ob) {
115                 /* Tex force field. */
116                 if (ob->pd && ob->pd->forcefield == PFIELD_TEXTURE) {
117                         return true;
118                 }
119
120                 /* modifiers */
121                 {
122                         bool check = false;
123                         modifiers_foreachTexLink(ob, texture_context_check_modifier_foreach, &check);
124                         if (check) {
125                                 return true;
126                         }
127                 }
128         }
129
130         /* brush */
131         if (BKE_paint_brush(BKE_paint_get_active_from_context(C))) {
132                 return true;
133         }
134
135         return false;
136 }
137
138 static void set_texture_context(const bContext *C, SpaceButs *sbuts)
139 {
140         Scene *scene = CTX_data_scene(C);
141
142         if (BKE_scene_use_new_shading_nodes(scene)) {
143                 return;  /* No texture context in new shading mode */
144         }
145
146         {
147                 bool valid_world = ED_texture_context_check_world(C);
148                 bool valid_material = ED_texture_context_check_material(C);
149                 bool valid_lamp = ED_texture_context_check_lamp(C);
150                 bool valid_particles = ED_texture_context_check_particles(C);
151                 bool valid_others = ED_texture_context_check_others(C);
152
153                 /* this is similar to direct user action, no need to keep "better" ctxt in _prev */
154                 if ((sbuts->mainb == BCONTEXT_WORLD) && valid_world) {
155                         sbuts->texture_context = sbuts->texture_context_prev = SB_TEXC_WORLD;
156                 }
157                 else if ((sbuts->mainb == BCONTEXT_MATERIAL) && valid_material) {
158                         sbuts->texture_context = sbuts->texture_context_prev = SB_TEXC_MATERIAL;
159                 }
160                 else if ((sbuts->mainb == BCONTEXT_DATA) && valid_lamp) {
161                         sbuts->texture_context = sbuts->texture_context_prev = SB_TEXC_LAMP;
162                 }
163                 else if ((sbuts->mainb == BCONTEXT_PARTICLE) && valid_particles) {
164                         sbuts->texture_context = sbuts->texture_context_prev = SB_TEXC_PARTICLES;
165                 }
166                 else if ((ELEM(sbuts->mainb, BCONTEXT_MODIFIER, BCONTEXT_PHYSICS)) && valid_others) {
167                         sbuts->texture_context = sbuts->texture_context_prev = SB_TEXC_OTHER;
168                 }
169                 /* Else, try to revive a previous "better" ctxt... */
170                 else if ((sbuts->texture_context_prev != sbuts->texture_context) &&
171                          (((sbuts->texture_context_prev == SB_TEXC_WORLD) && valid_world) ||
172                           ((sbuts->texture_context_prev == SB_TEXC_MATERIAL) && valid_material) ||
173                           ((sbuts->texture_context_prev == SB_TEXC_LAMP) && valid_lamp) ||
174                           ((sbuts->texture_context_prev == SB_TEXC_PARTICLES) && valid_particles) ||
175                           ((sbuts->texture_context_prev == SB_TEXC_OTHER) && valid_others)))
176                 {
177                         sbuts->texture_context = sbuts->texture_context_prev;
178                 }
179                 /* Else, just be sure that current context is valid! */
180                 else if (((sbuts->texture_context == SB_TEXC_WORLD) && !valid_world) ||
181                          ((sbuts->texture_context == SB_TEXC_MATERIAL) && !valid_material) ||
182                          ((sbuts->texture_context == SB_TEXC_LAMP) && !valid_lamp) ||
183                          ((sbuts->texture_context == SB_TEXC_PARTICLES) && !valid_particles) ||
184                          ((sbuts->texture_context == SB_TEXC_OTHER) && !valid_others))
185                 {
186                         /* this is default fallback, do keep "better" ctxt in _prev */
187                         sbuts->texture_context_prev = sbuts->texture_context;
188                         if (valid_material) {
189                                 sbuts->texture_context = SB_TEXC_MATERIAL;
190                         }
191                         else if (valid_lamp) {
192                                 sbuts->texture_context = SB_TEXC_LAMP;
193                         }
194                         else if (valid_particles) {
195                                 sbuts->texture_context = SB_TEXC_PARTICLES;
196                         }
197                         else if (valid_world) {
198                                 sbuts->texture_context = SB_TEXC_WORLD;
199                         }
200                         else if (valid_others) {
201                                 sbuts->texture_context = SB_TEXC_OTHER;
202                         }
203                 }
204         }
205 }
206
207 /************************* Texture User **************************/
208
209 static void buttons_texture_user_property_add(ListBase *users, ID *id, 
210                                               PointerRNA ptr, PropertyRNA *prop,
211                                               const char *category, int icon, const char *name)
212 {
213         ButsTextureUser *user = MEM_callocN(sizeof(ButsTextureUser), "ButsTextureUser");
214
215         user->id = id;
216         user->ptr = ptr;
217         user->prop = prop;
218         user->category = category;
219         user->icon = icon;
220         user->name = name;
221         user->index = BLI_countlist(users);
222
223         BLI_addtail(users, user);
224 }
225
226 static void buttons_texture_user_node_add(ListBase *users, ID *id, 
227                                           bNodeTree *ntree, bNode *node,
228                                           const char *category, int icon, const char *name)
229 {
230         ButsTextureUser *user = MEM_callocN(sizeof(ButsTextureUser), "ButsTextureUser");
231
232         user->id = id;
233         user->ntree = ntree;
234         user->node = node;
235         user->category = category;
236         user->icon = icon;
237         user->name = name;
238         user->index = BLI_countlist(users);
239
240         BLI_addtail(users, user);
241 }
242
243 static void buttons_texture_users_find_nodetree(ListBase *users, ID *id,
244                                                 bNodeTree *ntree, const char *category)
245 {
246         bNode *node;
247
248         if (ntree) {
249                 for (node = ntree->nodes.first; node; node = node->next) {
250                         if (node->typeinfo->nclass == NODE_CLASS_TEXTURE) {
251                                 PointerRNA ptr;
252                                 /* PropertyRNA *prop; */ /* UNUSED */
253                                 
254                                 RNA_pointer_create(&ntree->id, &RNA_Node, node, &ptr);
255                                 /* prop = RNA_struct_find_property(&ptr, "texture"); */ /* UNUSED */
256                                 
257                                 buttons_texture_user_node_add(users, id, ntree, node,
258                                                               category, RNA_struct_ui_icon(ptr.type), node->name);
259                         }
260                         else if (node->type == NODE_GROUP && node->id) {
261                                 buttons_texture_users_find_nodetree(users, id, (bNodeTree *)node->id, category);
262                         }
263                 }
264         }
265 }
266
267 static void buttons_texture_modifier_foreach(void *userData, Object *ob, ModifierData *md, const char *propname)
268 {
269         PointerRNA ptr;
270         PropertyRNA *prop;
271         ListBase *users = userData;
272
273         RNA_pointer_create(&ob->id, &RNA_Modifier, md, &ptr);
274         prop = RNA_struct_find_property(&ptr, propname);
275
276         buttons_texture_user_property_add(users, &ob->id, ptr, prop,
277                                           "Modifiers", RNA_struct_ui_icon(ptr.type), md->name);
278 }
279
280 static void buttons_texture_users_from_context(ListBase *users, const bContext *C, SpaceButs *sbuts)
281 {
282         Scene *scene = NULL;
283         Object *ob = NULL;
284         Material *ma = NULL;
285         Lamp *la = NULL;
286         World *wrld = NULL;
287         Brush *brush = NULL;
288         ID *pinid = sbuts->pinid;
289         bool limited_mode = (sbuts->flag & SB_TEX_USER_LIMITED) != 0;
290
291         /* get data from context */
292         if (pinid) {
293                 if (GS(pinid->name) == ID_SCE)
294                         scene = (Scene *)pinid;
295                 else if (GS(pinid->name) == ID_OB)
296                         ob = (Object *)pinid;
297                 else if (GS(pinid->name) == ID_LA)
298                         la = (Lamp *)pinid;
299                 else if (GS(pinid->name) == ID_WO)
300                         wrld = (World *)pinid;
301                 else if (GS(pinid->name) == ID_MA)
302                         ma = (Material *)pinid;
303                 else if (GS(pinid->name) == ID_BR)
304                         brush = (Brush *)pinid;
305         }
306
307         if (!scene)
308                 scene = CTX_data_scene(C);
309
310         if (!(pinid || pinid == &scene->id)) {
311                 ob = (scene->basact) ? scene->basact->object : NULL;
312                 wrld = scene->world;
313                 brush = BKE_paint_brush(BKE_paint_get_active_from_context(C));
314         }
315
316         if (ob && ob->type == OB_LAMP && !la)
317                 la = ob->data;
318         if (ob && !ma)
319                 ma = give_current_material(ob, ob->actcol);
320
321         /* fill users */
322         users->first = users->last = NULL;
323
324         if (ma && !limited_mode)
325                 buttons_texture_users_find_nodetree(users, &ma->id, ma->nodetree, "Material");
326         if (la && !limited_mode)
327                 buttons_texture_users_find_nodetree(users, &la->id, la->nodetree, "Lamp");
328         if (wrld && !limited_mode)
329                 buttons_texture_users_find_nodetree(users, &wrld->id, wrld->nodetree, "World");
330
331         if (ob) {
332                 ParticleSystem *psys = psys_get_current(ob);
333                 MTex *mtex;
334                 int a;
335
336                 /* modifiers */
337                 modifiers_foreachTexLink(ob, buttons_texture_modifier_foreach, users);
338
339                 /* particle systems */
340                 if (psys && !limited_mode) {
341                         for (a = 0; a < MAX_MTEX; a++) {
342                                 mtex = psys->part->mtex[a];
343
344                                 if (mtex) {
345                                         PointerRNA ptr;
346                                         PropertyRNA *prop;
347
348                                         RNA_pointer_create(&psys->part->id, &RNA_ParticleSettingsTextureSlot, mtex, &ptr);
349                                         prop = RNA_struct_find_property(&ptr, "texture");
350
351                                         buttons_texture_user_property_add(users, &psys->part->id, ptr, prop,
352                                                                           "Particles", RNA_struct_ui_icon(&RNA_ParticleSettings), psys->name);
353                                 }
354                         }
355                 }
356
357                 /* field */
358                 if (ob->pd && ob->pd->forcefield == PFIELD_TEXTURE) {
359                         PointerRNA ptr;
360                         PropertyRNA *prop;
361
362                         RNA_pointer_create(&ob->id, &RNA_FieldSettings, ob->pd, &ptr);
363                         prop = RNA_struct_find_property(&ptr, "texture");
364
365                         buttons_texture_user_property_add(users, &ob->id, ptr, prop,
366                                                           "Fields", ICON_FORCE_TEXTURE, "Texture Field");
367                 }
368         }
369
370         /* brush */
371         if (brush) {
372                 PointerRNA ptr;
373                 PropertyRNA *prop;
374
375                 /* texture */
376                 RNA_pointer_create(&brush->id, &RNA_BrushTextureSlot, &brush->mtex, &ptr);
377                 prop = RNA_struct_find_property(&ptr, "texture");
378
379                 buttons_texture_user_property_add(users, &brush->id, ptr, prop,
380                                                   "Brush", ICON_BRUSH_DATA, "Brush");
381
382                 /* mask texture */
383                 RNA_pointer_create(&brush->id, &RNA_BrushTextureSlot, &brush->mask_mtex, &ptr);
384                 prop = RNA_struct_find_property(&ptr, "texture");
385
386                 buttons_texture_user_property_add(users, &brush->id, ptr, prop,
387                                                   "Brush", ICON_BRUSH_DATA, "Brush Mask");
388         }
389 }
390
391 void buttons_texture_context_compute(const bContext *C, SpaceButs *sbuts)
392 {
393         /* gather available texture users in context. runs on every draw of
394          * properties editor, before the buttons are created. */
395         ButsContextTexture *ct = sbuts->texuser;
396         Scene *scene = CTX_data_scene(C);
397         ID *pinid = sbuts->pinid;
398
399         set_texture_context(C, sbuts);
400
401         if (!(BKE_scene_use_new_shading_nodes(scene) || (sbuts->texture_context == SB_TEXC_OTHER))) {
402                 if (ct) {
403                         BLI_freelistN(&ct->users);
404                         MEM_freeN(ct);
405                         sbuts->texuser = NULL;
406                 }
407
408                 return;
409         }
410
411         if (!ct) {
412                 ct = MEM_callocN(sizeof(ButsContextTexture), "ButsContextTexture");
413                 sbuts->texuser = ct;
414         }
415         else {
416                 BLI_freelistN(&ct->users);
417         }
418
419         buttons_texture_users_from_context(&ct->users, C, sbuts);
420
421         if (pinid && GS(pinid->name) == ID_TE) {
422                 ct->user = NULL;
423                 ct->texture = (Tex *)pinid;
424         }
425         else {
426                 /* set one user as active based on active index */
427                 if (ct->index >= BLI_countlist(&ct->users))
428                         ct->index = 0;
429
430                 ct->user = BLI_findlink(&ct->users, ct->index);
431                 ct->texture = NULL;
432
433                 if (ct->user) {
434                         if (ct->user->ptr.data) {
435                                 PointerRNA texptr;
436                                 Tex *tex;
437
438                                 /* get texture datablock pointer if it's a property */
439                                 texptr = RNA_property_pointer_get(&ct->user->ptr, ct->user->prop);
440                                 tex = (RNA_struct_is_a(texptr.type, &RNA_Texture)) ? texptr.data : NULL;
441
442                                 ct->texture = tex;
443                         }
444                         else if (ct->user->node && !(ct->user->node->flag & NODE_ACTIVE_TEXTURE)) {
445                                 ButsTextureUser *user;
446
447                                 /* detect change of active texture node in same node tree, in that
448                                  * case we also automatically switch to the other node */
449                                 for (user = ct->users.first; user; user = user->next) {
450                                         if (user->ntree == ct->user->ntree && user->node != ct->user->node) {
451                                                 if (user->node->flag & NODE_ACTIVE_TEXTURE) {
452                                                         ct->user = user;
453                                                         ct->index = BLI_findindex(&ct->users, user);
454                                                         break;
455                                                 }
456                                         }
457                                 }
458                         }
459                 }
460         }
461 }
462
463 static void template_texture_select(bContext *C, void *user_p, void *UNUSED(arg))
464 {
465         /* callback when selecting a texture user in the menu */
466         SpaceButs *sbuts = CTX_wm_space_buts(C);
467         ButsContextTexture *ct = (sbuts) ? sbuts->texuser : NULL;
468         ButsTextureUser *user = (ButsTextureUser *)user_p;
469         PointerRNA texptr;
470         Tex *tex;
471
472         if (!ct)
473                 return;
474
475         /* set user as active */
476         if (user->node) {
477                 ED_node_set_active(CTX_data_main(C), user->ntree, user->node);
478                 ct->texture = NULL;
479         }
480         else {
481                 texptr = RNA_property_pointer_get(&user->ptr, user->prop);
482                 tex = (RNA_struct_is_a(texptr.type, &RNA_Texture)) ? texptr.data : NULL;
483
484                 ct->texture = tex;
485
486                 if (user->ptr.type == &RNA_ParticleSettingsTextureSlot) {
487                         /* stupid exception for particle systems which still uses influence
488                          * from the old texture system, set the active texture slots as well */
489                         ParticleSettings *part = user->ptr.id.data;
490                         int a;
491
492                         for (a = 0; a < MAX_MTEX; a++)
493                                 if (user->ptr.data == part->mtex[a])
494                                         part->texact = a;
495                 }
496         }
497
498         ct->user = user;
499         ct->index = user->index;
500 }
501
502 static void template_texture_user_menu(bContext *C, uiLayout *layout, void *UNUSED(arg))
503 {
504         /* callback when opening texture user selection menu, to create buttons. */
505         SpaceButs *sbuts = CTX_wm_space_buts(C);
506         ButsContextTexture *ct = sbuts->texuser;
507         ButsTextureUser *user;
508         uiBlock *block = uiLayoutGetBlock(layout);
509         const char *last_category = NULL;
510
511         for (user = ct->users.first; user; user = user->next) {
512                 uiBut *but;
513                 char name[UI_MAX_NAME_STR];
514
515                 /* add label per category */
516                 if (!last_category || strcmp(last_category, user->category) != 0) {
517                         uiItemL(layout, user->category, ICON_NONE);
518                         but = block->buttons.last;
519                         but->flag = UI_TEXT_LEFT;
520                 }
521
522                 /* create button */
523                 if (user->prop) {
524                         PointerRNA texptr = RNA_property_pointer_get(&user->ptr, user->prop);
525                         Tex *tex = texptr.data;
526
527                         if (tex)
528                                 BLI_snprintf(name, UI_MAX_NAME_STR, "  %s - %s", user->name, tex->id.name + 2);
529                         else
530                                 BLI_snprintf(name, UI_MAX_NAME_STR, "  %s", user->name);
531                 }
532                 else
533                         BLI_snprintf(name, UI_MAX_NAME_STR, "  %s", user->name);
534
535                 but = uiDefIconTextBut(block, BUT, 0, user->icon, name, 0, 0, UI_UNIT_X * 4, UI_UNIT_Y,
536                                        NULL, 0.0, 0.0, 0.0, 0.0, "");
537                 uiButSetNFunc(but, template_texture_select, MEM_dupallocN(user), NULL);
538
539                 last_category = user->category;
540         }
541 }
542
543 void uiTemplateTextureUser(uiLayout *layout, bContext *C)
544 {
545         /* texture user selection dropdown menu. the available users have been
546          * gathered before drawing in ButsContextTexture, we merely need to
547          * display the current item. */
548         SpaceButs *sbuts = CTX_wm_space_buts(C);
549         ButsContextTexture *ct = (sbuts) ? sbuts->texuser : NULL;
550         uiBlock *block = uiLayoutGetBlock(layout);
551         uiBut *but;
552         ButsTextureUser *user;
553         char name[UI_MAX_NAME_STR];
554
555         if (!ct)
556                 return;
557
558         /* get current user */
559         user = ct->user;
560
561         if (!user) {
562                 uiItemL(layout, IFACE_("No textures in context"), ICON_NONE);
563                 return;
564         }
565
566         /* create button */
567         BLI_strncpy(name, user->name, UI_MAX_NAME_STR);
568
569         if (user->icon) {
570                 but = uiDefIconTextMenuBut(block, template_texture_user_menu, NULL,
571                                            user->icon, name, 0, 0, UI_UNIT_X * 4, UI_UNIT_Y, "");
572         }
573         else {
574                 but = uiDefMenuBut(block, template_texture_user_menu, NULL,
575                                    name, 0, 0, UI_UNIT_X * 4, UI_UNIT_Y, "");
576         }
577
578         /* some cosmetic tweaks */
579         but->type = MENU;
580         but->flag |= UI_TEXT_LEFT;
581         but->flag &= ~UI_ICON_SUBMENU;
582 }
583
584 /************************* Texture Show **************************/
585
586 static void template_texture_show(bContext *C, void *data_p, void *prop_p)
587 {
588         SpaceButs *sbuts = CTX_wm_space_buts(C);
589         ButsContextTexture *ct = (sbuts) ? sbuts->texuser : NULL;
590         ButsTextureUser *user;
591
592         if (!ct)
593                 return;
594
595         for (user = ct->users.first; user; user = user->next)
596                 if (user->ptr.data == data_p && user->prop == prop_p)
597                         break;
598         
599         if (user) {
600                 /* select texture */
601                 template_texture_select(C, user, NULL);
602
603                 /* change context */
604                 sbuts->mainb = BCONTEXT_TEXTURE;
605                 sbuts->mainbuser = sbuts->mainb;
606                 sbuts->preview = 1;
607
608                 /* redraw editor */
609                 ED_area_tag_redraw(CTX_wm_area(C));
610         }
611 }
612
613 void uiTemplateTextureShow(uiLayout *layout, bContext *C, PointerRNA *ptr, PropertyRNA *prop)
614 {
615         /* button to quickly show texture in texture tab */
616         SpaceButs *sbuts = CTX_wm_space_buts(C);
617         ButsContextTexture *ct = (sbuts) ? sbuts->texuser : NULL;
618         ButsTextureUser *user;
619
620         /* only show button in other tabs in properties editor */
621         if (!ct || sbuts->mainb == BCONTEXT_TEXTURE)
622                 return;
623
624         /* find corresponding texture user */
625         for (user = ct->users.first; user; user = user->next)
626                 if (user->ptr.data == ptr->data && user->prop == prop)
627                         break;
628         
629         /* draw button */
630         if (user) {
631                 uiBlock *block = uiLayoutGetBlock(layout);
632                 uiBut *but;
633                 
634                 but = uiDefIconBut(block, BUT, 0, ICON_BUTS, 0, 0, UI_UNIT_X, UI_UNIT_Y,
635                                    NULL, 0.0, 0.0, 0.0, 0.0, "Show texture in texture tab");
636                 uiButSetFunc(but, template_texture_show, user->ptr.data, user->prop);
637         }
638 }
639