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