]> git.lyx.org Git - lyx.git/blob - src/bufferlist.C
900cda89df1e83e0f54fa21973876850ffe973d3
[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 using std::mem_fun;
49
50 extern BufferView * current_view;
51
52 //
53 // Class BufferStorage
54 //
55
56 void BufferStorage::release(Buffer * buf)
57 {
58         Container::iterator it = find(container.begin(), container.end(), buf);
59         if (it != container.end()) {
60                 // Make sure that we don't store a LyXText in
61                 // the textcache that points to the buffer
62                 // we just deleted.
63                 Buffer * tmp = (*it);
64                 container.erase(it);
65                 textcache.removeAllWithBuffer(tmp);
66                 delete tmp;
67         }
68 }
69
70
71 Buffer * BufferStorage::newBuffer(string const & s, bool ronly)
72 {
73         Buffer * tmpbuf = new Buffer(s, ronly);
74         tmpbuf->params.useClassDefaults();
75         lyxerr[Debug::INFO] << "Assigning to buffer "
76                             << container.size() << endl;
77         container.push_back(tmpbuf);
78         return tmpbuf;
79 }
80
81
82 //
83 // Class BufferList
84 //
85
86 BufferList::BufferList()
87         : state_(BufferList::OK)
88 {}
89
90
91 bool BufferList::empty() const
92 {
93         return bstore.empty();
94 }
95
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 = !WriteAs(current_view, (*it));
117                                         else {
118                                                 reask = !MenuWrite(current_view, (*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 = !WriteAs(current_view, 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         // We try to save three places:
294
295         // 1) Same place as document. Unless it is an unnamed doc.
296         if (!buf->isUnnamed()) {
297                 string s = buf->fileName();
298                 s += ".emergency";
299                 lyxerr << "  " << s << endl;
300                 if (buf->writeFile(s, true)) {
301                         buf->markLyxClean();
302                         lyxerr << _("  Save seems successful. Phew.") << endl;
303                         return;
304                 } else {
305                         lyxerr << _("  Save failed! Trying...") << endl;
306                 }
307         }
308         
309         // 2) In HOME directory.
310         string s = AddName(GetEnvPath("HOME"), buf->fileName());
311         s += ".emergency";
312         lyxerr << " " << s << endl;
313         if (buf->writeFile(s, true)) {
314                 buf->markLyxClean();
315                 lyxerr << _("  Save seems successful. Phew.") << endl;
316                 return;
317         }
318         
319         lyxerr << _("  Save failed! Trying...") << endl;
320         
321         // 3) In "/tmp" directory.
322         // MakeAbsPath to prepend the current
323         // drive letter on OS/2
324         s = AddName(MakeAbsPath("/tmp/"), buf->fileName());
325         s += ".emergency";
326         lyxerr << " " << s << endl;
327         if (buf->writeFile(s, true)) {
328                 buf->markLyxClean();
329                 lyxerr << _("  Save seems successful. Phew.") << endl;
330                 return;
331         }
332         lyxerr << _("  Save failed! Bummer. Document is lost.") << endl;
333 }
334
335
336
337 Buffer * BufferList::readFile(string const & s, bool ronly)
338 {
339         Buffer * b = bstore.newBuffer(s, ronly);
340
341         string ts = s;
342         string e = OnlyPath(s);
343         string a = e;
344         // File information about normal file
345         FileInfo fileInfo2(s);
346
347         // Check if emergency save file exists and is newer.
348         e += OnlyFilename(s) + ".emergency";
349         FileInfo fileInfoE(e);
350
351         bool use_emergency = false;
352
353         if (fileInfoE.exist() && fileInfo2.exist()) {
354                 if (fileInfoE.getModificationTime()
355                     > fileInfo2.getModificationTime()) {
356                         if (AskQuestion(_("An emergency save of this document exists!"),
357                                         MakeDisplayPath(s, 50),
358                                         _("Try to load that instead?"))) {
359                                 ts = e;
360                                 // the file is not saved if we load the
361                                 // emergency file.
362                                 b->markDirty();
363                                 use_emergency = true;
364                         } else {
365                                 // Here, we should delete the emergency save
366                                 lyx::unlink(e);
367                         }
368                 }
369         }
370
371         if (!use_emergency) {
372                 // Now check if autosave file is newer.
373                 a += '#';
374                 a += OnlyFilename(s);
375                 a += '#';
376                 FileInfo fileInfoA(a);
377                 if (fileInfoA.exist() && fileInfo2.exist()) {
378                         if (fileInfoA.getModificationTime()
379                             > fileInfo2.getModificationTime()) {
380                                 if (AskQuestion(_("Autosave file is newer."),
381                                                 MakeDisplayPath(s, 50),
382                                                 _("Load that one instead?"))) {
383                                         ts = a;
384                                         // the file is not saved if we load the
385                                         // autosave file.
386                                         b->markDirty();
387                                 } else {
388                                         // Here, we should delete the autosave
389                                         lyx::unlink(a);
390                                 }
391                         }
392                 }
393         }
394         // not sure if this is the correct place to begin LyXLex
395         LyXLex lex(0, 0);
396         lex.setFile(ts);
397         if (b->readFile(lex))
398                 return b;
399         else {
400                 bstore.release(b);
401                 return 0;
402         }
403 }
404
405
406 bool BufferList::exists(string const & s) const
407 {
408         return find_if(bstore.begin(), bstore.end(),
409                        compare_memfun(&Buffer::fileName, s)) != bstore.end();
410 }
411
412
413 bool BufferList::isLoaded(Buffer const * b) const
414 {
415         BufferStorage::const_iterator cit =
416                 find(bstore.begin(), bstore.end(), b);
417         return cit != bstore.end();
418 }
419
420
421 Buffer * BufferList::getBuffer(string const & s)
422 {
423         BufferStorage::iterator it =
424                 find_if(bstore.begin(), bstore.end(),
425                         compare_memfun(&Buffer::fileName, s));
426         return it != bstore.end() ? (*it) : 0;
427 }
428
429
430 Buffer * BufferList::newFile(string const & name, string tname, bool isNamed)
431 {
432         // get a free buffer
433         Buffer * b = bstore.newBuffer(name);
434
435         // use defaults.lyx as a default template if it exists.
436         if (tname.empty()) {
437                 tname = LibFileSearch("templates", "defaults.lyx");
438         }
439         if (!tname.empty() && IsLyXFilename(tname)) {
440                 bool templateok = false;
441                 LyXLex lex(0, 0);
442                 lex.setFile(tname);
443                 if (lex.IsOK()) {
444                         if (b->readFile(lex)) {
445                                 templateok = true;
446                         }
447                 }
448                 if (!templateok) {
449                         WriteAlert(_("Error!"), _("Unable to open template"), 
450                                    MakeDisplayPath(tname));
451                         // no template, start with empty buffer
452                         b->paragraph = new LyXParagraph;
453                 }
454         } else {  // start with empty buffer
455                 b->paragraph = new LyXParagraph;
456         }
457
458         if (!lyxrc.new_ask_filename && !isNamed) {
459                 b->setUnnamed();
460                 b->setFileName(name);
461         }
462
463         b->setReadonly(false);
464         
465         return b;
466 }
467
468
469 Buffer * BufferList::loadLyXFile(string const & filename, bool tolastfiles)
470 {
471         // make sure our path is absolute
472         string const s = MakeAbsPath(filename);
473
474         // file already open?
475         if (exists(s)) {
476                 if (AskQuestion(_("Document is already open:"), 
477                                 MakeDisplayPath(s, 50),
478                                 _("Do you want to reload that document?"))) {
479                         // Reload is accomplished by closing and then loading
480                         if (!close(getBuffer(s))) {
481                                 return 0;
482                         }
483                         // Fall through to new load. (Asger)
484                 } else {
485                         // Here, we pretend that we just loaded the 
486                         // open document
487                         return getBuffer(s);
488                 }
489         }
490         Buffer * b = 0;
491         bool ro = false;
492         switch (IsFileWriteable(s)) {
493         case 0:
494 #if 0
495                 current_view->owner()->getMiniBuffer()->
496                         Set(_("File `") + MakeDisplayPath(s, 50) +
497                             _("' is read-only."));
498 #endif
499                 ro = true;
500                 // Fall through
501         case 1:
502                 b = readFile(s, ro);
503                 if (b) {
504                         b->lyxvc.file_found_hook(s);
505                 }
506                 break; //fine- it's r/w
507         case -1:
508                 // Here we probably should run
509                 if (LyXVC::file_not_found_hook(s)) {
510                         // Ask if the file should be checked out for
511                         // viewing/editing, if so: load it.
512                         if (AskQuestion(_("Do you want to retrieve file under version control?"))) {
513                                 // How can we know _how_ to do the checkout?
514                                 // With the current VC support it has to be,
515                                 // a RCS file since CVS do not have special ,v files.
516                                 RCS::retrive(s);
517                                 return loadLyXFile(filename, tolastfiles);
518                         }
519                 }
520                 if (AskQuestion(_("Cannot open specified file:"), 
521                                 MakeDisplayPath(s, 50),
522                                 _("Create new document with this name?")))
523                         {
524                                 // Find a free buffer
525                                 b = newFile(s, string(), true);
526                         }
527                 break;
528         }
529
530         if (b && tolastfiles)
531                 lastfiles->newFile(b->fileName());
532
533         return b;
534 }