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 // GuiView already destroyed
270 Buffer const * master = masterBuffer();
271 if (master != this && use_gui)
272 // We are closing buf which was a child document so we
273 // must update the labels and section numbering of its master
275 updateLabels(*master);
277 resetChildDocuments(false);
279 if (!temppath().empty() && !FileName(temppath()).destroyDirectory()) {
280 Alert::warning(_("Could not remove temporary directory"),
281 bformat(_("Could not remove the temporary directory %1$s"),
282 from_utf8(temppath())));
285 // Remove any previewed LaTeX snippets associated with this buffer.
286 graphics::Previews::get().removeLoader(*this);
292 void Buffer::changed() const
299 frontend::WorkAreaManager & Buffer::workAreaManager() const
301 BOOST_ASSERT(d->wa_);
306 Text & Buffer::text() const
308 return const_cast<Text &>(d->inset.text_);
312 Inset & Buffer::inset() const
314 return const_cast<InsetText &>(d->inset);
318 BufferParams & Buffer::params()
324 BufferParams const & Buffer::params() const
330 ParagraphList & Buffer::paragraphs()
332 return text().paragraphs();
336 ParagraphList const & Buffer::paragraphs() const
338 return text().paragraphs();
342 LyXVC & Buffer::lyxvc()
348 LyXVC const & Buffer::lyxvc() const
354 string const & Buffer::temppath() const
360 TexRow const & Buffer::texrow() const
366 TocBackend & Buffer::tocBackend() const
368 return d->toc_backend;
372 EmbeddedFiles & Buffer::embeddedFiles()
374 return d->embedded_files;
378 EmbeddedFiles const & Buffer::embeddedFiles() const
380 return d->embedded_files;
384 Undo & Buffer::undo()
390 string Buffer::latexName(bool const no_path) const
392 FileName latex_name = makeLatexName(d->filename);
393 return no_path ? latex_name.onlyFileName()
394 : latex_name.absFilename();
398 string Buffer::logName(LogType * type) const
400 string const filename = latexName(false);
402 if (filename.empty()) {
408 string const path = temppath();
410 FileName const fname(addName(temppath(),
411 onlyFilename(changeExtension(filename,
413 FileName const bname(
414 addName(path, onlyFilename(
415 changeExtension(filename,
416 formats.extension("literate") + ".out"))));
418 // If no Latex log or Build log is newer, show Build log
420 if (bname.exists() &&
421 (!fname.exists() || fname.lastModified() < bname.lastModified())) {
422 LYXERR(Debug::FILES, "Log name calculated as: " << bname);
425 return bname.absFilename();
427 LYXERR(Debug::FILES, "Log name calculated as: " << fname);
430 return fname.absFilename();
434 void Buffer::setReadonly(bool const flag)
436 if (d->read_only != flag) {
443 void Buffer::setFileName(string const & newfile)
445 d->filename = makeAbsPath(newfile);
446 setReadonly(d->filename.isReadOnly());
451 int Buffer::readHeader(Lexer & lex)
453 int unknown_tokens = 0;
455 int begin_header_line = -1;
457 // Initialize parameters that may be/go lacking in header:
458 params().branchlist().clear();
459 params().preamble.erase();
460 params().options.erase();
461 params().float_placement.erase();
462 params().paperwidth.erase();
463 params().paperheight.erase();
464 params().leftmargin.erase();
465 params().rightmargin.erase();
466 params().topmargin.erase();
467 params().bottommargin.erase();
468 params().headheight.erase();
469 params().headsep.erase();
470 params().footskip.erase();
471 params().listings_params.clear();
472 params().clearLayoutModules();
473 params().pdfoptions().clear();
475 for (int i = 0; i < 4; ++i) {
476 params().user_defined_bullet(i) = ITEMIZE_DEFAULTS[i];
477 params().temp_bullet(i) = ITEMIZE_DEFAULTS[i];
480 ErrorList & errorList = d->errorLists["Parse"];
484 string const token = lex.getString();
489 if (token == "\\end_header")
493 if (token == "\\begin_header") {
494 begin_header_line = line;
498 LYXERR(Debug::PARSER, "Handling document header token: `"
501 string unknown = params().readToken(lex, token, d->filename.onlyPath());
502 if (!unknown.empty()) {
503 if (unknown[0] != '\\' && token == "\\textclass") {
504 Alert::warning(_("Unknown document class"),
505 bformat(_("Using the default document class, because the "
506 "class %1$s is unknown."), from_utf8(unknown)));
509 docstring const s = bformat(_("Unknown token: "
513 errorList.push_back(ErrorItem(_("Document header error"),
518 if (begin_header_line) {
519 docstring const s = _("\\begin_header is missing");
520 errorList.push_back(ErrorItem(_("Document header error"),
524 return unknown_tokens;
529 // changed to be public and have one parameter
530 // Returns false if "\end_document" is not read (Asger)
531 bool Buffer::readDocument(Lexer & lex)
533 ErrorList & errorList = d->errorLists["Parse"];
537 string const token = lex.getString();
538 if (token != "\\begin_document") {
539 docstring const s = _("\\begin_document is missing");
540 errorList.push_back(ErrorItem(_("Document header error"),
544 // we are reading in a brand new document
545 BOOST_ASSERT(paragraphs().empty());
548 TextClass const & baseClass = textclasslist[params().getBaseClass()];
549 if (!baseClass.load(filePath())) {
550 string theclass = baseClass.name();
551 Alert::error(_("Can't load document class"), bformat(
552 _("Using the default document class, because the "
553 "class %1$s could not be loaded."), from_utf8(theclass)));
554 params().setBaseClass(defaultTextclass());
557 if (params().outputChanges) {
558 bool dvipost = LaTeXFeatures::isAvailable("dvipost");
559 bool xcolorsoul = LaTeXFeatures::isAvailable("soul") &&
560 LaTeXFeatures::isAvailable("xcolor");
562 if (!dvipost && !xcolorsoul) {
563 Alert::warning(_("Changes not shown in LaTeX output"),
564 _("Changes will not be highlighted in LaTeX output, "
565 "because neither dvipost nor xcolor/soul are installed.\n"
566 "Please install these packages or redefine "
567 "\\lyxadded and \\lyxdeleted in the LaTeX preamble."));
568 } else if (!xcolorsoul) {
569 Alert::warning(_("Changes not shown in LaTeX output"),
570 _("Changes will not be highlighted in LaTeX output "
571 "when using pdflatex, because xcolor and soul are not installed.\n"
572 "Please install both packages or redefine "
573 "\\lyxadded and \\lyxdeleted in the LaTeX preamble."));
578 bool const res = text().read(*this, lex, errorList);
579 for_each(text().paragraphs().begin(),
580 text().paragraphs().end(),
581 bind(&Paragraph::setInsetOwner, _1, &inset()));
587 // needed to insert the selection
588 void Buffer::insertStringAsLines(ParagraphList & pars,
589 pit_type & pit, pos_type & pos,
590 Font const & fn, docstring const & str, bool autobreakrows)
594 // insert the string, don't insert doublespace
595 bool space_inserted = true;
596 for (docstring::const_iterator cit = str.begin();
597 cit != str.end(); ++cit) {
598 Paragraph & par = pars[pit];
600 if (autobreakrows && (!par.empty() || par.allowEmpty())) {
601 breakParagraph(params(), pars, pit, pos,
602 par.layout()->isEnvironment());
605 space_inserted = true;
609 // do not insert consecutive spaces if !free_spacing
610 } else if ((*cit == ' ' || *cit == '\t') &&
611 space_inserted && !par.isFreeSpacing()) {
613 } else if (*cit == '\t') {
614 if (!par.isFreeSpacing()) {
615 // tabs are like spaces here
616 par.insertChar(pos, ' ', font, params().trackChanges);
618 space_inserted = true;
620 const pos_type n = 8 - pos % 8;
621 for (pos_type i = 0; i < n; ++i) {
622 par.insertChar(pos, ' ', font, params().trackChanges);
625 space_inserted = true;
627 } else if (!isPrintable(*cit)) {
628 // Ignore unprintables
631 // just insert the character
632 par.insertChar(pos, *cit, font, params().trackChanges);
634 space_inserted = (*cit == ' ');
641 bool Buffer::readString(std::string const & s)
643 params().compressed = false;
645 // remove dummy empty par
646 paragraphs().clear();
648 std::istringstream is(s);
650 FileName const name(tempName());
651 switch (readFile(lex, name, true)) {
655 // We need to call lyx2lyx, so write the input to a file
656 std::ofstream os(name.toFilesystemEncoding().c_str());
659 return readFile(name);
669 bool Buffer::readFile(FileName const & filename)
671 FileName fname(filename);
672 // Check if the file is compressed.
673 string format = filename.guessFormatFromContents();
674 if (format == "zip") {
675 // decompress to a temp directory
676 LYXERR(Debug::FILES, filename << " is in zip format. Unzip to " << temppath());
677 ::unzipToDir(filename.toFilesystemEncoding(), temppath());
679 FileName lyxfile(addName(temppath(), "content.lyx"));
680 // if both manifest.txt and file.lyx exist, this is am embedded file
681 if (lyxfile.exists()) {
682 params().embedded = true;
686 // The embedded lyx file can also be compressed, for backward compatibility
687 format = fname.guessFormatFromContents();
688 if (format == "gzip" || format == "zip" || format == "compress")
689 params().compressed = true;
691 // remove dummy empty par
692 paragraphs().clear();
695 if (readFile(lex, fname) != success)
702 bool Buffer::isFullyLoaded() const
704 return d->file_fully_loaded;
708 void Buffer::setFullyLoaded(bool value)
710 d->file_fully_loaded = value;
714 Buffer::ReadStatus Buffer::readFile(Lexer & lex, FileName const & filename,
717 BOOST_ASSERT(!filename.empty());
720 Alert::error(_("Document could not be read"),
721 bformat(_("%1$s could not be read."), from_utf8(filename.absFilename())));
726 string const token = lex.getString();
729 Alert::error(_("Document could not be read"),
730 bformat(_("%1$s could not be read."), from_utf8(filename.absFilename())));
734 // the first token _must_ be...
735 if (token != "\\lyxformat") {
736 lyxerr << "Token: " << token << endl;
738 Alert::error(_("Document format failure"),
739 bformat(_("%1$s is not a LyX document."),
740 from_utf8(filename.absFilename())));
745 string tmp_format = lex.getString();
746 //lyxerr << "LyX Format: `" << tmp_format << '\'' << endl;
747 // if present remove ".," from string.
748 string::size_type dot = tmp_format.find_first_of(".,");
749 //lyxerr << " dot found at " << dot << endl;
750 if (dot != string::npos)
751 tmp_format.erase(dot, 1);
752 int const file_format = convert<int>(tmp_format);
753 //lyxerr << "format: " << file_format << endl;
755 // save timestamp and checksum of the original disk file, making sure
756 // to not overwrite them with those of the file created in the tempdir
757 // when it has to be converted to the current format.
759 // Save the timestamp and checksum of disk file. If filename is an
760 // emergency file, save the timestamp and checksum of the original lyx file
761 // because isExternallyModified will check for this file. (BUG4193)
762 string diskfile = filename.absFilename();
763 if (suffixIs(diskfile, ".emergency"))
764 diskfile = diskfile.substr(0, diskfile.size() - 10);
765 saveCheckSum(FileName(diskfile));
768 if (file_format != LYX_FORMAT) {
771 // lyx2lyx would fail
774 FileName const tmpfile(tempName());
775 if (tmpfile.empty()) {
776 Alert::error(_("Conversion failed"),
777 bformat(_("%1$s is from a different"
778 " version of LyX, but a temporary"
779 " file for converting it could"
781 from_utf8(filename.absFilename())));
784 FileName const lyx2lyx = libFileSearch("lyx2lyx", "lyx2lyx");
785 if (lyx2lyx.empty()) {
786 Alert::error(_("Conversion script not found"),
787 bformat(_("%1$s is from a different"
788 " version of LyX, but the"
789 " conversion script lyx2lyx"
790 " could not be found."),
791 from_utf8(filename.absFilename())));
794 ostringstream command;
795 command << os::python()
796 << ' ' << quoteName(lyx2lyx.toFilesystemEncoding())
797 << " -t " << convert<string>(LYX_FORMAT)
798 << " -o " << quoteName(tmpfile.toFilesystemEncoding())
799 << ' ' << quoteName(filename.toFilesystemEncoding());
800 string const command_str = command.str();
802 LYXERR(Debug::INFO, "Running '" << command_str << '\'');
804 cmd_ret const ret = runCommand(command_str);
805 if (ret.first != 0) {
806 Alert::error(_("Conversion script failed"),
807 bformat(_("%1$s is from a different version"
808 " of LyX, but the lyx2lyx script"
809 " failed to convert it."),
810 from_utf8(filename.absFilename())));
813 bool const ret = readFile(tmpfile);
814 // Do stuff with tmpfile name and buffer name here.
815 return ret ? success : failure;
820 if (readDocument(lex)) {
821 Alert::error(_("Document format failure"),
822 bformat(_("%1$s ended unexpectedly, which means"
823 " that it is probably corrupted."),
824 from_utf8(filename.absFilename())));
827 d->file_fully_loaded = true;
832 // Should probably be moved to somewhere else: BufferView? LyXView?
833 bool Buffer::save() const
835 // We don't need autosaves in the immediate future. (Asger)
836 resetAutosaveTimers();
838 string const encodedFilename = d->filename.toFilesystemEncoding();
841 bool madeBackup = false;
843 // make a backup if the file already exists
844 if (lyxrc.make_backup && fileName().exists()) {
845 backupName = FileName(absFileName() + '~');
846 if (!lyxrc.backupdir_path.empty()) {
847 string const mangledName =
848 subst(subst(backupName.absFilename(), '/', '!'), ':', '!');
849 backupName = FileName(addName(lyxrc.backupdir_path,
852 if (fileName().copyTo(backupName, true)) {
855 Alert::error(_("Backup failure"),
856 bformat(_("Cannot create backup file %1$s.\n"
857 "Please check whether the directory exists and is writeable."),
858 from_utf8(backupName.absFilename())));
859 //LYXERR(Debug::DEBUG, "Fs error: " << fe.what());
863 // ask if the disk file has been externally modified (use checksum method)
864 if (fileName().exists() && isExternallyModified(checksum_method)) {
865 docstring const file = makeDisplayPath(absFileName(), 20);
866 docstring text = bformat(_("Document %1$s has been externally modified. Are you sure "
867 "you want to overwrite this file?"), file);
868 int const ret = Alert::prompt(_("Overwrite modified file?"),
869 text, 1, 1, _("&Overwrite"), _("&Cancel"));
874 if (writeFile(d->filename)) {
876 removeAutosaveFile(absFileName());
877 saveCheckSum(d->filename);
880 // Saving failed, so backup is not backup
882 rename(backupName, d->filename);
888 bool Buffer::writeFile(FileName const & fname) const
890 if (d->read_only && fname == d->filename)
896 if (params().embedded)
897 // first write the .lyx file to the temporary directory
898 content = FileName(addName(temppath(), "content.lyx"));
902 if (params().compressed) {
903 gz::ogzstream ofs(content.toFilesystemEncoding().c_str(), ios::out|ios::trunc);
909 ofstream ofs(content.toFilesystemEncoding().c_str(), ios::out|ios::trunc);
916 if (retval && params().embedded) {
917 // write file.lyx and all the embedded files to the zip file fname
918 // if embedding is enabled
919 return d->embedded_files.writeFile(fname);
925 bool Buffer::write(ostream & ofs) const
928 // Use the standard "C" locale for file output.
929 ofs.imbue(std::locale::classic());
932 // The top of the file should not be written by params().
934 // write out a comment in the top of the file
935 ofs << "#LyX " << lyx_version
936 << " created this file. For more info see http://www.lyx.org/\n"
937 << "\\lyxformat " << LYX_FORMAT << "\n"
938 << "\\begin_document\n";
941 /// For each author, set 'used' to true if there is a change
942 /// by this author in the document; otherwise set it to 'false'.
943 AuthorList::Authors::const_iterator a_it = params().authors().begin();
944 AuthorList::Authors::const_iterator a_end = params().authors().end();
945 for (; a_it != a_end; ++a_it)
946 a_it->second.setUsed(false);
948 ParIterator const end = par_iterator_end();
949 ParIterator it = par_iterator_begin();
950 for ( ; it != end; ++it)
951 it->checkAuthors(params().authors());
953 // now write out the buffer parameters.
954 ofs << "\\begin_header\n";
955 params().writeFile(ofs);
956 ofs << "\\end_header\n";
959 ofs << "\n\\begin_body\n";
960 text().write(*this, ofs);
961 ofs << "\n\\end_body\n";
963 // Write marker that shows file is complete
964 ofs << "\\end_document" << endl;
966 // Shouldn't really be needed....
969 // how to check if close went ok?
970 // Following is an attempt... (BE 20001011)
972 // good() returns false if any error occured, including some
974 // bad() returns true if something bad happened in the buffer,
975 // which should include file system full errors.
980 lyxerr << "File was not closed properly." << endl;
987 bool Buffer::makeLaTeXFile(FileName const & fname,
988 string const & original_path,
989 OutputParams const & runparams,
990 bool output_preamble, bool output_body) const
992 string const encoding = runparams.encoding->iconvName();
993 LYXERR(Debug::LATEX, "makeLaTeXFile encoding: " << encoding << "...");
995 odocfstream ofs(encoding);
996 if (!openFileWrite(ofs, fname))
999 //TexStream ts(ofs.rdbuf(), &texrow());
1001 bool failed_export = false;
1004 writeLaTeXSource(ofs, original_path,
1005 runparams, output_preamble, output_body);
1007 catch (iconv_codecvt_facet_exception & e) {
1008 lyxerr << "Caught iconv exception: " << e.what() << endl;
1009 failed_export = true;
1011 catch (std::exception const & e) {
1012 lyxerr << "Caught \"normal\" exception: " << e.what() << endl;
1013 failed_export = true;
1016 lyxerr << "Caught some really weird exception..." << endl;
1017 LyX::cref().emergencyCleanup();
1023 failed_export = true;
1024 lyxerr << "File '" << fname << "' was not closed properly." << endl;
1027 if (failed_export) {
1028 Alert::error(_("Encoding error"),
1029 _("Some characters of your document are probably not "
1030 "representable in the chosen encoding.\n"
1031 "Changing the document encoding to utf8 could help."));
1038 void Buffer::writeLaTeXSource(odocstream & os,
1039 string const & original_path,
1040 OutputParams const & runparams_in,
1041 bool const output_preamble, bool const output_body) const
1043 OutputParams runparams = runparams_in;
1045 // validate the buffer.
1046 LYXERR(Debug::LATEX, " Validating buffer...");
1047 LaTeXFeatures features(*this, params(), runparams);
1049 LYXERR(Debug::LATEX, " Buffer validation done.");
1051 // The starting paragraph of the coming rows is the
1052 // first paragraph of the document. (Asger)
1053 if (output_preamble && runparams.nice) {
1054 os << "%% LyX " << lyx_version << " created this file. "
1055 "For more info, see http://www.lyx.org/.\n"
1056 "%% Do not edit unless you really know what "
1058 d->texrow.newline();
1059 d->texrow.newline();
1061 LYXERR(Debug::INFO, "lyx document header finished");
1062 // There are a few differences between nice LaTeX and usual files:
1063 // usual is \batchmode and has a
1064 // special input@path to allow the including of figures
1065 // with either \input or \includegraphics (what figinsets do).
1066 // input@path is set when the actual parameter
1067 // original_path is set. This is done for usual tex-file, but not
1068 // for nice-latex-file. (Matthias 250696)
1069 // Note that input@path is only needed for something the user does
1070 // in the preamble, included .tex files or ERT, files included by
1071 // LyX work without it.
1072 if (output_preamble) {
1073 if (!runparams.nice) {
1074 // code for usual, NOT nice-latex-file
1075 os << "\\batchmode\n"; // changed
1076 // from \nonstopmode
1077 d->texrow.newline();
1079 if (!original_path.empty()) {
1081 // We don't know the encoding of inputpath
1082 docstring const inputpath = from_utf8(latex_path(original_path));
1083 os << "\\makeatletter\n"
1084 << "\\def\\input@path{{"
1085 << inputpath << "/}}\n"
1086 << "\\makeatother\n";
1087 d->texrow.newline();
1088 d->texrow.newline();
1089 d->texrow.newline();
1092 // Write the preamble
1093 runparams.use_babel = params().writeLaTeX(os, features, d->texrow);
1099 os << "\\begin{document}\n";
1100 d->texrow.newline();
1101 } // output_preamble
1103 d->texrow.start(paragraphs().begin()->id(), 0);
1105 LYXERR(Debug::INFO, "preamble finished, now the body.");
1107 if (!lyxrc.language_auto_begin &&
1108 !params().language->babel().empty()) {
1110 os << from_utf8(subst(lyxrc.language_command_begin,
1112 params().language->babel()))
1114 d->texrow.newline();
1117 Encoding const & encoding = params().encoding();
1118 if (encoding.package() == Encoding::CJK) {
1119 // Open a CJK environment, since in contrast to the encodings
1120 // handled by inputenc the document encoding is not set in
1121 // the preamble if it is handled by CJK.sty.
1122 os << "\\begin{CJK}{" << from_ascii(encoding.latexName())
1124 d->texrow.newline();
1127 // if we are doing a real file with body, even if this is the
1128 // child of some other buffer, let's cut the link here.
1129 // This happens for example if only a child document is printed.
1130 Buffer const * save_parent = 0;
1131 if (output_preamble) {
1132 save_parent = d->parent_buffer;
1133 d->parent_buffer = 0;
1136 loadChildDocuments();
1139 latexParagraphs(*this, paragraphs(), os, d->texrow, runparams);
1141 // Restore the parenthood if needed
1142 if (output_preamble)
1143 d->parent_buffer = save_parent;
1145 // add this just in case after all the paragraphs
1147 d->texrow.newline();
1149 if (encoding.package() == Encoding::CJK) {
1150 // Close the open CJK environment.
1151 // latexParagraphs will have opened one even if the last text
1153 os << "\\end{CJK}\n";
1154 d->texrow.newline();
1157 if (!lyxrc.language_auto_end &&
1158 !params().language->babel().empty()) {
1159 os << from_utf8(subst(lyxrc.language_command_end,
1161 params().language->babel()))
1163 d->texrow.newline();
1166 if (output_preamble) {
1167 os << "\\end{document}\n";
1168 d->texrow.newline();
1169 LYXERR(Debug::LATEX, "makeLaTeXFile...done");
1171 LYXERR(Debug::LATEX, "LaTeXFile for inclusion made.");
1173 runparams_in.encoding = runparams.encoding;
1175 // Just to be sure. (Asger)
1176 d->texrow.newline();
1178 LYXERR(Debug::INFO, "Finished making LaTeX file.");
1179 LYXERR(Debug::INFO, "Row count was " << d->texrow.rows() - 1 << '.');
1183 bool Buffer::isLatex() const
1185 return params().getTextClass().outputType() == LATEX;
1189 bool Buffer::isLiterate() const
1191 return params().getTextClass().outputType() == LITERATE;
1195 bool Buffer::isDocBook() const
1197 return params().getTextClass().outputType() == DOCBOOK;
1201 void Buffer::makeDocBookFile(FileName const & fname,
1202 OutputParams const & runparams,
1203 bool const body_only) const
1205 LYXERR(Debug::LATEX, "makeDocBookFile...");
1209 if (!openFileWrite(ofs, fname))
1212 writeDocBookSource(ofs, fname.absFilename(), runparams, body_only);
1216 lyxerr << "File '" << fname << "' was not closed properly." << endl;
1220 void Buffer::writeDocBookSource(odocstream & os, string const & fname,
1221 OutputParams const & runparams,
1222 bool const only_body) const
1224 LaTeXFeatures features(*this, params(), runparams);
1229 TextClass const & tclass = params().getTextClass();
1230 string const top_element = tclass.latexname();
1233 if (runparams.flavor == OutputParams::XML)
1234 os << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
1237 os << "<!DOCTYPE " << from_ascii(top_element) << ' ';
1240 if (! tclass.class_header().empty())
1241 os << from_ascii(tclass.class_header());
1242 else if (runparams.flavor == OutputParams::XML)
1243 os << "PUBLIC \"-//OASIS//DTD DocBook XML//EN\" "
1244 << "\"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd\"";
1246 os << " PUBLIC \"-//OASIS//DTD DocBook V4.2//EN\"";
1248 docstring preamble = from_utf8(params().preamble);
1249 if (runparams.flavor != OutputParams::XML ) {
1250 preamble += "<!ENTITY % output.print.png \"IGNORE\">\n";
1251 preamble += "<!ENTITY % output.print.pdf \"IGNORE\">\n";
1252 preamble += "<!ENTITY % output.print.eps \"IGNORE\">\n";
1253 preamble += "<!ENTITY % output.print.bmp \"IGNORE\">\n";
1256 string const name = runparams.nice
1257 ? changeExtension(absFileName(), ".sgml") : fname;
1258 preamble += features.getIncludedFiles(name);
1259 preamble += features.getLyXSGMLEntities();
1261 if (!preamble.empty()) {
1262 os << "\n [ " << preamble << " ]";
1267 string top = top_element;
1269 if (runparams.flavor == OutputParams::XML)
1270 top += params().language->code();
1272 top += params().language->code().substr(0,2);
1275 if (!params().options.empty()) {
1277 top += params().options;
1280 os << "<!-- " << ((runparams.flavor == OutputParams::XML)? "XML" : "SGML")
1281 << " file was created by LyX " << lyx_version
1282 << "\n See http://www.lyx.org/ for more information -->\n";
1284 params().getTextClass().counters().reset();
1286 loadChildDocuments();
1288 sgml::openTag(os, top);
1290 docbookParagraphs(paragraphs(), *this, os, runparams);
1291 sgml::closeTag(os, top_element);
1295 // chktex should be run with these flags disabled: 3, 22, 25, 30, 38(?)
1296 // Other flags: -wall -v0 -x
1297 int Buffer::runChktex()
1301 // get LaTeX-Filename
1302 FileName const path(temppath());
1303 string const name = addName(path.absFilename(), latexName());
1304 string const org_path = filePath();
1306 support::PathChanger p(path); // path to LaTeX file
1307 message(_("Running chktex..."));
1309 // Generate the LaTeX file if neccessary
1310 OutputParams runparams(¶ms().encoding());
1311 runparams.flavor = OutputParams::LATEX;
1312 runparams.nice = false;
1313 makeLaTeXFile(FileName(name), org_path, runparams);
1316 Chktex chktex(lyxrc.chktex_command, onlyFilename(name), filePath());
1317 int const res = chktex.run(terr); // run chktex
1320 Alert::error(_("chktex failure"),
1321 _("Could not run chktex successfully."));
1322 } else if (res > 0) {
1323 ErrorList & errlist = d->errorLists["ChkTeX"];
1325 bufferErrors(terr, errlist);
1336 void Buffer::validate(LaTeXFeatures & features) const
1338 TextClass const & tclass = params().getTextClass();
1340 if (params().outputChanges) {
1341 bool dvipost = LaTeXFeatures::isAvailable("dvipost");
1342 bool xcolorsoul = LaTeXFeatures::isAvailable("soul") &&
1343 LaTeXFeatures::isAvailable("xcolor");
1345 if (features.runparams().flavor == OutputParams::LATEX) {
1347 features.require("ct-dvipost");
1348 features.require("dvipost");
1349 } else if (xcolorsoul) {
1350 features.require("ct-xcolor-soul");
1351 features.require("soul");
1352 features.require("xcolor");
1354 features.require("ct-none");
1356 } else if (features.runparams().flavor == OutputParams::PDFLATEX ) {
1358 features.require("ct-xcolor-soul");
1359 features.require("soul");
1360 features.require("xcolor");
1361 features.require("pdfcolmk"); // improves color handling in PDF output
1363 features.require("ct-none");
1368 // AMS Style is at document level
1369 if (params().use_amsmath == BufferParams::package_on
1370 || tclass.provides("amsmath"))
1371 features.require("amsmath");
1372 if (params().use_esint == BufferParams::package_on)
1373 features.require("esint");
1375 loadChildDocuments();
1377 for_each(paragraphs().begin(), paragraphs().end(),
1378 boost::bind(&Paragraph::validate, _1, boost::ref(features)));
1380 // the bullet shapes are buffer level not paragraph level
1381 // so they are tested here
1382 for (int i = 0; i < 4; ++i) {
1383 if (params().user_defined_bullet(i) != ITEMIZE_DEFAULTS[i]) {
1384 int const font = params().user_defined_bullet(i).getFont();
1386 int const c = params()
1387 .user_defined_bullet(i)
1394 features.require("latexsym");
1396 } else if (font == 1) {
1397 features.require("amssymb");
1398 } else if ((font >= 2 && font <= 5)) {
1399 features.require("pifont");
1404 if (lyxerr.debugging(Debug::LATEX)) {
1405 features.showStruct();
1410 void Buffer::getLabelList(vector<docstring> & list) const
1412 /// if this is a child document and the parent is already loaded
1413 /// Use the parent's list instead [ale990407]
1414 Buffer const * tmp = masterBuffer();
1416 lyxerr << "masterBuffer() failed!" << endl;
1420 tmp->getLabelList(list);
1424 loadChildDocuments();
1426 for (InsetIterator it = inset_iterator_begin(inset()); it; ++it)
1427 it.nextInset()->getLabelList(*this, list);
1431 void Buffer::updateBibfilesCache() const
1433 // if this is a child document and the parent is already loaded
1434 // update the parent's cache instead
1435 Buffer const * tmp = masterBuffer();
1438 tmp->updateBibfilesCache();
1442 bibfilesCache_.clear();
1443 for (InsetIterator it = inset_iterator_begin(inset()); it; ++it) {
1444 if (it->lyxCode() == BIBTEX_CODE) {
1445 InsetBibtex const & inset =
1446 static_cast<InsetBibtex const &>(*it);
1447 vector<FileName> const bibfiles = inset.getFiles(*this);
1448 bibfilesCache_.insert(bibfilesCache_.end(),
1451 } else if (it->lyxCode() == INCLUDE_CODE) {
1452 InsetInclude & inset =
1453 static_cast<InsetInclude &>(*it);
1454 inset.updateBibfilesCache(*this);
1455 vector<FileName> const & bibfiles =
1456 inset.getBibfilesCache(*this);
1457 bibfilesCache_.insert(bibfilesCache_.end(),
1465 vector<FileName> const & Buffer::getBibfilesCache() const
1467 // if this is a child document and the parent is already loaded
1468 // use the parent's cache instead
1469 Buffer const * tmp = masterBuffer();
1472 return tmp->getBibfilesCache();
1474 // We update the cache when first used instead of at loading time.
1475 if (bibfilesCache_.empty())
1476 const_cast<Buffer *>(this)->updateBibfilesCache();
1478 return bibfilesCache_;
1482 bool Buffer::isDepClean(string const & name) const
1484 DepClean::const_iterator const it = d->dep_clean.find(name);
1485 if (it == d->dep_clean.end())
1491 void Buffer::markDepClean(string const & name)
1493 d->dep_clean[name] = true;
1497 bool Buffer::dispatch(string const & command, bool * result)
1499 return dispatch(lyxaction.lookupFunc(command), result);
1503 bool Buffer::dispatch(FuncRequest const & func, bool * result)
1505 bool dispatched = true;
1507 switch (func.action) {
1508 case LFUN_BUFFER_EXPORT: {
1509 bool const tmp = doExport(to_utf8(func.argument()), false);
1522 void Buffer::changeLanguage(Language const * from, Language const * to)
1527 for_each(par_iterator_begin(),
1529 bind(&Paragraph::changeLanguage, _1, params(), from, to));
1533 bool Buffer::isMultiLingual() const
1535 ParConstIterator end = par_iterator_end();
1536 for (ParConstIterator it = par_iterator_begin(); it != end; ++it)
1537 if (it->isMultiLingual(params()))
1544 ParIterator Buffer::getParFromID(int const id) const
1546 ParConstIterator it = par_iterator_begin();
1547 ParConstIterator const end = par_iterator_end();
1550 // John says this is called with id == -1 from undo
1551 lyxerr << "getParFromID(), id: " << id << endl;
1555 for (; it != end; ++it)
1563 bool Buffer::hasParWithID(int const id) const
1565 ParConstIterator const it = getParFromID(id);
1566 return it != par_iterator_end();
1570 ParIterator Buffer::par_iterator_begin()
1572 return lyx::par_iterator_begin(inset());
1576 ParIterator Buffer::par_iterator_end()
1578 return lyx::par_iterator_end(inset());
1582 ParConstIterator Buffer::par_iterator_begin() const
1584 return lyx::par_const_iterator_begin(inset());
1588 ParConstIterator Buffer::par_iterator_end() const
1590 return lyx::par_const_iterator_end(inset());
1594 Language const * Buffer::language() const
1596 return params().language;
1600 docstring const Buffer::B_(string const & l10n) const
1602 return params().B_(l10n);
1606 bool Buffer::isClean() const
1608 return d->lyx_clean;
1612 bool Buffer::isBakClean() const
1614 return d->bak_clean;
1618 bool Buffer::isExternallyModified(CheckMethod method) const
1620 BOOST_ASSERT(d->filename.exists());
1621 // if method == timestamp, check timestamp before checksum
1622 return (method == checksum_method
1623 || d->timestamp_ != d->filename.lastModified())
1624 && d->checksum_ != d->filename.checksum();
1628 void Buffer::saveCheckSum(FileName const & file) const
1630 if (file.exists()) {
1631 d->timestamp_ = file.lastModified();
1632 d->checksum_ = file.checksum();
1634 // in the case of save to a new file.
1641 void Buffer::markClean() const
1643 if (!d->lyx_clean) {
1644 d->lyx_clean = true;
1647 // if the .lyx file has been saved, we don't need an
1649 d->bak_clean = true;
1653 void Buffer::markBakClean() const
1655 d->bak_clean = true;
1659 void Buffer::setUnnamed(bool flag)
1665 bool Buffer::isUnnamed() const
1671 // FIXME: this function should be moved to buffer_pimpl.C
1672 void Buffer::markDirty()
1675 d->lyx_clean = false;
1678 d->bak_clean = false;
1680 DepClean::iterator it = d->dep_clean.begin();
1681 DepClean::const_iterator const end = d->dep_clean.end();
1683 for (; it != end; ++it)
1688 FileName Buffer::fileName() const
1694 string Buffer::absFileName() const
1696 return d->filename.absFilename();
1700 string Buffer::filePath() const
1702 return d->filename.onlyPath().absFilename();
1706 bool Buffer::isReadonly() const
1708 return d->read_only;
1712 void Buffer::setParent(Buffer const * buffer)
1714 // Avoids recursive include.
1715 d->parent_buffer = buffer == this ? 0 : buffer;
1719 Buffer const * Buffer::parent()
1721 return d->parent_buffer;
1725 Buffer const * Buffer::masterBuffer() const
1727 if (!d->parent_buffer)
1730 return d->parent_buffer->masterBuffer();
1734 bool Buffer::hasMacro(docstring const & name, Paragraph const & par) const
1736 Impl::PositionToMacroMap::iterator it;
1737 it = d->macros[name].upper_bound(par.macrocontextPosition());
1738 if (it != d->macros[name].end())
1741 // If there is a master buffer, query that
1742 Buffer const * master = masterBuffer();
1743 if (master && master != this)
1744 return master->hasMacro(name);
1746 return MacroTable::globalMacros().has(name);
1750 bool Buffer::hasMacro(docstring const & name) const
1752 if( !d->macros[name].empty() )
1755 // If there is a master buffer, query that
1756 Buffer const * master = masterBuffer();
1757 if (master && master != this)
1758 return master->hasMacro(name);
1760 return MacroTable::globalMacros().has(name);
1764 MacroData const & Buffer::getMacro(docstring const & name,
1765 Paragraph const & par) const
1767 Impl::PositionToMacroMap::iterator it;
1768 it = d->macros[name].upper_bound(par.macrocontextPosition());
1769 if( it != d->macros[name].end() )
1772 // If there is a master buffer, query that
1773 Buffer const * master = masterBuffer();
1774 if (master && master != this)
1775 return master->getMacro(name);
1777 return MacroTable::globalMacros().get(name);
1781 MacroData const & Buffer::getMacro(docstring const & name) const
1783 Impl::PositionToMacroMap::iterator it;
1784 it = d->macros[name].begin();
1785 if( it != d->macros[name].end() )
1788 // If there is a master buffer, query that
1789 Buffer const * master = masterBuffer();
1790 if (master && master != this)
1791 return master->getMacro(name);
1793 return MacroTable::globalMacros().get(name);
1797 void Buffer::updateMacros()
1799 // start with empty table
1800 d->macros = Impl::NameToPositionMacroMap();
1802 // Iterate over buffer
1803 ParagraphList & pars = text().paragraphs();
1804 for (size_t i = 0, n = pars.size(); i != n; ++i) {
1805 // set position again
1806 pars[i].setMacrocontextPosition(i);
1808 //lyxerr << "searching main par " << i
1809 // << " for macro definitions" << std::endl;
1810 InsetList const & insets = pars[i].insetList();
1811 InsetList::const_iterator it = insets.begin();
1812 InsetList::const_iterator end = insets.end();
1813 for ( ; it != end; ++it) {
1814 if (it->inset->lyxCode() != MATHMACRO_CODE)
1818 MathMacroTemplate const & macroTemplate
1819 = static_cast<MathMacroTemplate const &>(*it->inset);
1822 if (macroTemplate.validMacro()) {
1823 MacroData macro = macroTemplate.asMacroData();
1826 // call hasMacro here instead of directly querying mc to
1827 // also take the master document into consideration
1828 macro.setRedefinition(hasMacro(macroTemplate.name()));
1830 // register macro (possibly overwrite the previous one of this paragraph)
1831 d->macros[macroTemplate.name()][i] = macro;
1838 void Buffer::changeRefsIfUnique(docstring const & from, docstring const & to,
1841 //FIXME: This does not work for child documents yet.
1842 BOOST_ASSERT(code == CITE_CODE || code == REF_CODE);
1843 // Check if the label 'from' appears more than once
1844 vector<docstring> labels;
1847 if (code == CITE_CODE) {
1849 keys.fillWithBibKeys(this);
1850 BiblioInfo::const_iterator bit = keys.begin();
1851 BiblioInfo::const_iterator bend = keys.end();
1853 for (; bit != bend; ++bit)
1855 labels.push_back(bit->first);
1858 getLabelList(labels);
1859 paramName = "reference";
1862 if (std::count(labels.begin(), labels.end(), from) > 1)
1865 for (InsetIterator it = inset_iterator_begin(inset()); it; ++it) {
1866 if (it->lyxCode() == code) {
1867 InsetCommand & inset = static_cast<InsetCommand &>(*it);
1868 docstring const oldValue = inset.getParam(paramName);
1869 if (oldValue == from)
1870 inset.setParam(paramName, to);
1876 void Buffer::getSourceCode(odocstream & os, pit_type par_begin,
1877 pit_type par_end, bool full_source)
1879 OutputParams runparams(¶ms().encoding());
1880 runparams.nice = true;
1881 runparams.flavor = OutputParams::LATEX;
1882 runparams.linelen = lyxrc.plaintext_linelen;
1883 // No side effect of file copying and image conversion
1884 runparams.dryrun = true;
1888 os << "% " << _("Preview source code") << "\n\n";
1889 d->texrow.newline();
1890 d->texrow.newline();
1892 writeLaTeXSource(os, filePath(), runparams, true, true);
1894 writeDocBookSource(os, absFileName(), runparams, false);
1897 runparams.par_begin = par_begin;
1898 runparams.par_end = par_end;
1899 if (par_begin + 1 == par_end)
1901 << bformat(_("Preview source code for paragraph %1$d"), par_begin)
1905 << bformat(_("Preview source code from paragraph %1$s to %2$s"),
1906 convert<docstring>(par_begin),
1907 convert<docstring>(par_end - 1))
1909 d->texrow.newline();
1910 d->texrow.newline();
1911 // output paragraphs
1913 latexParagraphs(*this, paragraphs(), os, d->texrow, runparams);
1916 docbookParagraphs(paragraphs(), *this, os, runparams);
1922 ErrorList & Buffer::errorList(string const & type) const
1924 static ErrorList emptyErrorList;
1925 std::map<string, ErrorList>::iterator I = d->errorLists.find(type);
1926 if (I == d->errorLists.end())
1927 return emptyErrorList;
1933 void Buffer::structureChanged() const
1936 gui_->structureChanged();
1940 void Buffer::errors(std::string const & err) const
1947 void Buffer::message(docstring const & msg) const
1954 void Buffer::setBusy(bool on) const
1961 void Buffer::setReadOnly(bool on) const
1964 d->wa_->setReadOnly(on);
1968 void Buffer::updateTitles() const
1971 d->wa_->updateTitles();
1975 void Buffer::resetAutosaveTimers() const
1978 gui_->resetAutosaveTimers();
1982 void Buffer::setGuiDelegate(frontend::GuiBufferDelegate * gui)
1991 class AutoSaveBuffer : public support::ForkedProcess {
1994 AutoSaveBuffer(Buffer const & buffer, FileName const & fname)
1995 : buffer_(buffer), fname_(fname) {}
1997 virtual boost::shared_ptr<ForkedProcess> clone() const
1999 return boost::shared_ptr<ForkedProcess>(new AutoSaveBuffer(*this));
2004 command_ = to_utf8(bformat(_("Auto-saving %1$s"),
2005 from_utf8(fname_.absFilename())));
2006 return run(DontWait);
2010 virtual int generateChild();
2012 Buffer const & buffer_;
2017 #if !defined (HAVE_FORK)
2021 int AutoSaveBuffer::generateChild()
2023 // tmp_ret will be located (usually) in /tmp
2024 // will that be a problem?
2025 pid_t const pid = fork();
2026 // If you want to debug the autosave
2027 // you should set pid to -1, and comment out the fork.
2028 if (pid == 0 || pid == -1) {
2029 // pid = -1 signifies that lyx was unable
2030 // to fork. But we will do the save
2032 bool failed = false;
2034 FileName const tmp_ret(tempName(FileName(), "lyxauto"));
2035 if (!tmp_ret.empty()) {
2036 buffer_.writeFile(tmp_ret);
2037 // assume successful write of tmp_ret
2038 if (!rename(tmp_ret, fname_)) {
2040 // most likely couldn't move between
2041 // filesystems unless write of tmp_ret
2042 // failed so remove tmp file (if it
2044 tmp_ret.removeFile();
2051 // failed to write/rename tmp_ret so try writing direct
2052 if (!buffer_.writeFile(fname_)) {
2053 // It is dangerous to do this in the child,
2054 // but safe in the parent, so...
2055 if (pid == -1) // emit message signal.
2056 buffer_.message(_("Autosave failed!"));
2059 if (pid == 0) { // we are the child so...
2069 // Perfect target for a thread...
2070 void Buffer::autoSave() const
2072 if (isBakClean() || isReadonly()) {
2073 // We don't save now, but we'll try again later
2074 resetAutosaveTimers();
2078 // emit message signal.
2079 message(_("Autosaving current document..."));
2081 // create autosave filename
2082 string fname = filePath();
2084 fname += onlyFilename(absFileName());
2087 AutoSaveBuffer autosave(*this, FileName(fname));
2091 resetAutosaveTimers();
2095 /** Write a buffer to a new file name and rename the buffer
2096 according to the new file name.
2098 This function is e.g. used by menu callbacks and
2099 LFUN_BUFFER_WRITE_AS.
2101 If 'newname' is empty (the default), the user is asked via a
2102 dialog for the buffer's new name and location.
2104 If 'newname' is non-empty and has an absolute path, that is used.
2105 Otherwise the base directory of the buffer is used as the base
2106 for any relative path in 'newname'.
2109 bool Buffer::writeAs(string const & newname)
2111 string fname = absFileName();
2112 string const oldname = fname;
2114 if (newname.empty()) { /// No argument? Ask user through dialog
2117 FileDialog dlg(_("Choose a filename to save document as"),
2118 LFUN_BUFFER_WRITE_AS);
2119 dlg.setButton1(_("Documents|#o#O"), from_utf8(lyxrc.document_path));
2120 dlg.setButton2(_("Templates|#T#t"), from_utf8(lyxrc.template_path));
2122 if (!support::isLyXFilename(fname))
2125 support::FileFilterList const filter(_("LyX Documents (*.lyx)"));
2127 FileDialog::Result result =
2128 dlg.save(from_utf8(onlyPath(fname)),
2130 from_utf8(onlyFilename(fname)));
2132 if (result.first == FileDialog::Later)
2135 fname = to_utf8(result.second);
2140 // Make sure the absolute filename ends with appropriate suffix
2141 fname = makeAbsPath(fname).absFilename();
2142 if (!support::isLyXFilename(fname))
2146 fname = makeAbsPath(newname, onlyPath(oldname)).absFilename();
2148 if (FileName(fname).exists()) {
2149 docstring const file = makeDisplayPath(fname, 30);
2150 docstring text = bformat(_("The document %1$s already "
2151 "exists.\n\nDo you want to "
2152 "overwrite that document?"),
2154 int const ret = Alert::prompt(_("Overwrite document?"),
2155 text, 0, 1, _("&Overwrite"), _("&Cancel"));
2161 // Ok, change the name of the buffer
2164 bool unnamed = isUnnamed();
2166 saveCheckSum(FileName(fname));
2169 setFileName(oldname);
2170 setUnnamed(unnamed);
2171 saveCheckSum(FileName(oldname));
2175 removeAutosaveFile(oldname);
2180 bool Buffer::menuWrite()
2183 LyX::ref().session().lastFiles().add(FileName(absFileName()));
2187 // FIXME: we don't tell the user *WHY* the save failed !!
2189 docstring const file = makeDisplayPath(absFileName(), 30);
2191 docstring text = bformat(_("The document %1$s could not be saved.\n\n"
2192 "Do you want to rename the document and "
2193 "try again?"), file);
2194 int const ret = Alert::prompt(_("Rename and save?"),
2195 text, 0, 1, _("&Rename"), _("&Cancel"));
2204 void Buffer::resetChildDocuments(bool close_them) const
2206 for (InsetIterator it = inset_iterator_begin(inset()); it; ++it) {
2207 if (it->lyxCode() != INCLUDE_CODE)
2209 InsetCommand const & inset = static_cast<InsetCommand const &>(*it);
2210 InsetCommandParams const & ip = inset.params();
2212 resetParentBuffer(this, ip, close_them);
2215 if (use_gui && masterBuffer() == this)
2216 updateLabels(*this);
2220 void Buffer::loadChildDocuments() const
2222 bool parse_error = false;
2224 for (InsetIterator it = inset_iterator_begin(inset()); it; ++it) {
2225 if (it->lyxCode() != INCLUDE_CODE)
2227 InsetCommand const & inset = static_cast<InsetCommand const &>(*it);
2228 InsetCommandParams const & ip = inset.params();
2229 Buffer * child = loadIfNeeded(*this, ip);
2232 parse_error |= !child->errorList("Parse").empty();
2233 child->loadChildDocuments();
2236 if (use_gui && masterBuffer() == this)
2237 updateLabels(*this);
2241 string Buffer::bufferFormat() const
2251 bool Buffer::doExport(string const & format, bool put_in_tempdir,
2252 string & result_file) const
2254 string backend_format;
2255 OutputParams runparams(¶ms().encoding());
2256 runparams.flavor = OutputParams::LATEX;
2257 runparams.linelen = lyxrc.plaintext_linelen;
2258 vector<string> backs = backends();
2259 if (find(backs.begin(), backs.end(), format) == backs.end()) {
2260 // Get shortest path to format
2261 Graph::EdgePath path;
2262 for (vector<string>::const_iterator it = backs.begin();
2263 it != backs.end(); ++it) {
2264 Graph::EdgePath p = theConverters().getPath(*it, format);
2265 if (!p.empty() && (path.empty() || p.size() < path.size())) {
2266 backend_format = *it;
2271 runparams.flavor = theConverters().getFlavor(path);
2273 Alert::error(_("Couldn't export file"),
2274 bformat(_("No information for exporting the format %1$s."),
2275 formats.prettyName(format)));
2279 backend_format = format;
2280 // FIXME: Don't hardcode format names here, but use a flag
2281 if (backend_format == "pdflatex")
2282 runparams.flavor = OutputParams::PDFLATEX;
2285 string filename = latexName(false);
2286 filename = addName(temppath(), filename);
2287 filename = changeExtension(filename,
2288 formats.extension(backend_format));
2290 // Plain text backend
2291 if (backend_format == "text")
2292 writePlaintextFile(*this, FileName(filename), runparams);
2294 else if (backend_format == "lyx")
2295 writeFile(FileName(filename));
2297 else if (isDocBook()) {
2298 runparams.nice = !put_in_tempdir;
2299 makeDocBookFile(FileName(filename), runparams);
2302 else if (backend_format == format) {
2303 runparams.nice = true;
2304 if (!makeLaTeXFile(FileName(filename), string(), runparams))
2306 } else if (!lyxrc.tex_allows_spaces
2307 && support::contains(filePath(), ' ')) {
2308 Alert::error(_("File name error"),
2309 _("The directory path to the document cannot contain spaces."));
2312 runparams.nice = false;
2313 if (!makeLaTeXFile(FileName(filename), filePath(), runparams))
2317 string const error_type = (format == "program")
2318 ? "Build" : bufferFormat();
2319 string const ext = formats.extension(format);
2320 FileName const tmp_result_file(changeExtension(filename, ext));
2321 bool const success = theConverters().convert(this, FileName(filename),
2322 tmp_result_file, FileName(absFileName()), backend_format, format,
2323 errorList(error_type));
2324 // Emit the signal to show the error list.
2325 if (format != backend_format)
2331 result_file = tmp_result_file.absFilename();
2333 result_file = changeExtension(absFileName(), ext);
2334 // We need to copy referenced files (e. g. included graphics
2335 // if format == "dvi") to the result dir.
2336 vector<ExportedFile> const files =
2337 runparams.exportdata->externalFiles(format);
2338 string const dest = onlyPath(result_file);
2339 CopyStatus status = SUCCESS;
2340 for (vector<ExportedFile>::const_iterator it = files.begin();
2341 it != files.end() && status != CANCEL; ++it) {
2343 formats.getFormatFromFile(it->sourceName);
2344 status = copyFile(fmt, it->sourceName,
2345 makeAbsPath(it->exportName, dest),
2346 it->exportName, status == FORCE);
2348 if (status == CANCEL) {
2349 message(_("Document export cancelled."));
2350 } else if (tmp_result_file.exists()) {
2351 // Finally copy the main file
2352 status = copyFile(format, tmp_result_file,
2353 FileName(result_file), result_file,
2355 message(bformat(_("Document exported as %1$s "
2357 formats.prettyName(format),
2358 makeDisplayPath(result_file)));
2360 // This must be a dummy converter like fax (bug 1888)
2361 message(bformat(_("Document exported as %1$s"),
2362 formats.prettyName(format)));
2370 bool Buffer::doExport(string const & format, bool put_in_tempdir) const
2373 return doExport(format, put_in_tempdir, result_file);
2377 bool Buffer::preview(string const & format) const
2380 if (!doExport(format, true, result_file))
2382 return formats.view(*this, FileName(result_file), format);
2386 bool Buffer::isExportable(string const & format) const
2388 vector<string> backs = backends();
2389 for (vector<string>::const_iterator it = backs.begin();
2390 it != backs.end(); ++it)
2391 if (theConverters().isReachable(*it, format))
2397 vector<Format const *> Buffer::exportableFormats(bool only_viewable) const
2399 vector<string> backs = backends();
2400 vector<Format const *> result =
2401 theConverters().getReachable(backs[0], only_viewable, true);
2402 for (vector<string>::const_iterator it = backs.begin() + 1;
2403 it != backs.end(); ++it) {
2404 vector<Format const *> r =
2405 theConverters().getReachable(*it, only_viewable, false);
2406 result.insert(result.end(), r.begin(), r.end());
2412 vector<string> Buffer::backends() const
2415 if (params().getTextClass().isTeXClassAvailable()) {
2416 v.push_back(bufferFormat());
2417 // FIXME: Don't hardcode format names here, but use a flag
2418 if (v.back() == "latex")
2419 v.push_back("pdflatex");
2421 v.push_back("text");
2427 bool Buffer::readFileHelper(FileName const & s)
2429 // File information about normal file
2431 docstring const file = makeDisplayPath(s.absFilename(), 50);
2432 docstring text = bformat(_("The specified document\n%1$s"
2433 "\ncould not be read."), file);
2434 Alert::error(_("Could not read document"), text);
2438 // Check if emergency save file exists and is newer.
2439 FileName const e(s.absFilename() + ".emergency");
2441 if (e.exists() && s.exists() && e.lastModified() > s.lastModified()) {
2442 docstring const file = makeDisplayPath(s.absFilename(), 20);
2443 docstring const text =
2444 bformat(_("An emergency save of the document "
2446 "Recover emergency save?"), file);
2447 switch (Alert::prompt(_("Load emergency save?"), text, 0, 2,
2448 _("&Recover"), _("&Load Original"),
2452 // the file is not saved if we load the emergency file.
2462 // Now check if autosave file is newer.
2463 FileName const a(onlyPath(s.absFilename()) + '#' + onlyFilename(s.absFilename()) + '#');
2465 if (a.exists() && s.exists() && a.lastModified() > s.lastModified()) {
2466 docstring const file = makeDisplayPath(s.absFilename(), 20);
2467 docstring const text =
2468 bformat(_("The backup of the document "
2469 "%1$s is newer.\n\nLoad the "
2470 "backup instead?"), file);
2471 switch (Alert::prompt(_("Load backup?"), text, 0, 2,
2472 _("&Load backup"), _("Load &original"),
2476 // the file is not saved if we load the autosave file.
2480 // Here we delete the autosave
2491 bool Buffer::loadLyXFile(FileName const & s)
2493 if (s.isReadableFile()) {
2494 if (readFileHelper(s)) {
2495 lyxvc().file_found_hook(s);
2496 if (!s.isWritable())
2501 docstring const file = makeDisplayPath(s.absFilename(), 20);
2502 // Here we probably should run
2503 if (LyXVC::file_not_found_hook(s)) {
2504 docstring const text =
2505 bformat(_("Do you want to retrieve the document"
2506 " %1$s from version control?"), file);
2507 int const ret = Alert::prompt(_("Retrieve from version control?"),
2508 text, 0, 1, _("&Retrieve"), _("&Cancel"));
2511 // How can we know _how_ to do the checkout?
2512 // With the current VC support it has to be,
2513 // a RCS file since CVS do not have special ,v files.
2515 return loadLyXFile(s);
2523 void Buffer::bufferErrors(TeXErrors const & terr, ErrorList & errorList) const
2525 TeXErrors::Errors::const_iterator cit = terr.begin();
2526 TeXErrors::Errors::const_iterator end = terr.end();
2528 for (; cit != end; ++cit) {
2531 int errorRow = cit->error_in_line;
2532 bool found = d->texrow.getIdFromRow(errorRow, id_start,
2538 found = d->texrow.getIdFromRow(errorRow, id_end, pos_end);
2539 } while (found && id_start == id_end && pos_start == pos_end);
2541 errorList.push_back(ErrorItem(cit->error_desc,
2542 cit->error_text, id_start, pos_start, pos_end));