From e5657838a6fffb19efe6fc565d4e1ee9f0b226a0 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Daniel=20M=C3=BCller?= <d_muel20@uni-muenster.de>
Date: Thu, 19 Sep 2019 12:23:56 +0200
Subject: [PATCH] * Further minor bug fixes

---
 Grinder/Version.h                             |   4 +-
 .../engine/processors/GrabCutProcessor.cpp    |   2 +-
 .../engine/processors/ImageTagsProcessor.cpp  |   2 +-
 Grinder/image/ImageBuild.cpp                  |   4 +-
 Grinder/image/Layer.cpp                       |   3 +
 Grinder/ml/blocks/InferenceBlock.cpp          |   1 +
 .../MachineLearningMethodProcessor.impl.h     |   2 +-
 .../MachineLearningProcessor.impl.h           |   4 +-
 .../editors/BaristaNetworkPropertyEditor.cpp  |   2 -
 .../widgets/BaristaNetworksComboBox.cpp       |   5 +
 .../barista/widgets/BaristaNetworksComboBox.h |   2 +-
 Grinder/ui/dlg/SplitConnectionDialog.cpp      | 140 +++++++++--------
 Grinder/ui/image/ImageEditorWidget.cpp        |   5 +-
 Grinder/ui/mainwnd/GrinderWindow.cpp          |   6 +-
 .../editors/ImageReferencePropertyEditor.cpp  | 148 +++++++++---------
 .../widgets/engine/CanvasBlocksComboBox.cpp   |   5 +
 .../ui/widgets/engine/CanvasBlocksComboBox.h  |   2 +-
 Grinder/ui/widgets/project/LabelsComboBox.cpp | 105 +++++++------
 Grinder/ui/widgets/project/LabelsComboBox.h   |  62 ++++----
 19 files changed, 266 insertions(+), 238 deletions(-)

diff --git a/Grinder/Version.h b/Grinder/Version.h
index 2937c61..cde3156 100644
--- a/Grinder/Version.h
+++ b/Grinder/Version.h
@@ -10,14 +10,14 @@
 
 #define GRNDR_INFO_TITLE		"Grinder"
 #define GRNDR_INFO_COPYRIGHT	"Copyright (c) WWU Muenster"
-#define GRNDR_INFO_DATE			"08.9.2019"
+#define GRNDR_INFO_DATE			"19.9.2019"
 #define GRNDR_INFO_COMPANY		"WWU Muenster"
 #define GRNDR_INFO_WEBSITE		"http://www.uni-muenster.de"
 
 #define GRNDR_VERSION_MAJOR		0
 #define GRNDR_VERSION_MINOR		15
 #define GRNDR_VERSION_REVISION	0
-#define GRNDR_VERSION_BUILD		391
+#define GRNDR_VERSION_BUILD		392
 
 namespace grndr
 {
diff --git a/Grinder/engine/processors/GrabCutProcessor.cpp b/Grinder/engine/processors/GrabCutProcessor.cpp
index 28a2f17..a68b68f 100644
--- a/Grinder/engine/processors/GrabCutProcessor.cpp
+++ b/Grinder/engine/processors/GrabCutProcessor.cpp
@@ -23,7 +23,7 @@ const char* GrabCutProcessor::Data_Value_FGModel = "FGModel";
 
 GrabCutProcessor::GrabCutProcessor(const Block* block) : Processor(block)
 {
-
+	qRegisterMetaType<cv::Mat>();
 }
 
 void GrabCutProcessor::execute(EngineExecutionContext& ctx)
diff --git a/Grinder/engine/processors/ImageTagsProcessor.cpp b/Grinder/engine/processors/ImageTagsProcessor.cpp
index 8d8838c..412222a 100644
--- a/Grinder/engine/processors/ImageTagsProcessor.cpp
+++ b/Grinder/engine/processors/ImageTagsProcessor.cpp
@@ -12,7 +12,7 @@ const char* ImageTagsProcessor::Data_Value_ImageTags = "ImageTags";
 
 ImageTagsProcessor::ImageTagsProcessor(const Block* block) : Processor(block)
 {
-
+	qRegisterMetaType<const ImageTags*>();
 }
 
 void ImageTagsProcessor::execute(EngineExecutionContext& ctx)
diff --git a/Grinder/image/ImageBuild.cpp b/Grinder/image/ImageBuild.cpp
index 3c3a349..173af86 100644
--- a/Grinder/image/ImageBuild.cpp
+++ b/Grinder/image/ImageBuild.cpp
@@ -29,7 +29,9 @@ ImageBuild::ImageBuild(const Block* block, const std::vector<const ImageReferenc
 	if (imageReferences.empty())
 		throw std::invalid_argument{_EXCPT("imageReferences may not be empty")};
 
-	qRegisterMetaType<Connection*>();
+	qRegisterMetaType<const Block*>();
+	qRegisterMetaType<const ImageReference*>();
+	qRegisterMetaType<const Connection*>();
 
 	// Listen for connection events on the image tags in-port
 	if (auto imageTagsInPort = _block->ports().selectByType(PortType::ImageTagsIn))
diff --git a/Grinder/image/Layer.cpp b/Grinder/image/Layer.cpp
index 385ed27..d22f0fc 100644
--- a/Grinder/image/Layer.cpp
+++ b/Grinder/image/Layer.cpp
@@ -33,6 +33,9 @@ Layer::Layer(ImageBuild* imageBuild, QString name, Type type, Flags flags, const
 	if (type == Type::AutoGenerated && !generatingBlock)
 		throw std::invalid_argument{_EXCPT("generatingBlock may not be null")};
 
+	qRegisterMetaType<const Block*>();
+	qRegisterMetaType<const ImageReference*>();
+
 	checkPointers();
 
 	if (_type == Type::AutoGenerated)
diff --git a/Grinder/ml/blocks/InferenceBlock.cpp b/Grinder/ml/blocks/InferenceBlock.cpp
index 08c3950..d4b0403 100644
--- a/Grinder/ml/blocks/InferenceBlock.cpp
+++ b/Grinder/ml/blocks/InferenceBlock.cpp
@@ -24,6 +24,7 @@ void InferenceBlock::initBlock()
 	MachineLearningBlock::initBlock();
 
 	// Listen for various events in order to create the dynamic out-ports
+	qRegisterMetaType<std::shared_ptr<ImageTag>>();
 	qRegisterMetaType<const Connection*>();
 
 	connect(_imageTagsPort.get(), &Port::portConnected, this, &InferenceBlock::imageTagsConnected);
diff --git a/Grinder/ml/processors/MachineLearningMethodProcessor.impl.h b/Grinder/ml/processors/MachineLearningMethodProcessor.impl.h
index 9c88e62..67ae199 100644
--- a/Grinder/ml/processors/MachineLearningMethodProcessor.impl.h
+++ b/Grinder/ml/processors/MachineLearningMethodProcessor.impl.h
@@ -17,7 +17,7 @@ const char* MachineLearningMethodProcessor<MethodType>::Data_Value_State = "Stat
 template<typename MethodType>
 MachineLearningMethodProcessor<MethodType>::MachineLearningMethodProcessor(const Block* block) : Processor<MachineLearningMethodBlock<MethodType>>(block)
 {
-
+	qRegisterMetaType<const MachineLearningMethodBase*>();
 }
 
 template<typename MethodType>
diff --git a/Grinder/ml/processors/MachineLearningProcessor.impl.h b/Grinder/ml/processors/MachineLearningProcessor.impl.h
index 9cf296e..be87884 100644
--- a/Grinder/ml/processors/MachineLearningProcessor.impl.h
+++ b/Grinder/ml/processors/MachineLearningProcessor.impl.h
@@ -25,7 +25,9 @@ template<typename BlockType>
 MachineLearningProcessor<BlockType>::MachineLearningProcessor(const Block* block, MachineLearningProcessor::SpawnType spawnType, bool requiresBatchMode) : PipelineTaskProcessor<BlockType, MachineLearningTask, MachineLearningTaskData>(block, requiresBatchMode),
 	_spawnType{spawnType}
 {
-
+	qRegisterMetaType<const MachineLearningMethodBase*>();
+	qRegisterMetaType<const ImageTags*>();
+	qRegisterMetaType<std::shared_ptr<MachineLearningTask>>();
 }
 
 template<typename BlockType>
diff --git a/Grinder/ui/barista/editors/BaristaNetworkPropertyEditor.cpp b/Grinder/ui/barista/editors/BaristaNetworkPropertyEditor.cpp
index f2e1d24..a3c8ce3 100644
--- a/Grinder/ui/barista/editors/BaristaNetworkPropertyEditor.cpp
+++ b/Grinder/ui/barista/editors/BaristaNetworkPropertyEditor.cpp
@@ -7,8 +7,6 @@
 #include "BaristaNetworkPropertyEditor.h"
 #include "core/GrinderApplication.h"
 
-Q_DECLARE_METATYPE(ImageReference*)
-
 BaristaNetworkPropertyEditor::BaristaNetworkPropertyEditor(BaristaNetworkProperty* property, QWidget *parent) : PropertyEditor(property, parent)
 {
 	// Fill the network list
diff --git a/Grinder/ui/barista/widgets/BaristaNetworksComboBox.cpp b/Grinder/ui/barista/widgets/BaristaNetworksComboBox.cpp
index 5afabbb..01598a5 100644
--- a/Grinder/ui/barista/widgets/BaristaNetworksComboBox.cpp
+++ b/Grinder/ui/barista/widgets/BaristaNetworksComboBox.cpp
@@ -9,6 +9,11 @@
 
 Q_DECLARE_METATYPE(BaristaNetwork*);
 
+BaristaNetworksComboBox::BaristaNetworksComboBox(QWidget* parent) : QComboBox(parent)
+{
+	qRegisterMetaType<BaristaNetwork*>();
+}
+
 void BaristaNetworksComboBox::populate()
 {
 	clear();
diff --git a/Grinder/ui/barista/widgets/BaristaNetworksComboBox.h b/Grinder/ui/barista/widgets/BaristaNetworksComboBox.h
index 813bb0a..51c7d31 100644
--- a/Grinder/ui/barista/widgets/BaristaNetworksComboBox.h
+++ b/Grinder/ui/barista/widgets/BaristaNetworksComboBox.h
@@ -17,7 +17,7 @@ namespace grndr
 		Q_OBJECT
 
 	public:
-		using QComboBox::QComboBox;
+		BaristaNetworksComboBox(QWidget *parent = nullptr);
 
 	public:
 		void populate();
diff --git a/Grinder/ui/dlg/SplitConnectionDialog.cpp b/Grinder/ui/dlg/SplitConnectionDialog.cpp
index def51d6..25d15d7 100644
--- a/Grinder/ui/dlg/SplitConnectionDialog.cpp
+++ b/Grinder/ui/dlg/SplitConnectionDialog.cpp
@@ -1,69 +1,71 @@
-/******************************************************************************
- * File: SplitConnectionDialog.cpp
- * Date: 24.7.2018
- *****************************************************************************/
-
-#include "Grinder.h"
-#include "SplitConnectionDialog.h"
-#include "ui_SplitConnectionDialog.h"
-#include "pipeline/Block.h"
-
-Q_DECLARE_METATYPE(Port*)
-
-SplitConnectionDialog::SplitConnectionDialog(const Block* sourceBlock, const Block* destBlock, const Block* insertBlock, const Port* sourcePort, const Port* destPort, QWidget *parent) : QDialog(parent, Qt::Dialog|Qt::WindowTitleHint|Qt::WindowCloseButtonHint),
-	ui{new Ui::SplitConnectionDialog}
-{
-	setupUi();
-
-	fillPorts(sourceBlock, Port::Direction::Out, ui->lstSourcePort, sourcePort);
-	fillPorts(destBlock, Port::Direction::In, ui->lstDestPort, destPort);
-	fillPorts(insertBlock, Port::Direction::In, ui->lstIncomingPort);
-	fillPorts(insertBlock, Port::Direction::Out, ui->lstOutgoingPort);
-}
-
-SplitConnectionDialog::~SplitConnectionDialog()
-{
-	delete ui;
-}
-
-Port* SplitConnectionDialog::sourcePort() const
-{
-	return ui->lstSourcePort->currentData().value<Port*>();
-}
-
-Port* SplitConnectionDialog::destPort() const
-{
-	return ui->lstDestPort->currentData().value<Port*>();
-}
-
-Port* SplitConnectionDialog::incomingPort() const
-{
-	return ui->lstIncomingPort->currentData().value<Port*>();
-}
-
-Port* SplitConnectionDialog::outgoingPort() const
-{
-	return ui->lstOutgoingPort->currentData().value<Port*>();
-}
-
-void SplitConnectionDialog::fillPorts(const Block* block, Port::Direction portDir, QComboBox* comboBox, const Port* defaultPort)
-{
-	for (auto& port : block->ports().selectByDirection(portDir))
-	{
-		comboBox->addItem(port->getFormattedName(), QVariant::fromValue(port.get()));
-
-		if (port.get() == defaultPort)
-			comboBox->setCurrentIndex(comboBox->count() - 1);
-	}
-
-	if (comboBox->count() == 0)
-	{
-		comboBox->addItem("No ports to display", QVariant::fromValue<Port*>(nullptr));
-		comboBox->setEnabled(false);
-	}
-}
-
-void SplitConnectionDialog::setupUi()
-{
-	ui->setupUi(this);
-}
+/******************************************************************************
+ * File: SplitConnectionDialog.cpp
+ * Date: 24.7.2018
+ *****************************************************************************/
+
+#include "Grinder.h"
+#include "SplitConnectionDialog.h"
+#include "ui_SplitConnectionDialog.h"
+#include "pipeline/Block.h"
+
+Q_DECLARE_METATYPE(Port*)
+
+SplitConnectionDialog::SplitConnectionDialog(const Block* sourceBlock, const Block* destBlock, const Block* insertBlock, const Port* sourcePort, const Port* destPort, QWidget *parent) : QDialog(parent, Qt::Dialog|Qt::WindowTitleHint|Qt::WindowCloseButtonHint),
+	ui{new Ui::SplitConnectionDialog}
+{
+	qRegisterMetaType<Port*>();
+
+	setupUi();
+
+	fillPorts(sourceBlock, Port::Direction::Out, ui->lstSourcePort, sourcePort);
+	fillPorts(destBlock, Port::Direction::In, ui->lstDestPort, destPort);
+	fillPorts(insertBlock, Port::Direction::In, ui->lstIncomingPort);
+	fillPorts(insertBlock, Port::Direction::Out, ui->lstOutgoingPort);
+}
+
+SplitConnectionDialog::~SplitConnectionDialog()
+{
+	delete ui;
+}
+
+Port* SplitConnectionDialog::sourcePort() const
+{
+	return ui->lstSourcePort->currentData().value<Port*>();
+}
+
+Port* SplitConnectionDialog::destPort() const
+{
+	return ui->lstDestPort->currentData().value<Port*>();
+}
+
+Port* SplitConnectionDialog::incomingPort() const
+{
+	return ui->lstIncomingPort->currentData().value<Port*>();
+}
+
+Port* SplitConnectionDialog::outgoingPort() const
+{
+	return ui->lstOutgoingPort->currentData().value<Port*>();
+}
+
+void SplitConnectionDialog::fillPorts(const Block* block, Port::Direction portDir, QComboBox* comboBox, const Port* defaultPort)
+{
+	for (auto& port : block->ports().selectByDirection(portDir))
+	{
+		comboBox->addItem(port->getFormattedName(), QVariant::fromValue(port.get()));
+
+		if (port.get() == defaultPort)
+			comboBox->setCurrentIndex(comboBox->count() - 1);
+	}
+
+	if (comboBox->count() == 0)
+	{
+		comboBox->addItem("No ports to display", QVariant::fromValue<Port*>(nullptr));
+		comboBox->setEnabled(false);
+	}
+}
+
+void SplitConnectionDialog::setupUi()
+{
+	ui->setupUi(this);
+}
diff --git a/Grinder/ui/image/ImageEditorWidget.cpp b/Grinder/ui/image/ImageEditorWidget.cpp
index 14632a2..4d823f4 100644
--- a/Grinder/ui/image/ImageEditorWidget.cpp
+++ b/Grinder/ui/image/ImageEditorWidget.cpp
@@ -30,6 +30,8 @@ ImageEditorWidget::ImageEditorWidget(ImageEditor* imageEditor, QWidget* parent)
 	if (!imageEditor)
 		throw std::runtime_error{_EXCPT("imageEditor may not be null")};	
 
+	qRegisterMetaType<std::shared_ptr<ImageReference>>();
+
 	// Create editor actions
 	_copyImageBuildAction = UIUtils::createAction(this, "&Copy image build", FILE_ICON_COPY, SLOT(copyImageBuild()), "Copy the current image build to the clipboard (Ctrl+Shift+C)", "Ctrl+Shift+C", Qt::WidgetWithChildrenShortcut);
 	_pasteImageBuildAction = UIUtils::createAction(this, "&Paste image build", FILE_ICON_PASTE, SLOT(pasteImageBuild()), "Paste an image build from the clipboard (Ctrl+Shift+V)", "Ctrl+Shift+V", Qt::WidgetWithChildrenShortcut);
@@ -46,13 +48,10 @@ ImageEditorWidget::ImageEditorWidget(ImageEditor* imageEditor, QWidget* parent)
 
 	setupUi();	
 
-
 	// Reflect primary color changes
 	connect(&_imageEditor->environment(), &ImageEditorEnvironment::primaryColorChanged, this, &ImageEditorWidget::primaryColorChanged);
 
 	// Listen for various events to update our actions
-	qRegisterMetaType<std::shared_ptr<ImageReference>>();
-
 	connect(&grinder()->project(), SIGNAL(imageReferenceCreated(const std::shared_ptr<ImageReference>&)), this, SLOT(updateActions()));
 	connect(&grinder()->project(), SIGNAL(imageReferenceRemoved(const std::shared_ptr<ImageReference>&)), this, SLOT(updateActions()), Qt::QueuedConnection);	// Delay this signal so that the image reference has been removed from the image references list
 	connect(&grinder()->clipboardManager(), &ClipboardManager::dataChanged, this, &ImageEditorWidget::updateActions);
diff --git a/Grinder/ui/mainwnd/GrinderWindow.cpp b/Grinder/ui/mainwnd/GrinderWindow.cpp
index 43ed580..32c3e56 100644
--- a/Grinder/ui/mainwnd/GrinderWindow.cpp
+++ b/Grinder/ui/mainwnd/GrinderWindow.cpp
@@ -22,10 +22,14 @@
 #include <opencv2/highgui.hpp>
 
 Q_DECLARE_METATYPE(std::shared_ptr<Block>)
+Q_DECLARE_METATYPE(std::shared_ptr<ImageReference>)
 
 GrinderWindow::GrinderWindow(QWidget* parent) : QMainWindow(parent),
 	ui{new Ui::GrinderWindow()}
 {
+	qRegisterMetaType<std::shared_ptr<Block>>();
+	qRegisterMetaType<std::shared_ptr<ImageReference>>();
+
 	setupUi();
 
 	// Connect signals to update our UI
@@ -35,7 +39,7 @@ GrinderWindow::GrinderWindow(QWidget* parent) : QMainWindow(parent),
 	connect(&grinder()->projectController(), SIGNAL(projectLoaded(QString)), this, SLOT(updateRecentProjects(QString)));
 	connect(&grinder()->projectController(), SIGNAL(projectSaved(QString)), this, SLOT(updateRecentProjects(QString)));
 	connect(&grinder()->project(), SIGNAL(imageReferenceCreated(const std::shared_ptr<ImageReference>&)), this, SLOT(updateActions()));
-	connect(&grinder()->project(), SIGNAL(imageReferenceRemoved(const std::shared_ptr<ImageReference>&)), this, SLOT(updateActions()));
+	connect(&grinder()->project(), SIGNAL(imageReferenceRemoved(const std::shared_ptr<ImageReference>&)), this, SLOT(updateActions()), Qt::QueuedConnection);	// Delay this signal so that the image has been removed
 
 	// React to setting changes
 	connect(&grinder()->settings(), &GrinderSettings::settingsChanged, this, &GrinderWindow::settingsChanged);
diff --git a/Grinder/ui/properties/editors/ImageReferencePropertyEditor.cpp b/Grinder/ui/properties/editors/ImageReferencePropertyEditor.cpp
index 66e75c0..334de09 100644
--- a/Grinder/ui/properties/editors/ImageReferencePropertyEditor.cpp
+++ b/Grinder/ui/properties/editors/ImageReferencePropertyEditor.cpp
@@ -1,73 +1,75 @@
-/******************************************************************************
- * File: ImageReferencePropertyEditor.cpp
- * Date: 12.4.2018
- *****************************************************************************/
-
-#include "Grinder.h"
-#include "ImageReferencePropertyEditor.h"
-#include "core/GrinderApplication.h"
-
-Q_DECLARE_METATYPE(ImageReference*)
-
-ImageReferencePropertyEditor::ImageReferencePropertyEditor(ImageReferenceProperty* property, QWidget *parent) : PropertyEditor(property, parent)
-{
-	// Fill the image references list
-	populateImageReferences();
-
-	// Update the property whenever the current index has been changed
-	connect(this, SIGNAL(currentIndexChanged(int)), this, SLOT(currentIndexChanged(int)));
-
-	// React to new or deleted image references
-	connect(&grinder()->project(), SIGNAL(imageReferenceCreated(const std::shared_ptr<ImageReference>&)), this, SLOT(populateImageReferences()));
-	connect(&grinder()->project(), SIGNAL(imageReferenceRemoved(const std::shared_ptr<ImageReference>&)), this, SLOT(populateImageReferences()));
-}
-
-void ImageReferencePropertyEditor::applyPropertyValue()
-{
-	bool itemSelected = false;
-
-	// Search for the corresponding image reference item in the list
-	if (auto imageRef = _property->getValue())
-	{
-		for (int i = 0; i < count(); ++i)
-		{
-			auto data = itemData(i);
-
-			if (data.value<ImageReference*>() == imageRef)
-			{
-				setCurrentIndex(i);
-				itemSelected = true;
-				break;
-			}
-		}
-	}
-
-	// If no item was selected, select the default (active image) one
-	if (!itemSelected)
-		setCurrentIndex(0);
-}
-
-void ImageReferencePropertyEditor::populateImageReferences()
-{
-	clear();
-
-	// The first item always represents the currently active image
-	addItem("<Active image>", QVariant::fromValue<ImageReference*>(nullptr));
-
-	auto boldFont = font();
-	boldFont.setBold(true);
-	setItemData(0, boldFont, Qt::FontRole);
-
-	insertSeparator(1);
-
-	// Add all image references
-	for (const auto& imageRef : grinder()->project().imageReferences().sorted())
-		addItem(imageRef->getImageFileName(), QVariant::fromValue(imageRef.get()));
-
-	applyPropertyValue();
-}
-
-void ImageReferencePropertyEditor::currentIndexChanged(int index)
-{
-	_property->setValue(itemData(index).value<ImageReference*>());
-}
+/******************************************************************************
+ * File: ImageReferencePropertyEditor.cpp
+ * Date: 12.4.2018
+ *****************************************************************************/
+
+#include "Grinder.h"
+#include "ImageReferencePropertyEditor.h"
+#include "core/GrinderApplication.h"
+
+Q_DECLARE_METATYPE(ImageReference*)
+
+ImageReferencePropertyEditor::ImageReferencePropertyEditor(ImageReferenceProperty* property, QWidget *parent) : PropertyEditor(property, parent)
+{
+	qRegisterMetaType<ImageReference*>();
+
+	// Fill the image references list
+	populateImageReferences();
+
+	// Update the property whenever the current index has been changed
+	connect(this, SIGNAL(currentIndexChanged(int)), this, SLOT(currentIndexChanged(int)));
+
+	// React to new or deleted image references
+	connect(&grinder()->project(), SIGNAL(imageReferenceCreated(const std::shared_ptr<ImageReference>&)), this, SLOT(populateImageReferences()));
+	connect(&grinder()->project(), SIGNAL(imageReferenceRemoved(const std::shared_ptr<ImageReference>&)), this, SLOT(populateImageReferences()));
+}
+
+void ImageReferencePropertyEditor::applyPropertyValue()
+{
+	bool itemSelected = false;
+
+	// Search for the corresponding image reference item in the list
+	if (auto imageRef = _property->getValue())
+	{
+		for (int i = 0; i < count(); ++i)
+		{
+			auto data = itemData(i);
+
+			if (data.value<ImageReference*>() == imageRef)
+			{
+				setCurrentIndex(i);
+				itemSelected = true;
+				break;
+			}
+		}
+	}
+
+	// If no item was selected, select the default (active image) one
+	if (!itemSelected)
+		setCurrentIndex(0);
+}
+
+void ImageReferencePropertyEditor::populateImageReferences()
+{
+	clear();
+
+	// The first item always represents the currently active image
+	addItem("<Active image>", QVariant::fromValue<ImageReference*>(nullptr));
+
+	auto boldFont = font();
+	boldFont.setBold(true);
+	setItemData(0, boldFont, Qt::FontRole);
+
+	insertSeparator(1);
+
+	// Add all image references
+	for (const auto& imageRef : grinder()->project().imageReferences().sorted())
+		addItem(imageRef->getImageFileName(), QVariant::fromValue(imageRef.get()));
+
+	applyPropertyValue();
+}
+
+void ImageReferencePropertyEditor::currentIndexChanged(int index)
+{
+	_property->setValue(itemData(index).value<ImageReference*>());
+}
diff --git a/Grinder/ui/widgets/engine/CanvasBlocksComboBox.cpp b/Grinder/ui/widgets/engine/CanvasBlocksComboBox.cpp
index 4b8e6a3..692a234 100644
--- a/Grinder/ui/widgets/engine/CanvasBlocksComboBox.cpp
+++ b/Grinder/ui/widgets/engine/CanvasBlocksComboBox.cpp
@@ -9,6 +9,11 @@
 
 Q_DECLARE_METATYPE(Block*);
 
+CanvasBlocksComboBox::CanvasBlocksComboBox(QWidget* parent) : QComboBox(parent)
+{
+	qRegisterMetaType<Block*>();
+}
+
 void CanvasBlocksComboBox::populate(const Label* label, QString noneItemText)
 {
 	clear();
diff --git a/Grinder/ui/widgets/engine/CanvasBlocksComboBox.h b/Grinder/ui/widgets/engine/CanvasBlocksComboBox.h
index be476fb..6a33352 100644
--- a/Grinder/ui/widgets/engine/CanvasBlocksComboBox.h
+++ b/Grinder/ui/widgets/engine/CanvasBlocksComboBox.h
@@ -18,7 +18,7 @@ namespace grndr
 		Q_OBJECT
 
 	public:
-		using QComboBox::QComboBox;
+		CanvasBlocksComboBox(QWidget *parent = nullptr);
 
 	public:
 		void populate(const Label* label, QString noneItemText = "");
diff --git a/Grinder/ui/widgets/project/LabelsComboBox.cpp b/Grinder/ui/widgets/project/LabelsComboBox.cpp
index 164c207..d2a7352 100644
--- a/Grinder/ui/widgets/project/LabelsComboBox.cpp
+++ b/Grinder/ui/widgets/project/LabelsComboBox.cpp
@@ -1,50 +1,55 @@
-/******************************************************************************
- * File: LabelsComboBox.cpp
- * Date: 13.12.2018
- *****************************************************************************/
-
-#include "Grinder.h"
-#include "LabelsComboBox.h"
-#include "project/LabelVector.h"
-#include "core/GrinderApplication.h"
-
-Q_DECLARE_METATYPE(Label*);
-
-void LabelsComboBox::populate(const LabelVector& labels)
-{
-	int index = 0;
-
-	// Add all labels
-	for (const auto& label : labels)
-	{
-		addItem(label->getName(), QVariant::fromValue(label.get()));
-
-		if (label.get() == grinder()->projectController().activeLabel())
-			index = count() - 1;
-	}
-
-	if (count() == 0)
-	{
-		addItem("No labels to display", QVariant::fromValue<Label*>(nullptr));
-		setEnabled(false);
-	}
-
-	setCurrentIndex(index);
-}
-
-Label* LabelsComboBox::getSelectedLabel() const
-{
-	return currentData().value<Label*>();
-}
-
-void LabelsComboBox::selectLabel(const Label* label)
-{
-	for (int i = 0; i < count(); ++i)
-	{
-		if (itemData(i).value<Label*>() == label)
-		{
-			setCurrentIndex(i);
-			break;
-		}
-	}
-}
+/******************************************************************************
+ * File: LabelsComboBox.cpp
+ * Date: 13.12.2018
+ *****************************************************************************/
+
+#include "Grinder.h"
+#include "LabelsComboBox.h"
+#include "project/LabelVector.h"
+#include "core/GrinderApplication.h"
+
+Q_DECLARE_METATYPE(Label*);
+
+LabelsComboBox::LabelsComboBox(QWidget* parent) : QComboBox(parent)
+{
+	qRegisterMetaType<Label*>();
+}
+
+void LabelsComboBox::populate(const LabelVector& labels)
+{
+	int index = 0;
+
+	// Add all labels
+	for (const auto& label : labels)
+	{
+		addItem(label->getName(), QVariant::fromValue(label.get()));
+
+		if (label.get() == grinder()->projectController().activeLabel())
+			index = count() - 1;
+	}
+
+	if (count() == 0)
+	{
+		addItem("No labels to display", QVariant::fromValue<Label*>(nullptr));
+		setEnabled(false);
+	}
+
+	setCurrentIndex(index);
+}
+
+Label* LabelsComboBox::getSelectedLabel() const
+{
+	return currentData().value<Label*>();
+}
+
+void LabelsComboBox::selectLabel(const Label* label)
+{
+	for (int i = 0; i < count(); ++i)
+	{
+		if (itemData(i).value<Label*>() == label)
+		{
+			setCurrentIndex(i);
+			break;
+		}
+	}
+}
diff --git a/Grinder/ui/widgets/project/LabelsComboBox.h b/Grinder/ui/widgets/project/LabelsComboBox.h
index 011c7f3..de6b0ac 100644
--- a/Grinder/ui/widgets/project/LabelsComboBox.h
+++ b/Grinder/ui/widgets/project/LabelsComboBox.h
@@ -1,31 +1,31 @@
-/******************************************************************************
- * File: LabelsComboBox.h
- * Date: 13.12.2018
- *****************************************************************************/
-
-#ifndef LABELSCOMBOBOX_H
-#define LABELSCOMBOBOX_H
-
-#include <QComboBox>
-
-namespace grndr
-{
-	class Label;
-	class LabelVector;
-
-	class LabelsComboBox : public QComboBox
-	{
-		Q_OBJECT
-
-	public:
-		using QComboBox::QComboBox;
-
-	public:
-		void populate(const LabelVector& labels);
-
-		Label* getSelectedLabel() const;
-		void selectLabel(const Label* label);
-	};
-}
-
-#endif
+/******************************************************************************
+ * File: LabelsComboBox.h
+ * Date: 13.12.2018
+ *****************************************************************************/
+
+#ifndef LABELSCOMBOBOX_H
+#define LABELSCOMBOBOX_H
+
+#include <QComboBox>
+
+namespace grndr
+{
+	class Label;
+	class LabelVector;
+
+	class LabelsComboBox : public QComboBox
+	{
+		Q_OBJECT
+
+	public:
+		LabelsComboBox(QWidget *parent = nullptr);
+
+	public:
+		void populate(const LabelVector& labels);
+
+		Label* getSelectedLabel() const;
+		void selectLabel(const Label* label);
+	};
+}
+
+#endif
-- 
GitLab