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 string Buffer::logName(LogType * type) const
406 string const filename = latexName(false);
408 if (filename.empty()) {
414 string const path = temppath();
416 FileName const fname(addName(temppath(),
417 onlyFilename(changeExtension(filename,
419 FileName const bname(
420 addName(path, onlyFilename(
421 changeExtension(filename,
422 formats.extension("literate") + ".out"))));
424 // If no Latex log or Build log is newer, show Build log
426 if (bname.exists() &&
427 (!fname.exists() || fname.lastModified() < bname.lastModified())) {
428 LYXERR(Debug::FILES) << "Log name calculated as: " << bname << endl;
431 return bname.absFilename();
433 LYXERR(Debug::FILES) << "Log name calculated as: " << fname << endl;
436 return fname.absFilename();
440 void Buffer::setReadonly(bool const flag)
442 if (pimpl_->read_only != flag) {
443 pimpl_->read_only = flag;
449 void Buffer::setFileName(string const & newfile)
451 pimpl_->filename = makeAbsPath(newfile);
452 params().filepath = onlyPath(pimpl_->filename.absFilename());
453 setReadonly(pimpl_->filename.isReadOnly());
458 int Buffer::readHeader(Lexer & lex)
460 int unknown_tokens = 0;
462 int begin_header_line = -1;
464 // Initialize parameters that may be/go lacking in header:
465 params().branchlist().clear();
466 params().preamble.erase();
467 params().options.erase();
468 params().float_placement.erase();
469 params().paperwidth.erase();
470 params().paperheight.erase();
471 params().leftmargin.erase();
472 params().rightmargin.erase();
473 params().topmargin.erase();
474 params().bottommargin.erase();
475 params().headheight.erase();
476 params().headsep.erase();
477 params().footskip.erase();
478 params().listings_params.clear();
479 params().clearLayoutModules();
480 params().pdfoptions().clear();
482 for (int i = 0; i < 4; ++i) {
483 params().user_defined_bullet(i) = ITEMIZE_DEFAULTS[i];
484 params().temp_bullet(i) = ITEMIZE_DEFAULTS[i];
487 ErrorList & errorList = pimpl_->errorLists["Parse"];
491 string const token = lex.getString();
496 if (token == "\\end_header")
500 if (token == "\\begin_header") {
501 begin_header_line = line;
505 LYXERR(Debug::PARSER) << "Handling document header token: `"
506 << token << '\'' << endl;
508 string unknown = params().readToken(lex, token);
509 if (!unknown.empty()) {
510 if (unknown[0] != '\\' && token == "\\textclass") {
511 Alert::warning(_("Unknown document class"),
512 bformat(_("Using the default document class, because the "
513 "class %1$s is unknown."), from_utf8(unknown)));
516 docstring const s = bformat(_("Unknown token: "
520 errorList.push_back(ErrorItem(_("Document header error"),
525 if (begin_header_line) {
526 docstring const s = _("\\begin_header is missing");
527 errorList.push_back(ErrorItem(_("Document header error"),
531 return unknown_tokens;
536 // changed to be public and have one parameter
537 // Returns false if "\end_document" is not read (Asger)
538 bool Buffer::readDocument(Lexer & lex)
540 ErrorList & errorList = pimpl_->errorLists["Parse"];
544 string const token = lex.getString();
545 if (token != "\\begin_document") {
546 docstring const s = _("\\begin_document is missing");
547 errorList.push_back(ErrorItem(_("Document header error"),
551 // we are reading in a brand new document
552 BOOST_ASSERT(paragraphs().empty());
555 TextClass const & baseClass = textclasslist[params().getBaseClass()];
556 if (!baseClass.load(filePath())) {
557 string theclass = baseClass.name();
558 Alert::error(_("Can't load document class"), bformat(
559 _("Using the default document class, because the "
560 "class %1$s could not be loaded."), from_utf8(theclass)));
561 params().setBaseClass(defaultTextclass());
564 if (params().outputChanges) {
565 bool dvipost = LaTeXFeatures::isAvailable("dvipost");
566 bool xcolorsoul = LaTeXFeatures::isAvailable("soul") &&
567 LaTeXFeatures::isAvailable("xcolor");
569 if (!dvipost && !xcolorsoul) {
570 Alert::warning(_("Changes not shown in LaTeX output"),
571 _("Changes will not be highlighted in LaTeX output, "
572 "because neither dvipost nor xcolor/soul are installed.\n"
573 "Please install these packages or redefine "
574 "\\lyxadded and \\lyxdeleted in the LaTeX preamble."));
575 } else if (!xcolorsoul) {
576 Alert::warning(_("Changes not shown in LaTeX output"),
577 _("Changes will not be highlighted in LaTeX output "
578 "when using pdflatex, because xcolor and soul are not installed.\n"
579 "Please install both packages or redefine "
580 "\\lyxadded and \\lyxdeleted in the LaTeX preamble."));
585 bool const res = text().read(*this, lex, errorList);
586 for_each(text().paragraphs().begin(),
587 text().paragraphs().end(),
588 bind(&Paragraph::setInsetOwner, _1, &inset()));
594 // needed to insert the selection
595 void Buffer::insertStringAsLines(ParagraphList & pars,
596 pit_type & pit, pos_type & pos,
597 Font const & fn, docstring const & str, bool autobreakrows)
601 // insert the string, don't insert doublespace
602 bool space_inserted = true;
603 for (docstring::const_iterator cit = str.begin();
604 cit != str.end(); ++cit) {
605 Paragraph & par = pars[pit];
607 if (autobreakrows && (!par.empty() || par.allowEmpty())) {
608 breakParagraph(params(), pars, pit, pos,
609 par.layout()->isEnvironment());
612 space_inserted = true;
616 // do not insert consecutive spaces if !free_spacing
617 } else if ((*cit == ' ' || *cit == '\t') &&
618 space_inserted && !par.isFreeSpacing()) {
620 } else if (*cit == '\t') {
621 if (!par.isFreeSpacing()) {
622 // tabs are like spaces here
623 par.insertChar(pos, ' ', font, params().trackChanges);
625 space_inserted = true;
627 const pos_type n = 8 - pos % 8;
628 for (pos_type i = 0; i < n; ++i) {
629 par.insertChar(pos, ' ', font, params().trackChanges);
632 space_inserted = true;
634 } else if (!isPrintable(*cit)) {
635 // Ignore unprintables
638 // just insert the character
639 par.insertChar(pos, *cit, font, params().trackChanges);
641 space_inserted = (*cit == ' ');
648 bool Buffer::readString(std::string const & s)
650 params().compressed = false;
652 // remove dummy empty par
653 paragraphs().clear();
655 std::istringstream is(s);
657 FileName const name(tempName());
658 switch (readFile(lex, name, true)) {
662 // We need to call lyx2lyx, so write the input to a file
663 std::ofstream os(name.toFilesystemEncoding().c_str());
666 return readFile(name);
676 bool Buffer::readFile(FileName const & filename)
678 FileName fname(filename);
679 // Check if the file is compressed.
680 string format = filename.guessFormatFromContents();
681 if (format == "zip") {
682 // decompress to a temp directory
683 LYXERR(Debug::FILES) << filename << " is in zip format. Unzip to " << temppath() << endl;
684 ::unzipToDir(filename.toFilesystemEncoding(), temppath());
686 FileName lyxfile(addName(temppath(), "content.lyx"));
687 // if both manifest.txt and file.lyx exist, this is am embedded file
688 if (lyxfile.exists()) {
689 params().embedded = true;
693 // The embedded lyx file can also be compressed, for backward compatibility
694 format = fname.guessFormatFromContents();
695 if (format == "gzip" || format == "zip" || format == "compress")
696 params().compressed = true;
698 // remove dummy empty par
699 paragraphs().clear();
702 if (readFile(lex, fname) != success)
709 bool Buffer::isFullyLoaded() const
711 return pimpl_->file_fully_loaded;
715 void Buffer::setFullyLoaded(bool value)
717 pimpl_->file_fully_loaded = value;
721 Buffer::ReadStatus Buffer::readFile(Lexer & lex, FileName const & filename,
724 BOOST_ASSERT(!filename.empty());
727 Alert::error(_("Document could not be read"),
728 bformat(_("%1$s could not be read."), from_utf8(filename.absFilename())));
733 string const token = lex.getString();
736 Alert::error(_("Document could not be read"),
737 bformat(_("%1$s could not be read."), from_utf8(filename.absFilename())));
741 // the first token _must_ be...
742 if (token != "\\lyxformat") {
743 lyxerr << "Token: " << token << endl;
745 Alert::error(_("Document format failure"),
746 bformat(_("%1$s is not a LyX document."),
747 from_utf8(filename.absFilename())));
752 string tmp_format = lex.getString();
753 //lyxerr << "LyX Format: `" << tmp_format << '\'' << endl;
754 // if present remove ".," from string.
755 string::size_type dot = tmp_format.find_first_of(".,");
756 //lyxerr << " dot found at " << dot << endl;
757 if (dot != string::npos)
758 tmp_format.erase(dot, 1);
759 int const file_format = convert<int>(tmp_format);
760 //lyxerr << "format: " << file_format << endl;
762 // save timestamp and checksum of the original disk file, making sure
763 // to not overwrite them with those of the file created in the tempdir
764 // when it has to be converted to the current format.
765 if (!pimpl_->checksum_) {
766 // Save the timestamp and checksum of disk file. If filename is an
767 // emergency file, save the timestamp and sum of the original lyx file
768 // because isExternallyModified will check for this file. (BUG4193)
769 string diskfile = filename.toFilesystemEncoding();
770 if (suffixIs(diskfile, ".emergency"))
771 diskfile = diskfile.substr(0, diskfile.size() - 10);
772 saveCheckSum(FileName(diskfile));
775 if (file_format != LYX_FORMAT) {
778 // lyx2lyx would fail
781 FileName const tmpfile(tempName());
782 if (tmpfile.empty()) {
783 Alert::error(_("Conversion failed"),
784 bformat(_("%1$s is from a different"
785 " version of LyX, but a temporary"
786 " file for converting it could"
788 from_utf8(filename.absFilename())));
791 FileName const lyx2lyx = libFileSearch("lyx2lyx", "lyx2lyx");
792 if (lyx2lyx.empty()) {
793 Alert::error(_("Conversion script not found"),
794 bformat(_("%1$s is from a different"
795 " version of LyX, but the"
796 " conversion script lyx2lyx"
797 " could not be found."),
798 from_utf8(filename.absFilename())));
801 ostringstream command;
802 command << os::python()
803 << ' ' << quoteName(lyx2lyx.toFilesystemEncoding())
804 << " -t " << convert<string>(LYX_FORMAT)
805 << " -o " << quoteName(tmpfile.toFilesystemEncoding())
806 << ' ' << quoteName(filename.toFilesystemEncoding());
807 string const command_str = command.str();
809 LYXERR(Debug::INFO) << "Running '"
810 << command_str << '\''
813 cmd_ret const ret = runCommand(command_str);
814 if (ret.first != 0) {
815 Alert::error(_("Conversion script failed"),
816 bformat(_("%1$s is from a different version"
817 " of LyX, but the lyx2lyx script"
818 " failed to convert it."),
819 from_utf8(filename.absFilename())));
822 bool const ret = readFile(tmpfile);
823 // Do stuff with tmpfile name and buffer name here.
824 return ret ? success : failure;
829 if (readDocument(lex)) {
830 Alert::error(_("Document format failure"),
831 bformat(_("%1$s ended unexpectedly, which means"
832 " that it is probably corrupted."),
833 from_utf8(filename.absFilename())));
836 pimpl_->file_fully_loaded = true;
841 // Should probably be moved to somewhere else: BufferView? LyXView?
842 bool Buffer::save() const
844 // We don't need autosaves in the immediate future. (Asger)
845 resetAutosaveTimers();
847 string const encodedFilename = pimpl_->filename.toFilesystemEncoding();
850 bool madeBackup = false;
852 // make a backup if the file already exists
853 if (lyxrc.make_backup && fileName().exists()) {
854 backupName = FileName(absFileName() + '~');
855 if (!lyxrc.backupdir_path.empty()) {
856 string const mangledName =
857 subst(subst(os::internal_path(
858 backupName.absFilename()), '/', '!'), ':', '!');
859 backupName = FileName(addName(lyxrc.backupdir_path,
863 fs::copy_file(encodedFilename, backupName.toFilesystemEncoding(), false);
865 } catch (fs::filesystem_error const & fe) {
866 Alert::error(_("Backup failure"),
867 bformat(_("Cannot create backup file %1$s.\n"
868 "Please check whether the directory exists and is writeable."),
869 from_utf8(backupName.absFilename())));
870 LYXERR(Debug::DEBUG) << "Fs error: " << fe.what() << endl;
874 // ask if the disk file has been externally modified (use checksum method)
875 if (fileName().exists() && isExternallyModified(checksum_method)) {
876 docstring const file = makeDisplayPath(absFileName(), 20);
877 docstring text = bformat(_("Document %1$s has been externally modified. Are you sure "
878 "you want to overwrite this file?"), file);
879 int const ret = Alert::prompt(_("Overwrite modified file?"),
880 text, 1, 1, _("&Overwrite"), _("&Cancel"));
885 if (writeFile(pimpl_->filename)) {
887 removeAutosaveFile(absFileName());
888 saveCheckSum(pimpl_->filename);
891 // Saving failed, so backup is not backup
893 rename(backupName, pimpl_->filename);
899 bool Buffer::writeFile(FileName const & fname) const
901 if (pimpl_->read_only && fname == pimpl_->filename)
907 if (params().embedded)
908 // first write the .lyx file to the temporary directory
909 content = FileName(addName(temppath(), "content.lyx"));
913 if (params().compressed) {
914 gz::ogzstream ofs(content.toFilesystemEncoding().c_str(), ios::out|ios::trunc);
920 ofstream ofs(content.toFilesystemEncoding().c_str(), ios::out|ios::trunc);
927 if (retval && params().embedded) {
928 // write file.lyx and all the embedded files to the zip file fname
929 // if embedding is enabled
930 return pimpl_->embedded_files.writeFile(fname);
936 bool Buffer::write(ostream & ofs) const
939 // Use the standard "C" locale for file output.
940 ofs.imbue(std::locale::classic());
943 // The top of the file should not be written by params().
945 // write out a comment in the top of the file
946 ofs << "#LyX " << lyx_version
947 << " created this file. For more info see http://www.lyx.org/\n"
948 << "\\lyxformat " << LYX_FORMAT << "\n"
949 << "\\begin_document\n";
952 /// For each author, set 'used' to true if there is a change
953 /// by this author in the document; otherwise set it to 'false'.
954 AuthorList::Authors::const_iterator a_it = params().authors().begin();
955 AuthorList::Authors::const_iterator a_end = params().authors().end();
956 for (; a_it != a_end; ++a_it)
957 a_it->second.setUsed(false);
959 ParIterator const end = par_iterator_end();
960 ParIterator it = par_iterator_begin();
961 for ( ; it != end; ++it)
962 it->checkAuthors(params().authors());
964 // now write out the buffer parameters.
965 ofs << "\\begin_header\n";
966 params().writeFile(ofs);
967 ofs << "\\end_header\n";
970 ofs << "\n\\begin_body\n";
971 text().write(*this, ofs);
972 ofs << "\n\\end_body\n";
974 // Write marker that shows file is complete
975 ofs << "\\end_document" << endl;
977 // Shouldn't really be needed....
980 // how to check if close went ok?
981 // Following is an attempt... (BE 20001011)
983 // good() returns false if any error occured, including some
985 // bad() returns true if something bad happened in the buffer,
986 // which should include file system full errors.
991 lyxerr << "File was not closed properly." << endl;
998 bool Buffer::makeLaTeXFile(FileName const & fname,
999 string const & original_path,
1000 OutputParams const & runparams,
1001 bool output_preamble, bool output_body)
1003 string const encoding = runparams.encoding->iconvName();
1004 LYXERR(Debug::LATEX) << "makeLaTeXFile encoding: "
1005 << encoding << "..." << endl;
1007 odocfstream ofs(encoding);
1008 if (!openFileWrite(ofs, fname))
1011 //TexStream ts(ofs.rdbuf(), &texrow());
1013 bool failed_export = false;
1016 writeLaTeXSource(ofs, original_path,
1017 runparams, output_preamble, output_body);
1019 catch (iconv_codecvt_facet_exception & e) {
1020 lyxerr << "Caught iconv exception: " << e.what() << endl;
1021 failed_export = true;
1023 catch (std::exception const & e) {
1024 lyxerr << "Caught \"normal\" exception: " << e.what() << endl;
1025 failed_export = true;
1028 lyxerr << "Caught some really weird exception..." << endl;
1029 LyX::cref().emergencyCleanup();
1035 failed_export = true;
1036 lyxerr << "File '" << fname << "' was not closed properly." << endl;
1039 if (failed_export) {
1040 Alert::error(_("Encoding error"),
1041 _("Some characters of your document are probably not "
1042 "representable in the chosen encoding.\n"
1043 "Changing the document encoding to utf8 could help."));
1050 void Buffer::writeLaTeXSource(odocstream & os,
1051 string const & original_path,
1052 OutputParams const & runparams_in,
1053 bool const output_preamble, bool const output_body)
1055 OutputParams runparams = runparams_in;
1057 // validate the buffer.
1058 LYXERR(Debug::LATEX) << " Validating buffer..." << endl;
1059 LaTeXFeatures features(*this, params(), runparams);
1061 LYXERR(Debug::LATEX) << " Buffer validation done." << endl;
1063 // The starting paragraph of the coming rows is the
1064 // first paragraph of the document. (Asger)
1065 if (output_preamble && runparams.nice) {
1066 os << "%% LyX " << lyx_version << " created this file. "
1067 "For more info, see http://www.lyx.org/.\n"
1068 "%% Do not edit unless you really know what "
1073 LYXERR(Debug::INFO) << "lyx document header finished" << endl;
1074 // There are a few differences between nice LaTeX and usual files:
1075 // usual is \batchmode and has a
1076 // special input@path to allow the including of figures
1077 // with either \input or \includegraphics (what figinsets do).
1078 // input@path is set when the actual parameter
1079 // original_path is set. This is done for usual tex-file, but not
1080 // for nice-latex-file. (Matthias 250696)
1081 // Note that input@path is only needed for something the user does
1082 // in the preamble, included .tex files or ERT, files included by
1083 // LyX work without it.
1084 if (output_preamble) {
1085 if (!runparams.nice) {
1086 // code for usual, NOT nice-latex-file
1087 os << "\\batchmode\n"; // changed
1088 // from \nonstopmode
1091 if (!original_path.empty()) {
1093 // We don't know the encoding of inputpath
1094 docstring const inputpath = from_utf8(latex_path(original_path));
1095 os << "\\makeatletter\n"
1096 << "\\def\\input@path{{"
1097 << inputpath << "/}}\n"
1098 << "\\makeatother\n";
1104 // Write the preamble
1105 runparams.use_babel = params().writeLaTeX(os, features, texrow());
1111 os << "\\begin{document}\n";
1113 } // output_preamble
1115 texrow().start(paragraphs().begin()->id(), 0);
1117 LYXERR(Debug::INFO) << "preamble finished, now the body." << endl;
1119 if (!lyxrc.language_auto_begin &&
1120 !params().language->babel().empty()) {
1122 os << from_utf8(subst(lyxrc.language_command_begin,
1124 params().language->babel()))
1129 Encoding const & encoding = params().encoding();
1130 if (encoding.package() == Encoding::CJK) {
1131 // Open a CJK environment, since in contrast to the encodings
1132 // handled by inputenc the document encoding is not set in
1133 // the preamble if it is handled by CJK.sty.
1134 os << "\\begin{CJK}{" << from_ascii(encoding.latexName())
1139 // if we are doing a real file with body, even if this is the
1140 // child of some other buffer, let's cut the link here.
1141 // This happens for example if only a child document is printed.
1142 string save_parentname;
1143 if (output_preamble) {
1144 save_parentname = params().parentname;
1145 params().parentname.erase();
1148 loadChildDocuments();
1151 latexParagraphs(*this, paragraphs(), os, texrow(), runparams);
1153 // Restore the parenthood if needed
1154 if (output_preamble)
1155 params().parentname = save_parentname;
1157 // add this just in case after all the paragraphs
1161 if (encoding.package() == Encoding::CJK) {
1162 // Close the open CJK environment.
1163 // latexParagraphs will have opened one even if the last text
1165 os << "\\end{CJK}\n";
1169 if (!lyxrc.language_auto_end &&
1170 !params().language->babel().empty()) {
1171 os << from_utf8(subst(lyxrc.language_command_end,
1173 params().language->babel()))
1178 if (output_preamble) {
1179 os << "\\end{document}\n";
1182 LYXERR(Debug::LATEX) << "makeLaTeXFile...done" << endl;
1184 LYXERR(Debug::LATEX) << "LaTeXFile for inclusion made."
1187 runparams_in.encoding = runparams.encoding;
1189 // Just to be sure. (Asger)
1192 LYXERR(Debug::INFO) << "Finished making LaTeX file." << endl;
1193 LYXERR(Debug::INFO) << "Row count was " << texrow().rows() - 1
1198 bool Buffer::isLatex() const
1200 return params().getTextClass().outputType() == LATEX;
1204 bool Buffer::isLiterate() const
1206 return params().getTextClass().outputType() == LITERATE;
1210 bool Buffer::isDocBook() const
1212 return params().getTextClass().outputType() == DOCBOOK;
1216 void Buffer::makeDocBookFile(FileName const & fname,
1217 OutputParams const & runparams,
1218 bool const body_only)
1220 LYXERR(Debug::LATEX) << "makeDocBookFile..." << endl;
1224 if (!openFileWrite(ofs, fname))
1227 writeDocBookSource(ofs, fname.absFilename(), runparams, body_only);
1231 lyxerr << "File '" << fname << "' was not closed properly." << endl;
1235 void Buffer::writeDocBookSource(odocstream & os, string const & fname,
1236 OutputParams const & runparams,
1237 bool const only_body)
1239 LaTeXFeatures features(*this, params(), runparams);
1244 TextClass const & tclass = params().getTextClass();
1245 string const top_element = tclass.latexname();
1248 if (runparams.flavor == OutputParams::XML)
1249 os << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
1252 os << "<!DOCTYPE " << from_ascii(top_element) << ' ';
1255 if (! tclass.class_header().empty())
1256 os << from_ascii(tclass.class_header());
1257 else if (runparams.flavor == OutputParams::XML)
1258 os << "PUBLIC \"-//OASIS//DTD DocBook XML//EN\" "
1259 << "\"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd\"";
1261 os << " PUBLIC \"-//OASIS//DTD DocBook V4.2//EN\"";
1263 docstring preamble = from_utf8(params().preamble);
1264 if (runparams.flavor != OutputParams::XML ) {
1265 preamble += "<!ENTITY % output.print.png \"IGNORE\">\n";
1266 preamble += "<!ENTITY % output.print.pdf \"IGNORE\">\n";
1267 preamble += "<!ENTITY % output.print.eps \"IGNORE\">\n";
1268 preamble += "<!ENTITY % output.print.bmp \"IGNORE\">\n";
1271 string const name = runparams.nice
1272 ? changeExtension(absFileName(), ".sgml") : fname;
1273 preamble += features.getIncludedFiles(name);
1274 preamble += features.getLyXSGMLEntities();
1276 if (!preamble.empty()) {
1277 os << "\n [ " << preamble << " ]";
1282 string top = top_element;
1284 if (runparams.flavor == OutputParams::XML)
1285 top += params().language->code();
1287 top += params().language->code().substr(0,2);
1290 if (!params().options.empty()) {
1292 top += params().options;
1295 os << "<!-- " << ((runparams.flavor == OutputParams::XML)? "XML" : "SGML")
1296 << " file was created by LyX " << lyx_version
1297 << "\n See http://www.lyx.org/ for more information -->\n";
1299 params().getTextClass().counters().reset();
1301 loadChildDocuments();
1303 sgml::openTag(os, top);
1305 docbookParagraphs(paragraphs(), *this, os, runparams);
1306 sgml::closeTag(os, top_element);
1310 // chktex should be run with these flags disabled: 3, 22, 25, 30, 38(?)
1311 // Other flags: -wall -v0 -x
1312 int Buffer::runChktex()
1316 // get LaTeX-Filename
1317 FileName const path(temppath());
1318 string const name = addName(path.absFilename(), latexName());
1319 string const org_path = filePath();
1321 support::Path p(path); // path to LaTeX file
1322 message(_("Running chktex..."));
1324 // Generate the LaTeX file if neccessary
1325 OutputParams runparams(¶ms().encoding());
1326 runparams.flavor = OutputParams::LATEX;
1327 runparams.nice = false;
1328 makeLaTeXFile(FileName(name), org_path, runparams);
1331 Chktex chktex(lyxrc.chktex_command, onlyFilename(name), filePath());
1332 int const res = chktex.run(terr); // run chktex
1335 Alert::error(_("chktex failure"),
1336 _("Could not run chktex successfully."));
1337 } else if (res > 0) {
1338 ErrorList & errlist = pimpl_->errorLists["ChkTeX"];
1340 bufferErrors(terr, errlist);
1351 void Buffer::validate(LaTeXFeatures & features) const
1353 TextClass const & tclass = params().getTextClass();
1355 if (params().outputChanges) {
1356 bool dvipost = LaTeXFeatures::isAvailable("dvipost");
1357 bool xcolorsoul = LaTeXFeatures::isAvailable("soul") &&
1358 LaTeXFeatures::isAvailable("xcolor");
1360 if (features.runparams().flavor == OutputParams::LATEX) {
1362 features.require("ct-dvipost");
1363 features.require("dvipost");
1364 } else if (xcolorsoul) {
1365 features.require("ct-xcolor-soul");
1366 features.require("soul");
1367 features.require("xcolor");
1369 features.require("ct-none");
1371 } else if (features.runparams().flavor == OutputParams::PDFLATEX ) {
1373 features.require("ct-xcolor-soul");
1374 features.require("soul");
1375 features.require("xcolor");
1376 features.require("pdfcolmk"); // improves color handling in PDF output
1378 features.require("ct-none");
1383 // AMS Style is at document level
1384 if (params().use_amsmath == BufferParams::package_on
1385 || tclass.provides("amsmath"))
1386 features.require("amsmath");
1387 if (params().use_esint == BufferParams::package_on)
1388 features.require("esint");
1390 loadChildDocuments();
1392 for_each(paragraphs().begin(), paragraphs().end(),
1393 boost::bind(&Paragraph::validate, _1, boost::ref(features)));
1395 // the bullet shapes are buffer level not paragraph level
1396 // so they are tested here
1397 for (int i = 0; i < 4; ++i) {
1398 if (params().user_defined_bullet(i) != ITEMIZE_DEFAULTS[i]) {
1399 int const font = params().user_defined_bullet(i).getFont();
1401 int const c = params()
1402 .user_defined_bullet(i)
1409 features.require("latexsym");
1411 } else if (font == 1) {
1412 features.require("amssymb");
1413 } else if ((font >= 2 && font <= 5)) {
1414 features.require("pifont");
1419 if (lyxerr.debugging(Debug::LATEX)) {
1420 features.showStruct();
1425 void Buffer::getLabelList(vector<docstring> & list) const
1427 /// if this is a child document and the parent is already loaded
1428 /// Use the parent's list instead [ale990407]
1429 Buffer const * tmp = masterBuffer();
1431 lyxerr << "masterBuffer() failed!" << endl;
1435 tmp->getLabelList(list);
1439 loadChildDocuments();
1441 for (InsetIterator it = inset_iterator_begin(inset()); it; ++it)
1442 it.nextInset()->getLabelList(*this, list);
1446 void Buffer::updateBibfilesCache()
1448 // if this is a child document and the parent is already loaded
1449 // update the parent's cache instead
1450 Buffer * tmp = masterBuffer();
1453 tmp->updateBibfilesCache();
1457 bibfilesCache_.clear();
1458 for (InsetIterator it = inset_iterator_begin(inset()); it; ++it) {
1459 if (it->lyxCode() == BIBTEX_CODE) {
1460 InsetBibtex const & inset =
1461 static_cast<InsetBibtex const &>(*it);
1462 vector<FileName> const bibfiles = inset.getFiles(*this);
1463 bibfilesCache_.insert(bibfilesCache_.end(),
1466 } else if (it->lyxCode() == INCLUDE_CODE) {
1467 InsetInclude & inset =
1468 static_cast<InsetInclude &>(*it);
1469 inset.updateBibfilesCache(*this);
1470 vector<FileName> const & bibfiles =
1471 inset.getBibfilesCache(*this);
1472 bibfilesCache_.insert(bibfilesCache_.end(),
1480 vector<FileName> const & Buffer::getBibfilesCache() const
1482 // if this is a child document and the parent is already loaded
1483 // use the parent's cache instead
1484 Buffer const * tmp = masterBuffer();
1487 return tmp->getBibfilesCache();
1489 // We update the cache when first used instead of at loading time.
1490 if (bibfilesCache_.empty())
1491 const_cast<Buffer *>(this)->updateBibfilesCache();
1493 return bibfilesCache_;
1497 bool Buffer::isDepClean(string const & name) const
1499 DepClean::const_iterator const it = pimpl_->dep_clean.find(name);
1500 if (it == pimpl_->dep_clean.end())
1506 void Buffer::markDepClean(string const & name)
1508 pimpl_->dep_clean[name] = true;
1512 bool Buffer::dispatch(string const & command, bool * result)
1514 return dispatch(lyxaction.lookupFunc(command), result);
1518 bool Buffer::dispatch(FuncRequest const & func, bool * result)
1520 bool dispatched = true;
1522 switch (func.action) {
1523 case LFUN_BUFFER_EXPORT: {
1524 bool const tmp = doExport(to_utf8(func.argument()), false);
1537 void Buffer::changeLanguage(Language const * from, Language const * to)
1542 for_each(par_iterator_begin(),
1544 bind(&Paragraph::changeLanguage, _1, params(), from, to));
1548 bool Buffer::isMultiLingual() const
1550 ParConstIterator end = par_iterator_end();
1551 for (ParConstIterator it = par_iterator_begin(); it != end; ++it)
1552 if (it->isMultiLingual(params()))
1559 ParIterator Buffer::getParFromID(int const id) const
1561 ParConstIterator it = par_iterator_begin();
1562 ParConstIterator const end = par_iterator_end();
1565 // John says this is called with id == -1 from undo
1566 lyxerr << "getParFromID(), id: " << id << endl;
1570 for (; it != end; ++it)
1578 bool Buffer::hasParWithID(int const id) const
1580 ParConstIterator const it = getParFromID(id);
1581 return it != par_iterator_end();
1585 ParIterator Buffer::par_iterator_begin()
1587 return lyx::par_iterator_begin(inset());
1591 ParIterator Buffer::par_iterator_end()
1593 return lyx::par_iterator_end(inset());
1597 ParConstIterator Buffer::par_iterator_begin() const
1599 return lyx::par_const_iterator_begin(inset());
1603 ParConstIterator Buffer::par_iterator_end() const
1605 return lyx::par_const_iterator_end(inset());
1609 Language const * Buffer::language() const
1611 return params().language;
1615 docstring const Buffer::B_(string const & l10n) const
1617 return params().B_(l10n);
1621 bool Buffer::isClean() const
1623 return pimpl_->lyx_clean;
1627 bool Buffer::isBakClean() const
1629 return pimpl_->bak_clean;
1633 bool Buffer::isExternallyModified(CheckMethod method) const
1635 BOOST_ASSERT(pimpl_->filename.exists());
1636 // if method == timestamp, check timestamp before checksum
1637 return (method == checksum_method
1638 || pimpl_->timestamp_ != pimpl_->filename.lastModified())
1639 && pimpl_->checksum_ != sum(pimpl_->filename);
1643 void Buffer::saveCheckSum(FileName const & file) const
1645 if (file.exists()) {
1646 pimpl_->timestamp_ = file.lastModified();
1647 pimpl_->checksum_ = sum(file);
1649 // in the case of save to a new file.
1650 pimpl_->timestamp_ = 0;
1651 pimpl_->checksum_ = 0;
1656 void Buffer::markClean() const
1658 if (!pimpl_->lyx_clean) {
1659 pimpl_->lyx_clean = true;
1662 // if the .lyx file has been saved, we don't need an
1664 pimpl_->bak_clean = true;
1668 void Buffer::markBakClean() const
1670 pimpl_->bak_clean = true;
1674 void Buffer::setUnnamed(bool flag)
1676 pimpl_->unnamed = flag;
1680 bool Buffer::isUnnamed() const
1682 return pimpl_->unnamed;
1686 // FIXME: this function should be moved to buffer_pimpl.C
1687 void Buffer::markDirty()
1689 if (pimpl_->lyx_clean) {
1690 pimpl_->lyx_clean = false;
1693 pimpl_->bak_clean = false;
1695 DepClean::iterator it = pimpl_->dep_clean.begin();
1696 DepClean::const_iterator const end = pimpl_->dep_clean.end();
1698 for (; it != end; ++it)
1703 FileName Buffer::fileName() const
1705 return pimpl_->filename;
1709 string Buffer::absFileName() const
1711 return pimpl_->filename.absFilename();
1715 string const & Buffer::filePath() const
1717 return params().filepath;
1721 bool Buffer::isReadonly() const
1723 return pimpl_->read_only;
1727 void Buffer::setParentName(string const & name)
1729 if (name == pimpl_->filename.absFilename())
1730 // Avoids recursive include.
1731 params().parentname.clear();
1733 params().parentname = name;
1737 Buffer const * Buffer::masterBuffer() const
1739 if (!params().parentname.empty()
1740 && theBufferList().exists(params().parentname)) {
1741 Buffer const * buf = theBufferList().getBuffer(params().parentname);
1742 //We need to check if the parent is us...
1743 //FIXME RECURSIVE INCLUDE
1744 //This is not sufficient, since recursive includes could be downstream.
1745 if (buf && buf != this)
1746 return buf->masterBuffer();
1753 Buffer * Buffer::masterBuffer()
1755 if (!params().parentname.empty()
1756 && theBufferList().exists(params().parentname)) {
1757 Buffer * buf = theBufferList().getBuffer(params().parentname);
1758 //We need to check if the parent is us...
1759 //FIXME RECURSIVE INCLUDE
1760 //This is not sufficient, since recursive includes could be downstream.
1761 if (buf && buf != this)
1762 return buf->masterBuffer();
1769 bool Buffer::hasMacro(docstring const & name, Paragraph const & par) const
1771 Impl::PositionToMacroMap::iterator it;
1772 it = pimpl_->macros[name].upper_bound(par.macrocontextPosition());
1773 if (it != pimpl_->macros[name].end())
1776 // If there is a master buffer, query that
1777 const Buffer * master = masterBuffer();
1778 if (master && master != this)
1779 return master->hasMacro(name);
1781 return MacroTable::globalMacros().has(name);
1785 bool Buffer::hasMacro(docstring const & name) const
1787 if( !pimpl_->macros[name].empty() )
1790 // If there is a master buffer, query that
1791 const Buffer * master = masterBuffer();
1792 if (master && master != this)
1793 return master->hasMacro(name);
1795 return MacroTable::globalMacros().has(name);
1799 MacroData const & Buffer::getMacro(docstring const & name,
1800 Paragraph const & par) const
1802 Impl::PositionToMacroMap::iterator it;
1803 it = pimpl_->macros[name].upper_bound(par.macrocontextPosition());
1804 if( it != pimpl_->macros[name].end() )
1807 // If there is a master buffer, query that
1808 const Buffer * master = masterBuffer();
1809 if (master && master != this)
1810 return master->getMacro(name);
1812 return MacroTable::globalMacros().get(name);
1816 MacroData const & Buffer::getMacro(docstring const & name) const
1818 Impl::PositionToMacroMap::iterator it;
1819 it = pimpl_->macros[name].begin();
1820 if( it != pimpl_->macros[name].end() )
1823 // If there is a master buffer, query that
1824 const Buffer * master = masterBuffer();
1825 if (master && master != this)
1826 return master->getMacro(name);
1828 return MacroTable::globalMacros().get(name);
1832 void Buffer::updateMacros()
1834 // start with empty table
1835 pimpl_->macros = Impl::NameToPositionMacroMap();
1837 // Iterate over buffer
1838 ParagraphList & pars = text().paragraphs();
1839 for (size_t i = 0, n = pars.size(); i != n; ++i) {
1840 // set position again
1841 pars[i].setMacrocontextPosition(i);
1843 //lyxerr << "searching main par " << i
1844 // << " for macro definitions" << std::endl;
1845 InsetList const & insets = pars[i].insetList();
1846 InsetList::const_iterator it = insets.begin();
1847 InsetList::const_iterator end = insets.end();
1848 for ( ; it != end; ++it) {
1849 if (it->inset->lyxCode() != MATHMACRO_CODE)
1853 MathMacroTemplate const & macroTemplate
1854 = static_cast<MathMacroTemplate const &>(*it->inset);
1857 if (macroTemplate.validMacro()) {
1858 MacroData macro = macroTemplate.asMacroData();
1861 // call hasMacro here instead of directly querying mc to
1862 // also take the master document into consideration
1863 macro.setRedefinition(hasMacro(macroTemplate.name()));
1865 // register macro (possibly overwrite the previous one of this paragraph)
1866 pimpl_->macros[macroTemplate.name()][i] = macro;
1873 void Buffer::changeRefsIfUnique(docstring const & from, docstring const & to,
1876 //FIXME: This does not work for child documents yet.
1877 BOOST_ASSERT(code == CITE_CODE || code == REF_CODE);
1878 // Check if the label 'from' appears more than once
1879 vector<docstring> labels;
1882 if (code == CITE_CODE) {
1884 keys.fillWithBibKeys(this);
1885 BiblioInfo::const_iterator bit = keys.begin();
1886 BiblioInfo::const_iterator bend = keys.end();
1888 for (; bit != bend; ++bit)
1890 labels.push_back(bit->first);
1893 getLabelList(labels);
1894 paramName = "reference";
1897 if (std::count(labels.begin(), labels.end(), from) > 1)
1900 for (InsetIterator it = inset_iterator_begin(inset()); it; ++it) {
1901 if (it->lyxCode() == code) {
1902 InsetCommand & inset = static_cast<InsetCommand &>(*it);
1903 docstring const oldValue = inset.getParam(paramName);
1904 if (oldValue == from)
1905 inset.setParam(paramName, to);
1911 void Buffer::getSourceCode(odocstream & os, pit_type par_begin,
1912 pit_type par_end, bool full_source)
1914 OutputParams runparams(¶ms().encoding());
1915 runparams.nice = true;
1916 runparams.flavor = OutputParams::LATEX;
1917 runparams.linelen = lyxrc.plaintext_linelen;
1918 // No side effect of file copying and image conversion
1919 runparams.dryrun = true;
1923 os << "% " << _("Preview source code") << "\n\n";
1927 writeLaTeXSource(os, filePath(), runparams, true, true);
1929 writeDocBookSource(os, absFileName(), runparams, false);
1932 runparams.par_begin = par_begin;
1933 runparams.par_end = par_end;
1934 if (par_begin + 1 == par_end)
1936 << bformat(_("Preview source code for paragraph %1$d"), par_begin)
1940 << bformat(_("Preview source code from paragraph %1$s to %2$s"),
1941 convert<docstring>(par_begin),
1942 convert<docstring>(par_end - 1))
1946 // output paragraphs
1948 latexParagraphs(*this, paragraphs(), os, texrow(), runparams);
1951 docbookParagraphs(paragraphs(), *this, os, runparams);
1957 ErrorList const & Buffer::errorList(string const & type) const
1959 static ErrorList const emptyErrorList;
1960 std::map<string, ErrorList>::const_iterator I = pimpl_->errorLists.find(type);
1961 if (I == pimpl_->errorLists.end())
1962 return emptyErrorList;
1968 ErrorList & Buffer::errorList(string const & type)
1970 return pimpl_->errorLists[type];
1974 void Buffer::structureChanged() const
1977 gui_->structureChanged();
1981 void Buffer::errors(std::string const & err) const
1988 void Buffer::message(docstring const & msg) const
1995 void Buffer::setBusy(bool on) const
2002 void Buffer::setReadOnly(bool on) const
2005 gui_->setReadOnly(on);
2009 void Buffer::updateTitles() const
2012 gui_->updateTitles();
2016 void Buffer::resetAutosaveTimers() const
2019 gui_->resetAutosaveTimers();
2023 void Buffer::setGuiDelegate(frontend::GuiBufferDelegate * gui)
2032 class AutoSaveBuffer : public support::ForkedProcess {
2035 AutoSaveBuffer(Buffer const & buffer, FileName const & fname)
2036 : buffer_(buffer), fname_(fname) {}
2038 virtual boost::shared_ptr<ForkedProcess> clone() const
2040 return boost::shared_ptr<ForkedProcess>(new AutoSaveBuffer(*this));
2045 command_ = to_utf8(bformat(_("Auto-saving %1$s"),
2046 from_utf8(fname_.absFilename())));
2047 return run(DontWait);
2051 virtual int generateChild();
2053 Buffer const & buffer_;
2058 #if !defined (HAVE_FORK)
2062 int AutoSaveBuffer::generateChild()
2064 // tmp_ret will be located (usually) in /tmp
2065 // will that be a problem?
2066 pid_t const pid = fork();
2067 // If you want to debug the autosave
2068 // you should set pid to -1, and comment out the fork.
2069 if (pid == 0 || pid == -1) {
2070 // pid = -1 signifies that lyx was unable
2071 // to fork. But we will do the save
2073 bool failed = false;
2075 FileName const tmp_ret(tempName(FileName(), "lyxauto"));
2076 if (!tmp_ret.empty()) {
2077 buffer_.writeFile(tmp_ret);
2078 // assume successful write of tmp_ret
2079 if (!rename(tmp_ret, fname_)) {
2081 // most likely couldn't move between
2082 // filesystems unless write of tmp_ret
2083 // failed so remove tmp file (if it
2092 // failed to write/rename tmp_ret so try writing direct
2093 if (!buffer_.writeFile(fname_)) {
2094 // It is dangerous to do this in the child,
2095 // but safe in the parent, so...
2096 if (pid == -1) // emit message signal.
2097 buffer_.message(_("Autosave failed!"));
2100 if (pid == 0) { // we are the child so...
2110 // Perfect target for a thread...
2111 void Buffer::autoSave() const
2113 if (isBakClean() || isReadonly()) {
2114 // We don't save now, but we'll try again later
2115 resetAutosaveTimers();
2119 // emit message signal.
2120 message(_("Autosaving current document..."));
2122 // create autosave filename
2123 string fname = filePath();
2125 fname += onlyFilename(absFileName());
2128 AutoSaveBuffer autosave(*this, FileName(fname));
2132 resetAutosaveTimers();
2136 /** Write a buffer to a new file name and rename the buffer
2137 according to the new file name.
2139 This function is e.g. used by menu callbacks and
2140 LFUN_BUFFER_WRITE_AS.
2142 If 'newname' is empty (the default), the user is asked via a
2143 dialog for the buffer's new name and location.
2145 If 'newname' is non-empty and has an absolute path, that is used.
2146 Otherwise the base directory of the buffer is used as the base
2147 for any relative path in 'newname'.
2150 bool Buffer::writeAs(string const & newname)
2152 string fname = absFileName();
2153 string const oldname = fname;
2155 if (newname.empty()) { /// No argument? Ask user through dialog
2158 FileDialog dlg(_("Choose a filename to save document as"),
2159 LFUN_BUFFER_WRITE_AS);
2160 dlg.setButton1(_("Documents|#o#O"), from_utf8(lyxrc.document_path));
2161 dlg.setButton2(_("Templates|#T#t"), from_utf8(lyxrc.template_path));
2163 if (!support::isLyXFilename(fname))
2166 support::FileFilterList const filter(_("LyX Documents (*.lyx)"));
2168 FileDialog::Result result =
2169 dlg.save(from_utf8(onlyPath(fname)),
2171 from_utf8(onlyFilename(fname)));
2173 if (result.first == FileDialog::Later)
2176 fname = to_utf8(result.second);
2181 // Make sure the absolute filename ends with appropriate suffix
2182 fname = makeAbsPath(fname).absFilename();
2183 if (!support::isLyXFilename(fname))
2187 fname = makeAbsPath(newname, onlyPath(oldname)).absFilename();
2189 if (FileName(fname).exists()) {
2190 docstring const file = makeDisplayPath(fname, 30);
2191 docstring text = bformat(_("The document %1$s already "
2192 "exists.\n\nDo you want to "
2193 "overwrite that document?"),
2195 int const ret = Alert::prompt(_("Overwrite document?"),
2196 text, 0, 1, _("&Overwrite"), _("&Cancel"));
2202 // Ok, change the name of the buffer
2205 bool unnamed = isUnnamed();
2207 saveCheckSum(FileName(fname));
2210 setFileName(oldname);
2211 setUnnamed(unnamed);
2212 saveCheckSum(FileName(oldname));
2216 removeAutosaveFile(oldname);
2221 bool Buffer::menuWrite()
2224 LyX::ref().session().lastFiles().add(FileName(absFileName()));
2228 // FIXME: we don't tell the user *WHY* the save failed !!
2230 docstring const file = makeDisplayPath(absFileName(), 30);
2232 docstring text = bformat(_("The document %1$s could not be saved.\n\n"
2233 "Do you want to rename the document and "
2234 "try again?"), file);
2235 int const ret = Alert::prompt(_("Rename and save?"),
2236 text, 0, 1, _("&Rename"), _("&Cancel"));
2245 void Buffer::loadChildDocuments() const
2247 bool parse_error = false;
2249 for (InsetIterator it = inset_iterator_begin(inset()); it; ++it) {
2250 if (it->lyxCode() != INCLUDE_CODE)
2252 InsetCommand const & inset = static_cast<InsetCommand const &>(*it);
2253 InsetCommandParams const & ip = inset.params();
2254 Buffer * child = loadIfNeeded(*this, ip);
2257 parse_error |= !child->errorList("Parse").empty();
2258 child->loadChildDocuments();
2261 if (use_gui && masterBuffer() == this)
2262 updateLabels(*this);
2266 string Buffer::bufferFormat() const
2276 bool Buffer::doExport(string const & format, bool put_in_tempdir,
2277 string & result_file)
2279 string backend_format;
2280 OutputParams runparams(¶ms().encoding());
2281 runparams.flavor = OutputParams::LATEX;
2282 runparams.linelen = lyxrc.plaintext_linelen;
2283 vector<string> backs = backends();
2284 if (find(backs.begin(), backs.end(), format) == backs.end()) {
2285 // Get shortest path to format
2286 Graph::EdgePath path;
2287 for (vector<string>::const_iterator it = backs.begin();
2288 it != backs.end(); ++it) {
2289 Graph::EdgePath p = theConverters().getPath(*it, format);
2290 if (!p.empty() && (path.empty() || p.size() < path.size())) {
2291 backend_format = *it;
2296 runparams.flavor = theConverters().getFlavor(path);
2298 Alert::error(_("Couldn't export file"),
2299 bformat(_("No information for exporting the format %1$s."),
2300 formats.prettyName(format)));
2304 backend_format = format;
2305 // FIXME: Don't hardcode format names here, but use a flag
2306 if (backend_format == "pdflatex")
2307 runparams.flavor = OutputParams::PDFLATEX;
2310 string filename = latexName(false);
2311 filename = addName(temppath(), filename);
2312 filename = changeExtension(filename,
2313 formats.extension(backend_format));
2315 // Plain text backend
2316 if (backend_format == "text")
2317 writePlaintextFile(*this, FileName(filename), runparams);
2319 else if (backend_format == "lyx")
2320 writeFile(FileName(filename));
2322 else if (isDocBook()) {
2323 runparams.nice = !put_in_tempdir;
2324 makeDocBookFile(FileName(filename), runparams);
2327 else if (backend_format == format) {
2328 runparams.nice = true;
2329 if (!makeLaTeXFile(FileName(filename), string(), runparams))
2331 } else if (!lyxrc.tex_allows_spaces
2332 && support::contains(filePath(), ' ')) {
2333 Alert::error(_("File name error"),
2334 _("The directory path to the document cannot contain spaces."));
2337 runparams.nice = false;
2338 if (!makeLaTeXFile(FileName(filename), filePath(), runparams))
2342 string const error_type = (format == "program")
2343 ? "Build" : bufferFormat();
2344 string const ext = formats.extension(format);
2345 FileName const tmp_result_file(changeExtension(filename, ext));
2346 bool const success = theConverters().convert(this, FileName(filename),
2347 tmp_result_file, FileName(absFileName()), backend_format, format,
2348 errorList(error_type));
2349 // Emit the signal to show the error list.
2350 if (format != backend_format)
2356 result_file = tmp_result_file.absFilename();
2358 result_file = changeExtension(absFileName(), ext);
2359 // We need to copy referenced files (e. g. included graphics
2360 // if format == "dvi") to the result dir.
2361 vector<ExportedFile> const files =
2362 runparams.exportdata->externalFiles(format);
2363 string const dest = onlyPath(result_file);
2364 CopyStatus status = SUCCESS;
2365 for (vector<ExportedFile>::const_iterator it = files.begin();
2366 it != files.end() && status != CANCEL; ++it) {
2368 formats.getFormatFromFile(it->sourceName);
2369 status = copyFile(fmt, it->sourceName,
2370 makeAbsPath(it->exportName, dest),
2371 it->exportName, status == FORCE);
2373 if (status == CANCEL) {
2374 message(_("Document export cancelled."));
2375 } else if (tmp_result_file.exists()) {
2376 // Finally copy the main file
2377 status = copyFile(format, tmp_result_file,
2378 FileName(result_file), result_file,
2380 message(bformat(_("Document exported as %1$s "
2382 formats.prettyName(format),
2383 makeDisplayPath(result_file)));
2385 // This must be a dummy converter like fax (bug 1888)
2386 message(bformat(_("Document exported as %1$s"),
2387 formats.prettyName(format)));
2395 bool Buffer::doExport(string const & format, bool put_in_tempdir)
2398 return doExport(format, put_in_tempdir, result_file);
2402 bool Buffer::preview(string const & format)
2405 if (!doExport(format, true, result_file))
2407 return formats.view(*this, FileName(result_file), format);
2411 bool Buffer::isExportable(string const & format) const
2413 vector<string> backs = backends();
2414 for (vector<string>::const_iterator it = backs.begin();
2415 it != backs.end(); ++it)
2416 if (theConverters().isReachable(*it, format))
2422 vector<Format const *> Buffer::exportableFormats(bool only_viewable) const
2424 vector<string> backs = backends();
2425 vector<Format const *> result =
2426 theConverters().getReachable(backs[0], only_viewable, true);
2427 for (vector<string>::const_iterator it = backs.begin() + 1;
2428 it != backs.end(); ++it) {
2429 vector<Format const *> r =
2430 theConverters().getReachable(*it, only_viewable, false);
2431 result.insert(result.end(), r.begin(), r.end());
2437 vector<string> Buffer::backends() const
2440 if (params().getTextClass().isTeXClassAvailable()) {
2441 v.push_back(bufferFormat());
2442 // FIXME: Don't hardcode format names here, but use a flag
2443 if (v.back() == "latex")
2444 v.push_back("pdflatex");
2446 v.push_back("text");
2452 bool Buffer::readFileHelper(FileName const & s)
2454 // File information about normal file
2456 docstring const file = makeDisplayPath(s.absFilename(), 50);
2457 docstring text = bformat(_("The specified document\n%1$s"
2458 "\ncould not be read."), file);
2459 Alert::error(_("Could not read document"), text);
2463 // Check if emergency save file exists and is newer.
2464 FileName const e(s.absFilename() + ".emergency");
2466 if (e.exists() && s.exists() && e.lastModified() > s.lastModified()) {
2467 docstring const file = makeDisplayPath(s.absFilename(), 20);
2468 docstring const text =
2469 bformat(_("An emergency save of the document "
2471 "Recover emergency save?"), file);
2472 switch (Alert::prompt(_("Load emergency save?"), text, 0, 2,
2473 _("&Recover"), _("&Load Original"),
2477 // the file is not saved if we load the emergency file.
2487 // Now check if autosave file is newer.
2488 FileName const a(onlyPath(s.absFilename()) + '#' + onlyFilename(s.absFilename()) + '#');
2490 if (a.exists() && s.exists() && a.lastModified() > s.lastModified()) {
2491 docstring const file = makeDisplayPath(s.absFilename(), 20);
2492 docstring const text =
2493 bformat(_("The backup of the document "
2494 "%1$s is newer.\n\nLoad the "
2495 "backup instead?"), file);
2496 switch (Alert::prompt(_("Load backup?"), text, 0, 2,
2497 _("&Load backup"), _("Load &original"),
2501 // the file is not saved if we load the autosave file.
2505 // Here we delete the autosave
2516 bool Buffer::loadLyXFile(FileName const & s)
2518 if (s.isReadable()) {
2519 if (readFileHelper(s)) {
2520 lyxvc().file_found_hook(s);
2521 if (!s.isWritable())
2526 docstring const file = makeDisplayPath(s.absFilename(), 20);
2527 // Here we probably should run
2528 if (LyXVC::file_not_found_hook(s)) {
2529 docstring const text =
2530 bformat(_("Do you want to retrieve the document"
2531 " %1$s from version control?"), file);
2532 int const ret = Alert::prompt(_("Retrieve from version control?"),
2533 text, 0, 1, _("&Retrieve"), _("&Cancel"));
2536 // How can we know _how_ to do the checkout?
2537 // With the current VC support it has to be,
2538 // a RCS file since CVS do not have special ,v files.
2540 return loadLyXFile(s);
2548 void Buffer::bufferErrors(TeXErrors const & terr, ErrorList & errorList) const
2550 TeXErrors::Errors::const_iterator cit = terr.begin();
2551 TeXErrors::Errors::const_iterator end = terr.end();
2553 for (; cit != end; ++cit) {
2556 int errorRow = cit->error_in_line;
2557 bool found = texrow().getIdFromRow(errorRow, id_start,
2563 found = texrow().getIdFromRow(errorRow, id_end, pos_end);
2564 } while (found && id_start == id_end && pos_start == pos_end);
2566 errorList.push_back(ErrorItem(cit->error_desc,
2567 cit->error_text, id_start, pos_start, pos_end));