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
8 * Full author contact details are available in file CREDITS.
16 #include "BiblioInfo.h"
17 #include "BranchList.h"
18 #include "buffer_funcs.h"
19 #include "BufferList.h"
20 #include "BufferParams.h"
23 #include "Converter.h"
26 #include "DocIterator.h"
28 #include "ErrorList.h"
31 #include "FuncRequest.h"
33 #include "InsetIterator.h"
34 #include "InsetList.h"
37 #include "LaTeXFeatures.h"
39 #include "LyXAction.h"
47 #include "output_docbook.h"
48 #include "output_latex.h"
49 #include "output_plaintext.h"
50 #include "Paragraph.h"
51 #include "paragraph_funcs.h"
52 #include "ParagraphParameters.h"
53 #include "ParIterator.h"
57 #include "TextClassList.h"
58 #include "TexStream.h"
59 #include "TocBackend.h"
62 #include "EmbeddedFiles.h"
63 #include "PDFOptions.h"
65 #include "insets/InsetBibitem.h"
66 #include "insets/InsetBibtex.h"
67 #include "insets/InsetInclude.h"
68 #include "insets/InsetText.h"
70 #include "mathed/MathMacroTemplate.h"
71 #include "mathed/MacroTable.h"
72 #include "mathed/MathSupport.h"
74 #include "frontends/alert.h"
75 #include "frontends/Delegates.h"
76 #include "frontends/WorkAreaManager.h"
77 #include "frontends/FileDialog.h"
79 #include "graphics/Previews.h"
81 #include "support/types.h"
82 #include "support/lyxalgo.h"
83 #include "support/FileFilterList.h"
84 #include "support/filetools.h"
85 #include "support/Forkedcall.h"
86 #include "support/fs_extras.h"
87 #include "support/gzstream.h"
88 #include "support/lyxlib.h"
89 #include "support/os.h"
90 #include "support/Path.h"
91 #include "support/textutils.h"
92 #include "support/convert.h"
94 #if !defined (HAVE_FORK)
98 #include <boost/bind.hpp>
99 #include <boost/filesystem/exception.hpp>
100 #include <boost/filesystem/operations.hpp>
101 #include <boost/shared_ptr.hpp>
111 using std::make_pair;
116 using std::ostringstream;
127 using support::addName;
128 using support::bformat;
129 using support::changeExtension;
130 using support::cmd_ret;
131 using support::createBufferTmpDir;
132 using support::FileName;
133 using support::libFileSearch;
134 using support::latex_path;
135 using support::ltrim;
136 using support::makeAbsPath;
137 using support::makeDisplayPath;
138 using support::makeLatexName;
139 using support::onlyFilename;
140 using support::onlyPath;
141 using support::quoteName;
142 using support::removeAutosaveFile;
143 using support::rename;
144 using support::runCommand;
145 using support::split;
146 using support::subst;
147 using support::tempName;
150 using support::suffixIs;
152 namespace Alert = frontend::Alert;
153 namespace os = support::os;
154 namespace fs = boost::filesystem;
158 int const LYX_FORMAT = 295; //Uwe: htmlurl, href
163 typedef std::map<string, bool> DepClean;
168 Impl(Buffer & parent, FileName const & file, bool readonly);
175 /// need to regenerate .tex?
179 mutable bool lyx_clean;
181 /// is autosave needed?
182 mutable bool bak_clean;
184 /// is this a unnamed file (New...)?
190 /// name of the file the buffer is associated with.
193 /** Set to true only when the file is fully loaded.
194 * Used to prevent the premature generation of previews
195 * and by the citation inset.
197 bool file_fully_loaded;
199 /// our Text that should be wrapped in an InsetText
206 TocBackend toc_backend;
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 : lyx_clean(true), bak_clean(true), unnamed(false), read_only(readonly_),
229 filename(file), file_fully_loaded(false), inset(params),
230 toc_backend(&parent), embedded_files(&parent), timestamp_(0),
231 checksum_(0), wa_(0), undo_(parent)
233 inset.setAutoBreakRows(true);
234 lyxvc.setBuffer(&parent);
235 temppath = createBufferTmpDir();
236 params.filepath = onlyPath(file.absFilename());
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 : pimpl_(new Impl(*this, FileName(file), readonly)), gui_(0)
249 LYXERR(Debug::INFO) << "Buffer::Buffer()" << endl;
255 LYXERR(Debug::INFO) << "Buffer::~Buffer()" << endl;
256 // here the buffer should take care that it is
257 // saved properly, before it goes into the void.
259 Buffer * 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 if (!temppath().empty() && !FileName(temppath()).destroyDirectory()) {
267 Alert::warning(_("Could not remove temporary directory"),
268 bformat(_("Could not remove the temporary directory %1$s"),
269 from_utf8(temppath())));
272 // Remove any previewed LaTeX snippets associated with this buffer.
273 graphics::Previews::get().removeLoader(*this);
276 pimpl_->wa_->closeAll();
283 void Buffer::changed() const
286 pimpl_->wa_->redrawAll();
290 frontend::WorkAreaManager & Buffer::workAreaManager() const
292 BOOST_ASSERT(pimpl_->wa_);
297 Text & Buffer::text() const
299 return const_cast<Text &>(pimpl_->inset.text_);
303 Inset & Buffer::inset() const
305 return const_cast<InsetText &>(pimpl_->inset);
309 BufferParams & Buffer::params()
311 return pimpl_->params;
315 BufferParams const & Buffer::params() const
317 return pimpl_->params;
321 ParagraphList & Buffer::paragraphs()
323 return text().paragraphs();
327 ParagraphList const & Buffer::paragraphs() const
329 return text().paragraphs();
333 LyXVC & Buffer::lyxvc()
335 return pimpl_->lyxvc;
339 LyXVC const & Buffer::lyxvc() const
341 return pimpl_->lyxvc;
345 string const & Buffer::temppath() const
347 return pimpl_->temppath;
351 TexRow & Buffer::texrow()
353 return pimpl_->texrow;
357 TexRow const & Buffer::texrow() const
359 return pimpl_->texrow;
363 TocBackend & Buffer::tocBackend()
365 return pimpl_->toc_backend;
369 TocBackend const & Buffer::tocBackend() const
371 return pimpl_->toc_backend;
375 EmbeddedFiles & Buffer::embeddedFiles()
377 return pimpl_->embedded_files;
381 EmbeddedFiles const & Buffer::embeddedFiles() const
383 return pimpl_->embedded_files;
387 Undo & Buffer::undo()
389 return pimpl_->undo_;
393 string Buffer::latexName(bool const no_path) const
395 string const name = changeExtension(makeLatexName(absFileName()), ".tex");
396 return no_path ? onlyFilename(name) : name;
400 pair<Buffer::LogType, string> Buffer::logName() const
402 string const filename = latexName(false);
404 if (filename.empty())
405 return make_pair(Buffer::latexlog, string());
407 string const path = temppath();
409 FileName const fname(addName(temppath(),
410 onlyFilename(changeExtension(filename,
412 FileName const bname(
413 addName(path, onlyFilename(
414 changeExtension(filename,
415 formats.extension("literate") + ".out"))));
417 // If no Latex log or Build log is newer, show Build log
419 if (bname.exists() &&
420 (!fname.exists() || fname.lastModified() < bname.lastModified())) {
421 LYXERR(Debug::FILES) << "Log name calculated as: " << bname << endl;
422 return make_pair(Buffer::buildlog, bname.absFilename());
424 LYXERR(Debug::FILES) << "Log name calculated as: " << fname << endl;
425 return make_pair(Buffer::latexlog, fname.absFilename());
429 void Buffer::setReadonly(bool const flag)
431 if (pimpl_->read_only != flag) {
432 pimpl_->read_only = flag;
438 void Buffer::setFileName(string const & newfile)
440 pimpl_->filename = makeAbsPath(newfile);
441 params().filepath = onlyPath(pimpl_->filename.absFilename());
442 setReadonly(pimpl_->filename.isReadOnly());
447 int Buffer::readHeader(Lexer & lex)
449 int unknown_tokens = 0;
451 int begin_header_line = -1;
453 // Initialize parameters that may be/go lacking in header:
454 params().branchlist().clear();
455 params().preamble.erase();
456 params().options.erase();
457 params().float_placement.erase();
458 params().paperwidth.erase();
459 params().paperheight.erase();
460 params().leftmargin.erase();
461 params().rightmargin.erase();
462 params().topmargin.erase();
463 params().bottommargin.erase();
464 params().headheight.erase();
465 params().headsep.erase();
466 params().footskip.erase();
467 params().listings_params.clear();
468 params().clearLayoutModules();
469 params().pdfoptions().clear();
471 for (int i = 0; i < 4; ++i) {
472 params().user_defined_bullet(i) = ITEMIZE_DEFAULTS[i];
473 params().temp_bullet(i) = ITEMIZE_DEFAULTS[i];
476 ErrorList & errorList = pimpl_->errorLists["Parse"];
480 string const token = lex.getString();
485 if (token == "\\end_header")
489 if (token == "\\begin_header") {
490 begin_header_line = line;
494 LYXERR(Debug::PARSER) << "Handling document header token: `"
495 << token << '\'' << endl;
497 string unknown = params().readToken(lex, token);
498 if (!unknown.empty()) {
499 if (unknown[0] != '\\' && token == "\\textclass") {
500 Alert::warning(_("Unknown document class"),
501 bformat(_("Using the default document class, because the "
502 "class %1$s is unknown."), from_utf8(unknown)));
505 docstring const s = bformat(_("Unknown token: "
509 errorList.push_back(ErrorItem(_("Document header error"),
514 if (begin_header_line) {
515 docstring const s = _("\\begin_header is missing");
516 errorList.push_back(ErrorItem(_("Document header error"),
520 return unknown_tokens;
525 // changed to be public and have one parameter
526 // Returns false if "\end_document" is not read (Asger)
527 bool Buffer::readDocument(Lexer & lex)
529 ErrorList & errorList = pimpl_->errorLists["Parse"];
533 string const token = lex.getString();
534 if (token != "\\begin_document") {
535 docstring const s = _("\\begin_document is missing");
536 errorList.push_back(ErrorItem(_("Document header error"),
540 // we are reading in a brand new document
541 BOOST_ASSERT(paragraphs().empty());
544 TextClass const & baseClass = textclasslist[params().getBaseClass()];
545 if (!baseClass.load(filePath())) {
546 string theclass = baseClass.name();
547 Alert::error(_("Can't load document class"), bformat(
548 _("Using the default document class, because the "
549 "class %1$s could not be loaded."), from_utf8(theclass)));
550 params().setBaseClass(defaultTextclass());
553 if (params().outputChanges) {
554 bool dvipost = LaTeXFeatures::isAvailable("dvipost");
555 bool xcolorsoul = LaTeXFeatures::isAvailable("soul") &&
556 LaTeXFeatures::isAvailable("xcolor");
558 if (!dvipost && !xcolorsoul) {
559 Alert::warning(_("Changes not shown in LaTeX output"),
560 _("Changes will not be highlighted in LaTeX output, "
561 "because neither dvipost nor xcolor/soul are installed.\n"
562 "Please install these packages or redefine "
563 "\\lyxadded and \\lyxdeleted in the LaTeX preamble."));
564 } else if (!xcolorsoul) {
565 Alert::warning(_("Changes not shown in LaTeX output"),
566 _("Changes will not be highlighted in LaTeX output "
567 "when using pdflatex, because xcolor and soul are not installed.\n"
568 "Please install both packages or redefine "
569 "\\lyxadded and \\lyxdeleted in the LaTeX preamble."));
572 // read manifest after header
573 embeddedFiles().readManifest(lex, errorList);
576 bool const res = text().read(*this, lex, errorList);
577 for_each(text().paragraphs().begin(),
578 text().paragraphs().end(),
579 bind(&Paragraph::setInsetOwner, _1, &inset()));
585 // needed to insert the selection
586 void Buffer::insertStringAsLines(ParagraphList & pars,
587 pit_type & pit, pos_type & pos,
588 Font const & fn, docstring const & str, bool autobreakrows)
592 // insert the string, don't insert doublespace
593 bool space_inserted = true;
594 for (docstring::const_iterator cit = str.begin();
595 cit != str.end(); ++cit) {
596 Paragraph & par = pars[pit];
598 if (autobreakrows && (!par.empty() || par.allowEmpty())) {
599 breakParagraph(params(), pars, pit, pos,
600 par.layout()->isEnvironment());
603 space_inserted = true;
607 // do not insert consecutive spaces if !free_spacing
608 } else if ((*cit == ' ' || *cit == '\t') &&
609 space_inserted && !par.isFreeSpacing()) {
611 } else if (*cit == '\t') {
612 if (!par.isFreeSpacing()) {
613 // tabs are like spaces here
614 par.insertChar(pos, ' ', font, params().trackChanges);
616 space_inserted = true;
618 const pos_type n = 8 - pos % 8;
619 for (pos_type i = 0; i < n; ++i) {
620 par.insertChar(pos, ' ', font, params().trackChanges);
623 space_inserted = true;
625 } else if (!isPrintable(*cit)) {
626 // Ignore unprintables
629 // just insert the character
630 par.insertChar(pos, *cit, font, params().trackChanges);
632 space_inserted = (*cit == ' ');
639 bool Buffer::readString(std::string const & s)
641 params().compressed = false;
643 // remove dummy empty par
644 paragraphs().clear();
646 std::istringstream is(s);
648 FileName const name(tempName());
649 switch (readFile(lex, name, true)) {
653 // We need to call lyx2lyx, so write the input to a file
654 std::ofstream os(name.toFilesystemEncoding().c_str());
657 return readFile(name);
667 bool Buffer::readFile(FileName const & filename)
669 FileName fname(filename);
670 // Check if the file is compressed.
671 string format = filename.guessFormatFromContents();
672 if (format == "zip") {
673 // decompress to a temp directory
674 LYXERR(Debug::FILES) << filename << " is in zip format. Unzip to " << temppath() << endl;
675 ::unzipToDir(filename.toFilesystemEncoding(), temppath());
677 FileName lyxfile(addName(temppath(), "content.lyx"));
678 // if both manifest.txt and file.lyx exist, this is am embedded file
679 if (lyxfile.exists()) {
680 params().embedded = true;
684 // The embedded lyx file can also be compressed, for backward compatibility
685 format = fname.guessFormatFromContents();
686 if (format == "gzip" || format == "zip" || format == "compress")
687 params().compressed = true;
689 // remove dummy empty par
690 paragraphs().clear();
693 if (readFile(lex, fname) != success)
700 bool Buffer::isFullyLoaded() const
702 return pimpl_->file_fully_loaded;
706 void Buffer::setFullyLoaded(bool value)
708 pimpl_->file_fully_loaded = value;
712 Buffer::ReadStatus Buffer::readFile(Lexer & lex, FileName const & filename,
715 BOOST_ASSERT(!filename.empty());
718 Alert::error(_("Document could not be read"),
719 bformat(_("%1$s could not be read."), from_utf8(filename.absFilename())));
724 string const token = lex.getString();
727 Alert::error(_("Document could not be read"),
728 bformat(_("%1$s could not be read."), from_utf8(filename.absFilename())));
732 // the first token _must_ be...
733 if (token != "\\lyxformat") {
734 lyxerr << "Token: " << token << endl;
736 Alert::error(_("Document format failure"),
737 bformat(_("%1$s is not a LyX document."),
738 from_utf8(filename.absFilename())));
743 string tmp_format = lex.getString();
744 //lyxerr << "LyX Format: `" << tmp_format << '\'' << endl;
745 // if present remove ".," from string.
746 string::size_type dot = tmp_format.find_first_of(".,");
747 //lyxerr << " dot found at " << dot << endl;
748 if (dot != string::npos)
749 tmp_format.erase(dot, 1);
750 int const file_format = convert<int>(tmp_format);
751 //lyxerr << "format: " << file_format << endl;
753 // save timestamp and checksum of the original disk file, making sure
754 // to not overwrite them with those of the file created in the tempdir
755 // when it has to be converted to the current format.
756 if (!pimpl_->checksum_) {
757 // Save the timestamp and checksum of disk file. If filename is an
758 // emergency file, save the timestamp and sum of the original lyx file
759 // because isExternallyModified will check for this file. (BUG4193)
760 string diskfile = filename.toFilesystemEncoding();
761 if (suffixIs(diskfile, ".emergency"))
762 diskfile = diskfile.substr(0, diskfile.size() - 10);
763 saveCheckSum(FileName(diskfile));
766 if (file_format != LYX_FORMAT) {
769 // lyx2lyx would fail
772 FileName const tmpfile(tempName());
773 if (tmpfile.empty()) {
774 Alert::error(_("Conversion failed"),
775 bformat(_("%1$s is from a different"
776 " version of LyX, but a temporary"
777 " file for converting it could"
779 from_utf8(filename.absFilename())));
782 FileName const lyx2lyx = libFileSearch("lyx2lyx", "lyx2lyx");
783 if (lyx2lyx.empty()) {
784 Alert::error(_("Conversion script not found"),
785 bformat(_("%1$s is from a different"
786 " version of LyX, but the"
787 " conversion script lyx2lyx"
788 " could not be found."),
789 from_utf8(filename.absFilename())));
792 ostringstream command;
793 command << os::python()
794 << ' ' << quoteName(lyx2lyx.toFilesystemEncoding())
795 << " -t " << convert<string>(LYX_FORMAT)
796 << " -o " << quoteName(tmpfile.toFilesystemEncoding())
797 << ' ' << quoteName(filename.toFilesystemEncoding());
798 string const command_str = command.str();
800 LYXERR(Debug::INFO) << "Running '"
801 << 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 //lyxerr << "removing " << MacroTable::localMacros().size()
828 // << " temporary macro entries" << endl;
829 //MacroTable::localMacros().clear();
831 pimpl_->file_fully_loaded = true;
836 // Should probably be moved to somewhere else: BufferView? LyXView?
837 bool Buffer::save() const
839 // We don't need autosaves in the immediate future. (Asger)
840 resetAutosaveTimers();
842 string const encodedFilename = pimpl_->filename.toFilesystemEncoding();
845 bool madeBackup = false;
847 // make a backup if the file already exists
848 if (lyxrc.make_backup && fs::exists(encodedFilename)) {
849 backupName = FileName(absFileName() + '~');
850 if (!lyxrc.backupdir_path.empty())
851 backupName = FileName(addName(lyxrc.backupdir_path,
852 subst(os::internal_path(backupName.absFilename()), '/', '!')));
855 fs::copy_file(encodedFilename, backupName.toFilesystemEncoding(), false);
857 } catch (fs::filesystem_error const & fe) {
858 Alert::error(_("Backup failure"),
859 bformat(_("Cannot create backup file %1$s.\n"
860 "Please check whether the directory exists and is writeable."),
861 from_utf8(backupName.absFilename())));
862 LYXERR(Debug::DEBUG) << "Fs error: " << fe.what() << endl;
866 // ask if the disk file has been externally modified (use checksum method)
867 if (fs::exists(encodedFilename) && isExternallyModified(checksum_method)) {
868 docstring const file = makeDisplayPath(absFileName(), 20);
869 docstring text = bformat(_("Document %1$s has been externally modified. Are you sure "
870 "you want to overwrite this file?"), file);
871 int const ret = Alert::prompt(_("Overwrite modified file?"),
872 text, 1, 1, _("&Overwrite"), _("&Cancel"));
877 if (writeFile(pimpl_->filename)) {
879 removeAutosaveFile(absFileName());
880 saveCheckSum(pimpl_->filename);
883 // Saving failed, so backup is not backup
885 rename(backupName, pimpl_->filename);
891 bool Buffer::writeFile(FileName const & fname) const
893 if (pimpl_->read_only && fname == pimpl_->filename)
899 if (params().embedded)
900 // first write the .lyx file to the temporary directory
901 content = FileName(addName(temppath(), "content.lyx"));
905 if (params().compressed) {
906 gz::ogzstream ofs(content.toFilesystemEncoding().c_str(), ios::out|ios::trunc);
912 ofstream ofs(content.toFilesystemEncoding().c_str(), ios::out|ios::trunc);
919 if (retval && params().embedded) {
920 // write file.lyx and all the embedded files to the zip file fname
921 // if embedding is enabled
922 return pimpl_->embedded_files.writeFile(fname);
928 bool Buffer::write(ostream & ofs) const
931 // Use the standard "C" locale for file output.
932 ofs.imbue(std::locale::classic());
935 // The top of the file should not be written by params().
937 // write out a comment in the top of the file
938 ofs << "#LyX " << lyx_version
939 << " created this file. For more info see http://www.lyx.org/\n"
940 << "\\lyxformat " << LYX_FORMAT << "\n"
941 << "\\begin_document\n";
944 /// For each author, set 'used' to true if there is a change
945 /// by this author in the document; otherwise set it to 'false'.
946 AuthorList::Authors::const_iterator a_it = params().authors().begin();
947 AuthorList::Authors::const_iterator a_end = params().authors().end();
948 for (; a_it != a_end; ++a_it)
949 a_it->second.used(false);
951 ParIterator const end = par_iterator_end();
952 ParIterator it = par_iterator_begin();
953 for ( ; it != end; ++it)
954 it->checkAuthors(params().authors());
956 // now write out the buffer parameters.
957 ofs << "\\begin_header\n";
958 params().writeFile(ofs);
959 ofs << "\\end_header\n";
961 // write the manifest after header
962 ofs << "\n\\begin_manifest\n";
963 pimpl_->embedded_files.update();
964 embeddedFiles().writeManifest(ofs);
965 ofs << "\\end_manifest\n";
968 ofs << "\n\\begin_body\n";
969 text().write(*this, ofs);
970 ofs << "\n\\end_body\n";
972 // Write marker that shows file is complete
973 ofs << "\\end_document" << endl;
975 // Shouldn't really be needed....
978 // how to check if close went ok?
979 // Following is an attempt... (BE 20001011)
981 // good() returns false if any error occured, including some
983 // bad() returns true if something bad happened in the buffer,
984 // which should include file system full errors.
989 lyxerr << "File was not closed properly." << endl;
996 bool Buffer::makeLaTeXFile(FileName const & fname,
997 string const & original_path,
998 OutputParams const & runparams,
999 bool output_preamble, bool output_body)
1001 string const encoding = runparams.encoding->iconvName();
1002 LYXERR(Debug::LATEX) << "makeLaTeXFile encoding: "
1003 << encoding << "..." << endl;
1005 odocfstream ofs(encoding);
1006 if (!openFileWrite(ofs, fname))
1009 //TexStream ts(ofs.rdbuf(), &texrow());
1011 bool failed_export = false;
1014 writeLaTeXSource(ofs, original_path,
1015 runparams, output_preamble, output_body);
1017 catch (iconv_codecvt_facet_exception & e) {
1018 lyxerr << "Caught iconv exception: " << e.what() << endl;
1019 failed_export = true;
1021 catch (std::exception const & e) {
1022 lyxerr << "Caught \"normal\" exception: " << e.what() << endl;
1023 failed_export = true;
1026 lyxerr << "Caught some really weird exception..." << endl;
1027 LyX::cref().emergencyCleanup();
1033 failed_export = true;
1034 lyxerr << "File '" << fname << "' was not closed properly." << endl;
1037 if (failed_export) {
1038 Alert::error(_("Encoding error"),
1039 _("Some characters of your document are probably not "
1040 "representable in the chosen encoding.\n"
1041 "Changing the document encoding to utf8 could help."));
1048 void Buffer::writeLaTeXSource(odocstream & os,
1049 string const & original_path,
1050 OutputParams const & runparams_in,
1051 bool const output_preamble, bool const output_body)
1053 OutputParams runparams = runparams_in;
1055 // validate the buffer.
1056 LYXERR(Debug::LATEX) << " Validating buffer..." << endl;
1057 LaTeXFeatures features(*this, params(), runparams);
1059 LYXERR(Debug::LATEX) << " Buffer validation done." << endl;
1061 // The starting paragraph of the coming rows is the
1062 // first paragraph of the document. (Asger)
1063 if (output_preamble && runparams.nice) {
1064 os << "%% LyX " << lyx_version << " created this file. "
1065 "For more info, see http://www.lyx.org/.\n"
1066 "%% Do not edit unless you really know what "
1071 LYXERR(Debug::INFO) << "lyx document header finished" << endl;
1072 // There are a few differences between nice LaTeX and usual files:
1073 // usual is \batchmode and has a
1074 // special input@path to allow the including of figures
1075 // with either \input or \includegraphics (what figinsets do).
1076 // input@path is set when the actual parameter
1077 // original_path is set. This is done for usual tex-file, but not
1078 // for nice-latex-file. (Matthias 250696)
1079 // Note that input@path is only needed for something the user does
1080 // in the preamble, included .tex files or ERT, files included by
1081 // LyX work without it.
1082 if (output_preamble) {
1083 if (!runparams.nice) {
1084 // code for usual, NOT nice-latex-file
1085 os << "\\batchmode\n"; // changed
1086 // from \nonstopmode
1089 if (!original_path.empty()) {
1091 // We don't know the encoding of inputpath
1092 docstring const inputpath = from_utf8(latex_path(original_path));
1093 os << "\\makeatletter\n"
1094 << "\\def\\input@path{{"
1095 << inputpath << "/}}\n"
1096 << "\\makeatother\n";
1102 // Write the preamble
1103 runparams.use_babel = params().writeLaTeX(os, features, texrow());
1109 os << "\\begin{document}\n";
1111 } // output_preamble
1113 texrow().start(paragraphs().begin()->id(), 0);
1115 LYXERR(Debug::INFO) << "preamble finished, now the body." << endl;
1117 if (!lyxrc.language_auto_begin &&
1118 !params().language->babel().empty()) {
1120 os << from_utf8(subst(lyxrc.language_command_begin,
1122 params().language->babel()))
1127 Encoding const & encoding = params().encoding();
1128 if (encoding.package() == Encoding::CJK) {
1129 // Open a CJK environment, since in contrast to the encodings
1130 // handled by inputenc the document encoding is not set in
1131 // the preamble if it is handled by CJK.sty.
1132 os << "\\begin{CJK}{" << from_ascii(encoding.latexName())
1137 // if we are doing a real file with body, even if this is the
1138 // child of some other buffer, let's cut the link here.
1139 // This happens for example if only a child document is printed.
1140 string save_parentname;
1141 if (output_preamble) {
1142 save_parentname = params().parentname;
1143 params().parentname.erase();
1146 loadChildDocuments();
1149 latexParagraphs(*this, paragraphs(), os, texrow(), runparams);
1151 // Restore the parenthood if needed
1152 if (output_preamble)
1153 params().parentname = save_parentname;
1155 // add this just in case after all the paragraphs
1159 if (encoding.package() == Encoding::CJK) {
1160 // Close the open CJK environment.
1161 // latexParagraphs will have opened one even if the last text
1163 os << "\\end{CJK}\n";
1167 if (!lyxrc.language_auto_end &&
1168 !params().language->babel().empty()) {
1169 os << from_utf8(subst(lyxrc.language_command_end,
1171 params().language->babel()))
1176 if (output_preamble) {
1177 os << "\\end{document}\n";
1180 LYXERR(Debug::LATEX) << "makeLaTeXFile...done" << endl;
1182 LYXERR(Debug::LATEX) << "LaTeXFile for inclusion made."
1185 runparams_in.encoding = runparams.encoding;
1187 // Just to be sure. (Asger)
1190 LYXERR(Debug::INFO) << "Finished making LaTeX file." << endl;
1191 LYXERR(Debug::INFO) << "Row count was " << texrow().rows() - 1
1196 bool Buffer::isLatex() const
1198 return params().getTextClass().outputType() == LATEX;
1202 bool Buffer::isLiterate() const
1204 return params().getTextClass().outputType() == LITERATE;
1208 bool Buffer::isDocBook() const
1210 return params().getTextClass().outputType() == DOCBOOK;
1214 void Buffer::makeDocBookFile(FileName const & fname,
1215 OutputParams const & runparams,
1216 bool const body_only)
1218 LYXERR(Debug::LATEX) << "makeDocBookFile..." << endl;
1222 if (!openFileWrite(ofs, fname))
1225 writeDocBookSource(ofs, fname.absFilename(), runparams, body_only);
1229 lyxerr << "File '" << fname << "' was not closed properly." << endl;
1233 void Buffer::writeDocBookSource(odocstream & os, string const & fname,
1234 OutputParams const & runparams,
1235 bool const only_body)
1237 LaTeXFeatures features(*this, params(), runparams);
1242 TextClass const & tclass = params().getTextClass();
1243 string const top_element = tclass.latexname();
1246 if (runparams.flavor == OutputParams::XML)
1247 os << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
1250 os << "<!DOCTYPE " << from_ascii(top_element) << ' ';
1253 if (! tclass.class_header().empty())
1254 os << from_ascii(tclass.class_header());
1255 else if (runparams.flavor == OutputParams::XML)
1256 os << "PUBLIC \"-//OASIS//DTD DocBook XML//EN\" "
1257 << "\"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd\"";
1259 os << " PUBLIC \"-//OASIS//DTD DocBook V4.2//EN\"";
1261 docstring preamble = from_utf8(params().preamble);
1262 if (runparams.flavor != OutputParams::XML ) {
1263 preamble += "<!ENTITY % output.print.png \"IGNORE\">\n";
1264 preamble += "<!ENTITY % output.print.pdf \"IGNORE\">\n";
1265 preamble += "<!ENTITY % output.print.eps \"IGNORE\">\n";
1266 preamble += "<!ENTITY % output.print.bmp \"IGNORE\">\n";
1269 string const name = runparams.nice
1270 ? changeExtension(absFileName(), ".sgml") : fname;
1271 preamble += features.getIncludedFiles(name);
1272 preamble += features.getLyXSGMLEntities();
1274 if (!preamble.empty()) {
1275 os << "\n [ " << preamble << " ]";
1280 string top = top_element;
1282 if (runparams.flavor == OutputParams::XML)
1283 top += params().language->code();
1285 top += params().language->code().substr(0,2);
1288 if (!params().options.empty()) {
1290 top += params().options;
1293 os << "<!-- " << ((runparams.flavor == OutputParams::XML)? "XML" : "SGML")
1294 << " file was created by LyX " << lyx_version
1295 << "\n See http://www.lyx.org/ for more information -->\n";
1297 params().getTextClass().counters().reset();
1299 loadChildDocuments();
1301 sgml::openTag(os, top);
1303 docbookParagraphs(paragraphs(), *this, os, runparams);
1304 sgml::closeTag(os, top_element);
1308 // chktex should be run with these flags disabled: 3, 22, 25, 30, 38(?)
1309 // Other flags: -wall -v0 -x
1310 int Buffer::runChktex()
1314 // get LaTeX-Filename
1315 FileName const path(temppath());
1316 string const name = addName(path.absFilename(), latexName());
1317 string const org_path = filePath();
1319 support::Path p(path); // path to LaTeX file
1320 message(_("Running chktex..."));
1322 // Generate the LaTeX file if neccessary
1323 OutputParams runparams(¶ms().encoding());
1324 runparams.flavor = OutputParams::LATEX;
1325 runparams.nice = false;
1326 makeLaTeXFile(FileName(name), org_path, runparams);
1329 Chktex chktex(lyxrc.chktex_command, onlyFilename(name), filePath());
1330 int const res = chktex.run(terr); // run chktex
1333 Alert::error(_("chktex failure"),
1334 _("Could not run chktex successfully."));
1335 } else if (res > 0) {
1336 ErrorList & errorList = pimpl_->errorLists["ChkTeX"];
1337 // Clear out old errors
1339 // Fill-in the error list with the TeX errors
1340 bufferErrors(*this, terr, errorList);
1351 void Buffer::validate(LaTeXFeatures & features) const
1353 TextClass const & tclass = params().getTextClass();
1355 if (params().outputChanges) {
1356 bool dvipost = LaTeXFeatures::isAvailable("dvipost");
1357 bool xcolorsoul = LaTeXFeatures::isAvailable("soul") &&
1358 LaTeXFeatures::isAvailable("xcolor");
1360 if (features.runparams().flavor == OutputParams::LATEX) {
1362 features.require("ct-dvipost");
1363 features.require("dvipost");
1364 } else if (xcolorsoul) {
1365 features.require("ct-xcolor-soul");
1366 features.require("soul");
1367 features.require("xcolor");
1369 features.require("ct-none");
1371 } else if (features.runparams().flavor == OutputParams::PDFLATEX ) {
1373 features.require("ct-xcolor-soul");
1374 features.require("soul");
1375 features.require("xcolor");
1376 features.require("pdfcolmk"); // improves color handling in PDF output
1378 features.require("ct-none");
1383 // AMS Style is at document level
1384 if (params().use_amsmath == BufferParams::package_on
1385 || tclass.provides("amsmath"))
1386 features.require("amsmath");
1387 if (params().use_esint == BufferParams::package_on)
1388 features.require("esint");
1390 loadChildDocuments();
1392 for_each(paragraphs().begin(), paragraphs().end(),
1393 boost::bind(&Paragraph::validate, _1, boost::ref(features)));
1395 // the bullet shapes are buffer level not paragraph level
1396 // so they are tested here
1397 for (int i = 0; i < 4; ++i) {
1398 if (params().user_defined_bullet(i) != ITEMIZE_DEFAULTS[i]) {
1399 int const font = params().user_defined_bullet(i).getFont();
1401 int const c = params()
1402 .user_defined_bullet(i)
1409 features.require("latexsym");
1411 } else if (font == 1) {
1412 features.require("amssymb");
1413 } else if ((font >= 2 && font <= 5)) {
1414 features.require("pifont");
1419 if (lyxerr.debugging(Debug::LATEX)) {
1420 features.showStruct();
1425 void Buffer::getLabelList(vector<docstring> & list) const
1427 /// if this is a child document and the parent is already loaded
1428 /// Use the parent's list instead [ale990407]
1429 Buffer const * tmp = masterBuffer();
1431 lyxerr << "masterBuffer() failed!" << endl;
1435 tmp->getLabelList(list);
1439 loadChildDocuments();
1441 for (InsetIterator it = inset_iterator_begin(inset()); it; ++it)
1442 it.nextInset()->getLabelList(*this, list);
1446 void Buffer::updateBibfilesCache()
1448 // if this is a child document and the parent is already loaded
1449 // update the parent's cache instead
1450 Buffer * tmp = masterBuffer();
1453 tmp->updateBibfilesCache();
1457 bibfilesCache_.clear();
1458 for (InsetIterator it = inset_iterator_begin(inset()); it; ++it) {
1459 if (it->lyxCode() == BIBTEX_CODE) {
1460 InsetBibtex const & inset =
1461 static_cast<InsetBibtex const &>(*it);
1462 vector<FileName> const bibfiles = inset.getFiles(*this);
1463 bibfilesCache_.insert(bibfilesCache_.end(),
1466 } else if (it->lyxCode() == INCLUDE_CODE) {
1467 InsetInclude & inset =
1468 static_cast<InsetInclude &>(*it);
1469 inset.updateBibfilesCache(*this);
1470 vector<FileName> const & bibfiles =
1471 inset.getBibfilesCache(*this);
1472 bibfilesCache_.insert(bibfilesCache_.end(),
1480 vector<FileName> const & Buffer::getBibfilesCache() const
1482 // if this is a child document and the parent is already loaded
1483 // use the parent's cache instead
1484 Buffer const * tmp = masterBuffer();
1487 return tmp->getBibfilesCache();
1489 // We update the cache when first used instead of at loading time.
1490 if (bibfilesCache_.empty())
1491 const_cast<Buffer *>(this)->updateBibfilesCache();
1493 return bibfilesCache_;
1497 bool Buffer::isDepClean(string const & name) const
1499 DepClean::const_iterator const it = pimpl_->dep_clean.find(name);
1500 if (it == pimpl_->dep_clean.end())
1506 void Buffer::markDepClean(string const & name)
1508 pimpl_->dep_clean[name] = true;
1512 bool Buffer::dispatch(string const & command, bool * result)
1514 return dispatch(lyxaction.lookupFunc(command), result);
1518 bool Buffer::dispatch(FuncRequest const & func, bool * result)
1520 bool dispatched = true;
1522 switch (func.action) {
1523 case LFUN_BUFFER_EXPORT: {
1524 bool const tmp = doExport(to_utf8(func.argument()), false);
1537 void Buffer::changeLanguage(Language const * from, Language const * to)
1542 for_each(par_iterator_begin(),
1544 bind(&Paragraph::changeLanguage, _1, params(), from, to));
1548 bool Buffer::isMultiLingual() const
1550 ParConstIterator end = par_iterator_end();
1551 for (ParConstIterator it = par_iterator_begin(); it != end; ++it)
1552 if (it->isMultiLingual(params()))
1559 ParIterator Buffer::getParFromID(int const id) const
1561 ParConstIterator it = par_iterator_begin();
1562 ParConstIterator const end = par_iterator_end();
1565 // John says this is called with id == -1 from undo
1566 lyxerr << "getParFromID(), id: " << id << endl;
1570 for (; it != end; ++it)
1578 bool Buffer::hasParWithID(int const id) const
1580 ParConstIterator const it = getParFromID(id);
1581 return it != par_iterator_end();
1585 ParIterator Buffer::par_iterator_begin()
1587 return lyx::par_iterator_begin(inset());
1591 ParIterator Buffer::par_iterator_end()
1593 return lyx::par_iterator_end(inset());
1597 ParConstIterator Buffer::par_iterator_begin() const
1599 return lyx::par_const_iterator_begin(inset());
1603 ParConstIterator Buffer::par_iterator_end() const
1605 return lyx::par_const_iterator_end(inset());
1609 Language const * Buffer::language() const
1611 return params().language;
1615 docstring const Buffer::B_(string const & l10n) const
1617 return params().B_(l10n);
1621 bool Buffer::isClean() const
1623 return pimpl_->lyx_clean;
1627 bool Buffer::isBakClean() const
1629 return pimpl_->bak_clean;
1633 bool Buffer::isExternallyModified(CheckMethod method) const
1635 BOOST_ASSERT(pimpl_->filename.exists());
1636 // if method == timestamp, check timestamp before checksum
1637 return (method == checksum_method
1638 || pimpl_->timestamp_ != pimpl_->filename.lastModified())
1639 && pimpl_->checksum_ != sum(pimpl_->filename);
1643 void Buffer::saveCheckSum(FileName const & file) const
1645 if (file.exists()) {
1646 pimpl_->timestamp_ = file.lastModified();
1647 pimpl_->checksum_ = sum(file);
1649 // in the case of save to a new file.
1650 pimpl_->timestamp_ = 0;
1651 pimpl_->checksum_ = 0;
1656 void Buffer::markClean() const
1658 if (!pimpl_->lyx_clean) {
1659 pimpl_->lyx_clean = true;
1662 // if the .lyx file has been saved, we don't need an
1664 pimpl_->bak_clean = true;
1668 void Buffer::markBakClean() const
1670 pimpl_->bak_clean = true;
1674 void Buffer::setUnnamed(bool flag)
1676 pimpl_->unnamed = flag;
1680 bool Buffer::isUnnamed() const
1682 return pimpl_->unnamed;
1686 // FIXME: this function should be moved to buffer_pimpl.C
1687 void Buffer::markDirty()
1689 if (pimpl_->lyx_clean) {
1690 pimpl_->lyx_clean = false;
1693 pimpl_->bak_clean = false;
1695 DepClean::iterator it = pimpl_->dep_clean.begin();
1696 DepClean::const_iterator const end = pimpl_->dep_clean.end();
1698 for (; it != end; ++it)
1703 string Buffer::absFileName() const
1705 return pimpl_->filename.absFilename();
1709 string const & Buffer::filePath() const
1711 return params().filepath;
1715 bool Buffer::isReadonly() const
1717 return pimpl_->read_only;
1721 void Buffer::setParentName(string const & name)
1723 if (name == pimpl_->filename.absFilename())
1724 // Avoids recursive include.
1725 params().parentname.clear();
1727 params().parentname = name;
1731 Buffer const * Buffer::masterBuffer() const
1733 if (!params().parentname.empty()
1734 && theBufferList().exists(params().parentname)) {
1735 Buffer const * buf = theBufferList().getBuffer(params().parentname);
1736 //We need to check if the parent is us...
1737 //FIXME RECURSIVE INCLUDE
1738 //This is not sufficient, since recursive includes could be downstream.
1739 if (buf && buf != this)
1740 return buf->masterBuffer();
1747 Buffer * Buffer::masterBuffer()
1749 if (!params().parentname.empty()
1750 && theBufferList().exists(params().parentname)) {
1751 Buffer * buf = theBufferList().getBuffer(params().parentname);
1752 //We need to check if the parent is us...
1753 //FIXME RECURSIVE INCLUDE
1754 //This is not sufficient, since recursive includes could be downstream.
1755 if (buf && buf != this)
1756 return buf->masterBuffer();
1763 MacroData const & Buffer::getMacro(docstring const & name) const
1765 return pimpl_->macros.get(name);
1769 bool Buffer::hasMacro(docstring const & name) const
1771 return pimpl_->macros.has(name);
1775 void Buffer::insertMacro(docstring const & name, MacroData const & data)
1777 MacroTable::globalMacros().insert(name, data);
1778 pimpl_->macros.insert(name, data);
1782 void Buffer::buildMacros()
1784 // Start with global table.
1785 pimpl_->macros = MacroTable::globalMacros();
1788 ParagraphList const & pars = text().paragraphs();
1789 for (size_t i = 0, n = pars.size(); i != n; ++i) {
1790 //lyxerr << "searching main par " << i
1791 // << " for macro definitions" << std::endl;
1792 InsetList const & insets = pars[i].insetList();
1793 InsetList::const_iterator it = insets.begin();
1794 InsetList::const_iterator end = insets.end();
1795 for ( ; it != end; ++it) {
1796 //lyxerr << "found inset code " << it->inset->lyxCode() << std::endl;
1797 if (it->inset->lyxCode() == MATHMACRO_CODE) {
1798 MathMacroTemplate const & mac
1799 = static_cast<MathMacroTemplate const &>(*it->inset);
1800 insertMacro(mac.name(), mac.asMacroData());
1807 void Buffer::changeRefsIfUnique(docstring const & from, docstring const & to,
1810 //FIXME: This does not work for child documents yet.
1811 BOOST_ASSERT(code == CITE_CODE || code == REF_CODE);
1812 // Check if the label 'from' appears more than once
1813 vector<docstring> labels;
1815 if (code == CITE_CODE) {
1817 keys.fillWithBibKeys(this);
1818 BiblioInfo::const_iterator bit = keys.begin();
1819 BiblioInfo::const_iterator bend = keys.end();
1821 for (; bit != bend; ++bit)
1823 labels.push_back(bit->first);
1825 getLabelList(labels);
1827 if (std::count(labels.begin(), labels.end(), from) > 1)
1830 for (InsetIterator it = inset_iterator_begin(inset()); it; ++it) {
1831 if (it->lyxCode() == code) {
1832 InsetCommand & inset = static_cast<InsetCommand &>(*it);
1833 inset.replaceContents(to_utf8(from), to_utf8(to));
1839 void Buffer::getSourceCode(odocstream & os, pit_type par_begin,
1840 pit_type par_end, bool full_source)
1842 OutputParams runparams(¶ms().encoding());
1843 runparams.nice = true;
1844 runparams.flavor = OutputParams::LATEX;
1845 runparams.linelen = lyxrc.plaintext_linelen;
1846 // No side effect of file copying and image conversion
1847 runparams.dryrun = true;
1851 os << "% " << _("Preview source code") << "\n\n";
1855 writeLaTeXSource(os, filePath(), runparams, true, true);
1857 writeDocBookSource(os, absFileName(), runparams, false);
1860 runparams.par_begin = par_begin;
1861 runparams.par_end = par_end;
1862 if (par_begin + 1 == par_end)
1864 << bformat(_("Preview source code for paragraph %1$d"), par_begin)
1868 << bformat(_("Preview source code from paragraph %1$s to %2$s"),
1869 convert<docstring>(par_begin),
1870 convert<docstring>(par_end - 1))
1874 // output paragraphs
1876 latexParagraphs(*this, paragraphs(), os, texrow(), runparams);
1879 docbookParagraphs(paragraphs(), *this, os, runparams);
1885 ErrorList const & Buffer::errorList(string const & type) const
1887 static ErrorList const emptyErrorList;
1888 std::map<string, ErrorList>::const_iterator I = pimpl_->errorLists.find(type);
1889 if (I == pimpl_->errorLists.end())
1890 return emptyErrorList;
1896 ErrorList & Buffer::errorList(string const & type)
1898 return pimpl_->errorLists[type];
1902 void Buffer::structureChanged() const
1905 gui_->structureChanged();
1909 void Buffer::embeddingChanged() const
1912 gui_->embeddingChanged();
1916 void Buffer::errors(std::string const & err) const
1923 void Buffer::message(docstring const & msg) const
1930 void Buffer::busy(bool on) const
1937 void Buffer::readonly(bool on) const
1944 void Buffer::updateTitles() const
1947 gui_->updateTitles();
1951 void Buffer::resetAutosaveTimers() const
1954 gui_->resetAutosaveTimers();
1958 void Buffer::setGuiDelegate(frontend::GuiBufferDelegate * gui)
1967 class AutoSaveBuffer : public support::ForkedProcess {
1970 AutoSaveBuffer(Buffer const & buffer, FileName const & fname)
1971 : buffer_(buffer), fname_(fname) {}
1973 virtual boost::shared_ptr<ForkedProcess> clone() const
1975 return boost::shared_ptr<ForkedProcess>(new AutoSaveBuffer(*this));
1980 command_ = to_utf8(bformat(_("Auto-saving %1$s"),
1981 from_utf8(fname_.absFilename())));
1982 return run(DontWait);
1986 virtual int generateChild();
1988 Buffer const & buffer_;
1993 #if !defined (HAVE_FORK)
1997 int AutoSaveBuffer::generateChild()
1999 // tmp_ret will be located (usually) in /tmp
2000 // will that be a problem?
2001 pid_t const pid = fork();
2002 // If you want to debug the autosave
2003 // you should set pid to -1, and comment out the fork.
2004 if (pid == 0 || pid == -1) {
2005 // pid = -1 signifies that lyx was unable
2006 // to fork. But we will do the save
2008 bool failed = false;
2010 FileName const tmp_ret(tempName(FileName(), "lyxauto"));
2011 if (!tmp_ret.empty()) {
2012 buffer_.writeFile(tmp_ret);
2013 // assume successful write of tmp_ret
2014 if (!rename(tmp_ret, fname_)) {
2016 // most likely couldn't move between
2017 // filesystems unless write of tmp_ret
2018 // failed so remove tmp file (if it
2027 // failed to write/rename tmp_ret so try writing direct
2028 if (!buffer_.writeFile(fname_)) {
2029 // It is dangerous to do this in the child,
2030 // but safe in the parent, so...
2031 if (pid == -1) // emit message signal.
2032 buffer_.message(_("Autosave failed!"));
2035 if (pid == 0) { // we are the child so...
2045 // Perfect target for a thread...
2046 void Buffer::autoSave() const
2048 if (isBakClean() || isReadonly()) {
2049 // We don't save now, but we'll try again later
2050 resetAutosaveTimers();
2054 // emit message signal.
2055 message(_("Autosaving current document..."));
2057 // create autosave filename
2058 string fname = filePath();
2060 fname += onlyFilename(absFileName());
2063 AutoSaveBuffer autosave(*this, FileName(fname));
2067 resetAutosaveTimers();
2071 /** Write a buffer to a new file name and rename the buffer
2072 according to the new file name.
2074 This function is e.g. used by menu callbacks and
2075 LFUN_BUFFER_WRITE_AS.
2077 If 'newname' is empty (the default), the user is asked via a
2078 dialog for the buffer's new name and location.
2080 If 'newname' is non-empty and has an absolute path, that is used.
2081 Otherwise the base directory of the buffer is used as the base
2082 for any relative path in 'newname'.
2085 bool Buffer::writeAs(string const & newname)
2087 string fname = absFileName();
2088 string const oldname = fname;
2090 if (newname.empty()) { /// No argument? Ask user through dialog
2093 FileDialog fileDlg(_("Choose a filename to save document as"),
2094 LFUN_BUFFER_WRITE_AS,
2095 make_pair(_("Documents|#o#O"),
2096 from_utf8(lyxrc.document_path)),
2097 make_pair(_("Templates|#T#t"),
2098 from_utf8(lyxrc.template_path)));
2100 if (!support::isLyXFilename(fname))
2103 support::FileFilterList const filter(_("LyX Documents (*.lyx)"));
2105 FileDialog::Result result =
2106 fileDlg.save(from_utf8(onlyPath(fname)),
2108 from_utf8(onlyFilename(fname)));
2110 if (result.first == FileDialog::Later)
2113 fname = to_utf8(result.second);
2118 // Make sure the absolute filename ends with appropriate suffix
2119 fname = makeAbsPath(fname).absFilename();
2120 if (!support::isLyXFilename(fname))
2124 fname = makeAbsPath(newname, onlyPath(oldname)).absFilename();
2126 if (FileName(fname).exists()) {
2127 docstring const file = makeDisplayPath(fname, 30);
2128 docstring text = bformat(_("The document %1$s already "
2129 "exists.\n\nDo you want to "
2130 "overwrite that document?"),
2132 int const ret = Alert::prompt(_("Overwrite document?"),
2133 text, 0, 1, _("&Overwrite"), _("&Cancel"));
2139 // Ok, change the name of the buffer
2142 bool unnamed = isUnnamed();
2144 saveCheckSum(FileName(fname));
2147 setFileName(oldname);
2148 setUnnamed(unnamed);
2149 saveCheckSum(FileName(oldname));
2153 removeAutosaveFile(oldname);
2158 bool Buffer::menuWrite()
2161 LyX::ref().session().lastFiles().add(FileName(absFileName()));
2165 // FIXME: we don't tell the user *WHY* the save failed !!
2167 docstring const file = makeDisplayPath(absFileName(), 30);
2169 docstring text = bformat(_("The document %1$s could not be saved.\n\n"
2170 "Do you want to rename the document and "
2171 "try again?"), file);
2172 int const ret = Alert::prompt(_("Rename and save?"),
2173 text, 0, 1, _("&Rename"), _("&Cancel"));
2182 void Buffer::loadChildDocuments() const
2184 bool parse_error = false;
2186 for (InsetIterator it = inset_iterator_begin(inset()); it; ++it) {
2187 if (it->lyxCode() != INCLUDE_CODE)
2189 InsetInclude const & inset = static_cast<InsetInclude const &>(*it);
2190 InsetCommandParams const & ip = inset.params();
2191 Buffer * child = loadIfNeeded(*this, ip);
2194 parse_error |= !child->errorList("Parse").empty();
2195 child->loadChildDocuments();
2198 if (use_gui && masterBuffer() == this)
2199 updateLabels(*this);
2203 string Buffer::bufferFormat() const
2213 bool Buffer::doExport(string const & format,
2214 bool put_in_tempdir, string & result_file)
2216 string backend_format;
2217 OutputParams runparams(¶ms().encoding());
2218 runparams.flavor = OutputParams::LATEX;
2219 runparams.linelen = lyxrc.plaintext_linelen;
2220 vector<string> backs = backends();
2221 if (find(backs.begin(), backs.end(), format) == backs.end()) {
2222 // Get shortest path to format
2223 Graph::EdgePath path;
2224 for (vector<string>::const_iterator it = backs.begin();
2225 it != backs.end(); ++it) {
2226 Graph::EdgePath p = theConverters().getPath(*it, format);
2227 if (!p.empty() && (path.empty() || p.size() < path.size())) {
2228 backend_format = *it;
2233 runparams.flavor = theConverters().getFlavor(path);
2235 Alert::error(_("Couldn't export file"),
2236 bformat(_("No information for exporting the format %1$s."),
2237 formats.prettyName(format)));
2241 backend_format = format;
2242 // FIXME: Don't hardcode format names here, but use a flag
2243 if (backend_format == "pdflatex")
2244 runparams.flavor = OutputParams::PDFLATEX;
2247 string filename = latexName(false);
2248 filename = addName(temppath(), filename);
2249 filename = changeExtension(filename,
2250 formats.extension(backend_format));
2252 // Plain text backend
2253 if (backend_format == "text")
2254 writePlaintextFile(*this, FileName(filename), runparams);
2256 else if (backend_format == "lyx")
2257 writeFile(FileName(filename));
2259 else if (isDocBook()) {
2260 runparams.nice = !put_in_tempdir;
2261 makeDocBookFile(FileName(filename), runparams);
2264 else if (backend_format == format) {
2265 runparams.nice = true;
2266 if (!makeLaTeXFile(FileName(filename), string(), runparams))
2268 } else if (!lyxrc.tex_allows_spaces
2269 && support::contains(filePath(), ' ')) {
2270 Alert::error(_("File name error"),
2271 _("The directory path to the document cannot contain spaces."));
2274 runparams.nice = false;
2275 if (!makeLaTeXFile(FileName(filename), filePath(), runparams))
2279 string const error_type = (format == "program")
2280 ? "Build" : bufferFormat();
2281 string const ext = formats.extension(format);
2282 FileName const tmp_result_file(changeExtension(filename, ext));
2283 bool const success = theConverters().convert(this, FileName(filename),
2284 tmp_result_file, FileName(absFileName()), backend_format, format,
2285 errorList(error_type));
2286 // Emit the signal to show the error list.
2287 if (format != backend_format)
2293 result_file = tmp_result_file.absFilename();
2295 result_file = changeExtension(absFileName(), ext);
2296 // We need to copy referenced files (e. g. included graphics
2297 // if format == "dvi") to the result dir.
2298 vector<ExportedFile> const files =
2299 runparams.exportdata->externalFiles(format);
2300 string const dest = onlyPath(result_file);
2301 CopyStatus status = SUCCESS;
2302 for (vector<ExportedFile>::const_iterator it = files.begin();
2303 it != files.end() && status != CANCEL; ++it) {
2305 formats.getFormatFromFile(it->sourceName);
2306 status = copyFile(fmt, it->sourceName,
2307 makeAbsPath(it->exportName, dest),
2308 it->exportName, status == FORCE);
2310 if (status == CANCEL) {
2311 message(_("Document export cancelled."));
2312 } else if (tmp_result_file.exists()) {
2313 // Finally copy the main file
2314 status = copyFile(format, tmp_result_file,
2315 FileName(result_file), result_file,
2317 message(bformat(_("Document exported as %1$s "
2319 formats.prettyName(format),
2320 makeDisplayPath(result_file)));
2322 // This must be a dummy converter like fax (bug 1888)
2323 message(bformat(_("Document exported as %1$s"),
2324 formats.prettyName(format)));
2332 bool Buffer::doExport(string const & format, bool put_in_tempdir)
2335 return doExport(format, put_in_tempdir, result_file);
2339 bool Buffer::preview(string const & format)
2342 if (!doExport(format, true, result_file))
2344 return formats.view(*this, FileName(result_file), format);
2348 bool Buffer::isExportable(string const & format) const
2350 vector<string> backs = backends();
2351 for (vector<string>::const_iterator it = backs.begin();
2352 it != backs.end(); ++it)
2353 if (theConverters().isReachable(*it, format))
2359 vector<Format const *> Buffer::exportableFormats(bool only_viewable) const
2361 vector<string> backs = backends();
2362 vector<Format const *> result =
2363 theConverters().getReachable(backs[0], only_viewable, true);
2364 for (vector<string>::const_iterator it = backs.begin() + 1;
2365 it != backs.end(); ++it) {
2366 vector<Format const *> r =
2367 theConverters().getReachable(*it, only_viewable, false);
2368 result.insert(result.end(), r.begin(), r.end());
2374 vector<string> Buffer::backends() const
2377 if (params().getTextClass().isTeXClassAvailable()) {
2378 v.push_back(bufferFormat());
2379 // FIXME: Don't hardcode format names here, but use a flag
2380 if (v.back() == "latex")
2381 v.push_back("pdflatex");
2383 v.push_back("text");