Merge branch 'blender2.7'
[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 blender/editors/interface/interface_templates.c
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_LAMP,
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 (/* test only */
737                             (id_copy(CTX_data_main(C), id, NULL, true) == false) ||
738                             (idfrom && idfrom->lib) ||
739                             (!editable) ||
740                             /* object in editmode - don't change data */
741                             (idfrom && GS(idfrom->name) == ID_OB && (((Object *)idfrom)->mode & OB_MODE_EDIT)))
742                         {
743                                 UI_but_flag_enable(but, UI_BUT_DISABLED);
744                         }
745                 }
746
747                 if (user_alert) {
748                         UI_but_flag_enable(but, UI_BUT_REDALERT);
749                 }
750
751                 if (id->lib == NULL && !(ELEM(GS(id->name), ID_GR, ID_SCE, ID_SCR, ID_TXT, ID_OB, ID_WS)) &&
752                     (hide_buttons == false))
753                 {
754                         uiDefIconButR(
755                                 block, UI_BTYPE_ICON_TOGGLE, 0, ICON_FAKE_USER_OFF, 0, 0, UI_UNIT_X, UI_UNIT_Y,
756                                 &idptr, "use_fake_user", -1, 0, 0, -1, -1, NULL);
757                 }
758         }
759
760         if ((flag & UI_ID_ADD_NEW) && (hide_buttons == false)) {
761                 template_id_def_new_but(block, id, template_ui, type, newop, editable, flag & UI_ID_OPEN, false, UI_UNIT_X);
762         }
763
764         /* Due to space limit in UI - skip the "open" icon for packed data, and allow to unpack.
765          * Only for images, sound and fonts */
766         if (id && BKE_pack_check(id)) {
767                 but = uiDefIconButO(
768                         block, UI_BTYPE_BUT, "FILE_OT_unpack_item", WM_OP_INVOKE_REGION_WIN, ICON_PACKAGE, 0, 0,
769                         UI_UNIT_X, UI_UNIT_Y, TIP_("Packed File, click to unpack"));
770                 UI_but_operator_ptr_get(but);
771
772                 RNA_string_set(but->opptr, "id_name", id->name + 2);
773                 RNA_int_set(but->opptr, "id_type", GS(id->name));
774
775         }
776         else if (flag & UI_ID_OPEN) {
777                 int w = id ? UI_UNIT_X : (flag & UI_ID_ADD_NEW) ? UI_UNIT_X * 3 : UI_UNIT_X * 6;
778
779                 if (openop) {
780                         but = uiDefIconTextButO(
781                                 block, UI_BTYPE_BUT, openop, WM_OP_INVOKE_DEFAULT, ICON_FILEBROWSER, (id) ? "" : IFACE_("Open"),
782                                 0, 0, w, UI_UNIT_Y, NULL);
783                         UI_but_funcN_set(but, template_id_cb, MEM_dupallocN(template_ui), POINTER_FROM_INT(UI_ID_OPEN));
784                 }
785                 else {
786                         but = uiDefIconTextBut(
787                                 block, UI_BTYPE_BUT, 0, ICON_FILEBROWSER, (id) ? "" : IFACE_("Open"), 0, 0, w, UI_UNIT_Y,
788                                 NULL, 0, 0, 0, 0, NULL);
789                         UI_but_funcN_set(but, template_id_cb, MEM_dupallocN(template_ui), POINTER_FROM_INT(UI_ID_OPEN));
790                 }
791
792                 if ((idfrom && idfrom->lib) || !editable)
793                         UI_but_flag_enable(but, UI_BUT_DISABLED);
794         }
795
796         /* delete button */
797         /* don't use RNA_property_is_unlink here */
798         if (id && (flag & UI_ID_DELETE) && (hide_buttons == false)) {
799                 /* allow unlink if 'unlinkop' is passed, even when 'PROP_NEVER_UNLINK' is set */
800                 but = NULL;
801
802                 if (unlinkop) {
803                         but = uiDefIconButO(block, UI_BTYPE_BUT, unlinkop, WM_OP_INVOKE_DEFAULT, ICON_X, 0, 0, UI_UNIT_X, UI_UNIT_Y, NULL);
804                         /* so we can access the template from operators, font unlinking needs this */
805                         UI_but_funcN_set(but, NULL, MEM_dupallocN(template_ui), NULL);
806                 }
807                 else {
808                         if ((RNA_property_flag(template_ui->prop) & PROP_NEVER_UNLINK) == 0) {
809                                 but = uiDefIconBut(
810                                         block, UI_BTYPE_BUT, 0, ICON_X, 0, 0, UI_UNIT_X, UI_UNIT_Y, NULL, 0, 0, 0, 0,
811                                         TIP_("Unlink data-block "
812                                              "(Shift + Click to set users to zero, data will then not be saved)"));
813                                 UI_but_funcN_set(but, template_id_cb, MEM_dupallocN(template_ui), POINTER_FROM_INT(UI_ID_DELETE));
814
815                                 if (RNA_property_flag(template_ui->prop) & PROP_NEVER_NULL) {
816                                         UI_but_flag_enable(but, UI_BUT_DISABLED);
817                                 }
818                         }
819                 }
820
821                 if (but) {
822                         if ((idfrom && idfrom->lib) || !editable) {
823                                 UI_but_flag_enable(but, UI_BUT_DISABLED);
824                         }
825                 }
826         }
827
828         if (template_ui->idcode == ID_TE) {
829                 uiTemplateTextureShow(layout, C, &template_ui->ptr, template_ui->prop);
830         }
831         UI_block_align_end(block);
832 }
833
834
835 ID *UI_context_active_but_get_tab_ID(bContext *C)
836 {
837         uiBut *but = UI_context_active_but_get(C);
838
839         if (but && but->type == UI_BTYPE_TAB) {
840                 return but->custom_data;
841         }
842         else {
843                 return NULL;
844         }
845 }
846
847 static void template_ID_tabs(
848         bContext *C, uiLayout *layout, TemplateID *template, StructRNA *type, int flag,
849         const char *newop, const char *menu)
850 {
851         const ARegion *region = CTX_wm_region(C);
852         const PointerRNA active_ptr = RNA_property_pointer_get(&template->ptr, template->prop);
853         MenuType *mt = WM_menutype_find(menu, false);
854
855         const int but_align = ui_but_align_opposite_to_area_align_get(region);
856         const int but_height = UI_UNIT_Y * 1.1;
857
858         uiBlock *block = uiLayoutGetBlock(layout);
859         uiStyle *style = UI_style_get_dpi();
860
861         ListBase ordered;
862         BKE_id_ordered_list(&ordered, template->idlb);
863
864         for (LinkData *link = ordered.first; link; link = link->next) {
865                 ID *id = link->data;
866                 const int name_width = UI_fontstyle_string_width(&style->widgetlabel, id->name + 2);
867                 const int but_width = name_width + UI_UNIT_X;
868
869                 uiButTab *tab = (uiButTab *)uiDefButR_prop(
870                         block, UI_BTYPE_TAB, 0, id->name + 2, 0, 0, but_width, but_height,
871                         &template->ptr, template->prop, 0, 0.0f,
872                         sizeof(id->name) - 2, 0.0f, 0.0f, "");
873                 UI_but_funcN_set(&tab->but, template_ID_set_property_cb, MEM_dupallocN(template), id);
874                 tab->but.custom_data = (void *)id;
875                 tab->but.dragpoin = id;
876                 tab->menu = mt;
877
878                 UI_but_drawflag_enable(&tab->but, but_align);
879         }
880
881         BLI_freelistN(&ordered);
882
883         if (flag & UI_ID_ADD_NEW) {
884                 const bool editable = RNA_property_editable(&template->ptr, template->prop);
885                 uiBut *but;
886
887                 if (active_ptr.type) {
888                         type = active_ptr.type;
889                 }
890
891                 but = template_id_def_new_but(block, active_ptr.data, template, type, newop, editable, flag & UI_ID_OPEN, true, but_height);
892                 UI_but_drawflag_enable(but, but_align);
893         }
894 }
895
896 static void ui_template_id(
897         uiLayout *layout, bContext *C,
898         PointerRNA *ptr, const char *propname,
899         const char *newop, const char *openop, const char *unlinkop,
900         int flag, int prv_rows, int prv_cols, int filter, bool use_tabs,
901         float scale, const bool live_icon, const bool hide_buttons)
902 {
903         TemplateID *template_ui;
904         PropertyRNA *prop;
905         StructRNA *type;
906         short idcode;
907
908         prop = RNA_struct_find_property(ptr, propname);
909
910         if (!prop || RNA_property_type(prop) != PROP_POINTER) {
911                 RNA_warning("pointer property not found: %s.%s", RNA_struct_identifier(ptr->type), propname);
912                 return;
913         }
914
915         template_ui = MEM_callocN(sizeof(TemplateID), "TemplateID");
916         template_ui->ptr = *ptr;
917         template_ui->prop = prop;
918         template_ui->prv_rows = prv_rows;
919         template_ui->prv_cols = prv_cols;
920         template_ui->scale = scale;
921
922         if ((flag & UI_ID_PIN) == 0) {
923                 template_ui->filter = filter;
924         }
925         else {
926                 template_ui->filter = 0;
927         }
928
929         if (newop)
930                 flag |= UI_ID_ADD_NEW;
931         if (openop)
932                 flag |= UI_ID_OPEN;
933
934         type = RNA_property_pointer_type(ptr, prop);
935         idcode = RNA_type_to_ID_code(type);
936         template_ui->idcode = idcode;
937         template_ui->idlb = which_libbase(CTX_data_main(C), idcode);
938
939         /* create UI elements for this template
940          * - template_ID makes a copy of the template data and assigns it to the relevant buttons
941          */
942         if (template_ui->idlb) {
943                 if (use_tabs) {
944                         uiLayoutRow(layout, true);
945                         template_ID_tabs(C, layout, template_ui, type, flag, newop, unlinkop);
946                 }
947                 else {
948                         uiLayoutRow(layout, true);
949                         template_ID(
950                                 C, layout, template_ui, type, flag, newop, openop,
951                                 unlinkop, live_icon, hide_buttons);
952                 }
953         }
954
955         MEM_freeN(template_ui);
956 }
957
958 void uiTemplateID(
959         uiLayout *layout, bContext *C, PointerRNA *ptr, const char *propname, const char *newop,
960         const char *openop, const char *unlinkop,
961         int filter, const bool live_icon)
962 {
963         ui_template_id(
964                 layout, C, ptr, propname,
965                 newop, openop, unlinkop,
966                 UI_ID_BROWSE | UI_ID_RENAME | UI_ID_DELETE,
967                 0, 0, filter, false, 1.0f, live_icon, false);
968 }
969
970 void uiTemplateIDBrowse(
971         uiLayout *layout, bContext *C, PointerRNA *ptr, const char *propname, const char *newop,
972         const char *openop, const char *unlinkop, int filter)
973 {
974         ui_template_id(
975                 layout, C, ptr, propname,
976                 newop, openop, unlinkop,
977                 UI_ID_BROWSE | UI_ID_RENAME,
978                 0, 0, filter, false, 1.0f, false, false);
979 }
980
981 void uiTemplateIDPreview(
982         uiLayout *layout, bContext *C, PointerRNA *ptr, const char *propname, const char *newop,
983         const char *openop, const char *unlinkop, int rows, int cols, int filter,
984         const bool hide_buttons)
985 {
986         ui_template_id(
987                 layout, C, ptr, propname,
988                 newop, openop, unlinkop,
989                 UI_ID_BROWSE | UI_ID_RENAME | UI_ID_DELETE | UI_ID_PREVIEWS,
990                 rows, cols, filter, false, 1.0f, false, hide_buttons);
991 }
992
993 void uiTemplateGpencilColorPreview(
994         uiLayout *layout, bContext *C, PointerRNA *ptr, const char *propname,
995         int rows, int cols, float scale, int filter)
996 {
997         ui_template_id(
998                 layout, C, ptr, propname,
999                 NULL, NULL, NULL,
1000                 UI_ID_BROWSE | UI_ID_PREVIEWS | UI_ID_DELETE,
1001                 rows, cols, filter, false, scale < 0.5f ? 0.5f : scale, false, false);
1002 }
1003
1004 /**
1005  * Version of #uiTemplateID using tabs.
1006  */
1007 void uiTemplateIDTabs(
1008         uiLayout *layout, bContext *C,
1009         PointerRNA *ptr, const char *propname,
1010         const char *newop, const char *unlinkop,
1011         int filter)
1012 {
1013         ui_template_id(
1014                 layout, C, ptr, propname,
1015                 newop, NULL, unlinkop,
1016                 UI_ID_BROWSE | UI_ID_RENAME,
1017                 0, 0, filter, true, 1.0f, false, false);
1018 }
1019
1020 /************************ ID Chooser Template ***************************/
1021
1022 /**
1023  * This is for selecting the type of ID-block to use, and then from the relevant type choosing the block to use
1024  *
1025  * - propname: property identifier for property that ID-pointer gets stored to
1026  * - proptypename: property identifier for property used to determine the type of ID-pointer that can be used
1027  */
1028 void uiTemplateAnyID(
1029         uiLayout *layout, PointerRNA *ptr, const char *propname, const char *proptypename,
1030         const char *text)
1031 {
1032         PropertyRNA *propID, *propType;
1033         uiLayout *split, *row, *sub;
1034
1035         /* get properties... */
1036         propID = RNA_struct_find_property(ptr, propname);
1037         propType = RNA_struct_find_property(ptr, proptypename);
1038
1039         if (!propID || RNA_property_type(propID) != PROP_POINTER) {
1040                 RNA_warning("pointer property not found: %s.%s", RNA_struct_identifier(ptr->type), propname);
1041                 return;
1042         }
1043         if (!propType || RNA_property_type(propType) != PROP_ENUM) {
1044                 RNA_warning("pointer-type property not found: %s.%s", RNA_struct_identifier(ptr->type), proptypename);
1045                 return;
1046         }
1047
1048         /* Start drawing UI Elements using standard defines */
1049
1050         /* NOTE: split amount here needs to be synced with normal labels */
1051         split = uiLayoutSplit(layout, 0.33f, false);
1052
1053         /* FIRST PART ................................................ */
1054         row = uiLayoutRow(split, false);
1055
1056         /* Label - either use the provided text, or will become "ID-Block:" */
1057         if (text) {
1058                 if (text[0])
1059                         uiItemL(row, text, ICON_NONE);
1060         }
1061         else {
1062                 uiItemL(row, IFACE_("ID-Block:"), ICON_NONE);
1063         }
1064
1065         /* SECOND PART ................................................ */
1066         row = uiLayoutRow(split, true);
1067
1068         /* ID-Type Selector - just have a menu of icons */
1069
1070         /* HACK: special group just for the enum,
1071          * otherwise we get ugly layout with text included too... */
1072         sub = uiLayoutRow(row, true);
1073         uiLayoutSetAlignment(sub, UI_LAYOUT_ALIGN_LEFT);
1074
1075         uiItemFullR(sub, ptr, propType, 0, 0, UI_ITEM_R_ICON_ONLY, "", ICON_NONE);
1076
1077         /* ID-Block Selector - just use pointer widget... */
1078
1079         /* HACK: special group to counteract the effects of the previous enum,
1080          * which now pushes everything too far right. */
1081         sub = uiLayoutRow(row, true);
1082         uiLayoutSetAlignment(sub, UI_LAYOUT_ALIGN_EXPAND);
1083
1084         uiItemFullR(sub, ptr, propID, 0, 0, 0, "", ICON_NONE);
1085 }
1086
1087 /********************* Search Template ********************/
1088
1089 typedef struct TemplateSearch {
1090         uiRNACollectionSearch search_data;
1091
1092         bool use_previews;
1093         int preview_rows, preview_cols;
1094 } TemplateSearch;
1095
1096 static void template_search_handle_cb(bContext *C, void *arg_template, void *item)
1097 {
1098         TemplateSearch *template_search = arg_template;
1099         uiRNACollectionSearch *coll_search = &template_search->search_data;
1100         StructRNA *type = RNA_property_pointer_type(&coll_search->target_ptr, coll_search->target_prop);
1101         PointerRNA item_ptr;
1102
1103         RNA_pointer_create(NULL, type, item, &item_ptr);
1104         RNA_property_pointer_set(&coll_search->target_ptr, coll_search->target_prop, item_ptr);
1105         RNA_property_update(C, &coll_search->target_ptr, coll_search->target_prop);
1106 }
1107
1108 static uiBlock *template_search_menu(bContext *C, ARegion *region, void *arg_template)
1109 {
1110         static TemplateSearch template_search;
1111         PointerRNA active_ptr;
1112
1113         /* arg_template is malloced, can be freed by parent button */
1114         template_search = *((TemplateSearch *)arg_template);
1115         active_ptr = RNA_property_pointer_get(
1116                 &template_search.search_data.target_ptr,
1117                 template_search.search_data.target_prop);
1118
1119         return template_common_search_menu(
1120                 C, region, ui_rna_collection_search_cb, &template_search,
1121                 template_search_handle_cb, active_ptr.data,
1122                 template_search.preview_rows, template_search.preview_cols, 1.0f);
1123 }
1124
1125 static void template_search_add_button_searchmenu(
1126         const bContext *C, uiLayout *layout, uiBlock *block,
1127         TemplateSearch *template_search, const bool editable, const bool live_icon)
1128 {
1129         const char *ui_description = RNA_property_ui_description(template_search->search_data.target_prop);
1130
1131         template_add_button_search_menu(
1132                 C, layout, block,
1133                 &template_search->search_data.target_ptr, template_search->search_data.target_prop,
1134                 template_search_menu, MEM_dupallocN(template_search), ui_description,
1135                 template_search->use_previews, editable, live_icon);
1136 }
1137
1138 static void template_search_add_button_name(
1139         uiBlock *block, PointerRNA *active_ptr, const StructRNA *type)
1140 {
1141         uiDefAutoButR(
1142                 block, active_ptr, RNA_struct_name_property(type), 0, "", ICON_NONE,
1143                 0, 0, TEMPLATE_SEARCH_TEXTBUT_WIDTH, TEMPLATE_SEARCH_TEXTBUT_HEIGHT);
1144 }
1145
1146 static void template_search_add_button_operator(
1147         uiBlock *block, const char * const operator_name,
1148         const int opcontext, const int icon, const bool editable)
1149 {
1150         if (!operator_name) {
1151                 return;
1152         }
1153
1154         uiBut *but = uiDefIconButO(
1155                 block, UI_BTYPE_BUT, operator_name, opcontext, icon,
1156                 0, 0, UI_UNIT_X, UI_UNIT_Y, NULL);
1157
1158         if (!editable) {
1159                 UI_but_drawflag_enable(but, UI_BUT_DISABLED);
1160         }
1161 }
1162
1163 static void template_search_buttons(
1164         const bContext *C, uiLayout *layout, TemplateSearch *template_search,
1165         const char *newop, const char *unlinkop)
1166 {
1167         uiBlock *block = uiLayoutGetBlock(layout);
1168         uiRNACollectionSearch *search_data = &template_search->search_data;
1169         StructRNA *type = RNA_property_pointer_type(&search_data->target_ptr, search_data->target_prop);
1170         const bool editable = RNA_property_editable(&search_data->target_ptr, search_data->target_prop);
1171         PointerRNA active_ptr = RNA_property_pointer_get(&search_data->target_ptr, search_data->target_prop);
1172
1173         if (active_ptr.type) {
1174                 /* can only get correct type when there is an active item */
1175                 type = active_ptr.type;
1176         }
1177
1178         uiLayoutRow(layout, true);
1179         UI_block_align_begin(block);
1180
1181         template_search_add_button_searchmenu(C, layout, block, template_search, editable, false);
1182         template_search_add_button_name(block, &active_ptr, type);
1183         template_search_add_button_operator(block, newop, WM_OP_INVOKE_DEFAULT, ICON_DUPLICATE, editable);
1184         template_search_add_button_operator(block, unlinkop, WM_OP_INVOKE_REGION_WIN, ICON_X, editable);
1185
1186         UI_block_align_end(block);
1187 }
1188
1189 static PropertyRNA *template_search_get_searchprop(
1190         PointerRNA *targetptr, PropertyRNA *targetprop,
1191         PointerRNA *searchptr, const char * const searchpropname)
1192 {
1193         PropertyRNA *searchprop;
1194
1195         if (searchptr && !searchptr->data) {
1196                 searchptr = NULL;
1197         }
1198
1199         if (!searchptr && !searchpropname) {
1200                 /* both NULL means we don't use a custom rna collection to search in */
1201         }
1202         else if (!searchptr && searchpropname) {
1203                 RNA_warning("searchpropname defined (%s) but searchptr is missing", searchpropname);
1204         }
1205         else if (searchptr && !searchpropname) {
1206                 RNA_warning("searchptr defined (%s) but searchpropname is missing", RNA_struct_identifier(searchptr->type));
1207         }
1208         else if (!(searchprop = RNA_struct_find_property(searchptr, searchpropname))) {
1209                 RNA_warning("search collection property not found: %s.%s",
1210                             RNA_struct_identifier(searchptr->type), searchpropname);
1211         }
1212         else if (RNA_property_type(searchprop) != PROP_COLLECTION) {
1213                 RNA_warning("search collection property is not a collection type: %s.%s",
1214                             RNA_struct_identifier(searchptr->type), searchpropname);
1215         }
1216         /* check if searchprop has same type as targetprop */
1217         else if (RNA_property_pointer_type(searchptr, searchprop) != RNA_property_pointer_type(targetptr, targetprop)) {
1218                 RNA_warning("search collection items from %s.%s are not of type %s",
1219                             RNA_struct_identifier(searchptr->type), searchpropname,
1220                             RNA_struct_identifier(RNA_property_pointer_type(targetptr, targetprop)));
1221         }
1222         else {
1223                 return searchprop;
1224         }
1225
1226         return NULL;
1227 }
1228
1229 static TemplateSearch *template_search_setup(
1230         PointerRNA *ptr, const char * const propname,
1231         PointerRNA *searchptr, const char * const searchpropname)
1232 {
1233         TemplateSearch *template_search;
1234         PropertyRNA *prop, *searchprop;
1235
1236         prop = RNA_struct_find_property(ptr, propname);
1237
1238         if (!prop || RNA_property_type(prop) != PROP_POINTER) {
1239                 RNA_warning("pointer property not found: %s.%s", RNA_struct_identifier(ptr->type), propname);
1240                 return NULL;
1241         }
1242         searchprop = template_search_get_searchprop(ptr, prop, searchptr, searchpropname);
1243
1244         template_search = MEM_callocN(sizeof(*template_search), __func__);
1245         template_search->search_data.target_ptr = *ptr;
1246         template_search->search_data.target_prop = prop;
1247         template_search->search_data.search_ptr = *searchptr;
1248         template_search->search_data.search_prop = searchprop;
1249
1250         return template_search;
1251 }
1252
1253 /**
1254  * Search menu to pick an item from a collection.
1255  * A version of uiTemplateID that works for non-ID types.
1256  */
1257 void uiTemplateSearch(
1258         uiLayout *layout, bContext *C,
1259         PointerRNA *ptr, const char *propname,
1260         PointerRNA *searchptr, const char *searchpropname,
1261         const char *newop, const char *unlinkop)
1262 {
1263         TemplateSearch *template_search = template_search_setup(ptr, propname, searchptr, searchpropname);
1264         if (template_search != NULL) {
1265                 template_search_buttons(C, layout, template_search, newop, unlinkop);
1266                 MEM_freeN(template_search);
1267         }
1268 }
1269
1270 void uiTemplateSearchPreview(
1271         uiLayout *layout, bContext *C,
1272         PointerRNA *ptr, const char *propname,
1273         PointerRNA *searchptr, const char *searchpropname,
1274         const char *newop, const char *unlinkop,
1275         const int rows, const int cols)
1276 {
1277         TemplateSearch *template_search = template_search_setup(ptr, propname, searchptr, searchpropname);
1278
1279         if (template_search != NULL) {
1280                 template_search->use_previews = true;
1281                 template_search->preview_rows = rows;
1282                 template_search->preview_cols = cols;
1283
1284                 template_search_buttons(C, layout, template_search, newop, unlinkop);
1285
1286                 MEM_freeN(template_search);
1287         }
1288 }
1289
1290 /********************* RNA Path Builder Template ********************/
1291
1292 /* ---------- */
1293
1294 /**
1295  * This is creating/editing RNA-Paths
1296  *
1297  * - ptr: struct which holds the path property
1298  * - propname: property identifier for property that path gets stored to
1299  * - root_ptr: struct that path gets built from
1300  */
1301 void uiTemplatePathBuilder(
1302         uiLayout *layout, PointerRNA *ptr, const char *propname, PointerRNA *UNUSED(root_ptr),
1303         const char *text)
1304 {
1305         PropertyRNA *propPath;
1306         uiLayout *row;
1307
1308         /* check that properties are valid */
1309         propPath = RNA_struct_find_property(ptr, propname);
1310         if (!propPath || RNA_property_type(propPath) != PROP_STRING) {
1311                 RNA_warning("path property not found: %s.%s", RNA_struct_identifier(ptr->type), propname);
1312                 return;
1313         }
1314
1315         /* Start drawing UI Elements using standard defines */
1316         row = uiLayoutRow(layout, true);
1317
1318         /* Path (existing string) Widget */
1319         uiItemR(row, ptr, propname, 0, text, ICON_RNA);
1320
1321         /* TODO: attach something to this to make allow
1322          * searching of nested properties to 'build' the path */
1323 }
1324
1325 /************************ Modifier Template *************************/
1326
1327 #define ERROR_LIBDATA_MESSAGE IFACE_("Can't edit external libdata")
1328
1329 static void modifiers_convertToReal(bContext *C, void *ob_v, void *md_v)
1330 {
1331         Object *ob = ob_v;
1332         ModifierData *md = md_v;
1333         ModifierData *nmd = modifier_new(md->type);
1334
1335         modifier_copyData(md, nmd);
1336         nmd->mode &= ~eModifierMode_Virtual;
1337
1338         BLI_addhead(&ob->modifiers, nmd);
1339
1340         modifier_unique_name(&ob->modifiers, nmd);
1341
1342         ob->partype = PAROBJECT;
1343
1344         WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER, ob);
1345         DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
1346
1347         ED_undo_push(C, "Modifier convert to real");
1348 }
1349
1350 static int modifier_can_delete(ModifierData *md)
1351 {
1352         /* fluid particle modifier can't be deleted here */
1353         if (md->type == eModifierType_ParticleSystem)
1354                 if (((ParticleSystemModifierData *)md)->psys->part->type == PART_FLUID)
1355                         return 0;
1356
1357         return 1;
1358 }
1359
1360 /* Check whether Modifier is a simulation or not,
1361  * this is used for switching to the physics/particles context tab */
1362 static int modifier_is_simulation(ModifierData *md)
1363 {
1364         /* Physic Tab */
1365         if (ELEM(md->type, eModifierType_Cloth, eModifierType_Collision, eModifierType_Fluidsim, eModifierType_Smoke,
1366                  eModifierType_Softbody, eModifierType_Surface, eModifierType_DynamicPaint))
1367         {
1368                 return 1;
1369         }
1370         /* Particle Tab */
1371         else if (md->type == eModifierType_ParticleSystem) {
1372                 return 2;
1373         }
1374         else {
1375                 return 0;
1376         }
1377 }
1378
1379 static uiLayout *draw_modifier(
1380         uiLayout *layout, Scene *scene, Object *ob,
1381         ModifierData *md, int index, int cageIndex, int lastCageIndex)
1382 {
1383         const ModifierTypeInfo *mti = modifierType_getInfo(md->type);
1384         PointerRNA ptr;
1385         uiBut *but;
1386         uiBlock *block;
1387         uiLayout *box, *column, *row, *sub;
1388         uiLayout *result = NULL;
1389         int isVirtual = (md->mode & eModifierMode_Virtual);
1390         char str[128];
1391
1392         /* create RNA pointer */
1393         RNA_pointer_create(&ob->id, &RNA_Modifier, md, &ptr);
1394
1395         column = uiLayoutColumn(layout, true);
1396         uiLayoutSetContextPointer(column, "modifier", &ptr);
1397
1398         /* rounded header ------------------------------------------------------------------- */
1399         box = uiLayoutBox(column);
1400
1401         if (isVirtual) {
1402                 row = uiLayoutRow(box, false);
1403                 uiLayoutSetAlignment(row, UI_LAYOUT_ALIGN_EXPAND);
1404                 block = uiLayoutGetBlock(row);
1405                 /* VIRTUAL MODIFIER */
1406                 /* XXX this is not used now, since these cannot be accessed via RNA */
1407                 BLI_snprintf(str, sizeof(str), IFACE_("%s parent deform"), md->name);
1408                 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"));
1409
1410                 but = uiDefBut(
1411                         block, UI_BTYPE_BUT, 0, IFACE_("Make Real"), 0, 0, 80, 16, NULL, 0.0, 0.0, 0.0, 0.0,
1412                         TIP_("Convert virtual modifier to a real modifier"));
1413                 UI_but_func_set(but, modifiers_convertToReal, ob, md);
1414         }
1415         else {
1416                 /* REAL MODIFIER */
1417                 row = uiLayoutRow(box, false);
1418                 block = uiLayoutGetBlock(row);
1419
1420                 UI_block_emboss_set(block, UI_EMBOSS_NONE);
1421                 /* Open/Close .................................  */
1422                 uiItemR(row, &ptr, "show_expanded", 0, "", ICON_NONE);
1423
1424                 /* modifier-type icon */
1425                 uiItemL(row, "", RNA_struct_ui_icon(ptr.type));
1426                 UI_block_emboss_set(block, UI_EMBOSS);
1427
1428                 /* modifier name */
1429                 if (mti->isDisabled && mti->isDisabled(scene, md, 0)) {
1430                         uiLayoutSetRedAlert(row, true);
1431                 }
1432                 uiItemR(row, &ptr, "name", 0, "", ICON_NONE);
1433                 uiLayoutSetRedAlert(row, false);
1434
1435                 /* mode enabling buttons */
1436                 UI_block_align_begin(block);
1437                 /* Collision and Surface are always enabled, hide buttons! */
1438                 if (((md->type != eModifierType_Collision) || !(ob->pd && ob->pd->deflect)) &&
1439                     (md->type != eModifierType_Surface) )
1440                 {
1441                         uiItemR(row, &ptr, "show_render", 0, "", ICON_NONE);
1442                         uiItemR(row, &ptr, "show_viewport", 0, "", ICON_NONE);
1443
1444                         if (mti->flags & eModifierTypeFlag_SupportsEditmode) {
1445                                 sub = uiLayoutRow(row, true);
1446                                 if (!(md->mode & eModifierMode_Realtime)) {
1447                                         uiLayoutSetActive(sub, false);
1448                                 }
1449                                 uiItemR(sub, &ptr, "show_in_editmode", 0, "", ICON_NONE);
1450                         }
1451                 }
1452
1453                 if (ob->type == OB_MESH) {
1454                         if (modifier_supportsCage(scene, md) && (index <= lastCageIndex)) {
1455                                 sub = uiLayoutRow(row, true);
1456                                 if (index < cageIndex || !modifier_couldBeCage(scene, md)) {
1457                                         uiLayoutSetActive(sub, false);
1458                                 }
1459                                 uiItemR(sub, &ptr, "show_on_cage", 0, "", ICON_NONE);
1460                         }
1461                 } /* tessellation point for curve-typed objects */
1462                 else if (ELEM(ob->type, OB_CURVE, OB_SURF, OB_FONT)) {
1463                         /* some modifiers could work with pre-tessellated curves only */
1464                         if (ELEM(md->type, eModifierType_Hook, eModifierType_Softbody, eModifierType_MeshDeform)) {
1465                                 /* add disabled pre-tessellated button, so users could have
1466                                  * message for this modifiers */
1467                                 but = uiDefIconButBitI(
1468                                         block, UI_BTYPE_TOGGLE, eModifierMode_ApplyOnSpline, 0, ICON_SURFACE_DATA, 0, 0,
1469                                         UI_UNIT_X - 2, UI_UNIT_Y, &md->mode, 0.0, 0.0, 0.0, 0.0,
1470                                         TIP_("This modifier can only be applied on splines' points"));
1471                                 UI_but_flag_enable(but, UI_BUT_DISABLED);
1472                         }
1473                         else if (mti->type != eModifierTypeType_Constructive) {
1474                                 /* constructive modifiers tessellates curve before applying */
1475                                 uiItemR(row, &ptr, "use_apply_on_spline", 0, "", ICON_NONE);
1476                         }
1477                 }
1478
1479                 UI_block_align_end(block);
1480
1481                 /* Up/Down + Delete ........................... */
1482                 UI_block_align_begin(block);
1483                 uiItemO(row, "", ICON_TRIA_UP, "OBJECT_OT_modifier_move_up");
1484                 uiItemO(row, "", ICON_TRIA_DOWN, "OBJECT_OT_modifier_move_down");
1485                 UI_block_align_end(block);
1486
1487                 UI_block_emboss_set(block, UI_EMBOSS_NONE);
1488                 /* When Modifier is a simulation,
1489                  * show button to switch to context rather than the delete button. */
1490                 if (modifier_can_delete(md) &&
1491                     !modifier_is_simulation(md))
1492                 {
1493                         uiItemO(row, "", ICON_X, "OBJECT_OT_modifier_remove");
1494                 }
1495                 else if (modifier_is_simulation(md) == 1) {
1496                         uiItemStringO(row, "", ICON_PROPERTIES, "WM_OT_properties_context_change", "context", "PHYSICS");
1497                 }
1498                 else if (modifier_is_simulation(md) == 2) {
1499                         uiItemStringO(row, "", ICON_PROPERTIES, "WM_OT_properties_context_change", "context", "PARTICLES");
1500                 }
1501                 UI_block_emboss_set(block, UI_EMBOSS);
1502         }
1503
1504
1505         /* modifier settings (under the header) --------------------------------------------------- */
1506         if (!isVirtual && (md->mode & eModifierMode_Expanded)) {
1507                 /* apply/convert/copy */
1508                 box = uiLayoutBox(column);
1509                 row = uiLayoutRow(box, false);
1510
1511                 if (!ELEM(md->type, eModifierType_Collision, eModifierType_Surface)) {
1512                         /* only here obdata, the rest of modifiers is ob level */
1513                         UI_block_lock_set(block, BKE_object_obdata_is_libdata(ob), ERROR_LIBDATA_MESSAGE);
1514
1515                         if (md->type == eModifierType_ParticleSystem) {
1516                                 ParticleSystem *psys = ((ParticleSystemModifierData *)md)->psys;
1517
1518                                 if (!(ob->mode & OB_MODE_PARTICLE_EDIT)) {
1519                                         if (ELEM(psys->part->ren_as, PART_DRAW_GR, PART_DRAW_OB)) {
1520                                                 uiItemO(row, CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Convert"), ICON_NONE,
1521                                                         "OBJECT_OT_duplicates_make_real");
1522                                         }
1523                                         else if (psys->part->ren_as == PART_DRAW_PATH) {
1524                                                 uiItemO(row, CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Convert"), ICON_NONE,
1525                                                         "OBJECT_OT_modifier_convert");
1526                                         }
1527                                 }
1528                         }
1529                         else {
1530                                 uiLayoutSetOperatorContext(row, WM_OP_INVOKE_DEFAULT);
1531                                 uiItemEnumO(
1532                                         row, "OBJECT_OT_modifier_apply", CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Apply"),
1533                                         0, "apply_as", MODIFIER_APPLY_DATA);
1534
1535                                 if (modifier_isSameTopology(md) && !modifier_isNonGeometrical(md)) {
1536                                         uiItemEnumO(
1537                                                 row, "OBJECT_OT_modifier_apply",
1538                                                 CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Apply as Shape Key"),
1539                                                 0, "apply_as", MODIFIER_APPLY_SHAPE);
1540                                 }
1541                         }
1542
1543                         UI_block_lock_clear(block);
1544                         UI_block_lock_set(block, ob && ID_IS_LINKED(ob), ERROR_LIBDATA_MESSAGE);
1545
1546                         if (!ELEM(md->type, eModifierType_Fluidsim, eModifierType_Softbody, eModifierType_ParticleSystem,
1547                                   eModifierType_Cloth, eModifierType_Smoke))
1548                         {
1549                                 uiItemO(row, CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Copy"), ICON_NONE,
1550                                         "OBJECT_OT_modifier_copy");
1551                         }
1552                 }
1553
1554                 /* result is the layout block inside the box,
1555                  * that we return so that modifier settings can be drawn */
1556                 result = uiLayoutColumn(box, false);
1557                 block = uiLayoutAbsoluteBlock(box);
1558         }
1559
1560         /* error messages */
1561         if (md->error) {
1562                 box = uiLayoutBox(column);
1563                 row = uiLayoutRow(box, false);
1564                 uiItemL(row, md->error, ICON_ERROR);
1565         }
1566
1567         return result;
1568 }
1569
1570 uiLayout *uiTemplateModifier(uiLayout *layout, bContext *C, PointerRNA *ptr)
1571 {
1572         Scene *scene = CTX_data_scene(C);
1573         Object *ob;
1574         ModifierData *md, *vmd;
1575         VirtualModifierData virtualModifierData;
1576         int i, lastCageIndex, cageIndex;
1577
1578         /* verify we have valid data */
1579         if (!RNA_struct_is_a(ptr->type, &RNA_Modifier)) {
1580                 RNA_warning("Expected modifier on object");
1581                 return NULL;
1582         }
1583
1584         ob = ptr->id.data;
1585         md = ptr->data;
1586
1587         if (!ob || !(GS(ob->id.name) == ID_OB)) {
1588                 RNA_warning("Expected modifier on object");
1589                 return NULL;
1590         }
1591
1592         UI_block_lock_set(uiLayoutGetBlock(layout), (ob && ID_IS_LINKED(ob)), ERROR_LIBDATA_MESSAGE);
1593
1594         /* find modifier and draw it */
1595         cageIndex = modifiers_getCageIndex(scene, ob, &lastCageIndex, 0);
1596
1597         /* XXX virtual modifiers are not accessible for python */
1598         vmd = modifiers_getVirtualModifierList(ob, &virtualModifierData);
1599
1600         for (i = 0; vmd; i++, vmd = vmd->next) {
1601                 if (md == vmd)
1602                         return draw_modifier(layout, scene, ob, md, i, cageIndex, lastCageIndex);
1603                 else if (vmd->mode & eModifierMode_Virtual)
1604                         i--;
1605         }
1606
1607         return NULL;
1608 }
1609
1610 /************************ Grease Pencil Modifier Template *************************/
1611
1612 static uiLayout *gpencil_draw_modifier(
1613         uiLayout *layout, Object *ob,
1614         GpencilModifierData *md)
1615 {
1616         const GpencilModifierTypeInfo *mti = BKE_gpencil_modifierType_getInfo(md->type);
1617         PointerRNA ptr;
1618         uiBlock *block;
1619         uiLayout *box, *column, *row, *sub;
1620         uiLayout *result = NULL;
1621
1622         /* create RNA pointer */
1623         RNA_pointer_create(&ob->id, &RNA_GpencilModifier, md, &ptr);
1624
1625         column = uiLayoutColumn(layout, true);
1626         uiLayoutSetContextPointer(column, "modifier", &ptr);
1627
1628         /* rounded header ------------------------------------------------------------------- */
1629         box = uiLayoutBox(column);
1630
1631         row = uiLayoutRow(box, false);
1632         block = uiLayoutGetBlock(row);
1633
1634         UI_block_emboss_set(block, UI_EMBOSS_NONE);
1635         /* Open/Close .................................  */
1636         uiItemR(row, &ptr, "show_expanded", 0, "", ICON_NONE);
1637
1638         /* modifier-type icon */
1639         uiItemL(row, "", RNA_struct_ui_icon(ptr.type));
1640         UI_block_emboss_set(block, UI_EMBOSS);
1641
1642         /* modifier name */
1643         if (mti->isDisabled && mti->isDisabled(md, 0)) {
1644                 uiLayoutSetRedAlert(row, true);
1645         }
1646         uiItemR(row, &ptr, "name", 0, "", ICON_NONE);
1647         uiLayoutSetRedAlert(row, false);
1648
1649         /* mode enabling buttons */
1650         UI_block_align_begin(block);
1651         uiItemR(row, &ptr, "show_render", 0, "", ICON_NONE);
1652         uiItemR(row, &ptr, "show_viewport", 0, "", ICON_NONE);
1653
1654         if (mti->flags & eGpencilModifierTypeFlag_SupportsEditmode) {
1655                 sub = uiLayoutRow(row, true);
1656                 uiLayoutSetActive(sub, false);
1657                 uiItemR(sub, &ptr, "show_in_editmode", 0, "", ICON_NONE);
1658         }
1659
1660         UI_block_align_end(block);
1661
1662         /* Up/Down + Delete ........................... */
1663         UI_block_align_begin(block);
1664         uiItemO(row, "", ICON_TRIA_UP, "OBJECT_OT_gpencil_modifier_move_up");
1665         uiItemO(row, "", ICON_TRIA_DOWN, "OBJECT_OT_gpencil_modifier_move_down");
1666         UI_block_align_end(block);
1667
1668         UI_block_emboss_set(block, UI_EMBOSS_NONE);
1669         uiItemO(row, "", ICON_X, "OBJECT_OT_gpencil_modifier_remove");
1670         UI_block_emboss_set(block, UI_EMBOSS);
1671
1672         /* modifier settings (under the header) --------------------------------------------------- */
1673         if (md->mode & eGpencilModifierMode_Expanded) {
1674                 /* apply/convert/copy */
1675                 box = uiLayoutBox(column);
1676                 row = uiLayoutRow(box, false);
1677
1678                 /* only here obdata, the rest of modifiers is ob level */
1679                 UI_block_lock_set(block, BKE_object_obdata_is_libdata(ob), ERROR_LIBDATA_MESSAGE);
1680
1681                 uiLayoutSetOperatorContext(row, WM_OP_INVOKE_DEFAULT);
1682
1683                 sub = uiLayoutRow(row, false);
1684                 if (mti->flags & eGpencilModifierTypeFlag_NoApply) {
1685                         uiLayoutSetEnabled(sub, false);
1686                 }
1687                 uiItemEnumO(sub, "OBJECT_OT_gpencil_modifier_apply", CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Apply"),
1688                             0, "apply_as", MODIFIER_APPLY_DATA);
1689                 uiItemO(row, CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Copy"), ICON_NONE,
1690                         "OBJECT_OT_gpencil_modifier_copy");
1691
1692                 /* result is the layout block inside the box,
1693                  * that we return so that modifier settings can be drawn */
1694                 result = uiLayoutColumn(box, false);
1695                 block = uiLayoutAbsoluteBlock(box);
1696         }
1697
1698         /* error messages */
1699         if (md->error) {
1700                 box = uiLayoutBox(column);
1701                 row = uiLayoutRow(box, false);
1702                 uiItemL(row, md->error, ICON_ERROR);
1703         }
1704
1705         return result;
1706 }
1707
1708 uiLayout *uiTemplateGpencilModifier(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
1709 {
1710         Object *ob;
1711         GpencilModifierData *md, *vmd;
1712         int i;
1713
1714         /* verify we have valid data */
1715         if (!RNA_struct_is_a(ptr->type, &RNA_GpencilModifier)) {
1716                 RNA_warning("Expected modifier on object");
1717                 return NULL;
1718         }
1719
1720         ob = ptr->id.data;
1721         md = ptr->data;
1722
1723         if (!ob || !(GS(ob->id.name) == ID_OB)) {
1724                 RNA_warning("Expected modifier on object");
1725                 return NULL;
1726         }
1727
1728         UI_block_lock_set(uiLayoutGetBlock(layout), (ob && ID_IS_LINKED(ob)), ERROR_LIBDATA_MESSAGE);
1729
1730         /* find modifier and draw it */
1731         vmd = ob->greasepencil_modifiers.first;
1732         for (i = 0; vmd; i++, vmd = vmd->next) {
1733                 if (md == vmd)
1734                         return gpencil_draw_modifier(layout, ob, md);
1735         }
1736
1737         return NULL;
1738 }
1739
1740 /************************ Shader FX Template *************************/
1741
1742 static uiLayout *gpencil_draw_shaderfx(
1743         uiLayout *layout, Object *ob,
1744         ShaderFxData *md)
1745 {
1746         const ShaderFxTypeInfo *mti = BKE_shaderfxType_getInfo(md->type);
1747         PointerRNA ptr;
1748         uiBlock *block;
1749         uiLayout *box, *column, *row, *sub;
1750         uiLayout *result = NULL;
1751
1752         /* create RNA pointer */
1753         RNA_pointer_create(&ob->id, &RNA_ShaderFx, md, &ptr);
1754
1755         column = uiLayoutColumn(layout, true);
1756         uiLayoutSetContextPointer(column, "shaderfx", &ptr);
1757
1758         /* rounded header ------------------------------------------------------------------- */
1759         box = uiLayoutBox(column);
1760
1761         row = uiLayoutRow(box, false);
1762         block = uiLayoutGetBlock(row);
1763
1764         UI_block_emboss_set(block, UI_EMBOSS_NONE);
1765         /* Open/Close .................................  */
1766         uiItemR(row, &ptr, "show_expanded", 0, "", ICON_NONE);
1767
1768         /* shader-type icon */
1769         uiItemL(row, "", RNA_struct_ui_icon(ptr.type));
1770         UI_block_emboss_set(block, UI_EMBOSS);
1771
1772         /* effect name */
1773         if (mti->isDisabled && mti->isDisabled(md, 0)) {
1774                 uiLayoutSetRedAlert(row, true);
1775         }
1776         uiItemR(row, &ptr, "name", 0, "", ICON_NONE);
1777         uiLayoutSetRedAlert(row, false);
1778
1779         /* mode enabling buttons */
1780         UI_block_align_begin(block);
1781         uiItemR(row, &ptr, "show_render", 0, "", ICON_NONE);
1782         uiItemR(row, &ptr, "show_viewport", 0, "", ICON_NONE);
1783
1784         if (mti->flags & eShaderFxTypeFlag_SupportsEditmode) {
1785                 sub = uiLayoutRow(row, true);
1786                 uiLayoutSetActive(sub, false);
1787                 uiItemR(sub, &ptr, "show_in_editmode", 0, "", ICON_NONE);
1788         }
1789
1790         UI_block_align_end(block);
1791
1792         /* Up/Down + Delete ........................... */
1793         UI_block_align_begin(block);
1794         uiItemO(row, "", ICON_TRIA_UP, "OBJECT_OT_shaderfx_move_up");
1795         uiItemO(row, "", ICON_TRIA_DOWN, "OBJECT_OT_shaderfx_move_down");
1796         UI_block_align_end(block);
1797
1798         UI_block_emboss_set(block, UI_EMBOSS_NONE);
1799         uiItemO(row, "", ICON_X, "OBJECT_OT_shaderfx_remove");
1800         UI_block_emboss_set(block, UI_EMBOSS);
1801
1802         /* effect settings (under the header) --------------------------------------------------- */
1803         if (md->mode & eShaderFxMode_Expanded) {
1804                 /* apply/convert/copy */
1805                 box = uiLayoutBox(column);
1806                 row = uiLayoutRow(box, false);
1807
1808                 /* only here obdata, the rest of effect is ob level */
1809                 UI_block_lock_set(block, BKE_object_obdata_is_libdata(ob), ERROR_LIBDATA_MESSAGE);
1810
1811                 /* result is the layout block inside the box,
1812                  * that we return so that effect settings can be drawn */
1813                 result = uiLayoutColumn(box, false);
1814                 block = uiLayoutAbsoluteBlock(box);
1815         }
1816
1817         /* error messages */
1818         if (md->error) {
1819                 box = uiLayoutBox(column);
1820                 row = uiLayoutRow(box, false);
1821                 uiItemL(row, md->error, ICON_ERROR);
1822         }
1823
1824         return result;
1825 }
1826
1827 uiLayout *uiTemplateShaderFx(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
1828 {
1829         Object *ob;
1830         ShaderFxData *fx, *vfx;
1831         int i;
1832
1833         /* verify we have valid data */
1834         if (!RNA_struct_is_a(ptr->type, &RNA_ShaderFx)) {
1835                 RNA_warning("Expected shader fx on object");
1836                 return NULL;
1837         }
1838
1839         ob = ptr->id.data;
1840         fx = ptr->data;
1841
1842         if (!ob || !(GS(ob->id.name) == ID_OB)) {
1843                 RNA_warning("Expected shader fx on object");
1844                 return NULL;
1845         }
1846
1847         UI_block_lock_set(uiLayoutGetBlock(layout), (ob && ID_IS_LINKED(ob)), ERROR_LIBDATA_MESSAGE);
1848
1849         /* find modifier and draw it */
1850         vfx = ob->shader_fx.first;
1851         for (i = 0; vfx; i++, vfx = vfx->next) {
1852                 if (fx == vfx)
1853                         return gpencil_draw_shaderfx(layout, ob, fx);
1854         }
1855
1856         return NULL;
1857 }
1858
1859 /************************ Redo Buttons Template *************************/
1860
1861 static void template_operator_redo_property_buts_draw(
1862         const bContext *C, wmOperator *op,
1863         uiLayout *layout, int layout_flags,
1864         bool *r_has_advanced)
1865 {
1866         if (op->type->flag & OPTYPE_MACRO) {
1867                 for (wmOperator *macro_op = op->macro.first; macro_op; macro_op = macro_op->next) {
1868                         template_operator_redo_property_buts_draw(C, macro_op, layout, layout_flags, r_has_advanced);
1869                 }
1870         }
1871         else {
1872                 /* Might want to make label_align adjustable somehow. */
1873                 eAutoPropButsReturn return_info = uiTemplateOperatorPropertyButs(
1874                         C, layout, op,
1875                         UI_BUT_LABEL_ALIGN_NONE,
1876                         layout_flags);
1877                 if (return_info & UI_PROP_BUTS_ANY_FAILED_CHECK) {
1878                         if (r_has_advanced) {
1879                                 *r_has_advanced = true;
1880                         }
1881                 }
1882         }
1883 }
1884
1885 void uiTemplateOperatorRedoProperties(uiLayout *layout, const bContext *C)
1886 {
1887         wmOperator *op = WM_operator_last_redo(C);
1888         uiBlock *block = uiLayoutGetBlock(layout);
1889
1890         if (op == NULL) {
1891                 return;
1892         }
1893
1894         /* Disable for now, doesn't fit well in popover. */
1895 #if 0
1896         /* Repeat button with operator name as text. */
1897         uiItemFullO(layout, "SCREEN_OT_repeat_last", RNA_struct_ui_name(op->type->srna),
1898                     ICON_NONE, NULL, WM_OP_INVOKE_DEFAULT, 0, NULL);
1899 #endif
1900
1901         if (WM_operator_repeat_check(C, op)) {
1902                 int layout_flags = 0;
1903                 if (block->panel == NULL) {
1904                         layout_flags = UI_TEMPLATE_OP_PROPS_SHOW_TITLE;
1905                 }
1906 #if 0
1907                 bool has_advanced = false;
1908 #endif
1909
1910                 UI_block_func_handle_set(block, ED_undo_operator_repeat_cb_evt, op);
1911                 template_operator_redo_property_buts_draw(C, op, layout, layout_flags, NULL /* &has_advanced */ );
1912                 /* Warning! this leaves the handle function for any other users of this block. */
1913
1914 #if 0
1915                 if (has_advanced) {
1916                         uiItemO(layout, IFACE_("More..."), ICON_NONE, "SCREEN_OT_redo_last");
1917                 }
1918 #endif
1919         }
1920 }
1921
1922 /************************ Constraint Template *************************/
1923
1924 #include "DNA_constraint_types.h"
1925
1926 #include "BKE_action.h"
1927 #include "BKE_constraint.h"
1928
1929 #define B_CONSTRAINT_TEST           5
1930 // #define B_CONSTRAINT_CHANGETARGET   6
1931
1932 static void do_constraint_panels(bContext *C, void *ob_pt, int event)
1933 {
1934         Object *ob = (Object *)ob_pt;
1935
1936         switch (event) {
1937                 case B_CONSTRAINT_TEST:
1938                         break; /* no handling */
1939 #if 0   /* UNUSED */
1940                 case B_CONSTRAINT_CHANGETARGET:
1941                 {
1942                         Main *bmain = CTX_data_main(C);
1943                         if (ob->pose)
1944                                 BKE_pose_tag_recalc(bmain, ob->pose); /* checks & sorts pose channels */
1945                         DEG_relations_tag_update(bmain);
1946                         break;
1947                 }
1948 #endif
1949                 default:
1950                         break;
1951         }
1952
1953         /* note: RNA updates now call this, commenting else it gets called twice.
1954          * if there are problems because of this, then rna needs changed update functions.
1955          *
1956          * object_test_constraints(ob);
1957          * if (ob->pose) BKE_pose_update_constraint_flags(ob->pose); */
1958
1959         if (ob->type == OB_ARMATURE) DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY | ID_RECALC_TRANSFORM);
1960         else DEG_id_tag_update(&ob->id, ID_RECALC_TRANSFORM);
1961
1962         WM_event_add_notifier(C, NC_OBJECT | ND_CONSTRAINT, ob);
1963 }
1964
1965 static void constraint_active_func(bContext *UNUSED(C), void *ob_v, void *con_v)
1966 {
1967         ED_object_constraint_set_active(ob_v, con_v);
1968 }
1969
1970 /* draw panel showing settings for a constraint */
1971 static uiLayout *draw_constraint(uiLayout *layout, Object *ob, bConstraint *con)
1972 {
1973         bPoseChannel *pchan = BKE_pose_channel_active(ob);
1974         const bConstraintTypeInfo *cti;
1975         uiBlock *block;
1976         uiLayout *result = NULL, *col, *box, *row;
1977         PointerRNA ptr;
1978         char typestr[32];
1979         short proxy_protected, xco = 0, yco = 0;
1980         // int rb_col; // UNUSED
1981
1982         /* get constraint typeinfo */
1983         cti = BKE_constraint_typeinfo_get(con);
1984         if (cti == NULL) {
1985                 /* exception for 'Null' constraint - it doesn't have constraint typeinfo! */
1986                 BLI_strncpy(typestr, (con->type == CONSTRAINT_TYPE_NULL) ? IFACE_("Null") : IFACE_("Unknown"), sizeof(typestr));
1987         }
1988         else
1989                 BLI_strncpy(typestr, IFACE_(cti->name), sizeof(typestr));
1990
1991         /* determine whether constraint is proxy protected or not */
1992         if (BKE_constraints_proxylocked_owner(ob, pchan))
1993                 proxy_protected = (con->flag & CONSTRAINT_PROXY_LOCAL) == 0;
1994         else
1995                 proxy_protected = 0;
1996
1997         /* unless button has own callback, it adds this callback to button */
1998         block = uiLayoutGetBlock(layout);
1999         UI_block_func_handle_set(block, do_constraint_panels, ob);
2000         UI_block_func_set(block, constraint_active_func, ob, con);
2001
2002         RNA_pointer_create(&ob->id, &RNA_Constraint, con, &ptr);
2003
2004         col = uiLayoutColumn(layout, true);
2005         uiLayoutSetContextPointer(col, "constraint", &ptr);
2006
2007         box = uiLayoutBox(col);
2008         row = uiLayoutRow(box, false);
2009         block = uiLayoutGetBlock(box);
2010
2011         /* Draw constraint header */
2012
2013         /* open/close */
2014         UI_block_emboss_set(block, UI_EMBOSS_NONE);
2015         uiItemR(row, &ptr, "show_expanded", UI_ITEM_R_ICON_ONLY, "", ICON_NONE);
2016         UI_block_emboss_set(block, UI_EMBOSS);
2017
2018         /* name */
2019         uiDefBut(block, UI_BTYPE_LABEL, B_CONSTRAINT_TEST, typestr,
2020                  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, "");
2021
2022         if (con->flag & CONSTRAINT_DISABLE)
2023                 uiLayoutSetRedAlert(row, true);
2024
2025         if (proxy_protected == 0) {
2026                 uiItemR(row, &ptr, "name", 0, "", ICON_NONE);
2027         }
2028         else
2029                 uiItemL(row, con->name, ICON_NONE);
2030
2031         uiLayoutSetRedAlert(row, false);
2032
2033         /* proxy-protected constraints cannot be edited, so hide up/down + close buttons */
2034         if (proxy_protected) {
2035                 UI_block_emboss_set(block, UI_EMBOSS_NONE);
2036
2037                 /* draw a ghost icon (for proxy) and also a lock beside it,
2038                  * to show that constraint is "proxy locked" */
2039                 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,
2040                              NULL, 0.0, 0.0, 0.0, 0.0, TIP_("Proxy Protected"));
2041                 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,
2042                              NULL, 0.0, 0.0, 0.0, 0.0, TIP_("Proxy Protected"));
2043
2044                 UI_block_emboss_set(block, UI_EMBOSS);
2045         }
2046         else {
2047                 short prev_proxylock, show_upbut, show_downbut;
2048
2049                 /* Up/Down buttons:
2050                  * Proxy-constraints are not allowed to occur after local (non-proxy) constraints
2051                  * as that poses problems when restoring them, so disable the "up" button where
2052                  * it may cause this situation.
2053                  *
2054                  * Up/Down buttons should only be shown (or not grayed - todo) if they serve some purpose.
2055                  */
2056                 if (BKE_constraints_proxylocked_owner(ob, pchan)) {
2057                         if (con->prev) {
2058                                 prev_proxylock = (con->prev->flag & CONSTRAINT_PROXY_LOCAL) ? 0 : 1;
2059                         }
2060                         else
2061                                 prev_proxylock = 0;
2062                 }
2063                 else
2064                         prev_proxylock = 0;
2065
2066                 show_upbut = ((prev_proxylock == 0) && (con->prev));
2067                 show_downbut = (con->next) ? 1 : 0;
2068
2069                 /* enabled */
2070                 UI_block_emboss_set(block, UI_EMBOSS_NONE);
2071                 uiItemR(row, &ptr, "mute", 0, "",
2072                         (con->flag & CONSTRAINT_OFF) ? ICON_HIDE_ON : ICON_HIDE_OFF);
2073                 UI_block_emboss_set(block, UI_EMBOSS);
2074
2075                 uiLayoutSetOperatorContext(row, WM_OP_INVOKE_DEFAULT);
2076
2077                 /* up/down */
2078                 if (show_upbut || show_downbut) {
2079                         UI_block_align_begin(block);
2080                         if (show_upbut)
2081                                 uiItemO(row, "", ICON_TRIA_UP, "CONSTRAINT_OT_move_up");
2082
2083                         if (show_downbut)
2084                                 uiItemO(row, "", ICON_TRIA_DOWN, "CONSTRAINT_OT_move_down");
2085                         UI_block_align_end(block);
2086                 }
2087
2088                 /* Close 'button' - emboss calls here disable drawing of 'button' behind X */
2089                 UI_block_emboss_set(block, UI_EMBOSS_NONE);
2090                 uiItemO(row, "", ICON_X, "CONSTRAINT_OT_delete");
2091                 UI_block_emboss_set(block, UI_EMBOSS);
2092         }
2093
2094         /* Set but-locks for protected settings (magic numbers are used here!) */
2095         if (proxy_protected)
2096                 UI_block_lock_set(block, true, IFACE_("Cannot edit Proxy-Protected Constraint"));
2097
2098         /* Draw constraint data */
2099         if ((con->flag & CONSTRAINT_EXPAND) == 0) {
2100                 (yco) -= 10.5f * UI_UNIT_Y;
2101         }
2102         else {
2103                 box = uiLayoutBox(col);
2104                 block = uiLayoutAbsoluteBlock(box);
2105                 result = box;
2106         }
2107
2108         /* clear any locks set up for proxies/lib-linking */
2109         UI_block_lock_clear(block);
2110
2111         return result;
2112 }
2113
2114 uiLayout *uiTemplateConstraint(uiLayout *layout, PointerRNA *ptr)
2115 {
2116         Object *ob;
2117         bConstraint *con;
2118
2119         /* verify we have valid data */
2120         if (!RNA_struct_is_a(ptr->type, &RNA_Constraint)) {
2121                 RNA_warning("Expected constraint on object");
2122                 return NULL;
2123         }
2124
2125         ob = ptr->id.data;
2126         con = ptr->data;
2127
2128         if (!ob || !(GS(ob->id.name) == ID_OB)) {
2129                 RNA_warning("Expected constraint on object");
2130                 return NULL;
2131         }
2132
2133         UI_block_lock_set(uiLayoutGetBlock(layout), (ob && ID_IS_LINKED(ob)), ERROR_LIBDATA_MESSAGE);
2134
2135         /* hrms, the temporal constraint should not draw! */
2136         if (con->type == CONSTRAINT_TYPE_KINEMATIC) {
2137                 bKinematicConstraint *data = con->data;
2138                 if (data->flag & CONSTRAINT_IK_TEMP)
2139                         return NULL;
2140         }
2141
2142         return draw_constraint(layout, ob, con);
2143 }
2144
2145
2146 /************************* Preview Template ***************************/
2147
2148 #include "DNA_lamp_types.h"
2149 #include "DNA_material_types.h"
2150 #include "DNA_world_types.h"
2151
2152 #define B_MATPRV 1
2153
2154 static void do_preview_buttons(bContext *C, void *arg, int event)
2155 {
2156         switch (event) {
2157                 case B_MATPRV:
2158                         WM_event_add_notifier(C, NC_MATERIAL | ND_SHADING_PREVIEW, arg);
2159                         break;
2160         }
2161 }
2162
2163 void uiTemplatePreview(
2164         uiLayout *layout, bContext *C, ID *id, bool show_buttons, ID *parent, MTex *slot,
2165         const char *preview_id)
2166 {
2167         uiLayout *row, *col;
2168         uiBlock *block;
2169         uiPreview *ui_preview = NULL;
2170         ARegion *ar;
2171
2172         Material *ma = NULL;
2173         Tex *tex = (Tex *)id;
2174         ID *pid, *pparent;
2175         short *pr_texture = NULL;
2176         PointerRNA material_ptr;
2177         PointerRNA texture_ptr;
2178
2179         char _preview_id[UI_MAX_NAME_STR];
2180
2181         if (id && !ELEM(GS(id->name), ID_MA, ID_TE, ID_WO, ID_LA, ID_LS)) {
2182                 RNA_warning("Expected ID of type material, texture, light, world or line style");
2183                 return;
2184         }
2185
2186         /* decide what to render */
2187         pid = id;
2188         pparent = NULL;
2189
2190         if (id && (GS(id->name) == ID_TE)) {
2191                 if (parent && (GS(parent->name) == ID_MA))
2192                         pr_texture = &((Material *)parent)->pr_texture;
2193                 else if (parent && (GS(parent->name) == ID_WO))
2194                         pr_texture = &((World *)parent)->pr_texture;
2195                 else if (parent && (GS(parent->name) == ID_LA))
2196                         pr_texture = &((Lamp *)parent)->pr_texture;
2197                 else if (parent && (GS(parent->name) == ID_LS))
2198                         pr_texture = &((FreestyleLineStyle *)parent)->pr_texture;
2199
2200                 if (pr_texture) {
2201                         if (*pr_texture == TEX_PR_OTHER)
2202                                 pid = parent;
2203                         else if (*pr_texture == TEX_PR_BOTH)
2204                                 pparent = parent;
2205                 }
2206         }
2207
2208         if (!preview_id || (preview_id[0] == '\0')) {
2209                 /* If no identifier given, generate one from ID type. */
2210                 BLI_snprintf(_preview_id, UI_MAX_NAME_STR, "uiPreview_%s", BKE_idcode_to_name(GS(id->name)));
2211                 preview_id = _preview_id;
2212         }
2213
2214         /* Find or add the uiPreview to the current Region. */
2215         ar = CTX_wm_region(C);
2216         ui_preview = BLI_findstring(&ar->ui_previews, preview_id, offsetof(uiPreview, preview_id));
2217
2218         if (!ui_preview) {
2219                 ui_preview = MEM_callocN(sizeof(uiPreview), "uiPreview");
2220                 BLI_strncpy(ui_preview->preview_id, preview_id, sizeof(ui_preview->preview_id));
2221                 ui_preview->height = (short)(UI_UNIT_Y * 5.6f);
2222                 BLI_addtail(&ar->ui_previews, ui_preview);
2223         }
2224
2225         if (ui_preview->height < UI_UNIT_Y) {
2226                 ui_preview->height = UI_UNIT_Y;
2227         }
2228         else if (ui_preview->height > UI_UNIT_Y * 50) {  /* Rather high upper limit, yet not insane! */
2229                 ui_preview->height = UI_UNIT_Y * 50;
2230         }
2231
2232         /* layout */
2233         block = uiLayoutGetBlock(layout);
2234         row = uiLayoutRow(layout, false);
2235         col = uiLayoutColumn(row, false);
2236         uiLayoutSetKeepAspect(col, true);
2237
2238         /* add preview */
2239         uiDefBut(block, UI_BTYPE_EXTRA, 0, "", 0, 0, UI_UNIT_X * 10, ui_preview->height, pid, 0.0, 0.0, 0, 0, "");
2240         UI_but_func_drawextra_set(block, ED_preview_draw, pparent, slot);
2241         UI_block_func_handle_set(block, do_preview_buttons, NULL);
2242
2243         uiDefIconButS(
2244                 block, UI_BTYPE_GRIP, 0, ICON_GRIP, 0, 0, UI_UNIT_X * 10, (short)(UI_UNIT_Y * 0.3f), &ui_preview->height,
2245                 UI_UNIT_Y, UI_UNIT_Y * 50.0f, 0.0f, 0.0f, "");
2246
2247         /* add buttons */
2248         if (pid && show_buttons) {
2249                 if (GS(pid->name) == ID_MA || (pparent && GS(pparent->name) == ID_MA)) {
2250                         if (GS(pid->name) == ID_MA) ma = (Material *)pid;
2251                         else ma = (Material *)pparent;
2252
2253                         /* Create RNA Pointer */
2254                         RNA_pointer_create(&ma->id, &RNA_Material, ma, &material_ptr);
2255
2256                         col = uiLayoutColumn(row, true);
2257                         uiLayoutSetScaleX(col, 1.5);
2258                         uiItemR(col, &material_ptr, "preview_render_type", UI_ITEM_R_EXPAND, "", ICON_NONE);
2259                 }
2260
2261                 if (pr_texture) {
2262                         /* Create RNA Pointer */
2263                         RNA_pointer_create(id, &RNA_Texture, tex, &texture_ptr);
2264
2265                         uiLayoutRow(layout, true);
2266                         uiDefButS(block, UI_BTYPE_ROW, B_MATPRV, IFACE_("Texture"),  0, 0, UI_UNIT_X * 10, UI_UNIT_Y,
2267                                   pr_texture, 10, TEX_PR_TEXTURE, 0, 0, "");
2268                         if (GS(parent->name) == ID_MA) {
2269                                 uiDefButS(block, UI_BTYPE_ROW, B_MATPRV, IFACE_("Material"),  0, 0, UI_UNIT_X * 10, UI_UNIT_Y,
2270                                           pr_texture, 10, TEX_PR_OTHER, 0, 0, "");
2271                         }
2272                         else if (GS(parent->name) == ID_LA) {
2273                                 uiDefButS(block, UI_BTYPE_ROW, B_MATPRV, IFACE_("Light"),  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