=IDProperties Python update=
[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 int IDP_InsertToGroup(IDProperty *group, IDProperty *previous, IDProperty *pnew)
186 {
187         IDProperty *loop;
188         for (loop=group->data.group.first; loop; loop=loop->next) {
189                 if (BSTR_EQ(loop->name, pnew->name)) return 0;
190         }
191         
192         group->len++;
193         
194         BLI_insertlink(&group->data.group, previous, pnew);
195         return 1;
196 }
197
198 void IDP_RemFromGroup(IDProperty *group, IDProperty *prop)
199 {
200         group->len--;
201         BLI_remlink(&group->data.group, prop);
202 }
203
204 IDProperty *IDP_GetPropertyFromGroup(IDProperty *prop, char *name)
205 {
206         IDProperty *loop;
207         for (loop=prop->data.group.first; loop; loop=loop->next) {
208                 if (strcmp(loop->name, name)==0) return loop;
209         }
210         return NULL;
211 }
212
213 typedef struct IDPIter {
214         void *next;
215         IDProperty *parent;
216 } IDPIter;
217
218 void *IDP_GetGroupIterator(IDProperty *prop)
219 {
220         IDPIter *iter = MEM_callocN(sizeof(IDPIter), "IDPIter");
221         iter->next = prop->data.group.first;
222         iter->parent = prop;
223         return (void*) iter;
224 }
225
226 IDProperty *IDP_GroupIterNext(void *vself)
227 {
228         IDPIter *self = (IDPIter*) vself;
229         Link *next = (Link*) self->next;
230         if (self->next == NULL) {
231                 MEM_freeN(self);
232                 return NULL;
233         }
234
235         self->next = next->next;
236         return (void*) next;
237 }
238
239 void IDP_FreeIterBeforeEnd(void *vself)
240 {
241         MEM_freeN(vself);
242 }
243
244 /*Ok, the way things work, Groups free the ID Property structs of their children.
245   This is because all ID Property freeing functions free only direct data (not the ID Property
246   struct itself), but for Groups the child properties *are* considered
247   direct data.*/
248 void IDP_FreeGroup(IDProperty *prop)
249 {
250         IDProperty *loop, *next;
251         for (loop=prop->data.group.first; loop; loop=next)
252         {
253                 next = loop->next;
254                 IDP_FreeProperty(loop);
255                 MEM_freeN(loop);
256         }
257 }
258
259
260 /*-------- Main Functions --------*/
261
262 IDProperty *IDP_GetProperties(ID *id, int create_if_needed)
263 {
264         if (id->properties) return id->properties;
265         else {
266                 if (create_if_needed) {
267                         id->properties = MEM_callocN(sizeof(IDProperty), "IDProperty");
268                         id->properties->type = IDP_GROUP;
269                 }
270                 return id->properties;
271         }
272 }
273
274 IDProperty *IDP_New(int type, IDPropertyTemplate val, char *name)
275 {
276         IDProperty *prop=NULL;
277
278         switch (type) {
279                 case IDP_INT:
280                         prop = MEM_callocN(sizeof(IDProperty), "IDProperty int");
281                         prop->data.val = val.i;
282                         break;
283                 case IDP_FLOAT:
284                         prop = MEM_callocN(sizeof(IDProperty), "IDProperty float");
285                         *(float*)&prop->data.val = val.f;
286                         break;
287                 case IDP_ARRAY:
288                 {
289                         /*for now, we only support float and int arrays*/
290                         if (val.array.type == IDP_FLOAT || val.array.type == IDP_INT) {
291                                 prop = MEM_callocN(sizeof(IDProperty), "IDProperty array");
292                                 prop->len = prop->totallen = val.array.len;
293                                 prop->subtype = val.array.type;
294                                 prop->data.pointer = MEM_callocN(idp_size_table[val.array.type]*val.array.len, "id property array");
295                                 break;
296                         } else {
297                                 return NULL;
298                         }
299                 }
300                 case IDP_STRING:
301                 {
302                         char *st = val.str;
303                         int stlen;
304
305                         prop = MEM_callocN(sizeof(IDProperty), "IDProperty string");
306                         if (st == NULL) {
307                                 prop->data.pointer = MEM_callocN(DEFAULT_ALLOC_FOR_NULL_STRINGS, "id property string 1");
308                                 prop->totallen = DEFAULT_ALLOC_FOR_NULL_STRINGS;
309                                 prop->len = 1; /*NULL string, has len of 1 to account for null byte.*/
310                         } else {
311                                 stlen = strlen(st) + 1;
312                                 prop->data.pointer = MEM_callocN(stlen, "id property string 2");
313                                 prop->len = prop->totallen = stlen;
314                                 strcpy(prop->data.pointer, st);
315                         }
316                         break;
317                 }
318                 case IDP_GROUP:
319                 {
320                         prop = MEM_callocN(sizeof(IDProperty), "IDProperty group");
321                         /* heh I think all needed values are set properly by calloc anyway :) */
322                         break;
323                 }
324                 case IDP_MATRIX:
325                         prop = MEM_callocN(sizeof(IDProperty), "IDProperty array");
326                         if (val.matrix_or_vector.matvec_size == IDP_MATRIX4X4)
327                                 prop->data.pointer = MEM_callocN(sizeof(float)*4*4, "matrix 4x4 idproperty");
328                         else
329                                 prop->data.pointer = MEM_callocN(sizeof(float)*3*3, "matrix 3x3 idproperty");
330                 case IDP_VECTOR:
331                         prop = MEM_callocN(sizeof(IDProperty), "IDProperty array");
332                         switch (val.matrix_or_vector.matvec_size) {
333                                 case IDP_VECTOR4D:
334                                         prop->data.pointer = MEM_callocN(sizeof(float)*4, "vector 4d idproperty");
335                                         break;
336                                 case IDP_VECTOR3D:
337                                         prop->data.pointer = MEM_callocN(sizeof(float)*3, "vector 3d idproperty");
338                                         break;
339                                 case IDP_VECTOR2D:
340                                         prop->data.pointer = MEM_callocN(sizeof(float)*2, "vector 2d idproperty");
341                                         break;
342
343                         }
344                 default:
345                 {
346                         prop = MEM_callocN(sizeof(IDProperty), "IDProperty array");
347                         break;
348                 }
349         }
350
351         prop->type = type;
352         strncpy(prop->name, name, MAX_IDPROP_NAME);
353         return prop;
354 }
355
356 /*NOTE: this will free all child properties of list arrays and groups!
357   Also, note that this does NOT unlink anything!  Plus it doesn't free
358   the actual IDProperty struct either.*/
359 void IDP_FreeProperty(IDProperty *prop)
360 {
361         switch (prop->type) {
362                 case IDP_ARRAY:
363                         IDP_FreeArray(prop);
364                         break;
365                 case IDP_STRING:
366                         IDP_FreeString(prop);
367                         break;
368                 case IDP_GROUP:
369                         IDP_FreeGroup(prop);
370                         break;
371                 case IDP_VECTOR:
372                 case IDP_MATRIX:
373                         MEM_freeN(prop->data.pointer);
374                         break;
375         }
376 }
377
378 /*Unlinks any IDProperty<->ID linkage that might be going on.*/
379 void IDP_UnlinkProperty(IDProperty *prop)
380 {
381         switch (prop->type) {
382                 case IDP_ID:
383                         IDP_UnlinkID(prop);
384         }
385 }