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