diff options
author | Campbell Barton <ideasman42@gmail.com> | 2007-12-20 13:27:13 +0300 |
---|---|---|
committer | Campbell Barton <ideasman42@gmail.com> | 2007-12-20 13:27:13 +0300 |
commit | 5925fe36e0a176a73e65950f7ed5eef510c12f02 (patch) | |
tree | dc047e46e29c585820cd354704d2e7dc86188435 /source | |
parent | 0a682cb50fe46a0e4327546cb74370c8e88fa231 (diff) |
Render-farm and file utils for dealing with external data.
Useful to use before sending blend files to the renderfarm.
* Make all Paths Relative - makes any absolute paths relative.
* Report Missing Files - creates a textblock listing all missing files.
* Find Missing Files - searches a directory recursively for filenames that dont exist at their current path.
Added a path looper type and functions that currently loop on image, sound, font and external library paths.
Diffstat (limited to 'source')
-rw-r--r-- | source/blender/blenlib/BLI_boxpack2d.h | 5 | ||||
-rw-r--r-- | source/blender/blenlib/BLI_bpath.h | 59 | ||||
-rw-r--r-- | source/blender/blenlib/intern/bpath.c | 485 | ||||
-rw-r--r-- | source/blender/src/header_info.c | 100 |
4 files changed, 626 insertions, 23 deletions
diff --git a/source/blender/blenlib/BLI_boxpack2d.h b/source/blender/blenlib/BLI_boxpack2d.h index b5cf9cd81e9..50b864df5f8 100644 --- a/source/blender/blenlib/BLI_boxpack2d.h +++ b/source/blender/blenlib/BLI_boxpack2d.h @@ -28,10 +28,7 @@ * Contributor(s): Campbell Barton * * ***** END GPL/BL DUAL LICENSE BLOCK ***** - * - * The old math stuff from Ton. These will slowly phase out in favour - * of MTC calls. (or even MoTO :) ) - * */ + */ /* Box Packer */ diff --git a/source/blender/blenlib/BLI_bpath.h b/source/blender/blenlib/BLI_bpath.h new file mode 100644 index 00000000000..e2d7d23410c --- /dev/null +++ b/source/blender/blenlib/BLI_bpath.h @@ -0,0 +1,59 @@ +/** + * + * ***** 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): Campbell Barton + * + * ***** END GPL/BL DUAL LICENSE BLOCK ***** + */ + +/* Based on ghash, difference is ghash is not a fixed size, + * so for BPath we dont need to malloc */ + +struct BPathIterator { + char* path; + char* lib; + char* name; + void* data; + int len; + int type; +}; + +void BLI_bpathIterator_init (struct BPathIterator *bpi); +char* BLI_bpathIterator_getPath (struct BPathIterator *bpi); +char* BLI_bpathIterator_getLib (struct BPathIterator *bpi); +char* BLI_bpathIterator_getName (struct BPathIterator *bpi); +int BLI_bpathIterator_getType (struct BPathIterator *bpi); +int BLI_bpathIterator_getPathMaxLen(struct BPathIterator *bpi); +void BLI_bpathIterator_step (struct BPathIterator *bpi); +int BLI_bpathIterator_isDone (struct BPathIterator *bpi); +void BLI_bpathIterator_copyPathExpanded( struct BPathIterator *bpi, char *path_expanded); + +/* high level funcs */ + +/* creates a text file with missing files if there are any */ +struct Text * checkMissingFiles(void); +void makeFilesRelative(int *tot, int *changed, int *failed, int *linked); +void findMissingFiles(char *str); diff --git a/source/blender/blenlib/intern/bpath.c b/source/blender/blenlib/intern/bpath.c new file mode 100644 index 00000000000..598b5720b65 --- /dev/null +++ b/source/blender/blenlib/intern/bpath.c @@ -0,0 +1,485 @@ +/** + * + * ***** 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): Campbell barton + * + * ***** END GPL/BL DUAL LICENSE BLOCK ***** + */ + +#include "BLI_bpath.h" +#include "BKE_global.h" +#include "DNA_ID.h" /* Library */ +#include "DNA_vfont_types.h" +#include "DNA_image_types.h" +#include "DNA_sound_types.h" +#include "DNA_scene_types.h" /* to get the current frame */ +#include <stdlib.h> +#include <string.h> + +#include "BKE_main.h" /* so we can access G.main->*.first */ +#include "BKE_image.h" /* so we can check the image's type */ + +#include "blendef.h" +#include "BKE_utildefines.h" + +/* for writing to a textblock */ +#include "BKE_text.h" +#include "BLI_blenlib.h" +#include "DNA_text_types.h" + +/* path/file handeling stuff */ +#ifndef WIN32 + #include <dirent.h> +#else + #include "BLI_winstuff.h" +#endif + +#include <sys/stat.h> +#include <sys/types.h> + +#include <math.h> +#include <fcntl.h> +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <unistd.h> + +#define FILE_MAX 240 + + +/* TODO - BPATH_PLUGIN, BPATH_SEQ */ +enum BPathTypes { + BPATH_IMAGE = 0, + BPATH_SOUND, + BPATH_FONT, + BPATH_LIB, + + BPATH_DONE +}; + + +void BLI_bpathIterator_init( struct BPathIterator *bpi ) { + bpi->type = BPATH_IMAGE; + bpi->data = NULL; + BLI_bpathIterator_step(bpi); +} + +char* BLI_bpathIterator_getPath( struct BPathIterator *bpi) { + return bpi->path; +} +void BLI_bpathIterator_copyPathExpanded( struct BPathIterator *bpi, char *path_expanded) { + char *filepath, *libpath; + + filepath = BLI_bpathIterator_getPath(bpi); + libpath = BLI_bpathIterator_getLib(bpi); + + BLI_strncpy(path_expanded, filepath, FILE_MAXDIR*2); + + if (libpath) { /* check the files location relative to its library path */ + BLI_convertstringcode(path_expanded, libpath, G.scene->r.cfra); + } else { /* local data, use the blend files path */ + BLI_convertstringcode(path_expanded, G.sce, G.scene->r.cfra); + } +} +char* BLI_bpathIterator_getLib( struct BPathIterator *bpi) { + return bpi->lib; +} +char* BLI_bpathIterator_getName( struct BPathIterator *bpi) { + return bpi->name; +} +int BLI_bpathIterator_getType( struct BPathIterator *bpi) { + return bpi->type; +} +int BLI_bpathIterator_getPathMaxLen( struct BPathIterator *bpi) { + return bpi->len; +} + +/* gets the first or the next image that has a path - not a viewer node or generated image */ +static struct Image *ima_getpath__internal(struct Image *ima, int step_next) { + if (ima==NULL) + return NULL; + + if (step_next) + ima = ima->id.next; + + while (ima) { + if (ima->packedfile==NULL && ELEM3(ima->source, IMA_SRC_FILE, IMA_SRC_MOVIE, IMA_SRC_SEQUENCE)) + break; + /* image is not a image with a path, skip it */ + ima = ima->id.next; + } + return ima; +} + +static struct VFont *vf_getpath__internal(struct VFont *vf, int step_next) { + if (vf==NULL) + return NULL; + + if (step_next) + vf = vf->id.next; + + while (vf) { + if (vf->packedfile==NULL && BLI_streq(vf->name, "<builtin>")==0) { + break; + } + + /* font with no path, skip it */ + vf = vf->id.next; + } + return vf; +} + +static struct bSound *snd_getpath__internal(struct bSound *snd, int step_next) { + if (snd==NULL) + return NULL; + + if (step_next) + snd = snd->id.next; + + while (snd) { + if (snd->packedfile==NULL) { + break; + } + + /* font with no path, skip it */ + snd = snd->id.next; + } + return snd; +} + +void BLI_bpathIterator_step( struct BPathIterator *bpi) { + while (bpi->type != BPATH_DONE) { + + if ((bpi->type) == BPATH_IMAGE) { + /*if (bpi->data) bpi->data = ((ID *)bpi->data)->next;*/ + if (bpi->data) bpi->data = ima_getpath__internal( (Image *)bpi->data, 1 ); /* must skip images that have no path */ + else bpi->data = ima_getpath__internal(G.main->image.first, 0); + + if (bpi->data) { + /* get the path info from this datatype */ + Image *ima = (Image *)bpi->data; + + bpi->lib = ima->id.lib ? ima->id.lib->filename : NULL; + bpi->path = ima->name; + bpi->name = ima->id.name+2; + bpi->len = sizeof(ima->name); + + /* we are done, advancing to the next item, this type worked fine */ + break; + + } else { + bpi->type+=1; /* advance to the next type */ + } + + + } else if ((bpi->type) == BPATH_SOUND) { + if (bpi->data) bpi->data = snd_getpath__internal( (bSound *)bpi->data, 1 ); /* must skip images that have no path */ + else bpi->data = snd_getpath__internal(G.main->sound.first, 0); + + if (bpi->data) { + /* get the path info from this datatype */ + bSound *snd = (bSound *)bpi->data; + + bpi->lib = snd->id.lib ? snd->id.lib->filename : NULL; + bpi->path = snd->sample->name; + bpi->name = snd->id.name+2; + bpi->len = sizeof(snd->sample->name); + + /* we are done, advancing to the next item, this type worked fine */ + break; + } else { + bpi->type+=1; /* advance to the next type */ + } + + + } else if ((bpi->type) == BPATH_FONT) { + + if (bpi->data) bpi->data = vf_getpath__internal( (VFont *)bpi->data, 1 ); + else bpi->data = vf_getpath__internal( G.main->vfont.first, 0 ); + + if (bpi->data) { + /* get the path info from this datatype */ + VFont *vf = (VFont *)bpi->data; + + bpi->lib = vf->id.lib ? vf->id.lib->filename : NULL; + bpi->path = vf->name; + bpi->name = vf->id.name+2; + bpi->len = sizeof(vf->name); + + /* we are done, advancing to the next item, this type worked fine */ + break; + } else { + bpi->type+=1; /* advance to the next type */ + } + + + } else if ((bpi->type) == BPATH_LIB) { + + if (bpi->data) bpi->data = ((ID *)bpi->data)->next; + else bpi->data = G.main->library.first; + + if (bpi->data) { + /* get the path info from this datatype */ + Library *lib = (Library *)bpi->data; + + bpi->lib = NULL; + bpi->path = lib->name; + bpi->name = NULL; + bpi->len = sizeof(lib->name); + + /* we are done, advancing to the next item, this type worked fine */ + break; + } else { + bpi->type+=1; /* advance to the next type */ + } + } + } +} + +int BLI_bpathIterator_isDone( struct BPathIterator *bpi) { + return bpi->type==BPATH_DONE; +} + +/* include the path argument */ +static void bpathToText(Text *btxt, struct BPathIterator *bpi) +{ + char *name; + char path_expanded[FILE_MAXDIR*2]; + + switch(BLI_bpathIterator_getType(bpi)) { + case BPATH_IMAGE: + txt_insert_buf( btxt, "Image \"" ); + break; + case BPATH_SOUND: + txt_insert_buf( btxt, "Sound \"" ); + break; + case BPATH_FONT: + txt_insert_buf( btxt, "Font \"" ); + break; + case BPATH_LIB: + txt_insert_buf( btxt, "Library \"" ); + break; + default: + txt_insert_buf( btxt, "Unknown \"" ); + break; + } + + name = BLI_bpathIterator_getName(bpi); + + if (name) { + txt_insert_buf( btxt, name ); + } + txt_insert_buf( btxt, "\" " ); + + BLI_bpathIterator_copyPathExpanded(bpi, path_expanded); + + txt_insert_buf( btxt, path_expanded ); + txt_insert_buf( btxt, "\n" ); + txt_move_eof( btxt, 0 ); +} + +/* high level function */ +Text *checkMissingFiles(void) { + Text *btxt = NULL; + struct BPathIterator bpi; + + /* be sure there is low chance of the path being too short */ + char filepath_expanded[FILE_MAXDIR*2]; + char *filepath, *libpath; + int files_missing = 0; + + BLI_bpathIterator_init(&bpi); + while (!BLI_bpathIterator_isDone(&bpi)) { + filepath = BLI_bpathIterator_getPath(&bpi); + libpath = BLI_bpathIterator_getLib(&bpi); + + BLI_bpathIterator_copyPathExpanded( &bpi, filepath_expanded ); + + if (!BLI_exists(filepath_expanded)) { + if (!btxt) + btxt = add_empty_text( "missing_files.txt" ); + + bpathToText(btxt, &bpi); + files_missing = 1; + } + BLI_bpathIterator_step(&bpi); + } + return btxt; +} + +/* dont log any errors at the moment, should probably do this */ +void makeFilesRelative(int *tot, int *changed, int *failed, int *linked) { + struct BPathIterator bpi; + char *filepath, *libpath; + + /* be sure there is low chance of the path being too short */ + char filepath_relative[(FILE_MAXDIR * 2) + FILE_MAXFILE]; + + *tot = *changed = *failed = *linked = 0; + + BLI_bpathIterator_init(&bpi); + while (!BLI_bpathIterator_isDone(&bpi)) { + filepath = BLI_bpathIterator_getPath(&bpi); + libpath = BLI_bpathIterator_getLib(&bpi); + + if(strncmp(filepath, "//", 2)==0) { + if (libpath) { /* cant make relative if we are kibrary - TODO, LOG THIS */ + (*linked)++; + } else { /* local data, use the blend files path */ + BLI_strncpy(filepath_relative, filepath, sizeof(filepath_relative)); + BLI_makestringcode(G.sce, filepath_relative); + if (BLI_bpathIterator_getPathMaxLen(&bpi) < strlen(filepath_relative)) { + (*failed)++; + } else { + /* safe to to check the length */ + if(strncmp(filepath_relative, "//", 2)==0) { + (*failed)++; + } else { + strcpy(filepath, filepath_relative); + (*changed)++; + } + } + } + } + + BLI_bpathIterator_step(&bpi); + (*tot)++; + } +} + +/* find this file recursively, use the biggest file so thumbnails dont get used by mistake + - dir: subdir to search + - filename: set this filename + - filesize: filesize for the file +*/ +#define MAX_RECUR 16 +static int findFileRecursive(char *filename_new, const char *dirname, const char *filename, int *filesize, int *recur_depth) +{ + /* file searching stuff */ + DIR *dir; + int file = 0; + struct dirent *de; + struct stat status; + char path[FILE_MAX]; + int size; + + printf("DIR %s\n", dirname); + + dir = opendir(dirname); + + if (dir==0) + return 0; + + if (*filesize == -1) + *filesize = 0; /* dir opened fine */ + + while ((de = readdir(dir)) != NULL) { + + if (strncmp(".", de->d_name, 2)==0 || strncmp("..", de->d_name, 3)==0) + continue; + + BLI_join_dirfile(path, dirname, de->d_name); + + if (stat(path, &status) != 0) + continue; /* cant stat, dont bother with this file, could print debug info here */ + + if (S_ISREG(status.st_mode)) { /* is file */ + if (strncmp(filename, de->d_name, FILE_MAX)==0) { /* name matches */ + /* open the file to read its size */ + file = open(path, O_BINARY|O_RDONLY); + if (file >=0 ) { + size = BLI_filesize(file); + if (size > *filesize) { /* find the biggest file */ + *filesize = size; + BLI_strncpy(filename_new, path, FILE_MAX); + } + close(file); + } + } + } else if (S_ISDIR(status.st_mode)) { /* is subdir */ + if (*recur_depth <= MAX_RECUR) { + (*recur_depth)++; + findFileRecursive(filename_new, path, filename, filesize, recur_depth); + (*recur_depth)--; + } + } + } + closedir(dir); + return 1; +} + +/* high level function - call from fileselector */ +void findMissingFiles(char *str) { + struct BPathIterator bpi; + + /* be sure there is low chance of the path being too short */ + char filepath_expanded[FILE_MAXDIR*2]; + char *filepath, *libpath; + int filesize, recur_depth; + + char dirname[FILE_MAX], filename[FILE_MAX], filename_new[FILE_MAX], dummyname[FILE_MAX]; + + BLI_split_dirfile(str, dirname, dummyname); + + BLI_bpathIterator_init(&bpi); + + while (!BLI_bpathIterator_isDone(&bpi)) { + filepath = BLI_bpathIterator_getPath(&bpi); + libpath = BLI_bpathIterator_getLib(&bpi); + + if (libpath==NULL) { + + BLI_bpathIterator_copyPathExpanded( &bpi, filepath_expanded ); + + if (!BLI_exists(filepath_expanded)) { + /* can the dir be opened? */ + filesize = -1; + recur_depth = 0; + BLI_split_dirfile(filepath, dummyname, filename); /* the file to find */ + + findFileRecursive(filename_new, dirname, filename, &filesize, &recur_depth); + if (filesize == -1) { /* could not open dir */ + printf("Could not open dir \"%s\"\n", dirname); + return; + } + + if (filesize > 0) { + + if (BLI_bpathIterator_getPathMaxLen( &bpi ) < strlen(filename_new)) { + printf("cannot set path \"%s\" too long!", filename_new); + } else { + /* copy the found path into the old one */ + if (G.relbase_valid) + BLI_makestringcode(G.sce, filename_new); + + strcpy( BLI_bpathIterator_getPath( &bpi ), filename_new ); + } + } + } + } + BLI_bpathIterator_step(&bpi); + } +} diff --git a/source/blender/src/header_info.c b/source/blender/src/header_info.c index bf4beacaa58..f3933507a7b 100644 --- a/source/blender/src/header_info.c +++ b/source/blender/src/header_info.c @@ -101,6 +101,7 @@ #include "BLI_arithb.h" #include "BLI_blenlib.h" +#include "BLI_bpath.h" #include "BLO_writefile.h" #include "BSE_editipo.h" @@ -852,19 +853,6 @@ static void do_info_filemenu(void *arg, int event) case 25: BIF_screendump(1); break; - case 10: /* pack data */ - check_packAll(); - break; - case 11: /* unpack to current dir */ - unpackAll(PF_WRITE_LOCAL); - G.fileflags &= ~G_AUTOPACK; - break; - case 12: /* unpack data */ - if (buttons_do_unpack() != RET_CANCEL) { - /* Clear autopack bit only if user selected one of the unpack options */ - G.fileflags &= ~G_AUTOPACK; - } - break; case 13: exit_usiblender(); break; @@ -902,6 +890,7 @@ static void do_info_filemenu(void *arg, int event) U.flag ^= (USER_FILECOMPRESS); break; } + allqueue(REDRAWINFO, 0); } @@ -944,6 +933,81 @@ static uiBlock *info_openrecentmenu(void *arg_unused) return block; } +static void do_info_externalfiles(void *arg, int event) +{ + switch (event) { + + case 1: /* pack data */ + check_packAll(); + break; +#if 0 + case 2: /* unpack to current dir */ + unpackAll(PF_WRITE_LOCAL); + G.fileflags &= ~G_AUTOPACK; + break; +#endif + case 3: /* unpack data */ + if (buttons_do_unpack() != RET_CANCEL) { + /* Clear autopack bit only if user selected one of the unpack options */ + G.fileflags &= ~G_AUTOPACK; + } + break; + case 10: /* make all paths relative */ + { + int tot,changed,failed,linked; + char str[512]; + makeFilesRelative(&tot, &changed, &failed, &linked); + sprintf(str, "Make Relative%%t|Total files %i|Changed %i|Failed %i|Linked %i", tot, changed, failed, linked); + pupmenu(str); + } + break; + case 11: /* check images exist */ + { + /* Its really text but only care about the name */ + ID *btxt = (ID *)checkMissingFiles(); + + if (btxt) { + char str[128]; + sprintf(str, "Missing files listed in Text \"%s\"", btxt->name+2); + error(str); + } else { + okee("No external files missing"); + } + } + break; + case 12: /* search for referenced files that are not available */ + activate_fileselect(FILE_SPECIAL, "Find Missing Files", "", findMissingFiles); + break; + } + + allqueue(REDRAWINFO, 0); +} + +static uiBlock *info_externalfiles(void *arg_unused) +{ + uiBlock *block; + short yco = 20, menuwidth = 120; + + block= uiNewBlock(&curarea->uiblocks, "info_externalfiles", UI_EMBOSSP, UI_HELV, G.curscreen->mainwin); + uiBlockSetButmFunc(block, do_info_externalfiles, NULL); + + uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, "Pack into Blend", 0, yco-=20, 160, 19, NULL, 0.0, 0.0, 1, 1, ""); +#if 0 + uiDefBut(block, BUTM, 1, "Unpack Data to current dir", 0, yco-=20, 160, 19, NULL, 0.0, 0.0, 1, 2, "Removes all packed files from the project and saves them to the current directory"); +#endif + uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, "Unpack into Files...", 0, yco-=20, 160, 19, NULL, 0.0, 0.0, 1, 3, ""); + + uiDefBut(block, SEPR, 0, "", 0, yco-=6, menuwidth, 6, NULL, 0.0, 0.0, 0, 0, ""); + + uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, "Make all Paths Relative", 0, yco-=20, 160, 19, NULL, 0.0, 0.0, 1, 10, ""); + uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, "Report Missing Files", 0, yco-=20, 160, 19, NULL, 0.0, 0.0, 1, 11, ""); + uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, "Find Missing Files", 0, yco-=20, 160, 19, NULL, 0.0, 0.0, 1, 12, ""); + + uiBlockSetDirection(block, UI_RIGHT); + uiTextBoundsBlock(block, 60); + return block; +} + static uiBlock *info_filemenu(void *arg_unused) { uiBlock *block; @@ -997,13 +1061,11 @@ static uiBlock *info_filemenu(void *arg_unused) uiDefIconTextBlockBut(block, info_file_exportmenu, NULL, ICON_RIGHTARROW_THIN, "Export", 0, yco-=20, menuwidth, 19, ""); uiDefBut(block, SEPR, 0, "", 0, yco-=6, menuwidth, 6, NULL, 0.0, 0.0, 0, 0, ""); - - uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, "Pack Data", 0, yco-=20, 160, 19, NULL, 0.0, 0.0, 1, 10, ""); -// uiDefBut(block, BUTM, 1, "Unpack Data to current dir", 0, yco-=20, 160, 19, NULL, 0.0, 0.0, 1, 11, "Removes all packed files from the project and saves them to the current directory"); - uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, "Unpack Data...", 0, yco-=20, 160, 19, NULL, 0.0, 0.0, 1, 12, ""); - + + uiDefIconTextBlockBut(block, info_externalfiles, NULL, ICON_RIGHTARROW_THIN, "External Data",0, yco-=20, 120, 19, ""); + uiDefBut(block, SEPR, 0, "", 0, yco-=6, menuwidth, 6, NULL, 0.0, 0.0, 0, 0, ""); - + uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, "Quit Blender|Ctrl Q", 0, yco-=20, menuwidth, 19, NULL, 0.0, 0.0, 1, 13, ""); uiBlockSetDirection(block, UI_DOWN); |