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