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
46 BufferStorage::BufferStorage()
48 // Initialize the buffer array
49 for (int i=NUMBER_OF_BUFFERS-1; i >=0; i--) {
55 bool BufferStorage::isEmpty()
57 for (int i=NUMBER_OF_BUFFERS-1; i >=0; i--) {
58 if (buffer[i]) return false;
64 void BufferStorage::release(Buffer* buf)
67 for (i=0; i<NUMBER_OF_BUFFERS; i++)
68 if (buffer[i] == buf) break;
69 Buffer *tmpbuf = buffer[i];
75 Buffer* BufferStorage::newBuffer(string const &s,
80 while (i < NUMBER_OF_BUFFERS - 1
82 buffer[i] = new Buffer(s, lyxrc, ronly);
83 buffer[i]->params.useClassDefaults();
84 lyxerr.debug(string("Assigning to buffer ") + tostr(i), Error::ANY);
90 // Class BufferStrorage_Iter
93 Buffer* BufferStorage_Iter::operator() ()
96 for (i=index; i < BufferStorage::NUMBER_OF_BUFFERS; i++) {
106 Buffer* BufferStorage_Iter::operator[] (int a)
113 while(!cs->buffer[i++]);
115 if (i-1 < BufferStorage::NUMBER_OF_BUFFERS)
116 return cs->buffer[i-1];
124 BufferList::BufferList()
126 _state = BufferList::OK;
130 BufferList::~BufferList()
132 // I am sure something should be done here too.
136 bool BufferList::isEmpty()
138 return bstore.isEmpty();
141 extern void MenuWrite(Buffer*);
143 bool BufferList::QwriteAll()
145 bool askMoreConfirmation = false;
147 BufferStorage_Iter biter(bstore);
149 while ((b=biter())) {
150 if (!b->isLyxClean()) {
151 switch(AskConfirmation(_("Changes in document:"),
152 MakeDisplayPath(b->filename,50),
153 _("Save document?"))) {
158 askMoreConfirmation = true;
159 unsaved += MakeDisplayPath(b->filename,50);
167 if (askMoreConfirmation &&
168 lyxrc->exit_confirmation &&
169 !AskQuestion(_("Some documents were not saved:"),
170 unsaved, _("Exit anyway?"))) {
178 // Should probably be moved to somewhere else: BufferView? LyXView?
179 bool BufferList::write(Buffer *buf, bool makeBackup)
181 minibuffer->Set(_("Saving document"),
182 MakeDisplayPath(buf->filename),"...");
184 // We don't need autosaves in the immediate future. (Asger)
185 buf->resetAutosaveTimers();
189 string s = buf->filename + '~';
190 // Rename is the wrong way of making a backup,
191 // this is the correct way.
192 /* truss cp fil fil2:
193 lstat("LyXVC3.lyx", 0xEFFFF898) Err#2 ENOENT
194 stat("LyXVC.lyx", 0xEFFFF688) = 0
195 open("LyXVC.lyx", O_RDONLY) = 3
196 open("LyXVC3.lyx", O_WRONLY|O_CREAT|O_TRUNC, 0600) = 4
197 fstat(4, 0xEFFFF508) = 0
198 fstat(3, 0xEFFFF508) = 0
199 read(3, " # T h i s f i l e w".., 8192) = 5579
200 write(4, " # T h i s f i l e w".., 5579) = 5579
201 read(3, 0xEFFFD4A0, 8192) = 0
204 chmod("LyXVC3.lyx", 0100644) = 0
205 lseek(0, 0, SEEK_CUR) = 46440
209 // Should proabaly have some more error checking here.
210 // Should be cleaned up in 0.13, at least a bit.
211 // Doing it this way, also makes the inodes stay the same.
212 // This is still not a very good solution, in particular we
213 // might loose the owner of the backup.
214 FileInfo finfo(buf->filename);
216 mode_t fmode = finfo.getMode();
218 struct utimbuf *times =
219 (struct utimbuf*)new char[sizeof(struct utimbuf)];
220 times->actime = finfo.getAccessTime();
221 times->modtime = finfo.getModificationTime();
222 long blksize = finfo.getBlockSize();
223 lyxerr.debug(string("BlockSize: ") + tostr(blksize));
224 FilePtr fin(buf->filename,FilePtr::read);
225 FilePtr fout(s,FilePtr::truncate);
226 if (fin() && fout()) {
227 char * cbuf = new char[blksize+1];
231 c_read = fread(cbuf, 1, blksize, fin);
234 fwrite(cbuf, 1, c_read, fout);
238 chmod(s.c_str(), fmode);
240 if (utime(s.c_str(), times)) {
241 lyxerr.print("utime error.");
245 lyxerr.print("LyX was not able to make backupcopy. Beware.");
251 if (buf->writeFile(buf->filename,false)) {
254 minibuffer->Set(_("Document saved as"),
255 MakeDisplayPath(buf->filename));
257 // now delete the autosavefile
258 string a = OnlyPath(buf->filename);
260 a += OnlyFilename(buf->filename);
262 FileInfo fileinfo(a);
263 if (fileinfo.exist()) {
264 if (remove(a.c_str()) != 0) {
265 WriteFSAlert(_("Could not delete "
266 "auto-save file!"), a);
270 // Saving failed, so backup is not backup
272 string s = buf->filename + '~';
273 rename(s.c_str(), buf->filename.c_str());
275 minibuffer->Set(_("Save failed!"));
283 void BufferList::closeAll()
285 _state = BufferList::CLOSING;
287 BufferStorage_Iter biter(bstore);
289 while ((b=biter())) {
292 _state = BufferList::OK;
296 void BufferList::resize()
298 BufferStorage_Iter biter(bstore);
300 while ((b=biter())) {
306 bool BufferList::close(Buffer *buf)
310 if (buf->paragraph && !buf->isLyxClean() && !quitting) {
312 switch(AskConfirmation(_("Changes in document:"),
313 MakeDisplayPath(buf->filename,50),
314 _("Save document?"))){
317 lastfiles->newFile(buf->filename);
335 void BufferList::makePup(int pup)
336 /* This should be changed to return a char const *const
337 in the same way as for lastfiles.[hC]
341 BufferStorage_Iter biter(bstore);
343 while ((b=biter())) {
344 string relbuf = MakeDisplayPath(b->filename,30);
345 fl_addtopup(pup, relbuf.c_str());
348 if (ant == 0) fl_addtopup(pup,_("No Documents Open!%t"));
352 Buffer* BufferList::first()
354 BufferStorage_Iter biter(bstore);
359 Buffer* BufferList::getBuffer(int choice)
361 BufferStorage_Iter biter(bstore);
365 // Be careful, this could be 0.
370 void BufferList::updateInset(Inset *inset, bool mark_dirty)
372 BufferStorage_Iter biter(bstore);
374 while ((b=biter())) {
375 if (b->text && b->text->UpdateInset(inset)) {
384 int BufferList::unlockInset(UpdatableInset *inset)
386 if (!inset) return 1;
388 BufferStorage_Iter biter(bstore);
390 while ((b=biter())) {
391 if (b->the_locking_inset == inset) {
400 void BufferList::updateIncludedTeXfiles(string const & mastertmpdir)
402 BufferStorage_Iter biter(bstore);
404 while ((b=biter())) {
405 if (!b->isDepClean(mastertmpdir)) {
406 string writefile = mastertmpdir;
408 writefile += ChangeExtension(b->getFileName(), ".tex", true);
409 b->makeLaTeXFile(writefile,mastertmpdir,false,true);
410 b->markDepClean(mastertmpdir);
416 void BufferList::emergencyWriteAll()
418 BufferStorage_Iter biter(bstore);
420 while ((b=biter())) {
421 if (!b->isLyxClean()) {
424 lyxerr.print(_("lyx: Attempting to save"
429 for (int i=0; i<3 && !madeit; i++) {
432 // We try to save three places:
433 // 1) Same place as document.
434 // 2) In HOME directory.
435 // 3) In "/tmp" directory.
439 s = AddName(GetEnvPath("HOME"),
441 } else { // MakeAbsPath to prepend the current drive letter on OS/2
442 s = AddName(MakeAbsPath("/tmp/"),
447 lyxerr.print(string(" ") + tostr(i+1) + ") " + s);
449 if (b->writeFile(s,true)) {
451 lyxerr.print(_(" Save seems successful. Phew."));
454 lyxerr.print(_(" Save failed! Trying..."));
456 lyxerr.print(_(" Save failed! Bummer. Document is lost."));
464 Buffer* BufferList::readFile(string const & s, bool ronly)
466 Buffer *b = bstore.newBuffer(s, lyxrc, ronly);
469 string e = OnlyPath(s);
471 // File information about normal file
472 FileInfo fileInfo2(s);
474 // Check if emergency save file exists and is newer.
475 e += OnlyFilename(s) + ".emergency";
476 FileInfo fileInfoE(e);
478 bool use_emergency = false;
480 if (fileInfoE.exist() && fileInfo2.exist()) {
481 if (fileInfoE.getModificationTime()
482 > fileInfo2.getModificationTime()) {
483 if (AskQuestion(_("An emergency save of this document exists!"),
484 MakeDisplayPath(s,50),
485 _("Try to load that instead?"))) {
487 // the file is not saved if we load the
490 use_emergency = true;
492 // Here, we should delete the emergency save
498 if (!use_emergency) {
499 // Now check if autosave file is newer.
501 a += OnlyFilename(s);
503 FileInfo fileInfoA(a);
504 if (fileInfoA.exist() && fileInfo2.exist()) {
505 if (fileInfoA.getModificationTime()
506 > fileInfo2.getModificationTime()) {
507 if (AskQuestion(_("Autosave file is newer."),
508 MakeDisplayPath(s,50),
509 _("Load that one instead?"))) {
511 // the file is not saved if we load the
515 // Here, we should delete the autosave
521 // not sure if this is the correct place to begin LyXLex
524 if (b->readFile(lex))
533 bool BufferList::exists(string const & s)
535 BufferStorage_Iter biter(bstore);
537 while ((b=biter())) {
538 if (b->filename == s)
545 Buffer* BufferList::getBuffer(string const &s)
547 BufferStorage_Iter biter(bstore);
549 while ((b=biter())) {
557 Buffer* BufferList::newFile(string const & name, string tname)
559 /* get a free buffer */
560 Buffer *b = bstore.newBuffer(name, lyxrc);
562 // use defaults.lyx as a default template if it exists.
564 tname = LibFileSearch("templates", "defaults.lyx");
566 if (!tname.empty() && IsLyXFilename(tname)){
567 bool templateok = false;
571 if (b->readFile(lex)) {
576 WriteAlert(_("Error!"),_("Unable to open template"),
577 MakeDisplayPath(tname));
578 // no template, start with empty buffer
579 b->paragraph = new LyXParagraph();
582 else { // start with empty buffer
583 b->paragraph = new LyXParagraph();
587 b->setReadonly(false);
593 Buffer* BufferList::loadLyXFile(string const & filename, bool tolastfiles)
595 // make sure our path is absolute
596 string s = MakeAbsPath(filename);
598 // Is this done too early?
600 if (IsSGMLFilename(s)) {
602 if (fi.exist() && fi.readable()) {
603 if (!RunLinuxDoc(-1, s)) {
604 s = ChangeExtension (s, ".lyx", false);
605 } else { // sgml2lyx failed
606 WriteAlert(_("Error!"),
607 _("Could not convert file"),s);
611 // just change the extension and it will be
612 // handled like a regular lyx file that does
614 s = ChangeExtension(s, ".lyx", false);
618 // file already open?
620 if (AskQuestion(_("Document is already open:"),
621 MakeDisplayPath(s,50),
622 _("Do you want to reload that document?"))) {
623 // Reload is accomplished by closing and then loading
624 if (!close(getBuffer(s))) {
627 // Fall through to new load. (Asger)
629 // Here, we pretend that we just loaded the
636 switch (IsFileWriteable(s)) {
638 minibuffer->Set(_("File `")+MakeDisplayPath(s,50)+
639 _("' is read-only."));
645 b->lyxvc.file_found_hook(s);
647 break; //fine- it's r/w
649 // Here we probably should run
650 if (LyXVC::file_not_found_hook(s)) {
651 // Ask if the file should be checked out for
652 // viewing/editing, if so: load it.
653 lyxerr.print("Do you want to checkout?");
655 if (AskQuestion(_("Cannot open specified file:"),
656 MakeDisplayPath(s,50),
657 _("Create new document with this name?")))
659 // Find a free buffer
660 b = newFile(s,string());
665 if (b && tolastfiles)
666 lastfiles->newFile(b->getFileName());