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