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"
95 #if !defined (HAVE_FORK)
99 #include <boost/bind.hpp>
100 #include <boost/shared_ptr.hpp>
110 using std::make_pair;
115 using std::ostringstream;
126 using support::addName;
127 using support::bformat;
128 using support::changeExtension;
129 using support::cmd_ret;
130 using support::createBufferTmpDir;
131 using support::FileName;
132 using support::libFileSearch;
133 using support::latex_path;
134 using support::ltrim;
135 using support::makeAbsPath;
136 using support::makeDisplayPath;
137 using support::makeLatexName;
138 using support::onlyFilename;
139 using support::onlyPath;
140 using support::quoteName;
141 using support::removeAutosaveFile;
142 using support::rename;
143 using support::runCommand;
144 using support::split;
145 using support::subst;
146 using support::tempName;
148 using support::suffixIs;
150 namespace Alert = frontend::Alert;
151 namespace os = support::os;
155 int const LYX_FORMAT = 303; // Uwe: Serbocroatian
160 typedef std::map<string, bool> DepClean;
165 Impl(Buffer & parent, FileName const & file, bool readonly);
178 mutable TexRow texrow;
179 Buffer const * parent_buffer;
181 /// need to regenerate .tex?
185 mutable bool lyx_clean;
187 /// is autosave needed?
188 mutable bool bak_clean;
190 /// is this a unnamed file (New...)?
196 /// name of the file the buffer is associated with.
199 /** Set to true only when the file is fully loaded.
200 * Used to prevent the premature generation of previews
201 * and by the citation inset.
203 bool file_fully_loaded;
205 /// our Text that should be wrapped in an InsetText
209 mutable TocBackend toc_backend;
212 typedef std::map<unsigned int, MacroData, std::greater<int> > PositionToMacroMap;
213 typedef std::map<docstring, PositionToMacroMap> NameToPositionMacroMap;
214 NameToPositionMacroMap macros;
216 /// Container for all sort of Buffer dependant errors.
217 map<string, ErrorList> errorLists;
219 /// all embedded files of this buffer
220 EmbeddedFiles embedded_files;
222 /// timestamp and checksum used to test if the file has been externally
223 /// modified. (Used to properly enable 'File->Revert to saved', bug 4114).
225 unsigned long checksum_;
228 frontend::WorkAreaManager * wa_;
235 Buffer::Impl::Impl(Buffer & parent, FileName const & file, bool readonly_)
236 : parent_buffer(0), lyx_clean(true), bak_clean(true), unnamed(false),
237 read_only(readonly_), filename(file), file_fully_loaded(false),
238 inset(params), toc_backend(&parent), embedded_files(&parent),
239 timestamp_(0), checksum_(0), wa_(0), undo_(parent)
241 inset.setAutoBreakRows(true);
242 lyxvc.setBuffer(&parent);
243 temppath = createBufferTmpDir();
245 // FIXME: And now do something if temppath == string(), because we
246 // assume from now on that temppath points to a valid temp dir.
247 // See http://www.mail-archive.com/lyx-devel@lists.lyx.org/msg67406.html
250 wa_ = new frontend::WorkAreaManager;
254 Buffer::Buffer(string const & file, bool readonly)
255 : d(new Impl(*this, FileName(file), readonly)), gui_(0)
257 LYXERR(Debug::INFO, "Buffer::Buffer()");
263 LYXERR(Debug::INFO, "Buffer::~Buffer()");
264 // here the buffer should take care that it is
265 // saved properly, before it goes into the void.
267 Buffer const * master = masterBuffer();
268 if (master != this && use_gui)
269 // We are closing buf which was a child document so we
270 // must update the labels and section numbering of its master
272 updateLabels(*master);
274 resetChildDocuments(false);
276 if (!temppath().empty() && !FileName(temppath()).destroyDirectory()) {
277 Alert::warning(_("Could not remove temporary directory"),
278 bformat(_("Could not remove the temporary directory %1$s"),
279 from_utf8(temppath())));
282 // Remove any previewed LaTeX snippets associated with this buffer.
283 graphics::Previews::get().removeLoader(*this);
287 void Buffer::changed() const
294 frontend::WorkAreaManager & Buffer::workAreaManager() const
296 BOOST_ASSERT(d->wa_);
301 Text & Buffer::text() const
303 return const_cast<Text &>(d->inset.text_);
307 Inset & Buffer::inset() const
309 return const_cast<InsetText &>(d->inset);
313 BufferParams & Buffer::params()
319 BufferParams const & Buffer::params() const
325 ParagraphList & Buffer::paragraphs()
327 return text().paragraphs();
331 ParagraphList const & Buffer::paragraphs() const
333 return text().paragraphs();
337 LyXVC & Buffer::lyxvc()
343 LyXVC const & Buffer::lyxvc() const
349 string const & Buffer::temppath() const
355 TexRow const & Buffer::texrow() const
361 TocBackend & Buffer::tocBackend() const
363 return d->toc_backend;
367 EmbeddedFiles & Buffer::embeddedFiles()
369 return d->embedded_files;
373 EmbeddedFiles const & Buffer::embeddedFiles() const
375 return d->embedded_files;
379 Undo & Buffer::undo()
385 string Buffer::latexName(bool const no_path) const
387 FileName latex_name = makeLatexName(d->filename);
388 return no_path ? latex_name.onlyFileName()
389 : latex_name.absFilename();
393 string Buffer::logName(LogType * type) const
395 string const filename = latexName(false);
397 if (filename.empty()) {
403 string const path = temppath();
405 FileName const fname(addName(temppath(),
406 onlyFilename(changeExtension(filename,
408 FileName const bname(
409 addName(path, onlyFilename(
410 changeExtension(filename,
411 formats.extension("literate") + ".out"))));
413 // If no Latex log or Build log is newer, show Build log
415 if (bname.exists() &&
416 (!fname.exists() || fname.lastModified() < bname.lastModified())) {
417 LYXERR(Debug::FILES, "Log name calculated as: " << bname);
420 return bname.absFilename();
422 LYXERR(Debug::FILES, "Log name calculated as: " << fname);
425 return fname.absFilename();
429 void Buffer::setReadonly(bool const flag)
431 if (d->read_only != flag) {
438 void Buffer::setFileName(string const & newfile)
440 d->filename = makeAbsPath(newfile);
441 setReadonly(d->filename.isReadOnly());
446 int Buffer::readHeader(Lexer & lex)
448 int unknown_tokens = 0;
450 int begin_header_line = -1;
452 // Initialize parameters that may be/go lacking in header:
453 params().branchlist().clear();
454 params().preamble.erase();
455 params().options.erase();
456 params().float_placement.erase();
457 params().paperwidth.erase();
458 params().paperheight.erase();
459 params().leftmargin.erase();
460 params().rightmargin.erase();
461 params().topmargin.erase();
462 params().bottommargin.erase();
463 params().headheight.erase();
464 params().headsep.erase();
465 params().footskip.erase();
466 params().listings_params.clear();
467 params().clearLayoutModules();
468 params().pdfoptions().clear();
470 for (int i = 0; i < 4; ++i) {
471 params().user_defined_bullet(i) = ITEMIZE_DEFAULTS[i];
472 params().temp_bullet(i) = ITEMIZE_DEFAULTS[i];
475 ErrorList & errorList = d->errorLists["Parse"];
479 string const token = lex.getString();
484 if (token == "\\end_header")
488 if (token == "\\begin_header") {
489 begin_header_line = line;
493 LYXERR(Debug::PARSER, "Handling document header token: `"
496 string unknown = params().readToken(lex, token, d->filename.onlyPath());
497 if (!unknown.empty()) {
498 if (unknown[0] != '\\' && token == "\\textclass") {
499 Alert::warning(_("Unknown document class"),
500 bformat(_("Using the default document class, because the "
501 "class %1$s is unknown."), from_utf8(unknown)));
504 docstring const s = bformat(_("Unknown token: "
508 errorList.push_back(ErrorItem(_("Document header error"),
513 if (begin_header_line) {
514 docstring const s = _("\\begin_header is missing");
515 errorList.push_back(ErrorItem(_("Document header error"),
519 return unknown_tokens;
524 // changed to be public and have one parameter
525 // Returns false if "\end_document" is not read (Asger)
526 bool Buffer::readDocument(Lexer & lex)
528 ErrorList & errorList = d->errorLists["Parse"];
532 string const token = lex.getString();
533 if (token != "\\begin_document") {
534 docstring const s = _("\\begin_document is missing");
535 errorList.push_back(ErrorItem(_("Document header error"),
539 // we are reading in a brand new document
540 BOOST_ASSERT(paragraphs().empty());
543 TextClass const & baseClass = textclasslist[params().getBaseClass()];
544 if (!baseClass.load(filePath())) {
545 string theclass = baseClass.name();
546 Alert::error(_("Can't load document class"), bformat(
547 _("Using the default document class, because the "
548 "class %1$s could not be loaded."), from_utf8(theclass)));
549 params().setBaseClass(defaultTextclass());
552 if (params().outputChanges) {
553 bool dvipost = LaTeXFeatures::isAvailable("dvipost");
554 bool xcolorsoul = LaTeXFeatures::isAvailable("soul") &&
555 LaTeXFeatures::isAvailable("xcolor");
557 if (!dvipost && !xcolorsoul) {
558 Alert::warning(_("Changes not shown in LaTeX output"),
559 _("Changes will not be highlighted in LaTeX output, "
560 "because neither dvipost nor xcolor/soul are installed.\n"
561 "Please install these packages or redefine "
562 "\\lyxadded and \\lyxdeleted in the LaTeX preamble."));
563 } else if (!xcolorsoul) {
564 Alert::warning(_("Changes not shown in LaTeX output"),
565 _("Changes will not be highlighted in LaTeX output "
566 "when using pdflatex, because xcolor and soul are not installed.\n"
567 "Please install both packages or redefine "
568 "\\lyxadded and \\lyxdeleted in the LaTeX preamble."));
573 bool const res = text().read(*this, lex, errorList);
574 for_each(text().paragraphs().begin(),
575 text().paragraphs().end(),
576 bind(&Paragraph::setInsetOwner, _1, &inset()));
582 // needed to insert the selection
583 void Buffer::insertStringAsLines(ParagraphList & pars,
584 pit_type & pit, pos_type & pos,
585 Font const & fn, docstring const & str, bool autobreakrows)
589 // insert the string, don't insert doublespace
590 bool space_inserted = true;
591 for (docstring::const_iterator cit = str.begin();
592 cit != str.end(); ++cit) {
593 Paragraph & par = pars[pit];
595 if (autobreakrows && (!par.empty() || par.allowEmpty())) {
596 breakParagraph(params(), pars, pit, pos,
597 par.layout()->isEnvironment());
600 space_inserted = true;
604 // do not insert consecutive spaces if !free_spacing
605 } else if ((*cit == ' ' || *cit == '\t') &&
606 space_inserted && !par.isFreeSpacing()) {
608 } else if (*cit == '\t') {
609 if (!par.isFreeSpacing()) {
610 // tabs are like spaces here
611 par.insertChar(pos, ' ', font, params().trackChanges);
613 space_inserted = true;
615 const pos_type n = 8 - pos % 8;
616 for (pos_type i = 0; i < n; ++i) {
617 par.insertChar(pos, ' ', font, params().trackChanges);
620 space_inserted = true;
622 } else if (!isPrintable(*cit)) {
623 // Ignore unprintables
626 // just insert the character
627 par.insertChar(pos, *cit, font, params().trackChanges);
629 space_inserted = (*cit == ' ');
636 bool Buffer::readString(std::string const & s)
638 params().compressed = false;
640 // remove dummy empty par
641 paragraphs().clear();
643 std::istringstream is(s);
645 FileName const name(tempName());
646 switch (readFile(lex, name, true)) {
650 // We need to call lyx2lyx, so write the input to a file
651 std::ofstream os(name.toFilesystemEncoding().c_str());
654 return readFile(name);
664 bool Buffer::readFile(FileName const & filename)
666 FileName fname(filename);
667 // Check if the file is compressed.
668 string format = filename.guessFormatFromContents();
669 if (format == "zip") {
670 // decompress to a temp directory
671 LYXERR(Debug::FILES, filename << " is in zip format. Unzip to " << temppath());
672 ::unzipToDir(filename.toFilesystemEncoding(), temppath());
674 FileName lyxfile(addName(temppath(), "content.lyx"));
675 // if both manifest.txt and file.lyx exist, this is am embedded file
676 if (lyxfile.exists()) {
677 params().embedded = true;
681 // The embedded lyx file can also be compressed, for backward compatibility
682 format = fname.guessFormatFromContents();
683 if (format == "gzip" || format == "zip" || format == "compress")
684 params().compressed = true;
686 // remove dummy empty par
687 paragraphs().clear();
690 if (readFile(lex, fname) != success)
697 bool Buffer::isFullyLoaded() const
699 return d->file_fully_loaded;
703 void Buffer::setFullyLoaded(bool value)
705 d->file_fully_loaded = value;
709 Buffer::ReadStatus Buffer::readFile(Lexer & lex, FileName const & filename,
712 BOOST_ASSERT(!filename.empty());
715 Alert::error(_("Document could not be read"),
716 bformat(_("%1$s could not be read."), from_utf8(filename.absFilename())));
721 string const token = lex.getString();
724 Alert::error(_("Document could not be read"),
725 bformat(_("%1$s could not be read."), from_utf8(filename.absFilename())));
729 // the first token _must_ be...
730 if (token != "\\lyxformat") {
731 lyxerr << "Token: " << token << endl;
733 Alert::error(_("Document format failure"),
734 bformat(_("%1$s is not a LyX document."),
735 from_utf8(filename.absFilename())));
740 string tmp_format = lex.getString();
741 //lyxerr << "LyX Format: `" << tmp_format << '\'' << endl;
742 // if present remove ".," from string.
743 string::size_type dot = tmp_format.find_first_of(".,");
744 //lyxerr << " dot found at " << dot << endl;
745 if (dot != string::npos)
746 tmp_format.erase(dot, 1);
747 int const file_format = convert<int>(tmp_format);
748 //lyxerr << "format: " << file_format << endl;
750 // save timestamp and checksum of the original disk file, making sure
751 // to not overwrite them with those of the file created in the tempdir
752 // when it has to be converted to the current format.
754 // Save the timestamp and checksum of disk file. If filename is an
755 // emergency file, save the timestamp and checksum of the original lyx file
756 // because isExternallyModified will check for this file. (BUG4193)
757 string diskfile = filename.absFilename();
758 if (suffixIs(diskfile, ".emergency"))
759 diskfile = diskfile.substr(0, diskfile.size() - 10);
760 saveCheckSum(FileName(diskfile));
763 if (file_format != LYX_FORMAT) {
766 // lyx2lyx would fail
769 FileName const tmpfile(tempName());
770 if (tmpfile.empty()) {
771 Alert::error(_("Conversion failed"),
772 bformat(_("%1$s is from a different"
773 " version of LyX, but a temporary"
774 " file for converting it could"
776 from_utf8(filename.absFilename())));
779 FileName const lyx2lyx = libFileSearch("lyx2lyx", "lyx2lyx");
780 if (lyx2lyx.empty()) {
781 Alert::error(_("Conversion script not found"),
782 bformat(_("%1$s is from a different"
783 " version of LyX, but the"
784 " conversion script lyx2lyx"
785 " could not be found."),
786 from_utf8(filename.absFilename())));
789 ostringstream command;
790 command << os::python()
791 << ' ' << quoteName(lyx2lyx.toFilesystemEncoding())
792 << " -t " << convert<string>(LYX_FORMAT)
793 << " -o " << quoteName(tmpfile.toFilesystemEncoding())
794 << ' ' << quoteName(filename.toFilesystemEncoding());
795 string const command_str = command.str();
797 LYXERR(Debug::INFO, "Running '" << command_str << '\'');
799 cmd_ret const ret = runCommand(command_str);
800 if (ret.first != 0) {
801 Alert::error(_("Conversion script failed"),
802 bformat(_("%1$s is from a different version"
803 " of LyX, but the lyx2lyx script"
804 " failed to convert it."),
805 from_utf8(filename.absFilename())));
808 bool const ret = readFile(tmpfile);
809 // Do stuff with tmpfile name and buffer name here.
810 return ret ? success : failure;
815 if (readDocument(lex)) {
816 Alert::error(_("Document format failure"),
817 bformat(_("%1$s ended unexpectedly, which means"
818 " that it is probably corrupted."),
819 from_utf8(filename.absFilename())));
822 d->file_fully_loaded = true;
827 // Should probably be moved to somewhere else: BufferView? LyXView?
828 bool Buffer::save() const
830 // We don't need autosaves in the immediate future. (Asger)
831 resetAutosaveTimers();
833 string const encodedFilename = d->filename.toFilesystemEncoding();
836 bool madeBackup = false;
838 // make a backup if the file already exists
839 if (lyxrc.make_backup && fileName().exists()) {
840 backupName = FileName(absFileName() + '~');
841 if (!lyxrc.backupdir_path.empty()) {
842 string const mangledName =
843 subst(subst(backupName.absFilename(), '/', '!'), ':', '!');
844 backupName = FileName(addName(lyxrc.backupdir_path,
847 if (fileName().copyTo(backupName, true)) {
850 Alert::error(_("Backup failure"),
851 bformat(_("Cannot create backup file %1$s.\n"
852 "Please check whether the directory exists and is writeable."),
853 from_utf8(backupName.absFilename())));
854 //LYXERR(Debug::DEBUG, "Fs error: " << fe.what());
858 // ask if the disk file has been externally modified (use checksum method)
859 if (fileName().exists() && isExternallyModified(checksum_method)) {
860 docstring const file = makeDisplayPath(absFileName(), 20);
861 docstring text = bformat(_("Document %1$s has been externally modified. Are you sure "
862 "you want to overwrite this file?"), file);
863 int const ret = Alert::prompt(_("Overwrite modified file?"),
864 text, 1, 1, _("&Overwrite"), _("&Cancel"));
869 if (writeFile(d->filename)) {
871 removeAutosaveFile(absFileName());
872 saveCheckSum(d->filename);
875 // Saving failed, so backup is not backup
877 rename(backupName, d->filename);
883 bool Buffer::writeFile(FileName const & fname) const
885 if (d->read_only && fname == d->filename)
891 if (params().embedded)
892 // first write the .lyx file to the temporary directory
893 content = FileName(addName(temppath(), "content.lyx"));
897 if (params().compressed) {
898 gz::ogzstream ofs(content.toFilesystemEncoding().c_str(), ios::out|ios::trunc);
904 ofstream ofs(content.toFilesystemEncoding().c_str(), ios::out|ios::trunc);
911 if (retval && params().embedded) {
912 // write file.lyx and all the embedded files to the zip file fname
913 // if embedding is enabled
914 return d->embedded_files.writeFile(fname);
920 bool Buffer::write(ostream & ofs) const
923 // Use the standard "C" locale for file output.
924 ofs.imbue(std::locale::classic());
927 // The top of the file should not be written by params().
929 // write out a comment in the top of the file
930 ofs << "#LyX " << lyx_version
931 << " created this file. For more info see http://www.lyx.org/\n"
932 << "\\lyxformat " << LYX_FORMAT << "\n"
933 << "\\begin_document\n";
936 /// For each author, set 'used' to true if there is a change
937 /// by this author in the document; otherwise set it to 'false'.
938 AuthorList::Authors::const_iterator a_it = params().authors().begin();
939 AuthorList::Authors::const_iterator a_end = params().authors().end();
940 for (; a_it != a_end; ++a_it)
941 a_it->second.setUsed(false);
943 ParIterator const end = par_iterator_end();
944 ParIterator it = par_iterator_begin();
945 for ( ; it != end; ++it)
946 it->checkAuthors(params().authors());
948 // now write out the buffer parameters.
949 ofs << "\\begin_header\n";
950 params().writeFile(ofs);
951 ofs << "\\end_header\n";
954 ofs << "\n\\begin_body\n";
955 text().write(*this, ofs);
956 ofs << "\n\\end_body\n";
958 // Write marker that shows file is complete
959 ofs << "\\end_document" << endl;
961 // Shouldn't really be needed....
964 // how to check if close went ok?
965 // Following is an attempt... (BE 20001011)
967 // good() returns false if any error occured, including some
969 // bad() returns true if something bad happened in the buffer,
970 // which should include file system full errors.
975 lyxerr << "File was not closed properly." << endl;
982 bool Buffer::makeLaTeXFile(FileName const & fname,
983 string const & original_path,
984 OutputParams const & runparams,
985 bool output_preamble, bool output_body) const
987 string const encoding = runparams.encoding->iconvName();
988 LYXERR(Debug::LATEX, "makeLaTeXFile encoding: " << encoding << "...");
990 odocfstream ofs(encoding);
991 if (!openFileWrite(ofs, fname))
994 //TexStream ts(ofs.rdbuf(), &texrow());
996 bool failed_export = false;
999 writeLaTeXSource(ofs, original_path,
1000 runparams, output_preamble, output_body);
1002 catch (iconv_codecvt_facet_exception & e) {
1003 lyxerr << "Caught iconv exception: " << e.what() << endl;
1004 failed_export = true;
1006 catch (std::exception const & e) {
1007 lyxerr << "Caught \"normal\" exception: " << e.what() << endl;
1008 failed_export = true;
1011 lyxerr << "Caught some really weird exception..." << endl;
1012 LyX::cref().emergencyCleanup();
1018 failed_export = true;
1019 lyxerr << "File '" << fname << "' was not closed properly." << endl;
1022 if (failed_export) {
1023 Alert::error(_("Encoding error"),
1024 _("Some characters of your document are probably not "
1025 "representable in the chosen encoding.\n"
1026 "Changing the document encoding to utf8 could help."));
1033 void Buffer::writeLaTeXSource(odocstream & os,
1034 string const & original_path,
1035 OutputParams const & runparams_in,
1036 bool const output_preamble, bool const output_body) const
1038 OutputParams runparams = runparams_in;
1040 // validate the buffer.
1041 LYXERR(Debug::LATEX, " Validating buffer...");
1042 LaTeXFeatures features(*this, params(), runparams);
1044 LYXERR(Debug::LATEX, " Buffer validation done.");
1046 // The starting paragraph of the coming rows is the
1047 // first paragraph of the document. (Asger)
1048 if (output_preamble && runparams.nice) {
1049 os << "%% LyX " << lyx_version << " created this file. "
1050 "For more info, see http://www.lyx.org/.\n"
1051 "%% Do not edit unless you really know what "
1053 d->texrow.newline();
1054 d->texrow.newline();
1056 LYXERR(Debug::INFO, "lyx document header finished");
1057 // There are a few differences between nice LaTeX and usual files:
1058 // usual is \batchmode and has a
1059 // special input@path to allow the including of figures
1060 // with either \input or \includegraphics (what figinsets do).
1061 // input@path is set when the actual parameter
1062 // original_path is set. This is done for usual tex-file, but not
1063 // for nice-latex-file. (Matthias 250696)
1064 // Note that input@path is only needed for something the user does
1065 // in the preamble, included .tex files or ERT, files included by
1066 // LyX work without it.
1067 if (output_preamble) {
1068 if (!runparams.nice) {
1069 // code for usual, NOT nice-latex-file
1070 os << "\\batchmode\n"; // changed
1071 // from \nonstopmode
1072 d->texrow.newline();
1074 if (!original_path.empty()) {
1076 // We don't know the encoding of inputpath
1077 docstring const inputpath = from_utf8(latex_path(original_path));
1078 os << "\\makeatletter\n"
1079 << "\\def\\input@path{{"
1080 << inputpath << "/}}\n"
1081 << "\\makeatother\n";
1082 d->texrow.newline();
1083 d->texrow.newline();
1084 d->texrow.newline();
1087 // Write the preamble
1088 runparams.use_babel = params().writeLaTeX(os, features, d->texrow);
1094 os << "\\begin{document}\n";
1095 d->texrow.newline();
1096 } // output_preamble
1098 d->texrow.start(paragraphs().begin()->id(), 0);
1100 LYXERR(Debug::INFO, "preamble finished, now the body.");
1102 if (!lyxrc.language_auto_begin &&
1103 !params().language->babel().empty()) {
1105 os << from_utf8(subst(lyxrc.language_command_begin,
1107 params().language->babel()))
1109 d->texrow.newline();
1112 Encoding const & encoding = params().encoding();
1113 if (encoding.package() == Encoding::CJK) {
1114 // Open a CJK environment, since in contrast to the encodings
1115 // handled by inputenc the document encoding is not set in
1116 // the preamble if it is handled by CJK.sty.
1117 os << "\\begin{CJK}{" << from_ascii(encoding.latexName())
1119 d->texrow.newline();
1122 // if we are doing a real file with body, even if this is the
1123 // child of some other buffer, let's cut the link here.
1124 // This happens for example if only a child document is printed.
1125 Buffer const * save_parent = 0;
1126 if (output_preamble) {
1127 save_parent = d->parent_buffer;
1128 d->parent_buffer = 0;
1131 loadChildDocuments();
1134 latexParagraphs(*this, paragraphs(), os, d->texrow, runparams);
1136 // Restore the parenthood if needed
1137 if (output_preamble)
1138 d->parent_buffer = save_parent;
1140 // add this just in case after all the paragraphs
1142 d->texrow.newline();
1144 if (encoding.package() == Encoding::CJK) {
1145 // Close the open CJK environment.
1146 // latexParagraphs will have opened one even if the last text
1148 os << "\\end{CJK}\n";
1149 d->texrow.newline();
1152 if (!lyxrc.language_auto_end &&
1153 !params().language->babel().empty()) {
1154 os << from_utf8(subst(lyxrc.language_command_end,
1156 params().language->babel()))
1158 d->texrow.newline();
1161 if (output_preamble) {
1162 os << "\\end{document}\n";
1163 d->texrow.newline();
1164 LYXERR(Debug::LATEX, "makeLaTeXFile...done");
1166 LYXERR(Debug::LATEX, "LaTeXFile for inclusion made.");
1168 runparams_in.encoding = runparams.encoding;
1170 // Just to be sure. (Asger)
1171 d->texrow.newline();
1173 LYXERR(Debug::INFO, "Finished making LaTeX file.");
1174 LYXERR(Debug::INFO, "Row count was " << d->texrow.rows() - 1 << '.');
1178 bool Buffer::isLatex() const
1180 return params().getTextClass().outputType() == LATEX;
1184 bool Buffer::isLiterate() const
1186 return params().getTextClass().outputType() == LITERATE;
1190 bool Buffer::isDocBook() const
1192 return params().getTextClass().outputType() == DOCBOOK;
1196 void Buffer::makeDocBookFile(FileName const & fname,
1197 OutputParams const & runparams,
1198 bool const body_only) const
1200 LYXERR(Debug::LATEX, "makeDocBookFile...");
1204 if (!openFileWrite(ofs, fname))
1207 writeDocBookSource(ofs, fname.absFilename(), runparams, body_only);
1211 lyxerr << "File '" << fname << "' was not closed properly." << endl;
1215 void Buffer::writeDocBookSource(odocstream & os, string const & fname,
1216 OutputParams const & runparams,
1217 bool const only_body) const
1219 LaTeXFeatures features(*this, params(), runparams);
1224 TextClass const & tclass = params().getTextClass();
1225 string const top_element = tclass.latexname();
1228 if (runparams.flavor == OutputParams::XML)
1229 os << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
1232 os << "<!DOCTYPE " << from_ascii(top_element) << ' ';
1235 if (! tclass.class_header().empty())
1236 os << from_ascii(tclass.class_header());
1237 else if (runparams.flavor == OutputParams::XML)
1238 os << "PUBLIC \"-//OASIS//DTD DocBook XML//EN\" "
1239 << "\"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd\"";
1241 os << " PUBLIC \"-//OASIS//DTD DocBook V4.2//EN\"";
1243 docstring preamble = from_utf8(params().preamble);
1244 if (runparams.flavor != OutputParams::XML ) {
1245 preamble += "<!ENTITY % output.print.png \"IGNORE\">\n";
1246 preamble += "<!ENTITY % output.print.pdf \"IGNORE\">\n";
1247 preamble += "<!ENTITY % output.print.eps \"IGNORE\">\n";
1248 preamble += "<!ENTITY % output.print.bmp \"IGNORE\">\n";
1251 string const name = runparams.nice
1252 ? changeExtension(absFileName(), ".sgml") : fname;
1253 preamble += features.getIncludedFiles(name);
1254 preamble += features.getLyXSGMLEntities();
1256 if (!preamble.empty()) {
1257 os << "\n [ " << preamble << " ]";
1262 string top = top_element;
1264 if (runparams.flavor == OutputParams::XML)
1265 top += params().language->code();
1267 top += params().language->code().substr(0,2);
1270 if (!params().options.empty()) {
1272 top += params().options;
1275 os << "<!-- " << ((runparams.flavor == OutputParams::XML)? "XML" : "SGML")
1276 << " file was created by LyX " << lyx_version
1277 << "\n See http://www.lyx.org/ for more information -->\n";
1279 params().getTextClass().counters().reset();
1281 loadChildDocuments();
1283 sgml::openTag(os, top);
1285 docbookParagraphs(paragraphs(), *this, os, runparams);
1286 sgml::closeTag(os, top_element);
1290 // chktex should be run with these flags disabled: 3, 22, 25, 30, 38(?)
1291 // Other flags: -wall -v0 -x
1292 int Buffer::runChktex()
1296 // get LaTeX-Filename
1297 FileName const path(temppath());
1298 string const name = addName(path.absFilename(), latexName());
1299 string const org_path = filePath();
1301 support::PathChanger p(path); // path to LaTeX file
1302 message(_("Running chktex..."));
1304 // Generate the LaTeX file if neccessary
1305 OutputParams runparams(¶ms().encoding());
1306 runparams.flavor = OutputParams::LATEX;
1307 runparams.nice = false;
1308 makeLaTeXFile(FileName(name), org_path, runparams);
1311 Chktex chktex(lyxrc.chktex_command, onlyFilename(name), filePath());
1312 int const res = chktex.run(terr); // run chktex
1315 Alert::error(_("chktex failure"),
1316 _("Could not run chktex successfully."));
1317 } else if (res > 0) {
1318 ErrorList & errlist = d->errorLists["ChkTeX"];
1320 bufferErrors(terr, errlist);
1331 void Buffer::validate(LaTeXFeatures & features) const
1333 TextClass const & tclass = params().getTextClass();
1335 if (params().outputChanges) {
1336 bool dvipost = LaTeXFeatures::isAvailable("dvipost");
1337 bool xcolorsoul = LaTeXFeatures::isAvailable("soul") &&
1338 LaTeXFeatures::isAvailable("xcolor");
1340 if (features.runparams().flavor == OutputParams::LATEX) {
1342 features.require("ct-dvipost");
1343 features.require("dvipost");
1344 } else if (xcolorsoul) {
1345 features.require("ct-xcolor-soul");
1346 features.require("soul");
1347 features.require("xcolor");
1349 features.require("ct-none");
1351 } else if (features.runparams().flavor == OutputParams::PDFLATEX ) {
1353 features.require("ct-xcolor-soul");
1354 features.require("soul");
1355 features.require("xcolor");
1356 features.require("pdfcolmk"); // improves color handling in PDF output
1358 features.require("ct-none");
1363 // AMS Style is at document level
1364 if (params().use_amsmath == BufferParams::package_on
1365 || tclass.provides("amsmath"))
1366 features.require("amsmath");
1367 if (params().use_esint == BufferParams::package_on)
1368 features.require("esint");
1370 loadChildDocuments();
1372 for_each(paragraphs().begin(), paragraphs().end(),
1373 boost::bind(&Paragraph::validate, _1, boost::ref(features)));
1375 // the bullet shapes are buffer level not paragraph level
1376 // so they are tested here
1377 for (int i = 0; i < 4; ++i) {
1378 if (params().user_defined_bullet(i) != ITEMIZE_DEFAULTS[i]) {
1379 int const font = params().user_defined_bullet(i).getFont();
1381 int const c = params()
1382 .user_defined_bullet(i)
1389 features.require("latexsym");
1391 } else if (font == 1) {
1392 features.require("amssymb");
1393 } else if ((font >= 2 && font <= 5)) {
1394 features.require("pifont");
1399 if (lyxerr.debugging(Debug::LATEX)) {
1400 features.showStruct();
1405 void Buffer::getLabelList(vector<docstring> & list) const
1407 /// if this is a child document and the parent is already loaded
1408 /// Use the parent's list instead [ale990407]
1409 Buffer const * tmp = masterBuffer();
1411 lyxerr << "masterBuffer() failed!" << endl;
1415 tmp->getLabelList(list);
1419 loadChildDocuments();
1421 for (InsetIterator it = inset_iterator_begin(inset()); it; ++it)
1422 it.nextInset()->getLabelList(*this, list);
1426 void Buffer::updateBibfilesCache() const
1428 // if this is a child document and the parent is already loaded
1429 // update the parent's cache instead
1430 Buffer const * tmp = masterBuffer();
1433 tmp->updateBibfilesCache();
1437 bibfilesCache_.clear();
1438 for (InsetIterator it = inset_iterator_begin(inset()); it; ++it) {
1439 if (it->lyxCode() == BIBTEX_CODE) {
1440 InsetBibtex const & inset =
1441 static_cast<InsetBibtex const &>(*it);
1442 vector<FileName> const bibfiles = inset.getFiles(*this);
1443 bibfilesCache_.insert(bibfilesCache_.end(),
1446 } else if (it->lyxCode() == INCLUDE_CODE) {
1447 InsetInclude & inset =
1448 static_cast<InsetInclude &>(*it);
1449 inset.updateBibfilesCache(*this);
1450 vector<FileName> const & bibfiles =
1451 inset.getBibfilesCache(*this);
1452 bibfilesCache_.insert(bibfilesCache_.end(),
1460 vector<FileName> const & Buffer::getBibfilesCache() const
1462 // if this is a child document and the parent is already loaded
1463 // use the parent's cache instead
1464 Buffer const * tmp = masterBuffer();
1467 return tmp->getBibfilesCache();
1469 // We update the cache when first used instead of at loading time.
1470 if (bibfilesCache_.empty())
1471 const_cast<Buffer *>(this)->updateBibfilesCache();
1473 return bibfilesCache_;
1477 bool Buffer::isDepClean(string const & name) const
1479 DepClean::const_iterator const it = d->dep_clean.find(name);
1480 if (it == d->dep_clean.end())
1486 void Buffer::markDepClean(string const & name)
1488 d->dep_clean[name] = true;
1492 bool Buffer::dispatch(string const & command, bool * result)
1494 return dispatch(lyxaction.lookupFunc(command), result);
1498 bool Buffer::dispatch(FuncRequest const & func, bool * result)
1500 bool dispatched = true;
1502 switch (func.action) {
1503 case LFUN_BUFFER_EXPORT: {
1504 bool const tmp = doExport(to_utf8(func.argument()), false);
1517 void Buffer::changeLanguage(Language const * from, Language const * to)
1522 for_each(par_iterator_begin(),
1524 bind(&Paragraph::changeLanguage, _1, params(), from, to));
1528 bool Buffer::isMultiLingual() const
1530 ParConstIterator end = par_iterator_end();
1531 for (ParConstIterator it = par_iterator_begin(); it != end; ++it)
1532 if (it->isMultiLingual(params()))
1539 ParIterator Buffer::getParFromID(int const id) const
1541 ParConstIterator it = par_iterator_begin();
1542 ParConstIterator const end = par_iterator_end();
1545 // John says this is called with id == -1 from undo
1546 lyxerr << "getParFromID(), id: " << id << endl;
1550 for (; it != end; ++it)
1558 bool Buffer::hasParWithID(int const id) const
1560 ParConstIterator const it = getParFromID(id);
1561 return it != par_iterator_end();
1565 ParIterator Buffer::par_iterator_begin()
1567 return lyx::par_iterator_begin(inset());
1571 ParIterator Buffer::par_iterator_end()
1573 return lyx::par_iterator_end(inset());
1577 ParConstIterator Buffer::par_iterator_begin() const
1579 return lyx::par_const_iterator_begin(inset());
1583 ParConstIterator Buffer::par_iterator_end() const
1585 return lyx::par_const_iterator_end(inset());
1589 Language const * Buffer::language() const
1591 return params().language;
1595 docstring const Buffer::B_(string const & l10n) const
1597 return params().B_(l10n);
1601 bool Buffer::isClean() const
1603 return d->lyx_clean;
1607 bool Buffer::isBakClean() const
1609 return d->bak_clean;
1613 bool Buffer::isExternallyModified(CheckMethod method) const
1615 BOOST_ASSERT(d->filename.exists());
1616 // if method == timestamp, check timestamp before checksum
1617 return (method == checksum_method
1618 || d->timestamp_ != d->filename.lastModified())
1619 && d->checksum_ != d->filename.checksum();
1623 void Buffer::saveCheckSum(FileName const & file) const
1625 if (file.exists()) {
1626 d->timestamp_ = file.lastModified();
1627 d->checksum_ = file.checksum();
1629 // in the case of save to a new file.
1636 void Buffer::markClean() const
1638 if (!d->lyx_clean) {
1639 d->lyx_clean = true;
1642 // if the .lyx file has been saved, we don't need an
1644 d->bak_clean = true;
1648 void Buffer::markBakClean() const
1650 d->bak_clean = true;
1654 void Buffer::setUnnamed(bool flag)
1660 bool Buffer::isUnnamed() const
1666 // FIXME: this function should be moved to buffer_pimpl.C
1667 void Buffer::markDirty()
1670 d->lyx_clean = false;
1673 d->bak_clean = false;
1675 DepClean::iterator it = d->dep_clean.begin();
1676 DepClean::const_iterator const end = d->dep_clean.end();
1678 for (; it != end; ++it)
1683 FileName Buffer::fileName() const
1689 string Buffer::absFileName() const
1691 return d->filename.absFilename();
1695 string Buffer::filePath() const
1697 return d->filename.onlyPath().absFilename();
1701 bool Buffer::isReadonly() const
1703 return d->read_only;
1707 void Buffer::setParent(Buffer const * buffer)
1709 // Avoids recursive include.
1710 d->parent_buffer = buffer == this ? 0 : buffer;
1714 Buffer const * Buffer::parent()
1716 return d->parent_buffer;
1720 Buffer const * Buffer::masterBuffer() const
1722 if (!d->parent_buffer)
1725 return d->parent_buffer->masterBuffer();
1729 bool Buffer::hasMacro(docstring const & name, Paragraph const & par) const
1731 Impl::PositionToMacroMap::iterator it;
1732 it = d->macros[name].upper_bound(par.macrocontextPosition());
1733 if (it != d->macros[name].end())
1736 // If there is a master buffer, query that
1737 Buffer const * master = masterBuffer();
1738 if (master && master != this)
1739 return master->hasMacro(name);
1741 return MacroTable::globalMacros().has(name);
1745 bool Buffer::hasMacro(docstring const & name) const
1747 if( !d->macros[name].empty() )
1750 // If there is a master buffer, query that
1751 Buffer const * master = masterBuffer();
1752 if (master && master != this)
1753 return master->hasMacro(name);
1755 return MacroTable::globalMacros().has(name);
1759 MacroData const & Buffer::getMacro(docstring const & name,
1760 Paragraph const & par) const
1762 Impl::PositionToMacroMap::iterator it;
1763 it = d->macros[name].upper_bound(par.macrocontextPosition());
1764 if( it != d->macros[name].end() )
1767 // If there is a master buffer, query that
1768 Buffer const * master = masterBuffer();
1769 if (master && master != this)
1770 return master->getMacro(name);
1772 return MacroTable::globalMacros().get(name);
1776 MacroData const & Buffer::getMacro(docstring const & name) const
1778 Impl::PositionToMacroMap::iterator it;
1779 it = d->macros[name].begin();
1780 if( it != d->macros[name].end() )
1783 // If there is a master buffer, query that
1784 Buffer const * master = masterBuffer();
1785 if (master && master != this)
1786 return master->getMacro(name);
1788 return MacroTable::globalMacros().get(name);
1792 void Buffer::updateMacros()
1794 // start with empty table
1795 d->macros = Impl::NameToPositionMacroMap();
1797 // Iterate over buffer
1798 ParagraphList & pars = text().paragraphs();
1799 for (size_t i = 0, n = pars.size(); i != n; ++i) {
1800 // set position again
1801 pars[i].setMacrocontextPosition(i);
1803 //lyxerr << "searching main par " << i
1804 // << " for macro definitions" << std::endl;
1805 InsetList const & insets = pars[i].insetList();
1806 InsetList::const_iterator it = insets.begin();
1807 InsetList::const_iterator end = insets.end();
1808 for ( ; it != end; ++it) {
1809 if (it->inset->lyxCode() != MATHMACRO_CODE)
1813 MathMacroTemplate const & macroTemplate
1814 = static_cast<MathMacroTemplate const &>(*it->inset);
1817 if (macroTemplate.validMacro()) {
1818 MacroData macro = macroTemplate.asMacroData();
1821 // call hasMacro here instead of directly querying mc to
1822 // also take the master document into consideration
1823 macro.setRedefinition(hasMacro(macroTemplate.name()));
1825 // register macro (possibly overwrite the previous one of this paragraph)
1826 d->macros[macroTemplate.name()][i] = macro;
1833 void Buffer::changeRefsIfUnique(docstring const & from, docstring const & to,
1836 //FIXME: This does not work for child documents yet.
1837 BOOST_ASSERT(code == CITE_CODE || code == REF_CODE);
1838 // Check if the label 'from' appears more than once
1839 vector<docstring> labels;
1842 if (code == CITE_CODE) {
1844 keys.fillWithBibKeys(this);
1845 BiblioInfo::const_iterator bit = keys.begin();
1846 BiblioInfo::const_iterator bend = keys.end();
1848 for (; bit != bend; ++bit)
1850 labels.push_back(bit->first);
1853 getLabelList(labels);
1854 paramName = "reference";
1857 if (std::count(labels.begin(), labels.end(), from) > 1)
1860 for (InsetIterator it = inset_iterator_begin(inset()); it; ++it) {
1861 if (it->lyxCode() == code) {
1862 InsetCommand & inset = static_cast<InsetCommand &>(*it);
1863 docstring const oldValue = inset.getParam(paramName);
1864 if (oldValue == from)
1865 inset.setParam(paramName, to);
1871 void Buffer::getSourceCode(odocstream & os, pit_type par_begin,
1872 pit_type par_end, bool full_source)
1874 OutputParams runparams(¶ms().encoding());
1875 runparams.nice = true;
1876 runparams.flavor = OutputParams::LATEX;
1877 runparams.linelen = lyxrc.plaintext_linelen;
1878 // No side effect of file copying and image conversion
1879 runparams.dryrun = true;
1883 os << "% " << _("Preview source code") << "\n\n";
1884 d->texrow.newline();
1885 d->texrow.newline();
1887 writeLaTeXSource(os, filePath(), runparams, true, true);
1889 writeDocBookSource(os, absFileName(), runparams, false);
1892 runparams.par_begin = par_begin;
1893 runparams.par_end = par_end;
1894 if (par_begin + 1 == par_end)
1896 << bformat(_("Preview source code for paragraph %1$d"), par_begin)
1900 << bformat(_("Preview source code from paragraph %1$s to %2$s"),
1901 convert<docstring>(par_begin),
1902 convert<docstring>(par_end - 1))
1904 d->texrow.newline();
1905 d->texrow.newline();
1906 // output paragraphs
1908 latexParagraphs(*this, paragraphs(), os, d->texrow, runparams);
1911 docbookParagraphs(paragraphs(), *this, os, runparams);
1917 ErrorList & Buffer::errorList(string const & type) const
1919 static ErrorList emptyErrorList;
1920 std::map<string, ErrorList>::iterator I = d->errorLists.find(type);
1921 if (I == d->errorLists.end())
1922 return emptyErrorList;
1928 void Buffer::structureChanged() const
1931 gui_->structureChanged();
1935 void Buffer::errors(std::string const & err) const
1942 void Buffer::message(docstring const & msg) const
1949 void Buffer::setBusy(bool on) const
1956 void Buffer::setReadOnly(bool on) const
1959 d->wa_->setReadOnly(on);
1963 void Buffer::updateTitles() const
1966 d->wa_->updateTitles();
1970 void Buffer::resetAutosaveTimers() const
1973 gui_->resetAutosaveTimers();
1977 void Buffer::setGuiDelegate(frontend::GuiBufferDelegate * gui)
1986 class AutoSaveBuffer : public support::ForkedProcess {
1989 AutoSaveBuffer(Buffer const & buffer, FileName const & fname)
1990 : buffer_(buffer), fname_(fname) {}
1992 virtual boost::shared_ptr<ForkedProcess> clone() const
1994 return boost::shared_ptr<ForkedProcess>(new AutoSaveBuffer(*this));
1999 command_ = to_utf8(bformat(_("Auto-saving %1$s"),
2000 from_utf8(fname_.absFilename())));
2001 return run(DontWait);
2005 virtual int generateChild();
2007 Buffer const & buffer_;
2012 #if !defined (HAVE_FORK)
2016 int AutoSaveBuffer::generateChild()
2018 // tmp_ret will be located (usually) in /tmp
2019 // will that be a problem?
2020 pid_t const pid = fork();
2021 // If you want to debug the autosave
2022 // you should set pid to -1, and comment out the fork.
2023 if (pid == 0 || pid == -1) {
2024 // pid = -1 signifies that lyx was unable
2025 // to fork. But we will do the save
2027 bool failed = false;
2029 FileName const tmp_ret(tempName(FileName(), "lyxauto"));
2030 if (!tmp_ret.empty()) {
2031 buffer_.writeFile(tmp_ret);
2032 // assume successful write of tmp_ret
2033 if (!rename(tmp_ret, fname_)) {
2035 // most likely couldn't move between
2036 // filesystems unless write of tmp_ret
2037 // failed so remove tmp file (if it
2039 tmp_ret.removeFile();
2046 // failed to write/rename tmp_ret so try writing direct
2047 if (!buffer_.writeFile(fname_)) {
2048 // It is dangerous to do this in the child,
2049 // but safe in the parent, so...
2050 if (pid == -1) // emit message signal.
2051 buffer_.message(_("Autosave failed!"));
2054 if (pid == 0) { // we are the child so...
2064 // Perfect target for a thread...
2065 void Buffer::autoSave() const
2067 if (isBakClean() || isReadonly()) {
2068 // We don't save now, but we'll try again later
2069 resetAutosaveTimers();
2073 // emit message signal.
2074 message(_("Autosaving current document..."));
2076 // create autosave filename
2077 string fname = filePath();
2079 fname += onlyFilename(absFileName());
2082 AutoSaveBuffer autosave(*this, FileName(fname));
2086 resetAutosaveTimers();
2090 /** Write a buffer to a new file name and rename the buffer
2091 according to the new file name.
2093 This function is e.g. used by menu callbacks and
2094 LFUN_BUFFER_WRITE_AS.
2096 If 'newname' is empty (the default), the user is asked via a
2097 dialog for the buffer's new name and location.
2099 If 'newname' is non-empty and has an absolute path, that is used.
2100 Otherwise the base directory of the buffer is used as the base
2101 for any relative path in 'newname'.
2104 bool Buffer::writeAs(string const & newname)
2106 string fname = absFileName();
2107 string const oldname = fname;
2109 if (newname.empty()) { /// No argument? Ask user through dialog
2112 FileDialog dlg(_("Choose a filename to save document as"),
2113 LFUN_BUFFER_WRITE_AS);
2114 dlg.setButton1(_("Documents|#o#O"), from_utf8(lyxrc.document_path));
2115 dlg.setButton2(_("Templates|#T#t"), from_utf8(lyxrc.template_path));
2117 if (!support::isLyXFilename(fname))
2120 support::FileFilterList const filter(_("LyX Documents (*.lyx)"));
2122 FileDialog::Result result =
2123 dlg.save(from_utf8(onlyPath(fname)),
2125 from_utf8(onlyFilename(fname)));
2127 if (result.first == FileDialog::Later)
2130 fname = to_utf8(result.second);
2135 // Make sure the absolute filename ends with appropriate suffix
2136 fname = makeAbsPath(fname).absFilename();
2137 if (!support::isLyXFilename(fname))
2141 fname = makeAbsPath(newname, onlyPath(oldname)).absFilename();
2143 if (FileName(fname).exists()) {
2144 docstring const file = makeDisplayPath(fname, 30);
2145 docstring text = bformat(_("The document %1$s already "
2146 "exists.\n\nDo you want to "
2147 "overwrite that document?"),
2149 int const ret = Alert::prompt(_("Overwrite document?"),
2150 text, 0, 1, _("&Overwrite"), _("&Cancel"));
2156 // Ok, change the name of the buffer
2159 bool unnamed = isUnnamed();
2161 saveCheckSum(FileName(fname));
2164 setFileName(oldname);
2165 setUnnamed(unnamed);
2166 saveCheckSum(FileName(oldname));
2170 removeAutosaveFile(oldname);
2175 bool Buffer::menuWrite()
2178 LyX::ref().session().lastFiles().add(FileName(absFileName()));
2182 // FIXME: we don't tell the user *WHY* the save failed !!
2184 docstring const file = makeDisplayPath(absFileName(), 30);
2186 docstring text = bformat(_("The document %1$s could not be saved.\n\n"
2187 "Do you want to rename the document and "
2188 "try again?"), file);
2189 int const ret = Alert::prompt(_("Rename and save?"),
2190 text, 0, 1, _("&Rename"), _("&Cancel"));
2199 void Buffer::resetChildDocuments(bool close_them) const
2201 for (InsetIterator it = inset_iterator_begin(inset()); it; ++it) {
2202 if (it->lyxCode() != INCLUDE_CODE)
2204 InsetCommand const & inset = static_cast<InsetCommand const &>(*it);
2205 InsetCommandParams const & ip = inset.params();
2207 resetParentBuffer(this, ip, close_them);
2210 if (use_gui && masterBuffer() == this)
2211 updateLabels(*this);
2215 void Buffer::loadChildDocuments() const
2217 bool parse_error = false;
2219 for (InsetIterator it = inset_iterator_begin(inset()); it; ++it) {
2220 if (it->lyxCode() != INCLUDE_CODE)
2222 InsetCommand const & inset = static_cast<InsetCommand const &>(*it);
2223 InsetCommandParams const & ip = inset.params();
2224 Buffer * child = loadIfNeeded(*this, ip);
2227 parse_error |= !child->errorList("Parse").empty();
2228 child->loadChildDocuments();
2231 if (use_gui && masterBuffer() == this)
2232 updateLabels(*this);
2236 string Buffer::bufferFormat() const
2246 bool Buffer::doExport(string const & format, bool put_in_tempdir,
2247 string & result_file) const
2249 string backend_format;
2250 OutputParams runparams(¶ms().encoding());
2251 runparams.flavor = OutputParams::LATEX;
2252 runparams.linelen = lyxrc.plaintext_linelen;
2253 vector<string> backs = backends();
2254 if (find(backs.begin(), backs.end(), format) == backs.end()) {
2255 // Get shortest path to format
2256 Graph::EdgePath path;
2257 for (vector<string>::const_iterator it = backs.begin();
2258 it != backs.end(); ++it) {
2259 Graph::EdgePath p = theConverters().getPath(*it, format);
2260 if (!p.empty() && (path.empty() || p.size() < path.size())) {
2261 backend_format = *it;
2266 runparams.flavor = theConverters().getFlavor(path);
2268 Alert::error(_("Couldn't export file"),
2269 bformat(_("No information for exporting the format %1$s."),
2270 formats.prettyName(format)));
2274 backend_format = format;
2275 // FIXME: Don't hardcode format names here, but use a flag
2276 if (backend_format == "pdflatex")
2277 runparams.flavor = OutputParams::PDFLATEX;
2280 string filename = latexName(false);
2281 filename = addName(temppath(), filename);
2282 filename = changeExtension(filename,
2283 formats.extension(backend_format));
2285 // Plain text backend
2286 if (backend_format == "text")
2287 writePlaintextFile(*this, FileName(filename), runparams);
2289 else if (backend_format == "lyx")
2290 writeFile(FileName(filename));
2292 else if (isDocBook()) {
2293 runparams.nice = !put_in_tempdir;
2294 makeDocBookFile(FileName(filename), runparams);
2297 else if (backend_format == format) {
2298 runparams.nice = true;
2299 if (!makeLaTeXFile(FileName(filename), string(), runparams))
2301 } else if (!lyxrc.tex_allows_spaces
2302 && support::contains(filePath(), ' ')) {
2303 Alert::error(_("File name error"),
2304 _("The directory path to the document cannot contain spaces."));
2307 runparams.nice = false;
2308 if (!makeLaTeXFile(FileName(filename), filePath(), runparams))
2312 string const error_type = (format == "program")
2313 ? "Build" : bufferFormat();
2314 string const ext = formats.extension(format);
2315 FileName const tmp_result_file(changeExtension(filename, ext));
2316 bool const success = theConverters().convert(this, FileName(filename),
2317 tmp_result_file, FileName(absFileName()), backend_format, format,
2318 errorList(error_type));
2319 // Emit the signal to show the error list.
2320 if (format != backend_format)
2326 result_file = tmp_result_file.absFilename();
2328 result_file = changeExtension(absFileName(), ext);
2329 // We need to copy referenced files (e. g. included graphics
2330 // if format == "dvi") to the result dir.
2331 vector<ExportedFile> const files =
2332 runparams.exportdata->externalFiles(format);
2333 string const dest = onlyPath(result_file);
2334 CopyStatus status = SUCCESS;
2335 for (vector<ExportedFile>::const_iterator it = files.begin();
2336 it != files.end() && status != CANCEL; ++it) {
2338 formats.getFormatFromFile(it->sourceName);
2339 status = copyFile(fmt, it->sourceName,
2340 makeAbsPath(it->exportName, dest),
2341 it->exportName, status == FORCE);
2343 if (status == CANCEL) {
2344 message(_("Document export cancelled."));
2345 } else if (tmp_result_file.exists()) {
2346 // Finally copy the main file
2347 status = copyFile(format, tmp_result_file,
2348 FileName(result_file), result_file,
2350 message(bformat(_("Document exported as %1$s "
2352 formats.prettyName(format),
2353 makeDisplayPath(result_file)));
2355 // This must be a dummy converter like fax (bug 1888)
2356 message(bformat(_("Document exported as %1$s"),
2357 formats.prettyName(format)));
2365 bool Buffer::doExport(string const & format, bool put_in_tempdir) const
2368 return doExport(format, put_in_tempdir, result_file);
2372 bool Buffer::preview(string const & format) const
2375 if (!doExport(format, true, result_file))
2377 return formats.view(*this, FileName(result_file), format);
2381 bool Buffer::isExportable(string const & format) const
2383 vector<string> backs = backends();
2384 for (vector<string>::const_iterator it = backs.begin();
2385 it != backs.end(); ++it)
2386 if (theConverters().isReachable(*it, format))
2392 vector<Format const *> Buffer::exportableFormats(bool only_viewable) const
2394 vector<string> backs = backends();
2395 vector<Format const *> result =
2396 theConverters().getReachable(backs[0], only_viewable, true);
2397 for (vector<string>::const_iterator it = backs.begin() + 1;
2398 it != backs.end(); ++it) {
2399 vector<Format const *> r =
2400 theConverters().getReachable(*it, only_viewable, false);
2401 result.insert(result.end(), r.begin(), r.end());
2407 vector<string> Buffer::backends() const
2410 if (params().getTextClass().isTeXClassAvailable()) {
2411 v.push_back(bufferFormat());
2412 // FIXME: Don't hardcode format names here, but use a flag
2413 if (v.back() == "latex")
2414 v.push_back("pdflatex");
2416 v.push_back("text");
2422 bool Buffer::readFileHelper(FileName const & s)
2424 // File information about normal file
2426 docstring const file = makeDisplayPath(s.absFilename(), 50);
2427 docstring text = bformat(_("The specified document\n%1$s"
2428 "\ncould not be read."), file);
2429 Alert::error(_("Could not read document"), text);
2433 // Check if emergency save file exists and is newer.
2434 FileName const e(s.absFilename() + ".emergency");
2436 if (e.exists() && s.exists() && e.lastModified() > s.lastModified()) {
2437 docstring const file = makeDisplayPath(s.absFilename(), 20);
2438 docstring const text =
2439 bformat(_("An emergency save of the document "
2441 "Recover emergency save?"), file);
2442 switch (Alert::prompt(_("Load emergency save?"), text, 0, 2,
2443 _("&Recover"), _("&Load Original"),
2447 // the file is not saved if we load the emergency file.
2457 // Now check if autosave file is newer.
2458 FileName const a(onlyPath(s.absFilename()) + '#' + onlyFilename(s.absFilename()) + '#');
2460 if (a.exists() && s.exists() && a.lastModified() > s.lastModified()) {
2461 docstring const file = makeDisplayPath(s.absFilename(), 20);
2462 docstring const text =
2463 bformat(_("The backup of the document "
2464 "%1$s is newer.\n\nLoad the "
2465 "backup instead?"), file);
2466 switch (Alert::prompt(_("Load backup?"), text, 0, 2,
2467 _("&Load backup"), _("Load &original"),
2471 // the file is not saved if we load the autosave file.
2475 // Here we delete the autosave
2486 bool Buffer::loadLyXFile(FileName const & s)
2488 if (s.isReadableFile()) {
2489 if (readFileHelper(s)) {
2490 lyxvc().file_found_hook(s);
2491 if (!s.isWritable())
2496 docstring const file = makeDisplayPath(s.absFilename(), 20);
2497 // Here we probably should run
2498 if (LyXVC::file_not_found_hook(s)) {
2499 docstring const text =
2500 bformat(_("Do you want to retrieve the document"
2501 " %1$s from version control?"), file);
2502 int const ret = Alert::prompt(_("Retrieve from version control?"),
2503 text, 0, 1, _("&Retrieve"), _("&Cancel"));
2506 // How can we know _how_ to do the checkout?
2507 // With the current VC support it has to be,
2508 // a RCS file since CVS do not have special ,v files.
2510 return loadLyXFile(s);
2518 void Buffer::bufferErrors(TeXErrors const & terr, ErrorList & errorList) const
2520 TeXErrors::Errors::const_iterator cit = terr.begin();
2521 TeXErrors::Errors::const_iterator end = terr.end();
2523 for (; cit != end; ++cit) {
2526 int errorRow = cit->error_in_line;
2527 bool found = d->texrow.getIdFromRow(errorRow, id_start,
2533 found = d->texrow.getIdFromRow(errorRow, id_end, pos_end);
2534 } while (found && id_start == id_end && pos_start == pos_end);
2536 errorList.push_back(ErrorItem(cit->error_desc,
2537 cit->error_text, id_start, pos_start, pos_end));