Logging: Use CLOG for blenkernel
[blender.git] / source / blender / blenkernel / intern / idprop.c
1 /*
2  * ***** BEGIN GPL LICENSE BLOCK *****
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software Foundation,
16  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17  *
18  * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
19  * All rights reserved.
20  *
21  * Contributor(s): Joseph Eagar
22  *
23  * ***** END GPL LICENSE BLOCK *****
24  */
25
26 /** \file blender/blenkernel/intern/idprop.c
27  *  \ingroup bke
28  */
29
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <stddef.h>
33 #include <string.h>
34
35 #include "BLI_utildefines.h"
36 #include "BLI_string.h"
37 #include "BLI_listbase.h"
38 #include "BLI_math.h"
39
40 #include "BKE_idprop.h"
41 #include "BKE_library.h"
42
43 #include "CLG_log.h"
44
45 #include "MEM_guardedalloc.h"
46
47 #include "BLI_strict_flags.h"
48
49 /* IDPropertyTemplate is a union in DNA_ID.h */
50
51 /**
52  * if the new is 'IDP_ARRAY_REALLOC_LIMIT' items less,
53  * than #IDProperty.totallen, reallocate anyway.
54  */
55 #define IDP_ARRAY_REALLOC_LIMIT 200
56
57 static CLG_LogRef LOG = {"bke.idprop"};
58
59 /*local size table.*/
60 static size_t idp_size_table[] = {
61         1, /*strings*/
62         sizeof(int),
63         sizeof(float),
64         sizeof(float) * 3, /*Vector type, deprecated*/
65         sizeof(float) * 16, /*Matrix type, deprecated*/
66         0, /*arrays don't have a fixed size*/
67         sizeof(ListBase), /*Group type*/
68         sizeof(void *),
69         sizeof(double),
70 };
71
72
73 /* -------------------------------------------------------------------- */
74 /* Array Functions */
75
76 /** \name IDP Array API
77  * \{ */
78
79 #define GETPROP(prop, i) &(IDP_IDPArray(prop)[i])
80
81 /* --------- property array type -------------*/
82
83 /**
84  * \note as a start to move away from the stupid IDP_New function, this type
85  * has it's own allocation function.
86  */
87 IDProperty *IDP_NewIDPArray(const char *name)
88 {
89         IDProperty *prop = MEM_callocN(sizeof(IDProperty), "IDProperty prop array");
90         prop->type = IDP_IDPARRAY;
91         prop->len = 0;
92         BLI_strncpy(prop->name, name, MAX_IDPROP_NAME);
93
94         return prop;
95 }
96
97 IDProperty *IDP_CopyIDPArray(const IDProperty *array, const int flag)
98 {
99         /* don't use MEM_dupallocN because this may be part of an array */
100         IDProperty *narray, *tmp;
101         int i;
102
103         BLI_assert(array->type == IDP_IDPARRAY);
104
105         narray = MEM_mallocN(sizeof(IDProperty), __func__);
106         *narray = *array;
107
108         narray->data.pointer = MEM_dupallocN(array->data.pointer);
109         for (i = 0; i < narray->len; i++) {
110                 /* ok, the copy functions always allocate a new structure,
111                  * which doesn't work here.  instead, simply copy the
112                  * contents of the new structure into the array cell,
113                  * then free it.  this makes for more maintainable
114                  * code than simply reimplementing the copy functions
115                  * in this loop.*/
116                 tmp = IDP_CopyProperty_ex(GETPROP(narray, i), flag);
117                 memcpy(GETPROP(narray, i), tmp, sizeof(IDProperty));
118                 MEM_freeN(tmp);
119         }
120
121         return narray;
122 }
123
124 static void IDP_FreeIDPArray(IDProperty *prop, const bool do_id_user)
125 {
126         int i;
127
128         BLI_assert(prop->type == IDP_IDPARRAY);
129
130         for (i = 0; i < prop->len; i++)
131                 IDP_FreeProperty_ex(GETPROP(prop, i), do_id_user);
132
133         if (prop->data.pointer)
134                 MEM_freeN(prop->data.pointer);
135 }
136
137 /* shallow copies item */
138 void IDP_SetIndexArray(IDProperty *prop, int index, IDProperty *item)
139 {
140         IDProperty *old;
141
142         BLI_assert(prop->type == IDP_IDPARRAY);
143
144         if (index >= prop->len || index < 0)
145                 return;
146
147         old = GETPROP(prop, index);
148         if (item != old) {
149                 IDP_FreeProperty(old);
150
151                 memcpy(old, item, sizeof(IDProperty));
152         }
153 }
154
155 IDProperty *IDP_GetIndexArray(IDProperty *prop, int index)
156 {
157         BLI_assert(prop->type == IDP_IDPARRAY);
158
159         return GETPROP(prop, index);
160 }
161
162 void IDP_AppendArray(IDProperty *prop, IDProperty *item)
163 {
164         BLI_assert(prop->type == IDP_IDPARRAY);
165
166         IDP_ResizeIDPArray(prop, prop->len + 1);
167         IDP_SetIndexArray(prop, prop->len - 1, item);
168 }
169
170 void IDP_ResizeIDPArray(IDProperty *prop, int newlen)
171 {
172         int newsize;
173
174         BLI_assert(prop->type == IDP_IDPARRAY);
175
176         /* first check if the array buffer size has room */
177         if (newlen <= prop->totallen) {
178                 if (newlen < prop->len && prop->totallen - newlen < IDP_ARRAY_REALLOC_LIMIT) {
179                         int i;
180
181                         for (i = newlen; i < prop->len; i++)
182                                 IDP_FreeProperty(GETPROP(prop, i));
183
184                         prop->len = newlen;
185                         return;
186                 }
187                 else if (newlen >= prop->len) {
188                         prop->len = newlen;
189                         return;
190                 }
191         }
192
193         /* free trailing items */
194         if (newlen < prop->len) {
195                 /* newlen is smaller */
196                 int i;
197                 for (i = newlen; i < prop->len; i++) {
198                         IDP_FreeProperty(GETPROP(prop, i));
199                 }
200         }
201
202         /* - Note: This code comes from python, here's the corresponding comment. - */
203         /* This over-allocates proportional to the list size, making room
204          * for additional growth.  The over-allocation is mild, but is
205          * enough to give linear-time amortized behavior over a long
206          * sequence of appends() in the presence of a poorly-performing
207          * system realloc().
208          * The growth pattern is:  0, 4, 8, 16, 25, 35, 46, 58, 72, 88, ...
209          */
210         newsize = newlen;
211         newsize = (newsize >> 3) + (newsize < 9 ? 3 : 6) + newsize;
212         prop->data.pointer = MEM_recallocN(prop->data.pointer, sizeof(IDProperty) * (size_t)newsize);
213         prop->len = newlen;
214         prop->totallen = newsize;
215 }
216
217 /* ----------- Numerical Array Type ----------- */
218 static void idp_resize_group_array(IDProperty *prop, int newlen, void *newarr)
219 {
220         if (prop->subtype != IDP_GROUP)
221                 return;
222
223         if (newlen >= prop->len) {
224                 /* bigger */
225                 IDProperty **array = newarr;
226                 IDPropertyTemplate val;
227                 int a;
228
229                 for (a = prop->len; a < newlen; a++) {
230                         val.i = 0; /* silence MSVC warning about uninitialized var when debugging */
231                         array[a] = IDP_New(IDP_GROUP, &val, "IDP_ResizeArray group");
232                 }
233         }
234         else {
235                 /* smaller */
236                 IDProperty **array = prop->data.pointer;
237                 int a;
238
239                 for (a = newlen; a < prop->len; a++) {
240                         IDP_FreeProperty(array[a]);
241                         MEM_freeN(array[a]);
242                 }
243         }
244 }
245
246 /*this function works for strings too!*/
247 void IDP_ResizeArray(IDProperty *prop, int newlen)
248 {
249         int newsize;
250         const bool is_grow = newlen >= prop->len;
251
252         /* first check if the array buffer size has room */
253         if (newlen <= prop->totallen && prop->totallen - newlen < IDP_ARRAY_REALLOC_LIMIT) {
254                 idp_resize_group_array(prop, newlen, prop->data.pointer);
255                 prop->len = newlen;
256                 return;
257         }
258
259         /* - Note: This code comes from python, here's the corresponding comment. - */
260         /* This over-allocates proportional to the list size, making room
261          * for additional growth.  The over-allocation is mild, but is
262          * enough to give linear-time amortized behavior over a long
263          * sequence of appends() in the presence of a poorly-performing
264          * system realloc().
265          * The growth pattern is:  0, 4, 8, 16, 25, 35, 46, 58, 72, 88, ...
266          */
267         newsize = newlen;
268         newsize = (newsize >> 3) + (newsize < 9 ? 3 : 6) + newsize;
269
270         if (is_grow == false)
271                 idp_resize_group_array(prop, newlen, prop->data.pointer);
272
273         prop->data.pointer = MEM_recallocN(
274                 prop->data.pointer, idp_size_table[(int)prop->subtype] * (size_t)newsize);
275
276         if (is_grow == true)
277                 idp_resize_group_array(prop, newlen, prop->data.pointer);
278
279         prop->len = newlen;
280         prop->totallen = newsize;
281 }
282
283 void IDP_FreeArray(IDProperty *prop)
284 {
285         if (prop->data.pointer) {
286                 idp_resize_group_array(prop, 0, NULL);
287                 MEM_freeN(prop->data.pointer);
288         }
289 }
290
291
292 static IDProperty *idp_generic_copy(const IDProperty *prop, const int UNUSED(flag))
293 {
294         IDProperty *newp = MEM_callocN(sizeof(IDProperty), __func__);
295
296         BLI_strncpy(newp->name, prop->name, MAX_IDPROP_NAME);
297         newp->type = prop->type;
298         newp->flag = prop->flag;
299         newp->data.val = prop->data.val;
300         newp->data.val2 = prop->data.val2;
301
302         return newp;
303 }
304
305 static IDProperty *IDP_CopyArray(const IDProperty *prop, const int flag)
306 {
307         IDProperty *newp = idp_generic_copy(prop, flag);
308
309         if (prop->data.pointer) {
310                 newp->data.pointer = MEM_dupallocN(prop->data.pointer);
311
312                 if (prop->type == IDP_GROUP) {
313                         IDProperty **array = newp->data.pointer;
314                         int a;
315
316                         for (a = 0; a < prop->len; a++)
317                                 array[a] = IDP_CopyProperty_ex(array[a], flag);
318                 }
319         }
320         newp->len = prop->len;
321         newp->subtype = prop->subtype;
322         newp->totallen = prop->totallen;
323
324         return newp;
325 }
326 /** \} */
327
328
329 /* -------------------------------------------------------------------- */
330 /* String Functions */
331
332 /** \name IDProperty String API
333  * \{ */
334
335 /**
336  *
337  * \param st: The string to assign.
338  * \param name: The property name.
339  * \param maxlen: The size of the new string (including the \0 terminator).
340  * \return The new string property.
341  */
342 IDProperty *IDP_NewString(const char *st, const char *name, int maxlen)
343 {
344         IDProperty *prop = MEM_callocN(sizeof(IDProperty), "IDProperty string");
345
346         if (st == NULL) {
347                 prop->data.pointer = MEM_mallocN(DEFAULT_ALLOC_FOR_NULL_STRINGS, "id property string 1");
348                 *IDP_String(prop) = '\0';
349                 prop->totallen = DEFAULT_ALLOC_FOR_NULL_STRINGS;
350                 prop->len = 1;  /* NULL string, has len of 1 to account for null byte. */
351         }
352         else {
353                 /* include null terminator '\0' */
354                 int stlen = (int)strlen(st) + 1;
355
356                 if (maxlen > 0 && maxlen < stlen)
357                         stlen = maxlen;
358
359                 prop->data.pointer = MEM_mallocN((size_t)stlen, "id property string 2");
360                 prop->len = prop->totallen = stlen;
361                 BLI_strncpy(prop->data.pointer, st, (size_t)stlen);
362         }
363
364         prop->type = IDP_STRING;
365         BLI_strncpy(prop->name, name, MAX_IDPROP_NAME);
366
367         return prop;
368 }
369
370 static IDProperty *IDP_CopyString(const IDProperty *prop, const int flag)
371 {
372         IDProperty *newp;
373
374         BLI_assert(prop->type == IDP_STRING);
375         newp = idp_generic_copy(prop, flag);
376
377         if (prop->data.pointer)
378                 newp->data.pointer = MEM_dupallocN(prop->data.pointer);
379         newp->len = prop->len;
380         newp->subtype = prop->subtype;
381         newp->totallen = prop->totallen;
382
383         return newp;
384 }
385
386
387 void IDP_AssignString(IDProperty *prop, const char *st, int maxlen)
388 {
389         int stlen;
390
391         BLI_assert(prop->type == IDP_STRING);
392         stlen = (int)strlen(st);
393         if (maxlen > 0 && maxlen < stlen)
394                 stlen = maxlen;
395
396         if (prop->subtype == IDP_STRING_SUB_BYTE) {
397                 IDP_ResizeArray(prop, stlen);
398                 memcpy(prop->data.pointer, st, (size_t)stlen);
399         }
400         else {
401                 stlen++;
402                 IDP_ResizeArray(prop, stlen);
403                 BLI_strncpy(prop->data.pointer, st, (size_t)stlen);
404         }
405 }
406
407 void IDP_ConcatStringC(IDProperty *prop, const char *st)
408 {
409         int newlen;
410
411         BLI_assert(prop->type == IDP_STRING);
412
413         newlen = prop->len + (int)strlen(st);
414         /* we have to remember that prop->len includes the null byte for strings.
415          * so there's no need to add +1 to the resize function.*/
416         IDP_ResizeArray(prop, newlen);
417         strcat(prop->data.pointer, st);
418 }
419
420 void IDP_ConcatString(IDProperty *str1, IDProperty *append)
421 {
422         int newlen;
423
424         BLI_assert(append->type == IDP_STRING);
425
426         /* since ->len for strings includes the NULL byte, we have to subtract one or
427          * we'll get an extra null byte after each concatenation operation.*/
428         newlen = str1->len + append->len - 1;
429         IDP_ResizeArray(str1, newlen);
430         strcat(str1->data.pointer, append->data.pointer);
431 }
432
433 void IDP_FreeString(IDProperty *prop)
434 {
435         BLI_assert(prop->type == IDP_STRING);
436
437         if (prop->data.pointer)
438                 MEM_freeN(prop->data.pointer);
439 }
440 /** \} */
441
442
443 /* -------------------------------------------------------------------- */
444 /* ID Type */
445
446 /** \name IDProperty ID API
447  * \{ */
448
449 static IDProperty *IDP_CopyID(const IDProperty *prop, const int flag)
450 {
451         IDProperty *newp;
452
453         BLI_assert(prop->type == IDP_ID);
454         newp = idp_generic_copy(prop, flag);
455
456         newp->data.pointer = prop->data.pointer;
457         if ((flag & LIB_ID_CREATE_NO_USER_REFCOUNT) == 0) {
458                 id_us_plus(IDP_Id(newp));
459         }
460
461         return newp;
462 }
463
464 /** \} */
465
466
467 /* -------------------------------------------------------------------- */
468 /* Group Functions */
469
470 /** \name IDProperty Group API
471  * \{ */
472
473 /**
474  * Checks if a property with the same name as prop exists, and if so replaces it.
475  */
476 static IDProperty *IDP_CopyGroup(const IDProperty *prop, const int flag)
477 {
478         IDProperty *newp, *link;
479
480         BLI_assert(prop->type == IDP_GROUP);
481         newp = idp_generic_copy(prop, flag);
482         newp->len = prop->len;
483         newp->subtype = prop->subtype;
484
485         for (link = prop->data.group.first; link; link = link->next) {
486                 BLI_addtail(&newp->data.group, IDP_CopyProperty_ex(link, flag));
487         }
488
489         return newp;
490 }
491
492 /* use for syncing proxies.
493  * When values name and types match, copy the values, else ignore */
494 void IDP_SyncGroupValues(IDProperty *dest, const IDProperty *src)
495 {
496         IDProperty *other, *prop;
497
498         BLI_assert(dest->type == IDP_GROUP);
499         BLI_assert(src->type == IDP_GROUP);
500
501         for (prop = src->data.group.first; prop; prop = prop->next) {
502                 other = BLI_findstring(&dest->data.group, prop->name, offsetof(IDProperty, name));
503                 if (other && prop->type == other->type) {
504                         switch (prop->type) {
505                                 case IDP_INT:
506                                 case IDP_FLOAT:
507                                 case IDP_DOUBLE:
508                                         other->data = prop->data;
509                                         break;
510                                 case IDP_GROUP:
511                                         IDP_SyncGroupValues(other, prop);
512                                         break;
513                                 default:
514                                 {
515                                         BLI_insertlinkreplace(&dest->data.group, other, IDP_CopyProperty(prop));
516                                         IDP_FreeProperty(other);
517                                         MEM_freeN(other);
518                                         break;
519                                 }
520                         }
521                 }
522         }
523 }
524
525 void IDP_SyncGroupTypes(IDProperty *dst, const IDProperty *src, const bool do_arraylen)
526 {
527         IDProperty *prop_dst, *prop_dst_next;
528         const IDProperty *prop_src;
529
530         for (prop_dst = dst->data.group.first; prop_dst; prop_dst = prop_dst_next) {
531                 prop_dst_next = prop_dst->next;
532                 if ((prop_src = IDP_GetPropertyFromGroup((IDProperty *)src, prop_dst->name))) {
533                         /* check of we should replace? */
534                         if ((prop_dst->type != prop_src->type || prop_dst->subtype != prop_src->subtype) ||
535                             (do_arraylen && ELEM(prop_dst->type, IDP_ARRAY, IDP_IDPARRAY) && (prop_src->len != prop_dst->len)))
536                         {
537                                 BLI_insertlinkreplace(&dst->data.group, prop_dst, IDP_CopyProperty(prop_src));
538                                 IDP_FreeProperty(prop_dst);
539                                 MEM_freeN(prop_dst);
540                         }
541                         else if (prop_dst->type == IDP_GROUP) {
542                                 IDP_SyncGroupTypes(prop_dst, prop_src, do_arraylen);
543                         }
544                 }
545                 else {
546                         IDP_FreeFromGroup(dst, prop_dst);
547                 }
548         }
549 }
550
551 /**
552  * Replaces all properties with the same name in a destination group from a source group.
553  */
554 void IDP_ReplaceGroupInGroup(IDProperty *dest, const IDProperty *src)
555 {
556         IDProperty *loop, *prop;
557
558         BLI_assert(dest->type == IDP_GROUP);
559         BLI_assert(src->type == IDP_GROUP);
560
561         for (prop = src->data.group.first; prop; prop = prop->next) {
562                 for (loop = dest->data.group.first; loop; loop = loop->next) {
563                         if (STREQ(loop->name, prop->name)) {
564                                 BLI_insertlinkreplace(&dest->data.group, loop, IDP_CopyProperty(prop));
565                                 IDP_FreeProperty(loop);
566                                 MEM_freeN(loop);
567                                 break;
568                         }
569                 }
570
571                 /* only add at end if not added yet */
572                 if (loop == NULL) {
573                         IDProperty *copy = IDP_CopyProperty(prop);
574                         dest->len++;
575                         BLI_addtail(&dest->data.group, copy);
576                 }
577         }
578 }
579
580 /**
581  * Checks if a property with the same name as prop exists, and if so replaces it.
582  * Use this to preserve order!
583  */
584 void IDP_ReplaceInGroup_ex(IDProperty *group, IDProperty *prop, IDProperty *prop_exist)
585 {
586         BLI_assert(group->type == IDP_GROUP);
587         BLI_assert(prop_exist == IDP_GetPropertyFromGroup(group, prop->name));
588
589         if (prop_exist != NULL) {
590                 BLI_insertlinkreplace(&group->data.group, prop_exist, prop);
591                 IDP_FreeProperty(prop_exist);
592                 MEM_freeN(prop_exist);
593         }
594         else {
595                 group->len++;
596                 BLI_addtail(&group->data.group, prop);
597         }
598 }
599
600 void IDP_ReplaceInGroup(IDProperty *group, IDProperty *prop)
601 {
602         IDProperty *prop_exist = IDP_GetPropertyFromGroup(group, prop->name);
603
604         IDP_ReplaceInGroup_ex(group, prop, prop_exist);
605 }
606
607 /**
608  * If a property is missing in \a dest, add it.
609  * Do it recursively.
610  */
611 void IDP_MergeGroup_ex(IDProperty *dest, const IDProperty *src, const bool do_overwrite, const int flag)
612 {
613         IDProperty *prop;
614
615         BLI_assert(dest->type == IDP_GROUP);
616         BLI_assert(src->type == IDP_GROUP);
617
618         if (do_overwrite) {
619                 for (prop = src->data.group.first; prop; prop = prop->next) {
620                         if (prop->type == IDP_GROUP) {
621                                 IDProperty *prop_exist = IDP_GetPropertyFromGroup(dest, prop->name);
622
623                                 if (prop_exist != NULL) {
624                                         IDP_MergeGroup_ex(prop_exist, prop, do_overwrite, flag);
625                                         continue;
626                                 }
627                         }
628
629                         IDProperty *copy = IDP_CopyProperty_ex(prop, flag);
630                         IDP_ReplaceInGroup(dest, copy);
631                 }
632         }
633         else {
634                 for (prop = src->data.group.first; prop; prop = prop->next) {
635                         IDProperty *prop_exist = IDP_GetPropertyFromGroup(dest, prop->name);
636                         if (prop_exist != NULL) {
637                                 if (prop->type == IDP_GROUP) {
638                                         IDP_MergeGroup_ex(prop_exist, prop, do_overwrite, flag);
639                                         continue;
640                                 }
641                         }
642                         else {
643                                 IDProperty *copy = IDP_CopyProperty_ex(prop, flag);
644                                 dest->len++;
645                                 BLI_addtail(&dest->data.group, copy);
646                         }
647                 }
648         }
649 }
650
651 /**
652  * If a property is missing in \a dest, add it.
653  * Do it recursively.
654  */
655 void IDP_MergeGroup(IDProperty *dest, const IDProperty *src, const bool do_overwrite)
656 {
657         IDP_MergeGroup_ex(dest, src, do_overwrite, 0);
658 }
659
660 /**
661  * This function has a sanity check to make sure ID properties with the same name don't
662  * get added to the group.
663  *
664  * The sanity check just means the property is not added to the group if another property
665  * exists with the same name; the client code using ID properties then needs to detect this
666  * (the function that adds new properties to groups, IDP_AddToGroup, returns false if a property can't
667  * be added to the group, and true if it can) and free the property.
668  *
669  * Currently the code to free ID properties is designed to leave the actual struct
670  * you pass it un-freed, this is needed for how the system works.  This means
671  * to free an ID property, you first call IDP_FreeProperty then MEM_freeN the
672  * struct.  In the future this will just be IDP_FreeProperty and the code will
673  * be reorganized to work properly.
674  */
675 bool IDP_AddToGroup(IDProperty *group, IDProperty *prop)
676 {
677         BLI_assert(group->type == IDP_GROUP);
678
679         if (IDP_GetPropertyFromGroup(group, prop->name) == NULL) {
680                 group->len++;
681                 BLI_addtail(&group->data.group, prop);
682                 return true;
683         }
684
685         return false;
686 }
687
688 /**
689  * This is the same as IDP_AddToGroup, only you pass an item
690  * in the group list to be inserted after.
691  */
692 bool IDP_InsertToGroup(IDProperty *group, IDProperty *previous, IDProperty *pnew)
693 {
694         BLI_assert(group->type == IDP_GROUP);
695
696         if (IDP_GetPropertyFromGroup(group, pnew->name) == NULL) {
697                 group->len++;
698                 BLI_insertlinkafter(&group->data.group, previous, pnew);
699                 return true;
700         }
701
702         return false;
703 }
704
705 /**
706  * \note this does not free the property!!
707  *
708  * To free the property, you have to do:
709  * IDP_FreeProperty(prop); //free all subdata
710  * MEM_freeN(prop); //free property struct itself
711  */
712 void IDP_RemoveFromGroup(IDProperty *group, IDProperty *prop)
713 {
714         BLI_assert(group->type == IDP_GROUP);
715
716         group->len--;
717         BLI_remlink(&group->data.group, prop);
718 }
719
720 /**
721  * Removes the property from the group and frees it.
722  */
723 void IDP_FreeFromGroup(IDProperty *group, IDProperty *prop)
724 {
725         IDP_RemoveFromGroup(group, prop);
726         IDP_FreeProperty(prop);
727         MEM_freeN(prop);
728 }
729
730 IDProperty *IDP_GetPropertyFromGroup(IDProperty *prop, const char *name)
731 {
732         BLI_assert(prop->type == IDP_GROUP);
733
734         return (IDProperty *)BLI_findstring(&prop->data.group, name, offsetof(IDProperty, name));
735 }
736 /** same as above but ensure type match */
737 IDProperty *IDP_GetPropertyTypeFromGroup(IDProperty *prop, const char *name, const char type)
738 {
739         IDProperty *idprop = IDP_GetPropertyFromGroup(prop, name);
740         return (idprop && idprop->type == type) ? idprop : NULL;
741 }
742
743 /* Ok, the way things work, Groups free the ID Property structs of their children.
744  * This is because all ID Property freeing functions free only direct data (not the ID Property
745  * struct itself), but for Groups the child properties *are* considered
746  * direct data. */
747 static void IDP_FreeGroup(IDProperty *prop, const bool do_id_user)
748 {
749         IDProperty *loop;
750
751         BLI_assert(prop->type == IDP_GROUP);
752         for (loop = prop->data.group.first; loop; loop = loop->next) {
753                 IDP_FreeProperty_ex(loop, do_id_user);
754         }
755         BLI_freelistN(&prop->data.group);
756 }
757 /** \} */
758
759
760 /* -------------------------------------------------------------------- */
761 /* Main Functions */
762
763 /** \name IDProperty Main API
764  * \{ */
765 IDProperty *IDP_CopyProperty_ex(const IDProperty *prop, const int flag)
766 {
767         switch (prop->type) {
768                 case IDP_GROUP: return IDP_CopyGroup(prop, flag);
769                 case IDP_STRING: return IDP_CopyString(prop, flag);
770                 case IDP_ID: return IDP_CopyID(prop, flag);
771                 case IDP_ARRAY: return IDP_CopyArray(prop, flag);
772                 case IDP_IDPARRAY: return IDP_CopyIDPArray(prop, flag);
773                 default: return idp_generic_copy(prop, flag);
774         }
775 }
776
777 IDProperty *IDP_CopyProperty(const IDProperty *prop)
778 {
779         return IDP_CopyProperty_ex(prop, 0);
780 }
781
782 /* Updates ID pointers after an object has been copied */
783 /* TODO Nuke this once its only user has been correctly converted to use generic ID management from BKE_library! */
784 void IDP_RelinkProperty(struct IDProperty *prop)
785 {
786         if (!prop)
787                 return;
788
789         switch (prop->type) {
790                 case IDP_GROUP:
791                 {
792                         for (IDProperty *loop = prop->data.group.first; loop; loop = loop->next) {
793                                 IDP_RelinkProperty(loop);
794                         }
795                         break;
796                 }
797                 case IDP_IDPARRAY:
798                 {
799                         IDProperty *idp_array = IDP_Array(prop);
800                         for (int i = 0; i < prop->len; i++) {
801                                 IDP_RelinkProperty(&idp_array[i]);
802                         }
803                         break;
804                 }
805                 case IDP_ID:
806                 {
807                         ID *id = IDP_Id(prop);
808                         if (id && id->newid) {
809                                 id_us_min(IDP_Id(prop));
810                                 prop->data.pointer = id->newid;
811                                 id_us_plus(IDP_Id(prop));
812                         }
813                         break;
814                 }
815                 default:
816                         break;  /* Nothing to do for other IDProp types. */
817         }
818 }
819
820 /**
821  * Get the Group property that contains the id properties for ID id.  Set create_if_needed
822  * to create the Group property and attach it to id if it doesn't exist; otherwise
823  * the function will return NULL if there's no Group property attached to the ID.
824  */
825 IDProperty *IDP_GetProperties(ID *id, const bool create_if_needed)
826 {
827         if (id->properties) {
828                 return id->properties;
829         }
830         else {
831                 if (create_if_needed) {
832                         id->properties = MEM_callocN(sizeof(IDProperty), "IDProperty");
833                         id->properties->type = IDP_GROUP;
834                         /* don't overwrite the data's name and type
835                          * some functions might need this if they
836                          * don't have a real ID, should be named elsewhere - Campbell */
837                         /* strcpy(id->name, "top_level_group");*/
838                 }
839                 return id->properties;
840         }
841 }
842
843 /**
844  * \param is_strict: When false treat missing items as a match */
845 bool IDP_EqualsProperties_ex(IDProperty *prop1, IDProperty *prop2, const bool is_strict)
846 {
847         if (prop1 == NULL && prop2 == NULL)
848                 return true;
849         else if (prop1 == NULL || prop2 == NULL)
850                 return is_strict ? false : true;
851         else if (prop1->type != prop2->type)
852                 return false;
853
854         switch (prop1->type) {
855                 case IDP_INT:
856                         return (IDP_Int(prop1) == IDP_Int(prop2));
857                 case IDP_FLOAT:
858 #if !defined(NDEBUG) && defined(WITH_PYTHON)
859                         {
860                                 float p1 = IDP_Float(prop1);
861                                 float p2 = IDP_Float(prop2);
862                                 if ((p1 != p2) && ((fabsf(p1 - p2) / max_ff(p1, p2)) < 0.001f)) {
863                                         printf("WARNING: Comparing two float properties that have nearly the same value (%f vs. %f)\n", p1, p2);
864                                         printf("    p1: ");
865                                         IDP_print(prop1);
866                                         printf("    p2: ");
867                                         IDP_print(prop2);
868                                 }
869                         }
870 #endif
871                         return (IDP_Float(prop1) == IDP_Float(prop2));
872                 case IDP_DOUBLE:
873                         return (IDP_Double(prop1) == IDP_Double(prop2));
874                 case IDP_STRING:
875                 {
876                         return (((prop1->len == prop2->len) &&
877                                  STREQLEN(IDP_String(prop1), IDP_String(prop2), (size_t)prop1->len)));
878                 }
879                 case IDP_ARRAY:
880                         if (prop1->len == prop2->len && prop1->subtype == prop2->subtype) {
881                                 return (memcmp(IDP_Array(prop1),
882                                                IDP_Array(prop2),
883                                                idp_size_table[(int)prop1->subtype] * (size_t)prop1->len) == 0);
884                         }
885                         return false;
886                 case IDP_GROUP:
887                 {
888                         IDProperty *link1, *link2;
889
890                         if (is_strict && prop1->len != prop2->len)
891                                 return false;
892
893                         for (link1 = prop1->data.group.first; link1; link1 = link1->next) {
894                                 link2 = IDP_GetPropertyFromGroup(prop2, link1->name);
895
896                                 if (!IDP_EqualsProperties_ex(link1, link2, is_strict))
897                                         return false;
898                         }
899
900                         return true;
901                 }
902                 case IDP_IDPARRAY:
903                 {
904                         IDProperty *array1 = IDP_IDPArray(prop1);
905                         IDProperty *array2 = IDP_IDPArray(prop2);
906                         int i;
907
908                         if (prop1->len != prop2->len)
909                                 return false;
910
911                         for (i = 0; i < prop1->len; i++) {
912                                 if (!IDP_EqualsProperties_ex(&array1[i], &array2[i], is_strict))
913                                         return false;
914                         }
915                         return true;
916                 }
917                 case IDP_ID:
918                         return (IDP_Id(prop1) == IDP_Id(prop2));
919                 default:
920                         BLI_assert(0);
921                         break;
922         }
923
924         return true;
925 }
926
927 bool IDP_EqualsProperties(IDProperty *prop1, IDProperty *prop2)
928 {
929         return IDP_EqualsProperties_ex(prop1, prop2, true);
930 }
931
932 /**
933  * Allocate a new ID.
934  *
935  * This function takes three arguments: the ID property type, a union which defines
936  * it's initial value, and a name.
937  *
938  * The union is simple to use; see the top of this header file for its definition.
939  * An example of using this function:
940  *
941  * \code{.c}
942  * IDPropertyTemplate val;
943  * IDProperty *group, *idgroup, *color;
944  * group = IDP_New(IDP_GROUP, val, "group1"); //groups don't need a template.
945  *
946  * val.array.len = 4
947  * val.array.type = IDP_FLOAT;
948  * color = IDP_New(IDP_ARRAY, val, "color1");
949  *
950  * idgroup = IDP_GetProperties(some_id, 1);
951  * IDP_AddToGroup(idgroup, color);
952  * IDP_AddToGroup(idgroup, group);
953  * \endcode
954  *
955  * Note that you MUST either attach the id property to an id property group with
956  * IDP_AddToGroup or MEM_freeN the property, doing anything else might result in
957  * a memory leak.
958  */
959 IDProperty *IDP_New(const char type, const IDPropertyTemplate *val, const char *name)
960 {
961         IDProperty *prop = NULL;
962
963         switch (type) {
964                 case IDP_INT:
965                         prop = MEM_callocN(sizeof(IDProperty), "IDProperty int");
966                         prop->data.val = val->i;
967                         break;
968                 case IDP_FLOAT:
969                         prop = MEM_callocN(sizeof(IDProperty), "IDProperty float");
970                         *(float *)&prop->data.val = val->f;
971                         break;
972                 case IDP_DOUBLE:
973                         prop = MEM_callocN(sizeof(IDProperty), "IDProperty double");
974                         *(double *)&prop->data.val = val->d;
975                         break;
976                 case IDP_ARRAY:
977                 {
978                         /* for now, we only support float and int and double arrays */
979                         if ( (val->array.type == IDP_FLOAT) ||
980                              (val->array.type == IDP_INT) ||
981                              (val->array.type == IDP_DOUBLE) ||
982                              (val->array.type == IDP_GROUP) )
983                         {
984                                 prop = MEM_callocN(sizeof(IDProperty), "IDProperty array");
985                                 prop->subtype = val->array.type;
986                                 if (val->array.len) {
987                                         prop->data.pointer = MEM_callocN(
988                                                 idp_size_table[val->array.type] * (size_t)val->array.len, "id property array");
989                                 }
990                                 prop->len = prop->totallen = val->array.len;
991                                 break;
992                         }
993                         CLOG_ERROR(&LOG, "bad array type.");
994                         return NULL;
995                 }
996                 case IDP_STRING:
997                 {
998                         const char *st = val->string.str;
999
1000                         prop = MEM_callocN(sizeof(IDProperty), "IDProperty string");
1001                         if (val->string.subtype == IDP_STRING_SUB_BYTE) {
1002                                 /* note, intentionally not null terminated */
1003                                 if (st == NULL) {
1004                                         prop->data.pointer = MEM_mallocN(DEFAULT_ALLOC_FOR_NULL_STRINGS, "id property string 1");
1005                                         *IDP_String(prop) = '\0';
1006                                         prop->totallen = DEFAULT_ALLOC_FOR_NULL_STRINGS;
1007                                         prop->len = 0;
1008                                 }
1009                                 else {
1010                                         prop->data.pointer = MEM_mallocN((size_t)val->string.len, "id property string 2");
1011                                         prop->len = prop->totallen = val->string.len;
1012                                         memcpy(prop->data.pointer, st, (size_t)val->string.len);
1013                                 }
1014                                 prop->subtype = IDP_STRING_SUB_BYTE;
1015                         }
1016                         else {
1017                                 if (st == NULL || val->string.len <= 1) {
1018                                         prop->data.pointer = MEM_mallocN(DEFAULT_ALLOC_FOR_NULL_STRINGS, "id property string 1");
1019                                         *IDP_String(prop) = '\0';
1020                                         prop->totallen = DEFAULT_ALLOC_FOR_NULL_STRINGS;
1021                                         /* NULL string, has len of 1 to account for null byte. */
1022                                         prop->len = 1;
1023                                 }
1024                                 else {
1025                                         BLI_assert((int)val->string.len <= (int)strlen(st) + 1);
1026                                         prop->data.pointer = MEM_mallocN((size_t)val->string.len, "id property string 3");
1027                                         memcpy(prop->data.pointer, st, (size_t)val->string.len - 1);
1028                                         IDP_String(prop)[val->string.len - 1] = '\0';
1029                                         prop->len = prop->totallen = val->string.len;
1030                                 }
1031                                 prop->subtype = IDP_STRING_SUB_UTF8;
1032                         }
1033                         break;
1034                 }
1035                 case IDP_GROUP:
1036                 {
1037                         /* Values are set properly by calloc. */
1038                         prop = MEM_callocN(sizeof(IDProperty), "IDProperty group");
1039                         break;
1040                 }
1041                 case IDP_ID:
1042                 {
1043                         prop = MEM_callocN(sizeof(IDProperty), "IDProperty datablock");
1044                         prop->data.pointer = (void *)val->id;
1045                         prop->type = IDP_ID;
1046                         id_us_plus(IDP_Id(prop));
1047                         break;
1048                 }
1049                 default:
1050                 {
1051                         prop = MEM_callocN(sizeof(IDProperty), "IDProperty array");
1052                         break;
1053                 }
1054         }
1055
1056         prop->type = type;
1057         BLI_strncpy(prop->name, name, MAX_IDPROP_NAME);
1058
1059         return prop;
1060 }
1061
1062 /**
1063  * \note This will free allocated data, all child properties of arrays and groups, and unlink IDs!
1064  * But it does not free the actual IDProperty struct itself.
1065  */
1066 void IDP_FreeProperty_ex(IDProperty *prop, const bool do_id_user)
1067 {
1068         switch (prop->type) {
1069                 case IDP_ARRAY:
1070                         IDP_FreeArray(prop);
1071                         break;
1072                 case IDP_STRING:
1073                         IDP_FreeString(prop);
1074                         break;
1075                 case IDP_GROUP:
1076                         IDP_FreeGroup(prop, do_id_user);
1077                         break;
1078                 case IDP_IDPARRAY:
1079                         IDP_FreeIDPArray(prop, do_id_user);
1080                         break;
1081                 case IDP_ID:
1082                         if (do_id_user) {
1083                                 id_us_min(IDP_Id(prop));
1084                         }
1085                         break;
1086         }
1087 }
1088
1089 void IDP_FreeProperty(IDProperty *prop)
1090 {
1091         IDP_FreeProperty_ex(prop, true);
1092 }
1093
1094 void IDP_ClearProperty(IDProperty *prop)
1095 {
1096         IDP_FreeProperty(prop);
1097         prop->data.pointer = NULL;
1098         prop->len = prop->totallen = 0;
1099 }
1100
1101 void IDP_Reset(IDProperty *prop, const IDProperty *reference)
1102 {
1103         if (prop == NULL) {
1104                 return;
1105         }
1106         IDP_ClearProperty(prop);
1107         if (reference != NULL) {
1108                 IDP_MergeGroup(prop, reference, true);
1109         }
1110 }
1111
1112 /** \} */