Browse Source

Refactored AircraftWidget

Unified construction, signals and slots usage and naming between PilotsWidget and AircraftWidget.

Tweaks to handling of NULL values in the database when creating it from scratch or committing entries.
Felix Turowsky 4 years ago
parent
commit
62a5d05c6f
53 changed files with 1098 additions and 377 deletions
  1. 3 2
      mainwindow.cpp
  2. 1 0
      mainwindow.h
  3. 3 2
      mainwindow.ui
  4. 11 3
      openPilotLog.pro
  5. 8 6
      src/classes/adownload.cpp
  6. 1 1
      src/classes/adownload.h
  7. 1 1
      src/classes/aircraft.cpp
  8. 1 1
      src/classes/flight.cpp
  9. 1 1
      src/classes/pilot.cpp
  10. 31 25
      src/database/adatabasesetup.cpp
  11. 1 1
      src/database/db.cpp
  12. 1 1
      src/database/dbinfo.cpp
  13. 1 1
      src/database/entry_deprecated.cpp
  14. 0 8
      src/experimental/ADebug.h
  15. 34 0
      src/experimental/aaircraftentry.cpp
  16. 20 0
      src/experimental/aaircraftentry.h
  17. 103 15
      src/experimental/adatabase.cpp
  18. 48 4
      src/experimental/adatabase.h
  19. 17 0
      src/experimental/aentry.cpp
  20. 17 0
      src/experimental/aentry.h
  21. 17 0
      src/experimental/apilotentry.cpp
  22. 17 0
      src/experimental/apilotentry.h
  23. 34 0
      src/experimental/atailentry.cpp
  24. 37 0
      src/experimental/atailentry.h
  25. 2 0
      src/experimental/decl.h
  26. 1 1
      src/functions/acalc.cpp
  27. 1 1
      src/functions/astat.cpp
  28. 2 2
      src/gui/dialogues/newflightdialog.cpp
  29. 1 1
      src/gui/dialogues/newpilotdialog.cpp
  30. 0 2
      src/gui/dialogues/newpilotdialog.h
  31. 55 1
      src/gui/dialogues/newtail.ui
  32. 139 120
      src/gui/dialogues/newtaildialog.cpp
  33. 29 23
      src/gui/dialogues/newtaildialog.h
  34. 40 24
      src/gui/widgets/aircraftwidget.cpp
  35. 6 5
      src/gui/widgets/aircraftwidget.h
  36. 2 2
      src/gui/widgets/aircraftwidget.ui
  37. 59 2
      src/gui/widgets/debugwidget.cpp
  38. 10 0
      src/gui/widgets/debugwidget.h
  39. 54 44
      src/gui/widgets/debugwidget.ui
  40. 1 6
      src/gui/widgets/homewidget.cpp
  41. 0 3
      src/gui/widgets/homewidget.h
  42. 11 21
      src/gui/widgets/homewidget.ui
  43. 1 1
      src/gui/widgets/logbookwidget.cpp
  44. 25 28
      src/gui/widgets/pilotswidget.cpp
  45. 3 5
      src/gui/widgets/pilotswidget.h
  46. 9 9
      src/gui/widgets/pilotswidget.ui
  47. 1 1
      src/gui/widgets/settingswidget.cpp
  48. 1 1
      src/gui/widgets/totalswidget.cpp
  49. 73 0
      src/testing/abenchmark.cpp
  50. 45 0
      src/testing/abenchmark.h
  51. 1 2
      src/testing/adebug.h
  52. 59 0
      src/testing/atimer.cpp
  53. 59 0
      src/testing/atimer.h

+ 3 - 2
mainwindow.cpp

@@ -17,12 +17,13 @@
  */
 #include "mainwindow.h"
 #include "ui_mainwindow.h"
-#include "src/functions/adebug.h"
+#include "src/testing/adebug.h"
 
 MainWindow::MainWindow(QWidget *parent)
     : QMainWindow(parent)
     , ui(new Ui::MainWindow)
 {
+    ATimer timer(this);
     ui->setupUi(this);
 
     // Set up Toolbar
@@ -132,7 +133,7 @@ void MainWindow::on_actionNewFlight_triggered()
 
 void MainWindow::on_actionNewAircraft_triggered()
 {
-    NewTailDialog nt = NewTailDialog(QString(), Db::createNew, this);
+    NewTailDialog nt = NewTailDialog(QString(), this);
     nt.exec();
 }
 

+ 1 - 0
mainwindow.h

@@ -37,6 +37,7 @@
 #include "src/gui/dialogues/newpilotdialog.h"
 #include "src/classes/arunguard.h"
 #include "src/experimental/adatabase.h"
+#include "src/testing/atimer.h"
 
 QT_BEGIN_NAMESPACE
 namespace Ui {

+ 3 - 2
mainwindow.ui

@@ -17,8 +17,9 @@
    <layout class="QGridLayout" name="gridLayout">
     <item row="0" column="0">
      <widget class="QStackedWidget" name="stackedWidget">
-      <widget class="QWidget" name="page"/>
-      <widget class="QWidget" name="page_2"/>
+      <property name="currentIndex">
+       <number>-1</number>
+      </property>
      </widget>
     </item>
    </layout>

+ 11 - 3
openPilotLog.pro

@@ -29,9 +29,11 @@ SOURCES += \
     src/database/db.cpp \
     src/database/dbinfo.cpp \
     src/database/entry_deprecated.cpp \
+    src/experimental/aaircraftentry.cpp \
     src/experimental/adatabase.cpp \
     src/experimental/aentry.cpp \
     src/experimental/apilotentry.cpp \
+    src/experimental/atailentry.cpp \
     src/functions/acalc.cpp \
     src/functions/areadcsv.cpp \
     src/functions/astat.cpp \
@@ -45,7 +47,9 @@ SOURCES += \
     src/gui/widgets/logbookwidget.cpp \
     src/gui/widgets/pilotswidget.cpp \
     src/gui/widgets/settingswidget.cpp \
-    src/gui/widgets/totalswidget.cpp
+    src/gui/widgets/totalswidget.cpp \
+    src/testing/abenchmark.cpp \
+    src/testing/atimer.cpp
 
 HEADERS += \
     mainwindow.h \
@@ -61,12 +65,13 @@ HEADERS += \
     src/database/dbinfo.h \
     src/database/entry_deprecated.h \
     src/experimental/UserInput.h \
+    src/experimental/aaircraftentry.h \
     src/experimental/adatabase.h \
     src/experimental/aentry.h \
     src/experimental/apilotentry.h \
+    src/experimental/atailentry.h \
     src/experimental/decl.h \
     src/functions/acalc.h \
-    src/functions/src/functions/adebug.h \
     src/functions/areadcsv.h \
     src/functions/astat.h \
     src/gui/dialogues/firstrundialog.h \
@@ -79,7 +84,10 @@ HEADERS += \
     src/gui/widgets/logbookwidget.h \
     src/gui/widgets/pilotswidget.h \
     src/gui/widgets/settingswidget.h \
-    src/gui/widgets/totalswidget.h
+    src/gui/widgets/totalswidget.h \
+    src/testing/abenchmark.h \
+    src/testing/adebug.h \
+    src/testing/atimer.h
 
 FORMS += \
     mainwindow.ui \

+ 8 - 6
src/classes/adownload.cpp

@@ -16,7 +16,7 @@
  *along with this program.  If not, see <https://www.gnu.org/licenses/>.
  */
 #include "adownload.h"
-#include "src/functions/adebug.h"
+#include "src/testing/adebug.h"
 
 
 
@@ -46,13 +46,15 @@ void ADownload::download()
     QNetworkRequest request(target);
     DEB("Downloading from: " << target.toString());
 
-    //QObject::connect(manager.get(request), SIGNAL(downloadProgress(qint64,qint64)), this, SLOT(downloadProgress(qint64,qint64)));
+    QObject::connect(manager.get(request), SIGNAL(downloadProgress(qint64,qint64)), this, SLOT(downloadProgress(qint64,qint64)));
+}
+
+
+void ADownload::downloadProgress(qint64 received, qint64 total)
+{
+    DEB("Received " << received << " bytes of " << total);
 }
 
-/* not needed for now
- * void ADownload::downloadProgress(qint64 received, qint64 total)
- * {}
- */
 
 
 void ADownload::downloadFinished(QNetworkReply *data)

+ 1 - 1
src/classes/adownload.h

@@ -51,7 +51,7 @@ signals:
 
 public slots:
     void downloadFinished(QNetworkReply* data);
-    //void downloadProgress(qint64 received, qint64 total); //not needed for now
+    void downloadProgress(qint64 received, qint64 total);
 };
 
 #endif // ADOWNLOAD_H

+ 1 - 1
src/classes/aircraft.cpp

@@ -16,7 +16,7 @@
  *along with this program.  If not, see <https://www.gnu.org/licenses/>.
  */
 #include "aircraft.h"
-#include "src/functions/adebug.h"
+#include "src/testing/adebug.h"
 
 
 Aircraft::Aircraft()

+ 1 - 1
src/classes/flight.cpp

@@ -16,7 +16,7 @@
  *along with this program.  If not, see <https://www.gnu.org/licenses/>.
  */
 #include "flight.h"
-#include "src/functions/adebug.h"
+#include "src/testing/adebug.h"
 
 Flight::Flight()
 {

+ 1 - 1
src/classes/pilot.cpp

@@ -16,7 +16,7 @@
  *along with this program.  If not, see <https://www.gnu.org/licenses/>.
  */
 #include "pilot.h"
-#include "src/functions/adebug.h"
+#include "src/testing/adebug.h"
 
 
 

+ 31 - 25
src/database/adatabasesetup.cpp

@@ -16,7 +16,7 @@
  *along with this program.  If not, see <https://www.gnu.org/licenses/>.
  */
 #include "adatabasesetup.h"
-#include "src/functions/adebug.h"
+#include "src/testing/adebug.h"
 
 
 // Statements for creation of database tables, Revision 12
@@ -157,8 +157,6 @@ const QString createViewDefault = "CREATE VIEW viewDefault AS "
         "INNER JOIN tails on flights.acft = tails.tail_id "
         "ORDER BY date DESC ";
 
-
-
 const QString createViewEASA = "CREATE VIEW viewEASA AS "
         "SELECT "
         "flight_id, doft as 'Date', "
@@ -192,41 +190,49 @@ const QString createViewEASA = "CREATE VIEW viewEASA AS "
 
 const QString createViewTails = "CREATE VIEW viewTails AS "
         "SELECT "
-        "tail_id AS 'ID', registration AS 'Registration', "
-        "make||' '||model||'-'||variant AS 'Type', "
-        "company AS 'Company' "
-        "FROM tails";
+        "tail_id AS \"ID\", "
+        "registration AS \"Registration\", "
+        "make||\" \"||model AS \"Type\", "
+        "company AS \"Company\" "
+        "FROM tails WHERE model IS NOT NULL AND variant IS NULL "
+        "UNION "
+        "SELECT "
+        "tail_id AS \"ID\", "
+        "registration AS \"Registration\", "
+        "make||\" \"||model||\"-\"||variant AS \"Type\", "
+        "company AS \"Company\" "
+        "FROM tails WHERE variant IS NOT NULL";
 
 const QString createViewPilots = "CREATE VIEW viewPilots AS "
         "SELECT "
-        "pilot_id AS 'ID', "
-        "piclastname AS 'Last Name', "
-        "picfirstname AS 'First Name', company AS 'Company' "
+        "pilot_id AS \"ID\", "
+        "piclastname AS \"Last Name\", "
+        "picfirstname AS \"First Name\", company AS \"Company\" "
         "FROM pilots";
 
 const QString createViewQCompleter = "CREATE VIEW viewQCompleter AS "
         "SELECT airport_id, icao, iata, tail_id, registration, pilot_id, "
-        "piclastname||', '||picfirstname AS 'pilot_name', alias "
+        "piclastname||\", \"||picfirstname AS \"pilot_name\", alias "
         "FROM airports "
         "LEFT JOIN tails ON airports.airport_id = tails.tail_id "
         "LEFT JOIN pilots ON airports.airport_id = pilots.pilot_id";
 
 const QString createViewTotals = "CREATE VIEW viewTotals AS "
         "SELECT "
-        "printf('%02d',CAST(SUM(tblk) AS INT)/60)||':'||printf('%02d',CAST(SUM(tblk) AS INT)%60) AS 'TOTAL', "
-        "printf('%02d',CAST(SUM(tSPSE) AS INT)/60)||':'||printf('%02d',CAST(SUM(tSPSE) AS INT)%60) AS 'SP SE', "
-        "printf('%02d',CAST(SUM(tSPME) AS INT)/60)||':'||printf('%02d',CAST(SUM(tSPME) AS INT)%60) AS 'SP ME', "
-        "printf('%02d',CAST(SUM(tNIGHT) AS INT)/60)||':'||printf('%02d',CAST(SUM(tNIGHT) AS INT)%60) AS 'NIGHT', "
-        "printf('%02d',CAST(SUM(tIFR) AS INT)/60)||':'||printf('%02d',CAST(SUM(tIFR) AS INT)%60) AS 'IFR', "
-        "printf('%02d',CAST(SUM(tPIC) AS INT)/60)||':'||printf('%02d',CAST(SUM(tPIC) AS INT)%60) AS 'PIC', "
-        "printf('%02d',CAST(SUM(tPICUS) AS INT)/60)||':'||printf('%02d',CAST(SUM(tPICUS) AS INT)%60) AS 'PICUS', "
-        "printf('%02d',CAST(SUM(tSIC) AS INT)/60)||':'||printf('%02d',CAST(SUM(tSIC) AS INT)%60) AS 'SIC', "
-        "printf('%02d',CAST(SUM(tDual) AS INT)/60)||':'||printf('%02d',CAST(SUM(tDual) AS INT)%60) AS 'DUAL', "
-        "printf('%02d',CAST(SUM(tFI) AS INT)/60)||':'||printf('%02d',CAST(SUM(tFI) AS INT)%60) AS 'INSTRUCTOR', "
-        "printf('%02d',CAST(SUM(tSIM) AS INT)/60)||':'||printf('%02d',CAST(SUM(tSIM) AS INT)%60) AS 'SIMULATOR', "
-        "printf('%02d',CAST(SUM(tMP) AS INT)/60)||':'||printf('%02d',CAST(SUM(tMP) AS INT)%60) AS 'MultPilot', "
-        "CAST(SUM(toDay) AS INT) AS 'TO Day', CAST(SUM(toNight) AS INT) AS 'TO Night', "
-        "CAST(SUM(ldgDay) AS INT) AS 'LDG Day', CAST(SUM(ldgNight) AS INT) AS 'LDG Night' "
+        "printf(\"%02d\",CAST(SUM(tblk) AS INT)/60)||\":\"||printf(\"%02d\",CAST(SUM(tblk) AS INT)%60) AS \"TOTAL\", "
+        "printf(\"%02d\",CAST(SUM(tSPSE) AS INT)/60)||\":\"||printf(\"%02d\",CAST(SUM(tSPSE) AS INT)%60) AS \"SP SE\", "
+        "printf(\"%02d\",CAST(SUM(tSPME) AS INT)/60)||\":\"||printf(\"%02d\",CAST(SUM(tSPME) AS INT)%60) AS \"SP ME\", "
+        "printf(\"%02d\",CAST(SUM(tNIGHT) AS INT)/60)||\":\"||printf(\"%02d\",CAST(SUM(tNIGHT) AS INT)%60) AS \"NIGHT\", "
+        "printf(\"%02d\",CAST(SUM(tIFR) AS INT)/60)||\":\"||printf(\"%02d\",CAST(SUM(tIFR) AS INT)%60) AS \"IFR\", "
+        "printf(\"%02d\",CAST(SUM(tPIC) AS INT)/60)||\":\"||printf(\"%02d\",CAST(SUM(tPIC) AS INT)%60) AS \"PIC\", "
+        "printf(\"%02d\",CAST(SUM(tPICUS) AS INT)/60)||\":\"||printf(\"%02d\",CAST(SUM(tPICUS) AS INT)%60) AS \"PICUS\", "
+        "printf(\"%02d\",CAST(SUM(tSIC) AS INT)/60)||\":\"||printf(\"%02d\",CAST(SUM(tSIC) AS INT)%60) AS \"SIC\", "
+        "printf(\"%02d\",CAST(SUM(tDual) AS INT)/60)||\":\"||printf(\"%02d\",CAST(SUM(tDual) AS INT)%60) AS \"DUAL\", "
+        "printf(\"%02d\",CAST(SUM(tFI) AS INT)/60)||\":\"||printf(\"%02d\",CAST(SUM(tFI) AS INT)%60) AS \"INSTRUCTOR\", "
+        "printf(\"%02d\",CAST(SUM(tSIM) AS INT)/60)||\":\"||printf(\"%02d\",CAST(SUM(tSIM) AS INT)%60) AS \"SIMULATOR\", "
+        "printf(\"%02d\",CAST(SUM(tMP) AS INT)/60)||\":\"||printf(\"%02d\",CAST(SUM(tMP) AS INT)%60) AS \"MultPilot\", "
+        "CAST(SUM(toDay) AS INT) AS \"TO Day\", CAST(SUM(toNight) AS INT) AS \"TO Night\", "
+        "CAST(SUM(ldgDay) AS INT) AS \"LDG Day\", CAST(SUM(ldgNight) AS INT) AS \"LDG Night\" "
         "FROM flights";
 const QStringList tables = {
     createTablePilots,

+ 1 - 1
src/database/db.cpp

@@ -17,7 +17,7 @@
  */
 #include "db.h"
 #include "dbinfo.h"
-#include "src/functions/adebug.h"
+#include "src/testing/adebug.h"
 
 /*!
  * \brief Db::iconnect - see Db::connect

+ 1 - 1
src/database/dbinfo.cpp

@@ -16,7 +16,7 @@
  *along with this program.  If not, see <https://www.gnu.org/licenses/>.
  */
 #include "dbinfo.h"
-#include "src/functions/adebug.h"
+#include "src/testing/adebug.h"
 
 DbInfo::DbInfo()
 {

+ 1 - 1
src/database/entry_deprecated.cpp

@@ -16,7 +16,7 @@
  *along with this program.  If not, see <https://www.gnu.org/licenses/>.
  */
 #include "entry_deprecated.h"
-#include "src/functions/adebug.h"
+#include "src/testing/adebug.h"
 #include "db.h"
 
 Entry_deprecated::Entry_deprecated()

+ 0 - 8
src/experimental/ADebug.h

@@ -1,8 +0,0 @@
-#ifndef ADEBUG_H
-#define ADEBUG_H
-
-#include <QDebug>
-
-#define DEB qDebug()
-
-#endif

+ 34 - 0
src/experimental/aaircraftentry.cpp

@@ -0,0 +1,34 @@
+/*
+ *openTail Log - A FOSS Tail Logbook Application
+ *Copyright (C) 2020  Felix Turowsky
+ *
+ *This program is free software: you can redistribute it and/or modify
+ *it under the terms of the GNU General Public License as published by
+ *the Free Software Foundation, either version 3 of the License, or
+ *(at your option) any later version.
+ *
+ *This program is distributed in the hope that it will be useful,
+ *but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *GNU General Public License for more details.
+ *
+ *You should have received a copy of the GNU General Public License
+ *along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+#include "aaircraftentry.h"
+
+namespace experimental {
+
+AAircraftEntry::AAircraftEntry()
+    : AEntry::AEntry(DEFAULT_AIRCRAFT_POSITION)
+{}
+
+AAircraftEntry::AAircraftEntry(int row_id)
+    : AEntry::AEntry(DataPosition("aircraft", row_id))
+{}
+
+AAircraftEntry::AAircraftEntry(TableData table_data)
+    : AEntry::AEntry(DEFAULT_AIRCRAFT_POSITION, table_data)
+{}
+
+} // namespace experimental

+ 20 - 0
src/experimental/aaircraftentry.h

@@ -0,0 +1,20 @@
+#ifndef AAIRCRAFTENTRY_H
+#define AAIRCRAFTENTRY_H
+
+#include "src/experimental/aentry.h"
+#include "src/experimental/decl.h"
+
+namespace experimental {
+
+struct AAircraftEntry : public AEntry {
+public:
+    AAircraftEntry();
+    AAircraftEntry(const AAircraftEntry& te) = default;
+    AAircraftEntry& operator=(const AAircraftEntry& te) = default;
+    AAircraftEntry(int row_id);
+    AAircraftEntry(TableData table_data);
+};
+
+} // namespace experimental
+
+#endif // AAIRCRAFTENTRY_H

+ 103 - 15
src/experimental/adatabase.cpp

@@ -1,3 +1,20 @@
+/*
+ *openPilot Log - A FOSS Pilot Logbook Application
+ *Copyright (C) 2020  Felix Turowsky
+ *
+ *This program is free software: you can redistribute it and/or modify
+ *it under the terms of the GNU General Public License as published by
+ *the Free Software Foundation, either version 3 of the License, or
+ *(at your option) any later version.
+ *
+ *This program is distributed in the hope that it will be useful,
+ *but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *GNU General Public License for more details.
+ *
+ *You should have received a copy of the GNU General Public License
+ *along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
 #include "adatabase.h"
 
 namespace experimental {
@@ -93,14 +110,21 @@ bool ADataBase::remove(AEntry entry)
 
 bool ADataBase::exists(AEntry entry)
 {
-    if(entry.getPosition() == DEFAULT_PILOT_POSITION)
+    if(entry.getPosition().second == 0)
         return false;
 
     //Check database for row id
     QString statement = "SELECT COUNT(*) FROM " + entry.getPosition().tableName +
             " WHERE ROWID=" + QString::number(entry.getPosition().rowId);
     //this returns either 1 or 0 since row ids are unique
-    QSqlQuery query(statement);
+    QSqlQuery query;
+    query.prepare(statement);
+    query.setForwardOnly(true);
+    query.exec();
+    if (!query.isActive()) {
+        emit sqlError(query.lastError(), statement);
+        DEB("Query Error: " << query.lastError().text() << statement);
+    }
     query.next();
     int rowId = query.value(0).toInt();
     if (rowId) {
@@ -118,10 +142,10 @@ bool ADataBase::update(AEntry updated_entry)
     auto data = updated_entry.getData();
     QString statement = "UPDATE " + updated_entry.getPosition().tableName + " SET ";
     for (auto i = data.constBegin(); i != data.constEnd(); ++i) {
-        if (i.key() != QString()) {
-            statement += i.key() + QLatin1String("=\"") + i.value() + QLatin1String("\", ");
+        if (i.value() == QString()) {
+            statement += i.key() + QLatin1String("=NULL") + QLatin1String(", ");
         } else {
-            DEB(i.key() << "is empty key. skipping.");
+            statement += i.key() + QLatin1String("=\"") + i.value() + QLatin1String("\", ");
         }
     }
     statement.chop(2); // Remove last comma
@@ -156,10 +180,15 @@ bool ADataBase::insert(AEntry new_entry)
     statement.chop(2);
     statement += QLatin1String(") VALUES (");
     for (i = data.begin(); i != data.end(); ++i) {
-        statement += QLatin1String("\"") + i.value() + QLatin1String("\", ");
+        if (i.value() == "") {
+            statement += QLatin1String("NULL, ");
+        } else {
+            statement += QLatin1String("\"") + i.value() + QLatin1String("\", ");
+        }
     }
     statement.chop(2);
     statement += QLatin1String(")");
+    DEB(statement);
 
     QSqlQuery query(statement);
     //check result.
@@ -189,7 +218,10 @@ TableData ADataBase::getEntryData(DataPosition data_position)
     //Check Database for rowId
     QString statement = "SELECT COUNT(*) FROM " + data_position.first
                       + " WHERE ROWID=" + QString::number(data_position.second);
-    QSqlQuery check_query(statement);
+    QSqlQuery check_query;
+    check_query.prepare(statement);
+    check_query.setForwardOnly(true);
+    check_query.exec();
 
     if (check_query.lastError().type() != QSqlError::NoError) {
         DEB("SQL error: " << check_query.lastError().text());
@@ -208,7 +240,11 @@ TableData ADataBase::getEntryData(DataPosition data_position)
     statement = "SELECT * FROM " + data_position.first
               + " WHERE ROWID=" + QString::number(data_position.second);
 
-    QSqlQuery select_query(statement);
+    QSqlQuery select_query;
+    select_query.prepare(statement);
+    select_query.setForwardOnly(true);
+    select_query.exec();
+
     if (select_query.lastError().type() != QSqlError::NoError) {
         DEB("SQL error: " << select_query.lastError().text());
         DEB("Statement: " << statement);
@@ -233,12 +269,26 @@ AEntry ADataBase::getEntry(DataPosition data_position)
 
 APilotEntry ADataBase::getPilotEntry(RowId row_id)
 {
-    APilotEntry pilotEntry(row_id);
-    pilotEntry.setData(getEntryData(pilotEntry.getPosition()));
-    return pilotEntry;
+    APilotEntry pilot_entry(row_id);
+    pilot_entry.setData(getEntryData(pilot_entry.getPosition()));
+    return pilot_entry;
+}
+
+ATailEntry ADataBase::getTailEntry(RowId row_id)
+{
+    ATailEntry tail_entry(row_id);
+    tail_entry.setData(getEntryData(tail_entry.getPosition()));
+    return tail_entry;
+}
+
+AAircraftEntry ADataBase::getAircraftEntry(RowId row_id)
+{
+    AAircraftEntry aircraft_entry(row_id);
+    aircraft_entry.setData(getEntryData(aircraft_entry.getPosition()));
+    return aircraft_entry;
 }
 
-QStringList ADataBase::getCompletionList(ADataBase::CompleterTarget target)
+const QStringList ADataBase::getCompletionList(ADataBase::CompleterTarget target)
 {
     QString statement;
 
@@ -262,11 +312,14 @@ QStringList ADataBase::getCompletionList(ADataBase::CompleterTarget target)
         break;
     }
 
-    QSqlQuery query(statement);
-    if(!query.first())
+    QSqlQuery query;
+    query.prepare(statement);
+    query.setForwardOnly(true);
+    query.exec();
+
+    if(!query.isActive())
         emit sqlError(query.lastError(), statement);
 
-    query.previous();
     QStringList completer_list;
     while (query.next())
         completer_list.append(query.value(0).toString());
@@ -278,6 +331,41 @@ QStringList ADataBase::getCompletionList(ADataBase::CompleterTarget target)
     return completer_list;
 }
 
+const QMap<QString, int> ADataBase::getIdMap(ADataBase::CompleterTarget target)
+{
+    QString statement;
+
+    switch (target) {
+    case pilots:
+        statement.append("SELECT ROWID, piclastname||\",\"||picfirstname FROM pilots");
+        break;
+    case aircraft:
+        statement.append("SELECT ROWID, make||\" \"||model FROM aircraft WHERE model IS NOT NULL "
+                         "UNION "
+                         "SELECT ROWID, make||\" \"||model||\"-\"||variant FROM aircraft WHERE variant IS NOT NULL");
+        break;
+    default:
+        DEB("Not a valid completer target for this function.");
+        return QMap<QString, int>();
+    }
+
+    auto id_map = QMap<QString, int>();
+    auto query = QSqlQuery(statement);
+    if (!query.isActive()) {
+        DEB("No result found. Check Query and Error.");
+        DEB("Query: " << statement);
+        DEB("Error: " << query.lastError().text());
+        emit sqlError(query.lastError(), statement);
+        return QMap<QString, int>();
+    } else {
+        QVector<QString> query_result;
+        while (query.next()) {
+            id_map.insert(query.value(1).toString(), query.value(0).toInt());
+        }
+        return id_map;
+    }
+}
+
 QVector<QString> ADataBase::customQuery(QString statement, int return_values)
 {
     QSqlQuery query(statement);

+ 48 - 4
src/experimental/adatabase.h

@@ -1,3 +1,20 @@
+/*
+ *openPilot Log - A FOSS Pilot Logbook Application
+ *Copyright (C) 2020  Felix Turowsky
+ *
+ *This program is free software: you can redistribute it and/or modify
+ *it under the terms of the GNU General Public License as published by
+ *the Free Software Foundation, either version 3 of the License, or
+ *(at your option) any later version.
+ *
+ *This program is distributed in the hope that it will be useful,
+ *but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *GNU General Public License for more details.
+ *
+ *You should have received a copy of the GNU General Public License
+ *along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
 #ifndef __DB_H__
 #define __DB_H__
 
@@ -8,10 +25,12 @@
 #include <QSqlError>
 #include <QSqlTableModel>
 #include "src/database/dbinfo.h"
-#include "src/functions/adebug.h"
+#include "src/testing/adebug.h"
 
 #include "aentry.h"
 #include "apilotentry.h"
+#include "atailentry.h"
+#include "aaircraftentry.h"
 
 namespace experimental {
 
@@ -106,15 +125,40 @@ public:
      * with only the RowId required as input.
      */
     APilotEntry getPilotEntry(RowId row_id);
-    // [G] TODO: Ensure PilotDialog works great and slowly move to
-    // other dialogs
+
+    /*!
+     * \brief retreives a TailEntry from the database.
+     *
+     * This function is a wrapper for DataBase::getEntry(DataPosition),
+     * where the table is already set and which returns a TailEntry
+     * instead of an Entry. It allows for easy access to a tail entry
+     * with only the RowId required as input.
+     */
+    ATailEntry getTailEntry(RowId row_id);
+
+    /*!
+     * \brief retreives a TailEntry from the database.
+     *
+     * This function is a wrapper for DataBase::getEntry(DataPosition),
+     * where the table is already set and which returns an AAircraftEntry
+     * instead of an AEntry. It allows for easy access to an aircraft entry
+     * with only the RowId required as input.
+     */
+    AAircraftEntry getAircraftEntry(RowId row_id);
 
     /*!
      * \brief getCompletionList returns a QStringList of values for a
      * QCompleter based on database values
      * \return
      */
-    QStringList getCompletionList(CompleterTarget);
+    const QStringList getCompletionList(CompleterTarget);
+
+    /*!
+     * \brief getIdMap returns a map of a human-readable database value and
+     * its row id. Used in the Dialogs to map user input to unique database entries.
+     * \return
+     */
+    const QMap<QString, int> getIdMap(CompleterTarget);
 signals:
     void sqlSuccessful();
 

+ 17 - 0
src/experimental/aentry.cpp

@@ -1,3 +1,20 @@
+/*
+ *openPilot Log - A FOSS Pilot Logbook Application
+ *Copyright (C) 2020  Felix Turowsky
+ *
+ *This program is free software: you can redistribute it and/or modify
+ *it under the terms of the GNU General Public License as published by
+ *the Free Software Foundation, either version 3 of the License, or
+ *(at your option) any later version.
+ *
+ *This program is distributed in the hope that it will be useful,
+ *but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *GNU General Public License for more details.
+ *
+ *You should have received a copy of the GNU General Public License
+ *along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
 #include "aentry.h"
 
 namespace experimental {

+ 17 - 0
src/experimental/aentry.h

@@ -1,3 +1,20 @@
+/*
+ *openPilot Log - A FOSS Pilot Logbook Application
+ *Copyright (C) 2020  Felix Turowsky
+ *
+ *This program is free software: you can redistribute it and/or modify
+ *it under the terms of the GNU General Public License as published by
+ *the Free Software Foundation, either version 3 of the License, or
+ *(at your option) any later version.
+ *
+ *This program is distributed in the hope that it will be useful,
+ *but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *GNU General Public License for more details.
+ *
+ *You should have received a copy of the GNU General Public License
+ *along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
 #ifndef ENTRY_H
 #define ENTRY_H
 

+ 17 - 0
src/experimental/apilotentry.cpp

@@ -1,3 +1,20 @@
+/*
+ *openPilot Log - A FOSS Pilot Logbook Application
+ *Copyright (C) 2020  Felix Turowsky
+ *
+ *This program is free software: you can redistribute it and/or modify
+ *it under the terms of the GNU General Public License as published by
+ *the Free Software Foundation, either version 3 of the License, or
+ *(at your option) any later version.
+ *
+ *This program is distributed in the hope that it will be useful,
+ *but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *GNU General Public License for more details.
+ *
+ *You should have received a copy of the GNU General Public License
+ *along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
 #include "apilotentry.h"
 
 namespace experimental {

+ 17 - 0
src/experimental/apilotentry.h

@@ -1,3 +1,20 @@
+/*
+ *openPilot Log - A FOSS Pilot Logbook Application
+ *Copyright (C) 2020  Felix Turowsky
+ *
+ *This program is free software: you can redistribute it and/or modify
+ *it under the terms of the GNU General Public License as published by
+ *the Free Software Foundation, either version 3 of the License, or
+ *(at your option) any later version.
+ *
+ *This program is distributed in the hope that it will be useful,
+ *but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *GNU General Public License for more details.
+ *
+ *You should have received a copy of the GNU General Public License
+ *along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
 #ifndef APILOTENTRY_H
 #define APILOTENTRY_H
 

+ 34 - 0
src/experimental/atailentry.cpp

@@ -0,0 +1,34 @@
+/*
+ *openTail Log - A FOSS Tail Logbook Application
+ *Copyright (C) 2020  Felix Turowsky
+ *
+ *This program is free software: you can redistribute it and/or modify
+ *it under the terms of the GNU General Public License as published by
+ *the Free Software Foundation, either version 3 of the License, or
+ *(at your option) any later version.
+ *
+ *This program is distributed in the hope that it will be useful,
+ *but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *GNU General Public License for more details.
+ *
+ *You should have received a copy of the GNU General Public License
+ *along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+#include "atailentry.h"
+
+namespace experimental {
+
+ATailEntry::ATailEntry()
+    : AEntry::AEntry(DEFAULT_TAIL_POSITION)
+{}
+
+ATailEntry::ATailEntry(int row_id)
+    : AEntry::AEntry(DataPosition("tails", row_id))
+{}
+
+ATailEntry::ATailEntry(TableData table_data)
+    : AEntry::AEntry(DEFAULT_TAIL_POSITION, table_data)
+{}
+
+} // namespace experimental

+ 37 - 0
src/experimental/atailentry.h

@@ -0,0 +1,37 @@
+/*
+ *openTail Log - A FOSS Tail Logbook Application
+ *Copyright (C) 2020  Felix Turowsky
+ *
+ *This program is free software: you can redistribute it and/or modify
+ *it under the terms of the GNU General Public License as published by
+ *the Free Software Foundation, either version 3 of the License, or
+ *(at your option) any later version.
+ *
+ *This program is distributed in the hope that it will be useful,
+ *but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *GNU General Public License for more details.
+ *
+ *You should have received a copy of the GNU General Public License
+ *along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+#ifndef ATAILENTRY_H
+#define ATAILENTRY_H
+
+#include "src/experimental/aentry.h"
+#include "src/experimental/decl.h"
+
+namespace experimental {
+
+struct ATailEntry : public AEntry {
+public:
+    ATailEntry();
+    ATailEntry(const ATailEntry& te) = default;
+    ATailEntry& operator=(const ATailEntry& te) = default;
+    ATailEntry(int row_id);
+    ATailEntry(TableData table_data);
+};
+
+} // namespace experimental
+
+#endif // ATAILENTRY_H

+ 2 - 0
src/experimental/decl.h

@@ -38,6 +38,8 @@ struct DataPosition : QPair<TableName, RowId> {
     DataPosition& operator=(const DataPosition& other) = default;
 };
 auto const DEFAULT_PILOT_POSITION = DataPosition("pilots", 0);
+auto const DEFAULT_TAIL_POSITION = DataPosition("tails", 0);
+auto const DEFAULT_AIRCRAFT_POSITION = DataPosition("aircraft", 0);
 
 }
 

+ 1 - 1
src/functions/acalc.cpp

@@ -1,5 +1,5 @@
 #include "acalc.h"
-#include "src/functions/adebug.h"
+#include "src/testing/adebug.h"
 
 using namespace ACalc;
 

+ 1 - 1
src/functions/astat.cpp

@@ -16,7 +16,7 @@
  *along with this program.  If not, see <https://www.gnu.org/licenses/>.
  */
 #include "astat.h"
-#include "src/functions/adebug.h"
+#include "src/testing/adebug.h"
 
 using namespace experimental;
 

+ 2 - 2
src/gui/dialogues/newflightdialog.cpp

@@ -17,7 +17,7 @@
  */
 #include "newflightdialog.h"
 #include "ui_newflight.h"
-#include "src/functions/adebug.h"
+#include "src/testing/adebug.h"
 
 
 
@@ -483,7 +483,7 @@ void NewFlightDialog::addNewAircraftMessageBox(QLineEdit *parent)
     {
         DEB("Add new aircraft selected");
         // create and open new aircraft dialog
-        auto na = NewTailDialog(ui->acftLineEdit->text(), Db::createNew, this);
+        auto na = NewTailDialog(ui->acftLineEdit->text(), this);
         na.exec();
         QString statement = "SELECT MAX(tail_id)  FROM tails";
         QString id = Db::customQuery(statement,1).first();

+ 1 - 1
src/gui/dialogues/newpilotdialog.cpp

@@ -17,7 +17,7 @@
  */
 #include "newpilotdialog.h"
 #include "ui_newpilot.h"
-#include "src/functions/adebug.h"
+#include "src/testing/adebug.h"
 
 /* Examples for names around the world:
  * José Eduardo Santos Tavares Melo Silva

+ 0 - 2
src/gui/dialogues/newpilotdialog.h

@@ -45,8 +45,6 @@ public:
 private slots:
     void on_buttonBox_accepted();
 
-//public slots:
-
     void onCommitSuccessful();
 
     void onCommitUnsuccessful(const QSqlError &sqlError, const QString &);

+ 55 - 1
src/gui/dialogues/newtail.ui

@@ -16,6 +16,12 @@
   <layout class="QGridLayout" name="gridLayout">
    <item row="7" column="0">
     <widget class="QLabel" name="operationLabel">
+     <property name="minimumSize">
+      <size>
+       <width>160</width>
+       <height>0</height>
+      </size>
+     </property>
      <property name="text">
       <string>Operation</string>
      </property>
@@ -43,6 +49,12 @@
    </item>
    <item row="6" column="0">
     <widget class="QLabel" name="variantLabel">
+     <property name="minimumSize">
+      <size>
+       <width>160</width>
+       <height>0</height>
+      </size>
+     </property>
      <property name="text">
       <string>Variant</string>
      </property>
@@ -82,6 +94,12 @@
    </item>
    <item row="10" column="0">
     <widget class="QLabel" name="weightLabel">
+     <property name="minimumSize">
+      <size>
+       <width>160</width>
+       <height>0</height>
+      </size>
+     </property>
      <property name="text">
       <string>Weight Class</string>
      </property>
@@ -118,6 +136,12 @@
    </item>
    <item row="2" column="0">
     <widget class="QLabel" name="registrationLabel">
+     <property name="minimumSize">
+      <size>
+       <width>160</width>
+       <height>0</height>
+      </size>
+     </property>
      <property name="text">
       <string>Registration</string>
      </property>
@@ -125,6 +149,12 @@
    </item>
    <item row="5" column="0">
     <widget class="QLabel" name="modelLabel">
+     <property name="minimumSize">
+      <size>
+       <width>160</width>
+       <height>0</height>
+      </size>
+     </property>
      <property name="text">
       <string>Model</string>
      </property>
@@ -132,6 +162,12 @@
    </item>
    <item row="3" column="0">
     <widget class="QLabel" name="companyLabel">
+     <property name="minimumSize">
+      <size>
+       <width>160</width>
+       <height>0</height>
+      </size>
+     </property>
      <property name="text">
       <string>Company</string>
      </property>
@@ -157,7 +193,7 @@
    <item row="0" column="1">
     <widget class="QLineEdit" name="searchLineEdit">
      <property name="maxLength">
-      <number>20</number>
+      <number>40</number>
      </property>
      <property name="placeholderText">
       <string>start typing to search for aircraft types</string>
@@ -176,6 +212,12 @@
    </item>
    <item row="0" column="0">
     <widget class="QLabel" name="searchLabel">
+     <property name="minimumSize">
+      <size>
+       <width>160</width>
+       <height>0</height>
+      </size>
+     </property>
      <property name="font">
       <font>
        <weight>75</weight>
@@ -224,6 +266,12 @@
    </item>
    <item row="8" column="0">
     <widget class="QLabel" name="powerPlantLabel">
+     <property name="minimumSize">
+      <size>
+       <width>160</width>
+       <height>0</height>
+      </size>
+     </property>
      <property name="text">
       <string>Power Plant</string>
      </property>
@@ -231,6 +279,12 @@
    </item>
    <item row="4" column="0">
     <widget class="QLabel" name="makeLabel">
+     <property name="minimumSize">
+      <size>
+       <width>160</width>
+       <height>0</height>
+      </size>
+     </property>
      <property name="text">
       <string>Make</string>
      </property>

+ 139 - 120
src/gui/dialogues/newtaildialog.cpp

@@ -17,7 +17,7 @@
  */
 #include "newtaildialog.h"
 #include "ui_newtail.h"
-#include "src/functions/adebug.h"
+#include "src/testing/adebug.h"
 
 static const auto REG_VALID = QPair<QString, QRegularExpression> {
     "registrationLineEdit", QRegularExpression("\\w+-\\w+")};
@@ -30,39 +30,40 @@ static const auto VARIANT_VALID = QPair<QString, QRegularExpression> {
 static const auto LINE_EDIT_VALIDATORS = QVector({REG_VALID, MAKE_VALID, MODEL_VALID, VARIANT_VALID});
 
 
-//Dialog to be used to create a new tail
-NewTailDialog::NewTailDialog(QString newreg, Db::editRole edRole, QWidget *parent) :
+NewTailDialog::NewTailDialog(QString new_registration, QWidget *parent) :
     QDialog(parent),
     ui(new Ui::NewTail)
 {
-    DEB("new NewTailDialog\n");
+    DEB("new NewTailDialog (experimental)");
     ui->setupUi(this);
 
-    role = edRole;
+    connectSignals();
     setupCompleter();
     setupValidators();
 
-    ui->registrationLineEdit->setText(newreg);
+    ui->registrationLineEdit->setText(new_registration);
     ui->searchLineEdit->setStyleSheet("border: 1px solid blue");
     ui->searchLineEdit->setFocus();
+
+    entry = experimental::ATailEntry();
 }
 
-//Dialog to be used to edit an existing tail
-NewTailDialog::NewTailDialog(Aircraft dbentry, Db::editRole edRole, QWidget *parent) :
+NewTailDialog::NewTailDialog(int row_id, QWidget *parent) :
     QDialog(parent),
     ui(new Ui::NewTail)
 {
-    DEB("new NewTailDialog\n");
-    oldEntry = dbentry;
-    role = edRole;
+    using namespace experimental;
+    DEB("New experimental New Pilot Dialog (edit existing)");
     ui->setupUi(this);
 
     ui->searchLabel->hide();
     ui->searchLineEdit->hide();
     ui->line->hide();
 
+    connectSignals();
     setupValidators();
-    formFiller(oldEntry);
+    entry = aDB()->getTailEntry(row_id);
+    fillForm(entry);
 }
 
 NewTailDialog::~NewTailDialog()
@@ -73,89 +74,97 @@ NewTailDialog::~NewTailDialog()
 /// Functions
 
 /*!
- * \brief NewTail::setupCompleter creates a QMap<aircaft_id,searchstring> for auto completion,
- * obtains a QStringList for QCompleter and sets up search line edit accordingly
+ * \brief NewTail::setupCompleter obtains a QMap<QString searchstring, int aircaft_id> for auto completion
+ * and obtains a QStringList for QCompleter. This function then sets up the search line edit where
+ * the user can select a template from the aircraft database to pre-fill the form with the details
+ * for the selected type.
  */
 void NewTailDialog::setupCompleter()
 {
-    auto query = QLatin1String("SELECT make||' '||model||'-'||variant, aircraft_id FROM aircraft");
-    auto vector = Db::customQuery(query, 2);
-    QMap<QString, int> map;
-    for (int i = 0; i < vector.length() - 2 ; i += 2) {
-        if (vector[i] != QLatin1String("")) {
-            map.insert(vector[i], vector[i + 1].toInt());
-        }
-    }
-    //creating QStringlist for QCompleter. This list is identical to a list of map<key>,
-    //but creating it like this is faster.
+    using namespace experimental;
+    idMap = aDB()->getIdMap(ADataBase::aircraft);
+    aircraftList = aDB()->getCompletionList(experimental::ADataBase::aircraft);
 
-
-    auto aircraftlist = experimental::aDB()->getCompletionList(experimental::ADataBase::aircraft);
-    idMap = map;
-    QCompleter *completer = new QCompleter(aircraftlist, ui->searchLineEdit);
+    QCompleter *completer = new QCompleter(aircraftList, ui->searchLineEdit);
     completer->setCaseSensitivity(Qt::CaseInsensitive);
     completer->setCompletionMode(QCompleter::PopupCompletion);
     completer->setFilterMode(Qt::MatchContains);
     ui->searchLineEdit->setCompleter(completer);
+
+    QObject::connect(completer, static_cast<void(QCompleter::*)(const QString &)>(&QCompleter::activated),
+                     this, &NewTailDialog::onSearchCompleterActivated);
+    QObject::connect(completer, static_cast<void(QCompleter::*)(const QString &)>(&QCompleter::highlighted),
+                     this, &NewTailDialog::onSearchCompleterActivated);
+
+
 }
 
 void NewTailDialog::setupValidators()
 {
     for(const auto& pair : LINE_EDIT_VALIDATORS){
-        auto line_edit = parent()->findChild<QLineEdit*>(pair.first);
-        auto validator = new QRegularExpressionValidator(pair.second,line_edit);
+        auto line_edit = this->findChild<QLineEdit*>(pair.first);
+        auto validator = new QRegularExpressionValidator(pair.second, line_edit);
         line_edit->setValidator(validator);
     }
 }
+
+void NewTailDialog::connectSignals()
+{
+    using namespace experimental;
+    QObject::connect(aDB(), &ADataBase::sqlSuccessful,
+                     this,  &NewTailDialog::onCommitSuccessful);
+    QObject::connect(aDB(), &ADataBase::sqlError,
+                     this,  &NewTailDialog::onCommitUnsuccessful);
+}
+
 /*!
- * \brief NewTail::formFiller populates the Dialog with the
- * information contained in an aircraft object.
- * \param db - entry retreived from database
+ * \brief NewTailDialog::fillForm populates the Dialog with the
+ * information contained in an entry object. This can be either
+ * a template (AAircraft, used when creating a new entry) or
+ * a tail (ATail, used when editing an existing entry)
+ * \param entry
  */
-void NewTailDialog::formFiller(Entry_deprecated entry)
+void NewTailDialog::fillForm(experimental::AEntry entry)
 {
-    DEB("Filling Form for a/c" << entry);
+    DEB("Filling Form for (experimental) a/c" << entry.getPosition());
     //fill Line Edits
-    auto line_edits = parent()->findChildren<QLineEdit *>();
+    auto line_edits = this->findChildren<QLineEdit *>();
     for (const auto &le : line_edits) {
-        QString name = le->objectName();
-        name.chop(8);//remove "LineEdit"
-        QString value = entry.data.value(name);
-        if (!value.isEmpty()) {
-            le->setText(value);
-        };
+        QString name = le->objectName().remove("LineEdit");
+        QString value = entry.getData().value(name);
+        le->setText(value);
     }
     //select comboboxes
-    QVector<QString> operation = {entry.data.value("singleengine"), entry.data.value("multiengine")};
-    QVector<QString> ppNumber =  {entry.data.value("singlepilot"), entry.data.value("multipilot")};
-    QVector<QString> ppType =    {entry.data.value("unpowered"), entry.data.value("piston"),
-                                  entry.data.value("turboprop"), entry.data.value("jet")
+    QVector<QString> operation = {entry.getData().value("singleengine"), entry.getData().value("multiengine")};
+    QVector<QString> ppNumber =  {entry.getData().value("singlepilot"), entry.getData().value("multipilot")};
+    QVector<QString> ppType =    {entry.getData().value("unpowered"), entry.getData().value("piston"),
+                                  entry.getData().value("turboprop"), entry.getData().value("jet")
                                  };
-    QVector<QString> weight =    {entry.data.value("light"), entry.data.value("medium"),
-                                  entry.data.value("heavy"), entry.data.value("super")
+    QVector<QString> weight =    {entry.getData().value("light"), entry.getData().value("medium"),
+                                  entry.getData().value("heavy"), entry.getData().value("super")
                                  };
 
-
     ui->operationComboBox->setCurrentIndex(operation.indexOf("1") + 1);
     ui->ppNumberComboBox->setCurrentIndex(ppNumber.indexOf("1") + 1);
     ui->ppTypeComboBox->setCurrentIndex(ppType.indexOf("1") + 1);
     ui->weightComboBox->setCurrentIndex(weight.indexOf("1") + 1);
 }
+
 /*!
  * \brief NewTail::verify A simple check for empty recommended fields in the form
  * \return true if all reconmmended fields are populated
  */
 bool NewTailDialog::verify()
 {
-    auto recommended_line_edits = parent()->findChildren<QLineEdit *>("registrationLineEdit");
-    recommended_line_edits.append(parent()->findChild<QLineEdit *>("makeLineEdit"));
-    recommended_line_edits.append(parent()->findChild<QLineEdit *>("modelLineEdit"));
+    auto recommended_line_edits = this->findChildren<QLineEdit *>("registrationLineEdit");
+    recommended_line_edits.append(this->findChild<QLineEdit *>("makeLineEdit"));
+    recommended_line_edits.append(this->findChild<QLineEdit *>("modelLineEdit"));
 
-    auto recommended_combo_boxes = parent()->findChildren<QComboBox *>("operationComboBox");
-    recommended_combo_boxes.append(parent()->findChild<QComboBox *>("ppNumberComboBox"));
-    recommended_combo_boxes.append(parent()->findChild<QComboBox *>("ppTypeComboBox"));
+    auto recommended_combo_boxes = this->findChildren<QComboBox *>("operationComboBox");
+    recommended_combo_boxes.append(this->findChild<QComboBox *>("ppNumberComboBox"));
+    recommended_combo_boxes.append(this->findChild<QComboBox *>("ppTypeComboBox"));
 
-    for (const auto le : recommended_line_edits) {
+    for (const auto &le : recommended_line_edits) {
         if (le->text() != "") {
             DEB("Good: " << le);
             recommended_line_edits.removeOne(le);
@@ -165,7 +174,7 @@ bool NewTailDialog::verify()
             DEB("Not Good: " << le);
         }
     }
-    for (const auto cb : recommended_combo_boxes) {
+    for (const auto &cb : recommended_combo_boxes) {
         if (cb->currentIndex() != 0) {
 
             recommended_combo_boxes.removeOne(cb);
@@ -187,19 +196,18 @@ bool NewTailDialog::verify()
  * or updates a database entry and commits or updates the database
  * \param edRole editExisting or createNew
  */
-void NewTailDialog::submitForm(Db::editRole edRole)
+void NewTailDialog::submitForm()
 {
     DEB("Creating Database Object...");
-    QMap<QString, QString> newData;
+    using namespace experimental;
+    TableData new_data;
     //retreive Line Edits
-    auto line_edits = parent()->findChildren<QLineEdit *>();
+    auto line_edits = this->findChildren<QLineEdit *>();
+    line_edits.removeOne(this->findChild<QLineEdit *>("searchLineEdit"));
 
     for (const auto &le : line_edits) {
-        QString name = le->objectName();
-        name.chop(8);//remove "LineEdit"
-        if (!le->text().isEmpty()) {
-            newData.insert(name, le->text());
-        }
+        QString name = le->objectName().remove("LineEdit");
+        new_data.insert(name, le->text());
     }
     //prepare comboboxes
     QVector<QString> operation = {"singlepilot", "multipilot"};
@@ -212,49 +220,26 @@ void NewTailDialog::submitForm(Db::editRole edRole)
                                  };
 
     if (ui->operationComboBox->currentIndex() != 0) {
-        newData.insert(operation[ui->operationComboBox->currentIndex() - 1], QLatin1String("1"));
+        new_data.insert(operation[ui->operationComboBox->currentIndex() - 1], QLatin1String("1"));
     }
     if (ui->ppNumberComboBox->currentIndex() != 0) {
-        newData.insert(ppNumber[ui->ppNumberComboBox->currentIndex() - 1], QLatin1String("1"));
+        new_data.insert(ppNumber[ui->ppNumberComboBox->currentIndex() - 1], QLatin1String("1"));
     }
     if (ui->ppTypeComboBox->currentIndex() != 0) {
-        newData.insert(ppType[ui->ppTypeComboBox->currentIndex() - 1], QLatin1String("1"));
+        new_data.insert(ppType[ui->ppTypeComboBox->currentIndex() - 1], QLatin1String("1"));
     }
     if (ui->weightComboBox->currentIndex() != 0) {
-        newData.insert(weight[ui->weightComboBox->currentIndex() - 1], QLatin1String("1"));
+        new_data.insert(weight[ui->weightComboBox->currentIndex() - 1], QLatin1String("1"));
     }
+
     //create db object
-    switch (edRole) {
-    case Db::createNew: {
-        auto newEntry = Aircraft(newData);;
-        newEntry.commit();
-        break;
-    }
-    case Db::editExisting:
-        oldEntry.setData(newData);
-        oldEntry.commit();
-        ACalc::updateAutoTimes(oldEntry.position.second);
-        break;
-    }
+
+    entry.setData(new_data);
+    aDB()->commit(entry);
 }
 
 /// Slots
 
-void NewTailDialog::on_searchLineEdit_textChanged(const QString &arg1)
-{
-    if (aircraftlist.contains(
-                arg1)) { //equivalent to editing finished for this purpose. todo: consider connecing qcompleter activated signal with editing finished slot.
-
-        DEB("Template Selected. aircraft_id is: " << idMap.value(arg1));
-        //call autofiller for dialog
-        formFiller(Entry_deprecated("aircraft",idMap.value(arg1)));
-        ui->searchLineEdit->setStyleSheet("border: 1px solid green");
-    } else {
-        //for example, editing finished without selecting a result from Qcompleter
-        ui->searchLineEdit->setStyleSheet("border: 1px solid orange");
-    }
-}
-
 void NewTailDialog::on_operationComboBox_currentIndexChanged(int index)
 {
     if (index != 0) {
@@ -290,37 +275,71 @@ void NewTailDialog::on_buttonBox_accepted()
         auto nope = QMessageBox(this);
         nope.setText("Registration cannot be empty.");
         nope.exec();
-    } else {
-        if (verify()) {
-            DEB("Form verified");
-            submitForm(role);
-            accept();
+        return;
+    }
+
+    if (!verify()) {
+        if (!ASettings::read("userdata/acAllowIncomplete").toInt()) {
+            auto nope = QMessageBox(this);
+            nope.setIcon(QMessageBox::Warning);
+            nope.setText("Some or all recommended fields are empty.\nPlease go back and "
+                         "complete the form.\n\nYou can allow logging incomplete tail entries on the settings page.");
+            nope.exec();
+            return;
         } else {
-            if (!ASettings::read("userdata/acAllowIncomplete").toInt()) {
-                auto nope = QMessageBox(this);
-                nope.setText("Some or all fields are empty.\nPlease go back and "
-                              "complete the form.\n\nYou can allow logging incomplete entries on the settings page.");
-                nope.exec();
-            } else {
-                QMessageBox::StandardButton reply;
-                reply = QMessageBox::question(this, "Warning",
-                                              "Some recommended fields are empty.\n\n"
-                                              "If you do not fill out the aircraft details, "
-                                              "it will be impossible to automatically determine Single/Multi Pilot Times or Single/Multi Engine Time."
-                                              "This will also impact statistics and auto-logging capabilites.\n\n"
-                                              "It is highly recommended to fill in all the details.\n\n"
-                                              "Are you sure you want to proceed?",
-                                              QMessageBox::Yes | QMessageBox::No);
-                if (reply == QMessageBox::Yes) {
-                    submitForm(role);
-                    accept();
-                }
+            QMessageBox::StandardButton reply;
+            reply = QMessageBox::question(this, "Warning",
+                                          "Some recommended fields are empty.\n\n"
+                                          "If you do not fill out the aircraft details, "
+                                          "it will be impossible to automatically determine Single/Multi Pilot Times or Single/Multi Engine Time."
+                                          "This will also impact statistics and auto-logging capabilites.\n\n"
+                                          "It is highly recommended to fill in all the details.\n\n"
+                                          "Are you sure you want to proceed?",
+                                          QMessageBox::Yes | QMessageBox::No);
+            if (reply == QMessageBox::Yes) {
+                submitForm();
             }
         }
     }
+    DEB("Form verified");
+    submitForm();
+}
+
+void NewTailDialog::onSearchCompleterActivated()
+{
+    DEB("Search completer activated!");
+    const auto &text = ui->searchLineEdit->text();
+    if (aircraftList.contains(text)) {
+
+            DEB("Template Selected. aircraft_id is: " << idMap.value(text));
+            //call autofiller for dialog
+            using namespace experimental;
+            fillForm(aDB()->getAircraftEntry(idMap.value(text)));
+            ui->searchLineEdit->setStyleSheet("border: 1px solid green");
+            ui->searchLabel->setText(text);
+        } else {
+            //for example, editing finished without selecting a result from Qcompleter
+            ui->searchLineEdit->setStyleSheet("border: 1px solid orange");
+        }
 }
 
 void NewTailDialog::on_registrationLineEdit_textChanged(const QString &arg1)
 {
     ui->registrationLineEdit->setText(arg1.toUpper());
 }
+
+void NewTailDialog::onCommitSuccessful()
+{
+    ACalc::updateAutoTimes(entry.getPosition().second); // To do: update to use new db architecture with new ATailEntry
+    accept();
+}
+
+void NewTailDialog::onCommitUnsuccessful(const QSqlError &sqlError, const QString &)
+{
+    auto mb = QMessageBox(this);
+    mb.setIcon(QMessageBox::Critical);
+    mb.setText("The following error has ocurred.\n\n"
+               + sqlError.text()
+               + "\n\nYour entry has not been saved.");
+    mb.exec();
+}

+ 29 - 23
src/gui/dialogues/newtaildialog.h

@@ -29,6 +29,8 @@
 #include "src/functions/acalc.h"
 #include "src/database/entry_deprecated.h"
 #include "src/experimental/adatabase.h"
+#include "src/experimental/atailentry.h"
+#include "src/experimental/aaircraftentry.h"
 
 namespace Ui {
 class NewTail;
@@ -45,49 +47,53 @@ class NewTailDialog : public QDialog
     Q_OBJECT
 
 public:
-    //to create new tail from scratch
-    explicit NewTailDialog(QString reg, Db::editRole edRole, QWidget *parent = nullptr);
-    //to edit existing tail
-    explicit NewTailDialog(Aircraft dbentry, Db::editRole edRole, QWidget *parent = nullptr);
+    // experimental create new tail
+    explicit NewTailDialog(QString new_registration, QWidget *parent = nullptr);
+    // experimental edit existing tail
+    explicit NewTailDialog(int row_id, QWidget *parent = nullptr);
 
     ~NewTailDialog();
-private slots:
+private:
 
-    void on_searchLineEdit_textChanged(const QString &arg1);
+    Ui::NewTail *ui;
 
-    void on_buttonBox_accepted();
+    experimental::ATailEntry entry;
 
-    void on_operationComboBox_currentIndexChanged(int index);
+    QStringList aircraftList;
 
-    void on_ppTypeComboBox_currentIndexChanged(int index);
+    QMap<QString, int> idMap;
 
-    void on_ppNumberComboBox_currentIndexChanged(int index);
+    void setupCompleter();
 
-    void on_weightComboBox_currentIndexChanged(int index);
+    void setupValidators();
 
-    void on_registrationLineEdit_textChanged(const QString &arg1);
+    void connectSignals();
 
-private:
+    void fillForm(experimental::AEntry entry);
 
-    Ui::NewTail *ui;
+    void submitForm();
 
-    Db::editRole role;
+    bool verify();
 
-    Aircraft oldEntry;
+private slots:
 
-    QStringList aircraftlist;
+    void on_operationComboBox_currentIndexChanged(int index);
 
-    QMap<QString, int> idMap;
+    void on_ppTypeComboBox_currentIndexChanged(int index);
 
-    void submitForm(Db::editRole edRole);
+    void on_ppNumberComboBox_currentIndexChanged(int index);
 
-    void setupCompleter();
+    void on_weightComboBox_currentIndexChanged(int index);
 
-    void setupValidators();
+    void on_registrationLineEdit_textChanged(const QString &arg1);
 
-    void formFiller(Entry_deprecated);
+    void on_buttonBox_accepted();
 
-    bool verify();
+    void onSearchCompleterActivated();
+
+    void onCommitSuccessful();
+
+    void onCommitUnsuccessful(const QSqlError &sqlError, const QString &);
 };
 
 #endif // NEWTAIL_H

+ 40 - 24
src/gui/widgets/aircraftwidget.cpp

@@ -28,8 +28,10 @@ AircraftWidget::AircraftWidget(QWidget *parent) :
 {
     DEB("New AircraftWidet");
     ui->setupUi(this);
-    sortColumn = ASettings::read("userdata/acSortColumn").toInt();
-    refreshModelAndView();
+    ui->stackedWidget->addWidget(this->findChild<QWidget*>("welcomePageTails"));
+    ui->stackedWidget->setCurrentWidget(this->findChild<QWidget*>("welcomePageTails"));
+
+    setupModelAndView();
 }
 
 AircraftWidget::~AircraftWidget()
@@ -42,16 +44,17 @@ AircraftWidget::~AircraftWidget()
  * Functions
  */
 
-void AircraftWidget::refreshModelAndView()
+void AircraftWidget::setupModelAndView()
 {
-    ui->stackedWidget->addWidget(parent()->findChild<QWidget*>("welcomePageTails"));
-    ui->stackedWidget->setCurrentWidget(parent()->findChild<QWidget*>("welcomePageTails"));
+    sortColumn = ASettings::read("userdata/acSortColumn").toInt();
 
+    model = new QSqlTableModel(this);
     model->setTable("viewTails");
     model->select();
 
-    QTableView *view = ui->tableView;
+    view = ui->tableView;
     view->setModel(model);
+
     view->setSelectionBehavior(QAbstractItemView::SelectRows);
     view->setSelectionMode(QAbstractItemView::SingleSelection);
     view->setEditTriggers(QAbstractItemView::NoEditTriggers);
@@ -66,10 +69,10 @@ void AircraftWidget::refreshModelAndView()
 
     view->show();
 
-    connect(ui->tableView->selectionModel(), SIGNAL(selectionChanged(const QItemSelection &, const QItemSelection &)),
-            this, SLOT(tableView_selectionChanged()));
-    connect(ui->tableView->horizontalHeader(), SIGNAL(sectionClicked(int)),
-            this, SLOT(tableView_headerClicked(int)));
+    QObject::connect(ui->tableView->selectionModel(), &QItemSelectionModel::selectionChanged,
+                     this, &AircraftWidget::tableView_selectionChanged);
+    QObject::connect(ui->tableView->horizontalHeader(), &QHeaderView::sectionClicked,
+                     this, &AircraftWidget::tableView_headerClicked);
 }
 
 /*
@@ -116,7 +119,7 @@ void AircraftWidget::on_deleteButton_clicked()
                 }
             }
         }
-        refreshModelAndView();
+        model->select();
     } else {
         auto mb = QMessageBox(this);
         mb.setText("No aircraft selected.");
@@ -126,22 +129,32 @@ void AircraftWidget::on_deleteButton_clicked()
 
 void AircraftWidget::on_newButton_clicked()
 {
-    auto nt = NewTailDialog(QString(), Db::createNew, this);
+    auto nt = NewTailDialog(QString(), this);
     connect(&nt, SIGNAL(accepted()), this, SLOT(acft_editing_finished()));
     connect(&nt, SIGNAL(rejected()), this, SLOT(acft_editing_finished()));
     nt.exec();
 }
 
-void AircraftWidget::on_searchLineEdit_textChanged(const QString &arg1)
+void AircraftWidget::on_aircraftSearchLineEdit_textChanged(const QString &arg1)
 {
-    if(ui->searchComboBox->currentIndex() == 0){
-        ui->searchLineEdit->setText(arg1.toUpper());
+    if(ui->aircraftSearchComboBox->currentIndex() == 0){
+        ui->aircraftSearchLineEdit->setText(arg1.toUpper());
     }
-    model->setFilter(ui->searchComboBox->currentText() + " LIKE \"%" + arg1 + "%\"");
+    model->setFilter(ui->aircraftSearchComboBox->currentText() + " LIKE \"%" + arg1 + "%\"");
 }
 
 void AircraftWidget::tableView_selectionChanged()
 {
+    if (this->findChild<NewTailDialog*>() != nullptr) {
+        DEB("Selection changed. Deleting orphaned dialog.");
+        delete this->findChild<NewTailDialog*>();
+        /// [F] if the user changes the selection without making any changes,
+        /// if(selectedTails.length() == 1) spawns a new dialog without the
+        /// old one being deleted, since neither accept() nor reject() was emitted.
+        /// Is this a reasonable way of avoiding pollution? Creating the widgets on the
+        /// stack does not seem to solve the problem since the Dialog does not get destroyed
+        /// until either accept() or reject() is emitted so I went for this solution.
+    }
     auto *selection = ui->tableView->selectionModel();
     selectedTails.clear();
 
@@ -150,13 +163,16 @@ void AircraftWidget::tableView_selectionChanged()
         DEB("Selected Tails(s) with ID: " << selectedTails);
     }
     if(selectedTails.length() == 1) {
-        auto nt = NewTailDialog(Aircraft(selectedTails.first()), Db::editExisting, this);
-        connect(&nt, SIGNAL(accepted()), this, SLOT(acft_editing_finished()));
-        connect(&nt, SIGNAL(rejected()), this, SLOT(acft_editing_finished()));
-        ui->stackedWidget->addWidget(&nt);
-        ui->stackedWidget->setCurrentWidget(&nt);
-        nt.setWindowFlag(Qt::Widget);
-        nt.exec();
+        auto* nt = new NewTailDialog(selectedTails.first(), this);
+        QObject::connect(nt,   &QDialog::accepted,
+                         this, &AircraftWidget::acft_editing_finished);
+        QObject::connect(nt,   &QDialog::rejected,
+                         this, &AircraftWidget::acft_editing_finished);
+        ui->stackedWidget->addWidget(nt);
+        ui->stackedWidget->setCurrentWidget(nt);
+        nt->setWindowFlag(Qt::Widget);
+        nt->setAttribute(Qt::WA_DeleteOnClose);
+        nt->exec();
     }
 }
 
@@ -168,5 +184,5 @@ void AircraftWidget::tableView_headerClicked(int column)
 
 void AircraftWidget::acft_editing_finished()
 {
-    refreshModelAndView();
+    model->select();
 }

+ 6 - 5
src/gui/widgets/aircraftwidget.h

@@ -22,8 +22,7 @@
 #include <QItemSelection>
 #include <QSqlTableModel>
 #include <QDebug>
-#include <QLabel>
-#include <QHBoxLayout>
+#include <QTableView>
 
 #include "src/classes/asettings.h"
 #include "src/gui/dialogues/newtaildialog.h"
@@ -54,18 +53,20 @@ private slots:
 
     void acft_editing_finished();
 
-    void on_searchLineEdit_textChanged(const QString &arg1);
+    void on_aircraftSearchLineEdit_textChanged(const QString &arg1);
 
 private:
     Ui::AircraftWidget *ui;
 
-    QSqlTableModel *model = new QSqlTableModel;
+    QSqlTableModel *model;
+
+    QTableView *view;
 
     qint32 sortColumn;
 
     QVector<qint32> selectedTails;
 
-    void refreshModelAndView();
+    void setupModelAndView();
 };
 
 #endif // AIRCRAFTWIDGET_H

+ 2 - 2
src/gui/widgets/aircraftwidget.ui

@@ -76,7 +76,7 @@
             </widget>
            </item>
            <item row="0" column="1">
-            <widget class="QLineEdit" name="searchLineEdit"/>
+            <widget class="QLineEdit" name="aircraftSearchLineEdit"/>
            </item>
            <item row="1" column="0">
             <widget class="QLabel" name="label_2">
@@ -86,7 +86,7 @@
             </widget>
            </item>
            <item row="1" column="1">
-            <widget class="QComboBox" name="searchComboBox">
+            <widget class="QComboBox" name="aircraftSearchComboBox">
              <item>
               <property name="text">
                <string>Registration</string>

+ 59 - 2
src/gui/widgets/debugwidget.cpp

@@ -1,6 +1,6 @@
 #include "debugwidget.h"
 #include "ui_debugwidget.h"
-#include "src/functions/adebug.h"
+
 
 
 DebugWidget::DebugWidget(QWidget *parent) :
@@ -23,6 +23,7 @@ DebugWidget::~DebugWidget()
 
 void DebugWidget::on_resetUserTablesPushButton_clicked()
 {
+    ATimer timer(this);
     QMessageBox result;
     if (ADataBaseSetup::resetToDefault()){
         result.setText("Database successfully reset.\n\nRestarting app.");
@@ -33,11 +34,11 @@ void DebugWidget::on_resetUserTablesPushButton_clicked()
         result.setText("Errors have occurred. Check console for Debug output. ");
         result.exec();
     }
-
 }
 
 void DebugWidget::on_resetDatabasePushButton_clicked()
 {
+    ATimer timer(this);
     QMessageBox mb(this);
     //check if template dir exists and create if needed.
     QDir dir("data/templates");
@@ -89,6 +90,7 @@ void DebugWidget::downloadFinished()
 
 void DebugWidget::on_fillUserDataPushButton_clicked()
 {
+    ATimer timer(this);
     QMessageBox mb(this);
     //check if template dir exists and create if needed.
     QDir dir("data/templates");
@@ -138,6 +140,7 @@ void DebugWidget::on_selectCsvPushButton_clicked()
 
 void DebugWidget::on_importCsvPushButton_clicked()
 {
+    ATimer timer(this);
     auto file = QFileInfo(ui->importCsvLineEdit->text());
     DEB("File exists/is file: " << file.exists() << file.isFile() << " Path: " << file.absoluteFilePath());
 
@@ -158,3 +161,57 @@ void DebugWidget::on_importCsvPushButton_clicked()
         mb.exec();
     }
 }
+
+void DebugWidget::on_debugPushButton_clicked()
+{
+    qlonglong number_of_runs = 5000;
+    long time1 = 0;
+    long time2 = 0;
+    using namespace experimental;
+    {
+        ATimer timer;
+        for (int i = 0; i < number_of_runs; i++) {
+            // first block, do stuff here...
+            aDB()->getEntry({"pilots", i});
+        }
+
+        time1 = timer.timeNow();
+    }
+    {
+        ATimer timer;
+        for (int i = 0; i < number_of_runs; i++) {
+            // second block, do stuff here...
+            aDB()->getPilotEntry(i);
+        }
+        time2 = timer.timeNow();
+    }
+
+    DEB("First block executed  " << number_of_runs << " times for a total of " << time1 << " milliseconds.");
+    DEB("Second block executed " << number_of_runs << " times for a total of " << time2 << " milliseconds.");
+}
+
+/*
+    qlonglong number_of_runs = 5000;
+    long time1 = 0;
+    long time2 = 0;
+    using namespace experimental;
+    {
+
+        ATimer timer;
+        for (int i = 0; i < number_of_runs; i++) {
+            // first block, do stuff here...
+        }
+
+        time1 = timer.timeNow();
+    }
+    {
+        ATimer timer;
+        for (int i = 0; i < number_of_runs; i++) {
+            // second block, do stuff here...
+        }
+        time2 = timer.timeNow();
+    }
+
+    DEB("First block executed " << number_of_runs << " times for a total of " << time1 << " milliseconds.");
+    DEB("Second block executed " << number_of_runs << " times for a total of " << time2 << " milliseconds.");
+*/

+ 10 - 0
src/gui/widgets/debugwidget.h

@@ -14,6 +14,14 @@
 #include "src/classes/adownload.h"
 #include "src/functions/areadcsv.h"
 
+#include "src/experimental/adatabase.h"
+#include "src/experimental/aentry.h"
+#include "src/experimental/apilotentry.h"
+
+#include "src/testing/abenchmark.h"
+#include "src/testing/atimer.h"
+#include "src/testing/adebug.h"
+
 namespace Ui {
 class DebugWidget;
 }
@@ -39,6 +47,8 @@ private slots:
 
     void on_importCsvPushButton_clicked();
 
+    void on_debugPushButton_clicked();
+
 private:
     Ui::DebugWidget *ui;
 

+ 54 - 44
src/gui/widgets/debugwidget.ui

@@ -6,7 +6,7 @@
    <rect>
     <x>0</x>
     <y>0</y>
-    <width>640</width>
+    <width>1005</width>
     <height>480</height>
    </rect>
   </property>
@@ -24,44 +24,49 @@
        <string>Database</string>
       </attribute>
       <layout class="QGridLayout" name="gridLayout_2">
-       <item row="0" column="0">
-        <widget class="QPushButton" name="resetDatabasePushButton">
+       <item row="3" column="3">
+        <widget class="QLineEdit" name="importCsvLineEdit">
          <property name="minimumSize">
           <size>
-           <width>220</width>
+           <width>110</width>
            <height>0</height>
           </size>
          </property>
-         <property name="text">
-          <string>Reset Database</string>
+         <property name="placeholderText">
+          <string>/path/to/file.csv</string>
          </property>
         </widget>
        </item>
-       <item row="3" column="1" colspan="2">
-        <widget class="QComboBox" name="tableComboBox">
+       <item row="3" column="4">
+        <widget class="QPushButton" name="selectCsvPushButton">
          <property name="minimumSize">
           <size>
            <width>110</width>
            <height>0</height>
           </size>
          </property>
-         <item>
-          <property name="text">
-           <string>Select Table</string>
-          </property>
-         </item>
+         <property name="text">
+          <string>Select File</string>
+         </property>
         </widget>
        </item>
-       <item row="3" column="0">
-        <widget class="QPushButton" name="importCsvPushButton">
+       <item row="0" column="0">
+        <widget class="QPushButton" name="resetDatabasePushButton">
          <property name="minimumSize">
           <size>
-           <width>110</width>
+           <width>220</width>
            <height>0</height>
           </size>
          </property>
          <property name="text">
-          <string>Import CSV</string>
+          <string>Reset Database</string>
+         </property>
+        </widget>
+       </item>
+       <item row="2" column="0">
+        <widget class="QPushButton" name="fillUserDataPushButton">
+         <property name="text">
+          <string>Fill User Table with test data</string>
          </property>
         </widget>
        </item>
@@ -78,21 +83,37 @@
          </property>
         </widget>
        </item>
-       <item row="3" column="3">
-        <widget class="QLineEdit" name="importCsvLineEdit">
+       <item row="0" column="3">
+        <widget class="QLabel" name="label">
+         <property name="text">
+          <string>Backup current database (if exists) and create a new one from scratch.</string>
+         </property>
+        </widget>
+       </item>
+       <item row="1" column="3">
+        <widget class="QLabel" name="label_2">
+         <property name="text">
+          <string>Keep current database but delete entries in pilots, aircraft and flights</string>
+         </property>
+        </widget>
+       </item>
+       <item row="3" column="1" colspan="2">
+        <widget class="QComboBox" name="tableComboBox">
          <property name="minimumSize">
           <size>
            <width>110</width>
            <height>0</height>
           </size>
          </property>
-         <property name="placeholderText">
-          <string>/path/to/file.csv</string>
-         </property>
+         <item>
+          <property name="text">
+           <string>Select Table</string>
+          </property>
+         </item>
         </widget>
        </item>
-       <item row="3" column="4">
-        <widget class="QPushButton" name="selectCsvPushButton">
+       <item row="3" column="0">
+        <widget class="QPushButton" name="importCsvPushButton">
          <property name="minimumSize">
           <size>
            <width>110</width>
@@ -100,37 +121,26 @@
           </size>
          </property>
          <property name="text">
-          <string>Select File</string>
-         </property>
-        </widget>
-       </item>
-       <item row="2" column="0">
-        <widget class="QPushButton" name="fillUserDataPushButton">
-         <property name="text">
-          <string>Fill User Table with test data</string>
+          <string>Import CSV</string>
          </property>
         </widget>
        </item>
-       <item row="0" column="3">
-        <widget class="QLabel" name="label">
+       <item row="2" column="3">
+        <widget class="QLabel" name="label_3">
          <property name="text">
-          <string>Backup current database (if exists) and create a new one from scratch.</string>
+          <string>Fill a new database with sample entries</string>
          </property>
         </widget>
        </item>
-       <item row="1" column="3">
-        <widget class="QLabel" name="label_2">
+       <item row="4" column="0">
+        <widget class="QPushButton" name="debugPushButton">
          <property name="text">
-          <string>Keep current database but delete entries in pilots, aircraft and flights</string>
+          <string>Do Debug Stuff!</string>
          </property>
         </widget>
        </item>
-       <item row="2" column="3">
-        <widget class="QLabel" name="label_3">
-         <property name="text">
-          <string>Fill a new database with sample entries</string>
-         </property>
-        </widget>
+       <item row="4" column="3">
+        <widget class="QLineEdit" name="debugLineEdit"/>
        </item>
       </layout>
      </widget>

+ 1 - 6
src/gui/widgets/homewidget.cpp

@@ -17,7 +17,7 @@
  */
 #include "homewidget.h"
 #include "ui_homewidget.h"
-#include "src/functions/adebug.h"
+#include "src/testing/adebug.h"
 
 
 HomeWidget::HomeWidget(QWidget *parent) :
@@ -35,8 +35,3 @@ HomeWidget::~HomeWidget()
 {
     delete ui;
 }
-
-void HomeWidget::on_pushButton_clicked()
-{
-    // do debug stuff
-}

+ 0 - 3
src/gui/widgets/homewidget.h

@@ -48,9 +48,6 @@ public:
     explicit HomeWidget(QWidget *parent = nullptr);
     ~HomeWidget();
 
-private slots:
-    void on_pushButton_clicked();
-
 private:
     Ui::HomeWidget *ui;
 

+ 11 - 21
src/gui/widgets/homewidget.ui

@@ -27,19 +27,6 @@
      </property>
     </spacer>
    </item>
-   <item row="1" column="0">
-    <spacer name="horizontalSpacer">
-     <property name="orientation">
-      <enum>Qt::Horizontal</enum>
-     </property>
-     <property name="sizeHint" stdset="0">
-      <size>
-       <width>414</width>
-       <height>20</height>
-      </size>
-     </property>
-    </spacer>
-   </item>
    <item row="1" column="1" colspan="2">
     <widget class="QStackedWidget" name="stackedWidget">
      <property name="currentIndex">
@@ -75,15 +62,18 @@
      </property>
     </spacer>
    </item>
-   <item row="3" column="1">
-    <widget class="QPushButton" name="pushButton">
-     <property name="text">
-      <string>Debug</string>
+   <item row="1" column="0">
+    <spacer name="horizontalSpacer">
+     <property name="orientation">
+      <enum>Qt::Horizontal</enum>
      </property>
-    </widget>
-   </item>
-   <item row="4" column="1">
-    <widget class="QLineEdit" name="lineEdit"/>
+     <property name="sizeHint" stdset="0">
+      <size>
+       <width>414</width>
+       <height>20</height>
+      </size>
+     </property>
+    </spacer>
    </item>
   </layout>
  </widget>

+ 1 - 1
src/gui/widgets/logbookwidget.cpp

@@ -17,7 +17,7 @@
  */
 #include "logbookwidget.h"
 #include "ui_logbookwidget.h"
-#include "src/functions/adebug.h"
+#include "src/testing/adebug.h"
 
 LogbookWidget::LogbookWidget(QWidget *parent) :
     QWidget(parent),

+ 25 - 28
src/gui/widgets/pilotswidget.cpp

@@ -17,7 +17,7 @@
  */
 #include "pilotswidget.h"
 #include "ui_pilotswidget.h"
-#include "src/functions/adebug.h"
+#include "src/testing/adebug.h"
 
 using namespace experimental;
 PilotsWidget::PilotsWidget(QWidget *parent) :
@@ -25,15 +25,8 @@ PilotsWidget::PilotsWidget(QWidget *parent) :
     ui(new Ui::PilotsWidget)
 {
     ui->setupUi(this);
-    sortColumn = ASettings::read("userdata/pilSortColumn").toInt();
 
     setupModelAndView();
-    refreshModelAndView();
-
-    QObject::connect(ui->tableView->selectionModel(), &QItemSelectionModel::selectionChanged,
-                     this, &PilotsWidget::tableView_selectionChanged);
-    QObject::connect(ui->tableView->horizontalHeader(), &QHeaderView::sectionClicked,
-                     this, &PilotsWidget::tableView_headerClicked);
 }
 
 PilotsWidget::~PilotsWidget()
@@ -43,11 +36,14 @@ PilotsWidget::~PilotsWidget()
 
 void PilotsWidget::setupModelAndView()
 {
+    sortColumn = ASettings::read("userdata/pilSortColumn").toInt();
+
+    model = new QSqlTableModel(this);
     model->setTable("viewPilots");
     model->setFilter("ID > 1");//to not allow editing of self, shall be done via settings
     model->select();
 
-    view = ui->tableView;
+    view = ui->pilotsTableView;
     view->setModel(model);
     view->setSelectionBehavior(QAbstractItemView::SelectRows);
     view->setSelectionMode(QAbstractItemView::SingleSelection);
@@ -60,31 +56,31 @@ void PilotsWidget::setupModelAndView()
     view->setAlternatingRowColors(true);
     view->setSortingEnabled(true);
     view->sortByColumn(sortColumn, Qt::AscendingOrder);
-}
 
-void PilotsWidget::refreshModelAndView()
-{
-    ui->stackedWidget->setCurrentWidget(this->findChild<QWidget*>("welcomePage"));
-    model->select();
-    view->show();
+    QObject::connect(ui->pilotsTableView->selectionModel(), &QItemSelectionModel::selectionChanged,
+                     this, &PilotsWidget::tableView_selectionChanged);
+    QObject::connect(ui->pilotsTableView->horizontalHeader(), &QHeaderView::sectionClicked,
+                     this, &PilotsWidget::tableView_headerClicked);
 }
 
-void PilotsWidget::on_searchLineEdit_textChanged(const QString &arg1)
+void PilotsWidget::on_pilotSearchLineEdit_textChanged(const QString &arg1)
 {
-    model->setFilter("\"" + ui->searchComboBox->currentText() + "\" LIKE \"%" + arg1 + "%\" AND ID > 1");
+    model->setFilter("\"" + ui->pilotsSearchComboBox->currentText() + "\" LIKE \"%" + arg1 + "%\" AND ID > 1");
 }
 
 void PilotsWidget::tableView_selectionChanged()//const QItemSelection &index, const QItemSelection &
-{   
-    if (this->findChild<NewPilotDialog*>("NewPilot") != nullptr) {
+{
+    if (this->findChild<NewPilotDialog*>() != nullptr) {
         DEB("Selection changed. Deleting orphaned dialog.");
         delete this->findChild<NewPilotDialog*>();
         /// [F] if the user changes the selection without making any changes,
         /// if(selectedPilots.length() == 1) spawns a new dialog without the
         /// old one being deleted, since neither accept() nor reject() was emitted.
-        /// Is this a reasonable way of avoiding pollution?
+        /// Is this a reasonable way of avoiding pollution? Creating the widgets on the
+        /// stack does not seem to solve the problem since the Dialog does not get deleted
+        /// until either accept() or reject() is emitted so I went for this solution.
     }
-    auto *selection = ui->tableView->selectionModel();
+    auto *selection = ui->pilotsTableView->selectionModel();
     selectedPilots.clear();
 
     for (const auto& row : selection->selectedRows()) {
@@ -94,7 +90,6 @@ void PilotsWidget::tableView_selectionChanged()//const QItemSelection &index, co
     if(selectedPilots.length() == 1) {
 
         NewPilotDialog* np = new NewPilotDialog(selectedPilots.first(), this);
-        DEB("new dialog: " << np->objectName());
         QObject::connect(np, &QDialog::accepted,
                          this, &PilotsWidget::pilot_editing_finished);
         QObject::connect(np, &QDialog::rejected,
@@ -116,9 +111,11 @@ void PilotsWidget::tableView_headerClicked(int column)
 void PilotsWidget::on_newButton_clicked()
 {
     NewPilotDialog* np = new NewPilotDialog(this);
+    QObject::connect(np,   &QDialog::accepted,
+                     this, &PilotsWidget::pilot_editing_finished);
+    QObject::connect(np,   &QDialog::rejected,
+                     this, &PilotsWidget::pilot_editing_finished);
     np->setAttribute(Qt::WA_DeleteOnClose);
-    connect(np, SIGNAL(accepted()), this, SLOT(pilot_editing_finished()));
-    connect(np, SIGNAL(rejected()), this, SLOT(pilot_editing_finished()));
     np->exec();
 }
 
@@ -139,14 +136,14 @@ void PilotsWidget::on_deletePushButton_clicked()
         /// for example, the user has changed airlines and does not want to have his 'old'
         /// colleagues polluting his logbook anymore.
         /// On the other hand we could run into issues with foreign key constraints on the
-        /// flights table (see on_delete_unsuccessful) below
+        /// flights table (see on_delete_unsuccessful) below.
+        /// I think batch-editing should be implemented at some point, but batch-deleting should not.
 
     } else if (selectedPilots.length() == 1) {
         auto entry = aDB()->getPilotEntry(selectedPilots.first());
         aDB()->remove(entry);
         }
-    refreshModelAndView();
-
+    model->select();
 }
 
 void PilotsWidget::on_deleteUnsuccessful()
@@ -183,5 +180,5 @@ void PilotsWidget::on_deleteUnsuccessful()
 
 void PilotsWidget::pilot_editing_finished()
 {
-    refreshModelAndView();
+    model->select();
 }

+ 3 - 5
src/gui/widgets/pilotswidget.h

@@ -53,22 +53,20 @@ private slots:
 
     void pilot_editing_finished();
 
-    void on_searchLineEdit_textChanged(const QString &arg1);
+    void on_pilotSearchLineEdit_textChanged(const QString &arg1);
 
 private:
     Ui::PilotsWidget *ui;
 
-    QSqlTableModel *model = new QSqlTableModel(this);
+    QSqlTableModel *model;
 
-    QTableView *view = new QTableView(this);
+    QTableView *view;
 
     qint32 sortColumn;
 
     QVector<qint32> selectedPilots;
 
     void setupModelAndView();
-
-    void refreshModelAndView();
 };
 
 #endif // PILOTSWIDGET_H

+ 9 - 9
src/gui/widgets/pilotswidget.ui

@@ -6,8 +6,8 @@
    <rect>
     <x>0</x>
     <y>0</y>
-    <width>1920</width>
-    <height>1080</height>
+    <width>1280</width>
+    <height>720</height>
    </rect>
   </property>
   <property name="windowTitle">
@@ -15,9 +15,9 @@
   </property>
   <layout class="QGridLayout" name="gridLayout">
    <item row="0" column="0">
-    <widget class="QTableView" name="tableView"/>
+    <widget class="QTableView" name="pilotsTableView"/>
    </item>
-   <item row="0" column="1">
+   <item row="0" column="1" rowspan="3">
     <layout class="QHBoxLayout" name="horizontalLayout">
      <item>
       <spacer name="horizontalSpacer">
@@ -53,7 +53,7 @@
           </spacer>
          </item>
          <item row="1" column="0">
-          <layout class="QGridLayout" name="gridLayout_2">
+          <layout class="QGridLayout" name="pilotsDiasogGridLayout">
            <item row="0" column="0">
             <widget class="QLabel" name="searchLabel">
              <property name="text">
@@ -62,7 +62,7 @@
             </widget>
            </item>
            <item row="0" column="1">
-            <widget class="QLineEdit" name="searchLineEdit"/>
+            <widget class="QLineEdit" name="pilotSearchLineEdit"/>
            </item>
            <item row="1" column="0">
             <widget class="QLabel" name="searchInLabel">
@@ -72,7 +72,7 @@
             </widget>
            </item>
            <item row="1" column="1">
-            <widget class="QComboBox" name="searchComboBox">
+            <widget class="QComboBox" name="pilotsSearchComboBox">
              <item>
               <property name="text">
                <string>Last Name</string>
@@ -139,14 +139,14 @@
     </layout>
    </item>
    <item row="1" column="0">
-    <widget class="QPushButton" name="newButton">
+    <widget class="QPushButton" name="newPilotPushButton">
      <property name="text">
       <string>New Pilot</string>
      </property>
     </widget>
    </item>
    <item row="2" column="0">
-    <widget class="QPushButton" name="deletePushButton">
+    <widget class="QPushButton" name="deletPilotButton">
      <property name="text">
       <string>Delete Pilot</string>
      </property>

+ 1 - 1
src/gui/widgets/settingswidget.cpp

@@ -18,7 +18,7 @@
 #include "settingswidget.h"
 #include "ui_settingswidget.h"
 #include "src/database/dbinfo.h"
-#include "src/functions/adebug.h"
+#include "src/testing/adebug.h"
 
 static const auto FIRSTNAME_VALID = QPair<QString, QRegularExpression> {
     "picfirstnameLineEdit", QRegularExpression("[a-zA-Z]+")};

+ 1 - 1
src/gui/widgets/totalswidget.cpp

@@ -1,6 +1,6 @@
 #include "totalswidget.h"
 #include "ui_totalswidget.h"
-#include "src/functions/adebug.h"
+#include "src/testing/adebug.h"
 
 TotalsWidget::TotalsWidget(QWidget *parent) :
     QWidget(parent),

+ 73 - 0
src/testing/abenchmark.cpp

@@ -0,0 +1,73 @@
+/*
+ *openPilot Log - A FOSS Pilot Logbook Application
+ *Copyright (C) 2020  Felix Turowsky
+ *
+ *This program is free software: you can redistribute it and/or modify
+ *it under the terms of the GNU General Public License as published by
+ *the Free Software Foundation, either version 3 of the License, or
+ *(at your option) any later version.
+ *
+ *This program is distributed in the hope that it will be useful,
+ *but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *GNU General Public License for more details.
+ *
+ *You should have received a copy of the GNU General Public License
+ *along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+#include "abenchmark.h"
+
+ABenchmark::ABenchmark()
+{
+
+}
+
+ABenchmark::ABenchmark(void (*function_one)(), void (*function_two)(), int number_of_runs)
+{
+        auto start1 = std::chrono::high_resolution_clock::now();
+        for (int i = 0; i < number_of_runs; i++) {
+            function_one();
+        }
+        auto stop1 = std::chrono::high_resolution_clock::now();
+        DEB("First Function execution finished (" << number_of_runs << " runs)");
+
+
+        auto start2 = std::chrono::high_resolution_clock::now();
+        for (int i = 0; i < number_of_runs; i++) {
+            function_two();
+        }
+        auto stop2 = std::chrono::high_resolution_clock::now();
+        DEB("Second Function execution finished (" << number_of_runs << " runs)");
+
+        DEB("Execution time for function 1: "
+            << std::chrono::duration_cast<std::chrono::milliseconds>(stop1 - start1).count()
+            << "milliseconds.");
+        DEB("Execution time for function 2: "
+            << std::chrono::duration_cast<std::chrono::milliseconds>(stop2 - start2).count()
+            << "milliseconds.");
+}
+
+ABenchmark::ABenchmark(bool (*function_one)(), bool (*function_two)(), int number_of_runs)
+{
+    auto start1 = std::chrono::high_resolution_clock::now();
+    for (int i = 0; i < number_of_runs; i++) {
+        function_one();
+    }
+    auto stop1 = std::chrono::high_resolution_clock::now();
+    DEB("First Function execution finished (" << number_of_runs << " runs)");
+
+
+    auto start2 = std::chrono::high_resolution_clock::now();
+    for (int i = 0; i < number_of_runs; i++) {
+        function_two();
+    }
+    auto stop2 = std::chrono::high_resolution_clock::now();
+    DEB("Second Function execution finished (" << number_of_runs << " runs)");
+
+    DEB("Execution time for function 1: "
+        << std::chrono::duration_cast<std::chrono::milliseconds>(stop1 - start1).count()
+        << "milliseconds.");
+    DEB("Execution time for function 2: "
+        << std::chrono::duration_cast<std::chrono::milliseconds>(stop2 - start2).count()
+        << "milliseconds.");
+}

+ 45 - 0
src/testing/abenchmark.h

@@ -0,0 +1,45 @@
+/*
+ *openPilot Log - A FOSS Pilot Logbook Application
+ *Copyright (C) 2020  Felix Turowsky
+ *
+ *This program is free software: you can redistribute it and/or modify
+ *it under the terms of the GNU General Public License as published by
+ *the Free Software Foundation, either version 3 of the License, or
+ *(at your option) any later version.
+ *
+ *This program is distributed in the hope that it will be useful,
+ *but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *GNU General Public License for more details.
+ *
+ *You should have received a copy of the GNU General Public License
+ *along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+#ifndef ABENCHMARK_H
+#define ABENCHMARK_H
+
+#include <QObject>
+#include "src/testing/adebug.h"
+#include "src/experimental/adatabase.h"
+#include "src/experimental/aentry.h"
+#include "src/experimental/decl.h"
+
+/*!
+ * \brief The ABenchmark class provides quick access to benchmarking two functions for
+ * the purpose of performance testing.
+ *
+ */
+class ABenchmark
+{
+public:
+    ABenchmark();
+
+    ABenchmark(void (*function_one)(), void (*function_two)(), int number_of_runs);
+
+    ABenchmark(bool (*function_one)(), bool (*function_two)(), int number_of_runs);
+
+    //ABenchmark(experimental::AEntry (*function_one)(experimental::DataPosition), experimental::AEntry (*function_two)(int), int number_of_runs);
+
+};
+
+#endif // ABENCHMARK_H

+ 1 - 2
src/functions/adebug.h → src/testing/adebug.h

@@ -3,8 +3,7 @@
 
 #include <QDebug>
 
-// Debug Makro
 #define DEB(expr) \
     qDebug() << __PRETTY_FUNCTION__ << "\t" << expr
 
-#endif // ADEBUG_H
+#endif

+ 59 - 0
src/testing/atimer.cpp

@@ -0,0 +1,59 @@
+/*
+ *openPilot Log - A FOSS Pilot Logbook Application
+ *Copyright (C) 2020  Felix Turowsky
+ *
+ *This program is free software: you can redistribute it and/or modify
+ *it under the terms of the GNU General Public License as published by
+ *the Free Software Foundation, either version 3 of the License, or
+ *(at your option) any later version.
+ *
+ *This program is distributed in the hope that it will be useful,
+ *but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *GNU General Public License for more details.
+ *
+ *You should have received a copy of the GNU General Public License
+ *along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+#include "atimer.h"
+
+
+ATimer::ATimer(QObject *parent) : QObject(parent)
+{
+     start = std::chrono::high_resolution_clock::now();
+     if(parent == nullptr) {
+         DEB("Starting Timer... ");
+     } else {
+         DEB("Starting Timer for: " << parent->objectName());
+     }
+
+}
+
+ATimer::~ATimer()
+{
+    stop = std::chrono::high_resolution_clock::now();
+    if(parent() == nullptr) {
+        DEB("Execution time: "
+                 << std::chrono::duration_cast<std::chrono::milliseconds>(stop - start).count()
+                 << "milliseconds.");
+    } else {
+        DEB("Execution time for: " << parent()->objectName() << ": "
+                 << std::chrono::duration_cast<std::chrono::milliseconds>(stop - start).count()
+                 << "milliseconds.");
+    }
+}
+
+long ATimer::timeNow()
+{
+    intermediate_point = std::chrono::high_resolution_clock::now();
+    if(parent() == nullptr) {
+        DEB("Intermediate time: "
+                 << std::chrono::duration_cast<std::chrono::milliseconds>(intermediate_point - start).count()
+                 << "milliseconds.");
+    } else {
+        DEB("Intermediate time for: " << parent()->objectName() << ": "
+                 << std::chrono::duration_cast<std::chrono::milliseconds>(intermediate_point - start).count()
+                 << "milliseconds.");
+    }
+    return std::chrono::duration_cast<std::chrono::milliseconds>(intermediate_point - start).count();
+}

+ 59 - 0
src/testing/atimer.h

@@ -0,0 +1,59 @@
+/*
+ *openPilot Log - A FOSS Pilot Logbook Application
+ *Copyright (C) 2020  Felix Turowsky
+ *
+ *This program is free software: you can redistribute it and/or modify
+ *it under the terms of the GNU General Public License as published by
+ *the Free Software Foundation, either version 3 of the License, or
+ *(at your option) any later version.
+ *
+ *This program is distributed in the hope that it will be useful,
+ *but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *GNU General Public License for more details.
+ *
+ *You should have received a copy of the GNU General Public License
+ *along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+#ifndef ATIMER_H
+#define ATIMER_H
+
+#include <QObject>
+#include <chrono>
+#include <QDebug>
+#include "src/testing/adebug.h"
+
+/*!
+ * \brief The ATimer class provides an easy to use performance timer.
+ *
+ * It automatically stops when going out of scope and prints the elapsed time
+ * to the console. Intermediate timings can be manually obtained with timeNow().
+ *
+ * It can be given a QObject as a parent to time its lifetime or can be used without
+ * parent in any context.
+ */
+class ATimer : public QObject
+{
+    Q_OBJECT
+public:
+    ATimer(QObject* parent = nullptr);
+    ~ATimer();
+
+    /*!
+     * \brief timeNow takes an intermediate timing and returns miliseconds elapsed.
+     */
+    long timeNow();
+private:
+
+    std::chrono::high_resolution_clock::time_point start;
+
+    std::chrono::high_resolution_clock::time_point intermediate_point;
+
+    std::chrono::high_resolution_clock::time_point stop;
+
+    double duration;
+
+};
+
+
+#endif // ATIMER_H