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