]> git.lyx.org Git - lyx.git/blob - src/bufferlist.C
Initial revision
[lyx.git] / src / bufferlist.C
1 /* This file is part of
2  * ======================================================
3  * 
4  *           LyX, The Document Word Processor
5  *
6  *          Copyright (C) 1995 Matthias Ettrich
7  *          Copyright (C) 1995-1998 The LyX Team. 
8  *
9  *           This file is Copyleft (C) 1996-1998
10  *           Lars Gullik Bjønnes
11  *
12  *======================================================
13  */
14
15 #ifdef __GNUG__
16 #pragma implementation
17 #endif
18
19 #include <config.h>
20 #include <sys/types.h>
21 #include <utime.h>
22 #include "bufferlist.h"
23 #include "lyx_main.h"
24 #include "minibuffer.h"
25 #include "FileInfo.h"
26 #include "filetools.h"
27 #include "lyx_gui_misc.h"
28 #include "lastfiles.h"
29 #include "error.h"
30 #include "lyxrc.h"
31 #include "lyxscreen.h"
32 #include "lyxtext.h"
33 #include "lyx_cb.h"
34 #include "gettext.h"
35
36 //      $Id: bufferlist.C,v 1.1 1999/09/27 18:44:37 larsbj Exp $        
37
38 #if !defined(lint) && !defined(WITH_WARNINGS)
39 static char vcid[] = "$Id: bufferlist.C,v 1.1 1999/09/27 18:44:37 larsbj Exp $";
40 #endif /* lint */
41
42 extern BufferView *current_view;
43 extern MiniBuffer *minibuffer;
44 extern void SmallUpdate(signed char);
45 extern void BeforeChange();
46 extern int RunLinuxDoc(int, LString const &);
47
48 //
49 // Class BufferStorage
50 //
51
52 BufferStorage::BufferStorage()
53 {
54         // Initialize the buffer array
55         for (int i=NUMBER_OF_BUFFERS-1; i >=0; i--) {
56                 buffer[i] = 0;
57         }       
58 }
59
60
61 bool BufferStorage::isEmpty()
62 {
63         for (int i=NUMBER_OF_BUFFERS-1; i >=0; i--) {
64                 if (buffer[i]) return false;
65         }
66         return true;
67 }
68
69
70 void BufferStorage::release(Buffer* buf)
71 {
72         int i=0;
73         for (i=0; i<NUMBER_OF_BUFFERS; i++)
74                 if (buffer[i] == buf) break;
75         Buffer *tmpbuf = buffer[i];
76         buffer[i] = 0;
77         delete tmpbuf;
78 }
79
80
81 Buffer* BufferStorage::newBuffer(LString const &s,
82                                  LyXRC *lyxrc,
83                                  bool ronly)
84 {
85         int i=0;
86         while (i < NUMBER_OF_BUFFERS - 1
87                && buffer[i]) i++;
88         buffer[i] = new Buffer(s, lyxrc, ronly);
89         buffer[i]->params.useClassDefaults();
90         lyxerr.debug(LString("Assigning to buffer ") + i, Error::ANY);
91         return buffer[i];
92 }
93
94
95 //
96 // Class BufferStrorage_Iter
97 //
98
99 Buffer* BufferStorage_Iter::operator() ()
100 {
101         int i=0;
102         for (i=index; i < BufferStorage::NUMBER_OF_BUFFERS; i++) {
103                 if (cs->buffer[i]) {
104                         index = i+1;
105                         return cs->buffer[i];
106                 }
107         }
108         return 0;       
109 }
110
111
112 Buffer* BufferStorage_Iter::operator[] (int a)
113 {
114         // a is >=1
115         if (a<=0) return 0;
116         
117         int i=0;
118         while (a--) {
119                 while(!cs->buffer[i++]);
120         }
121         if (i-1 < BufferStorage::NUMBER_OF_BUFFERS)
122                 return cs->buffer[i-1];
123         return 0;       
124 }
125
126
127 //
128 // Class BufferList
129 //
130 BufferList::BufferList()
131 {
132         _state = BufferList::OK;
133 }
134
135
136 BufferList::~BufferList()
137 {
138         // I am sure something should be done here too.
139 }
140
141
142 bool BufferList::isEmpty()
143 {
144         return bstore.isEmpty();
145 }
146
147 extern void MenuWrite(Buffer*);
148
149 bool BufferList::QwriteAll()
150 {
151         bool askMoreConfirmation = false;
152         LString unsaved;
153         BufferStorage_Iter biter(bstore);
154         Buffer *b=0;
155         while ((b=biter())) {
156                 if (!b->isLyxClean()) {
157                         switch(AskConfirmation(_("Changes in document:"),
158                                                MakeDisplayPath(b->filename,50),
159                                                _("Save document?"))) {
160                         case 1: // Yes
161                                 MenuWrite(b);
162                                 break;
163                         case 2: // No
164                                 askMoreConfirmation = true;
165                                 unsaved += MakeDisplayPath(b->filename,50);
166                                 unsaved += "\n";
167                                 break;
168                         case 3: // Cancel
169                                 return false;
170                         }
171                 }
172         }
173         if (askMoreConfirmation &&
174             lyxrc->exit_confirmation &&
175             !AskQuestion(_("Some documents were not saved:"),
176                          unsaved, _("Exit anyway?"))) {
177                 return false;
178         }
179
180         return true;
181 }
182
183
184 // Should probably be moved to somewhere else: BufferView? LyXView?
185 bool BufferList::write(Buffer *buf, bool makeBackup)
186 {
187         minibuffer->Set(_("Saving document"),
188                         MakeDisplayPath(buf->filename),"...");
189
190         // We don't need autosaves in the immediate future. (Asger)
191         buf->resetAutosaveTimers();
192
193         // make a backup
194         if (makeBackup) {
195                 LString s = buf->filename + '~';
196                 // Rename is the wrong way of making a backup,
197                 // this is the correct way.
198                 /* truss cp fil fil2:
199                    lstat("LyXVC3.lyx", 0xEFFFF898)                 Err#2 ENOENT
200                    stat("LyXVC.lyx", 0xEFFFF688)                   = 0
201                    open("LyXVC.lyx", O_RDONLY)                     = 3
202                    open("LyXVC3.lyx", O_WRONLY|O_CREAT|O_TRUNC, 0600) = 4
203                    fstat(4, 0xEFFFF508)                            = 0
204                    fstat(3, 0xEFFFF508)                            = 0
205                    read(3, " # T h i s   f i l e   w".., 8192)     = 5579
206                    write(4, " # T h i s   f i l e   w".., 5579)    = 5579
207                    read(3, 0xEFFFD4A0, 8192)                       = 0
208                    close(4)                                        = 0
209                    close(3)                                        = 0
210                    chmod("LyXVC3.lyx", 0100644)                    = 0
211                    lseek(0, 0, SEEK_CUR)                           = 46440
212                    _exit(0)
213                  */
214
215                 // Should proabaly have some more error checking here.
216                 // Should be cleaned up in 0.13, at least a bit.
217                 // Doing it this way, also makes the inodes stay the same.
218                 // This is still not a very good solution, in particular we
219                 // might loose the owner of the backup.
220                 FileInfo finfo(buf->filename);
221                 if (finfo.exist()) {
222                         mode_t fmode = finfo.getMode();
223
224                         struct utimbuf *times =
225                                 (struct utimbuf*)new char[sizeof(struct utimbuf)];
226                         times->actime = finfo.getAccessTime();
227                         times->modtime = finfo.getModificationTime();
228                         long blksize = finfo.getBlockSize();
229                         lyxerr.debug(LString("BlockSize: ") + int(blksize));
230                         FilePtr fin(buf->filename,FilePtr::read);
231                         FilePtr fout(s,FilePtr::truncate);
232                         if (fin() && fout()) {
233                                 char * cbuf = new char[blksize+1];
234                                 size_t c_read = 0;
235                                 size_t c_write = 0;
236                                 do {
237                                         c_read = fread(cbuf, 1, blksize, fin);
238                                         if (c_read != 0)
239                                                 c_write =
240                                                         fwrite(cbuf, 1, c_read, fout);
241                                 } while (c_read);
242                                 fin.close();
243                                 fout.close();
244                                 chmod(s.c_str(), fmode);
245                                 
246                                 if (utime(s.c_str(), times)) {
247                                         lyxerr.print("utime error.");
248                                 }
249                                 delete [] cbuf;
250                         } else {
251                                 lyxerr.print("LyX was not able to make backupcopy. Beware.");
252                         }
253                         delete[] times;
254                 }
255         }
256         
257         if (buf->writeFile(buf->filename,false)) {
258                 buf->markLyxClean();
259
260                 minibuffer->Set(_("Document saved as"),
261                                 MakeDisplayPath(buf->filename));
262
263                 // now delete the autosavefile
264                 LString a = OnlyPath(buf->filename);
265                 a += '#';
266                 a += OnlyFilename(buf->filename);
267                 a += '#';
268                 FileInfo fileinfo(a);
269                 if (fileinfo.exist()) {
270                         if (remove(a.c_str()) != 0) {
271                                 WriteFSAlert(_("Could not delete "
272                                                "auto-save file!"), a);
273                         }
274                 }
275         } else {
276                 // Saving failed, so backup is not backup
277                 if (makeBackup) {
278                         LString s = buf->filename + '~';
279                         rename(s.c_str(), buf->filename.c_str());
280                 }
281                 minibuffer->Set(_("Save failed!"));
282                 return false;
283         }
284
285         return true;
286 }
287
288
289 void BufferList::closeAll()
290 {
291         _state = BufferList::CLOSING;
292         
293         BufferStorage_Iter biter(bstore);
294         Buffer *b=0;
295         while ((b=biter())) {
296                 close(b);
297         }
298         _state = BufferList::OK;
299 }
300
301
302 void BufferList::resize()
303 {
304         BufferStorage_Iter biter(bstore);
305         Buffer *b=0;
306         while ((b=biter())) {
307                 b->resize();
308         }
309 }
310
311
312 bool BufferList::close(Buffer *buf)
313 {
314         buf->InsetUnlock();
315         
316         if (buf->paragraph && !buf->isLyxClean() && !quitting) {
317                 ProhibitInput();
318                 switch(AskConfirmation(_("Changes in document:"),
319                               MakeDisplayPath(buf->filename,50),
320                                       _("Save document?"))){
321                 case 1: // Yes
322                         if (write(buf)) {
323                                 lastfiles->newFile(buf->filename);
324                         } else {
325                                 AllowInput();
326                                 return false;
327                         }
328                         break;
329                 case 3: // Cancel
330                         AllowInput();
331                         return false;
332                 }
333                 AllowInput();
334         }
335
336         bstore.release(buf);
337         return true;
338 }
339
340
341 void BufferList::makePup(int pup)
342         /* This should be changed to return a char const *const
343            in the same way as for lastfiles.[hC]
344            */
345 {
346         int ant=0;
347         BufferStorage_Iter biter(bstore);
348         Buffer *b=0;
349         while ((b=biter())) {
350                 LString relbuf = MakeDisplayPath(b->filename,30);
351                 fl_addtopup(pup, relbuf.c_str());
352                 ant++;
353         }
354         if (ant == 0) fl_addtopup(pup,_("No Documents Open!%t"));
355 }
356
357
358 Buffer* BufferList::first()
359 {
360         BufferStorage_Iter biter(bstore);
361         return biter();
362 }
363
364
365 Buffer* BufferList::getBuffer(int choice)
366 {
367         BufferStorage_Iter biter(bstore);
368         Buffer *b=0;
369         b = biter[choice];
370             
371         // Be careful, this could be 0.    
372         return b;
373 }
374
375
376 void BufferList::updateInset(Inset *inset, bool mark_dirty)
377 {
378         BufferStorage_Iter biter(bstore);
379         Buffer *b=0;
380         while ((b=biter())) {
381                 if (b->text && b->text->UpdateInset(inset)) {
382                         if (mark_dirty)
383                                 b->markDirty();
384                         break;
385                 }
386         }
387 }
388
389
390 int BufferList::unlockInset(UpdatableInset *inset)
391 {
392         if (!inset) return 1;
393         
394         BufferStorage_Iter biter(bstore);
395         Buffer *b=0;
396         while ((b=biter())) {
397                 if (b->the_locking_inset == inset) {
398                         b->InsetUnlock();
399                         return 0;
400                 }
401         }
402         return 1;
403 }
404
405
406 void BufferList::updateIncludedTeXfiles(LString const & mastertmpdir)
407 {
408         BufferStorage_Iter biter(bstore);
409         Buffer *b=0;
410         while ((b=biter())) {
411                 if (!b->isDepClean(mastertmpdir)) {
412                         LString writefile = mastertmpdir;
413                         writefile += '/';
414                         writefile += ChangeExtension(b->getFileName(), ".tex", true);
415                         b->makeLaTeXFile(writefile,mastertmpdir,false,true);
416                         b->markDepClean(mastertmpdir);
417                 }
418         }
419 }
420
421
422 void BufferList::emergencyWriteAll()
423 {
424         BufferStorage_Iter biter(bstore);
425         Buffer *b=0;
426         while ((b=biter())) {
427                 if (!b->isLyxClean()) {
428                         bool madeit=false;
429                         
430                         lyxerr.print(_("lyx: Attempting to save"
431                                       " document ")
432                                       + b->filename
433                                       + _(" as..."));
434                         
435                         for (int i=0; i<3 && !madeit; i++) {
436                                 LString s;
437                                 
438                                 // We try to save three places:
439                                 // 1) Same place as document.
440                                 // 2) In HOME directory.
441                                 // 3) In "/tmp" directory.
442                                 if (i==0) {
443                                         s = b->filename;
444                                 } else if (i==1) {
445                                         s = AddName(getEnvPath("HOME"),
446                                                     b->filename);
447                                 } else { // MakeAbsPath to prepend the current drive letter on OS/2
448                                         s = AddName(MakeAbsPath("/tmp/"),
449                                                     b->filename);
450                                 }
451                                 s += ".emergency";
452                                 
453                                 lyxerr.print(LString("  ") + (i+1) + ") " + s);
454                                 
455                                 if (b->writeFile(s,true)) {
456                                         b->markLyxClean();
457                                         lyxerr.print(_("  Save seems successful. Phew."));
458                                         madeit = true;
459                                 } else if (i != 2) {
460                                         lyxerr.print(_("  Save failed! Trying..."));
461                                 } else {
462                                         lyxerr.print(_("  Save failed! Bummer. Document is lost."));
463                                 }
464                         }
465                 }
466         }
467 }
468
469
470 Buffer* BufferList::readFile(LString const & s, bool ronly)
471 {
472         Buffer *b = bstore.newBuffer(s, lyxrc, ronly);
473
474         LString ts = s;
475         LString e = OnlyPath(s);
476         LString a = e;
477         // File information about normal file
478         FileInfo fileInfo2(s);
479
480         // Check if emergency save file exists and is newer.
481         e += OnlyFilename(s) + ".emergency";
482         FileInfo fileInfoE(e);
483
484         bool use_emergency = false;
485
486         if (fileInfoE.exist() && fileInfo2.exist()) {
487                 if (fileInfoE.getModificationTime()
488                     > fileInfo2.getModificationTime()) {
489                         if (AskQuestion(_("An emergency save of this document exists!"),
490                                         MakeDisplayPath(s,50),
491                                         _("Try to load that instead?"))) {
492                                 ts = e;
493                                 // the file is not saved if we load the
494                                 // emergency file.
495                                 b->markDirty();
496                                 use_emergency = true;
497                         } else {
498                                 // Here, we should delete the emergency save
499                                 unlink(e.c_str());
500                         }
501                 }
502         }
503
504         if (!use_emergency) {
505                 // Now check if autosave file is newer.
506                 a += '#';
507                 a += OnlyFilename(s);
508                 a += '#';
509                 FileInfo fileInfoA(a);
510                 if (fileInfoA.exist() && fileInfo2.exist()) {
511                         if (fileInfoA.getModificationTime()
512                             > fileInfo2.getModificationTime()) {
513                                 if (AskQuestion(_("Autosave file is newer."),
514                                                 MakeDisplayPath(s,50),
515                                                 _("Load that one instead?"))) {
516                                         ts = a;
517                                         // the file is not saved if we load the
518                                         // autosave file.
519                                         b->markDirty();
520                                 } else {
521                                         // Here, we should delete the autosave
522                                         unlink(a.c_str());
523                                 }
524                         }
525                 }
526         }
527         // not sure if this is the correct place to begin LyXLex
528         LyXLex lex(NULL, 0);
529         lex.setFile(ts);
530         if (b->readFile(lex))
531                 return b;
532         else {
533                 bstore.release(b);
534                 return 0;
535         }
536 }
537
538
539 bool BufferList::exists(LString const & s)
540 {
541         BufferStorage_Iter biter(bstore);
542         Buffer *b=0;
543         while ((b=biter())) {
544                 if (b->filename == s)
545                         return true;
546         }
547         return false;
548 }
549
550
551 Buffer* BufferList::getBuffer(LString const &s)
552 {
553         BufferStorage_Iter biter(bstore);
554         Buffer *b=0;
555         while ((b=biter())) {
556                 if (b->filename ==s)
557                         return b;
558         }
559         return 0;
560 }
561
562
563 Buffer* BufferList::newFile(LString const & name, LString tname)
564 {
565         /* get a free buffer */ 
566         Buffer *b = bstore.newBuffer(name, lyxrc);
567
568         // use defaults.lyx as a default template if it exists.
569         if (tname.empty()) {
570                 tname = LibFileSearch("templates", "defaults.lyx");
571         }
572         if (!tname.empty() && IsLyXFilename(tname)){
573                 bool templateok = false;
574                 LyXLex lex(NULL,0);
575                 lex.setFile(tname);
576                 if (lex.IsOK()) {
577                         if (b->readFile(lex)) {
578                                 templateok = true;
579                         }
580                 }
581                 if (!templateok) {
582                         WriteAlert(_("Error!"),_("Unable to open template"), 
583                                    MakeDisplayPath(tname));
584                         // no template, start with empty buffer
585                         b->paragraph = new LyXParagraph();
586                 }
587         }
588         else {  // start with empty buffer
589                 b->paragraph = new LyXParagraph();
590         }
591
592         b->markDirty();
593         b->setReadonly(false);
594         
595         return b;
596 }
597
598
599 Buffer* BufferList::loadLyXFile(LString const & filename, bool tolastfiles)
600 {
601         // make sure our path is absolute
602         LString s = MakeAbsPath(filename);
603
604         // Is this done too early?
605         // Is it LinuxDoc?
606         if (IsSGMLFilename(s)) {
607                 FileInfo fi(s);
608                 if (fi.exist() && fi.readable()) {
609                         if (!RunLinuxDoc(-1, s)) {
610                                 s = ChangeExtension (s, ".lyx", false);
611                         } else { // sgml2lyx failed
612                                 WriteAlert(_("Error!"),
613                                            _("Could not convert file"),s);
614                                 return 0;
615                         }
616                 } else {
617                         // just change the extension and it will be
618                         // handled like a regular lyx file that does
619                         // not exist.
620                         s = ChangeExtension(s, ".lyx", false);
621                 }
622         }
623         
624         // file already open?
625         if (exists(s)) {
626                 if (AskQuestion(_("Document is already open:"), 
627                                 MakeDisplayPath(s,50),
628                                 _("Do you want to reload that document?"))) {
629                         // Reload is accomplished by closing and then loading
630                         if (!close(getBuffer(s))) {
631                                 return 0;
632                         }
633                         // Fall through to new load. (Asger)
634                 } else {
635                         // Here, we pretend that we just loaded the 
636                         // open document
637                         return getBuffer(s);
638                 }
639         }
640         Buffer *b=0;
641         bool ro = false;
642         switch (IsFileWriteable(s)) {
643         case 0:
644                 minibuffer->Set(_("File `")+MakeDisplayPath(s,50)+
645                                 _("' is read-only."));
646                 ro = true;
647                 // Fall through
648         case 1:
649                 b=readFile(s, ro);
650                 if (b) {
651                         b->lyxvc.file_found_hook(s);
652                 }
653                 break; //fine- it's r/w
654         case -1:
655                 // Here we probably should run
656                 if (LyXVC::file_not_found_hook(s)) {
657                         // Ask if the file should be checked out for
658                         // viewing/editing, if so: load it.
659                         lyxerr.print("Do you want to checkout?");
660                 }
661                 if (AskQuestion(_("Cannot open specified file:"), 
662                                 MakeDisplayPath(s,50),
663                                 _("Create new document with this name?")))
664                 {
665                         // Find a free buffer
666                         b = newFile(s,LString());
667                 }
668                 break;
669         }
670
671         if (b && tolastfiles)
672                 lastfiles->newFile(b->getFileName());
673
674         return b;
675 }