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