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);
170 mutable TexRow texrow;
171 Buffer const * parent_buffer;
173 /// need to regenerate .tex?
177 mutable bool lyx_clean;
179 /// is autosave needed?
180 mutable bool bak_clean;
182 /// is this a unnamed file (New...)?
188 /// name of the file the buffer is associated with.
191 /** Set to true only when the file is fully loaded.
192 * Used to prevent the premature generation of previews
193 * and by the citation inset.
195 bool file_fully_loaded;
197 /// our Text that should be wrapped in an InsetText
201 mutable TocBackend toc_backend;
204 typedef std::map<unsigned int, MacroData, std::greater<int> > PositionToMacroMap;
205 typedef std::map<docstring, PositionToMacroMap> NameToPositionMacroMap;
206 NameToPositionMacroMap macros;
208 /// Container for all sort of Buffer dependant errors.
209 map<string, ErrorList> errorLists;
211 /// all embedded files of this buffer
212 EmbeddedFiles embedded_files;
214 /// timestamp and checksum used to test if the file has been externally
215 /// modified. (Used to properly enable 'File->Revert to saved', bug 4114).
217 unsigned long checksum_;
220 frontend::WorkAreaManager * wa_;
227 Buffer::Impl::Impl(Buffer & parent, FileName const & file, bool readonly_)
228 : parent_buffer(0), lyx_clean(true), bak_clean(true), unnamed(false),
229 read_only(readonly_), filename(file), file_fully_loaded(false),
230 inset(params), toc_backend(&parent), embedded_files(&parent),
231 timestamp_(0), checksum_(0), wa_(0), undo_(parent)
233 inset.setAutoBreakRows(true);
234 lyxvc.setBuffer(&parent);
235 temppath = createBufferTmpDir();
237 // FIXME: And now do something if temppath == string(), because we
238 // assume from now on that temppath points to a valid temp dir.
239 // See http://www.mail-archive.com/lyx-devel@lists.lyx.org/msg67406.html
242 wa_ = new frontend::WorkAreaManager;
246 Buffer::Buffer(string const & file, bool readonly)
247 : d(new Impl(*this, FileName(file), readonly)), gui_(0)
249 LYXERR(Debug::INFO, "Buffer::Buffer()");
255 LYXERR(Debug::INFO, "Buffer::~Buffer()");
256 // here the buffer should take care that it is
257 // saved properly, before it goes into the void.
259 Buffer const * master = masterBuffer();
260 if (master != this && use_gui)
261 // We are closing buf which was a child document so we
262 // must update the labels and section numbering of its master
264 updateLabels(*master);
266 resetChildDocuments(false);
268 if (!temppath().empty() && !FileName(temppath()).destroyDirectory()) {
269 Alert::warning(_("Could not remove temporary directory"),
270 bformat(_("Could not remove the temporary directory %1$s"),
271 from_utf8(temppath())));
274 // Remove any previewed LaTeX snippets associated with this buffer.
275 graphics::Previews::get().removeLoader(*this);
285 void Buffer::changed() const
292 frontend::WorkAreaManager & Buffer::workAreaManager() const
294 BOOST_ASSERT(d->wa_);
299 Text & Buffer::text() const
301 return const_cast<Text &>(d->inset.text_);
305 Inset & Buffer::inset() const
307 return const_cast<InsetText &>(d->inset);
311 BufferParams & Buffer::params()
317 BufferParams const & Buffer::params() const
323 ParagraphList & Buffer::paragraphs()
325 return text().paragraphs();
329 ParagraphList const & Buffer::paragraphs() const
331 return text().paragraphs();
335 LyXVC & Buffer::lyxvc()
341 LyXVC const & Buffer::lyxvc() const
347 string const & Buffer::temppath() const
353 TexRow const & Buffer::texrow() const
359 TocBackend & Buffer::tocBackend() const
361 return d->toc_backend;
365 EmbeddedFiles & Buffer::embeddedFiles()
367 return d->embedded_files;
371 EmbeddedFiles const & Buffer::embeddedFiles() const
373 return d->embedded_files;
377 Undo & Buffer::undo()
383 string Buffer::latexName(bool const no_path) const
385 FileName latex_name = makeLatexName(d->filename);
386 return no_path ? latex_name.onlyFileName()
387 : latex_name.absFilename();
391 string Buffer::logName(LogType * type) const
393 string const filename = latexName(false);
395 if (filename.empty()) {
401 string const path = temppath();
403 FileName const fname(addName(temppath(),
404 onlyFilename(changeExtension(filename,
406 FileName const bname(
407 addName(path, onlyFilename(
408 changeExtension(filename,
409 formats.extension("literate") + ".out"))));
411 // If no Latex log or Build log is newer, show Build log
413 if (bname.exists() &&
414 (!fname.exists() || fname.lastModified() < bname.lastModified())) {
415 LYXERR(Debug::FILES, "Log name calculated as: " << bname);
418 return bname.absFilename();
420 LYXERR(Debug::FILES, "Log name calculated as: " << fname);
423 return fname.absFilename();
427 void Buffer::setReadonly(bool const flag)
429 if (d->read_only != flag) {
436 void Buffer::setFileName(string const & newfile)
438 d->filename = makeAbsPath(newfile);
439 setReadonly(d->filename.isReadOnly());
444 int Buffer::readHeader(Lexer & lex)
446 int unknown_tokens = 0;
448 int begin_header_line = -1;
450 // Initialize parameters that may be/go lacking in header:
451 params().branchlist().clear();
452 params().preamble.erase();
453 params().options.erase();
454 params().float_placement.erase();
455 params().paperwidth.erase();
456 params().paperheight.erase();
457 params().leftmargin.erase();
458 params().rightmargin.erase();
459 params().topmargin.erase();
460 params().bottommargin.erase();
461 params().headheight.erase();
462 params().headsep.erase();
463 params().footskip.erase();
464 params().listings_params.clear();
465 params().clearLayoutModules();
466 params().pdfoptions().clear();
468 for (int i = 0; i < 4; ++i) {
469 params().user_defined_bullet(i) = ITEMIZE_DEFAULTS[i];
470 params().temp_bullet(i) = ITEMIZE_DEFAULTS[i];
473 ErrorList & errorList = d->errorLists["Parse"];
477 string const token = lex.getString();
482 if (token == "\\end_header")
486 if (token == "\\begin_header") {
487 begin_header_line = line;
491 LYXERR(Debug::PARSER, "Handling document header token: `"
494 string unknown = params().readToken(lex, token, d->filename.onlyPath());
495 if (!unknown.empty()) {
496 if (unknown[0] != '\\' && token == "\\textclass") {
497 Alert::warning(_("Unknown document class"),
498 bformat(_("Using the default document class, because the "
499 "class %1$s is unknown."), from_utf8(unknown)));
502 docstring const s = bformat(_("Unknown token: "
506 errorList.push_back(ErrorItem(_("Document header error"),
511 if (begin_header_line) {
512 docstring const s = _("\\begin_header is missing");
513 errorList.push_back(ErrorItem(_("Document header error"),
517 return unknown_tokens;
522 // changed to be public and have one parameter
523 // Returns false if "\end_document" is not read (Asger)
524 bool Buffer::readDocument(Lexer & lex)
526 ErrorList & errorList = d->errorLists["Parse"];
530 string const token = lex.getString();
531 if (token != "\\begin_document") {
532 docstring const s = _("\\begin_document is missing");
533 errorList.push_back(ErrorItem(_("Document header error"),
537 // we are reading in a brand new document
538 BOOST_ASSERT(paragraphs().empty());
541 TextClass const & baseClass = textclasslist[params().getBaseClass()];
542 if (!baseClass.load(filePath())) {
543 string theclass = baseClass.name();
544 Alert::error(_("Can't load document class"), bformat(
545 _("Using the default document class, because the "
546 "class %1$s could not be loaded."), from_utf8(theclass)));
547 params().setBaseClass(defaultTextclass());
550 if (params().outputChanges) {
551 bool dvipost = LaTeXFeatures::isAvailable("dvipost");
552 bool xcolorsoul = LaTeXFeatures::isAvailable("soul") &&
553 LaTeXFeatures::isAvailable("xcolor");
555 if (!dvipost && !xcolorsoul) {
556 Alert::warning(_("Changes not shown in LaTeX output"),
557 _("Changes will not be highlighted in LaTeX output, "
558 "because neither dvipost nor xcolor/soul are installed.\n"
559 "Please install these packages or redefine "
560 "\\lyxadded and \\lyxdeleted in the LaTeX preamble."));
561 } else if (!xcolorsoul) {
562 Alert::warning(_("Changes not shown in LaTeX output"),
563 _("Changes will not be highlighted in LaTeX output "
564 "when using pdflatex, because xcolor and soul are not installed.\n"
565 "Please install both packages or redefine "
566 "\\lyxadded and \\lyxdeleted in the LaTeX preamble."));
571 bool const res = text().read(*this, lex, errorList);
572 for_each(text().paragraphs().begin(),
573 text().paragraphs().end(),
574 bind(&Paragraph::setInsetOwner, _1, &inset()));
580 // needed to insert the selection
581 void Buffer::insertStringAsLines(ParagraphList & pars,
582 pit_type & pit, pos_type & pos,
583 Font const & fn, docstring const & str, bool autobreakrows)
587 // insert the string, don't insert doublespace
588 bool space_inserted = true;
589 for (docstring::const_iterator cit = str.begin();
590 cit != str.end(); ++cit) {
591 Paragraph & par = pars[pit];
593 if (autobreakrows && (!par.empty() || par.allowEmpty())) {
594 breakParagraph(params(), pars, pit, pos,
595 par.layout()->isEnvironment());
598 space_inserted = true;
602 // do not insert consecutive spaces if !free_spacing
603 } else if ((*cit == ' ' || *cit == '\t') &&
604 space_inserted && !par.isFreeSpacing()) {
606 } else if (*cit == '\t') {
607 if (!par.isFreeSpacing()) {
608 // tabs are like spaces here
609 par.insertChar(pos, ' ', font, params().trackChanges);
611 space_inserted = true;
613 const pos_type n = 8 - pos % 8;
614 for (pos_type i = 0; i < n; ++i) {
615 par.insertChar(pos, ' ', font, params().trackChanges);
618 space_inserted = true;
620 } else if (!isPrintable(*cit)) {
621 // Ignore unprintables
624 // just insert the character
625 par.insertChar(pos, *cit, font, params().trackChanges);
627 space_inserted = (*cit == ' ');
634 bool Buffer::readString(std::string const & s)
636 params().compressed = false;
638 // remove dummy empty par
639 paragraphs().clear();
641 std::istringstream is(s);
643 FileName const name(tempName());
644 switch (readFile(lex, name, true)) {
648 // We need to call lyx2lyx, so write the input to a file
649 std::ofstream os(name.toFilesystemEncoding().c_str());
652 return readFile(name);
662 bool Buffer::readFile(FileName const & filename)
664 FileName fname(filename);
665 // Check if the file is compressed.
666 string format = filename.guessFormatFromContents();
667 if (format == "zip") {
668 // decompress to a temp directory
669 LYXERR(Debug::FILES, filename << " is in zip format. Unzip to " << temppath());
670 ::unzipToDir(filename.toFilesystemEncoding(), temppath());
672 FileName lyxfile(addName(temppath(), "content.lyx"));
673 // if both manifest.txt and file.lyx exist, this is am embedded file
674 if (lyxfile.exists()) {
675 params().embedded = true;
679 // The embedded lyx file can also be compressed, for backward compatibility
680 format = fname.guessFormatFromContents();
681 if (format == "gzip" || format == "zip" || format == "compress")
682 params().compressed = true;
684 // remove dummy empty par
685 paragraphs().clear();
688 if (readFile(lex, fname) != success)
695 bool Buffer::isFullyLoaded() const
697 return d->file_fully_loaded;
701 void Buffer::setFullyLoaded(bool value)
703 d->file_fully_loaded = value;
707 Buffer::ReadStatus Buffer::readFile(Lexer & lex, FileName const & filename,
710 BOOST_ASSERT(!filename.empty());
713 Alert::error(_("Document could not be read"),
714 bformat(_("%1$s could not be read."), from_utf8(filename.absFilename())));
719 string const token = lex.getString();
722 Alert::error(_("Document could not be read"),
723 bformat(_("%1$s could not be read."), from_utf8(filename.absFilename())));
727 // the first token _must_ be...
728 if (token != "\\lyxformat") {
729 lyxerr << "Token: " << token << endl;
731 Alert::error(_("Document format failure"),
732 bformat(_("%1$s is not a LyX document."),
733 from_utf8(filename.absFilename())));
738 string tmp_format = lex.getString();
739 //lyxerr << "LyX Format: `" << tmp_format << '\'' << endl;
740 // if present remove ".," from string.
741 string::size_type dot = tmp_format.find_first_of(".,");
742 //lyxerr << " dot found at " << dot << endl;
743 if (dot != string::npos)
744 tmp_format.erase(dot, 1);
745 int const file_format = convert<int>(tmp_format);
746 //lyxerr << "format: " << file_format << endl;
748 // save timestamp and checksum of the original disk file, making sure
749 // to not overwrite them with those of the file created in the tempdir
750 // when it has to be converted to the current format.
752 // Save the timestamp and checksum of disk file. If filename is an
753 // emergency file, save the timestamp and checksum of the original lyx file
754 // because isExternallyModified will check for this file. (BUG4193)
755 string diskfile = filename.absFilename();
756 if (suffixIs(diskfile, ".emergency"))
757 diskfile = diskfile.substr(0, diskfile.size() - 10);
758 saveCheckSum(FileName(diskfile));
761 if (file_format != LYX_FORMAT) {
764 // lyx2lyx would fail
767 FileName const tmpfile(tempName());
768 if (tmpfile.empty()) {
769 Alert::error(_("Conversion failed"),
770 bformat(_("%1$s is from a different"
771 " version of LyX, but a temporary"
772 " file for converting it could"
774 from_utf8(filename.absFilename())));
777 FileName const lyx2lyx = libFileSearch("lyx2lyx", "lyx2lyx");
778 if (lyx2lyx.empty()) {
779 Alert::error(_("Conversion script not found"),
780 bformat(_("%1$s is from a different"
781 " version of LyX, but the"
782 " conversion script lyx2lyx"
783 " could not be found."),
784 from_utf8(filename.absFilename())));
787 ostringstream command;
788 command << os::python()
789 << ' ' << quoteName(lyx2lyx.toFilesystemEncoding())
790 << " -t " << convert<string>(LYX_FORMAT)
791 << " -o " << quoteName(tmpfile.toFilesystemEncoding())
792 << ' ' << quoteName(filename.toFilesystemEncoding());
793 string const command_str = command.str();
795 LYXERR(Debug::INFO, "Running '" << command_str << '\'');
797 cmd_ret const ret = runCommand(command_str);
798 if (ret.first != 0) {
799 Alert::error(_("Conversion script failed"),
800 bformat(_("%1$s is from a different version"
801 " of LyX, but the lyx2lyx script"
802 " failed to convert it."),
803 from_utf8(filename.absFilename())));
806 bool const ret = readFile(tmpfile);
807 // Do stuff with tmpfile name and buffer name here.
808 return ret ? success : failure;
813 if (readDocument(lex)) {
814 Alert::error(_("Document format failure"),
815 bformat(_("%1$s ended unexpectedly, which means"
816 " that it is probably corrupted."),
817 from_utf8(filename.absFilename())));
820 d->file_fully_loaded = true;
825 // Should probably be moved to somewhere else: BufferView? LyXView?
826 bool Buffer::save() const
828 // We don't need autosaves in the immediate future. (Asger)
829 resetAutosaveTimers();
831 string const encodedFilename = d->filename.toFilesystemEncoding();
834 bool madeBackup = false;
836 // make a backup if the file already exists
837 if (lyxrc.make_backup && fileName().exists()) {
838 backupName = FileName(absFileName() + '~');
839 if (!lyxrc.backupdir_path.empty()) {
840 string const mangledName =
841 subst(subst(backupName.absFilename(), '/', '!'), ':', '!');
842 backupName = FileName(addName(lyxrc.backupdir_path,
845 if (fileName().copyTo(backupName, true)) {
848 Alert::error(_("Backup failure"),
849 bformat(_("Cannot create backup file %1$s.\n"
850 "Please check whether the directory exists and is writeable."),
851 from_utf8(backupName.absFilename())));
852 //LYXERR(Debug::DEBUG, "Fs error: " << fe.what());
856 // ask if the disk file has been externally modified (use checksum method)
857 if (fileName().exists() && isExternallyModified(checksum_method)) {
858 docstring const file = makeDisplayPath(absFileName(), 20);
859 docstring text = bformat(_("Document %1$s has been externally modified. Are you sure "
860 "you want to overwrite this file?"), file);
861 int const ret = Alert::prompt(_("Overwrite modified file?"),
862 text, 1, 1, _("&Overwrite"), _("&Cancel"));
867 if (writeFile(d->filename)) {
869 removeAutosaveFile(absFileName());
870 saveCheckSum(d->filename);
873 // Saving failed, so backup is not backup
875 rename(backupName, d->filename);
881 bool Buffer::writeFile(FileName const & fname) const
883 if (d->read_only && fname == d->filename)
889 if (params().embedded)
890 // first write the .lyx file to the temporary directory
891 content = FileName(addName(temppath(), "content.lyx"));
895 if (params().compressed) {
896 gz::ogzstream ofs(content.toFilesystemEncoding().c_str(), ios::out|ios::trunc);
902 ofstream ofs(content.toFilesystemEncoding().c_str(), ios::out|ios::trunc);
909 if (retval && params().embedded) {
910 // write file.lyx and all the embedded files to the zip file fname
911 // if embedding is enabled
912 return d->embedded_files.writeFile(fname);
918 bool Buffer::write(ostream & ofs) const
921 // Use the standard "C" locale for file output.
922 ofs.imbue(std::locale::classic());
925 // The top of the file should not be written by params().
927 // write out a comment in the top of the file
928 ofs << "#LyX " << lyx_version
929 << " created this file. For more info see http://www.lyx.org/\n"
930 << "\\lyxformat " << LYX_FORMAT << "\n"
931 << "\\begin_document\n";
934 /// For each author, set 'used' to true if there is a change
935 /// by this author in the document; otherwise set it to 'false'.
936 AuthorList::Authors::const_iterator a_it = params().authors().begin();
937 AuthorList::Authors::const_iterator a_end = params().authors().end();
938 for (; a_it != a_end; ++a_it)
939 a_it->second.setUsed(false);
941 ParIterator const end = par_iterator_end();
942 ParIterator it = par_iterator_begin();
943 for ( ; it != end; ++it)
944 it->checkAuthors(params().authors());
946 // now write out the buffer parameters.
947 ofs << "\\begin_header\n";
948 params().writeFile(ofs);
949 ofs << "\\end_header\n";
952 ofs << "\n\\begin_body\n";
953 text().write(*this, ofs);
954 ofs << "\n\\end_body\n";
956 // Write marker that shows file is complete
957 ofs << "\\end_document" << endl;
959 // Shouldn't really be needed....
962 // how to check if close went ok?
963 // Following is an attempt... (BE 20001011)
965 // good() returns false if any error occured, including some
967 // bad() returns true if something bad happened in the buffer,
968 // which should include file system full errors.
973 lyxerr << "File was not closed properly." << endl;
980 bool Buffer::makeLaTeXFile(FileName const & fname,
981 string const & original_path,
982 OutputParams const & runparams,
983 bool output_preamble, bool output_body) const
985 string const encoding = runparams.encoding->iconvName();
986 LYXERR(Debug::LATEX, "makeLaTeXFile encoding: " << encoding << "...");
988 odocfstream ofs(encoding);
989 if (!openFileWrite(ofs, fname))
992 //TexStream ts(ofs.rdbuf(), &texrow());
994 bool failed_export = false;
997 writeLaTeXSource(ofs, original_path,
998 runparams, output_preamble, output_body);
1000 catch (iconv_codecvt_facet_exception & e) {
1001 lyxerr << "Caught iconv exception: " << e.what() << endl;
1002 failed_export = true;
1004 catch (std::exception const & e) {
1005 lyxerr << "Caught \"normal\" exception: " << e.what() << endl;
1006 failed_export = true;
1009 lyxerr << "Caught some really weird exception..." << endl;
1010 LyX::cref().emergencyCleanup();
1016 failed_export = true;
1017 lyxerr << "File '" << fname << "' was not closed properly." << endl;
1020 if (failed_export) {
1021 Alert::error(_("Encoding error"),
1022 _("Some characters of your document are probably not "
1023 "representable in the chosen encoding.\n"
1024 "Changing the document encoding to utf8 could help."));
1031 void Buffer::writeLaTeXSource(odocstream & os,
1032 string const & original_path,
1033 OutputParams const & runparams_in,
1034 bool const output_preamble, bool const output_body) const
1036 OutputParams runparams = runparams_in;
1038 // validate the buffer.
1039 LYXERR(Debug::LATEX, " Validating buffer...");
1040 LaTeXFeatures features(*this, params(), runparams);
1042 LYXERR(Debug::LATEX, " Buffer validation done.");
1044 // The starting paragraph of the coming rows is the
1045 // first paragraph of the document. (Asger)
1046 if (output_preamble && runparams.nice) {
1047 os << "%% LyX " << lyx_version << " created this file. "
1048 "For more info, see http://www.lyx.org/.\n"
1049 "%% Do not edit unless you really know what "
1051 d->texrow.newline();
1052 d->texrow.newline();
1054 LYXERR(Debug::INFO, "lyx document header finished");
1055 // There are a few differences between nice LaTeX and usual files:
1056 // usual is \batchmode and has a
1057 // special input@path to allow the including of figures
1058 // with either \input or \includegraphics (what figinsets do).
1059 // input@path is set when the actual parameter
1060 // original_path is set. This is done for usual tex-file, but not
1061 // for nice-latex-file. (Matthias 250696)
1062 // Note that input@path is only needed for something the user does
1063 // in the preamble, included .tex files or ERT, files included by
1064 // LyX work without it.
1065 if (output_preamble) {
1066 if (!runparams.nice) {
1067 // code for usual, NOT nice-latex-file
1068 os << "\\batchmode\n"; // changed
1069 // from \nonstopmode
1070 d->texrow.newline();
1072 if (!original_path.empty()) {
1074 // We don't know the encoding of inputpath
1075 docstring const inputpath = from_utf8(latex_path(original_path));
1076 os << "\\makeatletter\n"
1077 << "\\def\\input@path{{"
1078 << inputpath << "/}}\n"
1079 << "\\makeatother\n";
1080 d->texrow.newline();
1081 d->texrow.newline();
1082 d->texrow.newline();
1085 // Write the preamble
1086 runparams.use_babel = params().writeLaTeX(os, features, d->texrow);
1092 os << "\\begin{document}\n";
1093 d->texrow.newline();
1094 } // output_preamble
1096 d->texrow.start(paragraphs().begin()->id(), 0);
1098 LYXERR(Debug::INFO, "preamble finished, now the body.");
1100 if (!lyxrc.language_auto_begin &&
1101 !params().language->babel().empty()) {
1103 os << from_utf8(subst(lyxrc.language_command_begin,
1105 params().language->babel()))
1107 d->texrow.newline();
1110 Encoding const & encoding = params().encoding();
1111 if (encoding.package() == Encoding::CJK) {
1112 // Open a CJK environment, since in contrast to the encodings
1113 // handled by inputenc the document encoding is not set in
1114 // the preamble if it is handled by CJK.sty.
1115 os << "\\begin{CJK}{" << from_ascii(encoding.latexName())
1117 d->texrow.newline();
1120 // if we are doing a real file with body, even if this is the
1121 // child of some other buffer, let's cut the link here.
1122 // This happens for example if only a child document is printed.
1123 Buffer const * save_parent = 0;
1124 if (output_preamble) {
1125 save_parent = d->parent_buffer;
1126 d->parent_buffer = 0;
1129 loadChildDocuments();
1132 latexParagraphs(*this, paragraphs(), os, d->texrow, runparams);
1134 // Restore the parenthood if needed
1135 if (output_preamble)
1136 d->parent_buffer = save_parent;
1138 // add this just in case after all the paragraphs
1140 d->texrow.newline();
1142 if (encoding.package() == Encoding::CJK) {
1143 // Close the open CJK environment.
1144 // latexParagraphs will have opened one even if the last text
1146 os << "\\end{CJK}\n";
1147 d->texrow.newline();
1150 if (!lyxrc.language_auto_end &&
1151 !params().language->babel().empty()) {
1152 os << from_utf8(subst(lyxrc.language_command_end,
1154 params().language->babel()))
1156 d->texrow.newline();
1159 if (output_preamble) {
1160 os << "\\end{document}\n";
1161 d->texrow.newline();
1162 LYXERR(Debug::LATEX, "makeLaTeXFile...done");
1164 LYXERR(Debug::LATEX, "LaTeXFile for inclusion made.");
1166 runparams_in.encoding = runparams.encoding;
1168 // Just to be sure. (Asger)
1169 d->texrow.newline();
1171 LYXERR(Debug::INFO, "Finished making LaTeX file.");
1172 LYXERR(Debug::INFO, "Row count was " << d->texrow.rows() - 1 << '.');
1176 bool Buffer::isLatex() const
1178 return params().getTextClass().outputType() == LATEX;
1182 bool Buffer::isLiterate() const
1184 return params().getTextClass().outputType() == LITERATE;
1188 bool Buffer::isDocBook() const
1190 return params().getTextClass().outputType() == DOCBOOK;
1194 void Buffer::makeDocBookFile(FileName const & fname,
1195 OutputParams const & runparams,
1196 bool const body_only) const
1198 LYXERR(Debug::LATEX, "makeDocBookFile...");
1202 if (!openFileWrite(ofs, fname))
1205 writeDocBookSource(ofs, fname.absFilename(), runparams, body_only);
1209 lyxerr << "File '" << fname << "' was not closed properly." << endl;
1213 void Buffer::writeDocBookSource(odocstream & os, string const & fname,
1214 OutputParams const & runparams,
1215 bool const only_body) const
1217 LaTeXFeatures features(*this, params(), runparams);
1222 TextClass const & tclass = params().getTextClass();
1223 string const top_element = tclass.latexname();
1226 if (runparams.flavor == OutputParams::XML)
1227 os << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
1230 os << "<!DOCTYPE " << from_ascii(top_element) << ' ';
1233 if (! tclass.class_header().empty())
1234 os << from_ascii(tclass.class_header());
1235 else if (runparams.flavor == OutputParams::XML)
1236 os << "PUBLIC \"-//OASIS//DTD DocBook XML//EN\" "
1237 << "\"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd\"";
1239 os << " PUBLIC \"-//OASIS//DTD DocBook V4.2//EN\"";
1241 docstring preamble = from_utf8(params().preamble);
1242 if (runparams.flavor != OutputParams::XML ) {
1243 preamble += "<!ENTITY % output.print.png \"IGNORE\">\n";
1244 preamble += "<!ENTITY % output.print.pdf \"IGNORE\">\n";
1245 preamble += "<!ENTITY % output.print.eps \"IGNORE\">\n";
1246 preamble += "<!ENTITY % output.print.bmp \"IGNORE\">\n";
1249 string const name = runparams.nice
1250 ? changeExtension(absFileName(), ".sgml") : fname;
1251 preamble += features.getIncludedFiles(name);
1252 preamble += features.getLyXSGMLEntities();
1254 if (!preamble.empty()) {
1255 os << "\n [ " << preamble << " ]";
1260 string top = top_element;
1262 if (runparams.flavor == OutputParams::XML)
1263 top += params().language->code();
1265 top += params().language->code().substr(0,2);
1268 if (!params().options.empty()) {
1270 top += params().options;
1273 os << "<!-- " << ((runparams.flavor == OutputParams::XML)? "XML" : "SGML")
1274 << " file was created by LyX " << lyx_version
1275 << "\n See http://www.lyx.org/ for more information -->\n";
1277 params().getTextClass().counters().reset();
1279 loadChildDocuments();
1281 sgml::openTag(os, top);
1283 docbookParagraphs(paragraphs(), *this, os, runparams);
1284 sgml::closeTag(os, top_element);
1288 // chktex should be run with these flags disabled: 3, 22, 25, 30, 38(?)
1289 // Other flags: -wall -v0 -x
1290 int Buffer::runChktex()
1294 // get LaTeX-Filename
1295 FileName const path(temppath());
1296 string const name = addName(path.absFilename(), latexName());
1297 string const org_path = filePath();
1299 support::PathChanger p(path); // path to LaTeX file
1300 message(_("Running chktex..."));
1302 // Generate the LaTeX file if neccessary
1303 OutputParams runparams(¶ms().encoding());
1304 runparams.flavor = OutputParams::LATEX;
1305 runparams.nice = false;
1306 makeLaTeXFile(FileName(name), org_path, runparams);
1309 Chktex chktex(lyxrc.chktex_command, onlyFilename(name), filePath());
1310 int const res = chktex.run(terr); // run chktex
1313 Alert::error(_("chktex failure"),
1314 _("Could not run chktex successfully."));
1315 } else if (res > 0) {
1316 ErrorList & errlist = d->errorLists["ChkTeX"];
1318 bufferErrors(terr, errlist);
1329 void Buffer::validate(LaTeXFeatures & features) const
1331 TextClass const & tclass = params().getTextClass();
1333 if (params().outputChanges) {
1334 bool dvipost = LaTeXFeatures::isAvailable("dvipost");
1335 bool xcolorsoul = LaTeXFeatures::isAvailable("soul") &&
1336 LaTeXFeatures::isAvailable("xcolor");
1338 if (features.runparams().flavor == OutputParams::LATEX) {
1340 features.require("ct-dvipost");
1341 features.require("dvipost");
1342 } else if (xcolorsoul) {
1343 features.require("ct-xcolor-soul");
1344 features.require("soul");
1345 features.require("xcolor");
1347 features.require("ct-none");
1349 } else if (features.runparams().flavor == OutputParams::PDFLATEX ) {
1351 features.require("ct-xcolor-soul");
1352 features.require("soul");
1353 features.require("xcolor");
1354 features.require("pdfcolmk"); // improves color handling in PDF output
1356 features.require("ct-none");
1361 // AMS Style is at document level
1362 if (params().use_amsmath == BufferParams::package_on
1363 || tclass.provides("amsmath"))
1364 features.require("amsmath");
1365 if (params().use_esint == BufferParams::package_on)
1366 features.require("esint");
1368 loadChildDocuments();
1370 for_each(paragraphs().begin(), paragraphs().end(),
1371 boost::bind(&Paragraph::validate, _1, boost::ref(features)));
1373 // the bullet shapes are buffer level not paragraph level
1374 // so they are tested here
1375 for (int i = 0; i < 4; ++i) {
1376 if (params().user_defined_bullet(i) != ITEMIZE_DEFAULTS[i]) {
1377 int const font = params().user_defined_bullet(i).getFont();
1379 int const c = params()
1380 .user_defined_bullet(i)
1387 features.require("latexsym");
1389 } else if (font == 1) {
1390 features.require("amssymb");
1391 } else if ((font >= 2 && font <= 5)) {
1392 features.require("pifont");
1397 if (lyxerr.debugging(Debug::LATEX)) {
1398 features.showStruct();
1403 void Buffer::getLabelList(vector<docstring> & list) const
1405 /// if this is a child document and the parent is already loaded
1406 /// Use the parent's list instead [ale990407]
1407 Buffer const * tmp = masterBuffer();
1409 lyxerr << "masterBuffer() failed!" << endl;
1413 tmp->getLabelList(list);
1417 loadChildDocuments();
1419 for (InsetIterator it = inset_iterator_begin(inset()); it; ++it)
1420 it.nextInset()->getLabelList(*this, list);
1424 void Buffer::updateBibfilesCache() const
1426 // if this is a child document and the parent is already loaded
1427 // update the parent's cache instead
1428 Buffer const * tmp = masterBuffer();
1431 tmp->updateBibfilesCache();
1435 bibfilesCache_.clear();
1436 for (InsetIterator it = inset_iterator_begin(inset()); it; ++it) {
1437 if (it->lyxCode() == BIBTEX_CODE) {
1438 InsetBibtex const & inset =
1439 static_cast<InsetBibtex const &>(*it);
1440 vector<FileName> const bibfiles = inset.getFiles(*this);
1441 bibfilesCache_.insert(bibfilesCache_.end(),
1444 } else if (it->lyxCode() == INCLUDE_CODE) {
1445 InsetInclude & inset =
1446 static_cast<InsetInclude &>(*it);
1447 inset.updateBibfilesCache(*this);
1448 vector<FileName> const & bibfiles =
1449 inset.getBibfilesCache(*this);
1450 bibfilesCache_.insert(bibfilesCache_.end(),
1458 vector<FileName> const & Buffer::getBibfilesCache() const
1460 // if this is a child document and the parent is already loaded
1461 // use the parent's cache instead
1462 Buffer const * tmp = masterBuffer();
1465 return tmp->getBibfilesCache();
1467 // We update the cache when first used instead of at loading time.
1468 if (bibfilesCache_.empty())
1469 const_cast<Buffer *>(this)->updateBibfilesCache();
1471 return bibfilesCache_;
1475 bool Buffer::isDepClean(string const & name) const
1477 DepClean::const_iterator const it = d->dep_clean.find(name);
1478 if (it == d->dep_clean.end())
1484 void Buffer::markDepClean(string const & name)
1486 d->dep_clean[name] = true;
1490 bool Buffer::dispatch(string const & command, bool * result)
1492 return dispatch(lyxaction.lookupFunc(command), result);
1496 bool Buffer::dispatch(FuncRequest const & func, bool * result)
1498 bool dispatched = true;
1500 switch (func.action) {
1501 case LFUN_BUFFER_EXPORT: {
1502 bool const tmp = doExport(to_utf8(func.argument()), false);
1515 void Buffer::changeLanguage(Language const * from, Language const * to)
1520 for_each(par_iterator_begin(),
1522 bind(&Paragraph::changeLanguage, _1, params(), from, to));
1526 bool Buffer::isMultiLingual() const
1528 ParConstIterator end = par_iterator_end();
1529 for (ParConstIterator it = par_iterator_begin(); it != end; ++it)
1530 if (it->isMultiLingual(params()))
1537 ParIterator Buffer::getParFromID(int const id) const
1539 ParConstIterator it = par_iterator_begin();
1540 ParConstIterator const end = par_iterator_end();
1543 // John says this is called with id == -1 from undo
1544 lyxerr << "getParFromID(), id: " << id << endl;
1548 for (; it != end; ++it)
1556 bool Buffer::hasParWithID(int const id) const
1558 ParConstIterator const it = getParFromID(id);
1559 return it != par_iterator_end();
1563 ParIterator Buffer::par_iterator_begin()
1565 return lyx::par_iterator_begin(inset());
1569 ParIterator Buffer::par_iterator_end()
1571 return lyx::par_iterator_end(inset());
1575 ParConstIterator Buffer::par_iterator_begin() const
1577 return lyx::par_const_iterator_begin(inset());
1581 ParConstIterator Buffer::par_iterator_end() const
1583 return lyx::par_const_iterator_end(inset());
1587 Language const * Buffer::language() const
1589 return params().language;
1593 docstring const Buffer::B_(string const & l10n) const
1595 return params().B_(l10n);
1599 bool Buffer::isClean() const
1601 return d->lyx_clean;
1605 bool Buffer::isBakClean() const
1607 return d->bak_clean;
1611 bool Buffer::isExternallyModified(CheckMethod method) const
1613 BOOST_ASSERT(d->filename.exists());
1614 // if method == timestamp, check timestamp before checksum
1615 return (method == checksum_method
1616 || d->timestamp_ != d->filename.lastModified())
1617 && d->checksum_ != d->filename.checksum();
1621 void Buffer::saveCheckSum(FileName const & file) const
1623 if (file.exists()) {
1624 d->timestamp_ = file.lastModified();
1625 d->checksum_ = file.checksum();
1627 // in the case of save to a new file.
1634 void Buffer::markClean() const
1636 if (!d->lyx_clean) {
1637 d->lyx_clean = true;
1640 // if the .lyx file has been saved, we don't need an
1642 d->bak_clean = true;
1646 void Buffer::markBakClean() const
1648 d->bak_clean = true;
1652 void Buffer::setUnnamed(bool flag)
1658 bool Buffer::isUnnamed() const
1664 // FIXME: this function should be moved to buffer_pimpl.C
1665 void Buffer::markDirty()
1668 d->lyx_clean = false;
1671 d->bak_clean = false;
1673 DepClean::iterator it = d->dep_clean.begin();
1674 DepClean::const_iterator const end = d->dep_clean.end();
1676 for (; it != end; ++it)
1681 FileName Buffer::fileName() const
1687 string Buffer::absFileName() const
1689 return d->filename.absFilename();
1693 string Buffer::filePath() const
1695 return d->filename.onlyPath().absFilename();
1699 bool Buffer::isReadonly() const
1701 return d->read_only;
1705 void Buffer::setParent(Buffer const * buffer)
1707 // Avoids recursive include.
1708 d->parent_buffer = buffer == this ? 0 : buffer;
1712 Buffer const * Buffer::parent()
1714 return d->parent_buffer;
1718 Buffer const * Buffer::masterBuffer() const
1720 if (!d->parent_buffer)
1723 return d->parent_buffer->masterBuffer();
1727 bool Buffer::hasMacro(docstring const & name, Paragraph const & par) const
1729 Impl::PositionToMacroMap::iterator it;
1730 it = d->macros[name].upper_bound(par.macrocontextPosition());
1731 if (it != d->macros[name].end())
1734 // If there is a master buffer, query that
1735 Buffer const * master = masterBuffer();
1736 if (master && master != this)
1737 return master->hasMacro(name);
1739 return MacroTable::globalMacros().has(name);
1743 bool Buffer::hasMacro(docstring const & name) const
1745 if( !d->macros[name].empty() )
1748 // If there is a master buffer, query that
1749 Buffer const * master = masterBuffer();
1750 if (master && master != this)
1751 return master->hasMacro(name);
1753 return MacroTable::globalMacros().has(name);
1757 MacroData const & Buffer::getMacro(docstring const & name,
1758 Paragraph const & par) const
1760 Impl::PositionToMacroMap::iterator it;
1761 it = d->macros[name].upper_bound(par.macrocontextPosition());
1762 if( it != d->macros[name].end() )
1765 // If there is a master buffer, query that
1766 Buffer const * master = masterBuffer();
1767 if (master && master != this)
1768 return master->getMacro(name);
1770 return MacroTable::globalMacros().get(name);
1774 MacroData const & Buffer::getMacro(docstring const & name) const
1776 Impl::PositionToMacroMap::iterator it;
1777 it = d->macros[name].begin();
1778 if( it != d->macros[name].end() )
1781 // If there is a master buffer, query that
1782 Buffer const * master = masterBuffer();
1783 if (master && master != this)
1784 return master->getMacro(name);
1786 return MacroTable::globalMacros().get(name);
1790 void Buffer::updateMacros()
1792 // start with empty table
1793 d->macros = Impl::NameToPositionMacroMap();
1795 // Iterate over buffer
1796 ParagraphList & pars = text().paragraphs();
1797 for (size_t i = 0, n = pars.size(); i != n; ++i) {
1798 // set position again
1799 pars[i].setMacrocontextPosition(i);
1801 //lyxerr << "searching main par " << i
1802 // << " for macro definitions" << std::endl;
1803 InsetList const & insets = pars[i].insetList();
1804 InsetList::const_iterator it = insets.begin();
1805 InsetList::const_iterator end = insets.end();
1806 for ( ; it != end; ++it) {
1807 if (it->inset->lyxCode() != MATHMACRO_CODE)
1811 MathMacroTemplate const & macroTemplate
1812 = static_cast<MathMacroTemplate const &>(*it->inset);
1815 if (macroTemplate.validMacro()) {
1816 MacroData macro = macroTemplate.asMacroData();
1819 // call hasMacro here instead of directly querying mc to
1820 // also take the master document into consideration
1821 macro.setRedefinition(hasMacro(macroTemplate.name()));
1823 // register macro (possibly overwrite the previous one of this paragraph)
1824 d->macros[macroTemplate.name()][i] = macro;
1831 void Buffer::changeRefsIfUnique(docstring const & from, docstring const & to,
1834 //FIXME: This does not work for child documents yet.
1835 BOOST_ASSERT(code == CITE_CODE || code == REF_CODE);
1836 // Check if the label 'from' appears more than once
1837 vector<docstring> labels;
1840 if (code == CITE_CODE) {
1842 keys.fillWithBibKeys(this);
1843 BiblioInfo::const_iterator bit = keys.begin();
1844 BiblioInfo::const_iterator bend = keys.end();
1846 for (; bit != bend; ++bit)
1848 labels.push_back(bit->first);
1851 getLabelList(labels);
1852 paramName = "reference";
1855 if (std::count(labels.begin(), labels.end(), from) > 1)
1858 for (InsetIterator it = inset_iterator_begin(inset()); it; ++it) {
1859 if (it->lyxCode() == code) {
1860 InsetCommand & inset = static_cast<InsetCommand &>(*it);
1861 docstring const oldValue = inset.getParam(paramName);
1862 if (oldValue == from)
1863 inset.setParam(paramName, to);
1869 void Buffer::getSourceCode(odocstream & os, pit_type par_begin,
1870 pit_type par_end, bool full_source)
1872 OutputParams runparams(¶ms().encoding());
1873 runparams.nice = true;
1874 runparams.flavor = OutputParams::LATEX;
1875 runparams.linelen = lyxrc.plaintext_linelen;
1876 // No side effect of file copying and image conversion
1877 runparams.dryrun = true;
1881 os << "% " << _("Preview source code") << "\n\n";
1882 d->texrow.newline();
1883 d->texrow.newline();
1885 writeLaTeXSource(os, filePath(), runparams, true, true);
1887 writeDocBookSource(os, absFileName(), runparams, false);
1890 runparams.par_begin = par_begin;
1891 runparams.par_end = par_end;
1892 if (par_begin + 1 == par_end)
1894 << bformat(_("Preview source code for paragraph %1$d"), par_begin)
1898 << bformat(_("Preview source code from paragraph %1$s to %2$s"),
1899 convert<docstring>(par_begin),
1900 convert<docstring>(par_end - 1))
1902 d->texrow.newline();
1903 d->texrow.newline();
1904 // output paragraphs
1906 latexParagraphs(*this, paragraphs(), os, d->texrow, runparams);
1909 docbookParagraphs(paragraphs(), *this, os, runparams);
1915 ErrorList & Buffer::errorList(string const & type) const
1917 static ErrorList emptyErrorList;
1918 std::map<string, ErrorList>::iterator I = d->errorLists.find(type);
1919 if (I == d->errorLists.end())
1920 return emptyErrorList;
1926 void Buffer::structureChanged() const
1929 gui_->structureChanged();
1933 void Buffer::errors(std::string const & err) const
1940 void Buffer::message(docstring const & msg) const
1947 void Buffer::setBusy(bool on) const
1954 void Buffer::setReadOnly(bool on) const
1957 d->wa_->setReadOnly(on);
1961 void Buffer::updateTitles() const
1964 d->wa_->updateTitles();
1968 void Buffer::resetAutosaveTimers() const
1971 gui_->resetAutosaveTimers();
1975 void Buffer::setGuiDelegate(frontend::GuiBufferDelegate * gui)
1984 class AutoSaveBuffer : public support::ForkedProcess {
1987 AutoSaveBuffer(Buffer const & buffer, FileName const & fname)
1988 : buffer_(buffer), fname_(fname) {}
1990 virtual boost::shared_ptr<ForkedProcess> clone() const
1992 return boost::shared_ptr<ForkedProcess>(new AutoSaveBuffer(*this));
1997 command_ = to_utf8(bformat(_("Auto-saving %1$s"),
1998 from_utf8(fname_.absFilename())));
1999 return run(DontWait);
2003 virtual int generateChild();
2005 Buffer const & buffer_;
2010 #if !defined (HAVE_FORK)
2014 int AutoSaveBuffer::generateChild()
2016 // tmp_ret will be located (usually) in /tmp
2017 // will that be a problem?
2018 pid_t const pid = fork();
2019 // If you want to debug the autosave
2020 // you should set pid to -1, and comment out the fork.
2021 if (pid == 0 || pid == -1) {
2022 // pid = -1 signifies that lyx was unable
2023 // to fork. But we will do the save
2025 bool failed = false;
2027 FileName const tmp_ret(tempName(FileName(), "lyxauto"));
2028 if (!tmp_ret.empty()) {
2029 buffer_.writeFile(tmp_ret);
2030 // assume successful write of tmp_ret
2031 if (!rename(tmp_ret, fname_)) {
2033 // most likely couldn't move between
2034 // filesystems unless write of tmp_ret
2035 // failed so remove tmp file (if it
2037 tmp_ret.removeFile();
2044 // failed to write/rename tmp_ret so try writing direct
2045 if (!buffer_.writeFile(fname_)) {
2046 // It is dangerous to do this in the child,
2047 // but safe in the parent, so...
2048 if (pid == -1) // emit message signal.
2049 buffer_.message(_("Autosave failed!"));
2052 if (pid == 0) { // we are the child so...
2062 // Perfect target for a thread...
2063 void Buffer::autoSave() const
2065 if (isBakClean() || isReadonly()) {
2066 // We don't save now, but we'll try again later
2067 resetAutosaveTimers();
2071 // emit message signal.
2072 message(_("Autosaving current document..."));
2074 // create autosave filename
2075 string fname = filePath();
2077 fname += onlyFilename(absFileName());
2080 AutoSaveBuffer autosave(*this, FileName(fname));
2084 resetAutosaveTimers();
2088 /** Write a buffer to a new file name and rename the buffer
2089 according to the new file name.
2091 This function is e.g. used by menu callbacks and
2092 LFUN_BUFFER_WRITE_AS.
2094 If 'newname' is empty (the default), the user is asked via a
2095 dialog for the buffer's new name and location.
2097 If 'newname' is non-empty and has an absolute path, that is used.
2098 Otherwise the base directory of the buffer is used as the base
2099 for any relative path in 'newname'.
2102 bool Buffer::writeAs(string const & newname)
2104 string fname = absFileName();
2105 string const oldname = fname;
2107 if (newname.empty()) { /// No argument? Ask user through dialog
2110 FileDialog dlg(_("Choose a filename to save document as"),
2111 LFUN_BUFFER_WRITE_AS);
2112 dlg.setButton1(_("Documents|#o#O"), from_utf8(lyxrc.document_path));
2113 dlg.setButton2(_("Templates|#T#t"), from_utf8(lyxrc.template_path));
2115 if (!support::isLyXFilename(fname))
2118 support::FileFilterList const filter(_("LyX Documents (*.lyx)"));
2120 FileDialog::Result result =
2121 dlg.save(from_utf8(onlyPath(fname)),
2123 from_utf8(onlyFilename(fname)));
2125 if (result.first == FileDialog::Later)
2128 fname = to_utf8(result.second);
2133 // Make sure the absolute filename ends with appropriate suffix
2134 fname = makeAbsPath(fname).absFilename();
2135 if (!support::isLyXFilename(fname))
2139 fname = makeAbsPath(newname, onlyPath(oldname)).absFilename();
2141 if (FileName(fname).exists()) {
2142 docstring const file = makeDisplayPath(fname, 30);
2143 docstring text = bformat(_("The document %1$s already "
2144 "exists.\n\nDo you want to "
2145 "overwrite that document?"),
2147 int const ret = Alert::prompt(_("Overwrite document?"),
2148 text, 0, 1, _("&Overwrite"), _("&Cancel"));
2154 // Ok, change the name of the buffer
2157 bool unnamed = isUnnamed();
2159 saveCheckSum(FileName(fname));
2162 setFileName(oldname);
2163 setUnnamed(unnamed);
2164 saveCheckSum(FileName(oldname));
2168 removeAutosaveFile(oldname);
2173 bool Buffer::menuWrite()
2176 LyX::ref().session().lastFiles().add(FileName(absFileName()));
2180 // FIXME: we don't tell the user *WHY* the save failed !!
2182 docstring const file = makeDisplayPath(absFileName(), 30);
2184 docstring text = bformat(_("The document %1$s could not be saved.\n\n"
2185 "Do you want to rename the document and "
2186 "try again?"), file);
2187 int const ret = Alert::prompt(_("Rename and save?"),
2188 text, 0, 1, _("&Rename"), _("&Cancel"));
2197 void Buffer::resetChildDocuments(bool close_them) const
2199 for (InsetIterator it = inset_iterator_begin(inset()); it; ++it) {
2200 if (it->lyxCode() != INCLUDE_CODE)
2202 InsetCommand const & inset = static_cast<InsetCommand const &>(*it);
2203 InsetCommandParams const & ip = inset.params();
2205 resetParentBuffer(this, ip, close_them);
2208 if (use_gui && masterBuffer() == this)
2209 updateLabels(*this);
2213 void Buffer::loadChildDocuments() const
2215 bool parse_error = false;
2217 for (InsetIterator it = inset_iterator_begin(inset()); it; ++it) {
2218 if (it->lyxCode() != INCLUDE_CODE)
2220 InsetCommand const & inset = static_cast<InsetCommand const &>(*it);
2221 InsetCommandParams const & ip = inset.params();
2222 Buffer * child = loadIfNeeded(*this, ip);
2225 parse_error |= !child->errorList("Parse").empty();
2226 child->loadChildDocuments();
2229 if (use_gui && masterBuffer() == this)
2230 updateLabels(*this);
2234 string Buffer::bufferFormat() const
2244 bool Buffer::doExport(string const & format, bool put_in_tempdir,
2245 string & result_file) const
2247 string backend_format;
2248 OutputParams runparams(¶ms().encoding());
2249 runparams.flavor = OutputParams::LATEX;
2250 runparams.linelen = lyxrc.plaintext_linelen;
2251 vector<string> backs = backends();
2252 if (find(backs.begin(), backs.end(), format) == backs.end()) {
2253 // Get shortest path to format
2254 Graph::EdgePath path;
2255 for (vector<string>::const_iterator it = backs.begin();
2256 it != backs.end(); ++it) {
2257 Graph::EdgePath p = theConverters().getPath(*it, format);
2258 if (!p.empty() && (path.empty() || p.size() < path.size())) {
2259 backend_format = *it;
2264 runparams.flavor = theConverters().getFlavor(path);
2266 Alert::error(_("Couldn't export file"),
2267 bformat(_("No information for exporting the format %1$s."),
2268 formats.prettyName(format)));
2272 backend_format = format;
2273 // FIXME: Don't hardcode format names here, but use a flag
2274 if (backend_format == "pdflatex")
2275 runparams.flavor = OutputParams::PDFLATEX;
2278 string filename = latexName(false);
2279 filename = addName(temppath(), filename);
2280 filename = changeExtension(filename,
2281 formats.extension(backend_format));
2283 // Plain text backend
2284 if (backend_format == "text")
2285 writePlaintextFile(*this, FileName(filename), runparams);
2287 else if (backend_format == "lyx")
2288 writeFile(FileName(filename));
2290 else if (isDocBook()) {
2291 runparams.nice = !put_in_tempdir;
2292 makeDocBookFile(FileName(filename), runparams);
2295 else if (backend_format == format) {
2296 runparams.nice = true;
2297 if (!makeLaTeXFile(FileName(filename), string(), runparams))
2299 } else if (!lyxrc.tex_allows_spaces
2300 && support::contains(filePath(), ' ')) {
2301 Alert::error(_("File name error"),
2302 _("The directory path to the document cannot contain spaces."));
2305 runparams.nice = false;
2306 if (!makeLaTeXFile(FileName(filename), filePath(), runparams))
2310 string const error_type = (format == "program")
2311 ? "Build" : bufferFormat();
2312 string const ext = formats.extension(format);
2313 FileName const tmp_result_file(changeExtension(filename, ext));
2314 bool const success = theConverters().convert(this, FileName(filename),
2315 tmp_result_file, FileName(absFileName()), backend_format, format,
2316 errorList(error_type));
2317 // Emit the signal to show the error list.
2318 if (format != backend_format)
2324 result_file = tmp_result_file.absFilename();
2326 result_file = changeExtension(absFileName(), ext);
2327 // We need to copy referenced files (e. g. included graphics
2328 // if format == "dvi") to the result dir.
2329 vector<ExportedFile> const files =
2330 runparams.exportdata->externalFiles(format);
2331 string const dest = onlyPath(result_file);
2332 CopyStatus status = SUCCESS;
2333 for (vector<ExportedFile>::const_iterator it = files.begin();
2334 it != files.end() && status != CANCEL; ++it) {
2336 formats.getFormatFromFile(it->sourceName);
2337 status = copyFile(fmt, it->sourceName,
2338 makeAbsPath(it->exportName, dest),
2339 it->exportName, status == FORCE);
2341 if (status == CANCEL) {
2342 message(_("Document export cancelled."));
2343 } else if (tmp_result_file.exists()) {
2344 // Finally copy the main file
2345 status = copyFile(format, tmp_result_file,
2346 FileName(result_file), result_file,
2348 message(bformat(_("Document exported as %1$s "
2350 formats.prettyName(format),
2351 makeDisplayPath(result_file)));
2353 // This must be a dummy converter like fax (bug 1888)
2354 message(bformat(_("Document exported as %1$s"),
2355 formats.prettyName(format)));
2363 bool Buffer::doExport(string const & format, bool put_in_tempdir) const
2366 return doExport(format, put_in_tempdir, result_file);
2370 bool Buffer::preview(string const & format) const
2373 if (!doExport(format, true, result_file))
2375 return formats.view(*this, FileName(result_file), format);
2379 bool Buffer::isExportable(string const & format) const
2381 vector<string> backs = backends();
2382 for (vector<string>::const_iterator it = backs.begin();
2383 it != backs.end(); ++it)
2384 if (theConverters().isReachable(*it, format))
2390 vector<Format const *> Buffer::exportableFormats(bool only_viewable) const
2392 vector<string> backs = backends();
2393 vector<Format const *> result =
2394 theConverters().getReachable(backs[0], only_viewable, true);
2395 for (vector<string>::const_iterator it = backs.begin() + 1;
2396 it != backs.end(); ++it) {
2397 vector<Format const *> r =
2398 theConverters().getReachable(*it, only_viewable, false);
2399 result.insert(result.end(), r.begin(), r.end());
2405 vector<string> Buffer::backends() const
2408 if (params().getTextClass().isTeXClassAvailable()) {
2409 v.push_back(bufferFormat());
2410 // FIXME: Don't hardcode format names here, but use a flag
2411 if (v.back() == "latex")
2412 v.push_back("pdflatex");
2414 v.push_back("text");
2420 bool Buffer::readFileHelper(FileName const & s)
2422 // File information about normal file
2424 docstring const file = makeDisplayPath(s.absFilename(), 50);
2425 docstring text = bformat(_("The specified document\n%1$s"
2426 "\ncould not be read."), file);
2427 Alert::error(_("Could not read document"), text);
2431 // Check if emergency save file exists and is newer.
2432 FileName const e(s.absFilename() + ".emergency");
2434 if (e.exists() && s.exists() && e.lastModified() > s.lastModified()) {
2435 docstring const file = makeDisplayPath(s.absFilename(), 20);
2436 docstring const text =
2437 bformat(_("An emergency save of the document "
2439 "Recover emergency save?"), file);
2440 switch (Alert::prompt(_("Load emergency save?"), text, 0, 2,
2441 _("&Recover"), _("&Load Original"),
2445 // the file is not saved if we load the emergency file.
2455 // Now check if autosave file is newer.
2456 FileName const a(onlyPath(s.absFilename()) + '#' + onlyFilename(s.absFilename()) + '#');
2458 if (a.exists() && s.exists() && a.lastModified() > s.lastModified()) {
2459 docstring const file = makeDisplayPath(s.absFilename(), 20);
2460 docstring const text =
2461 bformat(_("The backup of the document "
2462 "%1$s is newer.\n\nLoad the "
2463 "backup instead?"), file);
2464 switch (Alert::prompt(_("Load backup?"), text, 0, 2,
2465 _("&Load backup"), _("Load &original"),
2469 // the file is not saved if we load the autosave file.
2473 // Here we delete the autosave
2484 bool Buffer::loadLyXFile(FileName const & s)
2486 if (s.isReadableFile()) {
2487 if (readFileHelper(s)) {
2488 lyxvc().file_found_hook(s);
2489 if (!s.isWritable())
2494 docstring const file = makeDisplayPath(s.absFilename(), 20);
2495 // Here we probably should run
2496 if (LyXVC::file_not_found_hook(s)) {
2497 docstring const text =
2498 bformat(_("Do you want to retrieve the document"
2499 " %1$s from version control?"), file);
2500 int const ret = Alert::prompt(_("Retrieve from version control?"),
2501 text, 0, 1, _("&Retrieve"), _("&Cancel"));
2504 // How can we know _how_ to do the checkout?
2505 // With the current VC support it has to be,
2506 // a RCS file since CVS do not have special ,v files.
2508 return loadLyXFile(s);
2516 void Buffer::bufferErrors(TeXErrors const & terr, ErrorList & errorList) const
2518 TeXErrors::Errors::const_iterator cit = terr.begin();
2519 TeXErrors::Errors::const_iterator end = terr.end();
2521 for (; cit != end; ++cit) {
2524 int errorRow = cit->error_in_line;
2525 bool found = d->texrow.getIdFromRow(errorRow, id_start,
2531 found = d->texrow.getIdFromRow(errorRow, id_end, pos_end);
2532 } while (found && id_start == id_end && pos_start == pos_end);
2534 errorList.push_back(ErrorItem(cit->error_desc,
2535 cit->error_text, id_start, pos_start, pos_end));