Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPablo <pablodp606@gmail.com>2021-08-28 20:58:13 +0300
committerPablo <pablodp606@gmail.com>2021-08-28 20:58:13 +0300
commit6648f82ef7d6a15c8e8fb5474bdee6d2e7853a26 (patch)
tree738aed0dcdc759bf24d47ab91bcfaa63e823568f
parent320ff879485213d4bb3eda2e40f7481cc6d7c83c (diff)
parent457302b67b9de6a92240c2736306cfa01187101d (diff)
Merge branch 'master' into sculpt-dev
-rw-r--r--release/scripts/modules/rna_prop_ui.py118
-rw-r--r--release/scripts/startup/bl_operators/object.py2
-rw-r--r--release/scripts/startup/bl_operators/wm.py83
-rw-r--r--release/scripts/startup/bl_ui/space_sequencer.py3
-rw-r--r--release/scripts/startup/keyingsets_builtins.py4
-rw-r--r--source/blender/blenfont/intern/blf_dir.c13
-rw-r--r--source/blender/blenfont/intern/blf_font.c14
-rw-r--r--source/blender/blenkernel/BKE_blender_version.h2
-rw-r--r--source/blender/blenkernel/BKE_idprop.h27
-rw-r--r--source/blender/blenkernel/intern/idprop.c336
-rw-r--r--source/blender/blenkernel/intern/object_dupli.cc4
-rw-r--r--source/blender/blenkernel/intern/text.c12
-rw-r--r--source/blender/blenkernel/intern/unit.c2
-rw-r--r--source/blender/blenlib/BLI_string_utf8.h13
-rw-r--r--source/blender/blenlib/intern/string_cursor_utf8.c10
-rw-r--r--source/blender/blenlib/intern/string_utf8.c251
-rw-r--r--source/blender/blenlib/tests/BLI_span_test.cc2
-rw-r--r--source/blender/blenloader/intern/versioning_300.c269
-rw-r--r--source/blender/editors/interface/interface_eyedropper.c5
-rw-r--r--source/blender/editors/interface/interface_eyedropper_datablock.c10
-rw-r--r--source/blender/editors/interface/interface_eyedropper_depth.c6
-rw-r--r--source/blender/editors/interface/interface_eyedropper_intern.h5
-rw-r--r--source/blender/editors/interface/interface_handlers.c3
-rw-r--r--source/blender/editors/interface/interface_widgets.c17
-rw-r--r--source/blender/editors/space_action/action_draw.c24
-rw-r--r--source/blender/editors/space_buttons/buttons_context.c3
-rw-r--r--source/blender/editors/space_console/console_ops.c2
-rw-r--r--source/blender/editors/space_image/image_draw.c10
-rw-r--r--source/blender/editors/space_node/node_draw.cc29
-rw-r--r--source/blender/editors/space_node/node_intern.h2
-rw-r--r--source/blender/editors/space_sequencer/sequencer_draw.c9
-rw-r--r--source/blender/editors/space_text/text_draw.c6
-rw-r--r--source/blender/editors/space_text/text_ops.c4
-rw-r--r--source/blender/editors/space_view3d/view3d_edit.c3
-rw-r--r--source/blender/editors/transform/transform_convert_sequencer.c220
-rw-r--r--source/blender/editors/transform/transform_mode_edge_seq_slide.c25
-rw-r--r--source/blender/imbuf/intern/thumbs.c5
-rw-r--r--source/blender/io/alembic/exporter/abc_custom_props.cc3
-rw-r--r--source/blender/makesdna/DNA_ID.h54
-rw-r--r--source/blender/makesdna/DNA_modifier_types.h1
-rw-r--r--source/blender/makesdna/DNA_scene_types.h9
-rw-r--r--source/blender/makesrna/RNA_access.h6
-rw-r--r--source/blender/makesrna/intern/rna_access.c443
-rw-r--r--source/blender/makesrna/intern/rna_internal_types.h3
-rw-r--r--source/blender/makesrna/intern/rna_modifier.c6
-rw-r--r--source/blender/makesrna/intern/rna_object.c11
-rw-r--r--source/blender/makesrna/intern/rna_scene.c20
-rw-r--r--source/blender/modifiers/intern/MOD_mask.cc435
-rw-r--r--source/blender/modifiers/intern/MOD_nodes.cc517
-rw-r--r--source/blender/python/generic/CMakeLists.txt3
-rw-r--r--source/blender/python/generic/idprop_py_api.c2
-rw-r--r--source/blender/python/generic/idprop_py_ui_api.c734
-rw-r--r--source/blender/python/generic/idprop_py_ui_api.h33
-rw-r--r--source/blender/python/intern/bpy.c2
-rw-r--r--source/blender/python/intern/bpy_rna.c50
-rw-r--r--source/blender/sequencer/SEQ_iterator.h21
-rw-r--r--source/blender/sequencer/SEQ_sequencer.h1
-rw-r--r--source/blender/sequencer/SEQ_transform.h1
-rw-r--r--source/blender/sequencer/intern/effects.c53
-rw-r--r--source/blender/sequencer/intern/iterator.c18
-rw-r--r--source/blender/sequencer/intern/sequencer.c8
-rw-r--r--source/blender/sequencer/intern/strip_transform.c6
-rw-r--r--source/blender/windowmanager/xr/intern/wm_xr.c1
-rw-r--r--source/blender/windowmanager/xr/intern/wm_xr_actionmap.c51
-rw-r--r--tests/python/bl_pyapi_idprop.py63
65 files changed, 2921 insertions, 1187 deletions
diff --git a/release/scripts/modules/rna_prop_ui.py b/release/scripts/modules/rna_prop_ui.py
index bafa2b28bbf..26a2f9ad89b 100644
--- a/release/scripts/modules/rna_prop_ui.py
+++ b/release/scripts/modules/rna_prop_ui.py
@@ -30,24 +30,6 @@ ARRAY_TYPES = (list, tuple, IDPropertyArray, Vector)
MAX_DISPLAY_ROWS = 4
-def rna_idprop_ui_get(item, *, create=True):
- try:
- return item['_RNA_UI']
- except:
- if create:
- item['_RNA_UI'] = {}
- return item['_RNA_UI']
- else:
- return None
-
-
-def rna_idprop_ui_del(item):
- try:
- del item['_RNA_UI']
- except KeyError:
- pass
-
-
def rna_idprop_quote_path(prop):
return "[\"%s\"]" % bpy.utils.escape_identifier(prop)
@@ -59,32 +41,9 @@ def rna_idprop_ui_prop_update(item, prop):
prop_rna.update()
-def rna_idprop_ui_prop_get(item, prop, *, create=True):
-
- rna_ui = rna_idprop_ui_get(item, create=create)
-
- if rna_ui is None:
- return None
-
- try:
- return rna_ui[prop]
- except:
- rna_ui[prop] = {}
- return rna_ui[prop]
-
-
-def rna_idprop_ui_prop_clear(item, prop, *, remove=True):
- rna_ui = rna_idprop_ui_get(item, create=False)
-
- if rna_ui is None:
- return
-
- try:
- del rna_ui[prop]
- except KeyError:
- pass
- if remove and len(item.keys()) == 1:
- rna_idprop_ui_del(item)
+def rna_idprop_ui_prop_clear(item, prop):
+ ui_data = item.id_properties_ui(prop)
+ ui_data.clear()
def rna_idprop_context_value(context, context_member, property_type):
@@ -106,8 +65,7 @@ def rna_idprop_context_value(context, context_member, property_type):
def rna_idprop_has_properties(rna_item):
keys = rna_item.keys()
- nbr_props = len(keys)
- return (nbr_props > 1) or (nbr_props and '_RNA_UI' not in keys)
+ return bool(keys)
def rna_idprop_value_to_python(value):
@@ -126,31 +84,8 @@ def rna_idprop_value_item_type(value):
def rna_idprop_ui_prop_default_set(item, prop, value):
- defvalue = None
- try:
- prop_type, is_array = rna_idprop_value_item_type(item[prop])
-
- if prop_type in {int, float, str}:
- if is_array and isinstance(value, ARRAY_TYPES):
- value = [prop_type(item) for item in value]
- if any(value):
- defvalue = value
- else:
- defvalue = prop_type(value)
- except KeyError:
- pass
- except ValueError:
- pass
-
- if defvalue:
- rna_ui = rna_idprop_ui_prop_get(item, prop, create=True)
- rna_ui["default"] = defvalue
- else:
- rna_ui = rna_idprop_ui_prop_get(item, prop)
- if rna_ui:
- rna_ui.pop("default", None)
-
- return defvalue
+ ui_data = item.id_properties_ui(prop)
+ ui_data.update(default=value)
def rna_idprop_ui_create(
@@ -163,7 +98,7 @@ def rna_idprop_ui_create(
):
"""Create and initialize a custom property with limits, defaults and other settings."""
- proptype, is_array = rna_idprop_value_item_type(default)
+ proptype, _ = rna_idprop_value_item_type(default)
# Sanitize limits
if proptype is bool:
@@ -180,35 +115,22 @@ def rna_idprop_ui_create(
rna_idprop_ui_prop_update(item, prop)
- # Clear the UI settings
- rna_ui_group = rna_idprop_ui_get(item, create=True)
- rna_ui_group[prop] = {}
- rna_ui = rna_ui_group[prop]
-
- # Assign limits and default
- if proptype in {int, float, bool}:
- # The type must be exactly the same
- rna_ui["min"] = proptype(min)
- rna_ui["soft_min"] = proptype(soft_min)
- rna_ui["max"] = proptype(max)
- rna_ui["soft_max"] = proptype(soft_max)
-
- if default and (not is_array or any(default)):
- rna_ui["default"] = default
-
- if is_array and subtype and subtype != 'NONE':
- rna_ui["subtype"] = subtype
-
- # Assign other settings
- if description is not None:
- rna_ui["description"] = description
+ # Update the UI settings.
+ ui_data = item.id_properties_ui(prop)
+ ui_data.update(
+ subtype=subtype,
+ min=min,
+ max=max,
+ soft_min=soft_min,
+ soft_max=soft_max,
+ description=description,
+ default=default,
+ )
prop_path = rna_idprop_quote_path(prop)
item.property_overridable_library_set(prop_path, overridable)
- return rna_ui
-
def draw(layout, context, context_member, property_type, *, use_edit=True):
@@ -254,10 +176,6 @@ def draw(layout, context, context_member, property_type, *, use_edit=True):
flow = layout.grid_flow(row_major=False, columns=0, even_columns=True, even_rows=False, align=True)
for key, val in items:
-
- if key == '_RNA_UI':
- continue
-
is_rna = (key in rna_properties)
# only show API defined for developers
diff --git a/release/scripts/startup/bl_operators/object.py b/release/scripts/startup/bl_operators/object.py
index d61bed71cab..df37db40102 100644
--- a/release/scripts/startup/bl_operators/object.py
+++ b/release/scripts/startup/bl_operators/object.py
@@ -970,7 +970,7 @@ class OBJECT_OT_assign_property_defaults(Operator):
def assign_defaults(obj):
from rna_prop_ui import rna_idprop_ui_prop_default_set
- rna_properties = {'_RNA_UI'} | {prop.identifier for prop in obj.bl_rna.properties if prop.is_runtime}
+ rna_properties = {prop.identifier for prop in obj.bl_rna.properties if prop.is_runtime}
for prop, value in obj.items():
if prop not in rna_properties:
diff --git a/release/scripts/startup/bl_operators/wm.py b/release/scripts/startup/bl_operators/wm.py
index 6a3830ad1e4..989c067efec 100644
--- a/release/scripts/startup/bl_operators/wm.py
+++ b/release/scripts/startup/bl_operators/wm.py
@@ -1388,10 +1388,8 @@ class WM_OT_properties_edit(Operator):
def execute(self, context):
from rna_prop_ui import (
- rna_idprop_ui_prop_get,
rna_idprop_ui_prop_clear,
rna_idprop_ui_prop_update,
- rna_idprop_ui_prop_default_set,
rna_idprop_value_item_type,
)
@@ -1431,27 +1429,35 @@ class WM_OT_properties_edit(Operator):
prop_type_new = type(prop_value)
prop_type, is_array = rna_idprop_value_item_type(prop_value)
- prop_ui = rna_idprop_ui_prop_get(item, prop)
-
- if prop_type in {float, int}:
- prop_ui["min"] = prop_type(self.min)
- prop_ui["max"] = prop_type(self.max)
-
- if self.use_soft_limits:
- prop_ui["soft_min"] = prop_type(self.soft_min)
- prop_ui["soft_max"] = prop_type(self.soft_max)
- else:
- prop_ui["soft_min"] = prop_type(self.min)
- prop_ui["soft_max"] = prop_type(self.max)
-
- if prop_type == float and is_array and self.subtype != 'NONE':
- prop_ui["subtype"] = self.subtype
- else:
- prop_ui.pop("subtype", None)
-
- prop_ui["description"] = self.description
-
- rna_idprop_ui_prop_default_set(item, prop, default_eval)
+ ui_data = item.id_properties_ui(prop)
+ ui_data.update(subtype=self.subtype, description=self.description)
+
+ if prop_type == int:
+ if type(default_eval) == str:
+ self.report({'WARNING'}, "Could not evaluate number from default value")
+ default_eval = None
+ elif hasattr(default_eval, "__len__"):
+ default_eval = [int(round(value)) for value in default_eval]
+ ui_data.update(
+ min=int(round(self.min)),
+ max=int(round(self.max)),
+ soft_min=int(round(self.soft_min)),
+ soft_max=int(round(self.soft_max)),
+ default=default_eval,
+ )
+ elif prop_type == float:
+ if type(default_eval) == str:
+ self.report({'WARNING'}, "Could not evaluate number from default value")
+ default_eval = None
+ ui_data.update(
+ min=self.min,
+ max=self.max,
+ soft_min=self.soft_min,
+ soft_max=self.soft_max,
+ default=default_eval,
+ )
+ elif prop_type == str:
+ ui_data.update(default=self.default)
# If we have changed the type of the property, update its potential anim curves!
if prop_type_old != prop_type_new:
@@ -1492,7 +1498,6 @@ class WM_OT_properties_edit(Operator):
def invoke(self, context, _event):
from rna_prop_ui import (
- rna_idprop_ui_prop_get,
rna_idprop_value_to_python,
rna_idprop_value_item_type
)
@@ -1526,28 +1531,22 @@ class WM_OT_properties_edit(Operator):
self.default = ""
# setup defaults
- prop_ui = rna_idprop_ui_prop_get(item, prop, create=False)
- if prop_ui:
- self.min = prop_ui.get("min", -1000000000)
- self.max = prop_ui.get("max", 1000000000)
- self.description = prop_ui.get("description", "")
-
- defval = prop_ui.get("default", None)
- if defval is not None:
- self.default = str(rna_idprop_value_to_python(defval))
-
- self.soft_min = prop_ui.get("soft_min", self.min)
- self.soft_max = prop_ui.get("soft_max", self.max)
+ ui_data = item.id_properties_ui(prop)
+ rna_data = ui_data.as_dict()
+ self.subtype = rna_data["subtype"]
+ if prop_type in {int, float}:
+ self.min = rna_data["min"]
+ self.max = rna_data["max"]
+ self.soft_min = rna_data["soft_min"]
+ self.soft_max = rna_data["soft_max"]
self.use_soft_limits = (
self.min != self.soft_min or
self.max != self.soft_max
)
+ if prop_type in {int, float, str}:
+ self.default = str(rna_data["default"])
- subtype = prop_ui.get("subtype", None)
- else:
- subtype = None
-
- self._init_subtype(prop_type, is_array, subtype)
+ self._init_subtype(prop_type, is_array, self.subtype)
# store for comparison
self._cmp_props = self._cmp_props_get()
@@ -1688,7 +1687,6 @@ class WM_OT_properties_remove(Operator):
def execute(self, context):
from rna_prop_ui import (
- rna_idprop_ui_prop_clear,
rna_idprop_ui_prop_update,
)
data_path = self.data_path
@@ -1701,7 +1699,6 @@ class WM_OT_properties_remove(Operator):
prop = self.property
rna_idprop_ui_prop_update(item, prop)
del item[prop]
- rna_idprop_ui_prop_clear(item, prop)
return {'FINISHED'}
diff --git a/release/scripts/startup/bl_ui/space_sequencer.py b/release/scripts/startup/bl_ui/space_sequencer.py
index 20fb39e8c1f..79c1cb973ca 100644
--- a/release/scripts/startup/bl_ui/space_sequencer.py
+++ b/release/scripts/startup/bl_ui/space_sequencer.py
@@ -150,6 +150,9 @@ class SEQUENCER_HT_header(Header):
if st.view_type in {'SEQUENCER', 'SEQUENCER_PREVIEW'}:
tool_settings = context.tool_settings
+ sequencer_tool_settings = tool_settings.sequencer_tool_settings
+ row = layout.row(align=True)
+ row.prop(sequencer_tool_settings, "overlap_mode", text="")
row = layout.row(align=True)
row.prop(tool_settings, "use_snap_sequencer", text="")
sub = row.row(align=True)
diff --git a/release/scripts/startup/keyingsets_builtins.py b/release/scripts/startup/keyingsets_builtins.py
index ceffeaaff6c..d9811b6c0ed 100644
--- a/release/scripts/startup/keyingsets_builtins.py
+++ b/release/scripts/startup/keyingsets_builtins.py
@@ -523,10 +523,6 @@ class WholeCharacterMixin:
# go over all custom properties for bone
for prop in bone.keys():
- # ignore special "_RNA_UI" used for UI editing
- if prop == "_RNA_UI":
- continue
-
# for now, just add all of 'em
prop_rna = type(bone).bl_rna.properties.get(prop, None)
if prop_rna is None:
diff --git a/source/blender/blenfont/intern/blf_dir.c b/source/blender/blenfont/intern/blf_dir.c
index 55c02b34f3d..f4a20faf109 100644
--- a/source/blender/blenfont/intern/blf_dir.c
+++ b/source/blender/blenfont/intern/blf_dir.c
@@ -47,9 +47,6 @@
#include "blf_internal.h"
#include "blf_internal_types.h"
-#include "BKE_global.h"
-#include "BKE_main.h"
-
static ListBase global_font_dir = {NULL, NULL};
static DirBLF *blf_dir_find(const char *path)
@@ -127,6 +124,8 @@ void BLF_dir_free(char **dirs, int count)
char *blf_dir_search(const char *file)
{
+ BLI_assert_msg(!BLI_path_is_rel(file), "Relative paths must always be expanded!");
+
DirBLF *dir;
char full_path[FILE_MAX];
char *s = NULL;
@@ -140,11 +139,9 @@ char *blf_dir_search(const char *file)
}
if (!s) {
- /* Assume file is either an absolute path, or a relative path to current directory. */
- BLI_strncpy(full_path, file, sizeof(full_path));
- BLI_path_abs(full_path, BKE_main_blendfile_path(G_MAIN));
- if (BLI_exists(full_path)) {
- s = BLI_strdup(full_path);
+ /* This may be an absolute path which exists. */
+ if (BLI_exists(file)) {
+ s = BLI_strdup(file);
}
}
diff --git a/source/blender/blenfont/intern/blf_font.c b/source/blender/blenfont/intern/blf_font.c
index dbcd1d6016d..27478bd7f8e 100644
--- a/source/blender/blenfont/intern/blf_font.c
+++ b/source/blender/blenfont/intern/blf_font.c
@@ -673,23 +673,23 @@ size_t blf_font_width_to_rstrlen(
GlyphBLF *g, *g_prev;
int pen_x, width_new;
size_t i, i_prev, i_tmp;
- char *s, *s_prev;
+ const char *s, *s_prev;
GlyphCacheBLF *gc = blf_glyph_cache_acquire(font);
const int width_i = (int)width;
i = BLI_strnlen(str, str_len);
- s = BLI_str_find_prev_char_utf8(str, &str[i]);
- i = (size_t)((s != NULL) ? s - str : 0);
- s_prev = BLI_str_find_prev_char_utf8(str, s);
- i_prev = (size_t)((s_prev != NULL) ? s_prev - str : 0);
+ s = BLI_str_find_prev_char_utf8(&str[i], str);
+ i = (size_t)(s - str);
+ s_prev = BLI_str_find_prev_char_utf8(s, str);
+ i_prev = (size_t)(s_prev - str);
i_tmp = i;
g = blf_utf8_next_fast(font, gc, str, str_len, &i_tmp, &c);
for (width_new = pen_x = 0; (s != NULL);
i = i_prev, s = s_prev, c = c_prev, g = g_prev, g_prev = NULL, width_new = pen_x) {
- s_prev = BLI_str_find_prev_char_utf8(str, s);
- i_prev = (size_t)((s_prev != NULL) ? s_prev - str : 0);
+ s_prev = BLI_str_find_prev_char_utf8(s, str);
+ i_prev = (size_t)(s_prev - str);
if (s_prev != NULL) {
i_tmp = i_prev;
diff --git a/source/blender/blenkernel/BKE_blender_version.h b/source/blender/blenkernel/BKE_blender_version.h
index 4ed4225c836..315a77499e5 100644
--- a/source/blender/blenkernel/BKE_blender_version.h
+++ b/source/blender/blenkernel/BKE_blender_version.h
@@ -39,7 +39,7 @@ extern "C" {
/* Blender file format version. */
#define BLENDER_FILE_VERSION BLENDER_VERSION
-#define BLENDER_FILE_SUBVERSION 18
+#define BLENDER_FILE_SUBVERSION 19
/* Minimum Blender version that supports reading file written with the current
* version. Older Blender versions will test this and show a warning if the file
diff --git a/source/blender/blenkernel/BKE_idprop.h b/source/blender/blenkernel/BKE_idprop.h
index a5cb6489194..c28ac63388b 100644
--- a/source/blender/blenkernel/BKE_idprop.h
+++ b/source/blender/blenkernel/BKE_idprop.h
@@ -32,6 +32,7 @@ struct BlendLibReader;
struct BlendWriter;
struct ID;
struct IDProperty;
+struct IDPropertyUIData;
typedef union IDPropertyTemplate {
int i;
@@ -183,6 +184,10 @@ void IDP_Reset(struct IDProperty *prop, const struct IDProperty *reference);
# define IDP_Id(prop) ((ID *)(prop)->data.pointer)
#endif
+int IDP_coerce_to_int_or_zero(const struct IDProperty *prop);
+float IDP_coerce_to_float_or_zero(const struct IDProperty *prop);
+double IDP_coerce_to_double_or_zero(const struct IDProperty *prop);
+
/**
* Call a callback for each idproperty in the hierarchy under given root one (included).
*
@@ -209,6 +214,28 @@ void IDP_BlendReadData_impl(struct BlendDataReader *reader,
void IDP_BlendReadLib(struct BlendLibReader *reader, struct IDProperty *prop);
void IDP_BlendReadExpand(struct BlendExpander *expander, struct IDProperty *prop);
+typedef enum eIDPropertyUIDataType {
+ /** Other properties types that don't support RNA UI data. */
+ IDP_UI_DATA_TYPE_UNSUPPORTED = -1,
+ /** IDP_INT or IDP_ARRAY with subtype IDP_INT. */
+ IDP_UI_DATA_TYPE_INT = 0,
+ /** IDP_FLOAT and IDP_DOUBLE or IDP_ARRAY properties with a float or double subtypes. */
+ IDP_UI_DATA_TYPE_FLOAT = 1,
+ /** IDP_STRING properties. */
+ IDP_UI_DATA_TYPE_STRING = 2,
+ /** IDP_ID. */
+ IDP_UI_DATA_TYPE_ID = 3,
+} eIDPropertyUIDataType;
+
+bool IDP_ui_data_supported(const struct IDProperty *prop);
+eIDPropertyUIDataType IDP_ui_data_type(const struct IDProperty *prop);
+void IDP_ui_data_free(struct IDProperty *prop);
+void IDP_ui_data_free_unique_contents(struct IDPropertyUIData *ui_data,
+ eIDPropertyUIDataType type,
+ const struct IDPropertyUIData *other);
+struct IDPropertyUIData *IDP_ui_data_ensure(struct IDProperty *prop);
+struct IDPropertyUIData *IDP_ui_data_copy(const struct IDProperty *prop);
+
#ifdef __cplusplus
}
#endif
diff --git a/source/blender/blenkernel/intern/idprop.c b/source/blender/blenkernel/intern/idprop.c
index 7a88a00c44f..f7411f541b7 100644
--- a/source/blender/blenkernel/intern/idprop.c
+++ b/source/blender/blenkernel/intern/idprop.c
@@ -21,6 +21,7 @@
* \ingroup bke
*/
+#include <limits.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
@@ -274,6 +275,43 @@ void IDP_FreeArray(IDProperty *prop)
}
}
+IDPropertyUIData *IDP_ui_data_copy(const IDProperty *prop)
+{
+ IDPropertyUIData *dst_ui_data = MEM_dupallocN(prop->ui_data);
+
+ /* Copy extra type specific data. */
+ switch (IDP_ui_data_type(prop)) {
+ case IDP_UI_DATA_TYPE_STRING: {
+ const IDPropertyUIDataString *src = (const IDPropertyUIDataString *)prop->ui_data;
+ IDPropertyUIDataString *dst = (IDPropertyUIDataString *)dst_ui_data;
+ dst->default_value = MEM_dupallocN(src->default_value);
+ break;
+ }
+ case IDP_UI_DATA_TYPE_ID: {
+ break;
+ }
+ case IDP_UI_DATA_TYPE_INT: {
+ const IDPropertyUIDataInt *src = (const IDPropertyUIDataInt *)prop->ui_data;
+ IDPropertyUIDataInt *dst = (IDPropertyUIDataInt *)dst_ui_data;
+ dst->default_array = MEM_dupallocN(src->default_array);
+ break;
+ }
+ case IDP_UI_DATA_TYPE_FLOAT: {
+ const IDPropertyUIDataFloat *src = (const IDPropertyUIDataFloat *)prop->ui_data;
+ IDPropertyUIDataFloat *dst = (IDPropertyUIDataFloat *)dst_ui_data;
+ dst->default_array = MEM_dupallocN(src->default_array);
+ break;
+ }
+ case IDP_UI_DATA_TYPE_UNSUPPORTED: {
+ break;
+ }
+ }
+
+ dst_ui_data->description = MEM_dupallocN(prop->ui_data->description);
+
+ return dst_ui_data;
+}
+
static IDProperty *idp_generic_copy(const IDProperty *prop, const int UNUSED(flag))
{
IDProperty *newp = MEM_callocN(sizeof(IDProperty), __func__);
@@ -284,6 +322,10 @@ static IDProperty *idp_generic_copy(const IDProperty *prop, const int UNUSED(fla
newp->data.val = prop->data.val;
newp->data.val2 = prop->data.val2;
+ if (prop->ui_data != NULL) {
+ newp->ui_data = IDP_ui_data_copy(prop);
+ }
+
return newp;
}
@@ -679,6 +721,7 @@ bool IDP_InsertToGroup(IDProperty *group, IDProperty *previous, IDProperty *pnew
void IDP_RemoveFromGroup(IDProperty *group, IDProperty *prop)
{
BLI_assert(group->type == IDP_GROUP);
+ BLI_assert(BLI_findindex(&group->data.group, prop) != -1);
group->len--;
BLI_remlink(&group->data.group, prop);
@@ -725,6 +768,60 @@ static void IDP_FreeGroup(IDProperty *prop, const bool do_id_user)
/** \name Main Functions (IDProperty Main API)
* \{ */
+/**
+ * Return an int from an IDProperty with a compatible type. This should be avoided, but
+ * it's sometimes necessary, for example when legacy files have incorrect property types.
+ */
+int IDP_coerce_to_int_or_zero(const IDProperty *prop)
+{
+ switch (prop->type) {
+ case IDP_INT:
+ return IDP_Int(prop);
+ case IDP_DOUBLE:
+ return (int)IDP_Double(prop);
+ case IDP_FLOAT:
+ return (int)IDP_Float(prop);
+ default:
+ return 0;
+ }
+}
+
+/**
+ * Return a double from an IDProperty with a compatible type. This should be avoided, but
+ * it's sometimes necessary, for example when legacy files have incorrect property types.
+ */
+double IDP_coerce_to_double_or_zero(const IDProperty *prop)
+{
+ switch (prop->type) {
+ case IDP_DOUBLE:
+ return IDP_Double(prop);
+ case IDP_FLOAT:
+ return (double)IDP_Float(prop);
+ case IDP_INT:
+ return (double)IDP_Int(prop);
+ default:
+ return 0.0;
+ }
+}
+
+/**
+ * Return a float from an IDProperty with a compatible type. This should be avoided, but
+ * it's sometimes necessary, for example when legacy files have incorrect property types.
+ */
+float IDP_coerce_to_float_or_zero(const IDProperty *prop)
+{
+ switch (prop->type) {
+ case IDP_FLOAT:
+ return IDP_Float(prop);
+ case IDP_DOUBLE:
+ return (float)IDP_Double(prop);
+ case IDP_INT:
+ return (float)IDP_Int(prop);
+ default:
+ return 0.0f;
+ }
+}
+
IDProperty *IDP_CopyProperty_ex(const IDProperty *prop, const int flag)
{
switch (prop->type) {
@@ -1000,6 +1097,85 @@ IDProperty *IDP_New(const char type, const IDPropertyTemplate *val, const char *
}
/**
+ * Free allocated pointers in the UI data that isn't shared with the UI data in the #other
+ * argument. Useful for returning early on failure when updating UI data in place, or when
+ * replacing a subset of the UI data's allocated pointers.
+ */
+void IDP_ui_data_free_unique_contents(IDPropertyUIData *ui_data,
+ const eIDPropertyUIDataType type,
+ const IDPropertyUIData *other)
+{
+ if (ui_data->description != other->description) {
+ MEM_SAFE_FREE(ui_data->description);
+ }
+
+ switch (type) {
+ case IDP_UI_DATA_TYPE_STRING: {
+ const IDPropertyUIDataString *other_string = (const IDPropertyUIDataString *)other;
+ IDPropertyUIDataString *ui_data_string = (IDPropertyUIDataString *)ui_data;
+ if (ui_data_string->default_value != other_string->default_value) {
+ MEM_SAFE_FREE(ui_data_string->default_value);
+ }
+ break;
+ }
+ case IDP_UI_DATA_TYPE_ID: {
+ break;
+ }
+ case IDP_UI_DATA_TYPE_INT: {
+ const IDPropertyUIDataInt *other_int = (const IDPropertyUIDataInt *)other;
+ IDPropertyUIDataInt *ui_data_int = (IDPropertyUIDataInt *)ui_data;
+ if (ui_data_int->default_array != other_int->default_array) {
+ MEM_SAFE_FREE(ui_data_int->default_array);
+ }
+ break;
+ }
+ case IDP_UI_DATA_TYPE_FLOAT: {
+ const IDPropertyUIDataFloat *other_float = (const IDPropertyUIDataFloat *)other;
+ IDPropertyUIDataFloat *ui_data_float = (IDPropertyUIDataFloat *)ui_data;
+ if (ui_data_float->default_array != other_float->default_array) {
+ MEM_SAFE_FREE(ui_data_float->default_array);
+ }
+ break;
+ }
+ case IDP_UI_DATA_TYPE_UNSUPPORTED: {
+ break;
+ }
+ }
+}
+
+void IDP_ui_data_free(IDProperty *prop)
+{
+ switch (IDP_ui_data_type(prop)) {
+ case IDP_UI_DATA_TYPE_STRING: {
+ IDPropertyUIDataString *ui_data_string = (IDPropertyUIDataString *)prop->ui_data;
+ MEM_SAFE_FREE(ui_data_string->default_value);
+ break;
+ }
+ case IDP_UI_DATA_TYPE_ID: {
+ break;
+ }
+ case IDP_UI_DATA_TYPE_INT: {
+ IDPropertyUIDataInt *ui_data_int = (IDPropertyUIDataInt *)prop->ui_data;
+ MEM_SAFE_FREE(ui_data_int->default_array);
+ break;
+ }
+ case IDP_UI_DATA_TYPE_FLOAT: {
+ IDPropertyUIDataFloat *ui_data_float = (IDPropertyUIDataFloat *)prop->ui_data;
+ MEM_SAFE_FREE(ui_data_float->default_array);
+ break;
+ }
+ case IDP_UI_DATA_TYPE_UNSUPPORTED: {
+ break;
+ }
+ }
+
+ MEM_SAFE_FREE(prop->ui_data->description);
+
+ MEM_freeN(prop->ui_data);
+ prop->ui_data = NULL;
+}
+
+/**
* \note This will free allocated data, all child properties of arrays and groups, and unlink IDs!
* But it does not free the actual IDProperty struct itself.
*/
@@ -1024,6 +1200,10 @@ void IDP_FreePropertyContent_ex(IDProperty *prop, const bool do_id_user)
}
break;
}
+
+ if (prop->ui_data != NULL) {
+ IDP_ui_data_free(prop);
+ }
}
void IDP_FreePropertyContent(IDProperty *prop)
@@ -1104,6 +1284,48 @@ void IDP_foreach_property(IDProperty *id_property_root,
void IDP_WriteProperty_OnlyData(const IDProperty *prop, BlendWriter *writer);
+static void write_ui_data(const IDProperty *prop, BlendWriter *writer)
+{
+ IDPropertyUIData *ui_data = prop->ui_data;
+
+ BLO_write_string(writer, ui_data->description);
+
+ switch (IDP_ui_data_type(prop)) {
+ case IDP_UI_DATA_TYPE_STRING: {
+ IDPropertyUIDataString *ui_data_string = (IDPropertyUIDataString *)ui_data;
+ BLO_write_string(writer, ui_data_string->default_value);
+ BLO_write_struct(writer, IDPropertyUIDataString, ui_data);
+ break;
+ }
+ case IDP_UI_DATA_TYPE_ID: {
+ BLO_write_struct(writer, IDPropertyUIDataID, ui_data);
+ break;
+ }
+ case IDP_UI_DATA_TYPE_INT: {
+ IDPropertyUIDataInt *ui_data_int = (IDPropertyUIDataInt *)ui_data;
+ if (prop->type == IDP_ARRAY) {
+ BLO_write_int32_array(
+ writer, (uint)ui_data_int->default_array_len, (int32_t *)ui_data_int->default_array);
+ }
+ BLO_write_struct(writer, IDPropertyUIDataInt, ui_data);
+ break;
+ }
+ case IDP_UI_DATA_TYPE_FLOAT: {
+ IDPropertyUIDataFloat *ui_data_float = (IDPropertyUIDataFloat *)ui_data;
+ if (prop->type == IDP_ARRAY) {
+ BLO_write_double_array(
+ writer, (uint)ui_data_float->default_array_len, ui_data_float->default_array);
+ }
+ BLO_write_struct(writer, IDPropertyUIDataFloat, ui_data);
+ break;
+ }
+ case IDP_UI_DATA_TYPE_UNSUPPORTED: {
+ BLI_assert_unreachable();
+ break;
+ }
+ }
+}
+
static void IDP_WriteArray(const IDProperty *prop, BlendWriter *writer)
{
/* Remember to set #IDProperty.totallen to len in the linking code! */
@@ -1165,6 +1387,9 @@ void IDP_WriteProperty_OnlyData(const IDProperty *prop, BlendWriter *writer)
IDP_WriteIDPArray(prop, writer);
break;
}
+ if (prop->ui_data != NULL) {
+ write_ui_data(prop, writer);
+ }
}
void IDP_BlendWrite(BlendWriter *writer, const IDProperty *prop)
@@ -1175,6 +1400,43 @@ void IDP_BlendWrite(BlendWriter *writer, const IDProperty *prop)
static void IDP_DirectLinkProperty(IDProperty *prop, BlendDataReader *reader);
+static void read_ui_data(IDProperty *prop, BlendDataReader *reader)
+{
+ BLO_read_data_address(reader, &prop->ui_data);
+ BLO_read_data_address(reader, &prop->ui_data->description);
+
+ switch (IDP_ui_data_type(prop)) {
+ case IDP_UI_DATA_TYPE_STRING: {
+ IDPropertyUIDataString *ui_data_string = (IDPropertyUIDataString *)prop->ui_data;
+ BLO_read_data_address(reader, &ui_data_string->default_value);
+ break;
+ }
+ case IDP_UI_DATA_TYPE_ID: {
+ break;
+ }
+ case IDP_UI_DATA_TYPE_INT: {
+ IDPropertyUIDataInt *ui_data_int = (IDPropertyUIDataInt *)prop->ui_data;
+ if (prop->type == IDP_ARRAY) {
+ BLO_read_int32_array(
+ reader, ui_data_int->default_array_len, (int **)&ui_data_int->default_array);
+ }
+ break;
+ }
+ case IDP_UI_DATA_TYPE_FLOAT: {
+ IDPropertyUIDataFloat *ui_data_float = (IDPropertyUIDataFloat *)prop->ui_data;
+ if (prop->type == IDP_ARRAY) {
+ BLO_read_double_array(
+ reader, ui_data_float->default_array_len, (double **)&ui_data_float->default_array);
+ }
+ break;
+ }
+ case IDP_UI_DATA_TYPE_UNSUPPORTED: {
+ BLI_assert_unreachable();
+ break;
+ }
+ }
+}
+
static void IDP_DirectLinkIDPArray(IDProperty *prop, BlendDataReader *reader)
{
/* since we didn't save the extra buffer, set totallen to len */
@@ -1279,6 +1541,10 @@ static void IDP_DirectLinkProperty(IDProperty *prop, BlendDataReader *reader)
prop->subtype = 0;
IDP_Int(prop) = 0;
}
+
+ if (prop->ui_data != NULL) {
+ read_ui_data(prop, reader);
+ }
}
void IDP_BlendReadData_impl(BlendDataReader *reader, IDProperty **prop, const char *caller_func_id)
@@ -1358,4 +1624,74 @@ void IDP_BlendReadExpand(struct BlendExpander *expander, IDProperty *prop)
}
}
+eIDPropertyUIDataType IDP_ui_data_type(const IDProperty *prop)
+{
+ if (prop->type == IDP_STRING) {
+ return IDP_UI_DATA_TYPE_STRING;
+ }
+ if (prop->type == IDP_ID) {
+ return IDP_UI_DATA_TYPE_ID;
+ }
+ if (prop->type == IDP_INT || (prop->type == IDP_ARRAY && prop->subtype == IDP_INT)) {
+ return IDP_UI_DATA_TYPE_INT;
+ }
+ if (ELEM(prop->type, IDP_FLOAT, IDP_DOUBLE) ||
+ (prop->type == IDP_ARRAY && ELEM(prop->subtype, IDP_FLOAT, IDP_DOUBLE))) {
+ return IDP_UI_DATA_TYPE_FLOAT;
+ }
+ return IDP_UI_DATA_TYPE_UNSUPPORTED;
+}
+
+bool IDP_ui_data_supported(const IDProperty *prop)
+{
+ return IDP_ui_data_type(prop) != IDP_UI_DATA_TYPE_UNSUPPORTED;
+}
+
+IDPropertyUIData *IDP_ui_data_ensure(IDProperty *prop)
+{
+ if (prop->ui_data != NULL) {
+ return prop->ui_data;
+ }
+
+ switch (IDP_ui_data_type(prop)) {
+ case IDP_UI_DATA_TYPE_STRING: {
+ prop->ui_data = MEM_callocN(sizeof(IDPropertyUIDataString), __func__);
+ break;
+ }
+ case IDP_UI_DATA_TYPE_ID: {
+ IDPropertyUIDataID *ui_data = MEM_callocN(sizeof(IDPropertyUIDataID), __func__);
+ prop->ui_data = (IDPropertyUIData *)ui_data;
+ break;
+ }
+ case IDP_UI_DATA_TYPE_INT: {
+ IDPropertyUIDataInt *ui_data = MEM_callocN(sizeof(IDPropertyUIDataInt), __func__);
+ ui_data->min = INT_MIN;
+ ui_data->max = INT_MAX;
+ ui_data->soft_min = INT_MIN;
+ ui_data->soft_max = INT_MAX;
+ ui_data->step = 1;
+ prop->ui_data = (IDPropertyUIData *)ui_data;
+ break;
+ }
+ case IDP_UI_DATA_TYPE_FLOAT: {
+ IDPropertyUIDataFloat *ui_data = MEM_callocN(sizeof(IDPropertyUIDataFloat), __func__);
+ ui_data->min = -FLT_MAX;
+ ui_data->max = FLT_MAX;
+ ui_data->soft_min = -FLT_MAX;
+ ui_data->soft_max = FLT_MAX;
+ ui_data->step = 1.0f;
+ ui_data->precision = 3;
+ prop->ui_data = (IDPropertyUIData *)ui_data;
+ break;
+ }
+ case IDP_UI_DATA_TYPE_UNSUPPORTED: {
+ /* UI data not supported for remaining types, this shouldn't be called in those cases. */
+ BLI_assert_unreachable();
+ break;
+ }
+ }
+
+ return prop->ui_data;
+}
+
/** \} */
diff --git a/source/blender/blenkernel/intern/object_dupli.cc b/source/blender/blenkernel/intern/object_dupli.cc
index 141a9a25eca..a46ac4b1175 100644
--- a/source/blender/blenkernel/intern/object_dupli.cc
+++ b/source/blender/blenkernel/intern/object_dupli.cc
@@ -653,10 +653,10 @@ static Object *find_family_object(
return *ob_pt;
}
- char ch_utf8[7];
+ char ch_utf8[BLI_UTF8_MAX + 1];
size_t ch_utf8_len;
- ch_utf8_len = BLI_str_utf8_from_unicode(ch, ch_utf8);
+ ch_utf8_len = BLI_str_utf8_from_unicode(ch, ch_utf8, sizeof(ch_utf8) - 1);
ch_utf8[ch_utf8_len] = '\0';
ch_utf8_len += 1; /* Compare with null terminator. */
diff --git a/source/blender/blenkernel/intern/text.c b/source/blender/blenkernel/intern/text.c
index 7f1f6590e48..6b7b3213a83 100644
--- a/source/blender/blenkernel/intern/text.c
+++ b/source/blender/blenkernel/intern/text.c
@@ -329,7 +329,8 @@ int txt_extended_ascii_as_utf8(char **str)
memcpy(newstr + mi, (*str) + i, bad_char);
- BLI_str_utf8_from_unicode((*str)[i + bad_char], newstr + mi + bad_char);
+ const int mofs = mi + bad_char;
+ BLI_str_utf8_from_unicode((*str)[i + bad_char], newstr + mofs, (length + added) - mofs);
i += bad_char + 1;
mi += bad_char + 2;
}
@@ -933,7 +934,7 @@ void txt_move_left(Text *text, const bool sel)
(*charp) -= tabsize;
}
else {
- const char *prev = BLI_str_prev_char_utf8((*linep)->line + *charp);
+ const char *prev = BLI_str_find_prev_char_utf8((*linep)->line + *charp, (*linep)->line);
*charp = prev - (*linep)->line;
}
}
@@ -1938,7 +1939,8 @@ void txt_backspace_char(Text *text)
txt_pop_sel(text);
}
else { /* Just backspacing a char */
- const char *prev = BLI_str_prev_char_utf8(text->curl->line + text->curc);
+ const char *prev = BLI_str_find_prev_char_utf8(text->curl->line + text->curc,
+ text->curl->line);
size_t c_len = prev - text->curl->line;
c = BLI_str_utf8_as_unicode_step(text->curl->line, text->curl->len, &c_len);
c_len -= prev - text->curl->line;
@@ -2004,7 +2006,7 @@ static bool txt_add_char_intern(Text *text, unsigned int add, bool replace_tabs)
txt_delete_sel(text);
- add_len = BLI_str_utf8_from_unicode(add, ch);
+ add_len = BLI_str_utf8_from_unicode(add, ch, sizeof(ch));
tmp = MEM_mallocN(text->curl->len + add_len + 1, "textline_string");
@@ -2060,7 +2062,7 @@ bool txt_replace_char(Text *text, unsigned int add)
del = BLI_str_utf8_as_unicode_step(text->curl->line, text->curl->len, &del_size);
del_size -= text->curc;
UNUSED_VARS(del);
- add_size = BLI_str_utf8_from_unicode(add, ch);
+ add_size = BLI_str_utf8_from_unicode(add, ch, sizeof(ch));
if (add_size > del_size) {
char *tmp = MEM_mallocN(text->curl->len + add_size - del_size + 1, "textline_string");
diff --git a/source/blender/blenkernel/intern/unit.c b/source/blender/blenkernel/intern/unit.c
index 4581d410444..4e9a3c9fb2e 100644
--- a/source/blender/blenkernel/intern/unit.c
+++ b/source/blender/blenkernel/intern/unit.c
@@ -717,7 +717,7 @@ static const char *unit_find_str(const char *str, const char *substr, bool case_
if (str_found == str ||
/* Weak unicode support!, so "µm" won't match up be replaced by "m"
* since non ascii utf8 values will NEVER return true */
- isalpha_or_utf8(*BLI_str_prev_char_utf8(str_found)) == 0) {
+ isalpha_or_utf8(*BLI_str_find_prev_char_utf8(str_found, str)) == 0) {
/* Next char cannot be alphanum. */
int len_name = strlen(substr);
diff --git a/source/blender/blenlib/BLI_string_utf8.h b/source/blender/blenlib/BLI_string_utf8.h
index a9cb13a3277..3b7463affc0 100644
--- a/source/blender/blenlib/BLI_string_utf8.h
+++ b/source/blender/blenlib/BLI_string_utf8.h
@@ -48,7 +48,9 @@ unsigned int BLI_str_utf8_as_unicode_step_or_error(
const char *__restrict p, size_t p_len, size_t *__restrict index) ATTR_WARN_UNUSED_RESULT
ATTR_NONNULL(1, 3);
-size_t BLI_str_utf8_from_unicode(unsigned int c, char *outbuf);
+size_t BLI_str_utf8_from_unicode_len(unsigned int c) ATTR_WARN_UNUSED_RESULT;
+size_t BLI_str_utf8_from_unicode(unsigned int c, char *outbuf, const size_t outbuf_len)
+ ATTR_NONNULL(2);
size_t BLI_str_utf8_as_utf32(char32_t *__restrict dst_w,
const char *__restrict src_c,
const size_t maxncpy) ATTR_NONNULL(1, 2);
@@ -57,11 +59,10 @@ size_t BLI_str_utf32_as_utf8(char *__restrict dst,
const size_t maxncpy) ATTR_NONNULL(1, 2);
size_t BLI_str_utf32_as_utf8_len(const char32_t *src) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1);
-char *BLI_str_find_prev_char_utf8(const char *str, const char *p) ATTR_WARN_UNUSED_RESULT
- ATTR_NONNULL(1, 2);
-char *BLI_str_find_next_char_utf8(const char *p, const char *end) ATTR_WARN_UNUSED_RESULT
- ATTR_NONNULL(1);
-char *BLI_str_prev_char_utf8(const char *p) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1);
+const char *BLI_str_find_prev_char_utf8(const char *p, const char *str_start)
+ ATTR_WARN_UNUSED_RESULT ATTR_RETURNS_NONNULL ATTR_NONNULL(1, 2);
+const char *BLI_str_find_next_char_utf8(const char *p, const char *str_end)
+ ATTR_WARN_UNUSED_RESULT ATTR_RETURNS_NONNULL ATTR_NONNULL(1, 2);
/* wchar_t functions, copied from blenders own font.c originally */
size_t BLI_wstrlen_utf8(const wchar_t *src) ATTR_NONNULL(1) ATTR_WARN_UNUSED_RESULT;
diff --git a/source/blender/blenlib/intern/string_cursor_utf8.c b/source/blender/blenlib/intern/string_cursor_utf8.c
index f76a3114e09..eb49572f06c 100644
--- a/source/blender/blenlib/intern/string_cursor_utf8.c
+++ b/source/blender/blenlib/intern/string_cursor_utf8.c
@@ -117,7 +117,7 @@ bool BLI_str_cursor_step_next_utf8(const char *str, size_t maxlen, int *pos)
const char *str_end = str + (maxlen + 1);
const char *str_pos = str + (*pos);
const char *str_next = BLI_str_find_next_char_utf8(str_pos, str_end);
- if (str_next) {
+ if (str_next != str_end) {
(*pos) += (str_next - str_pos);
if ((*pos) > (int)maxlen) {
(*pos) = (int)maxlen;
@@ -132,11 +132,9 @@ bool BLI_str_cursor_step_prev_utf8(const char *str, size_t UNUSED(maxlen), int *
{
if ((*pos) > 0) {
const char *str_pos = str + (*pos);
- const char *str_prev = BLI_str_find_prev_char_utf8(str, str_pos);
- if (str_prev) {
- (*pos) -= (str_pos - str_prev);
- return true;
- }
+ const char *str_prev = BLI_str_find_prev_char_utf8(str_pos, str);
+ (*pos) -= (str_pos - str_prev);
+ return true;
}
return false;
diff --git a/source/blender/blenlib/intern/string_utf8.c b/source/blender/blenlib/intern/string_utf8.c
index e35e2bcca3c..3a5e2713b76 100644
--- a/source/blender/blenlib/intern/string_utf8.c
+++ b/source/blender/blenlib/intern/string_utf8.c
@@ -296,36 +296,19 @@ size_t BLI_strncpy_wchar_as_utf8(char *__restrict dst,
const wchar_t *__restrict src,
const size_t maxncpy)
{
- const size_t maxlen = maxncpy - 1;
- /* #BLI_UTF8_MAX is max utf8 length of an unicode char. */
- const int64_t maxlen_secured = (int64_t)maxlen - BLI_UTF8_MAX;
- size_t len = 0;
-
BLI_assert(maxncpy != 0);
-
+ size_t len = 0;
#ifdef DEBUG_STRSIZE
memset(dst, 0xff, sizeof(*dst) * maxncpy);
#endif
-
- while (*src && len <= maxlen_secured) {
- len += BLI_str_utf8_from_unicode((uint)*src++, dst + len);
- }
-
- /* We have to be more careful for the last six bytes,
- * to avoid buffer overflow in case utf8-encoded char would be too long for our dst buffer. */
- while (*src) {
- char t[BLI_UTF8_MAX];
- size_t l = BLI_str_utf8_from_unicode((uint)*src++, t);
- BLI_assert(l <= BLI_UTF8_MAX);
- if (len + l > maxlen) {
- break;
- }
- memcpy(dst + len, t, l);
- len += l;
+ while (*src && len < maxncpy) {
+ len += BLI_str_utf8_from_unicode((uint)*src++, dst + len, maxncpy - len);
}
-
dst[len] = '\0';
-
+ /* Return the correct length when part of the final byte did not fit into the string. */
+ while ((len > 0) && UNLIKELY(dst[len - 1] == '\0')) {
+ len--;
+ }
return len;
}
@@ -335,7 +318,7 @@ size_t BLI_wstrlen_utf8(const wchar_t *src)
size_t len = 0;
while (*src) {
- len += BLI_str_utf8_from_unicode((uint)*src++, NULL);
+ len += BLI_str_utf8_from_unicode_len((uint)*src++);
}
return len;
@@ -608,56 +591,78 @@ uint BLI_str_utf8_as_unicode_step(const char *__restrict p,
}
/* was g_unichar_to_utf8 */
+
+#define UTF8_VARS_FROM_CHAR32(Char, First, Len) \
+ if (Char < 0x80) { \
+ First = 0; \
+ Len = 1; \
+ } \
+ else if (Char < 0x800) { \
+ First = 0xc0; \
+ Len = 2; \
+ } \
+ else if (Char < 0x10000) { \
+ First = 0xe0; \
+ Len = 3; \
+ } \
+ else if (Char < 0x200000) { \
+ First = 0xf0; \
+ Len = 4; \
+ } \
+ else if (Char < 0x4000000) { \
+ First = 0xf8; \
+ Len = 5; \
+ } \
+ else { \
+ First = 0xfc; \
+ Len = 6; \
+ } \
+ (void)0
+
+size_t BLI_str_utf8_from_unicode_len(const uint c)
+{
+ /* If this gets modified, also update the copy in g_string_insert_unichar() */
+ uint len = 0;
+ uint first;
+
+ UTF8_VARS_FROM_CHAR32(c, first, len);
+ (void)first;
+
+ return len;
+}
+
/**
* BLI_str_utf8_from_unicode:
+ *
* \param c: a Unicode character code
- * \param outbuf: output buffer, must have at least 6 bytes of space.
- * If %NULL, the length will be computed and returned
- * and nothing will be written to outbuf.
+ * \param outbuf: output buffer, must have at least `outbuf_len` bytes of space.
+ * If the length required by `c` exceeds `outbuf_len`,
+ * the bytes available bytes will be zeroed and `outbuf_len` returned.
*
* Converts a single character to UTF-8.
*
- * \return number of bytes written
+ * \return number of bytes written.
*/
-size_t BLI_str_utf8_from_unicode(uint c, char *outbuf)
+size_t BLI_str_utf8_from_unicode(uint c, char *outbuf, const size_t outbuf_len)
+
{
/* If this gets modified, also update the copy in g_string_insert_unichar() */
uint len = 0;
uint first;
- uint i;
- if (c < 0x80) {
- first = 0;
- len = 1;
- }
- else if (c < 0x800) {
- first = 0xc0;
- len = 2;
- }
- else if (c < 0x10000) {
- first = 0xe0;
- len = 3;
- }
- else if (c < 0x200000) {
- first = 0xf0;
- len = 4;
- }
- else if (c < 0x4000000) {
- first = 0xf8;
- len = 5;
- }
- else {
- first = 0xfc;
- len = 6;
+ UTF8_VARS_FROM_CHAR32(c, first, len);
+
+ if (UNLIKELY(outbuf_len < len)) {
+ /* NULL terminate instead of writing a partial byte. */
+ memset(outbuf, 0x0, outbuf_len);
+ return outbuf_len;
}
- if (outbuf) {
- for (i = len - 1; i > 0; i--) {
- outbuf[i] = (c & 0x3f) | 0x80;
- c >>= 6;
- }
- outbuf[0] = c | first;
+ for (uint i = len - 1; i > 0; i--) {
+ outbuf[i] = (c & 0x3f) | 0x80;
+ c >>= 6;
}
+ outbuf[0] = c | first;
return len;
}
@@ -686,12 +691,7 @@ size_t BLI_str_utf8_as_utf32(char32_t *__restrict dst_w,
else {
*dst_w = '?';
const char *src_c_next = BLI_str_find_next_char_utf8(src_c + index, src_c_end);
- if (src_c_next != NULL) {
- index = (size_t)(src_c_next - src_c);
- }
- else {
- index += 1;
- }
+ index = (size_t)(src_c_next - src_c);
}
dst_w++;
len++;
@@ -706,36 +706,19 @@ size_t BLI_str_utf32_as_utf8(char *__restrict dst,
const char32_t *__restrict src,
const size_t maxncpy)
{
- const size_t maxlen = maxncpy - 1;
- /* #BLI_UTF8_MAX is max utf8 length of an unicode char. */
- const int64_t maxlen_secured = (int64_t)maxlen - BLI_UTF8_MAX;
- size_t len = 0;
-
BLI_assert(maxncpy != 0);
-
+ size_t len = 0;
#ifdef DEBUG_STRSIZE
memset(dst, 0xff, sizeof(*dst) * maxncpy);
#endif
-
- while (*src && len <= maxlen_secured) {
- len += BLI_str_utf8_from_unicode((uint)*src++, dst + len);
- }
-
- /* We have to be more careful for the last six bytes,
- * to avoid buffer overflow in case utf8-encoded char would be too long for our dst buffer. */
- while (*src) {
- char t[BLI_UTF8_MAX];
- size_t l = BLI_str_utf8_from_unicode((uint)*src++, t);
- BLI_assert(l <= BLI_UTF8_MAX);
- if (len + l > maxlen) {
- break;
- }
- memcpy(dst + len, t, l);
- len += l;
+ while (*src && len < maxncpy) {
+ len += BLI_str_utf8_from_unicode((uint)*src++, dst + len, maxncpy - len);
}
-
dst[len] = '\0';
-
+ /* Return the correct length when part of the final byte did not fit into the string. */
+ while ((len > 0) && UNLIKELY(dst[len - 1] == '\0')) {
+ len--;
+ }
return len;
}
@@ -745,7 +728,7 @@ size_t BLI_str_utf32_as_utf8_len(const char32_t *src)
size_t len = 0;
while (*src) {
- len += BLI_str_utf8_from_unicode((uint)*src++, NULL);
+ len += BLI_str_utf8_from_unicode_len((uint)*src++);
}
return len;
@@ -758,31 +741,33 @@ size_t BLI_str_utf32_as_utf8_len(const char32_t *src)
* \param p: pointer to some position within \a str
*
* Given a position \a p with a UTF-8 encoded string \a str, find the start
- * of the previous UTF-8 character starting before. \a p Returns %NULL if no
- * UTF-8 characters are present in \a str before \a p
+ * of the previous UTF-8 character starting before. \a p Returns \a str_start if no
+ * UTF-8 characters are present in \a str_start before \a p.
*
* \a p does not have to be at the beginning of a UTF-8 character. No check
* is made to see if the character found is actually valid other than
* it starts with an appropriate byte.
*
- * Return value: a pointer to the found character or %NULL.
+ * \return A pointer to the found character.
*/
-char *BLI_str_find_prev_char_utf8(const char *str, const char *p)
+const char *BLI_str_find_prev_char_utf8(const char *p, const char *str_start)
{
- for (--p; p >= str; p--) {
- if ((*p & 0xc0) != 0x80) {
- return (char *)p;
+ BLI_assert(p >= str_start);
+ if (str_start < p) {
+ for (--p; p >= str_start; p--) {
+ if ((*p & 0xc0) != 0x80) {
+ return (char *)p;
+ }
}
}
- return NULL;
+ return p;
}
/* was g_utf8_find_next_char */
/**
* BLI_str_find_next_char_utf8:
* \param p: a pointer to a position within a UTF-8 encoded string
- * \param end: a pointer to the byte following the end of the string,
- * or %NULL to indicate that the string is nul-terminated.
+ * \param end: a pointer to the byte following the end of the string.
*
* Finds the start of the next UTF-8 character in the string after \a p
*
@@ -790,50 +775,18 @@ char *BLI_str_find_prev_char_utf8(const char *str, const char *p)
* is made to see if the character found is actually valid other than
* it starts with an appropriate byte.
*
- * Return value: a pointer to the found character or %NULL
- */
-char *BLI_str_find_next_char_utf8(const char *p, const char *end)
-{
- if (*p) {
- if (end) {
- BLI_assert(end >= p);
- for (++p; p < end && (*p & 0xc0) == 0x80; p++) {
- /* do nothing */
- }
- }
- else {
- for (++p; (*p & 0xc0) == 0x80; p++) {
- /* do nothing */
- }
- }
- }
- return (p == end) ? NULL : (char *)p;
-}
-
-/* was g_utf8_prev_char */
-/**
- * BLI_str_prev_char_utf8:
- * \param p: a pointer to a position within a UTF-8 encoded string
- *
- * Finds the previous UTF-8 character in the string before \a p
- *
- * \a p does not have to be at the beginning of a UTF-8 character. No check
- * is made to see if the character found is actually valid other than
- * it starts with an appropriate byte. If \a p might be the first
- * character of the string, you must use g_utf8_find_prev_char() instead.
- *
- * Return value: a pointer to the found character.
+ * \return a pointer to the found character or a pointer to the null terminating character '\0'.
*/
-char *BLI_str_prev_char_utf8(const char *p)
+const char *BLI_str_find_next_char_utf8(const char *p, const char *str_end)
{
- while (1) {
- p--;
- if ((*p & 0xc0) != 0x80) {
- return (char *)p;
+ BLI_assert(p <= str_end);
+ if ((p < str_end) && (*p != '\0')) {
+ for (++p; p < str_end && (*p & 0xc0) == 0x80; p++) {
+ /* do nothing */
}
}
+ return p;
}
-/* end glib copy */
size_t BLI_str_partition_utf8(const char *str,
const uint delim[],
@@ -858,19 +811,21 @@ size_t BLI_str_partition_ex_utf8(const char *str,
const char **suf,
const bool from_right)
{
- const uint *d;
const size_t str_len = end ? (size_t)(end - str) : strlen(str);
- size_t index;
+ if (end == NULL) {
+ end = str + str_len;
+ }
/* Note that here, we assume end points to a valid utf8 char! */
- BLI_assert(end == NULL || (end >= str && (BLI_str_utf8_as_unicode(end) != BLI_UTF8_ERR)));
+ BLI_assert((end >= str) && (BLI_str_utf8_as_unicode(end) != BLI_UTF8_ERR));
*suf = (char *)(str + str_len);
- for (*sep = (char *)(from_right ? BLI_str_find_prev_char_utf8(str, str + str_len) : str),
- index = 0;
- *sep >= str && (!end || *sep < end) && **sep != '\0';
- *sep = (char *)(from_right ? BLI_str_find_prev_char_utf8(str, *sep) : str + index)) {
+ size_t index;
+ for (*sep = (char *)(from_right ? BLI_str_find_prev_char_utf8(end, str) : str), index = 0;
+ from_right ? (*sep > str) : ((*sep < end) && (**sep != '\0'));
+ *sep = (char *)(from_right ? (str != *sep ? BLI_str_find_prev_char_utf8(*sep, str) : NULL) :
+ str + index)) {
size_t index_ofs = 0;
const uint c = BLI_str_utf8_as_unicode_step_or_error(*sep, (size_t)(end - *sep), &index_ofs);
index += index_ofs;
@@ -880,7 +835,7 @@ size_t BLI_str_partition_ex_utf8(const char *str,
break;
}
- for (d = delim; *d != '\0'; d++) {
+ for (const uint *d = delim; *d != '\0'; d++) {
if (*d == c) {
/* *suf is already correct in case from_right is true. */
if (!from_right) {
diff --git a/source/blender/blenlib/tests/BLI_span_test.cc b/source/blender/blenlib/tests/BLI_span_test.cc
index 5e3c6a1b680..4d23a53c08a 100644
--- a/source/blender/blenlib/tests/BLI_span_test.cc
+++ b/source/blender/blenlib/tests/BLI_span_test.cc
@@ -235,7 +235,7 @@ TEST(span, FillIndices)
TEST(span, SizeInBytes)
{
- std::array<int, 10> a;
+ std::array<int, 10> a{};
Span<int> a_span(a);
EXPECT_EQ(a_span.size_in_bytes(), static_cast<int64_t>(sizeof(a)));
EXPECT_EQ(a_span.size_in_bytes(), 40);
diff --git a/source/blender/blenloader/intern/versioning_300.c b/source/blender/blenloader/intern/versioning_300.c
index 72572b05ef6..2456b22f046 100644
--- a/source/blender/blenloader/intern/versioning_300.c
+++ b/source/blender/blenloader/intern/versioning_300.c
@@ -20,6 +20,10 @@
/* allow readfile to use deprecated functionality */
#define DNA_DEPRECATED_ALLOW
+#include <string.h>
+
+#include "MEM_guardedalloc.h"
+
#include "BLI_listbase.h"
#include "BLI_math_vector.h"
#include "BLI_path_util.h"
@@ -46,10 +50,14 @@
#include "BKE_deform.h"
#include "BKE_fcurve.h"
#include "BKE_fcurve_driver.h"
+#include "BKE_idprop.h"
#include "BKE_lib_id.h"
#include "BKE_main.h"
#include "BKE_node.h"
+#include "RNA_access.h"
+#include "RNA_enum_types.h"
+
#include "BLO_readfile.h"
#include "MEM_guardedalloc.h"
#include "readfile.h"
@@ -60,6 +68,235 @@
#include "versioning_common.h"
+static IDProperty *idproperty_find_ui_container(IDProperty *idprop_group)
+{
+ LISTBASE_FOREACH (IDProperty *, prop, &idprop_group->data.group) {
+ if (prop->type == IDP_GROUP && STREQ(prop->name, "_RNA_UI")) {
+ return prop;
+ }
+ }
+ return NULL;
+}
+
+static void version_idproperty_move_data_int(IDPropertyUIDataInt *ui_data,
+ const IDProperty *prop_ui_data)
+{
+ IDProperty *min = IDP_GetPropertyFromGroup(prop_ui_data, "min");
+ if (min != NULL) {
+ ui_data->min = ui_data->soft_min = IDP_coerce_to_int_or_zero(min);
+ }
+ IDProperty *max = IDP_GetPropertyFromGroup(prop_ui_data, "max");
+ if (max != NULL) {
+ ui_data->max = ui_data->soft_max = IDP_coerce_to_int_or_zero(max);
+ }
+ IDProperty *soft_min = IDP_GetPropertyFromGroup(prop_ui_data, "soft_min");
+ if (soft_min != NULL) {
+ ui_data->soft_min = IDP_coerce_to_int_or_zero(soft_min);
+ ui_data->soft_min = MIN2(ui_data->soft_min, ui_data->min);
+ }
+ IDProperty *soft_max = IDP_GetPropertyFromGroup(prop_ui_data, "soft_max");
+ if (soft_max != NULL) {
+ ui_data->soft_max = IDP_coerce_to_int_or_zero(soft_max);
+ ui_data->soft_max = MAX2(ui_data->soft_max, ui_data->max);
+ }
+ IDProperty *step = IDP_GetPropertyFromGroup(prop_ui_data, "step");
+ if (step != NULL) {
+ ui_data->step = IDP_coerce_to_int_or_zero(soft_max);
+ }
+ IDProperty *default_value = IDP_GetPropertyFromGroup(prop_ui_data, "default");
+ if (default_value != NULL) {
+ if (default_value->type == IDP_ARRAY) {
+ if (default_value->subtype == IDP_INT) {
+ ui_data->default_array = MEM_dupallocN(IDP_Array(default_value));
+ ui_data->default_array_len = default_value->len;
+ }
+ }
+ else if (default_value->type == IDP_INT) {
+ ui_data->default_value = IDP_coerce_to_int_or_zero(default_value);
+ }
+ }
+}
+
+static void version_idproperty_move_data_float(IDPropertyUIDataFloat *ui_data,
+ const IDProperty *prop_ui_data)
+{
+ IDProperty *min = IDP_GetPropertyFromGroup(prop_ui_data, "min");
+ if (min != NULL) {
+ ui_data->min = ui_data->soft_min = IDP_coerce_to_double_or_zero(min);
+ }
+ IDProperty *max = IDP_GetPropertyFromGroup(prop_ui_data, "max");
+ if (max != NULL) {
+ ui_data->max = ui_data->soft_max = IDP_coerce_to_double_or_zero(min);
+ }
+ IDProperty *soft_min = IDP_GetPropertyFromGroup(prop_ui_data, "soft_min");
+ if (soft_min != NULL) {
+ ui_data->soft_min = IDP_coerce_to_double_or_zero(soft_min);
+ ui_data->soft_min = MAX2(ui_data->soft_min, ui_data->min);
+ }
+ IDProperty *soft_max = IDP_GetPropertyFromGroup(prop_ui_data, "soft_max");
+ if (soft_max != NULL) {
+ ui_data->soft_max = IDP_coerce_to_double_or_zero(soft_max);
+ ui_data->soft_max = MIN2(ui_data->soft_max, ui_data->max);
+ }
+ IDProperty *step = IDP_GetPropertyFromGroup(prop_ui_data, "step");
+ if (step != NULL) {
+ ui_data->step = IDP_coerce_to_float_or_zero(step);
+ }
+ IDProperty *precision = IDP_GetPropertyFromGroup(prop_ui_data, "precision");
+ if (precision != NULL) {
+ ui_data->precision = IDP_coerce_to_int_or_zero(precision);
+ }
+ IDProperty *default_value = IDP_GetPropertyFromGroup(prop_ui_data, "default");
+ if (default_value != NULL) {
+ if (default_value->type == IDP_ARRAY) {
+ if (ELEM(default_value->subtype, IDP_FLOAT, IDP_DOUBLE)) {
+ ui_data->default_array = MEM_dupallocN(IDP_Array(default_value));
+ ui_data->default_array_len = default_value->len;
+ }
+ }
+ else if (ELEM(default_value->type, IDP_DOUBLE, IDP_FLOAT)) {
+ ui_data->default_value = IDP_coerce_to_double_or_zero(default_value);
+ }
+ }
+}
+
+static void version_idproperty_move_data_string(IDPropertyUIDataString *ui_data,
+ const IDProperty *prop_ui_data)
+{
+ IDProperty *default_value = IDP_GetPropertyFromGroup(prop_ui_data, "default");
+ if (default_value != NULL && default_value->type == IDP_STRING) {
+ ui_data->default_value = BLI_strdup(IDP_String(default_value));
+ }
+}
+
+static void version_idproperty_ui_data(IDProperty *idprop_group)
+{
+ if (idprop_group == NULL) { /* NULL check here to reduce verbosity of calls to this function. */
+ return;
+ }
+
+ IDProperty *ui_container = idproperty_find_ui_container(idprop_group);
+ if (ui_container == NULL) {
+ return;
+ }
+
+ LISTBASE_FOREACH (IDProperty *, prop, &idprop_group->data.group) {
+ IDProperty *prop_ui_data = IDP_GetPropertyFromGroup(ui_container, prop->name);
+ if (prop_ui_data == NULL) {
+ continue;
+ }
+
+ if (!IDP_ui_data_supported(prop)) {
+ continue;
+ }
+
+ IDPropertyUIData *ui_data = IDP_ui_data_ensure(prop);
+
+ IDProperty *subtype = IDP_GetPropertyFromGroup(prop_ui_data, "subtype");
+ if (subtype != NULL && subtype->type == IDP_STRING) {
+ const char *subtype_string = IDP_String(subtype);
+ int result = PROP_NONE;
+ RNA_enum_value_from_id(rna_enum_property_subtype_items, subtype_string, &result);
+ ui_data->rna_subtype = result;
+ }
+
+ IDProperty *description = IDP_GetPropertyFromGroup(prop_ui_data, "description");
+ if (description != NULL && description->type == IDP_STRING) {
+ ui_data->description = BLI_strdup(IDP_String(description));
+ }
+
+ /* Type specific data. */
+ switch (IDP_ui_data_type(prop)) {
+ case IDP_UI_DATA_TYPE_STRING:
+ version_idproperty_move_data_string((IDPropertyUIDataString *)ui_data, prop_ui_data);
+ break;
+ case IDP_UI_DATA_TYPE_ID:
+ break;
+ case IDP_UI_DATA_TYPE_INT:
+ version_idproperty_move_data_int((IDPropertyUIDataInt *)ui_data, prop_ui_data);
+ break;
+ case IDP_UI_DATA_TYPE_FLOAT:
+ version_idproperty_move_data_float((IDPropertyUIDataFloat *)ui_data, prop_ui_data);
+ break;
+ case IDP_UI_DATA_TYPE_UNSUPPORTED:
+ BLI_assert_unreachable();
+ break;
+ }
+
+ IDP_FreeFromGroup(ui_container, prop_ui_data);
+ }
+
+ IDP_FreeFromGroup(idprop_group, ui_container);
+}
+
+static void do_versions_idproperty_bones_recursive(Bone *bone)
+{
+ version_idproperty_ui_data(bone->prop);
+ LISTBASE_FOREACH (Bone *, child_bone, &bone->childbase) {
+ do_versions_idproperty_bones_recursive(child_bone);
+ }
+}
+
+/**
+ * For every data block that supports them, initialize the new IDProperty UI data struct based on
+ * the old more complicated storage. Assumes only the top level of IDProperties below the parent
+ * group had UI data in a "_RNA_UI" group.
+ *
+ * \note The following IDProperty groups in DNA aren't exposed in the UI or are runtime-only, so
+ * they don't have UI data: wmOperator, bAddon, bUserMenuItem_Op, wmKeyMapItem, wmKeyConfigPref,
+ * uiList, FFMpegCodecData, View3DShading, bToolRef, TimeMarker, ViewLayer, bPoseChannel.
+ */
+static void do_versions_idproperty_ui_data(Main *bmain)
+{
+ /* ID data. */
+ ID *id;
+ FOREACH_MAIN_ID_BEGIN (bmain, id) {
+ IDProperty *idprop_group = IDP_GetProperties(id, false);
+ version_idproperty_ui_data(idprop_group);
+ }
+ FOREACH_MAIN_ID_END;
+
+ /* Bones. */
+ LISTBASE_FOREACH (bArmature *, armature, &bmain->armatures) {
+ LISTBASE_FOREACH (Bone *, bone, &armature->bonebase) {
+ do_versions_idproperty_bones_recursive(bone);
+ }
+ }
+
+ /* Nodes and node sockets. */
+ LISTBASE_FOREACH (bNodeTree *, ntree, &bmain->nodetrees) {
+ LISTBASE_FOREACH (bNode *, node, &ntree->nodes) {
+ version_idproperty_ui_data(node->prop);
+ }
+ LISTBASE_FOREACH (bNodeSocket *, socket, &ntree->inputs) {
+ version_idproperty_ui_data(socket->prop);
+ }
+ LISTBASE_FOREACH (bNodeSocket *, socket, &ntree->outputs) {
+ version_idproperty_ui_data(socket->prop);
+ }
+ }
+
+ /* The UI data from exposed node modifier properties is just copied from the corresponding node
+ * group, but the copying only runs when necessary, so we still need to version UI data here. */
+ LISTBASE_FOREACH (Object *, ob, &bmain->objects) {
+ LISTBASE_FOREACH (ModifierData *, md, &ob->modifiers) {
+ if (md->type == eModifierType_Nodes) {
+ NodesModifierData *nmd = (NodesModifierData *)md;
+ version_idproperty_ui_data(nmd->settings.properties);
+ }
+ }
+ }
+
+ /* Sequences. */
+ LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) {
+ if (scene->ed != NULL) {
+ LISTBASE_FOREACH (Sequence *, seq, &scene->ed->seqbase) {
+ version_idproperty_ui_data(seq->prop);
+ }
+ }
+ }
+}
+
static void sort_linked_ids(Main *bmain)
{
ListBase *lb;
@@ -252,6 +489,7 @@ void do_versions_after_linking_300(Main *bmain, ReportList *UNUSED(reports))
*/
{
/* Keep this block, even when empty. */
+ do_versions_idproperty_ui_data(bmain);
}
}
@@ -776,18 +1014,7 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain)
}
}
- /**
- * Versioning code until next subversion bump goes here.
- *
- * \note Be sure to check when bumping the version:
- * - "versioning_userdef.c", #blo_do_versions_userdef
- * - "versioning_userdef.c", #do_versions_theme
- *
- * \note Keep this message at the bottom of the function.
- */
- {
- /* Keep this block, even when empty. */
-
+ if (!MAIN_VERSION_ATLEAST(bmain, 300, 19)) {
/* Add node storage for subdivision surface node. */
FOREACH_NODETREE_BEGIN (bmain, ntree, id) {
if (ntree->type == NTREE_GEOMETRY) {
@@ -818,5 +1045,23 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain)
}
}
}
+
+ LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) {
+ SequencerToolSettings *sequencer_tool_settings = SEQ_tool_settings_ensure(scene);
+ sequencer_tool_settings->overlap_mode = SEQ_OVERLAP_SHUFFLE;
+ }
+ }
+
+ /**
+ * Versioning code until next subversion bump goes here.
+ *
+ * \note Be sure to check when bumping the version:
+ * - "versioning_userdef.c", #blo_do_versions_userdef
+ * - "versioning_userdef.c", #do_versions_theme
+ *
+ * \note Keep this message at the bottom of the function.
+ */
+ {
+ /* Keep this block, even when empty. */
}
}
diff --git a/source/blender/editors/interface/interface_eyedropper.c b/source/blender/editors/interface/interface_eyedropper.c
index 13566a59910..2e7b0ce532c 100644
--- a/source/blender/editors/interface/interface_eyedropper.c
+++ b/source/blender/editors/interface/interface_eyedropper.c
@@ -125,10 +125,7 @@ void eyedropper_draw_cursor_text_window(const struct wmWindow *window, const cha
eyedropper_draw_cursor_text_ex(x, y, name);
}
-void eyedropper_draw_cursor_text_region(const struct bContext *C,
- const int x,
- const int y,
- const char *name)
+void eyedropper_draw_cursor_text_region(const int x, const int y, const char *name)
{
if (name[0] == '\0') {
return;
diff --git a/source/blender/editors/interface/interface_eyedropper_datablock.c b/source/blender/editors/interface/interface_eyedropper_datablock.c
index 0eeeb0a39e4..4996c803dfe 100644
--- a/source/blender/editors/interface/interface_eyedropper_datablock.c
+++ b/source/blender/editors/interface/interface_eyedropper_datablock.c
@@ -75,10 +75,12 @@ typedef struct DataDropper {
char name[200];
} DataDropper;
-static void datadropper_draw_cb(const struct bContext *C, ARegion *region, void *arg)
+static void datadropper_draw_cb(const struct bContext *UNUSED(C),
+ ARegion *UNUSED(region),
+ void *arg)
{
DataDropper *ddr = arg;
- eyedropper_draw_cursor_text_region(C, UNPACK2(ddr->name_pos), ddr->name);
+ eyedropper_draw_cursor_text_region(UNPACK2(ddr->name_pos), ddr->name);
}
static int datadropper_init(bContext *C, wmOperator *op)
@@ -254,7 +256,7 @@ static void datadropper_cancel(bContext *C, wmOperator *op)
}
/* To switch the draw callback when region under mouse event changes */
-static void datadropper_set_draw_callback_region(bContext *C, ScrArea *area, DataDropper *ddr)
+static void datadropper_set_draw_callback_region(ScrArea *area, DataDropper *ddr)
{
if (area) {
/* If spacetype changed */
@@ -311,7 +313,7 @@ static int datadropper_modal(bContext *C, wmOperator *op, const wmEvent *event)
datadropper_win_area_find(C, mval, mval, &win, &area);
/* Set the region for eyedropper cursor text drawing */
- datadropper_set_draw_callback_region(C, area, ddr);
+ datadropper_set_draw_callback_region(area, ddr);
datadropper_id_sample_pt(C, win, area, ddr, mval[0], mval[1], &id);
}
diff --git a/source/blender/editors/interface/interface_eyedropper_depth.c b/source/blender/editors/interface/interface_eyedropper_depth.c
index 6f272201085..311f718d950 100644
--- a/source/blender/editors/interface/interface_eyedropper_depth.c
+++ b/source/blender/editors/interface/interface_eyedropper_depth.c
@@ -76,10 +76,12 @@ typedef struct DepthDropper {
char name[200];
} DepthDropper;
-static void depthdropper_draw_cb(const struct bContext *C, ARegion *region, void *arg)
+static void depthdropper_draw_cb(const struct bContext *UNUSED(C),
+ ARegion *UNUSED(region),
+ void *arg)
{
DepthDropper *ddr = arg;
- eyedropper_draw_cursor_text_region(C, UNPACK2(ddr->name_pos), ddr->name);
+ eyedropper_draw_cursor_text_region(UNPACK2(ddr->name_pos), ddr->name);
}
static int depthdropper_init(bContext *C, wmOperator *op)
diff --git a/source/blender/editors/interface/interface_eyedropper_intern.h b/source/blender/editors/interface/interface_eyedropper_intern.h
index 4295a781af8..f9f3fcfb5d1 100644
--- a/source/blender/editors/interface/interface_eyedropper_intern.h
+++ b/source/blender/editors/interface/interface_eyedropper_intern.h
@@ -24,10 +24,7 @@
/* interface_eyedropper.c */
void eyedropper_draw_cursor_text_window(const struct wmWindow *window, const char *name);
-void eyedropper_draw_cursor_text_region(const struct bContext *C,
- const int x,
- const int y,
- const char *name);
+void eyedropper_draw_cursor_text_region(const int x, const int y, const char *name);
uiBut *eyedropper_get_property_button_under_mouse(bContext *C, const wmEvent *event);
void datadropper_win_area_find(const struct bContext *C,
const int mval[2],
diff --git a/source/blender/editors/interface/interface_handlers.c b/source/blender/editors/interface/interface_handlers.c
index a618daec4bc..977e9661dd9 100644
--- a/source/blender/editors/interface/interface_handlers.c
+++ b/source/blender/editors/interface/interface_handlers.c
@@ -2933,8 +2933,9 @@ static int ui_text_position_from_hidden(uiBut *but, int pos)
{
const char *butstr = (but->editstr) ? but->editstr : but->drawstr;
const char *strpos = butstr;
+ const char *str_end = butstr + strlen(butstr);
for (int i = 0; i < pos; i++) {
- strpos = BLI_str_find_next_char_utf8(strpos, NULL);
+ strpos = BLI_str_find_next_char_utf8(strpos, str_end);
}
return (strpos - butstr);
diff --git a/source/blender/editors/interface/interface_widgets.c b/source/blender/editors/interface/interface_widgets.c
index 48f638dac33..a2b86ccd947 100644
--- a/source/blender/editors/interface/interface_widgets.c
+++ b/source/blender/editors/interface/interface_widgets.c
@@ -1498,15 +1498,15 @@ static void widget_draw_submenu_tria(const uiBut *but,
static void ui_text_clip_give_prev_off(uiBut *but, const char *str)
{
- const char *prev_utf8 = BLI_str_find_prev_char_utf8(str, str + but->ofs);
+ const char *prev_utf8 = BLI_str_find_prev_char_utf8(str + but->ofs, str);
const int bytes = str + but->ofs - prev_utf8;
but->ofs -= bytes;
}
-static void ui_text_clip_give_next_off(uiBut *but, const char *str)
+static void ui_text_clip_give_next_off(uiBut *but, const char *str, const char *str_end)
{
- const char *next_utf8 = BLI_str_find_next_char_utf8(str + but->ofs, NULL);
+ const char *next_utf8 = BLI_str_find_next_char_utf8(str + but->ofs, str_end);
const int bytes = next_utf8 - (str + but->ofs);
but->ofs += bytes;
@@ -1739,7 +1739,8 @@ static void ui_text_clip_cursor(const uiFontStyle *fstyle, uiBut *but, const rct
but->strwidth = BLF_width(fstyle->uifont_id, but->editstr + but->ofs, INT_MAX);
if (but->strwidth > okwidth) {
- int len = strlen(but->editstr);
+ const int editstr_len = strlen(but->editstr);
+ int len = editstr_len;
while (but->strwidth > okwidth) {
float width;
@@ -1749,7 +1750,7 @@ static void ui_text_clip_cursor(const uiFontStyle *fstyle, uiBut *but, const rct
/* if cursor is at 20 pixels of right side button we clip left */
if (width > okwidth - 20) {
- ui_text_clip_give_next_off(but, but->editstr);
+ ui_text_clip_give_next_off(but, but->editstr, but->editstr + editstr_len);
}
else {
int bytes;
@@ -1757,7 +1758,7 @@ static void ui_text_clip_cursor(const uiFontStyle *fstyle, uiBut *but, const rct
if (width < 20 && but->ofs > 0) {
ui_text_clip_give_prev_off(but, but->editstr);
}
- bytes = BLI_str_utf8_size(BLI_str_find_prev_char_utf8(but->editstr, but->editstr + len));
+ bytes = BLI_str_utf8_size(BLI_str_find_prev_char_utf8(but->editstr + len, but->editstr));
if (bytes == -1) {
bytes = 1;
}
@@ -1805,7 +1806,7 @@ static void ui_text_clip_right_label(const uiFontStyle *fstyle, uiBut *but, cons
/* chop off the leading text, starting from the right */
while (but->strwidth > okwidth && cp2 > but->drawstr) {
- const char *prev_utf8 = BLI_str_find_prev_char_utf8(but->drawstr, cp2);
+ const char *prev_utf8 = BLI_str_find_prev_char_utf8(cp2, but->drawstr);
const int bytes = cp2 - prev_utf8;
/* shift the text after and including cp2 back by 1 char,
@@ -1825,7 +1826,7 @@ static void ui_text_clip_right_label(const uiFontStyle *fstyle, uiBut *but, cons
/* after the leading text is gone, chop off the : and following space, with ofs */
while ((but->strwidth > okwidth) && (but->ofs < 2)) {
- ui_text_clip_give_next_off(but, but->drawstr);
+ ui_text_clip_give_next_off(but, but->drawstr, but->drawstr + drawstr_len);
but->strwidth = BLF_width(
fstyle->uifont_id, but->drawstr + but->ofs, sizeof(but->drawstr) - but->ofs);
if (but->strwidth < 10) {
diff --git a/source/blender/editors/space_action/action_draw.c b/source/blender/editors/space_action/action_draw.c
index a3bdcd2adf5..6f1a90e56a5 100644
--- a/source/blender/editors/space_action/action_draw.c
+++ b/source/blender/editors/space_action/action_draw.c
@@ -259,17 +259,18 @@ void draw_channel_strips(bAnimContext *ac, SpaceAction *saction, ARegion *region
else {
color = sel ? col1 : col2;
}
- /* frames less than one get less saturated background */
+
+ /* Color overlay on frames between the start/end frames. */
immUniformColor4ubv(color);
- immRectf(pos, 0.0f, ymin, v2d->cur.xmin, ymax);
+ immRectf(pos, ac->scene->r.sfra, ymin, ac->scene->r.efra, ymax);
- /* frames one and higher get a saturated background */
- immUniformColor3ubvAlpha(color, MIN2(255, color[3] * 2));
- immRectf(pos, v2d->cur.xmin, ymin, v2d->cur.xmax + EXTRA_SCROLL_PAD, ymax);
+ /* Color overlay outside the start/end frame range get a more transparent overlay. */
+ immUniformColor3ubvAlpha(color, MIN2(255, color[3] / 2));
+ immRectf(pos, v2d->cur.xmin, ymin, ac->scene->r.sfra, ymax);
+ immRectf(pos, ac->scene->r.efra, ymin, v2d->cur.xmax + EXTRA_SCROLL_PAD, ymax);
}
else if (ac->datatype == ANIMCONT_MASK) {
/* TODO: this is a copy of gpencil. */
- /* frames less than one get less saturated background */
uchar *color;
if (ale->type == ANIMTYPE_SUMMARY) {
color = col_summary;
@@ -277,12 +278,15 @@ void draw_channel_strips(bAnimContext *ac, SpaceAction *saction, ARegion *region
else {
color = sel ? col1 : col2;
}
+
+ /* Color overlay on frames between the start/end frames. */
immUniformColor4ubv(color);
- immRectf(pos, 0.0f, ymin, v2d->cur.xmin, ymax);
+ immRectf(pos, ac->scene->r.sfra, ymin, ac->scene->r.efra, ymax);
- /* frames one and higher get a saturated background */
- immUniformColor3ubvAlpha(color, MIN2(255, color[3] * 2));
- immRectf(pos, v2d->cur.xmin, ymin, v2d->cur.xmax + EXTRA_SCROLL_PAD, ymax);
+ /* Color overlay outside the start/end frame range get a more transparent overlay. */
+ immUniformColor3ubvAlpha(color, MIN2(255, color[3] / 2));
+ immRectf(pos, v2d->cur.xmin, ymin, ac->scene->r.sfra, ymax);
+ immRectf(pos, ac->scene->r.efra, ymin, v2d->cur.xmax + EXTRA_SCROLL_PAD, ymax);
}
}
}
diff --git a/source/blender/editors/space_buttons/buttons_context.c b/source/blender/editors/space_buttons/buttons_context.c
index 70b715e0119..bf855db07e9 100644
--- a/source/blender/editors/space_buttons/buttons_context.c
+++ b/source/blender/editors/space_buttons/buttons_context.c
@@ -987,7 +987,8 @@ int /*eContextResult*/ buttons_context(const bContext *C,
matnr = 0;
}
/* Keep aligned with rna_Object_material_slots_get. */
- CTX_data_pointer_set(result, &ob->id, &RNA_MaterialSlot, POINTER_FROM_INT(matnr + 1));
+ CTX_data_pointer_set(
+ result, &ob->id, &RNA_MaterialSlot, (void *)(matnr + (uintptr_t)&ob->id));
}
}
diff --git a/source/blender/editors/space_console/console_ops.c b/source/blender/editors/space_console/console_ops.c
index 763beb8671b..c6fb2560dc0 100644
--- a/source/blender/editors/space_console/console_ops.c
+++ b/source/blender/editors/space_console/console_ops.c
@@ -435,7 +435,7 @@ static int console_insert_invoke(bContext *C, wmOperator *op, const wmEvent *eve
}
else {
/* in theory, ghost can set value to extended ascii here */
- len = BLI_str_utf8_from_unicode(event->ascii, str);
+ len = BLI_str_utf8_from_unicode(event->ascii, str, sizeof(str) - 1);
}
str[len] = '\0';
RNA_string_set(op->ptr, "text", str);
diff --git a/source/blender/editors/space_image/image_draw.c b/source/blender/editors/space_image/image_draw.c
index 4f66506d28b..d2af26aa1d7 100644
--- a/source/blender/editors/space_image/image_draw.c
+++ b/source/blender/editors/space_image/image_draw.c
@@ -389,10 +389,12 @@ void ED_image_draw_info(Scene *scene,
immRecti(pos, color_quater_x, color_quater_y, color_rect_half.xmax, color_rect_half.ymax);
immRecti(pos, color_rect_half.xmin, color_rect_half.ymin, color_quater_x, color_quater_y);
- GPU_blend(GPU_BLEND_ALPHA);
- immUniformColor3fvAlpha(finalcol, fp ? fp[3] : (cp[3] / 255.0f));
- immRecti(pos, color_rect.xmin, color_rect.ymin, color_rect.xmax, color_rect.ymax);
- GPU_blend(GPU_BLEND_NONE);
+ if (fp != NULL || cp != NULL) {
+ GPU_blend(GPU_BLEND_ALPHA);
+ immUniformColor3fvAlpha(finalcol, fp ? fp[3] : (cp[3] / 255.0f));
+ immRecti(pos, color_rect.xmin, color_rect.ymin, color_rect.xmax, color_rect.ymax);
+ GPU_blend(GPU_BLEND_NONE);
+ }
}
else {
immUniformColor3fv(finalcol);
diff --git a/source/blender/editors/space_node/node_draw.cc b/source/blender/editors/space_node/node_draw.cc
index b67117f1ad0..5b4e3b3b6f5 100644
--- a/source/blender/editors/space_node/node_draw.cc
+++ b/source/blender/editors/space_node/node_draw.cc
@@ -360,7 +360,11 @@ static void node_update_basis(const bContext *C, bNodeTree *ntree, bNode *node)
/* Get "global" coordinates. */
float locx, locy;
node_to_view(node, 0.0f, 0.0f, &locx, &locy);
- float dy = locy;
+ /* Round the node origin because text contents are always pixel-aligned. */
+ locx = round(locx);
+ locy = round(locy);
+
+ int dy = locy;
/* Header. */
dy -= NODE_DY;
@@ -412,9 +416,9 @@ static void node_update_basis(const bContext *C, bNodeTree *ntree, bNode *node)
/* Ensure minimum socket height in case layout is empty. */
buty = min_ii(buty, dy - NODE_DY);
- nsock->locx = locx + NODE_WIDTH(node);
- /* Place the socket circle in the middle of the layout. */
- nsock->locy = 0.5f * (dy + buty);
+ /* Round the socket location to stop it from jiggling. */
+ nsock->locx = round(locx + NODE_WIDTH(node));
+ nsock->locy = round(0.5f * (dy + buty));
dy = buty;
if (nsock->next) {
@@ -549,8 +553,8 @@ static void node_update_basis(const bContext *C, bNodeTree *ntree, bNode *node)
buty = min_ii(buty, dy - NODE_DY);
nsock->locx = locx;
- /* Place the socket circle in the middle of the layout. */
- nsock->locy = 0.5f * (dy + buty);
+ /* Round the socket vertical position to stop it from jiggling. */
+ nsock->locy = round(0.5f * (dy + buty));
dy = buty - multi_input_socket_offset * 0.5;
if (nsock->next) {
@@ -587,6 +591,9 @@ static void node_update_hidden(bNode *node)
/* Get "global" coords. */
float locx, locy;
node_to_view(node, 0.0f, 0.0f, &locx, &locy);
+ /* Round the node origin because text contents are always pixel-aligned. */
+ locx = round(locx);
+ locy = round(locy);
/* Calculate minimal radius. */
LISTBASE_FOREACH (bNodeSocket *, nsock, &node->inputs) {
@@ -617,8 +624,9 @@ static void node_update_hidden(bNode *node)
LISTBASE_FOREACH (bNodeSocket *, nsock, &node->outputs) {
if (!nodeSocketIsHidden(nsock)) {
- nsock->locx = node->totr.xmax - hiddenrad + sinf(rad) * hiddenrad;
- nsock->locy = node->totr.ymin + hiddenrad + cosf(rad) * hiddenrad;
+ /* Round the socket location to stop it from jiggling. */
+ nsock->locx = round(node->totr.xmax - hiddenrad + sinf(rad) * hiddenrad);
+ nsock->locy = round(node->totr.ymin + hiddenrad + cosf(rad) * hiddenrad);
rad += drad;
}
}
@@ -628,8 +636,9 @@ static void node_update_hidden(bNode *node)
LISTBASE_FOREACH (bNodeSocket *, nsock, &node->inputs) {
if (!nodeSocketIsHidden(nsock)) {
- nsock->locx = node->totr.xmin + hiddenrad + sinf(rad) * hiddenrad;
- nsock->locy = node->totr.ymin + hiddenrad + cosf(rad) * hiddenrad;
+ /* Round the socket location to stop it from jiggling. */
+ nsock->locx = round(node->totr.xmin + hiddenrad + sinf(rad) * hiddenrad);
+ nsock->locy = round(node->totr.ymin + hiddenrad + cosf(rad) * hiddenrad);
rad += drad;
}
}
diff --git a/source/blender/editors/space_node/node_intern.h b/source/blender/editors/space_node/node_intern.h
index df20420e472..d35fd729131 100644
--- a/source/blender/editors/space_node/node_intern.h
+++ b/source/blender/editors/space_node/node_intern.h
@@ -328,7 +328,7 @@ extern const char *node_context_dir[];
#define BASIS_RAD (0.2f * U.widget_unit)
#define NODE_DYS (U.widget_unit / 2)
#define NODE_DY U.widget_unit
-#define NODE_SOCKDY (0.08f * U.widget_unit)
+#define NODE_SOCKDY (0.1f * U.widget_unit)
#define NODE_WIDTH(node) (node->width * UI_DPI_FAC)
#define NODE_HEIGHT(node) (node->height * UI_DPI_FAC)
#define NODE_MARGIN_X (1.10f * U.widget_unit)
diff --git a/source/blender/editors/space_sequencer/sequencer_draw.c b/source/blender/editors/space_sequencer/sequencer_draw.c
index b3c39e2fa6f..fe3ff469e50 100644
--- a/source/blender/editors/space_sequencer/sequencer_draw.c
+++ b/source/blender/editors/space_sequencer/sequencer_draw.c
@@ -737,7 +737,8 @@ static void draw_seq_handle(View2D *v2d,
}
}
-static void draw_seq_outline(Sequence *seq,
+static void draw_seq_outline(Scene *scene,
+ Sequence *seq,
uint pos,
float x1,
float x2,
@@ -765,7 +766,9 @@ static void draw_seq_outline(Sequence *seq,
* - Slightly lighter.
* - Red when overlapping with other strips.
*/
- if ((G.moving & G_TRANSFORM_SEQ) && (seq->flag & SELECT)) {
+ const eSeqOverlapMode overlap_mode = SEQ_tool_settings_overlap_mode_get(scene);
+ if ((G.moving & G_TRANSFORM_SEQ) && (seq->flag & SELECT) &&
+ overlap_mode != SEQ_OVERLAP_OVERWRITE) {
if (seq->flag & SEQ_OVERLAP) {
col[0] = 255;
col[1] = col[2] = 33;
@@ -1383,7 +1386,7 @@ static void draw_seq_strip(const bContext *C,
v2d, seq, handsize_clamped, SEQ_RIGHTHANDLE, pos, seq_active, pixelx, y_threshold);
}
- draw_seq_outline(seq, pos, x1, x2, y1, y2, pixelx, pixely, seq_active);
+ draw_seq_outline(scene, seq, pos, x1, x2, y1, y2, pixelx, pixely, seq_active);
immUnbindProgram();
diff --git a/source/blender/editors/space_text/text_draw.c b/source/blender/editors/space_text/text_draw.c
index b6ba95885e4..99fcb2092c3 100644
--- a/source/blender/editors/space_text/text_draw.c
+++ b/source/blender/editors/space_text/text_draw.c
@@ -1472,7 +1472,7 @@ static void draw_brackets(const SpaceText *st, const TextDrawContext *tdc, ARegi
/* closing bracket, search backward for open */
fc--;
if (c > 0) {
- c -= linep->line + c - BLI_str_prev_char_utf8(linep->line + c);
+ c -= linep->line + c - BLI_str_find_prev_char_utf8(linep->line + c, linep->line);
}
while (linep) {
while (fc >= 0) {
@@ -1493,7 +1493,7 @@ static void draw_brackets(const SpaceText *st, const TextDrawContext *tdc, ARegi
}
fc--;
if (c > 0) {
- c -= linep->line + c - BLI_str_prev_char_utf8(linep->line + c);
+ c -= linep->line + c - BLI_str_find_prev_char_utf8(linep->line + c, linep->line);
}
}
if (endl) {
@@ -1508,7 +1508,7 @@ static void draw_brackets(const SpaceText *st, const TextDrawContext *tdc, ARegi
fc = -1;
}
if (linep->len) {
- c = BLI_str_prev_char_utf8(linep->line + linep->len) - linep->line;
+ c = BLI_str_find_prev_char_utf8(linep->line + linep->len, linep->line) - linep->line;
}
else {
fc = -1;
diff --git a/source/blender/editors/space_text/text_ops.c b/source/blender/editors/space_text/text_ops.c
index f480f60a2b9..c3bc474b98a 100644
--- a/source/blender/editors/space_text/text_ops.c
+++ b/source/blender/editors/space_text/text_ops.c
@@ -1955,7 +1955,7 @@ static void txt_wrap_move_eol(SpaceText *st, ARegion *region, const bool sel)
end = MIN2(end, i);
if (chop) {
- endj = BLI_str_prev_char_utf8((*linep)->line + j) - (*linep)->line;
+ endj = BLI_str_find_prev_char_utf8((*linep)->line + j, (*linep)->line) - (*linep)->line;
}
if (endj >= oldc) {
@@ -3486,7 +3486,7 @@ static int text_insert_invoke(bContext *C, wmOperator *op, const wmEvent *event)
}
else {
/* in theory, ghost can set value to extended ascii here */
- len = BLI_str_utf8_from_unicode(event->ascii, str);
+ len = BLI_str_utf8_from_unicode(event->ascii, str, sizeof(str) - 1);
}
str[len] = '\0';
RNA_string_set(op->ptr, "text", str);
diff --git a/source/blender/editors/space_view3d/view3d_edit.c b/source/blender/editors/space_view3d/view3d_edit.c
index b055a0fe947..466820353b9 100644
--- a/source/blender/editors/space_view3d/view3d_edit.c
+++ b/source/blender/editors/space_view3d/view3d_edit.c
@@ -5292,7 +5292,7 @@ static int toggle_shading_exec(bContext *C, wmOperator *op)
}
ED_view3d_shade_update(bmain, v3d, area);
- WM_event_add_notifier(C, NC_SPACE | ND_SPACE_VIEW3D, v3d);
+ WM_event_add_notifier(C, NC_SPACE | ND_SPACE_VIEW3D | NS_VIEW3D_SHADING, v3d);
return OPERATOR_FINISHED;
}
@@ -5347,6 +5347,7 @@ static int toggle_xray_exec(bContext *C, wmOperator *op)
}
ED_area_tag_redraw(area);
+ WM_event_add_notifier(C, NC_SPACE | ND_SPACE_VIEW3D | NS_VIEW3D_SHADING, v3d);
return OPERATOR_FINISHED;
}
diff --git a/source/blender/editors/transform/transform_convert_sequencer.c b/source/blender/editors/transform/transform_convert_sequencer.c
index 45ed0f3b664..9548498f0b8 100644
--- a/source/blender/editors/transform/transform_convert_sequencer.c
+++ b/source/blender/editors/transform/transform_convert_sequencer.c
@@ -29,10 +29,13 @@
#include "BLI_math.h"
#include "BKE_context.h"
+#include "BKE_main.h"
#include "BKE_report.h"
#include "ED_markers.h"
+#include "SEQ_edit.h"
+#include "SEQ_effects.h"
#include "SEQ_iterator.h"
#include "SEQ_relations.h"
#include "SEQ_sequencer.h"
@@ -341,12 +344,17 @@ static bool seq_transform_check_strip_effects(SeqCollection *transformed_strips)
return false;
}
+static ListBase *seqbase_active_get(const TransInfo *t)
+{
+ Editing *ed = SEQ_editing_get(t->scene, false);
+ return SEQ_active_seqbase_get(ed);
+}
+
/* Offset all strips positioned after left edge of transformed strips boundbox by amount equal
* to overlap of transformed strips. */
static void seq_transform_handle_expand_to_fit(TransInfo *t, SeqCollection *transformed_strips)
{
- Editing *ed = SEQ_editing_get(t->scene, false);
- ListBase *seqbasep = SEQ_active_seqbase_get(ed);
+ ListBase *seqbasep = seqbase_active_get(t);
ListBase *markers = &t->scene->markers;
const bool use_sync_markers = (((SpaceSeq *)t->area->spacedata.first)->flag &
SEQ_MARKER_TRANS) != 0;
@@ -378,23 +386,178 @@ static void seq_transform_handle_expand_to_fit(TransInfo *t, SeqCollection *tran
SEQ_collection_free(right_side_strips);
}
-static void seq_transform_handle_overlap(TransInfo *t, SeqCollection *transformed_strips)
+static SeqCollection *query_overwrite_targets(const TransInfo *t,
+ SeqCollection *transformed_strips)
{
- Editing *ed = SEQ_editing_get(t->scene, false);
- ListBase *seqbasep = SEQ_active_seqbase_get(ed);
+ SeqCollection *collection = SEQ_query_unselected_strips(seqbase_active_get(t));
+
+ Sequence *seq, *seq_transformed;
+ SEQ_ITERATOR_FOREACH (seq, collection) {
+ bool does_overlap = false;
- if (t->flag & T_ALT_TRANSFORM) {
- seq_transform_handle_expand_to_fit(t, transformed_strips);
+ SEQ_ITERATOR_FOREACH (seq_transformed, transformed_strips) {
+ /* Effects of transformed strips can be unselected. These must not be included. */
+ if (seq == seq_transformed) {
+ SEQ_collection_remove_strip(seq, collection);
+ }
+ if (SEQ_transform_test_overlap_seq_seq(seq, seq_transformed)) {
+ does_overlap = true;
+ }
+ }
+
+ if (!does_overlap) {
+ SEQ_collection_remove_strip(seq, collection);
+ }
}
- else {
- ListBase *markers = &t->scene->markers;
- const bool use_sync_markers = (((SpaceSeq *)t->area->spacedata.first)->flag &
- SEQ_MARKER_TRANS) != 0;
- /* Shuffle non strips with no effects attached. */
- SeqCollection *standalone_strips = extract_standalone_strips(transformed_strips);
- SEQ_transform_seqbase_shuffle_time(
- standalone_strips, seqbasep, t->scene, markers, use_sync_markers);
- SEQ_collection_free(standalone_strips);
+
+ return collection;
+}
+
+typedef enum eOvelapDescrition {
+ /* No overlap. */
+ STRIP_OVERLAP_NONE,
+ /* Overlapping strip covers overlapped completely. */
+ STRIP_OVERLAP_IS_FULL,
+ /* Overlapping strip is inside overlapped. */
+ STRIP_OVERLAP_IS_INSIDE,
+ /* Partial overlap between 2 strips. */
+ STRIP_OVERLAP_LEFT_SIDE,
+ STRIP_OVERLAP_RIGHT_SIDE,
+} eOvelapDescrition;
+
+static eOvelapDescrition overlap_description_get(const Sequence *transformed,
+ const Sequence *target)
+{
+ if (transformed->startdisp <= target->startdisp && transformed->enddisp >= target->enddisp) {
+ return STRIP_OVERLAP_IS_FULL;
+ }
+ if (transformed->startdisp > target->startdisp && transformed->enddisp < target->enddisp) {
+ return STRIP_OVERLAP_IS_INSIDE;
+ }
+ if (transformed->startdisp <= target->startdisp && target->startdisp <= transformed->enddisp) {
+ return STRIP_OVERLAP_LEFT_SIDE;
+ }
+ if (transformed->startdisp <= target->enddisp && target->enddisp <= transformed->enddisp) {
+ return STRIP_OVERLAP_RIGHT_SIDE;
+ }
+ return STRIP_OVERLAP_NONE;
+}
+
+/* Split strip in 3 parts, remove middle part and fit transformed inside. */
+static void seq_transform_handle_overwrite_split(const TransInfo *t,
+ const Sequence *transformed,
+ Sequence *target)
+{
+ Main *bmain = CTX_data_main(t->context);
+ Scene *scene = t->scene;
+ ListBase *seqbase = seqbase_active_get(t);
+
+ Sequence *split_strip = SEQ_edit_strip_split(
+ bmain, scene, seqbase, target, transformed->startdisp, SEQ_SPLIT_SOFT, NULL);
+ SEQ_edit_strip_split(
+ bmain, scene, seqbase, split_strip, transformed->enddisp, SEQ_SPLIT_SOFT, NULL);
+ SEQ_edit_flag_for_removal(scene, seqbase_active_get(t), split_strip);
+}
+
+/* Trim strips by adjusting handle position.
+ * This is bit more complicated in case overlap happens on effect. */
+static void seq_transform_handle_overwrite_trim(const TransInfo *t,
+ const Sequence *transformed,
+ Sequence *target,
+ const eOvelapDescrition overlap)
+{
+ SeqCollection *targets = SEQ_query_by_reference(
+ target, seqbase_active_get(t), SEQ_query_strip_effect_chain);
+
+ /* Expand collection by adding all target's children, effects and their children. */
+ if ((target->type & SEQ_TYPE_EFFECT) != 0) {
+ SEQ_collection_expand(seqbase_active_get(t), targets, SEQ_query_strip_effect_chain);
+ }
+
+ /* Trim all non effects, that have influence on effect length which is overlapping. */
+ Sequence *seq;
+ SEQ_ITERATOR_FOREACH (seq, targets) {
+ if ((seq->type & SEQ_TYPE_EFFECT) != 0 && SEQ_effect_get_num_inputs(seq->type) > 0) {
+ continue;
+ }
+ if (overlap == STRIP_OVERLAP_LEFT_SIDE) {
+ SEQ_transform_set_left_handle_frame(seq, transformed->enddisp);
+ }
+ else {
+ BLI_assert(overlap == STRIP_OVERLAP_RIGHT_SIDE);
+ SEQ_transform_set_right_handle_frame(seq, transformed->startdisp);
+ }
+ SEQ_time_update_sequence(t->scene, seq);
+ }
+ SEQ_collection_free(targets);
+}
+
+static void seq_transform_handle_overwrite(const TransInfo *t, SeqCollection *transformed_strips)
+{
+ SeqCollection *targets = query_overwrite_targets(t, transformed_strips);
+
+ bool strips_delete = false;
+ Sequence *target;
+ Sequence *transformed;
+ SEQ_ITERATOR_FOREACH (target, targets) {
+ SEQ_ITERATOR_FOREACH (transformed, transformed_strips) {
+ if (transformed->machine != target->machine) {
+ continue;
+ }
+
+ const eOvelapDescrition overlap = overlap_description_get(transformed, target);
+
+ if (overlap == STRIP_OVERLAP_IS_FULL) {
+ /* Remove covered strip. */
+ SEQ_edit_flag_for_removal(t->scene, seqbase_active_get(t), target);
+ strips_delete = true;
+ }
+ else if (overlap == STRIP_OVERLAP_IS_INSIDE) {
+ seq_transform_handle_overwrite_split(t, transformed, target);
+ strips_delete = true;
+ }
+ else if (ELEM(overlap, STRIP_OVERLAP_LEFT_SIDE, STRIP_OVERLAP_RIGHT_SIDE)) {
+ seq_transform_handle_overwrite_trim(t, transformed, target, overlap);
+ }
+ }
+ }
+
+ SEQ_collection_free(targets);
+
+ if (strips_delete) {
+ SEQ_edit_remove_flagged_sequences(t->scene, seqbase_active_get(t));
+ }
+}
+
+static void seq_transform_handle_overlap_shuffle(const TransInfo *t,
+ SeqCollection *transformed_strips)
+{
+ ListBase *seqbase = seqbase_active_get(t);
+ ListBase *markers = &t->scene->markers;
+ const bool use_sync_markers = (((SpaceSeq *)t->area->spacedata.first)->flag &
+ SEQ_MARKER_TRANS) != 0;
+ /* Shuffle non strips with no effects attached. */
+ SeqCollection *standalone_strips = extract_standalone_strips(transformed_strips);
+ SEQ_transform_seqbase_shuffle_time(
+ standalone_strips, seqbase, t->scene, markers, use_sync_markers);
+ SEQ_collection_free(standalone_strips);
+}
+
+static void seq_transform_handle_overlap(TransInfo *t, SeqCollection *transformed_strips)
+{
+ ListBase *seqbasep = seqbase_active_get(t);
+ const eSeqOverlapMode overlap_mode = SEQ_tool_settings_overlap_mode_get(t->scene);
+
+ switch (overlap_mode) {
+ case SEQ_OVERLAP_EXPAND:
+ seq_transform_handle_expand_to_fit(t, transformed_strips);
+ break;
+ case SEQ_OVERLAP_OVERWRITE:
+ seq_transform_handle_overwrite(t, transformed_strips);
+ break;
+ case SEQ_OVERLAP_SHUFFLE:
+ seq_transform_handle_overlap_shuffle(t, transformed_strips);
+ break;
}
if (seq_transform_check_strip_effects(transformed_strips)) {
@@ -409,6 +572,7 @@ static void seq_transform_handle_overlap(TransInfo *t, SeqCollection *transforme
if (SEQ_transform_test_overlap(seqbasep, seq)) {
SEQ_transform_seqbase_shuffle(seqbasep, seq, t->scene);
}
+ seq->flag &= ~SEQ_OVERLAP;
}
}
@@ -432,6 +596,7 @@ static void freeSeqData(TransInfo *t, TransDataContainer *tc, TransCustomData *c
}
SeqCollection *transformed_strips = seq_transform_collection_from_transdata(tc);
+ SEQ_collection_expand(seqbase_active_get(t), transformed_strips, SEQ_query_strip_effect_chain);
if (t->state == TRANS_CANCEL) {
seq_transform_cancel(t, transformed_strips);
@@ -549,7 +714,7 @@ BLI_INLINE void trans_update_seq(Scene *sce, Sequence *seq, int old_start, int s
static void flushTransSeq(TransInfo *t)
{
/* Editing null check already done */
- ListBase *seqbasep = SEQ_editing_get(t->scene, false)->seqbasep;
+ ListBase *seqbasep = seqbase_active_get(t);
int a, new_frame;
TransData *td = NULL;
@@ -601,22 +766,17 @@ static void flushTransSeq(TransInfo *t)
/* need to do the overlap check in a new loop otherwise adjacent strips
* will not be updated and we'll get false positives */
- Sequence *seq_prev = NULL;
- seq_prev = NULL;
- for (a = 0, td = tc->data, td2d = tc->data_2d; a < tc->data_len; a++, td++, td2d++) {
-
- tdsq = (TransDataSeq *)td->extra;
- seq = tdsq->seq;
+ SeqCollection *transformed_strips = seq_transform_collection_from_transdata(tc);
+ SEQ_collection_expand(seqbase_active_get(t), transformed_strips, SEQ_query_strip_effect_chain);
- if (seq != seq_prev) {
- /* test overlap, displays red outline */
- seq->flag &= ~SEQ_OVERLAP;
- if (SEQ_transform_test_overlap(seqbasep, seq)) {
- seq->flag |= SEQ_OVERLAP;
- }
+ SEQ_ITERATOR_FOREACH (seq, transformed_strips) {
+ /* test overlap, displays red outline */
+ seq->flag &= ~SEQ_OVERLAP;
+ if (SEQ_transform_test_overlap(seqbasep, seq)) {
+ seq->flag |= SEQ_OVERLAP;
}
- seq_prev = seq;
}
+ SEQ_collection_free(transformed_strips);
}
/* helper for recalcData() - for sequencer transforms */
diff --git a/source/blender/editors/transform/transform_mode_edge_seq_slide.c b/source/blender/editors/transform/transform_mode_edge_seq_slide.c
index a8f7fc43b5e..fb70d04a530 100644
--- a/source/blender/editors/transform/transform_mode_edge_seq_slide.c
+++ b/source/blender/editors/transform/transform_mode_edge_seq_slide.c
@@ -54,18 +54,6 @@
/** \name Transform (Sequencer Slide)
* \{ */
-static eRedrawFlag seq_slide_handleEvent(struct TransInfo *t, const wmEvent *event)
-{
- BLI_assert(t->mode == TFM_SEQ_SLIDE);
- const wmKeyMapItem *kmi = t->custom.mode.data;
- if (kmi && event->type == kmi->type && event->val == kmi->val) {
- /* Allows the "Expand to Fit" effect to be enabled as a toggle. */
- t->flag ^= T_ALT_TRANSFORM;
- return TREDRAW_HARD;
- }
- return TREDRAW_NOTHING;
-}
-
static void headerSeqSlide(TransInfo *t, const float val[2], char str[UI_MAX_DRAW_STR])
{
char tvec[NUM_STR_REP_LEN * 3];
@@ -79,17 +67,7 @@ static void headerSeqSlide(TransInfo *t, const float val[2], char str[UI_MAX_DRA
}
ofs += BLI_snprintf_rlen(
- str + ofs, UI_MAX_DRAW_STR - ofs, TIP_("Sequence Slide: %s%s, ("), &tvec[0], t->con.text);
-
- const wmKeyMapItem *kmi = t->custom.mode.data;
- if (kmi) {
- ofs += WM_keymap_item_to_string(kmi, false, str + ofs, UI_MAX_DRAW_STR - ofs);
- }
-
- ofs += BLI_snprintf_rlen(str + ofs,
- UI_MAX_DRAW_STR - ofs,
- TIP_(" or Alt) Expand to fit %s"),
- WM_bool_as_string((t->flag & T_ALT_TRANSFORM) != 0));
+ str + ofs, UI_MAX_DRAW_STR - ofs, TIP_("Sequence Slide: %s%s"), &tvec[0], t->con.text);
}
static void applySeqSlideValue(TransInfo *t, const float val[2])
@@ -148,7 +126,6 @@ static void applySeqSlide(TransInfo *t, const int UNUSED(mval[2]))
void initSeqSlide(TransInfo *t)
{
t->transform = applySeqSlide;
- t->handleEvent = seq_slide_handleEvent;
t->tsnap.applySnap = transform_snap_sequencer_apply_translate;
initMouseInputMode(t, &t->mouse, INPUT_VECTOR);
diff --git a/source/blender/imbuf/intern/thumbs.c b/source/blender/imbuf/intern/thumbs.c
index 19f34a6aacd..a09f06726a6 100644
--- a/source/blender/imbuf/intern/thumbs.c
+++ b/source/blender/imbuf/intern/thumbs.c
@@ -435,8 +435,9 @@ static ImBuf *thumb_create_ex(const char *file_path,
scaledy = (float)tsize;
scaledx = ((float)img->x / (float)img->y) * tsize;
}
- ex = (short)scaledx;
- ey = (short)scaledy;
+ /* Scaling down must never assign zero width/height, see: T89868. */
+ ex = MAX2(1, (short)scaledx);
+ ey = MAX2(1, (short)scaledy);
/* save some time by only scaling byte buf */
if (img->rect_float) {
diff --git a/source/blender/io/alembic/exporter/abc_custom_props.cc b/source/blender/io/alembic/exporter/abc_custom_props.cc
index 4ea2fd03fff..e27a9012dab 100644
--- a/source/blender/io/alembic/exporter/abc_custom_props.cc
+++ b/source/blender/io/alembic/exporter/abc_custom_props.cc
@@ -60,9 +60,6 @@ void CustomPropertiesExporter::write_all(const IDProperty *group)
/* Loop over the properties, just like IDP_foreach_property() does, but without the recursion. */
LISTBASE_FOREACH (IDProperty *, id_property, &group->data.group) {
- if (STREQ(id_property->name, "_RNA_UI")) {
- continue;
- }
write(id_property);
}
}
diff --git a/source/blender/makesdna/DNA_ID.h b/source/blender/makesdna/DNA_ID.h
index 10a5a0f1c47..5b7c99f2545 100644
--- a/source/blender/makesdna/DNA_ID.h
+++ b/source/blender/makesdna/DNA_ID.h
@@ -59,6 +59,58 @@ typedef struct DrawDataList {
struct DrawData *first, *last;
} DrawDataList;
+typedef struct IDPropertyUIData {
+ /** Tooltip / property description pointer. Owned by the IDProperty. */
+ char *description;
+ /** RNA subtype, used for every type except string properties (PropertySubType). */
+ int rna_subtype;
+
+ char _pad[4];
+} IDPropertyUIData;
+
+/* IDP_UI_DATA_TYPE_INT */
+typedef struct IDPropertyUIDataInt {
+ IDPropertyUIData base;
+ int *default_array; /* Only for array properties. */
+ int default_array_len;
+ char _pad[4];
+
+ int min;
+ int max;
+ int soft_min;
+ int soft_max;
+ int step;
+ int default_value;
+} IDPropertyUIDataInt;
+
+/* IDP_UI_DATA_TYPE_FLOAT */
+typedef struct IDPropertyUIDataFloat {
+ IDPropertyUIData base;
+ double *default_array; /* Only for array properties. */
+ int default_array_len;
+ char _pad[4];
+
+ float step;
+ int precision;
+
+ double min;
+ double max;
+ double soft_min;
+ double soft_max;
+ double default_value;
+} IDPropertyUIDataFloat;
+
+/* IDP_UI_DATA_TYPE_STRING */
+typedef struct IDPropertyUIDataString {
+ IDPropertyUIData base;
+ char *default_value;
+} IDPropertyUIDataString;
+
+/* IDP_UI_DATA_TYPE_ID */
+typedef struct IDPropertyUIDataID {
+ IDPropertyUIData base;
+} IDPropertyUIDataID;
+
typedef struct IDPropertyData {
void *pointer;
ListBase group;
@@ -87,6 +139,8 @@ typedef struct IDProperty {
/* totallen is total length of allocated array/string, including a buffer.
* Note that the buffering is mild; the code comes from python's list implementation. */
int totallen;
+
+ IDPropertyUIData *ui_data;
} IDProperty;
#define MAX_IDPROP_NAME 64
diff --git a/source/blender/makesdna/DNA_modifier_types.h b/source/blender/makesdna/DNA_modifier_types.h
index 1bebbc35747..13213f70fed 100644
--- a/source/blender/makesdna/DNA_modifier_types.h
+++ b/source/blender/makesdna/DNA_modifier_types.h
@@ -298,6 +298,7 @@ enum {
/* Mask Modifier -> flag */
enum {
MOD_MASK_INV = (1 << 0),
+ MOD_MASK_SMOOTH = (1 << 1),
};
typedef struct ArrayModifierData {
diff --git a/source/blender/makesdna/DNA_scene_types.h b/source/blender/makesdna/DNA_scene_types.h
index f6bf5446a60..2840c795884 100644
--- a/source/blender/makesdna/DNA_scene_types.h
+++ b/source/blender/makesdna/DNA_scene_types.h
@@ -1347,12 +1347,19 @@ typedef struct SequencerToolSettings {
int fit_method;
short snap_mode;
short snap_flag;
- int _pad0;
+ /* eSeqOverlapMode */
+ int overlap_mode;
/** When there are many snap points, 0-1 range corresponds to resolution from boundbox to all
* possible snap points. */
int snap_distance;
} SequencerToolSettings;
+typedef enum eSeqOverlapMode {
+ SEQ_OVERLAP_EXPAND,
+ SEQ_OVERLAP_OVERWRITE,
+ SEQ_OVERLAP_SHUFFLE,
+} eSeqOverlapMode;
+
typedef enum eSeqImageFitMethod {
SEQ_SCALE_TO_FIT,
SEQ_SCALE_TO_FILL,
diff --git a/source/blender/makesrna/RNA_access.h b/source/blender/makesrna/RNA_access.h
index b943a8fad5a..abbe609d0ef 100644
--- a/source/blender/makesrna/RNA_access.h
+++ b/source/blender/makesrna/RNA_access.h
@@ -1006,7 +1006,7 @@ int RNA_property_int_get_index(PointerRNA *ptr, PropertyRNA *prop, int index);
void RNA_property_int_set_array(PointerRNA *ptr, PropertyRNA *prop, const int *values);
void RNA_property_int_set_index(PointerRNA *ptr, PropertyRNA *prop, int index, int value);
int RNA_property_int_get_default(PointerRNA *ptr, PropertyRNA *prop);
-bool RNA_property_int_set_default(PointerRNA *ptr, PropertyRNA *prop, int value);
+bool RNA_property_int_set_default(PropertyRNA *prop, int value);
void RNA_property_int_get_default_array(PointerRNA *ptr, PropertyRNA *prop, int *values);
int RNA_property_int_get_default_index(PointerRNA *ptr, PropertyRNA *prop, int index);
@@ -1018,7 +1018,7 @@ float RNA_property_float_get_index(PointerRNA *ptr, PropertyRNA *prop, int index
void RNA_property_float_set_array(PointerRNA *ptr, PropertyRNA *prop, const float *values);
void RNA_property_float_set_index(PointerRNA *ptr, PropertyRNA *prop, int index, float value);
float RNA_property_float_get_default(PointerRNA *ptr, PropertyRNA *prop);
-bool RNA_property_float_set_default(PointerRNA *ptr, PropertyRNA *prop, float value);
+bool RNA_property_float_set_default(PropertyRNA *prop, float value);
void RNA_property_float_get_default_array(PointerRNA *ptr, PropertyRNA *prop, float *values);
float RNA_property_float_get_default_index(PointerRNA *ptr, PropertyRNA *prop, int index);
@@ -1028,7 +1028,7 @@ char *RNA_property_string_get_alloc(
void RNA_property_string_set(PointerRNA *ptr, PropertyRNA *prop, const char *value);
void RNA_property_string_set_bytes(PointerRNA *ptr, PropertyRNA *prop, const char *value, int len);
int RNA_property_string_length(PointerRNA *ptr, PropertyRNA *prop);
-void RNA_property_string_get_default(PointerRNA *ptr, PropertyRNA *prop, char *value);
+void RNA_property_string_get_default(PropertyRNA *prop, char *value, int max_len);
char *RNA_property_string_get_default_alloc(
PointerRNA *ptr, PropertyRNA *prop, char *fixedbuf, int fixedlen, int *r_len);
int RNA_property_string_default_length(PointerRNA *ptr, PropertyRNA *prop);
diff --git a/source/blender/makesrna/intern/rna_access.c b/source/blender/makesrna/intern/rna_access.c
index 8fa2d0ed849..a2905018cc7 100644
--- a/source/blender/makesrna/intern/rna_access.c
+++ b/source/blender/makesrna/intern/rna_access.c
@@ -246,130 +246,6 @@ void rna_idproperty_touch(IDProperty *idprop)
idprop->flag &= ~IDP_FLAG_GHOST;
}
-static IDProperty *rna_idproperty_ui_container(PropertyRNA *prop)
-{
- IDProperty *idprop;
-
- for (idprop = ((IDProperty *)prop)->prev; idprop; idprop = idprop->prev) {
- if (STREQ(RNA_IDP_UI, idprop->name)) {
- break;
- }
- }
-
- if (idprop == NULL) {
- for (idprop = ((IDProperty *)prop)->next; idprop; idprop = idprop->next) {
- if (STREQ(RNA_IDP_UI, idprop->name)) {
- break;
- }
- }
- }
-
- return idprop;
-}
-
-/* return a UI local ID prop definition for this prop */
-static const IDProperty *rna_idproperty_ui(const PropertyRNA *prop)
-{
- IDProperty *idprop = rna_idproperty_ui_container((PropertyRNA *)prop);
-
- if (idprop) {
- return IDP_GetPropertyTypeFromGroup(idprop, ((IDProperty *)prop)->name, IDP_GROUP);
- }
-
- return NULL;
-}
-
-/* return or create a UI local ID prop definition for this prop */
-static IDProperty *rna_idproperty_ui_ensure(PointerRNA *ptr, PropertyRNA *prop, bool create)
-{
- IDProperty *idprop = rna_idproperty_ui_container(prop);
- IDPropertyTemplate dummy = {0};
-
- if (idprop == NULL && create) {
- IDProperty *props = RNA_struct_idprops(ptr, false);
-
- /* Sanity check: props is the actual container of this property. */
- if (props != NULL && BLI_findindex(&props->data.group, prop) >= 0) {
- idprop = IDP_New(IDP_GROUP, &dummy, RNA_IDP_UI);
-
- if (!IDP_AddToGroup(props, idprop)) {
- IDP_FreePropertyContent(idprop);
- return NULL;
- }
- }
- }
-
- if (idprop) {
- const char *name = ((IDProperty *)prop)->name;
- IDProperty *rv = IDP_GetPropertyTypeFromGroup(idprop, name, IDP_GROUP);
-
- if (rv == NULL && create) {
- rv = IDP_New(IDP_GROUP, &dummy, name);
-
- if (!IDP_AddToGroup(idprop, rv)) {
- IDP_FreePropertyContent(rv);
- return NULL;
- }
- }
-
- return rv;
- }
-
- return NULL;
-}
-
-static bool rna_idproperty_ui_set_default(PointerRNA *ptr,
- PropertyRNA *prop,
- const char type,
- IDPropertyTemplate *value)
-{
- BLI_assert(ELEM(type, IDP_INT, IDP_DOUBLE));
-
- if (prop->magic == RNA_MAGIC) {
- return false;
- }
-
- /* attempt to get the local ID values */
- IDProperty *idp_ui = rna_idproperty_ui_ensure(ptr, prop, value != NULL);
-
- if (idp_ui == NULL) {
- return (value == NULL);
- }
-
- IDProperty *item = IDP_GetPropertyTypeFromGroup(idp_ui, "default", type);
-
- if (value == NULL) {
- if (item != NULL) {
- IDP_RemoveFromGroup(idp_ui, item);
- }
- }
- else {
- if (item != NULL) {
- switch (type) {
- case IDP_INT:
- IDP_Int(item) = value->i;
- break;
- case IDP_DOUBLE:
- IDP_Double(item) = value->d;
- break;
- default:
- BLI_assert(false);
- return false;
- }
- }
- else {
- item = IDP_New(type, value, "default");
-
- if (!IDP_AddToGroup(idp_ui, item)) {
- IDP_FreePropertyContent(item);
- return false;
- }
- }
- }
-
- return true;
-}
-
IDProperty **RNA_struct_idprops_p(PointerRNA *ptr)
{
StructRNA *type = ptr->type;
@@ -693,28 +569,17 @@ static const char *rna_ensure_property_identifier(const PropertyRNA *prop)
static const char *rna_ensure_property_description(const PropertyRNA *prop)
{
- const char *description = NULL;
-
if (prop->magic == RNA_MAGIC) {
- description = prop->description;
+ return prop->description;
}
- else {
- /* attempt to get the local ID values */
- const IDProperty *idp_ui = rna_idproperty_ui(prop);
-
- if (idp_ui) {
- IDProperty *item = IDP_GetPropertyTypeFromGroup(idp_ui, "description", IDP_STRING);
- if (item) {
- description = IDP_String(item);
- }
- }
- if (description == NULL) {
- description = ((IDProperty *)prop)->name; /* XXX: not correct. */
- }
+ const IDProperty *idprop = (const IDProperty *)prop;
+ if (idprop->ui_data) {
+ const IDPropertyUIData *ui_data = idprop->ui_data;
+ return ui_data->description;
}
- return description;
+ return "";
}
static const char *rna_ensure_property_name(const PropertyRNA *prop)
@@ -1196,19 +1061,9 @@ PropertySubType RNA_property_subtype(PropertyRNA *prop)
if (prop->magic != RNA_MAGIC) {
IDProperty *idprop = (IDProperty *)prop;
- if (ELEM(idprop->type, IDP_INT, IDP_FLOAT, IDP_DOUBLE) ||
- ((idprop->type == IDP_ARRAY) && ELEM(idprop->subtype, IDP_INT, IDP_FLOAT, IDP_DOUBLE))) {
- const IDProperty *idp_ui = rna_idproperty_ui(prop);
-
- if (idp_ui) {
- IDProperty *item = IDP_GetPropertyTypeFromGroup(idp_ui, "subtype", IDP_STRING);
-
- if (item) {
- int result = PROP_NONE;
- RNA_enum_value_from_id(rna_enum_property_subtype_items, IDP_String(item), &result);
- return (PropertySubType)result;
- }
- }
+ if (idprop->ui_data) {
+ IDPropertyUIData *ui_data = idprop->ui_data;
+ return (PropertySubType)ui_data->rna_subtype;
}
}
@@ -1387,20 +1242,17 @@ void RNA_property_int_range(PointerRNA *ptr, PropertyRNA *prop, int *hardmin, in
int softmin, softmax;
if (prop->magic != RNA_MAGIC) {
- /* attempt to get the local ID values */
- const IDProperty *idp_ui = rna_idproperty_ui(prop);
-
- if (idp_ui) {
- IDProperty *item;
-
- item = IDP_GetPropertyTypeFromGroup(idp_ui, "min", IDP_INT);
- *hardmin = item ? IDP_Int(item) : INT_MIN;
-
- item = IDP_GetPropertyTypeFromGroup(idp_ui, "max", IDP_INT);
- *hardmax = item ? IDP_Int(item) : INT_MAX;
-
- return;
+ const IDProperty *idprop = (IDProperty *)prop;
+ if (idprop->ui_data) {
+ IDPropertyUIDataInt *ui_data = (IDPropertyUIDataInt *)idprop->ui_data;
+ *hardmin = ui_data->min;
+ *hardmax = ui_data->max;
+ }
+ else {
+ *hardmin = INT_MIN;
+ *hardmax = INT_MAX;
}
+ return;
}
if (iprop->range) {
@@ -1428,23 +1280,19 @@ void RNA_property_int_ui_range(
int hardmin, hardmax;
if (prop->magic != RNA_MAGIC) {
- /* attempt to get the local ID values */
- const IDProperty *idp_ui = rna_idproperty_ui(prop);
-
- if (idp_ui) {
- IDProperty *item;
-
- item = IDP_GetPropertyTypeFromGroup(idp_ui, "soft_min", IDP_INT);
- *softmin = item ? IDP_Int(item) : INT_MIN;
-
- item = IDP_GetPropertyTypeFromGroup(idp_ui, "soft_max", IDP_INT);
- *softmax = item ? IDP_Int(item) : INT_MAX;
-
- item = IDP_GetPropertyTypeFromGroup(idp_ui, "step", IDP_INT);
- *step = item ? IDP_Int(item) : 1;
-
- return;
+ const IDProperty *idprop = (IDProperty *)prop;
+ if (idprop->ui_data) {
+ IDPropertyUIDataInt *ui_data_int = (IDPropertyUIDataInt *)idprop->ui_data;
+ *softmin = ui_data_int->soft_min;
+ *softmax = ui_data_int->soft_max;
+ *step = ui_data_int->step;
}
+ else {
+ *softmin = INT_MIN;
+ *softmax = INT_MAX;
+ *step = 1;
+ }
+ return;
}
*softmin = iprop->softmin;
@@ -1478,20 +1326,17 @@ void RNA_property_float_range(PointerRNA *ptr, PropertyRNA *prop, float *hardmin
float softmin, softmax;
if (prop->magic != RNA_MAGIC) {
- /* attempt to get the local ID values */
- const IDProperty *idp_ui = rna_idproperty_ui(prop);
-
- if (idp_ui) {
- IDProperty *item;
-
- item = IDP_GetPropertyTypeFromGroup(idp_ui, "min", IDP_DOUBLE);
- *hardmin = item ? (float)IDP_Double(item) : -FLT_MAX;
-
- item = IDP_GetPropertyTypeFromGroup(idp_ui, "max", IDP_DOUBLE);
- *hardmax = item ? (float)IDP_Double(item) : FLT_MAX;
-
- return;
+ const IDProperty *idprop = (IDProperty *)prop;
+ if (idprop->ui_data) {
+ IDPropertyUIDataFloat *ui_data = (IDPropertyUIDataFloat *)idprop->ui_data;
+ *hardmin = (float)ui_data->min;
+ *hardmax = (float)ui_data->max;
+ }
+ else {
+ *hardmin = -FLT_MAX;
+ *hardmax = FLT_MAX;
}
+ return;
}
if (fprop->range) {
@@ -1523,26 +1368,21 @@ void RNA_property_float_ui_range(PointerRNA *ptr,
float hardmin, hardmax;
if (prop->magic != RNA_MAGIC) {
- /* attempt to get the local ID values */
- const IDProperty *idp_ui = rna_idproperty_ui(prop);
-
- if (idp_ui) {
- IDProperty *item;
-
- item = IDP_GetPropertyTypeFromGroup(idp_ui, "soft_min", IDP_DOUBLE);
- *softmin = item ? (float)IDP_Double(item) : -FLT_MAX;
-
- item = IDP_GetPropertyTypeFromGroup(idp_ui, "soft_max", IDP_DOUBLE);
- *softmax = item ? (float)IDP_Double(item) : FLT_MAX;
-
- item = IDP_GetPropertyTypeFromGroup(idp_ui, "step", IDP_DOUBLE);
- *step = item ? (float)IDP_Double(item) : 1.0f;
-
- item = IDP_GetPropertyTypeFromGroup(idp_ui, "precision", IDP_DOUBLE);
- *precision = item ? (float)IDP_Double(item) : 3.0f;
-
- return;
+ const IDProperty *idprop = (IDProperty *)prop;
+ if (idprop->ui_data) {
+ IDPropertyUIDataFloat *ui_data = (IDPropertyUIDataFloat *)idprop->ui_data;
+ *softmin = (float)ui_data->soft_min;
+ *softmax = (float)ui_data->soft_max;
+ *step = ui_data->step;
+ *precision = (float)ui_data->precision;
+ }
+ else {
+ *softmin = -FLT_MAX;
+ *softmax = FLT_MAX;
+ *step = 1.0f;
+ *precision = 3.0f;
}
+ return;
}
*softmin = fprop->softmin;
@@ -2905,29 +2745,28 @@ int RNA_property_int_get_default(PointerRNA *UNUSED(ptr), PropertyRNA *prop)
IntPropertyRNA *iprop = (IntPropertyRNA *)rna_ensure_property(prop);
if (prop->magic != RNA_MAGIC) {
- /* attempt to get the local ID values */
- const IDProperty *idp_ui = rna_idproperty_ui(prop);
-
- if (idp_ui) {
- IDProperty *item;
-
- item = IDP_GetPropertyTypeFromGroup(idp_ui, "default", IDP_INT);
- return item ? IDP_Int(item) : iprop->defaultvalue;
+ const IDProperty *idprop = (const IDProperty *)prop;
+ if (idprop->ui_data) {
+ const IDPropertyUIDataInt *ui_data = (const IDPropertyUIDataInt *)idprop->ui_data;
+ return ui_data->default_value;
}
}
return iprop->defaultvalue;
}
-bool RNA_property_int_set_default(PointerRNA *ptr, PropertyRNA *prop, int value)
+bool RNA_property_int_set_default(PropertyRNA *prop, int value)
{
- if (value != 0) {
- IDPropertyTemplate val = {
- .i = value,
- };
- return rna_idproperty_ui_set_default(ptr, prop, IDP_INT, &val);
+ if (prop->magic == RNA_MAGIC) {
+ return false;
}
- return rna_idproperty_ui_set_default(ptr, prop, IDP_INT, NULL);
+
+ IDProperty *idprop = (IDProperty *)prop;
+ BLI_assert(idprop->type == IDP_INT);
+
+ IDPropertyUIDataInt *ui_data = (IDPropertyUIDataInt *)IDP_ui_data_ensure(idprop);
+ ui_data->default_value = value;
+ return true;
}
void RNA_property_int_get_default_array(PointerRNA *ptr, PropertyRNA *prop, int *values)
@@ -2940,17 +2779,22 @@ void RNA_property_int_get_default_array(PointerRNA *ptr, PropertyRNA *prop, int
if (prop->magic != RNA_MAGIC) {
int length = rna_ensure_property_array_length(ptr, prop);
- const IDProperty *idp_ui = rna_idproperty_ui(prop);
- IDProperty *item = idp_ui ? IDP_GetPropertyFromGroup(idp_ui, "default") : NULL;
-
- int defval = (item && item->type == IDP_INT) ? IDP_Int(item) : iprop->defaultvalue;
-
- if (item && item->type == IDP_ARRAY && item->subtype == IDP_INT) {
- rna_property_int_fill_default_array_values(
- IDP_Array(item), item->len, defval, length, values);
- }
- else {
- rna_property_int_fill_default_array_values(NULL, 0, defval, length, values);
+ const IDProperty *idprop = (const IDProperty *)prop;
+ if (idprop->ui_data) {
+ BLI_assert(idprop->type == IDP_ARRAY);
+ BLI_assert(idprop->subtype == IDP_INT);
+ const IDPropertyUIDataInt *ui_data = (const IDPropertyUIDataInt *)idprop->ui_data;
+ if (ui_data->default_array) {
+ rna_property_int_fill_default_array_values(ui_data->default_array,
+ ui_data->default_array_len,
+ ui_data->default_value,
+ length,
+ values);
+ }
+ else {
+ rna_property_int_fill_default_array_values(
+ NULL, 0, ui_data->default_value, length, values);
+ }
}
}
else if (prop->arraydimension == 0) {
@@ -3066,6 +2910,26 @@ static void rna_property_float_fill_default_array_values(
}
}
+/**
+ * The same logic as #rna_property_float_fill_default_array_values for a double array.
+ */
+static void rna_property_float_fill_default_array_values_double(const double *default_array,
+ const int default_array_len,
+ const double default_value,
+ const int out_length,
+ float *r_values)
+{
+ const int array_copy_len = MIN2(out_length, default_array_len);
+
+ for (int i = 0; i < array_copy_len; i++) {
+ r_values[i] = (float)default_array[i];
+ }
+
+ for (int i = array_copy_len; i < out_length; i++) {
+ r_values[i] = (float)default_value;
+ }
+}
+
static void rna_property_float_get_default_array_values(PointerRNA *ptr,
FloatPropertyRNA *fprop,
float *r_values)
@@ -3268,29 +3132,29 @@ float RNA_property_float_get_default(PointerRNA *UNUSED(ptr), PropertyRNA *prop)
BLI_assert(RNA_property_array_check(prop) == false);
if (prop->magic != RNA_MAGIC) {
- /* attempt to get the local ID values */
- const IDProperty *idp_ui = rna_idproperty_ui(prop);
-
- if (idp_ui) {
- IDProperty *item;
-
- item = IDP_GetPropertyTypeFromGroup(idp_ui, "default", IDP_DOUBLE);
- return item ? IDP_Double(item) : fprop->defaultvalue;
+ const IDProperty *idprop = (const IDProperty *)prop;
+ if (idprop->ui_data) {
+ BLI_assert(ELEM(idprop->type, IDP_FLOAT, IDP_DOUBLE));
+ const IDPropertyUIDataFloat *ui_data = (const IDPropertyUIDataFloat *)idprop->ui_data;
+ return (float)ui_data->default_value;
}
}
return fprop->defaultvalue;
}
-bool RNA_property_float_set_default(PointerRNA *ptr, PropertyRNA *prop, float value)
+bool RNA_property_float_set_default(PropertyRNA *prop, float value)
{
- if (value != 0) {
- IDPropertyTemplate val = {
- .d = value,
- };
- return rna_idproperty_ui_set_default(ptr, prop, IDP_DOUBLE, &val);
+ if (prop->magic == RNA_MAGIC) {
+ return false;
}
- return rna_idproperty_ui_set_default(ptr, prop, IDP_DOUBLE, NULL);
+
+ IDProperty *idprop = (IDProperty *)prop;
+ BLI_assert(idprop->type == IDP_FLOAT);
+
+ IDPropertyUIDataFloat *ui_data = (IDPropertyUIDataFloat *)IDP_ui_data_ensure(idprop);
+ ui_data->default_value = (double)value;
+ return true;
}
void RNA_property_float_get_default_array(PointerRNA *ptr, PropertyRNA *prop, float *values)
@@ -3303,23 +3167,16 @@ void RNA_property_float_get_default_array(PointerRNA *ptr, PropertyRNA *prop, fl
if (prop->magic != RNA_MAGIC) {
int length = rna_ensure_property_array_length(ptr, prop);
- const IDProperty *idp_ui = rna_idproperty_ui(prop);
- IDProperty *item = idp_ui ? IDP_GetPropertyFromGroup(idp_ui, "default") : NULL;
-
- float defval = (item && item->type == IDP_DOUBLE) ? IDP_Double(item) : fprop->defaultvalue;
-
- if (item && item->type == IDP_ARRAY && item->subtype == IDP_DOUBLE) {
- double *defarr = IDP_Array(item);
- for (int i = 0; i < length; i++) {
- values[i] = (i < item->len) ? (float)defarr[i] : defval;
- }
- }
- else if (item && item->type == IDP_ARRAY && item->subtype == IDP_FLOAT) {
- rna_property_float_fill_default_array_values(
- IDP_Array(item), item->len, defval, length, values);
- }
- else {
- rna_property_float_fill_default_array_values(NULL, 0, defval, length, values);
+ const IDProperty *idprop = (const IDProperty *)prop;
+ if (idprop->ui_data) {
+ BLI_assert(idprop->type == IDP_ARRAY);
+ BLI_assert(ELEM(idprop->subtype, IDP_FLOAT, IDP_DOUBLE));
+ const IDPropertyUIDataFloat *ui_data = (const IDPropertyUIDataFloat *)idprop->ui_data;
+ rna_property_float_fill_default_array_values_double(ui_data->default_array,
+ ui_data->default_array_len,
+ ui_data->default_value,
+ length,
+ values);
}
}
else if (prop->arraydimension == 0) {
@@ -3510,22 +3367,17 @@ void RNA_property_string_set_bytes(PointerRNA *ptr, PropertyRNA *prop, const cha
}
}
-void RNA_property_string_get_default(PointerRNA *UNUSED(ptr), PropertyRNA *prop, char *value)
+void RNA_property_string_get_default(PropertyRNA *prop, char *value, const int max_len)
{
StringPropertyRNA *sprop = (StringPropertyRNA *)rna_ensure_property(prop);
if (prop->magic != RNA_MAGIC) {
- /* attempt to get the local ID values */
- const IDProperty *idp_ui = rna_idproperty_ui(prop);
-
- if (idp_ui) {
- IDProperty *item;
-
- item = IDP_GetPropertyTypeFromGroup(idp_ui, "default", IDP_STRING);
- if (item) {
- strcpy(value, IDP_String(item));
- return;
- }
+ const IDProperty *idprop = (const IDProperty *)prop;
+ if (idprop->ui_data) {
+ BLI_assert(idprop->type == IDP_STRING);
+ const IDPropertyUIDataString *ui_data = (const IDPropertyUIDataString *)idprop->ui_data;
+ BLI_strncpy(value, ui_data->default_value, max_len);
+ return;
}
strcpy(value, "");
@@ -3554,7 +3406,7 @@ char *RNA_property_string_get_default_alloc(
buf = MEM_callocN(sizeof(char) * (length + 1), __func__);
}
- RNA_property_string_get_default(ptr, prop, buf);
+ RNA_property_string_get_default(prop, buf, length + 1);
if (r_len) {
*r_len = length;
@@ -3569,15 +3421,12 @@ int RNA_property_string_default_length(PointerRNA *UNUSED(ptr), PropertyRNA *pro
StringPropertyRNA *sprop = (StringPropertyRNA *)rna_ensure_property(prop);
if (prop->magic != RNA_MAGIC) {
- /* attempt to get the local ID values */
- const IDProperty *idp_ui = rna_idproperty_ui(prop);
-
- if (idp_ui) {
- IDProperty *item;
-
- item = IDP_GetPropertyTypeFromGroup(idp_ui, "default", IDP_STRING);
- if (item) {
- return strlen(IDP_String(item));
+ const IDProperty *idprop = (const IDProperty *)prop;
+ if (idprop->ui_data) {
+ BLI_assert(idprop->type == IDP_STRING);
+ const IDPropertyUIDataString *ui_data = (const IDPropertyUIDataString *)idprop->ui_data;
+ if (ui_data->default_value != NULL) {
+ return strlen(ui_data->default_value);
}
}
@@ -8200,12 +8049,12 @@ bool RNA_property_assign_default(PointerRNA *ptr, PropertyRNA *prop)
switch (RNA_property_type(prop)) {
case PROP_INT: {
int value = RNA_property_int_get(ptr, prop);
- return RNA_property_int_set_default(ptr, prop, value);
+ return RNA_property_int_set_default(prop, value);
}
case PROP_FLOAT: {
float value = RNA_property_float_get(ptr, prop);
- return RNA_property_float_set_default(ptr, prop, value);
+ return RNA_property_float_set_default(prop, value);
}
default:
diff --git a/source/blender/makesrna/intern/rna_internal_types.h b/source/blender/makesrna/intern/rna_internal_types.h
index 479306e8c06..22a9b9d930a 100644
--- a/source/blender/makesrna/intern/rna_internal_types.h
+++ b/source/blender/makesrna/intern/rna_internal_types.h
@@ -42,9 +42,6 @@ struct bContext;
typedef struct IDProperty IDProperty;
-/* store local properties here */
-#define RNA_IDP_UI "_RNA_UI"
-
/* Function Callbacks */
typedef void (*UpdateFunc)(struct Main *main, struct Scene *scene, struct PointerRNA *ptr);
diff --git a/source/blender/makesrna/intern/rna_modifier.c b/source/blender/makesrna/intern/rna_modifier.c
index 486d8d13564..f11d845c582 100644
--- a/source/blender/makesrna/intern/rna_modifier.c
+++ b/source/blender/makesrna/intern/rna_modifier.c
@@ -4431,6 +4431,12 @@ static void rna_def_modifier_mask(BlenderRNA *brna)
RNA_def_property_ui_text(prop, "Invert", "Use vertices that are not part of region defined");
RNA_def_property_update(prop, 0, "rna_Modifier_update");
+ prop = RNA_def_property(srna, "use_smooth", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flag", MOD_MASK_SMOOTH);
+ RNA_def_property_ui_text(
+ prop, "Smooth", "Use vertex group weights to cut faces at the weight contour");
+ RNA_def_property_update(prop, 0, "rna_Modifier_update");
+
prop = RNA_def_property(srna, "threshold", PROP_FLOAT, PROP_FACTOR);
RNA_def_property_float_sdna(prop, NULL, "threshold");
RNA_def_property_range(prop, 0.0, 1.0);
diff --git a/source/blender/makesrna/intern/rna_object.c b/source/blender/makesrna/intern/rna_object.c
index 0f6b89722a4..d3cd3158db1 100644
--- a/source/blender/makesrna/intern/rna_object.c
+++ b/source/blender/makesrna/intern/rna_object.c
@@ -1326,8 +1326,8 @@ static int rna_Object_rotation_4d_editable(PointerRNA *ptr, int index)
static int rna_MaterialSlot_index(PointerRNA *ptr)
{
- /* There is an offset of one, so that `ptr->data` is not null. */
- return POINTER_AS_INT(ptr->data) - 1;
+ /* There is an offset, so that `ptr->data` is not null and unique across IDs. */
+ return (uintptr_t)ptr->data - (uintptr_t)ptr->owner_id;
}
static int rna_MaterialSlot_material_editable(PointerRNA *ptr, const char **UNUSED(r_info))
@@ -1490,10 +1490,11 @@ static void rna_Object_material_slots_next(CollectionPropertyIterator *iter)
static PointerRNA rna_Object_material_slots_get(CollectionPropertyIterator *iter)
{
PointerRNA ptr;
- RNA_pointer_create((ID *)iter->internal.count.ptr,
+ ID *id = (ID *)iter->internal.count.ptr;
+ RNA_pointer_create(id,
&RNA_MaterialSlot,
- /* Add one, so that `ptr->data` is not null. */
- POINTER_FROM_INT(iter->internal.count.item + 1),
+ /* Add offset, so that `ptr->data` is not null and unique across IDs. */
+ (void *)(iter->internal.count.item + (uintptr_t)id),
&ptr);
return ptr;
}
diff --git a/source/blender/makesrna/intern/rna_scene.c b/source/blender/makesrna/intern/rna_scene.c
index 9d158761a21..0583cdeb1bb 100644
--- a/source/blender/makesrna/intern/rna_scene.c
+++ b/source/blender/makesrna/intern/rna_scene.c
@@ -3510,6 +3510,21 @@ static void rna_def_sequencer_tool_settings(BlenderRNA *brna)
{0, NULL, 0, NULL, NULL},
};
+ static const EnumPropertyItem scale_overlap_modes[] = {
+ {SEQ_OVERLAP_EXPAND, "EXPAND", 0, "Expand", "Move strips so transformed strips fits"},
+ {SEQ_OVERLAP_OVERWRITE,
+ "OVERWRITE",
+ 0,
+ "Overwrite",
+ "Trim or split strips to resolve overlap"},
+ {SEQ_OVERLAP_SHUFFLE,
+ "SHUFFLE",
+ 0,
+ "Shuffle",
+ "Move transformed strips to nearest free space to resolve overlap"},
+ {0, NULL, 0, NULL, NULL},
+ };
+
srna = RNA_def_struct(brna, "SequencerToolSettings", NULL);
RNA_def_struct_path_func(srna, "rna_SequencerToolSettings_path");
RNA_def_struct_ui_text(srna, "Sequencer Tool Settings", "");
@@ -3548,6 +3563,11 @@ static void rna_def_sequencer_tool_settings(BlenderRNA *brna)
RNA_def_property_int_default(prop, 15);
RNA_def_property_ui_range(prop, 0, 50, 1, 1);
RNA_def_property_ui_text(prop, "Snapping Distance", "Maximum distance for snapping in pixels");
+
+ /* Transform overlap handling. */
+ prop = RNA_def_property(srna, "overlap_mode", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_items(prop, scale_overlap_modes);
+ RNA_def_property_ui_text(prop, "Overlap Mode", "How to resolve overlap after transformation");
}
static void rna_def_unified_paint_settings(BlenderRNA *brna)
diff --git a/source/blender/modifiers/intern/MOD_mask.cc b/source/blender/modifiers/intern/MOD_mask.cc
index 9aa8e3dd7c8..306e79aa647 100644
--- a/source/blender/modifiers/intern/MOD_mask.cc
+++ b/source/blender/modifiers/intern/MOD_mask.cc
@@ -27,6 +27,7 @@
#include "BLI_ghash.h"
#include "BLI_listbase.h"
+#include "BLI_math.h"
#include "BLT_translation.h"
@@ -214,6 +215,40 @@ static void computed_masked_edges(const Mesh *mesh,
*r_num_masked_edges = num_masked_edges;
}
+static void computed_masked_edges_smooth(const Mesh *mesh,
+ Span<bool> vertex_mask,
+ MutableSpan<int> r_edge_map,
+ uint *r_num_masked_edges,
+ uint *r_num_add_vertices)
+{
+ BLI_assert(mesh->totedge == r_edge_map.size());
+
+ uint num_masked_edges = 0;
+ uint num_add_vertices = 0;
+ for (int i : IndexRange(mesh->totedge)) {
+ const MEdge &edge = mesh->medge[i];
+
+ /* only add if both verts will be in new mesh */
+ bool v1 = vertex_mask[edge.v1];
+ bool v2 = vertex_mask[edge.v2];
+ if (v1 && v2) {
+ r_edge_map[i] = num_masked_edges;
+ num_masked_edges++;
+ }
+ else if (v1 != v2) {
+ r_edge_map[i] = -2;
+ num_add_vertices++;
+ }
+ else {
+ r_edge_map[i] = -1;
+ }
+ }
+
+ num_masked_edges += num_add_vertices;
+ *r_num_masked_edges = num_masked_edges;
+ *r_num_add_vertices = num_add_vertices;
+}
+
static void computed_masked_polygons(const Mesh *mesh,
Span<bool> vertex_mask,
Vector<int> &r_masked_poly_indices,
@@ -224,7 +259,7 @@ static void computed_masked_polygons(const Mesh *mesh,
BLI_assert(mesh->totvert == vertex_mask.size());
r_masked_poly_indices.reserve(mesh->totpoly);
- r_loop_starts.reserve(mesh->totloop);
+ r_loop_starts.reserve(mesh->totpoly);
uint num_masked_loops = 0;
for (int i : IndexRange(mesh->totpoly)) {
@@ -250,6 +285,76 @@ static void computed_masked_polygons(const Mesh *mesh,
*r_num_masked_loops = num_masked_loops;
}
+static void compute_interpolated_polygons(const Mesh *mesh,
+ Span<bool> vertex_mask,
+ uint num_add_vertices,
+ uint num_masked_loops,
+ Vector<int> &r_masked_poly_indices,
+ Vector<int> &r_loop_starts,
+ uint *r_num_add_edges,
+ uint *r_num_add_polys,
+ uint *r_num_add_loops)
+{
+ BLI_assert(mesh->totvert == vertex_mask.size());
+
+ /* Can't really know ahead of time how much space to use exactly. Estimate limit instead. */
+ /* NOTE: this reserve can only lift the capacity if there are ngons, which get split. */
+ r_masked_poly_indices.reserve(r_masked_poly_indices.size() + num_add_vertices);
+ r_loop_starts.reserve(r_loop_starts.size() + num_add_vertices);
+
+ uint num_add_edges = 0;
+ uint num_add_polys = 0;
+ uint num_add_loops = 0;
+ for (int i : IndexRange(mesh->totpoly)) {
+ const MPoly &poly_src = mesh->mpoly[i];
+
+ int in_count = 0;
+ int start = -1;
+ int dst_totloop = -1;
+ Span<MLoop> loops_src(&mesh->mloop[poly_src.loopstart], poly_src.totloop);
+ for (const int j : loops_src.index_range()) {
+ const MLoop &loop = loops_src[j];
+ if (vertex_mask[loop.v]) {
+ in_count++;
+ }
+ else if (start == -1) {
+ start = j;
+ }
+ }
+ if (0 < in_count && in_count < poly_src.totloop) {
+ /* Ring search starting at a vertex which is not included in the mask. */
+ const MLoop *last_loop = &loops_src[start];
+ bool v_loop_in_mask_last = vertex_mask[last_loop->v];
+ for (const int j : loops_src.index_range()) {
+ const MLoop &loop = loops_src[(start + 1 + j) % poly_src.totloop];
+ const bool v_loop_in_mask = vertex_mask[loop.v];
+ if (v_loop_in_mask && !v_loop_in_mask_last) {
+ dst_totloop = 3;
+ }
+ else if (!v_loop_in_mask && v_loop_in_mask_last) {
+ BLI_assert(dst_totloop > 2);
+ r_masked_poly_indices.append(i);
+ r_loop_starts.append(num_masked_loops + num_add_loops);
+ num_add_loops += dst_totloop;
+ num_add_polys++;
+ num_add_edges++;
+ dst_totloop = -1;
+ }
+ else if (v_loop_in_mask && v_loop_in_mask_last) {
+ BLI_assert(dst_totloop > 2);
+ dst_totloop++;
+ }
+ last_loop = &loop;
+ v_loop_in_mask_last = v_loop_in_mask;
+ }
+ }
+ }
+
+ *r_num_add_edges = num_add_edges;
+ *r_num_add_polys = num_add_polys;
+ *r_num_add_loops = num_add_loops;
+}
+
void copy_masked_vertices_to_new_mesh(const Mesh &src_mesh, Mesh &dst_mesh, Span<int> vertex_map)
{
BLI_assert(src_mesh.totvert == vertex_map.size());
@@ -267,6 +372,89 @@ void copy_masked_vertices_to_new_mesh(const Mesh &src_mesh, Mesh &dst_mesh, Span
}
}
+static float get_interp_factor_from_vgroup(
+ MDeformVert *dvert, int defgrp_index, float threshold, uint v1, uint v2)
+{
+ /* NOTE: this calculation is done twice for every vertex,
+ * instead of storing it the first time and then reusing it. */
+ float value1 = BKE_defvert_find_weight(&dvert[v1], defgrp_index);
+ float value2 = BKE_defvert_find_weight(&dvert[v2], defgrp_index);
+ return (threshold - value1) / (value2 - value1);
+}
+
+static void add_interp_verts_copy_edges_to_new_mesh(const Mesh &src_mesh,
+ Mesh &dst_mesh,
+ Span<bool> vertex_mask,
+ Span<int> vertex_map,
+ MDeformVert *dvert,
+ int defgrp_index,
+ float threshold,
+ uint num_masked_edges,
+ uint num_add_verts,
+ MutableSpan<int> r_edge_map)
+{
+ BLI_assert(src_mesh.totvert == vertex_mask.size());
+ BLI_assert(src_mesh.totedge == r_edge_map.size());
+
+ uint vert_index = dst_mesh.totvert - num_add_verts;
+ uint edge_index = num_masked_edges - num_add_verts;
+ for (int i_src : IndexRange(src_mesh.totedge)) {
+ if (r_edge_map[i_src] != -1) {
+ int i_dst = r_edge_map[i_src];
+ if (i_dst == -2) {
+ i_dst = edge_index;
+ }
+ const MEdge &e_src = src_mesh.medge[i_src];
+ MEdge &e_dst = dst_mesh.medge[i_dst];
+
+ CustomData_copy_data(&src_mesh.edata, &dst_mesh.edata, i_src, i_dst, 1);
+ e_dst = e_src;
+ e_dst.v1 = vertex_map[e_src.v1];
+ e_dst.v2 = vertex_map[e_src.v2];
+ }
+ if (r_edge_map[i_src] == -2) {
+ const int i_dst = edge_index++;
+ r_edge_map[i_src] = i_dst;
+ const MEdge &e_src = src_mesh.medge[i_src];
+ /* Cut destination edge and make v1 the new vertex. */
+ MEdge &e_dst = dst_mesh.medge[i_dst];
+ if (!vertex_mask[e_src.v1]) {
+ e_dst.v1 = vert_index;
+ }
+ else {
+ BLI_assert(!vertex_mask[e_src.v2]);
+ e_dst.v2 = e_dst.v1;
+ e_dst.v1 = vert_index;
+ }
+ /* Create the new vertex. */
+ float fac = get_interp_factor_from_vgroup(
+ dvert, defgrp_index, threshold, e_src.v1, e_src.v2);
+
+ float weights[2] = {1.0f - fac, fac};
+ CustomData_interp(
+ &src_mesh.vdata, &dst_mesh.vdata, (int *)&e_src.v1, weights, nullptr, 2, vert_index);
+ MVert &v = dst_mesh.mvert[vert_index];
+ MVert &v1 = src_mesh.mvert[e_src.v1];
+ MVert &v2 = src_mesh.mvert[e_src.v2];
+
+ interp_v3_v3v3(v.co, v1.co, v2.co, fac);
+
+ float no1[3];
+ float no2[3];
+ normal_short_to_float_v3(no1, v1.no);
+ normal_short_to_float_v3(no2, v2.no);
+ mul_v3_fl(no1, weights[0]);
+ madd_v3_v3fl(no1, no2, weights[1]);
+ normalize_v3(no1);
+ normal_float_to_short_v3(v.no, no1);
+
+ vert_index++;
+ }
+ }
+ BLI_assert(vert_index == dst_mesh.totvert);
+ BLI_assert(edge_index == num_masked_edges);
+}
+
void copy_masked_edges_to_new_mesh(const Mesh &src_mesh,
Mesh &dst_mesh,
Span<int> vertex_map,
@@ -276,7 +464,7 @@ void copy_masked_edges_to_new_mesh(const Mesh &src_mesh,
BLI_assert(src_mesh.totedge == edge_map.size());
for (const int i_src : IndexRange(src_mesh.totedge)) {
const int i_dst = edge_map[i_src];
- if (i_dst == -1) {
+ if (i_dst == -1 || i_dst == -2) {
continue;
}
@@ -290,6 +478,36 @@ void copy_masked_edges_to_new_mesh(const Mesh &src_mesh,
}
}
+static void copy_masked_polys_to_new_mesh(const Mesh &src_mesh,
+ Mesh &dst_mesh,
+ Span<int> vertex_map,
+ Span<int> edge_map,
+ Span<int> masked_poly_indices,
+ Span<int> new_loop_starts,
+ int num_masked_polys)
+{
+ for (const int i_dst : IndexRange(num_masked_polys)) {
+ const int i_src = masked_poly_indices[i_dst];
+
+ const MPoly &mp_src = src_mesh.mpoly[i_src];
+ MPoly &mp_dst = dst_mesh.mpoly[i_dst];
+ const int i_ml_src = mp_src.loopstart;
+ const int i_ml_dst = new_loop_starts[i_dst];
+
+ CustomData_copy_data(&src_mesh.pdata, &dst_mesh.pdata, i_src, i_dst, 1);
+ CustomData_copy_data(&src_mesh.ldata, &dst_mesh.ldata, i_ml_src, i_ml_dst, mp_src.totloop);
+
+ const MLoop *ml_src = src_mesh.mloop + i_ml_src;
+ MLoop *ml_dst = dst_mesh.mloop + i_ml_dst;
+
+ mp_dst = mp_src;
+ mp_dst.loopstart = i_ml_dst;
+ for (int i : IndexRange(mp_src.totloop)) {
+ ml_dst[i].v = vertex_map[ml_src[i].v];
+ ml_dst[i].e = edge_map[ml_src[i].e];
+ }
+ }
+}
void copy_masked_polys_to_new_mesh(const Mesh &src_mesh,
Mesh &dst_mesh,
Span<int> vertex_map,
@@ -320,6 +538,139 @@ void copy_masked_polys_to_new_mesh(const Mesh &src_mesh,
}
}
+static void add_interpolated_polys_to_new_mesh(const Mesh &src_mesh,
+ Mesh &dst_mesh,
+ Span<bool> vertex_mask,
+ Span<int> vertex_map,
+ Span<int> edge_map,
+ MDeformVert *dvert,
+ int defgrp_index,
+ float threshold,
+ Span<int> masked_poly_indices,
+ Span<int> new_loop_starts,
+ int num_masked_polys,
+ int num_add_edges)
+{
+ int edge_index = dst_mesh.totedge - num_add_edges;
+ int sub_poly_index = 0;
+ int last_i_src = -1;
+ for (const int i_dst :
+ IndexRange(num_masked_polys, masked_poly_indices.size() - num_masked_polys)) {
+ const int i_src = masked_poly_indices[i_dst];
+ if (i_src == last_i_src) {
+ sub_poly_index++;
+ }
+ else {
+ sub_poly_index = 0;
+ last_i_src = i_src;
+ }
+
+ const MPoly &mp_src = src_mesh.mpoly[i_src];
+ MPoly &mp_dst = dst_mesh.mpoly[i_dst];
+ const int i_ml_src = mp_src.loopstart;
+ int i_ml_dst = new_loop_starts[i_dst];
+ const int mp_totloop = (i_dst + 1 < new_loop_starts.size() ? new_loop_starts[i_dst + 1] :
+ dst_mesh.totloop) -
+ i_ml_dst;
+
+ CustomData_copy_data(&src_mesh.pdata, &dst_mesh.pdata, i_src, i_dst, 1);
+
+ mp_dst = mp_src;
+ mp_dst.loopstart = i_ml_dst;
+ mp_dst.totloop = mp_totloop;
+
+ /* Ring search starting at a vertex which is not included in the mask. */
+ int start = -sub_poly_index - 1;
+ bool skip = false;
+ Span<MLoop> loops_src(&src_mesh.mloop[i_ml_src], mp_src.totloop);
+ for (const int j : loops_src.index_range()) {
+ if (!vertex_mask[loops_src[j].v]) {
+ if (start == -1) {
+ start = j;
+ break;
+ }
+ if (!skip) {
+ skip = true;
+ }
+ }
+ else if (skip) {
+ skip = false;
+ start++;
+ }
+ }
+
+ BLI_assert(start >= 0);
+ BLI_assert(edge_index < dst_mesh.totedge);
+
+ const MLoop *last_loop = &loops_src[start];
+ bool v_loop_in_mask_last = vertex_mask[last_loop->v];
+ int last_index = start;
+ for (const int j : loops_src.index_range()) {
+ const int index = (start + 1 + j) % mp_src.totloop;
+ const MLoop &loop = loops_src[index];
+ const bool v_loop_in_mask = vertex_mask[loop.v];
+ if (v_loop_in_mask && !v_loop_in_mask_last) {
+ /* Start new cut. */
+ float fac = get_interp_factor_from_vgroup(
+ dvert, defgrp_index, threshold, last_loop->v, loop.v);
+ float weights[2] = {1.0f - fac, fac};
+ int indices[2] = {i_ml_src + last_index, i_ml_src + index};
+ CustomData_interp(
+ &src_mesh.ldata, &dst_mesh.ldata, indices, weights, nullptr, 2, i_ml_dst);
+ MLoop &cut_dst_loop = dst_mesh.mloop[i_ml_dst];
+ cut_dst_loop.e = edge_map[last_loop->e];
+ cut_dst_loop.v = dst_mesh.medge[cut_dst_loop.e].v1;
+ i_ml_dst++;
+
+ CustomData_copy_data(&src_mesh.ldata, &dst_mesh.ldata, i_ml_src + index, i_ml_dst, 1);
+ MLoop &next_dst_loop = dst_mesh.mloop[i_ml_dst];
+ next_dst_loop.v = vertex_map[loop.v];
+ next_dst_loop.e = edge_map[loop.e];
+ i_ml_dst++;
+ }
+ else if (!v_loop_in_mask && v_loop_in_mask_last) {
+ BLI_assert(i_ml_dst != mp_dst.loopstart);
+ /* End active cut. */
+ float fac = get_interp_factor_from_vgroup(
+ dvert, defgrp_index, threshold, last_loop->v, loop.v);
+ float weights[2] = {1.0f - fac, fac};
+ int indices[2] = {i_ml_src + last_index, i_ml_src + index};
+ CustomData_interp(
+ &src_mesh.ldata, &dst_mesh.ldata, indices, weights, nullptr, 2, i_ml_dst);
+ MLoop &cut_dst_loop = dst_mesh.mloop[i_ml_dst];
+ cut_dst_loop.e = edge_index;
+ cut_dst_loop.v = dst_mesh.medge[edge_map[last_loop->e]].v1;
+ i_ml_dst++;
+
+ /* Create closing edge. */
+ MEdge &cut_edge = dst_mesh.medge[edge_index];
+ cut_edge.v1 = dst_mesh.mloop[mp_dst.loopstart].v;
+ cut_edge.v2 = cut_dst_loop.v;
+ BLI_assert(cut_edge.v1 != cut_edge.v2);
+ cut_edge.flag = ME_EDGEDRAW | ME_EDGERENDER;
+ edge_index++;
+
+ /* Only handle one of the cuts per iteration. */
+ break;
+ }
+ else if (v_loop_in_mask && v_loop_in_mask_last) {
+ BLI_assert(i_ml_dst != mp_dst.loopstart);
+ /* Extend active poly. */
+ CustomData_copy_data(&src_mesh.ldata, &dst_mesh.ldata, i_ml_src + index, i_ml_dst, 1);
+ MLoop &dst_loop = dst_mesh.mloop[i_ml_dst];
+ dst_loop.v = vertex_map[loop.v];
+ dst_loop.e = edge_map[loop.e];
+ i_ml_dst++;
+ }
+ last_loop = &loop;
+ last_index = index;
+ v_loop_in_mask_last = v_loop_in_mask;
+ }
+ BLI_assert(mp_dst.loopstart + mp_dst.totloop == i_ml_dst);
+ }
+ BLI_assert(edge_index == dst_mesh.totedge);
+}
+
/* Components of the algorithm:
* 1. Figure out which vertices should be present in the output mesh.
* 2. Find edges and polygons only using those vertices.
@@ -329,6 +680,8 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *UNUSED(ctx)
{
MaskModifierData *mmd = reinterpret_cast<MaskModifierData *>(md);
const bool invert_mask = mmd->flag & MOD_MASK_INV;
+ const bool use_interpolation = mmd->mode == MOD_MASK_MODE_VGROUP &&
+ (mmd->flag & MOD_MASK_SMOOTH);
/* Return empty or input mesh when there are no vertex groups. */
MDeformVert *dvert = (MDeformVert *)CustomData_get_layer(&mesh->vdata, CD_MDEFORMVERT);
@@ -342,6 +695,8 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *UNUSED(ctx)
return mesh;
}
+ int defgrp_index = -1;
+
Array<bool> vertex_mask;
if (mmd->mode == MOD_MASK_MODE_ARM) {
Object *armature_ob = mmd->ob_arm;
@@ -355,7 +710,8 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *UNUSED(ctx)
compute_vertex_mask__armature_mode(dvert, mesh, armature_ob, mmd->threshold, vertex_mask);
}
else {
- int defgrp_index = BKE_id_defgroup_name_index(&mesh->id, mmd->vgroup);
+ BLI_assert(mmd->mode == MOD_MASK_MODE_VGROUP);
+ defgrp_index = BKE_id_defgroup_name_index(&mesh->id, mmd->vgroup);
/* Return input mesh if the vertex group does not exist. */
if (defgrp_index == -1) {
@@ -376,7 +732,15 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *UNUSED(ctx)
Array<int> edge_map(mesh->totedge);
uint num_masked_edges;
- computed_masked_edges(mesh, vertex_mask, edge_map, &num_masked_edges);
+ uint num_add_vertices;
+ if (use_interpolation) {
+ computed_masked_edges_smooth(
+ mesh, vertex_mask, edge_map, &num_masked_edges, &num_add_vertices);
+ }
+ else {
+ computed_masked_edges(mesh, vertex_mask, edge_map, &num_masked_edges);
+ num_add_vertices = 0;
+ }
Vector<int> masked_poly_indices;
Vector<int> new_loop_starts;
@@ -389,13 +753,65 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *UNUSED(ctx)
&num_masked_polys,
&num_masked_loops);
- Mesh *result = BKE_mesh_new_nomain_from_template(
- mesh, num_masked_vertices, num_masked_edges, 0, num_masked_loops, num_masked_polys);
+ uint num_add_edges = 0;
+ uint num_add_polys = 0;
+ uint num_add_loops = 0;
+ if (use_interpolation) {
+ compute_interpolated_polygons(mesh,
+ vertex_mask,
+ num_add_vertices,
+ num_masked_loops,
+ masked_poly_indices,
+ new_loop_starts,
+ &num_add_edges,
+ &num_add_polys,
+ &num_add_loops);
+ }
+
+ Mesh *result = BKE_mesh_new_nomain_from_template(mesh,
+ num_masked_vertices + num_add_vertices,
+ num_masked_edges + num_add_edges,
+ 0,
+ num_masked_loops + num_add_loops,
+ num_masked_polys + num_add_polys);
copy_masked_vertices_to_new_mesh(*mesh, *result, vertex_map);
- copy_masked_edges_to_new_mesh(*mesh, *result, vertex_map, edge_map);
- copy_masked_polys_to_new_mesh(
- *mesh, *result, vertex_map, edge_map, masked_poly_indices, new_loop_starts);
+ if (use_interpolation) {
+ add_interp_verts_copy_edges_to_new_mesh(*mesh,
+ *result,
+ vertex_mask,
+ vertex_map,
+ dvert,
+ defgrp_index,
+ mmd->threshold,
+ num_masked_edges,
+ num_add_vertices,
+ edge_map);
+ }
+ else {
+ copy_masked_edges_to_new_mesh(*mesh, *result, vertex_map, edge_map);
+ }
+ copy_masked_polys_to_new_mesh(*mesh,
+ *result,
+ vertex_map,
+ edge_map,
+ masked_poly_indices,
+ new_loop_starts,
+ num_masked_polys);
+ if (use_interpolation) {
+ add_interpolated_polys_to_new_mesh(*mesh,
+ *result,
+ vertex_mask,
+ vertex_map,
+ edge_map,
+ dvert,
+ defgrp_index,
+ mmd->threshold,
+ masked_poly_indices,
+ new_loop_starts,
+ num_masked_polys,
+ num_add_edges);
+ }
BKE_mesh_calc_edges_loose(result);
/* Tag to recalculate normals later. */
@@ -441,6 +857,7 @@ static void panel_draw(const bContext *UNUSED(C), Panel *panel)
}
else if (mode == MOD_MASK_MODE_VGROUP) {
modifier_vgroup_ui(layout, ptr, &ob_ptr, "vertex_group", "invert_vertex_group", nullptr);
+ uiItemR(layout, ptr, "use_smooth", 0, nullptr, ICON_NONE);
}
uiItemR(layout, ptr, "threshold", 0, nullptr, ICON_NONE);
diff --git a/source/blender/modifiers/intern/MOD_nodes.cc b/source/blender/modifiers/intern/MOD_nodes.cc
index 620c7ef438a..9d8630b21e7 100644
--- a/source/blender/modifiers/intern/MOD_nodes.cc
+++ b/source/blender/modifiers/intern/MOD_nodes.cc
@@ -289,348 +289,178 @@ static bool logging_enabled(const ModifierEvalContext *ctx)
return true;
}
-/**
- * This code is responsible for creating the new property and also creating the group of
- * properties in the prop_ui_container group for the UI info, the mapping for which is
- * scattered about in RNA_access.c.
- *
- * TODO(Hans): Codify this with some sort of table or refactor IDProperty use in RNA_access.c.
- */
-struct SocketPropertyType {
- /* Create the actual property used to store the data for the modifier. */
- IDProperty *(*create_prop)(const bNodeSocket &socket, const char *name);
- /* Reused to build the "soft_min" property too. */
- IDProperty *(*create_min_ui_prop)(const bNodeSocket &socket, const char *name);
- /* Reused to build the "soft_max" property too. */
- IDProperty *(*create_max_ui_prop)(const bNodeSocket &socket, const char *name);
- /* This uses the same values as #create_prop, but sometimes the type is different, so it can't
- * be the same function. */
- IDProperty *(*create_default_ui_prop)(const bNodeSocket &socket, const char *name);
- PropertyType (*rna_subtype_get)(const bNodeSocket &socket);
- bool (*is_correct_type)(const IDProperty &property);
- void (*init_cpp_value)(const IDProperty &property, void *r_value);
-};
-
-static IDProperty *socket_add_property(IDProperty *settings_prop_group,
- IDProperty *ui_container,
- const SocketPropertyType &property_type,
- const bNodeSocket &socket)
+static IDProperty *id_property_create_from_socket(const bNodeSocket &socket)
{
- const char *new_prop_name = socket.identifier;
- /* Add the property actually storing the data to the modifier's group. */
- IDProperty *prop = property_type.create_prop(socket, new_prop_name);
- IDP_AddToGroup(settings_prop_group, prop);
-
- prop->flag |= IDP_FLAG_OVERRIDABLE_LIBRARY;
-
- /* Make the group in the UI container group to hold the property's UI settings. */
- IDProperty *prop_ui_group;
- {
- IDPropertyTemplate idprop = {0};
- prop_ui_group = IDP_New(IDP_GROUP, &idprop, new_prop_name);
- IDP_AddToGroup(ui_container, prop_ui_group);
- }
-
- /* Set property description (tooltip). */
- IDPropertyTemplate property_description_template;
- property_description_template.string.str = socket.description;
- property_description_template.string.len = BLI_strnlen(socket.description, MAX_NAME) + 1;
- property_description_template.string.subtype = IDP_STRING_SUB_UTF8;
- IDProperty *description = IDP_New(IDP_STRING, &property_description_template, "description");
- IDP_AddToGroup(prop_ui_group, description);
-
- /* Create the properties for the socket's UI settings. */
- if (property_type.create_min_ui_prop != nullptr) {
- IDP_AddToGroup(prop_ui_group, property_type.create_min_ui_prop(socket, "min"));
- IDP_AddToGroup(prop_ui_group, property_type.create_min_ui_prop(socket, "soft_min"));
- }
- if (property_type.create_max_ui_prop != nullptr) {
- IDP_AddToGroup(prop_ui_group, property_type.create_max_ui_prop(socket, "max"));
- IDP_AddToGroup(prop_ui_group, property_type.create_max_ui_prop(socket, "soft_max"));
- }
- if (property_type.create_default_ui_prop != nullptr) {
- IDP_AddToGroup(prop_ui_group, property_type.create_default_ui_prop(socket, "default"));
- }
- if (property_type.rna_subtype_get != nullptr) {
- const char *subtype_identifier = nullptr;
- RNA_enum_identifier(rna_enum_property_subtype_items,
- property_type.rna_subtype_get(socket),
- &subtype_identifier);
-
- if (subtype_identifier != nullptr) {
+ switch (socket.type) {
+ case SOCK_FLOAT: {
+ bNodeSocketValueFloat *value = (bNodeSocketValueFloat *)socket.default_value;
+ IDPropertyTemplate idprop = {0};
+ idprop.f = value->value;
+ IDProperty *property = IDP_New(IDP_FLOAT, &idprop, socket.identifier);
+ IDPropertyUIDataFloat *ui_data = (IDPropertyUIDataFloat *)IDP_ui_data_ensure(property);
+ ui_data->base.rna_subtype = value->subtype;
+ ui_data->min = ui_data->soft_min = (double)value->min;
+ ui_data->max = ui_data->soft_max = (double)value->max;
+ ui_data->default_value = value->value;
+ return property;
+ }
+ case SOCK_INT: {
+ bNodeSocketValueInt *value = (bNodeSocketValueInt *)socket.default_value;
+ IDPropertyTemplate idprop = {0};
+ idprop.i = value->value;
+ IDProperty *property = IDP_New(IDP_INT, &idprop, socket.identifier);
+ IDPropertyUIDataInt *ui_data = (IDPropertyUIDataInt *)IDP_ui_data_ensure(property);
+ ui_data->base.rna_subtype = value->subtype;
+ ui_data->min = ui_data->soft_min = value->min;
+ ui_data->max = ui_data->soft_max = value->max;
+ ui_data->default_value = value->value;
+ return property;
+ }
+ case SOCK_VECTOR: {
+ bNodeSocketValueVector *value = (bNodeSocketValueVector *)socket.default_value;
+ IDPropertyTemplate idprop = {0};
+ idprop.array.len = 3;
+ idprop.array.type = IDP_FLOAT;
+ IDProperty *property = IDP_New(IDP_ARRAY, &idprop, socket.identifier);
+ copy_v3_v3((float *)IDP_Array(property), value->value);
+ IDPropertyUIDataFloat *ui_data = (IDPropertyUIDataFloat *)IDP_ui_data_ensure(property);
+ ui_data->base.rna_subtype = value->subtype;
+ ui_data->min = ui_data->soft_min = (double)value->min;
+ ui_data->max = ui_data->soft_max = (double)value->max;
+ ui_data->default_array = (double *)MEM_mallocN(sizeof(double[3]), "mod_prop_default");
+ ui_data->default_array_len = 3;
+ for (int i = 3; i < 3; i++) {
+ ui_data->default_array[i] = (double)value->value[i];
+ }
+ return property;
+ }
+ case SOCK_BOOLEAN: {
+ bNodeSocketValueBoolean *value = (bNodeSocketValueBoolean *)socket.default_value;
IDPropertyTemplate idprop = {0};
- idprop.string.str = subtype_identifier;
- idprop.string.len = BLI_strnlen(subtype_identifier, MAX_NAME) + 1;
- IDP_AddToGroup(prop_ui_group, IDP_New(IDP_STRING, &idprop, "subtype"));
+ idprop.i = value->value != 0;
+ IDProperty *property = IDP_New(IDP_INT, &idprop, socket.identifier);
+ IDPropertyUIDataInt *ui_data = (IDPropertyUIDataInt *)IDP_ui_data_ensure(property);
+ ui_data->min = ui_data->soft_min = 0;
+ ui_data->max = ui_data->soft_max = 1;
+ ui_data->default_value = value->value != 0;
+ return property;
+ }
+ case SOCK_STRING: {
+ bNodeSocketValueString *value = (bNodeSocketValueString *)socket.default_value;
+ IDProperty *property = IDP_NewString(
+ value->value, socket.identifier, BLI_strnlen(value->value, sizeof(value->value)) + 1);
+ IDPropertyUIDataString *ui_data = (IDPropertyUIDataString *)IDP_ui_data_ensure(property);
+ ui_data->default_value = BLI_strdup(value->value);
+ return property;
+ }
+ case SOCK_OBJECT: {
+ bNodeSocketValueObject *value = (bNodeSocketValueObject *)socket.default_value;
+ IDPropertyTemplate idprop = {0};
+ idprop.id = (ID *)value->value;
+ return IDP_New(IDP_ID, &idprop, socket.identifier);
+ }
+ case SOCK_COLLECTION: {
+ bNodeSocketValueCollection *value = (bNodeSocketValueCollection *)socket.default_value;
+ IDPropertyTemplate idprop = {0};
+ idprop.id = (ID *)value->value;
+ return IDP_New(IDP_ID, &idprop, socket.identifier);
+ }
+ case SOCK_TEXTURE: {
+ bNodeSocketValueTexture *value = (bNodeSocketValueTexture *)socket.default_value;
+ IDPropertyTemplate idprop = {0};
+ idprop.id = (ID *)value->value;
+ return IDP_New(IDP_ID, &idprop, socket.identifier);
+ }
+ case SOCK_MATERIAL: {
+ bNodeSocketValueMaterial *value = (bNodeSocketValueMaterial *)socket.default_value;
+ IDPropertyTemplate idprop = {0};
+ idprop.id = (ID *)value->value;
+ return IDP_New(IDP_ID, &idprop, socket.identifier);
}
}
+ return nullptr;
+}
- return prop;
+static bool id_property_type_matches_socket(const bNodeSocket &socket, const IDProperty &property)
+{
+ switch (socket.type) {
+ case SOCK_FLOAT:
+ return ELEM(property.type, IDP_FLOAT, IDP_DOUBLE);
+ case SOCK_INT:
+ return property.type == IDP_INT;
+ case SOCK_VECTOR:
+ return property.type == IDP_ARRAY && property.subtype == IDP_FLOAT && property.len == 3;
+ case SOCK_BOOLEAN:
+ return property.type == IDP_INT;
+ case SOCK_STRING:
+ return property.type == IDP_STRING;
+ case SOCK_OBJECT:
+ case SOCK_COLLECTION:
+ case SOCK_TEXTURE:
+ case SOCK_MATERIAL:
+ return property.type == IDP_ID;
+ }
+ BLI_assert_unreachable();
+ return false;
}
-static const SocketPropertyType *get_socket_property_type(const bNodeSocket &bsocket)
+static void init_socket_cpp_value_from_property(const IDProperty &property,
+ const eNodeSocketDatatype socket_value_type,
+ void *r_value)
{
- switch (bsocket.type) {
+ switch (socket_value_type) {
case SOCK_FLOAT: {
- static const SocketPropertyType float_type = {
- [](const bNodeSocket &socket, const char *name) {
- bNodeSocketValueFloat *value = (bNodeSocketValueFloat *)socket.default_value;
- IDPropertyTemplate idprop = {0};
- idprop.f = value->value;
- return IDP_New(IDP_FLOAT, &idprop, name);
- },
- [](const bNodeSocket &socket, const char *name) {
- bNodeSocketValueFloat *value = (bNodeSocketValueFloat *)socket.default_value;
- IDPropertyTemplate idprop = {0};
- idprop.d = value->min;
- return IDP_New(IDP_DOUBLE, &idprop, name);
- },
- [](const bNodeSocket &socket, const char *name) {
- bNodeSocketValueFloat *value = (bNodeSocketValueFloat *)socket.default_value;
- IDPropertyTemplate idprop = {0};
- idprop.d = value->max;
- return IDP_New(IDP_DOUBLE, &idprop, name);
- },
- [](const bNodeSocket &socket, const char *name) {
- bNodeSocketValueFloat *value = (bNodeSocketValueFloat *)socket.default_value;
- IDPropertyTemplate idprop = {0};
- idprop.d = value->value;
- return IDP_New(IDP_DOUBLE, &idprop, name);
- },
- [](const bNodeSocket &socket) {
- return (PropertyType)((bNodeSocketValueFloat *)socket.default_value)->subtype;
- },
- [](const IDProperty &property) { return ELEM(property.type, IDP_FLOAT, IDP_DOUBLE); },
- [](const IDProperty &property, void *r_value) {
- if (property.type == IDP_FLOAT) {
- *(float *)r_value = IDP_Float(&property);
- }
- else if (property.type == IDP_DOUBLE) {
- *(float *)r_value = (float)IDP_Double(&property);
- }
- },
- };
- return &float_type;
+ if (property.type == IDP_FLOAT) {
+ *(float *)r_value = IDP_Float(&property);
+ }
+ else if (property.type == IDP_DOUBLE) {
+ *(float *)r_value = (float)IDP_Double(&property);
+ }
+ break;
}
case SOCK_INT: {
- static const SocketPropertyType int_type = {
- [](const bNodeSocket &socket, const char *name) {
- bNodeSocketValueInt *value = (bNodeSocketValueInt *)socket.default_value;
- IDPropertyTemplate idprop = {0};
- idprop.i = value->value;
- return IDP_New(IDP_INT, &idprop, name);
- },
- [](const bNodeSocket &socket, const char *name) {
- bNodeSocketValueInt *value = (bNodeSocketValueInt *)socket.default_value;
- IDPropertyTemplate idprop = {0};
- idprop.i = value->min;
- return IDP_New(IDP_INT, &idprop, name);
- },
- [](const bNodeSocket &socket, const char *name) {
- bNodeSocketValueInt *value = (bNodeSocketValueInt *)socket.default_value;
- IDPropertyTemplate idprop = {0};
- idprop.i = value->max;
- return IDP_New(IDP_INT, &idprop, name);
- },
- [](const bNodeSocket &socket, const char *name) {
- bNodeSocketValueInt *value = (bNodeSocketValueInt *)socket.default_value;
- IDPropertyTemplate idprop = {0};
- idprop.i = value->value;
- return IDP_New(IDP_INT, &idprop, name);
- },
- [](const bNodeSocket &socket) {
- return (PropertyType)((bNodeSocketValueInt *)socket.default_value)->subtype;
- },
- [](const IDProperty &property) { return property.type == IDP_INT; },
- [](const IDProperty &property, void *r_value) { *(int *)r_value = IDP_Int(&property); },
- };
- return &int_type;
+ *(int *)r_value = IDP_Int(&property);
+ break;
}
case SOCK_VECTOR: {
- static const SocketPropertyType vector_type = {
- [](const bNodeSocket &socket, const char *name) {
- bNodeSocketValueVector *value = (bNodeSocketValueVector *)socket.default_value;
- IDPropertyTemplate idprop = {0};
- idprop.array.len = 3;
- idprop.array.type = IDP_FLOAT;
- IDProperty *property = IDP_New(IDP_ARRAY, &idprop, name);
- copy_v3_v3((float *)IDP_Array(property), value->value);
- return property;
- },
- [](const bNodeSocket &socket, const char *name) {
- bNodeSocketValueVector *value = (bNodeSocketValueVector *)socket.default_value;
- IDPropertyTemplate idprop = {0};
- idprop.d = value->min;
- return IDP_New(IDP_DOUBLE, &idprop, name);
- },
- [](const bNodeSocket &socket, const char *name) {
- bNodeSocketValueVector *value = (bNodeSocketValueVector *)socket.default_value;
- IDPropertyTemplate idprop = {0};
- idprop.d = value->max;
- return IDP_New(IDP_DOUBLE, &idprop, name);
- },
- [](const bNodeSocket &socket, const char *name) {
- bNodeSocketValueVector *value = (bNodeSocketValueVector *)socket.default_value;
- IDPropertyTemplate idprop = {0};
- idprop.array.len = 3;
- idprop.array.type = IDP_FLOAT;
- IDProperty *property = IDP_New(IDP_ARRAY, &idprop, name);
- copy_v3_v3((float *)IDP_Array(property), value->value);
- return property;
- },
- [](const bNodeSocket &socket) {
- return (PropertyType)((bNodeSocketValueVector *)socket.default_value)->subtype;
- },
- [](const IDProperty &property) {
- return property.type == IDP_ARRAY && property.subtype == IDP_FLOAT &&
- property.len == 3;
- },
- [](const IDProperty &property, void *r_value) {
- copy_v3_v3((float *)r_value, (const float *)IDP_Array(&property));
- },
- };
- return &vector_type;
+ copy_v3_v3((float *)r_value, (const float *)IDP_Array(&property));
+ break;
}
case SOCK_BOOLEAN: {
- static const SocketPropertyType boolean_type = {
- [](const bNodeSocket &socket, const char *name) {
- bNodeSocketValueBoolean *value = (bNodeSocketValueBoolean *)socket.default_value;
- IDPropertyTemplate idprop = {0};
- idprop.i = value->value != 0;
- return IDP_New(IDP_INT, &idprop, name);
- },
- [](const bNodeSocket &UNUSED(socket), const char *name) {
- IDPropertyTemplate idprop = {0};
- idprop.i = 0;
- return IDP_New(IDP_INT, &idprop, name);
- },
- [](const bNodeSocket &UNUSED(socket), const char *name) {
- IDPropertyTemplate idprop = {0};
- idprop.i = 1;
- return IDP_New(IDP_INT, &idprop, name);
- },
- [](const bNodeSocket &socket, const char *name) {
- bNodeSocketValueBoolean *value = (bNodeSocketValueBoolean *)socket.default_value;
- IDPropertyTemplate idprop = {0};
- idprop.i = value->value != 0;
- return IDP_New(IDP_INT, &idprop, name);
- },
- nullptr,
- [](const IDProperty &property) { return property.type == IDP_INT; },
- [](const IDProperty &property, void *r_value) {
- *(bool *)r_value = IDP_Int(&property) != 0;
- },
- };
- return &boolean_type;
+ *(bool *)r_value = IDP_Int(&property) != 0;
+ break;
}
case SOCK_STRING: {
- static const SocketPropertyType string_type = {
- [](const bNodeSocket &socket, const char *name) {
- bNodeSocketValueString *value = (bNodeSocketValueString *)socket.default_value;
- return IDP_NewString(
- value->value, name, BLI_strnlen(value->value, sizeof(value->value)) + 1);
- },
- nullptr,
- nullptr,
- [](const bNodeSocket &socket, const char *name) {
- bNodeSocketValueString *value = (bNodeSocketValueString *)socket.default_value;
- return IDP_NewString(
- value->value, name, BLI_strnlen(value->value, sizeof(value->value)) + 1);
- },
- nullptr,
- [](const IDProperty &property) { return property.type == IDP_STRING; },
- [](const IDProperty &property, void *r_value) {
- new (r_value) std::string(IDP_String(&property));
- },
- };
- return &string_type;
+ new (r_value) std::string(IDP_String(&property));
+ break;
}
case SOCK_OBJECT: {
- static const SocketPropertyType object_type = {
- [](const bNodeSocket &socket, const char *name) {
- bNodeSocketValueObject *value = (bNodeSocketValueObject *)socket.default_value;
- IDPropertyTemplate idprop = {0};
- idprop.id = (ID *)value->value;
- return IDP_New(IDP_ID, &idprop, name);
- },
- nullptr,
- nullptr,
- nullptr,
- nullptr,
- [](const IDProperty &property) { return property.type == IDP_ID; },
- [](const IDProperty &property, void *r_value) {
- ID *id = IDP_Id(&property);
- Object *object = (id && GS(id->name) == ID_OB) ? (Object *)id : nullptr;
- *(Object **)r_value = object;
- },
- };
- return &object_type;
+ ID *id = IDP_Id(&property);
+ Object *object = (id && GS(id->name) == ID_OB) ? (Object *)id : nullptr;
+ *(Object **)r_value = object;
+ break;
}
case SOCK_COLLECTION: {
- static const SocketPropertyType collection_type = {
- [](const bNodeSocket &socket, const char *name) {
- bNodeSocketValueCollection *value = (bNodeSocketValueCollection *)socket.default_value;
- IDPropertyTemplate idprop = {0};
- idprop.id = (ID *)value->value;
- return IDP_New(IDP_ID, &idprop, name);
- },
- nullptr,
- nullptr,
- nullptr,
- nullptr,
- [](const IDProperty &property) { return property.type == IDP_ID; },
- [](const IDProperty &property, void *r_value) {
- ID *id = IDP_Id(&property);
- Collection *collection = (id && GS(id->name) == ID_GR) ? (Collection *)id : nullptr;
- *(Collection **)r_value = collection;
- },
- };
- return &collection_type;
+ ID *id = IDP_Id(&property);
+ Collection *collection = (id && GS(id->name) == ID_GR) ? (Collection *)id : nullptr;
+ *(Collection **)r_value = collection;
+ break;
}
case SOCK_TEXTURE: {
- static const SocketPropertyType texture_type = {
- [](const bNodeSocket &socket, const char *name) {
- bNodeSocketValueTexture *value = (bNodeSocketValueTexture *)socket.default_value;
- IDPropertyTemplate idprop = {0};
- idprop.id = (ID *)value->value;
- return IDP_New(IDP_ID, &idprop, name);
- },
- nullptr,
- nullptr,
- nullptr,
- nullptr,
- [](const IDProperty &property) { return property.type == IDP_ID; },
- [](const IDProperty &property, void *r_value) {
- ID *id = IDP_Id(&property);
- Tex *texture = (id && GS(id->name) == ID_TE) ? (Tex *)id : nullptr;
- *(Tex **)r_value = texture;
- },
- };
- return &texture_type;
+ ID *id = IDP_Id(&property);
+ Tex *texture = (id && GS(id->name) == ID_TE) ? (Tex *)id : nullptr;
+ *(Tex **)r_value = texture;
+ break;
}
case SOCK_MATERIAL: {
- static const SocketPropertyType material_type = {
- [](const bNodeSocket &socket, const char *name) {
- bNodeSocketValueMaterial *value = (bNodeSocketValueMaterial *)socket.default_value;
- IDPropertyTemplate idprop = {0};
- idprop.id = (ID *)value->value;
- return IDP_New(IDP_ID, &idprop, name);
- },
- nullptr,
- nullptr,
- nullptr,
- nullptr,
- [](const IDProperty &property) { return property.type == IDP_ID; },
- [](const IDProperty &property, void *r_value) {
- ID *id = IDP_Id(&property);
- Material *material = (id && GS(id->name) == ID_MA) ? (Material *)id : nullptr;
- *(Material **)r_value = material;
- },
- };
- return &material_type;
+ ID *id = IDP_Id(&property);
+ Material *material = (id && GS(id->name) == ID_MA) ? (Material *)id : nullptr;
+ *(Material **)r_value = material;
+ break;
}
default: {
- return nullptr;
+ BLI_assert_unreachable();
+ break;
}
}
}
@@ -647,32 +477,39 @@ void MOD_nodes_update_interface(Object *object, NodesModifierData *nmd)
}
IDProperty *old_properties = nmd->settings.properties;
-
{
IDPropertyTemplate idprop = {0};
nmd->settings.properties = IDP_New(IDP_GROUP, &idprop, "Nodes Modifier Settings");
}
- IDProperty *ui_container_group;
- {
- IDPropertyTemplate idprop = {0};
- ui_container_group = IDP_New(IDP_GROUP, &idprop, "_RNA_UI");
- IDP_AddToGroup(nmd->settings.properties, ui_container_group);
- }
-
LISTBASE_FOREACH (bNodeSocket *, socket, &nmd->node_group->inputs) {
- const SocketPropertyType *property_type = get_socket_property_type(*socket);
- if (property_type == nullptr) {
+ IDProperty *new_prop = id_property_create_from_socket(*socket);
+ if (new_prop == nullptr) {
+ /* Out of the set of supported input sockets, only
+ * geometry sockets aren't added to the modifier. */
+ BLI_assert(socket->type == SOCK_GEOMETRY);
continue;
}
- IDProperty *new_prop = socket_add_property(
- nmd->settings.properties, ui_container_group, *property_type, *socket);
+ new_prop->flag |= IDP_FLAG_OVERRIDABLE_LIBRARY;
+ if (socket->description[0] != '\0') {
+ IDPropertyUIData *ui_data = IDP_ui_data_ensure(new_prop);
+ ui_data->description = BLI_strdup(socket->description);
+ }
+ IDP_AddToGroup(nmd->settings.properties, new_prop);
if (old_properties != nullptr) {
IDProperty *old_prop = IDP_GetPropertyFromGroup(old_properties, socket->identifier);
- if (old_prop != nullptr && property_type->is_correct_type(*old_prop)) {
+ if (old_prop != nullptr && id_property_type_matches_socket(*socket, *old_prop)) {
+ /* #IDP_CopyPropertyContent replaces the UI data as well, which we don't (we only
+ * want to replace the values). So release it temporarily and replace it after. */
+ IDPropertyUIData *ui_data = new_prop->ui_data;
+ new_prop->ui_data = nullptr;
IDP_CopyPropertyContent(new_prop, old_prop);
+ if (new_prop->ui_data != nullptr) {
+ IDP_ui_data_free(new_prop);
+ }
+ new_prop->ui_data = ui_data;
}
}
}
@@ -713,14 +550,8 @@ void MOD_nodes_init(Main *bmain, NodesModifierData *nmd)
static void initialize_group_input(NodesModifierData &nmd,
const bNodeSocket &socket,
- const CPPType &cpp_type,
void *r_value)
{
- const SocketPropertyType *property_type = get_socket_property_type(socket);
- if (property_type == nullptr) {
- cpp_type.copy_construct(cpp_type.default_value(), r_value);
- return;
- }
if (nmd.settings.properties == nullptr) {
socket.typeinfo->get_geometry_nodes_cpp_value(socket, r_value);
return;
@@ -731,11 +562,13 @@ static void initialize_group_input(NodesModifierData &nmd,
socket.typeinfo->get_geometry_nodes_cpp_value(socket, r_value);
return;
}
- if (!property_type->is_correct_type(*property)) {
+ if (!id_property_type_matches_socket(socket, *property)) {
socket.typeinfo->get_geometry_nodes_cpp_value(socket, r_value);
return;
}
- property_type->init_cpp_value(*property, r_value);
+
+ init_socket_cpp_value_from_property(
+ *property, static_cast<eNodeSocketDatatype>(socket.type), r_value);
}
static Vector<SpaceSpreadsheet *> find_spreadsheet_editors(Main *bmain)
@@ -886,7 +719,7 @@ static GeometrySet compute_geometry(const DerivedNodeTree &tree,
for (const OutputSocketRef *socket : remaining_input_sockets) {
const CPPType &cpp_type = *socket->typeinfo()->get_geometry_nodes_cpp_type();
void *value_in = allocator.allocate(cpp_type.size(), cpp_type.alignment());
- initialize_group_input(*nmd, *socket->bsocket(), cpp_type, value_in);
+ initialize_group_input(*nmd, *socket->bsocket(), value_in);
group_inputs.add_new({root_context, socket}, {cpp_type, value_in});
}
}
@@ -954,8 +787,7 @@ static void check_property_socket_sync(const Object *ob, ModifierData *md)
continue;
}
- const SocketPropertyType *property_type = get_socket_property_type(*socket);
- if (!property_type->is_correct_type(*property)) {
+ if (!id_property_type_matches_socket(*socket, *property)) {
BKE_modifier_set_error(
ob, md, "Property type does not match input socket \"(%s)\"", socket->name);
continue;
@@ -1051,17 +883,12 @@ static void draw_property_for_socket(uiLayout *layout,
const IDProperty *modifier_props,
const bNodeSocket &socket)
{
- const SocketPropertyType *property_type = get_socket_property_type(socket);
- if (property_type == nullptr) {
- return;
- }
-
/* The property should be created in #MOD_nodes_update_interface with the correct type. */
IDProperty *property = IDP_GetPropertyFromGroup(modifier_props, socket.identifier);
/* IDProperties can be removed with python, so there could be a situation where
* there isn't a property for a socket or it doesn't have the correct type. */
- if (property == nullptr || !property_type->is_correct_type(*property)) {
+ if (property == nullptr || !id_property_type_matches_socket(socket, *property)) {
return;
}
diff --git a/source/blender/python/generic/CMakeLists.txt b/source/blender/python/generic/CMakeLists.txt
index dce6a7e1c91..10cc1e8b113 100644
--- a/source/blender/python/generic/CMakeLists.txt
+++ b/source/blender/python/generic/CMakeLists.txt
@@ -21,6 +21,7 @@ set(INC
../../blenlib
../../gpu
../../makesdna
+ ../../makesrna
../../../../intern/glew-mx
../../../../intern/guardedalloc
)
@@ -36,6 +37,7 @@ set(SRC
blf_py_api.c
bpy_threads.c
idprop_py_api.c
+ idprop_py_ui_api.c
imbuf_py_api.c
py_capi_utils.c
@@ -43,6 +45,7 @@ set(SRC
bl_math_py_api.h
blf_py_api.h
idprop_py_api.h
+ idprop_py_ui_api.h
imbuf_py_api.h
py_capi_utils.h
diff --git a/source/blender/python/generic/idprop_py_api.c b/source/blender/python/generic/idprop_py_api.c
index 024900db691..58a947943b1 100644
--- a/source/blender/python/generic/idprop_py_api.c
+++ b/source/blender/python/generic/idprop_py_api.c
@@ -25,6 +25,7 @@
#include "BLI_utildefines.h"
#include "idprop_py_api.h"
+#include "idprop_py_ui_api.h"
#include "BKE_idprop.h"
@@ -2135,6 +2136,7 @@ static PyObject *BPyInit_idprop_types(void)
submodule = PyModule_Create(&IDProp_types_module_def);
IDProp_Init_Types();
+ IDPropertyUIData_Init_Types();
/* bmesh_py_types.c */
PyModule_AddType(submodule, &BPy_IDGroup_Type);
diff --git a/source/blender/python/generic/idprop_py_ui_api.c b/source/blender/python/generic/idprop_py_ui_api.c
new file mode 100644
index 00000000000..92cc3b8e1dd
--- /dev/null
+++ b/source/blender/python/generic/idprop_py_ui_api.c
@@ -0,0 +1,734 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+/** \file
+ * \ingroup pygen
+ */
+
+#include <Python.h>
+
+#include "MEM_guardedalloc.h"
+
+#include "BLI_string.h"
+#include "BLI_utildefines.h"
+
+#include "idprop_py_ui_api.h"
+
+#include "BKE_idprop.h"
+
+#include "DNA_ID.h"
+
+#include "RNA_access.h"
+#include "RNA_enum_types.h"
+
+#include "../intern/bpy_rna.h"
+
+#define USE_STRING_COERCE
+
+#ifdef USE_STRING_COERCE
+# include "py_capi_utils.h"
+#endif
+
+#include "python_utildefines.h"
+
+/* -------------------------------------------------------------------- */
+/** \name UI Data Update
+ * \{ */
+
+static bool args_contain_key(PyObject *kwargs, const char *name)
+{
+ PyObject *py_key = PyUnicode_FromString(name);
+ const bool result = PyDict_Contains(kwargs, py_key) == 1;
+ Py_DECREF(py_key);
+ return result;
+}
+
+/**
+ * \return False when parsing fails, in which case caller should return NULL.
+ */
+static bool idprop_ui_data_update_base(IDPropertyUIData *ui_data,
+ const char *rna_subtype,
+ const char *description)
+{
+ if (rna_subtype != NULL) {
+ if (pyrna_enum_value_from_id(rna_enum_property_subtype_items,
+ rna_subtype,
+ &ui_data->rna_subtype,
+ "IDPropertyUIManager.update") == -1) {
+ return false;
+ }
+ }
+
+ if (description != NULL) {
+ ui_data->description = BLI_strdup(description);
+ }
+
+ return true;
+}
+
+/**
+ * \note The default value needs special handling because for array IDProperties it can
+ * be a single value or an array, but for non-array properties it can only be a value.
+ */
+static bool idprop_ui_data_update_int_default(IDProperty *idprop,
+ IDPropertyUIDataInt *ui_data,
+ PyObject *default_value)
+{
+ if (PySequence_Check(default_value)) {
+ if (idprop->type != IDP_ARRAY) {
+ PyErr_SetString(PyExc_TypeError, "Only array properties can have array default values");
+ return false;
+ }
+
+ Py_ssize_t len = PySequence_Size(default_value);
+ int *new_default_array = (int *)MEM_malloc_arrayN(len, sizeof(int), __func__);
+ if (PyC_AsArray(
+ new_default_array, sizeof(int), default_value, len, &PyLong_Type, "ui_data_update") ==
+ -1) {
+ MEM_freeN(new_default_array);
+ return false;
+ }
+
+ ui_data->default_array_len = len;
+ ui_data->default_array = new_default_array;
+ }
+ else {
+ const int value = PyC_Long_AsI32(default_value);
+ if ((value == -1) && PyErr_Occurred()) {
+ PyErr_SetString(PyExc_ValueError, "Error converting \"default\" argument to integer");
+ return false;
+ }
+ ui_data->default_value = value;
+ }
+
+ return true;
+}
+
+/**
+ * \return False when parsing fails, in which case caller should return NULL.
+ */
+static bool idprop_ui_data_update_int(IDProperty *idprop, PyObject *args, PyObject *kwargs)
+{
+ const char *rna_subtype = NULL;
+ const char *description = NULL;
+ int min, max, soft_min, soft_max, step;
+ PyObject *default_value = NULL;
+ const char *kwlist[] = {
+ "min", "max", "soft_min", "soft_max", "step", "default", "subtype", "description", NULL};
+ if (!PyArg_ParseTupleAndKeywords(args,
+ kwargs,
+ "|$iiiiiOzz:update",
+ (char **)kwlist,
+ &min,
+ &max,
+ &soft_min,
+ &soft_max,
+ &step,
+ &default_value,
+ &rna_subtype,
+ &description)) {
+ return false;
+ }
+
+ /* Write to a temporary copy of the UI data in case some part of the parsing fails. */
+ IDPropertyUIDataInt *ui_data_orig = (IDPropertyUIDataInt *)idprop->ui_data;
+ IDPropertyUIDataInt ui_data = *ui_data_orig;
+
+ if (!idprop_ui_data_update_base(&ui_data.base, rna_subtype, description)) {
+ IDP_ui_data_free_unique_contents(&ui_data.base, IDP_ui_data_type(idprop), &ui_data_orig->base);
+ return false;
+ }
+
+ if (args_contain_key(kwargs, "min")) {
+ ui_data.min = min;
+ ui_data.soft_min = MAX2(ui_data.soft_min, ui_data.min);
+ ui_data.max = MAX2(ui_data.min, ui_data.max);
+ }
+ if (args_contain_key(kwargs, "max")) {
+ ui_data.max = max;
+ ui_data.soft_max = MIN2(ui_data.soft_max, ui_data.max);
+ ui_data.min = MIN2(ui_data.min, ui_data.max);
+ }
+ if (args_contain_key(kwargs, "soft_min")) {
+ ui_data.soft_min = soft_min;
+ ui_data.soft_min = MAX2(ui_data.soft_min, ui_data.min);
+ ui_data.soft_max = MAX2(ui_data.soft_min, ui_data.soft_max);
+ }
+ if (args_contain_key(kwargs, "soft_max")) {
+ ui_data.soft_max = soft_max;
+ ui_data.soft_max = MIN2(ui_data.soft_max, ui_data.max);
+ ui_data.soft_min = MIN2(ui_data.soft_min, ui_data.soft_max);
+ }
+ if (args_contain_key(kwargs, "step")) {
+ ui_data.step = step;
+ }
+
+ if (!ELEM(default_value, NULL, Py_None)) {
+ if (!idprop_ui_data_update_int_default(idprop, &ui_data, default_value)) {
+ IDP_ui_data_free_unique_contents(
+ &ui_data.base, IDP_ui_data_type(idprop), &ui_data_orig->base);
+ return false;
+ }
+ }
+
+ /* Write back to the proeprty's UI data. */
+ IDP_ui_data_free_unique_contents(&ui_data_orig->base, IDP_ui_data_type(idprop), &ui_data.base);
+ *ui_data_orig = ui_data;
+ return true;
+}
+
+/**
+ * \note The default value needs special handling because for array IDProperties it can
+ * be a single value or an array, but for non-array properties it can only be a value.
+ */
+static bool idprop_ui_data_update_float_default(IDProperty *idprop,
+ IDPropertyUIDataFloat *ui_data,
+ PyObject *default_value)
+{
+ if (PySequence_Check(default_value)) {
+ if (idprop->type != IDP_ARRAY) {
+ PyErr_SetString(PyExc_TypeError, "Only array properties can have array default values");
+ return false;
+ }
+
+ Py_ssize_t len = PySequence_Size(default_value);
+ double *new_default_array = (double *)MEM_malloc_arrayN(len, sizeof(double), __func__);
+ if (PyC_AsArray(new_default_array,
+ sizeof(double),
+ default_value,
+ len,
+ &PyFloat_Type,
+ "ui_data_update") == -1) {
+ MEM_freeN(new_default_array);
+ return false;
+ }
+
+ ui_data->default_array_len = len;
+ ui_data->default_array = new_default_array;
+ }
+ else {
+ const double value = PyFloat_AsDouble(default_value);
+ if ((value == -1.0) && PyErr_Occurred()) {
+ PyErr_SetString(PyExc_ValueError, "Error converting \"default\" argument to double");
+ return false;
+ }
+ ui_data->default_value = value;
+ }
+
+ return true;
+}
+
+/**
+ * \return False when parsing fails, in which case caller should return NULL.
+ */
+static bool idprop_ui_data_update_float(IDProperty *idprop, PyObject *args, PyObject *kwargs)
+{
+ const char *rna_subtype = NULL;
+ const char *description = NULL;
+ int precision;
+ double min, max, soft_min, soft_max, step;
+ PyObject *default_value = NULL;
+ const char *kwlist[] = {"min",
+ "max",
+ "soft_min",
+ "soft_max",
+ "step",
+ "precision",
+ "default",
+ "subtype",
+ "description",
+ NULL};
+ if (!PyArg_ParseTupleAndKeywords(args,
+ kwargs,
+ "|$dddddiOzz:update",
+ (char **)kwlist,
+ &min,
+ &max,
+ &soft_min,
+ &soft_max,
+ &step,
+ &precision,
+ &default_value,
+ &rna_subtype,
+ &description)) {
+ return false;
+ }
+
+ /* Write to a temporary copy of the UI data in case some part of the parsing fails. */
+ IDPropertyUIDataFloat *ui_data_orig = (IDPropertyUIDataFloat *)idprop->ui_data;
+ IDPropertyUIDataFloat ui_data = *ui_data_orig;
+
+ if (!idprop_ui_data_update_base(&ui_data.base, rna_subtype, description)) {
+ IDP_ui_data_free_unique_contents(&ui_data.base, IDP_ui_data_type(idprop), &ui_data_orig->base);
+ return false;
+ }
+
+ if (args_contain_key(kwargs, "min")) {
+ ui_data.min = min;
+ ui_data.soft_min = MAX2(ui_data.soft_min, ui_data.min);
+ ui_data.max = MAX2(ui_data.min, ui_data.max);
+ }
+ if (args_contain_key(kwargs, "max")) {
+ ui_data.max = max;
+ ui_data.soft_max = MIN2(ui_data.soft_max, ui_data.max);
+ ui_data.min = MIN2(ui_data.min, ui_data.max);
+ }
+ if (args_contain_key(kwargs, "soft_min")) {
+ ui_data.soft_min = soft_min;
+ ui_data.soft_min = MAX2(ui_data.soft_min, ui_data.min);
+ ui_data.soft_max = MAX2(ui_data.soft_min, ui_data.soft_max);
+ }
+ if (args_contain_key(kwargs, "soft_max")) {
+ ui_data.soft_max = soft_max;
+ ui_data.soft_max = MIN2(ui_data.soft_max, ui_data.max);
+ ui_data.soft_min = MIN2(ui_data.soft_min, ui_data.soft_max);
+ }
+ if (args_contain_key(kwargs, "step")) {
+ ui_data.step = (float)step;
+ }
+ if (args_contain_key(kwargs, "precision")) {
+ ui_data.precision = precision;
+ }
+
+ if (!ELEM(default_value, NULL, Py_None)) {
+ if (!idprop_ui_data_update_float_default(idprop, &ui_data, default_value)) {
+ IDP_ui_data_free_unique_contents(
+ &ui_data.base, IDP_ui_data_type(idprop), &ui_data_orig->base);
+ return false;
+ }
+ }
+
+ /* Write back to the proeprty's UI data. */
+ IDP_ui_data_free_unique_contents(&ui_data_orig->base, IDP_ui_data_type(idprop), &ui_data.base);
+ *ui_data_orig = ui_data;
+ return true;
+}
+
+/**
+ * \return False when parsing fails, in which case caller should return NULL.
+ */
+static bool idprop_ui_data_update_string(IDProperty *idprop, PyObject *args, PyObject *kwargs)
+{
+ const char *rna_subtype = NULL;
+ const char *description = NULL;
+ const char *default_value;
+ const char *kwlist[] = {"default", "subtype", "description", NULL};
+ if (!PyArg_ParseTupleAndKeywords(args,
+ kwargs,
+ "|$zzz:update",
+ (char **)kwlist,
+ &default_value,
+ &rna_subtype,
+ &description)) {
+ return false;
+ }
+
+ /* Write to a temporary copy of the UI data in case some part of the parsing fails. */
+ IDPropertyUIDataString *ui_data_orig = (IDPropertyUIDataString *)idprop->ui_data;
+ IDPropertyUIDataString ui_data = *ui_data_orig;
+
+ if (!idprop_ui_data_update_base(&ui_data.base, rna_subtype, description)) {
+ IDP_ui_data_free_unique_contents(&ui_data.base, IDP_ui_data_type(idprop), &ui_data_orig->base);
+ return false;
+ }
+
+ if (default_value != NULL) {
+ ui_data.default_value = BLI_strdup(default_value);
+ }
+
+ /* Write back to the proeprty's UI data. */
+ IDP_ui_data_free_unique_contents(&ui_data_orig->base, IDP_ui_data_type(idprop), &ui_data.base);
+ *ui_data_orig = ui_data;
+ return true;
+}
+
+/**
+ * \return False when parsing fails, in which case caller should return NULL.
+ */
+static bool idprop_ui_data_update_id(IDProperty *idprop, PyObject *args, PyObject *kwargs)
+{
+ const char *rna_subtype = NULL;
+ const char *description = NULL;
+ const char *kwlist[] = {"subtype", "description", NULL};
+ if (!PyArg_ParseTupleAndKeywords(
+ args, kwargs, "|$zz:update", (char **)kwlist, &rna_subtype, &description)) {
+ return false;
+ }
+
+ /* Write to a temporary copy of the UI data in case some part of the parsing fails. */
+ IDPropertyUIDataID *ui_data_orig = (IDPropertyUIDataID *)idprop->ui_data;
+ IDPropertyUIDataID ui_data = *ui_data_orig;
+
+ if (!idprop_ui_data_update_base(&ui_data.base, rna_subtype, description)) {
+ IDP_ui_data_free_unique_contents(&ui_data.base, IDP_ui_data_type(idprop), &ui_data_orig->base);
+ return false;
+ }
+
+ /* Write back to the proeprty's UI data. */
+ IDP_ui_data_free_unique_contents(&ui_data_orig->base, IDP_ui_data_type(idprop), &ui_data.base);
+ *ui_data_orig = ui_data;
+ return true;
+}
+
+PyDoc_STRVAR(BPy_IDPropertyUIManager_update_doc,
+ ".. method:: update( "
+ "subtype=None, "
+ "min=None, "
+ "max=None, "
+ "soft_min=None, "
+ "soft_max=None, "
+ "precision=None, "
+ "step=None, "
+ "default=None, "
+ "description=None)\n"
+ "\n"
+ " Update the RNA information of the IDProperty used for interaction and\n"
+ " display in the user interface. The required types for many of the keyword\n"
+ " arguments depend on the type of the property.\n ");
+static PyObject *BPy_IDPropertyUIManager_update(BPy_IDPropertyUIManager *self,
+ PyObject *args,
+ PyObject *kwargs)
+{
+ IDProperty *property = self->property;
+ BLI_assert(IDP_ui_data_supported(property));
+
+ switch (IDP_ui_data_type(property)) {
+ case IDP_UI_DATA_TYPE_INT:
+ IDP_ui_data_ensure(property);
+ if (!idprop_ui_data_update_int(property, args, kwargs)) {
+ return NULL;
+ }
+ Py_RETURN_NONE;
+ case IDP_UI_DATA_TYPE_FLOAT:
+ IDP_ui_data_ensure(property);
+ if (!idprop_ui_data_update_float(property, args, kwargs)) {
+ return NULL;
+ }
+ Py_RETURN_NONE;
+ case IDP_UI_DATA_TYPE_STRING:
+ IDP_ui_data_ensure(property);
+ if (!idprop_ui_data_update_string(property, args, kwargs)) {
+ return NULL;
+ }
+ Py_RETURN_NONE;
+ case IDP_UI_DATA_TYPE_ID:
+ IDP_ui_data_ensure(property);
+ if (!idprop_ui_data_update_id(property, args, kwargs)) {
+ return NULL;
+ }
+ Py_RETURN_NONE;
+ case IDP_UI_DATA_TYPE_UNSUPPORTED:
+ PyErr_Format(PyExc_TypeError, "IDProperty \"%s\" does not support RNA data", property->name);
+ return NULL;
+ }
+
+ BLI_assert_unreachable();
+ Py_RETURN_NONE;
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name UI Data As Dictionary
+ * \{ */
+
+static void idprop_ui_data_to_dict_int(IDProperty *property, PyObject *dict)
+{
+ IDPropertyUIDataInt *ui_data = (IDPropertyUIDataInt *)property->ui_data;
+ PyObject *item;
+
+ PyDict_SetItemString(dict, "min", item = PyLong_FromLong(ui_data->min));
+ Py_DECREF(item);
+ PyDict_SetItemString(dict, "max", item = PyLong_FromLong(ui_data->max));
+ Py_DECREF(item);
+ PyDict_SetItemString(dict, "soft_min", item = PyLong_FromLong(ui_data->soft_min));
+ Py_DECREF(item);
+ PyDict_SetItemString(dict, "soft_max", item = PyLong_FromLong(ui_data->soft_max));
+ Py_DECREF(item);
+ PyDict_SetItemString(dict, "step", item = PyLong_FromLong(ui_data->step));
+ Py_DECREF(item);
+ if (property->type == IDP_ARRAY) {
+ PyObject *list = PyList_New(ui_data->default_array_len);
+ for (int i = 0; i < ui_data->default_array_len; i++) {
+ PyList_SET_ITEM(list, i, PyLong_FromLong(ui_data->default_array[i]));
+ }
+ PyDict_SetItemString(dict, "default", list);
+ Py_DECREF(list);
+ }
+ else {
+ PyDict_SetItemString(dict, "default", item = PyLong_FromLong(ui_data->step));
+ Py_DECREF(item);
+ }
+}
+
+static void idprop_ui_data_to_dict_float(IDProperty *property, PyObject *dict)
+{
+ IDPropertyUIDataFloat *ui_data = (IDPropertyUIDataFloat *)property->ui_data;
+ PyObject *item;
+
+ PyDict_SetItemString(dict, "min", item = PyFloat_FromDouble(ui_data->min));
+ Py_DECREF(item);
+ PyDict_SetItemString(dict, "max", item = PyFloat_FromDouble(ui_data->max));
+ Py_DECREF(item);
+ PyDict_SetItemString(dict, "soft_min", item = PyFloat_FromDouble(ui_data->soft_min));
+ Py_DECREF(item);
+ PyDict_SetItemString(dict, "soft_max", item = PyFloat_FromDouble(ui_data->soft_max));
+ Py_DECREF(item);
+ PyDict_SetItemString(dict, "step", item = PyFloat_FromDouble((double)ui_data->step));
+ Py_DECREF(item);
+ PyDict_SetItemString(dict, "precision", item = PyFloat_FromDouble((double)ui_data->precision));
+ Py_DECREF(item);
+ if (property->type == IDP_ARRAY) {
+ PyObject *list = PyList_New(ui_data->default_array_len);
+ for (int i = 0; i < ui_data->default_array_len; i++) {
+ PyList_SET_ITEM(list, i, PyFloat_FromDouble(ui_data->default_array[i]));
+ }
+ PyDict_SetItemString(dict, "default", list);
+ Py_DECREF(list);
+ }
+ else {
+ PyDict_SetItemString(dict, "default", item = PyFloat_FromDouble(ui_data->step));
+ Py_DECREF(item);
+ }
+}
+
+static void idprop_ui_data_to_dict_string(IDProperty *property, PyObject *dict)
+{
+ IDPropertyUIDataString *ui_data = (IDPropertyUIDataString *)property->ui_data;
+ PyObject *item;
+
+ const char *default_value = (ui_data->default_value == NULL) ? "" : ui_data->default_value;
+
+ PyDict_SetItemString(dict, "default", item = PyUnicode_FromString(default_value));
+ Py_DECREF(item);
+}
+
+PyDoc_STRVAR(BPy_IDPropertyUIManager_as_dict_doc,
+ ".. method:: as_dict()\n"
+ "\n"
+ " Return a dictionary of the property's RNA UI data. The fields in the\n"
+ " returned dictionary and their types will depend on the property's type.\n");
+static PyObject *BPy_IDIDPropertyUIManager_as_dict(BPy_IDPropertyUIManager *self)
+{
+ IDProperty *property = self->property;
+ BLI_assert(IDP_ui_data_supported(property));
+
+ IDPropertyUIData *ui_data = IDP_ui_data_ensure(property);
+
+ PyObject *dict = PyDict_New();
+
+ /* RNA subtype. */
+ {
+ const char *subtype_id = NULL;
+ RNA_enum_identifier(rna_enum_property_subtype_items, ui_data->rna_subtype, &subtype_id);
+ PyObject *item = PyUnicode_FromString(subtype_id);
+ PyDict_SetItemString(dict, "subtype", item);
+ Py_DECREF(item);
+ }
+
+ /* Description. */
+ if (ui_data->description != NULL) {
+ PyObject *item = PyUnicode_FromString(ui_data->description);
+ PyDict_SetItemString(dict, "description", item);
+ Py_DECREF(item);
+ }
+
+ /* Type specific data. */
+ switch (IDP_ui_data_type(property)) {
+ case IDP_UI_DATA_TYPE_STRING:
+ idprop_ui_data_to_dict_string(property, dict);
+ break;
+ case IDP_UI_DATA_TYPE_ID:
+ break;
+ case IDP_UI_DATA_TYPE_INT:
+ idprop_ui_data_to_dict_int(property, dict);
+ break;
+ case IDP_UI_DATA_TYPE_FLOAT:
+ idprop_ui_data_to_dict_float(property, dict);
+ break;
+ case IDP_UI_DATA_TYPE_UNSUPPORTED:
+ BLI_assert_unreachable();
+ break;
+ }
+
+ return dict;
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name UI Data Clear
+ * \{ */
+
+PyDoc_STRVAR(BPy_IDPropertyUIManager_clear_doc,
+ ".. method:: clear()\n"
+ "\n"
+ " Remove the RNA UI data from this IDProperty.\n");
+static PyObject *BPy_IDPropertyUIManager_clear(BPy_IDPropertyUIManager *self)
+{
+ IDProperty *property = self->property;
+ BLI_assert(IDP_ui_data_supported(property));
+
+ if (property == NULL) {
+ PyErr_SetString(PyExc_RuntimeError, "IDPropertyUIManager missing property");
+ BLI_assert_unreachable();
+ return NULL;
+ }
+
+ if (property->ui_data != NULL) {
+ IDP_ui_data_free(property);
+ }
+
+ Py_RETURN_NONE;
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name UI Data Copying
+ * \{ */
+
+PyDoc_STRVAR(
+ BPy_IDPropertyUIManager_update_from_doc,
+ ".. method:: update_from(ui_manager_source)\n"
+ "\n"
+ " Copy UI data from an IDProperty in the source group to a property in this group.\n "
+ " If the source property has no UI data, the target UI data will be reset if it exists.\n"
+ "\n"
+ " :raises TypeError: If the types of the two properties don't match.\n");
+static PyObject *BPy_IDPropertyUIManager_update_from(BPy_IDPropertyUIManager *self, PyObject *args)
+{
+ IDProperty *property = self->property;
+ BLI_assert(IDP_ui_data_supported(property));
+
+ BPy_IDPropertyUIManager *ui_manager_src;
+ if (!PyArg_ParseTuple(args, "O!:update_from", &BPy_IDPropertyUIManager_Type, &ui_manager_src)) {
+ return NULL;
+ }
+
+ if (property->ui_data != NULL) {
+ IDP_ui_data_free(property);
+ }
+
+ property->ui_data = IDP_ui_data_copy(ui_manager_src->property);
+
+ Py_RETURN_NONE;
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name UI Data Manager Definition
+ * \{ */
+
+static struct PyMethodDef BPy_IDPropertyUIManager_methods[] = {
+ {"update",
+ (PyCFunction)BPy_IDPropertyUIManager_update,
+ METH_VARARGS | METH_KEYWORDS,
+ BPy_IDPropertyUIManager_update_doc},
+ {"as_dict",
+ (PyCFunction)BPy_IDIDPropertyUIManager_as_dict,
+ METH_NOARGS,
+ BPy_IDPropertyUIManager_as_dict_doc},
+ {"clear",
+ (PyCFunction)BPy_IDPropertyUIManager_clear,
+ METH_NOARGS,
+ BPy_IDPropertyUIManager_clear_doc},
+ {"update_from",
+ (PyCFunction)BPy_IDPropertyUIManager_update_from,
+ METH_VARARGS,
+ BPy_IDPropertyUIManager_update_from_doc},
+ {NULL, NULL, 0, NULL},
+};
+
+static PyObject *BPy_IDPropertyUIManager_repr(BPy_IDPropertyUIManager *self)
+{
+ return PyUnicode_FromFormat(
+ "<bpy id prop ui manager: name=\"%s\", address=%p>", self->property->name, self->property);
+}
+
+static Py_hash_t BPy_IDPropertyUIManager_hash(BPy_IDPropertyUIManager *self)
+{
+ return _Py_HashPointer(self->property);
+}
+
+PyTypeObject BPy_IDPropertyUIManager_Type = {
+ PyVarObject_HEAD_INIT(NULL, 0)
+ /* For printing, in format "<module>.<name>" */
+ "IDPropertyUIManager", /* char *tp_name; */
+ sizeof(BPy_IDPropertyUIManager), /* int tp_basicsize; */
+ 0, /* tp_itemsize; For allocation */
+
+ /* Methods to implement standard operations */
+
+ NULL, /* destructor tp_dealloc; */
+ 0, /* tp_vectorcall_offset */
+ NULL, /* getattrfunc tp_getattr; */
+ NULL, /* setattrfunc tp_setattr; */
+ NULL, /* cmpfunc tp_compare; */
+ (reprfunc)BPy_IDPropertyUIManager_repr, /* reprfunc tp_repr; */
+
+ /* Method suites for standard classes */
+
+ NULL, /* PyNumberMethods *tp_as_number; */
+ NULL, /* PySequenceMethods *tp_as_sequence; */
+ NULL, /* PyMappingMethods *tp_as_mapping; */
+
+ /* More standard operations (here for binary compatibility) */
+
+ (hashfunc)BPy_IDPropertyUIManager_hash, /* hashfunc tp_hash; */
+ NULL, /* ternaryfunc tp_call; */
+ NULL, /* reprfunc tp_str; */
+ NULL, /* getattrofunc tp_getattro; */
+ NULL, /* setattrofunc tp_setattro; */
+
+ /* Functions to access object as input/output buffer */
+ NULL, /* PyBufferProcs *tp_as_buffer; */
+
+ /*** Flags to define presence of optional/expanded features ***/
+ Py_TPFLAGS_DEFAULT, /* long tp_flags; */
+
+ NULL, /* char *tp_doc; Documentation string */
+ /*** Assigned meaning in release 2.0 ***/
+ /* call function for all accessible objects */
+ NULL, /* traverseproc tp_traverse; */
+
+ /* delete references to contained objects */
+ NULL, /* inquiry tp_clear; */
+
+ /*** Assigned meaning in release 2.1 ***/
+ /*** rich comparisons ***/
+ NULL, /* richcmpfunc tp_richcompare; */
+
+ /*** weak reference enabler ***/
+ 0, /* long tp_weaklistoffset; */
+
+ /*** Added in release 2.2 ***/
+ /* Iterators */
+ NULL, /* getiterfunc tp_iter; */
+ NULL, /* iternextfunc tp_iternext; */
+ /*** Attribute descriptor and subclassing stuff ***/
+ BPy_IDPropertyUIManager_methods, /* struct PyMethodDef *tp_methods; */
+ NULL, /* struct PyMemberDef *tp_members; */
+ NULL, /* struct PyGetSetDef *tp_getset; */
+};
+
+void IDPropertyUIData_Init_Types()
+{
+ PyType_Ready(&BPy_IDPropertyUIManager_Type);
+}
+
+/** \} */
diff --git a/source/blender/python/generic/idprop_py_ui_api.h b/source/blender/python/generic/idprop_py_ui_api.h
new file mode 100644
index 00000000000..f678e5ae9c1
--- /dev/null
+++ b/source/blender/python/generic/idprop_py_ui_api.h
@@ -0,0 +1,33 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+/** \file
+ * \ingroup pygen
+ */
+
+#pragma once
+
+struct ID;
+struct IDProperty;
+
+extern PyTypeObject BPy_IDPropertyUIManager_Type;
+
+typedef struct BPy_IDPropertyUIManager {
+ PyObject_VAR_HEAD
+ struct IDProperty *property;
+} BPy_IDPropertyUIManager;
+
+void IDPropertyUIData_Init_Types(void);
diff --git a/source/blender/python/intern/bpy.c b/source/blender/python/intern/bpy.c
index 0e0f431cb19..7646109c1b0 100644
--- a/source/blender/python/intern/bpy.c
+++ b/source/blender/python/intern/bpy.c
@@ -56,6 +56,7 @@
/* external util modules */
#include "../generic/idprop_py_api.h"
+#include "../generic/idprop_py_ui_api.h"
#include "bpy_msgbus.h"
#ifdef WITH_FREESTYLE
@@ -407,6 +408,7 @@ void BPy_init_modules(struct bContext *C)
}
/* stand alone utility modules not related to blender directly */
IDProp_Init_Types(); /* not actually a submodule, just types */
+ IDPropertyUIData_Init_Types();
#ifdef WITH_FREESTYLE
Freestyle_Init();
#endif
diff --git a/source/blender/python/intern/bpy_rna.c b/source/blender/python/intern/bpy_rna.c
index e9d5ae278bb..ac1fdeb2bad 100644
--- a/source/blender/python/intern/bpy_rna.c
+++ b/source/blender/python/intern/bpy_rna.c
@@ -73,6 +73,7 @@
#include "DEG_depsgraph_query.h"
#include "../generic/idprop_py_api.h" /* For IDprop lookups. */
+#include "../generic/idprop_py_ui_api.h"
#include "../generic/py_capi_utils.h"
#include "../generic/python_utildefines.h"
@@ -4309,6 +4310,51 @@ static PyObject *pyrna_struct_id_properties_ensure(BPy_StructRNA *self)
return (PyObject *)group;
}
+PyDoc_STRVAR(pyrna_struct_id_properties_ui_doc,
+ ".. method:: id_properties_ui(key)\n"
+ "\n"
+ " :return: Return an object used to manage an IDProperty's UI data.\n"
+ " :arg key: String name of the property.\n"
+ " :rtype: :class:`bpy.types.IDPropertyUIManager`\n");
+static PyObject *pyrna_struct_id_properties_ui(BPy_StructRNA *self, PyObject *args)
+{
+ PYRNA_STRUCT_CHECK_OBJ(self);
+
+ if (RNA_struct_idprops_check(self->ptr.type) == 0) {
+ PyErr_SetString(PyExc_TypeError, "This type doesn't support IDProperties");
+ return NULL;
+ }
+
+ const char *key;
+ if (!PyArg_ParseTuple(args, "s:ui_data", &key)) {
+ return NULL;
+ }
+
+ IDProperty *parent_group = RNA_struct_idprops(&self->ptr, true);
+
+ /* This is a paranoid check that theoretically might not be necessary.
+ * It allows the possibility that some structs can't ensure IDProperties. */
+ if (parent_group == NULL) {
+ return Py_None;
+ }
+
+ IDProperty *property = IDP_GetPropertyFromGroup(parent_group, key);
+ if (property == NULL) {
+ PyErr_SetString(PyExc_KeyError, "Property not found in IDProperty group");
+ return NULL;
+ }
+
+ if (!IDP_ui_data_supported(property)) {
+ PyErr_Format(PyExc_TypeError, "IDProperty \"%s\" does not support UI data", property->name);
+ return NULL;
+ }
+
+ BPy_IDPropertyUIManager *ui_manager = PyObject_New(BPy_IDPropertyUIManager,
+ &BPy_IDPropertyUIManager_Type);
+ ui_manager->property = property;
+ return (PyObject *)ui_manager;
+}
+
PyDoc_STRVAR(pyrna_struct_id_properties_clear_doc,
".. method:: id_properties_clear()\n\n"
" :return: Remove the parent group for an RNA struct's custom IDProperties.\n");
@@ -5829,6 +5875,10 @@ static struct PyMethodDef pyrna_struct_methods[] = {
(PyCFunction)pyrna_struct_id_properties_clear,
METH_NOARGS,
pyrna_struct_id_properties_clear_doc},
+ {"id_properties_ui",
+ (PyCFunction)pyrna_struct_id_properties_ui,
+ METH_VARARGS,
+ pyrna_struct_id_properties_ui_doc},
/* experimental */
/* unused for now */
diff --git a/source/blender/sequencer/SEQ_iterator.h b/source/blender/sequencer/SEQ_iterator.h
index 055db07f646..7eab9f84b50 100644
--- a/source/blender/sequencer/SEQ_iterator.h
+++ b/source/blender/sequencer/SEQ_iterator.h
@@ -34,10 +34,24 @@ struct GSet;
struct GSetIterator;
struct Sequence;
+/* Utility macro to construct an unique (within a file) variable name for iterator macro.
+ * Use indirect macro evaluation to ensure the `__LINE__` is expanded (rather than being
+ * treated as a name token),
+ *
+ * The `__LINE__` is defined at the invocation of the `SEQ_ITERATOR_FOREACH` and is not changed
+ * afterwards. This makes it safe to expand it several times in the `SEQ_ITERATOR_FOREACH`.
+ *
+ * This allows to have nested foreach loops.
+ *
+ * NOTE: Putting nested loop to a wrapper macro is not supported. */
+#define _SEQ_ITERATOR_NAME_JOIN(x, y) x##_##y
+#define _SEQ_ITERATOR_NAME_EVALUATE(x, y) _SEQ_ITERATOR_NAME_JOIN(x, y)
+#define _SEQ_ITERATOR_NAME(prefix) _SEQ_ITERATOR_NAME_EVALUATE(prefix, __LINE__)
+
#define SEQ_ITERATOR_FOREACH(var, collection) \
- for (SeqIterator iter = {{{NULL}}}; \
- SEQ_iterator_ensure(collection, &iter, &var) && var != NULL; \
- var = SEQ_iterator_yield(&iter))
+ for (SeqIterator _SEQ_ITERATOR_NAME(iter) = {{{NULL}}}; \
+ SEQ_iterator_ensure(collection, &_SEQ_ITERATOR_NAME(iter), &var) && var != NULL; \
+ var = SEQ_iterator_yield(&_SEQ_ITERATOR_NAME(iter)))
typedef struct SeqCollection {
struct GSet *set;
@@ -79,6 +93,7 @@ SeqCollection *SEQ_query_by_reference(struct Sequence *seq_reference,
struct ListBase *seqbase,
SeqCollection *collection));
SeqCollection *SEQ_query_selected_strips(struct ListBase *seqbase);
+SeqCollection *SEQ_query_unselected_strips(struct ListBase *seqbase);
SeqCollection *SEQ_query_all_strips(struct ListBase *seqbase);
SeqCollection *SEQ_query_all_strips_recursive(struct ListBase *seqbase);
void SEQ_query_strip_effect_chain(struct Sequence *seq_reference,
diff --git a/source/blender/sequencer/SEQ_sequencer.h b/source/blender/sequencer/SEQ_sequencer.h
index 54ef37b9049..15fe0be3571 100644
--- a/source/blender/sequencer/SEQ_sequencer.h
+++ b/source/blender/sequencer/SEQ_sequencer.h
@@ -63,6 +63,7 @@ void SEQ_tool_settings_fit_method_set(struct Scene *scene, eSeqImageFitMethod fi
short SEQ_tool_settings_snap_flag_get(struct Scene *scene);
short SEQ_tool_settings_snap_mode_get(struct Scene *scene);
int SEQ_tool_settings_snap_distance_get(struct Scene *scene);
+eSeqOverlapMode SEQ_tool_settings_overlap_mode_get(struct Scene *scene);
struct SequencerToolSettings *SEQ_tool_settings_copy(struct SequencerToolSettings *tool_settings);
struct Editing *SEQ_editing_get(struct Scene *scene, bool alloc);
struct Editing *SEQ_editing_ensure(struct Scene *scene);
diff --git a/source/blender/sequencer/SEQ_transform.h b/source/blender/sequencer/SEQ_transform.h
index 9ff827333be..1977835f627 100644
--- a/source/blender/sequencer/SEQ_transform.h
+++ b/source/blender/sequencer/SEQ_transform.h
@@ -41,6 +41,7 @@ bool SEQ_transform_sequence_can_be_translated(struct Sequence *seq);
bool SEQ_transform_single_image_check(struct Sequence *seq);
void SEQ_transform_fix_single_image_seq_offsets(struct Sequence *seq);
bool SEQ_transform_test_overlap(struct ListBase *seqbasep, struct Sequence *test);
+bool SEQ_transform_test_overlap_seq_seq(struct Sequence *seq1, struct Sequence *seq2);
void SEQ_transform_translate_sequence(struct Scene *scene, struct Sequence *seq, int delta);
bool SEQ_transform_seqbase_shuffle_ex(struct ListBase *seqbasep,
struct Sequence *test,
diff --git a/source/blender/sequencer/intern/effects.c b/source/blender/sequencer/intern/effects.c
index d4adad9a34d..37bac523645 100644
--- a/source/blender/sequencer/intern/effects.c
+++ b/source/blender/sequencer/intern/effects.c
@@ -39,6 +39,7 @@
#include "BLI_utildefines.h"
#include "DNA_anim_types.h"
+#include "DNA_packedFile_types.h"
#include "DNA_scene_types.h"
#include "DNA_sequence_types.h"
#include "DNA_space_types.h"
@@ -3755,31 +3756,47 @@ static void init_text_effect(Sequence *seq)
void SEQ_effect_text_font_unload(TextVars *data, const bool do_id_user)
{
- if (data) {
- /* Unlink the VFont */
- if (do_id_user && data->text_font != NULL) {
- id_us_min(&data->text_font->id);
- data->text_font = NULL;
- }
+ if (data == NULL) {
+ return;
+ }
- /* Unload the BLF font. */
- if (data->text_blf_id >= 0) {
- BLF_unload_id(data->text_blf_id);
- }
+ /* Unlink the VFont */
+ if (do_id_user && data->text_font != NULL) {
+ id_us_min(&data->text_font->id);
+ data->text_font = NULL;
+ }
+
+ /* Unload the BLF font. */
+ if (data->text_blf_id >= 0) {
+ BLF_unload_id(data->text_blf_id);
}
}
void SEQ_effect_text_font_load(TextVars *data, const bool do_id_user)
{
- if (data->text_font != NULL) {
- if (do_id_user) {
- id_us_plus(&data->text_font->id);
- }
+ VFont *vfont = data->text_font;
+ if (vfont == NULL) {
+ return;
+ }
+
+ if (do_id_user) {
+ id_us_plus(&vfont->id);
+ }
+ if (vfont->packedfile != NULL) {
+ PackedFile *pf = vfont->packedfile;
+ /* Create a name that's unique between library data-blocks to avoid loading
+ * a font per strip which will load fonts many times. */
+ char name[MAX_ID_FULL_NAME];
+ BKE_id_full_name_get(name, &vfont->id, 0);
+
+ data->text_blf_id = BLF_load_mem(name, pf->data, pf->size);
+ }
+ else {
char path[FILE_MAX];
- STRNCPY(path, data->text_font->filepath);
+ STRNCPY(path, vfont->filepath);
BLI_assert(BLI_thread_is_main());
- BLI_path_abs(path, ID_BLEND_PATH_FROM_GLOBAL(&data->text_font->id));
+ BLI_path_abs(path, ID_BLEND_PATH_FROM_GLOBAL(&vfont->id));
data->text_blf_id = BLF_load(path);
}
@@ -3850,9 +3867,7 @@ static ImBuf *do_text_effect(const SeqRenderData *context,
if (data->text_blf_id == SEQ_FONT_NOT_LOADED) {
data->text_blf_id = -1;
- if (data->text_font) {
- data->text_blf_id = BLF_load(data->text_font->filepath);
- }
+ SEQ_effect_text_font_load(data, false);
}
if (data->text_blf_id >= 0) {
diff --git a/source/blender/sequencer/intern/iterator.c b/source/blender/sequencer/intern/iterator.c
index a9cbf4879f1..58f68205f51 100644
--- a/source/blender/sequencer/intern/iterator.c
+++ b/source/blender/sequencer/intern/iterator.c
@@ -341,6 +341,24 @@ SeqCollection *SEQ_query_selected_strips(ListBase *seqbase)
}
/**
+ * Query all unselected strips in seqbase.
+ *
+ * \param seqbase: ListBase in which strips are queried
+ * \return strip collection
+ */
+SeqCollection *SEQ_query_unselected_strips(ListBase *seqbase)
+{
+ SeqCollection *collection = SEQ_collection_create(__func__);
+ LISTBASE_FOREACH (Sequence *, seq, seqbase) {
+ if ((seq->flag & SELECT) != 0) {
+ continue;
+ }
+ SEQ_collection_append_strip(seq, collection);
+ }
+ return collection;
+}
+
+/**
* Query all effect strips that are directly or indirectly connected to seq_reference.
* This includes all effects of seq_reference, strips used by another inputs and their effects, so
* that whole chain is fully independent of other strips.
diff --git a/source/blender/sequencer/intern/sequencer.c b/source/blender/sequencer/intern/sequencer.c
index 2863af8fb2e..a62b76cd832 100644
--- a/source/blender/sequencer/intern/sequencer.c
+++ b/source/blender/sequencer/intern/sequencer.c
@@ -323,6 +323,8 @@ SequencerToolSettings *SEQ_tool_settings_init(void)
tool_settings->snap_mode = SEQ_SNAP_TO_STRIPS | SEQ_SNAP_TO_CURRENT_FRAME |
SEQ_SNAP_TO_STRIP_HOLD;
tool_settings->snap_distance = 15;
+ tool_settings->overlap_mode = SEQ_OVERLAP_SHUFFLE;
+
return tool_settings;
}
@@ -372,6 +374,12 @@ void SEQ_tool_settings_fit_method_set(Scene *scene, eSeqImageFitMethod fit_metho
tool_settings->fit_method = fit_method;
}
+eSeqOverlapMode SEQ_tool_settings_overlap_mode_get(Scene *scene)
+{
+ const SequencerToolSettings *tool_settings = SEQ_tool_settings_ensure(scene);
+ return tool_settings->overlap_mode;
+}
+
/**
* Get seqbase that is being viewed currently. This can be main seqbase or meta strip seqbase
*
diff --git a/source/blender/sequencer/intern/strip_transform.c b/source/blender/sequencer/intern/strip_transform.c
index 9f69f434ca0..3a5f93a72b0 100644
--- a/source/blender/sequencer/intern/strip_transform.c
+++ b/source/blender/sequencer/intern/strip_transform.c
@@ -208,7 +208,7 @@ bool SEQ_transform_sequence_can_be_translated(Sequence *seq)
return !(seq->type & SEQ_TYPE_EFFECT) || (SEQ_effect_get_num_inputs(seq->type) == 0);
}
-static bool seq_overlap(Sequence *seq1, Sequence *seq2)
+bool SEQ_transform_test_overlap_seq_seq(Sequence *seq1, Sequence *seq2)
{
return (seq1 != seq2 && seq1->machine == seq2->machine &&
((seq1->enddisp <= seq2->startdisp) || (seq1->startdisp >= seq2->enddisp)) == 0);
@@ -220,7 +220,7 @@ bool SEQ_transform_test_overlap(ListBase *seqbasep, Sequence *test)
seq = seqbasep->first;
while (seq) {
- if (seq_overlap(test, seq)) {
+ if (SEQ_transform_test_overlap_seq_seq(test, seq)) {
return true;
}
@@ -316,7 +316,7 @@ static int shuffle_seq_time_offset_test(SeqCollection *strips_to_shuffle,
SEQ_ITERATOR_FOREACH (seq, strips_to_shuffle) {
LISTBASE_FOREACH (Sequence *, seq_other, seqbasep) {
- if (!seq_overlap(seq, seq_other)) {
+ if (!SEQ_transform_test_overlap_seq_seq(seq, seq_other)) {
continue;
}
if (UNLIKELY(SEQ_collection_has_strip(seq_other, strips_to_shuffle))) {
diff --git a/source/blender/windowmanager/xr/intern/wm_xr.c b/source/blender/windowmanager/xr/intern/wm_xr.c
index 4877addbb77..297205d1e79 100644
--- a/source/blender/windowmanager/xr/intern/wm_xr.c
+++ b/source/blender/windowmanager/xr/intern/wm_xr.c
@@ -148,7 +148,6 @@ bool wm_xr_events_handle(wmWindowManager *wm)
wmXrRuntimeData *wm_xr_runtime_data_create(void)
{
wmXrRuntimeData *runtime = MEM_callocN(sizeof(*runtime), __func__);
- runtime->actactionmap = runtime->selactionmap = -1;
return runtime;
}
diff --git a/source/blender/windowmanager/xr/intern/wm_xr_actionmap.c b/source/blender/windowmanager/xr/intern/wm_xr_actionmap.c
index 673fdfcd602..8903305adb4 100644
--- a/source/blender/windowmanager/xr/intern/wm_xr_actionmap.c
+++ b/source/blender/windowmanager/xr/intern/wm_xr_actionmap.c
@@ -143,14 +143,9 @@ bool WM_xr_actionmap_binding_remove(XrActionMapItem *ami, XrActionMapBinding *am
if (idx != -1) {
BLI_freelinkN(&ami->bindings, amb);
- if (BLI_listbase_is_empty(&ami->bindings)) {
- ami->selbinding = -1;
- }
- else {
- if (idx <= ami->selbinding) {
- if (--ami->selbinding < 0) {
- ami->selbinding = 0;
- }
+ if (idx <= ami->selbinding) {
+ if (--ami->selbinding < 0) {
+ ami->selbinding = 0;
}
}
@@ -181,7 +176,7 @@ XrActionMapBinding *WM_xr_actionmap_binding_find(XrActionMapItem *ami, const cha
static void wm_xr_actionmap_item_bindings_clear(XrActionMapItem *ami)
{
BLI_freelistN(&ami->bindings);
- ami->selbinding = -1;
+ ami->selbinding = 0;
}
static void wm_xr_actionmap_item_properties_set(XrActionMapItem *ami)
@@ -262,7 +257,6 @@ XrActionMapItem *WM_xr_actionmap_item_new(XrActionMap *actionmap,
if (ami_prev) {
WM_xr_actionmap_item_ensure_unique(actionmap, ami);
}
- ami->selbinding = -1;
BLI_addtail(&actionmap->items, ami);
@@ -355,14 +349,9 @@ bool WM_xr_actionmap_item_remove(XrActionMap *actionmap, XrActionMapItem *ami)
wm_xr_actionmap_item_properties_free(ami);
BLI_freelinkN(&actionmap->items, ami);
- if (BLI_listbase_is_empty(&actionmap->items)) {
- actionmap->selitem = -1;
- }
- else {
- if (idx <= actionmap->selitem) {
- if (--actionmap->selitem < 0) {
- actionmap->selitem = 0;
- }
+ if (idx <= actionmap->selitem) {
+ if (--actionmap->selitem < 0) {
+ actionmap->selitem = 0;
}
}
@@ -403,7 +392,6 @@ XrActionMap *WM_xr_actionmap_new(wmXrRuntimeData *runtime, const char *name, boo
if (am_prev) {
WM_xr_actionmap_ensure_unique(runtime, am);
}
- am->selitem = -1;
BLI_addtail(&runtime->actionmaps, am);
@@ -487,19 +475,14 @@ bool WM_xr_actionmap_remove(wmXrRuntimeData *runtime, XrActionMap *actionmap)
WM_xr_actionmap_clear(actionmap);
BLI_freelinkN(&runtime->actionmaps, actionmap);
- if (BLI_listbase_is_empty(&runtime->actionmaps)) {
- runtime->actactionmap = runtime->selactionmap = -1;
- }
- else {
- if (idx <= runtime->actactionmap) {
- if (--runtime->actactionmap < 0) {
- runtime->actactionmap = 0;
- }
+ if (idx <= runtime->actactionmap) {
+ if (--runtime->actactionmap < 0) {
+ runtime->actactionmap = 0;
}
- if (idx <= runtime->selactionmap) {
- if (--runtime->selactionmap < 0) {
- runtime->selactionmap = 0;
- }
+ }
+ if (idx <= runtime->selactionmap) {
+ if (--runtime->selactionmap < 0) {
+ runtime->selactionmap = 0;
}
}
@@ -528,7 +511,7 @@ void WM_xr_actionmap_clear(XrActionMap *actionmap)
BLI_freelistN(&actionmap->items);
- actionmap->selitem = -1;
+ actionmap->selitem = 0;
}
void WM_xr_actionmaps_clear(wmXrRuntimeData *runtime)
@@ -539,7 +522,7 @@ void WM_xr_actionmaps_clear(wmXrRuntimeData *runtime)
BLI_freelistN(&runtime->actionmaps);
- runtime->actactionmap = runtime->selactionmap = -1;
+ runtime->actactionmap = runtime->selactionmap = 0;
}
ListBase *WM_xr_actionmaps_get(wmXrRuntimeData *runtime)
@@ -554,7 +537,6 @@ short WM_xr_actionmap_active_index_get(const wmXrRuntimeData *runtime)
void WM_xr_actionmap_active_index_set(wmXrRuntimeData *runtime, short idx)
{
- BLI_assert(idx < BLI_listbase_count(&runtime->actionmaps));
runtime->actactionmap = idx;
}
@@ -565,7 +547,6 @@ short WM_xr_actionmap_selected_index_get(const wmXrRuntimeData *runtime)
void WM_xr_actionmap_selected_index_set(wmXrRuntimeData *runtime, short idx)
{
- BLI_assert(idx < BLI_listbase_count(&runtime->actionmaps));
runtime->selactionmap = idx;
}
diff --git a/tests/python/bl_pyapi_idprop.py b/tests/python/bl_pyapi_idprop.py
index 38cd9d04a6b..bdf625c1c2e 100644
--- a/tests/python/bl_pyapi_idprop.py
+++ b/tests/python/bl_pyapi_idprop.py
@@ -246,6 +246,69 @@ class TestBufferProtocol(TestHelper, unittest.TestCase):
self.assertEqual(list(view1), list(view2))
self.assertEqual(view1.tobytes(), view2.tobytes())
+class TestRNAData(TestHelper, unittest.TestCase):
+
+ def test_custom_properties_none(self):
+ bpy.data.objects.new("test", None)
+ test_object = bpy.data.objects["test"]
+
+ # Access default RNA data values.
+ test_object.id_properties_clear()
+ test_object["test_prop"] = 0.5
+ ui_data_test_prop = test_object.id_properties_ui("test_prop")
+
+ rna_data = ui_data_test_prop.as_dict()
+ self.assertTrue("min" in rna_data)
+ self.assertLess(rna_data["min"], -10000.0)
+ self.assertEqual(rna_data["subtype"], "NONE")
+ self.assertGreater(rna_data["soft_max"], 10000.0)
+
+ # Change RNA data values.
+ ui_data_test_prop.update(subtype="TEMPERATURE", min=0, soft_min=0.1)
+ rna_data = ui_data_test_prop.as_dict()
+ self.assertEqual(rna_data["min"], 0)
+ self.assertEqual(rna_data["soft_min"], 0.1)
+ self.assertEqual(rna_data["subtype"], "TEMPERATURE")
+
+ # Copy RNA data values from one property to another.
+ test_object["test_prop_2"] = 11.7
+ ui_data_test_prop_2 = test_object.id_properties_ui("test_prop_2")
+ ui_data_test_prop_2.update_from(ui_data_test_prop)
+ rna_data = ui_data_test_prop_2.as_dict()
+ self.assertEqual(rna_data["min"], 0)
+ self.assertEqual(rna_data["soft_min"], 0.1)
+ self.assertEqual(rna_data["subtype"], "TEMPERATURE")
+ self.assertGreater(rna_data["soft_max"], 10000.0)
+
+ # Copy RNA data values to another object's property.
+ bpy.data.objects.new("test_2", None)
+ test_object_2 = bpy.data.objects["test_2"]
+ test_object_2["test_prop_3"] = 20.1
+ ui_data_test_prop_3 = test_object_2.id_properties_ui("test_prop_3")
+ ui_data_test_prop_3.update_from(ui_data_test_prop_2)
+ rna_data = ui_data_test_prop_3.as_dict()
+ self.assertEqual(rna_data["min"], 0)
+ self.assertEqual(rna_data["soft_min"], 0.1)
+ self.assertEqual(rna_data["subtype"], "TEMPERATURE")
+ self.assertGreater(rna_data["soft_max"], 10000.0)
+
+ # Test RNA data for string property.
+ test_object.id_properties_clear()
+ test_object["test_string_prop"] = "Hello there!"
+ ui_data_test_prop_string = test_object.id_properties_ui("test_string_prop")
+ ui_data_test_prop_string.update(default="Goodbye where?")
+ rna_data = ui_data_test_prop_string.as_dict()
+ self.assertEqual(rna_data["default"], "Goodbye where?")
+
+ # Test RNA data for array property.
+ test_object.id_properties_clear()
+ test_object["test_array_prop"] = [1, 2, 3]
+ ui_data_test_prop_array = test_object.id_properties_ui("test_array_prop")
+ ui_data_test_prop_array.update(default=[1, 2])
+ rna_data = ui_data_test_prop_array.as_dict()
+ self.assertEqual(rna_data["default"], [1, 2])
+
+
if __name__ == '__main__':
import sys