Further information is available here: http://wiki.blender.org/bin/view.pl/Blenderdev/UnicodeFont3D Shortlist of features: - Unicode character support for Font3D - UI to select characters from Unicode character list - UI to select Unicode table areas - Optimized character loading (Load only those characters which are used in font object) Please test extensively if it breaks anything, try also loading/saving files, packing fonts, etc. The official text regression file in the regression suite should be a good start. Thanks to mikasaari for this very useful addition!
704 lines
14 KiB
C
704 lines
14 KiB
C
|
|
/* blender.c jan 94 MIXED MODEL
|
|
*
|
|
* common help functions and 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 *****
|
|
*/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include <config.h>
|
|
#endif
|
|
|
|
#ifndef WIN32
|
|
#include <unistd.h> // for read close
|
|
#include <sys/param.h> // for MAXPATHLEN
|
|
#else
|
|
#include <io.h> // for open close read
|
|
#endif
|
|
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <fcntl.h> // for open
|
|
|
|
#include "MEM_guardedalloc.h"
|
|
#include "DNA_listBase.h"
|
|
#include "DNA_sdna_types.h"
|
|
#include "DNA_userdef_types.h"
|
|
#include "DNA_object_types.h"
|
|
#include "DNA_curve_types.h"
|
|
|
|
#include "BLI_blenlib.h"
|
|
#include "BLI_dynstr.h"
|
|
|
|
#include "IMB_imbuf_types.h"
|
|
#include "IMB_imbuf.h"
|
|
|
|
#include "DNA_mesh_types.h"
|
|
#include "DNA_screen_types.h"
|
|
|
|
#include "BKE_library.h"
|
|
#include "BKE_blender.h"
|
|
#include "BKE_displist.h"
|
|
#include "BKE_global.h"
|
|
#include "BKE_main.h"
|
|
#include "BKE_object.h"
|
|
#include "BKE_scene.h"
|
|
#include "BKE_curve.h"
|
|
#include "BKE_font.h"
|
|
|
|
#include "BLI_editVert.h"
|
|
|
|
#include "BLO_undofile.h"
|
|
#include "BLO_readfile.h"
|
|
#include "BLO_writefile.h"
|
|
|
|
#include "BKE_bad_level_calls.h" // for freeAllRad editNurb free_editMesh free_editText free_editArmature
|
|
#include "BKE_utildefines.h" // O_BINARY FALSE
|
|
#include "BIF_mainqueue.h" // mainqenter for onload script
|
|
#include "mydevice.h"
|
|
#include "nla.h"
|
|
#include "blendef.h"
|
|
|
|
Global G;
|
|
UserDef U;
|
|
|
|
char versionstr[48]= "";
|
|
|
|
/* ************************************************ */
|
|
/* pushpop facility: to store data temporally, FIFO! */
|
|
|
|
ListBase ppmain={0, 0};
|
|
|
|
typedef struct PushPop {
|
|
struct PushPop *next, *prev;
|
|
void *data;
|
|
int len;
|
|
} PushPop;
|
|
|
|
void pushdata(void *data, int len)
|
|
{
|
|
PushPop *pp;
|
|
|
|
pp= MEM_mallocN(sizeof(PushPop), "pushpop");
|
|
BLI_addtail(&ppmain, pp);
|
|
pp->data= MEM_mallocN(len, "pushpop");
|
|
pp->len= len;
|
|
memcpy(pp->data, data, len);
|
|
}
|
|
|
|
void popfirst(void *data)
|
|
{
|
|
PushPop *pp;
|
|
|
|
pp= ppmain.first;
|
|
if(pp) {
|
|
memcpy(data, pp->data, pp->len);
|
|
BLI_remlink(&ppmain, pp);
|
|
MEM_freeN(pp->data);
|
|
MEM_freeN(pp);
|
|
}
|
|
else printf("error in popfirst\n");
|
|
}
|
|
|
|
void poplast(void *data)
|
|
{
|
|
PushPop *pp;
|
|
|
|
pp= ppmain.last;
|
|
if(pp) {
|
|
memcpy(data, pp->data, pp->len);
|
|
BLI_remlink(&ppmain, pp);
|
|
MEM_freeN(pp->data);
|
|
MEM_freeN(pp);
|
|
}
|
|
else printf("error in poplast\n");
|
|
}
|
|
|
|
void free_pushpop()
|
|
{
|
|
PushPop *pp;
|
|
|
|
pp= ppmain.first;
|
|
while(pp) {
|
|
BLI_remlink(&ppmain, pp);
|
|
MEM_freeN(pp->data);
|
|
MEM_freeN(pp);
|
|
}
|
|
}
|
|
|
|
void pushpop_test()
|
|
{
|
|
if(ppmain.first) printf("pushpop not empty\n");
|
|
free_pushpop();
|
|
}
|
|
|
|
|
|
|
|
/* ********** free ********** */
|
|
|
|
void free_blender(void)
|
|
{
|
|
free_main(G.main);
|
|
G.main= NULL;
|
|
|
|
IMB_freeImBufdata(); /* imbuf lib */
|
|
}
|
|
|
|
void duplicatelist(ListBase *list1, ListBase *list2) /* copy from 2 to 1 */
|
|
{
|
|
struct Link *link1, *link2;
|
|
|
|
list1->first= list1->last= 0;
|
|
|
|
link2= list2->first;
|
|
while(link2) {
|
|
|
|
link1= MEM_dupallocN(link2);
|
|
BLI_addtail(list1, link1);
|
|
|
|
link2= link2->next;
|
|
}
|
|
}
|
|
|
|
static EditMesh theEditMesh;
|
|
|
|
void initglobals(void)
|
|
{
|
|
memset(&G, 0, sizeof(Global));
|
|
|
|
G.editMesh = &theEditMesh;
|
|
memset(G.editMesh, 0, sizeof(G.editMesh));
|
|
|
|
U.savetime= 1;
|
|
|
|
G.main= MEM_callocN(sizeof(Main), "initglobals");
|
|
|
|
strcpy(G.ima, "//");
|
|
|
|
G.version= BLENDER_VERSION;
|
|
|
|
G.order= 1;
|
|
G.order= (((char*)&G.order)[0])?L_ENDIAN:B_ENDIAN;
|
|
|
|
sprintf(versionstr, "www.blender.org %d", G.version);
|
|
|
|
#ifdef _WIN32 // FULLSCREEN
|
|
G.windowstate = G_WINDOWSTATE_USERDEF;
|
|
#endif
|
|
|
|
clear_workob(); /* object.c */
|
|
|
|
G.charstart = 0x0000;
|
|
G.charmin = 0x0000;
|
|
G.charmax = 0xffff;
|
|
}
|
|
|
|
/***/
|
|
|
|
static void clear_global(void)
|
|
{
|
|
extern short winqueue_break; /* screen.c */
|
|
|
|
freeAllRad();
|
|
free_main(G.main); /* free all lib data */
|
|
freefastshade(); /* othwerwise old lamp settings stay active */
|
|
|
|
|
|
/* prevent hanging vars */
|
|
R.backbuf= 0;
|
|
|
|
/* force all queues to be left */
|
|
winqueue_break= 1;
|
|
|
|
if (G.obedit) {
|
|
freeNurblist(&editNurb);
|
|
free_editMesh(G.editMesh);
|
|
free_editText();
|
|
free_editArmature();
|
|
}
|
|
|
|
G.curscreen= NULL;
|
|
G.scene= NULL;
|
|
G.main= NULL;
|
|
|
|
G.obedit= NULL;
|
|
G.saction= NULL;
|
|
G.buts= NULL;
|
|
G.v2d= NULL;
|
|
G.vd= NULL;
|
|
G.soops= NULL;
|
|
G.sima= NULL;
|
|
G.sipo= NULL;
|
|
|
|
G.f &= ~(G_WEIGHTPAINT + G_VERTEXPAINT + G_FACESELECT);
|
|
}
|
|
|
|
/* make sure path names are correct for OS */
|
|
static void clean_paths(Main *main)
|
|
{
|
|
Image *image= main->image.first;
|
|
bSound *sound= main->sound.first;
|
|
Scene *scene= main->scene.first;
|
|
Editing *ed;
|
|
Sequence *seq;
|
|
Strip *strip;
|
|
|
|
|
|
while(image) {
|
|
BLI_clean(image->name);
|
|
image= image->id.next;
|
|
}
|
|
|
|
while(sound) {
|
|
BLI_clean(sound->name);
|
|
sound= sound->id.next;
|
|
}
|
|
|
|
while(scene) {
|
|
ed= scene->ed;
|
|
if(ed) {
|
|
seq= ed->seqbasep->first;
|
|
while(seq) {
|
|
if(seq->plugin) {
|
|
BLI_clean(seq->plugin->name);
|
|
}
|
|
strip= seq->strip;
|
|
while(strip) {
|
|
BLI_clean(strip->dir);
|
|
strip= strip->next;
|
|
}
|
|
seq= seq->next;
|
|
}
|
|
}
|
|
BLI_clean(scene->r.backbuf);
|
|
BLI_clean(scene->r.pic);
|
|
BLI_clean(scene->r.ftype);
|
|
|
|
scene= scene->id.next;
|
|
}
|
|
}
|
|
|
|
static void setup_app_data(BlendFileData *bfd, char *filename)
|
|
{
|
|
Object *ob;
|
|
bScreen *curscreen= NULL;
|
|
Scene *curscene= NULL;
|
|
char mode;
|
|
|
|
/* 'u' = undo save, 'n' = no UI load */
|
|
if(bfd->main->screen.first==NULL) mode= 'u';
|
|
else if(G.fileflags & G_FILE_NO_UI) mode= 'n';
|
|
else mode= 0;
|
|
|
|
clean_paths(bfd->main);
|
|
|
|
/* no load screens? */
|
|
if(mode) {
|
|
/* comes from readfile.c */
|
|
extern void lib_link_screen_restore(Main *, Scene *);
|
|
|
|
SWAP(ListBase, G.main->screen, bfd->main->screen);
|
|
|
|
/* we re-use current screen */
|
|
curscreen= G.curscreen;
|
|
/* but use new Scene pointer */
|
|
curscene= bfd->curscene;
|
|
if(curscene==NULL) curscene= bfd->main->scene.first;
|
|
/* and we enforce curscene to be in current screen */
|
|
curscreen->scene= curscene;
|
|
|
|
/* clear_global will free G.main, here we can still restore pointers */
|
|
lib_link_screen_restore(bfd->main, curscene);
|
|
}
|
|
|
|
clear_global();
|
|
|
|
if(mode!='u') G.save_over = 1;
|
|
|
|
G.main= bfd->main;
|
|
if (bfd->user) {
|
|
|
|
/* only here free userdef themes... */
|
|
BLI_freelistN(&U.themes);
|
|
|
|
U= *bfd->user;
|
|
MEM_freeN(bfd->user);
|
|
|
|
}
|
|
|
|
/* case G_FILE_NO_UI or no screens in file */
|
|
if(mode) {
|
|
G.curscreen= curscreen;
|
|
G.scene= curscene;
|
|
}
|
|
else {
|
|
G.winpos= bfd->winpos;
|
|
G.displaymode= bfd->displaymode;
|
|
G.fileflags= bfd->fileflags;
|
|
G.curscreen= bfd->curscreen;
|
|
G.scene= G.curscreen->scene;
|
|
}
|
|
/* this can happen when active scene was lib-linked, and doesnt exist anymore */
|
|
if(G.scene==NULL) {
|
|
G.scene= G.main->scene.first;
|
|
G.curscreen->scene= G.scene;
|
|
}
|
|
|
|
/* special cases, override loaded flags: */
|
|
if (G.f & G_DEBUG) bfd->globalf |= G_DEBUG;
|
|
else bfd->globalf &= ~G_DEBUG;
|
|
if (!(G.f & G_DOSCRIPTLINKS)) bfd->globalf &= ~G_DOSCRIPTLINKS;
|
|
|
|
G.f= bfd->globalf;
|
|
|
|
/* last stage of do_versions actually, update objects (like recalc poses) */
|
|
for(ob= G.main->object.first; ob; ob= ob->id.next) {
|
|
if(ob->recalc) object_handle_update(ob);
|
|
}
|
|
|
|
if (!G.background) {
|
|
setscreen(G.curscreen);
|
|
}
|
|
/* baseflags */
|
|
set_scene_bg(G.scene);
|
|
|
|
if (G.f & G_DOSCRIPTLINKS) {
|
|
/* there's an onload scriptlink to execute in screenmain */
|
|
mainqenter(ONLOAD_SCRIPT, 1);
|
|
}
|
|
|
|
strcpy(G.sce, filename);
|
|
strcpy(G.main->name, filename); /* is guaranteed current file */
|
|
|
|
MEM_freeN(bfd);
|
|
}
|
|
|
|
/* returns:
|
|
0: no load file
|
|
1: OK
|
|
2: OK, and with new user settings
|
|
*/
|
|
|
|
int BKE_read_file(char *dir, void *type_r)
|
|
{
|
|
BlendReadError bre;
|
|
BlendFileData *bfd;
|
|
int retval= 1;
|
|
|
|
if (!G.background)
|
|
waitcursor(1);
|
|
|
|
bfd= BLO_read_from_file(dir, &bre);
|
|
if (bfd) {
|
|
if(bfd->user) retval= 2;
|
|
if (type_r)
|
|
*((BlenFileType*)type_r)= bfd->type;
|
|
|
|
setup_app_data(bfd, dir);
|
|
} else {
|
|
error("Loading %s failed: %s", dir, BLO_bre_as_string(bre));
|
|
}
|
|
|
|
if (!G.background)
|
|
waitcursor(0);
|
|
|
|
return (bfd?retval:0);
|
|
}
|
|
|
|
int BKE_read_file_from_memory(char* filebuf, int filelength, void *type_r)
|
|
{
|
|
BlendReadError bre;
|
|
BlendFileData *bfd;
|
|
|
|
if (!G.background)
|
|
waitcursor(1);
|
|
|
|
bfd= BLO_read_from_memory(filebuf, filelength, &bre);
|
|
if (bfd) {
|
|
if (type_r)
|
|
*((BlenFileType*)type_r)= bfd->type;
|
|
|
|
setup_app_data(bfd, "<memory>");
|
|
} else {
|
|
error("Loading failed: %s", BLO_bre_as_string(bre));
|
|
}
|
|
|
|
if (!G.background)
|
|
waitcursor(0);
|
|
|
|
return (bfd?1:0);
|
|
}
|
|
|
|
int BKE_read_file_from_memfile(MemFile *memfile)
|
|
{
|
|
BlendReadError bre;
|
|
BlendFileData *bfd;
|
|
|
|
if (!G.background)
|
|
waitcursor(1);
|
|
|
|
bfd= BLO_read_from_memfile(memfile, &bre);
|
|
if (bfd) {
|
|
setup_app_data(bfd, "<memory>");
|
|
} else {
|
|
error("Loading failed: %s", BLO_bre_as_string(bre));
|
|
}
|
|
|
|
if (!G.background)
|
|
waitcursor(0);
|
|
|
|
return (bfd?1:0);
|
|
}
|
|
|
|
|
|
/* ***************** GLOBAL UNDO *************** */
|
|
|
|
#define UNDO_DISK 0
|
|
|
|
#define MAXUNDONAME 64
|
|
typedef struct UndoElem {
|
|
struct UndoElem *next, *prev;
|
|
char str[FILE_MAXDIR+FILE_MAXFILE];
|
|
char name[MAXUNDONAME];
|
|
MemFile memfile;
|
|
} UndoElem;
|
|
|
|
#define MAXUNDO 32
|
|
static ListBase undobase={NULL, NULL};
|
|
static UndoElem *curundo= NULL;
|
|
|
|
|
|
static int read_undosave(UndoElem *uel)
|
|
{
|
|
char scestr[FILE_MAXDIR+FILE_MAXFILE];
|
|
int success=0, fileflags;
|
|
|
|
strcpy(scestr, G.sce); /* temporal store */
|
|
fileflags= G.fileflags;
|
|
G.fileflags |= G_FILE_NO_UI;
|
|
|
|
if(UNDO_DISK)
|
|
success= BKE_read_file(uel->str, NULL);
|
|
else
|
|
success= BKE_read_file_from_memfile(&uel->memfile);
|
|
|
|
/* restore */
|
|
strcpy(G.sce, scestr);
|
|
G.fileflags= fileflags;
|
|
|
|
return success;
|
|
}
|
|
|
|
/* name can be a dynamic string */
|
|
void BKE_write_undo(char *name)
|
|
{
|
|
int nr, success;
|
|
UndoElem *uel;
|
|
|
|
if( (U.uiflag & USER_GLOBALUNDO)==0) return;
|
|
|
|
/* remove all undos after (also when curundo==NULL) */
|
|
while(undobase.last != curundo) {
|
|
uel= undobase.last;
|
|
BLI_remlink(&undobase, uel);
|
|
BLO_free_memfile(&uel->memfile);
|
|
MEM_freeN(uel);
|
|
}
|
|
|
|
/* make new */
|
|
curundo= uel= MEM_callocN(sizeof(UndoElem), "undo file");
|
|
strncpy(uel->name, name, MAXUNDONAME-1);
|
|
BLI_addtail(&undobase, uel);
|
|
|
|
/* and limit amount to the maximum */
|
|
nr= 0;
|
|
uel= undobase.last;
|
|
while(uel) {
|
|
nr++;
|
|
if(nr==MAXUNDO) break;
|
|
uel= uel->prev;
|
|
}
|
|
if(uel) {
|
|
while(undobase.first!=uel) {
|
|
UndoElem *first= undobase.first;
|
|
BLI_remlink(&undobase, first);
|
|
/* the merge is because of compression */
|
|
BLO_merge_memfile(&first->memfile, &first->next->memfile);
|
|
MEM_freeN(first);
|
|
}
|
|
}
|
|
|
|
|
|
/* disk save version */
|
|
if(UNDO_DISK) {
|
|
static int counter= 0;
|
|
char *err, tstr[FILE_MAXDIR+FILE_MAXFILE];
|
|
char numstr[32];
|
|
|
|
/* calculate current filename */
|
|
counter++;
|
|
counter= counter % MAXUNDO;
|
|
|
|
sprintf(numstr, "%d.blend", counter);
|
|
BLI_make_file_string("/", tstr, U.tempdir, numstr);
|
|
|
|
success= BLO_write_file(tstr, G.fileflags, &err);
|
|
|
|
strcpy(curundo->str, tstr);
|
|
}
|
|
else {
|
|
MemFile *prevfile=NULL;
|
|
char *err;
|
|
|
|
if(curundo->prev) prevfile= &(curundo->prev->memfile);
|
|
|
|
success= BLO_write_file_mem(prevfile, &curundo->memfile, G.fileflags, &err);
|
|
|
|
}
|
|
}
|
|
|
|
/* 1= an undo, -1 is a redo. we have to make sure 'curundo' remains at current situation */
|
|
void BKE_undo_step(int step)
|
|
{
|
|
|
|
if(step==0) {
|
|
read_undosave(curundo);
|
|
}
|
|
else if(step==1) {
|
|
/* curundo should never be NULL, after restart or load file it should call undo_save */
|
|
if(curundo==NULL || curundo->prev==NULL) error("No undo available");
|
|
else {
|
|
if(G.f & G_DEBUG) printf("undo %s\n", curundo->name);
|
|
curundo= curundo->prev;
|
|
read_undosave(curundo);
|
|
}
|
|
}
|
|
else {
|
|
|
|
/* curundo has to remain current situation! */
|
|
|
|
if(curundo==NULL || curundo->next==NULL) error("No redo available");
|
|
else {
|
|
read_undosave(curundo->next);
|
|
curundo= curundo->next;
|
|
if(G.f & G_DEBUG) printf("redo %s\n", curundo->name);
|
|
}
|
|
}
|
|
}
|
|
|
|
void BKE_reset_undo(void)
|
|
{
|
|
UndoElem *uel;
|
|
|
|
uel= undobase.first;
|
|
while(uel) {
|
|
BLO_free_memfile(&uel->memfile);
|
|
uel= uel->next;
|
|
}
|
|
|
|
BLI_freelistN(&undobase);
|
|
curundo= NULL;
|
|
}
|
|
|
|
/* based on index nr it does a restore */
|
|
void BKE_undo_number(int nr)
|
|
{
|
|
UndoElem *uel;
|
|
int a=1;
|
|
|
|
for(uel= undobase.first; uel; uel= uel->next, a++) {
|
|
if(a==nr) break;
|
|
}
|
|
curundo= uel;
|
|
BKE_undo_step(0);
|
|
}
|
|
|
|
char *BKE_undo_menu_string(void)
|
|
{
|
|
UndoElem *uel;
|
|
DynStr *ds= BLI_dynstr_new();
|
|
char *menu;
|
|
|
|
BLI_dynstr_append(ds, "Global Undo History %t");
|
|
|
|
for(uel= undobase.first; uel; uel= uel->next) {
|
|
BLI_dynstr_append(ds, "|");
|
|
BLI_dynstr_append(ds, uel->name);
|
|
}
|
|
|
|
menu= BLI_dynstr_get_cstring(ds);
|
|
BLI_dynstr_free(ds);
|
|
|
|
return menu;
|
|
}
|
|
|
|
/* saves quit.blend */
|
|
void BKE_undo_save_quit(void)
|
|
{
|
|
UndoElem *uel;
|
|
MemFileChunk *chunk;
|
|
int file;
|
|
char str[FILE_MAXDIR+FILE_MAXFILE];
|
|
|
|
if( (U.uiflag & USER_GLOBALUNDO)==0) return;
|
|
|
|
uel= curundo;
|
|
if(uel==NULL) {
|
|
printf("No undo buffer to save recovery file\n");
|
|
return;
|
|
}
|
|
|
|
/* no undo state to save */
|
|
if(undobase.first==undobase.last) return;
|
|
|
|
BLI_make_file_string("/", str, U.tempdir, "quit.blend");
|
|
|
|
file = open(str,O_BINARY+O_WRONLY+O_CREAT+O_TRUNC, 0666);
|
|
if(file == -1) {
|
|
printf("Unable to save %s\n", str);
|
|
return;
|
|
}
|
|
|
|
chunk= uel->memfile.chunks.first;
|
|
while(chunk) {
|
|
if( write(file, chunk->buf, chunk->size) != chunk->size) break;
|
|
chunk= chunk->next;
|
|
}
|
|
|
|
close(file);
|
|
|
|
if(chunk) printf("Unable to save %s\n", str);
|
|
else printf("Saved session recovery to %s\n", str);
|
|
}
|
|
|