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