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"
26 #include "DocIterator.h"
27 #include "EmbeddedFiles.h"
29 #include "ErrorList.h"
32 #include "FuncRequest.h"
33 #include "InsetIterator.h"
34 #include "InsetList.h"
36 #include "LaTeXFeatures.h"
40 #include "LyXAction.h"
44 #include "output_docbook.h"
46 #include "output_latex.h"
47 #include "output_plaintext.h"
48 #include "paragraph_funcs.h"
49 #include "Paragraph.h"
50 #include "ParagraphParameters.h"
51 #include "ParIterator.h"
52 #include "PDFOptions.h"
56 #include "TexStream.h"
57 #include "TextClassList.h"
59 #include "TocBackend.h"
61 #include "VCBackend.h"
64 #include "insets/InsetBibitem.h"
65 #include "insets/InsetBibtex.h"
66 #include "insets/InsetInclude.h"
67 #include "insets/InsetText.h"
69 #include "mathed/MacroTable.h"
70 #include "mathed/MathMacroTemplate.h"
71 #include "mathed/MathSupport.h"
73 #include "frontends/alert.h"
74 #include "frontends/Delegates.h"
75 #include "frontends/WorkAreaManager.h"
76 #include "frontends/FileDialog.h"
78 #include "graphics/Previews.h"
80 #include "support/convert.h"
81 #include "support/debug.h"
82 #include "support/FileFilterList.h"
83 #include "support/filetools.h"
84 #include "support/ForkedCalls.h"
85 #include "support/gettext.h"
86 #include "support/gzstream.h"
87 #include "support/lstrings.h"
88 #include "support/lyxalgo.h"
89 #include "support/lyxlib.h"
90 #include "support/os.h"
91 #include "support/Path.h"
92 #include "support/textutils.h"
93 #include "support/types.h"
94 #include "support/FileZipListDir.h"
96 #if !defined (HAVE_FORK)
100 #include <boost/bind.hpp>
101 #include <boost/shared_ptr.hpp>
111 using std::make_pair;
116 using std::ostringstream;
127 using support::addName;
128 using support::bformat;
129 using support::changeExtension;
130 using support::cmd_ret;
131 using support::createBufferTmpDir;
132 using support::FileName;
133 using support::libFileSearch;
134 using support::latex_path;
135 using support::ltrim;
136 using support::makeAbsPath;
137 using support::makeDisplayPath;
138 using support::makeLatexName;
139 using support::onlyFilename;
140 using support::onlyPath;
141 using support::quoteName;
142 using support::removeAutosaveFile;
143 using support::rename;
144 using support::runCommand;
145 using support::split;
146 using support::subst;
147 using support::tempName;
149 using support::suffixIs;
151 namespace Alert = frontend::Alert;
152 namespace os = support::os;
156 int const LYX_FORMAT = 303; // Uwe: Serbocroatian
161 typedef std::map<string, bool> DepClean;
166 Impl(Buffer & parent, FileName const & file, bool readonly);
179 mutable TexRow texrow;
180 Buffer const * parent_buffer;
182 /// need to regenerate .tex?
186 mutable bool lyx_clean;
188 /// is autosave needed?
189 mutable bool bak_clean;
191 /// is this a unnamed file (New...)?
197 /// name of the file the buffer is associated with.
200 /** Set to true only when the file is fully loaded.
201 * Used to prevent the premature generation of previews
202 * and by the citation inset.
204 bool file_fully_loaded;
206 /// our Text that should be wrapped in an InsetText
210 mutable TocBackend toc_backend;
213 typedef std::map<unsigned int, MacroData, std::greater<int> > PositionToMacroMap;
214 typedef std::map<docstring, PositionToMacroMap> NameToPositionMacroMap;
215 NameToPositionMacroMap macros;
217 /// Container for all sort of Buffer dependant errors.
218 map<string, ErrorList> errorLists;
220 /// all embedded files of this buffer
221 EmbeddedFiles embedded_files;
223 /// timestamp and checksum used to test if the file has been externally
224 /// modified. (Used to properly enable 'File->Revert to saved', bug 4114).
226 unsigned long checksum_;
229 frontend::WorkAreaManager * wa_;
236 Buffer::Impl::Impl(Buffer & parent, FileName const & file, bool readonly_)
237 : parent_buffer(0), lyx_clean(true), bak_clean(true), unnamed(false),
238 read_only(readonly_), filename(file), file_fully_loaded(false),
239 inset(params), toc_backend(&parent), embedded_files(&parent),
240 timestamp_(0), checksum_(0), wa_(0), undo_(parent)
242 inset.setAutoBreakRows(true);
243 lyxvc.setBuffer(&parent);
244 temppath = createBufferTmpDir();
246 // FIXME: And now do something if temppath == string(), because we
247 // assume from now on that temppath points to a valid temp dir.
248 // See http://www.mail-archive.com/lyx-devel@lists.lyx.org/msg67406.html
251 wa_ = new frontend::WorkAreaManager;
255 Buffer::Buffer(string const & file, bool readonly)
256 : d(new Impl(*this, FileName(file), readonly)), gui_(0)
258 LYXERR(Debug::INFO, "Buffer::Buffer()");
264 LYXERR(Debug::INFO, "Buffer::~Buffer()");
265 // here the buffer should take care that it is
266 // saved properly, before it goes into the void.
268 // GuiView already destroyed
271 Buffer const * master = masterBuffer();
272 if (master != this && use_gui)
273 // We are closing buf which was a child document so we
274 // must update the labels and section numbering of its master
276 updateLabels(*master);
278 resetChildDocuments(false);
280 if (!temppath().empty() && !FileName(temppath()).destroyDirectory()) {
281 Alert::warning(_("Could not remove temporary directory"),
282 bformat(_("Could not remove the temporary directory %1$s"),
283 from_utf8(temppath())));
286 // Remove any previewed LaTeX snippets associated with this buffer.
287 graphics::Previews::get().removeLoader(*this);
293 void Buffer::changed() const
300 frontend::WorkAreaManager & Buffer::workAreaManager() const
302 BOOST_ASSERT(d->wa_);
307 Text & Buffer::text() const
309 return const_cast<Text &>(d->inset.text_);
313 Inset & Buffer::inset() const
315 return const_cast<InsetText &>(d->inset);
319 BufferParams & Buffer::params()
325 BufferParams const & Buffer::params() const
331 ParagraphList & Buffer::paragraphs()
333 return text().paragraphs();
337 ParagraphList const & Buffer::paragraphs() const
339 return text().paragraphs();
343 LyXVC & Buffer::lyxvc()
349 LyXVC const & Buffer::lyxvc() const
355 string const & Buffer::temppath() const
361 TexRow const & Buffer::texrow() const
367 TocBackend & Buffer::tocBackend() const
369 return d->toc_backend;
373 EmbeddedFiles & Buffer::embeddedFiles()
375 return d->embedded_files;
379 EmbeddedFiles const & Buffer::embeddedFiles() const
381 return d->embedded_files;
385 Undo & Buffer::undo()
391 string Buffer::latexName(bool const no_path) const
393 FileName latex_name = makeLatexName(d->filename);
394 return no_path ? latex_name.onlyFileName()
395 : latex_name.absFilename();
399 string Buffer::logName(LogType * type) const
401 string const filename = latexName(false);
403 if (filename.empty()) {
409 string const path = temppath();
411 FileName const fname(addName(temppath(),
412 onlyFilename(changeExtension(filename,
414 FileName const bname(
415 addName(path, onlyFilename(
416 changeExtension(filename,
417 formats.extension("literate") + ".out"))));
419 // If no Latex log or Build log is newer, show Build log
421 if (bname.exists() &&
422 (!fname.exists() || fname.lastModified() < bname.lastModified())) {
423 LYXERR(Debug::FILES, "Log name calculated as: " << bname);
426 return bname.absFilename();
428 LYXERR(Debug::FILES, "Log name calculated as: " << fname);
431 return fname.absFilename();
435 void Buffer::setReadonly(bool const flag)
437 if (d->read_only != flag) {
444 void Buffer::setFileName(string const & newfile)
446 d->filename = makeAbsPath(newfile);
447 setReadonly(d->filename.isReadOnly());
452 int Buffer::readHeader(Lexer & lex)
454 int unknown_tokens = 0;
456 int begin_header_line = -1;
458 // Initialize parameters that may be/go lacking in header:
459 params().branchlist().clear();
460 params().preamble.erase();
461 params().options.erase();
462 params().float_placement.erase();
463 params().paperwidth.erase();
464 params().paperheight.erase();
465 params().leftmargin.erase();
466 params().rightmargin.erase();
467 params().topmargin.erase();
468 params().bottommargin.erase();
469 params().headheight.erase();
470 params().headsep.erase();
471 params().footskip.erase();
472 params().listings_params.clear();
473 params().clearLayoutModules();
474 params().pdfoptions().clear();
476 for (int i = 0; i < 4; ++i) {
477 params().user_defined_bullet(i) = ITEMIZE_DEFAULTS[i];
478 params().temp_bullet(i) = ITEMIZE_DEFAULTS[i];
481 ErrorList & errorList = d->errorLists["Parse"];
485 string const token = lex.getString();
490 if (token == "\\end_header")
494 if (token == "\\begin_header") {
495 begin_header_line = line;
499 LYXERR(Debug::PARSER, "Handling document header token: `"
502 string unknown = params().readToken(lex, token, d->filename.onlyPath());
503 if (!unknown.empty()) {
504 if (unknown[0] != '\\' && token == "\\textclass") {
505 Alert::warning(_("Unknown document class"),
506 bformat(_("Using the default document class, because the "
507 "class %1$s is unknown."), from_utf8(unknown)));
510 docstring const s = bformat(_("Unknown token: "
514 errorList.push_back(ErrorItem(_("Document header error"),
519 if (begin_header_line) {
520 docstring const s = _("\\begin_header is missing");
521 errorList.push_back(ErrorItem(_("Document header error"),
525 return unknown_tokens;
530 // changed to be public and have one parameter
531 // Returns false if "\end_document" is not read (Asger)
532 bool Buffer::readDocument(Lexer & lex)
534 ErrorList & errorList = d->errorLists["Parse"];
538 string const token = lex.getString();
539 if (token != "\\begin_document") {
540 docstring const s = _("\\begin_document is missing");
541 errorList.push_back(ErrorItem(_("Document header error"),
545 // we are reading in a brand new document
546 BOOST_ASSERT(paragraphs().empty());
549 TextClass const & baseClass = textclasslist[params().getBaseClass()];
550 if (!baseClass.load(filePath())) {
551 string theclass = baseClass.name();
552 Alert::error(_("Can't load document class"), bformat(
553 _("Using the default document class, because the "
554 "class %1$s could not be loaded."), from_utf8(theclass)));
555 params().setBaseClass(defaultTextclass());
558 if (params().outputChanges) {
559 bool dvipost = LaTeXFeatures::isAvailable("dvipost");
560 bool xcolorsoul = LaTeXFeatures::isAvailable("soul") &&
561 LaTeXFeatures::isAvailable("xcolor");
563 if (!dvipost && !xcolorsoul) {
564 Alert::warning(_("Changes not shown in LaTeX output"),
565 _("Changes will not be highlighted in LaTeX output, "
566 "because neither dvipost nor xcolor/soul are installed.\n"
567 "Please install these packages or redefine "
568 "\\lyxadded and \\lyxdeleted in the LaTeX preamble."));
569 } else if (!xcolorsoul) {
570 Alert::warning(_("Changes not shown in LaTeX output"),
571 _("Changes will not be highlighted in LaTeX output "
572 "when using pdflatex, because xcolor and soul are not installed.\n"
573 "Please install both packages or redefine "
574 "\\lyxadded and \\lyxdeleted in the LaTeX preamble."));
579 bool const res = text().read(*this, lex, errorList);
580 for_each(text().paragraphs().begin(),
581 text().paragraphs().end(),
582 bind(&Paragraph::setInsetOwner, _1, &inset()));
588 // needed to insert the selection
589 void Buffer::insertStringAsLines(ParagraphList & pars,
590 pit_type & pit, pos_type & pos,
591 Font const & fn, docstring const & str, bool autobreakrows)
595 // insert the string, don't insert doublespace
596 bool space_inserted = true;
597 for (docstring::const_iterator cit = str.begin();
598 cit != str.end(); ++cit) {
599 Paragraph & par = pars[pit];
601 if (autobreakrows && (!par.empty() || par.allowEmpty())) {
602 breakParagraph(params(), pars, pit, pos,
603 par.layout()->isEnvironment());
606 space_inserted = true;
610 // do not insert consecutive spaces if !free_spacing
611 } else if ((*cit == ' ' || *cit == '\t') &&
612 space_inserted && !par.isFreeSpacing()) {
614 } else if (*cit == '\t') {
615 if (!par.isFreeSpacing()) {
616 // tabs are like spaces here
617 par.insertChar(pos, ' ', font, params().trackChanges);
619 space_inserted = true;
621 const pos_type n = 8 - pos % 8;
622 for (pos_type i = 0; i < n; ++i) {
623 par.insertChar(pos, ' ', font, params().trackChanges);
626 space_inserted = true;
628 } else if (!isPrintable(*cit)) {
629 // Ignore unprintables
632 // just insert the character
633 par.insertChar(pos, *cit, font, params().trackChanges);
635 space_inserted = (*cit == ' ');
642 bool Buffer::readString(std::string const & s)
644 params().compressed = false;
646 // remove dummy empty par
647 paragraphs().clear();
649 std::istringstream is(s);
651 FileName const name(tempName());
652 switch (readFile(lex, name, true)) {
656 // We need to call lyx2lyx, so write the input to a file
657 std::ofstream os(name.toFilesystemEncoding().c_str());
660 return readFile(name);
670 bool Buffer::readFile(FileName const & filename)
672 FileName fname(filename);
673 // Check if the file is compressed.
674 string format = filename.guessFormatFromContents();
675 if (format == "zip") {
676 // decompress to a temp directory
677 LYXERR(Debug::FILES, filename << " is in zip format. Unzip to " << temppath());
678 ::unzipToDir(filename.toFilesystemEncoding(), temppath());
680 FileName lyxfile(addName(temppath(), "content.lyx"));
681 // if both manifest.txt and file.lyx exist, this is am embedded file
682 if (lyxfile.exists()) {
683 params().embedded = true;
687 // The embedded lyx file can also be compressed, for backward compatibility
688 format = fname.guessFormatFromContents();
689 if (format == "gzip" || format == "zip" || format == "compress")
690 params().compressed = true;
692 // remove dummy empty par
693 paragraphs().clear();
696 if (readFile(lex, fname) != success)
703 bool Buffer::isFullyLoaded() const
705 return d->file_fully_loaded;
709 void Buffer::setFullyLoaded(bool value)
711 d->file_fully_loaded = value;
715 Buffer::ReadStatus Buffer::readFile(Lexer & lex, FileName const & filename,
718 BOOST_ASSERT(!filename.empty());
721 Alert::error(_("Document could not be read"),
722 bformat(_("%1$s could not be read."), from_utf8(filename.absFilename())));
727 string const token = lex.getString();
730 Alert::error(_("Document could not be read"),
731 bformat(_("%1$s could not be read."), from_utf8(filename.absFilename())));
735 // the first token _must_ be...
736 if (token != "\\lyxformat") {
737 lyxerr << "Token: " << token << endl;
739 Alert::error(_("Document format failure"),
740 bformat(_("%1$s is not a LyX document."),
741 from_utf8(filename.absFilename())));
746 string tmp_format = lex.getString();
747 //lyxerr << "LyX Format: `" << tmp_format << '\'' << endl;
748 // if present remove ".," from string.
749 string::size_type dot = tmp_format.find_first_of(".,");
750 //lyxerr << " dot found at " << dot << endl;
751 if (dot != string::npos)
752 tmp_format.erase(dot, 1);
753 int const file_format = convert<int>(tmp_format);
754 //lyxerr << "format: " << file_format << endl;
756 // save timestamp and checksum of the original disk file, making sure
757 // to not overwrite them with those of the file created in the tempdir
758 // when it has to be converted to the current format.
760 // Save the timestamp and checksum of disk file. If filename is an
761 // emergency file, save the timestamp and checksum of the original lyx file
762 // because isExternallyModified will check for this file. (BUG4193)
763 string diskfile = filename.absFilename();
764 if (suffixIs(diskfile, ".emergency"))
765 diskfile = diskfile.substr(0, diskfile.size() - 10);
766 saveCheckSum(FileName(diskfile));
769 if (file_format != LYX_FORMAT) {
772 // lyx2lyx would fail
775 FileName const tmpfile(tempName());
776 if (tmpfile.empty()) {
777 Alert::error(_("Conversion failed"),
778 bformat(_("%1$s is from a different"
779 " version of LyX, but a temporary"
780 " file for converting it could"
782 from_utf8(filename.absFilename())));
785 FileName const lyx2lyx = libFileSearch("lyx2lyx", "lyx2lyx");
786 if (lyx2lyx.empty()) {
787 Alert::error(_("Conversion script not found"),
788 bformat(_("%1$s is from a different"
789 " version of LyX, but the"
790 " conversion script lyx2lyx"
791 " could not be found."),
792 from_utf8(filename.absFilename())));
795 ostringstream command;
796 command << os::python()
797 << ' ' << quoteName(lyx2lyx.toFilesystemEncoding())
798 << " -t " << convert<string>(LYX_FORMAT)
799 << " -o " << quoteName(tmpfile.toFilesystemEncoding())
800 << ' ' << quoteName(filename.toFilesystemEncoding());
801 string const command_str = command.str();
803 LYXERR(Debug::INFO, "Running '" << command_str << '\'');
805 cmd_ret const ret = runCommand(command_str);
806 if (ret.first != 0) {
807 Alert::error(_("Conversion script failed"),
808 bformat(_("%1$s is from a different version"
809 " of LyX, but the lyx2lyx script"
810 " failed to convert it."),
811 from_utf8(filename.absFilename())));
814 bool const ret = readFile(tmpfile);
815 // Do stuff with tmpfile name and buffer name here.
816 return ret ? success : failure;
821 if (readDocument(lex)) {
822 Alert::error(_("Document format failure"),
823 bformat(_("%1$s ended unexpectedly, which means"
824 " that it is probably corrupted."),
825 from_utf8(filename.absFilename())));
828 d->file_fully_loaded = true;
833 // Should probably be moved to somewhere else: BufferView? LyXView?
834 bool Buffer::save() const
836 // We don't need autosaves in the immediate future. (Asger)
837 resetAutosaveTimers();
839 string const encodedFilename = d->filename.toFilesystemEncoding();
842 bool madeBackup = false;
844 // make a backup if the file already exists
845 if (lyxrc.make_backup && fileName().exists()) {
846 backupName = FileName(absFileName() + '~');
847 if (!lyxrc.backupdir_path.empty()) {
848 string const mangledName =
849 subst(subst(backupName.absFilename(), '/', '!'), ':', '!');
850 backupName = FileName(addName(lyxrc.backupdir_path,
853 if (fileName().copyTo(backupName, true)) {
856 Alert::error(_("Backup failure"),
857 bformat(_("Cannot create backup file %1$s.\n"
858 "Please check whether the directory exists and is writeable."),
859 from_utf8(backupName.absFilename())));
860 //LYXERR(Debug::DEBUG, "Fs error: " << fe.what());
864 // ask if the disk file has been externally modified (use checksum method)
865 if (fileName().exists() && isExternallyModified(checksum_method)) {
866 docstring const file = makeDisplayPath(absFileName(), 20);
867 docstring text = bformat(_("Document %1$s has been externally modified. Are you sure "
868 "you want to overwrite this file?"), file);
869 int const ret = Alert::prompt(_("Overwrite modified file?"),
870 text, 1, 1, _("&Overwrite"), _("&Cancel"));
875 if (writeFile(d->filename)) {
877 removeAutosaveFile(absFileName());
878 saveCheckSum(d->filename);
881 // Saving failed, so backup is not backup
883 rename(backupName, d->filename);
889 bool Buffer::writeFile(FileName const & fname) const
891 if (d->read_only && fname == d->filename)
897 if (params().embedded)
898 // first write the .lyx file to the temporary directory
899 content = FileName(addName(temppath(), "content.lyx"));
903 if (params().compressed) {
904 gz::ogzstream ofs(content.toFilesystemEncoding().c_str(), ios::out|ios::trunc);
910 ofstream ofs(content.toFilesystemEncoding().c_str(), ios::out|ios::trunc);
917 if (retval && params().embedded) {
918 // write file.lyx and all the embedded files to the zip file fname
919 // if embedding is enabled
920 return d->embedded_files.writeFile(fname);
926 bool Buffer::write(ostream & ofs) const
929 // Use the standard "C" locale for file output.
930 ofs.imbue(std::locale::classic());
933 // The top of the file should not be written by params().
935 // write out a comment in the top of the file
936 ofs << "#LyX " << lyx_version
937 << " created this file. For more info see http://www.lyx.org/\n"
938 << "\\lyxformat " << LYX_FORMAT << "\n"
939 << "\\begin_document\n";
942 /// For each author, set 'used' to true if there is a change
943 /// by this author in the document; otherwise set it to 'false'.
944 AuthorList::Authors::const_iterator a_it = params().authors().begin();
945 AuthorList::Authors::const_iterator a_end = params().authors().end();
946 for (; a_it != a_end; ++a_it)
947 a_it->second.setUsed(false);
949 ParIterator const end = par_iterator_end();
950 ParIterator it = par_iterator_begin();
951 for ( ; it != end; ++it)
952 it->checkAuthors(params().authors());
954 // now write out the buffer parameters.
955 ofs << "\\begin_header\n";
956 params().writeFile(ofs);
957 ofs << "\\end_header\n";
960 ofs << "\n\\begin_body\n";
961 text().write(*this, ofs);
962 ofs << "\n\\end_body\n";
964 // Write marker that shows file is complete
965 ofs << "\\end_document" << endl;
967 // Shouldn't really be needed....
970 // how to check if close went ok?
971 // Following is an attempt... (BE 20001011)
973 // good() returns false if any error occured, including some
975 // bad() returns true if something bad happened in the buffer,
976 // which should include file system full errors.
981 lyxerr << "File was not closed properly." << endl;
988 bool Buffer::makeLaTeXFile(FileName const & fname,
989 string const & original_path,
990 OutputParams const & runparams,
991 bool output_preamble, bool output_body) const
993 string const encoding = runparams.encoding->iconvName();
994 LYXERR(Debug::LATEX, "makeLaTeXFile encoding: " << encoding << "...");
996 odocfstream ofs(encoding);
997 if (!openFileWrite(ofs, fname))
1000 //TexStream ts(ofs.rdbuf(), &texrow());
1002 bool failed_export = false;
1005 writeLaTeXSource(ofs, original_path,
1006 runparams, output_preamble, output_body);
1008 catch (iconv_codecvt_facet_exception & e) {
1009 lyxerr << "Caught iconv exception: " << e.what() << endl;
1010 failed_export = true;
1012 catch (std::exception const & e) {
1013 lyxerr << "Caught \"normal\" exception: " << e.what() << endl;
1014 failed_export = true;
1017 lyxerr << "Caught some really weird exception..." << endl;
1018 LyX::cref().emergencyCleanup();
1024 failed_export = true;
1025 lyxerr << "File '" << fname << "' was not closed properly." << endl;
1028 if (failed_export) {
1029 Alert::error(_("Encoding error"),
1030 _("Some characters of your document are probably not "
1031 "representable in the chosen encoding.\n"
1032 "Changing the document encoding to utf8 could help."));
1039 void Buffer::writeLaTeXSource(odocstream & os,
1040 string const & original_path,
1041 OutputParams const & runparams_in,
1042 bool const output_preamble, bool const output_body) const
1044 OutputParams runparams = runparams_in;
1046 // validate the buffer.
1047 LYXERR(Debug::LATEX, " Validating buffer...");
1048 LaTeXFeatures features(*this, params(), runparams);
1050 LYXERR(Debug::LATEX, " Buffer validation done.");
1052 // The starting paragraph of the coming rows is the
1053 // first paragraph of the document. (Asger)
1054 if (output_preamble && runparams.nice) {
1055 os << "%% LyX " << lyx_version << " created this file. "
1056 "For more info, see http://www.lyx.org/.\n"
1057 "%% Do not edit unless you really know what "
1059 d->texrow.newline();
1060 d->texrow.newline();
1062 LYXERR(Debug::INFO, "lyx document header finished");
1063 // There are a few differences between nice LaTeX and usual files:
1064 // usual is \batchmode and has a
1065 // special input@path to allow the including of figures
1066 // with either \input or \includegraphics (what figinsets do).
1067 // input@path is set when the actual parameter
1068 // original_path is set. This is done for usual tex-file, but not
1069 // for nice-latex-file. (Matthias 250696)
1070 // Note that input@path is only needed for something the user does
1071 // in the preamble, included .tex files or ERT, files included by
1072 // LyX work without it.
1073 if (output_preamble) {
1074 if (!runparams.nice) {
1075 // code for usual, NOT nice-latex-file
1076 os << "\\batchmode\n"; // changed
1077 // from \nonstopmode
1078 d->texrow.newline();
1080 if (!original_path.empty()) {
1082 // We don't know the encoding of inputpath
1083 docstring const inputpath = from_utf8(latex_path(original_path));
1084 os << "\\makeatletter\n"
1085 << "\\def\\input@path{{"
1086 << inputpath << "/}}\n"
1087 << "\\makeatother\n";
1088 d->texrow.newline();
1089 d->texrow.newline();
1090 d->texrow.newline();
1093 // Write the preamble
1094 runparams.use_babel = params().writeLaTeX(os, features, d->texrow);
1100 os << "\\begin{document}\n";
1101 d->texrow.newline();
1102 } // output_preamble
1104 d->texrow.start(paragraphs().begin()->id(), 0);
1106 LYXERR(Debug::INFO, "preamble finished, now the body.");
1108 if (!lyxrc.language_auto_begin &&
1109 !params().language->babel().empty()) {
1111 os << from_utf8(subst(lyxrc.language_command_begin,
1113 params().language->babel()))
1115 d->texrow.newline();
1118 Encoding const & encoding = params().encoding();
1119 if (encoding.package() == Encoding::CJK) {
1120 // Open a CJK environment, since in contrast to the encodings
1121 // handled by inputenc the document encoding is not set in
1122 // the preamble if it is handled by CJK.sty.
1123 os << "\\begin{CJK}{" << from_ascii(encoding.latexName())
1125 d->texrow.newline();
1128 // if we are doing a real file with body, even if this is the
1129 // child of some other buffer, let's cut the link here.
1130 // This happens for example if only a child document is printed.
1131 Buffer const * save_parent = 0;
1132 if (output_preamble) {
1133 save_parent = d->parent_buffer;
1134 d->parent_buffer = 0;
1137 loadChildDocuments();
1140 latexParagraphs(*this, paragraphs(), os, d->texrow, runparams);
1142 // Restore the parenthood if needed
1143 if (output_preamble)
1144 d->parent_buffer = save_parent;
1146 // add this just in case after all the paragraphs
1148 d->texrow.newline();
1150 if (encoding.package() == Encoding::CJK) {
1151 // Close the open CJK environment.
1152 // latexParagraphs will have opened one even if the last text
1154 os << "\\end{CJK}\n";
1155 d->texrow.newline();
1158 if (!lyxrc.language_auto_end &&
1159 !params().language->babel().empty()) {
1160 os << from_utf8(subst(lyxrc.language_command_end,
1162 params().language->babel()))
1164 d->texrow.newline();
1167 if (output_preamble) {
1168 os << "\\end{document}\n";
1169 d->texrow.newline();
1170 LYXERR(Debug::LATEX, "makeLaTeXFile...done");
1172 LYXERR(Debug::LATEX, "LaTeXFile for inclusion made.");
1174 runparams_in.encoding = runparams.encoding;
1176 // Just to be sure. (Asger)
1177 d->texrow.newline();
1179 LYXERR(Debug::INFO, "Finished making LaTeX file.");
1180 LYXERR(Debug::INFO, "Row count was " << d->texrow.rows() - 1 << '.');
1184 bool Buffer::isLatex() const
1186 return params().getTextClass().outputType() == LATEX;
1190 bool Buffer::isLiterate() const
1192 return params().getTextClass().outputType() == LITERATE;
1196 bool Buffer::isDocBook() const
1198 return params().getTextClass().outputType() == DOCBOOK;
1202 void Buffer::makeDocBookFile(FileName const & fname,
1203 OutputParams const & runparams,
1204 bool const body_only) const
1206 LYXERR(Debug::LATEX, "makeDocBookFile...");
1210 if (!openFileWrite(ofs, fname))
1213 writeDocBookSource(ofs, fname.absFilename(), runparams, body_only);
1217 lyxerr << "File '" << fname << "' was not closed properly." << endl;
1221 void Buffer::writeDocBookSource(odocstream & os, string const & fname,
1222 OutputParams const & runparams,
1223 bool const only_body) const
1225 LaTeXFeatures features(*this, params(), runparams);
1230 TextClass const & tclass = params().getTextClass();
1231 string const top_element = tclass.latexname();
1234 if (runparams.flavor == OutputParams::XML)
1235 os << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
1238 os << "<!DOCTYPE " << from_ascii(top_element) << ' ';
1241 if (! tclass.class_header().empty())
1242 os << from_ascii(tclass.class_header());
1243 else if (runparams.flavor == OutputParams::XML)
1244 os << "PUBLIC \"-//OASIS//DTD DocBook XML//EN\" "
1245 << "\"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd\"";
1247 os << " PUBLIC \"-//OASIS//DTD DocBook V4.2//EN\"";
1249 docstring preamble = from_utf8(params().preamble);
1250 if (runparams.flavor != OutputParams::XML ) {
1251 preamble += "<!ENTITY % output.print.png \"IGNORE\">\n";
1252 preamble += "<!ENTITY % output.print.pdf \"IGNORE\">\n";
1253 preamble += "<!ENTITY % output.print.eps \"IGNORE\">\n";
1254 preamble += "<!ENTITY % output.print.bmp \"IGNORE\">\n";
1257 string const name = runparams.nice
1258 ? changeExtension(absFileName(), ".sgml") : fname;
1259 preamble += features.getIncludedFiles(name);
1260 preamble += features.getLyXSGMLEntities();
1262 if (!preamble.empty()) {
1263 os << "\n [ " << preamble << " ]";
1268 string top = top_element;
1270 if (runparams.flavor == OutputParams::XML)
1271 top += params().language->code();
1273 top += params().language->code().substr(0,2);
1276 if (!params().options.empty()) {
1278 top += params().options;
1281 os << "<!-- " << ((runparams.flavor == OutputParams::XML)? "XML" : "SGML")
1282 << " file was created by LyX " << lyx_version
1283 << "\n See http://www.lyx.org/ for more information -->\n";
1285 params().getTextClass().counters().reset();
1287 loadChildDocuments();
1289 sgml::openTag(os, top);
1291 docbookParagraphs(paragraphs(), *this, os, runparams);
1292 sgml::closeTag(os, top_element);
1296 // chktex should be run with these flags disabled: 3, 22, 25, 30, 38(?)
1297 // Other flags: -wall -v0 -x
1298 int Buffer::runChktex()
1302 // get LaTeX-Filename
1303 FileName const path(temppath());
1304 string const name = addName(path.absFilename(), latexName());
1305 string const org_path = filePath();
1307 support::PathChanger p(path); // path to LaTeX file
1308 message(_("Running chktex..."));
1310 // Generate the LaTeX file if neccessary
1311 OutputParams runparams(¶ms().encoding());
1312 runparams.flavor = OutputParams::LATEX;
1313 runparams.nice = false;
1314 makeLaTeXFile(FileName(name), org_path, runparams);
1317 Chktex chktex(lyxrc.chktex_command, onlyFilename(name), filePath());
1318 int const res = chktex.run(terr); // run chktex
1321 Alert::error(_("chktex failure"),
1322 _("Could not run chktex successfully."));
1323 } else if (res > 0) {
1324 ErrorList & errlist = d->errorLists["ChkTeX"];
1326 bufferErrors(terr, errlist);
1337 void Buffer::validate(LaTeXFeatures & features) const
1339 TextClass const & tclass = params().getTextClass();
1341 if (params().outputChanges) {
1342 bool dvipost = LaTeXFeatures::isAvailable("dvipost");
1343 bool xcolorsoul = LaTeXFeatures::isAvailable("soul") &&
1344 LaTeXFeatures::isAvailable("xcolor");
1346 if (features.runparams().flavor == OutputParams::LATEX) {
1348 features.require("ct-dvipost");
1349 features.require("dvipost");
1350 } else if (xcolorsoul) {
1351 features.require("ct-xcolor-soul");
1352 features.require("soul");
1353 features.require("xcolor");
1355 features.require("ct-none");
1357 } else if (features.runparams().flavor == OutputParams::PDFLATEX ) {
1359 features.require("ct-xcolor-soul");
1360 features.require("soul");
1361 features.require("xcolor");
1362 features.require("pdfcolmk"); // improves color handling in PDF output
1364 features.require("ct-none");
1369 // AMS Style is at document level
1370 if (params().use_amsmath == BufferParams::package_on
1371 || tclass.provides("amsmath"))
1372 features.require("amsmath");
1373 if (params().use_esint == BufferParams::package_on)
1374 features.require("esint");
1376 loadChildDocuments();
1378 for_each(paragraphs().begin(), paragraphs().end(),
1379 boost::bind(&Paragraph::validate, _1, boost::ref(features)));
1381 // the bullet shapes are buffer level not paragraph level
1382 // so they are tested here
1383 for (int i = 0; i < 4; ++i) {
1384 if (params().user_defined_bullet(i) != ITEMIZE_DEFAULTS[i]) {
1385 int const font = params().user_defined_bullet(i).getFont();
1387 int const c = params()
1388 .user_defined_bullet(i)
1395 features.require("latexsym");
1397 } else if (font == 1) {
1398 features.require("amssymb");
1399 } else if ((font >= 2 && font <= 5)) {
1400 features.require("pifont");
1405 if (lyxerr.debugging(Debug::LATEX)) {
1406 features.showStruct();
1411 void Buffer::getLabelList(vector<docstring> & list) const
1413 /// if this is a child document and the parent is already loaded
1414 /// Use the parent's list instead [ale990407]
1415 Buffer const * tmp = masterBuffer();
1417 lyxerr << "masterBuffer() failed!" << endl;
1421 tmp->getLabelList(list);
1425 loadChildDocuments();
1427 for (InsetIterator it = inset_iterator_begin(inset()); it; ++it)
1428 it.nextInset()->getLabelList(*this, list);
1432 void Buffer::updateBibfilesCache() const
1434 // if this is a child document and the parent is already loaded
1435 // update the parent's cache instead
1436 Buffer const * tmp = masterBuffer();
1439 tmp->updateBibfilesCache();
1443 bibfilesCache_.clear();
1444 for (InsetIterator it = inset_iterator_begin(inset()); it; ++it) {
1445 if (it->lyxCode() == BIBTEX_CODE) {
1446 InsetBibtex const & inset =
1447 static_cast<InsetBibtex const &>(*it);
1448 vector<FileName> const bibfiles = inset.getFiles(*this);
1449 bibfilesCache_.insert(bibfilesCache_.end(),
1452 } else if (it->lyxCode() == INCLUDE_CODE) {
1453 InsetInclude & inset =
1454 static_cast<InsetInclude &>(*it);
1455 inset.updateBibfilesCache(*this);
1456 vector<FileName> const & bibfiles =
1457 inset.getBibfilesCache(*this);
1458 bibfilesCache_.insert(bibfilesCache_.end(),
1466 vector<FileName> const & Buffer::getBibfilesCache() const
1468 // if this is a child document and the parent is already loaded
1469 // use the parent's cache instead
1470 Buffer const * tmp = masterBuffer();
1473 return tmp->getBibfilesCache();
1475 // We update the cache when first used instead of at loading time.
1476 if (bibfilesCache_.empty())
1477 const_cast<Buffer *>(this)->updateBibfilesCache();
1479 return bibfilesCache_;
1483 bool Buffer::isDepClean(string const & name) const
1485 DepClean::const_iterator const it = d->dep_clean.find(name);
1486 if (it == d->dep_clean.end())
1492 void Buffer::markDepClean(string const & name)
1494 d->dep_clean[name] = true;
1498 bool Buffer::dispatch(string const & command, bool * result)
1500 return dispatch(lyxaction.lookupFunc(command), result);
1504 bool Buffer::dispatch(FuncRequest const & func, bool * result)
1506 bool dispatched = true;
1508 switch (func.action) {
1509 case LFUN_BUFFER_EXPORT: {
1510 bool const tmp = doExport(to_utf8(func.argument()), false);
1523 void Buffer::changeLanguage(Language const * from, Language const * to)
1528 for_each(par_iterator_begin(),
1530 bind(&Paragraph::changeLanguage, _1, params(), from, to));
1534 bool Buffer::isMultiLingual() const
1536 ParConstIterator end = par_iterator_end();
1537 for (ParConstIterator it = par_iterator_begin(); it != end; ++it)
1538 if (it->isMultiLingual(params()))
1545 ParIterator Buffer::getParFromID(int const id) const
1547 ParConstIterator it = par_iterator_begin();
1548 ParConstIterator const end = par_iterator_end();
1551 // John says this is called with id == -1 from undo
1552 lyxerr << "getParFromID(), id: " << id << endl;
1556 for (; it != end; ++it)
1564 bool Buffer::hasParWithID(int const id) const
1566 ParConstIterator const it = getParFromID(id);
1567 return it != par_iterator_end();
1571 ParIterator Buffer::par_iterator_begin()
1573 return lyx::par_iterator_begin(inset());
1577 ParIterator Buffer::par_iterator_end()
1579 return lyx::par_iterator_end(inset());
1583 ParConstIterator Buffer::par_iterator_begin() const
1585 return lyx::par_const_iterator_begin(inset());
1589 ParConstIterator Buffer::par_iterator_end() const
1591 return lyx::par_const_iterator_end(inset());
1595 Language const * Buffer::language() const
1597 return params().language;
1601 docstring const Buffer::B_(string const & l10n) const
1603 return params().B_(l10n);
1607 bool Buffer::isClean() const
1609 return d->lyx_clean;
1613 bool Buffer::isBakClean() const
1615 return d->bak_clean;
1619 bool Buffer::isExternallyModified(CheckMethod method) const
1621 BOOST_ASSERT(d->filename.exists());
1622 // if method == timestamp, check timestamp before checksum
1623 return (method == checksum_method
1624 || d->timestamp_ != d->filename.lastModified())
1625 && d->checksum_ != d->filename.checksum();
1629 void Buffer::saveCheckSum(FileName const & file) const
1631 if (file.exists()) {
1632 d->timestamp_ = file.lastModified();
1633 d->checksum_ = file.checksum();
1635 // in the case of save to a new file.
1642 void Buffer::markClean() const
1644 if (!d->lyx_clean) {
1645 d->lyx_clean = true;
1648 // if the .lyx file has been saved, we don't need an
1650 d->bak_clean = true;
1654 void Buffer::markBakClean() const
1656 d->bak_clean = true;
1660 void Buffer::setUnnamed(bool flag)
1666 bool Buffer::isUnnamed() const
1672 // FIXME: this function should be moved to buffer_pimpl.C
1673 void Buffer::markDirty()
1676 d->lyx_clean = false;
1679 d->bak_clean = false;
1681 DepClean::iterator it = d->dep_clean.begin();
1682 DepClean::const_iterator const end = d->dep_clean.end();
1684 for (; it != end; ++it)
1689 FileName Buffer::fileName() const
1695 string Buffer::absFileName() const
1697 return d->filename.absFilename();
1701 string Buffer::filePath() const
1703 return d->filename.onlyPath().absFilename();
1707 bool Buffer::isReadonly() const
1709 return d->read_only;
1713 void Buffer::setParent(Buffer const * buffer)
1715 // Avoids recursive include.
1716 d->parent_buffer = buffer == this ? 0 : buffer;
1720 Buffer const * Buffer::parent()
1722 return d->parent_buffer;
1726 Buffer const * Buffer::masterBuffer() const
1728 if (!d->parent_buffer)
1731 return d->parent_buffer->masterBuffer();
1735 bool Buffer::hasMacro(docstring const & name, Paragraph const & par) const
1737 Impl::PositionToMacroMap::iterator it;
1738 it = d->macros[name].upper_bound(par.macrocontextPosition());
1739 if (it != d->macros[name].end())
1742 // If there is a master buffer, query that
1743 Buffer const * master = masterBuffer();
1744 if (master && master != this)
1745 return master->hasMacro(name);
1747 return MacroTable::globalMacros().has(name);
1751 bool Buffer::hasMacro(docstring const & name) const
1753 if( !d->macros[name].empty() )
1756 // If there is a master buffer, query that
1757 Buffer const * master = masterBuffer();
1758 if (master && master != this)
1759 return master->hasMacro(name);
1761 return MacroTable::globalMacros().has(name);
1765 MacroData const & Buffer::getMacro(docstring const & name,
1766 Paragraph const & par) const
1768 Impl::PositionToMacroMap::iterator it;
1769 it = d->macros[name].upper_bound(par.macrocontextPosition());
1770 if( it != d->macros[name].end() )
1773 // If there is a master buffer, query that
1774 Buffer const * master = masterBuffer();
1775 if (master && master != this)
1776 return master->getMacro(name);
1778 return MacroTable::globalMacros().get(name);
1782 MacroData const & Buffer::getMacro(docstring const & name) const
1784 Impl::PositionToMacroMap::iterator it;
1785 it = d->macros[name].begin();
1786 if( it != d->macros[name].end() )
1789 // If there is a master buffer, query that
1790 Buffer const * master = masterBuffer();
1791 if (master && master != this)
1792 return master->getMacro(name);
1794 return MacroTable::globalMacros().get(name);
1798 void Buffer::updateMacros()
1800 // start with empty table
1801 d->macros = Impl::NameToPositionMacroMap();
1803 // Iterate over buffer
1804 ParagraphList & pars = text().paragraphs();
1805 for (size_t i = 0, n = pars.size(); i != n; ++i) {
1806 // set position again
1807 pars[i].setMacrocontextPosition(i);
1809 //lyxerr << "searching main par " << i
1810 // << " for macro definitions" << std::endl;
1811 InsetList const & insets = pars[i].insetList();
1812 InsetList::const_iterator it = insets.begin();
1813 InsetList::const_iterator end = insets.end();
1814 for ( ; it != end; ++it) {
1815 if (it->inset->lyxCode() != MATHMACRO_CODE)
1819 MathMacroTemplate const & macroTemplate
1820 = static_cast<MathMacroTemplate const &>(*it->inset);
1823 if (macroTemplate.validMacro()) {
1824 MacroData macro = macroTemplate.asMacroData();
1827 // call hasMacro here instead of directly querying mc to
1828 // also take the master document into consideration
1829 macro.setRedefinition(hasMacro(macroTemplate.name()));
1831 // register macro (possibly overwrite the previous one of this paragraph)
1832 d->macros[macroTemplate.name()][i] = macro;
1839 void Buffer::changeRefsIfUnique(docstring const & from, docstring const & to,
1842 //FIXME: This does not work for child documents yet.
1843 BOOST_ASSERT(code == CITE_CODE || code == REF_CODE);
1844 // Check if the label 'from' appears more than once
1845 vector<docstring> labels;
1848 if (code == CITE_CODE) {
1850 keys.fillWithBibKeys(this);
1851 BiblioInfo::const_iterator bit = keys.begin();
1852 BiblioInfo::const_iterator bend = keys.end();
1854 for (; bit != bend; ++bit)
1856 labels.push_back(bit->first);
1859 getLabelList(labels);
1860 paramName = "reference";
1863 if (std::count(labels.begin(), labels.end(), from) > 1)
1866 for (InsetIterator it = inset_iterator_begin(inset()); it; ++it) {
1867 if (it->lyxCode() == code) {
1868 InsetCommand & inset = static_cast<InsetCommand &>(*it);
1869 docstring const oldValue = inset.getParam(paramName);
1870 if (oldValue == from)
1871 inset.setParam(paramName, to);
1877 void Buffer::getSourceCode(odocstream & os, pit_type par_begin,
1878 pit_type par_end, bool full_source)
1880 OutputParams runparams(¶ms().encoding());
1881 runparams.nice = true;
1882 runparams.flavor = OutputParams::LATEX;
1883 runparams.linelen = lyxrc.plaintext_linelen;
1884 // No side effect of file copying and image conversion
1885 runparams.dryrun = true;
1889 os << "% " << _("Preview source code") << "\n\n";
1890 d->texrow.newline();
1891 d->texrow.newline();
1893 writeLaTeXSource(os, filePath(), runparams, true, true);
1895 writeDocBookSource(os, absFileName(), runparams, false);
1898 runparams.par_begin = par_begin;
1899 runparams.par_end = par_end;
1900 if (par_begin + 1 == par_end)
1902 << bformat(_("Preview source code for paragraph %1$d"), par_begin)
1906 << bformat(_("Preview source code from paragraph %1$s to %2$s"),
1907 convert<docstring>(par_begin),
1908 convert<docstring>(par_end - 1))
1910 d->texrow.newline();
1911 d->texrow.newline();
1912 // output paragraphs
1914 latexParagraphs(*this, paragraphs(), os, d->texrow, runparams);
1917 docbookParagraphs(paragraphs(), *this, os, runparams);
1923 ErrorList & Buffer::errorList(string const & type) const
1925 static ErrorList emptyErrorList;
1926 std::map<string, ErrorList>::iterator I = d->errorLists.find(type);
1927 if (I == d->errorLists.end())
1928 return emptyErrorList;
1934 void Buffer::structureChanged() const
1937 gui_->structureChanged();
1941 void Buffer::errors(std::string const & err) const
1948 void Buffer::message(docstring const & msg) const
1955 void Buffer::setBusy(bool on) const
1962 void Buffer::setReadOnly(bool on) const
1965 d->wa_->setReadOnly(on);
1969 void Buffer::updateTitles() const
1972 d->wa_->updateTitles();
1976 void Buffer::resetAutosaveTimers() const
1979 gui_->resetAutosaveTimers();
1983 void Buffer::setGuiDelegate(frontend::GuiBufferDelegate * gui)
1992 class AutoSaveBuffer : public support::ForkedProcess {
1995 AutoSaveBuffer(Buffer const & buffer, FileName const & fname)
1996 : buffer_(buffer), fname_(fname) {}
1998 virtual boost::shared_ptr<ForkedProcess> clone() const
2000 return boost::shared_ptr<ForkedProcess>(new AutoSaveBuffer(*this));
2005 command_ = to_utf8(bformat(_("Auto-saving %1$s"),
2006 from_utf8(fname_.absFilename())));
2007 return run(DontWait);
2011 virtual int generateChild();
2013 Buffer const & buffer_;
2018 #if !defined (HAVE_FORK)
2022 int AutoSaveBuffer::generateChild()
2024 // tmp_ret will be located (usually) in /tmp
2025 // will that be a problem?
2026 pid_t const pid = fork();
2027 // If you want to debug the autosave
2028 // you should set pid to -1, and comment out the fork.
2029 if (pid == 0 || pid == -1) {
2030 // pid = -1 signifies that lyx was unable
2031 // to fork. But we will do the save
2033 bool failed = false;
2035 FileName const tmp_ret(tempName(FileName(), "lyxauto"));
2036 if (!tmp_ret.empty()) {
2037 buffer_.writeFile(tmp_ret);
2038 // assume successful write of tmp_ret
2039 if (!rename(tmp_ret, fname_)) {
2041 // most likely couldn't move between
2042 // filesystems unless write of tmp_ret
2043 // failed so remove tmp file (if it
2045 tmp_ret.removeFile();
2052 // failed to write/rename tmp_ret so try writing direct
2053 if (!buffer_.writeFile(fname_)) {
2054 // It is dangerous to do this in the child,
2055 // but safe in the parent, so...
2056 if (pid == -1) // emit message signal.
2057 buffer_.message(_("Autosave failed!"));
2060 if (pid == 0) { // we are the child so...
2070 // Perfect target for a thread...
2071 void Buffer::autoSave() const
2073 if (isBakClean() || isReadonly()) {
2074 // We don't save now, but we'll try again later
2075 resetAutosaveTimers();
2079 // emit message signal.
2080 message(_("Autosaving current document..."));
2082 // create autosave filename
2083 string fname = filePath();
2085 fname += onlyFilename(absFileName());
2088 AutoSaveBuffer autosave(*this, FileName(fname));
2092 resetAutosaveTimers();
2096 /** Write a buffer to a new file name and rename the buffer
2097 according to the new file name.
2099 This function is e.g. used by menu callbacks and
2100 LFUN_BUFFER_WRITE_AS.
2102 If 'newname' is empty (the default), the user is asked via a
2103 dialog for the buffer's new name and location.
2105 If 'newname' is non-empty and has an absolute path, that is used.
2106 Otherwise the base directory of the buffer is used as the base
2107 for any relative path in 'newname'.
2110 bool Buffer::writeAs(string const & newname)
2112 string fname = absFileName();
2113 string const oldname = fname;
2115 if (newname.empty()) { /// No argument? Ask user through dialog
2118 FileDialog dlg(_("Choose a filename to save document as"),
2119 LFUN_BUFFER_WRITE_AS);
2120 dlg.setButton1(_("Documents|#o#O"), from_utf8(lyxrc.document_path));
2121 dlg.setButton2(_("Templates|#T#t"), from_utf8(lyxrc.template_path));
2123 if (!support::isLyXFilename(fname))
2126 support::FileFilterList const filter(_("LyX Documents (*.lyx)"));
2128 FileDialog::Result result =
2129 dlg.save(from_utf8(onlyPath(fname)),
2131 from_utf8(onlyFilename(fname)));
2133 if (result.first == FileDialog::Later)
2136 fname = to_utf8(result.second);
2141 // Make sure the absolute filename ends with appropriate suffix
2142 fname = makeAbsPath(fname).absFilename();
2143 if (!support::isLyXFilename(fname))
2147 fname = makeAbsPath(newname, onlyPath(oldname)).absFilename();
2149 if (FileName(fname).exists()) {
2150 docstring const file = makeDisplayPath(fname, 30);
2151 docstring text = bformat(_("The document %1$s already "
2152 "exists.\n\nDo you want to "
2153 "overwrite that document?"),
2155 int const ret = Alert::prompt(_("Overwrite document?"),
2156 text, 0, 1, _("&Overwrite"), _("&Cancel"));
2162 // Ok, change the name of the buffer
2165 bool unnamed = isUnnamed();
2167 saveCheckSum(FileName(fname));
2170 setFileName(oldname);
2171 setUnnamed(unnamed);
2172 saveCheckSum(FileName(oldname));
2176 removeAutosaveFile(oldname);
2181 bool Buffer::menuWrite()
2184 LyX::ref().session().lastFiles().add(FileName(absFileName()));
2188 // FIXME: we don't tell the user *WHY* the save failed !!
2190 docstring const file = makeDisplayPath(absFileName(), 30);
2192 docstring text = bformat(_("The document %1$s could not be saved.\n\n"
2193 "Do you want to rename the document and "
2194 "try again?"), file);
2195 int const ret = Alert::prompt(_("Rename and save?"),
2196 text, 0, 1, _("&Rename"), _("&Cancel"));
2205 void Buffer::resetChildDocuments(bool close_them) const
2207 for (InsetIterator it = inset_iterator_begin(inset()); it; ++it) {
2208 if (it->lyxCode() != INCLUDE_CODE)
2210 InsetCommand const & inset = static_cast<InsetCommand const &>(*it);
2211 InsetCommandParams const & ip = inset.params();
2213 resetParentBuffer(this, ip, close_them);
2216 if (use_gui && masterBuffer() == this)
2217 updateLabels(*this);
2221 void Buffer::loadChildDocuments() const
2223 bool parse_error = false;
2225 for (InsetIterator it = inset_iterator_begin(inset()); it; ++it) {
2226 if (it->lyxCode() != INCLUDE_CODE)
2228 InsetCommand const & inset = static_cast<InsetCommand const &>(*it);
2229 InsetCommandParams const & ip = inset.params();
2230 Buffer * child = loadIfNeeded(*this, ip);
2233 parse_error |= !child->errorList("Parse").empty();
2234 child->loadChildDocuments();
2237 if (use_gui && masterBuffer() == this)
2238 updateLabels(*this);
2242 string Buffer::bufferFormat() const
2252 bool Buffer::doExport(string const & format, bool put_in_tempdir,
2253 string & result_file) const
2255 string backend_format;
2256 OutputParams runparams(¶ms().encoding());
2257 runparams.flavor = OutputParams::LATEX;
2258 runparams.linelen = lyxrc.plaintext_linelen;
2259 vector<string> backs = backends();
2260 if (find(backs.begin(), backs.end(), format) == backs.end()) {
2261 // Get shortest path to format
2262 Graph::EdgePath path;
2263 for (vector<string>::const_iterator it = backs.begin();
2264 it != backs.end(); ++it) {
2265 Graph::EdgePath p = theConverters().getPath(*it, format);
2266 if (!p.empty() && (path.empty() || p.size() < path.size())) {
2267 backend_format = *it;
2272 runparams.flavor = theConverters().getFlavor(path);
2274 Alert::error(_("Couldn't export file"),
2275 bformat(_("No information for exporting the format %1$s."),
2276 formats.prettyName(format)));
2280 backend_format = format;
2281 // FIXME: Don't hardcode format names here, but use a flag
2282 if (backend_format == "pdflatex")
2283 runparams.flavor = OutputParams::PDFLATEX;
2286 string filename = latexName(false);
2287 filename = addName(temppath(), filename);
2288 filename = changeExtension(filename,
2289 formats.extension(backend_format));
2291 // Plain text backend
2292 if (backend_format == "text")
2293 writePlaintextFile(*this, FileName(filename), runparams);
2295 else if (backend_format == "lyx")
2296 writeFile(FileName(filename));
2298 else if (isDocBook()) {
2299 runparams.nice = !put_in_tempdir;
2300 makeDocBookFile(FileName(filename), runparams);
2303 else if (backend_format == format) {
2304 runparams.nice = true;
2305 if (!makeLaTeXFile(FileName(filename), string(), runparams))
2307 } else if (!lyxrc.tex_allows_spaces
2308 && support::contains(filePath(), ' ')) {
2309 Alert::error(_("File name error"),
2310 _("The directory path to the document cannot contain spaces."));
2313 runparams.nice = false;
2314 if (!makeLaTeXFile(FileName(filename), filePath(), runparams))
2318 string const error_type = (format == "program")
2319 ? "Build" : bufferFormat();
2320 string const ext = formats.extension(format);
2321 FileName const tmp_result_file(changeExtension(filename, ext));
2322 bool const success = theConverters().convert(this, FileName(filename),
2323 tmp_result_file, FileName(absFileName()), backend_format, format,
2324 errorList(error_type));
2325 // Emit the signal to show the error list.
2326 if (format != backend_format)
2332 result_file = tmp_result_file.absFilename();
2334 result_file = changeExtension(absFileName(), ext);
2335 // We need to copy referenced files (e. g. included graphics
2336 // if format == "dvi") to the result dir.
2337 vector<ExportedFile> const files =
2338 runparams.exportdata->externalFiles(format);
2339 string const dest = onlyPath(result_file);
2340 CopyStatus status = SUCCESS;
2341 for (vector<ExportedFile>::const_iterator it = files.begin();
2342 it != files.end() && status != CANCEL; ++it) {
2344 formats.getFormatFromFile(it->sourceName);
2345 status = copyFile(fmt, it->sourceName,
2346 makeAbsPath(it->exportName, dest),
2347 it->exportName, status == FORCE);
2349 if (status == CANCEL) {
2350 message(_("Document export cancelled."));
2351 } else if (tmp_result_file.exists()) {
2352 // Finally copy the main file
2353 status = copyFile(format, tmp_result_file,
2354 FileName(result_file), result_file,
2356 message(bformat(_("Document exported as %1$s "
2358 formats.prettyName(format),
2359 makeDisplayPath(result_file)));
2361 // This must be a dummy converter like fax (bug 1888)
2362 message(bformat(_("Document exported as %1$s"),
2363 formats.prettyName(format)));
2371 bool Buffer::doExport(string const & format, bool put_in_tempdir) const
2374 return doExport(format, put_in_tempdir, result_file);
2378 bool Buffer::preview(string const & format) const
2381 if (!doExport(format, true, result_file))
2383 return formats.view(*this, FileName(result_file), format);
2387 bool Buffer::isExportable(string const & format) const
2389 vector<string> backs = backends();
2390 for (vector<string>::const_iterator it = backs.begin();
2391 it != backs.end(); ++it)
2392 if (theConverters().isReachable(*it, format))
2398 vector<Format const *> Buffer::exportableFormats(bool only_viewable) const
2400 vector<string> backs = backends();
2401 vector<Format const *> result =
2402 theConverters().getReachable(backs[0], only_viewable, true);
2403 for (vector<string>::const_iterator it = backs.begin() + 1;
2404 it != backs.end(); ++it) {
2405 vector<Format const *> r =
2406 theConverters().getReachable(*it, only_viewable, false);
2407 result.insert(result.end(), r.begin(), r.end());
2413 vector<string> Buffer::backends() const
2416 if (params().getTextClass().isTeXClassAvailable()) {
2417 v.push_back(bufferFormat());
2418 // FIXME: Don't hardcode format names here, but use a flag
2419 if (v.back() == "latex")
2420 v.push_back("pdflatex");
2422 v.push_back("text");
2428 bool Buffer::readFileHelper(FileName const & s)
2430 // File information about normal file
2432 docstring const file = makeDisplayPath(s.absFilename(), 50);
2433 docstring text = bformat(_("The specified document\n%1$s"
2434 "\ncould not be read."), file);
2435 Alert::error(_("Could not read document"), text);
2439 // Check if emergency save file exists and is newer.
2440 FileName const e(s.absFilename() + ".emergency");
2442 if (e.exists() && s.exists() && e.lastModified() > s.lastModified()) {
2443 docstring const file = makeDisplayPath(s.absFilename(), 20);
2444 docstring const text =
2445 bformat(_("An emergency save of the document "
2447 "Recover emergency save?"), file);
2448 switch (Alert::prompt(_("Load emergency save?"), text, 0, 2,
2449 _("&Recover"), _("&Load Original"),
2453 // the file is not saved if we load the emergency file.
2463 // Now check if autosave file is newer.
2464 FileName const a(onlyPath(s.absFilename()) + '#' + onlyFilename(s.absFilename()) + '#');
2466 if (a.exists() && s.exists() && a.lastModified() > s.lastModified()) {
2467 docstring const file = makeDisplayPath(s.absFilename(), 20);
2468 docstring const text =
2469 bformat(_("The backup of the document "
2470 "%1$s is newer.\n\nLoad the "
2471 "backup instead?"), file);
2472 switch (Alert::prompt(_("Load backup?"), text, 0, 2,
2473 _("&Load backup"), _("Load &original"),
2477 // the file is not saved if we load the autosave file.
2481 // Here we delete the autosave
2492 bool Buffer::loadLyXFile(FileName const & s)
2494 if (s.isReadableFile()) {
2495 if (readFileHelper(s)) {
2496 lyxvc().file_found_hook(s);
2497 if (!s.isWritable())
2502 docstring const file = makeDisplayPath(s.absFilename(), 20);
2503 // Here we probably should run
2504 if (LyXVC::file_not_found_hook(s)) {
2505 docstring const text =
2506 bformat(_("Do you want to retrieve the document"
2507 " %1$s from version control?"), file);
2508 int const ret = Alert::prompt(_("Retrieve from version control?"),
2509 text, 0, 1, _("&Retrieve"), _("&Cancel"));
2512 // How can we know _how_ to do the checkout?
2513 // With the current VC support it has to be,
2514 // a RCS file since CVS do not have special ,v files.
2516 return loadLyXFile(s);
2524 void Buffer::bufferErrors(TeXErrors const & terr, ErrorList & errorList) const
2526 TeXErrors::Errors::const_iterator cit = terr.begin();
2527 TeXErrors::Errors::const_iterator end = terr.end();
2529 for (; cit != end; ++cit) {
2532 int errorRow = cit->error_in_line;
2533 bool found = d->texrow.getIdFromRow(errorRow, id_start,
2539 found = d->texrow.getIdFromRow(errorRow, id_end, pos_end);
2540 } while (found && id_start == id_end && pos_start == pos_end);
2542 errorList.push_back(ErrorItem(cit->error_desc,
2543 cit->error_text, id_start, pos_start, pos_end));