Fix/Cleanup: I18N: Bad usage of IFACE_ instead of TIP_.
[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(&template_ui->ptr, template_ui->prop, idptr, NULL);
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(&template_ui->ptr, template_ui->prop, idptr, NULL);
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(&template_ui->ptr, template_ui->prop, idptr, NULL);
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(&template_ui->ptr, template_ui->prop, idptr, NULL);
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(&coll_search->target_ptr, coll_search->target_prop, item_ptr, NULL);
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 TIP_("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               WM_operatortype_name(op->type, op->ptr),
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", 0, "", ICON_NONE);
2445
2446   /* constraint-type icon */
2447   uiItemL(row, "", RNA_struct_ui_icon(ptr.type));
2448   UI_block_emboss_set(block, UI_EMBOSS);
2449
2450   if (con->flag & CONSTRAINT_DISABLE) {
2451     uiLayoutSetRedAlert(row, true);
2452   }
2453
2454   if (proxy_protected == 0) {
2455     uiItemR(row, &ptr, "name", 0, "", ICON_NONE);
2456   }
2457   else {
2458     uiItemL(row, con->name, ICON_NONE);
2459   }
2460
2461   uiLayoutSetRedAlert(row, false);
2462
2463   /* proxy-protected constraints cannot be edited, so hide up/down + close buttons */
2464   if (proxy_protected) {
2465     UI_block_emboss_set(block, UI_EMBOSS_NONE);
2466
2467     /* draw a ghost icon (for proxy) and also a lock beside it,
2468      * to show that constraint is "proxy locked" */
2469     uiDefIconBut(block,
2470                  UI_BTYPE_BUT,
2471                  0,
2472                  ICON_GHOST_ENABLED,
2473                  xco + 12.2f * UI_UNIT_X,
2474                  yco,
2475                  0.95f * UI_UNIT_X,
2476                  0.95f * UI_UNIT_Y,
2477                  NULL,
2478                  0.0,
2479                  0.0,
2480                  0.0,
2481                  0.0,
2482                  TIP_("Proxy Protected"));
2483     uiDefIconBut(block,
2484                  UI_BTYPE_BUT,
2485                  0,
2486                  ICON_LOCKED,
2487                  xco + 13.1f * UI_UNIT_X,
2488                  yco,
2489                  0.95f * UI_UNIT_X,
2490                  0.95f * UI_UNIT_Y,
2491                  NULL,
2492                  0.0,
2493                  0.0,
2494                  0.0,
2495                  0.0,
2496                  TIP_("Proxy Protected"));
2497
2498     UI_block_emboss_set(block, UI_EMBOSS);
2499   }
2500   else {
2501     short prev_proxylock, show_upbut, show_downbut;
2502
2503     /* Up/Down buttons:
2504      * Proxy-constraints are not allowed to occur after local (non-proxy) constraints
2505      * as that poses problems when restoring them, so disable the "up" button where
2506      * it may cause this situation.
2507      *
2508      * Up/Down buttons should only be shown (or not grayed - todo) if they serve some purpose.
2509      */
2510     if (BKE_constraints_proxylocked_owner(ob, pchan)) {
2511       if (con->prev) {
2512         prev_proxylock = (con->prev->flag & CONSTRAINT_PROXY_LOCAL) ? 0 : 1;
2513       }
2514       else {
2515         prev_proxylock = 0;
2516       }
2517     }
2518     else {
2519       prev_proxylock = 0;
2520     }
2521
2522     show_upbut = ((prev_proxylock == 0) && (con->prev));
2523     show_downbut = (con->next) ? 1 : 0;
2524
2525     /* enabled */
2526     UI_block_emboss_set(block, UI_EMBOSS_NONE);
2527     uiItemR(row, &ptr, "mute", 0, "", (con->flag & CONSTRAINT_OFF) ? ICON_HIDE_ON : ICON_HIDE_OFF);
2528     UI_block_emboss_set(block, UI_EMBOSS);
2529
2530     uiLayoutSetOperatorContext(row, WM_OP_INVOKE_DEFAULT);
2531
2532     /* up/down */
2533     if (show_upbut || show_downbut) {
2534       UI_block_align_begin(block);
2535       if (show_upbut) {
2536         uiItemO(row, "", ICON_TRIA_UP, "CONSTRAINT_OT_move_up");
2537       }
2538
2539       if (show_downbut) {
2540         uiItemO(row, "", ICON_TRIA_DOWN, "CONSTRAINT_OT_move_down");
2541       }
2542       UI_block_align_end(block);
2543     }
2544
2545     /* Close 'button' - emboss calls here disable drawing of 'button' behind X */
2546     UI_block_emboss_set(block, UI_EMBOSS_NONE);
2547     uiItemO(row, "", ICON_X, "CONSTRAINT_OT_delete");
2548     UI_block_emboss_set(block, UI_EMBOSS);
2549   }
2550
2551   /* Set but-locks for protected settings (magic numbers are used here!) */
2552   if (proxy_protected) {
2553     UI_block_lock_set(block, true, TIP_("Cannot edit Proxy-Protected Constraint"));
2554   }
2555
2556   /* Draw constraint data */
2557   if ((con->flag & CONSTRAINT_EXPAND) == 0) {
2558     (yco) -= 10.5f * UI_UNIT_Y;
2559   }
2560   else {
2561     box = uiLayoutBox(col);
2562     block = uiLayoutAbsoluteBlock(box);
2563     result = box;
2564   }
2565
2566   /* clear any locks set up for proxies/lib-linking */
2567   UI_block_lock_clear(block);
2568
2569   return result;
2570 }
2571
2572 uiLayout *uiTemplateConstraint(uiLayout *layout, PointerRNA *ptr)
2573 {
2574   Object *ob;
2575   bConstraint *con;
2576
2577   /* verify we have valid data */
2578   if (!RNA_struct_is_a(ptr->type, &RNA_Constraint)) {
2579     RNA_warning("Expected constraint on object");
2580     return NULL;
2581   }
2582
2583   ob = ptr->id.data;
2584   con = ptr->data;
2585
2586   if (!ob || !(GS(ob->id.name) == ID_OB)) {
2587     RNA_warning("Expected constraint on object");
2588     return NULL;
2589   }
2590
2591   UI_block_lock_set(uiLayoutGetBlock(layout), (ob && ID_IS_LINKED(ob)), ERROR_LIBDATA_MESSAGE);
2592
2593   /* hrms, the temporal constraint should not draw! */
2594   if (con->type == CONSTRAINT_TYPE_KINEMATIC) {
2595     bKinematicConstraint *data = con->data;
2596     if (data->flag & CONSTRAINT_IK_TEMP) {
2597       return NULL;
2598     }
2599   }
2600
2601   return draw_constraint(layout, ob, con);
2602 }
2603
2604 /************************* Preview Template ***************************/
2605
2606 #include "DNA_light_types.h"
2607 #include "DNA_material_types.h"
2608 #include "DNA_world_types.h"
2609
2610 #define B_MATPRV 1
2611
2612 static void do_preview_buttons(bContext *C, void *arg, int event)
2613 {
2614   switch (event) {
2615     case B_MATPRV:
2616       WM_event_add_notifier(C, NC_MATERIAL | ND_SHADING_PREVIEW, arg);
2617       break;
2618   }
2619 }
2620
2621 void uiTemplatePreview(uiLayout *layout,
2622                        bContext *C,
2623                        ID *id,
2624                        bool show_buttons,
2625                        ID *parent,
2626                        MTex *slot,
2627                        const char *preview_id)
2628 {
2629   uiLayout *row, *col;
2630   uiBlock *block;
2631   uiPreview *ui_preview = NULL;
2632   ARegion *ar;
2633
2634   Material *ma = NULL;
2635   Tex *tex = (Tex *)id;
2636   ID *pid, *pparent;
2637   short *pr_texture = NULL;
2638   PointerRNA material_ptr;
2639   PointerRNA texture_ptr;
2640
2641   char _preview_id[UI_MAX_NAME_STR];
2642
2643   if (id && !ELEM(GS(id->name), ID_MA, ID_TE, ID_WO, ID_LA, ID_LS)) {
2644     RNA_warning("Expected ID of type material, texture, light, world or line style");
2645     return;
2646   }
2647
2648   /* decide what to render */
2649   pid = id;
2650   pparent = NULL;
2651
2652   if (id && (GS(id->name) == ID_TE)) {
2653     if (parent && (GS(parent->name) == ID_MA)) {
2654       pr_texture = &((Material *)parent)->pr_texture;
2655     }
2656     else if (parent && (GS(parent->name) == ID_WO)) {
2657       pr_texture = &((World *)parent)->pr_texture;
2658     }
2659     else if (parent && (GS(parent->name) == ID_LA)) {
2660       pr_texture = &((Light *)parent)->pr_texture;
2661     }
2662     else if (parent && (GS(parent->name) == ID_LS)) {
2663       pr_texture = &((FreestyleLineStyle *)parent)->pr_texture;
2664     }
2665
2666     if (pr_texture) {
2667       if (*pr_texture == TEX_PR_OTHER) {
2668         pid = parent;
2669       }
2670       else if (*pr_texture == TEX_PR_BOTH) {
2671         pparent = parent;
2672       }
2673     }
2674   }
2675
2676   if (!preview_id || (preview_id[0] == '\0')) {
2677     /* If no identifier given, generate one from ID type. */
2678     BLI_snprintf(_preview_id, UI_MAX_NAME_STR, "uiPreview_%s", BKE_idcode_to_name(GS(id->name)));
2679     preview_id = _preview_id;
2680   }
2681
2682   /* Find or add the uiPreview to the current Region. */
2683   ar = CTX_wm_region(C);
2684   ui_preview = BLI_findstring(&ar->ui_previews, preview_id, offsetof(uiPreview, preview_id));
2685
2686   if (!ui_preview) {
2687     ui_preview = MEM_callocN(sizeof(uiPreview), "uiPreview");
2688     BLI_strncpy(ui_preview->preview_id, preview_id, sizeof(ui_preview->preview_id));
2689     ui_preview->height = (short)(UI_UNIT_Y * 7.6f);
2690     BLI_addtail(&ar->ui_previews, ui_preview);
2691   }
2692
2693   if (ui_preview->height < UI_UNIT_Y) {
2694     ui_preview->height = UI_UNIT_Y;
2695   }
2696   else if (ui_preview->height > UI_UNIT_Y * 50) { /* Rather high upper limit, yet not insane! */
2697     ui_preview->height = UI_UNIT_Y * 50;
2698   }
2699
2700   /* layout */
2701   block = uiLayoutGetBlock(layout);
2702   row = uiLayoutRow(layout, false);
2703   col = uiLayoutColumn(row, false);
2704   uiLayoutSetKeepAspect(col, true);
2705
2706   /* add preview */
2707   uiDefBut(block,
2708            UI_BTYPE_EXTRA,
2709            0,
2710            "",
2711            0,
2712            0,
2713            UI_UNIT_X * 10,
2714            ui_preview->height,
2715            pid,
2716            0.0,
2717            0.0,
2718            0,
2719            0,
2720            "");
2721   UI_but_func_drawextra_set(block, ED_preview_draw, pparent, slot);
2722   UI_block_func_handle_set(block, do_preview_buttons, NULL);
2723
2724   uiDefIconButS(block,
2725                 UI_BTYPE_GRIP,
2726                 0,
2727                 ICON_GRIP,
2728                 0,
2729                 0,
2730                 UI_UNIT_X * 10,
2731                 (short)(UI_UNIT_Y * 0.3f),
2732                 &ui_preview->height,
2733                 UI_UNIT_Y,
2734                 UI_UNIT_Y * 50.0f,
2735                 0.0f,
2736                 0.0f,
2737                 "");
2738
2739   /* add buttons */
2740   if (pid && show_buttons) {
2741     if (GS(pid->name) == ID_MA || (pparent && GS(pparent->name) == ID_MA)) {
2742       if (GS(pid->name) == ID_MA) {
2743         ma = (Material *)pid;
2744       }
2745       else {
2746         ma = (Material *)pparent;
2747       }
2748
2749       /* Create RNA Pointer */
2750       RNA_pointer_create(&ma->id, &RNA_Material, ma, &material_ptr);
2751
2752       col = uiLayoutColumn(row, true);
2753       uiLayoutSetScaleX(col, 1.5);
2754       uiItemR(col, &material_ptr, "preview_render_type", UI_ITEM_R_EXPAND, "", ICON_NONE);
2755       uiItemS(col);
2756       uiItemR(col, &material_ptr, "use_preview_world", 0, "", ICON_WORLD);
2757     }
2758
2759     if (pr_texture) {
2760       /* Create RNA Pointer */
2761       RNA_pointer_create(id, &RNA_Texture, tex, &texture_ptr);
2762
2763       uiLayoutRow(layout, true);
2764       uiDefButS(block,
2765                 UI_BTYPE_ROW,
2766                 B_MATPRV,
2767                 IFACE_("Texture"),
2768                 0,
2769                 0,
2770                 UI_UNIT_X * 10,
2771                 UI_UNIT_Y,
2772                 pr_texture,
2773                 10,
2774                 TEX_PR_TEXTURE,
2775                 0,
2776                 0,
2777                 "");
2778       if (GS(parent->name) == ID_MA) {
2779         uiDefButS(block,
2780                   UI_BTYPE_ROW,
2781                   B_MATPRV,
2782                   IFACE_("Material"),
2783                   0,
2784                   0,
2785                   UI_UNIT_X * 10,
2786                   UI_UNIT_Y,
2787                   pr_texture,
2788                   10,
2789                   TEX_PR_OTHER,
2790                   0,
2791                   0,
2792                   "");
2793       }
2794       else if (GS(parent->name) == ID_LA) {
2795         uiDefButS(block,
2796                   UI_BTYPE_ROW,
2797                   B_MATPRV,
2798                   CTX_IFACE_(BLT_I18NCONTEXT_ID_LIGHT, "Light"),
2799                   0,
2800                   0,
2801                   UI_UNIT_X * 10,
2802                   UI_UNIT_Y,
2803                   pr_texture,
2804                   10,
2805                   TEX_PR_OTHER,
2806                   0,
2807                   0,
2808                   "");
2809       }
2810       else if (GS(parent->name) == ID_WO) {
2811         uiDefButS(block,
2812                   UI_BTYPE_ROW,
2813                   B_MATPRV,
2814                   IFACE_("World"),
2815                   0,
2816                   0,
2817                   UI_UNIT_X * 10,
2818                   UI_UNIT_Y,
2819                   pr_texture,
2820                   10,
2821                   TEX_PR_OTHER,
2822                   0,
2823                   0,
2824                   "");
2825       }
2826       else if (GS(parent->name) == ID_LS) {
2827         uiDefButS(block,
2828                   UI_BTYPE_ROW,
2829                   B_MATPRV,
2830                   IFACE_("Line Style"),
2831                   0,
2832                   0,
2833                   UI_UNIT_X * 10,
2834                   UI_UNIT_Y,
2835                   pr_texture,
2836                   10,
2837                   TEX_PR_OTHER,
2838                   0,
2839                   0,
2840                   "");
2841       }
2842       uiDefButS(block,
2843                 UI_BTYPE_ROW,
2844                 B_MATPRV,
2845                 IFACE_("Both"),
2846                 0,
2847                 0,
2848                 UI_UNIT_X * 10,
2849                 UI_UNIT_Y,
2850                 pr_texture,
2851                 10,
2852                 TEX_PR_BOTH,
2853                 0,
2854                 0,
2855                 "");
2856
2857       /* Alpha button for texture preview */
2858       if (*pr_texture != TEX_PR_OTHER) {
2859         row = uiLayoutRow(layout, false);
2860         uiItemR(row, &texture_ptr, "use_preview_alpha", 0, NULL, ICON_NONE);
2861       }
2862     }
2863   }
2864 }
2865
2866 /********************** ColorRamp Template **************************/
2867
2868 typedef struct RNAUpdateCb {
2869   PointerRNA ptr;
2870   PropertyRNA *prop;
2871 } RNAUpdateCb;
2872
2873 static void rna_update_cb(bContext *C, void *arg_cb, void *UNUSED(arg))
2874 {
2875   RNAUpdateCb *cb = (RNAUpdateCb *)arg_cb;
2876
2877   /* we call update here on the pointer property, this way the
2878    * owner of the curve mapping can still define it's own update
2879    * and notifier, even if the CurveMapping struct is shared. */
2880   RNA_property_update(C, &cb->ptr, cb->prop);
2881 }
2882
2883 enum {
2884   CB_FUNC_FLIP,
2885   CB_FUNC_DISTRIBUTE_LR,
2886   CB_FUNC_DISTRIBUTE_EVENLY,
2887   CB_FUNC_RESET,
2888 };
2889
2890 static void colorband_flip_cb(bContext *C, ColorBand *coba)
2891 {
2892   CBData data_tmp[MAXCOLORBAND];
2893
2894   int a;
2895
2896   for (a = 0; a < coba->tot; a++) {
2897     data_tmp[a] = coba->data[coba->tot - (a + 1)];
2898   }
2899   for (a = 0; a < coba->tot; a++) {
2900     data_tmp[a].pos = 1.0f - data_tmp[a].pos;
2901     coba->data[a] = data_tmp[a];
2902   }
2903
2904   /* may as well flip the cur*/
2905   coba->cur = coba->tot - (coba->cur + 1);
2906
2907   ED_undo_push(C, "Flip Color Ramp");
2908 }
2909
2910 static void colorband_distribute_cb(bContext *C, ColorBand *coba, bool evenly)
2911 {
2912   if (coba->tot > 1) {
2913     int a;
2914     int tot = evenly ? coba->tot - 1 : coba->tot;
2915     float gap = 1.0f / tot;
2916     float pos = 0.0f;
2917     for (a = 0; a < coba->tot; a++) {
2918       coba->data[a].pos = pos;
2919       pos += gap;
2920     }
2921     ED_undo_push(C, evenly ? "Distribute Stops Evenly" : "Distribute Stops from Left");
2922   }
2923 }
2924
2925 static void colorband_tools_dofunc(bContext *C, void *coba_v, int event)
2926 {
2927   ColorBand *coba = coba_v;
2928
2929   switch (event) {
2930     case CB_FUNC_FLIP:
2931       colorband_flip_cb(C, coba);
2932       break;
2933     case CB_FUNC_DISTRIBUTE_LR:
2934       colorband_distribute_cb(C, coba, false);
2935       break;
2936     case CB_FUNC_DISTRIBUTE_EVENLY:
2937       colorband_distribute_cb(C, coba, true);
2938       break;
2939     case CB_FUNC_RESET:
2940       BKE_colorband_init(coba, true);
2941       ED_undo_push(C, "Reset Color Ramp");
2942       break;
2943   }
2944   ED_region_tag_redraw(CTX_wm_region(C));
2945 }
2946
2947 static uiBlock *colorband_tools_func(bContext *C, ARegion *ar, void *coba_v)
2948 {
2949   uiStyle *style = UI_style_get_dpi();
2950   ColorBand *coba = coba_v;
2951   uiBlock *block;
2952   short yco = 0, menuwidth = 10 * UI_UNIT_X;
2953
2954   block = UI_block_begin(C, ar, __func__, UI_EMBOSS_PULLDOWN);
2955   UI_block_func_butmenu_set(block, colorband_tools_dofunc, coba);
2956
2957   uiLayout *layout = UI_block_layout(block,
2958                                      UI_LAYOUT_VERTICAL,
2959                                      UI_LAYOUT_MENU,
2960                                      0,
2961                                      0,
2962                                      UI_MENU_WIDTH_MIN,
2963                                      0,
2964                                      UI_MENU_PADDING,
2965                                      style);
2966   UI_block_layout_set_current(block, layout);
2967   {
2968     PointerRNA coba_ptr;
2969     RNA_pointer_create(NULL, &RNA_ColorRamp, coba, &coba_ptr);
2970     uiLayoutSetContextPointer(layout, "color_ramp", &coba_ptr);
2971   }
2972
2973   /* We could move these to operators,
2974    * although this isn't important unless we want to assign key shortcuts to them. */
2975   {
2976     uiDefIconTextBut(block,
2977                      UI_BTYPE_BUT_MENU,
2978                      1,
2979                      ICON_BLANK1,
2980                      IFACE_("Flip Color Ramp"),
2981                      0,
2982                      yco -= UI_UNIT_Y,
2983                      menuwidth,
2984                      UI_UNIT_Y,
2985                      NULL,
2986                      0.0,
2987                      0.0,
2988                      0,
2989                      CB_FUNC_FLIP,
2990                      "");
2991     uiDefIconTextBut(block,
2992                      UI_BTYPE_BUT_MENU,
2993                      1,
2994                      ICON_BLANK1,
2995                      IFACE_("Distribute Stops from Left"),
2996                      0,
2997                      yco -= UI_UNIT_Y,
2998                      menuwidth,
2999                      UI_UNIT_Y,
3000                      NULL,
3001                      0.0,
3002                      0.0,
3003                      0,
3004                      CB_FUNC_DISTRIBUTE_LR,
3005                      "");
3006     uiDefIconTextBut(block,
3007                      UI_BTYPE_BUT_MENU,
3008                      1,
3009                      ICON_BLANK1,
3010                      IFACE_("Distribute Stops Evenly"),
3011                      0,
3012                      yco -= UI_UNIT_Y,
3013                      menuwidth,
3014                      UI_UNIT_Y,
3015                      NULL,
3016                      0.0,
3017                      0.0,
3018                      0,
3019                      CB_FUNC_DISTRIBUTE_EVENLY,
3020                      "");
3021
3022     uiItemO(layout, IFACE_("Eyedropper"), ICON_EYEDROPPER, "UI_OT_eyedropper_colorramp");
3023
3024     uiDefIconTextBut(block,
3025                      UI_BTYPE_BUT_MENU,
3026                      1,
3027                      ICON_BLANK1,
3028                      IFACE_("Reset Color Ramp"),
3029                      0,
3030                      yco -= UI_UNIT_Y,
3031                      menuwidth,
3032                      UI_UNIT_Y,
3033                      NULL,
3034                      0.0,
3035                      0.0,
3036                      0,
3037                      CB_FUNC_RESET,
3038                      "");
3039   }
3040
3041   UI_block_direction_set(block, UI_DIR_DOWN);
3042   UI_block_bounds_set_text(block, 3.0f * UI_UNIT_X);
3043
3044   return block;
3045 }
3046
3047 static void colorband_add_cb(bContext *C, void *cb_v, void *coba_v)
3048 {
3049   ColorBand *coba = coba_v;
3050   float pos = 0.5f;
3051
3052   if (coba->tot > 1) {
3053     if (coba->cur > 0) {
3054       pos = (coba->data[coba->cur - 1].pos + coba->data[coba->cur].pos) * 0.5f;
3055     }
3056     else {
3057       pos = (coba->data[coba->cur + 1].pos + coba->data[coba->cur].pos) * 0.5f;
3058     }
3059   }
3060
3061   if (BKE_colorband_element_add(coba, pos)) {
3062     rna_update_cb(C, cb_v, NULL);
3063     ED_undo_push(C, "Add Color Ramp Stop");
3064   }
3065 }
3066
3067 static void colorband_del_cb(bContext *C, void *cb_v, void *coba_v)
3068 {
3069   ColorBand *coba = coba_v;
3070
3071   if (BKE_colorband_element_remove(coba, coba->cur)) {
3072     ED_undo_push(C, "Delete Color Ramp Stop");
3073     rna_update_cb(C, cb_v, NULL);
3074   }
3075 }
3076
3077 static void colorband_update_cb(bContext *UNUSED(C), void *bt_v, void *coba_v)
3078 {
3079   uiBut *bt = bt_v;
3080   ColorBand *coba = coba_v;
3081
3082   /* sneaky update here, we need to sort the colorband points to be in order,
3083    * however the RNA pointer then is wrong, so we update it */
3084   BKE_colorband_update_sort(coba);
3085   bt->rnapoin.data = coba->data + coba->cur;
3086 }
3087
3088 static void colorband_buttons_layout(uiLayout *layout,
3089                                      uiBlock *block,
3090                                      ColorBand *coba,
3091                                      const rctf *butr,
3092                                      RNAUpdateCb *cb,
3093                                      int expand)
3094 {
3095   uiLayout *row, *split, *subsplit;
3096   uiBut *bt;
3097   float unit = BLI_rctf_size_x(butr) / 14.0f;
3098   float xs = butr->xmin;
3099   float ys = butr->ymin;
3100   PointerRNA ptr;
3101
3102   RNA_pointer_create(cb->ptr.id.data, &RNA_ColorRamp, coba, &ptr);
3103
3104   split = uiLayoutSplit(layout, 0.4f, false);
3105
3106   UI_block_emboss_set(block, UI_EMBOSS_NONE);
3107   UI_block_align_begin(block);
3108   row = uiLayoutRow(split, false);
3109
3110   bt = uiDefIconTextBut(block,
3111                         UI_BTYPE_BUT,
3112                         0,
3113                         ICON_ADD,
3114                         "",
3115                         0,
3116                         0,
3117                         2.0f * unit,
3118                         UI_UNIT_Y,
3119                         NULL,
3120                         0,
3121                         0,
3122                         0,
3123                         0,
3124                         TIP_("Add a new color stop to the color ramp"));
3125   UI_but_funcN_set(bt, colorband_add_cb, MEM_dupallocN(cb), coba);
3126
3127   bt = uiDefIconTextBut(block,
3128                         UI_BTYPE_BUT,
3129                         0,
3130                         ICON_REMOVE,
3131                         "",
3132                         xs + 2.0f * unit,
3133                         ys + UI_UNIT_Y,
3134                         2.0f * unit,
3135                         UI_UNIT_Y,
3136                         NULL,
3137                         0,
3138                         0,
3139                         0,
3140                         0,
3141                         TIP_("Delete the active position"));
3142   UI_but_funcN_set(bt, colorband_del_cb, MEM_dupallocN(cb), coba);
3143
3144   bt = uiDefIconBlockBut(block,
3145                          colorband_tools_func,
3146                          coba,
3147                          0,
3148                          ICON_DOWNARROW_HLT,
3149                          xs + 4.0f * unit,
3150                          ys + UI_UNIT_Y,
3151                          2.0f * unit,
3152                          UI_UNIT_Y,
3153                          TIP_("Tools"));
3154   UI_but_funcN_set(bt, rna_update_cb, MEM_dupallocN(cb), coba);
3155
3156   UI_block_align_end(block);
3157   UI_block_emboss_set(block, UI_EMBOSS);
3158
3159   row = uiLayoutRow(split, false);
3160
3161   UI_block_align_begin(block);
3162   uiItemR(row, &ptr, "color_mode", 0, "", ICON_NONE);
3163   if (ELEM(coba->color_mode, COLBAND_BLEND_HSV, COLBAND_BLEND_HSL)) {
3164     uiItemR(row, &ptr, "hue_interpolation", 0, "", ICON_NONE);
3165   }
3166   else { /* COLBAND_BLEND_RGB */
3167     uiItemR(row, &ptr, "interpolation", 0, "", ICON_NONE);
3168   }
3169   UI_block_align_end(block);
3170
3171   row = uiLayoutRow(layout, false);
3172
3173   bt = uiDefBut(block,
3174                 UI_BTYPE_COLORBAND,
3175                 0,
3176                 "",
3177                 xs,
3178                 ys,
3179                 BLI_rctf_size_x(butr),
3180                 UI_UNIT_Y,
3181                 coba,
3182                 0,
3183                 0,
3184                 0,
3185                 0,
3186                 "");
3187   UI_but_funcN_set(bt, rna_update_cb, MEM_dupallocN(cb), NULL);
3188
3189   row = uiLayoutRow(layout, false);
3190
3191   if (coba->tot) {
3192     CBData *cbd = coba->data + coba->cur;
3193
3194     RNA_pointer_create(cb->ptr.id.data, &RNA_ColorRampElement, cbd, &ptr);
3195
3196     if (!expand) {
3197       split = uiLayoutSplit(layout, 0.3f, false);
3198
3199       row = uiLayoutRow(split, false);
3200       uiDefButS(block,
3201                 UI_BTYPE_NUM,
3202                 0,
3203                 "",
3204                 0,
3205                 0,
3206                 5.0f * UI_UNIT_X,
3207                 UI_UNIT_Y,
3208                 &coba->cur,
3209                 0.0,
3210                 (float)(MAX2(0, coba->tot - 1)),
3211                 0,
3212                 0,
3213                 TIP_("Choose active color stop"));
3214       row = uiLayoutRow(split, false);
3215       uiItemR(row, &ptr, "position", 0, IFACE_("Pos"), ICON_NONE);
3216       bt = block->buttons.last;
3217       bt->a1 = 1.0f; /* gives a bit more precision for modifying position */
3218       UI_but_func_set(bt, colorband_update_cb, bt, coba);
3219
3220       row = uiLayoutRow(layout, false);
3221       uiItemR(row, &ptr, "color", 0, "", ICON_NONE);
3222       bt = block->buttons.last;
3223       UI_but_funcN_set(bt, rna_update_cb, MEM_dupallocN(cb), NULL);
3224     }
3225     else {
3226       split = uiLayoutSplit(layout, 0.5f, false);
3227       subsplit = uiLayoutSplit(split, 0.35f, false);
3228
3229       row = uiLayoutRow(subsplit, false);
3230       uiDefButS(block,
3231                 UI_BTYPE_NUM,
3232                 0,
3233                 "",
3234                 0,
3235                 0,
3236                 5.0f * UI_UNIT_X,
3237                 UI_UNIT_Y,
3238                 &coba->cur,
3239                 0.0,
3240                 (float)(MAX2(0, coba->tot - 1)),
3241                 0,
3242                 0,
3243                 TIP_("Choose active color stop"));
3244       row = uiLayoutRow(subsplit, false);
3245       uiItemR(row, &ptr, "position", UI_ITEM_R_SLIDER, IFACE_("Pos"), ICON_NONE);
3246       bt = block->buttons.last;
3247       bt->a1 = 1.0f; /* gives a bit more precision for modifying position */
3248       UI_but_func_set(bt, colorband_update_cb, bt, coba);
3249
3250       row = uiLayoutRow(split, false);
3251       uiItemR(row, &ptr, "color", 0, "", ICON_NONE);
3252       bt = block->buttons.last;
3253       UI_but_funcN_set(bt, rna_update_cb, MEM_dupallocN(cb), NULL);
3254     }
3255   }
3256 }
3257
3258 void uiTemplateColorRamp(uiLayout *layout, PointerRNA *ptr, const char *propname, bool expand)
3259 {
3260   PropertyRNA *prop = RNA_struct_find_property(ptr, propname);
3261   PointerRNA cptr;
3262   RNAUpdateCb *cb;
3263   uiBlock *block;
3264   ID *id;
3265   rctf rect;
3266
3267   if (!prop || RNA_property_type(prop) != PROP_POINTER) {
3268     return;
3269   }
3270
3271   cptr = RNA_property_pointer_get(ptr, prop);
3272   if (!cptr.data || !RNA_struct_is_a(cptr.type, &RNA_ColorRamp)) {
3273     return;
3274   }
3275
3276   cb = MEM_callocN(sizeof(RNAUpdateCb), "RNAUpdateCb");
3277   cb->ptr = *ptr;
3278   cb->prop = prop;
3279
3280   rect.xmin = 0;
3281   rect.xmax = 10.0f * UI_UNIT_X;
3282   rect.ymin = 0;
3283   rect.ymax = 19.5f * UI_UNIT_X;
3284
3285   block = uiLayoutAbsoluteBlock(layout);
3286
3287   id = cptr.id.data;
3288   UI_block_lock_set(block, (id && ID_IS_LINKED(id)), ERROR_LIBDATA_MESSAGE);
3289
3290   colorband_buttons_layout(layout, block, cptr.data, &rect, cb, expand);
3291
3292   UI_block_lock_clear(block);
3293
3294   MEM_freeN(cb);
3295 }
3296
3297 /********************* Icon Template ************************/
3298 /**
3299  * \param icon_scale: Scale of the icon, 1x == button height.
3300  */
3301 void uiTemplateIcon(uiLayout *layout, int icon_value, float icon_scale)
3302 {
3303   uiBlock *block;
3304   uiBut *but;
3305
3306   block = uiLayoutAbsoluteBlock(layout);
3307   but = uiDefIconBut(block,
3308                      UI_BTYPE_LABEL,
3309                      0,
3310                      ICON_X,
3311                      0,
3312                      0,
3313                      UI_UNIT_X * icon_scale,
3314                      UI_UNIT_Y * icon_scale,
3315                      NULL,
3316                      0.0,
3317                      0.0,
3318                      0.0,
3319                      0.0,
3320                      "");
3321   ui_def_but_icon(but, icon_value, UI_HAS_ICON | UI_BUT_ICON_PREVIEW);
3322 }
3323
3324 /********************* Icon viewer Template ************************/
3325 typedef struct IconViewMenuArgs {
3326   PointerRNA ptr;
3327   PropertyRNA *prop;
3328   bool show_labels;
3329   float icon_scale;
3330 } IconViewMenuArgs;
3331
3332 /* ID Search browse menu, open */
3333 static uiBlock *ui_icon_view_menu_cb(bContext *C, ARegion *ar, void *arg_litem)