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"
40 #include "output_docbook.h"
41 #include "output_latex.h"
42 #include "output_linuxdoc.h"
43 #include "paragraph.h"
44 #include "paragraph_funcs.h"
45 #include "ParagraphParameters.h"
46 #include "PosIterator.h"
52 #include "insets/insetbibitem.h"
53 #include "insets/insetbibtex.h"
54 #include "insets/insetinclude.h"
55 #include "insets/insettext.h"
57 #include "frontends/Alert.h"
59 #include "graphics/Previews.h"
61 #include "support/FileInfo.h"
62 #include "support/filetools.h"
63 #include "support/gzstream.h"
64 #include "support/lyxlib.h"
65 #include "support/os.h"
66 #include "support/path.h"
67 #include "support/textutils.h"
68 #include "support/tostr.h"
70 #include <boost/bind.hpp>
72 #include "support/std_sstream.h"
84 using lyx::support::AddName;
85 using lyx::support::atoi;
86 using lyx::support::bformat;
87 using lyx::support::ChangeExtension;
88 using lyx::support::cmd_ret;
89 using lyx::support::createBufferTmpDir;
90 using lyx::support::destroyDir;
91 using lyx::support::FileInfo;
92 using lyx::support::FileInfo;
93 using lyx::support::getExtFromContents;
94 using lyx::support::IsDirWriteable;
95 using lyx::support::IsFileWriteable;
96 using lyx::support::LibFileSearch;
97 using lyx::support::ltrim;
98 using lyx::support::MakeAbsPath;
99 using lyx::support::MakeDisplayPath;
100 using lyx::support::MakeLatexName;
101 using lyx::support::OnlyFilename;
102 using lyx::support::OnlyPath;
103 using lyx::support::Path;
104 using lyx::support::QuoteName;
105 using lyx::support::removeAutosaveFile;
106 using lyx::support::rename;
107 using lyx::support::RunCommand;
108 using lyx::support::split;
109 using lyx::support::strToInt;
110 using lyx::support::subst;
111 using lyx::support::tempName;
112 using lyx::support::trim;
114 namespace os = lyx::support::os;
118 using std::make_pair;
123 using std::ostringstream;
131 // all these externs should eventually be removed.
132 extern BufferList bufferlist;
136 const int LYX_FORMAT = 230;
141 typedef std::map<string, bool> DepClean;
145 Impl(Buffer & parent, string const & file, bool readonly);
147 limited_stack<Undo> undostack;
148 limited_stack<Undo> redostack;
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;
183 /// our LyXText that should be wrapped in an InsetText
188 Buffer::Impl::Impl(Buffer & parent, string const & file, bool readonly_)
189 : lyx_clean(true), bak_clean(true), unnamed(false), read_only(readonly_),
190 filename(file), filepath(OnlyPath(file)), file_fully_loaded(false),
193 lyxvc.buffer(&parent);
194 temppath = createBufferTmpDir();
195 // FIXME: And now do something if temppath == string(), because we
196 // assume from now on that temppath points to a valid temp dir.
197 // See http://www.mail-archive.com/lyx-devel@lists.lyx.org/msg67406.html
201 Buffer::Buffer(string const & file, bool ronly)
202 : pimpl_(new Impl(*this, file, ronly))
204 lyxerr[Debug::INFO] << "Buffer::Buffer()" << endl;
205 lyxerr << "Buffer::Buffer()" << endl;
211 lyxerr[Debug::INFO] << "Buffer::~Buffer()" << endl;
212 // here the buffer should take care that it is
213 // saved properly, before it goes into the void.
217 if (!temppath().empty() && destroyDir(temppath()) != 0) {
218 Alert::warning(_("Could not remove temporary directory"),
219 bformat(_("Could not remove the temporary directory %1$s"), temppath()));
222 // Remove any previewed LaTeX snippets associated with this buffer.
223 lyx::graphics::Previews::get().removeLoader(*this);
227 LyXText & Buffer::text() const
229 return const_cast<LyXText &>(pimpl_->inset.text_);
233 InsetBase & Buffer::inset() const
235 return const_cast<InsetText &>(pimpl_->inset);
239 limited_stack<Undo> & Buffer::undostack()
241 return pimpl_->undostack;
245 limited_stack<Undo> const & Buffer::undostack() const
247 return pimpl_->undostack;
251 limited_stack<Undo> & Buffer::redostack()
253 return pimpl_->redostack;
257 limited_stack<Undo> const & Buffer::redostack() const
259 return pimpl_->redostack;
263 BufferParams & Buffer::params()
265 return pimpl_->params;
269 BufferParams const & Buffer::params() const
271 return pimpl_->params;
275 ParagraphList & Buffer::paragraphs()
277 return text().paragraphs();
281 ParagraphList const & Buffer::paragraphs() const
283 return text().paragraphs();
287 LyXVC & Buffer::lyxvc()
289 return pimpl_->lyxvc;
293 LyXVC const & Buffer::lyxvc() const
295 return pimpl_->lyxvc;
299 string const & Buffer::temppath() const
301 return pimpl_->temppath;
305 TexRow & Buffer::texrow()
307 return pimpl_->texrow;
311 TexRow const & Buffer::texrow() const
313 return pimpl_->texrow;
317 string const Buffer::getLatexName(bool no_path) const
319 string const name = ChangeExtension(MakeLatexName(fileName()), ".tex");
320 return no_path ? OnlyFilename(name) : name;
324 pair<Buffer::LogType, string> const Buffer::getLogName() const
326 string const filename = getLatexName(false);
328 if (filename.empty())
329 return make_pair(Buffer::latexlog, string());
331 string const path = temppath();
333 string const fname = AddName(path,
334 OnlyFilename(ChangeExtension(filename,
337 AddName(path, OnlyFilename(
338 ChangeExtension(filename,
339 formats.extension("literate") + ".out")));
341 // If no Latex log or Build log is newer, show Build log
343 FileInfo const f_fi(fname);
344 FileInfo const b_fi(bname);
347 (!f_fi.exist() || f_fi.getModificationTime() < b_fi.getModificationTime())) {
348 lyxerr[Debug::FILES] << "Log name calculated as: " << bname << endl;
349 return make_pair(Buffer::buildlog, bname);
351 lyxerr[Debug::FILES] << "Log name calculated as: " << fname << endl;
352 return make_pair(Buffer::latexlog, fname);
356 void Buffer::setReadonly(bool flag)
358 if (pimpl_->read_only != flag) {
359 pimpl_->read_only = flag;
365 void Buffer::setFileName(string const & newfile)
367 pimpl_->filename = MakeAbsPath(newfile);
368 pimpl_->filepath = OnlyPath(pimpl_->filename);
369 setReadonly(IsFileWriteable(pimpl_->filename) == 0);
374 // We'll remove this later. (Lgb)
377 void unknownClass(string const & unknown)
379 Alert::warning(_("Unknown document class"),
380 bformat(_("Using the default document class, because the "
381 "class %1$s is unknown."), unknown));
387 int Buffer::readHeader(LyXLex & lex)
389 int unknown_tokens = 0;
393 string const token = lex.getString();
398 if (token == "\\end_header")
401 lyxerr[Debug::PARSER] << "Handling header token: `"
402 << token << '\'' << endl;
405 string unknown = params().readToken(lex, token);
406 if (!unknown.empty()) {
407 if (unknown[0] != '\\') {
408 unknownClass(unknown);
411 string const s = bformat(_("Unknown token: "
415 error(ErrorItem(_("Header error"), s,
420 return unknown_tokens;
424 // candidate for move to BufferView
425 // (at least some parts in the beginning of the func)
428 // changed to be public and have one parameter
429 // if par = 0 normal behavior
430 // else insert behavior
431 // Returns false if "\end_document" is not read (Asger)
432 bool Buffer::readBody(LyXLex & lex)
434 bool the_end_read = false;
436 if (paragraphs().empty()) {
438 if (!params().getLyXTextClass().load()) {
439 string theclass = params().getLyXTextClass().name();
440 Alert::error(_("Can't load document class"), bformat(
441 "Using the default document class, because the "
442 " class %1$s could not be loaded.", theclass));
443 params().textclass = 0;
446 // We don't want to adopt the parameters from the
447 // document we insert, so read them into a temporary buffer
448 // and then discard it
450 Buffer tmpbuf("", false);
451 tmpbuf.readHeader(lex);
454 if (text().read(*this, lex))
461 // needed to insert the selection
462 void Buffer::insertStringAsLines(ParagraphList::iterator & par, pos_type & pos,
463 LyXFont const & fn, string const & str)
465 LyXLayout_ptr const & layout = par->layout();
469 par->checkInsertChar(font);
470 // insert the string, don't insert doublespace
471 bool space_inserted = true;
472 bool autobreakrows = !par->inInset() ||
473 static_cast<InsetText *>(par->inInset())->getAutoBreakRows();
474 for(string::const_iterator cit = str.begin();
475 cit != str.end(); ++cit) {
477 if (autobreakrows && (!par->empty() || par->allowEmpty())) {
478 breakParagraph(params(), paragraphs(), par, pos,
479 layout->isEnvironment());
482 space_inserted = true;
486 // do not insert consecutive spaces if !free_spacing
487 } else if ((*cit == ' ' || *cit == '\t') &&
488 space_inserted && !par->isFreeSpacing()) {
490 } else if (*cit == '\t') {
491 if (!par->isFreeSpacing()) {
492 // tabs are like spaces here
493 par->insertChar(pos, ' ', font);
495 space_inserted = true;
497 const pos_type n = 8 - pos % 8;
498 for (pos_type i = 0; i < n; ++i) {
499 par->insertChar(pos, ' ', font);
502 space_inserted = true;
504 } else if (!IsPrintable(*cit)) {
505 // Ignore unprintables
508 // just insert the character
509 par->insertChar(pos, *cit, font);
511 space_inserted = (*cit == ' ');
518 bool Buffer::readFile(string const & filename)
520 // Check if the file is compressed.
521 string const format = getExtFromContents(filename);
522 if (format == "gzip" || format == "zip" || format == "compress") {
523 params().compressed = true;
526 bool ret = readFile(filename, paragraphs().begin());
528 // After we have read a file, we must ensure that the buffer
529 // language is set and used in the gui.
530 // If you know of a better place to put this, please tell me. (Lgb)
531 updateDocLang(params().language);
537 bool Buffer::readFile(string const & filename, ParagraphList::iterator pit)
540 lex.setFile(filename);
541 return readFile(lex, filename, pit);
545 bool Buffer::fully_loaded() const
547 return pimpl_->file_fully_loaded;
551 void Buffer::fully_loaded(bool value)
553 pimpl_->file_fully_loaded = value;
557 bool Buffer::readFile(LyXLex & lex, string const & filename,
558 ParagraphList::iterator pit)
560 BOOST_ASSERT(!filename.empty());
563 Alert::error(_("Document could not be read"),
564 bformat(_("%1$s could not be read."), filename));
569 string const token(lex.getString());
572 Alert::error(_("Document could not be read"),
573 bformat(_("%1$s could not be read."), filename));
577 // the first token _must_ be...
578 if (token != "\\lyxformat") {
579 lyxerr << "Token: " << token << endl;
581 Alert::error(_("Document format failure"),
582 bformat(_("%1$s is not a LyX document."),
588 string tmp_format = lex.getString();
589 //lyxerr << "LyX Format: `" << tmp_format << '\'' << endl;
590 // if present remove ".," from string.
591 string::size_type dot = tmp_format.find_first_of(".,");
592 //lyxerr << " dot found at " << dot << endl;
593 if (dot != string::npos)
594 tmp_format.erase(dot, 1);
595 int file_format = strToInt(tmp_format);
596 //lyxerr << "format: " << file_format << endl;
598 if (file_format > LYX_FORMAT) {
599 Alert::warning(_("Document format failure"),
600 bformat(_("%1$s was created with a newer"
601 " version of LyX. This is likely to"
604 } else if (file_format < LYX_FORMAT) {
605 string const tmpfile = tempName();
606 if (tmpfile.empty()) {
607 Alert::error(_("Conversion failed"),
608 bformat(_("%1$s is from an earlier"
609 " version of LyX, but a temporary"
610 " file for converting it could"
615 string command = LibFileSearch("lyx2lyx", "lyx2lyx");
616 if (command.empty()) {
617 Alert::error(_("Conversion script not found"),
618 bformat(_("%1$s is from an earlier"
619 " version of LyX, but the"
620 " conversion script lyx2lyx"
621 " could not be found."),
627 + " -o " + tmpfile + ' '
628 + QuoteName(filename);
629 lyxerr[Debug::INFO] << "Running '"
632 cmd_ret const ret = RunCommand(command);
633 if (ret.first != 0) {
634 Alert::error(_("Conversion script failed"),
635 bformat(_("%1$s is from an earlier version"
636 " of LyX, but the lyx2lyx script"
637 " failed to convert it."),
641 bool ret = readFile(tmpfile, pit);
642 // Do stuff with tmpfile name and buffer name here.
648 bool the_end = readBody(lex);
649 params().setPaperStuff();
653 if (token == "\\end_document")
657 Alert::error(_("Document format failure"),
658 bformat(_("%1$s ended unexpectedly, which means"
659 " that it is probably corrupted."),
663 pimpl_->file_fully_loaded = true;
668 // Should probably be moved to somewhere else: BufferView? LyXView?
669 bool Buffer::save() const
671 // We don't need autosaves in the immediate future. (Asger)
672 resetAutosaveTimers();
676 if (lyxrc.make_backup) {
677 s = fileName() + '~';
678 if (!lyxrc.backupdir_path.empty())
679 s = AddName(lyxrc.backupdir_path,
680 subst(os::slashify_path(s),'/','!'));
682 // Rename is the wrong way of making a backup,
683 // this is the correct way.
684 /* truss cp fil fil2:
685 lstat("LyXVC3.lyx", 0xEFFFF898) Err#2 ENOENT
686 stat("LyXVC.lyx", 0xEFFFF688) = 0
687 open("LyXVC.lyx", O_RDONLY) = 3
688 open("LyXVC3.lyx", O_WRONLY|O_CREAT|O_TRUNC, 0600) = 4
689 fstat(4, 0xEFFFF508) = 0
690 fstat(3, 0xEFFFF508) = 0
691 read(3, " # T h i s f i l e w".., 8192) = 5579
692 write(4, " # T h i s f i l e w".., 5579) = 5579
693 read(3, 0xEFFFD4A0, 8192) = 0
696 chmod("LyXVC3.lyx", 0100644) = 0
697 lseek(0, 0, SEEK_CUR) = 46440
701 // Should probably have some more error checking here.
702 // Doing it this way, also makes the inodes stay the same.
703 // This is still not a very good solution, in particular we
704 // might loose the owner of the backup.
705 FileInfo finfo(fileName());
707 mode_t fmode = finfo.getMode();
708 struct utimbuf times = {
709 finfo.getAccessTime(),
710 finfo.getModificationTime() };
712 ifstream ifs(fileName().c_str());
713 ofstream ofs(s.c_str(), ios::out|ios::trunc);
718 ::chmod(s.c_str(), fmode);
720 if (::utime(s.c_str(), ×)) {
721 lyxerr << "utime error." << endl;
724 lyxerr << "LyX was not able to make "
725 "backup copy. Beware." << endl;
730 if (writeFile(fileName())) {
732 removeAutosaveFile(fileName());
734 // Saving failed, so backup is not backup
735 if (lyxrc.make_backup)
736 rename(s, fileName());
743 bool Buffer::writeFile(string const & fname) const
745 if (pimpl_->read_only && fname == fileName())
748 FileInfo finfo(fname);
749 if (finfo.exist() && !finfo.writable())
754 if (params().compressed) {
755 gz::ogzstream ofs(fname.c_str());
759 retval = do_writeFile(ofs);
762 ofstream ofs(fname.c_str());
766 retval = do_writeFile(ofs);
773 bool Buffer::do_writeFile(ostream & ofs) const
776 // Use the standard "C" locale for file output.
777 ofs.imbue(std::locale::classic());
780 // The top of the file should not be written by params().
782 // write out a comment in the top of the file
783 ofs << "#LyX " << lyx_version
784 << " created this file. For more info see http://www.lyx.org/\n"
785 << "\\lyxformat " << LYX_FORMAT << "\n";
787 // now write out the buffer parameters.
788 params().writeFile(ofs);
790 ofs << "\\end_header\n";
793 text().write(*this, ofs);
795 // Write marker that shows file is complete
796 ofs << "\n\\end_document" << endl;
798 // Shouldn't really be needed....
801 // how to check if close went ok?
802 // Following is an attempt... (BE 20001011)
804 // good() returns false if any error occured, including some
806 // bad() returns true if something bad happened in the buffer,
807 // which should include file system full errors.
812 lyxerr << "File was not closed properly." << endl;
819 void Buffer::makeLaTeXFile(string const & fname,
820 string const & original_path,
821 OutputParams const & runparams,
822 bool output_preamble, bool output_body)
824 lyxerr[Debug::LATEX] << "makeLaTeXFile..." << endl;
827 if (!openFileWrite(ofs, fname))
830 makeLaTeXFile(ofs, original_path,
831 runparams, output_preamble, output_body);
835 lyxerr << "File '" << fname << "' was not closed properly." << endl;
839 void Buffer::makeLaTeXFile(ostream & os,
840 string const & original_path,
841 OutputParams const & runparams_in,
842 bool output_preamble, bool output_body)
844 OutputParams runparams = runparams_in;
846 // validate the buffer.
847 lyxerr[Debug::LATEX] << " Validating buffer..." << endl;
848 LaTeXFeatures features(*this, params(), runparams.nice);
850 lyxerr[Debug::LATEX] << " Buffer validation done." << endl;
853 // The starting paragraph of the coming rows is the
854 // first paragraph of the document. (Asger)
855 texrow().start(paragraphs().begin()->id(), 0);
857 if (output_preamble && runparams.nice) {
858 os << "%% LyX " << lyx_version << " created this file. "
859 "For more info, see http://www.lyx.org/.\n"
860 "%% Do not edit unless you really know what "
865 lyxerr[Debug::INFO] << "lyx header finished" << endl;
866 // There are a few differences between nice LaTeX and usual files:
867 // usual is \batchmode and has a
868 // special input@path to allow the including of figures
869 // with either \input or \includegraphics (what figinsets do).
870 // input@path is set when the actual parameter
871 // original_path is set. This is done for usual tex-file, but not
872 // for nice-latex-file. (Matthias 250696)
873 if (output_preamble) {
874 if (!runparams.nice) {
875 // code for usual, NOT nice-latex-file
876 os << "\\batchmode\n"; // changed
880 if (!original_path.empty()) {
881 string inputpath = os::external_path(original_path);
882 subst(inputpath, "~", "\\string~");
883 os << "\\makeatletter\n"
884 << "\\def\\input@path{{"
885 << inputpath << "/}}\n"
886 << "\\makeatother\n";
892 // Write the preamble
893 runparams.use_babel = params().writeLaTeX(os, features, texrow());
899 os << "\\begin{document}\n";
902 lyxerr[Debug::INFO] << "preamble finished, now the body." << endl;
904 if (!lyxrc.language_auto_begin) {
905 os << subst(lyxrc.language_command_begin, "$$lang",
906 params().language->babel())
911 latexParagraphs(*this, paragraphs(), os, texrow(), runparams);
913 // add this just in case after all the paragraphs
917 if (!lyxrc.language_auto_end) {
918 os << subst(lyxrc.language_command_end, "$$lang",
919 params().language->babel())
924 if (output_preamble) {
925 os << "\\end{document}\n";
928 lyxerr[Debug::LATEX] << "makeLaTeXFile...done" << endl;
930 lyxerr[Debug::LATEX] << "LaTeXFile for inclusion made."
934 // Just to be sure. (Asger)
937 lyxerr[Debug::INFO] << "Finished making LaTeX file." << endl;
938 lyxerr[Debug::INFO] << "Row count was " << texrow().rows() - 1
943 bool Buffer::isLatex() const
945 return params().getLyXTextClass().outputType() == LATEX;
949 bool Buffer::isLinuxDoc() const
951 return params().getLyXTextClass().outputType() == LINUXDOC;
955 bool Buffer::isLiterate() const
957 return params().getLyXTextClass().outputType() == LITERATE;
961 bool Buffer::isDocBook() const
963 return params().getLyXTextClass().outputType() == DOCBOOK;
967 bool Buffer::isSGML() const
969 LyXTextClass const & tclass = params().getLyXTextClass();
971 return tclass.outputType() == LINUXDOC ||
972 tclass.outputType() == DOCBOOK;
976 void Buffer::makeLinuxDocFile(string const & fname,
977 OutputParams const & runparams,
981 if (!openFileWrite(ofs, fname))
984 LaTeXFeatures features(*this, params(), runparams.nice);
989 LyXTextClass const & tclass = params().getLyXTextClass();
991 string top_element = tclass.latexname();
994 ofs << tclass.class_header();
996 string preamble = params().preamble;
997 string const name = runparams.nice ? ChangeExtension(pimpl_->filename, ".sgml")
999 preamble += features.getIncludedFiles(name);
1000 preamble += features.getLyXSGMLEntities();
1002 if (!preamble.empty()) {
1003 ofs << " [ " << preamble << " ]";
1007 if (params().options.empty())
1008 sgml::openTag(ofs, 0, false, top_element);
1010 string top = top_element;
1012 top += params().options;
1013 sgml::openTag(ofs, 0, false, top);
1017 ofs << "<!-- LyX " << lyx_version
1018 << " created this file. For more info see http://www.lyx.org/"
1021 linuxdocParagraphs(*this, paragraphs(), ofs, runparams);
1025 sgml::closeTag(ofs, 0, false, top_element);
1030 lyxerr << "File '" << fname << "' was not closed properly." << endl;
1034 void Buffer::makeDocBookFile(string const & fname,
1035 OutputParams const & runparams,
1039 if (!openFileWrite(ofs, fname))
1042 LaTeXFeatures features(*this, params(), runparams.nice);
1047 LyXTextClass const & tclass = params().getLyXTextClass();
1048 string top_element = tclass.latexname();
1051 ofs << subst(tclass.class_header(), "#", top_element);
1053 string preamble = params().preamble;
1054 string const name = runparams.nice ? ChangeExtension(pimpl_->filename, ".sgml")
1056 preamble += features.getIncludedFiles(name);
1057 preamble += features.getLyXSGMLEntities();
1059 if (!preamble.empty()) {
1060 ofs << "\n [ " << preamble << " ]";
1065 string top = top_element;
1067 top += params().language->code();
1070 if (!params().options.empty()) {
1072 top += params().options;
1074 sgml::openTag(ofs, 0, false, top);
1076 ofs << "<!-- SGML/XML file was created by LyX " << lyx_version
1077 << "\n See http://www.lyx.org/ for more information -->\n";
1079 params().getLyXTextClass().counters().reset();
1080 docbookParagraphs(*this, paragraphs(), ofs, runparams);
1083 sgml::closeTag(ofs, 0, false, top_element);
1087 lyxerr << "File '" << fname << "' was not closed properly." << endl;
1091 // chktex should be run with these flags disabled: 3, 22, 25, 30, 38(?)
1092 // Other flags: -wall -v0 -x
1093 int Buffer::runChktex()
1097 // get LaTeX-Filename
1098 string const name = getLatexName();
1099 string const path = temppath();
1100 string const org_path = filePath();
1102 Path p(path); // path to LaTeX file
1103 message(_("Running chktex..."));
1105 // Generate the LaTeX file if neccessary
1106 OutputParams runparams;
1107 runparams.flavor = OutputParams::LATEX;
1108 runparams.nice = false;
1109 makeLaTeXFile(name, org_path, runparams);
1112 Chktex chktex(lyxrc.chktex_command, name, filePath());
1113 int res = chktex.run(terr); // run chktex
1116 Alert::error(_("chktex failure"),
1117 _("Could not run chktex successfully."));
1118 } else if (res > 0) {
1119 // Insert all errors as errors boxes
1120 bufferErrors(*this, terr);
1129 void Buffer::validate(LaTeXFeatures & features) const
1131 LyXTextClass const & tclass = params().getLyXTextClass();
1133 if (params().tracking_changes) {
1134 features.require("dvipost");
1135 features.require("color");
1138 // AMS Style is at document level
1139 if (params().use_amsmath == BufferParams::AMS_ON
1140 || tclass.provides(LyXTextClass::amsmath))
1141 features.require("amsmath");
1143 for_each(paragraphs().begin(), paragraphs().end(),
1144 boost::bind(&Paragraph::validate, _1, boost::ref(features)));
1146 // the bullet shapes are buffer level not paragraph level
1147 // so they are tested here
1148 for (int i = 0; i < 4; ++i) {
1149 if (params().user_defined_bullet(i) != ITEMIZE_DEFAULTS[i]) {
1150 int const font = params().user_defined_bullet(i).getFont();
1152 int const c = params()
1153 .user_defined_bullet(i)
1160 features.require("latexsym");
1162 } else if (font == 1) {
1163 features.require("amssymb");
1164 } else if ((font >= 2 && font <= 5)) {
1165 features.require("pifont");
1170 if (lyxerr.debugging(Debug::LATEX)) {
1171 features.showStruct();
1176 void Buffer::getLabelList(std::vector<string> & list) const
1178 /// if this is a child document and the parent is already loaded
1179 /// Use the parent's list instead [ale990407]
1180 if (!params().parentname.empty()
1181 && bufferlist.exists(params().parentname)) {
1182 Buffer const * tmp = bufferlist.getBuffer(params().parentname);
1184 tmp->getLabelList(list);
1189 for (inset_iterator it = inset_const_iterator_begin();
1190 it != inset_const_iterator_end(); ++it) {
1191 it->getLabelList(*this, list);
1196 // This is also a buffer property (ale)
1197 void Buffer::fillWithBibKeys(std::vector<std::pair<string, string> > & keys)
1200 /// if this is a child document and the parent is already loaded
1201 /// use the parent's list instead [ale990412]
1202 if (!params().parentname.empty() &&
1203 bufferlist.exists(params().parentname)) {
1204 Buffer const * tmp = bufferlist.getBuffer(params().parentname);
1206 tmp->fillWithBibKeys(keys);
1211 for (inset_iterator it = inset_const_iterator_begin();
1212 it != inset_const_iterator_end(); ++it) {
1213 if (it->lyxCode() == InsetOld::BIBTEX_CODE) {
1214 InsetBibtex const & inset =
1215 dynamic_cast<InsetBibtex const &>(*it);
1216 inset.fillWithBibKeys(*this, keys);
1217 } else if (it->lyxCode() == InsetOld::INCLUDE_CODE) {
1218 InsetInclude const & inset =
1219 dynamic_cast<InsetInclude const &>(*it);
1220 inset.fillWithBibKeys(*this, keys);
1221 } else if (it->lyxCode() == InsetOld::BIBITEM_CODE) {
1222 InsetBibitem const & inset =
1223 dynamic_cast<InsetBibitem const &>(*it);
1224 string const key = inset.getContents();
1225 string const opt = inset.getOptions();
1226 string const ref; // = pit->asString(this, false);
1227 string const info = opt + "TheBibliographyRef" + ref;
1228 keys.push_back(pair<string, string>(key, info));
1234 bool Buffer::isDepClean(string const & name) const
1236 DepClean::const_iterator it = pimpl_->dep_clean.find(name);
1237 if (it == pimpl_->dep_clean.end())
1243 void Buffer::markDepClean(string const & name)
1245 pimpl_->dep_clean[name] = true;
1249 bool Buffer::dispatch(string const & command, bool * result)
1251 return dispatch(lyxaction.lookupFunc(command), result);
1255 bool Buffer::dispatch(FuncRequest const & func, bool * result)
1257 bool dispatched = true;
1259 switch (func.action) {
1261 bool const tmp = Exporter::Export(this, func.argument, false);
1274 void Buffer::changeLanguage(Language const * from, Language const * to)
1276 lyxerr << "Changing Language!" << endl;
1278 // Take care of l10n/i18n
1281 ParIterator end = par_iterator_end();
1282 for (ParIterator it = par_iterator_begin(); it != end; ++it)
1283 it->changeLanguage(params(), from, to);
1287 void Buffer::updateDocLang(Language const * nlang)
1289 pimpl_->messages.reset(new Messages(nlang->code()));
1293 bool Buffer::isMultiLingual() const
1295 ParConstIterator end = par_iterator_end();
1296 for (ParConstIterator it = par_iterator_begin(); it != end; ++it)
1297 if (it->isMultiLingual(params()))
1304 void Buffer::inset_iterator::setParagraph()
1306 while (pit != pend) {
1307 it = pit->insetlist.begin();
1308 if (it != pit->insetlist.end())
1315 ParIterator Buffer::getParFromID(int id) const
1317 #warning FIXME: const correctness! (Andre)
1318 ParIterator it = const_cast<Buffer*>(this)->par_iterator_begin();
1319 ParIterator end = const_cast<Buffer*>(this)->par_iterator_end();
1321 #warning FIXME, perhaps this func should return a ParIterator? (Lgb)
1323 // John says this is called with id == -1 from undo
1324 lyxerr << "getParFromID(), id: " << id << endl;
1328 for (; it != end; ++it)
1336 bool Buffer::hasParWithID(int id) const
1338 ParConstIterator it = par_iterator_begin();
1339 ParConstIterator end = par_iterator_end();
1342 // John says this is called with id == -1 from undo
1343 lyxerr << "hasParWithID(), id: " << id << endl;
1347 for (; it != end; ++it)
1355 PosIterator Buffer::pos_iterator_begin()
1357 return PosIterator(¶graphs(), paragraphs().begin(), 0);
1361 PosIterator Buffer::pos_iterator_end()
1363 return PosIterator(¶graphs(), paragraphs().end(), 0);
1367 ParIterator Buffer::par_iterator_begin()
1369 return ParIterator(paragraphs().begin(), paragraphs());
1373 ParIterator Buffer::par_iterator_end()
1375 return ParIterator(paragraphs().end(), paragraphs());
1379 ParConstIterator Buffer::par_iterator_begin() const
1381 ParagraphList const & pars = paragraphs();
1382 return ParConstIterator(const_cast<ParagraphList&>(pars).begin(), pars);
1386 ParConstIterator Buffer::par_iterator_end() const
1388 ParagraphList const & pars = paragraphs();
1389 return ParConstIterator(const_cast<ParagraphList&>(pars).end(), pars);
1393 Language const * Buffer::getLanguage() const
1395 return params().language;
1399 string const Buffer::B_(string const & l10n) const
1401 if (pimpl_->messages.get()) {
1402 return pimpl_->messages->get(l10n);
1409 bool Buffer::isClean() const
1411 return pimpl_->lyx_clean;
1415 bool Buffer::isBakClean() const
1417 return pimpl_->bak_clean;
1421 void Buffer::markClean() const
1423 if (!pimpl_->lyx_clean) {
1424 pimpl_->lyx_clean = true;
1427 // if the .lyx file has been saved, we don't need an
1429 pimpl_->bak_clean = true;
1433 void Buffer::markBakClean()
1435 pimpl_->bak_clean = true;
1439 void Buffer::setUnnamed(bool flag)
1441 pimpl_->unnamed = flag;
1445 bool Buffer::isUnnamed() const
1447 return pimpl_->unnamed;
1451 void Buffer::markDirty()
1453 if (pimpl_->lyx_clean) {
1454 pimpl_->lyx_clean = false;
1457 pimpl_->bak_clean = false;
1459 DepClean::iterator it = pimpl_->dep_clean.begin();
1460 DepClean::const_iterator const end = pimpl_->dep_clean.end();
1462 for (; it != end; ++it) {
1468 string const & Buffer::fileName() const
1470 return pimpl_->filename;
1474 string const & Buffer::filePath() const
1476 return pimpl_->filepath;
1480 bool Buffer::isReadonly() const
1482 return pimpl_->read_only;
1486 void Buffer::setParentName(string const & name)
1488 params().parentname = name;
1492 Buffer::inset_iterator::inset_iterator()
1497 Buffer::inset_iterator::inset_iterator(base_type p, base_type e)
1504 Buffer::inset_iterator Buffer::inset_iterator_begin()
1506 return inset_iterator(paragraphs().begin(), paragraphs().end());
1510 Buffer::inset_iterator Buffer::inset_iterator_end()
1512 return inset_iterator(paragraphs().end(), paragraphs().end());
1516 Buffer::inset_iterator Buffer::inset_const_iterator_begin() const
1518 ParagraphList & pars = const_cast<ParagraphList&>(paragraphs());
1519 return inset_iterator(pars.begin(), pars.end());
1523 Buffer::inset_iterator Buffer::inset_const_iterator_end() const
1525 ParagraphList & pars = const_cast<ParagraphList&>(paragraphs());
1526 return inset_iterator(pars.end(), pars.end());
1530 Buffer::inset_iterator & Buffer::inset_iterator::operator++()
1534 if (it == pit->insetlist.end()) {
1543 Buffer::inset_iterator Buffer::inset_iterator::operator++(int)
1545 inset_iterator tmp = *this;
1551 Buffer::inset_iterator::reference Buffer::inset_iterator::operator*()
1557 Buffer::inset_iterator::pointer Buffer::inset_iterator::operator->()
1563 ParagraphList::iterator Buffer::inset_iterator::getPar() const
1569 lyx::pos_type Buffer::inset_iterator::getPos() const
1575 bool operator==(Buffer::inset_iterator const & iter1,
1576 Buffer::inset_iterator const & iter2)
1578 return iter1.pit == iter2.pit
1579 && (iter1.pit == iter1.pend || iter1.it == iter2.it);
1583 bool operator!=(Buffer::inset_iterator const & iter1,
1584 Buffer::inset_iterator const & iter2)
1586 return !(iter1 == iter2);