]> git.lyx.org Git - lyx.git/blob - src/bufferlist.C
Removed the automatic import of linuxdoc at start, it will be added later
[lyx.git] / src / bufferlist.C
1 /* This file is part of
2  * ====================================================== 
3  * 
4  *           LyX, The Document Word Processor
5  *
6  *           Copyright 1995 Matthias Ettrich
7  *           Copyright 1995-2000 The LyX Team. 
8  *
9  *           This file is Copyright 1996-2000
10  *           Lars Gullik Bjønnes
11  *
12  * ====================================================== 
13  */
14
15 #ifdef __GNUG__
16 #pragma implementation
17 #endif
18
19 #include <config.h>
20
21 #include <algorithm>
22
23 #include "bufferlist.h"
24 #include "lyx_main.h"
25 #include "minibuffer.h"
26 #include "support/FileInfo.h"
27 #include "support/filetools.h"
28 #include "lyx_gui_misc.h"
29 #include "lastfiles.h"
30 #include "debug.h"
31 #include "lyxrc.h"
32 #include "lyxtext.h"
33 #include "lyx_cb.h"
34 #include "gettext.h"
35 #include "LyXView.h"
36 #include "vc-backend.h"
37 #include "TextCache.h"
38
39 extern BufferView * current_view; // called too many times in this file...
40
41 using std::find;
42
43 //
44 // Class BufferStorage
45 //
46
47 void BufferStorage::release(Buffer * buf)
48 {
49         Container::iterator it = find(container.begin(), container.end(), buf);
50         if (it != container.end()) {
51                 // Make sure that we don't store a LyXText in
52                 // the textcache that points to the buffer
53                 // we just deleted.
54                 Buffer * tmp = (*it);
55                 container.erase(it);
56                 textcache.removeAllWithBuffer(tmp);
57                 delete tmp;
58         }
59 }
60
61
62 Buffer * BufferStorage::newBuffer(string const & s, bool ronly)
63 {
64         Buffer * tmpbuf = new Buffer(s, ronly);
65         tmpbuf->params.useClassDefaults();
66         lyxerr.debug() << "Assigning to buffer "
67                        << container.size() << endl;
68         container.push_back(tmpbuf);
69         return tmpbuf;
70 }
71
72
73 //
74 // Class BufferList
75 //
76
77 BufferList::BufferList()
78         : state_(BufferList::OK)
79 {}
80
81
82 bool BufferList::empty() const
83 {
84         return bstore.empty();
85 }
86
87
88 extern void MenuWrite(Buffer *);
89
90 bool BufferList::QwriteAll()
91 {
92         bool askMoreConfirmation = false;
93         string unsaved;
94         for(BufferStorage::iterator it = bstore.begin();
95             it != bstore.end(); ++it) {
96                 if (!(*it)->isLyxClean()) {
97                         switch(AskConfirmation(_("Changes in document:"),
98                                                MakeDisplayPath((*it)->fileName(),
99                                                                50),
100                                                _("Save document?"))) {
101                         case 1: // Yes
102                                 MenuWrite((*it));
103                                 break;
104                         case 2: // No
105                                 askMoreConfirmation = true;
106                                 unsaved += MakeDisplayPath((*it)->fileName(),
107                                                            50);
108                                 unsaved += "\n";
109                                 break;
110                         case 3: // Cancel
111                                 return false;
112                         }
113                 }
114         }
115         if (askMoreConfirmation &&
116             lyxrc.exit_confirmation &&
117             !AskQuestion(_("Some documents were not saved:"),
118                          unsaved, _("Exit anyway?"))) {
119                 return false;
120         }
121
122         return true;
123 }
124
125
126 void BufferList::closeAll()
127 {
128         state_ = BufferList::CLOSING;
129         // Since we are closing we can just as well delete all
130         // in the textcache this will also speed the closing/quiting up a bit.
131         textcache.clear();
132         
133         while (!bstore.empty()) {
134                 close(bstore.front());
135         }
136         state_ = BufferList::OK;
137 }
138
139
140 void BufferList::resize()
141 {
142         for(BufferStorage::iterator it = bstore.begin();
143             it != bstore.end(); ++it) {
144                 (*it)->resize();
145         }
146 }
147
148
149 bool BufferList::close(Buffer * buf)
150 {
151         if (buf->getUser()) buf->getUser()->insetUnlock();
152         
153         if (buf->paragraph && !buf->isLyxClean() && !quitting) {
154                 ProhibitInput();
155                 switch(AskConfirmation(_("Changes in document:"),
156                                        MakeDisplayPath(buf->fileName(), 50),
157                                        _("Save document?"))){
158                 case 1: // Yes
159                         if (buf->save(lyxrc.make_backup)) {
160                                 lastfiles->newFile(buf->fileName());
161                         } else {
162                                 AllowInput();
163                                 return false;
164                         }
165                         break;
166                 case 3: // Cancel
167                         AllowInput();
168                         return false;
169                 }
170                 AllowInput();
171         }
172
173         bstore.release(buf);
174         return true;
175 }
176
177
178 vector<string> BufferList::getFileNames() const
179 {
180         vector<string> nvec;
181         for(BufferStorage::const_iterator cit = bstore.begin();
182             cit != bstore.end(); ++cit) {
183                 nvec.push_back((*cit)->fileName());
184         }
185         return nvec;
186 }
187
188
189 Buffer * BufferList::first()
190 {
191         if (bstore.empty()) return 0;
192         return bstore.front();
193 }
194
195
196 Buffer * BufferList::getBuffer(int choice)
197 {
198         if (choice >= bstore.size()) return 0;
199         return bstore[choice];
200 }
201
202
203 int BufferList::unlockInset(UpdatableInset * inset)
204 {
205         if (!inset) return 1;
206         for(BufferStorage::iterator it = bstore.begin();
207             it != bstore.end(); ++it) {
208                 if ((*it)->getUser()
209                     && (*it)->getUser()->the_locking_inset == inset) {
210                         (*it)->getUser()->insetUnlock();
211                         return 0;
212                 }
213         }
214         return 1;
215 }
216
217
218 void BufferList::updateIncludedTeXfiles(string const & mastertmpdir)
219 {
220         for(BufferStorage::iterator it = bstore.begin();
221             it != bstore.end(); ++it) {
222                 if (!(*it)->isDepClean(mastertmpdir)) {
223                         string writefile = mastertmpdir;
224                         writefile += '/';
225                         writefile += ChangeExtension((*it)->fileName(),
226                                                      ".tex", true);
227                         (*it)->makeLaTeXFile(writefile, mastertmpdir,
228                                              false, true);
229                         (*it)->markDepClean(mastertmpdir);
230                 }
231         }
232 }
233
234
235 void BufferList::emergencyWriteAll()
236 {
237         for (BufferStorage::iterator it = bstore.begin();
238              it != bstore.end(); ++it) {
239                 if (!(*it)->isLyxClean()) {
240                         bool madeit = false;
241                         
242                         lyxerr <<_("lyx: Attempting to save"
243                                    " document ")
244                                << (*it)->fileName()
245                                << _(" as...") << endl;
246                         
247                         for (int i = 0; i < 3 && !madeit; ++i) {
248                                 string s;
249                                 
250                                 // We try to save three places:
251                                 // 1) Same place as document.
252                                 // 2) In HOME directory.
253                                 // 3) In "/tmp" directory.
254                                 if (i == 0) {
255                                         s = (*it)->fileName();
256                                 } else if (i == 1) {
257                                         s = AddName(GetEnvPath("HOME"),
258                                                     (*it)->fileName());
259                                 } else {
260                                         // MakeAbsPath to prepend the current
261                                         // drive letter on OS/2
262                                         s = AddName(MakeAbsPath("/tmp/"),
263                                                     (*it)->fileName());
264                                 }
265                                 s += ".emergency";
266                                 
267                                 lyxerr << "  " << i + 1 << ") " << s << endl;
268                                 
269                                 if ((*it)->writeFile(s, true)) {
270                                         (*it)->markLyxClean();
271                                         lyxerr << _("  Save seems successful. "
272                                                     "Phew.") << endl;
273                                         madeit = true;
274                                 } else if (i != 2) {
275                                         lyxerr << _("  Save failed! Trying...")
276                                                << endl;
277                                 } else {
278                                         lyxerr << _("  Save failed! Bummer. "
279                                                     "Document is lost.")
280                                                << endl;
281                                 }
282                         }
283                 }
284         }
285 }
286
287
288 Buffer * BufferList::readFile(string const & s, bool ronly)
289 {
290         Buffer * b = bstore.newBuffer(s, ronly);
291
292         string ts = s;
293         string e = OnlyPath(s);
294         string a = e;
295         // File information about normal file
296         FileInfo fileInfo2(s);
297
298         // Check if emergency save file exists and is newer.
299         e += OnlyFilename(s) + ".emergency";
300         FileInfo fileInfoE(e);
301
302         bool use_emergency = false;
303
304         if (fileInfoE.exist() && fileInfo2.exist()) {
305                 if (fileInfoE.getModificationTime()
306                     > fileInfo2.getModificationTime()) {
307                         if (AskQuestion(_("An emergency save of this document exists!"),
308                                         MakeDisplayPath(s, 50),
309                                         _("Try to load that instead?"))) {
310                                 ts = e;
311                                 // the file is not saved if we load the
312                                 // emergency file.
313                                 b->markDirty();
314                                 use_emergency = true;
315                         } else {
316                                 // Here, we should delete the emergency save
317                                 ::unlink(e.c_str());
318                         }
319                 }
320         }
321
322         if (!use_emergency) {
323                 // Now check if autosave file is newer.
324                 a += '#';
325                 a += OnlyFilename(s);
326                 a += '#';
327                 FileInfo fileInfoA(a);
328                 if (fileInfoA.exist() && fileInfo2.exist()) {
329                         if (fileInfoA.getModificationTime()
330                             > fileInfo2.getModificationTime()) {
331                                 if (AskQuestion(_("Autosave file is newer."),
332                                                 MakeDisplayPath(s, 50),
333                                                 _("Load that one instead?"))) {
334                                         ts = a;
335                                         // the file is not saved if we load the
336                                         // autosave file.
337                                         b->markDirty();
338                                 } else {
339                                         // Here, we should delete the autosave
340                                         ::unlink(a.c_str());
341                                 }
342                         }
343                 }
344         }
345         // not sure if this is the correct place to begin LyXLex
346         LyXLex lex(0, 0);
347         lex.setFile(ts);
348         if (b->readFile(lex))
349                 return b;
350         else {
351                 bstore.release(b);
352                 return 0;
353         }
354 }
355
356
357 bool BufferList::exists(string const & s) const
358 {
359         for (BufferStorage::const_iterator cit = bstore.begin();
360              cit != bstore.end(); ++cit) {
361                 if ((*cit)->fileName() == s)
362                         return true;
363         }
364         return false;
365 }
366
367
368 bool BufferList::isLoaded(Buffer const * b) const
369 {
370         BufferStorage::const_iterator cit =
371                 find(bstore.begin(), bstore.end(), b);
372         return cit != bstore.end();
373 }
374
375
376 Buffer * BufferList::getBuffer(string const & s)
377 {
378         for(BufferStorage::iterator it = bstore.begin();
379             it != bstore.end(); ++it) {
380                 if ((*it)->fileName() == s)
381                         return (*it);
382         }
383         return 0;
384 }
385
386
387 Buffer * BufferList::newFile(string const & name, string tname)
388 {
389         // get a free buffer
390         Buffer * b = bstore.newBuffer(name);
391
392         // use defaults.lyx as a default template if it exists.
393         if (tname.empty()) {
394                 tname = LibFileSearch("templates", "defaults.lyx");
395         }
396         if (!tname.empty() && IsLyXFilename(tname)) {
397                 bool templateok = false;
398                 LyXLex lex(0, 0);
399                 lex.setFile(tname);
400                 if (lex.IsOK()) {
401                         if (b->readFile(lex)) {
402                                 templateok = true;
403                         }
404                 }
405                 if (!templateok) {
406                         WriteAlert(_("Error!"), _("Unable to open template"), 
407                                    MakeDisplayPath(tname));
408                         // no template, start with empty buffer
409                         b->paragraph = new LyXParagraph;
410                 }
411         }
412         else {  // start with empty buffer
413                 b->paragraph = new LyXParagraph;
414         }
415
416         b->markDirty();
417         b->setReadonly(false);
418         
419         return b;
420 }
421
422
423 Buffer * BufferList::loadLyXFile(string const & filename, bool tolastfiles)
424 {
425         // make sure our path is absolute
426         string s = MakeAbsPath(filename);
427
428         // file already open?
429         if (exists(s)) {
430                 if (AskQuestion(_("Document is already open:"), 
431                                 MakeDisplayPath(s, 50),
432                                 _("Do you want to reload that document?"))) {
433                         // Reload is accomplished by closing and then loading
434                         if (!close(getBuffer(s))) {
435                                 return 0;
436                         }
437                         // Fall through to new load. (Asger)
438                 } else {
439                         // Here, we pretend that we just loaded the 
440                         // open document
441                         return getBuffer(s);
442                 }
443         }
444         Buffer * b = 0;
445         bool ro = false;
446         switch (IsFileWriteable(s)) {
447         case 0:
448 #if 0
449                 current_view->owner()->getMiniBuffer()->
450                         Set(_("File `") + MakeDisplayPath(s, 50) +
451                             _("' is read-only."));
452 #endif
453                 ro = true;
454                 // Fall through
455         case 1:
456                 b = readFile(s, ro);
457                 if (b) {
458                         b->lyxvc.file_found_hook(s);
459                 }
460                 break; //fine- it's r/w
461         case -1:
462                 // Here we probably should run
463                 if (LyXVC::file_not_found_hook(s)) {
464                         // Ask if the file should be checked out for
465                         // viewing/editing, if so: load it.
466                         if (AskQuestion(_("Do you want to retrive file under version control?"))) {
467                                 // How can we know _how_ to do the checkout?
468                                 // With the current VC support it has to be,
469                                 // a RCS file since CVS do not have special ,v files.
470                                 RCS::retrive(s);
471                                 return loadLyXFile(filename, tolastfiles);
472                         }
473                 }
474                 if (AskQuestion(_("Cannot open specified file:"), 
475                                 MakeDisplayPath(s, 50),
476                                 _("Create new document with this name?")))
477                         {
478                                 // Find a free buffer
479                                 b = newFile(s, string());
480                         }
481                 break;
482         }
483
484         if (b && tolastfiles)
485                 lastfiles->newFile(b->fileName());
486
487         return b;
488 }