resolved conflict state with HEAD r14096
[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
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         newp->len = prop->len;
216         
217         for (link=prop->data.group.first; link; link=link->next) {
218                 BLI_addtail(&newp->data.group, IDP_CopyProperty(link));
219         }
220
221         return newp;
222 }
223
224 void IDP_ReplaceInGroup(IDProperty *group, IDProperty *prop)
225 {
226         IDProperty *loop;
227         for (loop=group->data.group.first; loop; loop=loop->next) {
228                 if (BSTR_EQ(loop->name, prop->name)) {
229                         if (loop->next) BLI_insertlinkbefore(&group->data.group, loop->next, prop);
230                         else BLI_addtail(&group->data.group, prop);
231                         
232                         BLI_remlink(&group->data.group, loop);
233                         IDP_FreeProperty(loop);
234                         MEM_freeN(loop);
235                         
236                         return;
237                 }
238         }
239
240         group->len++;
241         BLI_addtail(&group->data.group, prop);
242 }
243
244 /*returns 0 if an id property with the same name exists and it failed,
245   or 1 if it succeeded in adding to the group.*/
246 int IDP_AddToGroup(IDProperty *group, IDProperty *prop)
247 {
248         IDProperty *loop;
249         for (loop=group->data.group.first; loop; loop=loop->next) {
250                 if (BSTR_EQ(loop->name, prop->name)) return 0;
251         }
252
253         group->len++;
254         BLI_addtail(&group->data.group, prop);
255
256         return 1;
257 }
258
259 int IDP_InsertToGroup(IDProperty *group, IDProperty *previous, IDProperty *pnew)
260 {
261         IDProperty *loop;
262         for (loop=group->data.group.first; loop; loop=loop->next) {
263                 if (BSTR_EQ(loop->name, pnew->name)) return 0;
264         }
265         
266         group->len++;
267         
268         BLI_insertlink(&group->data.group, previous, pnew);
269         return 1;
270 }
271
272 void IDP_RemFromGroup(IDProperty *group, IDProperty *prop)
273 {
274         group->len--;
275         BLI_remlink(&group->data.group, prop);
276 }
277
278 IDProperty *IDP_GetPropertyFromGroup(IDProperty *prop, char *name)
279 {
280         IDProperty *loop;
281         for (loop=prop->data.group.first; loop; loop=loop->next) {
282                 if (strcmp(loop->name, name)==0) return loop;
283         }
284         return NULL;
285 }
286
287 typedef struct IDPIter {
288         void *next;
289         IDProperty *parent;
290 } IDPIter;
291
292 void *IDP_GetGroupIterator(IDProperty *prop)
293 {
294         IDPIter *iter = MEM_callocN(sizeof(IDPIter), "IDPIter");
295         iter->next = prop->data.group.first;
296         iter->parent = prop;
297         return (void*) iter;
298 }
299
300 IDProperty *IDP_GroupIterNext(void *vself)
301 {
302         IDPIter *self = (IDPIter*) vself;
303         Link *next = (Link*) self->next;
304         if (self->next == NULL) {
305                 MEM_freeN(self);
306                 return NULL;
307         }
308
309         self->next = next->next;
310         return (void*) next;
311 }
312
313 void IDP_FreeIterBeforeEnd(void *vself)
314 {
315         MEM_freeN(vself);
316 }
317
318 /*Ok, the way things work, Groups free the ID Property structs of their children.
319   This is because all ID Property freeing functions free only direct data (not the ID Property
320   struct itself), but for Groups the child properties *are* considered
321   direct data.*/
322 void IDP_FreeGroup(IDProperty *prop)
323 {
324         IDProperty *loop, *next;
325         for (loop=prop->data.group.first; loop; loop=next)
326         {
327                 next = loop->next;
328                 BLI_remlink(&prop->data.group, loop);
329                 IDP_FreeProperty(loop);
330                 MEM_freeN(loop);
331         }
332 }
333
334
335 /*-------- Main Functions --------*/
336 IDProperty *IDP_CopyProperty(IDProperty *prop)
337 {
338         switch (prop->type) {
339                 case IDP_GROUP: return IDP_CopyGroup(prop);
340                 case IDP_STRING: return IDP_CopyString(prop);
341                 case IDP_ARRAY: return IDP_CopyArray(prop);
342                 default: return idp_generic_copy(prop);
343         }
344 }
345
346 IDProperty *IDP_GetProperties(ID *id, int create_if_needed)
347 {
348         if (id->properties) return id->properties;
349         else {
350                 if (create_if_needed) {
351                         id->properties = MEM_callocN(sizeof(IDProperty), "IDProperty");
352                         id->properties->type = IDP_GROUP;
353                 }
354                 return id->properties;
355         }
356 }
357
358 IDProperty *IDP_New(int type, IDPropertyTemplate val, char *name)
359 {
360         IDProperty *prop=NULL;
361
362         switch (type) {
363                 case IDP_INT:
364                         prop = MEM_callocN(sizeof(IDProperty), "IDProperty int");
365                         prop->data.val = val.i;
366                         break;
367                 case IDP_FLOAT:
368                         prop = MEM_callocN(sizeof(IDProperty), "IDProperty float");
369                         *(float*)&prop->data.val = val.f;
370                         break;
371                 case IDP_ARRAY:
372                 {
373                         /*for now, we only support float and int arrays*/
374                         if (val.array.type == IDP_FLOAT || val.array.type == IDP_INT) {
375                                 prop = MEM_callocN(sizeof(IDProperty), "IDProperty array");
376                                 prop->len = prop->totallen = val.array.len;
377                                 prop->subtype = val.array.type;
378                                 prop->data.pointer = MEM_callocN(idp_size_table[val.array.type]*val.array.len, "id property array");
379                                 break;
380                         } else {
381                                 return NULL;
382                         }
383                 }
384                 case IDP_STRING:
385                 {
386                         char *st = val.str;
387                         int stlen;
388
389                         prop = MEM_callocN(sizeof(IDProperty), "IDProperty string");
390                         if (st == NULL) {
391                                 prop->data.pointer = MEM_callocN(DEFAULT_ALLOC_FOR_NULL_STRINGS, "id property string 1");
392                                 prop->totallen = DEFAULT_ALLOC_FOR_NULL_STRINGS;
393                                 prop->len = 1; /*NULL string, has len of 1 to account for null byte.*/
394                         } else {
395                                 stlen = strlen(st) + 1;
396                                 prop->data.pointer = MEM_callocN(stlen, "id property string 2");
397                                 prop->len = prop->totallen = stlen;
398                                 strcpy(prop->data.pointer, st);
399                         }
400                         break;
401                 }
402                 case IDP_GROUP:
403                 {
404                         prop = MEM_callocN(sizeof(IDProperty), "IDProperty group");
405                         /* heh I think all needed values are set properly by calloc anyway :) */
406                         break;
407                 }
408                 default:
409                 {
410                         prop = MEM_callocN(sizeof(IDProperty), "IDProperty array");
411                         break;
412                 }
413         }
414
415         prop->type = type;
416         strncpy(prop->name, name, MAX_IDPROP_NAME);
417         return prop;
418 }
419
420 /*NOTE: this will free all child properties of list arrays and groups!
421   Also, note that this does NOT unlink anything!  Plus it doesn't free
422   the actual IDProperty struct either.*/
423 void IDP_FreeProperty(IDProperty *prop)
424 {
425         switch (prop->type) {
426                 case IDP_ARRAY:
427                         IDP_FreeArray(prop);
428                         break;
429                 case IDP_STRING:
430                         IDP_FreeString(prop);
431                         break;
432                 case IDP_GROUP:
433                         IDP_FreeGroup(prop);
434                         break;
435         }
436 }
437
438 /*Unlinks any IDProperty<->ID linkage that might be going on.*/
439 void IDP_UnlinkProperty(IDProperty *prop)
440 {
441         switch (prop->type) {
442                 case IDP_ID:
443                         IDP_UnlinkID(prop);
444         }
445 }