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