]> git.lyx.org Git - lyx.git/blob - src/EmbeddedFiles.cpp
Fix dialog handling of Insert Plain Text
[lyx.git] / src / EmbeddedFiles.cpp
1 // -*- C++ -*-
2 /**
3  * \file EmbeddedFiles.cpp
4  * This file is part of LyX, the document processor.
5  * Licence details can be found in the file COPYING.
6  *
7  * \author Bo Peng
8  *
9  * Full author contact details are available in file CREDITS.
10  *
11  */
12
13 #include <config.h>
14
15 #include "EmbeddedFiles.h"
16 #include "Buffer.h"
17 #include "BufferParams.h"
18 #include "Paragraph.h"
19 #include "InsetIterator.h"
20 #include "debug.h"
21 #include "gettext.h"
22 #include "Format.h"
23 #include "Lexer.h"
24 #include "ErrorList.h"
25
26 #include "frontends/alert.h"
27
28 #include <boost/filesystem/operations.hpp>
29
30 #include "support/filetools.h"
31 #include "support/convert.h"
32 #include "support/lyxlib.h"
33 #include "support/lstrings.h"
34
35 #include "LyX.h"
36 #include "Session.h"
37
38 #include <sstream>
39 #include <fstream>
40 #include <utility>
41
42 using std::ofstream;
43 using std::endl;
44 using std::vector;
45 using std::string;
46 using std::pair;
47 using std::make_pair;
48 using std::istream;
49 using std::ostream;
50 using std::getline;
51 using std::istringstream;
52
53 namespace lyx {
54
55 namespace fs = boost::filesystem;
56 namespace Alert = frontend::Alert;
57
58 using support::FileName;
59 using support::DocFileName;
60 using support::makeAbsPath;
61 using support::addName;
62 using support::absolutePath;
63 using support::onlyFilename;
64 using support::makeRelPath;
65 using support::changeExtension;
66 using support::bformat;
67 using support::prefixIs;
68 using support::sum;
69 using support::makedir;
70
71
72 EmbeddedFile::EmbeddedFile(string const & file, string const & inzip_name,
73         bool embed, Inset const * inset)
74         : DocFileName(file, true), inzip_name_(inzip_name), embedded_(embed),
75                 inset_list_()
76 {
77         if (inset != NULL)
78                 inset_list_.push_back(inset);
79 }
80
81
82 string EmbeddedFile::embeddedFile(Buffer const * buf) const
83 {
84         return addName(buf->temppath(), inzip_name_);
85 }
86
87
88 void EmbeddedFile::addInset(Inset const * inset)
89 {
90         inset_list_.push_back(inset);
91 }
92
93
94 Inset const * EmbeddedFile::inset(int idx) const
95 {
96         BOOST_ASSERT(idx < refCount());
97         // some embedded file do not have a valid par iterator
98         return inset_list_[idx];
99 }
100
101
102 void EmbeddedFile::saveBookmark(Buffer const * buf, int idx) const
103 {
104         Inset const * ptr = inset(idx);
105         // This might not be the most efficient method ... 
106         for (InsetIterator it = inset_iterator_begin(buf->inset()); it; ++it)
107                 if (&(*it) == ptr) {
108                         // this is basically BufferView::saveBookmark(0)
109                         LyX::ref().session().bookmarks().save(
110                                 FileName(buf->absFileName()),
111                                 it.bottom().pit(),
112                                 it.bottom().pos(),
113                                 it.paragraph().id(),
114                                 it.pos(),
115                                 0
116                         );
117                 }
118         // this inset can not be located. There is something wrong that needs
119         // to be fixed.
120         BOOST_ASSERT(true);
121 }
122
123
124 string EmbeddedFile::availableFile(Buffer const * buf) const
125 {
126         return embedded() ? embeddedFile(buf) : absFilename();
127 }
128
129
130 bool EmbeddedFile::extract(Buffer const * buf) const
131 {
132         string ext_file = absFilename();
133         string emb_file = embeddedFile(buf);
134
135         FileName emb(emb_file);
136         FileName ext(ext_file);
137
138         if (!emb.exists())
139                 return false;
140
141         // if external file already exists ...
142         if (ext.exists()) {
143                 // no need to copy if the files are the same
144                 if (sum(*this) == sum(FileName(emb_file)))
145                         return true;
146                 // otherwise, ask if overwrite
147                 int ret = Alert::prompt(
148                         _("Overwrite external file?"),
149                         bformat(_("External file %1$s already exists, do you want to overwrite it"),
150                                 from_utf8(ext_file)), 1, 1, _("&Overwrite"), _("&Cancel"));
151                 if (ret != 0)
152                         // if the user does not want to overwrite, we still consider it
153                         // a successful operation.
154                         return true;
155         }
156         // copy file
157
158         // need to make directory?
159         string path = support::onlyPath(ext_file);
160         if (!fs::is_directory(path))
161                 makedir(const_cast<char*>(path.c_str()), 0755);
162         if (emb.copyTo(ext, false))
163                 return true;
164         Alert::error(_("Copy file failure"),
165                  bformat(_("Cannot copy file %1$s to %2$s.\n"
166                                  "Please check whether the directory exists and is writeable."),
167                                 from_utf8(emb_file), from_utf8(ext_file)));
168         //LYXERR(Debug::DEBUG, "Fs error: " << fe.what());
169         return false;
170 }
171
172
173 bool EmbeddedFile::updateFromExternalFile(Buffer const * buf) const
174 {
175         string ext_file = absFilename();
176         string emb_file = embeddedFile(buf);
177
178         FileName emb(emb_file);
179         FileName ext(ext_file);
180
181         if (!ext.exists())
182                 return false;
183         
184         // if embedded file already exists ...
185         if (emb.exists()) {
186                 // no need to copy if the files are the same
187                 if (sum(*this) == sum(FileName(emb_file)))
188                         return true;
189                 // other wise, ask if overwrite
190                 int const ret = Alert::prompt(
191                         _("Update embedded file?"),
192                         bformat(_("Embedded file %1$s already exists, do you want to overwrite it"),
193                                 from_utf8(ext_file)), 1, 1, _("&Overwrite"), _("&Cancel"));
194                 if (ret != 0)
195                         // if the user does not want to overwrite, we still consider it
196                         // a successful operation.
197                         return true;
198         }
199         // copy file
200         // need to make directory?
201         string path = support::onlyPath(emb_file);
202         if (!fs::is_directory(path))
203                 makedir(const_cast<char*>(path.c_str()), 0755);
204         if (ext.copyTo(emb, false))
205                 return true;
206         Alert::error(_("Copy file failure"),
207                  bformat(_("Cannot copy file %1$s to %2$s.\n"
208                            "Please check whether the directory exists and is writeable."),
209                                 from_utf8(ext_file), from_utf8(emb_file)));
210         //LYXERR(Debug::DEBUG, "Fs error: " << fe.what());
211         return false;
212 }
213
214
215 void EmbeddedFile::updateInsets(Buffer const * buf) const
216 {
217         vector<Inset const *>::const_iterator it = inset_list_.begin();
218         vector<Inset const *>::const_iterator it_end = inset_list_.end();
219         for (; it != it_end; ++it)
220                 const_cast<Inset *>(*it)->updateEmbeddedFile(*buf, *this);
221 }
222
223
224 bool EmbeddedFiles::enabled() const
225 {
226         return buffer_->params().embedded;
227 }
228
229
230 void EmbeddedFiles::enable(bool flag)
231 {
232         if (enabled() != flag) {
233                 // if enable, copy all files to temppath()
234                 // if disable, extract all files
235                 if ((flag && !updateFromExternalFile()) || (!flag && !extract()))
236                         return;
237                 // if operation is successful
238                 buffer_->markDirty();
239                 buffer_->params().embedded = flag;
240                 if (flag)
241                         updateInsets();
242         }
243 }
244
245
246 EmbeddedFile & EmbeddedFiles::registerFile(string const & filename,
247         bool embed, Inset const * inset, string const & inzipName)
248 {
249         // filename can be relative or absolute, translate to absolute filename
250         string abs_filename = makeAbsPath(filename, buffer_->filePath()).absFilename();
251         // try to find this file from the list
252         EmbeddedFileList::iterator it = file_list_.begin();
253         EmbeddedFileList::iterator it_end = file_list_.end();
254         for (; it != it_end; ++it)
255                 if (it->absFilename() == abs_filename || it->embeddedFile(buffer_) == abs_filename)
256                         break;
257         // find this filename, keep the original embedding status
258         if (it != file_list_.end()) {
259                 it->addInset(inset);
260                 return *it;
261         }
262         //
263         file_list_.push_back(EmbeddedFile(abs_filename, 
264                 getInzipName(abs_filename, inzipName), embed, inset));
265         return file_list_.back();
266 }
267
268
269 void EmbeddedFiles::update()
270 {
271         file_list_.clear();
272
273         for (InsetIterator it = inset_iterator_begin(buffer_->inset()); it; ++it)
274                 it->registerEmbeddedFiles(*buffer_, *this);
275 }
276
277
278 bool EmbeddedFiles::writeFile(DocFileName const & filename)
279 {
280         // file in the temporary path has the content
281         string const content = FileName(addName(buffer_->temppath(),
282                 "content.lyx")).toFilesystemEncoding();
283
284         vector<pair<string, string> > filenames;
285         // add content.lyx to filenames
286         filenames.push_back(make_pair(content, "content.lyx"));
287         // prepare list of embedded file
288         EmbeddedFileList::iterator it = file_list_.begin();
289         EmbeddedFileList::iterator it_end = file_list_.end();
290         for (; it != it_end; ++it) {
291                 if (it->embedded()) {
292                         string file = it->availableFile(buffer_);
293                         if (file.empty())
294                                 lyxerr << "File " << it->absFilename() << " does not exist. Skip embedding it. " << endl;
295                         else
296                                 filenames.push_back(make_pair(file, it->inzipName()));
297                 }
298         }
299         // write a zip file with all these files. Write to a temp file first, to
300         // avoid messing up the original file in case something goes terribly wrong.
301         DocFileName zipfile(addName(buffer_->temppath(),
302                 onlyFilename(changeExtension(
303                         filename.toFilesystemEncoding(), ".zip"))));
304
305         ::zipFiles(zipfile.toFilesystemEncoding(), filenames);
306         // copy file back
307         if (!zipfile.copyTo(filename, false)) {
308                 Alert::error(_("Save failure"),
309                                  bformat(_("Cannot create file %1$s.\n"
310                                            "Please check whether the directory exists and is writeable."),
311                                          from_utf8(filename.absFilename())));
312                 //LYXERR(Debug::DEBUG, "Fs error: " << fe.what());
313         }
314         return true;
315 }
316
317
318 EmbeddedFiles::EmbeddedFileList::const_iterator
319 EmbeddedFiles::find(std::string filename) const
320 {
321         EmbeddedFileList::const_iterator it = file_list_.begin();
322         EmbeddedFileList::const_iterator it_end = file_list_.end();
323         for (; it != it_end; ++it)
324                 if (it->absFilename() == filename || it->embeddedFile(buffer_) == filename)     
325                         return it;
326         return file_list_.end();
327 }
328
329
330 bool EmbeddedFiles::extract() const
331 {
332         EmbeddedFileList::const_iterator it = file_list_.begin();
333         EmbeddedFileList::const_iterator it_end = file_list_.end();
334         for (; it != it_end; ++it)
335                 if (it->embedded())
336                         if(!it->extract(buffer_))
337                                 return false;
338         return true;
339 }
340
341
342 bool EmbeddedFiles::updateFromExternalFile() const
343 {
344         EmbeddedFileList::const_iterator it = file_list_.begin();
345         EmbeddedFileList::const_iterator it_end = file_list_.end();
346         for (; it != it_end; ++it)
347                 if (it->embedded())
348                         if (!it->updateFromExternalFile(buffer_))
349                                 return false;
350         return true;
351 }
352
353
354 string const EmbeddedFiles::getInzipName(string const & abs_filename, string const & name)
355 {
356         // register a new one, using relative file path as inzip_name
357         string inzip_name = name;
358         if (name.empty())
359                 inzip_name = to_utf8(makeRelPath(from_utf8(abs_filename),
360                         from_utf8(buffer_->filePath())));
361         // if inzip_name is an absolute path, use filename only to avoid
362         // leaking of filesystem information in inzip_name
363         // The second case covers cases '../path/file' and '.'
364         if (absolutePath(inzip_name) || prefixIs(inzip_name, "."))
365                 inzip_name = onlyFilename(abs_filename);
366         // if this name has been used...
367         // use _1_name, _2_name etc
368         string tmp = inzip_name;
369         EmbeddedFileList::iterator it;
370         EmbeddedFileList::iterator it_end = file_list_.end();
371         bool unique_name = false;
372         size_t i = 0;
373         while (!unique_name) {
374                 unique_name = true;
375                 if (i > 0)
376                         inzip_name = convert<string>(i) + "_" + tmp;
377                 it = file_list_.begin();
378                 for (; it != it_end; ++it)
379                         if (it->inzipName() == inzip_name) {
380                                 unique_name = false;
381                                 ++i;
382                                 break;
383                         }
384         }
385         return inzip_name;
386 }
387
388
389 void EmbeddedFiles::updateInsets() const
390 {
391         EmbeddedFiles::EmbeddedFileList::const_iterator it = begin();
392         EmbeddedFiles::EmbeddedFileList::const_iterator it_end = end();
393         for (; it != it_end; ++it)
394                 if (it->refCount() > 0)
395                         it->updateInsets(buffer_);
396 }
397
398
399 }