diff options
author | Lukas Toenne <lukas.toenne@googlemail.com> | 2013-01-05 18:56:37 +0400 |
---|---|---|
committer | Lukas Toenne <lukas.toenne@googlemail.com> | 2013-01-05 18:56:37 +0400 |
commit | e8b415bdb4ba282e3574cad1463bf2512fe5eb8f (patch) | |
tree | 42d45b749f9442393ef08e41a1c98bc3e4978872 /source/blender/python/intern/bpy_props.c | |
parent | 5ee3cd6c86391e78a6905d1aa9c5241f623dcfc1 (diff) |
This patch adds support in bpy.props for getter/setter callback functions. We already have update callbacks, but generic get/set functions can come in handy in some cases where the functionality is too complex to use a single value.
The current C callback functions are too simple allow a straightforward implementation, in particular they don't receive the PropertyRNA pointer itself as an argument, which means the callback cannot directly access the PropertyRNA's py_data pointers which store the python function objects. For this reason a second runtime variant of these callbacks has been added. It is only used for runtime callbacks and not in makesrna, but otherwise works the same way.
Diffstat (limited to 'source/blender/python/intern/bpy_props.c')
-rw-r--r-- | source/blender/python/intern/bpy_props.c | 1690 |
1 files changed, 1387 insertions, 303 deletions
diff --git a/source/blender/python/intern/bpy_props.c b/source/blender/python/intern/bpy_props.c index 57ddee0c8c0..d3ce25b4f04 100644 --- a/source/blender/python/intern/bpy_props.c +++ b/source/blender/python/intern/bpy_props.c @@ -50,9 +50,11 @@ #include "../generic/py_capi_utils.h" /* initial definition of callback slots we'll probably have more then 1 */ -#define BPY_DATA_CB_SLOT_SIZE 1 +#define BPY_DATA_CB_SLOT_SIZE 3 #define BPY_DATA_CB_SLOT_UPDATE 0 +#define BPY_DATA_CB_SLOT_GET 1 +#define BPY_DATA_CB_SLOT_SET 2 extern BPy_StructRNA *bpy_context_module; @@ -234,21 +236,1066 @@ static void bpy_prop_update_cb(struct bContext *C, struct PointerRNA *ptr, struc } } -static int bpy_prop_callback_check(PyObject *py_func, int argcount) +static int bpy_prop_boolean_get_cb(struct PointerRNA *ptr, struct PropertyRNA *prop) +{ + PyObject **py_data = (PyObject **)RNA_property_py_data_get(prop); + PyObject *py_func; + PyObject *args; + PyObject *self; + PyObject *ret; + const int is_write_ok = pyrna_write_check(); + int value; + + BLI_assert(py_data != NULL); + + if (!is_write_ok) { + pyrna_write_set(TRUE); + } + + py_func = py_data[BPY_DATA_CB_SLOT_GET]; + + args = PyTuple_New(1); + self = pyrna_struct_as_instance(ptr); + PyTuple_SET_ITEM(args, 0, self); + + ret = PyObject_CallObject(py_func, args); + + Py_DECREF(args); + + if (ret == NULL) { + printf_func_error(py_func); + value = FALSE; + } + else { + value = PyLong_AsLong(ret); + + if (value == -1 && PyErr_Occurred()) { + printf_func_error(py_func); + value = FALSE; + } + + Py_DECREF(ret); + } + + if (!is_write_ok) { + pyrna_write_set(FALSE); + } + + return value; +} + +static void bpy_prop_boolean_set_cb(struct PointerRNA *ptr, struct PropertyRNA *prop, int value) +{ + PyObject **py_data = (PyObject **)RNA_property_py_data_get(prop); + PyObject *py_func; + PyObject *args; + PyObject *self; + PyObject *ret; + const int is_write_ok = pyrna_write_check(); + + BLI_assert(py_data != NULL); + + if (!is_write_ok) { + pyrna_write_set(TRUE); + } + + py_func = py_data[BPY_DATA_CB_SLOT_SET]; + + args = PyTuple_New(2); + self = pyrna_struct_as_instance(ptr); + PyTuple_SET_ITEM(args, 0, self); + + PyTuple_SET_ITEM(args, 1, PyBool_FromLong(value)); + + ret = PyObject_CallObject(py_func, args); + + Py_DECREF(args); + + if (ret == NULL) { + printf_func_error(py_func); + } + else { + if (ret != Py_None) { + PyErr_SetString(PyExc_ValueError, "the return value must be None"); + printf_func_error(py_func); + } + + Py_DECREF(ret); + } + + if (!is_write_ok) { + pyrna_write_set(FALSE); + } +} + +static void bpy_prop_boolean_array_get_cb(struct PointerRNA *ptr, struct PropertyRNA *prop, int *values) +{ + PyObject **py_data = (PyObject **)RNA_property_py_data_get(prop); + PyObject *py_func; + PyObject *args; + PyObject *self; + PyObject *ret; + const int is_write_ok = pyrna_write_check(); + int i, len = RNA_property_array_length(ptr, prop); + + BLI_assert(py_data != NULL); + + if (!is_write_ok) { + pyrna_write_set(TRUE); + } + + py_func = py_data[BPY_DATA_CB_SLOT_GET]; + + args = PyTuple_New(1); + self = pyrna_struct_as_instance(ptr); + PyTuple_SET_ITEM(args, 0, self); + + ret = PyObject_CallObject(py_func, args); + + Py_DECREF(args); + + if (ret == NULL) { + printf_func_error(py_func); + + for (i = 0; i < len; ++i) + values[i] = FALSE; + } + else { + if (ret && PyC_AsArray(values, ret, len, &PyBool_Type, FALSE, "BoolVectorProperty get") < 0) { + printf_func_error(py_func); + + for (i = 0; i < len; ++i) + values[i] = FALSE; + } + + Py_DECREF(ret); + } + + if (!is_write_ok) { + pyrna_write_set(FALSE); + } +} + +static void bpy_prop_boolean_array_set_cb(struct PointerRNA *ptr, struct PropertyRNA *prop, const int *values) +{ + PyObject **py_data = (PyObject **)RNA_property_py_data_get(prop); + PyObject *py_func; + PyObject *args; + PyObject *self; + PyObject *ret; + PyObject *py_values; + const int is_write_ok = pyrna_write_check(); + int len = RNA_property_array_length(ptr, prop); + + BLI_assert(py_data != NULL); + + if (!is_write_ok) { + pyrna_write_set(TRUE); + } + + py_func = py_data[BPY_DATA_CB_SLOT_SET]; + + args = PyTuple_New(2); + self = pyrna_struct_as_instance(ptr); + PyTuple_SET_ITEM(args, 0, self); + + py_values = PyC_FromArray(values, len, &PyBool_Type, FALSE, "BoolVectorProperty set"); + if (!py_values) { + printf_func_error(py_func); + } + else + PyTuple_SET_ITEM(args, 1, py_values); + + ret = PyObject_CallObject(py_func, args); + + Py_DECREF(args); + + if (ret == NULL) { + printf_func_error(py_func); + } + else { + if (ret != Py_None) { + PyErr_SetString(PyExc_ValueError, "the return value must be None"); + printf_func_error(py_func); + } + + Py_DECREF(ret); + } + + if (!is_write_ok) { + pyrna_write_set(FALSE); + } +} + +static int bpy_prop_int_get_cb(struct PointerRNA *ptr, struct PropertyRNA *prop) +{ + PyObject **py_data = (PyObject **)RNA_property_py_data_get(prop); + PyObject *py_func; + PyObject *args; + PyObject *self; + PyObject *ret; + const int is_write_ok = pyrna_write_check(); + int value; + + BLI_assert(py_data != NULL); + + if (!is_write_ok) { + pyrna_write_set(TRUE); + } + + py_func = py_data[BPY_DATA_CB_SLOT_GET]; + + args = PyTuple_New(1); + self = pyrna_struct_as_instance(ptr); + PyTuple_SET_ITEM(args, 0, self); + + ret = PyObject_CallObject(py_func, args); + + Py_DECREF(args); + + if (ret == NULL) { + printf_func_error(py_func); + value = 0.0f; + } + else { + value = PyLong_AsLong(ret); + + if (value == -1 && PyErr_Occurred()) { + printf_func_error(py_func); + value = 0; + } + + Py_DECREF(ret); + } + + if (!is_write_ok) { + pyrna_write_set(FALSE); + } + + return value; +} + +static void bpy_prop_int_set_cb(struct PointerRNA *ptr, struct PropertyRNA *prop, int value) +{ + PyObject **py_data = (PyObject **)RNA_property_py_data_get(prop); + PyObject *py_func; + PyObject *args; + PyObject *self; + PyObject *ret; + const int is_write_ok = pyrna_write_check(); + + BLI_assert(py_data != NULL); + + if (!is_write_ok) { + pyrna_write_set(TRUE); + } + + py_func = py_data[BPY_DATA_CB_SLOT_SET]; + + args = PyTuple_New(2); + self = pyrna_struct_as_instance(ptr); + PyTuple_SET_ITEM(args, 0, self); + + PyTuple_SET_ITEM(args, 1, PyLong_FromLong(value)); + + ret = PyObject_CallObject(py_func, args); + + Py_DECREF(args); + + if (ret == NULL) { + printf_func_error(py_func); + } + else { + if (ret != Py_None) { + PyErr_SetString(PyExc_ValueError, "the return value must be None"); + printf_func_error(py_func); + } + + Py_DECREF(ret); + } + + if (!is_write_ok) { + pyrna_write_set(FALSE); + } +} + +static void bpy_prop_int_array_get_cb(struct PointerRNA *ptr, struct PropertyRNA *prop, int *values) +{ + PyObject **py_data = (PyObject **)RNA_property_py_data_get(prop); + PyObject *py_func; + PyObject *args; + PyObject *self; + PyObject *ret; + const int is_write_ok = pyrna_write_check(); + int i, len = RNA_property_array_length(ptr, prop); + + BLI_assert(py_data != NULL); + + if (!is_write_ok) { + pyrna_write_set(TRUE); + } + + py_func = py_data[BPY_DATA_CB_SLOT_GET]; + + args = PyTuple_New(1); + self = pyrna_struct_as_instance(ptr); + PyTuple_SET_ITEM(args, 0, self); + + ret = PyObject_CallObject(py_func, args); + + Py_DECREF(args); + + if (ret == NULL) { + printf_func_error(py_func); + + for (i = 0; i < len; ++i) + values[i] = 0; + } + else { + if (ret && PyC_AsArray(values, ret, len, &PyLong_Type, FALSE, "IntVectorProperty get") < 0) { + printf_func_error(py_func); + + for (i = 0; i < len; ++i) + values[i] = 0; + } + + Py_DECREF(ret); + } + + if (!is_write_ok) { + pyrna_write_set(FALSE); + } +} + +static void bpy_prop_int_array_set_cb(struct PointerRNA *ptr, struct PropertyRNA *prop, const int *values) +{ + PyObject **py_data = (PyObject **)RNA_property_py_data_get(prop); + PyObject *py_func; + PyObject *args; + PyObject *self; + PyObject *ret; + PyObject *py_values; + const int is_write_ok = pyrna_write_check(); + int len = RNA_property_array_length(ptr, prop); + + BLI_assert(py_data != NULL); + + if (!is_write_ok) { + pyrna_write_set(TRUE); + } + + py_func = py_data[BPY_DATA_CB_SLOT_SET]; + + args = PyTuple_New(2); + self = pyrna_struct_as_instance(ptr); + PyTuple_SET_ITEM(args, 0, self); + + py_values = PyC_FromArray(values, len, &PyLong_Type, FALSE, "IntVectorProperty set"); + if (!py_values) { + printf_func_error(py_func); + } + else + PyTuple_SET_ITEM(args, 1, py_values); + + ret = PyObject_CallObject(py_func, args); + + Py_DECREF(args); + + if (ret == NULL) { + printf_func_error(py_func); + } + else { + if (ret != Py_None) { + PyErr_SetString(PyExc_ValueError, "the return value must be None"); + printf_func_error(py_func); + } + + Py_DECREF(ret); + } + + if (!is_write_ok) { + pyrna_write_set(FALSE); + } +} + +static float bpy_prop_float_get_cb(struct PointerRNA *ptr, struct PropertyRNA *prop) +{ + PyObject **py_data = (PyObject **)RNA_property_py_data_get(prop); + PyObject *py_func; + PyObject *args; + PyObject *self; + PyObject *ret; + const int is_write_ok = pyrna_write_check(); + float value; + + BLI_assert(py_data != NULL); + + if (!is_write_ok) { + pyrna_write_set(TRUE); + } + + py_func = py_data[BPY_DATA_CB_SLOT_GET]; + + args = PyTuple_New(1); + self = pyrna_struct_as_instance(ptr); + PyTuple_SET_ITEM(args, 0, self); + + ret = PyObject_CallObject(py_func, args); + + Py_DECREF(args); + + if (ret == NULL) { + printf_func_error(py_func); + value = 0.0f; + } + else { + value = PyFloat_AsDouble(ret); + + if (value == -1.0f && PyErr_Occurred()) { + printf_func_error(py_func); + value = 0.0f; + } + + Py_DECREF(ret); + } + + if (!is_write_ok) { + pyrna_write_set(FALSE); + } + + return value; +} + +static void bpy_prop_float_set_cb(struct PointerRNA *ptr, struct PropertyRNA *prop, float value) +{ + PyObject **py_data = (PyObject **)RNA_property_py_data_get(prop); + PyObject *py_func; + PyObject *args; + PyObject *self; + PyObject *ret; + const int is_write_ok = pyrna_write_check(); + + BLI_assert(py_data != NULL); + + if (!is_write_ok) { + pyrna_write_set(TRUE); + } + + py_func = py_data[BPY_DATA_CB_SLOT_SET]; + + args = PyTuple_New(2); + self = pyrna_struct_as_instance(ptr); + PyTuple_SET_ITEM(args, 0, self); + + PyTuple_SET_ITEM(args, 1, PyFloat_FromDouble(value)); + + ret = PyObject_CallObject(py_func, args); + + Py_DECREF(args); + + if (ret == NULL) { + printf_func_error(py_func); + } + else { + if (ret != Py_None) { + PyErr_SetString(PyExc_ValueError, "the return value must be None"); + printf_func_error(py_func); + } + + Py_DECREF(ret); + } + + if (!is_write_ok) { + pyrna_write_set(FALSE); + } +} + +static void bpy_prop_float_array_get_cb(struct PointerRNA *ptr, struct PropertyRNA *prop, float *values) +{ + PyObject **py_data = (PyObject **)RNA_property_py_data_get(prop); + PyObject *py_func; + PyObject *args; + PyObject *self; + PyObject *ret; + const int is_write_ok = pyrna_write_check(); + int i, len = RNA_property_array_length(ptr, prop); + + BLI_assert(py_data != NULL); + + if (!is_write_ok) { + pyrna_write_set(TRUE); + } + + py_func = py_data[BPY_DATA_CB_SLOT_GET]; + + args = PyTuple_New(1); + self = pyrna_struct_as_instance(ptr); + PyTuple_SET_ITEM(args, 0, self); + + ret = PyObject_CallObject(py_func, args); + + Py_DECREF(args); + + if (ret == NULL) { + printf_func_error(py_func); + + for (i = 0; i < len; ++i) + values[i] = 0.0f; + } + else { + if (ret && PyC_AsArray(values, ret, len, &PyFloat_Type, FALSE, "FloatVectorProperty get") < 0) { + printf_func_error(py_func); + + for (i = 0; i < len; ++i) + values[i] = 0.0f; + } + + Py_DECREF(ret); + } + + if (!is_write_ok) { + pyrna_write_set(FALSE); + } +} + +static void bpy_prop_float_array_set_cb(struct PointerRNA *ptr, struct PropertyRNA *prop, const float *values) +{ + PyObject **py_data = (PyObject **)RNA_property_py_data_get(prop); + PyObject *py_func; + PyObject *args; + PyObject *self; + PyObject *ret; + PyObject *py_values; + const int is_write_ok = pyrna_write_check(); + int len = RNA_property_array_length(ptr, prop); + + BLI_assert(py_data != NULL); + + if (!is_write_ok) { + pyrna_write_set(TRUE); + } + + py_func = py_data[BPY_DATA_CB_SLOT_SET]; + + args = PyTuple_New(2); + self = pyrna_struct_as_instance(ptr); + PyTuple_SET_ITEM(args, 0, self); + + py_values = PyC_FromArray(values, len, &PyFloat_Type, FALSE, "FloatVectorProperty set"); + if (!py_values) { + printf_func_error(py_func); + } + else + PyTuple_SET_ITEM(args, 1, py_values); + + ret = PyObject_CallObject(py_func, args); + + Py_DECREF(args); + + if (ret == NULL) { + printf_func_error(py_func); + } + else { + if (ret != Py_None) { + PyErr_SetString(PyExc_ValueError, "the return value must be None"); + printf_func_error(py_func); + } + + Py_DECREF(ret); + } + + if (!is_write_ok) { + pyrna_write_set(FALSE); + } +} + +static void bpy_prop_string_get_cb(struct PointerRNA *ptr, struct PropertyRNA *prop, char *value) +{ + PyObject **py_data = (PyObject **)RNA_property_py_data_get(prop); + PyObject *py_func; + PyObject *args; + PyObject *self; + PyObject *ret; + const int is_write_ok = pyrna_write_check(); + + BLI_assert(py_data != NULL); + + if (!is_write_ok) { + pyrna_write_set(TRUE); + } + + py_func = py_data[BPY_DATA_CB_SLOT_GET]; + + args = PyTuple_New(1); + self = pyrna_struct_as_instance(ptr); + PyTuple_SET_ITEM(args, 0, self); + + ret = PyObject_CallObject(py_func, args); + + Py_DECREF(args); + + if (ret == NULL) { + printf_func_error(py_func); + value[0] = '\0'; + } + else { + Py_ssize_t length; + char *buffer = _PyUnicode_AsStringAndSize(ret, &length); + + if (!buffer && PyErr_Occurred()) { + printf_func_error(py_func); + value[0] = '\0'; + } + else { + memcpy(value, buffer, length+1); + } + + Py_DECREF(ret); + } + + if (!is_write_ok) { + pyrna_write_set(FALSE); + } +} + +static int bpy_prop_string_length_cb(struct PointerRNA *ptr, struct PropertyRNA *prop) +{ + PyObject **py_data = (PyObject **)RNA_property_py_data_get(prop); + PyObject *py_func; + PyObject *args; + PyObject *self; + PyObject *ret; + const int is_write_ok = pyrna_write_check(); + int length; + + BLI_assert(py_data != NULL); + + if (!is_write_ok) { + pyrna_write_set(TRUE); + } + + py_func = py_data[BPY_DATA_CB_SLOT_GET]; + + args = PyTuple_New(1); + self = pyrna_struct_as_instance(ptr); + PyTuple_SET_ITEM(args, 0, self); + + ret = PyObject_CallObject(py_func, args); + + Py_DECREF(args); + + if (ret == NULL) { + printf_func_error(py_func); + } + else { + length = PyUnicode_GetLength(ret); + Py_DECREF(ret); + } + + if (!is_write_ok) { + pyrna_write_set(FALSE); + } + + return length; +} + +static void bpy_prop_string_set_cb(struct PointerRNA *ptr, struct PropertyRNA *prop, const char *value) +{ + PyObject **py_data = (PyObject **)RNA_property_py_data_get(prop); + PyObject *py_func; + PyObject *args; + PyObject *self; + PyObject *ret; + const int is_write_ok = pyrna_write_check(); + PyObject *py_value; + + BLI_assert(py_data != NULL); + + if (!is_write_ok) { + pyrna_write_set(TRUE); + } + + py_func = py_data[BPY_DATA_CB_SLOT_SET]; + + args = PyTuple_New(2); + self = pyrna_struct_as_instance(ptr); + PyTuple_SET_ITEM(args, 0, self); + + py_value = PyUnicode_FromString(value); + if (!py_value) { + PyErr_SetString(PyExc_ValueError, "the return value must be a string"); + printf_func_error(py_func); + } + else + PyTuple_SET_ITEM(args, 1, py_value); + + ret = PyObject_CallObject(py_func, args); + + Py_DECREF(args); + + if (ret == NULL) { + printf_func_error(py_func); + } + else { + if (ret != Py_None) { + PyErr_SetString(PyExc_ValueError, "the return value must be None"); + printf_func_error(py_func); + } + + Py_DECREF(ret); + } + + if (!is_write_ok) { + pyrna_write_set(FALSE); + } +} + +static int bpy_prop_enum_get_cb(struct PointerRNA *ptr, struct PropertyRNA *prop) +{ + PyObject **py_data = (PyObject **)RNA_property_py_data_get(prop); + PyObject *py_func; + PyObject *args; + PyObject *self; + PyObject *ret; + const int is_write_ok = pyrna_write_check(); + int value; + + BLI_assert(py_data != NULL); + + if (!is_write_ok) { + pyrna_write_set(TRUE); + } + + py_func = py_data[BPY_DATA_CB_SLOT_GET]; + + args = PyTuple_New(1); + self = pyrna_struct_as_instance(ptr); + PyTuple_SET_ITEM(args, 0, self); + + ret = PyObject_CallObject(py_func, args); + + Py_DECREF(args); + + if (ret == NULL) { + printf_func_error(py_func); + value = RNA_property_enum_get_default(ptr, prop); + } + else { + value = PyLong_AsLong(ret); + + if (value == -1 && PyErr_Occurred()) { + printf_func_error(py_func); + value = RNA_property_enum_get_default(ptr, prop); + } + + Py_DECREF(ret); + } + + if (!is_write_ok) { + pyrna_write_set(FALSE); + } + + return value; +} + +static void bpy_prop_enum_set_cb(struct PointerRNA *ptr, struct PropertyRNA *prop, int value) +{ + PyObject **py_data = (PyObject **)RNA_property_py_data_get(prop); + PyObject *py_func; + PyObject *args; + PyObject *self; + PyObject *ret; + const int is_write_ok = pyrna_write_check(); + + BLI_assert(py_data != NULL); + + if (!is_write_ok) { + pyrna_write_set(TRUE); + } + + py_func = py_data[BPY_DATA_CB_SLOT_SET]; + + args = PyTuple_New(2); + self = pyrna_struct_as_instance(ptr); + PyTuple_SET_ITEM(args, 0, self); + + PyTuple_SET_ITEM(args, 1, PyLong_FromLong(value)); + + ret = PyObject_CallObject(py_func, args); + + Py_DECREF(args); + + if (ret == NULL) { + printf_func_error(py_func); + } + else { + if (ret != Py_None) { + PyErr_SetString(PyExc_ValueError, "the return value must be None"); + printf_func_error(py_func); + } + + Py_DECREF(ret); + } + + if (!is_write_ok) { + pyrna_write_set(FALSE); + } +} + +/* utility function we need for parsing int's in an if statement */ +static int py_long_as_int(PyObject *py_long, int *r_int) +{ + if (PyLong_CheckExact(py_long)) { + *r_int = (int)PyLong_AS_LONG(py_long); + return 0; + } + else { + return -1; + } +} + +#if 0 +/* copies orig to buf, then sets orig to buf, returns copy length */ +static size_t strswapbufcpy(char *buf, const char **orig) +{ + const char *src = *orig; + char *dst = buf; + size_t i = 0; + *orig = buf; + while ((*dst = *src)) { dst++; src++; i++; } + return i + 1; /* include '\0' */ +} +#endif + +static EnumPropertyItem *enum_items_from_py(PyObject *seq_fast, PyObject *def, int *defvalue, const short is_enum_flag) +{ + EnumPropertyItem *items; + PyObject *item; + const Py_ssize_t seq_len = PySequence_Fast_GET_SIZE(seq_fast); + Py_ssize_t totbuf = 0; + int i; + short def_used = 0; + const char *def_cmp = NULL; + + if (is_enum_flag) { + if (seq_len > RNA_ENUM_BITFLAG_SIZE) { + PyErr_SetString(PyExc_TypeError, + "EnumProperty(...): maximum " + STRINGIFY(RNA_ENUM_BITFLAG_SIZE) + " members for a ENUM_FLAG type property"); + return NULL; + } + if (def && !PySet_Check(def)) { + PyErr_Format(PyExc_TypeError, + "EnumProperty(...): default option must be a 'set' " + "type when ENUM_FLAG is enabled, not a '%.200s'", + Py_TYPE(def)->tp_name); + return NULL; + } + } + else { + if (def) { + def_cmp = _PyUnicode_AsString(def); + if (def_cmp == NULL) { + PyErr_Format(PyExc_TypeError, + "EnumProperty(...): default option must be a 'str' " + "type when ENUM_FLAG is disabled, not a '%.200s'", + Py_TYPE(def)->tp_name); + return NULL; + } + } + } + + /* blank value */ + *defvalue = 0; + + items = MEM_callocN(sizeof(EnumPropertyItem) * (seq_len + 1), "enum_items_from_py1"); + + for (i = 0; i < seq_len; i++) { + EnumPropertyItem tmp = {0, "", 0, "", ""}; + Py_ssize_t item_size; + Py_ssize_t id_str_size; + Py_ssize_t name_str_size; + Py_ssize_t desc_str_size; + + item = PySequence_Fast_GET_ITEM(seq_fast, i); + + if ((PyTuple_CheckExact(item)) && + (item_size = PyTuple_GET_SIZE(item)) && + (item_size == 3 || item_size == 4) && + (tmp.identifier = _PyUnicode_AsStringAndSize(PyTuple_GET_ITEM(item, 0), &id_str_size)) && + (tmp.name = _PyUnicode_AsStringAndSize(PyTuple_GET_ITEM(item, 1), &name_str_size)) && + (tmp.description = _PyUnicode_AsStringAndSize(PyTuple_GET_ITEM(item, 2), &desc_str_size)) && + /* TODO, number isn't ensured to be unique from the script author */ + (item_size < 4 || py_long_as_int(PyTuple_GET_ITEM(item, 3), &tmp.value) != -1)) + { + if (is_enum_flag) { + if (item_size < 4) { + tmp.value = 1 << i; + } + + if (def && PySet_Contains(def, PyTuple_GET_ITEM(item, 0))) { + *defvalue |= tmp.value; + def_used++; + } + } + else { + if (item_size < 4) { + tmp.value = i; + } + + if (def && def_used == 0 && strcmp(def_cmp, tmp.identifier) == 0) { + *defvalue = tmp.value; + def_used++; /* only ever 1 */ + } + } + + items[i] = tmp; + + /* calculate combine string length */ + totbuf += id_str_size + name_str_size + desc_str_size + 3; /* 3 is for '\0's */ + } + else { + MEM_freeN(items); + PyErr_SetString(PyExc_TypeError, + "EnumProperty(...): expected a tuple containing " + "(identifier, name, description) and optionally a " + "unique number"); + return NULL; + } + + } + + if (is_enum_flag) { + /* strict check that all set members were used */ + if (def && def_used != PySet_GET_SIZE(def)) { + MEM_freeN(items); + + PyErr_Format(PyExc_TypeError, + "EnumProperty(..., default={...}): set has %d unused member(s)", + PySet_GET_SIZE(def) - def_used); + return NULL; + } + } + else { + if (def && def_used == 0) { + MEM_freeN(items); + + PyErr_Format(PyExc_TypeError, + "EnumProperty(..., default=\'%s\'): not found in enum members", + def_cmp); + return NULL; + } + } + + /* disabled duplicating strings because the array can still be freed and + * the strings from it referenced, for now we can't support dynamically + * created strings from python. */ +#if 0 + /* this would all work perfectly _but_ the python strings may be freed + * immediately after use, so we need to duplicate them, ugh. + * annoying because it works most of the time without this. */ + { + EnumPropertyItem *items_dup = MEM_mallocN((sizeof(EnumPropertyItem) * (seq_len + 1)) + (sizeof(char) * totbuf), + "enum_items_from_py2"); + EnumPropertyItem *items_ptr = items_dup; + char *buf = ((char *)items_dup) + (sizeof(EnumPropertyItem) * (seq_len + 1)); + memcpy(items_dup, items, sizeof(EnumPropertyItem) * (seq_len + 1)); + for (i = 0; i < seq_len; i++, items_ptr++) { + buf += strswapbufcpy(buf, &items_ptr->identifier); + buf += strswapbufcpy(buf, &items_ptr->name); + buf += strswapbufcpy(buf, &items_ptr->description); + } + MEM_freeN(items); + items = items_dup; + } + /* end string duplication */ +#endif + + return items; +} + +static EnumPropertyItem *bpy_prop_enum_itemf_cb(struct bContext *C, PointerRNA *ptr, PropertyRNA *prop, int *free) +{ + PyGILState_STATE gilstate; + + PyObject *py_func = RNA_property_enum_py_data_get(prop); + PyObject *self = NULL; + PyObject *args; + PyObject *items; /* returned from the function call */ + + EnumPropertyItem *eitems = NULL; + int err = 0; + + bpy_context_set(C, &gilstate); + + args = PyTuple_New(2); + self = pyrna_struct_as_instance(ptr); + PyTuple_SET_ITEM(args, 0, self); + + /* now get the context */ + PyTuple_SET_ITEM(args, 1, (PyObject *)bpy_context_module); + Py_INCREF(bpy_context_module); + + items = PyObject_CallObject(py_func, args); + + Py_DECREF(args); + + if (items == NULL) { + err = -1; + } + else { + PyObject *items_fast; + int defvalue_dummy = 0; + + if (!(items_fast = PySequence_Fast(items, "EnumProperty(...): " + "return value from the callback was not a sequence"))) + { + err = -1; + } + else { + eitems = enum_items_from_py(items_fast, NULL, &defvalue_dummy, + (RNA_property_flag(prop) & PROP_ENUM_FLAG) != 0); + + Py_DECREF(items_fast); + + if (!eitems) { + err = -1; + } + } + + Py_DECREF(items); + } + + if (err != -1) { /* worked */ + *free = 1; + } + else { + printf_func_error(py_func); + + eitems = DummyRNA_NULL_items; + } + + + bpy_context_clear(C, &gilstate); + return eitems; +} + +static int bpy_prop_callback_check(PyObject *py_func, const char *keyword, int argcount) { if (py_func && py_func != Py_None) { if (!PyFunction_Check(py_func)) { PyErr_Format(PyExc_TypeError, - "update keyword: expected a function type, not a %.200s", - Py_TYPE(py_func)->tp_name); + "%s keyword: expected a function type, not a %.200s", + keyword, Py_TYPE(py_func)->tp_name); return -1; } else { PyCodeObject *f_code = (PyCodeObject *)PyFunction_GET_CODE(py_func); if (f_code->co_argcount != argcount) { PyErr_Format(PyExc_TypeError, - "update keyword: expected a function taking %d arguments, not %d", - argcount, f_code->co_argcount); + "%s keyword: expected a function taking %d arguments, not %d", + keyword, argcount, f_code->co_argcount); return -1; } } @@ -257,32 +1304,215 @@ static int bpy_prop_callback_check(PyObject *py_func, int argcount) return 0; } +static PyObject **bpy_prop_py_data_get(struct PropertyRNA *prop) +{ + PyObject **py_data = RNA_property_py_data_get(prop); + if (!py_data) { + py_data = MEM_callocN(sizeof(PyObject *) * BPY_DATA_CB_SLOT_SIZE, __func__); + RNA_def_py_data(prop, py_data); + } + return py_data; +} -static int bpy_prop_callback_assign(struct PropertyRNA *prop, PyObject *update_cb) +static void bpy_prop_callback_assign_update(struct PropertyRNA *prop, PyObject *update_cb) { /* assume this is already checked for type and arg length */ if (update_cb && update_cb != Py_None) { - PyObject **py_data = MEM_callocN(sizeof(PyObject *) * BPY_DATA_CB_SLOT_SIZE, __func__); + PyObject **py_data = bpy_prop_py_data_get(prop); + RNA_def_property_update_runtime(prop, (void *)bpy_prop_update_cb); py_data[BPY_DATA_CB_SLOT_UPDATE] = update_cb; - RNA_def_py_data(prop, py_data); RNA_def_property_flag(prop, PROP_CONTEXT_PROPERTY_UPDATE); } +} - return 0; +static void bpy_prop_callback_assign_boolean(struct PropertyRNA *prop, PyObject *get_cb, PyObject *set_cb) +{ + BooleanPropertyGetFunc rna_get_cb = NULL; + BooleanPropertySetFunc rna_set_cb = NULL; + + if (get_cb && get_cb != Py_None) { + PyObject **py_data = bpy_prop_py_data_get(prop); + + rna_get_cb = bpy_prop_boolean_get_cb; + py_data[BPY_DATA_CB_SLOT_GET] = get_cb; + } + + if (set_cb && set_cb != Py_None) { + PyObject **py_data = bpy_prop_py_data_get(prop); + + rna_set_cb = bpy_prop_boolean_set_cb; + py_data[BPY_DATA_CB_SLOT_SET] = set_cb; + } + + RNA_def_property_boolean_funcs_runtime(prop, rna_get_cb, rna_set_cb); } -/* utility function we need for parsing int's in an if statement */ -static int py_long_as_int(PyObject *py_long, int *r_int) +static void bpy_prop_callback_assign_boolean_array(struct PropertyRNA *prop, PyObject *get_cb, PyObject *set_cb) { - if (PyLong_CheckExact(py_long)) { - *r_int = (int)PyLong_AS_LONG(py_long); - return 0; + BooleanArrayPropertyGetFunc rna_get_cb = NULL; + BooleanArrayPropertySetFunc rna_set_cb = NULL; + + if (get_cb && get_cb != Py_None) { + PyObject **py_data = bpy_prop_py_data_get(prop); + + rna_get_cb = bpy_prop_boolean_array_get_cb; + py_data[BPY_DATA_CB_SLOT_GET] = get_cb; } - else { - return -1; + + if (set_cb && set_cb != Py_None) { + PyObject **py_data = bpy_prop_py_data_get(prop); + + rna_set_cb = bpy_prop_boolean_array_set_cb; + py_data[BPY_DATA_CB_SLOT_SET] = set_cb; + } + + RNA_def_property_boolean_array_funcs_runtime(prop, rna_get_cb, rna_set_cb); +} + +static void bpy_prop_callback_assign_int(struct PropertyRNA *prop, PyObject *get_cb, PyObject *set_cb) +{ + IntPropertyGetFunc rna_get_cb = NULL; + IntPropertySetFunc rna_set_cb = NULL; + + if (get_cb && get_cb != Py_None) { + PyObject **py_data = bpy_prop_py_data_get(prop); + + rna_get_cb = bpy_prop_int_get_cb; + py_data[BPY_DATA_CB_SLOT_GET] = get_cb; } + + if (set_cb && set_cb != Py_None) { + PyObject **py_data = bpy_prop_py_data_get(prop); + + rna_set_cb = bpy_prop_int_set_cb; + py_data[BPY_DATA_CB_SLOT_SET] = set_cb; + } + + RNA_def_property_int_funcs_runtime(prop, rna_get_cb, rna_set_cb, NULL); +} + +static void bpy_prop_callback_assign_int_array(struct PropertyRNA *prop, PyObject *get_cb, PyObject *set_cb) +{ + IntArrayPropertyGetFunc rna_get_cb = NULL; + IntArrayPropertySetFunc rna_set_cb = NULL; + + if (get_cb && get_cb != Py_None) { + PyObject **py_data = bpy_prop_py_data_get(prop); + + rna_get_cb = bpy_prop_int_array_get_cb; + py_data[BPY_DATA_CB_SLOT_GET] = get_cb; + } + + if (set_cb && set_cb != Py_None) { + PyObject **py_data = bpy_prop_py_data_get(prop); + + rna_set_cb = bpy_prop_int_array_set_cb; + py_data[BPY_DATA_CB_SLOT_SET] = set_cb; + } + + RNA_def_property_int_array_funcs_runtime(prop, rna_get_cb, rna_set_cb, NULL); +} + +static void bpy_prop_callback_assign_float(struct PropertyRNA *prop, PyObject *get_cb, PyObject *set_cb) +{ + FloatPropertyGetFunc rna_get_cb = NULL; + FloatPropertySetFunc rna_set_cb = NULL; + + if (get_cb && get_cb != Py_None) { + PyObject **py_data = bpy_prop_py_data_get(prop); + + rna_get_cb = bpy_prop_float_get_cb; + py_data[BPY_DATA_CB_SLOT_GET] = get_cb; + } + + if (set_cb && set_cb != Py_None) { + PyObject **py_data = bpy_prop_py_data_get(prop); + + rna_set_cb = bpy_prop_float_set_cb; + py_data[BPY_DATA_CB_SLOT_SET] = set_cb; + } + + RNA_def_property_float_funcs_runtime(prop, rna_get_cb, rna_set_cb, NULL); +} + +static void bpy_prop_callback_assign_float_array(struct PropertyRNA *prop, PyObject *get_cb, PyObject *set_cb) +{ + FloatArrayPropertyGetFunc rna_get_cb = NULL; + FloatArrayPropertySetFunc rna_set_cb = NULL; + + if (get_cb && get_cb != Py_None) { + PyObject **py_data = bpy_prop_py_data_get(prop); + + rna_get_cb = bpy_prop_float_array_get_cb; + py_data[BPY_DATA_CB_SLOT_GET] = get_cb; + } + + if (set_cb && set_cb != Py_None) { + PyObject **py_data = bpy_prop_py_data_get(prop); + + rna_set_cb = bpy_prop_float_array_set_cb; + py_data[BPY_DATA_CB_SLOT_SET] = set_cb; + } + + RNA_def_property_float_array_funcs_runtime(prop, rna_get_cb, rna_set_cb, NULL); +} + +static void bpy_prop_callback_assign_string(struct PropertyRNA *prop, PyObject *get_cb, PyObject *set_cb) +{ + StringPropertyGetFunc rna_get_cb = NULL; + StringPropertyLengthFunc rna_length_cb = NULL; + StringPropertySetFunc rna_set_cb = NULL; + + if (get_cb && get_cb != Py_None) { + PyObject **py_data = bpy_prop_py_data_get(prop); + + rna_get_cb = bpy_prop_string_get_cb; + rna_length_cb = bpy_prop_string_length_cb; + py_data[BPY_DATA_CB_SLOT_GET] = get_cb; + } + + if (set_cb && set_cb != Py_None) { + PyObject **py_data = bpy_prop_py_data_get(prop); + + rna_set_cb = bpy_prop_string_set_cb; + py_data[BPY_DATA_CB_SLOT_SET] = set_cb; + } + + RNA_def_property_string_funcs_runtime(prop, rna_get_cb, rna_length_cb, rna_set_cb); +} + +static void bpy_prop_callback_assign_enum(struct PropertyRNA *prop, PyObject *get_cb, PyObject *set_cb, PyObject *itemf_cb) +{ + EnumPropertyGetFunc rna_get_cb = NULL; + EnumPropertyItemFunc rna_itemf_cb = NULL; + EnumPropertySetFunc rna_set_cb = NULL; + + if (get_cb && get_cb != Py_None) { + PyObject **py_data = bpy_prop_py_data_get(prop); + + rna_get_cb = bpy_prop_enum_get_cb; + py_data[BPY_DATA_CB_SLOT_GET] = get_cb; + } + + if (set_cb && set_cb != Py_None) { + PyObject **py_data = bpy_prop_py_data_get(prop); + + rna_set_cb = bpy_prop_enum_set_cb; + py_data[BPY_DATA_CB_SLOT_SET] = set_cb; + } + + if (itemf_cb && itemf_cb != Py_None) { + rna_itemf_cb = bpy_prop_enum_itemf_cb; + RNA_def_property_enum_py_data(prop, (void *)itemf_cb); + + /* watch out!, if a user is tricky they can probably crash blender + * if they manage to free the callback, take care! */ + /* Py_INCREF(itemf_cb); */ + } + + RNA_def_property_enum_funcs_runtime(prop, rna_get_cb, rna_set_cb, rna_itemf_cb); } /* this define runs at the start of each function and deals with @@ -386,7 +1616,9 @@ PyDoc_STRVAR(BPy_BoolProperty_doc, "default=False, " "options={'ANIMATABLE'}, " "subtype='NONE', " - "update=None)\n" + "update=None, " + "get=None, " + "set=None)\n" "\n" " Returns a new boolean property definition.\n" "\n" @@ -406,7 +1638,7 @@ static PyObject *BPy_BoolProperty(PyObject *self, PyObject *args, PyObject *kw) if (srna) { static const char *kwlist[] = {"attr", "name", "description", "default", - "options", "subtype", "update", NULL}; + "options", "subtype", "update", "get", "set", NULL}; const char *id = NULL, *name = NULL, *description = ""; int id_len; int def = 0; @@ -416,20 +1648,28 @@ static PyObject *BPy_BoolProperty(PyObject *self, PyObject *args, PyObject *kw) char *pysubtype = NULL; int subtype = PROP_NONE; PyObject *update_cb = NULL; + PyObject *get_cb = NULL; + PyObject *set_cb = NULL; if (!PyArg_ParseTupleAndKeywords(args, kw, - "s#|ssiO!sO:BoolProperty", + "s#|ssiO!sOOO:BoolProperty", (char **)kwlist, &id, &id_len, &name, &description, &def, &PySet_Type, &pyopts, &pysubtype, - &update_cb)) + &update_cb, &get_cb, &set_cb)) { return NULL; } BPY_PROPDEF_SUBTYPE_CHECK(BoolProperty, property_flag_items, property_subtype_number_items); - if (bpy_prop_callback_check(update_cb, 2) == -1) { + if (bpy_prop_callback_check(update_cb, "update", 2) == -1) { + return NULL; + } + if (bpy_prop_callback_check(get_cb, "get", 1) == -1) { + return NULL; + } + if (bpy_prop_callback_check(set_cb, "set", 2) == -1) { return NULL; } @@ -443,7 +1683,8 @@ static PyObject *BPy_BoolProperty(PyObject *self, PyObject *args, PyObject *kw) if (opts & PROP_SKIP_SAVE) RNA_def_property_flag(prop, PROP_SKIP_SAVE); if (opts & PROP_LIB_EXCEPTION) RNA_def_property_flag(prop, PROP_LIB_EXCEPTION); } - bpy_prop_callback_assign(prop, update_cb); + bpy_prop_callback_assign_update(prop, update_cb); + bpy_prop_callback_assign_boolean(prop, get_cb, set_cb); RNA_def_property_duplicate_pointers(srna, prop); } @@ -457,7 +1698,9 @@ PyDoc_STRVAR(BPy_BoolVectorProperty_doc, "options={'ANIMATABLE'}, " "subtype='NONE', " "size=3, " - "update=None)\n" + "update=None, " + "get=None, " + "set=None)\n" "\n" " Returns a new vector boolean property definition.\n" "\n" @@ -483,7 +1726,7 @@ static PyObject *BPy_BoolVectorProperty(PyObject *self, PyObject *args, PyObject if (srna) { static const char *kwlist[] = {"attr", "name", "description", "default", - "options", "subtype", "size", "update", NULL}; + "options", "subtype", "size", "update", "get", "set", NULL}; const char *id = NULL, *name = NULL, *description = ""; int id_len; int def[PYRNA_STACK_ARRAY] = {0}; @@ -495,13 +1738,15 @@ static PyObject *BPy_BoolVectorProperty(PyObject *self, PyObject *args, PyObject char *pysubtype = NULL; int subtype = PROP_NONE; PyObject *update_cb = NULL; + PyObject *get_cb = NULL; + PyObject *set_cb = NULL; if (!PyArg_ParseTupleAndKeywords(args, kw, - "s#|ssOO!siO:BoolVectorProperty", + "s#|ssOO!siOOO:BoolVectorProperty", (char **)kwlist, &id, &id_len, &name, &description, &pydef, &PySet_Type, &pyopts, &pysubtype, &size, - &update_cb)) + &update_cb, &get_cb, &set_cb)) { return NULL; } @@ -518,7 +1763,13 @@ static PyObject *BPy_BoolVectorProperty(PyObject *self, PyObject *args, PyObject if (pydef && PyC_AsArray(def, pydef, size, &PyBool_Type, FALSE, "BoolVectorProperty(default=sequence)") < 0) return NULL; - if (bpy_prop_callback_check(update_cb, 2) == -1) { + if (bpy_prop_callback_check(update_cb, "update", 2) == -1) { + return NULL; + } + if (bpy_prop_callback_check(get_cb, "get", 1) == -1) { + return NULL; + } + if (bpy_prop_callback_check(set_cb, "set", 2) == -1) { return NULL; } @@ -534,7 +1785,8 @@ static PyObject *BPy_BoolVectorProperty(PyObject *self, PyObject *args, PyObject if (opts & PROP_SKIP_SAVE) RNA_def_property_flag(prop, PROP_SKIP_SAVE); if (opts & PROP_LIB_EXCEPTION) RNA_def_property_flag(prop, PROP_LIB_EXCEPTION); } - bpy_prop_callback_assign(prop, update_cb); + bpy_prop_callback_assign_update(prop, update_cb); + bpy_prop_callback_assign_boolean_array(prop, get_cb, set_cb); RNA_def_property_duplicate_pointers(srna, prop); } @@ -550,7 +1802,9 @@ PyDoc_STRVAR(BPy_IntProperty_doc, "step=1, " "options={'ANIMATABLE'}, " "subtype='NONE', " - "update=None)\n" + "update=None, " + "get=None, " + "set=None)\n" "\n" " Returns a new int property definition.\n" "\n" @@ -571,7 +1825,7 @@ static PyObject *BPy_IntProperty(PyObject *self, PyObject *args, PyObject *kw) if (srna) { static const char *kwlist[] = {"attr", "name", "description", "default", "min", "max", "soft_min", "soft_max", - "step", "options", "subtype", "update", NULL}; + "step", "options", "subtype", "update", "get", "set", NULL}; const char *id = NULL, *name = NULL, *description = ""; int id_len; int min = INT_MIN, max = INT_MAX, soft_min = INT_MIN, soft_max = INT_MAX, step = 1, def = 0; @@ -581,21 +1835,29 @@ static PyObject *BPy_IntProperty(PyObject *self, PyObject *args, PyObject *kw) char *pysubtype = NULL; int subtype = PROP_NONE; PyObject *update_cb = NULL; + PyObject *get_cb = NULL; + PyObject *set_cb = NULL; if (!PyArg_ParseTupleAndKeywords(args, kw, - "s#|ssiiiiiiO!sO:IntProperty", + "s#|ssiiiiiiO!sOOO:IntProperty", (char **)kwlist, &id, &id_len, &name, &description, &def, &min, &max, &soft_min, &soft_max, &step, &PySet_Type, &pyopts, &pysubtype, - &update_cb)) + &update_cb, &get_cb, &set_cb)) { return NULL; } BPY_PROPDEF_SUBTYPE_CHECK(IntProperty, property_flag_items, property_subtype_number_items); - if (bpy_prop_callback_check(update_cb, 2) == -1) { + if (bpy_prop_callback_check(update_cb, "update", 2) == -1) { + return NULL; + } + if (bpy_prop_callback_check(get_cb, "get", 1) == -1) { + return NULL; + } + if (bpy_prop_callback_check(set_cb, "set", 2) == -1) { return NULL; } @@ -611,7 +1873,8 @@ static PyObject *BPy_IntProperty(PyObject *self, PyObject *args, PyObject *kw) if (opts & PROP_SKIP_SAVE) RNA_def_property_flag(prop, PROP_SKIP_SAVE); if (opts & PROP_LIB_EXCEPTION) RNA_def_property_flag(prop, PROP_LIB_EXCEPTION); } - bpy_prop_callback_assign(prop, update_cb); + bpy_prop_callback_assign_update(prop, update_cb); + bpy_prop_callback_assign_int(prop, get_cb, set_cb); RNA_def_property_duplicate_pointers(srna, prop); } Py_RETURN_NONE; @@ -626,7 +1889,9 @@ PyDoc_STRVAR(BPy_IntVectorProperty_doc, "options={'ANIMATABLE'}, " "subtype='NONE', " "size=3, " - "update=None)\n" + "update=None, " + "get=None, " + "set=None)\n" "\n" " Returns a new vector int property definition.\n" "\n" @@ -653,7 +1918,7 @@ static PyObject *BPy_IntVectorProperty(PyObject *self, PyObject *args, PyObject if (srna) { static const char *kwlist[] = {"attr", "name", "description", "default", "min", "max", "soft_min", "soft_max", - "step", "options", "subtype", "size", "update", NULL}; + "step", "options", "subtype", "size", "update", "get", "set", NULL}; const char *id = NULL, *name = NULL, *description = ""; int id_len; int min = INT_MIN, max = INT_MAX, soft_min = INT_MIN, soft_max = INT_MAX, step = 1; @@ -666,15 +1931,17 @@ static PyObject *BPy_IntVectorProperty(PyObject *self, PyObject *args, PyObject char *pysubtype = NULL; int subtype = PROP_NONE; PyObject *update_cb = NULL; + PyObject *get_cb = NULL; + PyObject *set_cb = NULL; if (!PyArg_ParseTupleAndKeywords(args, kw, - "s#|ssOiiiiiO!siO:IntVectorProperty", + "s#|ssOiiiiiO!siOOO:IntVectorProperty", (char **)kwlist, &id, &id_len, &name, &description, &pydef, &min, &max, &soft_min, &soft_max, &step, &PySet_Type, &pyopts, &pysubtype, &size, - &update_cb)) + &update_cb, &get_cb, &set_cb)) { return NULL; } @@ -691,7 +1958,13 @@ static PyObject *BPy_IntVectorProperty(PyObject *self, PyObject *args, PyObject if (pydef && PyC_AsArray(def, pydef, size, &PyLong_Type, FALSE, "IntVectorProperty(default=sequence)") < 0) return NULL; - if (bpy_prop_callback_check(update_cb, 2) == -1) { + if (bpy_prop_callback_check(update_cb, "update", 2) == -1) { + return NULL; + } + if (bpy_prop_callback_check(get_cb, "get", 1) == -1) { + return NULL; + } + if (bpy_prop_callback_check(set_cb, "set", 2) == -1) { return NULL; } @@ -708,7 +1981,8 @@ static PyObject *BPy_IntVectorProperty(PyObject *self, PyObject *args, PyObject if (opts & PROP_SKIP_SAVE) RNA_def_property_flag(prop, PROP_SKIP_SAVE); if (opts & PROP_LIB_EXCEPTION) RNA_def_property_flag(prop, PROP_LIB_EXCEPTION); } - bpy_prop_callback_assign(prop, update_cb); + bpy_prop_callback_assign_update(prop, update_cb); + bpy_prop_callback_assign_int_array(prop, get_cb, set_cb); RNA_def_property_duplicate_pointers(srna, prop); } Py_RETURN_NONE; @@ -726,7 +2000,9 @@ PyDoc_STRVAR(BPy_FloatProperty_doc, "options={'ANIMATABLE'}, " "subtype='NONE', " "unit='NONE', " - "update=None)\n" + "update=None, " + "get=None, " + "set=None)\n" "\n" " Returns a new float property definition.\n" "\n" @@ -748,7 +2024,8 @@ static PyObject *BPy_FloatProperty(PyObject *self, PyObject *args, PyObject *kw) if (srna) { static const char *kwlist[] = {"attr", "name", "description", "default", "min", "max", "soft_min", "soft_max", - "step", "precision", "options", "subtype", "unit", "update", NULL}; + "step", "precision", "options", "subtype", + "unit", "update", "get", "set", NULL}; const char *id = NULL, *name = NULL, *description = ""; int id_len; float min = -FLT_MAX, max = FLT_MAX, soft_min = -FLT_MAX, soft_max = FLT_MAX, step = 3, def = 0.0f; @@ -761,15 +2038,17 @@ static PyObject *BPy_FloatProperty(PyObject *self, PyObject *args, PyObject *kw) char *pyunit = NULL; int unit = PROP_UNIT_NONE; PyObject *update_cb = NULL; + PyObject *get_cb = NULL; + PyObject *set_cb = NULL; if (!PyArg_ParseTupleAndKeywords(args, kw, - "s#|ssffffffiO!ssO:FloatProperty", + "s#|ssffffffiO!ssOOO:FloatProperty", (char **)kwlist, &id, &id_len, &name, &description, &def, &min, &max, &soft_min, &soft_max, &step, &precision, &PySet_Type, &pyopts, &pysubtype, &pyunit, - &update_cb)) + &update_cb, &get_cb, &set_cb)) { return NULL; } @@ -781,7 +2060,13 @@ static PyObject *BPy_FloatProperty(PyObject *self, PyObject *args, PyObject *kw) return NULL; } - if (bpy_prop_callback_check(update_cb, 2) == -1) { + if (bpy_prop_callback_check(update_cb, "update", 2) == -1) { + return NULL; + } + if (bpy_prop_callback_check(get_cb, "get", 1) == -1) { + return NULL; + } + if (bpy_prop_callback_check(set_cb, "set", 2) == -1) { return NULL; } @@ -797,7 +2082,8 @@ static PyObject *BPy_FloatProperty(PyObject *self, PyObject *args, PyObject *kw) if (opts & PROP_SKIP_SAVE) RNA_def_property_flag(prop, PROP_SKIP_SAVE); if (opts & PROP_LIB_EXCEPTION) RNA_def_property_flag(prop, PROP_LIB_EXCEPTION); } - bpy_prop_callback_assign(prop, update_cb); + bpy_prop_callback_assign_update(prop, update_cb); + bpy_prop_callback_assign_float(prop, get_cb, set_cb); RNA_def_property_duplicate_pointers(srna, prop); } Py_RETURN_NONE; @@ -814,7 +2100,9 @@ PyDoc_STRVAR(BPy_FloatVectorProperty_doc, "options={'ANIMATABLE'}, " "subtype='NONE', " "size=3, " - "update=None)\n" + "update=None, " + "get=None, " + "set=None)\n" "\n" " Returns a new vector float property definition.\n" "\n" @@ -842,7 +2130,8 @@ static PyObject *BPy_FloatVectorProperty(PyObject *self, PyObject *args, PyObjec if (srna) { static const char *kwlist[] = {"attr", "name", "description", "default", "min", "max", "soft_min", "soft_max", - "step", "precision", "options", "subtype", "unit", "size", "update", NULL}; + "step", "precision", "options", "subtype", + "unit", "size", "update", "get", "set", NULL}; const char *id = NULL, *name = NULL, *description = ""; int id_len; float min = -FLT_MAX, max = FLT_MAX, soft_min = -FLT_MAX, soft_max = FLT_MAX, step = 3; @@ -857,15 +2146,17 @@ static PyObject *BPy_FloatVectorProperty(PyObject *self, PyObject *args, PyObjec char *pyunit = NULL; int unit = PROP_UNIT_NONE; PyObject *update_cb = NULL; + PyObject *get_cb = NULL; + PyObject *set_cb = NULL; if (!PyArg_ParseTupleAndKeywords(args, kw, - "s#|ssOfffffiO!ssiO:FloatVectorProperty", + "s#|ssOfffffiO!ssiOOO:FloatVectorProperty", (char **)kwlist, &id, &id_len, &name, &description, &pydef, &min, &max, &soft_min, &soft_max, &step, &precision, &PySet_Type, &pyopts, &pysubtype, &pyunit, &size, - &update_cb)) + &update_cb, &get_cb, &set_cb)) { return NULL; } @@ -887,7 +2178,13 @@ static PyObject *BPy_FloatVectorProperty(PyObject *self, PyObject *args, PyObjec if (pydef && PyC_AsArray(def, pydef, size, &PyFloat_Type, FALSE, "FloatVectorProperty(default=sequence)") < 0) return NULL; - if (bpy_prop_callback_check(update_cb, 2) == -1) { + if (bpy_prop_callback_check(update_cb, "update", 2) == -1) { + return NULL; + } + if (bpy_prop_callback_check(get_cb, "get", 1) == -1) { + return NULL; + } + if (bpy_prop_callback_check(set_cb, "set", 2) == -1) { return NULL; } @@ -904,7 +2201,8 @@ static PyObject *BPy_FloatVectorProperty(PyObject *self, PyObject *args, PyObjec if (opts & PROP_SKIP_SAVE) RNA_def_property_flag(prop, PROP_SKIP_SAVE); if (opts & PROP_LIB_EXCEPTION) RNA_def_property_flag(prop, PROP_LIB_EXCEPTION); } - bpy_prop_callback_assign(prop, update_cb); + bpy_prop_callback_assign_update(prop, update_cb); + bpy_prop_callback_assign_float_array(prop, get_cb, set_cb); RNA_def_property_duplicate_pointers(srna, prop); } Py_RETURN_NONE; @@ -917,7 +2215,9 @@ PyDoc_STRVAR(BPy_StringProperty_doc, "maxlen=0, " "options={'ANIMATABLE'}, " "subtype='NONE', " - "update=None)\n" + "update=None, " + "get=None, " + "set=None)\n" "\n" " Returns a new string property definition.\n" "\n" @@ -937,7 +2237,7 @@ static PyObject *BPy_StringProperty(PyObject *self, PyObject *args, PyObject *kw if (srna) { static const char *kwlist[] = {"attr", "name", "description", "default", - "maxlen", "options", "subtype", "update", NULL}; + "maxlen", "options", "subtype", "update", "get", "set", NULL}; const char *id = NULL, *name = NULL, *description = "", *def = ""; int id_len; int maxlen = 0; @@ -947,20 +2247,28 @@ static PyObject *BPy_StringProperty(PyObject *self, PyObject *args, PyObject *kw char *pysubtype = NULL; int subtype = PROP_NONE; PyObject *update_cb = NULL; + PyObject *get_cb = NULL; + PyObject *set_cb = NULL; if (!PyArg_ParseTupleAndKeywords(args, kw, - "s#|sssiO!sO:StringProperty", + "s#|sssiO!sOOO:StringProperty", (char **)kwlist, &id, &id_len, &name, &description, &def, &maxlen, &PySet_Type, &pyopts, &pysubtype, - &update_cb)) + &update_cb, &get_cb, &set_cb)) { return NULL; } BPY_PROPDEF_SUBTYPE_CHECK(StringProperty, property_flag_items, property_subtype_string_items); - if (bpy_prop_callback_check(update_cb, 2) == -1) { + if (bpy_prop_callback_check(update_cb, "update", 2) == -1) { + return NULL; + } + if (bpy_prop_callback_check(get_cb, "get", 1) == -1) { + return NULL; + } + if (bpy_prop_callback_check(set_cb, "set", 2) == -1) { return NULL; } @@ -975,246 +2283,22 @@ static PyObject *BPy_StringProperty(PyObject *self, PyObject *args, PyObject *kw if (opts & PROP_SKIP_SAVE) RNA_def_property_flag(prop, PROP_SKIP_SAVE); if (opts & PROP_LIB_EXCEPTION) RNA_def_property_flag(prop, PROP_LIB_EXCEPTION); } - bpy_prop_callback_assign(prop, update_cb); + bpy_prop_callback_assign_update(prop, update_cb); + bpy_prop_callback_assign_string(prop, get_cb, set_cb); RNA_def_property_duplicate_pointers(srna, prop); } Py_RETURN_NONE; } -#if 0 -/* copies orig to buf, then sets orig to buf, returns copy length */ -static size_t strswapbufcpy(char *buf, const char **orig) -{ - const char *src = *orig; - char *dst = buf; - size_t i = 0; - *orig = buf; - while ((*dst = *src)) { dst++; src++; i++; } - return i + 1; /* include '\0' */ -} -#endif - -static EnumPropertyItem *enum_items_from_py(PyObject *seq_fast, PyObject *def, int *defvalue, const short is_enum_flag) -{ - EnumPropertyItem *items; - PyObject *item; - const Py_ssize_t seq_len = PySequence_Fast_GET_SIZE(seq_fast); - Py_ssize_t totbuf = 0; - int i; - short def_used = 0; - const char *def_cmp = NULL; - - if (is_enum_flag) { - if (seq_len > RNA_ENUM_BITFLAG_SIZE) { - PyErr_SetString(PyExc_TypeError, - "EnumProperty(...): maximum " - STRINGIFY(RNA_ENUM_BITFLAG_SIZE) - " members for a ENUM_FLAG type property"); - return NULL; - } - if (def && !PySet_Check(def)) { - PyErr_Format(PyExc_TypeError, - "EnumProperty(...): default option must be a 'set' " - "type when ENUM_FLAG is enabled, not a '%.200s'", - Py_TYPE(def)->tp_name); - return NULL; - } - } - else { - if (def) { - def_cmp = _PyUnicode_AsString(def); - if (def_cmp == NULL) { - PyErr_Format(PyExc_TypeError, - "EnumProperty(...): default option must be a 'str' " - "type when ENUM_FLAG is disabled, not a '%.200s'", - Py_TYPE(def)->tp_name); - return NULL; - } - } - } - - /* blank value */ - *defvalue = 0; - - items = MEM_callocN(sizeof(EnumPropertyItem) * (seq_len + 1), "enum_items_from_py1"); - - for (i = 0; i < seq_len; i++) { - EnumPropertyItem tmp = {0, "", 0, "", ""}; - Py_ssize_t item_size; - Py_ssize_t id_str_size; - Py_ssize_t name_str_size; - Py_ssize_t desc_str_size; - - item = PySequence_Fast_GET_ITEM(seq_fast, i); - - if ((PyTuple_CheckExact(item)) && - (item_size = PyTuple_GET_SIZE(item)) && - (item_size == 3 || item_size == 4) && - (tmp.identifier = _PyUnicode_AsStringAndSize(PyTuple_GET_ITEM(item, 0), &id_str_size)) && - (tmp.name = _PyUnicode_AsStringAndSize(PyTuple_GET_ITEM(item, 1), &name_str_size)) && - (tmp.description = _PyUnicode_AsStringAndSize(PyTuple_GET_ITEM(item, 2), &desc_str_size)) && - /* TODO, number isn't ensured to be unique from the script author */ - (item_size < 4 || py_long_as_int(PyTuple_GET_ITEM(item, 3), &tmp.value) != -1)) - { - if (is_enum_flag) { - if (item_size < 4) { - tmp.value = 1 << i; - } - - if (def && PySet_Contains(def, PyTuple_GET_ITEM(item, 0))) { - *defvalue |= tmp.value; - def_used++; - } - } - else { - if (item_size < 4) { - tmp.value = i; - } - - if (def && def_used == 0 && strcmp(def_cmp, tmp.identifier) == 0) { - *defvalue = tmp.value; - def_used++; /* only ever 1 */ - } - } - - items[i] = tmp; - - /* calculate combine string length */ - totbuf += id_str_size + name_str_size + desc_str_size + 3; /* 3 is for '\0's */ - } - else { - MEM_freeN(items); - PyErr_SetString(PyExc_TypeError, - "EnumProperty(...): expected a tuple containing " - "(identifier, name, description) and optionally a " - "unique number"); - return NULL; - } - - } - - if (is_enum_flag) { - /* strict check that all set members were used */ - if (def && def_used != PySet_GET_SIZE(def)) { - MEM_freeN(items); - - PyErr_Format(PyExc_TypeError, - "EnumProperty(..., default={...}): set has %d unused member(s)", - PySet_GET_SIZE(def) - def_used); - return NULL; - } - } - else { - if (def && def_used == 0) { - MEM_freeN(items); - - PyErr_Format(PyExc_TypeError, - "EnumProperty(..., default=\'%s\'): not found in enum members", - def_cmp); - return NULL; - } - } - - /* disabled duplicating strings because the array can still be freed and - * the strings from it referenced, for now we can't support dynamically - * created strings from python. */ -#if 0 - /* this would all work perfectly _but_ the python strings may be freed - * immediately after use, so we need to duplicate them, ugh. - * annoying because it works most of the time without this. */ - { - EnumPropertyItem *items_dup = MEM_mallocN((sizeof(EnumPropertyItem) * (seq_len + 1)) + (sizeof(char) * totbuf), - "enum_items_from_py2"); - EnumPropertyItem *items_ptr = items_dup; - char *buf = ((char *)items_dup) + (sizeof(EnumPropertyItem) * (seq_len + 1)); - memcpy(items_dup, items, sizeof(EnumPropertyItem) * (seq_len + 1)); - for (i = 0; i < seq_len; i++, items_ptr++) { - buf += strswapbufcpy(buf, &items_ptr->identifier); - buf += strswapbufcpy(buf, &items_ptr->name); - buf += strswapbufcpy(buf, &items_ptr->description); - } - MEM_freeN(items); - items = items_dup; - } - /* end string duplication */ -#endif - - return items; -} - -static EnumPropertyItem *bpy_props_enum_itemf(struct bContext *C, PointerRNA *ptr, PropertyRNA *prop, int *free) -{ - PyGILState_STATE gilstate; - - PyObject *py_func = RNA_property_enum_py_data_get(prop); - PyObject *self = NULL; - PyObject *args; - PyObject *items; /* returned from the function call */ - - EnumPropertyItem *eitems = NULL; - int err = 0; - - bpy_context_set(C, &gilstate); - - args = PyTuple_New(2); - self = pyrna_struct_as_instance(ptr); - PyTuple_SET_ITEM(args, 0, self); - - /* now get the context */ - PyTuple_SET_ITEM(args, 1, (PyObject *)bpy_context_module); - Py_INCREF(bpy_context_module); - - items = PyObject_CallObject(py_func, args); - - Py_DECREF(args); - - if (items == NULL) { - err = -1; - } - else { - PyObject *items_fast; - int defvalue_dummy = 0; - - if (!(items_fast = PySequence_Fast(items, "EnumProperty(...): " - "return value from the callback was not a sequence"))) - { - err = -1; - } - else { - eitems = enum_items_from_py(items_fast, NULL, &defvalue_dummy, - (RNA_property_flag(prop) & PROP_ENUM_FLAG) != 0); - - Py_DECREF(items_fast); - - if (!eitems) { - err = -1; - } - } - - Py_DECREF(items); - } - - if (err != -1) { /* worked */ - *free = 1; - } - else { - printf_func_error(py_func); - - eitems = DummyRNA_NULL_items; - } - - - bpy_context_clear(C, &gilstate); - return eitems; -} - PyDoc_STRVAR(BPy_EnumProperty_doc, ".. function:: EnumProperty(items, " "name=\"\", " "description=\"\", " "default=\"\", " "options={'ANIMATABLE'}, " - "update=None)\n" + "update=None, " + "get=None, " + "set=None)\n" "\n" " Returns a new enumerator property definition.\n" "\n" @@ -1246,7 +2330,7 @@ static PyObject *BPy_EnumProperty(PyObject *self, PyObject *args, PyObject *kw) if (srna) { static const char *kwlist[] = {"attr", "items", "name", "description", "default", - "options", "update", NULL}; + "options", "update", "get", "set", NULL}; const char *id = NULL, *name = NULL, *description = ""; PyObject *def = NULL; int id_len; @@ -1258,20 +2342,28 @@ static PyObject *BPy_EnumProperty(PyObject *self, PyObject *args, PyObject *kw) int opts = 0; short is_itemf = FALSE; PyObject *update_cb = NULL; + PyObject *get_cb = NULL; + PyObject *set_cb = NULL; if (!PyArg_ParseTupleAndKeywords(args, kw, - "s#O|ssOO!O:EnumProperty", + "s#O|ssOO!OOO:EnumProperty", (char **)kwlist, &id, &id_len, &items, &name, &description, &def, &PySet_Type, &pyopts, - &update_cb)) + &update_cb, &get_cb, &set_cb)) { return NULL; } BPY_PROPDEF_CHECK(EnumProperty, property_flag_enum_items); - if (bpy_prop_callback_check(update_cb, 2) == -1) { + if (bpy_prop_callback_check(update_cb, "update", 2) == -1) { + return NULL; + } + if (bpy_prop_callback_check(get_cb, "get", 1) == -1) { + return NULL; + } + if (bpy_prop_callback_check(set_cb, "set", 2) == -1) { return NULL; } @@ -1314,22 +2406,14 @@ static PyObject *BPy_EnumProperty(PyObject *self, PyObject *args, PyObject *kw) if (opts & PROP_ENUM_FLAG) prop = RNA_def_enum_flag(srna, id, eitems, defvalue, name ? name : id, description); else prop = RNA_def_enum(srna, id, eitems, defvalue, name ? name : id, description); - if (is_itemf) { - RNA_def_enum_funcs(prop, bpy_props_enum_itemf); - RNA_def_enum_py_data(prop, (void *)items); - - /* watch out!, if a user is tricky they can probably crash blender - * if they manage to free the callback, take care! */ - /* Py_INCREF(items); */ - } - if (pyopts) { if (opts & PROP_HIDDEN) RNA_def_property_flag(prop, PROP_HIDDEN); if ((opts & PROP_ANIMATABLE) == 0) RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); if (opts & PROP_SKIP_SAVE) RNA_def_property_flag(prop, PROP_SKIP_SAVE); if (opts & PROP_LIB_EXCEPTION) RNA_def_property_flag(prop, PROP_LIB_EXCEPTION); } - bpy_prop_callback_assign(prop, update_cb); + bpy_prop_callback_assign_update(prop, update_cb); + bpy_prop_callback_assign_enum(prop, get_cb, set_cb, (is_itemf ? items : NULL)); RNA_def_property_duplicate_pointers(srna, prop); if (is_itemf == FALSE) { @@ -1424,7 +2508,7 @@ static PyObject *BPy_PointerProperty(PyObject *self, PyObject *args, PyObject *k if (!ptype) return NULL; - if (bpy_prop_callback_check(update_cb, 2) == -1) { + if (bpy_prop_callback_check(update_cb, "update", 2) == -1) { return NULL; } @@ -1435,7 +2519,7 @@ static PyObject *BPy_PointerProperty(PyObject *self, PyObject *args, PyObject *k if (opts & PROP_SKIP_SAVE) RNA_def_property_flag(prop, PROP_SKIP_SAVE); if (opts & PROP_LIB_EXCEPTION) RNA_def_property_flag(prop, PROP_LIB_EXCEPTION); } - bpy_prop_callback_assign(prop, update_cb); + bpy_prop_callback_assign_update(prop, update_cb); RNA_def_property_duplicate_pointers(srna, prop); } Py_RETURN_NONE; |