From e1984bd32ea8cd1b11636a4608e3ae53867651be Mon Sep 17 00:00:00 2001
From: Martin Hanzik <martin@hanzik.com>
Date: Sun, 6 May 2018 17:06:59 +0200
Subject: [PATCH] Add output saving

---
 agui2/src/Converter.cpp                     | 11 ++++
 agui2/src/Converter.hpp                     |  2 +
 agui2/src/Graphics/Dialogs/OutputDialog.cpp | 73 +++++++++++++++++++++
 agui2/src/Graphics/Dialogs/OutputDialog.hpp | 11 ++++
 agui2/src/Graphics/Dialogs/OutputDialog.ui  |  7 ++
 agui2/src/GraphvizIntegrator.cpp            | 13 +++-
 agui2/src/GraphvizIntegrator.hpp            |  2 +
 7 files changed, 117 insertions(+), 2 deletions(-)

diff --git a/agui2/src/Converter.cpp b/agui2/src/Converter.cpp
index d23992b2e8..27b654ae96 100644
--- a/agui2/src/Converter.cpp
+++ b/agui2/src/Converter.cpp
@@ -138,4 +138,15 @@ namespace Converter {
         }
         return nullptr;
     }
+
+    void saveToImage(const std::shared_ptr<abstraction::OperationAbstraction>& data, const QString& filename) {
+        if (auto dot = Converter::toDOT(data)) {
+            if (!GraphvizIntegrator::createImageFile(*dot, filename, GraphvizIntegrator::formatFromFilename(filename))) {
+                throw std::runtime_error { "Failed to write to file." };
+            }
+        }
+        else {
+            throw std::runtime_error { "Failed to convert data to DOT." };
+        }
+    }
 }
diff --git a/agui2/src/Converter.hpp b/agui2/src/Converter.hpp
index 84916a7437..efc091f582 100644
--- a/agui2/src/Converter.hpp
+++ b/agui2/src/Converter.hpp
@@ -17,6 +17,8 @@ namespace Converter {
     std::shared_ptr<abstraction::OperationAbstraction> tryParse(const QString& input);
     std::shared_ptr<abstraction::OperationAbstraction> parseXML(const QString& xml);
     std::shared_ptr<abstraction::OperationAbstraction> parseText(const QString& txt);
+
+    void saveToImage(const std::shared_ptr<abstraction::OperationAbstraction>& data, const QString& filename);
 };
 
 
diff --git a/agui2/src/Graphics/Dialogs/OutputDialog.cpp b/agui2/src/Graphics/Dialogs/OutputDialog.cpp
index b2bd3a5c0c..c4fc0bf5cd 100644
--- a/agui2/src/Graphics/Dialogs/OutputDialog.cpp
+++ b/agui2/src/Graphics/Dialogs/OutputDialog.cpp
@@ -3,6 +3,9 @@
 #include <utility>
 #include <Converter.hpp>
 #include <registry/StringWriterRegistry.hpp>
+#include <QtWidgets/QFileDialog>
+#include <QtWidgets/QMessageBox>
+#include <QtCore/QTextStream>
 
 OutputDialog::OutputDialog(std::shared_ptr<abstraction::OperationAbstraction> object)
     : ui(new Ui::OutputDialog)
@@ -43,3 +46,73 @@ OutputDialog::OutputDialog(std::shared_ptr<abstraction::OperationAbstraction> ob
 void OutputDialog::hideTab(QWidget* tab) {
     ui->tabWidget->setTabEnabled(ui->tabWidget->indexOf(tab), false);
 }
+
+void OutputDialog::on_saveBtn_clicked() {
+    auto [filter, extension] = this->getCurrentTabFileFilter();
+    QString filename(QFileDialog::getSaveFileName(this,
+                                                  tr("Save file"),
+                                                  QDir::homePath(),
+                                                  filter));
+    if (filename.isEmpty()) {
+        return;
+    }
+
+    if (!filename.endsWith(extension)) {
+        filename += extension;
+    }
+
+    auto type = this->getCurrentTabType();
+    if (type == TabType::Image) {
+        Converter::saveToImage(this->object, filename);
+    }
+    else {
+        QFile file(filename);
+        if (!file.open(QFile::WriteOnly | QFile::Text | QFile::Truncate)) {
+            QMessageBox::warning(this, "Warning", "Failed to open file.");
+            return;
+        }
+
+        QTextStream stream(&file);
+        switch (type) {
+            case TabType::Text:
+                stream << ui->textEdit_text->toPlainText();
+                break;
+            case TabType::XML:
+                stream << ui->textEdit_xml->toPlainText();
+                break;
+            case TabType::DOT:
+                stream << ui->textEdit_dot->toPlainText();
+                break;
+            case TabType::Image:
+                Q_ASSERT(false);
+                break;
+        }
+        file.close();
+    }
+}
+
+TabType OutputDialog::getCurrentTabType() const {
+    if (ui->tabWidget->currentWidget() == ui->tab_text) {
+        return TabType::Text;
+    }
+    else if (ui->tabWidget->currentWidget() == ui->tab_xml) {
+        return TabType::XML;
+    }
+    else if (ui->tabWidget->currentWidget() == ui->tab_dot) {
+        return TabType::DOT;
+    }
+    else if (ui->tabWidget->currentWidget() == ui->tab_image) {
+        return TabType::Image;
+    }
+
+    Q_ASSERT(false);
+}
+
+std::pair<QString, QString> OutputDialog::getCurrentTabFileFilter() const {
+    switch (this->getCurrentTabType()) {
+        case TabType::Text: return { "Text files (*.txt)", ".txt" };
+        case TabType::XML: return { "XML files (*.xml)", ".xml" };
+        case TabType::DOT: return { "DOT files (*.dot)", ".dot" };
+        case TabType::Image: return { "PNG files (*.png)", ".png" };
+    }
+}
diff --git a/agui2/src/Graphics/Dialogs/OutputDialog.hpp b/agui2/src/Graphics/Dialogs/OutputDialog.hpp
index 2a0578ae7a..f7457e67d3 100644
--- a/agui2/src/Graphics/Dialogs/OutputDialog.hpp
+++ b/agui2/src/Graphics/Dialogs/OutputDialog.hpp
@@ -7,6 +7,13 @@
 
 #include <ui_OutputDialog.h>
 
+enum class TabType {
+    Text,
+    XML,
+    DOT,
+    Image
+};
+
 class OutputDialog : public QDialog
 {
 Q_OBJECT
@@ -15,10 +22,14 @@ public:
     explicit OutputDialog(std::shared_ptr<abstraction::OperationAbstraction> object);
 
 private slots:
+    void on_saveBtn_clicked();
 
 private:
     void hideTab(QWidget* tab);
 
+    TabType getCurrentTabType() const;
+    std::pair<QString, QString> getCurrentTabFileFilter() const;
+
     std::unique_ptr<Ui::OutputDialog> ui;
     std::shared_ptr<abstraction::OperationAbstraction> object;
 };
diff --git a/agui2/src/Graphics/Dialogs/OutputDialog.ui b/agui2/src/Graphics/Dialogs/OutputDialog.ui
index 00bdd5daee..3341e58e46 100644
--- a/agui2/src/Graphics/Dialogs/OutputDialog.ui
+++ b/agui2/src/Graphics/Dialogs/OutputDialog.ui
@@ -93,6 +93,13 @@
      </widget>
     </widget>
    </item>
+   <item row="1" column="0">
+    <widget class="QPushButton" name="saveBtn">
+     <property name="text">
+      <string>Save</string>
+     </property>
+    </widget>
+   </item>
   </layout>
  </widget>
  <resources/>
diff --git a/agui2/src/GraphvizIntegrator.cpp b/agui2/src/GraphvizIntegrator.cpp
index 06a7113e9e..a2ed55b78e 100644
--- a/agui2/src/GraphvizIntegrator.cpp
+++ b/agui2/src/GraphvizIntegrator.cpp
@@ -5,9 +5,9 @@
 QString selectFormat(GraphvizIntegrator::PictureFormat format) {
     switch (format) {
         case GraphvizIntegrator::PictureFormat::PNG:
-            return QString("png");
+            return "png";
         case GraphvizIntegrator::PictureFormat::SVG:
-            return QString("svg");
+            return "svg";
     }
 }
 
@@ -46,4 +46,13 @@ namespace GraphvizIntegrator {
         agclose(G);
         return true;
     }
+
+
+    PictureFormat formatFromFilename(const QString& format) {
+        if (format.endsWith("svg"))
+            return SVG;
+        if (format.endsWith("png"))
+            return PNG;
+        throw std::runtime_error { "Failed to determine output format from filename." };
+    }
 }
\ No newline at end of file
diff --git a/agui2/src/GraphvizIntegrator.hpp b/agui2/src/GraphvizIntegrator.hpp
index 93ca41b3f0..e20be0374c 100644
--- a/agui2/src/GraphvizIntegrator.hpp
+++ b/agui2/src/GraphvizIntegrator.hpp
@@ -11,6 +11,8 @@ namespace GraphvizIntegrator
 
     QImage createImage(const QString& dotData , PictureFormat format);
     bool createImageFile(const QString& dotData, QString filename , PictureFormat format);
+
+    PictureFormat formatFromFilename(const QString& qString);
 };
 
 
-- 
GitLab