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