]> git.lyx.org Git - lyx.git/blob - src/Buffer.cpp
f6d8827d1a9f605a7585428365748e65455afac6
[lyx.git] / src / Buffer.cpp
1 /**
2  * \file Buffer.cpp
3  * This file is part of LyX, the document processor.
4  * Licence details can be found in the file COPYING.
5  *
6  * \author Lars Gullik Bjønnes
7  * \author Stefan Schimanski
8  *
9  * Full author contact details are available in file CREDITS.
10  */
11
12 #include <config.h>
13
14 #include "Buffer.h"
15
16 #include "Author.h"
17 #include "LayoutFile.h"
18 #include "BiblioInfo.h"
19 #include "BranchList.h"
20 #include "buffer_funcs.h"
21 #include "BufferList.h"
22 #include "BufferParams.h"
23 #include "Bullet.h"
24 #include "Chktex.h"
25 #include "Converter.h"
26 #include "Counters.h"
27 #include "DispatchResult.h"
28 #include "DocIterator.h"
29 #include "Encoding.h"
30 #include "ErrorList.h"
31 #include "Exporter.h"
32 #include "Format.h"
33 #include "FuncRequest.h"
34 #include "FuncStatus.h"
35 #include "IndicesList.h"
36 #include "InsetIterator.h"
37 #include "InsetList.h"
38 #include "Language.h"
39 #include "LaTeXFeatures.h"
40 #include "LaTeX.h"
41 #include "Layout.h"
42 #include "Lexer.h"
43 #include "LyXAction.h"
44 #include "LyX.h"
45 #include "LyXRC.h"
46 #include "LyXVC.h"
47 #include "output_docbook.h"
48 #include "output.h"
49 #include "output_latex.h"
50 #include "output_xhtml.h"
51 #include "output_plaintext.h"
52 #include "Paragraph.h"
53 #include "ParagraphParameters.h"
54 #include "ParIterator.h"
55 #include "PDFOptions.h"
56 #include "SpellChecker.h"
57 #include "sgml.h"
58 #include "TexRow.h"
59 #include "TexStream.h"
60 #include "Text.h"
61 #include "TextClass.h"
62 #include "TocBackend.h"
63 #include "Undo.h"
64 #include "VCBackend.h"
65 #include "version.h"
66 #include "WordLangTuple.h"
67 #include "WordList.h"
68
69 #include "insets/InsetBibitem.h"
70 #include "insets/InsetBibtex.h"
71 #include "insets/InsetBranch.h"
72 #include "insets/InsetInclude.h"
73 #include "insets/InsetText.h"
74
75 #include "mathed/MacroTable.h"
76 #include "mathed/MathMacroTemplate.h"
77 #include "mathed/MathSupport.h"
78
79 #include "frontends/alert.h"
80 #include "frontends/Delegates.h"
81 #include "frontends/WorkAreaManager.h"
82
83 #include "graphics/Previews.h"
84
85 #include "support/lassert.h"
86 #include "support/convert.h"
87 #include "support/debug.h"
88 #include "support/docstring_list.h"
89 #include "support/ExceptionMessage.h"
90 #include "support/FileName.h"
91 #include "support/FileNameList.h"
92 #include "support/filetools.h"
93 #include "support/ForkedCalls.h"
94 #include "support/gettext.h"
95 #include "support/gzstream.h"
96 #include "support/lstrings.h"
97 #include "support/lyxalgo.h"
98 #include "support/os.h"
99 #include "support/Package.h"
100 #include "support/Path.h"
101 #include "support/Systemcall.h"
102 #include "support/textutils.h"
103 #include "support/types.h"
104
105 #include <boost/bind.hpp>
106 #include <boost/shared_ptr.hpp>
107
108 #include <algorithm>
109 #include <fstream>
110 #include <iomanip>
111 #include <map>
112 #include <set>
113 #include <sstream>
114 #include <stack>
115 #include <vector>
116
117 using namespace std;
118 using namespace lyx::support;
119
120 namespace lyx {
121
122 namespace Alert = frontend::Alert;
123 namespace os = support::os;
124
125 namespace {
126
127 // Do not remove the comment below, so we get merge conflict in
128 // independent branches. Instead add your own.
129 int const LYX_FORMAT = 370; // uwestoehr: option to suppress default date
130
131 typedef map<string, bool> DepClean;
132 typedef map<docstring, pair<InsetLabel const *, Buffer::References> > RefCache;
133
134 void showPrintError(string const & name)
135 {
136         docstring str = bformat(_("Could not print the document %1$s.\n"
137                                             "Check that your printer is set up correctly."),
138                              makeDisplayPath(name, 50));
139         Alert::error(_("Print document failed"), str);
140 }
141
142 } // namespace anon
143
144 class BufferSet : public std::set<Buffer const *> {};
145
146 class Buffer::Impl
147 {
148 public:
149         Impl(Buffer & parent, FileName const & file, bool readonly);
150
151         ~Impl()
152         {
153                 if (wa_) {
154                         wa_->closeAll();
155                         delete wa_;
156                 }
157                 delete inset;
158         }
159
160         BufferParams params;
161         LyXVC lyxvc;
162         FileName temppath;
163         mutable TexRow texrow;
164
165         /// need to regenerate .tex?
166         DepClean dep_clean;
167
168         /// is save needed?
169         mutable bool lyx_clean;
170
171         /// is autosave needed?
172         mutable bool bak_clean;
173
174         /// is this a unnamed file (New...)?
175         bool unnamed;
176
177         /// buffer is r/o
178         bool read_only;
179
180         /// name of the file the buffer is associated with.
181         FileName filename;
182
183         /** Set to true only when the file is fully loaded.
184          *  Used to prevent the premature generation of previews
185          *  and by the citation inset.
186          */
187         bool file_fully_loaded;
188
189         ///
190         mutable TocBackend toc_backend;
191
192         /// macro tables
193         typedef pair<DocIterator, MacroData> ScopeMacro;
194         typedef map<DocIterator, ScopeMacro> PositionScopeMacroMap;
195         typedef map<docstring, PositionScopeMacroMap> NamePositionScopeMacroMap;
196         /// map from the macro name to the position map,
197         /// which maps the macro definition position to the scope and the MacroData.
198         NamePositionScopeMacroMap macros;
199         bool macro_lock;
200
201         /// positions of child buffers in the buffer
202         typedef map<Buffer const * const, DocIterator> BufferPositionMap;
203         typedef pair<DocIterator, Buffer const *> ScopeBuffer;
204         typedef map<DocIterator, ScopeBuffer> PositionScopeBufferMap;
205         /// position of children buffers in this buffer
206         BufferPositionMap children_positions;
207         /// map from children inclusion positions to their scope and their buffer
208         PositionScopeBufferMap position_to_children;
209
210         /// Container for all sort of Buffer dependant errors.
211         map<string, ErrorList> errorLists;
212
213         /// timestamp and checksum used to test if the file has been externally
214         /// modified. (Used to properly enable 'File->Revert to saved', bug 4114).
215         time_t timestamp_;
216         unsigned long checksum_;
217
218         ///
219         frontend::WorkAreaManager * wa_;
220
221         ///
222         Undo undo_;
223
224         /// A cache for the bibfiles (including bibfiles of loaded child
225         /// documents), needed for appropriate update of natbib labels.
226         mutable support::FileNameList bibfilesCache_;
227
228         // FIXME The caching mechanism could be improved. At present, we have a
229         // cache for each Buffer, that caches all the bibliography info for that
230         // Buffer. A more efficient solution would be to have a global cache per
231         // file, and then to construct the Buffer's bibinfo from that.
232         /// A cache for bibliography info
233         mutable BiblioInfo bibinfo_;
234         /// whether the bibinfo cache is valid
235         bool bibinfoCacheValid_;
236         /// Cache of timestamps of .bib files
237         map<FileName, time_t> bibfileStatus_;
238
239         mutable RefCache ref_cache_;
240
241         /// our Text that should be wrapped in an InsetText
242         InsetText * inset;
243
244         /// This is here to force the test to be done whenever parent_buffer
245         /// is accessed.
246         Buffer const * parent() const { 
247                 // if parent_buffer is not loaded, then it has been unloaded,
248                 // which means that parent_buffer is an invalid pointer. So we
249                 // set it to null in that case.
250                 if (!theBufferList().isLoaded(parent_buffer))
251                         parent_buffer = 0;
252                 return parent_buffer; 
253         }
254         ///
255         void setParent(Buffer const * pb) { parent_buffer = pb; }
256 private:
257         /// So we can force access via the accessors.
258         mutable Buffer const * parent_buffer;
259 };
260
261
262 /// Creates the per buffer temporary directory
263 static FileName createBufferTmpDir()
264 {
265         static int count;
266         // We are in our own directory.  Why bother to mangle name?
267         // In fact I wrote this code to circumvent a problematic behaviour
268         // (bug?) of EMX mkstemp().
269         FileName tmpfl(package().temp_dir().absFilename() + "/lyx_tmpbuf" +
270                 convert<string>(count++));
271
272         if (!tmpfl.createDirectory(0777)) {
273                 throw ExceptionMessage(WarningException, _("Disk Error: "), bformat(
274                         _("LyX could not create the temporary directory '%1$s' (Disk is full maybe?)"),
275                         from_utf8(tmpfl.absFilename())));
276         }
277         return tmpfl;
278 }
279
280
281 Buffer::Impl::Impl(Buffer & parent, FileName const & file, bool readonly_)
282         : lyx_clean(true), bak_clean(true), unnamed(false),
283           read_only(readonly_), filename(file), file_fully_loaded(false),
284           toc_backend(&parent), macro_lock(false), timestamp_(0),
285           checksum_(0), wa_(0), undo_(parent), bibinfoCacheValid_(false),
286           parent_buffer(0)
287 {
288         temppath = createBufferTmpDir();
289         lyxvc.setBuffer(&parent);
290         if (use_gui)
291                 wa_ = new frontend::WorkAreaManager;
292 }
293
294
295 Buffer::Buffer(string const & file, bool readonly)
296         : d(new Impl(*this, FileName(file), readonly)), gui_(0)
297 {
298         LYXERR(Debug::INFO, "Buffer::Buffer()");
299
300         d->inset = new InsetText(*this);
301         d->inset->setAutoBreakRows(true);
302         d->inset->getText(0)->setMacrocontextPosition(par_iterator_begin());
303 }
304
305
306 Buffer::~Buffer()
307 {
308         LYXERR(Debug::INFO, "Buffer::~Buffer()");
309         // here the buffer should take care that it is
310         // saved properly, before it goes into the void.
311
312         // GuiView already destroyed
313         gui_ = 0;
314
315         if (d->unnamed && d->filename.extension() == "internal") {
316                 // No need to do additional cleanups for internal buffer.
317                 delete d;
318                 return;
319         }
320
321         // loop over children
322         Impl::BufferPositionMap::iterator it = d->children_positions.begin();
323         Impl::BufferPositionMap::iterator end = d->children_positions.end();
324         for (; it != end; ++it) {
325                 Buffer * child = const_cast<Buffer *>(it->first);
326                 // The child buffer might have been closed already.
327                 if (theBufferList().isLoaded(child))
328                         theBufferList().releaseChild(this, child);
329         }
330         
331         // clear references to children in macro tables
332         d->children_positions.clear();
333         d->position_to_children.clear();
334
335         if (!d->temppath.destroyDirectory()) {
336                 Alert::warning(_("Could not remove temporary directory"),
337                         bformat(_("Could not remove the temporary directory %1$s"),
338                         from_utf8(d->temppath.absFilename())));
339         }
340
341         // Remove any previewed LaTeX snippets associated with this buffer.
342         thePreviews().removeLoader(*this);
343
344         delete d;
345 }
346
347
348 void Buffer::changed() const
349 {
350         if (d->wa_)
351                 d->wa_->redrawAll();
352 }
353
354
355 frontend::WorkAreaManager & Buffer::workAreaManager() const
356 {
357         LASSERT(d->wa_, /**/);
358         return *d->wa_;
359 }
360
361
362 Text & Buffer::text() const
363 {
364         return d->inset->text();
365 }
366
367
368 Inset & Buffer::inset() const
369 {
370         return *d->inset;
371 }
372
373
374 BufferParams & Buffer::params()
375 {
376         return d->params;
377 }
378
379
380 BufferParams const & Buffer::params() const
381 {
382         return d->params;
383 }
384
385
386 ParagraphList & Buffer::paragraphs()
387 {
388         return text().paragraphs();
389 }
390
391
392 ParagraphList const & Buffer::paragraphs() const
393 {
394         return text().paragraphs();
395 }
396
397
398 LyXVC & Buffer::lyxvc()
399 {
400         return d->lyxvc;
401 }
402
403
404 LyXVC const & Buffer::lyxvc() const
405 {
406         return d->lyxvc;
407 }
408
409
410 string const Buffer::temppath() const
411 {
412         return d->temppath.absFilename();
413 }
414
415
416 TexRow & Buffer::texrow()
417 {
418         return d->texrow;
419 }
420
421
422 TexRow const & Buffer::texrow() const
423 {
424         return d->texrow;
425 }
426
427
428 TocBackend & Buffer::tocBackend() const
429 {
430         return d->toc_backend;
431 }
432
433
434 Undo & Buffer::undo()
435 {
436         return d->undo_;
437 }
438
439
440 string Buffer::latexName(bool const no_path) const
441 {
442         FileName latex_name =
443                 makeLatexName(exportFileName());
444         return no_path ? latex_name.onlyFileName()
445                 : latex_name.absFilename();
446 }
447
448
449 FileName Buffer::exportFileName() const
450 {
451         docstring const branch_suffix =
452                 params().branchlist().getFilenameSuffix();
453         if (branch_suffix.empty())
454                 return fileName();
455
456         string const name = fileName().onlyFileNameWithoutExt()
457                 + to_utf8(branch_suffix);
458         FileName res(fileName().onlyPath().absFilename() + "/" + name);
459         res.changeExtension(fileName().extension());
460
461         return res;
462 }
463
464
465 string Buffer::logName(LogType * type) const
466 {
467         string const filename = latexName(false);
468
469         if (filename.empty()) {
470                 if (type)
471                         *type = latexlog;
472                 return string();
473         }
474
475         string const path = temppath();
476
477         FileName const fname(addName(temppath(),
478                                      onlyFilename(changeExtension(filename,
479                                                                   ".log"))));
480
481         // FIXME: how do we know this is the name of the build log?
482         FileName const bname(
483                 addName(path, onlyFilename(
484                         changeExtension(filename,
485                                         formats.extension(bufferFormat()) + ".out"))));
486
487         // Also consider the master buffer log file
488         FileName masterfname = fname;
489         LogType mtype;
490         if (masterBuffer() != this) {
491                 string const mlogfile = masterBuffer()->logName(&mtype);
492                 masterfname = FileName(mlogfile);
493         }
494
495         // If no Latex log or Build log is newer, show Build log
496         if (bname.exists() &&
497             ((!fname.exists() && !masterfname.exists())
498              || (fname.lastModified() < bname.lastModified()
499                  && masterfname.lastModified() < bname.lastModified()))) {
500                 LYXERR(Debug::FILES, "Log name calculated as: " << bname);
501                 if (type)
502                         *type = buildlog;
503                 return bname.absFilename();
504         // If we have a newer master file log or only a master log, show this
505         } else if (fname != masterfname
506                    && (!fname.exists() && (masterfname.exists()
507                    || fname.lastModified() < masterfname.lastModified()))) {
508                 LYXERR(Debug::FILES, "Log name calculated as: " << masterfname);
509                 if (type)
510                         *type = mtype;
511                 return masterfname.absFilename();
512         }
513         LYXERR(Debug::FILES, "Log name calculated as: " << fname);
514         if (type)
515                         *type = latexlog;
516         return fname.absFilename();
517 }
518
519
520 void Buffer::setReadonly(bool const flag)
521 {
522         if (d->read_only != flag) {
523                 d->read_only = flag;
524                 setReadOnly(flag);
525         }
526 }
527
528
529 void Buffer::setFileName(string const & newfile)
530 {
531         d->filename = makeAbsPath(newfile);
532         setReadonly(d->filename.isReadOnly());
533         updateTitles();
534 }
535
536
537 int Buffer::readHeader(Lexer & lex)
538 {
539         int unknown_tokens = 0;
540         int line = -1;
541         int begin_header_line = -1;
542
543         // Initialize parameters that may be/go lacking in header:
544         params().branchlist().clear();
545         params().preamble.erase();
546         params().options.erase();
547         params().master.erase();
548         params().float_placement.erase();
549         params().paperwidth.erase();
550         params().paperheight.erase();
551         params().leftmargin.erase();
552         params().rightmargin.erase();
553         params().topmargin.erase();
554         params().bottommargin.erase();
555         params().headheight.erase();
556         params().headsep.erase();
557         params().footskip.erase();
558         params().columnsep.erase();
559         params().fontsCJK.erase();
560         params().listings_params.clear();
561         params().clearLayoutModules();
562         params().clearRemovedModules();
563         params().pdfoptions().clear();
564         params().indiceslist().clear();
565         params().backgroundcolor = lyx::rgbFromHexName("#ffffff");
566
567         for (int i = 0; i < 4; ++i) {
568                 params().user_defined_bullet(i) = ITEMIZE_DEFAULTS[i];
569                 params().temp_bullet(i) = ITEMIZE_DEFAULTS[i];
570         }
571
572         ErrorList & errorList = d->errorLists["Parse"];
573
574         while (lex.isOK()) {
575                 string token;
576                 lex >> token;
577
578                 if (token.empty())
579                         continue;
580
581                 if (token == "\\end_header")
582                         break;
583
584                 ++line;
585                 if (token == "\\begin_header") {
586                         begin_header_line = line;
587                         continue;
588                 }
589
590                 LYXERR(Debug::PARSER, "Handling document header token: `"
591                                       << token << '\'');
592
593                 string unknown = params().readToken(lex, token, d->filename.onlyPath());
594                 if (!unknown.empty()) {
595                         if (unknown[0] != '\\' && token == "\\textclass") {
596                                 Alert::warning(_("Unknown document class"),
597                        bformat(_("Using the default document class, because the "
598                                               "class %1$s is unknown."), from_utf8(unknown)));
599                         } else {
600                                 ++unknown_tokens;
601                                 docstring const s = bformat(_("Unknown token: "
602                                                                         "%1$s %2$s\n"),
603                                                          from_utf8(token),
604                                                          lex.getDocString());
605                                 errorList.push_back(ErrorItem(_("Document header error"),
606                                         s, -1, 0, 0));
607                         }
608                 }
609         }
610         if (begin_header_line) {
611                 docstring const s = _("\\begin_header is missing");
612                 errorList.push_back(ErrorItem(_("Document header error"),
613                         s, -1, 0, 0));
614         }
615
616         params().makeDocumentClass();
617
618         return unknown_tokens;
619 }
620
621
622 // Uwe C. Schroeder
623 // changed to be public and have one parameter
624 // Returns true if "\end_document" is not read (Asger)
625 bool Buffer::readDocument(Lexer & lex)
626 {
627         ErrorList & errorList = d->errorLists["Parse"];
628         errorList.clear();
629
630         if (!lex.checkFor("\\begin_document")) {
631                 docstring const s = _("\\begin_document is missing");
632                 errorList.push_back(ErrorItem(_("Document header error"),
633                         s, -1, 0, 0));
634         }
635
636         // we are reading in a brand new document
637         LASSERT(paragraphs().empty(), /**/);
638
639         readHeader(lex);
640
641         if (params().outputChanges) {
642                 bool dvipost    = LaTeXFeatures::isAvailable("dvipost");
643                 bool xcolorulem = LaTeXFeatures::isAvailable("ulem") &&
644                                   LaTeXFeatures::isAvailable("xcolor");
645
646                 if (!dvipost && !xcolorulem) {
647                         Alert::warning(_("Changes not shown in LaTeX output"),
648                                        _("Changes will not be highlighted in LaTeX output, "
649                                          "because neither dvipost nor xcolor/ulem are installed.\n"
650                                          "Please install these packages or redefine "
651                                          "\\lyxadded and \\lyxdeleted in the LaTeX preamble."));
652                 } else if (!xcolorulem) {
653                         Alert::warning(_("Changes not shown in LaTeX output"),
654                                        _("Changes will not be highlighted in LaTeX output "
655                                          "when using pdflatex, because xcolor and ulem are not installed.\n"
656                                          "Please install both packages or redefine "
657                                          "\\lyxadded and \\lyxdeleted in the LaTeX preamble."));
658                 }
659         }
660
661         if (!params().master.empty()) {
662                 FileName const master_file = makeAbsPath(params().master,
663                            onlyPath(absFileName()));
664                 if (isLyXFilename(master_file.absFilename())) {
665                         Buffer * master = 
666                                 checkAndLoadLyXFile(master_file, true);
667                         if (master) {
668                                 // necessary e.g. after a reload
669                                 // to re-register the child (bug 5873)
670                                 // FIXME: clean up updateMacros (here, only
671                                 // child registering is needed).
672                                 master->updateMacros();
673                                 // set master as master buffer, but only
674                                 // if we are a real child
675                                 if (master->isChild(this))
676                                         setParent(master);
677                                 // if the master is not fully loaded
678                                 // it is probably just loading this
679                                 // child. No warning needed then.
680                                 else if (master->isFullyLoaded())
681                                         LYXERR0("The master '"
682                                                 << params().master
683                                                 << "' assigned to this document ("
684                                                 << absFileName()
685                                                 << ") does not include "
686                                                 "this document. Ignoring the master assignment.");
687                         }
688                 }
689         }
690
691         // read main text
692         bool const res = text().read(lex, errorList, d->inset);
693
694         updateMacros();
695         updateMacroInstances();
696         return res;
697 }
698
699
700 bool Buffer::readString(string const & s)
701 {
702         params().compressed = false;
703
704         // remove dummy empty par
705         paragraphs().clear();
706         Lexer lex;
707         istringstream is(s);
708         lex.setStream(is);
709         FileName const name = FileName::tempName("Buffer_readString");
710         switch (readFile(lex, name, true)) {
711         case failure:
712                 return false;
713         case wrongversion: {
714                 // We need to call lyx2lyx, so write the input to a file
715                 ofstream os(name.toFilesystemEncoding().c_str());
716                 os << s;
717                 os.close();
718                 return readFile(name);
719         }
720         case success:
721                 break;
722         }
723
724         return true;
725 }
726
727
728 bool Buffer::readFile(FileName const & filename)
729 {
730         FileName fname(filename);
731
732         params().compressed = fname.isZippedFile();
733
734         // remove dummy empty par
735         paragraphs().clear();
736         Lexer lex;
737         lex.setFile(fname);
738         if (readFile(lex, fname) != success)
739                 return false;
740
741         return true;
742 }
743
744
745 bool Buffer::isFullyLoaded() const
746 {
747         return d->file_fully_loaded;
748 }
749
750
751 void Buffer::setFullyLoaded(bool value)
752 {
753         d->file_fully_loaded = value;
754 }
755
756
757 Buffer::ReadStatus Buffer::readFile(Lexer & lex, FileName const & filename,
758                 bool fromstring)
759 {
760         LASSERT(!filename.empty(), /**/);
761
762         // the first (non-comment) token _must_ be...
763         if (!lex.checkFor("\\lyxformat")) {
764                 Alert::error(_("Document format failure"),
765                              bformat(_("%1$s is not a readable LyX document."),
766                                        from_utf8(filename.absFilename())));
767                 return failure;
768         }
769
770         string tmp_format;
771         lex >> tmp_format;
772         //lyxerr << "LyX Format: `" << tmp_format << '\'' << endl;
773         // if present remove ".," from string.
774         size_t dot = tmp_format.find_first_of(".,");
775         //lyxerr << "           dot found at " << dot << endl;
776         if (dot != string::npos)
777                         tmp_format.erase(dot, 1);
778         int const file_format = convert<int>(tmp_format);
779         //lyxerr << "format: " << file_format << endl;
780
781         // save timestamp and checksum of the original disk file, making sure
782         // to not overwrite them with those of the file created in the tempdir
783         // when it has to be converted to the current format.
784         if (!d->checksum_) {
785                 // Save the timestamp and checksum of disk file. If filename is an
786                 // emergency file, save the timestamp and checksum of the original lyx file
787                 // because isExternallyModified will check for this file. (BUG4193)
788                 string diskfile = filename.absFilename();
789                 if (suffixIs(diskfile, ".emergency"))
790                         diskfile = diskfile.substr(0, diskfile.size() - 10);
791                 saveCheckSum(FileName(diskfile));
792         }
793
794         if (file_format != LYX_FORMAT) {
795
796                 if (fromstring)
797                         // lyx2lyx would fail
798                         return wrongversion;
799
800                 FileName const tmpfile = FileName::tempName("Buffer_readFile");
801                 if (tmpfile.empty()) {
802                         Alert::error(_("Conversion failed"),
803                                      bformat(_("%1$s is from a different"
804                                               " version of LyX, but a temporary"
805                                               " file for converting it could"
806                                               " not be created."),
807                                               from_utf8(filename.absFilename())));
808                         return failure;
809                 }
810                 FileName const lyx2lyx = libFileSearch("lyx2lyx", "lyx2lyx");
811                 if (lyx2lyx.empty()) {
812                         Alert::error(_("Conversion script not found"),
813                                      bformat(_("%1$s is from a different"
814                                                " version of LyX, but the"
815                                                " conversion script lyx2lyx"
816                                                " could not be found."),
817                                                from_utf8(filename.absFilename())));
818                         return failure;
819                 }
820                 ostringstream command;
821                 command << os::python()
822                         << ' ' << quoteName(lyx2lyx.toFilesystemEncoding())
823                         << " -t " << convert<string>(LYX_FORMAT)
824                         << " -o " << quoteName(tmpfile.toFilesystemEncoding())
825                         << ' ' << quoteName(filename.toFilesystemEncoding());
826                 string const command_str = command.str();
827
828                 LYXERR(Debug::INFO, "Running '" << command_str << '\'');
829
830                 cmd_ret const ret = runCommand(command_str);
831                 if (ret.first != 0) {
832                         Alert::error(_("Conversion script failed"),
833                                      bformat(_("%1$s is from a different version"
834                                               " of LyX, but the lyx2lyx script"
835                                               " failed to convert it."),
836                                               from_utf8(filename.absFilename())));
837                         return failure;
838                 } else {
839                         bool const ret = readFile(tmpfile);
840                         // Do stuff with tmpfile name and buffer name here.
841                         return ret ? success : failure;
842                 }
843
844         }
845
846         if (readDocument(lex)) {
847                 Alert::error(_("Document format failure"),
848                              bformat(_("%1$s ended unexpectedly, which means"
849                                                     " that it is probably corrupted."),
850                                        from_utf8(filename.absFilename())));
851         }
852
853         d->file_fully_loaded = true;
854         return success;
855 }
856
857
858 // Should probably be moved to somewhere else: BufferView? LyXView?
859 bool Buffer::save() const
860 {
861         // We don't need autosaves in the immediate future. (Asger)
862         resetAutosaveTimers();
863
864         string const encodedFilename = d->filename.toFilesystemEncoding();
865
866         FileName backupName;
867         bool madeBackup = false;
868
869         // make a backup if the file already exists
870         if (lyxrc.make_backup && fileName().exists()) {
871                 backupName = FileName(absFileName() + '~');
872                 if (!lyxrc.backupdir_path.empty()) {
873                         string const mangledName =
874                                 subst(subst(backupName.absFilename(), '/', '!'), ':', '!');
875                         backupName = FileName(addName(lyxrc.backupdir_path,
876                                                       mangledName));
877                 }
878                 if (fileName().copyTo(backupName)) {
879                         madeBackup = true;
880                 } else {
881                         Alert::error(_("Backup failure"),
882                                      bformat(_("Cannot create backup file %1$s.\n"
883                                                "Please check whether the directory exists and is writeable."),
884                                              from_utf8(backupName.absFilename())));
885                         //LYXERR(Debug::DEBUG, "Fs error: " << fe.what());
886                 }
887         }
888
889         // ask if the disk file has been externally modified (use checksum method)
890         if (fileName().exists() && isExternallyModified(checksum_method)) {
891                 docstring const file = makeDisplayPath(absFileName(), 20);
892                 docstring text = bformat(_("Document %1$s has been externally modified. Are you sure "
893                                                              "you want to overwrite this file?"), file);
894                 int const ret = Alert::prompt(_("Overwrite modified file?"),
895                         text, 1, 1, _("&Overwrite"), _("&Cancel"));
896                 if (ret == 1)
897                         return false;
898         }
899
900         if (writeFile(d->filename)) {
901                 markClean();
902                 return true;
903         } else {
904                 // Saving failed, so backup is not backup
905                 if (madeBackup)
906                         backupName.moveTo(d->filename);
907                 return false;
908         }
909 }
910
911
912 bool Buffer::writeFile(FileName const & fname) const
913 {
914         if (d->read_only && fname == d->filename)
915                 return false;
916
917         bool retval = false;
918
919         docstring const str = bformat(_("Saving document %1$s..."),
920                 makeDisplayPath(fname.absFilename()));
921         message(str);
922
923         if (params().compressed) {
924                 gz::ogzstream ofs(fname.toFilesystemEncoding().c_str(), ios::out|ios::trunc);
925                 retval = ofs && write(ofs);
926         } else {
927                 ofstream ofs(fname.toFilesystemEncoding().c_str(), ios::out|ios::trunc);
928                 retval = ofs && write(ofs);
929         }
930
931         if (!retval) {
932                 message(str + _(" could not write file!"));
933                 return false;
934         }
935
936         removeAutosaveFile();
937
938         saveCheckSum(d->filename);
939         message(str + _(" done."));
940
941         return true;
942 }
943
944
945 docstring Buffer::emergencyWrite()
946 {
947         // No need to save if the buffer has not changed.
948         if (isClean())
949                 return docstring();
950
951         string const doc = isUnnamed() ? onlyFilename(absFileName()) : absFileName();
952
953         docstring user_message = bformat(
954                 _("LyX: Attempting to save document %1$s\n"), from_utf8(doc));
955
956         // We try to save three places:
957         // 1) Same place as document. Unless it is an unnamed doc.
958         if (!isUnnamed()) {
959                 string s = absFileName();
960                 s += ".emergency";
961                 LYXERR0("  " << s);
962                 if (writeFile(FileName(s))) {
963                         markClean();
964                         user_message += bformat(_("  Saved to %1$. Phew.\n"), from_utf8(s));
965                         return user_message;
966                 } else {
967                         user_message += _("  Save failed! Trying again...\n");
968                 }
969         }
970
971         // 2) In HOME directory.
972         string s = addName(package().home_dir().absFilename(), absFileName());
973         s += ".emergency";
974         lyxerr << ' ' << s << endl;
975         if (writeFile(FileName(s))) {
976                 markClean();
977                 user_message += bformat(_("  Saved to %1$. Phew.\n"), from_utf8(s));
978                 return user_message;
979         }
980
981         user_message += _("  Save failed! Trying yet again...\n");
982
983         // 3) In "/tmp" directory.
984         // MakeAbsPath to prepend the current
985         // drive letter on OS/2
986         s = addName(package().temp_dir().absFilename(), absFileName());
987         s += ".emergency";
988         lyxerr << ' ' << s << endl;
989         if (writeFile(FileName(s))) {
990                 markClean();
991                 user_message += bformat(_("  Saved to %1$. Phew.\n"), from_utf8(s));
992                 return user_message;
993         }
994
995         user_message += _("  Save failed! Bummer. Document is lost.");
996         // Don't try again.
997         markClean();
998         return user_message;
999 }
1000
1001
1002 bool Buffer::write(ostream & ofs) const
1003 {
1004 #ifdef HAVE_LOCALE
1005         // Use the standard "C" locale for file output.
1006         ofs.imbue(locale::classic());
1007 #endif
1008
1009         // The top of the file should not be written by params().
1010
1011         // write out a comment in the top of the file
1012         ofs << "#LyX " << lyx_version
1013             << " created this file. For more info see http://www.lyx.org/\n"
1014             << "\\lyxformat " << LYX_FORMAT << "\n"
1015             << "\\begin_document\n";
1016
1017         /// For each author, set 'used' to true if there is a change
1018         /// by this author in the document; otherwise set it to 'false'.
1019         AuthorList::Authors::const_iterator a_it = params().authors().begin();
1020         AuthorList::Authors::const_iterator a_end = params().authors().end();
1021         for (; a_it != a_end; ++a_it)
1022                 a_it->setUsed(false);
1023
1024         ParIterator const end = const_cast<Buffer *>(this)->par_iterator_end();
1025         ParIterator it = const_cast<Buffer *>(this)->par_iterator_begin();
1026         for ( ; it != end; ++it)
1027                 it->checkAuthors(params().authors());
1028
1029         // now write out the buffer parameters.
1030         ofs << "\\begin_header\n";
1031         params().writeFile(ofs);
1032         ofs << "\\end_header\n";
1033
1034         // write the text
1035         ofs << "\n\\begin_body\n";
1036         text().write(ofs);
1037         ofs << "\n\\end_body\n";
1038
1039         // Write marker that shows file is complete
1040         ofs << "\\end_document" << endl;
1041
1042         // Shouldn't really be needed....
1043         //ofs.close();
1044
1045         // how to check if close went ok?
1046         // Following is an attempt... (BE 20001011)
1047
1048         // good() returns false if any error occured, including some
1049         //        formatting error.
1050         // bad()  returns true if something bad happened in the buffer,
1051         //        which should include file system full errors.
1052
1053         bool status = true;
1054         if (!ofs) {
1055                 status = false;
1056                 lyxerr << "File was not closed properly." << endl;
1057         }
1058
1059         return status;
1060 }
1061
1062
1063 bool Buffer::makeLaTeXFile(FileName const & fname,
1064                            string const & original_path,
1065                            OutputParams const & runparams_in,
1066                            bool output_preamble, bool output_body) const
1067 {
1068         OutputParams runparams = runparams_in;
1069         if (params().useXetex)
1070                 runparams.flavor = OutputParams::XETEX;
1071
1072         string const encoding = runparams.encoding->iconvName();
1073         LYXERR(Debug::LATEX, "makeLaTeXFile encoding: " << encoding << "...");
1074
1075         ofdocstream ofs;
1076         try { ofs.reset(encoding); }
1077         catch (iconv_codecvt_facet_exception & e) {
1078                 lyxerr << "Caught iconv exception: " << e.what() << endl;
1079                 Alert::error(_("Iconv software exception Detected"), bformat(_("Please "
1080                         "verify that the support software for your encoding (%1$s) is "
1081                         "properly installed"), from_ascii(encoding)));
1082                 return false;
1083         }
1084         if (!openFileWrite(ofs, fname))
1085                 return false;
1086
1087         //TexStream ts(ofs.rdbuf(), &texrow());
1088         ErrorList & errorList = d->errorLists["Export"];
1089         errorList.clear();
1090         bool failed_export = false;
1091         try {
1092                 d->texrow.reset();
1093                 writeLaTeXSource(ofs, original_path,
1094                       runparams, output_preamble, output_body);
1095         }
1096         catch (EncodingException & e) {
1097                 odocstringstream ods;
1098                 ods.put(e.failed_char);
1099                 ostringstream oss;
1100                 oss << "0x" << hex << e.failed_char << dec;
1101                 docstring msg = bformat(_("Could not find LaTeX command for character '%1$s'"
1102                                           " (code point %2$s)"),
1103                                           ods.str(), from_utf8(oss.str()));
1104                 errorList.push_back(ErrorItem(msg, _("Some characters of your document are probably not "
1105                                 "representable in the chosen encoding.\n"
1106                                 "Changing the document encoding to utf8 could help."),
1107                                 e.par_id, e.pos, e.pos + 1));
1108                 failed_export = true;
1109         }
1110         catch (iconv_codecvt_facet_exception & e) {
1111                 errorList.push_back(ErrorItem(_("iconv conversion failed"),
1112                         _(e.what()), -1, 0, 0));
1113                 failed_export = true;
1114         }
1115         catch (exception const & e) {
1116                 errorList.push_back(ErrorItem(_("conversion failed"),
1117                         _(e.what()), -1, 0, 0));
1118                 failed_export = true;
1119         }
1120         catch (...) {
1121                 lyxerr << "Caught some really weird exception..." << endl;
1122                 lyx_exit(1);
1123         }
1124
1125         ofs.close();
1126         if (ofs.fail()) {
1127                 failed_export = true;
1128                 lyxerr << "File '" << fname << "' was not closed properly." << endl;
1129         }
1130
1131         errors("Export");
1132         return !failed_export;
1133 }
1134
1135
1136 void Buffer::writeLaTeXSource(odocstream & os,
1137                            string const & original_path,
1138                            OutputParams const & runparams_in,
1139                            bool const output_preamble, bool const output_body) const
1140 {
1141         // The child documents, if any, shall be already loaded at this point.
1142
1143         OutputParams runparams = runparams_in;
1144
1145         // Classify the unicode characters appearing in math insets
1146         Encodings::initUnicodeMath(*this);
1147
1148         // validate the buffer.
1149         LYXERR(Debug::LATEX, "  Validating buffer...");
1150         LaTeXFeatures features(*this, params(), runparams);
1151         validate(features);
1152         LYXERR(Debug::LATEX, "  Buffer validation done.");
1153
1154         // The starting paragraph of the coming rows is the
1155         // first paragraph of the document. (Asger)
1156         if (output_preamble && runparams.nice) {
1157                 os << "%% LyX " << lyx_version << " created this file.  "
1158                         "For more info, see http://www.lyx.org/.\n"
1159                         "%% Do not edit unless you really know what "
1160                         "you are doing.\n";
1161                 d->texrow.newline();
1162                 d->texrow.newline();
1163         }
1164         LYXERR(Debug::INFO, "lyx document header finished");
1165
1166         // Don't move this behind the parent_buffer=0 code below,
1167         // because then the macros will not get the right "redefinition"
1168         // flag as they don't see the parent macros which are output before.
1169         updateMacros();
1170
1171         // fold macros if possible, still with parent buffer as the
1172         // macros will be put in the prefix anyway.
1173         updateMacroInstances();
1174
1175         // There are a few differences between nice LaTeX and usual files:
1176         // usual is \batchmode and has a
1177         // special input@path to allow the including of figures
1178         // with either \input or \includegraphics (what figinsets do).
1179         // input@path is set when the actual parameter
1180         // original_path is set. This is done for usual tex-file, but not
1181         // for nice-latex-file. (Matthias 250696)
1182         // Note that input@path is only needed for something the user does
1183         // in the preamble, included .tex files or ERT, files included by
1184         // LyX work without it.
1185         if (output_preamble) {
1186                 if (!runparams.nice) {
1187                         // code for usual, NOT nice-latex-file
1188                         os << "\\batchmode\n"; // changed
1189                         // from \nonstopmode
1190                         d->texrow.newline();
1191                 }
1192                 if (!original_path.empty()) {
1193                         // FIXME UNICODE
1194                         // We don't know the encoding of inputpath
1195                         docstring const inputpath = from_utf8(latex_path(original_path));
1196                         os << "\\makeatletter\n"
1197                            << "\\def\\input@path{{"
1198                            << inputpath << "/}}\n"
1199                            << "\\makeatother\n";
1200                         d->texrow.newline();
1201                         d->texrow.newline();
1202                         d->texrow.newline();
1203                 }
1204
1205                 // get parent macros (if this buffer has a parent) which will be
1206                 // written at the document begin further down.
1207                 MacroSet parentMacros;
1208                 listParentMacros(parentMacros, features);
1209
1210                 // Write the preamble
1211                 runparams.use_babel = params().writeLaTeX(os, features, d->texrow);
1212
1213                 runparams.use_japanese = features.isRequired("japanese");
1214
1215                 if (!output_body)
1216                         return;
1217
1218                 // make the body.
1219                 os << "\\begin{document}\n";
1220                 d->texrow.newline();
1221
1222                 // output the parent macros
1223                 MacroSet::iterator it = parentMacros.begin();
1224                 MacroSet::iterator end = parentMacros.end();
1225                 for (; it != end; ++it)
1226                         (*it)->write(os, true);
1227         } // output_preamble
1228
1229         d->texrow.start(paragraphs().begin()->id(), 0);
1230
1231         LYXERR(Debug::INFO, "preamble finished, now the body.");
1232
1233         // if we are doing a real file with body, even if this is the
1234         // child of some other buffer, let's cut the link here.
1235         // This happens for example if only a child document is printed.
1236         Buffer const * save_parent = 0;
1237         if (output_preamble) {
1238                 save_parent = d->parent();
1239                 d->setParent(0);
1240         }
1241
1242         // the real stuff
1243         latexParagraphs(*this, text(), os, d->texrow, runparams);
1244
1245         // Restore the parenthood if needed
1246         if (output_preamble)
1247                 d->setParent(save_parent);
1248
1249         // add this just in case after all the paragraphs
1250         os << endl;
1251         d->texrow.newline();
1252
1253         if (output_preamble) {
1254                 os << "\\end{document}\n";
1255                 d->texrow.newline();
1256                 LYXERR(Debug::LATEX, "makeLaTeXFile...done");
1257         } else {
1258                 LYXERR(Debug::LATEX, "LaTeXFile for inclusion made.");
1259         }
1260         runparams_in.encoding = runparams.encoding;
1261
1262         // Just to be sure. (Asger)
1263         d->texrow.newline();
1264
1265         LYXERR(Debug::INFO, "Finished making LaTeX file.");
1266         LYXERR(Debug::INFO, "Row count was " << d->texrow.rows() - 1 << '.');
1267 }
1268
1269
1270 bool Buffer::isLatex() const
1271 {
1272         return params().documentClass().outputType() == LATEX;
1273 }
1274
1275
1276 bool Buffer::isLiterate() const
1277 {
1278         return params().documentClass().outputType() == LITERATE;
1279 }
1280
1281
1282 bool Buffer::isDocBook() const
1283 {
1284         return params().documentClass().outputType() == DOCBOOK;
1285 }
1286
1287
1288 void Buffer::makeDocBookFile(FileName const & fname,
1289                               OutputParams const & runparams,
1290                               bool const body_only) const
1291 {
1292         LYXERR(Debug::LATEX, "makeDocBookFile...");
1293
1294         ofdocstream ofs;
1295         if (!openFileWrite(ofs, fname))
1296                 return;
1297
1298         writeDocBookSource(ofs, fname.absFilename(), runparams, body_only);
1299
1300         ofs.close();
1301         if (ofs.fail())
1302                 lyxerr << "File '" << fname << "' was not closed properly." << endl;
1303 }
1304
1305
1306 void Buffer::writeDocBookSource(odocstream & os, string const & fname,
1307                              OutputParams const & runparams,
1308                              bool const only_body) const
1309 {
1310         LaTeXFeatures features(*this, params(), runparams);
1311         validate(features);
1312
1313         d->texrow.reset();
1314
1315         DocumentClass const & tclass = params().documentClass();
1316         string const top_element = tclass.latexname();
1317
1318         if (!only_body) {
1319                 if (runparams.flavor == OutputParams::XML)
1320                         os << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
1321
1322                 // FIXME UNICODE
1323                 os << "<!DOCTYPE " << from_ascii(top_element) << ' ';
1324
1325                 // FIXME UNICODE
1326                 if (! tclass.class_header().empty())
1327                         os << from_ascii(tclass.class_header());
1328                 else if (runparams.flavor == OutputParams::XML)
1329                         os << "PUBLIC \"-//OASIS//DTD DocBook XML//EN\" "
1330                             << "\"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd\"";
1331                 else
1332                         os << " PUBLIC \"-//OASIS//DTD DocBook V4.2//EN\"";
1333
1334                 docstring preamble = from_utf8(params().preamble);
1335                 if (runparams.flavor != OutputParams::XML ) {
1336                         preamble += "<!ENTITY % output.print.png \"IGNORE\">\n";
1337                         preamble += "<!ENTITY % output.print.pdf \"IGNORE\">\n";
1338                         preamble += "<!ENTITY % output.print.eps \"IGNORE\">\n";
1339                         preamble += "<!ENTITY % output.print.bmp \"IGNORE\">\n";
1340                 }
1341
1342                 string const name = runparams.nice
1343                         ? changeExtension(absFileName(), ".sgml") : fname;
1344                 preamble += features.getIncludedFiles(name);
1345                 preamble += features.getLyXSGMLEntities();
1346
1347                 if (!preamble.empty()) {
1348                         os << "\n [ " << preamble << " ]";
1349                 }
1350                 os << ">\n\n";
1351         }
1352
1353         string top = top_element;
1354         top += " lang=\"";
1355         if (runparams.flavor == OutputParams::XML)
1356                 top += params().language->code();
1357         else
1358                 top += params().language->code().substr(0, 2);
1359         top += '"';
1360
1361         if (!params().options.empty()) {
1362                 top += ' ';
1363                 top += params().options;
1364         }
1365
1366         os << "<!-- " << ((runparams.flavor == OutputParams::XML)? "XML" : "SGML")
1367             << " file was created by LyX " << lyx_version
1368             << "\n  See http://www.lyx.org/ for more information -->\n";
1369
1370         params().documentClass().counters().reset();
1371
1372         updateMacros();
1373
1374         sgml::openTag(os, top);
1375         os << '\n';
1376         docbookParagraphs(text(), *this, os, runparams);
1377         sgml::closeTag(os, top_element);
1378 }
1379
1380
1381 void Buffer::makeLyXHTMLFile(FileName const & fname,
1382                               OutputParams const & runparams,
1383                               bool const body_only) const
1384 {
1385         LYXERR(Debug::LATEX, "makeLYXHTMLFile...");
1386
1387         ofdocstream ofs;
1388         if (!openFileWrite(ofs, fname))
1389                 return;
1390
1391         writeLyXHTMLSource(ofs, runparams, body_only);
1392
1393         ofs.close();
1394         if (ofs.fail())
1395                 lyxerr << "File '" << fname << "' was not closed properly." << endl;
1396 }
1397
1398
1399 void Buffer::writeLyXHTMLSource(odocstream & os,
1400                              OutputParams const & runparams,
1401                              bool const only_body) const
1402 {
1403         LaTeXFeatures features(*this, params(), runparams);
1404         validate(features);
1405
1406         d->texrow.reset();
1407
1408         if (!only_body) {
1409                 os << "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\"" <<
1410                         " \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">\n";
1411                 // FIXME Language should be set properly.
1412                 os << "<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en\" lang=\"en\">\n";
1413                 // FIXME Header
1414                 os << "<head>\n";
1415                 // FIXME Presumably need to set this right
1416                 os << "<meta http-equiv=\"Content-type\" content=\"text/html;charset=UTF-8\" />\n";
1417                 // FIXME Get this during validation? What about other meta-data?
1418                 os << "<title>TBA</title>\n";
1419
1420                 os << features.getTClassHTMLPreamble();
1421
1422                 os << '\n';
1423
1424                 docstring const styleinfo = features.getTClassHTMLStyles();
1425                 if (!styleinfo.empty()) {
1426                         os << "<style type='text/css'>\n";
1427                         os << styleinfo;
1428                         os << "</style>\n";
1429                 }
1430                 os << "</head>\n<body>\n";
1431         }
1432
1433         params().documentClass().counters().reset();
1434         xhtmlParagraphs(text(), *this, os, runparams);
1435         if (!only_body)
1436                 os << "</body>\n</html>\n";
1437 }
1438
1439
1440 // chktex should be run with these flags disabled: 3, 22, 25, 30, 38(?)
1441 // Other flags: -wall -v0 -x
1442 int Buffer::runChktex()
1443 {
1444         setBusy(true);
1445
1446         // get LaTeX-Filename
1447         FileName const path(temppath());
1448         string const name = addName(path.absFilename(), latexName());
1449         string const org_path = filePath();
1450
1451         PathChanger p(path); // path to LaTeX file
1452         message(_("Running chktex..."));
1453
1454         // Generate the LaTeX file if neccessary
1455         OutputParams runparams(&params().encoding());
1456         runparams.flavor = OutputParams::LATEX;
1457         runparams.nice = false;
1458         makeLaTeXFile(FileName(name), org_path, runparams);
1459
1460         TeXErrors terr;
1461         Chktex chktex(lyxrc.chktex_command, onlyFilename(name), filePath());
1462         int const res = chktex.run(terr); // run chktex
1463
1464         if (res == -1) {
1465                 Alert::error(_("chktex failure"),
1466                              _("Could not run chktex successfully."));
1467         } else if (res > 0) {
1468                 ErrorList & errlist = d->errorLists["ChkTeX"];
1469                 errlist.clear();
1470                 bufferErrors(terr, errlist);
1471         }
1472
1473         setBusy(false);
1474
1475         errors("ChkTeX");
1476
1477         return res;
1478 }
1479
1480
1481 void Buffer::validate(LaTeXFeatures & features) const
1482 {
1483         params().validate(features);
1484
1485         updateMacros();
1486
1487         for_each(paragraphs().begin(), paragraphs().end(),
1488                  boost::bind(&Paragraph::validate, _1, boost::ref(features)));
1489
1490         if (lyxerr.debugging(Debug::LATEX)) {
1491                 features.showStruct();
1492         }
1493 }
1494
1495
1496 void Buffer::getLabelList(vector<docstring> & list) const
1497 {
1498         // If this is a child document, use the parent's list instead.
1499         Buffer const * const pbuf = d->parent();
1500         if (pbuf) {
1501                 pbuf->getLabelList(list);
1502                 return;
1503         }
1504
1505         list.clear();
1506         Toc & toc = d->toc_backend.toc("label");
1507         TocIterator toc_it = toc.begin();
1508         TocIterator end = toc.end();
1509         for (; toc_it != end; ++toc_it) {
1510                 if (toc_it->depth() == 0)
1511                         list.push_back(toc_it->str());
1512         }
1513 }
1514
1515
1516 void Buffer::updateBibfilesCache(UpdateScope scope) const
1517 {
1518         // If this is a child document, use the parent's cache instead.
1519         Buffer const * const pbuf = d->parent();
1520         if (pbuf && scope != UpdateChildOnly) {
1521                 pbuf->updateBibfilesCache();
1522                 return;
1523         }
1524
1525         d->bibfilesCache_.clear();
1526         for (InsetIterator it = inset_iterator_begin(inset()); it; ++it) {
1527                 if (it->lyxCode() == BIBTEX_CODE) {
1528                         InsetBibtex const & inset =
1529                                 static_cast<InsetBibtex const &>(*it);
1530                         support::FileNameList const bibfiles = inset.getBibFiles();
1531                         d->bibfilesCache_.insert(d->bibfilesCache_.end(),
1532                                 bibfiles.begin(),
1533                                 bibfiles.end());
1534                 } else if (it->lyxCode() == INCLUDE_CODE) {
1535                         InsetInclude & inset =
1536                                 static_cast<InsetInclude &>(*it);
1537                         inset.updateBibfilesCache();
1538                         support::FileNameList const & bibfiles =
1539                                         inset.getBibfilesCache();
1540                         d->bibfilesCache_.insert(d->bibfilesCache_.end(),
1541                                 bibfiles.begin(),
1542                                 bibfiles.end());
1543                 }
1544         }
1545         // the bibinfo cache is now invalid
1546         d->bibinfoCacheValid_ = false;
1547 }
1548
1549
1550 void Buffer::invalidateBibinfoCache()
1551 {
1552         d->bibinfoCacheValid_ = false;
1553 }
1554
1555
1556 support::FileNameList const & Buffer::getBibfilesCache(UpdateScope scope) const
1557 {
1558         // If this is a child document, use the parent's cache instead.
1559         Buffer const * const pbuf = d->parent();
1560         if (pbuf && scope != UpdateChildOnly)
1561                 return pbuf->getBibfilesCache();
1562
1563         // We update the cache when first used instead of at loading time.
1564         if (d->bibfilesCache_.empty())
1565                 const_cast<Buffer *>(this)->updateBibfilesCache(scope);
1566
1567         return d->bibfilesCache_;
1568 }
1569
1570
1571 BiblioInfo const & Buffer::masterBibInfo() const
1572 {
1573         // if this is a child document and the parent is already loaded
1574         // use the parent's list instead  [ale990412]
1575         Buffer const * const tmp = masterBuffer();
1576         LASSERT(tmp, /**/);
1577         if (tmp != this)
1578                 return tmp->masterBibInfo();
1579         return localBibInfo();
1580 }
1581
1582
1583 BiblioInfo const & Buffer::localBibInfo() const
1584 {
1585         if (d->bibinfoCacheValid_) {
1586                 support::FileNameList const & bibfilesCache = getBibfilesCache();
1587                 // compare the cached timestamps with the actual ones.
1588                 support::FileNameList::const_iterator ei = bibfilesCache.begin();
1589                 support::FileNameList::const_iterator en = bibfilesCache.end();
1590                 for (; ei != en; ++ ei) {
1591                         time_t lastw = ei->lastModified();
1592                         if (lastw != d->bibfileStatus_[*ei]) {
1593                                 d->bibinfoCacheValid_ = false;
1594                                 d->bibfileStatus_[*ei] = lastw;
1595                                 break;
1596                         }
1597                 }
1598         }
1599
1600         if (!d->bibinfoCacheValid_) {
1601                 d->bibinfo_.clear();
1602                 for (InsetIterator it = inset_iterator_begin(inset()); it; ++it)
1603                         it->fillWithBibKeys(d->bibinfo_, it);
1604                 d->bibinfoCacheValid_ = true;
1605         }
1606         return d->bibinfo_;
1607 }
1608
1609
1610 bool Buffer::isDepClean(string const & name) const
1611 {
1612         DepClean::const_iterator const it = d->dep_clean.find(name);
1613         if (it == d->dep_clean.end())
1614                 return true;
1615         return it->second;
1616 }
1617
1618
1619 void Buffer::markDepClean(string const & name)
1620 {
1621         d->dep_clean[name] = true;
1622 }
1623
1624
1625 bool Buffer::getStatus(FuncRequest const & cmd, FuncStatus & flag)
1626 {
1627         switch (cmd.action) {
1628                 case LFUN_BUFFER_EXPORT: {
1629                         docstring const arg = cmd.argument();
1630                         bool enable = arg == "custom" || isExportable(to_utf8(arg));
1631                         if (!enable)
1632                                 flag.message(bformat(
1633                                         _("Don't know how to export to format: %1$s"), arg));
1634                         flag.setEnabled(enable);
1635                         break;
1636                 }
1637
1638                 case LFUN_BRANCH_ACTIVATE: 
1639                 case LFUN_BRANCH_DEACTIVATE: {
1640                         BranchList const & branchList = params().branchlist();
1641                         docstring const branchName = cmd.argument();
1642                         flag.setEnabled(!branchName.empty()
1643                                 && branchList.find(branchName));
1644                         break;
1645                 }
1646
1647                 case LFUN_BRANCH_ADD:
1648                 case LFUN_BRANCHES_RENAME:
1649                 case LFUN_BUFFER_PRINT:
1650                         // if no Buffer is present, then of course we won't be called!
1651                         flag.setEnabled(true);
1652                         break;
1653
1654                 default:
1655                         return false;
1656         }
1657         return true;
1658 }
1659
1660
1661 void Buffer::dispatch(string const & command, DispatchResult & result)
1662 {
1663         return dispatch(lyxaction.lookupFunc(command), result);
1664 }
1665
1666
1667 // NOTE We can end up here even if we have no GUI, because we are called
1668 // by LyX::exec to handled command-line requests. So we may need to check 
1669 // whether we have a GUI or not. The boolean use_gui holds this information.
1670 void Buffer::dispatch(FuncRequest const & func, DispatchResult & dr)
1671 {
1672         // We'll set this back to false if need be.
1673         bool dispatched = true;
1674
1675         switch (func.action) {
1676         case LFUN_BUFFER_EXPORT: {
1677                 bool success = doExport(to_utf8(func.argument()), false);
1678                 dr.setError(success);
1679                 if (!success)
1680                         dr.setMessage(bformat(_("Error exporting to format: %1$s."), 
1681                                               func.argument()));
1682                 break;
1683         }
1684
1685         case LFUN_BRANCH_ADD: {
1686                 BranchList & branchList = params().branchlist();
1687                 docstring const branchName = func.argument();
1688                 if (branchName.empty()) {
1689                         dispatched = false;
1690                         break;
1691                 }
1692                 Branch * branch = branchList.find(branchName);
1693                 if (branch) {
1694                         LYXERR0("Branch " << branchName << " does already exist.");
1695                         dr.setError(true);
1696                         docstring const msg = 
1697                                 bformat(_("Branch \"%1$s\" does already exist."), branchName);
1698                         dr.setMessage(msg);
1699                 } else {
1700                         branchList.add(branchName);
1701                         dr.setError(false);
1702                         dr.update(Update::Force);
1703                 }
1704                 break;
1705         }
1706
1707         case LFUN_BRANCH_ACTIVATE:
1708         case LFUN_BRANCH_DEACTIVATE: {
1709                 BranchList & branchList = params().branchlist();
1710                 docstring const branchName = func.argument();
1711                 // the case without a branch name is handled elsewhere
1712                 if (branchName.empty()) {
1713                         dispatched = false;
1714                         break;
1715                 }
1716                 Branch * branch = branchList.find(branchName);
1717                 if (!branch) {
1718                         LYXERR0("Branch " << branchName << " does not exist.");
1719                         dr.setError(true);
1720                         docstring const msg = 
1721                                 bformat(_("Branch \"%1$s\" does not exist."), branchName);
1722                         dr.setMessage(msg);
1723                 } else {
1724                         branch->setSelected(func.action == LFUN_BRANCH_ACTIVATE);
1725                         dr.setError(false);
1726                         dr.update(Update::Force);
1727                 }
1728                 break;
1729         }
1730
1731         case LFUN_BRANCHES_RENAME: {
1732                 if (func.argument().empty())
1733                         break;
1734
1735                 docstring const oldname = from_utf8(func.getArg(0));
1736                 docstring const newname = from_utf8(func.getArg(1));
1737                 InsetIterator it  = inset_iterator_begin(inset());
1738                 InsetIterator const end = inset_iterator_end(inset());
1739                 bool success = false;
1740                 for (; it != end; ++it) {
1741                         if (it->lyxCode() == BRANCH_CODE) {
1742                                 InsetBranch & ins = static_cast<InsetBranch &>(*it);
1743                                 if (ins.branch() == oldname) {
1744                                         undo().recordUndo(it);
1745                                         ins.rename(newname);
1746                                         success = true;
1747                                         continue;
1748                                 }
1749                         }
1750                         if (it->lyxCode() == INCLUDE_CODE) {
1751                                 // get buffer of external file
1752                                 InsetInclude const & ins =
1753                                         static_cast<InsetInclude const &>(*it);
1754                                 Buffer * child = ins.getChildBuffer();
1755                                 if (!child)
1756                                         continue;
1757                                 child->dispatch(func, dr);
1758                         }
1759                 }
1760
1761                 if (success)
1762                         dr.update(Update::Force);
1763                 break;
1764         }
1765
1766         case LFUN_BUFFER_PRINT: {
1767                 // we'll assume there's a problem until we succeed
1768                 dr.setError(true); 
1769                 string target = func.getArg(0);
1770                 string target_name = func.getArg(1);
1771                 string command = func.getArg(2);
1772
1773                 if (target.empty()
1774                     || target_name.empty()
1775                     || command.empty()) {
1776                         LYXERR0("Unable to parse " << func.argument());
1777                         docstring const msg = 
1778                                 bformat(_("Unable to parse \"%1$s\""), func.argument());
1779                         dr.setMessage(msg);
1780                         break;
1781                 }
1782                 if (target != "printer" && target != "file") {
1783                         LYXERR0("Unrecognized target \"" << target << '"');
1784                         docstring const msg = 
1785                                 bformat(_("Unrecognized target \"%1$s\""), from_utf8(target));
1786                         dr.setMessage(msg);
1787                         break;
1788                 }
1789
1790                 if (!doExport("dvi", true)) {
1791                         showPrintError(absFileName());
1792                         dr.setMessage(_("Error exporting to DVI."));
1793                         break;
1794                 }
1795
1796                 // Push directory path.
1797                 string const path = temppath();
1798                 // Prevent the compiler from optimizing away p
1799                 FileName pp(path);
1800                 PathChanger p(pp);
1801
1802                 // there are three cases here:
1803                 // 1. we print to a file
1804                 // 2. we print directly to a printer
1805                 // 3. we print using a spool command (print to file first)
1806                 Systemcall one;
1807                 int res = 0;
1808                 string const dviname = changeExtension(latexName(true), "dvi");
1809
1810                 if (target == "printer") {
1811                         if (!lyxrc.print_spool_command.empty()) {
1812                                 // case 3: print using a spool
1813                                 string const psname = changeExtension(dviname,".ps");
1814                                 command += ' ' + lyxrc.print_to_file
1815                                         + quoteName(psname)
1816                                         + ' '
1817                                         + quoteName(dviname);
1818
1819                                 string command2 = lyxrc.print_spool_command + ' ';
1820                                 if (target_name != "default") {
1821                                         command2 += lyxrc.print_spool_printerprefix
1822                                                 + target_name
1823                                                 + ' ';
1824                                 }
1825                                 command2 += quoteName(psname);
1826                                 // First run dvips.
1827                                 // If successful, then spool command
1828                                 res = one.startscript(Systemcall::Wait, command);
1829
1830                                 if (res == 0) {
1831                                         // If there's no GUI, we have to wait on this command. Otherwise,
1832                                         // LyX deletes the temporary directory, and with it the spooled
1833                                         // file, before it can be printed!!
1834                                         Systemcall::Starttype stype = use_gui ?
1835                                                 Systemcall::DontWait : Systemcall::Wait;
1836                                         res = one.startscript(stype, command2);
1837                                 }
1838                         } else {
1839                                 // case 2: print directly to a printer
1840                                 if (target_name != "default")
1841                                         command += ' ' + lyxrc.print_to_printer + target_name + ' ';
1842                                 // as above....
1843                                 Systemcall::Starttype stype = use_gui ?
1844                                         Systemcall::DontWait : Systemcall::Wait;
1845                                 res = one.startscript(stype, command + quoteName(dviname));
1846                         }
1847
1848                 } else {
1849                         // case 1: print to a file
1850                         FileName const filename(makeAbsPath(target_name, filePath()));
1851                         FileName const dvifile(makeAbsPath(dviname, path));
1852                         if (filename.exists()) {
1853                                 docstring text = bformat(
1854                                         _("The file %1$s already exists.\n\n"
1855                                           "Do you want to overwrite that file?"),
1856                                         makeDisplayPath(filename.absFilename()));
1857                                 if (Alert::prompt(_("Overwrite file?"),
1858                                                   text, 0, 1, _("&Overwrite"), _("&Cancel")) != 0)
1859                                         break;
1860                         }
1861                         command += ' ' + lyxrc.print_to_file
1862                                 + quoteName(filename.toFilesystemEncoding())
1863                                 + ' '
1864                                 + quoteName(dvifile.toFilesystemEncoding());
1865                         // as above....
1866                         Systemcall::Starttype stype = use_gui ?
1867                                 Systemcall::DontWait : Systemcall::Wait;
1868                         res = one.startscript(stype, command);
1869                 }
1870
1871                 if (res == 0) 
1872                         dr.setError(false);
1873                 else {
1874                         dr.setMessage(_("Error running external commands."));
1875                         showPrintError(absFileName());
1876                 }
1877                 break;
1878         }
1879
1880         default:
1881                 dispatched = false;
1882                 break;
1883         }
1884         dr.dispatched(dispatched);
1885 }
1886
1887
1888 void Buffer::changeLanguage(Language const * from, Language const * to)
1889 {
1890         LASSERT(from, /**/);
1891         LASSERT(to, /**/);
1892
1893         for_each(par_iterator_begin(),
1894                  par_iterator_end(),
1895                  bind(&Paragraph::changeLanguage, _1, params(), from, to));
1896 }
1897
1898
1899 bool Buffer::isMultiLingual() const
1900 {
1901         ParConstIterator end = par_iterator_end();
1902         for (ParConstIterator it = par_iterator_begin(); it != end; ++it)
1903                 if (it->isMultiLingual(params()))
1904                         return true;
1905
1906         return false;
1907 }
1908
1909
1910 DocIterator Buffer::getParFromID(int const id) const
1911 {
1912         Buffer * buf = const_cast<Buffer *>(this);
1913         if (id < 0) {
1914                 // John says this is called with id == -1 from undo
1915                 lyxerr << "getParFromID(), id: " << id << endl;
1916                 return doc_iterator_end(buf);
1917         }
1918
1919         for (DocIterator it = doc_iterator_begin(buf); !it.atEnd(); it.forwardPar())
1920                 if (it.paragraph().id() == id)
1921                         return it;
1922
1923         return doc_iterator_end(buf);
1924 }
1925
1926
1927 bool Buffer::hasParWithID(int const id) const
1928 {
1929         return !getParFromID(id).atEnd();
1930 }
1931
1932
1933 ParIterator Buffer::par_iterator_begin()
1934 {
1935         return ParIterator(doc_iterator_begin(this));
1936 }
1937
1938
1939 ParIterator Buffer::par_iterator_end()
1940 {
1941         return ParIterator(doc_iterator_end(this));
1942 }
1943
1944
1945 ParConstIterator Buffer::par_iterator_begin() const
1946 {
1947         return ParConstIterator(doc_iterator_begin(this));
1948 }
1949
1950
1951 ParConstIterator Buffer::par_iterator_end() const
1952 {
1953         return ParConstIterator(doc_iterator_end(this));
1954 }
1955
1956
1957 Language const * Buffer::language() const
1958 {
1959         return params().language;
1960 }
1961
1962
1963 docstring const Buffer::B_(string const & l10n) const
1964 {
1965         return params().B_(l10n);
1966 }
1967
1968
1969 bool Buffer::isClean() const
1970 {
1971         return d->lyx_clean;
1972 }
1973
1974
1975 bool Buffer::isBakClean() const
1976 {
1977         return d->bak_clean;
1978 }
1979
1980
1981 bool Buffer::isExternallyModified(CheckMethod method) const
1982 {
1983         LASSERT(d->filename.exists(), /**/);
1984         // if method == timestamp, check timestamp before checksum
1985         return (method == checksum_method
1986                 || d->timestamp_ != d->filename.lastModified())
1987                 && d->checksum_ != d->filename.checksum();
1988 }
1989
1990
1991 void Buffer::saveCheckSum(FileName const & file) const
1992 {
1993         if (file.exists()) {
1994                 d->timestamp_ = file.lastModified();
1995                 d->checksum_ = file.checksum();
1996         } else {
1997                 // in the case of save to a new file.
1998                 d->timestamp_ = 0;
1999                 d->checksum_ = 0;
2000         }
2001 }
2002
2003
2004 void Buffer::markClean() const
2005 {
2006         if (!d->lyx_clean) {
2007                 d->lyx_clean = true;
2008                 updateTitles();
2009         }
2010         // if the .lyx file has been saved, we don't need an
2011         // autosave
2012         d->bak_clean = true;
2013 }
2014
2015
2016 void Buffer::markBakClean() const
2017 {
2018         d->bak_clean = true;
2019 }
2020
2021
2022 void Buffer::setUnnamed(bool flag)
2023 {
2024         d->unnamed = flag;
2025 }
2026
2027
2028 bool Buffer::isUnnamed() const
2029 {
2030         return d->unnamed;
2031 }
2032
2033
2034 // FIXME: this function should be moved to buffer_pimpl.C
2035 void Buffer::markDirty()
2036 {
2037         if (d->lyx_clean) {
2038                 d->lyx_clean = false;
2039                 updateTitles();
2040         }
2041         d->bak_clean = false;
2042
2043         DepClean::iterator it = d->dep_clean.begin();
2044         DepClean::const_iterator const end = d->dep_clean.end();
2045
2046         for (; it != end; ++it)
2047                 it->second = false;
2048 }
2049
2050
2051 FileName Buffer::fileName() const
2052 {
2053         return d->filename;
2054 }
2055
2056
2057 string Buffer::absFileName() const
2058 {
2059         return d->filename.absFilename();
2060 }
2061
2062
2063 string Buffer::filePath() const
2064 {
2065         return d->filename.onlyPath().absFilename() + "/";
2066 }
2067
2068
2069 bool Buffer::isReadonly() const
2070 {
2071         return d->read_only;
2072 }
2073
2074
2075 void Buffer::setParent(Buffer const * buffer)
2076 {
2077         // Avoids recursive include.
2078         d->setParent(buffer == this ? 0 : buffer);
2079         updateMacros();
2080 }
2081
2082
2083 Buffer const * Buffer::parent() const
2084 {
2085         return d->parent();
2086 }
2087
2088
2089 void Buffer::collectRelatives(BufferSet & bufs) const
2090 {
2091         bufs.insert(this);
2092         if (parent())
2093                 parent()->collectRelatives(bufs);
2094
2095         // loop over children
2096         Impl::BufferPositionMap::iterator it = d->children_positions.begin();
2097         Impl::BufferPositionMap::iterator end = d->children_positions.end();
2098         for (; it != end; ++it)
2099                 bufs.insert(const_cast<Buffer *>(it->first));
2100 }
2101
2102
2103 std::vector<Buffer const *> Buffer::allRelatives() const
2104 {
2105         BufferSet bufs;
2106         collectRelatives(bufs);
2107         BufferSet::iterator it = bufs.begin();
2108         std::vector<Buffer const *> ret;
2109         for (; it != bufs.end(); ++it)
2110                 ret.push_back(*it);
2111         return ret;
2112 }
2113
2114
2115 Buffer const * Buffer::masterBuffer() const
2116 {
2117         Buffer const * const pbuf = d->parent();
2118         if (!pbuf)
2119                 return this;
2120
2121         return pbuf->masterBuffer();
2122 }
2123
2124
2125 bool Buffer::isChild(Buffer * child) const
2126 {
2127         return d->children_positions.find(child) != d->children_positions.end();
2128 }
2129
2130
2131 DocIterator Buffer::firstChildPosition(Buffer const * child)
2132 {
2133         Impl::BufferPositionMap::iterator it;
2134         it = d->children_positions.find(child);
2135         if (it == d->children_positions.end())
2136                 return DocIterator(this);
2137         return it->second;
2138 }
2139
2140
2141 std::vector<Buffer *> Buffer::getChildren() const
2142 {
2143         std::vector<Buffer *> clist;
2144         // loop over children
2145         Impl::BufferPositionMap::iterator it = d->children_positions.begin();
2146         Impl::BufferPositionMap::iterator end = d->children_positions.end();
2147         for (; it != end; ++it) {
2148                 Buffer * child = const_cast<Buffer *>(it->first);
2149                 clist.push_back(child);
2150                 // there might be grandchildren
2151                 std::vector<Buffer *> glist = child->getChildren();
2152                 for (vector<Buffer *>::const_iterator git = glist.begin();
2153                      git != glist.end(); ++git)
2154                         clist.push_back(*git);
2155         }
2156         return clist;
2157 }
2158
2159
2160 template<typename M>
2161 typename M::iterator greatest_below(M & m, typename M::key_type const & x)
2162 {
2163         if (m.empty())
2164                 return m.end();
2165
2166         typename M::iterator it = m.lower_bound(x);
2167         if (it == m.begin())
2168                 return m.end();
2169
2170         it--;
2171         return it;
2172 }
2173
2174
2175 MacroData const * Buffer::getBufferMacro(docstring const & name,
2176                                          DocIterator const & pos) const
2177 {
2178         LYXERR(Debug::MACROS, "Searching for " << to_ascii(name) << " at " << pos);
2179
2180         // if paragraphs have no macro context set, pos will be empty
2181         if (pos.empty())
2182                 return 0;
2183
2184         // we haven't found anything yet
2185         DocIterator bestPos = par_iterator_begin();
2186         MacroData const * bestData = 0;
2187
2188         // find macro definitions for name
2189         Impl::NamePositionScopeMacroMap::iterator nameIt
2190                 = d->macros.find(name);
2191         if (nameIt != d->macros.end()) {
2192                 // find last definition in front of pos or at pos itself
2193                 Impl::PositionScopeMacroMap::const_iterator it
2194                         = greatest_below(nameIt->second, pos);
2195                 if (it != nameIt->second.end()) {
2196                         while (true) {
2197                                 // scope ends behind pos?
2198                                 if (pos < it->second.first) {
2199                                         // Looks good, remember this. If there
2200                                         // is no external macro behind this,
2201                                         // we found the right one already.
2202                                         bestPos = it->first;
2203                                         bestData = &it->second.second;
2204                                         break;
2205                                 }
2206
2207                                 // try previous macro if there is one
2208                                 if (it == nameIt->second.begin())
2209                                         break;
2210                                 it--;
2211                         }
2212                 }
2213         }
2214
2215         // find macros in included files
2216         Impl::PositionScopeBufferMap::const_iterator it
2217                 = greatest_below(d->position_to_children, pos);
2218         if (it == d->position_to_children.end())
2219                 // no children before
2220                 return bestData;
2221
2222         while (true) {
2223                 // do we know something better (i.e. later) already?
2224                 if (it->first < bestPos )
2225                         break;
2226
2227                 // scope ends behind pos?
2228                 if (pos < it->second.first) {
2229                         // look for macro in external file
2230                         d->macro_lock = true;
2231                         MacroData const * data
2232                         = it->second.second->getMacro(name, false);
2233                         d->macro_lock = false;
2234                         if (data) {
2235                                 bestPos = it->first;
2236                                 bestData = data;
2237                                 break;
2238                         }
2239                 }
2240
2241                 // try previous file if there is one
2242                 if (it == d->position_to_children.begin())
2243                         break;
2244                 --it;
2245         }
2246
2247         // return the best macro we have found
2248         return bestData;
2249 }
2250
2251
2252 MacroData const * Buffer::getMacro(docstring const & name,
2253         DocIterator const & pos, bool global) const
2254 {
2255         if (d->macro_lock)
2256                 return 0;
2257
2258         // query buffer macros
2259         MacroData const * data = getBufferMacro(name, pos);
2260         if (data != 0)
2261                 return data;
2262
2263         // If there is a master buffer, query that
2264         Buffer const * const pbuf = d->parent();
2265         if (pbuf) {
2266                 d->macro_lock = true;
2267                 MacroData const * macro = pbuf->getMacro(
2268                         name, *this, false);
2269                 d->macro_lock = false;
2270                 if (macro)
2271                         return macro;
2272         }
2273
2274         if (global) {
2275                 data = MacroTable::globalMacros().get(name);
2276                 if (data != 0)
2277                         return data;
2278         }
2279
2280         return 0;
2281 }
2282
2283
2284 MacroData const * Buffer::getMacro(docstring const & name, bool global) const
2285 {
2286         // set scope end behind the last paragraph
2287         DocIterator scope = par_iterator_begin();
2288         scope.pit() = scope.lastpit() + 1;
2289
2290         return getMacro(name, scope, global);
2291 }
2292
2293
2294 MacroData const * Buffer::getMacro(docstring const & name,
2295         Buffer const & child, bool global) const
2296 {
2297         // look where the child buffer is included first
2298         Impl::BufferPositionMap::iterator it = d->children_positions.find(&child);
2299         if (it == d->children_positions.end())
2300                 return 0;
2301
2302         // check for macros at the inclusion position
2303         return getMacro(name, it->second, global);
2304 }
2305
2306
2307 void Buffer::updateMacros(DocIterator & it, DocIterator & scope) const
2308 {
2309         pit_type lastpit = it.lastpit();
2310
2311         // look for macros in each paragraph
2312         while (it.pit() <= lastpit) {
2313                 Paragraph & par = it.paragraph();
2314
2315                 // iterate over the insets of the current paragraph
2316                 InsetList const & insets = par.insetList();
2317                 InsetList::const_iterator iit = insets.begin();
2318                 InsetList::const_iterator end = insets.end();
2319                 for (; iit != end; ++iit) {
2320                         it.pos() = iit->pos;
2321
2322                         // is it a nested text inset?
2323                         if (iit->inset->asInsetText()) {
2324                                 // Inset needs its own scope?
2325                                 InsetText const * itext = iit->inset->asInsetText();
2326                                 bool newScope = itext->isMacroScope();
2327
2328                                 // scope which ends just behind the inset
2329                                 DocIterator insetScope = it;
2330                                 ++insetScope.pos();
2331
2332                                 // collect macros in inset
2333                                 it.push_back(CursorSlice(*iit->inset));
2334                                 updateMacros(it, newScope ? insetScope : scope);
2335                                 it.pop_back();
2336                                 continue;
2337                         }
2338
2339                         // is it an external file?
2340                         if (iit->inset->lyxCode() == INCLUDE_CODE) {
2341                                 // get buffer of external file
2342                                 InsetInclude const & inset =
2343                                         static_cast<InsetInclude const &>(*iit->inset);
2344                                 d->macro_lock = true;
2345                                 Buffer * child = inset.getChildBuffer();
2346                                 d->macro_lock = false;
2347                                 if (!child)
2348                                         continue;
2349
2350                                 // register its position, but only when it is
2351                                 // included first in the buffer
2352                                 if (d->children_positions.find(child) ==
2353                                         d->children_positions.end())
2354                                                 d->children_positions[child] = it;
2355
2356                                 // register child with its scope
2357                                 d->position_to_children[it] = Impl::ScopeBuffer(scope, child);
2358                                 continue;
2359                         }
2360
2361                         if (iit->inset->lyxCode() != MATHMACRO_CODE)
2362                                 continue;
2363
2364                         // get macro data
2365                         MathMacroTemplate & macroTemplate =
2366                                 static_cast<MathMacroTemplate &>(*iit->inset);
2367                         MacroContext mc(*this, it);
2368                         macroTemplate.updateToContext(mc);
2369
2370                         // valid?
2371                         bool valid = macroTemplate.validMacro();
2372                         // FIXME: Should be fixNameAndCheckIfValid() in fact,
2373                         // then the BufferView's cursor will be invalid in
2374                         // some cases which leads to crashes.
2375                         if (!valid)
2376                                 continue;
2377
2378                         // register macro
2379                         d->macros[macroTemplate.name()][it] =
2380                                 Impl::ScopeMacro(scope, MacroData(*this, it));
2381                 }
2382
2383                 // next paragraph
2384                 it.pit()++;
2385                 it.pos() = 0;
2386         }
2387 }
2388
2389
2390 void Buffer::updateMacros() const
2391 {
2392         if (d->macro_lock)
2393                 return;
2394
2395         LYXERR(Debug::MACROS, "updateMacro of " << d->filename.onlyFileName());
2396
2397         // start with empty table
2398         d->macros.clear();
2399         d->children_positions.clear();
2400         d->position_to_children.clear();
2401
2402         // Iterate over buffer, starting with first paragraph
2403         // The scope must be bigger than any lookup DocIterator
2404         // later. For the global lookup, lastpit+1 is used, hence
2405         // we use lastpit+2 here.
2406         DocIterator it = par_iterator_begin();
2407         DocIterator outerScope = it;
2408         outerScope.pit() = outerScope.lastpit() + 2;
2409         updateMacros(it, outerScope);
2410 }
2411
2412
2413 void Buffer::getUsedBranches(std::list<docstring> & result, bool const from_master) const
2414 {
2415         InsetIterator it  = inset_iterator_begin(inset());
2416         InsetIterator const end = inset_iterator_end(inset());
2417         for (; it != end; ++it) {
2418                 if (it->lyxCode() == BRANCH_CODE) {
2419                         InsetBranch & br = static_cast<InsetBranch &>(*it);
2420                         docstring const name = br.branch();
2421                         if (!from_master && !params().branchlist().find(name))
2422                                 result.push_back(name);
2423                         else if (from_master && !masterBuffer()->params().branchlist().find(name))
2424                                 result.push_back(name);
2425                         continue;
2426                 }
2427                 if (it->lyxCode() == INCLUDE_CODE) {
2428                         // get buffer of external file
2429                         InsetInclude const & ins =
2430                                 static_cast<InsetInclude const &>(*it);
2431                         Buffer * child = ins.getChildBuffer();
2432                         if (!child)
2433                                 continue;
2434                         child->getUsedBranches(result, true);
2435                 }
2436         }
2437         // remove duplicates
2438         result.unique();
2439 }
2440
2441
2442 void Buffer::updateMacroInstances() const
2443 {
2444         LYXERR(Debug::MACROS, "updateMacroInstances for "
2445                 << d->filename.onlyFileName());
2446         DocIterator it = doc_iterator_begin(this);
2447         DocIterator end = doc_iterator_end(this);
2448         for (; it != end; it.forwardPos()) {
2449                 // look for MathData cells in InsetMathNest insets
2450                 Inset * inset = it.nextInset();
2451                 if (!inset)
2452                         continue;
2453
2454                 InsetMath * minset = inset->asInsetMath();
2455                 if (!minset)
2456                         continue;
2457
2458                 // update macro in all cells of the InsetMathNest
2459                 DocIterator::idx_type n = minset->nargs();
2460                 MacroContext mc = MacroContext(*this, it);
2461                 for (DocIterator::idx_type i = 0; i < n; ++i) {
2462                         MathData & data = minset->cell(i);
2463                         data.updateMacros(0, mc);
2464                 }
2465         }
2466 }
2467
2468
2469 void Buffer::listMacroNames(MacroNameSet & macros) const
2470 {
2471         if (d->macro_lock)
2472                 return;
2473
2474         d->macro_lock = true;
2475
2476         // loop over macro names
2477         Impl::NamePositionScopeMacroMap::iterator nameIt = d->macros.begin();
2478         Impl::NamePositionScopeMacroMap::iterator nameEnd = d->macros.end();
2479         for (; nameIt != nameEnd; ++nameIt)
2480                 macros.insert(nameIt->first);
2481
2482         // loop over children
2483         Impl::BufferPositionMap::iterator it = d->children_positions.begin();
2484         Impl::BufferPositionMap::iterator end = d->children_positions.end();
2485         for (; it != end; ++it)
2486                 it->first->listMacroNames(macros);
2487
2488         // call parent
2489         Buffer const * const pbuf = d->parent();
2490         if (pbuf)
2491                 pbuf->listMacroNames(macros);
2492
2493         d->macro_lock = false;
2494 }
2495
2496
2497 void Buffer::listParentMacros(MacroSet & macros, LaTeXFeatures & features) const
2498 {
2499         Buffer const * const pbuf = d->parent();
2500         if (!pbuf)
2501                 return;
2502
2503         MacroNameSet names;
2504         pbuf->listMacroNames(names);
2505
2506         // resolve macros
2507         MacroNameSet::iterator it = names.begin();
2508         MacroNameSet::iterator end = names.end();
2509         for (; it != end; ++it) {
2510                 // defined?
2511                 MacroData const * data =
2512                 pbuf->getMacro(*it, *this, false);
2513                 if (data) {
2514                         macros.insert(data);
2515
2516                         // we cannot access the original MathMacroTemplate anymore
2517                         // here to calls validate method. So we do its work here manually.
2518                         // FIXME: somehow make the template accessible here.
2519                         if (data->optionals() > 0)
2520                                 features.require("xargs");
2521                 }
2522         }
2523 }
2524
2525
2526 Buffer::References & Buffer::references(docstring const & label)
2527 {
2528         if (d->parent())
2529                 return const_cast<Buffer *>(masterBuffer())->references(label);
2530
2531         RefCache::iterator it = d->ref_cache_.find(label);
2532         if (it != d->ref_cache_.end())
2533                 return it->second.second;
2534
2535         static InsetLabel const * dummy_il = 0;
2536         static References const dummy_refs;
2537         it = d->ref_cache_.insert(
2538                 make_pair(label, make_pair(dummy_il, dummy_refs))).first;
2539         return it->second.second;
2540 }
2541
2542
2543 Buffer::References const & Buffer::references(docstring const & label) const
2544 {
2545         return const_cast<Buffer *>(this)->references(label);
2546 }
2547
2548
2549 void Buffer::setInsetLabel(docstring const & label, InsetLabel const * il)
2550 {
2551         masterBuffer()->d->ref_cache_[label].first = il;
2552 }
2553
2554
2555 InsetLabel const * Buffer::insetLabel(docstring const & label) const
2556 {
2557         return masterBuffer()->d->ref_cache_[label].first;
2558 }
2559
2560
2561 void Buffer::clearReferenceCache() const
2562 {
2563         if (!d->parent())
2564                 d->ref_cache_.clear();
2565 }
2566
2567
2568 void Buffer::changeRefsIfUnique(docstring const & from, docstring const & to,
2569         InsetCode code)
2570 {
2571         //FIXME: This does not work for child documents yet.
2572         LASSERT(code == CITE_CODE, /**/);
2573         // Check if the label 'from' appears more than once
2574         vector<docstring> labels;
2575         string paramName;
2576         BiblioInfo const & keys = masterBibInfo();
2577         BiblioInfo::const_iterator bit  = keys.begin();
2578         BiblioInfo::const_iterator bend = keys.end();
2579
2580         for (; bit != bend; ++bit)
2581                 // FIXME UNICODE
2582                 labels.push_back(bit->first);
2583         paramName = "key";
2584
2585         if (count(labels.begin(), labels.end(), from) > 1)
2586                 return;
2587
2588         for (InsetIterator it = inset_iterator_begin(inset()); it; ++it) {
2589                 if (it->lyxCode() == code) {
2590                         InsetCommand & inset = static_cast<InsetCommand &>(*it);
2591                         docstring const oldValue = inset.getParam(paramName);
2592                         if (oldValue == from)
2593                                 inset.setParam(paramName, to);
2594                 }
2595         }
2596 }
2597
2598
2599 void Buffer::getSourceCode(odocstream & os, pit_type par_begin,
2600         pit_type par_end, bool full_source) const
2601 {
2602         OutputParams runparams(&params().encoding());
2603         runparams.nice = true;
2604         runparams.flavor = params().useXetex ? 
2605                 OutputParams::XETEX : OutputParams::LATEX;
2606         runparams.linelen = lyxrc.plaintext_linelen;
2607         // No side effect of file copying and image conversion
2608         runparams.dryrun = true;
2609
2610         if (full_source) {
2611                 os << "% " << _("Preview source code") << "\n\n";
2612                 d->texrow.reset();
2613                 d->texrow.newline();
2614                 d->texrow.newline();
2615                 if (isDocBook())
2616                         writeDocBookSource(os, absFileName(), runparams, false);
2617                 else
2618                         // latex or literate
2619                         writeLaTeXSource(os, string(), runparams, true, true);
2620         } else {
2621                 runparams.par_begin = par_begin;
2622                 runparams.par_end = par_end;
2623                 if (par_begin + 1 == par_end) {
2624                         os << "% "
2625                            << bformat(_("Preview source code for paragraph %1$d"), par_begin)
2626                            << "\n\n";
2627                 } else {
2628                         os << "% "
2629                            << bformat(_("Preview source code from paragraph %1$s to %2$s"),
2630                                         convert<docstring>(par_begin),
2631                                         convert<docstring>(par_end - 1))
2632                            << "\n\n";
2633                 }
2634                 TexRow texrow;
2635                 texrow.reset();
2636                 texrow.newline();
2637                 texrow.newline();
2638                 // output paragraphs
2639                 if (isDocBook())
2640                         docbookParagraphs(text(), *this, os, runparams);
2641                 else 
2642                         // latex or literate
2643                         latexParagraphs(*this, text(), os, texrow, runparams);
2644         }
2645 }
2646
2647
2648 ErrorList & Buffer::errorList(string const & type) const
2649 {
2650         static ErrorList emptyErrorList;
2651         map<string, ErrorList>::iterator I = d->errorLists.find(type);
2652         if (I == d->errorLists.end())
2653                 return emptyErrorList;
2654
2655         return I->second;
2656 }
2657
2658
2659 void Buffer::updateTocItem(std::string const & type,
2660         DocIterator const & dit) const
2661 {
2662         if (gui_)
2663                 gui_->updateTocItem(type, dit);
2664 }
2665
2666
2667 void Buffer::structureChanged() const
2668 {
2669         if (gui_)
2670                 gui_->structureChanged();
2671 }
2672
2673
2674 void Buffer::errors(string const & err, bool from_master) const
2675 {
2676         if (gui_)
2677                 gui_->errors(err, from_master);
2678 }
2679
2680
2681 void Buffer::message(docstring const & msg) const
2682 {
2683         if (gui_)
2684                 gui_->message(msg);
2685 }
2686
2687
2688 void Buffer::setBusy(bool on) const
2689 {
2690         if (gui_)
2691                 gui_->setBusy(on);
2692 }
2693
2694
2695 void Buffer::setReadOnly(bool on) const
2696 {
2697         if (d->wa_)
2698                 d->wa_->setReadOnly(on);
2699 }
2700
2701
2702 void Buffer::updateTitles() const
2703 {
2704         if (d->wa_)
2705                 d->wa_->updateTitles();
2706 }
2707
2708
2709 void Buffer::resetAutosaveTimers() const
2710 {
2711         if (gui_)
2712                 gui_->resetAutosaveTimers();
2713 }
2714
2715
2716 bool Buffer::hasGuiDelegate() const
2717 {
2718         return gui_;
2719 }
2720
2721
2722 void Buffer::setGuiDelegate(frontend::GuiBufferDelegate * gui)
2723 {
2724         gui_ = gui;
2725 }
2726
2727
2728
2729 namespace {
2730
2731 class AutoSaveBuffer : public ForkedProcess {
2732 public:
2733         ///
2734         AutoSaveBuffer(Buffer const & buffer, FileName const & fname)
2735                 : buffer_(buffer), fname_(fname) {}
2736         ///
2737         virtual boost::shared_ptr<ForkedProcess> clone() const
2738         {
2739                 return boost::shared_ptr<ForkedProcess>(new AutoSaveBuffer(*this));
2740         }
2741         ///
2742         int start()
2743         {
2744                 command_ = to_utf8(bformat(_("Auto-saving %1$s"),
2745                                                  from_utf8(fname_.absFilename())));
2746                 return run(DontWait);
2747         }
2748 private:
2749         ///
2750         virtual int generateChild();
2751         ///
2752         Buffer const & buffer_;
2753         FileName fname_;
2754 };
2755
2756
2757 int AutoSaveBuffer::generateChild()
2758 {
2759         // tmp_ret will be located (usually) in /tmp
2760         // will that be a problem?
2761         // Note that this calls ForkedCalls::fork(), so it's
2762         // ok cross-platform.
2763         pid_t const pid = fork();
2764         // If you want to debug the autosave
2765         // you should set pid to -1, and comment out the fork.
2766         if (pid != 0 && pid != -1)
2767                 return pid;
2768
2769         // pid = -1 signifies that lyx was unable
2770         // to fork. But we will do the save
2771         // anyway.
2772         bool failed = false;
2773         FileName const tmp_ret = FileName::tempName("lyxauto");
2774         if (!tmp_ret.empty()) {
2775                 buffer_.writeFile(tmp_ret);
2776                 // assume successful write of tmp_ret
2777                 if (!tmp_ret.moveTo(fname_))
2778                         failed = true;
2779         } else
2780                 failed = true;
2781
2782         if (failed) {
2783                 // failed to write/rename tmp_ret so try writing direct
2784                 if (!buffer_.writeFile(fname_)) {
2785                         // It is dangerous to do this in the child,
2786                         // but safe in the parent, so...
2787                         if (pid == -1) // emit message signal.
2788                                 buffer_.message(_("Autosave failed!"));
2789                 }
2790         }
2791
2792         if (pid == 0) // we are the child so...
2793                 _exit(0);
2794
2795         return pid;
2796 }
2797
2798 } // namespace anon
2799
2800
2801 FileName Buffer::getAutosaveFilename() const
2802 {
2803         // if the document is unnamed try to save in the backup dir, else
2804         // in the default document path, and as a last try in the filePath, 
2805         // which will most often be the temporary directory
2806         string fpath;
2807         if (isUnnamed())
2808                 fpath = lyxrc.backupdir_path.empty() ? lyxrc.document_path
2809                         : lyxrc.backupdir_path;
2810         if (!isUnnamed() || fpath.empty() || !FileName(fpath).exists())
2811                 fpath = filePath();
2812
2813         string const fname = "#" + d->filename.onlyFileName() + "#";
2814         return makeAbsPath(fname, fpath);
2815 }
2816
2817
2818 void Buffer::removeAutosaveFile() const
2819 {
2820         FileName const f = getAutosaveFilename();
2821         if (f.exists())
2822                 f.removeFile();
2823 }
2824
2825
2826 void Buffer::moveAutosaveFile(support::FileName const & oldauto) const
2827 {
2828         FileName const newauto = getAutosaveFilename();
2829         if (!(oldauto == newauto || oldauto.moveTo(newauto)))
2830                 LYXERR0("Unable to remove autosave file `" << oldauto << "'!");
2831 }
2832
2833
2834 // Perfect target for a thread...
2835 void Buffer::autoSave() const
2836 {
2837         if (isBakClean() || isReadonly()) {
2838                 // We don't save now, but we'll try again later
2839                 resetAutosaveTimers();
2840                 return;
2841         }
2842
2843         // emit message signal.
2844         message(_("Autosaving current document..."));
2845         AutoSaveBuffer autosave(*this, getAutosaveFilename());
2846         autosave.start();
2847
2848         markBakClean();
2849         resetAutosaveTimers();
2850 }
2851
2852
2853 string Buffer::bufferFormat() const
2854 {
2855         string format = params().documentClass().outputFormat();
2856         if (format == "latex") {
2857                 if (params().useXetex)
2858                         return "xetex";
2859                 if (params().encoding().package() == Encoding::japanese)
2860                         return "platex";
2861         }
2862         return format;
2863 }
2864
2865
2866 string Buffer::getDefaultOutputFormat() const
2867 {
2868         if (!params().defaultOutputFormat.empty()
2869             && params().defaultOutputFormat != "default")
2870                 return params().defaultOutputFormat;
2871         typedef vector<Format const *> Formats;
2872         Formats formats = exportableFormats(true);
2873         if (isDocBook()
2874             || isLiterate()
2875             || params().useXetex
2876             || params().encoding().package() == Encoding::japanese) {
2877                 if (formats.empty())
2878                         return string();
2879                 // return the first we find
2880                 return formats.front()->name();
2881         }
2882         return lyxrc.default_view_format;
2883 }
2884
2885
2886
2887 bool Buffer::doExport(string const & format, bool put_in_tempdir,
2888         string & result_file) const
2889 {
2890         string backend_format;
2891         OutputParams runparams(&params().encoding());
2892         runparams.flavor = OutputParams::LATEX;
2893         runparams.linelen = lyxrc.plaintext_linelen;
2894         vector<string> backs = backends();
2895         if (find(backs.begin(), backs.end(), format) == backs.end()) {
2896                 // Get shortest path to format
2897                 Graph::EdgePath path;
2898                 for (vector<string>::const_iterator it = backs.begin();
2899                      it != backs.end(); ++it) {
2900                         Graph::EdgePath p = theConverters().getPath(*it, format);
2901                         if (!p.empty() && (path.empty() || p.size() < path.size())) {
2902                                 backend_format = *it;
2903                                 path = p;
2904                         }
2905                 }
2906                 if (!path.empty())
2907                         runparams.flavor = theConverters().getFlavor(path);
2908                 else {
2909                         Alert::error(_("Couldn't export file"),
2910                                 bformat(_("No information for exporting the format %1$s."),
2911                                    formats.prettyName(format)));
2912                         return false;
2913                 }
2914         } else {
2915                 backend_format = format;
2916                 // FIXME: Don't hardcode format names here, but use a flag
2917                 if (backend_format == "pdflatex")
2918                         runparams.flavor = OutputParams::PDFLATEX;
2919         }
2920
2921         string filename = latexName(false);
2922         filename = addName(temppath(), filename);
2923         filename = changeExtension(filename,
2924                                    formats.extension(backend_format));
2925
2926         // fix macros
2927         updateMacroInstances();
2928
2929         // Plain text backend
2930         if (backend_format == "text")
2931                 writePlaintextFile(*this, FileName(filename), runparams);
2932         // no backend
2933         else if (backend_format == "xhtml")
2934                 makeLyXHTMLFile(FileName(filename), runparams);
2935         else if (backend_format == "lyx")
2936                 writeFile(FileName(filename));
2937         // Docbook backend
2938         else if (isDocBook()) {
2939                 runparams.nice = !put_in_tempdir;
2940                 makeDocBookFile(FileName(filename), runparams);
2941         }
2942         // LaTeX backend
2943         else if (backend_format == format) {
2944                 runparams.nice = true;
2945                 if (!makeLaTeXFile(FileName(filename), string(), runparams))
2946                         return false;
2947         } else if (!lyxrc.tex_allows_spaces
2948                    && contains(filePath(), ' ')) {
2949                 Alert::error(_("File name error"),
2950                            _("The directory path to the document cannot contain spaces."));
2951                 return false;
2952         } else {
2953                 runparams.nice = false;
2954                 if (!makeLaTeXFile(FileName(filename), filePath(), runparams))
2955                         return false;
2956         }
2957
2958         string const error_type = (format == "program")
2959                 ? "Build" : bufferFormat();
2960         ErrorList & error_list = d->errorLists[error_type];
2961         string const ext = formats.extension(format);
2962         FileName const tmp_result_file(changeExtension(filename, ext));
2963         bool const success = theConverters().convert(this, FileName(filename),
2964                 tmp_result_file, FileName(absFileName()), backend_format, format,
2965                 error_list);
2966         // Emit the signal to show the error list.
2967         if (format != backend_format) {
2968                 errors(error_type);
2969                 // also to the children, in case of master-buffer-view
2970                 std::vector<Buffer *> clist = getChildren();
2971                 for (vector<Buffer *>::const_iterator cit = clist.begin();
2972                      cit != clist.end(); ++cit)
2973                         (*cit)->errors(error_type, true);
2974         }
2975         if (!success)
2976                 return false;
2977
2978         if (put_in_tempdir) {
2979                 result_file = tmp_result_file.absFilename();
2980                 return true;
2981         }
2982
2983         result_file = changeExtension(exportFileName().absFilename(), ext);
2984         // We need to copy referenced files (e. g. included graphics
2985         // if format == "dvi") to the result dir.
2986         vector<ExportedFile> const files =
2987                 runparams.exportdata->externalFiles(format);
2988         string const dest = onlyPath(result_file);
2989         CopyStatus status = SUCCESS;
2990         for (vector<ExportedFile>::const_iterator it = files.begin();
2991                 it != files.end() && status != CANCEL; ++it) {
2992                 string const fmt = formats.getFormatFromFile(it->sourceName);
2993                 status = copyFile(fmt, it->sourceName,
2994                         makeAbsPath(it->exportName, dest),
2995                         it->exportName, status == FORCE);
2996         }
2997         if (status == CANCEL) {
2998                 message(_("Document export cancelled."));
2999         } else if (tmp_result_file.exists()) {
3000                 // Finally copy the main file
3001                 status = copyFile(format, tmp_result_file,
3002                         FileName(result_file), result_file,
3003                         status == FORCE);
3004                 message(bformat(_("Document exported as %1$s "
3005                         "to file `%2$s'"),
3006                         formats.prettyName(format),
3007                         makeDisplayPath(result_file)));
3008         } else {
3009                 // This must be a dummy converter like fax (bug 1888)
3010                 message(bformat(_("Document exported as %1$s"),
3011                         formats.prettyName(format)));
3012         }
3013
3014         return true;
3015 }
3016
3017
3018 bool Buffer::doExport(string const & format, bool put_in_tempdir) const
3019 {
3020         string result_file;
3021         return doExport(format, put_in_tempdir, result_file);
3022 }
3023
3024
3025 bool Buffer::preview(string const & format) const
3026 {
3027         string result_file;
3028         if (!doExport(format, true, result_file))
3029                 return false;
3030         return formats.view(*this, FileName(result_file), format);
3031 }
3032
3033
3034 bool Buffer::isExportable(string const & format) const
3035 {
3036         vector<string> backs = backends();
3037         for (vector<string>::const_iterator it = backs.begin();
3038              it != backs.end(); ++it)
3039                 if (theConverters().isReachable(*it, format))
3040                         return true;
3041         return false;
3042 }
3043
3044
3045 vector<Format const *> Buffer::exportableFormats(bool only_viewable) const
3046 {
3047         vector<string> backs = backends();
3048         vector<Format const *> result =
3049                 theConverters().getReachable(backs[0], only_viewable, true);
3050         for (vector<string>::const_iterator it = backs.begin() + 1;
3051              it != backs.end(); ++it) {
3052                 vector<Format const *>  r =
3053                         theConverters().getReachable(*it, only_viewable, false);
3054                 result.insert(result.end(), r.begin(), r.end());
3055         }
3056         return result;
3057 }
3058
3059
3060 vector<string> Buffer::backends() const
3061 {
3062         vector<string> v;
3063         if (params().baseClass()->isTeXClassAvailable()) {
3064                 v.push_back(bufferFormat());
3065                 // FIXME: Don't hardcode format names here, but use a flag
3066                 if (v.back() == "latex")
3067                         v.push_back("pdflatex");
3068         }
3069         v.push_back("text");
3070         v.push_back("xhtml");
3071         v.push_back("lyx");
3072         return v;
3073 }
3074
3075
3076 bool Buffer::readFileHelper(FileName const & s)
3077 {
3078         // File information about normal file
3079         if (!s.exists()) {
3080                 docstring const file = makeDisplayPath(s.absFilename(), 50);
3081                 docstring text = bformat(_("The specified document\n%1$s"
3082                                                      "\ncould not be read."), file);
3083                 Alert::error(_("Could not read document"), text);
3084                 return false;
3085         }
3086
3087         // Check if emergency save file exists and is newer.
3088         FileName const e(s.absFilename() + ".emergency");
3089
3090         if (e.exists() && s.exists() && e.lastModified() > s.lastModified()) {
3091                 docstring const file = makeDisplayPath(s.absFilename(), 20);
3092                 docstring const text =
3093                         bformat(_("An emergency save of the document "
3094                                   "%1$s exists.\n\n"
3095                                                "Recover emergency save?"), file);
3096                 switch (Alert::prompt(_("Load emergency save?"), text, 0, 2,
3097                                       _("&Recover"),  _("&Load Original"),
3098                                       _("&Cancel")))
3099                 {
3100                 case 0: {
3101                         // the file is not saved if we load the emergency file.
3102                         markDirty();
3103                         docstring str;
3104                         bool res;
3105
3106                         if ((res = readFile(e)) == success)
3107                                 str = _("Document was successfully recovered.");
3108                         else
3109                                 str = _("Document was NOT successfully recovered.");
3110                         str += "\n\n" + _("Remove emergency file now?");
3111
3112                         if (!Alert::prompt(_("Delete emergency file?"), str, 1, 1,
3113                                         _("&Remove"), _("&Keep it"))) {
3114                                 e.removeFile();
3115                                 if (res == success)
3116                                         Alert::warning(_("Emergency file deleted"),
3117                                                 _("Do not forget to save your file now!"), true);
3118                                 }
3119                         return res;
3120                 }
3121                 case 1:
3122                         if (!Alert::prompt(_("Delete emergency file?"),
3123                                         _("Remove emergency file now?"), 1, 1,
3124                                         _("&Remove"), _("&Keep it")))
3125                                 e.removeFile();
3126                         break;
3127                 default:
3128                         return false;
3129                 }
3130         }
3131
3132         // Now check if autosave file is newer.
3133         FileName const a(onlyPath(s.absFilename()) + '#' + onlyFilename(s.absFilename()) + '#');
3134
3135         if (a.exists() && s.exists() && a.lastModified() > s.lastModified()) {
3136                 docstring const file = makeDisplayPath(s.absFilename(), 20);
3137                 docstring const text =
3138                         bformat(_("The backup of the document "
3139                                   "%1$s is newer.\n\nLoad the "
3140                                                "backup instead?"), file);
3141                 switch (Alert::prompt(_("Load backup?"), text, 0, 2,
3142                                       _("&Load backup"), _("Load &original"),
3143                                       _("&Cancel") ))
3144                 {
3145                 case 0:
3146                         // the file is not saved if we load the autosave file.
3147                         markDirty();
3148                         return readFile(a);
3149                 case 1:
3150                         // Here we delete the autosave
3151                         a.removeFile();
3152                         break;
3153                 default:
3154                         return false;
3155                 }
3156         }
3157         return readFile(s);
3158 }
3159
3160
3161 bool Buffer::loadLyXFile(FileName const & s)
3162 {
3163         if (s.isReadableFile()) {
3164                 if (readFileHelper(s)) {
3165                         lyxvc().file_found_hook(s);
3166                         if (!s.isWritable())
3167                                 setReadonly(true);
3168                         return true;
3169                 }
3170         } else {
3171                 docstring const file = makeDisplayPath(s.absFilename(), 20);
3172                 // Here we probably should run
3173                 if (LyXVC::file_not_found_hook(s)) {
3174                         docstring const text =
3175                                 bformat(_("Do you want to retrieve the document"
3176                                                        " %1$s from version control?"), file);
3177                         int const ret = Alert::prompt(_("Retrieve from version control?"),
3178                                 text, 0, 1, _("&Retrieve"), _("&Cancel"));
3179
3180                         if (ret == 0) {
3181                                 // How can we know _how_ to do the checkout?
3182                                 // With the current VC support it has to be,
3183                                 // a RCS file since CVS do not have special ,v files.
3184                                 RCS::retrieve(s);
3185                                 return loadLyXFile(s);
3186                         }
3187                 }
3188         }
3189         return false;
3190 }
3191
3192
3193 void Buffer::bufferErrors(TeXErrors const & terr, ErrorList & errorList) const
3194 {
3195         TeXErrors::Errors::const_iterator cit = terr.begin();
3196         TeXErrors::Errors::const_iterator end = terr.end();
3197
3198         for (; cit != end; ++cit) {
3199                 int id_start = -1;
3200                 int pos_start = -1;
3201                 int errorRow = cit->error_in_line;
3202                 bool found = d->texrow.getIdFromRow(errorRow, id_start,
3203                                                        pos_start);
3204                 int id_end = -1;
3205                 int pos_end = -1;
3206                 do {
3207                         ++errorRow;
3208                         found = d->texrow.getIdFromRow(errorRow, id_end, pos_end);
3209                 } while (found && id_start == id_end && pos_start == pos_end);
3210
3211                 errorList.push_back(ErrorItem(cit->error_desc,
3212                         cit->error_text, id_start, pos_start, pos_end));
3213         }
3214 }
3215
3216
3217 void Buffer::setBuffersForInsets() const
3218 {
3219         inset().setBuffer(const_cast<Buffer &>(*this)); 
3220 }
3221
3222
3223 void Buffer::updateLabels(UpdateScope scope) const
3224 {
3225         // Use the master text class also for child documents
3226         Buffer const * const master = masterBuffer();
3227         DocumentClass const & textclass = master->params().documentClass();
3228
3229         // keep the buffers to be children in this set. If the call from the
3230         // master comes back we can see which of them were actually seen (i.e.
3231         // via an InsetInclude). The remaining ones in the set need still be updated.
3232         static std::set<Buffer const *> bufToUpdate;
3233         if (scope == UpdateMaster) {
3234                 // If this is a child document start with the master
3235                 if (master != this) {
3236                         bufToUpdate.insert(this);
3237                         master->updateLabels();
3238                         // Do this here in case the master has no gui associated with it. Then, 
3239                         // the TocModel is not updated and TocModel::toc_ is invalid (bug 5699).
3240                         if (!master->gui_)
3241                                 structureChanged();
3242
3243                         // was buf referenced from the master (i.e. not in bufToUpdate anymore)?
3244                         if (bufToUpdate.find(this) == bufToUpdate.end())
3245                                 return;
3246                 }
3247
3248                 // start over the counters in the master
3249                 textclass.counters().reset();
3250         }
3251
3252         // update will be done below for this buffer
3253         bufToUpdate.erase(this);
3254
3255         // update all caches
3256         clearReferenceCache();
3257         updateMacros();
3258
3259         Buffer & cbuf = const_cast<Buffer &>(*this);
3260
3261         LASSERT(!text().paragraphs().empty(), /**/);
3262
3263         // do the real work
3264         ParIterator parit = cbuf.par_iterator_begin();
3265         updateLabels(parit);
3266
3267         if (master != this)
3268                 // TocBackend update will be done later.
3269                 return;
3270
3271         cbuf.tocBackend().update();
3272         if (scope == UpdateMaster)
3273                 cbuf.structureChanged();
3274 }
3275
3276
3277 static depth_type getDepth(DocIterator const & it)
3278 {
3279         depth_type depth = 0;
3280         for (size_t i = 0 ; i < it.depth() ; ++i)
3281                 if (!it[i].inset().inMathed())
3282                         depth += it[i].paragraph().getDepth() + 1;
3283         // remove 1 since the outer inset does not count
3284         return depth - 1;
3285 }
3286
3287 static depth_type getItemDepth(ParIterator const & it)
3288 {
3289         Paragraph const & par = *it;
3290         LabelType const labeltype = par.layout().labeltype;
3291
3292         if (labeltype != LABEL_ENUMERATE && labeltype != LABEL_ITEMIZE)
3293                 return 0;
3294
3295         // this will hold the lowest depth encountered up to now.
3296         depth_type min_depth = getDepth(it);
3297         ParIterator prev_it = it;
3298         while (true) {
3299                 if (prev_it.pit())
3300                         --prev_it.top().pit();
3301                 else {
3302                         // start of nested inset: go to outer par
3303                         prev_it.pop_back();
3304                         if (prev_it.empty()) {
3305                                 // start of document: nothing to do
3306                                 return 0;
3307                         }
3308                 }
3309
3310                 // We search for the first paragraph with same label
3311                 // that is not more deeply nested.
3312                 Paragraph & prev_par = *prev_it;
3313                 depth_type const prev_depth = getDepth(prev_it);
3314                 if (labeltype == prev_par.layout().labeltype) {
3315                         if (prev_depth < min_depth)
3316                                 return prev_par.itemdepth + 1;
3317                         if (prev_depth == min_depth)
3318                                 return prev_par.itemdepth;
3319                 }
3320                 min_depth = min(min_depth, prev_depth);
3321                 // small optimization: if we are at depth 0, we won't
3322                 // find anything else
3323                 if (prev_depth == 0)
3324                         return 0;
3325         }
3326 }
3327
3328
3329 static bool needEnumCounterReset(ParIterator const & it)
3330 {
3331         Paragraph const & par = *it;
3332         LASSERT(par.layout().labeltype == LABEL_ENUMERATE, /**/);
3333         depth_type const cur_depth = par.getDepth();
3334         ParIterator prev_it = it;
3335         while (prev_it.pit()) {
3336                 --prev_it.top().pit();
3337                 Paragraph const & prev_par = *prev_it;
3338                 if (prev_par.getDepth() <= cur_depth)
3339                         return  prev_par.layout().labeltype != LABEL_ENUMERATE;
3340         }
3341         // start of nested inset: reset
3342         return true;
3343 }
3344
3345
3346 // set the label of a paragraph. This includes the counters.
3347 static void setLabel(Buffer const & buf, ParIterator & it)
3348 {
3349         BufferParams const & bp = buf.masterBuffer()->params();
3350         DocumentClass const & textclass = bp.documentClass();
3351         Paragraph & par = it.paragraph();
3352         Layout const & layout = par.layout();
3353         Counters & counters = textclass.counters();
3354
3355         if (par.params().startOfAppendix()) {
3356                 // FIXME: only the counter corresponding to toplevel
3357                 // sectionning should be reset
3358                 counters.reset();
3359                 counters.appendix(true);
3360         }
3361         par.params().appendix(counters.appendix());
3362
3363         // Compute the item depth of the paragraph
3364         par.itemdepth = getItemDepth(it);
3365
3366         if (layout.margintype == MARGIN_MANUAL
3367             || layout.latextype == LATEX_BIB_ENVIRONMENT) {
3368                 if (par.params().labelWidthString().empty())
3369                         par.params().labelWidthString(par.expandLabel(layout, bp));
3370         } else {
3371                 par.params().labelWidthString(docstring());
3372         }
3373
3374         switch(layout.labeltype) {
3375         case LABEL_COUNTER:
3376                 if (layout.toclevel <= bp.secnumdepth
3377                     && (layout.latextype != LATEX_ENVIRONMENT
3378                         || it.text()->isFirstInSequence(it.pit()))) {
3379                         counters.step(layout.counter);
3380                         par.params().labelString(
3381                                 par.expandLabel(layout, bp));
3382                 } else
3383                         par.params().labelString(docstring());
3384                 break;
3385
3386         case LABEL_ITEMIZE: {
3387                 // At some point of time we should do something more
3388                 // clever here, like:
3389                 //   par.params().labelString(
3390                 //    bp.user_defined_bullet(par.itemdepth).getText());
3391                 // for now, use a simple hardcoded label
3392                 docstring itemlabel;
3393                 switch (par.itemdepth) {
3394                 case 0:
3395                         itemlabel = char_type(0x2022);
3396                         break;
3397                 case 1:
3398                         itemlabel = char_type(0x2013);
3399                         break;
3400                 case 2:
3401                         itemlabel = char_type(0x2217);
3402                         break;
3403                 case 3:
3404                         itemlabel = char_type(0x2219); // or 0x00b7
3405                         break;
3406                 }
3407                 par.params().labelString(itemlabel);
3408                 break;
3409         }
3410
3411         case LABEL_ENUMERATE: {
3412                 docstring enumcounter = layout.counter.empty() ? from_ascii("enum") : layout.counter;
3413
3414                 switch (par.itemdepth) {
3415                 case 2:
3416                         enumcounter += 'i';
3417                 case 1:
3418                         enumcounter += 'i';
3419                 case 0:
3420                         enumcounter += 'i';
3421                         break;
3422                 case 3:
3423                         enumcounter += "iv";
3424                         break;
3425                 default:
3426                         // not a valid enumdepth...
3427                         break;
3428                 }
3429
3430                 // Maybe we have to reset the enumeration counter.
3431                 if (needEnumCounterReset(it))
3432                         counters.reset(enumcounter);
3433                 counters.step(enumcounter);
3434
3435                 string const & lang = par.getParLanguage(bp)->code();
3436                 par.params().labelString(counters.theCounter(enumcounter, lang));
3437
3438                 break;
3439         }
3440
3441         case LABEL_SENSITIVE: {
3442                 string const & type = counters.current_float();
3443                 docstring full_label;
3444                 if (type.empty())
3445                         full_label = buf.B_("Senseless!!! ");
3446                 else {
3447                         docstring name = buf.B_(textclass.floats().getType(type).name());
3448                         if (counters.hasCounter(from_utf8(type))) {
3449                                 string const & lang = par.getParLanguage(bp)->code();
3450                                 counters.step(from_utf8(type));
3451                                 full_label = bformat(from_ascii("%1$s %2$s:"), 
3452                                                      name, 
3453                                                      counters.theCounter(from_utf8(type), lang));
3454                         } else
3455                                 full_label = bformat(from_ascii("%1$s #:"), name);      
3456                 }
3457                 par.params().labelString(full_label);   
3458                 break;
3459         }
3460
3461         case LABEL_NO_LABEL:
3462                 par.params().labelString(docstring());
3463                 break;
3464
3465         case LABEL_MANUAL:
3466         case LABEL_TOP_ENVIRONMENT:
3467         case LABEL_CENTERED_TOP_ENVIRONMENT:
3468         case LABEL_STATIC:      
3469         case LABEL_BIBLIO:
3470                 par.params().labelString(par.expandLabel(layout, bp));
3471                 break;
3472         }
3473 }
3474
3475
3476 void Buffer::updateLabels(ParIterator & parit) const
3477 {
3478         LASSERT(parit.pit() == 0, /**/);
3479
3480         // set the position of the text in the buffer to be able
3481         // to resolve macros in it. This has nothing to do with
3482         // labels, but by putting it here we avoid implementing
3483         // a whole bunch of traversal routines just for this call.
3484         parit.text()->setMacrocontextPosition(parit);
3485
3486         depth_type maxdepth = 0;
3487         pit_type const lastpit = parit.lastpit();
3488         for ( ; parit.pit() <= lastpit ; ++parit.pit()) {
3489                 // reduce depth if necessary
3490                 parit->params().depth(min(parit->params().depth(), maxdepth));
3491                 maxdepth = parit->getMaxDepthAfter();
3492
3493                 // set the counter for this paragraph
3494                 setLabel(*this, parit);
3495
3496                 // Now the insets
3497                 InsetList::const_iterator iit = parit->insetList().begin();
3498                 InsetList::const_iterator end = parit->insetList().end();
3499                 for (; iit != end; ++iit) {
3500                         parit.pos() = iit->pos;
3501                         iit->inset->updateLabels(parit);
3502                 }
3503         }
3504 }
3505
3506
3507 int Buffer::spellCheck(DocIterator & from, DocIterator & to,
3508         WordLangTuple & word_lang, docstring_list & suggestions) const
3509 {
3510         int progress = 0;
3511         WordLangTuple wl;
3512         suggestions.clear();
3513         word_lang = WordLangTuple();
3514         // OK, we start from here.
3515         DocIterator const end = doc_iterator_end(this);
3516         for (; from != end; from.forwardPos()) {
3517                 // We are only interested in text so remove the math CursorSlice.
3518                 while (from.inMathed())
3519                         from.forwardInset();
3520                 to = from;
3521                 if (from.paragraph().spellCheck(from.pos(), to.pos(), wl, suggestions)) {
3522                         word_lang = wl;
3523                         break;
3524                 }
3525                 from = to;
3526                 ++progress;
3527         }
3528         return progress;
3529 }
3530
3531 } // namespace lyx