The attached patch almost works. (Details like the icon are not yet there.) There are some big problems, however, which seem to derive from the fact that the whole menu and toolbar setup is not really designed to be modified on the fly.

The problem here seems to trace to how FuncRequest objects are handled by our Action class. Namely, when an Action is created, it retains a const & to a FuncRequest. So one can't pass a temporary. This means that I have to create a kind of cache for these objects. The idea was to keep them around as long as we need them. But it turns out that we need them rather a long time. If you look at the FIXME in the patch, you will see what I mean. Clearing the menu is supposed to delete any actions it owns:

void QMenu::clear ()
Removes all the menu's actions. Actions owned by the menu and not shown in any other widget are deleted.

In fact, however, they seem still to be around, and if we clear the cache we eventually segfault. If we don't clear the cache, we are effectively leaking memory.

I can make this work by changing the Action class so it makes a copy of the FuncRequest. But I worry that this is just hiding the real problem, and that all these Action objects we're creating are leaking, or at least being retained for no good reason.

Help wanted!

Richard

>From 13a9c8f7571f4451e3ffef16dc19bdd08be86add Mon Sep 17 00:00:00 2001
From: Richard Heck <rgh...@lyx.org>
Date: Fri, 24 Jun 2016 17:47:30 -0400
Subject: [PATCH] Attempt to do the inset thing with a menu.

---
 lib/ui/stdtoolbars.inc           |   2 +
 src/frontends/qt4/GuiToolbar.cpp | 120 +++++++++++++++++++++++++++++++++++++++
 src/frontends/qt4/GuiToolbar.h   |  24 ++++++++
 src/frontends/qt4/GuiView.cpp    |   2 +-
 src/frontends/qt4/GuiView.h      |   2 +
 src/frontends/qt4/Toolbars.cpp   |   9 ++-
 src/frontends/qt4/Toolbars.h     |   4 +-
 7 files changed, 160 insertions(+), 3 deletions(-)

diff --git a/lib/ui/stdtoolbars.inc b/lib/ui/stdtoolbars.inc
index d375afa..c504e4a 100644
--- a/lib/ui/stdtoolbars.inc
+++ b/lib/ui/stdtoolbars.inc
@@ -95,6 +95,8 @@ ToolbarSet
 		Item "Toggle outline" "dialog-toggle toc"
 		Item "Toggle math toolbar" "toolbar-toggle math"
 		Item "Toggle table toolbar" "toolbar-toggle table"
+		Separator
+		Insets
 	End
 
 	Toolbar "view/update" "View/Update"
diff --git a/src/frontends/qt4/GuiToolbar.cpp b/src/frontends/qt4/GuiToolbar.cpp
index 77471c9..094617d 100644
--- a/src/frontends/qt4/GuiToolbar.cpp
+++ b/src/frontends/qt4/GuiToolbar.cpp
@@ -25,19 +25,27 @@
 #include "InsertTableWidget.h"
 #include "LayoutBox.h"
 #include "qt_helpers.h"
+#include "TextClass.h"
 #include "Toolbars.h"
 
+#include "Buffer.h"
+#include "BufferParams.h"
+#include "BufferView.h"
+#include "Cursor.h"
 #include "FuncRequest.h"
 #include "FuncStatus.h"
 #include "KeyMap.h"
 #include "LyX.h"
 #include "LyXRC.h"
 #include "Session.h"
+#include "Text.h"
 
 #include "support/debug.h"
 #include "support/gettext.h"
 #include "support/lstrings.h"
 
+#include <list>
+
 #include <QSettings>
 #include <QShowEvent>
 #include <QString>
@@ -263,6 +271,114 @@ void MenuButton::updateTriggered()
 }
 
 
+class InsetMenuButton::Private
+{
+	/// noncopyable
+	Private(Private const &);
+	void operator=(Private const &);
+public:
+	Private() : inset_(0)
+	{}
+
+	/// since Action retains a reference to the
+	/// FuncRequest, we need to keep these around
+	typedef std::list<FuncRequest> FuncCache;
+	///
+	DocumentClassConstPtr text_class_;
+	///
+	InsetText const * inset_;
+	///
+	FuncCache fcache_;
+};
+
+
+InsetMenuButton::InsetMenuButton(GuiToolbar * bar)
+	: QToolButton(bar), bar_(bar), d(new Private())
+{
+	setPopupMode(QToolButton::InstantPopup);
+	QString const label = qt_("Add Custom Inset");
+	setToolTip(label);
+	setStatusTip(label);
+	setText(label);
+	connect(bar, SIGNAL(iconSizeChanged(QSize)),
+		this, SLOT(setIconSize(QSize)));
+	initialize();
+}
+
+
+void InsetMenuButton::initialize()
+{
+	QString const label = qt_("Add Custom Inset");
+
+	QMenu * m = new QMenu(label, this);
+	m->setWindowTitle(label);
+	m->setTearOffEnabled(true);
+	setMenu(m);
+
+	connect(bar_, SIGNAL(updated()), this, SLOT(updateTriggered()));
+	updateTriggered();
+}
+
+
+void InsetMenuButton::updateTriggered()
+{
+	GuiView const & owner = bar_->owner();
+	BufferView const * bv = owner.currentBufferView();
+	QMenu * m = menu();
+	if (!bv) {
+		if (m)
+			m->clear();
+		setEnabled(false);
+		setMinimumWidth(sizeHint().width());
+		d->text_class_.reset();
+		d->inset_ = 0;
+			d->fcache_.clear();
+		return;
+    }
+    
+	// we'll only update the inset list if the text class has changed
+	// or we've moved from one inset to another
+	DocumentClassConstPtr text_class = bv->buffer().params().documentClassPtr();
+	InsetText const * inset = &(bv->cursor().innerText()->inset());
+	if (d->text_class_ == text_class && d->inset_ == inset)
+		return;
+    
+	if (m)
+		m->clear();
+	d->inset_ = inset;
+	d->text_class_ = text_class;
+	// FIXME If this is uncommented, then we crash.
+	// Presumably, the Action objects are still active
+	// for some reason, even though they have been
+	// removed from the menu.
+	//d->fcache_.clear();
+	
+	TextClass::InsetLayouts const & insetLayouts = d->text_class_->insetLayouts();
+	TextClass::InsetLayouts::const_iterator iit = insetLayouts.begin();
+	TextClass::InsetLayouts::const_iterator ien = insetLayouts.end();
+	
+	for (; iit != ien; ++iit) {
+		InsetLayout const & il = iit->second;
+		if (il.lyxtype() != InsetLayout::CUSTOM)
+			continue;
+        
+		docstring const name = iit->first;
+		QString const loc_item = toqstr(translateIfPossible(
+				prefixIs(name, from_ascii("Flex:")) ? 
+				name.substr(5) : name));
+
+		FuncRequest const func(LFUN_FLEX_INSERT, 
+			from_ascii("\"") + name + from_ascii("\""), FuncRequest::TOOLBAR);
+		d->fcache_.push_back(func);
+		FuncRequest const & cfunc = d->fcache_.back();
+		Action * act = 
+			new Action(getIcon(cfunc, false), loc_item, cfunc, loc_item, this);
+		m->addAction(act);
+	}
+	setEnabled(!d->fcache_.empty());
+}
+
+
 void GuiToolbar::add(ToolbarItem const & item)
 {
 	switch (item.type_) {
@@ -277,6 +393,10 @@ void GuiToolbar::add(ToolbarItem const & item)
 		action->setVisible(true);
 		break;
 	}
+	case ToolbarItem::INSETS: {
+		addWidget(new InsetMenuButton(this));
+		break;
+	}
 	case ToolbarItem::MINIBUFFER:
 		command_buffer_ = new GuiCommandBuffer(&owner_);
 		addWidget(command_buffer_);
diff --git a/src/frontends/qt4/GuiToolbar.h b/src/frontends/qt4/GuiToolbar.h
index caad355..83d5b29 100644
--- a/src/frontends/qt4/GuiToolbar.h
+++ b/src/frontends/qt4/GuiToolbar.h
@@ -61,6 +61,28 @@ private Q_SLOTS:
 
 
 
+class InsetMenuButton : public QToolButton
+{
+	Q_OBJECT
+public:
+	///
+	InsetMenuButton(GuiToolbar * bar);
+
+private:
+	///
+	void initialize();
+	///
+	GuiToolbar * bar_;
+	///
+	class Private;
+	Private * d;
+
+private Q_SLOTS:
+	///
+	void updateTriggered();
+};
+
+
 class GuiToolbar : public QToolBar
 {
 	Q_OBJECT
@@ -102,6 +124,8 @@ public:
 
 	///
 	Action * addItem(ToolbarItem const & item);
+    ///
+    GuiView const & owner() { return owner_; }
 
 Q_SIGNALS:
 	///
diff --git a/src/frontends/qt4/GuiView.cpp b/src/frontends/qt4/GuiView.cpp
index 87ee553..fbfbb53 100644
--- a/src/frontends/qt4/GuiView.cpp
+++ b/src/frontends/qt4/GuiView.cpp
@@ -1344,7 +1344,7 @@ void GuiView::setBusy(bool busy)
 		return;
 	}
 	QApplication::restoreOverrideCursor();
-	updateLayoutList();	
+	updateLayoutList();
 }
 
 
diff --git a/src/frontends/qt4/GuiView.h b/src/frontends/qt4/GuiView.h
index c3ccdbd..eed736b 100644
--- a/src/frontends/qt4/GuiView.h
+++ b/src/frontends/qt4/GuiView.h
@@ -46,6 +46,7 @@ class Dialog;
 class LayoutBox;
 class GuiToolbar;
 class GuiWorkArea;
+class InsetCombo;
 class TabWorkArea;
 class TocModels;
 class ToolbarInfo;
@@ -149,6 +150,7 @@ public:
 
 	///
 	LayoutBox * getLayoutDialog() const;
+	InsetCombo * getInsetCombo() const;
 
 	/// hides the workarea and makes sure it is clean
 	bool hideWorkArea(GuiWorkArea * wa);
diff --git a/src/frontends/qt4/Toolbars.cpp b/src/frontends/qt4/Toolbars.cpp
index 20eee93..05f3269 100644
--- a/src/frontends/qt4/Toolbars.cpp
+++ b/src/frontends/qt4/Toolbars.cpp
@@ -74,7 +74,8 @@ ToolbarInfo & ToolbarInfo::read(Lexer & lex)
 		TO_EXPORTFORMATS,
 		TO_IMPORTFORMATS,
 		TO_UPDATEFORMATS,
-		TO_VIEWFORMATS
+		TO_VIEWFORMATS,
+		TO_INSETS
 	};
 
 	struct LexerKeyword toolTags[] = {
@@ -82,6 +83,7 @@ ToolbarInfo & ToolbarInfo::read(Lexer & lex)
 		{ "exportformats", TO_EXPORTFORMATS },
 		{ "iconpalette", TO_ICONPALETTE },
 		{ "importformats", TO_IMPORTFORMATS },
+		{ "insets", TO_INSETS },
 		{ "item", TO_COMMAND },
 		{ "layouts", TO_LAYOUTS },
 		{ "minibuffer", TO_MINIBUFFER },
@@ -178,6 +180,11 @@ ToolbarInfo & ToolbarInfo::read(Lexer & lex)
 				FuncRequest(FuncCode(ToolbarItem::LAYOUTS))));
 			break;
 
+		case TO_INSETS: {
+			add(ToolbarItem(ToolbarItem::INSETS, "Hi"));
+			break;
+        }
+
 		case TO_TABLEINSERT:
 			if (lex.next(true)) {
 				docstring const tooltip = lex.getDocString();
diff --git a/src/frontends/qt4/Toolbars.h b/src/frontends/qt4/Toolbars.h
index 02d0ebe..01d4679 100644
--- a/src/frontends/qt4/Toolbars.h
+++ b/src/frontends/qt4/Toolbars.h
@@ -43,7 +43,9 @@ public:
 		/// a button that expands a menu but remembers the last choice
 		STICKYPOPUPMENU,
 		///
-		ICONPALETTE
+		ICONPALETTE,
+		///
+		INSETS
 	};
 
 	ToolbarItem(Type type,
-- 
2.5.0

Reply via email to