comparing keymaps was too sloppy or too strict, now sloppy keymap comparison works by setting all the operator properties to their default values if they are not already set, then compare this with the keymap item (ignoring values missing from either one). ... this way any non default keymap setting wont match with an operator menu item which doesnt set this operator at all (a problem sighted in this bug report). developer notes: - IDP_EqualsProperties_ex() function adds an argument to treat missing members of either group to act as if there is a match. - WM_operator_properties_default() function to reset RNA values to their defaults. - add IDP_spit(), debug only function to print out ID properties.
827 lines
21 KiB
C
827 lines
21 KiB
C
/*
|
|
* ***** BEGIN GPL LICENSE BLOCK *****
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License
|
|
* as published by the Free Software Foundation; either version 2
|
|
* of the License, or (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software Foundation,
|
|
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
*
|
|
* The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
|
|
* All rights reserved.
|
|
*
|
|
* Contributor(s): Joseph Eagar
|
|
*
|
|
* ***** END GPL LICENSE BLOCK *****
|
|
*/
|
|
|
|
/** \file blender/blenkernel/intern/idprop.c
|
|
* \ingroup bke
|
|
*/
|
|
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <stddef.h>
|
|
#include <string.h>
|
|
|
|
#include "BLI_utildefines.h"
|
|
#include "BLI_string.h"
|
|
#include "BLI_listbase.h"
|
|
|
|
#include "BKE_idprop.h"
|
|
#include "BKE_library.h"
|
|
|
|
#include "MEM_guardedalloc.h"
|
|
|
|
/* IDPropertyTemplate is a union in DNA_ID.h */
|
|
|
|
/*local size table.*/
|
|
static char idp_size_table[] = {
|
|
1, /*strings*/
|
|
sizeof(int),
|
|
sizeof(float),
|
|
sizeof(float) * 3, /*Vector type, deprecated*/
|
|
sizeof(float) * 16, /*Matrix type, deprecated*/
|
|
0, /*arrays don't have a fixed size*/
|
|
sizeof(ListBase), /*Group type*/
|
|
sizeof(void *),
|
|
sizeof(double)
|
|
};
|
|
|
|
/* ------------Property Array Type ----------- */
|
|
#define GETPROP(prop, i) (((IDProperty *)(prop)->data.pointer) + (i))
|
|
|
|
/* --------- property array type -------------*/
|
|
|
|
/* note: as a start to move away from the stupid IDP_New function, this type
|
|
* has it's own allocation function.*/
|
|
IDProperty *IDP_NewIDPArray(const char *name)
|
|
{
|
|
IDProperty *prop = MEM_callocN(sizeof(IDProperty), "IDProperty prop array");
|
|
prop->type = IDP_IDPARRAY;
|
|
prop->len = 0;
|
|
BLI_strncpy(prop->name, name, MAX_IDPROP_NAME);
|
|
|
|
return prop;
|
|
}
|
|
|
|
IDProperty *IDP_CopyIDPArray(IDProperty *array)
|
|
{
|
|
/* don't use MEM_dupallocN because this may be part of an array */
|
|
IDProperty *narray = MEM_mallocN(sizeof(IDProperty), "IDP_CopyIDPArray"), *tmp;
|
|
int i;
|
|
|
|
*narray = *array;
|
|
|
|
narray->data.pointer = MEM_dupallocN(array->data.pointer);
|
|
for (i = 0; i < narray->len; i++) {
|
|
/* ok, the copy functions always allocate a new structure,
|
|
* which doesn't work here. instead, simply copy the
|
|
* contents of the new structure into the array cell,
|
|
* then free it. this makes for more maintainable
|
|
* code than simply reimplementing the copy functions
|
|
* in this loop.*/
|
|
tmp = IDP_CopyProperty(GETPROP(narray, i));
|
|
memcpy(GETPROP(narray, i), tmp, sizeof(IDProperty));
|
|
MEM_freeN(tmp);
|
|
}
|
|
|
|
return narray;
|
|
}
|
|
|
|
void IDP_FreeIDPArray(IDProperty *prop)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < prop->len; i++)
|
|
IDP_FreeProperty(GETPROP(prop, i));
|
|
|
|
if (prop->data.pointer)
|
|
MEM_freeN(prop->data.pointer);
|
|
}
|
|
|
|
/*shallow copies item*/
|
|
void IDP_SetIndexArray(IDProperty *prop, int index, IDProperty *item)
|
|
{
|
|
IDProperty *old = GETPROP(prop, index);
|
|
if (index >= prop->len || index < 0) return;
|
|
if (item != old) IDP_FreeProperty(old);
|
|
|
|
memcpy(GETPROP(prop, index), item, sizeof(IDProperty));
|
|
}
|
|
|
|
IDProperty *IDP_GetIndexArray(IDProperty *prop, int index)
|
|
{
|
|
return GETPROP(prop, index);
|
|
}
|
|
|
|
void IDP_AppendArray(IDProperty *prop, IDProperty *item)
|
|
{
|
|
IDP_ResizeIDPArray(prop, prop->len + 1);
|
|
IDP_SetIndexArray(prop, prop->len - 1, item);
|
|
}
|
|
|
|
void IDP_ResizeIDPArray(IDProperty *prop, int newlen)
|
|
{
|
|
void *newarr;
|
|
int newsize = newlen;
|
|
|
|
/*first check if the array buffer size has room*/
|
|
/*if newlen is 200 chars less then totallen, reallocate anyway*/
|
|
if (newlen <= prop->totallen && prop->totallen - newlen < 200) {
|
|
int i;
|
|
|
|
for (i = newlen; i < prop->len; i++)
|
|
IDP_FreeProperty(GETPROP(prop, i));
|
|
|
|
prop->len = newlen;
|
|
return;
|
|
}
|
|
|
|
/* - Note: This code comes from python, here's the corresponding comment. - */
|
|
/* This over-allocates proportional to the list size, making room
|
|
* for additional growth. The over-allocation is mild, but is
|
|
* enough to give linear-time amortized behavior over a long
|
|
* sequence of appends() in the presence of a poorly-performing
|
|
* system realloc().
|
|
* The growth pattern is: 0, 4, 8, 16, 25, 35, 46, 58, 72, 88, ...
|
|
*/
|
|
newsize = (newsize >> 3) + (newsize < 9 ? 3 : 6) + newsize;
|
|
|
|
newarr = MEM_callocN(sizeof(IDProperty) * newsize, "idproperty array resized");
|
|
if (newlen >= prop->len) {
|
|
/* newlen is bigger */
|
|
memcpy(newarr, prop->data.pointer, prop->len * sizeof(IDProperty));
|
|
}
|
|
else {
|
|
int i;
|
|
/* newlen is smaller */
|
|
for (i = newlen; i < prop->len; i++) {
|
|
IDP_FreeProperty(GETPROP(prop, i));
|
|
}
|
|
memcpy(newarr, prop->data.pointer, newlen * sizeof(IDProperty));
|
|
}
|
|
|
|
if (prop->data.pointer)
|
|
MEM_freeN(prop->data.pointer);
|
|
prop->data.pointer = newarr;
|
|
prop->len = newlen;
|
|
prop->totallen = newsize;
|
|
}
|
|
|
|
/* ----------- Numerical Array Type ----------- */
|
|
static void idp_resize_group_array(IDProperty *prop, int newlen, void *newarr)
|
|
{
|
|
if (prop->subtype != IDP_GROUP)
|
|
return;
|
|
|
|
if (newlen >= prop->len) {
|
|
/* bigger */
|
|
IDProperty **array = newarr;
|
|
IDPropertyTemplate val;
|
|
int a;
|
|
|
|
for (a = prop->len; a < newlen; a++) {
|
|
val.i = 0; /* silence MSVC warning about uninitialized var when debugging */
|
|
array[a] = IDP_New(IDP_GROUP, &val, "IDP_ResizeArray group");
|
|
}
|
|
}
|
|
else {
|
|
/* smaller */
|
|
IDProperty **array = prop->data.pointer;
|
|
int a;
|
|
|
|
for (a = newlen; a < prop->len; a++) {
|
|
IDP_FreeProperty(array[a]);
|
|
MEM_freeN(array[a]);
|
|
}
|
|
}
|
|
}
|
|
|
|
/*this function works for strings too!*/
|
|
void IDP_ResizeArray(IDProperty *prop, int newlen)
|
|
{
|
|
void *newarr;
|
|
int newsize = newlen;
|
|
|
|
/*first check if the array buffer size has room*/
|
|
/*if newlen is 200 chars less then totallen, reallocate anyway*/
|
|
if (newlen <= prop->totallen && prop->totallen - newlen < 200) {
|
|
idp_resize_group_array(prop, newlen, prop->data.pointer);
|
|
prop->len = newlen;
|
|
return;
|
|
}
|
|
|
|
/* - Note: This code comes from python, here's the corresponding comment. - */
|
|
/* This over-allocates proportional to the list size, making room
|
|
* for additional growth. The over-allocation is mild, but is
|
|
* enough to give linear-time amortized behavior over a long
|
|
* sequence of appends() in the presence of a poorly-performing
|
|
* system realloc().
|
|
* The growth pattern is: 0, 4, 8, 16, 25, 35, 46, 58, 72, 88, ...
|
|
*/
|
|
newsize = (newsize >> 3) + (newsize < 9 ? 3 : 6) + newsize;
|
|
|
|
newarr = MEM_callocN(idp_size_table[(int)prop->subtype] * newsize, "idproperty array resized");
|
|
if (newlen >= prop->len) {
|
|
/* newlen is bigger */
|
|
memcpy(newarr, prop->data.pointer, prop->len * idp_size_table[(int)prop->subtype]);
|
|
idp_resize_group_array(prop, newlen, newarr);
|
|
}
|
|
else {
|
|
/* newlen is smaller */
|
|
idp_resize_group_array(prop, newlen, newarr);
|
|
memcpy(newarr, prop->data.pointer, newlen * idp_size_table[(int)prop->subtype]);
|
|
}
|
|
|
|
MEM_freeN(prop->data.pointer);
|
|
prop->data.pointer = newarr;
|
|
prop->len = newlen;
|
|
prop->totallen = newsize;
|
|
}
|
|
|
|
void IDP_FreeArray(IDProperty *prop)
|
|
{
|
|
if (prop->data.pointer) {
|
|
idp_resize_group_array(prop, 0, NULL);
|
|
MEM_freeN(prop->data.pointer);
|
|
}
|
|
}
|
|
|
|
|
|
static IDProperty *idp_generic_copy(IDProperty *prop)
|
|
{
|
|
IDProperty *newp = MEM_callocN(sizeof(IDProperty), "IDProperty array dup");
|
|
|
|
BLI_strncpy(newp->name, prop->name, MAX_IDPROP_NAME);
|
|
newp->type = prop->type;
|
|
newp->flag = prop->flag;
|
|
newp->data.val = prop->data.val;
|
|
newp->data.val2 = prop->data.val2;
|
|
|
|
return newp;
|
|
}
|
|
|
|
static IDProperty *IDP_CopyArray(IDProperty *prop)
|
|
{
|
|
IDProperty *newp = idp_generic_copy(prop);
|
|
|
|
if (prop->data.pointer) {
|
|
newp->data.pointer = MEM_dupallocN(prop->data.pointer);
|
|
|
|
if (prop->type == IDP_GROUP) {
|
|
IDProperty **array = newp->data.pointer;
|
|
int a;
|
|
|
|
for (a = 0; a < prop->len; a++)
|
|
array[a] = IDP_CopyProperty(array[a]);
|
|
}
|
|
}
|
|
newp->len = prop->len;
|
|
newp->subtype = prop->subtype;
|
|
newp->totallen = prop->totallen;
|
|
|
|
return newp;
|
|
}
|
|
|
|
/* ---------- String Type ------------ */
|
|
IDProperty *IDP_NewString(const char *st, const char *name, int maxlen)
|
|
{
|
|
IDProperty *prop = MEM_callocN(sizeof(IDProperty), "IDProperty string");
|
|
|
|
if (st == NULL) {
|
|
prop->data.pointer = MEM_callocN(DEFAULT_ALLOC_FOR_NULL_STRINGS, "id property string 1");
|
|
prop->totallen = DEFAULT_ALLOC_FOR_NULL_STRINGS;
|
|
prop->len = 1; /*NULL string, has len of 1 to account for null byte.*/
|
|
}
|
|
else {
|
|
int stlen = strlen(st);
|
|
|
|
if (maxlen > 0 && maxlen < stlen)
|
|
stlen = maxlen;
|
|
|
|
stlen++; /* null terminator '\0' */
|
|
|
|
prop->data.pointer = MEM_callocN(stlen, "id property string 2");
|
|
prop->len = prop->totallen = stlen;
|
|
BLI_strncpy(prop->data.pointer, st, stlen);
|
|
}
|
|
|
|
prop->type = IDP_STRING;
|
|
BLI_strncpy(prop->name, name, MAX_IDPROP_NAME);
|
|
|
|
return prop;
|
|
}
|
|
|
|
static IDProperty *IDP_CopyString(IDProperty *prop)
|
|
{
|
|
IDProperty *newp = idp_generic_copy(prop);
|
|
|
|
if (prop->data.pointer) newp->data.pointer = MEM_dupallocN(prop->data.pointer);
|
|
newp->len = prop->len;
|
|
newp->subtype = prop->subtype;
|
|
newp->totallen = prop->totallen;
|
|
|
|
return newp;
|
|
}
|
|
|
|
|
|
void IDP_AssignString(IDProperty *prop, const char *st, int maxlen)
|
|
{
|
|
int stlen = strlen(st);
|
|
|
|
if (maxlen > 0 && maxlen < stlen)
|
|
stlen = maxlen;
|
|
|
|
if (prop->subtype == IDP_STRING_SUB_BYTE) {
|
|
IDP_ResizeArray(prop, stlen);
|
|
memcpy(prop->data.pointer, st, stlen);
|
|
}
|
|
else {
|
|
stlen++; /* make room for null byte */
|
|
IDP_ResizeArray(prop, stlen);
|
|
BLI_strncpy(prop->data.pointer, st, stlen);
|
|
}
|
|
}
|
|
|
|
void IDP_ConcatStringC(IDProperty *prop, const char *st)
|
|
{
|
|
int newlen;
|
|
|
|
newlen = prop->len + strlen(st);
|
|
/* we have to remember that prop->len includes the null byte for strings.
|
|
* so there's no need to add +1 to the resize function.*/
|
|
IDP_ResizeArray(prop, newlen);
|
|
strcat(prop->data.pointer, st);
|
|
}
|
|
|
|
void IDP_ConcatString(IDProperty *str1, IDProperty *append)
|
|
{
|
|
int newlen;
|
|
|
|
/* since ->len for strings includes the NULL byte, we have to subtract one or
|
|
* we'll get an extra null byte after each concatenation operation.*/
|
|
newlen = str1->len + append->len - 1;
|
|
IDP_ResizeArray(str1, newlen);
|
|
strcat(str1->data.pointer, append->data.pointer);
|
|
}
|
|
|
|
void IDP_FreeString(IDProperty *prop)
|
|
{
|
|
if (prop->data.pointer)
|
|
MEM_freeN(prop->data.pointer);
|
|
}
|
|
|
|
|
|
/*-------- ID Type, not in use yet -------*/
|
|
|
|
void IDP_LinkID(IDProperty *prop, ID *id)
|
|
{
|
|
if (prop->data.pointer) ((ID *)prop->data.pointer)->us--;
|
|
prop->data.pointer = id;
|
|
id_us_plus(id);
|
|
}
|
|
|
|
void IDP_UnlinkID(IDProperty *prop)
|
|
{
|
|
((ID *)prop->data.pointer)->us--;
|
|
}
|
|
|
|
/*-------- Group Functions -------*/
|
|
|
|
/*checks if a property with the same name as prop exists, and if so replaces it.*/
|
|
static IDProperty *IDP_CopyGroup(IDProperty *prop)
|
|
{
|
|
IDProperty *newp = idp_generic_copy(prop), *link;
|
|
newp->len = prop->len;
|
|
|
|
for (link = prop->data.group.first; link; link = link->next) {
|
|
BLI_addtail(&newp->data.group, IDP_CopyProperty(link));
|
|
}
|
|
|
|
return newp;
|
|
}
|
|
|
|
/* use for syncing proxies.
|
|
* When values name and types match, copy the values, else ignore */
|
|
void IDP_SyncGroupValues(IDProperty *dest, IDProperty *src)
|
|
{
|
|
IDProperty *other, *prop;
|
|
for (prop = src->data.group.first; prop; prop = prop->next) {
|
|
other = BLI_findstring(&dest->data.group, prop->name, offsetof(IDProperty, name));
|
|
if (other && prop->type == other->type) {
|
|
switch (prop->type) {
|
|
case IDP_INT:
|
|
case IDP_FLOAT:
|
|
case IDP_DOUBLE:
|
|
other->data = prop->data;
|
|
break;
|
|
case IDP_GROUP:
|
|
IDP_SyncGroupValues(other, prop);
|
|
break;
|
|
default:
|
|
{
|
|
IDProperty *tmp = other;
|
|
IDProperty *copy = IDP_CopyProperty(prop);
|
|
|
|
BLI_insertlinkafter(&dest->data.group, other, copy);
|
|
BLI_remlink(&dest->data.group, tmp);
|
|
|
|
IDP_FreeProperty(tmp);
|
|
MEM_freeN(tmp);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* replaces all properties with the same name in a destination group from a source group.
|
|
*/
|
|
void IDP_ReplaceGroupInGroup(IDProperty *dest, IDProperty *src)
|
|
{
|
|
IDProperty *loop, *prop;
|
|
for (prop = src->data.group.first; prop; prop = prop->next) {
|
|
for (loop = dest->data.group.first; loop; loop = loop->next) {
|
|
if (strcmp(loop->name, prop->name) == 0) {
|
|
IDProperty *copy = IDP_CopyProperty(prop);
|
|
|
|
BLI_insertlink(&dest->data.group, loop, copy);
|
|
|
|
BLI_remlink(&dest->data.group, loop);
|
|
IDP_FreeProperty(loop);
|
|
MEM_freeN(loop);
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* only add at end if not added yet */
|
|
if (loop == NULL) {
|
|
IDProperty *copy = IDP_CopyProperty(prop);
|
|
dest->len++;
|
|
BLI_addtail(&dest->data.group, copy);
|
|
}
|
|
}
|
|
}
|
|
/*
|
|
* replaces a property with the same name in a group, or adds
|
|
* it if the properly doesn't exist.
|
|
*/
|
|
void IDP_ReplaceInGroup(IDProperty *group, IDProperty *prop)
|
|
{
|
|
IDProperty *loop;
|
|
if ((loop = IDP_GetPropertyFromGroup(group, prop->name))) {
|
|
BLI_insertlink(&group->data.group, loop, prop);
|
|
|
|
BLI_remlink(&group->data.group, loop);
|
|
IDP_FreeProperty(loop);
|
|
MEM_freeN(loop);
|
|
}
|
|
else {
|
|
group->len++;
|
|
BLI_addtail(&group->data.group, prop);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* If a property is missing in \a dest, add it.
|
|
*/
|
|
void IDP_MergeGroup(IDProperty *dest, IDProperty *src, const int do_overwrite)
|
|
{
|
|
IDProperty *prop;
|
|
|
|
if (do_overwrite) {
|
|
for (prop = src->data.group.first; prop; prop = prop->next) {
|
|
IDProperty *copy = IDP_CopyProperty(prop);
|
|
IDP_ReplaceInGroup(dest, copy);
|
|
}
|
|
}
|
|
else {
|
|
for (prop = src->data.group.first; prop; prop = prop->next) {
|
|
if (IDP_GetPropertyFromGroup(dest, prop->name) == NULL) {
|
|
IDProperty *copy = IDP_CopyProperty(prop);
|
|
dest->len++;
|
|
BLI_addtail(&dest->data.group, copy);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* returns 0 if an id property with the same name exists and it failed,
|
|
* or 1 if it succeeded in adding to the group.*/
|
|
int IDP_AddToGroup(IDProperty *group, IDProperty *prop)
|
|
{
|
|
if (IDP_GetPropertyFromGroup(group, prop->name) == NULL) {
|
|
group->len++;
|
|
BLI_addtail(&group->data.group, prop);
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int IDP_InsertToGroup(IDProperty *group, IDProperty *previous, IDProperty *pnew)
|
|
{
|
|
if (IDP_GetPropertyFromGroup(group, pnew->name) == NULL) {
|
|
group->len++;
|
|
BLI_insertlink(&group->data.group, previous, pnew);
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void IDP_RemFromGroup(IDProperty *group, IDProperty *prop)
|
|
{
|
|
group->len--;
|
|
BLI_remlink(&group->data.group, prop);
|
|
}
|
|
|
|
IDProperty *IDP_GetPropertyFromGroup(IDProperty *prop, const char *name)
|
|
{
|
|
return (IDProperty *)BLI_findstring(&prop->data.group, name, offsetof(IDProperty, name));
|
|
}
|
|
|
|
IDProperty *IDP_GetPropertyTypeFromGroup(IDProperty *prop, const char *name, const char type)
|
|
{
|
|
IDProperty *idprop = IDP_GetPropertyFromGroup(prop, name);
|
|
return (idprop && idprop->type == type) ? idprop : NULL;
|
|
}
|
|
|
|
typedef struct IDPIter {
|
|
void *next;
|
|
IDProperty *parent;
|
|
} IDPIter;
|
|
|
|
void *IDP_GetGroupIterator(IDProperty *prop)
|
|
{
|
|
IDPIter *iter = MEM_callocN(sizeof(IDPIter), "IDPIter");
|
|
iter->next = prop->data.group.first;
|
|
iter->parent = prop;
|
|
return (void *) iter;
|
|
}
|
|
|
|
IDProperty *IDP_GroupIterNext(void *vself)
|
|
{
|
|
IDPIter *self = (IDPIter *) vself;
|
|
Link *next = (Link *) self->next;
|
|
if (self->next == NULL) {
|
|
MEM_freeN(self);
|
|
return NULL;
|
|
}
|
|
|
|
self->next = next->next;
|
|
return (void *) next;
|
|
}
|
|
|
|
void IDP_FreeIterBeforeEnd(void *vself)
|
|
{
|
|
MEM_freeN(vself);
|
|
}
|
|
|
|
/* Ok, the way things work, Groups free the ID Property structs of their children.
|
|
* This is because all ID Property freeing functions free only direct data (not the ID Property
|
|
* struct itself), but for Groups the child properties *are* considered
|
|
* direct data. */
|
|
static void IDP_FreeGroup(IDProperty *prop)
|
|
{
|
|
IDProperty *loop;
|
|
for (loop = prop->data.group.first; loop; loop = loop->next) {
|
|
IDP_FreeProperty(loop);
|
|
}
|
|
BLI_freelistN(&prop->data.group);
|
|
}
|
|
|
|
|
|
/*-------- Main Functions --------*/
|
|
IDProperty *IDP_CopyProperty(IDProperty *prop)
|
|
{
|
|
switch (prop->type) {
|
|
case IDP_GROUP: return IDP_CopyGroup(prop);
|
|
case IDP_STRING: return IDP_CopyString(prop);
|
|
case IDP_ARRAY: return IDP_CopyArray(prop);
|
|
case IDP_IDPARRAY: return IDP_CopyIDPArray(prop);
|
|
default: return idp_generic_copy(prop);
|
|
}
|
|
}
|
|
|
|
IDProperty *IDP_GetProperties(ID *id, int create_if_needed)
|
|
{
|
|
if (id->properties) {
|
|
return id->properties;
|
|
}
|
|
else {
|
|
if (create_if_needed) {
|
|
id->properties = MEM_callocN(sizeof(IDProperty), "IDProperty");
|
|
id->properties->type = IDP_GROUP;
|
|
/* don't overwrite the data's name and type
|
|
* some functions might need this if they
|
|
* don't have a real ID, should be named elsewhere - Campbell */
|
|
/* strcpy(id->name, "top_level_group");*/
|
|
}
|
|
return id->properties;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* \param is_strict When FALSE treat missing items as a match */
|
|
int IDP_EqualsProperties_ex(IDProperty *prop1, IDProperty *prop2, const int is_strict)
|
|
{
|
|
if (prop1 == NULL && prop2 == NULL)
|
|
return 1;
|
|
else if (prop1 == NULL || prop2 == NULL)
|
|
return is_strict ? 0 : 1;
|
|
else if (prop1->type != prop2->type)
|
|
return 0;
|
|
|
|
switch (prop1->type) {
|
|
case IDP_INT:
|
|
return (IDP_Int(prop1) == IDP_Int(prop2));
|
|
case IDP_FLOAT:
|
|
return (IDP_Float(prop1) == IDP_Float(prop2));
|
|
case IDP_DOUBLE:
|
|
return (IDP_Double(prop1) == IDP_Double(prop2));
|
|
case IDP_STRING:
|
|
return ((prop1->len == prop2->len) && strncmp(IDP_String(prop1), IDP_String(prop2), prop1->len) == 0);
|
|
case IDP_ARRAY:
|
|
if (prop1->len == prop2->len && prop1->subtype == prop2->subtype) {
|
|
return memcmp(IDP_Array(prop1), IDP_Array(prop2), idp_size_table[(int)prop1->subtype] * prop1->len);
|
|
}
|
|
else {
|
|
return 0;
|
|
}
|
|
case IDP_GROUP:
|
|
{
|
|
IDProperty *link1, *link2;
|
|
|
|
if (is_strict && BLI_countlist(&prop1->data.group) != BLI_countlist(&prop2->data.group))
|
|
return 0;
|
|
|
|
for (link1 = prop1->data.group.first; link1; link1 = link1->next) {
|
|
link2 = IDP_GetPropertyFromGroup(prop2, link1->name);
|
|
|
|
if (!IDP_EqualsProperties_ex(link1, link2, is_strict))
|
|
return 0;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
case IDP_IDPARRAY:
|
|
{
|
|
IDProperty *array1 = IDP_IDPArray(prop1);
|
|
IDProperty *array2 = IDP_IDPArray(prop2);
|
|
int i;
|
|
|
|
if (prop1->len != prop2->len)
|
|
return 0;
|
|
|
|
for (i = 0; i < prop1->len; i++)
|
|
if (!IDP_EqualsProperties(&array1[i], &array2[i]))
|
|
return 0;
|
|
return 1;
|
|
}
|
|
default:
|
|
/* should never get here */
|
|
BLI_assert(0);
|
|
break;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
int IDP_EqualsProperties(IDProperty *prop1, IDProperty *prop2)
|
|
{
|
|
return IDP_EqualsProperties_ex(prop1, prop2, TRUE);
|
|
}
|
|
|
|
/* 'val' is never NULL, don't check */
|
|
IDProperty *IDP_New(const int type, const IDPropertyTemplate *val, const char *name)
|
|
{
|
|
IDProperty *prop = NULL;
|
|
|
|
switch (type) {
|
|
case IDP_INT:
|
|
prop = MEM_callocN(sizeof(IDProperty), "IDProperty int");
|
|
prop->data.val = val->i;
|
|
break;
|
|
case IDP_FLOAT:
|
|
prop = MEM_callocN(sizeof(IDProperty), "IDProperty float");
|
|
*(float *)&prop->data.val = val->f;
|
|
break;
|
|
case IDP_DOUBLE:
|
|
prop = MEM_callocN(sizeof(IDProperty), "IDProperty float");
|
|
*(double *)&prop->data.val = val->d;
|
|
break;
|
|
case IDP_ARRAY:
|
|
{
|
|
/* for now, we only support float and int and double arrays */
|
|
if ( (val->array.type == IDP_FLOAT) ||
|
|
(val->array.type == IDP_INT) ||
|
|
(val->array.type == IDP_DOUBLE) ||
|
|
(val->array.type == IDP_GROUP) )
|
|
{
|
|
prop = MEM_callocN(sizeof(IDProperty), "IDProperty array");
|
|
prop->subtype = val->array.type;
|
|
if (val->array.len)
|
|
prop->data.pointer = MEM_callocN(idp_size_table[val->array.type] * val->array.len, "id property array");
|
|
prop->len = prop->totallen = val->array.len;
|
|
break;
|
|
}
|
|
else {
|
|
return NULL;
|
|
}
|
|
}
|
|
case IDP_STRING:
|
|
{
|
|
const char *st = val->string.str;
|
|
|
|
prop = MEM_callocN(sizeof(IDProperty), "IDProperty string");
|
|
if (val->string.subtype == IDP_STRING_SUB_BYTE) {
|
|
/* note, intentionally not null terminated */
|
|
if (st == NULL) {
|
|
prop->data.pointer = MEM_callocN(DEFAULT_ALLOC_FOR_NULL_STRINGS, "id property string 1");
|
|
prop->totallen = DEFAULT_ALLOC_FOR_NULL_STRINGS;
|
|
prop->len = 0;
|
|
}
|
|
else {
|
|
prop->data.pointer = MEM_mallocN(val->string.len, "id property string 2");
|
|
prop->len = prop->totallen = val->string.len;
|
|
memcpy(prop->data.pointer, st, val->string.len);
|
|
}
|
|
prop->subtype = IDP_STRING_SUB_BYTE;
|
|
}
|
|
else {
|
|
if (st == NULL) {
|
|
prop->data.pointer = MEM_callocN(DEFAULT_ALLOC_FOR_NULL_STRINGS, "id property string 1");
|
|
prop->totallen = DEFAULT_ALLOC_FOR_NULL_STRINGS;
|
|
prop->len = 1; /*NULL string, has len of 1 to account for null byte.*/
|
|
}
|
|
else {
|
|
int stlen = strlen(st) + 1;
|
|
prop->data.pointer = MEM_mallocN(stlen, "id property string 3");
|
|
prop->len = prop->totallen = stlen;
|
|
memcpy(prop->data.pointer, st, stlen);
|
|
}
|
|
prop->subtype = IDP_STRING_SUB_UTF8;
|
|
}
|
|
break;
|
|
}
|
|
case IDP_GROUP:
|
|
{
|
|
prop = MEM_callocN(sizeof(IDProperty), "IDProperty group");
|
|
/* heh I think all needed values are set properly by calloc anyway :) */
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
prop = MEM_callocN(sizeof(IDProperty), "IDProperty array");
|
|
break;
|
|
}
|
|
}
|
|
|
|
prop->type = type;
|
|
BLI_strncpy(prop->name, name, MAX_IDPROP_NAME);
|
|
|
|
return prop;
|
|
}
|
|
|
|
/* NOTE: this will free all child properties including list arrays and groups!
|
|
* Also, note that this does NOT unlink anything! Plus it doesn't free
|
|
* the actual IDProperty struct either.*/
|
|
void IDP_FreeProperty(IDProperty *prop)
|
|
{
|
|
switch (prop->type) {
|
|
case IDP_ARRAY:
|
|
IDP_FreeArray(prop);
|
|
break;
|
|
case IDP_STRING:
|
|
IDP_FreeString(prop);
|
|
break;
|
|
case IDP_GROUP:
|
|
IDP_FreeGroup(prop);
|
|
break;
|
|
case IDP_IDPARRAY:
|
|
IDP_FreeIDPArray(prop);
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Unlinks any IDProperty<->ID linkage that might be going on.
|
|
* note: currently unused.*/
|
|
void IDP_UnlinkProperty(IDProperty *prop)
|
|
{
|
|
switch (prop->type) {
|
|
case IDP_ID:
|
|
IDP_UnlinkID(prop);
|
|
}
|
|
}
|