svn merge -r 16866:17042 https://svn.blender.org/svnroot/bf-blender/trunk/blender
[blender.git] / source / blender / blenkernel / intern / idprop.c
1 /**
2  * $Id: idprop.c
3  *
4  * ***** BEGIN GPL 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.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software Foundation,
18  * Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
19  *
20  * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
21  * All rights reserved.
22  *
23  * Contributor(s): Joseph Eagar
24  *
25  * ***** END GPL LICENSE BLOCK *****
26  */
27  
28 #include "DNA_listBase.h"
29 #include "DNA_ID.h"
30
31 #include "BKE_idprop.h"
32 #include "BKE_global.h"
33 #include "BKE_library.h"
34 #include "BKE_utildefines.h"
35
36 #include "BLI_blenlib.h"
37
38 #include "MEM_guardedalloc.h"
39
40 #include <stdio.h>
41 #include <stdlib.h>
42 #include <string.h>
43
44 #define BSTR_EQ(a, b)   (*(a) == *(b) && !strcmp(a, b))
45
46 /* IDPropertyTemplate is a union in DNA_ID.h */
47
48 /*local size table.*/
49 static char idp_size_table[] = {
50         1, /*strings*/
51         sizeof(int),
52         sizeof(float),
53         sizeof(float)*3, /*Vector type, deprecated*/
54         sizeof(float)*16, /*Matrix type, deprecated*/
55         0, /*arrays don't have a fixed size*/
56         sizeof(ListBase), /*Group type*/
57         sizeof(void*),
58         sizeof(double)
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
106  static IDProperty *idp_generic_copy(IDProperty *prop)
107  {
108         IDProperty *newp = MEM_callocN(sizeof(IDProperty), "IDProperty array dup");
109
110         strncpy(newp->name, prop->name, MAX_IDPROP_NAME);
111         newp->type = prop->type;
112         newp->flag = prop->flag;
113         newp->data.val = prop->data.val;
114         newp->data.val2 = prop->data.val2;
115
116         return newp;
117  }
118
119 IDProperty *IDP_CopyArray(IDProperty *prop)
120 {
121         IDProperty *newp = idp_generic_copy(prop);
122
123         if (prop->data.pointer) newp->data.pointer = MEM_dupallocN(prop->data.pointer);
124         newp->len = prop->len;
125         newp->subtype = prop->subtype;
126         newp->totallen = prop->totallen;
127
128         return newp;
129 }
130
131 /*taken from readfile.c*/
132 #define SWITCH_LONGINT(a) { \
133     char s_i, *p_i; \
134     p_i= (char *)&(a);  \
135     s_i=p_i[0]; p_i[0]=p_i[7]; p_i[7]=s_i; \
136     s_i=p_i[1]; p_i[1]=p_i[6]; p_i[6]=s_i; \
137     s_i=p_i[2]; p_i[2]=p_i[5]; p_i[5]=s_i; \
138     s_i=p_i[3]; p_i[3]=p_i[4]; p_i[4]=s_i; }
139
140
141
142 /* ---------- String Type ------------ */
143 IDProperty *IDP_CopyString(IDProperty *prop)
144 {
145         IDProperty *newp = idp_generic_copy(prop);
146
147         if (prop->data.pointer) newp->data.pointer = MEM_dupallocN(prop->data.pointer);
148         newp->len = prop->len;
149         newp->subtype = prop->subtype;
150         newp->totallen = prop->totallen;
151
152         return newp;
153 }
154
155
156 void IDP_AssignString(IDProperty *prop, char *st)
157 {
158         int stlen;
159
160         stlen = strlen(st);
161
162         IDP_ResizeArray(prop, stlen+1); /*make room for null byte :) */
163         strcpy(prop->data.pointer, st);
164 }
165
166 void IDP_ConcatStringC(IDProperty *prop, char *st)
167 {
168         int newlen;
169
170         newlen = prop->len + strlen(st);
171         /*we have to remember that prop->len includes the null byte for strings.
172          so there's no need to add +1 to the resize function.*/
173         IDP_ResizeArray(prop, newlen);
174         strcat(prop->data.pointer, st);
175 }
176
177 void IDP_ConcatString(IDProperty *str1, IDProperty *append)
178 {
179         int newlen;
180
181         /*since ->len for strings includes the NULL byte, we have to subtract one or
182          we'll get an extra null byte after each concatination operation.*/
183         newlen = str1->len + append->len - 1;
184         IDP_ResizeArray(str1, newlen);
185         strcat(str1->data.pointer, append->data.pointer);
186 }
187
188 void IDP_FreeString(IDProperty *prop)
189 {
190         MEM_freeN(prop->data.pointer);
191 }
192
193
194 /*-------- ID Type, not in use yet -------*/
195
196 void IDP_LinkID(IDProperty *prop, ID *id)
197 {
198         if (prop->data.pointer) ((ID*)prop->data.pointer)->us--;
199         prop->data.pointer = id;
200         id_us_plus(id);
201 }
202
203 void IDP_UnlinkID(IDProperty *prop)
204 {
205         ((ID*)prop->data.pointer)->us--;
206 }
207
208 /*-------- Group Functions -------*/
209
210 /*checks if a property with the same name as prop exists, and if so replaces it.*/
211 IDProperty *IDP_CopyGroup(IDProperty *prop)
212 {
213         IDProperty *newp = idp_generic_copy(prop), *link;
214         newp->len = prop->len;
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 /*
224  replaces a property with the same name in a group, or adds 
225  it if the propery doesn't exist.
226 */
227 void IDP_ReplaceInGroup(IDProperty *group, IDProperty *prop)
228 {
229         IDProperty *loop;
230         for (loop=group->data.group.first; loop; loop=loop->next) {
231                 if (BSTR_EQ(loop->name, prop->name)) {
232                         if (loop->next) BLI_insertlinkbefore(&group->data.group, loop->next, prop);
233                         else BLI_addtail(&group->data.group, prop);
234                         
235                         BLI_remlink(&group->data.group, loop);
236                         IDP_FreeProperty(loop);
237                         MEM_freeN(loop);                        
238                         return;
239                 }
240         }
241
242         group->len++;
243         BLI_addtail(&group->data.group, prop);
244 }
245
246 /*returns 0 if an id property with the same name exists and it failed,
247   or 1 if it succeeded in adding to the group.*/
248 int IDP_AddToGroup(IDProperty *group, IDProperty *prop)
249 {
250         IDProperty *loop;
251         for (loop=group->data.group.first; loop; loop=loop->next) {
252                 if (BSTR_EQ(loop->name, prop->name)) return 0;
253         }
254
255         group->len++;
256         BLI_addtail(&group->data.group, prop);
257
258         return 1;
259 }
260
261 int IDP_InsertToGroup(IDProperty *group, IDProperty *previous, IDProperty *pnew)
262 {
263         IDProperty *loop;
264         for (loop=group->data.group.first; loop; loop=loop->next) {
265                 if (BSTR_EQ(loop->name, pnew->name)) return 0;
266         }
267         
268         group->len++;
269
270         BLI_insertlink(&group->data.group, previous, pnew);
271         return 1;
272 }
273
274 void IDP_RemFromGroup(IDProperty *group, IDProperty *prop)
275 {
276         group->len--;
277         BLI_remlink(&group->data.group, prop);
278 }
279
280 IDProperty *IDP_GetPropertyFromGroup(IDProperty *prop, char *name)
281 {
282         IDProperty *loop;
283         for (loop=prop->data.group.first; loop; loop=loop->next) {
284                 if (strcmp(loop->name, name)==0) return loop;
285         }
286         return NULL;
287 }
288
289 typedef struct IDPIter {
290         void *next;
291         IDProperty *parent;
292 } IDPIter;
293
294 void *IDP_GetGroupIterator(IDProperty *prop)
295 {
296         IDPIter *iter = MEM_callocN(sizeof(IDPIter), "IDPIter");
297         iter->next = prop->data.group.first;
298         iter->parent = prop;
299         return (void*) iter;
300 }
301
302 IDProperty *IDP_GroupIterNext(void *vself)
303 {
304         IDPIter *self = (IDPIter*) vself;
305         Link *next = (Link*) self->next;
306         if (self->next == NULL) {
307                 MEM_freeN(self);
308                 return NULL;
309         }
310
311         self->next = next->next;
312         return (void*) next;
313 }
314
315 void IDP_FreeIterBeforeEnd(void *vself)
316 {
317         MEM_freeN(vself);
318 }
319
320 /*Ok, the way things work, Groups free the ID Property structs of their children.
321   This is because all ID Property freeing functions free only direct data (not the ID Property
322   struct itself), but for Groups the child properties *are* considered
323   direct data.*/
324 static void IDP_FreeGroup(IDProperty *prop)
325 {
326         IDProperty *loop, *next;
327         for (loop=prop->data.group.first; loop; loop=next)
328         {
329                 next = loop->next;
330                 BLI_remlink(&prop->data.group, loop);
331                 IDP_FreeProperty(loop);
332                 MEM_freeN(loop);
333         }
334 }
335
336
337 /*-------- Main Functions --------*/
338 IDProperty *IDP_CopyProperty(IDProperty *prop)
339 {
340         switch (prop->type) {
341                 case IDP_GROUP: return IDP_CopyGroup(prop);
342                 case IDP_STRING: return IDP_CopyString(prop);
343                 case IDP_ARRAY: return IDP_CopyArray(prop);
344                 default: return idp_generic_copy(prop);
345         }
346 }
347
348 IDProperty *IDP_GetProperties(ID *id, int create_if_needed)
349 {
350         if (id->properties) return id->properties;
351         else {
352                 if (create_if_needed) {
353                         id->properties = MEM_callocN(sizeof(IDProperty), "IDProperty");
354                         id->properties->type = IDP_GROUP;
355                         /* dont overwite the data's name and type
356                          * some functions might need this if they
357                          * dont have a real ID, should be named elsewhere - Campbell */
358                         /* strcpy(id->name, "top_level_group");*/
359                 }
360                 return id->properties;
361         }
362 }
363
364 IDProperty *IDP_New(int type, IDPropertyTemplate val, char *name)
365 {
366         IDProperty *prop=NULL;
367
368         switch (type) {
369                 case IDP_INT:
370                         prop = MEM_callocN(sizeof(IDProperty), "IDProperty int");
371                         prop->data.val = val.i;
372                         break;
373                 case IDP_FLOAT:
374                         prop = MEM_callocN(sizeof(IDProperty), "IDProperty float");
375                         *(float*)&prop->data.val = val.f;
376                         break;
377                 case IDP_DOUBLE:
378                         prop = MEM_callocN(sizeof(IDProperty), "IDProperty float");
379                         *(double*)&prop->data.val = val.d;
380                         break;          
381                 case IDP_ARRAY:
382                 {
383                         /*for now, we only support float and int and double arrays*/
384                         if (val.array.type == IDP_FLOAT || val.array.type == IDP_INT || val.array.type == IDP_DOUBLE) {
385                                 prop = MEM_callocN(sizeof(IDProperty), "IDProperty array");
386                                 prop->len = prop->totallen = val.array.len;
387                                 prop->subtype = val.array.type;
388                                 prop->data.pointer = MEM_callocN(idp_size_table[val.array.type]*val.array.len, "id property array");
389                                 break;
390                         } else {
391                                 return NULL;
392                         }
393                 }
394                 case IDP_STRING:
395                 {
396                         char *st = val.str;
397                         int stlen;
398
399                         prop = MEM_callocN(sizeof(IDProperty), "IDProperty string");
400                         if (st == NULL) {
401                                 prop->data.pointer = MEM_callocN(DEFAULT_ALLOC_FOR_NULL_STRINGS, "id property string 1");
402                                 prop->totallen = DEFAULT_ALLOC_FOR_NULL_STRINGS;
403                                 prop->len = 1; /*NULL string, has len of 1 to account for null byte.*/
404                         } else {
405                                 stlen = strlen(st) + 1;
406                                 prop->data.pointer = MEM_callocN(stlen, "id property string 2");
407                                 prop->len = prop->totallen = stlen;
408                                 strcpy(prop->data.pointer, st);
409                         }
410                         break;
411                 }
412                 case IDP_GROUP:
413                 {
414                         prop = MEM_callocN(sizeof(IDProperty), "IDProperty group");
415                         /* heh I think all needed values are set properly by calloc anyway :) */
416                         break;
417                 }
418                 default:
419                 {
420                         prop = MEM_callocN(sizeof(IDProperty), "IDProperty array");
421                         break;
422                 }
423         }
424
425         prop->type = type;
426         strncpy(prop->name, name, MAX_IDPROP_NAME);
427         
428         /*security null byte*/
429         prop->name[MAX_IDPROP_NAME-1] = 0;
430         
431         return prop;
432 }
433
434 /*NOTE: this will free all child properties including list arrays and groups!
435   Also, note that this does NOT unlink anything!  Plus it doesn't free
436   the actual IDProperty struct either.*/
437 void IDP_FreeProperty(IDProperty *prop)
438 {
439         switch (prop->type) {
440                 case IDP_ARRAY:
441                         IDP_FreeArray(prop);
442                         break;
443                 case IDP_STRING:
444                         IDP_FreeString(prop);
445                         break;
446                 case IDP_GROUP:
447                         IDP_FreeGroup(prop);
448                         break;
449         }
450 }
451
452 /*Unlinks any IDProperty<->ID linkage that might be going on.
453   note: currently unused.*/
454 void IDP_UnlinkProperty(IDProperty *prop)
455 {
456         switch (prop->type) {
457                 case IDP_ID:
458                         IDP_UnlinkID(prop);
459         }
460 }