]> git.lyx.org Git - lyx.git/blob - src/BufferList.cpp
d4dfbdbf642eb42e362b7338d0e93d3d6c155281
[lyx.git] / src / BufferList.cpp
1 /**
2  * \file BufferList.cpp
3  * This file is part of LyX, the document processor.
4  * Licence details can be found in the file COPYING.
5  *
6  * \author Lars Gullik Bjønnes
7  *
8  * Full author contact details are available in file CREDITS.
9  */
10
11 #include <config.h>
12
13 #include "BufferList.h"
14
15 #include "Author.h"
16 #include "Buffer.h"
17 #include "BufferParams.h"
18 #include "debug.h"
19 #include "gettext.h"
20 #include "Session.h"
21 #include "LyX.h"
22 #include "output_latex.h"
23 #include "ParagraphList.h"
24
25 #include "frontends/alert.h"
26
27 #include "support/filetools.h"
28 #include "support/Package.h"
29
30 #include <boost/bind.hpp>
31
32 #include <algorithm>
33 #include <functional>
34
35 using boost::bind;
36
37 using std::auto_ptr;
38 using std::endl;
39 using std::equal_to;
40 using std::find;
41 using std::find_if;
42 using std::for_each;
43 using std::string;
44 using std::vector;
45 using std::back_inserter;
46 using std::transform;
47
48
49 namespace lyx {
50
51 using support::addName;
52 using support::bformat;
53 using support::FileName;
54 using support::makeDisplayPath;
55 using support::onlyFilename;
56 using support::removeAutosaveFile;
57 using support::package;
58 using support::prefixIs;
59
60 namespace Alert = lyx::frontend::Alert;
61
62
63 BufferList::BufferList()
64 {}
65
66
67 bool BufferList::empty() const
68 {
69         return bstore.empty();
70 }
71
72
73 BufferList::iterator BufferList::begin()
74 {
75         return bstore.begin();
76 }
77
78
79 BufferList::const_iterator BufferList::begin() const
80 {
81         return bstore.begin();
82 }
83
84
85 BufferList::iterator BufferList::end()
86 {
87         return bstore.end();
88 }
89
90
91 BufferList::const_iterator BufferList::end() const
92 {
93         return bstore.end();
94 }
95
96
97 bool BufferList::quitWriteBuffer(Buffer * buf)
98 {
99         BOOST_ASSERT(buf);
100
101         docstring file;
102         if (buf->isUnnamed())
103                 file = from_utf8(onlyFilename(buf->fileName()));
104         else
105                 file = makeDisplayPath(buf->fileName(), 30);
106
107         docstring const text =
108                 bformat(_("The document %1$s has unsaved changes.\n\n"
109                                        "Do you want to save the document or discard the changes?"),
110                                            file);
111         int const ret = Alert::prompt(_("Save changed document?"),
112                 text, 0, 2, _("&Save"), _("&Discard"), _("&Cancel"));
113
114         if (ret == 0) {
115                 // FIXME: WriteAs can be asynch !
116                 // but not right now...maybe we should remove that
117
118                 bool succeeded;
119
120                 if (buf->isUnnamed())
121                         succeeded = buf->writeAs();
122                 else
123                         succeeded = buf->menuWrite();
124
125                 if (!succeeded)
126                         return false;
127         } else if (ret == 1) {
128                 // if we crash after this we could
129                 // have no autosave file but I guess
130                 // this is really inprobable (Jug)
131                 if (buf->isUnnamed())
132                         removeAutosaveFile(buf->fileName());
133
134         } else {
135                 return false;
136         }
137
138         return true;
139 }
140
141
142 bool BufferList::quitWriteAll()
143 {
144         BufferStorage::iterator it = bstore.begin();
145         BufferStorage::iterator end = bstore.end();
146         for (; it != end; ++it) {
147                 if ((*it)->isClean())
148                         continue;
149
150                 if (!quitWriteBuffer(*it))
151                         return false;
152         }
153         // now, all buffers have been written sucessfully
154         // save file names to .lyx/session
155         it = bstore.begin();
156         for (; it != end; ++it) {
157                 // if master/slave are both open, do not save slave since it
158                 // will be automatically loaded when the master is loaded
159                 if ((*it)->getMasterBuffer() == (*it))
160                         LyX::ref().session().lastOpened().add(FileName((*it)->fileName()));
161         }
162
163         return true;
164 }
165
166
167 void BufferList::release(Buffer * buf)
168 {
169         BOOST_ASSERT(buf);
170         BufferStorage::iterator const it =
171                 find(bstore.begin(), bstore.end(), buf);
172         if (it != bstore.end()) {
173                 Buffer * tmp = (*it);
174                 BOOST_ASSERT(tmp);
175                 bstore.erase(it);
176                 delete tmp;
177         }
178 }
179
180
181 Buffer * BufferList::newBuffer(string const & s, bool const ronly)
182 {
183         auto_ptr<Buffer> tmpbuf(new Buffer(s, ronly));
184         tmpbuf->params().useClassDefaults();
185         LYXERR(Debug::INFO) << "Assigning to buffer "
186                             << bstore.size() << endl;
187         bstore.push_back(tmpbuf.get());
188         return tmpbuf.release();
189 }
190
191
192 void BufferList::closeAll()
193 {
194         while (!bstore.empty()) {
195                 close(bstore.front(), false);
196         }
197 }
198
199
200 bool BufferList::close(Buffer * buf, bool const ask)
201 {
202         BOOST_ASSERT(buf);
203
204         if (!ask || buf->isClean() || buf->paragraphs().empty()) {
205                 release(buf);
206                 return true;
207         }
208
209         docstring fname;
210         if (buf->isUnnamed())
211                 fname = from_utf8(onlyFilename(buf->fileName()));
212         else
213                 fname = makeDisplayPath(buf->fileName(), 30);
214
215         docstring const text =
216                 bformat(_("The document %1$s has unsaved changes.\n\n"
217                                        "Do you want to save the document or discard the changes?"),
218                                            fname);
219         int const ret = Alert::prompt(_("Save changed document?"),
220                 text, 0, 2, _("&Save"), _("&Discard"), _("&Cancel"));
221
222         if (ret == 0) {
223                 if (buf->isUnnamed()) {
224                         if (!buf->writeAs())
225                                 return false;
226                 } else if (!buf->menuWrite())
227                         return false;
228         } else if (ret == 2)
229                 return false;
230                 
231         removeAutosaveFile(buf->fileName());
232
233         release(buf);
234         return true;
235 }
236
237
238 vector<string> const BufferList::getFileNames() const
239 {
240         vector<string> nvec;
241         transform(bstore.begin(), bstore.end(),
242                   back_inserter(nvec),
243                   boost::bind(&Buffer::fileName, _1));
244         return nvec;
245 }
246
247
248 Buffer * BufferList::first()
249 {
250         if (bstore.empty())
251                 return 0;
252         return bstore.front();
253 }
254
255
256 Buffer * BufferList::last()
257 {
258         if (bstore.empty())
259                 return 0;
260         return bstore.back();
261 }
262
263
264 Buffer * BufferList::getBuffer(unsigned int choice)
265 {
266         if (choice >= bstore.size())
267                 return 0;
268         return bstore[choice];
269 }
270
271
272 Buffer * BufferList::next(Buffer const * buf) const
273 {
274         BOOST_ASSERT(buf);
275
276         if (bstore.empty())
277                 return 0;
278         BufferStorage::const_iterator it = find(bstore.begin(),
279                                                 bstore.end(), buf);
280         BOOST_ASSERT(it != bstore.end());
281         ++it;
282         if (it == bstore.end())
283                 return bstore.front();
284         else
285                 return *it;
286 }
287
288
289 Buffer * BufferList::previous(Buffer const * buf) const
290 {
291         BOOST_ASSERT(buf);
292
293         if (bstore.empty())
294                 return 0;
295         BufferStorage::const_iterator it = find(bstore.begin(),
296                                                 bstore.end(), buf);
297         BOOST_ASSERT(it != bstore.end());
298         if (it == bstore.begin())
299                 return bstore.back();
300         else
301                 return *(it - 1);
302 }
303
304
305 void BufferList::updateIncludedTeXfiles(string const & mastertmpdir,
306                                         OutputParams const & runparams)
307 {
308         BufferStorage::iterator it = bstore.begin();
309         BufferStorage::iterator end = bstore.end();
310         for (; it != end; ++it) {
311                 if (!(*it)->isDepClean(mastertmpdir)) {
312                         string writefile = addName(mastertmpdir, (*it)->getLatexName());
313                         (*it)->makeLaTeXFile(FileName(writefile), mastertmpdir,
314                                              runparams, false);
315                         (*it)->markDepClean(mastertmpdir);
316                 }
317         }
318 }
319
320
321 void BufferList::emergencyWriteAll()
322 {
323         for_each(bstore.begin(), bstore.end(),
324                  bind(&BufferList::emergencyWrite, this, _1));
325 }
326
327
328 void BufferList::emergencyWrite(Buffer * buf)
329 {
330         // Use ::assert to avoid a loop, BOOST_ASSERT ends up calling ::assert
331         // compare with 0 to avoid pointer/interger comparison
332         // ::assert(buf != 0);
333         if (!buf)
334                 return;
335
336         // No need to save if the buffer has not changed.
337         if (buf->isClean())
338                 return;
339
340         string const doc = buf->isUnnamed()
341                 ? onlyFilename(buf->fileName()) : buf->fileName();
342
343         lyxerr << to_utf8(
344                 bformat(_("LyX: Attempting to save document %1$s"), from_utf8(doc)))
345                 << endl;
346
347         // We try to save three places:
348         // 1) Same place as document. Unless it is an unnamed doc.
349         if (!buf->isUnnamed()) {
350                 string s = buf->fileName();
351                 s += ".emergency";
352                 lyxerr << "  " << s << endl;
353                 if (buf->writeFile(FileName(s))) {
354                         buf->markClean();
355                         lyxerr << to_utf8(_("  Save seems successful. Phew.")) << endl;
356                         return;
357                 } else {
358                         lyxerr << to_utf8(_("  Save failed! Trying...")) << endl;
359                 }
360         }
361
362         // 2) In HOME directory.
363         string s = addName(package().home_dir().absFilename(), buf->fileName());
364         s += ".emergency";
365         lyxerr << ' ' << s << endl;
366         if (buf->writeFile(FileName(s))) {
367                 buf->markClean();
368                 lyxerr << to_utf8(_("  Save seems successful. Phew.")) << endl;
369                 return;
370         }
371
372         lyxerr << to_utf8(_("  Save failed! Trying...")) << endl;
373
374         // 3) In "/tmp" directory.
375         // MakeAbsPath to prepend the current
376         // drive letter on OS/2
377         s = addName(package().temp_dir().absFilename(), buf->fileName());
378         s += ".emergency";
379         lyxerr << ' ' << s << endl;
380         if (buf->writeFile(FileName(s))) {
381                 buf->markClean();
382                 lyxerr << to_utf8(_("  Save seems successful. Phew.")) << endl;
383                 return;
384         }
385         lyxerr << to_utf8(_("  Save failed! Bummer. Document is lost.")) << endl;
386 }
387
388
389 bool BufferList::exists(string const & s) const
390 {
391         return find_if(bstore.begin(), bstore.end(),
392                        bind(equal_to<string>(),
393                             bind(&Buffer::fileName, _1),
394                             s))
395                 != bstore.end();
396 }
397
398
399 bool BufferList::isLoaded(Buffer const * b) const
400 {
401         BOOST_ASSERT(b);
402         BufferStorage::const_iterator cit =
403                 find(bstore.begin(), bstore.end(), b);
404         return cit != bstore.end();
405 }
406
407
408 Buffer * BufferList::getBuffer(string const & s)
409 {
410         BufferStorage::iterator it =
411                 find_if(bstore.begin(), bstore.end(),
412                         bind(equal_to<string>(),
413                              bind(&Buffer::fileName, _1),
414                              s));
415
416         return it != bstore.end() ? (*it) : 0;
417 }
418
419
420 Buffer * BufferList::getBufferFromTmp(string const & s)
421 {
422         BufferStorage::iterator it = bstore.begin();
423         BufferStorage::iterator end = bstore.end();
424         for (; it < end; ++it)
425                 if (prefixIs(s, (*it)->temppath()))
426                         return *it;
427         return 0;
428 }
429
430
431 void BufferList::setCurrentAuthor(docstring const & name, docstring const & email)
432 {
433         BufferStorage::iterator it = bstore.begin();
434         BufferStorage::iterator end = bstore.end();
435         for (; it != end; ++it) {
436                 (*it)->params().authors().record(0, Author(name, email));
437         }
438 }
439
440
441 } // namespace lyx