mirror of
https://git.lyx.org/repos/lyx.git
synced 2024-11-08 10:51:03 +00:00
Embedding: adjust how InsetGraphics interacts with EmbeddedFiles, the ?th overhaul of the EmbeddedFile class :-(
git-svn-id: svn://svn.lyx.org/lyx/lyx-devel/trunk@22377 a592a061-630c-0410-9148-cb99ea01b6c8
This commit is contained in:
parent
9073fd6b9f
commit
56a1dfe65f
@ -43,28 +43,78 @@ using namespace lyx::support;
|
||||
|
||||
namespace lyx {
|
||||
|
||||
/** Dir name used for ".." in the bundled file.
|
||||
|
||||
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)
|
||||
|
||||
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
|
||||
|
||||
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.
|
||||
*/
|
||||
const std::string upDirName = "LyX.Embed.Dir.Up";
|
||||
|
||||
namespace Alert = frontend::Alert;
|
||||
|
||||
|
||||
EmbeddedFile::EmbeddedFile(string const & file, string const & inzip_name,
|
||||
bool embed, Inset const * inset)
|
||||
: DocFileName(file, true), inzip_name_(inzip_name), embedded_(embed),
|
||||
inset_list_()
|
||||
EmbeddedFile::EmbeddedFile(string const & file, std::string const & buffer_path)
|
||||
: DocFileName("", false), inzip_name_(""), embedded_(false), inset_list_()
|
||||
{
|
||||
if (inset != NULL)
|
||||
inset_list_.push_back(inset);
|
||||
set(file, buffer_path);
|
||||
}
|
||||
|
||||
|
||||
void EmbeddedFile::set(std::string const & filename, std::string const & buffer_path)
|
||||
{
|
||||
DocFileName::set(filename, buffer_path);
|
||||
if (filename.empty())
|
||||
return;
|
||||
|
||||
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_ = "";
|
||||
// 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);
|
||||
}
|
||||
|
||||
|
||||
string EmbeddedFile::embeddedFile(Buffer const * buf) const
|
||||
{
|
||||
return addName(buf->temppath(), inzip_name_);
|
||||
BOOST_ASSERT(embeddable());
|
||||
string temp = buf->temppath();
|
||||
if (!suffixIs(temp, '/'))
|
||||
temp += '/';
|
||||
return temp + inzip_name_;
|
||||
}
|
||||
|
||||
|
||||
string EmbeddedFile::availableFile(Buffer const * buf) const
|
||||
{
|
||||
return embedded() ? embeddedFile(buf) : absFilename();
|
||||
}
|
||||
|
||||
|
||||
void EmbeddedFile::addInset(Inset const * inset)
|
||||
{
|
||||
inset_list_.push_back(inset);
|
||||
if (inset != NULL)
|
||||
inset_list_.push_back(inset);
|
||||
}
|
||||
|
||||
|
||||
@ -76,36 +126,22 @@ Inset const * EmbeddedFile::inset(int idx) const
|
||||
}
|
||||
|
||||
|
||||
void EmbeddedFile::saveBookmark(Buffer const * buf, int idx) const
|
||||
void EmbeddedFile::setEmbed(bool embed)
|
||||
{
|
||||
Inset const * ptr = inset(idx);
|
||||
// This might not be the most efficient method ...
|
||||
for (InsetIterator it = inset_iterator_begin(buf->inset()); it; ++it)
|
||||
if (&(*it) == ptr) {
|
||||
// this is basically BufferView::saveBookmark(0)
|
||||
LyX::ref().session().bookmarks().save(
|
||||
FileName(buf->absFileName()),
|
||||
it.bottom().pit(),
|
||||
it.bottom().pos(),
|
||||
it.paragraph().id(),
|
||||
it.pos(),
|
||||
0
|
||||
);
|
||||
}
|
||||
// this inset can not be located. There is something wrong that needs
|
||||
// to be fixed.
|
||||
BOOST_ASSERT(true);
|
||||
}
|
||||
|
||||
|
||||
string EmbeddedFile::availableFile(Buffer const * buf) const
|
||||
{
|
||||
return embedded() ? embeddedFile(buf) : absFilename();
|
||||
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
|
||||
{
|
||||
BOOST_ASSERT(embeddable());
|
||||
|
||||
string ext_file = absFilename();
|
||||
string emb_file = embeddedFile(buf);
|
||||
|
||||
@ -113,7 +149,10 @@ bool EmbeddedFile::extract(Buffer const * buf) const
|
||||
FileName ext(ext_file);
|
||||
|
||||
if (!emb.exists())
|
||||
return false;
|
||||
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()) {
|
||||
@ -142,8 +181,10 @@ bool EmbeddedFile::extract(Buffer const * buf) const
|
||||
return false;
|
||||
}
|
||||
|
||||
if (emb.copyTo(ext))
|
||||
if (emb.copyTo(ext)) {
|
||||
LYXERR(Debug::FILES, "Extract file " << emb_file << " to " << ext_file << endl);
|
||||
return true;
|
||||
}
|
||||
|
||||
throw ExceptionMessage(ErrorException, _("Copy file failure"),
|
||||
bformat(_("Cannot copy file %1$s to %2$s.\n"
|
||||
@ -155,15 +196,26 @@ bool EmbeddedFile::extract(Buffer const * buf) const
|
||||
|
||||
bool EmbeddedFile::updateFromExternalFile(Buffer const * buf) const
|
||||
{
|
||||
BOOST_ASSERT(embeddable());
|
||||
|
||||
string ext_file = absFilename();
|
||||
string emb_file = embeddedFile(buf);
|
||||
|
||||
FileName emb(emb_file);
|
||||
FileName ext(ext_file);
|
||||
|
||||
if (!ext.exists())
|
||||
return false;
|
||||
|
||||
if (!ext.exists()) {
|
||||
// no need to update
|
||||
if (emb.exists())
|
||||
return true;
|
||||
// no external and internal file
|
||||
throw ExceptionMessage(ErrorException,
|
||||
_("Failed to embed file"),
|
||||
bformat(_("Failed to embed file %1$s.\n"
|
||||
"Please check whether this file exists and is readable."),
|
||||
from_utf8(ext_file)));
|
||||
}
|
||||
|
||||
// if embedded file already exists ...
|
||||
if (emb.exists()) {
|
||||
// no need to copy if the files are the same
|
||||
@ -186,7 +238,7 @@ bool EmbeddedFile::updateFromExternalFile(Buffer const * buf) const
|
||||
path.createPath();
|
||||
if (ext.copyTo(emb))
|
||||
return true;
|
||||
throw ExceptionMessage(ErrorException,
|
||||
throw ExceptionMessage(ErrorException,
|
||||
_("Copy file failure"),
|
||||
bformat(_("Cannot copy file %1$s to %2$s.\n"
|
||||
"Please check whether the directory exists and is writeable."),
|
||||
@ -205,6 +257,20 @@ void EmbeddedFile::updateInsets(Buffer const * buf) const
|
||||
}
|
||||
|
||||
|
||||
bool operator==(EmbeddedFile const & lhs, EmbeddedFile const & rhs)
|
||||
{
|
||||
return lhs.absFilename() == rhs.absFilename()
|
||||
&& lhs.saveAbsPath() == rhs.saveAbsPath()
|
||||
&& lhs.embedded() == rhs.embedded();
|
||||
}
|
||||
|
||||
|
||||
bool operator!=(EmbeddedFile const & lhs, EmbeddedFile const & rhs)
|
||||
{
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
|
||||
|
||||
bool EmbeddedFiles::enabled() const
|
||||
{
|
||||
return buffer_->params().embedded;
|
||||
@ -232,25 +298,26 @@ void EmbeddedFiles::enable(bool flag)
|
||||
}
|
||||
|
||||
|
||||
EmbeddedFile & EmbeddedFiles::registerFile(string const & filename,
|
||||
bool embed, Inset const * inset, string const & inzipName)
|
||||
EmbeddedFile & EmbeddedFiles::registerFile(EmbeddedFile const & file, Inset const * inset)
|
||||
{
|
||||
// filename can be relative or absolute, translate to absolute filename
|
||||
string abs_filename = makeAbsPath(filename, buffer_->filePath()).absFilename();
|
||||
// try to find this file from the list
|
||||
EmbeddedFileList::iterator it = file_list_.begin();
|
||||
EmbeddedFileList::iterator it_end = file_list_.end();
|
||||
for (; it != it_end; ++it)
|
||||
if (it->absFilename() == abs_filename || it->embeddedFile(buffer_) == abs_filename)
|
||||
break;
|
||||
// find this filename, keep the original embedding status
|
||||
if (it != file_list_.end()) {
|
||||
it->addInset(inset);
|
||||
return *it;
|
||||
}
|
||||
if (it->absFilename() == file.absFilename()) {
|
||||
if (it->embedded() != file.embedded()) {
|
||||
Alert::error(_("Wrong embedding status."),
|
||||
bformat(_("File %1$s is included in more than one insets, "
|
||||
"but with different embedding status. Assuming embedding status."),
|
||||
from_utf8(it->outputFilename())));
|
||||
it->setEmbed(true);
|
||||
}
|
||||
it->addInset(inset);
|
||||
return *it;
|
||||
}
|
||||
//
|
||||
file_list_.push_back(EmbeddedFile(abs_filename,
|
||||
getInzipName(abs_filename, inzipName), embed, inset));
|
||||
file_list_.push_back(file);
|
||||
file_list_.back().addInset(inset);
|
||||
return file_list_.back();
|
||||
}
|
||||
|
||||
@ -278,11 +345,14 @@ bool EmbeddedFiles::writeFile(DocFileName const & filename)
|
||||
EmbeddedFileList::iterator it_end = file_list_.end();
|
||||
for (; it != it_end; ++it) {
|
||||
if (it->embedded()) {
|
||||
string file = it->availableFile(buffer_);
|
||||
if (file.empty())
|
||||
lyxerr << "File " << it->absFilename() << " does not exist. Skip embedding it. " << endl;
|
||||
else
|
||||
filenames.push_back(make_pair(file, it->inzipName()));
|
||||
string file = it->embeddedFile(buffer_);
|
||||
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?"),
|
||||
it->displayName()));
|
||||
filenames.push_back(make_pair(file, it->inzipName()));
|
||||
LYXERR(Debug::FILES, "Writing file " << it->outputFilename()
|
||||
<< " as " << it->inzipName() << endl);
|
||||
}
|
||||
}
|
||||
// write a zip file with all these files. Write to a temp file first, to
|
||||
@ -310,7 +380,7 @@ 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)
|
||||
if (it->absFilename() == filename || it->embeddedFile(buffer_) == filename)
|
||||
return it;
|
||||
return file_list_.end();
|
||||
}
|
||||
@ -332,7 +402,7 @@ bool EmbeddedFiles::extractAll() const
|
||||
count_extracted += 1;
|
||||
} else
|
||||
count_external += 1;
|
||||
docstring const msg = bformat(_("%1$d external files are ignored.\n"
|
||||
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;
|
||||
@ -353,51 +423,16 @@ bool EmbeddedFiles::updateFromExternalFile() const
|
||||
bformat(_("Error: can not embed file %1$s.\n"), it->displayName()));
|
||||
return false;
|
||||
} else
|
||||
count_external += 1;
|
||||
count_embedded += 1;
|
||||
} else
|
||||
count_external += 1;
|
||||
docstring const msg = bformat(_("%1$d external files are ignored.\n"
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
string const EmbeddedFiles::getInzipName(string const & abs_filename, string const & name)
|
||||
{
|
||||
// register a new one, using relative file path as inzip_name
|
||||
string inzip_name = name;
|
||||
if (name.empty())
|
||||
inzip_name = to_utf8(makeRelPath(from_utf8(abs_filename),
|
||||
from_utf8(buffer_->filePath())));
|
||||
// if inzip_name is an absolute path, use filename only to avoid
|
||||
// leaking of filesystem information in inzip_name
|
||||
// The second case covers cases '../path/file' and '.'
|
||||
if (FileName(inzip_name).isAbsolute() || prefixIs(inzip_name, "."))
|
||||
inzip_name = onlyFilename(abs_filename);
|
||||
// if this name has been used...
|
||||
// use _1_name, _2_name etc
|
||||
string tmp = inzip_name;
|
||||
EmbeddedFileList::iterator it;
|
||||
EmbeddedFileList::iterator it_end = file_list_.end();
|
||||
bool unique_name = false;
|
||||
size_t i = 0;
|
||||
while (!unique_name) {
|
||||
unique_name = true;
|
||||
if (i > 0)
|
||||
inzip_name = convert<string>(i) + "_" + tmp;
|
||||
it = file_list_.begin();
|
||||
for (; it != it_end; ++it)
|
||||
if (it->inzipName() == inzip_name) {
|
||||
unique_name = false;
|
||||
++i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return inzip_name;
|
||||
}
|
||||
|
||||
|
||||
void EmbeddedFiles::updateInsets() const
|
||||
{
|
||||
EmbeddedFiles::EmbeddedFileList::const_iterator it = begin();
|
||||
@ -407,5 +442,4 @@ void EmbeddedFiles::updateInsets() const
|
||||
it->updateInsets(buffer_);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@ -56,7 +56,10 @@ be embedded. These embedded files can be viewed or edited through
|
||||
the embedding dialog. This file can be shared with others more
|
||||
easily.
|
||||
|
||||
Format a anb b can be converted easily, by packing/unpacking a .lyx file.
|
||||
Format a and b can be converted easily, by packing/unpacking a .lyx file.
|
||||
|
||||
NOTE: With current implementation, files with absolute filenames (not in
|
||||
or deeper under the current document directory) can not be embedded.
|
||||
|
||||
Implementation:
|
||||
======================
|
||||
@ -95,14 +98,15 @@ class ErrorList;
|
||||
class EmbeddedFile : public support::DocFileName
|
||||
{
|
||||
public:
|
||||
EmbeddedFile() {};
|
||||
|
||||
EmbeddedFile(std::string const & file, std::string const & inzip_name,
|
||||
bool embedded, Inset const * inset);
|
||||
|
||||
/// filename in the zip file, usually the relative path
|
||||
EmbeddedFile(std::string const & file = std::string(),
|
||||
std::string const & buffer_path = std::string());
|
||||
|
||||
/// 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_; }
|
||||
void setInzipName(std::string name) { inzip_name_ = name; }
|
||||
|
||||
/// embedded file, equals to temppath()/inzipName()
|
||||
std::string embeddedFile(Buffer const * buf) const;
|
||||
/// embeddedFile() or absFilename() depending on embedding status
|
||||
@ -111,9 +115,6 @@ public:
|
||||
/// add an inset that refers to this file
|
||||
void addInset(Inset const * inset);
|
||||
Inset const * inset(int idx) const;
|
||||
/// save the location of this inset as bookmark so that
|
||||
/// it can be located using LFUN_BOOKMARK_GOTO
|
||||
void saveBookmark(Buffer const * buf, 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
|
||||
@ -124,7 +125,9 @@ public:
|
||||
bool embedded() const { return embedded_; }
|
||||
/// set embedding status. updateFromExternal() should be called before this
|
||||
/// to copy or sync the embedded file with external one.
|
||||
void setEmbed(bool embed) { embedded_ = embed; }
|
||||
void setEmbed(bool embed);
|
||||
/// Only files in or under current document path is embeddable
|
||||
bool embeddable() const { return inzip_name_ != ""; }
|
||||
|
||||
/// extract file, does not change embedding status
|
||||
bool extract(Buffer const * buf) const;
|
||||
@ -132,9 +135,10 @@ public:
|
||||
bool updateFromExternalFile(Buffer const * buf) const;
|
||||
///
|
||||
/// After the embedding status is changed, update all insets related
|
||||
/// to this file item.
|
||||
/// Because inset pointers may not be up to date, EmbeddedFiles::update()
|
||||
/// would better be called before this function is called.
|
||||
/// to this file item. For example, a graphic inset may need to monitor
|
||||
/// embedded file instead of external file. To make sure inset pointers
|
||||
/// are up to date, please make sure there is no modification to the
|
||||
/// document between EmbeddedFiles::update() and this function.
|
||||
void updateInsets(Buffer const * buf) const;
|
||||
|
||||
private:
|
||||
@ -148,6 +152,10 @@ private:
|
||||
};
|
||||
|
||||
|
||||
bool operator==(EmbeddedFile const & lhs, EmbeddedFile const & rhs);
|
||||
bool operator!=(EmbeddedFile const & lhs, EmbeddedFile const & rhs);
|
||||
|
||||
|
||||
class EmbeddedFiles {
|
||||
public:
|
||||
typedef std::vector<EmbeddedFile> EmbeddedFileList;
|
||||
@ -164,15 +172,10 @@ public:
|
||||
void enable(bool flag);
|
||||
|
||||
/// add a file item.
|
||||
/* \param filename filename to add
|
||||
* \param embed embedding status. For a new file item, this is always true.
|
||||
* If the file already exists, this parameter is ignored.
|
||||
/* \param file Embedded file to add
|
||||
* \param inset Inset pointer
|
||||
* \param inzipName suggested inzipname
|
||||
*/
|
||||
EmbeddedFile & registerFile(std::string const & filename, bool embed = false,
|
||||
Inset const * inset = 0,
|
||||
std::string const & inzipName = std::string());
|
||||
EmbeddedFile & registerFile(EmbeddedFile const & file, Inset const * inset = 0);
|
||||
|
||||
/// scan the buffer and get a list of EmbeddedFile
|
||||
void update();
|
||||
@ -200,8 +203,6 @@ public:
|
||||
/// update all insets to use embedded files when embedding status is changed
|
||||
void updateInsets() const;
|
||||
private:
|
||||
/// get a unique inzip name, a suggestion can be given.
|
||||
std::string const getInzipName(std::string const & name, std::string const & inzipName);
|
||||
/// list of embedded files
|
||||
EmbeddedFileList file_list_;
|
||||
///
|
||||
|
@ -77,6 +77,7 @@ TODO
|
||||
|
||||
#include "support/convert.h"
|
||||
#include "support/docstream.h"
|
||||
#include "support/ExceptionMessage.h"
|
||||
#include "support/filetools.h"
|
||||
#include "support/lyxlib.h"
|
||||
#include "support/lstrings.h"
|
||||
@ -94,6 +95,8 @@ using namespace lyx::support;
|
||||
|
||||
namespace lyx {
|
||||
|
||||
namespace Alert = frontend::Alert;
|
||||
|
||||
namespace {
|
||||
|
||||
/// Find the most suitable image format for images in \p format
|
||||
@ -168,9 +171,20 @@ void InsetGraphics::doDispatch(Cursor & cur, FuncRequest & cmd)
|
||||
Buffer const & buffer = cur.buffer();
|
||||
InsetGraphicsParams p;
|
||||
InsetGraphicsMailer::string2params(to_utf8(cmd.argument()), buffer, p);
|
||||
if (!p.filename.empty())
|
||||
// when embedding is enabled, change of embedding status leads to actions
|
||||
if (!p.filename.empty() && buffer.embeddedFiles().enabled() ) {
|
||||
try {
|
||||
if (p.filename.embedded())
|
||||
p.filename.updateFromExternalFile(&buffer);
|
||||
else
|
||||
p.filename.extract(&buffer);
|
||||
} catch (ExceptionMessage const & message) {
|
||||
Alert::error(message.title_, message.details_);
|
||||
// do not set parameter if an error happens
|
||||
break;
|
||||
}
|
||||
setParams(p);
|
||||
else
|
||||
} else
|
||||
cur.noUpdate();
|
||||
break;
|
||||
}
|
||||
@ -210,8 +224,7 @@ bool InsetGraphics::getStatus(Cursor & cur, FuncRequest const & cmd,
|
||||
void InsetGraphics::registerEmbeddedFiles(Buffer const &,
|
||||
EmbeddedFiles & files) const
|
||||
{
|
||||
files.registerFile(params().filename.absFilename(),
|
||||
false, this);
|
||||
files.registerFile(params().filename, this);
|
||||
}
|
||||
|
||||
|
||||
|
@ -209,7 +209,7 @@ public:
|
||||
* \param buffer_path if \c filename has a relative path, generate
|
||||
* the absolute path using this.
|
||||
*/
|
||||
void set(std::string const & filename, std::string const & buffer_path);
|
||||
virtual void set(std::string const & filename, std::string const & buffer_path);
|
||||
|
||||
void erase();
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user