merge runk 16887:16950
[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                         strcpy(id->name, "top_level_group");
356                 }
357                 return id->properties;
358         }
359 }
360
361 IDProperty *IDP_New(int type, IDPropertyTemplate val, char *name)
362 {
363         IDProperty *prop=NULL;
364
365         switch (type) {
366                 case IDP_INT:
367                         prop = MEM_callocN(sizeof(IDProperty), "IDProperty int");
368                         prop->data.val = val.i;
369                         break;
370                 case IDP_FLOAT:
371                         prop = MEM_callocN(sizeof(IDProperty), "IDProperty float");
372                         *(float*)&prop->data.val = val.f;
373                         break;
374                 case IDP_DOUBLE:
375                         prop = MEM_callocN(sizeof(IDProperty), "IDProperty float");
376                         *(double*)&prop->data.val = val.d;
377                         break;          
378                 case IDP_ARRAY:
379                 {
380                         /*for now, we only support float and int and double arrays*/
381                         if (val.array.type == IDP_FLOAT || val.array.type == IDP_INT || val.array.type == IDP_DOUBLE) {
382                                 prop = MEM_callocN(sizeof(IDProperty), "IDProperty array");
383                                 prop->len = prop->totallen = val.array.len;
384                                 prop->subtype = val.array.type;
385                                 prop->data.pointer = MEM_callocN(idp_size_table[val.array.type]*val.array.len, "id property array");
386                                 break;
387                         } else {
388                                 return NULL;
389                         }
390                 }
391                 case IDP_STRING:
392                 {
393                         char *st = val.str;
394                         int stlen;
395
396                         prop = MEM_callocN(sizeof(IDProperty), "IDProperty string");
397                         if (st == NULL) {
398                                 prop->data.pointer = MEM_callocN(DEFAULT_ALLOC_FOR_NULL_STRINGS, "id property string 1");
399                                 prop->totallen = DEFAULT_ALLOC_FOR_NULL_STRINGS;
400                                 prop->len = 1; /*NULL string, has len of 1 to account for null byte.*/
401                         } else {
402                                 stlen = strlen(st) + 1;
403                                 prop->data.pointer = MEM_callocN(stlen, "id property string 2");
404                                 prop->len = prop->totallen = stlen;
405                                 strcpy(prop->data.pointer, st);
406                         }
407                         break;
408                 }
409                 case IDP_GROUP:
410                 {
411                         prop = MEM_callocN(sizeof(IDProperty), "IDProperty group");
412                         /* heh I think all needed values are set properly by calloc anyway :) */
413                         break;
414                 }
415                 default:
416                 {
417                         prop = MEM_callocN(sizeof(IDProperty), "IDProperty array");
418                         break;
419                 }
420         }
421
422         prop->type = type;
423         strncpy(prop->name, name, MAX_IDPROP_NAME);
424         
425         /*security null byte*/
426         prop->name[MAX_IDPROP_NAME-1] = 0;
427         
428         return prop;
429 }
430
431 /*NOTE: this will free all child properties including list arrays and groups!
432   Also, note that this does NOT unlink anything!  Plus it doesn't free
433   the actual IDProperty struct either.*/
434 void IDP_FreeProperty(IDProperty *prop)
435 {
436         switch (prop->type) {
437                 case IDP_ARRAY:
438                         IDP_FreeArray(prop);
439                         break;
440                 case IDP_STRING:
441                         IDP_FreeString(prop);
442                         break;
443                 case IDP_GROUP:
444                         IDP_FreeGroup(prop);
445                         break;
446         }
447 }
448
449 /*Unlinks any IDProperty<->ID linkage that might be going on.
450   note: currently unused.*/
451 void IDP_UnlinkProperty(IDProperty *prop)
452 {
453         switch (prop->type) {
454                 case IDP_ID:
455                         IDP_UnlinkID(prop);
456         }
457 }