The attached is an initial shot at implementing a kind of \refstepcounter facility for LyX. The idea is that we can keep track, as we pass InsetLabels, of which counter is then "active" and use this information in displaying InsetRefs that reference those labels. It works reasonably well, in that we have the right sort of information saved. (Lots of FIXMEs here and there, though.)

The problem is that we really need more information, and I'm not sure if that is going to take too much time. (This is all done via updateLabels().) Suppose e.g. that the active counter is subsection. Then we'd want to display something like \thesection.\thesubsection. But we don't easily know which counters we will need to remember. We don't want to save the whole Counters structure. So the obvious thing is actually to calculate the label as we go by, via Counters::theCounter(), but this is what seems as if it might take too much time. I haven't actually tried doing that yet, as my machine is pretty dang fast, so I'm not sure I'd see the effects. I guess I'm wondering if anyone has a view whether this is worth doing, or whether there's some other way.

If we don't want to do it every time through, then we can give up on the in-LyX display, and I can somehow arrange to do all this only on the updateLabels() run just prior to output, which is what I really want it for anyway.

Cheers,
Richard

Index: src/Counters.h
===================================================================
--- src/Counters.h	(revision 32054)
+++ src/Counters.h	(working copy)
@@ -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.
@@ -146,8 +148,26 @@
 	bool isSubfloat() const { return subfloat_; }
 	/// Set the state variable indicating whether we are in a subfloat.
 	void isSubfloat(bool s) { subfloat_ = s; }
+	/// 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 +182,9 @@
 	 */
 	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,6 +195,11 @@
 	std::string current_float_;
 	/// Are we in a subfloat?
 	bool subfloat_;
+	/// Used to keep track of active counters when going through 
+	/// updateLabels().
+	std::deque<docstring> counter_stack_;
+	/// Same, but for last layout.
+	std::deque<Layout const *> layout_stack_;
 };
 
 
Index: src/Counters.cpp
===================================================================
--- src/Counters.cpp	(revision 32054)
+++ src/Counters.cpp	(working copy)
@@ -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(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();
@@ -236,6 +237,9 @@
 	}
 
 	it->second.step();
+	LASSERT(counter_stack_.size() > 0, /* */);
+	counter_stack_.pop_back();
+	counter_stack_.push_back(ctr);
 	it = counterList_.begin();
 	CounterList::iterator const end = counterList_.end();
 	for (; it != end; ++it) {
@@ -255,6 +259,9 @@
 	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 +537,65 @@
 }
 
 
+docstring Counters::currentCounter() const
+{ 
+	LASSERT(!counter_stack_.empty(), /* */);
+	return counter_stack_.back(); 
+}
+
+
+void Counters::setActiveLayout(Layout const & lay)
+{
+	LASSERT(!layout_stack_.empty(), /* */);
+	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(), /* */);
+	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
Index: src/Buffer.cpp
===================================================================
--- src/Buffer.cpp	(revision 32054)
+++ src/Buffer.cpp	(working copy)
@@ -3683,6 +3683,9 @@
 }
 
 
+// FIXME There is still an issue somewhere in the Features
+// manual with extra counters lying around at the end. I'll
+// have to debug that later.
 void Buffer::updateLabels(ParIterator & parit) const
 {
 	LASSERT(parit.pit() == 0, /**/);
@@ -3696,11 +3699,17 @@
 	depth_type maxdepth = 0;
 	pit_type const lastpit = parit.lastpit();
 	for ( ; parit.pit() <= lastpit ; ++parit.pit()) {
-		// reduce depth if necessary
+		// Reduce depth if necessary
 		parit->params().depth(min(parit->params().depth(), maxdepth));
 		maxdepth = parit->getMaxDepthAfter();
 
-		// set the counter for this paragraph
+		// 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);
 
 		// Now the insets
Index: src/insets/InsetLabel.h
===================================================================
--- src/insets/InsetLabel.h	(revision 32054)
+++ src/insets/InsetLabel.h	(working copy)
@@ -17,6 +17,8 @@
 
 namespace lyx {
 
+class Counter;
+
 class InsetLabel : public InsetCommand {
 public:
 	///
@@ -48,7 +50,7 @@
 	///
 	static ParamInfo const & findInfo(std::string const &);
 	///
-	static std::string defaultCommand() { return "label"; };
+	static std::string defaultCommand() { return "label"; }
 	///
 	static bool isCompatibleCommand(std::string const & s) 
 		{ return s == "label"; }
@@ -60,6 +62,10 @@
 	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_; }
+	///
+	int counterValue() const { return counter_value_; }
 protected:
 	///
 	void doDispatch(Cursor & cur, FuncRequest & cmd);
@@ -68,6 +74,10 @@
 	Inset * clone() const { return new InsetLabel(*this); }
 	///
 	docstring screen_label_;
+	///
+	docstring active_counter_;
+	///
+	int counter_value_;
 };
 
 
Index: src/insets/InsetLabel.cpp
===================================================================
--- src/insets/InsetLabel.cpp	(revision 32054)
+++ src/insets/InsetLabel.cpp	(working copy)
@@ -16,6 +16,7 @@
 
 #include "buffer_funcs.h"
 #include "Buffer.h"
+#include "BufferParams.h"
 #include "BufferView.h"
 #include "CutAndPaste.h"
 #include "DispatchResult.h"
@@ -27,6 +28,7 @@
 #include "ParIterator.h"
 #include "sgml.h"
 #include "Text.h"
+#include "TextClass.h"
 #include "TocBackend.h"
 
 #include "frontends/alert.h"
@@ -115,6 +117,13 @@
 	}
 	buffer().setInsetLabel(label, this);
 	screen_label_ = label;
+	Counters const & cnts = 
+		buffer().masterBuffer()->params().documentClass().counters();
+	active_counter_ = cnts.currentCounter();
+	if (!active_counter_.empty())
+		counter_value_ = cnts.value(active_counter_);
+	else 
+		counter_value_ = 0;
 }
 
 
Index: src/insets/InsetRef.cpp
===================================================================
--- src/insets/InsetRef.cpp	(revision 32054)
+++ src/insets/InsetRef.cpp	(working copy)
@@ -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"
@@ -27,6 +28,8 @@
 #include "support/gettext.h"
 #include "support/lstrings.h"
 
+#include <boost/lexical_cast.hpp>
+
 using namespace lyx::support;
 using namespace std;
 
@@ -66,8 +69,61 @@
 }
 
 
+// FIXME There is a bug here about updating the screen.
+// We probably need to invalidate screen_label_ when the command
+// is changed, so need to implement a local version of doDispatch.
+// We'll also want tooltips now, and maybe to make this configurable.
 docstring InsetRef::screenLabel() const
 {
+	// cached?
+	if (!screen_label_.empty())
+		return screen_label_;
+	
+	docstring const & ref = getParam("reference");
+	InsetLabel const * il = buffer().insetLabel(ref);
+	string const & cmd = params().getCmdName();
+	// we can't do anything special with page references
+	if (il) {
+		// Try to construct a label from the InsetLabel we reference.
+		docstring const & cntr = il->activeCounter();
+		int value = il->counterValue();
+		// if this fails, then we probably don't have the counter, so
+		// we'll do it the other way.
+		if (value != 0) {
+			// ?? For some reason, I can't see to get access to
+			// our convert() function. So I do it manually.
+			docstring val = from_ascii(boost::lexical_cast<string>(value));
+			if (cmd == "ref")
+				screen_label_ = val;
+			else if (cmd == "vref")
+				screen_label_ = bformat(from_ascii("%1$s on page ##"), val);
+			else if (cmd == "pageref" || cmd == "vpageref")
+				screen_label_ = _("on page ##");
+			else if (cmd == "eqref")
+				screen_label_ = bformat(from_ascii("equation (%1$s)"), val);
+			else { // "prettyref"
+				docstring cntrname = translateIfPossible(cntr);
+				// FIXME Use the label string, if we have it. Otherwise, do this.
+				screen_label_ = bformat(from_ascii("%1$s %2$s"), cntrname, val);
+			}
+		}
+		return screen_label_;
+	}
+
+	// else reference is unknown
+	// construct a label from our parameters
+	for (int i = 0; !types[i].latex_name.empty(); ++i) {
+		if (getCmdName() == types[i].latex_name) {
+			screen_label_ = _(types[i].short_gui_name);
+			break;
+		}
+	}
+	screen_label_ += ref;
+
+	if (!isLatex && !getParam("name").empty()) {
+		screen_label_ += "||";
+		screen_label_ += getParam("name");
+	}
 	return screen_label_;
 }
 
@@ -137,19 +193,8 @@
 	docstring const & label = getParam("reference");
 	// register this inset into the buffer reference cache.
 	buffer().references(label).push_back(make_pair(this, it));
-
-	for (int i = 0; !types[i].latex_name.empty(); ++i) {
-		if (getCmdName() == types[i].latex_name) {
-			screen_label_ = _(types[i].short_gui_name);
-			break;
-		}
-	}
-	screen_label_ += getParam("reference");
-
-	if (!isLatex && !getParam("name").empty()) {
-		screen_label_ += "||";
-		screen_label_ += getParam("name");
-	}
+	// invalidate label cache
+	screen_label_.clear();
 }
 
 
Index: src/insets/InsetText.cpp
===================================================================
--- src/insets/InsetText.cpp	(revision 32054)
+++ src/insets/InsetText.cpp	(working copy)
@@ -649,10 +649,22 @@
 	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.
+		Counters & cnt = buffer().masterBuffer()->params().documentClass().counters();
+		LYXERR0("Entering " << name());
+		cnt.clearLastLayout();
+		// FIXME cnt.saveLastCounter()?
 		buffer().updateLabels(it2);
-	else {
+		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);
 		tclass.counters() = savecnt;

Reply via email to