From 692363bbcaad41110924e2f3f4149eb56c62b212 Mon Sep 17 00:00:00 2001 From: Bo Peng Date: Thu, 30 Aug 2007 20:46:42 +0000 Subject: [PATCH] Embedding patch two: read/write embedded files' * src/insets/InsetGraphics.h * src/insets/InsetExternal.cpp * src/insets/InsetGraphics.cpp * src/insets/InsetInclude.cpp * src/insets/Inset.h * src/insets/InsetInclude.h * src/insets/InsetExternal.h: register embedded files * src/EmbeddedFiles.h|cpp: core of embedded files * src/Buffer.h|cpp: read/write embed file * src/BufferParams.h|cpp: embedded flag * src/Makefile.am * po/POTFILES.in * development/scons/scons_manifest.py: build system updates git-svn-id: svn://svn.lyx.org/lyx/lyx-devel/trunk@19924 a592a061-630c-0410-9148-cb99ea01b6c8 --- development/scons/scons_manifest.py | 2 + po/POTFILES.in | 1 + src/Buffer.cpp | 68 +++++- src/Buffer.h | 5 + src/BufferParams.cpp | 3 + src/BufferParams.h | 2 + src/EmbeddedFiles.cpp | 307 ++++++++++++++++++++++++++++ src/EmbeddedFiles.h | 239 ++++++++++++++++++++++ src/Makefile.am | 2 + src/insets/Inset.h | 4 + src/insets/InsetExternal.cpp | 7 + src/insets/InsetExternal.h | 4 + src/insets/InsetGraphics.cpp | 9 + src/insets/InsetGraphics.h | 2 + src/insets/InsetInclude.cpp | 10 + src/insets/InsetInclude.h | 4 + 16 files changed, 663 insertions(+), 6 deletions(-) create mode 100644 src/EmbeddedFiles.cpp create mode 100644 src/EmbeddedFiles.h diff --git a/development/scons/scons_manifest.py b/development/scons/scons_manifest.py index 771af3c5d9..a59a035f0a 100644 --- a/development/scons/scons_manifest.py +++ b/development/scons/scons_manifest.py @@ -56,6 +56,7 @@ src_header_files = Split(''' Dimension.h DispatchResult.h DocIterator.h + EmbeddedFiles.h Encoding.h ErrorList.h Exporter.h @@ -166,6 +167,7 @@ src_pre_files = Split(''' CutAndPaste.cpp DepTable.cpp DocIterator.cpp + EmbeddedFiles.cpp Encoding.cpp ErrorList.cpp Exporter.cpp diff --git a/po/POTFILES.in b/po/POTFILES.in index 7144f9878e..f8839fc2f4 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -7,6 +7,7 @@ src/Chktex.cpp src/Color.cpp src/Converter.cpp src/CutAndPaste.cpp +src/EmbeddedFiles.cpp src/Exporter.cpp src/Font.cpp src/Format.cpp diff --git a/src/Buffer.cpp b/src/Buffer.cpp index 267da451ec..6e0cb12714 100644 --- a/src/Buffer.cpp +++ b/src/Buffer.cpp @@ -54,6 +54,7 @@ #include "TocBackend.h" #include "Undo.h" #include "version.h" +#include "EmbeddedFiles.h" #include "insets/InsetBibitem.h" #include "insets/InsetBibtex.h" @@ -98,6 +99,7 @@ using std::map; using std::ostream; using std::ostringstream; using std::ofstream; +using std::ifstream; using std::pair; using std::stack; using std::vector; @@ -132,6 +134,7 @@ using support::subst; using support::tempName; using support::trim; using support::sum; +using support::unzipToDir; namespace Alert = frontend::Alert; namespace os = support::os; @@ -194,6 +197,9 @@ public: /// Container for all sort of Buffer dependant errors. map errorLists; + /// all embedded files of this buffer + EmbeddedFiles embedded_files; + /// timestamp and checksum used to test if the file has been externally /// modified. (Used to properly enable 'File->Revert to saved', bug 4114). time_t timestamp_; @@ -204,7 +210,7 @@ public: Buffer::Impl::Impl(Buffer & parent, FileName const & file, bool readonly_) : lyx_clean(true), bak_clean(true), unnamed(false), read_only(readonly_), filename(file), file_fully_loaded(false), inset(params), - toc_backend(&parent), timestamp_(0), checksum_(0) + toc_backend(&parent), embedded_files(&parent), timestamp_(0), checksum_(0) { inset.setAutoBreakRows(true); lyxvc.buffer(&parent); @@ -351,6 +357,18 @@ TocBackend const & Buffer::tocBackend() const } +EmbeddedFiles & Buffer::embeddedFiles() +{ + return pimpl_->embedded_files; +} + + +EmbeddedFiles const & Buffer::embeddedFiles() const +{ + return pimpl_->embedded_files; +} + + string const Buffer::getLatexName(bool const no_path) const { string const name = changeExtension(makeLatexName(fileName()), ".tex"); @@ -634,8 +652,32 @@ bool Buffer::readString(std::string const & s) bool Buffer::readFile(FileName const & filename) { + FileName fname(filename); // Check if the file is compressed. - string const format = getFormatFromContents(filename); + string format = getFormatFromContents(filename); + if (format == "zip") { + // decompress to a temp directory + LYXERR(Debug::FILES) << filename << " is in zip format. Unzip to " << temppath() << endl; + unzipToDir(filename.toFilesystemEncoding(), temppath()); + // + FileName manifest(addName(temppath(), "manifest.txt")); + FileName lyxfile(addName(temppath(), + onlyFilename(filename.toFilesystemEncoding()))); + // if both manifest.txt and file.lyx exist, this is am embedded file + if (fs::exists(manifest.toFilesystemEncoding()) && + fs::exists(lyxfile.toFilesystemEncoding())) { + params().embedded = true; + fname = lyxfile; + // read manifest file + ifstream is(manifest.toFilesystemEncoding().c_str()); + is >> pimpl_->embedded_files; + is.close(); + LYXERR(Debug::FILES) << filename << " is a embedded file. Its manifest is:\n" + << pimpl_->embedded_files; + } + } + // The embedded lyx file can also be compressed, for backward compatibility + format = getFormatFromContents(fname); if (format == "gzip" || format == "zip" || format == "compress") { params().compressed = true; } @@ -643,8 +685,8 @@ bool Buffer::readFile(FileName const & filename) // remove dummy empty par paragraphs().clear(); Lexer lex(0, 0); - lex.setFile(filename); - if (readFile(lex, filename) != success) + lex.setFile(fname); + if (readFile(lex, fname) != success) return false; return true; @@ -845,20 +887,34 @@ bool Buffer::writeFile(FileName const & fname) const bool retval = false; + FileName content; + if (params().embedded) + // first write the .lyx file to the temporary directory + content = FileName(addName(temppath(), + onlyFilename(fname.toFilesystemEncoding()))); + else + content = fname; + if (params().compressed) { - gz::ogzstream ofs(fname.toFilesystemEncoding().c_str(), ios::out|ios::trunc); + gz::ogzstream ofs(content.toFilesystemEncoding().c_str(), ios::out|ios::trunc); if (!ofs) return false; retval = write(ofs); } else { - ofstream ofs(fname.toFilesystemEncoding().c_str(), ios::out|ios::trunc); + ofstream ofs(content.toFilesystemEncoding().c_str(), ios::out|ios::trunc); if (!ofs) return false; retval = write(ofs); } + if (retval && params().embedded) { + // write file.lyx and all the embedded files to the zip file fname + // if embedding is enabled, and there is any embedded file + pimpl_->embedded_files.update(); + return pimpl_->embedded_files.write(fname); + } return retval; } diff --git a/src/Buffer.h b/src/Buffer.h index 450622043e..9d7d266bf6 100644 --- a/src/Buffer.h +++ b/src/Buffer.h @@ -396,6 +396,11 @@ public: TocBackend & tocBackend(); TocBackend const & tocBackend() const; //@} + + //@{ + EmbeddedFiles & embeddedFiles(); + EmbeddedFiles const & embeddedFiles() const; + //@} private: /** Inserts a file into a document diff --git a/src/BufferParams.cpp b/src/BufferParams.cpp index f78a120340..06f0084093 100644 --- a/src/BufferParams.cpp +++ b/src/BufferParams.cpp @@ -353,6 +353,9 @@ BufferParams::BufferParams() listings_params = string(); pagestyle = "default"; compressed = false; + // temporarily enable embedding for testing. Will set to false + // when embedding GUI is added + embedded = true; for (int iter = 0; iter < 4; ++iter) { user_defined_bullet(iter) = ITEMIZE_DEFAULTS[iter]; temp_bullet(iter) = ITEMIZE_DEFAULTS[iter]; diff --git a/src/BufferParams.h b/src/BufferParams.h index dd2422ef5d..7922c66792 100644 --- a/src/BufferParams.h +++ b/src/BufferParams.h @@ -261,6 +261,8 @@ public: std::string parentname; /// bool compressed; + /// + bool embedded; /// the author list for the document AuthorList & authors(); diff --git a/src/EmbeddedFiles.cpp b/src/EmbeddedFiles.cpp new file mode 100644 index 0000000000..57b60733b2 --- /dev/null +++ b/src/EmbeddedFiles.cpp @@ -0,0 +1,307 @@ +// -*- C++ -*- +/** + * \file EmbeddedFiles.cpp + * This file is part of LyX, the document processor. + * Licence details can be found in the file COPYING. + * + * \author Bo Peng + * + * Full author contact details are available in file CREDITS. + * + */ + +#include + +#include "EmbeddedFiles.h" +#include "Buffer.h" +#include "BufferParams.h" +#include "Paragraph.h" +#include "ParIterator.h" +#include "debug.h" +#include "gettext.h" +#include "Format.h" + +#include "frontends/alert.h" + +#include + +#include "support/filetools.h" +#include "support/fs_extras.h" +#include "support/convert.h" + +#include +#include +#include + +using std::ofstream; +using std::endl; +using std::vector; +using std::string; +using std::pair; +using std::make_pair; +using std::istream; +using std::ostream; +using std::getline; +using std::istringstream; + +namespace lyx { + +namespace fs = boost::filesystem; +namespace Alert = frontend::Alert; + +using support::FileName; +using support::DocFileName; +using support::makeAbsPath; +using support::addName; +using support::onlyPath; +using support::absolutePath; +using support::onlyFilename; +using support::makeRelPath; +using support::changeExtension; +using support::bformat; +using support::zipFiles; + + +EmbeddedFile::EmbeddedFile(string const & file, string const & inzip_name, + STATUS status, ParConstIterator const & pit) + : DocFileName(file, true), inzip_name_(inzip_name), status_(status), + par_it_(pit), valid_(true) +{} + + +string EmbeddedFile::embeddedFile(Buffer const * buf) const +{ + return addName(buf->temppath(), inzip_name_); +} + + +void EmbeddedFile::setParIter(ParConstIterator const & pit) +{ + par_it_ = pit; +} + + +bool EmbeddedFiles::enabled() const +{ + return buffer_->params().embedded; +} + + +void EmbeddedFiles::enable(bool flag) +{ + if (enabled() != flag) { + // file will be changed + buffer_->markDirty(); + buffer_->params().embedded = flag; + } +} + + +void EmbeddedFiles::registerFile(string const & filename, + EmbeddedFile::STATUS status, ParConstIterator const & pit) +{ + 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) + break; + // find this filename + if (it != file_list_.end()) { + it->setParIter(pit); + it->setStatus(status); + it->validate(); + return; + } + // register a new one, using relative file path as inzip_name + string inzip_name = to_utf8(makeRelPath(from_utf8(abs_filename), + from_utf8(buffer_->fileName()))); + // if inzip_name is an absolute path, use filename only to avoid + // leaking of filesystem information in inzip_name + if (absolutePath(inzip_name)) + inzip_name = onlyFilename(inzip_name); + // if this name has been used... + // use _1_name, _2_name etc + if (!validInzipName(inzip_name)) { + size_t i = 1; + string tmp = inzip_name; + do { + inzip_name = convert(i) + "_" + tmp; + } while (!validInzipName(inzip_name)); + } + file_list_.push_back(EmbeddedFile(abs_filename, inzip_name, status, pit)); +} + + +void EmbeddedFiles::update() +{ + // invalidate all files, obsolete files will then not be validated by the + // following document scan. These files will still be kept though, because + // they may be added later and their embedding status will be meaningful + // again (thinking of cut/paste of an InsetInclude). + EmbeddedFileList::iterator it = file_list_.begin(); + EmbeddedFileList::iterator it_end = file_list_.end(); + for (; it != it_end; ++it) + it->invalidate(); + + ParIterator pit = buffer_->par_iterator_begin(); + ParIterator pit_end = buffer_->par_iterator_end(); + for (; pit != pit_end; ++pit) { + // For each paragraph, traverse its insets and register embedded files + InsetList::const_iterator iit = pit->insetlist.begin(); + InsetList::const_iterator iit_end = pit->insetlist.end(); + for (; iit != iit_end; ++iit) { + Inset & inset = *iit->inset; + inset.registerEmbeddedFiles(*buffer_, *this, pit); + } + } + LYXERR(Debug::FILES) << "Manifest updated: " << endl + << *this + << "End Manifest" << endl; +} + + +bool EmbeddedFiles::write(DocFileName const & filename) +{ + // file in the temporary path has the content + string const content = FileName(addName(buffer_->temppath(), + onlyFilename(filename.toFilesystemEncoding()))).toFilesystemEncoding(); + + // get a file list and write a manifest file + vector > filenames; + string const manifest = FileName( + addName(buffer_->temppath(), "manifest.txt")).toFilesystemEncoding(); + + // write a manifest file + ofstream os(manifest.c_str()); + os << *this; + os.close(); + // prepare list of embedded file + EmbeddedFileList::iterator it = file_list_.begin(); + EmbeddedFileList::iterator it_end = file_list_.end(); + for (; it != it_end; ++it) { + if (it->valid() && it->embedded()) { + // use external file if possible + if (it->status() != EmbeddedFile::EMBEDDED && fs::exists(it->absFilename())) + filenames.push_back(make_pair(it->absFilename(), it->inzipName())); + // use embedded file (AUTO or EMBEDDED mode) + else if(fs::exists(it->embeddedFile(buffer_))) + filenames.push_back(make_pair(it->embeddedFile(buffer_), it->inzipName())); + else + lyxerr << "File " << it->absFilename() << " does not exist. Skip embedding it. " << endl; + } + } + // add filename (.lyx) and manifest to filenames + filenames.push_back(make_pair(content, onlyFilename(filename.toFilesystemEncoding()))); + filenames.push_back(make_pair(manifest, "manifest.txt")); + // write a zip file with all these files. Write to a temp file first, to + // avoid messing up the original file in case something goes terribly wrong. + DocFileName zipfile(addName(buffer_->temppath(), + onlyFilename(changeExtension( + filename.toFilesystemEncoding(), ".zip")))); + + zipFiles(zipfile, filenames); + // copy file back + try { + fs::copy_file(zipfile.toFilesystemEncoding(), filename.toFilesystemEncoding(), false); + } catch (fs::filesystem_error const & fe) { + Alert::error(_("Save failure"), + bformat(_("Cannot create file %1$s.\n" + "Please check whether the directory exists and is writeable."), + from_utf8(filename.absFilename()))); + LYXERR(Debug::DEBUG) << "Fs error: " << fe.what() << endl; + } + return true; +} + + +bool EmbeddedFiles::validInzipName(string const & name) +{ + EmbeddedFileList::iterator it = file_list_.begin(); + EmbeddedFileList::iterator it_end = file_list_.end(); + for (; it != it_end; ++it) + if (it->inzipName() == name) + return false; + return true; +} + + +istream & operator>> (istream & is, EmbeddedFiles & files) +{ + files.clear(); + string tmp; + getline(is, tmp); + // get version + istringstream itmp(tmp); + int version; + itmp.ignore(string("# LyX manifest version ").size()); + itmp >> version; + + if (version != 1) { + lyxerr << "This version of LyX can only read LyX manifest version 1" << endl; + return is; + } + + getline(is, tmp); + if (tmp != "") { + lyxerr << "Invalid manifest file, lacking " << endl; + return is; + } + // manifest file may be messed up, be carefully + while (is.good()) { + getline(is, tmp); + if (tmp != "") + break; + + string fname; + getline(is, fname); + string inzip_name; + getline(is, inzip_name); + getline(is, tmp); + istringstream itmp(tmp); + int status; + itmp >> status; + + getline(is, tmp); + if (tmp != "") { + lyxerr << "Invalid manifest file, lacking " << endl; + break; + } + + files.registerFile(fname, static_cast(status)); + }; + // the last line must be + if (tmp != "") { + lyxerr << "Invalid manifest file, lacking " << endl; + return is; + } + return is; +} + + +ostream & operator<< (ostream & os, EmbeddedFiles const & files) +{ + // store a version so that operator >> can read later versions + // using version information. + os << "# lyx manifest version 1\n"; + os << "\n"; + EmbeddedFiles::EmbeddedFileList::const_iterator it = files.begin(); + EmbeddedFiles::EmbeddedFileList::const_iterator it_end = files.end(); + for (; it != it_end; ++it) { + if (!it->valid()) + continue; + // use differnt lines to make reading easier. + os << "\n" + // save the relative path + << to_utf8(makeRelPath(from_utf8(it->absFilename()), + from_utf8(files.buffer_->filePath()))) << '\n' + << it->inzipName() << '\n' + << it->status() << '\n' + << "\n"; + } + os << "\n"; + return os; +} + +} diff --git a/src/EmbeddedFiles.h b/src/EmbeddedFiles.h new file mode 100644 index 0000000000..a06e6a2130 --- /dev/null +++ b/src/EmbeddedFiles.h @@ -0,0 +1,239 @@ +// -*- C++ -*- +/** + * \file EmbeddedFiles.h + * This file is part of LyX, the document processor. + * Licence details can be found in the file COPYING. + * + * \author Bo Peng + * + * Full author contact details are available in file CREDITS. + * + */ + +#ifndef EMBEDDEDFILES_H +#define EMBEDDEDFILES_H + +#include "support/FileName.h" + +#include +#include + +#include "ParIterator.h" +#include "Paragraph.h" + +/** + +This file, and the embedding dialog implemented in src/frontends, implements +an 'Embedded Files' feature of lyx. + + +Expected features: +========================= + +1. With embedding enabled (disabled by default), .lyx file can embed graphics, +listings, bib file etc. + +2. Embedding of certain files are automatic (graphics, bib etc), and +other files can be embedded manually. + +3. Embedded file.lyx file is a zip file, with file.lyx, manifest.txt +and embedded files. + +4. Embedded files can be "EMBEDDED", "EXTERNAL", or "AUTO". In the +"AUTO" mode, external files will be used if available; otherwise the +embedded version will be used. In this way, users can work as usual by +modifying external listings, graphics, and do not have to worry about +embedding. "EMBEDDED" and "EXTERNAL" modes ignore or use external files +respectively. + +5. An embedding dialog is provided to change embedding status (buffer +level or individual embedded files), manually embed, extract, view +or edit files. + +Overall, this feature allows two ways of editing a .lyx file + +a. The continuous use of the pure-text .lyx file format with external +files. This is the default file format, and allows external editing +of .lyx file and better use of version control systems. + +b. The embedded way. Figures etc are inserted to .lyx file and will +be embedded. These embedded files can be viewed or edited through +the embedding dialog. This file can be shared with others more +easily. The advantage of lyx' embedding approach is that external files +will be automatically used and embedded if the file is in "AUTO" mode. + + +Implementation: +====================== + +1. An EmbeddedFiles class is implemented to keep the embedded files ( +class EmbeddedFile). (c.f. src/EmbeddedFiles.[h|cpp]) +This class keeps a manifest that has + a. external relative filename + b. inzip filename. It is the relative path name if the embedded file is + in or under the document directory, or file name otherwise. Name aliasing + is used if two external files share the same name. + c. embedding mode. +It also provides functions to + a. manipulate manifest + b. scan a buffer for embeddable files + c. look up inzipname from external filename + d. look up external filename from inzipname + +2. When a file is saved, it is scanned for embedded files. (c.f. +EmbeddedFiles::update(), Inset::registerEmbeddedFiles()). + +3. When a lyx file file.lyx is saved, it is save to tmppath() first. +Embedded files are compressed along with file.lyx and a manifest.txt. +If embedding is disabled, file.lyx is saved in the usual pure-text form. +(c.f. Buffer::writeFile(), EmbeddedFiles::write()) + +4. When a lyx file.lyx file is opened, if it is a zip file, it is +decompressed to tmppath(). If manifest.txt and file.lyx exists in +tmppath(), the manifest is read to buffer, and tmppath()/file.lyx is +read as usual. If file.lyx is not a zip file, it is read as usual. +(c.f. bool Buffer::readFile()) + +5. A menu item Document -> Embedded Files is provided to open +a embedding dialog. It handles a EmbddedFiles point directly. +From this dialog, a user can disable embedding, change embedding status, +or embed other files, extract, view, edit files. + +6. When a lyx file is loaded, Embedded files can have + a. both external and internal copy + b. only external copy (filename()) + c. only embedded copy (temppath()/inzipname) +And each can have "AUTO", "EXTERNAL", "EMBEDDED" status. Proper +handling of each case is required. + +7. If embedding of a .lyx file with embedded files is disabled, all its +embedded files are copied to their respective external filenames. This +is why external filename will exist even if a file is at "EMBEDDED" status. + +8. Individual embeddable insets should find ways to handle embedded files. +InsetGraphics replace params().filename with its temppath()/inzipname version +when the inset is created. The filename appears as /tmp/..../inzipname +when lyx runs. When params().filename is saved, lyx checks if this is an +embedded file (check path == temppath()), if so, save filename() instead. +(c.f. InsetGraphic::read(), InsetGraphics::edit(), InsetGraphicsParams::write()) + + +*/ + +namespace lyx { + +class Buffer; + +class EmbeddedFile : public support::DocFileName +{ +public: + /** + Embedding status of this DocFileName. + */ + enum STATUS { + // uninitialized/invalid status + NONE, + // If the external version of the file is available, it will be used + // to generate output, and be embedded to the saved lyx file. + // Otherwise, embedded version will be used. + AUTO, + // Always use embedded version. + EMBEDDED, + // Do not embed this file, always use external version. + EXTERNAL + }; + + EmbeddedFile(std::string const & file, std::string const & inzip_name, + STATUS status, ParConstIterator const & pit); + + /// filename in the zip file, usually the relative path + std::string inzipName() const { return inzip_name_; } + /// embedded file, equals to temppath()/inzipName() + std::string embeddedFile(Buffer const * buf) const; + + /// paragraph id + void setParIter(ParConstIterator const & pit); + int const parID() const { return par_it_->id(); } + + /// embedding status of this file + bool embedded() const { return status_ != EXTERNAL; } + STATUS status() const { return status_; } + void setStatus(STATUS status) { status_ = status; } + + // A flag indicating whether or not this filename is valid. + // When lyx runs, InsetGraphics etc may be added or removed so filename + // maybe obsolete. In Buffer::updateEmbeddedFiles, the EmbeddedFiles is first + // invalidated (c.f. invalidate()), and all insets are asked to register + // embedded files. In this way, EmbeddedFileList will be refreshed, with + // status setting untouched. + bool valid() const { return valid_; } + void validate() { valid_ = true; } + void invalidate() { valid_ = false; } + +private: + /// filename in zip file + std::string inzip_name_; + /// the status of this docfile + STATUS status_; + /// + bool valid_; + /// Current position of the item, used to locate the files + /// A figure may be referred by several items. In this case + /// only the last location is recorded. + ParConstIterator par_it_; +}; + + +class EmbeddedFiles { +public: + typedef std::vector EmbeddedFileList; +public: + /// + EmbeddedFiles(Buffer * buffer = NULL): file_list_(), buffer_(buffer) {} + /// + ~EmbeddedFiles() {} + + /// return buffer params embedded flag + bool enabled() const; + /// set buffer params embedded flag + void enable(bool flag); + + /// add a file item + void registerFile(std::string const & filename, + EmbeddedFile::STATUS status=EmbeddedFile::AUTO, + ParConstIterator const & pit = ParConstIterator()); + + /// scan the buffer and get a list of EmbeddedFile + void update(); + + /// write a zip file + bool write(support::DocFileName const & filename); + + void clear() { file_list_.clear(); } + + /// + 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(); } + + /// + friend std::istream & operator>> (std::istream & is, EmbeddedFiles &); + + friend std::ostream & operator<< (std::ostream & os, EmbeddedFiles const &); +private: + /// if a inzip name already exists + bool validInzipName(std::string const & name); + /// list of embedded files + EmbeddedFileList file_list_; + /// + Buffer * buffer_; +}; + + +std::istream & operator>> (std::istream & is, EmbeddedFiles &); + +std::ostream & operator<< (std::ostream & os, EmbeddedFiles const &); + +} +#endif diff --git a/src/Makefile.am b/src/Makefile.am index 412073fc93..2770227f8e 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -127,6 +127,8 @@ liblyxcore_la_SOURCES = \ DispatchResult.h \ DocIterator.cpp \ DocIterator.h \ + EmbeddedFiles.h \ + EmbeddedFiles.cpp \ Encoding.cpp \ Encoding.h \ ErrorList.cpp \ diff --git a/src/insets/Inset.h b/src/insets/Inset.h index 03999ec2ad..6b2b14a731 100644 --- a/src/insets/Inset.h +++ b/src/insets/Inset.h @@ -49,6 +49,7 @@ class ParConstIterator; class ParIterator; class Text; class TocList; +class EmbeddedFiles; namespace graphics { class PreviewLoader; } @@ -438,6 +439,9 @@ public: /// Add an entry to the TocList /// pit is the ParConstIterator of the paragraph containing the inset virtual void addToToc(TocList &, Buffer const &, ParConstIterator const &) const {} + /// report files that can be embedded with the lyx file + virtual void registerEmbeddedFiles(Buffer const &, EmbeddedFiles &, + ParConstIterator const &) const {}; /// Fill keys with BibTeX information virtual void fillWithBibKeys(Buffer const &, BiblioInfo &, InsetIterator const &) const { return; } diff --git a/src/insets/InsetExternal.cpp b/src/insets/InsetExternal.cpp index 9b51d7b232..31115a4a85 100644 --- a/src/insets/InsetExternal.cpp +++ b/src/insets/InsetExternal.cpp @@ -481,6 +481,13 @@ bool InsetExternal::getStatus(Cursor & cur, FuncRequest const & cmd, } +void InsetExternal::registerEmbeddedFiles(Buffer const &, + EmbeddedFiles & files, ParConstIterator const & pit) const +{ + files.registerFile(params_.filename.absFilename(), EmbeddedFile::AUTO, pit); +} + + void InsetExternal::edit(Cursor & cur, bool) { InsetExternalMailer(*this).showDialog(&cur.bv()); diff --git a/src/insets/InsetExternal.h b/src/insets/InsetExternal.h index 3c300e4f3a..ac6cc991fa 100644 --- a/src/insets/InsetExternal.h +++ b/src/insets/InsetExternal.h @@ -14,6 +14,7 @@ #include "Inset.h" #include "ExternalTransforms.h" +#include "EmbeddedFiles.h" #include "support/FileName.h" #include "support/Translator.h" @@ -147,6 +148,9 @@ public: void edit(Cursor & cur, bool left); /// bool getStatus(Cursor &, FuncRequest const &, FuncStatus &) const; + /// external file can be embedded + void registerEmbeddedFiles(Buffer const &, EmbeddedFiles &, + ParConstIterator const &) const; protected: InsetExternal(InsetExternal const &); diff --git a/src/insets/InsetGraphics.cpp b/src/insets/InsetGraphics.cpp index cbfd9ddd56..247268fada 100644 --- a/src/insets/InsetGraphics.cpp +++ b/src/insets/InsetGraphics.cpp @@ -71,6 +71,7 @@ TODO #include "Mover.h" #include "OutputParams.h" #include "sgml.h" +#include "EmbeddedFiles.h" #include "frontends/alert.h" @@ -230,6 +231,14 @@ bool InsetGraphics::getStatus(Cursor & cur, FuncRequest const & cmd, } +void InsetGraphics::registerEmbeddedFiles(Buffer const &, + EmbeddedFiles & files, ParConstIterator const & pit) const +{ + files.registerFile(params().filename.absFilename(), + EmbeddedFile::AUTO, pit); +} + + void InsetGraphics::edit(Cursor & cur, bool) { InsetGraphicsMailer(*this).showDialog(&cur.bv()); diff --git a/src/insets/InsetGraphics.h b/src/insets/InsetGraphics.h index 454b1a8e08..ee96bc3b62 100644 --- a/src/insets/InsetGraphics.h +++ b/src/insets/InsetGraphics.h @@ -78,6 +78,8 @@ public: void editGraphics(InsetGraphicsParams const &, Buffer const &) const; /// bool getStatus(Cursor &, FuncRequest const &, FuncStatus &) const; + /// all graphics can be embedded + void registerEmbeddedFiles(Buffer const &, EmbeddedFiles &, ParConstIterator const &) const; protected: InsetGraphics(InsetGraphics const &); /// diff --git a/src/insets/InsetInclude.cpp b/src/insets/InsetInclude.cpp index 5be47172d4..82a48be4f9 100644 --- a/src/insets/InsetInclude.cpp +++ b/src/insets/InsetInclude.cpp @@ -958,6 +958,16 @@ void InsetInclude::updateLabels(Buffer const & buffer, } +void InsetInclude::registerEmbeddedFiles(Buffer const & buffer, + EmbeddedFiles & files, ParConstIterator const & pit) const +{ + // include and input are temprarily not considered. + if (isVerbatim(params_) || isListings(params_)) + files.registerFile(includedFilename(buffer, params_).absFilename(), + EmbeddedFile::AUTO, pit); +} + + string const InsetIncludeMailer::name_("include"); InsetIncludeMailer::InsetIncludeMailer(InsetInclude & inset) diff --git a/src/insets/InsetInclude.h b/src/insets/InsetInclude.h index fe21dba0f5..41d11ab126 100644 --- a/src/insets/InsetInclude.h +++ b/src/insets/InsetInclude.h @@ -18,6 +18,7 @@ #include "RenderButton.h" #include "MailInset.h" #include "Counters.h" +#include "EmbeddedFiles.h" #include "support/FileName.h" @@ -103,6 +104,9 @@ public: bool getStatus(Cursor &, FuncRequest const &, FuncStatus &) const; /// void updateLabels(Buffer const & buffer, ParIterator const &); + /// child document can be embedded + void registerEmbeddedFiles(Buffer const &, EmbeddedFiles &, + ParConstIterator const &) const; protected: InsetInclude(InsetInclude const &); ///