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