Embedding: After another round of EmbeddedFiles changes, embedded figures should more or less work now.

* src/insets/InsetExternal.cpp: fix compiling. (Does not work yet)
	* src/insets/InsetGraphics.cpp: use latexFilename to produce output.
	* src/insets/InsetGraphicsParams.cpp: use availableFile to show preview.
	* src/EmbeddedFiles.h|cpp: More things are moved from EmbeddedFiles to EmbeddedFile
	* src/frontends/qt4/GuiGraphics.cpp: remove embeddable-related operations.
	* src/support/FileName.h: make isReadableFile virtual


git-svn-id: svn://svn.lyx.org/lyx/lyx-devel/trunk@22410 a592a061-630c-0410-9148-cb99ea01b6c8
This commit is contained in:
Bo Peng 2008-01-07 03:34:21 +00:00
parent 53673d6acc
commit c9a7e8c416
7 changed files with 159 additions and 204 deletions

View File

@ -48,29 +48,35 @@ namespace lyx {
Under the lyx temp directory, content.lyx and its embedded files are usually
saved as
$temp/file.lyx
$temp/figure1.png for ./figure1.png)
$temp/sub/figure2.png for ./sub/figure2.png)
$temp/$embDirName/file.lyx
$temp/$embDirName/figure1.png for ./figure1.png)
$temp/$embDirName/sub/figure2.png for ./sub/figure2.png)
This works fine for embedded files that are in the current or deeper directory
of the document directory, but not for files such as ../figures/figure.png.
A unique name $upDirName is chosen to represent .. in such filenames so that
'up' directories can be stored 'down' the directory tree:
$temp/$upDirName/figures/figure.png for ../figures/figure.png
$temp/$upDirName/$upDirName/figure.png for ../../figure.png
$temp/$embDirName/$upDirName/figures/figure.png for ../figures/figure.png
$temp/$embDirName/$upDirName/$upDirName/figure.png for ../../figure.png
This name has to be fixed because it is used in lyx bundled .zip file.
Note that absolute files are not embeddable because there is no easy
way to put them under $temp.
Using a similar trick, we use $absDirName for absolute path so that
an absolute filename can be saved as
$temp/$embDirName/$absDirName/a/absolute/path for /a/absolute/path
*/
const std::string embDirName = "LyX.Embedded.Files";
const std::string upDirName = "LyX.Embed.Dir.Up";
const std::string absDirName = "LyX.Embed.Dir.Abs";
namespace Alert = frontend::Alert;
EmbeddedFile::EmbeddedFile(string const & file, std::string const & buffer_path)
: DocFileName("", false), inzip_name_(""), embedded_(false), inset_list_()
: DocFileName("", false), inzip_name_(""), embedded_(false), inset_list_(),
temp_path_("")
{
set(file, buffer_path);
}
@ -84,30 +90,39 @@ void EmbeddedFile::set(std::string const & filename, std::string const & buffer_
inzip_name_ = to_utf8(makeRelPath(from_utf8(absFilename()),
from_utf8(buffer_path)));
// if inzip_name_ is an absolute path, this file is not embeddable
if (FileName(inzip_name_).isAbsolute())
inzip_name_ = "";
inzip_name_ = absDirName + '/' + inzip_name_;
// replace .. by upDirName
if (prefixIs(inzip_name_, "."))
inzip_name_ = subst(inzip_name_, "..", upDirName);
LYXERR(Debug::FILES, "Create embedded file " << filename <<
" with in zip name " << inzip_name_ << endl);
// to avoid name conflict between $docu_path/file and $temp_path/file
// embedded files are in a subdirectory of $temp_path.
inzip_name_ = embDirName + '/' + inzip_name_;
}
string EmbeddedFile::embeddedFile(Buffer const * buf) const
string EmbeddedFile::embeddedFile() const
{
BOOST_ASSERT(embeddable());
string temp = buf->temppath();
if (!suffixIs(temp, '/'))
temp += '/';
return temp + inzip_name_;
BOOST_ASSERT(enabled());
return temp_path_ + inzip_name_;
}
string EmbeddedFile::availableFile(Buffer const * buf) const
FileName EmbeddedFile::availableFile() const
{
return embedded() ? embeddedFile(buf) : absFilename();
if (enabled() && embedded())
return FileName(embeddedFile());
else
return *this;
}
string EmbeddedFile::latexFilename(std::string const & buffer_path) const
{
return (enabled() && embedded()) ? inzip_name_ : relFilename(buffer_path);
}
@ -118,41 +133,49 @@ void EmbeddedFile::addInset(Inset const * inset)
}
Inset const * EmbeddedFile::inset(int idx) const
{
BOOST_ASSERT(idx < refCount());
// some embedded file do not have a valid par iterator
return inset_list_[idx];
}
void EmbeddedFile::setEmbed(bool embed)
{
if (!embeddable() && embed) {
Alert::error(_("Embedding failed."), bformat(
_("Cannot embed file %1$s because its path is not relative to document path."),
from_utf8(absFilename())));
return;
}
embedded_ = embed;
}
bool EmbeddedFile::extract(Buffer const * buf) const
void EmbeddedFile::enable(bool flag, Buffer const * buf)
{
BOOST_ASSERT(embeddable());
if (enabled() == flag)
return;
if (flag) {
temp_path_ = buf->temppath();
if (!suffixIs(temp_path_, '/'))
temp_path_ += '/';
if (embedded())
updateFromExternalFile();
} else {
extract();
temp_path_ = "";
}
}
bool EmbeddedFile::extract() const
{
BOOST_ASSERT(enabled());
string ext_file = absFilename();
string emb_file = embeddedFile(buf);
string emb_file = embeddedFile();
FileName emb(emb_file);
FileName ext(ext_file);
if (!emb.exists())
throw ExceptionMessage(ErrorException, _("Failed to extract file"),
bformat(_("Cannot extract file '%1$s'.\n"
"Source file %2$s does not exist"),
from_utf8(outputFilename()), from_utf8(emb_file)));
if (!emb.exists()) {
if (ext.exists())
return true;
else
throw ExceptionMessage(ErrorException, _("Failed to extract file"),
bformat(_("Cannot extract file '%1$s'.\n"
"Source file %2$s does not exist"),
from_utf8(outputFilename()), from_utf8(emb_file)));
}
// if external file already exists ...
if (ext.exists()) {
@ -194,12 +217,12 @@ bool EmbeddedFile::extract(Buffer const * buf) const
}
bool EmbeddedFile::updateFromExternalFile(Buffer const * buf) const
bool EmbeddedFile::updateFromExternalFile() const
{
BOOST_ASSERT(embeddable());
BOOST_ASSERT(enabled());
string ext_file = absFilename();
string emb_file = embeddedFile(buf);
string emb_file = embeddedFile();
FileName emb(emb_file);
FileName ext(ext_file);
@ -257,6 +280,12 @@ void EmbeddedFile::updateInsets(Buffer const * buf) const
}
bool EmbeddedFile::isReadableFile() const
{
return availableFile().isReadableFile();
}
bool operator==(EmbeddedFile const & lhs, EmbeddedFile const & rhs)
{
return lhs.absFilename() == rhs.absFilename()
@ -279,27 +308,50 @@ bool EmbeddedFiles::enabled() const
void EmbeddedFiles::enable(bool flag)
{
if (enabled() != flag) {
// update embedded file list
update();
// An exception may be thrown.
if (flag)
// if enable, copy all files to temppath()
updateFromExternalFile();
if (enabled() == flag)
return;
// update embedded file list
update();
int count_embedded = 0;
int count_external = 0;
EmbeddedFileList::iterator it = file_list_.begin();
EmbeddedFileList::iterator it_end = file_list_.end();
// an exception may be thrown
for (; it != it_end; ++it) {
it->enable(flag, buffer_);
if (it->embedded())
count_embedded ++;
else
// if disable, extract all files
extractAll();
// if operation is successful (no exception is thrown)
buffer_->markDirty();
buffer_->params().embedded = flag;
if (flag)
updateInsets();
count_external ++;
}
// if operation is successful (no exception is thrown)
buffer_->markDirty();
buffer_->params().embedded = flag;
// if the operation is successful, update insets
for (it = file_list_.begin(); it != it_end; ++it)
it->updateInsets(buffer_);
// show result
if (flag) {
docstring const msg = bformat(_("%1$d external files are ignored.\n"
"%2$d embeddable files are embedded.\n"), count_external, count_embedded);
Alert::information(_("Packing all files"), msg);
} else {
docstring const msg = bformat(_("%1$d external files are ignored.\n"
"%2$d embedded files are extracted.\n"), count_external, count_embedded);
Alert::information(_("Unpacking all files"), msg);
}
}
EmbeddedFile & EmbeddedFiles::registerFile(EmbeddedFile const & file, Inset const * inset)
void EmbeddedFiles::registerFile(EmbeddedFile const & file, Inset const * inset)
{
BOOST_ASSERT(!enabled() || file.availableFile().exists());
BOOST_ASSERT(!enabled() || file.enabled());
// try to find this file from the list
EmbeddedFileList::iterator it = file_list_.begin();
EmbeddedFileList::iterator it_end = file_list_.end();
@ -311,14 +363,15 @@ EmbeddedFile & EmbeddedFiles::registerFile(EmbeddedFile const & file, Inset cons
"but with different embedding status. Assuming embedding status."),
from_utf8(it->outputFilename())));
it->setEmbed(true);
// update the inset with this embedding status.
const_cast<Inset*>(inset)->updateEmbeddedFile(*buffer_, *it);
}
it->addInset(inset);
return *it;
return;
}
//
file_list_.push_back(file);
file_list_.back().addInset(inset);
return file_list_.back();
}
@ -341,11 +394,12 @@ bool EmbeddedFiles::writeFile(DocFileName const & filename)
// add content.lyx to filenames
filenames.push_back(make_pair(content, "content.lyx"));
// prepare list of embedded file
update();
EmbeddedFileList::iterator it = file_list_.begin();
EmbeddedFileList::iterator it_end = file_list_.end();
for (; it != it_end; ++it) {
if (it->embedded()) {
string file = it->embeddedFile(buffer_);
string file = it->embeddedFile();
if (!FileName(file).exists())
throw ExceptionMessage(ErrorException, _("Failed to write file"),
bformat(_("Embedded file %1$s does not exist. Did you tamper lyx temporary directory?"),
@ -373,73 +427,4 @@ bool EmbeddedFiles::writeFile(DocFileName const & filename)
return true;
}
EmbeddedFiles::EmbeddedFileList::const_iterator
EmbeddedFiles::find(string filename) const
{
EmbeddedFileList::const_iterator it = file_list_.begin();
EmbeddedFileList::const_iterator it_end = file_list_.end();
for (; it != it_end; ++it)
if (it->absFilename() == filename || it->embeddedFile(buffer_) == filename)
return it;
return file_list_.end();
}
bool EmbeddedFiles::extractAll() const
{
EmbeddedFileList::const_iterator it = file_list_.begin();
EmbeddedFileList::const_iterator it_end = file_list_.end();
int count_extracted = 0;
int count_external = 0;
for (; it != it_end; ++it)
if (it->embedded()) {
if(!it->extract(buffer_)) {
throw ExceptionMessage(ErrorException,
_("Failed to extract file"),
bformat(_("Error: can not extract file %1$s.\n"), it->displayName()));
} else
count_extracted += 1;
} else
count_external += 1;
docstring const msg = bformat(_("%1$d external or non-embeddable files are ignored.\n"
"%2$d embedded files are extracted.\n"), count_external, count_extracted);
Alert::information(_("Unpacking all files"), msg);
return true;
}
bool EmbeddedFiles::updateFromExternalFile() const
{
EmbeddedFileList::const_iterator it = file_list_.begin();
EmbeddedFileList::const_iterator it_end = file_list_.end();
int count_embedded = 0;
int count_external = 0;
for (; it != it_end; ++it)
if (it->embedded()) {
if (!it->updateFromExternalFile(buffer_)) {
throw ExceptionMessage(ErrorException,
_("Failed to embed file"),
bformat(_("Error: can not embed file %1$s.\n"), it->displayName()));
return false;
} else
count_embedded += 1;
} else
count_external += 1;
docstring const msg = bformat(_("%1$d external or non-embeddable files are ignored.\n"
"%2$d embeddable files are embedded.\n"), count_external, count_embedded);
Alert::information(_("Packing all files"), msg);
return true;
}
void EmbeddedFiles::updateInsets() const
{
EmbeddedFiles::EmbeddedFileList::const_iterator it = begin();
EmbeddedFiles::EmbeddedFileList::const_iterator it_end = end();
for (; it != it_end; ++it)
if (it->refCount() > 0)
it->updateInsets(buffer_);
}
}

View File

@ -103,22 +103,20 @@ public:
/// set filename and inzipName.
void set(std::string const & filename, std::string const & buffer_path);
/// filename in the zip file, which is the relative path
std::string inzipName() const { return inzip_name_; }
/// embedded file, equals to temppath()/inzipName()
std::string embeddedFile(Buffer const * buf) const;
std::string embeddedFile() const;
/// embeddedFile() or absFilename() depending on embedding status
std::string availableFile(Buffer const * buf) const;
/// and whether or not embedding is enabled.
FileName availableFile() const;
///
std::string latexFilename(std::string const & buffer_path) const;
/// add an inset that refers to this file
void addInset(Inset const * inset);
Inset const * inset(int idx) const;
/// Number of Insets this file item is referred
/// If refCount() == 0, this file must be manually inserted.
/// This fact is used by the update() function to skip updating
/// such items.
int refCount() const { return inset_list_.size(); }
/// embedding status of this file
@ -126,13 +124,16 @@ public:
/// set embedding status. updateFromExternal() should be called before this
/// to copy or sync the embedded file with external one.
void setEmbed(bool embed);
/// Only files in or under current document path is embeddable
bool embeddable() const { return inzip_name_ != ""; }
/// whether or not embedding is enabled in the current buffer
bool enabled() const { return temp_path_ != ""; }
/// enable embedding of this file
void enable(bool flag, Buffer const * buf);
/// extract file, does not change embedding status
bool extract(Buffer const * buf) const;
bool extract() const;
/// update embedded file from external file, does not change embedding status
bool updateFromExternalFile(Buffer const * buf) const;
bool updateFromExternalFile() const;
///
/// After the embedding status is changed, update all insets related
/// to this file item. For example, a graphic inset may need to monitor
@ -141,6 +142,9 @@ public:
/// document between EmbeddedFiles::update() and this function.
void updateInsets(Buffer const * buf) const;
/// Check readability of availableFile
bool isReadableFile() const;
private:
/// filename in zip file
std::string inzip_name_;
@ -149,6 +153,11 @@ private:
/// Insets that contains this file item. Because a
/// file item can be referred by several Insets, a vector is used.
std::vector<Inset const *> inset_list_;
/// Embedded file needs to know whether enbedding is enabled,
/// and where is the lyx temporary directory. Such information can
/// be retrived from a buffer, but a buffer is not always available when
/// an EmbeddedFile is used.
std::string temp_path_;
};
@ -175,7 +184,7 @@ public:
/* \param file Embedded file to add
* \param inset Inset pointer
*/
EmbeddedFile & registerFile(EmbeddedFile const & file, Inset const * inset = 0);
void registerFile(EmbeddedFile const & file, Inset const * inset = 0);
/// scan the buffer and get a list of EmbeddedFile
void update();
@ -185,23 +194,12 @@ public:
void clear() { file_list_.clear(); }
///
EmbeddedFile & operator[](size_t idx) { return *(file_list_.begin() + idx); }
EmbeddedFile const & operator[](size_t idx) const { return *(file_list_.begin() + idx); }
///
EmbeddedFileList::iterator begin() { return file_list_.begin(); }
EmbeddedFileList::iterator end() { return file_list_.end(); }
EmbeddedFileList::const_iterator begin() const { return file_list_.begin(); }
EmbeddedFileList::const_iterator end() const { return file_list_.end(); }
// try to locate filename, using either absFilename() or embeddedFile()
EmbeddedFileList::const_iterator find(std::string filename) const;
/// extract all file items, used when disable embedding
bool extractAll() const;
/// update all files from external, used when enable embedding
bool updateFromExternalFile() const;
///
/// update all insets to use embedded files when embedding status is changed
void updateInsets() const;
private:
/// list of embedded files
EmbeddedFileList file_list_;

View File

@ -300,10 +300,6 @@ void GuiGraphics::on_filename_textChanged(const QString & filename)
{
editPB->setDisabled(filename.isEmpty());
EmbeddedFile file = EmbeddedFile(fromqstr(filename), bufferFilepath());
if (!file.embeddable()) {
embedCB->setCheckState(Qt::Unchecked);
embedCB->setDisabled(true);
}
}
@ -459,7 +455,6 @@ void GuiGraphics::updateContents()
string const name =
igp.filename.outputFilename(bufferFilepath());
filename->setText(toqstr(name));
embedCB->setEnabled(igp.filename.embeddable());
embedCB->setCheckState(igp.filename.embedded() ? Qt::Checked : Qt::Unchecked);
// set the bounding box values

View File

@ -182,15 +182,8 @@ void InsetExternalParams::write(Buffer const & buffer, ostream & os) const
os << "External\n"
<< "\ttemplate " << templatename() << '\n';
if (!filename.empty()) {
// when we save, we still use the original filename
EmbeddedFiles::EmbeddedFileList::const_iterator it =
buffer.embeddedFiles().find(filename.toFilesystemEncoding());
if (it != buffer.embeddedFiles().end())
os << "\tfilename " << DocFileName(it->absFilename()).outputFilename(buffer.filePath()) << '\n';
else
os << "\tfilename " << filename.outputFilename(buffer.filePath()) << '\n';
}
if (!filename.empty())
os << "\tfilename " << filename.outputFilename(buffer.filePath()) << '\n';
if (display != defaultDisplayType)
os << "\tdisplay "
@ -297,12 +290,6 @@ bool InsetExternalParams::read(Buffer const & buffer, Lexer & lex)
lex.eatLine();
string const name = lex.getString();
filename.set(name, buffer.filePath());
// maybe this file is embedded
EmbeddedFiles::EmbeddedFileList::const_iterator it = buffer.embeddedFiles().find(filename.toFilesystemEncoding());
if (it != buffer.embeddedFiles().end())
// using available file, embedded or external, depending on file availability and
// embedding status.
filename = DocFileName(it->availableFile(&buffer));
break;
}

View File

@ -171,13 +171,9 @@ void InsetGraphics::doDispatch(Cursor & cur, FuncRequest & cmd)
Buffer const & buffer = cur.buffer();
InsetGraphicsParams p;
InsetGraphicsMailer::string2params(to_utf8(cmd.argument()), buffer, p);
// when embedding is enabled, change of embedding status leads to actions
if (!p.filename.empty() && buffer.embeddedFiles().enabled() ) {
if (!p.filename.empty()) {
try {
if (p.filename.embedded())
p.filename.updateFromExternalFile(&buffer);
else
p.filename.extract(&buffer);
updateEmbeddedFile(buffer, p.filename);
} catch (ExceptionMessage const & message) {
Alert::error(message.title_, message.details_);
// do not set parameter if an error happens
@ -231,11 +227,16 @@ void InsetGraphics::registerEmbeddedFiles(Buffer const &,
void InsetGraphics::updateEmbeddedFile(Buffer const & buf,
EmbeddedFile const & file)
{
BOOST_ASSERT(buf.embeddedFiles().enabled());
params_.filename = file;
// when embedding is enabled, change of embedding status leads to actions
EmbeddedFile temp = file;
temp.enable(buf.embeddedFiles().enabled(), &buf);
// this will not be set if an exception is thorwn in enable()
params_.filename = temp;
LYXERR(Debug::FILES, "Update InsetGraphic with File "
<< params_.filename.toFilesystemEncoding()
<< ", embedding status: " << params_.filename.embedded());
<< ", embedding status: " << params_.filename.embedded()
<< ", enabled: " << params_.filename.enabled());
}
@ -279,16 +280,7 @@ void InsetGraphics::read(Buffer const & buf, Lexer & lex)
else
LYXERR(Debug::GRAPHICS, "Not a Graphics inset!");
// InsetGraphics is read, with filename in params_. We do not know if this file actually
// exists or is embedded so we need to get the 'availableFile' from buf.embeddedFiles()
if (buf.embeddedFiles().enabled()) {
EmbeddedFiles::EmbeddedFileList::const_iterator it =
buf.embeddedFiles().find(params_.filename.toFilesystemEncoding());
if (it != buf.embeddedFiles().end())
// using available file, embedded or external, depending on file availability and
// embedding status.
params_.filename = *it;
}
params_.filename.enable(buf.embeddedFiles().enabled(), &buf);
graphic_->update(params().as_grfxParams());
}
@ -577,7 +569,8 @@ string const InsetGraphics::prepareFile(Buffer const & buf,
if (params().filename.empty())
return string();
string const orig_file = params().filename.absFilename();
string const orig_file = params().filename.availableFile().absFilename();
// this is for dryrun and display purposes, do not use latexFilename
string const rel_file = params().filename.relFilename(buf.filePath());
// previewing source code, no file copying or file format conversion
@ -586,7 +579,7 @@ string const InsetGraphics::prepareFile(Buffer const & buf,
// temp_file will contain the file for LaTeX to act on if, for example,
// we move it to a temp dir or uncompress it.
FileName temp_file = params().filename;
FileName temp_file = params().filename.availableFile();
// The master buffer. This is useful when there are multiple levels
// of include files
@ -606,7 +599,7 @@ string const InsetGraphics::prepareFile(Buffer const & buf,
GraphicsCopyStatus status;
boost::tie(status, temp_file) =
copyToDirIfNeeded(params().filename, temp_path);
copyToDirIfNeeded(params().filename.availableFile(), temp_path);
if (status == FAILURE)
return orig_file;
@ -763,9 +756,6 @@ int InsetGraphics::latex(Buffer const & buf, odocstream & os,
LYXERR(Debug::GRAPHICS, "insetgraphics::latex: Filename = "
<< params().filename.absFilename());
string const relative_file =
params().filename.relFilename(buf.filePath());
bool const file_exists = !params().filename.empty()
&& params().filename.isReadableFile();
string const message = file_exists ?

View File

@ -273,7 +273,7 @@ bool InsetGraphicsParams::Read(Lexer & lex, string const & token, string const &
graphics::Params InsetGraphicsParams::as_grfxParams() const
{
graphics::Params pars;
pars.filename = filename;
pars.filename = filename.availableFile();
pars.scale = lyxscale;
pars.angle = convert<double>(rotateAngle);

View File

@ -83,7 +83,7 @@ public:
/// return true when file/directory is readable
bool isReadableDirectory() const;
/// return true when it is a file and readable
bool isReadableFile() const;
virtual bool isReadableFile() const;
/// return true when file/directory is writable
bool isWritable() const;
/// return true when file/directory is writable (write test file)