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;
425 // changed to be public and have one parameter
426 // Returns false if "\end_document" is not read (Asger)
427 bool Buffer::readBody(LyXLex & lex)
429 bool the_end_read = false;
431 if (paragraphs().empty()) {
433 if (!params().getLyXTextClass().load()) {
434 string theclass = params().getLyXTextClass().name();
435 Alert::error(_("Can't load document class"), bformat(
436 "Using the default document class, because the "
437 " class %1$s could not be loaded.", theclass));
438 params().textclass = 0;
441 // We don't want to adopt the parameters from the
442 // document we insert, so read them into a temporary buffer
443 // and then discard it
445 Buffer tmpbuf("", false);
446 tmpbuf.readHeader(lex);
449 if (text().read(*this, lex))
456 // needed to insert the selection
457 void Buffer::insertStringAsLines(ParagraphList::iterator & par, pos_type & pos,
458 LyXFont const & fn, string const & str)
460 LyXLayout_ptr const & layout = par->layout();
464 par->checkInsertChar(font);
465 // insert the string, don't insert doublespace
466 bool space_inserted = true;
467 bool autobreakrows = !par->inInset() ||
468 static_cast<InsetText *>(par->inInset())->getAutoBreakRows();
469 for(string::const_iterator cit = str.begin();
470 cit != str.end(); ++cit) {
472 if (autobreakrows && (!par->empty() || par->allowEmpty())) {
473 breakParagraph(params(), paragraphs(), par, pos,
474 layout->isEnvironment());
477 space_inserted = true;
481 // do not insert consecutive spaces if !free_spacing
482 } else if ((*cit == ' ' || *cit == '\t') &&
483 space_inserted && !par->isFreeSpacing()) {
485 } else if (*cit == '\t') {
486 if (!par->isFreeSpacing()) {
487 // tabs are like spaces here
488 par->insertChar(pos, ' ', font);
490 space_inserted = true;
492 const pos_type n = 8 - pos % 8;
493 for (pos_type i = 0; i < n; ++i) {
494 par->insertChar(pos, ' ', font);
497 space_inserted = true;
499 } else if (!IsPrintable(*cit)) {
500 // Ignore unprintables
503 // just insert the character
504 par->insertChar(pos, *cit, font);
506 space_inserted = (*cit == ' ');
513 bool Buffer::readFile(string const & filename)
515 // Check if the file is compressed.
516 string const format = getExtFromContents(filename);
517 if (format == "gzip" || format == "zip" || format == "compress") {
518 params().compressed = true;
521 // remove dummy empty par
522 paragraphs().clear();
523 bool ret = readFile(filename, paragraphs().end());
525 // After we have read a file, we must ensure that the buffer
526 // language is set and used in the gui.
527 // If you know of a better place to put this, please tell me. (Lgb)
528 updateDocLang(params().language);
534 bool Buffer::readFile(string const & filename, ParagraphList::iterator pit)
537 lex.setFile(filename);
538 return readFile(lex, filename, pit);
542 bool Buffer::fully_loaded() const
544 return pimpl_->file_fully_loaded;
548 void Buffer::fully_loaded(bool value)
550 pimpl_->file_fully_loaded = value;
554 bool Buffer::readFile(LyXLex & lex, string const & filename,
555 ParagraphList::iterator pit)
557 BOOST_ASSERT(!filename.empty());
560 Alert::error(_("Document could not be read"),
561 bformat(_("%1$s could not be read."), filename));
566 string const token(lex.getString());
569 Alert::error(_("Document could not be read"),
570 bformat(_("%1$s could not be read."), filename));
574 // the first token _must_ be...
575 if (token != "\\lyxformat") {
576 lyxerr << "Token: " << token << endl;
578 Alert::error(_("Document format failure"),
579 bformat(_("%1$s is not a LyX document."),
585 string tmp_format = lex.getString();
586 //lyxerr << "LyX Format: `" << tmp_format << '\'' << endl;
587 // if present remove ".," from string.
588 string::size_type dot = tmp_format.find_first_of(".,");
589 //lyxerr << " dot found at " << dot << endl;
590 if (dot != string::npos)
591 tmp_format.erase(dot, 1);
592 int file_format = strToInt(tmp_format);
593 //lyxerr << "format: " << file_format << endl;
595 if (file_format > LYX_FORMAT) {
596 Alert::warning(_("Document format failure"),
597 bformat(_("%1$s was created with a newer"
598 " version of LyX. This is likely to"
601 } else if (file_format < LYX_FORMAT) {
602 string const tmpfile = tempName();
603 if (tmpfile.empty()) {
604 Alert::error(_("Conversion failed"),
605 bformat(_("%1$s is from an earlier"
606 " version of LyX, but a temporary"
607 " file for converting it could"
612 string command = LibFileSearch("lyx2lyx", "lyx2lyx");
613 if (command.empty()) {
614 Alert::error(_("Conversion script not found"),
615 bformat(_("%1$s is from an earlier"
616 " version of LyX, but the"
617 " conversion script lyx2lyx"
618 " could not be found."),
624 + " -o " + tmpfile + ' '
625 + QuoteName(filename);
626 lyxerr[Debug::INFO] << "Running '"
629 cmd_ret const ret = RunCommand(command);
630 if (ret.first != 0) {
631 Alert::error(_("Conversion script failed"),
632 bformat(_("%1$s is from an earlier version"
633 " of LyX, but the lyx2lyx script"
634 " failed to convert it."),
638 bool ret = readFile(tmpfile, pit);
639 // Do stuff with tmpfile name and buffer name here.
645 bool the_end = readBody(lex);
646 params().setPaperStuff();
650 if (token == "\\end_document")
654 Alert::error(_("Document format failure"),
655 bformat(_("%1$s ended unexpectedly, which means"
656 " that it is probably corrupted."),
660 pimpl_->file_fully_loaded = true;
665 // Should probably be moved to somewhere else: BufferView? LyXView?
666 bool Buffer::save() const
668 // We don't need autosaves in the immediate future. (Asger)
669 resetAutosaveTimers();
673 if (lyxrc.make_backup) {
674 s = fileName() + '~';
675 if (!lyxrc.backupdir_path.empty())
676 s = AddName(lyxrc.backupdir_path,
677 subst(os::slashify_path(s),'/','!'));
679 // Rename is the wrong way of making a backup,
680 // this is the correct way.
681 /* truss cp fil fil2:
682 lstat("LyXVC3.lyx", 0xEFFFF898) Err#2 ENOENT
683 stat("LyXVC.lyx", 0xEFFFF688) = 0
684 open("LyXVC.lyx", O_RDONLY) = 3
685 open("LyXVC3.lyx", O_WRONLY|O_CREAT|O_TRUNC, 0600) = 4
686 fstat(4, 0xEFFFF508) = 0
687 fstat(3, 0xEFFFF508) = 0
688 read(3, " # T h i s f i l e w".., 8192) = 5579
689 write(4, " # T h i s f i l e w".., 5579) = 5579
690 read(3, 0xEFFFD4A0, 8192) = 0
693 chmod("LyXVC3.lyx", 0100644) = 0
694 lseek(0, 0, SEEK_CUR) = 46440
698 // Should probably have some more error checking here.
699 // Doing it this way, also makes the inodes stay the same.
700 // This is still not a very good solution, in particular we
701 // might loose the owner of the backup.
702 FileInfo finfo(fileName());
704 mode_t fmode = finfo.getMode();
705 struct utimbuf times = {
706 finfo.getAccessTime(),
707 finfo.getModificationTime() };
709 ifstream ifs(fileName().c_str());
710 ofstream ofs(s.c_str(), ios::out|ios::trunc);
715 ::chmod(s.c_str(), fmode);
717 if (::utime(s.c_str(), ×)) {
718 lyxerr << "utime error." << endl;
721 lyxerr << "LyX was not able to make "
722 "backup copy. Beware." << endl;
727 if (writeFile(fileName())) {
729 removeAutosaveFile(fileName());
731 // Saving failed, so backup is not backup
732 if (lyxrc.make_backup)
733 rename(s, fileName());
740 bool Buffer::writeFile(string const & fname) const
742 if (pimpl_->read_only && fname == fileName())
745 FileInfo finfo(fname);
746 if (finfo.exist() && !finfo.writable())
751 if (params().compressed) {
752 gz::ogzstream ofs(fname.c_str());
756 retval = do_writeFile(ofs);
759 ofstream ofs(fname.c_str());
763 retval = do_writeFile(ofs);
770 bool Buffer::do_writeFile(ostream & ofs) const
773 // Use the standard "C" locale for file output.
774 ofs.imbue(std::locale::classic());
777 // The top of the file should not be written by params().
779 // write out a comment in the top of the file
780 ofs << "#LyX " << lyx_version
781 << " created this file. For more info see http://www.lyx.org/\n"
782 << "\\lyxformat " << LYX_FORMAT << "\n";
784 // now write out the buffer parameters.
785 params().writeFile(ofs);
787 ofs << "\\end_header\n";
790 text().write(*this, ofs);
792 // Write marker that shows file is complete
793 ofs << "\n\\end_document" << endl;
795 // Shouldn't really be needed....
798 // how to check if close went ok?
799 // Following is an attempt... (BE 20001011)
801 // good() returns false if any error occured, including some
803 // bad() returns true if something bad happened in the buffer,
804 // which should include file system full errors.
809 lyxerr << "File was not closed properly." << endl;
816 void Buffer::makeLaTeXFile(string const & fname,
817 string const & original_path,
818 OutputParams const & runparams,
819 bool output_preamble, bool output_body)
821 lyxerr[Debug::LATEX] << "makeLaTeXFile..." << endl;
824 if (!openFileWrite(ofs, fname))
827 makeLaTeXFile(ofs, original_path,
828 runparams, output_preamble, output_body);
832 lyxerr << "File '" << fname << "' was not closed properly." << endl;
836 void Buffer::makeLaTeXFile(ostream & os,
837 string const & original_path,
838 OutputParams const & runparams_in,
839 bool output_preamble, bool output_body)
841 OutputParams runparams = runparams_in;
843 // validate the buffer.
844 lyxerr[Debug::LATEX] << " Validating buffer..." << endl;
845 LaTeXFeatures features(*this, params(), runparams.nice);
847 lyxerr[Debug::LATEX] << " Buffer validation done." << endl;
850 // The starting paragraph of the coming rows is the
851 // first paragraph of the document. (Asger)
852 texrow().start(paragraphs().begin()->id(), 0);
854 if (output_preamble && runparams.nice) {
855 os << "%% LyX " << lyx_version << " created this file. "
856 "For more info, see http://www.lyx.org/.\n"
857 "%% Do not edit unless you really know what "
862 lyxerr[Debug::INFO] << "lyx header finished" << endl;
863 // There are a few differences between nice LaTeX and usual files:
864 // usual is \batchmode and has a
865 // special input@path to allow the including of figures
866 // with either \input or \includegraphics (what figinsets do).
867 // input@path is set when the actual parameter
868 // original_path is set. This is done for usual tex-file, but not
869 // for nice-latex-file. (Matthias 250696)
870 if (output_preamble) {
871 if (!runparams.nice) {
872 // code for usual, NOT nice-latex-file
873 os << "\\batchmode\n"; // changed
877 if (!original_path.empty()) {
878 string inputpath = os::external_path(original_path);
879 subst(inputpath, "~", "\\string~");
880 os << "\\makeatletter\n"
881 << "\\def\\input@path{{"
882 << inputpath << "/}}\n"
883 << "\\makeatother\n";
889 // Write the preamble
890 runparams.use_babel = params().writeLaTeX(os, features, texrow());
896 os << "\\begin{document}\n";
899 lyxerr[Debug::INFO] << "preamble finished, now the body." << endl;
901 if (!lyxrc.language_auto_begin) {
902 os << subst(lyxrc.language_command_begin, "$$lang",
903 params().language->babel())
908 latexParagraphs(*this, paragraphs(), os, texrow(), runparams);
910 // add this just in case after all the paragraphs
914 if (!lyxrc.language_auto_end) {
915 os << subst(lyxrc.language_command_end, "$$lang",
916 params().language->babel())
921 if (output_preamble) {
922 os << "\\end{document}\n";
925 lyxerr[Debug::LATEX] << "makeLaTeXFile...done" << endl;
927 lyxerr[Debug::LATEX] << "LaTeXFile for inclusion made."
931 // Just to be sure. (Asger)
934 lyxerr[Debug::INFO] << "Finished making LaTeX file." << endl;
935 lyxerr[Debug::INFO] << "Row count was " << texrow().rows() - 1
940 bool Buffer::isLatex() const
942 return params().getLyXTextClass().outputType() == LATEX;
946 bool Buffer::isLinuxDoc() const
948 return params().getLyXTextClass().outputType() == LINUXDOC;
952 bool Buffer::isLiterate() const
954 return params().getLyXTextClass().outputType() == LITERATE;
958 bool Buffer::isDocBook() const
960 return params().getLyXTextClass().outputType() == DOCBOOK;
964 bool Buffer::isSGML() const
966 LyXTextClass const & tclass = params().getLyXTextClass();
968 return tclass.outputType() == LINUXDOC ||
969 tclass.outputType() == DOCBOOK;
973 void Buffer::makeLinuxDocFile(string const & fname,
974 OutputParams const & runparams,
978 if (!openFileWrite(ofs, fname))
981 LaTeXFeatures features(*this, params(), runparams.nice);
986 LyXTextClass const & tclass = params().getLyXTextClass();
988 string top_element = tclass.latexname();
991 ofs << tclass.class_header();
993 string preamble = params().preamble;
994 string const name = runparams.nice ? ChangeExtension(pimpl_->filename, ".sgml")
996 preamble += features.getIncludedFiles(name);
997 preamble += features.getLyXSGMLEntities();
999 if (!preamble.empty()) {
1000 ofs << " [ " << preamble << " ]";
1004 if (params().options.empty())
1005 sgml::openTag(ofs, 0, false, top_element);
1007 string top = top_element;
1009 top += params().options;
1010 sgml::openTag(ofs, 0, false, top);
1014 ofs << "<!-- LyX " << lyx_version
1015 << " created this file. For more info see http://www.lyx.org/"
1018 linuxdocParagraphs(*this, paragraphs(), ofs, runparams);
1022 sgml::closeTag(ofs, 0, false, top_element);
1027 lyxerr << "File '" << fname << "' was not closed properly." << endl;
1031 void Buffer::makeDocBookFile(string const & fname,
1032 OutputParams const & runparams,
1036 if (!openFileWrite(ofs, fname))
1039 LaTeXFeatures features(*this, params(), runparams.nice);
1044 LyXTextClass const & tclass = params().getLyXTextClass();
1045 string top_element = tclass.latexname();
1048 ofs << subst(tclass.class_header(), "#", top_element);
1050 string preamble = params().preamble;
1051 string const name = runparams.nice ? ChangeExtension(pimpl_->filename, ".sgml")
1053 preamble += features.getIncludedFiles(name);
1054 preamble += features.getLyXSGMLEntities();
1056 if (!preamble.empty()) {
1057 ofs << "\n [ " << preamble << " ]";
1062 string top = top_element;
1064 top += params().language->code();
1067 if (!params().options.empty()) {
1069 top += params().options;
1071 sgml::openTag(ofs, 0, false, top);
1073 ofs << "<!-- SGML/XML file was created by LyX " << lyx_version
1074 << "\n See http://www.lyx.org/ for more information -->\n";
1076 params().getLyXTextClass().counters().reset();
1077 docbookParagraphs(*this, paragraphs(), ofs, runparams);
1080 sgml::closeTag(ofs, 0, false, top_element);
1084 lyxerr << "File '" << fname << "' was not closed properly." << endl;
1088 // chktex should be run with these flags disabled: 3, 22, 25, 30, 38(?)
1089 // Other flags: -wall -v0 -x
1090 int Buffer::runChktex()
1094 // get LaTeX-Filename
1095 string const name = getLatexName();
1096 string const path = temppath();
1097 string const org_path = filePath();
1099 Path p(path); // path to LaTeX file
1100 message(_("Running chktex..."));
1102 // Generate the LaTeX file if neccessary
1103 OutputParams runparams;
1104 runparams.flavor = OutputParams::LATEX;
1105 runparams.nice = false;
1106 makeLaTeXFile(name, org_path, runparams);
1109 Chktex chktex(lyxrc.chktex_command, name, filePath());
1110 int res = chktex.run(terr); // run chktex
1113 Alert::error(_("chktex failure"),
1114 _("Could not run chktex successfully."));
1115 } else if (res > 0) {
1116 // Insert all errors as errors boxes
1117 bufferErrors(*this, terr);
1126 void Buffer::validate(LaTeXFeatures & features) const
1128 LyXTextClass const & tclass = params().getLyXTextClass();
1130 if (params().tracking_changes) {
1131 features.require("dvipost");
1132 features.require("color");
1135 // AMS Style is at document level
1136 if (params().use_amsmath == BufferParams::AMS_ON
1137 || tclass.provides(LyXTextClass::amsmath))
1138 features.require("amsmath");
1140 for_each(paragraphs().begin(), paragraphs().end(),
1141 boost::bind(&Paragraph::validate, _1, boost::ref(features)));
1143 // the bullet shapes are buffer level not paragraph level
1144 // so they are tested here
1145 for (int i = 0; i < 4; ++i) {
1146 if (params().user_defined_bullet(i) != ITEMIZE_DEFAULTS[i]) {
1147 int const font = params().user_defined_bullet(i).getFont();
1149 int const c = params()
1150 .user_defined_bullet(i)
1157 features.require("latexsym");
1159 } else if (font == 1) {
1160 features.require("amssymb");
1161 } else if ((font >= 2 && font <= 5)) {
1162 features.require("pifont");
1167 if (lyxerr.debugging(Debug::LATEX)) {
1168 features.showStruct();
1173 void Buffer::getLabelList(std::vector<string> & list) const
1175 /// if this is a child document and the parent is already loaded
1176 /// Use the parent's list instead [ale990407]
1177 if (!params().parentname.empty()
1178 && bufferlist.exists(params().parentname)) {
1179 Buffer const * tmp = bufferlist.getBuffer(params().parentname);
1181 tmp->getLabelList(list);
1186 for (inset_iterator it = inset_const_iterator_begin();
1187 it != inset_const_iterator_end(); ++it) {
1188 it->getLabelList(*this, list);
1193 // This is also a buffer property (ale)
1194 void Buffer::fillWithBibKeys(std::vector<std::pair<string, string> > & keys)
1197 /// if this is a child document and the parent is already loaded
1198 /// use the parent's list instead [ale990412]
1199 if (!params().parentname.empty() &&
1200 bufferlist.exists(params().parentname)) {
1201 Buffer const * tmp = bufferlist.getBuffer(params().parentname);
1203 tmp->fillWithBibKeys(keys);
1208 for (inset_iterator it = inset_const_iterator_begin();
1209 it != inset_const_iterator_end(); ++it) {
1210 if (it->lyxCode() == InsetOld::BIBTEX_CODE) {
1211 InsetBibtex const & inset =
1212 dynamic_cast<InsetBibtex const &>(*it);
1213 inset.fillWithBibKeys(*this, keys);
1214 } else if (it->lyxCode() == InsetOld::INCLUDE_CODE) {
1215 InsetInclude const & inset =
1216 dynamic_cast<InsetInclude const &>(*it);
1217 inset.fillWithBibKeys(*this, keys);
1218 } else if (it->lyxCode() == InsetOld::BIBITEM_CODE) {
1219 InsetBibitem const & inset =
1220 dynamic_cast<InsetBibitem const &>(*it);
1221 string const key = inset.getContents();
1222 string const opt = inset.getOptions();
1223 string const ref; // = pit->asString(this, false);
1224 string const info = opt + "TheBibliographyRef" + ref;
1225 keys.push_back(pair<string, string>(key, info));
1231 bool Buffer::isDepClean(string const & name) const
1233 DepClean::const_iterator it = pimpl_->dep_clean.find(name);
1234 if (it == pimpl_->dep_clean.end())
1240 void Buffer::markDepClean(string const & name)
1242 pimpl_->dep_clean[name] = true;
1246 bool Buffer::dispatch(string const & command, bool * result)
1248 return dispatch(lyxaction.lookupFunc(command), result);
1252 bool Buffer::dispatch(FuncRequest const & func, bool * result)
1254 bool dispatched = true;
1256 switch (func.action) {
1258 bool const tmp = Exporter::Export(this, func.argument, false);
1271 void Buffer::changeLanguage(Language const * from, Language const * to)
1273 lyxerr << "Changing Language!" << endl;
1275 // Take care of l10n/i18n
1278 ParIterator end = par_iterator_end();
1279 for (ParIterator it = par_iterator_begin(); it != end; ++it)
1280 it->changeLanguage(params(), from, to);
1284 void Buffer::updateDocLang(Language const * nlang)
1286 pimpl_->messages.reset(new Messages(nlang->code()));
1290 bool Buffer::isMultiLingual() const
1292 ParConstIterator end = par_iterator_end();
1293 for (ParConstIterator it = par_iterator_begin(); it != end; ++it)
1294 if (it->isMultiLingual(params()))
1301 void Buffer::inset_iterator::setParagraph()
1303 while (pit != pend) {
1304 it = pit->insetlist.begin();
1305 if (it != pit->insetlist.end())
1312 ParIterator Buffer::getParFromID(int id) const
1314 #warning FIXME: const correctness! (Andre)
1315 ParIterator it = const_cast<Buffer*>(this)->par_iterator_begin();
1316 ParIterator end = const_cast<Buffer*>(this)->par_iterator_end();
1318 #warning FIXME, perhaps this func should return a ParIterator? (Lgb)
1320 // John says this is called with id == -1 from undo
1321 lyxerr << "getParFromID(), id: " << id << endl;
1325 for (; it != end; ++it)
1333 bool Buffer::hasParWithID(int id) const
1335 ParConstIterator it = par_iterator_begin();
1336 ParConstIterator end = par_iterator_end();
1339 // John says this is called with id == -1 from undo
1340 lyxerr << "hasParWithID(), id: " << id << endl;
1344 for (; it != end; ++it)
1352 PosIterator Buffer::pos_iterator_begin()
1354 return PosIterator(¶graphs(), paragraphs().begin(), 0);
1358 PosIterator Buffer::pos_iterator_end()
1360 return PosIterator(¶graphs(), paragraphs().end(), 0);
1364 ParIterator Buffer::par_iterator_begin()
1366 return ParIterator(paragraphs().begin(), paragraphs());
1370 ParIterator Buffer::par_iterator_end()
1372 return ParIterator(paragraphs().end(), paragraphs());
1376 ParConstIterator Buffer::par_iterator_begin() const
1378 ParagraphList const & pars = paragraphs();
1379 return ParConstIterator(const_cast<ParagraphList&>(pars).begin(), pars);
1383 ParConstIterator Buffer::par_iterator_end() const
1385 ParagraphList const & pars = paragraphs();
1386 return ParConstIterator(const_cast<ParagraphList&>(pars).end(), pars);
1390 Language const * Buffer::getLanguage() const
1392 return params().language;
1396 string const Buffer::B_(string const & l10n) const
1398 if (pimpl_->messages.get()) {
1399 return pimpl_->messages->get(l10n);
1406 bool Buffer::isClean() const
1408 return pimpl_->lyx_clean;
1412 bool Buffer::isBakClean() const
1414 return pimpl_->bak_clean;
1418 void Buffer::markClean() const
1420 if (!pimpl_->lyx_clean) {
1421 pimpl_->lyx_clean = true;
1424 // if the .lyx file has been saved, we don't need an
1426 pimpl_->bak_clean = true;
1430 void Buffer::markBakClean()
1432 pimpl_->bak_clean = true;
1436 void Buffer::setUnnamed(bool flag)
1438 pimpl_->unnamed = flag;
1442 bool Buffer::isUnnamed() const
1444 return pimpl_->unnamed;
1448 void Buffer::markDirty()
1450 if (pimpl_->lyx_clean) {
1451 pimpl_->lyx_clean = false;
1454 pimpl_->bak_clean = false;
1456 DepClean::iterator it = pimpl_->dep_clean.begin();
1457 DepClean::const_iterator const end = pimpl_->dep_clean.end();
1459 for (; it != end; ++it) {
1465 string const & Buffer::fileName() const
1467 return pimpl_->filename;
1471 string const & Buffer::filePath() const
1473 return pimpl_->filepath;
1477 bool Buffer::isReadonly() const
1479 return pimpl_->read_only;
1483 void Buffer::setParentName(string const & name)
1485 params().parentname = name;
1489 Buffer::inset_iterator::inset_iterator()
1494 Buffer::inset_iterator::inset_iterator(base_type p, base_type e)
1501 Buffer::inset_iterator Buffer::inset_iterator_begin()
1503 return inset_iterator(paragraphs().begin(), paragraphs().end());
1507 Buffer::inset_iterator Buffer::inset_iterator_end()
1509 return inset_iterator(paragraphs().end(), paragraphs().end());
1513 Buffer::inset_iterator Buffer::inset_const_iterator_begin() const
1515 ParagraphList & pars = const_cast<ParagraphList&>(paragraphs());
1516 return inset_iterator(pars.begin(), pars.end());
1520 Buffer::inset_iterator Buffer::inset_const_iterator_end() const
1522 ParagraphList & pars = const_cast<ParagraphList&>(paragraphs());
1523 return inset_iterator(pars.end(), pars.end());
1527 Buffer::inset_iterator & Buffer::inset_iterator::operator++()
1531 if (it == pit->insetlist.end()) {
1540 Buffer::inset_iterator Buffer::inset_iterator::operator++(int)
1542 inset_iterator tmp = *this;
1548 Buffer::inset_iterator::reference Buffer::inset_iterator::operator*()
1554 Buffer::inset_iterator::pointer Buffer::inset_iterator::operator->()
1560 ParagraphList::iterator Buffer::inset_iterator::getPar() const
1566 lyx::pos_type Buffer::inset_iterator::getPos() const
1572 bool operator==(Buffer::inset_iterator const & iter1,
1573 Buffer::inset_iterator const & iter2)
1575 return iter1.pit == iter2.pit
1576 && (iter1.pit == iter1.pend || iter1.it == iter2.it);
1580 bool operator!=(Buffer::inset_iterator const & iter1,
1581 Buffer::inset_iterator const & iter2)
1583 return !(iter1 == iter2);