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_alloca.h"
41 #include "BLI_fnmatch.h"
42 #include "BLI_listbase.h"
43 #include "BLI_math.h"
44 #include "BLI_path_util.h"
45 #include "BLI_rect.h"
46 #include "BLI_string.h"
47 #include "BLI_string_search.h"
48 #include "BLI_timecode.h"
49 #include "BLI_utildefines.h"
50 
51 #include "BLF_api.h"
52 #include "BLT_translation.h"
53 
54 #include "BKE_action.h"
55 #include "BKE_colorband.h"
56 #include "BKE_colortools.h"
57 #include "BKE_constraint.h"
58 #include "BKE_context.h"
59 #include "BKE_curveprofile.h"
60 #include "BKE_global.h"
61 #include "BKE_gpencil_modifier.h"
62 #include "BKE_idprop.h"
63 #include "BKE_idtype.h"
64 #include "BKE_layer.h"
65 #include "BKE_lib_id.h"
66 #include "BKE_lib_override.h"
67 #include "BKE_linestyle.h"
68 #include "BKE_main.h"
69 #include "BKE_modifier.h"
70 #include "BKE_object.h"
71 #include "BKE_packedFile.h"
72 #include "BKE_particle.h"
73 #include "BKE_report.h"
74 #include "BKE_scene.h"
75 #include "BKE_screen.h"
76 #include "BKE_shader_fx.h"
77 
78 #include "DEG_depsgraph.h"
79 #include "DEG_depsgraph_build.h"
80 
81 #include "ED_fileselect.h"
82 #include "ED_object.h"
83 #include "ED_render.h"
84 #include "ED_screen.h"
85 #include "ED_undo.h"
86 
87 #include "RNA_access.h"
88 
89 #include "WM_api.h"
90 #include "WM_types.h"
91 
92 #include "BLO_readfile.h"
93 
94 #include "UI_interface.h"
95 #include "UI_interface_icons.h"
96 #include "UI_view2d.h"
97 #include "interface_intern.h"
98 
99 #include "PIL_time.h"
100 
101 /* we may want to make this optional, disable for now. */
102 // #define USE_OP_RESET_BUT
103 
104 /* defines for templateID/TemplateSearch */
105 #define TEMPLATE_SEARCH_TEXTBUT_WIDTH (UI_UNIT_X * 6)
106 #define TEMPLATE_SEARCH_TEXTBUT_HEIGHT UI_UNIT_Y
107 
UI_template_fix_linking(void)108 void UI_template_fix_linking(void)
109 {
110 }
111 
112 /* -------------------------------------------------------------------- */
113 /** \name Header Template
114  * \{ */
115 
uiTemplateHeader(uiLayout * layout,bContext * C)116 void uiTemplateHeader(uiLayout *layout, bContext *C)
117 {
118   uiBlock *block = uiLayoutAbsoluteBlock(layout);
119   ED_area_header_switchbutton(C, block, 0);
120 }
121 
122 /** \} */
123 
124 /* -------------------------------------------------------------------- */
125 /** \name Search Menu Helpers
126  * \{ */
127 
128 /**
129  * Add a block button for the search menu for templateID and templateSearch.
130  */
template_add_button_search_menu(const bContext * C,uiLayout * layout,uiBlock * block,PointerRNA * ptr,PropertyRNA * prop,uiBlockCreateFunc block_func,void * block_argN,const char * const tip,const bool use_previews,const bool editable,const bool live_icon)131 static void template_add_button_search_menu(const bContext *C,
132                                             uiLayout *layout,
133                                             uiBlock *block,
134                                             PointerRNA *ptr,
135                                             PropertyRNA *prop,
136                                             uiBlockCreateFunc block_func,
137                                             void *block_argN,
138                                             const char *const tip,
139                                             const bool use_previews,
140                                             const bool editable,
141                                             const bool live_icon)
142 {
143   const PointerRNA active_ptr = RNA_property_pointer_get(ptr, prop);
144   ID *id = (active_ptr.data && RNA_struct_is_ID(active_ptr.type)) ? active_ptr.data : NULL;
145   const ID *idfrom = ptr->owner_id;
146   const StructRNA *type = active_ptr.type ? active_ptr.type : RNA_property_pointer_type(ptr, prop);
147   uiBut *but;
148 
149   if (use_previews) {
150     ARegion *region = CTX_wm_region(C);
151     /* Ugly tool header exception. */
152     const bool use_big_size = (region->regiontype != RGN_TYPE_TOOL_HEADER);
153     /* Ugly exception for screens here,
154      * drawing their preview in icon size looks ugly/useless */
155     const bool use_preview_icon = use_big_size || (id && (GS(id->name) != ID_SCR));
156     const short width = UI_UNIT_X * (use_big_size ? 6 : 1.6f);
157     const short height = UI_UNIT_Y * (use_big_size ? 6 : 1);
158     uiLayout *col = NULL;
159 
160     if (use_big_size) {
161       /* Assume column layout here. To be more correct, we should check if the layout passed to
162        * template_id is a column one, but this should work well in practice. */
163       col = uiLayoutColumn(layout, true);
164     }
165 
166     but = uiDefBlockButN(block, block_func, block_argN, "", 0, 0, width, height, tip);
167     if (use_preview_icon) {
168       const int icon = id ? ui_id_icon_get(C, id, use_big_size) : RNA_struct_ui_icon(type);
169       ui_def_but_icon(but, icon, UI_HAS_ICON | UI_BUT_ICON_PREVIEW);
170     }
171     else {
172       ui_def_but_icon(but, RNA_struct_ui_icon(type), UI_HAS_ICON);
173       UI_but_drawflag_enable(but, UI_BUT_ICON_LEFT);
174     }
175 
176     if ((idfrom && idfrom->lib) || !editable) {
177       UI_but_flag_enable(but, UI_BUT_DISABLED);
178     }
179     if (use_big_size) {
180       uiLayoutRow(col ? col : layout, true);
181     }
182   }
183   else {
184     but = uiDefBlockButN(block, block_func, block_argN, "", 0, 0, UI_UNIT_X * 1.6, UI_UNIT_Y, tip);
185 
186     if (live_icon) {
187       const int icon = id ? ui_id_icon_get(C, id, false) : RNA_struct_ui_icon(type);
188       ui_def_but_icon(but, icon, UI_HAS_ICON | UI_BUT_ICON_PREVIEW);
189     }
190     else {
191       ui_def_but_icon(but, RNA_struct_ui_icon(type), UI_HAS_ICON);
192     }
193     if (id) {
194       /* default dragging of icon for id browse buttons */
195       UI_but_drag_set_id(but, id);
196     }
197     UI_but_drawflag_enable(but, UI_BUT_ICON_LEFT);
198 
199     if ((idfrom && idfrom->lib) || !editable) {
200       UI_but_flag_enable(but, UI_BUT_DISABLED);
201     }
202   }
203 }
204 
template_common_search_menu(const bContext * C,ARegion * region,uiButSearchUpdateFn search_update_fn,void * search_arg,uiButHandleFunc search_exec_fn,void * active_item,const int preview_rows,const int preview_cols,float scale)205 static uiBlock *template_common_search_menu(const bContext *C,
206                                             ARegion *region,
207                                             uiButSearchUpdateFn search_update_fn,
208                                             void *search_arg,
209                                             uiButHandleFunc search_exec_fn,
210                                             void *active_item,
211                                             const int preview_rows,
212                                             const int preview_cols,
213                                             float scale)
214 {
215   static char search[256];
216   wmWindow *win = CTX_wm_window(C);
217   uiBut *but;
218 
219   /* clear initial search string, then all items show */
220   search[0] = 0;
221 
222   uiBlock *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  */
template_ID_set_property_exec_fn(bContext * C,void * arg_template,void * item)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 
id_search_allows_id(TemplateID * template_ui,const int flag,ID * id,const char * query)332 static bool id_search_allows_id(TemplateID *template_ui, const int flag, ID *id, const char *query)
333 {
334   ID *id_from = template_ui->ptr.owner_id;
335 
336   /* Do self check. */
337   if ((flag & PROP_ID_SELF_CHECK) && id == id_from) {
338     return false;
339   }
340 
341   /* Use filter. */
342   if (RNA_property_type(template_ui->prop) == PROP_POINTER) {
343     PointerRNA ptr;
344     RNA_id_pointer_create(id, &ptr);
345     if (RNA_property_pointer_poll(&template_ui->ptr, template_ui->prop, &ptr) == 0) {
346       return false;
347     }
348   }
349 
350   /* Hide dot-datablocks, but only if filter does not force them visible. */
351   if (U.uiflag & USER_HIDE_DOT) {
352     if ((id->name[2] == '.') && (query[0] != '.')) {
353       return false;
354     }
355   }
356 
357   return true;
358 }
359 
id_search_add(const bContext * C,TemplateID * template_ui,uiSearchItems * items,ID * id)360 static bool id_search_add(const bContext *C, TemplateID *template_ui, uiSearchItems *items, ID *id)
361 {
362   /* +1 is needed because BKE_id_ui_prefix used 3 letter prefix
363    * followed by ID_NAME-2 characters from id->name
364    */
365   char name_ui[MAX_ID_FULL_NAME_UI];
366   int iconid = ui_id_icon_get(C, id, template_ui->preview);
367   const bool use_lib_prefix = template_ui->preview || iconid;
368   const bool has_sep_char = (id->lib != NULL);
369 
370   /* When using previews, the library hint (linked, overridden, missing) is added with a
371    * character prefix, otherwise we can use a icon. */
372   int name_prefix_offset;
373   BKE_id_full_name_ui_prefix_get(name_ui, id, use_lib_prefix, UI_SEP_CHAR, &name_prefix_offset);
374   if (!use_lib_prefix) {
375     iconid = UI_icon_from_library(id);
376   }
377 
378   if (!UI_search_item_add(items,
379                           name_ui,
380                           id,
381                           iconid,
382                           has_sep_char ? UI_BUT_HAS_SEP_CHAR : 0,
383                           name_prefix_offset)) {
384     return false;
385   }
386 
387   return true;
388 }
389 
390 /* ID Search browse menu, do the search */
id_search_cb(const bContext * C,void * arg_template,const char * str,uiSearchItems * items)391 static void id_search_cb(const bContext *C,
392                          void *arg_template,
393                          const char *str,
394                          uiSearchItems *items)
395 {
396   TemplateID *template_ui = (TemplateID *)arg_template;
397   ListBase *lb = template_ui->idlb;
398   const int flag = RNA_property_flag(template_ui->prop);
399 
400   StringSearch *search = BLI_string_search_new();
401 
402   /* ID listbase */
403   LISTBASE_FOREACH (ID *, id, lb) {
404     if (id_search_allows_id(template_ui, flag, id, str)) {
405       BLI_string_search_add(search, id->name + 2, id);
406     }
407   }
408 
409   ID **filtered_ids;
410   int filtered_amount = BLI_string_search_query(search, str, (void ***)&filtered_ids);
411 
412   for (int i = 0; i < filtered_amount; i++) {
413     if (!id_search_add(C, template_ui, items, filtered_ids[i])) {
414       break;
415     }
416   }
417 
418   MEM_freeN(filtered_ids);
419   BLI_string_search_free(search);
420 }
421 
422 /**
423  * Use id tags for filtering.
424  */
id_search_cb_tagged(const bContext * C,void * arg_template,const char * str,uiSearchItems * items)425 static void id_search_cb_tagged(const bContext *C,
426                                 void *arg_template,
427                                 const char *str,
428                                 uiSearchItems *items)
429 {
430   TemplateID *template_ui = (TemplateID *)arg_template;
431   ListBase *lb = template_ui->idlb;
432   const int flag = RNA_property_flag(template_ui->prop);
433 
434   StringSearch *search = BLI_string_search_new();
435 
436   /* ID listbase */
437   LISTBASE_FOREACH (ID *, id, lb) {
438     if (id->tag & LIB_TAG_DOIT) {
439       if (id_search_allows_id(template_ui, flag, id, str)) {
440         BLI_string_search_add(search, id->name + 2, id);
441       }
442       id->tag &= ~LIB_TAG_DOIT;
443     }
444   }
445 
446   ID **filtered_ids;
447   int filtered_amount = BLI_string_search_query(search, str, (void ***)&filtered_ids);
448 
449   for (int i = 0; i < filtered_amount; i++) {
450     if (!id_search_add(C, template_ui, items, filtered_ids[i])) {
451       break;
452     }
453   }
454 
455   MEM_freeN(filtered_ids);
456   BLI_string_search_free(search);
457 }
458 
459 /**
460  * A version of 'id_search_cb' that lists scene objects.
461  */
id_search_cb_objects_from_scene(const bContext * C,void * arg_template,const char * str,uiSearchItems * items)462 static void id_search_cb_objects_from_scene(const bContext *C,
463                                             void *arg_template,
464                                             const char *str,
465                                             uiSearchItems *items)
466 {
467   TemplateID *template_ui = (TemplateID *)arg_template;
468   ListBase *lb = template_ui->idlb;
469   Scene *scene = NULL;
470   ID *id_from = template_ui->ptr.owner_id;
471 
472   if (id_from && GS(id_from->name) == ID_SCE) {
473     scene = (Scene *)id_from;
474   }
475   else {
476     scene = CTX_data_scene(C);
477   }
478 
479   BKE_main_id_flag_listbase(lb, LIB_TAG_DOIT, false);
480 
481   FOREACH_SCENE_OBJECT_BEGIN (scene, ob_iter) {
482     ob_iter->id.tag |= LIB_TAG_DOIT;
483   }
484   FOREACH_SCENE_OBJECT_END;
485   id_search_cb_tagged(C, arg_template, str, items);
486 }
487 
488 /* ID Search browse menu, open */
id_search_menu(bContext * C,ARegion * region,void * arg_litem)489 static uiBlock *id_search_menu(bContext *C, ARegion *region, void *arg_litem)
490 {
491   static TemplateID template_ui;
492   PointerRNA active_item_ptr;
493   void (*id_search_update_fn)(
494       const bContext *, void *, const char *, uiSearchItems *) = id_search_cb;
495 
496   /* arg_litem is malloced, can be freed by parent button */
497   template_ui = *((TemplateID *)arg_litem);
498   active_item_ptr = RNA_property_pointer_get(&template_ui.ptr, template_ui.prop);
499 
500   if (template_ui.filter) {
501     /* Currently only used for objects. */
502     if (template_ui.idcode == ID_OB) {
503       if (template_ui.filter == UI_TEMPLATE_ID_FILTER_AVAILABLE) {
504         id_search_update_fn = id_search_cb_objects_from_scene;
505       }
506     }
507   }
508 
509   return template_common_search_menu(C,
510                                      region,
511                                      id_search_update_fn,
512                                      &template_ui,
513                                      template_ID_set_property_exec_fn,
514                                      active_item_ptr.data,
515                                      template_ui.prv_rows,
516                                      template_ui.prv_cols,
517                                      template_ui.scale);
518 }
519 
520 /** \} */
521 
522 /* -------------------------------------------------------------------- */
523 /** \name ID Template
524  * \{ */
525 
526 /* This is for browsing and editing the ID-blocks used */
527 
528 /* for new/open operators */
UI_context_active_but_prop_get_templateID(bContext * C,PointerRNA * r_ptr,PropertyRNA ** r_prop)529 void UI_context_active_but_prop_get_templateID(bContext *C,
530                                                PointerRNA *r_ptr,
531                                                PropertyRNA **r_prop)
532 {
533   TemplateID *template_ui;
534   uiBut *but = UI_context_active_but_get(C);
535 
536   memset(r_ptr, 0, sizeof(*r_ptr));
537   *r_prop = NULL;
538 
539   if (but && but->func_argN) {
540     template_ui = but->func_argN;
541     *r_ptr = template_ui->ptr;
542     *r_prop = template_ui->prop;
543   }
544 }
545 
template_id_cb(bContext * C,void * arg_litem,void * arg_event)546 static void template_id_cb(bContext *C, void *arg_litem, void *arg_event)
547 {
548   TemplateID *template_ui = (TemplateID *)arg_litem;
549   PointerRNA idptr = RNA_property_pointer_get(&template_ui->ptr, template_ui->prop);
550   ID *id = idptr.data;
551   const int event = POINTER_AS_INT(arg_event);
552   const char *undo_push_label = NULL;
553 
554   switch (event) {
555     case UI_ID_BROWSE:
556     case UI_ID_PIN:
557       RNA_warning("warning, id event %d shouldn't come here", event);
558       break;
559     case UI_ID_OPEN:
560     case UI_ID_ADD_NEW:
561       /* these call UI_context_active_but_prop_get_templateID */
562       break;
563     case UI_ID_DELETE:
564       memset(&idptr, 0, sizeof(idptr));
565       RNA_property_pointer_set(&template_ui->ptr, template_ui->prop, idptr, NULL);
566       RNA_property_update(C, &template_ui->ptr, template_ui->prop);
567 
568       if (id && CTX_wm_window(C)->eventstate->shift) {
569         /* only way to force-remove data (on save) */
570         id_us_clear_real(id);
571         id_fake_user_clear(id);
572         id->us = 0;
573         undo_push_label = "Delete Data-Block";
574       }
575 
576       break;
577     case UI_ID_FAKE_USER:
578       if (id) {
579         if (id->flag & LIB_FAKEUSER) {
580           id_us_plus(id);
581         }
582         else {
583           id_us_min(id);
584         }
585         undo_push_label = "Fake User";
586       }
587       else {
588         return;
589       }
590       break;
591     case UI_ID_LOCAL:
592       if (id) {
593         Main *bmain = CTX_data_main(C);
594         if (CTX_wm_window(C)->eventstate->shift) {
595           if (ID_IS_OVERRIDABLE_LIBRARY(id)) {
596             /* Only remap that specific ID usage to overriding local data-block. */
597             ID *override_id = BKE_lib_override_library_create_from_id(bmain, id, false);
598             if (override_id != NULL) {
599               BKE_main_id_clear_newpoins(bmain);
600 
601               if (GS(override_id->name) == ID_OB) {
602                 Scene *scene = CTX_data_scene(C);
603                 if (!BKE_collection_has_object_recursive(scene->master_collection,
604                                                          (Object *)override_id)) {
605                   BKE_collection_object_add_from(
606                       bmain, scene, (Object *)id, (Object *)override_id);
607                 }
608               }
609 
610               /* Assign new pointer, takes care of updates/notifiers */
611               RNA_id_pointer_create(override_id, &idptr);
612             }
613             undo_push_label = "Make Library Override";
614           }
615         }
616         else {
617           if (BKE_lib_id_make_local(bmain, id, false, 0)) {
618             BKE_main_id_clear_newpoins(bmain);
619 
620             /* reassign to get get proper updates/notifiers */
621             idptr = RNA_property_pointer_get(&template_ui->ptr, template_ui->prop);
622             undo_push_label = "Make Local";
623           }
624         }
625         if (undo_push_label != NULL) {
626           RNA_property_pointer_set(&template_ui->ptr, template_ui->prop, idptr, NULL);
627           RNA_property_update(C, &template_ui->ptr, template_ui->prop);
628         }
629       }
630       break;
631     case UI_ID_OVERRIDE:
632       if (id && ID_IS_OVERRIDE_LIBRARY(id)) {
633         BKE_lib_override_library_free(&id->override_library, true);
634         /* reassign to get get proper updates/notifiers */
635         idptr = RNA_property_pointer_get(&template_ui->ptr, template_ui->prop);
636         RNA_property_pointer_set(&template_ui->ptr, template_ui->prop, idptr, NULL);
637         RNA_property_update(C, &template_ui->ptr, template_ui->prop);
638         undo_push_label = "Override Data-Block";
639       }
640       break;
641     case UI_ID_ALONE:
642       if (id) {
643         const bool do_scene_obj = ((GS(id->name) == ID_OB) &&
644                                    (template_ui->ptr.type == &RNA_LayerObjects));
645 
646         /* make copy */
647         if (do_scene_obj) {
648           Main *bmain = CTX_data_main(C);
649           Scene *scene = CTX_data_scene(C);
650           ED_object_single_user(bmain, scene, (struct Object *)id);
651           WM_event_add_notifier(C, NC_WINDOW, NULL);
652           DEG_relations_tag_update(bmain);
653         }
654         else {
655           Main *bmain = CTX_data_main(C);
656           id_single_user(C, id, &template_ui->ptr, template_ui->prop);
657           DEG_relations_tag_update(bmain);
658         }
659         undo_push_label = "Make Single User";
660       }
661       break;
662 #if 0
663     case UI_ID_AUTO_NAME:
664       break;
665 #endif
666   }
667 
668   if (undo_push_label != NULL) {
669     ED_undo_push(C, undo_push_label);
670   }
671 }
672 
template_id_browse_tip(const StructRNA * type)673 static const char *template_id_browse_tip(const StructRNA *type)
674 {
675   if (type) {
676     switch ((ID_Type)RNA_type_to_ID_code(type)) {
677       case ID_SCE:
678         return N_("Browse Scene to be linked");
679       case ID_OB:
680         return N_("Browse Object to be linked");
681       case ID_ME:
682         return N_("Browse Mesh Data to be linked");
683       case ID_CU:
684         return N_("Browse Curve Data to be linked");
685       case ID_MB:
686         return N_("Browse Metaball Data to be linked");
687       case ID_MA:
688         return N_("Browse Material to be linked");
689       case ID_TE:
690         return N_("Browse Texture to be linked");
691       case ID_IM:
692         return N_("Browse Image to be linked");
693       case ID_LS:
694         return N_("Browse Line Style Data to be linked");
695       case ID_LT:
696         return N_("Browse Lattice Data to be linked");
697       case ID_LA:
698         return N_("Browse Light Data to be linked");
699       case ID_CA:
700         return N_("Browse Camera Data to be linked");
701       case ID_WO:
702         return N_("Browse World Settings to be linked");
703       case ID_SCR:
704         return N_("Choose Screen layout");
705       case ID_TXT:
706         return N_("Browse Text to be linked");
707       case ID_SPK:
708         return N_("Browse Speaker Data to be linked");
709       case ID_SO:
710         return N_("Browse Sound to be linked");
711       case ID_AR:
712         return N_("Browse Armature data to be linked");
713       case ID_AC:
714         return N_("Browse Action to be linked");
715       case ID_NT:
716         return N_("Browse Node Tree to be linked");
717       case ID_BR:
718         return N_("Browse Brush to be linked");
719       case ID_PA:
720         return N_("Browse Particle Settings to be linked");
721       case ID_GD:
722         return N_("Browse Grease Pencil Data to be linked");
723       case ID_MC:
724         return N_("Browse Movie Clip to be linked");
725       case ID_MSK:
726         return N_("Browse Mask to be linked");
727       case ID_PAL:
728         return N_("Browse Palette Data to be linked");
729       case ID_PC:
730         return N_("Browse Paint Curve Data to be linked");
731       case ID_CF:
732         return N_("Browse Cache Files to be linked");
733       case ID_WS:
734         return N_("Browse Workspace to be linked");
735       case ID_LP:
736         return N_("Browse LightProbe to be linked");
737       case ID_HA:
738         return N_("Browse Hair Data to be linked");
739       case ID_PT:
740         return N_("Browse Point Cloud Data to be linked");
741       case ID_VO:
742         return N_("Browse Volume Data to be linked");
743       case ID_SIM:
744         return N_("Browse Simulation to be linked");
745 
746       /* Use generic text. */
747       case ID_LI:
748       case ID_IP:
749       case ID_KE:
750       case ID_VF:
751       case ID_GR:
752       case ID_WM:
753         break;
754     }
755   }
756   return N_("Browse ID data to be linked");
757 }
758 
759 /**
760  * \return a type-based i18n context, needed e.g. by "New" button.
761  * In most languages, this adjective takes different form based on gender of type name...
762  */
763 #ifdef WITH_INTERNATIONAL
template_id_context(StructRNA * type)764 static const char *template_id_context(StructRNA *type)
765 {
766   if (type) {
767     return BKE_idtype_idcode_to_translation_context(RNA_type_to_ID_code(type));
768   }
769   return BLT_I18NCONTEXT_DEFAULT;
770 }
771 #else
772 #  define template_id_context(type) 0
773 #endif
774 
template_id_def_new_but(uiBlock * block,const ID * id,const TemplateID * template_ui,StructRNA * type,const char * const newop,const bool editable,const bool id_open,const bool use_tab_but,int but_height)775 static uiBut *template_id_def_new_but(uiBlock *block,
776                                       const ID *id,
777                                       const TemplateID *template_ui,
778                                       StructRNA *type,
779                                       const char *const newop,
780                                       const bool editable,
781                                       const bool id_open,
782                                       const bool use_tab_but,
783                                       int but_height)
784 {
785   ID *idfrom = template_ui->ptr.owner_id;
786   uiBut *but;
787   const int w = id ? UI_UNIT_X : id_open ? UI_UNIT_X * 3 : UI_UNIT_X * 6;
788   const int but_type = use_tab_but ? UI_BTYPE_TAB : UI_BTYPE_BUT;
789 
790   /* i18n markup, does nothing! */
791   BLT_I18N_MSGID_MULTI_CTXT("New",
792                             BLT_I18NCONTEXT_DEFAULT,
793                             BLT_I18NCONTEXT_ID_SCENE,
794                             BLT_I18NCONTEXT_ID_OBJECT,
795                             BLT_I18NCONTEXT_ID_MESH,
796                             BLT_I18NCONTEXT_ID_CURVE,
797                             BLT_I18NCONTEXT_ID_METABALL,
798                             BLT_I18NCONTEXT_ID_MATERIAL,
799                             BLT_I18NCONTEXT_ID_TEXTURE,
800                             BLT_I18NCONTEXT_ID_IMAGE,
801                             BLT_I18NCONTEXT_ID_LATTICE,
802                             BLT_I18NCONTEXT_ID_LIGHT,
803                             BLT_I18NCONTEXT_ID_CAMERA,
804                             BLT_I18NCONTEXT_ID_WORLD,
805                             BLT_I18NCONTEXT_ID_SCREEN,
806                             BLT_I18NCONTEXT_ID_TEXT, );
807   BLT_I18N_MSGID_MULTI_CTXT("New",
808                             BLT_I18NCONTEXT_ID_SPEAKER,
809                             BLT_I18NCONTEXT_ID_SOUND,
810                             BLT_I18NCONTEXT_ID_ARMATURE,
811                             BLT_I18NCONTEXT_ID_ACTION,
812                             BLT_I18NCONTEXT_ID_NODETREE,
813                             BLT_I18NCONTEXT_ID_BRUSH,
814                             BLT_I18NCONTEXT_ID_PARTICLESETTINGS,
815                             BLT_I18NCONTEXT_ID_GPENCIL,
816                             BLT_I18NCONTEXT_ID_FREESTYLELINESTYLE,
817                             BLT_I18NCONTEXT_ID_WORKSPACE,
818                             BLT_I18NCONTEXT_ID_LIGHTPROBE,
819                             BLT_I18NCONTEXT_ID_HAIR,
820                             BLT_I18NCONTEXT_ID_POINTCLOUD,
821                             BLT_I18NCONTEXT_ID_VOLUME,
822                             BLT_I18NCONTEXT_ID_SIMULATION, );
823   /* Note: BLT_I18N_MSGID_MULTI_CTXT takes a maximum number of parameters,
824    * check the definition to see if a new call must be added when the limit
825    * is exceeded. */
826 
827   if (newop) {
828     but = uiDefIconTextButO(block,
829                             but_type,
830                             newop,
831                             WM_OP_INVOKE_DEFAULT,
832                             (id && !use_tab_but) ? ICON_DUPLICATE : ICON_ADD,
833                             (id) ? "" : CTX_IFACE_(template_id_context(type), "New"),
834                             0,
835                             0,
836                             w,
837                             but_height,
838                             NULL);
839     UI_but_funcN_set(
840         but, template_id_cb, MEM_dupallocN(template_ui), POINTER_FROM_INT(UI_ID_ADD_NEW));
841   }
842   else {
843     but = uiDefIconTextBut(block,
844                            but_type,
845                            0,
846                            (id && !use_tab_but) ? ICON_DUPLICATE : ICON_ADD,
847                            (id) ? "" : CTX_IFACE_(template_id_context(type), "New"),
848                            0,
849                            0,
850                            w,
851                            but_height,
852                            NULL,
853                            0,
854                            0,
855                            0,
856                            0,
857                            NULL);
858     UI_but_funcN_set(
859         but, template_id_cb, MEM_dupallocN(template_ui), POINTER_FROM_INT(UI_ID_ADD_NEW));
860   }
861 
862   if ((idfrom && idfrom->lib) || !editable) {
863     UI_but_flag_enable(but, UI_BUT_DISABLED);
864   }
865 
866 #ifndef WITH_INTERNATIONAL
867   UNUSED_VARS(type);
868 #endif
869 
870   return but;
871 }
872 
template_ID(const bContext * C,uiLayout * layout,TemplateID * template_ui,StructRNA * type,int flag,const char * newop,const char * openop,const char * unlinkop,const char * text,const bool live_icon,const bool hide_buttons)873 static void template_ID(const bContext *C,
874                         uiLayout *layout,
875                         TemplateID *template_ui,
876                         StructRNA *type,
877                         int flag,
878                         const char *newop,
879                         const char *openop,
880                         const char *unlinkop,
881                         const char *text,
882                         const bool live_icon,
883                         const bool hide_buttons)
884 {
885   uiBut *but;
886   uiBlock *block;
887   PointerRNA idptr;
888   // ListBase *lb; // UNUSED
889   ID *id, *idfrom;
890   const bool editable = RNA_property_editable(&template_ui->ptr, template_ui->prop);
891   const bool use_previews = template_ui->preview = (flag & UI_ID_PREVIEWS) != 0;
892 
893   idptr = RNA_property_pointer_get(&template_ui->ptr, template_ui->prop);
894   id = idptr.data;
895   idfrom = template_ui->ptr.owner_id;
896   // lb = template_ui->idlb;
897 
898   block = uiLayoutGetBlock(layout);
899   UI_block_align_begin(block);
900 
901   if (idptr.type) {
902     type = idptr.type;
903   }
904 
905   if (text) {
906     /* Add label resepecting the separated layout property split state. */
907     uiItemL_respect_property_split(layout, text, ICON_NONE);
908   }
909 
910   if (flag & UI_ID_BROWSE) {
911     template_add_button_search_menu(C,
912                                     layout,
913                                     block,
914                                     &template_ui->ptr,
915                                     template_ui->prop,
916                                     id_search_menu,
917                                     MEM_dupallocN(template_ui),
918                                     TIP_(template_id_browse_tip(type)),
919                                     use_previews,
920                                     editable,
921                                     live_icon);
922   }
923 
924   /* text button with name */
925   if (id) {
926     char name[UI_MAX_NAME_STR];
927     const bool user_alert = (id->us <= 0);
928 
929     // text_idbutton(id, name);
930     name[0] = '\0';
931     but = uiDefButR(block,
932                     UI_BTYPE_TEXT,
933                     0,
934                     name,
935                     0,
936                     0,
937                     TEMPLATE_SEARCH_TEXTBUT_WIDTH,
938                     TEMPLATE_SEARCH_TEXTBUT_HEIGHT,
939                     &idptr,
940                     "name",
941                     -1,
942                     0,
943                     0,
944                     -1,
945                     -1,
946                     RNA_struct_ui_description(type));
947     UI_but_funcN_set(
948         but, template_id_cb, MEM_dupallocN(template_ui), POINTER_FROM_INT(UI_ID_RENAME));
949     if (user_alert) {
950       UI_but_flag_enable(but, UI_BUT_REDALERT);
951     }
952 
953     if (id->lib) {
954       if (id->tag & LIB_TAG_INDIRECT) {
955         but = uiDefIconBut(block,
956                            UI_BTYPE_BUT,
957                            0,
958                            ICON_LIBRARY_DATA_INDIRECT,
959                            0,
960                            0,
961                            UI_UNIT_X,
962                            UI_UNIT_Y,
963                            NULL,
964                            0,
965                            0,
966                            0,
967                            0,
968                            TIP_("Indirect library data-block, cannot change"));
969         UI_but_flag_enable(but, UI_BUT_DISABLED);
970       }
971       else {
972         const bool disabled = (!BKE_lib_id_make_local(CTX_data_main(C), id, true /* test */, 0) ||
973                                (idfrom && idfrom->lib));
974         but = uiDefIconBut(block,
975                            UI_BTYPE_BUT,
976                            0,
977                            ICON_LIBRARY_DATA_DIRECT,
978                            0,
979                            0,
980                            UI_UNIT_X,
981                            UI_UNIT_Y,
982                            NULL,
983                            0,
984                            0,
985                            0,
986                            0,
987                            TIP_("Direct linked library data-block, click to make local, "
988                                 "Shift + Click to create a library override"));
989         if (disabled) {
990           UI_but_flag_enable(but, UI_BUT_DISABLED);
991         }
992         else {
993           UI_but_funcN_set(
994               but, template_id_cb, MEM_dupallocN(template_ui), POINTER_FROM_INT(UI_ID_LOCAL));
995         }
996       }
997     }
998     else if (ID_IS_OVERRIDE_LIBRARY(id)) {
999       but = uiDefIconBut(block,
1000                          UI_BTYPE_BUT,
1001                          0,
1002                          ICON_LIBRARY_DATA_OVERRIDE,
1003                          0,
1004                          0,
1005                          UI_UNIT_X,
1006                          UI_UNIT_Y,
1007                          NULL,
1008                          0,
1009                          0,
1010                          0,
1011                          0,
1012                          TIP_("Library override of linked data-block, click to make fully local"));
1013       UI_but_funcN_set(
1014           but, template_id_cb, MEM_dupallocN(template_ui), POINTER_FROM_INT(UI_ID_OVERRIDE));
1015     }
1016 
1017     if ((ID_REAL_USERS(id) > 1) && (hide_buttons == false)) {
1018       char numstr[32];
1019       short numstr_len;
1020 
1021       numstr_len = BLI_snprintf(numstr, sizeof(numstr), "%d", ID_REAL_USERS(id));
1022 
1023       but = uiDefBut(
1024           block,
1025           UI_BTYPE_BUT,
1026           0,
1027           numstr,
1028           0,
1029           0,
1030           numstr_len * 0.2f * UI_UNIT_X + UI_UNIT_X,
1031           UI_UNIT_Y,
1032           NULL,
1033           0,
1034           0,
1035           0,
1036           0,
1037           TIP_("Display number of users of this data (click to make a single-user copy)"));
1038       but->flag |= UI_BUT_UNDO;
1039 
1040       UI_but_funcN_set(
1041           but, template_id_cb, MEM_dupallocN(template_ui), POINTER_FROM_INT(UI_ID_ALONE));
1042       if ((!BKE_id_copy_is_allowed(id)) || (idfrom && idfrom->lib) || (!editable) ||
1043           /* object in editmode - don't change data */
1044           (idfrom && GS(idfrom->name) == ID_OB && (((Object *)idfrom)->mode & OB_MODE_EDIT))) {
1045         UI_but_flag_enable(but, UI_BUT_DISABLED);
1046       }
1047     }
1048 
1049     if (user_alert) {
1050       UI_but_flag_enable(but, UI_BUT_REDALERT);
1051     }
1052 
1053     if (id->lib == NULL && !(ELEM(GS(id->name), ID_GR, ID_SCE, ID_SCR, ID_TXT, ID_OB, ID_WS)) &&
1054         (hide_buttons == false)) {
1055       uiDefIconButR(block,
1056                     UI_BTYPE_ICON_TOGGLE,
1057                     0,
1058                     ICON_FAKE_USER_OFF,
1059                     0,
1060                     0,
1061                     UI_UNIT_X,
1062                     UI_UNIT_Y,
1063                     &idptr,
1064                     "use_fake_user",
1065                     -1,
1066                     0,
1067                     0,
1068                     -1,
1069                     -1,
1070                     NULL);
1071     }
1072   }
1073 
1074   if ((flag & UI_ID_ADD_NEW) && (hide_buttons == false)) {
1075     template_id_def_new_but(
1076         block, id, template_ui, type, newop, editable, flag & UI_ID_OPEN, false, UI_UNIT_X);
1077   }
1078 
1079   /* Due to space limit in UI - skip the "open" icon for packed data, and allow to unpack.
1080    * Only for images, sound and fonts */
1081   if (id && BKE_packedfile_id_check(id)) {
1082     but = uiDefIconButO(block,
1083                         UI_BTYPE_BUT,
1084                         "FILE_OT_unpack_item",
1085                         WM_OP_INVOKE_REGION_WIN,
1086                         ICON_PACKAGE,
1087                         0,
1088                         0,
1089                         UI_UNIT_X,
1090                         UI_UNIT_Y,
1091                         TIP_("Packed File, click to unpack"));
1092     UI_but_operator_ptr_get(but);
1093 
1094     RNA_string_set(but->opptr, "id_name", id->name + 2);
1095     RNA_int_set(but->opptr, "id_type", GS(id->name));
1096   }
1097   else if (flag & UI_ID_OPEN) {
1098     const int w = id ? UI_UNIT_X : (flag & UI_ID_ADD_NEW) ? UI_UNIT_X * 3 : UI_UNIT_X * 6;
1099 
1100     if (openop) {
1101       but = uiDefIconTextButO(block,
1102                               UI_BTYPE_BUT,
1103                               openop,
1104                               WM_OP_INVOKE_DEFAULT,
1105                               ICON_FILEBROWSER,
1106                               (id) ? "" : IFACE_("Open"),
1107                               0,
1108                               0,
1109                               w,
1110                               UI_UNIT_Y,
1111                               NULL);
1112       UI_but_funcN_set(
1113           but, template_id_cb, MEM_dupallocN(template_ui), POINTER_FROM_INT(UI_ID_OPEN));
1114     }
1115     else {
1116       but = uiDefIconTextBut(block,
1117                              UI_BTYPE_BUT,
1118                              0,
1119                              ICON_FILEBROWSER,
1120                              (id) ? "" : IFACE_("Open"),
1121                              0,
1122                              0,
1123                              w,
1124                              UI_UNIT_Y,
1125                              NULL,
1126                              0,
1127                              0,
1128                              0,
1129                              0,
1130                              NULL);
1131       UI_but_funcN_set(
1132           but, template_id_cb, MEM_dupallocN(template_ui), POINTER_FROM_INT(UI_ID_OPEN));
1133     }
1134 
1135     if ((idfrom && idfrom->lib) || !editable) {
1136       UI_but_flag_enable(but, UI_BUT_DISABLED);
1137     }
1138   }
1139 
1140   /* delete button */
1141   /* don't use RNA_property_is_unlink here */
1142   if (id && (flag & UI_ID_DELETE) && (hide_buttons == false)) {
1143     /* allow unlink if 'unlinkop' is passed, even when 'PROP_NEVER_UNLINK' is set */
1144     but = NULL;
1145 
1146     if (unlinkop) {
1147       but = uiDefIconButO(block,
1148                           UI_BTYPE_BUT,
1149                           unlinkop,
1150                           WM_OP_INVOKE_DEFAULT,
1151                           ICON_X,
1152                           0,
1153                           0,
1154                           UI_UNIT_X,
1155                           UI_UNIT_Y,
1156                           NULL);
1157       /* so we can access the template from operators, font unlinking needs this */
1158       UI_but_funcN_set(but, NULL, MEM_dupallocN(template_ui), NULL);
1159     }
1160     else {
1161       if ((RNA_property_flag(template_ui->prop) & PROP_NEVER_UNLINK) == 0) {
1162         but = uiDefIconBut(
1163             block,
1164             UI_BTYPE_BUT,
1165             0,
1166             ICON_X,
1167             0,
1168             0,
1169             UI_UNIT_X,
1170             UI_UNIT_Y,
1171             NULL,
1172             0,
1173             0,
1174             0,
1175             0,
1176             TIP_("Unlink data-block "
1177                  "(Shift + Click to set users to zero, data will then not be saved)"));
1178         UI_but_funcN_set(
1179             but, template_id_cb, MEM_dupallocN(template_ui), POINTER_FROM_INT(UI_ID_DELETE));
1180 
1181         if (RNA_property_flag(template_ui->prop) & PROP_NEVER_NULL) {
1182           UI_but_flag_enable(but, UI_BUT_DISABLED);
1183         }
1184       }
1185     }
1186 
1187     if (but) {
1188       if ((idfrom && idfrom->lib) || !editable) {
1189         UI_but_flag_enable(but, UI_BUT_DISABLED);
1190       }
1191     }
1192   }
1193 
1194   if (template_ui->idcode == ID_TE) {
1195     uiTemplateTextureShow(layout, C, &template_ui->ptr, template_ui->prop);
1196   }
1197   UI_block_align_end(block);
1198 }
1199 
UI_context_active_but_get_tab_ID(bContext * C)1200 ID *UI_context_active_but_get_tab_ID(bContext *C)
1201 {
1202   uiBut *but = UI_context_active_but_get(C);
1203 
1204   if (but && but->type == UI_BTYPE_TAB) {
1205     return but->custom_data;
1206   }
1207   return NULL;
1208 }
1209 
template_ID_tabs(const bContext * C,uiLayout * layout,TemplateID * template,StructRNA * type,int flag,const char * newop,const char * menu)1210 static void template_ID_tabs(const bContext *C,
1211                              uiLayout *layout,
1212                              TemplateID *template,
1213                              StructRNA *type,
1214                              int flag,
1215                              const char *newop,
1216                              const char *menu)
1217 {
1218   const ARegion *region = CTX_wm_region(C);
1219   const PointerRNA active_ptr = RNA_property_pointer_get(&template->ptr, template->prop);
1220   MenuType *mt = menu ? WM_menutype_find(menu, false) : NULL;
1221 
1222   const int but_align = ui_but_align_opposite_to_area_align_get(region);
1223   const int but_height = UI_UNIT_Y * 1.1;
1224 
1225   uiBlock *block = uiLayoutGetBlock(layout);
1226   const uiStyle *style = UI_style_get_dpi();
1227 
1228   ListBase ordered;
1229   BKE_id_ordered_list(&ordered, template->idlb);
1230 
1231   LISTBASE_FOREACH (LinkData *, link, &ordered) {
1232     ID *id = link->data;
1233     const int name_width = UI_fontstyle_string_width(&style->widget, id->name + 2);
1234     const int but_width = name_width + UI_UNIT_X;
1235 
1236     uiButTab *tab = (uiButTab *)uiDefButR_prop(block,
1237                                                UI_BTYPE_TAB,
1238                                                0,
1239                                                id->name + 2,
1240                                                0,
1241                                                0,
1242                                                but_width,
1243                                                but_height,
1244                                                &template->ptr,
1245                                                template->prop,
1246                                                0,
1247                                                0.0f,
1248                                                sizeof(id->name) - 2,
1249                                                0.0f,
1250                                                0.0f,
1251                                                "");
1252     UI_but_funcN_set(&tab->but, template_ID_set_property_exec_fn, MEM_dupallocN(template), id);
1253     tab->but.custom_data = (void *)id;
1254     tab->but.dragpoin = id;
1255     tab->menu = mt;
1256 
1257     UI_but_drawflag_enable(&tab->but, but_align);
1258   }
1259 
1260   BLI_freelistN(&ordered);
1261 
1262   if (flag & UI_ID_ADD_NEW) {
1263     const bool editable = RNA_property_editable(&template->ptr, template->prop);
1264     uiBut *but;
1265 
1266     if (active_ptr.type) {
1267       type = active_ptr.type;
1268     }
1269 
1270     but = template_id_def_new_but(block,
1271                                   active_ptr.data,
1272                                   template,
1273                                   type,
1274                                   newop,
1275                                   editable,
1276                                   flag & UI_ID_OPEN,
1277                                   true,
1278                                   but_height);
1279     UI_but_drawflag_enable(but, but_align);
1280   }
1281 }
1282 
ui_template_id(uiLayout * layout,const bContext * C,PointerRNA * ptr,const char * propname,const char * newop,const char * openop,const char * unlinkop,const char * menu,const char * text,int flag,int prv_rows,int prv_cols,int filter,bool use_tabs,float scale,const bool live_icon,const bool hide_buttons)1283 static void ui_template_id(uiLayout *layout,
1284                            const bContext *C,
1285                            PointerRNA *ptr,
1286                            const char *propname,
1287                            const char *newop,
1288                            const char *openop,
1289                            const char *unlinkop,
1290                            /* Only respected by tabs (use_tabs). */
1291                            const char *menu,
1292                            const char *text,
1293                            int flag,
1294                            int prv_rows,
1295                            int prv_cols,
1296                            int filter,
1297                            bool use_tabs,
1298                            float scale,
1299                            const bool live_icon,
1300                            const bool hide_buttons)
1301 {
1302   TemplateID *template_ui;
1303   PropertyRNA *prop;
1304   StructRNA *type;
1305   short idcode;
1306 
1307   prop = RNA_struct_find_property(ptr, propname);
1308 
1309   if (!prop || RNA_property_type(prop) != PROP_POINTER) {
1310     RNA_warning("pointer property not found: %s.%s", RNA_struct_identifier(ptr->type), propname);
1311     return;
1312   }
1313 
1314   template_ui = MEM_callocN(sizeof(TemplateID), "TemplateID");
1315   template_ui->ptr = *ptr;
1316   template_ui->prop = prop;
1317   template_ui->prv_rows = prv_rows;
1318   template_ui->prv_cols = prv_cols;
1319   template_ui->scale = scale;
1320 
1321   if ((flag & UI_ID_PIN) == 0) {
1322     template_ui->filter = filter;
1323   }
1324   else {
1325     template_ui->filter = 0;
1326   }
1327 
1328   if (newop) {
1329     flag |= UI_ID_ADD_NEW;
1330   }
1331   if (openop) {
1332     flag |= UI_ID_OPEN;
1333   }
1334 
1335   type = RNA_property_pointer_type(ptr, prop);
1336   idcode = RNA_type_to_ID_code(type);
1337   template_ui->idcode = idcode;
1338   template_ui->idlb = which_libbase(CTX_data_main(C), idcode);
1339 
1340   /* create UI elements for this template
1341    * - template_ID makes a copy of the template data and assigns it to the relevant buttons
1342    */
1343   if (template_ui->idlb) {
1344     if (use_tabs) {
1345       layout = uiLayoutRow(layout, true);
1346       template_ID_tabs(C, layout, template_ui, type, flag, newop, menu);
1347     }
1348     else {
1349       layout = uiLayoutRow(layout, true);
1350       template_ID(C,
1351                   layout,
1352                   template_ui,
1353                   type,
1354                   flag,
1355                   newop,
1356                   openop,
1357                   unlinkop,
1358                   text,
1359                   live_icon,
1360                   hide_buttons);
1361     }
1362   }
1363 
1364   MEM_freeN(template_ui);
1365 }
1366 
uiTemplateID(uiLayout * layout,const bContext * C,PointerRNA * ptr,const char * propname,const char * newop,const char * openop,const char * unlinkop,int filter,const bool live_icon,const char * text)1367 void uiTemplateID(uiLayout *layout,
1368                   const bContext *C,
1369                   PointerRNA *ptr,
1370                   const char *propname,
1371                   const char *newop,
1372                   const char *openop,
1373                   const char *unlinkop,
1374                   int filter,
1375                   const bool live_icon,
1376                   const char *text)
1377 {
1378   ui_template_id(layout,
1379                  C,
1380                  ptr,
1381                  propname,
1382                  newop,
1383                  openop,
1384                  unlinkop,
1385                  NULL,
1386                  text,
1387                  UI_ID_BROWSE | UI_ID_RENAME | UI_ID_DELETE,
1388                  0,
1389                  0,
1390                  filter,
1391                  false,
1392                  1.0f,
1393                  live_icon,
1394                  false);
1395 }
1396 
uiTemplateIDBrowse(uiLayout * layout,bContext * C,PointerRNA * ptr,const char * propname,const char * newop,const char * openop,const char * unlinkop,int filter,const char * text)1397 void uiTemplateIDBrowse(uiLayout *layout,
1398                         bContext *C,
1399                         PointerRNA *ptr,
1400                         const char *propname,
1401                         const char *newop,
1402                         const char *openop,
1403                         const char *unlinkop,
1404                         int filter,
1405                         const char *text)
1406 {
1407   ui_template_id(layout,
1408                  C,
1409                  ptr,
1410                  propname,
1411                  newop,
1412                  openop,
1413                  unlinkop,
1414                  NULL,
1415                  text,
1416                  UI_ID_BROWSE | UI_ID_RENAME,
1417                  0,
1418                  0,
1419                  filter,
1420                  false,
1421                  1.0f,
1422                  false,
1423                  false);
1424 }
1425 
uiTemplateIDPreview(uiLayout * layout,bContext * C,PointerRNA * ptr,const char * propname,const char * newop,const char * openop,const char * unlinkop,int rows,int cols,int filter,const bool hide_buttons)1426 void uiTemplateIDPreview(uiLayout *layout,
1427                          bContext *C,
1428                          PointerRNA *ptr,
1429                          const char *propname,
1430                          const char *newop,
1431                          const char *openop,
1432                          const char *unlinkop,
1433                          int rows,
1434                          int cols,
1435                          int filter,
1436                          const bool hide_buttons)
1437 {
1438   ui_template_id(layout,
1439                  C,
1440                  ptr,
1441                  propname,
1442                  newop,
1443                  openop,
1444                  unlinkop,
1445                  NULL,
1446                  NULL,
1447                  UI_ID_BROWSE | UI_ID_RENAME | UI_ID_DELETE | UI_ID_PREVIEWS,
1448                  rows,
1449                  cols,
1450                  filter,
1451                  false,
1452                  1.0f,
1453                  false,
1454                  hide_buttons);
1455 }
1456 
uiTemplateGpencilColorPreview(uiLayout * layout,bContext * C,PointerRNA * ptr,const char * propname,int rows,int cols,float scale,int filter)1457 void uiTemplateGpencilColorPreview(uiLayout *layout,
1458                                    bContext *C,
1459                                    PointerRNA *ptr,
1460                                    const char *propname,
1461                                    int rows,
1462                                    int cols,
1463                                    float scale,
1464                                    int filter)
1465 {
1466   ui_template_id(layout,
1467                  C,
1468                  ptr,
1469                  propname,
1470                  NULL,
1471                  NULL,
1472                  NULL,
1473                  NULL,
1474                  NULL,
1475                  UI_ID_BROWSE | UI_ID_PREVIEWS | UI_ID_DELETE,
1476                  rows,
1477                  cols,
1478                  filter,
1479                  false,
1480                  scale < 0.5f ? 0.5f : scale,
1481                  false,
1482                  false);
1483 }
1484 
1485 /**
1486  * Version of #uiTemplateID using tabs.
1487  */
uiTemplateIDTabs(uiLayout * layout,bContext * C,PointerRNA * ptr,const char * propname,const char * newop,const char * menu,int filter)1488 void uiTemplateIDTabs(uiLayout *layout,
1489                       bContext *C,
1490                       PointerRNA *ptr,
1491                       const char *propname,
1492                       const char *newop,
1493                       const char *menu,
1494                       int filter)
1495 {
1496   ui_template_id(layout,
1497                  C,
1498                  ptr,
1499                  propname,
1500                  newop,
1501                  NULL,
1502                  NULL,
1503                  menu,
1504                  NULL,
1505                  UI_ID_BROWSE | UI_ID_RENAME,
1506                  0,
1507                  0,
1508                  filter,
1509                  true,
1510                  1.0f,
1511                  false,
1512                  false);
1513 }
1514 
1515 /** \} */
1516 
1517 /* -------------------------------------------------------------------- */
1518 /** \name ID Chooser Template
1519  * \{ */
1520 
1521 /**
1522  * This is for selecting the type of ID-block to use,
1523  * and then from the relevant type choosing the block to use.
1524  *
1525  * \param propname: property identifier for property that ID-pointer gets stored to.
1526  * \param proptypename: property identifier for property
1527  * used to determine the type of ID-pointer that can be used.
1528  */
uiTemplateAnyID(uiLayout * layout,PointerRNA * ptr,const char * propname,const char * proptypename,const char * text)1529 void uiTemplateAnyID(uiLayout *layout,
1530                      PointerRNA *ptr,
1531                      const char *propname,
1532                      const char *proptypename,
1533                      const char *text)
1534 {
1535   PropertyRNA *propID, *propType;
1536   uiLayout *split, *row, *sub;
1537 
1538   /* get properties... */
1539   propID = RNA_struct_find_property(ptr, propname);
1540   propType = RNA_struct_find_property(ptr, proptypename);
1541 
1542   if (!propID || RNA_property_type(propID) != PROP_POINTER) {
1543     RNA_warning("pointer property not found: %s.%s", RNA_struct_identifier(ptr->type), propname);
1544     return;
1545   }
1546   if (!propType || RNA_property_type(propType) != PROP_ENUM) {
1547     RNA_warning(
1548         "pointer-type property not found: %s.%s", RNA_struct_identifier(ptr->type), proptypename);
1549     return;
1550   }
1551 
1552   /* Start drawing UI Elements using standard defines */
1553 
1554   /* NOTE: split amount here needs to be synced with normal labels */
1555   split = uiLayoutSplit(layout, 0.33f, false);
1556 
1557   /* FIRST PART ................................................ */
1558   row = uiLayoutRow(split, false);
1559 
1560   /* Label - either use the provided text, or will become "ID-Block:" */
1561   if (text) {
1562     if (text[0]) {
1563       uiItemL(row, text, ICON_NONE);
1564     }
1565   }
1566   else {
1567     uiItemL(row, IFACE_("ID-Block:"), ICON_NONE);
1568   }
1569 
1570   /* SECOND PART ................................................ */
1571   row = uiLayoutRow(split, true);
1572 
1573   /* ID-Type Selector - just have a menu of icons */
1574 
1575   /* HACK: special group just for the enum,
1576    * otherwise we get ugly layout with text included too... */
1577   sub = uiLayoutRow(row, true);
1578   uiLayoutSetAlignment(sub, UI_LAYOUT_ALIGN_LEFT);
1579 
1580   uiItemFullR(sub, ptr, propType, 0, 0, UI_ITEM_R_ICON_ONLY, "", ICON_NONE);
1581 
1582   /* ID-Block Selector - just use pointer widget... */
1583 
1584   /* HACK: special group to counteract the effects of the previous enum,
1585    * which now pushes everything too far right. */
1586   sub = uiLayoutRow(row, true);
1587   uiLayoutSetAlignment(sub, UI_LAYOUT_ALIGN_EXPAND);
1588 
1589   uiItemFullR(sub, ptr, propID, 0, 0, 0, "", ICON_NONE);
1590 }
1591 
1592 /** \} */
1593 
1594 /* -------------------------------------------------------------------- */
1595 /** \name Search Template
1596  * \{ */
1597 
1598 typedef struct TemplateSearch {
1599   uiRNACollectionSearch search_data;
1600 
1601   bool use_previews;
1602   int preview_rows, preview_cols;
1603 } TemplateSearch;
1604 
template_search_exec_fn(bContext * C,void * arg_template,void * item)1605 static void template_search_exec_fn(bContext *C, void *arg_template, void *item)
1606 {
1607   TemplateSearch *template_search = arg_template;
1608   uiRNACollectionSearch *coll_search = &template_search->search_data;
1609   StructRNA *type = RNA_property_pointer_type(&coll_search->target_ptr, coll_search->target_prop);
1610   PointerRNA item_ptr;
1611 
1612   RNA_pointer_create(NULL, type, item, &item_ptr);
1613   RNA_property_pointer_set(&coll_search->target_ptr, coll_search->target_prop, item_ptr, NULL);
1614   RNA_property_update(C, &coll_search->target_ptr, coll_search->target_prop);
1615 }
1616 
template_search_menu(bContext * C,ARegion * region,void * arg_template)1617 static uiBlock *template_search_menu(bContext *C, ARegion *region, void *arg_template)
1618 {
1619   static TemplateSearch template_search;
1620 
1621   /* arg_template is malloced, can be freed by parent button */
1622   template_search = *((TemplateSearch *)arg_template);
1623   PointerRNA active_ptr = RNA_property_pointer_get(&template_search.search_data.target_ptr,
1624                                                    template_search.search_data.target_prop);
1625 
1626   return template_common_search_menu(C,
1627                                      region,
1628                                      ui_rna_collection_search_update_fn,
1629                                      &template_search,
1630                                      template_search_exec_fn,
1631                                      active_ptr.data,
1632                                      template_search.preview_rows,
1633                                      template_search.preview_cols,
1634                                      1.0f);
1635 }
1636 
template_search_add_button_searchmenu(const bContext * C,uiLayout * layout,uiBlock * block,TemplateSearch * template_search,const bool editable,const bool live_icon)1637 static void template_search_add_button_searchmenu(const bContext *C,
1638                                                   uiLayout *layout,
1639                                                   uiBlock *block,
1640                                                   TemplateSearch *template_search,
1641                                                   const bool editable,
1642                                                   const bool live_icon)
1643 {
1644   const char *ui_description = RNA_property_ui_description(
1645       template_search->search_data.target_prop);
1646 
1647   template_add_button_search_menu(C,
1648                                   layout,
1649                                   block,
1650                                   &template_search->search_data.target_ptr,
1651                                   template_search->search_data.target_prop,
1652                                   template_search_menu,
1653                                   MEM_dupallocN(template_search),
1654                                   ui_description,
1655                                   template_search->use_previews,
1656                                   editable,
1657                                   live_icon);
1658 }
1659 
template_search_add_button_name(uiBlock * block,PointerRNA * active_ptr,const StructRNA * type)1660 static void template_search_add_button_name(uiBlock *block,
1661                                             PointerRNA *active_ptr,
1662                                             const StructRNA *type)
1663 {
1664   uiDefAutoButR(block,
1665                 active_ptr,
1666                 RNA_struct_name_property(type),
1667                 0,
1668                 "",
1669                 ICON_NONE,
1670                 0,
1671                 0,
1672                 TEMPLATE_SEARCH_TEXTBUT_WIDTH,
1673                 TEMPLATE_SEARCH_TEXTBUT_HEIGHT);
1674 }
1675 
template_search_add_button_operator(uiBlock * block,const char * const operator_name,const int opcontext,const int icon,const bool editable)1676 static void template_search_add_button_operator(uiBlock *block,
1677                                                 const char *const operator_name,
1678                                                 const int opcontext,
1679                                                 const int icon,
1680                                                 const bool editable)
1681 {
1682   if (!operator_name) {
1683     return;
1684   }
1685 
1686   uiBut *but = uiDefIconButO(
1687       block, UI_BTYPE_BUT, operator_name, opcontext, icon, 0, 0, UI_UNIT_X, UI_UNIT_Y, NULL);
1688 
1689   if (!editable) {
1690     UI_but_drawflag_enable(but, UI_BUT_DISABLED);
1691   }
1692 }
1693 
template_search_buttons(const bContext * C,uiLayout * layout,TemplateSearch * template_search,const char * newop,const char * unlinkop)1694 static void template_search_buttons(const bContext *C,
1695                                     uiLayout *layout,
1696                                     TemplateSearch *template_search,
1697                                     const char *newop,
1698                                     const char *unlinkop)
1699 {
1700   uiBlock *block = uiLayoutGetBlock(layout);
1701   uiRNACollectionSearch *search_data = &template_search->search_data;
1702   StructRNA *type = RNA_property_pointer_type(&search_data->target_ptr, search_data->target_prop);
1703   const bool editable = RNA_property_editable(&search_data->target_ptr, search_data->target_prop);
1704   PointerRNA active_ptr = RNA_property_pointer_get(&search_data->target_ptr,
1705                                                    search_data->target_prop);
1706 
1707   if (active_ptr.type) {
1708     /* can only get correct type when there is an active item */
1709     type = active_ptr.type;
1710   }
1711 
1712   uiLayoutRow(layout, true);
1713   UI_block_align_begin(block);
1714 
1715   template_search_add_button_searchmenu(C, layout, block, template_search, editable, false);
1716   template_search_add_button_name(block, &active_ptr, type);
1717   template_search_add_button_operator(
1718       block, newop, WM_OP_INVOKE_DEFAULT, ICON_DUPLICATE, editable);
1719   template_search_add_button_operator(block, unlinkop, WM_OP_INVOKE_REGION_WIN, ICON_X, editable);
1720 
1721   UI_block_align_end(block);
1722 }
1723 
template_search_get_searchprop(PointerRNA * targetptr,PropertyRNA * targetprop,PointerRNA * searchptr,const char * const searchpropname)1724 static PropertyRNA *template_search_get_searchprop(PointerRNA *targetptr,
1725                                                    PropertyRNA *targetprop,
1726                                                    PointerRNA *searchptr,
1727                                                    const char *const searchpropname)
1728 {
1729   PropertyRNA *searchprop;
1730 
1731   if (searchptr && !searchptr->data) {
1732     searchptr = NULL;
1733   }
1734 
1735   if (!searchptr && !searchpropname) {
1736     /* both NULL means we don't use a custom rna collection to search in */
1737   }
1738   else if (!searchptr && searchpropname) {
1739     RNA_warning("searchpropname defined (%s) but searchptr is missing", searchpropname);
1740   }
1741   else if (searchptr && !searchpropname) {
1742     RNA_warning("searchptr defined (%s) but searchpropname is missing",
1743                 RNA_struct_identifier(searchptr->type));
1744   }
1745   else if (!(searchprop = RNA_struct_find_property(searchptr, searchpropname))) {
1746     RNA_warning("search collection property not found: %s.%s",
1747                 RNA_struct_identifier(searchptr->type),
1748                 searchpropname);
1749   }
1750   else if (RNA_property_type(searchprop) != PROP_COLLECTION) {
1751     RNA_warning("search collection property is not a collection type: %s.%s",
1752                 RNA_struct_identifier(searchptr->type),
1753                 searchpropname);
1754   }
1755   /* check if searchprop has same type as targetprop */
1756   else if (RNA_property_pointer_type(searchptr, searchprop) !=
1757            RNA_property_pointer_type(targetptr, targetprop)) {
1758     RNA_warning("search collection items from %s.%s are not of type %s",
1759                 RNA_struct_identifier(searchptr->type),
1760                 searchpropname,
1761                 RNA_struct_identifier(RNA_property_pointer_type(targetptr, targetprop)));
1762   }
1763   else {
1764     return searchprop;
1765   }
1766 
1767   return NULL;
1768 }
1769 
template_search_setup(PointerRNA * ptr,const char * const propname,PointerRNA * searchptr,const char * const searchpropname)1770 static TemplateSearch *template_search_setup(PointerRNA *ptr,
1771                                              const char *const propname,
1772                                              PointerRNA *searchptr,
1773                                              const char *const searchpropname)
1774 {
1775   TemplateSearch *template_search;
1776   PropertyRNA *prop, *searchprop;
1777 
1778   prop = RNA_struct_find_property(ptr, propname);
1779 
1780   if (!prop || RNA_property_type(prop) != PROP_POINTER) {
1781     RNA_warning("pointer property not found: %s.%s", RNA_struct_identifier(ptr->type), propname);
1782     return NULL;
1783   }
1784   searchprop = template_search_get_searchprop(ptr, prop, searchptr, searchpropname);
1785 
1786   template_search = MEM_callocN(sizeof(*template_search), __func__);
1787   template_search->search_data.target_ptr = *ptr;
1788   template_search->search_data.target_prop = prop;
1789   template_search->search_data.search_ptr = *searchptr;
1790   template_search->search_data.search_prop = searchprop;
1791 
1792   return template_search;
1793 }
1794 
1795 /**
1796  * Search menu to pick an item from a collection.
1797  * A version of uiTemplateID that works for non-ID types.
1798  */
uiTemplateSearch(uiLayout * layout,bContext * C,PointerRNA * ptr,const char * propname,PointerRNA * searchptr,const char * searchpropname,const char * newop,const char * unlinkop)1799 void uiTemplateSearch(uiLayout *layout,
1800                       bContext *C,
1801                       PointerRNA *ptr,
1802                       const char *propname,
1803                       PointerRNA *searchptr,
1804                       const char *searchpropname,
1805                       const char *newop,
1806                       const char *unlinkop)
1807 {
1808   TemplateSearch *template_search = template_search_setup(
1809       ptr, propname, searchptr, searchpropname);
1810   if (template_search != NULL) {
1811     template_search_buttons(C, layout, template_search, newop, unlinkop);
1812     MEM_freeN(template_search);
1813   }
1814 }
1815 
uiTemplateSearchPreview(uiLayout * layout,bContext * C,PointerRNA * ptr,const char * propname,PointerRNA * searchptr,const char * searchpropname,const char * newop,const char * unlinkop,const int rows,const int cols)1816 void uiTemplateSearchPreview(uiLayout *layout,
1817                              bContext *C,
1818                              PointerRNA *ptr,
1819                              const char *propname,
1820                              PointerRNA *searchptr,
1821                              const char *searchpropname,
1822                              const char *newop,
1823                              const char *unlinkop,
1824                              const int rows,
1825                              const int cols)
1826 {
1827   TemplateSearch *template_search = template_search_setup(
1828       ptr, propname, searchptr, searchpropname);
1829 
1830   if (template_search != NULL) {
1831     template_search->use_previews = true;
1832     template_search->preview_rows = rows;
1833     template_search->preview_cols = cols;
1834 
1835     template_search_buttons(C, layout, template_search, newop, unlinkop);
1836 
1837     MEM_freeN(template_search);
1838   }
1839 }
1840 
1841 /** \} */
1842 
1843 /* -------------------------------------------------------------------- */
1844 /** \name RNA Path Builder Template
1845  * \{ */
1846 
1847 /* ---------- */
1848 
1849 /**
1850  * This is creating/editing RNA-Paths
1851  *
1852  * - ptr: struct which holds the path property
1853  * - propname: property identifier for property that path gets stored to
1854  * - root_ptr: struct that path gets built from
1855  */
uiTemplatePathBuilder(uiLayout * layout,PointerRNA * ptr,const char * propname,PointerRNA * UNUSED (root_ptr),const char * text)1856 void uiTemplatePathBuilder(uiLayout *layout,
1857                            PointerRNA *ptr,
1858                            const char *propname,
1859                            PointerRNA *UNUSED(root_ptr),
1860                            const char *text)
1861 {
1862   PropertyRNA *propPath;
1863   uiLayout *row;
1864 
1865   /* check that properties are valid */
1866   propPath = RNA_struct_find_property(ptr, propname);
1867   if (!propPath || RNA_property_type(propPath) != PROP_STRING) {
1868     RNA_warning("path property not found: %s.%s", RNA_struct_identifier(ptr->type), propname);
1869     return;
1870   }
1871 
1872   /* Start drawing UI Elements using standard defines */
1873   row = uiLayoutRow(layout, true);
1874 
1875   /* Path (existing string) Widget */
1876   uiItemR(row, ptr, propname, 0, text, ICON_RNA);
1877 
1878   /* TODO: attach something to this to make allow
1879    * searching of nested properties to 'build' the path */
1880 }
1881 
1882 /** \} */
1883 
1884 /* -------------------------------------------------------------------- */
1885 /** \name Modifiers Template
1886  *
1887  *  Template for building the panel layout for the active object's modifiers.
1888  * \{ */
1889 
modifier_panel_id(void * md_link,char * r_name)1890 static void modifier_panel_id(void *md_link, char *r_name)
1891 {
1892   ModifierData *md = (ModifierData *)md_link;
1893   BKE_modifier_type_panel_id(md->type, r_name);
1894 }
1895 
uiTemplateModifiers(uiLayout * UNUSED (layout),bContext * C)1896 void uiTemplateModifiers(uiLayout *UNUSED(layout), bContext *C)
1897 {
1898   ARegion *region = CTX_wm_region(C);
1899 
1900   Object *ob = ED_object_active_context(C);
1901   ListBase *modifiers = &ob->modifiers;
1902 
1903   const bool panels_match = UI_panel_list_matches_data(region, modifiers, modifier_panel_id);
1904 
1905   if (!panels_match) {
1906     UI_panels_free_instanced(C, region);
1907     ModifierData *md = modifiers->first;
1908     for (int i = 0; md; i++, md = md->next) {
1909       const ModifierTypeInfo *mti = BKE_modifier_get_info(md->type);
1910       if (mti->panelRegister == NULL) {
1911         continue;
1912       }
1913 
1914       char panel_idname[MAX_NAME];
1915       modifier_panel_id(md, panel_idname);
1916 
1917       /* Create custom data RNA pointer. */
1918       PointerRNA *md_ptr = MEM_mallocN(sizeof(PointerRNA), "panel customdata");
1919       RNA_pointer_create(&ob->id, &RNA_Modifier, md, md_ptr);
1920 
1921       UI_panel_add_instanced(C, region, &region->panels, panel_idname, md_ptr);
1922     }
1923   }
1924   else {
1925     /* Assuming there's only one group of instanced panels, update the custom data pointers. */
1926     Panel *panel = region->panels.first;
1927     LISTBASE_FOREACH (ModifierData *, md, modifiers) {
1928       const ModifierTypeInfo *mti = BKE_modifier_get_info(md->type);
1929       if (mti->panelRegister == NULL) {
1930         continue;
1931       }
1932 
1933       /* Move to the next instanced panel corresponding to the next modifier. */
1934       while ((panel->type == NULL) || !(panel->type->flag & PNL_INSTANCED)) {
1935         panel = panel->next;
1936         BLI_assert(panel != NULL); /* There shouldn't be fewer panels than modifiers with UIs. */
1937       }
1938 
1939       PointerRNA *md_ptr = MEM_mallocN(sizeof(PointerRNA), "panel customdata");
1940       RNA_pointer_create(&ob->id, &RNA_Modifier, md, md_ptr);
1941       UI_panel_custom_data_set(panel, md_ptr);
1942 
1943       panel = panel->next;
1944     }
1945   }
1946 }
1947 
1948 /** \} */
1949 
1950 /* -------------------------------------------------------------------- */
1951 /** \name Constraints Template
1952  *
1953  *  Template for building the panel layout for the active object or bone's constraints.
1954  * \{ */
1955 
1956 /** For building the panel UI for constraints. */
1957 #define CONSTRAINT_TYPE_PANEL_PREFIX "OBJECT_PT_"
1958 #define CONSTRAINT_BONE_TYPE_PANEL_PREFIX "BONE_PT_"
1959 
1960 /**
1961  * Check if the panel's ID starts with 'BONE', meaning it is a bone constraint.
1962  */
constraint_panel_is_bone(Panel * panel)1963 static bool constraint_panel_is_bone(Panel *panel)
1964 {
1965   return (panel->panelname[0] == 'B') && (panel->panelname[1] == 'O') &&
1966          (panel->panelname[2] == 'N') && (panel->panelname[3] == 'E');
1967 }
1968 
1969 /**
1970  * Move a constraint to the index it's moved to after a drag and drop.
1971  */
constraint_reorder(bContext * C,Panel * panel,int new_index)1972 static void constraint_reorder(bContext *C, Panel *panel, int new_index)
1973 {
1974   bool constraint_from_bone = constraint_panel_is_bone(panel);
1975 
1976   PointerRNA *con_ptr = UI_panel_custom_data_get(panel);
1977   bConstraint *con = (bConstraint *)con_ptr->data;
1978 
1979   PointerRNA props_ptr;
1980   wmOperatorType *ot = WM_operatortype_find("CONSTRAINT_OT_move_to_index", false);
1981   WM_operator_properties_create_ptr(&props_ptr, ot);
1982   RNA_string_set(&props_ptr, "constraint", con->name);
1983   RNA_int_set(&props_ptr, "index", new_index);
1984   /* Set owner to #EDIT_CONSTRAINT_OWNER_OBJECT or #EDIT_CONSTRAINT_OWNER_BONE. */
1985   RNA_enum_set(&props_ptr, "owner", constraint_from_bone ? 1 : 0);
1986   WM_operator_name_call_ptr(C, ot, WM_OP_INVOKE_DEFAULT, &props_ptr);
1987   WM_operator_properties_free(&props_ptr);
1988 }
1989 
1990 /**
1991  * Get the expand flag from the active constraint to use for the panel.
1992  */
get_constraint_expand_flag(const bContext * UNUSED (C),Panel * panel)1993 static short get_constraint_expand_flag(const bContext *UNUSED(C), Panel *panel)
1994 {
1995   PointerRNA *con_ptr = UI_panel_custom_data_get(panel);
1996   bConstraint *con = (bConstraint *)con_ptr->data;
1997 
1998   return con->ui_expand_flag;
1999 }
2000 
2001 /**
2002  * Save the expand flag for the panel and sub-panels to the constraint.
2003  */
set_constraint_expand_flag(const bContext * UNUSED (C),Panel * panel,short expand_flag)2004 static void set_constraint_expand_flag(const bContext *UNUSED(C), Panel *panel, short expand_flag)
2005 {
2006   PointerRNA *con_ptr = UI_panel_custom_data_get(panel);
2007   bConstraint *con = (bConstraint *)con_ptr->data;
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  */
object_constraint_panel_id(void * md_link,char * r_name)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 
bone_constraint_panel_id(void * md_link,char * r_name)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  */
uiTemplateConstraints(uiLayout * UNUSED (layout),bContext * C,bool use_bone_constraints)2038 void uiTemplateConstraints(uiLayout *UNUSED(layout), bContext *C, bool use_bone_constraints)
2039 {
2040   ARegion *region = CTX_wm_region(C);
2041 
2042   Object *ob = ED_object_active_context(C);
2043   ListBase *constraints = {NULL};
2044   if (use_bone_constraints) {
2045     constraints = ED_object_pose_constraint_list(C);
2046   }
2047   else if (ob != NULL) {
2048     constraints = &ob->constraints;
2049   }
2050 
2051   /* Switch between the bone panel ID function and the object panel ID function. */
2052   uiListPanelIDFromDataFunc panel_id_func = use_bone_constraints ? bone_constraint_panel_id :
2053                                                                    object_constraint_panel_id;
2054 
2055   const bool panels_match = UI_panel_list_matches_data(region, constraints, panel_id_func);
2056 
2057   if (!panels_match) {
2058     UI_panels_free_instanced(C, region);
2059     bConstraint *con = (constraints == NULL) ? NULL : constraints->first;
2060     for (int i = 0; con; i++, con = con->next) {
2061       /* Dont show temporary constraints (AutoIK and targetless IK constraints). */
2062       if (con->type == CONSTRAINT_TYPE_KINEMATIC) {
2063         bKinematicConstraint *data = con->data;
2064         if (data->flag & CONSTRAINT_IK_TEMP) {
2065           continue;
2066         }
2067       }
2068 
2069       char panel_idname[MAX_NAME];
2070       panel_id_func(con, panel_idname);
2071 
2072       /* Create custom data RNA pointer. */
2073       PointerRNA *con_ptr = MEM_mallocN(sizeof(PointerRNA), "panel customdata");
2074       RNA_pointer_create(&ob->id, &RNA_Constraint, con, con_ptr);
2075 
2076       Panel *new_panel = UI_panel_add_instanced(C, region, &region->panels, panel_idname, con_ptr);
2077 
2078       if (new_panel) {
2079         /* Set the list panel functionality function pointers since we don't do it with python. */
2080         new_panel->type->set_list_data_expand_flag = set_constraint_expand_flag;
2081         new_panel->type->get_list_data_expand_flag = get_constraint_expand_flag;
2082         new_panel->type->reorder = constraint_reorder;
2083       }
2084     }
2085   }
2086   else {
2087     /* Assuming there's only one group of instanced panels, update the custom data pointers. */
2088     Panel *panel = region->panels.first;
2089     LISTBASE_FOREACH (bConstraint *, con, constraints) {
2090       /* Dont show temporary constraints (AutoIK and targetless IK constraints). */
2091       if (con->type == CONSTRAINT_TYPE_KINEMATIC) {
2092         bKinematicConstraint *data = con->data;
2093         if (data->flag & CONSTRAINT_IK_TEMP) {
2094           continue;
2095         }
2096       }
2097 
2098       /* Move to the next instanced panel corresponding to the next constraint. */
2099       while ((panel->type == NULL) || !(panel->type->flag & PNL_INSTANCED)) {
2100         panel = panel->next;
2101         BLI_assert(panel != NULL); /* There shouldn't be fewer panels than constraint panels. */
2102       }
2103 
2104       PointerRNA *con_ptr = MEM_mallocN(sizeof(PointerRNA), "constraint panel customdata");
2105       RNA_pointer_create(&ob->id, &RNA_Constraint, con, con_ptr);
2106       UI_panel_custom_data_set(panel, con_ptr);
2107 
2108       panel = panel->next;
2109     }
2110   }
2111 }
2112 
2113 #undef CONSTRAINT_TYPE_PANEL_PREFIX
2114 #undef CONSTRAINT_BONE_TYPE_PANEL_PREFIX
2115 
2116 /** \} */
2117 
2118 /* -------------------------------------------------------------------- */
2119 /** \name Grease Pencil Modifiers Template
2120  * \{ */
2121 
2122 /**
2123  * Function with void * argument for #uiListPanelIDFromDataFunc.
2124  */
gpencil_modifier_panel_id(void * md_link,char * r_name)2125 static void gpencil_modifier_panel_id(void *md_link, char *r_name)
2126 {
2127   ModifierData *md = (ModifierData *)md_link;
2128   BKE_gpencil_modifierType_panel_id(md->type, r_name);
2129 }
2130 
uiTemplateGpencilModifiers(uiLayout * UNUSED (layout),bContext * C)2131 void uiTemplateGpencilModifiers(uiLayout *UNUSED(layout), bContext *C)
2132 {
2133   ARegion *region = CTX_wm_region(C);
2134   Object *ob = ED_object_active_context(C);
2135   ListBase *modifiers = &ob->greasepencil_modifiers;
2136 
2137   const bool panels_match = UI_panel_list_matches_data(
2138       region, modifiers, gpencil_modifier_panel_id);
2139 
2140   if (!panels_match) {
2141     UI_panels_free_instanced(C, region);
2142     GpencilModifierData *md = modifiers->first;
2143     for (int i = 0; md; i++, md = md->next) {
2144       const GpencilModifierTypeInfo *mti = BKE_gpencil_modifier_get_info(md->type);
2145       if (mti->panelRegister == NULL) {
2146         continue;
2147       }
2148 
2149       char panel_idname[MAX_NAME];
2150       gpencil_modifier_panel_id(md, panel_idname);
2151 
2152       /* Create custom data RNA pointer. */
2153       PointerRNA *md_ptr = MEM_mallocN(sizeof(PointerRNA), "panel customdata");
2154       RNA_pointer_create(&ob->id, &RNA_GpencilModifier, md, md_ptr);
2155 
2156       UI_panel_add_instanced(C, region, &region->panels, panel_idname, md_ptr);
2157     }
2158   }
2159   else {
2160     /* Assuming there's only one group of instanced panels, update the custom data pointers. */
2161     Panel *panel = region->panels.first;
2162     LISTBASE_FOREACH (ModifierData *, md, modifiers) {
2163       const GpencilModifierTypeInfo *mti = BKE_gpencil_modifier_get_info(md->type);
2164       if (mti->panelRegister == NULL) {
2165         continue;
2166       }
2167 
2168       /* Move to the next instanced panel corresponding to the next modifier. */
2169       while ((panel->type == NULL) || !(panel->type->flag & PNL_INSTANCED)) {
2170         panel = panel->next;
2171         BLI_assert(panel != NULL); /* There shouldn't be fewer panels than modifiers with UIs. */
2172       }
2173 
2174       PointerRNA *md_ptr = MEM_mallocN(sizeof(PointerRNA), "panel customdata");
2175       RNA_pointer_create(&ob->id, &RNA_GpencilModifier, md, md_ptr);
2176       UI_panel_custom_data_set(panel, md_ptr);
2177 
2178       panel = panel->next;
2179     }
2180   }
2181 }
2182 
2183 /** \} */
2184 
2185 /** \} */
2186 
2187 #define ERROR_LIBDATA_MESSAGE TIP_("Can't edit external library data")
2188 
2189 /* -------------------------------------------------------------------- */
2190 /** \name ShaderFx Template
2191  *
2192  *  Template for building the panel layout for the active object's grease pencil shader
2193  * effects.
2194  * \{ */
2195 
2196 /**
2197  * Function with void * argument for #uiListPanelIDFromDataFunc.
2198  */
shaderfx_panel_id(void * fx_v,char * r_idname)2199 static void shaderfx_panel_id(void *fx_v, char *r_idname)
2200 {
2201   ShaderFxData *fx = (ShaderFxData *)fx_v;
2202   BKE_shaderfxType_panel_id(fx->type, r_idname);
2203 }
2204 
2205 /**
2206  * Check if the shader effect panels don't match the data and rebuild the panels if so.
2207  */
uiTemplateShaderFx(uiLayout * UNUSED (layout),bContext * C)2208 void uiTemplateShaderFx(uiLayout *UNUSED(layout), bContext *C)
2209 {
2210   ARegion *region = CTX_wm_region(C);
2211   Object *ob = ED_object_active_context(C);
2212   ListBase *shaderfx = &ob->shader_fx;
2213 
2214   const bool panels_match = UI_panel_list_matches_data(region, shaderfx, shaderfx_panel_id);
2215 
2216   if (!panels_match) {
2217     UI_panels_free_instanced(C, region);
2218     ShaderFxData *fx = shaderfx->first;
2219     for (int i = 0; fx; i++, fx = fx->next) {
2220       char panel_idname[MAX_NAME];
2221       shaderfx_panel_id(fx, panel_idname);
2222 
2223       /* Create custom data RNA pointer. */
2224       PointerRNA *fx_ptr = MEM_mallocN(sizeof(PointerRNA), "panel customdata");
2225       RNA_pointer_create(&ob->id, &RNA_ShaderFx, fx, fx_ptr);
2226 
2227       UI_panel_add_instanced(C, region, &region->panels, panel_idname, fx_ptr);
2228     }
2229   }
2230   else {
2231     /* Assuming there's only one group of instanced panels, update the custom data pointers. */
2232     Panel *panel = region->panels.first;
2233     LISTBASE_FOREACH (ShaderFxData *, fx, shaderfx) {
2234       const ShaderFxTypeInfo *fxi = BKE_shaderfx_get_info(fx->type);
2235       if (fxi->panelRegister == NULL) {
2236         continue;
2237       }
2238 
2239       /* Move to the next instanced panel corresponding to the next modifier. */
2240       while ((panel->type == NULL) || !(panel->type->flag & PNL_INSTANCED)) {
2241         panel = panel->next;
2242         BLI_assert(panel != NULL); /* There shouldn't be fewer panels than modifiers with UIs. */
2243       }
2244 
2245       PointerRNA *fx_ptr = MEM_mallocN(sizeof(PointerRNA), "panel customdata");
2246       RNA_pointer_create(&ob->id, &RNA_ShaderFx, fx, fx_ptr);
2247       UI_panel_custom_data_set(panel, fx_ptr);
2248 
2249       panel = panel->next;
2250     }
2251   }
2252 }
2253 
2254 /** \} */
2255 
2256 /* -------------------------------------------------------------------- */
2257 /** \name Operator Property Buttons Template
2258  * \{ */
2259 
2260 typedef struct uiTemplateOperatorPropertyPollParam {
2261   const bContext *C;
2262   wmOperator *op;
2263   short flag;
2264 } uiTemplateOperatorPropertyPollParam;
2265 
2266 #ifdef USE_OP_RESET_BUT
ui_layout_operator_buts__reset_cb(bContext * UNUSED (C),void * op_pt,void * UNUSED (arg_dummy2))2267 static void ui_layout_operator_buts__reset_cb(bContext *UNUSED(C),
2268                                               void *op_pt,
2269                                               void *UNUSED(arg_dummy2))
2270 {
2271   WM_operator_properties_reset((wmOperator *)op_pt);
2272 }
2273 #endif
2274 
ui_layout_operator_buts_poll_property(struct PointerRNA * UNUSED (ptr),struct PropertyRNA * prop,void * user_data)2275 static bool ui_layout_operator_buts_poll_property(struct PointerRNA *UNUSED(ptr),
2276                                                   struct PropertyRNA *prop,
2277                                                   void *user_data)
2278 {
2279   uiTemplateOperatorPropertyPollParam *params = user_data;
2280 
2281   if ((params->flag & UI_TEMPLATE_OP_PROPS_HIDE_ADVANCED) &&
2282       (RNA_property_tags(prop) & OP_PROP_TAG_ADVANCED)) {
2283     return false;
2284   }
2285   return params->op->type->poll_property(params->C, params->op, prop);
2286 }
2287 
template_operator_property_buts_draw_single(const bContext * C,wmOperator * op,uiLayout * layout,const eButLabelAlign label_align,int layout_flags)2288 static eAutoPropButsReturn template_operator_property_buts_draw_single(
2289     const bContext *C,
2290     wmOperator *op,
2291     uiLayout *layout,
2292     const eButLabelAlign label_align,
2293     int layout_flags)
2294 {
2295   uiBlock *block = uiLayoutGetBlock(layout);
2296   eAutoPropButsReturn return_info = 0;
2297 
2298   if (!op->properties) {
2299     const IDPropertyTemplate val = {0};
2300     op->properties = IDP_New(IDP_GROUP, &val, "wmOperatorProperties");
2301   }
2302 
2303   /* poll() on this operator may still fail,
2304    * at the moment there is no nice feedback when this happens just fails silently. */
2305   if (!WM_operator_repeat_check(C, op)) {
2306     UI_block_lock_set(block, true, "Operator can't' redo");
2307     return return_info;
2308   }
2309 
2310   /* useful for macros where only one of the steps can't be re-done */
2311   UI_block_lock_clear(block);
2312 
2313   if (layout_flags & UI_TEMPLATE_OP_PROPS_SHOW_TITLE) {
2314     uiItemL(layout, WM_operatortype_name(op->type, op->ptr), ICON_NONE);
2315   }
2316 
2317   /* menu */
2318   if (op->type->flag & OPTYPE_PRESET) {
2319     /* XXX, no simple way to get WM_MT_operator_presets.bl_label
2320      * from python! Label remains the same always! */
2321     PointerRNA op_ptr;
2322     uiLayout *row;
2323 
2324     block->ui_operator = op;
2325 
2326     row = uiLayoutRow(layout, true);
2327     uiItemM(row, "WM_MT_operator_presets", NULL, ICON_NONE);
2328 
2329     wmOperatorType *ot = WM_operatortype_find("WM_OT_operator_preset_add", false);
2330     uiItemFullO_ptr(row, ot, "", ICON_ADD, NULL, WM_OP_INVOKE_DEFAULT, 0, &op_ptr);
2331     RNA_string_set(&op_ptr, "operator", op->type->idname);
2332 
2333     uiItemFullO_ptr(row, ot, "", ICON_REMOVE, NULL, WM_OP_INVOKE_DEFAULT, 0, &op_ptr);
2334     RNA_string_set(&op_ptr, "operator", op->type->idname);
2335     RNA_boolean_set(&op_ptr, "remove_active", true);
2336   }
2337 
2338   if (op->type->ui) {
2339     op->layout = layout;
2340     op->type->ui((bContext *)C, op);
2341     op->layout = NULL;
2342 
2343     /* UI_LAYOUT_OP_SHOW_EMPTY ignored. retun_info is ignored too. We could
2344      * allow ot.ui callback to return this, but not needed right now. */
2345   }
2346   else {
2347     wmWindowManager *wm = CTX_wm_manager(C);
2348     uiTemplateOperatorPropertyPollParam user_data = {.C = C, .op = op, .flag = layout_flags};
2349     const bool use_prop_split = (layout_flags & UI_TEMPLATE_OP_PROPS_NO_SPLIT_LAYOUT) == 0;
2350 
2351     PointerRNA ptr;
2352     RNA_pointer_create(&wm->id, op->type->srna, op->properties, &ptr);
2353 
2354     uiLayoutSetPropSep(layout, use_prop_split);
2355     uiLayoutSetPropDecorate(layout, false);
2356 
2357     /* main draw call */
2358     return_info = uiDefAutoButsRNA(
2359         layout,
2360         &ptr,
2361         op->type->poll_property ? ui_layout_operator_buts_poll_property : NULL,
2362         op->type->poll_property ? &user_data : NULL,
2363         op->type->prop,
2364         label_align,
2365         (layout_flags & UI_TEMPLATE_OP_PROPS_COMPACT));
2366 
2367     if ((return_info & UI_PROP_BUTS_NONE_ADDED) &&
2368         (layout_flags & UI_TEMPLATE_OP_PROPS_SHOW_EMPTY)) {
2369       uiItemL(layout, IFACE_("No Properties"), ICON_NONE);
2370     }
2371   }
2372 
2373 #ifdef USE_OP_RESET_BUT
2374   /* its possible that reset can do nothing if all have PROP_SKIP_SAVE enabled
2375    * but this is not so important if this button is drawn in those cases
2376    * (which isn't all that likely anyway) - campbell */
2377   if (op->properties->len) {
2378     uiBut *but;
2379     uiLayout *col; /* needed to avoid alignment errors with previous buttons */
2380 
2381     col = uiLayoutColumn(layout, false);
2382     block = uiLayoutGetBlock(col);
2383     but = uiDefIconTextBut(block,
2384                            UI_BTYPE_BUT,
2385                            0,
2386                            ICON_FILE_REFRESH,
2387                            IFACE_("Reset"),
2388                            0,
2389                            0,
2390                            UI_UNIT_X,
2391                            UI_UNIT_Y,
2392                            NULL,
2393                            0.0,
2394                            0.0,
2395                            0.0,
2396                            0.0,
2397                            TIP_("Reset operator defaults"));
2398     UI_but_func_set(but, ui_layout_operator_buts__reset_cb, op, NULL);
2399   }
2400 #endif
2401 
2402   /* set various special settings for buttons */
2403 
2404   /* Only do this if we're not refreshing an existing UI. */
2405   if (block->oldblock == NULL) {
2406     const bool is_popup = (block->flag & UI_BLOCK_KEEP_OPEN) != 0;
2407 
2408     LISTBASE_FOREACH (uiBut *, but, &block->buttons) {
2409       /* no undo for buttons for operator redo panels */
2410       UI_but_flag_disable(but, UI_BUT_UNDO);
2411 
2412       /* only for popups, see T36109. */
2413 
2414       /* if button is operator's default property, and a text-field, enable focus for it
2415        * - this is used for allowing operators with popups to rename stuff with fewer clicks
2416        */
2417       if (is_popup) {
2418         if ((but->rnaprop == op->type->prop) && (but->type == UI_BTYPE_TEXT)) {
2419           UI_but_focus_on_enter_event(CTX_wm_window(C), but);
2420         }
2421       }
2422     }
2423   }
2424 
2425   return return_info;
2426 }
2427 
template_operator_property_buts_draw_recursive(const bContext * C,wmOperator * op,uiLayout * layout,const eButLabelAlign label_align,int layout_flags,bool * r_has_advanced)2428 static void template_operator_property_buts_draw_recursive(const bContext *C,
2429                                                            wmOperator *op,
2430                                                            uiLayout *layout,
2431                                                            const eButLabelAlign label_align,
2432                                                            int layout_flags,
2433                                                            bool *r_has_advanced)
2434 {
2435   if (op->type->flag & OPTYPE_MACRO) {
2436     LISTBASE_FOREACH (wmOperator *, macro_op, &op->macro) {
2437       template_operator_property_buts_draw_recursive(
2438           C, macro_op, layout, label_align, layout_flags, r_has_advanced);
2439     }
2440   }
2441   else {
2442     /* Might want to make label_align adjustable somehow. */
2443     eAutoPropButsReturn return_info = template_operator_property_buts_draw_single(
2444         C, op, layout, label_align, layout_flags);
2445     if (return_info & UI_PROP_BUTS_ANY_FAILED_CHECK) {
2446       if (r_has_advanced) {
2447         *r_has_advanced = true;
2448       }
2449     }
2450   }
2451 }
2452 
ui_layout_operator_properties_only_booleans(const bContext * C,wmWindowManager * wm,wmOperator * op,int layout_flags)2453 static bool ui_layout_operator_properties_only_booleans(const bContext *C,
2454                                                         wmWindowManager *wm,
2455                                                         wmOperator *op,
2456                                                         int layout_flags)
2457 {
2458   if (op->type->flag & OPTYPE_MACRO) {
2459     LISTBASE_FOREACH (wmOperator *, macro_op, &op->macro) {
2460       if (!ui_layout_operator_properties_only_booleans(C, wm, macro_op, layout_flags)) {
2461         return false;
2462       }
2463     }
2464   }
2465   else {
2466     uiTemplateOperatorPropertyPollParam user_data = {.C = C, .op = op, .flag = layout_flags};
2467     PointerRNA ptr;
2468 
2469     RNA_pointer_create(&wm->id, op->type->srna, op->properties, &ptr);
2470 
2471     RNA_STRUCT_BEGIN (&ptr, prop) {
2472       if (RNA_property_flag(prop) & PROP_HIDDEN) {
2473         continue;
2474       }
2475       if (op->type->poll_property &&
2476           !ui_layout_operator_buts_poll_property(&ptr, prop, &user_data)) {
2477         continue;
2478       }
2479       if (RNA_property_type(prop) != PROP_BOOLEAN) {
2480         return false;
2481       }
2482     }
2483     RNA_STRUCT_END;
2484   }
2485 
2486   return true;
2487 }
2488 
2489 /**
2490  * Draw Operator property buttons for redoing execution with different settings.
2491  * This function does not initialize the layout,
2492  * functions can be called on the layout before and after.
2493  */
uiTemplateOperatorPropertyButs(const bContext * C,uiLayout * layout,wmOperator * op,eButLabelAlign label_align,short flag)2494 void uiTemplateOperatorPropertyButs(
2495     const bContext *C, uiLayout *layout, wmOperator *op, eButLabelAlign label_align, short flag)
2496 {
2497   wmWindowManager *wm = CTX_wm_manager(C);
2498 
2499   /* If there are only checkbox items, don't use split layout by default. It looks weird if the
2500    * checkboxes only use half the width. */
2501   if (ui_layout_operator_properties_only_booleans(C, wm, op, flag)) {
2502     flag |= UI_TEMPLATE_OP_PROPS_NO_SPLIT_LAYOUT;
2503   }
2504 
2505   template_operator_property_buts_draw_recursive(C, op, layout, label_align, flag, NULL);
2506 }
2507 
uiTemplateOperatorRedoProperties(uiLayout * layout,const bContext * C)2508 void uiTemplateOperatorRedoProperties(uiLayout *layout, const bContext *C)
2509 {
2510   wmOperator *op = WM_operator_last_redo(C);
2511   uiBlock *block = uiLayoutGetBlock(layout);
2512 
2513   if (op == NULL) {
2514     return;
2515   }
2516 
2517   /* Disable for now, doesn't fit well in popover. */
2518 #if 0
2519   /* Repeat button with operator name as text. */
2520   uiItemFullO(layout,
2521               "SCREEN_OT_repeat_last",
2522               WM_operatortype_name(op->type, op->ptr),
2523               ICON_NONE,
2524               NULL,
2525               WM_OP_INVOKE_DEFAULT,
2526               0,
2527               NULL);
2528 #endif
2529 
2530   if (WM_operator_repeat_check(C, op)) {
2531     int layout_flags = 0;
2532     if (block->panel == NULL) {
2533       layout_flags = UI_TEMPLATE_OP_PROPS_SHOW_TITLE;
2534     }
2535 #if 0
2536     bool has_advanced = false;
2537 #endif
2538 
2539     UI_block_func_handle_set(block, ED_undo_operator_repeat_cb_evt, op);
2540     template_operator_property_buts_draw_recursive(
2541         C, op, layout, UI_BUT_LABEL_ALIGN_NONE, layout_flags, NULL /* &has_advanced */);
2542     /* Warning! this leaves the handle function for any other users of this block. */
2543 
2544 #if 0
2545     if (has_advanced) {
2546       uiItemO(layout, IFACE_("More..."), ICON_NONE, "SCREEN_OT_redo_last");
2547     }
2548 #endif
2549   }
2550 }
2551 
2552 /** \} */
2553 
2554 /* -------------------------------------------------------------------- */
2555 /** \name Constraint Header Template
2556  * \{ */
2557 
2558 #define ERROR_LIBDATA_MESSAGE TIP_("Can't edit external library data")
2559 
constraint_active_func(bContext * UNUSED (C),void * ob_v,void * con_v)2560 static void constraint_active_func(bContext *UNUSED(C), void *ob_v, void *con_v)
2561 {
2562   ED_object_constraint_active_set(ob_v, con_v);
2563 }
2564 
draw_constraint_header(uiLayout * layout,Object * ob,bConstraint * con)2565 static void draw_constraint_header(uiLayout *layout, Object *ob, bConstraint *con)
2566 {
2567   bPoseChannel *pchan = BKE_pose_channel_active(ob);
2568   short proxy_protected, xco = 0, yco = 0;
2569   // int rb_col; // UNUSED
2570 
2571   /* determine whether constraint is proxy protected or not */
2572   if (BKE_constraints_proxylocked_owner(ob, pchan)) {
2573     proxy_protected = (con->flag & CONSTRAINT_PROXY_LOCAL) == 0;
2574   }
2575   else {
2576     proxy_protected = 0;
2577   }
2578 
2579   /* unless button has own callback, it adds this callback to button */
2580   uiBlock *block = uiLayoutGetBlock(layout);
2581   UI_block_func_set(block, constraint_active_func, ob, con);
2582 
2583   PointerRNA ptr;
2584   RNA_pointer_create(&ob->id, &RNA_Constraint, con, &ptr);
2585 
2586   uiLayoutSetContextPointer(layout, "constraint", &ptr);
2587 
2588   /* Constraint type icon. */
2589   uiLayout *sub = uiLayoutRow(layout, false);
2590   uiLayoutSetEmboss(sub, false);
2591   uiLayoutSetRedAlert(sub, (con->flag & CONSTRAINT_DISABLE));
2592   uiItemL(sub, "", RNA_struct_ui_icon(ptr.type));
2593 
2594   UI_block_emboss_set(block, UI_EMBOSS);
2595 
2596   if (proxy_protected == 0) {
2597     uiItemR(layout, &ptr, "name", 0, "", ICON_NONE);
2598   }
2599   else {
2600     uiItemL(layout, con->name, ICON_NONE);
2601   }
2602 
2603   /* proxy-protected constraints cannot be edited, so hide up/down + close buttons */
2604   if (proxy_protected) {
2605     UI_block_emboss_set(block, UI_EMBOSS_NONE);
2606 
2607     /* draw a ghost icon (for proxy) and also a lock beside it,
2608      * to show that constraint is "proxy locked" */
2609     uiDefIconBut(block,
2610                  UI_BTYPE_BUT,
2611                  0,
2612                  ICON_GHOST_ENABLED,
2613                  xco + 12.2f * UI_UNIT_X,
2614                  yco,
2615                  0.95f * UI_UNIT_X,
2616                  0.95f * UI_UNIT_Y,
2617                  NULL,
2618                  0.0,
2619                  0.0,
2620                  0.0,
2621                  0.0,
2622                  TIP_("Proxy Protected"));
2623     uiDefIconBut(block,
2624                  UI_BTYPE_BUT,
2625                  0,
2626                  ICON_LOCKED,
2627                  xco + 13.1f * UI_UNIT_X,
2628                  yco,
2629                  0.95f * UI_UNIT_X,
2630                  0.95f * UI_UNIT_Y,
2631                  NULL,
2632                  0.0,
2633                  0.0,
2634                  0.0,
2635                  0.0,
2636                  TIP_("Proxy Protected"));
2637 
2638     UI_block_emboss_set(block, UI_EMBOSS);
2639   }
2640   else {
2641     /* enabled */
2642     UI_block_emboss_set(block, UI_EMBOSS_NONE);
2643     uiItemR(layout, &ptr, "mute", 0, "", 0);
2644     UI_block_emboss_set(block, UI_EMBOSS);
2645 
2646     uiLayoutSetOperatorContext(layout, WM_OP_INVOKE_DEFAULT);
2647 
2648     /* Close 'button' - emboss calls here disable drawing of 'button' behind X */
2649     UI_block_emboss_set(block, UI_EMBOSS_NONE);
2650     uiItemO(layout, "", ICON_X, "CONSTRAINT_OT_delete");
2651     UI_block_emboss_set(block, UI_EMBOSS);
2652 
2653     /* Some extra padding at the end, so the 'x' icon isn't too close to drag button. */
2654     uiItemS(layout);
2655   }
2656 
2657   /* Set but-locks for protected settings (magic numbers are used here!) */
2658   if (proxy_protected) {
2659     UI_block_lock_set(block, true, TIP_("Cannot edit Proxy-Protected Constraint"));
2660   }
2661 
2662   /* clear any locks set up for proxies/lib-linking */
2663   UI_block_lock_clear(block);
2664 }
2665 
uiTemplateConstraintHeader(uiLayout * layout,PointerRNA * ptr)2666 void uiTemplateConstraintHeader(uiLayout *layout, PointerRNA *ptr)
2667 {
2668   /* verify we have valid data */
2669   if (!RNA_struct_is_a(ptr->type, &RNA_Constraint)) {
2670     RNA_warning("Expected constraint on object");
2671     return;
2672   }
2673 
2674   Object *ob = (Object *)ptr->owner_id;
2675   bConstraint *con = ptr->data;
2676 
2677   if (!ob || !(GS(ob->id.name) == ID_OB)) {
2678     RNA_warning("Expected constraint on object");
2679     return;
2680   }
2681 
2682   UI_block_lock_set(uiLayoutGetBlock(layout), (ob && ID_IS_LINKED(ob)), ERROR_LIBDATA_MESSAGE);
2683 
2684   draw_constraint_header(layout, ob, con);
2685 }
2686 
2687 /** \} */
2688 
2689 /* -------------------------------------------------------------------- */
2690 /** \name Preview Template
2691  * \{ */
2692 
2693 #include "DNA_light_types.h"
2694 #include "DNA_material_types.h"
2695 #include "DNA_world_types.h"
2696 
2697 #define B_MATPRV 1
2698 
do_preview_buttons(bContext * C,void * arg,int event)2699 static void do_preview_buttons(bContext *C, void *arg, int event)
2700 {
2701   switch (event) {
2702     case B_MATPRV:
2703       WM_event_add_notifier(C, NC_MATERIAL | ND_SHADING_PREVIEW, arg);
2704       break;
2705   }
2706 }
2707 
uiTemplatePreview(uiLayout * layout,bContext * C,ID * id,bool show_buttons,ID * parent,MTex * slot,const char * preview_id)2708 void uiTemplatePreview(uiLayout *layout,
2709                        bContext *C,
2710                        ID *id,
2711                        bool show_buttons,
2712                        ID *parent,
2713                        MTex *slot,
2714                        const char *preview_id)
2715 {
2716   Material *ma = NULL;
2717   Tex *tex = (Tex *)id;
2718   short *pr_texture = NULL;
2719   PointerRNA material_ptr;
2720   PointerRNA texture_ptr;
2721 
2722   char _preview_id[UI_MAX_NAME_STR];
2723 
2724   if (id && !ELEM(GS(id->name), ID_MA, ID_TE, ID_WO, ID_LA, ID_LS)) {
2725     RNA_warning("Expected ID of type material, texture, light, world or line style");
2726     return;
2727   }
2728 
2729   /* decide what to render */
2730   ID *pid = id;
2731   ID *pparent = NULL;
2732 
2733   if (id && (GS(id->name) == ID_TE)) {
2734     if (parent && (GS(parent->name) == ID_MA)) {
2735       pr_texture = &((Material *)parent)->pr_texture;
2736     }
2737     else if (parent && (GS(parent->name) == ID_WO)) {
2738       pr_texture = &((World *)parent)->pr_texture;
2739     }
2740     else if (parent && (GS(parent->name) == ID_LA)) {
2741       pr_texture = &((Light *)parent)->pr_texture;
2742     }
2743     else if (parent && (GS(parent->name) == ID_LS)) {
2744       pr_texture = &((FreestyleLineStyle *)parent)->pr_texture;
2745     }
2746 
2747     if (pr_texture) {
2748       if (*pr_texture == TEX_PR_OTHER) {
2749         pid = parent;
2750       }
2751       else if (*pr_texture == TEX_PR_BOTH) {
2752         pparent = parent;
2753       }
2754     }
2755   }
2756 
2757   if (!preview_id || (preview_id[0] == '\0')) {
2758     /* If no identifier given, generate one from ID type. */
2759     BLI_snprintf(
2760         _preview_id, UI_MAX_NAME_STR, "uiPreview_%s", BKE_idtype_idcode_to_name(GS(id->name)));
2761     preview_id = _preview_id;
2762   }
2763 
2764   /* Find or add the uiPreview to the current Region. */
2765   ARegion *region = CTX_wm_region(C);
2766   uiPreview *ui_preview = BLI_findstring(
2767       &region->ui_previews, preview_id, offsetof(uiPreview, preview_id));
2768 
2769   if (!ui_preview) {
2770     ui_preview = MEM_callocN(sizeof(uiPreview), "uiPreview");
2771     BLI_strncpy(ui_preview->preview_id, preview_id, sizeof(ui_preview->preview_id));
2772     ui_preview->height = (short)(UI_UNIT_Y * 7.6f);
2773     BLI_addtail(&region->ui_previews, ui_preview);
2774   }
2775 
2776   if (ui_preview->height < UI_UNIT_Y) {
2777     ui_preview->height = UI_UNIT_Y;
2778   }
2779   else if (ui_preview->height > UI_UNIT_Y * 50) { /* Rather high upper limit, yet not insane! */
2780     ui_preview->height = UI_UNIT_Y * 50;
2781   }
2782 
2783   /* layout */
2784   uiBlock *block = uiLayoutGetBlock(layout);
2785   uiLayout *row = uiLayoutRow(layout, false);
2786   uiLayout *col = uiLayoutColumn(row, false);
2787   uiLayoutSetKeepAspect(col, true);
2788 
2789   /* add preview */
2790   uiDefBut(block,
2791            UI_BTYPE_EXTRA,
2792            0,
2793            "",
2794            0,
2795            0,
2796            UI_UNIT_X * 10,
2797            ui_preview->height,
2798            pid,
2799            0.0,
2800            0.0,
2801            0,
2802            0,
2803            "");
2804   UI_but_func_drawextra_set(block, ED_preview_draw, pparent, slot);
2805   UI_block_func_handle_set(block, do_preview_buttons, NULL);
2806 
2807   uiDefIconButS(block,
2808                 UI_BTYPE_GRIP,
2809                 0,
2810                 ICON_GRIP,
2811                 0,
2812                 0,
2813                 UI_UNIT_X * 10,
2814                 (short)(UI_UNIT_Y * 0.3f),
2815                 &ui_preview->height,
2816                 UI_UNIT_Y,
2817                 UI_UNIT_Y * 50.0f,
2818                 0.0f,
2819                 0.0f,
2820                 "");
2821 
2822   /* add buttons */
2823   if (pid && show_buttons) {
2824     if (GS(pid->name) == ID_MA || (pparent && GS(pparent->name) == ID_MA)) {
2825       if (GS(pid->name) == ID_MA) {
2826         ma = (Material *)pid;
2827       }
2828       else {
2829         ma = (Material *)pparent;
2830       }
2831 
2832       /* Create RNA Pointer */
2833       RNA_pointer_create(&ma->id, &RNA_Material, ma, &material_ptr);
2834 
2835       col = uiLayoutColumn(row, true);
2836       uiLayoutSetScaleX(col, 1.5);
2837       uiItemR(col, &material_ptr, "preview_render_type", UI_ITEM_R_EXPAND, "", ICON_NONE);
2838 
2839       /* EEVEE preview file has baked lighting so use_preview_world has no effect,
2840        * just hide the option until this feature is supported. */
2841       if (!BKE_scene_uses_blender_eevee(CTX_data_scene(C))) {
2842         uiItemS(col);
2843         uiItemR(col, &material_ptr, "use_preview_world", 0, "", ICON_WORLD);
2844       }
2845     }
2846 
2847     if (pr_texture) {
2848       /* Create RNA Pointer */
2849       RNA_pointer_create(id, &RNA_Texture, tex, &texture_ptr);
2850 
2851       uiLayoutRow(layout, true);
2852       uiDefButS(block,
2853                 UI_BTYPE_ROW,
2854                 B_MATPRV,
2855                 IFACE_("Texture"),
2856                 0,
2857                 0,
2858                 UI_UNIT_X * 10,
2859                 UI_UNIT_Y,
2860                 pr_texture,
2861                 10,
2862                 TEX_PR_TEXTURE,
2863                 0,
2864                 0,
2865                 "");
2866       if (GS(parent->name) == ID_MA) {
2867         uiDefButS(block,
2868                   UI_BTYPE_ROW,
2869                   B_MATPRV,
2870                   IFACE_("Material"),
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_LA) {
2883         uiDefButS(block,
2884                   UI_BTYPE_ROW,
2885                   B_MATPRV,
2886                   CTX_IFACE_(BLT_I18NCONTEXT_ID_LIGHT, "Light"),
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_WO) {
2899         uiDefButS(block,
2900                   UI_BTYPE_ROW,
2901                   B_MATPRV,
2902                   IFACE_("World"),
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       else if (GS(parent->name) == ID_LS) {
2915         uiDefButS(block,
2916                   UI_BTYPE_ROW,
2917                   B_MATPRV,
2918                   IFACE_("Line Style"),
2919                   0,
2920                   0,
2921                   UI_UNIT_X * 10,
2922                   UI_UNIT_Y,
2923                   pr_texture,
2924                   10,
2925                   TEX_PR_OTHER,
2926                   0,
2927                   0,
2928                   "");
2929       }
2930       uiDefButS(block,
2931                 UI_BTYPE_ROW,
2932                 B_MATPRV,
2933                 IFACE_("Both"),
2934                 0,
2935                 0,
2936                 UI_UNIT_X * 10,
2937                 UI_UNIT_Y,
2938                 pr_texture,
2939                 10,
2940                 TEX_PR_BOTH,
2941                 0,
2942                 0,
2943                 "");
2944 
2945       /* Alpha button for texture preview */
2946       if (*pr_texture != TEX_PR_OTHER) {
2947         row = uiLayoutRow(layout, false);
2948         uiItemR(row, &texture_ptr, "use_preview_alpha", 0, NULL, ICON_NONE);
2949       }
2950     }
2951   }
2952 }
2953 
2954 /** \} */
2955 
2956 /* -------------------------------------------------------------------- */
2957 /** \name ColorRamp Template
2958  * \{ */
2959 
2960 typedef struct RNAUpdateCb {
2961   PointerRNA ptr;
2962   PropertyRNA *prop;
2963 } RNAUpdateCb;
2964 
rna_update_cb(bContext * C,void * arg_cb,void * UNUSED (arg))2965 static void rna_update_cb(bContext *C, void *arg_cb, void *UNUSED(arg))
2966 {
2967   RNAUpdateCb *cb = (RNAUpdateCb *)arg_cb;
2968 
2969   /* we call update here on the pointer property, this way the
2970    * owner of the curve mapping can still define its own update
2971    * and notifier, even if the CurveMapping struct is shared. */
2972   RNA_property_update(C, &cb->ptr, cb->prop);
2973 }
2974 
2975 enum {
2976   CB_FUNC_FLIP,
2977   CB_FUNC_DISTRIBUTE_LR,
2978   CB_FUNC_DISTRIBUTE_EVENLY,
2979   CB_FUNC_RESET,
2980 };
2981 
colorband_flip_cb(bContext * C,ColorBand * coba)2982 static void colorband_flip_cb(bContext *C, ColorBand *coba)
2983 {
2984   CBData data_tmp[MAXCOLORBAND];
2985 
2986   for (int a = 0; a < coba->tot; a++) {
2987     data_tmp[a] = coba->data[coba->tot - (a + 1)];
2988   }
2989   for (int a = 0; a < coba->tot; a++) {
2990     data_tmp[a].pos = 1.0f - data_tmp[a].pos;
2991     coba->data[a] = data_tmp[a];
2992   }
2993 
2994   /* may as well flip the cur*/
2995   coba->cur = coba->tot - (coba->cur + 1);
2996 
2997   ED_undo_push(C, "Flip Color Ramp");
2998 }
2999 
colorband_distribute_cb(bContext * C,ColorBand * coba,bool evenly)3000 static void colorband_distribute_cb(bContext *C, ColorBand *coba, bool evenly)
3001 {
3002   if (coba->tot > 1) {
3003     const int tot = evenly ? coba->tot - 1 : coba->tot;
3004     const float gap = 1.0f / tot;
3005     float pos = 0.0f;
3006     for (int a = 0; a < coba->tot; a++) {
3007       coba->data[a].pos = pos;
3008       pos += gap;
3009     }
3010     ED_undo_push(C, evenly ? "Distribute Stops Evenly" : "Distribute Stops from Left");
3011   }
3012 }
3013 
colorband_tools_dofunc(bContext * C,void * coba_v,int event)3014 static void colorband_tools_dofunc(bContext *C, void *coba_v, int event)
3015 {
3016   ColorBand *coba = coba_v;
3017 
3018   switch (event) {
3019     case CB_FUNC_FLIP:
3020       colorband_flip_cb(C, coba);
3021       break;
3022     case CB_FUNC_DISTRIBUTE_LR:
3023       colorband_distribute_cb(C, coba, false);
3024       break;
3025     case CB_FUNC_DISTRIBUTE_EVENLY:
3026       colorband_distribute_cb(C, coba, true);
3027       break;
3028     case CB_FUNC_RESET:
3029       BKE_colorband_init(coba, true);
3030       ED_undo_push(C, "Reset Color Ramp");
3031       break;
3032   }
3033   ED_region_tag_redraw(CTX_wm_region(C));
3034 }
3035 
colorband_tools_func(bContext * C,ARegion * region,void * coba_v)3036 static uiBlock *colorband_tools_func(bContext *C, ARegion *region, void *coba_v)
3037 {
3038   const uiStyle *style = UI_style_get_dpi();
3039   ColorBand *coba = coba_v;
3040   short yco = 0;
3041   short menuwidth = 10 * UI_UNIT_X;
3042 
3043   uiBlock *block = UI_block_begin(C, region, __func__, UI_EMBOSS_PULLDOWN);
3044   UI_block_func_butmenu_set(block, colorband_tools_dofunc, coba);
3045 
3046   uiLayout *layout = UI_block_layout(block,
3047                                      UI_LAYOUT_VERTICAL,
3048                                      UI_LAYOUT_MENU,
3049                                      0,
3050                                      0,
3051                                      UI_MENU_WIDTH_MIN,
3052                                      0,
3053                                      UI_MENU_PADDING,
3054                                      style);
3055   UI_block_layout_set_current(block, layout);
3056   {
3057     PointerRNA coba_ptr;
3058     RNA_pointer_create(NULL, &RNA_ColorRamp, coba, &coba_ptr);
3059     uiLayoutSetContextPointer(layout, "color_ramp", &coba_ptr);
3060   }
3061 
3062   /* We could move these to operators,
3063    * although this isn't important unless we want to assign key shortcuts to them. */
3064   {
3065     uiDefIconTextBut(block,
3066                      UI_BTYPE_BUT_MENU,
3067                      1,
3068                      ICON_BLANK1,
3069                      IFACE_("Flip Color Ramp"),
3070                      0,
3071                      yco -= UI_UNIT_Y,
3072                      menuwidth,
3073                      UI_UNIT_Y,
3074                      NULL,
3075                      0.0,
3076                      0.0,
3077                      0,
3078                      CB_FUNC_FLIP,
3079                      "");
3080     uiDefIconTextBut(block,
3081                      UI_BTYPE_BUT_MENU,
3082                      1,
3083                      ICON_BLANK1,
3084                      IFACE_("Distribute Stops from Left"),
3085                      0,
3086                      yco -= UI_UNIT_Y,
3087                      menuwidth,
3088                      UI_UNIT_Y,
3089                      NULL,
3090                      0.0,
3091                      0.0,
3092                      0,
3093                      CB_FUNC_DISTRIBUTE_LR,
3094                      "");
3095     uiDefIconTextBut(block,
3096                      UI_BTYPE_BUT_MENU,
3097                      1,
3098                      ICON_BLANK1,
3099                      IFACE_("Distribute Stops Evenly"),
3100                      0,
3101                      yco -= UI_UNIT_Y,
3102                      menuwidth,
3103                      UI_UNIT_Y,
3104                      NULL,
3105                      0.0,
3106                      0.0,
3107                      0,
3108                      CB_FUNC_DISTRIBUTE_EVENLY,
3109                      "");
3110 
3111     uiItemO(layout, IFACE_("Eyedropper"), ICON_EYEDROPPER, "UI_OT_eyedropper_colorramp");
3112 
3113     uiDefIconTextBut(block,
3114                      UI_BTYPE_BUT_MENU,
3115                      1,
3116                      ICON_BLANK1,
3117                      IFACE_("Reset Color Ramp"),
3118                      0,
3119                      yco -= UI_UNIT_Y,
3120                      menuwidth,
3121                      UI_UNIT_Y,
3122                      NULL,
3123                      0.0,
3124                      0.0,
3125                      0,
3126                      CB_FUNC_RESET,
3127                      "");
3128   }
3129 
3130   UI_block_direction_set(block, UI_DIR_DOWN);
3131   UI_block_bounds_set_text(block, 3.0f * UI_UNIT_X);
3132 
3133   return block;
3134 }
3135 
colorband_add_cb(bContext * C,void * cb_v,void * coba_v)3136 static void colorband_add_cb(bContext *C, void *cb_v, void *coba_v)
3137 {
3138   ColorBand *coba = coba_v;
3139   float pos = 0.5f;
3140 
3141   if (coba->tot > 1) {
3142     if (coba->cur > 0) {
3143       pos = (coba->data[coba->cur - 1].pos + coba->data[coba->cur].pos) * 0.5f;
3144     }
3145     else {
3146       pos = (coba->data[coba->cur + 1].pos + coba->data[coba->cur].pos) * 0.5f;
3147     }
3148   }
3149 
3150   if (BKE_colorband_element_add(coba, pos)) {
3151     rna_update_cb(C, cb_v, NULL);
3152     ED_undo_push(C, "Add Color Ramp Stop");
3153   }
3154 }
3155 
colorband_del_cb(bContext * C,void * cb_v,void * coba_v)3156 static void colorband_del_cb(bContext *C, void *cb_v, void *coba_v)
3157 {
3158   ColorBand *coba = coba_v;
3159 
3160   if (BKE_colorband_element_remove(coba, coba->cur)) {
3161     ED_undo_push(C, "Delete Color Ramp Stop");
3162     rna_update_cb(C, cb_v, NULL);
3163   }
3164 }
3165 
colorband_update_cb(bContext * UNUSED (C),void * bt_v,void * coba_v)3166 static void colorband_update_cb(bContext *UNUSED(C), void *bt_v, void *coba_v)
3167 {
3168   uiBut *bt = bt_v;
3169   ColorBand *coba = coba_v;
3170 
3171   /* Sneaky update here, we need to sort the color-band points to be in order,
3172    * however the RNA pointer then is wrong, so we update it */
3173   BKE_colorband_update_sort(coba);
3174   bt->rnapoin.data = coba->data + coba->cur;
3175 }
3176 
colorband_buttons_layout(uiLayout * layout,uiBlock * block,ColorBand * coba,const rctf * butr,RNAUpdateCb * cb,int expand)3177 static void colorband_buttons_layout(uiLayout *layout,
3178                                      uiBlock *block,
3179                                      ColorBand *coba,
3180                                      const rctf *butr,
3181                                      RNAUpdateCb *cb,
3182                                      int expand)
3183 {
3184   uiBut *bt;
3185   const float unit = BLI_rctf_size_x(butr) / 14.0f;
3186   const float xs = butr->xmin;
3187   const float ys = butr->ymin;
3188 
3189   PointerRNA ptr;
3190   RNA_pointer_create(cb->ptr.owner_id, &RNA_ColorRamp, coba, &ptr);
3191 
3192   uiLayout *split = uiLayoutSplit(layout, 0.4f, false);
3193 
3194   UI_block_emboss_set(block, UI_EMBOSS_NONE);
3195   UI_block_align_begin(block);
3196   uiLayout *row = uiLayoutRow(split, false);
3197 
3198   bt = uiDefIconTextBut(block,
3199                         UI_BTYPE_BUT,
3200                         0,
3201                         ICON_ADD,
3202                         "",
3203                         0,
3204                         0,
3205                         2.0f * unit,
3206                         UI_UNIT_Y,
3207                         NULL,
3208                         0,
3209                         0,
3210                         0,
3211                         0,
3212                         TIP_("Add a new color stop to the color ramp"));
3213   UI_but_funcN_set(bt, colorband_add_cb, MEM_dupallocN(cb), coba);
3214 
3215   bt = uiDefIconTextBut(block,
3216                         UI_BTYPE_BUT,
3217                         0,
3218                         ICON_REMOVE,
3219                         "",
3220                         xs + 2.0f * unit,
3221                         ys + UI_UNIT_Y,
3222                         2.0f * unit,
3223                         UI_UNIT_Y,
3224                         NULL,
3225                         0,
3226                         0,
3227                         0,
3228                         0,
3229                         TIP_("Delete the active position"));
3230   UI_but_funcN_set(bt, colorband_del_cb, MEM_dupallocN(cb), coba);
3231 
3232   bt = uiDefIconBlockBut(block,
3233                          colorband_tools_func,
3234                          coba,
3235                          0,
3236                          ICON_DOWNARROW_HLT,
3237                          xs + 4.0f * unit,
3238                          ys + UI_UNIT_Y,
3239                          2.0f * unit,
3240                          UI_UNIT_Y,
3241                          TIP_("Tools"));
3242   UI_but_funcN_set(bt, rna_update_cb, MEM_dupallocN(cb), coba);
3243 
3244   UI_block_align_end(block);
3245   UI_block_emboss_set(block, UI_EMBOSS);
3246 
3247   row = uiLayoutRow(split, false);
3248 
3249   UI_block_align_begin(block);
3250   uiItemR(row, &ptr, "color_mode", 0, "", ICON_NONE);
3251   if (ELEM(coba->color_mode, COLBAND_BLEND_HSV, COLBAND_BLEND_HSL)) {
3252     uiItemR(row, &ptr, "hue_interpolation", 0, "", ICON_NONE);
3253   }
3254   else { /* COLBAND_BLEND_RGB */
3255     uiItemR(row, &ptr, "interpolation", 0, "", ICON_NONE);
3256   }
3257   UI_block_align_end(block);
3258 
3259   row = uiLayoutRow(layout, false);
3260 
3261   bt = uiDefBut(block,
3262                 UI_BTYPE_COLORBAND,
3263                 0,
3264                 "",
3265                 xs,
3266                 ys,
3267                 BLI_rctf_size_x(butr),
3268                 UI_UNIT_Y,
3269                 coba,
3270                 0,
3271                 0,
3272                 0,
3273                 0,
3274                 "");
3275   UI_but_funcN_set(bt, rna_update_cb, MEM_dupallocN(cb), NULL);
3276 
3277   row = uiLayoutRow(layout, false);
3278 
3279   if (coba->tot) {
3280     CBData *cbd = coba->data + coba->cur;
3281 
3282     RNA_pointer_create(cb->ptr.owner_id, &RNA_ColorRampElement, cbd, &ptr);
3283 
3284     if (!expand) {
3285       split = uiLayoutSplit(layout, 0.3f, false);
3286 
3287       row = uiLayoutRow(split, false);
3288       bt = uiDefButS(block,
3289                      UI_BTYPE_NUM,
3290                      0,
3291                      "",
3292                      0,
3293                      0,
3294                      5.0f * UI_UNIT_X,
3295                      UI_UNIT_Y,
3296                      &coba->cur,
3297                      0.0,
3298                      (float)(MAX2(0, coba->tot - 1)),
3299                      0,
3300                      0,
3301                      TIP_("Choose active color stop"));
3302       UI_but_number_step_size_set(bt, 1);
3303 
3304       row = uiLayoutRow(split, false);
3305       uiItemR(row, &ptr, "position", 0, IFACE_("Pos"), ICON_NONE);
3306       bt = block->buttons.last;
3307       bt->a1 = 1.0f; /* gives a bit more precision for modifying position */
3308       UI_but_func_set(bt, colorband_update_cb, bt, coba);
3309 
3310       row = uiLayoutRow(layout, false);
3311       uiItemR(row, &ptr, "color", 0, "", ICON_NONE);
3312       bt = block->buttons.last;
3313       UI_but_funcN_set(bt, rna_update_cb, MEM_dupallocN(cb), NULL);
3314     }
3315     else {
3316       split = uiLayoutSplit(layout, 0.5f, false);
3317       uiLayout *subsplit = uiLayoutSplit(split, 0.35f, false);
3318 
3319       row = uiLayoutRow(subsplit, false);
3320       bt = uiDefButS(block,
3321                      UI_BTYPE_NUM,
3322                      0,
3323                      "",
3324                      0,
3325                      0,
3326                      5.0f * UI_UNIT_X,
3327                      UI_UNIT_Y,
3328                      &coba->cur,
3329                      0.0,
3330                      (float)(MAX2(0, coba->tot - 1)),
3331                      0,
3332                      0,
3333                      TIP_("Choose active color stop"));
3334       UI_but_number_step_size_set(bt, 1);
3335 
3336       row = uiLayoutRow(subsplit, false);
3337       uiItemR(row, &ptr, "position", UI_ITEM_R_SLIDER, IFACE_("Pos"), ICON_NONE);
3338       bt = block->buttons.last;
3339       bt->a1 = 1.0f; /* gives a bit more precision for modifying position */
3340       UI_but_func_set(bt, colorband_update_cb, bt, coba);
3341 
3342       row = uiLayoutRow(split, false);
3343       uiItemR(row, &ptr, "color", 0, "", ICON_NONE);
3344       bt = block->buttons.last;
3345       UI_but_funcN_set(bt, rna_update_cb, MEM_dupallocN(cb), NULL);
3346     }
3347   }
3348 }
3349 
uiTemplateColorRamp(uiLayout * layout,PointerRNA * ptr,const char * propname,bool expand)3350 void uiTemplateColorRamp(uiLayout *layout, PointerRNA *ptr, const char *propname, bool expand)
3351 {
3352   PropertyRNA *prop = RNA_struct_find_property(ptr, propname);
3353 
3354   if (!prop || RNA_property_type(prop) != PROP_POINTER) {
3355     return;
3356   }
3357 
3358   PointerRNA cptr = RNA_property_pointer_get(ptr, prop);
3359   if (!cptr.data || !RNA_struct_is_a(cptr.type, &RNA_ColorRamp)) {
3360     return;
3361   }
3362 
3363   RNAUpdateCb *cb = MEM_callocN(sizeof(RNAUpdateCb), "RNAUpdateCb");
3364   cb->ptr = *ptr;
3365   cb->prop = prop;
3366 
3367   rctf rect;
3368   rect.xmin = 0;
3369   rect.xmax = 10.0f * UI_UNIT_X;
3370   rect.ymin = 0;
3371   rect.ymax = 19.5f * UI_UNIT_X;
3372 
3373   uiBlock *block = uiLayoutAbsoluteBlock(layout);
3374 
3375   ID *id = cptr.owner_id;
3376   UI_block_lock_set(block, (id && ID_IS_LINKED(id)), ERROR_LIBDATA_MESSAGE);
3377 
3378   colorband_buttons_layout(layout, block, cptr.data, &rect, cb, expand);
3379 
3380   UI_block_lock_clear(block);
3381 
3382   MEM_freeN(cb);
3383 }
3384 
3385 /** \} */
3386 
3387 /* -------------------------------------------------------------------- */
3388 /** \name Icon Template
3389  * \{ */
3390 
3391 /**
3392  * \param icon_scale: Scale of the icon, 1x == button height.
3393  */
uiTemplateIcon(uiLayout * layout,int icon_value,float icon_scale)3394 void uiTemplateIcon(uiLayout *layout, int icon_value, float icon_scale)
3395 {
3396   uiBlock *block = uiLayoutAbsoluteBlock(layout);
3397   uiBut *but = uiDefIconBut(block,
3398                             UI_BTYPE_LABEL,
3399                             0,
3400                             ICON_X,
3401                             0,
3402                             0,
3403                             UI_UNIT_X * icon_scale,
3404                             UI_UNIT_Y * icon_scale,
3405                             NULL,
3406                             0.0,
3407                             0.0,
3408                             0.0,
3409                             0.0,
3410                             "");
3411   ui_def_but_icon(but, icon_value, UI_HAS_ICON | UI_BUT_ICON_PREVIEW);
3412 }
3413 
3414 /** \} */
3415 
3416 /* -------------------------------------------------------------------- */
3417 /** \name Icon viewer Template
3418  * \{ */
3419 
3420 typedef struct IconViewMenuArgs {
3421   PointerRNA ptr;
3422   PropertyRNA *prop;
3423   bool show_labels;
3424   float icon_scale;
3425 } IconViewMenuArgs;
3426 
3427 /* ID Search browse menu, open */
ui_icon_view_menu_cb(bContext * C,ARegion * region,void * arg_litem)3428 static uiBlock *ui_icon_view_menu_cb(bContext *C, ARegion *region, void *arg_litem)
3429 {
3430   static IconViewMenuArgs args;
3431 
3432   /* arg_litem is malloced, can be freed by parent button */
3433   args = *((IconViewMenuArgs *)arg_litem);
3434   int w = UI_UNIT_X * (args.icon_scale);
3435   int h = UI_UNIT_X * (args.icon_scale + args.show_labels);
3436 
3437   uiBlock *block = UI_block_begin(C, region, "_popup", UI_EMBOSS_PULLDOWN);
3438   UI_block_flag_enable(block, UI_BLOCK_LOOP | UI_BLOCK_NO_FLIP);
3439   UI_block_theme_style_set(block, UI_BLOCK_THEME_STYLE_POPUP);
3440 
3441   bool free;
3442   const EnumPropertyItem *item;
3443   RNA_property_enum_items(C, &args.ptr, args.prop, &item, NULL, &free);
3444 
3445   for (int a = 0; item[a].identifier; a++) {
3446     int x = (a % 8) * w;
3447     int y = -(a / 8) * h;
3448 
3449     int icon = item[a].icon;
3450     int value = item[a].value;
3451     uiBut *but;
3452     if (args.show_labels) {
3453       but = uiDefIconTextButR_prop(block,
3454                                    UI_BTYPE_ROW,
3455                                    0,
3456                                    icon,
3457                                    item[a].name,
3458                                    x,
3459                                    y,
3460                                    w,
3461                                    h,
3462                                    &args.ptr,
3463                                    args.prop,
3464                                    -1,
3465                                    0,
3466                                    value,
3467                                    -1,
3468                                    -1,
3469                                    NULL);
3470     }
3471     else {
3472       but = uiDefIconButR_prop(block,
3473                                UI_BTYPE_ROW,
3474                                0,
3475                                icon,
3476                                x,
3477                                y,
3478                                w,
3479                                h,
3480                                &args.ptr,
3481                                args.prop,
3482                                -1,
3483                                0,
3484                                value,
3485                                -1,
3486                                -1,
3487                                NULL);
3488     }
3489     ui_def_but_icon(but, icon, UI_HAS_ICON | UI_BUT_ICON_PREVIEW);
3490   }
3491 
3492   UI_block_bounds_set_normal(block, 0.3f * U.widget_unit);
3493   UI_block_direction_set(block, UI_DIR_DOWN);
3494 
3495   if (free) {
3496     MEM_freeN((void *)item);
3497   }
3498 
3499   return block;
3500 }
3501 
3502 /**
3503  * \param icon_scale: Scale of the icon, 1x == button height.
3504  */
uiTemplateIconView(uiLayout * layout,PointerRNA * ptr,const char * propname,bool show_labels,float icon_scale,float icon_scale_popup)3505 void uiTemplateIconView(uiLayout *layout,
3506                         PointerRNA *ptr,
3507                         const char *propname,
3508                         bool show_labels,
3509                         float icon_scale,
3510                         float icon_scale_popup)
3511 {
3512   PropertyRNA *prop = RNA_struct_find_property(ptr, propname);
3513 
3514   if (!prop || RNA_property_type(prop) != PROP_ENUM) {
3515     RNA_warning(
3516         "property of type Enum not found: %s.%s", RNA_struct_identifier(ptr->type), propname);
3517     return;
3518   }
3519 
3520   uiBlock *block = uiLayoutAbsoluteBlock(layout);
3521 
3522   int tot_items;
3523   bool free_items;
3524   const EnumPropertyItem *items;
3525   RNA_property_enum_items(block->evil_C, ptr, prop, &items, &tot_items, &free_items);
3526   int value = RNA_property_enum_get(ptr, prop);
3527   int icon = ICON_NONE;
3528   RNA_enum_icon_from_value(items, value, &icon);
3529 
3530   uiBut *but;
3531   if (RNA_property_editable(ptr, prop)) {
3532     IconViewMenuArgs *cb_args = MEM_callocN(sizeof(IconViewMenuArgs), __func__);
3533     cb_args->ptr = *ptr;
3534     cb_args->prop = prop;
3535     cb_args->show_labels = show_labels;
3536     cb_args->icon_scale = icon_scale_popup;
3537 
3538     but = uiDefBlockButN(block,
3539                          ui_icon_view_menu_cb,
3540                          cb_args,
3541                          "",
3542                          0,
3543                          0,
3544                          UI_UNIT_X * icon_scale,
3545                          UI_UNIT_Y * icon_scale,
3546                          "");
3547   }
3548   else {
3549     but = uiDefIconBut(block,
3550                        UI_BTYPE_LABEL,
3551                        0,
3552                        ICON_X,
3553                        0,
3554                        0,
3555                        UI_UNIT_X * icon_scale,
3556                        UI_UNIT_Y * icon_scale,
3557                        NULL,
3558                        0.0,
3559                        0.0,
3560                        0.0,
3561                        0.0,
3562                        "");
3563   }
3564 
3565   ui_def_but_icon(but, icon, UI_HAS_ICON | UI_BUT_ICON_PREVIEW);
3566 
3567   if (free_items) {
3568     MEM_freeN((void *)items);
3569   }
3570 }
3571 
3572 /** \} */
3573 
3574 /* -------------------------------------------------------------------- */
3575 /** \name Histogram Template
3576  * \{ */
3577 
uiTemplateHistogram(uiLayout * layout,PointerRNA * ptr,const char * propname)3578 void uiTemplateHistogram(uiLayout *layout, PointerRNA *ptr, const char *propname)
3579 {
3580   PropertyRNA *prop = RNA_struct_find_property(ptr, propname);
3581 
3582   if (!prop || RNA_property_type(prop) != PROP_POINTER) {
3583     return;
3584   }
3585 
3586   PointerRNA cptr = RNA_property_pointer_get(ptr, prop);
3587   if (!cptr.data || !RNA_struct_is_a(cptr.type, &RNA_Histogram)) {
3588     return;
3589   }
3590   Histogram *hist = (Histogram *)cptr.data;
3591 
3592   if (hist->height < UI_UNIT_Y) {
3593     hist->height = UI_UNIT_Y;
3594   }
3595   else if (hist->height > UI_UNIT_Y * 20) {
3596     hist->height = UI_UNIT_Y * 20;
3597   }
3598 
3599   uiLayout *col = uiLayoutColumn(layout, true);
3600   uiBlock *block = uiLayoutGetBlock(col);
3601 
3602   uiDefBut(
3603       block, UI_BTYPE_HISTOGRAM, 0, "", 0, 0, UI_UNIT_X * 10, hist->height, hist, 0, 0, 0, 0, "");
3604 
3605   /* Resize grip. */
3606   uiDefIconButI(block,
3607                 UI_BTYPE_GRIP,
3608                 0,
3609                 ICON_GRIP,
3610                 0,
3611                 0,
3612                 UI_UNIT_X * 10,
3613                 (short)(UI_UNIT_Y * 0.3f),
3614                 &hist->height,
3615                 UI_UNIT_Y,
3616                 UI_UNIT_Y * 20.0f,
3617                 0.0f,
3618                 0.0f,
3619                 "");
3620 }
3621 
3622 /** \} */
3623 
3624 /* -------------------------------------------------------------------- */
3625 /** \name Waveform Template
3626  * \{ */
3627 
uiTemplateWaveform(uiLayout * layout,PointerRNA * ptr,const char * propname)3628 void uiTemplateWaveform(uiLayout *layout, PointerRNA *ptr, const char *propname)
3629 {
3630   PropertyRNA *prop = RNA_struct_find_property(ptr, propname);
3631 
3632   if (!prop || RNA_property_type(prop) != PROP_POINTER) {
3633     return;
3634   }
3635 
3636   PointerRNA cptr = RNA_property_pointer_get(ptr, prop);
3637   if (!cptr.data || !RNA_struct_is_a(cptr.type, &RNA_Scopes)) {
3638     return;
3639   }
3640   Scopes *scopes = (Scopes *)cptr.data;
3641 
3642   uiLayout *col = uiLayoutColumn(layout, true);
3643   uiBlock *block = uiLayoutGetBlock(col);
3644 
3645   if (scopes->wavefrm_height < UI_UNIT_Y) {
3646     scopes->wavefrm_height = UI_UNIT_Y;
3647   }
3648   else if (scopes->wavefrm_height > UI_UNIT_Y * 20) {
3649     scopes->wavefrm_height = UI_UNIT_Y * 20;
3650   }
3651 
3652   uiDefBut(block,
3653            UI_BTYPE_WAVEFORM,
3654            0,
3655            "",
3656            0,
3657            0,
3658            UI_UNIT_X * 10,
3659            scopes->wavefrm_height,
3660            scopes,
3661            0,
3662            0,
3663            0,
3664            0,
3665            "");
3666 
3667   /* Resize grip. */
3668   uiDefIconButI(block,
3669                 UI_BTYPE_GRIP,
3670                 0,
3671                 ICON_GRIP,
3672                 0,
3673                 0,
3674                 UI_UNIT_X * 10,
3675                 (short)(UI_UNIT_Y * 0.3f),
3676                 &scopes->wavefrm_height,
3677                 UI_UNIT_Y,
3678                 UI_UNIT_Y * 20.0f,
3679                 0.0f,
3680                 0.0f,
3681                 "");
3682 }
3683 
3684 /** \} */
3685 
3686 /* -------------------------------------------------------------------- */
3687 /** \name Vector-Scope Template
3688  * \{ */
3689 
uiTemplateVectorscope(uiLayout * layout,PointerRNA * ptr,const char * propname)3690 void uiTemplateVectorscope(uiLayout *layout, PointerRNA *ptr, const char *propname)
3691 {
3692   PropertyRNA *prop = RNA_struct_find_property(ptr, propname);
3693 
3694   if (!prop || RNA_property_type(prop) != PROP_POINTER) {
3695     return;
3696   }
3697 
3698   PointerRNA cptr = RNA_property_pointer_get(ptr, prop);
3699   if (!cptr.data || !RNA_struct_is_a(cptr.type, &RNA_Scopes)) {
3700     return;
3701   }
3702   Scopes *scopes = (Scopes *)cptr.data;
3703 
3704   if (scopes->vecscope_height < UI_UNIT_Y) {
3705     scopes->vecscope_height = UI_UNIT_Y;
3706   }
3707   else if (scopes->vecscope_height > UI_UNIT_Y * 20) {
3708     scopes->vecscope_height = UI_UNIT_Y * 20;
3709   }
3710 
3711   uiLayout *col = uiLayoutColumn(layout, true);
3712   uiBlock *block = uiLayoutGetBlock(col);
3713 
3714   uiDefBut(block,
3715            UI_BTYPE_VECTORSCOPE,
3716            0,
3717            "",
3718            0,
3719            0,
3720            UI_UNIT_X * 10,
3721            scopes->vecscope_height,
3722            scopes,
3723            0,
3724            0,
3725            0,
3726            0,
3727            "");
3728 
3729   /* Resize grip. */
3730   uiDefIconButI(block,
3731                 UI_BTYPE_GRIP,
3732                 0,
3733                 ICON_GRIP,
3734                 0,
3735                 0,
3736                 UI_UNIT_X * 10,
3737                 (short)(UI_UNIT_Y * 0.3f),
3738                 &scopes->vecscope_height,
3739                 UI_UNIT_Y,
3740                 UI_UNIT_Y * 20.0f,
3741                 0.0f,
3742                 0.0f,
3743                 "");
3744 }
3745 
3746 /** \} */
3747 
3748 /* -------------------------------------------------------------------- */
3749 /** \name CurveMapping Template
3750  * \{ */
3751 
curvemap_buttons_zoom_in(bContext * C,void * cumap_v,void * UNUSED (arg))3752 static void curvemap_buttons_zoom_in(bContext *C, void *cumap_v, void *UNUSED(arg))
3753 {
3754   CurveMapping *cumap = cumap_v;
3755 
3756   /* we allow 20 times zoom */
3757   if (BLI_rctf_size_x(&cumap->curr) > 0.04f * BLI_rctf_size_x(&cumap->clipr)) {
3758     const float dx = 0.1154f * BLI_rctf_size_x(&cumap->curr);
3759     cumap->curr.xmin += dx;
3760     cumap->curr.xmax -= dx;
3761     const float dy = 0.1154f * BLI_rctf_size_y(&cumap->curr);
3762     cumap->curr.ymin += dy;
3763     cumap->curr.ymax -= dy;
3764   }
3765 
3766   ED_region_tag_redraw(CTX_wm_region(C));
3767 }
3768 
curvemap_buttons_zoom_out(bContext * C,void * cumap_v,void * UNUSED (unused))3769 static void curvemap_buttons_zoom_out(bContext *C, void *cumap_v, void *UNUSED(unused))
3770 {
3771   CurveMapping *cumap = cumap_v;
3772   float d, d1;
3773 
3774   /* we allow 20 times zoom, but don't view outside clip */
3775   if (BLI_rctf_size_x(&cumap->curr) < 20.0f * BLI_rctf_size_x(&cumap->clipr)) {
3776     d = d1 = 0.15f * BLI_rctf_size_x(&cumap->curr);
3777 
3778     if (cumap->flag & CUMA_DO_CLIP) {
3779       if (cumap->curr.xmin - d < cumap->clipr.xmin) {
3780         d1 = cumap->curr.xmin - cumap->clipr.xmin;
3781       }
3782     }
3783     cumap->curr.xmin -= d1;
3784 
3785     d1 = d;
3786     if (cumap->flag & CUMA_DO_CLIP) {
3787       if (cumap->curr.xmax + d > cumap->clipr.xmax) {
3788         d1 = -cumap->curr.xmax + cumap->clipr.xmax;
3789       }
3790     }
3791     cumap->curr.xmax += d1;
3792 
3793     d = d1 = 0.15f * BLI_rctf_size_y(&cumap->curr);
3794 
3795     if (cumap->flag & CUMA_DO_CLIP) {
3796       if (cumap->curr.ymin - d < cumap->clipr.ymin) {
3797         d1 = cumap->curr.ymin - cumap->clipr.ymin;
3798       }
3799     }
3800     cumap->curr.ymin -= d1;
3801 
3802     d1 = d;
3803     if (cumap->flag & CUMA_DO_CLIP) {
3804       if (cumap->curr.ymax + d > cumap->clipr.ymax) {
3805         d1 = -cumap->curr.ymax + cumap->clipr.ymax;
3806       }
3807     }
3808     cumap->curr.ymax += d1;
3809   }
3810 
3811   ED_region_tag_redraw(CTX_wm_region(C));
3812 }
3813 
curvemap_buttons_setclip(bContext * UNUSED (C),void * cumap_v,void * UNUSED (arg))3814 static void curvemap_buttons_setclip(bContext *UNUSED(C), void *cumap_v, void *UNUSED(arg))
3815 {
3816   CurveMapping *cumap = cumap_v;
3817 
3818   BKE_curvemapping_changed(cumap, false);
3819 }
3820 
curvemap_buttons_delete(bContext * C,void * cb_v,void * cumap_v)3821 static void curvemap_buttons_delete(bContext *C, void *cb_v, void *cumap_v)
3822 {
3823   CurveMapping *cumap = cumap_v;
3824 
3825   BKE_curvemap_remove(cumap->cm + cumap->cur, SELECT);
3826   BKE_curvemapping_changed(cumap, false);
3827 
3828   rna_update_cb(C, cb_v, NULL);
3829 }
3830 
3831 /* NOTE: this is a block-menu, needs 0 events, otherwise the menu closes */
curvemap_clipping_func(bContext * C,ARegion * region,void * cumap_v)3832 static uiBlock *curvemap_clipping_func(bContext *C, ARegion *region, void *cumap_v)
3833 {
3834   CurveMapping *cumap = cumap_v;
3835   uiBut *bt;
3836   const float width = 8 * UI_UNIT_X;
3837 
3838   uiBlock *block = UI_block_begin(C, region, __func__, UI_EMBOSS);
3839   UI_block_flag_enable(block, UI_BLOCK_KEEP_OPEN | UI_BLOCK_MOVEMOUSE_QUIT);
3840   UI_block_theme_style_set(block, UI_BLOCK_THEME_STYLE_POPUP);
3841 
3842   bt = uiDefButBitI(block,
3843                     UI_BTYPE_CHECKBOX,
3844                     CUMA_DO_CLIP,
3845                     1,
3846                     IFACE_("Use Clipping"),
3847                     0,
3848                     5 * UI_UNIT_Y,
3849                     width,
3850                     UI_UNIT_Y,
3851                     &cumap->flag,
3852                     0.0,
3853                     0.0,
3854                     10,
3855                     0,
3856                     "");
3857   UI_but_func_set(bt, curvemap_buttons_setclip, cumap, NULL);
3858 
3859   UI_block_align_begin(block);
3860   bt = uiDefButF(block,
3861                  UI_BTYPE_NUM,
3862                  0,
3863                  IFACE_("Min X:"),
3864                  0,
3865                  4 * UI_UNIT_Y,
3866                  width,
3867                  UI_UNIT_Y,
3868                  &cumap->clipr.xmin,
3869                  -100.0,
3870                  cumap->clipr.xmax,
3871                  0,
3872                  0,
3873                  "");
3874   UI_but_number_step_size_set(bt, 10);
3875   UI_but_number_precision_set(bt, 2);
3876   bt = uiDefButF(block,
3877                  UI_BTYPE_NUM,
3878                  0,
3879                  IFACE_("Min Y:"),
3880                  0,
3881                  3 * UI_UNIT_Y,
3882                  width,
3883                  UI_UNIT_Y,
3884                  &cumap->clipr.ymin,
3885                  -100.0,
3886                  cumap->clipr.ymax,
3887                  0,
3888                  0,
3889                  "");
3890   UI_but_number_step_size_set(bt, 10);
3891   UI_but_number_precision_set(bt, 2);
3892   bt = uiDefButF(block,
3893                  UI_BTYPE_NUM,
3894                  0,
3895                  IFACE_("Max X:"),
3896                  0,
3897                  2 * UI_UNIT_Y,
3898                  width,
3899                  UI_UNIT_Y,
3900                  &cumap->clipr.xmax,
3901                  cumap->clipr.xmin,
3902                  100.0,
3903                  0,
3904                  0,
3905                  "");
3906   UI_but_number_step_size_set(bt, 10);
3907   UI_but_number_precision_set(bt, 2);
3908   bt = uiDefButF(block,
3909                  UI_BTYPE_NUM,
3910                  0,
3911                  IFACE_("Max Y:"),
3912                  0,
3913                  UI_UNIT_Y,
3914                  width,
3915                  UI_UNIT_Y,
3916                  &cumap->clipr.ymax,
3917                  cumap->clipr.ymin,
3918                  100.0,
3919                  0,
3920                  0,
3921                  "");
3922   UI_but_number_step_size_set(bt, 10);
3923   UI_but_number_precision_set(bt, 2);
3924 
3925   UI_block_bounds_set_normal(block, 0.3f * U.widget_unit);
3926   UI_block_direction_set(block, UI_DIR_DOWN);
3927 
3928   return block;
3929 }
3930 
3931 /* only for BKE_curvemap_tools_dofunc */
3932 enum {
3933   UICURVE_FUNC_RESET_NEG,
3934   UICURVE_FUNC_RESET_POS,
3935   UICURVE_FUNC_RESET_VIEW,
3936   UICURVE_FUNC_HANDLE_VECTOR,
3937   UICURVE_FUNC_HANDLE_AUTO,
3938   UICURVE_FUNC_HANDLE_AUTO_ANIM,
3939   UICURVE_FUNC_EXTEND_HOZ,
3940   UICURVE_FUNC_EXTEND_EXP,
3941 };
3942 
curvemap_tools_dofunc(bContext * C,void * cumap_v,int event)3943 static void curvemap_tools_dofunc(bContext *C, void *cumap_v, int event)
3944 {
3945   CurveMapping *cumap = cumap_v;
3946   CurveMap *cuma = cumap->cm + cumap->cur;
3947 
3948   switch (event) {
3949     case UICURVE_FUNC_RESET_NEG:
3950     case UICURVE_FUNC_RESET_POS: /* reset */
3951       BKE_curvemap_reset(cuma,
3952                          &cumap->clipr,
3953                          cumap->preset,
3954                          (event == UICURVE_FUNC_RESET_NEG) ? CURVEMAP_SLOPE_NEGATIVE :
3955                                                              CURVEMAP_SLOPE_POSITIVE);
3956       BKE_curvemapping_changed(cumap, false);
3957       break;
3958     case UICURVE_FUNC_RESET_VIEW:
3959       cumap->curr = cumap->clipr;
3960       break;
3961     case UICURVE_FUNC_HANDLE_VECTOR: /* set vector */
3962       BKE_curvemap_handle_set(cuma, HD_VECT);
3963       BKE_curvemapping_changed(cumap, false);
3964       break;
3965     case UICURVE_FUNC_HANDLE_AUTO: /* set auto */
3966       BKE_curvemap_handle_set(cuma, HD_AUTO);
3967       BKE_curvemapping_changed(cumap, false);
3968       break;
3969     case UICURVE_FUNC_HANDLE_AUTO_ANIM: /* set auto-clamped */
3970       BKE_curvemap_handle_set(cuma, HD_AUTO_ANIM);
3971       BKE_curvemapping_changed(cumap, false);
3972       break;
3973     case UICURVE_FUNC_EXTEND_HOZ: /* extend horiz */
3974       cumap->flag &= ~CUMA_EXTEND_EXTRAPOLATE;
3975       BKE_curvemapping_changed(cumap, false);
3976       break;
3977     case UICURVE_FUNC_EXTEND_EXP: /* extend extrapolate */
3978       cumap->flag |= CUMA_EXTEND_EXTRAPOLATE;
3979       BKE_curvemapping_changed(cumap, false);
3980       break;
3981   }
3982   ED_undo_push(C, "CurveMap tools");
3983   ED_region_tag_redraw(CTX_wm_region(C));
3984 }
3985 
curvemap_tools_func(bContext * C,ARegion * region,CurveMapping * cumap,bool show_extend,int reset_mode)3986 static uiBlock *curvemap_tools_func(
3987     bContext *C, ARegion *region, CurveMapping *cumap, bool show_extend, int reset_mode)
3988 {
3989   short yco = 0;
3990   const short menuwidth = 10 * UI_UNIT_X;
3991 
3992   uiBlock *block = UI_block_begin(C, region, __func__, UI_EMBOSS);
3993   UI_block_func_butmenu_set(block, curvemap_tools_dofunc, cumap);
3994 
3995   {
3996     uiDefIconTextBut(block,
3997                      UI_BTYPE_BUT_MENU,
3998                      1,
3999                      ICON_BLANK1,
4000                      IFACE_("Reset View"),
4001                      0,
4002                      yco -= UI_UNIT_Y,
4003                      menuwidth,
4004                      UI_UNIT_Y,
4005                      NULL,
4006                      0.0,
4007                      0.0,
4008                      0,
4009                      UICURVE_FUNC_RESET_VIEW,
4010                      "");
4011     uiDefIconTextBut(block,
4012                      UI_BTYPE_BUT_MENU,
4013                      1,
4014                      ICON_BLANK1,
4015                      IFACE_("Vector Handle"),
4016                      0,
4017                      yco -= UI_UNIT_Y,
4018                      menuwidth,
4019                      UI_UNIT_Y,
4020                      NULL,
4021                      0.0,
4022                      0.0,
4023                      0,
4024                      UICURVE_FUNC_HANDLE_VECTOR,
4025                      "");
4026     uiDefIconTextBut(block,
4027                      UI_BTYPE_BUT_MENU,
4028                      1,
4029                      ICON_BLANK1,
4030                      IFACE_("Auto Handle"),
4031                      0,
4032                      yco -= UI_UNIT_Y,
4033                      menuwidth,
4034                      UI_UNIT_Y,
4035                      NULL,
4036                      0.0,
4037                      0.0,
4038                      0,
4039                      UICURVE_FUNC_HANDLE_AUTO,
4040                      "");
4041     uiDefIconTextBut(block,
4042                      UI_BTYPE_BUT_MENU,
4043                      1,
4044                      ICON_BLANK1,
4045                      IFACE_("Auto Clamped Handle"),
4046                      0,
4047                      yco -= UI_UNIT_Y,
4048                      menuwidth,
4049                      UI_UNIT_Y,
4050                      NULL,
4051                      0.0,
4052                      0.0,
4053                      0,
4054                      UICURVE_FUNC_HANDLE_AUTO_ANIM,
4055                      "");
4056   }
4057 
4058   if (show_extend) {
4059     uiDefIconTextBut(block,
4060                      UI_BTYPE_BUT_MENU,
4061                      1,
4062                      ICON_BLANK1,
4063                      IFACE_("Extend Horizontal"),
4064                      0,
4065                      yco -= UI_UNIT_Y,
4066                      menuwidth,
4067                      UI_UNIT_Y,
4068                      NULL,
4069                      0.0,
4070                      0.0,
4071                      0,
4072                      UICURVE_FUNC_EXTEND_HOZ,
4073                      "");
4074     uiDefIconTextBut(block,
4075                      UI_BTYPE_BUT_MENU,
4076                      1,
4077                      ICON_BLANK1,
4078                      IFACE_("Extend Extrapolated"),
4079                      0,
4080                      yco -= UI_UNIT_Y,
4081                      menuwidth,
4082                      UI_UNIT_Y,
4083                      NULL,
4084                      0.0,
4085                      0.0,
4086                      0,
4087                      UICURVE_FUNC_EXTEND_EXP,
4088                      "");
4089   }
4090 
4091   {
4092     uiDefIconTextBut(block,
4093                      UI_BTYPE_BUT_MENU,
4094                      1,
4095                      ICON_BLANK1,
4096                      IFACE_("Reset Curve"),
4097                      0,
4098                      yco -= UI_UNIT_Y,
4099                      menuwidth,
4100                      UI_UNIT_Y,
4101                      NULL,
4102                      0.0,
4103                      0.0,
4104                      0,
4105                      reset_mode,
4106                      "");
4107   }
4108 
4109   UI_block_direction_set(block, UI_DIR_DOWN);
4110   UI_block_bounds_set_text(block, 3.0f * UI_UNIT_X);
4111 
4112   return block;
4113 }
4114 
curvemap_tools_posslope_func(bContext * C,ARegion * region,void * cumap_v)4115 static uiBlock *curvemap_tools_posslope_func(bContext *C, ARegion *region, void *cumap_v)
4116 {
4117   return curvemap_tools_func(C, region, cumap_v, true, UICURVE_FUNC_RESET_POS);
4118 }
4119 
curvemap_tools_negslope_func(bContext * C,ARegion * region,void * cumap_v)4120 static uiBlock *curvemap_tools_negslope_func(bContext *C, ARegion *region, void *cumap_v)
4121 {
4122   return curvemap_tools_func(C, region, cumap_v, true, UICURVE_FUNC_RESET_NEG);
4123 }
4124 
curvemap_brush_tools_func(bContext * C,ARegion * region,void * cumap_v)4125 static uiBlock *curvemap_brush_tools_func(bContext *C, ARegion *region, void *cumap_v)
4126 {
4127   return curvemap_tools_func(C, region, cumap_v, false, UICURVE_FUNC_RESET_NEG);
4128 }
4129 
curvemap_brush_tools_negslope_func(bContext * C,ARegion * region,void * cumap_v)4130 static uiBlock *curvemap_brush_tools_negslope_func(bContext *C, ARegion *region, void *cumap_v)
4131 {
4132   return curvemap_tools_func(C, region, cumap_v, false, UICURVE_FUNC_RESET_POS);
4133 }
4134 
curvemap_buttons_redraw(bContext * C,void * UNUSED (arg1),void * UNUSED (arg2))4135 static void curvemap_buttons_redraw(bContext *C, void *UNUSED(arg1), void *UNUSED(arg2))
4136 {
4137   ED_region_tag_redraw(CTX_wm_region(C));
4138 }
4139 
curvemap_buttons_update(bContext * C,void * arg1_v,void * cumap_v)4140 static void curvemap_buttons_update(bContext *C, void *arg1_v, void *cumap_v)
4141 {
4142   CurveMapping *cumap = cumap_v;
4143   BKE_curvemapping_changed(cumap, true);
4144   rna_update_cb(C, arg1_v, NULL);
4145 }
4146 
curvemap_buttons_reset(bContext * C,void * cb_v,void * cumap_v)4147 static void curvemap_buttons_reset(bContext *C, void *cb_v, void *cumap_v)
4148 {
4149   CurveMapping *cumap = cumap_v;
4150   cumap->preset = CURVE_PRESET_LINE;
4151   for (int a = 0; a < CM_TOT; a++) {
4152     BKE_curvemap_reset(cumap->cm + a, &cumap->clipr, cumap->preset, CURVEMAP_SLOPE_POSITIVE);
4153   }
4154 
4155   cumap->black[0] = cumap->black[1] = cumap->black[2] = 0.0f;
4156   cumap->white[0] = cumap->white[1] = cumap->white[2] = 1.0f;
4157   BKE_curvemapping_set_black_white(cumap, NULL, NULL);
4158 
4159   BKE_curvemapping_changed(cumap, false);
4160 
4161   rna_update_cb(C, cb_v, NULL);
4162 }
4163 
4164 /**
4165  * \note Still unsure how this call evolves.
4166  *
4167  * \param labeltype: Used for defining which curve-channels to show.
4168  */
curvemap_buttons_layout(uiLayout * layout,PointerRNA * ptr,char labeltype,bool levels,bool brush,bool neg_slope,bool tone,RNAUpdateCb * cb)4169 static void curvemap_buttons_layout(uiLayout *layout,
4170                                     PointerRNA *ptr,
4171                                     char labeltype,
4172                                     bool levels,
4173                                     bool brush,
4174                                     bool neg_slope,
4175                                     bool tone,
4176                                     RNAUpdateCb *cb)
4177 {
4178   CurveMapping *cumap = ptr->data;
4179   CurveMap *cm = &cumap->cm[cumap->cur];
4180   uiBut *bt;
4181   const float dx = UI_UNIT_X;
4182   int bg = -1;
4183 
4184   uiBlock *block = uiLayoutGetBlock(layout);
4185 
4186   if (tone) {
4187     uiLayout *split = uiLayoutSplit(layout, 0.0f, false);
4188     uiItemR(uiLayoutRow(split, false), ptr, "tone", UI_ITEM_R_EXPAND, NULL, ICON_NONE);
4189   }
4190 
4191   /* curve chooser */
4192   uiLayout *row = uiLayoutRow(layout, false);
4193 
4194   if (labeltype == 'v') {
4195     /* vector */
4196     uiLayout *sub = uiLayoutRow(row, true);
4197     uiLayoutSetAlignment(sub, UI_LAYOUT_ALIGN_LEFT);
4198 
4199     if (cumap->cm[0].curve) {
4200       bt = uiDefButI(
4201           block, UI_BTYPE_ROW, 0, "X", 0, 0, dx, dx, &cumap->cur, 0.0, 0.0, 0.0, 0.0, "");
4202       UI_but_func_set(bt, curvemap_buttons_redraw, NULL, NULL);
4203     }
4204     if (cumap->cm[1].curve) {
4205       bt = uiDefButI(
4206           block, UI_BTYPE_ROW, 0, "Y", 0, 0, dx, dx, &cumap->cur, 0.0, 1.0, 0.0, 0.0, "");
4207       UI_but_func_set(bt, curvemap_buttons_redraw, NULL, NULL);
4208     }
4209     if (cumap->cm[2].curve) {
4210       bt = uiDefButI(
4211           block, UI_BTYPE_ROW, 0, "Z", 0, 0, dx, dx, &cumap->cur, 0.0, 2.0, 0.0, 0.0, "");
4212       UI_but_func_set(bt, curvemap_buttons_redraw, NULL, NULL);
4213     }
4214   }
4215   else if (labeltype == 'c') {
4216     /* color */
4217     uiLayout *sub = uiLayoutRow(row, true);
4218     uiLayoutSetAlignment(sub, UI_LAYOUT_ALIGN_LEFT);
4219 
4220     if (cumap->cm[3].curve) {
4221       bt = uiDefButI(
4222           block, UI_BTYPE_ROW, 0, "C", 0, 0, dx, dx, &cumap->cur, 0.0, 3.0, 0.0, 0.0, "");
4223       UI_but_func_set(bt, curvemap_buttons_redraw, NULL, NULL);
4224     }
4225     if (cumap->cm[0].curve) {
4226       bt = uiDefButI(
4227           block, UI_BTYPE_ROW, 0, "R", 0, 0, dx, dx, &cumap->cur, 0.0, 0.0, 0.0, 0.0, "");
4228       UI_but_func_set(bt, curvemap_buttons_redraw, NULL, NULL);
4229     }
4230     if (cumap->cm[1].curve) {
4231       bt = uiDefButI(
4232           block, UI_BTYPE_ROW, 0, "G", 0, 0, dx, dx, &cumap->cur, 0.0, 1.0, 0.0, 0.0, "");
4233       UI_but_func_set(bt, curvemap_buttons_redraw, NULL, NULL);
4234     }
4235     if (cumap->cm[2].curve) {
4236       bt = uiDefButI(
4237           block, UI_BTYPE_ROW, 0, "B", 0, 0, dx, dx, &cumap->cur, 0.0, 2.0, 0.0, 0.0, "");
4238       UI_but_func_set(bt, curvemap_buttons_redraw, NULL, NULL);
4239     }
4240   }
4241   else if (labeltype == 'h') {
4242     /* HSV */
4243     uiLayout *sub = uiLayoutRow(row, true);
4244     uiLayoutSetAlignment(sub, UI_LAYOUT_ALIGN_LEFT);
4245 
4246     if (cumap->cm[0].curve) {
4247       bt = uiDefButI(
4248           block, UI_BTYPE_ROW, 0, "H", 0, 0, dx, dx, &cumap->cur, 0.0, 0.0, 0.0, 0.0, "");
4249       UI_but_func_set(bt, curvemap_buttons_redraw, NULL, NULL);
4250     }
4251     if (cumap->cm[1].curve) {
4252       bt = uiDefButI(
4253           block, UI_BTYPE_ROW, 0, "S", 0, 0, dx, dx, &cumap->cur, 0.0, 1.0, 0.0, 0.0, "");
4254       UI_but_func_set(bt, curvemap_buttons_redraw, NULL, NULL);
4255     }
4256     if (cumap->cm[2].curve) {
4257       bt = uiDefButI(
4258           block, UI_BTYPE_ROW, 0, "V", 0, 0, dx, dx, &cumap->cur, 0.0, 2.0, 0.0, 0.0, "");
4259       UI_but_func_set(bt, curvemap_buttons_redraw, NULL, NULL);
4260     }
4261   }
4262   else {
4263     uiLayoutSetAlignment(row, UI_LAYOUT_ALIGN_RIGHT);
4264   }
4265 
4266   if (labeltype == 'h') {
4267     bg = UI_GRAD_H;
4268   }
4269 
4270   /* operation buttons */
4271   uiLayoutRow(row, true);
4272 
4273   UI_block_emboss_set(block, UI_EMBOSS_NONE);
4274 
4275   bt = uiDefIconBut(block,
4276                     UI_BTYPE_BUT,
4277                     0,
4278                     ICON_ZOOM_IN,
4279                     0,
4280                     0,
4281                     dx,
4282                     dx,
4283                     NULL,
4284                     0.0,
4285                     0.0,
4286                     0.0,
4287                     0.0,
4288                     TIP_("Zoom in"));
4289   UI_but_func_set(bt, curvemap_buttons_zoom_in, cumap, NULL);
4290 
4291   bt = uiDefIconBut(block,
4292                     UI_BTYPE_BUT,
4293                     0,
4294                     ICON_ZOOM_OUT,
4295                     0,
4296                     0,
4297                     dx,
4298                     dx,
4299                     NULL,
4300                     0.0,
4301                     0.0,
4302                     0.0,
4303                     0.0,
4304                     TIP_("Zoom out"));
4305   UI_but_func_set(bt, curvemap_buttons_zoom_out, cumap, NULL);
4306 
4307   if (brush && neg_slope) {
4308     bt = uiDefIconBlockBut(block,
4309                            curvemap_brush_tools_negslope_func,
4310                            cumap,
4311                            0,
4312                            ICON_DOWNARROW_HLT,
4313                            0,
4314                            0,
4315                            dx,
4316                            dx,
4317                            TIP_("Tools"));
4318   }
4319   else if (brush) {
4320     bt = uiDefIconBlockBut(block,
4321                            curvemap_brush_tools_func,
4322                            cumap,
4323                            0,
4324                            ICON_DOWNARROW_HLT,
4325                            0,
4326                            0,
4327                            dx,
4328                            dx,
4329                            TIP_("Tools"));
4330   }
4331   else if (neg_slope) {
4332     bt = uiDefIconBlockBut(block,
4333                            curvemap_tools_negslope_func,
4334                            cumap,
4335                            0,
4336                            ICON_DOWNARROW_HLT,
4337                            0,
4338                            0,
4339                            dx,
4340                            dx,
4341                            TIP_("Tools"));
4342   }
4343   else {
4344     bt = uiDefIconBlockBut(block,
4345                            curvemap_tools_posslope_func,
4346                            cumap,
4347                            0,
4348                            ICON_DOWNARROW_HLT,
4349                            0,
4350                            0,
4351                            dx,
4352                            dx,
4353                            TIP_("Tools"));
4354   }
4355 
4356   UI_but_funcN_set(bt, rna_update_cb, MEM_dupallocN(cb), NULL);
4357 
4358   int icon = (cumap->flag & CUMA_DO_CLIP) ? ICON_CLIPUV_HLT : ICON_CLIPUV_DEHLT;
4359   bt = uiDefIconBlockBut(
4360       block, curvemap_clipping_func, cumap, 0, icon, 0, 0, dx, dx, TIP_("Clipping Options"));
4361   UI_but_funcN_set(bt, rna_update_cb, MEM_dupallocN(cb), NULL);
4362 
4363   bt = uiDefIconBut(block,
4364                     UI_BTYPE_BUT,
4365                     0,
4366                     ICON_X,
4367                     0,
4368                     0,
4369                     dx,
4370                     dx,
4371                     NULL,
4372                     0.0,
4373                     0.0,
4374                     0.0,
4375                     0.0,
4376                     TIP_("Delete points"));
4377   UI_but_funcN_set(bt, curvemap_buttons_delete, MEM_dupallocN(cb), cumap);
4378 
4379   UI_block_emboss_set(block, UI_EMBOSS);
4380 
4381   UI_block_funcN_set(block, rna_update_cb, MEM_dupallocN(cb), NULL);
4382 
4383   /* curve itself */
4384   int size = max_ii(uiLayoutGetWidth(layout), UI_UNIT_X);
4385   row = uiLayoutRow(layout, false);
4386   uiButCurveMapping *curve_but = (uiButCurveMapping *)uiDefBut(
4387       block, UI_BTYPE_CURVE, 0, "", 0, 0, size, 8.0f * UI_UNIT_X, cumap, 0.0f, 1.0f, 0, 0, "");
4388   curve_but->gradient_type = bg;
4389 
4390   /* sliders for selected point */
4391   CurveMapPoint *cmp = NULL;
4392   for (int i = 0; i < cm->totpoint; i++) {
4393     if (cm->curve[i].flag & CUMA_SELECT) {
4394       cmp = &cm->curve[i];
4395       break;
4396     }
4397   }
4398 
4399   if (cmp) {
4400     rctf bounds;
4401     if (cumap->flag & CUMA_DO_CLIP) {
4402       bounds = cumap->clipr;
4403     }
4404     else {
4405       bounds.xmin = bounds.ymin = -1000.0;
4406       bounds.xmax = bounds.ymax = 1000.0;
4407     }
4408 
4409     uiLayoutRow(layout, true);
4410     UI_block_funcN_set(block, curvemap_buttons_update, MEM_dupallocN(cb), cumap);
4411     bt = uiDefButF(block,
4412                    UI_BTYPE_NUM,
4413                    0,
4414                    "X",
4415                    0,
4416                    2 * UI_UNIT_Y,
4417                    UI_UNIT_X * 10,
4418                    UI_UNIT_Y,
4419                    &cmp->x,
4420                    bounds.xmin,
4421                    bounds.xmax,
4422                    0,
4423                    0,
4424                    "");
4425     UI_but_number_step_size_set(bt, 1);
4426     UI_but_number_precision_set(bt, 5);
4427     bt = uiDefButF(block,
4428                    UI_BTYPE_NUM,
4429                    0,
4430                    "Y",
4431                    0,
4432                    1 * UI_UNIT_Y,
4433                    UI_UNIT_X * 10,
4434                    UI_UNIT_Y,
4435                    &cmp->y,
4436                    bounds.ymin,
4437                    bounds.ymax,
4438                    0,
4439                    0,
4440                    "");
4441     UI_but_number_step_size_set(bt, 1);
4442     UI_but_number_precision_set(bt, 5);
4443   }
4444 
4445   /* black/white levels */
4446   if (levels) {
4447     uiLayout *split = uiLayoutSplit(layout, 0.0f, false);
4448     uiItemR(uiLayoutColumn(split, false), ptr, "black_level", UI_ITEM_R_EXPAND, NULL, ICON_NONE);
4449     uiItemR(uiLayoutColumn(split, false), ptr, "white_level", UI_ITEM_R_EXPAND, NULL, ICON_NONE);
4450 
4451     uiLayoutRow(layout, false);
4452     bt = uiDefBut(block,
4453                   UI_BTYPE_BUT,
4454                   0,
4455                   IFACE_("Reset"),
4456                   0,
4457                   0,
4458                   UI_UNIT_X * 10,
4459                   UI_UNIT_Y,
4460                   NULL,
4461                   0.0f,
4462                   0.0f,
4463                   0,
4464                   0,
4465                   TIP_("Reset Black/White point and curves"));
4466     UI_but_funcN_set(bt, curvemap_buttons_reset, MEM_dupallocN(cb), cumap);
4467   }
4468 
4469   UI_block_funcN_set(block, NULL, NULL, NULL);
4470 }
4471 
uiTemplateCurveMapping(uiLayout * layout,PointerRNA * ptr,const char * propname,int type,bool levels,bool brush,bool neg_slope,bool tone)4472 void uiTemplateCurveMapping(uiLayout *layout,
4473                             PointerRNA *ptr,
4474                             const char *propname,
4475                             int type,
4476                             bool levels,
4477                             bool brush,
4478                             bool neg_slope,
4479                             bool tone)
4480 {
4481   PropertyRNA *prop = RNA_struct_find_property(ptr, propname);
4482   uiBlock *block = uiLayoutGetBlock(layout);
4483 
4484   if (!prop) {
4485     RNA_warning("curve property not found: %s.%s", RNA_struct_identifier(ptr->type), propname);
4486     return;
4487   }
4488 
4489   if (RNA_property_type(prop) != PROP_POINTER) {
4490     RNA_warning("curve is not a pointer: %s.%s", RNA_struct_identifier(ptr->type), propname);
4491     return;
4492   }
4493 
4494   PointerRNA cptr = RNA_property_pointer_get(ptr, prop);
4495   if (!cptr.data || !RNA_struct_is_a(cptr.type, &RNA_CurveMapping)) {
4496     return;
4497   }
4498 
4499   RNAUpdateCb *cb = MEM_callocN(sizeof(RNAUpdateCb), "RNAUpdateCb");
4500   cb->ptr = *ptr;
4501   cb->prop = prop;
4502 
4503   ID *id = cptr.owner_id;
4504   UI_block_lock_set(block, (id && ID_IS_LINKED(id)), ERROR_LIBDATA_MESSAGE);
4505 
4506   curvemap_buttons_layout(layout, &cptr, type, levels, brush, neg_slope, tone, cb);
4507 
4508   UI_block_lock_clear(block);
4509 
4510   MEM_freeN(cb);
4511 }
4512 
4513 /** \} */
4514 
4515 /* -------------------------------------------------------------------- */
4516 /** \name Curve Profile Template
4517  * \{ */
4518 
CurveProfile_presets_dofunc(bContext * C,void * profile_v,int event)4519 static void CurveProfile_presets_dofunc(bContext *C, void *profile_v, int event)
4520 {
4521   CurveProfile *profile = profile_v;
4522 
4523   profile->preset = event;
4524   BKE_curveprofile_reset(profile);
4525   BKE_curveprofile_update(profile, PROF_UPDATE_NONE);
4526 
4527   ED_undo_push(C, "CurveProfile tools");
4528   ED_region_tag_redraw(CTX_wm_region(C));
4529 }
4530 
CurveProfile_presets_func(bContext * C,ARegion * region,CurveProfile * profile)4531 static uiBlock *CurveProfile_presets_func(bContext *C, ARegion *region, CurveProfile *profile)
4532 {
4533   short yco = 0;
4534 
4535   uiBlock *block = UI_block_begin(C, region, __func__, UI_EMBOSS);
4536   UI_block_func_butmenu_set(block, CurveProfile_presets_dofunc, profile);
4537 
4538   uiDefIconTextBut(block,
4539                    UI_BTYPE_BUT_MENU,
4540                    1,
4541                    ICON_BLANK1,
4542                    IFACE_("Default"),
4543                    0,
4544                    yco -= UI_UNIT_Y,
4545                    0,
4546                    UI_UNIT_Y,
4547                    NULL,
4548                    0.0,
4549                    0.0,
4550                    0,
4551                    PROF_PRESET_LINE,
4552                    "");
4553   uiDefIconTextBut(block,
4554                    UI_BTYPE_BUT_MENU,
4555                    1,
4556                    ICON_BLANK1,
4557                    IFACE_("Support Loops"),
4558                    0,
4559                    yco -= UI_UNIT_Y,
4560                    0,
4561                    UI_UNIT_Y,
4562                    NULL,
4563                    0.0,
4564                    0.0,
4565                    0,
4566                    PROF_PRESET_SUPPORTS,
4567                    "");
4568   uiDefIconTextBut(block,
4569                    UI_BTYPE_BUT_MENU,
4570                    1,
4571                    ICON_BLANK1,
4572                    IFACE_("Cornice Molding"),
4573                    0,
4574                    yco -= UI_UNIT_Y,
4575                    0,
4576                    UI_UNIT_Y,
4577                    NULL,
4578                    0.0,
4579                    0.0,
4580                    0,
4581                    PROF_PRESET_CORNICE,
4582                    "");
4583   uiDefIconTextBut(block,
4584                    UI_BTYPE_BUT_MENU,
4585                    1,
4586                    ICON_BLANK1,
4587                    IFACE_("Crown Molding"),
4588                    0,
4589                    yco -= UI_UNIT_Y,
4590                    0,
4591                    UI_UNIT_Y,
4592                    NULL,
4593                    0.0,
4594                    0.0,
4595                    0,
4596                    PROF_PRESET_CROWN,
4597                    "");
4598   uiDefIconTextBut(block,
4599                    UI_BTYPE_BUT_MENU,
4600                    1,
4601                    ICON_BLANK1,
4602                    IFACE_("Steps"),
4603                    0,
4604                    yco -= UI_UNIT_Y,
4605                    0,
4606                    UI_UNIT_Y,
4607                    NULL,
4608                    0.0,
4609                    0.0,
4610                    0,
4611                    PROF_PRESET_STEPS,
4612                    "");
4613 
4614   UI_block_direction_set(block, UI_DIR_DOWN);
4615   UI_block_bounds_set_text(block, (int)(3.0f * UI_UNIT_X));
4616 
4617   return block;
4618 }
4619 
CurveProfile_buttons_presets(bContext * C,ARegion * region,void * profile_v)4620 static uiBlock *CurveProfile_buttons_presets(bContext *C, ARegion *region, void *profile_v)
4621 {
4622   return CurveProfile_presets_func(C, region, (CurveProfile *)profile_v);
4623 }
4624 
4625 /* Only for CurveProfile tools block */
4626 enum {
4627   UIPROFILE_FUNC_RESET,
4628   UIPROFILE_FUNC_RESET_VIEW,
4629 };
4630 
CurveProfile_tools_dofunc(bContext * C,void * profile_v,int event)4631 static void CurveProfile_tools_dofunc(bContext *C, void *profile_v, int event)
4632 {
4633   CurveProfile *profile = profile_v;
4634 
4635   switch (event) {
4636     case UIPROFILE_FUNC_RESET: /* reset */
4637       BKE_curveprofile_reset(profile);
4638       BKE_curveprofile_update(profile, PROF_UPDATE_NONE);
4639       break;
4640     case UIPROFILE_FUNC_RESET_VIEW: /* reset view to clipping rect */
4641       profile->view_rect = profile->clip_rect;
4642       break;
4643   }
4644   ED_undo_push(C, "CurveProfile tools");
4645   ED_region_tag_redraw(CTX_wm_region(C));
4646 }
4647 
CurveProfile_tools_func(bContext * C,ARegion * region,CurveProfile * profile)4648 static uiBlock *CurveProfile_tools_func(bContext *C, ARegion *region, CurveProfile *profile)
4649 {
4650   short yco = 0;
4651 
4652   uiBlock *block = UI_block_begin(C, region, __func__, UI_EMBOSS);
4653   UI_block_func_butmenu_set(block, CurveProfile_tools_dofunc, profile);
4654 
4655   uiDefIconTextBut(block,
4656                    UI_BTYPE_BUT_MENU,
4657                    1,
4658                    ICON_BLANK1,
4659                    IFACE_("Reset View"),
4660                    0,
4661                    yco -= UI_UNIT_Y,
4662                    0,
4663                    UI_UNIT_Y,
4664                    NULL,
4665                    0.0,
4666                    0.0,
4667                    0,
4668                    UIPROFILE_FUNC_RESET_VIEW,
4669                    "");
4670   uiDefIconTextBut(block,
4671                    UI_BTYPE_BUT_MENU,
4672                    1,
4673                    ICON_BLANK1,
4674                    IFACE_("Reset Curve"),
4675                    0,
4676                    yco -= UI_UNIT_Y,
4677                    0,
4678                    UI_UNIT_Y,
4679                    NULL,
4680                    0.0,
4681                    0.0,
4682                    0,
4683                    UIPROFILE_FUNC_RESET,
4684                    "");
4685 
4686   UI_block_direction_set(block, UI_DIR_DOWN);
4687   UI_block_bounds_set_text(block, (int)(3.0f * UI_UNIT_X));
4688 
4689   return block;
4690 }
4691 
CurveProfile_buttons_tools(bContext * C,ARegion * region,void * profile_v)4692 static uiBlock *CurveProfile_buttons_tools(bContext *C, ARegion *region, void *profile_v)
4693 {
4694   return CurveProfile_tools_func(C, region, (CurveProfile *)profile_v);
4695 }
4696 
CurveProfile_buttons_zoom_in(bContext * C,void * profile_v,void * UNUSED (arg))4697 static void CurveProfile_buttons_zoom_in(bContext *C, void *profile_v, void *UNUSED(arg))
4698 {
4699   CurveProfile *profile = profile_v;
4700 
4701   /* Allow a 20x zoom. */
4702   if (BLI_rctf_size_x(&profile->view_rect) > 0.04f * BLI_rctf_size_x(&profile->clip_rect)) {
4703     const float dx = 0.1154f * BLI_rctf_size_x(&profile->view_rect);
4704     profile->view_rect.xmin += dx;
4705     profile->view_rect.xmax -= dx;
4706     const float dy = 0.1154f * BLI_rctf_size_y(&profile->view_rect);
4707     profile->view_rect.ymin += dy;
4708     profile->view_rect.ymax -= dy;
4709   }
4710 
4711   ED_region_tag_redraw(CTX_wm_region(C));
4712 }
4713 
CurveProfile_buttons_zoom_out(bContext * C,void * profile_v,void * UNUSED (arg))4714 static void CurveProfile_buttons_zoom_out(bContext *C, void *profile_v, void *UNUSED(arg))
4715 {
4716   CurveProfile *profile = profile_v;
4717 
4718   /* Allow 20 times zoom, but don't view outside clip */
4719   if (BLI_rctf_size_x(&profile->view_rect) < 20.0f * BLI_rctf_size_x(&profile->clip_rect)) {
4720     float d = 0.15f * BLI_rctf_size_x(&profile->view_rect);
4721     float d1 = d;
4722 
4723     if (profile->flag & PROF_USE_CLIP) {
4724       if (profile->view_rect.xmin - d < profile->clip_rect.xmin) {
4725         d1 = profile->view_rect.xmin - profile->clip_rect.xmin;
4726       }
4727     }
4728     profile->view_rect.xmin -= d1;
4729 
4730     d1 = d;
4731     if (profile->flag & PROF_USE_CLIP) {
4732       if (profile->view_rect.xmax + d > profile->clip_rect.xmax) {
4733         d1 = -profile->view_rect.xmax + profile->clip_rect.xmax;
4734       }
4735     }
4736     profile->view_rect.xmax += d1;
4737 
4738     d = d1 = 0.15f * BLI_rctf_size_y(&profile->view_rect);
4739 
4740     if (profile->flag & PROF_USE_CLIP) {
4741       if (profile->view_rect.ymin - d < profile->clip_rect.ymin) {
4742         d1 = profile->view_rect.ymin - profile->clip_rect.ymin;
4743       }
4744     }
4745     profile->view_rect.ymin -= d1;
4746 
4747     d1 = d;
4748     if (profile->flag & PROF_USE_CLIP) {
4749       if (profile->view_rect.ymax + d > profile->clip_rect.ymax) {
4750         d1 = -profile->view_rect.ymax + profile->clip_rect.ymax;
4751       }
4752     }
4753     profile->view_rect.ymax += d1;
4754   }
4755 
4756   ED_region_tag_redraw(CTX_wm_region(C));
4757 }
4758 
CurveProfile_clipping_toggle(bContext * C,void * cb_v,void * profile_v)4759 static void CurveProfile_clipping_toggle(bContext *C, void *cb_v, void *profile_v)
4760 {
4761   CurveProfile *profile = profile_v;
4762 
4763   profile->flag ^= PROF_USE_CLIP;
4764 
4765   BKE_curveprofile_update(profile, PROF_UPDATE_NONE);
4766   rna_update_cb(C, cb_v, NULL);
4767 }
4768 
CurveProfile_buttons_reverse(bContext * C,void * cb_v,void * profile_v)4769 static void CurveProfile_buttons_reverse(bContext *C, void *cb_v, void *profile_v)
4770 {
4771   CurveProfile *profile = profile_v;
4772 
4773   BKE_curveprofile_reverse(profile);
4774   BKE_curveprofile_update(profile, PROF_UPDATE_NONE);
4775   rna_update_cb(C, cb_v, NULL);
4776 }
4777 
CurveProfile_buttons_delete(bContext * C,void * cb_v,void * profile_v)4778 static void CurveProfile_buttons_delete(bContext *C, void *cb_v, void *profile_v)
4779 {
4780   CurveProfile *profile = profile_v;
4781 
4782   BKE_curveprofile_remove_by_flag(profile, SELECT);
4783   BKE_curveprofile_update(profile, PROF_UPDATE_NONE);
4784 
4785   rna_update_cb(C, cb_v, NULL);
4786 }
4787 
CurveProfile_buttons_update(bContext * C,void * arg1_v,void * profile_v)4788 static void CurveProfile_buttons_update(bContext *C, void *arg1_v, void *profile_v)
4789 {
4790   CurveProfile *profile = profile_v;
4791   BKE_curveprofile_update(profile, PROF_UPDATE_REMOVE_DOUBLES | PROF_UPDATE_CLIP);
4792   rna_update_cb(C, arg1_v, NULL);
4793 }
4794 
CurveProfile_buttons_reset(bContext * C,void * arg1_v,void * profile_v)4795 static void CurveProfile_buttons_reset(bContext *C, void *arg1_v, void *profile_v)
4796 {
4797   CurveProfile *profile = profile_v;
4798   BKE_curveprofile_reset(profile);
4799   BKE_curveprofile_update(profile, PROF_UPDATE_NONE);
4800   rna_update_cb(C, arg1_v, NULL);
4801 }
4802 
CurveProfile_buttons_layout(uiLayout * layout,PointerRNA * ptr,RNAUpdateCb * cb)4803 static void CurveProfile_buttons_layout(uiLayout *layout, PointerRNA *ptr, RNAUpdateCb *cb)
4804 {
4805   CurveProfile *profile = ptr->data;
4806   uiBut *bt;
4807 
4808   uiBlock *block = uiLayoutGetBlock(layout);
4809 
4810   UI_block_emboss_set(block, UI_EMBOSS);
4811 
4812   uiLayoutSetPropSep(layout, false);
4813 
4814   /* Preset selector */
4815   /* There is probably potential to use simpler "uiItemR" functions here, but automatic updating
4816    * after a preset is selected would be more complicated. */
4817   uiLayout *row = uiLayoutRow(layout, true);
4818   bt = uiDefBlockBut(
4819       block, CurveProfile_buttons_presets, profile, "Preset", 0, 0, UI_UNIT_X, UI_UNIT_X, "");
4820   UI_but_funcN_set(bt, rna_update_cb, MEM_dupallocN(cb), NULL);
4821 
4822   /* Show a "re-apply" preset button when it has been changed from the preset. */
4823   if (profile->flag & PROF_DIRTY_PRESET) {
4824     /* Only for dynamic presets. */
4825     if (ELEM(profile->preset, PROF_PRESET_STEPS, PROF_PRESET_SUPPORTS)) {
4826       bt = uiDefIconTextBut(block,
4827                             UI_BTYPE_BUT,
4828                             0,
4829                             ICON_NONE,
4830                             "Apply Preset",
4831                             0,
4832                             0,
4833                             UI_UNIT_X,
4834                             UI_UNIT_X,
4835                             NULL,
4836                             0.0,
4837                             0.0,
4838                             0.0,
4839                             0.0,
4840                             "Reapply and update the preset, removing changes");
4841       UI_but_funcN_set(bt, CurveProfile_buttons_reset, MEM_dupallocN(cb), profile);
4842     }
4843   }
4844 
4845   row = uiLayoutRow(layout, false);
4846 
4847   /* (Left aligned) */
4848   uiLayout *sub = uiLayoutRow(row, true);
4849   uiLayoutSetAlignment(sub, UI_LAYOUT_ALIGN_LEFT);
4850 
4851   /* Zoom in */
4852   bt = uiDefIconBut(block,
4853                     UI_BTYPE_BUT,
4854                     0,
4855                     ICON_ZOOM_IN,
4856                     0,
4857                     0,
4858                     UI_UNIT_X,
4859                     UI_UNIT_X,
4860                     NULL,
4861                     0.0,
4862                     0.0,
4863                     0.0,
4864                     0.0,
4865                     TIP_("Zoom in"));
4866   UI_but_func_set(bt, CurveProfile_buttons_zoom_in, profile, NULL);
4867 
4868   /* Zoom out */
4869   bt = uiDefIconBut(block,
4870                     UI_BTYPE_BUT,
4871                     0,
4872                     ICON_ZOOM_OUT,
4873                     0,
4874                     0,
4875                     UI_UNIT_X,
4876                     UI_UNIT_X,
4877                     NULL,
4878                     0.0,
4879                     0.0,
4880                     0.0,
4881                     0.0,
4882                     TIP_("Zoom out"));
4883   UI_but_func_set(bt, CurveProfile_buttons_zoom_out, profile, NULL);
4884 
4885   /* (Right aligned) */
4886   sub = uiLayoutRow(row, true);
4887   uiLayoutSetAlignment(sub, UI_LAYOUT_ALIGN_RIGHT);
4888 
4889   /* Reset view, reset curve */
4890   bt = uiDefIconBlockBut(
4891       block, CurveProfile_buttons_tools, profile, 0, 0, 0, 0, UI_UNIT_X, UI_UNIT_X, TIP_("Tools"));
4892   UI_but_funcN_set(bt, rna_update_cb, MEM_dupallocN(cb), NULL);
4893 
4894   /* Flip path */
4895   bt = uiDefIconBut(block,
4896                     UI_BTYPE_BUT,
4897                     0,
4898                     ICON_ARROW_LEFTRIGHT,
4899                     0,
4900                     0,
4901                     UI_UNIT_X,
4902                     UI_UNIT_X,
4903                     NULL,
4904                     0.0,
4905                     0.0,
4906                     0.0,
4907                     0.0,
4908                     TIP_("Reverse Path"));
4909   UI_but_funcN_set(bt, CurveProfile_buttons_reverse, MEM_dupallocN(cb), profile);
4910 
4911   /* Clipping toggle */
4912   int icon = (profile->flag & PROF_USE_CLIP) ? ICON_CLIPUV_HLT : ICON_CLIPUV_DEHLT;
4913   bt = uiDefIconBut(block,
4914                     UI_BTYPE_BUT,
4915                     0,
4916                     icon,
4917                     0,
4918                     0,
4919                     UI_UNIT_X,
4920                     UI_UNIT_X,
4921                     NULL,
4922                     0.0,
4923                     0.0,
4924                     0.0,
4925                     0.0,
4926                     TIP_("Toggle Profile Clipping"));
4927   UI_but_funcN_set(bt, CurveProfile_clipping_toggle, MEM_dupallocN(cb), profile);
4928 
4929   UI_block_funcN_set(block, rna_update_cb, MEM_dupallocN(cb), NULL);
4930 
4931   /* The path itself */
4932   int path_width = max_ii(uiLayoutGetWidth(layout), UI_UNIT_X);
4933   path_width = min_ii(path_width, (int)(16.0f * UI_UNIT_X));
4934   int path_height = path_width;
4935   uiLayoutRow(layout, false);
4936   uiDefBut(block,
4937            UI_BTYPE_CURVEPROFILE,
4938            0,
4939            "",
4940            0,
4941            0,
4942            (short)path_width,
4943            (short)path_height,
4944            profile,
4945            0.0f,
4946            1.0f,
4947            -1,
4948            0,
4949            "");
4950 
4951   /* Position sliders for (first) selected point */
4952   int i;
4953   float *selection_x, *selection_y;
4954   bool point_last_or_first = false;
4955   CurveProfilePoint *point = NULL;
4956   for (i = 0; i < profile->path_len; i++) {
4957     if (profile->path[i].flag & PROF_SELECT) {
4958       point = &profile->path[i];
4959       selection_x = &point->x;
4960       selection_y = &point->y;
4961       break;
4962     }
4963     if (profile->path[i].flag & PROF_H1_SELECT) {
4964       point = &profile->path[i];
4965       selection_x = &point->h1_loc[0];
4966       selection_y = &point->h1_loc[1];
4967     }
4968     else if (profile->path[i].flag & PROF_H2_SELECT) {
4969       point = &profile->path[i];
4970       selection_x = &point->h2_loc[0];
4971       selection_y = &point->h2_loc[1];
4972     }
4973   }
4974   if (i == 0 || i == profile->path_len - 1) {
4975     point_last_or_first = true;
4976   }
4977 
4978   /* Selected point data */
4979   rctf bounds;
4980   if (point) {
4981     if (profile->flag & PROF_USE_CLIP) {
4982       bounds = profile->clip_rect;
4983     }
4984     else {
4985       bounds.xmin = bounds.ymin = -1000.0;
4986       bounds.xmax = bounds.ymax = 1000.0;
4987     }
4988 
4989     row = uiLayoutRow(layout, true);
4990 
4991     PointerRNA point_ptr;
4992     RNA_pointer_create(ptr->owner_id, &RNA_CurveProfilePoint, point, &point_ptr);
4993     PropertyRNA *prop_handle_type = RNA_struct_find_property(&point_ptr, "handle_type_1");
4994     uiItemFullR(row,
4995                 &point_ptr,
4996                 prop_handle_type,
4997                 RNA_NO_INDEX,
4998                 0,
4999                 UI_ITEM_R_EXPAND | UI_ITEM_R_ICON_ONLY,
5000                 "",
5001                 ICON_NONE);
5002 
5003     /* Position */
5004     bt = uiDefButF(block,
5005                    UI_BTYPE_NUM,
5006                    0,
5007                    "X:",
5008                    0,
5009                    2 * UI_UNIT_Y,
5010                    UI_UNIT_X * 10,
5011                    UI_UNIT_Y,
5012                    selection_x,
5013                    bounds.xmin,
5014                    bounds.xmax,
5015                    0,
5016                    0,
5017                    "");
5018     UI_but_number_step_size_set(bt, 1);
5019     UI_but_number_precision_set(bt, 5);
5020     UI_but_funcN_set(bt, CurveProfile_buttons_update, MEM_dupallocN(cb), profile);
5021     if (point_last_or_first) {
5022       UI_but_flag_enable(bt, UI_BUT_DISABLED);
5023     }
5024     bt = uiDefButF(block,
5025                    UI_BTYPE_NUM,
5026                    0,
5027                    "Y:",
5028                    0,
5029                    1 * UI_UNIT_Y,
5030                    UI_UNIT_X * 10,
5031                    UI_UNIT_Y,
5032                    selection_y,
5033                    bounds.ymin,
5034                    bounds.ymax,
5035                    0,
5036                    0,
5037                    "");
5038     UI_but_number_step_size_set(bt, 1);
5039     UI_but_number_precision_set(bt, 5);
5040     UI_but_funcN_set(bt, CurveProfile_buttons_update, MEM_dupallocN(cb), profile);
5041     if (point_last_or_first) {
5042       UI_but_flag_enable(bt, UI_BUT_DISABLED);
5043     }
5044 
5045     /* Delete points */
5046     bt = uiDefIconBut(block,
5047                       UI_BTYPE_BUT,
5048                       0,
5049                       ICON_X,
5050                       0,
5051                       0,
5052                       UI_UNIT_X,
5053                       UI_UNIT_X,
5054                       NULL,
5055                       0.0,
5056                       0.0,
5057                       0.0,
5058                       0.0,
5059                       TIP_("Delete points"));
5060     UI_but_funcN_set(bt, CurveProfile_buttons_delete, MEM_dupallocN(cb), profile);
5061     if (point_last_or_first) {
5062       UI_but_flag_enable(bt, UI_BUT_DISABLED);
5063     }
5064   }
5065 
5066   uiItemR(layout, ptr, "use_sample_straight_edges", 0, NULL, ICON_NONE);
5067   uiItemR(layout, ptr, "use_sample_even_lengths", 0, NULL, ICON_NONE);
5068 
5069   UI_block_funcN_set(block, NULL, NULL, NULL);
5070 }
5071 
5072 /**
5073  * Template for a path creation widget intended for custom bevel profiles.
5074  * This section is quite similar to #uiTemplateCurveMapping, but with reduced complexity.
5075  */
uiTemplateCurveProfile(uiLayout * layout,PointerRNA * ptr,const char * propname)5076 void uiTemplateCurveProfile(uiLayout *layout, PointerRNA *ptr, const char *propname)
5077 {
5078   PropertyRNA *prop = RNA_struct_find_property(ptr, propname);
5079 
5080   uiBlock *block = uiLayoutGetBlock(layout);
5081 
5082   if (!prop) {
5083     RNA_warning(
5084         "Curve Profile property not found: %s.%s", RNA_struct_identifier(ptr->type), propname);
5085     return;
5086   }
5087 
5088   if (RNA_property_type(prop) != PROP_POINTER) {
5089     RNA_warning(
5090         "Curve Profile is not a pointer: %s.%s", RNA_struct_identifier(ptr->type), propname);
5091     return;
5092   }
5093 
5094   PointerRNA cptr = RNA_property_pointer_get(ptr, prop);
5095   if (!cptr.data || !RNA_struct_is_a(cptr.type, &RNA_CurveProfile)) {
5096     return;
5097   }
5098 
5099   /* Share update functionality with the CurveMapping widget template. */
5100   RNAUpdateCb *cb = MEM_callocN(sizeof(RNAUpdateCb), "RNAUpdateCb");
5101   cb->ptr = *ptr;
5102   cb->prop = prop;
5103 
5104   ID *id = cptr.owner_id;
5105   UI_block_lock_set(block, (id && ID_IS_LINKED(id)), ERROR_LIBDATA_MESSAGE);
5106 
5107   CurveProfile_buttons_layout(layout, &cptr, cb);
5108 
5109   UI_block_lock_clear(block);
5110 
5111   MEM_freeN(cb);
5112 }
5113 
5114 /** \} */
5115 
5116 /* -------------------------------------------------------------------- */
5117 /** \name ColorPicker Template
5118  * \{ */
5119 
5120 #define WHEEL_SIZE (5 * U.widget_unit)
5121 
5122 /* This template now follows User Preference for type - name is not correct anymore... */
uiTemplateColorPicker(uiLayout * layout,PointerRNA * ptr,const char * propname,bool value_slider,bool lock,bool lock_luminosity,bool cubic)5123 void uiTemplateColorPicker(uiLayout *layout,
5124                            PointerRNA *ptr,
5125                            const char *propname,
5126                            bool value_slider,
5127                            bool lock,
5128                            bool lock_luminosity,
5129                            bool cubic)
5130 {
5131   PropertyRNA *prop = RNA_struct_find_property(ptr, propname);
5132   uiBlock *block = uiLayoutGetBlock(layout);
5133   ColorPicker *cpicker = ui_block_colorpicker_create(block);
5134 
5135   if (!prop) {
5136     RNA_warning("property not found: %s.%s", RNA_struct_identifier(ptr->type), propname);
5137     return;
5138   }
5139 
5140   float softmin, softmax, step, precision;
5141   RNA_property_float_ui_range(ptr, prop, &softmin, &softmax, &step, &precision);
5142 
5143   uiLayout *col = uiLayoutColumn(layout, true);
5144   uiLayout *row = uiLayoutRow(col, true);
5145 
5146   uiBut *but = NULL;
5147   uiButHSVCube *hsv_but;
5148   switch (U.color_picker_type) {
5149     case USER_CP_SQUARE_SV:
5150     case USER_CP_SQUARE_HS:
5151     case USER_CP_SQUARE_HV:
5152       hsv_but = (uiButHSVCube *)uiDefButR_prop(block,
5153                                                UI_BTYPE_HSVCUBE,
5154                                                0,
5155                                                "",
5156                                                0,
5157                                                0,
5158                                                WHEEL_SIZE,
5159                                                WHEEL_SIZE,
5160                                                ptr,
5161                                                prop,
5162                                                -1,
5163                                                0.0,
5164                                                0.0,
5165                                                0,
5166                                                0,
5167                                                "");
5168       switch (U.color_picker_type) {
5169         case USER_CP_SQUARE_SV:
5170           hsv_but->gradient_type = UI_GRAD_SV;
5171           break;
5172         case USER_CP_SQUARE_HS:
5173           hsv_but->gradient_type = UI_GRAD_HS;
5174           break;
5175         case USER_CP_SQUARE_HV:
5176           hsv_but->gradient_type = UI_GRAD_HV;
5177           break;
5178       }
5179       but = &hsv_but->but;
5180       break;
5181 
5182     /* user default */
5183     case USER_CP_CIRCLE_HSV:
5184     case USER_CP_CIRCLE_HSL:
5185     default:
5186       but = uiDefButR_prop(block,
5187                            UI_BTYPE_HSVCIRCLE,
5188                            0,
5189                            "",
5190                            0,
5191                            0,
5192                            WHEEL_SIZE,
5193                            WHEEL_SIZE,
5194                            ptr,
5195                            prop,
5196                            -1,
5197                            0.0,
5198                            0.0,
5199                            0,
5200                            0,
5201                            "");
5202       break;
5203   }
5204 
5205   but->custom_data = cpicker;
5206 
5207   cpicker->use_color_lock = lock;
5208   cpicker->use_color_cubic = cubic;
5209   cpicker->use_luminosity_lock = lock_luminosity;
5210 
5211   if (lock_luminosity) {
5212     float color[4]; /* in case of alpha */
5213     RNA_property_float_get_array(ptr, prop, color);
5214     but->a2 = len_v3(color);
5215     cpicker->luminosity_lock_value = len_v3(color);
5216   }
5217 
5218   if (value_slider) {
5219     switch (U.color_picker_type) {
5220       case USER_CP_CIRCLE_HSL:
5221         uiItemS(row);
5222         hsv_but = (uiButHSVCube *)uiDefButR_prop(block,
5223                                                  UI_BTYPE_HSVCUBE,
5224                                                  0,
5225                                                  "",
5226                                                  WHEEL_SIZE + 6,
5227                                                  0,
5228                                                  14 * UI_DPI_FAC,
5229                                                  WHEEL_SIZE,
5230                                                  ptr,
5231                                                  prop,
5232                                                  -1,
5233                                                  softmin,
5234                                                  softmax,
5235                                                  0,
5236                                                  0,
5237                                                  "");
5238         hsv_but->gradient_type = UI_GRAD_L_ALT;
5239         break;
5240       case USER_CP_SQUARE_SV:
5241         uiItemS(col);
5242         hsv_but = (uiButHSVCube *)uiDefButR_prop(block,
5243                                                  UI_BTYPE_HSVCUBE,
5244                                                  0,
5245                                                  "",
5246                                                  0,
5247                                                  4,
5248                                                  WHEEL_SIZE,
5249                                                  18 * UI_DPI_FAC,
5250                                                  ptr,
5251                                                  prop,
5252                                                  -1,
5253                                                  softmin,
5254                                                  softmax,
5255                                                  0,
5256                                                  0,
5257                                                  "");
5258         hsv_but->gradient_type = UI_GRAD_SV + 3;
5259         break;
5260       case USER_CP_SQUARE_HS:
5261         uiItemS(col);
5262         hsv_but = (uiButHSVCube *)uiDefButR_prop(block,
5263                                                  UI_BTYPE_HSVCUBE,
5264                                                  0,
5265                                                  "",
5266                                                  0,
5267                                                  4,
5268                                                  WHEEL_SIZE,
5269                                                  18 * UI_DPI_FAC,
5270                                                  ptr,
5271                                                  prop,
5272                                                  -1,
5273                                                  softmin,
5274                                                  softmax,
5275                                                  0,
5276                                                  0,
5277                                                  "");
5278         hsv_but->gradient_type = UI_GRAD_HS + 3;
5279         break;
5280       case USER_CP_SQUARE_HV:
5281         uiItemS(col);
5282         hsv_but = (uiButHSVCube *)uiDefButR_prop(block,
5283                                                  UI_BTYPE_HSVCUBE,
5284                                                  0,
5285                                                  "",
5286                                                  0,
5287                                                  4,
5288                                                  WHEEL_SIZE,
5289                                                  18 * UI_DPI_FAC,
5290                                                  ptr,
5291                                                  prop,
5292                                                  -1,
5293                                                  softmin,
5294                                                  softmax,
5295                                                  0,
5296                                                  0,
5297                                                  "");
5298         hsv_but->gradient_type = UI_GRAD_HV + 3;
5299         break;
5300 
5301       /* user default */
5302       case USER_CP_CIRCLE_HSV:
5303       default:
5304         uiItemS(row);
5305         hsv_but = (uiButHSVCube *)uiDefButR_prop(block,
5306                                                  UI_BTYPE_HSVCUBE,
5307                                                  0,
5308                                                  "",
5309                                                  WHEEL_SIZE + 6,
5310                                                  0,
5311                                                  14 * UI_DPI_FAC,
5312                                                  WHEEL_SIZE,
5313                                                  ptr,
5314                                                  prop,
5315                                                  -1,
5316                                                  softmin,
5317                                                  softmax,
5318                                                  0,
5319                                                  0,
5320                                                  "");
5321         hsv_but->gradient_type = UI_GRAD_V_ALT;
5322         break;
5323     }
5324 
5325     hsv_but->but.custom_data = cpicker;
5326   }
5327 }
5328 
ui_template_palette_menu(bContext * UNUSED (C),uiLayout * layout,void * UNUSED (but_p))5329 static void ui_template_palette_menu(bContext *UNUSED(C), uiLayout *layout, void *UNUSED(but_p))
5330 {
5331   uiLayout *row;
5332 
5333   uiItemL(layout, IFACE_("Sort by:"), ICON_NONE);
5334   row = uiLayoutRow(layout, false);
5335   uiItemEnumO_value(row, IFACE_("Hue"), ICON_NONE, "PALETTE_OT_sort", "type", 1);
5336   row = uiLayoutRow(layout, false);
5337   uiItemEnumO_value(row, IFACE_("Saturation"), ICON_NONE, "PALETTE_OT_sort", "type", 2);
5338   row = uiLayoutRow(layout, false);
5339   uiItemEnumO_value(row, IFACE_("Value"), ICON_NONE, "PALETTE_OT_sort", "type", 3);
5340   row = uiLayoutRow(layout, false);
5341   uiItemEnumO_value(row, IFACE_("Luminance"), ICON_NONE, "PALETTE_OT_sort", "type", 4);
5342 }
5343 
uiTemplatePalette(uiLayout * layout,PointerRNA * ptr,const char * propname,bool UNUSED (colors))5344 void uiTemplatePalette(uiLayout *layout,
5345                        PointerRNA *ptr,
5346                        const char *propname,
5347                        bool UNUSED(colors))
5348 {
5349   PropertyRNA *prop = RNA_struct_find_property(ptr, propname);
5350   uiBut *but = NULL;
5351 
5352   const int cols_per_row = MAX2(uiLayoutGetWidth(layout) / UI_UNIT_X, 1);
5353 
5354   if (!prop) {
5355     RNA_warning("property not found: %s.%s", RNA_struct_identifier(ptr->type), propname);
5356     return;
5357   }
5358 
5359   PointerRNA cptr = RNA_property_pointer_get(ptr, prop);
5360   if (!cptr.data || !RNA_struct_is_a(cptr.type, &RNA_Palette)) {
5361     return;
5362   }
5363 
5364   uiBlock *block = uiLayoutGetBlock(layout);
5365 
5366   Palette *palette = cptr.data;
5367 
5368   uiLayout *col = uiLayoutColumn(layout, true);
5369   uiLayoutRow(col, true);
5370   uiDefIconButO(block,
5371                 UI_BTYPE_BUT,
5372                 "PALETTE_OT_color_add",
5373                 WM_OP_INVOKE_DEFAULT,
5374                 ICON_ADD,
5375                 0,
5376                 0,
5377                 UI_UNIT_X,
5378                 UI_UNIT_Y,
5379                 NULL);
5380   uiDefIconButO(block,
5381                 UI_BTYPE_BUT,
5382                 "PALETTE_OT_color_delete",
5383                 WM_OP_INVOKE_DEFAULT,
5384                 ICON_REMOVE,
5385                 0,
5386                 0,
5387                 UI_UNIT_X,
5388                 UI_UNIT_Y,
5389                 NULL);
5390   if (palette->colors.first != NULL) {
5391     but = uiDefIconButO(block,
5392                         UI_BTYPE_BUT,
5393                         "PALETTE_OT_color_move",
5394                         WM_OP_INVOKE_DEFAULT,
5395                         ICON_TRIA_UP,
5396                         0,
5397                         0,
5398                         UI_UNIT_X,
5399                         UI_UNIT_Y,
5400                         NULL);
5401     UI_but_operator_ptr_get(but);
5402     RNA_enum_set(but->opptr, "type", -1);
5403 
5404     but = uiDefIconButO(block,
5405                         UI_BTYPE_BUT,
5406                         "PALETTE_OT_color_move",
5407                         WM_OP_INVOKE_DEFAULT,
5408                         ICON_TRIA_DOWN,
5409                         0,
5410                         0,
5411                         UI_UNIT_X,
5412                         UI_UNIT_Y,
5413                         NULL);
5414     UI_but_operator_ptr_get(but);
5415     RNA_enum_set(but->opptr, "type", 1);
5416 
5417     /* Menu. */
5418     uiDefIconMenuBut(
5419         block, ui_template_palette_menu, NULL, ICON_SORTSIZE, 0, 0, UI_UNIT_X, UI_UNIT_Y, "");
5420   }
5421 
5422   col = uiLayoutColumn(layout, true);
5423   uiLayoutRow(col, true);
5424 
5425   int row_cols = 0, col_id = 0;
5426   LISTBASE_FOREACH (PaletteColor *, color, &palette->colors) {
5427     if (row_cols >= cols_per_row) {
5428       uiLayoutRow(col, true);
5429       row_cols = 0;
5430     }
5431 
5432     PointerRNA color_ptr;
5433     RNA_pointer_create(&palette->id, &RNA_PaletteColor, color, &color_ptr);
5434     uiButColor *color_but = (uiButColor *)uiDefButR(block,
5435                                                     UI_BTYPE_COLOR,
5436                                                     0,
5437                                                     "",
5438                                                     0,
5439                                                     0,
5440                                                     UI_UNIT_X,
5441                                                     UI_UNIT_Y,
5442                                                     &color_ptr,
5443                                                     "color",
5444                                                     -1,
5445                                                     0.0,
5446                                                     1.0,
5447                                                     0.0,
5448                                                     0.0,
5449                                                     "");
5450     color_but->is_pallete_color = true;
5451     color_but->palette_color_index = col_id;
5452     row_cols++;
5453     col_id++;
5454   }
5455 }
5456 
uiTemplateCryptoPicker(uiLayout * layout,PointerRNA * ptr,const char * propname)5457 void uiTemplateCryptoPicker(uiLayout *layout, PointerRNA *ptr, const char *propname)
5458 {
5459   PropertyRNA *prop = RNA_struct_find_property(ptr, propname);
5460 
5461   if (!prop) {
5462     RNA_warning("property not found: %s.%s", RNA_struct_identifier(ptr->type), propname);
5463     return;
5464   }
5465 
5466   uiBlock *block = uiLayoutGetBlock(layout);
5467 
5468   uiBut *but = uiDefIconTextButO(block,
5469                                  UI_BTYPE_BUT,
5470                                  "UI_OT_eyedropper_color",
5471                                  WM_OP_INVOKE_DEFAULT,
5472                                  ICON_EYEDROPPER,
5473                                  RNA_property_ui_name(prop),
5474                                  0,
5475                                  0,
5476                                  UI_UNIT_X,
5477                                  UI_UNIT_Y,
5478                                  RNA_property_ui_description(prop));
5479   but->rnapoin = *ptr;
5480   but->rnaprop = prop;
5481   but->rnaindex = -1;
5482 
5483   PointerRNA *opptr = UI_but_operator_ptr_get(but);
5484   /* Important for crypto-matte operation. */
5485   RNA_boolean_set(opptr, "use_accumulate", false);
5486 }
5487 
5488 /** \} */
5489 
5490 /* -------------------------------------------------------------------- */
5491 /** \name Layer Buttons Template
5492  * \{ */
5493 
handle_layer_buttons(bContext * C,void * arg1,void * arg2)5494 static void handle_layer_buttons(bContext *C, void *arg1, void *arg2)
5495 {
5496   uiBut *but = arg1;
5497   const int cur = POINTER_AS_INT(arg2);
5498   wmWindow *win = CTX_wm_window(C);
5499   int shift = win->eventstate->shift;
5500 
5501   if (!shift) {
5502     int tot = RNA_property_array_length(&but->rnapoin, but->rnaprop);
5503 
5504     /* Normally clicking only selects one layer */
5505     RNA_property_boolean_set_index(&but->rnapoin, but->rnaprop, cur, true);
5506     for (int i = 0; i < tot; i++) {
5507       if (i != cur) {
5508         RNA_property_boolean_set_index(&but->rnapoin, but->rnaprop, i, false);
5509       }
5510     }
5511   }
5512 
5513   /* view3d layer change should update depsgraph (invisible object changed maybe) */
5514   /* see view3d_header.c */
5515 }
5516 
5517 /**
5518  * \todo for now, grouping of layers is determined by dividing up the length of
5519  * the array of layer bitflags
5520  */
uiTemplateLayers(uiLayout * layout,PointerRNA * ptr,const char * propname,PointerRNA * used_ptr,const char * used_propname,int active_layer)5521 void uiTemplateLayers(uiLayout *layout,
5522                       PointerRNA *ptr,
5523                       const char *propname,
5524                       PointerRNA *used_ptr,
5525                       const char *used_propname,
5526                       int active_layer)
5527 {
5528   const int cols_per_group = 5;
5529 
5530   PropertyRNA *prop = RNA_struct_find_property(ptr, propname);
5531   if (!prop) {
5532     RNA_warning("layers property not found: %s.%s", RNA_struct_identifier(ptr->type), propname);
5533     return;
5534   }
5535 
5536   /* the number of layers determines the way we group them
5537    * - we want 2 rows only (for now)
5538    * - The number of columns (cols) is the total number of buttons per row the 'remainder'
5539    *   is added to this, as it will be ok to have first row slightly wider if need be.
5540    * - For now, only split into groups if group will have at least 5 items.
5541    */
5542   const int layers = RNA_property_array_length(ptr, prop);
5543   const int cols = (layers / 2) + (layers % 2);
5544   const int groups = ((cols / 2) < cols_per_group) ? (1) : (cols / cols_per_group);
5545 
5546   PropertyRNA *used_prop = NULL;
5547   if (used_ptr && used_propname) {
5548     used_prop = RNA_struct_find_property(used_ptr, used_propname);
5549     if (!used_prop) {
5550       RNA_warning("used layers property not found: %s.%s",
5551                   RNA_struct_identifier(ptr->type),
5552                   used_propname);
5553       return;
5554     }
5555 
5556     if (RNA_property_array_length(used_ptr, used_prop) < layers) {
5557       used_prop = NULL;
5558     }
5559   }
5560 
5561   /* layers are laid out going across rows, with the columns being divided into groups */
5562 
5563   for (int group = 0; group < groups; group++) {
5564     uiLayout *uCol = uiLayoutColumn(layout, true);
5565 
5566     for (int row = 0; row < 2; row++) {
5567       uiLayout *uRow = uiLayoutRow(uCol, true);
5568       uiBlock *block = uiLayoutGetBlock(uRow);
5569       int layer = groups * cols_per_group * row + cols_per_group * group;
5570 
5571       /* add layers as toggle buts */
5572       for (int col = 0; (col < cols_per_group) && (layer < layers); col++, layer++) {
5573         int icon = 0;
5574         const int butlay = 1 << layer;
5575 
5576         if (active_layer & butlay) {
5577           icon = ICON_LAYER_ACTIVE;
5578         }
5579         else if (used_prop && RNA_property_boolean_get_index(used_ptr, used_prop, layer)) {
5580           icon = ICON_LAYER_USED;
5581         }
5582 
5583         uiBut *but = uiDefAutoButR(
5584             block, ptr, prop, layer, "", icon, 0, 0, UI_UNIT_X / 2, UI_UNIT_Y / 2);
5585         UI_but_func_set(but, handle_layer_buttons, but, POINTER_FROM_INT(layer));
5586         but->type = UI_BTYPE_TOGGLE;
5587       }
5588     }
5589   }
5590 }
5591 
5592 /** \} */
5593 
5594 /* -------------------------------------------------------------------- */
5595 /** \name List Template
5596  * \{ */
5597 
uilist_draw_item_default(struct uiList * ui_list,struct bContext * UNUSED (C),struct uiLayout * layout,struct PointerRNA * UNUSED (dataptr),struct PointerRNA * itemptr,int icon,struct PointerRNA * UNUSED (active_dataptr),const char * UNUSED (active_propname),int UNUSED (index),int UNUSED (flt_flag))5598 static void uilist_draw_item_default(struct uiList *ui_list,
5599                                      struct bContext *UNUSED(C),
5600                                      struct uiLayout *layout,
5601                                      struct PointerRNA *UNUSED(dataptr),
5602                                      struct PointerRNA *itemptr,
5603                                      int icon,
5604                                      struct PointerRNA *UNUSED(active_dataptr),
5605                                      const char *UNUSED(active_propname),
5606                                      int UNUSED(index),
5607                                      int UNUSED(flt_flag))
5608 {
5609   PropertyRNA *nameprop = RNA_struct_name_property(itemptr->type);
5610 
5611   /* Simplest one! */
5612   switch (ui_list->layout_type) {
5613     case UILST_LAYOUT_GRID:
5614       uiItemL(layout, "", icon);
5615       break;
5616     case UILST_LAYOUT_DEFAULT:
5617     case UILST_LAYOUT_COMPACT:
5618     default:
5619       if (nameprop) {
5620         uiItemFullR(layout, itemptr, nameprop, RNA_NO_INDEX, 0, UI_ITEM_R_NO_BG, "", icon);
5621       }
5622       else {
5623         uiItemL(layout, "", icon);
5624       }
5625       break;
5626   }
5627 }
5628 
uilist_draw_filter_default(struct uiList * ui_list,struct bContext * UNUSED (C),struct uiLayout * layout)5629 static void uilist_draw_filter_default(struct uiList *ui_list,
5630                                        struct bContext *UNUSED(C),
5631                                        struct uiLayout *layout)
5632 {
5633   PointerRNA listptr;
5634   RNA_pointer_create(NULL, &RNA_UIList, ui_list, &listptr);
5635 
5636   uiLayout *row = uiLayoutRow(layout, false);
5637 
5638   uiLayout *subrow = uiLayoutRow(row, true);
5639   uiItemR(subrow, &listptr, "filter_name", 0, "", ICON_NONE);
5640   uiItemR(subrow,
5641           &listptr,
5642           "use_filter_invert",
5643           UI_ITEM_R_TOGGLE | UI_ITEM_R_ICON_ONLY,
5644           "",
5645           ICON_ARROW_LEFTRIGHT);
5646 
5647   if ((ui_list->filter_sort_flag & UILST_FLT_SORT_LOCK) == 0) {
5648     subrow = uiLayoutRow(row, true);
5649     uiItemR(subrow,
5650             &listptr,
5651             "use_filter_sort_alpha",
5652             UI_ITEM_R_TOGGLE | UI_ITEM_R_ICON_ONLY,
5653             "",
5654             ICON_NONE);
5655     uiItemR(subrow,
5656             &listptr,
5657             "use_filter_sort_reverse",
5658             UI_ITEM_R_TOGGLE | UI_ITEM_R_ICON_ONLY,
5659             "",
5660             (ui_list->filter_sort_flag & UILST_FLT_SORT_REVERSE) ? ICON_SORT_DESC : ICON_SORT_ASC);
5661   }
5662 }
5663 
5664 typedef struct {
5665   char name[MAX_IDPROP_NAME];
5666   int org_idx;
5667 } StringCmp;
5668 
cmpstringp(const void * p1,const void * p2)5669 static int cmpstringp(const void *p1, const void *p2)
5670 {
5671   /* Case-insensitive comparison. */
5672   return BLI_strcasecmp(((StringCmp *)p1)->name, ((StringCmp *)p2)->name);
5673 }
5674 
uilist_filter_items_default(struct uiList * ui_list,struct bContext * UNUSED (C),struct PointerRNA * dataptr,const char * propname)5675 static void uilist_filter_items_default(struct uiList *ui_list,
5676                                         struct bContext *UNUSED(C),
5677                                         struct PointerRNA *dataptr,
5678                                         const char *propname)
5679 {
5680   uiListDyn *dyn_data = ui_list->dyn_data;
5681   PropertyRNA *prop = RNA_struct_find_property(dataptr, propname);
5682 
5683   const char *filter_raw = ui_list->filter_byname;
5684   char *filter = (char *)filter_raw, filter_buff[32], *filter_dyn = NULL;
5685   const bool filter_exclude = (ui_list->filter_flag & UILST_FLT_EXCLUDE) != 0;
5686   const bool order_by_name = (ui_list->filter_sort_flag & UILST_FLT_SORT_MASK) ==
5687                              UILST_FLT_SORT_ALPHA;
5688   const int len = RNA_property_collection_length(dataptr, prop);
5689 
5690   dyn_data->items_shown = dyn_data->items_len = len;
5691 
5692   if (len && (order_by_name || filter_raw[0])) {
5693     StringCmp *names = NULL;
5694     int order_idx = 0, i = 0;
5695 
5696     if (order_by_name) {
5697       names = MEM_callocN(sizeof(StringCmp) * len, "StringCmp");
5698     }
5699     if (filter_raw[0]) {
5700       const size_t slen = strlen(filter_raw);
5701 
5702       dyn_data->items_filter_flags = MEM_callocN(sizeof(int) * len, "items_filter_flags");
5703       dyn_data->items_shown = 0;
5704 
5705       /* Implicitly add heading/trailing wildcards if needed. */
5706       if (slen + 3 <= sizeof(filter_buff)) {
5707         filter = filter_buff;
5708       }
5709       else {
5710         filter = filter_dyn = MEM_mallocN((slen + 3) * sizeof(char), "filter_dyn");
5711       }
5712       BLI_strncpy_ensure_pad(filter, filter_raw, '*', slen + 3);
5713     }
5714 
5715     RNA_PROP_BEGIN (dataptr, itemptr, prop) {
5716       bool do_order = false;
5717 
5718       char *namebuf = RNA_struct_name_get_alloc(&itemptr, NULL, 0, NULL);
5719       const char *name = namebuf ? namebuf : "";
5720 
5721       if (filter[0]) {
5722         /* Case-insensitive! */
5723         if (fnmatch(filter, name, FNM_CASEFOLD) == 0) {
5724           dyn_data->items_filter_flags[i] = UILST_FLT_ITEM;
5725           if (!filter_exclude) {
5726             dyn_data->items_shown++;
5727             do_order = order_by_name;
5728           }
5729           // printf("%s: '%s' matches '%s'\n", __func__, name, filter);
5730         }
5731         else if (filter_exclude) {
5732           dyn_data->items_shown++;
5733           do_order = order_by_name;
5734         }
5735       }
5736       else {
5737         do_order = order_by_name;
5738       }
5739 
5740       if (do_order) {
5741         names[order_idx].org_idx = order_idx;
5742         BLI_strncpy(names[order_idx++].name, name, MAX_IDPROP_NAME);
5743       }
5744 
5745       /* free name */
5746       if (namebuf) {
5747         MEM_freeN(namebuf);
5748       }
5749       i++;
5750     }
5751     RNA_PROP_END;
5752 
5753     if (order_by_name) {
5754       int new_idx;
5755       /* note: order_idx equals either to ui_list->items_len if no filtering done,
5756        *       or to ui_list->items_shown if filter is enabled,
5757        *       or to (ui_list->items_len - ui_list->items_shown) if filtered items are excluded.
5758        *       This way, we only sort items we actually intend to draw!
5759        */
5760       qsort(names, order_idx, sizeof(StringCmp), cmpstringp);
5761 
5762       dyn_data->items_filter_neworder = MEM_mallocN(sizeof(int) * order_idx,
5763                                                     "items_filter_neworder");
5764       for (new_idx = 0; new_idx < order_idx; new_idx++) {
5765         dyn_data->items_filter_neworder[names[new_idx].org_idx] = new_idx;
5766       }
5767     }
5768 
5769     if (filter_dyn) {
5770       MEM_freeN(filter_dyn);
5771     }
5772     if (names) {
5773       MEM_freeN(names);
5774     }
5775   }
5776 }
5777 
5778 typedef struct {
5779   PointerRNA item;
5780   int org_idx;
5781   int flt_flag;
5782 } _uilist_item;
5783 
5784 typedef struct {
5785   int visual_items; /* Visual number of items (i.e. number of items we have room to display). */
5786   int start_idx;    /* Index of first item to display. */
5787   int end_idx;      /* Index of last item to display + 1. */
5788 } uiListLayoutdata;
5789 
uilist_prepare(uiList * ui_list,int len,int activei,int rows,int maxrows,int columns,uiListLayoutdata * layoutdata)5790 static void uilist_prepare(uiList *ui_list,
5791                            int len,
5792                            int activei,
5793                            int rows,
5794                            int maxrows,
5795                            int columns,
5796                            uiListLayoutdata *layoutdata)
5797 {
5798   uiListDyn *dyn_data = ui_list->dyn_data;
5799   const bool use_auto_size = (ui_list->list_grip < (rows - UI_LIST_AUTO_SIZE_THRESHOLD));
5800 
5801   /* default rows */
5802   if (rows <= 0) {
5803     rows = 5;
5804   }
5805   dyn_data->visual_height_min = rows;
5806   if (maxrows < rows) {
5807     maxrows = max_ii(rows, 5);
5808   }
5809   if (columns <= 0) {
5810     columns = 9;
5811   }
5812 
5813   int activei_row;
5814   if (columns > 1) {
5815     dyn_data->height = (int)ceil((double)len / (double)columns);
5816     activei_row = (int)floor((double)activei / (double)columns);
5817   }
5818   else {
5819     dyn_data->height = len;
5820     activei_row = activei;
5821   }
5822 
5823   if (!use_auto_size) {
5824     /* No auto-size, yet we clamp at min size! */
5825     maxrows = rows = max_ii(ui_list->list_grip, rows);
5826   }
5827   else if ((rows != maxrows) && (dyn_data->height > rows)) {
5828     /* Expand size if needed and possible. */
5829     rows = min_ii(dyn_data->height, maxrows);
5830   }
5831 
5832   /* If list length changes or list is tagged to check this,
5833    * and active is out of view, scroll to it .*/
5834   if (ui_list->list_last_len != len || ui_list->flag & UILST_SCROLL_TO_ACTIVE_ITEM) {
5835     if (activei_row < ui_list->list_scroll) {
5836       ui_list->list_scroll = activei_row;
5837     }
5838     else if (activei_row >= ui_list->list_scroll + rows) {
5839       ui_list->list_scroll = activei_row - rows + 1;
5840     }
5841     ui_list->flag &= ~UILST_SCROLL_TO_ACTIVE_ITEM;
5842   }
5843 
5844   int max_scroll = max_ii(0, dyn_data->height - rows);
5845   CLAMP(ui_list->list_scroll, 0, max_scroll);
5846   ui_list->list_last_len = len;
5847   dyn_data->visual_height = rows;
5848   layoutdata->visual_items = rows * columns;
5849   layoutdata->start_idx = ui_list->list_scroll * columns;
5850   layoutdata->end_idx = min_ii(layoutdata->start_idx + rows * columns, len);
5851 }
5852 
uilist_resize_update_cb(bContext * C,void * arg1,void * UNUSED (arg2))5853 static void uilist_resize_update_cb(bContext *C, void *arg1, void *UNUSED(arg2))
5854 {
5855   uiList *ui_list = arg1;
5856   uiListDyn *dyn_data = ui_list->dyn_data;
5857 
5858   /* This way we get diff in number of additional items to show (positive) or hide (negative). */
5859   const int diff = round_fl_to_int((float)(dyn_data->resize - dyn_data->resize_prev) /
5860                                    (float)UI_UNIT_Y);
5861 
5862   if (diff != 0) {
5863     ui_list->list_grip += diff;
5864     dyn_data->resize_prev += diff * UI_UNIT_Y;
5865     ui_list->flag |= UILST_SCROLL_TO_ACTIVE_ITEM;
5866   }
5867 
5868   /* In case uilist is in popup, we need special refreshing */
5869   ED_region_tag_refresh_ui(CTX_wm_menu(C));
5870 }
5871 
uilist_item_use_dynamic_tooltip(PointerRNA * itemptr,const char * propname)5872 static void *uilist_item_use_dynamic_tooltip(PointerRNA *itemptr, const char *propname)
5873 {
5874   if (propname && propname[0] && itemptr && itemptr->data) {
5875     PropertyRNA *prop = RNA_struct_find_property(itemptr, propname);
5876 
5877     if (prop && (RNA_property_type(prop) == PROP_STRING)) {
5878       return RNA_property_string_get_alloc(itemptr, prop, NULL, 0, NULL);
5879     }
5880   }
5881   return NULL;
5882 }
5883 
uilist_item_tooltip_func(bContext * UNUSED (C),void * argN,const char * tip)5884 static char *uilist_item_tooltip_func(bContext *UNUSED(C), void *argN, const char *tip)
5885 {
5886   char *dyn_tooltip = argN;
5887   return BLI_sprintfN("%s - %s", tip, dyn_tooltip);
5888 }
5889 
uiTemplateList(uiLayout * layout,bContext * C,const char * listtype_name,const char * list_id,PointerRNA * dataptr,const char * propname,PointerRNA * active_dataptr,const char * active_propname,const char * item_dyntip_propname,int rows,int maxrows,int layout_type,int columns,bool sort_reverse,bool sort_lock)5890 void uiTemplateList(uiLayout *layout,
5891                     bContext *C,
5892                     const char *listtype_name,
5893                     const char *list_id,
5894                     PointerRNA *dataptr,
5895                     const char *propname,
5896                     PointerRNA *active_dataptr,
5897                     const char *active_propname,
5898                     const char *item_dyntip_propname,
5899                     int rows,
5900                     int maxrows,
5901                     int layout_type,
5902                     int columns,
5903                     bool sort_reverse,
5904                     bool sort_lock)
5905 {
5906   PropertyRNA *prop = NULL, *activeprop;
5907   _uilist_item *items_ptr = NULL;
5908   uiLayout *glob = NULL, *box, *row, *col, *subrow, *sub, *overlap;
5909   uiBut *but;
5910 
5911   uiListLayoutdata layoutdata;
5912   char ui_list_id[UI_MAX_NAME_STR];
5913   char numstr[32];
5914   int rnaicon = ICON_NONE, icon = ICON_NONE;
5915   int i = 0, activei = 0;
5916   int len = 0;
5917 
5918   /* validate arguments */
5919   /* Forbid default UI_UL_DEFAULT_CLASS_NAME list class without a custom list_id! */
5920   if (STREQ(UI_UL_DEFAULT_CLASS_NAME, listtype_name) && !(list_id && list_id[0])) {
5921     RNA_warning("template_list using default '%s' UIList class must provide a custom list_id",
5922                 UI_UL_DEFAULT_CLASS_NAME);
5923     return;
5924   }
5925 
5926   uiBlock *block = uiLayoutGetBlock(layout);
5927 
5928   if (!active_dataptr->data) {
5929     RNA_warning("No active data");
5930     return;
5931   }
5932 
5933   if (dataptr->data) {
5934     prop = RNA_struct_find_property(dataptr, propname);
5935     if (!prop) {
5936       RNA_warning("Property not found: %s.%s", RNA_struct_identifier(dataptr->type), propname);
5937       return;
5938     }
5939   }
5940 
5941   activeprop = RNA_struct_find_property(active_dataptr, active_propname);
5942   if (!activeprop) {
5943     RNA_warning(
5944         "Property not found: %s.%s", RNA_struct_identifier(active_dataptr->type), active_propname);
5945     return;
5946   }
5947 
5948   if (prop) {
5949     PropertyType type = RNA_property_type(prop);
5950     if (type != PROP_COLLECTION) {
5951       RNA_warning("Expected a collection data property");
5952       return;
5953     }
5954   }
5955 
5956   PropertyType activetype = RNA_property_type(activeprop);
5957   if (activetype != PROP_INT) {
5958     RNA_warning("Expected an integer active data property");
5959     return;
5960   }
5961 
5962   /* get icon */
5963   if (dataptr->data && prop) {
5964     StructRNA *ptype = RNA_property_pointer_type(dataptr, prop);
5965     rnaicon = RNA_struct_ui_icon(ptype);
5966   }
5967 
5968   /* get active data */
5969   activei = RNA_property_int_get(active_dataptr, activeprop);
5970 
5971   /* Find the uiList type. */
5972   uiListType *ui_list_type = WM_uilisttype_find(listtype_name, false);
5973 
5974   if (ui_list_type == NULL) {
5975     RNA_warning("List type %s not found", listtype_name);
5976     return;
5977   }
5978 
5979   uiListDrawItemFunc draw_item = ui_list_type->draw_item ? ui_list_type->draw_item :
5980                                                            uilist_draw_item_default;
5981   uiListDrawFilterFunc draw_filter = ui_list_type->draw_filter ? ui_list_type->draw_filter :
5982                                                                  uilist_draw_filter_default;
5983   uiListFilterItemsFunc filter_items = ui_list_type->filter_items ? ui_list_type->filter_items :
5984                                                                     uilist_filter_items_default;
5985 
5986   /* Find or add the uiList to the current Region. */
5987   /* We tag the list id with the list type... */
5988   BLI_snprintf(
5989       ui_list_id, sizeof(ui_list_id), "%s_%s", ui_list_type->idname, list_id ? list_id : "");
5990 
5991   /* Allows to work in popups. */
5992   ARegion *region = CTX_wm_menu(C);
5993   if (region == NULL) {
5994     region = CTX_wm_region(C);
5995   }
5996   uiList *ui_list = BLI_findstring(&region->ui_lists, ui_list_id, offsetof(uiList, list_id));
5997 
5998   if (!ui_list) {
5999     ui_list = MEM_callocN(sizeof(uiList), "uiList");
6000     BLI_strncpy(ui_list->list_id, ui_list_id, sizeof(ui_list->list_id));
6001     BLI_addtail(&region->ui_lists, ui_list);
6002     ui_list->list_grip = -UI_LIST_AUTO_SIZE_THRESHOLD; /* Force auto size by default. */
6003     if (sort_reverse) {
6004       ui_list->filter_sort_flag |= UILST_FLT_SORT_REVERSE;
6005     }
6006     if (sort_lock) {
6007       ui_list->filter_sort_flag |= UILST_FLT_SORT_LOCK;
6008     }
6009   }
6010 
6011   if (!ui_list->dyn_data) {
6012     ui_list->dyn_data = MEM_callocN(sizeof(uiListDyn), "uiList.dyn_data");
6013   }
6014   uiListDyn *dyn_data = ui_list->dyn_data;
6015 
6016   /* Because we can't actually pass type across save&load... */
6017   ui_list->type = ui_list_type;
6018   ui_list->layout_type = layout_type;
6019 
6020   /* Reset filtering data. */
6021   MEM_SAFE_FREE(dyn_data->items_filter_flags);
6022   MEM_SAFE_FREE(dyn_data->items_filter_neworder);
6023   dyn_data->items_len = dyn_data->items_shown = -1;
6024 
6025   /* When active item changed since last draw, scroll to it. */
6026   if (activei != ui_list->list_last_activei) {
6027     ui_list->flag |= UILST_SCROLL_TO_ACTIVE_ITEM;
6028     ui_list->list_last_activei = activei;
6029   }
6030 
6031   /* Filter list items! (not for compact layout, though) */
6032   if (dataptr->data && prop) {
6033     const int filter_exclude = ui_list->filter_flag & UILST_FLT_EXCLUDE;
6034     const bool order_reverse = (ui_list->filter_sort_flag & UILST_FLT_SORT_REVERSE) != 0;
6035     int items_shown, idx = 0;
6036 #if 0
6037     int prev_ii = -1, prev_i;
6038 #endif
6039 
6040     if (layout_type == UILST_LAYOUT_COMPACT) {
6041       dyn_data->items_len = dyn_data->items_shown = RNA_property_collection_length(dataptr, prop);
6042     }
6043     else {
6044       // printf("%s: filtering...\n", __func__);
6045       filter_items(ui_list, C, dataptr, propname);
6046       // printf("%s: filtering done.\n", __func__);
6047     }
6048 
6049     items_shown = dyn_data->items_shown;
6050     if (items_shown >= 0) {
6051       bool activei_mapping_pending = true;
6052       items_ptr = MEM_mallocN(sizeof(_uilist_item) * items_shown, __func__);
6053       // printf("%s: items shown: %d.\n", __func__, items_shown);
6054       RNA_PROP_BEGIN (dataptr, itemptr, prop) {
6055         if (!dyn_data->items_filter_flags ||
6056             ((dyn_data->items_filter_flags[i] & UILST_FLT_ITEM) ^ filter_exclude)) {
6057           int ii;
6058           if (dyn_data->items_filter_neworder) {
6059             ii = dyn_data->items_filter_neworder[idx++];
6060             ii = order_reverse ? items_shown - ii - 1 : ii;
6061           }
6062           else {
6063             ii = order_reverse ? items_shown - ++idx : idx++;
6064           }
6065           // printf("%s: ii: %d\n", __func__, ii);
6066           items_ptr[ii].item = itemptr;
6067           items_ptr[ii].org_idx = i;
6068           items_ptr[ii].flt_flag = dyn_data->items_filter_flags ? dyn_data->items_filter_flags[i] :
6069                                                                   0;
6070 
6071           if (activei_mapping_pending && activei == i) {
6072             activei = ii;
6073             /* So that we do not map again activei! */
6074             activei_mapping_pending = false;
6075           }
6076 #if 0 /* For now, do not alter active element, even if it will be hidden... */
6077           else if (activei < i) {
6078             /* We do not want an active but invisible item!
6079              * Only exception is when all items are filtered out...
6080              */
6081             if (prev_ii >= 0) {
6082               activei = prev_ii;
6083               RNA_property_int_set(active_dataptr, activeprop, prev_i);
6084             }
6085             else {
6086               activei = ii;
6087               RNA_property_int_set(active_dataptr, activeprop, i);
6088             }
6089           }
6090           prev_i = i;
6091           prev_ii = ii;
6092 #endif
6093         }
6094         i++;
6095       }
6096       RNA_PROP_END;
6097 
6098       if (activei_mapping_pending) {
6099         /* No active item found, set to 'invalid' -1 value... */
6100         activei = -1;
6101       }
6102     }
6103     if (dyn_data->items_shown >= 0) {
6104       len = dyn_data->items_shown;
6105     }
6106     else {
6107       len = dyn_data->items_len;
6108     }
6109   }
6110 
6111   switch (layout_type) {
6112     case UILST_LAYOUT_DEFAULT:
6113       /* layout */
6114       box = uiLayoutListBox(layout, ui_list, active_dataptr, activeprop);
6115       glob = uiLayoutColumn(box, true);
6116       row = uiLayoutRow(glob, false);
6117       col = uiLayoutColumn(row, true);
6118 
6119       /* init numbers */
6120       uilist_prepare(ui_list, len, activei, rows, maxrows, 1, &layoutdata);
6121 
6122       if (dataptr->data && prop) {
6123         /* create list items */
6124         for (i = layoutdata.start_idx; i < layoutdata.end_idx; i++) {
6125           PointerRNA *itemptr = &items_ptr[i].item;
6126           void *dyntip_data;
6127           const int org_i = items_ptr[i].org_idx;
6128           const int flt_flag = items_ptr[i].flt_flag;
6129           uiBlock *subblock = uiLayoutGetBlock(col);
6130 
6131           overlap = uiLayoutOverlap(col);
6132 
6133           UI_block_flag_enable(subblock, UI_BLOCK_LIST_ITEM);
6134 
6135           /* list item behind label & other buttons */
6136           sub = uiLayoutRow(overlap, false);
6137 
6138           but = uiDefButR_prop(subblock,
6139                                UI_BTYPE_LISTROW,
6140                                0,
6141                                "",
6142                                0,
6143                                0,
6144                                UI_UNIT_X * 10,
6145                                UI_UNIT_Y,
6146                                active_dataptr,
6147                                activeprop,
6148                                0,
6149                                0,
6150                                org_i,
6151                                0,
6152                                0,
6153                                TIP_("Double click to rename"));
6154           if ((dyntip_data = uilist_item_use_dynamic_tooltip(itemptr, item_dyntip_propname))) {
6155             UI_but_func_tooltip_set(but, uilist_item_tooltip_func, dyntip_data);
6156           }
6157 
6158           sub = uiLayoutRow(overlap, false);
6159 
6160           icon = UI_icon_from_rnaptr(C, itemptr, rnaicon, false);
6161           if (icon == ICON_DOT) {
6162             icon = ICON_NONE;
6163           }
6164           draw_item(ui_list,
6165                     C,
6166                     sub,
6167                     dataptr,
6168                     itemptr,
6169                     icon,
6170                     active_dataptr,
6171                     active_propname,
6172                     org_i,
6173                     flt_flag);
6174 
6175           /* If we are "drawing" active item, set all labels as active. */
6176           if (i == activei) {
6177             ui_layout_list_set_labels_active(sub);
6178           }
6179 
6180           UI_block_flag_disable(subblock, UI_BLOCK_LIST_ITEM);
6181         }
6182       }
6183 
6184       /* add dummy buttons to fill space */
6185       for (; i < layoutdata.start_idx + layoutdata.visual_items; i++) {
6186         uiItemL(col, "", ICON_NONE);
6187       }
6188 
6189       /* add scrollbar */
6190       if (len > layoutdata.visual_items) {
6191         col = uiLayoutColumn(row, false);
6192         uiDefButI(block,
6193                   UI_BTYPE_SCROLL,
6194                   0,
6195                   "",
6196                   0,
6197                   0,
6198                   V2D_SCROLL_WIDTH,
6199                   UI_UNIT_Y * dyn_data->visual_height,
6200                   &ui_list->list_scroll,
6201                   0,
6202                   dyn_data->height - dyn_data->visual_height,
6203                   dyn_data->visual_height,
6204                   0,
6205                   "");
6206       }
6207       break;
6208     case UILST_LAYOUT_COMPACT:
6209       row = uiLayoutRow(layout, true);
6210 
6211       if ((dataptr->data && prop) && (dyn_data->items_shown > 0) && (activei >= 0) &&
6212           (activei < dyn_data->items_shown)) {
6213         PointerRNA *itemptr = &items_ptr[activei].item;
6214         const int org_i = items_ptr[activei].org_idx;
6215 
6216         icon = UI_icon_from_rnaptr(C, itemptr, rnaicon, false);
6217         if (icon == ICON_DOT) {
6218           icon = ICON_NONE;
6219         }
6220         draw_item(
6221             ui_list, C, row, dataptr, itemptr, icon, active_dataptr, active_propname, org_i, 0);
6222       }
6223       /* if list is empty, add in dummy button */
6224       else {
6225         uiItemL(row, "", ICON_NONE);
6226       }
6227 
6228       /* next/prev button */
6229       BLI_snprintf(numstr, sizeof(numstr), "%d :", dyn_data->items_shown);
6230       but = uiDefIconTextButR_prop(block,
6231                                    UI_BTYPE_NUM,
6232                                    0,
6233                                    0,
6234                                    numstr,
6235                                    0,
6236                                    0,
6237                                    UI_UNIT_X * 5,
6238                                    UI_UNIT_Y,
6239                                    active_dataptr,
6240                                    activeprop,
6241                                    0,
6242                                    0,
6243                                    0,
6244                                    0,
6245                                    0,
6246                                    "");
6247       if (dyn_data->items_shown == 0) {
6248         UI_but_flag_enable(but, UI_BUT_DISABLED);
6249       }
6250       break;
6251     case UILST_LAYOUT_GRID:
6252       box = uiLayoutListBox(layout, ui_list, active_dataptr, activeprop);
6253       glob = uiLayoutColumn(box, true);
6254       row = uiLayoutRow(glob, false);
6255       col = uiLayoutColumn(row, true);
6256       subrow = NULL; /* Quite gcc warning! */
6257 
6258       uilist_prepare(ui_list, len, activei, rows, maxrows, columns, &layoutdata);
6259 
6260       if (dataptr->data && prop) {
6261         /* create list items */
6262         for (i = layoutdata.start_idx; i < layoutdata.end_idx; i++) {
6263           PointerRNA *itemptr = &items_ptr[i].item;
6264           const int org_i = items_ptr[i].org_idx;
6265           const int flt_flag = items_ptr[i].flt_flag;
6266 
6267           /* create button */
6268           if (!(i % columns)) {
6269             subrow = uiLayoutRow(col, false);
6270           }
6271 
6272           uiBlock *subblock = uiLayoutGetBlock(subrow);
6273           overlap = uiLayoutOverlap(subrow);
6274 
6275           UI_block_flag_enable(subblock, UI_BLOCK_LIST_ITEM);
6276 
6277           /* list item behind label & other buttons */
6278           sub = uiLayoutRow(overlap, false);
6279 
6280           but = uiDefButR_prop(subblock,
6281                                UI_BTYPE_LISTROW,
6282                                0,
6283                                "",
6284                                0,
6285                                0,
6286                                UI_UNIT_X * 10,
6287                                UI_UNIT_Y,
6288                                active_dataptr,
6289                                activeprop,
6290                                0,
6291                                0,
6292                                org_i,
6293                                0,
6294                                0,
6295                                NULL);
6296           UI_but_drawflag_enable(but, UI_BUT_NO_TOOLTIP);
6297 
6298           sub = uiLayoutRow(overlap, false);
6299 
6300           icon = UI_icon_from_rnaptr(C, itemptr, rnaicon, false);
6301           draw_item(ui_list,
6302                     C,
6303                     sub,
6304                     dataptr,
6305                     itemptr,
6306                     icon,
6307                     active_dataptr,
6308                     active_propname,
6309                     org_i,
6310                     flt_flag);
6311 
6312           /* If we are "drawing" active item, set all labels as active. */
6313           if (i == activei) {
6314             ui_layout_list_set_labels_active(sub);
6315           }
6316 
6317           UI_block_flag_disable(subblock, UI_BLOCK_LIST_ITEM);
6318         }
6319       }
6320 
6321       /* add dummy buttons to fill space */
6322       for (; i < layoutdata.start_idx + layoutdata.visual_items; i++) {
6323         if (!(i % columns)) {
6324           subrow = uiLayoutRow(col, false);
6325         }
6326         uiItemL(subrow, "", ICON_NONE);
6327       }
6328 
6329       /* add scrollbar */
6330       if (len > layoutdata.visual_items) {
6331         /* col = */ uiLayoutColumn(row, false);
6332         uiDefButI(block,
6333                   UI_BTYPE_SCROLL,
6334                   0,
6335                   "",
6336                   0,
6337                   0,
6338                   V2D_SCROLL_WIDTH,
6339                   UI_UNIT_Y * dyn_data->visual_height,
6340                   &ui_list->list_scroll,
6341                   0,
6342                   dyn_data->height - dyn_data->visual_height,
6343                   dyn_data->visual_height,
6344                   0,
6345                   "");
6346       }
6347       break;
6348   }
6349 
6350   if (glob) {
6351     /* About UI_BTYPE_GRIP drag-resize:
6352      * We can't directly use results from a grip button, since we have a
6353      * rather complex behavior here (sizing by discrete steps and, overall, autosize feature).
6354      * Since we *never* know whether we are grip-resizing or not
6355      * (because there is no callback for when a button enters/leaves its "edit mode"),
6356      * we use the fact that grip-controlled value (dyn_data->resize) is completely handled
6357      * by the grip during the grab resize, so settings its value here has no effect at all.
6358      *
6359      * It is only meaningful when we are not resizing,
6360      * in which case this gives us the correct "init drag" value.
6361      * Note we cannot affect dyn_data->resize_prev here,
6362      * since this value is not controlled by the grip!
6363      */
6364     dyn_data->resize = dyn_data->resize_prev +
6365                        (dyn_data->visual_height - ui_list->list_grip) * UI_UNIT_Y;
6366 
6367     row = uiLayoutRow(glob, true);
6368     uiBlock *subblock = uiLayoutGetBlock(row);
6369     UI_block_emboss_set(subblock, UI_EMBOSS_NONE);
6370 
6371     if (ui_list->filter_flag & UILST_FLT_SHOW) {
6372       but = uiDefIconButBitI(subblock,
6373                              UI_BTYPE_TOGGLE,
6374                              UILST_FLT_SHOW,
6375                              0,
6376                              ICON_DISCLOSURE_TRI_DOWN,
6377                              0,
6378                              0,
6379                              UI_UNIT_X,
6380                              UI_UNIT_Y * 0.5f,
6381                              &(ui_list->filter_flag),
6382                              0,
6383                              0,
6384                              0,
6385                              0,
6386                              TIP_("Hide filtering options"));
6387       UI_but_flag_disable(but, UI_BUT_UNDO); /* skip undo on screen buttons */
6388 
6389       but = uiDefIconButI(subblock,
6390                           UI_BTYPE_GRIP,
6391                           0,
6392                           ICON_GRIP,
6393                           0,
6394                           0,
6395                           UI_UNIT_X * 10.0f,
6396                           UI_UNIT_Y * 0.5f,
6397                           &dyn_data->resize,
6398                           0.0,
6399                           0.0,
6400                           0,
6401                           0,
6402                           "");
6403       UI_but_func_set(but, uilist_resize_update_cb, ui_list, NULL);
6404 
6405       UI_block_emboss_set(subblock, UI_EMBOSS);
6406 
6407       col = uiLayoutColumn(glob, false);
6408       subblock = uiLayoutGetBlock(col);
6409       uiDefBut(subblock,
6410                UI_BTYPE_SEPR,
6411                0,
6412                "",
6413                0,
6414                0,
6415                UI_UNIT_X,
6416                UI_UNIT_Y * 0.05f,
6417                NULL,
6418                0.0,
6419                0.0,
6420                0,
6421                0,
6422                "");
6423 
6424       draw_filter(ui_list, C, col);
6425     }
6426     else {
6427       but = uiDefIconButBitI(subblock,
6428                              UI_BTYPE_TOGGLE,
6429                              UILST_FLT_SHOW,
6430                              0,
6431                              ICON_DISCLOSURE_TRI_RIGHT,
6432                              0,
6433                              0,
6434                              UI_UNIT_X,
6435                              UI_UNIT_Y * 0.5f,
6436                              &(ui_list->filter_flag),
6437                              0,
6438                              0,
6439                              0,
6440                              0,
6441                              TIP_("Show filtering options"));
6442       UI_but_flag_disable(but, UI_BUT_UNDO); /* skip undo on screen buttons */
6443 
6444       but = uiDefIconButI(subblock,
6445                           UI_BTYPE_GRIP,
6446                           0,
6447                           ICON_GRIP,
6448                           0,
6449                           0,
6450                           UI_UNIT_X * 10.0f,
6451                           UI_UNIT_Y * 0.5f,
6452                           &dyn_data->resize,
6453                           0.0,
6454                           0.0,
6455                           0,
6456                           0,
6457                           "");
6458       UI_but_func_set(but, uilist_resize_update_cb, ui_list, NULL);
6459 
6460       UI_block_emboss_set(subblock, UI_EMBOSS);
6461     }
6462   }
6463 
6464   if (items_ptr) {
6465     MEM_freeN(items_ptr);
6466   }
6467 }
6468 
6469 /** \} */
6470 
6471 /* -------------------------------------------------------------------- */
6472 /** \name Running Jobs Template
6473  * \{ */
6474 
6475 #define B_STOPRENDER 1
6476 #define B_STOPCAST 2
6477 #define B_STOPANIM 3
6478 #define B_STOPCOMPO 4
6479 #define B_STOPSEQ 5
6480 #define B_STOPCLIP 6
6481 #define B_STOPFILE 7
6482 #define B_STOPOTHER 8
6483 
do_running_jobs(bContext * C,void * UNUSED (arg),int event)6484 static void do_running_jobs(bContext *C, void *UNUSED(arg), int event)
6485 {
6486   switch (event) {
6487     case B_STOPRENDER:
6488       G.is_break = true;
6489       break;
6490     case B_STOPCAST:
6491       WM_jobs_stop(CTX_wm_manager(C), CTX_wm_screen(C), NULL);
6492       break;
6493     case B_STOPANIM:
6494       WM_operator_name_call(C, "SCREEN_OT_animation_play", WM_OP_INVOKE_SCREEN, NULL);
6495       break;
6496     case B_STOPCOMPO:
6497       WM_jobs_stop(CTX_wm_manager(C), CTX_data_scene(C), NULL);
6498       break;
6499     case B_STOPSEQ:
6500       WM_jobs_stop(CTX_wm_manager(C), CTX_data_scene(C), NULL);
6501       break;
6502     case B_STOPCLIP:
6503       WM_jobs_stop(CTX_wm_manager(C), CTX_data_scene(C), NULL);
6504       break;
6505     case B_STOPFILE:
6506       WM_jobs_stop(CTX_wm_manager(C), CTX_data_scene(C), NULL);
6507       break;
6508     case B_STOPOTHER:
6509       G.is_break = true;
6510       break;
6511   }
6512 }
6513 
6514 struct ProgressTooltip_Store {
6515   wmWindowManager *wm;
6516   void *owner;
6517 };
6518 
progress_tooltip_func(bContext * UNUSED (C),void * argN,const char * UNUSED (tip))6519 static char *progress_tooltip_func(bContext *UNUSED(C), void *argN, const char *UNUSED(tip))
6520 {
6521   struct ProgressTooltip_Store *arg = argN;
6522   wmWindowManager *wm = arg->wm;
6523   void *owner = arg->owner;
6524 
6525   const float progress = WM_jobs_progress(wm, owner);
6526 
6527   /* create tooltip text and associate it with the job */
6528   char elapsed_str[32];
6529   char remaining_str[32] = "Unknown";
6530   const double elapsed = PIL_check_seconds_timer() - WM_jobs_starttime(wm, owner);
6531   BLI_timecode_string_from_time_simple(elapsed_str, sizeof(elapsed_str), elapsed);
6532 
6533   if (progress) {
6534     const double remaining = (elapsed / (double)progress) - elapsed;
6535     BLI_timecode_string_from_time_simple(remaining_str, sizeof(remaining_str), remaining);
6536   }
6537 
6538   return BLI_sprintfN(
6539       "Time Remaining: %s\n"
6540       "Time Elapsed: %s",
6541       remaining_str,
6542       elapsed_str);
6543 }
6544 
uiTemplateRunningJobs(uiLayout * layout,bContext * C)6545 void uiTemplateRunningJobs(uiLayout *layout, bContext *C)
6546 {
6547   Main *bmain = CTX_data_main(C);
6548   wmWindowManager *wm = CTX_wm_manager(C);
6549   ScrArea *area = CTX_wm_area(C);
6550   void *owner = NULL;
6551   int handle_event, icon = 0;
6552 
6553   uiBlock *block = uiLayoutGetBlock(layout);
6554   UI_block_layout_set_current(block, layout);
6555 
6556   UI_block_func_handle_set(block, do_running_jobs, NULL);
6557 
6558   /* another scene can be rendering too, for example via compositor */
6559   LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) {
6560     if (WM_jobs_test(wm, scene, WM_JOB_TYPE_ANY)) {
6561       handle_event = B_STOPOTHER;
6562       icon = ICON_NONE;
6563       owner = scene;
6564     }
6565     else {
6566       continue;
6567     }
6568 
6569     if (WM_jobs_test(wm, scene, WM_JOB_TYPE_SEQ_BUILD_PROXY)) {
6570       handle_event = B_STOPSEQ;
6571       icon = ICON_SEQUENCE;
6572       owner = scene;
6573       break;
6574     }
6575     if (WM_jobs_test(wm, scene, WM_JOB_TYPE_SEQ_BUILD_PREVIEW)) {
6576       handle_event = B_STOPSEQ;
6577       icon = ICON_SEQUENCE;
6578       break;
6579     }
6580     if (WM_jobs_test(wm, scene, WM_JOB_TYPE_CLIP_BUILD_PROXY)) {
6581       handle_event = B_STOPCLIP;
6582       icon = ICON_TRACKER;
6583       break;
6584     }
6585     if (WM_jobs_test(wm, scene, WM_JOB_TYPE_CLIP_PREFETCH)) {
6586       handle_event = B_STOPCLIP;
6587       icon = ICON_TRACKER;
6588       break;
6589     }
6590     if (WM_jobs_test(wm, scene, WM_JOB_TYPE_CLIP_TRACK_MARKERS)) {
6591       handle_event = B_STOPCLIP;
6592       icon = ICON_TRACKER;
6593       break;
6594     }
6595     if (WM_jobs_test(wm, scene, WM_JOB_TYPE_CLIP_SOLVE_CAMERA)) {
6596       handle_event = B_STOPCLIP;
6597       icon = ICON_TRACKER;
6598       break;
6599     }
6600     if (WM_jobs_test(wm, scene, WM_JOB_TYPE_FILESEL_READDIR)) {
6601       handle_event = B_STOPFILE;
6602       icon = ICON_FILEBROWSER;
6603       break;
6604     }
6605     if (WM_jobs_test(wm, scene, WM_JOB_TYPE_RENDER)) {
6606       handle_event = B_STOPRENDER;
6607       icon = ICON_SCENE;
6608       break;
6609     }
6610     if (WM_jobs_test(wm, scene, WM_JOB_TYPE_COMPOSITE)) {
6611       handle_event = B_STOPCOMPO;
6612       icon = ICON_RENDERLAYERS;
6613       break;
6614     }
6615     if (WM_jobs_test(wm, scene, WM_JOB_TYPE_OBJECT_BAKE_TEXTURE) ||
6616         WM_jobs_test(wm, scene, WM_JOB_TYPE_OBJECT_BAKE)) {
6617       /* Skip bake jobs in compositor to avoid compo header displaying
6618        * progress bar which is not being updated (bake jobs only need
6619        * to update NC_IMAGE context.
6620        */
6621       if (area->spacetype != SPACE_NODE) {
6622         handle_event = B_STOPOTHER;
6623         icon = ICON_IMAGE;
6624         break;
6625       }
6626       continue;
6627     }
6628     if (WM_jobs_test(wm, scene, WM_JOB_TYPE_DPAINT_BAKE)) {
6629       handle_event = B_STOPOTHER;
6630       icon = ICON_MOD_DYNAMICPAINT;
6631       break;
6632     }
6633     if (WM_jobs_test(wm, scene, WM_JOB_TYPE_POINTCACHE)) {
6634       handle_event = B_STOPOTHER;
6635       icon = ICON_PHYSICS;
6636       break;
6637     }
6638     if (WM_jobs_test(wm, scene, WM_JOB_TYPE_OBJECT_SIM_FLUID)) {
6639       handle_event = B_STOPOTHER;
6640       icon = ICON_MOD_FLUIDSIM;
6641       break;
6642     }
6643     if (WM_jobs_test(wm, scene, WM_JOB_TYPE_OBJECT_SIM_OCEAN)) {
6644       handle_event = B_STOPOTHER;
6645       icon = ICON_MOD_OCEAN;
6646       break;
6647     }
6648   }
6649 
6650   if (owner) {
6651     const uiFontStyle *fstyle = UI_FSTYLE_WIDGET;
6652     const bool active = !(G.is_break || WM_jobs_is_stopped(wm, owner));
6653 
6654     uiLayout *row = uiLayoutRow(layout, false);
6655     block = uiLayoutGetBlock(row);
6656 
6657     /* get percentage done and set it as the UI text */
6658     const float progress = WM_jobs_progress(wm, owner);
6659     char text[8];
6660     BLI_snprintf(text, 8, "%d%%", (int)(progress * 100));
6661 
6662     const char *name = active ? WM_jobs_name(wm, owner) : "Canceling...";
6663 
6664     /* job name and icon */
6665     const int textwidth = UI_fontstyle_string_width(fstyle, name);
6666     uiDefIconTextBut(block,
6667                      UI_BTYPE_LABEL,
6668                      0,
6669                      icon,
6670                      name,
6671                      0,
6672                      0,
6673                      textwidth + UI_UNIT_X * 1.5f,
6674                      UI_UNIT_Y,
6675                      NULL,
6676                      0.0f,
6677                      0.0f,
6678                      0.0f,
6679                      0.0f,
6680                      "");
6681 
6682     /* stick progress bar and cancel button together */
6683     row = uiLayoutRow(layout, true);
6684     uiLayoutSetActive(row, active);
6685     block = uiLayoutGetBlock(row);
6686 
6687     {
6688       struct ProgressTooltip_Store *tip_arg = MEM_mallocN(sizeof(*tip_arg), __func__);
6689       tip_arg->wm = wm;
6690       tip_arg->owner = owner;
6691       uiButProgressbar *but_progress = (uiButProgressbar *)uiDefIconTextBut(block,
6692                                                                             UI_BTYPE_PROGRESS_BAR,
6693                                                                             0,
6694                                                                             0,
6695                                                                             text,
6696                                                                             UI_UNIT_X,
6697                                                                             0,
6698                                                                             UI_UNIT_X * 6.0f,
6699                                                                             UI_UNIT_Y,
6700                                                                             NULL,
6701                                                                             0.0f,
6702                                                                             0.0f,
6703                                                                             0.0f,
6704                                                                             0,
6705                                                                             NULL);
6706 
6707       but_progress->progress = progress;
6708       UI_but_func_tooltip_set(&but_progress->but, progress_tooltip_func, tip_arg);
6709     }
6710 
6711     if (!wm->is_interface_locked) {
6712       uiDefIconTextBut(block,
6713                        UI_BTYPE_BUT,
6714                        handle_event,
6715                        ICON_PANEL_CLOSE,
6716                        "",
6717                        0,
6718                        0,
6719                        UI_UNIT_X,
6720                        UI_UNIT_Y,
6721                        NULL,
6722                        0.0f,
6723                        0.0f,
6724                        0,
6725                        0,
6726                        TIP_("Stop this job"));
6727     }
6728   }
6729 
6730   if (ED_screen_animation_no_scrub(wm)) {
6731     uiDefIconTextBut(block,
6732                      UI_BTYPE_BUT,
6733                      B_STOPANIM,
6734                      ICON_CANCEL,
6735                      IFACE_("Anim Player"),
6736                      0,
6737                      0,
6738                      UI_UNIT_X * 5.0f,
6739                      UI_UNIT_Y,
6740                      NULL,
6741                      0.0f,
6742                      0.0f,
6743                      0,
6744                      0,
6745                      TIP_("Stop animation playback"));
6746   }
6747 }
6748 
6749 /** \} */
6750 
6751 /* -------------------------------------------------------------------- */
6752 /** \name Reports for Last Operator Template
6753  * \{ */
6754 
uiTemplateReportsBanner(uiLayout * layout,bContext * C)6755 void uiTemplateReportsBanner(uiLayout *layout, bContext *C)
6756 {
6757   ReportList *reports = CTX_wm_reports(C);
6758   Report *report = BKE_reports_last_displayable(reports);
6759   const uiStyle *style = UI_style_get();
6760 
6761   uiBut *but;
6762 
6763   /* if the report display has timed out, don't show */
6764   if (!reports->reporttimer) {
6765     return;
6766   }
6767 
6768   ReportTimerInfo *rti = (ReportTimerInfo *)reports->reporttimer->customdata;
6769 
6770   if (!rti || rti->widthfac == 0.0f || !report) {
6771     return;
6772   }
6773 
6774   uiLayout *ui_abs = uiLayoutAbsolute(layout, false);
6775   uiBlock *block = uiLayoutGetBlock(ui_abs);
6776 
6777   UI_fontstyle_set(&style->widgetlabel);
6778   int width = BLF_width(style->widgetlabel.uifont_id, report->message, report->len);
6779   width = min_ii((int)(rti->widthfac * width), width);
6780   width = max_ii(width, 10 * UI_DPI_FAC);
6781 
6782   /* make a box around the report to make it stand out */
6783   UI_block_align_begin(block);
6784   but = uiDefBut(
6785       block, UI_BTYPE_ROUNDBOX, 0, "", 0, 0, UI_UNIT_X + 5, UI_UNIT_Y, NULL, 0.0f, 0.0f, 0, 0, "");
6786   /* set the report's bg color in but->col - UI_BTYPE_ROUNDBOX feature */
6787   rgba_float_to_uchar(but->col, rti->col);
6788 
6789   but = uiDefBut(block,
6790                  UI_BTYPE_ROUNDBOX,
6791                  0,
6792                  "",
6793                  UI_UNIT_X + 5,
6794                  0,
6795                  UI_UNIT_X + width,
6796                  UI_UNIT_Y,
6797                  NULL,
6798                  0.0f,
6799                  0.0f,
6800                  0,
6801                  0,
6802                  "");
6803   rgba_float_to_uchar(but->col, rti->col);
6804 
6805   UI_block_align_end(block);
6806 
6807   /* icon and report message on top */
6808   const int icon = UI_icon_from_report_type(report->type);
6809 
6810   /* XXX: temporary operator to dump all reports to a text block, but only if more than 1 report
6811    * to be shown instead of icon when appropriate...
6812    */
6813   UI_block_emboss_set(block, UI_EMBOSS_NONE);
6814 
6815   if (reports->list.first != reports->list.last) {
6816     uiDefIconButO(block,
6817                   UI_BTYPE_BUT,
6818                   "SCREEN_OT_info_log_show",
6819                   WM_OP_INVOKE_REGION_WIN,
6820                   icon,
6821                   2,
6822                   0,
6823                   UI_UNIT_X,
6824                   UI_UNIT_Y,
6825                   TIP_("Click to see the remaining reports in text block: 'Recent Reports'"));
6826   }
6827   else {
6828     uiDefIconBut(
6829         block, UI_BTYPE_LABEL, 0, icon, 2, 0, UI_UNIT_X, UI_UNIT_Y, NULL, 0.0f, 0.0f, 0, 0, "");
6830   }
6831 
6832   but = uiDefButO(block,
6833                   UI_BTYPE_BUT,
6834                   "SCREEN_OT_info_log_show",
6835                   WM_OP_INVOKE_REGION_WIN,
6836                   report->message,
6837                   UI_UNIT_X + 5,
6838                   0,
6839                   UI_UNIT_X + width,
6840                   UI_UNIT_Y,
6841                   "Show in Info Log");
6842   rgba_float_to_uchar(but->col, rti->col);
6843 }
6844 
uiTemplateInputStatus(uiLayout * layout,struct bContext * C)6845 void uiTemplateInputStatus(uiLayout *layout, struct bContext *C)
6846 {
6847   wmWindow *win = CTX_wm_window(C);
6848   WorkSpace *workspace = CTX_wm_workspace(C);
6849 
6850   /* Workspace status text has priority. */
6851   if (workspace->status_text) {
6852     uiItemL(layout, workspace->status_text, ICON_NONE);
6853     return;
6854   }
6855 
6856   if (WM_window_modal_keymap_status_draw(C, win, layout)) {
6857     return;
6858   }
6859 
6860   /* Otherwise should cursor keymap status. */
6861   for (int i = 0; i < 3; i++) {
6862     uiLayout *box = uiLayoutRow(layout, false);
6863     uiLayout *col = uiLayoutColumn(box, false);
6864     uiLayout *row = uiLayoutRow(col, true);
6865     uiLayoutSetAlignment(row, UI_LAYOUT_ALIGN_LEFT);
6866 
6867     const char *msg = WM_window_cursor_keymap_status_get(win, i, 0);
6868     const char *msg_drag = WM_window_cursor_keymap_status_get(win, i, 1);
6869 
6870     if (msg || (msg_drag == NULL)) {
6871       uiItemL(row, msg ? msg : "", (ICON_MOUSE_LMB + i));
6872     }
6873 
6874     if (msg_drag) {
6875       uiItemL(row, msg_drag, (ICON_MOUSE_LMB_DRAG + i));
6876     }
6877 
6878     /* Use trick with empty string to keep icons in same position. */
6879     row = uiLayoutRow(col, false);
6880     uiItemL(row, "                                                                   ", ICON_NONE);
6881   }
6882 }
6883 
6884 /** \} */
6885 
6886 /* -------------------------------------------------------------------- */
6887 /** \name Keymap Template
6888  * \{ */
6889 
keymap_item_modified(bContext * UNUSED (C),void * kmi_p,void * UNUSED (unused))6890 static void keymap_item_modified(bContext *UNUSED(C), void *kmi_p, void *UNUSED(unused))
6891 {
6892   wmKeyMapItem *kmi = (wmKeyMapItem *)kmi_p;
6893   WM_keyconfig_update_tag(NULL, kmi);
6894 }
6895 
template_keymap_item_properties(uiLayout * layout,const char * title,PointerRNA * ptr)6896 static void template_keymap_item_properties(uiLayout *layout, const char *title, PointerRNA *ptr)
6897 {
6898   uiItemS(layout);
6899 
6900   if (title) {
6901     uiItemL(layout, title, ICON_NONE);
6902   }
6903 
6904   uiLayout *flow = uiLayoutColumnFlow(layout, 2, false);
6905 
6906   RNA_STRUCT_BEGIN_SKIP_RNA_TYPE (ptr, prop) {
6907     const bool is_set = RNA_property_is_set(ptr, prop);
6908     uiBut *but;
6909 
6910     /* recurse for nested properties */
6911     if (RNA_property_type(prop) == PROP_POINTER) {
6912       PointerRNA propptr = RNA_property_pointer_get(ptr, prop);
6913 
6914       if (propptr.data && RNA_struct_is_a(propptr.type, &RNA_OperatorProperties)) {
6915         const char *name = RNA_property_ui_name(prop);
6916         template_keymap_item_properties(layout, name, &propptr);
6917         continue;
6918       }
6919     }
6920 
6921     uiLayout *box = uiLayoutBox(flow);
6922     uiLayoutSetActive(box, is_set);
6923     uiLayout *row = uiLayoutRow(box, false);
6924 
6925     /* property value */
6926     uiItemFullR(row, ptr, prop, -1, 0, 0, NULL, ICON_NONE);
6927 
6928     if (is_set) {
6929       /* unset operator */
6930       uiBlock *block = uiLayoutGetBlock(row);
6931       UI_block_emboss_set(block, UI_EMBOSS_NONE);
6932       but = uiDefIconButO(block,
6933                           UI_BTYPE_BUT,
6934                           "UI_OT_unset_property_button",
6935                           WM_OP_EXEC_DEFAULT,
6936                           ICON_X,
6937                           0,
6938                           0,
6939                           UI_UNIT_X,
6940                           UI_UNIT_Y,
6941                           NULL);
6942       but->rnapoin = *ptr;
6943       but->rnaprop = prop;
6944       UI_block_emboss_set(block, UI_EMBOSS);
6945     }
6946   }
6947   RNA_STRUCT_END;
6948 }
6949 
uiTemplateKeymapItemProperties(uiLayout * layout,PointerRNA * ptr)6950 void uiTemplateKeymapItemProperties(uiLayout *layout, PointerRNA *ptr)
6951 {
6952   PointerRNA propptr = RNA_pointer_get(ptr, "properties");
6953 
6954   if (propptr.data) {
6955     uiBut *but = uiLayoutGetBlock(layout)->buttons.last;
6956 
6957     WM_operator_properties_sanitize(&propptr, false);
6958     template_keymap_item_properties(layout, NULL, &propptr);
6959 
6960     /* attach callbacks to compensate for missing properties update,
6961      * we don't know which keymap (item) is being modified there */
6962     for (; but; but = but->next) {
6963       /* operator buttons may store props for use (file selector, T36492) */
6964       if (but->rnaprop) {
6965         UI_but_func_set(but, keymap_item_modified, ptr->data, NULL);
6966 
6967         /* Otherwise the keymap will be re-generated which we're trying to edit,
6968          * see: T47685 */
6969         UI_but_flag_enable(but, UI_BUT_UPDATE_DELAY);
6970       }
6971     }
6972   }
6973 }
6974 
6975 /** \} */
6976 
6977 /* -------------------------------------------------------------------- */
6978 /** \name Event Icon Template
6979  *
6980  * \{ */
6981 
uiTemplateEventFromKeymapItem(struct uiLayout * layout,const char * text,const struct wmKeyMapItem * kmi,bool text_fallback)6982 bool uiTemplateEventFromKeymapItem(struct uiLayout *layout,
6983                                    const char *text,
6984                                    const struct wmKeyMapItem *kmi,
6985                                    bool text_fallback)
6986 {
6987   bool ok = false;
6988 
6989   int icon_mod[4];
6990 #ifdef WITH_HEADLESS
6991   int icon = 0;
6992 #else
6993   const int icon = UI_icon_from_keymap_item(kmi, icon_mod);
6994 #endif
6995   if (icon != 0) {
6996     for (int j = 0; j < ARRAY_SIZE(icon_mod) && icon_mod[j]; j++) {
6997       uiItemL(layout, "", icon_mod[j]);
6998     }
6999     uiItemL(layout, text, icon);
7000     ok = true;
7001   }
7002   else if (text_fallback) {
7003     const char *event_text = WM_key_event_string(kmi->type, true);
7004     uiItemL(layout, event_text, ICON_NONE);
7005     uiItemL(layout, text, ICON_NONE);
7006     ok = true;
7007   }
7008   return ok;
7009 }
7010 
7011 /** \} */
7012 
7013 /* -------------------------------------------------------------------- */
7014 /** \name Color Management Template
7015  * \{ */
7016 
uiTemplateColorspaceSettings(uiLayout * layout,PointerRNA * ptr,const char * propname)7017 void uiTemplateColorspaceSettings(uiLayout *layout, PointerRNA *ptr, const char *propname)
7018 {
7019   PropertyRNA *prop = RNA_struct_find_property(ptr, propname);
7020 
7021   if (!prop) {
7022     printf(
7023         "%s: property not found: %s.%s\n", __func__, RNA_struct_identifier(ptr->type), propname);
7024     return;
7025   }
7026 
7027   PointerRNA colorspace_settings_ptr = RNA_property_pointer_get(ptr, prop);
7028 
7029   uiItemR(layout, &colorspace_settings_ptr, "name", 0, IFACE_("Color Space"), ICON_NONE);
7030 }
7031 
uiTemplateColormanagedViewSettings(uiLayout * layout,bContext * UNUSED (C),PointerRNA * ptr,const char * propname)7032 void uiTemplateColormanagedViewSettings(uiLayout *layout,
7033                                         bContext *UNUSED(C),
7034                                         PointerRNA *ptr,
7035                                         const char *propname)
7036 {
7037   PropertyRNA *prop = RNA_struct_find_property(ptr, propname);
7038 
7039   if (!prop) {
7040     printf(
7041         "%s: property not found: %s.%s\n", __func__, RNA_struct_identifier(ptr->type), propname);
7042     return;
7043   }
7044 
7045   PointerRNA view_transform_ptr = RNA_property_pointer_get(ptr, prop);
7046   ColorManagedViewSettings *view_settings = view_transform_ptr.data;
7047 
7048   uiLayout *col = uiLayoutColumn(layout, false);
7049 
7050   uiLayout *row = uiLayoutRow(col, false);
7051   uiItemR(row, &view_transform_ptr, "view_transform", 0, IFACE_("View"), ICON_NONE);
7052 
7053   col = uiLayoutColumn(layout, false);
7054   uiItemR(col, &view_transform_ptr, "exposure", 0, NULL, ICON_NONE);
7055   uiItemR(col, &view_transform_ptr, "gamma", 0, NULL, ICON_NONE);
7056 
7057   uiItemR(col, &view_transform_ptr, "look", 0, IFACE_("Look"), ICON_NONE);
7058 
7059   col = uiLayoutColumn(layout, false);
7060   uiItemR(col, &view_transform_ptr, "use_curve_mapping", 0, NULL, ICON_NONE);
7061   if (view_settings->flag & COLORMANAGE_VIEW_USE_CURVES) {
7062     uiTemplateCurveMapping(
7063         col, &view_transform_ptr, "curve_mapping", 'c', true, false, false, false);
7064   }
7065 }
7066 
7067 /** \} */
7068 
7069 /* -------------------------------------------------------------------- */
7070 /** \name Component Menu
7071  * \{ */
7072 
7073 typedef struct ComponentMenuArgs {
7074   PointerRNA ptr;
7075   char propname[64]; /* XXX arbitrary */
7076 } ComponentMenuArgs;
7077 /* NOTE: this is a block-menu, needs 0 events, otherwise the menu closes */
component_menu(bContext * C,ARegion * region,void * args_v)7078 static uiBlock *component_menu(bContext *C, ARegion *region, void *args_v)
7079 {
7080   ComponentMenuArgs *args = (ComponentMenuArgs *)args_v;
7081 
7082   uiBlock *block = UI_block_begin(C, region, __func__, UI_EMBOSS);
7083   UI_block_flag_enable(block, UI_BLOCK_KEEP_OPEN);
7084 
7085   uiLayout *layout = uiLayoutColumn(UI_block_layout(block,
7086                                                     UI_LAYOUT_VERTICAL,
7087                                                     UI_LAYOUT_PANEL,
7088                                                     0,
7089                                                     0,
7090                                                     UI_UNIT_X * 6,
7091                                                     UI_UNIT_Y,
7092                                                     0,
7093                                                     UI_style_get()),
7094                                     0);
7095 
7096   uiItemR(layout, &args->ptr, args->propname, UI_ITEM_R_EXPAND, "", ICON_NONE);
7097 
7098   UI_block_bounds_set_normal(block, 0.3f * U.widget_unit);
7099   UI_block_direction_set(block, UI_DIR_DOWN);
7100 
7101   return block;
7102 }
uiTemplateComponentMenu(uiLayout * layout,PointerRNA * ptr,const char * propname,const char * name)7103 void uiTemplateComponentMenu(uiLayout *layout,
7104                              PointerRNA *ptr,
7105                              const char *propname,
7106                              const char *name)
7107 {
7108   ComponentMenuArgs *args = MEM_callocN(sizeof(ComponentMenuArgs), "component menu template args");
7109 
7110   args->ptr = *ptr;
7111   BLI_strncpy(args->propname, propname, sizeof(args->propname));
7112 
7113   uiBlock *block = uiLayoutGetBlock(layout);
7114   UI_block_align_begin(block);
7115 
7116   uiBut *but = uiDefBlockButN(
7117       block, component_menu, args, name, 0, 0, UI_UNIT_X * 6, UI_UNIT_Y, "");
7118   /* set rna directly, uiDefBlockButN doesn't do this */
7119   but->rnapoin = *ptr;
7120   but->rnaprop = RNA_struct_find_property(ptr, propname);
7121   but->rnaindex = 0;
7122 
7123   UI_block_align_end(block);
7124 }
7125 
7126 /** \} */
7127 
7128 /* -------------------------------------------------------------------- */
7129 /** \name Node Socket Icon Template
7130  * \{ */
7131 
uiTemplateNodeSocket(uiLayout * layout,bContext * UNUSED (C),float * color)7132 void uiTemplateNodeSocket(uiLayout *layout, bContext *UNUSED(C), float *color)
7133 {
7134   uiBlock *block = uiLayoutGetBlock(layout);
7135   UI_block_align_begin(block);
7136 
7137   /* XXX using explicit socket colors is not quite ideal.
7138    * Eventually it should be possible to use theme colors for this purpose,
7139    * but this requires a better design for extendable color palettes in user prefs.
7140    */
7141   uiBut *but = uiDefBut(
7142       block, UI_BTYPE_NODE_SOCKET, 0, "", 0, 0, UI_UNIT_X, UI_UNIT_Y, NULL, 0, 0, 0, 0, "");
7143   rgba_float_to_uchar(but->col, color);
7144 
7145   UI_block_align_end(block);
7146 }
7147 
7148 /** \} */
7149 
7150 /* -------------------------------------------------------------------- */
7151 /** \name Cache File Template
7152  * \{ */
7153 
uiTemplateCacheFile(uiLayout * layout,const bContext * C,PointerRNA * ptr,const char * propname)7154 void uiTemplateCacheFile(uiLayout *layout,
7155                          const bContext *C,
7156                          PointerRNA *ptr,
7157                          const char *propname)
7158 {
7159   if (!ptr->data) {
7160     return;
7161   }
7162 
7163   PropertyRNA *prop = RNA_struct_find_property(ptr, propname);
7164 
7165   if (!prop) {
7166     printf(
7167         "%s: property not found: %s.%s\n", __func__, RNA_struct_identifier(ptr->type), propname);
7168     return;
7169   }
7170 
7171   if (RNA_property_type(prop) != PROP_POINTER) {
7172     printf("%s: expected pointer property for %s.%s\n",
7173            __func__,
7174            RNA_struct_identifier(ptr->type),
7175            propname);
7176     return;
7177   }
7178 
7179   PointerRNA fileptr = RNA_property_pointer_get(ptr, prop);
7180   CacheFile *file = fileptr.data;
7181 
7182   uiLayoutSetContextPointer(layout, "edit_cachefile", &fileptr);
7183 
7184   uiTemplateID(layout,
7185                C,
7186                ptr,
7187                propname,
7188                NULL,
7189                "CACHEFILE_OT_open",
7190                NULL,
7191                UI_TEMPLATE_ID_FILTER_ALL,
7192                false,
7193                NULL);
7194 
7195   if (!file) {
7196     return;
7197   }
7198 
7199   SpaceProperties *sbuts = CTX_wm_space_properties(C);
7200 
7201   uiLayout *row, *sub, *subsub;
7202 
7203   uiLayoutSetPropSep(layout, true);
7204 
7205   row = uiLayoutRow(layout, true);
7206   uiItemR(row, &fileptr, "filepath", 0, NULL, ICON_NONE);
7207   sub = uiLayoutRow(row, true);
7208   uiItemO(sub, "", ICON_FILE_REFRESH, "cachefile.reload");
7209 
7210   row = uiLayoutRow(layout, false);
7211   uiItemR(row, &fileptr, "is_sequence", 0, NULL, ICON_NONE);
7212 
7213   row = uiLayoutRowWithHeading(layout, true, IFACE_("Override Frame"));
7214   sub = uiLayoutRow(row, true);
7215   uiLayoutSetPropDecorate(sub, false);
7216   uiItemR(sub, &fileptr, "override_frame", 0, "", ICON_NONE);
7217   subsub = uiLayoutRow(sub, true);
7218   uiLayoutSetActive(subsub, RNA_boolean_get(&fileptr, "override_frame"));
7219   uiItemR(subsub, &fileptr, "frame", 0, "", ICON_NONE);
7220   uiItemDecoratorR(row, &fileptr, "frame", 0);
7221 
7222   row = uiLayoutRow(layout, false);
7223   uiItemR(row, &fileptr, "frame_offset", 0, NULL, ICON_NONE);
7224   uiLayoutSetActive(row, !RNA_boolean_get(&fileptr, "is_sequence"));
7225 
7226   if (sbuts->mainb == BCONTEXT_CONSTRAINT) {
7227     row = uiLayoutRow(layout, false);
7228     uiItemR(row, &fileptr, "scale", 0, IFACE_("Manual Scale"), ICON_NONE);
7229   }
7230 
7231   uiItemR(layout, &fileptr, "velocity_name", 0, NULL, ICON_NONE);
7232   uiItemR(layout, &fileptr, "velocity_unit", 0, NULL, ICON_NONE);
7233 
7234   /* TODO: unused for now, so no need to expose. */
7235 #if 0
7236   row = uiLayoutRow(layout, false);
7237   uiItemR(row, &fileptr, "forward_axis", 0, "Forward Axis", ICON_NONE);
7238 
7239   row = uiLayoutRow(layout, false);
7240   uiItemR(row, &fileptr, "up_axis", 0, "Up Axis", ICON_NONE);
7241 #endif
7242 }
7243 
7244 /** \} */
7245 
7246 /* -------------------------------------------------------------------- */
7247 /** \name Recent Files Template
7248  * \{ */
7249 
uiTemplateRecentFiles(uiLayout * layout,int rows)7250 int uiTemplateRecentFiles(uiLayout *layout, int rows)
7251 {
7252   int i;
7253   LISTBASE_FOREACH_INDEX (RecentFile *, recent, &G.recent_files, i) {
7254     if (i >= rows) {
7255       break;
7256     }
7257 
7258     const char *filename = BLI_path_basename(recent->filepath);
7259     PointerRNA ptr;
7260     uiItemFullO(layout,
7261                 "WM_OT_open_mainfile",
7262                 filename,
7263                 BLO_has_bfile_extension(filename) ? ICON_FILE_BLEND : ICON_FILE_BACKUP,
7264                 NULL,
7265                 WM_OP_INVOKE_DEFAULT,
7266                 0,
7267                 &ptr);
7268     RNA_string_set(&ptr, "filepath", recent->filepath);
7269     RNA_boolean_set(&ptr, "display_file_selector", false);
7270   }
7271 
7272   return i;
7273 }
7274 
7275 /** \} */
7276 
7277 /* -------------------------------------------------------------------- */
7278 /** \name FileSelectParams Path Button Template
7279  * \{ */
7280 
uiTemplateFileSelectPath(uiLayout * layout,bContext * C,FileSelectParams * params)7281 void uiTemplateFileSelectPath(uiLayout *layout, bContext *C, FileSelectParams *params)
7282 {
7283   bScreen *screen = CTX_wm_screen(C);
7284   SpaceFile *sfile = CTX_wm_space_file(C);
7285 
7286   ED_file_path_button(screen, sfile, params, uiLayoutGetBlock(layout));
7287 }
7288 
7289 /** \} */
7290