=IDProperties bugfix=
[blender.git] / source / blender / blenkernel / intern / idprop.c
1 /**
2  * $Id: idprop.c
3  *
4  * ***** BEGIN GPL/BL DUAL LICENSE BLOCK *****
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * as published by the Free Software Foundation; either version 2
9  * of the License, or (at your option) any later version. The Blender
10  * Foundation also sells licenses for use in proprietary software under
11  * the Blender License.  See http://www.blender.org/BL/ for information
12  * about this.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software Foundation,
21  * Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
22  *
23  * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
24  * All rights reserved.
25  *
26  * Contributor(s): Joseph Eagar
27  *
28  * ***** END GPL/BL DUAL LICENSE BLOCK *****
29  */
30  
31 #include "DNA_listBase.h"
32 #include "DNA_ID.h"
33
34 #include "BKE_idprop.h"
35 #include "BKE_global.h"
36 #include "BKE_library.h"
37 #include "BKE_utildefines.h"
38
39 #include "BLI_blenlib.h"
40
41 #include "MEM_guardedalloc.h"
42
43 #include <stdio.h>
44 #include <stdlib.h>
45 #include <string.h>
46
47 #define BSTR_EQ(a, b)   (*(a) == *(b) && !strcmp(a, b))
48
49 /* IDPropertyTemplate is a union in DNA_ID.h */
50 static char idp_size_table[] = {
51         0, /*strings don't have fixed sizes :)*/
52         sizeof(int),
53         sizeof(float),
54         sizeof(float)*3, /*Vector type*/
55         sizeof(float)*16, /*Matrix type, we allocate max 4x4 even if in 3x3 mode*/
56         0, /*arrays don't have a fixed size either :)*/
57         sizeof(ListBase), /*Group type*/
58         sizeof(void*)
59 };
60
61
62 /* ----------- Array Type ----------- */
63
64 /*this function works for strings too!*/
65 void IDP_ResizeArray(IDProperty *prop, int newlen)
66 {
67         void *newarr;
68         int newsize=newlen;
69
70         /*first check if the array buffer size has room*/
71         /*if newlen is 200 chars less then totallen, reallocate anyway*/
72         if (newlen <= prop->totallen && prop->totallen - newlen < 200) {
73                 prop->len = newlen;
74                 return;
75         }
76
77         /* - Note: This code comes from python, here's the corrusponding comment. - */
78         /* This over-allocates proportional to the list size, making room
79          * for additional growth.  The over-allocation is mild, but is
80          * enough to give linear-time amortized behavior over a long
81          * sequence of appends() in the presence of a poorly-performing
82          * system realloc().
83          * The growth pattern is:  0, 4, 8, 16, 25, 35, 46, 58, 72, 88, ...
84          */
85         newsize = (newsize >> 3) + (newsize < 9 ? 3 : 6) + newsize;
86
87         newarr = MEM_callocN(idp_size_table[prop->type]*newsize, "idproperty array resized");
88         /*newlen is bigger*/
89         if (newlen >= prop->len) memcpy(newarr, prop->data.pointer, prop->len*idp_size_table[prop->type]);
90         /*newlen is smaller*/
91         else memcpy(newarr, prop->data.pointer, newlen*prop->len*idp_size_table[prop->type]);
92
93         MEM_freeN(prop->data.pointer);
94         prop->data.pointer = newarr;
95         prop->len = newlen;
96         prop->totallen = newsize;
97 }
98
99  void IDP_FreeArray(IDProperty *prop)
100 {
101         if (prop->data.pointer)
102                 MEM_freeN(prop->data.pointer);
103 }
104
105 /*taken from readfile.c*/
106 #define SWITCH_LONGINT(a) { \
107     char s_i, *p_i; \
108     p_i= (char *)&(a);  \
109     s_i=p_i[0]; p_i[0]=p_i[7]; p_i[7]=s_i; \
110     s_i=p_i[1]; p_i[1]=p_i[6]; p_i[6]=s_i; \
111     s_i=p_i[2]; p_i[2]=p_i[5]; p_i[5]=s_i; \
112     s_i=p_i[3]; p_i[3]=p_i[4]; p_i[4]=s_i; }
113
114
115
116 /* ---------- String Type ------------ */
117 void IDP_AssignString(IDProperty *prop, char *st)
118 {
119         int stlen;
120
121         stlen = strlen(st);
122
123         IDP_ResizeArray(prop, stlen+1); /*make room for null byte :) */
124         strcpy(prop->data.pointer, st);
125 }
126
127 void IDP_ConcatStringC(IDProperty *prop, char *st)
128 {
129         int newlen;
130
131         newlen = prop->len + strlen(st);
132         /*we have to remember that prop->len includes the null byte for strings.
133          so there's no need to add +1 to the resize function.*/
134         IDP_ResizeArray(prop, newlen);
135         strcat(prop->data.pointer, st);
136 }
137
138 void IDP_ConcatString(IDProperty *str1, IDProperty *append)
139 {
140         int newlen;
141
142         /*since ->len for strings includes the NULL byte, we have to subtract one or
143          we'll get an extra null byte after each concatination operation.*/
144         newlen = str1->len + append->len - 1;
145         IDP_ResizeArray(str1, newlen);
146         strcat(str1->data.pointer, append->data.pointer);
147 }
148
149 void IDP_FreeString(IDProperty *prop)
150 {
151         MEM_freeN(prop->data.pointer);
152 }
153
154
155 /*-------- ID Type -------*/
156
157 void IDP_LinkID(IDProperty *prop, ID *id)
158 {
159         if (prop->data.pointer) ((ID*)prop->data.pointer)->us--;
160         prop->data.pointer = id;
161         id_us_plus(id);
162 }
163
164 void IDP_UnlinkID(IDProperty *prop)
165 {
166         ((ID*)prop->data.pointer)->us--;
167 }
168
169 /*-------- Group Functions -------*/
170 /*returns 0 if an id property with the same name exists and it failed,
171   or 1 if it succeeded in adding to the group.*/
172 int IDP_AddToGroup(IDProperty *group, IDProperty *prop)
173 {
174         IDProperty *loop;
175         for (loop=group->data.group.first; loop; loop=loop->next) {
176                 if (BSTR_EQ(loop->name, prop->name)) return 0;
177         }
178
179         group->len++;
180         BLI_addtail(&group->data.group, prop);
181
182         return 1;
183 }
184
185 void IDP_RemFromGroup(IDProperty *group, IDProperty *prop)
186 {
187         group->len--;
188         BLI_remlink(&group->data.group, prop);
189 }
190
191 IDProperty *IDP_GetPropertyFromGroup(IDProperty *prop, char *name)
192 {
193         IDProperty *loop;
194         for (loop=prop->data.group.first; loop; loop=loop->next) {
195                 if (strcmp(loop->name, name)==0) return loop;
196         }
197         return NULL;
198 }
199
200 typedef struct IDPIter {
201         void *next;
202         IDProperty *parent;
203 } IDPIter;
204
205 void *IDP_GetGroupIterator(IDProperty *prop)
206 {
207         IDPIter *iter = MEM_callocN(sizeof(IDPIter), "IDPIter");
208         iter->next = prop->data.group.first;
209         iter->parent = prop;
210         return (void*) iter;
211 }
212
213 void *IDP_GroupIterNext(void *vself)
214 {
215         IDPIter *self = (IDPIter*) vself;
216         Link *next = (Link*) self->next;
217         if (self->next == NULL) {
218                 MEM_freeN(self);
219                 return NULL;
220         }
221
222         self->next = next->next;
223         return (void*) next;
224 }
225
226 void IDP_FreeIterBeforeEnd(void *vself)
227 {
228         MEM_freeN(vself);
229 }
230
231 /*Ok, the way things work, Groups free the ID Property structs of their children.
232   This is because all ID Property freeing functions free only direct data (not the ID Property
233   struct itself), but for Groups the child properties *are* considered
234   direct data.*/
235 void IDP_FreeGroup(IDProperty *prop)
236 {
237         IDProperty *loop, *next;
238         for (loop=prop->data.group.first; loop; loop=next)
239         {
240                 next = loop->next;
241                 IDP_FreeProperty(loop);
242                 MEM_freeN(loop);
243         }
244 }
245
246
247 /*-------- Main Functions --------*/
248
249 IDProperty *IDP_GetProperties(ID *id, int create_if_needed)
250 {
251         if (id->properties) return id->properties;
252         else {
253                 if (create_if_needed) {
254                         id->properties = MEM_callocN(sizeof(IDProperty), "IDProperty");
255                         id->properties->type = IDP_GROUP;
256                 }
257                 return id->properties;
258         }
259 }
260
261 IDProperty *IDP_New(int type, IDPropertyTemplate val, char *name)
262 {
263         IDProperty *prop=NULL;
264
265         switch (type) {
266                 case IDP_INT:
267                         prop = MEM_callocN(sizeof(IDProperty), "IDProperty int");
268                         prop->data.val = val.i;
269                         break;
270                 case IDP_FLOAT:
271                         prop = MEM_callocN(sizeof(IDProperty), "IDProperty float");
272                         *(float*)&prop->data.val = val.f;
273                         break;
274                 case IDP_ARRAY:
275                 {
276                         /*for now, we only support float and int arrays*/
277                         if (val.array.type == IDP_FLOAT || val.array.type == IDP_INT) {
278                                 prop = MEM_callocN(sizeof(IDProperty), "IDProperty array");
279                                 prop->len = prop->totallen = val.array.len;
280                                 prop->subtype = val.array.type;
281                                 prop->data.pointer = MEM_callocN(idp_size_table[val.array.type]*val.array.len, "id property array");
282                                 break;
283                         } else {
284                                 return NULL;
285                         }
286                 }
287                 case IDP_STRING:
288                 {
289                         char *st = val.str;
290                         int stlen;
291
292                         prop = MEM_callocN(sizeof(IDProperty), "IDProperty string");
293                         if (st == NULL) {
294                                 prop->data.pointer = MEM_callocN(DEFAULT_ALLOC_FOR_NULL_STRINGS, "id property string 1");
295                                 prop->totallen = DEFAULT_ALLOC_FOR_NULL_STRINGS;
296                                 prop->len = 1; /*NULL string, has len of 1 to account for null byte.*/
297                         } else {
298                                 stlen = strlen(st) + 1;
299                                 prop->data.pointer = MEM_callocN(stlen, "id property string 2");
300                                 prop->len = prop->totallen = stlen;
301                                 strcpy(prop->data.pointer, st);
302                         }
303                         break;
304                 }
305                 case IDP_GROUP:
306                 {
307                         prop = MEM_callocN(sizeof(IDProperty), "IDProperty group");
308                         /* heh I think all needed values are set properly by calloc anyway :) */
309                         break;
310                 }
311                 case IDP_MATRIX:
312                         prop = MEM_callocN(sizeof(IDProperty), "IDProperty array");
313                         if (val.matrix_or_vector.matvec_size == IDP_MATRIX4X4)
314                                 prop->data.pointer = MEM_callocN(sizeof(float)*4*4, "matrix 4x4 idproperty");
315                         else
316                                 prop->data.pointer = MEM_callocN(sizeof(float)*3*3, "matrix 3x3 idproperty");
317                 case IDP_VECTOR:
318                         prop = MEM_callocN(sizeof(IDProperty), "IDProperty array");
319                         switch (val.matrix_or_vector.matvec_size) {
320                                 case IDP_VECTOR4D:
321                                         prop->data.pointer = MEM_callocN(sizeof(float)*4, "vector 4d idproperty");
322                                         break;
323                                 case IDP_VECTOR3D:
324                                         prop->data.pointer = MEM_callocN(sizeof(float)*3, "vector 3d idproperty");
325                                         break;
326                                 case IDP_VECTOR2D:
327                                         prop->data.pointer = MEM_callocN(sizeof(float)*2, "vector 2d idproperty");
328                                         break;
329
330                         }
331                 default:
332                 {
333                         prop = MEM_callocN(sizeof(IDProperty), "IDProperty array");
334                         break;
335                 }
336         }
337
338         prop->type = type;
339         strncpy(prop->name, name, MAX_IDPROP_NAME);
340         return prop;
341 }
342
343 /*NOTE: this will free all child properties of list arrays and groups!
344   Also, note that this does NOT unlink anything!  Plus it doesn't free
345   the actual IDProperty struct either.*/
346 void IDP_FreeProperty(IDProperty *prop)
347 {
348         switch (prop->type) {
349                 case IDP_ARRAY:
350                         IDP_FreeArray(prop);
351                         break;
352                 case IDP_STRING:
353                         IDP_FreeString(prop);
354                         break;
355                 case IDP_GROUP:
356                         IDP_FreeGroup(prop);
357                         break;
358                 case IDP_VECTOR:
359                 case IDP_MATRIX:
360                         MEM_freeN(prop->data.pointer);
361                         break;
362         }
363 }
364
365 /*Unlinks any IDProperty<->ID linkage that might be going on.*/
366 void IDP_UnlinkProperty(IDProperty *prop)
367 {
368         switch (prop->type) {
369                 case IDP_ID:
370                         IDP_UnlinkID(prop);
371         }
372 }