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"
47 #include "output_docbook.h"
49 #include "output_latex.h"
50 #include "output_plaintext.h"
51 #include "paragraph_funcs.h"
52 #include "Paragraph.h"
53 #include "ParagraphParameters.h"
54 #include "ParIterator.h"
55 #include "PDFOptions.h"
59 #include "TexStream.h"
60 #include "TextClassList.h"
62 #include "TocBackend.h"
64 #include "VCBackend.h"
67 #include "insets/InsetBibitem.h"
68 #include "insets/InsetBibtex.h"
69 #include "insets/InsetInclude.h"
70 #include "insets/InsetText.h"
72 #include "mathed/MacroTable.h"
73 #include "mathed/MathMacroTemplate.h"
74 #include "mathed/MathSupport.h"
76 #include "frontends/alert.h"
77 #include "frontends/Delegates.h"
78 #include "frontends/WorkAreaManager.h"
79 #include "frontends/FileDialog.h"
81 #include "graphics/Previews.h"
83 #include "support/types.h"
84 #include "support/lyxalgo.h"
85 #include "support/FileFilterList.h"
86 #include "support/filetools.h"
87 #include "support/Forkedcall.h"
88 #include "support/fs_extras.h"
89 #include "support/gzstream.h"
90 #include "support/lyxlib.h"
91 #include "support/os.h"
92 #include "support/Path.h"
93 #include "support/textutils.h"
94 #include "support/convert.h"
96 #if !defined (HAVE_FORK)
100 #include <boost/bind.hpp>
101 #include <boost/filesystem/exception.hpp>
102 #include <boost/filesystem/operations.hpp>
103 #include <boost/shared_ptr.hpp>
113 using std::make_pair;
118 using std::ostringstream;
129 using support::addName;
130 using support::bformat;
131 using support::changeExtension;
132 using support::cmd_ret;
133 using support::createBufferTmpDir;
134 using support::FileName;
135 using support::libFileSearch;
136 using support::latex_path;
137 using support::ltrim;
138 using support::makeAbsPath;
139 using support::makeDisplayPath;
140 using support::makeLatexName;
141 using support::onlyFilename;
142 using support::onlyPath;
143 using support::quoteName;
144 using support::removeAutosaveFile;
145 using support::rename;
146 using support::runCommand;
147 using support::split;
148 using support::subst;
149 using support::tempName;
152 using support::suffixIs;
154 namespace Alert = frontend::Alert;
155 namespace os = support::os;
156 namespace fs = boost::filesystem;
160 int const LYX_FORMAT = 299; //Uwe: Hyperlink types
165 typedef std::map<string, bool> DepClean;
170 Impl(Buffer & parent, FileName const & file, bool readonly);
177 /// need to regenerate .tex?
181 mutable bool lyx_clean;
183 /// is autosave needed?
184 mutable bool bak_clean;
186 /// is this a unnamed file (New...)?
192 /// name of the file the buffer is associated with.
195 /** Set to true only when the file is fully loaded.
196 * Used to prevent the premature generation of previews
197 * and by the citation inset.
199 bool file_fully_loaded;
201 /// our Text that should be wrapped in an InsetText
205 TocBackend toc_backend;
208 typedef std::map<unsigned int, MacroData, std::greater<int> > PositionToMacroMap;
209 typedef std::map<docstring, PositionToMacroMap> NameToPositionMacroMap;
210 NameToPositionMacroMap macros;
212 /// Container for all sort of Buffer dependant errors.
213 map<string, ErrorList> errorLists;
215 /// all embedded files of this buffer
216 EmbeddedFiles embedded_files;
218 /// timestamp and checksum used to test if the file has been externally
219 /// modified. (Used to properly enable 'File->Revert to saved', bug 4114).
221 unsigned long checksum_;
224 frontend::WorkAreaManager * wa_;
231 Buffer::Impl::Impl(Buffer & parent, FileName const & file, bool readonly_)
232 : lyx_clean(true), bak_clean(true), unnamed(false), read_only(readonly_),
233 filename(file), file_fully_loaded(false), inset(params),
234 toc_backend(&parent), embedded_files(&parent), timestamp_(0),
235 checksum_(0), wa_(0), undo_(parent)
237 inset.setAutoBreakRows(true);
238 lyxvc.setBuffer(&parent);
239 temppath = createBufferTmpDir();
240 params.filepath = onlyPath(file.absFilename());
241 // FIXME: And now do something if temppath == string(), because we
242 // assume from now on that temppath points to a valid temp dir.
243 // See http://www.mail-archive.com/lyx-devel@lists.lyx.org/msg67406.html
246 wa_ = new frontend::WorkAreaManager;
250 Buffer::Buffer(string const & file, bool readonly)
251 : pimpl_(new Impl(*this, FileName(file), readonly)), gui_(0)
253 LYXERR(Debug::INFO) << "Buffer::Buffer()" << endl;
259 LYXERR(Debug::INFO) << "Buffer::~Buffer()" << endl;
260 // here the buffer should take care that it is
261 // saved properly, before it goes into the void.
263 Buffer * master = masterBuffer();
264 if (master != this && use_gui)
265 // We are closing buf which was a child document so we
266 // must update the labels and section numbering of its master
268 updateLabels(*master);
270 if (!temppath().empty() && !FileName(temppath()).destroyDirectory()) {
271 Alert::warning(_("Could not remove temporary directory"),
272 bformat(_("Could not remove the temporary directory %1$s"),
273 from_utf8(temppath())));
276 // Remove any previewed LaTeX snippets associated with this buffer.
277 graphics::Previews::get().removeLoader(*this);
280 pimpl_->wa_->closeAll();
287 void Buffer::changed() const
290 pimpl_->wa_->redrawAll();
294 frontend::WorkAreaManager & Buffer::workAreaManager() const
296 BOOST_ASSERT(pimpl_->wa_);
301 Text & Buffer::text() const
303 return const_cast<Text &>(pimpl_->inset.text_);
307 Inset & Buffer::inset() const
309 return const_cast<InsetText &>(pimpl_->inset);
313 BufferParams & Buffer::params()
315 return pimpl_->params;
319 BufferParams const & Buffer::params() const
321 return pimpl_->params;
325 ParagraphList & Buffer::paragraphs()
327 return text().paragraphs();
331 ParagraphList const & Buffer::paragraphs() const
333 return text().paragraphs();
337 LyXVC & Buffer::lyxvc()
339 return pimpl_->lyxvc;
343 LyXVC const & Buffer::lyxvc() const
345 return pimpl_->lyxvc;
349 string const & Buffer::temppath() const
351 return pimpl_->temppath;
355 TexRow & Buffer::texrow()
357 return pimpl_->texrow;
361 TexRow const & Buffer::texrow() const
363 return pimpl_->texrow;
367 TocBackend & Buffer::tocBackend()
369 return pimpl_->toc_backend;
373 TocBackend const & Buffer::tocBackend() const
375 return pimpl_->toc_backend;
379 EmbeddedFiles & Buffer::embeddedFiles()
381 return pimpl_->embedded_files;
385 EmbeddedFiles const & Buffer::embeddedFiles() const
387 return pimpl_->embedded_files;
391 Undo & Buffer::undo()
393 return pimpl_->undo_;
397 string Buffer::latexName(bool const no_path) const
399 string const name = changeExtension(makeLatexName(absFileName()), ".tex");
400 return no_path ? onlyFilename(name) : name;
404 pair<Buffer::LogType, string> Buffer::logName() const
406 string const filename = latexName(false);
408 if (filename.empty())
409 return make_pair(Buffer::latexlog, string());
411 string const path = temppath();
413 FileName const fname(addName(temppath(),
414 onlyFilename(changeExtension(filename,
416 FileName const bname(
417 addName(path, onlyFilename(
418 changeExtension(filename,
419 formats.extension("literate") + ".out"))));
421 // If no Latex log or Build log is newer, show Build log
423 if (bname.exists() &&
424 (!fname.exists() || fname.lastModified() < bname.lastModified())) {
425 LYXERR(Debug::FILES) << "Log name calculated as: " << bname << endl;
426 return make_pair(Buffer::buildlog, bname.absFilename());
428 LYXERR(Debug::FILES) << "Log name calculated as: " << fname << endl;
429 return make_pair(Buffer::latexlog, fname.absFilename());
433 void Buffer::setReadonly(bool const flag)
435 if (pimpl_->read_only != flag) {
436 pimpl_->read_only = flag;
442 void Buffer::setFileName(string const & newfile)
444 pimpl_->filename = makeAbsPath(newfile);
445 params().filepath = onlyPath(pimpl_->filename.absFilename());
446 setReadonly(pimpl_->filename.isReadOnly());
451 int Buffer::readHeader(Lexer & lex)
453 int unknown_tokens = 0;
455 int begin_header_line = -1;
457 // Initialize parameters that may be/go lacking in header:
458 params().branchlist().clear();
459 params().preamble.erase();
460 params().options.erase();
461 params().float_placement.erase();
462 params().paperwidth.erase();
463 params().paperheight.erase();
464 params().leftmargin.erase();
465 params().rightmargin.erase();
466 params().topmargin.erase();
467 params().bottommargin.erase();
468 params().headheight.erase();
469 params().headsep.erase();
470 params().footskip.erase();
471 params().listings_params.clear();
472 params().clearLayoutModules();
473 params().pdfoptions().clear();
475 for (int i = 0; i < 4; ++i) {
476 params().user_defined_bullet(i) = ITEMIZE_DEFAULTS[i];
477 params().temp_bullet(i) = ITEMIZE_DEFAULTS[i];
480 ErrorList & errorList = pimpl_->errorLists["Parse"];
484 string const token = lex.getString();
489 if (token == "\\end_header")
493 if (token == "\\begin_header") {
494 begin_header_line = line;
498 LYXERR(Debug::PARSER) << "Handling document header token: `"
499 << token << '\'' << endl;
501 string unknown = params().readToken(lex, token);
502 if (!unknown.empty()) {
503 if (unknown[0] != '\\' && token == "\\textclass") {
504 Alert::warning(_("Unknown document class"),
505 bformat(_("Using the default document class, because the "
506 "class %1$s is unknown."), from_utf8(unknown)));
509 docstring const s = bformat(_("Unknown token: "
513 errorList.push_back(ErrorItem(_("Document header error"),
518 if (begin_header_line) {
519 docstring const s = _("\\begin_header is missing");
520 errorList.push_back(ErrorItem(_("Document header error"),
524 return unknown_tokens;
529 // changed to be public and have one parameter
530 // Returns false if "\end_document" is not read (Asger)
531 bool Buffer::readDocument(Lexer & lex)
533 ErrorList & errorList = pimpl_->errorLists["Parse"];
537 string const token = lex.getString();
538 if (token != "\\begin_document") {
539 docstring const s = _("\\begin_document is missing");
540 errorList.push_back(ErrorItem(_("Document header error"),
544 // we are reading in a brand new document
545 BOOST_ASSERT(paragraphs().empty());
548 TextClass const & baseClass = textclasslist[params().getBaseClass()];
549 if (!baseClass.load(filePath())) {
550 string theclass = baseClass.name();
551 Alert::error(_("Can't load document class"), bformat(
552 _("Using the default document class, because the "
553 "class %1$s could not be loaded."), from_utf8(theclass)));
554 params().setBaseClass(defaultTextclass());
557 if (params().outputChanges) {
558 bool dvipost = LaTeXFeatures::isAvailable("dvipost");
559 bool xcolorsoul = LaTeXFeatures::isAvailable("soul") &&
560 LaTeXFeatures::isAvailable("xcolor");
562 if (!dvipost && !xcolorsoul) {
563 Alert::warning(_("Changes not shown in LaTeX output"),
564 _("Changes will not be highlighted in LaTeX output, "
565 "because neither dvipost nor xcolor/soul are installed.\n"
566 "Please install these packages or redefine "
567 "\\lyxadded and \\lyxdeleted in the LaTeX preamble."));
568 } else if (!xcolorsoul) {
569 Alert::warning(_("Changes not shown in LaTeX output"),
570 _("Changes will not be highlighted in LaTeX output "
571 "when using pdflatex, because xcolor and soul are not installed.\n"
572 "Please install both packages or redefine "
573 "\\lyxadded and \\lyxdeleted in the LaTeX preamble."));
578 bool const res = text().read(*this, lex, errorList);
579 for_each(text().paragraphs().begin(),
580 text().paragraphs().end(),
581 bind(&Paragraph::setInsetOwner, _1, &inset()));
587 // needed to insert the selection
588 void Buffer::insertStringAsLines(ParagraphList & pars,
589 pit_type & pit, pos_type & pos,
590 Font const & fn, docstring const & str, bool autobreakrows)
594 // insert the string, don't insert doublespace
595 bool space_inserted = true;
596 for (docstring::const_iterator cit = str.begin();
597 cit != str.end(); ++cit) {
598 Paragraph & par = pars[pit];
600 if (autobreakrows && (!par.empty() || par.allowEmpty())) {
601 breakParagraph(params(), pars, pit, pos,
602 par.layout()->isEnvironment());
605 space_inserted = true;
609 // do not insert consecutive spaces if !free_spacing
610 } else if ((*cit == ' ' || *cit == '\t') &&
611 space_inserted && !par.isFreeSpacing()) {
613 } else if (*cit == '\t') {
614 if (!par.isFreeSpacing()) {
615 // tabs are like spaces here
616 par.insertChar(pos, ' ', font, params().trackChanges);
618 space_inserted = true;
620 const pos_type n = 8 - pos % 8;
621 for (pos_type i = 0; i < n; ++i) {
622 par.insertChar(pos, ' ', font, params().trackChanges);
625 space_inserted = true;
627 } else if (!isPrintable(*cit)) {
628 // Ignore unprintables
631 // just insert the character
632 par.insertChar(pos, *cit, font, params().trackChanges);
634 space_inserted = (*cit == ' ');
641 bool Buffer::readString(std::string const & s)
643 params().compressed = false;
645 // remove dummy empty par
646 paragraphs().clear();
648 std::istringstream is(s);
650 FileName const name(tempName());
651 switch (readFile(lex, name, true)) {
655 // We need to call lyx2lyx, so write the input to a file
656 std::ofstream os(name.toFilesystemEncoding().c_str());
659 return readFile(name);
669 bool Buffer::readFile(FileName const & filename)
671 FileName fname(filename);
672 // Check if the file is compressed.
673 string format = filename.guessFormatFromContents();
674 if (format == "zip") {
675 // decompress to a temp directory
676 LYXERR(Debug::FILES) << filename << " is in zip format. Unzip to " << temppath() << endl;
677 ::unzipToDir(filename.toFilesystemEncoding(), temppath());
679 FileName lyxfile(addName(temppath(), "content.lyx"));
680 // if both manifest.txt and file.lyx exist, this is am embedded file
681 if (lyxfile.exists()) {
682 params().embedded = true;
686 // The embedded lyx file can also be compressed, for backward compatibility
687 format = fname.guessFormatFromContents();
688 if (format == "gzip" || format == "zip" || format == "compress")
689 params().compressed = true;
691 // remove dummy empty par
692 paragraphs().clear();
695 if (readFile(lex, fname) != success)
702 bool Buffer::isFullyLoaded() const
704 return pimpl_->file_fully_loaded;
708 void Buffer::setFullyLoaded(bool value)
710 pimpl_->file_fully_loaded = value;
714 Buffer::ReadStatus Buffer::readFile(Lexer & lex, FileName const & filename,
717 BOOST_ASSERT(!filename.empty());
720 Alert::error(_("Document could not be read"),
721 bformat(_("%1$s could not be read."), from_utf8(filename.absFilename())));
726 string const token = lex.getString();
729 Alert::error(_("Document could not be read"),
730 bformat(_("%1$s could not be read."), from_utf8(filename.absFilename())));
734 // the first token _must_ be...
735 if (token != "\\lyxformat") {
736 lyxerr << "Token: " << token << endl;
738 Alert::error(_("Document format failure"),
739 bformat(_("%1$s is not a LyX document."),
740 from_utf8(filename.absFilename())));
745 string tmp_format = lex.getString();
746 //lyxerr << "LyX Format: `" << tmp_format << '\'' << endl;
747 // if present remove ".," from string.
748 string::size_type dot = tmp_format.find_first_of(".,");
749 //lyxerr << " dot found at " << dot << endl;
750 if (dot != string::npos)
751 tmp_format.erase(dot, 1);
752 int const file_format = convert<int>(tmp_format);
753 //lyxerr << "format: " << file_format << endl;
755 // save timestamp and checksum of the original disk file, making sure
756 // to not overwrite them with those of the file created in the tempdir
757 // when it has to be converted to the current format.
758 if (!pimpl_->checksum_) {
759 // Save the timestamp and checksum of disk file. If filename is an
760 // emergency file, save the timestamp and sum of the original lyx file
761 // because isExternallyModified will check for this file. (BUG4193)
762 string diskfile = filename.toFilesystemEncoding();
763 if (suffixIs(diskfile, ".emergency"))
764 diskfile = diskfile.substr(0, diskfile.size() - 10);
765 saveCheckSum(FileName(diskfile));
768 if (file_format != LYX_FORMAT) {
771 // lyx2lyx would fail
774 FileName const tmpfile(tempName());
775 if (tmpfile.empty()) {
776 Alert::error(_("Conversion failed"),
777 bformat(_("%1$s is from a different"
778 " version of LyX, but a temporary"
779 " file for converting it could"
781 from_utf8(filename.absFilename())));
784 FileName const lyx2lyx = libFileSearch("lyx2lyx", "lyx2lyx");
785 if (lyx2lyx.empty()) {
786 Alert::error(_("Conversion script not found"),
787 bformat(_("%1$s is from a different"
788 " version of LyX, but the"
789 " conversion script lyx2lyx"
790 " could not be found."),
791 from_utf8(filename.absFilename())));
794 ostringstream command;
795 command << os::python()
796 << ' ' << quoteName(lyx2lyx.toFilesystemEncoding())
797 << " -t " << convert<string>(LYX_FORMAT)
798 << " -o " << quoteName(tmpfile.toFilesystemEncoding())
799 << ' ' << quoteName(filename.toFilesystemEncoding());
800 string const command_str = command.str();
802 LYXERR(Debug::INFO) << "Running '"
803 << command_str << '\''
806 cmd_ret const ret = runCommand(command_str);
807 if (ret.first != 0) {
808 Alert::error(_("Conversion script failed"),
809 bformat(_("%1$s is from a different version"
810 " of LyX, but the lyx2lyx script"
811 " failed to convert it."),
812 from_utf8(filename.absFilename())));
815 bool const ret = readFile(tmpfile);
816 // Do stuff with tmpfile name and buffer name here.
817 return ret ? success : failure;
822 if (readDocument(lex)) {
823 Alert::error(_("Document format failure"),
824 bformat(_("%1$s ended unexpectedly, which means"
825 " that it is probably corrupted."),
826 from_utf8(filename.absFilename())));
829 pimpl_->file_fully_loaded = true;
834 // Should probably be moved to somewhere else: BufferView? LyXView?
835 bool Buffer::save() const
837 // We don't need autosaves in the immediate future. (Asger)
838 resetAutosaveTimers();
840 string const encodedFilename = pimpl_->filename.toFilesystemEncoding();
843 bool madeBackup = false;
845 // make a backup if the file already exists
846 if (lyxrc.make_backup && fs::exists(encodedFilename)) {
847 backupName = FileName(absFileName() + '~');
848 if (!lyxrc.backupdir_path.empty()) {
849 string const mangledName =
850 subst(subst(os::internal_path(
851 backupName.absFilename()), '/', '!'), ':', '!');
852 backupName = FileName(addName(lyxrc.backupdir_path,
856 fs::copy_file(encodedFilename, backupName.toFilesystemEncoding(), false);
858 } catch (fs::filesystem_error const & fe) {
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() << endl;
867 // ask if the disk file has been externally modified (use checksum method)
868 if (fs::exists(encodedFilename) && 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.used(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: "
998 << encoding << "..." << endl;
1000 odocfstream ofs(encoding);
1001 if (!openFileWrite(ofs, fname))
1004 //TexStream ts(ofs.rdbuf(), &texrow());
1006 bool failed_export = false;
1009 writeLaTeXSource(ofs, original_path,
1010 runparams, output_preamble, output_body);
1012 catch (iconv_codecvt_facet_exception & e) {
1013 lyxerr << "Caught iconv exception: " << e.what() << endl;
1014 failed_export = true;
1016 catch (std::exception const & e) {
1017 lyxerr << "Caught \"normal\" exception: " << e.what() << endl;
1018 failed_export = true;
1021 lyxerr << "Caught some really weird exception..." << endl;
1022 LyX::cref().emergencyCleanup();
1028 failed_export = true;
1029 lyxerr << "File '" << fname << "' was not closed properly." << endl;
1032 if (failed_export) {
1033 Alert::error(_("Encoding error"),
1034 _("Some characters of your document are probably not "
1035 "representable in the chosen encoding.\n"
1036 "Changing the document encoding to utf8 could help."));
1043 void Buffer::writeLaTeXSource(odocstream & os,
1044 string const & original_path,
1045 OutputParams const & runparams_in,
1046 bool const output_preamble, bool const output_body)
1048 OutputParams runparams = runparams_in;
1050 // validate the buffer.
1051 LYXERR(Debug::LATEX) << " Validating buffer..." << endl;
1052 LaTeXFeatures features(*this, params(), runparams);
1054 LYXERR(Debug::LATEX) << " Buffer validation done." << endl;
1056 // The starting paragraph of the coming rows is the
1057 // first paragraph of the document. (Asger)
1058 if (output_preamble && runparams.nice) {
1059 os << "%% LyX " << lyx_version << " created this file. "
1060 "For more info, see http://www.lyx.org/.\n"
1061 "%% Do not edit unless you really know what "
1066 LYXERR(Debug::INFO) << "lyx document header finished" << endl;
1067 // There are a few differences between nice LaTeX and usual files:
1068 // usual is \batchmode and has a
1069 // special input@path to allow the including of figures
1070 // with either \input or \includegraphics (what figinsets do).
1071 // input@path is set when the actual parameter
1072 // original_path is set. This is done for usual tex-file, but not
1073 // for nice-latex-file. (Matthias 250696)
1074 // Note that input@path is only needed for something the user does
1075 // in the preamble, included .tex files or ERT, files included by
1076 // LyX work without it.
1077 if (output_preamble) {
1078 if (!runparams.nice) {
1079 // code for usual, NOT nice-latex-file
1080 os << "\\batchmode\n"; // changed
1081 // from \nonstopmode
1084 if (!original_path.empty()) {
1086 // We don't know the encoding of inputpath
1087 docstring const inputpath = from_utf8(latex_path(original_path));
1088 os << "\\makeatletter\n"
1089 << "\\def\\input@path{{"
1090 << inputpath << "/}}\n"
1091 << "\\makeatother\n";
1097 // Write the preamble
1098 runparams.use_babel = params().writeLaTeX(os, features, texrow());
1104 os << "\\begin{document}\n";
1106 } // output_preamble
1108 texrow().start(paragraphs().begin()->id(), 0);
1110 LYXERR(Debug::INFO) << "preamble finished, now the body." << endl;
1112 if (!lyxrc.language_auto_begin &&
1113 !params().language->babel().empty()) {
1115 os << from_utf8(subst(lyxrc.language_command_begin,
1117 params().language->babel()))
1122 Encoding const & encoding = params().encoding();
1123 if (encoding.package() == Encoding::CJK) {
1124 // Open a CJK environment, since in contrast to the encodings
1125 // handled by inputenc the document encoding is not set in
1126 // the preamble if it is handled by CJK.sty.
1127 os << "\\begin{CJK}{" << from_ascii(encoding.latexName())
1132 // if we are doing a real file with body, even if this is the
1133 // child of some other buffer, let's cut the link here.
1134 // This happens for example if only a child document is printed.
1135 string save_parentname;
1136 if (output_preamble) {
1137 save_parentname = params().parentname;
1138 params().parentname.erase();
1141 loadChildDocuments();
1144 latexParagraphs(*this, paragraphs(), os, texrow(), runparams);
1146 // Restore the parenthood if needed
1147 if (output_preamble)
1148 params().parentname = save_parentname;
1150 // add this just in case after all the paragraphs
1154 if (encoding.package() == Encoding::CJK) {
1155 // Close the open CJK environment.
1156 // latexParagraphs will have opened one even if the last text
1158 os << "\\end{CJK}\n";
1162 if (!lyxrc.language_auto_end &&
1163 !params().language->babel().empty()) {
1164 os << from_utf8(subst(lyxrc.language_command_end,
1166 params().language->babel()))
1171 if (output_preamble) {
1172 os << "\\end{document}\n";
1175 LYXERR(Debug::LATEX) << "makeLaTeXFile...done" << endl;
1177 LYXERR(Debug::LATEX) << "LaTeXFile for inclusion made."
1180 runparams_in.encoding = runparams.encoding;
1182 // Just to be sure. (Asger)
1185 LYXERR(Debug::INFO) << "Finished making LaTeX file." << endl;
1186 LYXERR(Debug::INFO) << "Row count was " << texrow().rows() - 1
1191 bool Buffer::isLatex() const
1193 return params().getTextClass().outputType() == LATEX;
1197 bool Buffer::isLiterate() const
1199 return params().getTextClass().outputType() == LITERATE;
1203 bool Buffer::isDocBook() const
1205 return params().getTextClass().outputType() == DOCBOOK;
1209 void Buffer::makeDocBookFile(FileName const & fname,
1210 OutputParams const & runparams,
1211 bool const body_only)
1213 LYXERR(Debug::LATEX) << "makeDocBookFile..." << endl;
1217 if (!openFileWrite(ofs, fname))
1220 writeDocBookSource(ofs, fname.absFilename(), runparams, body_only);
1224 lyxerr << "File '" << fname << "' was not closed properly." << endl;
1228 void Buffer::writeDocBookSource(odocstream & os, string const & fname,
1229 OutputParams const & runparams,
1230 bool const only_body)
1232 LaTeXFeatures features(*this, params(), runparams);
1237 TextClass const & tclass = params().getTextClass();
1238 string const top_element = tclass.latexname();
1241 if (runparams.flavor == OutputParams::XML)
1242 os << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
1245 os << "<!DOCTYPE " << from_ascii(top_element) << ' ';
1248 if (! tclass.class_header().empty())
1249 os << from_ascii(tclass.class_header());
1250 else if (runparams.flavor == OutputParams::XML)
1251 os << "PUBLIC \"-//OASIS//DTD DocBook XML//EN\" "
1252 << "\"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd\"";
1254 os << " PUBLIC \"-//OASIS//DTD DocBook V4.2//EN\"";
1256 docstring preamble = from_utf8(params().preamble);
1257 if (runparams.flavor != OutputParams::XML ) {
1258 preamble += "<!ENTITY % output.print.png \"IGNORE\">\n";
1259 preamble += "<!ENTITY % output.print.pdf \"IGNORE\">\n";
1260 preamble += "<!ENTITY % output.print.eps \"IGNORE\">\n";
1261 preamble += "<!ENTITY % output.print.bmp \"IGNORE\">\n";
1264 string const name = runparams.nice
1265 ? changeExtension(absFileName(), ".sgml") : fname;
1266 preamble += features.getIncludedFiles(name);
1267 preamble += features.getLyXSGMLEntities();
1269 if (!preamble.empty()) {
1270 os << "\n [ " << preamble << " ]";
1275 string top = top_element;
1277 if (runparams.flavor == OutputParams::XML)
1278 top += params().language->code();
1280 top += params().language->code().substr(0,2);
1283 if (!params().options.empty()) {
1285 top += params().options;
1288 os << "<!-- " << ((runparams.flavor == OutputParams::XML)? "XML" : "SGML")
1289 << " file was created by LyX " << lyx_version
1290 << "\n See http://www.lyx.org/ for more information -->\n";
1292 params().getTextClass().counters().reset();
1294 loadChildDocuments();
1296 sgml::openTag(os, top);
1298 docbookParagraphs(paragraphs(), *this, os, runparams);
1299 sgml::closeTag(os, top_element);
1303 // chktex should be run with these flags disabled: 3, 22, 25, 30, 38(?)
1304 // Other flags: -wall -v0 -x
1305 int Buffer::runChktex()
1309 // get LaTeX-Filename
1310 FileName const path(temppath());
1311 string const name = addName(path.absFilename(), latexName());
1312 string const org_path = filePath();
1314 support::Path p(path); // path to LaTeX file
1315 message(_("Running chktex..."));
1317 // Generate the LaTeX file if neccessary
1318 OutputParams runparams(¶ms().encoding());
1319 runparams.flavor = OutputParams::LATEX;
1320 runparams.nice = false;
1321 makeLaTeXFile(FileName(name), org_path, runparams);
1324 Chktex chktex(lyxrc.chktex_command, onlyFilename(name), filePath());
1325 int const res = chktex.run(terr); // run chktex
1328 Alert::error(_("chktex failure"),
1329 _("Could not run chktex successfully."));
1330 } else if (res > 0) {
1331 ErrorList & errlist = pimpl_->errorLists["ChkTeX"];
1333 bufferErrors(terr, errlist);
1344 void Buffer::validate(LaTeXFeatures & features) const
1346 TextClass const & tclass = params().getTextClass();
1348 if (params().outputChanges) {
1349 bool dvipost = LaTeXFeatures::isAvailable("dvipost");
1350 bool xcolorsoul = LaTeXFeatures::isAvailable("soul") &&
1351 LaTeXFeatures::isAvailable("xcolor");
1353 if (features.runparams().flavor == OutputParams::LATEX) {
1355 features.require("ct-dvipost");
1356 features.require("dvipost");
1357 } else if (xcolorsoul) {
1358 features.require("ct-xcolor-soul");
1359 features.require("soul");
1360 features.require("xcolor");
1362 features.require("ct-none");
1364 } else if (features.runparams().flavor == OutputParams::PDFLATEX ) {
1366 features.require("ct-xcolor-soul");
1367 features.require("soul");
1368 features.require("xcolor");
1369 features.require("pdfcolmk"); // improves color handling in PDF output
1371 features.require("ct-none");
1376 // AMS Style is at document level
1377 if (params().use_amsmath == BufferParams::package_on
1378 || tclass.provides("amsmath"))
1379 features.require("amsmath");
1380 if (params().use_esint == BufferParams::package_on)
1381 features.require("esint");
1383 loadChildDocuments();
1385 for_each(paragraphs().begin(), paragraphs().end(),
1386 boost::bind(&Paragraph::validate, _1, boost::ref(features)));
1388 // the bullet shapes are buffer level not paragraph level
1389 // so they are tested here
1390 for (int i = 0; i < 4; ++i) {
1391 if (params().user_defined_bullet(i) != ITEMIZE_DEFAULTS[i]) {
1392 int const font = params().user_defined_bullet(i).getFont();
1394 int const c = params()
1395 .user_defined_bullet(i)
1402 features.require("latexsym");
1404 } else if (font == 1) {
1405 features.require("amssymb");
1406 } else if ((font >= 2 && font <= 5)) {
1407 features.require("pifont");
1412 if (lyxerr.debugging(Debug::LATEX)) {
1413 features.showStruct();
1418 void Buffer::getLabelList(vector<docstring> & list) const
1420 /// if this is a child document and the parent is already loaded
1421 /// Use the parent's list instead [ale990407]
1422 Buffer const * tmp = masterBuffer();
1424 lyxerr << "masterBuffer() failed!" << endl;
1428 tmp->getLabelList(list);
1432 loadChildDocuments();
1434 for (InsetIterator it = inset_iterator_begin(inset()); it; ++it)
1435 it.nextInset()->getLabelList(*this, list);
1439 void Buffer::updateBibfilesCache()
1441 // if this is a child document and the parent is already loaded
1442 // update the parent's cache instead
1443 Buffer * tmp = masterBuffer();
1446 tmp->updateBibfilesCache();
1450 bibfilesCache_.clear();
1451 for (InsetIterator it = inset_iterator_begin(inset()); it; ++it) {
1452 if (it->lyxCode() == BIBTEX_CODE) {
1453 InsetBibtex const & inset =
1454 static_cast<InsetBibtex const &>(*it);
1455 vector<FileName> const bibfiles = inset.getFiles(*this);
1456 bibfilesCache_.insert(bibfilesCache_.end(),
1459 } else if (it->lyxCode() == INCLUDE_CODE) {
1460 InsetInclude & inset =
1461 static_cast<InsetInclude &>(*it);
1462 inset.updateBibfilesCache(*this);
1463 vector<FileName> const & bibfiles =
1464 inset.getBibfilesCache(*this);
1465 bibfilesCache_.insert(bibfilesCache_.end(),
1473 vector<FileName> const & Buffer::getBibfilesCache() const
1475 // if this is a child document and the parent is already loaded
1476 // use the parent's cache instead
1477 Buffer const * tmp = masterBuffer();
1480 return tmp->getBibfilesCache();
1482 // We update the cache when first used instead of at loading time.
1483 if (bibfilesCache_.empty())
1484 const_cast<Buffer *>(this)->updateBibfilesCache();
1486 return bibfilesCache_;
1490 bool Buffer::isDepClean(string const & name) const
1492 DepClean::const_iterator const it = pimpl_->dep_clean.find(name);
1493 if (it == pimpl_->dep_clean.end())
1499 void Buffer::markDepClean(string const & name)
1501 pimpl_->dep_clean[name] = true;
1505 bool Buffer::dispatch(string const & command, bool * result)
1507 return dispatch(lyxaction.lookupFunc(command), result);
1511 bool Buffer::dispatch(FuncRequest const & func, bool * result)
1513 bool dispatched = true;
1515 switch (func.action) {
1516 case LFUN_BUFFER_EXPORT: {
1517 bool const tmp = doExport(to_utf8(func.argument()), false);
1530 void Buffer::changeLanguage(Language const * from, Language const * to)
1535 for_each(par_iterator_begin(),
1537 bind(&Paragraph::changeLanguage, _1, params(), from, to));
1541 bool Buffer::isMultiLingual() const
1543 ParConstIterator end = par_iterator_end();
1544 for (ParConstIterator it = par_iterator_begin(); it != end; ++it)
1545 if (it->isMultiLingual(params()))
1552 ParIterator Buffer::getParFromID(int const id) const
1554 ParConstIterator it = par_iterator_begin();
1555 ParConstIterator const end = par_iterator_end();
1558 // John says this is called with id == -1 from undo
1559 lyxerr << "getParFromID(), id: " << id << endl;
1563 for (; it != end; ++it)
1571 bool Buffer::hasParWithID(int const id) const
1573 ParConstIterator const it = getParFromID(id);
1574 return it != par_iterator_end();
1578 ParIterator Buffer::par_iterator_begin()
1580 return lyx::par_iterator_begin(inset());
1584 ParIterator Buffer::par_iterator_end()
1586 return lyx::par_iterator_end(inset());
1590 ParConstIterator Buffer::par_iterator_begin() const
1592 return lyx::par_const_iterator_begin(inset());
1596 ParConstIterator Buffer::par_iterator_end() const
1598 return lyx::par_const_iterator_end(inset());
1602 Language const * Buffer::language() const
1604 return params().language;
1608 docstring const Buffer::B_(string const & l10n) const
1610 return params().B_(l10n);
1614 bool Buffer::isClean() const
1616 return pimpl_->lyx_clean;
1620 bool Buffer::isBakClean() const
1622 return pimpl_->bak_clean;
1626 bool Buffer::isExternallyModified(CheckMethod method) const
1628 BOOST_ASSERT(pimpl_->filename.exists());
1629 // if method == timestamp, check timestamp before checksum
1630 return (method == checksum_method
1631 || pimpl_->timestamp_ != pimpl_->filename.lastModified())
1632 && pimpl_->checksum_ != sum(pimpl_->filename);
1636 void Buffer::saveCheckSum(FileName const & file) const
1638 if (file.exists()) {
1639 pimpl_->timestamp_ = file.lastModified();
1640 pimpl_->checksum_ = sum(file);
1642 // in the case of save to a new file.
1643 pimpl_->timestamp_ = 0;
1644 pimpl_->checksum_ = 0;
1649 void Buffer::markClean() const
1651 if (!pimpl_->lyx_clean) {
1652 pimpl_->lyx_clean = true;
1655 // if the .lyx file has been saved, we don't need an
1657 pimpl_->bak_clean = true;
1661 void Buffer::markBakClean() const
1663 pimpl_->bak_clean = true;
1667 void Buffer::setUnnamed(bool flag)
1669 pimpl_->unnamed = flag;
1673 bool Buffer::isUnnamed() const
1675 return pimpl_->unnamed;
1679 // FIXME: this function should be moved to buffer_pimpl.C
1680 void Buffer::markDirty()
1682 if (pimpl_->lyx_clean) {
1683 pimpl_->lyx_clean = false;
1686 pimpl_->bak_clean = false;
1688 DepClean::iterator it = pimpl_->dep_clean.begin();
1689 DepClean::const_iterator const end = pimpl_->dep_clean.end();
1691 for (; it != end; ++it)
1696 string Buffer::absFileName() const
1698 return pimpl_->filename.absFilename();
1702 string const & Buffer::filePath() const
1704 return params().filepath;
1708 bool Buffer::isReadonly() const
1710 return pimpl_->read_only;
1714 void Buffer::setParentName(string const & name)
1716 if (name == pimpl_->filename.absFilename())
1717 // Avoids recursive include.
1718 params().parentname.clear();
1720 params().parentname = name;
1724 Buffer const * Buffer::masterBuffer() const
1726 if (!params().parentname.empty()
1727 && theBufferList().exists(params().parentname)) {
1728 Buffer const * buf = theBufferList().getBuffer(params().parentname);
1729 //We need to check if the parent is us...
1730 //FIXME RECURSIVE INCLUDE
1731 //This is not sufficient, since recursive includes could be downstream.
1732 if (buf && buf != this)
1733 return buf->masterBuffer();
1740 Buffer * Buffer::masterBuffer()
1742 if (!params().parentname.empty()
1743 && theBufferList().exists(params().parentname)) {
1744 Buffer * buf = theBufferList().getBuffer(params().parentname);
1745 //We need to check if the parent is us...
1746 //FIXME RECURSIVE INCLUDE
1747 //This is not sufficient, since recursive includes could be downstream.
1748 if (buf && buf != this)
1749 return buf->masterBuffer();
1756 bool Buffer::hasMacro(docstring const & name, Paragraph const & par) const
1758 Impl::PositionToMacroMap::iterator it;
1759 it = pimpl_->macros[name].upper_bound(par.macrocontextPosition());
1760 if( it != pimpl_->macros[name].end() )
1763 // If there is a master buffer, query that
1764 const Buffer *master = masterBuffer();
1765 if (master && master!=this)
1766 return master->hasMacro(name);
1768 return MacroTable::globalMacros().has(name);
1772 bool Buffer::hasMacro(docstring const & name) const
1774 if( !pimpl_->macros[name].empty() )
1777 // If there is a master buffer, query that
1778 const Buffer *master = masterBuffer();
1779 if (master && master!=this)
1780 return master->hasMacro(name);
1782 return MacroTable::globalMacros().has(name);
1786 MacroData const & Buffer::getMacro(docstring const & name, Paragraph const & par) const
1788 Impl::PositionToMacroMap::iterator it;
1789 it = pimpl_->macros[name].upper_bound(par.macrocontextPosition());
1790 if( it != pimpl_->macros[name].end() )
1793 // If there is a master buffer, query that
1794 const Buffer *master = masterBuffer();
1795 if (master && master!=this)
1796 return master->getMacro(name);
1798 return MacroTable::globalMacros().get(name);
1802 MacroData const & Buffer::getMacro(docstring const & name) const
1804 Impl::PositionToMacroMap::iterator it;
1805 it = pimpl_->macros[name].begin();
1806 if( it != pimpl_->macros[name].end() )
1809 // If there is a master buffer, query that
1810 const Buffer *master = masterBuffer();
1811 if (master && master!=this)
1812 return master->getMacro(name);
1814 return MacroTable::globalMacros().get(name);
1818 void Buffer::updateMacros()
1820 // start with empty table
1821 pimpl_->macros = Impl::NameToPositionMacroMap();
1823 // Iterate over buffer
1824 ParagraphList & pars = text().paragraphs();
1825 for (size_t i = 0, n = pars.size(); i != n; ++i) {
1826 // set position again
1827 pars[i].setMacrocontextPosition(i);
1829 //lyxerr << "searching main par " << i
1830 // << " for macro definitions" << std::endl;
1831 InsetList const & insets = pars[i].insetList();
1832 InsetList::const_iterator it = insets.begin();
1833 InsetList::const_iterator end = insets.end();
1834 for ( ; it != end; ++it) {
1835 if (it->inset->lyxCode() != MATHMACRO_CODE)
1839 MathMacroTemplate const & macroTemplate
1840 = static_cast<MathMacroTemplate const &>(*it->inset);
1843 if (macroTemplate.validMacro()) {
1844 MacroData macro = macroTemplate.asMacroData();
1847 // call hasMacro here instead of directly querying mc to
1848 // also take the master document into consideration
1849 macro.setRedefinition(hasMacro(macroTemplate.name()));
1851 // register macro (possibly overwrite the previous one of this paragraph)
1852 pimpl_->macros[macroTemplate.name()][i] = macro;
1859 void Buffer::changeRefsIfUnique(docstring const & from, docstring const & to,
1862 //FIXME: This does not work for child documents yet.
1863 BOOST_ASSERT(code == CITE_CODE || code == REF_CODE);
1864 // Check if the label 'from' appears more than once
1865 vector<docstring> labels;
1868 if (code == CITE_CODE) {
1870 keys.fillWithBibKeys(this);
1871 BiblioInfo::const_iterator bit = keys.begin();
1872 BiblioInfo::const_iterator bend = keys.end();
1874 for (; bit != bend; ++bit)
1876 labels.push_back(bit->first);
1879 getLabelList(labels);
1880 paramName = "reference";
1883 if (std::count(labels.begin(), labels.end(), from) > 1)
1886 for (InsetIterator it = inset_iterator_begin(inset()); it; ++it) {
1887 if (it->lyxCode() == code) {
1888 InsetCommand & inset = static_cast<InsetCommand &>(*it);
1889 docstring const oldValue = inset.getParam(paramName);
1890 if (oldValue == from)
1891 inset.setParam(paramName, to);
1897 void Buffer::getSourceCode(odocstream & os, pit_type par_begin,
1898 pit_type par_end, bool full_source)
1900 OutputParams runparams(¶ms().encoding());
1901 runparams.nice = true;
1902 runparams.flavor = OutputParams::LATEX;
1903 runparams.linelen = lyxrc.plaintext_linelen;
1904 // No side effect of file copying and image conversion
1905 runparams.dryrun = true;
1909 os << "% " << _("Preview source code") << "\n\n";
1913 writeLaTeXSource(os, filePath(), runparams, true, true);
1915 writeDocBookSource(os, absFileName(), runparams, false);
1918 runparams.par_begin = par_begin;
1919 runparams.par_end = par_end;
1920 if (par_begin + 1 == par_end)
1922 << bformat(_("Preview source code for paragraph %1$d"), par_begin)
1926 << bformat(_("Preview source code from paragraph %1$s to %2$s"),
1927 convert<docstring>(par_begin),
1928 convert<docstring>(par_end - 1))
1932 // output paragraphs
1934 latexParagraphs(*this, paragraphs(), os, texrow(), runparams);
1937 docbookParagraphs(paragraphs(), *this, os, runparams);
1943 ErrorList const & Buffer::errorList(string const & type) const
1945 static ErrorList const emptyErrorList;
1946 std::map<string, ErrorList>::const_iterator I = pimpl_->errorLists.find(type);
1947 if (I == pimpl_->errorLists.end())
1948 return emptyErrorList;
1954 ErrorList & Buffer::errorList(string const & type)
1956 return pimpl_->errorLists[type];
1960 void Buffer::structureChanged() const
1963 gui_->structureChanged();
1967 void Buffer::errors(std::string const & err) const
1974 void Buffer::message(docstring const & msg) const
1981 void Buffer::setBusy(bool on) const
1988 void Buffer::setReadOnly(bool on) const
1991 gui_->setReadOnly(on);
1995 void Buffer::updateTitles() const
1998 gui_->updateTitles();
2002 void Buffer::resetAutosaveTimers() const
2005 gui_->resetAutosaveTimers();
2009 void Buffer::setGuiDelegate(frontend::GuiBufferDelegate * gui)
2018 class AutoSaveBuffer : public support::ForkedProcess {
2021 AutoSaveBuffer(Buffer const & buffer, FileName const & fname)
2022 : buffer_(buffer), fname_(fname) {}
2024 virtual boost::shared_ptr<ForkedProcess> clone() const
2026 return boost::shared_ptr<ForkedProcess>(new AutoSaveBuffer(*this));
2031 command_ = to_utf8(bformat(_("Auto-saving %1$s"),
2032 from_utf8(fname_.absFilename())));
2033 return run(DontWait);
2037 virtual int generateChild();
2039 Buffer const & buffer_;
2044 #if !defined (HAVE_FORK)
2048 int AutoSaveBuffer::generateChild()
2050 // tmp_ret will be located (usually) in /tmp
2051 // will that be a problem?
2052 pid_t const pid = fork();
2053 // If you want to debug the autosave
2054 // you should set pid to -1, and comment out the fork.
2055 if (pid == 0 || pid == -1) {
2056 // pid = -1 signifies that lyx was unable
2057 // to fork. But we will do the save
2059 bool failed = false;
2061 FileName const tmp_ret(tempName(FileName(), "lyxauto"));
2062 if (!tmp_ret.empty()) {
2063 buffer_.writeFile(tmp_ret);
2064 // assume successful write of tmp_ret
2065 if (!rename(tmp_ret, fname_)) {
2067 // most likely couldn't move between
2068 // filesystems unless write of tmp_ret
2069 // failed so remove tmp file (if it
2078 // failed to write/rename tmp_ret so try writing direct
2079 if (!buffer_.writeFile(fname_)) {
2080 // It is dangerous to do this in the child,
2081 // but safe in the parent, so...
2082 if (pid == -1) // emit message signal.
2083 buffer_.message(_("Autosave failed!"));
2086 if (pid == 0) { // we are the child so...
2096 // Perfect target for a thread...
2097 void Buffer::autoSave() const
2099 if (isBakClean() || isReadonly()) {
2100 // We don't save now, but we'll try again later
2101 resetAutosaveTimers();
2105 // emit message signal.
2106 message(_("Autosaving current document..."));
2108 // create autosave filename
2109 string fname = filePath();
2111 fname += onlyFilename(absFileName());
2114 AutoSaveBuffer autosave(*this, FileName(fname));
2118 resetAutosaveTimers();
2122 /** Write a buffer to a new file name and rename the buffer
2123 according to the new file name.
2125 This function is e.g. used by menu callbacks and
2126 LFUN_BUFFER_WRITE_AS.
2128 If 'newname' is empty (the default), the user is asked via a
2129 dialog for the buffer's new name and location.
2131 If 'newname' is non-empty and has an absolute path, that is used.
2132 Otherwise the base directory of the buffer is used as the base
2133 for any relative path in 'newname'.
2136 bool Buffer::writeAs(string const & newname)
2138 string fname = absFileName();
2139 string const oldname = fname;
2141 if (newname.empty()) { /// No argument? Ask user through dialog
2144 FileDialog dlg(_("Choose a filename to save document as"),
2145 LFUN_BUFFER_WRITE_AS);
2146 dlg.setButton1(_("Documents|#o#O"), from_utf8(lyxrc.document_path));
2147 dlg.setButton2(_("Templates|#T#t"), from_utf8(lyxrc.template_path));
2149 if (!support::isLyXFilename(fname))
2152 support::FileFilterList const filter(_("LyX Documents (*.lyx)"));
2154 FileDialog::Result result =
2155 dlg.save(from_utf8(onlyPath(fname)),
2157 from_utf8(onlyFilename(fname)));
2159 if (result.first == FileDialog::Later)
2162 fname = to_utf8(result.second);
2167 // Make sure the absolute filename ends with appropriate suffix
2168 fname = makeAbsPath(fname).absFilename();
2169 if (!support::isLyXFilename(fname))
2173 fname = makeAbsPath(newname, onlyPath(oldname)).absFilename();
2175 if (FileName(fname).exists()) {
2176 docstring const file = makeDisplayPath(fname, 30);
2177 docstring text = bformat(_("The document %1$s already "
2178 "exists.\n\nDo you want to "
2179 "overwrite that document?"),
2181 int const ret = Alert::prompt(_("Overwrite document?"),
2182 text, 0, 1, _("&Overwrite"), _("&Cancel"));
2188 // Ok, change the name of the buffer
2191 bool unnamed = isUnnamed();
2193 saveCheckSum(FileName(fname));
2196 setFileName(oldname);
2197 setUnnamed(unnamed);
2198 saveCheckSum(FileName(oldname));
2202 removeAutosaveFile(oldname);
2207 bool Buffer::menuWrite()
2210 LyX::ref().session().lastFiles().add(FileName(absFileName()));
2214 // FIXME: we don't tell the user *WHY* the save failed !!
2216 docstring const file = makeDisplayPath(absFileName(), 30);
2218 docstring text = bformat(_("The document %1$s could not be saved.\n\n"
2219 "Do you want to rename the document and "
2220 "try again?"), file);
2221 int const ret = Alert::prompt(_("Rename and save?"),
2222 text, 0, 1, _("&Rename"), _("&Cancel"));
2231 void Buffer::loadChildDocuments() const
2233 bool parse_error = false;
2235 for (InsetIterator it = inset_iterator_begin(inset()); it; ++it) {
2236 if (it->lyxCode() != INCLUDE_CODE)
2238 InsetCommand const & inset = static_cast<InsetCommand const &>(*it);
2239 InsetCommandParams const & ip = inset.params();
2240 Buffer * child = loadIfNeeded(*this, ip);
2243 parse_error |= !child->errorList("Parse").empty();
2244 child->loadChildDocuments();
2247 if (use_gui && masterBuffer() == this)
2248 updateLabels(*this);
2252 string Buffer::bufferFormat() const
2262 bool Buffer::doExport(string const & format, bool put_in_tempdir,
2263 string & result_file)
2265 string backend_format;
2266 OutputParams runparams(¶ms().encoding());
2267 runparams.flavor = OutputParams::LATEX;
2268 runparams.linelen = lyxrc.plaintext_linelen;
2269 vector<string> backs = backends();
2270 if (find(backs.begin(), backs.end(), format) == backs.end()) {
2271 // Get shortest path to format
2272 Graph::EdgePath path;
2273 for (vector<string>::const_iterator it = backs.begin();
2274 it != backs.end(); ++it) {
2275 Graph::EdgePath p = theConverters().getPath(*it, format);
2276 if (!p.empty() && (path.empty() || p.size() < path.size())) {
2277 backend_format = *it;
2282 runparams.flavor = theConverters().getFlavor(path);
2284 Alert::error(_("Couldn't export file"),
2285 bformat(_("No information for exporting the format %1$s."),
2286 formats.prettyName(format)));
2290 backend_format = format;
2291 // FIXME: Don't hardcode format names here, but use a flag
2292 if (backend_format == "pdflatex")
2293 runparams.flavor = OutputParams::PDFLATEX;
2296 string filename = latexName(false);
2297 filename = addName(temppath(), filename);
2298 filename = changeExtension(filename,
2299 formats.extension(backend_format));
2301 // Plain text backend
2302 if (backend_format == "text")
2303 writePlaintextFile(*this, FileName(filename), runparams);
2305 else if (backend_format == "lyx")
2306 writeFile(FileName(filename));
2308 else if (isDocBook()) {
2309 runparams.nice = !put_in_tempdir;
2310 makeDocBookFile(FileName(filename), runparams);
2313 else if (backend_format == format) {
2314 runparams.nice = true;
2315 if (!makeLaTeXFile(FileName(filename), string(), runparams))
2317 } else if (!lyxrc.tex_allows_spaces
2318 && support::contains(filePath(), ' ')) {
2319 Alert::error(_("File name error"),
2320 _("The directory path to the document cannot contain spaces."));
2323 runparams.nice = false;
2324 if (!makeLaTeXFile(FileName(filename), filePath(), runparams))
2328 string const error_type = (format == "program")
2329 ? "Build" : bufferFormat();
2330 string const ext = formats.extension(format);
2331 FileName const tmp_result_file(changeExtension(filename, ext));
2332 bool const success = theConverters().convert(this, FileName(filename),
2333 tmp_result_file, FileName(absFileName()), backend_format, format,
2334 errorList(error_type));
2335 // Emit the signal to show the error list.
2336 if (format != backend_format)
2342 result_file = tmp_result_file.absFilename();
2344 result_file = changeExtension(absFileName(), ext);
2345 // We need to copy referenced files (e. g. included graphics
2346 // if format == "dvi") to the result dir.
2347 vector<ExportedFile> const files =
2348 runparams.exportdata->externalFiles(format);
2349 string const dest = onlyPath(result_file);
2350 CopyStatus status = SUCCESS;
2351 for (vector<ExportedFile>::const_iterator it = files.begin();
2352 it != files.end() && status != CANCEL; ++it) {
2354 formats.getFormatFromFile(it->sourceName);
2355 status = copyFile(fmt, it->sourceName,
2356 makeAbsPath(it->exportName, dest),
2357 it->exportName, status == FORCE);
2359 if (status == CANCEL) {
2360 message(_("Document export cancelled."));
2361 } else if (tmp_result_file.exists()) {
2362 // Finally copy the main file
2363 status = copyFile(format, tmp_result_file,
2364 FileName(result_file), result_file,
2366 message(bformat(_("Document exported as %1$s "
2368 formats.prettyName(format),
2369 makeDisplayPath(result_file)));
2371 // This must be a dummy converter like fax (bug 1888)
2372 message(bformat(_("Document exported as %1$s"),
2373 formats.prettyName(format)));
2381 bool Buffer::doExport(string const & format, bool put_in_tempdir)
2384 return doExport(format, put_in_tempdir, result_file);
2388 bool Buffer::preview(string const & format)
2391 if (!doExport(format, true, result_file))
2393 return formats.view(*this, FileName(result_file), format);
2397 bool Buffer::isExportable(string const & format) const
2399 vector<string> backs = backends();
2400 for (vector<string>::const_iterator it = backs.begin();
2401 it != backs.end(); ++it)
2402 if (theConverters().isReachable(*it, format))
2408 vector<Format const *> Buffer::exportableFormats(bool only_viewable) const
2410 vector<string> backs = backends();
2411 vector<Format const *> result =
2412 theConverters().getReachable(backs[0], only_viewable, true);
2413 for (vector<string>::const_iterator it = backs.begin() + 1;
2414 it != backs.end(); ++it) {
2415 vector<Format const *> r =
2416 theConverters().getReachable(*it, only_viewable, false);
2417 result.insert(result.end(), r.begin(), r.end());
2423 vector<string> Buffer::backends() const
2426 if (params().getTextClass().isTeXClassAvailable()) {
2427 v.push_back(bufferFormat());
2428 // FIXME: Don't hardcode format names here, but use a flag
2429 if (v.back() == "latex")
2430 v.push_back("pdflatex");
2432 v.push_back("text");
2438 bool Buffer::readFileHelper(FileName const & s)
2440 // File information about normal file
2442 docstring const file = makeDisplayPath(s.absFilename(), 50);
2443 docstring text = bformat(_("The specified document\n%1$s"
2444 "\ncould not be read."), file);
2445 Alert::error(_("Could not read document"), text);
2449 // Check if emergency save file exists and is newer.
2450 FileName const e(s.absFilename() + ".emergency");
2452 if (e.exists() && s.exists() && e.lastModified() > s.lastModified()) {
2453 docstring const file = makeDisplayPath(s.absFilename(), 20);
2454 docstring const text =
2455 bformat(_("An emergency save of the document "
2457 "Recover emergency save?"), file);
2458 switch (Alert::prompt(_("Load emergency save?"), text, 0, 2,
2459 _("&Recover"), _("&Load Original"),
2463 // the file is not saved if we load the emergency file.
2473 // Now check if autosave file is newer.
2474 FileName const a(onlyPath(s.absFilename()) + '#' + onlyFilename(s.absFilename()) + '#');
2476 if (a.exists() && s.exists() && a.lastModified() > s.lastModified()) {
2477 docstring const file = makeDisplayPath(s.absFilename(), 20);
2478 docstring const text =
2479 bformat(_("The backup of the document "
2480 "%1$s is newer.\n\nLoad the "
2481 "backup instead?"), file);
2482 switch (Alert::prompt(_("Load backup?"), text, 0, 2,
2483 _("&Load backup"), _("Load &original"),
2487 // the file is not saved if we load the autosave file.
2491 // Here we delete the autosave
2502 bool Buffer::loadLyXFile(FileName const & s)
2504 if (s.isReadable()) {
2505 if (readFileHelper(s)) {
2506 lyxvc().file_found_hook(s);
2507 if (!s.isWritable())
2512 docstring const file = makeDisplayPath(s.absFilename(), 20);
2513 // Here we probably should run
2514 if (LyXVC::file_not_found_hook(s)) {
2515 docstring const text =
2516 bformat(_("Do you want to retrieve the document"
2517 " %1$s from version control?"), file);
2518 int const ret = Alert::prompt(_("Retrieve from version control?"),
2519 text, 0, 1, _("&Retrieve"), _("&Cancel"));
2522 // How can we know _how_ to do the checkout?
2523 // With the current VC support it has to be,
2524 // a RCS file since CVS do not have special ,v files.
2526 return loadLyXFile(s);
2534 void Buffer::bufferErrors(TeXErrors const & terr, ErrorList & errorList) const
2536 TeXErrors::Errors::const_iterator cit = terr.begin();
2537 TeXErrors::Errors::const_iterator end = terr.end();
2539 for (; cit != end; ++cit) {
2542 int errorRow = cit->error_in_line;
2543 bool found = texrow().getIdFromRow(errorRow, id_start,
2549 found = texrow().getIdFromRow(errorRow, id_end, pos_end);
2550 } while (found && id_start == id_end && pos_start == pos_end);
2552 errorList.push_back(ErrorItem(cit->error_desc,
2553 cit->error_text, id_start, pos_start, pos_end));