3 * This file is part of LyX, the document processor.
4 * Licence details can be found in the file COPYING.
6 * \author Lars Gullik Bjønnes
8 * Full author contact details are available in file CREDITS.
16 #include "buffer_funcs.h"
17 #include "bufferlist.h"
18 #include "bufferparams.h"
23 #include "errorlist.h"
26 #include "funcrequest.h"
28 #include "iterators.h"
31 #include "LaTeXFeatures.h"
32 #include "LyXAction.h"
38 #include "output_docbook.h"
39 #include "output_latex.h"
40 #include "output_linuxdoc.h"
41 #include "paragraph.h"
42 #include "paragraph_funcs.h"
43 #include "ParagraphParameters.h"
44 #include "PosIterator.h"
50 #include "insets/insetbibitem.h"
51 #include "insets/insetbibtex.h"
52 #include "insets/insetinclude.h"
53 #include "insets/insettext.h"
55 #include "frontends/Alert.h"
57 #include "graphics/Previews.h"
59 #include "support/FileInfo.h"
60 #include "support/filetools.h"
61 #include "support/gzstream.h"
62 #include "support/lyxlib.h"
63 #include "support/os.h"
64 #include "support/path.h"
65 #include "support/textutils.h"
66 #include "support/tostr.h"
68 #include <boost/bind.hpp>
70 #include "support/std_sstream.h"
82 using lyx::support::AddName;
83 using lyx::support::atoi;
84 using lyx::support::bformat;
85 using lyx::support::ChangeExtension;
86 using lyx::support::cmd_ret;
87 using lyx::support::CreateBufferTmpDir;
88 using lyx::support::destroyDir;
89 using lyx::support::FileInfo;
90 using lyx::support::FileInfo;
91 using lyx::support::getExtFromContents;
92 using lyx::support::IsDirWriteable;
93 using lyx::support::IsFileWriteable;
94 using lyx::support::LibFileSearch;
95 using lyx::support::ltrim;
96 using lyx::support::MakeAbsPath;
97 using lyx::support::MakeDisplayPath;
98 using lyx::support::MakeLatexName;
99 using lyx::support::OnlyFilename;
100 using lyx::support::OnlyPath;
101 using lyx::support::Path;
102 using lyx::support::QuoteName;
103 using lyx::support::removeAutosaveFile;
104 using lyx::support::rename;
105 using lyx::support::RunCommand;
106 using lyx::support::split;
107 using lyx::support::strToInt;
108 using lyx::support::subst;
109 using lyx::support::tempName;
110 using lyx::support::trim;
112 namespace os = lyx::support::os;
116 using std::make_pair;
121 using std::ostringstream;
129 // all these externs should eventually be removed.
130 extern BufferList bufferlist;
134 const int LYX_FORMAT = 225;
139 typedef std::map<string, bool> DepClean;
143 Impl(Buffer & parent, string const & file, bool readonly);
145 limited_stack<Undo> undostack;
146 limited_stack<Undo> redostack;
148 ParagraphList paragraphs;
154 /// need to regenerate .tex ?
158 mutable bool lyx_clean;
160 /// is autosave needed
161 mutable bool bak_clean;
163 /// is this a unnamed file (New...)
169 /// name of the file the buffer is associated with.
172 /// The path to the document file.
175 boost::scoped_ptr<Messages> messages;
177 /** set to true only when the file is fully loaded.
178 * Used to prevent the premature generation of previews
179 * and by the citation inset.
181 bool file_fully_loaded;
185 Buffer::Impl::Impl(Buffer & parent, string const & file, bool readonly_)
187 lyx_clean(true), bak_clean(true), unnamed(false), read_only(readonly_),
188 filename(file), filepath(OnlyPath(file)), file_fully_loaded(false)
190 lyxvc.buffer(&parent);
191 if (readonly_ || lyxrc.use_tempdir)
192 temppath = CreateBufferTmpDir();
196 Buffer::Buffer(string const & file, bool ronly)
197 : pimpl_(new Impl(*this, file, ronly))
199 lyxerr[Debug::INFO] << "Buffer::Buffer()" << endl;
205 lyxerr[Debug::INFO] << "Buffer::~Buffer()" << endl;
206 // here the buffer should take care that it is
207 // saved properly, before it goes into the void.
211 if (!temppath().empty() && destroyDir(temppath()) != 0) {
212 Alert::warning(_("Could not remove temporary directory"),
213 bformat(_("Could not remove the temporary directory %1$s"), temppath()));
216 paragraphs().clear();
218 // Remove any previewed LaTeX snippets associated with this buffer.
219 lyx::graphics::Previews::get().removeLoader(*this);
223 limited_stack<Undo> & Buffer::undostack()
225 return pimpl_->undostack;
229 limited_stack<Undo> const & Buffer::undostack() const
231 return pimpl_->undostack;
235 limited_stack<Undo> & Buffer::redostack()
237 return pimpl_->redostack;
241 limited_stack<Undo> const & Buffer::redostack() const
243 return pimpl_->redostack;
247 BufferParams & Buffer::params()
249 return pimpl_->params;
253 BufferParams const & Buffer::params() const
255 return pimpl_->params;
259 ParagraphList & Buffer::paragraphs()
261 return pimpl_->paragraphs;
265 ParagraphList const & Buffer::paragraphs() const
267 return pimpl_->paragraphs;
271 LyXVC & Buffer::lyxvc()
273 return pimpl_->lyxvc;
277 LyXVC const & Buffer::lyxvc() const
279 return pimpl_->lyxvc;
283 string const & Buffer::temppath() const
285 return pimpl_->temppath;
289 bool & Buffer::niceFile()
291 return pimpl_->nicefile;
295 bool Buffer::niceFile() const
297 return pimpl_->nicefile;
301 TexRow & Buffer::texrow()
303 return pimpl_->texrow;
307 TexRow const & Buffer::texrow() const
309 return pimpl_->texrow;
313 string const Buffer::getLatexName(bool no_path) const
315 string const name = ChangeExtension(MakeLatexName(fileName()), ".tex");
316 return no_path ? OnlyFilename(name) : name;
320 pair<Buffer::LogType, string> const Buffer::getLogName() const
322 string const filename = getLatexName(false);
324 if (filename.empty())
325 return make_pair(Buffer::latexlog, string());
327 string path = OnlyPath(filename);
329 if (lyxrc.use_tempdir || !IsDirWriteable(path))
332 string const fname = AddName(path,
333 OnlyFilename(ChangeExtension(filename,
336 AddName(path, OnlyFilename(
337 ChangeExtension(filename,
338 formats.extension("literate") + ".out")));
340 // If no Latex log or Build log is newer, show Build log
342 FileInfo const f_fi(fname);
343 FileInfo const b_fi(bname);
346 (!f_fi.exist() || f_fi.getModificationTime() < b_fi.getModificationTime())) {
347 lyxerr[Debug::FILES] << "Log name calculated as: " << bname << endl;
348 return make_pair(Buffer::buildlog, bname);
350 lyxerr[Debug::FILES] << "Log name calculated as: " << fname << endl;
351 return make_pair(Buffer::latexlog, fname);
355 void Buffer::setReadonly(bool flag)
357 if (pimpl_->read_only != flag) {
358 pimpl_->read_only = flag;
364 void Buffer::setFileName(string const & newfile)
366 pimpl_->filename = MakeAbsPath(newfile);
367 pimpl_->filepath = OnlyPath(pimpl_->filename);
368 setReadonly(IsFileWriteable(pimpl_->filename) == 0);
373 // We'll remove this later. (Lgb)
376 void unknownClass(string const & unknown)
378 Alert::warning(_("Unknown document class"),
379 bformat(_("Using the default document class, because the "
380 "class %1$s is unknown."), unknown));
385 int Buffer::readHeader(LyXLex & lex)
387 int unknown_tokens = 0;
391 string const token = lex.getString();
396 if (token == "\\end_header")
399 lyxerr[Debug::PARSER] << "Handling header token: `"
400 << token << '\'' << endl;
403 string unknown = params().readToken(lex, token);
404 if (!unknown.empty()) {
405 if (unknown[0] != '\\') {
406 unknownClass(unknown);
409 string const s = bformat(_("Unknown token: "
413 error(ErrorItem(_("Header error"), s,
418 return unknown_tokens;
422 // candidate for move to BufferView
423 // (at least some parts in the beginning of the func)
426 // changed to be public and have one parameter
427 // if par = 0 normal behavior
428 // else insert behavior
429 // Returns false if "\end_document" is not read (Asger)
430 bool Buffer::readBody(LyXLex & lex, ParagraphList::iterator pit)
432 Paragraph::depth_type depth = 0;
433 bool the_end_read = false;
435 if (paragraphs().empty()) {
437 if (!params().getLyXTextClass().load()) {
438 string theclass = params().getLyXTextClass().name();
439 Alert::error(_("Can't load document class"), bformat(
440 "Using the default document class, because the "
441 " class %1$s could not be loaded.", theclass));
442 params().textclass = 0;
445 // We don't want to adopt the parameters from the
446 // document we insert, so read them into a temporary buffer
447 // and then discard it
449 Buffer tmpbuf("", false);
450 tmpbuf.readHeader(lex);
455 string const token = lex.getString();
460 lyxerr[Debug::PARSER] << "Handling token: `"
461 << token << '\'' << endl;
463 if (token == "\\end_document") {
468 readParagraph(lex, token, paragraphs(), pit, depth);
475 int Buffer::readParagraph(LyXLex & lex, string const & token,
476 ParagraphList & pars, ParagraphList::iterator & pit,
477 lyx::depth_type & depth)
479 static Change current_change;
482 if (token == "\\begin_layout") {
483 lex.pushToken(token);
486 par.params().depth(depth);
487 if (params().tracking_changes)
489 LyXFont f(LyXFont::ALL_INHERIT, params().language);
493 if (pit != pars.end())
496 pit = pars.insert(pit, par);
498 // FIXME: goddamn InsetTabular makes us pass a Buffer
500 ::readParagraph(*this, *pit, lex);
502 } else if (token == "\\begin_deeper") {
504 } else if (token == "\\end_deeper") {
506 lex.printError("\\end_deeper: " "depth is already null");
517 // needed to insert the selection
518 void Buffer::insertStringAsLines(ParagraphList::iterator & par, pos_type & pos,
519 LyXFont const & fn,string const & str)
521 LyXLayout_ptr const & layout = par->layout();
525 par->checkInsertChar(font);
526 // insert the string, don't insert doublespace
527 bool space_inserted = true;
528 bool autobreakrows = !par->inInset() ||
529 static_cast<InsetText *>(par->inInset())->getAutoBreakRows();
530 for(string::const_iterator cit = str.begin();
531 cit != str.end(); ++cit) {
533 if (autobreakrows && (!par->empty() || par->allowEmpty())) {
534 breakParagraph(params(), paragraphs(), par, pos,
535 layout->isEnvironment());
538 space_inserted = true;
542 // do not insert consecutive spaces if !free_spacing
543 } else if ((*cit == ' ' || *cit == '\t') &&
544 space_inserted && !par->isFreeSpacing()) {
546 } else if (*cit == '\t') {
547 if (!par->isFreeSpacing()) {
548 // tabs are like spaces here
549 par->insertChar(pos, ' ', font);
551 space_inserted = true;
553 const pos_type nb = 8 - pos % 8;
554 for (pos_type a = 0; a < nb ; ++a) {
555 par->insertChar(pos, ' ', font);
558 space_inserted = true;
560 } else if (!IsPrintable(*cit)) {
561 // Ignore unprintables
564 // just insert the character
565 par->insertChar(pos, *cit, font);
567 space_inserted = (*cit == ' ');
574 bool Buffer::readFile(string const & filename)
576 // Check if the file is compressed.
577 string const format = getExtFromContents(filename);
578 if (format == "gzip" || format == "zip" || format == "compress") {
579 params().compressed = true;
582 bool ret = readFile(filename, paragraphs().begin());
584 // After we have read a file, we must ensure that the buffer
585 // language is set and used in the gui.
586 // If you know of a better place to put this, please tell me. (Lgb)
587 updateDocLang(params().language);
593 bool Buffer::readFile(string const & filename, ParagraphList::iterator pit)
596 lex.setFile(filename);
598 return readFile(lex, filename, pit);
602 bool Buffer::fully_loaded() const
604 return pimpl_->file_fully_loaded;
608 void Buffer::fully_loaded(bool value)
610 pimpl_->file_fully_loaded = value;
614 bool Buffer::readFile(LyXLex & lex, string const & filename,
615 ParagraphList::iterator pit)
617 BOOST_ASSERT(!filename.empty());
620 Alert::error(_("Document could not be read"),
621 bformat(_("%1$s could not be read."), filename));
626 string const token(lex.getString());
629 Alert::error(_("Document could not be read"),
630 bformat(_("%1$s could not be read."), filename));
634 // the first token _must_ be...
635 if (token != "\\lyxformat") {
636 lyxerr << "Token: " << token << endl;
638 Alert::error(_("Document format failure"),
639 bformat(_("%1$s is not a LyX document."),
645 string tmp_format = lex.getString();
646 //lyxerr << "LyX Format: `" << tmp_format << '\'' << endl;
647 // if present remove ".," from string.
648 string::size_type dot = tmp_format.find_first_of(".,");
649 //lyxerr << " dot found at " << dot << endl;
650 if (dot != string::npos)
651 tmp_format.erase(dot, 1);
652 int file_format = strToInt(tmp_format);
653 //lyxerr << "format: " << file_format << endl;
655 if (file_format > LYX_FORMAT) {
656 Alert::warning(_("Document format failure"),
657 bformat(_("%1$s was created with a newer"
658 " version of LyX. This is likely to"
661 } else if (file_format < LYX_FORMAT) {
662 string const tmpfile = tempName();
663 string command = LibFileSearch("lyx2lyx", "lyx2lyx");
664 if (command.empty()) {
665 Alert::error(_("Conversion script not found"),
666 bformat(_("%1$s is from an earlier"
667 " version of LyX, but the"
668 " conversion script lyx2lyx"
669 " could not be found."),
675 + " -o " + tmpfile + ' '
676 + QuoteName(filename);
677 lyxerr[Debug::INFO] << "Running '"
680 cmd_ret const ret = RunCommand(command);
681 if (ret.first != 0) {
682 Alert::error(_("Conversion script failed"),
683 bformat(_("%1$s is from an earlier version"
684 " of LyX, but the lyx2lyx script"
685 " failed to convert it."),
689 bool ret = readFile(tmpfile, pit);
690 // Do stuff with tmpfile name and buffer name here.
696 bool the_end = readBody(lex, pit);
697 params().setPaperStuff();
700 Alert::error(_("Document format failure"),
701 bformat(_("%1$s ended unexpectedly, which means"
702 " that it is probably corrupted."),
705 pimpl_->file_fully_loaded = true;
710 // Should probably be moved to somewhere else: BufferView? LyXView?
711 bool Buffer::save() const
713 // We don't need autosaves in the immediate future. (Asger)
714 resetAutosaveTimers();
718 if (lyxrc.make_backup) {
719 s = fileName() + '~';
720 if (!lyxrc.backupdir_path.empty())
721 s = AddName(lyxrc.backupdir_path,
722 subst(os::slashify_path(s),'/','!'));
724 // Rename is the wrong way of making a backup,
725 // this is the correct way.
726 /* truss cp fil fil2:
727 lstat("LyXVC3.lyx", 0xEFFFF898) Err#2 ENOENT
728 stat("LyXVC.lyx", 0xEFFFF688) = 0
729 open("LyXVC.lyx", O_RDONLY) = 3
730 open("LyXVC3.lyx", O_WRONLY|O_CREAT|O_TRUNC, 0600) = 4
731 fstat(4, 0xEFFFF508) = 0
732 fstat(3, 0xEFFFF508) = 0
733 read(3, " # T h i s f i l e w".., 8192) = 5579
734 write(4, " # T h i s f i l e w".., 5579) = 5579
735 read(3, 0xEFFFD4A0, 8192) = 0
738 chmod("LyXVC3.lyx", 0100644) = 0
739 lseek(0, 0, SEEK_CUR) = 46440
743 // Should probably have some more error checking here.
744 // Doing it this way, also makes the inodes stay the same.
745 // This is still not a very good solution, in particular we
746 // might loose the owner of the backup.
747 FileInfo finfo(fileName());
749 mode_t fmode = finfo.getMode();
750 struct utimbuf times = {
751 finfo.getAccessTime(),
752 finfo.getModificationTime() };
754 ifstream ifs(fileName().c_str());
755 ofstream ofs(s.c_str(), ios::out|ios::trunc);
760 ::chmod(s.c_str(), fmode);
762 if (::utime(s.c_str(), ×)) {
763 lyxerr << "utime error." << endl;
766 lyxerr << "LyX was not able to make "
767 "backup copy. Beware." << endl;
772 if (writeFile(fileName())) {
774 removeAutosaveFile(fileName());
776 // Saving failed, so backup is not backup
777 if (lyxrc.make_backup) {
778 rename(s, fileName());
786 bool Buffer::writeFile(string const & fname) const
788 if (pimpl_->read_only && (fname == fileName())) {
792 FileInfo finfo(fname);
793 if (finfo.exist() && !finfo.writable()) {
799 if (params().compressed) {
800 gz::ogzstream ofs(fname.c_str());
805 retval = do_writeFile(ofs);
808 ofstream ofs(fname.c_str());
812 retval = do_writeFile(ofs);
819 bool Buffer::do_writeFile(ostream & ofs) const
823 // Use the standard "C" locale for file output.
824 ofs.imbue(std::locale::classic());
827 // The top of the file should not be written by params().
829 // write out a comment in the top of the file
830 ofs << "#LyX " << lyx_version
831 << " created this file. For more info see http://www.lyx.org/\n"
832 << "\\lyxformat " << LYX_FORMAT << "\n";
834 // now write out the buffer paramters.
835 params().writeFile(ofs);
837 ofs << "\\end_header\n";
839 Paragraph::depth_type depth = 0;
841 // this will write out all the paragraphs
842 // using recursive descent.
843 ParagraphList::const_iterator pit = paragraphs().begin();
844 ParagraphList::const_iterator pend = paragraphs().end();
845 for (; pit != pend; ++pit)
846 pit->write(*this, ofs, params(), depth);
848 // Write marker that shows file is complete
849 ofs << "\n\\end_document" << endl;
851 // Shouldn't really be needed....
854 // how to check if close went ok?
855 // Following is an attempt... (BE 20001011)
857 // good() returns false if any error occured, including some
859 // bad() returns true if something bad happened in the buffer,
860 // which should include file system full errors.
867 lyxerr << "Buffer::writeFile: BAD ERROR!" << endl;
869 lyxerr << "Buffer::writeFile: NOT SO BAD ERROR!"
879 void Buffer::makeLaTeXFile(string const & fname,
880 string const & original_path,
881 OutputParams const & runparams,
882 bool output_preamble, bool output_body)
884 lyxerr[Debug::LATEX] << "makeLaTeXFile..." << endl;
887 if (!openFileWrite(ofs, fname))
890 makeLaTeXFile(ofs, original_path,
891 runparams, output_preamble, output_body);
895 lyxerr << "File was not closed properly." << endl;
900 void Buffer::makeLaTeXFile(ostream & os,
901 string const & original_path,
902 OutputParams const & runparams_in,
903 bool output_preamble, bool output_body)
905 OutputParams runparams = runparams_in;
906 niceFile() = runparams.nice; // this will be used by Insetincludes.
908 // validate the buffer.
909 lyxerr[Debug::LATEX] << " Validating buffer..." << endl;
910 LaTeXFeatures features(*this, params());
912 lyxerr[Debug::LATEX] << " Buffer validation done." << endl;
915 // The starting paragraph of the coming rows is the
916 // first paragraph of the document. (Asger)
917 texrow().start(paragraphs().begin()->id(), 0);
919 if (output_preamble && runparams.nice) {
920 os << "%% LyX " << lyx_version << " created this file. "
921 "For more info, see http://www.lyx.org/.\n"
922 "%% Do not edit unless you really know what "
927 lyxerr[Debug::INFO] << "lyx header finished" << endl;
928 // There are a few differences between nice LaTeX and usual files:
929 // usual is \batchmode and has a
930 // special input@path to allow the including of figures
931 // with either \input or \includegraphics (what figinsets do).
932 // input@path is set when the actual parameter
933 // original_path is set. This is done for usual tex-file, but not
934 // for nice-latex-file. (Matthias 250696)
935 if (output_preamble) {
936 if (!runparams.nice) {
937 // code for usual, NOT nice-latex-file
938 os << "\\batchmode\n"; // changed
942 if (!original_path.empty()) {
943 string inputpath = os::external_path(original_path);
944 subst(inputpath, "~", "\\string~");
945 os << "\\makeatletter\n"
946 << "\\def\\input@path{{"
947 << inputpath << "/}}\n"
948 << "\\makeatother\n";
954 // Write the preamble
955 runparams.use_babel = params().writeLaTeX(os, features, texrow());
961 os << "\\begin{document}\n";
964 lyxerr[Debug::INFO] << "preamble finished, now the body." << endl;
966 if (!lyxrc.language_auto_begin) {
967 os << subst(lyxrc.language_command_begin, "$$lang",
968 params().language->babel())
973 latexParagraphs(*this, paragraphs(), os, texrow(), runparams);
975 // add this just in case after all the paragraphs
979 if (!lyxrc.language_auto_end) {
980 os << subst(lyxrc.language_command_end, "$$lang",
981 params().language->babel())
986 if (output_preamble) {
987 os << "\\end{document}\n";
990 lyxerr[Debug::LATEX] << "makeLaTeXFile...done" << endl;
992 lyxerr[Debug::LATEX] << "LaTeXFile for inclusion made."
996 // Just to be sure. (Asger)
999 lyxerr[Debug::INFO] << "Finished making LaTeX file." << endl;
1000 lyxerr[Debug::INFO] << "Row count was " << texrow().rows() - 1
1003 // we want this to be true outside previews (for insetexternal)
1008 bool Buffer::isLatex() const
1010 return params().getLyXTextClass().outputType() == LATEX;
1014 bool Buffer::isLinuxDoc() const
1016 return params().getLyXTextClass().outputType() == LINUXDOC;
1020 bool Buffer::isLiterate() const
1022 return params().getLyXTextClass().outputType() == LITERATE;
1026 bool Buffer::isDocBook() const
1028 return params().getLyXTextClass().outputType() == DOCBOOK;
1032 bool Buffer::isSGML() const
1034 LyXTextClass const & tclass = params().getLyXTextClass();
1036 return tclass.outputType() == LINUXDOC ||
1037 tclass.outputType() == DOCBOOK;
1041 void Buffer::makeLinuxDocFile(string const & fname,
1042 OutputParams const & runparams,
1046 if (!openFileWrite(ofs, fname))
1049 niceFile() = runparams.nice; // this will be used by included files.
1051 LaTeXFeatures features(*this, params());
1057 LyXTextClass const & tclass = params().getLyXTextClass();
1059 string top_element = tclass.latexname();
1062 ofs << "<!doctype linuxdoc system";
1064 string preamble = params().preamble;
1065 string const name = runparams.nice ? ChangeExtension(pimpl_->filename, ".sgml")
1067 preamble += features.getIncludedFiles(name);
1068 preamble += features.getLyXSGMLEntities();
1070 if (!preamble.empty()) {
1071 ofs << " [ " << preamble << " ]";
1075 if (params().options.empty())
1076 sgml::openTag(ofs, 0, false, top_element);
1078 string top = top_element;
1080 top += params().options;
1081 sgml::openTag(ofs, 0, false, top);
1085 ofs << "<!-- LyX " << lyx_version
1086 << " created this file. For more info see http://www.lyx.org/"
1089 linuxdocParagraphs(*this, paragraphs(), ofs, runparams);
1093 sgml::closeTag(ofs, 0, false, top_element);
1097 // How to check for successful close
1099 // we want this to be true outside previews (for insetexternal)
1104 void Buffer::makeDocBookFile(string const & fname,
1105 OutputParams const & runparams,
1109 if (!openFileWrite(ofs, fname))
1112 niceFile() = runparams.nice; // this will be used by Insetincludes.
1114 LaTeXFeatures features(*this, params());
1119 LyXTextClass const & tclass = params().getLyXTextClass();
1120 string top_element = tclass.latexname();
1123 ofs << "<!DOCTYPE " << top_element
1124 << " PUBLIC \"-//OASIS//DTD DocBook V4.1//EN\"";
1126 string preamble = params().preamble;
1127 string const name = runparams.nice ? ChangeExtension(pimpl_->filename, ".sgml")
1129 preamble += features.getIncludedFiles(name);
1130 preamble += features.getLyXSGMLEntities();
1132 if (!preamble.empty()) {
1133 ofs << "\n [ " << preamble << " ]";
1138 string top = top_element;
1140 top += params().language->code();
1143 if (!params().options.empty()) {
1145 top += params().options;
1147 sgml::openTag(ofs, 0, false, top);
1149 ofs << "<!-- DocBook file was created by LyX " << lyx_version
1150 << "\n See http://www.lyx.org/ for more information -->\n";
1152 params().getLyXTextClass().counters().reset();
1153 docbookParagraphs(*this, paragraphs(), ofs, runparams);
1156 sgml::closeTag(ofs, 0, false, top_element);
1159 // How to check for successful close
1161 // we want this to be true outside previews (for insetexternal)
1166 // chktex should be run with these flags disabled: 3, 22, 25, 30, 38(?)
1167 // Other flags: -wall -v0 -x
1168 int Buffer::runChktex()
1172 // get LaTeX-Filename
1173 string const name = getLatexName();
1174 string path = filePath();
1176 string const org_path = path;
1177 if (lyxrc.use_tempdir || !IsDirWriteable(path)) {
1181 Path p(path); // path to LaTeX file
1182 message(_("Running chktex..."));
1184 // Generate the LaTeX file if neccessary
1185 OutputParams runparams;
1186 runparams.flavor = OutputParams::LATEX;
1187 runparams.nice = false;
1188 makeLaTeXFile(name, org_path, runparams);
1191 Chktex chktex(lyxrc.chktex_command, name, filePath());
1192 int res = chktex.run(terr); // run chktex
1195 Alert::error(_("chktex failure"),
1196 _("Could not run chktex successfully."));
1197 } else if (res > 0) {
1198 // Insert all errors as errors boxes
1199 bufferErrors(*this, terr);
1208 void Buffer::validate(LaTeXFeatures & features) const
1210 LyXTextClass const & tclass = params().getLyXTextClass();
1212 if (params().tracking_changes) {
1213 features.require("dvipost");
1214 features.require("color");
1217 // AMS Style is at document level
1218 if (params().use_amsmath == BufferParams::AMS_ON
1219 || tclass.provides(LyXTextClass::amsmath))
1220 features.require("amsmath");
1222 for_each(paragraphs().begin(), paragraphs().end(),
1223 boost::bind(&Paragraph::validate, _1, boost::ref(features)));
1225 // the bullet shapes are buffer level not paragraph level
1226 // so they are tested here
1227 for (int i = 0; i < 4; ++i) {
1228 if (params().user_defined_bullet(i) != ITEMIZE_DEFAULTS[i]) {
1229 int const font = params().user_defined_bullet(i).getFont();
1231 int const c = params()
1232 .user_defined_bullet(i)
1239 features.require("latexsym");
1241 } else if (font == 1) {
1242 features.require("amssymb");
1243 } else if ((font >= 2 && font <= 5)) {
1244 features.require("pifont");
1249 if (lyxerr.debugging(Debug::LATEX)) {
1250 features.showStruct();
1255 void Buffer::getLabelList(std::vector<string> & list) const
1257 /// if this is a child document and the parent is already loaded
1258 /// Use the parent's list instead [ale990407]
1259 if (!params().parentname.empty()
1260 && bufferlist.exists(params().parentname)) {
1261 Buffer const * tmp = bufferlist.getBuffer(params().parentname);
1263 tmp->getLabelList(list);
1268 for (inset_iterator it = inset_const_iterator_begin();
1269 it != inset_const_iterator_end(); ++it) {
1270 it->getLabelList(*this, list);
1275 // This is also a buffer property (ale)
1276 void Buffer::fillWithBibKeys(std::vector<std::pair<string, string> > & keys) const
1278 /// if this is a child document and the parent is already loaded
1279 /// use the parent's list instead [ale990412]
1280 if (!params().parentname.empty() &&
1281 bufferlist.exists(params().parentname)) {
1282 Buffer const * tmp = bufferlist.getBuffer(params().parentname);
1284 tmp->fillWithBibKeys(keys);
1289 for (inset_iterator it = inset_const_iterator_begin();
1290 it != inset_const_iterator_end(); ++it) {
1291 if (it->lyxCode() == InsetOld::BIBTEX_CODE) {
1292 InsetBibtex const & inset =
1293 dynamic_cast<InsetBibtex const &>(*it);
1294 inset.fillWithBibKeys(*this, keys);
1295 } else if (it->lyxCode() == InsetOld::INCLUDE_CODE) {
1296 InsetInclude const & inset =
1297 dynamic_cast<InsetInclude const &>(*it);
1298 inset.fillWithBibKeys(*this, keys);
1299 } else if (it->lyxCode() == InsetOld::BIBITEM_CODE) {
1300 InsetBibitem const & inset =
1301 dynamic_cast<InsetBibitem const &>(*it);
1302 string const key = inset.getContents();
1303 string const opt = inset.getOptions();
1304 string const ref; // = pit->asString(this, false);
1305 string const info = opt + "TheBibliographyRef" + ref;
1306 keys.push_back(pair<string, string>(key, info));
1312 bool Buffer::isDepClean(string const & name) const
1314 DepClean::const_iterator it = pimpl_->dep_clean.find(name);
1315 if (it == pimpl_->dep_clean.end())
1321 void Buffer::markDepClean(string const & name)
1323 pimpl_->dep_clean[name] = true;
1327 bool Buffer::dispatch(string const & command, bool * result)
1329 return dispatch(lyxaction.lookupFunc(command), result);
1333 bool Buffer::dispatch(FuncRequest const & func, bool * result)
1335 bool dispatched = true;
1337 switch (func.action) {
1339 bool const tmp = Exporter::Export(this, func.argument, false);
1352 void Buffer::changeLanguage(Language const * from, Language const * to)
1354 lyxerr << "Changing Language!" << endl;
1356 // Take care of l10n/i18n
1359 ParIterator end = par_iterator_end();
1360 for (ParIterator it = par_iterator_begin(); it != end; ++it)
1361 it->changeLanguage(params(), from, to);
1365 void Buffer::updateDocLang(Language const * nlang)
1367 pimpl_->messages.reset(new Messages(nlang->code()));
1371 bool Buffer::isMultiLingual()
1373 ParIterator end = par_iterator_end();
1374 for (ParIterator it = par_iterator_begin(); it != end; ++it)
1375 if (it->isMultiLingual(params()))
1382 void Buffer::inset_iterator::setParagraph()
1384 while (pit != pend) {
1385 it = pit->insetlist.begin();
1386 if (it != pit->insetlist.end())
1393 ParIterator Buffer::getParFromID(int id) const
1395 #warning FIXME: const correctness! (Andre)
1396 ParIterator it = const_cast<Buffer*>(this)->par_iterator_begin();
1397 ParIterator end = const_cast<Buffer*>(this)->par_iterator_end();
1399 #warning FIXME, perhaps this func should return a ParIterator? (Lgb)
1401 // John says this is called with id == -1 from undo
1402 lyxerr << "getParFromID(), id: " << id << endl;
1406 for (; it != end; ++it)
1414 bool Buffer::hasParWithID(int id) const
1416 ParConstIterator it = par_iterator_begin();
1417 ParConstIterator end = par_iterator_end();
1420 // John says this is called with id == -1 from undo
1421 lyxerr << "hasParWithID(), id: " << id << endl;
1425 for (; it != end; ++it)
1433 PosIterator Buffer::pos_iterator_begin()
1435 return PosIterator(¶graphs(), paragraphs().begin(), 0);
1439 PosIterator Buffer::pos_iterator_end()
1441 return PosIterator(¶graphs(), paragraphs().end(), 0);
1445 ParIterator Buffer::par_iterator_begin()
1447 return ParIterator(paragraphs().begin(), paragraphs());
1451 ParIterator Buffer::par_iterator_end()
1453 return ParIterator(paragraphs().end(), paragraphs());
1457 ParConstIterator Buffer::par_iterator_begin() const
1459 ParagraphList const & pars = paragraphs();
1460 return ParConstIterator(const_cast<ParagraphList&>(pars).begin(), pars);
1464 ParConstIterator Buffer::par_iterator_end() const
1466 ParagraphList const & pars = paragraphs();
1467 return ParConstIterator(const_cast<ParagraphList&>(pars).end(), pars);
1471 Language const * Buffer::getLanguage() const
1473 return params().language;
1477 string const Buffer::B_(string const & l10n) const
1479 if (pimpl_->messages.get()) {
1480 return pimpl_->messages->get(l10n);
1487 bool Buffer::isClean() const
1489 return pimpl_->lyx_clean;
1493 bool Buffer::isBakClean() const
1495 return pimpl_->bak_clean;
1499 void Buffer::markClean() const
1501 if (!pimpl_->lyx_clean) {
1502 pimpl_->lyx_clean = true;
1505 // if the .lyx file has been saved, we don't need an
1507 pimpl_->bak_clean = true;
1511 void Buffer::markBakClean()
1513 pimpl_->bak_clean = true;
1517 void Buffer::setUnnamed(bool flag)
1519 pimpl_->unnamed = flag;
1523 bool Buffer::isUnnamed()
1525 return pimpl_->unnamed;
1529 void Buffer::markDirty()
1531 if (pimpl_->lyx_clean) {
1532 pimpl_->lyx_clean = false;
1535 pimpl_->bak_clean = false;
1537 DepClean::iterator it = pimpl_->dep_clean.begin();
1538 DepClean::const_iterator const end = pimpl_->dep_clean.end();
1540 for (; it != end; ++it) {
1546 string const & Buffer::fileName() const
1548 return pimpl_->filename;
1552 string const & Buffer::filePath() const
1554 return pimpl_->filepath;
1558 bool Buffer::isReadonly() const
1560 return pimpl_->read_only;
1564 void Buffer::setParentName(string const & name)
1566 params().parentname = name;
1570 Buffer::inset_iterator::inset_iterator()
1575 Buffer::inset_iterator::inset_iterator(base_type p, base_type e)
1582 Buffer::inset_iterator Buffer::inset_iterator_begin()
1584 return inset_iterator(paragraphs().begin(), paragraphs().end());
1588 Buffer::inset_iterator Buffer::inset_iterator_end()
1590 return inset_iterator(paragraphs().end(), paragraphs().end());
1594 Buffer::inset_iterator Buffer::inset_const_iterator_begin() const
1596 ParagraphList & pars = const_cast<ParagraphList&>(paragraphs());
1597 return inset_iterator(pars.begin(), pars.end());
1601 Buffer::inset_iterator Buffer::inset_const_iterator_end() const
1603 ParagraphList & pars = const_cast<ParagraphList&>(paragraphs());
1604 return inset_iterator(pars.end(), pars.end());
1608 Buffer::inset_iterator & Buffer::inset_iterator::operator++()
1612 if (it == pit->insetlist.end()) {
1621 Buffer::inset_iterator Buffer::inset_iterator::operator++(int)
1623 inset_iterator tmp = *this;
1629 Buffer::inset_iterator::reference Buffer::inset_iterator::operator*()
1635 Buffer::inset_iterator::pointer Buffer::inset_iterator::operator->()
1641 ParagraphList::iterator Buffer::inset_iterator::getPar() const
1647 lyx::pos_type Buffer::inset_iterator::getPos() const
1653 bool operator==(Buffer::inset_iterator const & iter1,
1654 Buffer::inset_iterator const & iter2)
1656 return iter1.pit == iter2.pit
1657 && (iter1.pit == iter1.pend || iter1.it == iter2.it);
1661 bool operator!=(Buffer::inset_iterator const & iter1,
1662 Buffer::inset_iterator const & iter2)
1664 return !(iter1 == iter2);