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() << "Assigning to buffer " << i << endl;
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() << "BlockSize: " << blksize << endl;
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 << "utime error." << endl;
245 lyxerr << "LyX was not able to make backupcopy. Beware." << endl;
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 <<_("lyx: Attempting to save"
427 << _(" as...") << endl;
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 << " " << i+1 << ") " << s << endl;
449 if (b->writeFile(s,true)) {
451 lyxerr << _(" Save seems successful. Phew.") << endl;
454 lyxerr << _(" Save failed! Trying...")
457 lyxerr << _(" Save failed! Bummer. Document is lost.") << endl;
465 Buffer* BufferList::readFile(string const & s, bool ronly)
467 Buffer *b = bstore.newBuffer(s, lyxrc, ronly);
470 string e = OnlyPath(s);
472 // File information about normal file
473 FileInfo fileInfo2(s);
475 // Check if emergency save file exists and is newer.
476 e += OnlyFilename(s) + ".emergency";
477 FileInfo fileInfoE(e);
479 bool use_emergency = false;
481 if (fileInfoE.exist() && fileInfo2.exist()) {
482 if (fileInfoE.getModificationTime()
483 > fileInfo2.getModificationTime()) {
484 if (AskQuestion(_("An emergency save of this document exists!"),
485 MakeDisplayPath(s,50),
486 _("Try to load that instead?"))) {
488 // the file is not saved if we load the
491 use_emergency = true;
493 // Here, we should delete the emergency save
499 if (!use_emergency) {
500 // Now check if autosave file is newer.
502 a += OnlyFilename(s);
504 FileInfo fileInfoA(a);
505 if (fileInfoA.exist() && fileInfo2.exist()) {
506 if (fileInfoA.getModificationTime()
507 > fileInfo2.getModificationTime()) {
508 if (AskQuestion(_("Autosave file is newer."),
509 MakeDisplayPath(s,50),
510 _("Load that one instead?"))) {
512 // the file is not saved if we load the
516 // Here, we should delete the autosave
522 // not sure if this is the correct place to begin LyXLex
525 if (b->readFile(lex))
534 bool BufferList::exists(string const & s)
536 BufferStorage_Iter biter(bstore);
538 while ((b=biter())) {
539 if (b->filename == s)
546 Buffer* BufferList::getBuffer(string const &s)
548 BufferStorage_Iter biter(bstore);
550 while ((b=biter())) {
558 Buffer* BufferList::newFile(string const & name, string tname)
560 /* get a free buffer */
561 Buffer *b = bstore.newBuffer(name, lyxrc);
563 // use defaults.lyx as a default template if it exists.
565 tname = LibFileSearch("templates", "defaults.lyx");
567 if (!tname.empty() && IsLyXFilename(tname)){
568 bool templateok = false;
572 if (b->readFile(lex)) {
577 WriteAlert(_("Error!"),_("Unable to open template"),
578 MakeDisplayPath(tname));
579 // no template, start with empty buffer
580 b->paragraph = new LyXParagraph();
583 else { // start with empty buffer
584 b->paragraph = new LyXParagraph();
588 b->setReadonly(false);
594 Buffer* BufferList::loadLyXFile(string const & filename, bool tolastfiles)
596 // make sure our path is absolute
597 string s = MakeAbsPath(filename);
599 // Is this done too early?
601 if (IsSGMLFilename(s)) {
603 if (fi.exist() && fi.readable()) {
604 if (!RunLinuxDoc(-1, s)) {
605 s = ChangeExtension (s, ".lyx", false);
606 } else { // sgml2lyx failed
607 WriteAlert(_("Error!"),
608 _("Could not convert file"),s);
612 // just change the extension and it will be
613 // handled like a regular lyx file that does
615 s = ChangeExtension(s, ".lyx", false);
619 // file already open?
621 if (AskQuestion(_("Document is already open:"),
622 MakeDisplayPath(s,50),
623 _("Do you want to reload that document?"))) {
624 // Reload is accomplished by closing and then loading
625 if (!close(getBuffer(s))) {
628 // Fall through to new load. (Asger)
630 // Here, we pretend that we just loaded the
637 switch (IsFileWriteable(s)) {
639 minibuffer->Set(_("File `")+MakeDisplayPath(s,50)+
640 _("' is read-only."));
646 b->lyxvc.file_found_hook(s);
648 break; //fine- it's r/w
650 // Here we probably should run
651 if (LyXVC::file_not_found_hook(s)) {
652 // Ask if the file should be checked out for
653 // viewing/editing, if so: load it.
654 lyxerr << "Do you want to checkout?" << endl;
656 if (AskQuestion(_("Cannot open specified file:"),
657 MakeDisplayPath(s,50),
658 _("Create new document with this name?")))
660 // Find a free buffer
661 b = newFile(s,string());
666 if (b && tolastfiles)
667 lastfiles->newFile(b->getFileName());