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, ®ion->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, ®ion->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, ®ion->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, ®ion->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 ®ion->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(®ion->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(®ion->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(®ion->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