diff --git a/GUI/Models/JobItem.cpp b/GUI/Models/JobItem.cpp
index 524c2d8083a5c430413844a0245a26d8c2726422..59738e8d10a162ae4a05932380747d4ed99555d1 100644
--- a/GUI/Models/JobItem.cpp
+++ b/GUI/Models/JobItem.cpp
@@ -36,15 +36,11 @@ JobItem::JobItem() : SessionItem(M_TYPE)
     addProperty(P_INSTRUMENT_NAME, QString())->setEditable(false);
     addProperty(P_WITH_FITTING, false)->setVisible(false);
 
-    addProperty(P_STATUS, "Idle")->setEditable(false);
+    addProperty(P_STATUS, jobStatusToString(JobStatus::Idle))->setEditable(false);
 
     addProperty(P_BEGIN_TIME, QString())->setEditable(false);
     addProperty(P_END_TIME, QString())->setEditable(false);
 
-    auto durationItem = addProperty(P_DURATION, QString());
-    durationItem->setEditable(false);
-    durationItem->setToolTip("Duration of DWBA simulation in sec.msec format");
-
     addProperty(P_COMMENTS, QString())->setVisible(false);
     addProperty(P_PROGRESS, 0)->setVisible(false);
     addProperty(P_PRESENTATION_TYPE, QVariant::Type::Invalid)->setVisible(false);
@@ -100,15 +96,15 @@ void JobItem::addDataItem(DataItem* data)
     insertItem(-1, data, T_OUTPUT);
 }
 
-QString JobItem::getStatus() const
+JobStatus JobItem::getStatus() const
 {
-    return getItemValue(P_STATUS).toString();
+    return jobStatusFromString(getItemValue(P_STATUS).toString());
 }
 
-void JobItem::setStatus(const QString& status)
+void JobItem::setStatus(const JobStatus status)
 {
-    setItemValue(P_STATUS, status);
-    if (status == "Failed") {
+    setItemValue(P_STATUS, jobStatusToString(status));
+    if (status == JobStatus::Failed) {
         if (DataItem* intensityItem = dataItem()) {
             if (intensityItem->getOutputData())
                 intensityItem->getOutputData()->setAllTo(0.0);
@@ -124,27 +120,32 @@ bool JobItem::isStatusPropertyName(const QString& name)
 
 bool JobItem::isIdle() const
 {
-    return getStatus() == "Idle";
+    return getStatus() == JobStatus::Idle;
 }
 
 bool JobItem::isRunning() const
 {
-    return getStatus() == "Running";
+    return getStatus() == JobStatus::Running;
 }
 
 bool JobItem::isCompleted() const
 {
-    return getStatus() == "Completed";
+    return getStatus() == JobStatus::Completed;
 }
 
 bool JobItem::isCanceled() const
 {
-    return getStatus() == "Canceled";
+    return getStatus() == JobStatus::Canceled;
 }
 
 bool JobItem::isFailed() const
 {
-    return getStatus() == "Failed";
+    return getStatus() == JobStatus::Failed;
+}
+
+bool JobItem::isFitting() const
+{
+    return getStatus() == JobStatus::Fitting;
 }
 
 bool JobItem::isValidForFitting()
@@ -162,6 +163,11 @@ void JobItem::setBeginTime(const QDateTime& begin_time)
     setItemValue(P_BEGIN_TIME, begin_time.toString(Qt::ISODateWithMs));
 }
 
+bool JobItem::isBeginTimePropertyName(const QString& name)
+{
+    return name == P_BEGIN_TIME;
+}
+
 QDateTime JobItem::endTime() const
 {
     return QDateTime::fromString(getItemValue(P_END_TIME).toString(), Qt::ISODateWithMs);
@@ -170,16 +176,20 @@ QDateTime JobItem::endTime() const
 void JobItem::setEndTime(const QDateTime& end_time)
 {
     setItemValue(P_END_TIME, end_time.toString(Qt::ISODateWithMs));
-    QString duration;
-    if (end_time.isValid()) {
-        QDateTime begin_time = beginTime();
-        if (begin_time.isValid()) {
-            qint64 delta = begin_time.msecsTo(end_time);
-            if (delta >0)
-                duration = QString::number(delta / 1000., 'f', 3);
-        }
-    }
-    setItemValue(P_DURATION, duration);
+}
+
+bool JobItem::isEndTimePropertyName(const QString& name)
+{
+    return name == P_END_TIME;
+}
+
+std::optional<size_t> JobItem::duration() const
+{
+    QDateTime begin_time = beginTime();
+    QDateTime end_time = endTime();
+    if (begin_time.isValid() && end_time.isValid() && begin_time < end_time)
+        return begin_time.msecsTo(end_time);
+    return std::nullopt;
 }
 
 QString JobItem::getComments() const
@@ -207,6 +217,11 @@ void JobItem::setProgress(int progress)
     setItemValue(P_PROGRESS, progress);
 }
 
+bool JobItem::isProgressPropertyName(const QString& name)
+{
+    return name == P_PROGRESS;
+}
+
 bool JobItem::runImmediately() const
 {
     return simulationOptionsItem()->runImmediately();
@@ -333,11 +348,21 @@ Data1DViewItem* JobItem::createDataViewItem()
     return model()->insertItem<Data1DViewItem>(this, -1, T_DATAVIEW);
 }
 
+QString JobItem::sampleName() const
+{
+    return getItemValue(P_SAMPLE_NAME).toString();
+}
+
 void JobItem::setSampleName(const QString& name)
 {
     getItem(P_SAMPLE_NAME)->setValue(name);
 }
 
+QString JobItem::instrumentName() const
+{
+    return getItemValue(P_INSTRUMENT_NAME).toString();
+}
+
 void JobItem::setInstrumentName(const QString& name)
 {
     getItem(P_INSTRUMENT_NAME)->setValue(name);
diff --git a/GUI/Models/JobItem.h b/GUI/Models/JobItem.h
index 22506f34906e395e6ffb03139d496ee6ece1fd01..ab0e7226399fdfb3db6d5632965c070995dd0f5f 100644
--- a/GUI/Models/JobItem.h
+++ b/GUI/Models/JobItem.h
@@ -16,6 +16,7 @@
 #define BORNAGAIN_GUI_MODELS_JOBITEM_H
 
 #include "GUI/Models/SessionItem.h"
+#include "GUI/Models/JobStatus.h"
 #include "GUI/Models/SessionModel.h"
 
 class DataItem;
@@ -33,6 +34,7 @@ class ISimulation;
 class SimulationOptionsItem;
 
 #include <QDateTime>
+#include <optional>
 
 class BA_CORE_API_ JobItem : public SessionItem {
 
@@ -44,7 +46,6 @@ private:
     static constexpr auto P_STATUS{"Status"};
     static constexpr auto P_BEGIN_TIME{"Begin time"};
     static constexpr auto P_END_TIME{"End time"};
-    static constexpr auto P_DURATION{"Duration"};
     static constexpr auto P_COMMENTS{"Comments"};
     static constexpr auto P_PROGRESS{"Progress"};
     static constexpr auto P_PRESENTATION_TYPE{"Presentation type"};
@@ -71,8 +72,8 @@ public:
     template <typename T> T* setDataType();
     void addDataItem(DataItem* data);
 
-    QString getStatus() const;
-    void setStatus(const QString& status);
+    JobStatus getStatus() const;
+    void setStatus(const JobStatus status);
     static bool isStatusPropertyName(const QString& name);
 
     bool isIdle() const;
@@ -80,13 +81,19 @@ public:
     bool isCompleted() const;
     bool isCanceled() const;
     bool isFailed() const;
+    bool isFitting() const;
     bool isValidForFitting();
 
     QDateTime beginTime() const;
     void setBeginTime(const QDateTime& begin_time);
+    static bool isBeginTimePropertyName(const QString& name);
 
     QDateTime endTime() const;
     void setEndTime(const QDateTime& end_time);
+    static bool isEndTimePropertyName(const QString& name);
+
+    /// if begin and end time are both available the duration in ms, otherwise empty
+    std::optional<size_t> duration() const;
 
     QString getComments() const;
     void setComments(const QString& comments);
@@ -94,6 +101,7 @@ public:
 
     int getProgress() const;
     void setProgress(int progress);
+    static bool isProgressPropertyName(const QString& name);
 
     bool runImmediately() const;
     bool runInBackground() const;
@@ -130,8 +138,10 @@ public:
     void addDataViewItem(Data1DViewItem* data_view);
     Data1DViewItem* createDataViewItem();
 
+    QString sampleName() const;
     void setSampleName(const QString& name);
 
+    QString instrumentName() const;
     void setInstrumentName(const QString& name);
 
     const QString presentationType() const;
diff --git a/GUI/Models/JobModel.cpp b/GUI/Models/JobModel.cpp
index a6ebbc8385085f737c23cd1a420e6d17d042a4f7..ef29dbff63054c17ff0103a31a5058b9159138ac 100644
--- a/GUI/Models/JobModel.cpp
+++ b/GUI/Models/JobModel.cpp
@@ -114,7 +114,7 @@ bool JobModel::hasUnfinishedJobs()
 {
     bool result = m_queue_data->hasUnfinishedJobs();
     for (auto jobItem : topItems<JobItem>()) {
-        if (jobItem->getStatus() == "Fitting")
+        if (jobItem->getStatus() == JobStatus::Fitting)
             result = true;
     }
 
diff --git a/GUI/Models/JobQueueData.cpp b/GUI/Models/JobQueueData.cpp
index 7d2922faf82c5d6b0ae73f90cd32d7ecf344a4dc..7b77893f1cdad0e21144c2dee218c5b0eb0295ff 100644
--- a/GUI/Models/JobQueueData.cpp
+++ b/GUI/Models/JobQueueData.cpp
@@ -54,7 +54,7 @@ void JobQueueData::runJob(JobItem* jobItem)
         message += QString::fromStdString(std::string(ex.what()));
         jobItem->setComments(message);
         jobItem->setProgress(100);
-        jobItem->setStatus("Failed");
+        jobItem->setStatus(JobStatus::Failed);
         emit focusRequest(jobItem);
         return;
     }
@@ -106,7 +106,7 @@ void JobQueueData::onStartedJob()
 
     auto jobItem = m_jobModel->getJobItemForIdentifier(worker->identifier());
     jobItem->setProgress(0);
-    jobItem->setStatus("Running");
+    jobItem->setStatus(JobStatus::Running);
     jobItem->setBeginTime(worker->simulationStart());
     jobItem->setEndTime(QDateTime());
 }
@@ -226,7 +226,7 @@ void JobQueueData::processFinishedJob(JobWorker* worker, JobItem* jobItem)
     jobItem->setEndTime(worker->simulationEnd());
 
     // propagating status of runner
-    if (worker->status() == "Failed") {
+    if (worker->status() == JobStatus::Failed) {
         jobItem->setComments(worker->failureMessage());
     } else {
         // propagating simulation results
diff --git a/GUI/Models/JobStatus.cpp b/GUI/Models/JobStatus.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..10a4adf362c3f4e7f64c90cb95df960e0cf5c0fa
--- /dev/null
+++ b/GUI/Models/JobStatus.cpp
@@ -0,0 +1,42 @@
+//  ************************************************************************************************
+//
+//  BornAgain: simulate and fit reflection and scattering
+//
+//! @file      GUI/Models/JobWorker.cpp
+//! @brief     Implements class JobWorker
+//!
+//! @homepage  http://www.bornagainproject.org
+//! @license   GNU General Public License v3 or higher (see COPYING)
+//! @copyright Forschungszentrum Jülich GmbH 2018
+//! @authors   Scientific Computing Group at MLZ (see CITATION, AUTHORS)
+//
+//  ************************************************************************************************
+
+#include "GUI/Models/JobStatus.h"
+#include "Base/Utils/Assert.h"
+
+#include <boost/bimap.hpp>
+
+namespace {
+
+const std::vector<boost::bimap<JobStatus, QString>::value_type> values = {
+    {JobStatus::Idle, "Idle"},         {JobStatus::Running, "Running"},
+    {JobStatus::Fitting, "Fitting"},   {JobStatus::Completed, "Completed"},
+    {JobStatus::Canceled, "Canceled"}, {JobStatus::Failed, "Failed"}};
+
+const boost::bimap<JobStatus, QString> status2name(values.begin(), values.end());
+} // namespace
+
+const QString jobStatusToString(JobStatus status)
+{
+    auto it = status2name.left.find(status);
+    ASSERT(it != status2name.left.end());
+    return it->second;
+}
+
+JobStatus jobStatusFromString(const QString& name)
+{
+    auto it = status2name.right.find(name);
+    ASSERT(it != status2name.right.end());
+    return it->second;
+}
diff --git a/GUI/Models/JobStatus.h b/GUI/Models/JobStatus.h
new file mode 100644
index 0000000000000000000000000000000000000000..cd6f9f3ead02b90e04c2c850f0b0310285badd3a
--- /dev/null
+++ b/GUI/Models/JobStatus.h
@@ -0,0 +1,37 @@
+//  ************************************************************************************************
+//
+//  BornAgain: simulate and fit reflection and scattering
+//
+//! @file      GUI/Models/JobStatus.h
+//! @brief     Defines enum JobStatus
+//!
+//! @homepage  http://www.bornagainproject.org
+//! @license   GNU General Public License v3 or higher (see COPYING)
+//! @copyright Forschungszentrum Jülich GmbH 2018
+//! @authors   Scientific Computing Group at MLZ (see CITATION, AUTHORS)
+//
+//  ************************************************************************************************
+
+#ifndef BORNAGAIN_GUI_MODELS_JOBSTATUS_H
+#define BORNAGAIN_GUI_MODELS_JOBSTATUS_H
+
+#include <QString>
+
+//! The JobStatus enum lists the possible states of a job
+
+enum class JobStatus {
+    Idle,      //!< the job has not been started yet
+    Running,   //!< the job is busy calculating
+    Fitting,   //!< the job is busy fitting
+    Completed, //!< the job was successfully completed
+    Canceled,  //!< the job was stopped by the user
+    Failed     //!< the job aborted because it hit an error
+};
+
+//! get a string representation of the status
+const QString jobStatusToString(JobStatus status);
+
+//! get status value for given string representation
+JobStatus jobStatusFromString(const QString& name);
+
+#endif // BORNAGAIN_GUI_MODELS_JOBSTATUS_H
diff --git a/GUI/Models/JobWorker.cpp b/GUI/Models/JobWorker.cpp
index 322e87c32ce673ae30aa02425fcd6114a90a635d..a3acaede4bfc90fa8ab86d7c8e0b3e9367cf0b44 100644
--- a/GUI/Models/JobWorker.cpp
+++ b/GUI/Models/JobWorker.cpp
@@ -21,7 +21,7 @@ JobWorker::JobWorker(const QString& identifier, ISimulation* simulation)
     : m_identifier(identifier)
     , m_simulation(simulation)
     , m_percentage_done(0)
-    , m_job_status("Idle")
+    , m_job_status(JobStatus::Idle)
     , m_terminate_request_flag(false)
 {
 }
@@ -48,14 +48,14 @@ void JobWorker::start()
             return updateProgress(static_cast<int>(percentage_done));
         });
 
-        m_job_status = "Running";
+        m_job_status = JobStatus::Running;
 
         try {
             m_simulation->runSimulation();
-            if (m_job_status != "Canceled")
-                m_job_status = "Completed";
+            if (m_job_status != JobStatus::Canceled)
+                m_job_status = JobStatus::Completed;
         } catch (const std::exception& ex) {
-            m_job_status = "Failed";
+            m_job_status = JobStatus::Failed;
             m_percentage_done = 100;
             m_failure_message =
                 "JobRunner::start() -> ISimulation failed with exception throw:\n\n";
@@ -64,7 +64,7 @@ void JobWorker::start()
         }
 
     } else {
-        m_job_status = "Failed";
+        m_job_status = JobStatus::Failed;
         m_percentage_done = 100;
         m_failure_message = "JobRunner::start() -> Error. ISimulation doesn't exist.";
     }
@@ -74,7 +74,7 @@ void JobWorker::start()
     emit finished();
 }
 
-QString JobWorker::status() const
+JobStatus JobWorker::status() const
 {
     return m_job_status;
 }
@@ -99,7 +99,7 @@ const QDateTime& JobWorker::simulationEnd() const
 void JobWorker::terminate()
 {
     m_terminate_request_flag = true;
-    m_job_status = "Canceled";
+    m_job_status = JobStatus::Canceled;
 }
 
 //! Sets current progress. Returns true if we want to continue the simulation.
diff --git a/GUI/Models/JobWorker.h b/GUI/Models/JobWorker.h
index 2060d50f3aee6b5843e7112e984c759b21f3d88e..9eb805ef3102b7e4ea1c5d890c3eeb54093a4a96 100644
--- a/GUI/Models/JobWorker.h
+++ b/GUI/Models/JobWorker.h
@@ -15,6 +15,7 @@
 #ifndef BORNAGAIN_GUI_MODELS_JOBWORKER_H
 #define BORNAGAIN_GUI_MODELS_JOBWORKER_H
 
+#include "GUI/Models/JobStatus.h"
 #include <QObject>
 #include <QDateTime>
 
@@ -31,7 +32,7 @@ public:
 
     int progress() const;
 
-    QString status() const;
+    JobStatus status() const;
 
     QString failureMessage() const;
 
@@ -53,7 +54,7 @@ private:
     QString m_identifier;
     ISimulation* m_simulation;
     int m_percentage_done;
-    QString m_job_status;
+    JobStatus m_job_status;
     bool m_terminate_request_flag;
     QString m_failure_message;
     QDateTime m_simulation_start;
diff --git a/GUI/Views/FitWidgets/FitParameterWidget.cpp b/GUI/Views/FitWidgets/FitParameterWidget.cpp
index 030ab36a7a9ab702b0638bc7bf35419675a8bc18..5bf4bb4d792de6dae83d14deca36c540f3958bf9 100644
--- a/GUI/Views/FitWidgets/FitParameterWidget.cpp
+++ b/GUI/Views/FitWidgets/FitParameterWidget.cpp
@@ -245,7 +245,7 @@ void FitParameterWidget::init_actions()
 
 void FitParameterWidget::initTuningWidgetContextMenu(QMenu& menu)
 {
-    if (jobItem()->getStatus() == "Fitting") {
+    if (jobItem()->getStatus() == JobStatus::Fitting) {
         setActionsEnabled(false);
         return;
     }
@@ -279,7 +279,7 @@ void FitParameterWidget::initTuningWidgetContextMenu(QMenu& menu)
 
 void FitParameterWidget::initFitParameterTreeContextMenu(QMenu& menu)
 {
-    if (jobItem()->getStatus() == "Fitting") {
+    if (jobItem()->getStatus() == JobStatus::Fitting) {
         setActionsEnabled(false);
         return;
     }
diff --git a/GUI/Views/FitWidgets/FitSessionController.cpp b/GUI/Views/FitWidgets/FitSessionController.cpp
index 136a04564633f56a7e09d20328a97b18ec15c6ae..d0bbf1b600911422d762693baa7172deab61cf1c 100644
--- a/GUI/Views/FitWidgets/FitSessionController.cpp
+++ b/GUI/Views/FitWidgets/FitSessionController.cpp
@@ -83,7 +83,7 @@ void FitSessionController::onStartFittingRequest()
         m_runFitManager->runFitting(m_objectiveBuilder);
 
     } catch (std::exception& e) {
-        m_jobItem->setStatus("Failed");
+        m_jobItem->setStatus(JobStatus::Failed);
         m_fitlog->append(e.what(), FitLogFlags::ERROR);
         emit fittingError(QString::fromStdString(e.what()));
     }
@@ -121,7 +121,7 @@ void FitSessionController::onFittingStarted()
 {
     m_fitlog->clearLog();
 
-    m_jobItem->setStatus("Fitting");
+    m_jobItem->setStatus(JobStatus::Fitting);
     m_jobItem->setProgress(0);
     m_jobItem->setBeginTime(m_runFitManager->fitStart());
     m_jobItem->setEndTime(QDateTime());
@@ -131,8 +131,8 @@ void FitSessionController::onFittingStarted()
 
 void FitSessionController::onFittingFinished()
 {
-    if (m_jobItem->getStatus() != "Failed")
-        m_jobItem->setStatus("Completed");
+    if (m_jobItem->getStatus() != JobStatus::Failed)
+        m_jobItem->setStatus(JobStatus::Completed);
     m_jobItem->setEndTime(m_runFitManager->fitEnd());
     m_jobItem->setProgress(100);
 
diff --git a/GUI/Views/FitWidgets/ParameterTuningWidget.cpp b/GUI/Views/FitWidgets/ParameterTuningWidget.cpp
index 549ac2a648b488b1b5006c34a86ec82b786b9aed..e3eedde0fe90ecec0fe1b100d323e524506026b6 100644
--- a/GUI/Views/FitWidgets/ParameterTuningWidget.cpp
+++ b/GUI/Views/FitWidgets/ParameterTuningWidget.cpp
@@ -191,7 +191,7 @@ JobItem* ParameterTuningWidget::jobItem()
 void ParameterTuningWidget::updateDragAndDropSettings()
 {
     ASSERT(jobItem());
-    if (jobItem()->getStatus() == "Fitting") {
+    if (jobItem()->getStatus() == JobStatus::Fitting) {
         setTuningDelegateEnabled(false);
         m_treeView->setDragDropMode(QAbstractItemView::NoDragDrop);
     } else {
diff --git a/GUI/Views/FitWidgets/RunFitControlWidget.cpp b/GUI/Views/FitWidgets/RunFitControlWidget.cpp
index 2e08a1e21169a54ef853fd5082724fcddfc4ba63..7c2429acbf666839d3a760d2ece34df6ec20550d 100644
--- a/GUI/Views/FitWidgets/RunFitControlWidget.cpp
+++ b/GUI/Views/FitWidgets/RunFitControlWidget.cpp
@@ -157,7 +157,7 @@ void RunFitControlWidget::updateControlElements()
 {
     setEnabled(isValidJobItem());
 
-    if (jobItem()->getStatus() == "Fitting") {
+    if (jobItem()->getStatus() == JobStatus::Fitting) {
         m_startButton->setEnabled(false);
         m_stopButton->setEnabled(true);
         m_cautionSign->clear();
diff --git a/GUI/Views/InstrumentWidgets/InstrumentEditor.cpp b/GUI/Views/InstrumentWidgets/InstrumentEditor.cpp
index ff621958d1112450e9d6f6d67deac74675fa5ed7..4bd008392d96190584488afd1adec34f443fc32b 100644
--- a/GUI/Views/InstrumentWidgets/InstrumentEditor.cpp
+++ b/GUI/Views/InstrumentWidgets/InstrumentEditor.cpp
@@ -131,8 +131,6 @@ void InstrumentEditor::setItem(InstrumentItem* item)
 
 void InstrumentEditor::onInstrumentNameChanged()
 {
-    if (m_item) {
+    if (m_item)
         m_item->setItemName(m_nameLineEdit->text());
-        emit instrumentNameChanged();
-    }
 }
diff --git a/GUI/Views/InstrumentWidgets/InstrumentEditor.h b/GUI/Views/InstrumentWidgets/InstrumentEditor.h
index 291eb5dd1dd8dda144d5188532a53537d8a6c4cc..c60a0857cbd8019d385bb45b49f1e8e8766ce373 100644
--- a/GUI/Views/InstrumentWidgets/InstrumentEditor.h
+++ b/GUI/Views/InstrumentWidgets/InstrumentEditor.h
@@ -40,9 +40,6 @@ public:
 
     void setItem(InstrumentItem* instrument);
 
-signals:
-    void instrumentNameChanged();
-
 private slots:
     void onInstrumentNameChanged();
 
diff --git a/GUI/Views/InstrumentWidgets/InstrumentListModel.cpp b/GUI/Views/InstrumentWidgets/InstrumentListModel.cpp
index df58d282c718e0ab5bcb62d633618761376b1a3a..c152878cba0cd0bf6d47307f6e2244681744d216 100644
--- a/GUI/Views/InstrumentWidgets/InstrumentListModel.cpp
+++ b/GUI/Views/InstrumentWidgets/InstrumentListModel.cpp
@@ -66,6 +66,15 @@ InstrumentListModel::InstrumentListModel(InstrumentModel* instruments, QObject*
     m_specularIcon.addPixmap(QPixmap(":/images/specular_instrument_shaded.svg"), QIcon::Normal);
     m_depthProbeIcon.addPixmap(QPixmap(":/images/depth_instrument.svg"), QIcon::Selected);
     m_depthProbeIcon.addPixmap(QPixmap(":/images/depth_instrument_shaded.svg"), QIcon::Normal);
+
+    for (InstrumentItem* instrument : m_instruments->instrumentItems())
+        attachToInstrument(instrument);
+}
+
+InstrumentListModel::~InstrumentListModel()
+{
+    for (InstrumentItem* instrument : m_instruments->instrumentItems())
+        detachFromInstrument(instrument);
 }
 
 int InstrumentListModel::rowCount(const QModelIndex&) const
@@ -129,7 +138,9 @@ QModelIndex InstrumentListModel::addNewDepthProbeInstrument()
 void InstrumentListModel::removeInstrument(const QModelIndex& index)
 {
     beginRemoveRows(QModelIndex(), index.row(), index.row());
-    m_instruments->removeInstrument(instrumentForIndex(index));
+    InstrumentItem* instrument = instrumentForIndex(index);
+    detachFromInstrument(instrument);
+    m_instruments->removeInstrument(instrument);
     endRemoveRows();
 }
 
@@ -149,6 +160,7 @@ QModelIndex InstrumentListModel::copyInstrument(const InstrumentItem* source)
     beginInsertRows(QModelIndex(), row, row);
     InstrumentItem* copy = m_instruments->insertCopy(*source);
     copy->setItemName(copyName);
+    attachToInstrument(copy);
     endInsertRows();
 
     return m_instruments->indexOfItem(copy);
@@ -166,7 +178,31 @@ template <class Instrument> QModelIndex InstrumentListModel::addNewInstrument()
     beginInsertRows(QModelIndex(), row, row);
     Instrument* instrument = m_instruments->insertItem<Instrument>();
     instrument->setItemName(name);
+    attachToInstrument(instrument);
     endInsertRows();
 
     return m_instruments->indexOfItem(instrument);
 }
+
+void InstrumentListModel::attachToInstrument(InstrumentItem* instrument)
+{
+    instrument->mapper()->setOnPropertyChange
+        (std::bind(&InstrumentListModel::notifyInstrumentPropertyChange, this, instrument,
+                   std::placeholders::_1), this);
+}
+
+void InstrumentListModel::detachFromInstrument(InstrumentItem* instrument)
+{
+    instrument->mapper()->unsubscribe(this);
+}
+
+void InstrumentListModel::notifyInstrumentPropertyChange(InstrumentItem* instrument,
+                                                         const QString& property)
+{
+    if (SessionItem::isItemNamePropertyName(property)) {
+        QVector<InstrumentItem*> instruments = m_instruments->instrumentItems();
+        int i = instruments.indexOf(instrument);
+        if (i != -1)
+            emit dataChanged(index(i,0),index(i,0));
+    }
+}
diff --git a/GUI/Views/InstrumentWidgets/InstrumentListModel.h b/GUI/Views/InstrumentWidgets/InstrumentListModel.h
index de0b10658b80a13e1ce929360fcfff1e8f7c7acc..182f381bdf5c73b4dcb9236bace09faf82f45148 100644
--- a/GUI/Views/InstrumentWidgets/InstrumentListModel.h
+++ b/GUI/Views/InstrumentWidgets/InstrumentListModel.h
@@ -26,6 +26,7 @@ class InstrumentListModel : public QAbstractListModel {
 
 public:
     InstrumentListModel(InstrumentModel* instruments, QObject* parent = nullptr);
+    ~InstrumentListModel();
 
     virtual int rowCount(const QModelIndex& parent = QModelIndex()) const override;
 
@@ -50,6 +51,10 @@ public:
 private:
     template <class Instrument> QModelIndex addNewInstrument();
 
+    void attachToInstrument(InstrumentItem* item);
+    void detachFromInstrument(InstrumentItem* item);
+    void notifyInstrumentPropertyChange(InstrumentItem* instrument, const QString& property);
+
 private:
     InstrumentModel* m_instruments;
 
diff --git a/GUI/Views/InstrumentWidgets/InstrumentListView.cpp b/GUI/Views/InstrumentWidgets/InstrumentListView.cpp
index 04545dc1c6c85ceb9ff75cce036bfd83c1e649f5..e9c64295323067c25579e8002b1c99e60d8dc700 100644
--- a/GUI/Views/InstrumentWidgets/InstrumentListView.cpp
+++ b/GUI/Views/InstrumentWidgets/InstrumentListView.cpp
@@ -146,11 +146,6 @@ QList<QAction*> InstrumentListView::toolbarActions() const
             m_loadFromLibraryAction};
 }
 
-void InstrumentListView::updateSelectedInstrument()
-{
-    update();
-}
-
 void InstrumentListView::onItemSelectionChanged()
 {
     updateActions();
diff --git a/GUI/Views/InstrumentWidgets/InstrumentListView.h b/GUI/Views/InstrumentWidgets/InstrumentListView.h
index 3619741f41a84587d986b1fda9bd4f9ef8a3e2b1..917c07163e66c37688ffee426ba59c3ea6915d31 100644
--- a/GUI/Views/InstrumentWidgets/InstrumentListView.h
+++ b/GUI/Views/InstrumentWidgets/InstrumentListView.h
@@ -41,9 +41,6 @@ public:
 signals:
     void instrumentSelected(InstrumentItem* instrument);
 
-public slots:
-    void updateSelectedInstrument();
-
 private slots:
     void onItemSelectionChanged();
     void onNewGisas();
diff --git a/GUI/Views/InstrumentWidgets/InstrumentView.cpp b/GUI/Views/InstrumentWidgets/InstrumentView.cpp
index f2827c8dbcde053531fec48fd261a26e0e1a3456..ece40599aa1d76f20bd08ae800ec507afba46d43 100644
--- a/GUI/Views/InstrumentWidgets/InstrumentView.cpp
+++ b/GUI/Views/InstrumentWidgets/InstrumentView.cpp
@@ -44,9 +44,6 @@ InstrumentView::InstrumentView(QWidget* parent, ProjectDocument* document)
     connect(m_instrumentListView, &InstrumentListView::instrumentSelected, this,
             &InstrumentView::onInstrumentSelected);
 
-    connect(m_instrumentEditor, &InstrumentEditor::instrumentNameChanged, m_instrumentListView,
-            &InstrumentListView::updateSelectedInstrument);
-
     onInstrumentSelected(nullptr);
 }
 
diff --git a/GUI/Views/JobView.cpp b/GUI/Views/JobView.cpp
index ce987983d953fe2b1f4c0d818575462874f745d2..daefd3f44d651989d4f9d9821ef4413805deeef7 100644
--- a/GUI/Views/JobView.cpp
+++ b/GUI/Views/JobView.cpp
@@ -61,7 +61,8 @@ void JobView::onFocusRequest(JobItem* jobItem)
     if (jobItem->runInBackground())
         return;
 
-    if (jobItem != m_jobSelector->currentJobItem()) {
+    QVector<JobItem*> jobs = m_jobSelector->selectedJobs();
+    if (jobs.size() != 1 || jobItem != jobs.front()) {
         m_jobSelector->makeJobItemSelected(jobItem);
         setAppropriateActivityForJob(jobItem);
     }
@@ -87,8 +88,11 @@ void JobView::setActivity(int activity)
 
 //! Propagates change in JobItem's selection down into main widgets.
 
-void JobView::onSelectionChanged(JobItem* jobItem)
+void JobView::onSelectionChanged(const QVector<JobItem*>& jobs)
 {
+    JobItem* jobItem = nullptr;
+    if (jobs.size() == 1)
+        jobItem = jobs.front();
     m_jobOutputDataWidget->setItem(jobItem);
     m_jobRealTimeWidget->setItem(jobItem);
     m_fitActivityPanel->setItem(jobItem);
@@ -154,7 +158,7 @@ void JobView::connectJobRelated()
     connect(m_document->jobModel(), &JobModel::focusRequest, this, &JobView::onFocusRequest);
 
     // JobItem selection: JobSelectorWidget -> this
-    connect(m_jobSelector, &JobSelectorWidget::selectionChanged, this,
+    connect(m_jobSelector, &JobSelectorWidget::selectedJobsChanged, this,
             &JobView::onSelectionChanged);
 }
 
diff --git a/GUI/Views/JobView.h b/GUI/Views/JobView.h
index e7b302618dba258d47881f9346efbdb3807baf3a..79264b24229b64af934ceae4178ee5c010d7bab9 100644
--- a/GUI/Views/JobView.h
+++ b/GUI/Views/JobView.h
@@ -48,7 +48,7 @@ signals:
 public slots:
     void onFocusRequest(JobItem* jobItem);
     void setActivity(int activity);
-    void onSelectionChanged(JobItem* jobItem);
+    void onSelectionChanged(const QVector<JobItem*>& jobs);
 
 private:
     void createSubWindows();
diff --git a/GUI/Views/JobWidgets/JobListModel.cpp b/GUI/Views/JobWidgets/JobListModel.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..3d8640a8c2eaabec7dd61abec90ab07a55b169cb
--- /dev/null
+++ b/GUI/Views/JobWidgets/JobListModel.cpp
@@ -0,0 +1,146 @@
+//  ************************************************************************************************
+//
+//  BornAgain: simulate and fit reflection and scattering
+//
+//! @file      GUI/Views/JobWidgets/JobListModel.cpp
+//! @brief     Implements class JobListModel
+//!
+//! @homepage  http://www.bornagainproject.org
+//! @license   GNU General Public License v3 or higher (see COPYING)
+//! @copyright Forschungszentrum Jülich GmbH 2018
+//! @authors   Scientific Computing Group at MLZ (see CITATION, AUTHORS)
+//
+//  ************************************************************************************************
+
+#include "GUI/Views/JobWidgets/JobListModel.h"
+#include "GUI/Models/JobItem.h"
+#include "GUI/Models/JobModel.h"
+
+//==================================================================================================
+// JobListModel
+//==================================================================================================
+
+//--------------------------------------------------------------------------------------------------
+// public member functions
+//--------------------------------------------------------------------------------------------------
+
+JobListModel::JobListModel(JobModel* jobs, QObject* parent)
+    : QAbstractListModel(parent), m_jobs(jobs)
+{
+    for (JobItem* job : m_jobs->jobItems())
+        attachToJob(job);
+
+    connect(jobs, &QAbstractItemModel::rowsAboutToBeInserted, this,
+            &JobListModel::onRowsAboutToBeInserted);
+    connect(jobs, &QAbstractItemModel::rowsInserted, this, &JobListModel::onRowsInserted);
+}
+
+JobListModel::~JobListModel()
+{
+    for (JobItem* job : m_jobs->jobItems())
+        detachFromJob(job);
+}
+
+int JobListModel::rowCount(const QModelIndex&) const
+{
+    return m_jobs->jobItems().size();
+}
+
+QVariant JobListModel::data(const QModelIndex& index, int role) const
+{
+    QVector<JobItem*> jobs = m_jobs->jobItems();
+    if (!index.isValid() || index.row() >= jobs.size() || index.row() < 0)
+        return QVariant();
+
+    JobItem* item = jobs[index.row()];
+    if (role == Qt::DisplayRole)
+        return item->itemName();
+
+    return QVariant();
+}
+
+JobItem* JobListModel::jobForIndex(const QModelIndex& index) const
+{
+    QVector<JobItem*> jobs = m_jobs->jobItems();
+    if (index.row() >= 0 && index.row() < jobs.size())
+        return jobs[index.row()];
+    else
+        return nullptr;
+}
+
+QModelIndex JobListModel::indexForJob(JobItem* job)
+{
+    QVector<JobItem*> jobs = m_jobs->jobItems();
+    int idx = jobs.indexOf(job);
+    if (idx != -1)
+        return index(idx, 0);
+    else
+        return QModelIndex();
+}
+
+void JobListModel::runJob(const QModelIndex& index)
+{
+    m_jobs->runJob(m_jobs->indexOfItem(jobForIndex(index)));
+}
+
+void JobListModel::removeJob(const QModelIndex& index)
+{
+    beginRemoveRows(QModelIndex(), index.row(), index.row());
+    JobItem* job = jobForIndex(index);
+    detachFromJob(job);
+    m_jobs->removeJob(m_jobs->indexOfItem(job));
+    endRemoveRows();
+}
+
+void JobListModel::cancelJob(const QModelIndex& index)
+{
+    m_jobs->cancelJob(m_jobs->indexOfItem(jobForIndex(index)));
+}
+
+//--------------------------------------------------------------------------------------------------
+// private slots
+//--------------------------------------------------------------------------------------------------
+
+void JobListModel::onRowsAboutToBeInserted(const QModelIndex& parent, int start, int end)
+{
+    if (!parent.isValid())
+        beginInsertRows(QModelIndex(), start, end);
+}
+
+void JobListModel::onRowsInserted(const QModelIndex& parent, int start, int end)
+{
+    if (!parent.isValid()) {
+        endInsertRows();
+        QVector<JobItem*> jobs = m_jobs->jobItems();
+        for (int i = start; i <= end; i++)
+            attachToJob(jobs.at(i));
+    }
+}
+
+//--------------------------------------------------------------------------------------------------
+// private member functions
+//--------------------------------------------------------------------------------------------------
+
+void JobListModel::attachToJob(JobItem* job)
+{
+    job->mapper()->setOnPropertyChange(
+        std::bind(&JobListModel::notifyJobPropertyChange, this, job, std::placeholders::_1), this);
+}
+
+void JobListModel::detachFromJob(JobItem* job)
+{
+    job->mapper()->unsubscribe(this);
+}
+
+void JobListModel::notifyJobPropertyChange(JobItem* job, const QString& property)
+{
+    if (SessionItem::isItemNamePropertyName(property) || JobItem::isStatusPropertyName(property)
+        || JobItem::isProgressPropertyName(property)) {
+        QVector<JobItem*> jobs = m_jobs->jobItems();
+        int i = jobs.indexOf(job);
+        if (i != -1) {
+            QModelIndex idx = index(i, 0);
+            emit dataChanged(idx, idx);
+        }
+    }
+}
diff --git a/GUI/Views/JobWidgets/JobListModel.h b/GUI/Views/JobWidgets/JobListModel.h
new file mode 100644
index 0000000000000000000000000000000000000000..6082852a6d80c60baa6e03ac9ad61e718efe42f2
--- /dev/null
+++ b/GUI/Views/JobWidgets/JobListModel.h
@@ -0,0 +1,54 @@
+//  ************************************************************************************************
+//
+//  BornAgain: simulate and fit reflection and scattering
+//
+//! @file      GUI/Views/JobWidgets/JobListModel.h
+//! @brief     Defines class JobListModel
+//!
+//! @homepage  http://www.bornagainproject.org
+//! @license   GNU General Public License v3 or higher (see COPYING)
+//! @copyright Forschungszentrum Jülich GmbH 2018
+//! @authors   Scientific Computing Group at MLZ (see CITATION, AUTHORS)
+//
+//  ************************************************************************************************
+
+#ifndef BORNAGAIN_GUI_VIEWS_JOBWIDGETS_JOBLISTMODEL_H
+#define BORNAGAIN_GUI_VIEWS_JOBWIDGETS_JOBLISTMODEL_H
+
+#include <QAbstractListModel>
+
+class JobItem;
+class JobModel;
+
+class JobListModel : public QAbstractListModel {
+    Q_OBJECT
+
+public:
+    JobListModel(JobModel* jobs, QObject* parent = nullptr);
+    ~JobListModel();
+
+    virtual int rowCount(const QModelIndex& parent = QModelIndex()) const override;
+
+    virtual QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override;
+
+    JobItem* jobForIndex(const QModelIndex& index) const;
+    QModelIndex indexForJob(JobItem* job);
+
+    void runJob(const QModelIndex& index);
+    void removeJob(const QModelIndex& index);
+    void cancelJob(const QModelIndex& index);
+
+private slots:
+    void onRowsAboutToBeInserted(const QModelIndex& parent, int start, int end);
+    void onRowsInserted(const QModelIndex& parent, int start, int end);
+
+private:
+    void attachToJob(JobItem* item);
+    void detachFromJob(JobItem* item);
+    void notifyJobPropertyChange(JobItem* job, const QString& property);
+
+private:
+    JobModel* m_jobs;
+};
+
+#endif // BORNAGAIN_GUI_VIEWS_JOBWIDGETS_JOBLISTMODEL_H
diff --git a/GUI/Views/JobWidgets/JobListView.cpp b/GUI/Views/JobWidgets/JobListView.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..a1a29c98ef3fd7e9edd561d68bb33570be2595e4
--- /dev/null
+++ b/GUI/Views/JobWidgets/JobListView.cpp
@@ -0,0 +1,251 @@
+//  ************************************************************************************************
+//
+//  BornAgain: simulate and fit reflection and scattering
+//
+//! @file      GUI/Views/JobWidgets/JobListView.cpp
+//! @brief     Implements class JobListView
+//!
+//! @homepage  http://www.bornagainproject.org
+//! @license   GNU General Public License v3 or higher (see COPYING)
+//! @copyright Forschungszentrum Jülich GmbH 2018
+//! @authors   Scientific Computing Group at MLZ (see CITATION, AUTHORS)
+//
+//  ************************************************************************************************
+
+#include "GUI/Views/JobWidgets/JobListView.h"
+#include "GUI/Models/IntensityDataItem.h"
+#include "GUI/Models/JobItem.h"
+#include "GUI/Models/JobModel.h"
+#include "GUI/Views/CommonWidgets/StyledToolBar.h"
+#include "GUI/Views/JobWidgets/JobListModel.h"
+#include "GUI/Views/JobWidgets/JobListViewDelegate.h"
+#include <QAction>
+#include <QListView>
+#include <QMenu>
+#include <QVBoxLayout>
+
+namespace {
+//! compare function for sorting indexes according to row descending
+bool row_descending(const QModelIndex& idx1, const QModelIndex& idx2)
+{
+    return idx1.row() > idx2.row();
+}
+
+//! compare function for sorting indexes according to row asscending
+bool row_ascending(const QModelIndex& idx1, const QModelIndex& idx2)
+{
+    return idx1.row() < idx2.row();
+}
+} // namespace
+
+//==================================================================================================
+// JobListView
+//==================================================================================================
+
+//--------------------------------------------------------------------------------------------------
+// public member functions
+//--------------------------------------------------------------------------------------------------
+
+JobListView::JobListView(JobModel* jobs, QWidget* parent, Qt::WindowFlags f) : QWidget(parent, f)
+{
+    setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Expanding);
+
+    QVBoxLayout* layout = new QVBoxLayout(this);
+    layout->setMargin(0);
+
+    m_runAction = new QAction("Run", this);
+    m_runAction->setIcon(QIcon(":/images/play.svg"));
+    m_runAction->setToolTip("Run currently selected jobs");
+    connect(m_runAction, &QAction::triggered, this, &JobListView::onRun);
+    addAction(m_runAction);
+
+    m_cancelAction = new QAction("Stop", this);
+    m_cancelAction->setIcon(QIcon(":/images/hand-right.svg"));
+    m_cancelAction->setToolTip("Stop currently selected jobs");
+    connect(m_cancelAction, &QAction::triggered, this, &JobListView::onCancel);
+    addAction(m_cancelAction);
+
+    m_removeAction = new QAction("Remove", this);
+    m_removeAction->setIcon(QIcon(":/images/delete.svg"));
+    m_removeAction->setToolTip("Remove currently selected jobs");
+    connect(m_removeAction, &QAction::triggered, this, &JobListView::onRemove);
+    addAction(m_removeAction);
+
+    m_equalizeMenu = new QMenu("Equalize selected plots", this);
+
+    QToolBar* toolBar = new StyledToolBar(this);
+    toolBar->setMinimumSize(toolBar->minimumHeight(), toolBar->minimumHeight());
+    toolBar->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
+    toolBar->addAction(m_runAction);
+    toolBar->addAction(m_cancelAction);
+    toolBar->addAction(m_removeAction);
+    layout->addWidget(toolBar);
+
+    m_listView = new QListView(this);
+    m_listView->setSelectionMode(QAbstractItemView::ExtendedSelection);
+    m_listViewDelegate = new JobListViewDelegate(this);
+    m_listView->setItemDelegate(m_listViewDelegate);
+    layout->addWidget(m_listView);
+
+    m_model = new JobListModel(jobs, this);
+    m_listView->setModel(m_model);
+
+    setContextMenuPolicy(Qt::CustomContextMenu);
+    connect(this, &QWidget::customContextMenuRequested, this, &JobListView::showContextMenu);
+
+    connect(m_listView->selectionModel(), &QItemSelectionModel::selectionChanged, this,
+            &JobListView::onItemSelectionChanged);
+    connect(m_model, &QAbstractItemModel::dataChanged, this, &JobListView::onDataChanged);
+
+    updateActions();
+    ensureItemSelected();
+}
+
+QVector<JobItem*> JobListView::selectedJobs() const
+{
+    QVector<JobItem*> jobs;
+    for (const QModelIndex& index : m_listView->selectionModel()->selectedIndexes())
+        jobs.push_back(m_model->jobForIndex(index));
+    return jobs;
+}
+
+void JobListView::selectJob(JobItem* job)
+{
+    QModelIndex idx = m_model->indexForJob(job);
+    QModelIndexList selected = m_listView->selectionModel()->selectedIndexes();
+
+    // Already selected, but we still will emit the signal to notify widgets.
+    // To handle the case, when the job was selected before it completed (and some stack widgets
+    // were refusing to show the content for non-complete job).
+    if (selected.size() == 1 && selected.front() == idx) {
+        emit selectedJobsChanged({job});
+        return;
+    }
+
+    m_listView->selectionModel()->select(idx, QItemSelectionModel::ClearAndSelect);
+}
+
+//--------------------------------------------------------------------------------------------------
+// private slots
+//--------------------------------------------------------------------------------------------------
+
+void JobListView::onItemSelectionChanged()
+{
+    updateActions();
+
+    emit selectedJobsChanged(selectedJobs());
+}
+
+void JobListView::onDataChanged(const QModelIndex& topLeft, const QModelIndex& bottomRight)
+{
+    // currently only single items change, not ranges; thus ranges are not supported
+    ASSERT(topLeft == bottomRight);
+
+    if (m_listView->selectionModel()->isSelected(topLeft))
+        updateActions();
+}
+
+void JobListView::onRun()
+{
+    for (const QModelIndex& index : m_listView->selectionModel()->selectedIndexes())
+        m_model->runJob(index);
+}
+
+void JobListView::onCancel()
+{
+    for (const QModelIndex& index : m_listView->selectionModel()->selectedIndexes())
+        m_model->cancelJob(index);
+}
+
+void JobListView::onRemove()
+{
+    QModelIndexList indexes = m_listView->selectionModel()->selectedIndexes();
+    std::sort(indexes.begin(), indexes.end(), row_descending);
+    for (const QModelIndex& index : indexes)
+        m_model->removeJob(index);
+    ensureItemSelected();
+}
+
+void JobListView::equalizeSelectedToJob(JobItem* srcJob)
+{
+    QModelIndexList indexes = m_listView->selectionModel()->selectedIndexes();
+
+    IntensityDataItem* srcData = srcJob->intensityDataItem();
+    if (!srcData)
+        return;
+
+    for (const QModelIndex& index : indexes) {
+        JobItem* job = m_model->jobForIndex(index);
+        if (job != srcJob) {
+            IntensityDataItem* data = job->intensityDataItem();
+            if (data) {
+                data->setLowerX(srcData->getLowerX());
+                data->setUpperX(srcData->getUpperX());
+                data->setLowerY(srcData->getLowerY());
+                data->setUpperY(srcData->getUpperY());
+                data->setLowerZ(srcData->getLowerZ());
+                data->setUpperZ(srcData->getUpperZ());
+            }
+        }
+    }
+}
+
+void JobListView::showContextMenu(const QPoint&)
+{
+    QMenu menu(this);
+    menu.addAction(m_runAction);
+    menu.addAction(m_cancelAction);
+    menu.addAction(m_removeAction);
+    menu.addSeparator();
+
+    m_equalizeMenu->clear();
+    QModelIndexList indexes = m_listView->selectionModel()->selectedIndexes();
+    if (indexes.size() > 1) {
+        std::sort(indexes.begin(), indexes.end(), row_ascending);
+        for (const QModelIndex& index : indexes) {
+            JobItem* job = m_model->jobForIndex(index);
+            QAction* action = m_equalizeMenu->addAction(QString("to ").append(job->itemName()));
+            connect(action, &QAction::triggered, this,
+                    std::bind(&JobListView::equalizeSelectedToJob, this, job));
+        }
+        m_equalizeMenu->setEnabled(true);
+    } else
+        m_equalizeMenu->setEnabled(false);
+    menu.addMenu(m_equalizeMenu);
+    menu.exec(QCursor::pos());
+}
+
+//--------------------------------------------------------------------------------------------------
+// private member functions
+//--------------------------------------------------------------------------------------------------
+
+void JobListView::updateActions()
+{
+    QModelIndexList indexes = m_listView->selectionModel()->selectedIndexes();
+
+    struct IsRunningOrFitting {
+        JobListModel* m_model;
+        IsRunningOrFitting(JobListModel* model) : m_model(model) {}
+        bool operator()(const QModelIndex& i) const
+        {
+            JobItem* job = m_model->jobForIndex(i);
+            ASSERT(job);
+            return job->isRunning() || job->isFitting();
+        }
+    };
+
+    bool none_running = std::none_of(indexes.begin(), indexes.end(), IsRunningOrFitting(m_model));
+    bool all_running = std::all_of(indexes.begin(), indexes.end(), IsRunningOrFitting(m_model));
+    bool nonempty = !indexes.empty();
+    m_runAction->setEnabled(nonempty && none_running);
+    m_cancelAction->setEnabled(nonempty && all_running);
+    m_removeAction->setEnabled(nonempty && none_running);
+}
+
+void JobListView::ensureItemSelected()
+{
+    if (!m_listView->selectionModel()->hasSelection() && m_model->rowCount()) {
+        QModelIndex last = m_model->index(m_model->rowCount() - 1, 0, QModelIndex());
+        m_listView->selectionModel()->select(last, QItemSelectionModel::ClearAndSelect);
+    }
+}
diff --git a/GUI/Views/JobWidgets/JobListView.h b/GUI/Views/JobWidgets/JobListView.h
new file mode 100644
index 0000000000000000000000000000000000000000..063dbfb2472fa4d0bcf169ed4e1442859a32efff
--- /dev/null
+++ b/GUI/Views/JobWidgets/JobListView.h
@@ -0,0 +1,64 @@
+//  ************************************************************************************************
+//
+//  BornAgain: simulate and fit reflection and scattering
+//
+//! @file      GUI/Views/JobWidgets/JobListView.h
+//! @brief     Defines class JobListView
+//!
+//! @homepage  http://www.bornagainproject.org
+//! @license   GNU General Public License v3 or higher (see COPYING)
+//! @copyright Forschungszentrum Jülich GmbH 2018
+//! @authors   Scientific Computing Group at MLZ (see CITATION, AUTHORS)
+//
+//  ************************************************************************************************
+
+#ifndef BORNAGAIN_GUI_VIEWS_JOBWIDGETS_JOBLISTVIEW_H
+#define BORNAGAIN_GUI_VIEWS_JOBWIDGETS_JOBLISTVIEW_H
+
+#include <QWidget>
+
+class JobModel;
+class QAction;
+class QListView;
+class QMenu;
+class JobItem;
+class JobListModel;
+class JobListViewDelegate;
+
+//! List of jobs on the top left side of JobView.
+
+class JobListView : public QWidget {
+    Q_OBJECT
+
+public:
+    JobListView(JobModel* jobs, QWidget* parent = nullptr, Qt::WindowFlags f = Qt::WindowFlags());
+    QVector<JobItem*> selectedJobs() const;
+    void selectJob(JobItem* job);
+
+signals:
+    void selectedJobsChanged(const QVector<JobItem*>& jobs);
+
+private slots:
+    void onItemSelectionChanged();
+    void onDataChanged(const QModelIndex& topLeft, const QModelIndex& bottomRight);
+    void onRun();
+    void onCancel();
+    void onRemove();
+    void equalizeSelectedToJob(JobItem* job);
+    void showContextMenu(const QPoint& pos);
+
+private:
+    void updateActions();
+    void ensureItemSelected();
+
+private:
+    QListView* m_listView;
+    JobListViewDelegate* m_listViewDelegate;
+    JobListModel* m_model;
+    QAction* m_runAction;
+    QAction* m_cancelAction;
+    QAction* m_removeAction;
+    QMenu* m_equalizeMenu;
+};
+
+#endif // BORNAGAIN_GUI_VIEWS_JOBWIDGETS_JOBLISTVIEW_H
diff --git a/GUI/Views/JobWidgets/JobListViewDelegate.cpp b/GUI/Views/JobWidgets/JobListViewDelegate.cpp
index 0c306ac4ede4f1c61e465b1258d17a75a2cf8ad7..e159fce4d8c1d0dc58a9eb8f24af825a8e712905 100644
--- a/GUI/Views/JobWidgets/JobListViewDelegate.cpp
+++ b/GUI/Views/JobWidgets/JobListViewDelegate.cpp
@@ -14,7 +14,7 @@
 
 #include "GUI/Views/JobWidgets/JobListViewDelegate.h"
 #include "GUI/Models/JobItem.h"
-#include "GUI/Models/JobModel.h"
+#include "GUI/Views/JobWidgets/JobListModel.h"
 #include <QApplication>
 #include <QMouseEvent>
 #include <QPaintDevice>
@@ -25,11 +25,11 @@
 JobListViewDelegate::JobListViewDelegate(QWidget* parent) : QItemDelegate(parent)
 {
     m_buttonState = QStyle::State_Enabled;
-    m_status_to_color["Idle"] = QColor(255, 286, 12);
-    m_status_to_color["Running"] = QColor(5, 150, 230);
-    m_status_to_color["Completed"] = QColor(5, 150, 230);
-    m_status_to_color["Canceled"] = QColor(186, 0, 0);
-    m_status_to_color["Failed"] = QColor(255, 186, 12);
+    m_status_to_color[JobStatus::Idle] = QColor(255, 286, 12);
+    m_status_to_color[JobStatus::Running] = QColor(5, 150, 230);
+    m_status_to_color[JobStatus::Completed] = QColor(5, 150, 230);
+    m_status_to_color[JobStatus::Canceled] = QColor(186, 0, 0);
+    m_status_to_color[JobStatus::Failed] = QColor(255, 186, 12);
 }
 
 void JobListViewDelegate::paint(QPainter* painter, const QStyleOptionViewItem& option,
@@ -38,10 +38,10 @@ void JobListViewDelegate::paint(QPainter* painter, const QStyleOptionViewItem& o
     if (option.state & QStyle::State_Selected)
         painter->fillRect(option.rect, option.palette.highlight());
 
-    const JobModel* model = static_cast<const JobModel*>(index.model());
+    const JobListModel* model = dynamic_cast<const JobListModel*>(index.model());
     ASSERT(model);
 
-    const JobItem* item = model->getJobItemForIndex(index);
+    const JobItem* item = model->jobForIndex(index);
     ASSERT(item);
 
     painter->save();
@@ -76,10 +76,10 @@ bool JobListViewDelegate::editorEvent(QEvent* event, QAbstractItemModel* model,
         return QItemDelegate::editorEvent(event, model, option, index);
     }
 
-    const JobModel* jqmodel = static_cast<const JobModel*>(index.model());
+    const JobListModel* jqmodel = dynamic_cast<const JobListModel*>(index.model());
     ASSERT(model);
 
-    const JobItem* item = jqmodel->getJobItemForIndex(index);
+    const JobItem* item = jqmodel->jobForIndex(index);
     ASSERT(item);
 
     if (!item->isRunning())
@@ -97,7 +97,6 @@ bool JobListViewDelegate::editorEvent(QEvent* event, QAbstractItemModel* model,
         m_buttonState = QStyle::State_Sunken;
     } else if (event->type() == QEvent::MouseButtonRelease) {
         m_buttonState = QStyle::State_Raised;
-        qDebug("JobListViewDelegate::editorEvent() -> cancel clicked");
         emit cancelButtonClicked(index);
     }
     return true;
diff --git a/GUI/Views/JobWidgets/JobListViewDelegate.h b/GUI/Views/JobWidgets/JobListViewDelegate.h
index f8edfa8ca5196218cb18fdcedaa4eb6b35e9a9f1..e046ceeac3e1b6c31a78f8bd5e993157d1a438f7 100644
--- a/GUI/Views/JobWidgets/JobListViewDelegate.h
+++ b/GUI/Views/JobWidgets/JobListViewDelegate.h
@@ -20,6 +20,7 @@
 #include <QRect>
 
 class JobItem;
+enum class JobStatus;
 
 //! ViewDelegate to show progress bar JobQueuListView
 class JobListViewDelegate : public QItemDelegate {
@@ -45,7 +46,7 @@ private:
     QRect getProgressBarRect(QRect optionRect) const;
     QRect getButtonRect(QRect optionRect) const;
 
-    QMap<QString, QColor> m_status_to_color;
+    QMap<JobStatus, QColor> m_status_to_color;
 };
 
 #endif // BORNAGAIN_GUI_VIEWS_JOBWIDGETS_JOBLISTVIEWDELEGATE_H
diff --git a/GUI/Views/JobWidgets/JobListWidget.cpp b/GUI/Views/JobWidgets/JobListWidget.cpp
deleted file mode 100644
index 2ec724a56e257902c1abcaa0bf55ef5892bc73dd..0000000000000000000000000000000000000000
--- a/GUI/Views/JobWidgets/JobListWidget.cpp
+++ /dev/null
@@ -1,120 +0,0 @@
-//  ************************************************************************************************
-//
-//  BornAgain: simulate and fit reflection and scattering
-//
-//! @file      GUI/Views/JobWidgets/JobListWidget.cpp
-//! @brief     Implements class JobListWidget
-//!
-//! @homepage  http://www.bornagainproject.org
-//! @license   GNU General Public License v3 or higher (see COPYING)
-//! @copyright Forschungszentrum Jülich GmbH 2018
-//! @authors   Scientific Computing Group at MLZ (see CITATION, AUTHORS)
-//
-//  ************************************************************************************************
-
-#include "GUI/Views/JobWidgets/JobListWidget.h"
-#include "GUI/Models/JobItem.h"
-#include "GUI/Models/JobModel.h"
-#include "GUI/Views/CommonWidgets/ItemSelectorWidget.h"
-#include "GUI/Views/CommonWidgets/StyleUtils.h"
-#include "GUI/Views/JobWidgets/JobListViewDelegate.h"
-#include <QItemSelectionModel>
-#include <QListView>
-#include <QVBoxLayout>
-
-JobListWidget::JobListWidget(QWidget* parent)
-    : QWidget(parent)
-    , m_listViewDelegate(new JobListViewDelegate(this))
-    , m_listView(new ItemSelectorWidget(this))
-    , m_jobModel(nullptr)
-{
-    setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
-
-    m_listView->listView()->setItemDelegate(m_listViewDelegate);
-    m_listView->listView()->setSelectionMode(QAbstractItemView::ExtendedSelection);
-
-    auto mainLayout = new QVBoxLayout;
-    mainLayout->setMargin(0);
-    mainLayout->setSpacing(0);
-
-    auto vlayout = new QVBoxLayout;
-    vlayout->setMargin(10);
-    vlayout->setSpacing(10);
-
-    vlayout->addWidget(m_listView);
-    mainLayout->addLayout(vlayout);
-
-    setLayout(mainLayout);
-
-    connect(m_listView, &ItemSelectorWidget::contextMenuRequest, this,
-            &JobListWidget::contextMenuRequest);
-
-    connect(m_listView, &ItemSelectorWidget::selectionChanged, this,
-            &JobListWidget::onItemSelectionChanged);
-}
-
-void JobListWidget::setModel(JobModel* model)
-{
-    ASSERT(model);
-    if (model != m_jobModel) {
-        m_jobModel = model;
-        m_listView->setModel(model);
-
-        connect(m_listViewDelegate, &JobListViewDelegate::cancelButtonClicked, m_jobModel,
-                &JobModel::cancelJob, Qt::UniqueConnection);
-    }
-}
-
-QItemSelectionModel* JobListWidget::selectionModel()
-{
-    return m_listView->selectionModel();
-}
-
-//! Returns currently selected JobItem
-
-const JobItem* JobListWidget::currentJobItem() const
-{
-    QModelIndexList selected = m_listView->selectionModel()->selectedIndexes();
-    return selected.size() == 1 ? m_jobModel->getJobItemForIndex(selected.at(0)) : nullptr;
-}
-
-QSize JobListWidget::sizeHint() const
-{
-    return QSize(GUI::Utils::Style::PropertyPanelWidth(),
-                 GUI::Utils::Style::PropertyPanelWidth() * 2);
-}
-
-QSize JobListWidget::minimumSizeHint() const
-{
-    return QSize(GUI::Utils::Style::PropertyPanelWidth(), GUI::Utils::Style::PropertyPanelWidth());
-}
-
-void JobListWidget::makeJobItemSelected(JobItem* jobItem)
-{
-    ASSERT(jobItem);
-    QModelIndexList selected = m_listView->selectionModel()->selectedIndexes();
-
-    // Already selected, but we still will emit the signal to notify widgets.
-    // To handle the case, when the job was selected before it completed (and some stack widgets
-    // were refusing to show the content for non-complete job).
-    if (selected.size() == 1 && selected.at(0) == jobItem->index()) {
-        emit selectionChanged(jobItem);
-        return;
-    }
-
-    m_listView->selectionModel()->clearSelection();
-    m_listView->selectionModel()->select(jobItem->index(), QItemSelectionModel::Select);
-}
-
-//! Recieves SeesionItem from ItemSelectorWidget and emits it further as JobItem.
-//! Null item means the absence of selection.
-
-void JobListWidget::onItemSelectionChanged(SessionItem* item)
-{
-    JobItem* jobItem(nullptr);
-    if (item) {
-        jobItem = dynamic_cast<JobItem*>(item);
-        ASSERT(jobItem);
-    }
-    emit selectionChanged(jobItem);
-}
diff --git a/GUI/Views/JobWidgets/JobListWidget.h b/GUI/Views/JobWidgets/JobListWidget.h
deleted file mode 100644
index 88f9eff9d8db902f64e7d4d3147388e38986bb89..0000000000000000000000000000000000000000
--- a/GUI/Views/JobWidgets/JobListWidget.h
+++ /dev/null
@@ -1,61 +0,0 @@
-//  ************************************************************************************************
-//
-//  BornAgain: simulate and fit reflection and scattering
-//
-//! @file      GUI/Views/JobWidgets/JobListWidget.h
-//! @brief     Defines class JobListWidget
-//!
-//! @homepage  http://www.bornagainproject.org
-//! @license   GNU General Public License v3 or higher (see COPYING)
-//! @copyright Forschungszentrum Jülich GmbH 2018
-//! @authors   Scientific Computing Group at MLZ (see CITATION, AUTHORS)
-//
-//  ************************************************************************************************
-
-#ifndef BORNAGAIN_GUI_VIEWS_JOBWIDGETS_JOBLISTWIDGET_H
-#define BORNAGAIN_GUI_VIEWS_JOBWIDGETS_JOBLISTWIDGET_H
-
-#include <QModelIndexList>
-#include <QWidget>
-
-class JobModel;
-class JobListViewDelegate;
-class QItemSelectionModel;
-class ItemSelectorWidget;
-class QModelIndex;
-class JobItem;
-class SessionItem;
-
-//! The JobListWidget class contains list view to select job items.
-
-class JobListWidget : public QWidget {
-    Q_OBJECT
-public:
-    explicit JobListWidget(QWidget* parent = nullptr);
-
-    void setModel(JobModel* model);
-
-    QItemSelectionModel* selectionModel();
-
-    const JobItem* currentJobItem() const;
-
-    QSize sizeHint() const;
-    QSize minimumSizeHint() const;
-
-signals:
-    void contextMenuRequest(const QPoint& point, const QModelIndex& index);
-    void selectionChanged(JobItem*);
-
-public slots:
-    void makeJobItemSelected(JobItem* jobItem);
-
-private slots:
-    void onItemSelectionChanged(SessionItem* item);
-
-private:
-    JobListViewDelegate* m_listViewDelegate;
-    ItemSelectorWidget* m_listView;
-    JobModel* m_jobModel;
-};
-
-#endif // BORNAGAIN_GUI_VIEWS_JOBWIDGETS_JOBLISTWIDGET_H
diff --git a/GUI/Views/JobWidgets/JobPropertiesTableModel.cpp b/GUI/Views/JobWidgets/JobPropertiesTableModel.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..04c55fb0bfd934bc1efeb06ba00395793e4897ab
--- /dev/null
+++ b/GUI/Views/JobWidgets/JobPropertiesTableModel.cpp
@@ -0,0 +1,170 @@
+//  ************************************************************************************************
+//
+//  BornAgain: simulate and fit reflection and scattering
+//
+//! @file      GUI/Views/JobWidgets/JobPropertiesTableModel.cpp
+//! @brief     Implements class JobPropertiesTableModel
+//!
+//! @homepage  http://www.bornagainproject.org
+//! @license   GNU General Public License v3 or higher (see COPYING)
+//! @copyright Forschungszentrum Jülich GmbH 2018
+//! @authors   Scientific Computing Group at MLZ (see CITATION, AUTHORS)
+//
+//  ************************************************************************************************
+
+#include "GUI/Views/JobWidgets/JobPropertiesTableModel.h"
+#include "GUI/Models/JobItem.h"
+
+namespace {
+namespace Column {
+enum Columns { Name, Value };
+}
+const QString ColumnNames[] = {"Name", "Value"};
+const int NumColumns = std::size(ColumnNames);
+
+namespace Row {
+enum Rows { Name, Sample, Instrument, Status, Begin, End, Duration };
+}
+const QString RowNames[] = {"Name", "Sample", "Instrument", "Status", "Begin", "End", "Duration"};
+const int NumRows = std::size(RowNames);
+
+const QString ModelDateShortFormat = "yyyy.MM.dd hh:mm:ss";
+} // namespace
+
+//==================================================================================================
+// JobPropertiesTableModel
+//==================================================================================================
+
+//--------------------------------------------------------------------------------------------------
+// public member functions
+//--------------------------------------------------------------------------------------------------
+
+JobPropertiesTableModel::JobPropertiesTableModel(QObject* parent)
+    : QAbstractTableModel(parent), m_item(nullptr)
+{
+}
+
+JobPropertiesTableModel::~JobPropertiesTableModel()
+{
+    if (m_item)
+        m_item->mapper()->unsubscribe(this);
+}
+
+int JobPropertiesTableModel::rowCount(const QModelIndex& parent) const
+{
+    if (!parent.isValid() && m_item)
+        return NumRows;
+    return 0;
+}
+
+int JobPropertiesTableModel::columnCount(const QModelIndex& parent) const
+{
+    if (!parent.isValid() && m_item)
+        return NumColumns;
+    return 0;
+}
+
+QVariant JobPropertiesTableModel::data(const QModelIndex& index, int role) const
+{
+    if ((role != Qt::DisplayRole && role != Qt::EditRole && role != Qt::ToolTipRole)
+        || index.column() < 0 || index.column() >= NumColumns || index.row() < 0
+        || index.row() >= NumRows || !m_item)
+        return QVariant();
+
+    switch (index.column()) {
+    case Column::Name:
+        return RowNames[index.row()];
+    case Column::Value: {
+        switch (index.row()) {
+        case Row::Name:
+            return m_item->itemName();
+        case Row::Sample:
+            return m_item->sampleName();
+        case Row::Instrument:
+            return m_item->instrumentName();
+        case Row::Status:
+            return jobStatusToString(m_item->getStatus());
+        case Row::Begin:
+            if (role == Qt::ToolTipRole)
+                return m_item->beginTime().toString(Qt::DefaultLocaleLongDate);
+            return m_item->beginTime().toString(ModelDateShortFormat);
+        case Row::End:
+            if (role == Qt::ToolTipRole)
+                return m_item->endTime().toString(Qt::DefaultLocaleLongDate);
+            return m_item->endTime().toString(ModelDateShortFormat);
+        case Row::Duration: {
+            std::optional<size_t> duration = m_item->duration();
+            if (duration)
+                return QString("%1 s").arg(duration.value() / 1000., 0, 'f', 3);
+            return QVariant();
+        }
+        default:
+            return QVariant();
+        }
+    }
+    default:
+        return QVariant();
+    }
+}
+
+QVariant JobPropertiesTableModel::headerData(int section, Qt::Orientation orientation,
+                                             int role) const
+{
+    if (role == Qt::DisplayRole && orientation == Qt::Horizontal && section >= 0
+        && section < NumColumns)
+        return ColumnNames[section];
+    return QVariant();
+}
+
+Qt::ItemFlags JobPropertiesTableModel::flags(const QModelIndex& index) const
+{
+    Qt::ItemFlags f = QAbstractTableModel::flags(index);
+    if (index.column() == Column::Value && index.row() == Row::Name && m_item)
+        f.setFlag(Qt::ItemIsEditable);
+    return f;
+}
+
+bool JobPropertiesTableModel::setData(const QModelIndex& index, const QVariant& value, int role)
+{
+    if (role != Qt::EditRole || index.column() != Column::Value || index.row() != Row::Name
+        || !m_item)
+        return false;
+    m_item->setItemName(value.toString());
+    emit dataChanged(index, index, {role});
+    return true;
+}
+
+void JobPropertiesTableModel::setItem(JobItem* item)
+{
+    beginResetModel();
+    if (m_item)
+        m_item->mapper()->unsubscribe(this);
+    m_item = item;
+    if (m_item)
+        m_item->mapper()->setOnPropertyChange(
+            std::bind(&JobPropertiesTableModel::notifyJobPropertyChange, this,
+                      std::placeholders::_1),
+            this);
+    endResetModel();
+}
+
+//--------------------------------------------------------------------------------------------------
+// private member functions
+//--------------------------------------------------------------------------------------------------
+
+void JobPropertiesTableModel::notifyJobPropertyChange(const QString& property)
+{
+    if (SessionItem::isItemNamePropertyName(property))
+        emit dataChanged(index(Row::Name, Column::Value), index(Row::Name, Column::Value),
+                         {Qt::DisplayRole, Qt::EditRole});
+    else if (JobItem::isStatusPropertyName(property))
+        emit dataChanged(index(Row::Status, Column::Value), index(Row::Status, Column::Value),
+                         {Qt::DisplayRole, Qt::EditRole});
+    else if (JobItem::isBeginTimePropertyName(property))
+        emit dataChanged(index(Row::Begin, Column::Value), index(Row::Begin, Column::Value),
+                         {Qt::DisplayRole, Qt::EditRole});
+    else if (JobItem::isEndTimePropertyName(property))
+        // will change duration very probably too
+        emit dataChanged(index(Row::End, Column::Value), index(Row::Duration, Column::Value),
+                         {Qt::DisplayRole, Qt::EditRole});
+}
diff --git a/GUI/Views/JobWidgets/JobPropertiesTableModel.h b/GUI/Views/JobWidgets/JobPropertiesTableModel.h
new file mode 100644
index 0000000000000000000000000000000000000000..92481115eec7e68999fdc296f1ad9edf95c94f81
--- /dev/null
+++ b/GUI/Views/JobWidgets/JobPropertiesTableModel.h
@@ -0,0 +1,48 @@
+//  ************************************************************************************************
+//
+//  BornAgain: simulate and fit reflection and scattering
+//
+//! @file      GUI/Views/JobWidgets/JobPropertiesWidget.h
+//! @brief     Defines class JobPropertiesWidget
+//!
+//! @homepage  http://www.bornagainproject.org
+//! @license   GNU General Public License v3 or higher (see COPYING)
+//! @copyright Forschungszentrum Jülich GmbH 2018
+//! @authors   Scientific Computing Group at MLZ (see CITATION, AUTHORS)
+//
+//  ************************************************************************************************
+
+#ifndef BORNAGAIN_GUI_VIEWS_JOBWIDGETS_JOBPROPERTIESTABLEMODEL_H
+#define BORNAGAIN_GUI_VIEWS_JOBWIDGETS_JOBPROPERTIESTABLEMODE_H
+
+#include <QAbstractTableModel>
+
+class JobItem;
+
+
+//! The JobPropertiesTableModel is a table model for the properties of a job except for the comment.
+//! The name of the job is editable, all other fields are read only.
+
+class JobPropertiesTableModel : public QAbstractTableModel {
+    Q_OBJECT
+public:
+    explicit JobPropertiesTableModel(QObject* parent = nullptr);
+    ~JobPropertiesTableModel();
+    virtual int rowCount(const QModelIndex& parent = QModelIndex()) const override;
+    virtual int columnCount(const QModelIndex& parent = QModelIndex()) const override;
+    virtual QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override;
+    virtual QVariant headerData(int section, Qt::Orientation orientation,
+                                int role = Qt::DisplayRole) const override;
+    virtual Qt::ItemFlags flags(const QModelIndex& index) const override;
+    virtual bool setData(const QModelIndex& index, const QVariant& value,
+                         int role = Qt::EditRole) override;
+    void setItem(JobItem* item);
+
+private:
+    void notifyJobPropertyChange(const QString& property);
+
+private:
+    JobItem* m_item;
+};
+
+#endif // BORNAGAIN_GUI_VIEWS_JOBWIDGETS_JOBPROPERTIESTABLEMODEL_H
diff --git a/GUI/Views/JobWidgets/JobPropertiesWidget.cpp b/GUI/Views/JobWidgets/JobPropertiesWidget.cpp
index a8ae841465f42d28e3852c15fa45eb94b081b627..00552914d67e6c5a7391feac3d3d71c9ead4929d 100644
--- a/GUI/Views/JobWidgets/JobPropertiesWidget.cpp
+++ b/GUI/Views/JobWidgets/JobPropertiesWidget.cpp
@@ -15,97 +15,106 @@
 #include "GUI/Views/JobWidgets/JobPropertiesWidget.h"
 #include "GUI/Models/JobItem.h"
 #include "GUI/Views/CommonWidgets/StyleUtils.h"
-#include "GUI/Views/PropertyEditor/ComponentEditor.h"
+#include "GUI/Views/JobWidgets/JobPropertiesTableModel.h"
 #include "GUI/Views/Tools/mainwindow_constants.h"
 #include <QTabBar>
 #include <QTabWidget>
 #include <QTextEdit>
+#include <QTreeView>
 #include <QVBoxLayout>
 
-JobPropertiesWidget::JobPropertiesWidget(QWidget* parent)
-    : SessionItemWidget(parent)
-    , m_tabWidget(new QTabWidget)
-    , m_componentEditor(new ComponentEditor)
-    , m_commentsEditor(new QTextEdit)
-    , m_block_update(false)
+namespace {
+enum ETabId { JOB_PROPERTIES, JOB_COMMENTS };
+}
+
+//==================================================================================================
+// JobPropertiesWidget
+//==================================================================================================
+
+//--------------------------------------------------------------------------------------------------
+// public member functions
+//--------------------------------------------------------------------------------------------------
+
+JobPropertiesWidget::JobPropertiesWidget(QWidget* parent, Qt::WindowFlags f)
+    : QWidget(parent, f), m_item(nullptr)
 {
     setSizePolicy(QSizePolicy::Expanding, QSizePolicy::MinimumExpanding);
     setWindowTitle(GUI::Constants::JobPropertiesWidgetName);
 
-    auto mainLayout = new QVBoxLayout;
-    mainLayout->setMargin(0);
-    mainLayout->setSpacing(0);
-    mainLayout->setContentsMargins(0, 0, 0, 0);
+    QVBoxLayout* layout = new QVBoxLayout(this);
+    layout->setMargin(0);
+    layout->setSpacing(0);
+    layout->setContentsMargins(0, 0, 0, 0);
 
+    m_tabWidget = new QTabWidget(this);
     m_tabWidget->setTabPosition(QTabWidget::South);
-    m_tabWidget->insertTab(JOB_PROPERTIES, m_componentEditor, "Job Properties");
-    m_tabWidget->insertTab(JOB_COMMENTS, m_commentsEditor, "Details");
+    layout->addWidget(m_tabWidget);
 
-    mainLayout->addWidget(m_tabWidget);
-    setLayout(mainLayout);
+    m_propertiesView = new QTreeView(this);
+    m_propertiesView->setRootIsDecorated(false);
+    m_propertiesView->setAlternatingRowColors(true);
+    m_tabWidget->insertTab(JOB_PROPERTIES, m_propertiesView, "Job Properties");
+    m_propertiesModel = new JobPropertiesTableModel(this);
+    m_propertiesView->setModel(m_propertiesModel);
 
-    connect(m_commentsEditor, &QTextEdit::textChanged, this, &JobPropertiesWidget::onTextChanged);
+    m_commentsEditor = new QTextEdit(this);
+    m_tabWidget->insertTab(JOB_COMMENTS, m_commentsEditor, "Comments");
+
+    connect(m_commentsEditor, &QTextEdit::textChanged, this,
+            &JobPropertiesWidget::onCommentsEdited);
 }
 
-QSize JobPropertiesWidget::sizeHint() const
+JobPropertiesWidget::~JobPropertiesWidget()
 {
-    return QSize(GUI::Utils::Style::PropertyPanelWidth(), GUI::Utils::Style::PropertyPanelWidth());
+    if (m_item)
+        m_item->mapper()->unsubscribe(this);
 }
 
-QSize JobPropertiesWidget::minimumSizeHint() const
+QSize JobPropertiesWidget::sizeHint() const
 {
     return QSize(GUI::Utils::Style::PropertyPanelWidth(), GUI::Utils::Style::PropertyPanelWidth());
 }
 
-void JobPropertiesWidget::subscribeToItem()
+QSize JobPropertiesWidget::minimumSizeHint() const
 {
-    currentItem()->mapper()->setOnPropertyChange(
-        [this](const QString& name) {
-            if (JobItem::isCommentsPropertyName(name))
-                updateItem();
-        },
-        this);
-
-    m_componentEditor->setItem(currentItem());
-
-    updateItem();
+    return QSize(GUI::Utils::Style::PropertyPanelWidth(), GUI::Utils::Style::PropertyPanelWidth());
 }
 
-void JobPropertiesWidget::unsubscribeFromItem()
+void JobPropertiesWidget::setItem(JobItem* item)
 {
-    m_componentEditor->setItem(nullptr);
+    if (m_item)
+        m_item->mapper()->unsubscribe(this);
+    m_item = item;
+    m_propertiesModel->setItem(m_item);
+    if (m_item) {
+        if (m_item->isFailed())
+            m_tabWidget->tabBar()->setTabTextColor(JOB_COMMENTS, Qt::red);
+        else
+            m_tabWidget->tabBar()->setTabTextColor(JOB_COMMENTS, Qt::black);
+        m_commentsEditor->setText(m_item->getComments());
+        m_item->mapper()->setOnPropertyChange(
+            std::bind(&JobPropertiesWidget::notifyJobPropertyChange, this, std::placeholders::_1));
+    } else
+        m_commentsEditor->clear();
 }
 
-void JobPropertiesWidget::contextMenuEvent(QContextMenuEvent*)
-{
-    // Reimplemented to suppress menu from main window
-}
+//--------------------------------------------------------------------------------------------------
+// private member functions
+//--------------------------------------------------------------------------------------------------
 
-void JobPropertiesWidget::onTextChanged()
+void JobPropertiesWidget::notifyJobPropertyChange(const QString& property)
 {
-    m_block_update = true;
-    JobItem* item = jobItem();
-    if (item)
-        item->setComments(m_commentsEditor->toPlainText());
-    m_block_update = false;
+    if (JobItem::isCommentsPropertyName(property) && m_item
+        && m_item->getComments() != m_commentsEditor->toPlainText())
+        m_commentsEditor->setPlainText(m_item->getComments());
 }
 
-void JobPropertiesWidget::updateItem()
-{
-    if (m_block_update)
-        return;
-
-    if (JobItem* item = jobItem()) {
-        if (item->getStatus() == "Failed")
-            m_tabWidget->tabBar()->setTabTextColor(JOB_COMMENTS, Qt::red);
-        else
-            m_tabWidget->tabBar()->setTabTextColor(JOB_COMMENTS, Qt::black);
-
-        m_commentsEditor->setText(item->getComments());
-    }
-}
+//--------------------------------------------------------------------------------------------------
+// private slots
+//--------------------------------------------------------------------------------------------------
 
-JobItem* JobPropertiesWidget::jobItem()
+void JobPropertiesWidget::onCommentsEdited()
 {
-    return dynamic_cast<JobItem*>(currentItem());
+    if (m_item)
+        m_item->setComments(m_commentsEditor->toPlainText());
 }
diff --git a/GUI/Views/JobWidgets/JobPropertiesWidget.h b/GUI/Views/JobWidgets/JobPropertiesWidget.h
index 47ddd1a3e1e37bb042b187be8fe2704f7089ee4c..cad504f2edc6e3e65755cb1d06440024d62be227 100644
--- a/GUI/Views/JobWidgets/JobPropertiesWidget.h
+++ b/GUI/Views/JobWidgets/JobPropertiesWidget.h
@@ -15,41 +15,39 @@
 #ifndef BORNAGAIN_GUI_VIEWS_JOBWIDGETS_JOBPROPERTIESWIDGET_H
 #define BORNAGAIN_GUI_VIEWS_JOBWIDGETS_JOBPROPERTIESWIDGET_H
 
-#include "GUI/Views/CommonWidgets/SessionItemWidget.h"
+#include <QWidget>
 
 class JobItem;
-class QTextEdit;
+class JobPropertiesTableModel;
 class QTabWidget;
-class ComponentEditor;
+class QTextEdit;
+class QTreeView;
 
 //! The JobPropertiesWidget class holds component editor for JobItem. Part of JobSelectorWidget,
 //! resides at lower left corner of JobView.
 
-class JobPropertiesWidget : public SessionItemWidget {
+class JobPropertiesWidget : public QWidget {
     Q_OBJECT
 public:
-    enum ETabId { JOB_PROPERTIES, JOB_COMMENTS };
-    explicit JobPropertiesWidget(QWidget* parent = nullptr);
+    explicit JobPropertiesWidget(QWidget* parent = nullptr, Qt::WindowFlags f = Qt::WindowFlags());
+    ~JobPropertiesWidget();
+    void setItem(JobItem* item);
 
     QSize sizeHint() const;
     QSize minimumSizeHint() const;
 
-protected:
-    void subscribeToItem();
-    void unsubscribeFromItem();
-    void contextMenuEvent(QContextMenuEvent*);
+private:
+    void notifyJobPropertyChange(const QString& property);
 
 private slots:
-    void onTextChanged();
+    void onCommentsEdited();
 
 private:
-    void updateItem();
-    JobItem* jobItem();
-
     QTabWidget* m_tabWidget;
-    ComponentEditor* m_componentEditor;
+    QTreeView* m_propertiesView;
+    JobPropertiesTableModel* m_propertiesModel;
     QTextEdit* m_commentsEditor;
-    bool m_block_update;
+    JobItem* m_item;
 };
 
 #endif // BORNAGAIN_GUI_VIEWS_JOBWIDGETS_JOBPROPERTIESWIDGET_H
diff --git a/GUI/Views/JobWidgets/JobSelectorActions.cpp b/GUI/Views/JobWidgets/JobSelectorActions.cpp
deleted file mode 100644
index 0e225dbefc5064fe4970d748ac8a900e039bec5a..0000000000000000000000000000000000000000
--- a/GUI/Views/JobWidgets/JobSelectorActions.cpp
+++ /dev/null
@@ -1,182 +0,0 @@
-//  ************************************************************************************************
-//
-//  BornAgain: simulate and fit reflection and scattering
-//
-//! @file      GUI/Views/JobWidgets/JobSelectorActions.cpp
-//! @brief     Implements class JobSelectorActions
-//!
-//! @homepage  http://www.bornagainproject.org
-//! @license   GNU General Public License v3 or higher (see COPYING)
-//! @copyright Forschungszentrum Jülich GmbH 2018
-//! @authors   Scientific Computing Group at MLZ (see CITATION, AUTHORS)
-//
-//  ************************************************************************************************
-
-#include "GUI/Views/JobWidgets/JobSelectorActions.h"
-#include "GUI/Models/IntensityDataItem.h"
-#include "GUI/Models/JobItem.h"
-#include "GUI/Models/JobModel.h"
-#include <QAction>
-#include <QItemSelectionModel>
-#include <QMenu>
-#include <QPersistentModelIndex>
-#include <memory>
-
-JobSelectorActions::JobSelectorActions(JobModel* jobModel, QObject* parent)
-    : QObject(parent)
-    , m_runJobAction(nullptr)
-    , m_removeJobAction(nullptr)
-    , m_selectionModel(nullptr)
-    , m_jobModel(jobModel)
-{
-    m_runJobAction = new QAction("Run", this);
-    m_runJobAction->setIcon(QIcon(":/images/play.svg"));
-    m_runJobAction->setToolTip("Run currently selected job");
-    connect(m_runJobAction, &QAction::triggered, this, &JobSelectorActions::onRunJob);
-
-    m_removeJobAction = new QAction("Remove", this);
-    m_removeJobAction->setIcon(QIcon(":/images/delete.svg"));
-    m_removeJobAction->setToolTip("Remove currently selected job.");
-    connect(m_removeJobAction, &QAction::triggered, this, &JobSelectorActions::onRemoveJob);
-}
-
-void JobSelectorActions::setSelectionModel(QItemSelectionModel* selectionModel)
-{
-    m_selectionModel = selectionModel;
-}
-
-void JobSelectorActions::onRunJob()
-{
-    QModelIndexList indexList = m_selectionModel->selectedIndexes();
-    for (auto index : indexList) {
-        if (canRunJob(index))
-            m_jobModel->runJob(index);
-    }
-}
-
-void JobSelectorActions::onRemoveJob()
-{
-    QList<QPersistentModelIndex> toRemove;
-    for (auto index : m_selectionModel->selectedIndexes())
-        if (canRemoveJob(index))
-            toRemove.append(QPersistentModelIndex(index));
-
-    for (auto index : toRemove)
-        m_jobModel->removeJob(index);
-}
-
-//! Generates context menu at given point. If indexAtPoint is provided, the actions will be done
-//! for corresponding JobItem
-
-void JobSelectorActions::onContextMenuRequest(const QPoint& point, const QModelIndex& indexAtPoint)
-{
-    QMenu menu;
-    initItemContextMenu(menu, indexAtPoint);
-    menu.exec(point);
-    setAllActionsEnabled(true);
-}
-
-//! Puts all IntensityDataItem axes range to the selected job
-
-void JobSelectorActions::equalizeSelectedToJob(int selected_id)
-{
-    QModelIndexList selectedList = m_selectionModel->selectedIndexes();
-
-    if (selected_id >= selectedList.size())
-        return;
-
-    JobItem* referenceItem = m_jobModel->getJobItemForIndex(selectedList.at(selected_id));
-    ASSERT(referenceItem);
-
-    IntensityDataItem* referenceDataItem = referenceItem->intensityDataItem();
-    if (!referenceDataItem)
-        return;
-
-    for (auto index : selectedList) {
-        JobItem* jobItem = m_jobModel->getJobItemForIndex(index);
-        if (jobItem == referenceItem)
-            continue;
-        if (IntensityDataItem* dataItem = jobItem->intensityDataItem()) {
-            dataItem->setLowerX(referenceDataItem->getLowerX());
-            dataItem->setUpperX(referenceDataItem->getUpperX());
-            dataItem->setLowerY(referenceDataItem->getLowerY());
-            dataItem->setUpperY(referenceDataItem->getUpperY());
-            dataItem->setLowerZ(referenceDataItem->getLowerZ());
-            dataItem->setUpperZ(referenceDataItem->getUpperZ());
-        }
-    }
-}
-
-void JobSelectorActions::initItemContextMenu(QMenu& menu, const QModelIndex& indexAtPoint)
-{
-    menu.setToolTipsVisible(true);
-
-    menu.addAction(m_runJobAction);
-    menu.addAction(m_removeJobAction);
-
-    QModelIndex targetIndex = indexAtPoint;
-    if (!targetIndex.isValid()) {
-        QModelIndexList indexList = m_selectionModel->selectedIndexes();
-        if (!indexList.empty())
-            targetIndex = indexList.first();
-    }
-    m_runJobAction->setEnabled(canRunJob(targetIndex));
-    m_removeJobAction->setEnabled(canRemoveJob(targetIndex));
-
-    setupEqualizeMenu(menu);
-}
-
-void JobSelectorActions::setupEqualizeMenu(QMenu& menu)
-{
-    menu.addSeparator();
-
-    QMenu* equalize_menu = menu.addMenu("Equalize selected plots");
-    equalize_menu->setToolTipsVisible(true);
-    equalize_menu->setToolTip(
-        "All plots from the list of selected jobs will be equalized to the one.");
-    QModelIndexList selected = m_selectionModel->selectedIndexes();
-
-    if (selected.size() <= 1) {
-        equalize_menu->setDisabled(true);
-        return;
-    }
-    std::sort(selected.begin(), selected.end(),
-              [](const QModelIndex& a, const QModelIndex& b) { return a.row() < b.row(); });
-
-    for (int i = 0; i < selected.count(); ++i) {
-        JobItem* jobItem = m_jobModel->getJobItemForIndex(selected.at(i));
-        QAction* action = equalize_menu->addAction(QString("to ").append(jobItem->itemName()));
-        connect(action, &QAction::triggered, [=] { equalizeSelectedToJob(i); });
-    }
-}
-
-void JobSelectorActions::setAllActionsEnabled(bool value) const
-{
-    m_runJobAction->setEnabled(value);
-    m_removeJobAction->setEnabled(value);
-}
-
-bool JobSelectorActions::canRunJob(const QModelIndex& index) const
-{
-    if (!index.isValid())
-        return false;
-
-    const JobItem* jobItem = m_jobModel->getJobItemForIndex(index);
-
-    if (jobItem->isRunning() || jobItem->getStatus() == "Fitting")
-        return false;
-
-    return true;
-}
-
-bool JobSelectorActions::canRemoveJob(const QModelIndex& index) const
-{
-    if (!index.isValid())
-        return false;
-
-    const JobItem* jobItem = m_jobModel->getJobItemForIndex(index);
-    if (jobItem->isRunning() || jobItem->getStatus() == "Fitting")
-        return false;
-
-    return true;
-}
diff --git a/GUI/Views/JobWidgets/JobSelectorActions.h b/GUI/Views/JobWidgets/JobSelectorActions.h
deleted file mode 100644
index 4973d53ab54bfa069806ff4c970742501bc7e357..0000000000000000000000000000000000000000
--- a/GUI/Views/JobWidgets/JobSelectorActions.h
+++ /dev/null
@@ -1,59 +0,0 @@
-//  ************************************************************************************************
-//
-//  BornAgain: simulate and fit reflection and scattering
-//
-//! @file      GUI/Views/JobWidgets/JobSelectorActions.h
-//! @brief     Defines class JobSelectorActions
-//!
-//! @homepage  http://www.bornagainproject.org
-//! @license   GNU General Public License v3 or higher (see COPYING)
-//! @copyright Forschungszentrum Jülich GmbH 2018
-//! @authors   Scientific Computing Group at MLZ (see CITATION, AUTHORS)
-//
-//  ************************************************************************************************
-
-#ifndef BORNAGAIN_GUI_VIEWS_JOBWIDGETS_JOBSELECTORACTIONS_H
-#define BORNAGAIN_GUI_VIEWS_JOBWIDGETS_JOBSELECTORACTIONS_H
-
-#include <QModelIndex>
-#include <QObject>
-
-class QAction;
-class JobModel;
-class QItemSelectionModel;
-class QModelIndex;
-class QMenu;
-
-//! The JobSelectorActions class contains actions to run/remove jobs. Actions are used by the
-//! toolbar and JobSelectorList's context menu.
-
-class JobSelectorActions : public QObject {
-    Q_OBJECT
-public:
-    JobSelectorActions(JobModel* jobModel, QObject* parent = 0);
-
-    void setSelectionModel(QItemSelectionModel* selectionModel);
-
-public slots:
-    void onRunJob();
-    void onRemoveJob();
-    void onContextMenuRequest(const QPoint& point, const QModelIndex& indexAtPoint = {});
-    void equalizeSelectedToJob(int selected_id);
-
-private:
-    void initItemContextMenu(QMenu& menu, const QModelIndex& indexAtPoint);
-    void setupEqualizeMenu(QMenu& menu);
-    void setAllActionsEnabled(bool value) const;
-    bool canRunJob(const QModelIndex& index) const;
-    bool canRemoveJob(const QModelIndex& index) const;
-
-public:
-    QAction* m_runJobAction;
-    QAction* m_removeJobAction;
-
-private:
-    QItemSelectionModel* m_selectionModel;
-    JobModel* m_jobModel;
-};
-
-#endif // BORNAGAIN_GUI_VIEWS_JOBWIDGETS_JOBSELECTORACTIONS_H
diff --git a/GUI/Views/JobWidgets/JobSelectorWidget.cpp b/GUI/Views/JobWidgets/JobSelectorWidget.cpp
index 88d49fdd9ac0d8e0e863d25203c8529bd2eeee8f..7cb961159964a7f4bfefcd31bf8f1b4d13e85de0 100644
--- a/GUI/Views/JobWidgets/JobSelectorWidget.cpp
+++ b/GUI/Views/JobWidgets/JobSelectorWidget.cpp
@@ -13,88 +13,62 @@
 //  ************************************************************************************************
 
 #include "GUI/Views/JobWidgets/JobSelectorWidget.h"
+#include "Base/Utils/Assert.h"
 #include "GUI/Models/JobItem.h"
-#include "GUI/Views/CommonWidgets/StyleUtils.h"
-#include "GUI/Views/CommonWidgets/StyledToolBar.h"
-#include "GUI/Views/JobWidgets/JobListWidget.h"
+#include "GUI/Views/JobWidgets/JobListView.h"
 #include "GUI/Views/JobWidgets/JobPropertiesWidget.h"
-#include "GUI/Views/JobWidgets/JobSelectorActions.h"
 #include "GUI/Views/Tools/mainwindow_constants.h"
-#include <QHBoxLayout>
 #include <QSplitter>
+#include <QVBoxLayout>
 
 JobSelectorWidget::JobSelectorWidget(JobModel* jobModel, QWidget* parent)
     : QWidget(parent)
-    , m_jobSelectorActions(new JobSelectorActions(jobModel, this))
-    , m_jobListWidget(new JobListWidget)
-    , m_jobProperties(new JobPropertiesWidget)
-    , m_jobModel(nullptr)
+    , m_jobModel(jobModel)
 {
     setWindowTitle(GUI::Constants::JobSelectorWidgetName);
     setObjectName("JobSelectorWidget");
 
     setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
 
-    setModel(jobModel);
+    QVBoxLayout* layout = new QVBoxLayout(this);
+    layout->setMargin(0);
+    layout->setSpacing(0);
+    layout->setContentsMargins(0, 0, 0, 0);
 
-    auto splitter = new QSplitter;
-    splitter->setOrientation(Qt::Vertical);
-    splitter->addWidget(m_jobListWidget);
-    splitter->addWidget(m_jobProperties);
+    QSplitter* splitter = new QSplitter(Qt::Vertical, this);
     splitter->setChildrenCollapsible(true);
+    layout->addWidget(splitter);
 
-    QToolBar* toolBar = new StyledToolBar(this);
-    toolBar->setMinimumSize(toolBar->minimumHeight(), toolBar->minimumHeight());
-    toolBar->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
-    toolBar->addAction(m_jobSelectorActions->m_runJobAction);
-    toolBar->addAction(m_jobSelectorActions->m_removeJobAction);
-
-    auto mainLayout = new QVBoxLayout;
-    mainLayout->setMargin(0);
-    mainLayout->setSpacing(0);
-    mainLayout->setContentsMargins(0, 0, 0, 0);
-    mainLayout->addWidget(toolBar);
-    mainLayout->addWidget(splitter);
-    setLayout(mainLayout);
-
-    m_jobSelectorActions->setSelectionModel(m_jobListWidget->selectionModel());
+    m_jobListView = new JobListView(m_jobModel, splitter);
+    splitter->addWidget(m_jobListView);
 
-    connect(m_jobListWidget, &JobListWidget::contextMenuRequest, m_jobSelectorActions,
-            &JobSelectorActions::onContextMenuRequest);
-    connect(m_jobListWidget, &JobListWidget::selectionChanged, this,
-            &JobSelectorWidget::onSelectionChanged);
-}
-
-void JobSelectorWidget::setModel(JobModel* jobModel)
-{
-    m_jobModel = jobModel;
-    m_jobListWidget->setModel(m_jobModel);
-}
+    m_jobProperties = new JobPropertiesWidget(this);
+    splitter->addWidget(m_jobProperties);
 
-QSize JobSelectorWidget::sizeHint() const
-{
-    return QSize(GUI::Utils::Style::PropertyPanelWidth(),
-                 GUI::Utils::Style::PropertyPanelWidth() * 2);
-}
+    connect(m_jobListView, &JobListView::selectedJobsChanged, this,
+            &JobSelectorWidget::onSelectedJobsChanged);
+    connect(m_jobListView, &JobListView::selectedJobsChanged, this,
+            &JobSelectorWidget::selectedJobsChanged);
 
-QSize JobSelectorWidget::minimumSizeHint() const
-{
-    return QSize(GUI::Utils::Style::PropertyPanelWidth(), GUI::Utils::Style::PropertyPanelWidth());
+    onSelectedJobsChanged(selectedJobs());
 }
 
-const JobItem* JobSelectorWidget::currentJobItem() const
+QVector<JobItem*> JobSelectorWidget::selectedJobs() const
 {
-    return m_jobListWidget->currentJobItem();
+    return m_jobListView->selectedJobs();
 }
 
 void JobSelectorWidget::makeJobItemSelected(JobItem* item)
 {
     ASSERT(item);
-    m_jobListWidget->makeJobItemSelected(item);
+    m_jobListView->selectJob(item);
 }
 
-void JobSelectorWidget::onSelectionChanged(JobItem* jobItem)
+
+void JobSelectorWidget::onSelectedJobsChanged(const QVector<JobItem*>& jobs)
 {
-    m_jobProperties->setItem(jobItem);
-    emit selectionChanged(jobItem);
+    if (jobs.size() == 1)
+        m_jobProperties->setItem(jobs.front());
+    else
+        m_jobProperties->setItem(nullptr);
 }
diff --git a/GUI/Views/JobWidgets/JobSelectorWidget.h b/GUI/Views/JobWidgets/JobSelectorWidget.h
index 39795eba3c2d0dc59b5cb449681c3aa560661a54..5d94525d2cb79780e42ac524d1dab6877bc01109 100644
--- a/GUI/Views/JobWidgets/JobSelectorWidget.h
+++ b/GUI/Views/JobWidgets/JobSelectorWidget.h
@@ -19,8 +19,7 @@
 
 class JobModel;
 class JobItem;
-class JobSelectorActions;
-class JobListWidget;
+class JobListView;
 class JobPropertiesWidget;
 
 //! The JobSelectorWidget class represents left panel of JobView. Contains a tree to select jobs
@@ -31,26 +30,19 @@ class JobSelectorWidget : public QWidget {
 
 public:
     explicit JobSelectorWidget(JobModel* jobModel, QWidget* parent = nullptr);
-
-    void setModel(JobModel* jobModel);
-
-    QSize sizeHint() const;
-    QSize minimumSizeHint() const;
-
-    const JobItem* currentJobItem() const;
+    QVector<JobItem*> selectedJobs() const;
 
 signals:
-    void selectionChanged(JobItem*);
+    void selectedJobsChanged(const QVector<JobItem*>& jobs);
 
 public slots:
     void makeJobItemSelected(JobItem*);
 
 private slots:
-    void onSelectionChanged(JobItem* jobItem);
+    void onSelectedJobsChanged(const QVector<JobItem*>& jobs);
 
 private:
-    JobSelectorActions* m_jobSelectorActions;
-    JobListWidget* m_jobListWidget;
+    JobListView* m_jobListView;
     JobPropertiesWidget* m_jobProperties;
     JobModel* m_jobModel;
 };
diff --git a/GUI/mainwindow/OutputDataIOService.cpp b/GUI/mainwindow/OutputDataIOService.cpp
index e5817567301b15ba4e746a29dfa8d4873daeda40..13d21bbe8b0b0b0f34507c0ae45c9097e201e07c 100644
--- a/GUI/mainwindow/OutputDataIOService.cpp
+++ b/GUI/mainwindow/OutputDataIOService.cpp
@@ -78,7 +78,7 @@ void OutputDataIOService::loadDataFiles(const QString& projectDir, MessageServic
             if (auto jobItem = parentJobItem(item)) {
                 if (jobItem->isRunning()) {
                     jobItem->setComments("Possible GUI crash while job was running");
-                    jobItem->setStatus("Failed");
+                    jobItem->setStatus(JobStatus::Failed);
                 }
             }
 
@@ -87,7 +87,7 @@ void OutputDataIOService::loadDataFiles(const QString& projectDir, MessageServic
                 // Handling corrupted file on disk
                 jobItem->setComments(
                     QString("Load of the data from disk failed with '%1'").arg(QString(ex.what())));
-                jobItem->setStatus("Failed");
+                jobItem->setStatus(JobStatus::Failed);
             }
             if (!messageService)
                 throw ex;