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