Sequencer: refactor clipboard copy to no longer increase user count.
[blender.git] / source / blender / blenkernel / intern / idprop_utils.c
1 /*
2  * ***** BEGIN GPL LICENSE BLOCK *****
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software Foundation,
16  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17  *
18  * ***** END GPL LICENSE BLOCK *****
19  */
20
21 /** \file blender/blenkernel/intern/idprop_utils.c
22  *  \ingroup bke
23  */
24
25 #include <stdio.h>
26 #include <string.h>
27
28 #include "BLI_utildefines.h"
29 #include "BLI_string.h"
30 #include "BLI_dynstr.h"
31
32 #include "BKE_idprop.h"
33 #include "BKE_idcode.h"
34
35 #include "MEM_guardedalloc.h"
36
37 #include "BLI_strict_flags.h"
38
39
40 /* -------------------------------------------------------------------- */
41 /** \name IDProp Repr
42  *
43  * Convert an IDProperty to a string.
44  *
45  * Output should be a valid Python literal
46  * (with minor exceptions - float nan for eg).
47  * \{ */
48
49 struct ReprState {
50         void (*str_append_fn)(void *user_data, const char *str, uint str_len);
51         void *user_data;
52         /* Big enough to format any primitive type. */
53         char buf[128];
54 };
55
56 static void idp_str_append_escape(struct ReprState *state, const char *str, const uint str_len, bool quote)
57 {
58         if (quote) {
59                 state->str_append_fn(state->user_data, "\"", 1);
60         }
61         uint i_prev = 0, i = 0;
62         while (i < str_len) {
63                 const char c = str[i];
64                 if (c == '"') {
65                         if (i_prev != i) {
66                                 state->str_append_fn(state->user_data, str + i_prev, i - i_prev);
67                         }
68                         state->str_append_fn(state->user_data, "\\\"", 2);
69                         i_prev = i + 1;
70                 }
71                 else if (c == '\\') {
72                         if (i_prev != i) {
73                                 state->str_append_fn(state->user_data, str + i_prev, i - i_prev);
74                         }
75                         state->str_append_fn(state->user_data, "\\\\", 2);
76                         i_prev = i + 1;
77                 }
78                 else if (c < 32) {
79                         if (i_prev != i) {
80                                 state->str_append_fn(state->user_data, str + i_prev, i - i_prev);
81                         }
82                         char buf[5];
83                         uint len = (uint)BLI_snprintf_rlen(buf, sizeof(buf), "\\x%02x", c);
84                         BLI_assert(len == 4);
85                         state->str_append_fn(state->user_data, buf, len);
86                         i_prev = i + 1;
87                 }
88                 i++;
89         }
90         state->str_append_fn(state->user_data, str + i_prev, i - i_prev);
91         if (quote) {
92                 state->str_append_fn(state->user_data, "\"", 1);
93         }
94 }
95
96 static void idp_repr_fn_recursive(struct ReprState *state, const IDProperty *prop)
97 {
98         /* Note: 'strlen' will be calculated at compile time for literals. */
99 #define STR_APPEND_STR(str)     state->str_append_fn(state->user_data, str, (uint)strlen(str))
100
101 #define STR_APPEND_STR_QUOTE(str)               idp_str_append_escape(state, str, (uint)strlen(str), true)
102 #define STR_APPEND_STR_LEN_QUOTE(str, str_len)  idp_str_append_escape(state, str, str_len, true)
103
104 #define STR_APPEND_FMT(format, ...) \
105         state->str_append_fn( \
106                 state->user_data, \
107                 state->buf, \
108                 (uint)BLI_snprintf_rlen(state->buf, sizeof(state->buf), format, __VA_ARGS__))
109
110         switch (prop->type) {
111                 case IDP_STRING:
112                 {
113                         STR_APPEND_STR_LEN_QUOTE(IDP_String(prop), (uint)MAX2(0, prop->len - 1));
114                         break;
115                 }
116                 case IDP_INT:
117                 {
118                         STR_APPEND_FMT("%d", IDP_Int(prop));
119                         break;
120                 }
121                 case IDP_FLOAT:
122                 {
123                         STR_APPEND_FMT("%g", (double)IDP_Float(prop));
124                         break;
125                 }
126                 case IDP_DOUBLE:
127                 {
128                         STR_APPEND_FMT("%g", IDP_Double(prop));
129                         break;
130                 }
131                 case IDP_ARRAY:
132                 {
133                         STR_APPEND_STR("[");
134                         switch (prop->subtype) {
135                                 case IDP_INT:
136                                         for (const int *v = prop->data.pointer, *v_end = v + prop->len; v != v_end; v++) {
137                                                 if (v != prop->data.pointer) {
138                                                         STR_APPEND_STR(", ");
139                                                 }
140                                                 STR_APPEND_FMT("%d", *v);
141                                         }
142                                         break;
143                                 case IDP_FLOAT:
144                                         for (const float *v = prop->data.pointer, *v_end = v + prop->len; v != v_end; v++) {
145                                                 if (v != prop->data.pointer) {
146                                                         STR_APPEND_STR(", ");
147                                                 }
148                                                 STR_APPEND_FMT("%g", (double)*v);
149                                         }
150                                         break;
151                                 case IDP_DOUBLE:
152                                         for (const double *v = prop->data.pointer, *v_end = v + prop->len; v != v_end; v++) {
153                                                 if (v != prop->data.pointer) {
154                                                         STR_APPEND_STR(", ");
155                                                 }
156                                                 STR_APPEND_FMT("%g", *v);
157                                         }
158                                         break;
159                         }
160                         STR_APPEND_STR("]");
161                         break;
162                 }
163                 case IDP_IDPARRAY:
164                 {
165                         STR_APPEND_STR("[");
166                         for (const IDProperty *v = prop->data.pointer, *v_end = v + prop->len; v != v_end; v++) {
167                                 if (v != prop->data.pointer) {
168                                         STR_APPEND_STR(", ");
169                                 }
170                                 idp_repr_fn_recursive(state, v);
171                         }
172                         STR_APPEND_STR("]");
173                         break;
174                 }
175                 case IDP_GROUP:
176                 {
177                         STR_APPEND_STR("{");
178                         for (const IDProperty *subprop = prop->data.group.first; subprop; subprop = subprop->next) {
179                                 if (subprop != prop->data.group.first) {
180                                         STR_APPEND_STR(", ");
181                                 }
182                                 STR_APPEND_STR_QUOTE(subprop->name);
183                                 STR_APPEND_STR(": ");
184                                 idp_repr_fn_recursive(state, subprop);
185                         }
186                         STR_APPEND_STR("}");
187                         break;
188                 }
189                 case IDP_ID:
190                 {
191                         const ID *id = prop->data.pointer;
192                         if (id != NULL) {
193                                 STR_APPEND_STR("bpy.data.");
194                                 STR_APPEND_STR(BKE_idcode_to_name_plural(GS(id->name)));
195                                 STR_APPEND_STR("[");
196                                 STR_APPEND_STR_QUOTE(id->name + 2);
197                                 STR_APPEND_STR("]");
198                         }
199                         else {
200                                 STR_APPEND_STR("None");
201                         }
202                         break;
203                 }
204                 default:
205                 {
206                         BLI_assert(0);
207                         break;
208                 }
209         }
210
211 #undef STR_APPEND_STR
212 #undef STR_APPEND_STR_QUOTE
213 #undef STR_APPEND_STR_LEN_QUOTE
214 #undef STR_APPEND_FMT
215
216 }
217
218 void IDP_repr_fn(
219         const IDProperty *prop,
220         void (*str_append_fn)(void *user_data, const char *str, uint str_len),
221         void *user_data)
222 {
223         struct ReprState state = {
224                 .str_append_fn = str_append_fn,
225                 .user_data = user_data,
226         };
227         idp_repr_fn_recursive(&state, prop);
228 }
229
230 static void repr_str(void *user_data, const char *str, uint len)
231 {
232         BLI_dynstr_nappend(user_data, str, (int)len);
233 }
234
235 char *IDP_reprN(const IDProperty *prop, uint *r_len)
236 {
237         DynStr *ds = BLI_dynstr_new();
238         IDP_repr_fn(prop, repr_str, ds);
239         char *cstring = BLI_dynstr_get_cstring(ds);
240         if (r_len != NULL) {
241                 *r_len = (uint)BLI_dynstr_get_len(ds);
242         }
243         BLI_dynstr_free(ds);
244         return cstring;
245 }
246
247 void IDP_print(const IDProperty *prop)
248 {
249         char *repr = IDP_reprN(prop, NULL);
250         printf("IDProperty(%p): ", prop);
251         puts(repr);
252         MEM_freeN(repr);
253 }
254
255 /** \} */