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 = masterBuffer();
260 if (master != this && use_gui)
261 // We are closing buf which was a child document so we
262 // must update the labels and section numbering of its master
264 updateLabels(*master);
266 if (!temppath().empty() && !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;
387 Undo & Buffer::undo()
389 return pimpl_->undo_;
393 string Buffer::latexName(bool const no_path) const
395 string const name = changeExtension(makeLatexName(absFileName()), ".tex");
396 return no_path ? onlyFilename(name) : name;
400 pair<Buffer::LogType, string> Buffer::logName() const
402 string const filename = latexName(false);
404 if (filename.empty())
405 return make_pair(Buffer::latexlog, string());
407 string const path = temppath();
409 FileName const fname(addName(temppath(),
410 onlyFilename(changeExtension(filename,
412 FileName const bname(
413 addName(path, onlyFilename(
414 changeExtension(filename,
415 formats.extension("literate") + ".out"))));
417 // If no Latex log or Build log is newer, show Build log
419 if (bname.exists() &&
420 (!fname.exists() || fname.lastModified() < bname.lastModified())) {
421 LYXERR(Debug::FILES) << "Log name calculated as: " << bname << endl;
422 return make_pair(Buffer::buildlog, bname.absFilename());
424 LYXERR(Debug::FILES) << "Log name calculated as: " << fname << endl;
425 return make_pair(Buffer::latexlog, fname.absFilename());
429 void Buffer::setReadonly(bool const flag)
431 if (pimpl_->read_only != flag) {
432 pimpl_->read_only = flag;
438 void Buffer::setFileName(string const & newfile)
440 pimpl_->filename = makeAbsPath(newfile);
441 params().filepath = onlyPath(pimpl_->filename.absFilename());
442 setReadonly(pimpl_->filename.isReadOnly());
447 int Buffer::readHeader(Lexer & lex)
449 int unknown_tokens = 0;
451 int begin_header_line = -1;
453 // Initialize parameters that may be/go lacking in header:
454 params().branchlist().clear();
455 params().preamble.erase();
456 params().options.erase();
457 params().float_placement.erase();
458 params().paperwidth.erase();
459 params().paperheight.erase();
460 params().leftmargin.erase();
461 params().rightmargin.erase();
462 params().topmargin.erase();
463 params().bottommargin.erase();
464 params().headheight.erase();
465 params().headsep.erase();
466 params().footskip.erase();
467 params().listings_params.clear();
468 params().clearLayoutModules();
469 params().pdfoptions().clear();
471 for (int i = 0; i < 4; ++i) {
472 params().user_defined_bullet(i) = ITEMIZE_DEFAULTS[i];
473 params().temp_bullet(i) = ITEMIZE_DEFAULTS[i];
476 ErrorList & errorList = pimpl_->errorLists["Parse"];
480 string const token = lex.getString();
485 if (token == "\\end_header")
489 if (token == "\\begin_header") {
490 begin_header_line = line;
494 LYXERR(Debug::PARSER) << "Handling document header token: `"
495 << token << '\'' << endl;
497 string unknown = params().readToken(lex, token);
498 if (!unknown.empty()) {
499 if (unknown[0] != '\\' && token == "\\textclass") {
500 Alert::warning(_("Unknown document class"),
501 bformat(_("Using the default document class, because the "
502 "class %1$s is unknown."), from_utf8(unknown)));
505 docstring const s = bformat(_("Unknown token: "
509 errorList.push_back(ErrorItem(_("Document header error"),
514 if (begin_header_line) {
515 docstring const s = _("\\begin_header is missing");
516 errorList.push_back(ErrorItem(_("Document header error"),
520 return unknown_tokens;
525 // changed to be public and have one parameter
526 // Returns false if "\end_document" is not read (Asger)
527 bool Buffer::readDocument(Lexer & lex)
529 ErrorList & errorList = pimpl_->errorLists["Parse"];
533 string const token = lex.getString();
534 if (token != "\\begin_document") {
535 docstring const s = _("\\begin_document is missing");
536 errorList.push_back(ErrorItem(_("Document header error"),
540 // we are reading in a brand new document
541 BOOST_ASSERT(paragraphs().empty());
544 TextClass const & baseClass = textclasslist[params().getBaseClass()];
545 if (!baseClass.load(filePath())) {
546 string theclass = baseClass.name();
547 Alert::error(_("Can't load document class"), bformat(
548 _("Using the default document class, because the "
549 "class %1$s could not be loaded."), from_utf8(theclass)));
550 params().setBaseClass(defaultTextclass());
553 if (params().outputChanges) {
554 bool dvipost = LaTeXFeatures::isAvailable("dvipost");
555 bool xcolorsoul = LaTeXFeatures::isAvailable("soul") &&
556 LaTeXFeatures::isAvailable("xcolor");
558 if (!dvipost && !xcolorsoul) {
559 Alert::warning(_("Changes not shown in LaTeX output"),
560 _("Changes will not be highlighted in LaTeX output, "
561 "because neither dvipost nor xcolor/soul are installed.\n"
562 "Please install these packages or redefine "
563 "\\lyxadded and \\lyxdeleted in the LaTeX preamble."));
564 } else if (!xcolorsoul) {
565 Alert::warning(_("Changes not shown in LaTeX output"),
566 _("Changes will not be highlighted in LaTeX output "
567 "when using pdflatex, because xcolor and soul are not installed.\n"
568 "Please install both packages or redefine "
569 "\\lyxadded and \\lyxdeleted in the LaTeX preamble."));
572 // read manifest after header
573 embeddedFiles().readManifest(lex, errorList);
576 bool const res = text().read(*this, lex, errorList);
577 for_each(text().paragraphs().begin(),
578 text().paragraphs().end(),
579 bind(&Paragraph::setInsetOwner, _1, &inset()));
585 // needed to insert the selection
586 void Buffer::insertStringAsLines(ParagraphList & pars,
587 pit_type & pit, pos_type & pos,
588 Font const & fn, docstring const & str, bool autobreakrows)
592 // insert the string, don't insert doublespace
593 bool space_inserted = true;
594 for (docstring::const_iterator cit = str.begin();
595 cit != str.end(); ++cit) {
596 Paragraph & par = pars[pit];
598 if (autobreakrows && (!par.empty() || par.allowEmpty())) {
599 breakParagraph(params(), pars, pit, pos,
600 par.layout()->isEnvironment());
603 space_inserted = true;
607 // do not insert consecutive spaces if !free_spacing
608 } else if ((*cit == ' ' || *cit == '\t') &&
609 space_inserted && !par.isFreeSpacing()) {
611 } else if (*cit == '\t') {
612 if (!par.isFreeSpacing()) {
613 // tabs are like spaces here
614 par.insertChar(pos, ' ', font, params().trackChanges);
616 space_inserted = true;
618 const pos_type n = 8 - pos % 8;
619 for (pos_type i = 0; i < n; ++i) {
620 par.insertChar(pos, ' ', font, params().trackChanges);
623 space_inserted = true;
625 } else if (!isPrintable(*cit)) {
626 // Ignore unprintables
629 // just insert the character
630 par.insertChar(pos, *cit, font, params().trackChanges);
632 space_inserted = (*cit == ' ');
639 bool Buffer::readString(std::string const & s)
641 params().compressed = false;
643 // remove dummy empty par
644 paragraphs().clear();
646 std::istringstream is(s);
648 FileName const name(tempName());
649 switch (readFile(lex, name, true)) {
653 // We need to call lyx2lyx, so write the input to a file
654 std::ofstream os(name.toFilesystemEncoding().c_str());
657 return readFile(name);
667 bool Buffer::readFile(FileName const & filename)
669 FileName fname(filename);
670 // Check if the file is compressed.
671 string format = getFormatFromContents(filename);
672 if (format == "zip") {
673 // decompress to a temp directory
674 LYXERR(Debug::FILES) << filename << " is in zip format. Unzip to " << temppath() << endl;
675 ::unzipToDir(filename.toFilesystemEncoding(), temppath());
677 FileName lyxfile(addName(temppath(), "content.lyx"));
678 // if both manifest.txt and file.lyx exist, this is am embedded file
679 if (lyxfile.exists()) {
680 params().embedded = true;
684 // The embedded lyx file can also be compressed, for backward compatibility
685 format = getFormatFromContents(fname);
686 if (format == "gzip" || format == "zip" || format == "compress") {
687 params().compressed = true;
690 // remove dummy empty par
691 paragraphs().clear();
694 if (readFile(lex, fname) != success)
701 bool Buffer::isFullyLoaded() const
703 return pimpl_->file_fully_loaded;
707 void Buffer::setFullyLoaded(bool value)
709 pimpl_->file_fully_loaded = value;
713 Buffer::ReadStatus Buffer::readFile(Lexer & lex, FileName const & filename,
716 BOOST_ASSERT(!filename.empty());
719 Alert::error(_("Document could not be read"),
720 bformat(_("%1$s could not be read."), from_utf8(filename.absFilename())));
725 string const token = lex.getString();
728 Alert::error(_("Document could not be read"),
729 bformat(_("%1$s could not be read."), from_utf8(filename.absFilename())));
733 // the first token _must_ be...
734 if (token != "\\lyxformat") {
735 lyxerr << "Token: " << token << endl;
737 Alert::error(_("Document format failure"),
738 bformat(_("%1$s is not a LyX document."),
739 from_utf8(filename.absFilename())));
744 string tmp_format = lex.getString();
745 //lyxerr << "LyX Format: `" << tmp_format << '\'' << endl;
746 // if present remove ".," from string.
747 string::size_type dot = tmp_format.find_first_of(".,");
748 //lyxerr << " dot found at " << dot << endl;
749 if (dot != string::npos)
750 tmp_format.erase(dot, 1);
751 int const file_format = convert<int>(tmp_format);
752 //lyxerr << "format: " << file_format << endl;
754 // save timestamp and checksum of the original disk file, making sure
755 // to not overwrite them with those of the file created in the tempdir
756 // when it has to be converted to the current format.
757 if (!pimpl_->checksum_) {
758 // Save the timestamp and checksum of disk file. If filename is an
759 // emergency file, save the timestamp and sum of the original lyx file
760 // because isExternallyModified will check for this file. (BUG4193)
761 string diskfile = filename.toFilesystemEncoding();
762 if (suffixIs(diskfile, ".emergency"))
763 diskfile = diskfile.substr(0, diskfile.size() - 10);
764 saveCheckSum(FileName(diskfile));
767 if (file_format != LYX_FORMAT) {
770 // lyx2lyx would fail
773 FileName const tmpfile(tempName());
774 if (tmpfile.empty()) {
775 Alert::error(_("Conversion failed"),
776 bformat(_("%1$s is from a different"
777 " version of LyX, but a temporary"
778 " file for converting it could"
780 from_utf8(filename.absFilename())));
783 FileName const lyx2lyx = libFileSearch("lyx2lyx", "lyx2lyx");
784 if (lyx2lyx.empty()) {
785 Alert::error(_("Conversion script not found"),
786 bformat(_("%1$s is from a different"
787 " version of LyX, but the"
788 " conversion script lyx2lyx"
789 " could not be found."),
790 from_utf8(filename.absFilename())));
793 ostringstream command;
794 command << os::python()
795 << ' ' << quoteName(lyx2lyx.toFilesystemEncoding())
796 << " -t " << convert<string>(LYX_FORMAT)
797 << " -o " << quoteName(tmpfile.toFilesystemEncoding())
798 << ' ' << quoteName(filename.toFilesystemEncoding());
799 string const command_str = command.str();
801 LYXERR(Debug::INFO) << "Running '"
802 << command_str << '\''
805 cmd_ret const ret = runCommand(command_str);
806 if (ret.first != 0) {
807 Alert::error(_("Conversion script failed"),
808 bformat(_("%1$s is from a different version"
809 " of LyX, but the lyx2lyx script"
810 " failed to convert it."),
811 from_utf8(filename.absFilename())));
814 bool const ret = readFile(tmpfile);
815 // Do stuff with tmpfile name and buffer name here.
816 return ret ? success : failure;
821 if (readDocument(lex)) {
822 Alert::error(_("Document format failure"),
823 bformat(_("%1$s ended unexpectedly, which means"
824 " that it is probably corrupted."),
825 from_utf8(filename.absFilename())));
828 //lyxerr << "removing " << MacroTable::localMacros().size()
829 // << " temporary macro entries" << endl;
830 //MacroTable::localMacros().clear();
832 pimpl_->file_fully_loaded = true;
837 // Should probably be moved to somewhere else: BufferView? LyXView?
838 bool Buffer::save() const
840 // We don't need autosaves in the immediate future. (Asger)
841 resetAutosaveTimers();
843 string const encodedFilename = pimpl_->filename.toFilesystemEncoding();
846 bool madeBackup = false;
848 // make a backup if the file already exists
849 if (lyxrc.make_backup && fs::exists(encodedFilename)) {
850 backupName = FileName(absFileName() + '~');
851 if (!lyxrc.backupdir_path.empty())
852 backupName = FileName(addName(lyxrc.backupdir_path,
853 subst(os::internal_path(backupName.absFilename()), '/', '!')));
856 fs::copy_file(encodedFilename, backupName.toFilesystemEncoding(), false);
858 } catch (fs::filesystem_error const & fe) {
859 Alert::error(_("Backup failure"),
860 bformat(_("Cannot create backup file %1$s.\n"
861 "Please check whether the directory exists and is writeable."),
862 from_utf8(backupName.absFilename())));
863 LYXERR(Debug::DEBUG) << "Fs error: " << fe.what() << endl;
867 // ask if the disk file has been externally modified (use checksum method)
868 if (fs::exists(encodedFilename) && isExternallyModified(checksum_method)) {
869 docstring const file = makeDisplayPath(absFileName(), 20);
870 docstring text = bformat(_("Document %1$s has been externally modified. Are you sure "
871 "you want to overwrite this file?"), file);
872 int const ret = Alert::prompt(_("Overwrite modified file?"),
873 text, 1, 1, _("&Overwrite"), _("&Cancel"));
878 if (writeFile(pimpl_->filename)) {
880 removeAutosaveFile(absFileName());
881 saveCheckSum(pimpl_->filename);
884 // Saving failed, so backup is not backup
886 rename(backupName, pimpl_->filename);
892 bool Buffer::writeFile(FileName const & fname) const
894 if (pimpl_->read_only && fname == pimpl_->filename)
900 if (params().embedded)
901 // first write the .lyx file to the temporary directory
902 content = FileName(addName(temppath(), "content.lyx"));
906 if (params().compressed) {
907 gz::ogzstream ofs(content.toFilesystemEncoding().c_str(), ios::out|ios::trunc);
913 ofstream ofs(content.toFilesystemEncoding().c_str(), ios::out|ios::trunc);
920 if (retval && params().embedded) {
921 // write file.lyx and all the embedded files to the zip file fname
922 // if embedding is enabled
923 return pimpl_->embedded_files.writeFile(fname);
929 bool Buffer::write(ostream & ofs) const
932 // Use the standard "C" locale for file output.
933 ofs.imbue(std::locale::classic());
936 // The top of the file should not be written by params().
938 // write out a comment in the top of the file
939 ofs << "#LyX " << lyx_version
940 << " created this file. For more info see http://www.lyx.org/\n"
941 << "\\lyxformat " << LYX_FORMAT << "\n"
942 << "\\begin_document\n";
945 /// For each author, set 'used' to true if there is a change
946 /// by this author in the document; otherwise set it to 'false'.
947 AuthorList::Authors::const_iterator a_it = params().authors().begin();
948 AuthorList::Authors::const_iterator a_end = params().authors().end();
949 for (; a_it != a_end; ++a_it)
950 a_it->second.used(false);
952 ParIterator const end = par_iterator_end();
953 ParIterator it = par_iterator_begin();
954 for ( ; it != end; ++it)
955 it->checkAuthors(params().authors());
957 // now write out the buffer parameters.
958 ofs << "\\begin_header\n";
959 params().writeFile(ofs);
960 ofs << "\\end_header\n";
962 // write the manifest after header
963 ofs << "\n\\begin_manifest\n";
964 pimpl_->embedded_files.update();
965 embeddedFiles().writeManifest(ofs);
966 ofs << "\\end_manifest\n";
969 ofs << "\n\\begin_body\n";
970 text().write(*this, ofs);
971 ofs << "\n\\end_body\n";
973 // Write marker that shows file is complete
974 ofs << "\\end_document" << endl;
976 // Shouldn't really be needed....
979 // how to check if close went ok?
980 // Following is an attempt... (BE 20001011)
982 // good() returns false if any error occured, including some
984 // bad() returns true if something bad happened in the buffer,
985 // which should include file system full errors.
990 lyxerr << "File was not closed properly." << endl;
997 bool Buffer::makeLaTeXFile(FileName const & fname,
998 string const & original_path,
999 OutputParams const & runparams,
1000 bool output_preamble, bool output_body)
1002 string const encoding = runparams.encoding->iconvName();
1003 LYXERR(Debug::LATEX) << "makeLaTeXFile encoding: "
1004 << encoding << "..." << endl;
1006 odocfstream ofs(encoding);
1007 if (!openFileWrite(ofs, fname))
1010 //TexStream ts(ofs.rdbuf(), &texrow());
1012 bool failed_export = false;
1015 writeLaTeXSource(ofs, original_path,
1016 runparams, output_preamble, output_body);
1018 catch (iconv_codecvt_facet_exception & e) {
1019 lyxerr << "Caught iconv exception: " << e.what() << endl;
1020 failed_export = true;
1022 catch (std::exception const & e) {
1023 lyxerr << "Caught \"normal\" exception: " << e.what() << endl;
1024 failed_export = true;
1027 lyxerr << "Caught some really weird exception..." << endl;
1028 LyX::cref().emergencyCleanup();
1034 failed_export = true;
1035 lyxerr << "File '" << fname << "' was not closed properly." << endl;
1038 if (failed_export) {
1039 Alert::error(_("Encoding error"),
1040 _("Some characters of your document are probably not "
1041 "representable in the chosen encoding.\n"
1042 "Changing the document encoding to utf8 could help."));
1049 void Buffer::writeLaTeXSource(odocstream & os,
1050 string const & original_path,
1051 OutputParams const & runparams_in,
1052 bool const output_preamble, bool const output_body)
1054 OutputParams runparams = runparams_in;
1056 // validate the buffer.
1057 LYXERR(Debug::LATEX) << " Validating buffer..." << endl;
1058 LaTeXFeatures features(*this, params(), runparams);
1060 LYXERR(Debug::LATEX) << " Buffer validation done." << endl;
1062 // The starting paragraph of the coming rows is the
1063 // first paragraph of the document. (Asger)
1064 if (output_preamble && runparams.nice) {
1065 os << "%% LyX " << lyx_version << " created this file. "
1066 "For more info, see http://www.lyx.org/.\n"
1067 "%% Do not edit unless you really know what "
1072 LYXERR(Debug::INFO) << "lyx document header finished" << endl;
1073 // There are a few differences between nice LaTeX and usual files:
1074 // usual is \batchmode and has a
1075 // special input@path to allow the including of figures
1076 // with either \input or \includegraphics (what figinsets do).
1077 // input@path is set when the actual parameter
1078 // original_path is set. This is done for usual tex-file, but not
1079 // for nice-latex-file. (Matthias 250696)
1080 // Note that input@path is only needed for something the user does
1081 // in the preamble, included .tex files or ERT, files included by
1082 // LyX work without it.
1083 if (output_preamble) {
1084 if (!runparams.nice) {
1085 // code for usual, NOT nice-latex-file
1086 os << "\\batchmode\n"; // changed
1087 // from \nonstopmode
1090 if (!original_path.empty()) {
1092 // We don't know the encoding of inputpath
1093 docstring const inputpath = from_utf8(latex_path(original_path));
1094 os << "\\makeatletter\n"
1095 << "\\def\\input@path{{"
1096 << inputpath << "/}}\n"
1097 << "\\makeatother\n";
1103 // Write the preamble
1104 runparams.use_babel = params().writeLaTeX(os, features, texrow());
1110 os << "\\begin{document}\n";
1112 } // output_preamble
1114 texrow().start(paragraphs().begin()->id(), 0);
1116 LYXERR(Debug::INFO) << "preamble finished, now the body." << endl;
1118 if (!lyxrc.language_auto_begin &&
1119 !params().language->babel().empty()) {
1121 os << from_utf8(subst(lyxrc.language_command_begin,
1123 params().language->babel()))
1128 Encoding const & encoding = params().encoding();
1129 if (encoding.package() == Encoding::CJK) {
1130 // Open a CJK environment, since in contrast to the encodings
1131 // handled by inputenc the document encoding is not set in
1132 // the preamble if it is handled by CJK.sty.
1133 os << "\\begin{CJK}{" << from_ascii(encoding.latexName())
1138 // if we are doing a real file with body, even if this is the
1139 // child of some other buffer, let's cut the link here.
1140 // This happens for example if only a child document is printed.
1141 string save_parentname;
1142 if (output_preamble) {
1143 save_parentname = params().parentname;
1144 params().parentname.erase();
1147 loadChildDocuments();
1150 latexParagraphs(*this, paragraphs(), os, texrow(), runparams);
1152 // Restore the parenthood if needed
1153 if (output_preamble)
1154 params().parentname = save_parentname;
1156 // add this just in case after all the paragraphs
1160 if (encoding.package() == Encoding::CJK) {
1161 // Close the open CJK environment.
1162 // latexParagraphs will have opened one even if the last text
1164 os << "\\end{CJK}\n";
1168 if (!lyxrc.language_auto_end &&
1169 !params().language->babel().empty()) {
1170 os << from_utf8(subst(lyxrc.language_command_end,
1172 params().language->babel()))
1177 if (output_preamble) {
1178 os << "\\end{document}\n";
1181 LYXERR(Debug::LATEX) << "makeLaTeXFile...done" << endl;
1183 LYXERR(Debug::LATEX) << "LaTeXFile for inclusion made."
1186 runparams_in.encoding = runparams.encoding;
1188 // Just to be sure. (Asger)
1191 LYXERR(Debug::INFO) << "Finished making LaTeX file." << endl;
1192 LYXERR(Debug::INFO) << "Row count was " << texrow().rows() - 1
1197 bool Buffer::isLatex() const
1199 return params().getTextClass().outputType() == LATEX;
1203 bool Buffer::isLiterate() const
1205 return params().getTextClass().outputType() == LITERATE;
1209 bool Buffer::isDocBook() const
1211 return params().getTextClass().outputType() == DOCBOOK;
1215 void Buffer::makeDocBookFile(FileName const & fname,
1216 OutputParams const & runparams,
1217 bool const body_only)
1219 LYXERR(Debug::LATEX) << "makeDocBookFile..." << endl;
1223 if (!openFileWrite(ofs, fname))
1226 writeDocBookSource(ofs, fname.absFilename(), runparams, body_only);
1230 lyxerr << "File '" << fname << "' was not closed properly." << endl;
1234 void Buffer::writeDocBookSource(odocstream & os, string const & fname,
1235 OutputParams const & runparams,
1236 bool const only_body)
1238 LaTeXFeatures features(*this, params(), runparams);
1243 TextClass const & tclass = params().getTextClass();
1244 string const top_element = tclass.latexname();
1247 if (runparams.flavor == OutputParams::XML)
1248 os << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
1251 os << "<!DOCTYPE " << from_ascii(top_element) << ' ';
1254 if (! tclass.class_header().empty())
1255 os << from_ascii(tclass.class_header());
1256 else if (runparams.flavor == OutputParams::XML)
1257 os << "PUBLIC \"-//OASIS//DTD DocBook XML//EN\" "
1258 << "\"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd\"";
1260 os << " PUBLIC \"-//OASIS//DTD DocBook V4.2//EN\"";
1262 docstring preamble = from_utf8(params().preamble);
1263 if (runparams.flavor != OutputParams::XML ) {
1264 preamble += "<!ENTITY % output.print.png \"IGNORE\">\n";
1265 preamble += "<!ENTITY % output.print.pdf \"IGNORE\">\n";
1266 preamble += "<!ENTITY % output.print.eps \"IGNORE\">\n";
1267 preamble += "<!ENTITY % output.print.bmp \"IGNORE\">\n";
1270 string const name = runparams.nice
1271 ? changeExtension(absFileName(), ".sgml") : fname;
1272 preamble += features.getIncludedFiles(name);
1273 preamble += features.getLyXSGMLEntities();
1275 if (!preamble.empty()) {
1276 os << "\n [ " << preamble << " ]";
1281 string top = top_element;
1283 if (runparams.flavor == OutputParams::XML)
1284 top += params().language->code();
1286 top += params().language->code().substr(0,2);
1289 if (!params().options.empty()) {
1291 top += params().options;
1294 os << "<!-- " << ((runparams.flavor == OutputParams::XML)? "XML" : "SGML")
1295 << " file was created by LyX " << lyx_version
1296 << "\n See http://www.lyx.org/ for more information -->\n";
1298 params().getTextClass().counters().reset();
1300 loadChildDocuments();
1302 sgml::openTag(os, top);
1304 docbookParagraphs(paragraphs(), *this, os, runparams);
1305 sgml::closeTag(os, top_element);
1309 // chktex should be run with these flags disabled: 3, 22, 25, 30, 38(?)
1310 // Other flags: -wall -v0 -x
1311 int Buffer::runChktex()
1315 // get LaTeX-Filename
1316 FileName const path(temppath());
1317 string const name = addName(path.absFilename(), latexName());
1318 string const org_path = filePath();
1320 support::Path p(path); // path to LaTeX file
1321 message(_("Running chktex..."));
1323 // Generate the LaTeX file if neccessary
1324 OutputParams runparams(¶ms().encoding());
1325 runparams.flavor = OutputParams::LATEX;
1326 runparams.nice = false;
1327 makeLaTeXFile(FileName(name), org_path, runparams);
1330 Chktex chktex(lyxrc.chktex_command, onlyFilename(name), filePath());
1331 int const res = chktex.run(terr); // run chktex
1334 Alert::error(_("chktex failure"),
1335 _("Could not run chktex successfully."));
1336 } else if (res > 0) {
1337 ErrorList & errorList = pimpl_->errorLists["ChkTeX"];
1338 // Clear out old errors
1340 // Fill-in the error list with the TeX errors
1341 bufferErrors(*this, terr, errorList);
1352 void Buffer::validate(LaTeXFeatures & features) const
1354 TextClass const & tclass = params().getTextClass();
1356 if (params().outputChanges) {
1357 bool dvipost = LaTeXFeatures::isAvailable("dvipost");
1358 bool xcolorsoul = LaTeXFeatures::isAvailable("soul") &&
1359 LaTeXFeatures::isAvailable("xcolor");
1361 if (features.runparams().flavor == OutputParams::LATEX) {
1363 features.require("ct-dvipost");
1364 features.require("dvipost");
1365 } else if (xcolorsoul) {
1366 features.require("ct-xcolor-soul");
1367 features.require("soul");
1368 features.require("xcolor");
1370 features.require("ct-none");
1372 } else if (features.runparams().flavor == OutputParams::PDFLATEX ) {
1374 features.require("ct-xcolor-soul");
1375 features.require("soul");
1376 features.require("xcolor");
1377 features.require("pdfcolmk"); // improves color handling in PDF output
1379 features.require("ct-none");
1384 // AMS Style is at document level
1385 if (params().use_amsmath == BufferParams::package_on
1386 || tclass.provides("amsmath"))
1387 features.require("amsmath");
1388 if (params().use_esint == BufferParams::package_on)
1389 features.require("esint");
1391 loadChildDocuments();
1393 for_each(paragraphs().begin(), paragraphs().end(),
1394 boost::bind(&Paragraph::validate, _1, boost::ref(features)));
1396 // the bullet shapes are buffer level not paragraph level
1397 // so they are tested here
1398 for (int i = 0; i < 4; ++i) {
1399 if (params().user_defined_bullet(i) != ITEMIZE_DEFAULTS[i]) {
1400 int const font = params().user_defined_bullet(i).getFont();
1402 int const c = params()
1403 .user_defined_bullet(i)
1410 features.require("latexsym");
1412 } else if (font == 1) {
1413 features.require("amssymb");
1414 } else if ((font >= 2 && font <= 5)) {
1415 features.require("pifont");
1420 if (lyxerr.debugging(Debug::LATEX)) {
1421 features.showStruct();
1426 void Buffer::getLabelList(vector<docstring> & list) const
1428 /// if this is a child document and the parent is already loaded
1429 /// Use the parent's list instead [ale990407]
1430 Buffer const * tmp = masterBuffer();
1432 lyxerr << "masterBuffer() failed!" << endl;
1436 tmp->getLabelList(list);
1440 loadChildDocuments();
1442 for (InsetIterator it = inset_iterator_begin(inset()); it; ++it)
1443 it.nextInset()->getLabelList(*this, list);
1447 void Buffer::updateBibfilesCache()
1449 // if this is a child document and the parent is already loaded
1450 // update the parent's cache instead
1451 Buffer * tmp = masterBuffer();
1454 tmp->updateBibfilesCache();
1458 bibfilesCache_.clear();
1459 for (InsetIterator it = inset_iterator_begin(inset()); it; ++it) {
1460 if (it->lyxCode() == BIBTEX_CODE) {
1461 InsetBibtex const & inset =
1462 static_cast<InsetBibtex const &>(*it);
1463 vector<FileName> const bibfiles = inset.getFiles(*this);
1464 bibfilesCache_.insert(bibfilesCache_.end(),
1467 } else if (it->lyxCode() == INCLUDE_CODE) {
1468 InsetInclude & inset =
1469 static_cast<InsetInclude &>(*it);
1470 inset.updateBibfilesCache(*this);
1471 vector<FileName> const & bibfiles =
1472 inset.getBibfilesCache(*this);
1473 bibfilesCache_.insert(bibfilesCache_.end(),
1481 vector<FileName> const & Buffer::getBibfilesCache() const
1483 // if this is a child document and the parent is already loaded
1484 // use the parent's cache instead
1485 Buffer const * tmp = masterBuffer();
1488 return tmp->getBibfilesCache();
1490 // We update the cache when first used instead of at loading time.
1491 if (bibfilesCache_.empty())
1492 const_cast<Buffer *>(this)->updateBibfilesCache();
1494 return bibfilesCache_;
1498 bool Buffer::isDepClean(string const & name) const
1500 DepClean::const_iterator const it = pimpl_->dep_clean.find(name);
1501 if (it == pimpl_->dep_clean.end())
1507 void Buffer::markDepClean(string const & name)
1509 pimpl_->dep_clean[name] = true;
1513 bool Buffer::dispatch(string const & command, bool * result)
1515 return dispatch(lyxaction.lookupFunc(command), result);
1519 bool Buffer::dispatch(FuncRequest const & func, bool * result)
1521 bool dispatched = true;
1523 switch (func.action) {
1524 case LFUN_BUFFER_EXPORT: {
1525 bool const tmp = Exporter::Export(this, to_utf8(func.argument()), false);
1538 void Buffer::changeLanguage(Language const * from, Language const * to)
1543 for_each(par_iterator_begin(),
1545 bind(&Paragraph::changeLanguage, _1, params(), from, to));
1549 bool Buffer::isMultiLingual() const
1551 ParConstIterator end = par_iterator_end();
1552 for (ParConstIterator it = par_iterator_begin(); it != end; ++it)
1553 if (it->isMultiLingual(params()))
1560 ParIterator Buffer::getParFromID(int const id) const
1562 ParConstIterator it = par_iterator_begin();
1563 ParConstIterator const end = par_iterator_end();
1566 // John says this is called with id == -1 from undo
1567 lyxerr << "getParFromID(), id: " << id << endl;
1571 for (; it != end; ++it)
1579 bool Buffer::hasParWithID(int const id) const
1581 ParConstIterator const it = getParFromID(id);
1582 return it != par_iterator_end();
1586 ParIterator Buffer::par_iterator_begin()
1588 return lyx::par_iterator_begin(inset());
1592 ParIterator Buffer::par_iterator_end()
1594 return lyx::par_iterator_end(inset());
1598 ParConstIterator Buffer::par_iterator_begin() const
1600 return lyx::par_const_iterator_begin(inset());
1604 ParConstIterator Buffer::par_iterator_end() const
1606 return lyx::par_const_iterator_end(inset());
1610 Language const * Buffer::language() const
1612 return params().language;
1616 docstring const Buffer::B_(string const & l10n) const
1618 return params().B_(l10n);
1622 bool Buffer::isClean() const
1624 return pimpl_->lyx_clean;
1628 bool Buffer::isBakClean() const
1630 return pimpl_->bak_clean;
1634 bool Buffer::isExternallyModified(CheckMethod method) const
1636 BOOST_ASSERT(pimpl_->filename.exists());
1637 // if method == timestamp, check timestamp before checksum
1638 return (method == checksum_method
1639 || pimpl_->timestamp_ != pimpl_->filename.lastModified())
1640 && pimpl_->checksum_ != sum(pimpl_->filename);
1644 void Buffer::saveCheckSum(FileName const & file) const
1646 if (file.exists()) {
1647 pimpl_->timestamp_ = file.lastModified();
1648 pimpl_->checksum_ = sum(file);
1650 // in the case of save to a new file.
1651 pimpl_->timestamp_ = 0;
1652 pimpl_->checksum_ = 0;
1657 void Buffer::markClean() const
1659 if (!pimpl_->lyx_clean) {
1660 pimpl_->lyx_clean = true;
1663 // if the .lyx file has been saved, we don't need an
1665 pimpl_->bak_clean = true;
1669 void Buffer::markBakClean() const
1671 pimpl_->bak_clean = true;
1675 void Buffer::setUnnamed(bool flag)
1677 pimpl_->unnamed = flag;
1681 bool Buffer::isUnnamed() const
1683 return pimpl_->unnamed;
1687 // FIXME: this function should be moved to buffer_pimpl.C
1688 void Buffer::markDirty()
1690 if (pimpl_->lyx_clean) {
1691 pimpl_->lyx_clean = false;
1694 pimpl_->bak_clean = false;
1696 DepClean::iterator it = pimpl_->dep_clean.begin();
1697 DepClean::const_iterator const end = pimpl_->dep_clean.end();
1699 for (; it != end; ++it)
1704 string Buffer::absFileName() const
1706 return pimpl_->filename.absFilename();
1710 string const & Buffer::filePath() const
1712 return params().filepath;
1716 bool Buffer::isReadonly() const
1718 return pimpl_->read_only;
1722 void Buffer::setParentName(string const & name)
1724 if (name == pimpl_->filename.absFilename())
1725 // Avoids recursive include.
1726 params().parentname.clear();
1728 params().parentname = name;
1732 Buffer const * Buffer::masterBuffer() const
1734 if (!params().parentname.empty()
1735 && theBufferList().exists(params().parentname)) {
1736 Buffer const * buf = theBufferList().getBuffer(params().parentname);
1737 //We need to check if the parent is us...
1738 //FIXME RECURSIVE INCLUDE
1739 //This is not sufficient, since recursive includes could be downstream.
1740 if (buf && buf != this)
1741 return buf->masterBuffer();
1748 Buffer * Buffer::masterBuffer()
1750 if (!params().parentname.empty()
1751 && theBufferList().exists(params().parentname)) {
1752 Buffer * buf = theBufferList().getBuffer(params().parentname);
1753 //We need to check if the parent is us...
1754 //FIXME RECURSIVE INCLUDE
1755 //This is not sufficient, since recursive includes could be downstream.
1756 if (buf && buf != this)
1757 return buf->masterBuffer();
1764 MacroData const & Buffer::getMacro(docstring const & name) const
1766 return pimpl_->macros.get(name);
1770 bool Buffer::hasMacro(docstring const & name) const
1772 return pimpl_->macros.has(name);
1776 void Buffer::insertMacro(docstring const & name, MacroData const & data)
1778 MacroTable::globalMacros().insert(name, data);
1779 pimpl_->macros.insert(name, data);
1783 void Buffer::buildMacros()
1785 // Start with global table.
1786 pimpl_->macros = MacroTable::globalMacros();
1789 ParagraphList const & pars = text().paragraphs();
1790 for (size_t i = 0, n = pars.size(); i != n; ++i) {
1791 //lyxerr << "searching main par " << i
1792 // << " for macro definitions" << std::endl;
1793 InsetList const & insets = pars[i].insetList();
1794 InsetList::const_iterator it = insets.begin();
1795 InsetList::const_iterator end = insets.end();
1796 for ( ; it != end; ++it) {
1797 //lyxerr << "found inset code " << it->inset->lyxCode() << std::endl;
1798 if (it->inset->lyxCode() == MATHMACRO_CODE) {
1799 MathMacroTemplate const & mac
1800 = static_cast<MathMacroTemplate const &>(*it->inset);
1801 insertMacro(mac.name(), mac.asMacroData());
1808 void Buffer::changeRefsIfUnique(docstring const & from, docstring const & to,
1811 //FIXME: This does not work for child documents yet.
1812 BOOST_ASSERT(code == CITE_CODE || code == REF_CODE);
1813 // Check if the label 'from' appears more than once
1814 vector<docstring> labels;
1816 if (code == CITE_CODE) {
1818 keys.fillWithBibKeys(this);
1819 BiblioInfo::const_iterator bit = keys.begin();
1820 BiblioInfo::const_iterator bend = keys.end();
1822 for (; bit != bend; ++bit)
1824 labels.push_back(bit->first);
1826 getLabelList(labels);
1828 if (std::count(labels.begin(), labels.end(), from) > 1)
1831 for (InsetIterator it = inset_iterator_begin(inset()); it; ++it) {
1832 if (it->lyxCode() == code) {
1833 InsetCommand & inset = static_cast<InsetCommand &>(*it);
1834 inset.replaceContents(to_utf8(from), to_utf8(to));
1840 void Buffer::getSourceCode(odocstream & os, pit_type par_begin,
1841 pit_type par_end, bool full_source)
1843 OutputParams runparams(¶ms().encoding());
1844 runparams.nice = true;
1845 runparams.flavor = OutputParams::LATEX;
1846 runparams.linelen = lyxrc.plaintext_linelen;
1847 // No side effect of file copying and image conversion
1848 runparams.dryrun = true;
1852 os << "% " << _("Preview source code") << "\n\n";
1856 writeLaTeXSource(os, filePath(), runparams, true, true);
1858 writeDocBookSource(os, absFileName(), runparams, false);
1861 runparams.par_begin = par_begin;
1862 runparams.par_end = par_end;
1863 if (par_begin + 1 == par_end)
1865 << bformat(_("Preview source code for paragraph %1$d"), par_begin)
1869 << bformat(_("Preview source code from paragraph %1$s to %2$s"),
1870 convert<docstring>(par_begin),
1871 convert<docstring>(par_end - 1))
1875 // output paragraphs
1877 latexParagraphs(*this, paragraphs(), os, texrow(), runparams);
1880 docbookParagraphs(paragraphs(), *this, os, runparams);
1886 ErrorList const & Buffer::errorList(string const & type) const
1888 static ErrorList const emptyErrorList;
1889 std::map<string, ErrorList>::const_iterator I = pimpl_->errorLists.find(type);
1890 if (I == pimpl_->errorLists.end())
1891 return emptyErrorList;
1897 ErrorList & Buffer::errorList(string const & type)
1899 return pimpl_->errorLists[type];
1903 void Buffer::structureChanged() const
1906 gui_->structureChanged();
1910 void Buffer::embeddingChanged() const
1913 gui_->embeddingChanged();
1917 void Buffer::errors(std::string const & err) const
1924 void Buffer::message(docstring const & msg) const
1931 void Buffer::busy(bool on) const
1938 void Buffer::readonly(bool on) const
1945 void Buffer::updateTitles() const
1948 gui_->updateTitles();
1952 void Buffer::resetAutosaveTimers() const
1955 gui_->resetAutosaveTimers();
1959 void Buffer::setGuiDelegate(frontend::GuiBufferDelegate * gui)
1968 class AutoSaveBuffer : public support::ForkedProcess {
1971 AutoSaveBuffer(Buffer const & buffer, FileName const & fname)
1972 : buffer_(buffer), fname_(fname) {}
1974 virtual boost::shared_ptr<ForkedProcess> clone() const
1976 return boost::shared_ptr<ForkedProcess>(new AutoSaveBuffer(*this));
1981 command_ = to_utf8(bformat(_("Auto-saving %1$s"),
1982 from_utf8(fname_.absFilename())));
1983 return run(DontWait);
1987 virtual int generateChild();
1989 Buffer const & buffer_;
1994 #if !defined (HAVE_FORK)
1998 int AutoSaveBuffer::generateChild()
2000 // tmp_ret will be located (usually) in /tmp
2001 // will that be a problem?
2002 pid_t const pid = fork();
2003 // If you want to debug the autosave
2004 // you should set pid to -1, and comment out the fork.
2005 if (pid == 0 || pid == -1) {
2006 // pid = -1 signifies that lyx was unable
2007 // to fork. But we will do the save
2009 bool failed = false;
2011 FileName const tmp_ret(tempName(FileName(), "lyxauto"));
2012 if (!tmp_ret.empty()) {
2013 buffer_.writeFile(tmp_ret);
2014 // assume successful write of tmp_ret
2015 if (!rename(tmp_ret, fname_)) {
2017 // most likely couldn't move between
2018 // filesystems unless write of tmp_ret
2019 // failed so remove tmp file (if it
2028 // failed to write/rename tmp_ret so try writing direct
2029 if (!buffer_.writeFile(fname_)) {
2030 // It is dangerous to do this in the child,
2031 // but safe in the parent, so...
2032 if (pid == -1) // emit message signal.
2033 buffer_.message(_("Autosave failed!"));
2036 if (pid == 0) { // we are the child so...
2046 // Perfect target for a thread...
2047 void Buffer::autoSave() const
2049 if (isBakClean() || isReadonly()) {
2050 // We don't save now, but we'll try again later
2051 resetAutosaveTimers();
2055 // emit message signal.
2056 message(_("Autosaving current document..."));
2058 // create autosave filename
2059 string fname = filePath();
2061 fname += onlyFilename(absFileName());
2064 AutoSaveBuffer autosave(*this, FileName(fname));
2068 resetAutosaveTimers();
2072 /** Write a buffer to a new file name and rename the buffer
2073 according to the new file name.
2075 This function is e.g. used by menu callbacks and
2076 LFUN_BUFFER_WRITE_AS.
2078 If 'newname' is empty (the default), the user is asked via a
2079 dialog for the buffer's new name and location.
2081 If 'newname' is non-empty and has an absolute path, that is used.
2082 Otherwise the base directory of the buffer is used as the base
2083 for any relative path in 'newname'.
2086 bool Buffer::writeAs(string const & newname)
2088 string fname = absFileName();
2089 string const oldname = fname;
2091 if (newname.empty()) { /// No argument? Ask user through dialog
2094 FileDialog fileDlg(_("Choose a filename to save document as"),
2095 LFUN_BUFFER_WRITE_AS,
2096 make_pair(_("Documents|#o#O"),
2097 from_utf8(lyxrc.document_path)),
2098 make_pair(_("Templates|#T#t"),
2099 from_utf8(lyxrc.template_path)));
2101 if (!support::isLyXFilename(fname))
2104 support::FileFilterList const filter(_("LyX Documents (*.lyx)"));
2106 FileDialog::Result result =
2107 fileDlg.save(from_utf8(onlyPath(fname)),
2109 from_utf8(onlyFilename(fname)));
2111 if (result.first == FileDialog::Later)
2114 fname = to_utf8(result.second);
2119 // Make sure the absolute filename ends with appropriate suffix
2120 fname = makeAbsPath(fname).absFilename();
2121 if (!support::isLyXFilename(fname))
2125 fname = makeAbsPath(newname, onlyPath(oldname)).absFilename();
2127 if (FileName(fname).exists()) {
2128 docstring const file = makeDisplayPath(fname, 30);
2129 docstring text = bformat(_("The document %1$s already "
2130 "exists.\n\nDo you want to "
2131 "overwrite that document?"),
2133 int const ret = Alert::prompt(_("Overwrite document?"),
2134 text, 0, 1, _("&Overwrite"), _("&Cancel"));
2140 // Ok, change the name of the buffer
2143 bool unnamed = isUnnamed();
2145 saveCheckSum(FileName(fname));
2148 setFileName(oldname);
2149 setUnnamed(unnamed);
2150 saveCheckSum(FileName(oldname));
2154 removeAutosaveFile(oldname);
2159 bool Buffer::menuWrite()
2162 LyX::ref().session().lastFiles().add(FileName(absFileName()));
2166 // FIXME: we don't tell the user *WHY* the save failed !!
2168 docstring const file = makeDisplayPath(absFileName(), 30);
2170 docstring text = bformat(_("The document %1$s could not be saved.\n\n"
2171 "Do you want to rename the document and "
2172 "try again?"), file);
2173 int const ret = Alert::prompt(_("Rename and save?"),
2174 text, 0, 1, _("&Rename"), _("&Cancel"));
2183 void Buffer::loadChildDocuments() const
2185 bool parse_error = false;
2187 for (InsetIterator it = inset_iterator_begin(inset()); it; ++it) {
2188 if (it->lyxCode() != INCLUDE_CODE)
2190 InsetInclude const & inset = static_cast<InsetInclude const &>(*it);
2191 InsetCommandParams const & ip = inset.params();
2192 Buffer * child = loadIfNeeded(*this, ip);
2195 parse_error |= !child->errorList("Parse").empty();
2196 child->loadChildDocuments();
2199 if (use_gui && masterBuffer() == this)
2200 updateLabels(*this);
2204 string Buffer::bufferFormat() const