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