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