Browse Source

Implemented functionality of airportWidget, Clean-up and bug fixes

airportWidget:
The airportWidget now allows for submitting of new airports and deleting of entries in the database.

Database: 
Fixed Database::disconnect to properly disconnect and remove the database connection
FirstRunDialog:
Added Logic for writing CurrencySettings iaw user input
NewFlightDialog:
Fixed a typo in onAcftLineEdit editing finished that resulted in an always true user condition for an input verification step. Brought ValidationItem enum into ValidationState class to avoid similar typos in the future.
opl.h:
Added a missing entry into DB_TABLES which was crashing the database creation logic
Row:
Added getPosition() for debugging purposes
Felix 2 years ago
parent
commit
1294f4bc1a

+ 0 - 57
docs/man/man3/Opl.3

@@ -1,57 +0,0 @@
-.TH "Opl" 3 "Fri Mar 4 2022" "openPilotLog" \" -*- nroff -*-
-.ad l
-.nh
-.SH NAME
-Opl \- A namespace to collect constants and enums used throughout the application\&.  
-
-.SH SYNOPSIS
-.br
-.PP
-.SS "Namespaces"
-
-.in +1c
-.ti -1c
-.RI " \fBDb\fP"
-.br
-.in -1c
-.SS "Classes"
-
-.in +1c
-.ti -1c
-.RI "class \fBANotificationHandler\fP"
-.br
-.RI "The \fBANotificationHandler\fP class handles displaying of user-directed messages\&. It displays information to the user in a QMessageBox and forwards the displayed message to \fBALog\fP so it is written to the console and log files\&. The INFO, WARN and CRIT makros provide convenient access\&. "
-.in -1c
-.SS "Enumerations"
-
-.in +1c
-.ti -1c
-.RI "enum class \fBTranslations\fP { \fBEnglish\fP, \fBGerman\fP, \fBSpanish\fP }"
-.br
-.ti -1c
-.RI "enum \fBPilotFunction\fP { \fBPIC\fP = 0, \fBPICUS\fP = 1, \fBSIC\fP = 2, \fBDUAL\fP = 3, \fBFI\fP = 4 }"
-.br
-.RI "PilotFunction Pilot in Command, Pilot in Command under Supervision, Second in Command (Co-Pilot), Dual, Flight Instructor\&. "
-.in -1c
-.SS "Functions"
-
-.in +1c
-.ti -1c
-.RI "void \fBloadPilotFunctios\fP (QComboBox *combo_box)"
-.br
-.ti -1c
-.RI "void \fBloadApproachTypes\fP (QComboBox *combo_box)"
-.br
-.in -1c
-.SH "Detailed Description"
-.PP 
-A namespace to collect constants and enums used throughout the application\&. 
-
-The opl namespace collects enums and constants that are used throughout the application and provide uniform access\&.
-.PP
-The date, time and datetime namespaces include enums used to differentiate date and time formats for QDate, QTime and QDateTime that deviate from standard values included in the Qt Framework like Qt::ISODate and are to be used in conjunction with the \&.toString() members of these classes\&.
-.PP
-The db namespace contains constants for programatically accessing the database in a fast and uniform manner\&. 
-.SH "Author"
-.PP 
-Generated automatically by Doxygen for openPilotLog from the source code\&.

+ 0 - 31
docs/man/man3/Opl_ANotificationHandler.3

@@ -1,31 +0,0 @@
-.TH "Opl::ANotificationHandler" 3 "Fri Mar 4 2022" "openPilotLog" \" -*- nroff -*-
-.ad l
-.nh
-.SH NAME
-Opl::ANotificationHandler \- The \fBANotificationHandler\fP class handles displaying of user-directed messages\&. It displays information to the user in a QMessageBox and forwards the displayed message to \fBALog\fP so it is written to the console and log files\&. The INFO, WARN and CRIT makros provide convenient access\&.  
-
-.SH SYNOPSIS
-.br
-.PP
-.PP
-\fC#include <opl\&.h>\fP
-.SS "Static Public Member Functions"
-
-.in +1c
-.ti -1c
-.RI "static void \fBinfo\fP (const QString msg, QWidget *parent=nullptr)"
-.br
-.ti -1c
-.RI "static void \fBwarn\fP (const QString msg, QWidget *parent=nullptr)"
-.br
-.ti -1c
-.RI "static void \fBcrit\fP (const QString msg, QWidget *parent=nullptr)"
-.br
-.in -1c
-.SH "Detailed Description"
-.PP 
-The \fBANotificationHandler\fP class handles displaying of user-directed messages\&. It displays information to the user in a QMessageBox and forwards the displayed message to \fBALog\fP so it is written to the console and log files\&. The INFO, WARN and CRIT makros provide convenient access\&. 
-
-.SH "Author"
-.PP 
-Generated automatically by Doxygen for openPilotLog from the source code\&.

+ 0 - 20
docs/man/man3/Opl_Db.3

@@ -1,20 +0,0 @@
-.TH "Opl::Db" 3 "Fri Mar 4 2022" "openPilotLog" \" -*- nroff -*-
-.ad l
-.nh
-.SH NAME
-Opl::Db
-.SH SYNOPSIS
-.br
-.PP
-.SH "Detailed Description"
-.PP 
-The opl::db namespace provides string literals to programatically access the database
-.PP
-Example usage, do: newData\&.insert(opl::db::FLIGHTS_DEP, ui->deptLocLineEdit->text()); newData\&.value(opl::db::AIRCRAFT_MULTIPILOT);
-.PP
-instead of: newData\&.insert('dept', ui->deptLocLineEdit->text()); newData\&.value('multipilot');
-.PP
-Declaring these literals here avoids memory allocation at runtime for construction of temporary qstrings like ('dept')\&. See https://doc.qt.io/qt-5/qstring.html#QStringLiteral and ensures uniform use throughout the application\&. 
-.SH "Author"
-.PP 
-Generated automatically by Doxygen for openPilotLog from the source code\&.

+ 0 - 1
main.cpp

@@ -20,7 +20,6 @@
 #include "src/functions/alog.h"
 #include "src/gui/dialogues/firstrundialog.h"
 #include "src/classes/arunguard.h"
-#include "src/database/database.h"
 #include "src/classes/asettings.h"
 #include "src/classes/astandardpaths.h"
 #include "src/classes/asettings.h"

+ 7 - 12
src/database/database.cpp

@@ -55,9 +55,13 @@ bool Database::connect()
 
 void Database::disconnect()
 {
+    QString connection_name;
+    {
     auto db = Database::database();
+    connection_name = db.connectionName();
     db.close();
-    db.removeDatabase(db.connectionName());
+    }
+    QSqlDatabase::removeDatabase(connection_name);
     LOG << "Database connection closed.";
 }
 
@@ -127,15 +131,6 @@ QSqlDatabase Database::database()
     return QSqlDatabase::database(QStringLiteral("qt_sql_default_connection"));
 }
 
-//bool Database::commit(const AEntry &entry)
-//{
-//    if (exists(entry)) {
-//        return update(entry);
-//    } else {
-//        return insert(entry);
-//    }
-//}
-
 bool Database::commit(const OPL::Row &row)
 {
     if (!row.isValid())
@@ -326,7 +321,7 @@ bool Database::update(const OPL::Row &updated_row)
 
     if (query.exec())
     {
-        DEB << "Entry successfully committed.";
+        LOG << QString("Entry successfully committed. %1").arg(updated_row.getPosition());
         emit dataBaseUpdated(updated_row.getTable());
         return true;
     } else {
@@ -369,7 +364,7 @@ bool Database::insert(const OPL::Row &new_row)
     //check result.
     if (query.exec())
     {
-        DEB << "Entry successfully committed.";
+        LOG << QString("Entry successfully committed. %1").arg(new_row.getPosition());
         emit dataBaseUpdated(new_row.getTable());
         return true;
     } else {

+ 1 - 1
src/database/database.h

@@ -75,7 +75,7 @@ private:
         OPL::DbTable::Aircraft,
         OPL::DbTable::Airports,
         OPL::DbTable::Currencies,
-        OPL::DbTable::Changelog
+        OPL::DbTable::Changelog,
     };
 
 

+ 19 - 0
src/database/row.cpp

@@ -69,6 +69,13 @@ const QString Row::getTableName() const
     return OPL::GLOBALS->getDbTableName(table);
 }
 
+//TODO: Remove when tweaking for performance. Used for debugging
+const QString Row::getPosition() const
+{
+    return QString("Table: %1 / RowID: %2").arg(OPL::GLOBALS->getDbTableName(table), QString::number(rowId));
+}
+
+//TODO: Remove when tweaking for performance. Used for debugging
 OPL::Row::operator QString() const
 {
     if (!isValid()) {
@@ -189,4 +196,16 @@ CurrencyEntry::CurrencyEntry(int row_id, const RowData_T &row_data)
     : Row(DbTable::Currencies, row_id, row_data)
 {}
 
+AirportEntry::AirportEntry()
+    : Row(DbTable::Airports, 0)
+{}
+
+AirportEntry::AirportEntry(const RowData_T &row_data)
+    : Row(DbTable::Airports, 0, row_data)
+{}
+
+AirportEntry::AirportEntry(int row_id, const RowData_T &row_data)
+    : Row(DbTable::Airports, row_id, row_data)
+{}
+
 } // namespace OPL

+ 12 - 0
src/database/row.h

@@ -60,6 +60,7 @@ public:
     void setRowId(int value);
     OPL::DbTable getTable() const;
     const QString getTableName() const;
+    const QString getPosition() const;
 
     bool isValid() const {return hasData && valid;}
 
@@ -148,5 +149,16 @@ public:
     CurrencyEntry(int row_id, const RowData_T &row_data);
 };
 
+/*!
+ * \brief A Row representing an Airport entry. See Row class for details.
+ */
+class AirportEntry : public Row
+{
+public:
+    AirportEntry();
+    AirportEntry(const RowData_T &row_data);
+    AirportEntry(int row_id, const RowData_T &row_data);
+};
+
 } // namespace OPL
 #endif // ROW_H

+ 25 - 10
src/gui/dialogues/firstrundialog.cpp

@@ -189,7 +189,7 @@ bool FirstRunDialog::finishSetup()
         message_box.exec();
         return false;
     }
-    DB->disconnect(); // connection will be re-established by main()
+    DB->disconnect(); // Connection will be re-established by MainWindow
     return true;
 }
 
@@ -205,6 +205,7 @@ bool FirstRunDialog::downloadTemplates(QString branch_name)
     QStringList template_table_names;
     for (const auto table : DB->getTemplateTables())
         template_table_names.append(OPL::GLOBALS->getDbTableName(table));
+
     // Download json files
     for (const auto& table_name : template_table_names) {
         QEventLoop loop;
@@ -218,8 +219,10 @@ bool FirstRunDialog::downloadTemplates(QString branch_name)
         loop.exec(); // event loop waits for download done signal before allowing loop to continue
 
         QFileInfo downloaded_file(template_dir.filePath(table_name + QLatin1String(".json")));
-        if (downloaded_file.size() == 0)
+        if (downloaded_file.size() == 0) {
+            LOG << "Unable to download template files (SSL / Network Error)";
             return false; // ssl/network error
+        }
     }
     // Download checksum files
     for (const auto& table_name : template_table_names) {
@@ -236,8 +239,10 @@ bool FirstRunDialog::downloadTemplates(QString branch_name)
         loop.exec(); // event loop waits for download done signal before allowing loop to continue
 
         QFileInfo downloaded_file(template_dir.filePath(table_name + QLatin1String(".md5")));
-        if (downloaded_file.size() == 0)
+        if (downloaded_file.size() == 0) {
+            LOG << "Unable to download checksum files (SSL / Network Error)";
             return false; // ssl/network error
+        }
     }
     // check downloadad files
     return verifyTemplates();
@@ -335,7 +340,7 @@ bool FirstRunDialog::createUserEntry()
 
 bool FirstRunDialog::writeCurrencies()
 {
-    const QList<QPair<OPL::CurrencyName, QDateEdit*>> currencies_list = {
+    const QHash<OPL::CurrencyName, QDateEdit*> currencies_list = {
         {OPL::CurrencyName::Licence,    ui->currLicDateEdit},
         {OPL::CurrencyName::TypeRating, ui->currTrDateEdit},
         {OPL::CurrencyName::LineCheck,  ui->currLckDateEdit},
@@ -343,18 +348,28 @@ bool FirstRunDialog::writeCurrencies()
         {OPL::CurrencyName::Custom1,    ui->currCustom1DateEdit},
         {OPL::CurrencyName::Custom2,    ui->currCustom2DateEdit},
     };
+    const QHash<OPL::CurrencyName, ASettings::UserData> settings_list = {
+        {OPL::CurrencyName::Licence,    ASettings::UserData::ShowLicCurrency },
+        {OPL::CurrencyName::TypeRating, ASettings::UserData::ShowTrCurrency },
+        {OPL::CurrencyName::LineCheck,  ASettings::UserData::ShowLckCurrency },
+        {OPL::CurrencyName::Medical,    ASettings::UserData::ShowMedCurrency },
+        {OPL::CurrencyName::Custom1,    ASettings::UserData::ShowCustom1Currency },
+        {OPL::CurrencyName::Custom2,    ASettings::UserData::ShowCustom2Currency },
+    };
 
     QDate today = QDate::currentDate();
-    for (const auto &pair : currencies_list) {
+    for (const auto &date_edit : currencies_list) {
+        const auto enum_value = currencies_list.key(date_edit);
         // only write dates that have been edited
-        if (pair.second->date() != today) {
-            RowData_T row_data = {{OPL::Db::CURRENCIES_EXPIRYDATE, pair.second->date().toString(Qt::ISODate)}};
-            if (pair.first == OPL::CurrencyName::Custom1)
+        if (date_edit->date() != today) {
+            RowData_T row_data = {{OPL::Db::CURRENCIES_EXPIRYDATE, date_edit->date().toString(Qt::ISODate)}};
+            if (enum_value == OPL::CurrencyName::Custom1)
                 row_data.insert(OPL::Db::CURRENCIES_CURRENCYNAME, ui->currCustom1LineEdit->text());
-            else if(pair.first == OPL::CurrencyName::Custom2)
+            else if(enum_value == OPL::CurrencyName::Custom2)
                 row_data.insert(OPL::Db::CURRENCIES_CURRENCYNAME, ui->currCustom2LineEdit->text());
 
-            OPL::CurrencyEntry entry(static_cast<int>(pair.first), row_data);
+            ASettings::write(settings_list.value(enum_value), true); // Show selected currency on Home Screen
+            OPL::CurrencyEntry entry(static_cast<int>(enum_value), row_data);
             if (!DB->commit(entry))
                 return false;
         }

+ 16 - 2
src/gui/dialogues/newairportdialog.cpp

@@ -3,6 +3,8 @@
 #include <QValidator>
 
 #include "src/opl.h"
+#include "src/database/database.h"
+#include "src/database/row.h"
 
 NewAirportDialog::NewAirportDialog(QWidget *parent) :
     QDialog(parent),
@@ -37,8 +39,20 @@ void NewAirportDialog::on_buttonBox_accepted()
         return;
     }
     // create Entry object
-    // if submit() then accept()
-    QDialog::accept();
+    RowData_T airport_data = {
+        {OPL::Db::AIRPORTS_ICAO, ui->icaoLineEdit->text()},
+        {OPL::Db::AIRPORTS_IATA, ui->iataLineEdit->text()},
+        {OPL::Db::AIRPORTS_LAT,  ui->latitudeLineEdit->text()},
+        {OPL::Db::AIRPORTS_LON,  ui->longitudeLineEdit->text()},
+    };
+
+    OPL::AirportEntry entry(airport_data);
+    if(DB->commit(entry))
+        QDialog::accept();
+    else {
+        WARN(tr("Unable to add Airport to the database. The following error has ocurred:<br><br>%1").arg(DB->lastError.text()));
+        return;
+    }
 }
 
 void NewAirportDialog::on_iataLineEdit_textChanged(const QString &arg1)

+ 8 - 9
src/gui/dialogues/newflightdialog.cpp

@@ -51,7 +51,6 @@ NewFlightDialog::NewFlightDialog(OPL::DbCompletionData &completion_data,
         ui->functionComboBox->setCurrentIndex(2);
         emit ui->sicNameLineEdit->editingFinished();
     }
-
     ui->doftLineEdit->setText(ADate::currentDate());
     emit ui->doftLineEdit->editingFinished();
 }
@@ -610,6 +609,7 @@ void NewFlightDialog::on_acftLineEdit_editingFinished()
     const auto line_edit = ui->acftLineEdit;
     int acft_id = completionData.tailsIdMap.key(line_edit->text());
     DEB << line_edit->text() << " has acft_id: " << acft_id;
+    DEB << completionData.tailsIdMap;
 
     if (acft_id != 0) { // Success
         onGoodInputReceived(line_edit);
@@ -617,14 +617,13 @@ void NewFlightDialog::on_acftLineEdit_editingFinished()
     }
     // check for whitespaces
     acft_id = completionData.tailsIdMap.key(line_edit->text().split(" ").first());
-    if (acft != 0) {
+    if (acft_id != 0) {
         line_edit->setText(completionData.tailsIdMap.value(acft_id));
         onGoodInputReceived(line_edit);
         return;
     }
 
 
-
     // try to use a completion
     if (!line_edit->completer()->currentCompletion().isEmpty() && !line_edit->text().isEmpty()) {
         line_edit->setText(line_edit->completer()->currentCompletion().split(QLatin1Char(' ')).first());
@@ -745,16 +744,16 @@ void NewFlightDialog::on_buttonBox_accepted()
         emit le->editingFinished();
     // If input verification is passed, continue, otherwise prompt user to correct
     if (!validationState.allValid()) {
-        const auto display_names = QHash<ValidationItem, QString> {
-            {ValidationItem::doft, QObject::tr("Date of Flight")},      {ValidationItem::dept, QObject::tr("Departure Airport")},
-            {ValidationItem::dest, QObject::tr("Destination Airport")}, {ValidationItem::tofb, QObject::tr("Time Off Blocks")},
-            {ValidationItem::tonb, QObject::tr("Time on Blocks")},      {ValidationItem::pic, QObject::tr("PIC Name")},
-            {ValidationItem::acft, QObject::tr("Aircraft Registration")}
+        const auto display_names = QHash<ValidationState::ValidationItem, QString> {
+            {ValidationState::ValidationItem::doft, QObject::tr("Date of Flight")},      {ValidationState::ValidationItem::dept, QObject::tr("Departure Airport")},
+            {ValidationState::ValidationItem::dest, QObject::tr("Destination Airport")}, {ValidationState::ValidationItem::tofb, QObject::tr("Time Off Blocks")},
+            {ValidationState::ValidationItem::tonb, QObject::tr("Time on Blocks")},      {ValidationState::ValidationItem::pic, QObject::tr("PIC Name")},
+            {ValidationState::ValidationItem::acft, QObject::tr("Aircraft Registration")}
         };
         QString missing_items;
         for (int i=0; i < mandatoryLineEdits->size(); i++) {
             if (!validationState.validAt(i)){
-                missing_items.append(display_names.value(static_cast<ValidationItem>(i)) + "<br>");
+                missing_items.append(display_names.value(static_cast<ValidationState::ValidationItem>(i)) + "<br>");
                 mandatoryLineEdits->at(i)->setStyleSheet(QStringLiteral("border: 1px solid red"));
             }
         }

+ 7 - 5
src/gui/dialogues/newflightdialog.h

@@ -30,11 +30,7 @@
 #include "src/opl.h"
 #include "src/database/row.h"
 
-/*!
- * \brief The ValidationItem enum contains the items that are mandatory for logging a flight:
- * Date of Flight, Departure, Destination, Time Off Blocks, Time On Blocks, Pilot in Command, Aircraft Registration
- */
-enum ValidationItem {doft = 0, dept = 1, dest = 2, tofb = 3, tonb = 4, pic = 5, acft = 6};
+
 
 /*!
  * \brief The ValidationState class encapsulates a QBitArray that has a bit set (or unset) depending on wether the
@@ -45,6 +41,12 @@ class ValidationState {
 public:
     ValidationState() = default;
 
+    /*!
+     * \brief The ValidationItem enum contains the items that are mandatory for logging a flight:
+     * Date of Flight, Departure, Destination, Time Off Blocks, Time On Blocks, Pilot in Command, Aircraft Registration
+     */
+    enum ValidationItem {doft = 0, dept = 1, dest = 2, tofb = 3, tonb = 4, pic = 5, acft = 6};
+
     void validate(ValidationItem item)             { validationArray[item] = true;};
     void validate(int index)                       { validationArray[index] = true;};
     void invalidate(ValidationItem item)           { validationArray[item] = false;}

+ 85 - 7
src/gui/widgets/airportwidget.cpp

@@ -11,8 +11,10 @@ AirportWidget::AirportWidget(QWidget *parent) :
     setupModelAndeView();
     setupSearch();
 
-    QObject::connect(model,     &QSqlTableModel::beforeUpdate,
-                     this,      &AirportWidget::onUpdate);
+    QObject::connect(model,                  &QSqlTableModel::beforeUpdate,
+                     this,                   &AirportWidget::onUpdate);
+    QObject::connect(view->selectionModel(), &QItemSelectionModel::selectionChanged,
+                     this,                   &AirportWidget::onSelectionChanged);
 }
 
 AirportWidget::~AirportWidget()
@@ -26,15 +28,14 @@ void AirportWidget::setupModelAndeView()
     model->setEditStrategy(QSqlTableModel::OnFieldChange);
     model->setTable(TABLE_NAME);
     model->select();
+    model->sort(1, Qt::AscendingOrder);
 
     view = ui->tableView;
     view->setModel(model);
 
-    // To Do: Decide on and implement editing behaviour
-    //view->setSelectionBehavior(QAbstractItemView::SelectRows);
-    //view->setSelectionMode(QAbstractItemView::SingleSelection);
-    //view->setEditTriggers(QAbstractItemView::NoEditTriggers);
     view->horizontalHeader()->setStretchLastSection(QHeaderView::Stretch);
+    view->setSelectionBehavior(QAbstractItemView::SelectRows);
+    view->setSelectionMode(QAbstractItemView::ExtendedSelection);
     view->hideColumn(0);
     view->resizeColumnsToContents();
     view->verticalHeader()->hide();
@@ -69,11 +70,88 @@ void AirportWidget::on_searchComboBox_currentIndexChanged(int index)
 void AirportWidget::on_newAirportPushButton_clicked()
 {
     auto ap_dialog = NewAirportDialog(this);
-    if (ap_dialog.exec() == QDialog::Accepted)
+    if (ap_dialog.exec() == QDialog::Accepted) {
         model->select();
+        emit DB->dataBaseUpdated(OPL::DbTable::Airports);
+    }
+}
+
+void AirportWidget::on_deletePushButton_clicked()
+{
+    DEB << "Airports selected: " << selectedEntries.length();
+    if (selectedEntries.length() == 0) {
+        WARN(tr("<br>No Airport selected.<br>"));
+        return;
+    } else if (selectedEntries.length() > 0 && selectedEntries.length() <= 10) {
+        QStringList selected_airport_names;
+        for (const auto row_id : qAsConst(selectedEntries)) {
+            const auto data = DB->getRowData(OPL::DbTable::Airports, row_id);
+            selected_airport_names.append(data.value(OPL::Db::AIRPORTS_NAME).toString());
+        }
+
+        QString selected_airports_string;
+
+        for (auto &name : selected_airport_names) {
+            selected_airports_string.append(name);
+            selected_airports_string.append(QStringLiteral("&nbsp;&nbsp;&nbsp;&nbsp;<br>"));
+        }
+        QMessageBox confirm(this);
+        confirm.setStandardButtons(QMessageBox::Yes | QMessageBox::No);
+        confirm.setDefaultButton(QMessageBox::No);
+        confirm.setIcon(QMessageBox::Question);
+        confirm.setWindowTitle("Delete Airport");
+        confirm.setText(tr("The following airport(s) will be deleted:<br><br><b><tt>"
+                           "%1<br></b></tt>"
+                           "Deleting airports is irreversible.<br>Do you want to proceed?"
+                           ).arg(selected_airports_string));
+        if (confirm.exec() == QMessageBox::Yes) {
+            for (auto& row_id : selectedEntries) {
+                if(!DB->remove(OPL::Row(OPL::DbTable::Airports, row_id))) {
+                    WARN(tr("<br>Unable to delete.<br><br>The following error has ocurred: %1"
+                                       ).arg(DB->lastError.text()));
+                    return;
+                }
+            }
+            INFO(tr("%1 Airports have been deleted successfully."
+                                   ).arg(QString::number(selectedEntries.length())));
+            model->select();
+        }
+    } else if (selectedEntries.length() > 10) {
+        QMessageBox confirm;
+        confirm.setStandardButtons(QMessageBox::Yes | QMessageBox::No);
+        confirm.setDefaultButton(QMessageBox::No);
+        confirm.setIcon(QMessageBox::Warning);
+        confirm.setWindowTitle("Delete Airports");
+        confirm.setText(tr("You have selected %1 airports.<br><br>"
+                           "Deleting airports is irreversible.<br><br>"
+                           "Are you sure you want to proceed?"
+                           ).arg(QString::number(selectedEntries.length())));
+        if(confirm.exec() == QMessageBox::Yes) {
+            if (!DB->removeMany(OPL::DbTable::Airports, selectedEntries)) {
+                WARN(tr("Unable to delete. No changes have been made to the database. The following error has ocurred:<br><br>%1").arg(DB->lastError.text()));
+                return;
+            }
+            INFO(tr("%1 Airports have been deleted successfully."
+                                   ).arg(QString::number(selectedEntries.length())));
+            model->select();
+        }
+        model->select();
+    }
 }
 
 void AirportWidget::onUpdate()
 {
     emit DB->dataBaseUpdated(OPL::DbTable::Airports);
 }
+
+void AirportWidget::onSelectionChanged()
+{
+    selectedEntries.clear();
+    for (const auto& row : view->selectionModel()->selectedRows()) {
+        selectedEntries.append(row.data().toInt());
+        DEB << "Selected Airport(s) with ID: " << selectedEntries;
+    }
+}
+
+
+

+ 5 - 0
src/gui/widgets/airportwidget.h

@@ -32,10 +32,15 @@ private slots:
      */
     void onUpdate();
 
+    void onSelectionChanged();
+
+    void on_deletePushButton_clicked();
+
 private:
     Ui::AirportWidget *ui;
     QSqlTableModel *model;
     QTableView *view;
+    QList<int> selectedEntries;
 
     void setupModelAndeView();
     void setupSearch();

+ 13 - 6
src/gui/widgets/airportwidget.ui

@@ -14,9 +14,19 @@
    <string>Form</string>
   </property>
   <layout class="QGridLayout" name="gridLayout">
+   <item row="0" column="0" colspan="4">
+    <widget class="QTableView" name="tableView"/>
+   </item>
    <item row="1" column="1">
     <widget class="QLineEdit" name="searchLineEdit"/>
    </item>
+   <item row="2" column="0" colspan="4">
+    <widget class="QPushButton" name="newAirportPushButton">
+     <property name="text">
+      <string>Add New Airport</string>
+     </property>
+    </widget>
+   </item>
    <item row="1" column="2">
     <widget class="QLabel" name="searchInLabel">
      <property name="text">
@@ -34,13 +44,10 @@
    <item row="1" column="3">
     <widget class="QComboBox" name="searchComboBox"/>
    </item>
-   <item row="0" column="0" colspan="4">
-    <widget class="QTableView" name="tableView"/>
-   </item>
-   <item row="2" column="0" colspan="4">
-    <widget class="QPushButton" name="newAirportPushButton">
+   <item row="3" column="0" colspan="4">
+    <widget class="QPushButton" name="deletePushButton">
      <property name="text">
-      <string>Add New Airport</string>
+      <string>Delete Selected Airport</string>
      </property>
     </widget>
    </item>

+ 12 - 0
src/opl.h

@@ -194,6 +194,7 @@ private:
         {DbTable::Aircraft,     QStringLiteral("aircraft")},
         {DbTable::Airports,     QStringLiteral("airports")},
         {DbTable::Currencies,   QStringLiteral("currencies")},
+        {DbTable::Changelog,    QStringLiteral("changelog")},
     };
     const static inline QStringList APPROACH_TYPES = {
             QStringLiteral("VISUAL"),
@@ -319,6 +320,17 @@ const inline auto  SIMULATORS_ACFT        = QStringLiteral("aircraftType");
 const inline auto  SIMULATORS_REG         = QStringLiteral("registration");
 const inline auto  SIMULATORS_REMARKS     = QStringLiteral("remarks");
 
+// Airports table
+const inline auto AIRPORTS_ICAO           = QStringLiteral("icao");
+const inline auto AIRPORTS_IATA           = QStringLiteral("iata");
+const inline auto AIRPORTS_NAME           = QStringLiteral("name");
+const inline auto AIRPORTS_LAT            = QStringLiteral("lat");
+const inline auto AIRPORTS_LON            = QStringLiteral("long");
+const inline auto AIRPORTS_COUNTRY        = QStringLiteral("country");
+const inline auto AIRPORTS_ALTITIDUE      = QStringLiteral("alt");
+const inline auto AIRPORTS_UTC_OFFSET     = QStringLiteral("utcoffset");
+const inline auto AIRPORTS_TZ_OLSON       = QStringLiteral("tzolson");
+
 // all tables
 const inline auto  ROWID                  = QStringLiteral("rowid");
 const inline auto  NULL_TIME_hhmm         = QStringLiteral("00:00");