From d073cf24cfea38011aa330dd1ded85f735e245a6 Mon Sep 17 00:00:00 2001
From: tobiglaser <76131623+tobiglaser@users.noreply.github.com>
Date: Mon, 8 Aug 2022 01:29:56 +0200
Subject: [PATCH] Worksheet 4 finished, no documentation

---
 CMakeLists.txt               |   2 +
 include/Rover.h              |   2 +-
 src/AboutDialog.h            |  41 +++++++++
 src/CommandListOWN.cpp       |   1 -
 src/ControlModelRegistry.cpp |  26 ++----
 src/ControlModelRegistry.h   |   8 +-
 src/ControlUI.cpp            | 166 ++++++++++++++++++++++++++++++-----
 src/ControlUI.h              |  51 ++++++++++-
 src/PanelCommandTable.cpp    | 134 ++++++++++++++++++++++++++++
 src/PanelCommandTable.h      |  49 +++++++++++
 src/PanelCommandTypes.cpp    |   5 +-
 src/PanelCommandTypes.h      |   7 +-
 src/PanelConfigDirection.cpp |   4 +-
 src/PanelConfigGear.cpp      |  15 ++--
 src/PanelConfigPause.cpp     |  13 +--
 src/RoverDialog.h            |  33 +++++++
 src/TableCommandModel.cpp    | 107 ++++++++++++++++++++++
 src/TableCommandModel.h      |  24 +++++
 18 files changed, 624 insertions(+), 64 deletions(-)
 create mode 100644 src/AboutDialog.h
 create mode 100644 src/PanelCommandTable.cpp
 create mode 100644 src/PanelCommandTable.h
 create mode 100644 src/RoverDialog.h
 create mode 100644 src/TableCommandModel.cpp
 create mode 100644 src/TableCommandModel.h

diff --git a/CMakeLists.txt b/CMakeLists.txt
index e606ce6..f1c159e 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -23,6 +23,8 @@ set(PROJECT_SOURCES
         src/ControlModel.cpp
         src/ControlModelRegistry.cpp
         src/PanelCommandTypes.cpp
+        src/TableCommandModel.cpp
+        src/PanelCommandTable.cpp
         src/PanelConfigDirection.cpp
         src/PanelConfigPause.cpp
         src/PanelConfigGear.cpp
diff --git a/include/Rover.h b/include/Rover.h
index cde39cc..5ab0282 100644
--- a/include/Rover.h
+++ b/include/Rover.h
@@ -4,7 +4,7 @@
 class Rover
 {
 private:
-    std::string id;
+    std::string id = "Dummy";
 
 public:
     std::string getId() { return id; }
diff --git a/src/AboutDialog.h b/src/AboutDialog.h
new file mode 100644
index 0000000..684cc08
--- /dev/null
+++ b/src/AboutDialog.h
@@ -0,0 +1,41 @@
+#pragma once
+#include <array>
+#include <QLayout>
+#include <QTextEdit>
+#include <QPushButton>
+
+class AboutDialog : public QDialog
+{
+
+private:
+    QTextEdit *tA;
+    QPushButton *bOk;
+    std::array<QString, 7> text{
+        "Control Developer",
+        "",
+        "Tobias Glaser",
+        "Software intensive Systems SS22",
+        "Reutlingen University",
+        "",
+        "https://github.com/tobiglaser/java2cpp"};
+
+public:
+    AboutDialog()
+    {
+        setWindowTitle("About");
+        QVBoxLayout *l = new QVBoxLayout();
+        this->setLayout(l);
+        tA = new QTextEdit();
+        tA->setReadOnly(true);
+        tA->setAlignment(Qt::AlignCenter);
+        for (auto &&i : text)
+        {
+            tA->append(i);
+        }
+        l->addWidget(tA);
+        bOk = new QPushButton("Ok");
+        l->addWidget(bOk);
+        updateGeometry();
+        connect(bOk, SIGNAL(clicked()), this, SLOT(accept()));
+    };
+};
\ No newline at end of file
diff --git a/src/CommandListOWN.cpp b/src/CommandListOWN.cpp
index c3edacb..c26dd02 100644
--- a/src/CommandListOWN.cpp
+++ b/src/CommandListOWN.cpp
@@ -97,7 +97,6 @@ std::shared_ptr<ICommand> CommandList::remove(unsigned int _pos)
     {
         return nullptr;
     }
-    std::cout << "removing: " << toRemove->getCommand()->getName() << '\n';
     std::shared_ptr<Element> prev = toRemove->getPrev();
     std::shared_ptr<Element> next = toRemove->getNext();
     prev->setNext(next);
diff --git a/src/ControlModelRegistry.cpp b/src/ControlModelRegistry.cpp
index 8971df3..bbc56c1 100644
--- a/src/ControlModelRegistry.cpp
+++ b/src/ControlModelRegistry.cpp
@@ -3,9 +3,9 @@
 /**
  * @brief Add a Listener / Subscriber / Observer to the registry.
  * 
- * @param _listener A shared_ptr to the listener to be added.
+ * @param _listener A pointer to listener to be added.
  */
-void ControlModelRegistry::addControlModelListener(std::shared_ptr<IControlModelListener> _listener)
+void ControlModelRegistry::addControlModelListener(IControlModelListener *_listener)
 {
     registry.push_back(_listener);
 }
@@ -13,18 +13,12 @@ void ControlModelRegistry::addControlModelListener(std::shared_ptr<IControlModel
 /**
  * @brief Remove a Listener / Subscriber / Observer from the registry.
  *  Removal happens if the same address is referenced.
- * @param _listener A shared_ptr to the listener to be removed.
+ * @param _listener A pointer to listener to be removed.
  */
-void ControlModelRegistry::removeControlModelListener(std::shared_ptr<IControlModelListener> _listener)
+void ControlModelRegistry::removeControlModelListener(IControlModelListener *_listener)
 {
-    for (auto iter = registry.begin(); iter != registry.end(); ++iter)
-    {
-        if (iter->lock().get() == _listener.get())
-        {
-            registry.erase(iter);
-        }
-    }
-    
+    //see https://en.cppreference.com/w/cpp/container/vector/erase2
+    std::erase(registry, _listener);
 }
 
 /**
@@ -36,9 +30,7 @@ void ControlModelRegistry::notifyMessageChanged(std::string _message)
 {
     for (auto &&i : registry)
     {
-        std::shared_ptr<IControlModelListener> l = i.lock();
-        if (l)
-            l->messageUpdated(_message);
+        i->messageUpdated(_message);
     }
 }
 
@@ -50,8 +42,6 @@ void ControlModelRegistry::notifyRoverChanged()
 {
     for (auto &&i : registry)
     {
-        std::shared_ptr<IControlModelListener> l = i.lock();
-        if (l)
-            l->roverUpdated();
+        i->roverUpdated();
     }
 }
\ No newline at end of file
diff --git a/src/ControlModelRegistry.h b/src/ControlModelRegistry.h
index e34532f..29b104a 100644
--- a/src/ControlModelRegistry.h
+++ b/src/ControlModelRegistry.h
@@ -6,16 +6,16 @@
 
 /**
  * @brief ControlModelRegistry allows another class to use the Observer pattern.
- * 
+ * It registers Objects by raw pointer, not taking any ownership nor checking their validity.
  */
 class ControlModelRegistry
 {
 private:
-    std::vector<std::weak_ptr<IControlModelListener>> registry;
+    std::vector<IControlModelListener *> registry;
 
 public:
-    void addControlModelListener(std::shared_ptr<IControlModelListener> _listener);
-    void removeControlModelListener(std::shared_ptr<IControlModelListener> _listener);
+    void addControlModelListener(IControlModelListener *_listener);
+    void removeControlModelListener(IControlModelListener *_listener);
     void notifyMessageChanged(std::string _message);
     void notifyRoverChanged();
 };
diff --git a/src/ControlUI.cpp b/src/ControlUI.cpp
index 48be779..1982723 100644
--- a/src/ControlUI.cpp
+++ b/src/ControlUI.cpp
@@ -1,41 +1,167 @@
+#include <QApplication>
 #include <QSplitter>
-
+#include <QFileDialog>
+#include <QDir>
+#include <memory>
+#include "commandlib.h"
 #include "ControlUI.h"
-#include "PanelCommandTypes.h"
-
 #include "PanelConfigDirection.h"
 #include "PanelConfigPause.h"
 #include "PanelConfigGear.h"
 #include "PanelConfigDefault.h"
+#include "RoverDialog.h"
+#include "AboutDialog.h"
 
 #include <iostream>
 
 ControlUI::ControlUI(QWidget *parent)
     : QMainWindow(parent)
 {
+    panelConfigs =
+        {
+            new PanelConfigDefault(this),
+            new PanelConfigDirection(this),
+            new PanelConfigGear(this),
+            new PanelConfigPause(this)
+        };
     ControlModel::getInstance().readCommands("../testCommands.txt");
+    setView();
+    addMenuBar();
+    setController();
+}
 
+void ControlUI::setView()
+{
     setWindowTitle("ControlDeveloper");
+    QSplitter *splitterV = new QSplitter(Qt::Vertical);
+    setCentralWidget(splitterV);
     QSplitter *splitterH = new QSplitter(Qt::Horizontal);
-    setCentralWidget(splitterH);
-    PanelCommandTypes *pct = new PanelCommandTypes(&ControlModel::getInstance(), this);
-    splitterH->addWidget(pct);
+    splitterV->addWidget(splitterH);
+
+    messageArea = new QTextEdit();
+    messageArea->setReadOnly(true);
+    messageArea->setFontPointSize(11);
+    splitterV->addWidget(messageArea);
+
+    panelTypes = new PanelCommandTypes(&ControlModel::getInstance());
+    splitterH->addWidget(panelTypes);
+    panelTable = new PanelCommandTable(&ControlModel::getInstance());
+    splitterH->addWidget(panelTable);
+    configStack = new QStackedWidget();
+    splitterH->addWidget(configStack);
+    for (auto &&i : panelConfigs)
+    {
+        configStack->addWidget(i);
+    }
+}
+
+void ControlUI::addMenuBar()
+{
+    menuBar = new QMenuBar();
+    setMenuBar(menuBar);
+    mFile = new QMenu("File");
+    menuBar->addMenu(mFile);
+    aOpen = new QAction("Open...", this);
+    mFile->addAction(aOpen);
+    aSave = new QAction("Save...", this);
+    mFile->addAction(aSave);
+    aRover = new QAction("Select Rover...", this);
+    mFile->addAction(aRover);
+    aExit = new QAction("Exit", this);
+    mFile->addAction(aExit);
+    mHelp = new QMenu("Help");
+    menuBar->addMenu(mHelp);
+    aAbout = new QAction("About...", this);
+    mHelp->addAction(aAbout);
+}
+
+void ControlUI::setController()
+{
+    ControlModel::getInstance().addControlModelListener(this);
+    connect(aOpen, SIGNAL(triggered()), this, SLOT(onOpen()));
+    connect(aSave, SIGNAL(triggered()), this, SLOT(onSave()));
+    connect(aRover, SIGNAL(triggered()), this, SLOT(onRover()));
+    connect(aExit, SIGNAL(triggered()), this, SLOT(onExit()));
+    connect(aAbout, SIGNAL(triggered()), this, SLOT(onAbout()));
+    connect(panelTable, SIGNAL(pleaseUpdateTable(std::shared_ptr<ICommand>)), this, SLOT(updateTableView(std::shared_ptr<ICommand>)));
+    connect(panelTypes, SIGNAL(pleaseUpdateTable(std::shared_ptr<ICommand>)), this, SLOT(updateTableView(std::shared_ptr<ICommand>)));
+    connect(panelTable, SIGNAL(pleaseUpdateConfig(std::shared_ptr<ICommand>)), this, SLOT(updateConfigView(std::shared_ptr<ICommand>)));
+}
+
+void ControlUI::updateTableView(std::shared_ptr<ICommand> _icom)
+{
+    panelTable->updateTable(_icom);
+}
+
+void ControlUI::updateConfigView(std::shared_ptr<ICommand> _icom)
+{
+    if (dynamic_pointer_cast<IGear>(_icom))
+        configStack->setCurrentIndex(confNum::cGear);
+    else if (dynamic_pointer_cast<IPause>(_icom))
+        configStack->setCurrentIndex(confNum::cPause);
+    else if (dynamic_pointer_cast<IDirection>(_icom))
+        configStack->setCurrentIndex(confNum::cDirection);
+    else
+        configStack->setCurrentIndex(confNum::cDefault);
+
+    static_cast<PanelCommandConfig *>(configStack->currentWidget())->update(_icom);
+}
+
+void ControlUI::messageUpdated(std::string _message)
+{
+    messageArea->append(_message.c_str());
+}
+
+void ControlUI::roverUpdated()
+{
+    panelTable->updateSelectedRover();
+    messageUpdated("A Rover has been selected.");
+}
 
-    PanelConfigDirection *pcd = new PanelConfigDirection(this);
-    splitterH->addWidget(pcd);
-    PanelConfigPause *pcp = new PanelConfigPause(this);
-    splitterH->addWidget(pcp);
-    PanelConfigDefault *pcdef = new PanelConfigDefault(this);
-    splitterH->addWidget(pcdef);
-    PanelConfigGear *pcg = new PanelConfigGear(this);
-    splitterH->addWidget(pcg);
-    pcg->update(ControlModel::getInstance().getCommandList().getCommand(1));
-    pcd->update(ControlModel::getInstance().getCommandList().getCommand(2));
-    pcp->update(ControlModel::getInstance().getCommandList().getCommand(3));
+void ControlUI::onOpen()
+{
+    QString filter = "Text Files (*.txt)";
+    QString qpath = QFileDialog::getOpenFileName(this, "Read a File", QDir::homePath(), filter);
+    if (!qpath.isEmpty())
+    {
+        ControlModel::getInstance().readCommands(qpath.toStdString());
+        updateTableView(nullptr);
+    }
 }
 
-void ControlUI::updateTableView()
+void ControlUI::onSave()
 {
-    std::cout << "\n\nTable:\n";
-    ControlModel::getInstance().getCommandList().printCommands();
+    QString filter = "Text Files (*.txt)";
+    QString qpath = QFileDialog::getSaveFileName(this, "Save to File", QDir::homePath(), filter);
+    std::cout << qpath.toStdString() << std::endl;
+    if (!qpath.isEmpty())
+    {
+        ControlModel::getInstance().writeCommands(qpath.toStdString());
+    }
 }
+
+void ControlUI::onRover()
+{
+    RoverDialog r(&ControlModel::getInstance());
+    r.exec();
+}
+
+void ControlUI::onExit()
+{
+    QApplication::exit();
+}
+
+void ControlUI::onAbout()
+{
+    AboutDialog a;
+    a.exec();
+}
+
+ControlUI::~ControlUI()
+{
+    delete aOpen;
+    delete aSave;
+    delete aRover;
+    delete aExit;
+    delete aAbout;
+}
\ No newline at end of file
diff --git a/src/ControlUI.h b/src/ControlUI.h
index c46ddba..b0a4063 100644
--- a/src/ControlUI.h
+++ b/src/ControlUI.h
@@ -2,16 +2,63 @@
 
 #include <QMainWindow>
 #include <QTableView>
+#include <QStackedWidget>
+#include <QTextEdit>
+#include <QMenuBar>
+#include <QMenu>
+#include <QAction>
 
-class ControlUI : public QMainWindow
+#include <array>
+#include "commandlib.h"
+#include "IControlModelListener.h"
+#include "PanelCommandConfig.h"
+#include "PanelCommandTable.h"
+#include "PanelCommandTypes.h"
+
+class ControlUI : public QMainWindow, IControlModelListener
 {
     Q_OBJECT
 
 private:
+    QTextEdit *messageArea;
+    QStackedWidget *configStack;
+    PanelCommandTypes *panelTypes;
+    PanelCommandTable *panelTable;
+    std::array<PanelCommandConfig *, 4> panelConfigs;
+    enum confNum
+    {
+        cDefault = 0,
+        cDirection = 1,
+        cGear = 2,
+        cPause = 3
+    };
+    QMenuBar *menuBar;
+    QMenu *mFile;
+    QAction *aOpen;
+    QAction *aSave;
+    QAction *aRover;
+    QAction *aExit;
+    QMenu *mHelp;
+    QAction *aAbout;
+    void setView();
+    void setController();
+    void addMenuBar();
 
 public:
     ControlUI(QWidget *parent = nullptr);
+    ~ControlUI();
+
+
+    void messageUpdated(std::string _message);
+    void roverUpdated();
 
 public slots:
-    void updateTableView();
+    void updateTableView(std::shared_ptr<ICommand> _icom);
+    void updateConfigView(std::shared_ptr<ICommand> _icom);
+private slots:
+    void onOpen();
+    void onSave();
+    void onRover();
+    void onExit();
+    void onAbout();
 };
diff --git a/src/PanelCommandTable.cpp b/src/PanelCommandTable.cpp
new file mode 100644
index 0000000..441cd44
--- /dev/null
+++ b/src/PanelCommandTable.cpp
@@ -0,0 +1,134 @@
+#include "PanelCommandTable.h"
+#include <QBoxLayout>
+#include <QHeaderView>
+#include <iostream>
+
+PanelCommandTable::PanelCommandTable(ControlModel *_cM)
+{
+    cM = _cM;
+    setView();
+    setController();
+}
+
+void PanelCommandTable::setView()
+{
+    QBoxLayout *vLayout = new QBoxLayout(QBoxLayout::Direction::TopToBottom);
+    setLayout(vLayout);
+
+    QWidget *buttons = new QWidget();
+    QBoxLayout *hLayout = new QBoxLayout(QBoxLayout::Direction::LeftToRight);
+    buttons->setLayout(hLayout);
+
+    tCommands = new QTableView();
+    tM = new TableCommandModel(&cM->getCommandList());
+    tCommands->setModel(tM);
+    tCommands->setSelectionBehavior(QAbstractItemView::SelectionBehavior::SelectRows);
+    tCommands->setSelectionMode(QAbstractItemView::SelectionMode::SingleSelection);
+    tCommands->setWordWrap(false);
+    tCommands->horizontalHeader()->setStretchLastSection(true);
+    tCommands->verticalHeader()->hide();
+    vLayout->addWidget(tCommands);
+    vLayout->addWidget(buttons);
+
+    bRemove = new QPushButton("Remove");
+    hLayout->addWidget(bRemove);
+    bUp = new QPushButton("Up");
+    hLayout->addWidget(bUp);
+    bDown = new QPushButton("Down");
+    hLayout->addWidget(bDown);
+
+    hLayout->addStretch();
+    lRover = new QLabel();
+    hLayout->addWidget(lRover);
+    bStart = new QPushButton("Start");
+    hLayout->addWidget(bStart);
+    bStop = new QPushButton("Stop");
+    hLayout->addWidget(bStop);
+
+    lSM = tCommands->selectionModel();
+    updateSelectedRover();
+}
+
+void PanelCommandTable::setController()
+{
+    connect(bRemove, SIGNAL(clicked()), this, SLOT(onRemoveButton()));
+    connect(bUp,     SIGNAL(clicked()), this, SLOT(onUpButton()));
+    connect(bDown,   SIGNAL(clicked()), this, SLOT(onDownButton()));
+    connect(bStart,  SIGNAL(clicked()), this, SLOT(onStartButton()));
+    connect(bStop,   SIGNAL(clicked()), this, SLOT(onStopButton()));
+    connect(lSM,     SIGNAL(currentRowChanged(const QModelIndex, const QModelIndex)), this, SLOT(onSelectionChange(const QModelIndex, const QModelIndex)));
+}
+
+void PanelCommandTable::onRemoveButton()
+{
+    int index = lSM->currentIndex().row();
+    if (index >= 0) //valid selection
+    {
+        lSM->reset();
+        cM->getCommandList().remove(index + 1);
+        std::shared_ptr<ICommand> newSelect;
+        if (index == 0) // first item removed
+            newSelect = cM->getCommandList().getCommand(1);
+        else if (index == cM->getCommandList().getSize() - 1) //last item removed
+            newSelect = cM->getCommandList().getCommand(cM->getCommandList().getSize());
+        else // middle item removed
+            newSelect = cM->getCommandList().getCommand(index);
+        emit(pleaseUpdateTable(newSelect));
+    }
+}
+
+void PanelCommandTable::onUpButton()
+{
+    int index = lSM->currentIndex().row();
+    if (index >= 0) //valid selection
+    {
+        std::shared_ptr<ICommand> icom = cM->getCommandList().moveUp(index + 1);
+        emit(pleaseUpdateTable(icom));
+    }
+}
+
+void PanelCommandTable::onDownButton()
+{
+    int index = lSM->currentIndex().row();
+    if (index >= 0) //valid selection
+    {
+        std::shared_ptr<ICommand> icom = cM->getCommandList().moveDown(index + 1);
+        emit(pleaseUpdateTable(icom));
+    }
+}
+
+void PanelCommandTable::onStartButton()
+{
+    cM->start();
+}
+
+void PanelCommandTable::onStopButton()
+{
+    cM->stop();
+}
+
+void PanelCommandTable::updateTable(std::shared_ptr<ICommand> _icom)
+{
+    QModelIndex index = tM->onChange(_icom);
+    lSM->setCurrentIndex(index, QItemSelectionModel::SelectionFlag::ClearAndSelect | QItemSelectionModel::SelectionFlag::Rows);
+}
+
+void PanelCommandTable::updateSelectedRover()
+{
+    std::optional<std::string> roverId = cM->getSelectedRoverId();
+    if (roverId.has_value())
+    {
+        QString s = "Rover: ";
+        QString id = roverId.value().c_str();
+        lRover->setText(s + id);
+    }
+    else
+        lRover->setText("Rover: - ");
+}
+
+void PanelCommandTable::onSelectionChange(const QModelIndex &current, const QModelIndex &previous)
+{
+    int index = current.row() + 1;
+    std::shared_ptr<ICommand> icom = cM->getCommandList().getCommand(index);
+    emit(pleaseUpdateConfig(icom));
+}
\ No newline at end of file
diff --git a/src/PanelCommandTable.h b/src/PanelCommandTable.h
new file mode 100644
index 0000000..6d0817c
--- /dev/null
+++ b/src/PanelCommandTable.h
@@ -0,0 +1,49 @@
+#pragma once
+#include <QListWidget>
+#include <QLabel>
+#include <QPushButton>
+#include <QTableView>
+#include <memory>
+#include <commandlib.h>
+#include "ControlModel.h"
+#include "TableCommandModel.h"
+
+class PanelCommandTable : public QWidget
+{
+
+Q_OBJECT
+
+private:
+    TableCommandModel *tM;
+    QPushButton *bRemove;
+    QPushButton *bUp;
+    QPushButton *bDown;
+    QLabel *lRover;
+    QPushButton *bStart;
+    QPushButton *bStop;
+    QTableView *tCommands;
+    QItemSelectionModel *lSM;
+
+    ControlModel *cM;
+
+    void setView();
+    void setController();
+
+
+public:
+    PanelCommandTable(ControlModel *_cM);
+    void updateTable(std::shared_ptr<ICommand> _icom);
+    void updateSelectedRover();
+
+signals:
+    void pleaseUpdateTable(std::shared_ptr<ICommand>);
+    void pleaseUpdateConfig(std::shared_ptr<ICommand>);
+
+public slots:
+    void onRemoveButton();
+    void onUpButton();
+    void onDownButton();
+    void onStartButton();
+    void onStopButton();
+    void onSelectionChange(const QModelIndex &current, const QModelIndex &previous);
+};
diff --git a/src/PanelCommandTypes.cpp b/src/PanelCommandTypes.cpp
index 4cc86ae..bd73293 100644
--- a/src/PanelCommandTypes.cpp
+++ b/src/PanelCommandTypes.cpp
@@ -4,9 +4,8 @@
 #include "ComTypeListWidgetItem.h"
 
 
-PanelCommandTypes::PanelCommandTypes(ControlModel *_cM, ControlUI *_cui)
+PanelCommandTypes::PanelCommandTypes(ControlModel *_cM)
 {
-    cui = _cui;
     cM = _cM;
     setView();
     setController();
@@ -39,6 +38,6 @@ void PanelCommandTypes::onAddButton()
     {
         std::shared_ptr<ICommand> newCommand = listItem->getCommandType()->createInstance();
         cM->getCommandList().add(newCommand);
-        cui->updateTableView();
+        emit(pleaseUpdateTable(newCommand));
     }
 }
\ No newline at end of file
diff --git a/src/PanelCommandTypes.h b/src/PanelCommandTypes.h
index 4c5f00f..4d05dcb 100644
--- a/src/PanelCommandTypes.h
+++ b/src/PanelCommandTypes.h
@@ -2,7 +2,6 @@
 #include <QListWidget>
 #include <QPushButton>
 #include "ControlModel.h"
-#include "ControlUI.h"
 
 class PanelCommandTypes : public QWidget
 {
@@ -13,14 +12,16 @@ private:
     QListWidget *commandTypeList;
     QPushButton *bAdd;
     ControlModel *cM;
-    ControlUI *cui;
 
     void setView();
     void setController();
 
+signals:
+    void pleaseUpdateTable(std::shared_ptr<ICommand>);
+
 private slots:
     void onAddButton();
 
 public:
-    PanelCommandTypes(ControlModel *_cM, ControlUI *_cui);
+    PanelCommandTypes(ControlModel *_cM);
 };
diff --git a/src/PanelConfigDirection.cpp b/src/PanelConfigDirection.cpp
index 74def63..e0fcfc5 100644
--- a/src/PanelConfigDirection.cpp
+++ b/src/PanelConfigDirection.cpp
@@ -51,6 +51,8 @@ void PanelConfigDirection::onSaveButton()
     if(d)
     {
         d->setDegree(deg);
-        cui->updateTableView();
+
+        this->update(command);
+        cui->updateTableView(command);
     }
 }
\ No newline at end of file
diff --git a/src/PanelConfigGear.cpp b/src/PanelConfigGear.cpp
index b0b11b7..27fb2e8 100644
--- a/src/PanelConfigGear.cpp
+++ b/src/PanelConfigGear.cpp
@@ -56,11 +56,14 @@ void PanelConfigGear::onSaveButton()
     std::shared_ptr<Gear> g = dynamic_pointer_cast<Gear>(command);
     if(g)
     {
-        if (dur > 0 && dur < 8)
-        {
-            g->setDuration(dur);
-            g->setSpeed(speed);
-            cui->updateTableView();
-        }
+        if (dur < 0)
+            dur = 0;
+        if (dur > 8)
+            dur = 8;
+        g->setDuration(dur);
+        g->setSpeed(speed);
+
+        this->update(command);
+        cui->updateTableView(command);
     }
 }
\ No newline at end of file
diff --git a/src/PanelConfigPause.cpp b/src/PanelConfigPause.cpp
index e0d8b3c..8c827f5 100644
--- a/src/PanelConfigPause.cpp
+++ b/src/PanelConfigPause.cpp
@@ -50,10 +50,13 @@ void PanelConfigPause::onSaveButton()
     std::shared_ptr<Pause> p = dynamic_pointer_cast<Pause>(command);
     if(p)
     {
-        if (dur > 0 && dur < 8)
-        {
-            p->setDuration(dur);
-            cui->updateTableView();
-        }
+        if (dur < 0)
+            dur = 0;
+        if (dur > 8)
+            dur = 8;
+        p->setDuration(dur);
+
+        this->update(command);
+        cui->updateTableView(command);
     }
 }
\ No newline at end of file
diff --git a/src/RoverDialog.h b/src/RoverDialog.h
new file mode 100644
index 0000000..7c10e40
--- /dev/null
+++ b/src/RoverDialog.h
@@ -0,0 +1,33 @@
+#pragma once
+#include <QLayout>
+#include <QLabel>
+#include <QLineEdit>
+#include <QPushButton>
+#include "ControlModel.h"
+
+class RoverDialog : public QDialog
+{
+
+private:
+    QLineEdit *tRover;
+    QPushButton *bOk;
+
+public:
+    RoverDialog(ControlModel * cM)
+    {
+        cM->setSelectedRover();
+        std::string id = cM->getSelectedRoverId().value_or("none");
+        setWindowTitle("Rover Selection");
+        QVBoxLayout *l = new QVBoxLayout();
+        this->setLayout(l);
+        l->addWidget(new QLabel("The following Rover was selected:"));
+        tRover = new QLineEdit(id.c_str());
+        tRover->setAlignment(Qt::AlignCenter);
+        tRover->setReadOnly(true);
+        l->addWidget(tRover);
+        bOk = new QPushButton("Ok");
+        l->addWidget(bOk);
+        updateGeometry();
+        connect(bOk, SIGNAL(clicked()), this, SLOT(accept()));
+    };
+};
\ No newline at end of file
diff --git a/src/TableCommandModel.cpp b/src/TableCommandModel.cpp
new file mode 100644
index 0000000..890b0bd
--- /dev/null
+++ b/src/TableCommandModel.cpp
@@ -0,0 +1,107 @@
+#include "TableCommandModel.h"
+#include <QModelIndex>
+#include "commandlib.h"
+
+QVariant TableCommandModel::data(const QModelIndex &index, int role = Qt::DisplayRole) const
+{
+    if (!index.isValid())
+    {
+        return QVariant();
+    }
+    if (role == Qt::TextAlignmentRole)
+    {
+        return Qt::AlignLeft;
+    }
+    if (role == Qt::CheckStateRole)
+    {
+        return QVariant();
+    }
+
+    std::shared_ptr<ICommand> icom = cL->getCommand(index.row() + 1);
+    QVariant ret = QVariant();
+    if (index.column() == 0)
+    {
+        ret = index.row()+1;
+    }
+    else if (index.column() == 1)
+    {
+        ret = icom->getName().c_str();
+    }
+    else if (index.column() == 2)
+    {
+        bool typeWasFound = false;
+        std::stringstream s;
+
+        std::shared_ptr<IGear> pG = std::dynamic_pointer_cast<IGear>(icom);
+        if (pG)
+        {
+            typeWasFound = true;
+            s << "Speed: " << pG->getSpeed();
+            s << "; Duration: " << pG->getDuration();
+        }
+
+        std::shared_ptr<IDirection> pD = std::dynamic_pointer_cast<IDirection>(icom);
+        if (pD)
+        {
+            typeWasFound = true;
+            s << "Degree: " << pD->getDegree();
+        }
+
+        std::shared_ptr<IPause> pP = std::dynamic_pointer_cast<IPause>(icom);
+        if (pP)
+        {
+            typeWasFound = true;
+            s << "Duration: " << pP->getDuration();
+        }
+
+        if (typeWasFound)
+            ret = s.str().c_str();
+        else
+            ret = QVariant();
+    }
+    else
+    {
+        ret = QVariant();
+    }
+    return ret;
+}
+
+QVariant TableCommandModel::headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const
+{
+    if(orientation == Qt::Horizontal && role == Qt::DisplayRole)
+        if (section < headerNames.size())
+            return headerNames[section];
+    return QVariant();
+}
+
+QModelIndex TableCommandModel::onChange(std::shared_ptr<ICommand> _icom)
+{
+    QModelIndex ret;
+    if (_icom != nullptr)
+    {
+        //update only one command (and its neighbours)
+        int row = cL->getPos(_icom) - 1;
+        for (int j = -1; j <= 1; ++j)
+        {
+            for (int i = 0; i < columnCount(); ++i)
+            {
+                QModelIndex index = createIndex(row + j, i, nullptr);
+                emit(dataChanged(index, index));
+            }
+        }
+        ret  = createIndex(row, 0, nullptr);
+    }
+    else
+    {
+        ret = createIndex(- 1, 0, nullptr);
+    }
+    static int rowsBefore = 0;
+    int rowsNow = rowCount();
+    if (rowsNow != rowsBefore)
+    {
+        emit(layoutAboutToBeChanged());
+        emit(layoutChanged());
+        rowsBefore = rowsNow;
+    }
+    return ret;
+}
diff --git a/src/TableCommandModel.h b/src/TableCommandModel.h
new file mode 100644
index 0000000..7dbb8b5
--- /dev/null
+++ b/src/TableCommandModel.h
@@ -0,0 +1,24 @@
+#pragma once
+#include <QAbstractTableModel>
+#include <array>
+#include "CommandListOWN.h"
+
+class TableCommandModel : public QAbstractTableModel
+{
+private:
+    std::array<QString, 3> headerNames = {"No.", "Command", "Configuration"};
+    CommandList *cL;
+
+public:
+    TableCommandModel(CommandList *_list) { cL = _list; }
+    QVariant data(const QModelIndex &index, int role) const;
+    QVariant headerData(int section, Qt::Orientation orientation, int role) const;
+
+    int columnCount(const QModelIndex &parent = QModelIndex()) const { return 3; }
+    int rowCount(const QModelIndex &parent = QModelIndex()) const { return cL->getSize(); }
+
+    QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) { return createIndex(row, column, nullptr); }
+    QModelIndex parent(const QModelIndex &index) { return QModelIndex(); }
+
+    QModelIndex onChange(std::shared_ptr<ICommand> _icom);
+};
-- 
GitLab