]> Devoid-pointer.net GitWeb - anyanka.git/commitdiff
Initial commit
authorMichal Malý <madcatxster@prifuk.cz>
Sun, 17 Nov 2013 01:44:08 +0000 (02:44 +0100)
committerMichal Malý <madcatxster@prifuk.cz>
Sun, 17 Nov 2013 01:44:08 +0000 (02:44 +0100)
51 files changed:
Anyanka.pro [new file with mode: 0644]
agreinterface.cpp [new file with mode: 0644]
agreinterface.h [new file with mode: 0644]
datafilesloader.cpp [new file with mode: 0644]
datafilesloader.h [new file with mode: 0644]
datamanager.cpp [new file with mode: 0644]
datamanager.h [new file with mode: 0644]
globalinfo.cpp [new file with mode: 0644]
globalinfo.h [new file with mode: 0644]
gui/aboutanyanka.cpp [new file with mode: 0644]
gui/aboutanyanka.h [new file with mode: 0644]
gui/aboutanyanka.ui [new file with mode: 0644]
gui/graphview.cpp [new file with mode: 0644]
gui/graphview.h [new file with mode: 0644]
gui/graphviewcontextmenu.cpp [new file with mode: 0644]
gui/graphviewcontextmenu.h [new file with mode: 0644]
gui/mainwindow.cpp [new file with mode: 0644]
gui/mainwindow.h [new file with mode: 0644]
gui/mainwindow.ui [new file with mode: 0644]
gui/signalview.cpp [new file with mode: 0644]
gui/signalview.h [new file with mode: 0644]
gui/signalview.ui [new file with mode: 0644]
imgresources.qrc [new file with mode: 0644]
integratedpeak.cpp [new file with mode: 0644]
integratedpeak.h [new file with mode: 0644]
integrationtablemodel.cpp [new file with mode: 0644]
integrationtablemodel.h [new file with mode: 0644]
integrator.cpp [new file with mode: 0644]
integrator.h [new file with mode: 0644]
locallogger.cpp [new file with mode: 0644]
locallogger.h [new file with mode: 0644]
logger.cpp [new file with mode: 0644]
logger.h [new file with mode: 0644]
main.cpp [new file with mode: 0644]
mathhelpers.h [new file with mode: 0644]
metatypes.h [new file with mode: 0644]
resources/Qt_master_logo.png [new file with mode: 0644]
sequence.cpp [new file with mode: 0644]
sequence.h [new file with mode: 0644]
sequenceselectormodel.cpp [new file with mode: 0644]
sequenceselectormodel.h [new file with mode: 0644]
signal.cpp [new file with mode: 0644]
signal.h [new file with mode: 0644]
signalcontroller.cpp [new file with mode: 0644]
signalcontroller.h [new file with mode: 0644]
signaldatatablemodel.cpp [new file with mode: 0644]
signaldatatablemodel.h [new file with mode: 0644]
singlerundata.cpp [new file with mode: 0644]
singlerundata.h [new file with mode: 0644]
singlerunsselectormodel.cpp [new file with mode: 0644]
singlerunsselectormodel.h [new file with mode: 0644]

diff --git a/Anyanka.pro b/Anyanka.pro
new file mode 100644 (file)
index 0000000..779d7e0
--- /dev/null
@@ -0,0 +1,78 @@
+#-------------------------------------------------
+#
+# Project created by QtCreator 2013-10-28T18:00:16
+#
+#-------------------------------------------------
+
+QT       += core gui
+QT       -= qml_debug
+
+greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
+
+TARGET = Anyanka
+TEMPLATE = app
+
+unix {
+        LIBS += -ldl
+        QMAKE_CXXFLAGS += -std=c++11 -Wall
+}
+
+INCLUDEPATH += libAGRE
+# Add project root directory to work around VC11 include path scheme
+INCLUDEPATH += "./"
+
+SOURCES += main.cpp\
+    datafilesloader.cpp \
+    agreinterface.cpp \
+    datamanager.cpp \
+    signal.cpp \
+    singlerundata.cpp \
+    gui/mainwindow.cpp \
+    logger.cpp \
+    gui/signalview.cpp \
+    locallogger.cpp \
+    gui/graphview.cpp \
+    signalcontroller.cpp \
+    integratedpeak.cpp \
+    sequence.cpp \
+    sequenceselectormodel.cpp \
+    singlerunsselectormodel.cpp \
+    signaldatatablemodel.cpp \
+    gui/graphviewcontextmenu.cpp \
+    integrator.cpp \
+    integrationtablemodel.cpp \
+    gui/aboutanyanka.cpp \
+    globalinfo.cpp
+
+HEADERS  += \
+    datafilesloader.h \
+    agreinterface.h \
+    datamanager.h \
+    signal.h \
+    singlerundata.h \
+    gui/mainwindow.h \
+    logger.h \
+    gui/signalview.h \
+    locallogger.h \
+    gui/graphview.h \
+    signalcontroller.h \
+    metatypes.h \
+    mathhelpers.h \
+    integratedpeak.h \
+    sequence.h \
+    sequenceselectormodel.h \
+    singlerunsselectormodel.h \
+    signaldatatablemodel.h \
+    gui/graphviewcontextmenu.h \
+    integrator.h \
+    integrationtablemodel.h \
+    gui/aboutanyanka.h \
+    globalinfo.h
+
+FORMS += \
+    gui/mainwindow.ui \
+    gui/signalview.ui \
+    gui/aboutanyanka.ui
+
+RESOURCES += \
+    imgresources.qrc
diff --git a/agreinterface.cpp b/agreinterface.cpp
new file mode 100644 (file)
index 0000000..b022a97
--- /dev/null
@@ -0,0 +1,145 @@
+/*
+  Copyright (c) 2013 Michal Malý <madcatxster@prifuk.cz>
+
+  Permission is hereby granted, free of charge, to any person obtaining a copy
+  of this software and associated documentation files (the "Software"), to deal
+  in the Software without restriction, including without limitation the rights
+  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+  copies of the Software, and to permit persons to whom the Software is
+  furnished to do so, subject to the following conditions:
+
+  The above copyright notice and this permission notice shall be included in
+  all copies or substantial portions of the Software.
+
+  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+  THE SOFTWARE.
+*/
+
+#include "agreinterface.h"
+#include "logger.h"
+#include <QDebug>
+#ifdef Q_OS_UNIX
+#include <dlfcn.h>
+#elif defined Q_OS_WIN32
+#include <windows.h>
+#endif
+
+#ifdef Q_OS_UNIX
+#define __LIBRARY_NAME "libAGRE.so"
+#elif defined Q_OS_WIN
+#define __LIBRARY_NAME "libAGRE.dll"
+#endif
+
+#ifdef Q_OS_UNIX
+IAGRE_Reader* (*AGREInterface::m_create_reader)(bool)(nullptr);
+#elif defined Q_OS_WIN
+IAGRE_Reader* (__cdecl* AGREInterface::m_create_reader)(bool)(NULL);
+#endif
+AGREInterface* AGREInterface::m_instance(nullptr);
+
+const QString AGREInterface::AGRE_LIBRARY_NAME(__LIBRARY_NAME);
+const QString AGREInterface::ME_SENDER_STR("AGREInterface");
+
+/* Public static methods */
+
+AGREInterface* AGREInterface::instance()
+{
+  if (m_instance == nullptr)
+    m_instance = new AGREInterface();
+  return m_instance;
+}
+
+AGREInterface::AGREInterface(QObject* parent) :
+  QObject(parent),
+  m_inited(false),
+  m_lastAGREError(AGRE_ReturnCode::SUCCESS),
+  m_readerIFace(nullptr)
+{
+}
+
+AGREInterface::ReturnCode AGREInterface::initialize()
+{
+  if (m_inited)
+    return ReturnCode::SUCCESS;
+
+#ifdef Q_OS_UNIX
+  m_libagre_handle = dlopen(__LIBRARY_NAME, RTLD_LAZY);
+  if (m_libagre_handle == nullptr) {
+#elif defined Q_OS_WIN
+  m_libagre_handle = LoadLibrary(TEXT(__LIBRARY_NAME));
+  if (m_libagre_handle == NULL) {
+#endif
+    Logger::log(Logger::Level::CRITICAL, ME_SENDER_STR, "Library " + AGRE_LIBRARY_NAME + " could not have been loaded.");
+    return ReturnCode::E_NO_LIBRARY;
+  }
+
+#ifdef Q_OS_UNIX
+  m_create_reader = (IAGRE_Reader* (*)(bool))dlsym(m_libagre_handle, "create_agre_reader");
+  if (m_create_reader == nullptr) {
+#elif defined Q_OS_WIN
+  m_create_reader = (IAGRE_Reader* (__cdecl*)(bool))GetProcAddress(static_cast<HINSTANCE>(m_libagre_handle), "create_agre_reader");
+  if (m_create_reader == NULL) {
+#endif
+    Logger::log(Logger::Level::CRITICAL, ME_SENDER_STR, "create_agre_reader symbol could not have been resloved.");
+    return ReturnCode::E_CANNOT_RESOLVE;
+  }
+
+
+  m_readerIFace = m_create_reader(true);
+  if (m_readerIFace == nullptr) {
+    m_create_reader = nullptr;
+#ifdef Q_OS_UNIX
+    dlclose(m_libagre_handle);
+#elif defined Q_OS_WIN
+    FreeLibrary(static_cast<HINSTANCE>(m_libagre_handle));
+#endif
+    return ReturnCode::E_CANNOT_GET_IFACE;
+  }
+
+  m_inited = true;
+  return ReturnCode::SUCCESS;
+}
+
+/* ENDOF public static methods */
+
+/* Public methods */
+
+const std::vector<std::string>* AGREInterface::debugAGREInfo()
+{
+  if (!m_inited)
+    return nullptr;
+
+  return &m_readerIFace->debug_info_list();
+}
+
+AGREInterface::ReturnCode AGREInterface::readFile(const std::string& fileName, std::shared_ptr<AGRE_MeasurementInfo>& minfo)
+{
+  AGRE_ReturnCode ret;
+
+  if (!m_inited)
+    return ReturnCode::E_NOT_INITED;
+  ret = m_readerIFace->read_file(fileName, minfo);
+  if (ret != AGRE_ReturnCode::SUCCESS) {
+    m_lastAGREError = ret;
+    return ReturnCode::E_CANNOT_READ_FILE;
+  }
+
+  return ReturnCode::SUCCESS;
+}
+
+AGREInterface::~AGREInterface()
+{
+  m_inited = false;
+
+  m_readerIFace->destroy_agre_reader();
+#ifdef Q_OS_UNIX
+  dlclose(m_libagre_handle);
+#elif defined Q_OS_WIN
+  FreeLibrary(static_cast<HMODULE>(m_libagre_handle));
+#endif
+}
diff --git a/agreinterface.h b/agreinterface.h
new file mode 100644 (file)
index 0000000..75d899c
--- /dev/null
@@ -0,0 +1,77 @@
+/*
+  Copyright (c) 2013 Michal Malý <madcatxster@prifuk.cz>
+
+  Permission is hereby granted, free of charge, to any person obtaining a copy
+  of this software and associated documentation files (the "Software"), to deal
+  in the Software without restriction, including without limitation the rights
+  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+  copies of the Software, and to permit persons to whom the Software is
+  furnished to do so, subject to the following conditions:
+
+  The above copyright notice and this permission notice shall be included in
+  all copies or substantial portions of the Software.
+
+  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+  THE SOFTWARE.
+*/
+
+
+#ifndef AGREINTERFACE_H
+#define AGREINTERFACE_H
+
+#include <iagre_reader.h>
+#include <QtCore/QObject>
+
+class AGREInterface : public QObject
+{
+  Q_OBJECT
+  Q_DISABLE_COPY(AGREInterface)
+
+public:
+  enum class ReturnCode {
+    SUCCESS,
+    E_NO_LIBRARY,
+    E_CANNOT_RESOLVE,
+    E_CANNOT_GET_IFACE,
+    E_NOT_INITED,
+    E_CANNOT_READ_FILE
+  };
+
+  const std::vector<std::string>* debugAGREInfo();
+  AGRE_ReturnCode lastAGREError() { AGRE_ReturnCode r = m_lastAGREError; m_lastAGREError = AGRE_ReturnCode::SUCCESS; return r; }
+  ReturnCode initialize();
+  static AGREInterface* instance();
+  ReturnCode readFile(const std::string& fileName, std::shared_ptr<AGRE_MeasurementInfo>& minfo);
+
+  static const QString AGRE_LIBRARY_NAME;
+  static const QString ME_SENDER_STR;
+
+private:
+  explicit AGREInterface(QObject* parent = 0);
+  ~AGREInterface();
+
+  bool m_inited;
+  AGRE_ReturnCode m_lastAGREError;
+  IAGRE_Reader* m_readerIFace;
+
+#ifdef Q_OS_UNIX
+  static IAGRE_Reader* (*m_create_reader)(bool collect_debug);
+#elif defined Q_OS_WIN
+  static IAGRE_Reader* (__cdecl* m_create_reader)(bool collect_debug);
+#endif
+  void* m_libagre_handle;
+
+  static AGREInterface* m_instance;
+
+signals:
+
+public slots:
+
+};
+
+#endif // AGREINTERFACE_H
diff --git a/datafilesloader.cpp b/datafilesloader.cpp
new file mode 100644 (file)
index 0000000..61a9a88
--- /dev/null
@@ -0,0 +1,89 @@
+/*
+  Copyright (c) 2013 Michal Malý <madcatxster@prifuk.cz>
+
+  Permission is hereby granted, free of charge, to any person obtaining a copy
+  of this software and associated documentation files (the "Software"), to deal
+  in the Software without restriction, including without limitation the rights
+  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+  copies of the Software, and to permit persons to whom the Software is
+  furnished to do so, subject to the following conditions:
+
+  The above copyright notice and this permission notice shall be included in
+  all copies or substantial portions of the Software.
+
+  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+  THE SOFTWARE.
+*/
+
+#include "datafilesloader.h"
+#include "logger.h"
+#include <QtWidgets/QMessageBox>
+#include <QDebug>
+
+const QString DataFilesLoader::ME_SENDER_STR("DataFilesLoader");
+
+DataFilesLoader::DataFilesLoader(QObject *parent) :
+  QObject(parent)
+{
+  m_supportedFileTypes << "*.ch";
+}
+
+DataFilesLoader::ReturnCode DataFilesLoader::loadSingleRun(const QDir& path, std::vector<std::shared_ptr<AGRE_MeasurementInfo>>& dataFiles)
+{
+  for (const QString& s : path.entryList(m_supportedFileTypes, QDir::Files | QDir::NoDotAndDotDot)) {
+    std::shared_ptr<AGRE_MeasurementInfo> minfo = nullptr;
+    QString absPath = path.absoluteFilePath(s);
+
+    AGREInterface::ReturnCode ret = AGREInterface::instance()->readFile(absPath.toStdString(), minfo);
+    if (ret == AGREInterface::ReturnCode::SUCCESS)
+      dataFiles.push_back(minfo);
+    else if (ret == AGREInterface::ReturnCode::E_NOT_INITED) {
+      Logger::log(Logger::Level::CRITICAL, ME_SENDER_STR, "AGREInterface has not been initialized.");
+      QMessageBox::critical(nullptr, "AGREInterface error", "AGRE interface has not been initialized. This should not happen!\n"
+                            "Application cannot continue.");
+      exit(1);
+    } else {
+      QString errDesc = errorToString(AGREInterface::instance()->lastAGREError());
+      Logger::log(Logger::Level::DEBUG, ME_SENDER_STR, "Error reading file '" + s + "'" + errDesc);
+      const std::vector<std::string>* const dbgMsgs = AGREInterface::instance()->debugAGREInfo();
+      if (dbgMsgs == nullptr)
+        Logger::log(Logger::Level::CRITICAL, ME_SENDER_STR, "Null pointer to debug messages list. This should not happen!\n");
+      else {
+        for (const std::string& s : *dbgMsgs)
+          Logger::log(Logger::Level::INFO, ME_SENDER_STR, QString("AGRE debug:") + QString(s.c_str()));
+      }
+      QMessageBox::warning(nullptr, "Error reading file '" + s + "'", errDesc);
+      return ReturnCode::E_READ_ERROR;
+    }
+  }
+
+  if (dataFiles.size() == 0)
+    return ReturnCode::E_NO_FILES;
+
+  Logger::log(Logger::Level::DEBUG, ME_SENDER_STR, QString::number(dataFiles.size()) + " files were found and successfully parsed");
+
+  return ReturnCode::SUCCESS;
+}
+
+QString DataFilesLoader::errorToString(const AGRE_ReturnCode errorCode)
+{
+  switch (errorCode) {
+    case AGRE_ReturnCode::E_CANNOT_OPEN_FILE:
+      return "Cannot open file.";
+    case AGRE_ReturnCode::E_CORRUPTED_FILE:
+      return "File is corrupted.";
+    case AGRE_ReturnCode::E_INVALID_FILE:
+      return "File does not appear to be a ChemStation data file.";
+    case AGRE_ReturnCode::E_OUT_OF_RANGE:
+      return "File is too short to be a ChemStation data file.";
+    case AGRE_ReturnCode::E_READ_FAILED:
+      return "An error occured during reading.";
+    default:
+      return "Unspecified error " + QString::number(static_cast<int>(errorCode));
+  }
+}
diff --git a/datafilesloader.h b/datafilesloader.h
new file mode 100644 (file)
index 0000000..a9b1c62
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+  Copyright (c) 2013 Michal Malý <madcatxster@prifuk.cz>
+
+  Permission is hereby granted, free of charge, to any person obtaining a copy
+  of this software and associated documentation files (the "Software"), to deal
+  in the Software without restriction, including without limitation the rights
+  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+  copies of the Software, and to permit persons to whom the Software is
+  furnished to do so, subject to the following conditions:
+
+  The above copyright notice and this permission notice shall be included in
+  all copies or substantial portions of the Software.
+
+  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+  THE SOFTWARE.
+*/
+
+#ifndef DATAFILESLOADER_H
+#define DATAFILESLOADER_H
+
+#include "agreinterface.h"
+#include <QtCore/QDir>
+#include <QtCore/QObject>
+
+class DataFilesLoader : public QObject
+{
+  Q_OBJECT
+public:
+  enum class ReturnCode {
+    SUCCESS,
+    E_NO_FILES,
+    E_READ_ERROR,
+    E_FATAL
+  };
+
+  explicit DataFilesLoader(QObject* parent = 0);
+  ReturnCode loadSingleRun(const QDir& path, std::vector<std::shared_ptr<AGRE_MeasurementInfo>>& dataFiles);
+
+private:
+  QStringList m_supportedFileTypes;
+
+  QString errorToString(const AGRE_ReturnCode errorCode);
+
+  static const QString ME_SENDER_STR;
+
+signals:
+
+public slots:
+
+};
+
+#endif // DATAFILESLOADER_H
diff --git a/datamanager.cpp b/datamanager.cpp
new file mode 100644 (file)
index 0000000..58d7201
--- /dev/null
@@ -0,0 +1,426 @@
+/*
+  Copyright (c) 2013 Michal Malý <madcatxster@prifuk.cz>
+
+  Permission is hereby granted, free of charge, to any person obtaining a copy
+  of this software and associated documentation files (the "Software"), to deal
+  in the Software without restriction, including without limitation the rights
+  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+  copies of the Software, and to permit persons to whom the Software is
+  furnished to do so, subject to the following conditions:
+
+  The above copyright notice and this permission notice shall be included in
+  all copies or substantial portions of the Software.
+
+  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+  THE SOFTWARE.
+*/
+
+#include "datamanager.h"
+#include "logger.h"
+#include <QtWidgets/QMessageBox>
+
+#include <QDebug>
+
+const QString DataManager::ME_SENDER_STR("DataManager");
+const char DataManager::UNIT_KILOVOLTS_TEXT[] = {0x4B, 0x56, 0x00};
+const char DataManager::UNIT_MICROAMPERES_TEXT[] = {static_cast<char>(0xB5), 0x41, 0x00};
+const char DataManager::UNIT_MILLIAU_TEXT[] = {0x6D, 0x41, 0x55, 0x00};
+const char DataManager::UNIT_MILLIVOLTS_TEXT[] = {0x6D, 0x56, 0x00};
+const char DataManager::UNIT_WATTS_TEXT[] = {0x57, 0x00};
+const QStringList DataManager::DATA_DIR_SUFFIX = { "*.D" };
+
+DataManager::DataManager(QObject* parent) :
+  QObject(parent),
+  m_sequenceRejected(false)
+{
+  m_sequences.push_back(std::make_pair<const QString, std::shared_ptr<Sequence>>("Single runs", std::shared_ptr<Sequence>(new Sequence())));
+  m_seqSelModel.setSequences(&m_sequences);
+  m_sigNameCodec = QTextCodec::codecForName("ISO-8859-1");
+}
+
+std::shared_ptr<Sequence> DataManager::sequenceByKey(const QString& key)
+{
+  for (NameSequencePair p : m_sequences) {
+    if (p.first.compare(key) == 0)
+      return p.second;
+  }
+  return nullptr;
+}
+
+int DataManager::sequenceIdxByKey(const QString& key)
+{
+  int idx = 0;
+  for (NameSequencePair p : m_sequences) {
+    qDebug() << p.first << key;
+    if (p.first.compare(key) == 0)
+      return idx;
+    idx++;
+  }
+  return -1;
+}
+
+Signal::Equipment DataManager::equipmentFromFiletype(AGRE_Filetype filetype)
+{
+  switch (filetype) {
+    case AGRE_Filetype::CE_CCD:
+    case AGRE_Filetype::CE_CURRENT:
+    case AGRE_Filetype::CE_DAD:
+    case AGRE_Filetype::CE_POWER:
+    case AGRE_Filetype::CE_VOLTAGE:
+      return Signal::Equipment::CE;
+    default:
+      throw std::invalid_argument("Invalid type of file");
+  }
+}
+
+Signal::Resource DataManager::resourceFromFiletype(AGRE_Filetype filetype)
+{
+  switch (filetype) {
+    case AGRE_Filetype::CE_CCD:
+      return Signal::Resource::CE_CCD;
+    case AGRE_Filetype::CE_CURRENT:
+      return Signal::Resource::CE_CURRENT;
+    case AGRE_Filetype::CE_DAD:
+      return Signal::Resource::CE_DAD;
+    case AGRE_Filetype::CE_POWER:
+      return Signal::Resource::CE_POWER;
+    case AGRE_Filetype::CE_VOLTAGE:
+      return Signal::Resource::CE_VOLTAGE;
+    default:
+      throw std::invalid_argument("Invalid type of signal");
+  }
+}
+
+std::string DataManager::signalControllerKey(const QString& main, const QString& resource)
+{
+  return std::string(main.toStdString() + "_" + resource.toStdString());
+}
+
+Signal::YUnit DataManager::yunitFromUnitStr(const std::string& unit_str)
+{
+  static const QString LATIN_KILOVOLTS = QString::fromLatin1(DataManager::UNIT_KILOVOLTS_TEXT);
+  static const QString LATIN_MICROAMPERS = QString::fromLatin1(DataManager::UNIT_MICROAMPERES_TEXT);
+  static const QString LATIN_MILLIAU = QString::fromLatin1(DataManager::UNIT_MILLIAU_TEXT);
+  static const QString LATIN_MILLIVOLTS = QString::fromLatin1(DataManager::UNIT_MILLIVOLTS_TEXT);
+  static const QString LATIN_WATTS = QString::fromLatin1(DataManager::UNIT_WATTS_TEXT);
+  QString str = m_sigNameCodec->toUnicode(unit_str.c_str());
+
+  if (QString::compare(str, LATIN_MILLIVOLTS) == 0)
+    return Signal::YUnit::MILLIVOLTS;
+  else if (QString::compare(str, LATIN_KILOVOLTS) == 0)
+    return Signal::YUnit::KILOVOLTS;
+  else if (QString::compare(str, LATIN_MICROAMPERS) == 0)
+    return Signal::YUnit::MICROAMPERES;
+  else if (QString::compare(str, LATIN_MILLIAU) == 0)
+    return Signal::YUnit::MILLIAU;
+  else if (QString::compare(str, LATIN_WATTS) == 0)
+    return Signal::YUnit::WATTS;
+  else
+    throw std::invalid_argument(str.toStdString());
+}
+
+std::shared_ptr<SingleRunData> DataManager::loadSingleRun(QDir& dir)
+{
+  std::vector<std::shared_ptr<AGRE_MeasurementInfo>> dataFiles;
+  std::shared_ptr<SingleRunData> singleRun;
+  std::vector<std::shared_ptr<Signal>> sigs;
+  DataFilesLoader::ReturnCode ret;
+  LocalLoggerPtr llog = Logger::createLocalLog();
+  llog->setPrintLevel(Logger::Level::WARNING);
+
+  ret = m_dfl.loadSingleRun(dir, dataFiles);
+  if (ret == DataFilesLoader::ReturnCode::E_NO_FILES) {
+    QMessageBox::warning(nullptr, ME_SENDER_STR, "Directory " + dir.path() + " does not contain any ChemStation data files.");
+    return nullptr;
+  }
+  if (ret != DataFilesLoader::ReturnCode::SUCCESS) {
+    return nullptr;
+  }
+
+  QStringList operatorNames;
+  QStringList methodNames;
+  QStringList sampleInfos;
+  QList<QDate> dates;
+  QList<QTime> times;
+  for (std::shared_ptr<AGRE_MeasurementInfo> mi : dataFiles) {
+    Signal::Equipment eqp;
+    Signal::Resource res;
+    double samplingRate;
+    Signal::YUnit yu;
+    QDate date(mi->year, mi->month, mi->day);
+    QTime time(mi->hour, mi->minute, mi->second);
+    QString operatorName;
+    QString methodName;
+    QString sampleInfo;
+
+    operatorName = QString::fromStdString(mi->operator_name);
+    methodName = QString::fromStdString(mi->method_name);
+    sampleInfo = QString::fromStdString(mi->sample_info);
+
+    try {
+      eqp = equipmentFromFiletype(mi->file_type);
+    } catch (std::invalid_argument& ia) {
+      llog->log(Logger::ERROR, ME_SENDER_STR, ia.what());
+      continue;
+    }
+    try {
+      res = resourceFromFiletype(mi->file_type);
+    } catch (std::invalid_argument& ia) {
+      llog->log(Logger::ERROR, ME_SENDER_STR, ia.what());
+      continue;
+    }
+    try {
+      yu = yunitFromUnitStr(mi->y_units);
+    } catch (std::invalid_argument& ia) {
+      llog->log(Logger::ERROR, ME_SENDER_STR, QString("Invalid units ") + ia.what());
+      continue;
+    }
+    samplingRate = mi->sampling_rate;
+
+    /* Fill values */
+    std::vector<Signal::TimeValuePair> values;
+    for (const AGRE_TimeValuePair& tvp : mi->val_tbl) {
+      Signal::TimeValuePair stvp(tvp.first, tvp.second);
+      values.push_back(stvp);
+    }
+    std::shared_ptr<Signal> _s(new Signal(eqp, res, samplingRate, yu, mi->dad_wavelength_abs, mi->dad_wavelength_ref, values));
+    sigs.push_back(_s);
+
+    if (!date.isValid()) {
+      llog->log(Logger::WARNING, ME_SENDER_STR, QString("Date ") + date.toString() + QString("is invalid."));
+    } else
+      dates.append(date);
+    if (!time.isValid()) {
+      llog->log(Logger::WARNING, ME_SENDER_STR, QString("Time ") + time.toString() + QString("is invalid."));
+    } else
+      times.append(time);
+
+    operatorNames.append(operatorName);
+    methodNames.append(methodName);
+    sampleInfos.append(sampleInfo);
+  }
+
+  /* Check that all common run informaton are the same */
+  for (int i = 1; i < operatorNames.size(); i++) {
+    if (QString::compare(operatorNames.at(i-1), operatorNames.at(i)) != 0) {
+      QString w;
+      w += "Operator name in file " + QString::number(i-1) + " (" + operatorNames.at(i-1) + ") does not match that in file " + QString::number(i) + " (" + operatorNames.at(i) + ")";
+      llog->log(Logger::WARNING, ME_SENDER_STR, w);
+    }
+  }
+  for (int i = 1; i < methodNames.size(); i++) {
+    if (QString::compare(methodNames.at(i-1), methodNames.at(i)) != 0) {
+      QString w;
+      w += "Method name in file " + QString::number(i-1) + " (" + methodNames.at(i-1) + ") does not match that in file " + QString::number(i) + " (" + methodNames.at(i) + ")";
+      llog->log(Logger::WARNING, ME_SENDER_STR, w);
+    }
+  }
+  for (int i = 1; i < sampleInfos.size(); i++) {
+    if (QString::compare(sampleInfos.at(i-1), sampleInfos.at(i)) != 0) {
+      QString w;
+      w += "Sample info in file " + QString::number(i-1) + " (" + sampleInfos.at(i-1) + ") does not match that in file " + QString::number(i) + " (" + sampleInfos.at(i) + ")";
+      llog->log(Logger::WARNING, ME_SENDER_STR, w);
+    }
+  }
+  for (int i = 1; i < dates.size(); i++) {
+    if (dates.at(i-1) != dates.at(i)) {
+      QString w;
+      w += "Date in file " + QString::number(i-1) + " (" + dates.at(i-1).toString() + ") does not match that in file " + QString::number(i) + " (" + dates.at(i).toString() + ")";
+      llog->log(Logger::WARNING, ME_SENDER_STR, w);
+    }
+  }
+  for (int i = 1; i < times.size(); i++) {
+    if (times.at(i-1) != times.at(i)) {
+      QString w;
+      w += "Date in file " + QString::number(i-1) + " (" + times.at(i-1).toString() + ") does not match that in file" + QString::number(i) + " (" + times.at(i).toString() + ")";
+      llog->log(Logger::WARNING, ME_SENDER_STR, w);
+    }
+  }
+
+  singleRun = std::shared_ptr<SingleRunData>(new SingleRunData(methodNames.at(0), operatorNames.at(0), sampleInfos.at(0), dates.at(0), times.at(0), sigs,
+                                                               dir.dirName(), this));
+
+  Logger::log(Logger::DEBUG, ME_SENDER_STR, "Single run successfully parsed");
+
+  return singleRun;
+}
+
+void DataManager::showOneSignal(std::shared_ptr<SignalController> ctrl)
+{
+  SignalView* swp = new SignalView();
+
+  swp->setDataTableModel(ctrl->dataTableModel());
+  swp->setIntegrationTableModel(ctrl->integrationTableModel());
+  swp->setTypeText(ctrl->signal()->resourceToString());
+  swp->setXUnits(ctrl->signal()->xunitToString());
+  swp->setYUnits(ctrl->signal()->yunitToString());
+  connect(swp, SIGNAL(crosshairErased()), ctrl.get(), SLOT(onViewCrosshairErased()));
+  connect(swp, SIGNAL(crosshairMoved(int,int)), ctrl.get(), SLOT(onViewCrosshairMoved(int,int)));
+  connect(swp, SIGNAL(integrated(int,int,int,int)), ctrl.get(), SLOT(onViewIntegrated(int,int,int,int)));
+  connect(swp, SIGNAL(redrawNeeded()), ctrl.get(), SLOT(onViewRedrawNeeded()));
+  connect(swp, SIGNAL(resized(int,int)), ctrl.get(), SLOT(onViewResized(int,int)));
+  connect(swp, SIGNAL(showContextMenu(int,int,QPoint)), ctrl.get(), SLOT(onViewShowContextMenu(int,int,QPoint)));
+  connect(swp, SIGNAL(zoomed(int,int,int,int)), ctrl.get(), SLOT(onViewZoomed(int,int,int,int)));
+  connect(ctrl.get(), SIGNAL(guiDrawGraph(double*,size_t,double,double)), swp, SLOT(onUpdateGraph(double*,size_t,double,double)));
+  connect(ctrl.get(), SIGNAL(viewCtxMenuClosed()), swp, SLOT(onCtxMenuClosed()));
+  connect(ctrl.get(), SIGNAL(viewDrawIntegration(int,double,int,double,int,double,QString,QString,bool)), swp,
+            SLOT(onDrawIntegration(int,double,int,double,int,double,QString,QString,bool)));
+  connect(ctrl.get(), SIGNAL(viewUpdateCurrentValues(double,double)), swp, SLOT(onUpdateCurrentValues(double,double)));
+  connect(ctrl.get(), SIGNAL(viewRemoveCurrentValues()), swp, SLOT(onRemoveCurrentValues()));
+  emit addToDashboard(swp);
+
+  ctrl->draw();
+}
+
+/* Public slots */
+
+void DataManager::onLoadSequence(const QString& dir)
+{
+  QDir rootDir(dir);
+  QString uniqueName;
+  int nameSuffix = 1;
+  bool isUnique = false;
+  std::shared_ptr<Sequence> nSeq(new Sequence());
+
+  for (const QString& subPath : rootDir.entryList(DATA_DIR_SUFFIX, QDir::Dirs | QDir::NoDotAndDotDot)) {
+    QDir subDir(rootDir.absoluteFilePath(subPath));
+    std::shared_ptr<SingleRunData> srdata = loadSingleRun(subDir);
+    if (srdata == nullptr)
+      continue;
+
+    nSeq->add(subPath, srdata);
+  }
+  Logger::log(Logger::Level::DEBUG, ME_SENDER_STR, "Single runs loaded, count: " + QString::number(nSeq->count()));
+  if (nSeq->count() < 1) {
+    QMessageBox::warning(nullptr, "Data loading", "This directory does not appear to contain ChemStation sequence.");
+    return;
+  }
+
+  /* Generate unique name for the sequence */
+  uniqueName = rootDir.dirName();
+  while (!isUnique) {
+    for (const NameSequencePair& nsp : m_sequences) {
+      if (nsp.first.compare(uniqueName) == 0) {
+        if (nameSuffix == 1)
+          uniqueName += QString("_1");
+        else
+          uniqueName.replace(uniqueName.length()-1, 1, QString::number(nameSuffix));
+        nameSuffix++;
+      } else
+        isUnique = true;
+    }
+  }
+
+  /* Create signal controllers */
+  for (const SingleRunPair& srp : *nSeq) {
+    std::shared_ptr<SingleRunData> srdata = srp.second;
+    for (size_t idx = 0; idx < srdata->signalCount(); idx++) {
+      std::shared_ptr<SignalController> controller(new SignalController(srdata->signalAt(idx)));
+      nSeq->addController(signalControllerKey(srdata->dirName(), srdata->signalAt(idx)->uidString()), controller);
+    }
+    qDebug() << srp.first;
+  }
+
+  m_sequences.push_back(NameSequencePair(uniqueName, nSeq));
+  m_seqSelModel.seqenceAdded();
+
+  emit setActiveSequenceIndex(m_sequences.size()-1);
+}
+
+void DataManager::onLoadSingleRun(const QString& str)
+{
+  QString uniqueName;
+  int nameSuffix = 1;
+  std::shared_ptr<Sequence> srseq = m_sequences.at(0).second;
+  QDir dir(str);
+  std::shared_ptr<SingleRunData> srdata = loadSingleRun(dir);
+  if (srdata == nullptr)
+    return;
+
+  /* Generate unique name for the single run data */
+  uniqueName = dir.dirName();
+  while (srseq->find(uniqueName) != srseq->end()) {
+    Logger::log(Logger::Level::INFO, ME_SENDER_STR, "[" + uniqueName + "]: Already there, adjusting.");
+    if (nameSuffix == 1) {
+      uniqueName += QString("_1");
+    } else {
+      uniqueName.replace(uniqueName.length()-1, 1, QString::number(nameSuffix));
+    }
+    nameSuffix++;
+  }
+
+  /* Create signal controllers */
+  for (size_t idx = 0; idx < srdata->signalCount(); idx++) {
+    std::shared_ptr<SignalController> controller(new SignalController(srdata->signalAt(idx)));
+    srseq->addController(signalControllerKey(srdata->dirName(), srdata->signalAt(idx)->uidString()), controller);
+  }
+
+  srseq->add(uniqueName, srdata);
+  srseq->setSelectedRunKey(uniqueName);
+  emit setActiveSequenceIndex(0);
+}
+
+void DataManager::onSequenceSelected(const QString& key)
+{
+  if (m_sequenceRejected) {
+    m_sequenceRejected = false;
+    return;
+  }
+
+  m_activeSequence = sequenceByKey(key);
+  if (m_activeSequence == nullptr) {
+    Logger::log(Logger::Level::ERROR, ME_SENDER_STR, "[" + key + "]: No such sequence.");
+    m_activeSequence = sequenceByKey(m_prevSequenceKey);
+    m_sequenceRejected = true;
+    emit setActiveSequenceIndex(sequenceIdxByKey(m_prevSequenceKey));
+    return;
+  }
+
+  if (m_activeSequence->count() < 1) {
+    Logger::log(Logger::Level::ERROR, ME_SENDER_STR, "There are no runs in sequence [" + key + "].");
+    QMessageBox::information(nullptr, "Data manager", "Sequence '" + key + "' does not contain any runs.");
+    /* Revert to previous sequence */
+    m_activeSequence = sequenceByKey(m_prevSequenceKey);
+    m_sequenceRejected = true;
+    emit setActiveSequenceIndex(sequenceIdxByKey(m_prevSequenceKey));
+    return;
+  }
+
+  m_prevSequenceKey = key;
+  m_singleSelModel.setSingleRuns(m_activeSequence->allRuns());
+  emit setActiveSingleRunIndex(m_activeSequence->selectedRunIdx());
+}
+
+void DataManager::onSingleRunSelected(const QString& key)
+{
+  qDebug() << "SRKEY" << key;
+  if (m_activeSequence == nullptr)
+    return;
+
+  std::vector<SingleRunPair>::iterator it = m_activeSequence->find(key);
+  if (it == m_activeSequence->end()) {
+    Logger::log(Logger::Level::ERROR, ME_SENDER_STR, "[" + key + "]: No such single run.");
+    return;
+  }
+
+  m_activeSequence->setSelectedRunKey(key);
+
+  std::shared_ptr<SingleRunData> srdata = it->second;
+  emit cleanDashboard();
+  for (size_t idx = 0; idx < srdata->signalCount(); idx++) {
+    std::shared_ptr<SignalController> controller = m_activeSequence->controller(signalControllerKey(srdata->dirName(), srdata->signalAt(idx)->uidString()));
+    if (controller == nullptr) {
+      Logger::log(Logger::Level::ERROR, ME_SENDER_STR, "[" + srdata->dirName() + srdata->signalAt(idx)->uidString() + "]: No such controller.");
+      continue;
+    }
+    showOneSignal(controller);
+  }
+
+  emit setSingleRunInfo(srdata->methodName(), srdata->operatorName(), srdata->sampleInfo(), srdata->date().toString() +" "+ srdata->time().toString());
+  Logger::log(Logger::Level::DEBUG, ME_SENDER_STR, "Single run changed.");
+}
diff --git a/datamanager.h b/datamanager.h
new file mode 100644 (file)
index 0000000..bb7143e
--- /dev/null
@@ -0,0 +1,92 @@
+/*
+  Copyright (c) 2013 Michal Malý <madcatxster@prifuk.cz>
+
+  Permission is hereby granted, free of charge, to any person obtaining a copy
+  of this software and associated documentation files (the "Software"), to deal
+  in the Software without restriction, including without limitation the rights
+  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+  copies of the Software, and to permit persons to whom the Software is
+  furnished to do so, subject to the following conditions:
+
+  The above copyright notice and this permission notice shall be included in
+  all copies or substantial portions of the Software.
+
+  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+  THE SOFTWARE.
+*/
+
+#ifndef DATAMANAGER_H
+#define DATAMANAGER_H
+
+#include "datafilesloader.h"
+#include "gui/signalview.h"
+#include "sequence.h"
+#include "sequenceselectormodel.h"
+#include "singlerunsselectormodel.h"
+#include <exception>
+#include <QtCore/QDate>
+#include <QtCore/QObject>
+#include <QtCore/QTextCodec>
+#include <QtCore/QTime>
+#include "metatypes.h"
+
+
+class DataManager : public QObject
+{
+  Q_OBJECT
+public:
+  explicit DataManager(QObject* parent = nullptr);
+  SequenceSelectorModel* sequenceSelectorModel() { return &m_seqSelModel; }
+  SingleRunsSelectorModel* singleRunsSelectorModel() { return &m_singleSelModel; }
+
+private:
+  Signal::Equipment equipmentFromFiletype(AGRE_Filetype filetype);
+  std::shared_ptr<Sequence> sequenceByKey(const QString& key);
+  int sequenceIdxByKey(const QString& key);
+  std::shared_ptr<SingleRunData> loadSingleRun(QDir& dir);
+  Signal::Resource resourceFromFiletype(AGRE_Filetype filetype);
+  std::string signalControllerKey(const QString& main, const QString& resource) ;
+  void showOneSignal(std::shared_ptr<SignalController> ctrl);
+  Signal::YUnit yunitFromUnitStr(const std::string& unit_str);
+
+  std::shared_ptr<Sequence> m_activeSequence;
+  QString m_prevSequenceKey;
+  QString m_prevSingleRunKey;
+  SequenceSelectorModel m_seqSelModel;
+  bool m_sequenceRejected;
+  SingleRunsSelectorModel m_singleSelModel;
+  QTextCodec* m_sigNameCodec;
+  std::vector<NameSequencePair> m_sequences;
+  DataFilesLoader m_dfl;
+
+  static const QString ME_SENDER_STR;
+  static const char UNIT_KILOVOLTS_TEXT[];
+  static const char UNIT_MICROAMPERES_TEXT[];
+  static const char UNIT_MILLIAU_TEXT[];
+  static const char UNIT_MILLIVOLTS_TEXT[];
+  static const char UNIT_WATTS_TEXT[];
+
+  static const QStringList DATA_DIR_SUFFIX;
+
+signals:
+  void addToDashboard(SignalView* sw);
+  void cleanDashboard();
+  void setActiveSequenceIndex(const int idx);
+  void setActiveSingleRunIndex(const int idx);
+  void setSingleRunInfo(const QString& method, const QString& operatorname, const QString& sample, const QString& datetime);
+
+public slots:
+  void onLoadSequence(const QString& dir);
+  void onLoadSingleRun(const QString& dir);
+  void onSequenceSelected(const QString& key);
+  void onSingleRunSelected(const QString& key);
+
+
+};
+
+#endif // DATAMANAGER_H
diff --git a/globalinfo.cpp b/globalinfo.cpp
new file mode 100644 (file)
index 0000000..a198435
--- /dev/null
@@ -0,0 +1,27 @@
+/*
+  Copyright (c) 2013 Michal Malý <madcatxster@prifuk.cz>
+
+  Permission is hereby granted, free of charge, to any person obtaining a copy
+  of this software and associated documentation files (the "Software"), to deal
+  in the Software without restriction, including without limitation the rights
+  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+  copies of the Software, and to permit persons to whom the Software is
+  furnished to do so, subject to the following conditions:
+
+  The above copyright notice and this permission notice shall be included in
+  all copies or substantial portions of the Software.
+
+  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+  THE SOFTWARE.
+*/
+
+#include "globalinfo.h"
+
+const int GlobalInfo::APP_VERSION_MAJ(0);
+const int GlobalInfo::APP_VERSION_MIN(1);
+const QString GlobalInfo::APP_VERSION_REV("a");
diff --git a/globalinfo.h b/globalinfo.h
new file mode 100644 (file)
index 0000000..24e2fa4
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+  Copyright (c) 2013 Michal Malý <madcatxster@prifuk.cz>
+
+  Permission is hereby granted, free of charge, to any person obtaining a copy
+  of this software and associated documentation files (the "Software"), to deal
+  in the Software without restriction, including without limitation the rights
+  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+  copies of the Software, and to permit persons to whom the Software is
+  furnished to do so, subject to the following conditions:
+
+  The above copyright notice and this permission notice shall be included in
+  all copies or substantial portions of the Software.
+
+  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+  THE SOFTWARE.
+*/
+
+#ifndef GLOBALINFO_H
+#define GLOBALINFO_H
+
+#include <QtCore/QString>
+
+class GlobalInfo {
+private:
+  GlobalInfo(){}
+
+public:
+  static QString APP_VERSION_STRING() { return QString::number(GlobalInfo::APP_VERSION_MAJ) + "." + QString::number(GlobalInfo::APP_VERSION_MIN) +
+                                        GlobalInfo::APP_VERSION_REV; }
+
+  static const int APP_VERSION_MAJ;
+  static const int APP_VERSION_MIN;
+  static const QString APP_VERSION_REV;
+};
+
+#endif // GLOBALINFO_H
diff --git a/gui/aboutanyanka.cpp b/gui/aboutanyanka.cpp
new file mode 100644 (file)
index 0000000..d3bae24
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+  Copyright (c) 2013 Michal Malý <madcatxster@prifuk.cz>
+
+  Permission is hereby granted, free of charge, to any person obtaining a copy
+  of this software and associated documentation files (the "Software"), to deal
+  in the Software without restriction, including without limitation the rights
+  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+  copies of the Software, and to permit persons to whom the Software is
+  furnished to do so, subject to the following conditions:
+
+  The above copyright notice and this permission notice shall be included in
+  all copies or substantial portions of the Software.
+
+  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+  THE SOFTWARE.
+*/
+
+#include "aboutanyanka.h"
+#include "ui_aboutanyanka.h"
+
+#include <QDebug>
+
+AboutAnyanka::AboutAnyanka(QWidget *parent) :
+  QDialog(parent),
+  ui(new Ui::AboutAnyanka)
+{
+  ui->setupUi(this);
+  ui->ql_version->setText(GlobalInfo::APP_VERSION_STRING());
+  ui->ql_qtVersion->setText(qVersion());
+  ui->ql_qtLogo->setPixmap(QPixmap("://resources/Qt_master_logo.png").scaled(ui->ql_qtLogo->size(), Qt::KeepAspectRatio,
+                                                                             Qt::SmoothTransformation));
+}
+
+AboutAnyanka::~AboutAnyanka()
+{
+  delete ui;
+}
diff --git a/gui/aboutanyanka.h b/gui/aboutanyanka.h
new file mode 100644 (file)
index 0000000..8db8901
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+  Copyright (c) 2013 Michal Malý <madcatxster@prifuk.cz>
+
+  Permission is hereby granted, free of charge, to any person obtaining a copy
+  of this software and associated documentation files (the "Software"), to deal
+  in the Software without restriction, including without limitation the rights
+  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+  copies of the Software, and to permit persons to whom the Software is
+  furnished to do so, subject to the following conditions:
+
+  The above copyright notice and this permission notice shall be included in
+  all copies or substantial portions of the Software.
+
+  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+  THE SOFTWARE.
+*/
+
+#ifndef ABOUTANYANKA_H
+#define ABOUTANYANKA_H
+
+#include "globalinfo.h"
+#include <QtWidgets/QDialog>
+
+namespace Ui {
+  class AboutAnyanka;
+}
+
+class AboutAnyanka : public QDialog
+{
+  Q_OBJECT
+
+public:
+  explicit AboutAnyanka(QWidget* parent = nullptr);
+  ~AboutAnyanka();
+
+private:
+  Ui::AboutAnyanka *ui;
+};
+
+#endif // ABOUTANYANKA_H
diff --git a/gui/aboutanyanka.ui b/gui/aboutanyanka.ui
new file mode 100644 (file)
index 0000000..4bf4e57
--- /dev/null
@@ -0,0 +1,133 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>AboutAnyanka</class>
+ <widget class="QDialog" name="AboutAnyanka">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>363</width>
+    <height>313</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>Dialog</string>
+  </property>
+  <widget class="QLabel" name="ql_appName">
+   <property name="geometry">
+    <rect>
+     <x>115</x>
+     <y>13</y>
+     <width>121</width>
+     <height>41</height>
+    </rect>
+   </property>
+   <property name="font">
+    <font>
+     <pointsize>22</pointsize>
+     <weight>75</weight>
+     <bold>true</bold>
+    </font>
+   </property>
+   <property name="text">
+    <string>Anyanka</string>
+   </property>
+  </widget>
+  <widget class="QLabel" name="ql_versionCap">
+   <property name="geometry">
+    <rect>
+     <x>135</x>
+     <y>60</y>
+     <width>53</width>
+     <height>16</height>
+    </rect>
+   </property>
+   <property name="text">
+    <string>Version:</string>
+   </property>
+  </widget>
+  <widget class="QLabel" name="ql_version">
+   <property name="geometry">
+    <rect>
+     <x>185</x>
+     <y>60</y>
+     <width>41</width>
+     <height>16</height>
+    </rect>
+   </property>
+   <property name="text">
+    <string>-</string>
+   </property>
+   <property name="alignment">
+    <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
+   </property>
+  </widget>
+  <widget class="QLabel" name="ql_qtLogo">
+   <property name="geometry">
+    <rect>
+     <x>115</x>
+     <y>100</y>
+     <width>131</width>
+     <height>111</height>
+    </rect>
+   </property>
+   <property name="text">
+    <string>logo</string>
+   </property>
+   <property name="alignment">
+    <set>Qt::AlignCenter</set>
+   </property>
+  </widget>
+  <widget class="QLabel" name="ql_qtVersionCap">
+   <property name="geometry">
+    <rect>
+     <x>90</x>
+     <y>280</y>
+     <width>151</width>
+     <height>16</height>
+    </rect>
+   </property>
+   <property name="text">
+    <string>Using Qt 5 libraries version:</string>
+   </property>
+  </widget>
+  <widget class="QLabel" name="ql_qtVersion">
+   <property name="geometry">
+    <rect>
+     <x>244</x>
+     <y>280</y>
+     <width>41</width>
+     <height>16</height>
+    </rect>
+   </property>
+   <property name="text">
+    <string>-</string>
+   </property>
+   <property name="alignment">
+    <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
+   </property>
+  </widget>
+  <widget class="QLabel" name="ql_bunnies">
+   <property name="geometry">
+    <rect>
+     <x>62</x>
+     <y>240</y>
+     <width>240</width>
+     <height>21</height>
+    </rect>
+   </property>
+   <property name="font">
+    <font>
+     <pointsize>11</pointsize>
+     <weight>75</weight>
+     <bold>true</bold>
+    </font>
+   </property>
+   <property name="text">
+    <string>&quot;Fear nothing except for bunnies!&quot;</string>
+   </property>
+  </widget>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/gui/graphview.cpp b/gui/graphview.cpp
new file mode 100644 (file)
index 0000000..262b74b
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+  Copyright (c) 2013 Michal Malý <madcatxster@prifuk.cz>
+
+  Permission is hereby granted, free of charge, to any person obtaining a copy
+  of this software and associated documentation files (the "Software"), to deal
+  in the Software without restriction, including without limitation the rights
+  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+  copies of the Software, and to permit persons to whom the Software is
+  furnished to do so, subject to the following conditions:
+
+  The above copyright notice and this permission notice shall be included in
+  all copies or substantial portions of the Software.
+
+  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+  THE SOFTWARE.
+*/
+
+#include "gui/graphview.h"
+
+GraphView::GraphView(QWidget *parent) :
+  QLabel(parent)
+{
+  setMouseTracking(true);
+}
+
+void GraphView::leaveEvent(QEvent *)
+{
+  emit mouseCursorLeft();
+}
+
+void GraphView::mouseMoveEvent(QMouseEvent* ev)
+{
+  emit mouseMoved(ev->x(), ev->y());
+}
+
+void GraphView::mousePressEvent(QMouseEvent* ev)
+{
+  emit mouseButtonTriggered(ev->x(), ev->y(), ev->button(), ev->type(), ev->globalPos());
+}
+
+void GraphView::resizeEvent(QResizeEvent* ev)
+{
+  if (ev->size().width() > 1 && ev->size().height() > 1)
+    emit redrawNeeded();
+}
diff --git a/gui/graphview.h b/gui/graphview.h
new file mode 100644 (file)
index 0000000..895b420
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+  Copyright (c) 2013 Michal Malý <madcatxster@prifuk.cz>
+
+  Permission is hereby granted, free of charge, to any person obtaining a copy
+  of this software and associated documentation files (the "Software"), to deal
+  in the Software without restriction, including without limitation the rights
+  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+  copies of the Software, and to permit persons to whom the Software is
+  furnished to do so, subject to the following conditions:
+
+  The above copyright notice and this permission notice shall be included in
+  all copies or substantial portions of the Software.
+
+  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+  THE SOFTWARE.
+*/
+
+#ifndef GRAPHVIEW_H
+#define GRAPHVIEW_H
+
+#include <QtGui/QMouseEvent>
+#include <QtWidgets/QLabel>
+
+class GraphView : public QLabel
+{
+  Q_OBJECT
+public:
+  explicit GraphView(QWidget* parent = nullptr);
+  void leaveEvent(QEvent* );
+  void mouseMoveEvent(QMouseEvent* ev);
+  void mousePressEvent(QMouseEvent* ev);
+
+private:
+  void resizeEvent(QResizeEvent* ev);
+
+public slots:
+
+signals:
+  void mouseCursorLeft();
+  void mouseMoved(const int x, const int y);
+  void mouseButtonTriggered(const int x, const int y, Qt::MouseButton button, QEvent::Type type, const QPoint& globalPos);
+  void redrawNeeded();
+};
+
+#endif // GRAPHVIEW_H
diff --git a/gui/graphviewcontextmenu.cpp b/gui/graphviewcontextmenu.cpp
new file mode 100644 (file)
index 0000000..84552ce
--- /dev/null
@@ -0,0 +1,31 @@
+/*
+  Copyright (c) 2013 Michal Malý <madcatxster@prifuk.cz>
+
+  Permission is hereby granted, free of charge, to any person obtaining a copy
+  of this software and associated documentation files (the "Software"), to deal
+  in the Software without restriction, including without limitation the rights
+  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+  copies of the Software, and to permit persons to whom the Software is
+  furnished to do so, subject to the following conditions:
+
+  The above copyright notice and this permission notice shall be included in
+  all copies or substantial portions of the Software.
+
+  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+  THE SOFTWARE.
+*/
+
+#include "graphviewcontextmenu.h"
+
+GraphViewContextMenu::GraphViewContextMenu(QWidget* parent) :
+  QMenu(parent)
+{
+  m_acZoomOut = addAction("Zoom out");
+  addSeparator();
+  m_acDeletePeak = addAction("Delete peak");
+}
diff --git a/gui/graphviewcontextmenu.h b/gui/graphviewcontextmenu.h
new file mode 100644 (file)
index 0000000..1896f1c
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+  Copyright (c) 2013 Michal Malý <madcatxster@prifuk.cz>
+
+  Permission is hereby granted, free of charge, to any person obtaining a copy
+  of this software and associated documentation files (the "Software"), to deal
+  in the Software without restriction, including without limitation the rights
+  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+  copies of the Software, and to permit persons to whom the Software is
+  furnished to do so, subject to the following conditions:
+
+  The above copyright notice and this permission notice shall be included in
+  all copies or substantial portions of the Software.
+
+  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+  THE SOFTWARE.
+*/
+
+
+#ifndef GRAPHVIEWCONTEXTMENU_H
+#define GRAPHVIEWCONTEXTMENU_H
+
+#include <QtWidgets/QMenu>
+
+class GraphViewContextMenu : public QMenu
+{
+  Q_OBJECT
+public:
+  explicit GraphViewContextMenu(QWidget* parent = nullptr);
+
+  QAction* m_acDeletePeak;
+  QAction* m_acZoomOut;
+
+private:
+
+signals:
+
+public slots:
+
+};
+
+#endif // GRAPHVIEWCONTEXTMENU_H
diff --git a/gui/mainwindow.cpp b/gui/mainwindow.cpp
new file mode 100644 (file)
index 0000000..bc11e60
--- /dev/null
@@ -0,0 +1,159 @@
+/*
+  Copyright (c) 2013 Michal Malý <madcatxster@prifuk.cz>
+
+  Permission is hereby granted, free of charge, to any person obtaining a copy
+  of this software and associated documentation files (the "Software"), to deal
+  in the Software without restriction, including without limitation the rights
+  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+  copies of the Software, and to permit persons to whom the Software is
+  furnished to do so, subject to the following conditions:
+
+  The above copyright notice and this permission notice shall be included in
+  all copies or substantial portions of the Software.
+
+  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+  THE SOFTWARE.
+*/
+
+
+#include "gui/mainwindow.h"
+#include "ui_mainwindow.h"
+#include <QVBoxLayout>
+
+#include <QDebug>
+
+MainWindow::MainWindow(QWidget *parent) :
+  QMainWindow(parent),
+  ui(new Ui::MainWindow)
+{
+  ui->setupUi(this);
+  m_dashboard = new QSplitter(Qt::Vertical, this);
+  m_dashboard->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Expanding);
+  ui->qsca_dataView->setWidget(m_dashboard);
+
+  connectActions();
+
+  m_loadDataFileDialog = new QFileDialog(this);
+  m_loadDataFileDialog->setFileMode(QFileDialog::Directory);
+  m_loadDataFileDialog->setOptions(QFileDialog::ReadOnly);
+}
+
+/* Public methods */
+void MainWindow::setSequenceListModel(SequenceSelectorModel* model)
+{
+  ui->qcbox_sequence->setModel(model);
+}
+
+void MainWindow::setSingleRunsListModel(SingleRunsSelectorModel* model)
+{
+  ui->qcbox_singleRun->setModel(model);
+}
+
+/* Private methods */
+
+#ifdef Q_OS_UNIX
+void MainWindow::connectActions() noexcept
+#elif defined Q_OS_WIN
+void MainWindow::connectActions()
+#endif
+{
+  /* Controls panel */
+  connect(ui->qpb_integrate, SIGNAL(pressed()), this, SLOT(onIntegrateSelected()));
+  connect(ui->qpb_zoom, SIGNAL(pressed()), this, SLOT(onZoomSelected()));
+  connect(ui->actionLoad_single_run, SIGNAL(triggered()), this, SLOT(onLoadSingleRun()));
+  connect(ui->actionLoad_sequence, SIGNAL(triggered()), this, SLOT(onLoadSequence()));
+  connect(ui->actionAbout, SIGNAL(triggered()), this, SLOT(onAboutAnyanka()));
+  connect(ui->qcbox_sequence, SIGNAL(currentIndexChanged(QString)), this, SLOT(onSequenceSelected(QString)));
+  connect(ui->qcbox_singleRun, SIGNAL(currentIndexChanged(QString)), this, SLOT(onSingleRunSelected(QString)));
+}
+
+/* Public slots */
+void MainWindow::onAddToDashboard(SignalView* sw)
+{
+  if (ui->qpb_integrate->isChecked())
+    sw->setControlMode(GraphControlModes::INTEGRATE);
+  else if(ui->qpb_zoom->isChecked())
+    sw->setControlMode(GraphControlModes::ZOOM);
+
+  //m_itemsOnDashboard.push_back(sw);
+  connect(this, SIGNAL(controlModeChanged(GraphControlModes)), sw, SLOT(onControlModeChanged(GraphControlModes)));
+  m_dashboard->addWidget(sw);
+}
+
+void MainWindow::onCleanDashboard()
+{
+  while (m_dashboard->count() > 0)
+    delete m_dashboard->widget(0);
+}
+
+void MainWindow::onSetActiveSequenceIndex(const int idx)
+{
+  if (ui->qcbox_sequence->currentIndex() == idx)
+    emit onSequenceSelected(ui->qcbox_sequence->currentText());
+  else
+    ui->qcbox_sequence->setCurrentIndex(idx);
+}
+
+void MainWindow::onSetActiveSingleRunIndex(const int idx)
+{
+  ui->qcbox_singleRun->setCurrentIndex(idx);
+}
+
+void MainWindow::onSetSingleRunInfo(const QString &method, const QString &operatorname, const QString &sample, const QString &datetime)
+{
+  ui->qle_methodName->setText(method);
+  ui->qle_operatorName->setText(operatorname);
+  ui->qle_sampleInfo->setText(sample);
+  ui->qle_dateTime->setText(datetime);
+}
+
+/* Private slots */
+void MainWindow::onAboutAnyanka()
+{
+  AboutAnyanka aa(this);
+  aa.exec();
+}
+
+void MainWindow::onIntegrateSelected()
+{
+  emit controlModeChanged(GraphControlModes::INTEGRATE);
+}
+void MainWindow::onLoadSequence()
+{
+  if (m_loadDataFileDialog->exec() == QDialog::Accepted)
+    emit loadSequence(m_loadDataFileDialog->selectedFiles()[0]);
+}
+
+void MainWindow::onLoadSingleRun()
+{
+  if (m_loadDataFileDialog->exec() == QDialog::Accepted) {
+    qDebug() << "Chosen file" << m_loadDataFileDialog->selectedFiles();
+    emit loadSingleRun(m_loadDataFileDialog->selectedFiles()[0]);
+  }
+}
+
+void MainWindow::onZoomSelected()
+{
+  emit controlModeChanged(GraphControlModes::ZOOM);
+}
+
+/* Private slots */
+void MainWindow::onSequenceSelected(const QString &str)
+{
+  emit sequenceSelected(str);
+}
+
+void MainWindow::onSingleRunSelected(const QString &str)
+{
+  emit singleRunSelected(str);
+}
+
+MainWindow::~MainWindow()
+{
+  delete ui;
+}
diff --git a/gui/mainwindow.h b/gui/mainwindow.h
new file mode 100644 (file)
index 0000000..5dc82ed
--- /dev/null
@@ -0,0 +1,86 @@
+/*
+  Copyright (c) 2013 Michal Malý <madcatxster@prifuk.cz>
+
+  Permission is hereby granted, free of charge, to any person obtaining a copy
+  of this software and associated documentation files (the "Software"), to deal
+  in the Software without restriction, including without limitation the rights
+  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+  copies of the Software, and to permit persons to whom the Software is
+  furnished to do so, subject to the following conditions:
+
+  The above copyright notice and this permission notice shall be included in
+  all copies or substantial portions of the Software.
+
+  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+  THE SOFTWARE.
+*/
+
+#ifndef MAINWINDOW_H
+#define MAINWINDOW_H
+
+#include "aboutanyanka.h"
+#include "sequenceselectormodel.h"
+#include "singlerunsselectormodel.h"
+#include "signalview.h"
+#include <memory>
+#include <QtWidgets/QFileDialog>
+#include <QtWidgets/QMainWindow>
+#include <QtWidgets/QSplitter>
+#include "metatypes.h"
+
+namespace Ui {
+  class MainWindow;
+}
+
+class MainWindow : public QMainWindow
+{
+  Q_OBJECT
+
+public:
+  explicit MainWindow(QWidget* parent = 0);
+  ~MainWindow();
+  void setSequenceListModel(SequenceSelectorModel* model);
+  void setSingleRunsListModel(SingleRunsSelectorModel* model);
+
+private:
+#ifdef Q_OS_UNIX
+  void connectActions() noexcept;
+#elif defined Q_OS_WIN
+  void connectActions();
+#endif
+
+  //std::vector<SignalView*> m_itemsOnDashboard;
+  QSplitter* m_dashboard;
+  QFileDialog* m_loadDataFileDialog;
+  Ui::MainWindow *ui;
+
+public slots:
+  void onAddToDashboard(SignalView* sw);
+  void onCleanDashboard();
+  void onSetActiveSequenceIndex(const int idx);
+  void onSetActiveSingleRunIndex(const int idx);
+  void onSetSingleRunInfo(const QString& method, const QString& operatorname, const QString& sample, const QString& datetime);
+
+private slots:
+  void onAboutAnyanka();
+  void onIntegrateSelected();
+  void onLoadSequence();
+  void onLoadSingleRun();
+  void onSequenceSelected(const QString& str);
+  void onSingleRunSelected(const QString& str);
+  void onZoomSelected();
+
+signals:
+  void controlModeChanged(GraphControlModes mode);
+  void loadSequence(const QString& dir);
+  void loadSingleRun(const QString& dir);
+  void sequenceSelected(const QString& str);
+  void singleRunSelected(const QString& str);
+};
+
+#endif // MAINWINDOW_H
diff --git a/gui/mainwindow.ui b/gui/mainwindow.ui
new file mode 100644 (file)
index 0000000..0dcb07f
--- /dev/null
@@ -0,0 +1,251 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>MainWindow</class>
+ <widget class="QMainWindow" name="MainWindow">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>800</width>
+    <height>600</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>Anyanka</string>
+  </property>
+  <widget class="QWidget" name="centralwidget">
+   <layout class="QVBoxLayout" name="verticalLayout">
+    <item>
+     <layout class="QGridLayout" name="gridLayout">
+      <item row="0" column="0">
+       <widget class="QLabel" name="ql_sequence">
+        <property name="text">
+         <string>Sequence:</string>
+        </property>
+       </widget>
+      </item>
+      <item row="1" column="0">
+       <widget class="QLabel" name="ql_singleRun">
+        <property name="text">
+         <string>Run:</string>
+        </property>
+       </widget>
+      </item>
+      <item row="0" column="1">
+       <widget class="QComboBox" name="qcbox_sequence"/>
+      </item>
+      <item row="1" column="1">
+       <widget class="QComboBox" name="qcbox_singleRun"/>
+      </item>
+     </layout>
+    </item>
+    <item>
+     <widget class="QGroupBox" name="qgbox_controls">
+      <property name="enabled">
+       <bool>true</bool>
+      </property>
+      <property name="title">
+       <string/>
+      </property>
+      <property name="flat">
+       <bool>false</bool>
+      </property>
+      <layout class="QGridLayout" name="gridLayout_3">
+       <item row="0" column="0">
+        <widget class="QPushButton" name="qpb_zoom">
+         <property name="text">
+          <string>Zoom</string>
+         </property>
+         <property name="checkable">
+          <bool>true</bool>
+         </property>
+         <property name="checked">
+          <bool>true</bool>
+         </property>
+         <property name="autoExclusive">
+          <bool>true</bool>
+         </property>
+        </widget>
+       </item>
+       <item row="0" column="1">
+        <widget class="QPushButton" name="qpb_integrate">
+         <property name="text">
+          <string>Integrate</string>
+         </property>
+         <property name="checkable">
+          <bool>true</bool>
+         </property>
+         <property name="autoExclusive">
+          <bool>true</bool>
+         </property>
+        </widget>
+       </item>
+      </layout>
+     </widget>
+    </item>
+    <item>
+     <widget class="QFrame" name="qf_singleRunInfo">
+      <property name="frameShape">
+       <enum>QFrame::StyledPanel</enum>
+      </property>
+      <property name="frameShadow">
+       <enum>QFrame::Raised</enum>
+      </property>
+      <layout class="QGridLayout" name="gridLayout_2">
+       <item row="1" column="0">
+        <widget class="QLabel" name="ql_sampleInfo">
+         <property name="text">
+          <string>Sample info:</string>
+         </property>
+        </widget>
+       </item>
+       <item row="0" column="3">
+        <widget class="QLabel" name="ql_operatorName">
+         <property name="text">
+          <string>Operator:</string>
+         </property>
+        </widget>
+       </item>
+       <item row="0" column="0">
+        <widget class="QLabel" name="ql_methodName">
+         <property name="text">
+          <string>Method name:</string>
+         </property>
+        </widget>
+       </item>
+       <item row="0" column="4">
+        <widget class="QLineEdit" name="qle_operatorName">
+         <property name="alignment">
+          <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+         </property>
+         <property name="readOnly">
+          <bool>true</bool>
+         </property>
+        </widget>
+       </item>
+       <item row="0" column="2">
+        <widget class="QLineEdit" name="qle_methodName">
+         <property name="alignment">
+          <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+         </property>
+         <property name="readOnly">
+          <bool>true</bool>
+         </property>
+        </widget>
+       </item>
+       <item row="1" column="2">
+        <widget class="QLineEdit" name="qle_sampleInfo">
+         <property name="sizePolicy">
+          <sizepolicy hsizetype="Expanding" vsizetype="Fixed">
+           <horstretch>0</horstretch>
+           <verstretch>0</verstretch>
+          </sizepolicy>
+         </property>
+         <property name="alignment">
+          <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+         </property>
+         <property name="readOnly">
+          <bool>true</bool>
+         </property>
+        </widget>
+       </item>
+       <item row="1" column="3">
+        <widget class="QLabel" name="ql_dateTime">
+         <property name="text">
+          <string>Date/Time:</string>
+         </property>
+        </widget>
+       </item>
+       <item row="1" column="4">
+        <widget class="QLineEdit" name="qle_dateTime">
+         <property name="alignment">
+          <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+         </property>
+         <property name="readOnly">
+          <bool>true</bool>
+         </property>
+        </widget>
+       </item>
+      </layout>
+     </widget>
+    </item>
+    <item>
+     <widget class="QScrollArea" name="qsca_dataView">
+      <property name="sizePolicy">
+       <sizepolicy hsizetype="Preferred" vsizetype="Expanding">
+        <horstretch>0</horstretch>
+        <verstretch>0</verstretch>
+       </sizepolicy>
+      </property>
+      <property name="widgetResizable">
+       <bool>true</bool>
+      </property>
+      <widget class="QWidget" name="scrollAreaWidgetContents">
+       <property name="geometry">
+        <rect>
+         <x>0</x>
+         <y>0</y>
+         <width>780</width>
+         <height>336</height>
+        </rect>
+       </property>
+       <property name="sizePolicy">
+        <sizepolicy hsizetype="Preferred" vsizetype="Expanding">
+         <horstretch>0</horstretch>
+         <verstretch>0</verstretch>
+        </sizepolicy>
+       </property>
+      </widget>
+     </widget>
+    </item>
+   </layout>
+  </widget>
+  <widget class="QMenuBar" name="menubar">
+   <property name="geometry">
+    <rect>
+     <x>0</x>
+     <y>0</y>
+     <width>800</width>
+     <height>19</height>
+    </rect>
+   </property>
+   <widget class="QMenu" name="menuData">
+    <property name="title">
+     <string>Data</string>
+    </property>
+    <addaction name="actionLoad_single_run"/>
+    <addaction name="actionLoad_sequence"/>
+   </widget>
+   <widget class="QMenu" name="menuHelp">
+    <property name="title">
+     <string>Help</string>
+    </property>
+    <addaction name="actionAbout"/>
+   </widget>
+   <addaction name="menuData"/>
+   <addaction name="menuHelp"/>
+  </widget>
+  <widget class="QStatusBar" name="statusbar">
+   <property name="sizeGripEnabled">
+    <bool>true</bool>
+   </property>
+  </widget>
+  <action name="actionLoad_single_run">
+   <property name="text">
+    <string>Load single run</string>
+   </property>
+  </action>
+  <action name="actionLoad_sequence">
+   <property name="text">
+    <string>Load sequence</string>
+   </property>
+  </action>
+  <action name="actionAbout">
+   <property name="text">
+    <string>About</string>
+   </property>
+  </action>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/gui/signalview.cpp b/gui/signalview.cpp
new file mode 100644 (file)
index 0000000..6dcd17e
--- /dev/null
@@ -0,0 +1,547 @@
+/*
+  Copyright (c) 2013 Michal Malý <madcatxster@prifuk.cz>
+
+  Permission is hereby granted, free of charge, to any person obtaining a copy
+  of this software and associated documentation files (the "Software"), to deal
+  in the Software without restriction, including without limitation the rights
+  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+  copies of the Software, and to permit persons to whom the Software is
+  furnished to do so, subject to the following conditions:
+
+  The above copyright notice and this permission notice shall be included in
+  all copies or substantial portions of the Software.
+
+  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+  THE SOFTWARE.
+*/
+
+
+#include "gui/signalview.h"
+#include "ui_signalview.h"
+#include "mathhelpers.h"
+#include <cmath>
+#include <QtGui/QFontMetrics>
+#include <QtGui/QPainter>
+
+#include <QDebug>
+
+SignalView::SignalView(QWidget *parent) :
+  QWidget(parent),
+  m_graph(nullptr),
+  m_graphPlain(nullptr),
+  m_graphData(nullptr),
+  m_ctxMenuOpen(false),
+  m_mouseMode(SignalView::MouseMode::CROSSHAIR),
+  ui(new Ui::SignalView)
+{
+  ui->setupUi(this);
+  m_graphPainter = new QPainter();
+  m_graphCrosshairX = -1;
+  m_graphCrosshairY = -1;
+  m_integrateStartX = -1;
+
+  connect(ui->qw_graphView, SIGNAL(mouseCursorLeft()), this, SLOT(onGraphViewMouseCursorLeft()));
+  connect(ui->qw_graphView, SIGNAL(mouseMoved(int,int)), this, SLOT(onGraphViewMouseMoved(int,int)));
+  connect(ui->qw_graphView, SIGNAL(mouseButtonTriggered(int,int,Qt::MouseButton,QEvent::Type,QPoint)), this,
+          SLOT(onMouseButtonTriggered(int,int,Qt::MouseButton,QEvent::Type,QPoint)));
+  connect(ui->qw_graphView, SIGNAL(redrawNeeded()), this, SLOT(onGraphRedrawNeeded()));
+}
+
+/* Public functions */
+void SignalView::setControlMode(GraphControlModes mode)
+{
+  m_graphCtrlMode = mode;
+}
+
+void SignalView::setDataTableModel(SignalDataTableModel* model)
+{
+  ui->qtblv_signalDataTable->setModel(model);
+}
+
+void SignalView::setIntegrationTableModel(IntegrationTableModel *model)
+{
+  ui->qtblv_integrationTable->setModel(model);
+}
+
+void SignalView::setTypeText(const QString &text)
+{
+  ui->ql_signalType->setText(text);
+}
+
+void SignalView::setXUnits(const QString &text)
+{
+  ui->ql_xUnits->setText(text);
+}
+
+void SignalView::setYUnits(const QString &text)
+{
+  ui->ql_yUnits->setText(text);
+}
+
+/* Private functions */
+
+void SignalView::drawCrosshair(const int x, const int y)
+{
+  int w, h;
+  if (m_graph == nullptr)
+    return;
+  w = ui->qw_graphView->width();
+  h = ui->qw_graphView->height();
+
+  m_graphPainter->begin(m_graph);
+  m_graphPainter->setPen(Qt::black);
+  /* Draw new crosshair */
+  m_graphPainter->drawLine(0, y, w, y); /* Horizontal */
+  m_graphPainter->drawLine(x, 0, x, h); /* Vertical */
+  m_graphPainter->end();
+
+  m_graphCrosshairX = x;
+  m_graphCrosshairY = y;
+
+  ui->qw_graphView->setPixmap(*m_graph);
+
+}
+
+void SignalView::drawIntegrationBaseline(const int x, const int y)
+{
+  if (m_graph == nullptr)
+    return;
+  m_graphPainter->begin(m_graph);
+  /* Draw new line */
+  m_graphPainter->setPen(Qt::black);
+  m_graphPainter->drawLine(m_integrateStartX, m_integrateStartY, x, y);
+  m_graphPainter->end();
+
+  m_integrateStopX = x;
+  m_integrateStopY = y;
+
+  ui->qw_graphView->setPixmap(*m_graph);
+}
+
+void SignalView::drawZoomRect(const int x, const int y)
+{
+  int startX, startY, stopX, stopY;
+  if (m_graph == nullptr)
+    return;
+
+  if (x < m_zoomRectStartX) {
+    startX = x;
+    stopX = m_zoomRectStartX;
+  } else {
+    startX = m_zoomRectStartX;
+    stopX = x;
+  }
+  if (y < m_zoomRectStartY) {
+    startY = y;
+    stopY = m_zoomRectStartY;
+  } else {
+    startY = m_zoomRectStartY;
+    stopY = y;
+  }
+
+  eraseZoomRect();
+
+  m_graphPainter->begin(m_graph);
+  m_graphPainter->setPen(Qt::green);
+  // Top line
+  m_graphPainter->drawLine(startX, startY, stopX, startY);
+  // Left line
+  m_graphPainter->drawLine(startX, startY, startX, stopY);
+  // Right line
+  m_graphPainter->drawLine(stopX, startY, stopX, stopY);
+  // Bottom line
+  m_graphPainter->drawLine(startX, stopY, stopX, stopY);
+
+  m_zoomRectLastX = x;
+  m_zoomRectLastY = y;
+
+  m_graphPainter->end();
+
+  ui->qw_graphView->setPixmap(*m_graph);
+}
+
+void SignalView::eraseCrosshair(bool apply)
+{
+  int w, h;
+  if (m_graph == nullptr)
+    return;
+  w = ui->qw_graphView->width();
+  h = ui->qw_graphView->height();
+
+  if (m_graphCrosshairX != -1 || m_graphCrosshairY != -1) {
+      m_graphPainter->begin(m_graph);
+      m_graphPainter->drawPixmap(m_graphCrosshairX, 0, *m_graphPlain, m_graphCrosshairX, 0, 1, h);
+      m_graphPainter->drawPixmap(0, m_graphCrosshairY, *m_graphPlain, 0, m_graphCrosshairY, w, 1);
+      m_graphPainter->end();
+      if (apply)
+        ui->qw_graphView->setPixmap(*m_graph);
+  }
+}
+
+void SignalView::eraseIntegrationBaseline(bool apply)
+{
+  int lstartX, lstartY, lstopX, lstopY;
+  int w, h;
+
+  if (m_graph == nullptr)
+    return;
+  if (m_integrateStartX < 0)
+    return;
+
+  w = ui->qw_graphView->width();
+  h = ui->qw_graphView->height();
+
+  if (m_integrateStartX > m_integrateStopX) {
+    lstartX = (m_integrateStopX > 1) ? m_integrateStopX - 1 : 0;
+    lstopX = (m_integrateStartX < w-1) ? m_integrateStartX + 1 : w-1;
+  } else {
+    lstartX = (m_integrateStartX > 1) ? m_integrateStartX -1 : 0;
+    lstopX = (m_integrateStopX < w-1) ? m_integrateStopX + 1 : w-1;
+  }
+  if (m_integrateStartY > m_integrateStopY) {
+    lstartY = (m_integrateStopY > 1) ? m_integrateStopY - 1 : 0;
+    lstopY = (m_integrateStartY < h-1) ? m_integrateStartY + 1 : h-1;
+  } else {
+    lstartY = (m_integrateStartY > 1) ? m_integrateStartY - 1 : 0;
+    lstopY = (m_integrateStopY < h-1) ? m_integrateStopY + 1 : h-1;
+  }
+
+  m_graphPainter->begin(m_graph);
+  m_graphPainter->drawPixmap(lstartX, lstartY, *m_graphPlain, lstartX, lstartY, lstopX - lstartX+1, lstopY - lstartY+1); //FIXME: +1 should not be needed!
+  m_graphPainter->end();
+
+  if (apply)
+    ui->qw_graphView->setPixmap(*m_graph);
+}
+
+void SignalView::eraseZoomRect(bool apply)
+{
+  int lstartX, lstartY, lstopX, lstopY;
+  if (m_graph == nullptr)
+    return;
+
+  if (m_zoomRectLastX < m_zoomRectStartX) {
+    lstartX = m_zoomRectLastX;
+    lstopX = m_zoomRectStartX;
+  } else {
+    lstartX = m_zoomRectStartX;
+    lstopX = m_zoomRectLastX;
+  }
+  if (m_zoomRectLastY < m_zoomRectStartY) {
+    lstartY = m_zoomRectLastY;
+    lstopY = m_zoomRectStartY;
+  } else {
+    lstartY = m_zoomRectStartY;
+    lstopY = m_zoomRectLastY;
+  }
+
+  m_graphPainter->begin(m_graph);
+  /* Erase any existing lines */
+  // TODO: It might not be necessary to erase all lines every time - OPTIMIZE THIS !!!
+  // Top line
+  m_graphPainter->drawPixmap(lstartX, lstartY, *m_graphPlain, lstartX, lstartY, lstopX - lstartX, 1);
+  // Left line
+  m_graphPainter->drawPixmap(lstartX, lstartY, *m_graphPlain, lstartX, lstartY, 1, lstopY - lstartY);
+  // Right line
+  m_graphPainter->drawPixmap(lstopX, lstartY, *m_graphPlain, lstopX, lstartY, 1, lstopY - lstartY + 1);
+  // Bottom line
+  m_graphPainter->drawPixmap(lstartX, lstopY, *m_graphPlain, lstartX, lstopY, lstopX - lstartX + 1, 1);
+  m_graphPainter->end();
+
+  if (apply)
+    ui->qw_graphView->setPixmap(*m_graph);
+}
+
+void SignalView::redrawGraph()
+{
+  int w = ui->qw_graphView->width();
+  int h = ui->qw_graphView->height();
+  uint lastColumn = 0;
+  double yAvg = 0;
+  double yStep;
+  QPixmap* fresh = new QPixmap(w, h);
+
+  if (m_graphData == nullptr)
+    return;
+  if (w < 1 || h < 1)
+    return;
+
+  fresh->fill(Qt::white);
+  m_graphPainter->begin(fresh);
+  m_graphPainter->setPen(QColor(Qt::blue));
+
+  /* Scaling */
+  yStep = static_cast<double>(h) / fabs(m_graphMax - m_graphMin);
+
+  /* Downscale to grid */
+  if (w < m_graphLen) {
+    double columnsPerPixel = static_cast<double>(m_graphLen) / w;
+    uint columnsPerLastPixel = 0;
+    int* block = new int[w];
+    //qDebug() << "CPP" << columnsPerPixel << "yStep" << yStep << "Dims" << h << w << "Data" << w*columnsPerPixel << m_graphLen;
+    for (uint i = 0; i < m_graphLen; i++) {
+      uint column = i / columnsPerPixel;
+      if (column != lastColumn) {
+        yAvg /= columnsPerLastPixel;
+        block[lastColumn] = h - yStep * (yAvg -m_graphMin);
+        //qDebug() << "YAVG" << block[lastColumn] << "CPLP" << columnsPerLastPixel;
+        yAvg = 0;
+        columnsPerLastPixel= 0;
+        lastColumn = column;
+      }
+      yAvg += m_graphData[i];
+      columnsPerLastPixel++;
+    }
+    /* Add the last column */
+    yAvg /= columnsPerLastPixel;
+    block[w-1] = h - yStep * (yAvg - m_graphMin);
+    /* Draw the pixmap */
+    for (int i = 1; i < w; i++)
+      m_graphPainter->drawLine(i-1, floor(block[i-1]+0.5), i, floor(block[i]+0.5));
+    //qDebug() << "LAST YAVG" << block[m_graphLen-1] << "CPLP" << columnsPerLastPixel;
+    delete[] block;
+  } else { /* Upscale to grid */
+    double pixelsPerValue = static_cast<double>(w) / m_graphLen;
+    uint cPixX;
+    uint pPixX = 0;
+    uint cPixY;
+    uint pPixY = floor((h - yStep * (m_graphData[0] - m_graphMin)) + 0.5);
+    for (int i = 1; i < m_graphLen; i++) {
+      cPixX= floor(i * pixelsPerValue + 0.5);
+      cPixY = h - yStep * (m_graphData[i] - m_graphMin);
+      //qDebug() << "Upscale" << pPixX << pPixY << cPixX << cPixY;
+      m_graphPainter->drawLine(pPixX, pPixY, cPixX, cPixY);
+      pPixX = cPixX;
+      pPixY = cPixY;
+    }
+  }
+
+  m_graphPainter->end();
+  if (m_graphPlain != nullptr)
+    delete m_graphPlain;
+  m_graphPlain = new QPixmap(*fresh);
+  ui->qw_graphView->setPixmap(*fresh);
+  if (m_graph != nullptr)
+    delete m_graph;
+  m_graph = fresh;
+}
+
+void SignalView::resizeEvent(QResizeEvent* ev)
+{
+  Q_UNUSED(ev);
+
+  emit resized(ui->qw_graphView->width(), ui->qw_graphView->height());
+  emit redrawNeeded();
+}
+
+void SignalView::onUpdateCurrentValues(double x, double y)
+{
+  ui->qle_xValue->setText(QString::number(x));
+  ui->qle_yValue->setText(QString::number(y));
+}
+
+void SignalView::onRemoveCurrentValues()
+{
+  ui->qle_xValue->setText("-");
+  ui->qle_yValue->setText("-");
+}
+
+/* Public slots */
+void SignalView::onControlModeChanged(GraphControlModes mode)
+{
+  m_graphCtrlMode = mode;
+}
+
+void SignalView::onDrawIntegration(const int fromX, const double fromY, const int toX, const double toY, const int peakX, const double peakY, const QString& time,
+                                   const QString& area, const bool valley)
+{
+  int w, h;
+  int startX, startY, stopX, stopY, peakStartX, peakStartY;
+  double yStep, xStep;
+  double dStartY, dStopY;
+  QFont font("arial", 10);
+  QFontMetrics fm(font);
+  int textWidth;
+  if (m_graphPlain == nullptr)
+    return;
+
+  w = ui->qw_graphView->width();
+  h = ui->qw_graphView->height();
+  xStep = static_cast<double>(w) / m_graphLen;
+  yStep = static_cast<double>(h) / (m_graphMax - m_graphMin);
+  startX = floor(fromX * xStep + 0.5);
+  stopX = floor(toX * xStep + 0.5);
+  startY = floor((h - yStep * (fromY - m_graphMin)) + 0.5);
+  stopY = floor((h - yStep * (toY - m_graphMin)) + 0.5);
+  peakStartX = floor(peakX * xStep + 0.5);
+  peakStartY = floor(h - yStep * (peakY - m_graphMin) + 0.5);
+
+  dStartY = h - yStep * (fromY - m_graphMin);
+  dStopY = h - yStep * (toY - m_graphMin);
+
+  /*qDebug("Drawing peak");
+  qDebug() << "RSX" << fromX << "RSY" << fromY << "REX" << toX << "REY" << toY;
+  qDebug() << "X step" << xStep << "Y step" << yStep;
+  qDebug() << "SX" << startX << "SY" << startY << "EX" << stopX << "EY" << stopY << "w/h" << w << h;
+  qDebug() << "DSY" << dStartY << "DEY" << dStopY << "Slope I" << (toY-fromY)/(toX-fromX) << "Slope D" << (dStopY-dStartY)/(toX-fromX);
+  qDebug("---");*/
+
+  m_graphPainter->begin(m_graphPlain);
+  m_graphPainter->setPen(Qt::red);
+  m_graphPainter->drawLine(startX, startY, stopX, stopY);
+
+  /* Draw AREA and TIME caption */
+  m_graphPainter->setFont(font);
+  m_graphPainter->setPen(Qt::black);
+  textWidth = fm.width(time);
+  if (peakStartY - textWidth < 2)
+    peakStartY += textWidth - peakStartY + 2;
+  m_graphPainter->save();
+  m_graphPainter->translate(peakStartX, peakStartY);
+  m_graphPainter->rotate(-90);
+  m_graphPainter->drawText(0, 0, time);
+  m_graphPainter->rotate(+90);
+  m_graphPainter->restore();
+  m_graphPainter->drawText(peakStartX + 5, peakStartY, area);
+
+  m_graphPainter->end();
+  m_graphPainter->begin(m_graph);
+  m_graphPainter->drawPixmap(0, 0, *m_graphPlain, 0, 0, w, h);
+  m_graphPainter->end();
+  ui->qw_graphView->setPixmap(*m_graph);
+}
+
+void SignalView::onUpdateGraph(double* data, size_t len, double min, double max)
+{
+  if (m_graphData != nullptr)
+    delete[] m_graphData;
+  m_graphData = data;
+  m_graphLen = len;
+  m_graphMin = min;
+  m_graphMax = max;
+
+  redrawGraph();
+}
+/* ENDOF public slots */
+
+/* Private slots*/
+void SignalView::onGraphViewMouseCursorLeft()
+{
+  if(m_graph == nullptr)
+    return;
+
+  ui->qw_graphView->setPixmap(*m_graphPlain);
+
+  emit crosshairErased();
+}
+
+void SignalView::onGraphRedrawNeeded()
+{
+  emit redrawNeeded();
+}
+
+void SignalView::onMouseButtonTriggered(const int x, const int y, Qt::MouseButton button, QEvent::Type type, const QPoint& globalPos)
+{
+  if (m_ctxMenuOpen)
+    return;
+
+  switch (button) {
+    case Qt::LeftButton:
+      switch (m_mouseMode) {
+        case SignalView::MouseMode::CROSSHAIR:
+          if (m_graphCtrlMode == GraphControlModes::ZOOM) {
+            m_zoomRectStartX = x;
+            m_zoomRectStartY = y;
+            m_zoomRectLastX = x;
+            m_zoomRectLastY = y;
+            m_mouseMode = SignalView::MouseMode::ZOOM;
+          } else if (m_graphCtrlMode == GraphControlModes::INTEGRATE) {
+            m_integrateStartX = x;
+            m_integrateStartY = y;
+            m_integrateStopX = x;
+            m_integrateStopY = y;
+            eraseCrosshair();
+            m_graphCrosshairX = -1;
+            m_mouseMode = SignalView::MouseMode::INTEGRATE;
+          }
+          break;
+        case SignalView::MouseMode::ZOOM:
+          if (m_zoomRectLastX < m_zoomRectStartX)
+            std::swap(m_zoomRectLastX, m_zoomRectStartX);
+          if (m_zoomRectLastY < m_zoomRectStartY)
+            std::swap(m_zoomRectLastY, m_zoomRectStartY);
+          emit zoomed(m_zoomRectStartX, m_zoomRectStartY, m_zoomRectLastX, m_zoomRectLastY);
+          m_mouseMode = SignalView::MouseMode::CROSSHAIR;
+          break;
+        case SignalView::MouseMode::INTEGRATE:
+          if (m_integrateStartX > m_integrateStopX)
+            std::swap(m_integrateStartX, m_integrateStopX);
+          emit integrated(m_integrateStartX, m_integrateStartY, m_integrateStopX, m_integrateStopY);
+          eraseIntegrationBaseline(true);
+          m_integrateStartX = -1;
+          m_mouseMode = SignalView::MouseMode::CROSSHAIR;
+          break;
+      }
+      break;
+    case Qt::RightButton:
+      switch (m_mouseMode) {
+          case SignalView::MouseMode::CROSSHAIR:
+          m_ctxMenuOpen = true;
+          emit showContextMenu(x, y, globalPos);
+          break;
+        case SignalView::MouseMode::ZOOM:
+          eraseZoomRect(true);
+          m_mouseMode = SignalView::MouseMode::CROSSHAIR;
+          break;
+        case SignalView::MouseMode::INTEGRATE:
+          eraseIntegrationBaseline(true);
+          m_mouseMode = SignalView::MouseMode::CROSSHAIR;
+          break;
+        default:
+          break;
+      }
+      break;
+    default:
+      break;
+  }
+}
+
+void SignalView::onGraphViewMouseMoved(const int x, const int y)
+{
+  switch (m_mouseMode) {
+    case SignalView::MouseMode::CROSSHAIR:
+      eraseCrosshair();
+      drawCrosshair(x, y);
+      break;
+    case SignalView::MouseMode::ZOOM:
+      eraseCrosshair();
+      drawZoomRect(x, y);
+      break;
+    case SignalView::MouseMode::INTEGRATE:
+      eraseIntegrationBaseline();
+      drawIntegrationBaseline(x, y);
+      break;
+  }
+
+  emit crosshairMoved(x, y);
+}
+
+SignalView::~SignalView()
+{
+  if (m_graphPainter->isActive())
+    m_graphPainter->end();
+  delete ui;
+  if (m_graph != nullptr)
+    delete m_graph;
+  if (m_graphPlain != nullptr)
+    delete m_graphPlain;
+  if (m_graphData != nullptr)
+    delete[] m_graphData;
+
+  delete m_graphPainter;
+}
diff --git a/gui/signalview.h b/gui/signalview.h
new file mode 100644 (file)
index 0000000..2c0b66c
--- /dev/null
@@ -0,0 +1,120 @@
+/*
+  Copyright (c) 2013 Michal Malý <madcatxster@prifuk.cz>
+
+  Permission is hereby granted, free of charge, to any person obtaining a copy
+  of this software and associated documentation files (the "Software"), to deal
+  in the Software without restriction, including without limitation the rights
+  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+  copies of the Software, and to permit persons to whom the Software is
+  furnished to do so, subject to the following conditions:
+
+  The above copyright notice and this permission notice shall be included in
+  all copies or substantial portions of the Software.
+
+  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+  THE SOFTWARE.
+*/
+
+#ifndef SIGNALVIEW_H
+#define SIGNALVIEW_H
+
+#include "graphview.h"
+#include "integrationtablemodel.h"
+#include "signaldatatablemodel.h"
+#include <memory>
+#include <QtWidgets/QWidget>
+#include "../metatypes.h"
+
+namespace Ui {
+  class SignalView;
+}
+
+class SignalView : public QWidget
+{
+  Q_OBJECT
+
+public:
+  enum class MouseMode {
+    CROSSHAIR,
+    ZOOM,
+    INTEGRATE
+  };
+
+  explicit SignalView(QWidget* parent = nullptr);
+  ~SignalView();
+  void setControlMode(GraphControlModes mode);
+  void setDataTableModel (SignalDataTableModel* model);
+  void setIntegrationTableModel(IntegrationTableModel* model);
+  void setTypeText(const QString& text);
+  void setXUnits(const QString& text);
+  void setYUnits(const QString& text);
+
+private:
+  void drawCrosshair(const int x, const int y);
+  void drawIntegrationBaseline(const int x, const int y);
+  void drawZoomRect(const int x, const int y);
+  void eraseCrosshair(bool apply = false);
+  void eraseIntegrationBaseline(bool apply = false);
+  void eraseZoomRect(bool apply = false);
+  void redrawGraph();
+  void resizeEvent(QResizeEvent* ev);
+
+  QPainter* m_graphPainter;
+  /* Dataset to plot */
+  QPixmap* m_graph;
+  QPixmap* m_graphPlain;
+  double* m_graphData;
+  double m_graphMin;
+  double m_graphMax;
+  size_t m_graphLen;
+  /* Crosshair */
+  int m_graphCrosshairX;
+  int m_graphCrosshairY;
+  /* Integration baseline */
+  int m_integrateStartX;
+  int m_integrateStartY;
+  int m_integrateStopX;
+  int m_integrateStopY;
+  /* Zooming rectangle */
+  int m_zoomRectStartX;
+  int m_zoomRectStartY;
+  int m_zoomRectLastX;
+  int m_zoomRectLastY;
+
+  bool m_ctxMenuOpen;
+  GraphControlModes m_graphCtrlMode;
+  MouseMode m_mouseMode;
+  Ui::SignalView* ui;
+
+public slots:
+  void onControlModeChanged(GraphControlModes mode);
+  void onCtxMenuClosed() { m_ctxMenuOpen = false; }
+  void onDrawIntegration(const int fromX, const double fromY, const int toX, const double toY, const int peakX, const double peakY,
+                         const QString& time, const QString& area, const bool valley);
+  void onMouseButtonTriggered(const int x, const int y, Qt::MouseButton button, QEvent::Type type, const QPoint& globalPos);
+  void onUpdateCurrentValues(double x, double y);
+  void onRemoveCurrentValues();
+  void onUpdateGraph(double* arr, size_t len, double min, double max);
+
+private slots:
+  void onGraphRedrawNeeded();
+  void onGraphViewMouseCursorLeft();
+  void onGraphViewMouseMoved(const int x, const int y);
+
+signals:
+  void crosshairErased();
+  void crosshairMoved(const int x, const int y);
+  void integrated(const int fromX, const int fromY, const int toX, const int toY);
+  void redrawNeeded();
+  void resized(const int x, const int y);
+  void showContextMenu(const int x, const int y, const QPoint& globalPos);
+  void unzoomed();
+  void zoomed(const int fromX, const int fromY, const int toX, const int toY);
+};
+
+#endif // SIGNALVIEW_H
diff --git a/gui/signalview.ui b/gui/signalview.ui
new file mode 100644 (file)
index 0000000..03f18f1
--- /dev/null
@@ -0,0 +1,185 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>SignalView</class>
+ <widget class="QWidget" name="SignalView">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>475</width>
+    <height>366</height>
+   </rect>
+  </property>
+  <property name="sizePolicy">
+   <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
+    <horstretch>0</horstretch>
+    <verstretch>0</verstretch>
+   </sizepolicy>
+  </property>
+  <property name="windowTitle">
+   <string>Form</string>
+  </property>
+  <layout class="QVBoxLayout" name="verticalLayout">
+   <item>
+    <layout class="QFormLayout" name="formLayout">
+     <item row="0" column="0">
+      <widget class="QLabel" name="ql_signalTypeCap">
+       <property name="text">
+        <string>Type:</string>
+       </property>
+      </widget>
+     </item>
+     <item row="0" column="1">
+      <widget class="QLabel" name="ql_signalType">
+       <property name="text">
+        <string/>
+       </property>
+      </widget>
+     </item>
+    </layout>
+   </item>
+   <item>
+    <widget class="QTabWidget" name="qtw_tabs">
+     <property name="sizePolicy">
+      <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
+       <horstretch>0</horstretch>
+       <verstretch>0</verstretch>
+      </sizepolicy>
+     </property>
+     <property name="minimumSize">
+      <size>
+       <width>0</width>
+       <height>0</height>
+      </size>
+     </property>
+     <property name="tabShape">
+      <enum>QTabWidget::Rounded</enum>
+     </property>
+     <property name="currentIndex">
+      <number>0</number>
+     </property>
+     <property name="elideMode">
+      <enum>Qt::ElideNone</enum>
+     </property>
+     <widget class="QWidget" name="qtab_graph">
+      <attribute name="title">
+       <string>Graph</string>
+      </attribute>
+      <layout class="QVBoxLayout" name="verticalLayout_2">
+       <item>
+        <widget class="GraphView" name="qw_graphView" native="true">
+         <property name="sizePolicy">
+          <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
+           <horstretch>0</horstretch>
+           <verstretch>0</verstretch>
+          </sizepolicy>
+         </property>
+         <property name="minimumSize">
+          <size>
+           <width>0</width>
+           <height>10</height>
+          </size>
+         </property>
+        </widget>
+       </item>
+       <item>
+        <layout class="QGridLayout" name="gridLayout">
+         <item row="0" column="2">
+          <widget class="QLineEdit" name="qle_xValue">
+           <property name="text">
+            <string>-</string>
+           </property>
+           <property name="alignment">
+            <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+           </property>
+           <property name="readOnly">
+            <bool>true</bool>
+           </property>
+          </widget>
+         </item>
+         <item row="0" column="5">
+          <widget class="QLineEdit" name="qle_yValue">
+           <property name="text">
+            <string>-</string>
+           </property>
+           <property name="alignment">
+            <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+           </property>
+           <property name="readOnly">
+            <bool>true</bool>
+           </property>
+          </widget>
+         </item>
+         <item row="0" column="4">
+          <widget class="QLabel" name="ql_yValCap">
+           <property name="text">
+            <string>Y:</string>
+           </property>
+          </widget>
+         </item>
+         <item row="0" column="3">
+          <widget class="QLabel" name="ql_xUnits">
+           <property name="text">
+            <string>-</string>
+           </property>
+          </widget>
+         </item>
+         <item row="0" column="6">
+          <widget class="QLabel" name="ql_yUnits">
+           <property name="sizePolicy">
+            <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+             <horstretch>0</horstretch>
+             <verstretch>0</verstretch>
+            </sizepolicy>
+           </property>
+           <property name="text">
+            <string>-</string>
+           </property>
+          </widget>
+         </item>
+         <item row="0" column="1">
+          <widget class="QLabel" name="ql_xValueCap">
+           <property name="text">
+            <string>X:</string>
+           </property>
+          </widget>
+         </item>
+        </layout>
+       </item>
+      </layout>
+     </widget>
+     <widget class="QWidget" name="qtab_data">
+      <attribute name="title">
+       <string>Data</string>
+      </attribute>
+      <layout class="QVBoxLayout" name="verticalLayout_3">
+       <item>
+        <widget class="QTableView" name="qtblv_signalDataTable"/>
+       </item>
+      </layout>
+     </widget>
+     <widget class="QWidget" name="qtab_integration">
+      <attribute name="title">
+       <string>Integration</string>
+      </attribute>
+      <layout class="QVBoxLayout" name="verticalLayout_4">
+       <item>
+        <widget class="QTableView" name="qtblv_integrationTable"/>
+       </item>
+      </layout>
+     </widget>
+    </widget>
+   </item>
+  </layout>
+ </widget>
+ <customwidgets>
+  <customwidget>
+   <class>GraphView</class>
+   <extends>QWidget</extends>
+   <header>gui/graphview.h</header>
+   <container>1</container>
+  </customwidget>
+ </customwidgets>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/imgresources.qrc b/imgresources.qrc
new file mode 100644 (file)
index 0000000..3ff4708
--- /dev/null
@@ -0,0 +1,5 @@
+<RCC>
+    <qresource prefix="/">
+        <file>resources/Qt_master_logo.png</file>
+    </qresource>
+</RCC>
diff --git a/integratedpeak.cpp b/integratedpeak.cpp
new file mode 100644 (file)
index 0000000..64ae1da
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+  Copyright (c) 2013 Michal Malý <madcatxster@prifuk.cz>
+
+  Permission is hereby granted, free of charge, to any person obtaining a copy
+  of this software and associated documentation files (the "Software"), to deal
+  in the Software without restriction, including without limitation the rights
+  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+  copies of the Software, and to permit persons to whom the Software is
+  furnished to do so, subject to the following conditions:
+
+  The above copyright notice and this permission notice shall be included in
+  all copies or substantial portions of the Software.
+
+  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+  THE SOFTWARE.
+*/
+
+#include "integratedpeak.h"
+
+IntegratedPeak::IntegratedPeak(IntegratedPeak::Type type, double auc, size_t peakIdx, double peakTime, double peakVal,
+                               size_t fromIdx, size_t toIdx, double fromTime, double toTime, double fromY, double toY, QObject* parent) :
+  QObject(parent),
+  m_auc(auc),
+  m_fromIdx(fromIdx),
+  m_fromTime(fromTime),
+  m_fromY(fromY),
+  m_peakIdx(peakIdx),
+  m_peakTime(peakTime),
+  m_peakVal(peakVal),
+  m_toIdx(toIdx),
+  m_toTime(toTime),
+  m_toY(toY),
+  m_type(type)
+{
+}
+
+double IntegratedPeak::baselineSlope() const
+{
+  return (m_toY - m_fromY) / (m_toIdx - m_fromIdx);
+}
+
+double IntegratedPeak::height() const
+{
+  return fabs(m_peakVal - (baselineSlope() * (m_peakIdx - m_fromIdx) + m_fromY));
+}
diff --git a/integratedpeak.h b/integratedpeak.h
new file mode 100644 (file)
index 0000000..7db458d
--- /dev/null
@@ -0,0 +1,72 @@
+/*
+  Copyright (c) 2013 Michal Malý <madcatxster@prifuk.cz>
+
+  Permission is hereby granted, free of charge, to any person obtaining a copy
+  of this software and associated documentation files (the "Software"), to deal
+  in the Software without restriction, including without limitation the rights
+  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+  copies of the Software, and to permit persons to whom the Software is
+  furnished to do so, subject to the following conditions:
+
+  The above copyright notice and this permission notice shall be included in
+  all copies or substantial portions of the Software.
+
+  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+  THE SOFTWARE.
+*/
+
+
+#ifndef INTEGRATEDPEAK_H
+#define INTEGRATEDPEAK_H
+
+#include <QtCore/QObject>
+
+class IntegratedPeak : public QObject
+{
+  Q_OBJECT
+public:
+  enum class Type {
+    PEAK,
+    VALLEY
+  };
+
+  explicit IntegratedPeak(Type type, double auc, size_t peakIdx, double peakTime, double peakVal, size_t fromIdx, size_t toIdx, double fromTime, double toTime,
+                          double fromY, double toY, QObject* parent = nullptr);
+  double auc() const { return m_auc; }
+  double baselineSlope() const;
+  size_t fromIdx() const { return m_fromIdx; }
+  double fromY() const { return m_fromY; }
+  double fromTime() const { return m_fromTime;}
+  double height() const;
+  size_t peakIdx() const { return m_peakIdx; }
+  double peakTime() const { return m_peakTime; }
+  size_t toIdx() const { return m_toIdx; }
+  double toTime() const { return m_toTime; }
+  double toY() const { return m_toY; }
+  Type type() const { return m_type; }
+  double width() const { return m_toTime - m_fromTime; }
+
+private:
+  const double m_auc;
+  const size_t m_fromIdx;
+  const double m_fromTime;
+  const double m_fromY;
+  const size_t m_peakIdx;
+  const double m_peakTime;
+  const double m_peakVal;
+  const size_t m_toIdx;
+  const double m_toTime;
+  const double m_toY;
+  const Type m_type;
+
+public slots:
+signals:
+
+};
+
+#endif // INTEGRATEDPEAK_H
diff --git a/integrationtablemodel.cpp b/integrationtablemodel.cpp
new file mode 100644 (file)
index 0000000..9a2c200
--- /dev/null
@@ -0,0 +1,76 @@
+/*
+  Copyright (c) 2013 Michal Malý <madcatxster@prifuk.cz>
+
+  Permission is hereby granted, free of charge, to any person obtaining a copy
+  of this software and associated documentation files (the "Software"), to deal
+  in the Software without restriction, including without limitation the rights
+  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+  copies of the Software, and to permit persons to whom the Software is
+  furnished to do so, subject to the following conditions:
+
+  The above copyright notice and this permission notice shall be included in
+  all copies or substantial portions of the Software.
+
+  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+  THE SOFTWARE.
+*/
+
+#include "integrationtablemodel.h"
+
+IntegrationTableModel::IntegrationTableModel(const std::shared_ptr<Integrator> integrator, QObject* parent) :
+  QAbstractTableModel(parent),
+  m_integrator(integrator)
+{}
+
+QVariant IntegrationTableModel::data(const QModelIndex& index, int role) const
+{
+  if (role != Qt::DisplayRole)
+    return QVariant();
+  if (index.row() >= m_integrator->peakCount())
+    return QVariant();
+
+  const std::shared_ptr<IntegratedPeak> peak = m_integrator->peakByIdx(index.row());
+  switch (index.column()) {
+    case 0:
+      return QString::number(peak->peakTime(), 'f', 4);
+    case 1:
+      return QString::number(peak->auc(), 'f', 4);
+    case 2:
+      return QString::number(peak->height(), 'f', 4);
+    case 3:
+      return QString::number(peak->width(), 'f', 4);
+  }
+  return QVariant();
+}
+
+QVariant IntegrationTableModel::headerData(int section, Qt::Orientation orientation, int role) const
+{
+  if (role == Qt::DisplayRole && orientation == Qt::Horizontal) {
+    switch (section) {
+      case 0:
+        return QString("Time");
+      case 1:
+        return QString("Area");
+      case 2:
+        return QString("Height");
+      case 3:
+        return QString("Width");
+    }
+  }
+  return QVariant();
+}
+
+int IntegrationTableModel::rowCount(const QModelIndex& parent) const
+{
+  Q_UNUSED(parent);
+
+  if (m_integrator == nullptr)
+    return 0;
+
+  return m_integrator->peakCount();
+}
diff --git a/integrationtablemodel.h b/integrationtablemodel.h
new file mode 100644 (file)
index 0000000..90daac4
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+  Copyright (c) 2013 Michal Malý <madcatxster@prifuk.cz>
+
+  Permission is hereby granted, free of charge, to any person obtaining a copy
+  of this software and associated documentation files (the "Software"), to deal
+  in the Software without restriction, including without limitation the rights
+  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+  copies of the Software, and to permit persons to whom the Software is
+  furnished to do so, subject to the following conditions:
+
+  The above copyright notice and this permission notice shall be included in
+  all copies or substantial portions of the Software.
+
+  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+  THE SOFTWARE.
+*/
+
+#ifndef INTEGRATIONTABLEMODEL_H
+#define INTEGRATIONTABLEMODEL_H
+
+#include "integrator.h"
+#include <memory>
+#include <QtCore/QAbstractTableModel>
+
+class IntegrationTableModel : public QAbstractTableModel
+{
+public:
+  IntegrationTableModel(const std::shared_ptr<Integrator> integrator, QObject* parent = nullptr);
+  QVariant data(const QModelIndex& index, int role) const;
+  void doneReset() { endResetModel(); }
+  int columnCount(const QModelIndex& parent) const { Q_UNUSED(parent); return 4; }
+  QVariant headerData(int section, Qt::Orientation orientation, int role) const;
+  void prepReset() { beginResetModel(); }
+  int rowCount(const QModelIndex& parent) const;
+
+private:
+  const std::shared_ptr<Integrator> m_integrator;
+};
+
+#endif // INTEGRATIONTABLEMODEL_H
diff --git a/integrator.cpp b/integrator.cpp
new file mode 100644 (file)
index 0000000..8a811d2
--- /dev/null
@@ -0,0 +1,157 @@
+/*
+  Copyright (c) 2013 Michal Malý <madcatxster@prifuk.cz>
+
+  Permission is hereby granted, free of charge, to any person obtaining a copy
+  of this software and associated documentation files (the "Software"), to deal
+  in the Software without restriction, including without limitation the rights
+  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+  copies of the Software, and to permit persons to whom the Software is
+  furnished to do so, subject to the following conditions:
+
+  The above copyright notice and this permission notice shall be included in
+  all copies or substantial portions of the Software.
+
+  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+  THE SOFTWARE.
+*/
+
+#include "integrator.h"
+#include "logger.h"
+
+const QString Integrator::ME_SENDER_STR("Integrator");
+
+Integrator::Integrator(std::shared_ptr<Signal> signal, QObject* parent) :
+  QObject(parent),
+  m_signal(signal)
+{}
+
+Integrator::ReturnCode Integrator::deletePeak(size_t idx)
+{
+  std::multimap<size_t, std::shared_ptr<IntegratedPeak>>::iterator it = m_peaks.begin();
+  while (it != m_peaks.end() && it->first <= idx) {
+    if (it->first <= idx && it->second->toIdx() > idx) { /* Peak is surrounding the cursor */
+      m_peaks.erase(it);
+      return ReturnCode::SUCCESS;
+    }
+    it++;
+  }
+  return ReturnCode::E_NO_PEAK;
+}
+
+Integrator::ReturnCode Integrator::integrate(size_t startIdx, size_t stopIdx, double startY, double stopY, std::shared_ptr<IntegratedPeak>& peak)
+{
+  size_t peakValueIdx;
+  size_t startIntersIdx, stopIntersIdx;
+  size_t i;
+  double auc = 0.0;
+  double slope;
+  double startYVal, stopYVal;
+  bool expectDownward;
+  IntegratedPeak::Type peakType;
+
+  if (startIdx > m_signal->valuesCount()-1) {
+    Logger::log(Logger::Level::ERROR, ME_SENDER_STR, "Invalid startIdx value " + QString::number(startIdx));
+    return ReturnCode::E_INVAL;
+  }
+  if (stopIdx > m_signal->valuesCount()-1) {
+    Logger::log(Logger::Level::ERROR, ME_SENDER_STR, "Invalid stopIdx value " + QString::number(stopIdx));
+    return ReturnCode::E_INVAL;
+  }
+
+  slope = (stopY - startY) / (stopIdx - startIdx);
+  /* Try to find first intersection */
+  if (startY < m_signal->valueAt(startIdx)) {
+    expectDownward = true;
+    //qDebug() << "Expecting downward peak";
+  } else {
+    expectDownward = false;
+    //qDebug() << "Expecting upward peak";
+  }
+  for (i = startIdx; i <= stopIdx; i++) {
+    double value = m_signal->valueAt(i);
+    double blY = slope * (i - startIdx) + startY;
+
+    if (expectDownward && blY > value) { /* Found first intersection */
+      startIntersIdx = i-1;
+      startYVal = blY;
+      break;
+    } else if (!expectDownward && blY < value) { /* Found first intersection */
+      startIntersIdx = i-1;
+      startYVal = blY;
+      break;
+    }
+  }
+  if (i > stopIdx) {
+    Logger::log(Logger::Level::INFO, ME_SENDER_STR, "First intersection not found.");
+    return ReturnCode::E_NO_FIRST_INTERSECT;
+  }
+
+  /* Try to find second intersection */
+  for (; i <= stopIdx; i++) {
+    double value = m_signal->valueAt(i);
+    double blY = slope * (i - startIdx) + startY;
+
+    if (expectDownward && blY <= value) {
+      stopIntersIdx = i;
+      stopYVal = blY;
+      break;
+    } else if (!expectDownward && blY >= value) {
+      stopIntersIdx = i;
+      stopYVal = blY;
+      break;
+    }
+  }
+  if (i > stopIdx) {
+    Logger::log(Logger::Level::INFO, ME_SENDER_STR, "Second intersection not found.");
+    return ReturnCode::E_NO_SECOND_INTERSECT;
+  }
+
+  /* Integrate */
+  peakValueIdx = startIntersIdx;
+  for (i = startIntersIdx+1; i < stopIntersIdx; i++) {
+    double blXA = slope * (i - startIntersIdx - 1) + startY;
+    double blXB = slope * (i - startIntersIdx) + startY;
+    double valA = m_signal->valueAt(i-1);
+    double valB = m_signal->valueAt(i);
+    double tA = m_signal->timeAt(i-1);
+    double tB = m_signal->timeAt(i);
+    double avgH = (valB + valA)/2 - (blXB + blXA)/2;
+    double area = avgH * (tB - tA);
+    auc += area;
+    if (expectDownward && valB < m_signal->valueAt(peakValueIdx)) peakValueIdx = i;
+    else if (!expectDownward && valB > m_signal->valueAt(peakValueIdx)) peakValueIdx = i;
+    //qDebug() << area << auc;
+  }
+  /* Convert AUC to positive and seconds */
+  auc = fabs(auc) * 60;
+
+  /* Add peak to list */
+  if (expectDownward) peakType = IntegratedPeak::Type::VALLEY;
+  else peakType = IntegratedPeak::Type::PEAK;
+
+  /*qDebug("Peak integrated");
+  qDebug() << "SXC" << fromX << "SYC" << fromY << "EXC" << toX << "EYC" << toY << "Slope" << slope;
+  qDebug() << "1st(X, Y, vY)" << startIntersIdx << m_signal->valueAt(startIntersIdx) << startYVal;
+  qDebug() << "2nd(X, Y, vY)" << stopIntersIdx << m_signal->valueAt(stopIntersIdx) << stopYVal;
+  qDebug("---");*/
+
+  peak = std::shared_ptr<IntegratedPeak>(new IntegratedPeak(peakType, auc, peakValueIdx, m_signal->timeAt(peakValueIdx), m_signal->valueAt(peakValueIdx),
+                                                            startIntersIdx, stopIntersIdx, m_signal->timeAt(startIntersIdx), m_signal->timeAt(stopIntersIdx),
+                                                            startYVal, stopYVal));
+  m_peaks.insert(std::pair<size_t, std::shared_ptr<IntegratedPeak>>(startIdx, peak));
+
+  return ReturnCode::SUCCESS;
+}
+
+const std::shared_ptr<IntegratedPeak> Integrator::peakByIdx(const int idx) const
+{
+  std::multimap<size_t, std::shared_ptr<IntegratedPeak>>::const_iterator it = m_peaks.begin();
+  std::advance(it, idx);
+
+  return it->second;
+}
diff --git a/integrator.h b/integrator.h
new file mode 100644 (file)
index 0000000..15fd076
--- /dev/null
@@ -0,0 +1,64 @@
+/*
+  Copyright (c) 2013 Michal Malý <madcatxster@prifuk.cz>
+
+  Permission is hereby granted, free of charge, to any person obtaining a copy
+  of this software and associated documentation files (the "Software"), to deal
+  in the Software without restriction, including without limitation the rights
+  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+  copies of the Software, and to permit persons to whom the Software is
+  furnished to do so, subject to the following conditions:
+
+  The above copyright notice and this permission notice shall be included in
+  all copies or substantial portions of the Software.
+
+  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+  THE SOFTWARE.
+*/
+
+#ifndef INTEGRATOR_H
+#define INTEGRATOR_H
+
+#include "integratedpeak.h"
+#include "signal.h"
+#include <map>
+#include <memory>
+#include <QtCore/QObject>
+
+class Integrator : public QObject
+{
+  Q_OBJECT
+public:
+  enum class ReturnCode {
+    SUCCESS,
+    E_NO_FIRST_INTERSECT,
+    E_NO_SECOND_INTERSECT,
+    E_INVAL,
+    E_NO_PEAK
+  };
+
+  explicit Integrator(std::shared_ptr<Signal> signal, QObject* parent = nullptr);
+  ReturnCode deletePeak(size_t idx);
+  ReturnCode integrate(size_t startIdx, size_t stopIdx, double startY, double stopY, std::shared_ptr<IntegratedPeak>& peak);
+  const std::shared_ptr<IntegratedPeak> peakByIdx(const int idx) const;
+  std::multimap<size_t, std::shared_ptr<IntegratedPeak>>::const_iterator peaksCBegin() const { return m_peaks.cbegin(); }
+  std::multimap<size_t, std::shared_ptr<IntegratedPeak>>::const_iterator peaksCEnd() const { return m_peaks.cend(); }
+  size_t peakCount() const { return m_peaks.size(); }
+
+private:
+  std::multimap<size_t, std::shared_ptr<IntegratedPeak>> m_peaks;
+  std::shared_ptr<Signal> m_signal;
+
+  static const QString ME_SENDER_STR;
+
+signals:
+
+public slots:
+
+};
+
+#endif // INTEGRATOR_H
diff --git a/locallogger.cpp b/locallogger.cpp
new file mode 100644 (file)
index 0000000..a808d2c
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+  Copyright (c) 2013 Michal Malý <madcatxster@prifuk.cz>
+
+  Permission is hereby granted, free of charge, to any person obtaining a copy
+  of this software and associated documentation files (the "Software"), to deal
+  in the Software without restriction, including without limitation the rights
+  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+  copies of the Software, and to permit persons to whom the Software is
+  furnished to do so, subject to the following conditions:
+
+  The above copyright notice and this permission notice shall be included in
+  all copies or substantial portions of the Software.
+
+  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+  THE SOFTWARE.
+*/
+
+#include "locallogger.h"
+
+/* Static methods */
+LocalLoggerPtr LocalLogger::create(QObject* parent)
+{
+  return std::shared_ptr<LocalLogger>(new LocalLogger(parent));
+}
+
+/* ENDOF static methods */
+
+LocalLogger::LocalLogger(QObject* parent) : Logger(parent)
+{
+}
+
+void LocalLogger::log(Level level, const QString& sender, const QString& message, bool global)
+{
+  LoggedInfo li = std::make_tuple(QDateTime::currentMSecsSinceEpoch(), level, sender, message);
+  this->m_lock.lock();
+  this->m_logged.push_back(li);
+  this->m_lock.unlock();
+
+  if (global)
+    Logger::log(level, sender, message, true);
+  if (level >= m_printLogLevel)
+    readLast();
+}
+
+void LocalLogger::read(Level level, uint length)
+{
+  Logger::read(level, length, this);
+}
+
+void LocalLogger::readLast()
+{
+  Logger::readLast(this);
+}
+
+LocalLogger::~LocalLogger()
+{
+}
diff --git a/locallogger.h b/locallogger.h
new file mode 100644 (file)
index 0000000..efc160c
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+  Copyright (c) 2013 Michal Malý <madcatxster@prifuk.cz>
+
+  Permission is hereby granted, free of charge, to any person obtaining a copy
+  of this software and associated documentation files (the "Software"), to deal
+  in the Software without restriction, including without limitation the rights
+  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+  copies of the Software, and to permit persons to whom the Software is
+  furnished to do so, subject to the following conditions:
+
+  The above copyright notice and this permission notice shall be included in
+  all copies or substantial portions of the Software.
+
+  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+  THE SOFTWARE.
+*/
+
+
+#ifndef LOCALLOGGER_H
+#define LOCALLOGGER_H
+
+#include "logger.h"
+
+class LocalLogger : public Logger
+{
+public:
+  static LocalLoggerPtr create(QObject* parent);
+  ~LocalLogger();
+  void log(Level level, const QString& sender, const QString& message, bool global = false);
+  void read(Level level, uint length = 0);
+  void readLast();
+  void setPrintLevel(Level level) { m_printLogLevel = level; }
+
+private:
+  LocalLogger(QObject* parent = nullptr);
+};
+
+#endif // LOCALLOGGER_H
diff --git a/logger.cpp b/logger.cpp
new file mode 100644 (file)
index 0000000..e153eed
--- /dev/null
@@ -0,0 +1,136 @@
+/*
+  Copyright (c) 2013 Michal Malý <madcatxster@prifuk.cz>
+
+  Permission is hereby granted, free of charge, to any person obtaining a copy
+  of this software and associated documentation files (the "Software"), to deal
+  in the Software without restriction, including without limitation the rights
+  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+  copies of the Software, and to permit persons to whom the Software is
+  furnished to do so, subject to the following conditions:
+
+  The above copyright notice and this permission notice shall be included in
+  all copies or substantial portions of the Software.
+
+  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+  THE SOFTWARE.
+*/
+
+#include "logger.h"
+
+#include <QDebug>
+
+Logger* Logger::s_globalInstance(nullptr);
+
+/* Static methods */
+LocalLoggerPtr Logger::createLocalLog()
+{
+  LocalLoggerPtr localLogger(LocalLogger::create(s_globalInstance));
+
+  return localLogger;
+}
+
+void Logger::initializeGlobal()
+{
+  if (Logger::s_globalInstance == nullptr)
+    Logger::s_globalInstance = new Logger();
+}
+
+const QString Logger::levelToString(Level level)
+{
+  switch (level) {
+    case DEBUG:
+      return "DEBUG";
+    case INFO:
+      return "INFO";
+    case WARNING:
+      return "WARNING";
+    case ERROR:
+      return "ERROR";
+    case CRITICAL:
+      return "CRITICAL";
+    default:
+      return "UNKNOWN!";
+  }
+}
+
+void Logger::log(Level level, const QString& sender, const QString& message, bool suppressPrint)
+{
+  LoggedInfo li = std::make_tuple(QDateTime::currentMSecsSinceEpoch(), level, sender, message);
+  s_globalInstance->m_lock.lock();
+  s_globalInstance->m_logged.push_back(li);
+  s_globalInstance->m_lock.unlock();
+
+  if (level >= s_globalInstance->m_printLogLevel && !suppressPrint)
+    readLast();
+}
+
+void Logger::read(Level level, uint length, Logger* logger)
+{
+  Logger* _logger;
+  size_t logSize;
+  size_t from, to;
+
+  if (logger == nullptr)
+    _logger = s_globalInstance;
+  else
+    _logger = logger;
+  logSize = _logger->m_logged.size();
+
+  if (logSize < 1)
+    return;
+
+  if (length == 0) {
+    from = 0;
+    to = logSize - 1;
+  } else if (logSize < length) {
+    from = 0;
+    to = logSize - 1;
+  } else {
+    from = logSize - length - 1;
+    to = logSize - 1;
+  }
+
+  while (from <= to) {
+    const LoggedInfo& li = _logger->m_logged.at(from);
+    if (std::get<1>(li) >= level) {
+      QString out;
+      out = QString::number(std::get<0>(li));
+      out += " - " +  Logger::levelToString(std::get<1>(li));
+      out += ": " + std::get<2>(li);
+      out += ":: " + std::get<3>(li);
+      qDebug() << out;
+    }
+    from++;
+  }
+}
+
+void Logger::readLast(Logger* logger)
+{
+  Logger* _logger;
+  QString out;
+
+  if (logger == nullptr)
+    _logger = s_globalInstance;
+  else
+    _logger = logger;
+
+  const LoggedInfo& li = _logger->m_logged.back();
+  out = QString::number(std::get<0>(li)) + " - " + Logger::levelToString(std::get<1>(li)) + ": " + std::get<2>(li) + ":: " + std::get<3>(li);
+  qDebug() << out;
+}
+
+/* ENDOF static methods */
+
+Logger::Logger(QObject* parent) : QObject(parent)
+{
+}
+
+Logger::~Logger()
+{
+  Logger::read(Logger::Level::DEBUG);
+}
diff --git a/logger.h b/logger.h
new file mode 100644 (file)
index 0000000..2288688
--- /dev/null
+++ b/logger.h
@@ -0,0 +1,72 @@
+/*
+  Copyright (c) 2013 Michal Malý <madcatxster@prifuk.cz>
+
+  Permission is hereby granted, free of charge, to any person obtaining a copy
+  of this software and associated documentation files (the "Software"), to deal
+  in the Software without restriction, including without limitation the rights
+  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+  copies of the Software, and to permit persons to whom the Software is
+  furnished to do so, subject to the following conditions:
+
+  The above copyright notice and this permission notice shall be included in
+  all copies or substantial portions of the Software.
+
+  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+  THE SOFTWARE.
+*/
+
+
+#ifndef LOGGER_H
+#define LOGGER_H
+
+#include <memory>
+#include <mutex>
+#include <vector>
+#include <QtCore/QObject>
+#include <QtCore/QDateTime>
+
+class LocalLogger;
+typedef std::shared_ptr<LocalLogger> LocalLoggerPtr;
+
+class Logger : public QObject
+{
+  Q_OBJECT
+public:
+  enum Level {
+    DEBUG = 0,
+    INFO = 1,
+    WARNING = 2,
+    ERROR = 3,
+    CRITICAL = 4
+  };
+
+  ~Logger();
+  static LocalLoggerPtr createLocalLog();
+  static void initializeGlobal();
+  static void log(Level level, const QString& sender, const QString& message, bool suppressPrint = false);
+  static void read(Level level, uint length = 0, Logger* logger = nullptr);
+  static void readLast(Logger* logger = nullptr);
+  static void setPrintLevel(Level level) { s_globalInstance->m_printLogLevel = level; }
+
+protected:
+  typedef std::tuple<uint64_t, Level, const QString, const QString> LoggedInfo;
+
+  static const QString levelToString(Level level);
+
+  explicit Logger(QObject* parent = nullptr);
+
+  std::mutex m_lock;
+  std::vector<LoggedInfo> m_logged;
+  Level m_printLogLevel;
+
+  static Logger* s_globalInstance;
+
+};
+
+#include "locallogger.h"
+#endif // LOGGER_H
diff --git a/main.cpp b/main.cpp
new file mode 100644 (file)
index 0000000..bb4b0e3
--- /dev/null
+++ b/main.cpp
@@ -0,0 +1,75 @@
+/*
+  Copyright (c) 2013 Michal Malý <madcatxster@prifuk.cz>
+
+  Permission is hereby granted, free of charge, to any person obtaining a copy
+  of this software and associated documentation files (the "Software"), to deal
+  in the Software without restriction, including without limitation the rights
+  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+  copies of the Software, and to permit persons to whom the Software is
+  furnished to do so, subject to the following conditions:
+
+  The above copyright notice and this permission notice shall be included in
+  all copies or substantial portions of the Software.
+
+  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+  THE SOFTWARE.
+*/
+
+#include "gui/mainwindow.h"
+#include "datamanager.h"
+#include "logger.h"
+#include <QApplication>
+#include <QtWidgets/QMessageBox>
+
+int main(int argc, char *argv[])
+{
+  QApplication a(argc, argv);
+
+#warning Revisit the metatype shared_ptr<SignalView> registration
+  qRegisterMetaType<std::shared_ptr<SignalView>>();
+
+  Logger::initializeGlobal();
+  Logger::setPrintLevel(Logger::Level::DEBUG);
+  std::unique_ptr<DataManager> dMgr;
+  std::unique_ptr<MainWindow> mWin;
+
+  AGREInterface::ReturnCode aRet =  AGREInterface::instance()->initialize();
+  if (aRet == AGREInterface::ReturnCode::E_NO_LIBRARY) {
+    QMessageBox::critical(nullptr, "Dependencies error", "Library " + AGREInterface::AGRE_LIBRARY_NAME + " could not have been found.\n"
+                          "The application cannot continue.");
+    return 1;
+  } else if (aRet == AGREInterface::ReturnCode::E_CANNOT_RESOLVE) {
+    QMessageBox::critical(nullptr, "libAGRE error", "Some symbols from " + AGREInterface::AGRE_LIBRARY_NAME + " could not have been resolved.\n"
+                          "The application cannot continue.");
+    return 1;
+  } else if (aRet == AGREInterface::ReturnCode::E_CANNOT_GET_IFACE) {
+    QMessageBox::critical(nullptr, "libAGRE error", "An error occured during data reader initialization.\nThe application cannot continue.");
+    return 1;
+  }
+
+  dMgr = std::unique_ptr<DataManager>(new DataManager);
+  mWin = std::unique_ptr<MainWindow>(new MainWindow);
+  mWin->setSequenceListModel(dMgr->sequenceSelectorModel());
+  mWin->setSingleRunsListModel(dMgr->singleRunsSelectorModel());
+  /* Default to no sequence on load */
+  mWin->onSetActiveSequenceIndex(-1);
+  /* Connect SIGNALS/SLOTS */
+  QObject::connect(dMgr.get(), SIGNAL(addToDashboard(SignalView*)), mWin.get(), SLOT(onAddToDashboard(SignalView*)));
+  QObject::connect(dMgr.get(), SIGNAL(cleanDashboard()), mWin.get(), SLOT(onCleanDashboard()));
+  QObject::connect(dMgr.get(), SIGNAL(setActiveSequenceIndex(int)), mWin.get(), SLOT(onSetActiveSequenceIndex(int)));
+  QObject::connect(dMgr.get(), SIGNAL(setActiveSingleRunIndex(int)), mWin.get(), SLOT(onSetActiveSingleRunIndex(int)));
+  QObject::connect(dMgr.get(), SIGNAL(setSingleRunInfo(QString,QString,QString,QString)), mWin.get(), SLOT(onSetSingleRunInfo(QString,QString,QString,QString)));
+  QObject::connect(mWin.get(), SIGNAL(loadSingleRun(QString)), dMgr.get(), SLOT(onLoadSingleRun(QString)));
+  QObject::connect(mWin.get(), SIGNAL(loadSequence(QString)), dMgr.get(), SLOT(onLoadSequence(QString)));
+  QObject::connect(mWin.get(), SIGNAL(sequenceSelected(QString)), dMgr.get(), SLOT(onSequenceSelected(QString)));
+  QObject::connect(mWin.get(), SIGNAL(singleRunSelected(QString)), dMgr.get(), SLOT(onSingleRunSelected(QString)));
+
+  mWin->show();
+
+  return a.exec();
+}
diff --git a/mathhelpers.h b/mathhelpers.h
new file mode 100644 (file)
index 0000000..f608875
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+  Copyright (c) 2013 Michal Malý <madcatxster@prifuk.cz>
+
+  Permission is hereby granted, free of charge, to any person obtaining a copy
+  of this software and associated documentation files (the "Software"), to deal
+  in the Software without restriction, including without limitation the rights
+  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+  copies of the Software, and to permit persons to whom the Software is
+  furnished to do so, subject to the following conditions:
+
+  The above copyright notice and this permission notice shall be included in
+  all copies or substantial portions of the Software.
+
+  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+  THE SOFTWARE.
+*/
+
+#ifndef MATHHELPERS_H
+#define MATHHELPERS_H
+
+template <typename T> T average(T* vals, size_t len) {
+  T sum = 0;
+  for (int i = 0; i < len; i++)
+    sum += vals[i];
+  return sum / len;
+}
+
+#endif // MATHHELPERS_H
diff --git a/metatypes.h b/metatypes.h
new file mode 100644 (file)
index 0000000..96323fe
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+  Copyright (c) 2013 Michal Malý <madcatxster@prifuk.cz>
+
+  Permission is hereby granted, free of charge, to any person obtaining a copy
+  of this software and associated documentation files (the "Software"), to deal
+  in the Software without restriction, including without limitation the rights
+  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+  copies of the Software, and to permit persons to whom the Software is
+  furnished to do so, subject to the following conditions:
+
+  The above copyright notice and this permission notice shall be included in
+  all copies or substantial portions of the Software.
+
+  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+  THE SOFTWARE.
+*/
+
+#ifndef METATYPES_H
+#define METATYPES_H
+
+class SignalView;
+typedef std::shared_ptr<SignalView> SignalViewPtr;
+Q_DECLARE_METATYPE(SignalViewPtr);
+
+enum class GraphControlModes {
+  ZOOM,
+  INTEGRATE
+};
+Q_DECLARE_METATYPE(GraphControlModes);
+
+#endif // METATYPES_H
diff --git a/resources/Qt_master_logo.png b/resources/Qt_master_logo.png
new file mode 100644 (file)
index 0000000..6d48935
Binary files /dev/null and b/resources/Qt_master_logo.png differ
diff --git a/sequence.cpp b/sequence.cpp
new file mode 100644 (file)
index 0000000..52b415d
--- /dev/null
@@ -0,0 +1,110 @@
+/*
+  Copyright (c) 2013 Michal Malý <madcatxster@prifuk.cz>
+
+  Permission is hereby granted, free of charge, to any person obtaining a copy
+  of this software and associated documentation files (the "Software"), to deal
+  in the Software without restriction, including without limitation the rights
+  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+  copies of the Software, and to permit persons to whom the Software is
+  furnished to do so, subject to the following conditions:
+
+  The above copyright notice and this permission notice shall be included in
+  all copies or substantial portions of the Software.
+
+  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+  THE SOFTWARE.
+*/
+
+#include "sequence.h"
+
+Sequence::Sequence(QObject* parent) :
+  QObject(parent)
+{
+}
+
+Sequence::Sequence(std::vector<SingleRunPair>& singleRuns, QObject* parent) :
+  QObject(parent),
+  m_selectedRunKey(""),
+  m_singleRuns(singleRuns)
+{
+}
+
+int Sequence::add(QString name, std::shared_ptr<SingleRunData> value)
+{
+  m_singleRuns.push_back(SingleRunPair(name, value));
+  m_selectedRunKey = name;
+  return m_singleRuns.size()-1;
+}
+
+void Sequence::addController(const std::string& uid, std::shared_ptr<SignalController> controller)
+{
+  m_signalCtrls[uid] = controller;
+}
+
+SingleRunPair Sequence::at(const size_t idx)
+{
+  return m_singleRuns.at(idx);
+}
+
+std::shared_ptr<SignalController> Sequence::controller(const std::string& uid)
+{
+  std::unordered_map<std::string, std::shared_ptr<SignalController>>::iterator it;
+  it = m_signalCtrls.find(uid);
+  if (it == m_signalCtrls.end())
+    return nullptr;
+  else
+    return it->second;
+}
+
+std::vector<SingleRunPair>::iterator Sequence::find(const QString& key)
+{
+  std::vector<SingleRunPair>::iterator it = m_singleRuns.begin();
+  while (it != m_singleRuns.end()) {
+    if (it->first == key)
+      return it;
+    it++;
+  }
+  return m_singleRuns.end();
+}
+
+void Sequence::remove(const QString& key)
+{
+  std::vector<SingleRunPair>::iterator it = m_singleRuns.begin();
+  while (it++ != m_singleRuns.end()) {
+    if (it->first == key)
+      m_singleRuns.erase(it);
+  }
+}
+
+void Sequence::removeAt(const size_t idx)
+{
+  if (m_singleRuns.size() > idx)
+    m_singleRuns.erase(m_singleRuns.begin()+idx);
+}
+
+int Sequence::selectedRunIdx() const
+{
+  int idx = 0;
+  for (const SingleRunPair& p : m_singleRuns) {
+    if (p.first.compare(m_selectedRunKey) == 0)
+      return idx;
+    idx++;
+  }
+  return -1;
+}
+
+int Sequence::singleRunToIdx(const QString& key) const
+{
+  int idx = 0;
+  for (const SingleRunPair& p : m_singleRuns) {
+    if (p.first.compare(key) == 0)
+      return idx;
+    idx++;
+  }
+  return -1;
+}
diff --git a/sequence.h b/sequence.h
new file mode 100644 (file)
index 0000000..c8f986e
--- /dev/null
@@ -0,0 +1,70 @@
+/*
+  Copyright (c) 2013 Michal Malý <madcatxster@prifuk.cz>
+
+  Permission is hereby granted, free of charge, to any person obtaining a copy
+  of this software and associated documentation files (the "Software"), to deal
+  in the Software without restriction, including without limitation the rights
+  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+  copies of the Software, and to permit persons to whom the Software is
+  furnished to do so, subject to the following conditions:
+
+  The above copyright notice and this permission notice shall be included in
+  all copies or substantial portions of the Software.
+
+  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+  THE SOFTWARE.
+*/
+
+#ifndef SEQUENCE_H
+#define SEQUENCE_H
+
+#include "signalcontroller.h"
+#include "singlerundata.h"
+#include <unordered_map>
+#include <utility>
+#include <vector>
+#include <QtCore/QObject>
+
+typedef std::pair<QString, std::shared_ptr<SingleRunData>> SingleRunPair;
+
+class Sequence : public QObject
+{
+  Q_OBJECT
+public:
+  explicit Sequence(QObject* parent = nullptr);
+  explicit Sequence(std::vector<SingleRunPair>& singleRuns, QObject* parent = nullptr);
+  int add(QString name, std::shared_ptr<SingleRunData> value);
+  void addController(const std::string& uid, std::shared_ptr<SignalController> controller);
+  const std::vector<SingleRunPair>* allRuns() const { return &m_singleRuns; }
+  SingleRunPair at(size_t idx);
+  std::vector<SingleRunPair>::iterator begin() { return m_singleRuns.begin(); }
+  std::vector<SingleRunPair>::const_iterator cbegin() const { return m_singleRuns.cbegin(); }
+  std::vector<SingleRunPair>::const_iterator cend() const { return m_singleRuns.cend(); }
+  std::shared_ptr<SignalController> controller(const std::string& uid);
+  size_t count() const { return m_singleRuns.size(); }
+  std::vector<SingleRunPair>::iterator end() { return m_singleRuns.end(); }
+  std::vector<SingleRunPair>::iterator find(const QString& key);
+  void remove(const QString& key);
+  void removeAt(const size_t idx);
+  const QString& selectedRunKey() const { return m_selectedRunKey; }
+  int selectedRunIdx() const;
+  int singleRunToIdx(const QString& key) const;
+  void setSelectedRunKey(const QString& key) { m_selectedRunKey = key; }
+
+private:
+  QString m_selectedRunKey;
+  std::unordered_map<std::string, std::shared_ptr<SignalController>> m_signalCtrls;
+  std::vector<SingleRunPair> m_singleRuns;
+
+signals:
+
+public slots:
+
+};
+
+#endif // SEQUENCE_H
diff --git a/sequenceselectormodel.cpp b/sequenceselectormodel.cpp
new file mode 100644 (file)
index 0000000..d37d6c9
--- /dev/null
@@ -0,0 +1,75 @@
+/*
+  Copyright (c) 2013 Michal Malý <madcatxster@prifuk.cz>
+
+  Permission is hereby granted, free of charge, to any person obtaining a copy
+  of this software and associated documentation files (the "Software"), to deal
+  in the Software without restriction, including without limitation the rights
+  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+  copies of the Software, and to permit persons to whom the Software is
+  furnished to do so, subject to the following conditions:
+
+  The above copyright notice and this permission notice shall be included in
+  all copies or substantial portions of the Software.
+
+  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+  THE SOFTWARE.
+*/
+
+#include "sequenceselectormodel.h"
+
+SequenceSelectorModel::SequenceSelectorModel(QObject* parent) :
+  QAbstractListModel(parent),
+  m_activeSequences(nullptr)
+{
+}
+
+SequenceSelectorModel::SequenceSelectorModel(const std::vector<NameSequencePair>* sequences, QObject* parent) :
+  QAbstractListModel(parent),
+  m_activeSequences(sequences)
+{
+}
+
+QVariant SequenceSelectorModel::data(const QModelIndex& index, int role) const
+{
+  int seqIdx = 0;
+  if (role != Qt::DisplayRole)
+    return QVariant();
+  if (m_activeSequences == nullptr)
+    return QVariant();
+
+  std::vector<NameSequencePair>::const_iterator cit = m_activeSequences->cbegin();
+  while (cit != m_activeSequences->cend()) {
+    if (seqIdx == index.row())
+      return cit->first;
+    seqIdx++; cit++;
+  }
+  return QVariant();
+}
+
+int SequenceSelectorModel::rowCount(const QModelIndex& parent) const
+{
+  Q_UNUSED(parent);
+
+  if (m_activeSequences == nullptr)
+    return 0;
+  else
+    return m_activeSequences->size();
+}
+
+void SequenceSelectorModel::seqenceAdded()
+{
+  beginInsertRows(QModelIndex(), m_activeSequences->size()-1, m_activeSequences->size()-1);
+  endInsertRows();
+}
+
+void SequenceSelectorModel::setSequences(const std::vector<NameSequencePair>* sequences)
+{
+  beginResetModel();
+  m_activeSequences = sequences;
+  endResetModel();
+}
diff --git a/sequenceselectormodel.h b/sequenceselectormodel.h
new file mode 100644 (file)
index 0000000..9b2441f
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+  Copyright (c) 2013 Michal Malý <madcatxster@prifuk.cz>
+
+  Permission is hereby granted, free of charge, to any person obtaining a copy
+  of this software and associated documentation files (the "Software"), to deal
+  in the Software without restriction, including without limitation the rights
+  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+  copies of the Software, and to permit persons to whom the Software is
+  furnished to do so, subject to the following conditions:
+
+  The above copyright notice and this permission notice shall be included in
+  all copies or substantial portions of the Software.
+
+  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+  THE SOFTWARE.
+*/
+
+#ifndef SEQUENCESELECTORMODEL_H
+#define SEQUENCESELECTORMODEL_H
+
+#include "sequence.h"
+#include <mutex>
+#include <QtCore/QAbstractListModel>
+
+typedef std::pair<const QString, std::shared_ptr<Sequence>> NameSequencePair;
+
+class SequenceSelectorModel : public QAbstractListModel
+{
+public:
+  SequenceSelectorModel(QObject* parent = nullptr);
+  SequenceSelectorModel(const std::vector<NameSequencePair>* sequences, QObject* parent = nullptr);
+  QVariant data(const QModelIndex& index, int role) const;
+  void seqenceAdded();
+  int rowCount(const QModelIndex& parent) const;
+  void setSequences(const std::vector<NameSequencePair>* sequence);
+
+private:
+  const std::vector<NameSequencePair>* m_activeSequences;
+};
+
+#endif // SEQUENCESELECTORMODEL_H
diff --git a/signal.cpp b/signal.cpp
new file mode 100644 (file)
index 0000000..4262ee2
--- /dev/null
@@ -0,0 +1,137 @@
+/*
+  Copyright (c) 2013 Michal Malý <madcatxster@prifuk.cz>
+
+  Permission is hereby granted, free of charge, to any person obtaining a copy
+  of this software and associated documentation files (the "Software"), to deal
+  in the Software without restriction, including without limitation the rights
+  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+  copies of the Software, and to permit persons to whom the Software is
+  furnished to do so, subject to the following conditions:
+
+  The above copyright notice and this permission notice shall be included in
+  all copies or substantial portions of the Software.
+
+  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+  THE SOFTWARE.
+*/
+
+#include "signal.h"
+#include <cfloat>
+
+const QString Signal::RES_ABSORBANCE("Absorbance");
+const QString Signal::RES_CCD("CCD");
+const QString Signal::RES_CURRENT("Current");
+const QString Signal::RES_DAD("Absorbance");
+const QString Signal::RES_POWER("Power");
+const QString Signal::RES_VOLTAGE("Voltage");
+const QString Signal::KILOVOTLS_SI("KV");
+const QString Signal::MICROAMPERES_SI("\u03BCA");
+const QString Signal::MILLIAU_SI("mAU");
+const QString Signal::MILLIVOLTS_SI("mV");
+const QString Signal::WATTS_SI("W");
+
+Signal::Signal(const Equipment equipment, const Resource resource, const double samplingRate, const YUnit yunit, uint16_t wavelengthAbs, uint16_t wavelengthRef,
+               const std::vector<TimeValuePair>& values, QObject* parent) :
+  QObject(parent),
+  m_equipment(equipment),
+  m_resource(resource),
+  m_samplingRate(samplingRate),
+  m_values(values),
+  m_wavelengthAbs(wavelengthAbs),
+  m_wavelengthRef(wavelengthRef),
+  m_xunit(XUnit::MINUTE),
+  m_yunit(yunit)
+{
+  m_min = DBL_MAX;
+  m_max = DBL_MIN;
+  for (Signal::TimeValuePair tvp : values) {
+    if (tvp.second < m_min) m_min = tvp.second;
+    if (tvp.second > m_max) m_max = tvp.second;
+  }
+}
+
+std::vector<Signal::TimeValuePair>::const_iterator Signal::iteratorFrom(const size_t idx) const
+{
+  return m_values.cbegin()+idx-1;
+}
+
+Signal::TimeValuePair Signal::pairAt(const size_t idx) const
+{
+  if (idx >= m_values.size())
+    return m_values.back();
+  else
+    return m_values.at(idx);
+}
+
+QString Signal::resourceToString() const {
+  switch (m_resource) {
+    case Resource::CE_ABSORBANCE:
+      return RES_ABSORBANCE;
+    case Resource::CE_CCD:
+      return RES_CCD;
+    case Resource::CE_CURRENT:
+      return RES_CURRENT;
+    case Resource::CE_DAD:
+      return RES_DAD;
+    case Resource::CE_POWER:
+      return RES_POWER;
+    case Resource::CE_VOLTAGE:
+      return RES_VOLTAGE;
+    default:
+      return "?";
+  }
+}
+
+double Signal::timeAt(const size_t idx) const
+{
+  if (idx >= m_values.size())
+    return m_values.back().first;
+  else
+    return m_values.at(idx).first;
+}
+
+QString Signal::uidString() const
+{
+  return resourceToString() + QString::number(m_wavelengthAbs) + QString::number(m_wavelengthRef);
+}
+
+double Signal::valueAt(const size_t idx) const
+{
+  if (idx >= m_values.size())
+    return m_values.back().second;
+  else
+    return m_values.at(idx).second;
+}
+
+QString Signal::xunitToString() const
+{
+  switch (m_xunit) {
+    case XUnit::MINUTE:
+      return "min";
+    default:
+      return "?";
+  }
+}
+
+QString Signal::yunitToString() const
+{
+  switch (m_yunit) {
+    case YUnit::KILOVOLTS:
+      return KILOVOTLS_SI;
+    case YUnit::MICROAMPERES:
+      return MICROAMPERES_SI;
+    case YUnit::MILLIAU:
+      return MILLIAU_SI;
+    case YUnit::MILLIVOLTS:
+      return MILLIVOLTS_SI;
+    case YUnit::WATTS:
+      return WATTS_SI;
+    default:
+      return "?";
+  }
+}
diff --git a/signal.h b/signal.h
new file mode 100644 (file)
index 0000000..4284029
--- /dev/null
+++ b/signal.h
@@ -0,0 +1,109 @@
+/*
+  Copyright (c) 2013 Michal Malý <madcatxster@prifuk.cz>
+
+  Permission is hereby granted, free of charge, to any person obtaining a copy
+  of this software and associated documentation files (the "Software"), to deal
+  in the Software without restriction, including without limitation the rights
+  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+  copies of the Software, and to permit persons to whom the Software is
+  furnished to do so, subject to the following conditions:
+
+  The above copyright notice and this permission notice shall be included in
+  all copies or substantial portions of the Software.
+
+  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+  THE SOFTWARE.
+*/
+
+#ifndef SIGNAL_H
+#define SIGNAL_H
+
+#include <vector>
+#include <QtCore/QObject>
+
+class Signal : public QObject
+{
+  Q_OBJECT
+public:
+  typedef std::pair<double, double> TimeValuePair;
+  enum class Equipment {
+    CE
+  };
+  enum class Resource {
+    CE_ABSORBANCE,
+    CE_CCD,
+    CE_CURRENT,
+    CE_DAD,
+    CE_POWER,
+    CE_VOLTAGE
+  };
+  enum class XUnit {
+    MINUTE
+  };
+  enum class YUnit {
+    KILOVOLTS,
+    MICROAMPERES,
+    MILLIAU,
+    MILLIVOLTS,
+    WATTS
+  };
+
+  explicit Signal(const Equipment equipment, const Resource resource, const double samplingRate, const YUnit yunit, const uint16_t wavelengthAbs,
+                  const uint16_t wavelengthRef, const std::vector<TimeValuePair>& values, QObject* parent = nullptr);
+  Equipment equipment() const { return m_equipment; }
+  std::vector<TimeValuePair>::const_iterator iteratorFrom(const size_t idx) const;
+  double maximum() const { return m_max; }
+  double minimum() const { return m_min; }
+  TimeValuePair pairAt(const size_t idx) const;
+  Resource resource() const { return m_resource; }
+  QString resourceToString() const;
+  double timeAt(const size_t idx) const;
+  double samplingRate() const { return m_samplingRate; }
+  QString uidString() const;
+  double valueAt(const size_t idx) const;
+  std::vector<TimeValuePair>::const_iterator valuesBegin() const { return m_values.begin(); }
+  size_t valuesCount() const { return m_values.size(); }
+  std::vector<TimeValuePair>::const_iterator valuesEnd() const { return m_values.end(); }
+  uint16_t wavelengthAbsorbed() const { return m_wavelengthAbs; }
+  uint16_t wavelengthReference() const { return m_wavelengthRef; }
+  XUnit xunit() const { return m_xunit; }
+  QString xunitToString() const;
+  YUnit yunit() const { return m_yunit; }
+  QString yunitToString() const;
+
+private:
+  const Equipment m_equipment;
+  double m_max;
+  double m_min;
+  const Resource m_resource;
+  const double m_samplingRate;
+  const std::vector<TimeValuePair> m_values;
+  const uint16_t m_wavelengthAbs;
+  const uint16_t m_wavelengthRef;
+  const XUnit m_xunit;
+  const YUnit m_yunit;
+
+  static const QString RES_ABSORBANCE;
+  static const QString RES_CCD;
+  static const QString RES_CURRENT;
+  static const QString RES_DAD;
+  static const QString RES_POWER;
+  static const QString RES_VOLTAGE;
+  static const QString KILOVOTLS_SI;
+  static const QString MICROAMPERES_SI;
+  static const QString MILLIAU_SI;
+  static const QString MILLIVOLTS_SI;
+  static const QString WATTS_SI;
+
+signals:
+
+public slots:
+
+};
+
+#endif // SIGNAL_H
diff --git a/signalcontroller.cpp b/signalcontroller.cpp
new file mode 100644 (file)
index 0000000..65cfc96
--- /dev/null
@@ -0,0 +1,288 @@
+/*
+  Copyright (c) 2013 Michal Malý <madcatxster@prifuk.cz>
+
+  Permission is hereby granted, free of charge, to any person obtaining a copy
+  of this software and associated documentation files (the "Software"), to deal
+  in the Software without restriction, including without limitation the rights
+  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+  copies of the Software, and to permit persons to whom the Software is
+  furnished to do so, subject to the following conditions:
+
+  The above copyright notice and this permission notice shall be included in
+  all copies or substantial portions of the Software.
+
+  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+  THE SOFTWARE.
+*/
+
+#include "signalcontroller.h"
+#include "logger.h"
+#include <cfloat>
+
+#include <QDebug>
+
+const QString SignalController::ME_SENDER_STR("SignalController");
+
+SignalController::SignalController(std::shared_ptr<Signal> signal, QObject* parent) :
+  QObject(parent),
+  m_signal(signal)
+{
+  m_integrator = std::shared_ptr<Integrator>(new Integrator(signal));
+  m_integTblModel = new IntegrationTableModel(m_integrator);
+  m_dtblModel = new SignalDataTableModel(signal);
+
+  m_yMin = m_signal->minimum() - m_signal->minimum()*0.05;
+  m_yMax = m_signal->maximum() + m_signal->maximum()*0.05;
+  m_fromIdx = 0; m_toIdx = m_signal->valuesCount()-1;
+
+  connect(m_ctxMenu.m_acZoomOut, SIGNAL(triggered()), this, SLOT(onCtxMenuUnzoom()));
+  connect(m_ctxMenu.m_acDeletePeak, SIGNAL(triggered()), this, SLOT(onCtxMenuDeletePeak()));
+}
+
+/* Public methods */
+void SignalController::draw()
+{
+  drawGraph();
+  drawAllPeaks();
+}
+
+/* Private methods */
+
+void SignalController::drawAllPeaks()
+{
+  std::multimap<size_t, std::shared_ptr<IntegratedPeak>>::const_iterator cit = m_integrator->peaksCBegin();
+  while (cit != m_integrator->peaksCEnd())
+    drawIntegratedPeak((cit++)->second);
+}
+
+void SignalController::drawFullGraph()
+{
+  m_yMin = m_signal->minimum() - m_signal->minimum()*0.05;
+  m_yMax = m_signal->maximum() + m_signal->maximum()*0.05;
+  if (m_signal->valuesCount() < 1)
+    return;
+  m_fromIdx = 0; m_toIdx = m_signal->valuesCount()-1;
+
+  qDebug() << m_yMin << m_yMax;
+
+  drawGraph();
+}
+
+void SignalController::drawGraph()
+{
+  double* arr;
+  size_t len;
+
+  if (m_fromIdx > m_toIdx) {
+    Logger::log(Logger::Level::CRITICAL, ME_SENDER_STR, "From index lower than to index");
+    return;
+  }
+  if (m_fromIdx == m_toIdx)
+    return; //Nothing to zoom to
+  if (m_toIdx >= m_signal->valuesCount() || m_fromIdx >= m_signal->valuesCount()) {
+    Logger::log(Logger::Level::CRITICAL, ME_SENDER_STR, "Invalid value index");
+    return;
+  }
+  len = m_toIdx - m_fromIdx;
+
+  arr = new double[len];
+  for (uint idx = 0; idx < len; idx++)
+    arr[idx] = m_signal->valueAt(idx + m_fromIdx);
+
+  emit guiDrawGraph(arr, len, m_yMin, m_yMax);
+}
+
+void SignalController::drawIntegratedPeak(const std::shared_ptr<IntegratedPeak> peak)
+{
+  int fromX, toX;
+  double fromY, toY;
+  bool valley;
+  if (peak->toIdx() < m_fromIdx || peak->fromIdx() > m_toIdx) /* Peak is not in view X-wise, skip it */
+    return;
+
+  /* Check if the start of the baseline is in view */
+  if (peak->fromIdx()-1 >= m_fromIdx) {
+    fromX = peak->fromIdx() - m_fromIdx;
+    fromY = peak->fromY();
+  } else {
+    fromX = 0;
+    double bls = peak->baselineSlope();
+    fromY = bls * (m_fromIdx - peak->fromIdx()) + peak->fromY();
+    qDebug() << "Start OOR" << fromY << bls;
+  }
+  /* Check if the end of the baseline is in view */
+  if (peak->toIdx() <= m_toIdx) {
+    toX = peak->toIdx() - m_fromIdx;
+    toY = peak->toY();
+  } else {
+    double bls = peak->baselineSlope();
+    toX = m_toIdx - m_fromIdx;
+    toY = bls * (m_toIdx - peak->fromIdx()) + peak->fromY();
+    qDebug() << "Stop OOR" << toY << bls;
+  }
+
+  /*qDebug("Drawing integration");
+  qDebug() << "SX" << fromX << "SY" << fromY << "EX" << toX << "EY" << toY;
+  qDebug() << "Slope P" << peak->baselineSlope() << "Slope M" << (toY-fromY)/(toX-fromX);
+  qDebug("---");*/
+
+  const QString peakTime = QString::number(peak->peakTime(), 'f', 4);
+  const QString peakArea = QString("A: ") + QString::number(peak->auc(), 'f', 4);
+  if (peak->type() == IntegratedPeak::Type::PEAK)
+    valley = false;
+  else
+    valley = true;
+  emit viewDrawIntegration(fromX, fromY, toX, toY, peak->peakIdx() - m_fromIdx, m_signal->valueAt(peak->peakIdx()), peakTime, peakArea, valley);
+}
+
+bool SignalController::updateConstraints(const int fromX, const int fromY, const int toX, const int toY)
+{
+  size_t xRange = m_toIdx - m_fromIdx;
+  double yRange;
+  if (m_viewWidth < 1) {
+    Logger::log(Logger::Level::CRITICAL, ME_SENDER_STR, "Invalid viewWidth value " + QString::number(m_viewWidth));
+    return false;
+  }
+  if (m_viewHeight < 1) {
+    Logger::log(Logger::Level::CRITICAL, ME_SENDER_STR, "Invalid viewHeight value " + QString::number(m_viewHeight));
+    return false;
+  }
+  if (fromX > toX) {
+    Logger::log(Logger::Level::CRITICAL, ME_SENDER_STR, "fromX > toX");
+    return false;
+  }
+  if (fromY > toY) {
+    Logger::log(Logger::Level::CRITICAL, ME_SENDER_STR, "fromY > toY");
+    return false;
+  }
+
+  m_toIdx = (xRange * toX) / m_viewWidth + m_fromIdx;
+  m_fromIdx = (xRange * fromX) / m_viewWidth + m_fromIdx;
+
+  yRange = m_yMax - m_yMin;
+  m_yMin = (yRange * toY / -m_viewHeight) + m_yMax;
+  m_yMax = (yRange * fromY / -m_viewHeight) + m_yMax;
+
+  //qDebug() << fromX << fromY << toX << toY;
+  //qDebug() << m_fromIdx << m_toIdx << m_yMin << m_yMax;
+
+  return true;
+}
+
+double SignalController::yValToCoord(double y)
+{
+  return m_viewHeight / (m_yMax - m_yMin) * (y - m_yMin);
+}
+
+/* Public slots */
+
+void SignalController::onCtxMenuUnzoom()
+{
+  drawFullGraph();
+  drawAllPeaks();
+}
+
+void SignalController::onCtxMenuDeletePeak()
+{
+  size_t xRange = m_toIdx - m_fromIdx;
+  size_t idx = (xRange * m_ctxMenuX) / m_viewWidth + m_fromIdx;
+
+  m_integTblModel->prepReset();
+  Integrator::ReturnCode ret = m_integrator->deletePeak(idx);
+
+  switch (ret) {
+    case Integrator::ReturnCode::SUCCESS:
+      onViewRedrawNeeded();
+      break;
+    case Integrator::ReturnCode::E_NO_PEAK:
+      Logger::log(Logger::Level::DEBUG, ME_SENDER_STR, "No peak to delete.");
+      break;
+    default:
+      break;
+  }
+  m_integTblModel->doneReset();
+
+}
+
+void SignalController::onViewCrosshairErased()
+{
+  emit viewRemoveCurrentValues();
+}
+
+void SignalController::onViewCrosshairMoved(const int x, const int y)
+{
+  Q_UNUSED(y);
+
+  size_t xRange = m_toIdx - m_fromIdx;
+  int valueIdx = (xRange * x) / m_viewWidth + m_fromIdx;
+  Signal::TimeValuePair tvp = m_signal->pairAt(valueIdx);
+  emit viewUpdateCurrentValues(tvp.first, tvp.second);
+}
+
+void SignalController::onViewIntegrated(const int fromX, const int fromY, const int toX, const int toY)
+{
+  size_t xRange = m_toIdx - m_fromIdx;
+  size_t startIdx = (xRange * fromX) / m_viewWidth + m_fromIdx;
+  size_t stopIdx = (xRange * toX) / m_viewWidth + m_fromIdx;
+  double yRange = m_yMax - m_yMin;
+  double blStartY = (yRange * (m_viewHeight - fromY) / m_viewHeight) + m_yMin;
+  double blStopY = (yRange * (m_viewHeight - toY) / m_viewHeight) + m_yMin;
+  Integrator::ReturnCode ret;
+  std::shared_ptr<IntegratedPeak> peak;
+
+  m_integTblModel->prepReset();
+  ret = m_integrator->integrate(startIdx, stopIdx, blStartY, blStopY, peak);
+
+  switch (ret) {
+    case Integrator::ReturnCode::SUCCESS:
+      drawIntegratedPeak(peak);
+      break;
+    case Integrator::ReturnCode::E_NO_FIRST_INTERSECT:
+      Logger::log(Logger::Level::DEBUG, ME_SENDER_STR, "no first intersect");
+      break;
+    case Integrator::ReturnCode::E_NO_SECOND_INTERSECT:
+      Logger::log(Logger::Level::DEBUG, ME_SENDER_STR, "no first intersect");
+      break;
+    default:
+      break;
+  }
+  m_integTblModel->doneReset();
+}
+
+void SignalController::onViewRedrawNeeded()
+{
+  drawGraph();
+  drawAllPeaks();
+}
+
+void SignalController::onViewResized(const int w, const int h)
+{
+  m_viewHeight = h;
+  m_viewWidth = w;
+}
+
+void SignalController::onViewShowContextMenu(const int x, const int y, const QPoint& globalPos)
+{
+  m_ctxMenuX = x; m_ctxMenuY = y;
+  m_ctxMenu.exec(globalPos);
+  emit viewCtxMenuClosed();
+}
+
+void SignalController::onViewZoomed(const int fromX, const int fromY, const int toX, const int toY)
+{
+  if (!updateConstraints(fromX, fromY, toX, toY))
+    return;
+   drawGraph();
+   drawAllPeaks();
+}
+
+SignalController::~SignalController()
+{
+  delete m_dtblModel;
+  delete m_integTblModel;
+}
diff --git a/signalcontroller.h b/signalcontroller.h
new file mode 100644 (file)
index 0000000..a21d65c
--- /dev/null
@@ -0,0 +1,92 @@
+/*
+  Copyright (c) 2013 Michal Malý <madcatxster@prifuk.cz>
+
+  Permission is hereby granted, free of charge, to any person obtaining a copy
+  of this software and associated documentation files (the "Software"), to deal
+  in the Software without restriction, including without limitation the rights
+  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+  copies of the Software, and to permit persons to whom the Software is
+  furnished to do so, subject to the following conditions:
+
+  The above copyright notice and this permission notice shall be included in
+  all copies or substantial portions of the Software.
+
+  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+  THE SOFTWARE.
+*/
+
+#ifndef SIGNALCONTROLLER_H
+#define SIGNALCONTROLLER_H
+
+#include "gui/graphviewcontextmenu.h"
+#include "integrationtablemodel.h"
+#include "integrator.h"
+#include "signal.h"
+#include "signaldatatablemodel.h"
+#include <memory>
+#include <QtCore/QObject>
+
+class SignalController : public QObject
+{
+  Q_OBJECT
+public:
+  explicit SignalController(std::shared_ptr<Signal> signal, QObject* parent = nullptr);
+  ~SignalController();
+  void draw();
+  SignalDataTableModel* dataTableModel() { return m_dtblModel; }
+  IntegrationTableModel* integrationTableModel() { return m_integTblModel;}
+  const std::shared_ptr<Signal> signal() const { return m_signal; }
+
+private:
+  GraphViewContextMenu m_ctxMenu;
+  int m_ctxMenuX;
+  int m_ctxMenuY;
+  SignalDataTableModel* m_dtblModel;
+  uint m_fromIdx;
+  uint m_toIdx;
+  IntegrationTableModel* m_integTblModel;
+  std::shared_ptr<Integrator> m_integrator;
+  std::shared_ptr<Signal> m_signal;
+  int m_viewHeight;
+  int m_viewWidth;
+  double m_yMax;
+  double m_yMin;
+
+  void drawAllPeaks();
+  void drawFullGraph();
+  void drawGraph();
+  void drawIntegratedPeak(const std::shared_ptr<IntegratedPeak> peak);
+  bool updateConstraints(const int fromX, const int fromY, const int toX, const int toY);
+  double yValToCoord(double y);
+
+  static const QString ME_SENDER_STR;
+
+public slots:
+  void onCtxMenuUnzoom();
+  void onCtxMenuDeletePeak();
+  void onViewCrosshairErased();
+  void onViewCrosshairMoved(const int x, const int y);
+  void onViewIntegrated(const int fromX, const int fromY, const int toX, const int toY);
+  void onViewRedrawNeeded();
+  void onViewResized(const int w, const int h);
+  void onViewShowContextMenu(const int x, const int y, const QPoint& globalPos);
+  void onViewZoomed(const int fromX, const int fromY, const int toX, const int toY);
+
+signals:
+  void viewCtxMenuClosed();
+  void viewDrawIntegration(const int fromX, const double fromY, const int toX, const double toY, const int peakX, const double peakY, const QString& peakTime,
+                           const QString& peakArea, const bool valley);
+  void viewUpdateCurrentValues(double x, double y);
+  void viewRemoveCurrentValues();
+  void guiDrawGraph(double* data, size_t len, double min, double max);
+  void fillDataList();
+
+
+};
+
+#endif // SIGNALCONTROLLER_H
diff --git a/signaldatatablemodel.cpp b/signaldatatablemodel.cpp
new file mode 100644 (file)
index 0000000..e3f8544
--- /dev/null
@@ -0,0 +1,68 @@
+/*
+  Copyright (c) 2013 Michal Malý <madcatxster@prifuk.cz>
+
+  Permission is hereby granted, free of charge, to any person obtaining a copy
+  of this software and associated documentation files (the "Software"), to deal
+  in the Software without restriction, including without limitation the rights
+  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+  copies of the Software, and to permit persons to whom the Software is
+  furnished to do so, subject to the following conditions:
+
+  The above copyright notice and this permission notice shall be included in
+  all copies or substantial portions of the Software.
+
+  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+  THE SOFTWARE.
+*/
+
+#include "signaldatatablemodel.h"
+
+SignalDataTableModel::SignalDataTableModel(std::shared_ptr<Signal> signal, QObject* parent) :
+  QAbstractTableModel(parent),
+  m_signal(signal)
+{}
+
+QVariant SignalDataTableModel::data(const QModelIndex& index, int role) const
+{
+  if (role != Qt::DisplayRole)
+    return QVariant();
+  if (index.row() >= m_signal->valuesCount())
+    return QVariant();
+
+  if (index.column() == 0)
+    return m_signal->timeAt(index.row());
+  else if (index.column() == 1)
+    return m_signal->valueAt(index.row());
+
+  return QVariant();
+}
+
+QVariant SignalDataTableModel::headerData(int section, Qt::Orientation orientation, int role) const
+{
+  if (role == Qt::DisplayRole && orientation == Qt::Horizontal) {
+    switch (section) {
+      case 0:
+        return QString("Time ") + m_signal->xunitToString() + QString("]");
+      case 1:
+        return QString("Value [") + m_signal->yunitToString() + QString("]");
+      default:
+      return QVariant();
+    }
+  }
+  return QVariant();
+}
+
+int SignalDataTableModel::rowCount(const QModelIndex& parent) const
+{
+  Q_UNUSED(parent);
+
+  if (m_signal == nullptr)
+    return 0;
+  else
+    return m_signal->valuesCount();
+}
diff --git a/signaldatatablemodel.h b/signaldatatablemodel.h
new file mode 100644 (file)
index 0000000..2435a91
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+  Copyright (c) 2013 Michal Malý <madcatxster@prifuk.cz>
+
+  Permission is hereby granted, free of charge, to any person obtaining a copy
+  of this software and associated documentation files (the "Software"), to deal
+  in the Software without restriction, including without limitation the rights
+  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+  copies of the Software, and to permit persons to whom the Software is
+  furnished to do so, subject to the following conditions:
+
+  The above copyright notice and this permission notice shall be included in
+  all copies or substantial portions of the Software.
+
+  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+  THE SOFTWARE.
+*/
+
+#ifndef SIGNALDATATABLEMODEL_H
+#define SIGNALDATATABLEMODEL_H
+
+#include "signal.h"
+#include <memory>
+#include <QtCore/QAbstractTableModel>
+
+class SignalDataTableModel : public QAbstractTableModel
+{
+public:
+  SignalDataTableModel(std::shared_ptr<Signal> signal, QObject* parent = nullptr);
+  QVariant data(const QModelIndex& index, int role) const;
+  QVariant headerData(int section, Qt::Orientation orientation, int role) const;
+  int columnCount(const QModelIndex& parent) const { Q_UNUSED(parent); return 2; }
+  int rowCount(const QModelIndex& parent) const;
+
+private:
+  std::shared_ptr<Signal> m_signal;
+};
+
+#endif // SIGNALDATATABLEMODEL_H
diff --git a/singlerundata.cpp b/singlerundata.cpp
new file mode 100644 (file)
index 0000000..fd5ef76
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+  Copyright (c) 2013 Michal Malý <madcatxster@prifuk.cz>
+
+  Permission is hereby granted, free of charge, to any person obtaining a copy
+  of this software and associated documentation files (the "Software"), to deal
+  in the Software without restriction, including without limitation the rights
+  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+  copies of the Software, and to permit persons to whom the Software is
+  furnished to do so, subject to the following conditions:
+
+  The above copyright notice and this permission notice shall be included in
+  all copies or substantial portions of the Software.
+
+  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+  THE SOFTWARE.
+*/
+
+#include "singlerundata.h"
+
+SingleRunData::SingleRunData(const QString& methodName, const QString& operatorName, const QString& sampleInfo, const QDate date, const QTime time,
+                             std::vector<std::shared_ptr<Signal>>& sigs, const QString& dirname, QObject *parent) :
+  QObject(parent),
+  m_date(date),
+  m_dirName(dirname),
+  m_methodName(methodName),
+  m_operatorName(operatorName),
+  m_sampleInfo(sampleInfo),
+  m_signals(sigs),
+  m_time(time)
+{
+}
diff --git a/singlerundata.h b/singlerundata.h
new file mode 100644 (file)
index 0000000..2fdd88c
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+  Copyright (c) 2013 Michal Malý <madcatxster@prifuk.cz>
+
+  Permission is hereby granted, free of charge, to any person obtaining a copy
+  of this software and associated documentation files (the "Software"), to deal
+  in the Software without restriction, including without limitation the rights
+  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+  copies of the Software, and to permit persons to whom the Software is
+  furnished to do so, subject to the following conditions:
+
+  The above copyright notice and this permission notice shall be included in
+  all copies or substantial portions of the Software.
+
+  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+  THE SOFTWARE.
+*/
+
+#ifndef SINGLERUNDATA_H
+#define SINGLERUNDATA_H
+
+#include "signal.h"
+#include <memory>
+#include <QtCore/QDate>
+#include <QtCore/QObject>
+#include <QtCore/QTime>
+
+class SingleRunData : public QObject
+{
+  Q_OBJECT
+public:
+  explicit SingleRunData(const QString& methodName, const QString& operatorName, const QString& sampleInfo, const QDate date, const QTime time,
+                         std::vector<std::shared_ptr<Signal>>& sigs, const QString& dirname, QObject *parent = 0);
+  QDate date() const { return m_date; }
+  QString dirName() const { return m_dirName; }
+  QString methodName() const { return m_methodName; }
+  QString operatorName() const { return m_operatorName; }
+  QString sampleInfo() const { return m_sampleInfo; }
+  std::shared_ptr<Signal> signalAt(size_t idx) { return m_signals.at(idx); }
+  size_t signalCount() const { return m_signals.size(); }
+  QTime time() const { return m_time; }
+
+private:
+  const QDate m_date;
+  const QString m_dirName;
+  const QString m_methodName;
+  const QString m_operatorName;
+  const QString m_sampleInfo;
+  std::vector<std::shared_ptr<Signal>> m_signals;
+  const QTime m_time;
+
+signals:
+
+public slots:
+
+};
+
+#endif // SINGLERUNDATA_H
diff --git a/singlerunsselectormodel.cpp b/singlerunsselectormodel.cpp
new file mode 100644 (file)
index 0000000..4ee0293
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+  Copyright (c) 2013 Michal Malý <madcatxster@prifuk.cz>
+
+  Permission is hereby granted, free of charge, to any person obtaining a copy
+  of this software and associated documentation files (the "Software"), to deal
+  in the Software without restriction, including without limitation the rights
+  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+  copies of the Software, and to permit persons to whom the Software is
+  furnished to do so, subject to the following conditions:
+
+  The above copyright notice and this permission notice shall be included in
+  all copies or substantial portions of the Software.
+
+  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+  THE SOFTWARE.
+*/
+
+#include "singlerunsselectormodel.h"
+
+#include <QDebug>
+
+SingleRunsSelectorModel::SingleRunsSelectorModel(QObject *parent) :
+  QAbstractListModel(parent),
+  m_singleRuns(nullptr)
+{
+}
+
+QVariant SingleRunsSelectorModel::data(const QModelIndex& index, int role) const
+{
+  int runIdx = 0;
+  if (role != Qt::DisplayRole)
+    return QVariant();
+  if (m_singleRuns == nullptr)
+    return QVariant();
+
+  std::vector<SingleRunPair>::const_iterator cit = m_singleRuns->cbegin();
+  while (cit != m_singleRuns->cend()) {
+    if (runIdx == index.row())
+      return cit->first;
+    runIdx++; cit++;
+  }
+  return QVariant();
+}
+
+int SingleRunsSelectorModel::rowCount(const QModelIndex& parent) const
+{
+  Q_UNUSED(parent);
+
+  if (m_singleRuns == nullptr)
+    return 0;
+  else
+    return m_singleRuns->size();
+}
+
+void SingleRunsSelectorModel::setSingleRuns(const std::vector<SingleRunPair>* singleRuns)
+{
+  beginResetModel();
+  m_singleRuns = singleRuns;
+  endResetModel();
+}
diff --git a/singlerunsselectormodel.h b/singlerunsselectormodel.h
new file mode 100644 (file)
index 0000000..a343ab3
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+  Copyright (c) 2013 Michal Malý <madcatxster@prifuk.cz>
+
+  Permission is hereby granted, free of charge, to any person obtaining a copy
+  of this software and associated documentation files (the "Software"), to deal
+  in the Software without restriction, including without limitation the rights
+  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+  copies of the Software, and to permit persons to whom the Software is
+  furnished to do so, subject to the following conditions:
+
+  The above copyright notice and this permission notice shall be included in
+  all copies or substantial portions of the Software.
+
+  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+  THE SOFTWARE.
+*/
+
+#ifndef SINGLERUNSSELECTORMODEL_H
+#define SINGLERUNSSELECTORMODEL_H
+
+#include "sequence.h"
+#include <QAbstractListModel>
+
+class SingleRunsSelectorModel : public QAbstractListModel
+{
+  Q_OBJECT
+public:
+  explicit SingleRunsSelectorModel(QObject* parent = nullptr);
+  QVariant data(const QModelIndex& index, int role) const;
+  int rowCount(const QModelIndex& parent) const;
+  void setSingleRuns(const std::vector<SingleRunPair>* singleRuns);
+
+private:
+  const std::vector<SingleRunPair>* m_singleRuns;
+
+signals:
+
+public slots:
+
+};
+
+#endif // SINGLERUNSSELECTORMODEL_H