> I will remove that small feature though, because embedding status will
> be lost if a file without any embedded file is reopened. This can
> upset the user if s/he insert a figure later and expect it to be
> embedded.

Updated patch attached. This time, embedding will always result in a
zipped file.

OK to apply?
Bo
Index: src/insets/InsetGraphics.h
===================================================================
--- src/insets/InsetGraphics.h	(revision 19920)
+++ src/insets/InsetGraphics.h	(working copy)
@@ -78,6 +78,8 @@
 	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: src/insets/InsetExternal.cpp
===================================================================
--- src/insets/InsetExternal.cpp	(revision 19920)
+++ src/insets/InsetExternal.cpp	(working copy)
@@ -481,6 +481,13 @@
 }
 
 
+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: src/insets/InsetGraphics.cpp
===================================================================
--- src/insets/InsetGraphics.cpp	(revision 19920)
+++ src/insets/InsetGraphics.cpp	(working copy)
@@ -71,6 +71,7 @@
 #include "Mover.h"
 #include "OutputParams.h"
 #include "sgml.h"
+#include "EmbeddedFiles.h"
 
 #include "frontends/alert.h"
 
@@ -230,6 +231,14 @@
 }
 
 
+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: src/insets/InsetInclude.cpp
===================================================================
--- src/insets/InsetInclude.cpp	(revision 19920)
+++ src/insets/InsetInclude.cpp	(working copy)
@@ -958,6 +958,16 @@
 }
 
 
+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: src/insets/Inset.h
===================================================================
--- src/insets/Inset.h	(revision 19920)
+++ src/insets/Inset.h	(working copy)
@@ -49,6 +49,7 @@
 class ParIterator;
 class Text;
 class TocList;
+class EmbeddedFiles;
 
 
 namespace graphics { class PreviewLoader; }
@@ -438,6 +439,9 @@
 	/// 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: src/insets/InsetInclude.h
===================================================================
--- src/insets/InsetInclude.h	(revision 19920)
+++ src/insets/InsetInclude.h	(working copy)
@@ -18,6 +18,7 @@
 #include "RenderButton.h"
 #include "MailInset.h"
 #include "Counters.h"
+#include "EmbeddedFiles.h"
 
 #include "support/FileName.h"
 
@@ -103,6 +104,9 @@
 	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 &);
 	///
Index: src/insets/InsetExternal.h
===================================================================
--- src/insets/InsetExternal.h	(revision 19920)
+++ src/insets/InsetExternal.h	(working copy)
@@ -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 @@
 	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: src/EmbeddedFiles.cpp
===================================================================
--- src/EmbeddedFiles.cpp	(revision 0)
+++ src/EmbeddedFiles.cpp	(revision 0)
@@ -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;
+}
+
+}
Index: src/Buffer.h
===================================================================
--- src/Buffer.h	(revision 19920)
+++ src/Buffer.h	(working copy)
@@ -396,6 +396,11 @@
 	TocBackend & tocBackend();
 	TocBackend const & tocBackend() const;
 	//@}
+	
+	//@{
+	EmbeddedFiles & embeddedFiles();
+	EmbeddedFiles const & embeddedFiles() const;
+	//@}
 
 private:
 	/** Inserts a file into a document
Index: src/BufferParams.h
===================================================================
--- src/BufferParams.h	(revision 19920)
+++ src/BufferParams.h	(working copy)
@@ -261,6 +261,8 @@
 	std::string parentname;
 	///
 	bool compressed;
+	///
+	bool embedded;
 
 	/// the author list for the document
 	AuthorList & authors();
Index: src/Makefile.am
===================================================================
--- src/Makefile.am	(revision 19920)
+++ src/Makefile.am	(working copy)
@@ -127,6 +127,8 @@
 	DispatchResult.h \
 	DocIterator.cpp \
 	DocIterator.h \
+	EmbeddedFiles.h \
+	EmbeddedFiles.cpp \
 	Encoding.cpp \
 	Encoding.h \
 	ErrorList.cpp \
Index: src/EmbeddedFiles.h
===================================================================
--- src/EmbeddedFiles.h	(revision 0)
+++ src/EmbeddedFiles.h	(revision 0)
@@ -0,0 +1,244 @@
+// -*- 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. There is no subdirectory in this zip file (with current
+implementation).
+
+4. If no file is embedded, file.lyx is in plain text format. This is desired
+by many users because pure-text format allows easy manipulation and better
+version control.
+
+5. 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.
+
+6. 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 (no directory structure), 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.
+If there is any embedded files, these files are compressed along with
+file.lyx and a manifest.txt. If there is no embedded file, or 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: src/Buffer.cpp
===================================================================
--- src/Buffer.cpp	(revision 19920)
+++ src/Buffer.cpp	(working copy)
@@ -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::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::tempName;
 using support::trim;
 using support::sum;
+using support::unzipToDir;
 
 namespace Alert = frontend::Alert;
 namespace os = support::os;
@@ -194,6 +197,9 @@
 	/// 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 @@
 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 @@
 }
 
 
+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::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 @@
 	// 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 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: src/BufferParams.cpp
===================================================================
--- src/BufferParams.cpp	(revision 19920)
+++ src/BufferParams.cpp	(working copy)
@@ -353,6 +353,9 @@
 	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: po/POTFILES.in
===================================================================
--- po/POTFILES.in	(revision 19920)
+++ po/POTFILES.in	(working copy)
@@ -7,6 +7,7 @@
 src/Color.cpp
 src/Converter.cpp
 src/CutAndPaste.cpp
+src/EmbeddedFiles.cpp
 src/Exporter.cpp
 src/Font.cpp
 src/Format.cpp
Index: development/scons/scons_manifest.py
===================================================================
--- development/scons/scons_manifest.py	(revision 19920)
+++ development/scons/scons_manifest.py	(working copy)
@@ -56,6 +56,7 @@
     Dimension.h
     DispatchResult.h
     DocIterator.h
+    EmbeddedFiles.h
     Encoding.h
     ErrorList.h
     Exporter.h
@@ -166,6 +167,7 @@
     CutAndPaste.cpp
     DepTable.cpp
     DocIterator.cpp
+    EmbeddedFiles.cpp
     Encoding.cpp
     ErrorList.cpp
     Exporter.cpp

Reply via email to