Merge branch 'master' into blender2.8
authorCampbell Barton <ideasman42@gmail.com>
Wed, 23 Aug 2017 09:21:52 +0000 (19:21 +1000)
committerCampbell Barton <ideasman42@gmail.com>
Wed, 23 Aug 2017 09:21:52 +0000 (19:21 +1000)
source/blender/blenlib/BLI_string_utils.h
source/blender/blenlib/intern/string_utils.c
source/blender/makesrna/intern/rna_wm.c
source/blender/python/generic/py_capi_utils.h
source/blender/windowmanager/WM_api.h
source/blender/windowmanager/intern/wm_operators.c

index bb19ed574bb81b21188eaaba913818fade4015d5..a9fec5a1297b99c4c971fb90b5dd334924d3c633 100644 (file)
@@ -39,6 +39,7 @@ extern "C" {
 #endif
 
 #include "BLI_compiler_attrs.h"
+#include "BLI_utildefines.h"  /* only for _VA_NARGS_COUNT */
 
 struct ListBase;
 
@@ -49,6 +50,24 @@ size_t BLI_split_name_num(char *left, int *nr, const char *name, const char deli
 void BLI_string_split_suffix(const char *string, char *r_body, char *r_suf, const size_t str_len);
 void BLI_string_split_prefix(const char *string, char *r_pre, char *r_body, const size_t str_len);
 
+/* Join strings, return newly allocated string. */
+char *BLI_string_join_arrayN(
+        const char *strings[], uint strings_len) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
+char *BLI_string_join_array_by_sep_charN(
+        char sep, const char *strings[], uint strings_len) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
+char *BLI_string_join_array_by_sep_char_with_tableN(
+        char sep, char *table[], const char *strings[], uint strings_len) ATTR_NONNULL();
+/* Take multiple arguments, pass as (array, length). */
+#define BLI_string_joinN(...) \
+       BLI_string_join_arrayN( \
+       ((const char *[]){__VA_ARGS__}), _VA_NARGS_COUNT(__VA_ARGS__))
+#define BLI_string_join_by_sep_charN(sep, ...) \
+       BLI_string_join_array_by_sep_charN( \
+       sep, ((const char *[]){__VA_ARGS__}), _VA_NARGS_COUNT(__VA_ARGS__))
+#define BLI_string_join_by_sep_char_with_tableN(sep, table, ...) \
+       BLI_string_join_array_by_sep_char_with_tableN( \
+       sep, table, ((const char *[]){__VA_ARGS__}), _VA_NARGS_COUNT(__VA_ARGS__))
+
 void BLI_string_flip_side_name(char *r_name, const char *from_name, const bool strip_number, const size_t name_len);
 
 bool BLI_uniquename_cb(
index 8d91a55a5ad05115a6f6b5dc2a9848132caed910..a693463a3025366aca321e8ca13c77e42a3e6588 100644 (file)
@@ -383,3 +383,91 @@ bool BLI_uniquename(ListBase *list, void *vlink, const char *defname, char delim
 
        return BLI_uniquename_cb(uniquename_unique_check, &data, defname, delim, GIVE_STRADDR(vlink, name_offs), name_len);
 }
+
+/* ------------------------------------------------------------------------- */
+/** \name Join Strings
+ *
+ * For non array versions of these functions, use the macros:
+ * - #BLI_string_joinN
+ * - #BLI_string_join_by_sep_charN
+ * - #BLI_string_join_by_sep_char_with_tableN
+ *
+ * \{ */
+
+/**
+ * Join an array of strings into a newly allocated, null terminated string.
+ */
+char *BLI_string_join_arrayN(
+        const char *strings[], uint strings_len)
+{
+       uint total_len = 1;
+       for (uint i = 0; i < strings_len; i++) {
+               total_len += strlen(strings[i]);
+       }
+       char *result = MEM_mallocN(sizeof(char) * total_len, __func__); 
+       char *c = result;
+       for (uint i = 0; i < strings_len; i++) {
+               c += BLI_strcpy_rlen(c, strings[i]);
+       }
+       return result;
+}
+
+/**
+ * A version of #BLI_string_joinN that takes a separator which can be any character including '\0'.
+ */
+char *BLI_string_join_array_by_sep_charN(
+        char sep, const char *strings[], uint strings_len)
+{
+       uint total_len = 0;
+       for (uint i = 0; i < strings_len; i++) {
+               total_len += strlen(strings[i]) + 1;
+       }
+       if (total_len == 0) {
+               total_len = 1;
+       }
+
+       char *result = MEM_mallocN(sizeof(char) * total_len, __func__); 
+       char *c = result;
+       if (strings_len != 0) {
+               for (uint i = 0; i < strings_len; i++) {
+                       c += BLI_strcpy_rlen(c, strings[i]);
+                       *c = sep;
+                       c++;
+               }
+               c--;
+       }
+       *c = '\0';
+       return result;
+}
+
+/**
+ * A version of #BLI_string_join_array_by_sep_charN that takes a table array.
+ * The new location of each string is written into this array.
+ */
+char *BLI_string_join_array_by_sep_char_with_tableN(
+        char sep, char *table[], const char *strings[], uint strings_len)
+{
+       uint total_len = 0;
+       for (uint i = 0; i < strings_len; i++) {
+               total_len += strlen(strings[i]) + 1;
+       }
+       if (total_len == 0) {
+               total_len = 1;
+       }
+
+       char *result = MEM_mallocN(sizeof(char) * total_len, __func__); 
+       char *c = result;
+       if (strings_len != 0) {
+               for (uint i = 0; i < strings_len; i++) {
+                       table[i] = c;  /* <-- only difference to BLI_string_join_array_by_sep_charN. */
+                       c += BLI_strcpy_rlen(c, strings[i]);
+                       *c = sep;
+                       c++;
+               }
+               c--;
+       }
+       *c = '\0';
+       return result;
+}
+
+/** \} */
index 637ba1d35e59e5e5b80fed22d85bafd44f1d95a8..b0b44604751d737ba9b761f930cdd1412f574a01 100644 (file)
@@ -32,6 +32,7 @@
 #include "DNA_windowmanager_types.h"
 
 #include "BLI_utildefines.h"
+#include "BLI_string_utils.h"
 
 #include "BLT_translation.h"
 
@@ -1216,8 +1217,8 @@ static StructRNA *rna_Operator_register(
        struct {
                char idname[OP_MAX_TYPENAME];
                char name[OP_MAX_TYPENAME];
-               char descr[RNA_DYN_DESCR_MAX];
-               char ctxt[RNA_DYN_DESCR_MAX];
+               char description[RNA_DYN_DESCR_MAX];
+               char translation_context[RNA_DYN_DESCR_MAX];
                char undo_group[OP_MAX_TYPENAME];
        } temp_buffers;
 
@@ -1225,15 +1226,15 @@ static StructRNA *rna_Operator_register(
        dummyop.type = &dummyot;
        dummyot.idname = temp_buffers.idname; /* only assigne the pointer, string is NULL'd */
        dummyot.name = temp_buffers.name; /* only assigne the pointer, string is NULL'd */
-       dummyot.description = temp_buffers.descr; /* only assigne the pointer, string is NULL'd */
-       dummyot.translation_context = temp_buffers.ctxt; /* only assigne the pointer, string is NULL'd */
+       dummyot.description = temp_buffers.description; /* only assigne the pointer, string is NULL'd */
+       dummyot.translation_context = temp_buffers.translation_context; /* only assigne the pointer, string is NULL'd */
        dummyot.undo_group = temp_buffers.undo_group; /* only assigne the pointer, string is NULL'd */
        RNA_pointer_create(NULL, &RNA_Operator, &dummyop, &dummyotr);
 
        /* clear in case they are left unset */
-       temp_buffers.idname[0] = temp_buffers.name[0] = temp_buffers.descr[0] = temp_buffers.undo_group[0] = '\0';
+       temp_buffers.idname[0] = temp_buffers.name[0] = temp_buffers.description[0] = temp_buffers.undo_group[0] = '\0';
        /* We have to set default op context! */
-       strcpy(temp_buffers.ctxt, BLT_I18NCONTEXT_OPERATOR_DEFAULT);
+       strcpy(temp_buffers.translation_context, BLT_I18NCONTEXT_OPERATOR_DEFAULT);
 
        /* validate the python class */
        if (validate(&dummyotr, data, have_function) != 0)
@@ -1248,72 +1249,31 @@ static StructRNA *rna_Operator_register(
        if (!RNA_struct_available_or_report(reports, identifier)) {
                return NULL;
        }
+       if (!WM_operator_py_idname_ok_or_report(reports, identifier, temp_buffers.idname)) {
+               return NULL;
+       }
 
-       {   /* convert foo.bar to FOO_OT_bar
-                * allocate the description and the idname in 1 go */
-
-               /* inconveniently long name sanity check */
-               {
-                       char *ch = temp_buffers.idname;
-                       int i;
-                       int dot = 0;
-                       for (i = 0; *ch; i++) {
-                               if ((*ch >= 'a' && *ch <= 'z') || (*ch >= '0' && *ch <= '9') || *ch == '_') {
-                                       /* pass */
-                               }
-                               else if (*ch == '.') {
-                                       dot++;
-                               }
-                               else {
-                                       BKE_reportf(reports, RPT_ERROR,
-                                                   "Registering operator class: '%s', invalid bl_idname '%s', at position %d",
-                                                   identifier, temp_buffers.idname, i);
-                                       return NULL;
-                               }
-
-                               ch++;
-                       }
-
-                       if (i > ((int)sizeof(dummyop.idname)) - 3) {
-                               BKE_reportf(reports, RPT_ERROR, "Registering operator class: '%s', invalid bl_idname '%s', "
-                                           "is too long, maximum length is %d", identifier, temp_buffers.idname,
-                                           (int)sizeof(dummyop.idname) - 3);
-                               return NULL;
-                       }
-
-                       if (dot != 1) {
-                               BKE_reportf(reports, RPT_ERROR,
-                                           "Registering operator class: '%s', invalid bl_idname '%s', must contain 1 '.' character",
-                                           identifier, temp_buffers.idname);
-                               return NULL;
-                       }
-               }
-               /* end sanity check */
-
-               {
-                       const uint idname_len = strlen(temp_buffers.idname) + 4;
-                       const uint name_len = strlen(temp_buffers.name) + 1;
-                       const uint desc_len = strlen(temp_buffers.descr) + 1;
-                       const uint ctxt_len = strlen(temp_buffers.ctxt) + 1;
-                       const uint undo_group_len = strlen(temp_buffers.undo_group) + 1;
-                       /* 2 terminators and 3 to convert a.b -> A_OT_b */
-                       char *ch = MEM_mallocN(
-                               sizeof(char) * (idname_len + name_len + desc_len + ctxt_len + undo_group_len), __func__);
-                       WM_operator_bl_idname(ch, temp_buffers.idname); /* convert the idname from python */
-                       dummyot.idname = ch;
-                       ch += idname_len;
-                       memcpy(ch, temp_buffers.name, name_len);
-                       dummyot.name = ch;
-                       ch += name_len;
-                       memcpy(ch, temp_buffers.descr, desc_len);
-                       dummyot.description = ch;
-                       ch += desc_len;
-                       memcpy(ch, temp_buffers.ctxt, ctxt_len);
-                       dummyot.translation_context = ch;
-                       ch += ctxt_len;
-                       memcpy(ch, temp_buffers.undo_group, undo_group_len);
-                       dummyot.undo_group = ch;
-               }
+       /* Convert foo.bar to FOO_OT_bar
+        * allocate all strings at once. */
+       {
+               char idname_conv[sizeof(dummyop.idname)];
+               WM_operator_bl_idname(idname_conv, temp_buffers.idname); /* convert the idname from python */
+               const char *strings[] = {
+                       idname_conv,
+                       temp_buffers.name,
+                       temp_buffers.description,
+                       temp_buffers.translation_context,
+                       temp_buffers.undo_group,
+               };
+               char *strings_table[ARRAY_SIZE(strings)];
+               BLI_string_join_array_by_sep_char_with_tableN('\0', strings_table, strings, ARRAY_SIZE(strings));
+
+               dummyot.idname = strings_table[0];  /* allocated string stored here */
+               dummyot.name = strings_table[1];
+               dummyot.description = strings_table[2];
+               dummyot.translation_context = strings_table[3];
+               dummyot.undo_group = strings_table[4];
+               BLI_assert(ARRAY_SIZE(strings) == 5);
        }
 
        /* XXX, this doubles up with the operator name [#29666]
@@ -1389,8 +1349,8 @@ static StructRNA *rna_MacroOperator_register(
        struct {
                char idname[OP_MAX_TYPENAME];
                char name[OP_MAX_TYPENAME];
-               char descr[RNA_DYN_DESCR_MAX];
-               char ctxt[RNA_DYN_DESCR_MAX];
+               char description[RNA_DYN_DESCR_MAX];
+               char translation_context[RNA_DYN_DESCR_MAX];
                char undo_group[OP_MAX_TYPENAME];
        } temp_buffers;
 
@@ -1398,15 +1358,15 @@ static StructRNA *rna_MacroOperator_register(
        dummyop.type = &dummyot;
        dummyot.idname = temp_buffers.idname; /* only assigne the pointer, string is NULL'd */
        dummyot.name = temp_buffers.name; /* only assigne the pointer, string is NULL'd */
-       dummyot.description = temp_buffers.descr; /* only assigne the pointer, string is NULL'd */
-       dummyot.translation_context = temp_buffers.ctxt; /* only assigne the pointer, string is NULL'd */
+       dummyot.description = temp_buffers.description; /* only assigne the pointer, string is NULL'd */
+       dummyot.translation_context = temp_buffers.translation_context; /* only assigne the pointer, string is NULL'd */
        dummyot.undo_group = temp_buffers.undo_group; /* only assigne the pointer, string is NULL'd */
        RNA_pointer_create(NULL, &RNA_Macro, &dummyop, &dummyotr);
 
        /* clear in case they are left unset */
-       temp_buffers.idname[0] = temp_buffers.name[0] = temp_buffers.descr[0] = temp_buffers.undo_group[0] = '\0';
+       temp_buffers.idname[0] = temp_buffers.name[0] = temp_buffers.description[0] = temp_buffers.undo_group[0] = '\0';
        /* We have to set default op context! */
-       strcpy(temp_buffers.ctxt, BLT_I18NCONTEXT_OPERATOR_DEFAULT);
+       strcpy(temp_buffers.translation_context, BLT_I18NCONTEXT_OPERATOR_DEFAULT);
 
        /* validate the python class */
        if (validate(&dummyotr, data, have_function) != 0)
@@ -1427,31 +1387,31 @@ static StructRNA *rna_MacroOperator_register(
        if (!RNA_struct_available_or_report(reports, identifier)) {
                return NULL;
        }
+       if (!WM_operator_py_idname_ok_or_report(reports, identifier, temp_buffers.idname)) {
+               return NULL;
+       }
 
-       {   /* convert foo.bar to FOO_OT_bar
-                * allocate the description and the idname in 1 go */
-               const uint idname_len = strlen(temp_buffers.idname) + 4;
-               const uint name_len = strlen(temp_buffers.name) + 1;
-               const uint desc_len = strlen(temp_buffers.descr) + 1;
-               const uint ctxt_len = strlen(temp_buffers.ctxt) + 1;
-               const uint undo_group_len = strlen(temp_buffers.undo_group) + 1;
-               /* 2 terminators and 3 to convert a.b -> A_OT_b */
-               char *ch = MEM_mallocN(
-                       sizeof(char) * (idname_len + name_len + desc_len + ctxt_len + undo_group_len), __func__);
-               WM_operator_bl_idname(ch, temp_buffers.idname); /* convert the idname from python */
-               dummyot.idname = ch;
-               ch += idname_len;
-               memcpy(ch, temp_buffers.name, name_len);
-               dummyot.name = ch;
-               ch += name_len;
-               memcpy(ch, temp_buffers.descr, desc_len);
-               dummyot.description = ch;
-               ch += desc_len;
-               memcpy(ch, temp_buffers.ctxt, ctxt_len);
-               dummyot.translation_context = ch;
-               ch += ctxt_len;
-               memcpy(ch, temp_buffers.undo_group, undo_group_len);
-               dummyot.undo_group = ch;
+       /* Convert foo.bar to FOO_OT_bar
+        * allocate all strings at once. */
+       {
+               char idname_conv[sizeof(dummyop.idname)];
+               WM_operator_bl_idname(idname_conv, temp_buffers.idname); /* convert the idname from python */
+               const char *strings[] = {
+                       idname_conv,
+                       temp_buffers.name,
+                       temp_buffers.description,
+                       temp_buffers.translation_context,
+                       temp_buffers.undo_group,
+               };
+               char *strings_table[ARRAY_SIZE(strings)];
+               BLI_string_join_array_by_sep_char_with_tableN('\0', strings_table, strings, ARRAY_SIZE(strings));
+
+               dummyot.idname = strings_table[0];  /* allocated string stored here */
+               dummyot.name = strings_table[1];
+               dummyot.description = strings_table[2];
+               dummyot.translation_context = strings_table[3];
+               dummyot.undo_group = strings_table[4];
+               BLI_assert(ARRAY_SIZE(strings) == 5);
        }
 
        /* XXX, this doubles up with the operator name [#29666]
index 9f500f4c76b2f8e34e391ca875955562562a96b0..95d3f1d3775fd1c340e0a98ba71bc8ecc87bb275 100644 (file)
@@ -28,6 +28,7 @@
 #define __PY_CAPI_UTILS_H__
 
 #include "BLI_sys_types.h"
+#include "BLI_utildefines.h"  /* only for _VA_NARGS_COUNT */
 
 void                   PyC_ObSpit(const char *name, PyObject *var);
 void                   PyC_LineSpit(void);
@@ -52,13 +53,13 @@ PyObject       *PyC_Tuple_PackArray_I32FromBool(const int *array, uint len);
 PyObject       *PyC_Tuple_PackArray_Bool(const bool *array, uint len);
 
 #define PyC_Tuple_Pack_F32(...) \
-       PyC_Tuple_PackArray_F32(((const float []){__VA_ARGS__}), (sizeof((const float []){__VA_ARGS__}) / sizeof(float)))
+       PyC_Tuple_PackArray_F32(((const float []){__VA_ARGS__}), _VA_NARGS_COUNT(__VA_ARGS__))
 #define PyC_Tuple_Pack_I32(...) \
-       PyC_Tuple_PackArray_I32(((const int []){__VA_ARGS__}), (sizeof((const int []){__VA_ARGS__}) / sizeof(int)))
+       PyC_Tuple_PackArray_I32(((const int []){__VA_ARGS__}), _VA_NARGS_COUNT(__VA_ARGS__))
 #define PyC_Tuple_Pack_I32FromBool(...) \
-       PyC_Tuple_PackArray_I32FromBool(((const int []){__VA_ARGS__}), (sizeof((const int []){__VA_ARGS__}) / sizeof(int)))
+       PyC_Tuple_PackArray_I32FromBool(((const int []){__VA_ARGS__}), _VA_NARGS_COUNT(__VA_ARGS__))
 #define PyC_Tuple_Pack_Bool(...) \
-       PyC_Tuple_PackArray_Bool(((const bool []){__VA_ARGS__}), (sizeof((const bool []){__VA_ARGS__}) / sizeof(bool)))
+       PyC_Tuple_PackArray_Bool(((const bool []){__VA_ARGS__}), _VA_NARGS_COUNT(__VA_ARGS__))
 
 void            PyC_Tuple_Fill(PyObject *tuple, PyObject *value);
 void            PyC_List_Fill(PyObject *list, PyObject *value);
index 6e5f11c30625d58d186d2fe24dbfba826919d7f7..762a6f8e8d2ccdfb86816f1c8a98a3e023e18be2 100644 (file)
@@ -379,6 +379,7 @@ bool         WM_operator_pystring_abbreviate(char *str, int str_len_max);
 char           *WM_prop_pystring_assign(struct bContext *C, struct PointerRNA *ptr, struct PropertyRNA *prop, int index);
 void           WM_operator_bl_idname(char *to, const char *from);
 void           WM_operator_py_idname(char *to, const char *from);
+bool        WM_operator_py_idname_ok_or_report(struct ReportList *reports, const char *classname, const char *idname);
 
 /* *************** uilist types ******************** */
 void                WM_uilisttype_init(void);
index 38d122aff5fd0148f759d5e2db0f5a5e95e6f0af..dfd03813ac362e99c3ab45cb30eba62f0e554649 100644 (file)
@@ -575,6 +575,46 @@ void WM_operator_bl_idname(char *to, const char *from)
                to[0] = 0;
 }
 
+/**
+ * Sanity check to ensure #WM_operator_bl_idname won't fail.
+ * \returns true when there are no problems with \a idname, otherwise report an error.
+ */
+bool WM_operator_py_idname_ok_or_report(ReportList *reports, const char *classname, const char *idname)
+{
+       const char *ch = idname;
+       int dot = 0;
+       int i;
+       for (i = 0; *ch; i++, ch++) {
+               if ((*ch >= 'a' && *ch <= 'z') || (*ch >= '0' && *ch <= '9') || *ch == '_') {
+                       /* pass */
+               }
+               else if (*ch == '.') {
+                       dot++;
+               }
+               else {
+                       BKE_reportf(reports, RPT_ERROR,
+                                   "Registering operator class: '%s', invalid bl_idname '%s', at position %d",
+                                   classname, idname, i);
+                       return false;
+               }
+       }
+
+       if (i > (MAX_NAME - 3)) {
+               BKE_reportf(reports, RPT_ERROR, "Registering operator class: '%s', invalid bl_idname '%s', "
+                           "is too long, maximum length is %d", classname, idname,
+                           MAX_NAME - 3);
+               return false;
+       }
+
+       if (dot != 1) {
+               BKE_reportf(reports, RPT_ERROR,
+                           "Registering operator class: '%s', invalid bl_idname '%s', must contain 1 '.' character",
+                           classname, idname);
+               return false;
+       }
+       return true;
+}
+
 /**
  * Print a string representation of the operator, with the args that it runs so python can run it again.
  *