svn merge -r 21041:21301 https://svn.blender.org/svnroot/bf-blender/branches/blender2...
[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 <stdio.h>
29 #include <stdlib.h>
30 #include <string.h>
31
32 #include "DNA_listBase.h"
33 #include "DNA_ID.h"
34
35 #include "BKE_idprop.h"
36 #include "BKE_global.h"
37 #include "BKE_library.h"
38 #include "BKE_utildefines.h"
39
40 #include "BLI_blenlib.h"
41
42 #include "MEM_guardedalloc.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 /* ------------Property Array Type ----------- */
62 #define GETPROP(prop, i) (((IDProperty*)(prop)->data.pointer)+(i))
63
64 /* --------- property array type -------------*/
65
66 /*note: as a start to move away from the stupid IDP_New function, this type
67   has it's own allocation function.*/
68 IDProperty *IDP_NewIDPArray(const char *name)
69 {
70         IDProperty *prop = MEM_callocN(sizeof(IDProperty), "IDProperty prop array");
71         prop->type = IDP_IDPARRAY;
72         prop->len = 0;
73         BLI_strncpy(prop->name, name, MAX_IDPROP_NAME);
74         
75         return prop;
76 }
77
78 IDProperty *IDP_CopyIDPArray(IDProperty *array)
79 {
80         IDProperty *narray = MEM_dupallocN(array), *tmp;
81         int i;
82         
83         narray->data.pointer = MEM_dupallocN(array->data.pointer);
84         for (i=0; i<narray->len; i++) {
85                 /*ok, the copy functions always allocate a new structure,
86                   which doesn't work here.  instead, simply copy the
87                   contents of the new structure into the array cell,
88                   then free it.  this makes for more maintainable
89                   code than simply reimplementing the copy functions
90                   in this loop.*/
91                 tmp = IDP_CopyProperty(GETPROP(narray, i));
92                 memcpy(GETPROP(narray, i), tmp, sizeof(IDProperty));
93                 MEM_freeN(tmp);
94         }
95         
96         return narray;
97 }
98
99 void IDP_FreeIDPArray(IDProperty *prop)
100 {
101         int i;
102         
103         for (i=0; i<prop->len; i++)
104                 IDP_FreeProperty(GETPROP(prop, i));
105
106         if(prop->data.pointer)
107                 MEM_freeN(prop->data.pointer);
108 }
109
110 /*shallow copies item*/
111 void IDP_SetIndexArray(IDProperty *prop, int index, IDProperty *item)
112 {
113         IDProperty *old = GETPROP(prop, index);
114         if (index >= prop->len || index < 0) return;
115         if (item != old) IDP_FreeProperty(old);
116         
117         memcpy(GETPROP(prop, index), item, sizeof(IDProperty));
118 }
119
120 IDProperty *IDP_GetIndexArray(IDProperty *prop, int index)
121 {
122         return GETPROP(prop, index);
123 }
124
125 IDProperty *IDP_AppendArray(IDProperty *prop, IDProperty *item)
126 {
127         IDP_ResizeIDPArray(prop, prop->len+1);
128         IDP_SetIndexArray(prop, prop->len-1, item);
129         return item;
130 }
131
132 void IDP_ResizeIDPArray(IDProperty *prop, int newlen)
133 {
134         void *newarr;
135         int newsize=newlen;
136
137         /*first check if the array buffer size has room*/
138         /*if newlen is 200 chars less then totallen, reallocate anyway*/
139         if (newlen <= prop->totallen && prop->totallen - newlen < 200) {
140                 int i;
141
142                 for(i=newlen; i<prop->len; i++)
143                         IDP_FreeProperty(GETPROP(prop, i));
144
145                 prop->len = newlen;
146                 return;
147         }
148
149         /* - Note: This code comes from python, here's the corrusponding comment. - */
150         /* This over-allocates proportional to the list size, making room
151          * for additional growth.  The over-allocation is mild, but is
152          * enough to give linear-time amortized behavior over a long
153          * sequence of appends() in the presence of a poorly-performing
154          * system realloc().
155          * The growth pattern is:  0, 4, 8, 16, 25, 35, 46, 58, 72, 88, ...
156          */
157         newsize = (newsize >> 3) + (newsize < 9 ? 3 : 6) + newsize;
158
159         newarr = MEM_callocN(sizeof(IDProperty)*newsize, "idproperty array resized");
160         if (newlen >= prop->len) {
161                 /* newlen is bigger*/
162                 memcpy(newarr, prop->data.pointer, prop->len*sizeof(IDProperty));
163         }
164         else {
165                 int i;
166                 /* newlen is smaller*/
167                 for (i=newlen; i<prop->len; i++) {
168                         IDP_FreeProperty(GETPROP(prop, i));
169                 }
170                 memcpy(newarr, prop->data.pointer, newlen*prop->len*sizeof(IDProperty));
171         }
172
173         if(prop->data.pointer)
174                 MEM_freeN(prop->data.pointer);
175         prop->data.pointer = newarr;
176         prop->len = newlen;
177         prop->totallen = newsize;
178 }
179
180 /* ----------- Numerical Array Type ----------- */
181 static void idp_resize_group_array(IDProperty *prop, int newlen, void *newarr)
182 {
183         if(prop->subtype != IDP_GROUP)
184                 return;
185
186         if(newlen >= prop->len) {
187                 /* bigger */
188                 IDProperty **array= newarr;
189                 IDPropertyTemplate val;
190                 int a;
191
192                 for(a=prop->len; a<newlen; a++) {
193                         val.i = 0; /* silence MSVC warning about uninitialized var when debugging */
194                         array[a]= IDP_New(IDP_GROUP, val, "IDP_ResizeArray group");
195                 }
196         }
197         else {
198                 /* smaller */
199                 IDProperty **array= prop->data.pointer;
200                 int a;
201
202                 for(a=newlen; a<prop->len; a++) {
203                         IDP_FreeProperty(array[a]);
204                         MEM_freeN(array[a]);
205                 }
206         }
207 }
208
209 /*this function works for strings too!*/
210 void IDP_ResizeArray(IDProperty *prop, int newlen)
211 {
212         void *newarr;
213         int newsize=newlen;
214
215         /*first check if the array buffer size has room*/
216         /*if newlen is 200 chars less then totallen, reallocate anyway*/
217         if (newlen <= prop->totallen && prop->totallen - newlen < 200) {
218                 idp_resize_group_array(prop, newlen, prop->data.pointer);
219                 prop->len = newlen;
220                 return;
221         }
222
223         /* - Note: This code comes from python, here's the corrusponding comment. - */
224         /* This over-allocates proportional to the list size, making room
225          * for additional growth.  The over-allocation is mild, but is
226          * enough to give linear-time amortized behavior over a long
227          * sequence of appends() in the presence of a poorly-performing
228          * system realloc().
229          * The growth pattern is:  0, 4, 8, 16, 25, 35, 46, 58, 72, 88, ...
230          */
231         newsize = (newsize >> 3) + (newsize < 9 ? 3 : 6) + newsize;
232
233         newarr = MEM_callocN(idp_size_table[(int)prop->subtype]*newsize, "idproperty array resized");
234         if (newlen >= prop->len) {
235                 /* newlen is bigger*/
236                 memcpy(newarr, prop->data.pointer, prop->len*idp_size_table[(int)prop->subtype]);
237                 idp_resize_group_array(prop, newlen, newarr);
238         }
239         else {
240                 /* newlen is smaller*/
241                 idp_resize_group_array(prop, newlen, newarr);
242                 memcpy(newarr, prop->data.pointer, newlen*prop->len*idp_size_table[(int)prop->subtype]);
243         }
244
245         MEM_freeN(prop->data.pointer);
246         prop->data.pointer = newarr;
247         prop->len = newlen;
248         prop->totallen = newsize;
249 }
250
251 void IDP_FreeArray(IDProperty *prop)
252 {
253         if (prop->data.pointer) {
254                 idp_resize_group_array(prop, 0, NULL);
255                 MEM_freeN(prop->data.pointer);
256         }
257 }
258
259
260  static IDProperty *idp_generic_copy(IDProperty *prop)
261  {
262         IDProperty *newp = MEM_callocN(sizeof(IDProperty), "IDProperty array dup");
263
264         BLI_strncpy(newp->name, prop->name, MAX_IDPROP_NAME);
265         newp->type = prop->type;
266         newp->flag = prop->flag;
267         newp->data.val = prop->data.val;
268         newp->data.val2 = prop->data.val2;
269
270         return newp;
271  }
272
273 IDProperty *IDP_CopyArray(IDProperty *prop)
274 {
275         IDProperty *newp = idp_generic_copy(prop);
276
277         if (prop->data.pointer) {
278                 newp->data.pointer = MEM_dupallocN(prop->data.pointer);
279
280                 if(prop->type == IDP_GROUP) {
281                         IDProperty **array= newp->data.pointer;
282                         int a;
283
284                         for(a=0; a<prop->len; a++)
285                                 array[a]= IDP_CopyProperty(array[a]);
286                 }
287         }
288         newp->len = prop->len;
289         newp->subtype = prop->subtype;
290         newp->totallen = prop->totallen;
291
292         return newp;
293 }
294
295 /*taken from readfile.c*/
296 #define SWITCH_LONGINT(a) { \
297     char s_i, *p_i; \
298     p_i= (char *)&(a);  \
299     s_i=p_i[0]; p_i[0]=p_i[7]; p_i[7]=s_i; \
300     s_i=p_i[1]; p_i[1]=p_i[6]; p_i[6]=s_i; \
301     s_i=p_i[2]; p_i[2]=p_i[5]; p_i[5]=s_i; \
302     s_i=p_i[3]; p_i[3]=p_i[4]; p_i[4]=s_i; }
303
304
305
306 /* ---------- String Type ------------ */
307 IDProperty *IDP_CopyString(IDProperty *prop)
308 {
309         IDProperty *newp = idp_generic_copy(prop);
310
311         if (prop->data.pointer) newp->data.pointer = MEM_dupallocN(prop->data.pointer);
312         newp->len = prop->len;
313         newp->subtype = prop->subtype;
314         newp->totallen = prop->totallen;
315
316         return newp;
317 }
318
319
320 void IDP_AssignString(IDProperty *prop, char *st)
321 {
322         int stlen;
323
324         stlen = strlen(st);
325
326         IDP_ResizeArray(prop, stlen+1); /*make room for null byte :) */
327         strcpy(prop->data.pointer, st);
328 }
329
330 void IDP_ConcatStringC(IDProperty *prop, char *st)
331 {
332         int newlen;
333
334         newlen = prop->len + strlen(st);
335         /*we have to remember that prop->len includes the null byte for strings.
336          so there's no need to add +1 to the resize function.*/
337         IDP_ResizeArray(prop, newlen);
338         strcat(prop->data.pointer, st);
339 }
340
341 void IDP_ConcatString(IDProperty *str1, IDProperty *append)
342 {
343         int newlen;
344
345         /*since ->len for strings includes the NULL byte, we have to subtract one or
346          we'll get an extra null byte after each concatination operation.*/
347         newlen = str1->len + append->len - 1;
348         IDP_ResizeArray(str1, newlen);
349         strcat(str1->data.pointer, append->data.pointer);
350 }
351
352 void IDP_FreeString(IDProperty *prop)
353 {
354         if(prop->data.pointer)
355                 MEM_freeN(prop->data.pointer);
356 }
357
358
359 /*-------- ID Type, not in use yet -------*/
360
361 void IDP_LinkID(IDProperty *prop, ID *id)
362 {
363         if (prop->data.pointer) ((ID*)prop->data.pointer)->us--;
364         prop->data.pointer = id;
365         id_us_plus(id);
366 }
367
368 void IDP_UnlinkID(IDProperty *prop)
369 {
370         ((ID*)prop->data.pointer)->us--;
371 }
372
373 /*-------- Group Functions -------*/
374
375 /*checks if a property with the same name as prop exists, and if so replaces it.*/
376 IDProperty *IDP_CopyGroup(IDProperty *prop)
377 {
378         IDProperty *newp = idp_generic_copy(prop), *link;
379         newp->len = prop->len;
380         
381         for (link=prop->data.group.first; link; link=link->next) {
382                 BLI_addtail(&newp->data.group, IDP_CopyProperty(link));
383         }
384
385         return newp;
386 }
387
388 /*
389  replaces a property with the same name in a group, or adds 
390  it if the propery doesn't exist.
391 */
392 void IDP_ReplaceInGroup(IDProperty *group, IDProperty *prop)
393 {
394         IDProperty *loop;
395         for (loop=group->data.group.first; loop; loop=loop->next) {
396                 if (BSTR_EQ(loop->name, prop->name)) {
397                         if (loop->next) BLI_insertlinkbefore(&group->data.group, loop->next, prop);
398                         else BLI_addtail(&group->data.group, prop);
399                         
400                         BLI_remlink(&group->data.group, loop);
401                         IDP_FreeProperty(loop);
402                         MEM_freeN(loop);                        
403                         return;
404                 }
405         }
406
407         group->len++;
408         BLI_addtail(&group->data.group, prop);
409 }
410
411 /*returns 0 if an id property with the same name exists and it failed,
412   or 1 if it succeeded in adding to the group.*/
413 int IDP_AddToGroup(IDProperty *group, IDProperty *prop)
414 {
415         IDProperty *loop;
416         for (loop=group->data.group.first; loop; loop=loop->next) {
417                 if (BSTR_EQ(loop->name, prop->name)) return 0;
418         }
419
420         group->len++;
421         BLI_addtail(&group->data.group, prop);
422
423         return 1;
424 }
425
426 int IDP_InsertToGroup(IDProperty *group, IDProperty *previous, IDProperty *pnew)
427 {
428         IDProperty *loop;
429         for (loop=group->data.group.first; loop; loop=loop->next) {
430                 if (BSTR_EQ(loop->name, pnew->name)) return 0;
431         }
432         
433         group->len++;
434
435         BLI_insertlink(&group->data.group, previous, pnew);
436         return 1;
437 }
438
439 void IDP_RemFromGroup(IDProperty *group, IDProperty *prop)
440 {
441         group->len--;
442         BLI_remlink(&group->data.group, prop);
443 }
444
445 IDProperty *IDP_GetPropertyFromGroup(IDProperty *prop, char *name)
446 {
447         IDProperty *loop;
448         for (loop=prop->data.group.first; loop; loop=loop->next) {
449                 if (strcmp(loop->name, name)==0) return loop;
450         }
451         return NULL;
452 }
453
454 typedef struct IDPIter {
455         void *next;
456         IDProperty *parent;
457 } IDPIter;
458
459 void *IDP_GetGroupIterator(IDProperty *prop)
460 {
461         IDPIter *iter = MEM_callocN(sizeof(IDPIter), "IDPIter");
462         iter->next = prop->data.group.first;
463         iter->parent = prop;
464         return (void*) iter;
465 }
466
467 IDProperty *IDP_GroupIterNext(void *vself)
468 {
469         IDPIter *self = (IDPIter*) vself;
470         Link *next = (Link*) self->next;
471         if (self->next == NULL) {
472                 MEM_freeN(self);
473                 return NULL;
474         }
475
476         self->next = next->next;
477         return (void*) next;
478 }
479
480 void IDP_FreeIterBeforeEnd(void *vself)
481 {
482         MEM_freeN(vself);
483 }
484
485 /*Ok, the way things work, Groups free the ID Property structs of their children.
486   This is because all ID Property freeing functions free only direct data (not the ID Property
487   struct itself), but for Groups the child properties *are* considered
488   direct data.*/
489 static void IDP_FreeGroup(IDProperty *prop)
490 {
491         IDProperty *loop, *next;
492         for (loop=prop->data.group.first; loop; loop=next)
493         {
494                 next = loop->next;
495                 BLI_remlink(&prop->data.group, loop);
496                 IDP_FreeProperty(loop);
497                 MEM_freeN(loop);
498         }
499 }
500
501
502 /*-------- Main Functions --------*/
503 IDProperty *IDP_CopyProperty(IDProperty *prop)
504 {
505         switch (prop->type) {
506                 case IDP_GROUP: return IDP_CopyGroup(prop);
507                 case IDP_STRING: return IDP_CopyString(prop);
508                 case IDP_ARRAY: return IDP_CopyArray(prop);
509                 case IDP_IDPARRAY: return IDP_CopyIDPArray(prop);
510                 default: return idp_generic_copy(prop);
511         }
512 }
513
514 IDProperty *IDP_GetProperties(ID *id, int create_if_needed)
515 {
516         if (id->properties) return id->properties;
517         else {
518                 if (create_if_needed) {
519                         id->properties = MEM_callocN(sizeof(IDProperty), "IDProperty");
520                         id->properties->type = IDP_GROUP;
521                         /* dont overwite the data's name and type
522                          * some functions might need this if they
523                          * dont have a real ID, should be named elsewhere - Campbell */
524                         /* strcpy(id->name, "top_level_group");*/
525                 }
526                 return id->properties;
527         }
528 }
529
530 int IDP_EqualsProperties(IDProperty *prop1, IDProperty *prop2)
531 {
532         if(prop1 == NULL && prop2 == NULL)
533                 return 1;
534         else if(prop1 == NULL || prop2 == NULL)
535                 return 0;
536         else if(prop1->type != prop2->type)
537                 return 0;
538
539         if(prop1->type == IDP_INT)
540                 return (IDP_Int(prop1) == IDP_Int(prop2));
541         else if(prop1->type == IDP_FLOAT)
542                 return (IDP_Float(prop1) == IDP_Float(prop2));
543         else if(prop1->type == IDP_DOUBLE)
544                 return (IDP_Double(prop1) == IDP_Double(prop2));
545         else if(prop1->type == IDP_STRING)
546                 return BSTR_EQ(IDP_String(prop1), IDP_String(prop2));
547         else if(prop1->type == IDP_ARRAY) {
548                 if(prop1->len == prop2->len && prop1->subtype == prop2->subtype)
549                         return memcmp(IDP_Array(prop1), IDP_Array(prop2), idp_size_table[(int)prop1->subtype]*prop1->len);
550                 else
551                         return 0;
552         }
553         else if(prop1->type == IDP_GROUP) {
554                 IDProperty *link1, *link2;
555
556                 if(BLI_countlist(&prop1->data.group) != BLI_countlist(&prop2->data.group))
557                         return 0;
558
559                 for(link1=prop1->data.group.first; link1; link1=link1->next) {
560                         link2= IDP_GetPropertyFromGroup(prop2, link1->name);
561
562                         if(!IDP_EqualsProperties(link1, link2))
563                                 return 0;
564                 }
565
566                 return 1;
567         }
568         else if(prop1->type == IDP_IDPARRAY) {
569                 IDProperty *array1= IDP_IDPArray(prop1);
570                 IDProperty *array2= IDP_IDPArray(prop2);
571                 int i;
572
573                 if(prop1->len != prop2->len)
574                         return 0;
575                 
576                 for(i=0; i<prop1->len; i++)
577                         if(!IDP_EqualsProperties(&array1[i], &array2[i]))
578                                 return 0;
579         }
580         
581         return 1;
582 }
583
584 IDProperty *IDP_New(int type, IDPropertyTemplate val, const char *name)
585 {
586         IDProperty *prop=NULL;
587
588         switch (type) {
589                 case IDP_INT:
590                         prop = MEM_callocN(sizeof(IDProperty), "IDProperty int");
591                         prop->data.val = val.i;
592                         break;
593                 case IDP_FLOAT:
594                         prop = MEM_callocN(sizeof(IDProperty), "IDProperty float");
595                         *(float*)&prop->data.val = val.f;
596                         break;
597                 case IDP_DOUBLE:
598                         prop = MEM_callocN(sizeof(IDProperty), "IDProperty float");
599                         *(double*)&prop->data.val = val.d;
600                         break;          
601                 case IDP_ARRAY:
602                 {
603                         /*for now, we only support float and int and double arrays*/
604                         if (val.array.type == IDP_FLOAT || val.array.type == IDP_INT || val.array.type == IDP_DOUBLE || val.array.type == IDP_GROUP) {
605                                 prop = MEM_callocN(sizeof(IDProperty), "IDProperty array");
606                                 prop->subtype = val.array.type;
607                                 if (val.array.len)
608                                         prop->data.pointer = MEM_callocN(idp_size_table[val.array.type]*val.array.len, "id property array");
609                                 prop->len = prop->totallen = val.array.len;
610                                 break;
611                         } else {
612                                 return NULL;
613                         }
614                 }
615                 case IDP_STRING:
616                 {
617                         char *st = val.str;
618                         int stlen;
619
620                         prop = MEM_callocN(sizeof(IDProperty), "IDProperty string");
621                         if (st == NULL) {
622                                 prop->data.pointer = MEM_callocN(DEFAULT_ALLOC_FOR_NULL_STRINGS, "id property string 1");
623                                 prop->totallen = DEFAULT_ALLOC_FOR_NULL_STRINGS;
624                                 prop->len = 1; /*NULL string, has len of 1 to account for null byte.*/
625                         } else {
626                                 stlen = strlen(st) + 1;
627                                 prop->data.pointer = MEM_callocN(stlen, "id property string 2");
628                                 prop->len = prop->totallen = stlen;
629                                 strcpy(prop->data.pointer, st);
630                         }
631                         break;
632                 }
633                 case IDP_GROUP:
634                 {
635                         prop = MEM_callocN(sizeof(IDProperty), "IDProperty group");
636                         /* heh I think all needed values are set properly by calloc anyway :) */
637                         break;
638                 }
639                 default:
640                 {
641                         prop = MEM_callocN(sizeof(IDProperty), "IDProperty array");
642                         break;
643                 }
644         }
645
646         prop->type = type;
647         BLI_strncpy(prop->name, name, MAX_IDPROP_NAME);
648         
649         /*security null byte*/
650         prop->name[MAX_IDPROP_NAME-1] = 0;
651         
652         return prop;
653 }
654
655 /*NOTE: this will free all child properties including list arrays and groups!
656   Also, note that this does NOT unlink anything!  Plus it doesn't free
657   the actual IDProperty struct either.*/
658 void IDP_FreeProperty(IDProperty *prop)
659 {
660         switch (prop->type) {
661                 case IDP_ARRAY:
662                         IDP_FreeArray(prop);
663                         break;
664                 case IDP_STRING:
665                         IDP_FreeString(prop);
666                         break;
667                 case IDP_GROUP:
668                         IDP_FreeGroup(prop);
669                         break;
670                 case IDP_IDPARRAY:
671                         IDP_FreeIDPArray(prop);
672                         break;
673         }
674 }
675
676 /*Unlinks any IDProperty<->ID linkage that might be going on.
677   note: currently unused.*/
678 void IDP_UnlinkProperty(IDProperty *prop)
679 {
680         switch (prop->type) {
681                 case IDP_ID:
682                         IDP_UnlinkID(prop);
683         }
684 }