mirror of
https://git.lyx.org/repos/lyx.git
synced 2024-12-23 21:40:19 +00:00
Better XHTML output for InsetRef.
The idea here is to implement something like \refstepcounter for LyX. We do this by tracking the "active" counter in Counters.cpp. We also have to track when we go in and out of environments to which counters are local, and so on and so forth. This all gets done in updateLabels(), but only if we are producing output, which is why I added the output boolean a while ago. I expect there are bugs in here, though it seems to work pretty well with the documents I've tested. git-svn-id: svn://svn.lyx.org/lyx/lyx-devel/trunk@33108 a592a061-630c-0410-9148-cb99ea01b6c8
This commit is contained in:
parent
be7d7df5a3
commit
22dc345e8c
@ -1,7 +1,6 @@
|
||||
TODO:
|
||||
1. The counter patch, and better output for InsetRef.
|
||||
2. CSS needs work in several places, mostly floats. Maybe check elyxer on that.
|
||||
3. MathML
|
||||
1. CSS needs work in several places, mostly floats. Maybe check elyxer on that.
|
||||
2. MathML
|
||||
|
||||
|
||||
These insets work but still need work:
|
||||
@ -26,9 +25,6 @@ These insets work but still need work:
|
||||
InsetNomencl and InsetPrintNomencl: Do not work at all yet, but would be easy to do.
|
||||
First, Nomencl would need to go to the TOC, which it should do anyway. Then just
|
||||
do as for TOC and Index, more or less.
|
||||
InsetRef: At present, we just use the label name as associated text, and put it
|
||||
into square brackets. It'd be nice to be able to do more, but for that we'd need to
|
||||
associate counters with the labels, and we don't have that yet.
|
||||
InsetTabular: Works reasonably well, but we don't do anything with any of the
|
||||
arguments provided for longtable. There are probably other limitations, too,
|
||||
since I'm very much not an expert with tables.
|
||||
|
@ -3628,7 +3628,7 @@ static bool needEnumCounterReset(ParIterator const & it)
|
||||
|
||||
|
||||
// set the label of a paragraph. This includes the counters.
|
||||
void Buffer::setLabel(ParIterator & it) const
|
||||
void Buffer::setLabel(ParIterator & it, bool for_output) const
|
||||
{
|
||||
BufferParams const & bp = this->masterBuffer()->params();
|
||||
DocumentClass const & textclass = bp.documentClass();
|
||||
@ -3660,7 +3660,7 @@ void Buffer::setLabel(ParIterator & it) const
|
||||
if (layout.toclevel <= bp.secnumdepth
|
||||
&& (layout.latextype != LATEX_ENVIRONMENT
|
||||
|| it.text()->isFirstInSequence(it.pit()))) {
|
||||
counters.step(layout.counter);
|
||||
counters.step(layout.counter, for_output);
|
||||
par.params().labelString(
|
||||
par.expandLabel(layout, bp));
|
||||
} else
|
||||
@ -3714,7 +3714,7 @@ void Buffer::setLabel(ParIterator & it) const
|
||||
// Maybe we have to reset the enumeration counter.
|
||||
if (needEnumCounterReset(it))
|
||||
counters.reset(enumcounter);
|
||||
counters.step(enumcounter);
|
||||
counters.step(enumcounter, for_output);
|
||||
|
||||
string const & lang = par.getParLanguage(bp)->code();
|
||||
par.params().labelString(counters.theCounter(enumcounter, lang));
|
||||
@ -3731,7 +3731,7 @@ void Buffer::setLabel(ParIterator & it) const
|
||||
docstring name = this->B_(textclass.floats().getType(type).name());
|
||||
if (counters.hasCounter(from_utf8(type))) {
|
||||
string const & lang = par.getParLanguage(bp)->code();
|
||||
counters.step(from_utf8(type));
|
||||
counters.step(from_utf8(type), for_output);
|
||||
full_label = bformat(from_ascii("%1$s %2$s:"),
|
||||
name,
|
||||
counters.theCounter(from_utf8(type), lang));
|
||||
@ -3774,10 +3774,18 @@ void Buffer::updateLabels(ParIterator & parit, bool out) const
|
||||
parit->params().depth(min(parit->params().depth(), maxdepth));
|
||||
maxdepth = parit->getMaxDepthAfter();
|
||||
|
||||
if (out) {
|
||||
// track the active counters
|
||||
// we have to do this for the master buffer, since the local
|
||||
// buffer isn't tracking anything.
|
||||
masterBuffer()->params().documentClass().counters().
|
||||
setActiveLayout(parit->layout());
|
||||
}
|
||||
|
||||
// set the counter for this paragraph
|
||||
setLabel(parit);
|
||||
setLabel(parit, out);
|
||||
|
||||
// Now the insets
|
||||
// now the insets
|
||||
InsetList::const_iterator iit = parit->insetList().begin();
|
||||
InsetList::const_iterator end = parit->insetList().end();
|
||||
for (; iit != end; ++iit) {
|
||||
|
@ -14,6 +14,7 @@
|
||||
#include <config.h>
|
||||
|
||||
#include "Counters.h"
|
||||
#include "Layout.h"
|
||||
#include "Lexer.h"
|
||||
|
||||
#include "support/convert.h"
|
||||
@ -37,8 +38,8 @@ Counter::Counter()
|
||||
}
|
||||
|
||||
|
||||
Counter::Counter(docstring const & mc, docstring const & ls,
|
||||
docstring const & lsa)
|
||||
Counter::Counter(docstring const & mc, docstring const & ls,
|
||||
docstring const & lsa)
|
||||
: master_(mc), labelstring_(ls), labelstringappendix_(lsa)
|
||||
{
|
||||
reset();
|
||||
@ -226,7 +227,7 @@ int Counters::value(docstring const & ctr) const
|
||||
}
|
||||
|
||||
|
||||
void Counters::step(docstring const & ctr)
|
||||
void Counters::step(docstring const & ctr, bool track_counters)
|
||||
{
|
||||
CounterList::iterator it = counterList_.find(ctr);
|
||||
if (it == counterList_.end()) {
|
||||
@ -236,6 +237,11 @@ void Counters::step(docstring const & ctr)
|
||||
}
|
||||
|
||||
it->second.step();
|
||||
if (track_counters) {
|
||||
LASSERT(!counter_stack_.empty(), /* */);
|
||||
counter_stack_.pop_back();
|
||||
counter_stack_.push_back(ctr);
|
||||
}
|
||||
it = counterList_.begin();
|
||||
CounterList::iterator const end = counterList_.end();
|
||||
for (; it != end; ++it) {
|
||||
@ -255,6 +261,9 @@ void Counters::reset()
|
||||
CounterList::iterator const end = counterList_.end();
|
||||
for (; it != end; ++it)
|
||||
it->second.reset();
|
||||
counter_stack_.clear();
|
||||
counter_stack_.push_back(from_ascii(""));
|
||||
layout_stack_.push_back(0);
|
||||
}
|
||||
|
||||
|
||||
@ -530,4 +539,65 @@ docstring Counters::counterLabel(docstring const & format,
|
||||
}
|
||||
|
||||
|
||||
docstring Counters::currentCounter() const
|
||||
{
|
||||
LASSERT(!counter_stack_.empty(), /* */);
|
||||
return counter_stack_.back();
|
||||
}
|
||||
|
||||
|
||||
void Counters::setActiveLayout(Layout const & lay)
|
||||
{
|
||||
LASSERT(!layout_stack_.empty(), return);
|
||||
Layout const * const lastlay = layout_stack_.back();
|
||||
// we want to check whether the layout has changed and, if so,
|
||||
// whether we are coming out of or going into an environment.
|
||||
if (!lastlay) {
|
||||
layout_stack_.pop_back();
|
||||
layout_stack_.push_back(&lay);
|
||||
if (lay.isEnvironment())
|
||||
beginEnvironment();
|
||||
} else if (lastlay->name() != lay.name()) {
|
||||
layout_stack_.pop_back();
|
||||
layout_stack_.push_back(&lay);
|
||||
if (lastlay->isEnvironment()) {
|
||||
// we are coming out of an environment
|
||||
// LYXERR0("Out: " << lastlay->name());
|
||||
endEnvironment();
|
||||
}
|
||||
if (lay.isEnvironment()) {
|
||||
// we are going into a new environment
|
||||
// LYXERR0("In: " << lay.name());
|
||||
beginEnvironment();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Counters::beginEnvironment()
|
||||
{
|
||||
docstring cnt = counter_stack_.back();
|
||||
counter_stack_.push_back(cnt);
|
||||
deque<docstring>::const_iterator it = counter_stack_.begin();
|
||||
deque<docstring>::const_iterator en = counter_stack_.end();
|
||||
// docstring d;
|
||||
// for (; it != en; ++it)
|
||||
// d += " --> " + *it;
|
||||
// LYXERR0(counter_stack_.size() << ": " << d);
|
||||
}
|
||||
|
||||
|
||||
void Counters::endEnvironment()
|
||||
{
|
||||
LASSERT(!counter_stack_.empty(), return);
|
||||
counter_stack_.pop_back();
|
||||
deque<docstring>::const_iterator it = counter_stack_.begin();
|
||||
deque<docstring>::const_iterator en = counter_stack_.end();
|
||||
// docstring d;
|
||||
// for (; it != en; ++it)
|
||||
// d += " --> " + *it;
|
||||
// LYXERR0(counter_stack_.size() << ": " << d);
|
||||
}
|
||||
|
||||
|
||||
} // namespace lyx
|
||||
|
@ -18,11 +18,13 @@
|
||||
#include "support/docstring.h"
|
||||
|
||||
#include <map>
|
||||
#include <deque>
|
||||
#include <vector>
|
||||
|
||||
|
||||
namespace lyx {
|
||||
|
||||
class Layout;
|
||||
class Lexer;
|
||||
|
||||
/// This represents a single counter.
|
||||
@ -111,10 +113,10 @@ public:
|
||||
int value(docstring const & ctr) const;
|
||||
/// Increment by one counter named by arg, and zeroes slave
|
||||
/// counter(s) for which it is the master.
|
||||
/** Sub-slaves not zeroed! That happens at slave's first step
|
||||
* 0->1. Seems to be sufficient.
|
||||
*/
|
||||
void step(docstring const & ctr);
|
||||
/// Sub-slaves are not zeroed! That happens at slave's first
|
||||
/// step 0->1. Seems to be sufficient.
|
||||
/// \param for_output: whether to track the counters
|
||||
void step(docstring const & ctr, bool track_counters = false);
|
||||
/// Reset all counters.
|
||||
void reset();
|
||||
/// Reset counters matched by match string.
|
||||
@ -146,8 +148,30 @@ public:
|
||||
bool isSubfloat() const { return subfloat_; }
|
||||
/// Set the state variable indicating whether we are in a subfloat.
|
||||
void isSubfloat(bool s) { subfloat_ = s; }
|
||||
|
||||
/// \name refstepcounter
|
||||
// @{
|
||||
/// The currently active counter, so far as references go.
|
||||
/// We're trying to track \refstepcounter in LaTeX, more or less.
|
||||
/// Note that this may be empty.
|
||||
docstring currentCounter() const;
|
||||
/// Called during update labels as we go through various paragraphs,
|
||||
/// to track the layouts as we go through.
|
||||
void setActiveLayout(Layout const & lay);
|
||||
/// Also for updateLabels().
|
||||
/// Call this when entering things like footnotes, where there is now
|
||||
/// no "last layout" and we want to restore the "last layout" on exit.
|
||||
void clearLastLayout() { layout_stack_.push_back(0); }
|
||||
/// Call then when existing things like footnotes.
|
||||
void restoreLastLayout() { layout_stack_.pop_back(); }
|
||||
///
|
||||
void saveLastCounter()
|
||||
{ counter_stack_.push_back(counter_stack_.back()); }
|
||||
///
|
||||
void restoreLastCounter() { counter_stack_.pop_back(); }
|
||||
// @}
|
||||
private:
|
||||
/** expands recusrsively any \\the<counter> macro in the
|
||||
/** expands recursively any \\the<counter> macro in the
|
||||
* labelstring of \c counter. The \c lang code is used to
|
||||
* translate the string.
|
||||
*/
|
||||
@ -162,6 +186,11 @@ private:
|
||||
*/
|
||||
docstring labelItem(docstring const & ctr,
|
||||
docstring const & numbertype) const;
|
||||
/// Used for managing the counter_stack_.
|
||||
// @{
|
||||
void beginEnvironment();
|
||||
void endEnvironment();
|
||||
// @}
|
||||
/// Maps counter (layout) names to actual counters.
|
||||
typedef std::map<docstring, Counter> CounterList;
|
||||
/// Instantiate.
|
||||
@ -172,9 +201,12 @@ private:
|
||||
std::string current_float_;
|
||||
/// Are we in a subfloat?
|
||||
bool subfloat_;
|
||||
/// Used to keep track of active counters.
|
||||
std::deque<docstring> counter_stack_;
|
||||
/// Same, but for last layout.
|
||||
std::deque<Layout const *> layout_stack_;
|
||||
};
|
||||
|
||||
|
||||
} // namespace lyx
|
||||
|
||||
#endif
|
||||
|
@ -314,6 +314,10 @@ void InsetCaption::updateLabels(ParIterator const & it, bool out)
|
||||
string const & lang = it.paragraph().getParLanguage(master.params())->code();
|
||||
Counters & cnts = tclass.counters();
|
||||
string const & type = cnts.current_float();
|
||||
if (out) {
|
||||
// counters are local to the caption
|
||||
cnts.saveLastCounter();
|
||||
}
|
||||
// Memorize type for addToToc().
|
||||
type_ = type;
|
||||
if (type.empty())
|
||||
@ -333,7 +337,7 @@ void InsetCaption::updateLabels(ParIterator const & it, bool out)
|
||||
master.B_(tclass.floats().getType(type).name()));
|
||||
}
|
||||
if (cnts.hasCounter(counter)) {
|
||||
cnts.step(counter);
|
||||
cnts.step(counter, out);
|
||||
full_label_ = bformat(from_ascii("%1$s %2$s:"),
|
||||
name,
|
||||
cnts.theCounter(counter, lang));
|
||||
@ -343,6 +347,8 @@ void InsetCaption::updateLabels(ParIterator const & it, bool out)
|
||||
|
||||
// Do the real work now.
|
||||
InsetText::updateLabels(it, out);
|
||||
if (out)
|
||||
cnts.restoreLastCounter();
|
||||
}
|
||||
|
||||
|
||||
|
@ -199,6 +199,10 @@ void InsetFloat::updateLabels(ParIterator const & it, bool out)
|
||||
{
|
||||
Counters & cnts =
|
||||
buffer().masterBuffer()->params().documentClass().counters();
|
||||
if (out) {
|
||||
// counters are local to the float
|
||||
cnts.saveLastCounter();
|
||||
}
|
||||
string const saveflt = cnts.current_float();
|
||||
bool const savesubflt = cnts.isSubfloat();
|
||||
|
||||
@ -217,6 +221,8 @@ void InsetFloat::updateLabels(ParIterator const & it, bool out)
|
||||
|
||||
//reset afterwards
|
||||
cnts.current_float(saveflt);
|
||||
if (out)
|
||||
cnts.restoreLastCounter();
|
||||
cnts.isSubfloat(savesubflt);
|
||||
}
|
||||
|
||||
|
@ -40,16 +40,22 @@ void InsetFoot::updateLabels(ParIterator const & it, bool out)
|
||||
{
|
||||
BufferParams const & bp = buffer().masterBuffer()->params();
|
||||
Counters & cnts = bp.documentClass().counters();
|
||||
if (out) {
|
||||
// the footnote counter is local to this inset
|
||||
cnts.saveLastCounter();
|
||||
}
|
||||
Paragraph const & outer = it.paragraph();
|
||||
InsetLayout const & il = getLayout();
|
||||
docstring const & count = il.counter();
|
||||
if (!outer.layout().intitle && cnts.hasCounter(count)) {
|
||||
cnts.step(count);
|
||||
cnts.step(count, out);
|
||||
custom_label_= translateIfPossible(il.labelstring())
|
||||
+ ' ' + cnts.theCounter(count, outer.getParLanguage(bp)->code());
|
||||
setLabel(custom_label_);
|
||||
}
|
||||
InsetCollapsable::updateLabels(it, out);
|
||||
if (out)
|
||||
cnts.restoreLastCounter();
|
||||
}
|
||||
|
||||
|
||||
|
@ -16,17 +16,20 @@
|
||||
|
||||
#include "buffer_funcs.h"
|
||||
#include "Buffer.h"
|
||||
#include "BufferParams.h"
|
||||
#include "BufferView.h"
|
||||
#include "CutAndPaste.h"
|
||||
#include "DispatchResult.h"
|
||||
#include "FuncRequest.h"
|
||||
#include "FuncStatus.h"
|
||||
#include "InsetIterator.h"
|
||||
#include "Language.h"
|
||||
#include "LyXFunc.h"
|
||||
#include "output_xhtml.h"
|
||||
#include "ParIterator.h"
|
||||
#include "sgml.h"
|
||||
#include "Text.h"
|
||||
#include "TextClass.h"
|
||||
#include "TocBackend.h"
|
||||
|
||||
#include "frontends/alert.h"
|
||||
@ -105,7 +108,7 @@ docstring InsetLabel::screenLabel() const
|
||||
}
|
||||
|
||||
|
||||
void InsetLabel::updateLabels(ParIterator const &, bool)
|
||||
void InsetLabel::updateLabels(ParIterator const & par, bool out)
|
||||
{
|
||||
docstring const & label = getParam("name");
|
||||
if (buffer().insetLabel(label)) {
|
||||
@ -115,6 +118,18 @@ void InsetLabel::updateLabels(ParIterator const &, bool)
|
||||
}
|
||||
buffer().setInsetLabel(label, this);
|
||||
screen_label_ = label;
|
||||
|
||||
if (out) {
|
||||
// save info on the active counter
|
||||
Counters const & cnts =
|
||||
buffer().masterBuffer()->params().documentClass().counters();
|
||||
active_counter_ = cnts.currentCounter();
|
||||
Language const * lang = par->getParLanguage(buffer().params());
|
||||
if (lang && !active_counter_.empty())
|
||||
counter_value_ = cnts.theCounter(active_counter_, lang->code());
|
||||
else
|
||||
counter_value_ = _("(unknown)");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -17,6 +17,8 @@
|
||||
|
||||
namespace lyx {
|
||||
|
||||
class Counter;
|
||||
|
||||
class InsetLabel : public InsetCommand {
|
||||
public:
|
||||
///
|
||||
@ -60,6 +62,10 @@ public:
|
||||
void updateCommand(docstring const & new_label, bool updaterefs = true);
|
||||
///
|
||||
bool getStatus(Cursor & cur, FuncRequest const & cmd, FuncStatus & status) const;
|
||||
///
|
||||
docstring const & activeCounter() const { return active_counter_; }
|
||||
///
|
||||
docstring const & counterValue() const { return counter_value_; }
|
||||
protected:
|
||||
///
|
||||
void doDispatch(Cursor & cur, FuncRequest & cmd);
|
||||
@ -68,6 +74,10 @@ private:
|
||||
Inset * clone() const { return new InsetLabel(*this); }
|
||||
///
|
||||
docstring screen_label_;
|
||||
///
|
||||
docstring active_counter_;
|
||||
///
|
||||
docstring counter_value_;
|
||||
};
|
||||
|
||||
|
||||
|
@ -15,6 +15,7 @@
|
||||
#include "Cursor.h"
|
||||
#include "DispatchResult.h"
|
||||
#include "FuncRequest.h"
|
||||
#include "InsetLabel.h"
|
||||
#include "LaTeXFeatures.h"
|
||||
#include "LyXFunc.h"
|
||||
#include "OutputParams.h"
|
||||
@ -118,13 +119,37 @@ int InsetRef::docbook(odocstream & os, OutputParams const & runparams) const
|
||||
|
||||
docstring InsetRef::xhtml(XHTMLStream & xs, OutputParams const &) const
|
||||
{
|
||||
docstring const & ref = getParam("reference");
|
||||
InsetLabel const * il = buffer().insetLabel(ref);
|
||||
string const & cmd = params().getCmdName();
|
||||
docstring display_string;
|
||||
|
||||
if (il && !il->counterValue().empty()) {
|
||||
// Try to construct a label from the InsetLabel we reference.
|
||||
docstring const & cntr = il->activeCounter();
|
||||
docstring const & value = il->counterValue();
|
||||
if (cmd == "ref")
|
||||
display_string = value;
|
||||
else if (cmd == "vref")
|
||||
display_string = bformat(from_ascii("%1$s on page ##"), value);
|
||||
else if (cmd == "pageref" || cmd == "vpageref")
|
||||
display_string = _("on page ##");
|
||||
else if (cmd == "eqref")
|
||||
display_string = bformat(from_ascii("equation (%1$s)"), value);
|
||||
else { // "prettyref"
|
||||
docstring cntrname = translateIfPossible(cntr);
|
||||
// FIXME Use the label string, if we have it. Otherwise, do this.
|
||||
display_string = bformat(from_ascii("%1$s %2$s"), cntrname, value);
|
||||
}
|
||||
} else
|
||||
display_string = ref;
|
||||
|
||||
// FIXME What we'd really like to do is to be able to output some
|
||||
// appropriate sort of text here. But to do that, we need to associate
|
||||
// some sort of counter with the label, and we don't have that yet.
|
||||
docstring const ref = html::cleanAttr(getParam("reference"));
|
||||
string const attr = "href=\"#" + to_utf8(ref) + "\"";
|
||||
string const attr = "href=\"#" + html::cleanAttr(to_utf8(ref)) + "\"";
|
||||
xs << html::StartTag("a", attr);
|
||||
xs << ref;
|
||||
xs << display_string;
|
||||
xs << html::EndTag("a");
|
||||
return docstring();
|
||||
}
|
||||
|
@ -504,7 +504,7 @@ docstring InsetText::insetAsXHTML(XHTMLStream & xs, OutputParams const & runpara
|
||||
if ((opts & WriteLabel) && !il.counter().empty()) {
|
||||
BufferParams const & bp = buffer().masterBuffer()->params();
|
||||
Counters & cntrs = bp.documentClass().counters();
|
||||
cntrs.step(il.counter());
|
||||
cntrs.step(il.counter(), true);
|
||||
// FIXME: translate to paragraph language
|
||||
if (!il.htmllabel().empty()) {
|
||||
docstring const lbl =
|
||||
@ -653,10 +653,28 @@ void InsetText::updateLabels(ParIterator const & it, bool out)
|
||||
ParIterator it2 = it;
|
||||
it2.forwardPos();
|
||||
LASSERT(&it2.inset() == this && it2.pit() == 0, return);
|
||||
if (producesOutput())
|
||||
if (producesOutput()) {
|
||||
// FIXME We only want to do this, in fact, for some insets.
|
||||
// But we'll need layout info for that.
|
||||
InsetLayout const & il = getLayout();
|
||||
bool const save_layouts = out && il.htmlisblock();
|
||||
Counters & cnt = buffer().masterBuffer()->params().documentClass().counters();
|
||||
if (save_layouts) {
|
||||
// LYXERR0("Entering " << name());
|
||||
cnt.clearLastLayout();
|
||||
// FIXME cnt.saveLastCounter()?
|
||||
}
|
||||
buffer().updateLabels(it2, out);
|
||||
else {
|
||||
if (save_layouts) {
|
||||
// LYXERR0("Exiting " << name());
|
||||
cnt.restoreLastLayout();
|
||||
// FIXME cnt.restoreLastCounter()?
|
||||
}
|
||||
} else {
|
||||
DocumentClass const & tclass = buffer().masterBuffer()->params().documentClass();
|
||||
// Note that we do not need to call:
|
||||
// tclass.counters().clearLastLayout()
|
||||
// since we are saving and restoring the existing counters, etc.
|
||||
Counters const savecnt = tclass.counters();
|
||||
buffer().updateLabels(it2, out);
|
||||
tclass.counters() = savecnt;
|
||||
|
@ -119,6 +119,10 @@ void InsetWrap::updateLabels(ParIterator const & it, bool out)
|
||||
setLabel(_("wrap: ") + floatName(params_.type));
|
||||
Counters & cnts =
|
||||
buffer().masterBuffer()->params().documentClass().counters();
|
||||
if (out) {
|
||||
// counters are local to the wrap
|
||||
cnts.saveLastCounter();
|
||||
}
|
||||
string const saveflt = cnts.current_float();
|
||||
|
||||
// Tell to captions what the current float is
|
||||
@ -128,6 +132,8 @@ void InsetWrap::updateLabels(ParIterator const & it, bool out)
|
||||
|
||||
// reset afterwards
|
||||
cnts.current_float(saveflt);
|
||||
if (out)
|
||||
cnts.restoreLastCounter();
|
||||
}
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user