3dbfd3d2f6a92bfc29c10a8b111d0bc2172c647a
[blender.git] / source / blender / editors / interface / interface_templates.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
17 /** \file
18  * \ingroup edinterface
19  */
20
21 #include <ctype.h>
22 #include <stdlib.h>
23 #include <stddef.h>
24 #include <string.h>
25
26 #include "MEM_guardedalloc.h"
27
28 #include "DNA_cachefile_types.h"
29 #include "DNA_constraint_types.h"
30 #include "DNA_node_types.h"
31 #include "DNA_scene_types.h"
32 #include "DNA_object_types.h"
33 #include "DNA_object_force_types.h"
34 #include "DNA_brush_types.h"
35 #include "DNA_texture_types.h"
36 #include "DNA_gpencil_modifier_types.h"
37 #include "DNA_shader_fx_types.h"
38
39 #include "BLI_utildefines.h"
40 #include "BLI_alloca.h"
41 #include "BLI_string.h"
42 #include "BLI_ghash.h"
43 #include "BLI_rect.h"
44 #include "BLI_math.h"
45 #include "BLI_listbase.h"
46 #include "BLI_fnmatch.h"
47 #include "BLI_path_util.h"
48 #include "BLI_timecode.h"
49
50 #include "BLF_api.h"
51 #include "BLT_translation.h"
52
53 #include "BKE_action.h"
54 #include "BKE_colorband.h"
55 #include "BKE_colortools.h"
56 #include "BKE_constraint.h"
57 #include "BKE_context.h"
58 #include "BKE_global.h"
59 #include "BKE_gpencil_modifier.h"
60 #include "BKE_idcode.h"
61 #include "BKE_idprop.h"
62 #include "BKE_layer.h"
63 #include "BKE_library.h"
64 #include "BKE_library_override.h"
65 #include "BKE_linestyle.h"
66 #include "BKE_main.h"
67 #include "BKE_modifier.h"
68 #include "BKE_object.h"
69 #include "BKE_packedFile.h"
70 #include "BKE_paint.h"
71 #include "BKE_particle.h"
72 #include "BKE_report.h"
73 #include "BKE_screen.h"
74 #include "BKE_shader_fx.h"
75
76 #include "DEG_depsgraph.h"
77 #include "DEG_depsgraph_build.h"
78
79 #include "ED_screen.h"
80 #include "ED_object.h"
81 #include "ED_render.h"
82 #include "ED_undo.h"
83
84 #include "RNA_access.h"
85
86 #include "WM_api.h"
87 #include "WM_types.h"
88
89 #include "BLO_readfile.h"
90
91 #include "UI_interface.h"
92 #include "UI_interface_icons.h"
93 #include "interface_intern.h"
94
95 #include "PIL_time.h"
96
97 // #define USE_OP_RESET_BUT  // we may want to make this optional, disable for now.
98
99 /* defines for templateID/TemplateSearch */
100 #define TEMPLATE_SEARCH_TEXTBUT_WIDTH (UI_UNIT_X * 6)
101 #define TEMPLATE_SEARCH_TEXTBUT_HEIGHT UI_UNIT_Y
102
103 void UI_template_fix_linking(void)
104 {
105 }
106
107 /**
108  * Add a block button for the search menu for templateID and templateSearch.
109  */
110 static void template_add_button_search_menu(const bContext *C,
111                                             uiLayout *layout,
112                                             uiBlock *block,
113                                             PointerRNA *ptr,
114                                             PropertyRNA *prop,
115                                             uiBlockCreateFunc block_func,
116                                             void *block_argN,
117                                             const char *const tip,
118                                             const bool use_previews,
119                                             const bool editable,
120                                             const bool live_icon)
121 {
122   PointerRNA active_ptr = RNA_property_pointer_get(ptr, prop);
123   ID *id = (active_ptr.data && RNA_struct_is_ID(active_ptr.type)) ? active_ptr.data : NULL;
124   const ID *idfrom = ptr->id.data;
125   const StructRNA *type = active_ptr.type ? active_ptr.type : RNA_property_pointer_type(ptr, prop);
126   uiBut *but;
127
128   if (use_previews) {
129     ARegion *region = CTX_wm_region(C);
130     /* Ugly tool header exception. */
131     const bool use_big_size = (region->regiontype != RGN_TYPE_TOOL_HEADER);
132     /* Ugly exception for screens here,
133      * drawing their preview in icon size looks ugly/useless */
134     const bool use_preview_icon = use_big_size || (id && (GS(id->name) != ID_SCR));
135     const short width = UI_UNIT_X * (use_big_size ? 6 : 1.6f);
136     const short height = UI_UNIT_Y * (use_big_size ? 6 : 1);
137
138     but = uiDefBlockButN(block, block_func, block_argN, "", 0, 0, width, height, tip);
139     if (use_preview_icon) {
140       int icon = id ? ui_id_icon_get(C, id, use_big_size) : RNA_struct_ui_icon(type);
141       ui_def_but_icon(but, icon, UI_HAS_ICON | UI_BUT_ICON_PREVIEW);
142     }
143     else {
144       ui_def_but_icon(but, RNA_struct_ui_icon(type), UI_HAS_ICON);
145       UI_but_drawflag_enable(but, UI_BUT_ICON_LEFT);
146     }
147
148     if ((idfrom && idfrom->lib) || !editable) {
149       UI_but_flag_enable(but, UI_BUT_DISABLED);
150     }
151     if (use_big_size) {
152       uiLayoutRow(layout, true);
153     }
154   }
155   else {
156     but = uiDefBlockButN(block, block_func, block_argN, "", 0, 0, UI_UNIT_X * 1.6, UI_UNIT_Y, tip);
157
158     if (live_icon) {
159       int icon = id ? ui_id_icon_get(C, id, false) : RNA_struct_ui_icon(type);
160       ui_def_but_icon(but, icon, UI_HAS_ICON | UI_BUT_ICON_PREVIEW);
161     }
162     else {
163       ui_def_but_icon(but, RNA_struct_ui_icon(type), UI_HAS_ICON);
164     }
165     if (id) {
166       /* default dragging of icon for id browse buttons */
167       UI_but_drag_set_id(but, id);
168     }
169     UI_but_drawflag_enable(but, UI_BUT_ICON_LEFT);
170
171     if ((idfrom && idfrom->lib) || !editable) {
172       UI_but_flag_enable(but, UI_BUT_DISABLED);
173     }
174   }
175 }
176
177 static uiBlock *template_common_search_menu(const bContext *C,
178                                             ARegion *region,
179                                             uiButSearchFunc search_func,
180                                             void *search_arg,
181                                             uiButHandleFunc handle_func,
182                                             void *active_item,
183                                             const int preview_rows,
184                                             const int preview_cols,
185                                             float scale)
186 {
187   static char search[256];
188   wmWindow *win = CTX_wm_window(C);
189   uiBlock *block;
190   uiBut *but;
191
192   /* clear initial search string, then all items show */
193   search[0] = 0;
194
195   block = UI_block_begin(C, region, "_popup", UI_EMBOSS);
196   UI_block_flag_enable(block, UI_BLOCK_LOOP | UI_BLOCK_SEARCH_MENU);
197   UI_block_theme_style_set(block, UI_BLOCK_THEME_STYLE_POPUP);
198
199   /* preview thumbnails */
200   if (preview_rows > 0 && preview_cols > 0) {
201     const int w = 4 * U.widget_unit * preview_cols * scale;
202     const int h = 5 * U.widget_unit * preview_rows * scale;
203
204     /* fake button, it holds space for search items */
205     uiDefBut(block, UI_BTYPE_LABEL, 0, "", 10, 26, w, h, NULL, 0, 0, 0, 0, NULL);
206
207     but = uiDefSearchBut(block,
208                          search,
209                          0,
210                          ICON_VIEWZOOM,
211                          sizeof(search),
212                          10,
213                          0,
214                          w,
215                          UI_UNIT_Y,
216                          preview_rows,
217                          preview_cols,
218                          "");
219   }
220   /* list view */
221   else {
222     const int searchbox_width = UI_searchbox_size_x();
223     const int searchbox_height = UI_searchbox_size_y();
224
225     /* fake button, it holds space for search items */
226     uiDefBut(block,
227              UI_BTYPE_LABEL,
228              0,
229              "",
230              10,
231              15,
232              searchbox_width,
233              searchbox_height,
234              NULL,
235              0,
236              0,
237              0,
238              0,
239              NULL);
240     but = uiDefSearchBut(block,
241                          search,
242                          0,
243                          ICON_VIEWZOOM,
244                          sizeof(search),
245                          10,
246                          0,
247                          searchbox_width,
248                          UI_UNIT_Y - 1,
249                          0,
250                          0,
251                          "");
252   }
253   UI_but_func_search_set(
254       but, ui_searchbox_create_generic, search_func, search_arg, false, handle_func, active_item);
255
256   UI_block_bounds_set_normal(block, 0.3f * U.widget_unit);
257   UI_block_direction_set(block, UI_DIR_DOWN);
258
259   /* give search-field focus */
260   UI_but_focus_on_enter_event(win, but);
261   /* this type of search menu requires undo */
262   but->flag |= UI_BUT_UNDO;
263
264   return block;
265 }
266
267 /********************** Header Template *************************/
268
269 void uiTemplateHeader(uiLayout *layout, bContext *C)
270 {
271   uiBlock *block;
272
273   block = uiLayoutAbsoluteBlock(layout);
274   ED_area_header_switchbutton(C, block, 0);
275 }
276
277 /********************** Search Callbacks *************************/
278
279 typedef struct TemplateID {
280   PointerRNA ptr;
281   PropertyRNA *prop;
282
283   ListBase *idlb;
284   short idcode;
285   short filter;
286   int prv_rows, prv_cols;
287   bool preview;
288   float scale;
289 } TemplateID;
290
291 /* Search browse menu, assign  */
292 static void template_ID_set_property_cb(bContext *C, void *arg_template, void *item)
293 {
294   TemplateID *template_ui = (TemplateID *)arg_template;
295
296   /* ID */
297   if (item) {
298     PointerRNA idptr;
299
300     RNA_id_pointer_create(item, &idptr);
301     RNA_property_pointer_set(NULL, &template_ui->ptr, template_ui->prop, idptr);
302     RNA_property_update(C, &template_ui->ptr, template_ui->prop);
303   }
304 }
305
306 static bool id_search_add(const bContext *C,
307                           TemplateID *template_ui,
308                           const int flag,
309                           const char *str,
310                           uiSearchItems *items,
311                           ID *id)
312 {
313   ID *id_from = template_ui->ptr.id.data;
314
315   if (!((flag & PROP_ID_SELF_CHECK) && id == id_from)) {
316
317     /* use filter */
318     if (RNA_property_type(template_ui->prop) == PROP_POINTER) {
319       PointerRNA ptr;
320       RNA_id_pointer_create(id, &ptr);
321       if (RNA_property_pointer_poll(&template_ui->ptr, template_ui->prop, &ptr) == 0) {
322         return true;
323       }
324     }
325
326     /* hide dot-datablocks, but only if filter does not force it visible */
327     if (U.uiflag & USER_HIDE_DOT) {
328       if ((id->name[2] == '.') && (str[0] != '.')) {
329         return true;
330       }
331     }
332
333     if (*str == '\0' || BLI_strcasestr(id->name + 2, str)) {
334       /* +1 is needed because BKE_id_ui_prefix used 3 letter prefix
335        * followed by ID_NAME-2 characters from id->name
336        */
337       char name_ui[MAX_ID_FULL_NAME_UI];
338       BKE_id_full_name_ui_prefix_get(name_ui, id);
339
340       int iconid = ui_id_icon_get(C, id, template_ui->preview);
341
342       if (false == UI_search_item_add(items, name_ui, id, iconid)) {
343         return false;
344       }
345     }
346   }
347   return true;
348 }
349
350 /* ID Search browse menu, do the search */
351 static void id_search_cb(const bContext *C,
352                          void *arg_template,
353                          const char *str,
354                          uiSearchItems *items)
355 {
356   TemplateID *template_ui = (TemplateID *)arg_template;
357   ListBase *lb = template_ui->idlb;
358   ID *id;
359   int flag = RNA_property_flag(template_ui->prop);
360
361   /* ID listbase */
362   for (id = lb->first; id; id = id->next) {
363     if (!id_search_add(C, template_ui, flag, str, items, id)) {
364       break;
365     }
366   }
367 }
368
369 /**
370  * Use id tags for filtering.
371  */
372 static void id_search_cb_tagged(const bContext *C,
373                                 void *arg_template,
374                                 const char *str,
375                                 uiSearchItems *items)
376 {
377   TemplateID *template_ui = (TemplateID *)arg_template;
378   ListBase *lb = template_ui->idlb;
379   ID *id;
380   int flag = RNA_property_flag(template_ui->prop);
381
382   /* ID listbase */
383   for (id = lb->first; id; id = id->next) {
384     if (id->tag & LIB_TAG_DOIT) {
385       if (!id_search_add(C, template_ui, flag, str, items, id)) {
386         break;
387       }
388       id->tag &= ~LIB_TAG_DOIT;
389     }
390   }
391 }
392
393 /**
394  * A version of 'id_search_cb' that lists scene objects.
395  */
396 static void id_search_cb_objects_from_scene(const bContext *C,
397                                             void *arg_template,
398                                             const char *str,
399                                             uiSearchItems *items)
400 {
401   TemplateID *template_ui = (TemplateID *)arg_template;
402   ListBase *lb = template_ui->idlb;
403   Scene *scene = NULL;
404   ID *id_from = template_ui->ptr.id.data;
405
406   if (id_from && GS(id_from->name) == ID_SCE) {
407     scene = (Scene *)id_from;
408   }
409   else {
410     scene = CTX_data_scene(C);
411   }
412
413   BKE_main_id_flag_listbase(lb, LIB_TAG_DOIT, false);
414
415   FOREACH_SCENE_OBJECT_BEGIN (scene, ob_iter) {
416     ob_iter->id.tag |= LIB_TAG_DOIT;
417   }
418   FOREACH_SCENE_OBJECT_END;
419   id_search_cb_tagged(C, arg_template, str, items);
420 }
421
422 /* ID Search browse menu, open */
423 static uiBlock *id_search_menu(bContext *C, ARegion *ar, void *arg_litem)
424 {
425   static TemplateID template_ui;
426   PointerRNA active_item_ptr;
427   void (*id_search_cb_p)(const bContext *, void *, const char *, uiSearchItems *) = id_search_cb;
428
429   /* arg_litem is malloced, can be freed by parent button */
430   template_ui = *((TemplateID *)arg_litem);
431   active_item_ptr = RNA_property_pointer_get(&template_ui.ptr, template_ui.prop);
432
433   if (template_ui.filter) {
434     /* Currently only used for objects. */
435     if (template_ui.idcode == ID_OB) {
436       if (template_ui.filter == UI_TEMPLATE_ID_FILTER_AVAILABLE) {
437         id_search_cb_p = id_search_cb_objects_from_scene;
438       }
439     }
440   }
441
442   return template_common_search_menu(C,
443                                      ar,
444                                      id_search_cb_p,
445                                      &template_ui,
446                                      template_ID_set_property_cb,
447                                      active_item_ptr.data,
448                                      template_ui.prv_rows,
449                                      template_ui.prv_cols,
450                                      template_ui.scale);
451 }
452
453 /************************ ID Template ***************************/
454 /* This is for browsing and editing the ID-blocks used */
455
456 /* for new/open operators */
457 void UI_context_active_but_prop_get_templateID(bContext *C,
458                                                PointerRNA *r_ptr,
459                                                PropertyRNA **r_prop)
460 {
461   TemplateID *template_ui;
462   uiBut *but = UI_context_active_but_get(C);
463
464   memset(r_ptr, 0, sizeof(*r_ptr));
465   *r_prop = NULL;
466
467   if (but && but->func_argN) {
468     template_ui = but->func_argN;
469     *r_ptr = template_ui->ptr;
470     *r_prop = template_ui->prop;
471   }
472 }
473
474 static void template_id_cb(bContext *C, void *arg_litem, void *arg_event)
475 {
476   TemplateID *template_ui = (TemplateID *)arg_litem;
477   PointerRNA idptr = RNA_property_pointer_get(&template_ui->ptr, template_ui->prop);
478   ID *id = idptr.data;
479   int event = POINTER_AS_INT(arg_event);
480
481   switch (event) {
482     case UI_ID_BROWSE:
483     case UI_ID_PIN:
484       RNA_warning("warning, id event %d shouldnt come here", event);
485       break;
486     case UI_ID_OPEN:
487     case UI_ID_ADD_NEW:
488       /* these call UI_context_active_but_prop_get_templateID */
489       break;
490     case UI_ID_DELETE:
491       memset(&idptr, 0, sizeof(idptr));
492       RNA_property_pointer_set(NULL, &template_ui->ptr, template_ui->prop, idptr);
493       RNA_property_update(C, &template_ui->ptr, template_ui->prop);
494
495       if (id && CTX_wm_window(C)->eventstate->shift) {
496         /* only way to force-remove data (on save) */
497         id_fake_user_clear(id);
498         id->us = 0;
499       }
500
501       break;
502     case UI_ID_FAKE_USER:
503       if (id) {
504         if (id->flag & LIB_FAKEUSER) {
505           id_us_plus(id);
506         }
507         else {
508           id_us_min(id);
509         }
510       }
511       else {
512         return;
513       }
514       break;
515     case UI_ID_LOCAL:
516       if (id) {
517         Main *bmain = CTX_data_main(C);
518         if (BKE_override_static_is_enabled() && CTX_wm_window(C)->eventstate->shift) {
519           ID *override_id = BKE_override_static_create_from_id(bmain, id);
520           if (override_id != NULL) {
521             BKE_main_id_clear_newpoins(bmain);
522
523             /* Assign new pointer, takes care of updates/notifiers */
524             RNA_id_pointer_create(override_id, &idptr);
525           }
526         }
527         else {
528           if (id_make_local(bmain, id, false, false)) {
529             BKE_main_id_clear_newpoins(bmain);
530
531             /* reassign to get get proper updates/notifiers */
532             idptr = RNA_property_pointer_get(&template_ui->ptr, template_ui->prop);
533           }
534         }
535         RNA_property_pointer_set(NULL, &template_ui->ptr, template_ui->prop, idptr);
536         RNA_property_update(C, &template_ui->ptr, template_ui->prop);
537       }
538       break;
539     case UI_ID_OVERRIDE:
540       if (id && id->override_static) {
541         BKE_override_static_free(&id->override_static);
542         /* reassign to get get proper updates/notifiers */
543         idptr = RNA_property_pointer_get(&template_ui->ptr, template_ui->prop);
544         RNA_property_pointer_set(NULL, &template_ui->ptr, template_ui->prop, idptr);
545         RNA_property_update(C, &template_ui->ptr, template_ui->prop);
546       }
547       break;
548     case UI_ID_ALONE:
549       if (id) {
550         const bool do_scene_obj = ((GS(id->name) == ID_OB) &&
551                                    (template_ui->ptr.type == &RNA_LayerObjects));
552
553         /* make copy */
554         if (do_scene_obj) {
555           Main *bmain = CTX_data_main(C);
556           Scene *scene = CTX_data_scene(C);
557           ED_object_single_user(bmain, scene, (struct Object *)id);
558           WM_event_add_notifier(C, NC_WINDOW, NULL);
559           DEG_relations_tag_update(bmain);
560         }
561         else {
562           if (id) {
563             Main *bmain = CTX_data_main(C);
564             id_single_user(C, id, &template_ui->ptr, template_ui->prop);
565             DEG_relations_tag_update(bmain);
566           }
567         }
568       }
569       break;
570 #if 0
571     case UI_ID_AUTO_NAME:
572       break;
573 #endif
574   }
575 }
576
577 static const char *template_id_browse_tip(const StructRNA *type)
578 {
579   if (type) {
580     switch (RNA_type_to_ID_code(type)) {
581       case ID_SCE:
582         return N_("Browse Scene to be linked");
583       case ID_OB:
584         return N_("Browse Object to be linked");
585       case ID_ME:
586         return N_("Browse Mesh Data to be linked");
587       case ID_CU:
588         return N_("Browse Curve Data to be linked");
589       case ID_MB:
590         return N_("Browse Metaball Data to be linked");
591       case ID_MA:
592         return N_("Browse Material to be linked");
593       case ID_TE:
594         return N_("Browse Texture to be linked");
595       case ID_IM:
596         return N_("Browse Image to be linked");
597       case ID_LS:
598         return N_("Browse Line Style Data to be linked");
599       case ID_LT:
600         return N_("Browse Lattice Data to be linked");
601       case ID_LA:
602         return N_("Browse Light Data to be linked");
603       case ID_CA:
604         return N_("Browse Camera Data to be linked");
605       case ID_WO:
606         return N_("Browse World Settings to be linked");
607       case ID_SCR:
608         return N_("Choose Screen layout");
609       case ID_TXT:
610         return N_("Browse Text to be linked");
611       case ID_SPK:
612         return N_("Browse Speaker Data to be linked");
613       case ID_SO:
614         return N_("Browse Sound to be linked");
615       case ID_AR:
616         return N_("Browse Armature data to be linked");
617       case ID_AC:
618         return N_("Browse Action to be linked");
619       case ID_NT:
620         return N_("Browse Node Tree to be linked");
621       case ID_BR:
622         return N_("Browse Brush to be linked");
623       case ID_PA:
624         return N_("Browse Particle Settings to be linked");
625       case ID_GD:
626         return N_("Browse Grease Pencil Data to be linked");
627       case ID_MC:
628         return N_("Browse Movie Clip to be linked");
629       case ID_MSK:
630         return N_("Browse Mask to be linked");
631       case ID_PAL:
632         return N_("Browse Palette Data to be linked");
633       case ID_PC:
634         return N_("Browse Paint Curve Data to be linked");
635       case ID_CF:
636         return N_("Browse Cache Files to be linked");
637       case ID_WS:
638         return N_("Browse Workspace to be linked");
639       case ID_LP:
640         return N_("Browse LightProbe to be linked");
641     }
642   }
643   return N_("Browse ID data to be linked");
644 }
645
646 /**
647  * \return a type-based i18n context, needed e.g. by "New" button.
648  * In most languages, this adjective takes different form based on gender of type name...
649  */
650 #ifdef WITH_INTERNATIONAL
651 static const char *template_id_context(StructRNA *type)
652 {
653   if (type) {
654     return BKE_idcode_to_translation_context(RNA_type_to_ID_code(type));
655   }
656   return BLT_I18NCONTEXT_DEFAULT;
657 }
658 #endif
659
660 static uiBut *template_id_def_new_but(uiBlock *block,
661                                       const ID *id,
662                                       const TemplateID *template_ui,
663                                       StructRNA *type,
664                                       const char *const newop,
665                                       const bool editable,
666                                       const bool id_open,
667                                       const bool use_tab_but,
668                                       int but_height)
669 {
670   ID *idfrom = template_ui->ptr.id.data;
671   uiBut *but;
672   const int w = id ? UI_UNIT_X : id_open ? UI_UNIT_X * 3 : UI_UNIT_X * 6;
673   const int but_type = use_tab_but ? UI_BTYPE_TAB : UI_BTYPE_BUT;
674
675   /* i18n markup, does nothing! */
676   BLT_I18N_MSGID_MULTI_CTXT("New",
677                             BLT_I18NCONTEXT_DEFAULT,
678                             BLT_I18NCONTEXT_ID_SCENE,
679                             BLT_I18NCONTEXT_ID_OBJECT,
680                             BLT_I18NCONTEXT_ID_MESH,
681                             BLT_I18NCONTEXT_ID_CURVE,
682                             BLT_I18NCONTEXT_ID_METABALL,
683                             BLT_I18NCONTEXT_ID_MATERIAL,
684                             BLT_I18NCONTEXT_ID_TEXTURE,
685                             BLT_I18NCONTEXT_ID_IMAGE,
686                             BLT_I18NCONTEXT_ID_LATTICE,
687                             BLT_I18NCONTEXT_ID_LIGHT,
688                             BLT_I18NCONTEXT_ID_CAMERA,
689                             BLT_I18NCONTEXT_ID_WORLD,
690                             BLT_I18NCONTEXT_ID_SCREEN,
691                             BLT_I18NCONTEXT_ID_TEXT, );
692   BLT_I18N_MSGID_MULTI_CTXT("New",
693                             BLT_I18NCONTEXT_ID_SPEAKER,
694                             BLT_I18NCONTEXT_ID_SOUND,
695                             BLT_I18NCONTEXT_ID_ARMATURE,
696                             BLT_I18NCONTEXT_ID_ACTION,
697                             BLT_I18NCONTEXT_ID_NODETREE,
698                             BLT_I18NCONTEXT_ID_BRUSH,
699                             BLT_I18NCONTEXT_ID_PARTICLESETTINGS,
700                             BLT_I18NCONTEXT_ID_GPENCIL,
701                             BLT_I18NCONTEXT_ID_FREESTYLELINESTYLE,
702                             BLT_I18NCONTEXT_ID_WORKSPACE,
703                             BLT_I18NCONTEXT_ID_LIGHTPROBE, );
704
705   if (newop) {
706     but = uiDefIconTextButO(block,
707                             but_type,
708                             newop,
709                             WM_OP_INVOKE_DEFAULT,
710                             (id && !use_tab_but) ? ICON_DUPLICATE : ICON_ADD,
711                             (id) ? "" : CTX_IFACE_(template_id_context(type), "New"),
712                             0,
713                             0,
714                             w,
715                             but_height,
716                             NULL);
717     UI_but_funcN_set(
718         but, template_id_cb, MEM_dupallocN(template_ui), POINTER_FROM_INT(UI_ID_ADD_NEW));
719   }
720   else {
721     but = uiDefIconTextBut(block,
722                            but_type,
723                            0,
724                            (id && !use_tab_but) ? ICON_DUPLICATE : ICON_ADD,
725                            (id) ? "" : CTX_IFACE_(template_id_context(type), "New"),
726                            0,
727                            0,
728                            w,
729                            but_height,
730                            NULL,
731                            0,
732                            0,
733                            0,
734                            0,
735                            NULL);
736     UI_but_funcN_set(
737         but, template_id_cb, MEM_dupallocN(template_ui), POINTER_FROM_INT(UI_ID_ADD_NEW));
738   }
739
740   if ((idfrom && idfrom->lib) || !editable) {
741     UI_but_flag_enable(but, UI_BUT_DISABLED);
742   }
743
744 #ifndef WITH_INTERNATIONAL
745   UNUSED_VARS(type);
746 #endif
747
748   return but;
749 }
750
751 static void template_ID(bContext *C,
752                         uiLayout *layout,
753                         TemplateID *template_ui,
754                         StructRNA *type,
755                         int flag,
756                         const char *newop,
757                         const char *openop,
758                         const char *unlinkop,
759                         const bool live_icon,
760                         const bool hide_buttons)
761 {
762   uiBut *but;
763   uiBlock *block;
764   PointerRNA idptr;
765   // ListBase *lb; // UNUSED
766   ID *id, *idfrom;
767   const bool editable = RNA_property_editable(&template_ui->ptr, template_ui->prop);
768   const bool use_previews = template_ui->preview = (flag & UI_ID_PREVIEWS) != 0;
769
770   idptr = RNA_property_pointer_get(&template_ui->ptr, template_ui->prop);
771   id = idptr.data;
772   idfrom = template_ui->ptr.id.data;
773   // lb = template_ui->idlb;
774
775   block = uiLayoutGetBlock(layout);
776   UI_block_align_begin(block);
777
778   if (idptr.type) {
779     type = idptr.type;
780   }
781
782   if (flag & UI_ID_BROWSE) {
783     template_add_button_search_menu(C,
784                                     layout,
785                                     block,
786                                     &template_ui->ptr,
787                                     template_ui->prop,
788                                     id_search_menu,
789                                     MEM_dupallocN(template_ui),
790                                     TIP_(template_id_browse_tip(type)),
791                                     use_previews,
792                                     editable,
793                                     live_icon);
794   }
795
796   /* text button with name */
797   if (id) {
798     char name[UI_MAX_NAME_STR];
799     const bool user_alert = (id->us <= 0);
800
801     // text_idbutton(id, name);
802     name[0] = '\0';
803     but = uiDefButR(block,
804                     UI_BTYPE_TEXT,
805                     0,
806                     name,
807                     0,
808                     0,
809                     TEMPLATE_SEARCH_TEXTBUT_WIDTH,
810                     TEMPLATE_SEARCH_TEXTBUT_HEIGHT,
811                     &idptr,
812                     "name",
813                     -1,
814                     0,
815                     0,
816                     -1,
817                     -1,
818                     RNA_struct_ui_description(type));
819     UI_but_funcN_set(
820         but, template_id_cb, MEM_dupallocN(template_ui), POINTER_FROM_INT(UI_ID_RENAME));
821     if (user_alert) {
822       UI_but_flag_enable(but, UI_BUT_REDALERT);
823     }
824
825     if (id->lib) {
826       if (id->tag & LIB_TAG_INDIRECT) {
827         but = uiDefIconBut(block,
828                            UI_BTYPE_BUT,
829                            0,
830                            ICON_LIBRARY_DATA_INDIRECT,
831                            0,
832                            0,
833                            UI_UNIT_X,
834                            UI_UNIT_Y,
835                            NULL,
836                            0,
837                            0,
838                            0,
839                            0,
840                            TIP_("Indirect library data-block, cannot change"));
841         UI_but_flag_enable(but, UI_BUT_DISABLED);
842       }
843       else {
844         const bool disabled = (!id_make_local(CTX_data_main(C), id, true /* test */, false) ||
845                                (idfrom && idfrom->lib));
846         but = uiDefIconBut(block,
847                            UI_BTYPE_BUT,
848                            0,
849                            ICON_LIBRARY_DATA_DIRECT,
850                            0,
851                            0,
852                            UI_UNIT_X,
853                            UI_UNIT_Y,
854                            NULL,
855                            0,
856                            0,
857                            0,
858                            0,
859                            BKE_override_static_is_enabled() ?
860                                TIP_("Direct linked library data-block, click to make local, "
861                                     "Shift + Click to create a static override") :
862                                TIP_("Direct linked library data-block, click to make local"));
863         if (disabled) {
864           UI_but_flag_enable(but, UI_BUT_DISABLED);
865         }
866         else {
867           UI_but_funcN_set(
868               but, template_id_cb, MEM_dupallocN(template_ui), POINTER_FROM_INT(UI_ID_LOCAL));
869         }
870       }
871     }
872     else if (ID_IS_STATIC_OVERRIDE(id)) {
873       but = uiDefIconBut(
874           block,
875           UI_BTYPE_BUT,
876           0,
877           ICON_LIBRARY_DATA_OVERRIDE,
878           0,
879           0,
880           UI_UNIT_X,
881           UI_UNIT_Y,
882           NULL,
883           0,
884           0,
885           0,
886           0,
887           TIP_("Static override of linked library data-block, click to make fully local"));
888       UI_but_funcN_set(
889           but, template_id_cb, MEM_dupallocN(template_ui), POINTER_FROM_INT(UI_ID_OVERRIDE));
890     }
891
892     if ((ID_REAL_USERS(id) > 1) && (hide_buttons == false)) {
893       char numstr[32];
894       short numstr_len;
895
896       numstr_len = BLI_snprintf(numstr, sizeof(numstr), "%d", ID_REAL_USERS(id));
897
898       but = uiDefBut(
899           block,
900           UI_BTYPE_BUT,
901           0,
902           numstr,
903           0,
904           0,
905           numstr_len * 0.2f * UI_UNIT_X + UI_UNIT_X,
906           UI_UNIT_Y,
907           NULL,
908           0,
909           0,
910           0,
911           0,
912           TIP_("Display number of users of this data (click to make a single-user copy)"));
913       but->flag |= UI_BUT_UNDO;
914
915       UI_but_funcN_set(
916           but, template_id_cb, MEM_dupallocN(template_ui), POINTER_FROM_INT(UI_ID_ALONE));
917       if ((!BKE_id_copy_is_allowed(id)) || (idfrom && idfrom->lib) || (!editable) ||
918           /* object in editmode - don't change data */
919           (idfrom && GS(idfrom->name) == ID_OB && (((Object *)idfrom)->mode & OB_MODE_EDIT))) {
920         UI_but_flag_enable(but, UI_BUT_DISABLED);
921       }
922     }
923
924     if (user_alert) {
925       UI_but_flag_enable(but, UI_BUT_REDALERT);
926     }
927
928     if (id->lib == NULL && !(ELEM(GS(id->name), ID_GR, ID_SCE, ID_SCR, ID_TXT, ID_OB, ID_WS)) &&
929         (hide_buttons == false)) {
930       uiDefIconButR(block,
931                     UI_BTYPE_ICON_TOGGLE,
932                     0,
933                     ICON_FAKE_USER_OFF,
934                     0,
935                     0,
936                     UI_UNIT_X,
937                     UI_UNIT_Y,
938                     &idptr,
939                     "use_fake_user",
940                     -1,
941                     0,
942                     0,
943                     -1,
944                     -1,
945                     NULL);
946     }
947   }
948
949   if ((flag & UI_ID_ADD_NEW) && (hide_buttons == false)) {
950     template_id_def_new_but(
951         block, id, template_ui, type, newop, editable, flag & UI_ID_OPEN, false, UI_UNIT_X);
952   }
953
954   /* Due to space limit in UI - skip the "open" icon for packed data, and allow to unpack.
955    * Only for images, sound and fonts */
956   if (id && BKE_pack_check(id)) {
957     but = uiDefIconButO(block,
958                         UI_BTYPE_BUT,
959                         "FILE_OT_unpack_item",
960                         WM_OP_INVOKE_REGION_WIN,
961                         ICON_PACKAGE,
962                         0,
963                         0,
964                         UI_UNIT_X,
965                         UI_UNIT_Y,
966                         TIP_("Packed File, click to unpack"));
967     UI_but_operator_ptr_get(but);
968
969     RNA_string_set(but->opptr, "id_name", id->name + 2);
970     RNA_int_set(but->opptr, "id_type", GS(id->name));
971   }
972   else if (flag & UI_ID_OPEN) {
973     int w = id ? UI_UNIT_X : (flag & UI_ID_ADD_NEW) ? UI_UNIT_X * 3 : UI_UNIT_X * 6;
974
975     if (openop) {
976       but = uiDefIconTextButO(block,
977                               UI_BTYPE_BUT,
978                               openop,
979                               WM_OP_INVOKE_DEFAULT,
980                               ICON_FILEBROWSER,
981                               (id) ? "" : IFACE_("Open"),
982                               0,
983                               0,
984                               w,
985                               UI_UNIT_Y,
986                               NULL);
987       UI_but_funcN_set(
988           but, template_id_cb, MEM_dupallocN(template_ui), POINTER_FROM_INT(UI_ID_OPEN));
989     }
990     else {
991       but = uiDefIconTextBut(block,
992                              UI_BTYPE_BUT,
993                              0,
994                              ICON_FILEBROWSER,
995                              (id) ? "" : IFACE_("Open"),
996                              0,
997                              0,
998                              w,
999                              UI_UNIT_Y,
1000                              NULL,
1001                              0,
1002                              0,
1003                              0,
1004                              0,
1005                              NULL);
1006       UI_but_funcN_set(
1007           but, template_id_cb, MEM_dupallocN(template_ui), POINTER_FROM_INT(UI_ID_OPEN));
1008     }
1009
1010     if ((idfrom && idfrom->lib) || !editable) {
1011       UI_but_flag_enable(but, UI_BUT_DISABLED);
1012     }
1013   }
1014
1015   /* delete button */
1016   /* don't use RNA_property_is_unlink here */
1017   if (id && (flag & UI_ID_DELETE) && (hide_buttons == false)) {
1018     /* allow unlink if 'unlinkop' is passed, even when 'PROP_NEVER_UNLINK' is set */
1019     but = NULL;
1020
1021     if (unlinkop) {
1022       but = uiDefIconButO(block,
1023                           UI_BTYPE_BUT,
1024                           unlinkop,
1025                           WM_OP_INVOKE_DEFAULT,
1026                           ICON_X,
1027                           0,
1028                           0,
1029                           UI_UNIT_X,
1030                           UI_UNIT_Y,
1031                           NULL);
1032       /* so we can access the template from operators, font unlinking needs this */
1033       UI_but_funcN_set(but, NULL, MEM_dupallocN(template_ui), NULL);
1034     }
1035     else {
1036       if ((RNA_property_flag(template_ui->prop) & PROP_NEVER_UNLINK) == 0) {
1037         but = uiDefIconBut(
1038             block,
1039             UI_BTYPE_BUT,
1040             0,
1041             ICON_X,
1042             0,
1043             0,
1044             UI_UNIT_X,
1045             UI_UNIT_Y,
1046             NULL,
1047             0,
1048             0,
1049             0,
1050             0,
1051             TIP_("Unlink data-block "
1052                  "(Shift + Click to set users to zero, data will then not be saved)"));
1053         UI_but_funcN_set(
1054             but, template_id_cb, MEM_dupallocN(template_ui), POINTER_FROM_INT(UI_ID_DELETE));
1055
1056         if (RNA_property_flag(template_ui->prop) & PROP_NEVER_NULL) {
1057           UI_but_flag_enable(but, UI_BUT_DISABLED);
1058         }
1059       }
1060     }
1061
1062     if (but) {
1063       if ((idfrom && idfrom->lib) || !editable) {
1064         UI_but_flag_enable(but, UI_BUT_DISABLED);
1065       }
1066     }
1067   }
1068
1069   if (template_ui->idcode == ID_TE) {
1070     uiTemplateTextureShow(layout, C, &template_ui->ptr, template_ui->prop);
1071   }
1072   UI_block_align_end(block);
1073 }
1074
1075 ID *UI_context_active_but_get_tab_ID(bContext *C)
1076 {
1077   uiBut *but = UI_context_active_but_get(C);
1078
1079   if (but && but->type == UI_BTYPE_TAB) {
1080     return but->custom_data;
1081   }
1082   else {
1083     return NULL;
1084   }
1085 }
1086
1087 static void template_ID_tabs(bContext *C,
1088                              uiLayout *layout,
1089                              TemplateID *template,
1090                              StructRNA *type,
1091                              int flag,
1092                              const char *newop,
1093                              const char *menu)
1094 {
1095   const ARegion *region = CTX_wm_region(C);
1096   const PointerRNA active_ptr = RNA_property_pointer_get(&template->ptr, template->prop);
1097   MenuType *mt = WM_menutype_find(menu, false);
1098
1099   const int but_align = ui_but_align_opposite_to_area_align_get(region);
1100   const int but_height = UI_UNIT_Y * 1.1;
1101
1102   uiBlock *block = uiLayoutGetBlock(layout);
1103   uiStyle *style = UI_style_get_dpi();
1104
1105   ListBase ordered;
1106   BKE_id_ordered_list(&ordered, template->idlb);
1107
1108   for (LinkData *link = ordered.first; link; link = link->next) {
1109     ID *id = link->data;
1110     const int name_width = UI_fontstyle_string_width(&style->widgetlabel, id->name + 2);
1111     const int but_width = name_width + UI_UNIT_X;
1112
1113     uiButTab *tab = (uiButTab *)uiDefButR_prop(block,
1114                                                UI_BTYPE_TAB,
1115                                                0,
1116                                                id->name + 2,
1117                                                0,
1118                                                0,
1119                                                but_width,
1120                                                but_height,
1121                                                &template->ptr,
1122                                                template->prop,
1123                                                0,
1124                                                0.0f,
1125                                                sizeof(id->name) - 2,
1126                                                0.0f,
1127                                                0.0f,
1128                                                "");
1129     UI_but_funcN_set(&tab->but, template_ID_set_property_cb, MEM_dupallocN(template), id);
1130     tab->but.custom_data = (void *)id;
1131     tab->but.dragpoin = id;
1132     tab->menu = mt;
1133
1134     UI_but_drawflag_enable(&tab->but, but_align);
1135   }
1136
1137   BLI_freelistN(&ordered);
1138
1139   if (flag & UI_ID_ADD_NEW) {
1140     const bool editable = RNA_property_editable(&template->ptr, template->prop);
1141     uiBut *but;
1142
1143     if (active_ptr.type) {
1144       type = active_ptr.type;
1145     }
1146
1147     but = template_id_def_new_but(block,
1148                                   active_ptr.data,
1149                                   template,
1150                                   type,
1151                                   newop,
1152                                   editable,
1153                                   flag & UI_ID_OPEN,
1154                                   true,
1155                                   but_height);
1156     UI_but_drawflag_enable(but, but_align);
1157   }
1158 }
1159
1160 static void ui_template_id(uiLayout *layout,
1161                            bContext *C,
1162                            PointerRNA *ptr,
1163                            const char *propname,
1164                            const char *newop,
1165                            const char *openop,
1166                            const char *unlinkop,
1167                            int flag,
1168                            int prv_rows,
1169                            int prv_cols,
1170                            int filter,
1171                            bool use_tabs,
1172                            float scale,
1173                            const bool live_icon,
1174                            const bool hide_buttons)
1175 {
1176   TemplateID *template_ui;
1177   PropertyRNA *prop;
1178   StructRNA *type;
1179   short idcode;
1180
1181   prop = RNA_struct_find_property(ptr, propname);
1182
1183   if (!prop || RNA_property_type(prop) != PROP_POINTER) {
1184     RNA_warning("pointer property not found: %s.%s", RNA_struct_identifier(ptr->type), propname);
1185     return;
1186   }
1187
1188   template_ui = MEM_callocN(sizeof(TemplateID), "TemplateID");
1189   template_ui->ptr = *ptr;
1190   template_ui->prop = prop;
1191   template_ui->prv_rows = prv_rows;
1192   template_ui->prv_cols = prv_cols;
1193   template_ui->scale = scale;
1194
1195   if ((flag & UI_ID_PIN) == 0) {
1196     template_ui->filter = filter;
1197   }
1198   else {
1199     template_ui->filter = 0;
1200   }
1201
1202   if (newop) {
1203     flag |= UI_ID_ADD_NEW;
1204   }
1205   if (openop) {
1206     flag |= UI_ID_OPEN;
1207   }
1208
1209   type = RNA_property_pointer_type(ptr, prop);
1210   idcode = RNA_type_to_ID_code(type);
1211   template_ui->idcode = idcode;
1212   template_ui->idlb = which_libbase(CTX_data_main(C), idcode);
1213
1214   /* create UI elements for this template
1215    * - template_ID makes a copy of the template data and assigns it to the relevant buttons
1216    */
1217   if (template_ui->idlb) {
1218     if (use_tabs) {
1219       uiLayoutRow(layout, true);
1220       template_ID_tabs(C, layout, template_ui, type, flag, newop, unlinkop);
1221     }
1222     else {
1223       uiLayoutRow(layout, true);
1224       template_ID(
1225           C, layout, template_ui, type, flag, newop, openop, unlinkop, live_icon, hide_buttons);
1226     }
1227   }
1228
1229   MEM_freeN(template_ui);
1230 }
1231
1232 void uiTemplateID(uiLayout *layout,
1233                   bContext *C,
1234                   PointerRNA *ptr,
1235                   const char *propname,
1236                   const char *newop,
1237                   const char *openop,
1238                   const char *unlinkop,
1239                   int filter,
1240                   const bool live_icon)
1241 {
1242   ui_template_id(layout,
1243                  C,
1244                  ptr,
1245                  propname,
1246                  newop,
1247                  openop,
1248                  unlinkop,
1249                  UI_ID_BROWSE | UI_ID_RENAME | UI_ID_DELETE,
1250                  0,
1251                  0,
1252                  filter,
1253                  false,
1254                  1.0f,
1255                  live_icon,
1256                  false);
1257 }
1258
1259 void uiTemplateIDBrowse(uiLayout *layout,
1260                         bContext *C,
1261                         PointerRNA *ptr,
1262                         const char *propname,
1263                         const char *newop,
1264                         const char *openop,
1265                         const char *unlinkop,
1266                         int filter)
1267 {
1268   ui_template_id(layout,
1269                  C,
1270                  ptr,
1271                  propname,
1272                  newop,
1273                  openop,
1274                  unlinkop,
1275                  UI_ID_BROWSE | UI_ID_RENAME,
1276                  0,
1277                  0,
1278                  filter,
1279                  false,
1280                  1.0f,
1281                  false,
1282                  false);
1283 }
1284
1285 void uiTemplateIDPreview(uiLayout *layout,
1286                          bContext *C,
1287                          PointerRNA *ptr,
1288                          const char *propname,
1289                          const char *newop,
1290                          const char *openop,
1291                          const char *unlinkop,
1292                          int rows,
1293                          int cols,
1294                          int filter,
1295                          const bool hide_buttons)
1296 {
1297   ui_template_id(layout,
1298                  C,
1299                  ptr,
1300                  propname,
1301                  newop,
1302                  openop,
1303                  unlinkop,
1304                  UI_ID_BROWSE | UI_ID_RENAME | UI_ID_DELETE | UI_ID_PREVIEWS,
1305                  rows,
1306                  cols,
1307                  filter,
1308                  false,
1309                  1.0f,
1310                  false,
1311                  hide_buttons);
1312 }
1313
1314 void uiTemplateGpencilColorPreview(uiLayout *layout,
1315                                    bContext *C,
1316                                    PointerRNA *ptr,
1317                                    const char *propname,
1318                                    int rows,
1319                                    int cols,
1320                                    float scale,
1321                                    int filter)
1322 {
1323   ui_template_id(layout,
1324                  C,
1325                  ptr,
1326                  propname,
1327                  NULL,
1328                  NULL,
1329                  NULL,
1330                  UI_ID_BROWSE | UI_ID_PREVIEWS | UI_ID_DELETE,
1331                  rows,
1332                  cols,
1333                  filter,
1334                  false,
1335                  scale < 0.5f ? 0.5f : scale,
1336                  false,
1337                  false);
1338 }
1339
1340 /**
1341  * Version of #uiTemplateID using tabs.
1342  */
1343 void uiTemplateIDTabs(uiLayout *layout,
1344                       bContext *C,
1345                       PointerRNA *ptr,
1346                       const char *propname,
1347                       const char *newop,
1348                       const char *unlinkop,
1349                       int filter)
1350 {
1351   ui_template_id(layout,
1352                  C,
1353                  ptr,
1354                  propname,
1355                  newop,
1356                  NULL,
1357                  unlinkop,
1358                  UI_ID_BROWSE | UI_ID_RENAME,
1359                  0,
1360                  0,
1361                  filter,
1362                  true,
1363                  1.0f,
1364                  false,
1365                  false);
1366 }
1367
1368 /************************ ID Chooser Template ***************************/
1369
1370 /**
1371  * This is for selecting the type of ID-block to use,
1372  * and then from the relevant type choosing the block to use.
1373  *
1374  * \param propname: property identifier for property that ID-pointer gets stored to.
1375  * \param proptypename: property identifier for property
1376  * used to determine the type of ID-pointer that can be used.
1377  */
1378 void uiTemplateAnyID(uiLayout *layout,
1379                      PointerRNA *ptr,
1380                      const char *propname,
1381                      const char *proptypename,
1382                      const char *text)
1383 {
1384   PropertyRNA *propID, *propType;
1385   uiLayout *split, *row, *sub;
1386
1387   /* get properties... */
1388   propID = RNA_struct_find_property(ptr, propname);
1389   propType = RNA_struct_find_property(ptr, proptypename);
1390
1391   if (!propID || RNA_property_type(propID) != PROP_POINTER) {
1392     RNA_warning("pointer property not found: %s.%s", RNA_struct_identifier(ptr->type), propname);
1393     return;
1394   }
1395   if (!propType || RNA_property_type(propType) != PROP_ENUM) {
1396     RNA_warning(
1397         "pointer-type property not found: %s.%s", RNA_struct_identifier(ptr->type), proptypename);
1398     return;
1399   }
1400
1401   /* Start drawing UI Elements using standard defines */
1402
1403   /* NOTE: split amount here needs to be synced with normal labels */
1404   split = uiLayoutSplit(layout, 0.33f, false);
1405
1406   /* FIRST PART ................................................ */
1407   row = uiLayoutRow(split, false);
1408
1409   /* Label - either use the provided text, or will become "ID-Block:" */
1410   if (text) {
1411     if (text[0]) {
1412       uiItemL(row, text, ICON_NONE);
1413     }
1414   }
1415   else {
1416     uiItemL(row, IFACE_("ID-Block:"), ICON_NONE);
1417   }
1418
1419   /* SECOND PART ................................................ */
1420   row = uiLayoutRow(split, true);
1421
1422   /* ID-Type Selector - just have a menu of icons */
1423
1424   /* HACK: special group just for the enum,
1425    * otherwise we get ugly layout with text included too... */
1426   sub = uiLayoutRow(row, true);
1427   uiLayoutSetAlignment(sub, UI_LAYOUT_ALIGN_LEFT);
1428
1429   uiItemFullR(sub, ptr, propType, 0, 0, UI_ITEM_R_ICON_ONLY, "", ICON_NONE);
1430
1431   /* ID-Block Selector - just use pointer widget... */
1432
1433   /* HACK: special group to counteract the effects of the previous enum,
1434    * which now pushes everything too far right. */
1435   sub = uiLayoutRow(row, true);
1436   uiLayoutSetAlignment(sub, UI_LAYOUT_ALIGN_EXPAND);
1437
1438   uiItemFullR(sub, ptr, propID, 0, 0, 0, "", ICON_NONE);
1439 }
1440
1441 /********************* Search Template ********************/
1442
1443 typedef struct TemplateSearch {
1444   uiRNACollectionSearch search_data;
1445
1446   bool use_previews;
1447   int preview_rows, preview_cols;
1448 } TemplateSearch;
1449
1450 static void template_search_handle_cb(bContext *C, void *arg_template, void *item)
1451 {
1452   TemplateSearch *template_search = arg_template;
1453   uiRNACollectionSearch *coll_search = &template_search->search_data;
1454   StructRNA *type = RNA_property_pointer_type(&coll_search->target_ptr, coll_search->target_prop);
1455   PointerRNA item_ptr;
1456
1457   RNA_pointer_create(NULL, type, item, &item_ptr);
1458   RNA_property_pointer_set(NULL, &coll_search->target_ptr, coll_search->target_prop, item_ptr);
1459   RNA_property_update(C, &coll_search->target_ptr, coll_search->target_prop);
1460 }
1461
1462 static uiBlock *template_search_menu(bContext *C, ARegion *region, void *arg_template)
1463 {
1464   static TemplateSearch template_search;
1465   PointerRNA active_ptr;
1466
1467   /* arg_template is malloced, can be freed by parent button */
1468   template_search = *((TemplateSearch *)arg_template);
1469   active_ptr = RNA_property_pointer_get(&template_search.search_data.target_ptr,
1470                                         template_search.search_data.target_prop);
1471
1472   return template_common_search_menu(C,
1473                                      region,
1474                                      ui_rna_collection_search_cb,
1475                                      &template_search,
1476                                      template_search_handle_cb,
1477                                      active_ptr.data,
1478                                      template_search.preview_rows,
1479                                      template_search.preview_cols,
1480                                      1.0f);
1481 }
1482
1483 static void template_search_add_button_searchmenu(const bContext *C,
1484                                                   uiLayout *layout,
1485                                                   uiBlock *block,
1486                                                   TemplateSearch *template_search,
1487                                                   const bool editable,
1488                                                   const bool live_icon)
1489 {
1490   const char *ui_description = RNA_property_ui_description(
1491       template_search->search_data.target_prop);
1492
1493   template_add_button_search_menu(C,
1494                                   layout,
1495                                   block,
1496                                   &template_search->search_data.target_ptr,
1497                                   template_search->search_data.target_prop,
1498                                   template_search_menu,
1499                                   MEM_dupallocN(template_search),
1500                                   ui_description,
1501                                   template_search->use_previews,
1502                                   editable,
1503                                   live_icon);
1504 }
1505
1506 static void template_search_add_button_name(uiBlock *block,
1507                                             PointerRNA *active_ptr,
1508                                             const StructRNA *type)
1509 {
1510   uiDefAutoButR(block,
1511                 active_ptr,
1512                 RNA_struct_name_property(type),
1513                 0,
1514                 "",
1515                 ICON_NONE,
1516                 0,
1517                 0,
1518                 TEMPLATE_SEARCH_TEXTBUT_WIDTH,
1519                 TEMPLATE_SEARCH_TEXTBUT_HEIGHT);
1520 }
1521
1522 static void template_search_add_button_operator(uiBlock *block,
1523                                                 const char *const operator_name,
1524                                                 const int opcontext,
1525                                                 const int icon,
1526                                                 const bool editable)
1527 {
1528   if (!operator_name) {
1529     return;
1530   }
1531
1532   uiBut *but = uiDefIconButO(
1533       block, UI_BTYPE_BUT, operator_name, opcontext, icon, 0, 0, UI_UNIT_X, UI_UNIT_Y, NULL);
1534
1535   if (!editable) {
1536     UI_but_drawflag_enable(but, UI_BUT_DISABLED);
1537   }
1538 }
1539
1540 static void template_search_buttons(const bContext *C,
1541                                     uiLayout *layout,
1542                                     TemplateSearch *template_search,
1543                                     const char *newop,
1544                                     const char *unlinkop)
1545 {
1546   uiBlock *block = uiLayoutGetBlock(layout);
1547   uiRNACollectionSearch *search_data = &template_search->search_data;
1548   StructRNA *type = RNA_property_pointer_type(&search_data->target_ptr, search_data->target_prop);
1549   const bool editable = RNA_property_editable(&search_data->target_ptr, search_data->target_prop);
1550   PointerRNA active_ptr = RNA_property_pointer_get(&search_data->target_ptr,
1551                                                    search_data->target_prop);
1552
1553   if (active_ptr.type) {
1554     /* can only get correct type when there is an active item */
1555     type = active_ptr.type;
1556   }
1557
1558   uiLayoutRow(layout, true);
1559   UI_block_align_begin(block);
1560
1561   template_search_add_button_searchmenu(C, layout, block, template_search, editable, false);
1562   template_search_add_button_name(block, &active_ptr, type);
1563   template_search_add_button_operator(
1564       block, newop, WM_OP_INVOKE_DEFAULT, ICON_DUPLICATE, editable);
1565   template_search_add_button_operator(block, unlinkop, WM_OP_INVOKE_REGION_WIN, ICON_X, editable);
1566
1567   UI_block_align_end(block);
1568 }
1569
1570 static PropertyRNA *template_search_get_searchprop(PointerRNA *targetptr,
1571                                                    PropertyRNA *targetprop,
1572                                                    PointerRNA *searchptr,
1573                                                    const char *const searchpropname)
1574 {
1575   PropertyRNA *searchprop;
1576
1577   if (searchptr && !searchptr->data) {
1578     searchptr = NULL;
1579   }
1580
1581   if (!searchptr && !searchpropname) {
1582     /* both NULL means we don't use a custom rna collection to search in */
1583   }
1584   else if (!searchptr && searchpropname) {
1585     RNA_warning("searchpropname defined (%s) but searchptr is missing", searchpropname);
1586   }
1587   else if (searchptr && !searchpropname) {
1588     RNA_warning("searchptr defined (%s) but searchpropname is missing",
1589                 RNA_struct_identifier(searchptr->type));
1590   }
1591   else if (!(searchprop = RNA_struct_find_property(searchptr, searchpropname))) {
1592     RNA_warning("search collection property not found: %s.%s",
1593                 RNA_struct_identifier(searchptr->type),
1594                 searchpropname);
1595   }
1596   else if (RNA_property_type(searchprop) != PROP_COLLECTION) {
1597     RNA_warning("search collection property is not a collection type: %s.%s",
1598                 RNA_struct_identifier(searchptr->type),
1599                 searchpropname);
1600   }
1601   /* check if searchprop has same type as targetprop */
1602   else if (RNA_property_pointer_type(searchptr, searchprop) !=
1603            RNA_property_pointer_type(targetptr, targetprop)) {
1604     RNA_warning("search collection items from %s.%s are not of type %s",
1605                 RNA_struct_identifier(searchptr->type),
1606                 searchpropname,
1607                 RNA_struct_identifier(RNA_property_pointer_type(targetptr, targetprop)));
1608   }
1609   else {
1610     return searchprop;
1611   }
1612
1613   return NULL;
1614 }
1615
1616 static TemplateSearch *template_search_setup(PointerRNA *ptr,
1617                                              const char *const propname,
1618                                              PointerRNA *searchptr,
1619                                              const char *const searchpropname)
1620 {
1621   TemplateSearch *template_search;
1622   PropertyRNA *prop, *searchprop;
1623
1624   prop = RNA_struct_find_property(ptr, propname);
1625
1626   if (!prop || RNA_property_type(prop) != PROP_POINTER) {
1627     RNA_warning("pointer property not found: %s.%s", RNA_struct_identifier(ptr->type), propname);
1628     return NULL;
1629   }
1630   searchprop = template_search_get_searchprop(ptr, prop, searchptr, searchpropname);
1631
1632   template_search = MEM_callocN(sizeof(*template_search), __func__);
1633   template_search->search_data.target_ptr = *ptr;
1634   template_search->search_data.target_prop = prop;
1635   template_search->search_data.search_ptr = *searchptr;
1636   template_search->search_data.search_prop = searchprop;
1637
1638   return template_search;
1639 }
1640
1641 /**
1642  * Search menu to pick an item from a collection.
1643  * A version of uiTemplateID that works for non-ID types.
1644  */
1645 void uiTemplateSearch(uiLayout *layout,
1646                       bContext *C,
1647                       PointerRNA *ptr,
1648                       const char *propname,
1649                       PointerRNA *searchptr,
1650                       const char *searchpropname,
1651                       const char *newop,
1652                       const char *unlinkop)
1653 {
1654   TemplateSearch *template_search = template_search_setup(
1655       ptr, propname, searchptr, searchpropname);
1656   if (template_search != NULL) {
1657     template_search_buttons(C, layout, template_search, newop, unlinkop);
1658     MEM_freeN(template_search);
1659   }
1660 }
1661
1662 void uiTemplateSearchPreview(uiLayout *layout,
1663                              bContext *C,
1664                              PointerRNA *ptr,
1665                              const char *propname,
1666                              PointerRNA *searchptr,
1667                              const char *searchpropname,
1668                              const char *newop,
1669                              const char *unlinkop,
1670                              const int rows,
1671                              const int cols)
1672 {
1673   TemplateSearch *template_search = template_search_setup(
1674       ptr, propname, searchptr, searchpropname);
1675
1676   if (template_search != NULL) {
1677     template_search->use_previews = true;
1678     template_search->preview_rows = rows;
1679     template_search->preview_cols = cols;
1680
1681     template_search_buttons(C, layout, template_search, newop, unlinkop);
1682
1683     MEM_freeN(template_search);
1684   }
1685 }
1686
1687 /********************* RNA Path Builder Template ********************/
1688
1689 /* ---------- */
1690
1691 /**
1692  * This is creating/editing RNA-Paths
1693  *
1694  * - ptr: struct which holds the path property
1695  * - propname: property identifier for property that path gets stored to
1696  * - root_ptr: struct that path gets built from
1697  */
1698 void uiTemplatePathBuilder(uiLayout *layout,
1699                            PointerRNA *ptr,
1700                            const char *propname,
1701                            PointerRNA *UNUSED(root_ptr),
1702                            const char *text)
1703 {
1704   PropertyRNA *propPath;
1705   uiLayout *row;
1706
1707   /* check that properties are valid */
1708   propPath = RNA_struct_find_property(ptr, propname);
1709   if (!propPath || RNA_property_type(propPath) != PROP_STRING) {
1710     RNA_warning("path property not found: %s.%s", RNA_struct_identifier(ptr->type), propname);
1711     return;
1712   }
1713
1714   /* Start drawing UI Elements using standard defines */
1715   row = uiLayoutRow(layout, true);
1716
1717   /* Path (existing string) Widget */
1718   uiItemR(row, ptr, propname, 0, text, ICON_RNA);
1719
1720   /* TODO: attach something to this to make allow
1721    * searching of nested properties to 'build' the path */
1722 }
1723
1724 /************************ Modifier Template *************************/
1725
1726 #define ERROR_LIBDATA_MESSAGE IFACE_("Can't edit external library data")
1727
1728 static void modifiers_convertToReal(bContext *C, void *ob_v, void *md_v)
1729 {
1730   Object *ob = ob_v;
1731   ModifierData *md = md_v;
1732   ModifierData *nmd = modifier_new(md->type);
1733
1734   modifier_copyData(md, nmd);
1735   nmd->mode &= ~eModifierMode_Virtual;
1736
1737   BLI_addhead(&ob->modifiers, nmd);
1738
1739   modifier_unique_name(&ob->modifiers, nmd);
1740
1741   ob->partype = PAROBJECT;
1742
1743   WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER, ob);
1744   DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
1745
1746   ED_undo_push(C, "Modifier convert to real");
1747 }
1748
1749 static int modifier_can_delete(ModifierData *md)
1750 {
1751   /* fluid particle modifier can't be deleted here */
1752   if (md->type == eModifierType_ParticleSystem) {
1753     if (((ParticleSystemModifierData *)md)->psys->part->type == PART_FLUID) {
1754       return 0;
1755     }
1756   }
1757
1758   return 1;
1759 }
1760
1761 /* Check whether Modifier is a simulation or not,
1762  * this is used for switching to the physics/particles context tab */
1763 static int modifier_is_simulation(ModifierData *md)
1764 {
1765   /* Physic Tab */
1766   if (ELEM(md->type,
1767            eModifierType_Cloth,
1768            eModifierType_Collision,
1769            eModifierType_Fluidsim,
1770            eModifierType_Smoke,
1771            eModifierType_Softbody,
1772            eModifierType_Surface,
1773            eModifierType_DynamicPaint)) {
1774     return 1;
1775   }
1776   /* Particle Tab */
1777   else if (md->type == eModifierType_ParticleSystem) {
1778     return 2;
1779   }
1780   else {
1781     return 0;
1782   }
1783 }
1784
1785 static uiLayout *draw_modifier(uiLayout *layout,
1786                                Scene *scene,
1787                                Object *ob,
1788                                ModifierData *md,
1789                                int index,
1790                                int cageIndex,
1791                                int lastCageIndex)
1792 {
1793   const ModifierTypeInfo *mti = modifierType_getInfo(md->type);
1794   PointerRNA ptr;
1795   uiBut *but;
1796   uiBlock *block;
1797   uiLayout *box, *column, *row, *sub;
1798   uiLayout *result = NULL;
1799   int isVirtual = (md->mode & eModifierMode_Virtual);
1800   char str[128];
1801
1802   /* create RNA pointer */
1803   RNA_pointer_create(&ob->id, &RNA_Modifier, md, &ptr);
1804
1805   column = uiLayoutColumn(layout, true);
1806   uiLayoutSetContextPointer(column, "modifier", &ptr);
1807
1808   /* rounded header ------------------------------------------------------------------- */
1809   box = uiLayoutBox(column);
1810
1811   if (isVirtual) {
1812     row = uiLayoutRow(box, false);
1813     uiLayoutSetAlignment(row, UI_LAYOUT_ALIGN_EXPAND);
1814     block = uiLayoutGetBlock(row);
1815     /* VIRTUAL MODIFIER */
1816     /* XXX this is not used now, since these cannot be accessed via RNA */
1817     BLI_snprintf(str, sizeof(str), IFACE_("%s parent deform"), md->name);
1818     uiDefBut(block,
1819              UI_BTYPE_LABEL,
1820              0,
1821              str,
1822              0,
1823              0,
1824              185,
1825              UI_UNIT_Y,
1826              NULL,
1827              0.0,
1828              0.0,
1829              0.0,
1830              0.0,
1831              TIP_("Modifier name"));
1832
1833     but = uiDefBut(block,
1834                    UI_BTYPE_BUT,
1835                    0,
1836                    IFACE_("Make Real"),
1837                    0,
1838                    0,
1839                    80,
1840                    16,
1841                    NULL,
1842                    0.0,
1843                    0.0,
1844                    0.0,
1845                    0.0,
1846                    TIP_("Convert virtual modifier to a real modifier"));
1847     UI_but_func_set(but, modifiers_convertToReal, ob, md);
1848   }
1849   else {
1850     /* REAL MODIFIER */
1851     row = uiLayoutRow(box, false);
1852     block = uiLayoutGetBlock(row);
1853
1854     UI_block_emboss_set(block, UI_EMBOSS_NONE);
1855     /* Open/Close .................................  */
1856     uiItemR(row, &ptr, "show_expanded", 0, "", ICON_NONE);
1857
1858     /* modifier-type icon */
1859     uiItemL(row, "", RNA_struct_ui_icon(ptr.type));
1860     UI_block_emboss_set(block, UI_EMBOSS);
1861
1862     /* modifier name */
1863     if (mti->isDisabled && mti->isDisabled(scene, md, 0)) {
1864       uiLayoutSetRedAlert(row, true);
1865     }
1866     uiItemR(row, &ptr, "name", 0, "", ICON_NONE);
1867     uiLayoutSetRedAlert(row, false);
1868
1869     /* mode enabling buttons */
1870     UI_block_align_begin(block);
1871     /* Collision and Surface are always enabled, hide buttons! */
1872     if (((md->type != eModifierType_Collision) || !(ob->pd && ob->pd->deflect)) &&
1873         (md->type != eModifierType_Surface)) {
1874       uiItemR(row, &ptr, "show_render", 0, "", ICON_NONE);
1875       uiItemR(row, &ptr, "show_viewport", 0, "", ICON_NONE);
1876
1877       if (mti->flags & eModifierTypeFlag_SupportsEditmode) {
1878         sub = uiLayoutRow(row, true);
1879         if (!(md->mode & eModifierMode_Realtime)) {
1880           uiLayoutSetActive(sub, false);
1881         }
1882         uiItemR(sub, &ptr, "show_in_editmode", 0, "", ICON_NONE);
1883       }
1884     }
1885
1886     if (ob->type == OB_MESH) {
1887       if (modifier_supportsCage(scene, md) && (index <= lastCageIndex)) {
1888         sub = uiLayoutRow(row, true);
1889         if (index < cageIndex || !modifier_couldBeCage(scene, md)) {
1890           uiLayoutSetActive(sub, false);
1891         }
1892         uiItemR(sub, &ptr, "show_on_cage", 0, "", ICON_NONE);
1893       }
1894     } /* tessellation point for curve-typed objects */
1895     else if (ELEM(ob->type, OB_CURVE, OB_SURF, OB_FONT)) {
1896       /* some modifiers could work with pre-tessellated curves only */
1897       if (ELEM(md->type, eModifierType_Hook, eModifierType_Softbody, eModifierType_MeshDeform)) {
1898         /* add disabled pre-tessellated button, so users could have
1899          * message for this modifiers */
1900         but = uiDefIconButBitI(block,
1901                                UI_BTYPE_TOGGLE,
1902                                eModifierMode_ApplyOnSpline,
1903                                0,
1904                                ICON_SURFACE_DATA,
1905                                0,
1906                                0,
1907                                UI_UNIT_X - 2,
1908                                UI_UNIT_Y,
1909                                &md->mode,
1910                                0.0,
1911                                0.0,
1912                                0.0,
1913                                0.0,
1914                                TIP_("This modifier can only be applied on splines' points"));
1915         UI_but_flag_enable(but, UI_BUT_DISABLED);
1916       }
1917       else if (mti->type != eModifierTypeType_Constructive) {
1918         /* constructive modifiers tessellates curve before applying */
1919         uiItemR(row, &ptr, "use_apply_on_spline", 0, "", ICON_NONE);
1920       }
1921     }
1922
1923     UI_block_align_end(block);
1924
1925     /* Up/Down + Delete ........................... */
1926     UI_block_align_begin(block);
1927     uiItemO(row, "", ICON_TRIA_UP, "OBJECT_OT_modifier_move_up");
1928     uiItemO(row, "", ICON_TRIA_DOWN, "OBJECT_OT_modifier_move_down");
1929     UI_block_align_end(block);
1930
1931     UI_block_emboss_set(block, UI_EMBOSS_NONE);
1932     /* When Modifier is a simulation,
1933      * show button to switch to context rather than the delete button. */
1934     if (modifier_can_delete(md) && !modifier_is_simulation(md)) {
1935       uiItemO(row, "", ICON_X, "OBJECT_OT_modifier_remove");
1936     }
1937     else if (modifier_is_simulation(md) == 1) {
1938       uiItemStringO(
1939           row, "", ICON_PROPERTIES, "WM_OT_properties_context_change", "context", "PHYSICS");
1940     }
1941     else if (modifier_is_simulation(md) == 2) {
1942       uiItemStringO(
1943           row, "", ICON_PROPERTIES, "WM_OT_properties_context_change", "context", "PARTICLES");
1944     }
1945     UI_block_emboss_set(block, UI_EMBOSS);
1946   }
1947
1948   /* modifier settings (under the header) --------------------------------------------------- */
1949   if (!isVirtual && (md->mode & eModifierMode_Expanded)) {
1950     /* apply/convert/copy */
1951     box = uiLayoutBox(column);
1952     row = uiLayoutRow(box, false);
1953
1954     if (!ELEM(md->type, eModifierType_Collision, eModifierType_Surface)) {
1955       /* only here obdata, the rest of modifiers is ob level */
1956       UI_block_lock_set(block, BKE_object_obdata_is_libdata(ob), ERROR_LIBDATA_MESSAGE);
1957
1958       if (md->type == eModifierType_ParticleSystem) {
1959         ParticleSystem *psys = ((ParticleSystemModifierData *)md)->psys;
1960
1961         if (!(ob->mode & OB_MODE_PARTICLE_EDIT)) {
1962           if (ELEM(psys->part->ren_as, PART_DRAW_GR, PART_DRAW_OB)) {
1963             uiItemO(row,
1964                     CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Convert"),
1965                     ICON_NONE,
1966                     "OBJECT_OT_duplicates_make_real");
1967           }
1968           else if (psys->part->ren_as == PART_DRAW_PATH) {
1969             uiItemO(row,
1970                     CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Convert"),
1971                     ICON_NONE,
1972                     "OBJECT_OT_modifier_convert");
1973           }
1974         }
1975       }
1976       else {
1977         uiLayoutSetOperatorContext(row, WM_OP_INVOKE_DEFAULT);
1978         uiItemEnumO(row,
1979                     "OBJECT_OT_modifier_apply",
1980                     CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Apply"),
1981                     0,
1982                     "apply_as",
1983                     MODIFIER_APPLY_DATA);
1984
1985         if (modifier_isSameTopology(md) && !modifier_isNonGeometrical(md)) {
1986           uiItemEnumO(row,
1987                       "OBJECT_OT_modifier_apply",
1988                       CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Apply as Shape Key"),
1989                       0,
1990                       "apply_as",
1991                       MODIFIER_APPLY_SHAPE);
1992         }
1993       }
1994
1995       UI_block_lock_clear(block);
1996       UI_block_lock_set(block, ob && ID_IS_LINKED(ob), ERROR_LIBDATA_MESSAGE);
1997
1998       if (!ELEM(md->type,
1999                 eModifierType_Fluidsim,
2000                 eModifierType_Softbody,
2001                 eModifierType_ParticleSystem,
2002                 eModifierType_Cloth,
2003                 eModifierType_Smoke)) {
2004         uiItemO(row,
2005                 CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Copy"),
2006                 ICON_NONE,
2007                 "OBJECT_OT_modifier_copy");
2008       }
2009     }
2010
2011     /* result is the layout block inside the box,
2012      * that we return so that modifier settings can be drawn */
2013     result = uiLayoutColumn(box, false);
2014     block = uiLayoutAbsoluteBlock(box);
2015   }
2016
2017   /* error messages */
2018   if (md->error) {
2019     box = uiLayoutBox(column);
2020     row = uiLayoutRow(box, false);
2021     uiItemL(row, md->error, ICON_ERROR);
2022   }
2023
2024   return result;
2025 }
2026
2027 uiLayout *uiTemplateModifier(uiLayout *layout, bContext *C, PointerRNA *ptr)
2028 {
2029   Scene *scene = CTX_data_scene(C);
2030   Object *ob;
2031   ModifierData *md, *vmd;
2032   VirtualModifierData virtualModifierData;
2033   int i, lastCageIndex, cageIndex;
2034
2035   /* verify we have valid data */
2036   if (!RNA_struct_is_a(ptr->type, &RNA_Modifier)) {
2037     RNA_warning("Expected modifier on object");
2038     return NULL;
2039   }
2040
2041   ob = ptr->id.data;
2042   md = ptr->data;
2043
2044   if (!ob || !(GS(ob->id.name) == ID_OB)) {
2045     RNA_warning("Expected modifier on object");
2046     return NULL;
2047   }
2048
2049   UI_block_lock_set(uiLayoutGetBlock(layout), (ob && ID_IS_LINKED(ob)), ERROR_LIBDATA_MESSAGE);
2050
2051   /* find modifier and draw it */
2052   cageIndex = modifiers_getCageIndex(scene, ob, &lastCageIndex, 0);
2053
2054   /* XXX virtual modifiers are not accessible for python */
2055   vmd = modifiers_getVirtualModifierList(ob, &virtualModifierData);
2056
2057   for (i = 0; vmd; i++, vmd = vmd->next) {
2058     if (md == vmd) {
2059       return draw_modifier(layout, scene, ob, md, i, cageIndex, lastCageIndex);
2060     }
2061     else if (vmd->mode & eModifierMode_Virtual) {
2062       i--;
2063     }
2064   }
2065
2066   return NULL;
2067 }
2068
2069 /************************ Grease Pencil Modifier Template *************************/
2070
2071 static uiLayout *gpencil_draw_modifier(uiLayout *layout, Object *ob, GpencilModifierData *md)
2072 {
2073   const GpencilModifierTypeInfo *mti = BKE_gpencil_modifierType_getInfo(md->type);
2074   PointerRNA ptr;
2075   uiBlock *block;
2076   uiLayout *box, *column, *row, *sub;
2077   uiLayout *result = NULL;
2078
2079   /* create RNA pointer */
2080   RNA_pointer_create(&ob->id, &RNA_GpencilModifier, md, &ptr);
2081
2082   column = uiLayoutColumn(layout, true);
2083   uiLayoutSetContextPointer(column, "modifier", &ptr);
2084
2085   /* rounded header ------------------------------------------------------------------- */
2086   box = uiLayoutBox(column);
2087
2088   row = uiLayoutRow(box, false);
2089   block = uiLayoutGetBlock(row);
2090
2091   UI_block_emboss_set(block, UI_EMBOSS_NONE);
2092   /* Open/Close .................................  */
2093   uiItemR(row, &ptr, "show_expanded", 0, "", ICON_NONE);
2094
2095   /* modifier-type icon */
2096   uiItemL(row, "", RNA_struct_ui_icon(ptr.type));
2097   UI_block_emboss_set(block, UI_EMBOSS);
2098
2099   /* modifier name */
2100   if (mti->isDisabled && mti->isDisabled(md, 0)) {
2101     uiLayoutSetRedAlert(row, true);
2102   }
2103   uiItemR(row, &ptr, "name", 0, "", ICON_NONE);
2104   uiLayoutSetRedAlert(row, false);
2105
2106   /* mode enabling buttons */
2107   UI_block_align_begin(block);
2108   uiItemR(row, &ptr, "show_render", 0, "", ICON_NONE);
2109   uiItemR(row, &ptr, "show_viewport", 0, "", ICON_NONE);
2110
2111   if (mti->flags & eGpencilModifierTypeFlag_SupportsEditmode) {
2112     sub = uiLayoutRow(row, true);
2113     uiLayoutSetActive(sub, false);
2114     uiItemR(sub, &ptr, "show_in_editmode", 0, "", ICON_NONE);
2115   }
2116
2117   UI_block_align_end(block);
2118
2119   /* Up/Down + Delete ........................... */
2120   UI_block_align_begin(block);
2121   uiItemO(row, "", ICON_TRIA_UP, "OBJECT_OT_gpencil_modifier_move_up");
2122   uiItemO(row, "", ICON_TRIA_DOWN, "OBJECT_OT_gpencil_modifier_move_down");
2123   UI_block_align_end(block);
2124
2125   UI_block_emboss_set(block, UI_EMBOSS_NONE);
2126   uiItemO(row, "", ICON_X, "OBJECT_OT_gpencil_modifier_remove");
2127   UI_block_emboss_set(block, UI_EMBOSS);
2128
2129   /* modifier settings (under the header) --------------------------------------------------- */
2130   if (md->mode & eGpencilModifierMode_Expanded) {
2131     /* apply/convert/copy */
2132     box = uiLayoutBox(column);
2133     row = uiLayoutRow(box, false);
2134
2135     /* only here obdata, the rest of modifiers is ob level */
2136     UI_block_lock_set(block, BKE_object_obdata_is_libdata(ob), ERROR_LIBDATA_MESSAGE);
2137
2138     uiLayoutSetOperatorContext(row, WM_OP_INVOKE_DEFAULT);
2139
2140     sub = uiLayoutRow(row, false);
2141     if (mti->flags & eGpencilModifierTypeFlag_NoApply) {
2142       uiLayoutSetEnabled(sub, false);
2143     }
2144     uiItemEnumO(sub,
2145                 "OBJECT_OT_gpencil_modifier_apply",
2146                 CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Apply"),
2147                 0,
2148                 "apply_as",
2149                 MODIFIER_APPLY_DATA);
2150     uiItemO(row,
2151             CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Copy"),
2152             ICON_NONE,
2153             "OBJECT_OT_gpencil_modifier_copy");
2154
2155     /* result is the layout block inside the box,
2156      * that we return so that modifier settings can be drawn */
2157     result = uiLayoutColumn(box, false);
2158     block = uiLayoutAbsoluteBlock(box);
2159   }
2160
2161   /* error messages */
2162   if (md->error) {
2163     box = uiLayoutBox(column);
2164     row = uiLayoutRow(box, false);
2165     uiItemL(row, md->error, ICON_ERROR);
2166   }
2167
2168   return result;
2169 }
2170
2171 uiLayout *uiTemplateGpencilModifier(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
2172 {
2173   Object *ob;
2174   GpencilModifierData *md, *vmd;
2175   int i;
2176
2177   /* verify we have valid data */
2178   if (!RNA_struct_is_a(ptr->type, &RNA_GpencilModifier)) {
2179     RNA_warning("Expected modifier on object");
2180     return NULL;
2181   }
2182
2183   ob = ptr->id.data;
2184   md = ptr->data;
2185
2186   if (!ob || !(GS(ob->id.name) == ID_OB)) {
2187     RNA_warning("Expected modifier on object");
2188     return NULL;
2189   }
2190
2191   UI_block_lock_set(uiLayoutGetBlock(layout), (ob && ID_IS_LINKED(ob)), ERROR_LIBDATA_MESSAGE);
2192
2193   /* find modifier and draw it */
2194   vmd = ob->greasepencil_modifiers.first;
2195   for (i = 0; vmd; i++, vmd = vmd->next) {
2196     if (md == vmd) {
2197       return gpencil_draw_modifier(layout, ob, md);
2198     }
2199   }
2200
2201   return NULL;
2202 }
2203
2204 /************************ Shader FX Template *************************/
2205
2206 static uiLayout *gpencil_draw_shaderfx(uiLayout *layout, Object *ob, ShaderFxData *md)
2207 {
2208   const ShaderFxTypeInfo *mti = BKE_shaderfxType_getInfo(md->type);
2209   PointerRNA ptr;
2210   uiBlock *block;
2211   uiLayout *box, *column, *row, *sub;
2212   uiLayout *result = NULL;
2213
2214   /* create RNA pointer */
2215   RNA_pointer_create(&ob->id, &RNA_ShaderFx, md, &ptr);
2216
2217   column = uiLayoutColumn(layout, true);
2218   uiLayoutSetContextPointer(column, "shaderfx", &ptr);
2219
2220   /* rounded header ------------------------------------------------------------------- */
2221   box = uiLayoutBox(column);
2222
2223   row = uiLayoutRow(box, false);
2224   block = uiLayoutGetBlock(row);
2225
2226   UI_block_emboss_set(block, UI_EMBOSS_NONE);
2227   /* Open/Close .................................  */
2228   uiItemR(row, &ptr, "show_expanded", 0, "", ICON_NONE);
2229
2230   /* shader-type icon */
2231   uiItemL(row, "", RNA_struct_ui_icon(ptr.type));
2232   UI_block_emboss_set(block, UI_EMBOSS);
2233
2234   /* effect name */
2235   if (mti->isDisabled && mti->isDisabled(md, 0)) {
2236     uiLayoutSetRedAlert(row, true);
2237   }
2238   uiItemR(row, &ptr, "name", 0, "", ICON_NONE);
2239   uiLayoutSetRedAlert(row, false);
2240
2241   /* mode enabling buttons */
2242   UI_block_align_begin(block);
2243   uiItemR(row, &ptr, "show_render", 0, "", ICON_NONE);
2244   uiItemR(row, &ptr, "show_viewport", 0, "", ICON_NONE);
2245
2246   if (mti->flags & eShaderFxTypeFlag_SupportsEditmode) {
2247     sub = uiLayoutRow(row, true);
2248     uiLayoutSetActive(sub, false);
2249     uiItemR(sub, &ptr, "show_in_editmode", 0, "", ICON_NONE);
2250   }
2251
2252   UI_block_align_end(block);
2253
2254   /* Up/Down + Delete ........................... */
2255   UI_block_align_begin(block);
2256   uiItemO(row, "", ICON_TRIA_UP, "OBJECT_OT_shaderfx_move_up");
2257   uiItemO(row, "", ICON_TRIA_DOWN, "OBJECT_OT_shaderfx_move_down");
2258   UI_block_align_end(block);
2259
2260   UI_block_emboss_set(block, UI_EMBOSS_NONE);
2261   uiItemO(row, "", ICON_X, "OBJECT_OT_shaderfx_remove");
2262   UI_block_emboss_set(block, UI_EMBOSS);
2263
2264   /* effect settings (under the header) --------------------------------------------------- */
2265   if (md->mode & eShaderFxMode_Expanded) {
2266     /* apply/convert/copy */
2267     box = uiLayoutBox(column);
2268     row = uiLayoutRow(box, false);
2269
2270     /* only here obdata, the rest of effect is ob level */
2271     UI_block_lock_set(block, BKE_object_obdata_is_libdata(ob), ERROR_LIBDATA_MESSAGE);
2272
2273     /* result is the layout block inside the box,
2274      * that we return so that effect settings can be drawn */
2275     result = uiLayoutColumn(box, false);
2276     block = uiLayoutAbsoluteBlock(box);
2277   }
2278
2279   /* error messages */
2280   if (md->error) {
2281     box = uiLayoutBox(column);
2282     row = uiLayoutRow(box, false);
2283     uiItemL(row, md->error, ICON_ERROR);
2284   }
2285
2286   return result;
2287 }
2288
2289 uiLayout *uiTemplateShaderFx(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
2290 {
2291   Object *ob;
2292   ShaderFxData *fx, *vfx;
2293   int i;
2294
2295   /* verify we have valid data */
2296   if (!RNA_struct_is_a(ptr->type, &RNA_ShaderFx)) {
2297     RNA_warning("Expected shader fx on object");
2298     return NULL;
2299   }
2300
2301   ob = ptr->id.data;
2302   fx = ptr->data;
2303
2304   if (!ob || !(GS(ob->id.name) == ID_OB)) {
2305     RNA_warning("Expected shader fx on object");
2306     return NULL;
2307   }
2308
2309   UI_block_lock_set(uiLayoutGetBlock(layout), (ob && ID_IS_LINKED(ob)), ERROR_LIBDATA_MESSAGE);
2310
2311   /* find modifier and draw it */
2312   vfx = ob->shader_fx.first;
2313   for (i = 0; vfx; i++, vfx = vfx->next) {
2314     if (fx == vfx) {
2315       return gpencil_draw_shaderfx(layout, ob, fx);
2316     }
2317   }
2318
2319   return NULL;
2320 }
2321
2322 /************************ Redo Buttons Template *************************/
2323
2324 static void template_operator_redo_property_buts_draw(
2325     const bContext *C, wmOperator *op, uiLayout *layout, int layout_flags, bool *r_has_advanced)
2326 {
2327   if (op->type->flag & OPTYPE_MACRO) {
2328     for (wmOperator *macro_op = op->macro.first; macro_op; macro_op = macro_op->next) {
2329       template_operator_redo_property_buts_draw(C, macro_op, layout, layout_flags, r_has_advanced);
2330     }
2331   }
2332   else {
2333     /* Might want to make label_align adjustable somehow. */
2334     eAutoPropButsReturn return_info = uiTemplateOperatorPropertyButs(
2335         C, layout, op, UI_BUT_LABEL_ALIGN_NONE, layout_flags);
2336     if (return_info & UI_PROP_BUTS_ANY_FAILED_CHECK) {
2337       if (r_has_advanced) {
2338         *r_has_advanced = true;
2339       }
2340     }
2341   }
2342 }
2343
2344 void uiTemplateOperatorRedoProperties(uiLayout *layout, const bContext *C)
2345 {
2346   wmOperator *op = WM_operator_last_redo(C);
2347   uiBlock *block = uiLayoutGetBlock(layout);
2348
2349   if (op == NULL) {
2350     return;
2351   }
2352
2353   /* Disable for now, doesn't fit well in popover. */
2354 #if 0
2355   /* Repeat button with operator name as text. */
2356   uiItemFullO(layout,
2357               "SCREEN_OT_repeat_last",
2358               RNA_struct_ui_name(op->type->srna),
2359               ICON_NONE,
2360               NULL,
2361               WM_OP_INVOKE_DEFAULT,
2362               0,
2363               NULL);
2364 #endif
2365
2366   if (WM_operator_repeat_check(C, op)) {
2367     int layout_flags = 0;
2368     if (block->panel == NULL) {
2369       layout_flags = UI_TEMPLATE_OP_PROPS_SHOW_TITLE;
2370     }
2371 #if 0
2372     bool has_advanced = false;
2373 #endif
2374
2375     UI_block_func_handle_set(block, ED_undo_operator_repeat_cb_evt, op);
2376     template_operator_redo_property_buts_draw(
2377         C, op, layout, layout_flags, NULL /* &has_advanced */);
2378     /* Warning! this leaves the handle function for any other users of this block. */
2379
2380 #if 0
2381     if (has_advanced) {
2382       uiItemO(layout, IFACE_("More..."), ICON_NONE, "SCREEN_OT_redo_last");
2383     }
2384 #endif
2385   }
2386 }
2387
2388 /************************ Constraint Template *************************/
2389
2390 static void constraint_active_func(bContext *UNUSED(C), void *ob_v, void *con_v)
2391 {
2392   ED_object_constraint_set_active(ob_v, con_v);
2393 }
2394
2395 /* draw panel showing settings for a constraint */
2396 static uiLayout *draw_constraint(uiLayout *layout, Object *ob, bConstraint *con)
2397 {
2398   bPoseChannel *pchan = BKE_pose_channel_active(ob);
2399   const bConstraintTypeInfo *cti;
2400   uiBlock *block;
2401   uiLayout *result = NULL, *col, *box, *row;
2402   PointerRNA ptr;
2403   char typestr[32];
2404   short proxy_protected, xco = 0, yco = 0;
2405   // int rb_col; // UNUSED
2406
2407   /* get constraint typeinfo */
2408   cti = BKE_constraint_typeinfo_get(con);
2409   if (cti == NULL) {
2410     /* exception for 'Null' constraint - it doesn't have constraint typeinfo! */
2411     BLI_strncpy(typestr,
2412                 (con->type == CONSTRAINT_TYPE_NULL) ? IFACE_("Null") : IFACE_("Unknown"),
2413                 sizeof(typestr));
2414   }
2415   else {
2416     BLI_strncpy(typestr, IFACE_(cti->name), sizeof(typestr));
2417   }
2418
2419   /* determine whether constraint is proxy protected or not */
2420   if (BKE_constraints_proxylocked_owner(ob, pchan)) {
2421     proxy_protected = (con->flag & CONSTRAINT_PROXY_LOCAL) == 0;
2422   }
2423   else {
2424     proxy_protected = 0;
2425   }
2426
2427   /* unless button has own callback, it adds this callback to button */
2428   block = uiLayoutGetBlock(layout);
2429   UI_block_func_set(block, constraint_active_func, ob, con);
2430
2431   RNA_pointer_create(&ob->id, &RNA_Constraint, con, &ptr);
2432
2433   col = uiLayoutColumn(layout, true);
2434   uiLayoutSetContextPointer(col, "constraint", &ptr);
2435
2436   box = uiLayoutBox(col);
2437   row = uiLayoutRow(box, false);
2438   block = uiLayoutGetBlock(box);
2439
2440   /* Draw constraint header */
2441
2442   /* open/close */
2443   UI_block_emboss_set(block, UI_EMBOSS_NONE);
2444   uiItemR(row, &ptr, "show_expanded", UI_ITEM_R_ICON_ONLY, "", ICON_NONE);
2445   UI_block_emboss_set(block, UI_EMBOSS);
2446
2447   /* name */
2448   uiDefBut(block,
2449            UI_BTYPE_LABEL,
2450            0,
2451            typestr,
2452            xco + 0.5f * UI_UNIT_X,
2453            yco,
2454            5 * UI_UNIT_X,
2455            0.9f * UI_UNIT_Y,
2456            NULL,
2457            0.0,
2458            0.0,
2459            0.0,
2460            0.0,
2461            "");
2462
2463   if (con->flag & CONSTRAINT_DISABLE) {
2464     uiLayoutSetRedAlert(row, true);
2465   }
2466
2467   if (proxy_protected == 0) {
2468     uiItemR(row, &ptr, "name", 0, "", ICON_NONE);
2469   }
2470   else {
2471     uiItemL(row, con->name, ICON_NONE);
2472   }
2473
2474   uiLayoutSetRedAlert(row, false);
2475
2476   /* proxy-protected constraints cannot be edited, so hide up/down + close buttons */
2477   if (proxy_protected) {
2478     UI_block_emboss_set(block, UI_EMBOSS_NONE);
2479
2480     /* draw a ghost icon (for proxy) and also a lock beside it,
2481      * to show that constraint is "proxy locked" */
2482     uiDefIconBut(block,
2483                  UI_BTYPE_BUT,
2484                  0,
2485                  ICON_GHOST_ENABLED,
2486                  xco + 12.2f * UI_UNIT_X,
2487                  yco,
2488                  0.95f * UI_UNIT_X,
2489                  0.95f * UI_UNIT_Y,
2490                  NULL,
2491                  0.0,
2492                  0.0,
2493                  0.0,
2494                  0.0,
2495                  TIP_("Proxy Protected"));
2496     uiDefIconBut(block,
2497                  UI_BTYPE_BUT,
2498                  0,
2499                  ICON_LOCKED,
2500                  xco + 13.1f * UI_UNIT_X,
2501                  yco,
2502                  0.95f * UI_UNIT_X,
2503                  0.95f * UI_UNIT_Y,
2504                  NULL,
2505                  0.0,
2506                  0.0,
2507                  0.0,
2508                  0.0,
2509                  TIP_("Proxy Protected"));
2510
2511     UI_block_emboss_set(block, UI_EMBOSS);
2512   }
2513   else {
2514     short prev_proxylock, show_upbut, show_downbut;
2515
2516     /* Up/Down buttons:
2517      * Proxy-constraints are not allowed to occur after local (non-proxy) constraints
2518      * as that poses problems when restoring them, so disable the "up" button where
2519      * it may cause this situation.
2520      *
2521      * Up/Down buttons should only be shown (or not grayed - todo) if they serve some purpose.
2522      */
2523     if (BKE_constraints_proxylocked_owner(ob, pchan)) {
2524       if (con->prev) {
2525         prev_proxylock = (con->prev->flag & CONSTRAINT_PROXY_LOCAL) ? 0 : 1;
2526       }
2527       else {
2528         prev_proxylock = 0;
2529       }
2530     }
2531     else {
2532       prev_proxylock = 0;
2533     }
2534
2535     show_upbut = ((prev_proxylock == 0) && (con->prev));
2536     show_downbut = (con->next) ? 1 : 0;
2537
2538     /* enabled */
2539     UI_block_emboss_set(block, UI_EMBOSS_NONE);
2540     uiItemR(row, &ptr, "mute", 0, "", (con->flag & CONSTRAINT_OFF) ? ICON_HIDE_ON : ICON_HIDE_OFF);
2541     UI_block_emboss_set(block, UI_EMBOSS);
2542
2543     uiLayoutSetOperatorContext(row, WM_OP_INVOKE_DEFAULT);
2544
2545     /* up/down */
2546     if (show_upbut || show_downbut) {
2547       UI_block_align_begin(block);
2548       if (show_upbut) {
2549         uiItemO(row, "", ICON_TRIA_UP, "CONSTRAINT_OT_move_up");
2550       }
2551
2552       if (show_downbut) {
2553         uiItemO(row, "", ICON_TRIA_DOWN, "CONSTRAINT_OT_move_down");
2554       }
2555       UI_block_align_end(block);
2556     }
2557
2558     /* Close 'button' - emboss calls here disable drawing of 'button' behind X */
2559     UI_block_emboss_set(block, UI_EMBOSS_NONE);
2560     uiItemO(row, "", ICON_X, "CONSTRAINT_OT_delete");
2561     UI_block_emboss_set(block, UI_EMBOSS);
2562   }
2563
2564   /* Set but-locks for protected settings (magic numbers are used here!) */
2565   if (proxy_protected) {
2566     UI_block_lock_set(block, true, IFACE_("Cannot edit Proxy-Protected Constraint"));
2567   }
2568
2569   /* Draw constraint data */
2570   if ((con->flag & CONSTRAINT_EXPAND) == 0) {
2571     (yco) -= 10.5f * UI_UNIT_Y;
2572   }
2573   else {
2574     box = uiLayoutBox(col);
2575     block = uiLayoutAbsoluteBlock(box);
2576     result = box;
2577   }
2578
2579   /* clear any locks set up for proxies/lib-linking */
2580   UI_block_lock_clear(block);
2581
2582   return result;
2583 }
2584
2585 uiLayout *uiTemplateConstraint(uiLayout *layout, PointerRNA *ptr)
2586 {
2587   Object *ob;
2588   bConstraint *con;
2589
2590   /* verify we have valid data */
2591   if (!RNA_struct_is_a(ptr->type, &RNA_Constraint)) {
2592     RNA_warning("Expected constraint on object");
2593     return NULL;
2594   }
2595
2596   ob = ptr->id.data;
2597   con = ptr->data;
2598
2599   if (!ob || !(GS(ob->id.name) == ID_OB)) {
2600     RNA_warning("Expected constraint on object");
2601     return NULL;
2602   }
2603
2604   UI_block_lock_set(uiLayoutGetBlock(layout), (ob && ID_IS_LINKED(ob)), ERROR_LIBDATA_MESSAGE);
2605
2606   /* hrms, the temporal constraint should not draw! */
2607   if (con->type == CONSTRAINT_TYPE_KINEMATIC) {
2608     bKinematicConstraint *data = con->data;
2609     if (data->flag & CONSTRAINT_IK_TEMP) {
2610       return NULL;
2611     }
2612   }
2613
2614   return draw_constraint(layout, ob, con);
2615 }
2616
2617 /************************* Preview Template ***************************/
2618
2619 #include "DNA_light_types.h"
2620 #include "DNA_material_types.h"
2621 #include "DNA_world_types.h"
2622
2623 #define B_MATPRV 1
2624
2625 static void do_preview_buttons(bContext *C, void *arg, int event)
2626 {
2627   switch (event) {
2628     case B_MATPRV:
2629       WM_event_add_notifier(C, NC_MATERIAL | ND_SHADING_PREVIEW, arg);
2630       break;
2631   }
2632 }
2633
2634 void uiTemplatePreview(uiLayout *layout,
2635                        bContext *C,
2636                        ID *id,
2637                        bool show_buttons,
2638                        ID *parent,
2639                        MTex *slot,
2640                        const char *preview_id)
2641 {
2642   uiLayout *row, *col;
2643   uiBlock *block;
2644   uiPreview *ui_preview = NULL;
2645   ARegion *ar;
2646
2647   Material *ma = NULL;
2648   Tex *tex = (Tex *)id;
2649   ID *pid, *pparent;
2650   short *pr_texture = NULL;
2651   PointerRNA material_ptr;
2652   PointerRNA texture_ptr;
2653
2654   char _preview_id[UI_MAX_NAME_STR];
2655
2656   if (id && !ELEM(GS(id->name), ID_MA, ID_TE, ID_WO, ID_LA, ID_LS)) {
2657     RNA_warning("Expected ID of type material, texture, light, world or line style");
2658     return;
2659   }
2660
2661   /* decide what to render */
2662   pid = id;
2663   pparent = NULL;
2664
2665   if (id && (GS(id->name) == ID_TE)) {
2666     if (parent && (GS(parent->name) == ID_MA)) {
2667       pr_texture = &((Material *)parent)->pr_texture;
2668     }
2669     else if (parent && (GS(parent->name) == ID_WO)) {
2670       pr_texture = &((World *)parent)->pr_texture;
2671     }
2672     else if (parent && (GS(parent->name) == ID_LA)) {
2673       pr_texture = &((Light *)parent)->pr_texture;
2674     }
2675     else if (parent && (GS(parent->name) == ID_LS)) {
2676       pr_texture = &((FreestyleLineStyle *)parent)->pr_texture;
2677     }
2678
2679     if (pr_texture) {
2680       if (*pr_texture == TEX_PR_OTHER) {
2681         pid = parent;
2682       }
2683       else if (*pr_texture == TEX_PR_BOTH) {
2684         pparent = parent;
2685       }
2686     }
2687   }
2688
2689   if (!preview_id || (preview_id[0] == '\0')) {
2690     /* If no identifier given, generate one from ID type. */
2691     BLI_snprintf(_preview_id, UI_MAX_NAME_STR, "uiPreview_%s", BKE_idcode_to_name(GS(id->name)));
2692     preview_id = _preview_id;
2693   }
2694
2695   /* Find or add the uiPreview to the current Region. */
2696   ar = CTX_wm_region(C);
2697   ui_preview = BLI_findstring(&ar->ui_previews, preview_id, offsetof(uiPreview, preview_id));
2698
2699   if (!ui_preview) {
2700     ui_preview = MEM_callocN(sizeof(uiPreview), "uiPreview");
2701     BLI_strncpy(ui_preview->preview_id, preview_id, sizeof(ui_preview->preview_id));
2702     ui_preview->height = (short)(UI_UNIT_Y * 7.6f);
2703     BLI_addtail(&ar->ui_previews, ui_preview);
2704   }
2705
2706   if (ui_preview->height < UI_UNIT_Y) {
2707     ui_preview->height = UI_UNIT_Y;
2708   }
2709   else if (ui_preview->height > UI_UNIT_Y * 50) { /* Rather high upper limit, yet not insane! */
2710     ui_preview->height = UI_UNIT_Y * 50;
2711   }
2712
2713   /* layout */
2714   block = uiLayoutGetBlock(layout);
2715   row = uiLayoutRow(layout, false);
2716   col = uiLayoutColumn(row, false);
2717   uiLayoutSetKeepAspect(col, true);
2718
2719   /* add preview */
2720   uiDefBut(block,
2721            UI_BTYPE_EXTRA,
2722            0,
2723            "",
2724            0,
2725            0,
2726            UI_UNIT_X * 10,
2727            ui_preview->height,
2728            pid,
2729            0.0,
2730            0.0,
2731            0,
2732            0,
2733            "");
2734   UI_but_func_drawextra_set(block, ED_preview_draw, pparent, slot);
2735   UI_block_func_handle_set(block, do_preview_buttons, NULL);
2736
2737   uiDefIconButS(block,
2738                 UI_BTYPE_GRIP,
2739                 0,
2740                 ICON_GRIP,
2741                 0,
2742                 0,
2743                 UI_UNIT_X * 10,
2744                 (short)(UI_UNIT_Y * 0.3f),
2745                 &ui_preview->height,
2746                 UI_UNIT_Y,
2747                 UI_UNIT_Y * 50.0f,
2748                 0.0f,
2749                 0.0f,
2750                 "");
2751
2752   /* add buttons */
2753   if (pid && show_buttons) {
2754     if (GS(pid->name) == ID_MA || (pparent && GS(pparent->name) == ID_MA)) {
2755       if (GS(pid->name) == ID_MA) {
2756         ma = (Material *)pid;
2757       }
2758       else {
2759         ma = (Material *)pparent;
2760       }
2761
2762       /* Create RNA Pointer */
2763       RNA_pointer_create(&ma->id, &RNA_Material, ma, &material_ptr);
2764
2765       col = uiLayoutColumn(row, true);
2766       uiLayoutSetScaleX(col, 1.5);
2767       uiItemR(col, &material_ptr, "preview_render_type", UI_ITEM_R_EXPAND, "", ICON_NONE);
2768       uiItemS(col);
2769       uiItemR(col, &material_ptr, "use_preview_world", 0, "", ICON_WORLD);
2770     }
2771
2772     if (pr_texture) {
2773       /* Create RNA Pointer */
2774       RNA_pointer_create(id, &RNA_Texture, tex, &texture_ptr);
2775
2776       uiLayoutRow(layout, true);
2777       uiDefButS(block,
2778                 UI_BTYPE_ROW,
2779                 B_MATPRV,
2780                 IFACE_("Texture"),
2781                 0,
2782                 0,
2783                 UI_UNIT_X * 10,
2784                 UI_UNIT_Y,
2785                 pr_texture,
2786                 10,
2787                 TEX_PR_TEXTURE,
2788                 0,
2789                 0,
2790                 "");
2791       if (GS(parent->name) == ID_MA) {
2792         uiDefButS(block,
2793                   UI_BTYPE_ROW,
2794                   B_MATPRV,
2795                   IFACE_("Material"),
2796                   0,
2797                   0,
2798                   UI_UNIT_X * 10,
2799                   UI_UNIT_Y,
2800                   pr_texture,
2801                   10,
2802                   TEX_PR_OTHER,
2803                   0,
2804                   0,
2805                   "");
2806       }
2807       else if (GS(parent->name) == ID_LA) {
2808         uiDefButS(block,
2809                   UI_BTYPE_ROW,
2810                   B_MATPRV,
2811                   CTX_IFACE_(BLT_I18NCONTEXT_ID_LIGHT, "Light"),
2812                   0,
2813                   0,
2814                   UI_UNIT_X * 10,
2815                   UI_UNIT_Y,
2816                   pr_texture,
2817                   10,
2818                   TEX_PR_OTHER,
2819                   0,
2820                   0,
2821                   "");
2822       }
2823       else if (GS(parent->name) == ID_WO) {
2824         uiDefButS(block,
2825                   UI_BTYPE_ROW,
2826                   B_MATPRV,
2827                   IFACE_("World"),
2828                   0,
2829                   0,
2830                   UI_UNIT_X * 10,
2831                   UI_UNIT_Y,
2832                   pr_texture,
2833                   10,
2834                   TEX_PR_OTHER,
2835                   0,
2836                   0,
2837                   "");
2838       }
2839       else if (GS(parent->name) == ID_LS) {
2840         uiDefButS(block,
2841                   UI_BTYPE_ROW,
2842                   B_MATPRV,
2843                   IFACE_("Line Style"),
2844                   0,
2845                   0,
2846                   UI_UNIT_X * 10,
2847                   UI_UNIT_Y,
2848                   pr_texture,
2849                   10,
2850                   TEX_PR_OTHER,
2851                   0,
2852                   0,
2853                   "");
2854       }
2855       uiDefButS(block,
2856                 UI_BTYPE_ROW,
2857                 B_MATPRV,
2858                 IFACE_("Both"),
2859                 0,
2860                 0,
2861                 UI_UNIT_X * 10,
2862                 UI_UNIT_Y,
2863                 pr_texture,
2864                 10,
2865                 TEX_PR_BOTH,
2866                 0,
2867                 0,
2868                 "");
2869
2870       /* Alpha button for texture preview */
2871       if (*pr_texture != TEX_PR_OTHER) {
2872         row = uiLayoutRow(layout, false);
2873         uiItemR(row, &texture_ptr, "use_preview_alpha", 0, NULL, ICON_NONE);
2874       }
2875     }
2876   }
2877 }
2878
2879 /********************** ColorRamp Template **************************/
2880
2881 typedef struct RNAUpdateCb {
2882   PointerRNA ptr;
2883   PropertyRNA *prop;
2884 } RNAUpdateCb;
2885
2886 static void rna_update_cb(bContext *C, void *arg_cb, void *UNUSED(arg))
2887 {
2888   RNAUpdateCb *cb = (RNAUpdateCb *)arg_cb;
2889
2890   /* we call update here on the pointer property, this way the
2891    * owner of the curve mapping can still define it's own update
2892    * and notifier, even if the CurveMapping struct is shared. */
2893   RNA_property_update(C, &cb->ptr, cb->prop);
2894 }
2895
2896 enum {
2897   CB_FUNC_FLIP,
2898   CB_FUNC_DISTRIBUTE_LR,
2899   CB_FUNC_DISTRIBUTE_EVENLY,
2900   CB_FUNC_RESET,
2901 };
2902
2903 static void colorband_flip_cb(bContext *C, ColorBand *coba)
2904 {
2905   CBData data_tmp[MAXCOLORBAND];
2906
2907   int a;
2908
2909   for (a = 0; a < coba->tot; a++) {
2910     data_tmp[a] = coba->data[coba->tot - (a + 1)];
2911   }
2912   for (a = 0; a < coba->tot; a++) {
2913     data_tmp[a].pos = 1.0f - data_tmp[a].pos;
2914     coba->data[a] = data_tmp[a];
2915   }
2916
2917   /* may as well flip the cur*/
2918   coba->cur = coba->tot - (coba->cur + 1);
2919
2920   ED_undo_push(C, "Flip Color Ramp");
2921 }
2922
2923 static void colorband_distribute_cb(bContext *C, ColorBand *coba, bool evenly)
2924 {
2925   if (coba->tot > 1) {
2926     int a;
2927     int tot = evenly ? coba->tot - 1 : coba->tot;
2928     float gap = 1.0f / tot;
2929     float pos = 0.0f;
2930     for (a = 0; a < coba->tot; a++) {
2931       coba->data[a].pos = pos;
2932       pos += gap;
2933     }
2934     ED_undo_push(C, evenly ? "Distribute Stops Evenly" : "Distribute Stops from Left");
2935   }
2936 }
2937
2938 static void colorband_tools_dofunc(bContext *C, void *coba_v, int event)
2939 {
2940   ColorBand *coba = coba_v;
2941
2942   switch (event) {
2943     case CB_FUNC_FLIP:
2944       colorband_flip_cb(C, coba);
2945       break;
2946     case CB_FUNC_DISTRIBUTE_LR:
2947       colorband_distribute_cb(C, coba, false);
2948       break;
2949     case CB_FUNC_DISTRIBUTE_EVENLY:
2950       colorband_distribute_cb(C, coba, true);
2951       break;
2952     case CB_FUNC_RESET:
2953       BKE_colorband_init(coba, true);
2954       ED_undo_push(C, "Reset Color Ramp");
2955       break;
2956   }
2957   ED_region_tag_redraw(CTX_wm_region(C));
2958 }
2959
2960 static uiBlock *colorband_tools_func(bContext *C, ARegion *ar, void *coba_v)
2961 {
2962   uiStyle *style = UI_style_get_dpi();
2963   ColorBand *coba = coba_v;
2964   uiBlock *block;
2965   short yco = 0, menuwidth = 10 * UI_UNIT_X;
2966
2967   block = UI_block_begin(C, ar, __func__, UI_EMBOSS_PULLDOWN);
2968   UI_block_func_butmenu_set(block, colorband_tools_dofunc, coba);
2969
2970   uiLayout *layout = UI_block_layout(block,
2971                                      UI_LAYOUT_VERTICAL,
2972                                      UI_LAYOUT_MENU,
2973                                      0,
2974                                      0,
2975                                      UI_MENU_WIDTH_MIN,
2976                                      0,
2977                                      UI_MENU_PADDING,
2978                                      style);
2979   UI_block_layout_set_current(block, layout);
2980   {
2981     PointerRNA coba_ptr;
2982     RNA_pointer_create(NULL, &RNA_ColorRamp, coba, &coba_ptr);
2983     uiLayoutSetContextPointer(layout, "color_ramp", &coba_ptr);
2984   }
2985
2986   /* We could move these to operators,
2987    * although this isn't important unless we want to assign key shortcuts to them. */
2988   {
2989     uiDefIconTextBut(block,
2990                      UI_BTYPE_BUT_MENU,
2991                      1,
2992                      ICON_BLANK1,
2993                      IFACE_("Flip Color Ramp"),
2994                      0,
2995                      yco -= UI_UNIT_Y,
2996                      menuwidth,
2997                      UI_UNIT_Y,
2998                      NULL,
2999                      0.0,
3000                      0.0,
3001                      0,
3002                      CB_FUNC_FLIP,
3003                      "");
3004     uiDefIconTextBut(block,
3005                      UI_BTYPE_BUT_MENU,
3006                      1,
3007                      ICON_BLANK1,
3008                      IFACE_("Distribute Stops from Left"),
3009                      0,
3010                      yco -= UI_UNIT_Y,
3011                      menuwidth,
3012                      UI_UNIT_Y,
3013                      NULL,
3014                      0.0,
3015                      0.0,
3016                      0,
3017                      CB_FUNC_DISTRIBUTE_LR,
3018                      "");
3019     uiDefIconTextBut(block,
3020                      UI_BTYPE_BUT_MENU,
3021                      1,
3022                      ICON_BLANK1,
3023                      IFACE_("Distribute Stops Evenly"),
3024                      0,
3025                      yco -= UI_UNIT_Y,
3026                      menuwidth,
3027                      UI_UNIT_Y,
3028                      NULL,
3029                      0.0,
3030                      0.0,
3031                      0,
3032                      CB_FUNC_DISTRIBUTE_EVENLY,
3033                      "");
3034
3035     uiItemO(layout, IFACE_("Eyedropper"), ICON_EYEDROPPER, "UI_OT_eyedropper_colorramp");
3036
3037     uiDefIconTextBut(block,
3038                      UI_BTYPE_BUT_MENU,
3039                      1,
3040                      ICON_BLANK1,
3041                      IFACE_("Reset Color Ramp"),
3042                      0,
3043                      yco -= UI_UNIT_Y,
3044                      menuwidth,
3045                      UI_UNIT_Y,
3046                      NULL,
3047                      0.0,
3048                      0.0,
3049                      0,
3050                      CB_FUNC_RESET,
3051                      "");
3052   }
3053
3054   UI_block_direction_set(block, UI_DIR_DOWN);
3055   UI_block_bounds_set_text(block, 3.0f * UI_UNIT_X);
3056
3057   return block;
3058 }
3059
3060 static void colorband_add_cb(bContext *C, void *cb_v, void *coba_v)
3061 {
3062   ColorBand *coba = coba_v;
3063   float pos = 0.5f;
3064
3065   if (coba->tot > 1) {
3066     if (coba->cur > 0) {
3067       pos = (coba->data[coba->cur - 1].pos + coba->data[coba->cur].pos) * 0.5f;
3068     }
3069     else {
3070       pos = (coba->data[coba->cur + 1].pos + coba->data[coba->cur].pos) * 0.5f;
3071     }
3072   }
3073
3074   if (BKE_colorband_element_add(coba, pos)) {
3075     rna_update_cb(C, cb_v, NULL);
3076     ED_undo_push(C, "Add Color Ramp Stop");
3077   }
3078 }
3079
3080 static void colorband_del_cb(bContext *C, void *cb_v, void *coba_v)
3081 {
3082   ColorBand *coba = coba_v;
3083
3084   if (BKE_colorband_element_remove(coba, coba->cur)) {
3085     ED_undo_push(C, "Delete Color Ramp Stop");
3086     rna_update_cb(C, cb_v, NULL);
3087   }
3088 }
3089
3090 static void colorband_update_cb(bContext *UNUSED(C), void *bt_v, void *coba_v)
3091 {
3092   uiBut *bt = bt_v;
3093   ColorBand *coba = coba_v;
3094
3095   /* sneaky update here, we need to sort the colorband points to be in order,
3096    * however the RNA pointer then is wrong, so we update it */
3097   BKE_colorband_update_sort(coba);
3098   bt->rnapoin.data = coba->data + coba->cur;
3099 }
3100
3101 static void colorband_buttons_layout(uiLayout *layout,
3102                                      uiBlock *block,
3103                                      ColorBand *coba,
3104                                      const rctf *butr,
3105                                      RNAUpdateCb *cb,
3106                                      int expand)
3107 {
3108   uiLayout *row, *split, *subsplit;
3109   uiBut *bt;
3110   float unit = BLI_rctf_size_x(butr) / 14.0f;
3111   float xs = butr->xmin;
3112   float ys = butr->ymin;
3113   PointerRNA ptr;
3114
3115   RNA_pointer_create(cb->ptr.id.data, &RNA_ColorRamp, coba, &ptr);
3116
3117   split = uiLayoutSplit(layout, 0.4f, false);
3118
3119   UI_block_emboss_set(block, UI_EMBOSS_NONE);
3120   UI_block_align_begin(block);
3121   row = uiLayoutRow(split, false);
3122
3123   bt = uiDefIconTextBut(block,
3124                         UI_BTYPE_BUT,
3125                         0,
3126                         ICON_ADD,
3127                         "",
3128                         0,
3129                         0,
3130                         2.0f * unit,
3131                         UI_UNIT_Y,
3132                         NULL,
3133                         0,
3134                         0,
3135                         0,
3136                         0,
3137                         TIP_("Add a new color stop to the color ramp"));
3138   UI_but_funcN_set(bt, colorband_add_cb, MEM_dupallocN(cb), coba);
3139
3140   bt = uiDefIconTextBut(block,
3141                         UI_BTYPE_BUT,
3142                         0,
3143                         ICON_REMOVE,
3144                         "",
3145                         xs + 2.0f * unit,
3146                         ys + UI_UNIT_Y,
3147                         2.0f * unit,
3148                         UI_UNIT_Y,
3149                         NULL,
3150                         0,
3151                         0,
3152                         0,
3153                         0,
3154                         TIP_("Delete the active position"));
3155   UI_but_funcN_set(bt, colorband_del_cb, MEM_dupallocN(cb), coba);
3156
3157   bt = uiDefIconBlockBut(block,
3158                          colorband_tools_func,
3159                          coba,
3160                          0,
3161                          ICON_DOWNARROW_HLT,
3162                          xs + 4.0f * unit,
3163                          ys + UI_UNIT_Y,
3164                          2.0f * unit,
3165                          UI_UNIT_Y,
3166                          TIP_("Tools"));
3167   UI_but_funcN_set(bt, rna_update_cb, MEM_dupallocN(cb), coba);
3168
3169   UI_block_align_end(block);
3170   UI_block_emboss_set(block, UI_EMBOSS);
3171
3172   row = uiLayoutRow(split, false);
3173
3174   UI_block_align_begin(block);
3175   uiItemR(row, &ptr, "color_mode", 0, "", ICON_NONE);
3176   if (ELEM(coba->color_mode, COLBAND_BLEND_HSV, COLBAND_BLEND_HSL)) {
3177     uiItemR(row, &ptr, "hue_interpolation", 0, "", ICON_NONE);
3178   }
3179   else { /* COLBAND_BLEND_RGB */
3180     uiItemR(row, &ptr, "interpolation", 0, "", ICON_NONE);
3181   }
3182   UI_block_align_end(block);
3183
3184   row = uiLayoutRow(layout, false);
3185
3186   bt = uiDefBut(block,
3187                 UI_BTYPE_COLORBAND,
3188                 0,
3189                 "",
3190                 xs,
3191                 ys,
3192                 BLI_rctf_size_x(butr),
3193                 UI_UNIT_Y,
3194                 coba,
3195                 0,
3196                 0,
3197                 0,
3198                 0,
3199                 "");
3200   UI_but_funcN_set(bt, rna_update_cb, MEM_dupallocN(cb), NULL);
3201
3202   row = uiLayoutRow(layout, false);
3203
3204   if (coba->tot) {
3205     CBData *cbd = coba->data + coba->cur;
3206
3207     RNA_pointer_create(cb->ptr.id.data, &RNA_ColorRampElement, cbd, &ptr);
3208
3209     if (!expand) {
3210       split = uiLayoutSplit(layout, 0.3f, false);
3211
3212       row = uiLayoutRow(split, false);
3213       uiDefButS(block,
3214                 UI_BTYPE_NUM,
3215                 0,
3216                 "",
3217                 0,
3218                 0,
3219                 5.0f * UI_UNIT_X,
3220                 UI_UNIT_Y,
3221                 &coba->cur,
3222                 0.0,
3223                 (float)(MAX2(0, coba->tot - 1)),
3224                 0,
3225                 0,
3226                 TIP_("Choose active color stop"));
3227       row = uiLayoutRow(split, false);
3228       uiItemR(row, &ptr, "position", 0, IFACE_("Pos"), ICON_NONE);
3229       bt = block->buttons.last;
3230       bt->a1 = 1.0f; /* gives a bit more precision for modifying position */
3231       UI_but_func_set(bt, colorband_update_cb, bt, coba);
3232
3233       row = uiLayoutRow(layout, false);
3234       uiItemR(row, &ptr, "color", 0, "", ICON_NONE);
3235       bt = block->buttons.last;
3236       UI_but_funcN_set(bt, rna_update_cb, MEM_dupallocN(cb), NULL);
3237     }
3238     else {
3239       split = uiLayoutSplit(layout, 0.5f, false);
3240       subsplit = uiLayoutSplit(split, 0.35f, false);
3241
3242       row = uiLayoutRow(subsplit, false);
3243       uiDefButS(block,
3244                 UI_BTYPE_NUM,
3245                 0,
3246                 "",
3247                 0,
3248                 0,
3249                 5.0f * UI_UNIT_X,
3250                 UI_UNIT_Y,
3251                 &coba->cur,
3252                 0.0,
3253                 (float)(MAX2(0, coba->tot - 1)),
3254                 0,
3255                 0,
3256                 TIP_("Choose active color stop"));
3257       row = uiLayoutRow(subsplit, false);
3258       uiItemR(row, &ptr, "position", UI_ITEM_R_SLIDER, IFACE_("Pos"), ICON_NONE);
3259       bt = block->buttons.last;
3260       bt->a1 = 1.0f; /* gives a bit more precision for modifying position */
3261       UI_but_func_set(bt, colorband_update_cb, bt, coba);
3262
3263       row = uiLayoutRow(split, false);
3264       uiItemR(row, &ptr, "color", 0, "", ICON_NONE);
3265       bt = block->buttons.last;
3266       UI_but_funcN_set(bt, rna_update_cb, MEM_dupallocN(cb), NULL);
3267     }
3268   }
3269 }
3270
3271 void uiTemplateColorRamp(uiLayout *layout, PointerRNA *ptr, const char *propname, bool expand)
3272 {
3273   PropertyRNA *prop = RNA_struct_find_property(ptr, propname);
3274   PointerRNA cptr;
3275   RNAUpdateCb *cb;
3276   uiBlock *block;
3277   ID *id;
3278   rctf rect;
3279
3280   if (!prop || RNA_property_type(prop) != PROP_POINTER) {
3281     return;
3282   }
3283
3284   cptr = RNA_property_pointer_get(ptr, prop);
3285   if (!cptr.data || !RNA_struct_is_a(cptr.type, &RNA_ColorRamp)) {
3286     return;
3287   }
3288
3289   cb = MEM_callocN(sizeof(RNAUpdateCb), "RNAUpdateCb");
3290   cb->ptr = *ptr;
3291   cb->prop = prop;
3292
3293   rect.xmin = 0;
3294   rect.xmax = 10.0f * UI_UNIT_X;
3295   rect.ymin = 0;
3296   rect.ymax = 19.5f * UI_UNIT_X;
3297
3298   block = uiLayoutAbsoluteBlock(layout);
3299
3300   id = cptr.id.data;
3301   UI_block_lock_set(block, (id && ID_IS_LINKED(id)), ERROR_LIBDATA_MESSAGE);
3302
3303   colorband_buttons_layout(layout, block, cptr.data, &rect, cb, expand);
3304
3305   UI_block_lock_clear(block);
3306
3307   MEM_freeN(cb);
3308 }
3309
3310 /********************* Icon Template ************************/
3311 /**
3312  * \param icon_scale: Scale of the icon, 1x == button height.
3313  */
3314 void uiTemplateIcon(uiLayout *layout, int icon_value, float icon_scale)
3315 {
3316   uiBlock *block;
3317   uiBut *but;
3318
3319   block = uiLayoutAbsoluteBlock(layout);
3320   but = uiDefIconBut(block,
3321                      UI_BTYPE_LABEL,
3322                      0,
3323                      ICON_X,
3324                      0,
3325                      0,
3326                      UI_UNIT_X * icon_scale,
3327                      UI_UNIT_Y * icon_scale,