1 /* This file is part of
2 * ======================================================
4 * LyX, The Document Word Processor
6 * Copyright 1995 Matthias Ettrich
7 * Copyright 1995-1999 The LyX Team.
9 * This file is Copyright 1996-1999
12 * ======================================================
16 #pragma implementation
20 #include <sys/types.h>
22 #include "bufferlist.h"
24 #include "minibuffer.h"
25 #include "support/FileInfo.h"
26 #include "support/filetools.h"
27 #include "lyx_gui_misc.h"
28 #include "lastfiles.h"
31 #include "lyxscreen.h"
36 extern BufferView * current_view;
37 extern MiniBuffer * minibuffer;
38 extern void SmallUpdate(signed char);
39 extern void BeforeChange();
40 extern int RunLinuxDoc(int, string const &);
43 // Class BufferStorage
47 BufferStorage::BufferStorage()
49 // Initialize the buffer array
50 for (int i=NUMBER_OF_BUFFERS-1; i >=0; i--) {
57 bool BufferStorage::isEmpty()
59 for (int i=NUMBER_OF_BUFFERS-1; i >=0; i--) {
60 if (buffer[i]) return false;
66 void BufferStorage::release(Buffer * buf)
69 for(Container::iterator it = container.begin();
70 it != container.end(); ++it) {
72 Buffer * tmpbuf = (*it);
80 for (i = 0; i < NUMBER_OF_BUFFERS; i++)
81 if (buffer[i] == buf) break;
82 Buffer * tmpbuf = buffer[i];
89 Buffer * BufferStorage::newBuffer(string const & s,
94 Buffer * tmpbuf = new Buffer(s, lyxrc, ronly);
95 tmpbuf->params.useClassDefaults();
96 lyxerr.debug() << "Assigning to buffer "
97 << container.size() + 1 << endl;
98 container.push_back(tmpbuf);
102 while (i < NUMBER_OF_BUFFERS - 1
104 buffer[i] = new Buffer(s, lyxrc, ronly);
105 buffer[i]->params.useClassDefaults();
106 lyxerr.debug() << "Assigning to buffer " << i << endl;
114 // Class BufferStrorage_Iter
117 Buffer * BufferStorage_Iter::operator() ()
120 for (i = index; i < BufferStorage::NUMBER_OF_BUFFERS; i++) {
123 return cs->buffer[i];
130 Buffer * BufferStorage_Iter::operator[] (int a)
133 if (a <= 0) return 0;
137 while(!cs->buffer[i++]);
139 if (i - 1 < BufferStorage::NUMBER_OF_BUFFERS)
140 return cs->buffer[i - 1];
149 BufferList::BufferList()
151 _state = BufferList::OK;
155 bool BufferList::empty()
157 return bstore.empty();
161 extern void MenuWrite(Buffer *);
163 bool BufferList::QwriteAll()
165 bool askMoreConfirmation = false;
168 for(BufferStorage::iterator it = bstore.begin();
169 it != bstore.end(); ++it) {
170 if (!(*it)->isLyxClean()) {
171 switch(AskConfirmation(_("Changes in document:"),
172 MakeDisplayPath((*it)->filename,
174 _("Save document?"))) {
179 askMoreConfirmation = true;
180 unsaved += MakeDisplayPath((*it)->filename,50);
189 BufferStorage_Iter biter(bstore);
191 while ((b = biter())) {
192 if (!b->isLyxClean()) {
193 switch(AskConfirmation(_("Changes in document:"),
194 MakeDisplayPath(b->filename,50),
195 _("Save document?"))) {
200 askMoreConfirmation = true;
201 unsaved += MakeDisplayPath(b->filename,50);
210 if (askMoreConfirmation &&
211 lyxrc->exit_confirmation &&
212 !AskQuestion(_("Some documents were not saved:"),
213 unsaved, _("Exit anyway?"))) {
221 // Should probably be moved to somewhere else: BufferView? LyXView?
222 bool BufferList::write(Buffer * buf, bool makeBackup)
224 minibuffer->Set(_("Saving document"),
225 MakeDisplayPath(buf->filename),"...");
227 // We don't need autosaves in the immediate future. (Asger)
228 buf->resetAutosaveTimers();
232 string s = buf->filename + '~';
233 // Rename is the wrong way of making a backup,
234 // this is the correct way.
235 /* truss cp fil fil2:
236 lstat("LyXVC3.lyx", 0xEFFFF898) Err#2 ENOENT
237 stat("LyXVC.lyx", 0xEFFFF688) = 0
238 open("LyXVC.lyx", O_RDONLY) = 3
239 open("LyXVC3.lyx", O_WRONLY|O_CREAT|O_TRUNC, 0600) = 4
240 fstat(4, 0xEFFFF508) = 0
241 fstat(3, 0xEFFFF508) = 0
242 read(3, " # T h i s f i l e w".., 8192) = 5579
243 write(4, " # T h i s f i l e w".., 5579) = 5579
244 read(3, 0xEFFFD4A0, 8192) = 0
247 chmod("LyXVC3.lyx", 0100644) = 0
248 lseek(0, 0, SEEK_CUR) = 46440
252 // Should proabaly have some more error checking here.
253 // Should be cleaned up in 0.13, at least a bit.
254 // Doing it this way, also makes the inodes stay the same.
255 // This is still not a very good solution, in particular we
256 // might loose the owner of the backup.
257 FileInfo finfo(buf->filename);
259 mode_t fmode = finfo.getMode();
261 struct utimbuf *times =
262 (struct utimbuf*)new char[sizeof(struct utimbuf)];
263 times->actime = finfo.getAccessTime();
264 times->modtime = finfo.getModificationTime();
265 long blksize = finfo.getBlockSize();
266 lyxerr.debug() << "BlockSize: " << blksize << endl;
267 FilePtr fin(buf->filename,FilePtr::read);
268 FilePtr fout(s,FilePtr::truncate);
269 if (fin() && fout()) {
270 char * cbuf = new char[blksize+1];
274 c_read = fread(cbuf, 1, blksize, fin);
277 fwrite(cbuf, 1, c_read, fout);
281 chmod(s.c_str(), fmode);
283 if (utime(s.c_str(), times)) {
284 lyxerr << "utime error." << endl;
288 lyxerr << "LyX was not able to make backupcopy. Beware." << endl;
294 if (buf->writeFile(buf->filename,false)) {
297 minibuffer->Set(_("Document saved as"),
298 MakeDisplayPath(buf->filename));
300 // now delete the autosavefile
301 string a = OnlyPath(buf->filename);
303 a += OnlyFilename(buf->filename);
305 FileInfo fileinfo(a);
306 if (fileinfo.exist()) {
307 if (remove(a.c_str()) != 0) {
308 WriteFSAlert(_("Could not delete "
309 "auto-save file!"), a);
313 // Saving failed, so backup is not backup
315 string s = buf->filename + '~';
316 rename(s.c_str(), buf->filename.c_str());
318 minibuffer->Set(_("Save failed!"));
326 void BufferList::closeAll()
328 _state = BufferList::CLOSING;
330 while (!bstore.empty()) {
331 close(bstore.front());
334 BufferStorage_Iter biter(bstore);
336 while ((b = biter())) {
340 _state = BufferList::OK;
344 void BufferList::resize()
347 for(BufferStorage::iterator it = bstore.begin();
348 it != bstore.end(); ++it) {
352 BufferStorage_Iter biter(bstore);
354 while ((b = biter())) {
361 bool BufferList::close(Buffer * buf)
365 if (buf->paragraph && !buf->isLyxClean() && !quitting) {
367 switch(AskConfirmation(_("Changes in document:"),
368 MakeDisplayPath(buf->filename,50),
369 _("Save document?"))){
372 lastfiles->newFile(buf->filename);
390 void BufferList::makePup(int pup)
391 /* This should be changed to return a char const *const
392 in the same way as for lastfiles.[hC]
397 for(BufferStorage::iterator it = bstore.begin();
398 it != bstore.end(); ++it) {
399 string relbuf = MakeDisplayPath((*it)->filename, 30);
400 fl_addtopup(pup, relbuf.c_str());
404 BufferStorage_Iter biter(bstore);
406 while ((b = biter())) {
407 string relbuf = MakeDisplayPath(b->filename, 30);
408 fl_addtopup(pup, relbuf.c_str());
412 if (ant == 0) fl_addtopup(pup,_("No Documents Open!%t"));
416 Buffer * BufferList::first()
419 if (bstore.empty()) return 0;
420 return bstore.front();
422 BufferStorage_Iter biter(bstore);
428 Buffer * BufferList::getBuffer(int choice)
431 if (choice >= bstore.size()) return 0;
432 return bstore[choice];
434 BufferStorage_Iter biter(bstore);
438 // Be careful, this could be 0.
444 void BufferList::updateInset(Inset * inset, bool mark_dirty)
447 for (BufferStorage::iterator it = bstore.begin();
448 it != bstore.end(); ++it) {
449 if ((*it)->text && (*it)->text->UpdateInset(inset)) {
456 BufferStorage_Iter biter(bstore);
458 while ((b=biter())) {
459 if (b->text && b->text->UpdateInset(inset)) {
469 int BufferList::unlockInset(UpdatableInset * inset)
472 if (!inset) return 1;
473 for(BufferStorage::iterator it = bstore.begin();
474 it != bstore.end(); ++it) {
475 if ((*it)->the_locking_inset == inset) {
476 (*it)->InsetUnlock();
482 if (!inset) return 1;
484 BufferStorage_Iter biter(bstore);
486 while ((b=biter())) {
487 if (b->the_locking_inset == inset) {
497 void BufferList::updateIncludedTeXfiles(string const & mastertmpdir)
500 for(BufferStorage::iterator it = bstore.begin();
501 it != bstore.end(); ++it) {
502 if (!(*it)->isDepClean(mastertmpdir)) {
503 string writefile = mastertmpdir;
505 writefile += ChangeExtension((*it)->getFileName(),
507 (*it)->makeLaTeXFile(writefile, mastertmpdir,
509 (*it)->markDepClean(mastertmpdir);
514 BufferStorage_Iter biter(bstore);
516 while ((b=biter())) {
517 if (!b->isDepClean(mastertmpdir)) {
518 string writefile = mastertmpdir;
520 writefile += ChangeExtension(b->getFileName(), ".tex", true);
521 b->makeLaTeXFile(writefile,mastertmpdir,false,true);
522 b->markDepClean(mastertmpdir);
529 void BufferList::emergencyWriteAll()
532 for (BufferStorage::iterator it = bstore.begin();
533 it != bstore.end(); ++it) {
534 if (!(*it)->isLyxClean()) {
537 lyxerr <<_("lyx: Attempting to save"
540 << _(" as...") << endl;
542 for (int i = 0; i < 3 && !madeit; ++i) {
545 // We try to save three places:
546 // 1) Same place as document.
547 // 2) In HOME directory.
548 // 3) In "/tmp" directory.
552 s = AddName(GetEnvPath("HOME"),
554 } else { // MakeAbsPath to prepend the current drive letter on OS/2
555 s = AddName(MakeAbsPath("/tmp/"),
560 lyxerr << " " << i + 1 << ") " << s << endl;
562 if ((*it)->writeFile(s,true)) {
563 (*it)->markLyxClean();
564 lyxerr << _(" Save seems successful. "
568 lyxerr << _(" Save failed! Trying...")
571 lyxerr << _(" Save failed! Bummer. Document is lost.") << endl;
577 BufferStorage_Iter biter(bstore);
579 while ((b = biter())) {
580 if (!b->isLyxClean()) {
583 lyxerr <<_("lyx: Attempting to save"
586 << _(" as...") << endl;
588 for (int i=0; i<3 && !madeit; i++) {
591 // We try to save three places:
592 // 1) Same place as document.
593 // 2) In HOME directory.
594 // 3) In "/tmp" directory.
598 s = AddName(GetEnvPath("HOME"),
600 } else { // MakeAbsPath to prepend the current drive letter on OS/2
601 s = AddName(MakeAbsPath("/tmp/"),
606 lyxerr << " " << i + 1 << ") " << s << endl;
608 if (b->writeFile(s,true)) {
610 lyxerr << _(" Save seems successful. Phew.") << endl;
613 lyxerr << _(" Save failed! Trying...")
616 lyxerr << _(" Save failed! Bummer. Document is lost.") << endl;
625 Buffer* BufferList::readFile(string const & s, bool ronly)
627 Buffer * b = bstore.newBuffer(s, lyxrc, ronly);
630 string e = OnlyPath(s);
632 // File information about normal file
633 FileInfo fileInfo2(s);
635 // Check if emergency save file exists and is newer.
636 e += OnlyFilename(s) + ".emergency";
637 FileInfo fileInfoE(e);
639 bool use_emergency = false;
641 if (fileInfoE.exist() && fileInfo2.exist()) {
642 if (fileInfoE.getModificationTime()
643 > fileInfo2.getModificationTime()) {
644 if (AskQuestion(_("An emergency save of this document exists!"),
645 MakeDisplayPath(s,50),
646 _("Try to load that instead?"))) {
648 // the file is not saved if we load the
651 use_emergency = true;
653 // Here, we should delete the emergency save
659 if (!use_emergency) {
660 // Now check if autosave file is newer.
662 a += OnlyFilename(s);
664 FileInfo fileInfoA(a);
665 if (fileInfoA.exist() && fileInfo2.exist()) {
666 if (fileInfoA.getModificationTime()
667 > fileInfo2.getModificationTime()) {
668 if (AskQuestion(_("Autosave file is newer."),
669 MakeDisplayPath(s,50),
670 _("Load that one instead?"))) {
672 // the file is not saved if we load the
676 // Here, we should delete the autosave
682 // not sure if this is the correct place to begin LyXLex
685 if (b->readFile(lex))
694 bool BufferList::exists(string const & s)
697 for (BufferStorage::iterator it = bstore.begin();
698 it != bstore.end(); ++it) {
699 if ((*it)->filename == s)
704 BufferStorage_Iter biter(bstore);
706 while ((b = biter())) {
707 if (b->filename == s)
715 Buffer * BufferList::getBuffer(string const & s)
718 for(BufferStorage::iterator it = bstore.begin();
719 it != bstore.end(); ++it) {
720 if ((*it)->filename == s)
725 BufferStorage_Iter biter(bstore);
727 while ((b = biter())) {
728 if (b->filename == s)
736 Buffer * BufferList::newFile(string const & name, string tname)
738 /* get a free buffer */
739 Buffer * b = bstore.newBuffer(name, lyxrc);
741 // use defaults.lyx as a default template if it exists.
743 tname = LibFileSearch("templates", "defaults.lyx");
745 if (!tname.empty() && IsLyXFilename(tname)){
746 bool templateok = false;
750 if (b->readFile(lex)) {
755 WriteAlert(_("Error!"),_("Unable to open template"),
756 MakeDisplayPath(tname));
757 // no template, start with empty buffer
758 b->paragraph = new LyXParagraph;
760 b->paragraph->text.reserve(500);
764 else { // start with empty buffer
765 b->paragraph = new LyXParagraph;
767 b->paragraph->text.reserve(500);
772 b->setReadonly(false);
778 Buffer * BufferList::loadLyXFile(string const & filename, bool tolastfiles)
780 // make sure our path is absolute
781 string s = MakeAbsPath(filename);
783 // Is this done too early?
785 if (IsSGMLFilename(s)) {
787 if (fi.exist() && fi.readable()) {
788 if (!RunLinuxDoc(-1, s)) {
789 s = ChangeExtension (s, ".lyx", false);
790 } else { // sgml2lyx failed
791 WriteAlert(_("Error!"),
792 _("Could not convert file"),s);
796 // just change the extension and it will be
797 // handled like a regular lyx file that does
799 s = ChangeExtension(s, ".lyx", false);
803 // file already open?
805 if (AskQuestion(_("Document is already open:"),
806 MakeDisplayPath(s,50),
807 _("Do you want to reload that document?"))) {
808 // Reload is accomplished by closing and then loading
809 if (!close(getBuffer(s))) {
812 // Fall through to new load. (Asger)
814 // Here, we pretend that we just loaded the
821 switch (IsFileWriteable(s)) {
823 minibuffer->Set(_("File `")+MakeDisplayPath(s,50)+
824 _("' is read-only."));
830 b->lyxvc.file_found_hook(s);
832 break; //fine- it's r/w
834 // Here we probably should run
835 if (LyXVC::file_not_found_hook(s)) {
836 // Ask if the file should be checked out for
837 // viewing/editing, if so: load it.
838 lyxerr << "Do you want to checkout?" << endl;
840 if (AskQuestion(_("Cannot open specified file:"),
841 MakeDisplayPath(s,50),
842 _("Create new document with this name?")))
844 // Find a free buffer
845 b = newFile(s,string());
850 if (b && tolastfiles)
851 lastfiles->newFile(b->getFileName());