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"
25 #include "DocIterator.h"
27 #include "ErrorList.h"
30 #include "FuncRequest.h"
32 #include "InsetIterator.h"
33 #include "InsetList.h"
36 #include "LaTeXFeatures.h"
38 #include "LyXAction.h"
46 #include "output_docbook.h"
47 #include "output_latex.h"
48 #include "Paragraph.h"
49 #include "paragraph_funcs.h"
50 #include "ParagraphParameters.h"
51 #include "ParIterator.h"
55 #include "TextClassList.h"
56 #include "TexStream.h"
57 #include "TocBackend.h"
60 #include "EmbeddedFiles.h"
61 #include "PDFOptions.h"
63 #include "insets/InsetBibitem.h"
64 #include "insets/InsetBibtex.h"
65 #include "insets/InsetInclude.h"
66 #include "insets/InsetText.h"
68 #include "mathed/MathMacroTemplate.h"
69 #include "mathed/MacroTable.h"
70 #include "mathed/MathSupport.h"
72 #include "frontends/alert.h"
73 #include "frontends/Delegates.h"
74 #include "frontends/WorkAreaManager.h"
75 #include "frontends/FileDialog.h"
77 #include "graphics/Previews.h"
79 #include "support/types.h"
80 #include "support/lyxalgo.h"
81 #include "support/FileFilterList.h"
82 #include "support/filetools.h"
83 #include "support/Forkedcall.h"
84 #include "support/fs_extras.h"
85 #include "support/gzstream.h"
86 #include "support/lyxlib.h"
87 #include "support/os.h"
88 #include "support/Path.h"
89 #include "support/textutils.h"
90 #include "support/convert.h"
92 #if !defined (HAVE_FORK)
96 #include <boost/bind.hpp>
97 #include <boost/filesystem/exception.hpp>
98 #include <boost/filesystem/operations.hpp>
99 #include <boost/shared_ptr.hpp>
109 using std::make_pair;
114 using std::ostringstream;
125 using support::addName;
126 using support::bformat;
127 using support::changeExtension;
128 using support::cmd_ret;
129 using support::createBufferTmpDir;
130 using support::destroyDir;
131 using support::FileName;
132 using support::getFormatFromContents;
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.buffer(&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 = getMasterBuffer();
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() && !destroyDir(FileName(temppath()))) {
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;
386 Undo & Buffer::undo()
388 return pimpl_->undo_;
393 string const Buffer::getLatexName(bool const no_path) const
395 string const name = changeExtension(makeLatexName(fileName()), ".tex");
396 return no_path ? onlyFilename(name) : name;
400 pair<Buffer::LogType, string> const Buffer::getLogName() const
402 string const filename = getLatexName(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 (fs::exists(bname.toFilesystemEncoding()) &&
420 (!fs::exists(fname.toFilesystemEncoding()) ||
421 fs::last_write_time(fname.toFilesystemEncoding()) < fs::last_write_time(bname.toFilesystemEncoding()))) {
422 LYXERR(Debug::FILES) << "Log name calculated as: " << bname << endl;
423 return make_pair(Buffer::buildlog, bname.absFilename());
425 LYXERR(Debug::FILES) << "Log name calculated as: " << fname << endl;
426 return make_pair(Buffer::latexlog, fname.absFilename());
430 void Buffer::setReadonly(bool const flag)
432 if (pimpl_->read_only != flag) {
433 pimpl_->read_only = flag;
439 void Buffer::setFileName(string const & newfile)
441 pimpl_->filename = makeAbsPath(newfile);
442 params().filepath = onlyPath(pimpl_->filename.absFilename());
443 setReadonly(fs::is_readonly(pimpl_->filename.toFilesystemEncoding()));
448 // We'll remove this later. (Lgb)
451 void unknownClass(string const & unknown)
453 Alert::warning(_("Unknown document class"),
454 bformat(_("Using the default document class, because the "
455 "class %1$s is unknown."), from_utf8(unknown)));
461 int Buffer::readHeader(Lexer & lex)
463 int unknown_tokens = 0;
465 int begin_header_line = -1;
467 // Initialize parameters that may be/go lacking in header:
468 params().branchlist().clear();
469 params().preamble.erase();
470 params().options.erase();
471 params().float_placement.erase();
472 params().paperwidth.erase();
473 params().paperheight.erase();
474 params().leftmargin.erase();
475 params().rightmargin.erase();
476 params().topmargin.erase();
477 params().bottommargin.erase();
478 params().headheight.erase();
479 params().headsep.erase();
480 params().footskip.erase();
481 params().listings_params.clear();
482 params().clearLayoutModules();
483 params().pdfoptions().clear();
485 for (int i = 0; i < 4; ++i) {
486 params().user_defined_bullet(i) = ITEMIZE_DEFAULTS[i];
487 params().temp_bullet(i) = ITEMIZE_DEFAULTS[i];
490 ErrorList & errorList = pimpl_->errorLists["Parse"];
494 string const token = lex.getString();
499 if (token == "\\end_header")
503 if (token == "\\begin_header") {
504 begin_header_line = line;
508 LYXERR(Debug::PARSER) << "Handling document header token: `"
509 << token << '\'' << endl;
511 string unknown = params().readToken(lex, token);
512 if (!unknown.empty()) {
513 if (unknown[0] != '\\' && token == "\\textclass") {
514 unknownClass(unknown);
517 docstring const s = bformat(_("Unknown token: "
521 errorList.push_back(ErrorItem(_("Document header error"),
526 if (begin_header_line) {
527 docstring const s = _("\\begin_header is missing");
528 errorList.push_back(ErrorItem(_("Document header error"),
532 return unknown_tokens;
537 // changed to be public and have one parameter
538 // Returns false if "\end_document" is not read (Asger)
539 bool Buffer::readDocument(Lexer & lex)
541 ErrorList & errorList = pimpl_->errorLists["Parse"];
545 string const token = lex.getString();
546 if (token != "\\begin_document") {
547 docstring const s = _("\\begin_document is missing");
548 errorList.push_back(ErrorItem(_("Document header error"),
552 // we are reading in a brand new document
553 BOOST_ASSERT(paragraphs().empty());
556 TextClass const & baseClass = textclasslist[params().getBaseClass()];
557 if (!baseClass.load(filePath())) {
558 string theclass = baseClass.name();
559 Alert::error(_("Can't load document class"), bformat(
560 _("Using the default document class, because the "
561 "class %1$s could not be loaded."), from_utf8(theclass)));
562 params().setBaseClass(defaultTextclass());
565 if (params().outputChanges) {
566 bool dvipost = LaTeXFeatures::isAvailable("dvipost");
567 bool xcolorsoul = LaTeXFeatures::isAvailable("soul") &&
568 LaTeXFeatures::isAvailable("xcolor");
570 if (!dvipost && !xcolorsoul) {
571 Alert::warning(_("Changes not shown in LaTeX output"),
572 _("Changes will not be highlighted in LaTeX output, "
573 "because neither dvipost nor xcolor/soul are installed.\n"
574 "Please install these packages or redefine "
575 "\\lyxadded and \\lyxdeleted in the LaTeX preamble."));
576 } else if (!xcolorsoul) {
577 Alert::warning(_("Changes not shown in LaTeX output"),
578 _("Changes will not be highlighted in LaTeX output "
579 "when using pdflatex, because xcolor and soul are not installed.\n"
580 "Please install both packages or redefine "
581 "\\lyxadded and \\lyxdeleted in the LaTeX preamble."));
584 // read manifest after header
585 embeddedFiles().readManifest(lex, errorList);
588 bool const res = text().read(*this, lex, errorList);
589 for_each(text().paragraphs().begin(),
590 text().paragraphs().end(),
591 bind(&Paragraph::setInsetOwner, _1, &inset()));
597 // needed to insert the selection
598 void Buffer::insertStringAsLines(ParagraphList & pars,
599 pit_type & pit, pos_type & pos,
600 Font const & fn, docstring const & str, bool autobreakrows)
604 // insert the string, don't insert doublespace
605 bool space_inserted = true;
606 for (docstring::const_iterator cit = str.begin();
607 cit != str.end(); ++cit) {
608 Paragraph & par = pars[pit];
610 if (autobreakrows && (!par.empty() || par.allowEmpty())) {
611 breakParagraph(params(), pars, pit, pos,
612 par.layout()->isEnvironment());
615 space_inserted = true;
619 // do not insert consecutive spaces if !free_spacing
620 } else if ((*cit == ' ' || *cit == '\t') &&
621 space_inserted && !par.isFreeSpacing()) {
623 } else if (*cit == '\t') {
624 if (!par.isFreeSpacing()) {
625 // tabs are like spaces here
626 par.insertChar(pos, ' ', font, params().trackChanges);
628 space_inserted = true;
630 const pos_type n = 8 - pos % 8;
631 for (pos_type i = 0; i < n; ++i) {
632 par.insertChar(pos, ' ', font, params().trackChanges);
635 space_inserted = true;
637 } else if (!isPrintable(*cit)) {
638 // Ignore unprintables
641 // just insert the character
642 par.insertChar(pos, *cit, font, params().trackChanges);
644 space_inserted = (*cit == ' ');
651 bool Buffer::readString(std::string const & s)
653 params().compressed = false;
655 // remove dummy empty par
656 paragraphs().clear();
658 std::istringstream is(s);
660 FileName const name(tempName());
661 switch (readFile(lex, name, true)) {
665 // We need to call lyx2lyx, so write the input to a file
666 std::ofstream os(name.toFilesystemEncoding().c_str());
669 return readFile(name);
679 bool Buffer::readFile(FileName const & filename)
681 FileName fname(filename);
682 // Check if the file is compressed.
683 string format = getFormatFromContents(filename);
684 if (format == "zip") {
685 // decompress to a temp directory
686 LYXERR(Debug::FILES) << filename << " is in zip format. Unzip to " << temppath() << endl;
687 ::unzipToDir(filename.toFilesystemEncoding(), temppath());
689 FileName lyxfile(addName(temppath(), "content.lyx"));
690 // if both manifest.txt and file.lyx exist, this is am embedded file
691 if (fs::exists(lyxfile.toFilesystemEncoding())) {
692 params().embedded = true;
696 // The embedded lyx file can also be compressed, for backward compatibility
697 format = getFormatFromContents(fname);
698 if (format == "gzip" || format == "zip" || format == "compress") {
699 params().compressed = true;
702 // remove dummy empty par
703 paragraphs().clear();
706 if (readFile(lex, fname) != success)
713 bool Buffer::fully_loaded() const
715 return pimpl_->file_fully_loaded;
719 void Buffer::fully_loaded(bool const value)
721 pimpl_->file_fully_loaded = value;
725 Buffer::ReadStatus Buffer::readFile(Lexer & lex, FileName const & filename,
728 BOOST_ASSERT(!filename.empty());
731 Alert::error(_("Document could not be read"),
732 bformat(_("%1$s could not be read."), from_utf8(filename.absFilename())));
737 string const token = lex.getString();
740 Alert::error(_("Document could not be read"),
741 bformat(_("%1$s could not be read."), from_utf8(filename.absFilename())));
745 // the first token _must_ be...
746 if (token != "\\lyxformat") {
747 lyxerr << "Token: " << token << endl;
749 Alert::error(_("Document format failure"),
750 bformat(_("%1$s is not a LyX document."),
751 from_utf8(filename.absFilename())));
756 string tmp_format = lex.getString();
757 //lyxerr << "LyX Format: `" << tmp_format << '\'' << endl;
758 // if present remove ".," from string.
759 string::size_type dot = tmp_format.find_first_of(".,");
760 //lyxerr << " dot found at " << dot << endl;
761 if (dot != string::npos)
762 tmp_format.erase(dot, 1);
763 int const file_format = convert<int>(tmp_format);
764 //lyxerr << "format: " << file_format << endl;
766 // save timestamp and checksum of the original disk file, making sure
767 // to not overwrite them with those of the file created in the tempdir
768 // when it has to be converted to the current format.
769 if (!pimpl_->checksum_) {
770 // Save the timestamp and checksum of disk file. If filename is an
771 // emergency file, save the timestamp and sum of the original lyx file
772 // because isExternallyModified will check for this file. (BUG4193)
773 string diskfile = filename.toFilesystemEncoding();
774 if (suffixIs(diskfile, ".emergency"))
775 diskfile = diskfile.substr(0, diskfile.size() - 10);
776 saveCheckSum(diskfile);
779 if (file_format != LYX_FORMAT) {
782 // lyx2lyx would fail
785 FileName const tmpfile(tempName());
786 if (tmpfile.empty()) {
787 Alert::error(_("Conversion failed"),
788 bformat(_("%1$s is from a different"
789 " version of LyX, but a temporary"
790 " file for converting it could"
792 from_utf8(filename.absFilename())));
795 FileName const lyx2lyx = libFileSearch("lyx2lyx", "lyx2lyx");
796 if (lyx2lyx.empty()) {
797 Alert::error(_("Conversion script not found"),
798 bformat(_("%1$s is from a different"
799 " version of LyX, but the"
800 " conversion script lyx2lyx"
801 " could not be found."),
802 from_utf8(filename.absFilename())));
805 ostringstream command;
806 command << os::python()
807 << ' ' << quoteName(lyx2lyx.toFilesystemEncoding())
808 << " -t " << convert<string>(LYX_FORMAT)
809 << " -o " << quoteName(tmpfile.toFilesystemEncoding())
810 << ' ' << quoteName(filename.toFilesystemEncoding());
811 string const command_str = command.str();
813 LYXERR(Debug::INFO) << "Running '"
814 << command_str << '\''
817 cmd_ret const ret = runCommand(command_str);
818 if (ret.first != 0) {
819 Alert::error(_("Conversion script failed"),
820 bformat(_("%1$s is from a different version"
821 " of LyX, but the lyx2lyx script"
822 " failed to convert it."),
823 from_utf8(filename.absFilename())));
826 bool const ret = readFile(tmpfile);
827 // Do stuff with tmpfile name and buffer name here.
828 return ret ? success : failure;
833 if (readDocument(lex)) {
834 Alert::error(_("Document format failure"),
835 bformat(_("%1$s ended unexpectedly, which means"
836 " that it is probably corrupted."),
837 from_utf8(filename.absFilename())));
840 //lyxerr << "removing " << MacroTable::localMacros().size()
841 // << " temporary macro entries" << endl;
842 //MacroTable::localMacros().clear();
844 pimpl_->file_fully_loaded = true;
849 // Should probably be moved to somewhere else: BufferView? LyXView?
850 bool Buffer::save() const
852 // We don't need autosaves in the immediate future. (Asger)
853 resetAutosaveTimers();
855 string const encodedFilename = pimpl_->filename.toFilesystemEncoding();
858 bool madeBackup = false;
860 // make a backup if the file already exists
861 if (lyxrc.make_backup && fs::exists(encodedFilename)) {
862 backupName = FileName(fileName() + '~');
863 if (!lyxrc.backupdir_path.empty())
864 backupName = FileName(addName(lyxrc.backupdir_path,
865 subst(os::internal_path(backupName.absFilename()), '/', '!')));
868 fs::copy_file(encodedFilename, backupName.toFilesystemEncoding(), false);
870 } catch (fs::filesystem_error const & fe) {
871 Alert::error(_("Backup failure"),
872 bformat(_("Cannot create backup file %1$s.\n"
873 "Please check whether the directory exists and is writeable."),
874 from_utf8(backupName.absFilename())));
875 LYXERR(Debug::DEBUG) << "Fs error: " << fe.what() << endl;
879 // ask if the disk file has been externally modified (use checksum method)
880 if (fs::exists(encodedFilename) && isExternallyModified(checksum_method)) {
881 docstring const file = makeDisplayPath(fileName(), 20);
882 docstring text = bformat(_("Document %1$s has been externally modified. Are you sure "
883 "you want to overwrite this file?"), file);
884 int const ret = Alert::prompt(_("Overwrite modified file?"),
885 text, 1, 1, _("&Overwrite"), _("&Cancel"));
890 if (writeFile(pimpl_->filename)) {
892 removeAutosaveFile(fileName());
893 saveCheckSum(pimpl_->filename.toFilesystemEncoding());
896 // Saving failed, so backup is not backup
898 rename(backupName, pimpl_->filename);
904 bool Buffer::writeFile(FileName const & fname) const
906 if (pimpl_->read_only && fname == pimpl_->filename)
912 if (params().embedded)
913 // first write the .lyx file to the temporary directory
914 content = FileName(addName(temppath(), "content.lyx"));
918 if (params().compressed) {
919 gz::ogzstream ofs(content.toFilesystemEncoding().c_str(), ios::out|ios::trunc);
925 ofstream ofs(content.toFilesystemEncoding().c_str(), ios::out|ios::trunc);
932 if (retval && params().embedded) {
933 // write file.lyx and all the embedded files to the zip file fname
934 // if embedding is enabled
935 return pimpl_->embedded_files.writeFile(fname);
941 bool Buffer::write(ostream & ofs) const
944 // Use the standard "C" locale for file output.
945 ofs.imbue(std::locale::classic());
948 // The top of the file should not be written by params().
950 // write out a comment in the top of the file
951 ofs << "#LyX " << lyx_version
952 << " created this file. For more info see http://www.lyx.org/\n"
953 << "\\lyxformat " << LYX_FORMAT << "\n"
954 << "\\begin_document\n";
957 /// For each author, set 'used' to true if there is a change
958 /// by this author in the document; otherwise set it to 'false'.
959 AuthorList::Authors::const_iterator a_it = params().authors().begin();
960 AuthorList::Authors::const_iterator a_end = params().authors().end();
961 for (; a_it != a_end; ++a_it)
962 a_it->second.used(false);
964 ParIterator const end = par_iterator_end();
965 ParIterator it = par_iterator_begin();
966 for ( ; it != end; ++it)
967 it->checkAuthors(params().authors());
969 // now write out the buffer parameters.
970 ofs << "\\begin_header\n";
971 params().writeFile(ofs);
972 ofs << "\\end_header\n";
974 // write the manifest after header
975 ofs << "\n\\begin_manifest\n";
976 pimpl_->embedded_files.update();
977 embeddedFiles().writeManifest(ofs);
978 ofs << "\\end_manifest\n";
981 ofs << "\n\\begin_body\n";
982 text().write(*this, ofs);
983 ofs << "\n\\end_body\n";
985 // Write marker that shows file is complete
986 ofs << "\\end_document" << endl;
988 // Shouldn't really be needed....
991 // how to check if close went ok?
992 // Following is an attempt... (BE 20001011)
994 // good() returns false if any error occured, including some
996 // bad() returns true if something bad happened in the buffer,
997 // which should include file system full errors.
1002 lyxerr << "File was not closed properly." << endl;
1009 bool Buffer::makeLaTeXFile(FileName const & fname,
1010 string const & original_path,
1011 OutputParams const & runparams,
1012 bool output_preamble, bool output_body)
1014 string const encoding = runparams.encoding->iconvName();
1015 LYXERR(Debug::LATEX) << "makeLaTeXFile encoding: "
1016 << encoding << "..." << endl;
1018 odocfstream ofs(encoding);
1019 if (!openFileWrite(ofs, fname))
1022 //TexStream ts(ofs.rdbuf(), &texrow());
1024 bool failed_export = false;
1027 writeLaTeXSource(ofs, original_path,
1028 runparams, output_preamble, output_body);
1030 catch (iconv_codecvt_facet_exception & e) {
1031 lyxerr << "Caught iconv exception: " << e.what() << endl;
1032 failed_export = true;
1034 catch (std::exception const & e) {
1035 lyxerr << "Caught \"normal\" exception: " << e.what() << endl;
1036 failed_export = true;
1039 lyxerr << "Caught some really weird exception..." << endl;
1040 LyX::cref().emergencyCleanup();
1046 failed_export = true;
1047 lyxerr << "File '" << fname << "' was not closed properly." << endl;
1050 if (failed_export) {
1051 Alert::error(_("Encoding error"),
1052 _("Some characters of your document are probably not "
1053 "representable in the chosen encoding.\n"
1054 "Changing the document encoding to utf8 could help."));
1061 void Buffer::writeLaTeXSource(odocstream & os,
1062 string const & original_path,
1063 OutputParams const & runparams_in,
1064 bool const output_preamble, bool const output_body)
1066 OutputParams runparams = runparams_in;
1068 // validate the buffer.
1069 LYXERR(Debug::LATEX) << " Validating buffer..." << endl;
1070 LaTeXFeatures features(*this, params(), runparams);
1072 LYXERR(Debug::LATEX) << " Buffer validation done." << endl;
1074 // The starting paragraph of the coming rows is the
1075 // first paragraph of the document. (Asger)
1076 if (output_preamble && runparams.nice) {
1077 os << "%% LyX " << lyx_version << " created this file. "
1078 "For more info, see http://www.lyx.org/.\n"
1079 "%% Do not edit unless you really know what "
1084 LYXERR(Debug::INFO) << "lyx document header finished" << endl;
1085 // There are a few differences between nice LaTeX and usual files:
1086 // usual is \batchmode and has a
1087 // special input@path to allow the including of figures
1088 // with either \input or \includegraphics (what figinsets do).
1089 // input@path is set when the actual parameter
1090 // original_path is set. This is done for usual tex-file, but not
1091 // for nice-latex-file. (Matthias 250696)
1092 // Note that input@path is only needed for something the user does
1093 // in the preamble, included .tex files or ERT, files included by
1094 // LyX work without it.
1095 if (output_preamble) {
1096 if (!runparams.nice) {
1097 // code for usual, NOT nice-latex-file
1098 os << "\\batchmode\n"; // changed
1099 // from \nonstopmode
1102 if (!original_path.empty()) {
1104 // We don't know the encoding of inputpath
1105 docstring const inputpath = from_utf8(latex_path(original_path));
1106 os << "\\makeatletter\n"
1107 << "\\def\\input@path{{"
1108 << inputpath << "/}}\n"
1109 << "\\makeatother\n";
1115 // Write the preamble
1116 runparams.use_babel = params().writeLaTeX(os, features, texrow());
1122 os << "\\begin{document}\n";
1124 } // output_preamble
1126 texrow().start(paragraphs().begin()->id(), 0);
1128 LYXERR(Debug::INFO) << "preamble finished, now the body." << endl;
1130 if (!lyxrc.language_auto_begin &&
1131 !params().language->babel().empty()) {
1133 os << from_utf8(subst(lyxrc.language_command_begin,
1135 params().language->babel()))
1140 Encoding const & encoding = params().encoding();
1141 if (encoding.package() == Encoding::CJK) {
1142 // Open a CJK environment, since in contrast to the encodings
1143 // handled by inputenc the document encoding is not set in
1144 // the preamble if it is handled by CJK.sty.
1145 os << "\\begin{CJK}{" << from_ascii(encoding.latexName())
1150 // if we are doing a real file with body, even if this is the
1151 // child of some other buffer, let's cut the link here.
1152 // This happens for example if only a child document is printed.
1153 string save_parentname;
1154 if (output_preamble) {
1155 save_parentname = params().parentname;
1156 params().parentname.erase();
1159 loadChildDocuments(*this);
1162 latexParagraphs(*this, paragraphs(), os, texrow(), runparams);
1164 // Restore the parenthood if needed
1165 if (output_preamble)
1166 params().parentname = save_parentname;
1168 // add this just in case after all the paragraphs
1172 if (encoding.package() == Encoding::CJK) {
1173 // Close the open CJK environment.
1174 // latexParagraphs will have opened one even if the last text
1176 os << "\\end{CJK}\n";
1180 if (!lyxrc.language_auto_end &&
1181 !params().language->babel().empty()) {
1182 os << from_utf8(subst(lyxrc.language_command_end,
1184 params().language->babel()))
1189 if (output_preamble) {
1190 os << "\\end{document}\n";
1193 LYXERR(Debug::LATEX) << "makeLaTeXFile...done" << endl;
1195 LYXERR(Debug::LATEX) << "LaTeXFile for inclusion made."
1198 runparams_in.encoding = runparams.encoding;
1200 // Just to be sure. (Asger)
1203 LYXERR(Debug::INFO) << "Finished making LaTeX file." << endl;
1204 LYXERR(Debug::INFO) << "Row count was " << texrow().rows() - 1
1209 bool Buffer::isLatex() const
1211 return params().getTextClass().outputType() == LATEX;
1215 bool Buffer::isLiterate() const
1217 return params().getTextClass().outputType() == LITERATE;
1221 bool Buffer::isDocBook() const
1223 return params().getTextClass().outputType() == DOCBOOK;
1227 void Buffer::makeDocBookFile(FileName const & fname,
1228 OutputParams const & runparams,
1229 bool const body_only)
1231 LYXERR(Debug::LATEX) << "makeDocBookFile..." << endl;
1235 if (!openFileWrite(ofs, fname))
1238 writeDocBookSource(ofs, fname.absFilename(), runparams, body_only);
1242 lyxerr << "File '" << fname << "' was not closed properly." << endl;
1246 void Buffer::writeDocBookSource(odocstream & os, string const & fname,
1247 OutputParams const & runparams,
1248 bool const only_body)
1250 LaTeXFeatures features(*this, params(), runparams);
1255 TextClass const & tclass = params().getTextClass();
1256 string const top_element = tclass.latexname();
1259 if (runparams.flavor == OutputParams::XML)
1260 os << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
1263 os << "<!DOCTYPE " << from_ascii(top_element) << ' ';
1266 if (! tclass.class_header().empty())
1267 os << from_ascii(tclass.class_header());
1268 else if (runparams.flavor == OutputParams::XML)
1269 os << "PUBLIC \"-//OASIS//DTD DocBook XML//EN\" "
1270 << "\"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd\"";
1272 os << " PUBLIC \"-//OASIS//DTD DocBook V4.2//EN\"";
1274 docstring preamble = from_utf8(params().preamble);
1275 if (runparams.flavor != OutputParams::XML ) {
1276 preamble += "<!ENTITY % output.print.png \"IGNORE\">\n";
1277 preamble += "<!ENTITY % output.print.pdf \"IGNORE\">\n";
1278 preamble += "<!ENTITY % output.print.eps \"IGNORE\">\n";
1279 preamble += "<!ENTITY % output.print.bmp \"IGNORE\">\n";
1282 string const name = runparams.nice ? changeExtension(fileName(), ".sgml")
1284 preamble += features.getIncludedFiles(name);
1285 preamble += features.getLyXSGMLEntities();
1287 if (!preamble.empty()) {
1288 os << "\n [ " << preamble << " ]";
1293 string top = top_element;
1295 if (runparams.flavor == OutputParams::XML)
1296 top += params().language->code();
1298 top += params().language->code().substr(0,2);
1301 if (!params().options.empty()) {
1303 top += params().options;
1306 os << "<!-- " << ((runparams.flavor == OutputParams::XML)? "XML" : "SGML")
1307 << " file was created by LyX " << lyx_version
1308 << "\n See http://www.lyx.org/ for more information -->\n";
1310 params().getTextClass().counters().reset();
1312 loadChildDocuments(*this);
1314 sgml::openTag(os, top);
1316 docbookParagraphs(paragraphs(), *this, os, runparams);
1317 sgml::closeTag(os, top_element);
1321 // chktex should be run with these flags disabled: 3, 22, 25, 30, 38(?)
1322 // Other flags: -wall -v0 -x
1323 int Buffer::runChktex()
1327 // get LaTeX-Filename
1328 FileName const path(temppath());
1329 string const name = addName(path.absFilename(), getLatexName());
1330 string const org_path = filePath();
1332 support::Path p(path); // path to LaTeX file
1333 message(_("Running chktex..."));
1335 // Generate the LaTeX file if neccessary
1336 OutputParams runparams(¶ms().encoding());
1337 runparams.flavor = OutputParams::LATEX;
1338 runparams.nice = false;
1339 makeLaTeXFile(FileName(name), org_path, runparams);
1342 Chktex chktex(lyxrc.chktex_command, onlyFilename(name), filePath());
1343 int const res = chktex.run(terr); // run chktex
1346 Alert::error(_("chktex failure"),
1347 _("Could not run chktex successfully."));
1348 } else if (res > 0) {
1349 ErrorList & errorList = pimpl_->errorLists["ChkTeX"];
1350 // Clear out old errors
1352 // Fill-in the error list with the TeX errors
1353 bufferErrors(*this, terr, errorList);
1364 void Buffer::validate(LaTeXFeatures & features) const
1366 TextClass const & tclass = params().getTextClass();
1368 if (params().outputChanges) {
1369 bool dvipost = LaTeXFeatures::isAvailable("dvipost");
1370 bool xcolorsoul = LaTeXFeatures::isAvailable("soul") &&
1371 LaTeXFeatures::isAvailable("xcolor");
1373 if (features.runparams().flavor == OutputParams::LATEX) {
1375 features.require("ct-dvipost");
1376 features.require("dvipost");
1377 } else if (xcolorsoul) {
1378 features.require("ct-xcolor-soul");
1379 features.require("soul");
1380 features.require("xcolor");
1382 features.require("ct-none");
1384 } else if (features.runparams().flavor == OutputParams::PDFLATEX ) {
1386 features.require("ct-xcolor-soul");
1387 features.require("soul");
1388 features.require("xcolor");
1389 features.require("pdfcolmk"); // improves color handling in PDF output
1391 features.require("ct-none");
1396 // AMS Style is at document level
1397 if (params().use_amsmath == BufferParams::package_on
1398 || tclass.provides("amsmath"))
1399 features.require("amsmath");
1400 if (params().use_esint == BufferParams::package_on)
1401 features.require("esint");
1403 loadChildDocuments(*this);
1405 for_each(paragraphs().begin(), paragraphs().end(),
1406 boost::bind(&Paragraph::validate, _1, boost::ref(features)));
1408 // the bullet shapes are buffer level not paragraph level
1409 // so they are tested here
1410 for (int i = 0; i < 4; ++i) {
1411 if (params().user_defined_bullet(i) != ITEMIZE_DEFAULTS[i]) {
1412 int const font = params().user_defined_bullet(i).getFont();
1414 int const c = params()
1415 .user_defined_bullet(i)
1422 features.require("latexsym");
1424 } else if (font == 1) {
1425 features.require("amssymb");
1426 } else if ((font >= 2 && font <= 5)) {
1427 features.require("pifont");
1432 if (lyxerr.debugging(Debug::LATEX)) {
1433 features.showStruct();
1438 void Buffer::getLabelList(vector<docstring> & list) const
1440 /// if this is a child document and the parent is already loaded
1441 /// Use the parent's list instead [ale990407]
1442 Buffer const * tmp = getMasterBuffer();
1444 lyxerr << "getMasterBuffer() failed!" << endl;
1448 tmp->getLabelList(list);
1452 loadChildDocuments(*this);
1454 for (InsetIterator it = inset_iterator_begin(inset()); it; ++it)
1455 it.nextInset()->getLabelList(*this, list);
1459 void Buffer::updateBibfilesCache()
1461 // if this is a child document and the parent is already loaded
1462 // update the parent's cache instead
1463 Buffer * tmp = getMasterBuffer();
1466 tmp->updateBibfilesCache();
1470 bibfilesCache_.clear();
1471 for (InsetIterator it = inset_iterator_begin(inset()); it; ++it) {
1472 if (it->lyxCode() == BIBTEX_CODE) {
1473 InsetBibtex const & inset =
1474 static_cast<InsetBibtex const &>(*it);
1475 vector<FileName> const bibfiles = inset.getFiles(*this);
1476 bibfilesCache_.insert(bibfilesCache_.end(),
1479 } else if (it->lyxCode() == INCLUDE_CODE) {
1480 InsetInclude & inset =
1481 static_cast<InsetInclude &>(*it);
1482 inset.updateBibfilesCache(*this);
1483 vector<FileName> const & bibfiles =
1484 inset.getBibfilesCache(*this);
1485 bibfilesCache_.insert(bibfilesCache_.end(),
1493 vector<FileName> const & Buffer::getBibfilesCache() const
1495 // if this is a child document and the parent is already loaded
1496 // use the parent's cache instead
1497 Buffer const * tmp = getMasterBuffer();
1500 return tmp->getBibfilesCache();
1502 // We update the cache when first used instead of at loading time.
1503 if (bibfilesCache_.empty())
1504 const_cast<Buffer *>(this)->updateBibfilesCache();
1506 return bibfilesCache_;
1510 bool Buffer::isDepClean(string const & name) const
1512 DepClean::const_iterator const it = pimpl_->dep_clean.find(name);
1513 if (it == pimpl_->dep_clean.end())
1519 void Buffer::markDepClean(string const & name)
1521 pimpl_->dep_clean[name] = true;
1525 bool Buffer::dispatch(string const & command, bool * result)
1527 return dispatch(lyxaction.lookupFunc(command), result);
1531 bool Buffer::dispatch(FuncRequest const & func, bool * result)
1533 bool dispatched = true;
1535 switch (func.action) {
1536 case LFUN_BUFFER_EXPORT: {
1537 bool const tmp = Exporter::Export(this, to_utf8(func.argument()), false);
1550 void Buffer::changeLanguage(Language const * from, Language const * to)
1555 for_each(par_iterator_begin(),
1557 bind(&Paragraph::changeLanguage, _1, params(), from, to));
1561 bool Buffer::isMultiLingual() const
1563 ParConstIterator end = par_iterator_end();
1564 for (ParConstIterator it = par_iterator_begin(); it != end; ++it)
1565 if (it->isMultiLingual(params()))
1572 ParIterator Buffer::getParFromID(int const id) const
1574 ParConstIterator it = par_iterator_begin();
1575 ParConstIterator const end = par_iterator_end();
1578 // John says this is called with id == -1 from undo
1579 lyxerr << "getParFromID(), id: " << id << endl;
1583 for (; it != end; ++it)
1591 bool Buffer::hasParWithID(int const id) const
1593 ParConstIterator const it = getParFromID(id);
1594 return it != par_iterator_end();
1598 ParIterator Buffer::par_iterator_begin()
1600 return lyx::par_iterator_begin(inset());
1604 ParIterator Buffer::par_iterator_end()
1606 return lyx::par_iterator_end(inset());
1610 ParConstIterator Buffer::par_iterator_begin() const
1612 return lyx::par_const_iterator_begin(inset());
1616 ParConstIterator Buffer::par_iterator_end() const
1618 return lyx::par_const_iterator_end(inset());
1622 Language const * Buffer::getLanguage() const
1624 return params().language;
1628 docstring const Buffer::B_(string const & l10n) const
1630 return params().B_(l10n);
1634 bool Buffer::isClean() const
1636 return pimpl_->lyx_clean;
1640 bool Buffer::isBakClean() const
1642 return pimpl_->bak_clean;
1646 bool Buffer::isExternallyModified(CheckMethod method) const
1648 BOOST_ASSERT(fs::exists(pimpl_->filename.toFilesystemEncoding()));
1649 // if method == timestamp, check timestamp before checksum
1650 return (method == checksum_method
1651 || pimpl_->timestamp_ != fs::last_write_time(pimpl_->filename.toFilesystemEncoding()))
1652 && pimpl_->checksum_ != sum(pimpl_->filename);
1656 void Buffer::saveCheckSum(string const & file) const
1658 if (fs::exists(file)) {
1659 pimpl_->timestamp_ = fs::last_write_time(file);
1660 pimpl_->checksum_ = sum(FileName(file));
1662 // in the case of save to a new file.
1663 pimpl_->timestamp_ = 0;
1664 pimpl_->checksum_ = 0;
1669 void Buffer::markClean() const
1671 if (!pimpl_->lyx_clean) {
1672 pimpl_->lyx_clean = true;
1675 // if the .lyx file has been saved, we don't need an
1677 pimpl_->bak_clean = true;
1681 void Buffer::markBakClean() const
1683 pimpl_->bak_clean = true;
1687 void Buffer::setUnnamed(bool flag)
1689 pimpl_->unnamed = flag;
1693 bool Buffer::isUnnamed() const
1695 return pimpl_->unnamed;
1699 // FIXME: this function should be moved to buffer_pimpl.C
1700 void Buffer::markDirty()
1702 if (pimpl_->lyx_clean) {
1703 pimpl_->lyx_clean = false;
1706 pimpl_->bak_clean = false;
1708 DepClean::iterator it = pimpl_->dep_clean.begin();
1709 DepClean::const_iterator const end = pimpl_->dep_clean.end();
1711 for (; it != end; ++it)
1716 string const Buffer::fileName() const
1718 return pimpl_->filename.absFilename();
1722 string const & Buffer::filePath() const
1724 return params().filepath;
1728 bool Buffer::isReadonly() const
1730 return pimpl_->read_only;
1734 void Buffer::setParentName(string const & name)
1736 if (name == pimpl_->filename.absFilename())
1737 // Avoids recursive include.
1738 params().parentname.clear();
1740 params().parentname = name;
1744 Buffer const * Buffer::getMasterBuffer() const
1746 if (!params().parentname.empty()
1747 && theBufferList().exists(params().parentname)) {
1748 Buffer const * buf = theBufferList().getBuffer(params().parentname);
1749 //We need to check if the parent is us...
1750 //FIXME RECURSIVE INCLUDE
1751 //This is not sufficient, since recursive includes could be downstream.
1752 if (buf && buf != this)
1753 return buf->getMasterBuffer();
1760 Buffer * Buffer::getMasterBuffer()
1762 if (!params().parentname.empty()
1763 && theBufferList().exists(params().parentname)) {
1764 Buffer * buf = theBufferList().getBuffer(params().parentname);
1765 //We need to check if the parent is us...
1766 //FIXME RECURSIVE INCLUDE
1767 //This is not sufficient, since recursive includes could be downstream.
1768 if (buf && buf != this)
1769 return buf->getMasterBuffer();
1776 MacroData const & Buffer::getMacro(docstring const & name) const
1778 return pimpl_->macros.get(name);
1782 bool Buffer::hasMacro(docstring const & name) const
1784 return pimpl_->macros.has(name);
1788 void Buffer::insertMacro(docstring const & name, MacroData const & data)
1790 MacroTable::globalMacros().insert(name, data);
1791 pimpl_->macros.insert(name, data);
1795 void Buffer::buildMacros()
1797 // Start with global table.
1798 pimpl_->macros = MacroTable::globalMacros();
1801 ParagraphList const & pars = text().paragraphs();
1802 for (size_t i = 0, n = pars.size(); i != n; ++i) {
1803 //lyxerr << "searching main par " << i
1804 // << " for macro definitions" << std::endl;
1805 InsetList const & insets = pars[i].insetList();
1806 InsetList::const_iterator it = insets.begin();
1807 InsetList::const_iterator end = insets.end();
1808 for ( ; it != end; ++it) {
1809 //lyxerr << "found inset code " << it->inset->lyxCode() << std::endl;
1810 if (it->inset->lyxCode() == MATHMACRO_CODE) {
1811 MathMacroTemplate const & mac
1812 = static_cast<MathMacroTemplate const &>(*it->inset);
1813 insertMacro(mac.name(), mac.asMacroData());
1820 void Buffer::changeRefsIfUnique(docstring const & from, docstring const & to,
1823 //FIXME: This does not work for child documents yet.
1824 BOOST_ASSERT(code == CITE_CODE || code == REF_CODE);
1825 // Check if the label 'from' appears more than once
1826 vector<docstring> labels;
1828 if (code == CITE_CODE) {
1830 keys.fillWithBibKeys(this);
1831 BiblioInfo::const_iterator bit = keys.begin();
1832 BiblioInfo::const_iterator bend = keys.end();
1834 for (; bit != bend; ++bit)
1836 labels.push_back(bit->first);
1838 getLabelList(labels);
1840 if (std::count(labels.begin(), labels.end(), from) > 1)
1843 for (InsetIterator it = inset_iterator_begin(inset()); it; ++it) {
1844 if (it->lyxCode() == code) {
1845 InsetCommand & inset = static_cast<InsetCommand &>(*it);
1846 inset.replaceContents(to_utf8(from), to_utf8(to));
1852 void Buffer::getSourceCode(odocstream & os, pit_type par_begin,
1853 pit_type par_end, bool full_source)
1855 OutputParams runparams(¶ms().encoding());
1856 runparams.nice = true;
1857 runparams.flavor = OutputParams::LATEX;
1858 runparams.linelen = lyxrc.plaintext_linelen;
1859 // No side effect of file copying and image conversion
1860 runparams.dryrun = true;
1864 os << "% " << _("Preview source code") << "\n\n";
1868 writeLaTeXSource(os, filePath(), runparams, true, true);
1870 writeDocBookSource(os, fileName(), runparams, false);
1873 runparams.par_begin = par_begin;
1874 runparams.par_end = par_end;
1875 if (par_begin + 1 == par_end)
1877 << bformat(_("Preview source code for paragraph %1$d"), par_begin)
1881 << bformat(_("Preview source code from paragraph %1$s to %2$s"),
1882 convert<docstring>(par_begin),
1883 convert<docstring>(par_end - 1))
1887 // output paragraphs
1889 latexParagraphs(*this, paragraphs(), os, texrow(), runparams);
1892 docbookParagraphs(paragraphs(), *this, os, runparams);
1898 ErrorList const & Buffer::errorList(string const & type) const
1900 static ErrorList const emptyErrorList;
1901 std::map<string, ErrorList>::const_iterator I = pimpl_->errorLists.find(type);
1902 if (I == pimpl_->errorLists.end())
1903 return emptyErrorList;
1909 ErrorList & Buffer::errorList(string const & type)
1911 return pimpl_->errorLists[type];
1915 void Buffer::structureChanged() const
1918 gui_->structureChanged();
1922 void Buffer::embeddingChanged() const
1925 gui_->embeddingChanged();
1929 void Buffer::errors(std::string const & err) const
1936 void Buffer::message(docstring const & msg) const
1943 void Buffer::busy(bool on) const
1950 void Buffer::readonly(bool on) const
1957 void Buffer::updateTitles() const
1960 gui_->updateTitles();
1964 void Buffer::resetAutosaveTimers() const
1967 gui_->resetAutosaveTimers();
1971 void Buffer::setGuiDelegate(frontend::GuiBufferDelegate * gui)
1980 class AutoSaveBuffer : public support::ForkedProcess {
1983 AutoSaveBuffer(Buffer const & buffer, FileName const & fname)
1984 : buffer_(buffer), fname_(fname) {}
1986 virtual boost::shared_ptr<ForkedProcess> clone() const
1988 return boost::shared_ptr<ForkedProcess>(new AutoSaveBuffer(*this));
1993 command_ = to_utf8(bformat(_("Auto-saving %1$s"),
1994 from_utf8(fname_.absFilename())));
1995 return run(DontWait);
1999 virtual int generateChild();
2001 Buffer const & buffer_;
2006 #if !defined (HAVE_FORK)
2010 int AutoSaveBuffer::generateChild()
2012 // tmp_ret will be located (usually) in /tmp
2013 // will that be a problem?
2014 pid_t const pid = fork();
2015 // If you want to debug the autosave
2016 // you should set pid to -1, and comment out the fork.
2017 if (pid == 0 || pid == -1) {
2018 // pid = -1 signifies that lyx was unable
2019 // to fork. But we will do the save
2021 bool failed = false;
2023 FileName const tmp_ret(tempName(FileName(), "lyxauto"));
2024 if (!tmp_ret.empty()) {
2025 buffer_.writeFile(tmp_ret);
2026 // assume successful write of tmp_ret
2027 if (!rename(tmp_ret, fname_)) {
2029 // most likely couldn't move between
2030 // filesystems unless write of tmp_ret
2031 // failed so remove tmp file (if it
2040 // failed to write/rename tmp_ret so try writing direct
2041 if (!buffer_.writeFile(fname_)) {
2042 // It is dangerous to do this in the child,
2043 // but safe in the parent, so...
2044 if (pid == -1) // emit message signal.
2045 buffer_.message(_("Autosave failed!"));
2048 if (pid == 0) { // we are the child so...
2058 // Perfect target for a thread...
2059 void Buffer::autoSave() const
2061 if (isBakClean() || isReadonly()) {
2062 // We don't save now, but we'll try again later
2063 resetAutosaveTimers();
2067 // emit message signal.
2068 message(_("Autosaving current document..."));
2070 // create autosave filename
2071 string fname = filePath();
2073 fname += onlyFilename(fileName());
2076 AutoSaveBuffer autosave(*this, FileName(fname));
2080 resetAutosaveTimers();
2084 /** Write a buffer to a new file name and rename the buffer
2085 according to the new file name.
2087 This function is e.g. used by menu callbacks and
2088 LFUN_BUFFER_WRITE_AS.
2090 If 'newname' is empty (the default), the user is asked via a
2091 dialog for the buffer's new name and location.
2093 If 'newname' is non-empty and has an absolute path, that is used.
2094 Otherwise the base directory of the buffer is used as the base
2095 for any relative path in 'newname'.
2098 bool Buffer::writeAs(string const & newname)
2100 string fname = fileName();
2101 string const oldname = fname;
2103 if (newname.empty()) { /// No argument? Ask user through dialog
2106 FileDialog fileDlg(_("Choose a filename to save document as"),
2107 LFUN_BUFFER_WRITE_AS,
2108 make_pair(_("Documents|#o#O"),
2109 from_utf8(lyxrc.document_path)),
2110 make_pair(_("Templates|#T#t"),
2111 from_utf8(lyxrc.template_path)));
2113 if (!support::isLyXFilename(fname))
2116 support::FileFilterList const filter(_("LyX Documents (*.lyx)"));
2118 FileDialog::Result result =
2119 fileDlg.save(from_utf8(onlyPath(fname)),
2121 from_utf8(onlyFilename(fname)));
2123 if (result.first == FileDialog::Later)
2126 fname = to_utf8(result.second);
2131 // Make sure the absolute filename ends with appropriate suffix
2132 fname = makeAbsPath(fname).absFilename();
2133 if (!support::isLyXFilename(fname))
2137 fname = makeAbsPath(newname, onlyPath(oldname)).absFilename();
2139 if (fs::exists(FileName(fname).toFilesystemEncoding())) {
2140 docstring const file = makeDisplayPath(fname, 30);
2141 docstring text = bformat(_("The document %1$s already "
2142 "exists.\n\nDo you want to "
2143 "overwrite that document?"),
2145 int const ret = Alert::prompt(_("Overwrite document?"),
2146 text, 0, 1, _("&Overwrite"), _("&Cancel"));
2152 // Ok, change the name of the buffer
2155 bool unnamed = isUnnamed();
2157 saveCheckSum(fname);
2160 setFileName(oldname);
2161 setUnnamed(unnamed);
2162 saveCheckSum(oldname);
2166 removeAutosaveFile(oldname);
2171 bool Buffer::menuWrite()
2174 LyX::ref().session().lastFiles().add(FileName(fileName()));
2178 // FIXME: we don't tell the user *WHY* the save failed !!
2180 docstring const file = makeDisplayPath(fileName(), 30);
2182 docstring text = bformat(_("The document %1$s could not be saved.\n\n"
2183 "Do you want to rename the document and "
2184 "try again?"), file);
2185 int const ret = Alert::prompt(_("Rename and save?"),
2186 text, 0, 1, _("&Rename"), _("&Cancel"));