Merge branch 'master' into blender2.8
[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 "BLT_translation.h"
41
42 #include "DNA_brush_types.h"
43 #include "DNA_ID.h"
44 #include "DNA_node_types.h"
45 #include "DNA_object_types.h"
46 #include "DNA_object_force_types.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_linestyle_types.h"
52
53 #include "BKE_context.h"
54 #include "BKE_layer.h"
55 #include "BKE_linestyle.h"
56 #include "BKE_modifier.h"
57 #include "BKE_node.h"
58 #include "BKE_paint.h"
59 #include "BKE_particle.h"
60 #include "BKE_scene.h"
61 #include "BKE_workspace.h"
62 #ifdef WITH_FREESTYLE
63 #  include "BKE_freestyle.h"
64 #endif
65
66 #include "RNA_access.h"
67
68 #include "UI_interface.h"
69 #include "UI_resources.h"
70
71 #include "ED_buttons.h"
72 #include "ED_node.h"
73 #include "ED_screen.h"
74
75 #include "../interface/interface_intern.h"
76
77 #include "buttons_intern.h" // own include
78
79 /************************* Texture User **************************/
80
81 static void buttons_texture_user_property_add(ListBase *users, ID *id,
82                                               PointerRNA ptr, PropertyRNA *prop,
83                                               const char *category, int icon, const char *name)
84 {
85         ButsTextureUser *user = MEM_callocN(sizeof(ButsTextureUser), "ButsTextureUser");
86
87         user->id = id;
88         user->ptr = ptr;
89         user->prop = prop;
90         user->category = category;
91         user->icon = icon;
92         user->name = name;
93         user->index = BLI_listbase_count(users);
94
95         BLI_addtail(users, user);
96 }
97
98 static void buttons_texture_user_node_add(ListBase *users, ID *id,
99                                           bNodeTree *ntree, bNode *node,
100                                           const char *category, int icon, const char *name)
101 {
102         ButsTextureUser *user = MEM_callocN(sizeof(ButsTextureUser), "ButsTextureUser");
103
104         user->id = id;
105         user->ntree = ntree;
106         user->node = node;
107         user->category = category;
108         user->icon = icon;
109         user->name = name;
110         user->index = BLI_listbase_count(users);
111
112         BLI_addtail(users, user);
113 }
114
115 static void buttons_texture_users_find_nodetree(ListBase *users, ID *id,
116                                                 bNodeTree *ntree, const char *category)
117 {
118         bNode *node;
119
120         if (ntree) {
121                 for (node = ntree->nodes.first; node; node = node->next) {
122                         if (node->typeinfo->nclass == NODE_CLASS_TEXTURE) {
123                                 PointerRNA ptr;
124                                 /* PropertyRNA *prop; */ /* UNUSED */
125
126                                 RNA_pointer_create(&ntree->id, &RNA_Node, node, &ptr);
127                                 /* prop = RNA_struct_find_property(&ptr, "texture"); */ /* UNUSED */
128
129                                 buttons_texture_user_node_add(users, id, ntree, node,
130                                                               category, RNA_struct_ui_icon(ptr.type), node->name);
131                         }
132                         else if (node->type == NODE_GROUP && node->id) {
133                                 buttons_texture_users_find_nodetree(users, id, (bNodeTree *)node->id, category);
134                         }
135                 }
136         }
137 }
138
139 static void buttons_texture_modifier_foreach(void *userData, Object *ob, ModifierData *md, const char *propname)
140 {
141         PointerRNA ptr;
142         PropertyRNA *prop;
143         ListBase *users = userData;
144
145         RNA_pointer_create(&ob->id, &RNA_Modifier, md, &ptr);
146         prop = RNA_struct_find_property(&ptr, propname);
147
148         buttons_texture_user_property_add(users, &ob->id, ptr, prop,
149                                           N_("Modifiers"), RNA_struct_ui_icon(ptr.type), md->name);
150 }
151
152 static void buttons_texture_users_from_context(ListBase *users, const bContext *C, SpaceButs *sbuts)
153 {
154         Scene *scene = NULL;
155         Object *ob = NULL;
156         FreestyleLineStyle *linestyle = NULL;
157         Brush *brush = NULL;
158         ID *pinid = sbuts->pinid;
159         bool limited_mode = (sbuts->flag & SB_TEX_USER_LIMITED) != 0;
160
161         /* get data from context */
162         if (pinid) {
163                 if (GS(pinid->name) == ID_SCE)
164                         scene = (Scene *)pinid;
165                 else if (GS(pinid->name) == ID_OB)
166                         ob = (Object *)pinid;
167                 else if (GS(pinid->name) == ID_BR)
168                         brush = (Brush *)pinid;
169                 else if (GS(pinid->name) == ID_LS)
170                         linestyle = (FreestyleLineStyle *)pinid;
171         }
172
173         if (!scene) {
174                 scene = CTX_data_scene(C);
175         }
176
177         const ID_Type id_type = pinid != NULL ? GS(pinid->name) : -1;
178         if (!pinid || id_type == ID_SCE) {
179                 WorkSpace *workspace = CTX_wm_workspace(C);
180                 ViewLayer *view_layer = BKE_workspace_view_layer_get(workspace, scene);
181
182                 brush = BKE_paint_brush(BKE_paint_get_active_from_context(C));
183                 linestyle = BKE_linestyle_active_from_view_layer(view_layer);
184                 ob = OBACT(view_layer);
185         }
186
187         /* fill users */
188         BLI_listbase_clear(users);
189
190         if (linestyle && !limited_mode)
191                 buttons_texture_users_find_nodetree(users, &linestyle->id, linestyle->nodetree, N_("Line Style"));
192
193         if (ob) {
194                 ParticleSystem *psys = psys_get_current(ob);
195                 MTex *mtex;
196                 int a;
197
198                 /* modifiers */
199                 modifiers_foreachTexLink(ob, buttons_texture_modifier_foreach, users);
200
201                 /* particle systems */
202                 if (psys && !limited_mode) {
203                         for (a = 0; a < MAX_MTEX; a++) {
204                                 mtex = psys->part->mtex[a];
205
206                                 if (mtex) {
207                                         PointerRNA ptr;
208                                         PropertyRNA *prop;
209
210                                         RNA_pointer_create(&psys->part->id, &RNA_ParticleSettingsTextureSlot, mtex, &ptr);
211                                         prop = RNA_struct_find_property(&ptr, "texture");
212
213                                         buttons_texture_user_property_add(users, &psys->part->id, ptr, prop, N_("Particles"),
214                                                                           RNA_struct_ui_icon(&RNA_ParticleSettings), psys->name);
215                                 }
216                         }
217                 }
218
219                 /* field */
220                 if (ob->pd && ob->pd->forcefield == PFIELD_TEXTURE) {
221                         PointerRNA ptr;
222                         PropertyRNA *prop;
223
224                         RNA_pointer_create(&ob->id, &RNA_FieldSettings, ob->pd, &ptr);
225                         prop = RNA_struct_find_property(&ptr, "texture");
226
227                         buttons_texture_user_property_add(users, &ob->id, ptr, prop,
228                                                           N_("Fields"), ICON_FORCE_TEXTURE, IFACE_("Texture Field"));
229                 }
230         }
231
232         /* brush */
233         if (brush) {
234                 PointerRNA ptr;
235                 PropertyRNA *prop;
236
237                 /* texture */
238                 RNA_pointer_create(&brush->id, &RNA_BrushTextureSlot, &brush->mtex, &ptr);
239                 prop = RNA_struct_find_property(&ptr, "texture");
240
241                 buttons_texture_user_property_add(users, &brush->id, ptr, prop,
242                                                   N_("Brush"), ICON_BRUSH_DATA, IFACE_("Brush"));
243
244                 /* mask texture */
245                 RNA_pointer_create(&brush->id, &RNA_BrushTextureSlot, &brush->mask_mtex, &ptr);
246                 prop = RNA_struct_find_property(&ptr, "texture");
247
248                 buttons_texture_user_property_add(users, &brush->id, ptr, prop,
249                                                   N_("Brush"), ICON_BRUSH_DATA, IFACE_("Brush Mask"));
250         }
251 }
252
253 void buttons_texture_context_compute(const bContext *C, SpaceButs *sbuts)
254 {
255         /* gather available texture users in context. runs on every draw of
256          * properties editor, before the buttons are created. */
257         ButsContextTexture *ct = sbuts->texuser;
258         ID *pinid = sbuts->pinid;
259
260         if (!ct) {
261                 ct = MEM_callocN(sizeof(ButsContextTexture), "ButsContextTexture");
262                 sbuts->texuser = ct;
263         }
264         else {
265                 BLI_freelistN(&ct->users);
266         }
267
268         buttons_texture_users_from_context(&ct->users, C, sbuts);
269
270         if (pinid && GS(pinid->name) == ID_TE) {
271                 ct->user = NULL;
272                 ct->texture = (Tex *)pinid;
273         }
274         else {
275                 /* set one user as active based on active index */
276                 if (ct->index >= BLI_listbase_count_at_most(&ct->users, ct->index + 1))
277                         ct->index = 0;
278
279                 ct->user = BLI_findlink(&ct->users, ct->index);
280                 ct->texture = NULL;
281
282                 if (ct->user) {
283                         if (ct->user->ptr.data) {
284                                 PointerRNA texptr;
285                                 Tex *tex;
286
287                                 /* get texture datablock pointer if it's a property */
288                                 texptr = RNA_property_pointer_get(&ct->user->ptr, ct->user->prop);
289                                 tex = (RNA_struct_is_a(texptr.type, &RNA_Texture)) ? texptr.data : NULL;
290
291                                 ct->texture = tex;
292                         }
293                         else if (ct->user->node && !(ct->user->node->flag & NODE_ACTIVE_TEXTURE)) {
294                                 ButsTextureUser *user;
295
296                                 /* detect change of active texture node in same node tree, in that
297                                  * case we also automatically switch to the other node */
298                                 for (user = ct->users.first; user; user = user->next) {
299                                         if (user->ntree == ct->user->ntree && user->node != ct->user->node) {
300                                                 if (user->node->flag & NODE_ACTIVE_TEXTURE) {
301                                                         ct->user = user;
302                                                         ct->index = BLI_findindex(&ct->users, user);
303                                                         break;
304                                                 }
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                 if (user->ptr.type == &RNA_ParticleSettingsTextureSlot) {
336                         /* stupid exception for particle systems which still uses influence
337                          * from the old texture system, set the active texture slots as well */
338                         ParticleSettings *part = user->ptr.id.data;
339                         int a;
340
341                         for (a = 0; a < MAX_MTEX; a++)
342                                 if (user->ptr.data == part->mtex[a])
343                                         part->texact = a;
344                 }
345
346                 if (sbuts && tex)
347                         sbuts->preview = 1;
348         }
349
350         ct->user = user;
351         ct->index = user->index;
352 }
353
354 static void template_texture_user_menu(bContext *C, uiLayout *layout, void *UNUSED(arg))
355 {
356         /* callback when opening texture user selection menu, to create buttons. */
357         SpaceButs *sbuts = CTX_wm_space_buts(C);
358         ButsContextTexture *ct = sbuts->texuser;
359         ButsTextureUser *user;
360         uiBlock *block = uiLayoutGetBlock(layout);
361         const char *last_category = NULL;
362
363         for (user = ct->users.first; user; user = user->next) {
364                 uiBut *but;
365                 char name[UI_MAX_NAME_STR];
366
367                 /* add label per category */
368                 if (!last_category || !STREQ(last_category, user->category)) {
369                         uiItemL(layout, IFACE_(user->category), ICON_NONE);
370                         but = block->buttons.last;
371                         but->drawflag = UI_BUT_TEXT_LEFT;
372                 }
373
374                 /* create button */
375                 if (user->prop) {
376                         PointerRNA texptr = RNA_property_pointer_get(&user->ptr, user->prop);
377                         Tex *tex = texptr.data;
378
379                         if (tex)
380                                 BLI_snprintf(name, UI_MAX_NAME_STR, "  %s - %s", user->name, tex->id.name + 2);
381                         else
382                                 BLI_snprintf(name, UI_MAX_NAME_STR, "  %s", user->name);
383                 }
384                 else
385                         BLI_snprintf(name, UI_MAX_NAME_STR, "  %s", user->name);
386
387                 but = uiDefIconTextBut(block, UI_BTYPE_BUT, 0, user->icon, name, 0, 0, UI_UNIT_X * 4, UI_UNIT_Y,
388                                        NULL, 0.0, 0.0, 0.0, 0.0, "");
389                 UI_but_funcN_set(but, template_texture_select, MEM_dupallocN(user), NULL);
390
391                 last_category = user->category;
392         }
393
394         UI_block_flag_enable(block, UI_BLOCK_NO_FLIP);
395 }
396
397 void uiTemplateTextureUser(uiLayout *layout, bContext *C)
398 {
399         /* texture user selection dropdown menu. the available users have been
400          * gathered before drawing in ButsContextTexture, we merely need to
401          * display the current item. */
402         SpaceButs *sbuts = CTX_wm_space_buts(C);
403         ButsContextTexture *ct = (sbuts) ? sbuts->texuser : NULL;
404         uiBlock *block = uiLayoutGetBlock(layout);
405         uiBut *but;
406         ButsTextureUser *user;
407         char name[UI_MAX_NAME_STR];
408
409         if (!ct)
410                 return;
411
412         /* get current user */
413         user = ct->user;
414
415         if (!user) {
416                 uiItemL(layout, IFACE_("No textures in context"), ICON_NONE);
417                 return;
418         }
419
420         /* create button */
421         BLI_strncpy(name, user->name, UI_MAX_NAME_STR);
422
423         if (user->icon) {
424                 but = uiDefIconTextMenuBut(block, template_texture_user_menu, NULL,
425                                            user->icon, name, 0, 0, UI_UNIT_X * 4, UI_UNIT_Y, "");
426         }
427         else {
428                 but = uiDefMenuBut(block, template_texture_user_menu, NULL,
429                                    name, 0, 0, UI_UNIT_X * 4, UI_UNIT_Y, "");
430         }
431
432         /* some cosmetic tweaks */
433         UI_but_type_set_menu_from_pulldown(but);
434
435         but->flag &= ~UI_BUT_ICON_SUBMENU;
436 }
437
438 /************************* Texture Show **************************/
439
440 static void template_texture_show(bContext *C, void *data_p, void *prop_p)
441 {
442         SpaceButs *sbuts = CTX_wm_space_buts(C);
443         ButsContextTexture *ct = (sbuts) ? sbuts->texuser : NULL;
444         ButsTextureUser *user;
445
446         if (!ct)
447                 return;
448
449         for (user = ct->users.first; user; user = user->next)
450                 if (user->ptr.data == data_p && user->prop == prop_p)
451                         break;
452
453         if (user) {
454                 /* select texture */
455                 template_texture_select(C, user, NULL);
456
457                 /* change context */
458                 sbuts->mainb = BCONTEXT_TEXTURE;
459                 sbuts->mainbuser = sbuts->mainb;
460                 sbuts->preview = 1;
461
462                 /* redraw editor */
463                 ED_area_tag_redraw(CTX_wm_area(C));
464         }
465 }
466
467 void uiTemplateTextureShow(uiLayout *layout, bContext *C, PointerRNA *ptr, PropertyRNA *prop)
468 {
469         /* button to quickly show texture in texture tab */
470         SpaceButs *sbuts = CTX_wm_space_buts(C);
471         ButsContextTexture *ct = (sbuts) ? sbuts->texuser : NULL;
472         ButsTextureUser *user;
473
474         /* only show button in other tabs in properties editor */
475         if (!ct || sbuts->mainb == BCONTEXT_TEXTURE)
476                 return;
477
478         /* find corresponding texture user */
479         for (user = ct->users.first; user; user = user->next)
480                 if (user->ptr.data == ptr->data && user->prop == prop)
481                         break;
482
483         /* draw button */
484         if (user) {
485                 uiBlock *block = uiLayoutGetBlock(layout);
486                 uiBut *but;
487
488                 but = uiDefIconBut(block, UI_BTYPE_BUT, 0, ICON_BUTS, 0, 0, UI_UNIT_X, UI_UNIT_Y,
489                                    NULL, 0.0, 0.0, 0.0, 0.0, TIP_("Show texture in texture tab"));
490                 UI_but_func_set(but, template_texture_show, user->ptr.data, user->prop);
491         }
492 }