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