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