]> git.lyx.org Git - lyx.git/blob - src/Buffer.cpp
chkconfig.ltx: sort fontnames
[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  *
8  * Full author contact details are available in file CREDITS.
9  */
10
11 #include <config.h>
12
13 #include "Buffer.h"
14
15 #include "Author.h"
16 #include "BranchList.h"
17 #include "buffer_funcs.h"
18 #include "BufferList.h"
19 #include "BufferParams.h"
20 #include "Counters.h"
21 #include "Bullet.h"
22 #include "Chktex.h"
23 #include "debug.h"
24 #include "Encoding.h"
25 #include "ErrorList.h"
26 #include "Exporter.h"
27 #include "Format.h"
28 #include "FuncRequest.h"
29 #include "gettext.h"
30 #include "InsetIterator.h"
31 #include "Language.h"
32 #include "LaTeX.h"
33 #include "LaTeXFeatures.h"
34 #include "LyXAction.h"
35 #include "Lexer.h"
36 #include "Text.h"
37 #include "LyXRC.h"
38 #include "LyXVC.h"
39 #include "Messages.h"
40 #include "output.h"
41 #include "output_docbook.h"
42 #include "output_latex.h"
43 #include "Paragraph.h"
44 #include "paragraph_funcs.h"
45 #include "ParagraphParameters.h"
46 #include "ParIterator.h"
47 #include "sgml.h"
48 #include "TexRow.h"
49 #include "TocBackend.h"
50 #include "Undo.h"
51 #include "version.h"
52
53 #include "insets/InsetBibitem.h"
54 #include "insets/InsetBibtex.h"
55 #include "insets/InsetInclude.h"
56 #include "insets/InsetText.h"
57
58 #include "mathed/MathMacroTemplate.h"
59 #include "mathed/MacroTable.h"
60 #include "mathed/MathSupport.h"
61
62 #include "frontends/alert.h"
63
64 #include "graphics/Previews.h"
65
66 #include "support/types.h"
67 #include "support/lyxalgo.h"
68 #include "support/filetools.h"
69 #include "support/fs_extras.h"
70 #include "support/lyxlib.h"
71 #include "support/os.h"
72 #include "support/Path.h"
73 #include "support/textutils.h"
74 #include "support/convert.h"
75
76 #include <boost/iostreams/filtering_stream.hpp>
77 #include <boost/iostreams/filter/gzip.hpp>
78 #include <boost/iostreams/device/file.hpp>
79 #include <boost/bind.hpp>
80 #include <boost/filesystem/exception.hpp>
81 #include <boost/filesystem/operations.hpp>
82
83 #if defined (HAVE_UTIME_H)
84 #include <utime.h>
85 #elif defined (HAVE_SYS_UTIME_H)
86 #include <sys/utime.h>
87 #endif
88
89 #include <iomanip>
90 #include <stack>
91 #include <sstream>
92 #include <fstream>
93
94
95 namespace lyx {
96
97 using support::addName;
98 using support::bformat;
99 using support::changeExtension;
100 using support::cmd_ret;
101 using support::createBufferTmpDir;
102 using support::destroyDir;
103 using support::FileName;
104 using support::getFormatFromContents;
105 using support::libFileSearch;
106 using support::latex_path;
107 using support::ltrim;
108 using support::makeAbsPath;
109 using support::makeDisplayPath;
110 using support::makeLatexName;
111 using support::onlyFilename;
112 using support::onlyPath;
113 using support::quoteName;
114 using support::removeAutosaveFile;
115 using support::rename;
116 using support::runCommand;
117 using support::split;
118 using support::subst;
119 using support::tempName;
120 using support::trim;
121
122 namespace Alert = frontend::Alert;
123 namespace os = support::os;
124 namespace fs = boost::filesystem;
125 namespace io = boost::iostreams;
126
127 using std::endl;
128 using std::for_each;
129 using std::make_pair;
130
131 using std::ios;
132 using std::map;
133 using std::ostream;
134 using std::ostringstream;
135 using std::ofstream;
136 using std::pair;
137 using std::stack;
138 using std::vector;
139 using std::string;
140
141
142 namespace {
143
144 int const LYX_FORMAT = 271;
145
146 } // namespace anon
147
148
149 typedef std::map<string, bool> DepClean;
150
151 class Buffer::Impl
152 {
153 public:
154         Impl(Buffer & parent, FileName const & file, bool readonly);
155
156         limited_stack<Undo> undostack;
157         limited_stack<Undo> redostack;
158         BufferParams params;
159         LyXVC lyxvc;
160         string temppath;
161         TexRow texrow;
162
163         /// need to regenerate .tex?
164         DepClean dep_clean;
165
166         /// is save needed?
167         mutable bool lyx_clean;
168
169         /// is autosave needed?
170         mutable bool bak_clean;
171
172         /// is this a unnamed file (New...)?
173         bool unnamed;
174
175         /// buffer is r/o
176         bool read_only;
177
178         /// name of the file the buffer is associated with.
179         FileName filename;
180
181         /** Set to true only when the file is fully loaded.
182          *  Used to prevent the premature generation of previews
183          *  and by the citation inset.
184          */
185         bool file_fully_loaded;
186
187         /// our Text that should be wrapped in an InsetText
188         InsetText inset;
189
190         ///
191         MacroTable macros;
192
193         ///
194         TocBackend toc_backend;
195 };
196
197
198 Buffer::Impl::Impl(Buffer & parent, FileName const & file, bool readonly_)
199         : lyx_clean(true), bak_clean(true), unnamed(false), read_only(readonly_),
200           filename(file), file_fully_loaded(false), inset(params),
201           toc_backend(&parent)
202 {
203         inset.setAutoBreakRows(true);
204         lyxvc.buffer(&parent);
205         temppath = createBufferTmpDir();
206         params.filepath = onlyPath(file.absFilename());
207         // FIXME: And now do something if temppath == string(), because we
208         // assume from now on that temppath points to a valid temp dir.
209         // See http://www.mail-archive.com/lyx-devel@lists.lyx.org/msg67406.html
210 }
211
212
213 Buffer::Buffer(string const & file, bool readonly)
214         : pimpl_(new Impl(*this, FileName(file), readonly))
215 {
216         LYXERR(Debug::INFO) << "Buffer::Buffer()" << endl;
217 }
218
219
220 Buffer::~Buffer()
221 {
222         LYXERR(Debug::INFO) << "Buffer::~Buffer()" << endl;
223         // here the buffer should take care that it is
224         // saved properly, before it goes into the void.
225
226         closing();
227
228         if (!temppath().empty() && !destroyDir(FileName(temppath()))) {
229                 Alert::warning(_("Could not remove temporary directory"),
230                         bformat(_("Could not remove the temporary directory %1$s"),
231                         from_utf8(temppath())));
232         }
233
234         // Remove any previewed LaTeX snippets associated with this buffer.
235         graphics::Previews::get().removeLoader(*this);
236 }
237
238
239 Text & Buffer::text() const
240 {
241         return const_cast<Text &>(pimpl_->inset.text_);
242 }
243
244
245 Inset & Buffer::inset() const
246 {
247         return const_cast<InsetText &>(pimpl_->inset);
248 }
249
250
251 limited_stack<Undo> & Buffer::undostack()
252 {
253         return pimpl_->undostack;
254 }
255
256
257 limited_stack<Undo> const & Buffer::undostack() const
258 {
259         return pimpl_->undostack;
260 }
261
262
263 limited_stack<Undo> & Buffer::redostack()
264 {
265         return pimpl_->redostack;
266 }
267
268
269 limited_stack<Undo> const & Buffer::redostack() const
270 {
271         return pimpl_->redostack;
272 }
273
274
275 BufferParams & Buffer::params()
276 {
277         return pimpl_->params;
278 }
279
280
281 BufferParams const & Buffer::params() const
282 {
283         return pimpl_->params;
284 }
285
286
287 ParagraphList & Buffer::paragraphs()
288 {
289         return text().paragraphs();
290 }
291
292
293 ParagraphList const & Buffer::paragraphs() const
294 {
295         return text().paragraphs();
296 }
297
298
299 LyXVC & Buffer::lyxvc()
300 {
301         return pimpl_->lyxvc;
302 }
303
304
305 LyXVC const & Buffer::lyxvc() const
306 {
307         return pimpl_->lyxvc;
308 }
309
310
311 string const & Buffer::temppath() const
312 {
313         return pimpl_->temppath;
314 }
315
316
317 TexRow & Buffer::texrow()
318 {
319         return pimpl_->texrow;
320 }
321
322
323 TexRow const & Buffer::texrow() const
324 {
325         return pimpl_->texrow;
326 }
327
328
329 TocBackend & Buffer::tocBackend()
330 {
331         return pimpl_->toc_backend;
332 }
333
334
335 TocBackend const & Buffer::tocBackend() const
336 {
337         return pimpl_->toc_backend;
338 }
339
340
341 string const Buffer::getLatexName(bool const no_path) const
342 {
343         string const name = changeExtension(makeLatexName(fileName()), ".tex");
344         return no_path ? onlyFilename(name) : name;
345 }
346
347
348 pair<Buffer::LogType, string> const Buffer::getLogName() const
349 {
350         string const filename = getLatexName(false);
351
352         if (filename.empty())
353                 return make_pair(Buffer::latexlog, string());
354
355         string const path = temppath();
356
357         FileName const fname(addName(temppath(),
358                                      onlyFilename(changeExtension(filename,
359                                                                   ".log"))));
360         FileName const bname(
361                 addName(path, onlyFilename(
362                         changeExtension(filename,
363                                         formats.extension("literate") + ".out"))));
364
365         // If no Latex log or Build log is newer, show Build log
366
367         if (fs::exists(bname.toFilesystemEncoding()) &&
368             (!fs::exists(fname.toFilesystemEncoding()) ||
369              fs::last_write_time(fname.toFilesystemEncoding()) < fs::last_write_time(bname.toFilesystemEncoding()))) {
370                 LYXERR(Debug::FILES) << "Log name calculated as: " << bname << endl;
371                 return make_pair(Buffer::buildlog, bname.absFilename());
372         }
373         LYXERR(Debug::FILES) << "Log name calculated as: " << fname << endl;
374         return make_pair(Buffer::latexlog, fname.absFilename());
375 }
376
377
378 void Buffer::setReadonly(bool const flag)
379 {
380         if (pimpl_->read_only != flag) {
381                 pimpl_->read_only = flag;
382                 readonly(flag);
383         }
384 }
385
386
387 void Buffer::setFileName(string const & newfile)
388 {
389         pimpl_->filename = makeAbsPath(newfile);
390         params().filepath = onlyPath(pimpl_->filename.absFilename());
391         setReadonly(fs::is_readonly(pimpl_->filename.toFilesystemEncoding()));
392         updateTitles();
393 }
394
395
396 // We'll remove this later. (Lgb)
397 namespace {
398
399 void unknownClass(string const & unknown)
400 {
401         Alert::warning(_("Unknown document class"),
402                        bformat(_("Using the default document class, because the "
403                                               "class %1$s is unknown."), from_utf8(unknown)));
404 }
405
406 } // anon
407
408
409 int Buffer::readHeader(Lexer & lex)
410 {
411         int unknown_tokens = 0;
412         int line = -1;
413         int begin_header_line = -1;
414
415         // Initialize parameters that may be/go lacking in header:
416         params().branchlist().clear();
417         params().preamble.erase();
418         params().options.erase();
419         params().float_placement.erase();
420         params().paperwidth.erase();
421         params().paperheight.erase();
422         params().leftmargin.erase();
423         params().rightmargin.erase();
424         params().topmargin.erase();
425         params().bottommargin.erase();
426         params().headheight.erase();
427         params().headsep.erase();
428         params().footskip.erase();
429         for (int i = 0; i < 4; ++i) {
430                 params().user_defined_bullet(i) = ITEMIZE_DEFAULTS[i];
431                 params().temp_bullet(i) = ITEMIZE_DEFAULTS[i];
432         }
433
434         ErrorList & errorList = errorLists_["Parse"];
435
436         while (lex.isOK()) {
437                 lex.next();
438                 string const token = lex.getString();
439
440                 if (token.empty())
441                         continue;
442
443                 if (token == "\\end_header")
444                         break;
445
446                 ++line;
447                 if (token == "\\begin_header") {
448                         begin_header_line = line;
449                         continue;
450                 }
451
452                 LYXERR(Debug::PARSER) << "Handling document header token: `"
453                                       << token << '\'' << endl;
454
455                 string unknown = params().readToken(lex, token);
456                 if (!unknown.empty()) {
457                         if (unknown[0] != '\\' && token == "\\textclass") {
458                                 unknownClass(unknown);
459                         } else {
460                                 ++unknown_tokens;
461                                 docstring const s = bformat(_("Unknown token: "
462                                                                         "%1$s %2$s\n"),
463                                                          from_utf8(token),
464                                                          lex.getDocString());
465                                 errorList.push_back(ErrorItem(_("Document header error"),
466                                         s, -1, 0, 0));
467                         }
468                 }
469         }
470         if (begin_header_line) {
471                 docstring const s = _("\\begin_header is missing");
472                 errorList.push_back(ErrorItem(_("Document header error"),
473                         s, -1, 0, 0));
474         }
475
476         return unknown_tokens;
477 }
478
479
480 // Uwe C. Schroeder
481 // changed to be public and have one parameter
482 // Returns false if "\end_document" is not read (Asger)
483 bool Buffer::readDocument(Lexer & lex)
484 {
485         ErrorList & errorList = errorLists_["Parse"];
486         errorList.clear();
487
488         lex.next();
489         string const token = lex.getString();
490         if (token != "\\begin_document") {
491                 docstring const s = _("\\begin_document is missing");
492                 errorList.push_back(ErrorItem(_("Document header error"),
493                         s, -1, 0, 0));
494         }
495
496         // we are reading in a brand new document
497         BOOST_ASSERT(paragraphs().empty());
498
499         readHeader(lex);
500         if (!params().getTextClass().load(filePath())) {
501                 string theclass = params().getTextClass().name();
502                 Alert::error(_("Can't load document class"), bformat(
503                         _("Using the default document class, because the "
504                                      "class %1$s could not be loaded."), from_utf8(theclass)));
505                 params().textclass = 0;
506         }
507
508         if (params().outputChanges) {
509                 bool dvipost    = LaTeXFeatures::isAvailable("dvipost");
510                 bool xcolorsoul = LaTeXFeatures::isAvailable("soul") &&
511                                   LaTeXFeatures::isAvailable("xcolor");
512                 
513                 if (!dvipost && !xcolorsoul) {
514                         Alert::warning(_("Changes not shown in LaTeX output"),
515                                        _("Changes will not be highlighted in LaTeX output, "
516                                          "because neither dvipost nor xcolor/soul are installed.\n"
517                                          "Please install these packages or redefine "
518                                          "\\lyxadded and \\lyxdeleted in the LaTeX preamble."));
519                 } else if (!xcolorsoul) {
520                         Alert::warning(_("Changes not shown in LaTeX output"),
521                                        _("Changes will not be highlighted in LaTeX output "
522                                          "when using pdflatex, because xcolor and soul are not installed.\n"
523                                          "Please install both packages or redefine "
524                                          "\\lyxadded and \\lyxdeleted in the LaTeX preamble."));
525                 }
526         }
527
528         bool const res = text().read(*this, lex, errorList);
529         for_each(text().paragraphs().begin(),
530                  text().paragraphs().end(),
531                  bind(&Paragraph::setInsetOwner, _1, &inset()));
532
533         return res;
534 }
535
536
537 // needed to insert the selection
538 void Buffer::insertStringAsLines(ParagraphList & pars,
539         pit_type & pit, pos_type & pos,
540         Font const & fn, docstring const & str, bool autobreakrows)
541 {
542         Font font = fn;
543
544         // insert the string, don't insert doublespace
545         bool space_inserted = true;
546         for (docstring::const_iterator cit = str.begin();
547             cit != str.end(); ++cit) {
548                 Paragraph & par = pars[pit];
549                 if (*cit == '\n') {
550                         if (autobreakrows && (!par.empty() || par.allowEmpty())) {
551                                 breakParagraph(params(), pars, pit, pos,
552                                                par.layout()->isEnvironment());
553                                 ++pit;
554                                 pos = 0;
555                                 space_inserted = true;
556                         } else {
557                                 continue;
558                         }
559                         // do not insert consecutive spaces if !free_spacing
560                 } else if ((*cit == ' ' || *cit == '\t') &&
561                            space_inserted && !par.isFreeSpacing()) {
562                         continue;
563                 } else if (*cit == '\t') {
564                         if (!par.isFreeSpacing()) {
565                                 // tabs are like spaces here
566                                 par.insertChar(pos, ' ', font, params().trackChanges);
567                                 ++pos;
568                                 space_inserted = true;
569                         } else {
570                                 const pos_type n = 8 - pos % 8;
571                                 for (pos_type i = 0; i < n; ++i) {
572                                         par.insertChar(pos, ' ', font, params().trackChanges);
573                                         ++pos;
574                                 }
575                                 space_inserted = true;
576                         }
577                 } else if (!isPrintable(*cit)) {
578                         // Ignore unprintables
579                         continue;
580                 } else {
581                         // just insert the character
582                         par.insertChar(pos, *cit, font, params().trackChanges);
583                         ++pos;
584                         space_inserted = (*cit == ' ');
585                 }
586
587         }
588 }
589
590
591 bool Buffer::readString(std::string const & s)
592 {
593         params().compressed = false;
594
595         // remove dummy empty par
596         paragraphs().clear();
597         Lexer lex(0, 0);
598         std::istringstream is(s);
599         lex.setStream(is);
600         FileName const name(tempName());
601         switch (readFile(lex, name, true)) {
602         case failure:
603                 return false;
604         case wrongversion: {
605                 // We need to call lyx2lyx, so write the input to a file
606                 std::ofstream os(name.toFilesystemEncoding().c_str());
607                 os << s;
608                 os.close();
609                 return readFile(name);
610         }
611         case success:
612                 break;
613         }
614
615         return true;
616 }
617
618
619 bool Buffer::readFile(FileName const & filename)
620 {
621         // Check if the file is compressed.
622         string const format = getFormatFromContents(filename);
623         if (format == "gzip" || format == "zip" || format == "compress") {
624                 params().compressed = true;
625         }
626
627         // remove dummy empty par
628         paragraphs().clear();
629         Lexer lex(0, 0);
630         lex.setFile(filename);
631         if (readFile(lex, filename) != success)
632                 return false;
633
634         return true;
635 }
636
637
638 bool Buffer::fully_loaded() const
639 {
640         return pimpl_->file_fully_loaded;
641 }
642
643
644 void Buffer::fully_loaded(bool const value)
645 {
646         pimpl_->file_fully_loaded = value;
647 }
648
649
650 Buffer::ReadStatus Buffer::readFile(Lexer & lex, FileName const & filename,
651                 bool fromstring)
652 {
653         BOOST_ASSERT(!filename.empty());
654
655         if (!lex.isOK()) {
656                 Alert::error(_("Document could not be read"),
657                              bformat(_("%1$s could not be read."), from_utf8(filename.absFilename())));
658                 return failure;
659         }
660
661         lex.next();
662         string const token(lex.getString());
663
664         if (!lex) {
665                 Alert::error(_("Document could not be read"),
666                              bformat(_("%1$s could not be read."), from_utf8(filename.absFilename())));
667                 return failure;
668         }
669
670         // the first token _must_ be...
671         if (token != "\\lyxformat") {
672                 lyxerr << "Token: " << token << endl;
673
674                 Alert::error(_("Document format failure"),
675                              bformat(_("%1$s is not a LyX document."),
676                                        from_utf8(filename.absFilename())));
677                 return failure;
678         }
679
680         lex.next();
681         string tmp_format = lex.getString();
682         //lyxerr << "LyX Format: `" << tmp_format << '\'' << endl;
683         // if present remove ".," from string.
684         string::size_type dot = tmp_format.find_first_of(".,");
685         //lyxerr << "           dot found at " << dot << endl;
686         if (dot != string::npos)
687                         tmp_format.erase(dot, 1);
688         int const file_format = convert<int>(tmp_format);
689         //lyxerr << "format: " << file_format << endl;
690
691         if (file_format != LYX_FORMAT) {
692
693                 if (fromstring)
694                         // lyx2lyx would fail
695                         return wrongversion;
696
697                 FileName const tmpfile(tempName());
698                 if (tmpfile.empty()) {
699                         Alert::error(_("Conversion failed"),
700                                      bformat(_("%1$s is from a different"
701                                               " version of LyX, but a temporary"
702                                               " file for converting it could"
703                                                             " not be created."),
704                                               from_utf8(filename.absFilename())));
705                         return failure;
706                 }
707                 FileName const lyx2lyx = libFileSearch("lyx2lyx", "lyx2lyx");
708                 if (lyx2lyx.empty()) {
709                         Alert::error(_("Conversion script not found"),
710                                      bformat(_("%1$s is from a different"
711                                                " version of LyX, but the"
712                                                " conversion script lyx2lyx"
713                                                             " could not be found."),
714                                                from_utf8(filename.absFilename())));
715                         return failure;
716                 }
717                 ostringstream command;
718                 command << os::python()
719                         << ' ' << quoteName(lyx2lyx.toFilesystemEncoding())
720                         << " -t " << convert<string>(LYX_FORMAT)
721                         << " -o " << quoteName(tmpfile.toFilesystemEncoding())
722                         << ' ' << quoteName(filename.toFilesystemEncoding());
723                 string const command_str = command.str();
724
725                 LYXERR(Debug::INFO) << "Running '"
726                                     << command_str << '\''
727                                     << endl;
728
729                 cmd_ret const ret = runCommand(command_str);
730                 if (ret.first != 0) {
731                         Alert::error(_("Conversion script failed"),
732                                      bformat(_("%1$s is from a different version"
733                                               " of LyX, but the lyx2lyx script"
734                                                             " failed to convert it."),
735                                               from_utf8(filename.absFilename())));
736                         return failure;
737                 } else {
738                         bool const ret = readFile(tmpfile);
739                         // Do stuff with tmpfile name and buffer name here.
740                         return ret ? success : failure;
741                 }
742
743         }
744
745         if (readDocument(lex)) {
746                 Alert::error(_("Document format failure"),
747                              bformat(_("%1$s ended unexpectedly, which means"
748                                                     " that it is probably corrupted."),
749                                        from_utf8(filename.absFilename())));
750         }
751
752         //lyxerr << "removing " << MacroTable::localMacros().size()
753         //      << " temporary macro entries" << endl;
754         //MacroTable::localMacros().clear();
755
756         pimpl_->file_fully_loaded = true;
757         return success;
758 }
759
760
761 // Should probably be moved to somewhere else: BufferView? LyXView?
762 bool Buffer::save() const
763 {
764         // We don't need autosaves in the immediate future. (Asger)
765         resetAutosaveTimers();
766
767         string const encodedFilename = pimpl_->filename.toFilesystemEncoding();
768
769         FileName backupName;
770         bool madeBackup = false;
771
772         // make a backup if the file already exists
773         if (lyxrc.make_backup && fs::exists(encodedFilename)) {
774                 backupName = FileName(fileName() + '~');
775                 if (!lyxrc.backupdir_path.empty())
776                         backupName = FileName(addName(lyxrc.backupdir_path,
777                                               subst(os::internal_path(backupName.absFilename()), '/', '!')));
778
779                 try {
780                         fs::copy_file(encodedFilename, backupName.toFilesystemEncoding(), false);
781                         madeBackup = true;
782                 } catch (fs::filesystem_error const & fe) {
783                         Alert::error(_("Backup failure"),
784                                      bformat(_("Cannot create backup file %1$s.\n"
785                                                "Please check whether the directory exists and is writeable."),
786                                              from_utf8(backupName.absFilename())));
787                         LYXERR(Debug::DEBUG) << "Fs error: " << fe.what() << endl;
788                 }
789         }
790
791         if (writeFile(pimpl_->filename)) {
792                 markClean();
793                 removeAutosaveFile(fileName());
794                 return true;
795         } else {
796                 // Saving failed, so backup is not backup
797                 if (madeBackup)
798                         rename(backupName, pimpl_->filename);
799                 return false;
800         }
801 }
802
803
804 bool Buffer::writeFile(FileName const & fname) const
805 {
806         if (pimpl_->read_only && fname == pimpl_->filename)
807                 return false;
808
809         bool retval = false;
810
811         if (params().compressed) {
812                 io::filtering_ostream ofs(io::gzip_compressor() | io::file_sink(fname.toFilesystemEncoding()));
813                 if (!ofs)
814                         return false;
815
816                 retval = write(ofs);
817         } else {
818                 ofstream ofs(fname.toFilesystemEncoding().c_str(), ios::out|ios::trunc);
819                 if (!ofs)
820                         return false;
821
822                 retval = write(ofs);
823         }
824
825         return retval;
826 }
827
828
829 bool Buffer::write(ostream & ofs) const
830 {
831 #ifdef HAVE_LOCALE
832         // Use the standard "C" locale for file output.
833         ofs.imbue(std::locale::classic());
834 #endif
835
836         // The top of the file should not be written by params().
837
838         // write out a comment in the top of the file
839         ofs << "#LyX " << lyx_version
840             << " created this file. For more info see http://www.lyx.org/\n"
841             << "\\lyxformat " << LYX_FORMAT << "\n"
842             << "\\begin_document\n";
843
844         // now write out the buffer parameters.
845         ofs << "\\begin_header\n";
846         params().writeFile(ofs);
847         ofs << "\\end_header\n";
848
849         // write the text
850         ofs << "\n\\begin_body\n";
851         text().write(*this, ofs);
852         ofs << "\n\\end_body\n";
853
854         // Write marker that shows file is complete
855         ofs << "\\end_document" << endl;
856
857         // Shouldn't really be needed....
858         //ofs.close();
859
860         // how to check if close went ok?
861         // Following is an attempt... (BE 20001011)
862
863         // good() returns false if any error occured, including some
864         //        formatting error.
865         // bad()  returns true if something bad happened in the buffer,
866         //        which should include file system full errors.
867
868         bool status = true;
869         if (!ofs) {
870                 status = false;
871                 lyxerr << "File was not closed properly." << endl;
872         }
873
874         return status;
875 }
876
877
878 bool Buffer::makeLaTeXFile(FileName const & fname,
879                            string const & original_path,
880                            OutputParams const & runparams,
881                            bool output_preamble, bool output_body)
882 {
883         string const encoding = runparams.encoding->iconvName();
884         LYXERR(Debug::LATEX) << "makeLaTeXFile encoding: "
885                 << encoding << "..." << endl;
886
887         odocfstream ofs(encoding);
888         if (!openFileWrite(ofs, fname))
889                 return false;
890
891         try {
892                 writeLaTeXSource(ofs, original_path,
893                       runparams, output_preamble, output_body);
894         }
895         catch (iconv_codecvt_facet_exception &) {
896                 Alert::error(_("Encoding error"),
897                         _("Some characters of your document are not "
898                           "representable in the chosen encoding.\n"
899                           "Changing the document encoding to utf8 could help."));
900                 return false;
901         }
902
903         ofs.close();
904         if (ofs.fail()) {
905                 lyxerr << "File '" << fname << "' was not closed properly." << endl;
906                 Alert::error(_("Error closing file"),
907                         _("The output file could not be closed properly.\n"
908                           " Probably some characters of your document are not "
909                           "representable in the chosen encoding.\n"
910                           "Changing the document encoding to utf8 could help."));
911                 return false;
912         }
913         return true;
914 }
915
916
917 void Buffer::writeLaTeXSource(odocstream & os,
918                            string const & original_path,
919                            OutputParams const & runparams_in,
920                            bool const output_preamble, bool const output_body)
921 {
922         OutputParams runparams = runparams_in;
923
924         // validate the buffer.
925         LYXERR(Debug::LATEX) << "  Validating buffer..." << endl;
926         LaTeXFeatures features(*this, params(), runparams);
927         validate(features);
928         LYXERR(Debug::LATEX) << "  Buffer validation done." << endl;
929
930         texrow().reset();
931
932         // The starting paragraph of the coming rows is the
933         // first paragraph of the document. (Asger)
934         texrow().start(paragraphs().begin()->id(), 0);
935
936         if (output_preamble && runparams.nice) {
937                 os << "%% LyX " << lyx_version << " created this file.  "
938                         "For more info, see http://www.lyx.org/.\n"
939                         "%% Do not edit unless you really know what "
940                         "you are doing.\n";
941                 texrow().newline();
942                 texrow().newline();
943         }
944         LYXERR(Debug::INFO) << "lyx document header finished" << endl;
945         // There are a few differences between nice LaTeX and usual files:
946         // usual is \batchmode and has a
947         // special input@path to allow the including of figures
948         // with either \input or \includegraphics (what figinsets do).
949         // input@path is set when the actual parameter
950         // original_path is set. This is done for usual tex-file, but not
951         // for nice-latex-file. (Matthias 250696)
952         // Note that input@path is only needed for something the user does
953         // in the preamble, included .tex files or ERT, files included by
954         // LyX work without it.
955         if (output_preamble) {
956                 if (!runparams.nice) {
957                         // code for usual, NOT nice-latex-file
958                         os << "\\batchmode\n"; // changed
959                         // from \nonstopmode
960                         texrow().newline();
961                 }
962                 if (!original_path.empty()) {
963                         // FIXME UNICODE
964                         // We don't know the encoding of inputpath
965                         docstring const inputpath = from_utf8(latex_path(original_path));
966                         os << "\\makeatletter\n"
967                            << "\\def\\input@path{{"
968                            << inputpath << "/}}\n"
969                            << "\\makeatother\n";
970                         texrow().newline();
971                         texrow().newline();
972                         texrow().newline();
973                 }
974
975                 // Write the preamble
976                 runparams.use_babel = params().writeLaTeX(os, features, texrow());
977
978                 if (!output_body)
979                         return;
980
981                 // make the body.
982                 os << "\\begin{document}\n";
983                 texrow().newline();
984         } // output_preamble
985         LYXERR(Debug::INFO) << "preamble finished, now the body." << endl;
986
987         if (!lyxrc.language_auto_begin &&
988             !params().language->babel().empty()) {
989                 // FIXME UNICODE
990                 os << from_utf8(subst(lyxrc.language_command_begin,
991                                            "$$lang",
992                                            params().language->babel()))
993                    << '\n';
994                 texrow().newline();
995         }
996
997         Encoding const & encoding = params().encoding();
998         if (encoding.package() == Encoding::CJK) {
999                 // Open a CJK environment, since in contrast to the encodings
1000                 // handled by inputenc the document encoding is not set in
1001                 // the preamble if it is handled by CJK.sty.
1002                 os << "\\begin{CJK}{" << from_ascii(encoding.latexName())
1003                    << "}{}\n";
1004                 texrow().newline();
1005         }
1006
1007         // if we are doing a real file with body, even if this is the
1008         // child of some other buffer, let's cut the link here.
1009         // This happens for example if only a child document is printed.
1010         string save_parentname;
1011         if (output_preamble) {
1012                 save_parentname = params().parentname;
1013                 params().parentname.erase();
1014         }
1015
1016         // the real stuff
1017         latexParagraphs(*this, paragraphs(), os, texrow(), runparams);
1018
1019         // Restore the parenthood if needed
1020         if (output_preamble)
1021                 params().parentname = save_parentname;
1022
1023         // add this just in case after all the paragraphs
1024         os << endl;
1025         texrow().newline();
1026
1027         if (encoding.package() == Encoding::CJK) {
1028                 // Close the open CJK environment.
1029                 // latexParagraphs will have opened one even if the last text
1030                 // was not CJK.
1031                 os << "\\end{CJK}\n";
1032                 texrow().newline();
1033         }
1034
1035         if (!lyxrc.language_auto_end &&
1036             !params().language->babel().empty()) {
1037                 os << from_utf8(subst(lyxrc.language_command_end,
1038                                            "$$lang",
1039                                            params().language->babel()))
1040                    << '\n';
1041                 texrow().newline();
1042         }
1043
1044         if (output_preamble) {
1045                 os << "\\end{document}\n";
1046                 texrow().newline();
1047
1048                 LYXERR(Debug::LATEX) << "makeLaTeXFile...done" << endl;
1049         } else {
1050                 LYXERR(Debug::LATEX) << "LaTeXFile for inclusion made."
1051                                      << endl;
1052         }
1053         runparams_in.encoding = runparams.encoding;
1054
1055         // Just to be sure. (Asger)
1056         texrow().newline();
1057
1058         LYXERR(Debug::INFO) << "Finished making LaTeX file." << endl;
1059         LYXERR(Debug::INFO) << "Row count was " << texrow().rows() - 1
1060                             << '.' << endl;
1061 }
1062
1063
1064 bool Buffer::isLatex() const
1065 {
1066         return params().getTextClass().outputType() == LATEX;
1067 }
1068
1069
1070 bool Buffer::isLiterate() const
1071 {
1072         return params().getTextClass().outputType() == LITERATE;
1073 }
1074
1075
1076 bool Buffer::isDocBook() const
1077 {
1078         return params().getTextClass().outputType() == DOCBOOK;
1079 }
1080
1081
1082 void Buffer::makeDocBookFile(FileName const & fname,
1083                               OutputParams const & runparams,
1084                               bool const body_only)
1085 {
1086         LYXERR(Debug::LATEX) << "makeDocBookFile..." << endl;
1087
1088         //ofstream ofs;
1089         odocfstream ofs;
1090         if (!openFileWrite(ofs, fname))
1091                 return;
1092
1093         writeDocBookSource(ofs, fname.absFilename(), runparams, body_only);
1094
1095         ofs.close();
1096         if (ofs.fail())
1097                 lyxerr << "File '" << fname << "' was not closed properly." << endl;
1098 }
1099
1100
1101 void Buffer::writeDocBookSource(odocstream & os, string const & fname,
1102                              OutputParams const & runparams,
1103                              bool const only_body)
1104 {
1105         LaTeXFeatures features(*this, params(), runparams);
1106         validate(features);
1107
1108         texrow().reset();
1109
1110         TextClass const & tclass = params().getTextClass();
1111         string const top_element = tclass.latexname();
1112
1113         if (!only_body) {
1114                 if (runparams.flavor == OutputParams::XML)
1115                         os << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
1116
1117                 // FIXME UNICODE
1118                 os << "<!DOCTYPE " << from_ascii(top_element) << ' ';
1119
1120                 // FIXME UNICODE
1121                 if (! tclass.class_header().empty())
1122                         os << from_ascii(tclass.class_header());
1123                 else if (runparams.flavor == OutputParams::XML)
1124                         os << "PUBLIC \"-//OASIS//DTD DocBook XML//EN\" "
1125                             << "\"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd\"";
1126                 else
1127                         os << " PUBLIC \"-//OASIS//DTD DocBook V4.2//EN\"";
1128
1129                 docstring preamble = from_utf8(params().preamble);
1130                 if (runparams.flavor != OutputParams::XML ) {
1131                         preamble += "<!ENTITY % output.print.png \"IGNORE\">\n";
1132                         preamble += "<!ENTITY % output.print.pdf \"IGNORE\">\n";
1133                         preamble += "<!ENTITY % output.print.eps \"IGNORE\">\n";
1134                         preamble += "<!ENTITY % output.print.bmp \"IGNORE\">\n";
1135                 }
1136
1137                 string const name = runparams.nice ? changeExtension(fileName(), ".sgml")
1138                          : fname;
1139                 preamble += features.getIncludedFiles(name);
1140                 preamble += features.getLyXSGMLEntities();
1141
1142                 if (!preamble.empty()) {
1143                         os << "\n [ " << preamble << " ]";
1144                 }
1145                 os << ">\n\n";
1146         }
1147
1148         string top = top_element;
1149         top += " lang=\"";
1150         if (runparams.flavor == OutputParams::XML)
1151                 top += params().language->code();
1152         else
1153                 top += params().language->code().substr(0,2);
1154         top += '"';
1155
1156         if (!params().options.empty()) {
1157                 top += ' ';
1158                 top += params().options;
1159         }
1160
1161         os << "<!-- " << ((runparams.flavor == OutputParams::XML)? "XML" : "SGML")
1162             << " file was created by LyX " << lyx_version
1163             << "\n  See http://www.lyx.org/ for more information -->\n";
1164
1165         params().getTextClass().counters().reset();
1166
1167         sgml::openTag(os, top);
1168         os << '\n';
1169         docbookParagraphs(paragraphs(), *this, os, runparams);
1170         sgml::closeTag(os, top_element);
1171 }
1172
1173
1174 // chktex should be run with these flags disabled: 3, 22, 25, 30, 38(?)
1175 // Other flags: -wall -v0 -x
1176 int Buffer::runChktex()
1177 {
1178         busy(true);
1179
1180         // get LaTeX-Filename
1181         FileName const path(temppath());
1182         string const name = addName(path.absFilename(), getLatexName());
1183         string const org_path = filePath();
1184
1185         support::Path p(path); // path to LaTeX file
1186         message(_("Running chktex..."));
1187
1188         // Generate the LaTeX file if neccessary
1189         OutputParams runparams(&params().encoding());
1190         runparams.flavor = OutputParams::LATEX;
1191         runparams.nice = false;
1192         makeLaTeXFile(FileName(name), org_path, runparams);
1193
1194         TeXErrors terr;
1195         Chktex chktex(lyxrc.chktex_command, onlyFilename(name), filePath());
1196         int const res = chktex.run(terr); // run chktex
1197
1198         if (res == -1) {
1199                 Alert::error(_("chktex failure"),
1200                              _("Could not run chktex successfully."));
1201         } else if (res > 0) {
1202                 ErrorList & errorList = errorLists_["ChkTeX"];
1203                 // Clear out old errors
1204                 errorList.clear();
1205                 // Fill-in the error list with the TeX errors
1206                 bufferErrors(*this, terr, errorList);
1207         }
1208
1209         busy(false);
1210
1211         errors("ChkTeX");
1212
1213         return res;
1214 }
1215
1216
1217 void Buffer::validate(LaTeXFeatures & features) const
1218 {
1219         TextClass const & tclass = params().getTextClass();
1220
1221         if (params().outputChanges) {
1222                 bool dvipost    = LaTeXFeatures::isAvailable("dvipost");
1223                 bool xcolorsoul = LaTeXFeatures::isAvailable("soul") &&
1224                                   LaTeXFeatures::isAvailable("xcolor");
1225                 
1226                 if (features.runparams().flavor == OutputParams::LATEX) {
1227                         if (dvipost) {
1228                                 features.require("ct-dvipost");
1229                                 features.require("dvipost");
1230                         } else if (xcolorsoul) {
1231                                 features.require("ct-xcolor-soul");
1232                                 features.require("soul");
1233                                 features.require("xcolor");
1234                         } else {        
1235                                 features.require("ct-none");
1236                         }
1237                 } else if (features.runparams().flavor == OutputParams::PDFLATEX ) {
1238                         if (xcolorsoul) {
1239                                 features.require("ct-xcolor-soul");
1240                                 features.require("soul");
1241                                 features.require("xcolor");
1242                                 features.require("pdfcolmk"); // improves color handling in PDF output
1243                         } else {
1244                                 features.require("ct-none");
1245                         }
1246                 }
1247         }
1248
1249         // AMS Style is at document level
1250         if (params().use_amsmath == BufferParams::package_on
1251             || tclass.provides("amsmath"))
1252                 features.require("amsmath");
1253         if (params().use_esint == BufferParams::package_on)
1254                 features.require("esint");
1255
1256         for_each(paragraphs().begin(), paragraphs().end(),
1257                  boost::bind(&Paragraph::validate, _1, boost::ref(features)));
1258
1259         // the bullet shapes are buffer level not paragraph level
1260         // so they are tested here
1261         for (int i = 0; i < 4; ++i) {
1262                 if (params().user_defined_bullet(i) != ITEMIZE_DEFAULTS[i]) {
1263                         int const font = params().user_defined_bullet(i).getFont();
1264                         if (font == 0) {
1265                                 int const c = params()
1266                                         .user_defined_bullet(i)
1267                                         .getCharacter();
1268                                 if (c == 16
1269                                    || c == 17
1270                                    || c == 25
1271                                    || c == 26
1272                                    || c == 31) {
1273                                         features.require("latexsym");
1274                                 }
1275                         } else if (font == 1) {
1276                                 features.require("amssymb");
1277                         } else if ((font >= 2 && font <= 5)) {
1278                                 features.require("pifont");
1279                         }
1280                 }
1281         }
1282
1283         if (lyxerr.debugging(Debug::LATEX)) {
1284                 features.showStruct();
1285         }
1286 }
1287
1288
1289 void Buffer::getLabelList(vector<docstring> & list) const
1290 {
1291         /// if this is a child document and the parent is already loaded
1292         /// Use the parent's list instead  [ale990407]
1293         Buffer const * tmp = getMasterBuffer();
1294         if (!tmp) {
1295                 lyxerr << "getMasterBuffer() failed!" << endl;
1296                 BOOST_ASSERT(tmp);
1297         }
1298         if (tmp != this) {
1299                 tmp->getLabelList(list);
1300                 return;
1301         }
1302
1303         for (InsetIterator it = inset_iterator_begin(inset()); it; ++it)
1304                 it.nextInset()->getLabelList(*this, list);
1305 }
1306
1307
1308 // This is also a buffer property (ale)
1309 void Buffer::fillWithBibKeys(vector<pair<string, docstring> > & keys)
1310         const
1311 {
1312         /// if this is a child document and the parent is already loaded
1313         /// use the parent's list instead  [ale990412]
1314         Buffer const * tmp = getMasterBuffer();
1315         BOOST_ASSERT(tmp);
1316         if (tmp != this) {
1317                 tmp->fillWithBibKeys(keys);
1318                 return;
1319         }
1320
1321         for (InsetIterator it = inset_iterator_begin(inset()); it; ++it) {
1322                 if (it->lyxCode() == Inset::BIBTEX_CODE) {
1323                         InsetBibtex const & inset =
1324                                 static_cast<InsetBibtex const &>(*it);
1325                         inset.fillWithBibKeys(*this, keys);
1326                 } else if (it->lyxCode() == Inset::INCLUDE_CODE) {
1327                         InsetInclude const & inset =
1328                                 static_cast<InsetInclude const &>(*it);
1329                         inset.fillWithBibKeys(*this, keys);
1330                 } else if (it->lyxCode() == Inset::BIBITEM_CODE) {
1331                         InsetBibitem const & inset =
1332                                 static_cast<InsetBibitem const &>(*it);
1333                         // FIXME UNICODE
1334                         string const key = to_utf8(inset.getParam("key"));
1335                         docstring const label = inset.getParam("label");
1336                         DocIterator doc_it(it); doc_it.forwardPos();
1337                         docstring const ref = doc_it.paragraph().asString(*this, false);
1338                         docstring const info = label + "TheBibliographyRef" + ref;
1339                         keys.push_back(pair<string, docstring>(key, info));
1340                 }
1341         }
1342 }
1343
1344
1345 void Buffer::updateBibfilesCache()
1346 {
1347         // if this is a child document and the parent is already loaded
1348         // update the parent's cache instead
1349         Buffer * tmp = getMasterBuffer();
1350         BOOST_ASSERT(tmp);
1351         if (tmp != this) {
1352                 tmp->updateBibfilesCache();
1353                 return;
1354         }
1355
1356         bibfilesCache_.clear();
1357         for (InsetIterator it = inset_iterator_begin(inset()); it; ++it) {
1358                 if (it->lyxCode() == Inset::BIBTEX_CODE) {
1359                         InsetBibtex const & inset =
1360                                 static_cast<InsetBibtex const &>(*it);
1361                         vector<FileName> const bibfiles = inset.getFiles(*this);
1362                         bibfilesCache_.insert(bibfilesCache_.end(),
1363                                 bibfiles.begin(),
1364                                 bibfiles.end());
1365                 } else if (it->lyxCode() == Inset::INCLUDE_CODE) {
1366                         InsetInclude & inset =
1367                                 static_cast<InsetInclude &>(*it);
1368                         inset.updateBibfilesCache(*this);
1369                         vector<FileName> const & bibfiles =
1370                                         inset.getBibfilesCache(*this);
1371                         bibfilesCache_.insert(bibfilesCache_.end(),
1372                                 bibfiles.begin(),
1373                                 bibfiles.end());
1374                 }
1375         }
1376 }
1377
1378
1379 vector<FileName> const & Buffer::getBibfilesCache() const
1380 {
1381         // if this is a child document and the parent is already loaded
1382         // use the parent's cache instead
1383         Buffer const * tmp = getMasterBuffer();
1384         BOOST_ASSERT(tmp);
1385         if (tmp != this)
1386                 return tmp->getBibfilesCache();
1387
1388         // We update the cache when first used instead of at loading time.
1389         if (bibfilesCache_.empty())
1390                 const_cast<Buffer *>(this)->updateBibfilesCache();
1391
1392         return bibfilesCache_;
1393 }
1394
1395
1396 bool Buffer::isDepClean(string const & name) const
1397 {
1398         DepClean::const_iterator const it = pimpl_->dep_clean.find(name);
1399         if (it == pimpl_->dep_clean.end())
1400                 return true;
1401         return it->second;
1402 }
1403
1404
1405 void Buffer::markDepClean(string const & name)
1406 {
1407         pimpl_->dep_clean[name] = true;
1408 }
1409
1410
1411 bool Buffer::dispatch(string const & command, bool * result)
1412 {
1413         return dispatch(lyxaction.lookupFunc(command), result);
1414 }
1415
1416
1417 bool Buffer::dispatch(FuncRequest const & func, bool * result)
1418 {
1419         bool dispatched = true;
1420
1421         switch (func.action) {
1422                 case LFUN_BUFFER_EXPORT: {
1423                         bool const tmp = Exporter::Export(this, to_utf8(func.argument()), false);
1424                         if (result)
1425                                 *result = tmp;
1426                         break;
1427                 }
1428
1429                 default:
1430                         dispatched = false;
1431         }
1432         return dispatched;
1433 }
1434
1435
1436 void Buffer::changeLanguage(Language const * from, Language const * to)
1437 {
1438         BOOST_ASSERT(from);
1439         BOOST_ASSERT(to);
1440
1441         for_each(par_iterator_begin(),
1442                  par_iterator_end(),
1443                  bind(&Paragraph::changeLanguage, _1, params(), from, to));
1444
1445         text().current_font.setLanguage(to);
1446         text().real_current_font.setLanguage(to);
1447 }
1448
1449
1450 bool Buffer::isMultiLingual() const
1451 {
1452         ParConstIterator end = par_iterator_end();
1453         for (ParConstIterator it = par_iterator_begin(); it != end; ++it)
1454                 if (it->isMultiLingual(params()))
1455                         return true;
1456
1457         return false;
1458 }
1459
1460
1461 ParIterator Buffer::getParFromID(int const id) const
1462 {
1463         ParConstIterator it = par_iterator_begin();
1464         ParConstIterator const end = par_iterator_end();
1465
1466         if (id < 0) {
1467                 // John says this is called with id == -1 from undo
1468                 lyxerr << "getParFromID(), id: " << id << endl;
1469                 return end;
1470         }
1471
1472         for (; it != end; ++it)
1473                 if (it->id() == id)
1474                         return it;
1475
1476         return end;
1477 }
1478
1479
1480 bool Buffer::hasParWithID(int const id) const
1481 {
1482         ParConstIterator const it = getParFromID(id);
1483         return it != par_iterator_end();
1484 }
1485
1486
1487 ParIterator Buffer::par_iterator_begin()
1488 {
1489         return lyx::par_iterator_begin(inset());
1490 }
1491
1492
1493 ParIterator Buffer::par_iterator_end()
1494 {
1495         return lyx::par_iterator_end(inset());
1496 }
1497
1498
1499 ParConstIterator Buffer::par_iterator_begin() const
1500 {
1501         return lyx::par_const_iterator_begin(inset());
1502 }
1503
1504
1505 ParConstIterator Buffer::par_iterator_end() const
1506 {
1507         return lyx::par_const_iterator_end(inset());
1508 }
1509
1510
1511 Language const * Buffer::getLanguage() const
1512 {
1513         return params().language;
1514 }
1515
1516
1517 docstring const Buffer::B_(string const & l10n) const
1518 {
1519         return params().B_(l10n);
1520 }
1521
1522
1523 bool Buffer::isClean() const
1524 {
1525         return pimpl_->lyx_clean;
1526 }
1527
1528
1529 bool Buffer::isBakClean() const
1530 {
1531         return pimpl_->bak_clean;
1532 }
1533
1534
1535 void Buffer::markClean() const
1536 {
1537         if (!pimpl_->lyx_clean) {
1538                 pimpl_->lyx_clean = true;
1539                 updateTitles();
1540         }
1541         // if the .lyx file has been saved, we don't need an
1542         // autosave
1543         pimpl_->bak_clean = true;
1544 }
1545
1546
1547 void Buffer::markBakClean()
1548 {
1549         pimpl_->bak_clean = true;
1550 }
1551
1552
1553 void Buffer::setUnnamed(bool flag)
1554 {
1555         pimpl_->unnamed = flag;
1556 }
1557
1558
1559 bool Buffer::isUnnamed() const
1560 {
1561         return pimpl_->unnamed;
1562 }
1563
1564
1565 #ifdef WITH_WARNINGS
1566 #warning this function should be moved to buffer_pimpl.C
1567 #endif
1568 void Buffer::markDirty()
1569 {
1570         if (pimpl_->lyx_clean) {
1571                 pimpl_->lyx_clean = false;
1572                 updateTitles();
1573         }
1574         pimpl_->bak_clean = false;
1575
1576         DepClean::iterator it = pimpl_->dep_clean.begin();
1577         DepClean::const_iterator const end = pimpl_->dep_clean.end();
1578
1579         for (; it != end; ++it)
1580                 it->second = false;
1581 }
1582
1583
1584 string const Buffer::fileName() const
1585 {
1586         return pimpl_->filename.absFilename();
1587 }
1588
1589
1590 string const & Buffer::filePath() const
1591 {
1592         return params().filepath;
1593 }
1594
1595
1596 bool Buffer::isReadonly() const
1597 {
1598         return pimpl_->read_only;
1599 }
1600
1601
1602 void Buffer::setParentName(string const & name)
1603 {
1604         params().parentname = name;
1605 }
1606
1607
1608 Buffer const * Buffer::getMasterBuffer() const
1609 {
1610         if (!params().parentname.empty()
1611             && theBufferList().exists(params().parentname)) {
1612                 Buffer const * buf = theBufferList().getBuffer(params().parentname);
1613                 if (buf)
1614                         return buf->getMasterBuffer();
1615         }
1616
1617         return this;
1618 }
1619
1620
1621 Buffer * Buffer::getMasterBuffer()
1622 {
1623         if (!params().parentname.empty()
1624             && theBufferList().exists(params().parentname)) {
1625                 Buffer * buf = theBufferList().getBuffer(params().parentname);
1626                 if (buf)
1627                         return buf->getMasterBuffer();
1628         }
1629
1630         return this;
1631 }
1632
1633
1634 MacroData const & Buffer::getMacro(docstring const & name) const
1635 {
1636         return pimpl_->macros.get(name);
1637 }
1638
1639
1640 bool Buffer::hasMacro(docstring const & name) const
1641 {
1642         return pimpl_->macros.has(name);
1643 }
1644
1645
1646 void Buffer::insertMacro(docstring const & name, MacroData const & data)
1647 {
1648         MacroTable::globalMacros().insert(name, data);
1649         pimpl_->macros.insert(name, data);
1650 }
1651
1652
1653 void Buffer::buildMacros()
1654 {
1655         // Start with global table.
1656         pimpl_->macros = MacroTable::globalMacros();
1657
1658         // Now add our own.
1659         ParagraphList const & pars = text().paragraphs();
1660         for (size_t i = 0, n = pars.size(); i != n; ++i) {
1661                 //lyxerr << "searching main par " << i
1662                 //      << " for macro definitions" << std::endl;
1663                 InsetList const & insets = pars[i].insetlist;
1664                 InsetList::const_iterator it = insets.begin();
1665                 InsetList::const_iterator end = insets.end();
1666                 for ( ; it != end; ++it) {
1667                         //lyxerr << "found inset code " << it->inset->lyxCode() << std::endl;
1668                         if (it->inset->lyxCode() == Inset::MATHMACRO_CODE) {
1669                                 MathMacroTemplate const & mac
1670                                         = static_cast<MathMacroTemplate const &>(*it->inset);
1671                                 insertMacro(mac.name(), mac.asMacroData());
1672                         }
1673                 }
1674         }
1675 }
1676
1677
1678 void Buffer::saveCursor(StableDocIterator cur, StableDocIterator anc)
1679 {
1680         cursor_ = cur;
1681         anchor_ = anc;
1682 }
1683
1684
1685 void Buffer::changeRefsIfUnique(docstring const & from, docstring const & to,
1686         Inset::Code code)
1687 {
1688         //FIXME: This does not work for child documents yet.
1689         BOOST_ASSERT(code == Inset::CITE_CODE || code == Inset::REF_CODE);
1690         // Check if the label 'from' appears more than once
1691         vector<docstring> labels;
1692
1693         if (code == Inset::CITE_CODE) {
1694                 vector<pair<string, docstring> > keys;
1695                 fillWithBibKeys(keys);
1696                 vector<pair<string, docstring> >::const_iterator bit  = keys.begin();
1697                 vector<pair<string, docstring> >::const_iterator bend = keys.end();
1698
1699                 for (; bit != bend; ++bit)
1700                         // FIXME UNICODE
1701                         labels.push_back(from_utf8(bit->first));
1702         } else
1703                 getLabelList(labels);
1704
1705         if (lyx::count(labels.begin(), labels.end(), from) > 1)
1706                 return;
1707
1708         for (InsetIterator it = inset_iterator_begin(inset()); it; ++it) {
1709                 if (it->lyxCode() == code) {
1710                         InsetCommand & inset = static_cast<InsetCommand &>(*it);
1711                         inset.replaceContents(to_utf8(from), to_utf8(to));
1712                 }
1713         }
1714 }
1715
1716
1717 void Buffer::getSourceCode(odocstream & os, pit_type par_begin,
1718         pit_type par_end, bool full_source)
1719 {
1720         OutputParams runparams(&params().encoding());
1721         runparams.nice = true;
1722         runparams.flavor = OutputParams::LATEX;
1723         runparams.linelen = lyxrc.plaintext_linelen;
1724         // No side effect of file copying and image conversion
1725         runparams.dryrun = true;
1726
1727         if (full_source) {
1728                 os << "% Preview source code\n\n";
1729                 if (isLatex())
1730                         writeLaTeXSource(os, filePath(), runparams, true, true);
1731                 else {
1732                         writeDocBookSource(os, fileName(), runparams, false);
1733                 }
1734         } else {
1735                 runparams.par_begin = par_begin;
1736                 runparams.par_end = par_end;
1737                 if (par_begin + 1 == par_end)
1738                         os << "% Preview source code for paragraph " << par_begin << "\n\n";
1739                 else
1740                         os << "% Preview source code from paragraph " << par_begin
1741                            << " to " << par_end - 1 << "\n\n";
1742                 // output paragraphs
1743                 if (isLatex()) {
1744                         texrow().reset();
1745                         latexParagraphs(*this, paragraphs(), os, texrow(), runparams);
1746                 } else {
1747                         // DocBook
1748                         docbookParagraphs(paragraphs(), *this, os, runparams);
1749                 }
1750         }
1751 }
1752
1753
1754 ErrorList const & Buffer::errorList(string const & type) const
1755 {
1756         static ErrorList const emptyErrorList;
1757         std::map<string, ErrorList>::const_iterator I = errorLists_.find(type);
1758         if (I == errorLists_.end())
1759                 return emptyErrorList;
1760
1761         return I->second;
1762 }
1763
1764
1765 ErrorList & Buffer::errorList(string const & type)
1766 {
1767         return errorLists_[type];
1768 }
1769
1770
1771 } // namespace lyx