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