b9b83abf67033f059108157090f044ee3be10685
[blender.git] / source / blender / editors / space_buttons / buttons_texture.c
1 /*
2  * This program is free software; you can redistribute it and/or
3  * modify it under the terms of the GNU General Public License
4  * as published by the Free Software Foundation; either version 2
5  * of the License, or (at your option) any later version.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * along with this program; if not, write to the Free Software Foundation,
14  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15  *
16  * The Original Code is Copyright (C) 2009 Blender Foundation.
17  * All rights reserved.
18  */
19
20 /** \file
21  * \ingroup spbuttons
22  */
23
24 #include <stdlib.h>
25 #include <string.h>
26
27 #include "MEM_guardedalloc.h"
28
29 #include "BLI_listbase.h"
30 #include "BLI_string.h"
31 #include "BLI_utildefines.h"
32
33 #include "BLT_translation.h"
34
35 #include "DNA_brush_types.h"
36 #include "DNA_ID.h"
37 #include "DNA_node_types.h"
38 #include "DNA_object_types.h"
39 #include "DNA_object_force_types.h"
40 #include "DNA_particle_types.h"
41 #include "DNA_scene_types.h"
42 #include "DNA_screen_types.h"
43 #include "DNA_space_types.h"
44 #include "DNA_linestyle_types.h"
45 #include "DNA_windowmanager_types.h"
46
47 #include "BKE_context.h"
48 #include "BKE_layer.h"
49 #include "BKE_linestyle.h"
50 #include "BKE_modifier.h"
51 #include "BKE_gpencil_modifier.h"
52 #include "BKE_node.h"
53 #include "BKE_paint.h"
54 #include "BKE_particle.h"
55 #include "BKE_scene.h"
56 #include "BKE_workspace.h"
57 #ifdef WITH_FREESTYLE
58 #  include "BKE_freestyle.h"
59 #endif
60
61 #include "RNA_access.h"
62
63 #include "UI_interface.h"
64 #include "UI_resources.h"
65
66 #include "ED_buttons.h"
67 #include "ED_node.h"
68 #include "ED_screen.h"
69
70 #include "WM_api.h"
71
72 #include "../interface/interface_intern.h"
73
74 #include "buttons_intern.h"  // own include
75
76 /************************* Texture User **************************/
77
78 static void buttons_texture_user_property_add(ListBase *users,
79                                               ID *id,
80                                               PointerRNA ptr,
81                                               PropertyRNA *prop,
82                                               const char *category,
83                                               int icon,
84                                               const char *name)
85 {
86   ButsTextureUser *user = MEM_callocN(sizeof(ButsTextureUser), "ButsTextureUser");
87
88   user->id = id;
89   user->ptr = ptr;
90   user->prop = prop;
91   user->category = category;
92   user->icon = icon;
93   user->name = name;
94   user->index = BLI_listbase_count(users);
95
96   BLI_addtail(users, user);
97 }
98
99 static void buttons_texture_user_node_add(ListBase *users,
100                                           ID *id,
101                                           bNodeTree *ntree,
102                                           bNode *node,
103                                           const char *category,
104                                           int icon,
105                                           const char *name)
106 {
107   ButsTextureUser *user = MEM_callocN(sizeof(ButsTextureUser), "ButsTextureUser");
108
109   user->id = id;
110   user->ntree = ntree;
111   user->node = node;
112   user->category = category;
113   user->icon = icon;
114   user->name = name;
115   user->index = BLI_listbase_count(users);
116
117   BLI_addtail(users, user);
118 }
119
120 static void buttons_texture_users_find_nodetree(ListBase *users,
121                                                 ID *id,
122                                                 bNodeTree *ntree,
123                                                 const char *category)
124 {
125   bNode *node;
126
127   if (ntree) {
128     for (node = ntree->nodes.first; node; node = node->next) {
129       if (node->typeinfo->nclass == NODE_CLASS_TEXTURE) {
130         PointerRNA ptr;
131         /* PropertyRNA *prop; */ /* UNUSED */
132
133         RNA_pointer_create(&ntree->id, &RNA_Node, node, &ptr);
134         /* prop = RNA_struct_find_property(&ptr, "texture"); */ /* UNUSED */
135
136         buttons_texture_user_node_add(
137             users, id, ntree, node, category, RNA_struct_ui_icon(ptr.type), node->name);
138       }
139       else if (node->type == NODE_GROUP && node->id) {
140         buttons_texture_users_find_nodetree(users, id, (bNodeTree *)node->id, category);
141       }
142     }
143   }
144 }
145
146 static void buttons_texture_modifier_foreach(void *userData,
147                                              Object *ob,
148                                              ModifierData *md,
149                                              const char *propname)
150 {
151   PointerRNA ptr;
152   PropertyRNA *prop;
153   ListBase *users = userData;
154
155   RNA_pointer_create(&ob->id, &RNA_Modifier, md, &ptr);
156   prop = RNA_struct_find_property(&ptr, propname);
157
158   buttons_texture_user_property_add(
159       users, &ob->id, ptr, prop, N_("Modifiers"), RNA_struct_ui_icon(ptr.type), md->name);
160 }
161
162 static void buttons_texture_modifier_gpencil_foreach(void *userData,
163                                                      Object *ob,
164                                                      GpencilModifierData *md,
165                                                      const char *propname)
166 {
167   PointerRNA ptr;
168   PropertyRNA *prop;
169   ListBase *users = userData;
170
171   RNA_pointer_create(&ob->id, &RNA_GpencilModifier, md, &ptr);
172   prop = RNA_struct_find_property(&ptr, propname);
173
174   buttons_texture_user_property_add(users,
175                                     &ob->id,
176                                     ptr,
177                                     prop,
178                                     N_("Grease Pencil Modifiers"),
179                                     RNA_struct_ui_icon(ptr.type),
180                                     md->name);
181 }
182
183 static void buttons_texture_users_from_context(ListBase *users,
184                                                const bContext *C,
185                                                SpaceProperties *sbuts)
186 {
187   Scene *scene = NULL;
188   Object *ob = NULL;
189   FreestyleLineStyle *linestyle = NULL;
190   Brush *brush = NULL;
191   ID *pinid = sbuts->pinid;
192   bool limited_mode = (sbuts->flag & SB_TEX_USER_LIMITED) != 0;
193
194   /* get data from context */
195   if (pinid) {
196     if (GS(pinid->name) == ID_SCE) {
197       scene = (Scene *)pinid;
198     }
199     else if (GS(pinid->name) == ID_OB) {
200       ob = (Object *)pinid;
201     }
202     else if (GS(pinid->name) == ID_BR) {
203       brush = (Brush *)pinid;
204     }
205     else if (GS(pinid->name) == ID_LS) {
206       linestyle = (FreestyleLineStyle *)pinid;
207     }
208   }
209
210   if (!scene) {
211     scene = CTX_data_scene(C);
212   }
213
214   const ID_Type id_type = pinid != NULL ? GS(pinid->name) : -1;
215   if (!pinid || id_type == ID_SCE) {
216     wmWindow *win = CTX_wm_window(C);
217     ViewLayer *view_layer = (win->scene == scene) ? WM_window_get_active_view_layer(win) :
218                                                     BKE_view_layer_default_view(scene);
219
220     brush = BKE_paint_brush(BKE_paint_get_active_from_context(C));
221     linestyle = BKE_linestyle_active_from_view_layer(view_layer);
222     ob = OBACT(view_layer);
223   }
224
225   /* fill users */
226   BLI_listbase_clear(users);
227
228   if (linestyle && !limited_mode) {
229     buttons_texture_users_find_nodetree(
230         users, &linestyle->id, linestyle->nodetree, N_("Line Style"));
231   }
232
233   if (ob) {
234     ParticleSystem *psys = psys_get_current(ob);
235     MTex *mtex;
236     int a;
237
238     /* modifiers */
239     modifiers_foreachTexLink(ob, buttons_texture_modifier_foreach, users);
240
241     /* grease pencil modifiers */
242     BKE_gpencil_modifiers_foreachTexLink(ob, buttons_texture_modifier_gpencil_foreach, users);
243
244     /* particle systems */
245     if (psys && !limited_mode) {
246       for (a = 0; a < MAX_MTEX; a++) {
247         mtex = psys->part->mtex[a];
248
249         if (mtex) {
250           PointerRNA ptr;
251           PropertyRNA *prop;
252
253           RNA_pointer_create(&psys->part->id, &RNA_ParticleSettingsTextureSlot, mtex, &ptr);
254           prop = RNA_struct_find_property(&ptr, "texture");
255
256           buttons_texture_user_property_add(users,
257                                             &psys->part->id,
258                                             ptr,
259                                             prop,
260                                             N_("Particles"),
261                                             RNA_struct_ui_icon(&RNA_ParticleSettings),
262                                             psys->name);
263         }
264       }
265     }
266
267     /* field */
268     if (ob->pd && ob->pd->forcefield == PFIELD_TEXTURE) {
269       PointerRNA ptr;
270       PropertyRNA *prop;
271
272       RNA_pointer_create(&ob->id, &RNA_FieldSettings, ob->pd, &ptr);
273       prop = RNA_struct_find_property(&ptr, "texture");
274
275       buttons_texture_user_property_add(
276           users, &ob->id, ptr, prop, N_("Fields"), ICON_FORCE_TEXTURE, IFACE_("Texture Field"));
277     }
278   }
279
280   /* brush */
281   if (brush) {
282     PointerRNA ptr;
283     PropertyRNA *prop;
284
285     /* texture */
286     RNA_pointer_create(&brush->id, &RNA_BrushTextureSlot, &brush->mtex, &ptr);
287     prop = RNA_struct_find_property(&ptr, "texture");
288
289     buttons_texture_user_property_add(
290         users, &brush->id, ptr, prop, N_("Brush"), ICON_BRUSH_DATA, IFACE_("Brush"));
291
292     /* mask texture */
293     RNA_pointer_create(&brush->id, &RNA_BrushTextureSlot, &brush->mask_mtex, &ptr);
294     prop = RNA_struct_find_property(&ptr, "texture");
295
296     buttons_texture_user_property_add(
297         users, &brush->id, ptr, prop, N_("Brush"), ICON_BRUSH_DATA, IFACE_("Brush Mask"));
298   }
299 }
300
301 void buttons_texture_context_compute(const bContext *C, SpaceProperties *sbuts)
302 {
303   /* gather available texture users in context. runs on every draw of
304    * properties editor, before the buttons are created. */
305   ButsContextTexture *ct = sbuts->texuser;
306   ID *pinid = sbuts->pinid;
307
308   if (!ct) {
309     ct = MEM_callocN(sizeof(ButsContextTexture), "ButsContextTexture");
310     sbuts->texuser = ct;
311   }
312   else {
313     BLI_freelistN(&ct->users);
314   }
315
316   buttons_texture_users_from_context(&ct->users, C, sbuts);
317
318   if (pinid && GS(pinid->name) == ID_TE) {
319     ct->user = NULL;
320     ct->texture = (Tex *)pinid;
321   }
322   else {
323     /* set one user as active based on active index */
324     if (ct->index >= BLI_listbase_count_at_most(&ct->users, ct->index + 1)) {
325       ct->index = 0;
326     }
327
328     ct->user = BLI_findlink(&ct->users, ct->index);
329     ct->texture = NULL;
330
331     if (ct->user) {
332       if (ct->user->ptr.data) {
333         PointerRNA texptr;
334         Tex *tex;
335
336         /* get texture datablock pointer if it's a property */
337         texptr = RNA_property_pointer_get(&ct->user->ptr, ct->user->prop);
338         tex = (RNA_struct_is_a(texptr.type, &RNA_Texture)) ? texptr.data : NULL;
339
340         ct->texture = tex;
341       }
342       else if (ct->user->node && !(ct->user->node->flag & NODE_ACTIVE_TEXTURE)) {
343         ButsTextureUser *user;
344
345         /* detect change of active texture node in same node tree, in that
346          * case we also automatically switch to the other node */
347         for (user = ct->users.first; user; user = user->next) {
348           if (user->ntree == ct->user->ntree && user->node != ct->user->node) {
349             if (user->node->flag & NODE_ACTIVE_TEXTURE) {
350               ct->user = user;
351               ct->index = BLI_findindex(&ct->users, user);
352               break;
353             }
354           }
355         }
356       }
357     }
358   }
359 }
360
361 static void template_texture_select(bContext *C, void *user_p, void *UNUSED(arg))
362 {
363   /* callback when selecting a texture user in the menu */
364   SpaceProperties *sbuts = CTX_wm_space_properties(C);
365   ButsContextTexture *ct = (sbuts) ? sbuts->texuser : NULL;
366   ButsTextureUser *user = (ButsTextureUser *)user_p;
367   PointerRNA texptr;
368   Tex *tex;
369
370   if (!ct) {
371     return;
372   }
373
374   /* set user as active */
375   if (user->node) {
376     ED_node_set_active(CTX_data_main(C), user->ntree, user->node);
377     ct->texture = NULL;
378   }
379   else {
380     texptr = RNA_property_pointer_get(&user->ptr, user->prop);
381     tex = (RNA_struct_is_a(texptr.type, &RNA_Texture)) ? texptr.data : NULL;
382
383     ct->texture = tex;
384
385     if (user->ptr.type == &RNA_ParticleSettingsTextureSlot) {
386       /* stupid exception for particle systems which still uses influence
387        * from the old texture system, set the active texture slots as well */
388       ParticleSettings *part = user->ptr.id.data;
389       int a;
390
391       for (a = 0; a < MAX_MTEX; a++) {
392         if (user->ptr.data == part->mtex[a]) {
393           part->texact = a;
394         }
395       }
396     }
397
398     if (sbuts && tex) {
399       sbuts->preview = 1;
400     }
401   }
402
403   ct->user = user;
404   ct->index = user->index;
405 }
406
407 static void template_texture_user_menu(bContext *C, uiLayout *layout, void *UNUSED(arg))
408 {
409   /* callback when opening texture user selection menu, to create buttons. */
410   SpaceProperties *sbuts = CTX_wm_space_properties(C);
411   ButsContextTexture *ct = sbuts->texuser;
412   ButsTextureUser *user;
413   uiBlock *block = uiLayoutGetBlock(layout);
414   const char *last_category = NULL;
415
416   for (user = ct->users.first; user; user = user->next) {
417     uiBut *but;
418     char name[UI_MAX_NAME_STR];
419
420     /* add label per category */
421     if (!last_category || !STREQ(last_category, user->category)) {
422       uiItemL(layout, IFACE_(user->category), ICON_NONE);
423       but = block->buttons.last;
424       but->drawflag = UI_BUT_TEXT_LEFT;
425     }
426
427     /* create button */
428     if (user->prop) {
429       PointerRNA texptr = RNA_property_pointer_get(&user->ptr, user->prop);
430       Tex *tex = texptr.data;
431
432       if (tex) {
433         BLI_snprintf(name, UI_MAX_NAME_STR, "  %s - %s", user->name, tex->id.name + 2);
434       }
435       else {
436         BLI_snprintf(name, UI_MAX_NAME_STR, "  %s", user->name);
437       }
438     }
439     else {
440       BLI_snprintf(name, UI_MAX_NAME_STR, "  %s", user->name);
441     }
442
443     but = uiDefIconTextBut(block,
444                            UI_BTYPE_BUT,
445                            0,
446                            user->icon,
447                            name,
448                            0,
449                            0,
450                            UI_UNIT_X * 4,
451                            UI_UNIT_Y,
452                            NULL,
453                            0.0,
454                            0.0,
455                            0.0,
456                            0.0,
457                            "");
458     UI_but_funcN_set(but, template_texture_select, MEM_dupallocN(user), NULL);
459
460     last_category = user->category;
461   }
462
463   UI_block_flag_enable(block, UI_BLOCK_NO_FLIP);
464 }
465
466 void uiTemplateTextureUser(uiLayout *layout, bContext *C)
467 {
468   /* texture user selection dropdown menu. the available users have been
469    * gathered before drawing in ButsContextTexture, we merely need to
470    * display the current item. */
471   SpaceProperties *sbuts = CTX_wm_space_properties(C);
472   ButsContextTexture *ct = (sbuts) ? sbuts->texuser : NULL;
473   uiBlock *block = uiLayoutGetBlock(layout);
474   uiBut *but;
475   ButsTextureUser *user;
476   char name[UI_MAX_NAME_STR];
477
478   if (!ct) {
479     return;
480   }
481
482   /* get current user */
483   user = ct->user;
484
485   if (!user) {
486     uiItemL(layout, IFACE_("No textures in context"), ICON_NONE);
487     return;
488   }
489
490   /* create button */
491   BLI_strncpy(name, user->name, UI_MAX_NAME_STR);
492
493   if (user->icon) {
494     but = uiDefIconTextMenuBut(block,
495                                template_texture_user_menu,
496                                NULL,
497                                user->icon,
498                                name,
499                                0,
500                                0,
501                                UI_UNIT_X * 4,
502                                UI_UNIT_Y,
503                                "");
504   }
505   else {
506     but = uiDefMenuBut(
507         block, template_texture_user_menu, NULL, name, 0, 0, UI_UNIT_X * 4, UI_UNIT_Y, "");
508   }
509
510   /* some cosmetic tweaks */
511   UI_but_type_set_menu_from_pulldown(but);
512
513   but->flag &= ~UI_BUT_ICON_SUBMENU;
514 }
515
516 /************************* Texture Show **************************/
517
518 static void template_texture_show(bContext *C, void *data_p, void *prop_p)
519 {
520   SpaceProperties *sbuts = CTX_wm_space_properties(C);
521   ButsContextTexture *ct = (sbuts) ? sbuts->texuser : NULL;
522   ButsTextureUser *user;
523
524   if (!ct) {
525     return;
526   }
527
528   for (user = ct->users.first; user; user = user->next) {
529     if (user->ptr.data == data_p && user->prop == prop_p) {
530       break;
531     }
532   }
533
534   if (user) {
535     /* select texture */
536     template_texture_select(C, user, NULL);
537
538     /* change context */
539     sbuts->mainb = BCONTEXT_TEXTURE;
540     sbuts->mainbuser = sbuts->mainb;
541     sbuts->preview = 1;
542
543     /* redraw editor */
544     ED_area_tag_redraw(CTX_wm_area(C));
545   }
546 }
547
548 void uiTemplateTextureShow(uiLayout *layout, bContext *C, PointerRNA *ptr, PropertyRNA *prop)
549 {
550   /* button to quickly show texture in texture tab */
551   SpaceProperties *sbuts = CTX_wm_space_properties(C);
552   ButsContextTexture *ct = (sbuts) ? sbuts->texuser : NULL;
553   ButsTextureUser *user;
554
555   /* only show button in other tabs in properties editor */
556   if (!ct || sbuts->mainb == BCONTEXT_TEXTURE) {
557     return;
558   }
559
560   /* find corresponding texture user */
561   for (user = ct->users.first; user; user = user->next) {
562     if (user->ptr.data == ptr->data && user->prop == prop) {
563       break;
564     }
565   }
566
567   /* draw button */
568   if (user) {
569     uiBlock *block = uiLayoutGetBlock(layout);
570     uiBut *but;
571
572     but = uiDefIconBut(block,
573                        UI_BTYPE_BUT,
574                        0,
575                        ICON_PROPERTIES,
576                        0,
577                        0,
578                        UI_UNIT_X,
579                        UI_UNIT_Y,
580                        NULL,
581                        0.0,
582                        0.0,
583                        0.0,
584                        0.0,
585                        TIP_("Show texture in texture tab"));
586     UI_but_func_set(but, template_texture_show, user->ptr.data, user->prop);
587   }
588 }