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);
289 void Buffer::changed() const
296 frontend::WorkAreaManager & Buffer::workAreaManager() const
298 BOOST_ASSERT(d->wa_);
303 Text & Buffer::text() const
305 return const_cast<Text &>(d->inset.text_);
309 Inset & Buffer::inset() const
311 return const_cast<InsetText &>(d->inset);
315 BufferParams & Buffer::params()
321 BufferParams const & Buffer::params() const
327 ParagraphList & Buffer::paragraphs()
329 return text().paragraphs();
333 ParagraphList const & Buffer::paragraphs() const
335 return text().paragraphs();
339 LyXVC & Buffer::lyxvc()
345 LyXVC const & Buffer::lyxvc() const
351 string const & Buffer::temppath() const
357 TexRow const & Buffer::texrow() const
363 TocBackend & Buffer::tocBackend() const
365 return d->toc_backend;
369 EmbeddedFiles & Buffer::embeddedFiles()
371 return d->embedded_files;
375 EmbeddedFiles const & Buffer::embeddedFiles() const
377 return d->embedded_files;
381 Undo & Buffer::undo()
387 string Buffer::latexName(bool const no_path) const
389 FileName latex_name = makeLatexName(d->filename);
390 return no_path ? latex_name.onlyFileName()
391 : latex_name.absFilename();
395 string Buffer::logName(LogType * type) const
397 string const filename = latexName(false);
399 if (filename.empty()) {
405 string const path = temppath();
407 FileName const fname(addName(temppath(),
408 onlyFilename(changeExtension(filename,
410 FileName const bname(
411 addName(path, onlyFilename(
412 changeExtension(filename,
413 formats.extension("literate") + ".out"))));
415 // If no Latex log or Build log is newer, show Build log
417 if (bname.exists() &&
418 (!fname.exists() || fname.lastModified() < bname.lastModified())) {
419 LYXERR(Debug::FILES, "Log name calculated as: " << bname);
422 return bname.absFilename();
424 LYXERR(Debug::FILES, "Log name calculated as: " << fname);
427 return fname.absFilename();
431 void Buffer::setReadonly(bool const flag)
433 if (d->read_only != flag) {
440 void Buffer::setFileName(string const & newfile)
442 d->filename = makeAbsPath(newfile);
443 setReadonly(d->filename.isReadOnly());
448 int Buffer::readHeader(Lexer & lex)
450 int unknown_tokens = 0;
452 int begin_header_line = -1;
454 // Initialize parameters that may be/go lacking in header:
455 params().branchlist().clear();
456 params().preamble.erase();
457 params().options.erase();
458 params().float_placement.erase();
459 params().paperwidth.erase();
460 params().paperheight.erase();
461 params().leftmargin.erase();
462 params().rightmargin.erase();
463 params().topmargin.erase();
464 params().bottommargin.erase();
465 params().headheight.erase();
466 params().headsep.erase();
467 params().footskip.erase();
468 params().listings_params.clear();
469 params().clearLayoutModules();
470 params().pdfoptions().clear();
472 for (int i = 0; i < 4; ++i) {
473 params().user_defined_bullet(i) = ITEMIZE_DEFAULTS[i];
474 params().temp_bullet(i) = ITEMIZE_DEFAULTS[i];
477 ErrorList & errorList = d->errorLists["Parse"];
481 string const token = lex.getString();
486 if (token == "\\end_header")
490 if (token == "\\begin_header") {
491 begin_header_line = line;
495 LYXERR(Debug::PARSER, "Handling document header token: `"
498 string unknown = params().readToken(lex, token, d->filename.onlyPath());
499 if (!unknown.empty()) {
500 if (unknown[0] != '\\' && token == "\\textclass") {
501 Alert::warning(_("Unknown document class"),
502 bformat(_("Using the default document class, because the "
503 "class %1$s is unknown."), from_utf8(unknown)));
506 docstring const s = bformat(_("Unknown token: "
510 errorList.push_back(ErrorItem(_("Document header error"),
515 if (begin_header_line) {
516 docstring const s = _("\\begin_header is missing");
517 errorList.push_back(ErrorItem(_("Document header error"),
521 return unknown_tokens;
526 // changed to be public and have one parameter
527 // Returns false if "\end_document" is not read (Asger)
528 bool Buffer::readDocument(Lexer & lex)
530 ErrorList & errorList = d->errorLists["Parse"];
534 string const token = lex.getString();
535 if (token != "\\begin_document") {
536 docstring const s = _("\\begin_document is missing");
537 errorList.push_back(ErrorItem(_("Document header error"),
541 // we are reading in a brand new document
542 BOOST_ASSERT(paragraphs().empty());
545 TextClass const & baseClass = textclasslist[params().getBaseClass()];
546 if (!baseClass.load(filePath())) {
547 string theclass = baseClass.name();
548 Alert::error(_("Can't load document class"), bformat(
549 _("Using the default document class, because the "
550 "class %1$s could not be loaded."), from_utf8(theclass)));
551 params().setBaseClass(defaultTextclass());
554 if (params().outputChanges) {
555 bool dvipost = LaTeXFeatures::isAvailable("dvipost");
556 bool xcolorsoul = LaTeXFeatures::isAvailable("soul") &&
557 LaTeXFeatures::isAvailable("xcolor");
559 if (!dvipost && !xcolorsoul) {
560 Alert::warning(_("Changes not shown in LaTeX output"),
561 _("Changes will not be highlighted in LaTeX output, "
562 "because neither dvipost nor xcolor/soul are installed.\n"
563 "Please install these packages or redefine "
564 "\\lyxadded and \\lyxdeleted in the LaTeX preamble."));
565 } else if (!xcolorsoul) {
566 Alert::warning(_("Changes not shown in LaTeX output"),
567 _("Changes will not be highlighted in LaTeX output "
568 "when using pdflatex, because xcolor and soul are not installed.\n"
569 "Please install both packages or redefine "
570 "\\lyxadded and \\lyxdeleted in the LaTeX preamble."));
575 bool const res = text().read(*this, lex, errorList);
576 for_each(text().paragraphs().begin(),
577 text().paragraphs().end(),
578 bind(&Paragraph::setInsetOwner, _1, &inset()));
584 // needed to insert the selection
585 void Buffer::insertStringAsLines(ParagraphList & pars,
586 pit_type & pit, pos_type & pos,
587 Font const & fn, docstring const & str, bool autobreakrows)
591 // insert the string, don't insert doublespace
592 bool space_inserted = true;
593 for (docstring::const_iterator cit = str.begin();
594 cit != str.end(); ++cit) {
595 Paragraph & par = pars[pit];
597 if (autobreakrows && (!par.empty() || par.allowEmpty())) {
598 breakParagraph(params(), pars, pit, pos,
599 par.layout()->isEnvironment());
602 space_inserted = true;
606 // do not insert consecutive spaces if !free_spacing
607 } else if ((*cit == ' ' || *cit == '\t') &&
608 space_inserted && !par.isFreeSpacing()) {
610 } else if (*cit == '\t') {
611 if (!par.isFreeSpacing()) {
612 // tabs are like spaces here
613 par.insertChar(pos, ' ', font, params().trackChanges);
615 space_inserted = true;
617 const pos_type n = 8 - pos % 8;
618 for (pos_type i = 0; i < n; ++i) {
619 par.insertChar(pos, ' ', font, params().trackChanges);
622 space_inserted = true;
624 } else if (!isPrintable(*cit)) {
625 // Ignore unprintables
628 // just insert the character
629 par.insertChar(pos, *cit, font, params().trackChanges);
631 space_inserted = (*cit == ' ');
638 bool Buffer::readString(std::string const & s)
640 params().compressed = false;
642 // remove dummy empty par
643 paragraphs().clear();
645 std::istringstream is(s);
647 FileName const name(tempName());
648 switch (readFile(lex, name, true)) {
652 // We need to call lyx2lyx, so write the input to a file
653 std::ofstream os(name.toFilesystemEncoding().c_str());
656 return readFile(name);
666 bool Buffer::readFile(FileName const & filename)
668 FileName fname(filename);
669 // Check if the file is compressed.
670 string format = filename.guessFormatFromContents();
671 if (format == "zip") {
672 // decompress to a temp directory
673 LYXERR(Debug::FILES, filename << " is in zip format. Unzip to " << temppath());
674 ::unzipToDir(filename.toFilesystemEncoding(), temppath());
676 FileName lyxfile(addName(temppath(), "content.lyx"));
677 // if both manifest.txt and file.lyx exist, this is am embedded file
678 if (lyxfile.exists()) {
679 params().embedded = true;
683 // The embedded lyx file can also be compressed, for backward compatibility
684 format = fname.guessFormatFromContents();
685 if (format == "gzip" || format == "zip" || format == "compress")
686 params().compressed = true;
688 // remove dummy empty par
689 paragraphs().clear();
692 if (readFile(lex, fname) != success)
699 bool Buffer::isFullyLoaded() const
701 return d->file_fully_loaded;
705 void Buffer::setFullyLoaded(bool value)
707 d->file_fully_loaded = value;
711 Buffer::ReadStatus Buffer::readFile(Lexer & lex, FileName const & filename,
714 BOOST_ASSERT(!filename.empty());
717 Alert::error(_("Document could not be read"),
718 bformat(_("%1$s could not be read."), from_utf8(filename.absFilename())));
723 string const token = lex.getString();
726 Alert::error(_("Document could not be read"),
727 bformat(_("%1$s could not be read."), from_utf8(filename.absFilename())));
731 // the first token _must_ be...
732 if (token != "\\lyxformat") {
733 lyxerr << "Token: " << token << endl;
735 Alert::error(_("Document format failure"),
736 bformat(_("%1$s is not a LyX document."),
737 from_utf8(filename.absFilename())));
742 string tmp_format = lex.getString();
743 //lyxerr << "LyX Format: `" << tmp_format << '\'' << endl;
744 // if present remove ".," from string.
745 string::size_type dot = tmp_format.find_first_of(".,");
746 //lyxerr << " dot found at " << dot << endl;
747 if (dot != string::npos)
748 tmp_format.erase(dot, 1);
749 int const file_format = convert<int>(tmp_format);
750 //lyxerr << "format: " << file_format << endl;
752 // save timestamp and checksum of the original disk file, making sure
753 // to not overwrite them with those of the file created in the tempdir
754 // when it has to be converted to the current format.
756 // Save the timestamp and checksum of disk file. If filename is an
757 // emergency file, save the timestamp and checksum of the original lyx file
758 // because isExternallyModified will check for this file. (BUG4193)
759 string diskfile = filename.absFilename();
760 if (suffixIs(diskfile, ".emergency"))
761 diskfile = diskfile.substr(0, diskfile.size() - 10);
762 saveCheckSum(FileName(diskfile));
765 if (file_format != LYX_FORMAT) {
768 // lyx2lyx would fail
771 FileName const tmpfile(tempName());
772 if (tmpfile.empty()) {
773 Alert::error(_("Conversion failed"),
774 bformat(_("%1$s is from a different"
775 " version of LyX, but a temporary"
776 " file for converting it could"
778 from_utf8(filename.absFilename())));
781 FileName const lyx2lyx = libFileSearch("lyx2lyx", "lyx2lyx");
782 if (lyx2lyx.empty()) {
783 Alert::error(_("Conversion script not found"),
784 bformat(_("%1$s is from a different"
785 " version of LyX, but the"
786 " conversion script lyx2lyx"
787 " could not be found."),
788 from_utf8(filename.absFilename())));
791 ostringstream command;
792 command << os::python()
793 << ' ' << quoteName(lyx2lyx.toFilesystemEncoding())
794 << " -t " << convert<string>(LYX_FORMAT)
795 << " -o " << quoteName(tmpfile.toFilesystemEncoding())
796 << ' ' << quoteName(filename.toFilesystemEncoding());
797 string const command_str = command.str();
799 LYXERR(Debug::INFO, "Running '" << command_str << '\'');
801 cmd_ret const ret = runCommand(command_str);
802 if (ret.first != 0) {
803 Alert::error(_("Conversion script failed"),
804 bformat(_("%1$s is from a different version"
805 " of LyX, but the lyx2lyx script"
806 " failed to convert it."),
807 from_utf8(filename.absFilename())));
810 bool const ret = readFile(tmpfile);
811 // Do stuff with tmpfile name and buffer name here.
812 return ret ? success : failure;
817 if (readDocument(lex)) {
818 Alert::error(_("Document format failure"),
819 bformat(_("%1$s ended unexpectedly, which means"
820 " that it is probably corrupted."),
821 from_utf8(filename.absFilename())));
824 d->file_fully_loaded = true;
829 // Should probably be moved to somewhere else: BufferView? LyXView?
830 bool Buffer::save() const
832 // We don't need autosaves in the immediate future. (Asger)
833 resetAutosaveTimers();
835 string const encodedFilename = d->filename.toFilesystemEncoding();
838 bool madeBackup = false;
840 // make a backup if the file already exists
841 if (lyxrc.make_backup && fileName().exists()) {
842 backupName = FileName(absFileName() + '~');
843 if (!lyxrc.backupdir_path.empty()) {
844 string const mangledName =
845 subst(subst(backupName.absFilename(), '/', '!'), ':', '!');
846 backupName = FileName(addName(lyxrc.backupdir_path,
849 if (fileName().copyTo(backupName, true)) {
852 Alert::error(_("Backup failure"),
853 bformat(_("Cannot create backup file %1$s.\n"
854 "Please check whether the directory exists and is writeable."),
855 from_utf8(backupName.absFilename())));
856 //LYXERR(Debug::DEBUG, "Fs error: " << fe.what());
860 // ask if the disk file has been externally modified (use checksum method)
861 if (fileName().exists() && isExternallyModified(checksum_method)) {
862 docstring const file = makeDisplayPath(absFileName(), 20);
863 docstring text = bformat(_("Document %1$s has been externally modified. Are you sure "
864 "you want to overwrite this file?"), file);
865 int const ret = Alert::prompt(_("Overwrite modified file?"),
866 text, 1, 1, _("&Overwrite"), _("&Cancel"));
871 if (writeFile(d->filename)) {
873 removeAutosaveFile(absFileName());
874 saveCheckSum(d->filename);
877 // Saving failed, so backup is not backup
879 rename(backupName, d->filename);
885 bool Buffer::writeFile(FileName const & fname) const
887 if (d->read_only && fname == d->filename)
893 if (params().embedded)
894 // first write the .lyx file to the temporary directory
895 content = FileName(addName(temppath(), "content.lyx"));
899 if (params().compressed) {
900 gz::ogzstream ofs(content.toFilesystemEncoding().c_str(), ios::out|ios::trunc);
906 ofstream ofs(content.toFilesystemEncoding().c_str(), ios::out|ios::trunc);
913 if (retval && params().embedded) {
914 // write file.lyx and all the embedded files to the zip file fname
915 // if embedding is enabled
916 return d->embedded_files.writeFile(fname);
922 bool Buffer::write(ostream & ofs) const
925 // Use the standard "C" locale for file output.
926 ofs.imbue(std::locale::classic());
929 // The top of the file should not be written by params().
931 // write out a comment in the top of the file
932 ofs << "#LyX " << lyx_version
933 << " created this file. For more info see http://www.lyx.org/\n"
934 << "\\lyxformat " << LYX_FORMAT << "\n"
935 << "\\begin_document\n";
938 /// For each author, set 'used' to true if there is a change
939 /// by this author in the document; otherwise set it to 'false'.
940 AuthorList::Authors::const_iterator a_it = params().authors().begin();
941 AuthorList::Authors::const_iterator a_end = params().authors().end();
942 for (; a_it != a_end; ++a_it)
943 a_it->second.setUsed(false);
945 ParIterator const end = par_iterator_end();
946 ParIterator it = par_iterator_begin();
947 for ( ; it != end; ++it)
948 it->checkAuthors(params().authors());
950 // now write out the buffer parameters.
951 ofs << "\\begin_header\n";
952 params().writeFile(ofs);
953 ofs << "\\end_header\n";
956 ofs << "\n\\begin_body\n";
957 text().write(*this, ofs);
958 ofs << "\n\\end_body\n";
960 // Write marker that shows file is complete
961 ofs << "\\end_document" << endl;
963 // Shouldn't really be needed....
966 // how to check if close went ok?
967 // Following is an attempt... (BE 20001011)
969 // good() returns false if any error occured, including some
971 // bad() returns true if something bad happened in the buffer,
972 // which should include file system full errors.
977 lyxerr << "File was not closed properly." << endl;
984 bool Buffer::makeLaTeXFile(FileName const & fname,
985 string const & original_path,
986 OutputParams const & runparams,
987 bool output_preamble, bool output_body) const
989 string const encoding = runparams.encoding->iconvName();
990 LYXERR(Debug::LATEX, "makeLaTeXFile encoding: " << encoding << "...");
992 odocfstream ofs(encoding);
993 if (!openFileWrite(ofs, fname))
996 //TexStream ts(ofs.rdbuf(), &texrow());
998 bool failed_export = false;
1001 writeLaTeXSource(ofs, original_path,
1002 runparams, output_preamble, output_body);
1004 catch (iconv_codecvt_facet_exception & e) {
1005 lyxerr << "Caught iconv exception: " << e.what() << endl;
1006 failed_export = true;
1008 catch (std::exception const & e) {
1009 lyxerr << "Caught \"normal\" exception: " << e.what() << endl;
1010 failed_export = true;
1013 lyxerr << "Caught some really weird exception..." << endl;
1014 LyX::cref().emergencyCleanup();
1020 failed_export = true;
1021 lyxerr << "File '" << fname << "' was not closed properly." << endl;
1024 if (failed_export) {
1025 Alert::error(_("Encoding error"),
1026 _("Some characters of your document are probably not "
1027 "representable in the chosen encoding.\n"
1028 "Changing the document encoding to utf8 could help."));
1035 void Buffer::writeLaTeXSource(odocstream & os,
1036 string const & original_path,
1037 OutputParams const & runparams_in,
1038 bool const output_preamble, bool const output_body) const
1040 OutputParams runparams = runparams_in;
1042 // validate the buffer.
1043 LYXERR(Debug::LATEX, " Validating buffer...");
1044 LaTeXFeatures features(*this, params(), runparams);
1046 LYXERR(Debug::LATEX, " Buffer validation done.");
1048 // The starting paragraph of the coming rows is the
1049 // first paragraph of the document. (Asger)
1050 if (output_preamble && runparams.nice) {
1051 os << "%% LyX " << lyx_version << " created this file. "
1052 "For more info, see http://www.lyx.org/.\n"
1053 "%% Do not edit unless you really know what "
1055 d->texrow.newline();
1056 d->texrow.newline();
1058 LYXERR(Debug::INFO, "lyx document header finished");
1059 // There are a few differences between nice LaTeX and usual files:
1060 // usual is \batchmode and has a
1061 // special input@path to allow the including of figures
1062 // with either \input or \includegraphics (what figinsets do).
1063 // input@path is set when the actual parameter
1064 // original_path is set. This is done for usual tex-file, but not
1065 // for nice-latex-file. (Matthias 250696)
1066 // Note that input@path is only needed for something the user does
1067 // in the preamble, included .tex files or ERT, files included by
1068 // LyX work without it.
1069 if (output_preamble) {
1070 if (!runparams.nice) {
1071 // code for usual, NOT nice-latex-file
1072 os << "\\batchmode\n"; // changed
1073 // from \nonstopmode
1074 d->texrow.newline();
1076 if (!original_path.empty()) {
1078 // We don't know the encoding of inputpath
1079 docstring const inputpath = from_utf8(latex_path(original_path));
1080 os << "\\makeatletter\n"
1081 << "\\def\\input@path{{"
1082 << inputpath << "/}}\n"
1083 << "\\makeatother\n";
1084 d->texrow.newline();
1085 d->texrow.newline();
1086 d->texrow.newline();
1089 // Write the preamble
1090 runparams.use_babel = params().writeLaTeX(os, features, d->texrow);
1096 os << "\\begin{document}\n";
1097 d->texrow.newline();
1098 } // output_preamble
1100 d->texrow.start(paragraphs().begin()->id(), 0);
1102 LYXERR(Debug::INFO, "preamble finished, now the body.");
1104 if (!lyxrc.language_auto_begin &&
1105 !params().language->babel().empty()) {
1107 os << from_utf8(subst(lyxrc.language_command_begin,
1109 params().language->babel()))
1111 d->texrow.newline();
1114 Encoding const & encoding = params().encoding();
1115 if (encoding.package() == Encoding::CJK) {
1116 // Open a CJK environment, since in contrast to the encodings
1117 // handled by inputenc the document encoding is not set in
1118 // the preamble if it is handled by CJK.sty.
1119 os << "\\begin{CJK}{" << from_ascii(encoding.latexName())
1121 d->texrow.newline();
1124 // if we are doing a real file with body, even if this is the
1125 // child of some other buffer, let's cut the link here.
1126 // This happens for example if only a child document is printed.
1127 Buffer const * save_parent = 0;
1128 if (output_preamble) {
1129 save_parent = d->parent_buffer;
1130 d->parent_buffer = 0;
1133 loadChildDocuments();
1136 latexParagraphs(*this, paragraphs(), os, d->texrow, runparams);
1138 // Restore the parenthood if needed
1139 if (output_preamble)
1140 d->parent_buffer = save_parent;
1142 // add this just in case after all the paragraphs
1144 d->texrow.newline();
1146 if (encoding.package() == Encoding::CJK) {
1147 // Close the open CJK environment.
1148 // latexParagraphs will have opened one even if the last text
1150 os << "\\end{CJK}\n";
1151 d->texrow.newline();
1154 if (!lyxrc.language_auto_end &&
1155 !params().language->babel().empty()) {
1156 os << from_utf8(subst(lyxrc.language_command_end,
1158 params().language->babel()))
1160 d->texrow.newline();
1163 if (output_preamble) {
1164 os << "\\end{document}\n";
1165 d->texrow.newline();
1166 LYXERR(Debug::LATEX, "makeLaTeXFile...done");
1168 LYXERR(Debug::LATEX, "LaTeXFile for inclusion made.");
1170 runparams_in.encoding = runparams.encoding;
1172 // Just to be sure. (Asger)
1173 d->texrow.newline();
1175 LYXERR(Debug::INFO, "Finished making LaTeX file.");
1176 LYXERR(Debug::INFO, "Row count was " << d->texrow.rows() - 1 << '.');
1180 bool Buffer::isLatex() const
1182 return params().getTextClass().outputType() == LATEX;
1186 bool Buffer::isLiterate() const
1188 return params().getTextClass().outputType() == LITERATE;
1192 bool Buffer::isDocBook() const
1194 return params().getTextClass().outputType() == DOCBOOK;
1198 void Buffer::makeDocBookFile(FileName const & fname,
1199 OutputParams const & runparams,
1200 bool const body_only) const
1202 LYXERR(Debug::LATEX, "makeDocBookFile...");
1206 if (!openFileWrite(ofs, fname))
1209 writeDocBookSource(ofs, fname.absFilename(), runparams, body_only);
1213 lyxerr << "File '" << fname << "' was not closed properly." << endl;
1217 void Buffer::writeDocBookSource(odocstream & os, string const & fname,
1218 OutputParams const & runparams,
1219 bool const only_body) const
1221 LaTeXFeatures features(*this, params(), runparams);
1226 TextClass const & tclass = params().getTextClass();
1227 string const top_element = tclass.latexname();
1230 if (runparams.flavor == OutputParams::XML)
1231 os << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
1234 os << "<!DOCTYPE " << from_ascii(top_element) << ' ';
1237 if (! tclass.class_header().empty())
1238 os << from_ascii(tclass.class_header());
1239 else if (runparams.flavor == OutputParams::XML)
1240 os << "PUBLIC \"-//OASIS//DTD DocBook XML//EN\" "
1241 << "\"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd\"";
1243 os << " PUBLIC \"-//OASIS//DTD DocBook V4.2//EN\"";
1245 docstring preamble = from_utf8(params().preamble);
1246 if (runparams.flavor != OutputParams::XML ) {
1247 preamble += "<!ENTITY % output.print.png \"IGNORE\">\n";
1248 preamble += "<!ENTITY % output.print.pdf \"IGNORE\">\n";
1249 preamble += "<!ENTITY % output.print.eps \"IGNORE\">\n";
1250 preamble += "<!ENTITY % output.print.bmp \"IGNORE\">\n";
1253 string const name = runparams.nice
1254 ? changeExtension(absFileName(), ".sgml") : fname;
1255 preamble += features.getIncludedFiles(name);
1256 preamble += features.getLyXSGMLEntities();
1258 if (!preamble.empty()) {
1259 os << "\n [ " << preamble << " ]";
1264 string top = top_element;
1266 if (runparams.flavor == OutputParams::XML)
1267 top += params().language->code();
1269 top += params().language->code().substr(0,2);
1272 if (!params().options.empty()) {
1274 top += params().options;
1277 os << "<!-- " << ((runparams.flavor == OutputParams::XML)? "XML" : "SGML")
1278 << " file was created by LyX " << lyx_version
1279 << "\n See http://www.lyx.org/ for more information -->\n";
1281 params().getTextClass().counters().reset();
1283 loadChildDocuments();
1285 sgml::openTag(os, top);
1287 docbookParagraphs(paragraphs(), *this, os, runparams);
1288 sgml::closeTag(os, top_element);
1292 // chktex should be run with these flags disabled: 3, 22, 25, 30, 38(?)
1293 // Other flags: -wall -v0 -x
1294 int Buffer::runChktex()
1298 // get LaTeX-Filename
1299 FileName const path(temppath());
1300 string const name = addName(path.absFilename(), latexName());
1301 string const org_path = filePath();
1303 support::PathChanger p(path); // path to LaTeX file
1304 message(_("Running chktex..."));
1306 // Generate the LaTeX file if neccessary
1307 OutputParams runparams(¶ms().encoding());
1308 runparams.flavor = OutputParams::LATEX;
1309 runparams.nice = false;
1310 makeLaTeXFile(FileName(name), org_path, runparams);
1313 Chktex chktex(lyxrc.chktex_command, onlyFilename(name), filePath());
1314 int const res = chktex.run(terr); // run chktex
1317 Alert::error(_("chktex failure"),
1318 _("Could not run chktex successfully."));
1319 } else if (res > 0) {
1320 ErrorList & errlist = d->errorLists["ChkTeX"];
1322 bufferErrors(terr, errlist);
1333 void Buffer::validate(LaTeXFeatures & features) const
1335 TextClass const & tclass = params().getTextClass();
1337 if (params().outputChanges) {
1338 bool dvipost = LaTeXFeatures::isAvailable("dvipost");
1339 bool xcolorsoul = LaTeXFeatures::isAvailable("soul") &&
1340 LaTeXFeatures::isAvailable("xcolor");
1342 if (features.runparams().flavor == OutputParams::LATEX) {
1344 features.require("ct-dvipost");
1345 features.require("dvipost");
1346 } else if (xcolorsoul) {
1347 features.require("ct-xcolor-soul");
1348 features.require("soul");
1349 features.require("xcolor");
1351 features.require("ct-none");
1353 } else if (features.runparams().flavor == OutputParams::PDFLATEX ) {
1355 features.require("ct-xcolor-soul");
1356 features.require("soul");
1357 features.require("xcolor");
1358 features.require("pdfcolmk"); // improves color handling in PDF output
1360 features.require("ct-none");
1365 // AMS Style is at document level
1366 if (params().use_amsmath == BufferParams::package_on
1367 || tclass.provides("amsmath"))
1368 features.require("amsmath");
1369 if (params().use_esint == BufferParams::package_on)
1370 features.require("esint");
1372 loadChildDocuments();
1374 for_each(paragraphs().begin(), paragraphs().end(),
1375 boost::bind(&Paragraph::validate, _1, boost::ref(features)));
1377 // the bullet shapes are buffer level not paragraph level
1378 // so they are tested here
1379 for (int i = 0; i < 4; ++i) {
1380 if (params().user_defined_bullet(i) != ITEMIZE_DEFAULTS[i]) {
1381 int const font = params().user_defined_bullet(i).getFont();
1383 int const c = params()
1384 .user_defined_bullet(i)
1391 features.require("latexsym");
1393 } else if (font == 1) {
1394 features.require("amssymb");
1395 } else if ((font >= 2 && font <= 5)) {
1396 features.require("pifont");
1401 if (lyxerr.debugging(Debug::LATEX)) {
1402 features.showStruct();
1407 void Buffer::getLabelList(vector<docstring> & list) const
1409 /// if this is a child document and the parent is already loaded
1410 /// Use the parent's list instead [ale990407]
1411 Buffer const * tmp = masterBuffer();
1413 lyxerr << "masterBuffer() failed!" << endl;
1417 tmp->getLabelList(list);
1421 loadChildDocuments();
1423 for (InsetIterator it = inset_iterator_begin(inset()); it; ++it)
1424 it.nextInset()->getLabelList(*this, list);
1428 void Buffer::updateBibfilesCache() const
1430 // if this is a child document and the parent is already loaded
1431 // update the parent's cache instead
1432 Buffer const * tmp = masterBuffer();
1435 tmp->updateBibfilesCache();
1439 bibfilesCache_.clear();
1440 for (InsetIterator it = inset_iterator_begin(inset()); it; ++it) {
1441 if (it->lyxCode() == BIBTEX_CODE) {
1442 InsetBibtex const & inset =
1443 static_cast<InsetBibtex const &>(*it);
1444 vector<FileName> const bibfiles = inset.getFiles(*this);
1445 bibfilesCache_.insert(bibfilesCache_.end(),
1448 } else if (it->lyxCode() == INCLUDE_CODE) {
1449 InsetInclude & inset =
1450 static_cast<InsetInclude &>(*it);
1451 inset.updateBibfilesCache(*this);
1452 vector<FileName> const & bibfiles =
1453 inset.getBibfilesCache(*this);
1454 bibfilesCache_.insert(bibfilesCache_.end(),
1462 vector<FileName> const & Buffer::getBibfilesCache() const
1464 // if this is a child document and the parent is already loaded
1465 // use the parent's cache instead
1466 Buffer const * tmp = masterBuffer();
1469 return tmp->getBibfilesCache();
1471 // We update the cache when first used instead of at loading time.
1472 if (bibfilesCache_.empty())
1473 const_cast<Buffer *>(this)->updateBibfilesCache();
1475 return bibfilesCache_;
1479 bool Buffer::isDepClean(string const & name) const
1481 DepClean::const_iterator const it = d->dep_clean.find(name);
1482 if (it == d->dep_clean.end())
1488 void Buffer::markDepClean(string const & name)
1490 d->dep_clean[name] = true;
1494 bool Buffer::dispatch(string const & command, bool * result)
1496 return dispatch(lyxaction.lookupFunc(command), result);
1500 bool Buffer::dispatch(FuncRequest const & func, bool * result)
1502 bool dispatched = true;
1504 switch (func.action) {
1505 case LFUN_BUFFER_EXPORT: {
1506 bool const tmp = doExport(to_utf8(func.argument()), false);
1519 void Buffer::changeLanguage(Language const * from, Language const * to)
1524 for_each(par_iterator_begin(),
1526 bind(&Paragraph::changeLanguage, _1, params(), from, to));
1530 bool Buffer::isMultiLingual() const
1532 ParConstIterator end = par_iterator_end();
1533 for (ParConstIterator it = par_iterator_begin(); it != end; ++it)
1534 if (it->isMultiLingual(params()))
1541 ParIterator Buffer::getParFromID(int const id) const
1543 ParConstIterator it = par_iterator_begin();
1544 ParConstIterator const end = par_iterator_end();
1547 // John says this is called with id == -1 from undo
1548 lyxerr << "getParFromID(), id: " << id << endl;
1552 for (; it != end; ++it)
1560 bool Buffer::hasParWithID(int const id) const
1562 ParConstIterator const it = getParFromID(id);
1563 return it != par_iterator_end();
1567 ParIterator Buffer::par_iterator_begin()
1569 return lyx::par_iterator_begin(inset());
1573 ParIterator Buffer::par_iterator_end()
1575 return lyx::par_iterator_end(inset());
1579 ParConstIterator Buffer::par_iterator_begin() const
1581 return lyx::par_const_iterator_begin(inset());
1585 ParConstIterator Buffer::par_iterator_end() const
1587 return lyx::par_const_iterator_end(inset());
1591 Language const * Buffer::language() const
1593 return params().language;
1597 docstring const Buffer::B_(string const & l10n) const
1599 return params().B_(l10n);
1603 bool Buffer::isClean() const
1605 return d->lyx_clean;
1609 bool Buffer::isBakClean() const
1611 return d->bak_clean;
1615 bool Buffer::isExternallyModified(CheckMethod method) const
1617 BOOST_ASSERT(d->filename.exists());
1618 // if method == timestamp, check timestamp before checksum
1619 return (method == checksum_method
1620 || d->timestamp_ != d->filename.lastModified())
1621 && d->checksum_ != d->filename.checksum();
1625 void Buffer::saveCheckSum(FileName const & file) const
1627 if (file.exists()) {
1628 d->timestamp_ = file.lastModified();
1629 d->checksum_ = file.checksum();
1631 // in the case of save to a new file.
1638 void Buffer::markClean() const
1640 if (!d->lyx_clean) {
1641 d->lyx_clean = true;
1644 // if the .lyx file has been saved, we don't need an
1646 d->bak_clean = true;
1650 void Buffer::markBakClean() const
1652 d->bak_clean = true;
1656 void Buffer::setUnnamed(bool flag)
1662 bool Buffer::isUnnamed() const
1668 // FIXME: this function should be moved to buffer_pimpl.C
1669 void Buffer::markDirty()
1672 d->lyx_clean = false;
1675 d->bak_clean = false;
1677 DepClean::iterator it = d->dep_clean.begin();
1678 DepClean::const_iterator const end = d->dep_clean.end();
1680 for (; it != end; ++it)
1685 FileName Buffer::fileName() const
1691 string Buffer::absFileName() const
1693 return d->filename.absFilename();
1697 string Buffer::filePath() const
1699 return d->filename.onlyPath().absFilename();
1703 bool Buffer::isReadonly() const
1705 return d->read_only;
1709 void Buffer::setParent(Buffer const * buffer)
1711 // Avoids recursive include.
1712 d->parent_buffer = buffer == this ? 0 : buffer;
1716 Buffer const * Buffer::parent()
1718 return d->parent_buffer;
1722 Buffer const * Buffer::masterBuffer() const
1724 if (!d->parent_buffer)
1727 return d->parent_buffer->masterBuffer();
1731 bool Buffer::hasMacro(docstring const & name, Paragraph const & par) const
1733 Impl::PositionToMacroMap::iterator it;
1734 it = d->macros[name].upper_bound(par.macrocontextPosition());
1735 if (it != d->macros[name].end())
1738 // If there is a master buffer, query that
1739 Buffer const * master = masterBuffer();
1740 if (master && master != this)
1741 return master->hasMacro(name);
1743 return MacroTable::globalMacros().has(name);
1747 bool Buffer::hasMacro(docstring const & name) const
1749 if( !d->macros[name].empty() )
1752 // If there is a master buffer, query that
1753 Buffer const * master = masterBuffer();
1754 if (master && master != this)
1755 return master->hasMacro(name);
1757 return MacroTable::globalMacros().has(name);
1761 MacroData const & Buffer::getMacro(docstring const & name,
1762 Paragraph const & par) const
1764 Impl::PositionToMacroMap::iterator it;
1765 it = d->macros[name].upper_bound(par.macrocontextPosition());
1766 if( it != d->macros[name].end() )
1769 // If there is a master buffer, query that
1770 Buffer const * master = masterBuffer();
1771 if (master && master != this)
1772 return master->getMacro(name);
1774 return MacroTable::globalMacros().get(name);
1778 MacroData const & Buffer::getMacro(docstring const & name) const
1780 Impl::PositionToMacroMap::iterator it;
1781 it = d->macros[name].begin();
1782 if( it != d->macros[name].end() )
1785 // If there is a master buffer, query that
1786 Buffer const * master = masterBuffer();
1787 if (master && master != this)
1788 return master->getMacro(name);
1790 return MacroTable::globalMacros().get(name);
1794 void Buffer::updateMacros()
1796 // start with empty table
1797 d->macros = Impl::NameToPositionMacroMap();
1799 // Iterate over buffer
1800 ParagraphList & pars = text().paragraphs();
1801 for (size_t i = 0, n = pars.size(); i != n; ++i) {
1802 // set position again
1803 pars[i].setMacrocontextPosition(i);
1805 //lyxerr << "searching main par " << i
1806 // << " for macro definitions" << std::endl;
1807 InsetList const & insets = pars[i].insetList();
1808 InsetList::const_iterator it = insets.begin();
1809 InsetList::const_iterator end = insets.end();
1810 for ( ; it != end; ++it) {
1811 if (it->inset->lyxCode() != MATHMACRO_CODE)
1815 MathMacroTemplate const & macroTemplate
1816 = static_cast<MathMacroTemplate const &>(*it->inset);
1819 if (macroTemplate.validMacro()) {
1820 MacroData macro = macroTemplate.asMacroData();
1823 // call hasMacro here instead of directly querying mc to
1824 // also take the master document into consideration
1825 macro.setRedefinition(hasMacro(macroTemplate.name()));
1827 // register macro (possibly overwrite the previous one of this paragraph)
1828 d->macros[macroTemplate.name()][i] = macro;
1835 void Buffer::changeRefsIfUnique(docstring const & from, docstring const & to,
1838 //FIXME: This does not work for child documents yet.
1839 BOOST_ASSERT(code == CITE_CODE || code == REF_CODE);
1840 // Check if the label 'from' appears more than once
1841 vector<docstring> labels;
1844 if (code == CITE_CODE) {
1846 keys.fillWithBibKeys(this);
1847 BiblioInfo::const_iterator bit = keys.begin();
1848 BiblioInfo::const_iterator bend = keys.end();
1850 for (; bit != bend; ++bit)
1852 labels.push_back(bit->first);
1855 getLabelList(labels);
1856 paramName = "reference";
1859 if (std::count(labels.begin(), labels.end(), from) > 1)
1862 for (InsetIterator it = inset_iterator_begin(inset()); it; ++it) {
1863 if (it->lyxCode() == code) {
1864 InsetCommand & inset = static_cast<InsetCommand &>(*it);
1865 docstring const oldValue = inset.getParam(paramName);
1866 if (oldValue == from)
1867 inset.setParam(paramName, to);
1873 void Buffer::getSourceCode(odocstream & os, pit_type par_begin,
1874 pit_type par_end, bool full_source)
1876 OutputParams runparams(¶ms().encoding());
1877 runparams.nice = true;
1878 runparams.flavor = OutputParams::LATEX;
1879 runparams.linelen = lyxrc.plaintext_linelen;
1880 // No side effect of file copying and image conversion
1881 runparams.dryrun = true;
1885 os << "% " << _("Preview source code") << "\n\n";
1886 d->texrow.newline();
1887 d->texrow.newline();
1889 writeLaTeXSource(os, filePath(), runparams, true, true);
1891 writeDocBookSource(os, absFileName(), runparams, false);
1894 runparams.par_begin = par_begin;
1895 runparams.par_end = par_end;
1896 if (par_begin + 1 == par_end)
1898 << bformat(_("Preview source code for paragraph %1$d"), par_begin)
1902 << bformat(_("Preview source code from paragraph %1$s to %2$s"),
1903 convert<docstring>(par_begin),
1904 convert<docstring>(par_end - 1))
1906 d->texrow.newline();
1907 d->texrow.newline();
1908 // output paragraphs
1910 latexParagraphs(*this, paragraphs(), os, d->texrow, runparams);
1913 docbookParagraphs(paragraphs(), *this, os, runparams);
1919 ErrorList & Buffer::errorList(string const & type) const
1921 static ErrorList emptyErrorList;
1922 std::map<string, ErrorList>::iterator I = d->errorLists.find(type);
1923 if (I == d->errorLists.end())
1924 return emptyErrorList;
1930 void Buffer::structureChanged() const
1933 gui_->structureChanged();
1937 void Buffer::errors(std::string const & err) const
1944 void Buffer::message(docstring const & msg) const
1951 void Buffer::setBusy(bool on) const
1958 void Buffer::setReadOnly(bool on) const
1961 d->wa_->setReadOnly(on);
1965 void Buffer::updateTitles() const
1968 d->wa_->updateTitles();
1972 void Buffer::resetAutosaveTimers() const
1975 gui_->resetAutosaveTimers();
1979 void Buffer::setGuiDelegate(frontend::GuiBufferDelegate * gui)
1988 class AutoSaveBuffer : public support::ForkedProcess {
1991 AutoSaveBuffer(Buffer const & buffer, FileName const & fname)
1992 : buffer_(buffer), fname_(fname) {}
1994 virtual boost::shared_ptr<ForkedProcess> clone() const
1996 return boost::shared_ptr<ForkedProcess>(new AutoSaveBuffer(*this));
2001 command_ = to_utf8(bformat(_("Auto-saving %1$s"),
2002 from_utf8(fname_.absFilename())));
2003 return run(DontWait);
2007 virtual int generateChild();
2009 Buffer const & buffer_;
2014 #if !defined (HAVE_FORK)
2018 int AutoSaveBuffer::generateChild()
2020 // tmp_ret will be located (usually) in /tmp
2021 // will that be a problem?
2022 pid_t const pid = fork();
2023 // If you want to debug the autosave
2024 // you should set pid to -1, and comment out the fork.
2025 if (pid == 0 || pid == -1) {
2026 // pid = -1 signifies that lyx was unable
2027 // to fork. But we will do the save
2029 bool failed = false;
2031 FileName const tmp_ret(tempName(FileName(), "lyxauto"));
2032 if (!tmp_ret.empty()) {
2033 buffer_.writeFile(tmp_ret);
2034 // assume successful write of tmp_ret
2035 if (!rename(tmp_ret, fname_)) {
2037 // most likely couldn't move between
2038 // filesystems unless write of tmp_ret
2039 // failed so remove tmp file (if it
2041 tmp_ret.removeFile();
2048 // failed to write/rename tmp_ret so try writing direct
2049 if (!buffer_.writeFile(fname_)) {
2050 // It is dangerous to do this in the child,
2051 // but safe in the parent, so...
2052 if (pid == -1) // emit message signal.
2053 buffer_.message(_("Autosave failed!"));
2056 if (pid == 0) { // we are the child so...
2066 // Perfect target for a thread...
2067 void Buffer::autoSave() const
2069 if (isBakClean() || isReadonly()) {
2070 // We don't save now, but we'll try again later
2071 resetAutosaveTimers();
2075 // emit message signal.
2076 message(_("Autosaving current document..."));
2078 // create autosave filename
2079 string fname = filePath();
2081 fname += onlyFilename(absFileName());
2084 AutoSaveBuffer autosave(*this, FileName(fname));
2088 resetAutosaveTimers();
2092 /** Write a buffer to a new file name and rename the buffer
2093 according to the new file name.
2095 This function is e.g. used by menu callbacks and
2096 LFUN_BUFFER_WRITE_AS.
2098 If 'newname' is empty (the default), the user is asked via a
2099 dialog for the buffer's new name and location.
2101 If 'newname' is non-empty and has an absolute path, that is used.
2102 Otherwise the base directory of the buffer is used as the base
2103 for any relative path in 'newname'.
2106 bool Buffer::writeAs(string const & newname)
2108 string fname = absFileName();
2109 string const oldname = fname;
2111 if (newname.empty()) { /// No argument? Ask user through dialog
2114 FileDialog dlg(_("Choose a filename to save document as"),
2115 LFUN_BUFFER_WRITE_AS);
2116 dlg.setButton1(_("Documents|#o#O"), from_utf8(lyxrc.document_path));
2117 dlg.setButton2(_("Templates|#T#t"), from_utf8(lyxrc.template_path));
2119 if (!support::isLyXFilename(fname))
2122 support::FileFilterList const filter(_("LyX Documents (*.lyx)"));
2124 FileDialog::Result result =
2125 dlg.save(from_utf8(onlyPath(fname)),
2127 from_utf8(onlyFilename(fname)));
2129 if (result.first == FileDialog::Later)
2132 fname = to_utf8(result.second);
2137 // Make sure the absolute filename ends with appropriate suffix
2138 fname = makeAbsPath(fname).absFilename();
2139 if (!support::isLyXFilename(fname))
2143 fname = makeAbsPath(newname, onlyPath(oldname)).absFilename();
2145 if (FileName(fname).exists()) {
2146 docstring const file = makeDisplayPath(fname, 30);
2147 docstring text = bformat(_("The document %1$s already "
2148 "exists.\n\nDo you want to "
2149 "overwrite that document?"),
2151 int const ret = Alert::prompt(_("Overwrite document?"),
2152 text, 0, 1, _("&Overwrite"), _("&Cancel"));
2158 // Ok, change the name of the buffer
2161 bool unnamed = isUnnamed();
2163 saveCheckSum(FileName(fname));
2166 setFileName(oldname);
2167 setUnnamed(unnamed);
2168 saveCheckSum(FileName(oldname));
2172 removeAutosaveFile(oldname);
2177 bool Buffer::menuWrite()
2180 LyX::ref().session().lastFiles().add(FileName(absFileName()));
2184 // FIXME: we don't tell the user *WHY* the save failed !!
2186 docstring const file = makeDisplayPath(absFileName(), 30);
2188 docstring text = bformat(_("The document %1$s could not be saved.\n\n"
2189 "Do you want to rename the document and "
2190 "try again?"), file);
2191 int const ret = Alert::prompt(_("Rename and save?"),
2192 text, 0, 1, _("&Rename"), _("&Cancel"));
2201 void Buffer::resetChildDocuments(bool close_them) const
2203 for (InsetIterator it = inset_iterator_begin(inset()); it; ++it) {
2204 if (it->lyxCode() != INCLUDE_CODE)
2206 InsetCommand const & inset = static_cast<InsetCommand const &>(*it);
2207 InsetCommandParams const & ip = inset.params();
2209 resetParentBuffer(this, ip, close_them);
2212 if (use_gui && masterBuffer() == this)
2213 updateLabels(*this);
2217 void Buffer::loadChildDocuments() const
2219 bool parse_error = false;
2221 for (InsetIterator it = inset_iterator_begin(inset()); it; ++it) {
2222 if (it->lyxCode() != INCLUDE_CODE)
2224 InsetCommand const & inset = static_cast<InsetCommand const &>(*it);
2225 InsetCommandParams const & ip = inset.params();
2226 Buffer * child = loadIfNeeded(*this, ip);
2229 parse_error |= !child->errorList("Parse").empty();
2230 child->loadChildDocuments();
2233 if (use_gui && masterBuffer() == this)
2234 updateLabels(*this);
2238 string Buffer::bufferFormat() const
2248 bool Buffer::doExport(string const & format, bool put_in_tempdir,
2249 string & result_file) const
2251 string backend_format;
2252 OutputParams runparams(¶ms().encoding());
2253 runparams.flavor = OutputParams::LATEX;
2254 runparams.linelen = lyxrc.plaintext_linelen;
2255 vector<string> backs = backends();
2256 if (find(backs.begin(), backs.end(), format) == backs.end()) {
2257 // Get shortest path to format
2258 Graph::EdgePath path;
2259 for (vector<string>::const_iterator it = backs.begin();
2260 it != backs.end(); ++it) {
2261 Graph::EdgePath p = theConverters().getPath(*it, format);
2262 if (!p.empty() && (path.empty() || p.size() < path.size())) {
2263 backend_format = *it;
2268 runparams.flavor = theConverters().getFlavor(path);
2270 Alert::error(_("Couldn't export file"),
2271 bformat(_("No information for exporting the format %1$s."),
2272 formats.prettyName(format)));
2276 backend_format = format;
2277 // FIXME: Don't hardcode format names here, but use a flag
2278 if (backend_format == "pdflatex")
2279 runparams.flavor = OutputParams::PDFLATEX;
2282 string filename = latexName(false);
2283 filename = addName(temppath(), filename);
2284 filename = changeExtension(filename,
2285 formats.extension(backend_format));
2287 // Plain text backend
2288 if (backend_format == "text")
2289 writePlaintextFile(*this, FileName(filename), runparams);
2291 else if (backend_format == "lyx")
2292 writeFile(FileName(filename));
2294 else if (isDocBook()) {
2295 runparams.nice = !put_in_tempdir;
2296 makeDocBookFile(FileName(filename), runparams);
2299 else if (backend_format == format) {
2300 runparams.nice = true;
2301 if (!makeLaTeXFile(FileName(filename), string(), runparams))
2303 } else if (!lyxrc.tex_allows_spaces
2304 && support::contains(filePath(), ' ')) {
2305 Alert::error(_("File name error"),
2306 _("The directory path to the document cannot contain spaces."));
2309 runparams.nice = false;
2310 if (!makeLaTeXFile(FileName(filename), filePath(), runparams))
2314 string const error_type = (format == "program")
2315 ? "Build" : bufferFormat();
2316 string const ext = formats.extension(format);
2317 FileName const tmp_result_file(changeExtension(filename, ext));
2318 bool const success = theConverters().convert(this, FileName(filename),
2319 tmp_result_file, FileName(absFileName()), backend_format, format,
2320 errorList(error_type));
2321 // Emit the signal to show the error list.
2322 if (format != backend_format)
2328 result_file = tmp_result_file.absFilename();
2330 result_file = changeExtension(absFileName(), ext);
2331 // We need to copy referenced files (e. g. included graphics
2332 // if format == "dvi") to the result dir.
2333 vector<ExportedFile> const files =
2334 runparams.exportdata->externalFiles(format);
2335 string const dest = onlyPath(result_file);
2336 CopyStatus status = SUCCESS;
2337 for (vector<ExportedFile>::const_iterator it = files.begin();
2338 it != files.end() && status != CANCEL; ++it) {
2340 formats.getFormatFromFile(it->sourceName);
2341 status = copyFile(fmt, it->sourceName,
2342 makeAbsPath(it->exportName, dest),
2343 it->exportName, status == FORCE);
2345 if (status == CANCEL) {
2346 message(_("Document export cancelled."));
2347 } else if (tmp_result_file.exists()) {
2348 // Finally copy the main file
2349 status = copyFile(format, tmp_result_file,
2350 FileName(result_file), result_file,
2352 message(bformat(_("Document exported as %1$s "
2354 formats.prettyName(format),
2355 makeDisplayPath(result_file)));
2357 // This must be a dummy converter like fax (bug 1888)
2358 message(bformat(_("Document exported as %1$s"),
2359 formats.prettyName(format)));
2367 bool Buffer::doExport(string const & format, bool put_in_tempdir) const
2370 return doExport(format, put_in_tempdir, result_file);
2374 bool Buffer::preview(string const & format) const
2377 if (!doExport(format, true, result_file))
2379 return formats.view(*this, FileName(result_file), format);
2383 bool Buffer::isExportable(string const & format) const
2385 vector<string> backs = backends();
2386 for (vector<string>::const_iterator it = backs.begin();
2387 it != backs.end(); ++it)
2388 if (theConverters().isReachable(*it, format))
2394 vector<Format const *> Buffer::exportableFormats(bool only_viewable) const
2396 vector<string> backs = backends();
2397 vector<Format const *> result =
2398 theConverters().getReachable(backs[0], only_viewable, true);
2399 for (vector<string>::const_iterator it = backs.begin() + 1;
2400 it != backs.end(); ++it) {
2401 vector<Format const *> r =
2402 theConverters().getReachable(*it, only_viewable, false);
2403 result.insert(result.end(), r.begin(), r.end());
2409 vector<string> Buffer::backends() const
2412 if (params().getTextClass().isTeXClassAvailable()) {
2413 v.push_back(bufferFormat());
2414 // FIXME: Don't hardcode format names here, but use a flag
2415 if (v.back() == "latex")
2416 v.push_back("pdflatex");
2418 v.push_back("text");
2424 bool Buffer::readFileHelper(FileName const & s)
2426 // File information about normal file
2428 docstring const file = makeDisplayPath(s.absFilename(), 50);
2429 docstring text = bformat(_("The specified document\n%1$s"
2430 "\ncould not be read."), file);
2431 Alert::error(_("Could not read document"), text);
2435 // Check if emergency save file exists and is newer.
2436 FileName const e(s.absFilename() + ".emergency");
2438 if (e.exists() && s.exists() && e.lastModified() > s.lastModified()) {
2439 docstring const file = makeDisplayPath(s.absFilename(), 20);
2440 docstring const text =
2441 bformat(_("An emergency save of the document "
2443 "Recover emergency save?"), file);
2444 switch (Alert::prompt(_("Load emergency save?"), text, 0, 2,
2445 _("&Recover"), _("&Load Original"),
2449 // the file is not saved if we load the emergency file.
2459 // Now check if autosave file is newer.
2460 FileName const a(onlyPath(s.absFilename()) + '#' + onlyFilename(s.absFilename()) + '#');
2462 if (a.exists() && s.exists() && a.lastModified() > s.lastModified()) {
2463 docstring const file = makeDisplayPath(s.absFilename(), 20);
2464 docstring const text =
2465 bformat(_("The backup of the document "
2466 "%1$s is newer.\n\nLoad the "
2467 "backup instead?"), file);
2468 switch (Alert::prompt(_("Load backup?"), text, 0, 2,
2469 _("&Load backup"), _("Load &original"),
2473 // the file is not saved if we load the autosave file.
2477 // Here we delete the autosave
2488 bool Buffer::loadLyXFile(FileName const & s)
2490 if (s.isReadableFile()) {
2491 if (readFileHelper(s)) {
2492 lyxvc().file_found_hook(s);
2493 if (!s.isWritable())
2498 docstring const file = makeDisplayPath(s.absFilename(), 20);
2499 // Here we probably should run
2500 if (LyXVC::file_not_found_hook(s)) {
2501 docstring const text =
2502 bformat(_("Do you want to retrieve the document"
2503 " %1$s from version control?"), file);
2504 int const ret = Alert::prompt(_("Retrieve from version control?"),
2505 text, 0, 1, _("&Retrieve"), _("&Cancel"));
2508 // How can we know _how_ to do the checkout?
2509 // With the current VC support it has to be,
2510 // a RCS file since CVS do not have special ,v files.
2512 return loadLyXFile(s);
2520 void Buffer::bufferErrors(TeXErrors const & terr, ErrorList & errorList) const
2522 TeXErrors::Errors::const_iterator cit = terr.begin();
2523 TeXErrors::Errors::const_iterator end = terr.end();
2525 for (; cit != end; ++cit) {
2528 int errorRow = cit->error_in_line;
2529 bool found = d->texrow.getIdFromRow(errorRow, id_start,
2535 found = d->texrow.getIdFromRow(errorRow, id_end, pos_end);
2536 } while (found && id_start == id_end && pos_start == pos_end);
2538 errorList.push_back(ErrorItem(cit->error_desc,
2539 cit->error_text, id_start, pos_start, pos_end));