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