#include "CutAndPaste.h"
#include "DispatchResult.h"
#include "DocIterator.h"
-#include "Encoding.h"
+#include "BufferEncodings.h"
#include "ErrorList.h"
#include "Exporter.h"
#include "Format.h"
#include "support/Package.h"
#include "support/PathChanger.h"
#include "support/Systemcall.h"
+#include "support/TempFile.h"
#include "support/textutils.h"
#include "support/types.h"
CloneList * clone_list_;
/// are we in the process of exporting this buffer?
mutable bool doing_export;
-
+
/// compute statistics
/// \p from initial position
/// \p to points to the end position
/// Creates the per buffer temporary directory
static FileName createBufferTmpDir()
{
+ // FIXME THREAD
static int count;
// We are in our own directory. Why bother to mangle name?
// In fact I wrote this code to circumvent a problematic behaviour
// if we're the master buffer, then we should get rid of the list
// of clones
if (!parent()) {
- // if this is not empty, we have leaked something. worse, one of the
- // children still has a reference to this list.
- LASSERT(d->clone_list_->empty(), /* */);
+ // If this is not empty, we have leaked something. Worse, one of the
+ // children still has a reference to this list. But we will try to
+ // continue, rather than shut down.
+ LATTEST(d->clone_list_->empty());
list<CloneList *>::iterator it =
find(cloned_buffers.begin(), cloned_buffers.end(), d->clone_list_);
- LASSERT(it != cloned_buffers.end(), /* */);
- cloned_buffers.erase(it);
+ if (it == cloned_buffers.end()) {
+ // We will leak in this case, but it is safe to continue.
+ LATTEST(false);
+ } else
+ cloned_buffers.erase(it);
delete d->clone_list_;
}
// FIXME Do we really need to do this right before we delete d?
// The clone needs its own DocumentClass, since running updateBuffer() will
// modify it, and we would otherwise be sharing it with the original Buffer.
- buffer_clone->params().makeDocumentClass();
+ buffer_clone->params().makeDocumentClass(true);
ErrorList el;
cap::switchBetweenClasses(
params().documentClassPtr(), buffer_clone->params().documentClassPtr(),
// The clone needs its own DocumentClass, since running updateBuffer() will
// modify it, and we would otherwise be sharing it with the original Buffer.
- buffer_clone->params().makeDocumentClass();
+ buffer_clone->params().makeDocumentClass(true);
ErrorList el;
cap::switchBetweenClasses(
params().documentClassPtr(), buffer_clone->params().documentClassPtr(),
frontend::WorkAreaManager & Buffer::workAreaManager() const
{
- LASSERT(d->wa_, /**/);
+ LBUFERR(d->wa_);
return *d->wa_;
}
}
+BufferParams const & Buffer::masterParams() const
+{
+ if (masterBuffer() == this)
+ return params();
+
+ BufferParams & mparams = const_cast<Buffer *>(masterBuffer())->params();
+ // Copy child authors to the params. We need those pointers.
+ AuthorList const & child_authors = params().authors();
+ AuthorList::Authors::const_iterator it = child_authors.begin();
+ for (; it != child_authors.end(); it++)
+ mparams.authors().record(*it);
+ return mparams;
+}
+
+
ParagraphList & Buffer::paragraphs()
{
return text().paragraphs();
params().html_latex_end.clear();
params().html_math_img_scale = 1.0;
params().output_sync_macro.erase();
- params().local_layout.clear();
+ params().setLocalLayout(string(), false);
+ params().setLocalLayout(string(), true);
for (int i = 0; i < 4; ++i) {
params().user_defined_bullet(i) = ITEMIZE_DEFAULTS[i];
}
}
- if (!params().master.empty()) {
+ if (!parent() && !params().master.empty()) {
FileName const master_file = makeAbsPath(params().master,
onlyPath(absFileName()));
if (isLyXFileName(master_file.absFileName())) {
}
+bool Buffer::importString(string const & format, docstring const & contents, ErrorList & errorList)
+{
+ Format const * fmt = formats.getFormat(format);
+ if (!fmt)
+ return false;
+ // It is important to use the correct extension here, since some
+ // converters create a wrong output file otherwise (e.g. html2latex)
+ TempFile const tempfile("Buffer_importStringXXXXXX." + fmt->extension());
+ FileName const name(tempfile.name());
+ ofdocstream os(name.toFilesystemEncoding().c_str());
+ // Do not convert os implicitly to bool, since that is forbidden in C++11.
+ bool const success = !(os << contents).fail();
+ os.close();
+
+ bool converted = false;
+ if (success) {
+ params().compressed = false;
+
+ // remove dummy empty par
+ paragraphs().clear();
+
+ converted = importFile(format, name, errorList);
+ }
+
+ if (name.exists())
+ name.removeFile();
+ return converted;
+}
+
+
+bool Buffer::importFile(string const & format, FileName const & name, ErrorList & errorList)
+{
+ if (!theConverters().isReachable(format, "lyx"))
+ return false;
+
+ TempFile const tempfile("Buffer_importFileXXXXXX.lyx");
+ FileName const lyx(tempfile.name());
+ if (theConverters().convert(0, name, lyx, name, format, "lyx", errorList))
+ return readFile(lyx) == ReadSuccess;
+
+ return false;
+}
+
+
bool Buffer::readString(string const & s)
{
params().compressed = false;
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;
+ bool success = parseLyXFormat(lex, fn, file_format) == ReadSuccess;
- if (file_format != LYX_FORMAT) {
+ if (success && file_format != LYX_FORMAT) {
// We need to call lyx2lyx, so write the input to a file
ofstream os(fn.toFilesystemEncoding().c_str());
os << s;
os.close();
// lyxvc in readFile
- return readFile(fn) == ReadSuccess;
+ if (readFile(fn) != ReadSuccess)
+ success = false;
}
-
- if (readDocument(lex))
- return false;
- return true;
+ else if (success)
+ if (readDocument(lex))
+ success = false;
+ if (fn.exists())
+ fn.removeFile();
+ return success;
}
Buffer::ReadStatus Buffer::convertLyXFormat(FileName const & fn,
FileName & tmpfile, int from_format)
{
- tmpfile = FileName::tempName("Buffer_convertLyXFormat");
+ tmpfile = FileName::tempName("Buffer_convertLyXFormatXXXXXX.lyx");
if(tmpfile.empty()) {
Alert::error(_("Conversion failed"),
bformat(_("%1$s is from a different"
bool Buffer::writeFile(FileName const & fname) const
{
- // FIXME Do we need to do these here? I don't think writing
- // the LyX file depends upon it. (RGH)
- // updateBuffer();
- // updateMacroInstances();
-
if (d->read_only && fname == d->filename)
return false;
// The top of the file should not be written by params().
// write out a comment in the top of the file
+ // Important: Keep the version formatting in sync with lyx2lyx and
+ // tex2lyx (bug 7951)
ofs << "#LyX " << lyx_version_major << "." << lyx_version_minor
<< " created this file. For more info see http://www.lyx.org/\n"
<< "\\lyxformat " << LYX_FORMAT << "\n"
lyxerr << "File '" << fname << "' was not closed properly." << endl;
}
- errors("Export");
+ if (runparams_in.silent)
+ errorList.clear();
+ else
+ errors("Export");
return !failed_export;
}
d->ignore_parent = true;
// Classify the unicode characters appearing in math insets
- Encodings::initUnicodeMath(*this);
+ BufferEncodings::initUnicodeMath(*this);
// validate the buffer.
LYXERR(Debug::LATEX, " Validating buffer...");
LaTeXFeatures features(*this, params(), runparams);
validate(features);
- runparams.use_polyglossia = features.usePolyglossia();
+ // This is only set once per document (in master)
+ if (!runparams.is_child)
+ runparams.use_polyglossia = features.usePolyglossia();
LYXERR(Debug::LATEX, " Buffer validation done.");
bool const output_preamble =
runparams.use_babel = params().writeLaTeX(os, features,
d->filename.onlyPath());
- runparams.use_japanese = features.isRequired("japanese");
+ // Japanese might be required only in some children of a document,
+ // but once required, we must keep use_japanese true.
+ runparams.use_japanese |= features.isRequired("japanese");
if (!output_body) {
// Restore the parenthood if needed
if (output_preamble) {
os << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
- << "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.1 plus MathML 2.0//EN\" \"http://www.w3.org/TR/2001/REC-MathML2-20010221/dtd/xhtml-math11-f.dtd\">\n"
+ << "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.1 plus MathML 2.0//EN\" \"http://www.w3.org/Math/DTD/mathml2/xhtml-math11-f.dtd\">\n"
// FIXME Language should be set properly.
<< "<html xmlns=\"http://www.w3.org/1999/xhtml\">\n"
<< "<head>\n"
<< ";\n";
css << "}\n";
}
-
+
docstring const dstyles = css.str();
if (!dstyles.empty()) {
bool written = false;
if (output_body_tag)
os << "<body>\n";
XHTMLStream xs(os);
- params().documentClass().counters().reset();
+ if (output != IncludedFile)
+ // if we're an included file, the counters are in the master.
+ params().documentClass().counters().reset();
xhtmlParagraphs(text(), *this, xs, runparams);
if (output_body_tag)
os << "</body>\n";
if (res == -1) {
Alert::error(_("chktex failure"),
_("Could not run chktex successfully."));
- } else if (res > 0) {
+ } else {
ErrorList & errlist = d->errorLists["ChkTeX"];
errlist.clear();
bufferErrors(terr, errlist);
setBusy(false);
- errors("ChkTeX");
+ if (runparams.silent)
+ d->errorLists["ChkTeX"].clear();
+ else
+ errors("ChkTeX");
return res;
}
switch (cmd.action()) {
- case LFUN_BUFFER_TOGGLE_READ_ONLY:
- flag.setOnOff(isReadonly());
- break;
+ case LFUN_BUFFER_TOGGLE_READ_ONLY:
+ flag.setOnOff(isReadonly());
+ break;
// FIXME: There is need for a command-line import.
//case LFUN_BUFFER_IMPORT:
- case LFUN_BUFFER_AUTO_SAVE:
- break;
+ case LFUN_BUFFER_AUTO_SAVE:
+ break;
- case LFUN_BUFFER_EXPORT_CUSTOM:
- // FIXME: Nothing to check here?
- break;
+ case LFUN_BUFFER_EXPORT_CUSTOM:
+ // FIXME: Nothing to check here?
+ break;
- case LFUN_BUFFER_EXPORT: {
- docstring const arg = cmd.argument();
- if (arg == "custom") {
- enable = true;
- break;
- }
- string format = to_utf8(arg);
- size_t pos = format.find(' ');
- if (pos != string::npos)
- format = format.substr(0, pos);
- enable = params().isExportable(format);
- if (!enable)
- flag.message(bformat(
- _("Don't know how to export to format: %1$s"), arg));
+ case LFUN_BUFFER_EXPORT: {
+ docstring const arg = cmd.argument();
+ if (arg == "custom") {
+ enable = true;
break;
}
+ string format = to_utf8(arg);
+ size_t pos = format.find(' ');
+ if (pos != string::npos)
+ format = format.substr(0, pos);
+ enable = params().isExportable(format);
+ if (!enable)
+ flag.message(bformat(
+ _("Don't know how to export to format: %1$s"), arg));
+ break;
+ }
- case LFUN_BUFFER_CHKTEX:
- enable = params().isLatex() && !lyxrc.chktex_command.empty();
- break;
+ case LFUN_BUFFER_CHKTEX:
+ enable = params().isLatex() && !lyxrc.chktex_command.empty();
+ break;
- case LFUN_BUILD_PROGRAM:
- enable = params().isExportable("program");
- break;
+ case LFUN_BUILD_PROGRAM:
+ enable = params().isExportable("program");
+ break;
- case LFUN_BRANCH_ADD:
- case LFUN_BRANCHES_RENAME:
- case LFUN_BUFFER_PRINT:
- // if no Buffer is present, then of course we won't be called!
- break;
+ case LFUN_BRANCH_ACTIVATE:
+ case LFUN_BRANCH_DEACTIVATE:
+ case LFUN_BRANCH_MASTER_ACTIVATE:
+ case LFUN_BRANCH_MASTER_DEACTIVATE: {
+ bool const master = (cmd.action() == LFUN_BRANCH_MASTER_ACTIVATE
+ || cmd.action() == LFUN_BRANCH_MASTER_DEACTIVATE);
+ BranchList const & branchList = master ? masterBuffer()->params().branchlist()
+ : params().branchlist();
+ docstring const branchName = cmd.argument();
+ flag.setEnabled(!branchName.empty() && branchList.find(branchName));
+ break;
+ }
- case LFUN_BUFFER_LANGUAGE:
- enable = !isReadonly();
- break;
+ case LFUN_BRANCH_ADD:
+ case LFUN_BRANCHES_RENAME:
+ case LFUN_BUFFER_PRINT:
+ // if no Buffer is present, then of course we won't be called!
+ break;
- default:
- return false;
+ case LFUN_BUFFER_LANGUAGE:
+ enable = !isReadonly();
+ break;
+
+ default:
+ return false;
}
flag.setEnabled(enable);
return true;
break;
}
- case LFUN_BUILD_PROGRAM:
- doExport("program", true);
+ case LFUN_BUILD_PROGRAM: {
+ ExportStatus const status = doExport("program", true);
+ dr.setError(status != ExportSuccess);
+ if (status != ExportSuccess)
+ dr.setMessage(_("Error generating literate programming code."));
break;
+ }
case LFUN_BUFFER_CHKTEX:
runChktex();
resetAutosaveTimers();
break;
+ case LFUN_BRANCH_ACTIVATE:
+ case LFUN_BRANCH_DEACTIVATE:
+ case LFUN_BRANCH_MASTER_ACTIVATE:
+ case LFUN_BRANCH_MASTER_DEACTIVATE: {
+ bool const master = (func.action() == LFUN_BRANCH_MASTER_ACTIVATE
+ || func.action() == LFUN_BRANCH_MASTER_DEACTIVATE);
+ Buffer * buf = master ? const_cast<Buffer *>(masterBuffer())
+ : this;
+
+ docstring const branch_name = func.argument();
+ // the case without a branch name is handled elsewhere
+ if (branch_name.empty()) {
+ dispatched = false;
+ break;
+ }
+ Branch * branch = buf->params().branchlist().find(branch_name);
+ if (!branch) {
+ LYXERR0("Branch " << branch_name << " does not exist.");
+ dr.setError(true);
+ docstring const msg =
+ bformat(_("Branch \"%1$s\" does not exist."), branch_name);
+ dr.setMessage(msg);
+ break;
+ }
+ bool const activate = (func.action() == LFUN_BRANCH_ACTIVATE
+ || func.action() == LFUN_BRANCH_MASTER_ACTIVATE);
+ if (branch->isSelected() != activate) {
+ buf->undo().recordUndoFullDocument(CursorData());
+ branch->setSelected(activate);
+ dr.setError(false);
+ dr.screenUpdate(Update::Force);
+ dr.forceBufferUpdate();
+ }
+ break;
+ }
+
case LFUN_BRANCH_ADD: {
docstring branch_name = func.argument();
if (branch_name.empty()) {
msg += ("\n");
msg += bformat(_("Branch \"%1$s\" already exists."), branch_name);
} else {
+ undo().recordUndoFullDocument(CursorData());
branch_list.add(branch_name);
branch = branch_list.find(branch_name);
string const x11hexname = X11hexname(branch->color());
break;
}
- if (!doExport("dvi", true)) {
+ if (doExport("dvi", true) != ExportSuccess) {
showPrintError(absFileName());
dr.setMessage(_("Error exporting to DVI."));
break;
void Buffer::changeLanguage(Language const * from, Language const * to)
{
- LASSERT(from, /**/);
- LASSERT(to, /**/);
+ LASSERT(from, return);
+ LASSERT(to, return);
for_each(par_iterator_begin(),
par_iterator_end(),
bool Buffer::isExternallyModified(CheckMethod method) const
{
- LASSERT(d->filename.exists(), /**/);
+ LASSERT(d->filename.exists(), return false);
// if method == timestamp, check timestamp before checksum
return (method == checksum_method
|| d->timestamp_ != d->filename.lastModified())
string Buffer::filePath() const
{
- int last = d->filename.onlyPath().absFileName().length() - 1;
+ string const abs = d->filename.onlyPath().absFileName();
+ if (abs.empty())
+ return abs;
+ int last = abs.length() - 1;
- return d->filename.onlyPath().absFileName()[last] == '/'
- ? d->filename.onlyPath().absFileName()
- : d->filename.onlyPath().absFileName() + "/";
+ return abs[last] == '/' ? abs : abs + '/';
}
}
-Buffer::References & Buffer::references(docstring const & label)
+Buffer::References & Buffer::getReferenceCache(docstring const & label)
{
if (d->parent())
- return const_cast<Buffer *>(masterBuffer())->references(label);
+ return const_cast<Buffer *>(masterBuffer())->getReferenceCache(label);
RefCache::iterator it = d->ref_cache_.find(label);
if (it != d->ref_cache_.end())
Buffer::References const & Buffer::references(docstring const & label) const
{
- return const_cast<Buffer *>(this)->references(label);
+ return const_cast<Buffer *>(this)->getReferenceCache(label);
+}
+
+
+void Buffer::addReference(docstring const & label, Inset * inset, ParIterator it)
+{
+ References & refs = getReferenceCache(label);
+ refs.push_back(make_pair(inset, it));
}
}
-void Buffer::changeRefsIfUnique(docstring const & from, docstring const & to,
- InsetCode code)
+void Buffer::changeRefsIfUnique(docstring const & from, docstring const & to)
{
//FIXME: This does not work for child documents yet.
- LASSERT(code == CITE_CODE, /**/);
-
reloadBibInfoCache();
// Check if the label 'from' appears more than once
string const paramName = "key";
for (InsetIterator it = inset_iterator_begin(inset()); it; ++it) {
- if (it->lyxCode() == code) {
- InsetCommand * inset = it->asInsetCommand();
- if (!inset)
- continue;
- docstring const oldValue = inset->getParam(paramName);
- if (oldValue == from)
- inset->setParam(paramName, to);
- }
+ if (it->lyxCode() != CITE_CODE)
+ continue;
+ InsetCommand * inset = it->asInsetCommand();
+ docstring const oldValue = inset->getParam(paramName);
+ if (oldValue == from)
+ inset->setParam(paramName, to);
}
}
}
-// helper class, to guarantee this gets reset properly
-class Buffer::MarkAsExporting {
-public:
- MarkAsExporting(Buffer const * buf) : buf_(buf)
- {
- LASSERT(buf_, /* */);
- buf_->setExportStatus(true);
- }
- ~MarkAsExporting()
- {
- buf_->setExportStatus(false);
- }
-private:
- Buffer const * const buf_;
-};
-
-
-
void Buffer::setExportStatus(bool e) const
{
d->doing_export = e;
// Emit the signal to show the error list or copy it back to the
// cloned Buffer so that it can be emitted afterwards.
if (format != backend_format) {
- if (d->cloned_buffer_) {
+ if (runparams.silent)
+ error_list.clear();
+ else if (d->cloned_buffer_)
d->cloned_buffer_->d->errorLists[error_type] =
d->errorLists[error_type];
- } else
+ else
errors(error_type);
// also to the children, in case of master-buffer-view
ListOfBuffers clist = getDescendents();
ListOfBuffers::const_iterator cit = clist.begin();
ListOfBuffers::const_iterator const cen = clist.end();
for (; cit != cen; ++cit) {
- if (d->cloned_buffer_) {
+ if (runparams.silent)
+ (*cit)->d->errorLists[error_type].clear();
+ else if (d->cloned_buffer_) {
// Enable reverse search by copying back the
// texrow object to the cloned buffer.
// FIXME: this is not thread safe.
void Buffer::updateBuffer(UpdateScope scope, UpdateType utype) const
{
+ LBUFERR(!text().paragraphs().empty());
+
// Use the master text class also for child documents
Buffer const * const master = masterBuffer();
DocumentClass const & textclass = master->params().documentClass();
// do this only if we are the top-level Buffer
- if (master == this)
+ if (master == this) {
+ textclass.counters().reset(from_ascii("bibitem"));
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.
if (master != this) {
bufToUpdate.insert(this);
master->updateBuffer(UpdateMaster, utype);
- // Do this here in case the master has no gui associated with it. Then,
- // the TocModel is not updated and TocModel::toc_ is invalid (bug 5699).
- if (!master->d->gui_)
+ // If the master buffer has no gui associated with it, then the TocModel is
+ // not updated during the updateBuffer call and TocModel::toc_ is invalid
+ // (bug 5699). The same happens if the master buffer is open in a different
+ // window. This test catches both possibilities.
+ // See: http://marc.info/?l=lyx-devel&m=138590578911716&w=2
+ // There remains a problem here: If there is another child open in yet a third
+ // window, that TOC is not updated. So some more general solution is needed at
+ // some point.
+ if (master->d->gui_ != d->gui_)
structureChanged();
// was buf referenced from the master (i.e. not in bufToUpdate anymore)?
Buffer & cbuf = const_cast<Buffer &>(*this);
- LASSERT(!text().paragraphs().empty(), /**/);
-
// do the real work
ParIterator parit = cbuf.par_iterator_begin();
updateBuffer(parit, utype);
d->bibinfo_cache_valid_ = true;
d->cite_labels_valid_ = true;
- cbuf.tocBackend().update(true);
+ cbuf.tocBackend().update(utype == OutputUpdate);
if (scope == UpdateMaster)
cbuf.structureChanged();
}
static bool needEnumCounterReset(ParIterator const & it)
{
Paragraph const & par = *it;
- LASSERT(par.layout().labeltype == LABEL_ENUMERATE, /**/);
+ LASSERT(par.layout().labeltype == LABEL_ENUMERATE, return false);
depth_type const cur_depth = par.getDepth();
ParIterator prev_it = it;
while (prev_it.pit()) {
void Buffer::updateBuffer(ParIterator & parit, UpdateType utype) const
{
+ // LASSERT: Is it safe to continue here, or should we just return?
LASSERT(parit.pit() == 0, /**/);
// Set the position of the text in the buffer to be able
word_count_ = 0;
char_count_ = 0;
blank_count_ = 0;
-
+
for (DocIterator dit = from ; dit != to && !dit.atEnd(); ) {
if (!dit.inTexted()) {
dit.forwardPos();
continue;
}
-
+
Paragraph const & par = dit.paragraph();
pos_type const pos = dit.pos();
-
+
// Copied and adapted from isWordSeparator() in Paragraph
if (pos == dit.lastpos()) {
inword = false;
break;
continue;
} else if (!par.isDeleted(pos)) {
- if (par.isWordSeparator(pos))
+ if (par.isWordSeparator(pos))
inword = false;
else if (!inword) {
++word_count_;
}
-Buffer::ReadStatus Buffer::reload(bool clearUndo)
+Buffer::ReadStatus Buffer::reload()
{
setBusy(true);
// c.f. bug http://www.lyx.org/trac/ticket/6587
updateTitles();
markClean();
message(bformat(_("Document %1$s reloaded."), disp_fn));
- if (clearUndo)
- d->undo_.clear();
+ d->undo_.clear();
} else {
message(bformat(_("Could not reload document %1$s."), disp_fn));
}