diff options
-rw-r--r-- | release/scripts/startup/bl_ui/__init__.py | 57 | ||||
-rw-r--r-- | source/blender/blenkernel/BKE_screen.h | 12 | ||||
-rw-r--r-- | source/blender/blenkernel/intern/screen.c | 14 | ||||
-rw-r--r-- | source/blender/blenloader/intern/readfile.c | 3 | ||||
-rw-r--r-- | source/blender/blenloader/intern/writefile.c | 11 | ||||
-rw-r--r-- | source/blender/editors/interface/interface_handlers.c | 50 | ||||
-rw-r--r-- | source/blender/editors/interface/interface_templates.c | 373 | ||||
-rw-r--r-- | source/blender/makesdna/DNA_screen_types.h | 31 | ||||
-rw-r--r-- | source/blender/makesrna/intern/rna_ui.c | 256 |
9 files changed, 716 insertions, 91 deletions
diff --git a/release/scripts/startup/bl_ui/__init__.py b/release/scripts/startup/bl_ui/__init__.py index df6247f65f5..ba256bdb6c8 100644 --- a/release/scripts/startup/bl_ui/__init__.py +++ b/release/scripts/startup/bl_ui/__init__.py @@ -142,6 +142,61 @@ def unregister(): # Define a default UIList, when a list does not need any custom drawing... # Keep in sync with its #defined name in UI_interface.h class UI_UL_list(bpy.types.UIList): - pass + # These are common filtering or ordering operations (same as the default C ones!). + @staticmethod + def filter_items_by_name(pattern, bitflag, items, propname="name", flags=None, reverse=False): + """ + Set FILTER_ITEM for items which name matches filter_name one (case-insensitive). + pattern is the filtering pattern. + propname is the name of the string property to use for filtering. + flags must be a list of integers the same length as items, or None! + return a list of flags (based on given flags if not None), + or an empty list if no flags were given and no filtering has been done. + """ + import fnmatch + + if not pattern: # Empty pattern = no filtering! + return flags or [] + + if flags is None: + flags = [0] * len(items) + for idx, it in enumerate(items): + name = getattr(it, propname, None) + # Implicitly add heading/trailing wildcards if needed. + if pattern[0] != "*": + pattern = "*" + pattern + if pattern[-1] != "*": + pattern = pattern + "*" + # This is similar to a logical xor + if bool(name and fnmatch.fnmatch(name.lower(), pattern.lower())) is not bool(reverse): + flags[idx] |= bitflag + return flags + + @staticmethod + def sort_items_helper(sort_data, key, reverse=False): + """ + Common sorting utility. Returns a neworder list mapping org_idx -> new_idx. + sort_data must be an (unordered) list of tuples [(org_idx, ...), (org_idx, ...), ...]. + key must be the same kind of callable you would use for sorted() builtin function. + reverse will reverse the sorting! + """ + sort_data.sort(key=key, reverse=reverse) + neworder = [None] * len(sort_data) + for newidx, (orgidx, *_) in enumerate(sort_data): + neworder[orgidx] = newidx + return neworder + + @classmethod + def sort_items_by_name(cls, items, propname="name"): + """ + Re-order items using their names (case-insensitive). + propname is the name of the string property to use for sorting. + return a list mapping org_idx -> new_idx, + or an empty list if no sorting has been done. + """ + neworder = [None] * len(items) + _sort = [(idx, getattr(it, propname, "")) for idx, it in enumerate(items)] + return cls.sort_items_helper(_sort, lambda e: e[1].lower()) + bpy.utils.register_class(UI_UL_list) diff --git a/source/blender/blenkernel/BKE_screen.h b/source/blender/blenkernel/BKE_screen.h index c883bdf74e0..3c0928d38a0 100644 --- a/source/blender/blenkernel/BKE_screen.h +++ b/source/blender/blenkernel/BKE_screen.h @@ -189,9 +189,15 @@ typedef struct PanelType { /* uilist types */ -/* draw an item in the uiList */ +/* Draw an item in the uiList */ typedef void (*uiListDrawItemFunc)(struct uiList *, struct bContext *, struct uiLayout *, struct PointerRNA *, - struct PointerRNA *, int, struct PointerRNA *, const char *, int); + struct PointerRNA *, int, struct PointerRNA *, const char *, int, int); + +/* Draw the filtering part of an uiList */ +typedef void (*uiListDrawFilterFunc)(struct uiList *, struct bContext *, struct uiLayout *); + +/* Filter items of an uiList */ +typedef void (*uiListFilterItemsFunc)(struct uiList *, struct bContext *, struct PointerRNA *, const char *); typedef struct uiListType { struct uiListType *next, *prev; @@ -199,6 +205,8 @@ typedef struct uiListType { char idname[BKE_ST_MAXNAME]; /* unique name */ uiListDrawItemFunc draw_item; + uiListDrawFilterFunc draw_filter; + uiListFilterItemsFunc filter_items; /* RNA integration */ ExtensionRNA ext; diff --git a/source/blender/blenkernel/intern/screen.c b/source/blender/blenkernel/intern/screen.c index 924cdff798d..efcbcacf974 100644 --- a/source/blender/blenkernel/intern/screen.c +++ b/source/blender/blenkernel/intern/screen.c @@ -47,6 +47,7 @@ #include "BLI_listbase.h" #include "BLI_utildefines.h" +#include "BKE_idprop.h" #include "BKE_screen.h" /* ************ Spacetype/regiontype handling ************** */ @@ -290,7 +291,18 @@ void BKE_area_region_free(SpaceType *st, ARegion *ar) for (uilst = ar->ui_lists.first; uilst; uilst = uilst->next) { if (uilst->dyn_data) { - MEM_freeN(uilst->dyn_data); + uiListDyn *dyn_data = uilst->dyn_data; + if (dyn_data->items_filter_flags) { + MEM_freeN(dyn_data->items_filter_flags); + } + if (dyn_data->items_filter_neworder) { + MEM_freeN(dyn_data->items_filter_neworder); + } + MEM_freeN(dyn_data); + } + if (uilst->properties) { + IDP_FreeProperty(uilst->properties); + MEM_freeN(uilst->properties); } } BLI_freelistN(&ar->ui_lists); diff --git a/source/blender/blenloader/intern/readfile.c b/source/blender/blenloader/intern/readfile.c index ff44bcc4cfe..76ed06ce57f 100644 --- a/source/blender/blenloader/intern/readfile.c +++ b/source/blender/blenloader/intern/readfile.c @@ -6128,6 +6128,9 @@ static void direct_link_region(FileData *fd, ARegion *ar, int spacetype) for (ui_list = ar->ui_lists.first; ui_list; ui_list = ui_list->next) { ui_list->type = NULL; ui_list->dyn_data = NULL; + ui_list->properties = newdataadr(fd, ui_list->properties); + if (ui_list->properties) + IDP_DirectLinkProperty(ui_list->properties, (fd->flags & FD_FLAGS_SWITCH_ENDIAN), fd); } if (spacetype == SPACE_EMPTY) { diff --git a/source/blender/blenloader/intern/writefile.c b/source/blender/blenloader/intern/writefile.c index b7254ffe1a9..83982781d8c 100644 --- a/source/blender/blenloader/intern/writefile.c +++ b/source/blender/blenloader/intern/writefile.c @@ -2394,6 +2394,15 @@ static void write_region(WriteData *wd, ARegion *ar, int spacetype) } } +static void write_uilist(WriteData *wd, uiList *ui_list) +{ + writestruct(wd, DATA, "uiList", 1, ui_list); + + if (ui_list->properties) { + IDP_WriteProperty(ui_list->properties, wd); + } +} + static void write_soops(WriteData *wd, SpaceOops *so, LinkNode **tmp_mem_list) { BLI_mempool *ts = so->treestore; @@ -2475,7 +2484,7 @@ static void write_screens(WriteData *wd, ListBase *scrbase) writestruct(wd, DATA, "Panel", 1, pa); for (ui_list = ar->ui_lists.first; ui_list; ui_list = ui_list->next) - writestruct(wd, DATA, "uiList", 1, ui_list); + write_uilist(wd, ui_list); } sl= sa->spacedata.first; diff --git a/source/blender/editors/interface/interface_handlers.c b/source/blender/editors/interface/interface_handlers.c index e4e756a8892..90040e221b0 100644 --- a/source/blender/editors/interface/interface_handlers.c +++ b/source/blender/editors/interface/interface_handlers.c @@ -6585,15 +6585,55 @@ static int ui_handle_list_event(bContext *C, const wmEvent *event, ARegion *ar) ((ELEM(type, WHEELUPMOUSE, WHEELDOWNMOUSE) && event->alt))) { const int value_orig = RNA_property_int_get(&but->rnapoin, but->rnaprop); - int value, min, max; + int value, min, max, inc; /* activate up/down the list */ value = value_orig; + if ((ui_list->filter_orderby_flag & UILST_FLT_ORDERBY_REVERSE) != 0) { + inc = ELEM(type, UPARROWKEY, WHEELUPMOUSE) ? 1 : -1; + } + else { + inc = ELEM(type, UPARROWKEY, WHEELUPMOUSE) ? -1 : 1; + } - if (ELEM(type, UPARROWKEY, WHEELUPMOUSE)) - value--; - else - value++; + if (dyn_data->items_filter_neworder || dyn_data->items_filter_flags) { + /* If we have a display order different from collection order, we have some work! */ + int *org_order = MEM_mallocN(dyn_data->items_shown * sizeof(int), AT); + int *new_order = dyn_data->items_filter_neworder; + int i, org_idx = -1, len = dyn_data->items_len; + int current_idx = -1; + int filter_exclude = ui_list->filter_flag & UILST_FLT_EXCLUDE; + + for (i = 0; i < len; i++) { + if (!dyn_data->items_filter_flags || + ((dyn_data->items_filter_flags[i] & UILST_FLT_ITEM) ^ filter_exclude)) + { + org_order[new_order ? new_order[++org_idx] : ++org_idx] = i; + if (i == value) { + current_idx = new_order ? new_order[org_idx] : org_idx; + } + } + else if (i == value && org_idx >= 0) { + current_idx = -(new_order ? new_order[org_idx] : org_idx) - 1; + } + } + /* Now, org_order maps displayed indices to real indices, + * and current_idx either contains the displayed index of active value (positive), + * or its more-nearest one (negated). + */ + if (current_idx < 0) { + current_idx = (current_idx * -1) + (inc < 0 ? inc : inc - 1); + } + else { + current_idx += inc; + } + CLAMP(current_idx, 0, dyn_data->items_shown - 1); + value = org_order[current_idx]; + MEM_freeN(org_order); + } + else { + value += inc; + } CLAMP(value, 0, dyn_data->items_len - 1); diff --git a/source/blender/editors/interface/interface_templates.c b/source/blender/editors/interface/interface_templates.c index 2098d881d4e..7c84a676827 100644 --- a/source/blender/editors/interface/interface_templates.c +++ b/source/blender/editors/interface/interface_templates.c @@ -44,6 +44,7 @@ #include "BLI_rect.h" #include "BLI_math.h" #include "BLI_listbase.h" +#include "BLI_fnmatch.h" #include "BLF_api.h" #include "BLF_translation.h" @@ -2471,7 +2472,7 @@ void uiTemplateGameStates(uiLayout *layout, PointerRNA *ptr, const char *propnam static void 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(index), int UNUSED(flt_flag)) { char *namebuf; const char *name; @@ -2497,6 +2498,153 @@ static void uilist_draw_item_default(struct uiList *ui_list, struct bContext *UN } } +static void uilist_draw_filter_default(struct uiList *ui_list, struct bContext *UNUSED(C), struct uiLayout *layout) +{ + PointerRNA listptr; + uiLayout *row, *subrow; + + RNA_pointer_create(NULL, &RNA_UIList, ui_list, &listptr); + + row = uiLayoutRow(layout, FALSE); + + subrow = uiLayoutRow(row, TRUE); + uiItemR(subrow, &listptr, "filter_name", 0, "", ICON_NONE); + uiItemR(subrow, &listptr, "use_filter_invert", UI_ITEM_R_TOGGLE | UI_ITEM_R_ICON_ONLY, "", + (ui_list->filter_flag & UILST_FLT_EXCLUDE) ? ICON_ZOOM_OUT : ICON_ZOOM_IN); + + subrow = uiLayoutRow(row, TRUE); + uiItemR(subrow, &listptr, "use_filter_orderby_name", UI_ITEM_R_TOGGLE, NULL, ICON_NONE); + uiItemR(subrow, &listptr, "use_filter_orderby_invert", UI_ITEM_R_TOGGLE | UI_ITEM_R_ICON_ONLY, "", + (ui_list->filter_orderby_flag & UILST_FLT_ORDERBY_REVERSE) ? ICON_TRIA_UP : ICON_TRIA_DOWN); +} + +typedef struct { + char name[MAX_IDPROP_NAME]; + int org_idx; +} StringCmp; + +static int cmpstringp(const void *p1, const void *p2) +{ + /* Case-insensitive comparison. */ + return strcasecmp(((StringCmp *) p1)->name, ((StringCmp *) p2)->name); +} + +static void uilist_filter_items_default(struct uiList *ui_list, struct bContext *UNUSED(C), struct PointerRNA *dataptr, + const char *propname) +{ + uiListDyn *dyn_data = ui_list->dyn_data; + PropertyRNA *prop = RNA_struct_find_property(dataptr, propname); + + const char *filter_raw = ui_list->filter_byname; + char *filter = (char *)filter_raw, filter_buff[32], *filter_dyn = NULL; + bool filter_exclude = (ui_list->filter_flag & UILST_FLT_EXCLUDE) != 0; + bool order_by_name = (ui_list->filter_orderby_flag & UILST_FLT_ORDERBY_NAME) != 0; + int len = RNA_property_collection_length(dataptr, prop); + + dyn_data->items_shown = dyn_data->items_len = len; + + if (len && (order_by_name || filter_raw[0])) { + StringCmp *names = NULL; + int order_idx = 0, i = 0; + + if (order_by_name) { + names = MEM_callocN(sizeof(StringCmp) * len, AT); + } + if (filter_raw[0]) { + size_t idx = 0, slen = strlen(filter_raw); + + dyn_data->items_filter_flags = MEM_callocN(sizeof(int) * len, AT); + dyn_data->items_shown = 0; + + /* Implicitly add heading/trailing wildcards if needed. */ + if (len + 3 <= 32) { + filter = filter_buff; + } + else { + filter = filter_dyn = MEM_mallocN((slen + 3) * sizeof(char), AT); + } + if (filter_raw[idx] != '*') { + filter[idx++] = '*'; + } + memcpy(filter + idx, filter_raw, slen); + idx += slen; + if (filter[idx - 1] != '*') { + filter[idx++] = '*'; + } + filter[idx] = '\0'; + } + + RNA_PROP_BEGIN (dataptr, itemptr, prop) + { + char *namebuf; + const char *name; + bool do_order = false; + + namebuf = RNA_struct_name_get_alloc(&itemptr, NULL, 0, NULL); + name = namebuf ? namebuf : ""; + + if (filter[0]) { + /* Case-insensitive! */ + if (fnmatch(filter, name, FNM_CASEFOLD) == 0) { + dyn_data->items_filter_flags[i] = UILST_FLT_ITEM; + if (!filter_exclude) { + dyn_data->items_shown++; + do_order = order_by_name; + } + //printf("%s: '%s' matches '%s'\n", __func__, name, filter); + } + else if (filter_exclude) { + dyn_data->items_shown++; + do_order = order_by_name; + } + } + else { + do_order = order_by_name; + } + + if (do_order) { + names[order_idx].org_idx = order_idx; + BLI_strncpy(names[order_idx++].name, name, MAX_IDPROP_NAME); + } + + /* free name */ + if (namebuf) { + MEM_freeN(namebuf); + } + i++; + } + RNA_PROP_END; + + if (order_by_name) { + int new_idx; + /* note: order_idx equals either to ui_list->items_len if no filtering done, + * or to ui_list->items_shown if filter is enabled, + * or to (ui_list->items_len - ui_list->items_shown) if filtered items are excluded. + * This way, we only sort items we actually intend to draw! + */ + qsort(names, order_idx, sizeof(StringCmp), cmpstringp); + + dyn_data->items_filter_neworder = MEM_mallocN(sizeof(int) * order_idx, AT); + for (new_idx = 0; new_idx < order_idx; new_idx++) { + dyn_data->items_filter_neworder[names[new_idx].org_idx] = new_idx; + } + } + + if (filter_dyn) { + MEM_freeN(filter_dyn); + } + if (names) { + MEM_freeN(names); + } + } +} + +typedef struct { + PointerRNA item; + int org_idx; + int flt_flag; +} _uilist_item; + typedef struct { int visual_items; /* Visual number of items (i.e. number of items we have room to display). */ int start_idx; /* Index of first item to display. */ @@ -2572,9 +2720,12 @@ void uiTemplateList(uiLayout *layout, bContext *C, const char *listtype_name, co uiListDyn *dyn_data; ARegion *ar; uiListDrawItemFunc draw_item; + uiListDrawFilterFunc draw_filter; + uiListFilterItemsFunc filter_items; PropertyRNA *prop = NULL, *activeprop; PropertyType type, activetype; + _uilist_item *items_ptr = NULL; StructRNA *ptype; uiLayout *glob = NULL, *box, *row, *col, *subrow, *sub, *overlap; uiBlock *block, *subblock; @@ -2586,7 +2737,6 @@ void uiTemplateList(uiLayout *layout, bContext *C, const char *listtype_name, co int rnaicon = ICON_NONE, icon = ICON_NONE; int i = 0, activei = 0; int len = 0; - int found; /* validate arguments */ /* Forbid default UI_UL_DEFAULT_CLASS_NAME list class without a custom list_id! */ @@ -2649,6 +2799,8 @@ void uiTemplateList(uiLayout *layout, bContext *C, const char *listtype_name, co } draw_item = ui_list_type->draw_item ? ui_list_type->draw_item : uilist_draw_item_default; + draw_filter = ui_list_type->draw_filter ? ui_list_type->draw_filter : uilist_draw_filter_default; + filter_items = ui_list_type->filter_items ? ui_list_type->filter_items : uilist_filter_items_default; /* Find or add the uiList to the current Region. */ /* We tag the list id with the list type... */ @@ -2672,8 +2824,89 @@ void uiTemplateList(uiLayout *layout, bContext *C, const char *listtype_name, co ui_list->type = ui_list_type; ui_list->layout_type = layout_type; - if (dataptr->data && prop) - dyn_data->items_len = dyn_data->items_shown = len = RNA_property_collection_length(dataptr, prop); + /* Reset filtering data. */ + if (dyn_data->items_filter_flags) { + MEM_freeN(dyn_data->items_filter_flags); + dyn_data->items_filter_flags = NULL; + } + if (dyn_data->items_filter_neworder) { + MEM_freeN(dyn_data->items_filter_neworder); + dyn_data->items_filter_neworder = NULL; + } + dyn_data->items_len = dyn_data->items_shown = -1; + + /* Filter list items! (not for compact layout, though) */ + if (dataptr->data && prop) { + int filter_exclude = ui_list->filter_flag & UILST_FLT_EXCLUDE; + bool order_reverse = (ui_list->filter_orderby_flag & UILST_FLT_ORDERBY_REVERSE) != 0; + int items_shown, idx = 0; +#if 0 + int prev_ii = -1, prev_i; +#endif + + if (layout_type == UILST_LAYOUT_COMPACT) { + dyn_data->items_len = dyn_data->items_shown = RNA_property_collection_length(dataptr, prop); + } + else { + //printf("%s: filtering...\n", __func__); + filter_items(ui_list, C, dataptr, propname); + //printf("%s: filtering done.\n", __func__); + } + + items_shown = dyn_data->items_shown; + if (items_shown >= 0) { + items_ptr = MEM_mallocN(sizeof(_uilist_item) * items_shown, AT); + //printf("%s: items shown: %d.\n", __func__, items_shown); + RNA_PROP_BEGIN (dataptr, itemptr, prop) + { + if (!dyn_data->items_filter_flags || + ((dyn_data->items_filter_flags[i] & UILST_FLT_ITEM) ^ filter_exclude)) + { + int ii; + if (dyn_data->items_filter_neworder) { + ii = dyn_data->items_filter_neworder[idx++]; + ii = order_reverse ? items_shown - ii - 1 : ii; + } + else { + ii = order_reverse ? items_shown - ++idx : idx++; + } + //printf("%s: ii: %d\n", __func__, ii); + items_ptr[ii].item = itemptr; + items_ptr[ii].org_idx = i; + items_ptr[ii].flt_flag = dyn_data->items_filter_flags ? dyn_data->items_filter_flags[i] : 0; + + if (activei == i) { + activei = ii; + } +# if 0 /* For now, do not alter active element, even if it will be hidden... */ + else if (activei < i) { + /* We do not want an active but invisible item! + * Only exception is when all items are filtered out... + */ + if (prev_ii >= 0) { + activei = prev_ii; + RNA_property_int_set(active_dataptr, activeprop, prev_i); + } + else { + activei = ii; + RNA_property_int_set(active_dataptr, activeprop, i); + } + } + prev_i = i; + prev_ii = ii; +#endif + } + i++; + } + RNA_PROP_END; + } + if (dyn_data->items_shown >= 0) { + len = dyn_data->items_shown; + } + else { + len = dyn_data->items_len; + } + } switch (layout_type) { case UILST_LAYOUT_DEFAULT: @@ -2688,38 +2921,39 @@ void uiTemplateList(uiLayout *layout, bContext *C, const char *listtype_name, co if (dataptr->data && prop) { /* create list items */ - RNA_PROP_BEGIN (dataptr, itemptr, prop) - { - if (i >= layoutdata.start_idx && i < layoutdata.end_idx) { - subblock = uiLayoutGetBlock(col); - overlap = uiLayoutOverlap(col); + for (i = layoutdata.start_idx; i < layoutdata.end_idx; i++) { + PointerRNA *itemptr = &items_ptr[i].item; + int org_i = items_ptr[i].org_idx; + int flt_flag = items_ptr[i].flt_flag; + subblock = uiLayoutGetBlock(col); - uiBlockSetFlag(subblock, UI_BLOCK_LIST_ITEM); + overlap = uiLayoutOverlap(col); - /* list item behind label & other buttons */ - sub = uiLayoutRow(overlap, FALSE); + uiBlockSetFlag(subblock, UI_BLOCK_LIST_ITEM); - but = uiDefButR_prop(subblock, LISTROW, 0, "", 0, 0, UI_UNIT_X * 10, UI_UNIT_Y, - active_dataptr, activeprop, 0, 0, i, 0, 0, NULL); - uiButSetFlag(but, UI_BUT_NO_TOOLTIP); + /* list item behind label & other buttons */ + sub = uiLayoutRow(overlap, FALSE); - sub = uiLayoutRow(overlap, FALSE); + but = uiDefButR_prop(subblock, LISTROW, 0, "", 0, 0, UI_UNIT_X * 10, UI_UNIT_Y, + active_dataptr, activeprop, 0, 0, org_i, 0, 0, NULL); + uiButSetFlag(but, UI_BUT_NO_TOOLTIP); - icon = UI_rnaptr_icon_get(C, &itemptr, rnaicon, false); - if (icon == ICON_DOT) - icon = ICON_NONE; - draw_item(ui_list, C, sub, dataptr, &itemptr, icon, active_dataptr, active_propname, i); + sub = uiLayoutRow(overlap, FALSE); + + icon = UI_rnaptr_icon_get(C, itemptr, rnaicon, false); + if (icon == ICON_DOT) + icon = ICON_NONE; + draw_item(ui_list, C, sub, dataptr, itemptr, icon, active_dataptr, active_propname, + org_i, flt_flag); - /* If we are "drawing" active item, set all labels as active. */ - if (i == activei) { - ui_layout_list_set_labels_active(sub); - } - uiBlockClearFlag(subblock, UI_BLOCK_LIST_ITEM); + /* If we are "drawing" active item, set all labels as active. */ + if (i == activei) { + ui_layout_list_set_labels_active(sub); } - i++; + + uiBlockClearFlag(subblock, UI_BLOCK_LIST_ITEM); } - RNA_PROP_END; } /* add dummy buttons to fill space */ @@ -2738,33 +2972,25 @@ void uiTemplateList(uiLayout *layout, bContext *C, const char *listtype_name, co case UILST_LAYOUT_COMPACT: row = uiLayoutRow(layout, TRUE); - if (dataptr->data && prop) { - /* create list items */ - RNA_PROP_BEGIN (dataptr, itemptr, prop) - { - found = (activei == i); + if (dataptr->data && prop && dyn_data->items_shown > 0) { + PointerRNA *itemptr = &items_ptr[activei].item; + int org_i = items_ptr[activei].org_idx; - if (found) { - icon = UI_rnaptr_icon_get(C, &itemptr, rnaicon, false); - if (icon == ICON_DOT) - icon = ICON_NONE; - draw_item(ui_list, C, row, dataptr, &itemptr, icon, active_dataptr, active_propname, i); - } - - i++; - } - RNA_PROP_END; + icon = UI_rnaptr_icon_get(C, itemptr, rnaicon, false); + if (icon == ICON_DOT) + icon = ICON_NONE; + draw_item(ui_list, C, row, dataptr, itemptr, icon, active_dataptr, active_propname, org_i, 0); } - /* if list is empty, add in dummy button */ - if (i == 0) + else { uiItemL(row, "", ICON_NONE); + } /* next/prev button */ - BLI_snprintf(numstr, sizeof(numstr), "%d :", i); + BLI_snprintf(numstr, sizeof(numstr), "%d :", dyn_data->items_shown); but = uiDefIconTextButR_prop(block, NUM, 0, 0, numstr, 0, 0, UI_UNIT_X * 5, UI_UNIT_Y, active_dataptr, activeprop, 0, 0, 0, 0, 0, ""); - if (i == 0) + if (dyn_data->items_shown == 0) uiButSetFlag(but, UI_BUT_DISABLED); break; case UILST_LAYOUT_GRID: @@ -2778,9 +3004,11 @@ void uiTemplateList(uiLayout *layout, bContext *C, const char *listtype_name, co if (dataptr->data && prop) { /* create list items */ - RNA_PROP_BEGIN (dataptr, itemptr, prop) - { - if (i >= layoutdata.start_idx && i < layoutdata.end_idx) { + for (i = layoutdata.start_idx; i < layoutdata.end_idx; i++) { + PointerRNA *itemptr = &items_ptr[i].item; + int org_i = items_ptr[i].org_idx; + int flt_flag = items_ptr[i].flt_flag; + /* create button */ if (!(i % columns)) subrow = uiLayoutRow(col, FALSE); @@ -2794,13 +3022,14 @@ void uiTemplateList(uiLayout *layout, bContext *C, const char *listtype_name, co sub = uiLayoutRow(overlap, FALSE); but = uiDefButR_prop(subblock, LISTROW, 0, "", 0, 0, UI_UNIT_X * 10, UI_UNIT_Y, - active_dataptr, activeprop, 0, 0, i, 0, 0, NULL); + active_dataptr, activeprop, 0, 0, org_i, 0, 0, NULL); uiButSetFlag(but, UI_BUT_NO_TOOLTIP); sub = uiLayoutRow(overlap, FALSE); - icon = UI_rnaptr_icon_get(C, &itemptr, rnaicon, false); - draw_item(ui_list, C, sub, dataptr, &itemptr, icon, active_dataptr, active_propname, i); + icon = UI_rnaptr_icon_get(C, itemptr, rnaicon, false); + draw_item(ui_list, C, sub, dataptr, itemptr, icon, active_dataptr, active_propname, + org_i, flt_flag); /* If we are "drawing" active item, set all labels as active. */ if (i == activei) { @@ -2808,10 +3037,7 @@ void uiTemplateList(uiLayout *layout, bContext *C, const char *listtype_name, co } uiBlockClearFlag(subblock, UI_BLOCK_LIST_ITEM); - } - i++; } - RNA_PROP_END; } /* add dummy buttons to fill space */ @@ -2837,11 +3063,40 @@ void uiTemplateList(uiLayout *layout, bContext *C, const char *listtype_name, co subblock = uiLayoutGetBlock(row); uiBlockSetEmboss(subblock, UI_EMBOSSN); - but = uiDefIconBut(subblock, BUT, 0, ICON_GRIP, 0, 0, UI_UNIT_X * 10.0f, UI_UNIT_Y * 0.4f, ui_list, - 0.0, 0.0, 0, -1, ""); - uiButClearFlag(but, UI_BUT_UNDO); /* skip undo on screen buttons */ + if (ui_list->filter_flag & UILST_FLT_SHOW) { + but = uiDefIconButBitI(subblock, TOG, UILST_FLT_SHOW, 0, ICON_DISCLOSURE_TRI_DOWN, 0, 0, + UI_UNIT_X, UI_UNIT_Y * 0.6f, &(ui_list->filter_flag), 0, 0, 0, 0, + TIP_("Hide filtering options")); + uiButClearFlag(but, UI_BUT_UNDO); /* skip undo on screen buttons */ + + but = uiDefIconBut(subblock, BUT, 0, ICON_GRIP, 0, 0, UI_UNIT_X * 10.0f, UI_UNIT_Y * 0.6f, ui_list, + 0.0, 0.0, 0, -1, ""); + uiButClearFlag(but, UI_BUT_UNDO); /* skip undo on screen buttons */ + + uiBlockSetEmboss(subblock, UI_EMBOSS); + + col = uiLayoutColumn(glob, FALSE); + subblock = uiLayoutGetBlock(col); + uiDefBut(subblock, SEPR, 0, "", 0, 0, UI_UNIT_X, UI_UNIT_Y * 0.05f, NULL, 0.0, 0.0, 0, 0, ""); + + draw_filter(ui_list, C, col); + } + else { + but = uiDefIconButBitI(subblock, TOG, UILST_FLT_SHOW, 0, ICON_DISCLOSURE_TRI_RIGHT, 0, 0, + UI_UNIT_X, UI_UNIT_Y * 0.6f, &(ui_list->filter_flag), 0, 0, 0, 0, + TIP_("Show filtering options")); + uiButClearFlag(but, UI_BUT_UNDO); /* skip undo on screen buttons */ + + but = uiDefIconBut(subblock, BUT, 0, ICON_GRIP, 0, 0, UI_UNIT_X * 10.0f, UI_UNIT_Y * 0.6f, ui_list, + 0.0, 0.0, 0, -1, ""); + uiButClearFlag(but, UI_BUT_UNDO); /* skip undo on screen buttons */ + + uiBlockSetEmboss(subblock, UI_EMBOSS); + } + } - uiBlockSetEmboss(subblock, UI_EMBOSS); + if (items_ptr) { + MEM_freeN(items_ptr); } } diff --git a/source/blender/makesdna/DNA_screen_types.h b/source/blender/makesdna/DNA_screen_types.h index 8e18a84060a..435f5c634a9 100644 --- a/source/blender/makesdna/DNA_screen_types.h +++ b/source/blender/makesdna/DNA_screen_types.h @@ -123,6 +123,10 @@ typedef struct uiListDyn { int items_len; /* Number of items in collection. */ int items_shown; /* Number of items actually visible after filtering. */ + + /* Filtering data. */ + int *items_filter_flags; /* items_len length. */ + int *items_filter_neworder; /* org_idx -> new_idx, items_len length. */ } uiListDyn; typedef struct uiList { /* some list UI data need to be saved in file */ @@ -140,6 +144,14 @@ typedef struct uiList { /* some list UI data need to be saved in file int list_last_len; int padi1; + /* Filtering data. */ + char filter_byname[64]; /* defined as UI_MAX_NAME_STR */ + int filter_flag; + int filter_orderby_flag; + + /* Custom sub-classes properties. */ + IDProperty *properties; + /* Dynamic data (runtime). */ uiListDyn *dyn_data; } uiList; @@ -259,6 +271,25 @@ enum { UILST_RESIZING = 1 << 1, /* We are currently resizing, deactivate autosize! */ }; +/* uiList filter flags (dyn_data) */ +enum { + UILST_FLT_ITEM = 1 << 31, /* This item has passed the filter process successfully. */ +}; + +/* uiList filter options */ +enum { + UILST_FLT_SHOW = 1 << 0, /* Show filtering UI. */ + UILST_FLT_EXCLUDE = UILST_FLT_ITEM, /* Exclude filtered items, *must* use this same value. */ +}; + +/* uiList filter orderby type */ +enum { + UILST_FLT_ORDERBY_NAME = 1 << 0, + UILST_FLT_ORDERBY_REVERSE = 1 << 31 /* Special value, bitflag used to reverse order! */ +}; + +#define UILST_FLT_ORDERBY_MASK (((unsigned int)UILST_FLT_ORDERBY_REVERSE) - 1) + /* regiontype, first two are the default set */ /* Do NOT change order, append on end. Types are hardcoded needed */ enum { diff --git a/source/blender/makesrna/intern/rna_ui.c b/source/blender/makesrna/intern/rna_ui.c index 9efe3d9f1d6..dbddf54fe04 100644 --- a/source/blender/makesrna/intern/rna_ui.c +++ b/source/blender/makesrna/intern/rna_ui.c @@ -31,6 +31,8 @@ #include "BLF_translation.h" +#include "BKE_idprop.h" + #include "RNA_define.h" #include "rna_internal.h" @@ -58,12 +60,9 @@ EnumPropertyItem operator_context_items[] = { }; EnumPropertyItem uilist_layout_type_items[] = { - {UILST_LAYOUT_DEFAULT, "DEFAULT", 0, "Default Layout", - "Use the default, multi-rows layout"}, - {UILST_LAYOUT_COMPACT, "COMPACT", 0, "Compact Layout", - "Use the compact, single-row layout"}, - {UILST_LAYOUT_GRID, "GRID", 0, "Grid Layout", - "Use the grid-based layout"}, + {UILST_LAYOUT_DEFAULT, "DEFAULT", 0, "Default Layout", "Use the default, multi-rows layout"}, + {UILST_LAYOUT_COMPACT, "COMPACT", 0, "Compact Layout", "Use the compact, single-row layout"}, + {UILST_LAYOUT_GRID, "GRID", 0, "Grid Layout", "Use the grid-based layout"}, {0, NULL, 0, NULL, NULL} }; @@ -269,8 +268,24 @@ static StructRNA *rna_Panel_refine(PointerRNA *ptr) } /* UIList */ +static unsigned int rna_UIList_filter_const_FILTER_ITEM_get(PointerRNA *ptr) +{ + return UILST_FLT_ITEM; +} + +static IDProperty *rna_UIList_idprops(PointerRNA *ptr, bool create) +{ + uiList *ui_list = (uiList *)ptr->data; + if (create && !ui_list->properties) { + IDPropertyTemplate val = {0}; + ui_list->properties = IDP_New(IDP_GROUP, &val, "RNA_UIList IDproperties group"); + } + + return ui_list->properties; +} + static void uilist_draw_item(uiList *ui_list, bContext *C, uiLayout *layout, PointerRNA *dataptr, PointerRNA *itemptr, - int icon, PointerRNA *active_dataptr, const char *active_propname, int index) + int icon, PointerRNA *active_dataptr, const char *active_propname, int index, int flt_flag) { extern FunctionRNA rna_UIList_draw_item_func; @@ -290,11 +305,139 @@ static void uilist_draw_item(uiList *ui_list, bContext *C, uiLayout *layout, Poi RNA_parameter_set_lookup(&list, "active_data", active_dataptr); RNA_parameter_set_lookup(&list, "active_property", &active_propname); RNA_parameter_set_lookup(&list, "index", &index); + RNA_parameter_set_lookup(&list, "flt_flag", &flt_flag); + ui_list->type->ext.call((bContext *)C, &ul_ptr, func, &list); + + RNA_parameter_list_free(&list); +} + +static void uilist_draw_filter(uiList *ui_list, bContext *C, uiLayout *layout) +{ + extern FunctionRNA rna_UIList_draw_filter_func; + + PointerRNA ul_ptr; + ParameterList list; + FunctionRNA *func; + + RNA_pointer_create(&CTX_wm_screen(C)->id, ui_list->type->ext.srna, ui_list, &ul_ptr); + func = &rna_UIList_draw_filter_func; /* RNA_struct_find_function(&ul_ptr, "draw_filter"); */ + + RNA_parameter_list_create(&list, &ul_ptr, func); + RNA_parameter_set_lookup(&list, "context", &C); + RNA_parameter_set_lookup(&list, "layout", &layout); ui_list->type->ext.call((bContext *)C, &ul_ptr, func, &list); RNA_parameter_list_free(&list); } +static void uilist_filter_items(uiList *ui_list, bContext *C, PointerRNA *dataptr, const char *propname) +{ + extern FunctionRNA rna_UIList_filter_items_func; + + PointerRNA ul_ptr; + ParameterList list; + FunctionRNA *func; + PropertyRNA *parm; + + uiListDyn *flt_data = ui_list->dyn_data; + int *filter_flags, *filter_neworder; + void *ret1, *ret2; + int ret_len; + int len = flt_data->items_len = RNA_collection_length(dataptr, propname); + + RNA_pointer_create(&CTX_wm_screen(C)->id, ui_list->type->ext.srna, ui_list, &ul_ptr); + func = &rna_UIList_filter_items_func; /* RNA_struct_find_function(&ul_ptr, "filter_items"); */ + + RNA_parameter_list_create(&list, &ul_ptr, func); + RNA_parameter_set_lookup(&list, "context", &C); + RNA_parameter_set_lookup(&list, "data", dataptr); + RNA_parameter_set_lookup(&list, "property", &propname); + + ui_list->type->ext.call((bContext *)C, &ul_ptr, func, &list); + + parm = RNA_function_find_parameter(NULL, func, "filter_flags"); + ret_len = RNA_parameter_dynamic_length_get(&list, parm); + if (ret_len != len && ret_len != 0) { + printf("%s: Error, py func returned %d items in %s, %d or none were expected.\n", AT, + RNA_parameter_dynamic_length_get(&list, parm), "filter_flags", len); + RNA_parameter_list_free(&list); + return; + } + RNA_parameter_get(&list, parm, &ret1); + filter_flags = (int *)ret1; + + parm = RNA_function_find_parameter(NULL, func, "filter_neworder"); + ret_len = RNA_parameter_dynamic_length_get(&list, parm); + if (ret_len != len && ret_len != 0) { + printf("%s: Error, py func returned %d items in %s, %d or none were expected.\n", AT, + RNA_parameter_dynamic_length_get(&list, parm), "filter_neworder", len); + RNA_parameter_list_free(&list); + return; + } + RNA_parameter_get(&list, parm, &ret2); + filter_neworder = (int *)ret2; + + /* We have to do some final checks and transforms... */ + { + int i, filter_exclude = ui_list->filter_flag & UILST_FLT_EXCLUDE; + if (filter_flags) { + flt_data->items_filter_flags = MEM_mallocN(sizeof(int) * len, AT); + memcpy(flt_data->items_filter_flags, filter_flags, sizeof(int) * len); + + if (filter_neworder) { + /* For sake of simplicity, py filtering is expected to filter all items, but we actually only want + * reordering data for shown items! + */ + int items_shown, shown_idx; + int t_idx, t_ni, prev_ni; + flt_data->items_shown = 0; + for (i = 0, shown_idx = 0; i < len; i++) { + if ((filter_flags[i] & UILST_FLT_ITEM) ^ filter_exclude) { + filter_neworder[shown_idx++] = filter_neworder[i]; + } + } + items_shown = flt_data->items_shown = shown_idx; + flt_data->items_filter_neworder = MEM_mallocN(sizeof(int) * items_shown, AT); + /* And now, bring back new indices into the [0, items_shown[ range! + * XXX This is O(N²)... :/ + */ + for (shown_idx = 0, prev_ni = -1; shown_idx < items_shown; shown_idx++) { + for (i = 0, t_ni = len, t_idx = -1; i < items_shown; i++) { + int ni = filter_neworder[i]; + if (ni > prev_ni && ni < t_ni) { + t_idx = i; + t_ni = ni; + } + } + if (t_idx >= 0) { + prev_ni = t_ni; + flt_data->items_filter_neworder[t_idx] = shown_idx; + } + } + } + else { + /* we still have to set flt_data->items_shown... */ + flt_data->items_shown = 0; + for (i = 0; i < len; i++) { + if ((filter_flags[i] & UILST_FLT_ITEM) ^ filter_exclude) { + flt_data->items_shown++; + } + } + } + } + else { + flt_data->items_shown = len; + + if (filter_neworder) { + flt_data->items_filter_neworder = MEM_mallocN(sizeof(int) * len, AT); + memcpy(flt_data->items_filter_neworder, filter_neworder, sizeof(int) * len); + } + } + } + + RNA_parameter_list_free(&list); +} + static void rna_UIList_unregister(Main *UNUSED(bmain), StructRNA *type) { uiListType *ult = RNA_struct_blender_type_get(type); @@ -318,7 +461,7 @@ static StructRNA *rna_UIList_register(Main *bmain, ReportList *reports, void *da uiListType *ult, dummyult = {NULL}; uiList dummyuilist = {NULL}; PointerRNA dummyul_ptr; - int have_function[1]; + int have_function[3]; size_t over_alloc = 0; /* warning, if this becomes a bess, we better do another alloc */ /* setup dummy menu & menu type to store static properties in */ @@ -349,9 +492,10 @@ static StructRNA *rna_UIList_register(Main *bmain, ReportList *reports, void *da ult->ext.call = call; ult->ext.free = free; RNA_struct_blender_type_set(ult->ext.srna, ult); - RNA_def_struct_flag(ult->ext.srna, STRUCT_NO_IDPROPERTIES); ult->draw_item = (have_function[0]) ? uilist_draw_item : NULL; + ult->draw_filter = (have_function[1]) ? uilist_draw_filter : NULL; + ult->filter_items = (have_function[2]) ? uilist_filter_items : NULL; WM_uilisttype_add(ult); @@ -874,8 +1018,45 @@ static void rna_def_uilist(BlenderRNA *brna) RNA_def_struct_sdna(srna, "uiList"); RNA_def_struct_refine_func(srna, "rna_UIList_refine"); RNA_def_struct_register_funcs(srna, "rna_UIList_register", "rna_UIList_unregister", NULL); + RNA_def_struct_idprops_func(srna, "rna_UIList_idprops"); - /* draw */ + /* Registration */ + prop = RNA_def_property(srna, "bl_idname", PROP_STRING, PROP_NONE); + RNA_def_property_string_sdna(prop, NULL, "type->idname"); + RNA_def_property_flag(prop, PROP_REGISTER | PROP_NEVER_CLAMP); + RNA_def_property_ui_text(prop, "ID Name", + "If this is set, the uilist gets a custom ID, otherwise it takes the " + "name of the class used to define the uilist (for example, if the " + "class name is \"OBJECT_UL_vgroups\", and bl_idname is not set by the " + "script, then bl_idname = \"OBJECT_UL_vgroups\")"); + + /* Data */ + prop = RNA_def_property(srna, "layout_type", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, uilist_layout_type_items); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + + /* Filter options */ + prop = RNA_def_property(srna, "use_filter_show", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "filter_flag", UILST_FLT_SHOW); + RNA_def_property_ui_text(prop, "Show Filter", "Show filtering options"); + + prop = RNA_def_property(srna, "filter_name", PROP_STRING, PROP_NONE); + RNA_def_property_string_sdna(prop, NULL, "filter_byname"); + RNA_def_property_ui_text(prop, "Filter by Name", "Only show items matching this name (use '*' as wildcard)"); + + prop = RNA_def_property(srna, "use_filter_invert", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "filter_flag", UILST_FLT_EXCLUDE); + RNA_def_property_ui_text(prop, "Invert", "Invert filtering (show hidden items, and vice-versa)"); + + prop = RNA_def_property(srna, "use_filter_orderby_name", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "filter_orderby_flag", UILST_FLT_ORDERBY_NAME); + RNA_def_property_ui_text(prop, "Order by Name", "Order shown items by their names"); + + prop = RNA_def_property(srna, "use_filter_orderby_invert", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "filter_orderby_flag", UILST_FLT_ORDERBY_REVERSE); + RNA_def_property_ui_text(prop, "Invert", "Invert the order of shown items"); + + /* draw_item */ func = RNA_def_function(srna, "draw_item", NULL); RNA_def_function_ui_description(func, "Draw an item in the list (NOTE: when you define your own draw_item " "function, you may want to check given 'item' is of the right type...)"); @@ -897,21 +1078,52 @@ static void rna_def_uilist(BlenderRNA *brna) "Identifier of property in active_data, for the active element"); RNA_def_property_flag(parm, PROP_REQUIRED); RNA_def_int(func, "index", 0, 0, INT_MAX, "", "Index of the item in the collection", 0, INT_MAX); + RNA_def_property_flag(parm, PROP_REQUIRED | PROP_PYFUNC_OPTIONAL); + prop = RNA_def_property(func, "flt_flag", PROP_INT, PROP_UNSIGNED); + RNA_def_property_ui_text(prop, "", "The filter-flag result for this item"); + RNA_def_property_flag(parm, PROP_REQUIRED | PROP_PYFUNC_OPTIONAL); + + /* draw_filter */ + func = RNA_def_function(srna, "draw_filter", NULL); + RNA_def_function_ui_description(func, "Draw filtering options"); + RNA_def_function_flag(func, FUNC_REGISTER_OPTIONAL); + parm = RNA_def_pointer(func, "context", "Context", "", ""); RNA_def_property_flag(parm, PROP_REQUIRED); + parm = RNA_def_pointer(func, "layout", "UILayout", "", "Layout to draw the item"); + RNA_def_property_flag(parm, PROP_REQUIRED | PROP_NEVER_NULL); - prop = RNA_def_property(srna, "layout_type", PROP_ENUM, PROP_NONE); - RNA_def_property_enum_items(prop, uilist_layout_type_items); - RNA_def_property_clear_flag(prop, PROP_EDITABLE); + /* filter */ + func = RNA_def_function(srna, "filter_items", NULL); + RNA_def_function_ui_description(func, "Filter and/or re-order items of the collection (output filter results in " + "filter_flags, and reorder results in filter_neworder arrays)"); + RNA_def_function_flag(func, FUNC_REGISTER_OPTIONAL); + parm = RNA_def_pointer(func, "context", "Context", "", ""); + RNA_def_property_flag(parm, PROP_REQUIRED); + parm = RNA_def_pointer(func, "data", "AnyType", "", "Data from which to take Collection property"); + RNA_def_property_flag(parm, PROP_REQUIRED | PROP_RNAPTR); + parm = RNA_def_string(func, "property", "", 0, "", "Identifier of property in data, for the collection"); + RNA_def_property_flag(parm, PROP_REQUIRED); + prop = RNA_def_property(func, "filter_flags", PROP_INT, PROP_UNSIGNED); + RNA_def_property_flag(prop, PROP_REQUIRED | PROP_DYNAMIC); + RNA_def_property_array(prop, 1); /* XXX Dummy value, default 0 does not work */ + RNA_def_property_ui_text(prop, "", "An array of filter flags, one for each item in the collection (NOTE: " + "FILTER_ITEM bit is reserved, it defines whether the item is shown or not)"); + RNA_def_function_output(func, prop); + prop = RNA_def_property(func, "filter_neworder", PROP_INT, PROP_UNSIGNED); + RNA_def_property_flag(prop, PROP_REQUIRED | PROP_DYNAMIC); + RNA_def_property_array(prop, 1); /* XXX Dummy value, default 0 does not work */ + RNA_def_property_ui_text(prop, "", "An array of indices, one for each item in the collection, mapping the org " + "index to the new one"); + RNA_def_function_output(func, prop); + + /* "Constants"! */ + RNA_define_verify_sdna(0); /* not in sdna */ - /* registration */ - prop = RNA_def_property(srna, "bl_idname", PROP_STRING, PROP_NONE); - RNA_def_property_string_sdna(prop, NULL, "type->idname"); - RNA_def_property_flag(prop, PROP_REGISTER | PROP_NEVER_CLAMP); - RNA_def_property_ui_text(prop, "ID Name", - "If this is set, the uilist gets a custom ID, otherwise it takes the " - "name of the class used to define the uilist (for example, if the " - "class name is \"OBJECT_UL_vgroups\", and bl_idname is not set by the " - "script, then bl_idname = \"OBJECT_UL_vgroups\")"); + prop = RNA_def_property(srna, "bitflag_filter_item", PROP_INT, PROP_UNSIGNED); + RNA_def_property_ui_text(prop, "FILTER_ITEM", + "The value of the reserved bitfalg 'FILTER_ITEM' (in filter_flags values)"); + RNA_def_property_int_funcs(prop, "rna_UIList_filter_const_FILTER_ITEM_get", NULL, NULL); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); } static void rna_def_header(BlenderRNA *brna) |