- Added a new Brush datablock, only used by image paint, but intended to be used in texture paint, vertex paint, weight paint and sculpt mode also. - Being a datablock, these brushes can be saved, appended and linked. They have a fake user by default, to make sure they are saved even if not selected. Image Painting: - Replaced the img module with C code in imagepaint.c - Airbrush is no longer a separate tool, but rather an option that can be used for soften, smear and clone also. - Blend modes mix, add, subtract, multiply, darken and lighten have been added, code taken directly from vertex paint. Note to project files maintainers: - The img module was removed from SCons and Makefiles, and this should be done in other build systems also. I'll wait to remove the module from cvs, to not break compilation.
1003 lines
21 KiB
C
1003 lines
21 KiB
C
/**
|
|
* $Id$
|
|
*
|
|
* ***** BEGIN GPL/BL DUAL 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. The Blender
|
|
* Foundation also sells licenses for use in proprietary software under
|
|
* the Blender License. See http://www.blender.org/BL/ for information
|
|
* about this.
|
|
*
|
|
* 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
*
|
|
* The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
|
|
* All rights reserved.
|
|
*
|
|
* The Original Code is: all of this file.
|
|
*
|
|
* Contributor(s): none yet.
|
|
*
|
|
* ***** END GPL/BL DUAL LICENSE BLOCK *****
|
|
*/
|
|
|
|
/*
|
|
* Contains management of ID's and libraries
|
|
* allocate and free of all library data
|
|
*
|
|
*/
|
|
|
|
|
|
#include <stdio.h>
|
|
#include <ctype.h>
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include <config.h>
|
|
#endif
|
|
|
|
#include "MEM_guardedalloc.h"
|
|
|
|
/* all types are needed here, in order to do memory operations */
|
|
#include "DNA_ID.h"
|
|
#include "DNA_listBase.h"
|
|
#include "DNA_scene_types.h"
|
|
#include "DNA_object_types.h"
|
|
#include "DNA_mesh_types.h"
|
|
#include "DNA_lattice_types.h"
|
|
#include "DNA_curve_types.h"
|
|
#include "DNA_meta_types.h"
|
|
#include "DNA_material_types.h"
|
|
#include "DNA_texture_types.h"
|
|
#include "DNA_image_types.h"
|
|
#include "DNA_wave_types.h"
|
|
#include "DNA_lamp_types.h"
|
|
#include "DNA_camera_types.h"
|
|
#include "DNA_ipo_types.h"
|
|
#include "DNA_key_types.h"
|
|
#include "DNA_world_types.h"
|
|
#include "DNA_screen_types.h"
|
|
#include "DNA_vfont_types.h"
|
|
#include "DNA_text_types.h"
|
|
#include "DNA_sound_types.h"
|
|
#include "DNA_group_types.h"
|
|
#include "DNA_armature_types.h"
|
|
#include "DNA_action_types.h"
|
|
#include "DNA_userdef_types.h"
|
|
#include "DNA_node_types.h"
|
|
#include "DNA_nla_types.h"
|
|
#include "DNA_effect_types.h"
|
|
#include "DNA_brush_types.h"
|
|
|
|
#include "BLI_blenlib.h"
|
|
#include "BLI_dynstr.h"
|
|
|
|
#include "BKE_bad_level_calls.h"
|
|
#include "BKE_library.h"
|
|
#include "BKE_main.h"
|
|
#include "BKE_global.h"
|
|
#include "BKE_sound.h"
|
|
#include "BKE_object.h"
|
|
#include "BKE_screen.h"
|
|
#include "BKE_script.h"
|
|
#include "BKE_mesh.h"
|
|
#include "BKE_material.h"
|
|
#include "BKE_curve.h"
|
|
#include "BKE_mball.h"
|
|
#include "BKE_text.h"
|
|
#include "BKE_texture.h"
|
|
#include "BKE_scene.h"
|
|
#include "BKE_icons.h"
|
|
#include "BKE_image.h"
|
|
#include "BKE_ipo.h"
|
|
#include "BKE_key.h"
|
|
#include "BKE_world.h"
|
|
#include "BKE_font.h"
|
|
#include "BKE_group.h"
|
|
#include "BKE_lattice.h"
|
|
#include "BKE_armature.h"
|
|
#include "BKE_action.h"
|
|
#include "BKE_node.h"
|
|
#include "BKE_effect.h"
|
|
#include "BKE_brush.h"
|
|
|
|
#include "BPI_script.h"
|
|
|
|
#define MAX_IDPUP 60 /* was 24 */
|
|
#define MAX_LIBARRAY 100 /* was 30, warning: used it readfile.c too */
|
|
|
|
/* ************* general ************************ */
|
|
|
|
void id_lib_extern(ID *id)
|
|
{
|
|
if(id) {
|
|
if(id->flag & LIB_INDIRECT) {
|
|
id->flag -= LIB_INDIRECT;
|
|
id->flag |= LIB_EXTERN;
|
|
}
|
|
}
|
|
}
|
|
|
|
void id_us_plus(ID *id)
|
|
{
|
|
if(id) {
|
|
id->us++;
|
|
if(id->flag & LIB_INDIRECT) {
|
|
id->flag -= LIB_INDIRECT;
|
|
id->flag |= LIB_EXTERN;
|
|
}
|
|
}
|
|
}
|
|
|
|
ListBase *wich_libbase(Main *mainlib, short type)
|
|
{
|
|
switch( type ) {
|
|
case ID_SCE:
|
|
return &(mainlib->scene);
|
|
case ID_LI:
|
|
return &(mainlib->library);
|
|
case ID_OB:
|
|
return &(mainlib->object);
|
|
case ID_ME:
|
|
return &(mainlib->mesh);
|
|
case ID_CU:
|
|
return &(mainlib->curve);
|
|
case ID_MB:
|
|
return &(mainlib->mball);
|
|
case ID_MA:
|
|
return &(mainlib->mat);
|
|
case ID_TE:
|
|
return &(mainlib->tex);
|
|
case ID_IM:
|
|
return &(mainlib->image);
|
|
case ID_WV:
|
|
return &(mainlib->wave);
|
|
case ID_LT:
|
|
return &(mainlib->latt);
|
|
case ID_LA:
|
|
return &(mainlib->lamp);
|
|
case ID_CA:
|
|
return &(mainlib->camera);
|
|
case ID_IP:
|
|
return &(mainlib->ipo);
|
|
case ID_KE:
|
|
return &(mainlib->key);
|
|
case ID_WO:
|
|
return &(mainlib->world);
|
|
case ID_SCR:
|
|
return &(mainlib->screen);
|
|
case ID_VF:
|
|
return &(mainlib->vfont);
|
|
case ID_TXT:
|
|
return &(mainlib->text);
|
|
case ID_SCRIPT:
|
|
return &(mainlib->script);
|
|
case ID_SO:
|
|
return &(mainlib->sound);
|
|
case ID_SAMPLE:
|
|
/* declared as an external in sound.h !!! */
|
|
return (samples);
|
|
case ID_GR:
|
|
return &(mainlib->group);
|
|
case ID_AR:
|
|
return &(mainlib->armature);
|
|
case ID_AC:
|
|
return &(mainlib->action);
|
|
case ID_NT:
|
|
return &(mainlib->nodetree);
|
|
case ID_BR:
|
|
return &(mainlib->brush);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int set_listbasepointers(Main *main, ListBase **lb)
|
|
{
|
|
/* BACKWARDS! also watch order of free-ing! (mesh<->mat) */
|
|
|
|
lb[0]= &(main->ipo);
|
|
lb[1]= &(main->key);
|
|
lb[2]= &(main->image);
|
|
lb[3]= &(main->tex);
|
|
lb[4]= &(main->mat);
|
|
lb[5]= &(main->vfont);
|
|
|
|
/* Important!: When adding a new object type,
|
|
* the specific data should be inserted here
|
|
*/
|
|
|
|
lb[6]= &(main->armature);
|
|
lb[7]= &(main->action);
|
|
|
|
lb[8]= &(main->mesh);
|
|
lb[9]= &(main->curve);
|
|
lb[10]= &(main->mball);
|
|
|
|
lb[11]= &(main->wave);
|
|
lb[12]= &(main->latt);
|
|
lb[13]= &(main->lamp);
|
|
lb[14]= &(main->camera);
|
|
|
|
lb[15]= &(main->world);
|
|
lb[16]= &(main->screen);
|
|
lb[17]= &(main->object);
|
|
lb[18]= &(main->scene);
|
|
lb[19]= &(main->library);
|
|
lb[20]= &(main->text);
|
|
lb[21]= &(main->sound);
|
|
lb[22]= &(main->group);
|
|
lb[23]= &(main->nodetree);
|
|
lb[24]= &(main->brush);
|
|
|
|
lb[25]= samples;
|
|
lb[26]= &(main->script);
|
|
lb[27]= NULL;
|
|
|
|
return 27;
|
|
}
|
|
|
|
/* *********** ALLOC AND FREE *****************
|
|
|
|
free_libblock(ListBase *lb, ID *id )
|
|
provide a list-basis and datablock, but only ID is read
|
|
|
|
void *alloc_libblock(ListBase *lb, type, name)
|
|
inserts in list and returns a new ID
|
|
|
|
***************************** */
|
|
|
|
static ID *alloc_libblock_notest(short type)
|
|
{
|
|
ID *id= NULL;
|
|
|
|
switch( type ) {
|
|
case ID_SCE:
|
|
id= MEM_callocN(sizeof(Scene), "scene");
|
|
break;
|
|
case ID_LI:
|
|
id= MEM_callocN(sizeof(Library), "library");
|
|
break;
|
|
case ID_OB:
|
|
id= MEM_callocN(sizeof(Object), "object");
|
|
break;
|
|
case ID_ME:
|
|
id= MEM_callocN(sizeof(Mesh), "mesh");
|
|
break;
|
|
case ID_CU:
|
|
id= MEM_callocN(sizeof(Curve), "curve");
|
|
break;
|
|
case ID_MB:
|
|
id= MEM_callocN(sizeof(MetaBall), "mball");
|
|
break;
|
|
case ID_MA:
|
|
id= MEM_callocN(sizeof(Material), "mat");
|
|
break;
|
|
case ID_TE:
|
|
id= MEM_callocN(sizeof(Tex), "tex");
|
|
break;
|
|
case ID_IM:
|
|
id= MEM_callocN(sizeof(Image), "image");
|
|
break;
|
|
case ID_WV:
|
|
id= MEM_callocN(sizeof(Wave), "wave");
|
|
break;
|
|
case ID_LT:
|
|
id= MEM_callocN(sizeof(Lattice), "latt");
|
|
break;
|
|
case ID_LA:
|
|
id= MEM_callocN(sizeof(Lamp), "lamp");
|
|
break;
|
|
case ID_CA:
|
|
id= MEM_callocN(sizeof(Camera), "camera");
|
|
break;
|
|
case ID_IP:
|
|
id= MEM_callocN(sizeof(Ipo), "ipo");
|
|
break;
|
|
case ID_KE:
|
|
id= MEM_callocN(sizeof(Key), "key");
|
|
break;
|
|
case ID_WO:
|
|
id= MEM_callocN(sizeof(World), "world");
|
|
break;
|
|
case ID_SCR:
|
|
id= MEM_callocN(sizeof(bScreen), "screen");
|
|
break;
|
|
case ID_VF:
|
|
id= MEM_callocN(sizeof(VFont), "vfont");
|
|
break;
|
|
case ID_TXT:
|
|
id= MEM_callocN(sizeof(Text), "text");
|
|
break;
|
|
case ID_SCRIPT:
|
|
id= MEM_callocN(sizeof(Script), "script");
|
|
break;
|
|
case ID_SO:
|
|
id= MEM_callocN(sizeof(bSound), "sound");
|
|
break;
|
|
case ID_SAMPLE:
|
|
id = MEM_callocN(sizeof(bSample), "sound");
|
|
break;
|
|
case ID_GR:
|
|
id= MEM_callocN(sizeof(Group), "sound");
|
|
break;
|
|
case ID_AR:
|
|
id = MEM_callocN(sizeof(bArmature), "armature");
|
|
break;
|
|
case ID_AC:
|
|
id = MEM_callocN(sizeof(bAction), "action");
|
|
break;
|
|
case ID_NT:
|
|
id = MEM_callocN(sizeof(bNodeTree), "nodetree");
|
|
break;
|
|
case ID_BR:
|
|
id = MEM_callocN(sizeof(Brush), "brush");
|
|
break;
|
|
}
|
|
return id;
|
|
}
|
|
|
|
// used everywhere in blenkernel and text.c
|
|
void *alloc_libblock(ListBase *lb, short type, const char *name)
|
|
{
|
|
ID *id= NULL;
|
|
|
|
id= alloc_libblock_notest(type);
|
|
if(id) {
|
|
BLI_addtail(lb, id);
|
|
id->us= 1;
|
|
id->icon_id = 0;
|
|
*( (short *)id->name )= type;
|
|
new_id(lb, id, name);
|
|
/* alphabetic insterion: is in new_id */
|
|
}
|
|
return id;
|
|
}
|
|
|
|
/* GS reads the memory pointed at in a specific ordering.
|
|
only use this definition, makes little and big endian systems
|
|
work fine, in conjunction with MAKE_ID */
|
|
|
|
/* from blendef: */
|
|
#define GS(a) (*((short *)(a)))
|
|
|
|
// used everywhere in blenkernel and text.c
|
|
void *copy_libblock(void *rt)
|
|
{
|
|
ID *idn, *id;
|
|
ListBase *lb;
|
|
char *cp, *cpn;
|
|
int idn_len;
|
|
|
|
id= rt;
|
|
|
|
lb= wich_libbase(G.main, GS(id->name));
|
|
idn= alloc_libblock(lb, GS(id->name), id->name+2);
|
|
|
|
idn_len= MEM_allocN_len(idn);
|
|
if(idn_len - sizeof(ID) > 0) {
|
|
cp= (char *)id;
|
|
cpn= (char *)idn;
|
|
memcpy(cpn+sizeof(ID), cp+sizeof(ID), idn_len - sizeof(ID));
|
|
}
|
|
|
|
id->newid= idn;
|
|
idn->flag |= LIB_NEW;
|
|
|
|
return idn;
|
|
}
|
|
|
|
static void free_library(Library *lib)
|
|
{
|
|
/* no freeing needed for libraries yet */
|
|
}
|
|
|
|
// used in headerbuttons.c image.c mesh.c screen.c sound.c and library.c
|
|
void free_libblock(ListBase *lb, void *idv)
|
|
{
|
|
ID *id= idv;
|
|
|
|
switch( GS(id->name) ) { /* GetShort from util.h */
|
|
case ID_SCE:
|
|
free_scene((Scene *)id);
|
|
break;
|
|
case ID_LI:
|
|
free_library((Library *)id);
|
|
break;
|
|
case ID_OB:
|
|
free_object((Object *)id);
|
|
break;
|
|
case ID_ME:
|
|
free_mesh((Mesh *)id);
|
|
break;
|
|
case ID_CU:
|
|
free_curve((Curve *)id);
|
|
break;
|
|
case ID_MB:
|
|
free_mball((MetaBall *)id);
|
|
break;
|
|
case ID_MA:
|
|
free_material((Material *)id);
|
|
break;
|
|
case ID_TE:
|
|
free_texture((Tex *)id);
|
|
break;
|
|
case ID_IM:
|
|
free_image((Image *)id);
|
|
break;
|
|
case ID_WV:
|
|
/* free_wave(id); */
|
|
break;
|
|
case ID_LT:
|
|
free_lattice((Lattice *)id);
|
|
break;
|
|
case ID_LA:
|
|
free_lamp((Lamp *)id);
|
|
break;
|
|
case ID_CA:
|
|
free_camera((Camera*) id);
|
|
break;
|
|
case ID_IP:
|
|
free_ipo((Ipo *)id);
|
|
break;
|
|
case ID_KE:
|
|
free_key((Key *)id);
|
|
break;
|
|
case ID_WO:
|
|
free_world((World *)id);
|
|
break;
|
|
case ID_SCR:
|
|
free_screen((bScreen *)id);
|
|
break;
|
|
case ID_VF:
|
|
free_vfont((VFont *)id);
|
|
break;
|
|
case ID_TXT:
|
|
free_text((Text *)id);
|
|
break;
|
|
case ID_SCRIPT:
|
|
free_script((Script *)id);
|
|
break;
|
|
case ID_SO:
|
|
sound_free_sound((bSound *)id);
|
|
break;
|
|
case ID_SAMPLE:
|
|
sound_free_sample((bSample *)id);
|
|
break;
|
|
case ID_GR:
|
|
free_group((Group *)id);
|
|
break;
|
|
case ID_AR:
|
|
free_armature((bArmature *)id);
|
|
break;
|
|
case ID_AC:
|
|
free_action((bAction *)id);
|
|
break;
|
|
case ID_NT:
|
|
ntreeFreeTree((bNodeTree *)id);
|
|
break;
|
|
case ID_BR:
|
|
free_brush((Brush *)id);
|
|
break;
|
|
}
|
|
|
|
BLI_remlink(lb, id);
|
|
MEM_freeN(id);
|
|
|
|
}
|
|
|
|
void free_libblock_us(ListBase *lb, void *idv) /* test users */
|
|
{
|
|
ID *id= idv;
|
|
|
|
id->us--;
|
|
|
|
if(id->us<0) {
|
|
if(id->lib) printf("ERROR block %s %s users %d\n", id->lib->name, id->name, id->us);
|
|
else printf("ERROR block %s users %d\n", id->name, id->us);
|
|
}
|
|
if(id->us==0) {
|
|
if( GS(id->name)==ID_OB ) unlink_object((Object *)id);
|
|
|
|
free_libblock(lb, id);
|
|
}
|
|
}
|
|
|
|
|
|
void free_main(Main *mainvar)
|
|
{
|
|
/* also call when reading a file, erase all, etc */
|
|
ListBase *lbarray[MAX_LIBARRAY];
|
|
int a;
|
|
|
|
a= set_listbasepointers(mainvar, lbarray);
|
|
while(a--) {
|
|
ListBase *lb= lbarray[a];
|
|
ID *id;
|
|
|
|
while ( (id= lb->first) ) {
|
|
free_libblock(lb, id);
|
|
}
|
|
}
|
|
|
|
MEM_freeN(mainvar);
|
|
}
|
|
|
|
/* ***************** ID ************************ */
|
|
|
|
|
|
ID *find_id(char *type, char *name) /* type: "OB" or "MA" etc */
|
|
{
|
|
ID *id;
|
|
ListBase *lb;
|
|
|
|
lb= wich_libbase(G.main, GS(type));
|
|
|
|
id= lb->first;
|
|
while(id) {
|
|
if( strcmp(id->name+2, name)==0 ) return id;
|
|
id= id->next;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static void get_flags_for_id(ID *id, char *buf)
|
|
{
|
|
int isfake= id->flag & LIB_FAKEUSER;
|
|
int isnode=0;
|
|
/* Writeout the flags for the entry, note there
|
|
* is a small hack that writes 5 spaces instead
|
|
* of 4 if no flags are displayed... this makes
|
|
* things usually line up ok - better would be
|
|
* to have that explicit, oh well - zr
|
|
*/
|
|
|
|
if(GS(id->name)==ID_MA)
|
|
isnode= ((Material *)id)->use_nodes;
|
|
|
|
if (id->us<0)
|
|
sprintf(buf, "-1W ");
|
|
else if (!id->lib && !isfake && id->us && !isnode)
|
|
sprintf(buf, " ");
|
|
else if(isnode)
|
|
sprintf(buf, "%c%cN%c ", id->lib?'L':' ', isfake?'F':' ', (id->us==0)?'O':' ');
|
|
else
|
|
sprintf(buf, "%c%c%c ", id->lib?'L':' ', isfake?'F':' ', (id->us==0)?'O':' ');
|
|
}
|
|
|
|
static void IDnames_to_dyn_pupstring(DynStr *pupds, ListBase *lb, ID *link, short *nr)
|
|
{
|
|
int i, nids= BLI_countlist(lb);
|
|
|
|
if (nr) *nr= -1;
|
|
|
|
if (nr && nids>MAX_IDPUP) {
|
|
BLI_dynstr_append(pupds, "DataBrowse %x-2");
|
|
*nr= -2;
|
|
} else {
|
|
ID *id;
|
|
|
|
for (i=0, id= lb->first; id; id= id->next, i++) {
|
|
char buf[32];
|
|
|
|
if (nr && id==link) *nr= i+1;
|
|
|
|
if (U.uiflag & USER_HIDE_DOT && id->name[2]=='.')
|
|
continue;
|
|
|
|
get_flags_for_id(id, buf);
|
|
|
|
BLI_dynstr_append(pupds, buf);
|
|
BLI_dynstr_append(pupds, id->name+2);
|
|
sprintf(buf, "%%x%d", i+1);
|
|
BLI_dynstr_append(pupds, buf);
|
|
|
|
/* icon */
|
|
switch(GS(id->name))
|
|
{
|
|
case ID_MA: /* fall through */
|
|
case ID_TE: /* fall through */
|
|
case ID_IM: /* fall through */
|
|
case ID_WO: /* fall through */
|
|
case ID_LA: /* fall through */
|
|
sprintf(buf, "%%i%d", BKE_icon_getid(id) );
|
|
BLI_dynstr_append(pupds, buf);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if(id->next)
|
|
BLI_dynstr_append(pupds, "|");
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Silly routine, the only difference between the one
|
|
* above is that it only adds items with a matching
|
|
* blocktype... this should be unified somehow... - zr
|
|
*/
|
|
static void IPOnames_to_dyn_pupstring(DynStr *pupds, ListBase *lb, ID *link, short *nr, int blocktype)
|
|
{
|
|
ID *id;
|
|
int i, nids;
|
|
|
|
for (id= lb->first, nids= 0; id; id= id->next) {
|
|
Ipo *ipo= (Ipo*) id;
|
|
|
|
if (ipo->blocktype==blocktype)
|
|
nids++;
|
|
}
|
|
|
|
if (nids>MAX_IDPUP) {
|
|
BLI_dynstr_append(pupds, "DataBrowse %x-2");
|
|
} else {
|
|
for (i=0, id= lb->first; id; id= id->next) {
|
|
Ipo *ipo= (Ipo*) id;
|
|
|
|
if (ipo->blocktype==blocktype) {
|
|
char buf[32];
|
|
|
|
if (id==link)
|
|
*nr= i+1;
|
|
|
|
if (U.uiflag & USER_HIDE_DOT && id->name[2]=='.')
|
|
continue;
|
|
|
|
get_flags_for_id(id, buf);
|
|
|
|
BLI_dynstr_append(pupds, buf);
|
|
BLI_dynstr_append(pupds, id->name+2);
|
|
sprintf(buf, "%%x%d", i+1);
|
|
BLI_dynstr_append(pupds, buf);
|
|
|
|
if(id->next)
|
|
BLI_dynstr_append(pupds, "|");
|
|
|
|
i++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// used by headerbuttons.c buttons.c editobject.c editseq.c
|
|
// if nr==NULL no MAX_IDPUP, this for non-header browsing
|
|
void IDnames_to_pupstring(char **str, char *title, char *extraops, ListBase *lb, ID *link, short *nr)
|
|
{
|
|
DynStr *pupds= BLI_dynstr_new();
|
|
|
|
if (title) {
|
|
BLI_dynstr_append(pupds, title);
|
|
BLI_dynstr_append(pupds, "%t|");
|
|
}
|
|
|
|
if (extraops) {
|
|
BLI_dynstr_append(pupds, extraops);
|
|
if (BLI_dynstr_get_len(pupds))
|
|
BLI_dynstr_append(pupds, "|");
|
|
}
|
|
|
|
IDnames_to_dyn_pupstring(pupds, lb, link, nr);
|
|
|
|
*str= BLI_dynstr_get_cstring(pupds);
|
|
BLI_dynstr_free(pupds);
|
|
}
|
|
|
|
// only used by headerbuttons.c
|
|
void IPOnames_to_pupstring(char **str, char *title, char *extraops, ListBase *lb, ID *link, short *nr, int blocktype)
|
|
{
|
|
DynStr *pupds= BLI_dynstr_new();
|
|
|
|
if (title) {
|
|
BLI_dynstr_append(pupds, title);
|
|
BLI_dynstr_append(pupds, "%t|");
|
|
}
|
|
|
|
if (extraops) {
|
|
BLI_dynstr_append(pupds, extraops);
|
|
if (BLI_dynstr_get_len(pupds))
|
|
BLI_dynstr_append(pupds, "|");
|
|
}
|
|
|
|
IPOnames_to_dyn_pupstring(pupds, lb, link, nr, blocktype);
|
|
|
|
*str= BLI_dynstr_get_cstring(pupds);
|
|
BLI_dynstr_free(pupds);
|
|
}
|
|
|
|
// used by buttons.c library.c mball.c
|
|
void splitIDname(char *name, char *left, int *nr)
|
|
{
|
|
int a;
|
|
|
|
*nr= 0;
|
|
strncpy(left, name, 21);
|
|
|
|
a= strlen(name);
|
|
if(a>1 && name[a-1]=='.') return;
|
|
|
|
while(a--) {
|
|
if( name[a]=='.' ) {
|
|
left[a]= 0;
|
|
*nr= atol(name+a+1);
|
|
return;
|
|
}
|
|
if( isdigit(name[a])==0 ) break;
|
|
|
|
left[a]= 0;
|
|
}
|
|
strcpy(left, name);
|
|
}
|
|
|
|
static void sort_alpha_id(ListBase *lb, ID *id)
|
|
{
|
|
ID *idtest;
|
|
|
|
/* insert alphabetically */
|
|
if(lb->first!=lb->last) {
|
|
BLI_remlink(lb, id);
|
|
|
|
idtest= lb->first;
|
|
while(idtest) {
|
|
if(BLI_strcasecmp(idtest->name, id->name)>0 || idtest->lib) {
|
|
BLI_insertlinkbefore(lb, idtest, id);
|
|
break;
|
|
}
|
|
idtest= idtest->next;
|
|
}
|
|
/* as last */
|
|
if(idtest==0) {
|
|
BLI_addtail(lb, id);
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
int new_id(ListBase *lb, ID *id, const char *tname)
|
|
/* only for local blocks: external en indirect blocks already have a unique ID */
|
|
/* return 1: created a new name */
|
|
{
|
|
ID *idtest;
|
|
int nr= 0, nrtest, maxtest=32, a;
|
|
char aname[32], *name, left[24], leftest[24], in_use[32];
|
|
|
|
/* - split name
|
|
* - search
|
|
*/
|
|
|
|
if(id->lib) return 0;
|
|
|
|
if(tname==0) name= id->name+2;
|
|
else {
|
|
/* tname can be const */
|
|
strncpy(aname, tname, 21);
|
|
name= aname;
|
|
|
|
if( strlen(name) > 21 ) name[21]= 0;
|
|
}
|
|
|
|
if(lb==0) lb= wich_libbase(G.main, GS(id->name));
|
|
|
|
/* phase 1: id already exists? */
|
|
idtest= lb->first;
|
|
while(idtest) {
|
|
|
|
if(id!=idtest && idtest->lib==0) {
|
|
|
|
/* do not test alphabetic! */
|
|
/* optimized */
|
|
if( idtest->name[2] == name[0] ) {
|
|
if(strcmp(name, idtest->name+2)==0) break;
|
|
}
|
|
}
|
|
|
|
idtest= idtest->next;
|
|
}
|
|
|
|
/* if there is no double return */
|
|
if(idtest==0) {
|
|
strcpy(id->name+2, name);
|
|
return 0;
|
|
}
|
|
|
|
memset(in_use, 0, maxtest);
|
|
|
|
splitIDname(name, left, &nr);
|
|
if(nr>999 && strlen(left)>16) left[16]= 0;
|
|
else if(strlen(left)>17) left[17]= 0;
|
|
|
|
|
|
idtest= lb->first;
|
|
while(idtest) {
|
|
|
|
if(id!=idtest && idtest->lib==0) {
|
|
|
|
splitIDname(idtest->name+2, leftest, &nrtest);
|
|
if(strcmp(left, leftest)==0) {
|
|
|
|
if(nrtest<maxtest) in_use[nrtest]= 1;
|
|
if(nr <= nrtest) nr= nrtest+1;
|
|
}
|
|
}
|
|
|
|
idtest= idtest->next;
|
|
}
|
|
|
|
for(a=0; a<maxtest; a++) {
|
|
if(a>=nr) break;
|
|
if( in_use[a]==0 ) {
|
|
nr= a;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if(nr==0) sprintf(id->name+2, "%s", left);
|
|
else {
|
|
if (nr >= 1000 && strlen(left) > 16) {
|
|
// this would overflow name buffer
|
|
left[16]= 0;
|
|
return (new_id(lb, id, left));
|
|
}
|
|
/* this format specifier is from hell... */
|
|
sprintf(id->name+2, "%s.%.3d", left, nr);
|
|
}
|
|
|
|
sort_alpha_id(lb, id);
|
|
|
|
return 1;
|
|
}
|
|
|
|
// next to indirect usage in read/writefile also in editobject.c scene.c
|
|
void clear_id_newpoins()
|
|
{
|
|
ListBase *lbarray[MAX_LIBARRAY];
|
|
ID *id;
|
|
int a;
|
|
|
|
a= set_listbasepointers(G.main, lbarray);
|
|
while(a--) {
|
|
id= lbarray[a]->first;
|
|
while(id) {
|
|
id->newid= 0;
|
|
id->flag &= ~LIB_NEW;
|
|
id= id->next;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* only for library fixes */
|
|
static void image_fix_relative_path(Image *ima)
|
|
{
|
|
if(ima->id.lib==NULL) return;
|
|
if(strncmp(ima->name, "//", 2)==0) {
|
|
BLI_convertstringcode(ima->name, ima->id.lib->filename, 0);
|
|
BLI_makestringcode(G.sce, ima->name);
|
|
}
|
|
}
|
|
|
|
#define LIBTAG(a) if(a && a->id.lib) {a->id.flag &=~LIB_INDIRECT; a->id.flag |= LIB_EXTERN;}
|
|
|
|
static void lib_indirect_test_id(ID *id)
|
|
{
|
|
|
|
if(id->lib)
|
|
return;
|
|
|
|
if(GS(id->name)==ID_OB) {
|
|
Object *ob= (Object *)id;
|
|
bActionStrip *strip;
|
|
Mesh *me;
|
|
PartEff *paf;
|
|
int a;
|
|
|
|
for (strip=ob->nlastrips.first; strip; strip=strip->next){
|
|
LIBTAG(strip->object);
|
|
LIBTAG(strip->act);
|
|
LIBTAG(strip->ipo);
|
|
}
|
|
|
|
for(a=0; a<ob->totcol; a++) {
|
|
LIBTAG(ob->mat[a]);
|
|
}
|
|
|
|
paf = give_parteff(ob);
|
|
if (paf)
|
|
LIBTAG(paf->group);
|
|
|
|
LIBTAG(ob->dup_group);
|
|
me= ob->data;
|
|
LIBTAG(me);
|
|
}
|
|
}
|
|
|
|
|
|
/* if lib!=NULL, only all from lib local */
|
|
void all_local(Library *lib)
|
|
{
|
|
ListBase *lbarray[MAX_LIBARRAY], tempbase={0, 0};
|
|
ID *id, *idn;
|
|
int a;
|
|
|
|
a= set_listbasepointers(G.main, lbarray);
|
|
while(a--) {
|
|
id= lbarray[a]->first;
|
|
|
|
while(id) {
|
|
id->newid= NULL;
|
|
idn= id->next; /* id is possibly being inserted again */
|
|
|
|
if(id->flag & (LIB_EXTERN|LIB_INDIRECT|LIB_NEW)) {
|
|
if(lib==NULL || id->lib==lib) {
|
|
id->flag &= ~(LIB_EXTERN|LIB_INDIRECT|LIB_NEW);
|
|
|
|
if(id->lib) {
|
|
/* relative file patch */
|
|
if(GS(id->name)==ID_IM)
|
|
image_fix_relative_path((Image *)id);
|
|
|
|
id->lib= NULL;
|
|
new_id(lbarray[a], id, 0); /* new_id only does it with double names */
|
|
sort_alpha_id(lbarray[a], id);
|
|
}
|
|
}
|
|
}
|
|
id= idn;
|
|
}
|
|
|
|
/* patch2: make it aphabetically */
|
|
while( (id=tempbase.first) ) {
|
|
BLI_remlink(&tempbase, id);
|
|
BLI_addtail(lbarray[a], id);
|
|
new_id(lbarray[a], id, 0);
|
|
}
|
|
}
|
|
|
|
/* patch 3: make sure library data isn't indirect falsely... */
|
|
a= set_listbasepointers(G.main, lbarray);
|
|
while(a--) {
|
|
for(id= lbarray[a]->first; id; id=id->next)
|
|
lib_indirect_test_id(id);
|
|
}
|
|
}
|
|
|
|
|
|
void test_idbutton(char *name)
|
|
{
|
|
/* called from buttons: when name already exists: call new_id */
|
|
ListBase *lb;
|
|
ID *idtest;
|
|
|
|
|
|
lb= wich_libbase(G.main, GS(name-2) );
|
|
if(lb==0) return;
|
|
|
|
/* search for id */
|
|
idtest= lb->first;
|
|
while(idtest) {
|
|
if( strcmp(idtest->name+2, name)==0) break;
|
|
idtest= idtest->next;
|
|
}
|
|
|
|
if(idtest) if( new_id(lb, idtest, name)==0 ) sort_alpha_id(lb, idtest);
|
|
}
|
|
|
|
void rename_id(ID *id, char *name)
|
|
{
|
|
ListBase *lb;
|
|
|
|
strncpy(id->name+2, name, 21);
|
|
lb= wich_libbase(G.main, GS(id->name) );
|
|
|
|
new_id(lb, id, name);
|
|
}
|
|
|