* This file is part of LyX, the document processor.
* Licence details can be found in the file COPYING.
*
- * \author Lars Gullik Bjønnes
+ * \author Lars Gullik Bjønnes
* \author Stefan Schimanski
*
* Full author contact details are available in file CREDITS.
#include "graphics/Previews.h"
-#include "support/assert.h"
+#include "support/lassert.h"
#include "support/convert.h"
#include "support/debug.h"
#include "support/ExceptionMessage.h"
#include <fstream>
#include <iomanip>
#include <map>
+#include <set>
#include <sstream>
#include <stack>
#include <vector>
namespace {
-int const LYX_FORMAT = 328;
+// Do not remove the comment below, so we get merge conflict in
+// independent branches. Instead add your own.
+int const LYX_FORMAT = 348; // uwestoehr: add support for \*phantom
typedef map<string, bool> DepClean;
typedef map<docstring, pair<InsetLabel const *, Buffer::References> > RefCache;
} // namespace anon
+class BufferSet : public std::set<Buffer const *> {};
+
class Buffer::Impl
{
public:
wa_->closeAll();
delete wa_;
}
+ delete inset;
}
-
+
BufferParams params;
LyXVC lyxvc;
FileName temppath;
/// which maps the macro definition position to the scope and the MacroData.
NamePositionScopeMacroMap macros;
bool macro_lock;
-
+
/// positions of child buffers in the buffer
typedef map<Buffer const * const, DocIterator> BufferPositionMap;
typedef pair<DocIterator, Buffer const *> ScopeBuffer;
/// documents), needed for appropriate update of natbib labels.
mutable support::FileNameList bibfilesCache_;
+ // FIXME The caching mechanism could be improved. At present, we have a
+ // cache for each Buffer, that caches all the bibliography info for that
+ // Buffer. A more efficient solution would be to have a global cache per
+ // file, and then to construct the Buffer's bibinfo from that.
+ /// A cache for bibliography info
+ mutable BiblioInfo bibinfo_;
+ /// whether the bibinfo cache is valid
+ bool bibinfoCacheValid_;
+ /// Cache of timestamps of .bib files
+ map<FileName, time_t> bibfileStatus_;
+
mutable RefCache ref_cache_;
/// our Text that should be wrapped in an InsetText
- InsetText inset;
+ InsetText * inset;
};
Buffer::Impl::Impl(Buffer & parent, FileName const & file, bool readonly_)
: parent_buffer(0), lyx_clean(true), bak_clean(true), unnamed(false),
read_only(readonly_), filename(file), file_fully_loaded(false),
- toc_backend(&parent), macro_lock(false),
- timestamp_(0), checksum_(0), wa_(0), undo_(parent)
+ toc_backend(&parent), macro_lock(false), timestamp_(0),
+ checksum_(0), wa_(0), undo_(parent), bibinfoCacheValid_(false)
{
temppath = createBufferTmpDir();
lyxvc.setBuffer(&parent);
{
LYXERR(Debug::INFO, "Buffer::Buffer()");
- d->inset.setBuffer(*this);
- d->inset.initParagraphs(*this);
- d->inset.setAutoBreakRows(true);
- d->inset.getText(0)->setMacrocontextPosition(par_iterator_begin());
+ d->inset = new InsetText(*this);
+ d->inset->setAutoBreakRows(true);
+ d->inset->getText(0)->setMacrocontextPosition(par_iterator_begin());
}
// GuiView already destroyed
gui_ = 0;
+ if (d->unnamed && d->filename.extension() == "internal") {
+ // No need to do additional cleanups for internal buffer.
+ delete d;
+ return;
+ }
+
+ // loop over children
+ Impl::BufferPositionMap::iterator it = d->children_positions.begin();
+ Impl::BufferPositionMap::iterator end = d->children_positions.end();
+ for (; it != end; ++it) {
+ Buffer * child = const_cast<Buffer *>(it->first);
+ // The child buffer might have been closed already.
+ if (theBufferList().isLoaded(child))
+ theBufferList().releaseChild(this, child);
+ }
+
// clear references to children in macro tables
d->children_positions.clear();
d->position_to_children.clear();
}
// Remove any previewed LaTeX snippets associated with this buffer.
- graphics::Previews::get().removeLoader(*this);
+ thePreviews().removeLoader(*this);
delete d;
}
Text & Buffer::text() const
{
- return const_cast<Text &>(d->inset.text_);
+ return d->inset->text();
}
Inset & Buffer::inset() const
{
- return const_cast<InsetText &>(d->inset);
+ return *d->inset;
}
}
+TexRow & Buffer::texrow()
+{
+ return d->texrow;
+}
+
+
TexRow const & Buffer::texrow() const
{
return d->texrow;
params().branchlist().clear();
params().preamble.erase();
params().options.erase();
+ params().master.erase();
params().float_placement.erase();
params().paperwidth.erase();
params().paperheight.erase();
params().headsep.erase();
params().footskip.erase();
params().columnsep.erase();
+ params().fontsCJK.erase();
params().listings_params.clear();
params().clearLayoutModules();
+ params().clearRemovedModules();
params().pdfoptions().clear();
-
+
for (int i = 0; i < 4; ++i) {
params().user_defined_bullet(i) = ITEMIZE_DEFAULTS[i];
params().temp_bullet(i) = ITEMIZE_DEFAULTS[i];
errorList.push_back(ErrorItem(_("Document header error"),
s, -1, 0, 0));
}
-
+
params().makeDocumentClass();
return unknown_tokens;
// Uwe C. Schroeder
// changed to be public and have one parameter
-// Returns false if "\end_document" is not read (Asger)
+// Returns true if "\end_document" is not read (Asger)
bool Buffer::readDocument(Lexer & lex)
{
ErrorList & errorList = d->errorLists["Parse"];
if (params().outputChanges) {
bool dvipost = LaTeXFeatures::isAvailable("dvipost");
- bool xcolorsoul = LaTeXFeatures::isAvailable("soul") &&
+ bool xcolorulem = LaTeXFeatures::isAvailable("ulem") &&
LaTeXFeatures::isAvailable("xcolor");
- if (!dvipost && !xcolorsoul) {
+ if (!dvipost && !xcolorulem) {
Alert::warning(_("Changes not shown in LaTeX output"),
_("Changes will not be highlighted in LaTeX output, "
- "because neither dvipost nor xcolor/soul are installed.\n"
+ "because neither dvipost nor xcolor/ulem are installed.\n"
"Please install these packages or redefine "
"\\lyxadded and \\lyxdeleted in the LaTeX preamble."));
- } else if (!xcolorsoul) {
+ } else if (!xcolorulem) {
Alert::warning(_("Changes not shown in LaTeX output"),
_("Changes will not be highlighted in LaTeX output "
- "when using pdflatex, because xcolor and soul are not installed.\n"
+ "when using pdflatex, because xcolor and ulem are not installed.\n"
"Please install both packages or redefine "
"\\lyxadded and \\lyxdeleted in the LaTeX preamble."));
}
}
+ if (!params().master.empty()) {
+ FileName const master_file = makeAbsPath(params().master,
+ onlyPath(absFileName()));
+ if (isLyXFilename(master_file.absFilename())) {
+ Buffer * master =
+ checkAndLoadLyXFile(master_file, true);
+ if (master) {
+ // set master as master buffer, but only
+ // if we are a real child
+ if (master->isChild(this))
+ setParent(master);
+ // if the master is not fully loaded
+ // it is probably just loading this
+ // child. No warning needed then.
+ else if (master->isFullyLoaded())
+ LYXERR0("The master '"
+ << params().master
+ << "' assigned to this document '"
+ << absFileName()
+ << "' does not include "
+ "this document. Ignoring the master assignment.");
+ }
+ }
+ }
+
// read main text
- bool const res = text().read(*this, lex, errorList, &(d->inset));
+ bool const res = text().read(*this, lex, errorList, d->inset);
updateMacros();
updateMacroInstances();
++pos;
space_inserted = true;
} else {
- const pos_type n = 8 - pos % 8;
- for (pos_type i = 0; i < n; ++i) {
- par.insertChar(pos, ' ', font, params().trackChanges);
- ++pos;
- }
+ par.insertChar(pos, *cit, font, params().trackChanges);
+ ++pos;
space_inserted = true;
}
} else if (!isPrintable(*cit)) {
Lexer lex;
istringstream is(s);
lex.setStream(is);
- FileName const name = FileName::tempName();
+ FileName const name = FileName::tempName("Buffer_readString");
switch (readFile(lex, name, true)) {
case failure:
return false;
{
FileName fname(filename);
+ params().compressed = fname.isZippedFile();
+
// remove dummy empty par
paragraphs().clear();
Lexer lex;
// lyx2lyx would fail
return wrongversion;
- FileName const tmpfile = FileName::tempName();
+ FileName const tmpfile = FileName::tempName("Buffer_readFile");
if (tmpfile.empty()) {
Alert::error(_("Conversion failed"),
bformat(_("%1$s is from a different"
string const encoding = runparams.encoding->iconvName();
LYXERR(Debug::LATEX, "makeLaTeXFile encoding: " << encoding << "...");
- odocfstream ofs;
+ ofdocstream ofs;
try { ofs.reset(encoding); }
catch (iconv_codecvt_facet_exception & e) {
lyxerr << "Caught iconv exception: " << e.what() << endl;
"representable in the chosen encoding.\n"
"Changing the document encoding to utf8 could help."),
e.par_id, e.pos, e.pos + 1));
- failed_export = true;
+ failed_export = true;
}
catch (iconv_codecvt_facet_exception & e) {
errorList.push_back(ErrorItem(_("iconv conversion failed"),
}
catch (...) {
lyxerr << "Caught some really weird exception..." << endl;
- LyX::cref().exit(1);
+ lyx_exit(1);
}
ofs.close();
OutputParams runparams = runparams_in;
+ // Classify the unicode characters appearing in math insets
+ Encodings::initUnicodeMath(*this);
+
// validate the buffer.
LYXERR(Debug::LATEX, " Validating buffer...");
LaTeXFeatures features(*this, params(), runparams);
// because then the macros will not get the right "redefinition"
// flag as they don't see the parent macros which are output before.
updateMacros();
-
+
// fold macros if possible, still with parent buffer as the
// macros will be put in the prefix anyway.
updateMacroInstances();
-
+
// There are a few differences between nice LaTeX and usual files:
// usual is \batchmode and has a
// special input@path to allow the including of figures
// Write the preamble
runparams.use_babel = params().writeLaTeX(os, features, d->texrow);
+ runparams.use_japanese = features.isRequired("japanese");
+
if (!output_body)
return;
// 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)
- (*it)->write(os, true);
+ (*it)->write(os, true);
} // output_preamble
d->texrow.start(paragraphs().begin()->id(), 0);
-
+
LYXERR(Debug::INFO, "preamble finished, now the body.");
// if we are doing a real file with body, even if this is the
{
LYXERR(Debug::LATEX, "makeDocBookFile...");
- odocfstream ofs;
+ ofdocstream ofs;
if (!openFileWrite(ofs, fname))
return;
if (runparams.flavor == OutputParams::XML)
top += params().language->code();
else
- top += params().language->code().substr(0,2);
+ top += params().language->code().substr(0, 2);
top += '"';
if (!params().options.empty()) {
bibfiles.end());
}
}
+ // the bibinfo cache is now invalid
+ d->bibinfoCacheValid_ = false;
+}
+
+
+void Buffer::invalidateBibinfoCache()
+{
+ d->bibinfoCacheValid_ = false;
}
}
+BiblioInfo const & Buffer::masterBibInfo() const
+{
+ // if this is a child document and the parent is already loaded
+ // use the parent's list instead [ale990412]
+ Buffer const * const tmp = masterBuffer();
+ LASSERT(tmp, /**/);
+ if (tmp != this)
+ return tmp->masterBibInfo();
+ return localBibInfo();
+}
+
+
+BiblioInfo const & Buffer::localBibInfo() const
+{
+ if (d->bibinfoCacheValid_) {
+ support::FileNameList const & bibfilesCache = getBibfilesCache();
+ // compare the cached timestamps with the actual ones.
+ support::FileNameList::const_iterator ei = bibfilesCache.begin();
+ support::FileNameList::const_iterator en = bibfilesCache.end();
+ for (; ei != en; ++ ei) {
+ time_t lastw = ei->lastModified();
+ if (lastw != d->bibfileStatus_[*ei]) {
+ d->bibinfoCacheValid_ = false;
+ d->bibfileStatus_[*ei] = lastw;
+ break;
+ }
+ }
+ }
+
+ if (!d->bibinfoCacheValid_) {
+ d->bibinfo_.clear();
+ for (InsetIterator it = inset_iterator_begin(inset()); it; ++it)
+ it->fillWithBibKeys(d->bibinfo_, it);
+ d->bibinfoCacheValid_ = true;
+ }
+ return d->bibinfo_;
+}
+
+
bool Buffer::isDepClean(string const & name) const
{
DepClean::const_iterator const it = d->dep_clean.find(name);
break;
}
+ case LFUN_BRANCH_ACTIVATE:
+ case LFUN_BRANCH_DEACTIVATE: {
+ BranchList & branchList = params().branchlist();
+ docstring const branchName = func.argument();
+ Branch * branch = branchList.find(branchName);
+ if (!branch)
+ LYXERR0("Branch " << branchName << " does not exist.");
+ else
+ branch->setSelected(func.action == LFUN_BRANCH_ACTIVATE);
+ if (result)
+ *result = true;
+ }
+
default:
dispatched = false;
}
DocIterator Buffer::getParFromID(int const id) const
{
+ Buffer * buf = const_cast<Buffer *>(this);
if (id < 0) {
// John says this is called with id == -1 from undo
lyxerr << "getParFromID(), id: " << id << endl;
- return doc_iterator_end(inset());
+ return doc_iterator_end(buf);
}
- for (DocIterator it = doc_iterator_begin(inset()); !it.atEnd(); it.forwardPar())
+ for (DocIterator it = doc_iterator_begin(buf); !it.atEnd(); it.forwardPar())
if (it.paragraph().id() == id)
return it;
- return doc_iterator_end(inset());
+ return doc_iterator_end(buf);
}
ParIterator Buffer::par_iterator_begin()
{
- return ParIterator(doc_iterator_begin(inset()));
+ return ParIterator(doc_iterator_begin(this));
}
ParIterator Buffer::par_iterator_end()
{
- return ParIterator(doc_iterator_end(inset()));
+ return ParIterator(doc_iterator_end(this));
}
ParConstIterator Buffer::par_iterator_begin() const
{
- return lyx::par_const_iterator_begin(inset());
+ return ParConstIterator(doc_iterator_begin(this));
}
ParConstIterator Buffer::par_iterator_end() const
{
- return lyx::par_const_iterator_end(inset());
+ return ParConstIterator(doc_iterator_end(this));
}
{
LASSERT(d->filename.exists(), /**/);
// if method == timestamp, check timestamp before checksum
- return (method == checksum_method
+ return (method == checksum_method
|| d->timestamp_ != d->filename.lastModified())
&& d->checksum_ != d->filename.checksum();
}
}
-Buffer const * Buffer::parent()
+Buffer const * Buffer::parent() const
{
return d->parent_buffer;
}
+void Buffer::collectRelatives(BufferSet & bufs) const
+{
+ bufs.insert(this);
+ if (parent())
+ parent()->collectRelatives(bufs);
+
+ // loop over children
+ Impl::BufferPositionMap::iterator it = d->children_positions.begin();
+ Impl::BufferPositionMap::iterator end = d->children_positions.end();
+ for (; it != end; ++it)
+ bufs.insert(const_cast<Buffer *>(it->first));
+}
+
+
+std::vector<Buffer const *> Buffer::allRelatives() const
+{
+ BufferSet bufs;
+ collectRelatives(bufs);
+ BufferSet::iterator it = bufs.begin();
+ std::vector<Buffer const *> ret;
+ for (; it != bufs.end(); ++it)
+ ret.push_back(*it);
+ return ret;
+}
+
+
Buffer const * Buffer::masterBuffer() const
{
if (!d->parent_buffer)
return this;
-
+
return d->parent_buffer->masterBuffer();
}
+bool Buffer::isChild(Buffer * child) const
+{
+ return d->children_positions.find(child) != d->children_positions.end();
+}
+
+
+DocIterator Buffer::firstChildPosition(Buffer const * child)
+{
+ Impl::BufferPositionMap::iterator it;
+ it = d->children_positions.find(child);
+ if (it == d->children_positions.end())
+ return DocIterator(this);
+ return it->second;
+}
+
+
+std::vector<Buffer *> Buffer::getChildren() const
+{
+ std::vector<Buffer *> clist;
+ // loop over children
+ Impl::BufferPositionMap::iterator it = d->children_positions.begin();
+ Impl::BufferPositionMap::iterator end = d->children_positions.end();
+ for (; it != end; ++it) {
+ Buffer * child = const_cast<Buffer *>(it->first);
+ clist.push_back(child);
+ // there might be grandchildren
+ std::vector<Buffer *> glist = child->getChildren();
+ for (vector<Buffer *>::const_iterator git = glist.begin();
+ git != glist.end(); ++git)
+ clist.push_back(*git);
+ }
+ return clist;
+}
+
+
template<typename M>
typename M::iterator greatest_below(M & m, typename M::key_type const & x)
{
return m.end();
it--;
- return it;
+ return it;
}
-MacroData const * Buffer::getBufferMacro(docstring const & name,
+MacroData const * Buffer::getBufferMacro(docstring const & name,
DocIterator const & pos) const
{
LYXERR(Debug::MACROS, "Searching for " << to_ascii(name) << " at " << pos);
// we haven't found anything yet
DocIterator bestPos = par_iterator_begin();
MacroData const * bestData = 0;
-
+
// find macro definitions for name
Impl::NamePositionScopeMacroMap::iterator nameIt
- = d->macros.find(name);
+ = d->macros.find(name);
if (nameIt != d->macros.end()) {
// find last definition in front of pos or at pos itself
Impl::PositionScopeMacroMap::const_iterator it
- = greatest_below(nameIt->second, pos);
+ = greatest_below(nameIt->second, pos);
if (it != nameIt->second.end()) {
while (true) {
// scope ends behind pos?
bestData = &it->second.second;
break;
}
-
+
// try previous macro if there is one
if (it == nameIt->second.begin())
break;
// find macros in included files
Impl::PositionScopeBufferMap::const_iterator it
- = greatest_below(d->position_to_children, pos);
+ = greatest_below(d->position_to_children, pos);
if (it == d->position_to_children.end())
// no children before
return bestData;
break;
--it;
}
-
+
// return the best macro we have found
return bestData;
}
DocIterator const & pos, bool global) const
{
if (d->macro_lock)
- return 0;
+ return 0;
// query buffer macros
MacroData const * data = getBufferMacro(name, pos);
InsetList::const_iterator end = insets.end();
for (; iit != end; ++iit) {
it.pos() = iit->pos;
-
+
// is it a nested text inset?
if (iit->inset->asInsetText()) {
// Inset needs its own scope?
- InsetText const * itext
+ InsetText const * itext
= iit->inset->asInsetText();
bool newScope = itext->isMacroScope();
- // scope which ends just behind the inset
+ // scope which ends just behind the inset
DocIterator insetScope = it;
++insetScope.pos();
it.pop_back();
continue;
}
-
+
// is it an external file?
if (iit->inset->lyxCode() == INCLUDE_CODE) {
// get buffer of external file
- InsetCommand const & inset
- = static_cast<InsetCommand const &>(*iit->inset);
- InsetCommandParams const & ip = inset.params();
+ InsetInclude const & inset
+ = static_cast<InsetInclude const &>(*iit->inset);
d->macro_lock = true;
- Buffer * child = loadIfNeeded(*this, ip);
+ Buffer * child = inset.loadIfNeeded(*this);
d->macro_lock = false;
if (!child)
- continue;
+ continue;
// register its position, but only when it is
// included first in the buffer
if (d->children_positions.find(child)
== d->children_positions.end())
d->children_positions[child] = it;
-
+
// register child with its scope
d->position_to_children[it] = Impl::ScopeBuffer(scope, child);
continue;
if (iit->inset->lyxCode() != MATHMACRO_CODE)
continue;
-
+
// get macro data
MathMacroTemplate & macroTemplate
= static_cast<MathMacroTemplate &>(*iit->inset);
{
LYXERR(Debug::MACROS, "updateMacroInstances for "
<< d->filename.onlyFileName());
- DocIterator it = doc_iterator_begin(inset());
- DocIterator end = doc_iterator_end(inset());
+ DocIterator it = doc_iterator_begin(this);
+ DocIterator end = doc_iterator_end(this);
for (; it != end; it.forwardPos()) {
// look for MathData cells in InsetMathNest insets
Inset * inset = it.nextInset();
return;
d->macro_lock = true;
-
+
// loop over macro names
Impl::NamePositionScopeMacroMap::iterator nameIt = d->macros.begin();
Impl::NamePositionScopeMacroMap::iterator nameEnd = d->macros.end();
if (d->parent_buffer)
d->parent_buffer->listMacroNames(macros);
- d->macro_lock = false;
+ d->macro_lock = false;
}
{
if (!d->parent_buffer)
return;
-
+
MacroNameSet names;
d->parent_buffer->listMacroNames(names);
-
+
// resolve macros
MacroNameSet::iterator it = names.begin();
MacroNameSet::iterator end = names.end();
for (; it != end; ++it) {
// defined?
- MacroData const * data =
+ MacroData const * data =
d->parent_buffer->getMacro(*it, *this, false);
if (data) {
macros.insert(data);
-
+
// we cannot access the original MathMacroTemplate anymore
// here to calls validate method. So we do its work here manually.
// FIXME: somehow make the template accessible here.
// Check if the label 'from' appears more than once
vector<docstring> labels;
string paramName;
- BiblioInfo keys;
- keys.fillWithBibKeys(this);
+ BiblioInfo const & keys = masterBibInfo();
BiblioInfo::const_iterator bit = keys.begin();
BiblioInfo::const_iterator bend = keys.end();
void Buffer::getSourceCode(odocstream & os, pit_type par_begin,
- pit_type par_end, bool full_source)
+ pit_type par_end, bool full_source) const
{
OutputParams runparams(¶ms().encoding());
runparams.nice = true;
os << "% " << _("Preview source code") << "\n\n";
d->texrow.newline();
d->texrow.newline();
- if (isLatex())
- writeLaTeXSource(os, filePath(), runparams, true, true);
- else
+ if (isDocBook())
writeDocBookSource(os, absFileName(), runparams, false);
+ else
+ // latex or literate
+ writeLaTeXSource(os, string(), runparams, true, true);
} else {
runparams.par_begin = par_begin;
runparams.par_end = par_end;
d->texrow.newline();
d->texrow.newline();
// output paragraphs
- if (isLatex())
- latexParagraphs(*this, text(), os, d->texrow, runparams);
- else
- // DocBook
+ if (isDocBook())
docbookParagraphs(paragraphs(), *this, os, runparams);
+ else
+ // latex or literate
+ latexParagraphs(*this, text(), os, d->texrow, runparams);
}
}
}
+void Buffer::updateTocItem(std::string const & type,
+ DocIterator const & dit) const
+{
+ if (gui_)
+ gui_->updateTocItem(type, dit);
+}
+
+
void Buffer::structureChanged() const
{
if (gui_)
}
+bool Buffer::hasGuiDelegate() const
+{
+ return gui_;
+}
+
+
void Buffer::setGuiDelegate(frontend::GuiBufferDelegate * gui)
{
gui_ = gui;
///
int start()
{
- command_ = to_utf8(bformat(_("Auto-saving %1$s"),
+ command_ = to_utf8(bformat(_("Auto-saving %1$s"),
from_utf8(fname_.absFilename())));
return run(DontWait);
}
return "docbook";
if (isLiterate())
return "literate";
+ if (params().encoding().package() == Encoding::japanese)
+ return "platex";
return "latex";
}
string const error_type = (format == "program")
? "Build" : bufferFormat();
+ 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),
tmp_result_file, FileName(absFileName()), backend_format, format,
- errorList(error_type));
+ error_list);
// Emit the signal to show the error list.
if (format != backend_format)
errors(error_type);
}
+void Buffer::setBuffersForInsets() const
+{
+ inset().setBuffer(const_cast<Buffer &>(*this));
+}
+
+
+void Buffer::updateLabels(UpdateScope scope) const
+{
+ // Use the master text class also for child documents
+ Buffer const * const master = masterBuffer();
+ DocumentClass const & textclass = master->params().documentClass();
+
+ // 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.
+ // via an InsetInclude). The remaining ones in the set need still be updated.
+ static std::set<Buffer const *> bufToUpdate;
+ if (scope == UpdateMaster) {
+ // If this is a child document start with the master
+ if (master != this) {
+ bufToUpdate.insert(this);
+ master->updateLabels();
+ // 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->gui_)
+ structureChanged();
+
+ // was buf referenced from the master (i.e. not in bufToUpdate anymore)?
+ if (bufToUpdate.find(this) == bufToUpdate.end())
+ return;
+ }
+
+ // start over the counters in the master
+ textclass.counters().reset();
+ }
+
+ // update will be done below for this buffer
+ bufToUpdate.erase(this);
+
+ // update all caches
+ clearReferenceCache();
+ updateMacros();
+
+ Buffer & cbuf = const_cast<Buffer &>(*this);
+
+ LASSERT(!text().paragraphs().empty(), /**/);
+
+ // do the real work
+ ParIterator parit = cbuf.par_iterator_begin();
+ updateLabels(parit);
+
+ if (master != this)
+ // TocBackend update will be done later.
+ return;
+
+ cbuf.tocBackend().update();
+ if (scope == UpdateMaster)
+ cbuf.structureChanged();
+}
+
+
+static depth_type getDepth(DocIterator const & it)
+{
+ depth_type depth = 0;
+ for (size_t i = 0 ; i < it.depth() ; ++i)
+ if (!it[i].inset().inMathed())
+ depth += it[i].paragraph().getDepth() + 1;
+ // remove 1 since the outer inset does not count
+ return depth - 1;
+}
+
+static depth_type getItemDepth(ParIterator const & it)
+{
+ Paragraph const & par = *it;
+ LabelType const labeltype = par.layout().labeltype;
+
+ if (labeltype != LABEL_ENUMERATE && labeltype != LABEL_ITEMIZE)
+ return 0;
+
+ // this will hold the lowest depth encountered up to now.
+ depth_type min_depth = getDepth(it);
+ ParIterator prev_it = it;
+ while (true) {
+ if (prev_it.pit())
+ --prev_it.top().pit();
+ else {
+ // start of nested inset: go to outer par
+ prev_it.pop_back();
+ if (prev_it.empty()) {
+ // start of document: nothing to do
+ return 0;
+ }
+ }
+
+ // We search for the first paragraph with same label
+ // that is not more deeply nested.
+ Paragraph & prev_par = *prev_it;
+ depth_type const prev_depth = getDepth(prev_it);
+ if (labeltype == prev_par.layout().labeltype) {
+ if (prev_depth < min_depth)
+ return prev_par.itemdepth + 1;
+ if (prev_depth == min_depth)
+ return prev_par.itemdepth;
+ }
+ min_depth = min(min_depth, prev_depth);
+ // small optimization: if we are at depth 0, we won't
+ // find anything else
+ if (prev_depth == 0)
+ return 0;
+ }
+}
+
+
+static bool needEnumCounterReset(ParIterator const & it)
+{
+ Paragraph const & par = *it;
+ LASSERT(par.layout().labeltype == LABEL_ENUMERATE, /**/);
+ depth_type const cur_depth = par.getDepth();
+ ParIterator prev_it = it;
+ while (prev_it.pit()) {
+ --prev_it.top().pit();
+ Paragraph const & prev_par = *prev_it;
+ if (prev_par.getDepth() <= cur_depth)
+ return prev_par.layout().labeltype != LABEL_ENUMERATE;
+ }
+ // start of nested inset: reset
+ return true;
+}
+
+
+// set the label of a paragraph. This includes the counters.
+static void setLabel(Buffer const & buf, ParIterator & it)
+{
+ BufferParams const & bp = buf.masterBuffer()->params();
+ DocumentClass const & textclass = bp.documentClass();
+ Paragraph & par = it.paragraph();
+ Layout const & layout = par.layout();
+ Counters & counters = textclass.counters();
+
+ if (par.params().startOfAppendix()) {
+ // FIXME: only the counter corresponding to toplevel
+ // sectionning should be reset
+ counters.reset();
+ counters.appendix(true);
+ }
+ par.params().appendix(counters.appendix());
+
+ // Compute the item depth of the paragraph
+ par.itemdepth = getItemDepth(it);
+
+ if (layout.margintype == MARGIN_MANUAL) {
+ if (par.params().labelWidthString().empty())
+ par.params().labelWidthString(par.translateIfPossible(layout.labelstring(), bp));
+ } else {
+ par.params().labelWidthString(docstring());
+ }
+
+ switch(layout.labeltype) {
+ case LABEL_COUNTER:
+ if (layout.toclevel <= bp.secnumdepth
+ && (layout.latextype != LATEX_ENVIRONMENT
+ || isFirstInSequence(it.pit(), it.plist()))) {
+ counters.step(layout.counter);
+ par.params().labelString(
+ par.expandLabel(layout, bp));
+ } else
+ par.params().labelString(docstring());
+ break;
+
+ case LABEL_ITEMIZE: {
+ // At some point of time we should do something more
+ // clever here, like:
+ // par.params().labelString(
+ // bp.user_defined_bullet(par.itemdepth).getText());
+ // for now, use a simple hardcoded label
+ docstring itemlabel;
+ switch (par.itemdepth) {
+ case 0:
+ itemlabel = char_type(0x2022);
+ break;
+ case 1:
+ itemlabel = char_type(0x2013);
+ break;
+ case 2:
+ itemlabel = char_type(0x2217);
+ break;
+ case 3:
+ itemlabel = char_type(0x2219); // or 0x00b7
+ break;
+ }
+ par.params().labelString(itemlabel);
+ break;
+ }
+
+ case LABEL_ENUMERATE: {
+ // FIXME: Yes I know this is a really, really! bad solution
+ // (Lgb)
+ docstring enumcounter = from_ascii("enum");
+
+ switch (par.itemdepth) {
+ case 2:
+ enumcounter += 'i';
+ case 1:
+ enumcounter += 'i';
+ case 0:
+ enumcounter += 'i';
+ break;
+ case 3:
+ enumcounter += "iv";
+ break;
+ default:
+ // not a valid enumdepth...
+ break;
+ }
+
+ // Maybe we have to reset the enumeration counter.
+ if (needEnumCounterReset(it))
+ counters.reset(enumcounter);
+
+ counters.step(enumcounter);
+
+ string format;
+
+ switch (par.itemdepth) {
+ case 0:
+ format = N_("\\arabic{enumi}.");
+ break;
+ case 1:
+ format = N_("(\\alph{enumii})");
+ break;
+ case 2:
+ format = N_("\\roman{enumiii}.");
+ break;
+ case 3:
+ format = N_("\\Alph{enumiv}.");
+ break;
+ default:
+ // not a valid enumdepth...
+ break;
+ }
+
+ par.params().labelString(counters.counterLabel(
+ par.translateIfPossible(from_ascii(format), bp)));
+
+ break;
+ }
+
+ case LABEL_SENSITIVE: {
+ string const & type = counters.current_float();
+ docstring full_label;
+ if (type.empty())
+ full_label = buf.B_("Senseless!!! ");
+ else {
+ docstring name = buf.B_(textclass.floats().getType(type).name());
+ if (counters.hasCounter(from_utf8(type))) {
+ counters.step(from_utf8(type));
+ full_label = bformat(from_ascii("%1$s %2$s:"),
+ name,
+ counters.theCounter(from_utf8(type)));
+ } else
+ full_label = bformat(from_ascii("%1$s #:"), name);
+ }
+ par.params().labelString(full_label);
+ break;
+ }
+
+ case LABEL_NO_LABEL:
+ par.params().labelString(docstring());
+ break;
+
+ case LABEL_MANUAL:
+ case LABEL_TOP_ENVIRONMENT:
+ case LABEL_CENTERED_TOP_ENVIRONMENT:
+ case LABEL_STATIC:
+ case LABEL_BIBLIO:
+ par.params().labelString(
+ par.translateIfPossible(layout.labelstring(), bp));
+ break;
+ }
+}
+
+
+void Buffer::updateLabels(ParIterator & parit) const
+{
+ 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.
+ parit.text()->setMacrocontextPosition(parit);
+
+ depth_type maxdepth = 0;
+ pit_type const lastpit = parit.lastpit();
+ for ( ; parit.pit() <= lastpit ; ++parit.pit()) {
+ // reduce depth if necessary
+ parit->params().depth(min(parit->params().depth(), maxdepth));
+ maxdepth = parit->getMaxDepthAfter();
+
+ // set the counter for this paragraph
+ setLabel(*this, parit);
+
+ // Now the insets
+ InsetList::const_iterator iit = parit->insetList().begin();
+ InsetList::const_iterator end = parit->insetList().end();
+ for (; iit != end; ++iit) {
+ parit.pos() = iit->pos;
+ iit->inset->updateLabels(parit);
+ }
+ }
+}
+
} // namespace lyx