Le 13/09/2015 22:28, Richard Heck a écrit :
On 09/12/2015 09:31 PM, Guillaume Munch wrote:

* I would like to discuss another set of patches that makes it
convenient to navigate within floats: see
<http://www.lyx.org/trac/ticket/9759>. To test the patch please
compare the behaviour, before and after, of the navigation menus and
of the outliner, in the presence of floats with 1) subfloats, 2)
multiple captions, 3) a total number of floats and subfloats of the
same type > 30. Detailed changes are in the patch header, as usual.
Would you prefer that I post directly the patches on the list in the
future?

I don't use floats much so am a bad person to test. But if you actually
want discussion of the patch, yes, post it here.


Here's a rebased version because it had already diverged...

1. Support for (std|boost)::make_shared.
I did not sense enough smart-pointer energy radiating from the code...
Sorry for the recompilation times.

2. Do not show subfigures in the menu (bug #9759)
The improvements to the navigation menu described in the original message above.

3. Better construction of the TOC for floats and captions using a new TocBuilder class
Fixes the situation caused by 2. in the outliner.

4. New class InsetCaptionable
Generalisation to wraps and listings and some code factoring.


>From 0b39537d33cf3a336de05833a839fb688c06e27a Mon Sep 17 00:00:00 2001
From: Guillaume Munch <g...@lyx.org>
Date: Wed, 2 Sep 2015 19:53:13 +0100
Subject: [PATCH 4/4] New class InsetCaptionable

This generalises the new use of TocBuilder (97e8101) to listings and wraps and
removes some duplicate code.

Make sure that we will never write an empty float type again in the future
(#9760)
---
 src/Makefile.am                  |  2 +
 src/frontends/qt4/TocModel.cpp   | 12 +++--
 src/frontends/qt4/TocModel.h     |  6 ++-
 src/insets/InsetCaptionable.cpp  | 94 ++++++++++++++++++++++++++++++++++++++
 src/insets/InsetCaptionable.h    | 54 ++++++++++++++++++++++
 src/insets/InsetCollapsable.cpp  | 13 ------
 src/insets/InsetCollapsable.h    |  2 -
 src/insets/InsetFloat.cpp        | 98 ++++++++++++++--------------------------
 src/insets/InsetFloat.h          | 14 +++---
 src/insets/InsetListings.cpp     | 28 +++---------
 src/insets/InsetListings.h       |  6 +--
 src/insets/InsetListingsParams.h |  2 +-
 src/insets/InsetWrap.cpp         | 47 ++++++++-----------
 src/insets/InsetWrap.h           |  8 ++--
 14 files changed, 234 insertions(+), 152 deletions(-)
 create mode 100644 src/insets/InsetCaptionable.cpp
 create mode 100644 src/insets/InsetCaptionable.h

diff --git a/src/Makefile.am b/src/Makefile.am
index cf25556..35d890f 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -573,6 +573,7 @@ SOURCEFILESINSETS = \
 	insets/InsetBox.cpp \
 	insets/InsetBranch.cpp \
 	insets/InsetCaption.cpp \
+	insets/InsetCaptionable.cpp \
 	insets/InsetCitation.cpp \
 	insets/InsetCollapsable.cpp \
 	insets/InsetCommand.cpp \
@@ -631,6 +632,7 @@ HEADERFILESINSETS = \
 	insets/InsetBox.h \
 	insets/InsetBranch.h \
 	insets/InsetCaption.h \
+	insets/InsetCaptionable.h \
 	insets/InsetCitation.h \
 	insets/InsetCode.h \
 	insets/InsetCollapsable.h \
diff --git a/src/frontends/qt4/TocModel.cpp b/src/frontends/qt4/TocModel.cpp
index a0059f7..c2cda38 100644
--- a/src/frontends/qt4/TocModel.cpp
+++ b/src/frontends/qt4/TocModel.cpp
@@ -80,8 +80,9 @@ public:
 
 TocModel::TocModel(QObject * parent)
 	: model_(new TocTypeModel(parent)),
-	sorted_model_(new QSortFilterProxyModel(parent)),
-	is_sorted_(false), toc_(0), maxdepth_(0), mindepth_(0)
+	  sorted_model_(new QSortFilterProxyModel(parent)),
+	  is_sorted_(false), toc_(make_shared<Toc const>()),
+	  maxdepth_(0), mindepth_(0)
 {
 	sorted_model_->setSortLocaleAware(true);
 	sorted_model_->setSourceModel(model_);
@@ -108,6 +109,7 @@ void TocModel::clear()
 {
 	model_->blockSignals(true);
 	model_->clear();
+	toc_ = make_shared<Toc const>();
 	model_->blockSignals(false);
 }
 
@@ -157,9 +159,9 @@ void TocModel::updateItem(DocIterator const & dit)
 }
 
 
-void TocModel::reset(Toc const & toc)
+void TocModel::reset(shared_ptr<Toc const> toc)
 {
-	toc_ = &toc;
+	toc_ = toc;
 	if (toc_->empty()) {
 		maxdepth_ = 0;
 		mindepth_ = 0;
@@ -364,7 +366,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());
diff --git a/src/frontends/qt4/TocModel.h b/src/frontends/qt4/TocModel.h
index 83e957b..6b4315b 100644
--- a/src/frontends/qt4/TocModel.h
+++ b/src/frontends/qt4/TocModel.h
@@ -12,6 +12,8 @@
 #ifndef TOCMODEL_H
 #define TOCMODEL_H
 
+#include "support/shared_ptr.h"
+
 #include <QHash>
 #include <QSortFilterProxyModel>
 
@@ -36,7 +38,7 @@ public:
 	///
 	TocModel(QObject * parent);
 	///
-	void reset(Toc const & toc);
+	void reset(shared_ptr<Toc const>);
 	///
 	void reset();
 	///
@@ -68,7 +70,7 @@ private:
 	///
 	bool is_sorted_;
 	///
-	Toc const * toc_;
+	shared_ptr<Toc const> toc_;
 	///
 	int maxdepth_;
 	///
diff --git a/src/insets/InsetCaptionable.cpp b/src/insets/InsetCaptionable.cpp
new file mode 100644
index 0000000..f90776a
--- /dev/null
+++ b/src/insets/InsetCaptionable.cpp
@@ -0,0 +1,94 @@
+/**
+ * \file InsetCaptionable.cpp
+ * This file is part of LyX, the document processor.
+ * Licence details can be found in the file COPYING.
+ *
+ * \author Alejandro Aguilar Sierra
+ * \author Jürgen Vigna
+ * \author Lars Gullik Bjønnes
+ * \author Guillaume Munch
+ *
+ * Full author contact details are available in file CREDITS.
+ */
+
+#include <config.h>
+
+#include "InsetCaptionable.h"
+
+#include "Buffer.h"
+#include "BufferParams.h"
+#include "BufferView.h"
+#include "FloatList.h"
+#include "TextClass.h"
+#include "TocBackend.h"
+
+using namespace std;
+
+
+namespace lyx {
+
+
+void InsetCaptionable::setCaptionType(std::string const & type)
+{
+	caption_type_ = type.empty() ? "senseless" : type;
+}
+
+
+/// common to InsetFloat and InsetWrap
+docstring InsetCaptionable::floatName(string const & type) const
+{
+	BufferParams const & bp = buffer().params();
+	FloatList const & floats = bp.documentClass().floats();
+	FloatList::const_iterator it = floats[type];
+	// FIXME UNICODE
+	return (it == floats.end()) ? from_ascii(type) : bp.B_(it->second.name());
+}
+
+
+void InsetCaptionable::addToToc(DocIterator const & cpit, bool output_active) const
+{
+	DocIterator pit = cpit;
+	pit.push_back(CursorSlice(const_cast<InsetCaptionable &>(*this)));
+	docstring str;
+	int length = output_active ? INT_MAX : TOC_ENTRY_LENGTH;
+	text().forOutliner(str, length);
+	shared_ptr<TocBuilder> b = buffer().tocBackend().builder(caption_type_);
+	b->pushItem(pit, str, output_active);
+	// Proceed with the rest of the inset.
+	InsetCollapsable::addToToc(cpit, output_active);
+	b->pop();
+}
+
+void InsetCaptionable::updateBuffer(ParIterator const & it, UpdateType utype)
+{
+	Counters & cnts =
+		buffer().masterBuffer()->params().documentClass().counters();
+	string const saveflt = cnts.current_float();
+	bool const savesubflt = cnts.isSubfloat();
+	if (utype == OutputUpdate) {
+		// counters are local to the float
+		cnts.saveLastCounter();
+	}
+	bool const subflt = hasSubCaptions(it);
+	// floats can only embed subfloats of their own kind
+	if (subflt && !saveflt.empty() && saveflt != "senseless")
+		setCaptionType(saveflt);
+	// Tell captions what the current float is
+	cnts.current_float(caption_type_);
+	cnts.isSubfloat(subflt);
+	InsetCollapsable::updateBuffer(it, utype);
+	// Restore counters
+	cnts.current_float(saveflt);
+	if (utype == OutputUpdate)
+		cnts.restoreLastCounter();
+	cnts.isSubfloat(savesubflt);
+}
+
+
+bool InsetCaptionable::insetAllowed(InsetCode c) const
+{
+	return (c == CAPTION_CODE) || InsetCollapsable::insetAllowed(c);
+}
+
+
+} // namespace lyx
diff --git a/src/insets/InsetCaptionable.h b/src/insets/InsetCaptionable.h
new file mode 100644
index 0000000..9e85f67
--- /dev/null
+++ b/src/insets/InsetCaptionable.h
@@ -0,0 +1,54 @@
+// -*- C++ -*-
+/**
+ * \file InsetCaptionable.h
+ * This file is part of LyX, the document processor.
+ * Licence details can be found in the file COPYING.
+ *
+ * \author Alejandro Aguilar Sierra
+ * \author Jürgen Vigna
+ * \author Lars Gullik Bjønnes
+ * \author Guillaume Munch
+ *
+ * Full author contact details are available in file CREDITS.
+ */
+
+#ifndef INSET_CAPTIONABLE_H
+#define INSET_CAPTIONABLE_H
+
+#include "InsetCollapsable.h"
+
+
+namespace lyx {
+
+class InsetCaptionable : public InsetCollapsable
+{
+public:
+	InsetCaptionable(Buffer * buffer)
+		: InsetCollapsable(buffer), caption_type_("senseless") {}
+	InsetCaptionable(Buffer * buffer, std::string const & type)
+		: InsetCollapsable(buffer), caption_type_(type) {}
+	///
+	std::string const & captionType() const { return caption_type_; }
+	///
+	docstring floatName(std::string const & type) const;
+	///
+protected:
+	///
+	virtual void setCaptionType(std::string const & type);
+	/// are our captions subcaptions?
+	virtual bool hasSubCaptions(ParIterator const &) const { return false; }
+	///
+	void addToToc(DocIterator const & di, bool output_active) const;
+	/// Update the counters of this inset and of its contents
+	void updateBuffer(ParIterator const &, UpdateType);
+	///
+	bool insetAllowed(InsetCode) const;
+private:
+	///
+	std::string caption_type_;
+};
+
+
+} // namespace lyx
+
+#endif // INSET_CAPTIONABLE_H
diff --git a/src/insets/InsetCollapsable.cpp b/src/insets/InsetCollapsable.cpp
index 6e5051e..623cebb 100644
--- a/src/insets/InsetCollapsable.cpp
+++ b/src/insets/InsetCollapsable.cpp
@@ -15,18 +15,15 @@
 #include "InsetCollapsable.h"
 
 #include "Buffer.h"
-#include "BufferParams.h"
 #include "BufferView.h"
 #include "Cursor.h"
 #include "Dimension.h"
-#include "FloatList.h"
 #include "FuncRequest.h"
 #include "FuncStatus.h"
 #include "InsetLayout.h"
 #include "Lexer.h"
 #include "MetricsInfo.h"
 #include "OutputParams.h"
-#include "TextClass.h"
 
 #include "frontends/FontMetrics.h"
 #include "frontends/Painter.h"
@@ -602,16 +599,6 @@ void InsetCollapsable::setStatus(Cursor & cur, CollapseStatus status)
 }
 
 
-docstring InsetCollapsable::floatName(string const & type) const
-{
-	BufferParams const & bp = buffer().params();
-	FloatList const & floats = bp.documentClass().floats();
-	FloatList::const_iterator it = floats[type];
-	// FIXME UNICODE
-	return (it == floats.end()) ? from_ascii(type) : bp.B_(it->second.name());
-}
-
-
 InsetLayout::InsetDecoration InsetCollapsable::decoration() const
 {
 	InsetLayout::InsetDecoration const dec = getLayout().decoration();
diff --git a/src/insets/InsetCollapsable.h b/src/insets/InsetCollapsable.h
index 1c990f9..f2ce0ca 100644
--- a/src/insets/InsetCollapsable.h
+++ b/src/insets/InsetCollapsable.h
@@ -148,8 +148,6 @@ public:
 	std::string contextMenu(BufferView const & bv, int x, int y) const;
 	///
 	std::string contextMenuName() const;
-	///
-	docstring floatName(std::string const & type) const;
 protected:
 	///
 	void doDispatch(Cursor & cur, FuncRequest & cmd);
diff --git a/src/insets/InsetFloat.cpp b/src/insets/InsetFloat.cpp
index 5a0a07b..1cda8d9 100644
--- a/src/insets/InsetFloat.cpp
+++ b/src/insets/InsetFloat.cpp
@@ -30,7 +30,6 @@
 #include "output_xhtml.h"
 #include "ParIterator.h"
 #include "TextClass.h"
-#include "TocBackend.h"
 
 #include "support/debug.h"
 #include "support/docstream.h"
@@ -114,9 +113,23 @@ namespace lyx {
 
 //FIXME: why do we set in stone the type here?
 InsetFloat::InsetFloat(Buffer * buf, string params_str)
-	: InsetCollapsable(buf)
+	: InsetCaptionable(buf)
 {
 	string2params(params_str, params_);
+	setCaptionType(params_.type);
+}
+
+
+// Enforce equality of float type and caption type.
+void InsetFloat::setCaptionType(std::string const & type)
+{
+	InsetCaptionable::setCaptionType(type);	
+	params_.type = captionType();
+	// check if the float type exists
+	if (buffer().params().documentClass().floats().typeExist(params_.type))
+		setLabel(_("float: ") + floatName(params_.type));
+	else
+		setLabel(bformat(_("ERROR: Unknown float type: %1$s"), from_utf8(params_.type)));
 }
 
 
@@ -129,7 +142,7 @@ docstring InsetFloat::layoutName() const
 docstring InsetFloat::toolTip(BufferView const & bv, int x, int y) const
 {
 	if (isOpen(bv))
-		return InsetCollapsable::toolTip(bv, x, y);
+		return InsetCaptionable::toolTip(bv, x, y);
 
 	OutputParams rp(&buffer().params().encoding());
 	return getCaptionText(rp);
@@ -152,10 +165,8 @@ void InsetFloat::doDispatch(Cursor & cur, FuncRequest & cmd)
 			params_.sideways  = params.sideways;
 		}
 		setNewLabel();
-		if (params_.type != params.type) {
-			params_.type = params.type;
-			cur.forceBufferUpdate();
-		}
+		if (params_.type != params.type)
+			setCaptionType(params.type);
 		// what we really want here is a TOC update, but that means
 		// a full buffer update
 		cur.forceBufferUpdate();
@@ -168,7 +179,7 @@ void InsetFloat::doDispatch(Cursor & cur, FuncRequest & cmd)
 	}
 
 	default:
-		InsetCollapsable::doDispatch(cur, cmd);
+		InsetCaptionable::doDispatch(cur, cmd);
 		break;
 	}
 }
@@ -185,7 +196,7 @@ bool InsetFloat::getStatus(Cursor & cur, FuncRequest const & cmd,
 		return true;
 
 	case LFUN_INSET_SETTINGS:
-		if (InsetCollapsable::getStatus(cur, cmd, flag)) {
+		if (InsetCaptionable::getStatus(cur, cmd, flag)) {
 			flag.setEnabled(flag.enabled() && !params_.subfloat);
 			return true;
 		} else
@@ -198,62 +209,25 @@ bool InsetFloat::getStatus(Cursor & cur, FuncRequest const & cmd,
 		}
 
 	default:
-		return InsetCollapsable::getStatus(cur, cmd, flag);
+		return InsetCaptionable::getStatus(cur, cmd, flag);
 	}
 }
 
 
-void InsetFloat::addToToc(DocIterator const & cpit, bool output_active) const
+bool InsetFloat::hasSubCaptions(ParIterator const & it) 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 =
-		buffer().masterBuffer()->params().documentClass().counters();
-	if (utype == OutputUpdate) {
-		// counters are local to the float
-		cnts.saveLastCounter();
-	}
-	string const saveflt = cnts.current_float();
-	bool const savesubflt = cnts.isSubfloat();
-
-	bool const subflt = (it.innerInsetOfType(FLOAT_CODE)
-			     || it.innerInsetOfType(WRAP_CODE));
-	// floats can only embed subfloats of their own kind
-	if (subflt)
-		params_.type = saveflt;
-	setSubfloat(subflt);
-
-	// Tell to captions what the current float is
-	cnts.current_float(params().type);
-	cnts.isSubfloat(subflt);
-
-	InsetCollapsable::updateBuffer(it, utype);
-
-	//reset afterwards
-	cnts.current_float(saveflt);
-	if (utype == OutputUpdate)
-		cnts.restoreLastCounter();
-	cnts.isSubfloat(savesubflt);
+	return (it.innerInsetOfType(FLOAT_CODE) || it.innerInsetOfType(WRAP_CODE));
 }
 
 
 void InsetFloatParams::write(ostream & os) const
 {
-	os << type << '\n';
+	if (type.empty()) {
+		// Better this than creating a parse error. This in fact happens in the
+		// parameters dialog via InsetFloatParams::params2string.
+		os << "senseless" << '\n';
+	} else
+		os << type << '\n';
 
 	if (!placement.empty())
 		os << "placement " << placement << "\n";
@@ -285,19 +259,15 @@ void InsetFloat::write(ostream & os) const
 {
 	os << "Float ";
 	params_.write(os);
-	InsetCollapsable::write(os);
+	InsetCaptionable::write(os);
 }
 
 
 void InsetFloat::read(Lexer & lex)
 {
 	params_.read(lex);
-	InsetCollapsable::read(lex);
-	// check if the float type exists
-	if (buffer().params().documentClass().floats().typeExist(params_.type))
-		setLabel(_("float: ") + floatName(params_.type));
-	else
-		setLabel(bformat(_("ERROR: Unknown float type: %1$s"), from_utf8(params_.type)));
+	InsetCaptionable::read(lex);
+	setCaptionType(params_.type);
 }
 
 
@@ -314,7 +284,7 @@ void InsetFloat::validate(LaTeXFeatures & features) const
 
 	features.useFloat(params_.type, features.inFloat());
 	features.inFloat(true);
-	InsetCollapsable::validate(features);
+	InsetCaptionable::validate(features);
 	features.inFloat(false);
 }
 
@@ -460,7 +430,7 @@ bool InsetFloat::insetAllowed(InsetCode code) const
 	case MARGIN_CODE:
 		return false;
 	default:
-		return InsetCollapsable::insetAllowed(code);
+		return InsetCaptionable::insetAllowed(code);
 	}
 }
 
diff --git a/src/insets/InsetFloat.h b/src/insets/InsetFloat.h
index d0e1afa..c58be58 100644
--- a/src/insets/InsetFloat.h
+++ b/src/insets/InsetFloat.h
@@ -13,7 +13,7 @@
 #ifndef INSET_FLOAT_H
 #define INSET_FLOAT_H
 
-#include "InsetCollapsable.h"
+#include "InsetCaptionable.h"
 
 
 namespace lyx {
@@ -22,7 +22,7 @@ class InsetFloatParams
 {
 public:
 	///
-	InsetFloatParams() : wide(false), sideways(false), subfloat(false) {}
+	InsetFloatParams() : type("senseless"), wide(false), sideways(false), subfloat(false) {}
 	///
 	void write(std::ostream & os) const;
 	///
@@ -48,12 +48,10 @@ public:
 /////////////////////////////////////////////////////////////////////////
 
 /// Used for "floating" objects like tables, figures etc.
-class InsetFloat : public InsetCollapsable
+class InsetFloat : public InsetCaptionable
 {
 public:
-	///
 	InsetFloat(Buffer * buffer, std::string params_str);
-
 	///
 	static void string2params(std::string const &, InsetFloatParams &);
 	///
@@ -72,6 +70,8 @@ public:
 	bool allowsCaptionVariation(std::string const &) const;
 private:
 	///
+	void setCaptionType(std::string const & type);
+	///
 	docstring layoutName() const;
 	///
 	docstring toolTip(BufferView const & bv, int x, int y) const;
@@ -101,9 +101,7 @@ private:
 	///
 	bool getStatus(Cursor &, FuncRequest const &, FuncStatus &) const;
 	///
-	void addToToc(DocIterator const & di, bool output_active) const;
-	/// Update the counters of this inset and of its contents
-	void updateBuffer(ParIterator const &, UpdateType);
+	bool hasSubCaptions(ParIterator const & it) const;
 	///
 	void doDispatch(Cursor & cur, FuncRequest & cmd);
 	///
diff --git a/src/insets/InsetListings.cpp b/src/insets/InsetListings.cpp
index 879b205..566636d 100644
--- a/src/insets/InsetListings.cpp
+++ b/src/insets/InsetListings.cpp
@@ -51,7 +51,7 @@ namespace lyx {
 
 
 InsetListings::InsetListings(Buffer * buf, InsetListingsParams const & par)
-	: InsetCollapsable(buf)
+	: InsetCaptionable(buf,"listing")
 {
 	status_ = par.status();
 }
@@ -69,22 +69,6 @@ Inset::DisplayType InsetListings::display() const
 }
 
 
-void InsetListings::updateBuffer(ParIterator const & it, UpdateType utype)
-{
-	Counters & cnts =
-		buffer().masterBuffer()->params().documentClass().counters();
-	string const saveflt = cnts.current_float();
-
-	// Tell to captions what the current float is
-	cnts.current_float("listing");
-
-	InsetCollapsable::updateBuffer(it, utype);
-
-	//reset afterwards
-	cnts.current_float(saveflt);
-}
-
-
 void InsetListings::write(ostream & os) const
 {
 	os << "listings" << "\n";
@@ -97,7 +81,7 @@ void InsetListings::write(ostream & os) const
 		os << "inline true\n";
 	else
 		os << "inline false\n";
-	InsetCollapsable::write(os);
+	InsetCaptionable::write(os);
 }
 
 
@@ -119,7 +103,7 @@ void InsetListings::read(Lexer & lex)
 			break;
 		}
 	}
-	InsetCollapsable::read(lex);
+	InsetCaptionable::read(lex);
 }
 
 
@@ -334,7 +318,7 @@ void InsetListings::doDispatch(Cursor & cur, FuncRequest & cmd)
 		break;
 
 	default:
-		InsetCollapsable::doDispatch(cur, cmd);
+		InsetCaptionable::doDispatch(cur, cmd);
 		break;
 	}
 }
@@ -356,7 +340,7 @@ bool InsetListings::getStatus(Cursor & cur, FuncRequest const & cmd,
 			}
 		}
 		default:
-			return InsetCollapsable::getStatus(cur, cmd, status);
+			return InsetCaptionable::getStatus(cur, cmd, status);
 	}
 }
 
@@ -377,7 +361,7 @@ void InsetListings::validate(LaTeXFeatures & features) const
 	string param_string = params().params();
 	if (param_string.find("\\color") != string::npos)
 		features.require("color");
-	InsetCollapsable::validate(features);
+	InsetCaptionable::validate(features);
 }
 
 
diff --git a/src/insets/InsetListings.h b/src/insets/InsetListings.h
index 0782d68..84acbb9 100644
--- a/src/insets/InsetListings.h
+++ b/src/insets/InsetListings.h
@@ -25,8 +25,8 @@ class LaTeXFeatures;
 //
 /////////////////////////////////////////////////////////////////////////
 
-/// A collapsable text inset for program listings.
-class InsetListings : public InsetCollapsable
+/// A captionable and collapsable text inset for program listings.
+class InsetListings : public InsetCaptionable
 {
 public:
 	///
@@ -48,8 +48,6 @@ private:
 	DisplayType display() const;
 	///
 	docstring layoutName() const { return from_ascii("Listings"); }
-	// Update the counters of this inset and of its contents
-	void updateBuffer(ParIterator const &, UpdateType);
 	///
 	void write(std::ostream & os) const;
 	///
diff --git a/src/insets/InsetListingsParams.h b/src/insets/InsetListingsParams.h
index c956643..7388acd 100644
--- a/src/insets/InsetListingsParams.h
+++ b/src/insets/InsetListingsParams.h
@@ -12,7 +12,7 @@
 #ifndef INSETLISTINGSPARAMS_H
 #define INSETLISTINGSPARAMS_H
 
-#include "InsetCollapsable.h"
+#include "InsetCaptionable.h"
 
 #include <map>
 
diff --git a/src/insets/InsetWrap.cpp b/src/insets/InsetWrap.cpp
index cfe8870..00223ad 100644
--- a/src/insets/InsetWrap.cpp
+++ b/src/insets/InsetWrap.cpp
@@ -43,10 +43,9 @@ using namespace std;
 namespace lyx {
 
 InsetWrap::InsetWrap(Buffer * buf, string const & type)
-	: InsetCollapsable(buf)
+	: InsetCaptionable(buf)
 {
-	setLabel(_("wrap: ") + floatName(type));
-	params_.type = type;
+	setCaptionType(type);
 	params_.lines = 0;
 	params_.placement = "o";
 	params_.overhang = Length(0, Length::PCW);
@@ -60,6 +59,15 @@ InsetWrap::~InsetWrap()
 }
 
 
+// Enforce equality of float type and caption type.
+void InsetWrap::setCaptionType(std::string const & type)
+{
+	InsetCaptionable::setCaptionType(type);
+	params_.type = captionType();
+	setLabel(_("wrap: ") + floatName(type));
+}
+
+
 docstring InsetWrap::layoutName() const
 {
 	return "Wrap:" + from_utf8(params_.type);
@@ -69,7 +77,7 @@ docstring InsetWrap::layoutName() const
 docstring InsetWrap::toolTip(BufferView const & bv, int x, int y) const
 {
 	if (isOpen(bv))
-		return InsetCollapsable::toolTip(bv, x, y);
+		return InsetCaptionable::toolTip(bv, x, y);
 	OutputParams rp(&buffer().params().encoding());
 	docstring caption_tip = getCaptionText(rp);
 	if (!caption_tip.empty())
@@ -97,7 +105,7 @@ void InsetWrap::doDispatch(Cursor & cur, FuncRequest & cmd)
 		break;
 
 	default:
-		InsetCollapsable::doDispatch(cur, cmd);
+		InsetCaptionable::doDispatch(cur, cmd);
 		break;
 	}
 }
@@ -113,31 +121,14 @@ bool InsetWrap::getStatus(Cursor & cur, FuncRequest const & cmd,
 		return true;
 
 	default:
-		return InsetCollapsable::getStatus(cur, cmd, flag);
+		return InsetCaptionable::getStatus(cur, cmd, flag);
 	}
 }
 
 
 void InsetWrap::updateBuffer(ParIterator const & it, UpdateType utype)
 {
-	setLabel(_("wrap: ") + floatName(params_.type));
-	Counters & cnts =
-		buffer().masterBuffer()->params().documentClass().counters();
-	if (utype == OutputUpdate) {
-		// counters are local to the wrap
-		cnts.saveLastCounter();
-	}
-	string const saveflt = cnts.current_float();
-
-	// Tell to captions what the current float is
-	cnts.current_float(params().type);
-
-	InsetCollapsable::updateBuffer(it, utype);
-
-	// reset afterwards
-	cnts.current_float(saveflt);
-	if (utype == OutputUpdate)
-		cnts.restoreLastCounter();
+	InsetCaptionable::updateBuffer(it, utype);
 }
 
 
@@ -164,14 +155,14 @@ void InsetWrapParams::read(Lexer & lex)
 void InsetWrap::write(ostream & os) const
 {
 	params_.write(os);
-	InsetCollapsable::write(os);
+	InsetCaptionable::write(os);
 }
 
 
 void InsetWrap::read(Lexer & lex)
 {
 	params_.read(lex);
-	InsetCollapsable::read(lex);
+	InsetCaptionable::read(lex);
 }
 
 
@@ -179,7 +170,7 @@ void InsetWrap::validate(LaTeXFeatures & features) const
 {
 	features.require("wrapfig");
 	features.inFloat(true);
-	InsetCollapsable::validate(features);
+	InsetCaptionable::validate(features);
 	features.inFloat(false);
 }
 
@@ -248,7 +239,7 @@ bool InsetWrap::insetAllowed(InsetCode code) const
 	case MARGIN_CODE:
 		return false;
 	default:
-		return InsetCollapsable::insetAllowed(code);
+		return InsetCaptionable::insetAllowed(code);
 	}
 }
 
diff --git a/src/insets/InsetWrap.h b/src/insets/InsetWrap.h
index ce6b73e..af1ae53 100644
--- a/src/insets/InsetWrap.h
+++ b/src/insets/InsetWrap.h
@@ -12,7 +12,7 @@
 #ifndef INSETWRAP_H
 #define INSETWRAP_H
 
-#include "InsetCollapsable.h"
+#include "InsetCaptionable.h"
 #include "Length.h"
 
 
@@ -41,7 +41,7 @@ public:
 
 /** The wrap inset
  */
-class InsetWrap : public InsetCollapsable {
+class InsetWrap : public InsetCaptionable {
 public:
 	///
 	InsetWrap(Buffer *, std::string const &);
@@ -55,6 +55,8 @@ public:
 	static std::string params2string(InsetWrapParams const &);
 private:
 	///
+	void setCaptionType(std::string const & type);
+	///
 	void write(std::ostream & os) const;
 	///
 	void read(Lexer & lex);
@@ -79,7 +81,7 @@ private:
 	bool showInsetDialog(BufferView *) const;
 	///
 	bool getStatus(Cursor &, FuncRequest const &, FuncStatus &) const;
-	/// Update the counters of this inset and of its contents
+	/// Update the label
 	void updateBuffer(ParIterator const &, UpdateType);
 	///
 	void doDispatch(Cursor & cur, FuncRequest & cmd);
-- 
2.1.4

>From ee2f939df9bd2c94ea9487bde63c4cfb1333bf9d Mon Sep 17 00:00:00 2001
From: Guillaume Munch <g...@lyx.org>
Date: Tue, 1 Sep 2015 17:08:35 +0100
Subject: [PATCH 3/4] 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
---
 src/BiblioInfo.cpp               |   6 +-
 src/Buffer.cpp                   |   7 +-
 src/BufferView.cpp               |   6 +-
 src/Changes.cpp                  |  14 +--
 src/TocBackend.cpp               | 209 ++++++++++++++++++++++++++-------------
 src/TocBackend.h                 | 114 ++++++++++++++++-----
 src/frontends/qt4/Menus.cpp      |  14 +--
 src/frontends/qt4/TocModel.cpp   |   2 +-
 src/frontends/qt4/qt_helpers.cpp |   2 +
 src/insets/InsetBranch.cpp       |   4 +-
 src/insets/InsetCaption.cpp      |  11 +--
 src/insets/InsetCitation.cpp     |   4 +-
 src/insets/InsetFloat.cpp        |  17 ++++
 src/insets/InsetFloat.h          |   7 +-
 src/insets/InsetFloatList.cpp    |   8 +-
 src/insets/InsetFoot.cpp         |   4 +-
 src/insets/InsetGraphics.cpp     |   2 +-
 src/insets/InsetInclude.cpp      |  41 ++++----
 src/insets/InsetIndex.cpp        |  10 +-
 src/insets/InsetLabel.cpp        |  36 +++----
 src/insets/InsetListings.cpp     |   3 +-
 src/insets/InsetMarginal.cpp     |   4 +-
 src/insets/InsetNomencl.cpp      |   8 +-
 src/insets/InsetNote.cpp         |   4 +-
 src/insets/InsetRef.cpp          |   4 +-
 src/insets/InsetTOC.cpp          |   9 +-
 src/insets/InsetText.cpp         |   7 +-
 src/mathed/InsetMathHull.cpp     |   4 +-
 28 files changed, 360 insertions(+), 201 deletions(-)

diff --git a/src/BiblioInfo.cpp b/src/BiblioInfo.cpp
index 7012779..d306c87 100644
--- a/src/BiblioInfo.cpp
+++ b/src/BiblioInfo.cpp
@@ -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;
diff --git a/src/Buffer.cpp b/src/Buffer.cpp
index 23fab45..0f37edd 100644
--- a/src/Buffer.cpp
+++ b/src/Buffer.cpp
@@ -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();
diff --git a/src/BufferView.cpp b/src/BufferView.cpp
index 8596247..2faf7aa 100644
--- a/src/BufferView.cpp
+++ b/src/BufferView.cpp
@@ -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());
diff --git a/src/Changes.cpp b/src/Changes.cpp
index 29471f2..df4a376 100644
--- a/src/Changes.cpp
+++ b/src/Changes.cpp
@@ -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)));
 	}
 }
diff --git a/src/TocBackend.cpp b/src/TocBackend.cpp
index f46b3c8..1eb8f80 100644
--- a/src/TocBackend.cpp
+++ b/src/TocBackend.cpp
@@ -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,23 +80,149 @@ FuncRequest TocItem::action() const
 
 ///////////////////////////////////////////////////////////////////////////
 //
+// Toc implementation
+//
+///////////////////////////////////////////////////////////////////////////
+
+TocIterator Toc::item(DocIterator const & dit) const
+{
+	TocIterator last = begin();
+	TocIterator it = end();
+	if (it == last)
+		return it;
+
+	--it;
+
+	DocIterator dit_text = dit;
+	if (dit_text.inMathed()) {
+		// We are only interested in text so remove the math CursorSlice.
+		while (dit_text.inMathed())
+			dit_text.pop_back();
+	}
+
+	for (; it != last; --it) {
+		// We verify that we don't compare contents of two
+		// different document. This happens when you
+		// have parent and child documents.
+		if (&it->dit_[0].inset() != &dit_text[0].inset())
+			continue;
+		if (it->dit_ <= dit_text)
+			return it;
+	}
+
+	// We are before the first Toc Item:
+	return last;
+}
+
+
+Toc::iterator Toc::item(int depth, docstring const & str)
+{
+	if (empty())
+		return end();
+	iterator it = begin();
+	iterator itend = end();
+	for (; it != itend; ++it) {
+		if (it->depth() == depth && it->str() == str)
+			break;
+	}
+	return it;
+}
+
+
+///////////////////////////////////////////////////////////////////////////
+//
+// 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
 //
 ///////////////////////////////////////////////////////////////////////////
 
-Toc const & TocBackend::toc(string const & type) const
+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(), { static Toc dummy; return dummy; });
+	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;
 }
 
 
-Toc & TocBackend::toc(string const & type)
+shared_ptr<TocBuilder> TocBackend::builder(string const & type)
 {
-	return tocs_[type];
+	return builders_.get(type, toc(type));
 }
 
 
@@ -122,7 +231,7 @@ bool TocBackend::updateItem(DocIterator const & dit)
 	if (dit.text()->getTocLevel(dit.pit()) == Layout::NOT_IN_TOC)
 		return false;
 
-	if (toc("tableofcontents").empty()) {
+	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!");
@@ -169,7 +278,10 @@ bool TocBackend::updateItem(DocIterator const & dit)
 
 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);
@@ -184,52 +296,7 @@ TocIterator TocBackend::item(string const & 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();
-	TocIterator it = end();
-	if (it == last)
-		return it;
-
-	--it;
-
-	DocIterator dit_text = dit;
-	if (dit_text.inMathed()) {
-		// We are only interested in text so remove the math CursorSlice.
-		while (dit_text.inMathed())
-			dit_text.pop_back();
-	}
-
-	for (; it != last; --it) {
-		// We verify that we don't compare contents of two
-		// different document. This happens when you
-		// have parent and child documents.
-		if (&it->dit_[0].inset() != &dit_text[0].inset())
-			continue;
-		if (it->dit_ <= dit_text)
-			return it;
-	}
-
-	// We are before the first Toc Item:
-	return last;
-}
-
-
-Toc::iterator Toc::item(int depth, docstring const & str)
-{
-	if (empty())
-		return end();
-	iterator it = begin();
-	iterator itend = end();
-	for (; it != itend; ++it) {
-		if (it->depth() == depth && it->str() == str)
-			break;
-	}
-	return it;
+	return toclist_it->second->item(dit);
 }
 
 
@@ -238,8 +305,8 @@ void TocBackend::writePlaintextTocList(string const & type,
 {
 	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)
diff --git a/src/TocBackend.h b/src/TocBackend.h
index 903072b..e18d28d 100644
--- a/src/TocBackend.h
+++ b/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
 
diff --git a/src/frontends/qt4/Menus.cpp b/src/frontends/qt4/Menus.cpp
index b8b8c31..c336542 100644
--- a/src/frontends/qt4/Menus.cpp
+++ b/src/frontends/qt4/Menus.cpp
@@ -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>")));
 	}
diff --git a/src/frontends/qt4/TocModel.cpp b/src/frontends/qt4/TocModel.cpp
index 44a2533..a0059f7 100644
--- a/src/frontends/qt4/TocModel.cpp
+++ b/src/frontends/qt4/TocModel.cpp
@@ -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());
diff --git a/src/frontends/qt4/qt_helpers.cpp b/src/frontends/qt4/qt_helpers.cpp
index a395abf..593aa3b 100644
--- a/src/frontends/qt4/qt_helpers.cpp
+++ b/src/frontends/qt4/qt_helpers.cpp
@@ -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();
diff --git a/src/insets/InsetBranch.cpp b/src/insets/InsetBranch.cpp
index 13ed53a..98e5c4c 100644
--- a/src/insets/InsetBranch.cpp
+++ b/src/insets/InsetBranch.cpp
@@ -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);
diff --git a/src/insets/InsetCaption.cpp b/src/insets/InsetCaption.cpp
index b832469..415c3ad 100644
--- a/src/insets/InsetCaption.cpp
+++ b/src/insets/InsetCaption.cpp
@@ -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
diff --git a/src/insets/InsetCitation.cpp b/src/insets/InsetCitation.cpp
index 36dc376..293b0a1 100644
--- a/src/insets/InsetCitation.cpp
+++ b/src/insets/InsetCitation.cpp
@@ -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));
 }
 
 
diff --git a/src/insets/InsetFloat.cpp b/src/insets/InsetFloat.cpp
index e611a81..5a0a07b 100644
--- a/src/insets/InsetFloat.cpp
+++ b/src/insets/InsetFloat.cpp
@@ -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 =
diff --git a/src/insets/InsetFloat.h b/src/insets/InsetFloat.h
index 6cff108..d0e1afa 100644
--- a/src/insets/InsetFloat.h
+++ b/src/insets/InsetFloat.h
@@ -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);
diff --git a/src/insets/InsetFloatList.cpp b/src/insets/InsetFloatList.cpp
index 981503f..9c84314 100644
--- a/src/insets/InsetFloatList.cpp
+++ b/src/insets/InsetFloatList.cpp
@@ -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 + "'";
diff --git a/src/insets/InsetFoot.cpp b/src/insets/InsetFoot.cpp
index 9df6f5d..e941f4a 100644
--- a/src/insets/InsetFoot.cpp
+++ b/src/insets/InsetFoot.cpp
@@ -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);
 }
diff --git a/src/insets/InsetGraphics.cpp b/src/insets/InsetGraphics.cpp
index ac9d080..bc82db7 100644
--- a/src/insets/InsetGraphics.cpp
+++ b/src/insets/InsetGraphics.cpp
@@ -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));
 }
 
 
diff --git a/src/insets/InsetInclude.cpp b/src/insets/InsetInclude.cpp
index 2631ecf..3883d22 100644
--- a/src/insets/InsetInclude.cpp
+++ b/src/insets/InsetInclude.cpp
@@ -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());
 }
 
 
diff --git a/src/insets/InsetIndex.cpp b/src/insets/InsetIndex.cpp
index d485a11..cb2704f 100644
--- a/src/insets/InsetIndex.cpp
+++ b/src/insets/InsetIndex.cpp
@@ -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())
diff --git a/src/insets/InsetLabel.cpp b/src/insets/InsetLabel.cpp
index 35d5f54..82923ff 100644
--- a/src/insets/InsetLabel.cpp
+++ b/src/insets/InsetLabel.cpp
@@ -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));
+		}
 	}
 }
 
diff --git a/src/insets/InsetListings.cpp b/src/insets/InsetListings.cpp
index 97cb04a..879b205 100644
--- a/src/insets/InsetListings.cpp
+++ b/src/insets/InsetListings.cpp
@@ -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;
 			}
diff --git a/src/insets/InsetMarginal.cpp b/src/insets/InsetMarginal.cpp
index 996d6ec..2970fcb 100644
--- a/src/insets/InsetMarginal.cpp
+++ b/src/insets/InsetMarginal.cpp
@@ -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);
 }
diff --git a/src/insets/InsetNomencl.cpp b/src/insets/InsetNomencl.cpp
index f7f740a..953eeea 100644
--- a/src/insets/InsetNomencl.cpp
+++ b/src/insets/InsetNomencl.cpp
@@ -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();
diff --git a/src/insets/InsetNote.cpp b/src/insets/InsetNote.cpp
index a80e1be..c1bb07a 100644
--- a/src/insets/InsetNote.cpp
+++ b/src/insets/InsetNote.cpp
@@ -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();
diff --git a/src/insets/InsetRef.cpp b/src/insets/InsetRef.cpp
index bd6b2ec..c0de3ba 100644
--- a/src/insets/InsetRef.cpp
+++ b/src/insets/InsetRef.cpp
@@ -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));
 }
 
 
diff --git a/src/insets/InsetTOC.cpp b/src/insets/InsetTOC.cpp
index d413892..76a8dc9 100644
--- a/src/insets/InsetTOC.cpp
+++ b/src/insets/InsetTOC.cpp
@@ -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();
diff --git a/src/insets/InsetText.cpp b/src/insets/InsetText.cpp
index 0073642..0558c27 100644
--- a/src/insets/InsetText.cpp
+++ b/src/insets/InsetText.cpp
@@ -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.
diff --git a/src/mathed/InsetMathHull.cpp b/src/mathed/InsetMathHull.cpp
index 442d7f3..485d62b 100644
--- a/src/mathed/InsetMathHull.cpp
+++ b/src/mathed/InsetMathHull.cpp
@@ -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));
 	}
 }
 
-- 
2.1.4

>From db483a7e2b6fa2e57bd280ac59984906096ade77 Mon Sep 17 00:00:00 2001
From: Guillaume Munch <g...@lyx.org>
Date: Mon, 31 Aug 2015 03:43:30 +0100
Subject: [PATCH 2/4] Do not show subfigures in the menu (bug #9759)

* More consistent behavour (no 30 elements limit)
* Subfloats are given depth 1
* Only show depth 0
* Always show "Open outliner..."
---
 src/frontends/qt4/Menus.cpp | 50 ++++++++++++++++++++++++++++++++-------------
 src/insets/InsetCaption.cpp |  8 ++++----
 src/insets/InsetCaption.h   |  2 ++
 3 files changed, 42 insertions(+), 18 deletions(-)

diff --git a/src/frontends/qt4/Menus.cpp b/src/frontends/qt4/Menus.cpp
index d0b5d91..b8b8c31 100644
--- a/src/frontends/qt4/Menus.cpp
+++ b/src/frontends/qt4/Menus.cpp
@@ -1312,27 +1312,49 @@ void MenuDefinition::expandToc(Buffer const * buf)
 			continue;
 
 		MenuDefinition submenu;
-		if (cit->second.size() >= 30) {
-			FuncRequest f(LFUN_DIALOG_SHOW, "toc " + cit->first);
-			submenu.add(MenuItem(MenuItem::Command, qt_("Open Navigator..."), f));
-		} else {
+		if (floatlist.typeExist(cit->first)) {
 			TocIterator ccit = cit->second.begin();
 			TocIterator eend = cit->second.end();
 			for (; ccit != eend; ++ccit) {
-				submenu.add(MenuItem(MenuItem::Command,
-					limitStringLength(ccit->str()) + '|',
-					FuncRequest(ccit->action())));
+				if (0 == ccit->depth()) {// omit subfloats
+					submenu.add(MenuItem(MenuItem::Command,
+										 limitStringLength(ccit->str()) + '|',
+										 FuncRequest(ccit->action())));
+				}
 			}
-		}
 
-		MenuItem item(MenuItem::Submenu, guiName(cit->first, buf->params()));
-		item.setSubmenu(submenu);
-		if (floatlist.typeExist(cit->first) || cit->first == "child") {
-			// Those two types deserve to be in the main menu.
+			FuncRequest f(LFUN_DIALOG_SHOW, "toc " + cit->first);
+			submenu.add(MenuItem(MenuItem::Separator));
+			submenu.add(MenuItem(MenuItem::Command, qt_("Open Navigator..."), f));
+			MenuItem item(MenuItem::Submenu, guiName(cit->first, buf->params()));
+			// deserves to be in the main menu.
 			item.setSubmenu(submenu);
 			add(item);
-		} else
-			other_lists.add(item);
+		} else {
+			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();
+				for (; ccit != eend; ++ccit) {
+					submenu.add(MenuItem(MenuItem::Command,
+										 limitStringLength(ccit->str()) + '|',
+										 FuncRequest(ccit->action())));
+				}
+			}
+
+			MenuItem item(MenuItem::Submenu, guiName(cit->first, buf->params()));
+			item.setSubmenu(submenu);
+			if (cit->first == "child") {
+				// deserves to be in the main menu.
+				add(item);
+			} else
+				other_lists.add(item);
+		}
 	}
 	if (!other_lists.empty()) {
 		MenuItem item(MenuItem::Submenu, qt_("Other Lists"));
diff --git a/src/insets/InsetCaption.cpp b/src/insets/InsetCaption.cpp
index 2465e7a..b832469 100644
--- a/src/insets/InsetCaption.cpp
+++ b/src/insets/InsetCaption.cpp
@@ -102,7 +102,7 @@ void InsetCaption::addToToc(DocIterator const & cpit, bool output_active) const
 	docstring str = full_label_;
 	int length = output_active ? INT_MAX : TOC_ENTRY_LENGTH;
 	text().forOutliner(str, length);
-	toc.push_back(TocItem(pit, 0, str, output_active));
+	toc.push_back(TocItem(pit, is_subfloat_ ? 1 : 0, str, output_active));
 
 	// Proceed with the rest of the inset.
 	InsetText::addToToc(cpit, output_active);
@@ -379,7 +379,7 @@ void InsetCaption::updateBuffer(ParIterator const & it, UpdateType utype)
 		else
 			name = master.B_(tclass.floats().getType(type).name());
 		docstring counter = from_utf8(type);
-		if (cnts.isSubfloat()) {
+		if ((is_subfloat_ = cnts.isSubfloat())) {
 			// only standard captions allowed in subfloats
 			type_ = "Standard";
 			counter = "sub-" + from_utf8(type);
@@ -402,9 +402,9 @@ void InsetCaption::updateBuffer(ParIterator const & it, UpdateType utype)
 			sec += bformat(from_ascii("(%1$s)"), labelstring);
 		}
 		if (!sec.empty())
-			full_label_ = bformat(from_ascii("%1$s %2$s:"), name, sec);
+			full_label_ = bformat(from_ascii("%1$s %2$s: "), name, sec);
 		else
-			full_label_ = bformat(from_ascii("%1$s #:"), name);
+			full_label_ = bformat(from_ascii("%1$s #: "), name);
 	}
 
 	// Do the real work now.
diff --git a/src/insets/InsetCaption.h b/src/insets/InsetCaption.h
index 26b3b26..97f2691 100644
--- a/src/insets/InsetCaption.h
+++ b/src/insets/InsetCaption.h
@@ -96,6 +96,8 @@ private:
 	///
 	std::string floattype_;
 	///
+	bool is_subfloat_;
+	///
 	std::string type_;
 	///
 	docstring custom_label_;
-- 
2.1.4

>From 0ca6709a2bc1f8920d334020fb127688c242556f Mon Sep 17 00:00:00 2001
From: Guillaume Munch <g...@lyx.org>
Date: Tue, 1 Sep 2015 17:47:01 +0100
Subject: [PATCH 1/4] Support for (std|boost)::make_shared

---
 src/support/shared_ptr.h | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/src/support/shared_ptr.h b/src/support/shared_ptr.h
index d011597..bb613de 100644
--- a/src/support/shared_ptr.h
+++ b/src/support/shared_ptr.h
@@ -20,6 +20,7 @@
 #else
 
 #include <boost/shared_ptr.hpp>
+#include <boost/make_shared.hpp>
 #define LYX_SHAREDPTR_NS boost
 
 #endif
@@ -27,6 +28,7 @@
 namespace lyx
 {
 	using LYX_SHAREDPTR_NS::shared_ptr;
+	using LYX_SHAREDPTR_NS::make_shared;
 	using LYX_SHAREDPTR_NS::const_pointer_cast;
 }
 
-- 
2.1.4

Reply via email to