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