]> git.lyx.org Git - features.git/commitdiff
Embedding patch two: read/write embedded files'
authorBo Peng <bpeng@lyx.org>
Thu, 30 Aug 2007 20:46:42 +0000 (20:46 +0000)
committerBo Peng <bpeng@lyx.org>
Thu, 30 Aug 2007 20:46:42 +0000 (20:46 +0000)
*  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

16 files changed:
development/scons/scons_manifest.py
po/POTFILES.in
src/Buffer.cpp
src/Buffer.h
src/BufferParams.cpp
src/BufferParams.h
src/EmbeddedFiles.cpp [new file with mode: 0644]
src/EmbeddedFiles.h [new file with mode: 0644]
src/Makefile.am
src/insets/Inset.h
src/insets/InsetExternal.cpp
src/insets/InsetExternal.h
src/insets/InsetGraphics.cpp
src/insets/InsetGraphics.h
src/insets/InsetInclude.cpp
src/insets/InsetInclude.h

index 771af3c5d9df30deafdbd97565c97e3e922d40e4..a59a035f0a1e25769ea9e0f813f3c02b2e1a5ac6 100644 (file)
@@ -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
index 7144f9878ef5b0e3590c0bf9e276dea06bdbc05f..f8839fc2f4c3365a766f09ec05e3d5e1ee2c1c53 100644 (file)
@@ -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
index 267da451ec706c50ec5b915b9954f44e9e826448..6e0cb127141c7cd3f4f4fb1c764aea5f437c425d 100644 (file)
@@ -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<string, ErrorList> 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;
 }
 
index 450622043ee0ab6a052026cb1dd2d7329785f99b..9d7d266bf6c100707687894ffb2e1bda81f2f755 100644 (file)
@@ -396,6 +396,11 @@ public:
        TocBackend & tocBackend();
        TocBackend const & tocBackend() const;
        //@}
+       
+       //@{
+       EmbeddedFiles & embeddedFiles();
+       EmbeddedFiles const & embeddedFiles() const;
+       //@}
 
 private:
        /** Inserts a file into a document
index f78a1203402f6b78404f54a61d13b6b4abc4a6a9..06f00840935a05fccce7494287c28a8b5fc902e0 100644 (file)
@@ -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];
index dd2422ef5d0bcaad8c147e647a9f40fbb34c92cb..7922c66792b28c8ad10a5c41f402886c6e39704a 100644 (file)
@@ -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 (file)
index 0000000..57b6073
--- /dev/null
@@ -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 <config.h>
+
+#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 <boost/filesystem/operations.hpp>
+
+#include "support/filetools.h"
+#include "support/fs_extras.h"
+#include "support/convert.h"
+
+#include <sstream>
+#include <fstream>
+#include <utility>
+
+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<string>(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<pair<string, string> > 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 != "<manifest>") {
+               lyxerr << "Invalid manifest file, lacking <manifest>" << endl;
+               return is;
+       }
+       // manifest file may be messed up, be carefully
+       while (is.good()) {
+               getline(is, tmp);
+               if (tmp != "<file>")
+                       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 != "</file>") {
+                       lyxerr << "Invalid manifest file, lacking </file>" << endl;
+                       break;
+               }
+
+               files.registerFile(fname, static_cast<EmbeddedFile::STATUS>(status));
+       };
+       // the last line must be </manifest>
+       if (tmp != "</manifest>") {
+               lyxerr << "Invalid manifest file, lacking </manifest>" << 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 << "<manifest>\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 << "<file>\n"
+                       // save the relative path
+                       << to_utf8(makeRelPath(from_utf8(it->absFilename()),
+                               from_utf8(files.buffer_->filePath()))) << '\n'
+                       << it->inzipName() << '\n'
+                       << it->status() << '\n'
+                       << "</file>\n";
+       }
+       os << "</manifest>\n";
+       return os;
+}
+
+}
diff --git a/src/EmbeddedFiles.h b/src/EmbeddedFiles.h
new file mode 100644 (file)
index 0000000..a06e6a2
--- /dev/null
@@ -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 <vector>
+#include <utility>
+
+#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<EmbeddedFile> 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
index 412073fc935872039daae0df78e5acec490061a0..2770227f8e69c62b1e3612938c2510388d79803c 100644 (file)
@@ -127,6 +127,8 @@ liblyxcore_la_SOURCES = \
        DispatchResult.h \
        DocIterator.cpp \
        DocIterator.h \
+       EmbeddedFiles.h \
+       EmbeddedFiles.cpp \
        Encoding.cpp \
        Encoding.h \
        ErrorList.cpp \
index 03999ec2adbdf0e470ebfe1590f2096713ffa8a2..6b2b14a7310c12f99beecbb4fbfd210dfff44cb1 100644 (file)
@@ -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; }
index 9b51d7b23277fe900152e0c403cd0928f6474dc8..31115a4a85b1d4b1e675ea66ea2f3996a8a5c5a6 100644 (file)
@@ -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());
index 3c300e4f3a1760518aae13c1f78bb3b323d10c99..ac6cc991fa8e9296d38da9c89463bf39c9716b7c 100644 (file)
@@ -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 &);
index cbfd9ddd56e2ff213fb03711adcdfd5e4a42d7d4..247268fada8a18f50d9671242b13d739e27bdda9 100644 (file)
@@ -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());
index 454b1a8e08ca4825cabd9135f045d14614942a1d..ee96bc3b626edd322e47ad37f1f6128e0b038b2b 100644 (file)
@@ -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 &);
        ///
index 5be47172d4840d686d6315936e4c49262147240b..82a48be4f9b6987e170e9d2499f77eb2f3bef733 100644 (file)
@@ -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)
index fe21dba0f5a464dea7682b2ab571c5ed4047928c..41d11ab12663a616ed7c4cd8951e6b0a947818b4 100644 (file)
@@ -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 &);
        ///