3 * \file EmbeddedFiles.cpp
4 * This file is part of LyX, the document processor.
5 * Licence details can be found in the file COPYING.
9 * Full author contact details are available in file CREDITS.
15 #include "EmbeddedFiles.h"
18 #include "BufferParams.h"
19 #include "ErrorList.h"
21 #include "InsetIterator.h"
24 #include "Paragraph.h"
27 #include "frontends/alert.h"
29 #include "support/debug.h"
30 #include "support/filetools.h"
31 #include "support/gettext.h"
32 #include "support/convert.h"
33 #include "support/lstrings.h"
34 #include "support/FileZipListDir.h"
41 using namespace lyx::support;
45 namespace Alert = frontend::Alert;
48 EmbeddedFile::EmbeddedFile(string const & file, string const & inzip_name,
49 bool embed, Inset const * inset)
50 : DocFileName(file, true), inzip_name_(inzip_name), embedded_(embed),
54 inset_list_.push_back(inset);
58 string EmbeddedFile::embeddedFile(Buffer const * buf) const
60 return addName(buf->temppath(), inzip_name_);
64 void EmbeddedFile::addInset(Inset const * inset)
66 inset_list_.push_back(inset);
70 Inset const * EmbeddedFile::inset(int idx) const
72 BOOST_ASSERT(idx < refCount());
73 // some embedded file do not have a valid par iterator
74 return inset_list_[idx];
78 void EmbeddedFile::saveBookmark(Buffer const * buf, int idx) const
80 Inset const * ptr = inset(idx);
81 // This might not be the most efficient method ...
82 for (InsetIterator it = inset_iterator_begin(buf->inset()); it; ++it)
84 // this is basically BufferView::saveBookmark(0)
85 LyX::ref().session().bookmarks().save(
86 FileName(buf->absFileName()),
94 // this inset can not be located. There is something wrong that needs
100 string EmbeddedFile::availableFile(Buffer const * buf) const
102 return embedded() ? embeddedFile(buf) : absFilename();
106 bool EmbeddedFile::extract(Buffer const * buf) const
108 string ext_file = absFilename();
109 string emb_file = embeddedFile(buf);
111 FileName emb(emb_file);
112 FileName ext(ext_file);
117 // if external file already exists ...
119 // no need to copy if the files are the same
120 if (checksum() == FileName(emb_file).checksum())
122 // otherwise, ask if overwrite
123 int ret = Alert::prompt(
124 _("Overwrite external file?"),
125 bformat(_("External file %1$s already exists, do you want to overwrite it"),
126 from_utf8(ext_file)), 1, 1, _("&Overwrite"), _("&Cancel"));
128 // if the user does not want to overwrite, we still consider it
129 // a successful operation.
134 // need to make directory?
135 FileName path = ext.onlyPath();
136 if (!path.createPath()) {
137 Alert::error(_("Copy file failure"),
138 bformat(_("Cannot create file path '%1$s'.\n"
139 "Please check whether the path is writeable."),
140 from_utf8(path.absFilename())));
147 Alert::error(_("Copy file failure"),
148 bformat(_("Cannot copy file %1$s to %2$s.\n"
149 "Please check whether the directory exists and is writeable."),
150 from_utf8(emb_file), from_utf8(ext_file)));
155 bool EmbeddedFile::updateFromExternalFile(Buffer const * buf) const
157 string ext_file = absFilename();
158 string emb_file = embeddedFile(buf);
160 FileName emb(emb_file);
161 FileName ext(ext_file);
166 // if embedded file already exists ...
168 // no need to copy if the files are the same
169 if (checksum() == FileName(emb_file).checksum())
171 // other wise, ask if overwrite
172 int const ret = Alert::prompt(
173 _("Update embedded file?"),
174 bformat(_("Embedded file %1$s already exists, do you want to overwrite it"),
175 from_utf8(ext_file)), 1, 1, _("&Overwrite"), _("&Cancel"));
177 // if the user does not want to overwrite, we still consider it
178 // a successful operation.
182 // need to make directory?
183 FileName path = emb.onlyPath();
184 if (!path.isDirectory())
188 Alert::error(_("Copy file failure"),
189 bformat(_("Cannot copy file %1$s to %2$s.\n"
190 "Please check whether the directory exists and is writeable."),
191 from_utf8(ext_file), from_utf8(emb_file)));
192 //LYXERR(Debug::DEBUG, "Fs error: " << fe.what());
197 void EmbeddedFile::updateInsets(Buffer const * buf) const
199 vector<Inset const *>::const_iterator it = inset_list_.begin();
200 vector<Inset const *>::const_iterator it_end = inset_list_.end();
201 for (; it != it_end; ++it)
202 const_cast<Inset *>(*it)->updateEmbeddedFile(*buf, *this);
206 bool EmbeddedFiles::enabled() const
208 return buffer_->params().embedded;
212 void EmbeddedFiles::enable(bool flag)
214 if (enabled() != flag) {
215 // if enable, copy all files to temppath()
216 // if disable, extract all files
217 if ((flag && !updateFromExternalFile()) || (!flag && !extract()))
219 // if operation is successful
220 buffer_->markDirty();
221 buffer_->params().embedded = flag;
228 EmbeddedFile & EmbeddedFiles::registerFile(string const & filename,
229 bool embed, Inset const * inset, string const & inzipName)
231 // filename can be relative or absolute, translate to absolute filename
232 string abs_filename = makeAbsPath(filename, buffer_->filePath()).absFilename();
233 // try to find this file from the list
234 EmbeddedFileList::iterator it = file_list_.begin();
235 EmbeddedFileList::iterator it_end = file_list_.end();
236 for (; it != it_end; ++it)
237 if (it->absFilename() == abs_filename || it->embeddedFile(buffer_) == abs_filename)
239 // find this filename, keep the original embedding status
240 if (it != file_list_.end()) {
245 file_list_.push_back(EmbeddedFile(abs_filename,
246 getInzipName(abs_filename, inzipName), embed, inset));
247 return file_list_.back();
251 void EmbeddedFiles::update()
255 for (InsetIterator it = inset_iterator_begin(buffer_->inset()); it; ++it)
256 it->registerEmbeddedFiles(*buffer_, *this);
260 bool EmbeddedFiles::writeFile(DocFileName const & filename)
262 // file in the temporary path has the content
263 string const content = FileName(addName(buffer_->temppath(),
264 "content.lyx")).toFilesystemEncoding();
266 vector<pair<string, string> > filenames;
267 // add content.lyx to filenames
268 filenames.push_back(make_pair(content, "content.lyx"));
269 // prepare list of embedded file
270 EmbeddedFileList::iterator it = file_list_.begin();
271 EmbeddedFileList::iterator it_end = file_list_.end();
272 for (; it != it_end; ++it) {
273 if (it->embedded()) {
274 string file = it->availableFile(buffer_);
276 lyxerr << "File " << it->absFilename() << " does not exist. Skip embedding it. " << endl;
278 filenames.push_back(make_pair(file, it->inzipName()));
281 // write a zip file with all these files. Write to a temp file first, to
282 // avoid messing up the original file in case something goes terribly wrong.
283 DocFileName zipfile(addName(buffer_->temppath(),
284 onlyFilename(changeExtension(
285 filename.toFilesystemEncoding(), ".zip"))));
287 ::zipFiles(zipfile.toFilesystemEncoding(), filenames);
289 if (!zipfile.copyTo(filename)) {
290 Alert::error(_("Save failure"),
291 bformat(_("Cannot create file %1$s.\n"
292 "Please check whether the directory exists and is writeable."),
293 from_utf8(filename.absFilename())));
294 //LYXERR(Debug::DEBUG, "Fs error: " << fe.what());
300 EmbeddedFiles::EmbeddedFileList::const_iterator
301 EmbeddedFiles::find(string filename) const
303 EmbeddedFileList::const_iterator it = file_list_.begin();
304 EmbeddedFileList::const_iterator it_end = file_list_.end();
305 for (; it != it_end; ++it)
306 if (it->absFilename() == filename || it->embeddedFile(buffer_) == filename)
308 return file_list_.end();
312 bool EmbeddedFiles::extract() const
314 EmbeddedFileList::const_iterator it = file_list_.begin();
315 EmbeddedFileList::const_iterator it_end = file_list_.end();
316 for (; it != it_end; ++it)
318 if(!it->extract(buffer_))
324 bool EmbeddedFiles::updateFromExternalFile() const
326 EmbeddedFileList::const_iterator it = file_list_.begin();
327 EmbeddedFileList::const_iterator it_end = file_list_.end();
328 for (; it != it_end; ++it)
330 if (!it->updateFromExternalFile(buffer_))
336 string const EmbeddedFiles::getInzipName(string const & abs_filename, string const & name)
338 // register a new one, using relative file path as inzip_name
339 string inzip_name = name;
341 inzip_name = to_utf8(makeRelPath(from_utf8(abs_filename),
342 from_utf8(buffer_->filePath())));
343 // if inzip_name is an absolute path, use filename only to avoid
344 // leaking of filesystem information in inzip_name
345 // The second case covers cases '../path/file' and '.'
346 if (absolutePath(inzip_name) || prefixIs(inzip_name, "."))
347 inzip_name = onlyFilename(abs_filename);
348 // if this name has been used...
349 // use _1_name, _2_name etc
350 string tmp = inzip_name;
351 EmbeddedFileList::iterator it;
352 EmbeddedFileList::iterator it_end = file_list_.end();
353 bool unique_name = false;
355 while (!unique_name) {
358 inzip_name = convert<string>(i) + "_" + tmp;
359 it = file_list_.begin();
360 for (; it != it_end; ++it)
361 if (it->inzipName() == inzip_name) {
371 void EmbeddedFiles::updateInsets() const
373 EmbeddedFiles::EmbeddedFileList::const_iterator it = begin();
374 EmbeddedFiles::EmbeddedFileList::const_iterator it_end = end();
375 for (; it != it_end; ++it)
376 if (it->refCount() > 0)
377 it->updateInsets(buffer_);