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
7 * \author Stefan Schimanski
9 * Full author contact details are available in file CREDITS.
17 #include "BiblioInfo.h"
18 #include "BranchList.h"
19 #include "buffer_funcs.h"
20 #include "BufferList.h"
21 #include "BufferParams.h"
24 #include "Converter.h"
27 #include "DocIterator.h"
28 #include "EmbeddedFiles.h"
30 #include "ErrorList.h"
33 #include "FuncRequest.h"
35 #include "InsetIterator.h"
36 #include "InsetList.h"
38 #include "LaTeXFeatures.h"
42 #include "LyXAction.h"
46 #include "output_docbook.h"
48 #include "output_latex.h"
49 #include "output_plaintext.h"
50 #include "paragraph_funcs.h"
51 #include "Paragraph.h"
52 #include "ParagraphParameters.h"
53 #include "ParIterator.h"
54 #include "PDFOptions.h"
58 #include "TexStream.h"
59 #include "TextClassList.h"
61 #include "TocBackend.h"
63 #include "VCBackend.h"
66 #include "insets/InsetBibitem.h"
67 #include "insets/InsetBibtex.h"
68 #include "insets/InsetInclude.h"
69 #include "insets/InsetText.h"
71 #include "mathed/MacroTable.h"
72 #include "mathed/MathMacroTemplate.h"
73 #include "mathed/MathSupport.h"
75 #include "frontends/alert.h"
76 #include "frontends/Delegates.h"
77 #include "frontends/WorkAreaManager.h"
78 #include "frontends/FileDialog.h"
80 #include "graphics/Previews.h"
82 #include "support/types.h"
83 #include "support/lyxalgo.h"
84 #include "support/FileFilterList.h"
85 #include "support/filetools.h"
86 #include "support/Forkedcall.h"
87 #include "support/gzstream.h"
88 #include "support/lstrings.h"
89 #include "support/lyxlib.h"
90 #include "support/os.h"
91 #include "support/Path.h"
92 #include "support/textutils.h"
93 #include "support/convert.h"
95 #if !defined (HAVE_FORK)
99 #include <boost/bind.hpp>
100 #include <boost/shared_ptr.hpp>
110 using std::make_pair;
115 using std::ostringstream;
126 using support::addName;
127 using support::bformat;
128 using support::changeExtension;
129 using support::cmd_ret;
130 using support::createBufferTmpDir;
131 using support::FileName;
132 using support::libFileSearch;
133 using support::latex_path;
134 using support::ltrim;
135 using support::makeAbsPath;
136 using support::makeDisplayPath;
137 using support::makeLatexName;
138 using support::onlyFilename;
139 using support::onlyPath;
140 using support::quoteName;
141 using support::removeAutosaveFile;
142 using support::rename;
143 using support::runCommand;
144 using support::split;
145 using support::subst;
146 using support::tempName;
149 using support::suffixIs;
151 namespace Alert = frontend::Alert;
152 namespace os = support::os;
156 int const LYX_FORMAT = 302; // Uwe: Latin and North Sami
161 typedef std::map<string, bool> DepClean;
166 Impl(Buffer & parent, FileName const & file, bool readonly);
173 /// need to regenerate .tex?
177 mutable bool lyx_clean;
179 /// is autosave needed?
180 mutable bool bak_clean;
182 /// is this a unnamed file (New...)?
188 /// name of the file the buffer is associated with.
191 /** Set to true only when the file is fully loaded.
192 * Used to prevent the premature generation of previews
193 * and by the citation inset.
195 bool file_fully_loaded;
197 /// our Text that should be wrapped in an InsetText
201 TocBackend toc_backend;
204 typedef std::map<unsigned int, MacroData, std::greater<int> > PositionToMacroMap;
205 typedef std::map<docstring, PositionToMacroMap> NameToPositionMacroMap;
206 NameToPositionMacroMap macros;
208 /// Container for all sort of Buffer dependant errors.
209 map<string, ErrorList> errorLists;
211 /// all embedded files of this buffer
212 EmbeddedFiles embedded_files;
214 /// timestamp and checksum used to test if the file has been externally
215 /// modified. (Used to properly enable 'File->Revert to saved', bug 4114).
217 unsigned long checksum_;
220 frontend::WorkAreaManager * wa_;
227 Buffer::Impl::Impl(Buffer & parent, FileName const & file, bool readonly_)
228 : lyx_clean(true), bak_clean(true), unnamed(false), read_only(readonly_),
229 filename(file), file_fully_loaded(false), inset(params),
230 toc_backend(&parent), embedded_files(&parent), timestamp_(0),
231 checksum_(0), wa_(0), undo_(parent)
233 inset.setAutoBreakRows(true);
234 lyxvc.setBuffer(&parent);
235 temppath = createBufferTmpDir();
236 params.filepath = onlyPath(file.absFilename());
237 // FIXME: And now do something if temppath == string(), because we
238 // assume from now on that temppath points to a valid temp dir.
239 // See http://www.mail-archive.com/lyx-devel@lists.lyx.org/msg67406.html
242 wa_ = new frontend::WorkAreaManager;
246 Buffer::Buffer(string const & file, bool readonly)
247 : pimpl_(new Impl(*this, FileName(file), readonly)), gui_(0)
249 LYXERR(Debug::INFO, "Buffer::Buffer()");
255 LYXERR(Debug::INFO, "Buffer::~Buffer()");
256 // here the buffer should take care that it is
257 // saved properly, before it goes into the void.
259 Buffer * master = masterBuffer();
260 if (master != this && use_gui)
261 // We are closing buf which was a child document so we
262 // must update the labels and section numbering of its master
264 updateLabels(*master);
266 if (!temppath().empty() && !FileName(temppath()).destroyDirectory()) {
267 Alert::warning(_("Could not remove temporary directory"),
268 bformat(_("Could not remove the temporary directory %1$s"),
269 from_utf8(temppath())));
272 // Remove any previewed LaTeX snippets associated with this buffer.
273 graphics::Previews::get().removeLoader(*this);
276 pimpl_->wa_->closeAll();
283 void Buffer::changed() const
286 pimpl_->wa_->redrawAll();
290 frontend::WorkAreaManager & Buffer::workAreaManager() const
292 BOOST_ASSERT(pimpl_->wa_);
297 Text & Buffer::text() const
299 return const_cast<Text &>(pimpl_->inset.text_);
303 Inset & Buffer::inset() const
305 return const_cast<InsetText &>(pimpl_->inset);
309 BufferParams & Buffer::params()
311 return pimpl_->params;
315 BufferParams const & Buffer::params() const
317 return pimpl_->params;
321 ParagraphList & Buffer::paragraphs()
323 return text().paragraphs();
327 ParagraphList const & Buffer::paragraphs() const
329 return text().paragraphs();
333 LyXVC & Buffer::lyxvc()
335 return pimpl_->lyxvc;
339 LyXVC const & Buffer::lyxvc() const
341 return pimpl_->lyxvc;
345 string const & Buffer::temppath() const
347 return pimpl_->temppath;
351 TexRow & Buffer::texrow()
353 return pimpl_->texrow;
357 TexRow const & Buffer::texrow() const
359 return pimpl_->texrow;
363 TocBackend & Buffer::tocBackend()
365 return pimpl_->toc_backend;
369 TocBackend const & Buffer::tocBackend() const
371 return pimpl_->toc_backend;
375 EmbeddedFiles & Buffer::embeddedFiles()
377 return pimpl_->embedded_files;
381 EmbeddedFiles const & Buffer::embeddedFiles() const
383 return pimpl_->embedded_files;
387 Undo & Buffer::undo()
389 return pimpl_->undo_;
393 string Buffer::latexName(bool const no_path) const
395 string const name = changeExtension(makeLatexName(absFileName()), ".tex");
396 return no_path ? onlyFilename(name) : name;
400 string Buffer::logName(LogType * type) const
402 string const filename = latexName(false);
404 if (filename.empty()) {
410 string const path = temppath();
412 FileName const fname(addName(temppath(),
413 onlyFilename(changeExtension(filename,
415 FileName const bname(
416 addName(path, onlyFilename(
417 changeExtension(filename,
418 formats.extension("literate") + ".out"))));
420 // If no Latex log or Build log is newer, show Build log
422 if (bname.exists() &&
423 (!fname.exists() || fname.lastModified() < bname.lastModified())) {
424 LYXERR(Debug::FILES, "Log name calculated as: " << bname);
427 return bname.absFilename();
429 LYXERR(Debug::FILES, "Log name calculated as: " << fname);
432 return fname.absFilename();
436 void Buffer::setReadonly(bool const flag)
438 if (pimpl_->read_only != flag) {
439 pimpl_->read_only = flag;
445 void Buffer::setFileName(string const & newfile)
447 pimpl_->filename = makeAbsPath(newfile);
448 params().filepath = onlyPath(pimpl_->filename.absFilename());
449 setReadonly(pimpl_->filename.isReadOnly());
454 int Buffer::readHeader(Lexer & lex)
456 int unknown_tokens = 0;
458 int begin_header_line = -1;
460 // Initialize parameters that may be/go lacking in header:
461 params().branchlist().clear();
462 params().preamble.erase();
463 params().options.erase();
464 params().float_placement.erase();
465 params().paperwidth.erase();
466 params().paperheight.erase();
467 params().leftmargin.erase();
468 params().rightmargin.erase();
469 params().topmargin.erase();
470 params().bottommargin.erase();
471 params().headheight.erase();
472 params().headsep.erase();
473 params().footskip.erase();
474 params().listings_params.clear();
475 params().clearLayoutModules();
476 params().pdfoptions().clear();
478 for (int i = 0; i < 4; ++i) {
479 params().user_defined_bullet(i) = ITEMIZE_DEFAULTS[i];
480 params().temp_bullet(i) = ITEMIZE_DEFAULTS[i];
483 ErrorList & errorList = pimpl_->errorLists["Parse"];
487 string const token = lex.getString();
492 if (token == "\\end_header")
496 if (token == "\\begin_header") {
497 begin_header_line = line;
501 LYXERR(Debug::PARSER, "Handling document header token: `"
504 string unknown = params().readToken(lex, token);
505 if (!unknown.empty()) {
506 if (unknown[0] != '\\' && token == "\\textclass") {
507 Alert::warning(_("Unknown document class"),
508 bformat(_("Using the default document class, because the "
509 "class %1$s is unknown."), from_utf8(unknown)));
512 docstring const s = bformat(_("Unknown token: "
516 errorList.push_back(ErrorItem(_("Document header error"),
521 if (begin_header_line) {
522 docstring const s = _("\\begin_header is missing");
523 errorList.push_back(ErrorItem(_("Document header error"),
527 return unknown_tokens;
532 // changed to be public and have one parameter
533 // Returns false if "\end_document" is not read (Asger)
534 bool Buffer::readDocument(Lexer & lex)
536 ErrorList & errorList = pimpl_->errorLists["Parse"];
540 string const token = lex.getString();
541 if (token != "\\begin_document") {
542 docstring const s = _("\\begin_document is missing");
543 errorList.push_back(ErrorItem(_("Document header error"),
547 // we are reading in a brand new document
548 BOOST_ASSERT(paragraphs().empty());
551 TextClass const & baseClass = textclasslist[params().getBaseClass()];
552 if (!baseClass.load(filePath())) {
553 string theclass = baseClass.name();
554 Alert::error(_("Can't load document class"), bformat(
555 _("Using the default document class, because the "
556 "class %1$s could not be loaded."), from_utf8(theclass)));
557 params().setBaseClass(defaultTextclass());
560 if (params().outputChanges) {
561 bool dvipost = LaTeXFeatures::isAvailable("dvipost");
562 bool xcolorsoul = LaTeXFeatures::isAvailable("soul") &&
563 LaTeXFeatures::isAvailable("xcolor");
565 if (!dvipost && !xcolorsoul) {
566 Alert::warning(_("Changes not shown in LaTeX output"),
567 _("Changes will not be highlighted in LaTeX output, "
568 "because neither dvipost nor xcolor/soul are installed.\n"
569 "Please install these packages or redefine "
570 "\\lyxadded and \\lyxdeleted in the LaTeX preamble."));
571 } else if (!xcolorsoul) {
572 Alert::warning(_("Changes not shown in LaTeX output"),
573 _("Changes will not be highlighted in LaTeX output "
574 "when using pdflatex, because xcolor and soul are not installed.\n"
575 "Please install both packages or redefine "
576 "\\lyxadded and \\lyxdeleted in the LaTeX preamble."));
581 bool const res = text().read(*this, lex, errorList);
582 for_each(text().paragraphs().begin(),
583 text().paragraphs().end(),
584 bind(&Paragraph::setInsetOwner, _1, &inset()));
590 // needed to insert the selection
591 void Buffer::insertStringAsLines(ParagraphList & pars,
592 pit_type & pit, pos_type & pos,
593 Font const & fn, docstring const & str, bool autobreakrows)
597 // insert the string, don't insert doublespace
598 bool space_inserted = true;
599 for (docstring::const_iterator cit = str.begin();
600 cit != str.end(); ++cit) {
601 Paragraph & par = pars[pit];
603 if (autobreakrows && (!par.empty() || par.allowEmpty())) {
604 breakParagraph(params(), pars, pit, pos,
605 par.layout()->isEnvironment());
608 space_inserted = true;
612 // do not insert consecutive spaces if !free_spacing
613 } else if ((*cit == ' ' || *cit == '\t') &&
614 space_inserted && !par.isFreeSpacing()) {
616 } else if (*cit == '\t') {
617 if (!par.isFreeSpacing()) {
618 // tabs are like spaces here
619 par.insertChar(pos, ' ', font, params().trackChanges);
621 space_inserted = true;
623 const pos_type n = 8 - pos % 8;
624 for (pos_type i = 0; i < n; ++i) {
625 par.insertChar(pos, ' ', font, params().trackChanges);
628 space_inserted = true;
630 } else if (!isPrintable(*cit)) {
631 // Ignore unprintables
634 // just insert the character
635 par.insertChar(pos, *cit, font, params().trackChanges);
637 space_inserted = (*cit == ' ');
644 bool Buffer::readString(std::string const & s)
646 params().compressed = false;
648 // remove dummy empty par
649 paragraphs().clear();
651 std::istringstream is(s);
653 FileName const name(tempName());
654 switch (readFile(lex, name, true)) {
658 // We need to call lyx2lyx, so write the input to a file
659 std::ofstream os(name.toFilesystemEncoding().c_str());
662 return readFile(name);
672 bool Buffer::readFile(FileName const & filename)
674 FileName fname(filename);
675 // Check if the file is compressed.
676 string format = filename.guessFormatFromContents();
677 if (format == "zip") {
678 // decompress to a temp directory
679 LYXERR(Debug::FILES, filename << " is in zip format. Unzip to " << temppath());
680 ::unzipToDir(filename.toFilesystemEncoding(), temppath());
682 FileName lyxfile(addName(temppath(), "content.lyx"));
683 // if both manifest.txt and file.lyx exist, this is am embedded file
684 if (lyxfile.exists()) {
685 params().embedded = true;
689 // The embedded lyx file can also be compressed, for backward compatibility
690 format = fname.guessFormatFromContents();
691 if (format == "gzip" || format == "zip" || format == "compress")
692 params().compressed = true;
694 // remove dummy empty par
695 paragraphs().clear();
698 if (readFile(lex, fname) != success)
705 bool Buffer::isFullyLoaded() const
707 return pimpl_->file_fully_loaded;
711 void Buffer::setFullyLoaded(bool value)
713 pimpl_->file_fully_loaded = value;
717 Buffer::ReadStatus Buffer::readFile(Lexer & lex, FileName const & filename,
720 BOOST_ASSERT(!filename.empty());
723 Alert::error(_("Document could not be read"),
724 bformat(_("%1$s could not be read."), from_utf8(filename.absFilename())));
729 string const token = lex.getString();
732 Alert::error(_("Document could not be read"),
733 bformat(_("%1$s could not be read."), from_utf8(filename.absFilename())));
737 // the first token _must_ be...
738 if (token != "\\lyxformat") {
739 lyxerr << "Token: " << token << endl;
741 Alert::error(_("Document format failure"),
742 bformat(_("%1$s is not a LyX document."),
743 from_utf8(filename.absFilename())));
748 string tmp_format = lex.getString();
749 //lyxerr << "LyX Format: `" << tmp_format << '\'' << endl;
750 // if present remove ".," from string.
751 string::size_type dot = tmp_format.find_first_of(".,");
752 //lyxerr << " dot found at " << dot << endl;
753 if (dot != string::npos)
754 tmp_format.erase(dot, 1);
755 int const file_format = convert<int>(tmp_format);
756 //lyxerr << "format: " << file_format << endl;
758 // save timestamp and checksum of the original disk file, making sure
759 // to not overwrite them with those of the file created in the tempdir
760 // when it has to be converted to the current format.
761 if (!pimpl_->checksum_) {
762 // Save the timestamp and checksum of disk file. If filename is an
763 // emergency file, save the timestamp and sum of the original lyx file
764 // because isExternallyModified will check for this file. (BUG4193)
765 string diskfile = filename.absFilename();
766 if (suffixIs(diskfile, ".emergency"))
767 diskfile = diskfile.substr(0, diskfile.size() - 10);
768 saveCheckSum(FileName(diskfile));
771 if (file_format != LYX_FORMAT) {
774 // lyx2lyx would fail
777 FileName const tmpfile(tempName());
778 if (tmpfile.empty()) {
779 Alert::error(_("Conversion failed"),
780 bformat(_("%1$s is from a different"
781 " version of LyX, but a temporary"
782 " file for converting it could"
784 from_utf8(filename.absFilename())));
787 FileName const lyx2lyx = libFileSearch("lyx2lyx", "lyx2lyx");
788 if (lyx2lyx.empty()) {
789 Alert::error(_("Conversion script not found"),
790 bformat(_("%1$s is from a different"
791 " version of LyX, but the"
792 " conversion script lyx2lyx"
793 " could not be found."),
794 from_utf8(filename.absFilename())));
797 ostringstream command;
798 command << os::python()
799 << ' ' << quoteName(lyx2lyx.toFilesystemEncoding())
800 << " -t " << convert<string>(LYX_FORMAT)
801 << " -o " << quoteName(tmpfile.toFilesystemEncoding())
802 << ' ' << quoteName(filename.toFilesystemEncoding());
803 string const command_str = command.str();
805 LYXERR(Debug::INFO, "Running '" << command_str << '\'');
807 cmd_ret const ret = runCommand(command_str);
808 if (ret.first != 0) {
809 Alert::error(_("Conversion script failed"),
810 bformat(_("%1$s is from a different version"
811 " of LyX, but the lyx2lyx script"
812 " failed to convert it."),
813 from_utf8(filename.absFilename())));
816 bool const ret = readFile(tmpfile);
817 // Do stuff with tmpfile name and buffer name here.
818 return ret ? success : failure;
823 if (readDocument(lex)) {
824 Alert::error(_("Document format failure"),
825 bformat(_("%1$s ended unexpectedly, which means"
826 " that it is probably corrupted."),
827 from_utf8(filename.absFilename())));
830 pimpl_->file_fully_loaded = true;
835 // Should probably be moved to somewhere else: BufferView? LyXView?
836 bool Buffer::save() const
838 // We don't need autosaves in the immediate future. (Asger)
839 resetAutosaveTimers();
841 string const encodedFilename = pimpl_->filename.toFilesystemEncoding();
844 bool madeBackup = false;
846 // make a backup if the file already exists
847 if (lyxrc.make_backup && fileName().exists()) {
848 backupName = FileName(absFileName() + '~');
849 if (!lyxrc.backupdir_path.empty()) {
850 string const mangledName =
851 subst(subst(os::internal_path(
852 backupName.absFilename()), '/', '!'), ':', '!');
853 backupName = FileName(addName(lyxrc.backupdir_path,
856 if (fileName().copyTo(backupName, false)) {
859 Alert::error(_("Backup failure"),
860 bformat(_("Cannot create backup file %1$s.\n"
861 "Please check whether the directory exists and is writeable."),
862 from_utf8(backupName.absFilename())));
863 //LYXERR(Debug::DEBUG, "Fs error: " << fe.what());
867 // ask if the disk file has been externally modified (use checksum method)
868 if (fileName().exists() && isExternallyModified(checksum_method)) {
869 docstring const file = makeDisplayPath(absFileName(), 20);
870 docstring text = bformat(_("Document %1$s has been externally modified. Are you sure "
871 "you want to overwrite this file?"), file);
872 int const ret = Alert::prompt(_("Overwrite modified file?"),
873 text, 1, 1, _("&Overwrite"), _("&Cancel"));
878 if (writeFile(pimpl_->filename)) {
880 removeAutosaveFile(absFileName());
881 saveCheckSum(pimpl_->filename);
884 // Saving failed, so backup is not backup
886 rename(backupName, pimpl_->filename);
892 bool Buffer::writeFile(FileName const & fname) const
894 if (pimpl_->read_only && fname == pimpl_->filename)
900 if (params().embedded)
901 // first write the .lyx file to the temporary directory
902 content = FileName(addName(temppath(), "content.lyx"));
906 if (params().compressed) {
907 gz::ogzstream ofs(content.toFilesystemEncoding().c_str(), ios::out|ios::trunc);
913 ofstream ofs(content.toFilesystemEncoding().c_str(), ios::out|ios::trunc);
920 if (retval && params().embedded) {
921 // write file.lyx and all the embedded files to the zip file fname
922 // if embedding is enabled
923 return pimpl_->embedded_files.writeFile(fname);
929 bool Buffer::write(ostream & ofs) const
932 // Use the standard "C" locale for file output.
933 ofs.imbue(std::locale::classic());
936 // The top of the file should not be written by params().
938 // write out a comment in the top of the file
939 ofs << "#LyX " << lyx_version
940 << " created this file. For more info see http://www.lyx.org/\n"
941 << "\\lyxformat " << LYX_FORMAT << "\n"
942 << "\\begin_document\n";
945 /// For each author, set 'used' to true if there is a change
946 /// by this author in the document; otherwise set it to 'false'.
947 AuthorList::Authors::const_iterator a_it = params().authors().begin();
948 AuthorList::Authors::const_iterator a_end = params().authors().end();
949 for (; a_it != a_end; ++a_it)
950 a_it->second.setUsed(false);
952 ParIterator const end = par_iterator_end();
953 ParIterator it = par_iterator_begin();
954 for ( ; it != end; ++it)
955 it->checkAuthors(params().authors());
957 // now write out the buffer parameters.
958 ofs << "\\begin_header\n";
959 params().writeFile(ofs);
960 ofs << "\\end_header\n";
963 ofs << "\n\\begin_body\n";
964 text().write(*this, ofs);
965 ofs << "\n\\end_body\n";
967 // Write marker that shows file is complete
968 ofs << "\\end_document" << endl;
970 // Shouldn't really be needed....
973 // how to check if close went ok?
974 // Following is an attempt... (BE 20001011)
976 // good() returns false if any error occured, including some
978 // bad() returns true if something bad happened in the buffer,
979 // which should include file system full errors.
984 lyxerr << "File was not closed properly." << endl;
991 bool Buffer::makeLaTeXFile(FileName const & fname,
992 string const & original_path,
993 OutputParams const & runparams,
994 bool output_preamble, bool output_body)
996 string const encoding = runparams.encoding->iconvName();
997 LYXERR(Debug::LATEX, "makeLaTeXFile encoding: " << encoding << "...");
999 odocfstream ofs(encoding);
1000 if (!openFileWrite(ofs, fname))
1003 //TexStream ts(ofs.rdbuf(), &texrow());
1005 bool failed_export = false;
1008 writeLaTeXSource(ofs, original_path,
1009 runparams, output_preamble, output_body);
1011 catch (iconv_codecvt_facet_exception & e) {
1012 lyxerr << "Caught iconv exception: " << e.what() << endl;
1013 failed_export = true;
1015 catch (std::exception const & e) {
1016 lyxerr << "Caught \"normal\" exception: " << e.what() << endl;
1017 failed_export = true;
1020 lyxerr << "Caught some really weird exception..." << endl;
1021 LyX::cref().emergencyCleanup();
1027 failed_export = true;
1028 lyxerr << "File '" << fname << "' was not closed properly." << endl;
1031 if (failed_export) {
1032 Alert::error(_("Encoding error"),
1033 _("Some characters of your document are probably not "
1034 "representable in the chosen encoding.\n"
1035 "Changing the document encoding to utf8 could help."));
1042 void Buffer::writeLaTeXSource(odocstream & os,
1043 string const & original_path,
1044 OutputParams const & runparams_in,
1045 bool const output_preamble, bool const output_body)
1047 OutputParams runparams = runparams_in;
1049 // validate the buffer.
1050 LYXERR(Debug::LATEX, " Validating buffer...");
1051 LaTeXFeatures features(*this, params(), runparams);
1053 LYXERR(Debug::LATEX, " Buffer validation done.");
1055 // The starting paragraph of the coming rows is the
1056 // first paragraph of the document. (Asger)
1057 if (output_preamble && runparams.nice) {
1058 os << "%% LyX " << lyx_version << " created this file. "
1059 "For more info, see http://www.lyx.org/.\n"
1060 "%% Do not edit unless you really know what "
1065 LYXERR(Debug::INFO, "lyx document header finished");
1066 // There are a few differences between nice LaTeX and usual files:
1067 // usual is \batchmode and has a
1068 // special input@path to allow the including of figures
1069 // with either \input or \includegraphics (what figinsets do).
1070 // input@path is set when the actual parameter
1071 // original_path is set. This is done for usual tex-file, but not
1072 // for nice-latex-file. (Matthias 250696)
1073 // Note that input@path is only needed for something the user does
1074 // in the preamble, included .tex files or ERT, files included by
1075 // LyX work without it.
1076 if (output_preamble) {
1077 if (!runparams.nice) {
1078 // code for usual, NOT nice-latex-file
1079 os << "\\batchmode\n"; // changed
1080 // from \nonstopmode
1083 if (!original_path.empty()) {
1085 // We don't know the encoding of inputpath
1086 docstring const inputpath = from_utf8(latex_path(original_path));
1087 os << "\\makeatletter\n"
1088 << "\\def\\input@path{{"
1089 << inputpath << "/}}\n"
1090 << "\\makeatother\n";
1096 // Write the preamble
1097 runparams.use_babel = params().writeLaTeX(os, features, texrow());
1103 os << "\\begin{document}\n";
1105 } // output_preamble
1107 texrow().start(paragraphs().begin()->id(), 0);
1109 LYXERR(Debug::INFO, "preamble finished, now the body.");
1111 if (!lyxrc.language_auto_begin &&
1112 !params().language->babel().empty()) {
1114 os << from_utf8(subst(lyxrc.language_command_begin,
1116 params().language->babel()))
1121 Encoding const & encoding = params().encoding();
1122 if (encoding.package() == Encoding::CJK) {
1123 // Open a CJK environment, since in contrast to the encodings
1124 // handled by inputenc the document encoding is not set in
1125 // the preamble if it is handled by CJK.sty.
1126 os << "\\begin{CJK}{" << from_ascii(encoding.latexName())
1131 // if we are doing a real file with body, even if this is the
1132 // child of some other buffer, let's cut the link here.
1133 // This happens for example if only a child document is printed.
1134 string save_parentname;
1135 if (output_preamble) {
1136 save_parentname = params().parentname;
1137 params().parentname.erase();
1140 loadChildDocuments();
1143 latexParagraphs(*this, paragraphs(), os, texrow(), runparams);
1145 // Restore the parenthood if needed
1146 if (output_preamble)
1147 params().parentname = save_parentname;
1149 // add this just in case after all the paragraphs
1153 if (encoding.package() == Encoding::CJK) {
1154 // Close the open CJK environment.
1155 // latexParagraphs will have opened one even if the last text
1157 os << "\\end{CJK}\n";
1161 if (!lyxrc.language_auto_end &&
1162 !params().language->babel().empty()) {
1163 os << from_utf8(subst(lyxrc.language_command_end,
1165 params().language->babel()))
1170 if (output_preamble) {
1171 os << "\\end{document}\n";
1173 LYXERR(Debug::LATEX, "makeLaTeXFile...done");
1175 LYXERR(Debug::LATEX, "LaTeXFile for inclusion made.");
1177 runparams_in.encoding = runparams.encoding;
1179 // Just to be sure. (Asger)
1182 LYXERR(Debug::INFO, "Finished making LaTeX file.");
1183 LYXERR(Debug::INFO, "Row count was " << texrow().rows() - 1 << '.');
1187 bool Buffer::isLatex() const
1189 return params().getTextClass().outputType() == LATEX;
1193 bool Buffer::isLiterate() const
1195 return params().getTextClass().outputType() == LITERATE;
1199 bool Buffer::isDocBook() const
1201 return params().getTextClass().outputType() == DOCBOOK;
1205 void Buffer::makeDocBookFile(FileName const & fname,
1206 OutputParams const & runparams,
1207 bool const body_only)
1209 LYXERR(Debug::LATEX, "makeDocBookFile...");
1213 if (!openFileWrite(ofs, fname))
1216 writeDocBookSource(ofs, fname.absFilename(), runparams, body_only);
1220 lyxerr << "File '" << fname << "' was not closed properly." << endl;
1224 void Buffer::writeDocBookSource(odocstream & os, string const & fname,
1225 OutputParams const & runparams,
1226 bool const only_body)
1228 LaTeXFeatures features(*this, params(), runparams);
1233 TextClass const & tclass = params().getTextClass();
1234 string const top_element = tclass.latexname();
1237 if (runparams.flavor == OutputParams::XML)
1238 os << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
1241 os << "<!DOCTYPE " << from_ascii(top_element) << ' ';
1244 if (! tclass.class_header().empty())
1245 os << from_ascii(tclass.class_header());
1246 else if (runparams.flavor == OutputParams::XML)
1247 os << "PUBLIC \"-//OASIS//DTD DocBook XML//EN\" "
1248 << "\"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd\"";
1250 os << " PUBLIC \"-//OASIS//DTD DocBook V4.2//EN\"";
1252 docstring preamble = from_utf8(params().preamble);
1253 if (runparams.flavor != OutputParams::XML ) {
1254 preamble += "<!ENTITY % output.print.png \"IGNORE\">\n";
1255 preamble += "<!ENTITY % output.print.pdf \"IGNORE\">\n";
1256 preamble += "<!ENTITY % output.print.eps \"IGNORE\">\n";
1257 preamble += "<!ENTITY % output.print.bmp \"IGNORE\">\n";
1260 string const name = runparams.nice
1261 ? changeExtension(absFileName(), ".sgml") : fname;
1262 preamble += features.getIncludedFiles(name);
1263 preamble += features.getLyXSGMLEntities();
1265 if (!preamble.empty()) {
1266 os << "\n [ " << preamble << " ]";
1271 string top = top_element;
1273 if (runparams.flavor == OutputParams::XML)
1274 top += params().language->code();
1276 top += params().language->code().substr(0,2);
1279 if (!params().options.empty()) {
1281 top += params().options;
1284 os << "<!-- " << ((runparams.flavor == OutputParams::XML)? "XML" : "SGML")
1285 << " file was created by LyX " << lyx_version
1286 << "\n See http://www.lyx.org/ for more information -->\n";
1288 params().getTextClass().counters().reset();
1290 loadChildDocuments();
1292 sgml::openTag(os, top);
1294 docbookParagraphs(paragraphs(), *this, os, runparams);
1295 sgml::closeTag(os, top_element);
1299 // chktex should be run with these flags disabled: 3, 22, 25, 30, 38(?)
1300 // Other flags: -wall -v0 -x
1301 int Buffer::runChktex()
1305 // get LaTeX-Filename
1306 FileName const path(temppath());
1307 string const name = addName(path.absFilename(), latexName());
1308 string const org_path = filePath();
1310 support::PathChanger p(path); // path to LaTeX file
1311 message(_("Running chktex..."));
1313 // Generate the LaTeX file if neccessary
1314 OutputParams runparams(¶ms().encoding());
1315 runparams.flavor = OutputParams::LATEX;
1316 runparams.nice = false;
1317 makeLaTeXFile(FileName(name), org_path, runparams);
1320 Chktex chktex(lyxrc.chktex_command, onlyFilename(name), filePath());
1321 int const res = chktex.run(terr); // run chktex
1324 Alert::error(_("chktex failure"),
1325 _("Could not run chktex successfully."));
1326 } else if (res > 0) {
1327 ErrorList & errlist = pimpl_->errorLists["ChkTeX"];
1329 bufferErrors(terr, errlist);
1340 void Buffer::validate(LaTeXFeatures & features) const
1342 TextClass const & tclass = params().getTextClass();
1344 if (params().outputChanges) {
1345 bool dvipost = LaTeXFeatures::isAvailable("dvipost");
1346 bool xcolorsoul = LaTeXFeatures::isAvailable("soul") &&
1347 LaTeXFeatures::isAvailable("xcolor");
1349 if (features.runparams().flavor == OutputParams::LATEX) {
1351 features.require("ct-dvipost");
1352 features.require("dvipost");
1353 } else if (xcolorsoul) {
1354 features.require("ct-xcolor-soul");
1355 features.require("soul");
1356 features.require("xcolor");
1358 features.require("ct-none");
1360 } else if (features.runparams().flavor == OutputParams::PDFLATEX ) {
1362 features.require("ct-xcolor-soul");
1363 features.require("soul");
1364 features.require("xcolor");
1365 features.require("pdfcolmk"); // improves color handling in PDF output
1367 features.require("ct-none");
1372 // AMS Style is at document level
1373 if (params().use_amsmath == BufferParams::package_on
1374 || tclass.provides("amsmath"))
1375 features.require("amsmath");
1376 if (params().use_esint == BufferParams::package_on)
1377 features.require("esint");
1379 loadChildDocuments();
1381 for_each(paragraphs().begin(), paragraphs().end(),
1382 boost::bind(&Paragraph::validate, _1, boost::ref(features)));
1384 // the bullet shapes are buffer level not paragraph level
1385 // so they are tested here
1386 for (int i = 0; i < 4; ++i) {
1387 if (params().user_defined_bullet(i) != ITEMIZE_DEFAULTS[i]) {
1388 int const font = params().user_defined_bullet(i).getFont();
1390 int const c = params()
1391 .user_defined_bullet(i)
1398 features.require("latexsym");
1400 } else if (font == 1) {
1401 features.require("amssymb");
1402 } else if ((font >= 2 && font <= 5)) {
1403 features.require("pifont");
1408 if (lyxerr.debugging(Debug::LATEX)) {
1409 features.showStruct();
1414 void Buffer::getLabelList(vector<docstring> & list) const
1416 /// if this is a child document and the parent is already loaded
1417 /// Use the parent's list instead [ale990407]
1418 Buffer const * tmp = masterBuffer();
1420 lyxerr << "masterBuffer() failed!" << endl;
1424 tmp->getLabelList(list);
1428 loadChildDocuments();
1430 for (InsetIterator it = inset_iterator_begin(inset()); it; ++it)
1431 it.nextInset()->getLabelList(*this, list);
1435 void Buffer::updateBibfilesCache()
1437 // if this is a child document and the parent is already loaded
1438 // update the parent's cache instead
1439 Buffer * tmp = masterBuffer();
1442 tmp->updateBibfilesCache();
1446 bibfilesCache_.clear();
1447 for (InsetIterator it = inset_iterator_begin(inset()); it; ++it) {
1448 if (it->lyxCode() == BIBTEX_CODE) {
1449 InsetBibtex const & inset =
1450 static_cast<InsetBibtex const &>(*it);
1451 vector<FileName> const bibfiles = inset.getFiles(*this);
1452 bibfilesCache_.insert(bibfilesCache_.end(),
1455 } else if (it->lyxCode() == INCLUDE_CODE) {
1456 InsetInclude & inset =
1457 static_cast<InsetInclude &>(*it);
1458 inset.updateBibfilesCache(*this);
1459 vector<FileName> const & bibfiles =
1460 inset.getBibfilesCache(*this);
1461 bibfilesCache_.insert(bibfilesCache_.end(),
1469 vector<FileName> const & Buffer::getBibfilesCache() const
1471 // if this is a child document and the parent is already loaded
1472 // use the parent's cache instead
1473 Buffer const * tmp = masterBuffer();
1476 return tmp->getBibfilesCache();
1478 // We update the cache when first used instead of at loading time.
1479 if (bibfilesCache_.empty())
1480 const_cast<Buffer *>(this)->updateBibfilesCache();
1482 return bibfilesCache_;
1486 bool Buffer::isDepClean(string const & name) const
1488 DepClean::const_iterator const it = pimpl_->dep_clean.find(name);
1489 if (it == pimpl_->dep_clean.end())
1495 void Buffer::markDepClean(string const & name)
1497 pimpl_->dep_clean[name] = true;
1501 bool Buffer::dispatch(string const & command, bool * result)
1503 return dispatch(lyxaction.lookupFunc(command), result);
1507 bool Buffer::dispatch(FuncRequest const & func, bool * result)
1509 bool dispatched = true;
1511 switch (func.action) {
1512 case LFUN_BUFFER_EXPORT: {
1513 bool const tmp = doExport(to_utf8(func.argument()), false);
1526 void Buffer::changeLanguage(Language const * from, Language const * to)
1531 for_each(par_iterator_begin(),
1533 bind(&Paragraph::changeLanguage, _1, params(), from, to));
1537 bool Buffer::isMultiLingual() const
1539 ParConstIterator end = par_iterator_end();
1540 for (ParConstIterator it = par_iterator_begin(); it != end; ++it)
1541 if (it->isMultiLingual(params()))
1548 ParIterator Buffer::getParFromID(int const id) const
1550 ParConstIterator it = par_iterator_begin();
1551 ParConstIterator const end = par_iterator_end();
1554 // John says this is called with id == -1 from undo
1555 lyxerr << "getParFromID(), id: " << id << endl;
1559 for (; it != end; ++it)
1567 bool Buffer::hasParWithID(int const id) const
1569 ParConstIterator const it = getParFromID(id);
1570 return it != par_iterator_end();
1574 ParIterator Buffer::par_iterator_begin()
1576 return lyx::par_iterator_begin(inset());
1580 ParIterator Buffer::par_iterator_end()
1582 return lyx::par_iterator_end(inset());
1586 ParConstIterator Buffer::par_iterator_begin() const
1588 return lyx::par_const_iterator_begin(inset());
1592 ParConstIterator Buffer::par_iterator_end() const
1594 return lyx::par_const_iterator_end(inset());
1598 Language const * Buffer::language() const
1600 return params().language;
1604 docstring const Buffer::B_(string const & l10n) const
1606 return params().B_(l10n);
1610 bool Buffer::isClean() const
1612 return pimpl_->lyx_clean;
1616 bool Buffer::isBakClean() const
1618 return pimpl_->bak_clean;
1622 bool Buffer::isExternallyModified(CheckMethod method) const
1624 BOOST_ASSERT(pimpl_->filename.exists());
1625 // if method == timestamp, check timestamp before checksum
1626 return (method == checksum_method
1627 || pimpl_->timestamp_ != pimpl_->filename.lastModified())
1628 && pimpl_->checksum_ != sum(pimpl_->filename);
1632 void Buffer::saveCheckSum(FileName const & file) const
1634 if (file.exists()) {
1635 pimpl_->timestamp_ = file.lastModified();
1636 pimpl_->checksum_ = sum(file);
1638 // in the case of save to a new file.
1639 pimpl_->timestamp_ = 0;
1640 pimpl_->checksum_ = 0;
1645 void Buffer::markClean() const
1647 if (!pimpl_->lyx_clean) {
1648 pimpl_->lyx_clean = true;
1651 // if the .lyx file has been saved, we don't need an
1653 pimpl_->bak_clean = true;
1657 void Buffer::markBakClean() const
1659 pimpl_->bak_clean = true;
1663 void Buffer::setUnnamed(bool flag)
1665 pimpl_->unnamed = flag;
1669 bool Buffer::isUnnamed() const
1671 return pimpl_->unnamed;
1675 // FIXME: this function should be moved to buffer_pimpl.C
1676 void Buffer::markDirty()
1678 if (pimpl_->lyx_clean) {
1679 pimpl_->lyx_clean = false;
1682 pimpl_->bak_clean = false;
1684 DepClean::iterator it = pimpl_->dep_clean.begin();
1685 DepClean::const_iterator const end = pimpl_->dep_clean.end();
1687 for (; it != end; ++it)
1692 FileName Buffer::fileName() const
1694 return pimpl_->filename;
1698 string Buffer::absFileName() const
1700 return pimpl_->filename.absFilename();
1704 string const & Buffer::filePath() const
1706 return params().filepath;
1710 bool Buffer::isReadonly() const
1712 return pimpl_->read_only;
1716 void Buffer::setParentName(string const & name)
1718 if (name == pimpl_->filename.absFilename())
1719 // Avoids recursive include.
1720 params().parentname.clear();
1722 params().parentname = name;
1726 Buffer const * Buffer::masterBuffer() const
1728 if (!params().parentname.empty()
1729 && theBufferList().exists(params().parentname)) {
1730 Buffer const * buf = theBufferList().getBuffer(params().parentname);
1731 //We need to check if the parent is us...
1732 //FIXME RECURSIVE INCLUDE
1733 //This is not sufficient, since recursive includes could be downstream.
1734 if (buf && buf != this)
1735 return buf->masterBuffer();
1742 Buffer * Buffer::masterBuffer()
1744 if (!params().parentname.empty()
1745 && theBufferList().exists(params().parentname)) {
1746 Buffer * buf = theBufferList().getBuffer(params().parentname);
1747 //We need to check if the parent is us...
1748 //FIXME RECURSIVE INCLUDE
1749 //This is not sufficient, since recursive includes could be downstream.
1750 if (buf && buf != this)
1751 return buf->masterBuffer();
1758 bool Buffer::hasMacro(docstring const & name, Paragraph const & par) const
1760 Impl::PositionToMacroMap::iterator it;
1761 it = pimpl_->macros[name].upper_bound(par.macrocontextPosition());
1762 if (it != pimpl_->macros[name].end())
1765 // If there is a master buffer, query that
1766 const Buffer * master = masterBuffer();
1767 if (master && master != this)
1768 return master->hasMacro(name);
1770 return MacroTable::globalMacros().has(name);
1774 bool Buffer::hasMacro(docstring const & name) const
1776 if( !pimpl_->macros[name].empty() )
1779 // If there is a master buffer, query that
1780 const Buffer * master = masterBuffer();
1781 if (master && master != this)
1782 return master->hasMacro(name);
1784 return MacroTable::globalMacros().has(name);
1788 MacroData const & Buffer::getMacro(docstring const & name,
1789 Paragraph const & par) const
1791 Impl::PositionToMacroMap::iterator it;
1792 it = pimpl_->macros[name].upper_bound(par.macrocontextPosition());
1793 if( it != pimpl_->macros[name].end() )
1796 // If there is a master buffer, query that
1797 const Buffer * master = masterBuffer();
1798 if (master && master != this)
1799 return master->getMacro(name);
1801 return MacroTable::globalMacros().get(name);
1805 MacroData const & Buffer::getMacro(docstring const & name) const
1807 Impl::PositionToMacroMap::iterator it;
1808 it = pimpl_->macros[name].begin();
1809 if( it != pimpl_->macros[name].end() )
1812 // If there is a master buffer, query that
1813 const Buffer * master = masterBuffer();
1814 if (master && master != this)
1815 return master->getMacro(name);
1817 return MacroTable::globalMacros().get(name);
1821 void Buffer::updateMacros()
1823 // start with empty table
1824 pimpl_->macros = Impl::NameToPositionMacroMap();
1826 // Iterate over buffer
1827 ParagraphList & pars = text().paragraphs();
1828 for (size_t i = 0, n = pars.size(); i != n; ++i) {
1829 // set position again
1830 pars[i].setMacrocontextPosition(i);
1832 //lyxerr << "searching main par " << i
1833 // << " for macro definitions" << std::endl;
1834 InsetList const & insets = pars[i].insetList();
1835 InsetList::const_iterator it = insets.begin();
1836 InsetList::const_iterator end = insets.end();
1837 for ( ; it != end; ++it) {
1838 if (it->inset->lyxCode() != MATHMACRO_CODE)
1842 MathMacroTemplate const & macroTemplate
1843 = static_cast<MathMacroTemplate const &>(*it->inset);
1846 if (macroTemplate.validMacro()) {
1847 MacroData macro = macroTemplate.asMacroData();
1850 // call hasMacro here instead of directly querying mc to
1851 // also take the master document into consideration
1852 macro.setRedefinition(hasMacro(macroTemplate.name()));
1854 // register macro (possibly overwrite the previous one of this paragraph)
1855 pimpl_->macros[macroTemplate.name()][i] = macro;
1862 void Buffer::changeRefsIfUnique(docstring const & from, docstring const & to,
1865 //FIXME: This does not work for child documents yet.
1866 BOOST_ASSERT(code == CITE_CODE || code == REF_CODE);
1867 // Check if the label 'from' appears more than once
1868 vector<docstring> labels;
1871 if (code == CITE_CODE) {
1873 keys.fillWithBibKeys(this);
1874 BiblioInfo::const_iterator bit = keys.begin();
1875 BiblioInfo::const_iterator bend = keys.end();
1877 for (; bit != bend; ++bit)
1879 labels.push_back(bit->first);
1882 getLabelList(labels);
1883 paramName = "reference";
1886 if (std::count(labels.begin(), labels.end(), from) > 1)
1889 for (InsetIterator it = inset_iterator_begin(inset()); it; ++it) {
1890 if (it->lyxCode() == code) {
1891 InsetCommand & inset = static_cast<InsetCommand &>(*it);
1892 docstring const oldValue = inset.getParam(paramName);
1893 if (oldValue == from)
1894 inset.setParam(paramName, to);
1900 void Buffer::getSourceCode(odocstream & os, pit_type par_begin,
1901 pit_type par_end, bool full_source)
1903 OutputParams runparams(¶ms().encoding());
1904 runparams.nice = true;
1905 runparams.flavor = OutputParams::LATEX;
1906 runparams.linelen = lyxrc.plaintext_linelen;
1907 // No side effect of file copying and image conversion
1908 runparams.dryrun = true;
1912 os << "% " << _("Preview source code") << "\n\n";
1916 writeLaTeXSource(os, filePath(), runparams, true, true);
1918 writeDocBookSource(os, absFileName(), runparams, false);
1921 runparams.par_begin = par_begin;
1922 runparams.par_end = par_end;
1923 if (par_begin + 1 == par_end)
1925 << bformat(_("Preview source code for paragraph %1$d"), par_begin)
1929 << bformat(_("Preview source code from paragraph %1$s to %2$s"),
1930 convert<docstring>(par_begin),
1931 convert<docstring>(par_end - 1))
1935 // output paragraphs
1937 latexParagraphs(*this, paragraphs(), os, texrow(), runparams);
1940 docbookParagraphs(paragraphs(), *this, os, runparams);
1946 ErrorList const & Buffer::errorList(string const & type) const
1948 static ErrorList const emptyErrorList;
1949 std::map<string, ErrorList>::const_iterator I = pimpl_->errorLists.find(type);
1950 if (I == pimpl_->errorLists.end())
1951 return emptyErrorList;
1957 ErrorList & Buffer::errorList(string const & type)
1959 return pimpl_->errorLists[type];
1963 void Buffer::structureChanged() const
1966 gui_->structureChanged();
1970 void Buffer::errors(std::string const & err) const
1977 void Buffer::message(docstring const & msg) const
1984 void Buffer::setBusy(bool on) const
1991 void Buffer::setReadOnly(bool on) const
1994 pimpl_->wa_->setReadOnly(on);
1998 void Buffer::updateTitles() const
2001 pimpl_->wa_->updateTitles();
2005 void Buffer::resetAutosaveTimers() const
2008 gui_->resetAutosaveTimers();
2012 void Buffer::setGuiDelegate(frontend::GuiBufferDelegate * gui)
2021 class AutoSaveBuffer : public support::ForkedProcess {
2024 AutoSaveBuffer(Buffer const & buffer, FileName const & fname)
2025 : buffer_(buffer), fname_(fname) {}
2027 virtual boost::shared_ptr<ForkedProcess> clone() const
2029 return boost::shared_ptr<ForkedProcess>(new AutoSaveBuffer(*this));
2034 command_ = to_utf8(bformat(_("Auto-saving %1$s"),
2035 from_utf8(fname_.absFilename())));
2036 return run(DontWait);
2040 virtual int generateChild();
2042 Buffer const & buffer_;
2047 #if !defined (HAVE_FORK)
2051 int AutoSaveBuffer::generateChild()
2053 // tmp_ret will be located (usually) in /tmp
2054 // will that be a problem?
2055 pid_t const pid = fork();
2056 // If you want to debug the autosave
2057 // you should set pid to -1, and comment out the fork.
2058 if (pid == 0 || pid == -1) {
2059 // pid = -1 signifies that lyx was unable
2060 // to fork. But we will do the save
2062 bool failed = false;
2064 FileName const tmp_ret(tempName(FileName(), "lyxauto"));
2065 if (!tmp_ret.empty()) {
2066 buffer_.writeFile(tmp_ret);
2067 // assume successful write of tmp_ret
2068 if (!rename(tmp_ret, fname_)) {
2070 // most likely couldn't move between
2071 // filesystems unless write of tmp_ret
2072 // failed so remove tmp file (if it
2081 // failed to write/rename tmp_ret so try writing direct
2082 if (!buffer_.writeFile(fname_)) {
2083 // It is dangerous to do this in the child,
2084 // but safe in the parent, so...
2085 if (pid == -1) // emit message signal.
2086 buffer_.message(_("Autosave failed!"));
2089 if (pid == 0) { // we are the child so...
2099 // Perfect target for a thread...
2100 void Buffer::autoSave() const
2102 if (isBakClean() || isReadonly()) {
2103 // We don't save now, but we'll try again later
2104 resetAutosaveTimers();
2108 // emit message signal.
2109 message(_("Autosaving current document..."));
2111 // create autosave filename
2112 string fname = filePath();
2114 fname += onlyFilename(absFileName());
2117 AutoSaveBuffer autosave(*this, FileName(fname));
2121 resetAutosaveTimers();
2125 /** Write a buffer to a new file name and rename the buffer
2126 according to the new file name.
2128 This function is e.g. used by menu callbacks and
2129 LFUN_BUFFER_WRITE_AS.
2131 If 'newname' is empty (the default), the user is asked via a
2132 dialog for the buffer's new name and location.
2134 If 'newname' is non-empty and has an absolute path, that is used.
2135 Otherwise the base directory of the buffer is used as the base
2136 for any relative path in 'newname'.
2139 bool Buffer::writeAs(string const & newname)
2141 string fname = absFileName();
2142 string const oldname = fname;
2144 if (newname.empty()) { /// No argument? Ask user through dialog
2147 FileDialog dlg(_("Choose a filename to save document as"),
2148 LFUN_BUFFER_WRITE_AS);
2149 dlg.setButton1(_("Documents|#o#O"), from_utf8(lyxrc.document_path));
2150 dlg.setButton2(_("Templates|#T#t"), from_utf8(lyxrc.template_path));
2152 if (!support::isLyXFilename(fname))
2155 support::FileFilterList const filter(_("LyX Documents (*.lyx)"));
2157 FileDialog::Result result =
2158 dlg.save(from_utf8(onlyPath(fname)),
2160 from_utf8(onlyFilename(fname)));
2162 if (result.first == FileDialog::Later)
2165 fname = to_utf8(result.second);
2170 // Make sure the absolute filename ends with appropriate suffix
2171 fname = makeAbsPath(fname).absFilename();
2172 if (!support::isLyXFilename(fname))
2176 fname = makeAbsPath(newname, onlyPath(oldname)).absFilename();
2178 if (FileName(fname).exists()) {
2179 docstring const file = makeDisplayPath(fname, 30);
2180 docstring text = bformat(_("The document %1$s already "
2181 "exists.\n\nDo you want to "
2182 "overwrite that document?"),
2184 int const ret = Alert::prompt(_("Overwrite document?"),
2185 text, 0, 1, _("&Overwrite"), _("&Cancel"));
2191 // Ok, change the name of the buffer
2194 bool unnamed = isUnnamed();
2196 saveCheckSum(FileName(fname));
2199 setFileName(oldname);
2200 setUnnamed(unnamed);
2201 saveCheckSum(FileName(oldname));
2205 removeAutosaveFile(oldname);
2210 bool Buffer::menuWrite()
2213 LyX::ref().session().lastFiles().add(FileName(absFileName()));
2217 // FIXME: we don't tell the user *WHY* the save failed !!
2219 docstring const file = makeDisplayPath(absFileName(), 30);
2221 docstring text = bformat(_("The document %1$s could not be saved.\n\n"
2222 "Do you want to rename the document and "
2223 "try again?"), file);
2224 int const ret = Alert::prompt(_("Rename and save?"),
2225 text, 0, 1, _("&Rename"), _("&Cancel"));
2234 void Buffer::loadChildDocuments() const
2236 bool parse_error = false;
2238 for (InsetIterator it = inset_iterator_begin(inset()); it; ++it) {
2239 if (it->lyxCode() != INCLUDE_CODE)
2241 InsetCommand const & inset = static_cast<InsetCommand const &>(*it);
2242 InsetCommandParams const & ip = inset.params();
2243 Buffer * child = loadIfNeeded(*this, ip);
2246 parse_error |= !child->errorList("Parse").empty();
2247 child->loadChildDocuments();
2250 if (use_gui && masterBuffer() == this)
2251 updateLabels(*this);
2255 string Buffer::bufferFormat() const
2265 bool Buffer::doExport(string const & format, bool put_in_tempdir,
2266 string & result_file)
2268 string backend_format;
2269 OutputParams runparams(¶ms().encoding());
2270 runparams.flavor = OutputParams::LATEX;
2271 runparams.linelen = lyxrc.plaintext_linelen;
2272 vector<string> backs = backends();
2273 if (find(backs.begin(), backs.end(), format) == backs.end()) {
2274 // Get shortest path to format
2275 Graph::EdgePath path;
2276 for (vector<string>::const_iterator it = backs.begin();
2277 it != backs.end(); ++it) {
2278 Graph::EdgePath p = theConverters().getPath(*it, format);
2279 if (!p.empty() && (path.empty() || p.size() < path.size())) {
2280 backend_format = *it;
2285 runparams.flavor = theConverters().getFlavor(path);
2287 Alert::error(_("Couldn't export file"),
2288 bformat(_("No information for exporting the format %1$s."),
2289 formats.prettyName(format)));
2293 backend_format = format;
2294 // FIXME: Don't hardcode format names here, but use a flag
2295 if (backend_format == "pdflatex")
2296 runparams.flavor = OutputParams::PDFLATEX;
2299 string filename = latexName(false);
2300 filename = addName(temppath(), filename);
2301 filename = changeExtension(filename,
2302 formats.extension(backend_format));
2304 // Plain text backend
2305 if (backend_format == "text")
2306 writePlaintextFile(*this, FileName(filename), runparams);
2308 else if (backend_format == "lyx")
2309 writeFile(FileName(filename));
2311 else if (isDocBook()) {
2312 runparams.nice = !put_in_tempdir;
2313 makeDocBookFile(FileName(filename), runparams);
2316 else if (backend_format == format) {
2317 runparams.nice = true;
2318 if (!makeLaTeXFile(FileName(filename), string(), runparams))
2320 } else if (!lyxrc.tex_allows_spaces
2321 && support::contains(filePath(), ' ')) {
2322 Alert::error(_("File name error"),
2323 _("The directory path to the document cannot contain spaces."));
2326 runparams.nice = false;
2327 if (!makeLaTeXFile(FileName(filename), filePath(), runparams))
2331 string const error_type = (format == "program")
2332 ? "Build" : bufferFormat();
2333 string const ext = formats.extension(format);
2334 FileName const tmp_result_file(changeExtension(filename, ext));
2335 bool const success = theConverters().convert(this, FileName(filename),
2336 tmp_result_file, FileName(absFileName()), backend_format, format,
2337 errorList(error_type));
2338 // Emit the signal to show the error list.
2339 if (format != backend_format)
2345 result_file = tmp_result_file.absFilename();
2347 result_file = changeExtension(absFileName(), ext);
2348 // We need to copy referenced files (e. g. included graphics
2349 // if format == "dvi") to the result dir.
2350 vector<ExportedFile> const files =
2351 runparams.exportdata->externalFiles(format);
2352 string const dest = onlyPath(result_file);
2353 CopyStatus status = SUCCESS;
2354 for (vector<ExportedFile>::const_iterator it = files.begin();
2355 it != files.end() && status != CANCEL; ++it) {
2357 formats.getFormatFromFile(it->sourceName);
2358 status = copyFile(fmt, it->sourceName,
2359 makeAbsPath(it->exportName, dest),
2360 it->exportName, status == FORCE);
2362 if (status == CANCEL) {
2363 message(_("Document export cancelled."));
2364 } else if (tmp_result_file.exists()) {
2365 // Finally copy the main file
2366 status = copyFile(format, tmp_result_file,
2367 FileName(result_file), result_file,
2369 message(bformat(_("Document exported as %1$s "
2371 formats.prettyName(format),
2372 makeDisplayPath(result_file)));
2374 // This must be a dummy converter like fax (bug 1888)
2375 message(bformat(_("Document exported as %1$s"),
2376 formats.prettyName(format)));
2384 bool Buffer::doExport(string const & format, bool put_in_tempdir)
2387 return doExport(format, put_in_tempdir, result_file);
2391 bool Buffer::preview(string const & format)
2394 if (!doExport(format, true, result_file))
2396 return formats.view(*this, FileName(result_file), format);
2400 bool Buffer::isExportable(string const & format) const
2402 vector<string> backs = backends();
2403 for (vector<string>::const_iterator it = backs.begin();
2404 it != backs.end(); ++it)
2405 if (theConverters().isReachable(*it, format))
2411 vector<Format const *> Buffer::exportableFormats(bool only_viewable) const
2413 vector<string> backs = backends();
2414 vector<Format const *> result =
2415 theConverters().getReachable(backs[0], only_viewable, true);
2416 for (vector<string>::const_iterator it = backs.begin() + 1;
2417 it != backs.end(); ++it) {
2418 vector<Format const *> r =
2419 theConverters().getReachable(*it, only_viewable, false);
2420 result.insert(result.end(), r.begin(), r.end());
2426 vector<string> Buffer::backends() const
2429 if (params().getTextClass().isTeXClassAvailable()) {
2430 v.push_back(bufferFormat());
2431 // FIXME: Don't hardcode format names here, but use a flag
2432 if (v.back() == "latex")
2433 v.push_back("pdflatex");
2435 v.push_back("text");
2441 bool Buffer::readFileHelper(FileName const & s)
2443 // File information about normal file
2445 docstring const file = makeDisplayPath(s.absFilename(), 50);
2446 docstring text = bformat(_("The specified document\n%1$s"
2447 "\ncould not be read."), file);
2448 Alert::error(_("Could not read document"), text);
2452 // Check if emergency save file exists and is newer.
2453 FileName const e(s.absFilename() + ".emergency");
2455 if (e.exists() && s.exists() && e.lastModified() > s.lastModified()) {
2456 docstring const file = makeDisplayPath(s.absFilename(), 20);
2457 docstring const text =
2458 bformat(_("An emergency save of the document "
2460 "Recover emergency save?"), file);
2461 switch (Alert::prompt(_("Load emergency save?"), text, 0, 2,
2462 _("&Recover"), _("&Load Original"),
2466 // the file is not saved if we load the emergency file.
2476 // Now check if autosave file is newer.
2477 FileName const a(onlyPath(s.absFilename()) + '#' + onlyFilename(s.absFilename()) + '#');
2479 if (a.exists() && s.exists() && a.lastModified() > s.lastModified()) {
2480 docstring const file = makeDisplayPath(s.absFilename(), 20);
2481 docstring const text =
2482 bformat(_("The backup of the document "
2483 "%1$s is newer.\n\nLoad the "
2484 "backup instead?"), file);
2485 switch (Alert::prompt(_("Load backup?"), text, 0, 2,
2486 _("&Load backup"), _("Load &original"),
2490 // the file is not saved if we load the autosave file.
2494 // Here we delete the autosave
2505 bool Buffer::loadLyXFile(FileName const & s)
2507 if (s.isReadableFile()) {
2508 if (readFileHelper(s)) {
2509 lyxvc().file_found_hook(s);
2510 if (!s.isWritable())
2515 docstring const file = makeDisplayPath(s.absFilename(), 20);
2516 // Here we probably should run
2517 if (LyXVC::file_not_found_hook(s)) {
2518 docstring const text =
2519 bformat(_("Do you want to retrieve the document"
2520 " %1$s from version control?"), file);
2521 int const ret = Alert::prompt(_("Retrieve from version control?"),
2522 text, 0, 1, _("&Retrieve"), _("&Cancel"));
2525 // How can we know _how_ to do the checkout?
2526 // With the current VC support it has to be,
2527 // a RCS file since CVS do not have special ,v files.
2529 return loadLyXFile(s);
2537 void Buffer::bufferErrors(TeXErrors const & terr, ErrorList & errorList) const
2539 TeXErrors::Errors::const_iterator cit = terr.begin();
2540 TeXErrors::Errors::const_iterator end = terr.end();
2542 for (; cit != end; ++cit) {
2545 int errorRow = cit->error_in_line;
2546 bool found = texrow().getIdFromRow(errorRow, id_start,
2552 found = texrow().getIdFromRow(errorRow, id_end, pos_end);
2553 } while (found && id_start == id_end && pos_start == pos_end);
2555 errorList.push_back(ErrorItem(cit->error_desc,
2556 cit->error_text, id_start, pos_start, pos_end));