From fb5adcf2ba1b7fdb3de0285275ceb6f4bcff41dc Mon Sep 17 00:00:00 2001
From: Mikhail Svechnikov <m.svechnikov@fz-juelich.de>
Date: Wed, 1 Feb 2023 15:46:17 +0100
Subject: [PATCH] Masks are free of SessionItem functionality

---
 GUI/Model/Data/ProjectionItems.cpp            | 23 +++-----
 GUI/Model/Data/ProjectionItems.h              | 17 +++++-
 GUI/Model/Device/MaskItems.cpp                | 24 ++++----
 GUI/View/Import/RealDataMaskWidget.cpp        |  2 +-
 GUI/View/Mask/MaskGraphicsScene.cpp           | 34 +++++------
 GUI/View/Mask/MaskViewFactory.cpp             | 56 ++++++++-----------
 GUI/View/Projection/ProjectionsPlot.cpp       | 33 +++++++----
 GUI/View/Projection/ProjectionsPlot.h         |  5 +-
 GUI/View/Projection/ProjectionsWidget.cpp     |  8 +--
 GUI/View/Projection/ProjectionsWidget.h       |  2 -
 .../Projection/SaveProjectionsAssistant.cpp   | 26 ++++-----
 .../Projection/SaveProjectionsAssistant.h     |  8 +--
 12 files changed, 120 insertions(+), 118 deletions(-)

diff --git a/GUI/Model/Data/ProjectionItems.cpp b/GUI/Model/Data/ProjectionItems.cpp
index 7993862f6f6..6bd822a6b19 100644
--- a/GUI/Model/Data/ProjectionItems.cpp
+++ b/GUI/Model/Data/ProjectionItems.cpp
@@ -18,12 +18,8 @@
 ProjectionContainerItem::ProjectionContainerItem()
     : MaskContainerItem(M_TYPE)
 {
-    // const QString T_CHILDREN = "children tag";
-    // registerTag(T_CHILDREN, 0, -1, {HorizontalLineItem::M_TYPE, VerticalLineItem::M_TYPE});
-    // setDefaultTag(T_CHILDREN);
 }
 
-
 void ProjectionContainerItem::insertMask(int row, MaskItem* maskItem)
 {
     verifyMask(maskItem);
@@ -40,17 +36,16 @@ void ProjectionContainerItem::verifyMask(MaskItem* maskItem) const
 {
     ASSERT(maskItem);
 
-    ASSERT(maskItem->modelType() == HorizontalLineItem::M_TYPE
-           || maskItem->modelType() == VerticalLineItem::M_TYPE);
+    ASSERT(dynamic_cast<HorizontalLineItem*>(maskItem) || dynamic_cast<VerticalLineItem*>(maskItem));
 }
 
-QVector<SessionItem*> ProjectionContainerItem::projectionsOfType(const QString& projectionType)
+QVector<SessionItem*> ProjectionContainerItem::projectionsOfType(ProjectionType projectionType)
 {
-    QVector<SessionItem*> projections;
-    for (const auto& proj : m_maskItems) {
-        if (proj.currentItem()->modelType() == projectionType)
-            projections.push_back(proj.currentItem());
-    }
-
-    return projections;
+    if(projectionType == ProjectionType::Horizontal)
+        return projections<HorizontalLineItem>();
+    else if(projectionType == ProjectionType::Vertical)
+        return projections<VerticalLineItem>();
+    else
+        ASSERT(false);
+    return {};
 }
diff --git a/GUI/Model/Data/ProjectionItems.h b/GUI/Model/Data/ProjectionItems.h
index 9ade761a8d2..69a9a33c012 100644
--- a/GUI/Model/Data/ProjectionItems.h
+++ b/GUI/Model/Data/ProjectionItems.h
@@ -19,6 +19,8 @@
 
 //! A container to hold ProjectionItems, intended to store projections of color map on X, Y axes.
 
+enum ProjectionType { Horizontal, Vertical, Invalid };
+
 class ProjectionContainerItem : public MaskContainerItem {
 public:
     static constexpr auto M_TYPE{"ProjectionContainer"};
@@ -29,7 +31,20 @@ public:
 
     void insertMask(int row, MaskItem* maskItem) override;
     void addMask(MaskItem* maskItem) override;
-    QVector<SessionItem*> projectionsOfType(const QString& projectionType);
+
+    QVector<SessionItem*> projectionsOfType(ProjectionType projectionType);
+
+private:
+    template <typename LineType>
+    QVector<SessionItem*> projections()
+    {
+        QVector<SessionItem*> result;
+        for (const auto& proj : m_maskItems)
+            if (dynamic_cast<LineType*>(proj.currentItem()))
+                result.push_back(proj.currentItem());
+        return result;
+    }
 };
 
+
 #endif // BORNAGAIN_GUI_MODEL_DATA_PROJECTIONITEMS_H
diff --git a/GUI/Model/Device/MaskItems.cpp b/GUI/Model/Device/MaskItems.cpp
index 0f9c0d7a830..c61a54cdaca 100644
--- a/GUI/Model/Device/MaskItems.cpp
+++ b/GUI/Model/Device/MaskItems.cpp
@@ -92,8 +92,8 @@ void MaskContainerItem::removeMask(MaskItem* maskItem)
 RegionOfInterestItem* MaskContainerItem::regionOfInterestItem() const
 {
     for (const auto& maskSel: m_maskItems)
-        if (maskSel.currentItem()->modelType() == RegionOfInterestItem::M_TYPE)
-            return dynamic_cast<RegionOfInterestItem*>(maskSel.currentItem());
+        if (auto* roi = dynamic_cast<RegionOfInterestItem*>(maskSel.currentItem()))
+            return roi;
 
     return nullptr;
 }
@@ -290,12 +290,13 @@ MaskItem::MaskItem(const QString& name)
 RectangleItem::RectangleItem()
     : RectangleItem(M_TYPE)
 {
+    setMaskName("RectangleMask");
 }
 
 RectangleItem::RectangleItem(const QString& modelType)
     : MaskItem(modelType)
 {
-    setItemName(modelType);
+    // used to create RegionOfInterestItem
 }
 
 std::unique_ptr<IShape2D> RectangleItem::createShape(double scale) const
@@ -432,6 +433,7 @@ void RectangleItem::readFrom(QXmlStreamReader* r)
 RegionOfInterestItem::RegionOfInterestItem()
     : RectangleItem(M_TYPE)
 {
+    setMaskName("RegionOfInterest");
     setMaskValue(false);
 }
 
@@ -447,7 +449,6 @@ std::unique_ptr<IShape2D> RegionOfInterestItem::createShape(double scale) const
 PolygonPointItem::PolygonPointItem()
     : MaskItemObject(M_TYPE)
 {
-    setItemName(M_TYPE);
 }
 
 double PolygonPointItem::posX() const
@@ -498,7 +499,7 @@ void PolygonPointItem::readFrom(QXmlStreamReader* r)
 PolygonItem::PolygonItem()
     : MaskItem(M_TYPE)
 {
-    setItemName(M_TYPE);
+    setMaskName("PolygonMask");
 }
 
 std::unique_ptr<IShape2D> PolygonItem::createShape(double scale) const
@@ -603,7 +604,7 @@ PolygonItem::~PolygonItem()
 VerticalLineItem::VerticalLineItem()
     : MaskItem(M_TYPE)
 {
-    setItemName(M_TYPE);
+    setMaskName("VerticalLineMask");
 }
 
 std::unique_ptr<IShape2D> VerticalLineItem::createShape(double scale) const
@@ -667,7 +668,7 @@ void VerticalLineItem::readFrom(QXmlStreamReader* r)
 HorizontalLineItem::HorizontalLineItem()
     : MaskItem(M_TYPE)
 {
-    setItemName(M_TYPE);
+    setMaskName("HorizontalLineMask");
 }
 
 std::unique_ptr<IShape2D> HorizontalLineItem::createShape(double scale) const
@@ -731,7 +732,7 @@ void HorizontalLineItem::readFrom(QXmlStreamReader* r)
 EllipseItem::EllipseItem()
     : MaskItem(M_TYPE)
 {
-    setItemName(M_TYPE);
+    setMaskName("EllipseMask");
 }
 
 std::unique_ptr<IShape2D> EllipseItem::createShape(double scale) const
@@ -893,8 +894,7 @@ void EllipseItem::readFrom(QXmlStreamReader* r)
 MaskAllItem::MaskAllItem()
     : MaskItem(M_TYPE)
 {
-    setItemName(M_TYPE);
-    // maskValueItem()->setEnabled(false);  // TODO: Is this needed?
+    setMaskName("MaskAllMask");
 }
 
 std::unique_ptr<IShape2D> MaskAllItem::createShape(double) const
@@ -997,8 +997,8 @@ void MaskContainerModel::removeMask(MaskItem* maskItem)
 RegionOfInterestItem* MaskContainerModel::regionOfInterestItem() const
 {
     for (MaskItem* maskItem : maskContainer->maskItems())
-        if (maskItem->modelType() == RegionOfInterestItem::M_TYPE)
-            return dynamic_cast<RegionOfInterestItem*>(maskItem);
+        if (auto* reg = dynamic_cast<RegionOfInterestItem*>(maskItem))
+            return reg;
 
     return nullptr;
 }
diff --git a/GUI/View/Import/RealDataMaskWidget.cpp b/GUI/View/Import/RealDataMaskWidget.cpp
index 68e1385e96f..552ac10e28f 100644
--- a/GUI/View/Import/RealDataMaskWidget.cpp
+++ b/GUI/View/Import/RealDataMaskWidget.cpp
@@ -91,7 +91,7 @@ void RealDataMaskWidget::setContext()
 
     m_editorPropertyPanel->setMaskContext(containerModel);
 
-    ASSERT(containerItem->modelType() == MaskContainerItem::M_TYPE);
+    ASSERT(dynamic_cast<MaskContainerItem*>(containerItem));
     m_editorCanvas->setSelectionModel(m_editorPropertyPanel->selectionModel());
     m_editorCanvas->setMaskContext(currentIntensityDataItem());
 
diff --git a/GUI/View/Mask/MaskGraphicsScene.cpp b/GUI/View/Mask/MaskGraphicsScene.cpp
index 3e683d1f3ba..ff2c8af3108 100644
--- a/GUI/View/Mask/MaskGraphicsScene.cpp
+++ b/GUI/View/Mask/MaskGraphicsScene.cpp
@@ -72,9 +72,8 @@ void MaskGraphicsScene::setMaskContext(IntensityDataItem* intensityItem)
         disconnectMaskContainer(m_maskContainerModel);
 
         // check container type
-        const QString containerType{maskContainerItem->modelType()};
-        ASSERT(containerType == MaskContainerItem::M_TYPE
-               || containerType == ProjectionContainerItem::M_TYPE);
+        ASSERT(dynamic_cast<MaskContainerItem*>(maskContainerItem)
+               || dynamic_cast<ProjectionContainerItem*>(maskContainerItem));
 
         m_maskContainerItem = maskContainerItem;
         m_maskContainerModel = maskContainerModel;
@@ -391,21 +390,17 @@ void MaskGraphicsScene::updateViews()
 
     IShape2DView* maskView = addViewForItem(m_maskContainerItem);
 
-    for (SessionItem* maskItem : m_maskContainerItem->maskItems()) {
+    for (MaskItem* maskItem : m_maskContainerItem->maskItems()) {
         if (!maskItem)
             continue;
-        if (maskItem->hasModelType<PropertyItem>())
-            continue;
 
         IShape2DView* itemView = addViewForItem(maskItem);
 
         if (itemView && maskView) {
             maskView->addView(itemView);
 
-            // Add views for the points of the PolygonItem without
-            // using the SessionItem mechanism
-            if (maskItem->hasModelType<PolygonItem>()) {
-                auto polygonItem = dynamic_cast<PolygonItem*>(maskItem);
+            // Add views for the points of the PolygonItem
+            if (auto polygonItem = dynamic_cast<PolygonItem*>(maskItem)) {
                 IShape2DView* const polygonView = itemView;
                 for (PolygonPointItem* pointItem : polygonItem->points()) {
                     IShape2DView* pointView = addViewForItem(pointItem);
@@ -478,7 +473,7 @@ bool MaskGraphicsScene::isValidForRectangleShapeDrawing(QGraphicsSceneMouseEvent
     if (m_context.isROIMode()) {
         // only one ROI is allowed
         for (SessionItem* item : m_ItemToView.keys())
-            if (item->modelType() == RegionOfInterestItem::M_TYPE)
+            if (dynamic_cast<RegionOfInterestItem*>(item))
                 return false;
     }
     return true;
@@ -527,8 +522,9 @@ bool MaskGraphicsScene::isValidForMaskAllDrawing(QGraphicsSceneMouseEvent* event
         return false;
     if (!m_context.isMaskAllMode())
         return false;
+
     for (SessionItem* item : m_ItemToView.keys())
-        if (item->modelType() == MaskAllItem::M_TYPE)
+        if (dynamic_cast<MaskAllItem*>(item))
             return false;
     return true;
 }
@@ -577,9 +573,9 @@ void MaskGraphicsScene::setInPanAndZoomMode(bool value)
 void MaskGraphicsScene::updateCursors()
 {
     for (auto it = m_ItemToView.begin(); it != m_ItemToView.end(); ++it) {
-        if (it.key()->modelType() == VerticalLineItem::M_TYPE)
+        if (dynamic_cast<VerticalLineItem*>(it.key()))
             it.value()->setCursor(m_context.isInZoomMode() ? Qt::ArrowCursor : Qt::SizeHorCursor);
-        else if (it.key()->modelType() == HorizontalLineItem::M_TYPE)
+        else if (dynamic_cast<HorizontalLineItem*>(it.key()))
             it.value()->setCursor(m_context.isInZoomMode() ? Qt::ArrowCursor : Qt::SizeVerCursor);
     }
 }
@@ -627,15 +623,13 @@ void MaskGraphicsScene::processRectangleOrEllipseItem(QGraphicsSceneMouseEvent*
     qreal ymin = std::min(click_pos.y(), mouse_pos.y());
     qreal ymax = std::max(click_pos.y(), mouse_pos.y());
 
-    if (m_currentItem->modelType() == RectangleItem::M_TYPE
-        || m_currentItem->modelType() == RegionOfInterestItem::M_TYPE) {
-        auto* rectItem = polymorphic_downcast<RectangleItem*>(m_currentItem);
+    if (auto* rectItem = dynamic_cast<RectangleItem*>(m_currentItem)) {
+        // RectangleItem or RegionOfInterestItem
         rectItem->setXLow(m_adaptor->fromSceneX(xmin));
         rectItem->setYLow(m_adaptor->fromSceneY(ymax));
         rectItem->setXUp(m_adaptor->fromSceneX(xmax));
         rectItem->setYUp(m_adaptor->fromSceneY(ymin));
-    } else if (m_currentItem->modelType() == EllipseItem::M_TYPE) {
-        auto* ellItem = polymorphic_downcast<EllipseItem*>(m_currentItem);
+    } else if (auto* ellItem = dynamic_cast<EllipseItem*>(m_currentItem)) {
         ellItem->setXCenter(m_adaptor->fromSceneX(xmin + (xmax - xmin) / 2.));
         ellItem->setYCenter(m_adaptor->fromSceneY(ymin + (ymax - ymin) / 2.));
         ellItem->setXRadius((m_adaptor->fromSceneX(xmax) - m_adaptor->fromSceneX(xmin)) / 2.);
@@ -661,7 +655,7 @@ void MaskGraphicsScene::processPolygonItem(QGraphicsSceneMouseEvent* event)
                                  QItemSelectionModel::Select);
         m_maskContainerItem->updateMaskNames();
     }
-    ASSERT(m_currentItem->modelType() == PolygonItem::M_TYPE);
+    ASSERT(dynamic_cast<PolygonItem*>(m_currentItem));
 
     if (PolygonView* polygon = currentPolygon()) {
         if (polygon->closePolygonIfNecessary()) {
diff --git a/GUI/View/Mask/MaskViewFactory.cpp b/GUI/View/Mask/MaskViewFactory.cpp
index c120395489b..f19d6674e29 100644
--- a/GUI/View/Mask/MaskViewFactory.cpp
+++ b/GUI/View/Mask/MaskViewFactory.cpp
@@ -33,50 +33,38 @@ IShape2DView* MaskViewFactory::createMaskView(SessionItem* item, ISceneAdaptor*
 {
     IShape2DView* result(nullptr);
 
-    if (item->hasModelType<MaskContainerItem>())
-        result = new MaskContainerView(polymorphic_downcast<MaskContainerItem*>(item));
+    if (auto* mask = dynamic_cast<MaskContainerItem*>(item))
+        result = new MaskContainerView(mask);
 
-    else if (item->hasModelType<ProjectionContainerItem>())
-        result = new MaskContainerView(polymorphic_downcast<ProjectionContainerItem*>(item));
+    else if (auto* mask = dynamic_cast<ProjectionContainerItem*>(item))
+        result = new MaskContainerView(mask);
 
+    else if (auto* mask = dynamic_cast<RectangleItem*>(item))
+        result = new RectangleView(mask);
 
-    else if (item->hasModelType<RectangleItem>())
-        result = new RectangleView(polymorphic_downcast<RectangleItem*>(item));
+    else if (auto* mask = dynamic_cast<PolygonItem*>(item))
+        result = new PolygonView(mask);
 
+    else if (auto* mask = dynamic_cast<PolygonPointItem*>(item))
+        result = new PolygonPointView(mask);
 
-    else if (item->hasModelType<PolygonItem>())
-        result = new PolygonView(polymorphic_downcast<PolygonItem*>(item));
+    else if (auto* mask = dynamic_cast<VerticalLineItem*>(item))
+        result = new VerticalLineView(mask);
 
+    else if (auto* mask = dynamic_cast<HorizontalLineItem*>(item))
+        result = new HorizontalLineView(mask);
 
-    else if (item->hasModelType<PolygonPointItem>())
-        result = new PolygonPointView(polymorphic_downcast<PolygonPointItem*>(item));
+    else if (auto* mask = dynamic_cast<EllipseItem*>(item))
+        result = new EllipseView(mask);
 
+    else if (auto* mask = dynamic_cast<MaskAllItem*>(item))
+        result = new MaskAllView(mask);
 
-    else if (item->hasModelType<VerticalLineItem>())
-        result = new VerticalLineView(polymorphic_downcast<VerticalLineItem*>(item));
+    else if (auto* mask = dynamic_cast<RegionOfInterestItem*>(item))
+        result = new RegionOfInterestView(mask);
 
-
-    else if (item->hasModelType<HorizontalLineItem>())
-        result = new HorizontalLineView(polymorphic_downcast<HorizontalLineItem*>(item));
-
-
-    else if (item->hasModelType<EllipseItem>())
-        result = new EllipseView(polymorphic_downcast<EllipseItem*>(item));
-
-
-    else if (item->hasModelType<MaskAllItem>())
-        result = new MaskAllView(polymorphic_downcast<MaskAllItem*>(item));
-
-
-    else if (item->hasModelType<RegionOfInterestItem>())
-        result = new RegionOfInterestView(polymorphic_downcast<RegionOfInterestItem*>(item));
-
-
-    else {
-        throw Error("MaskViewFactory::createSampleView() -> Error! "
-                    "Cannot create a view for "
-                    + item->modelType());
-    }
+    else 
+        ASSERT(false);
 
     result->setSceneAdaptor(adaptor);
 
diff --git a/GUI/View/Projection/ProjectionsPlot.cpp b/GUI/View/Projection/ProjectionsPlot.cpp
index 7cb5c33f6e3..744501e6b6a 100644
--- a/GUI/View/Projection/ProjectionsPlot.cpp
+++ b/GUI/View/Projection/ProjectionsPlot.cpp
@@ -27,9 +27,9 @@
 
 using boost::polymorphic_downcast;
 
-ProjectionsPlot::ProjectionsPlot(QString projectionType, QWidget* parent)
+ProjectionsPlot::ProjectionsPlot(ProjectionType projectionType, QWidget* parent)
     : DataItemBundleWidget(parent)
-    , m_projectionType(std::move(projectionType))
+    , m_projectionType(projectionType)
     , m_customPlot(new QCustomPlot)
 {
     auto* vlayout = new QVBoxLayout(this);
@@ -145,12 +145,23 @@ QVector<SessionItem*> ProjectionsPlot::projectionItems()
     return projectionContainerItem()->projectionsOfType(m_projectionType);
 }
 
+bool ProjectionsPlot::isCorrectProjectionType(SessionItem* item)
+{
+    if(m_projectionType == ProjectionType::Horizontal && dynamic_cast<HorizontalLineItem*>(item))
+        return true;
+
+    if(m_projectionType == ProjectionType::Vertical && dynamic_cast<VerticalLineItem*>(item))
+        return true;
+
+    return false;
+}
+
 QCPGraph* ProjectionsPlot::graphForItem(SessionItem* item)
 {
     if (!intensityItem())
         return nullptr;
 
-    if (item->modelType() != m_projectionType)
+    if (!isCorrectProjectionType(item))
         return nullptr;
 
     QCPGraph* graph = m_item_to_graph[item];
@@ -251,13 +262,13 @@ void ProjectionsPlot::setGraphFromItem(QCPGraph* graph, SessionItem* item)
     std::unique_ptr<Datafield> field;
 
     // TODO: merge with very similar code in SaveProjectionsAssistant::projectionsData
-    if (item->modelType() == HorizontalLineItem::M_TYPE) {
-        double value = polymorphic_downcast<HorizontalLineItem*>(item)->posY();
-        field.reset(intensityItem()->datafield()->xProjection(value));
-    } else {
-        double value = polymorphic_downcast<VerticalLineItem*>(item)->posX();
-        field.reset(intensityItem()->datafield()->yProjection(value));
-    }
+    if (const auto* horLine = dynamic_cast<HorizontalLineItem*>(item))
+        field.reset(intensityItem()->datafield()->xProjection(horLine->posY()));
+    else if (const auto* verLine = dynamic_cast<VerticalLineItem*>(item))
+        field.reset(intensityItem()->datafield()->yProjection(verLine->posX()));
+    else
+        ASSERT(false);
+
 
     auto centers = field->axis(0).binCenters();
     auto values = field->flatVector();
@@ -285,5 +296,5 @@ void ProjectionsPlot::replot()
 
 bool ProjectionsPlot::isHorizontalType()
 {
-    return m_projectionType == HorizontalLineItem::M_TYPE;
+    return m_projectionType == ProjectionType::Horizontal;
 }
diff --git a/GUI/View/Projection/ProjectionsPlot.h b/GUI/View/Projection/ProjectionsPlot.h
index 40a99c835fa..09195c62e59 100644
--- a/GUI/View/Projection/ProjectionsPlot.h
+++ b/GUI/View/Projection/ProjectionsPlot.h
@@ -30,7 +30,7 @@ class ProjectionsPlot : public DataItemBundleWidget {
     Q_OBJECT
 
 public:
-    ProjectionsPlot(QString projectionType, QWidget* parent = nullptr);
+    ProjectionsPlot(ProjectionType projectionType, QWidget* parent = nullptr);
 
     void setIntensityItem(IntensityDataItem* intensityDataItem) override;
 
@@ -43,6 +43,7 @@ public slots:
 private:
     IntensityDataItem* intensityItem();
     QVector<SessionItem*> projectionItems();
+    bool isCorrectProjectionType(SessionItem *item);
     ProjectionContainerItem* projectionContainerItem();
     QCPGraph* graphForItem(SessionItem* item);
 
@@ -63,7 +64,7 @@ private:
 
     bool isHorizontalType();
 
-    QString m_projectionType;
+    ProjectionType m_projectionType = ProjectionType::Invalid;
     QCustomPlot* m_customPlot;
     QMap<SessionItem*, QCPGraph*> m_item_to_graph;
 };
diff --git a/GUI/View/Projection/ProjectionsWidget.cpp b/GUI/View/Projection/ProjectionsWidget.cpp
index 0013ad622bb..f2abb5c956b 100644
--- a/GUI/View/Projection/ProjectionsWidget.cpp
+++ b/GUI/View/Projection/ProjectionsWidget.cpp
@@ -28,8 +28,8 @@ const int vertical_projection_tab = 1;
 
 ProjectionsWidget::ProjectionsWidget(QWidget* parent)
     : QWidget(parent)
-    , m_xProjection(new ProjectionsPlot(HorizontalLineItem::M_TYPE))
-    , m_yProjection(new ProjectionsPlot(VerticalLineItem::M_TYPE))
+    , m_xProjection(new ProjectionsPlot(ProjectionType::Horizontal))
+    , m_yProjection(new ProjectionsPlot(ProjectionType::Vertical))
     , m_tabWidget(new QTabWidget)
 {
     auto* layout = new QVBoxLayout;
@@ -37,8 +37,8 @@ ProjectionsWidget::ProjectionsWidget(QWidget* parent)
     layout->setSpacing(0);
 
     m_tabWidget->setTabPosition(QTabWidget::North);
-    m_tabWidget->insertTab(HORIZONTAL, m_xProjection, "Horizontal");
-    m_tabWidget->insertTab(VERTICAL, m_yProjection, "Vertical");
+    m_tabWidget->insertTab(ProjectionType::Horizontal, m_xProjection, "Horizontal");
+    m_tabWidget->insertTab(ProjectionType::Vertical, m_yProjection, "Vertical");
 
     layout->addWidget(m_tabWidget);
     setLayout(layout);
diff --git a/GUI/View/Projection/ProjectionsWidget.h b/GUI/View/Projection/ProjectionsWidget.h
index 4f683477abe..4b997f8aa97 100644
--- a/GUI/View/Projection/ProjectionsWidget.h
+++ b/GUI/View/Projection/ProjectionsWidget.h
@@ -27,8 +27,6 @@ class ProjectionsWidget : public QWidget {
     Q_OBJECT
 
 public:
-    enum ETabId { HORIZONTAL, VERTICAL };
-
     ProjectionsWidget(QWidget* parent = nullptr);
 
     void setIntensityItem(IntensityDataItem* intensityDataItem);
diff --git a/GUI/View/Projection/SaveProjectionsAssistant.cpp b/GUI/View/Projection/SaveProjectionsAssistant.cpp
index 15b219a9461..201fd1c8741 100644
--- a/GUI/View/Projection/SaveProjectionsAssistant.cpp
+++ b/GUI/View/Projection/SaveProjectionsAssistant.cpp
@@ -17,7 +17,6 @@
 #include "Device/Data/Datafield.h"
 #include "GUI/Application/ApplicationSettings.h"
 #include "GUI/Model/Data/IntensityDataItem.h"
-#include "GUI/Model/Data/ProjectionItems.h"
 #include "GUI/Model/Device/MaskItems.h"
 #include "GUI/Model/Project/ProjectDocument.h"
 #include "GUI/Util/Error.h"
@@ -88,11 +87,11 @@ void SaveProjectionsAssistant::saveProjections(QWidget* parent, IntensityDataIte
     QTextStream out(&file);
 
     out << "# Projections along x-axis (horizontal projections) \n";
-    out << projectionsToString(HorizontalLineItem::M_TYPE, intensityItem);
+    out << projectionsToString(ProjectionType::Horizontal, intensityItem);
     out << "\n";
 
     out << "# Projections along y-axis (vertical projections) \n";
-    out << projectionsToString(VerticalLineItem::M_TYPE, intensityItem);
+    out << projectionsToString(ProjectionType::Vertical, intensityItem);
     out << "\n";
 
     file.close();
@@ -100,7 +99,7 @@ void SaveProjectionsAssistant::saveProjections(QWidget* parent, IntensityDataIte
 
 //! Generates multi-line string with projections data of given type (horizontal, vertical).
 
-QString SaveProjectionsAssistant::projectionsToString(const QString& projectionsType,
+QString SaveProjectionsAssistant::projectionsToString(ProjectionType projectionsType,
                                                       IntensityDataItem* intensityItem)
 {
     QString result;
@@ -127,23 +126,24 @@ QString SaveProjectionsAssistant::projectionsToString(const QString& projections
 //! Returns projections data for all projections of given type (horizontal, vertical).
 
 SaveProjectionsAssistant::ProjectionsData
-SaveProjectionsAssistant::projectionsData(const QString& projectionsType,
+SaveProjectionsAssistant::projectionsData(ProjectionType projectionsType,
                                           IntensityDataItem* intensityItem)
 {
     ProjectionsData result;
-    result.is_horizontal = (projectionsType != VerticalLineItem::M_TYPE);
+    result.is_horizontal = (projectionsType == ProjectionType::Horizontal);
 
     for (auto* item : projectionItems(projectionsType, intensityItem)) {
         std::unique_ptr<Datafield> field;
         SaveProjectionsAssistant::Projection data;
 
-        if (item->modelType() == HorizontalLineItem::M_TYPE) {
-            data.axis_value = polymorphic_downcast<HorizontalLineItem*>(item)->posY();
+        if (const auto* horLine = dynamic_cast<HorizontalLineItem*>(item)) {
+            data.axis_value = horLine->posY();
             field.reset(m_field->xProjection(data.axis_value));
-        } else {
-            data.axis_value = polymorphic_downcast<VerticalLineItem*>(item)->posX();
+        } else if (const auto* verLine = dynamic_cast<VerticalLineItem*>(item)) {
+            data.axis_value = verLine->posX();
             field.reset(m_field->yProjection(data.axis_value));
-        }
+        } else
+            ASSERT(false);
 
         auto values = field->flatVector();
         auto centers = field->axis(0).binCenters();
@@ -157,12 +157,12 @@ SaveProjectionsAssistant::projectionsData(const QString& projectionsType,
 
 //! Returns vector of ProjectionItems sorted according to axis value.
 
-QVector<SessionItem*> SaveProjectionsAssistant::projectionItems(const QString& projectionsType,
+QVector<SessionItem*> SaveProjectionsAssistant::projectionItems(ProjectionType projectionsType,
                                                                 IntensityDataItem* intensityItem)
 {
     auto result = intensityItem->projectionContainerItem()->projectionsOfType(projectionsType);
     std::sort(result.begin(), result.end(),
-              projectionsType == HorizontalLineItem::M_TYPE ? horiz_less_posy : vert_less_posx);
+              projectionsType == ProjectionType::Horizontal ? horiz_less_posy : vert_less_posx);
     return result;
 }
 
diff --git a/GUI/View/Projection/SaveProjectionsAssistant.h b/GUI/View/Projection/SaveProjectionsAssistant.h
index 07b61e044a7..90ae628340a 100644
--- a/GUI/View/Projection/SaveProjectionsAssistant.h
+++ b/GUI/View/Projection/SaveProjectionsAssistant.h
@@ -15,6 +15,7 @@
 #ifndef BORNAGAIN_GUI_VIEW_PROJECTION_SAVEPROJECTIONSASSISTANT_H
 #define BORNAGAIN_GUI_VIEW_PROJECTION_SAVEPROJECTIONSASSISTANT_H
 
+#include "GUI/Model/Data/ProjectionItems.h"
 #include <QString>
 #include <QVector>
 #include <QWidget>
@@ -22,7 +23,6 @@
 
 class IntensityDataItem;
 class Datafield;
-class SessionItem;
 
 //! Assistant class which save all projections of IndensityDataItem into ASCII file.
 
@@ -45,12 +45,12 @@ private:
         QVector<Projection> projections;
     };
 
-    QString projectionsToString(const QString& projectionsType, IntensityDataItem* intensityItem);
+    QString projectionsToString(ProjectionType projectionsType, IntensityDataItem* intensityItem);
 
     const Datafield* m_field;
-    ProjectionsData projectionsData(const QString& projectionsType,
+    ProjectionsData projectionsData(ProjectionType projectionsType,
                                     IntensityDataItem* intensityItem);
-    QVector<SessionItem*> projectionItems(const QString& projectionsType,
+    QVector<SessionItem*> projectionItems(ProjectionType projectionsType,
                                           IntensityDataItem* intensityItem);
 
     QString projectionFileHeader(ProjectionsData& projectionsData);
-- 
GitLab