1 /* This file is part of
2 * ======================================================
4 * LyX, The Document Word Processor
6 * Copyright 1995 Matthias Ettrich
7 * Copyright 1995-2000 The LyX Team.
9 * This file is Copyright 1996-2000
12 * ======================================================
16 #pragma implementation
24 #include <sys/types.h>
27 #include "bufferlist.h"
29 #include "minibuffer.h"
30 #include "support/FileInfo.h"
31 #include "support/filetools.h"
32 #include "lyx_gui_misc.h"
33 #include "lastfiles.h"
36 #include "lyxscreen.h"
41 #include "vc-backend.h"
42 #include "TextCache.h"
44 extern BufferView * current_view;
45 extern int RunLinuxDoc(int, string const &);
53 // Class BufferStorage
56 void BufferStorage::release(Buffer * buf)
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
65 textcache.removeAllWithBuffer(tmp);
71 Buffer * BufferStorage::newBuffer(string const & s,
75 Buffer * tmpbuf = new Buffer(s, lyxrc, ronly);
76 tmpbuf->params.useClassDefaults();
77 lyxerr.debug() << "Assigning to buffer "
78 << container.size() << endl;
79 container.push_back(tmpbuf);
88 BufferList::BufferList()
89 : state_(BufferList::OK)
93 bool BufferList::empty() const
95 return bstore.empty();
99 extern void MenuWrite(Buffer *);
101 bool BufferList::QwriteAll()
103 bool askMoreConfirmation = false;
105 for(BufferStorage::iterator it = bstore.begin();
106 it != bstore.end(); ++it) {
107 if (!(*it)->isLyxClean()) {
108 switch(AskConfirmation(_("Changes in document:"),
109 MakeDisplayPath((*it)->fileName(),
111 _("Save document?"))) {
116 askMoreConfirmation = true;
117 unsaved += MakeDisplayPath((*it)->fileName(),
126 if (askMoreConfirmation &&
127 lyxrc->exit_confirmation &&
128 !AskQuestion(_("Some documents were not saved:"),
129 unsaved, _("Exit anyway?"))) {
137 // Should probably be moved to somewhere else: BufferView? LyXView?
138 bool BufferList::write(Buffer * buf, bool makeBackup)
144 ->Set(_("Saving document"),
145 MakeDisplayPath(buf->fileName()), "...");
147 // We don't need autosaves in the immediate future. (Asger)
148 buf->resetAutosaveTimers();
152 string s = buf->fileName() + '~';
153 // Rename is the wrong way of making a backup,
154 // this is the correct way.
155 /* truss cp fil fil2:
156 lstat("LyXVC3.lyx", 0xEFFFF898) Err#2 ENOENT
157 stat("LyXVC.lyx", 0xEFFFF688) = 0
158 open("LyXVC.lyx", O_RDONLY) = 3
159 open("LyXVC3.lyx", O_WRONLY|O_CREAT|O_TRUNC, 0600) = 4
160 fstat(4, 0xEFFFF508) = 0
161 fstat(3, 0xEFFFF508) = 0
162 read(3, " # T h i s f i l e w".., 8192) = 5579
163 write(4, " # T h i s f i l e w".., 5579) = 5579
164 read(3, 0xEFFFD4A0, 8192) = 0
167 chmod("LyXVC3.lyx", 0100644) = 0
168 lseek(0, 0, SEEK_CUR) = 46440
172 // Should proabaly have some more error checking here.
173 // Should be cleaned up in 0.13, at least a bit.
174 // Doing it this way, also makes the inodes stay the same.
175 // This is still not a very good solution, in particular we
176 // might loose the owner of the backup.
177 FileInfo finfo(buf->fileName());
179 mode_t fmode = finfo.getMode();
180 struct utimbuf * times = new struct utimbuf;
182 times->actime = finfo.getAccessTime();
183 times->modtime = finfo.getModificationTime();
184 ifstream ifs(buf->fileName().c_str());
185 ofstream ofs(s.c_str(), ios::out|ios::trunc);
190 ::chmod(s.c_str(), fmode);
192 if (::utime(s.c_str(), times)) {
193 lyxerr << "utime error." << endl;
196 lyxerr << "LyX was not able to make "
197 "backupcopy. Beware." << endl;
203 if (buf->writeFile(buf->fileName(), false)) {
206 current_view->owner()->getMiniBuffer()->
207 Set(_("Document saved as"),
208 MakeDisplayPath(buf->fileName()));
210 // now delete the autosavefile
211 string a = OnlyPath(buf->fileName());
213 a += OnlyFilename(buf->fileName());
215 FileInfo fileinfo(a);
216 if (fileinfo.exist()) {
217 if (::remove(a.c_str()) != 0) {
218 WriteFSAlert(_("Could not delete "
219 "auto-save file!"), a);
223 // Saving failed, so backup is not backup
225 string s = buf->fileName() + '~';
226 ::rename(s.c_str(), buf->fileName().c_str());
228 current_view->owner()->getMiniBuffer()->Set(_("Save failed!"));
236 void BufferList::closeAll()
238 state_ = BufferList::CLOSING;
239 // Since we are closing we can just as well delete all
240 // in the textcache this will also speed the closing/quiting up a bit.
243 while (!bstore.empty()) {
244 close(bstore.front());
246 state_ = BufferList::OK;
250 void BufferList::resize()
252 for(BufferStorage::iterator it = bstore.begin();
253 it != bstore.end(); ++it) {
259 bool BufferList::close(Buffer * buf)
261 if (buf->getUser()) buf->getUser()->insetUnlock();
263 if (buf->paragraph && !buf->isLyxClean() && !quitting) {
265 switch(AskConfirmation(_("Changes in document:"),
266 MakeDisplayPath(buf->fileName(), 50),
267 _("Save document?"))){
269 if (write(buf, lyxrc->make_backup)) {
270 lastfiles->newFile(buf->fileName());
288 vector<string> BufferList::getFileNames() const
291 for(BufferStorage::const_iterator cit = bstore.begin();
292 cit != bstore.end(); ++cit) {
293 nvec.push_back((*cit)->fileName());
299 Buffer * BufferList::first()
301 if (bstore.empty()) return 0;
302 return bstore.front();
306 Buffer * BufferList::getBuffer(int choice)
308 if (choice >= bstore.size()) return 0;
309 return bstore[choice];
313 void BufferList::updateInset(Inset * inset, bool mark_dirty)
315 for (BufferStorage::iterator it = bstore.begin();
316 it != bstore.end(); ++it) {
318 && (*it)->getUser()->text->UpdateInset(inset)) {
327 int BufferList::unlockInset(UpdatableInset * inset)
329 if (!inset) return 1;
330 for(BufferStorage::iterator it = bstore.begin();
331 it != bstore.end(); ++it) {
333 && (*it)->getUser()->the_locking_inset == inset) {
334 (*it)->getUser()->insetUnlock();
342 void BufferList::updateIncludedTeXfiles(string const & mastertmpdir)
344 for(BufferStorage::iterator it = bstore.begin();
345 it != bstore.end(); ++it) {
346 if (!(*it)->isDepClean(mastertmpdir)) {
347 string writefile = mastertmpdir;
349 writefile += ChangeExtension((*it)->fileName(),
351 (*it)->makeLaTeXFile(writefile, mastertmpdir,
353 (*it)->markDepClean(mastertmpdir);
359 void BufferList::emergencyWriteAll()
361 for (BufferStorage::iterator it = bstore.begin();
362 it != bstore.end(); ++it) {
363 if (!(*it)->isLyxClean()) {
366 lyxerr <<_("lyx: Attempting to save"
369 << _(" as...") << endl;
371 for (int i = 0; i < 3 && !madeit; ++i) {
374 // We try to save three places:
375 // 1) Same place as document.
376 // 2) In HOME directory.
377 // 3) In "/tmp" directory.
379 s = (*it)->fileName();
381 s = AddName(GetEnvPath("HOME"),
384 // MakeAbsPath to prepend the current
385 // drive letter on OS/2
386 s = AddName(MakeAbsPath("/tmp/"),
391 lyxerr << " " << i + 1 << ") " << s << endl;
393 if ((*it)->writeFile(s, true)) {
394 (*it)->markLyxClean();
395 lyxerr << _(" Save seems successful. "
399 lyxerr << _(" Save failed! Trying...")
402 lyxerr << _(" Save failed! Bummer. "
412 Buffer * BufferList::readFile(string const & s, bool ronly)
414 Buffer * b = bstore.newBuffer(s, lyxrc, ronly);
417 string e = OnlyPath(s);
419 // File information about normal file
420 FileInfo fileInfo2(s);
422 // Check if emergency save file exists and is newer.
423 e += OnlyFilename(s) + ".emergency";
424 FileInfo fileInfoE(e);
426 bool use_emergency = false;
428 if (fileInfoE.exist() && fileInfo2.exist()) {
429 if (fileInfoE.getModificationTime()
430 > fileInfo2.getModificationTime()) {
431 if (AskQuestion(_("An emergency save of this document exists!"),
432 MakeDisplayPath(s, 50),
433 _("Try to load that instead?"))) {
435 // the file is not saved if we load the
438 use_emergency = true;
440 // Here, we should delete the emergency save
446 if (!use_emergency) {
447 // Now check if autosave file is newer.
449 a += OnlyFilename(s);
451 FileInfo fileInfoA(a);
452 if (fileInfoA.exist() && fileInfo2.exist()) {
453 if (fileInfoA.getModificationTime()
454 > fileInfo2.getModificationTime()) {
455 if (AskQuestion(_("Autosave file is newer."),
456 MakeDisplayPath(s, 50),
457 _("Load that one instead?"))) {
459 // the file is not saved if we load the
463 // Here, we should delete the autosave
469 // not sure if this is the correct place to begin LyXLex
472 if (b->readFile(lex))
481 bool BufferList::exists(string const & s) const
483 for (BufferStorage::const_iterator cit = bstore.begin();
484 cit != bstore.end(); ++cit) {
485 if ((*cit)->fileName() == s)
492 bool BufferList::isLoaded(Buffer const * b) const
494 BufferStorage::const_iterator cit =
495 find(bstore.begin(), bstore.end(), b);
496 return cit != bstore.end();
500 Buffer * BufferList::getBuffer(string const & s)
502 for(BufferStorage::iterator it = bstore.begin();
503 it != bstore.end(); ++it) {
504 if ((*it)->fileName() == s)
511 Buffer * BufferList::newFile(string const & name, string tname)
514 Buffer * b = bstore.newBuffer(name, lyxrc);
516 // use defaults.lyx as a default template if it exists.
518 tname = LibFileSearch("templates", "defaults.lyx");
520 if (!tname.empty() && IsLyXFilename(tname)) {
521 bool templateok = false;
525 if (b->readFile(lex)) {
530 WriteAlert(_("Error!"), _("Unable to open template"),
531 MakeDisplayPath(tname));
532 // no template, start with empty buffer
533 b->paragraph = new LyXParagraph;
536 else { // start with empty buffer
537 b->paragraph = new LyXParagraph;
541 b->setReadonly(false);
547 Buffer * BufferList::loadLyXFile(string const & filename, bool tolastfiles)
549 // make sure our path is absolute
550 string s = MakeAbsPath(filename);
552 // Is this done too early?
554 if (IsSGMLFilename(s)) {
556 if (fi.exist() && fi.readable()) {
557 if (!RunLinuxDoc(-1, s)) {
558 s = ChangeExtension (s, ".lyx", false);
559 } else { // sgml2lyx failed
560 WriteAlert(_("Error!"),
561 _("Could not convert file"), s);
565 // just change the extension and it will be
566 // handled like a regular lyx file that does
568 s = ChangeExtension(s, ".lyx", false);
572 // file already open?
574 if (AskQuestion(_("Document is already open:"),
575 MakeDisplayPath(s, 50),
576 _("Do you want to reload that document?"))) {
577 // Reload is accomplished by closing and then loading
578 if (!close(getBuffer(s))) {
581 // Fall through to new load. (Asger)
583 // Here, we pretend that we just loaded the
590 switch (IsFileWriteable(s)) {
592 current_view->owner()->getMiniBuffer()->
593 Set(_("File `") + MakeDisplayPath(s, 50) +
594 _("' is read-only."));
600 b->lyxvc.file_found_hook(s);
602 break; //fine- it's r/w
604 // Here we probably should run
605 if (LyXVC::file_not_found_hook(s)) {
606 // Ask if the file should be checked out for
607 // viewing/editing, if so: load it.
608 if (AskQuestion(_("Do you want to retrive file under version control?"))) {
609 // How can we know _how_ to do the checkout?
610 // With the current VC support it has to be,
611 // a RCS file since CVS do not have special ,v files.
613 return loadLyXFile(filename, tolastfiles);
616 if (AskQuestion(_("Cannot open specified file:"),
617 MakeDisplayPath(s, 50),
618 _("Create new document with this name?")))
620 // Find a free buffer
621 b = newFile(s, string());
626 if (b && tolastfiles)
627 lastfiles->newFile(b->fileName());