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