]> git.lyx.org Git - lyx.git/blob - src/Buffer.cpp
ed94503c14631e77844a214f279e12613ad127a6
[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 "Cursor.h"
28 #include "CutAndPaste.h"
29 #include "DispatchResult.h"
30 #include "DocIterator.h"
31 #include "BufferEncodings.h"
32 #include "ErrorList.h"
33 #include "Exporter.h"
34 #include "Format.h"
35 #include "FuncRequest.h"
36 #include "FuncStatus.h"
37 #include "IndicesList.h"
38 #include "InsetIterator.h"
39 #include "InsetList.h"
40 #include "Language.h"
41 #include "LaTeXFeatures.h"
42 #include "LaTeX.h"
43 #include "Layout.h"
44 #include "Lexer.h"
45 #include "LyXAction.h"
46 #include "LyX.h"
47 #include "LyXRC.h"
48 #include "LyXVC.h"
49 #include "output_docbook.h"
50 #include "output.h"
51 #include "output_latex.h"
52 #include "output_xhtml.h"
53 #include "output_plaintext.h"
54 #include "Paragraph.h"
55 #include "ParagraphParameters.h"
56 #include "ParIterator.h"
57 #include "PDFOptions.h"
58 #include "SpellChecker.h"
59 #include "sgml.h"
60 #include "TexRow.h"
61 #include "Text.h"
62 #include "TextClass.h"
63 #include "TocBackend.h"
64 #include "Undo.h"
65 #include "VCBackend.h"
66 #include "version.h"
67 #include "WordLangTuple.h"
68 #include "WordList.h"
69
70 #include "insets/InsetBibtex.h"
71 #include "insets/InsetBranch.h"
72 #include "insets/InsetInclude.h"
73 #include "insets/InsetTabular.h"
74 #include "insets/InsetText.h"
75
76 #include "mathed/InsetMathHull.h"
77 #include "mathed/MacroTable.h"
78 #include "mathed/MathMacroTemplate.h"
79 #include "mathed/MathSupport.h"
80
81 #include "graphics/PreviewLoader.h"
82
83 #include "frontends/alert.h"
84 #include "frontends/Delegates.h"
85 #include "frontends/WorkAreaManager.h"
86
87 #include "support/lassert.h"
88 #include "support/convert.h"
89 #include "support/debug.h"
90 #include "support/docstring_list.h"
91 #include "support/ExceptionMessage.h"
92 #include "support/FileName.h"
93 #include "support/FileNameList.h"
94 #include "support/filetools.h"
95 #include "support/ForkedCalls.h"
96 #include "support/gettext.h"
97 #include "support/gzstream.h"
98 #include "support/lstrings.h"
99 #include "support/lyxalgo.h"
100 #include "support/mutex.h"
101 #include "support/os.h"
102 #include "support/Package.h"
103 #include "support/PathChanger.h"
104 #include "support/Systemcall.h"
105 #include "support/TempFile.h"
106 #include "support/textutils.h"
107 #include "support/types.h"
108
109 #include "support/bind.h"
110 #include "support/shared_ptr.h"
111
112 #include <algorithm>
113 #include <fstream>
114 #include <iomanip>
115 #include <map>
116 #include <set>
117 #include <sstream>
118 #include <vector>
119
120 using namespace std;
121 using namespace lyx::support;
122 using namespace lyx::graphics;
123
124 namespace lyx {
125
126 namespace Alert = frontend::Alert;
127 namespace os = support::os;
128
129 namespace {
130
131 int const LYX_FORMAT = LYX_FORMAT_LYX;
132
133 typedef map<string, bool> DepClean;
134 typedef map<docstring, pair<InsetLabel const *, Buffer::References> > RefCache;
135
136 void showPrintError(string const & name)
137 {
138         docstring str = bformat(_("Could not print the document %1$s.\n"
139                                             "Check that your printer is set up correctly."),
140                              makeDisplayPath(name, 50));
141         Alert::error(_("Print document failed"), str);
142 }
143
144 } // namespace anon
145
146
147 // A storehouse for the cloned buffers.
148 list<CloneList *> cloned_buffers;
149
150
151 class Buffer::Impl
152 {
153 public:
154         Impl(Buffer * owner, FileName const & file, bool readonly, Buffer const * cloned_buffer);
155
156         ~Impl()
157         {
158                 delete preview_loader_;
159                 if (wa_) {
160                         wa_->closeAll();
161                         delete wa_;
162                 }
163                 delete inset;
164         }
165
166         /// search for macro in local (buffer) table or in children
167         MacroData const * getBufferMacro(docstring const & name,
168                 DocIterator const & pos) const;
169
170         /// Update macro table starting with position of it \param it in some
171         /// text inset.
172         void updateMacros(DocIterator & it, DocIterator & scope);
173         ///
174         void setLabel(ParIterator & it, UpdateType utype) const;
175
176         /** If we have branches that use the file suffix
177             feature, return the file name with suffix appended.
178         */
179         support::FileName exportFileName() const;
180
181         Buffer * owner_;
182
183         BufferParams params;
184         LyXVC lyxvc;
185         FileName temppath;
186         mutable TexRow texrow;
187
188         /// need to regenerate .tex?
189         DepClean dep_clean;
190
191         /// is save needed?
192         mutable bool lyx_clean;
193
194         /// is autosave needed?
195         mutable bool bak_clean;
196
197         /// is this an unnamed file (New...)?
198         bool unnamed;
199
200         /// is this an internal bufffer?
201         bool internal_buffer;
202
203         /// buffer is r/o
204         bool read_only;
205
206         /// name of the file the buffer is associated with.
207         FileName filename;
208
209         /** Set to true only when the file is fully loaded.
210          *  Used to prevent the premature generation of previews
211          *  and by the citation inset.
212          */
213         bool file_fully_loaded;
214
215         /// Ignore the parent (e.g. when exporting a child standalone)?
216         bool ignore_parent;
217
218         ///
219         mutable TocBackend toc_backend;
220
221         /// macro tables
222         struct ScopeMacro {
223                 ScopeMacro() {}
224                 ScopeMacro(DocIterator const & s, MacroData const & m)
225                         : scope(s), macro(m) {}
226                 DocIterator scope;
227                 MacroData macro;
228         };
229         typedef map<DocIterator, ScopeMacro> PositionScopeMacroMap;
230         typedef map<docstring, PositionScopeMacroMap> NamePositionScopeMacroMap;
231         /// map from the macro name to the position map,
232         /// which maps the macro definition position to the scope and the MacroData.
233         NamePositionScopeMacroMap macros;
234         /// This seem to change the way Buffer::getMacro() works
235         mutable bool macro_lock;
236
237         /// positions of child buffers in the buffer
238         typedef map<Buffer const * const, DocIterator> BufferPositionMap;
239         struct ScopeBuffer {
240                 ScopeBuffer() {}
241                 ScopeBuffer(DocIterator const & s,Buffer const * b)
242                         : scope(s), buffer(b) {}
243                 DocIterator scope;
244                 Buffer const * buffer;
245         };
246         typedef map<DocIterator, ScopeBuffer> PositionScopeBufferMap;
247         /// position of children buffers in this buffer
248         BufferPositionMap children_positions;
249         /// map from children inclusion positions to their scope and their buffer
250         PositionScopeBufferMap position_to_children;
251
252         /// Keeps track of old buffer filePath() for save-as operations
253         string old_position;
254
255         /// Container for all sort of Buffer dependant errors.
256         map<string, ErrorList> errorLists;
257
258         /// timestamp and checksum used to test if the file has been externally
259         /// modified. (Used to properly enable 'File->Revert to saved', bug 4114).
260         time_t timestamp_;
261         unsigned long checksum_;
262
263         ///
264         frontend::WorkAreaManager * wa_;
265         ///
266         frontend::GuiBufferDelegate * gui_;
267
268         ///
269         Undo undo_;
270
271         /// A cache for the bibfiles (including bibfiles of loaded child
272         /// documents), needed for appropriate update of natbib labels.
273         mutable support::FileNameList bibfiles_cache_;
274
275         // FIXME The caching mechanism could be improved. At present, we have a
276         // cache for each Buffer, that caches all the bibliography info for that
277         // Buffer. A more efficient solution would be to have a global cache per
278         // file, and then to construct the Buffer's bibinfo from that.
279         /// A cache for bibliography info
280         mutable BiblioInfo bibinfo_;
281         /// whether the bibinfo cache is valid
282         mutable bool bibinfo_cache_valid_;
283         /// whether the bibfile cache is valid
284         mutable bool bibfile_cache_valid_;
285         /// Cache of timestamps of .bib files
286         map<FileName, time_t> bibfile_status_;
287         /// Indicates whether the bibinfo has changed since the last time
288         /// we ran updateBuffer(), i.e., whether citation labels may need
289         /// to be updated.
290         mutable bool cite_labels_valid_;
291         /// these hold the file name and format, written to by Buffer::preview
292         /// and read from by LFUN_BUFFER_VIEW_CACHE.
293         FileName preview_file_;
294         string preview_format_;
295
296         mutable RefCache ref_cache_;
297
298         /// our Text that should be wrapped in an InsetText
299         InsetText * inset;
300
301         ///
302         PreviewLoader * preview_loader_;
303
304         /// This is here to force the test to be done whenever parent_buffer
305         /// is accessed.
306         Buffer const * parent() const
307         {
308                 // ignore_parent temporarily "orphans" a buffer
309                 // (e.g. if a child is compiled standalone)
310                 if (ignore_parent)
311                         return 0;
312                 // if parent_buffer is not loaded, then it has been unloaded,
313                 // which means that parent_buffer is an invalid pointer. So we
314                 // set it to null in that case.
315                 // however, the BufferList doesn't know about cloned buffers, so
316                 // they will always be regarded as unloaded. in that case, we hope
317                 // for the best.
318                 if (!cloned_buffer_ && !theBufferList().isLoaded(parent_buffer))
319                         parent_buffer = 0;
320                 return parent_buffer;
321         }
322
323         ///
324         void setParent(Buffer const * pb)
325         {
326                 if (parent_buffer == pb)
327                         // nothing to do
328                         return;
329                 if (!cloned_buffer_ && parent_buffer && pb)
330                         LYXERR0("Warning: a buffer should not have two parents!");
331                 parent_buffer = pb;
332                 if (!cloned_buffer_ && parent_buffer) {
333                         parent_buffer->invalidateBibfileCache();
334                         parent_buffer->invalidateBibinfoCache();
335                 }
336         }
337
338         /// If non zero, this buffer is a clone of existing buffer \p cloned_buffer_
339         /// This one is useful for preview detached in a thread.
340         Buffer const * cloned_buffer_;
341         ///
342         CloneList * clone_list_;
343         /// are we in the process of exporting this buffer?
344         mutable bool doing_export;
345
346         /// compute statistics
347         /// \p from initial position
348         /// \p to points to the end position
349         void updateStatistics(DocIterator & from, DocIterator & to,
350                               bool skipNoOutput = true);
351         /// statistics accessor functions
352         int wordCount() const
353         {
354                 return word_count_;
355         }
356         int charCount(bool with_blanks) const
357         {
358                 return char_count_
359                 + (with_blanks ? blank_count_ : 0);
360         }
361
362 private:
363         /// So we can force access via the accessors.
364         mutable Buffer const * parent_buffer;
365
366         int word_count_;
367         int char_count_;
368         int blank_count_;
369
370 };
371
372
373 /// Creates the per buffer temporary directory
374 static FileName createBufferTmpDir()
375 {
376         // FIXME This would be the ideal application for a TempDir class (like
377         //       TempFile but for directories)
378         string counter;
379         {
380                 static int count;
381                 static Mutex mutex;
382                 Mutex::Locker locker(&mutex);
383                 counter = convert<string>(count++);
384         }
385         // We are in our own directory.  Why bother to mangle name?
386         // In fact I wrote this code to circumvent a problematic behaviour
387         // (bug?) of EMX mkstemp().
388         FileName tmpfl(package().temp_dir().absFileName() + "/lyx_tmpbuf" +
389                 counter);
390
391         if (!tmpfl.createDirectory(0777)) {
392                 throw ExceptionMessage(WarningException, _("Disk Error: "), bformat(
393                         _("LyX could not create the temporary directory '%1$s' (Disk is full maybe?)"),
394                         from_utf8(tmpfl.absFileName())));
395         }
396         return tmpfl;
397 }
398
399
400 Buffer::Impl::Impl(Buffer * owner, FileName const & file, bool readonly_,
401         Buffer const * cloned_buffer)
402         : owner_(owner), lyx_clean(true), bak_clean(true), unnamed(false),
403           internal_buffer(false), read_only(readonly_), filename(file),
404           file_fully_loaded(false), ignore_parent(false), toc_backend(owner),
405           macro_lock(false), timestamp_(0), checksum_(0), wa_(0), gui_(0),
406           undo_(*owner), bibinfo_cache_valid_(false), bibfile_cache_valid_(false),
407           cite_labels_valid_(false), inset(0), preview_loader_(0),
408           cloned_buffer_(cloned_buffer), clone_list_(0),
409           doing_export(false), parent_buffer(0),
410           word_count_(0), char_count_(0), blank_count_(0)
411 {
412         if (!cloned_buffer_) {
413                 temppath = createBufferTmpDir();
414                 lyxvc.setBuffer(owner_);
415                 if (use_gui)
416                         wa_ = new frontend::WorkAreaManager;
417                 return;
418         }
419         temppath = cloned_buffer_->d->temppath;
420         file_fully_loaded = true;
421         params = cloned_buffer_->d->params;
422         bibfiles_cache_ = cloned_buffer_->d->bibfiles_cache_;
423         bibinfo_ = cloned_buffer_->d->bibinfo_;
424         bibinfo_cache_valid_ = cloned_buffer_->d->bibinfo_cache_valid_;
425         bibfile_cache_valid_ = cloned_buffer_->d->bibfile_cache_valid_;
426         bibfile_status_ = cloned_buffer_->d->bibfile_status_;
427         cite_labels_valid_ = cloned_buffer_->d->cite_labels_valid_;
428         unnamed = cloned_buffer_->d->unnamed;
429         internal_buffer = cloned_buffer_->d->internal_buffer;
430         preview_file_ = cloned_buffer_->d->preview_file_;
431         preview_format_ = cloned_buffer_->d->preview_format_;
432 }
433
434
435 Buffer::Buffer(string const & file, bool readonly, Buffer const * cloned_buffer)
436         : d(new Impl(this, FileName(file), readonly, cloned_buffer))
437 {
438         LYXERR(Debug::INFO, "Buffer::Buffer()");
439         if (cloned_buffer) {
440                 d->inset = new InsetText(*cloned_buffer->d->inset);
441                 d->inset->setBuffer(*this);
442                 // FIXME: optimize this loop somewhat, maybe by creating a new
443                 // general recursive Inset::setId().
444                 DocIterator it = doc_iterator_begin(this);
445                 DocIterator cloned_it = doc_iterator_begin(cloned_buffer);
446                 for (; !it.atEnd(); it.forwardPar(), cloned_it.forwardPar())
447                         it.paragraph().setId(cloned_it.paragraph().id());
448         } else
449                 d->inset = new InsetText(this);
450         d->inset->getText(0)->setMacrocontextPosition(par_iterator_begin());
451 }
452
453
454 Buffer::~Buffer()
455 {
456         LYXERR(Debug::INFO, "Buffer::~Buffer()");
457         // here the buffer should take care that it is
458         // saved properly, before it goes into the void.
459
460         // GuiView already destroyed
461         d->gui_ = 0;
462
463         if (isInternal()) {
464                 // No need to do additional cleanups for internal buffer.
465                 delete d;
466                 return;
467         }
468
469         if (isClone()) {
470                 // this is in case of recursive includes: we won't try to delete
471                 // ourselves as a child.
472                 d->clone_list_->erase(this);
473                 // loop over children
474                 Impl::BufferPositionMap::iterator it = d->children_positions.begin();
475                 Impl::BufferPositionMap::iterator end = d->children_positions.end();
476                 for (; it != end; ++it) {
477                         Buffer * child = const_cast<Buffer *>(it->first);
478                                 if (d->clone_list_->erase(child))
479                                         delete child;
480                 }
481                 // if we're the master buffer, then we should get rid of the list
482                 // of clones
483                 if (!parent()) {
484                         // If this is not empty, we have leaked something. Worse, one of the
485                         // children still has a reference to this list. But we will try to
486                         // continue, rather than shut down.
487                         LATTEST(d->clone_list_->empty());
488                         list<CloneList *>::iterator it =
489                                 find(cloned_buffers.begin(), cloned_buffers.end(), d->clone_list_);
490                         if (it == cloned_buffers.end()) {
491                                 // We will leak in this case, but it is safe to continue.
492                                 LATTEST(false);
493                         } else
494                                 cloned_buffers.erase(it);
495                         delete d->clone_list_;
496                 }
497                 // FIXME Do we really need to do this right before we delete d?
498                 // clear references to children in macro tables
499                 d->children_positions.clear();
500                 d->position_to_children.clear();
501         } else {
502                 // loop over children
503                 Impl::BufferPositionMap::iterator it = d->children_positions.begin();
504                 Impl::BufferPositionMap::iterator end = d->children_positions.end();
505                 for (; it != end; ++it) {
506                         Buffer * child = const_cast<Buffer *>(it->first);
507                         if (theBufferList().isLoaded(child))
508                                 theBufferList().releaseChild(this, child);
509                 }
510
511                 if (!isClean()) {
512                         docstring msg = _("LyX attempted to close a document that had unsaved changes!\n");
513                         msg += emergencyWrite();
514                         Alert::warning(_("Attempting to close changed document!"), msg);
515                 }
516
517                 // FIXME Do we really need to do this right before we delete d?
518                 // clear references to children in macro tables
519                 d->children_positions.clear();
520                 d->position_to_children.clear();
521
522                 if (!d->temppath.destroyDirectory()) {
523                         Alert::warning(_("Could not remove temporary directory"),
524                                 bformat(_("Could not remove the temporary directory %1$s"),
525                                 from_utf8(d->temppath.absFileName())));
526                 }
527                 removePreviews();
528         }
529
530         delete d;
531 }
532
533
534 Buffer * Buffer::cloneFromMaster() const
535 {
536         BufferMap bufmap;
537         cloned_buffers.push_back(new CloneList);
538         CloneList * clones = cloned_buffers.back();
539
540         masterBuffer()->cloneWithChildren(bufmap, clones);
541
542         // make sure we got cloned
543         BufferMap::const_iterator bit = bufmap.find(this);
544         LASSERT(bit != bufmap.end(), return 0);
545         Buffer * cloned_buffer = bit->second;
546
547         return cloned_buffer;
548 }
549
550
551 void Buffer::cloneWithChildren(BufferMap & bufmap, CloneList * clones) const
552 {
553         // have we already been cloned?
554         if (bufmap.find(this) != bufmap.end())
555                 return;
556
557         Buffer * buffer_clone = new Buffer(fileName().absFileName(), false, this);
558
559         // The clone needs its own DocumentClass, since running updateBuffer() will
560         // modify it, and we would otherwise be sharing it with the original Buffer.
561         buffer_clone->params().makeDocumentClass(true);
562         ErrorList el;
563         cap::switchBetweenClasses(
564                         params().documentClassPtr(), buffer_clone->params().documentClassPtr(),
565                         static_cast<InsetText &>(buffer_clone->inset()), el);
566
567         bufmap[this] = buffer_clone;
568         clones->insert(buffer_clone);
569         buffer_clone->d->clone_list_ = clones;
570         buffer_clone->d->macro_lock = true;
571         buffer_clone->d->children_positions.clear();
572
573         // FIXME (Abdel 09/01/2010): this is too complicated. The whole children_positions and
574         // math macro caches need to be rethought and simplified.
575         // I am not sure wether we should handle Buffer cloning here or in BufferList.
576         // Right now BufferList knows nothing about buffer clones.
577         Impl::PositionScopeBufferMap::iterator it = d->position_to_children.begin();
578         Impl::PositionScopeBufferMap::iterator end = d->position_to_children.end();
579         for (; it != end; ++it) {
580                 DocIterator dit = it->first.clone(buffer_clone);
581                 dit.setBuffer(buffer_clone);
582                 Buffer * child = const_cast<Buffer *>(it->second.buffer);
583
584                 child->cloneWithChildren(bufmap, clones);
585                 BufferMap::iterator const bit = bufmap.find(child);
586                 LASSERT(bit != bufmap.end(), continue);
587                 Buffer * child_clone = bit->second;
588
589                 Inset * inset = dit.nextInset();
590                 LASSERT(inset && inset->lyxCode() == INCLUDE_CODE, continue);
591                 InsetInclude * inset_inc = static_cast<InsetInclude *>(inset);
592                 inset_inc->setChildBuffer(child_clone);
593                 child_clone->d->setParent(buffer_clone);
594                 // FIXME Do we need to do this now, or can we wait until we run updateMacros()?
595                 buffer_clone->setChild(dit, child_clone);
596         }
597         buffer_clone->d->macro_lock = false;
598         return;
599 }
600
601
602 Buffer * Buffer::cloneBufferOnly() const {
603         cloned_buffers.push_back(new CloneList);
604         CloneList * clones = cloned_buffers.back();
605         Buffer * buffer_clone = new Buffer(fileName().absFileName(), false, this);
606
607         // The clone needs its own DocumentClass, since running updateBuffer() will
608         // modify it, and we would otherwise be sharing it with the original Buffer.
609         buffer_clone->params().makeDocumentClass(true);
610         ErrorList el;
611         cap::switchBetweenClasses(
612                         params().documentClassPtr(), buffer_clone->params().documentClassPtr(),
613                         static_cast<InsetText &>(buffer_clone->inset()), el);
614
615         clones->insert(buffer_clone);
616         buffer_clone->d->clone_list_ = clones;
617
618         // we won't be cloning the children
619         buffer_clone->d->children_positions.clear();
620         return buffer_clone;
621 }
622
623
624 bool Buffer::isClone() const
625 {
626         return d->cloned_buffer_;
627 }
628
629
630 void Buffer::changed(bool update_metrics) const
631 {
632         if (d->wa_)
633                 d->wa_->redrawAll(update_metrics);
634 }
635
636
637 frontend::WorkAreaManager & Buffer::workAreaManager() const
638 {
639         LBUFERR(d->wa_);
640         return *d->wa_;
641 }
642
643
644 Text & Buffer::text() const
645 {
646         return d->inset->text();
647 }
648
649
650 Inset & Buffer::inset() const
651 {
652         return *d->inset;
653 }
654
655
656 BufferParams & Buffer::params()
657 {
658         return d->params;
659 }
660
661
662 BufferParams const & Buffer::params() const
663 {
664         return d->params;
665 }
666
667
668 BufferParams const & Buffer::masterParams() const
669 {
670         if (masterBuffer() == this)
671                 return params();
672
673         BufferParams & mparams = const_cast<Buffer *>(masterBuffer())->params();
674         // Copy child authors to the params. We need those pointers.
675         AuthorList const & child_authors = params().authors();
676         AuthorList::Authors::const_iterator it = child_authors.begin();
677         for (; it != child_authors.end(); ++it)
678                 mparams.authors().record(*it);
679         return mparams;
680 }
681
682
683 double Buffer::fontScalingFactor() const
684 {
685         return isExporting() ? 75.0 * params().html_math_img_scale
686                 : 0.01 * lyxrc.dpi * lyxrc.zoom * lyxrc.preview_scale_factor * params().display_pixel_ratio;
687 }
688
689
690 ParagraphList & Buffer::paragraphs()
691 {
692         return text().paragraphs();
693 }
694
695
696 ParagraphList const & Buffer::paragraphs() const
697 {
698         return text().paragraphs();
699 }
700
701
702 LyXVC & Buffer::lyxvc()
703 {
704         return d->lyxvc;
705 }
706
707
708 LyXVC const & Buffer::lyxvc() const
709 {
710         return d->lyxvc;
711 }
712
713
714 string const Buffer::temppath() const
715 {
716         return d->temppath.absFileName();
717 }
718
719
720 TexRow & Buffer::texrow()
721 {
722         return d->texrow;
723 }
724
725
726 TexRow const & Buffer::texrow() const
727 {
728         return d->texrow;
729 }
730
731
732 TocBackend & Buffer::tocBackend() const
733 {
734         return d->toc_backend;
735 }
736
737
738 Undo & Buffer::undo()
739 {
740         return d->undo_;
741 }
742
743
744 void Buffer::setChild(DocIterator const & dit, Buffer * child)
745 {
746         d->children_positions[child] = dit;
747 }
748
749
750 string Buffer::latexName(bool const no_path) const
751 {
752         FileName latex_name =
753                 makeLatexName(d->exportFileName());
754         return no_path ? latex_name.onlyFileName()
755                 : latex_name.absFileName();
756 }
757
758
759 FileName Buffer::Impl::exportFileName() const
760 {
761         docstring const branch_suffix =
762                 params.branchlist().getFileNameSuffix();
763         if (branch_suffix.empty())
764                 return filename;
765
766         string const name = filename.onlyFileNameWithoutExt()
767                 + to_utf8(branch_suffix);
768         FileName res(filename.onlyPath().absFileName() + "/" + name);
769         res.changeExtension(filename.extension());
770
771         return res;
772 }
773
774
775 string Buffer::logName(LogType * type) const
776 {
777         string const filename = latexName(false);
778
779         if (filename.empty()) {
780                 if (type)
781                         *type = latexlog;
782                 return string();
783         }
784
785         string const path = temppath();
786
787         FileName const fname(addName(temppath(),
788                                      onlyFileName(changeExtension(filename,
789                                                                   ".log"))));
790
791         // FIXME: how do we know this is the name of the build log?
792         FileName const bname(
793                 addName(path, onlyFileName(
794                         changeExtension(filename,
795                                         formats.extension(params().bufferFormat()) + ".out"))));
796
797         // Also consider the master buffer log file
798         FileName masterfname = fname;
799         LogType mtype = latexlog;
800         if (masterBuffer() != this) {
801                 string const mlogfile = masterBuffer()->logName(&mtype);
802                 masterfname = FileName(mlogfile);
803         }
804
805         // If no Latex log or Build log is newer, show Build log
806         if (bname.exists() &&
807             ((!fname.exists() && !masterfname.exists())
808              || (fname.lastModified() < bname.lastModified()
809                  && masterfname.lastModified() < bname.lastModified()))) {
810                 LYXERR(Debug::FILES, "Log name calculated as: " << bname);
811                 if (type)
812                         *type = buildlog;
813                 return bname.absFileName();
814         // If we have a newer master file log or only a master log, show this
815         } else if (fname != masterfname
816                    && (!fname.exists() && (masterfname.exists()
817                    || fname.lastModified() < masterfname.lastModified()))) {
818                 LYXERR(Debug::FILES, "Log name calculated as: " << masterfname);
819                 if (type)
820                         *type = mtype;
821                 return masterfname.absFileName();
822         }
823         LYXERR(Debug::FILES, "Log name calculated as: " << fname);
824         if (type)
825                         *type = latexlog;
826         return fname.absFileName();
827 }
828
829
830 void Buffer::setReadonly(bool const flag)
831 {
832         if (d->read_only != flag) {
833                 d->read_only = flag;
834                 changed(false);
835         }
836 }
837
838
839 void Buffer::setFileName(FileName const & fname)
840 {
841         bool const changed = fname != d->filename;
842         d->filename = fname;
843         if (changed)
844                 lyxvc().file_found_hook(fname);
845         setReadonly(d->filename.isReadOnly());
846         saveCheckSum();
847         updateTitles();
848 }
849
850
851 int Buffer::readHeader(Lexer & lex)
852 {
853         int unknown_tokens = 0;
854         int line = -1;
855         int begin_header_line = -1;
856
857         // Initialize parameters that may be/go lacking in header:
858         params().branchlist().clear();
859         params().preamble.erase();
860         params().options.erase();
861         params().master.erase();
862         params().float_placement.erase();
863         params().paperwidth.erase();
864         params().paperheight.erase();
865         params().leftmargin.erase();
866         params().rightmargin.erase();
867         params().topmargin.erase();
868         params().bottommargin.erase();
869         params().headheight.erase();
870         params().headsep.erase();
871         params().footskip.erase();
872         params().columnsep.erase();
873         params().fonts_cjk.erase();
874         params().listings_params.clear();
875         params().clearLayoutModules();
876         params().clearRemovedModules();
877         params().clearIncludedChildren();
878         params().pdfoptions().clear();
879         params().indiceslist().clear();
880         params().backgroundcolor = lyx::rgbFromHexName("#ffffff");
881         params().isbackgroundcolor = false;
882         params().fontcolor = RGBColor(0, 0, 0);
883         params().isfontcolor = false;
884         params().notefontcolor = RGBColor(0xCC, 0xCC, 0xCC);
885         params().boxbgcolor = RGBColor(0xFF, 0, 0);
886         params().html_latex_start.clear();
887         params().html_latex_end.clear();
888         params().html_math_img_scale = 1.0;
889         params().output_sync_macro.erase();
890         params().setLocalLayout(string(), false);
891         params().setLocalLayout(string(), true);
892
893         for (int i = 0; i < 4; ++i) {
894                 params().user_defined_bullet(i) = ITEMIZE_DEFAULTS[i];
895                 params().temp_bullet(i) = ITEMIZE_DEFAULTS[i];
896         }
897
898         ErrorList & errorList = d->errorLists["Parse"];
899
900         while (lex.isOK()) {
901                 string token;
902                 lex >> token;
903
904                 if (token.empty())
905                         continue;
906
907                 if (token == "\\end_header")
908                         break;
909
910                 ++line;
911                 if (token == "\\begin_header") {
912                         begin_header_line = line;
913                         continue;
914                 }
915
916                 LYXERR(Debug::PARSER, "Handling document header token: `"
917                                       << token << '\'');
918
919                 string unknown = params().readToken(lex, token, d->filename.onlyPath());
920                 if (!unknown.empty()) {
921                         if (unknown[0] != '\\' && token == "\\textclass") {
922                                 Alert::warning(_("Unknown document class"),
923                        bformat(_("Using the default document class, because the "
924                                               "class %1$s is unknown."), from_utf8(unknown)));
925                         } else {
926                                 ++unknown_tokens;
927                                 docstring const s = bformat(_("Unknown token: "
928                                                                         "%1$s %2$s\n"),
929                                                          from_utf8(token),
930                                                          lex.getDocString());
931                                 errorList.push_back(ErrorItem(_("Document header error"),
932                                         s, -1, 0, 0));
933                         }
934                 }
935         }
936         if (begin_header_line) {
937                 docstring const s = _("\\begin_header is missing");
938                 errorList.push_back(ErrorItem(_("Document header error"),
939                         s, -1, 0, 0));
940         }
941
942         params().makeDocumentClass();
943
944         return unknown_tokens;
945 }
946
947
948 // Uwe C. Schroeder
949 // changed to be public and have one parameter
950 // Returns true if "\end_document" is not read (Asger)
951 bool Buffer::readDocument(Lexer & lex)
952 {
953         ErrorList & errorList = d->errorLists["Parse"];
954         errorList.clear();
955
956         // remove dummy empty par
957         paragraphs().clear();
958
959         if (!lex.checkFor("\\begin_document")) {
960                 docstring const s = _("\\begin_document is missing");
961                 errorList.push_back(ErrorItem(_("Document header error"),
962                         s, -1, 0, 0));
963         }
964
965         readHeader(lex);
966
967         if (params().output_changes) {
968                 bool dvipost    = LaTeXFeatures::isAvailable("dvipost");
969                 bool xcolorulem = LaTeXFeatures::isAvailable("ulem") &&
970                                   LaTeXFeatures::isAvailable("xcolor");
971
972                 if (!dvipost && !xcolorulem) {
973                         Alert::warning(_("Changes not shown in LaTeX output"),
974                                        _("Changes will not be highlighted in LaTeX output, "
975                                          "because neither dvipost nor xcolor/ulem are installed.\n"
976                                          "Please install these packages or redefine "
977                                          "\\lyxadded and \\lyxdeleted in the LaTeX preamble."));
978                 } else if (!xcolorulem) {
979                         Alert::warning(_("Changes not shown in LaTeX output"),
980                                        _("Changes will not be highlighted in LaTeX output "
981                                          "when using pdflatex, because xcolor and ulem are not installed.\n"
982                                          "Please install both packages or redefine "
983                                          "\\lyxadded and \\lyxdeleted in the LaTeX preamble."));
984                 }
985         }
986
987         if (!parent() && !params().master.empty()) {
988                 FileName const master_file = makeAbsPath(params().master,
989                            onlyPath(absFileName()));
990                 if (isLyXFileName(master_file.absFileName())) {
991                         Buffer * master =
992                                 checkAndLoadLyXFile(master_file, true);
993                         if (master) {
994                                 // necessary e.g. after a reload
995                                 // to re-register the child (bug 5873)
996                                 // FIXME: clean up updateMacros (here, only
997                                 // child registering is needed).
998                                 master->updateMacros();
999                                 // set master as master buffer, but only
1000                                 // if we are a real child
1001                                 if (master->isChild(this))
1002                                         setParent(master);
1003                                 // if the master is not fully loaded
1004                                 // it is probably just loading this
1005                                 // child. No warning needed then.
1006                                 else if (master->isFullyLoaded())
1007                                         LYXERR0("The master '"
1008                                                 << params().master
1009                                                 << "' assigned to this document ("
1010                                                 << absFileName()
1011                                                 << ") does not include "
1012                                                 "this document. Ignoring the master assignment.");
1013                         }
1014                 }
1015         }
1016
1017         // assure we have a default index
1018         params().indiceslist().addDefault(B_("Index"));
1019
1020         // read main text
1021         bool const res = text().read(lex, errorList, d->inset);
1022
1023         // inform parent buffer about local macros
1024         if (parent()) {
1025                 Buffer const * pbuf = parent();
1026                 UserMacroSet::const_iterator cit = usermacros.begin();
1027                 UserMacroSet::const_iterator end = usermacros.end();
1028                 for (; cit != end; ++cit)
1029                         pbuf->usermacros.insert(*cit);
1030         }
1031         usermacros.clear();
1032         updateMacros();
1033         updateMacroInstances(InternalUpdate);
1034         return res;
1035 }
1036
1037
1038 bool Buffer::importString(string const & format, docstring const & contents, ErrorList & errorList)
1039 {
1040         Format const * fmt = formats.getFormat(format);
1041         if (!fmt)
1042                 return false;
1043         // It is important to use the correct extension here, since some
1044         // converters create a wrong output file otherwise (e.g. html2latex)
1045         TempFile const tempfile("Buffer_importStringXXXXXX." + fmt->extension());
1046         FileName const name(tempfile.name());
1047         ofdocstream os(name.toFilesystemEncoding().c_str());
1048         // Do not convert os implicitly to bool, since that is forbidden in C++11.
1049         bool const success = !(os << contents).fail();
1050         os.close();
1051
1052         bool converted = false;
1053         if (success) {
1054                 params().compressed = false;
1055
1056                 // remove dummy empty par
1057                 paragraphs().clear();
1058
1059                 converted = importFile(format, name, errorList);
1060         }
1061
1062         if (name.exists())
1063                 name.removeFile();
1064         return converted;
1065 }
1066
1067
1068 bool Buffer::importFile(string const & format, FileName const & name, ErrorList & errorList)
1069 {
1070         if (!theConverters().isReachable(format, "lyx"))
1071                 return false;
1072
1073         TempFile const tempfile("Buffer_importFileXXXXXX.lyx");
1074         FileName const lyx(tempfile.name());
1075         if (theConverters().convert(0, name, lyx, name, format, "lyx", errorList))
1076                 return readFile(lyx) == ReadSuccess;
1077
1078         return false;
1079 }
1080
1081
1082 bool Buffer::readString(string const & s)
1083 {
1084         params().compressed = false;
1085
1086         Lexer lex;
1087         istringstream is(s);
1088         lex.setStream(is);
1089         TempFile tempfile("Buffer_readStringXXXXXX.lyx");
1090         FileName const fn = tempfile.name();
1091
1092         int file_format;
1093         bool success = parseLyXFormat(lex, fn, file_format) == ReadSuccess;
1094
1095         if (success && file_format != LYX_FORMAT) {
1096                 // We need to call lyx2lyx, so write the input to a file
1097                 ofstream os(fn.toFilesystemEncoding().c_str());
1098                 os << s;
1099                 os.close();
1100                 // lyxvc in readFile
1101                 if (readFile(fn) != ReadSuccess)
1102                         success = false;
1103         }
1104         else if (success)
1105                 if (readDocument(lex))
1106                         success = false;
1107         return success;
1108 }
1109
1110
1111 Buffer::ReadStatus Buffer::readFile(FileName const & fn)
1112 {
1113         FileName fname(fn);
1114         Lexer lex;
1115         if (!lex.setFile(fname)) {
1116                 Alert::error(_("File Not Found"),
1117                         bformat(_("Unable to open file `%1$s'."),
1118                                 from_utf8(fn.absFileName())));
1119                 return ReadFileNotFound;
1120         }
1121
1122         int file_format;
1123         ReadStatus const ret_plf = parseLyXFormat(lex, fn, file_format);
1124         if (ret_plf != ReadSuccess)
1125                 return ret_plf;
1126
1127         if (file_format != LYX_FORMAT) {
1128                 FileName tmpFile;
1129                 ReadStatus const ret_clf = convertLyXFormat(fn, tmpFile, file_format);
1130                 if (ret_clf != ReadSuccess)
1131                         return ret_clf;
1132                 return readFile(tmpFile);
1133         }
1134
1135         // FIXME: InsetInfo needs to know whether the file is under VCS
1136         // during the parse process, so this has to be done before.
1137         lyxvc().file_found_hook(d->filename);
1138
1139         if (readDocument(lex)) {
1140                 Alert::error(_("Document format failure"),
1141                         bformat(_("%1$s ended unexpectedly, which means"
1142                                 " that it is probably corrupted."),
1143                                         from_utf8(fn.absFileName())));
1144                 return ReadDocumentFailure;
1145         }
1146
1147         d->file_fully_loaded = true;
1148         d->read_only = !d->filename.isWritable();
1149         params().compressed = formats.isZippedFile(d->filename);
1150         saveCheckSum();
1151         return ReadSuccess;
1152 }
1153
1154
1155 bool Buffer::isFullyLoaded() const
1156 {
1157         return d->file_fully_loaded;
1158 }
1159
1160
1161 void Buffer::setFullyLoaded(bool value)
1162 {
1163         d->file_fully_loaded = value;
1164 }
1165
1166
1167 PreviewLoader * Buffer::loader() const
1168 {
1169         if (!isExporting() && lyxrc.preview == LyXRC::PREVIEW_OFF)
1170                 return 0;
1171         if (!d->preview_loader_)
1172                 d->preview_loader_ = new PreviewLoader(*this);
1173         return d->preview_loader_;
1174 }
1175
1176
1177 void Buffer::removePreviews() const
1178 {
1179         delete d->preview_loader_;
1180         d->preview_loader_ = 0;
1181 }
1182
1183
1184 void Buffer::updatePreviews() const
1185 {
1186         PreviewLoader * ploader = loader();
1187         if (!ploader)
1188                 return;
1189
1190         InsetIterator it = inset_iterator_begin(*d->inset);
1191         InsetIterator const end = inset_iterator_end(*d->inset);
1192         for (; it != end; ++it)
1193                 it->addPreview(it, *ploader);
1194
1195         ploader->startLoading();
1196 }
1197
1198
1199 Buffer::ReadStatus Buffer::parseLyXFormat(Lexer & lex,
1200         FileName const & fn, int & file_format) const
1201 {
1202         if(!lex.checkFor("\\lyxformat")) {
1203                 Alert::error(_("Document format failure"),
1204                         bformat(_("%1$s is not a readable LyX document."),
1205                                 from_utf8(fn.absFileName())));
1206                 return ReadNoLyXFormat;
1207         }
1208
1209         string tmp_format;
1210         lex >> tmp_format;
1211
1212         // LyX formats 217 and earlier were written as 2.17. This corresponds
1213         // to files from LyX versions < 1.1.6.3. We just remove the dot in
1214         // these cases. See also: www.lyx.org/trac/changeset/1313.
1215         size_t dot = tmp_format.find_first_of(".,");
1216         if (dot != string::npos)
1217                 tmp_format.erase(dot, 1);
1218
1219         file_format = convert<int>(tmp_format);
1220         return ReadSuccess;
1221 }
1222
1223
1224 Buffer::ReadStatus Buffer::convertLyXFormat(FileName const & fn,
1225         FileName & tmpfile, int from_format)
1226 {
1227         TempFile tempfile("Buffer_convertLyXFormatXXXXXX.lyx");
1228         tempfile.setAutoRemove(false);
1229         tmpfile = tempfile.name();
1230         if(tmpfile.empty()) {
1231                 Alert::error(_("Conversion failed"),
1232                         bformat(_("%1$s is from a different"
1233                                 " version of LyX, but a temporary"
1234                                 " file for converting it could"
1235                                 " not be created."),
1236                                 from_utf8(fn.absFileName())));
1237                 return LyX2LyXNoTempFile;
1238         }
1239
1240         FileName const lyx2lyx = libFileSearch("lyx2lyx", "lyx2lyx");
1241         if (lyx2lyx.empty()) {
1242                 Alert::error(_("Conversion script not found"),
1243                      bformat(_("%1$s is from a different"
1244                                " version of LyX, but the"
1245                                " conversion script lyx2lyx"
1246                                " could not be found."),
1247                                from_utf8(fn.absFileName())));
1248                 return LyX2LyXNotFound;
1249         }
1250
1251         // Run lyx2lyx:
1252         //   $python$ "$lyx2lyx$" -t $LYX_FORMAT$ -o "$tempfile$" "$filetoread$"
1253         ostringstream command;
1254         command << os::python()
1255                 << ' ' << quoteName(lyx2lyx.toFilesystemEncoding())
1256                 << " -t " << convert<string>(LYX_FORMAT)
1257                 << " -o " << quoteName(tmpfile.toFilesystemEncoding())
1258                 << ' ' << quoteName(fn.toSafeFilesystemEncoding());
1259         string const command_str = command.str();
1260
1261         LYXERR(Debug::INFO, "Running '" << command_str << '\'');
1262
1263         cmd_ret const ret = runCommand(command_str);
1264         if (ret.first != 0) {
1265                 if (from_format < LYX_FORMAT) {
1266                         Alert::error(_("Conversion script failed"),
1267                                 bformat(_("%1$s is from an older version"
1268                                         " of LyX and the lyx2lyx script"
1269                                         " failed to convert it."),
1270                                         from_utf8(fn.absFileName())));
1271                         return LyX2LyXOlderFormat;
1272                 } else {
1273                         Alert::error(_("Conversion script failed"),
1274                                 bformat(_("%1$s is from a newer version"
1275                                         " of LyX and the lyx2lyx script"
1276                                         " failed to convert it."),
1277                                         from_utf8(fn.absFileName())));
1278                         return LyX2LyXNewerFormat;
1279                 }
1280         }
1281         return ReadSuccess;
1282 }
1283
1284
1285 // Should probably be moved to somewhere else: BufferView? GuiView?
1286 bool Buffer::save() const
1287 {
1288         docstring const file = makeDisplayPath(absFileName(), 20);
1289         d->filename.refresh();
1290
1291         // check the read-only status before moving the file as a backup
1292         if (d->filename.exists()) {
1293                 bool const read_only = !d->filename.isWritable();
1294                 if (read_only) {
1295                         Alert::warning(_("File is read-only"),
1296                                 bformat(_("The file %1$s cannot be written because it "
1297                                 "is marked as read-only."), file));
1298                         return false;
1299                 }
1300         }
1301
1302         // ask if the disk file has been externally modified (use checksum method)
1303         if (fileName().exists() && isExternallyModified(checksum_method)) {
1304                 docstring text =
1305                         bformat(_("Document %1$s has been externally modified. "
1306                                 "Are you sure you want to overwrite this file?"), file);
1307                 int const ret = Alert::prompt(_("Overwrite modified file?"),
1308                         text, 1, 1, _("&Overwrite"), _("&Cancel"));
1309                 if (ret == 1)
1310                         return false;
1311         }
1312
1313         // We don't need autosaves in the immediate future. (Asger)
1314         resetAutosaveTimers();
1315
1316         // if the file does not yet exist, none of the backup activity
1317         // that follows is necessary
1318         if (!fileName().exists())
1319                 return writeFile(fileName());
1320
1321         // we first write the file to a new name, then move it to its
1322         // proper location once that has been done successfully. that
1323         // way we preserve the original file if something goes wrong.
1324         string const justname = fileName().onlyFileNameWithoutExt();
1325         boost::scoped_ptr<TempFile>
1326                 tempfile(new TempFile(fileName().onlyPath(),
1327                    justname + "-XXXXXX.lyx"));
1328         bool const symlink = fileName().isSymLink();
1329         if (!symlink)
1330                 tempfile->setAutoRemove(false);
1331
1332         FileName savefile(tempfile->name());
1333         LYXERR(Debug::FILES, "Saving to " << savefile.absFileName());
1334         if (!writeFile(savefile))
1335                 return false;
1336
1337         // we will set this to false if we fail
1338         bool made_backup = true;
1339
1340         FileName backupName(absFileName() + '~');
1341         if (lyxrc.make_backup) {
1342                 if (!lyxrc.backupdir_path.empty()) {
1343                         string const mangledName =
1344                                 subst(subst(backupName.absFileName(), '/', '!'), ':', '!');
1345                         backupName = FileName(addName(lyxrc.backupdir_path,
1346                                                       mangledName));
1347                 }
1348
1349                 LYXERR(Debug::FILES, "Backing up original file to " <<
1350                                 backupName.absFileName());
1351                 // Except file is symlink do not copy because of #6587.
1352                 // Hard links have bad luck.
1353                 made_backup = symlink ?
1354                         fileName().copyTo(backupName):
1355                         fileName().moveTo(backupName);
1356
1357                 if (!made_backup) {
1358                         Alert::error(_("Backup failure"),
1359                                      bformat(_("Cannot create backup file %1$s.\n"
1360                                                "Please check whether the directory exists and is writable."),
1361                                              from_utf8(backupName.absFileName())));
1362                         //LYXERR(Debug::DEBUG, "Fs error: " << fe.what());
1363                 }
1364         }
1365
1366         // Destroy tempfile since it keeps the file locked on windows (bug 9234)
1367         // Only do this if tempfile is not in autoremove mode
1368         if (!symlink)
1369                 tempfile.reset();
1370         // If we have no symlink, we can simply rename the temp file.
1371         // Otherwise, we need to copy it so the symlink stays intact.
1372         if (made_backup && symlink ? savefile.copyTo(fileName(), true) :
1373                                            savefile.moveTo(fileName()))
1374         {
1375                 // saveCheckSum() was already called by writeFile(), but the
1376                 // time stamp is invalidated by copying/moving
1377                 saveCheckSum();
1378                 markClean();
1379                 return true;
1380         }
1381         // else we saved the file, but failed to move it to the right location.
1382
1383         if (lyxrc.make_backup && made_backup && !symlink) {
1384                 // the original file was moved to filename.lyx~, so it will look
1385                 // to the user as if it was deleted. (see bug #9234.) we could try
1386                 // to restore it, but that would basically mean trying to do again
1387                 // what we just failed to do. better to leave things as they are.
1388                 Alert::error(_("Write failure"),
1389                              bformat(_("The file has successfully been saved as:\n  %1$s.\n"
1390                            "But LyX could not move it to:\n  %2$s.\n"
1391                                                          "Your original file has been backed up to:\n  %3$s"),
1392                                      from_utf8(savefile.absFileName()),
1393              from_utf8(fileName().absFileName()),
1394                          from_utf8(backupName.absFileName())));
1395         } else {
1396                 // either we did not try to make a backup, or else we tried and failed,
1397                 // or else the original file was a symlink, in which case it was copied,
1398                 // not moved. so the original file is intact.
1399                 Alert::error(_("Write failure"),
1400                              bformat(_("Cannot move saved file to:\n  %1$s.\n"
1401                                        "But the file has successfully been saved as:\n  %2$s."),
1402                                      from_utf8(fileName().absFileName()),
1403                          from_utf8(savefile.absFileName())));
1404         }
1405         return false;
1406 }
1407
1408
1409 bool Buffer::writeFile(FileName const & fname) const
1410 {
1411         if (d->read_only && fname == d->filename)
1412                 return false;
1413
1414         bool retval = false;
1415
1416         docstring const str = bformat(_("Saving document %1$s..."),
1417                 makeDisplayPath(fname.absFileName()));
1418         message(str);
1419
1420         string const encoded_fname = fname.toSafeFilesystemEncoding(os::CREATE);
1421
1422         if (params().compressed) {
1423                 gz::ogzstream ofs(encoded_fname.c_str(), ios::out|ios::trunc);
1424                 retval = ofs && write(ofs);
1425         } else {
1426                 ofstream ofs(encoded_fname.c_str(), ios::out|ios::trunc);
1427                 retval = ofs && write(ofs);
1428         }
1429
1430         if (!retval) {
1431                 message(str + _(" could not write file!"));
1432                 return false;
1433         }
1434
1435         // see bug 6587
1436         // removeAutosaveFile();
1437
1438         saveCheckSum();
1439         message(str + _(" done."));
1440
1441         return true;
1442 }
1443
1444
1445 docstring Buffer::emergencyWrite()
1446 {
1447         // No need to save if the buffer has not changed.
1448         if (isClean())
1449                 return docstring();
1450
1451         string const doc = isUnnamed() ? onlyFileName(absFileName()) : absFileName();
1452
1453         docstring user_message = bformat(
1454                 _("LyX: Attempting to save document %1$s\n"), from_utf8(doc));
1455
1456         // We try to save three places:
1457         // 1) Same place as document. Unless it is an unnamed doc.
1458         if (!isUnnamed()) {
1459                 string s = absFileName();
1460                 s += ".emergency";
1461                 LYXERR0("  " << s);
1462                 if (writeFile(FileName(s))) {
1463                         markClean();
1464                         user_message += "  " + bformat(_("Saved to %1$s. Phew.\n"), from_utf8(s));
1465                         return user_message;
1466                 } else {
1467                         user_message += "  " + _("Save failed! Trying again...\n");
1468                 }
1469         }
1470
1471         // 2) In HOME directory.
1472         string s = addName(Package::get_home_dir().absFileName(), absFileName());
1473         s += ".emergency";
1474         lyxerr << ' ' << s << endl;
1475         if (writeFile(FileName(s))) {
1476                 markClean();
1477                 user_message += "  " + bformat(_("Saved to %1$s. Phew.\n"), from_utf8(s));
1478                 return user_message;
1479         }
1480
1481         user_message += "  " + _("Save failed! Trying yet again...\n");
1482
1483         // 3) In "/tmp" directory.
1484         // MakeAbsPath to prepend the current
1485         // drive letter on OS/2
1486         s = addName(package().temp_dir().absFileName(), absFileName());
1487         s += ".emergency";
1488         lyxerr << ' ' << s << endl;
1489         if (writeFile(FileName(s))) {
1490                 markClean();
1491                 user_message += "  " + bformat(_("Saved to %1$s. Phew.\n"), from_utf8(s));
1492                 return user_message;
1493         }
1494
1495         user_message += "  " + _("Save failed! Bummer. Document is lost.");
1496         // Don't try again.
1497         markClean();
1498         return user_message;
1499 }
1500
1501
1502 bool Buffer::write(ostream & ofs) const
1503 {
1504 #ifdef HAVE_LOCALE
1505         // Use the standard "C" locale for file output.
1506         ofs.imbue(locale::classic());
1507 #endif
1508
1509         // The top of the file should not be written by params().
1510
1511         // write out a comment in the top of the file
1512         // Important: Keep the version formatting in sync with lyx2lyx and
1513         //            tex2lyx (bug 7951)
1514         ofs << "#LyX " << lyx_version_major << "." << lyx_version_minor
1515             << " created this file. For more info see http://www.lyx.org/\n"
1516             << "\\lyxformat " << LYX_FORMAT << "\n"
1517             << "\\begin_document\n";
1518
1519         /// For each author, set 'used' to true if there is a change
1520         /// by this author in the document; otherwise set it to 'false'.
1521         AuthorList::Authors::const_iterator a_it = params().authors().begin();
1522         AuthorList::Authors::const_iterator a_end = params().authors().end();
1523         for (; a_it != a_end; ++a_it)
1524                 a_it->setUsed(false);
1525
1526         ParIterator const end = const_cast<Buffer *>(this)->par_iterator_end();
1527         ParIterator it = const_cast<Buffer *>(this)->par_iterator_begin();
1528         for ( ; it != end; ++it)
1529                 it->checkAuthors(params().authors());
1530
1531         // now write out the buffer parameters.
1532         ofs << "\\begin_header\n";
1533         params().writeFile(ofs);
1534         ofs << "\\end_header\n";
1535
1536         // write the text
1537         ofs << "\n\\begin_body\n";
1538         text().write(ofs);
1539         ofs << "\n\\end_body\n";
1540
1541         // Write marker that shows file is complete
1542         ofs << "\\end_document" << endl;
1543
1544         // Shouldn't really be needed....
1545         //ofs.close();
1546
1547         // how to check if close went ok?
1548         // Following is an attempt... (BE 20001011)
1549
1550         // good() returns false if any error occurred, including some
1551         //        formatting error.
1552         // bad()  returns true if something bad happened in the buffer,
1553         //        which should include file system full errors.
1554
1555         bool status = true;
1556         if (!ofs) {
1557                 status = false;
1558                 lyxerr << "File was not closed properly." << endl;
1559         }
1560
1561         return status;
1562 }
1563
1564
1565 bool Buffer::makeLaTeXFile(FileName const & fname,
1566                            string const & original_path,
1567                            OutputParams const & runparams_in,
1568                            OutputWhat output) const
1569 {
1570         OutputParams runparams = runparams_in;
1571
1572         // This is necessary for LuaTeX/XeTeX with tex fonts.
1573         // See FIXME in BufferParams::encoding()
1574         if (runparams.isFullUnicode())
1575                 runparams.encoding = encodings.fromLyXName("utf8-plain");
1576
1577         string const encoding = runparams.encoding->iconvName();
1578         LYXERR(Debug::LATEX, "makeLaTeXFile encoding: " << encoding << ", fname=" << fname.realPath());
1579
1580         ofdocstream ofs;
1581         try { ofs.reset(encoding); }
1582         catch (iconv_codecvt_facet_exception const & e) {
1583                 lyxerr << "Caught iconv exception: " << e.what() << endl;
1584                 Alert::error(_("Iconv software exception Detected"), bformat(_("Please "
1585                         "verify that the support software for your encoding (%1$s) is "
1586                         "properly installed"), from_ascii(encoding)));
1587                 return false;
1588         }
1589         if (!openFileWrite(ofs, fname))
1590                 return false;
1591
1592         ErrorList & errorList = d->errorLists["Export"];
1593         errorList.clear();
1594         bool failed_export = false;
1595         otexstream os(ofs, d->texrow);
1596
1597         // make sure we are ready to export
1598         // this needs to be done before we validate
1599         // FIXME Do we need to do this all the time? I.e., in children
1600         // of a master we are exporting?
1601         updateBuffer();
1602         updateMacroInstances(OutputUpdate);
1603
1604         try {
1605                 os.texrow().reset();
1606                 writeLaTeXSource(os, original_path, runparams, output);
1607         }
1608         catch (EncodingException const & e) {
1609                 docstring const failed(1, e.failed_char);
1610                 ostringstream oss;
1611                 oss << "0x" << hex << e.failed_char << dec;
1612                 docstring msg = bformat(_("Could not find LaTeX command for character '%1$s'"
1613                                           " (code point %2$s)"),
1614                                           failed, from_utf8(oss.str()));
1615                 errorList.push_back(ErrorItem(msg, _("Some characters of your document are probably not "
1616                                 "representable in the chosen encoding.\n"
1617                                 "Changing the document encoding to utf8 could help."),
1618                                 e.par_id, e.pos, e.pos + 1));
1619                 failed_export = true;
1620         }
1621         catch (iconv_codecvt_facet_exception const & e) {
1622                 errorList.push_back(ErrorItem(_("iconv conversion failed"),
1623                         _(e.what()), -1, 0, 0));
1624                 failed_export = true;
1625         }
1626         catch (exception const & e) {
1627                 errorList.push_back(ErrorItem(_("conversion failed"),
1628                         _(e.what()), -1, 0, 0));
1629                 failed_export = true;
1630         }
1631         catch (...) {
1632                 lyxerr << "Caught some really weird exception..." << endl;
1633                 lyx_exit(1);
1634         }
1635
1636         ofs.close();
1637         if (ofs.fail()) {
1638                 failed_export = true;
1639                 lyxerr << "File '" << fname << "' was not closed properly." << endl;
1640         }
1641
1642         if (runparams_in.silent)
1643                 errorList.clear();
1644         else
1645                 errors("Export");
1646         return !failed_export;
1647 }
1648
1649
1650 void Buffer::writeLaTeXSource(otexstream & os,
1651                            string const & original_path,
1652                            OutputParams const & runparams_in,
1653                            OutputWhat output) const
1654 {
1655         // The child documents, if any, shall be already loaded at this point.
1656
1657         OutputParams runparams = runparams_in;
1658
1659         // This is necessary for LuaTeX/XeTeX with tex fonts.
1660         // See FIXME in BufferParams::encoding()
1661         if (runparams.isFullUnicode())
1662                 runparams.encoding = encodings.fromLyXName("utf8-plain");
1663
1664         // If we are compiling a file standalone, even if this is the
1665         // child of some other buffer, let's cut the link here, so the
1666         // file is really independent and no concurring settings from
1667         // the master (e.g. branch state) interfere (see #8100).
1668         if (!runparams.is_child)
1669                 d->ignore_parent = true;
1670
1671         // Classify the unicode characters appearing in math insets
1672         BufferEncodings::initUnicodeMath(*this);
1673
1674         // validate the buffer.
1675         LYXERR(Debug::LATEX, "  Validating buffer...");
1676         LaTeXFeatures features(*this, params(), runparams);
1677         validate(features);
1678         // This is only set once per document (in master)
1679         if (!runparams.is_child)
1680                 runparams.use_polyglossia = features.usePolyglossia();
1681         LYXERR(Debug::LATEX, "  Buffer validation done.");
1682
1683         bool const output_preamble =
1684                 output == FullSource || output == OnlyPreamble;
1685         bool const output_body =
1686                 output == FullSource || output == OnlyBody;
1687
1688         // The starting paragraph of the coming rows is the
1689         // first paragraph of the document. (Asger)
1690         if (output_preamble && runparams.nice) {
1691                 os << "%% LyX " << lyx_version << " created this file.  "
1692                         "For more info, see http://www.lyx.org/.\n"
1693                         "%% Do not edit unless you really know what "
1694                         "you are doing.\n";
1695         }
1696         LYXERR(Debug::INFO, "lyx document header finished");
1697
1698         // There are a few differences between nice LaTeX and usual files:
1699         // usual files have \batchmode and special input@path to allow
1700         // inclusion of figures specified by an explicitly relative path
1701         // (i.e., a path starting with './' or '../') with either \input or
1702         // \includegraphics, as the TEXINPUTS method doesn't work in this case.
1703         // input@path is set when the actual parameter original_path is set.
1704         // This is done for usual tex-file, but not for nice-latex-file.
1705         // (Matthias 250696)
1706         // Note that input@path is only needed for something the user does
1707         // in the preamble, included .tex files or ERT, files included by
1708         // LyX work without it.
1709         if (output_preamble) {
1710                 if (!runparams.nice) {
1711                         // code for usual, NOT nice-latex-file
1712                         os << "\\batchmode\n"; // changed from \nonstopmode
1713                 }
1714                 if (!original_path.empty()) {
1715                         // FIXME UNICODE
1716                         // We don't know the encoding of inputpath
1717                         docstring const inputpath = from_utf8(original_path);
1718                         docstring uncodable_glyphs;
1719                         Encoding const * const enc = runparams.encoding;
1720                         if (enc) {
1721                                 for (size_t n = 0; n < inputpath.size(); ++n) {
1722                                         if (!enc->encodable(inputpath[n])) {
1723                                                 docstring const glyph(1, inputpath[n]);
1724                                                 LYXERR0("Uncodable character '"
1725                                                         << glyph
1726                                                         << "' in input path!");
1727                                                 uncodable_glyphs += glyph;
1728                                         }
1729                                 }
1730                         }
1731
1732                         // warn user if we found uncodable glyphs.
1733                         if (!uncodable_glyphs.empty()) {
1734                                 frontend::Alert::warning(
1735                                         _("Uncodable character in file path"),
1736                                         support::bformat(
1737                                           _("The path of your document\n"
1738                                             "(%1$s)\n"
1739                                             "contains glyphs that are unknown "
1740                                             "in the current document encoding "
1741                                             "(namely %2$s). This may result in "
1742                                             "incomplete output, unless "
1743                                             "TEXINPUTS contains the document "
1744                                             "directory and you don't use "
1745                                             "explicitly relative paths (i.e., "
1746                                             "paths starting with './' or "
1747                                             "'../') in the preamble or in ERT."
1748                                             "\n\nIn case of problems, choose "
1749                                             "an appropriate document encoding\n"
1750                                             "(such as utf8) or change the "
1751                                             "file path name."),
1752                                           inputpath, uncodable_glyphs));
1753                         } else {
1754                                 string docdir =
1755                                         support::latex_path(original_path);
1756                                 if (contains(docdir, '#')) {
1757                                         docdir = subst(docdir, "#", "\\#");
1758                                         os << "\\catcode`\\#=11"
1759                                               "\\def\\#{#}\\catcode`\\#=6\n";
1760                                 }
1761                                 if (contains(docdir, '%')) {
1762                                         docdir = subst(docdir, "%", "\\%");
1763                                         os << "\\catcode`\\%=11"
1764                                               "\\def\\%{%}\\catcode`\\%=14\n";
1765                                 }
1766                                 os << "\\makeatletter\n"
1767                                    << "\\def\\input@path{{"
1768                                    << docdir << "/}}\n"
1769                                    << "\\makeatother\n";
1770                         }
1771                 }
1772
1773                 // get parent macros (if this buffer has a parent) which will be
1774                 // written at the document begin further down.
1775                 MacroSet parentMacros;
1776                 listParentMacros(parentMacros, features);
1777
1778                 // Write the preamble
1779                 runparams.use_babel = params().writeLaTeX(os, features,
1780                                                           d->filename.onlyPath());
1781
1782                 // Japanese might be required only in some children of a document,
1783                 // but once required, we must keep use_japanese true.
1784                 runparams.use_japanese |= features.isRequired("japanese");
1785
1786                 if (!output_body) {
1787                         // Restore the parenthood if needed
1788                         if (!runparams.is_child)
1789                                 d->ignore_parent = false;
1790                         return;
1791                 }
1792
1793                 // make the body.
1794                 os << "\\begin{document}\n";
1795
1796                 // output the parent macros
1797                 MacroSet::iterator it = parentMacros.begin();
1798                 MacroSet::iterator end = parentMacros.end();
1799                 for (; it != end; ++it) {
1800                         int num_lines = (*it)->write(os.os(), true);
1801                         os.texrow().newlines(num_lines);
1802                 }
1803
1804         } // output_preamble
1805
1806         os.texrow().start(paragraphs().begin()->id(), 0);
1807
1808         LYXERR(Debug::INFO, "preamble finished, now the body.");
1809
1810         // the real stuff
1811         latexParagraphs(*this, text(), os, runparams);
1812
1813         // Restore the parenthood if needed
1814         if (!runparams.is_child)
1815                 d->ignore_parent = false;
1816
1817         // add this just in case after all the paragraphs
1818         os << endl;
1819
1820         if (output_preamble) {
1821                 os << "\\end{document}\n";
1822                 LYXERR(Debug::LATEX, "makeLaTeXFile...done");
1823         } else {
1824                 LYXERR(Debug::LATEX, "LaTeXFile for inclusion made.");
1825         }
1826         runparams_in.encoding = runparams.encoding;
1827
1828         // Just to be sure. (Asger)
1829         os.texrow().newline();
1830
1831         //for (int i = 0; i<d->texrow.rows(); i++) {
1832         // int id,pos;
1833         // if (d->texrow.getIdFromRow(i+1,id,pos) && id>0)
1834         //      lyxerr << i+1 << ":" << id << ":" << getParFromID(id).paragraph().asString()<<"\n";
1835         //}
1836
1837         LYXERR(Debug::INFO, "Finished making LaTeX file.");
1838         LYXERR(Debug::INFO, "Row count was " << os.texrow().rows() - 1 << '.');
1839 }
1840
1841
1842 void Buffer::makeDocBookFile(FileName const & fname,
1843                               OutputParams const & runparams,
1844                               OutputWhat output) const
1845 {
1846         LYXERR(Debug::LATEX, "makeDocBookFile...");
1847
1848         ofdocstream ofs;
1849         if (!openFileWrite(ofs, fname))
1850                 return;
1851
1852         // make sure we are ready to export
1853         // this needs to be done before we validate
1854         updateBuffer();
1855         updateMacroInstances(OutputUpdate);
1856
1857         writeDocBookSource(ofs, fname.absFileName(), runparams, output);
1858
1859         ofs.close();
1860         if (ofs.fail())
1861                 lyxerr << "File '" << fname << "' was not closed properly." << endl;
1862 }
1863
1864
1865 void Buffer::writeDocBookSource(odocstream & os, string const & fname,
1866                              OutputParams const & runparams,
1867                              OutputWhat output) const
1868 {
1869         LaTeXFeatures features(*this, params(), runparams);
1870         validate(features);
1871
1872         d->texrow.reset();
1873
1874         DocumentClass const & tclass = params().documentClass();
1875         string const & top_element = tclass.latexname();
1876
1877         bool const output_preamble =
1878                 output == FullSource || output == OnlyPreamble;
1879         bool const output_body =
1880           output == FullSource || output == OnlyBody;
1881
1882         if (output_preamble) {
1883                 if (runparams.flavor == OutputParams::XML)
1884                         os << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
1885
1886                 // FIXME UNICODE
1887                 os << "<!DOCTYPE " << from_ascii(top_element) << ' ';
1888
1889                 // FIXME UNICODE
1890                 if (! tclass.class_header().empty())
1891                         os << from_ascii(tclass.class_header());
1892                 else if (runparams.flavor == OutputParams::XML)
1893                         os << "PUBLIC \"-//OASIS//DTD DocBook XML//EN\" "
1894                             << "\"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd\"";
1895                 else
1896                         os << " PUBLIC \"-//OASIS//DTD DocBook V4.2//EN\"";
1897
1898                 docstring preamble = from_utf8(params().preamble);
1899                 if (runparams.flavor != OutputParams::XML ) {
1900                         preamble += "<!ENTITY % output.print.png \"IGNORE\">\n";
1901                         preamble += "<!ENTITY % output.print.pdf \"IGNORE\">\n";
1902                         preamble += "<!ENTITY % output.print.eps \"IGNORE\">\n";
1903                         preamble += "<!ENTITY % output.print.bmp \"IGNORE\">\n";
1904                 }
1905
1906                 string const name = runparams.nice
1907                         ? changeExtension(absFileName(), ".sgml") : fname;
1908                 preamble += features.getIncludedFiles(name);
1909                 preamble += features.getLyXSGMLEntities();
1910
1911                 if (!preamble.empty()) {
1912                         os << "\n [ " << preamble << " ]";
1913                 }
1914                 os << ">\n\n";
1915         }
1916
1917         if (output_body) {
1918                 string top = top_element;
1919                 top += " lang=\"";
1920                 if (runparams.flavor == OutputParams::XML)
1921                         top += params().language->code();
1922                 else
1923                         top += params().language->code().substr(0, 2);
1924                 top += '"';
1925
1926                 if (!params().options.empty()) {
1927                         top += ' ';
1928                         top += params().options;
1929                 }
1930
1931                 os << "<!-- " << ((runparams.flavor == OutputParams::XML)? "XML" : "SGML")
1932                                 << " file was created by LyX " << lyx_version
1933                                 << "\n  See http://www.lyx.org/ for more information -->\n";
1934
1935                 params().documentClass().counters().reset();
1936
1937                 sgml::openTag(os, top);
1938                 os << '\n';
1939                 docbookParagraphs(text(), *this, os, runparams);
1940                 sgml::closeTag(os, top_element);
1941         }
1942 }
1943
1944
1945 void Buffer::makeLyXHTMLFile(FileName const & fname,
1946                               OutputParams const & runparams) const
1947 {
1948         LYXERR(Debug::LATEX, "makeLyXHTMLFile...");
1949
1950         ofdocstream ofs;
1951         if (!openFileWrite(ofs, fname))
1952                 return;
1953
1954         // make sure we are ready to export
1955         // this has to be done before we validate
1956         updateBuffer(UpdateMaster, OutputUpdate);
1957         updateMacroInstances(OutputUpdate);
1958
1959         writeLyXHTMLSource(ofs, runparams, FullSource);
1960
1961         ofs.close();
1962         if (ofs.fail())
1963                 lyxerr << "File '" << fname << "' was not closed properly." << endl;
1964 }
1965
1966
1967 void Buffer::writeLyXHTMLSource(odocstream & os,
1968                              OutputParams const & runparams,
1969                              OutputWhat output) const
1970 {
1971         LaTeXFeatures features(*this, params(), runparams);
1972         validate(features);
1973         d->bibinfo_.makeCitationLabels(*this);
1974
1975         bool const output_preamble =
1976                 output == FullSource || output == OnlyPreamble;
1977         bool const output_body =
1978           output == FullSource || output == OnlyBody || output == IncludedFile;
1979
1980         if (output_preamble) {
1981                 os << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
1982                    << "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.1 plus MathML 2.0//EN\" \"http://www.w3.org/Math/DTD/mathml2/xhtml-math11-f.dtd\">\n"
1983                    // FIXME Language should be set properly.
1984                    << "<html xmlns=\"http://www.w3.org/1999/xhtml\">\n"
1985                    << "<head>\n"
1986                    << "<meta name=\"GENERATOR\" content=\"" << PACKAGE_STRING << "\" />\n"
1987                    // FIXME Presumably need to set this right
1988                    << "<meta http-equiv=\"Content-type\" content=\"text/html;charset=UTF-8\" />\n";
1989
1990                 docstring const & doctitle = features.htmlTitle();
1991                 os << "<title>"
1992                    << (doctitle.empty() ?
1993                          from_ascii("LyX Document") :
1994                          html::htmlize(doctitle, XHTMLStream::ESCAPE_ALL))
1995                    << "</title>\n";
1996
1997                 docstring styles = features.getTClassHTMLPreamble();
1998                 if (!styles.empty())
1999                         os << "\n<!-- Text Class Preamble -->\n" << styles << '\n';
2000
2001                 styles = from_utf8(features.getPreambleSnippets());
2002                 if (!styles.empty())
2003                         os << "\n<!-- Preamble Snippets -->\n" << styles << '\n';
2004
2005                 // we will collect CSS information in a stream, and then output it
2006                 // either here, as part of the header, or else in a separate file.
2007                 odocstringstream css;
2008                 styles = from_utf8(features.getCSSSnippets());
2009                 if (!styles.empty())
2010                         css << "/* LyX Provided Styles */\n" << styles << '\n';
2011
2012                 styles = features.getTClassHTMLStyles();
2013                 if (!styles.empty())
2014                         css << "/* Layout-provided Styles */\n" << styles << '\n';
2015
2016                 bool const needfg = params().fontcolor != RGBColor(0, 0, 0);
2017                 bool const needbg = params().backgroundcolor != RGBColor(0xFF, 0xFF, 0xFF);
2018                 if (needfg || needbg) {
2019                                 css << "\nbody {\n";
2020                                 if (needfg)
2021                                    css << "  color: "
2022                                             << from_ascii(X11hexname(params().fontcolor))
2023                                             << ";\n";
2024                                 if (needbg)
2025                                    css << "  background-color: "
2026                                             << from_ascii(X11hexname(params().backgroundcolor))
2027                                             << ";\n";
2028                                 css << "}\n";
2029                 }
2030
2031                 docstring const dstyles = css.str();
2032                 if (!dstyles.empty()) {
2033                         bool written = false;
2034                         if (params().html_css_as_file) {
2035                                 // open a file for CSS info
2036                                 ofdocstream ocss;
2037                                 string const fcssname = addName(temppath(), "docstyle.css");
2038                                 FileName const fcssfile = FileName(fcssname);
2039                                 if (openFileWrite(ocss, fcssfile)) {
2040                                         ocss << dstyles;
2041                                         ocss.close();
2042                                         written = true;
2043                                         // write link to header
2044                                         os << "<link rel='stylesheet' href='docstyle.css' type='text/css' />\n";
2045                                         // register file
2046                                         runparams.exportdata->addExternalFile("xhtml", fcssfile);
2047                                 }
2048                         }
2049                         // we are here if the CSS is supposed to be written to the header
2050                         // or if we failed to write it to an external file.
2051                         if (!written) {
2052                                 os << "<style type='text/css'>\n"
2053                                          << dstyles
2054                                          << "\n</style>\n";
2055                         }
2056                 }
2057                 os << "</head>\n";
2058         }
2059
2060         if (output_body) {
2061                 bool const output_body_tag = (output != IncludedFile);
2062                 if (output_body_tag)
2063                         os << "<body>\n";
2064                 XHTMLStream xs(os);
2065                 if (output != IncludedFile)
2066                         // if we're an included file, the counters are in the master.
2067                         params().documentClass().counters().reset();
2068                 xhtmlParagraphs(text(), *this, xs, runparams);
2069                 if (output_body_tag)
2070                         os << "</body>\n";
2071         }
2072
2073         if (output_preamble)
2074                 os << "</html>\n";
2075 }
2076
2077
2078 // chktex should be run with these flags disabled: 3, 22, 25, 30, 38(?)
2079 // Other flags: -wall -v0 -x
2080 int Buffer::runChktex()
2081 {
2082         setBusy(true);
2083
2084         // get LaTeX-Filename
2085         FileName const path(temppath());
2086         string const name = addName(path.absFileName(), latexName());
2087         string const org_path = filePath();
2088
2089         PathChanger p(path); // path to LaTeX file
2090         message(_("Running chktex..."));
2091
2092         // Generate the LaTeX file if neccessary
2093         OutputParams runparams(&params().encoding());
2094         runparams.flavor = OutputParams::LATEX;
2095         runparams.nice = false;
2096         runparams.linelen = lyxrc.plaintext_linelen;
2097         makeLaTeXFile(FileName(name), org_path, runparams);
2098
2099         TeXErrors terr;
2100         Chktex chktex(lyxrc.chktex_command, onlyFileName(name), filePath());
2101         int const res = chktex.run(terr); // run chktex
2102
2103         if (res == -1) {
2104                 Alert::error(_("chktex failure"),
2105                              _("Could not run chktex successfully."));
2106         } else {
2107                 ErrorList & errlist = d->errorLists["ChkTeX"];
2108                 errlist.clear();
2109                 bufferErrors(terr, errlist);
2110         }
2111
2112         setBusy(false);
2113
2114         if (runparams.silent)
2115                 d->errorLists["ChkTeX"].clear();
2116         else
2117                 errors("ChkTeX");
2118
2119         return res;
2120 }
2121
2122
2123 void Buffer::validate(LaTeXFeatures & features) const
2124 {
2125         // Validate the buffer params, but not for included
2126         // files, since they also use the parent buffer's
2127         // params (# 5941)
2128         if (!features.runparams().is_child)
2129                 params().validate(features);
2130
2131         for_each(paragraphs().begin(), paragraphs().end(),
2132                  bind(&Paragraph::validate, _1, ref(features)));
2133
2134         if (lyxerr.debugging(Debug::LATEX)) {
2135                 features.showStruct();
2136         }
2137 }
2138
2139
2140 void Buffer::getLabelList(vector<docstring> & list) const
2141 {
2142         // If this is a child document, use the master's list instead.
2143         if (parent()) {
2144                 masterBuffer()->getLabelList(list);
2145                 return;
2146         }
2147
2148         list.clear();
2149         Toc & toc = d->toc_backend.toc("label");
2150         TocIterator toc_it = toc.begin();
2151         TocIterator end = toc.end();
2152         for (; toc_it != end; ++toc_it) {
2153                 if (toc_it->depth() == 0)
2154                         list.push_back(toc_it->str());
2155         }
2156 }
2157
2158
2159 void Buffer::updateBibfilesCache(UpdateScope scope) const
2160 {
2161         // FIXME This is probably unnecssary, given where we call this.
2162         // If this is a child document, use the parent's cache instead.
2163         if (parent() && scope != UpdateChildOnly) {
2164                 masterBuffer()->updateBibfilesCache();
2165                 return;
2166         }
2167
2168         d->bibfiles_cache_.clear();
2169         for (InsetIterator it = inset_iterator_begin(inset()); it; ++it) {
2170                 if (it->lyxCode() == BIBTEX_CODE) {
2171                         InsetBibtex const & inset = static_cast<InsetBibtex const &>(*it);
2172                         support::FileNameList const bibfiles = inset.getBibFiles();
2173                         d->bibfiles_cache_.insert(d->bibfiles_cache_.end(),
2174                                 bibfiles.begin(),
2175                                 bibfiles.end());
2176                 } else if (it->lyxCode() == INCLUDE_CODE) {
2177                         InsetInclude & inset = static_cast<InsetInclude &>(*it);
2178                         Buffer const * const incbuf = inset.getChildBuffer();
2179                         if (!incbuf)
2180                                 continue;
2181                         support::FileNameList const & bibfiles =
2182                                         incbuf->getBibfilesCache(UpdateChildOnly);
2183                         if (!bibfiles.empty()) {
2184                                 d->bibfiles_cache_.insert(d->bibfiles_cache_.end(),
2185                                         bibfiles.begin(),
2186                                         bibfiles.end());
2187                         }
2188                 }
2189         }
2190         d->bibfile_cache_valid_ = true;
2191         d->bibinfo_cache_valid_ = false;
2192         d->cite_labels_valid_ = false;
2193 }
2194
2195
2196 void Buffer::invalidateBibinfoCache() const
2197 {
2198         d->bibinfo_cache_valid_ = false;
2199         d->cite_labels_valid_ = false;
2200         // also invalidate the cache for the parent buffer
2201         Buffer const * const pbuf = d->parent();
2202         if (pbuf)
2203                 pbuf->invalidateBibinfoCache();
2204 }
2205
2206
2207 void Buffer::invalidateBibfileCache() const
2208 {
2209         d->bibfile_cache_valid_ = false;
2210         d->bibinfo_cache_valid_ = false;
2211         d->cite_labels_valid_ = false;
2212         // also invalidate the cache for the parent buffer
2213         Buffer const * const pbuf = d->parent();
2214         if (pbuf)
2215                 pbuf->invalidateBibfileCache();
2216 }
2217
2218
2219 support::FileNameList const & Buffer::getBibfilesCache(UpdateScope scope) const
2220 {
2221         // FIXME This is probably unnecessary, given where we call this.
2222         // If this is a child document, use the master's cache instead.
2223         Buffer const * const pbuf = masterBuffer();
2224         if (pbuf != this && scope != UpdateChildOnly)
2225                 return pbuf->getBibfilesCache();
2226
2227         if (!d->bibfile_cache_valid_)
2228                 this->updateBibfilesCache(scope);
2229
2230         return d->bibfiles_cache_;
2231 }
2232
2233
2234 BiblioInfo const & Buffer::masterBibInfo() const
2235 {
2236         Buffer const * const tmp = masterBuffer();
2237         if (tmp != this)
2238                 return tmp->masterBibInfo();
2239         return d->bibinfo_;
2240 }
2241
2242
2243 void Buffer::checkIfBibInfoCacheIsValid() const
2244 {
2245         // use the master's cache
2246         Buffer const * const tmp = masterBuffer();
2247         if (tmp != this) {
2248                 tmp->checkIfBibInfoCacheIsValid();
2249                 return;
2250         }
2251
2252         // compare the cached timestamps with the actual ones.
2253         FileNameList const & bibfiles_cache = getBibfilesCache();
2254         FileNameList::const_iterator ei = bibfiles_cache.begin();
2255         FileNameList::const_iterator en = bibfiles_cache.end();
2256         for (; ei != en; ++ ei) {
2257                 time_t lastw = ei->lastModified();
2258                 time_t prevw = d->bibfile_status_[*ei];
2259                 if (lastw != prevw) {
2260                         d->bibinfo_cache_valid_ = false;
2261                         d->cite_labels_valid_ = false;
2262                         d->bibfile_status_[*ei] = lastw;
2263                 }
2264         }
2265 }
2266
2267
2268 void Buffer::reloadBibInfoCache() const
2269 {
2270         // use the master's cache
2271         Buffer const * const tmp = masterBuffer();
2272         if (tmp != this) {
2273                 tmp->reloadBibInfoCache();
2274                 return;
2275         }
2276
2277         checkIfBibInfoCacheIsValid();
2278         if (d->bibinfo_cache_valid_)
2279                 return;
2280
2281         d->bibinfo_.clear();
2282         collectBibKeys();
2283         d->bibinfo_cache_valid_ = true;
2284 }
2285
2286
2287 void Buffer::collectBibKeys() const
2288 {
2289         for (InsetIterator it = inset_iterator_begin(inset()); it; ++it)
2290                 it->collectBibKeys(it);
2291 }
2292
2293
2294 void Buffer::addBiblioInfo(BiblioInfo const & bi) const
2295 {
2296         Buffer const * tmp = masterBuffer();
2297         BiblioInfo & masterbi = (tmp == this) ?
2298                 d->bibinfo_ : tmp->d->bibinfo_;
2299         masterbi.mergeBiblioInfo(bi);
2300 }
2301
2302
2303 void Buffer::addBibTeXInfo(docstring const & key, BibTeXInfo const & bi) const
2304 {
2305         Buffer const * tmp = masterBuffer();
2306         BiblioInfo & masterbi = (tmp == this) ?
2307                 d->bibinfo_ : tmp->d->bibinfo_;
2308         masterbi[key] = bi;
2309 }
2310
2311
2312 void Buffer::makeCitationLabels() const
2313 {
2314         Buffer const * const master = masterBuffer();
2315         return d->bibinfo_.makeCitationLabels(*master);
2316 }
2317
2318
2319 bool Buffer::citeLabelsValid() const
2320 {
2321         return masterBuffer()->d->cite_labels_valid_;
2322 }
2323
2324
2325 void Buffer::removeBiblioTempFiles() const
2326 {
2327         // We remove files that contain LaTeX commands specific to the
2328         // particular bibliographic style being used, in order to avoid
2329         // LaTeX errors when we switch style.
2330         FileName const aux_file(addName(temppath(), changeExtension(latexName(),".aux")));
2331         FileName const bbl_file(addName(temppath(), changeExtension(latexName(),".bbl")));
2332         LYXERR(Debug::FILES, "Removing the .aux file " << aux_file);
2333         aux_file.removeFile();
2334         LYXERR(Debug::FILES, "Removing the .bbl file " << bbl_file);
2335         bbl_file.removeFile();
2336         // Also for the parent buffer
2337         Buffer const * const pbuf = parent();
2338         if (pbuf)
2339                 pbuf->removeBiblioTempFiles();
2340 }
2341
2342
2343 bool Buffer::isDepClean(string const & name) const
2344 {
2345         DepClean::const_iterator const it = d->dep_clean.find(name);
2346         if (it == d->dep_clean.end())
2347                 return true;
2348         return it->second;
2349 }
2350
2351
2352 void Buffer::markDepClean(string const & name)
2353 {
2354         d->dep_clean[name] = true;
2355 }
2356
2357
2358 bool Buffer::getStatus(FuncRequest const & cmd, FuncStatus & flag)
2359 {
2360         if (isInternal()) {
2361                 // FIXME? if there is an Buffer LFUN that can be dispatched even
2362                 // if internal, put a switch '(cmd.action)' here.
2363                 return false;
2364         }
2365
2366         bool enable = true;
2367
2368         switch (cmd.action()) {
2369
2370         case LFUN_BUFFER_TOGGLE_READ_ONLY:
2371                 flag.setOnOff(isReadonly());
2372                 break;
2373
2374                 // FIXME: There is need for a command-line import.
2375                 //case LFUN_BUFFER_IMPORT:
2376
2377         case LFUN_BUFFER_AUTO_SAVE:
2378                 break;
2379
2380         case LFUN_BUFFER_EXPORT_CUSTOM:
2381                 // FIXME: Nothing to check here?
2382                 break;
2383
2384         case LFUN_BUFFER_EXPORT: {
2385                 docstring const arg = cmd.argument();
2386                 if (arg == "custom") {
2387                         enable = true;
2388                         break;
2389                 }
2390                 string format = to_utf8(arg);
2391                 size_t pos = format.find(' ');
2392                 if (pos != string::npos)
2393                         format = format.substr(0, pos);
2394                 enable = params().isExportable(format);
2395                 if (!enable)
2396                         flag.message(bformat(
2397                                              _("Don't know how to export to format: %1$s"), arg));
2398                 break;
2399         }
2400
2401         case LFUN_BUFFER_CHKTEX:
2402                 enable = params().isLatex() && !lyxrc.chktex_command.empty();
2403                 break;
2404
2405         case LFUN_BUILD_PROGRAM:
2406                 enable = params().isExportable("program");
2407                 break;
2408
2409         case LFUN_BRANCH_ACTIVATE:
2410         case LFUN_BRANCH_DEACTIVATE:
2411         case LFUN_BRANCH_MASTER_ACTIVATE:
2412         case LFUN_BRANCH_MASTER_DEACTIVATE: {
2413                 bool const master = (cmd.action() == LFUN_BRANCH_MASTER_ACTIVATE
2414                                      || cmd.action() == LFUN_BRANCH_MASTER_DEACTIVATE);
2415                 BranchList const & branchList = master ? masterBuffer()->params().branchlist()
2416                         : params().branchlist();
2417                 docstring const branchName = cmd.argument();
2418                 flag.setEnabled(!branchName.empty() && branchList.find(branchName));
2419                 break;
2420         }
2421
2422         case LFUN_BRANCH_ADD:
2423         case LFUN_BRANCHES_RENAME:
2424         case LFUN_BUFFER_PRINT:
2425                 // if no Buffer is present, then of course we won't be called!
2426                 break;
2427
2428         case LFUN_BUFFER_LANGUAGE:
2429                 enable = !isReadonly();
2430                 break;
2431
2432         case LFUN_BUFFER_VIEW_CACHE:
2433                 enable = (d->preview_file_).exists();
2434                 break;
2435
2436         default:
2437                 return false;
2438         }
2439         flag.setEnabled(enable);
2440         return true;
2441 }
2442
2443
2444 void Buffer::dispatch(string const & command, DispatchResult & result)
2445 {
2446         return dispatch(lyxaction.lookupFunc(command), result);
2447 }
2448
2449
2450 // NOTE We can end up here even if we have no GUI, because we are called
2451 // by LyX::exec to handled command-line requests. So we may need to check
2452 // whether we have a GUI or not. The boolean use_gui holds this information.
2453 void Buffer::dispatch(FuncRequest const & func, DispatchResult & dr)
2454 {
2455         if (isInternal()) {
2456                 // FIXME? if there is an Buffer LFUN that can be dispatched even
2457                 // if internal, put a switch '(cmd.action())' here.
2458                 dr.dispatched(false);
2459                 return;
2460         }
2461         string const argument = to_utf8(func.argument());
2462         // We'll set this back to false if need be.
2463         bool dispatched = true;
2464         undo().beginUndoGroup();
2465
2466         switch (func.action()) {
2467         case LFUN_BUFFER_TOGGLE_READ_ONLY:
2468                 if (lyxvc().inUse()) {
2469                         string log = lyxvc().toggleReadOnly();
2470                         if (!log.empty())
2471                                 dr.setMessage(log);
2472                 }
2473                 else
2474                         setReadonly(!isReadonly());
2475                 break;
2476
2477         case LFUN_BUFFER_EXPORT: {
2478                 ExportStatus const status = doExport(argument, false);
2479                 dr.setError(status != ExportSuccess);
2480                 if (status != ExportSuccess)
2481                         dr.setMessage(bformat(_("Error exporting to format: %1$s."),
2482                                               func.argument()));
2483                 break;
2484         }
2485
2486         case LFUN_BUILD_PROGRAM: {
2487                 ExportStatus const status = doExport("program", true);
2488                 dr.setError(status != ExportSuccess);
2489                 if (status != ExportSuccess)
2490                         dr.setMessage(_("Error generating literate programming code."));
2491                 break;
2492         }
2493
2494         case LFUN_BUFFER_CHKTEX:
2495                 runChktex();
2496                 break;
2497
2498         case LFUN_BUFFER_EXPORT_CUSTOM: {
2499                 string format_name;
2500                 string command = split(argument, format_name, ' ');
2501                 Format const * format = formats.getFormat(format_name);
2502                 if (!format) {
2503                         lyxerr << "Format \"" << format_name
2504                                 << "\" not recognized!"
2505                                 << endl;
2506                         break;
2507                 }
2508
2509                 // The name of the file created by the conversion process
2510                 string filename;
2511
2512                 // Output to filename
2513                 if (format->name() == "lyx") {
2514                         string const latexname = latexName(false);
2515                         filename = changeExtension(latexname,
2516                                 format->extension());
2517                         filename = addName(temppath(), filename);
2518
2519                         if (!writeFile(FileName(filename)))
2520                                 break;
2521
2522                 } else {
2523                         doExport(format_name, true, filename);
2524                 }
2525
2526                 // Substitute $$FName for filename
2527                 if (!contains(command, "$$FName"))
2528                         command = "( " + command + " ) < $$FName";
2529                 command = subst(command, "$$FName", filename);
2530
2531                 // Execute the command in the background
2532                 Systemcall call;
2533                 call.startscript(Systemcall::DontWait, command, filePath());
2534                 break;
2535         }
2536
2537         // FIXME: There is need for a command-line import.
2538         /*
2539         case LFUN_BUFFER_IMPORT:
2540                 doImport(argument);
2541                 break;
2542         */
2543
2544         case LFUN_BUFFER_AUTO_SAVE:
2545                 autoSave();
2546                 resetAutosaveTimers();
2547                 break;
2548
2549         case LFUN_BRANCH_ACTIVATE:
2550         case LFUN_BRANCH_DEACTIVATE:
2551         case LFUN_BRANCH_MASTER_ACTIVATE:
2552         case LFUN_BRANCH_MASTER_DEACTIVATE: {
2553                 bool const master = (func.action() == LFUN_BRANCH_MASTER_ACTIVATE
2554                                      || func.action() == LFUN_BRANCH_MASTER_DEACTIVATE);
2555                 Buffer * buf = master ? const_cast<Buffer *>(masterBuffer())
2556                                       : this;
2557
2558                 docstring const branch_name = func.argument();
2559                 // the case without a branch name is handled elsewhere
2560                 if (branch_name.empty()) {
2561                         dispatched = false;
2562                         break;
2563                 }
2564                 Branch * branch = buf->params().branchlist().find(branch_name);
2565                 if (!branch) {
2566                         LYXERR0("Branch " << branch_name << " does not exist.");
2567                         dr.setError(true);
2568                         docstring const msg =
2569                                 bformat(_("Branch \"%1$s\" does not exist."), branch_name);
2570                         dr.setMessage(msg);
2571                         break;
2572                 }
2573                 bool const activate = (func.action() == LFUN_BRANCH_ACTIVATE
2574                                        || func.action() == LFUN_BRANCH_MASTER_ACTIVATE);
2575                 if (branch->isSelected() != activate) {
2576                         buf->undo().recordUndoBufferParams(CursorData());
2577                         branch->setSelected(activate);
2578                         dr.setError(false);
2579                         dr.screenUpdate(Update::Force);
2580                         dr.forceBufferUpdate();
2581                 }
2582                 break;
2583         }
2584
2585         case LFUN_BRANCH_ADD: {
2586                 docstring branch_name = func.argument();
2587                 if (branch_name.empty()) {
2588                         dispatched = false;
2589                         break;
2590                 }
2591                 BranchList & branch_list = params().branchlist();
2592                 vector<docstring> const branches =
2593                         getVectorFromString(branch_name, branch_list.separator());
2594                 docstring msg;
2595                 for (vector<docstring>::const_iterator it = branches.begin();
2596                      it != branches.end(); ++it) {
2597                         branch_name = *it;
2598                         Branch * branch = branch_list.find(branch_name);
2599                         if (branch) {
2600                                 LYXERR0("Branch " << branch_name << " already exists.");
2601                                 dr.setError(true);
2602                                 if (!msg.empty())
2603                                         msg += ("\n");
2604                                 msg += bformat(_("Branch \"%1$s\" already exists."), branch_name);
2605                         } else {
2606                                 undo().recordUndoBufferParams(CursorData());
2607                                 branch_list.add(branch_name);
2608                                 branch = branch_list.find(branch_name);
2609                                 string const x11hexname = X11hexname(branch->color());
2610                                 docstring const str = branch_name + ' ' + from_ascii(x11hexname);
2611                                 lyx::dispatch(FuncRequest(LFUN_SET_COLOR, str));
2612                                 dr.setError(false);
2613                                 dr.screenUpdate(Update::Force);
2614                         }
2615                 }
2616                 if (!msg.empty())
2617                         dr.setMessage(msg);
2618                 break;
2619         }
2620
2621         case LFUN_BRANCHES_RENAME: {
2622                 if (func.argument().empty())
2623                         break;
2624
2625                 docstring const oldname = from_utf8(func.getArg(0));
2626                 docstring const newname = from_utf8(func.getArg(1));
2627                 InsetIterator it  = inset_iterator_begin(inset());
2628                 InsetIterator const end = inset_iterator_end(inset());
2629                 bool success = false;
2630                 for (; it != end; ++it) {
2631                         if (it->lyxCode() == BRANCH_CODE) {
2632                                 InsetBranch & ins = static_cast<InsetBranch &>(*it);
2633                                 if (ins.branch() == oldname) {
2634                                         undo().recordUndo(CursorData(it));
2635                                         ins.rename(newname);
2636                                         success = true;
2637                                         continue;
2638                                 }
2639                         }
2640                         if (it->lyxCode() == INCLUDE_CODE) {
2641                                 // get buffer of external file
2642                                 InsetInclude const & ins =
2643                                         static_cast<InsetInclude const &>(*it);
2644                                 Buffer * child = ins.getChildBuffer();
2645                                 if (!child)
2646                                         continue;
2647                                 child->dispatch(func, dr);
2648                         }
2649                 }
2650
2651                 if (success) {
2652                         dr.screenUpdate(Update::Force);
2653                         dr.forceBufferUpdate();
2654                 }
2655                 break;
2656         }
2657
2658         case LFUN_BUFFER_PRINT: {
2659                 // we'll assume there's a problem until we succeed
2660                 dr.setError(true);
2661                 string target = func.getArg(0);
2662                 string target_name = func.getArg(1);
2663                 string command = func.getArg(2);
2664
2665                 if (target.empty()
2666                     || target_name.empty()
2667                     || command.empty()) {
2668                         LYXERR0("Unable to parse " << func.argument());
2669                         docstring const msg =
2670                                 bformat(_("Unable to parse \"%1$s\""), func.argument());
2671                         dr.setMessage(msg);
2672                         break;
2673                 }
2674                 if (target != "printer" && target != "file") {
2675                         LYXERR0("Unrecognized target \"" << target << '"');
2676                         docstring const msg =
2677                                 bformat(_("Unrecognized target \"%1$s\""), from_utf8(target));
2678                         dr.setMessage(msg);
2679                         break;
2680                 }
2681
2682                 if (doExport("dvi", true) != ExportSuccess) {
2683                         showPrintError(absFileName());
2684                         dr.setMessage(_("Error exporting to DVI."));
2685                         break;
2686                 }
2687
2688                 // Push directory path.
2689                 string const path = temppath();
2690                 // Prevent the compiler from optimizing away p
2691                 FileName pp(path);
2692                 PathChanger p(pp);
2693
2694                 // there are three cases here:
2695                 // 1. we print to a file
2696                 // 2. we print directly to a printer
2697                 // 3. we print using a spool command (print to file first)
2698                 Systemcall one;
2699                 int res = 0;
2700                 string const dviname = changeExtension(latexName(true), "dvi");
2701
2702                 if (target == "printer") {
2703                         if (!lyxrc.print_spool_command.empty()) {
2704                                 // case 3: print using a spool
2705                                 string const psname = changeExtension(dviname,".ps");
2706                                 command += ' ' + lyxrc.print_to_file
2707                                         + quoteName(psname)
2708                                         + ' '
2709                                         + quoteName(dviname);
2710
2711                                 string command2 = lyxrc.print_spool_command + ' ';
2712                                 if (target_name != "default") {
2713                                         command2 += lyxrc.print_spool_printerprefix
2714                                                 + target_name
2715                                                 + ' ';
2716                                 }
2717                                 command2 += quoteName(psname);
2718                                 // First run dvips.
2719                                 // If successful, then spool command
2720                                 res = one.startscript(Systemcall::Wait, command,
2721                                                       filePath());
2722
2723                                 if (res == 0) {
2724                                         // If there's no GUI, we have to wait on this command. Otherwise,
2725                                         // LyX deletes the temporary directory, and with it the spooled
2726                                         // file, before it can be printed!!
2727                                         Systemcall::Starttype stype = use_gui ?
2728                                                 Systemcall::DontWait : Systemcall::Wait;
2729                                         res = one.startscript(stype, command2,
2730                                                               filePath());
2731                                 }
2732                         } else {
2733                                 // case 2: print directly to a printer
2734                                 if (target_name != "default")
2735                                         command += ' ' + lyxrc.print_to_printer + target_name + ' ';
2736                                 // as above....
2737                                 Systemcall::Starttype stype = use_gui ?
2738                                         Systemcall::DontWait : Systemcall::Wait;
2739                                 res = one.startscript(stype, command +
2740                                                 quoteName(dviname), filePath());
2741                         }
2742
2743                 } else {
2744                         // case 1: print to a file
2745                         FileName const filename(makeAbsPath(target_name, filePath()));
2746                         FileName const dvifile(makeAbsPath(dviname, path));
2747                         if (filename.exists()) {
2748                                 docstring text = bformat(
2749                                         _("The file %1$s already exists.\n\n"
2750                                           "Do you want to overwrite that file?"),
2751                                         makeDisplayPath(filename.absFileName()));
2752                                 if (Alert::prompt(_("Overwrite file?"),
2753                                                   text, 0, 1, _("&Overwrite"), _("&Cancel")) != 0)
2754                                         break;
2755                         }
2756                         command += ' ' + lyxrc.print_to_file
2757                                 + quoteName(filename.toFilesystemEncoding())
2758                                 + ' '
2759                                 + quoteName(dvifile.toFilesystemEncoding());
2760                         // as above....
2761                         Systemcall::Starttype stype = use_gui ?
2762                                 Systemcall::DontWait : Systemcall::Wait;
2763                         res = one.startscript(stype, command, filePath());
2764                 }
2765
2766                 if (res == 0)
2767                         dr.setError(false);
2768                 else {
2769                         dr.setMessage(_("Error running external commands."));
2770                         showPrintError(absFileName());
2771                 }
2772                 break;
2773         }
2774
2775         case LFUN_BUFFER_VIEW_CACHE:
2776                 if (!formats.view(*this, d->preview_file_,
2777                                   d->preview_format_))
2778                         dr.setMessage(_("Error viewing the output file."));
2779                 break;
2780
2781         default:
2782                 dispatched = false;
2783                 break;
2784         }
2785         dr.dispatched(dispatched);
2786         undo().endUndoGroup();
2787 }
2788
2789
2790 void Buffer::changeLanguage(Language const * from, Language const * to)
2791 {
2792         LASSERT(from, return);
2793         LASSERT(to, return);
2794
2795         for_each(par_iterator_begin(),
2796                  par_iterator_end(),
2797                  bind(&Paragraph::changeLanguage, _1, params(), from, to));
2798 }
2799
2800
2801 bool Buffer::isMultiLingual() const
2802 {
2803         ParConstIterator end = par_iterator_end();
2804         for (ParConstIterator it = par_iterator_begin(); it != end; ++it)
2805                 if (it->isMultiLingual(params()))
2806                         return true;
2807
2808         return false;
2809 }
2810
2811
2812 std::set<Language const *> Buffer::getLanguages() const
2813 {
2814         std::set<Language const *> languages;
2815         getLanguages(languages);
2816         return languages;
2817 }
2818
2819
2820 void Buffer::getLanguages(std::set<Language const *> & languages) const
2821 {
2822         ParConstIterator end = par_iterator_end();
2823         // add the buffer language, even if it's not actively used
2824         languages.insert(language());
2825         // iterate over the paragraphs
2826         for (ParConstIterator it = par_iterator_begin(); it != end; ++it)
2827                 it->getLanguages(languages);
2828         // also children
2829         ListOfBuffers clist = getDescendents();
2830         ListOfBuffers::const_iterator cit = clist.begin();
2831         ListOfBuffers::const_iterator const cen = clist.end();
2832         for (; cit != cen; ++cit)
2833                 (*cit)->getLanguages(languages);
2834 }
2835
2836
2837 DocIterator Buffer::getParFromID(int const id) const
2838 {
2839         Buffer * buf = const_cast<Buffer *>(this);
2840         if (id < 0) {
2841                 // John says this is called with id == -1 from undo
2842                 lyxerr << "getParFromID(), id: " << id << endl;
2843                 return doc_iterator_end(buf);
2844         }
2845
2846         for (DocIterator it = doc_iterator_begin(buf); !it.atEnd(); it.forwardPar())
2847                 if (it.paragraph().id() == id)
2848                         return it;
2849
2850         return doc_iterator_end(buf);
2851 }
2852
2853
2854 bool Buffer::hasParWithID(int const id) const
2855 {
2856         return !getParFromID(id).atEnd();
2857 }
2858
2859
2860 ParIterator Buffer::par_iterator_begin()
2861 {
2862         return ParIterator(doc_iterator_begin(this));
2863 }
2864
2865
2866 ParIterator Buffer::par_iterator_end()
2867 {
2868         return ParIterator(doc_iterator_end(this));
2869 }
2870
2871
2872 ParConstIterator Buffer::par_iterator_begin() const
2873 {
2874         return ParConstIterator(doc_iterator_begin(this));
2875 }
2876
2877
2878 ParConstIterator Buffer::par_iterator_end() const
2879 {
2880         return ParConstIterator(doc_iterator_end(this));
2881 }
2882
2883
2884 Language const * Buffer::language() const
2885 {
2886         return params().language;
2887 }
2888
2889
2890 docstring const Buffer::B_(string const & l10n) const
2891 {
2892         return params().B_(l10n);
2893 }
2894
2895
2896 bool Buffer::isClean() const
2897 {
2898         return d->lyx_clean;
2899 }
2900
2901
2902 bool Buffer::isExternallyModified(CheckMethod method) const
2903 {
2904         LASSERT(d->filename.exists(), return false);
2905         // if method == timestamp, check timestamp before checksum
2906         return (method == checksum_method
2907                 || d->timestamp_ != d->filename.lastModified())
2908                 && d->checksum_ != d->filename.checksum();
2909 }
2910
2911
2912 void Buffer::saveCheckSum() const
2913 {
2914         FileName const & file = d->filename;
2915
2916         file.refresh();
2917         if (file.exists()) {
2918                 d->timestamp_ = file.lastModified();
2919                 d->checksum_ = file.checksum();
2920         } else {
2921                 // in the case of save to a new file.
2922                 d->timestamp_ = 0;
2923                 d->checksum_ = 0;
2924         }
2925 }
2926
2927
2928 void Buffer::markClean() const
2929 {
2930         if (!d->lyx_clean) {
2931                 d->lyx_clean = true;
2932                 updateTitles();
2933         }
2934         // if the .lyx file has been saved, we don't need an
2935         // autosave
2936         d->bak_clean = true;
2937         d->undo_.markDirty();
2938 }
2939
2940
2941 void Buffer::setUnnamed(bool flag)
2942 {
2943         d->unnamed = flag;
2944 }
2945
2946
2947 bool Buffer::isUnnamed() const
2948 {
2949         return d->unnamed;
2950 }
2951
2952
2953 /// \note
2954 /// Don't check unnamed, here: isInternal() is used in
2955 /// newBuffer(), where the unnamed flag has not been set by anyone
2956 /// yet. Also, for an internal buffer, there should be no need for
2957 /// retrieving fileName() nor for checking if it is unnamed or not.
2958 bool Buffer::isInternal() const
2959 {
2960         return d->internal_buffer;
2961 }
2962
2963
2964 void Buffer::setInternal(bool flag)
2965 {
2966         d->internal_buffer = flag;
2967 }
2968
2969
2970 void Buffer::markDirty()
2971 {
2972         if (d->lyx_clean) {
2973                 d->lyx_clean = false;
2974                 updateTitles();
2975         }
2976         d->bak_clean = false;
2977
2978         DepClean::iterator it = d->dep_clean.begin();
2979         DepClean::const_iterator const end = d->dep_clean.end();
2980
2981         for (; it != end; ++it)
2982                 it->second = false;
2983 }
2984
2985
2986 FileName Buffer::fileName() const
2987 {
2988         return d->filename;
2989 }
2990
2991
2992 string Buffer::absFileName() const
2993 {
2994         return d->filename.absFileName();
2995 }
2996
2997
2998 string Buffer::filePath() const
2999 {
3000         string const abs = d->filename.onlyPath().absFileName();
3001         if (abs.empty())
3002                 return abs;
3003         int last = abs.length() - 1;
3004
3005         return abs[last] == '/' ? abs : abs + '/';
3006 }
3007
3008
3009 bool Buffer::isReadonly() const
3010 {
3011         return d->read_only;
3012 }
3013
3014
3015 void Buffer::setParent(Buffer const * buffer)
3016 {
3017         // Avoids recursive include.
3018         d->setParent(buffer == this ? 0 : buffer);
3019         updateMacros();
3020 }
3021
3022
3023 Buffer const * Buffer::parent() const
3024 {
3025         return d->parent();
3026 }
3027
3028
3029 ListOfBuffers Buffer::allRelatives() const
3030 {
3031         ListOfBuffers lb = masterBuffer()->getDescendents();
3032         lb.push_front(const_cast<Buffer *>(masterBuffer()));
3033         return lb;
3034 }
3035
3036
3037 Buffer const * Buffer::masterBuffer() const
3038 {
3039         // FIXME Should be make sure we are not in some kind
3040         // of recursive include? A -> B -> A will crash this.
3041         Buffer const * const pbuf = d->parent();
3042         if (!pbuf)
3043                 return this;
3044
3045         return pbuf->masterBuffer();
3046 }
3047
3048
3049 bool Buffer::isChild(Buffer * child) const
3050 {
3051         return d->children_positions.find(child) != d->children_positions.end();
3052 }
3053
3054
3055 DocIterator Buffer::firstChildPosition(Buffer const * child)
3056 {
3057         Impl::BufferPositionMap::iterator it;
3058         it = d->children_positions.find(child);
3059         if (it == d->children_positions.end())
3060                 return DocIterator(this);
3061         return it->second;
3062 }
3063
3064
3065 bool Buffer::hasChildren() const
3066 {
3067         return !d->children_positions.empty();
3068 }
3069
3070
3071 void Buffer::collectChildren(ListOfBuffers & clist, bool grand_children) const
3072 {
3073         // loop over children
3074         Impl::BufferPositionMap::iterator it = d->children_positions.begin();
3075         Impl::BufferPositionMap::iterator end = d->children_positions.end();
3076         for (; it != end; ++it) {
3077                 Buffer * child = const_cast<Buffer *>(it->first);
3078                 // No duplicates
3079                 ListOfBuffers::const_iterator bit = find(clist.begin(), clist.end(), child);
3080                 if (bit != clist.end())
3081                         continue;
3082                 clist.push_back(child);
3083                 if (grand_children)
3084                         // there might be grandchildren
3085                         child->collectChildren(clist, true);
3086         }
3087 }
3088
3089
3090 ListOfBuffers Buffer::getChildren() const
3091 {
3092         ListOfBuffers v;
3093         collectChildren(v, false);
3094         // Make sure we have not included ourselves.
3095         ListOfBuffers::iterator bit = find(v.begin(), v.end(), this);
3096         if (bit != v.end()) {
3097                 LYXERR0("Recursive include detected in `" << fileName() << "'.");
3098                 v.erase(bit);
3099         }
3100         return v;
3101 }
3102
3103
3104 ListOfBuffers Buffer::getDescendents() const
3105 {
3106         ListOfBuffers v;
3107         collectChildren(v, true);
3108         // Make sure we have not included ourselves.
3109         ListOfBuffers::iterator bit = find(v.begin(), v.end(), this);
3110         if (bit != v.end()) {
3111                 LYXERR0("Recursive include detected in `" << fileName() << "'.");
3112                 v.erase(bit);
3113         }
3114         return v;
3115 }
3116
3117
3118 template<typename M>
3119 typename M::const_iterator greatest_below(M & m, typename M::key_type const & x)
3120 {
3121         if (m.empty())
3122                 return m.end();
3123
3124         typename M::const_iterator it = m.lower_bound(x);
3125         if (it == m.begin())
3126                 return m.end();
3127
3128         it--;
3129         return it;
3130 }
3131
3132
3133 MacroData const * Buffer::Impl::getBufferMacro(docstring const & name,
3134                                          DocIterator const & pos) const
3135 {
3136         LYXERR(Debug::MACROS, "Searching for " << to_ascii(name) << " at " << pos);
3137
3138         // if paragraphs have no macro context set, pos will be empty
3139         if (pos.empty())
3140                 return 0;
3141
3142         // we haven't found anything yet
3143         DocIterator bestPos = owner_->par_iterator_begin();
3144         MacroData const * bestData = 0;
3145
3146         // find macro definitions for name
3147         NamePositionScopeMacroMap::const_iterator nameIt = macros.find(name);
3148         if (nameIt != macros.end()) {
3149                 // find last definition in front of pos or at pos itself
3150                 PositionScopeMacroMap::const_iterator it
3151                         = greatest_below(nameIt->second, pos);
3152                 if (it != nameIt->second.end()) {
3153                         while (true) {
3154                                 // scope ends behind pos?
3155                                 if (pos < it->second.scope) {
3156                                         // Looks good, remember this. If there
3157                                         // is no external macro behind this,
3158                                         // we found the right one already.
3159                                         bestPos = it->first;
3160                                         bestData = &it->second.macro;
3161                                         break;
3162                                 }
3163
3164                                 // try previous macro if there is one
3165                                 if (it == nameIt->second.begin())
3166                                         break;
3167                                 --it;
3168                         }
3169                 }
3170         }
3171
3172         // find macros in included files
3173         PositionScopeBufferMap::const_iterator it
3174                 = greatest_below(position_to_children, pos);
3175         if (it == position_to_children.end())
3176                 // no children before
3177                 return bestData;
3178
3179         while (true) {
3180                 // do we know something better (i.e. later) already?
3181                 if (it->first < bestPos )
3182                         break;
3183
3184                 // scope ends behind pos?
3185                 if (pos < it->second.scope
3186                         && (cloned_buffer_ ||
3187                             theBufferList().isLoaded(it->second.buffer))) {
3188                         // look for macro in external file
3189                         macro_lock = true;
3190                         MacroData const * data
3191                                 = it->second.buffer->getMacro(name, false);
3192                         macro_lock = false;
3193                         if (data) {
3194                                 bestPos = it->first;
3195                                 bestData = data;
3196                                 break;
3197                         }
3198                 }
3199
3200                 // try previous file if there is one
3201                 if (it == position_to_children.begin())
3202                         break;
3203                 --it;
3204         }
3205
3206         // return the best macro we have found
3207         return bestData;
3208 }
3209
3210
3211 MacroData const * Buffer::getMacro(docstring const & name,
3212         DocIterator const & pos, bool global) const
3213 {
3214         if (d->macro_lock)
3215                 return 0;
3216
3217         // query buffer macros
3218         MacroData const * data = d->getBufferMacro(name, pos);
3219         if (data != 0)
3220                 return data;
3221
3222         // If there is a master buffer, query that
3223         Buffer const * const pbuf = d->parent();
3224         if (pbuf) {
3225                 d->macro_lock = true;
3226                 MacroData const * macro = pbuf->getMacro(
3227                         name, *this, false);
3228                 d->macro_lock = false;
3229                 if (macro)
3230                         return macro;
3231         }
3232
3233         if (global) {
3234                 data = MacroTable::globalMacros().get(name);
3235                 if (data != 0)
3236                         return data;
3237         }
3238
3239         return 0;
3240 }
3241
3242
3243 MacroData const * Buffer::getMacro(docstring const & name, bool global) const
3244 {
3245         // set scope end behind the last paragraph
3246         DocIterator scope = par_iterator_begin();
3247         scope.pit() = scope.lastpit() + 1;
3248
3249         return getMacro(name, scope, global);
3250 }
3251
3252
3253 MacroData const * Buffer::getMacro(docstring const & name,
3254         Buffer const & child, bool global) const
3255 {
3256         // look where the child buffer is included first
3257         Impl::BufferPositionMap::iterator it = d->children_positions.find(&child);
3258         if (it == d->children_positions.end())
3259                 return 0;
3260
3261         // check for macros at the inclusion position
3262         return getMacro(name, it->second, global);
3263 }
3264
3265
3266 void Buffer::Impl::updateMacros(DocIterator & it, DocIterator & scope)
3267 {
3268         pit_type const lastpit = it.lastpit();
3269
3270         // look for macros in each paragraph
3271         while (it.pit() <= lastpit) {
3272                 Paragraph & par = it.paragraph();
3273
3274                 // iterate over the insets of the current paragraph
3275                 InsetList const & insets = par.insetList();
3276                 InsetList::const_iterator iit = insets.begin();
3277                 InsetList::const_iterator end = insets.end();
3278                 for (; iit != end; ++iit) {
3279                         it.pos() = iit->pos;
3280
3281                         // is it a nested text inset?
3282                         if (iit->inset->asInsetText()) {
3283                                 // Inset needs its own scope?
3284                                 InsetText const * itext = iit->inset->asInsetText();
3285                                 bool newScope = itext->isMacroScope();
3286
3287                                 // scope which ends just behind the inset
3288                                 DocIterator insetScope = it;
3289                                 ++insetScope.pos();
3290
3291                                 // collect macros in inset
3292                                 it.push_back(CursorSlice(*iit->inset));
3293                                 updateMacros(it, newScope ? insetScope : scope);
3294                                 it.pop_back();
3295                                 continue;
3296                         }
3297
3298                         if (iit->inset->asInsetTabular()) {
3299                                 CursorSlice slice(*iit->inset);
3300                                 size_t const numcells = slice.nargs();
3301                                 for (; slice.idx() < numcells; slice.forwardIdx()) {
3302                                         it.push_back(slice);
3303                                         updateMacros(it, scope);
3304                                         it.pop_back();
3305                                 }
3306                                 continue;
3307                         }
3308
3309                         // is it an external file?
3310                         if (iit->inset->lyxCode() == INCLUDE_CODE) {
3311                                 // get buffer of external file
3312                                 InsetInclude const & inset =
3313                                         static_cast<InsetInclude const &>(*iit->inset);
3314                                 macro_lock = true;
3315                                 Buffer * child = inset.getChildBuffer();
3316                                 macro_lock = false;
3317                                 if (!child)
3318                                         continue;
3319
3320                                 // register its position, but only when it is
3321                                 // included first in the buffer
3322                                 if (children_positions.find(child) ==
3323                                         children_positions.end())
3324                                                 children_positions[child] = it;
3325
3326                                 // register child with its scope
3327                                 position_to_children[it] = Impl::ScopeBuffer(scope, child);
3328                                 continue;
3329                         }
3330
3331                         InsetMath * im = iit->inset->asInsetMath();
3332                         if (doing_export && im)  {
3333                                 InsetMathHull * hull = im->asHullInset();
3334                                 if (hull)
3335                                         hull->recordLocation(it);
3336                         }
3337
3338                         if (iit->inset->lyxCode() != MATHMACRO_CODE)
3339                                 continue;
3340
3341                         // get macro data
3342                         MathMacroTemplate & macroTemplate =
3343                                 *iit->inset->asInsetMath()->asMacroTemplate();
3344                         MacroContext mc(owner_, it);
3345                         macroTemplate.updateToContext(mc);
3346
3347                         // valid?
3348                         bool valid = macroTemplate.validMacro();
3349                         // FIXME: Should be fixNameAndCheckIfValid() in fact,
3350                         // then the BufferView's cursor will be invalid in
3351                         // some cases which leads to crashes.
3352                         if (!valid)
3353                                 continue;
3354
3355                         // register macro
3356                         // FIXME (Abdel), I don't understand why we pass 'it' here
3357                         // instead of 'macroTemplate' defined above... is this correct?
3358                         macros[macroTemplate.name()][it] =
3359                                 Impl::ScopeMacro(scope, MacroData(const_cast<Buffer *>(owner_), it));
3360                 }
3361
3362                 // next paragraph
3363                 it.pit()++;
3364                 it.pos() = 0;
3365         }
3366 }
3367
3368
3369 void Buffer::updateMacros() const
3370 {
3371         if (d->macro_lock)
3372                 return;
3373
3374         LYXERR(Debug::MACROS, "updateMacro of " << d->filename.onlyFileName());
3375
3376         // start with empty table
3377         d->macros.clear();
3378         d->children_positions.clear();
3379         d->position_to_children.clear();
3380
3381         // Iterate over buffer, starting with first paragraph
3382         // The scope must be bigger than any lookup DocIterator
3383         // later. For the global lookup, lastpit+1 is used, hence
3384         // we use lastpit+2 here.
3385         DocIterator it = par_iterator_begin();
3386         DocIterator outerScope = it;
3387         outerScope.pit() = outerScope.lastpit() + 2;
3388         d->updateMacros(it, outerScope);
3389 }
3390
3391
3392 void Buffer::getUsedBranches(std::list<docstring> & result, bool const from_master) const
3393 {
3394         InsetIterator it  = inset_iterator_begin(inset());
3395         InsetIterator const end = inset_iterator_end(inset());
3396         for (; it != end; ++it) {
3397                 if (it->lyxCode() == BRANCH_CODE) {
3398                         InsetBranch & br = static_cast<InsetBranch &>(*it);
3399                         docstring const name = br.branch();
3400                         if (!from_master && !params().branchlist().find(name))
3401                                 result.push_back(name);
3402                         else if (from_master && !masterBuffer()->params().branchlist().find(name))
3403                                 result.push_back(name);
3404                         continue;
3405                 }
3406                 if (it->lyxCode() == INCLUDE_CODE) {
3407                         // get buffer of external file
3408                         InsetInclude const & ins =
3409                                 static_cast<InsetInclude const &>(*it);
3410                         Buffer * child = ins.getChildBuffer();
3411                         if (!child)
3412                                 continue;
3413                         child->getUsedBranches(result, true);
3414                 }
3415         }
3416         // remove duplicates
3417         result.unique();
3418 }
3419
3420
3421 void Buffer::updateMacroInstances(UpdateType utype) const
3422 {
3423         LYXERR(Debug::MACROS, "updateMacroInstances for "
3424                 << d->filename.onlyFileName());
3425         DocIterator it = doc_iterator_begin(this);
3426         it.forwardInset();
3427         DocIterator const end = doc_iterator_end(this);
3428         for (; it != end; it.forwardInset()) {
3429                 // look for MathData cells in InsetMathNest insets
3430                 InsetMath * minset = it.nextInset()->asInsetMath();
3431                 if (!minset)
3432                         continue;
3433
3434                 // update macro in all cells of the InsetMathNest
3435                 DocIterator::idx_type n = minset->nargs();
3436                 MacroContext mc = MacroContext(this, it);
3437                 for (DocIterator::idx_type i = 0; i < n; ++i) {
3438                         MathData & data = minset->cell(i);
3439                         data.updateMacros(0, mc, utype);
3440                 }
3441         }
3442 }
3443
3444
3445 void Buffer::listMacroNames(MacroNameSet & macros) const
3446 {
3447         if (d->macro_lock)
3448                 return;
3449
3450         d->macro_lock = true;
3451
3452         // loop over macro names
3453         Impl::NamePositionScopeMacroMap::iterator nameIt = d->macros.begin();
3454         Impl::NamePositionScopeMacroMap::iterator nameEnd = d->macros.end();
3455         for (; nameIt != nameEnd; ++nameIt)
3456                 macros.insert(nameIt->first);
3457
3458         // loop over children
3459         Impl::BufferPositionMap::iterator it = d->children_positions.begin();
3460         Impl::BufferPositionMap::iterator end = d->children_positions.end();
3461         for (; it != end; ++it)
3462                 it->first->listMacroNames(macros);
3463
3464         // call parent
3465         Buffer const * const pbuf = d->parent();
3466         if (pbuf)
3467                 pbuf->listMacroNames(macros);
3468
3469         d->macro_lock = false;
3470 }
3471
3472
3473 void Buffer::listParentMacros(MacroSet & macros, LaTeXFeatures & features) const
3474 {
3475         Buffer const * const pbuf = d->parent();
3476         if (!pbuf)
3477                 return;
3478
3479         MacroNameSet names;
3480         pbuf->listMacroNames(names);
3481
3482         // resolve macros
3483         MacroNameSet::iterator it = names.begin();
3484         MacroNameSet::iterator end = names.end();
3485         for (; it != end; ++it) {
3486                 // defined?
3487                 MacroData const * data =
3488                 pbuf->getMacro(*it, *this, false);
3489                 if (data) {
3490                         macros.insert(data);
3491
3492                         // we cannot access the original MathMacroTemplate anymore
3493                         // here to calls validate method. So we do its work here manually.
3494                         // FIXME: somehow make the template accessible here.
3495                         if (data->optionals() > 0)
3496                                 features.require("xargs");
3497                 }
3498         }
3499 }
3500
3501
3502 Buffer::References & Buffer::getReferenceCache(docstring const & label)
3503 {
3504         if (d->parent())
3505                 return const_cast<Buffer *>(masterBuffer())->getReferenceCache(label);
3506
3507         RefCache::iterator it = d->ref_cache_.find(label);
3508         if (it != d->ref_cache_.end())
3509                 return it->second.second;
3510
3511         static InsetLabel const * dummy_il = 0;
3512         static References const dummy_refs;
3513         it = d->ref_cache_.insert(
3514                 make_pair(label, make_pair(dummy_il, dummy_refs))).first;
3515         return it->second.second;
3516 }
3517
3518
3519 Buffer::References const & Buffer::references(docstring const & label) const
3520 {
3521         return const_cast<Buffer *>(this)->getReferenceCache(label);
3522 }
3523
3524
3525 void Buffer::addReference(docstring const & label, Inset * inset, ParIterator it)
3526 {
3527         References & refs = getReferenceCache(label);
3528         refs.push_back(make_pair(inset, it));
3529 }
3530
3531
3532 void Buffer::setInsetLabel(docstring const & label, InsetLabel const * il)
3533 {
3534         masterBuffer()->d->ref_cache_[label].first = il;
3535 }
3536
3537
3538 InsetLabel const * Buffer::insetLabel(docstring const & label) const
3539 {
3540         return masterBuffer()->d->ref_cache_[label].first;
3541 }
3542
3543
3544 void Buffer::clearReferenceCache() const
3545 {
3546         if (!d->parent())
3547                 d->ref_cache_.clear();
3548 }
3549
3550
3551 void Buffer::changeRefsIfUnique(docstring const & from, docstring const & to)
3552 {
3553         //FIXME: This does not work for child documents yet.
3554         reloadBibInfoCache();
3555
3556         // Check if the label 'from' appears more than once
3557         BiblioInfo const & keys = masterBibInfo();
3558         BiblioInfo::const_iterator bit  = keys.begin();
3559         BiblioInfo::const_iterator bend = keys.end();
3560         vector<docstring> labels;
3561
3562         for (; bit != bend; ++bit)
3563                 // FIXME UNICODE
3564                 labels.push_back(bit->first);
3565
3566         if (count(labels.begin(), labels.end(), from) > 1)
3567                 return;
3568
3569         string const paramName = "key";
3570         for (InsetIterator it = inset_iterator_begin(inset()); it; ++it) {
3571                 if (it->lyxCode() != CITE_CODE)
3572                         continue;
3573                 InsetCommand * inset = it->asInsetCommand();
3574                 docstring const oldValue = inset->getParam(paramName);
3575                 if (oldValue == from)
3576                         inset->setParam(paramName, to);
3577         }
3578 }
3579
3580
3581 void Buffer::getSourceCode(odocstream & os, string const & format,
3582                            pit_type par_begin, pit_type par_end,
3583                            OutputWhat output, bool master) const
3584 {
3585         OutputParams runparams(&params().encoding());
3586         runparams.nice = true;
3587         runparams.flavor = params().getOutputFlavor(format);
3588         runparams.linelen = lyxrc.plaintext_linelen;
3589         // No side effect of file copying and image conversion
3590         runparams.dryrun = true;
3591
3592         if (output == CurrentParagraph) {
3593                 runparams.par_begin = par_begin;
3594                 runparams.par_end = par_end;
3595                 if (par_begin + 1 == par_end) {
3596                         os << "% "
3597                            << bformat(_("Preview source code for paragraph %1$d"), par_begin)
3598                            << "\n\n";
3599                 } else {
3600                         os << "% "
3601                            << bformat(_("Preview source code from paragraph %1$s to %2$s"),
3602                                         convert<docstring>(par_begin),
3603                                         convert<docstring>(par_end - 1))
3604                            << "\n\n";
3605                 }
3606                 // output paragraphs
3607                 if (runparams.flavor == OutputParams::LYX) {
3608                         Paragraph const & par = text().paragraphs()[par_begin];
3609                         ostringstream ods;
3610                         depth_type dt = par.getDepth();
3611                         par.write(ods, params(), dt);
3612                         os << from_utf8(ods.str());
3613                 } else if (runparams.flavor == OutputParams::HTML) {
3614                         XHTMLStream xs(os);
3615                         setMathFlavor(runparams);
3616                         xhtmlParagraphs(text(), *this, xs, runparams);
3617                 } else if (runparams.flavor == OutputParams::TEXT) {
3618                         bool dummy = false;
3619                         // FIXME Handles only one paragraph, unlike the others.
3620                         // Probably should have some routine with a signature like them.
3621                         writePlaintextParagraph(*this,
3622                                 text().paragraphs()[par_begin], os, runparams, dummy);
3623                 } else if (params().isDocBook()) {
3624                         docbookParagraphs(text(), *this, os, runparams);
3625                 } else {
3626                         // If we are previewing a paragraph, even if this is the
3627                         // child of some other buffer, let's cut the link here,
3628                         // so that no concurring settings from the master
3629                         // (e.g. branch state) interfere (see #8101).
3630                         if (!master)
3631                                 d->ignore_parent = true;
3632                         // We need to validate the Buffer params' features here
3633                         // in order to know if we should output polyglossia
3634                         // macros (instead of babel macros)
3635                         LaTeXFeatures features(*this, params(), runparams);
3636                         params().validate(features);
3637                         runparams.use_polyglossia = features.usePolyglossia();
3638                         TexRow texrow;
3639                         texrow.reset();
3640                         texrow.newline();
3641                         texrow.newline();
3642                         // latex or literate
3643                         otexstream ots(os, texrow);
3644
3645                         // the real stuff
3646                         latexParagraphs(*this, text(), ots, runparams);
3647
3648                         // Restore the parenthood
3649                         if (!master)
3650                                 d->ignore_parent = false;
3651                 }
3652         } else {
3653                 os << "% ";
3654                 if (output == FullSource)
3655                         os << _("Preview source code");
3656                 else if (output == OnlyPreamble)
3657                         os << _("Preview preamble");
3658                 else if (output == OnlyBody)
3659                         os << _("Preview body");
3660                 os << "\n\n";
3661                 if (runparams.flavor == OutputParams::LYX) {
3662                         ostringstream ods;
3663                         if (output == FullSource)
3664                                 write(ods);
3665                         else if (output == OnlyPreamble)
3666                                 params().writeFile(ods);
3667                         else if (output == OnlyBody)
3668                                 text().write(ods);
3669                         os << from_utf8(ods.str());
3670                 } else if (runparams.flavor == OutputParams::HTML) {
3671                         writeLyXHTMLSource(os, runparams, output);
3672                 } else if (runparams.flavor == OutputParams::TEXT) {
3673                         if (output == OnlyPreamble) {
3674                                 os << "% "<< _("Plain text does not have a preamble.");
3675                         } else
3676                                 writePlaintextFile(*this, os, runparams);
3677                 } else if (params().isDocBook()) {
3678                                 writeDocBookSource(os, absFileName(), runparams, output);
3679                 } else {
3680                         // latex or literate
3681                         d->texrow.reset();
3682                         d->texrow.newline();
3683                         d->texrow.newline();
3684                         otexstream ots(os, d->texrow);
3685                         if (master)
3686                                 runparams.is_child = true;
3687                         writeLaTeXSource(ots, string(), runparams, output);
3688                 }
3689         }
3690 }
3691
3692
3693 ErrorList & Buffer::errorList(string const & type) const
3694 {
3695         return d->errorLists[type];
3696 }
3697
3698
3699 void Buffer::updateTocItem(std::string const & type,
3700         DocIterator const & dit) const
3701 {
3702         if (d->gui_)
3703                 d->gui_->updateTocItem(type, dit);
3704 }
3705
3706
3707 void Buffer::structureChanged() const
3708 {
3709         if (d->gui_)
3710                 d->gui_->structureChanged();
3711 }
3712
3713
3714 void Buffer::errors(string const & err, bool from_master) const
3715 {
3716         if (d->gui_)
3717                 d->gui_->errors(err, from_master);
3718 }
3719
3720
3721 void Buffer::message(docstring const & msg) const
3722 {
3723         if (d->gui_)
3724                 d->gui_->message(msg);
3725 }
3726
3727
3728 void Buffer::setBusy(bool on) const
3729 {
3730         if (d->gui_)
3731                 d->gui_->setBusy(on);
3732 }
3733
3734
3735 void Buffer::updateTitles() const
3736 {
3737         if (d->wa_)
3738                 d->wa_->updateTitles();
3739 }
3740
3741
3742 void Buffer::resetAutosaveTimers() const
3743 {
3744         if (d->gui_)
3745                 d->gui_->resetAutosaveTimers();
3746 }
3747
3748
3749 bool Buffer::hasGuiDelegate() const
3750 {
3751         return d->gui_;
3752 }
3753
3754
3755 void Buffer::setGuiDelegate(frontend::GuiBufferDelegate * gui)
3756 {
3757         d->gui_ = gui;
3758 }
3759
3760
3761
3762 namespace {
3763
3764 class AutoSaveBuffer : public ForkedProcess {
3765 public:
3766         ///
3767         AutoSaveBuffer(Buffer const & buffer, FileName const & fname)
3768                 : buffer_(buffer), fname_(fname) {}
3769         ///
3770         virtual shared_ptr<ForkedProcess> clone() const
3771         {
3772                 return shared_ptr<ForkedProcess>(new AutoSaveBuffer(*this));
3773         }
3774         ///
3775         int start()
3776         {
3777                 command_ = to_utf8(bformat(_("Auto-saving %1$s"),
3778                                                  from_utf8(fname_.absFileName())));
3779                 return run(DontWait);
3780         }
3781 private:
3782         ///
3783         virtual int generateChild();
3784         ///
3785         Buffer const & buffer_;
3786         FileName fname_;
3787 };
3788
3789
3790 int AutoSaveBuffer::generateChild()
3791 {
3792 #if defined(__APPLE__)
3793         /* FIXME fork() is not usable for autosave on Mac OS X 10.6 (snow leopard)
3794          *   We should use something else like threads.
3795          *
3796          * Since I do not know how to determine at run time what is the OS X
3797          * version, I just disable forking altogether for now (JMarc)
3798          */
3799         pid_t const pid = -1;
3800 #else
3801         // tmp_ret will be located (usually) in /tmp
3802         // will that be a problem?
3803         // Note that this calls ForkedCalls::fork(), so it's
3804         // ok cross-platform.
3805         pid_t const pid = fork();
3806         // If you want to debug the autosave
3807         // you should set pid to -1, and comment out the fork.
3808         if (pid != 0 && pid != -1)
3809                 return pid;
3810 #endif
3811
3812         // pid = -1 signifies that lyx was unable
3813         // to fork. But we will do the save
3814         // anyway.
3815         bool failed = false;
3816         TempFile tempfile("lyxautoXXXXXX.lyx");
3817         tempfile.setAutoRemove(false);
3818         FileName const tmp_ret = tempfile.name();
3819         if (!tmp_ret.empty()) {
3820                 if (!buffer_.writeFile(tmp_ret))
3821                         failed = true;
3822                 else if (!tmp_ret.moveTo(fname_))
3823                         failed = true;
3824         } else
3825                 failed = true;
3826
3827         if (failed) {
3828                 // failed to write/rename tmp_ret so try writing direct
3829                 if (!buffer_.writeFile(fname_)) {
3830                         // It is dangerous to do this in the child,
3831                         // but safe in the parent, so...
3832                         if (pid == -1) // emit message signal.
3833                                 buffer_.message(_("Autosave failed!"));
3834                 }
3835         }
3836
3837         if (pid == 0) // we are the child so...
3838                 _exit(0);
3839
3840         return pid;
3841 }
3842
3843 } // namespace anon
3844
3845
3846 FileName Buffer::getEmergencyFileName() const
3847 {
3848         return FileName(d->filename.absFileName() + ".emergency");
3849 }
3850
3851
3852 FileName Buffer::getAutosaveFileName() const
3853 {
3854         // if the document is unnamed try to save in the backup dir, else
3855         // in the default document path, and as a last try in the filePath,
3856         // which will most often be the temporary directory
3857         string fpath;
3858         if (isUnnamed())
3859                 fpath = lyxrc.backupdir_path.empty() ? lyxrc.document_path
3860                         : lyxrc.backupdir_path;
3861         if (!isUnnamed() || fpath.empty() || !FileName(fpath).exists())
3862                 fpath = filePath();
3863
3864         string const fname = "#" + d->filename.onlyFileName() + "#";
3865
3866         return makeAbsPath(fname, fpath);
3867 }
3868
3869
3870 void Buffer::removeAutosaveFile() const
3871 {
3872         FileName const f = getAutosaveFileName();
3873         if (f.exists())
3874                 f.removeFile();
3875 }
3876
3877
3878 void Buffer::moveAutosaveFile(support::FileName const & oldauto) const
3879 {
3880         FileName const newauto = getAutosaveFileName();
3881         oldauto.refresh();
3882         if (newauto != oldauto && oldauto.exists())
3883                 if (!oldauto.moveTo(newauto))
3884                         LYXERR0("Unable to move autosave file `" << oldauto << "'!");
3885 }
3886
3887
3888 bool Buffer::autoSave() const
3889 {
3890         Buffer const * buf = d->cloned_buffer_ ? d->cloned_buffer_ : this;
3891         if (buf->d->bak_clean || isReadonly())
3892                 return true;
3893
3894         message(_("Autosaving current document..."));
3895         buf->d->bak_clean = true;
3896
3897         FileName const fname = getAutosaveFileName();
3898         LASSERT(d->cloned_buffer_, return false);
3899
3900         // If this buffer is cloned, we assume that
3901         // we are running in a separate thread already.
3902         TempFile tempfile("lyxautoXXXXXX.lyx");
3903         tempfile.setAutoRemove(false);
3904         FileName const tmp_ret = tempfile.name();
3905         if (!tmp_ret.empty()) {
3906                 writeFile(tmp_ret);
3907                 // assume successful write of tmp_ret
3908                 if (tmp_ret.moveTo(fname))
3909                         return true;
3910         }
3911         // failed to write/rename tmp_ret so try writing direct
3912         return writeFile(fname);
3913 }
3914
3915
3916 void Buffer::setExportStatus(bool e) const
3917 {
3918         d->doing_export = e;
3919         ListOfBuffers clist = getDescendents();
3920         ListOfBuffers::const_iterator cit = clist.begin();
3921         ListOfBuffers::const_iterator const cen = clist.end();
3922         for (; cit != cen; ++cit)
3923                 (*cit)->d->doing_export = e;
3924 }
3925
3926
3927 bool Buffer::isExporting() const
3928 {
3929         return d->doing_export;
3930 }
3931
3932
3933 Buffer::ExportStatus Buffer::doExport(string const & target, bool put_in_tempdir)
3934         const
3935 {
3936         string result_file;
3937         return doExport(target, put_in_tempdir, result_file);
3938 }
3939
3940 Buffer::ExportStatus Buffer::doExport(string const & target, bool put_in_tempdir,
3941         string & result_file) const
3942 {
3943         bool const update_unincluded =
3944                         params().maintain_unincluded_children
3945                         && !params().getIncludedChildren().empty();
3946
3947         // (1) export with all included children (omit \includeonly)
3948         if (update_unincluded) {
3949                 ExportStatus const status =
3950                         doExport(target, put_in_tempdir, true, result_file);
3951                 if (status != ExportSuccess)
3952                         return status;
3953         }
3954         // (2) export with included children only
3955         return doExport(target, put_in_tempdir, false, result_file);
3956 }
3957
3958
3959 void Buffer::setMathFlavor(OutputParams & op) const
3960 {
3961         switch (params().html_math_output) {
3962         case BufferParams::MathML:
3963                 op.math_flavor = OutputParams::MathAsMathML;
3964                 break;
3965         case BufferParams::HTML:
3966                 op.math_flavor = OutputParams::MathAsHTML;
3967                 break;
3968         case BufferParams::Images:
3969                 op.math_flavor = OutputParams::MathAsImages;
3970                 break;
3971         case BufferParams::LaTeX:
3972                 op.math_flavor = OutputParams::MathAsLaTeX;
3973                 break;
3974         }
3975 }
3976
3977
3978 Buffer::ExportStatus Buffer::doExport(string const & target, bool put_in_tempdir,
3979         bool includeall, string & result_file) const
3980 {
3981         LYXERR(Debug::FILES, "target=" << target);
3982         OutputParams runparams(&params().encoding());
3983         string format = target;
3984         string dest_filename;
3985         size_t pos = target.find(' ');
3986         if (pos != string::npos) {
3987                 dest_filename = target.substr(pos + 1, target.length() - pos - 1);
3988                 format = target.substr(0, pos);
3989                 runparams.export_folder = FileName(dest_filename).onlyPath().realPath();
3990                 FileName(dest_filename).onlyPath().createPath();
3991                 LYXERR(Debug::FILES, "format=" << format << ", dest_filename=" << dest_filename << ", export_folder=" << runparams.export_folder);
3992         }
3993         MarkAsExporting exporting(this);
3994         string backend_format;
3995         runparams.flavor = OutputParams::LATEX;
3996         runparams.linelen = lyxrc.plaintext_linelen;
3997         runparams.includeall = includeall;
3998         vector<string> backs = params().backends();
3999         Converters converters = theConverters();
4000         bool need_nice_file = false;
4001         if (find(backs.begin(), backs.end(), format) == backs.end()) {
4002                 // Get shortest path to format
4003                 converters.buildGraph();
4004                 Graph::EdgePath path;
4005                 for (vector<string>::const_iterator it = backs.begin();
4006                      it != backs.end(); ++it) {
4007                         Graph::EdgePath p = converters.getPath(*it, format);
4008                         if (!p.empty() && (path.empty() || p.size() < path.size())) {
4009                                 backend_format = *it;
4010                                 path = p;
4011                         }
4012                 }
4013                 if (path.empty()) {
4014                         if (!put_in_tempdir) {
4015                                 // Only show this alert if this is an export to a non-temporary
4016                                 // file (not for previewing).
4017                                 Alert::error(_("Couldn't export file"), bformat(
4018                                         _("No information for exporting the format %1$s."),
4019                                         formats.prettyName(format)));
4020                         }
4021                         return ExportNoPathToFormat;
4022                 }
4023                 runparams.flavor = converters.getFlavor(path, this);
4024                 Graph::EdgePath::const_iterator it = path.begin();
4025                 Graph::EdgePath::const_iterator en = path.end();
4026                 for (; it != en; ++it)
4027                         if (theConverters().get(*it).nice()) {
4028                                 need_nice_file = true;
4029                                 break;
4030                         }
4031
4032         } else {
4033                 backend_format = format;
4034                 LYXERR(Debug::FILES, "backend_format=" << backend_format);
4035                 // FIXME: Don't hardcode format names here, but use a flag
4036                 if (backend_format == "pdflatex")
4037                         runparams.flavor = OutputParams::PDFLATEX;
4038                 else if (backend_format == "luatex")
4039                         runparams.flavor = OutputParams::LUATEX;
4040                 else if (backend_format == "dviluatex")
4041                         runparams.flavor = OutputParams::DVILUATEX;
4042                 else if (backend_format == "xetex")
4043                         runparams.flavor = OutputParams::XETEX;
4044         }
4045
4046         string filename = latexName(false);
4047         filename = addName(temppath(), filename);
4048         filename = changeExtension(filename,
4049                                    formats.extension(backend_format));
4050         LYXERR(Debug::FILES, "filename=" << filename);
4051
4052         // Plain text backend
4053         if (backend_format == "text") {
4054                 runparams.flavor = OutputParams::TEXT;
4055                 writePlaintextFile(*this, FileName(filename), runparams);
4056         }
4057         // HTML backend
4058         else if (backend_format == "xhtml") {
4059                 runparams.flavor = OutputParams::HTML;
4060                 setMathFlavor(runparams);
4061                 makeLyXHTMLFile(FileName(filename), runparams);
4062         } else if (backend_format == "lyx")
4063                 writeFile(FileName(filename));
4064         // Docbook backend
4065         else if (params().isDocBook()) {
4066                 runparams.nice = !put_in_tempdir;
4067                 makeDocBookFile(FileName(filename), runparams);
4068         }
4069         // LaTeX backend
4070         else if (backend_format == format || need_nice_file) {
4071                 runparams.nice = true;
4072                 bool const success = makeLaTeXFile(FileName(filename), string(), runparams);
4073                 if (d->cloned_buffer_)
4074                         d->cloned_buffer_->d->errorLists["Export"] = d->errorLists["Export"];
4075                 if (!success)
4076                         return ExportError;
4077         } else if (!lyxrc.tex_allows_spaces
4078                    && contains(filePath(), ' ')) {
4079                 Alert::error(_("File name error"),
4080                            _("The directory path to the document cannot contain spaces."));
4081                 return ExportTexPathHasSpaces;
4082         } else {
4083                 runparams.nice = false;
4084                 bool const success = makeLaTeXFile(
4085                         FileName(filename), filePath(), runparams);
4086                 if (d->cloned_buffer_)
4087                         d->cloned_buffer_->d->errorLists["Export"] = d->errorLists["Export"];
4088                 if (!success)
4089                         return ExportError;
4090         }
4091
4092         string const error_type = (format == "program")
4093                 ? "Build" : params().bufferFormat();
4094         ErrorList & error_list = d->errorLists[error_type];
4095         string const ext = formats.extension(format);
4096         FileName const tmp_result_file(changeExtension(filename, ext));
4097         bool const success = converters.convert(this, FileName(filename),
4098                 tmp_result_file, FileName(absFileName()), backend_format, format,
4099                 error_list);
4100
4101         // Emit the signal to show the error list or copy it back to the
4102         // cloned Buffer so that it can be emitted afterwards.
4103         if (format != backend_format) {
4104                 if (runparams.silent)
4105                         error_list.clear();
4106                 else if (d->cloned_buffer_)
4107                         d->cloned_buffer_->d->errorLists[error_type] =
4108                                 d->errorLists[error_type];
4109                 else
4110                         errors(error_type);
4111                 // also to the children, in case of master-buffer-view
4112                 ListOfBuffers clist = getDescendents();
4113                 ListOfBuffers::const_iterator cit = clist.begin();
4114                 ListOfBuffers::const_iterator const cen = clist.end();
4115                 for (; cit != cen; ++cit) {
4116                         if (runparams.silent)
4117                                 (*cit)->d->errorLists[error_type].clear();
4118                         else if (d->cloned_buffer_) {
4119                                 // Enable reverse search by copying back the
4120                                 // texrow object to the cloned buffer.
4121                                 // FIXME: this is not thread safe.
4122                                 (*cit)->d->cloned_buffer_->d->texrow = (*cit)->d->texrow;
4123                                 (*cit)->d->cloned_buffer_->d->errorLists[error_type] =
4124                                         (*cit)->d->errorLists[error_type];
4125                         } else
4126                                 (*cit)->errors(error_type, true);
4127                 }
4128         }
4129
4130         if (d->cloned_buffer_) {
4131                 // Enable reverse dvi or pdf to work by copying back the texrow
4132                 // object to the cloned buffer.
4133                 // FIXME: There is a possibility of concurrent access to texrow
4134                 // here from the main GUI thread that should be securized.
4135                 d->cloned_buffer_->d->texrow = d->texrow;
4136                 string const error_type = params().bufferFormat();
4137                 d->cloned_buffer_->d->errorLists[error_type] = d->errorLists[error_type];
4138         }
4139
4140
4141         if (put_in_tempdir) {
4142                 result_file = tmp_result_file.absFileName();
4143                 return success ? ExportSuccess : ExportConverterError;
4144         }
4145
4146         if (dest_filename.empty())
4147                 result_file = changeExtension(d->exportFileName().absFileName(), ext);
4148         else
4149                 result_file = dest_filename;
4150         // We need to copy referenced files (e. g. included graphics
4151         // if format == "dvi") to the result dir.
4152         vector<ExportedFile> const files =
4153                 runparams.exportdata->externalFiles(format);
4154         string const dest = runparams.export_folder.empty() ?
4155                 onlyPath(result_file) : runparams.export_folder;
4156         bool use_force = use_gui ? lyxrc.export_overwrite == ALL_FILES
4157                                  : force_overwrite == ALL_FILES;
4158         CopyStatus status = use_force ? FORCE : SUCCESS;
4159
4160         vector<ExportedFile>::const_iterator it = files.begin();
4161         vector<ExportedFile>::const_iterator const en = files.end();
4162         for (; it != en && status != CANCEL; ++it) {
4163                 string const fmt = formats.getFormatFromFile(it->sourceName);
4164                 string fixedName = it->exportName;
4165                 if (!runparams.export_folder.empty()) {
4166                         // Relative pathnames starting with ../ will be sanitized
4167                         // if exporting to a different folder
4168                         while (fixedName.substr(0, 3) == "../")
4169                                 fixedName = fixedName.substr(3, fixedName.length() - 3);
4170                 }
4171                 FileName fixedFileName = makeAbsPath(fixedName, dest);
4172                 fixedFileName.onlyPath().createPath();
4173                 status = copyFile(fmt, it->sourceName,
4174                         fixedFileName,
4175                         it->exportName, status == FORCE,
4176                         runparams.export_folder.empty());
4177         }
4178
4179         if (status == CANCEL) {
4180                 message(_("Document export cancelled."));
4181                 return ExportCancel;
4182         }
4183
4184         if (tmp_result_file.exists()) {
4185                 // Finally copy the main file
4186                 use_force = use_gui ? lyxrc.export_overwrite != NO_FILES
4187                                     : force_overwrite != NO_FILES;
4188                 if (status == SUCCESS && use_force)
4189                         status = FORCE;
4190                 status = copyFile(format, tmp_result_file,
4191                         FileName(result_file), result_file,
4192                         status == FORCE);
4193                 if (status == CANCEL) {
4194                         message(_("Document export cancelled."));
4195                         return ExportCancel;
4196                 } else {
4197                         message(bformat(_("Document exported as %1$s "
4198                                 "to file `%2$s'"),
4199                                 formats.prettyName(format),
4200                                 makeDisplayPath(result_file)));
4201                 }
4202         } else {
4203                 // This must be a dummy converter like fax (bug 1888)
4204                 message(bformat(_("Document exported as %1$s"),
4205                         formats.prettyName(format)));
4206         }
4207
4208         return success ? ExportSuccess : ExportConverterError;
4209 }
4210
4211
4212 Buffer::ExportStatus Buffer::preview(string const & format) const
4213 {
4214         bool const update_unincluded =
4215                         params().maintain_unincluded_children
4216                         && !params().getIncludedChildren().empty();
4217         return preview(format, update_unincluded);
4218 }
4219
4220
4221 Buffer::ExportStatus Buffer::preview(string const & format, bool includeall) const
4222 {
4223         MarkAsExporting exporting(this);
4224         string result_file;
4225         // (1) export with all included children (omit \includeonly)
4226         if (includeall) {
4227                 ExportStatus const status = doExport(format, true, true, result_file);
4228                 if (status != ExportSuccess)
4229                         return status;
4230         }
4231         // (2) export with included children only
4232         ExportStatus const status = doExport(format, true, false, result_file);
4233         FileName const previewFile(result_file);
4234
4235         LATTEST (isClone());
4236         d->cloned_buffer_->d->preview_file_ = previewFile;
4237         d->cloned_buffer_->d->preview_format_ = format;
4238
4239         if (status != ExportSuccess)
4240                 return status;
4241         if (previewFile.exists()) {
4242                 if (!formats.view(*this, previewFile, format))
4243                         return PreviewError;
4244                 else
4245                         return PreviewSuccess;
4246         }
4247         else {
4248                 // Successful export but no output file?
4249                 // Probably a bug in error detection.
4250                 LATTEST (status != ExportSuccess);
4251
4252                 return status;
4253         }
4254 }
4255
4256
4257 Buffer::ReadStatus Buffer::extractFromVC()
4258 {
4259         bool const found = LyXVC::file_not_found_hook(d->filename);
4260         if (!found)
4261                 return ReadFileNotFound;
4262         if (!d->filename.isReadableFile())
4263                 return ReadVCError;
4264         return ReadSuccess;
4265 }
4266
4267
4268 Buffer::ReadStatus Buffer::loadEmergency()
4269 {
4270         FileName const emergencyFile = getEmergencyFileName();
4271         if (!emergencyFile.exists()
4272                   || emergencyFile.lastModified() <= d->filename.lastModified())
4273                 return ReadFileNotFound;
4274
4275         docstring const file = makeDisplayPath(d->filename.absFileName(), 20);
4276         docstring const text = bformat(_("An emergency save of the document "
4277                 "%1$s exists.\n\nRecover emergency save?"), file);
4278
4279         int const load_emerg = Alert::prompt(_("Load emergency save?"), text,
4280                 0, 2, _("&Recover"), _("&Load Original"), _("&Cancel"));
4281
4282         switch (load_emerg)
4283         {
4284         case 0: {
4285                 docstring str;
4286                 ReadStatus const ret_llf = loadThisLyXFile(emergencyFile);
4287                 bool const success = (ret_llf == ReadSuccess);
4288                 if (success) {
4289                         if (isReadonly()) {
4290                                 Alert::warning(_("File is read-only"),
4291                                         bformat(_("An emergency file is successfully loaded, "
4292                                         "but the original file %1$s is marked read-only. "
4293                                         "Please make sure to save the document as a different "
4294                                         "file."), from_utf8(d->filename.absFileName())));
4295                         }
4296                         markDirty();
4297                         lyxvc().file_found_hook(d->filename);
4298                         str = _("Document was successfully recovered.");
4299                 } else
4300                         str = _("Document was NOT successfully recovered.");
4301                 str += "\n\n" + bformat(_("Remove emergency file now?\n(%1$s)"),
4302                         makeDisplayPath(emergencyFile.absFileName()));
4303
4304                 int const del_emerg =
4305                         Alert::prompt(_("Delete emergency file?"), str, 1, 1,
4306                                 _("&Remove"), _("&Keep"));
4307                 if (del_emerg == 0) {
4308                         emergencyFile.removeFile();
4309                         if (success)
4310                                 Alert::warning(_("Emergency file deleted"),
4311                                         _("Do not forget to save your file now!"), true);
4312                         }
4313                 return success ? ReadSuccess : ReadEmergencyFailure;
4314         }
4315         case 1: {
4316                 int const del_emerg =
4317                         Alert::prompt(_("Delete emergency file?"),
4318                                 _("Remove emergency file now?"), 1, 1,
4319                                 _("&Remove"), _("&Keep"));
4320                 if (del_emerg == 0)
4321                         emergencyFile.removeFile();
4322                 return ReadOriginal;
4323         }
4324
4325         default:
4326                 break;
4327         }
4328         return ReadCancel;
4329 }
4330
4331
4332 Buffer::ReadStatus Buffer::loadAutosave()
4333 {
4334         // Now check if autosave file is newer.
4335         FileName const autosaveFile = getAutosaveFileName();
4336         if (!autosaveFile.exists()
4337                   || autosaveFile.lastModified() <= d->filename.lastModified())
4338                 return ReadFileNotFound;
4339
4340         docstring const file = makeDisplayPath(d->filename.absFileName(), 20);
4341         docstring const text = bformat(_("The backup of the document %1$s "
4342                 "is newer.\n\nLoad the backup instead?"), file);
4343         int const ret = Alert::prompt(_("Load backup?"), text, 0, 2,
4344                 _("&Load backup"), _("Load &original"), _("&Cancel"));
4345
4346         switch (ret)
4347         {
4348         case 0: {
4349                 ReadStatus const ret_llf = loadThisLyXFile(autosaveFile);
4350                 // the file is not saved if we load the autosave file.
4351                 if (ret_llf == ReadSuccess) {
4352                         if (isReadonly()) {
4353                                 Alert::warning(_("File is read-only"),
4354                                         bformat(_("A backup file is successfully loaded, "
4355                                         "but the original file %1$s is marked read-only. "
4356                                         "Please make sure to save the document as a "
4357                                         "different file."),
4358                                         from_utf8(d->filename.absFileName())));
4359                         }
4360                         markDirty();
4361                         lyxvc().file_found_hook(d->filename);
4362                         return ReadSuccess;
4363                 }
4364                 return ReadAutosaveFailure;
4365         }
4366         case 1:
4367                 // Here we delete the autosave
4368                 autosaveFile.removeFile();
4369                 return ReadOriginal;
4370         default:
4371                 break;
4372         }
4373         return ReadCancel;
4374 }
4375
4376
4377 Buffer::ReadStatus Buffer::loadLyXFile()
4378 {
4379         if (!d->filename.isReadableFile()) {
4380                 ReadStatus const ret_rvc = extractFromVC();
4381                 if (ret_rvc != ReadSuccess)
4382                         return ret_rvc;
4383         }
4384
4385         ReadStatus const ret_re = loadEmergency();
4386         if (ret_re == ReadSuccess || ret_re == ReadCancel)
4387                 return ret_re;
4388
4389         ReadStatus const ret_ra = loadAutosave();
4390         if (ret_ra == ReadSuccess || ret_ra == ReadCancel)
4391                 return ret_ra;
4392
4393         return loadThisLyXFile(d->filename);
4394 }
4395
4396
4397 Buffer::ReadStatus Buffer::loadThisLyXFile(FileName const & fn)
4398 {
4399         return readFile(fn);
4400 }
4401
4402
4403 void Buffer::bufferErrors(TeXErrors const & terr, ErrorList & errorList) const
4404 {
4405         TeXErrors::Errors::const_iterator it = terr.begin();
4406         TeXErrors::Errors::const_iterator end = terr.end();
4407         ListOfBuffers clist = getDescendents();
4408         ListOfBuffers::const_iterator cen = clist.end();
4409
4410         for (; it != end; ++it) {
4411                 int id_start = -1;
4412                 int pos_start = -1;
4413                 int errorRow = it->error_in_line;
4414                 Buffer const * buf = 0;
4415                 Impl const * p = d;
4416                 if (it->child_name.empty())
4417                     p->texrow.getIdFromRow(errorRow, id_start, pos_start);
4418                 else {
4419                         // The error occurred in a child
4420                         ListOfBuffers::const_iterator cit = clist.begin();
4421                         for (; cit != cen; ++cit) {
4422                                 string const child_name =
4423                                         DocFileName(changeExtension(
4424                                                 (*cit)->absFileName(), "tex")).
4425                                                         mangledFileName();
4426                                 if (it->child_name != child_name)
4427                                         continue;
4428                                 (*cit)->d->texrow.getIdFromRow(errorRow,
4429                                                         id_start, pos_start);
4430                                 if (id_start != -1) {
4431                                         buf = d->cloned_buffer_
4432                                                 ? (*cit)->d->cloned_buffer_->d->owner_
4433                                                 : (*cit)->d->owner_;
4434                                         p = (*cit)->d;
4435                                         break;
4436                                 }
4437                         }
4438                 }
4439                 int id_end = -1;
4440                 int pos_end = -1;
4441                 bool found;
4442                 do {
4443                         ++errorRow;
4444                         found = p->texrow.getIdFromRow(errorRow, id_end, pos_end);
4445                 } while (found && id_start == id_end && pos_start == pos_end);
4446
4447                 if (id_start != id_end) {
4448                         // Next registered position is outside the inset where
4449                         // the error occurred, so signal end-of-paragraph
4450                         pos_end = 0;
4451                 }
4452
4453                 errorList.push_back(ErrorItem(it->error_desc,
4454                         it->error_text, id_start, pos_start, pos_end, buf));
4455         }
4456 }
4457
4458
4459 void Buffer::setBuffersForInsets() const
4460 {
4461         inset().setBuffer(const_cast<Buffer &>(*this));
4462 }
4463
4464
4465 void Buffer::updateBuffer(UpdateScope scope, UpdateType utype) const
4466 {
4467         LBUFERR(!text().paragraphs().empty());
4468
4469         // Use the master text class also for child documents
4470         Buffer const * const master = masterBuffer();
4471         DocumentClass const & textclass = master->params().documentClass();
4472
4473         // do this only if we are the top-level Buffer
4474         if (master == this) {
4475                 textclass.counters().reset(from_ascii("bibitem"));
4476                 reloadBibInfoCache();
4477         }
4478
4479         // keep the buffers to be children in this set. If the call from the
4480         // master comes back we can see which of them were actually seen (i.e.
4481         // via an InsetInclude). The remaining ones in the set need still be updated.
4482         static std::set<Buffer const *> bufToUpdate;
4483         if (scope == UpdateMaster) {
4484                 // If this is a child document start with the master
4485                 if (master != this) {
4486                         bufToUpdate.insert(this);
4487                         master->updateBuffer(UpdateMaster, utype);
4488                         // If the master buffer has no gui associated with it, then the TocModel is
4489                         // not updated during the updateBuffer call and TocModel::toc_ is invalid
4490                         // (bug 5699). The same happens if the master buffer is open in a different
4491                         // window. This test catches both possibilities.
4492                         // See: http://marc.info/?l=lyx-devel&m=138590578911716&w=2
4493                         // There remains a problem here: If there is another child open in yet a third
4494                         // window, that TOC is not updated. So some more general solution is needed at
4495                         // some point.
4496                         if (master->d->gui_ != d->gui_)
4497                                 structureChanged();
4498
4499                         // was buf referenced from the master (i.e. not in bufToUpdate anymore)?
4500                         if (bufToUpdate.find(this) == bufToUpdate.end())
4501                                 return;
4502                 }
4503
4504                 // start over the counters in the master
4505                 textclass.counters().reset();
4506         }
4507
4508         // update will be done below for this buffer
4509         bufToUpdate.erase(this);
4510
4511         // update all caches
4512         clearReferenceCache();
4513         updateMacros();
4514
4515         Buffer & cbuf = const_cast<Buffer &>(*this);
4516
4517         // do the real work
4518         ParIterator parit = cbuf.par_iterator_begin();
4519         updateBuffer(parit, utype);
4520
4521         if (master != this)
4522                 // TocBackend update will be done later.
4523                 return;
4524
4525         d->bibinfo_cache_valid_ = true;
4526         d->cite_labels_valid_ = true;
4527         cbuf.tocBackend().update(utype == OutputUpdate);
4528         if (scope == UpdateMaster)
4529                 cbuf.structureChanged();
4530 }
4531
4532
4533 static depth_type getDepth(DocIterator const & it)
4534 {
4535         depth_type depth = 0;
4536         for (size_t i = 0 ; i < it.depth() ; ++i)
4537                 if (!it[i].inset().inMathed())
4538                         depth += it[i].paragraph().getDepth() + 1;
4539         // remove 1 since the outer inset does not count
4540         return depth - 1;
4541 }
4542
4543 static depth_type getItemDepth(ParIterator const & it)
4544 {
4545         Paragraph const & par = *it;
4546         LabelType const labeltype = par.layout().labeltype;
4547
4548         if (labeltype != LABEL_ENUMERATE && labeltype != LABEL_ITEMIZE)
4549                 return 0;
4550
4551         // this will hold the lowest depth encountered up to now.
4552         depth_type min_depth = getDepth(it);
4553         ParIterator prev_it = it;
4554         while (true) {
4555                 if (prev_it.pit())
4556                         --prev_it.top().pit();
4557                 else {
4558                         // start of nested inset: go to outer par
4559                         prev_it.pop_back();
4560                         if (prev_it.empty()) {
4561                                 // start of document: nothing to do
4562                                 return 0;
4563                         }
4564                 }
4565
4566                 // We search for the first paragraph with same label
4567                 // that is not more deeply nested.
4568                 Paragraph & prev_par = *prev_it;
4569                 depth_type const prev_depth = getDepth(prev_it);
4570                 if (labeltype == prev_par.layout().labeltype) {
4571                         if (prev_depth < min_depth)
4572                                 return prev_par.itemdepth + 1;
4573                         if (prev_depth == min_depth)
4574                                 return prev_par.itemdepth;
4575                 }
4576                 min_depth = min(min_depth, prev_depth);
4577                 // small optimization: if we are at depth 0, we won't
4578                 // find anything else
4579                 if (prev_depth == 0)
4580                         return 0;
4581         }
4582 }
4583
4584
4585 static bool needEnumCounterReset(ParIterator const & it)
4586 {
4587         Paragraph const & par = *it;
4588         LASSERT(par.layout().labeltype == LABEL_ENUMERATE, return false);
4589         depth_type const cur_depth = par.getDepth();
4590         ParIterator prev_it = it;
4591         while (prev_it.pit()) {
4592                 --prev_it.top().pit();
4593                 Paragraph const & prev_par = *prev_it;
4594                 if (prev_par.getDepth() <= cur_depth)
4595                         return  prev_par.layout().labeltype != LABEL_ENUMERATE;
4596         }
4597         // start of nested inset: reset
4598         return true;
4599 }
4600
4601
4602 // set the label of a paragraph. This includes the counters.
4603 void Buffer::Impl::setLabel(ParIterator & it, UpdateType utype) const
4604 {
4605         BufferParams const & bp = owner_->masterBuffer()->params();
4606         DocumentClass const & textclass = bp.documentClass();
4607         Paragraph & par = it.paragraph();
4608         Layout const & layout = par.layout();
4609         Counters & counters = textclass.counters();
4610
4611         if (par.params().startOfAppendix()) {
4612                 // We want to reset the counter corresponding to toplevel sectioning
4613                 Layout const & lay = textclass.getTOCLayout();
4614                 docstring const cnt = lay.counter;
4615                 if (!cnt.empty())
4616                         counters.reset(cnt);
4617                 counters.appendix(true);
4618         }
4619         par.params().appendix(counters.appendix());
4620
4621         // Compute the item depth of the paragraph
4622         par.itemdepth = getItemDepth(it);
4623
4624         if (layout.margintype == MARGIN_MANUAL) {
4625                 if (par.params().labelWidthString().empty())
4626                         par.params().labelWidthString(par.expandLabel(layout, bp));
4627         } else if (layout.latextype == LATEX_BIB_ENVIRONMENT) {
4628                 // we do not need to do anything here, since the empty case is
4629                 // handled during export.
4630         } else {
4631                 par.params().labelWidthString(docstring());
4632         }
4633
4634         switch(layout.labeltype) {
4635         case LABEL_ITEMIZE: {
4636                 // At some point of time we should do something more
4637                 // clever here, like:
4638                 //   par.params().labelString(
4639                 //    bp.user_defined_bullet(par.itemdepth).getText());
4640                 // for now, use a simple hardcoded label
4641                 docstring itemlabel;
4642                 switch (par.itemdepth) {
4643                 case 0:
4644                         itemlabel = char_type(0x2022);
4645                         break;
4646                 case 1:
4647                         itemlabel = char_type(0x2013);
4648                         break;
4649                 case 2:
4650                         itemlabel = char_type(0x2217);
4651                         break;
4652                 case 3:
4653                         itemlabel = char_type(0x2219); // or 0x00b7
4654                         break;
4655                 }
4656                 par.params().labelString(itemlabel);
4657                 break;
4658         }
4659
4660         case LABEL_ENUMERATE: {
4661                 docstring enumcounter = layout.counter.empty() ? from_ascii("enum") : layout.counter;
4662
4663                 switch (par.itemdepth) {
4664                 case 2:
4665                         enumcounter += 'i';
4666                 case 1:
4667                         enumcounter += 'i';
4668                 case 0:
4669                         enumcounter += 'i';
4670                         break;
4671                 case 3:
4672                         enumcounter += "iv";
4673                         break;
4674                 default:
4675                         // not a valid enumdepth...
4676                         break;
4677                 }
4678
4679                 // Maybe we have to reset the enumeration counter.
4680                 if (needEnumCounterReset(it))
4681                         counters.reset(enumcounter);
4682                 counters.step(enumcounter, utype);
4683
4684                 string const & lang = par.getParLanguage(bp)->code();
4685                 par.params().labelString(counters.theCounter(enumcounter, lang));
4686
4687                 break;
4688         }
4689
4690         case LABEL_SENSITIVE: {
4691                 string const & type = counters.current_float();
4692                 docstring full_label;
4693                 if (type.empty())
4694                         full_label = owner_->B_("Senseless!!! ");
4695                 else {
4696                         docstring name = owner_->B_(textclass.floats().getType(type).name());
4697                         if (counters.hasCounter(from_utf8(type))) {
4698                                 string const & lang = par.getParLanguage(bp)->code();
4699                                 counters.step(from_utf8(type), utype);
4700                                 full_label = bformat(from_ascii("%1$s %2$s:"),
4701                                                      name,
4702                                                      counters.theCounter(from_utf8(type), lang));
4703                         } else
4704                                 full_label = bformat(from_ascii("%1$s #:"), name);
4705                 }
4706                 par.params().labelString(full_label);
4707                 break;
4708         }
4709
4710         case LABEL_NO_LABEL:
4711                 par.params().labelString(docstring());
4712                 break;
4713
4714         case LABEL_ABOVE:
4715         case LABEL_CENTERED:
4716         case LABEL_STATIC: {
4717                 docstring const & lcounter = layout.counter;
4718                 if (!lcounter.empty()) {
4719                         if (layout.toclevel <= bp.secnumdepth
4720                                                 && (layout.latextype != LATEX_ENVIRONMENT
4721                                         || it.text()->isFirstInSequence(it.pit()))) {
4722                                 if (counters.hasCounter(lcounter))
4723                                         counters.step(lcounter, utype);
4724                                 par.params().labelString(par.expandLabel(layout, bp));
4725                         } else
4726                                 par.params().labelString(docstring());
4727                 } else
4728                         par.params().labelString(par.expandLabel(layout, bp));
4729                 break;
4730         }
4731
4732         case LABEL_MANUAL:
4733         case LABEL_BIBLIO:
4734                 par.params().labelString(par.expandLabel(layout, bp));
4735         }
4736 }
4737
4738
4739 void Buffer::updateBuffer(ParIterator & parit, UpdateType utype) const
4740 {
4741         // LASSERT: Is it safe to continue here, or should we just return?
4742         LASSERT(parit.pit() == 0, /**/);
4743
4744         // Set the position of the text in the buffer to be able
4745         // to resolve macros in it.
4746         parit.text()->setMacrocontextPosition(parit);
4747
4748         depth_type maxdepth = 0;
4749         pit_type const lastpit = parit.lastpit();
4750         for ( ; parit.pit() <= lastpit ; ++parit.pit()) {
4751                 // reduce depth if necessary
4752                 if (parit->params().depth() > maxdepth) {
4753                         /** FIXME: this function is const, but
4754                          * nevertheless it modifies the buffer. To be
4755                          * cleaner, one should modify the buffer in
4756                          * another function, which is actually
4757                          * non-const. This would however be costly in
4758                          * terms of code duplication.
4759                          */
4760                         const_cast<Buffer *>(this)->undo().recordUndo(CursorData(parit));
4761                         parit->params().depth(maxdepth);
4762                 }
4763                 maxdepth = parit->getMaxDepthAfter();
4764
4765                 if (utype == OutputUpdate) {
4766                         // track the active counters
4767                         // we have to do this for the master buffer, since the local
4768                         // buffer isn't tracking anything.
4769                         masterBuffer()->params().documentClass().counters().
4770                                         setActiveLayout(parit->layout());
4771                 }
4772
4773                 // set the counter for this paragraph
4774                 d->setLabel(parit, utype);
4775
4776                 // now the insets
4777                 InsetList::const_iterator iit = parit->insetList().begin();
4778                 InsetList::const_iterator end = parit->insetList().end();
4779                 for (; iit != end; ++iit) {
4780                         parit.pos() = iit->pos;
4781                         iit->inset->updateBuffer(parit, utype);
4782                 }
4783         }
4784 }
4785
4786
4787 int Buffer::spellCheck(DocIterator & from, DocIterator & to,
4788         WordLangTuple & word_lang, docstring_list & suggestions) const
4789 {
4790         int progress = 0;
4791         WordLangTuple wl;
4792         suggestions.clear();
4793         word_lang = WordLangTuple();
4794         bool const to_end = to.empty();
4795         DocIterator const end = to_end ? doc_iterator_end(this) : to;
4796         // OK, we start from here.
4797         for (; from != end; from.forwardPos()) {
4798                 // We are only interested in text so remove the math CursorSlice.
4799                 while (from.inMathed()) {
4800                         from.pop_back();
4801                         from.pos()++;
4802                 }
4803                 // If from is at the end of the document (which is possible
4804                 // when leaving the mathed) LyX will crash later otherwise.
4805                 if (from.atEnd() || (!to_end && from >= end))
4806                         break;
4807                 to = from;
4808                 from.paragraph().spellCheck();
4809                 SpellChecker::Result res = from.paragraph().spellCheck(from.pos(), to.pos(), wl, suggestions);
4810                 if (SpellChecker::misspelled(res)) {
4811                         word_lang = wl;
4812                         break;
4813                 }
4814
4815                 // Do not increase progress when from == to, otherwise the word
4816                 // count will be wrong.
4817                 if (from != to) {
4818                         from = to;
4819                         ++progress;
4820                 }
4821         }
4822         return progress;
4823 }
4824
4825
4826 void Buffer::Impl::updateStatistics(DocIterator & from, DocIterator & to, bool skipNoOutput)
4827 {
4828         bool inword = false;
4829         word_count_ = 0;
4830         char_count_ = 0;
4831         blank_count_ = 0;
4832
4833         for (DocIterator dit = from ; dit != to && !dit.atEnd(); ) {
4834                 if (!dit.inTexted()) {
4835                         dit.forwardPos();
4836                         continue;
4837                 }
4838
4839                 Paragraph const & par = dit.paragraph();
4840                 pos_type const pos = dit.pos();
4841
4842                 // Copied and adapted from isWordSeparator() in Paragraph
4843                 if (pos == dit.lastpos()) {
4844                         inword = false;
4845                 } else {
4846                         Inset const * ins = par.getInset(pos);
4847                         if (ins && skipNoOutput && !ins->producesOutput()) {
4848                                 // skip this inset
4849                                 ++dit.top().pos();
4850                                 // stop if end of range was skipped
4851                                 if (!to.atEnd() && dit >= to)
4852                                         break;
4853                                 continue;
4854                         } else if (!par.isDeleted(pos)) {
4855                                 if (par.isWordSeparator(pos))
4856                                         inword = false;
4857                                 else if (!inword) {
4858                                         ++word_count_;
4859                                         inword = true;
4860                                 }
4861                                 if (ins && ins->isLetter())
4862                                         ++char_count_;
4863                                 else if (ins && ins->isSpace())
4864                                         ++blank_count_;
4865                                 else {
4866                                         char_type const c = par.getChar(pos);
4867                                         if (isPrintableNonspace(c))
4868                                                 ++char_count_;
4869                                         else if (isSpace(c))
4870                                                 ++blank_count_;
4871                                 }
4872                         }
4873                 }
4874                 dit.forwardPos();
4875         }
4876 }
4877
4878
4879 void Buffer::updateStatistics(DocIterator & from, DocIterator & to, bool skipNoOutput) const
4880 {
4881         d->updateStatistics(from, to, skipNoOutput);
4882 }
4883
4884
4885 int Buffer::wordCount() const
4886 {
4887         return d->wordCount();
4888 }
4889
4890
4891 int Buffer::charCount(bool with_blanks) const
4892 {
4893         return d->charCount(with_blanks);
4894 }
4895
4896
4897 Buffer::ReadStatus Buffer::reload()
4898 {
4899         setBusy(true);
4900         // c.f. bug http://www.lyx.org/trac/ticket/6587
4901         removeAutosaveFile();
4902         // e.g., read-only status could have changed due to version control
4903         d->filename.refresh();
4904         docstring const disp_fn = makeDisplayPath(d->filename.absFileName());
4905
4906         // clear parent. this will get reset if need be.
4907         d->setParent(0);
4908         ReadStatus const status = loadLyXFile();
4909         if (status == ReadSuccess) {
4910                 updateBuffer();
4911                 changed(true);
4912                 updateTitles();
4913                 markClean();
4914                 message(bformat(_("Document %1$s reloaded."), disp_fn));
4915                 d->undo_.clear();
4916         } else {
4917                 message(bformat(_("Could not reload document %1$s."), disp_fn));
4918         }
4919         setBusy(false);
4920         removePreviews();
4921         updatePreviews();
4922         errors("Parse");
4923         return status;
4924 }
4925
4926
4927 bool Buffer::saveAs(FileName const & fn)
4928 {
4929         FileName const old_name = fileName();
4930         FileName const old_auto = getAutosaveFileName();
4931         bool const old_unnamed = isUnnamed();
4932         bool success = true;
4933         d->old_position = filePath();
4934
4935         setFileName(fn);
4936         markDirty();
4937         setUnnamed(false);
4938
4939         if (save()) {
4940                 // bring the autosave file with us, just in case.
4941                 moveAutosaveFile(old_auto);
4942                 // validate version control data and
4943                 // correct buffer title
4944                 lyxvc().file_found_hook(fileName());
4945                 updateTitles();
4946                 // the file has now been saved to the new location.
4947                 // we need to check that the locations of child buffers
4948                 // are still valid.
4949                 checkChildBuffers();
4950                 checkMasterBuffer();
4951         } else {
4952                 // save failed
4953                 // reset the old filename and unnamed state
4954                 setFileName(old_name);
4955                 setUnnamed(old_unnamed);
4956                 success = false;
4957         }
4958
4959         d->old_position.clear();
4960         return success;
4961 }
4962
4963
4964 void Buffer::checkChildBuffers()
4965 {
4966         Impl::BufferPositionMap::iterator it = d->children_positions.begin();
4967         Impl::BufferPositionMap::iterator const en = d->children_positions.end();
4968         for (; it != en; ++it) {
4969                 DocIterator dit = it->second;
4970                 Buffer * cbuf = const_cast<Buffer *>(it->first);
4971                 if (!cbuf || !theBufferList().isLoaded(cbuf))
4972                         continue;
4973                 Inset * inset = dit.nextInset();
4974                 LASSERT(inset && inset->lyxCode() == INCLUDE_CODE, continue);
4975                 InsetInclude * inset_inc = static_cast<InsetInclude *>(inset);
4976                 docstring const & incfile = inset_inc->getParam("filename");
4977                 string oldloc = cbuf->absFileName();
4978                 string newloc = makeAbsPath(to_utf8(incfile),
4979                                 onlyPath(absFileName())).absFileName();
4980                 if (oldloc == newloc)
4981                         continue;
4982                 // the location of the child file is incorrect.
4983                 cbuf->setParent(0);
4984                 inset_inc->setChildBuffer(0);
4985         }
4986         // invalidate cache of children
4987         d->children_positions.clear();
4988         d->position_to_children.clear();
4989 }
4990
4991
4992 // If a child has been saved under a different name/path, it might have been
4993 // orphaned. Therefore the master needs to be reset (bug 8161).
4994 void Buffer::checkMasterBuffer()
4995 {
4996         Buffer const * const master = masterBuffer();
4997         if (master == this)
4998                 return;
4999
5000         // necessary to re-register the child (bug 5873)
5001         // FIXME: clean up updateMacros (here, only
5002         // child registering is needed).
5003         master->updateMacros();
5004         // (re)set master as master buffer, but only
5005         // if we are a real child
5006         if (master->isChild(this))
5007                 setParent(master);
5008         else
5009                 setParent(0);
5010 }
5011
5012
5013 string Buffer::includedFilePath(string const & name) const
5014 {
5015         if (d->old_position.empty() || d->old_position == filePath())
5016                 return name;
5017
5018         if (FileName::isAbsolute(name))
5019                 return to_utf8(makeRelPath(from_utf8(name), from_utf8(filePath())));
5020
5021         // old_position already contains a trailing path separator
5022         string const cleanpath = FileName(d->old_position + name).realPath();
5023         return to_utf8(makeRelPath(from_utf8(cleanpath), from_utf8(filePath())));
5024 }
5025
5026 } // namespace lyx