]> git.lyx.org Git - lyx.git/blob - src/bufferlist.C
fix some C++ parsing bugs
[lyx.git] / src / bufferlist.C
1 /* This file is part of
2  * ======================================================
3  *
4  *           LyX, The Document Word Processor
5  *
6  *           Copyright 1995 Matthias Ettrich
7  *           Copyright 1995-2001 The LyX Team.
8  *
9  *           This file is Copyright 1996-2001
10  *           Lars Gullik Bjønnes
11  *
12  * ======================================================
13  */
14
15 #include <config.h>
16
17 #include "bufferlist.h"
18 #include "lyx_main.h"
19 #include "lastfiles.h"
20 #include "debug.h"
21 #include "lyxrc.h"
22 #include "lyxtext.h"
23 #include "lyx_cb.h"
24 #include "bufferview_funcs.h"
25 #include "BufferView.h"
26 #include "gettext.h"
27 #include "frontends/LyXView.h"
28 #include "vc-backend.h"
29 #include "TextCache.h"
30 #include "lyxlex.h"
31
32 #include "frontends/Alert.h"
33
34 #include "support/FileInfo.h"
35 #include "support/filetools.h"
36 #include "support/lyxmanip.h"
37 #include "support/lyxfunctional.h"
38 #include "support/LAssert.h"
39
40 #include <boost/bind.hpp>
41 #include "BoostFormat.h"
42
43 #include <cassert>
44 #include <algorithm>
45 #include <functional>
46
47
48 using std::vector;
49 using std::find;
50 using std::endl;
51 using std::find_if;
52 using std::for_each;
53 using std::mem_fun;
54
55 extern BufferView * current_view;
56
57
58 BufferList::BufferList()
59 {}
60
61
62 bool BufferList::empty() const
63 {
64         return bstore.empty();
65 }
66
67
68 bool BufferList::qwriteOne(Buffer * buf, string const & fname,
69                            string & unsaved_list)
70 {
71         bool reask = true;
72         while (reask) {
73                 switch (Alert::askConfirmation(_("Changes in document:"),
74                                        fname,
75                                        _("Save document?"))) {
76                 case 1: // Yes
77                         // FIXME: WriteAs can be asynch !
78                         if (buf->isUnnamed())
79                                 reask = !WriteAs(current_view, buf);
80                         else {
81                                 reask = !MenuWrite(current_view, buf);
82                         }
83                         break;
84                 case 2: // No
85                         // if we crash after this we could
86                         // have no autosave file but I guess
87                         // this is really inprobable (Jug)
88                         if (buf->isUnnamed()) {
89                                 removeAutosaveFile(buf->fileName());
90                         }
91
92                         unsaved_list += MakeDisplayPath(fname, 50) + "\n";
93                         return true;
94                 case 3: // Cancel
95                         return false;
96                 }
97         }
98         return true;
99 }
100
101
102 bool BufferList::qwriteAll()
103 {
104         string unsaved;
105         BufferStorage::iterator it = bstore.begin();
106         BufferStorage::iterator end = bstore.end();
107         for (; it != end; ++it) {
108                 if (!(*it)->isClean()) {
109                         string fname;
110                         if ((*it)->isUnnamed())
111                                 fname = OnlyFilename((*it)->fileName());
112                         else
113                                 fname = MakeDisplayPath((*it)->fileName(), 50);
114                         if (!qwriteOne(*it, fname, unsaved)) // cancel the request!
115                                 return false;
116                 }
117         }
118
119         return true;
120 }
121
122
123 void BufferList::release(Buffer * buf)
124 {
125         lyx::Assert(buf);
126         BufferStorage::iterator it = find(bstore.begin(), bstore.end(), buf);
127         if (it != bstore.end()) {
128                 // Make sure that we don't store a LyXText in
129                 // the textcache that points to the buffer
130                 // we just deleted.
131                 Buffer * tmp = (*it);
132                 bstore.erase(it);
133                 textcache.removeAllWithBuffer(tmp);
134                 delete tmp;
135         }
136 }
137
138
139 Buffer * BufferList::newBuffer(string const & s, bool ronly)
140 {
141         Buffer * tmpbuf = new Buffer(s, ronly);
142         tmpbuf->params.useClassDefaults();
143         lyxerr[Debug::INFO] << "Assigning to buffer "
144                             << bstore.size() << endl;
145         bstore.push_back(tmpbuf);
146         return tmpbuf;
147 }
148
149
150 void BufferList::closeAll()
151 {
152         // Since we are closing we can just as well delete all
153         // in the textcache this will also speed the closing/quiting up a bit.
154         textcache.clear();
155
156         while (!bstore.empty()) {
157                 close(bstore.front());
158         }
159 }
160
161
162 bool BufferList::close(Buffer * buf)
163 {
164         lyx::Assert(buf);
165
166         if (!buf->paragraphs.empty() && !buf->isClean() && !quitting) {
167                 string fname;
168                 if (buf->isUnnamed())
169                         fname = OnlyFilename(buf->fileName());
170                 else
171                         fname = MakeDisplayPath(buf->fileName(), 50);
172                 bool reask = true;
173                 while (reask) {
174                         switch (Alert::askConfirmation(_("Changes in document:"),
175                                                fname,
176                                                _("Save document?"))) {
177                         case 1: // Yes
178                                 if (buf->isUnnamed())
179                                         reask = !WriteAs(current_view, buf);
180                                 else if (buf->save()) {
181                                         lastfiles->newFile(buf->fileName());
182                                         reask = false;
183                                 } else {
184                                         return false;
185                                 }
186                                 break;
187                         case 2:
188                                 if (buf->isUnnamed()) {
189                                         removeAutosaveFile(buf->fileName());
190                                 }
191                                 reask = false;
192                                 break;
193                         case 3: // Cancel
194                                 return false;
195                         }
196                 }
197         }
198
199         release(buf);
200         return true;
201 }
202
203
204 vector<string> const BufferList::getFileNames() const
205 {
206         vector<string> nvec;
207         std::copy(bstore.begin(), bstore.end(),
208                   lyx::back_inserter_fun(nvec, &Buffer::fileName));
209         return nvec;
210 }
211
212
213 Buffer * BufferList::first()
214 {
215         if (bstore.empty())
216                 return 0;
217         return bstore.front();
218 }
219
220
221 Buffer * BufferList::getBuffer(unsigned int choice)
222 {
223         if (choice >= bstore.size())
224                 return 0;
225         return bstore[choice];
226 }
227
228
229 void BufferList::updateIncludedTeXfiles(string const & mastertmpdir)
230 {
231         BufferStorage::iterator it = bstore.begin();
232         BufferStorage::iterator end = bstore.end();
233         for (; it != end; ++it) {
234                 if (!(*it)->isDepClean(mastertmpdir)) {
235                         string writefile = mastertmpdir;
236                         writefile += '/';
237                         writefile += (*it)->getLatexName();
238                         (*it)->makeLaTeXFile(writefile, mastertmpdir,
239                                              false, true);
240                         (*it)->markDepClean(mastertmpdir);
241                 }
242         }
243 }
244
245
246 void BufferList::emergencyWriteAll()
247 {
248         for_each(bstore.begin(), bstore.end(),
249                  boost::bind(&BufferList::emergencyWrite, this, _1));
250 }
251
252
253 void BufferList::emergencyWrite(Buffer * buf)
254 {
255         // assert(buf) // this is not good since C assert takes an int
256                        // and a pointer is a long (JMarc)
257         assert(buf != 0); // use c assert to avoid a loop
258
259
260         // No need to save if the buffer has not changed.
261         if (buf->isClean())
262                 return;
263
264         string const doc = buf->isUnnamed()
265                 ? OnlyFilename(buf->fileName()) : buf->fileName();
266
267 #if USE_BOOST_FORMAT
268         lyxerr << boost::format(_("LyX: Attempting to save document %1$s"))
269                 % doc
270                << endl;
271 #else
272         lyxerr << _("LyX: Attempting to save document ") << doc << endl;
273 #endif
274         // We try to save three places:
275
276         // 1) Same place as document. Unless it is an unnamed doc.
277         if (!buf->isUnnamed()) {
278                 string s = buf->fileName();
279                 s += ".emergency";
280                 lyxerr << "  " << s << endl;
281                 if (buf->writeFile(s)) {
282                         buf->markClean();
283                         lyxerr << _("  Save seems successful. Phew.") << endl;
284                         return;
285                 } else {
286                         lyxerr << _("  Save failed! Trying...") << endl;
287                 }
288         }
289
290         // 2) In HOME directory.
291         string s = AddName(GetEnvPath("HOME"), buf->fileName());
292         s += ".emergency";
293         lyxerr << ' ' << s << endl;
294         if (buf->writeFile(s)) {
295                 buf->markClean();
296                 lyxerr << _("  Save seems successful. Phew.") << endl;
297                 return;
298         }
299
300         lyxerr << _("  Save failed! Trying...") << endl;
301
302         // 3) In "/tmp" directory.
303         // MakeAbsPath to prepend the current
304         // drive letter on OS/2
305         s = AddName(MakeAbsPath("/tmp/"), buf->fileName());
306         s += ".emergency";
307         lyxerr << ' ' << s << endl;
308         if (buf->writeFile(s)) {
309                 buf->markClean();
310                 lyxerr << _("  Save seems successful. Phew.") << endl;
311                 return;
312         }
313         lyxerr << _("  Save failed! Bummer. Document is lost.") << endl;
314 }
315
316
317
318 Buffer * BufferList::readFile(string const & s, bool ronly)
319 {
320         string ts(s);
321         string e = OnlyPath(s);
322         string a = e;
323         // File information about normal file
324         FileInfo fileInfo2(s);
325
326         if (!fileInfo2.exist()) {
327                 Alert::alert(_("Error!"), _("Cannot open file"),
328                         MakeDisplayPath(s));
329                 return 0;
330         }
331
332         Buffer * b = newBuffer(s, ronly);
333
334         // Check if emergency save file exists and is newer.
335         e += OnlyFilename(s) + ".emergency";
336         FileInfo fileInfoE(e);
337
338         bool use_emergency = false;
339
340         if (fileInfoE.exist() && fileInfo2.exist()) {
341                 if (fileInfoE.getModificationTime()
342                     > fileInfo2.getModificationTime()) {
343                         if (Alert::askQuestion(_("An emergency save of this document exists!"),
344                                         MakeDisplayPath(s, 50),
345                                         _("Try to load that instead?"))) {
346                                 ts = e;
347                                 // the file is not saved if we load the
348                                 // emergency file.
349                                 b->markDirty();
350                                 use_emergency = true;
351                         } else {
352                                 // Here, we should delete the emergency save
353                                 lyx::unlink(e);
354                         }
355                 }
356         }
357
358         if (!use_emergency) {
359                 // Now check if autosave file is newer.
360                 a += '#';
361                 a += OnlyFilename(s);
362                 a += '#';
363                 FileInfo fileInfoA(a);
364                 if (fileInfoA.exist() && fileInfo2.exist()) {
365                         if (fileInfoA.getModificationTime()
366                             > fileInfo2.getModificationTime()) {
367                                 if (Alert::askQuestion(_("Autosave file is newer."),
368                                                 MakeDisplayPath(s, 50),
369                                                 _("Load that one instead?"))) {
370                                         ts = a;
371                                         // the file is not saved if we load the
372                                         // autosave file.
373                                         b->markDirty();
374                                 } else {
375                                         // Here, we should delete the autosave
376                                         lyx::unlink(a);
377                                 }
378                         }
379                 }
380         }
381         // not sure if this is the correct place to begin LyXLex
382         LyXLex lex(0, 0);
383         lex.setFile(ts);
384         if (b->readFile(lex, ts))
385                 return b;
386         else {
387                 release(b);
388                 return 0;
389         }
390 }
391
392
393 bool BufferList::exists(string const & s) const
394 {
395         return find_if(bstore.begin(), bstore.end(),
396                        lyx::compare_memfun(&Buffer::fileName, s))
397                 != bstore.end();
398 }
399
400
401 bool BufferList::isLoaded(Buffer const * b) const
402 {
403         lyx::Assert(b);
404
405         BufferStorage::const_iterator cit =
406                 find(bstore.begin(), bstore.end(), b);
407         return cit != bstore.end();
408 }
409
410
411 Buffer * BufferList::getBuffer(string const & s)
412 {
413         BufferStorage::iterator it =
414                 find_if(bstore.begin(), bstore.end(),
415                         lyx::compare_memfun(&Buffer::fileName, s));
416         return it != bstore.end() ? (*it) : 0;
417 }
418
419
420 Buffer * BufferList::newFile(string const & name, string tname, bool isNamed)
421 {
422         // get a free buffer
423         Buffer * b = newBuffer(name);
424
425         // use defaults.lyx as a default template if it exists.
426         if (tname.empty()) {
427                 tname = LibFileSearch("templates", "defaults.lyx");
428         }
429         if (!tname.empty()) {
430                 bool templateok = false;
431                 LyXLex lex(0, 0);
432                 lex.setFile(tname);
433                 if (lex.isOK()) {
434                         if (b->readFile(lex, tname)) {
435                                 templateok = true;
436                         }
437                 }
438                 if (!templateok) {
439                         Alert::alert(_("Error!"), _("Unable to open template"),
440                                    MakeDisplayPath(tname));
441                         // no template, start with empty buffer
442                         b->paragraphs.set(new Paragraph);
443                         b->paragraphs.begin()->layout(b->params.getLyXTextClass().defaultLayout());
444                 }
445         } else {  // start with empty buffer
446                 b->paragraphs.set(new Paragraph);
447                 b->paragraphs.begin()->layout(b->params.getLyXTextClass().defaultLayout());
448         }
449
450         if (!isNamed) {
451                 b->setUnnamed();
452                 b->setFileName(name);
453         }
454
455         b->setReadonly(false);
456
457         return b;
458 }
459
460
461 Buffer * BufferList::loadLyXFile(string const & filename, bool tolastfiles)
462 {
463         // get absolute path of file and add ".lyx" to the filename if
464         // necessary
465         string s = FileSearch(string(), filename, "lyx");
466         if (s.empty()) {
467                 s = filename;
468         }
469
470         // file already open?
471         if (exists(s)) {
472                 if (Alert::askQuestion(_("Document is already open:"),
473                                 MakeDisplayPath(s, 50),
474                                 _("Do you want to reload that document?"))) {
475                         // Reload is accomplished by closing and then loading
476                         if (!close(getBuffer(s))) {
477                                 return 0;
478                         }
479                         // Fall through to new load. (Asger)
480                 } else {
481                         // Here, we pretend that we just loaded the
482                         // open document
483                         return getBuffer(s);
484                 }
485         }
486         Buffer * b = 0;
487         bool ro = false;
488         switch (IsFileWriteable(s)) {
489         case 0:
490                 ro = true;
491                 // Fall through
492         case 1:
493                 b = readFile(s, ro);
494                 if (b) {
495                         b->lyxvc.file_found_hook(s);
496                 }
497                 break; //fine- it's r/w
498         case -1:
499                 // Here we probably should run
500                 if (LyXVC::file_not_found_hook(s)) {
501                         // Ask if the file should be checked out for
502                         // viewing/editing, if so: load it.
503                         if (Alert::askQuestion(_("Do you want to retrieve file under version control?"))) {
504                                 // How can we know _how_ to do the checkout?
505                                 // With the current VC support it has to be,
506                                 // a RCS file since CVS do not have special ,v files.
507                                 RCS::retrieve(s);
508                                 return loadLyXFile(filename, tolastfiles);
509                         }
510                 }
511                 if (Alert::askQuestion(_("Cannot open specified file:"),
512                                 MakeDisplayPath(s, 50),
513                                 _("Create new document with this name?")))
514                         {
515                                 // Find a free buffer
516                                 b = newFile(s, string(), true);
517                         }
518                 break;
519         }
520
521         if (b && tolastfiles)
522                 lastfiles->newFile(b->fileName());
523
524         return b;
525 }
526
527
528 void BufferList::setCurrentAuthor(string const & name, string const & email)
529 {
530         BufferStorage::iterator it = bstore.begin();
531         BufferStorage::iterator end = bstore.end();
532         for (; it != end; ++it) {
533                 (*it)->authors().record(0, Author(name, email));
534         }
535 }