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