Blender V5.0
interface_template_list.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2023 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
8
9#include <algorithm>
10#include <cstdlib>
11#include <cstring>
12
13#include "BLI_fnmatch.h"
14#include "BLI_function_ref.hh"
15#include "BLI_listbase.h"
16#include "BLI_math_base.h"
17#include "BLI_string.h"
18#include "BLI_string_ref.hh"
19#include "BLI_string_utf8.h"
20#include "BLI_utildefines.h"
21#include "BLI_vector.hh"
22
23#include "BKE_screen.hh"
24
25#include "BLT_translation.hh"
26
27#include "ED_screen.hh"
28
29#include "MEM_guardedalloc.h"
30
31#include "RNA_access.hh"
32#include "RNA_prototypes.hh"
33
35#include "UI_view2d.hh"
36
37#include "WM_api.hh"
38
39#include "interface_intern.hh"
40
41using namespace blender;
42
53
54 /* Index as stored in the input property. I.e. the index before sorting. */
56};
57
66
72 /* Index of the active item following visual order. I.e. unlike
73 * TemplateListInputData.active_item_idx, this is the index after sorting. */
75};
76
85
87 int visual_items; /* Visual number of items (i.e. number of items we have room to display). */
88 int start_idx; /* Index of first item to display. */
89 int end_idx; /* Index of last item to display + 1. */
90};
91
92static void uilist_draw_item_default(uiList *ui_list,
93 const bContext * /*C*/,
94 uiLayout *layout,
95 PointerRNA * /*dataptr*/,
96 PointerRNA *itemptr,
97 int icon,
98 PointerRNA * /*active_dataptr*/,
99 const char * /*active_propname*/,
100 int /*index*/,
101 int /*flt_flag*/)
102{
103 PropertyRNA *nameprop = RNA_struct_name_property(itemptr->type);
104
105 /* Simplest one! */
106 switch (ui_list->layout_type) {
109 default:
110 if (nameprop) {
111 layout->prop(itemptr, nameprop, RNA_NO_INDEX, 0, UI_ITEM_R_NO_BG, "", icon);
112 }
113 else {
114 layout->label("", icon);
115 }
116 break;
117 }
118}
119
120static void uilist_draw_filter_default(uiList *ui_list, const bContext * /*C*/, uiLayout *layout)
121{
122 PointerRNA listptr = RNA_pointer_create_discrete(nullptr, &RNA_UIList, ui_list);
123
124 uiLayout *row = &layout->row(false);
125
126 uiLayout *subrow = &row->row(true);
127 subrow->prop(&listptr, "filter_name", UI_ITEM_NONE, "", ICON_NONE);
128 subrow->prop(&listptr,
129 "use_filter_invert",
131 "",
132 ICON_ARROW_LEFTRIGHT);
133
134 if ((ui_list->filter_sort_flag & UILST_FLT_SORT_LOCK) == 0) {
135 subrow = &row->row(true);
136 subrow->prop(
137 &listptr, "use_filter_sort_alpha", UI_ITEM_R_TOGGLE | UI_ITEM_R_ICON_ONLY, "", ICON_NONE);
138 subrow->prop(&listptr,
139 "use_filter_sort_reverse",
141 "",
142 (ui_list->filter_sort_flag & UILST_FLT_SORT_REVERSE) ? ICON_SORT_DESC :
143 ICON_SORT_ASC);
144 }
145}
146
148{
149 const char *filter_raw = list.filter_byname;
150
151 if (filter_raw[0]) {
152 const size_t slen = strlen(filter_raw);
153
154 /* Implicitly add heading/trailing wildcards if needed. */
155 if (slen + 3 <= sizeof(storage_.filter_buff)) {
156 filter_ = storage_.filter_buff;
157 }
158 else {
159 filter_ = storage_.filter_dyn = MEM_malloc_arrayN<char>((slen + 3), "filter_dyn");
160 }
161 BLI_strncpy_ensure_pad(filter_, filter_raw, '*', slen + 3);
162 }
163}
164
166{
167 MEM_SAFE_FREE(storage_.filter_dyn);
168}
169
172 int /*index*/)
173{
174 if (!filter_) {
176 }
177
178 /* Use `fnmatch` for shell-style globing.
179 * - Case-insensitive.
180 * - Don't handle escape characters as "special" characters are not expected in names.
181 * Unlike shell input - `\` should be treated like any other character.
182 */
183 const int fn_flag = FNM_CASEFOLD | FNM_NOESCAPE;
184 if (fnmatch(filter_, name.c_str(), fn_flag) == 0) {
186 }
188}
189
194
195static int cmpstringp(const void *p1, const void *p2)
196{
197 /* Case-insensitive comparison. */
198 return BLI_strcasecmp(static_cast<const StringCmp *>(p1)->name,
199 static_cast<const StringCmp *>(p2)->name);
200}
201
203 const bContext * /*C*/,
204 uiListItemFilterFn item_filter_fn,
205 PointerRNA *dataptr,
206 const char *propname,
207 uiListItemGetNameFn get_name_fn)
208{
209 uiListDyn *dyn_data = ui_list->dyn_data;
210 PropertyRNA *prop = RNA_struct_find_property(dataptr, propname);
211
212 const bool filter_exclude = (ui_list->filter_flag & UILST_FLT_EXCLUDE) != 0;
213 const bool order_by_name = (ui_list->filter_sort_flag & UILST_FLT_SORT_ALPHA) &&
215 const int len = RNA_property_collection_length(dataptr, prop);
216
217 dyn_data->items_shown = dyn_data->items_len = len;
218
219 if (len && (order_by_name || item_filter_fn)) {
220 StringCmp *names = nullptr;
221 int order_idx = 0, i = 0;
222
223 if (order_by_name) {
224 names = MEM_calloc_arrayN<StringCmp>(len, "StringCmp");
225 }
226
227 if (item_filter_fn) {
228 dyn_data->items_filter_flags = MEM_calloc_arrayN<int>(len, "items_filter_flags");
229 dyn_data->items_shown = 0;
230 }
231
232 RNA_PROP_BEGIN (dataptr, itemptr, prop) {
233 bool do_order = false;
234
235 char *namebuf;
236 if (get_name_fn) {
237 namebuf = BLI_strdup(get_name_fn(itemptr, i).c_str());
238 }
239 else {
240 namebuf = RNA_struct_name_get_alloc(&itemptr, nullptr, 0, nullptr);
241 }
242
243 const char *name = namebuf ? namebuf : "";
244
245 if (item_filter_fn) {
246 const eUIListFilterResult filter_result = item_filter_fn(itemptr, name, i);
247
248 if (filter_result == UI_LIST_ITEM_NEVER_SHOW) {
250 }
251 else if (filter_result == UI_LIST_ITEM_FILTER_MATCHES) {
252 if (!filter_exclude) {
254 dyn_data->items_shown++;
255 do_order = order_by_name;
256 }
257 // printf("%s: '%s' matches '%s'\n", __func__, name, filter);
258 }
259 else if (filter_exclude) {
261 dyn_data->items_shown++;
262 do_order = order_by_name;
263 }
264 }
265 else {
266 do_order = order_by_name;
267 }
268
269 if (do_order) {
270 names[order_idx].org_idx = order_idx;
271 STRNCPY(names[order_idx++].name, name);
272 }
273
274 /* free name */
275 if (namebuf) {
276 MEM_freeN(namebuf);
277 }
278 i++;
279 }
281
282 if (order_by_name) {
283 int new_idx;
284 /* NOTE: order_idx equals either to ui_list->items_len if no filtering done,
285 * or to ui_list->items_shown if filter is enabled,
286 * or to (ui_list->items_len - ui_list->items_shown) if filtered items are excluded.
287 * This way, we only sort items we actually intend to draw!
288 */
289 qsort(names, order_idx, sizeof(StringCmp), cmpstringp);
290
291 dyn_data->items_filter_neworder = MEM_malloc_arrayN<int>(order_idx, "items_filter_neworder");
292 for (new_idx = 0; new_idx < order_idx; new_idx++) {
293 dyn_data->items_filter_neworder[names[new_idx].org_idx] = new_idx;
294 }
295 }
296
297 if (names) {
298 MEM_freeN(names);
299 }
300 }
301}
302
303bool UI_list_item_index_is_filtered_visible(const uiList *ui_list, const int item_idx)
304{
305 const uiListDyn *dyn_data = ui_list->dyn_data;
306
307 if (!dyn_data->items_filter_flags) {
308 /* If there are no filter flags to check, always consider all items visible. */
309 return true;
310 }
311
312 if (dyn_data->items_filter_flags[item_idx] & UILST_FLT_ITEM_NEVER_SHOW) {
313 return false;
314 }
315
316 return (dyn_data->items_filter_flags[item_idx] & UILST_FLT_ITEM);
317}
318
323 const bContext *C,
324 PointerRNA *dataptr,
325 const char *propname)
326{
327 if (ui_list->filter_byname[0]) {
328 uiListNameFilter name_filter(*ui_list);
329 UI_list_filter_and_sort_items(ui_list, C, name_filter, dataptr, propname);
330 }
331 /* Optimization: Skip filtering entirely when there is no filter string set. */
332 else {
333 UI_list_filter_and_sort_items(ui_list, C, nullptr, dataptr, propname);
334 }
335}
336
337static void uilist_free_dyn_data(uiList *ui_list)
338{
339 uiListDyn *dyn_data = ui_list->dyn_data;
340 if (!dyn_data) {
341 return;
342 }
343
346 MEM_SAFE_FREE(dyn_data->customdata);
347}
348
355static bool ui_template_list_data_retrieve(const StringRef listtype_name,
356 const char *list_id,
357 PointerRNA *dataptr,
358 const StringRefNull propname,
359 PointerRNA *active_dataptr,
360 const StringRefNull active_propname,
361 const char *item_dyntip_propname,
362 TemplateListInputData *r_input_data,
363 uiListType **r_list_type)
364{
365 *r_input_data = {};
366
367 /* Forbid default UI_UL_DEFAULT_CLASS_NAME list class without a custom list_id! */
368 if ((UI_UL_DEFAULT_CLASS_NAME == listtype_name) && !(list_id && list_id[0])) {
369 RNA_warning("template_list using default '%s' UIList class must provide a custom list_id",
371 return false;
372 }
373
374 if (!active_dataptr->data) {
375 RNA_warning("No active data");
376 return false;
377 }
378
379 r_input_data->dataptr = *dataptr;
380 if (dataptr->data) {
381 r_input_data->prop = RNA_struct_find_property(dataptr, propname.c_str());
382 if (!r_input_data->prop) {
384 "Property not found: %s.%s", RNA_struct_identifier(dataptr->type), propname.c_str());
385 return false;
386 }
387 }
388
389 r_input_data->active_dataptr = *active_dataptr;
390 r_input_data->activeprop = RNA_struct_find_property(active_dataptr, active_propname.c_str());
391 if (!r_input_data->activeprop) {
392 RNA_warning("Property not found: %s.%s",
393 RNA_struct_identifier(active_dataptr->type),
394 active_propname.c_str());
395 return false;
396 }
397
398 if (r_input_data->prop) {
399 const PropertyType type = RNA_property_type(r_input_data->prop);
400 if (type != PROP_COLLECTION) {
401 RNA_warning("Expected a collection data property");
402 return false;
403 }
404 }
405
406 const PropertyType activetype = RNA_property_type(r_input_data->activeprop);
407 if (activetype != PROP_INT) {
408 RNA_warning("Expected an integer active data property");
409 return false;
410 }
411
412 /* Find the uiList type. */
413 if (!(*r_list_type = WM_uilisttype_find(listtype_name, false))) {
414 RNA_warning("List type %s not found", std::string(listtype_name).c_str());
415 return false;
416 }
417
418 r_input_data->active_item_idx = RNA_property_int_get(&r_input_data->active_dataptr,
419 r_input_data->activeprop);
420 r_input_data->item_dyntip_propname = item_dyntip_propname;
421
422 return true;
423}
424
426 PropertyRNA *list_prop,
427 const uiList *ui_list,
428 int activei,
429 TemplateListItems *r_items)
430{
431 const uiListDyn *dyn_data = ui_list->dyn_data;
432 const bool order_reverse = (ui_list->filter_sort_flag & UILST_FLT_SORT_REVERSE) != 0;
433 int i = 0;
434 int reorder_i = 0;
435 bool activei_mapping_pending = true;
436
437 RNA_PROP_BEGIN (list_ptr, itemptr, list_prop) {
439 int new_order_idx;
440 if (dyn_data->items_filter_neworder) {
441 new_order_idx = dyn_data->items_filter_neworder[reorder_i++];
442 new_order_idx = order_reverse ? dyn_data->items_shown - new_order_idx - 1 : new_order_idx;
443 }
444 else {
445 new_order_idx = order_reverse ? dyn_data->items_shown - ++reorder_i : reorder_i++;
446 }
447 // printf("%s: ii: %d\n", __func__, ii);
448 r_items->item_vec[new_order_idx].item = itemptr;
449 r_items->item_vec[new_order_idx].org_idx = i;
450 r_items->item_vec[new_order_idx].flt_flag = dyn_data->items_filter_flags ?
451 dyn_data->items_filter_flags[i] :
452 0;
453
454 if (activei_mapping_pending && activei == i) {
455 activei = new_order_idx;
456 /* So that we do not map again activei! */
457 activei_mapping_pending = false;
458 }
459#if 0 /* For now, do not alter active element, even if it will be hidden... */
460 else if (activei < i) {
461 /* We do not want an active but invisible item!
462 * Only exception is when all items are filtered out...
463 */
464 if (prev_order_idx >= 0) {
465 activei = prev_order_idx;
466 RNA_property_int_set(active_dataptr, activeprop, prev_i);
467 }
468 else {
469 activei = new_order_idx;
470 RNA_property_int_set(active_dataptr, activeprop, i);
471 }
472 }
473 prev_i = i;
474 prev_ii = new_order_idx;
475#endif
476 }
477 i++;
478 }
480
481 /* If mapping is still pending, no active item was found. Mark as invalid (-1) */
482 r_items->active_item_idx = activei_mapping_pending ? -1 : activei;
483}
484
489 uiList *ui_list,
490 TemplateListInputData *input_data,
491 const uiListFilterItemsFunc filter_items_fn,
492 TemplateListItems *r_items)
493{
494 uiListDyn *dyn_data = ui_list->dyn_data;
495
496 /* Filter list items! (not for compact layout, though) */
497 if (input_data->dataptr.data && input_data->prop) {
498 int items_shown;
499#if 0
500 int prev_ii = -1, prev_i;
501#endif
502
503 if (ui_list->layout_type == UILST_LAYOUT_COMPACT) {
505 &input_data->dataptr, input_data->prop);
506 }
507 else {
508 // printf("%s: filtering...\n", __func__);
509 filter_items_fn(ui_list, C, &input_data->dataptr, RNA_property_identifier(input_data->prop));
510 // printf("%s: filtering done.\n", __func__);
511 }
512
513 items_shown = dyn_data->items_shown;
514 if (items_shown >= 0) {
515 r_items->item_vec.resize(items_shown);
516 // printf("%s: items shown: %d.\n", __func__, items_shown);
517
519 &input_data->dataptr, input_data->prop, ui_list, input_data->active_item_idx, r_items);
520 }
521 }
522}
523
524static void uilist_prepare(uiList *ui_list,
525 const TemplateListItems *items,
526 const TemplateListLayoutDrawData *layout_data,
527 TemplateListVisualInfo *r_visual_info)
528{
529 uiListDyn *dyn_data = ui_list->dyn_data;
530 const bool use_auto_size = (ui_list->list_grip <
531 (layout_data->rows - UI_LIST_AUTO_SIZE_THRESHOLD));
532
533 int actual_rows = layout_data->rows;
534 int actual_maxrows = layout_data->maxrows;
535 int columns = layout_data->columns;
536
537 /* default rows */
538 if (actual_rows <= 0) {
539 actual_rows = 5;
540 }
541 dyn_data->visual_height_min = actual_rows;
542 if (actual_maxrows < actual_rows) {
543 actual_maxrows = max_ii(actual_rows, 5);
544 }
545 if (columns <= 0) {
546 columns = 9;
547 }
548
549 int activei_row;
550 if (columns > 1) {
551 dyn_data->height = int(ceil(double(items->item_vec.size()) / double(columns)));
552 activei_row = int(floor(double(items->active_item_idx) / double(columns)));
553 }
554 else {
555 dyn_data->height = items->item_vec.size();
556 activei_row = items->active_item_idx;
557 }
558
559 dyn_data->columns = columns;
560
561 if (!use_auto_size) {
562 /* No auto-size, yet we clamp at min size! */
563 actual_rows = max_ii(ui_list->list_grip, actual_rows);
564 }
565 else if ((actual_rows != actual_maxrows) && (dyn_data->height > actual_rows)) {
566 /* Expand size if needed and possible. */
567 actual_rows = min_ii(dyn_data->height, actual_maxrows);
568 }
569
570 /* If list length changes or list is tagged to check this,
571 * and active is out of view, scroll to it. */
572 if ((ui_list->list_last_len != items->item_vec.size()) ||
574 {
575 if (activei_row < ui_list->list_scroll) {
576 ui_list->list_scroll = activei_row;
577 }
578 else if (activei_row >= ui_list->list_scroll + actual_rows) {
579 ui_list->list_scroll = activei_row - actual_rows + 1;
580 }
582 }
583
584 const int max_scroll = max_ii(0, dyn_data->height - actual_rows);
585 CLAMP(ui_list->list_scroll, 0, max_scroll);
586 ui_list->list_last_len = items->item_vec.size();
587 dyn_data->visual_height = actual_rows;
588 r_visual_info->visual_items = actual_rows * columns;
589 r_visual_info->start_idx = ui_list->list_scroll * columns;
590 r_visual_info->end_idx = min_ii(r_visual_info->start_idx + actual_rows * columns,
591 items->item_vec.size());
592}
593
594static void uilist_resize_update(bContext *C, uiList *ui_list)
595{
596 uiListDyn *dyn_data = ui_list->dyn_data;
597
598 /* This way we get diff in number of additional items to show (positive) or hide (negative). */
599 const int diff = round_fl_to_int(float(dyn_data->resize - dyn_data->resize_prev) /
600 float(UI_UNIT_Y));
601
602 if (diff != 0) {
603 ui_list->list_grip += diff;
604 dyn_data->resize_prev += diff * UI_UNIT_Y;
606 }
607
608 /* In case uilist is in popup, we need special refreshing */
610}
611
612static void *uilist_item_use_dynamic_tooltip(PointerRNA *itemptr, const char *propname)
613{
614 if (propname && propname[0] && itemptr && itemptr->data) {
615 PropertyRNA *prop = RNA_struct_find_property(itemptr, propname);
616
617 if (prop && (RNA_property_type(prop) == PROP_STRING)) {
618 return RNA_property_string_get_alloc(itemptr, prop, nullptr, 0, nullptr);
619 }
620 }
621 return nullptr;
622}
623
624static std::string uilist_item_tooltip_func(bContext * /*C*/, void *argN, const StringRef tip)
625{
626 char *dyn_tooltip = static_cast<char *>(argN);
627 std::string tooltip_string = dyn_tooltip;
628 if (!tip.is_empty()) {
629 tooltip_string += '\n';
630 tooltip_string += tip;
631 }
632 return tooltip_string;
633}
634
639 uiListType *ui_list_type,
640 const char *list_id,
641 int layout_type,
642 bool sort_reverse,
643 bool sort_lock)
644{
645 /* Allows to work in popups. */
646 ARegion *region = CTX_wm_region_popup(C);
647 if (region == nullptr) {
648 region = CTX_wm_region(C);
649 }
650
651 /* Find or add the uiList to the current Region. */
652
653 char full_list_id[UI_MAX_NAME_STR];
654 WM_uilisttype_to_full_list_id(ui_list_type, list_id, full_list_id);
655
656 uiList *ui_list = static_cast<uiList *>(
657 BLI_findstring(&region->ui_lists, full_list_id, offsetof(uiList, list_id)));
658
659 if (!ui_list) {
660 ui_list = MEM_callocN<uiList>("uiList");
661 STRNCPY_UTF8(ui_list->list_id, full_list_id);
662 BLI_addtail(&region->ui_lists, ui_list);
663 ui_list->list_grip = -UI_LIST_AUTO_SIZE_THRESHOLD; /* Force auto size by default. */
664 if (sort_reverse) {
666 }
667 if (sort_lock) {
669 }
670 }
671
672 if (!ui_list->dyn_data) {
673 ui_list->dyn_data = MEM_callocN<uiListDyn>("uiList.dyn_data");
674 }
675 uiListDyn *dyn_data = ui_list->dyn_data;
676 /* Note that this isn't a `uiListType` callback, it's stored in the runtime list data. Otherwise
677 * the runtime data could leak when the type is unregistered (e.g. on "Reload Scripts"). */
679
680 /* Because we can't actually pass type across save&load... */
681 ui_list->type = ui_list_type;
682 ui_list->layout_type = layout_type;
683
684 /* Reset filtering data. */
687 dyn_data->items_len = dyn_data->items_shown = -1;
688
689 return ui_list;
690}
691
693 uiList *ui_list,
694 uiLayout *layout,
695 TemplateListInputData *input_data,
696 TemplateListItems *items,
697 const TemplateListLayoutDrawData *layout_data,
698 const enum uiTemplateListFlags flags)
699{
700 uiListDyn *dyn_data = ui_list->dyn_data;
701 const char *active_propname = RNA_property_identifier(input_data->activeprop);
702
703 uiLayout *glob = nullptr, *box, *row, *col, *sub, *overlap;
704 char numstr[32];
705 int rnaicon = ICON_NONE, icon = ICON_NONE;
706 uiBut *but;
707
708 uiBlock *block = layout->block();
709
710 /* get icon */
711 if (input_data->dataptr.data && input_data->prop) {
712 StructRNA *ptype = RNA_property_pointer_type(&input_data->dataptr, input_data->prop);
713 rnaicon = RNA_struct_ui_icon(ptype);
714 }
715
716 TemplateListVisualInfo visual_info;
717 switch (ui_list->layout_type) {
719 /* layout */
720 box = &layout->list_box(ui_list, &input_data->active_dataptr, input_data->activeprop);
721 glob = &box->column(true);
722 row = &glob->row(false);
723 col = &row->column(true);
724
725 TemplateListLayoutDrawData adjusted_layout_data = *layout_data;
726 adjusted_layout_data.columns = 1;
727 /* init numbers */
728 uilist_prepare(ui_list, items, &adjusted_layout_data, &visual_info);
729
730 int i = 0;
731 if (input_data->dataptr.data && input_data->prop) {
732
733 const bool editable = (RNA_property_flag(input_data->prop) & PROP_EDITABLE);
734
735 /* create list items */
736 for (i = visual_info.start_idx; i < visual_info.end_idx; i++) {
737 PointerRNA *itemptr = &items->item_vec[i].item;
738 void *dyntip_data;
739 const int org_i = items->item_vec[i].org_idx;
740 const int flt_flag = items->item_vec[i].flt_flag;
741 uiBlock *subblock = col->block();
742
743 overlap = &col->overlap();
744
746
747 /* list item behind label & other buttons */
748 overlap->row(false);
749
750 but = uiDefButR_prop(subblock,
752 0,
753 "",
754 0,
755 0,
756 UI_UNIT_X * 10,
757 UI_UNIT_Y,
758 &input_data->active_dataptr,
759 input_data->activeprop,
760 0,
761 0,
762 org_i,
763 editable ? TIP_("Select List Item "
764 "(Double click to rename)") :
765 TIP_("Select List Item"));
766
767 if ((dyntip_data = uilist_item_use_dynamic_tooltip(itemptr,
768 input_data->item_dyntip_propname)))
769 {
771 }
772
773 uiLayout *item_row = &overlap->row(true);
774
776
777 sub = &item_row->row(false);
778 icon = UI_icon_from_rnaptr(C, itemptr, rnaicon, false);
779 if (icon == ICON_DOT) {
780 icon = ICON_NONE;
781 }
782 layout_data->draw_item(ui_list,
783 C,
784 sub,
785 &input_data->dataptr,
786 itemptr,
787 icon,
788 &input_data->active_dataptr,
789 active_propname,
790 org_i,
791 flt_flag);
792
793 /* Items should be able to set context pointers for the layout. But the list-row button
794 * swallows events, so it needs the context storage too for handlers to see it. */
795 but->context = sub->context_store();
796
797 /* If we are "drawing" active item, set all labels as active. */
798 if (i == items->active_item_idx) {
800 }
801
804 }
805 }
806
807 /* add dummy buttons to fill space */
808 for (; i < visual_info.start_idx + visual_info.visual_items; i++) {
809 col->label("", ICON_NONE);
810 }
811
812 /* Add scroll-bar. */
813 if (items->item_vec.size() > visual_info.visual_items) {
814 row->column(false);
815 but = uiDefButI(block,
817 0,
818 "",
819 0,
820 0,
822 UI_UNIT_Y * dyn_data->visual_height,
823 &ui_list->list_scroll,
824 0,
825 dyn_data->height - dyn_data->visual_height,
826 "");
827 uiButScrollBar *but_scroll = reinterpret_cast<uiButScrollBar *>(but);
828 but_scroll->visual_height = dyn_data->visual_height;
829 }
830 break;
831 }
833 row = &layout->row(true);
834
835 if ((input_data->dataptr.data && input_data->prop) && (dyn_data->items_shown > 0) &&
836 (items->active_item_idx >= 0) && (items->active_item_idx < dyn_data->items_shown))
837 {
838 PointerRNA *itemptr = &items->item_vec[items->active_item_idx].item;
839 const int org_i = items->item_vec[items->active_item_idx].org_idx;
840
841 icon = UI_icon_from_rnaptr(C, itemptr, rnaicon, false);
842 if (icon == ICON_DOT) {
843 icon = ICON_NONE;
844 }
845 layout_data->draw_item(ui_list,
846 C,
847 row,
848 &input_data->dataptr,
849 itemptr,
850 icon,
851 &input_data->active_dataptr,
852 active_propname,
853 org_i,
854 0);
855 }
856 /* if list is empty, add in dummy button */
857 else {
858 row->label("", ICON_NONE);
859 }
860
861 /* next/prev button */
862 SNPRINTF_UTF8(numstr, "%d :", dyn_data->items_shown);
863 but = uiDefIconTextButR_prop(block,
865 0,
866 ICON_NONE,
867 numstr,
868 0,
869 0,
870 UI_UNIT_X * 5,
871 UI_UNIT_Y,
872 &input_data->active_dataptr,
873 input_data->activeprop,
874 0,
875 0,
876 0,
877 "");
878 if (dyn_data->items_shown == 0) {
880 }
881 break;
883 box = &layout->list_box(ui_list, &input_data->active_dataptr, input_data->activeprop);
884 /* For grip button. */
885 glob = &box->column(true);
886 /* For scroll-bar. */
887 row = &glob->row(false);
888
889 const bool show_names = (flags & UI_TEMPLATE_LIST_NO_NAMES) == 0;
890
891 const int size_x = UI_preview_tile_size_x();
892 const int size_y = show_names ? UI_preview_tile_size_y() : UI_preview_tile_size_y_no_label();
893
894 const int cols_per_row = std::max(int((box->width() - V2D_SCROLL_WIDTH) / size_x), 1);
895 uiLayout *grid = &row->grid_flow(true, cols_per_row, true, true, true);
896
897 TemplateListLayoutDrawData adjusted_layout_data = *layout_data;
898 adjusted_layout_data.columns = cols_per_row;
899 uilist_prepare(ui_list, items, &adjusted_layout_data, &visual_info);
900
901 if (input_data->dataptr.data && input_data->prop) {
902 /* create list items */
903 for (int i = visual_info.start_idx; i < visual_info.end_idx; i++) {
904 PointerRNA *itemptr = &items->item_vec[i].item;
905 const int org_i = items->item_vec[i].org_idx;
906 const int flt_flag = items->item_vec[i].flt_flag;
907
908 overlap = &grid->overlap();
909 col = &overlap->column(false);
910
911 uiBlock *subblock = col->block();
913
914 but = uiDefButR_prop(subblock,
916 0,
917 "",
918 0,
919 0,
920 size_x,
921 size_y,
922 &input_data->active_dataptr,
923 input_data->activeprop,
924 0,
925 0,
926 org_i,
927 std::nullopt);
929
930 col = &overlap->column(false);
931
932 icon = UI_icon_from_rnaptr(C, itemptr, rnaicon, false);
933 layout_data->draw_item(ui_list,
934 C,
935 col,
936 &input_data->dataptr,
937 itemptr,
938 icon,
939 &input_data->active_dataptr,
940 active_propname,
941 org_i,
942 flt_flag);
943
944 /* Items should be able to set context pointers for the layout. But the list-row button
945 * swallows events, so it needs the context storage too for handlers to see it. */
946 but->context = col->context_store();
947
948 /* If we are "drawing" active item, set all labels as active. */
949 if (i == items->active_item_idx) {
951 }
952
954 }
955 }
956
957 if (items->item_vec.size() > visual_info.visual_items) {
958 /* col = */ row->column(false);
959 but = uiDefButI(block,
961 0,
962 "",
963 0,
964 0,
966 size_y * dyn_data->visual_height,
967 &ui_list->list_scroll,
968 0,
969 dyn_data->height - dyn_data->visual_height,
970 "");
971 uiButScrollBar *but_scroll = reinterpret_cast<uiButScrollBar *>(but);
972 but_scroll->visual_height = dyn_data->visual_height;
973 }
974 break;
975 }
976
977 const bool add_filters_but = (flags & UI_TEMPLATE_LIST_NO_FILTER_OPTIONS) == 0;
978 if (glob && add_filters_but) {
979 const bool add_grip_but = (flags & UI_TEMPLATE_LIST_NO_GRIP) == 0;
980
981 /* About #ButType::Grip drag-resize:
982 * We can't directly use results from a grip button, since we have a
983 * rather complex behavior here (sizing by discrete steps and, overall, auto-size feature).
984 * Since we *never* know whether we are grip-resizing or not
985 * (because there is no callback for when a button enters/leaves its "edit mode"),
986 * we use the fact that grip-controlled value (dyn_data->resize) is completely handled
987 * by the grip during the grab resize, so settings its value here has no effect at all.
988 *
989 * It is only meaningful when we are not resizing,
990 * in which case this gives us the correct "init drag" value.
991 * Note we cannot affect `dyn_data->resize_prev here`,
992 * since this value is not controlled by the grip!
993 */
994 dyn_data->resize = dyn_data->resize_prev +
995 (dyn_data->visual_height - ui_list->list_grip) * UI_UNIT_Y;
996
997 row = &glob->row(true);
998 uiBlock *subblock = row->block();
1000
1001 if (ui_list->filter_flag & UILST_FLT_SHOW) {
1002 but = uiDefIconButBitI(subblock,
1005 0,
1006 ICON_DISCLOSURE_TRI_DOWN,
1007 0,
1008 0,
1009 UI_UNIT_X,
1010 UI_UNIT_Y * 0.5f,
1011 &(ui_list->filter_flag),
1012 0,
1013 0,
1014 TIP_("Hide filtering options"));
1015 UI_but_flag_disable(but, UI_BUT_UNDO); /* skip undo on screen buttons */
1016
1017 if (add_grip_but) {
1018 but = uiDefIconButI(subblock,
1020 0,
1021 ICON_GRIP,
1022 0,
1023 0,
1024 UI_UNIT_X * 10.0f,
1025 UI_UNIT_Y * 0.5f,
1026 &dyn_data->resize,
1027 0.0,
1028 0.0,
1029 "");
1030 UI_but_func_set(but, [ui_list](bContext &C) { uilist_resize_update(&C, ui_list); });
1031 }
1032
1034
1035 col = &glob->column(false);
1036 subblock = col->block();
1037 uiDefBut(subblock,
1039 0,
1040 "",
1041 0,
1042 0,
1043 UI_UNIT_X,
1044 UI_UNIT_Y * 0.05f,
1045 nullptr,
1046 0.0,
1047 0.0,
1048 "");
1049
1050 layout_data->draw_filter(ui_list, C, col);
1051 }
1052 else {
1053 but = uiDefIconButBitI(subblock,
1056 0,
1057 ICON_DISCLOSURE_TRI_RIGHT,
1058 0,
1059 0,
1060 UI_UNIT_X,
1061 UI_UNIT_Y * 0.5f,
1062 &(ui_list->filter_flag),
1063 0,
1064 0,
1065 TIP_("Show filtering options"));
1066 UI_but_flag_disable(but, UI_BUT_UNDO); /* skip undo on screen buttons */
1067
1068 if (add_grip_but) {
1069 but = uiDefIconButI(subblock,
1071 0,
1072 ICON_GRIP,
1073 0,
1074 0,
1075 UI_UNIT_X * 10.0f,
1076 UI_UNIT_Y * 0.5f,
1077 &dyn_data->resize,
1078 0.0,
1079 0.0,
1080 "");
1081 UI_but_func_set(but, [ui_list](bContext &C) { uilist_resize_update(&C, ui_list); });
1082 }
1083
1085 }
1086 }
1087}
1088
1090 const bContext *C,
1091 const char *listtype_name,
1092 const char *list_id,
1093 PointerRNA *dataptr,
1094 const StringRefNull propname,
1095 PointerRNA *active_dataptr,
1096 const StringRefNull active_propname,
1097 const char *item_dyntip_propname,
1098 int rows,
1099 int maxrows,
1100 int layout_type,
1101 int columns,
1102 enum uiTemplateListFlags flags,
1103 void *customdata)
1104{
1105 TemplateListInputData input_data = {};
1106 uiListType *ui_list_type;
1107 if (!ui_template_list_data_retrieve(listtype_name,
1108 list_id,
1109 dataptr,
1110 propname,
1111 active_dataptr,
1112 active_propname,
1113 item_dyntip_propname,
1114 &input_data,
1115 &ui_list_type))
1116 {
1117 return nullptr;
1118 }
1119
1120 uiListDrawItemFunc draw_item = ui_list_type->draw_item ? ui_list_type->draw_item :
1122 uiListDrawFilterFunc draw_filter = ui_list_type->draw_filter ? ui_list_type->draw_filter :
1124 uiListFilterItemsFunc filter_items = ui_list_type->filter_items ? ui_list_type->filter_items :
1126
1127 uiList *ui_list = ui_list_ensure(C,
1128 ui_list_type,
1129 list_id,
1130 layout_type,
1133 uiListDyn *dyn_data = ui_list->dyn_data;
1134
1135 MEM_SAFE_FREE(dyn_data->customdata);
1136 dyn_data->customdata = customdata;
1137
1138 /* When active item changed since last draw, scroll to it. */
1139 if (input_data.active_item_idx != ui_list->list_last_activei) {
1141 ui_list->list_last_activei = input_data.active_item_idx;
1142 }
1143
1144 TemplateListItems items;
1145 ui_template_list_collect_display_items(C, ui_list, &input_data, filter_items, &items);
1146
1147 TemplateListLayoutDrawData layout_data;
1148 layout_data.draw_item = draw_item;
1149 layout_data.draw_filter = draw_filter;
1150 layout_data.rows = rows;
1151 layout_data.maxrows = maxrows;
1152 layout_data.columns = columns;
1153
1154 ui_template_list_layout_draw(C, ui_list, layout, &input_data, &items, &layout_data, flags);
1155
1156 return ui_list;
1157}
1158
1160 const bContext *C,
1161 const char *listtype_name,
1162 const char *list_id,
1163 PointerRNA *dataptr,
1164 blender::StringRefNull propname,
1165 PointerRNA *active_dataptr,
1166 const char *active_propname,
1167 const char *item_dyntip_propname,
1168 int rows,
1169 int maxrows,
1170 int layout_type,
1171 int columns,
1172 enum uiTemplateListFlags flags)
1173{
1174 uiTemplateList_ex(layout,
1175 C,
1176 listtype_name,
1177 list_id,
1178 dataptr,
1179 propname,
1180 active_dataptr,
1181 active_propname,
1182 item_dyntip_propname,
1183 rows,
1184 maxrows,
1185 layout_type,
1186 columns,
1187 flags,
1188 nullptr);
1189}
1190
1191/* -------------------------------------------------------------------- */
1192
1195
1200
ARegion * CTX_wm_region_popup(const bContext *C)
ARegion * CTX_wm_region(const bContext *C)
void(*)(uiList *ui_list, const bContext *C, PointerRNA *, const char *propname) uiListFilterItemsFunc
void(*)(uiList *ui_list, const bContext *C, uiLayout *layout) uiListDrawFilterFunc
void(*)(uiList *ui_list, const bContext *C, uiLayout *layout, PointerRNA *dataptr, PointerRNA *itemptr, int icon, PointerRNA *active_dataptr, const char *active_propname, int index, int flt_flag) uiListDrawItemFunc
void * BLI_findstring(const ListBase *listbase, const char *id, int offset) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
Definition listbase.cc:608
void BLI_addtail(ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:111
MINLINE int round_fl_to_int(float a)
MINLINE int min_ii(int a, int b)
MINLINE int max_ii(int a, int b)
char * BLI_strdup(const char *str) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1) ATTR_MALLOC
Definition string.cc:41
int char char int BLI_strcasecmp(const char *s1, const char *s2) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1
char * STRNCPY(char(&dst)[N], const char *src)
Definition BLI_string.h:693
char char * BLI_strncpy_ensure_pad(char *__restrict dst, const char *__restrict src, char pad, size_t dst_maxncpy) ATTR_NONNULL(1
#define SNPRINTF_UTF8(dst, format,...)
#define STRNCPY_UTF8(dst, src)
#define CLAMP(a, b, c)
#define TIP_(msgid)
#define MAX_IDPROP_NAME
Definition DNA_ID.h:186
#define UI_LIST_AUTO_SIZE_THRESHOLD
@ UILST_FLT_EXCLUDE
@ UILST_FLT_SHOW
@ UILST_FLT_ITEM
@ UILST_FLT_ITEM_NEVER_SHOW
@ UILST_LAYOUT_COMPACT
@ UILST_LAYOUT_DEFAULT
@ UILST_LAYOUT_BIG_PREVIEW_GRID
@ UILST_SCROLL_TO_ACTIVE_ITEM
@ UILST_FLT_SORT_LOCK
@ UILST_FLT_SORT_ALPHA
@ UILST_FLT_SORT_REVERSE
void ED_region_tag_refresh_ui(ARegion *region)
Definition area.cc:647
Read Guarded memory(de)allocation.
#define MEM_SAFE_FREE(v)
#define RNA_PROP_END
#define RNA_PROP_BEGIN(sptr, itemptr, prop)
#define RNA_warning(format,...)
PropertyType
Definition RNA_types.hh:161
@ PROP_INT
Definition RNA_types.hh:163
@ PROP_STRING
Definition RNA_types.hh:165
@ PROP_COLLECTION
Definition RNA_types.hh:168
@ PROP_EDITABLE
Definition RNA_types.hh:306
#define C
Definition RandGen.cpp:29
void UI_but_func_set(uiBut *but, std::function< void(bContext &)> func)
blender::FunctionRef< std::string(const PointerRNA &itemptr, int index)> uiListItemGetNameFn
eUIListFilterResult
@ UI_LIST_ITEM_FILTER_MISMATCHES
@ UI_LIST_ITEM_NEVER_SHOW
@ UI_LIST_ITEM_FILTER_MATCHES
blender::FunctionRef< eUIListFilterResult( const PointerRNA &itemptr, blender::StringRefNull name, int index)> uiListItemFilterFn
uiBut * uiDefButI(uiBlock *block, ButType type, int retval, blender::StringRef str, int x, int y, short width, short height, int *poin, float min, float max, std::optional< blender::StringRef > tip)
void UI_but_flag_disable(uiBut *but, int flag)
#define UI_UNIT_Y
void UI_block_emboss_set(uiBlock *block, blender::ui::EmbossType emboss)
uiBut * uiDefIconButBitI(uiBlock *block, ButType type, int bit, int retval, int icon, int x, int y, short width, short height, int *poin, float min, float max, std::optional< blender::StringRef > tip)
@ UI_BLOCK_LIST_ITEM
@ UI_BUT_UNDO
@ UI_BUT_DISABLED
void UI_but_func_tooltip_set(uiBut *but, uiButToolTipFunc func, void *arg, uiFreeArgFunc free_arg)
int UI_preview_tile_size_y(const int size_px=96)
void UI_block_flag_disable(uiBlock *block, int flag)
void UI_but_drawflag_enable(uiBut *but, int flag)
uiBut * uiDefButR_prop(uiBlock *block, ButType type, int retval, std::optional< blender::StringRef > str, int x, int y, short width, short height, PointerRNA *ptr, PropertyRNA *prop, int index, float min, float max, std::optional< blender::StringRef > tip)
uiBut * uiDefIconTextButR_prop(uiBlock *block, ButType type, int retval, int icon, std::optional< blender::StringRef > str, int x, int y, short width, short height, PointerRNA *ptr, PropertyRNA *prop, int index, float min, float max, std::optional< blender::StringRef > tip)
#define UI_UL_DEFAULT_CLASS_NAME
uiBut * uiDefIconButI(uiBlock *block, ButType type, int retval, int icon, int x, int y, short width, short height, int *poin, float min, float max, std::optional< blender::StringRef > tip)
int UI_preview_tile_size_x(const int size_px=96)
#define UI_UNIT_X
void UI_block_flag_enable(uiBlock *block, int flag)
@ UI_BUT_NO_TOOLTIP
int UI_preview_tile_size_y_no_label(const int size_px=96)
uiBut * uiDefBut(uiBlock *block, uiButTypeWithPointerType but_and_ptr_type, int retval, blender::StringRef str, int x, int y, short width, short height, void *poin, float min, float max, std::optional< blender::StringRef > tip)
void UI_but_flag_enable(uiBut *but, int flag)
uiTemplateListFlags
@ UI_TEMPLATE_LIST_SORT_LOCK
@ UI_TEMPLATE_LIST_SORT_REVERSE
@ UI_TEMPLATE_LIST_NO_NAMES
@ UI_TEMPLATE_LIST_NO_FILTER_OPTIONS
@ UI_TEMPLATE_LIST_NO_GRIP
int UI_icon_from_rnaptr(const bContext *C, PointerRNA *ptr, int rnaicon, bool big)
@ UI_ITEM_R_TOGGLE
@ UI_ITEM_R_NO_BG
@ UI_ITEM_R_ICON_ONLY
#define UI_ITEM_NONE
void uiLayoutListItemAddPadding(uiLayout *layout)
#define UI_MAX_NAME_STR
#define V2D_SCROLL_WIDTH
Definition UI_view2d.hh:54
int64_t size() const
void resize(const int64_t new_size)
constexpr bool is_empty() const
constexpr const char * c_str() const
eUIListFilterResult operator()(const PointerRNA &itemptr, blender::StringRefNull name, int index)
#define offsetof(t, d)
IMETHOD Vector diff(const Vector &a, const Vector &b, double dt)
Definition frames.inl:1166
uint col
#define floor
#define ceil
#define RNA_NO_INDEX
void ui_layout_list_set_labels_active(uiLayout *layout)
uiListType * UI_UL_cache_file_layers()
static int cmpstringp(const void *p1, const void *p2)
static std::string uilist_item_tooltip_func(bContext *, void *argN, const StringRef tip)
static uiList * ui_list_ensure(const bContext *C, uiListType *ui_list_type, const char *list_id, int layout_type, bool sort_reverse, bool sort_lock)
static void uilist_resize_update(bContext *C, uiList *ui_list)
static void uilist_free_dyn_data(uiList *ui_list)
static void * uilist_item_use_dynamic_tooltip(PointerRNA *itemptr, const char *propname)
static bool ui_template_list_data_retrieve(const StringRef listtype_name, const char *list_id, PointerRNA *dataptr, const StringRefNull propname, PointerRNA *active_dataptr, const StringRefNull active_propname, const char *item_dyntip_propname, TemplateListInputData *r_input_data, uiListType **r_list_type)
void UI_list_filter_and_sort_items(uiList *ui_list, const bContext *, uiListItemFilterFn item_filter_fn, PointerRNA *dataptr, const char *propname, uiListItemGetNameFn get_name_fn)
static void ui_template_list_collect_display_items(const bContext *C, uiList *ui_list, TemplateListInputData *input_data, const uiListFilterItemsFunc filter_items_fn, TemplateListItems *r_items)
static void uilist_draw_filter_default(uiList *ui_list, const bContext *, uiLayout *layout)
static void uilist_filter_items_default(uiList *ui_list, const bContext *C, PointerRNA *dataptr, const char *propname)
void uiTemplateList(uiLayout *layout, const bContext *C, const char *listtype_name, const char *list_id, PointerRNA *dataptr, blender::StringRefNull propname, PointerRNA *active_dataptr, const char *active_propname, const char *item_dyntip_propname, int rows, int maxrows, int layout_type, int columns, enum uiTemplateListFlags flags)
uiList * uiTemplateList_ex(uiLayout *layout, const bContext *C, const char *listtype_name, const char *list_id, PointerRNA *dataptr, const StringRefNull propname, PointerRNA *active_dataptr, const StringRefNull active_propname, const char *item_dyntip_propname, int rows, int maxrows, int layout_type, int columns, enum uiTemplateListFlags flags, void *customdata)
static void ui_template_list_collect_items(PointerRNA *list_ptr, PropertyRNA *list_prop, const uiList *ui_list, int activei, TemplateListItems *r_items)
static void ui_template_list_layout_draw(const bContext *C, uiList *ui_list, uiLayout *layout, TemplateListInputData *input_data, TemplateListItems *items, const TemplateListLayoutDrawData *layout_data, const enum uiTemplateListFlags flags)
void ED_uilisttypes_ui()
static void uilist_prepare(uiList *ui_list, const TemplateListItems *items, const TemplateListLayoutDrawData *layout_data, TemplateListVisualInfo *r_visual_info)
bool UI_list_item_index_is_filtered_visible(const uiList *ui_list, const int item_idx)
static void uilist_draw_item_default(uiList *ui_list, const bContext *, uiLayout *layout, PointerRNA *, PointerRNA *itemptr, int icon, PointerRNA *, const char *, int, int)
void * MEM_calloc_arrayN(size_t len, size_t size, const char *str)
Definition mallocn.cc:123
void * MEM_callocN(size_t len, const char *str)
Definition mallocn.cc:118
void * MEM_malloc_arrayN(size_t len, size_t size, const char *str)
Definition mallocn.cc:133
void MEM_freeN(void *vmemh)
Definition mallocn.cc:113
const char * name
void RNA_property_int_set(PointerRNA *ptr, PropertyRNA *prop, int value)
StructRNA * RNA_property_pointer_type(PointerRNA *ptr, PropertyRNA *prop)
PropertyRNA * RNA_struct_find_property(PointerRNA *ptr, const char *identifier)
char * RNA_struct_name_get_alloc(PointerRNA *ptr, char *fixedbuf, int fixedlen, int *r_len)
PropertyType RNA_property_type(PropertyRNA *prop)
char * RNA_property_string_get_alloc(PointerRNA *ptr, PropertyRNA *prop, char *fixedbuf, int fixedlen, int *r_len)
const char * RNA_struct_identifier(const StructRNA *type)
int RNA_property_int_get(PointerRNA *ptr, PropertyRNA *prop)
int RNA_property_flag(PropertyRNA *prop)
int RNA_struct_ui_icon(const StructRNA *type)
PropertyRNA * RNA_struct_name_property(const StructRNA *type)
PointerRNA RNA_pointer_create_discrete(ID *id, StructRNA *type, void *data)
const char * RNA_property_identifier(const PropertyRNA *prop)
int RNA_property_collection_length(PointerRNA *ptr, PropertyRNA *prop)
ListBase ui_lists
StructRNA * type
Definition RNA_types.hh:52
void * data
Definition RNA_types.hh:53
char name[MAX_IDPROP_NAME]
blender::Vector< _uilist_item > item_vec
const bContextStore * context
uiBlock * block() const
void label(blender::StringRef name, int icon)
uiLayout & column(bool align)
uiLayout & grid_flow(bool row_major, int columns_len, bool even_columns, bool even_rows, bool align)
uiLayout & row(bool align)
uiLayout & list_box(uiList *ui_list, PointerRNA *actptr, PropertyRNA *actprop)
void prop(PointerRNA *ptr, PropertyRNA *prop, int index, int value, eUI_Item_Flag flag, std::optional< blender::StringRef > name_opt, int icon, std::optional< blender::StringRef > placeholder=std::nullopt)
uiLayout & overlap()
uiListFreeRuntimeDataFunc free_runtime_data_fn
int * items_filter_neworder
int * items_filter_flags
uiListFilterItemsFunc filter_items
uiListDrawFilterFunc draw_filter
uiListDrawItemFunc draw_item
int filter_sort_flag
uiListDyn * dyn_data
int list_last_activei
struct uiListType * type
char list_id[256]
char filter_byname[256]
i
Definition text_draw.cc:230
uint len
bool WM_uilisttype_add(uiListType *ult)
uiListType * WM_uilisttype_find(const StringRef idname, bool quiet)
void WM_uilisttype_to_full_list_id(const uiListType *ult, const char *list_id, char r_full_list_id[])