#include "WordLangTuple.h"
#include "WordList.h"
-#include "insets/InsetBibitem.h"
#include "insets/InsetBibtex.h"
#include "insets/InsetBranch.h"
#include "insets/InsetInclude.h"
// Do not remove the comment below, so we get merge conflict in
// independent branches. Instead add your own.
-int const LYX_FORMAT = 404; // rgh: refstyle
+int const LYX_FORMAT = 413; // rgh: html_css_as_file
typedef map<string, bool> DepClean;
typedef map<docstring, pair<InsetLabel const *, Buffer::References> > RefCache;
/// is autosave needed?
mutable bool bak_clean;
- /// is this a unnamed file (New...)?
+ /// is this an unnamed file (New...)?
bool unnamed;
/// buffer is r/o
from_utf8(d->temppath.absFileName())));
}
- // Remove any previewed LaTeX snippets associated with this buffer.
if (!isClone())
- thePreviews().removeLoader(*this);
+ removePreviews();
delete d;
}
}
-void Buffer::setFileName(string const & newfile)
+void Buffer::setFileName(FileName const & fname)
{
- d->filename = makeAbsPath(newfile);
+ d->filename = fname;
setReadonly(d->filename.isReadOnly());
+ saveCheckSum();
updateTitles();
}
params().headsep.erase();
params().footskip.erase();
params().columnsep.erase();
- params().fontsCJK.erase();
+ params().fonts_cjk.erase();
params().listings_params.clear();
params().clearLayoutModules();
params().clearRemovedModules();
ErrorList & errorList = d->errorLists["Parse"];
errorList.clear();
+ // remove dummy empty par
+ paragraphs().clear();
+
if (!lex.checkFor("\\begin_document")) {
docstring const s = _("\\begin_document is missing");
errorList.push_back(ErrorItem(_("Document header error"),
s, -1, 0, 0));
}
- // we are reading in a brand new document
- LASSERT(paragraphs().empty(), /**/);
-
readHeader(lex);
if (params().outputChanges) {
// read main text
bool const res = text().read(lex, errorList, d->inset);
+ // inform parent buffer about local macros
+ if (parent()) {
+ Buffer const * pbuf = parent();
+ UserMacroSet::const_iterator cit = usermacros.begin();
+ UserMacroSet::const_iterator end = usermacros.end();
+ for (; cit != end; ++cit)
+ pbuf->usermacros.insert(*cit);
+ }
usermacros.clear();
updateMacros();
updateMacroInstances();
{
params().compressed = false;
- // remove dummy empty par
- paragraphs().clear();
Lexer lex;
istringstream is(s);
lex.setStream(is);
- FileName const name = FileName::tempName("Buffer_readString");
- switch (readFile(lex, name, true)) {
- case failure:
- return false;
- case wrongversion: {
+ FileName const fn = FileName::tempName("Buffer_readString");
+
+ int file_format;
+ ReadStatus const ret_plf = parseLyXFormat(lex, fn, file_format);
+ if (ret_plf != ReadSuccess)
+ return ret_plf;
+
+ if (file_format != LYX_FORMAT) {
// We need to call lyx2lyx, so write the input to a file
- ofstream os(name.toFilesystemEncoding().c_str());
+ ofstream os(fn.toFilesystemEncoding().c_str());
os << s;
os.close();
- return readFile(name);
- }
- case success:
- break;
+ // lyxvc in readFile
+ return readFile(fn) == ReadSuccess;
}
+ if (readDocument(lex))
+ return false;
return true;
}
-bool Buffer::readFile(FileName const & filename)
+Buffer::ReadStatus Buffer::readFile(FileName const & fn)
{
- FileName fname(filename);
-
- params().compressed = fname.isZippedFile();
-
- // remove dummy empty par
- paragraphs().clear();
+ FileName fname(fn);
Lexer lex;
lex.setFile(fname);
- if (readFile(lex, fname) != success)
- return false;
- return true;
+ int file_format;
+ ReadStatus const ret_plf = parseLyXFormat(lex, fn, file_format);
+ if (ret_plf != ReadSuccess)
+ return ret_plf;
+
+ if (file_format != LYX_FORMAT) {
+ FileName tmpFile;
+ ReadStatus const ret_clf = convertLyXFormat(fn, tmpFile, file_format);
+ if (ret_clf != ReadSuccess)
+ return ret_clf;
+ return readFile(tmpFile);
+ }
+
+ // FIXME: InsetInfo needs to know whether the file is under VCS
+ // during the parse process, so this has to be done before.
+ lyxvc().file_found_hook(d->filename);
+
+ if (readDocument(lex)) {
+ Alert::error(_("Document format failure"),
+ bformat(_("%1$s ended unexpectedly, which means"
+ " that it is probably corrupted."),
+ from_utf8(fn.absFileName())));
+ return ReadDocumentFailure;
+ }
+
+ d->file_fully_loaded = true;
+ d->read_only = !d->filename.isWritable();
+ params().compressed = d->filename.isZippedFile();
+ saveCheckSum();
+ return ReadSuccess;
}
}
-Buffer::ReadStatus Buffer::readFile(Lexer & lex, FileName const & filename,
- bool fromstring)
+void Buffer::updatePreviews() const
+{
+ if (graphics::Previews::status() != LyXRC::PREVIEW_OFF)
+ thePreviews().generateBufferPreviews(*this);
+}
+
+
+void Buffer::removePreviews() const
{
- LASSERT(!filename.empty(), /**/);
+ thePreviews().removeLoader(*this);
+}
+
- // the first (non-comment) token _must_ be...
- if (!lex.checkFor("\\lyxformat")) {
+Buffer::ReadStatus Buffer::parseLyXFormat(Lexer & lex,
+ FileName const & fn, int & file_format) const
+{
+ if(!lex.checkFor("\\lyxformat")) {
Alert::error(_("Document format failure"),
- bformat(_("%1$s is not a readable LyX document."),
- from_utf8(filename.absFileName())));
- return failure;
- }
+ bformat(_("%1$s is not a readable LyX document."),
+ from_utf8(fn.absFileName())));
+ return ReadNoLyXFormat;
+ }
string tmp_format;
lex >> tmp_format;
- //lyxerr << "LyX Format: `" << tmp_format << '\'' << endl;
- // if present remove ".," from string.
+
+ // LyX formats 217 and earlier were written as 2.17. This corresponds
+ // to files from LyX versions < 1.1.6.3. We just remove the dot in
+ // these cases. See also: www.lyx.org/trac/changeset/1313.
size_t dot = tmp_format.find_first_of(".,");
- //lyxerr << " dot found at " << dot << endl;
if (dot != string::npos)
- tmp_format.erase(dot, 1);
- int const file_format = convert<int>(tmp_format);
- //lyxerr << "format: " << file_format << endl;
-
- // save timestamp and checksum of the original disk file, making sure
- // to not overwrite them with those of the file created in the tempdir
- // when it has to be converted to the current format.
- if (!d->checksum_) {
- // Save the timestamp and checksum of disk file. If filename is an
- // emergency file, save the timestamp and checksum of the original lyx file
- // because isExternallyModified will check for this file. (BUG4193)
- string diskfile = filename.absFileName();
- if (suffixIs(diskfile, ".emergency"))
- diskfile = diskfile.substr(0, diskfile.size() - 10);
- saveCheckSum(FileName(diskfile));
- }
+ tmp_format.erase(dot, 1);
- if (file_format != LYX_FORMAT) {
+ file_format = convert<int>(tmp_format);
+ return ReadSuccess;
+}
- if (fromstring)
- // lyx2lyx would fail
- return wrongversion;
-
- FileName const tmpfile = FileName::tempName("Buffer_readFile");
- if (tmpfile.empty()) {
- Alert::error(_("Conversion failed"),
- bformat(_("%1$s is from a different"
- " version of LyX, but a temporary"
- " file for converting it could"
- " not be created."),
- from_utf8(filename.absFileName())));
- return failure;
- }
- FileName const lyx2lyx = libFileSearch("lyx2lyx", "lyx2lyx");
- if (lyx2lyx.empty()) {
- Alert::error(_("Conversion script not found"),
- bformat(_("%1$s is from a different"
- " version of LyX, but the"
- " conversion script lyx2lyx"
- " could not be found."),
- from_utf8(filename.absFileName())));
- return failure;
- }
- ostringstream command;
- command << os::python()
- << ' ' << quoteName(lyx2lyx.toFilesystemEncoding())
- << " -t " << convert<string>(LYX_FORMAT)
- << " -o " << quoteName(tmpfile.toFilesystemEncoding())
- << ' ' << quoteName(filename.toSafeFilesystemEncoding());
- string const command_str = command.str();
-
- LYXERR(Debug::INFO, "Running '" << command_str << '\'');
-
- cmd_ret const ret = runCommand(command_str);
- if (ret.first != 0) {
- if (file_format < LYX_FORMAT)
- Alert::error(_("Conversion script failed"),
- bformat(_("%1$s is from an older version"
- " of LyX, but the lyx2lyx script"
- " failed to convert it."),
- from_utf8(filename.absFileName())));
- else
- Alert::error(_("Conversion script failed"),
- bformat(_("%1$s is from a newer version"
- " of LyX and cannot be converted by the"
- " lyx2lyx script."),
- from_utf8(filename.absFileName())));
- return failure;
- } else {
- bool const ret = readFile(tmpfile);
- // Do stuff with tmpfile name and buffer name here.
- return ret ? success : failure;
- }
+Buffer::ReadStatus Buffer::convertLyXFormat(FileName const & fn,
+ FileName & tmpfile, int from_format)
+{
+ tmpfile = FileName::tempName("Buffer_convertLyXFormat");
+ if(tmpfile.empty()) {
+ Alert::error(_("Conversion failed"),
+ bformat(_("%1$s is from a different"
+ " version of LyX, but a temporary"
+ " file for converting it could"
+ " not be created."),
+ from_utf8(fn.absFileName())));
+ return LyX2LyXNoTempFile;
}
- if (readDocument(lex)) {
- Alert::error(_("Document format failure"),
- bformat(_("%1$s ended unexpectedly, which means"
- " that it is probably corrupted."),
- from_utf8(filename.absFileName())));
- return failure;
+ FileName const lyx2lyx = libFileSearch("lyx2lyx", "lyx2lyx");
+ if (lyx2lyx.empty()) {
+ Alert::error(_("Conversion script not found"),
+ bformat(_("%1$s is from a different"
+ " version of LyX, but the"
+ " conversion script lyx2lyx"
+ " could not be found."),
+ from_utf8(fn.absFileName())));
+ return LyX2LyXNotFound;
}
- d->file_fully_loaded = true;
- return success;
+ // Run lyx2lyx:
+ // $python$ "$lyx2lyx$" -t $LYX_FORMAT$ -o "$tempfile$" "$filetoread$"
+ ostringstream command;
+ command << os::python()
+ << ' ' << quoteName(lyx2lyx.toFilesystemEncoding())
+ << " -t " << convert<string>(LYX_FORMAT)
+ << " -o " << quoteName(tmpfile.toFilesystemEncoding())
+ << ' ' << quoteName(fn.toSafeFilesystemEncoding());
+ string const command_str = command.str();
+
+ LYXERR(Debug::INFO, "Running '" << command_str << '\'');
+
+ cmd_ret const ret = runCommand(command_str);
+ if (ret.first != 0) {
+ if (from_format < LYX_FORMAT) {
+ Alert::error(_("Conversion script failed"),
+ bformat(_("%1$s is from an older version"
+ " of LyX and the lyx2lyx script"
+ " failed to convert it."),
+ from_utf8(fn.absFileName())));
+ return LyX2LyXOlderFormat;
+ } else {
+ Alert::error(_("Conversion script failed"),
+ bformat(_("%1$s is from a newer version"
+ " of LyX and the lyx2lyx script"
+ " failed to convert it."),
+ from_utf8(fn.absFileName())));
+ return LyX2LyXNewerFormat;
+ }
+ }
+ return ReadSuccess;
}
// Should probably be moved to somewhere else: BufferView? GuiView?
bool Buffer::save() const
{
+ docstring const file = makeDisplayPath(absFileName(), 20);
+ d->filename.refresh();
+
+ // check the read-only status before moving the file as a backup
+ if (d->filename.exists()) {
+ bool const read_only = !d->filename.isWritable();
+ if (read_only) {
+ Alert::warning(_("File is read-only"),
+ bformat(_("The file %1$s cannot be written because it "
+ "is marked as read-only."), file));
+ return false;
+ }
+ }
+
// ask if the disk file has been externally modified (use checksum method)
if (fileName().exists() && isExternallyModified(checksum_method)) {
- docstring const file = makeDisplayPath(absFileName(), 20);
- docstring text = bformat(_("Document %1$s has been externally modified. Are you sure "
- "you want to overwrite this file?"), file);
+ docstring text =
+ bformat(_("Document %1$s has been externally modified. "
+ "Are you sure you want to overwrite this file?"), file);
int const ret = Alert::prompt(_("Overwrite modified file?"),
text, 1, 1, _("&Overwrite"), _("&Cancel"));
if (ret == 1)
backupName = FileName(addName(lyxrc.backupdir_path,
mangledName));
}
- // do not copy because of #6587
- if (fileName().moveTo(backupName)) {
- madeBackup = true;
- } else {
+
+ // Except file is symlink do not copy because of #6587.
+ // Hard links have bad luck.
+ if (fileName().isSymLink())
+ madeBackup = fileName().copyTo(backupName);
+ else
+ madeBackup = fileName().moveTo(backupName);
+
+ if (!madeBackup) {
Alert::error(_("Backup failure"),
bformat(_("Cannot create backup file %1$s.\n"
- "Please check whether the directory exists and is writeable."),
+ "Please check whether the directory exists and is writable."),
from_utf8(backupName.absFileName())));
//LYXERR(Debug::DEBUG, "Fs error: " << fe.what());
}
// see bug 6587
// removeAutosaveFile();
- saveCheckSum(d->filename);
+ saveCheckSum();
message(str + _(" done."));
return true;
LYXERR0(" " << s);
if (writeFile(FileName(s))) {
markClean();
- user_message += bformat(_(" Saved to %1$s. Phew.\n"), from_utf8(s));
+ user_message += " " + bformat(_("Saved to %1$s. Phew.\n"), from_utf8(s));
return user_message;
} else {
- user_message += _(" Save failed! Trying again...\n");
+ user_message += " " + _("Save failed! Trying again...\n");
}
}
lyxerr << ' ' << s << endl;
if (writeFile(FileName(s))) {
markClean();
- user_message += bformat(_(" Saved to %1$s. Phew.\n"), from_utf8(s));
+ user_message += " " + bformat(_("Saved to %1$s. Phew.\n"), from_utf8(s));
return user_message;
}
- user_message += _(" Save failed! Trying yet again...\n");
+ user_message += " " + _("Save failed! Trying yet again...\n");
// 3) In "/tmp" directory.
// MakeAbsPath to prepend the current
lyxerr << ' ' << s << endl;
if (writeFile(FileName(s))) {
markClean();
- user_message += bformat(_(" Saved to %1$s. Phew.\n"), from_utf8(s));
+ user_message += " " + bformat(_("Saved to %1$s. Phew.\n"), from_utf8(s));
return user_message;
}
- user_message += _(" Save failed! Bummer. Document is lost.");
+ user_message += " " + _("Save failed! Bummer. Document is lost.");
// Don't try again.
markClean();
return user_message;
// The top of the file should not be written by params().
// write out a comment in the top of the file
- ofs << "#LyX " << lyx_version
+ ofs << "#LyX " << lyx_version_major << "." << lyx_version_minor
<< " created this file. For more info see http://www.lyx.org/\n"
<< "\\lyxformat " << LYX_FORMAT << "\n"
<< "\\begin_document\n";
bool output_preamble, bool output_body) const
{
OutputParams runparams = runparams_in;
- if (params().useXetex)
- runparams.flavor = OutputParams::XETEX;
+
+ // This is necessary for LuaTeX/XeTeX with tex fonts.
+ // See FIXME in BufferParams::encoding()
+ if (runparams.isFullUnicode())
+ runparams.encoding = encodings.fromLyXName("utf8-plain");
string const encoding = runparams.encoding->iconvName();
LYXERR(Debug::LATEX, "makeLaTeXFile encoding: " << encoding << "...");
ErrorList & errorList = d->errorLists["Export"];
errorList.clear();
bool failed_export = false;
+ otexstream os(ofs, d->texrow);
try {
- d->texrow.reset();
- writeLaTeXSource(ofs, original_path,
+ os.texrow().reset();
+ writeLaTeXSource(os, original_path,
runparams, output_preamble, output_body);
}
catch (EncodingException & e) {
}
-void Buffer::writeLaTeXSource(odocstream & os,
+void Buffer::writeLaTeXSource(otexstream & os,
string const & original_path,
OutputParams const & runparams_in,
bool const output_preamble, bool const output_body) const
"For more info, see http://www.lyx.org/.\n"
"%% Do not edit unless you really know what "
"you are doing.\n";
- d->texrow.newline();
- d->texrow.newline();
}
LYXERR(Debug::INFO, "lyx document header finished");
if (output_preamble) {
if (!runparams.nice) {
// code for usual, NOT nice-latex-file
- os << "\\batchmode\n"; // changed
- // from \nonstopmode
- d->texrow.newline();
+ os << "\\batchmode\n"; // changed from \nonstopmode
}
if (!original_path.empty()) {
// FIXME UNICODE
<< "\\def\\input@path{{"
<< inputpath << "/}}\n"
<< "\\makeatother\n";
- d->texrow.newline();
- d->texrow.newline();
- d->texrow.newline();
}
}
MacroSet parentMacros;
listParentMacros(parentMacros, features);
+ runparams.use_polyglossia = features.usePolyglossia();
// Write the preamble
runparams.use_babel = params().writeLaTeX(os, features,
- d->texrow,
d->filename.onlyPath());
runparams.use_japanese = features.isRequired("japanese");
// make the body.
os << "\\begin{document}\n";
- d->texrow.newline();
// output the parent macros
MacroSet::iterator it = parentMacros.begin();
MacroSet::iterator end = parentMacros.end();
for (; it != end; ++it) {
- int num_lines = (*it)->write(os, true);
- d->texrow.newlines(num_lines);
+ int num_lines = (*it)->write(os.os(), true);
+ os.texrow().newlines(num_lines);
}
} // output_preamble
- d->texrow.start(paragraphs().begin()->id(), 0);
+ os.texrow().start(paragraphs().begin()->id(), 0);
LYXERR(Debug::INFO, "preamble finished, now the body.");
}
// the real stuff
- latexParagraphs(*this, text(), os, d->texrow, runparams);
+ latexParagraphs(*this, text(), os, runparams);
// Restore the parenthood if needed
if (output_preamble)
// add this just in case after all the paragraphs
os << endl;
- d->texrow.newline();
if (output_preamble) {
os << "\\end{document}\n";
- d->texrow.newline();
LYXERR(Debug::LATEX, "makeLaTeXFile...done");
} else {
LYXERR(Debug::LATEX, "LaTeXFile for inclusion made.");
runparams_in.encoding = runparams.encoding;
// Just to be sure. (Asger)
- d->texrow.newline();
+ os.texrow().newline();
//for (int i = 0; i<d->texrow.rows(); i++) {
// int id,pos;
//}
LYXERR(Debug::INFO, "Finished making LaTeX file.");
- LYXERR(Debug::INFO, "Row count was " << d->texrow.rows() - 1 << '.');
+ LYXERR(Debug::INFO, "Row count was " << os.texrow().rows() - 1 << '.');
}
os << "\n<!-- Text Class Preamble -->\n"
<< features.getTClassHTMLPreamble()
- << "\n<!-- Premable Snippets -->\n"
+ << "\n<!-- Preamble Snippets -->\n"
<< from_utf8(features.getPreambleSnippets());
os << "\n<!-- Layout-provided Styles -->\n";
d->bibfiles_cache_.clear();
for (InsetIterator it = inset_iterator_begin(inset()); it; ++it) {
if (it->lyxCode() == BIBTEX_CODE) {
- InsetBibtex const & inset =
- static_cast<InsetBibtex const &>(*it);
+ InsetBibtex const & inset = static_cast<InsetBibtex const &>(*it);
support::FileNameList const bibfiles = inset.getBibFiles();
d->bibfiles_cache_.insert(d->bibfiles_cache_.end(),
bibfiles.begin(),
bibfiles.end());
} else if (it->lyxCode() == INCLUDE_CODE) {
- InsetInclude & inset =
- static_cast<InsetInclude &>(*it);
+ InsetInclude & inset = static_cast<InsetInclude &>(*it);
Buffer const * const incbuf = inset.getChildBuffer();
if (!incbuf)
continue;
}
-void Buffer::checkBibInfoCache() const
+void Buffer::checkIfBibInfoCacheIsValid() const
{
// use the master's cache
Buffer const * const tmp = masterBuffer();
if (tmp != this) {
- tmp->checkBibInfoCache();
+ tmp->checkIfBibInfoCacheIsValid();
return;
}
- // this will also reload the cache if it is invalid
- support::FileNameList const & bibfiles_cache = getBibfilesCache();
-
// compare the cached timestamps with the actual ones.
- support::FileNameList::const_iterator ei = bibfiles_cache.begin();
- support::FileNameList::const_iterator en = bibfiles_cache.end();
+ FileNameList const & bibfiles_cache = getBibfilesCache();
+ FileNameList::const_iterator ei = bibfiles_cache.begin();
+ FileNameList::const_iterator en = bibfiles_cache.end();
for (; ei != en; ++ ei) {
time_t lastw = ei->lastModified();
time_t prevw = d->bibfile_status_[*ei];
d->bibfile_status_[*ei] = lastw;
}
}
-
- // if not valid, then reload the info
- if (!d->bibinfo_cache_valid_) {
- d->bibinfo_.clear();
- fillWithBibKeys(d->bibinfo_);
- d->bibinfo_cache_valid_ = true;
+}
+
+
+void Buffer::reloadBibInfoCache() const
+{
+ // use the master's cache
+ Buffer const * const tmp = masterBuffer();
+ if (tmp != this) {
+ tmp->reloadBibInfoCache();
+ return;
}
+
+ checkIfBibInfoCacheIsValid();
+ if (d->bibinfo_cache_valid_)
+ return;
+
+ d->bibinfo_.clear();
+ collectBibKeys();
+ d->bibinfo_cache_valid_ = true;
}
-void Buffer::fillWithBibKeys(BiblioInfo & keys) const
+void Buffer::collectBibKeys() const
{
for (InsetIterator it = inset_iterator_begin(inset()); it; ++it)
- it->fillWithBibKeys(keys, it);
+ it->collectBibKeys(it);
+}
+
+
+void Buffer::addBiblioInfo(BiblioInfo const & bi) const
+{
+ Buffer const * tmp = masterBuffer();
+ BiblioInfo & masterbi = (tmp == this) ?
+ d->bibinfo_ : tmp->d->bibinfo_;
+ masterbi.mergeBiblioInfo(bi);
+}
+
+
+void Buffer::addBibTeXInfo(docstring const & key, BibTeXInfo const & bi) const
+{
+ Buffer const * tmp = masterBuffer();
+ BiblioInfo & masterbi = (tmp == this) ?
+ d->bibinfo_ : tmp->d->bibinfo_;
+ masterbi[key] = bi;
}
case LFUN_BUFFER_AUTO_SAVE:
autoSave();
+ resetAutosaveTimers();
break;
case LFUN_BRANCH_ADD: {
}
-void Buffer::saveCheckSum(FileName const & file) const
+void Buffer::saveCheckSum() const
{
+ FileName const & file = d->filename;
+
+ file.refresh();
if (file.exists()) {
d->timestamp_ = file.lastModified();
d->checksum_ = file.checksum();
ListOfBuffers Buffer::allRelatives() const
{
ListOfBuffers lb = masterBuffer()->getDescendents();
- lb.push_front(const_cast<Buffer *>(this));
+ lb.push_front(const_cast<Buffer *>(masterBuffer()));
return lb;
}
break;
// scope ends behind pos?
- if (pos < it->second.first) {
+ if (pos < it->second.first
+ && (cloned_buffer_ ||
+ theBufferList().isLoaded(it->second.second))) {
// look for macro in external file
macro_lock = true;
MacroData const * data
continue;
}
- if (doing_export && iit->inset->asInsetMath()) {
- InsetMath * im = static_cast<InsetMath *>(iit->inset);
- if (im->asHullInset()) {
- InsetMathHull * hull = static_cast<InsetMathHull *>(im);
+ InsetMath * im = iit->inset->asInsetMath();
+ if (doing_export && im) {
+ InsetMathHull * hull = im->asHullInset();
+ if (hull)
hull->recordLocation(it);
- }
}
if (iit->inset->lyxCode() != MATHMACRO_CODE)
// get macro data
MathMacroTemplate & macroTemplate =
- static_cast<MathMacroTemplate &>(*iit->inset);
+ *iit->inset->asInsetMath()->asMacroTemplate();
MacroContext mc(owner_, it);
macroTemplate.updateToContext(mc);
{
//FIXME: This does not work for child documents yet.
LASSERT(code == CITE_CODE, /**/);
+
+ reloadBibInfoCache();
+
// Check if the label 'from' appears more than once
- vector<docstring> labels;
- string paramName;
- checkBibInfoCache();
BiblioInfo const & keys = masterBibInfo();
BiblioInfo::const_iterator bit = keys.begin();
BiblioInfo::const_iterator bend = keys.end();
+ vector<docstring> labels;
for (; bit != bend; ++bit)
// FIXME UNICODE
labels.push_back(bit->first);
- paramName = "key";
if (count(labels.begin(), labels.end(), from) > 1)
return;
+ string const paramName = "key";
for (InsetIterator it = inset_iterator_begin(inset()); it; ++it) {
if (it->lyxCode() == code) {
- InsetCommand & inset = static_cast<InsetCommand &>(*it);
- docstring const oldValue = inset.getParam(paramName);
+ InsetCommand * inset = it->asInsetCommand();
+ if (!inset)
+ continue;
+ docstring const oldValue = inset->getParam(paramName);
if (oldValue == from)
- inset.setParam(paramName, to);
+ inset->setParam(paramName, to);
}
}
}
-void Buffer::getSourceCode(odocstream & os, pit_type par_begin,
- pit_type par_end, bool full_source) const
+void Buffer::getSourceCode(odocstream & os, string const format,
+ pit_type par_begin, pit_type par_end,
+ bool full_source) const
{
OutputParams runparams(¶ms().encoding());
runparams.nice = true;
- runparams.flavor = params().useXetex ?
- OutputParams::XETEX : OutputParams::LATEX;
+ runparams.flavor = getOutputFlavor(format);
runparams.linelen = lyxrc.plaintext_linelen;
// No side effect of file copying and image conversion
runparams.dryrun = true;
d->texrow.newline();
if (isDocBook())
writeDocBookSource(os, absFileName(), runparams, false);
- else
+ else if (runparams.flavor == OutputParams::HTML)
+ writeLyXHTMLSource(os, runparams, false);
+ else {
// latex or literate
- writeLaTeXSource(os, string(), runparams, true, true);
+ otexstream ots(os, d->texrow);
+ writeLaTeXSource(ots, string(), runparams, true, true);
+ }
} else {
runparams.par_begin = par_begin;
runparams.par_end = par_end;
// output paragraphs
if (isDocBook())
docbookParagraphs(text(), *this, os, runparams);
- else
+ else if (runparams.flavor == OutputParams::HTML) {
+ XHTMLStream xs(os);
+ xhtmlParagraphs(text(), *this, xs, runparams);
+ } else {
// latex or literate
- latexParagraphs(*this, text(), os, texrow, runparams);
+ otexstream ots(os, texrow);
+ latexParagraphs(*this, text(), ots, runparams);
+ }
}
}
} // namespace anon
+FileName Buffer::getEmergencyFileName() const
+{
+ return FileName(d->filename.absFileName() + ".emergency");
+}
+
+
FileName Buffer::getAutosaveFileName() const
{
// if the document is unnamed try to save in the backup dir, else
fpath = filePath();
string const fname = "#" + d->filename.onlyFileName() + "#";
+
return makeAbsPath(fname, fpath);
}
}
-// Perfect target for a thread...
-void Buffer::autoSave() const
+bool Buffer::autoSave() const
{
- if (d->bak_clean || isReadonly()) {
- // We don't save now, but we'll try again later
- resetAutosaveTimers();
- return;
- }
+ Buffer const * buf = d->cloned_buffer_ ? d->cloned_buffer_ : this;
+ if (buf->d->bak_clean || isReadonly())
+ return true;
- // emit message signal.
message(_("Autosaving current document..."));
- AutoSaveBuffer autosave(*this, getAutosaveFileName());
- autosave.start();
-
- d->bak_clean = true;
-
- resetAutosaveTimers();
+ buf->d->bak_clean = true;
+
+ FileName const fname = getAutosaveFileName();
+ if (d->cloned_buffer_) {
+ // If this buffer is cloned, we assume that
+ // we are running in a separate thread already.
+ FileName const tmp_ret = FileName::tempName("lyxauto");
+ if (!tmp_ret.empty()) {
+ writeFile(tmp_ret);
+ // assume successful write of tmp_ret
+ if (tmp_ret.moveTo(fname))
+ return true;
+ }
+ // failed to write/rename tmp_ret so try writing direct
+ return writeFile(fname);
+ } else {
+ /// This function is deprecated as the frontend needs to take care
+ /// of cloning the buffer and autosaving it in another thread. It
+ /// is still here to allow (QT_VERSION < 0x040400).
+ AutoSaveBuffer autosave(*this, fname);
+ autosave.start();
+ return true;
+ }
}
{
string format = params().documentClass().outputFormat();
if (format == "latex") {
- if (params().useXetex)
+ if (params().useNonTeXFonts)
return "xetex";
if (params().encoding().package() == Encoding::japanese)
return "platex";
string Buffer::getDefaultOutputFormat() const
{
- if (!params().defaultOutputFormat.empty()
- && params().defaultOutputFormat != "default")
- return params().defaultOutputFormat;
- typedef vector<Format const *> Formats;
- Formats formats = exportableFormats(true);
+ if (!params().default_output_format.empty()
+ && params().default_output_format != "default")
+ return params().default_output_format;
if (isDocBook()
- || isLiterate()
- || params().useXetex
+ || params().useNonTeXFonts
|| params().encoding().package() == Encoding::japanese) {
+ vector<Format const *> const formats = exportableFormats(true);
if (formats.empty())
return string();
// return the first we find
}
+OutputParams::FLAVOR Buffer::getOutputFlavor(string const format) const
+{
+ string const dformat = (format.empty() || format == "default") ?
+ getDefaultOutputFormat() : format;
+ DefaultFlavorCache::const_iterator it =
+ default_flavors_.find(dformat);
+
+ if (it != default_flavors_.end())
+ return it->second;
+
+ OutputParams::FLAVOR result = OutputParams::LATEX;
+
+ if (dformat == "xhtml")
+ result = OutputParams::HTML;
+ else {
+ // Try to determine flavor of default output format
+ vector<string> backs = backends();
+ if (find(backs.begin(), backs.end(), dformat) == backs.end()) {
+ // Get shortest path to format
+ Graph::EdgePath path;
+ for (vector<string>::const_iterator it = backs.begin();
+ it != backs.end(); ++it) {
+ Graph::EdgePath p = theConverters().getPath(*it, dformat);
+ if (!p.empty() && (path.empty() || p.size() < path.size())) {
+ path = p;
+ }
+ }
+ if (!path.empty())
+ result = theConverters().getFlavor(path);
+ }
+ }
+ // cache this flavor
+ default_flavors_[dformat] = result;
+ return result;
+}
+
+
namespace {
// helper class, to guarantee this gets reset properly
class MarkAsExporting {
runparams.linelen = lyxrc.plaintext_linelen;
runparams.includeall = includeall;
vector<string> backs = backends();
+ Converters converters = theConverters();
if (find(backs.begin(), backs.end(), format) == backs.end()) {
// Get shortest path to format
+ converters.buildGraph();
Graph::EdgePath path;
for (vector<string>::const_iterator it = backs.begin();
it != backs.end(); ++it) {
- Graph::EdgePath p = theConverters().getPath(*it, format);
+ Graph::EdgePath p = converters.getPath(*it, format);
if (!p.empty() && (path.empty() || p.size() < path.size())) {
backend_format = *it;
path = p;
}
return false;
}
- runparams.flavor = theConverters().getFlavor(path);
+ runparams.flavor = converters.getFlavor(path);
} else {
backend_format = format;
// FIXME: Don't hardcode format names here, but use a flag
if (backend_format == "pdflatex")
runparams.flavor = OutputParams::PDFLATEX;
+ else if (backend_format == "luatex")
+ runparams.flavor = OutputParams::LUATEX;
+ else if (backend_format == "xetex")
+ runparams.flavor = OutputParams::XETEX;
}
string filename = latexName(false);
break;
case BufferParams::LaTeX:
runparams.math_flavor = OutputParams::MathAsLaTeX;
- break;
+ break;
}
-
makeLyXHTMLFile(FileName(filename), runparams);
- } else if (backend_format == "lyx")
+ } else if (backend_format == "lyx")
writeFile(FileName(filename));
// Docbook backend
else if (isDocBook()) {
// LaTeX backend
else if (backend_format == format) {
runparams.nice = true;
- if (!makeLaTeXFile(FileName(filename), string(), runparams))
+ if (!makeLaTeXFile(FileName(filename), string(), runparams)) {
+ if (d->cloned_buffer_) {
+ d->cloned_buffer_->d->errorLists["Export"] =
+ d->errorLists["Export"];
+ }
return false;
+ }
} else if (!lyxrc.tex_allows_spaces
&& contains(filePath(), ' ')) {
Alert::error(_("File name error"),
return false;
} else {
runparams.nice = false;
- if (!makeLaTeXFile(FileName(filename), filePath(), runparams))
+ if (!makeLaTeXFile(FileName(filename), filePath(), runparams)) {
+ if (d->cloned_buffer_) {
+ d->cloned_buffer_->d->errorLists["Export"] =
+ d->errorLists["Export"];
+ }
return false;
+ }
}
string const error_type = (format == "program")
ErrorList & error_list = d->errorLists[error_type];
string const ext = formats.extension(format);
FileName const tmp_result_file(changeExtension(filename, ext));
- bool const success = theConverters().convert(this, FileName(filename),
+ bool const success = converters.convert(this, FileName(filename),
tmp_result_file, FileName(absFileName()), backend_format, format,
error_list);
// Emit the signal to show the error list or copy it back to the
- // cloned Buffer so that it cab be emitted afterwards.
+ // cloned Buffer so that it can be emitted afterwards.
if (format != backend_format) {
if (d->cloned_buffer_) {
d->cloned_buffer_->d->errorLists[error_type] =
ListOfBuffers::const_iterator const cen = clist.end();
for (; cit != cen; ++cit) {
if (d->cloned_buffer_) {
+ // Enable reverse search by copying back the
+ // texrow object to the cloned buffer.
+ // FIXME: this is not thread safe.
+ (*cit)->d->cloned_buffer_->d->texrow = (*cit)->d->texrow;
(*cit)->d->cloned_buffer_->d->errorLists[error_type] =
(*cit)->d->errorLists[error_type];
} else
vector<string> v;
v.push_back(bufferFormat());
// FIXME: Don't hardcode format names here, but use a flag
- if (v.back() == "latex")
+ if (v.back() == "latex") {
v.push_back("pdflatex");
+ v.push_back("luatex");
+ v.push_back("xetex");
+ } else if (v.back() == "xetex")
+ v.push_back("luatex");
v.push_back("xhtml");
v.push_back("text");
v.push_back("lyx");
}
-bool Buffer::readFileHelper(FileName const & s)
+Buffer::ReadStatus Buffer::extractFromVC()
{
- // File information about normal file
- if (!s.exists()) {
- docstring const file = makeDisplayPath(s.absFileName(), 50);
- docstring text = bformat(_("The specified document\n%1$s"
- "\ncould not be read."), file);
- Alert::error(_("Could not read document"), text);
- return false;
- }
+ bool const found = LyXVC::file_not_found_hook(d->filename);
+ if (!found)
+ return ReadFileNotFound;
+ if (!d->filename.isReadableFile())
+ return ReadVCError;
+ return ReadSuccess;
+}
- // Check if emergency save file exists and is newer.
- FileName const e(s.absFileName() + ".emergency");
-
- if (e.exists() && s.exists() && e.lastModified() > s.lastModified()) {
- docstring const file = makeDisplayPath(s.absFileName(), 20);
- docstring const text =
- bformat(_("An emergency save of the document "
- "%1$s exists.\n\n"
- "Recover emergency save?"), file);
- switch (Alert::prompt(_("Load emergency save?"), text, 0, 2,
- _("&Recover"), _("&Load Original"),
- _("&Cancel")))
- {
- case 0: {
- // the file is not saved if we load the emergency file.
+
+Buffer::ReadStatus Buffer::loadEmergency()
+{
+ FileName const emergencyFile = getEmergencyFileName();
+ if (!emergencyFile.exists()
+ || emergencyFile.lastModified() <= d->filename.lastModified())
+ return ReadFileNotFound;
+
+ docstring const file = makeDisplayPath(d->filename.absFileName(), 20);
+ docstring const text = bformat(_("An emergency save of the document "
+ "%1$s exists.\n\nRecover emergency save?"), file);
+
+ int const load_emerg = Alert::prompt(_("Load emergency save?"), text,
+ 0, 2, _("&Recover"), _("&Load Original"), _("&Cancel"));
+
+ switch (load_emerg)
+ {
+ case 0: {
+ docstring str;
+ ReadStatus const ret_llf = loadThisLyXFile(emergencyFile);
+ bool const success = (ret_llf == ReadSuccess);
+ if (success) {
+ if (isReadonly()) {
+ Alert::warning(_("File is read-only"),
+ bformat(_("An emergency file is successfully loaded, "
+ "but the original file %1$s is marked read-only. "
+ "Please make sure to save the document as a different "
+ "file."), from_utf8(d->filename.absFileName())));
+ }
markDirty();
- docstring str;
- bool res;
-
- if ((res = readFile(e)) == success)
- str = _("Document was successfully recovered.");
- else
- str = _("Document was NOT successfully recovered.");
- str += "\n\n" + bformat(_("Remove emergency file now?\n(%1$s)"),
- makeDisplayPath(e.absFileName()));
-
- if (!Alert::prompt(_("Delete emergency file?"), str, 1, 1,
- _("&Remove"), _("&Keep it"))) {
- e.removeFile();
- if (res == success)
- Alert::warning(_("Emergency file deleted"),
- _("Do not forget to save your file now!"), true);
- }
- return res;
- }
- case 1:
- if (!Alert::prompt(_("Delete emergency file?"),
- _("Remove emergency file now?"), 1, 1,
- _("&Remove"), _("&Keep it")))
- e.removeFile();
- break;
- default:
- return false;
- }
+ str = _("Document was successfully recovered.");
+ } else
+ str = _("Document was NOT successfully recovered.");
+ str += "\n\n" + bformat(_("Remove emergency file now?\n(%1$s)"),
+ makeDisplayPath(emergencyFile.absFileName()));
+
+ int const del_emerg =
+ Alert::prompt(_("Delete emergency file?"), str, 1, 1,
+ _("&Remove"), _("&Keep"));
+ if (del_emerg == 0) {
+ emergencyFile.removeFile();
+ if (success)
+ Alert::warning(_("Emergency file deleted"),
+ _("Do not forget to save your file now!"), true);
+ }
+ return success ? ReadSuccess : ReadEmergencyFailure;
+ }
+ case 1: {
+ int const del_emerg =
+ Alert::prompt(_("Delete emergency file?"),
+ _("Remove emergency file now?"), 1, 1,
+ _("&Remove"), _("&Keep"));
+ if (del_emerg == 0)
+ emergencyFile.removeFile();
+ return ReadOriginal;
}
+ default:
+ break;
+ }
+ return ReadCancel;
+}
+
+
+Buffer::ReadStatus Buffer::loadAutosave()
+{
// Now check if autosave file is newer.
- FileName const a(onlyPath(s.absFileName()) + '#' + onlyFileName(s.absFileName()) + '#');
-
- if (a.exists() && s.exists() && a.lastModified() > s.lastModified()) {
- docstring const file = makeDisplayPath(s.absFileName(), 20);
- docstring const text =
- bformat(_("The backup of the document "
- "%1$s is newer.\n\nLoad the "
- "backup instead?"), file);
- switch (Alert::prompt(_("Load backup?"), text, 0, 2,
- _("&Load backup"), _("Load &original"),
- _("&Cancel") ))
- {
- case 0:
- // the file is not saved if we load the autosave file.
+ FileName const autosaveFile = getAutosaveFileName();
+ if (!autosaveFile.exists()
+ || autosaveFile.lastModified() <= d->filename.lastModified())
+ return ReadFileNotFound;
+
+ docstring const file = makeDisplayPath(d->filename.absFileName(), 20);
+ docstring const text = bformat(_("The backup of the document %1$s "
+ "is newer.\n\nLoad the backup instead?"), file);
+ int const ret = Alert::prompt(_("Load backup?"), text, 0, 2,
+ _("&Load backup"), _("Load &original"), _("&Cancel"));
+
+ switch (ret)
+ {
+ case 0: {
+ ReadStatus const ret_llf = loadThisLyXFile(autosaveFile);
+ // the file is not saved if we load the autosave file.
+ if (ret_llf == ReadSuccess) {
+ if (isReadonly()) {
+ Alert::warning(_("File is read-only"),
+ bformat(_("A backup file is successfully loaded, "
+ "but the original file %1$s is marked read-only. "
+ "Please make sure to save the document as a "
+ "different file."),
+ from_utf8(d->filename.absFileName())));
+ }
markDirty();
- return readFile(a);
- case 1:
- // Here we delete the autosave
- a.removeFile();
- break;
- default:
- return false;
+ return ReadSuccess;
}
+ return ReadAutosaveFailure;
}
- return readFile(s);
+ case 1:
+ // Here we delete the autosave
+ autosaveFile.removeFile();
+ return ReadOriginal;
+ default:
+ break;
+ }
+ return ReadCancel;
}
-bool Buffer::loadLyXFile(FileName const & s)
+Buffer::ReadStatus Buffer::loadLyXFile()
{
- // If the file is not readable, we try to
- // retrieve the file from version control.
- if (!s.isReadableFile()
- && !LyXVC::file_not_found_hook(s))
- return false;
-
- if (s.isReadableFile()){
- // InsetInfo needs to know if file is under VCS
- lyxvc().file_found_hook(s);
- if (readFileHelper(s)) {
- d->read_only = !s.isWritable();
- return true;
- }
+ if (!d->filename.isReadableFile()) {
+ ReadStatus const ret_rvc = extractFromVC();
+ if (ret_rvc != ReadSuccess)
+ return ret_rvc;
}
- return false;
+
+ ReadStatus const ret_re = loadEmergency();
+ if (ret_re == ReadSuccess || ret_re == ReadCancel)
+ return ret_re;
+
+ ReadStatus const ret_ra = loadAutosave();
+ if (ret_ra == ReadSuccess || ret_ra == ReadCancel)
+ return ret_ra;
+
+ return loadThisLyXFile(d->filename);
+}
+
+
+Buffer::ReadStatus Buffer::loadThisLyXFile(FileName const & fn)
+{
+ return readFile(fn);
}
void Buffer::bufferErrors(TeXErrors const & terr, ErrorList & errorList) const
{
- TeXErrors::Errors::const_iterator cit = terr.begin();
+ TeXErrors::Errors::const_iterator it = terr.begin();
TeXErrors::Errors::const_iterator end = terr.end();
+ ListOfBuffers clist = getDescendents();
+ ListOfBuffers::const_iterator cen = clist.end();
- for (; cit != end; ++cit) {
+ for (; it != end; ++it) {
int id_start = -1;
int pos_start = -1;
- int errorRow = cit->error_in_line;
- bool found = d->texrow.getIdFromRow(errorRow, id_start,
- pos_start);
+ int errorRow = it->error_in_line;
+ Buffer const * buf = 0;
+ Impl const * p = d;
+ if (it->child_name.empty())
+ p->texrow.getIdFromRow(errorRow, id_start, pos_start);
+ else {
+ // The error occurred in a child
+ ListOfBuffers::const_iterator cit = clist.begin();
+ for (; cit != cen; ++cit) {
+ string const child_name =
+ DocFileName(changeExtension(
+ (*cit)->absFileName(), "tex")).
+ mangledFileName();
+ if (it->child_name != child_name)
+ continue;
+ (*cit)->d->texrow.getIdFromRow(errorRow,
+ id_start, pos_start);
+ if (id_start != -1) {
+ buf = d->cloned_buffer_
+ ? (*cit)->d->cloned_buffer_->d->owner_
+ : (*cit)->d->owner_;
+ p = (*cit)->d;
+ break;
+ }
+ }
+ }
int id_end = -1;
int pos_end = -1;
+ bool found;
do {
++errorRow;
- found = d->texrow.getIdFromRow(errorRow, id_end, pos_end);
+ found = p->texrow.getIdFromRow(errorRow, id_end, pos_end);
} while (found && id_start == id_end && pos_start == pos_end);
- errorList.push_back(ErrorItem(cit->error_desc,
- cit->error_text, id_start, pos_start, pos_end));
+ if (id_start != id_end) {
+ // Next registered position is outside the inset where
+ // the error occurred, so signal end-of-paragraph
+ pos_end = 0;
+ }
+
+ errorList.push_back(ErrorItem(it->error_desc,
+ it->error_text, id_start, pos_start, pos_end, buf));
}
}
// do this only if we are the top-level Buffer
if (master == this)
- checkBibInfoCache();
+ reloadBibInfoCache();
// keep the buffers to be children in this set. If the call from the
// master comes back we can see which of them were actually seen (i.e.
// TocBackend update will be done later.
return;
+ d->bibinfo_cache_valid_ = true;
cbuf.tocBackend().update();
if (scope == UpdateMaster)
cbuf.structureChanged();
switch(layout.labeltype) {
case LABEL_COUNTER:
if (layout.toclevel <= bp.secnumdepth
- && (layout.latextype != LATEX_ENVIRONMENT
- || it.text()->isFirstInSequence(it.pit()))) {
- counters.step(layout.counter, utype);
- par.params().labelString(
- par.expandLabel(layout, bp));
+ && (layout.latextype != LATEX_ENVIRONMENT
+ || it.text()->isFirstInSequence(it.pit()))) {
+ if (counters.hasCounter(layout.counter))
+ counters.step(layout.counter, utype);
+ par.params().labelString(par.expandLabel(layout, bp));
} else
par.params().labelString(docstring());
break;
{
LASSERT(parit.pit() == 0, /**/);
- // set the position of the text in the buffer to be able
- // to resolve macros in it. This has nothing to do with
- // labels, but by putting it here we avoid implementing
- // a whole bunch of traversal routines just for this call.
+ // Set the position of the text in the buffer to be able
+ // to resolve macros in it.
parit.text()->setMacrocontextPosition(parit);
depth_type maxdepth = 0;
}
-bool Buffer::reload()
+Buffer::ReadStatus Buffer::reload()
{
setBusy(true);
- // c.f. bug 6587
+ // c.f. bug http://www.lyx.org/trac/ticket/6587
removeAutosaveFile();
// e.g., read-only status could have changed due to version control
d->filename.refresh();
docstring const disp_fn = makeDisplayPath(d->filename.absFileName());
- bool const success = loadLyXFile(d->filename);
- if (success) {
+ ReadStatus const status = loadLyXFile();
+ if (status == ReadSuccess) {
updateBuffer();
changed(true);
updateTitles();
markClean();
message(bformat(_("Document %1$s reloaded."), disp_fn));
+ d->undo_.clear();
} else {
message(bformat(_("Could not reload document %1$s."), disp_fn));
}
setBusy(false);
- thePreviews().removeLoader(*this);
- if (graphics::Previews::status() != LyXRC::PREVIEW_OFF)
- thePreviews().generateBufferPreviews(*this);
+ removePreviews();
+ updatePreviews();
errors("Parse");
- return success;
+ return status;
+}
+
+
+bool Buffer::saveAs(FileName const & fn)
+{
+ FileName const old_name = fileName();
+ FileName const old_auto = getAutosaveFileName();
+ bool const old_unnamed = isUnnamed();
+
+ setFileName(fn);
+ markDirty();
+ setUnnamed(false);
+
+ if (save()) {
+ // bring the autosave file with us, just in case.
+ moveAutosaveFile(old_auto);
+ // validate version control data and
+ // correct buffer title
+ lyxvc().file_found_hook(fileName());
+ updateTitles();
+ // the file has now been saved to the new location.
+ // we need to check that the locations of child buffers
+ // are still valid.
+ checkChildBuffers();
+ return true;
+ } else {
+ // save failed
+ // reset the old filename and unnamed state
+ setFileName(old_name);
+ setUnnamed(old_unnamed);
+ return false;
+ }
}