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::destroyDir;
133 using support::FileName;
134 using support::getFormatFromContents;
135 using support::libFileSearch;
136 using support::latex_path;
137 using support::ltrim;
138 using support::makeAbsPath;
139 using support::makeDisplayPath;
140 using support::makeLatexName;
141 using support::onlyFilename;
142 using support::onlyPath;
143 using support::quoteName;
144 using support::removeAutosaveFile;
145 using support::rename;
146 using support::runCommand;
147 using support::split;
148 using support::subst;
149 using support::tempName;
152 using support::suffixIs;
154 namespace Alert = frontend::Alert;
155 namespace os = support::os;
156 namespace fs = boost::filesystem;
160 int const LYX_FORMAT = 295; //Uwe: htmlurl, href
165 typedef std::map<string, bool> DepClean;
170 Impl(Buffer & parent, FileName const & file, bool readonly);
177 /// need to regenerate .tex?
181 mutable bool lyx_clean;
183 /// is autosave needed?
184 mutable bool bak_clean;
186 /// is this a unnamed file (New...)?
192 /// name of the file the buffer is associated with.
195 /** Set to true only when the file is fully loaded.
196 * Used to prevent the premature generation of previews
197 * and by the citation inset.
199 bool file_fully_loaded;
201 /// our Text that should be wrapped in an InsetText
208 TocBackend toc_backend;
210 /// Container for all sort of Buffer dependant errors.
211 map<string, ErrorList> errorLists;
213 /// all embedded files of this buffer
214 EmbeddedFiles embedded_files;
216 /// timestamp and checksum used to test if the file has been externally
217 /// modified. (Used to properly enable 'File->Revert to saved', bug 4114).
219 unsigned long checksum_;
222 frontend::WorkAreaManager * wa_;
229 Buffer::Impl::Impl(Buffer & parent, FileName const & file, bool readonly_)
230 : lyx_clean(true), bak_clean(true), unnamed(false), read_only(readonly_),
231 filename(file), file_fully_loaded(false), inset(params),
232 toc_backend(&parent), embedded_files(&parent), timestamp_(0),
233 checksum_(0), wa_(0), undo_(parent)
235 inset.setAutoBreakRows(true);
236 lyxvc.buffer(&parent);
237 temppath = createBufferTmpDir();
238 params.filepath = onlyPath(file.absFilename());
239 // FIXME: And now do something if temppath == string(), because we
240 // assume from now on that temppath points to a valid temp dir.
241 // See http://www.mail-archive.com/lyx-devel@lists.lyx.org/msg67406.html
244 wa_ = new frontend::WorkAreaManager;
248 Buffer::Buffer(string const & file, bool readonly)
249 : pimpl_(new Impl(*this, FileName(file), readonly)), gui_(0)
251 LYXERR(Debug::INFO) << "Buffer::Buffer()" << endl;
257 LYXERR(Debug::INFO) << "Buffer::~Buffer()" << endl;
258 // here the buffer should take care that it is
259 // saved properly, before it goes into the void.
261 Buffer * master = masterBuffer();
262 if (master != this && use_gui)
263 // We are closing buf which was a child document so we
264 // must update the labels and section numbering of its master
266 updateLabels(*master);
268 if (!temppath().empty() && !destroyDir(FileName(temppath()))) {
269 Alert::warning(_("Could not remove temporary directory"),
270 bformat(_("Could not remove the temporary directory %1$s"),
271 from_utf8(temppath())));
274 // Remove any previewed LaTeX snippets associated with this buffer.
275 graphics::Previews::get().removeLoader(*this);
278 pimpl_->wa_->closeAll();
285 void Buffer::changed() const
288 pimpl_->wa_->redrawAll();
292 frontend::WorkAreaManager & Buffer::workAreaManager() const
294 BOOST_ASSERT(pimpl_->wa_);
299 Text & Buffer::text() const
301 return const_cast<Text &>(pimpl_->inset.text_);
305 Inset & Buffer::inset() const
307 return const_cast<InsetText &>(pimpl_->inset);
311 BufferParams & Buffer::params()
313 return pimpl_->params;
317 BufferParams const & Buffer::params() const
319 return pimpl_->params;
323 ParagraphList & Buffer::paragraphs()
325 return text().paragraphs();
329 ParagraphList const & Buffer::paragraphs() const
331 return text().paragraphs();
335 LyXVC & Buffer::lyxvc()
337 return pimpl_->lyxvc;
341 LyXVC const & Buffer::lyxvc() const
343 return pimpl_->lyxvc;
347 string const & Buffer::temppath() const
349 return pimpl_->temppath;
353 TexRow & Buffer::texrow()
355 return pimpl_->texrow;
359 TexRow const & Buffer::texrow() const
361 return pimpl_->texrow;
365 TocBackend & Buffer::tocBackend()
367 return pimpl_->toc_backend;
371 TocBackend const & Buffer::tocBackend() const
373 return pimpl_->toc_backend;
377 EmbeddedFiles & Buffer::embeddedFiles()
379 return pimpl_->embedded_files;
383 EmbeddedFiles const & Buffer::embeddedFiles() const
385 return pimpl_->embedded_files;
389 Undo & Buffer::undo()
391 return pimpl_->undo_;
395 string Buffer::latexName(bool const no_path) const
397 string const name = changeExtension(makeLatexName(absFileName()), ".tex");
398 return no_path ? onlyFilename(name) : name;
402 pair<Buffer::LogType, string> Buffer::logName() const
404 string const filename = latexName(false);
406 if (filename.empty())
407 return make_pair(Buffer::latexlog, string());
409 string const path = temppath();
411 FileName const fname(addName(temppath(),
412 onlyFilename(changeExtension(filename,
414 FileName const bname(
415 addName(path, onlyFilename(
416 changeExtension(filename,
417 formats.extension("literate") + ".out"))));
419 // If no Latex log or Build log is newer, show Build log
421 if (bname.exists() &&
422 (!fname.exists() || fname.lastModified() < bname.lastModified())) {
423 LYXERR(Debug::FILES) << "Log name calculated as: " << bname << endl;
424 return make_pair(Buffer::buildlog, bname.absFilename());
426 LYXERR(Debug::FILES) << "Log name calculated as: " << fname << endl;
427 return make_pair(Buffer::latexlog, fname.absFilename());
431 void Buffer::setReadonly(bool const flag)
433 if (pimpl_->read_only != flag) {
434 pimpl_->read_only = flag;
440 void Buffer::setFileName(string const & newfile)
442 pimpl_->filename = makeAbsPath(newfile);
443 params().filepath = onlyPath(pimpl_->filename.absFilename());
444 setReadonly(pimpl_->filename.isReadOnly());
449 int Buffer::readHeader(Lexer & lex)
451 int unknown_tokens = 0;
453 int begin_header_line = -1;
455 // Initialize parameters that may be/go lacking in header:
456 params().branchlist().clear();
457 params().preamble.erase();
458 params().options.erase();
459 params().float_placement.erase();
460 params().paperwidth.erase();
461 params().paperheight.erase();
462 params().leftmargin.erase();
463 params().rightmargin.erase();
464 params().topmargin.erase();
465 params().bottommargin.erase();
466 params().headheight.erase();
467 params().headsep.erase();
468 params().footskip.erase();
469 params().listings_params.clear();
470 params().clearLayoutModules();
471 params().pdfoptions().clear();
473 for (int i = 0; i < 4; ++i) {
474 params().user_defined_bullet(i) = ITEMIZE_DEFAULTS[i];
475 params().temp_bullet(i) = ITEMIZE_DEFAULTS[i];
478 ErrorList & errorList = pimpl_->errorLists["Parse"];
482 string const token = lex.getString();
487 if (token == "\\end_header")
491 if (token == "\\begin_header") {
492 begin_header_line = line;
496 LYXERR(Debug::PARSER) << "Handling document header token: `"
497 << token << '\'' << endl;
499 string unknown = params().readToken(lex, token);
500 if (!unknown.empty()) {
501 if (unknown[0] != '\\' && token == "\\textclass") {
502 Alert::warning(_("Unknown document class"),
503 bformat(_("Using the default document class, because the "
504 "class %1$s is unknown."), from_utf8(unknown)));
507 docstring const s = bformat(_("Unknown token: "
511 errorList.push_back(ErrorItem(_("Document header error"),
516 if (begin_header_line) {
517 docstring const s = _("\\begin_header is missing");
518 errorList.push_back(ErrorItem(_("Document header error"),
522 return unknown_tokens;
527 // changed to be public and have one parameter
528 // Returns false if "\end_document" is not read (Asger)
529 bool Buffer::readDocument(Lexer & lex)
531 ErrorList & errorList = pimpl_->errorLists["Parse"];
535 string const token = lex.getString();
536 if (token != "\\begin_document") {
537 docstring const s = _("\\begin_document is missing");
538 errorList.push_back(ErrorItem(_("Document header error"),
542 // we are reading in a brand new document
543 BOOST_ASSERT(paragraphs().empty());
546 TextClass const & baseClass = textclasslist[params().getBaseClass()];
547 if (!baseClass.load(filePath())) {
548 string theclass = baseClass.name();
549 Alert::error(_("Can't load document class"), bformat(
550 _("Using the default document class, because the "
551 "class %1$s could not be loaded."), from_utf8(theclass)));
552 params().setBaseClass(defaultTextclass());
555 if (params().outputChanges) {
556 bool dvipost = LaTeXFeatures::isAvailable("dvipost");
557 bool xcolorsoul = LaTeXFeatures::isAvailable("soul") &&
558 LaTeXFeatures::isAvailable("xcolor");
560 if (!dvipost && !xcolorsoul) {
561 Alert::warning(_("Changes not shown in LaTeX output"),
562 _("Changes will not be highlighted in LaTeX output, "
563 "because neither dvipost nor xcolor/soul are installed.\n"
564 "Please install these packages or redefine "
565 "\\lyxadded and \\lyxdeleted in the LaTeX preamble."));
566 } else if (!xcolorsoul) {
567 Alert::warning(_("Changes not shown in LaTeX output"),
568 _("Changes will not be highlighted in LaTeX output "
569 "when using pdflatex, because xcolor and soul are not installed.\n"
570 "Please install both packages or redefine "
571 "\\lyxadded and \\lyxdeleted in the LaTeX preamble."));
574 // read manifest after header
575 embeddedFiles().readManifest(lex, errorList);
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 = getFormatFromContents(filename);
674 if (format == "zip") {
675 // decompress to a temp directory
676 LYXERR(Debug::FILES) << filename << " is in zip format. Unzip to " << temppath() << endl;
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 = getFormatFromContents(fname);
688 if (format == "gzip" || format == "zip" || format == "compress") {
689 params().compressed = true;
692 // remove dummy empty par
693 paragraphs().clear();
696 if (readFile(lex, fname) != success)
703 bool Buffer::isFullyLoaded() const
705 return pimpl_->file_fully_loaded;
709 void Buffer::setFullyLoaded(bool value)
711 pimpl_->file_fully_loaded = value;
715 Buffer::ReadStatus Buffer::readFile(Lexer & lex, FileName const & filename,
718 BOOST_ASSERT(!filename.empty());
721 Alert::error(_("Document could not be read"),
722 bformat(_("%1$s could not be read."), from_utf8(filename.absFilename())));
727 string const token = lex.getString();
730 Alert::error(_("Document could not be read"),
731 bformat(_("%1$s could not be read."), from_utf8(filename.absFilename())));
735 // the first token _must_ be...
736 if (token != "\\lyxformat") {
737 lyxerr << "Token: " << token << endl;
739 Alert::error(_("Document format failure"),
740 bformat(_("%1$s is not a LyX document."),
741 from_utf8(filename.absFilename())));
746 string tmp_format = lex.getString();
747 //lyxerr << "LyX Format: `" << tmp_format << '\'' << endl;
748 // if present remove ".," from string.
749 string::size_type dot = tmp_format.find_first_of(".,");
750 //lyxerr << " dot found at " << dot << endl;
751 if (dot != string::npos)
752 tmp_format.erase(dot, 1);
753 int const file_format = convert<int>(tmp_format);
754 //lyxerr << "format: " << file_format << endl;
756 // save timestamp and checksum of the original disk file, making sure
757 // to not overwrite them with those of the file created in the tempdir
758 // when it has to be converted to the current format.
759 if (!pimpl_->checksum_) {
760 // Save the timestamp and checksum of disk file. If filename is an
761 // emergency file, save the timestamp and sum of the original lyx file
762 // because isExternallyModified will check for this file. (BUG4193)
763 string diskfile = filename.toFilesystemEncoding();
764 if (suffixIs(diskfile, ".emergency"))
765 diskfile = diskfile.substr(0, diskfile.size() - 10);
766 saveCheckSum(FileName(diskfile));
769 if (file_format != LYX_FORMAT) {
772 // lyx2lyx would fail
775 FileName const tmpfile(tempName());
776 if (tmpfile.empty()) {
777 Alert::error(_("Conversion failed"),
778 bformat(_("%1$s is from a different"
779 " version of LyX, but a temporary"
780 " file for converting it could"
782 from_utf8(filename.absFilename())));
785 FileName const lyx2lyx = libFileSearch("lyx2lyx", "lyx2lyx");
786 if (lyx2lyx.empty()) {
787 Alert::error(_("Conversion script not found"),
788 bformat(_("%1$s is from a different"
789 " version of LyX, but the"
790 " conversion script lyx2lyx"
791 " could not be found."),
792 from_utf8(filename.absFilename())));
795 ostringstream command;
796 command << os::python()
797 << ' ' << quoteName(lyx2lyx.toFilesystemEncoding())
798 << " -t " << convert<string>(LYX_FORMAT)
799 << " -o " << quoteName(tmpfile.toFilesystemEncoding())
800 << ' ' << quoteName(filename.toFilesystemEncoding());
801 string const command_str = command.str();
803 LYXERR(Debug::INFO) << "Running '"
804 << command_str << '\''
807 cmd_ret const ret = runCommand(command_str);
808 if (ret.first != 0) {
809 Alert::error(_("Conversion script failed"),
810 bformat(_("%1$s is from a different version"
811 " of LyX, but the lyx2lyx script"
812 " failed to convert it."),
813 from_utf8(filename.absFilename())));
816 bool const ret = readFile(tmpfile);
817 // Do stuff with tmpfile name and buffer name here.
818 return ret ? success : failure;
823 if (readDocument(lex)) {
824 Alert::error(_("Document format failure"),
825 bformat(_("%1$s ended unexpectedly, which means"
826 " that it is probably corrupted."),
827 from_utf8(filename.absFilename())));
830 //lyxerr << "removing " << MacroTable::localMacros().size()
831 // << " temporary macro entries" << endl;
832 //MacroTable::localMacros().clear();
834 pimpl_->file_fully_loaded = true;
839 // Should probably be moved to somewhere else: BufferView? LyXView?
840 bool Buffer::save() const
842 // We don't need autosaves in the immediate future. (Asger)
843 resetAutosaveTimers();
845 string const encodedFilename = pimpl_->filename.toFilesystemEncoding();
848 bool madeBackup = false;
850 // make a backup if the file already exists
851 if (lyxrc.make_backup && fs::exists(encodedFilename)) {
852 backupName = FileName(absFileName() + '~');
853 if (!lyxrc.backupdir_path.empty())
854 backupName = FileName(addName(lyxrc.backupdir_path,
855 subst(os::internal_path(backupName.absFilename()), '/', '!')));
858 fs::copy_file(encodedFilename, backupName.toFilesystemEncoding(), false);
860 } catch (fs::filesystem_error const & fe) {
861 Alert::error(_("Backup failure"),
862 bformat(_("Cannot create backup file %1$s.\n"
863 "Please check whether the directory exists and is writeable."),
864 from_utf8(backupName.absFilename())));
865 LYXERR(Debug::DEBUG) << "Fs error: " << fe.what() << endl;
869 // ask if the disk file has been externally modified (use checksum method)
870 if (fs::exists(encodedFilename) && isExternallyModified(checksum_method)) {
871 docstring const file = makeDisplayPath(absFileName(), 20);
872 docstring text = bformat(_("Document %1$s has been externally modified. Are you sure "
873 "you want to overwrite this file?"), file);
874 int const ret = Alert::prompt(_("Overwrite modified file?"),
875 text, 1, 1, _("&Overwrite"), _("&Cancel"));
880 if (writeFile(pimpl_->filename)) {
882 removeAutosaveFile(absFileName());
883 saveCheckSum(pimpl_->filename);
886 // Saving failed, so backup is not backup
888 rename(backupName, pimpl_->filename);
894 bool Buffer::writeFile(FileName const & fname) const
896 if (pimpl_->read_only && fname == pimpl_->filename)
902 if (params().embedded)
903 // first write the .lyx file to the temporary directory
904 content = FileName(addName(temppath(), "content.lyx"));
908 if (params().compressed) {
909 gz::ogzstream ofs(content.toFilesystemEncoding().c_str(), ios::out|ios::trunc);
915 ofstream ofs(content.toFilesystemEncoding().c_str(), ios::out|ios::trunc);
922 if (retval && params().embedded) {
923 // write file.lyx and all the embedded files to the zip file fname
924 // if embedding is enabled
925 return pimpl_->embedded_files.writeFile(fname);
931 bool Buffer::write(ostream & ofs) const
934 // Use the standard "C" locale for file output.
935 ofs.imbue(std::locale::classic());
938 // The top of the file should not be written by params().
940 // write out a comment in the top of the file
941 ofs << "#LyX " << lyx_version
942 << " created this file. For more info see http://www.lyx.org/\n"
943 << "\\lyxformat " << LYX_FORMAT << "\n"
944 << "\\begin_document\n";
947 /// For each author, set 'used' to true if there is a change
948 /// by this author in the document; otherwise set it to 'false'.
949 AuthorList::Authors::const_iterator a_it = params().authors().begin();
950 AuthorList::Authors::const_iterator a_end = params().authors().end();
951 for (; a_it != a_end; ++a_it)
952 a_it->second.used(false);
954 ParIterator const end = par_iterator_end();
955 ParIterator it = par_iterator_begin();
956 for ( ; it != end; ++it)
957 it->checkAuthors(params().authors());
959 // now write out the buffer parameters.
960 ofs << "\\begin_header\n";
961 params().writeFile(ofs);
962 ofs << "\\end_header\n";
964 // write the manifest after header
965 ofs << "\n\\begin_manifest\n";
966 pimpl_->embedded_files.update();
967 embeddedFiles().writeManifest(ofs);
968 ofs << "\\end_manifest\n";
971 ofs << "\n\\begin_body\n";
972 text().write(*this, ofs);
973 ofs << "\n\\end_body\n";
975 // Write marker that shows file is complete
976 ofs << "\\end_document" << endl;
978 // Shouldn't really be needed....
981 // how to check if close went ok?
982 // Following is an attempt... (BE 20001011)
984 // good() returns false if any error occured, including some
986 // bad() returns true if something bad happened in the buffer,
987 // which should include file system full errors.
992 lyxerr << "File was not closed properly." << endl;
999 bool Buffer::makeLaTeXFile(FileName const & fname,
1000 string const & original_path,
1001 OutputParams const & runparams,
1002 bool output_preamble, bool output_body)
1004 string const encoding = runparams.encoding->iconvName();
1005 LYXERR(Debug::LATEX) << "makeLaTeXFile encoding: "
1006 << encoding << "..." << endl;
1008 odocfstream ofs(encoding);
1009 if (!openFileWrite(ofs, fname))
1012 //TexStream ts(ofs.rdbuf(), &texrow());
1014 bool failed_export = false;
1017 writeLaTeXSource(ofs, original_path,
1018 runparams, output_preamble, output_body);
1020 catch (iconv_codecvt_facet_exception & e) {
1021 lyxerr << "Caught iconv exception: " << e.what() << endl;
1022 failed_export = true;
1024 catch (std::exception const & e) {
1025 lyxerr << "Caught \"normal\" exception: " << e.what() << endl;
1026 failed_export = true;
1029 lyxerr << "Caught some really weird exception..." << endl;
1030 LyX::cref().emergencyCleanup();
1036 failed_export = true;
1037 lyxerr << "File '" << fname << "' was not closed properly." << endl;
1040 if (failed_export) {
1041 Alert::error(_("Encoding error"),
1042 _("Some characters of your document are probably not "
1043 "representable in the chosen encoding.\n"
1044 "Changing the document encoding to utf8 could help."));
1051 void Buffer::writeLaTeXSource(odocstream & os,
1052 string const & original_path,
1053 OutputParams const & runparams_in,
1054 bool const output_preamble, bool const output_body)
1056 OutputParams runparams = runparams_in;
1058 // validate the buffer.
1059 LYXERR(Debug::LATEX) << " Validating buffer..." << endl;
1060 LaTeXFeatures features(*this, params(), runparams);
1062 LYXERR(Debug::LATEX) << " Buffer validation done." << endl;
1064 // The starting paragraph of the coming rows is the
1065 // first paragraph of the document. (Asger)
1066 if (output_preamble && runparams.nice) {
1067 os << "%% LyX " << lyx_version << " created this file. "
1068 "For more info, see http://www.lyx.org/.\n"
1069 "%% Do not edit unless you really know what "
1074 LYXERR(Debug::INFO) << "lyx document header finished" << endl;
1075 // There are a few differences between nice LaTeX and usual files:
1076 // usual is \batchmode and has a
1077 // special input@path to allow the including of figures
1078 // with either \input or \includegraphics (what figinsets do).
1079 // input@path is set when the actual parameter
1080 // original_path is set. This is done for usual tex-file, but not
1081 // for nice-latex-file. (Matthias 250696)
1082 // Note that input@path is only needed for something the user does
1083 // in the preamble, included .tex files or ERT, files included by
1084 // LyX work without it.
1085 if (output_preamble) {
1086 if (!runparams.nice) {
1087 // code for usual, NOT nice-latex-file
1088 os << "\\batchmode\n"; // changed
1089 // from \nonstopmode
1092 if (!original_path.empty()) {
1094 // We don't know the encoding of inputpath
1095 docstring const inputpath = from_utf8(latex_path(original_path));
1096 os << "\\makeatletter\n"
1097 << "\\def\\input@path{{"
1098 << inputpath << "/}}\n"
1099 << "\\makeatother\n";
1105 // Write the preamble
1106 runparams.use_babel = params().writeLaTeX(os, features, texrow());
1112 os << "\\begin{document}\n";
1114 } // output_preamble
1116 texrow().start(paragraphs().begin()->id(), 0);
1118 LYXERR(Debug::INFO) << "preamble finished, now the body." << endl;
1120 if (!lyxrc.language_auto_begin &&
1121 !params().language->babel().empty()) {
1123 os << from_utf8(subst(lyxrc.language_command_begin,
1125 params().language->babel()))
1130 Encoding const & encoding = params().encoding();
1131 if (encoding.package() == Encoding::CJK) {
1132 // Open a CJK environment, since in contrast to the encodings
1133 // handled by inputenc the document encoding is not set in
1134 // the preamble if it is handled by CJK.sty.
1135 os << "\\begin{CJK}{" << from_ascii(encoding.latexName())
1140 // if we are doing a real file with body, even if this is the
1141 // child of some other buffer, let's cut the link here.
1142 // This happens for example if only a child document is printed.
1143 string save_parentname;
1144 if (output_preamble) {
1145 save_parentname = params().parentname;
1146 params().parentname.erase();
1149 loadChildDocuments();
1152 latexParagraphs(*this, paragraphs(), os, texrow(), runparams);
1154 // Restore the parenthood if needed
1155 if (output_preamble)
1156 params().parentname = save_parentname;
1158 // add this just in case after all the paragraphs
1162 if (encoding.package() == Encoding::CJK) {
1163 // Close the open CJK environment.
1164 // latexParagraphs will have opened one even if the last text
1166 os << "\\end{CJK}\n";
1170 if (!lyxrc.language_auto_end &&
1171 !params().language->babel().empty()) {
1172 os << from_utf8(subst(lyxrc.language_command_end,
1174 params().language->babel()))
1179 if (output_preamble) {
1180 os << "\\end{document}\n";
1183 LYXERR(Debug::LATEX) << "makeLaTeXFile...done" << endl;
1185 LYXERR(Debug::LATEX) << "LaTeXFile for inclusion made."
1188 runparams_in.encoding = runparams.encoding;
1190 // Just to be sure. (Asger)
1193 LYXERR(Debug::INFO) << "Finished making LaTeX file." << endl;
1194 LYXERR(Debug::INFO) << "Row count was " << texrow().rows() - 1
1199 bool Buffer::isLatex() const
1201 return params().getTextClass().outputType() == LATEX;
1205 bool Buffer::isLiterate() const
1207 return params().getTextClass().outputType() == LITERATE;
1211 bool Buffer::isDocBook() const
1213 return params().getTextClass().outputType() == DOCBOOK;
1217 void Buffer::makeDocBookFile(FileName const & fname,
1218 OutputParams const & runparams,
1219 bool const body_only)
1221 LYXERR(Debug::LATEX) << "makeDocBookFile..." << endl;
1225 if (!openFileWrite(ofs, fname))
1228 writeDocBookSource(ofs, fname.absFilename(), runparams, body_only);
1232 lyxerr << "File '" << fname << "' was not closed properly." << endl;
1236 void Buffer::writeDocBookSource(odocstream & os, string const & fname,
1237 OutputParams const & runparams,
1238 bool const only_body)
1240 LaTeXFeatures features(*this, params(), runparams);
1245 TextClass const & tclass = params().getTextClass();
1246 string const top_element = tclass.latexname();
1249 if (runparams.flavor == OutputParams::XML)
1250 os << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
1253 os << "<!DOCTYPE " << from_ascii(top_element) << ' ';
1256 if (! tclass.class_header().empty())
1257 os << from_ascii(tclass.class_header());
1258 else if (runparams.flavor == OutputParams::XML)
1259 os << "PUBLIC \"-//OASIS//DTD DocBook XML//EN\" "
1260 << "\"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd\"";
1262 os << " PUBLIC \"-//OASIS//DTD DocBook V4.2//EN\"";
1264 docstring preamble = from_utf8(params().preamble);
1265 if (runparams.flavor != OutputParams::XML ) {
1266 preamble += "<!ENTITY % output.print.png \"IGNORE\">\n";
1267 preamble += "<!ENTITY % output.print.pdf \"IGNORE\">\n";
1268 preamble += "<!ENTITY % output.print.eps \"IGNORE\">\n";
1269 preamble += "<!ENTITY % output.print.bmp \"IGNORE\">\n";
1272 string const name = runparams.nice
1273 ? changeExtension(absFileName(), ".sgml") : fname;
1274 preamble += features.getIncludedFiles(name);
1275 preamble += features.getLyXSGMLEntities();
1277 if (!preamble.empty()) {
1278 os << "\n [ " << preamble << " ]";
1283 string top = top_element;
1285 if (runparams.flavor == OutputParams::XML)
1286 top += params().language->code();
1288 top += params().language->code().substr(0,2);
1291 if (!params().options.empty()) {
1293 top += params().options;
1296 os << "<!-- " << ((runparams.flavor == OutputParams::XML)? "XML" : "SGML")
1297 << " file was created by LyX " << lyx_version
1298 << "\n See http://www.lyx.org/ for more information -->\n";
1300 params().getTextClass().counters().reset();
1302 loadChildDocuments();
1304 sgml::openTag(os, top);
1306 docbookParagraphs(paragraphs(), *this, os, runparams);
1307 sgml::closeTag(os, top_element);
1311 // chktex should be run with these flags disabled: 3, 22, 25, 30, 38(?)
1312 // Other flags: -wall -v0 -x
1313 int Buffer::runChktex()
1317 // get LaTeX-Filename
1318 FileName const path(temppath());
1319 string const name = addName(path.absFilename(), latexName());
1320 string const org_path = filePath();
1322 support::Path p(path); // path to LaTeX file
1323 message(_("Running chktex..."));
1325 // Generate the LaTeX file if neccessary
1326 OutputParams runparams(¶ms().encoding());
1327 runparams.flavor = OutputParams::LATEX;
1328 runparams.nice = false;
1329 makeLaTeXFile(FileName(name), org_path, runparams);
1332 Chktex chktex(lyxrc.chktex_command, onlyFilename(name), filePath());
1333 int const res = chktex.run(terr); // run chktex
1336 Alert::error(_("chktex failure"),
1337 _("Could not run chktex successfully."));
1338 } else if (res > 0) {
1339 ErrorList & errorList = pimpl_->errorLists["ChkTeX"];
1340 // Clear out old errors
1342 // Fill-in the error list with the TeX errors
1343 bufferErrors(*this, terr, errorList);
1354 void Buffer::validate(LaTeXFeatures & features) const
1356 TextClass const & tclass = params().getTextClass();
1358 if (params().outputChanges) {
1359 bool dvipost = LaTeXFeatures::isAvailable("dvipost");
1360 bool xcolorsoul = LaTeXFeatures::isAvailable("soul") &&
1361 LaTeXFeatures::isAvailable("xcolor");
1363 if (features.runparams().flavor == OutputParams::LATEX) {
1365 features.require("ct-dvipost");
1366 features.require("dvipost");
1367 } else if (xcolorsoul) {
1368 features.require("ct-xcolor-soul");
1369 features.require("soul");
1370 features.require("xcolor");
1372 features.require("ct-none");
1374 } else if (features.runparams().flavor == OutputParams::PDFLATEX ) {
1376 features.require("ct-xcolor-soul");
1377 features.require("soul");
1378 features.require("xcolor");
1379 features.require("pdfcolmk"); // improves color handling in PDF output
1381 features.require("ct-none");
1386 // AMS Style is at document level
1387 if (params().use_amsmath == BufferParams::package_on
1388 || tclass.provides("amsmath"))
1389 features.require("amsmath");
1390 if (params().use_esint == BufferParams::package_on)
1391 features.require("esint");
1393 loadChildDocuments();
1395 for_each(paragraphs().begin(), paragraphs().end(),
1396 boost::bind(&Paragraph::validate, _1, boost::ref(features)));
1398 // the bullet shapes are buffer level not paragraph level
1399 // so they are tested here
1400 for (int i = 0; i < 4; ++i) {
1401 if (params().user_defined_bullet(i) != ITEMIZE_DEFAULTS[i]) {
1402 int const font = params().user_defined_bullet(i).getFont();
1404 int const c = params()
1405 .user_defined_bullet(i)
1412 features.require("latexsym");
1414 } else if (font == 1) {
1415 features.require("amssymb");
1416 } else if ((font >= 2 && font <= 5)) {
1417 features.require("pifont");
1422 if (lyxerr.debugging(Debug::LATEX)) {
1423 features.showStruct();
1428 void Buffer::getLabelList(vector<docstring> & list) const
1430 /// if this is a child document and the parent is already loaded
1431 /// Use the parent's list instead [ale990407]
1432 Buffer const * tmp = masterBuffer();
1434 lyxerr << "masterBuffer() failed!" << endl;
1438 tmp->getLabelList(list);
1442 loadChildDocuments();
1444 for (InsetIterator it = inset_iterator_begin(inset()); it; ++it)
1445 it.nextInset()->getLabelList(*this, list);
1449 void Buffer::updateBibfilesCache()
1451 // if this is a child document and the parent is already loaded
1452 // update the parent's cache instead
1453 Buffer * tmp = masterBuffer();
1456 tmp->updateBibfilesCache();
1460 bibfilesCache_.clear();
1461 for (InsetIterator it = inset_iterator_begin(inset()); it; ++it) {
1462 if (it->lyxCode() == BIBTEX_CODE) {
1463 InsetBibtex const & inset =
1464 static_cast<InsetBibtex const &>(*it);
1465 vector<FileName> const bibfiles = inset.getFiles(*this);
1466 bibfilesCache_.insert(bibfilesCache_.end(),
1469 } else if (it->lyxCode() == INCLUDE_CODE) {
1470 InsetInclude & inset =
1471 static_cast<InsetInclude &>(*it);
1472 inset.updateBibfilesCache(*this);
1473 vector<FileName> const & bibfiles =
1474 inset.getBibfilesCache(*this);
1475 bibfilesCache_.insert(bibfilesCache_.end(),
1483 vector<FileName> const & Buffer::getBibfilesCache() const
1485 // if this is a child document and the parent is already loaded
1486 // use the parent's cache instead
1487 Buffer const * tmp = masterBuffer();
1490 return tmp->getBibfilesCache();
1492 // We update the cache when first used instead of at loading time.
1493 if (bibfilesCache_.empty())
1494 const_cast<Buffer *>(this)->updateBibfilesCache();
1496 return bibfilesCache_;
1500 bool Buffer::isDepClean(string const & name) const
1502 DepClean::const_iterator const it = pimpl_->dep_clean.find(name);
1503 if (it == pimpl_->dep_clean.end())
1509 void Buffer::markDepClean(string const & name)
1511 pimpl_->dep_clean[name] = true;
1515 bool Buffer::dispatch(string const & command, bool * result)
1517 return dispatch(lyxaction.lookupFunc(command), result);
1521 bool Buffer::dispatch(FuncRequest const & func, bool * result)
1523 bool dispatched = true;
1525 switch (func.action) {
1526 case LFUN_BUFFER_EXPORT: {
1527 bool const tmp = doExport(to_utf8(func.argument()), false);
1540 void Buffer::changeLanguage(Language const * from, Language const * to)
1545 for_each(par_iterator_begin(),
1547 bind(&Paragraph::changeLanguage, _1, params(), from, to));
1551 bool Buffer::isMultiLingual() const
1553 ParConstIterator end = par_iterator_end();
1554 for (ParConstIterator it = par_iterator_begin(); it != end; ++it)
1555 if (it->isMultiLingual(params()))
1562 ParIterator Buffer::getParFromID(int const id) const
1564 ParConstIterator it = par_iterator_begin();
1565 ParConstIterator const end = par_iterator_end();
1568 // John says this is called with id == -1 from undo
1569 lyxerr << "getParFromID(), id: " << id << endl;
1573 for (; it != end; ++it)
1581 bool Buffer::hasParWithID(int const id) const
1583 ParConstIterator const it = getParFromID(id);
1584 return it != par_iterator_end();
1588 ParIterator Buffer::par_iterator_begin()
1590 return lyx::par_iterator_begin(inset());
1594 ParIterator Buffer::par_iterator_end()
1596 return lyx::par_iterator_end(inset());
1600 ParConstIterator Buffer::par_iterator_begin() const
1602 return lyx::par_const_iterator_begin(inset());
1606 ParConstIterator Buffer::par_iterator_end() const
1608 return lyx::par_const_iterator_end(inset());
1612 Language const * Buffer::language() const
1614 return params().language;
1618 docstring const Buffer::B_(string const & l10n) const
1620 return params().B_(l10n);
1624 bool Buffer::isClean() const
1626 return pimpl_->lyx_clean;
1630 bool Buffer::isBakClean() const
1632 return pimpl_->bak_clean;
1636 bool Buffer::isExternallyModified(CheckMethod method) const
1638 BOOST_ASSERT(pimpl_->filename.exists());
1639 // if method == timestamp, check timestamp before checksum
1640 return (method == checksum_method
1641 || pimpl_->timestamp_ != pimpl_->filename.lastModified())
1642 && pimpl_->checksum_ != sum(pimpl_->filename);
1646 void Buffer::saveCheckSum(FileName const & file) const
1648 if (file.exists()) {
1649 pimpl_->timestamp_ = file.lastModified();
1650 pimpl_->checksum_ = sum(file);
1652 // in the case of save to a new file.
1653 pimpl_->timestamp_ = 0;
1654 pimpl_->checksum_ = 0;
1659 void Buffer::markClean() const
1661 if (!pimpl_->lyx_clean) {
1662 pimpl_->lyx_clean = true;
1665 // if the .lyx file has been saved, we don't need an
1667 pimpl_->bak_clean = true;
1671 void Buffer::markBakClean() const
1673 pimpl_->bak_clean = true;
1677 void Buffer::setUnnamed(bool flag)
1679 pimpl_->unnamed = flag;
1683 bool Buffer::isUnnamed() const
1685 return pimpl_->unnamed;
1689 // FIXME: this function should be moved to buffer_pimpl.C
1690 void Buffer::markDirty()
1692 if (pimpl_->lyx_clean) {
1693 pimpl_->lyx_clean = false;
1696 pimpl_->bak_clean = false;
1698 DepClean::iterator it = pimpl_->dep_clean.begin();
1699 DepClean::const_iterator const end = pimpl_->dep_clean.end();
1701 for (; it != end; ++it)
1706 string Buffer::absFileName() const
1708 return pimpl_->filename.absFilename();
1712 string const & Buffer::filePath() const
1714 return params().filepath;
1718 bool Buffer::isReadonly() const
1720 return pimpl_->read_only;
1724 void Buffer::setParentName(string const & name)
1726 if (name == pimpl_->filename.absFilename())
1727 // Avoids recursive include.
1728 params().parentname.clear();
1730 params().parentname = name;
1734 Buffer const * Buffer::masterBuffer() const
1736 if (!params().parentname.empty()
1737 && theBufferList().exists(params().parentname)) {
1738 Buffer const * buf = theBufferList().getBuffer(params().parentname);
1739 //We need to check if the parent is us...
1740 //FIXME RECURSIVE INCLUDE
1741 //This is not sufficient, since recursive includes could be downstream.
1742 if (buf && buf != this)
1743 return buf->masterBuffer();
1750 Buffer * Buffer::masterBuffer()
1752 if (!params().parentname.empty()
1753 && theBufferList().exists(params().parentname)) {
1754 Buffer * buf = theBufferList().getBuffer(params().parentname);
1755 //We need to check if the parent is us...
1756 //FIXME RECURSIVE INCLUDE
1757 //This is not sufficient, since recursive includes could be downstream.
1758 if (buf && buf != this)
1759 return buf->masterBuffer();
1766 MacroData const & Buffer::getMacro(docstring const & name) const
1768 return pimpl_->macros.get(name);
1772 bool Buffer::hasMacro(docstring const & name) const
1774 return pimpl_->macros.has(name);
1778 void Buffer::insertMacro(docstring const & name, MacroData const & data)
1780 MacroTable::globalMacros().insert(name, data);
1781 pimpl_->macros.insert(name, data);
1785 void Buffer::buildMacros()
1787 // Start with global table.
1788 pimpl_->macros = MacroTable::globalMacros();
1791 ParagraphList const & pars = text().paragraphs();
1792 for (size_t i = 0, n = pars.size(); i != n; ++i) {
1793 //lyxerr << "searching main par " << i
1794 // << " for macro definitions" << std::endl;
1795 InsetList const & insets = pars[i].insetList();
1796 InsetList::const_iterator it = insets.begin();
1797 InsetList::const_iterator end = insets.end();
1798 for ( ; it != end; ++it) {
1799 //lyxerr << "found inset code " << it->inset->lyxCode() << std::endl;
1800 if (it->inset->lyxCode() == MATHMACRO_CODE) {
1801 MathMacroTemplate const & mac
1802 = static_cast<MathMacroTemplate const &>(*it->inset);
1803 insertMacro(mac.name(), mac.asMacroData());
1810 void Buffer::changeRefsIfUnique(docstring const & from, docstring const & to,
1813 //FIXME: This does not work for child documents yet.
1814 BOOST_ASSERT(code == CITE_CODE || code == REF_CODE);
1815 // Check if the label 'from' appears more than once
1816 vector<docstring> labels;
1818 if (code == CITE_CODE) {
1820 keys.fillWithBibKeys(this);
1821 BiblioInfo::const_iterator bit = keys.begin();
1822 BiblioInfo::const_iterator bend = keys.end();
1824 for (; bit != bend; ++bit)
1826 labels.push_back(bit->first);
1828 getLabelList(labels);
1830 if (std::count(labels.begin(), labels.end(), from) > 1)
1833 for (InsetIterator it = inset_iterator_begin(inset()); it; ++it) {
1834 if (it->lyxCode() == code) {
1835 InsetCommand & inset = static_cast<InsetCommand &>(*it);
1836 inset.replaceContents(to_utf8(from), to_utf8(to));
1842 void Buffer::getSourceCode(odocstream & os, pit_type par_begin,
1843 pit_type par_end, bool full_source)
1845 OutputParams runparams(¶ms().encoding());
1846 runparams.nice = true;
1847 runparams.flavor = OutputParams::LATEX;
1848 runparams.linelen = lyxrc.plaintext_linelen;
1849 // No side effect of file copying and image conversion
1850 runparams.dryrun = true;
1854 os << "% " << _("Preview source code") << "\n\n";
1858 writeLaTeXSource(os, filePath(), runparams, true, true);
1860 writeDocBookSource(os, absFileName(), runparams, false);
1863 runparams.par_begin = par_begin;
1864 runparams.par_end = par_end;
1865 if (par_begin + 1 == par_end)
1867 << bformat(_("Preview source code for paragraph %1$d"), par_begin)
1871 << bformat(_("Preview source code from paragraph %1$s to %2$s"),
1872 convert<docstring>(par_begin),
1873 convert<docstring>(par_end - 1))
1877 // output paragraphs
1879 latexParagraphs(*this, paragraphs(), os, texrow(), runparams);
1882 docbookParagraphs(paragraphs(), *this, os, runparams);
1888 ErrorList const & Buffer::errorList(string const & type) const
1890 static ErrorList const emptyErrorList;
1891 std::map<string, ErrorList>::const_iterator I = pimpl_->errorLists.find(type);
1892 if (I == pimpl_->errorLists.end())
1893 return emptyErrorList;
1899 ErrorList & Buffer::errorList(string const & type)
1901 return pimpl_->errorLists[type];
1905 void Buffer::structureChanged() const
1908 gui_->structureChanged();
1912 void Buffer::embeddingChanged() const
1915 gui_->embeddingChanged();
1919 void Buffer::errors(std::string const & err) const
1926 void Buffer::message(docstring const & msg) const
1933 void Buffer::busy(bool on) const
1940 void Buffer::readonly(bool on) const
1947 void Buffer::updateTitles() const
1950 gui_->updateTitles();
1954 void Buffer::resetAutosaveTimers() const
1957 gui_->resetAutosaveTimers();
1961 void Buffer::setGuiDelegate(frontend::GuiBufferDelegate * gui)
1970 class AutoSaveBuffer : public support::ForkedProcess {
1973 AutoSaveBuffer(Buffer const & buffer, FileName const & fname)
1974 : buffer_(buffer), fname_(fname) {}
1976 virtual boost::shared_ptr<ForkedProcess> clone() const
1978 return boost::shared_ptr<ForkedProcess>(new AutoSaveBuffer(*this));
1983 command_ = to_utf8(bformat(_("Auto-saving %1$s"),
1984 from_utf8(fname_.absFilename())));
1985 return run(DontWait);
1989 virtual int generateChild();
1991 Buffer const & buffer_;
1996 #if !defined (HAVE_FORK)
2000 int AutoSaveBuffer::generateChild()
2002 // tmp_ret will be located (usually) in /tmp
2003 // will that be a problem?
2004 pid_t const pid = fork();
2005 // If you want to debug the autosave
2006 // you should set pid to -1, and comment out the fork.
2007 if (pid == 0 || pid == -1) {
2008 // pid = -1 signifies that lyx was unable
2009 // to fork. But we will do the save
2011 bool failed = false;
2013 FileName const tmp_ret(tempName(FileName(), "lyxauto"));
2014 if (!tmp_ret.empty()) {
2015 buffer_.writeFile(tmp_ret);
2016 // assume successful write of tmp_ret
2017 if (!rename(tmp_ret, fname_)) {
2019 // most likely couldn't move between
2020 // filesystems unless write of tmp_ret
2021 // failed so remove tmp file (if it
2030 // failed to write/rename tmp_ret so try writing direct
2031 if (!buffer_.writeFile(fname_)) {
2032 // It is dangerous to do this in the child,
2033 // but safe in the parent, so...
2034 if (pid == -1) // emit message signal.
2035 buffer_.message(_("Autosave failed!"));
2038 if (pid == 0) { // we are the child so...
2048 // Perfect target for a thread...
2049 void Buffer::autoSave() const
2051 if (isBakClean() || isReadonly()) {
2052 // We don't save now, but we'll try again later
2053 resetAutosaveTimers();
2057 // emit message signal.
2058 message(_("Autosaving current document..."));
2060 // create autosave filename
2061 string fname = filePath();
2063 fname += onlyFilename(absFileName());
2066 AutoSaveBuffer autosave(*this, FileName(fname));
2070 resetAutosaveTimers();
2074 /** Write a buffer to a new file name and rename the buffer
2075 according to the new file name.
2077 This function is e.g. used by menu callbacks and
2078 LFUN_BUFFER_WRITE_AS.
2080 If 'newname' is empty (the default), the user is asked via a
2081 dialog for the buffer's new name and location.
2083 If 'newname' is non-empty and has an absolute path, that is used.
2084 Otherwise the base directory of the buffer is used as the base
2085 for any relative path in 'newname'.
2088 bool Buffer::writeAs(string const & newname)
2090 string fname = absFileName();
2091 string const oldname = fname;
2093 if (newname.empty()) { /// No argument? Ask user through dialog
2096 FileDialog fileDlg(_("Choose a filename to save document as"),
2097 LFUN_BUFFER_WRITE_AS,
2098 make_pair(_("Documents|#o#O"),
2099 from_utf8(lyxrc.document_path)),
2100 make_pair(_("Templates|#T#t"),
2101 from_utf8(lyxrc.template_path)));
2103 if (!support::isLyXFilename(fname))
2106 support::FileFilterList const filter(_("LyX Documents (*.lyx)"));
2108 FileDialog::Result result =
2109 fileDlg.save(from_utf8(onlyPath(fname)),
2111 from_utf8(onlyFilename(fname)));
2113 if (result.first == FileDialog::Later)
2116 fname = to_utf8(result.second);
2121 // Make sure the absolute filename ends with appropriate suffix
2122 fname = makeAbsPath(fname).absFilename();
2123 if (!support::isLyXFilename(fname))
2127 fname = makeAbsPath(newname, onlyPath(oldname)).absFilename();
2129 if (FileName(fname).exists()) {
2130 docstring const file = makeDisplayPath(fname, 30);
2131 docstring text = bformat(_("The document %1$s already "
2132 "exists.\n\nDo you want to "
2133 "overwrite that document?"),
2135 int const ret = Alert::prompt(_("Overwrite document?"),
2136 text, 0, 1, _("&Overwrite"), _("&Cancel"));
2142 // Ok, change the name of the buffer
2145 bool unnamed = isUnnamed();
2147 saveCheckSum(FileName(fname));
2150 setFileName(oldname);
2151 setUnnamed(unnamed);
2152 saveCheckSum(FileName(oldname));
2156 removeAutosaveFile(oldname);
2161 bool Buffer::menuWrite()
2164 LyX::ref().session().lastFiles().add(FileName(absFileName()));
2168 // FIXME: we don't tell the user *WHY* the save failed !!
2170 docstring const file = makeDisplayPath(absFileName(), 30);
2172 docstring text = bformat(_("The document %1$s could not be saved.\n\n"
2173 "Do you want to rename the document and "
2174 "try again?"), file);
2175 int const ret = Alert::prompt(_("Rename and save?"),
2176 text, 0, 1, _("&Rename"), _("&Cancel"));
2185 void Buffer::loadChildDocuments() const
2187 bool parse_error = false;
2189 for (InsetIterator it = inset_iterator_begin(inset()); it; ++it) {
2190 if (it->lyxCode() != INCLUDE_CODE)
2192 InsetInclude const & inset = static_cast<InsetInclude const &>(*it);
2193 InsetCommandParams const & ip = inset.params();
2194 Buffer * child = loadIfNeeded(*this, ip);
2197 parse_error |= !child->errorList("Parse").empty();
2198 child->loadChildDocuments();
2201 if (use_gui && masterBuffer() == this)
2202 updateLabels(*this);
2206 string Buffer::bufferFormat() const
2216 bool Buffer::doExport(string const & format,
2217 bool put_in_tempdir, string & result_file)
2219 string backend_format;
2220 OutputParams runparams(¶ms().encoding());
2221 runparams.flavor = OutputParams::LATEX;
2222 runparams.linelen = lyxrc.plaintext_linelen;
2223 vector<string> backs = backends();
2224 if (find(backs.begin(), backs.end(), format) == backs.end()) {
2225 // Get shortest path to format
2226 Graph::EdgePath path;
2227 for (vector<string>::const_iterator it = backs.begin();
2228 it != backs.end(); ++it) {
2229 Graph::EdgePath p = theConverters().getPath(*it, format);
2230 if (!p.empty() && (path.empty() || p.size() < path.size())) {
2231 backend_format = *it;
2236 runparams.flavor = theConverters().getFlavor(path);
2238 Alert::error(_("Couldn't export file"),
2239 bformat(_("No information for exporting the format %1$s."),
2240 formats.prettyName(format)));
2244 backend_format = format;
2245 // FIXME: Don't hardcode format names here, but use a flag
2246 if (backend_format == "pdflatex")
2247 runparams.flavor = OutputParams::PDFLATEX;
2250 string filename = latexName(false);
2251 filename = addName(temppath(), filename);
2252 filename = changeExtension(filename,
2253 formats.extension(backend_format));
2255 // Plain text backend
2256 if (backend_format == "text")
2257 writePlaintextFile(*this, FileName(filename), runparams);
2259 else if (backend_format == "lyx")
2260 writeFile(FileName(filename));
2262 else if (isDocBook()) {
2263 runparams.nice = !put_in_tempdir;
2264 makeDocBookFile(FileName(filename), runparams);
2267 else if (backend_format == format) {
2268 runparams.nice = true;
2269 if (!makeLaTeXFile(FileName(filename), string(), runparams))
2271 } else if (!lyxrc.tex_allows_spaces
2272 && support::contains(filePath(), ' ')) {
2273 Alert::error(_("File name error"),
2274 _("The directory path to the document cannot contain spaces."));
2277 runparams.nice = false;
2278 if (!makeLaTeXFile(FileName(filename), filePath(), runparams))
2282 string const error_type = (format == "program")
2283 ? "Build" : bufferFormat();
2284 string const ext = formats.extension(format);
2285 FileName const tmp_result_file(changeExtension(filename, ext));
2286 bool const success = theConverters().convert(this, FileName(filename),
2287 tmp_result_file, FileName(absFileName()), backend_format, format,
2288 errorList(error_type));
2289 // Emit the signal to show the error list.
2290 if (format != backend_format)
2296 result_file = tmp_result_file.absFilename();
2298 result_file = changeExtension(absFileName(), ext);
2299 // We need to copy referenced files (e. g. included graphics
2300 // if format == "dvi") to the result dir.
2301 vector<ExportedFile> const files =
2302 runparams.exportdata->externalFiles(format);
2303 string const dest = onlyPath(result_file);
2304 CopyStatus status = SUCCESS;
2305 for (vector<ExportedFile>::const_iterator it = files.begin();
2306 it != files.end() && status != CANCEL; ++it) {
2308 formats.getFormatFromFile(it->sourceName);
2309 status = copyFile(fmt, it->sourceName,
2310 makeAbsPath(it->exportName, dest),
2311 it->exportName, status == FORCE);
2313 if (status == CANCEL) {
2314 message(_("Document export cancelled."));
2315 } else if (tmp_result_file.exists()) {
2316 // Finally copy the main file
2317 status = copyFile(format, tmp_result_file,
2318 FileName(result_file), result_file,
2320 message(bformat(_("Document exported as %1$s "
2322 formats.prettyName(format),
2323 makeDisplayPath(result_file)));
2325 // This must be a dummy converter like fax (bug 1888)
2326 message(bformat(_("Document exported as %1$s"),
2327 formats.prettyName(format)));
2335 bool Buffer::doExport(string const & format, bool put_in_tempdir)
2338 return doExport(format, put_in_tempdir, result_file);
2342 bool Buffer::preview(string const & format)
2345 if (!doExport(format, true, result_file))
2347 return formats.view(*this, FileName(result_file), format);
2351 bool Buffer::isExportable(string const & format) const
2353 vector<string> backs = backends();
2354 for (vector<string>::const_iterator it = backs.begin();
2355 it != backs.end(); ++it)
2356 if (theConverters().isReachable(*it, format))
2362 vector<Format const *> Buffer::exportableFormats(bool only_viewable) const
2364 vector<string> backs = backends();
2365 vector<Format const *> result =
2366 theConverters().getReachable(backs[0], only_viewable, true);
2367 for (vector<string>::const_iterator it = backs.begin() + 1;
2368 it != backs.end(); ++it) {
2369 vector<Format const *> r =
2370 theConverters().getReachable(*it, only_viewable, false);
2371 result.insert(result.end(), r.begin(), r.end());
2377 vector<string> Buffer::backends() const
2380 if (params().getTextClass().isTeXClassAvailable()) {
2381 v.push_back(bufferFormat());
2382 // FIXME: Don't hardcode format names here, but use a flag
2383 if (v.back() == "latex")
2384 v.push_back("pdflatex");
2386 v.push_back("text");