mirror of
https://git.lyx.org/repos/lyx.git
synced 2024-12-22 13:18:28 +00:00
Better construction of the TOC for floats and captions
We introduce TocBuilder for building TOCs that take into account both float insets and their captions. * Floats without caption are shown with their content. * Floats with a caption are shown with their caption, but clicking the entry now correctly moves to the float and not to the caption. * Subsequent captions produce additional entries in the TOC. * Figures and subfigures are correctly ordered in the outliner. * New TOC "senseless" for captions appearing alone (a bit like broken references are still displayed in the menu and outliner). * Disable LFUN_CAPTION_INSERT if there is already a caption in a listing Known issues: * Inconsistent output for includes located inside floats * We should record the end of the float in addition of the beginning for a more accurate cursor -> outliner entry conversion
This commit is contained in:
parent
c02f6bd8a7
commit
94e992c5ed
@ -1005,9 +1005,9 @@ void BiblioInfo::collectCitedEntries(Buffer const & buf)
|
||||
// FIXME We may want to collect these differently, in the first case,
|
||||
// so that we might have them in order of appearance.
|
||||
set<docstring> citekeys;
|
||||
Toc const & toc = buf.tocBackend().toc("citation");
|
||||
Toc::const_iterator it = toc.begin();
|
||||
Toc::const_iterator const en = toc.end();
|
||||
shared_ptr<Toc const> toc = buf.tocBackend().toc("citation");
|
||||
Toc::const_iterator it = toc->begin();
|
||||
Toc::const_iterator const en = toc->end();
|
||||
for (; it != en; ++it) {
|
||||
if (it->str().empty())
|
||||
continue;
|
||||
|
@ -2163,9 +2163,9 @@ void Buffer::getLabelList(vector<docstring> & list) const
|
||||
}
|
||||
|
||||
list.clear();
|
||||
Toc & toc = d->toc_backend.toc("label");
|
||||
TocIterator toc_it = toc.begin();
|
||||
TocIterator end = toc.end();
|
||||
shared_ptr<Toc> toc = d->toc_backend.toc("label");
|
||||
TocIterator toc_it = toc->begin();
|
||||
TocIterator end = toc->end();
|
||||
for (; toc_it != end; ++toc_it) {
|
||||
if (toc_it->depth() == 0)
|
||||
list.push_back(toc_it->str());
|
||||
@ -4461,6 +4461,7 @@ void Buffer::updateBuffer(UpdateScope scope, UpdateType utype) const
|
||||
|
||||
d->bibinfo_cache_valid_ = true;
|
||||
d->cite_labels_valid_ = true;
|
||||
/// FIXME: Perf
|
||||
cbuf.tocBackend().update(utype == OutputUpdate);
|
||||
if (scope == UpdateMaster)
|
||||
cbuf.structureChanged();
|
||||
|
@ -2429,9 +2429,9 @@ void BufferView::gotoLabel(docstring const & label)
|
||||
Buffer const * buf = *it;
|
||||
|
||||
// find label
|
||||
Toc & toc = buf->tocBackend().toc("label");
|
||||
TocIterator toc_it = toc.begin();
|
||||
TocIterator end = toc.end();
|
||||
shared_ptr<Toc> toc = buf->tocBackend().toc("label");
|
||||
TocIterator toc_it = toc->begin();
|
||||
TocIterator end = toc->end();
|
||||
for (; toc_it != end; ++toc_it) {
|
||||
if (label == toc_it->str()) {
|
||||
lyx::dispatch(toc_it->action());
|
||||
|
@ -474,7 +474,7 @@ void Changes::addToToc(DocIterator const & cdit, Buffer const & buffer,
|
||||
if (table_.empty())
|
||||
return;
|
||||
|
||||
Toc & change_list = buffer.tocBackend().toc("change");
|
||||
shared_ptr<Toc> change_list = buffer.tocBackend().toc("change");
|
||||
AuthorList const & author_list = buffer.params().authors();
|
||||
DocIterator dit = cdit;
|
||||
|
||||
@ -500,18 +500,18 @@ void Changes::addToToc(DocIterator const & cdit, Buffer const & buffer,
|
||||
// the end of paragraph symbol from the Punctuation group
|
||||
str.push_back(0x204B);
|
||||
docstring const & author = author_list.get(it->change.author).name();
|
||||
Toc::iterator it = change_list.item(0, author);
|
||||
if (it == change_list.end()) {
|
||||
change_list.push_back(TocItem(dit, 0, author, output_active));
|
||||
change_list.push_back(TocItem(dit, 1, str, output_active,
|
||||
Toc::iterator it = change_list->item(0, author);
|
||||
if (it == change_list->end()) {
|
||||
change_list->push_back(TocItem(dit, 0, author, output_active));
|
||||
change_list->push_back(TocItem(dit, 1, str, output_active,
|
||||
support::wrapParas(str, 4)));
|
||||
continue;
|
||||
}
|
||||
for (++it; it != change_list.end(); ++it) {
|
||||
for (++it; it != change_list->end(); ++it) {
|
||||
if (it->depth() == 0 && it->str() != author)
|
||||
break;
|
||||
}
|
||||
change_list.insert(it, TocItem(dit, 1, str, output_active,
|
||||
change_list->insert(it, TocItem(dit, 1, str, output_active,
|
||||
support::wrapParas(str, 4)));
|
||||
}
|
||||
}
|
||||
|
@ -6,6 +6,7 @@
|
||||
* \author Jean-Marc Lasgouttes
|
||||
* \author Angus Leeming
|
||||
* \author Abdelrazak Younes
|
||||
* \author Guillaume Munch
|
||||
*
|
||||
* Full author contact details are available in file CREDITS.
|
||||
*/
|
||||
@ -57,18 +58,6 @@ int TocItem::id() const
|
||||
}
|
||||
|
||||
|
||||
int TocItem::depth() const
|
||||
{
|
||||
return depth_;
|
||||
}
|
||||
|
||||
|
||||
docstring const & TocItem::str() const
|
||||
{
|
||||
return str_;
|
||||
}
|
||||
|
||||
|
||||
docstring const & TocItem::tooltip() const
|
||||
{
|
||||
return tooltip_.empty() ? str_ : tooltip_;
|
||||
@ -81,12 +70,6 @@ docstring const TocItem::asString() const
|
||||
}
|
||||
|
||||
|
||||
DocIterator const & TocItem::dit() const
|
||||
{
|
||||
return dit_;
|
||||
}
|
||||
|
||||
|
||||
FuncRequest TocItem::action() const
|
||||
{
|
||||
string const arg = convert<string>(dit_.paragraph().id())
|
||||
@ -97,97 +80,10 @@ FuncRequest TocItem::action() const
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// TocBackend implementation
|
||||
// Toc implementation
|
||||
//
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
Toc const & TocBackend::toc(string const & type) const
|
||||
{
|
||||
// Is the type already supported?
|
||||
TocList::const_iterator it = tocs_.find(type);
|
||||
LASSERT(it != tocs_.end(), { static Toc dummy; return dummy; });
|
||||
|
||||
return it->second;
|
||||
}
|
||||
|
||||
|
||||
Toc & TocBackend::toc(string const & type)
|
||||
{
|
||||
return tocs_[type];
|
||||
}
|
||||
|
||||
|
||||
bool TocBackend::updateItem(DocIterator const & dit)
|
||||
{
|
||||
if (dit.text()->getTocLevel(dit.pit()) == Layout::NOT_IN_TOC)
|
||||
return false;
|
||||
|
||||
if (toc("tableofcontents").empty()) {
|
||||
// FIXME: should not happen,
|
||||
// a call to TocBackend::update() is missing somewhere
|
||||
LYXERR0("TocBackend::updateItem called but the TOC is empty!");
|
||||
return false;
|
||||
}
|
||||
|
||||
BufferParams const & bufparams = buffer_->params();
|
||||
const int min_toclevel = bufparams.documentClass().min_toclevel();
|
||||
|
||||
TocIterator toc_item = item("tableofcontents", dit);
|
||||
|
||||
docstring tocstring;
|
||||
|
||||
// For each paragraph, traverse its insets and let them add
|
||||
// their toc items
|
||||
Paragraph & par = toc_item->dit_.paragraph();
|
||||
InsetList::const_iterator it = par.insetList().begin();
|
||||
InsetList::const_iterator end = par.insetList().end();
|
||||
for (; it != end; ++it) {
|
||||
Inset & inset = *it->inset;
|
||||
if (inset.lyxCode() == ARG_CODE) {
|
||||
if (!tocstring.empty())
|
||||
break;
|
||||
Paragraph const & inset_par =
|
||||
*static_cast<InsetArgument&>(inset).paragraphs().begin();
|
||||
if (!par.labelString().empty())
|
||||
tocstring = par.labelString() + ' ';
|
||||
tocstring += inset_par.asString(AS_STR_INSETS);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int const toclevel = toc_item->dit_.text()->getTocLevel(toc_item->dit_.pit());
|
||||
if (toclevel != Layout::NOT_IN_TOC && toclevel >= min_toclevel
|
||||
&& tocstring.empty())
|
||||
tocstring = par.asString(AS_STR_LABEL | AS_STR_INSETS);
|
||||
|
||||
const_cast<TocItem &>(*toc_item).str_ = tocstring;
|
||||
|
||||
buffer_->updateTocItem("tableofcontents", dit);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void TocBackend::update(bool output_active)
|
||||
{
|
||||
tocs_.clear();
|
||||
if (!buffer_->isInternal()) {
|
||||
DocIterator dit;
|
||||
buffer_->inset().addToToc(dit, output_active);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
TocIterator TocBackend::item(string const & type,
|
||||
DocIterator const & dit) const
|
||||
{
|
||||
TocList::const_iterator toclist_it = tocs_.find(type);
|
||||
// Is the type supported?
|
||||
// We will try to make the best of it in release mode
|
||||
LASSERT(toclist_it != tocs_.end(), toclist_it = tocs_.begin());
|
||||
return toclist_it->second.item(dit);
|
||||
}
|
||||
|
||||
|
||||
TocIterator Toc::item(DocIterator const & dit) const
|
||||
{
|
||||
TocIterator last = begin();
|
||||
@ -233,13 +129,184 @@ Toc::iterator Toc::item(int depth, docstring const & str)
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// TocBuilder implementation
|
||||
//
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
TocBuilder::TocBuilder(shared_ptr<Toc> toc)
|
||||
: toc_(toc ? toc : make_shared<Toc>()),
|
||||
stack_()
|
||||
{
|
||||
LATTEST(toc);
|
||||
}
|
||||
|
||||
void TocBuilder::pushItem(DocIterator const & dit, docstring const & s,
|
||||
bool output_active, bool is_captioned)
|
||||
{
|
||||
toc_->push_back(TocItem(dit, stack_.size(), s, output_active));
|
||||
frame f = {
|
||||
toc_->size() - 1, //pos
|
||||
is_captioned, //is_captioned
|
||||
};
|
||||
stack_.push(f);
|
||||
}
|
||||
|
||||
void TocBuilder::captionItem(DocIterator const & dit, docstring const & s,
|
||||
bool output_active)
|
||||
{
|
||||
if (!stack_.empty() && !stack_.top().is_captioned) {
|
||||
// The float we entered has not yet been assigned a caption.
|
||||
// Assign the caption string to it.
|
||||
(*toc_)[stack_.top().pos].str(s);
|
||||
stack_.top().is_captioned = true;
|
||||
} else {
|
||||
// This is a new entry.
|
||||
pop();
|
||||
pushItem(dit, s, output_active, true);
|
||||
}
|
||||
}
|
||||
|
||||
void TocBuilder::pop()
|
||||
{
|
||||
if (!stack_.empty())
|
||||
stack_.pop();
|
||||
}
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// TocBuilderStore implementation
|
||||
//
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
shared_ptr<TocBuilder> TocBuilderStore::get(string const & type,
|
||||
shared_ptr<Toc> toc)
|
||||
{
|
||||
map_t::const_iterator it = map_.find(type);
|
||||
if (it == map_.end()) {
|
||||
it = map_.insert(std::make_pair(type,
|
||||
make_shared<TocBuilder>(toc))).first;
|
||||
}
|
||||
return it->second;
|
||||
}
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// TocBackend implementation
|
||||
//
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
shared_ptr<Toc const> TocBackend::toc(string const & type) const
|
||||
{
|
||||
// Is the type already supported?
|
||||
TocList::const_iterator it = tocs_.find(type);
|
||||
LASSERT(it != tocs_.end(), { return make_shared<Toc>(); });
|
||||
return it->second;
|
||||
}
|
||||
|
||||
|
||||
shared_ptr<Toc> TocBackend::toc(string const & type)
|
||||
{
|
||||
TocList::const_iterator it = tocs_.find(type);
|
||||
if (it == tocs_.end()) {
|
||||
it = tocs_.insert(std::make_pair(type, make_shared<Toc>())).first;
|
||||
}
|
||||
return it->second;
|
||||
}
|
||||
|
||||
|
||||
shared_ptr<TocBuilder> TocBackend::builder(string const & type)
|
||||
{
|
||||
return builders_.get(type, toc(type));
|
||||
}
|
||||
|
||||
|
||||
bool TocBackend::updateItem(DocIterator const & dit)
|
||||
{
|
||||
if (dit.text()->getTocLevel(dit.pit()) == Layout::NOT_IN_TOC)
|
||||
return false;
|
||||
|
||||
if (toc("tableofcontents")->empty()) {
|
||||
// FIXME: should not happen,
|
||||
// a call to TocBackend::update() is missing somewhere
|
||||
LYXERR0("TocBackend::updateItem called but the TOC is empty!");
|
||||
return false;
|
||||
}
|
||||
|
||||
BufferParams const & bufparams = buffer_->params();
|
||||
const int min_toclevel = bufparams.documentClass().min_toclevel();
|
||||
|
||||
TocIterator toc_item = item("tableofcontents", dit);
|
||||
|
||||
docstring tocstring;
|
||||
|
||||
// For each paragraph, traverse its insets and let them add
|
||||
// their toc items
|
||||
Paragraph & par = toc_item->dit_.paragraph();
|
||||
InsetList::const_iterator it = par.insetList().begin();
|
||||
InsetList::const_iterator end = par.insetList().end();
|
||||
for (; it != end; ++it) {
|
||||
Inset & inset = *it->inset;
|
||||
if (inset.lyxCode() == ARG_CODE) {
|
||||
if (!tocstring.empty())
|
||||
break;
|
||||
Paragraph const & inset_par =
|
||||
*static_cast<InsetArgument&>(inset).paragraphs().begin();
|
||||
if (!par.labelString().empty())
|
||||
tocstring = par.labelString() + ' ';
|
||||
tocstring += inset_par.asString(AS_STR_INSETS);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int const toclevel = toc_item->dit_.text()->getTocLevel(toc_item->dit_.pit());
|
||||
if (toclevel != Layout::NOT_IN_TOC && toclevel >= min_toclevel
|
||||
&& tocstring.empty())
|
||||
tocstring = par.asString(AS_STR_LABEL | AS_STR_INSETS);
|
||||
|
||||
const_cast<TocItem &>(*toc_item).str_ = tocstring;
|
||||
|
||||
buffer_->updateTocItem("tableofcontents", dit);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void TocBackend::update(bool output_active)
|
||||
{
|
||||
for (TocList::iterator it = tocs_.begin(); it != tocs_.end(); ++it)
|
||||
it->second->clear();
|
||||
tocs_.clear();
|
||||
builders_.clear();
|
||||
if (!buffer_->isInternal()) {
|
||||
DocIterator dit;
|
||||
buffer_->inset().addToToc(dit, output_active);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
TocIterator TocBackend::item(string const & type,
|
||||
DocIterator const & dit) const
|
||||
{
|
||||
TocList::const_iterator toclist_it = tocs_.find(type);
|
||||
// Is the type supported?
|
||||
// We will try to make the best of it in release mode
|
||||
LASSERT(toclist_it != tocs_.end(), toclist_it = tocs_.begin());
|
||||
return toclist_it->second->item(dit);
|
||||
}
|
||||
|
||||
|
||||
void TocBackend::writePlaintextTocList(string const & type,
|
||||
odocstringstream & os, size_t max_length) const
|
||||
{
|
||||
TocList::const_iterator cit = tocs_.find(type);
|
||||
if (cit != tocs_.end()) {
|
||||
TocIterator ccit = cit->second.begin();
|
||||
TocIterator end = cit->second.end();
|
||||
TocIterator ccit = cit->second->begin();
|
||||
TocIterator end = cit->second->end();
|
||||
for (; ccit != end; ++ccit) {
|
||||
os << ccit->asString() << from_utf8("\n");
|
||||
if (os.str().size() > max_length)
|
||||
|
114
src/TocBackend.h
114
src/TocBackend.h
@ -7,6 +7,7 @@
|
||||
* \author Jean-Marc Lasgouttes
|
||||
* \author Angus Leeming
|
||||
* \author Abdelrazak Younes
|
||||
* \author Guillaume Munch
|
||||
*
|
||||
* Full author contact details are available in file CREDITS.
|
||||
*/
|
||||
@ -16,10 +17,12 @@
|
||||
|
||||
#include "DocIterator.h"
|
||||
|
||||
#include "support/shared_ptr.h"
|
||||
#include "support/strfwd.h"
|
||||
|
||||
#include <map>
|
||||
#include <vector>
|
||||
#include <stack>
|
||||
#include <string>
|
||||
|
||||
|
||||
@ -28,6 +31,34 @@ namespace lyx {
|
||||
class Buffer;
|
||||
class FuncRequest;
|
||||
|
||||
|
||||
/* FIXME: toc types are currently identified by strings. It cannot be converted
|
||||
* into an enum because of the user-configurable indexing categories and
|
||||
* the user-definable float types provided by layout files.
|
||||
*
|
||||
* I leave this for documentation purposes for the moment.
|
||||
*
|
||||
enum TocType {
|
||||
TABLE_OF_CONTENTS,//"tableofcontents"
|
||||
CHILD,//"child"
|
||||
GRAPHICS,//"graphics"
|
||||
NOTE,//"note"
|
||||
BRANCH,//"branch"
|
||||
CHANGE,//"change"
|
||||
LABEL,//"label"
|
||||
CITATION,//"citation"
|
||||
EQUATION,//"equation"
|
||||
FOOTNOTE,//"footnote"
|
||||
MARGINAL_NOTE,//"marginalnote"
|
||||
INDEX,//"index", "index:<user-str>" (from interface)
|
||||
NOMENCL,//"nomencl"
|
||||
LISTING,//"listings"
|
||||
FLOAT,//"figure", "table", "algorithm", user-defined (from layout?)
|
||||
SENSELESS,//"senseless"
|
||||
TOC_TYPE_COUNT
|
||||
}
|
||||
*/
|
||||
|
||||
///
|
||||
/**
|
||||
*/
|
||||
@ -51,15 +82,17 @@ public:
|
||||
///
|
||||
int id() const;
|
||||
///
|
||||
int depth() const;
|
||||
int depth() const { return depth_; }
|
||||
///
|
||||
docstring const & str() const;
|
||||
docstring const & str() const { return str_; }
|
||||
///
|
||||
void str(docstring const & s) { str_ = s; }
|
||||
///
|
||||
docstring const & tooltip() const;
|
||||
///
|
||||
docstring const asString() const;
|
||||
///
|
||||
DocIterator const & dit() const;
|
||||
DocIterator const & dit() const { return dit_; }
|
||||
///
|
||||
bool isOutput() const { return output_; }
|
||||
|
||||
@ -95,9 +128,57 @@ public:
|
||||
|
||||
typedef Toc::const_iterator TocIterator;
|
||||
|
||||
|
||||
/// Caption-enabled TOC builders
|
||||
class TocBuilder
|
||||
{
|
||||
public:
|
||||
TocBuilder(shared_ptr<Toc> const toc);
|
||||
/// When entering a float
|
||||
void pushItem(DocIterator const & dit, docstring const & s,
|
||||
bool output_active, bool is_captioned = false);
|
||||
/// When encountering a caption
|
||||
void captionItem(DocIterator const & dit, docstring const & s,
|
||||
bool output_active);
|
||||
/// When exiting a float
|
||||
void pop();
|
||||
private:
|
||||
TocBuilder(){}
|
||||
///
|
||||
struct frame {
|
||||
Toc::size_type const pos;
|
||||
bool is_captioned;
|
||||
};
|
||||
///
|
||||
shared_ptr<Toc> const toc_;
|
||||
///
|
||||
std::stack<frame> stack_;
|
||||
};
|
||||
|
||||
|
||||
/// The ToC list.
|
||||
/// A class and no typedef because we want to forward declare it.
|
||||
class TocList : public std::map<std::string, Toc> {};
|
||||
class TocList : public std::map<std::string, shared_ptr<Toc> >
|
||||
{
|
||||
private:
|
||||
// this can create null pointers
|
||||
using std::map<std::string, shared_ptr<Toc> >::operator[];
|
||||
};
|
||||
|
||||
|
||||
///
|
||||
class TocBuilderStore
|
||||
{
|
||||
public:
|
||||
TocBuilderStore() {};
|
||||
///
|
||||
shared_ptr<TocBuilder> get(std::string const & type, shared_ptr<Toc> toc);
|
||||
///
|
||||
void clear() { map_.clear(); };
|
||||
private:
|
||||
typedef std::map<std::string, shared_ptr<TocBuilder> > map_t;
|
||||
map_t map_;
|
||||
};
|
||||
|
||||
|
||||
///
|
||||
@ -114,15 +195,13 @@ public:
|
||||
void update(bool output_active);
|
||||
/// \return true if the item was updated.
|
||||
bool updateItem(DocIterator const & pit);
|
||||
|
||||
///
|
||||
TocList const & tocs() const { return tocs_; }
|
||||
TocList & tocs() { return tocs_; }
|
||||
|
||||
///
|
||||
Toc const & toc(std::string const & type) const;
|
||||
Toc & toc(std::string const & type);
|
||||
|
||||
/// never null
|
||||
shared_ptr<Toc const> toc(std::string const & type) const;
|
||||
shared_ptr<Toc> toc(std::string const & type);
|
||||
/// nevel null
|
||||
shared_ptr<TocBuilder> builder(std::string const & type);
|
||||
/// Return the first Toc Item before the cursor
|
||||
TocIterator item(
|
||||
std::string const & type, ///< Type of Toc.
|
||||
@ -137,20 +216,11 @@ private:
|
||||
///
|
||||
TocList tocs_;
|
||||
///
|
||||
TocBuilderStore builders_;
|
||||
///
|
||||
Buffer const * buffer_;
|
||||
}; // TocBackend
|
||||
|
||||
inline bool operator==(TocItem const & a, TocItem const & b)
|
||||
{
|
||||
return a.id() == b.id() && a.str() == b.str() && a.depth() == b.depth();
|
||||
}
|
||||
|
||||
|
||||
inline bool operator!=(TocItem const & a, TocItem const & b)
|
||||
{
|
||||
return !(a == b);
|
||||
}
|
||||
|
||||
|
||||
} // namespace lyx
|
||||
|
||||
|
@ -1313,8 +1313,8 @@ void MenuDefinition::expandToc(Buffer const * buf)
|
||||
|
||||
MenuDefinition submenu;
|
||||
if (floatlist.typeExist(cit->first)) {
|
||||
TocIterator ccit = cit->second.begin();
|
||||
TocIterator eend = cit->second.end();
|
||||
TocIterator ccit = cit->second->begin();
|
||||
TocIterator eend = cit->second->end();
|
||||
for (; ccit != eend; ++ccit) {
|
||||
if (0 == ccit->depth()) {// omit subfloats
|
||||
submenu.add(MenuItem(MenuItem::Command,
|
||||
@ -1331,15 +1331,15 @@ void MenuDefinition::expandToc(Buffer const * buf)
|
||||
item.setSubmenu(submenu);
|
||||
add(item);
|
||||
} else {
|
||||
if (cit->second.size() >= 30) {
|
||||
if (cit->second->size() >= 30) {
|
||||
// FIXME: the behaviour of the interface should not change
|
||||
// arbitrarily. Each type should be audited to see if the list
|
||||
// can be optimised like for floats above.
|
||||
FuncRequest f(LFUN_DIALOG_SHOW, "toc " + cit->first);
|
||||
submenu.add(MenuItem(MenuItem::Command, qt_("Open Navigator..."), f));
|
||||
} else {
|
||||
TocIterator ccit = cit->second.begin();
|
||||
TocIterator eend = cit->second.end();
|
||||
TocIterator ccit = cit->second->begin();
|
||||
TocIterator eend = cit->second->end();
|
||||
for (; ccit != eend; ++ccit) {
|
||||
submenu.add(MenuItem(MenuItem::Command,
|
||||
limitStringLength(ccit->str()) + '|',
|
||||
@ -1367,8 +1367,8 @@ void MenuDefinition::expandToc(Buffer const * buf)
|
||||
if (cit == end)
|
||||
LYXERR(Debug::GUI, "No table of contents.");
|
||||
else {
|
||||
if (!cit->second.empty())
|
||||
expandToc2(cit->second, 0, cit->second.size(), 0);
|
||||
if (!cit->second->empty())
|
||||
expandToc2(* cit->second, 0, cit->second->size(), 0);
|
||||
else
|
||||
add(MenuItem(MenuItem::Info, qt_("<Empty Table of Contents>")));
|
||||
}
|
||||
|
@ -364,7 +364,7 @@ void TocModels::reset(BufferView const * bv)
|
||||
iterator mod_it = models_.find(type);
|
||||
if (mod_it == models_.end())
|
||||
mod_it = models_.insert(type, new TocModel(this));
|
||||
mod_it.value()->reset(it->second);
|
||||
mod_it.value()->reset(*it->second);
|
||||
|
||||
// Fill in the names_ model.
|
||||
QString const gui_name = guiName(it->first, bv->buffer().params());
|
||||
|
@ -628,6 +628,8 @@ QString guiName(string const & type, BufferParams const & bp)
|
||||
return qt_("Branches");
|
||||
if (type == "change")
|
||||
return qt_("Changes");
|
||||
if (type == "senseless")
|
||||
return qt_("Senseless");
|
||||
if (prefixIs(type, "index:")) {
|
||||
string const itype = split(type, ':');
|
||||
IndicesList const & indiceslist = bp.indiceslist();
|
||||
|
@ -353,10 +353,10 @@ void InsetBranch::addToToc(DocIterator const & cpit, bool output_active) const
|
||||
DocIterator pit = cpit;
|
||||
pit.push_back(CursorSlice(const_cast<InsetBranch &>(*this)));
|
||||
|
||||
Toc & toc = buffer().tocBackend().toc("branch");
|
||||
shared_ptr<Toc> toc = buffer().tocBackend().toc("branch");
|
||||
docstring str = params_.branch + ": ";
|
||||
text().forOutliner(str, TOC_ENTRY_LENGTH);
|
||||
toc.push_back(TocItem(pit, 0, str, output_active, toolTipText(docstring(), 3, 60)));
|
||||
toc->push_back(TocItem(pit, 0, str, output_active, toolTipText(docstring(), 3, 60)));
|
||||
// Proceed with the rest of the inset.
|
||||
bool const doing_output = output_active && isBranchSelected();
|
||||
InsetCollapsable::addToToc(cpit, doing_output);
|
||||
|
@ -92,18 +92,13 @@ void InsetCaption::setCustomLabel(docstring const & label)
|
||||
|
||||
void InsetCaption::addToToc(DocIterator const & cpit, bool output_active) const
|
||||
{
|
||||
if (floattype_.empty())
|
||||
return;
|
||||
|
||||
string const & type = floattype_.empty() ? "senseless" : floattype_;
|
||||
DocIterator pit = cpit;
|
||||
pit.push_back(CursorSlice(const_cast<InsetCaption &>(*this)));
|
||||
|
||||
Toc & toc = buffer().tocBackend().toc(floattype_);
|
||||
docstring str = full_label_;
|
||||
int length = output_active ? INT_MAX : TOC_ENTRY_LENGTH;
|
||||
text().forOutliner(str, length);
|
||||
toc.push_back(TocItem(pit, is_subfloat_ ? 1 : 0, str, output_active));
|
||||
|
||||
buffer().tocBackend().builder(type)->captionItem(pit, str, output_active);
|
||||
// Proceed with the rest of the inset.
|
||||
InsetText::addToToc(cpit, output_active);
|
||||
}
|
||||
@ -368,7 +363,7 @@ void InsetCaption::updateBuffer(ParIterator const & it, UpdateType utype)
|
||||
}
|
||||
// Memorize type for addToToc().
|
||||
floattype_ = type;
|
||||
if (type.empty())
|
||||
if (type.empty() || type == "senseless")
|
||||
full_label_ = master.B_("Senseless!!! ");
|
||||
else {
|
||||
// FIXME: life would be _much_ simpler if listings was
|
||||
|
@ -339,8 +339,8 @@ void InsetCitation::addToToc(DocIterator const & cpit, bool output_active) const
|
||||
// by both XHTML and plaintext output. So, if we change what goes into the TOC,
|
||||
// then we will also need to change that routine.
|
||||
docstring const tocitem = getParam("key");
|
||||
Toc & toc = buffer().tocBackend().toc("citation");
|
||||
toc.push_back(TocItem(cpit, 0, tocitem, output_active));
|
||||
shared_ptr<Toc> toc = buffer().tocBackend().toc("citation");
|
||||
toc->push_back(TocItem(cpit, 0, tocitem, output_active));
|
||||
}
|
||||
|
||||
|
||||
|
@ -30,6 +30,7 @@
|
||||
#include "output_xhtml.h"
|
||||
#include "ParIterator.h"
|
||||
#include "TextClass.h"
|
||||
#include "TocBackend.h"
|
||||
|
||||
#include "support/debug.h"
|
||||
#include "support/docstream.h"
|
||||
@ -202,6 +203,22 @@ bool InsetFloat::getStatus(Cursor & cur, FuncRequest const & cmd,
|
||||
}
|
||||
|
||||
|
||||
void InsetFloat::addToToc(DocIterator const & cpit, bool output_active) const
|
||||
{
|
||||
string const & type = params().type;
|
||||
DocIterator pit = cpit;
|
||||
pit.push_back(CursorSlice(const_cast<InsetFloat &>(*this)));
|
||||
docstring str;
|
||||
int length = output_active ? INT_MAX : TOC_ENTRY_LENGTH;
|
||||
text().forOutliner(str, length);
|
||||
shared_ptr<TocBuilder> builder = buffer().tocBackend().builder(type);
|
||||
builder->pushItem(pit, str, output_active);
|
||||
// Proceed with the rest of the inset.
|
||||
InsetCollapsable::addToToc(cpit, output_active);
|
||||
builder->pop();
|
||||
}
|
||||
|
||||
|
||||
void InsetFloat::updateBuffer(ParIterator const & it, UpdateType utype)
|
||||
{
|
||||
Counters & cnts =
|
||||
|
@ -18,7 +18,8 @@
|
||||
|
||||
namespace lyx {
|
||||
|
||||
class InsetFloatParams {
|
||||
class InsetFloatParams
|
||||
{
|
||||
public:
|
||||
///
|
||||
InsetFloatParams() : wide(false), sideways(false), subfloat(false) {}
|
||||
@ -99,7 +100,9 @@ private:
|
||||
bool inheritFont() const { return false; }
|
||||
///
|
||||
bool getStatus(Cursor &, FuncRequest const &, FuncStatus &) const;
|
||||
// Update the counters of this inset and of its contents
|
||||
///
|
||||
void addToToc(DocIterator const & di, bool output_active) const;
|
||||
/// Update the counters of this inset and of its contents
|
||||
void updateBuffer(ParIterator const &, UpdateType);
|
||||
///
|
||||
void doDispatch(Cursor & cur, FuncRequest & cmd);
|
||||
|
@ -211,8 +211,8 @@ docstring InsetFloatList::xhtml(XHTMLStream &, OutputParams const & op) const {
|
||||
|
||||
// FIXME Do we need to check if it exists? If so, we need a new
|
||||
// routine in TocBackend to do that.
|
||||
Toc const & toc = buffer().tocBackend().toc(toctype);
|
||||
if (toc.empty())
|
||||
shared_ptr<Toc const> toc = buffer().tocBackend().toc(toctype);
|
||||
if (toc->empty())
|
||||
return docstring();
|
||||
|
||||
// we want to look like a chapter, section, or whatever.
|
||||
@ -249,8 +249,8 @@ docstring InsetFloatList::xhtml(XHTMLStream &, OutputParams const & op) const {
|
||||
<< toclabel
|
||||
<< html::EndTag(tag);
|
||||
|
||||
Toc::const_iterator it = toc.begin();
|
||||
Toc::const_iterator const en = toc.end();
|
||||
Toc::const_iterator it = toc->begin();
|
||||
Toc::const_iterator const en = toc->end();
|
||||
for (; it != en; ++it) {
|
||||
Paragraph const & par = it->dit().innerParagraph();
|
||||
string const attr = "class='lyxtoc-floats lyxtoc-" + toctype + "'";
|
||||
|
@ -79,10 +79,10 @@ void InsetFoot::addToToc(DocIterator const & cpit, bool output_active) const
|
||||
DocIterator pit = cpit;
|
||||
pit.push_back(CursorSlice(const_cast<InsetFoot &>(*this)));
|
||||
|
||||
Toc & toc = buffer().tocBackend().toc("footnote");
|
||||
shared_ptr<Toc> toc = buffer().tocBackend().toc("footnote");
|
||||
docstring str = custom_label_ + ": ";
|
||||
text().forOutliner(str, TOC_ENTRY_LENGTH);
|
||||
toc.push_back(TocItem(pit, 0, str, output_active, toolTipText(docstring(), 3, 60)));
|
||||
toc->push_back(TocItem(pit, 0, str, output_active, toolTipText(docstring(), 3, 60)));
|
||||
// Proceed with the rest of the inset.
|
||||
InsetFootlike::addToToc(cpit, output_active);
|
||||
}
|
||||
|
@ -1031,7 +1031,7 @@ void InsetGraphics::addToToc(DocIterator const & cpit, bool output_active) const
|
||||
{
|
||||
//FIXME UNICODE
|
||||
docstring const str = from_utf8(params_.filename.onlyFileName());
|
||||
buffer().tocBackend().toc("graphics").push_back(TocItem(cpit, 0, str, output_active));
|
||||
buffer().tocBackend().toc("graphics")->push_back(TocItem(cpit, 0, str, output_active));
|
||||
}
|
||||
|
||||
|
||||
|
@ -1137,29 +1137,30 @@ void InsetInclude::addToToc(DocIterator const & cpit, bool output_active) const
|
||||
string caption = p.getParamValue("caption");
|
||||
if (caption.empty())
|
||||
return;
|
||||
Toc & toc = backend.toc("listing");
|
||||
docstring str = convert<docstring>(toc.size() + 1)
|
||||
shared_ptr<Toc> toc = backend.toc("listing");
|
||||
docstring str = convert<docstring>(toc->size() + 1)
|
||||
+ ". " + from_utf8(caption);
|
||||
DocIterator pit = cpit;
|
||||
toc.push_back(TocItem(pit, 0, str, output_active));
|
||||
return;
|
||||
toc->push_back(TocItem(pit, 0, str, output_active));
|
||||
} else {
|
||||
Buffer const * const childbuffer = getChildBuffer();
|
||||
if (!childbuffer)
|
||||
return;
|
||||
|
||||
shared_ptr<Toc> toc = backend.toc("child");
|
||||
docstring str = childbuffer->fileName().displayName();
|
||||
toc->push_back(TocItem(cpit, 0, str, output_active));
|
||||
|
||||
//TocList & toclist = backend.tocs();
|
||||
childbuffer->tocBackend().update(output_active);
|
||||
TocList const & childtoclist = childbuffer->tocBackend().tocs();
|
||||
TocList::const_iterator it = childtoclist.begin();
|
||||
TocList::const_iterator const end = childtoclist.end();
|
||||
for(; it != end; ++it) {
|
||||
shared_ptr<Toc> toc = backend.toc(it->first);
|
||||
toc->insert(toc->end(), it->second->begin(), it->second->end());
|
||||
}
|
||||
}
|
||||
Buffer const * const childbuffer = getChildBuffer();
|
||||
if (!childbuffer)
|
||||
return;
|
||||
|
||||
Toc & toc = backend.toc("child");
|
||||
docstring str = childbuffer->fileName().displayName();
|
||||
toc.push_back(TocItem(cpit, 0, str, output_active));
|
||||
|
||||
TocList & toclist = backend.tocs();
|
||||
childbuffer->tocBackend().update(output_active);
|
||||
TocList const & childtoclist = childbuffer->tocBackend().tocs();
|
||||
TocList::const_iterator it = childtoclist.begin();
|
||||
TocList::const_iterator const end = childtoclist.end();
|
||||
for(; it != end; ++it)
|
||||
toclist[it->first].insert(toclist[it->first].end(),
|
||||
it->second.begin(), it->second.end());
|
||||
}
|
||||
|
||||
|
||||
|
@ -358,7 +358,7 @@ void InsetIndex::addToToc(DocIterator const & cpit, bool output_active) const
|
||||
type += ":" + to_utf8(params_.index);
|
||||
// this is unlikely to be terribly long
|
||||
text().forOutliner(str, INT_MAX);
|
||||
buffer().tocBackend().toc(type).push_back(TocItem(pit, 0, str, output_active));
|
||||
buffer().tocBackend().toc(type)->push_back(TocItem(pit, 0, str, output_active));
|
||||
// Proceed with the rest of the inset.
|
||||
InsetCollapsable::addToToc(cpit, output_active);
|
||||
}
|
||||
@ -689,13 +689,13 @@ docstring InsetPrintIndex::xhtml(XHTMLStream &, OutputParams const & op) const
|
||||
if (bp.use_indices && getParam("type") != from_ascii("idx"))
|
||||
return docstring();
|
||||
|
||||
Toc const & toc = buffer().tocBackend().toc("index");
|
||||
if (toc.empty())
|
||||
shared_ptr<Toc const> toc = buffer().tocBackend().toc("index");
|
||||
if (toc->empty())
|
||||
return docstring();
|
||||
|
||||
// Collect the index entries in a form we can use them.
|
||||
Toc::const_iterator it = toc.begin();
|
||||
Toc::const_iterator const en = toc.end();
|
||||
Toc::const_iterator it = toc->begin();
|
||||
Toc::const_iterator const en = toc->end();
|
||||
vector<IndexEntry> entries;
|
||||
for (; it != en; ++it)
|
||||
if (it->isOutput())
|
||||
|
@ -172,25 +172,25 @@ void InsetLabel::updateBuffer(ParIterator const & par, UpdateType utype)
|
||||
void InsetLabel::addToToc(DocIterator const & cpit, bool output_active) const
|
||||
{
|
||||
docstring const & label = getParam("name");
|
||||
Toc & toc = buffer().tocBackend().toc("label");
|
||||
shared_ptr<Toc> toc = buffer().tocBackend().toc("label");
|
||||
if (buffer().insetLabel(label) != this) {
|
||||
toc.push_back(TocItem(cpit, 0, screen_label_, output_active));
|
||||
return;
|
||||
}
|
||||
toc.push_back(TocItem(cpit, 0, screen_label_, output_active));
|
||||
Buffer::References const & refs = buffer().references(label);
|
||||
Buffer::References::const_iterator it = refs.begin();
|
||||
Buffer::References::const_iterator end = refs.end();
|
||||
for (; it != end; ++it) {
|
||||
DocIterator const ref_pit(it->second);
|
||||
if (it->first->lyxCode() == MATH_REF_CODE)
|
||||
toc.push_back(TocItem(ref_pit, 1,
|
||||
it->first->asInsetMath()->asRefInset()->screenLabel(),
|
||||
output_active));
|
||||
else
|
||||
toc.push_back(TocItem(ref_pit, 1,
|
||||
static_cast<InsetRef *>(it->first)->getTOCString(),
|
||||
output_active));
|
||||
toc->push_back(TocItem(cpit, 0, screen_label_, output_active));
|
||||
} else {
|
||||
toc->push_back(TocItem(cpit, 0, screen_label_, output_active));
|
||||
Buffer::References const & refs = buffer().references(label);
|
||||
Buffer::References::const_iterator it = refs.begin();
|
||||
Buffer::References::const_iterator end = refs.end();
|
||||
for (; it != end; ++it) {
|
||||
DocIterator const ref_pit(it->second);
|
||||
if (it->first->lyxCode() == MATH_REF_CODE)
|
||||
toc->push_back(TocItem(ref_pit, 1,
|
||||
it->first->asInsetMath()->asRefInset()->screenLabel(),
|
||||
output_active));
|
||||
else
|
||||
toc->push_back(TocItem(ref_pit, 1,
|
||||
static_cast<InsetRef *>(it->first)->getTOCString(),
|
||||
output_active));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -349,7 +349,8 @@ bool InsetListings::getStatus(Cursor & cur, FuncRequest const & cmd,
|
||||
status.setEnabled(true);
|
||||
return true;
|
||||
case LFUN_CAPTION_INSERT: {
|
||||
if (params().isInline()) {
|
||||
// the inset outputs at most one caption
|
||||
if (params().isInline() || getCaptionInset()) {
|
||||
status.setEnabled(false);
|
||||
return true;
|
||||
}
|
||||
|
@ -56,10 +56,10 @@ void InsetMarginal::addToToc(DocIterator const & cpit, bool output_active) const
|
||||
DocIterator pit = cpit;
|
||||
pit.push_back(CursorSlice(const_cast<InsetMarginal &>(*this)));
|
||||
|
||||
Toc & toc = buffer().tocBackend().toc("marginalnote");
|
||||
shared_ptr<Toc> toc = buffer().tocBackend().toc("marginalnote");
|
||||
docstring str;
|
||||
text().forOutliner(str, TOC_ENTRY_LENGTH);
|
||||
toc.push_back(TocItem(pit, 0, str, output_active, toolTipText(docstring(), 3, 60)));
|
||||
toc->push_back(TocItem(pit, 0, str, output_active, toolTipText(docstring(), 3, 60)));
|
||||
// Proceed with the rest of the inset.
|
||||
InsetFootlike::addToToc(cpit, output_active);
|
||||
}
|
||||
|
@ -135,7 +135,7 @@ void InsetNomencl::validate(LaTeXFeatures & features) const
|
||||
void InsetNomencl::addToToc(DocIterator const & cpit, bool output_active) const
|
||||
{
|
||||
docstring const str = getParam("symbol");
|
||||
buffer().tocBackend().toc("nomencl").push_back(TocItem(cpit, 0, str, output_active));
|
||||
buffer().tocBackend().toc("nomencl")->push_back(TocItem(cpit, 0, str, output_active));
|
||||
}
|
||||
|
||||
|
||||
@ -190,11 +190,11 @@ typedef map<docstring, NomenclEntry > EntryMap;
|
||||
|
||||
docstring InsetPrintNomencl::xhtml(XHTMLStream &, OutputParams const & op) const
|
||||
{
|
||||
Toc const & toc = buffer().tocBackend().toc("nomencl");
|
||||
shared_ptr<Toc const> toc = buffer().tocBackend().toc("nomencl");
|
||||
|
||||
EntryMap entries;
|
||||
Toc::const_iterator it = toc.begin();
|
||||
Toc::const_iterator const en = toc.end();
|
||||
Toc::const_iterator it = toc->begin();
|
||||
Toc::const_iterator const en = toc->end();
|
||||
for (; it != en; ++it) {
|
||||
DocIterator dit = it->dit();
|
||||
Paragraph const & par = dit.innerParagraph();
|
||||
|
@ -212,11 +212,11 @@ void InsetNote::addToToc(DocIterator const & cpit, bool output_active) const
|
||||
DocIterator pit = cpit;
|
||||
pit.push_back(CursorSlice(const_cast<InsetNote &>(*this)));
|
||||
|
||||
Toc & toc = buffer().tocBackend().toc("note");
|
||||
shared_ptr<Toc> toc = buffer().tocBackend().toc("note");
|
||||
InsetLayout const & il = getLayout();
|
||||
docstring str = translateIfPossible(il.labelstring()) + from_ascii(": ");
|
||||
text().forOutliner(str, TOC_ENTRY_LENGTH);
|
||||
toc.push_back(TocItem(pit, 0, str, output_active, toolTipText(docstring(), 3, 60)));
|
||||
toc->push_back(TocItem(pit, 0, str, output_active, toolTipText(docstring(), 3, 60)));
|
||||
|
||||
// Proceed with the rest of the inset.
|
||||
bool doing_output = output_active && producesOutput();
|
||||
|
@ -312,8 +312,8 @@ void InsetRef::addToToc(DocIterator const & cpit, bool output_active) const
|
||||
|
||||
// It seems that this reference does not point to any valid label.
|
||||
screen_label_ = _("BROKEN: ") + screen_label_;
|
||||
Toc & toc = buffer().tocBackend().toc("label");
|
||||
toc.push_back(TocItem(cpit, 0, screen_label_, output_active));
|
||||
shared_ptr<Toc> toc = buffer().tocBackend().toc("label");
|
||||
toc->push_back(TocItem(cpit, 0, screen_label_, output_active));
|
||||
}
|
||||
|
||||
|
||||
|
@ -235,8 +235,9 @@ docstring InsetTOC::xhtml(XHTMLStream &, OutputParams const & op) const
|
||||
LASSERT(false, return docstring());
|
||||
}
|
||||
|
||||
Toc const & toc = buffer().masterBuffer()->tocBackend().toc(cmd2type(command));
|
||||
if (toc.empty())
|
||||
shared_ptr<Toc const> toc =
|
||||
buffer().masterBuffer()->tocBackend().toc(cmd2type(command));
|
||||
if (toc->empty())
|
||||
return docstring();
|
||||
|
||||
// we'll use our own stream, because we are going to defer everything.
|
||||
@ -264,9 +265,9 @@ docstring InsetTOC::xhtml(XHTMLStream &, OutputParams const & op) const
|
||||
|
||||
// Output of TOC
|
||||
if (use_depth)
|
||||
makeTOCWithDepth(xs, toc, op);
|
||||
makeTOCWithDepth(xs, *toc, op);
|
||||
else
|
||||
makeTOCNoDepth(xs, toc, op);
|
||||
makeTOCNoDepth(xs, *toc, op);
|
||||
|
||||
xs << html::EndTag("div") << html::CR();
|
||||
return ods.str();
|
||||
|
@ -805,7 +805,8 @@ void InsetText::addToToc(DocIterator const & cdit, bool output_active) const
|
||||
void InsetText::iterateForToc(DocIterator const & cdit, bool output_active) const
|
||||
{
|
||||
DocIterator dit = cdit;
|
||||
Toc & toc = buffer().tocBackend().toc("tableofcontents");
|
||||
// Ensure that any document has a table of contents
|
||||
shared_ptr<Toc> toc = buffer().tocBackend().toc("tableofcontents");
|
||||
|
||||
BufferParams const & bufparams = buffer_->params();
|
||||
int const min_toclevel = bufparams.documentClass().min_toclevel();
|
||||
@ -846,8 +847,8 @@ void InsetText::iterateForToc(DocIterator const & cdit, bool output_active) cons
|
||||
} else
|
||||
par.forOutliner(tocstring, length);
|
||||
dit.pos() = 0;
|
||||
toc.push_back(TocItem(dit, toclevel - min_toclevel,
|
||||
tocstring, doing_output, tocstring));
|
||||
toc->push_back(TocItem(dit, toclevel - min_toclevel,
|
||||
tocstring, doing_output, tocstring));
|
||||
}
|
||||
|
||||
// And now the list of changes.
|
||||
|
@ -301,14 +301,14 @@ void InsetMathHull::addToToc(DocIterator const & pit, bool output_active) const
|
||||
return;
|
||||
}
|
||||
|
||||
Toc & toc = buffer().tocBackend().toc("equation");
|
||||
shared_ptr<Toc> toc = buffer().tocBackend().toc("equation");
|
||||
|
||||
for (row_type row = 0; row != nrows(); ++row) {
|
||||
if (!numbered(row))
|
||||
continue;
|
||||
if (label_[row])
|
||||
label_[row]->addToToc(pit, output_active);
|
||||
toc.push_back(TocItem(pit, 0, nicelabel(row), output_active));
|
||||
toc->push_back(TocItem(pit, 0, nicelabel(row), output_active));
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user