=ID Property update=
[blender-staging.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
51 /*local size table.*/
52 static char idp_size_table[] = {
53         1, /*strings*/
54         sizeof(int),
55         sizeof(float),
56         sizeof(float)*3, /*Vector type, deprecated*/
57         sizeof(float)*16, /*Matrix type, deprecated*/
58         0, /*arrays don't have a fixed size*/
59         sizeof(ListBase), /*Group type*/
60         sizeof(void*)
61 };
62
63
64 /* ----------- Array Type ----------- */
65
66 /*this function works for strings too!*/
67 void IDP_ResizeArray(IDProperty *prop, int newlen)
68 {
69         void *newarr;
70         int newsize=newlen;
71
72         /*first check if the array buffer size has room*/
73         /*if newlen is 200 chars less then totallen, reallocate anyway*/
74         if (newlen <= prop->totallen && prop->totallen - newlen < 200) {
75                 prop->len = newlen;
76                 return;
77         }
78
79         /* - Note: This code comes from python, here's the corrusponding comment. - */
80         /* This over-allocates proportional to the list size, making room
81          * for additional growth.  The over-allocation is mild, but is
82          * enough to give linear-time amortized behavior over a long
83          * sequence of appends() in the presence of a poorly-performing
84          * system realloc().
85          * The growth pattern is:  0, 4, 8, 16, 25, 35, 46, 58, 72, 88, ...
86          */
87         newsize = (newsize >> 3) + (newsize < 9 ? 3 : 6) + newsize;
88
89         newarr = MEM_callocN(idp_size_table[prop->type]*newsize, "idproperty array resized");
90         /*newlen is bigger*/
91         if (newlen >= prop->len) memcpy(newarr, prop->data.pointer, prop->len*idp_size_table[prop->type]);
92         /*newlen is smaller*/
93         else memcpy(newarr, prop->data.pointer, newlen*prop->len*idp_size_table[prop->type]);
94
95         MEM_freeN(prop->data.pointer);
96         prop->data.pointer = newarr;
97         prop->len = newlen;
98         prop->totallen = newsize;
99 }
100
101  void IDP_FreeArray(IDProperty *prop)
102 {
103         if (prop->data.pointer)
104                 MEM_freeN(prop->data.pointer);
105 }
106
107
108  static IDProperty *idp_generic_copy(IDProperty *prop)
109  {
110         IDProperty *newp = MEM_callocN(sizeof(IDProperty), "IDProperty array dup");
111
112         strncpy(newp->name, prop->name, MAX_IDPROP_NAME);
113         newp->type = prop->type;
114         newp->flag = prop->flag;
115         newp->data.val = prop->data.val;
116
117         return newp;
118  }
119
120 IDProperty *IDP_CopyArray(IDProperty *prop)
121 {
122         IDProperty *newp = idp_generic_copy(prop);
123
124         if (prop->data.pointer) newp->data.pointer = MEM_dupallocN(prop->data.pointer);
125         newp->len = prop->len;
126         newp->subtype = prop->subtype;
127         newp->totallen = prop->totallen;
128
129         return newp;
130 }
131
132 /*taken from readfile.c*/
133 #define SWITCH_LONGINT(a) { \
134     char s_i, *p_i; \
135     p_i= (char *)&(a);  \
136     s_i=p_i[0]; p_i[0]=p_i[7]; p_i[7]=s_i; \
137     s_i=p_i[1]; p_i[1]=p_i[6]; p_i[6]=s_i; \
138     s_i=p_i[2]; p_i[2]=p_i[5]; p_i[5]=s_i; \
139     s_i=p_i[3]; p_i[3]=p_i[4]; p_i[4]=s_i; }
140
141
142
143 /* ---------- String Type ------------ */
144 IDProperty *IDP_CopyString(IDProperty *prop)
145 {
146         IDProperty *newp = idp_generic_copy(prop);
147
148         if (prop->data.pointer) newp->data.pointer = MEM_dupallocN(prop->data.pointer);
149         newp->len = prop->len;
150         newp->subtype = prop->subtype;
151         newp->totallen = prop->totallen;
152
153         return newp;
154 }
155
156
157 void IDP_AssignString(IDProperty *prop, char *st)
158 {
159         int stlen;
160
161         stlen = strlen(st);
162
163         IDP_ResizeArray(prop, stlen+1); /*make room for null byte :) */
164         strcpy(prop->data.pointer, st);
165 }
166
167 void IDP_ConcatStringC(IDProperty *prop, char *st)
168 {
169         int newlen;
170
171         newlen = prop->len + strlen(st);
172         /*we have to remember that prop->len includes the null byte for strings.
173          so there's no need to add +1 to the resize function.*/
174         IDP_ResizeArray(prop, newlen);
175         strcat(prop->data.pointer, st);
176 }
177
178 void IDP_ConcatString(IDProperty *str1, IDProperty *append)
179 {
180         int newlen;
181
182         /*since ->len for strings includes the NULL byte, we have to subtract one or
183          we'll get an extra null byte after each concatination operation.*/
184         newlen = str1->len + append->len - 1;
185         IDP_ResizeArray(str1, newlen);
186         strcat(str1->data.pointer, append->data.pointer);
187 }
188
189 void IDP_FreeString(IDProperty *prop)
190 {
191         MEM_freeN(prop->data.pointer);
192 }
193
194
195 /*-------- ID Type, not in use yet -------*/
196
197 void IDP_LinkID(IDProperty *prop, ID *id)
198 {
199         if (prop->data.pointer) ((ID*)prop->data.pointer)->us--;
200         prop->data.pointer = id;
201         id_us_plus(id);
202 }
203
204 void IDP_UnlinkID(IDProperty *prop)
205 {
206         ((ID*)prop->data.pointer)->us--;
207 }
208
209 /*-------- Group Functions -------*/
210
211 /*checks if a property with the same name as prop exists, and if so replaces it.*/
212 IDProperty *IDP_CopyGroup(IDProperty *prop)
213 {
214         IDProperty *newp = idp_generic_copy(prop), *link;
215
216         for (link=prop->data.group.first; link; link=link->next) {
217                 BLI_addtail(&newp->data.group, IDP_CopyProperty(link));
218         }
219
220         return newp;
221 }
222
223 void IDP_ReplaceInGroup(IDProperty *group, IDProperty *prop)
224 {
225         IDProperty *loop;
226         for (loop=group->data.group.first; loop; loop=loop->next) {
227                 if (BSTR_EQ(loop->name, prop->name)) {
228                         if (loop->next) BLI_insertlinkbefore(&group->data.group, loop->next, prop);
229                         else BLI_addtail(&group->data.group, prop);
230                         BLI_remlink(&group->data.group, loop);
231                         IDP_FreeProperty(loop);
232                         MEM_freeN(loop);
233                         group->len++;
234                         return;
235                 }
236         }
237
238         group->len++;
239         BLI_addtail(&group->data.group, prop);
240 }
241
242 /*returns 0 if an id property with the same name exists and it failed,
243   or 1 if it succeeded in adding to the group.*/
244 int IDP_AddToGroup(IDProperty *group, IDProperty *prop)
245 {
246         IDProperty *loop;
247         for (loop=group->data.group.first; loop; loop=loop->next) {
248                 if (BSTR_EQ(loop->name, prop->name)) return 0;
249         }
250
251         group->len++;
252         BLI_addtail(&group->data.group, prop);
253
254         return 1;
255 }
256
257 int IDP_InsertToGroup(IDProperty *group, IDProperty *previous, IDProperty *pnew)
258 {
259         IDProperty *loop;
260         for (loop=group->data.group.first; loop; loop=loop->next) {
261                 if (BSTR_EQ(loop->name, pnew->name)) return 0;
262         }
263         
264         group->len++;
265         
266         BLI_insertlink(&group->data.group, previous, pnew);
267         return 1;
268 }
269
270 void IDP_RemFromGroup(IDProperty *group, IDProperty *prop)
271 {
272         group->len--;
273         BLI_remlink(&group->data.group, prop);
274 }
275
276 IDProperty *IDP_GetPropertyFromGroup(IDProperty *prop, char *name)
277 {
278         IDProperty *loop;
279         for (loop=prop->data.group.first; loop; loop=loop->next) {
280                 if (strcmp(loop->name, name)==0) return loop;
281         }
282         return NULL;
283 }
284
285 typedef struct IDPIter {
286         void *next;
287         IDProperty *parent;
288 } IDPIter;
289
290 void *IDP_GetGroupIterator(IDProperty *prop)
291 {
292         IDPIter *iter = MEM_callocN(sizeof(IDPIter), "IDPIter");
293         iter->next = prop->data.group.first;
294         iter->parent = prop;
295         return (void*) iter;
296 }
297
298 IDProperty *IDP_GroupIterNext(void *vself)
299 {
300         IDPIter *self = (IDPIter*) vself;
301         Link *next = (Link*) self->next;
302         if (self->next == NULL) {
303                 MEM_freeN(self);
304                 return NULL;
305         }
306
307         self->next = next->next;
308         return (void*) next;
309 }
310
311 void IDP_FreeIterBeforeEnd(void *vself)
312 {
313         MEM_freeN(vself);
314 }
315
316 /*Ok, the way things work, Groups free the ID Property structs of their children.
317   This is because all ID Property freeing functions free only direct data (not the ID Property
318   struct itself), but for Groups the child properties *are* considered
319   direct data.*/
320 void IDP_FreeGroup(IDProperty *prop)
321 {
322         IDProperty *loop, *next;
323         for (loop=prop->data.group.first; loop; loop=next)
324         {
325                 next = loop->next;
326                 BLI_remlink(&prop->data.group, loop);
327                 IDP_FreeProperty(loop);
328                 MEM_freeN(loop);
329         }
330 }
331
332
333 /*-------- Main Functions --------*/
334 IDProperty *IDP_CopyProperty(IDProperty *prop)
335 {
336         switch (prop->type) {
337                 case IDP_GROUP: return IDP_CopyGroup(prop);
338                 case IDP_STRING: return IDP_CopyString(prop);
339                 case IDP_ARRAY: return IDP_CopyArray(prop);
340                 default: return idp_generic_copy(prop);
341         }
342 }
343
344 IDProperty *IDP_GetProperties(ID *id, int create_if_needed)
345 {
346         if (id->properties) return id->properties;
347         else {
348                 if (create_if_needed) {
349                         id->properties = MEM_callocN(sizeof(IDProperty), "IDProperty");
350                         id->properties->type = IDP_GROUP;
351                 }
352                 return id->properties;
353         }
354 }
355
356 IDProperty *IDP_New(int type, IDPropertyTemplate val, char *name)
357 {
358         IDProperty *prop=NULL;
359
360         switch (type) {
361                 case IDP_INT:
362                         prop = MEM_callocN(sizeof(IDProperty), "IDProperty int");
363                         prop->data.val = val.i;
364                         break;
365                 case IDP_FLOAT:
366                         prop = MEM_callocN(sizeof(IDProperty), "IDProperty float");
367                         *(float*)&prop->data.val = val.f;
368                         break;
369                 case IDP_ARRAY:
370                 {
371                         /*for now, we only support float and int arrays*/
372                         if (val.array.type == IDP_FLOAT || val.array.type == IDP_INT) {
373                                 prop = MEM_callocN(sizeof(IDProperty), "IDProperty array");
374                                 prop->len = prop->totallen = val.array.len;
375                                 prop->subtype = val.array.type;
376                                 prop->data.pointer = MEM_callocN(idp_size_table[val.array.type]*val.array.len, "id property array");
377                                 break;
378                         } else {
379                                 return NULL;
380                         }
381                 }
382                 case IDP_STRING:
383                 {
384                         char *st = val.str;
385                         int stlen;
386
387                         prop = MEM_callocN(sizeof(IDProperty), "IDProperty string");
388                         if (st == NULL) {
389                                 prop->data.pointer = MEM_callocN(DEFAULT_ALLOC_FOR_NULL_STRINGS, "id property string 1");
390                                 prop->totallen = DEFAULT_ALLOC_FOR_NULL_STRINGS;
391                                 prop->len = 1; /*NULL string, has len of 1 to account for null byte.*/
392                         } else {
393                                 stlen = strlen(st) + 1;
394                                 prop->data.pointer = MEM_callocN(stlen, "id property string 2");
395                                 prop->len = prop->totallen = stlen;
396                                 strcpy(prop->data.pointer, st);
397                         }
398                         break;
399                 }
400                 case IDP_GROUP:
401                 {
402                         prop = MEM_callocN(sizeof(IDProperty), "IDProperty group");
403                         /* heh I think all needed values are set properly by calloc anyway :) */
404                         break;
405                 }
406                 default:
407                 {
408                         prop = MEM_callocN(sizeof(IDProperty), "IDProperty array");
409                         break;
410                 }
411         }
412
413         prop->type = type;
414         strncpy(prop->name, name, MAX_IDPROP_NAME);
415         return prop;
416 }
417
418 /*NOTE: this will free all child properties of list arrays and groups!
419   Also, note that this does NOT unlink anything!  Plus it doesn't free
420   the actual IDProperty struct either.*/
421 void IDP_FreeProperty(IDProperty *prop)
422 {
423         switch (prop->type) {
424                 case IDP_ARRAY:
425                         IDP_FreeArray(prop);
426                         break;
427                 case IDP_STRING:
428                         IDP_FreeString(prop);
429                         break;
430                 case IDP_GROUP:
431                         IDP_FreeGroup(prop);
432                         break;
433         }
434 }
435
436 /*Unlinks any IDProperty<->ID linkage that might be going on.*/
437 void IDP_UnlinkProperty(IDProperty *prop)
438 {
439         switch (prop->type) {
440                 case IDP_ID:
441                         IDP_UnlinkID(prop);
442         }
443 }