/* library.c aug 94 MIXED MODEL * * jan 95 * * afhandeling ID's en libraries * allocceren en vrijgeven alle library data * * $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 ***** */ #include #include #include #include #ifdef WIN32 #include "BLI_winstuff.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_ika_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 "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_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_image.h" #include "BKE_ika.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" #define MAX_IDPUP 30 /* was 24 */ #define MAX_LIBARRAY 100 /* was 30 */ /* ************* ALGEMEEN ************************ */ 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_IK: return &(mainlib->ika); 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_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); } return 0; } int set_listbasepointers(Main *main, ListBase **lb) { /* BACKWARDS! let op volgorde van vrijgeven! (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->ika); lb[12]= &(main->wave); lb[13]= &(main->latt); lb[14]= &(main->lamp); lb[15]= &(main->camera); lb[16]= &(main->world); lb[17]= &(main->screen); lb[18]= &(main->object); lb[19]= &(main->scene); lb[20]= &(main->library); lb[21]= &(main->text); lb[22]= &(main->sound); lb[23]= &(main->group); lb[24]= samples; lb[25]= 0; return 25; } /* *********** ALLOC EN FREE ***************** free_libblock(ListBase *lb, ID *id ) lijstbasis en datablok geven, alleen ID wordt uitgelezen void *alloc_libblock(ListBase *lb, type, name) hangt in lijst en geeft nieuw ID ***************************** */ static ID *alloc_libblock_notest(short type) { ID *id= 0; 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_IK: id= MEM_callocN(sizeof(Ika), "ika"); 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_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; } return id; } // used everywhere in blenkernel and text.c void *alloc_libblock(ListBase *lb, short type, char *name) { ID *id= 0; id= alloc_libblock_notest(type); if(id) { BLI_addtail(lb, id); id->us= 1; *( (short *)id->name )= type; new_id(lb, id, name); /* alfabetisch opnieuw invoegen: zit in new_id */ } return id; } /* GS reads the memory pointed at in a specific ordering. There are, * however two definitions for it. I have jotted them down here, both, * but I think the first one is actually used. The thing is that * big-endian systems might read this the wrong way round. OTOH, we * constructed the IDs that are read out with this macro explicitly as * well. I expect we'll sort it out soon... */ /* from blendef: */ #define GS(a) (*((short *)(a))) /* from misc_util: flip the bytes from x */ /*#define GS(x) (((unsigned char *)(x))[0] << 8 | ((unsigned char *)(x))[1]) */ // 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 uit 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_IK: free_ika((Ika *)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(id); */ /* cast wasn't here before... spooky... */ 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_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; } BLI_remlink(lb, id); MEM_freeN(id); /* should not be here!! this is an interface-thing */ allspace(OOPS_TEST, 0); } 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) { /* ook aanroepen bij file inlezen, 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 ************************ */ // only used in exotic.c ID *find_id(char *type, char *name) /* type: "OB" of "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; /* 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 (id->us<0) sprintf(buf, "-1W "); else if (!id->lib && !isfake && id->us) sprintf(buf, " "); 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); *nr= -1; if (nids>MAX_IDPUP) { BLI_dynstr_append(pupds, "DataBrowse %x-2"); } else { ID *id; for (i=0, id= lb->first; id; id= id->next, i++) { char buf[32]; if (id==link) *nr= i+1; get_flags_for_id(id, buf); BLI_dynstr_append(pupds, buf); BLI_dynstr_append(pupds, id->name+2); 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; get_flags_for_id(id, buf); BLI_dynstr_append(pupds, buf); BLI_dynstr_append(pupds, id->name+2); if(id->next) BLI_dynstr_append(pupds, "|"); i++; } } } } // used by headerbuttons.c buttons.c editobject.c editseq.c 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|"); } IDnames_to_dyn_pupstring(pupds, lb, link, nr); if (extraops) { if (BLI_dynstr_get_len(pupds)) BLI_dynstr_append(pupds, "|"); BLI_dynstr_append(pupds, extraops); } *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|"); } IPOnames_to_dyn_pupstring(pupds, lb, link, nr, blocktype); if (extraops) { if (BLI_dynstr_get_len(pupds)) BLI_dynstr_append(pupds, "|"); BLI_dynstr_append(pupds, extraops); } *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; /* alfabetisch opnieuw invoegen */ if(lb->first!=lb->last) { BLI_remlink(lb, id); idtest= lb->first; while(idtest) { if(strcasecmp(idtest->name, id->name)>0 || idtest->lib) { BLI_insertlinkbefore(lb, idtest, id); break; } idtest= idtest->next; } /* als laatste */ if(idtest==0) { BLI_addtail(lb, id); } } } int new_id(ListBase *lb, ID *id, char *tname) /* alleen locale blokken: externe en indirekte hebben al een unieke ID */ /* return 1: nieuwe naam gemaakt */ { ID *idtest; int nr= 0, nrtest, maxtest=32, a; char aname[32], *name, left[24], leftest[24], in_use[32]; /* - naam splitsen * - zoeken */ 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)); /* eerste fase: bestaat de id al? */ idtest= lb->first; while(idtest) { if(id!=idtest && idtest->lib==0) { /* niet alphabetic testen! */ /* optim */ 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(nrtestnext; } for(a=0; 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 fucked... */ sprintf(id->name+2, "%s.%0.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; } } } void all_local(void) { 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= 0; id->flag &= ~(LIB_EXTERN|LIB_INDIRECT|LIB_NEW); idn= id->next; /* id wordt mogelijk opnieuw ingevoegd */ if(id->lib) { id->lib= 0; new_id(lbarray[a], id, 0); /* new_id doet dit alleen bij dubbele namen */ sort_alpha_id(lbarray[a], id); } else { /* patch: testen of de zaak wel alphabetisch is */ /* if(idn) { if(strcasecmp(id->name, idn->name)>0) { remlink(lbarray[a], id); addtail(&tempbase, id); } else if(id->prev) { idp= id->prev; if(strcasecmp(idp->name, id->name)>0) { remlink(lbarray[a], id); addtail(&tempbase, id); } } } */ } id= idn; } /* patch2: zorgen dat de zaak wel alphabetisch is */ while( (id=tempbase.first) ) { BLI_remlink(&tempbase, id); BLI_addtail(lbarray[a], id); new_id(lbarray[a], id, 0); } } } void test_idbutton(char *name) { /* vanuit buttons: als naam al bestaat: new_id aanroepen */ ListBase *lb; ID *idtest; lb= wich_libbase(G.main, GS(name-2) ); if(lb==0) return; /* zoek welke 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); }