diff --git a/CMakeLists.txt b/CMakeLists.txt index 0b0b1830..a2159a06 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -8,7 +8,7 @@ set(CMAKE_AUTOUIC ON) set(CMAKE_AUTOMOC ON) set(CMAKE_AUTORCC ON) -set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(QT_MIN_VERSION "5.5.1") @@ -53,34 +53,38 @@ set(PROJECT_SOURCES src/gui/dialogues/exporttocsvdialog.h src/gui/dialogues/exporttocsvdialog.cpp src/gui/dialogues/exporttocsvdialog.ui + src/gui/dialogues/entryeditdialog.h + src/gui/dialogues/entryeditdialog.cpp + # Widgets - src/gui/widgets/tailswidget.h - src/gui/widgets/tailswidget.cpp - src/gui/widgets/aircraftwidget.ui - src/gui/widgets/airportwidget.h - src/gui/widgets/airportwidget.cpp - src/gui/widgets/airportwidget.ui + src/gui/widgets/homewidget.h + src/gui/widgets/homewidget.cpp + src/gui/widgets/homewidget.ui + src/gui/widgets/totalswidget.h + src/gui/widgets/totalswidget.cpp + src/gui/widgets/totalswidget.ui src/gui/widgets/backupwidget.h src/gui/widgets/backupwidget.cpp src/gui/widgets/backupwidget.ui src/gui/widgets/debugwidget.h src/gui/widgets/debugwidget.cpp src/gui/widgets/debugwidget.ui - src/gui/widgets/homewidget.h - src/gui/widgets/homewidget.cpp - src/gui/widgets/homewidget.ui - src/gui/widgets/logbookwidget.h - src/gui/widgets/logbookwidget.cpp - src/gui/widgets/logbookwidget.ui - src/gui/widgets/pilotswidget.h - src/gui/widgets/pilotswidget.cpp - src/gui/widgets/pilotswidget.ui + src/gui/widgets/settingswidget.h src/gui/widgets/settingswidget.cpp src/gui/widgets/settingswidget.ui - src/gui/widgets/totalswidget.h - src/gui/widgets/totalswidget.cpp - src/gui/widgets/totalswidget.ui + + src/gui/widgets/tableeditwidget.h + src/gui/widgets/tableeditwidget.cpp + src/gui/widgets/pilottableeditwidget.h + src/gui/widgets/pilottableeditwidget.cpp + src/gui/widgets/tailtableeditwidget.h + src/gui/widgets/tailtableeditwidget.cpp + src/gui/widgets/airporttableeditwidget.h + src/gui/widgets/airporttableeditwidget.cpp + src/gui/widgets/logbooktableeditwidget.h + src/gui/widgets/logbooktableeditwidget.cpp + # Verification src/gui/verification/validationstate.h src/gui/verification/userinput.h @@ -95,7 +99,8 @@ set(PROJECT_SOURCES src/gui/verification/completerprovider.cpp src/gui/verification/tailinput.h src/gui/verification/tailinput.cpp - + src/gui/widgets/currencywidget.h + src/gui/widgets/currencywidget.cpp # Classes src/classes/style.h @@ -116,6 +121,20 @@ set(PROJECT_SOURCES src/classes/md5sum.cpp src/classes/time.h src/classes/time.cpp + src/classes/easaftl.h + src/classes/easaftl.cpp + src/classes/date.h + src/classes/date.cpp + src/classes/styleddatedelegate.h + src/classes/styleddatedelegate.cpp + src/classes/styledtimedelegate.h + src/classes/styledtimedelegate.cpp + src/classes/styledpilotdelegate.h + src/classes/styledpilotdelegate.cpp + src/classes/styledregistrationdelegate.h + src/classes/styledregistrationdelegate.cpp + src/classes/styledtypedelegate.h + src/classes/styledtypedelegate.cpp # Database Entries src/database/flightentry.h @@ -132,7 +151,6 @@ set(PROJECT_SOURCES src/database/simulatorentry.cpp src/database/currencyentry.h src/database/currencyentry.cpp - src/database/previousexperienceentry.h src/database/previousexperienceentry.cpp @@ -158,6 +176,8 @@ set(PROJECT_SOURCES src/database/databasecache.h src/database/databasecache.cpp + src/database/views/logbookviewinfo.h + # Ressources assets/icons.qrc assets/database/templates.qrc @@ -224,7 +244,7 @@ if(WIN32) ) endif() else() - if(${QT_VERSION_MAJOR} GREATER_EQUAL 6) +if(${QT_VERSION_MAJOR} GREATER_EQUAL 6) qt_add_executable(openPilotLog ${PROJECT_SOURCES} ${QM_FILES} diff --git a/assets/database/database_schema.sql b/assets/database/database_schema.sql index 65901615..4c86bd7e 100644 --- a/assets/database/database_schema.sql +++ b/assets/database/database_schema.sql @@ -22,6 +22,7 @@ CREATE TABLE IF NOT EXISTS 'tails' ( 'multiengine' INTEGER, 'engineType' INTEGER, 'weightClass' INTEGER, + 'typeString' TEXT, PRIMARY KEY('tail_id' AUTOINCREMENT) ); DROP TABLE IF EXISTS 'flights'; @@ -89,11 +90,11 @@ CREATE TABLE IF NOT EXISTS 'airports' ( PRIMARY KEY('airport_id' AUTOINCREMENT) ); DROP TABLE IF EXISTS 'currencies'; -CREATE TABLE IF NOT EXISTS 'currencies' ( - 'currency_id' INTEGER NOT NULL, - 'currencyName' TEXT, - 'expiryDate' NUMERIC, - PRIMARY KEY('currency_id' AUTOINCREMENT) +CREATE TABLE IF NOT EXISTS "currencies" ( + "currency_id" INTEGER NOT NULL, + "currencyName" TEXT, + "expiryDate" NUMERIC, + PRIMARY KEY('currency_id' AUTOINCREMENT) ); DROP TABLE IF EXISTS 'changelog'; CREATE TABLE IF NOT EXISTS 'changelog' ( @@ -134,133 +135,139 @@ CREATE TABLE 'previousExperience' ( 'autoland' INTEGER ); DROP VIEW IF EXISTS 'viewDefault'; -CREATE VIEW viewDefault AS +CREATE VIEW viewDefault AS SELECT flight_id, - doft as 'Date', - dept AS 'Dept', - printf('%02d',(tofb/60))||':'||printf('%02d',(tofb%60)) AS 'Time', - dest AS 'Dest', printf('%02d',(tonb/60))||':'||printf('%02d',(tonb%60)) AS 'Time ', - printf('%02d',(tblk/60))||':'||printf('%02d',(tblk%60)) AS 'Total', - CASE WHEN pilot_id = 1 THEN alias ELSE lastname||', '||substr(firstname, 1, 1)||'.' END AS 'Name PIC', - CASE WHEN variant IS NOT NULL THEN make||' '||model||'-'||variant ELSE make||' '||model END AS 'Type', - registration AS 'Registration', - FlightNumber AS 'Flight #', - remarks AS 'Remarks' -FROM flights + doft, + dept, + tofb, + dest, + tonb, + tblk, + pilots.pilot_id, + tails.tail_id, + tails.registration, + flightNumber, + remarks +FROM flights INNER JOIN pilots on flights.pic = pilots.pilot_id INNER JOIN tails on flights.acft = tails.tail_id -ORDER BY date DESC; +ORDER BY doft DESC DROP VIEW IF EXISTS 'viewDefaultSim'; -CREATE VIEW viewDefaultSim AS -SELECT flight_id AS 'rowid', - doft as 'Date', - dept AS 'Dept', - printf('%02d',(tofb/60))||':'||printf('%02d',(tofb%60)) AS 'Time', - dest AS 'Dest', printf('%02d',(tonb/60))||':'||printf('%02d',(tonb%60)) AS 'Time ', - printf('%02d',(tblk/60))||':'||printf('%02d',(tblk%60)) AS 'Total', - CASE WHEN pilot_id = 1 THEN alias ELSE lastname||', '||substr(firstname, 1, 1)||'.' END AS 'Name PIC', - CASE WHEN variant IS NOT NULL THEN make||' '||model||'-'||variant ELSE make||' '||model END AS 'Type', - registration AS 'Registration', - null AS 'Sim Type', - null AS 'Time of Session', - remarks AS 'Remarks' -FROM flights -INNER JOIN pilots on flights.pic = pilots.pilot_id -INNER JOIN tails on flights.acft = tails.tail_id -UNION - SELECT (session_id * -1), - date, - null, null, null, null, - 'SIM', - null, - aircraftType, - registration, - deviceType, - printf('%02d',(totalTime/60))||':'||printf('%02d',(totalTime%60)), +CREATE VIEW viewDefaultSim AS +SELECT flights.flight_id, + flights.doft, + flights.dept, + flights.tofb, + flights.dest, + flights.tonb, + flights.tblk, + pilots.pilot_id , + tails.tail_id, + tails.registration, + null AS 'deviceType', + null AS 'SimTime', + flights.remarks +FROM flights +INNER JOIN pilots on flights.pic = pilots.pilot_id +INNER JOIN tails on flights.acft = tails.tail_id +UNION + SELECT (simulators.session_id * -1), + simulators.date, + null, null, null, null, null, null, + simulators.aircraftType, + simulators.registration, + simulators.deviceType, + simulators.totalTime, remarks -FROM simulators -ORDER BY date DESC; +FROM simulators +ORDER BY date DESC DROP VIEW IF EXISTS 'viewEasa'; -CREATE VIEW viewEasa AS SELECT flight_id, - doft as 'Date', - dept AS 'Dept', - printf('%02d',(tofb/60))||':'||printf('%02d',(tofb%60)) AS 'Time', - dest AS 'Dest', - printf('%02d',(tonb/60))||':'||printf('%02d',(tonb%60)) AS 'Time ', - CASE WHEN variant IS NOT NULL THEN make||' '||model||'-'||variant ELSE make||' '||model END AS 'Type', - registration AS 'Registration', - (SELECT printf('%02d',(tSPSE/60))||':'||printf('%02d',(tSPSE%60)) WHERE tSPSE IS NOT NULL) AS 'SP SE', - (SELECT printf('%02d',(tSPME/60))||':'||printf('%02d',(tSPME%60)) WHERE tSPME IS NOT NULL) AS 'SP ME', - (SELECT printf('%02d',(tMP/60))||':'||printf('%02d',(tMP%60)) WHERE tMP IS NOT NULL) AS 'MP', - printf('%02d',(tblk/60))||':'||printf('%02d',(tblk%60)) AS 'Total', - CASE WHEN pilot_id = 1 THEN alias ELSE lastname||', '||substr(firstname, 1, 1)||'.' END AS 'Name PIC', - ldgDay AS 'L/D', - ldgNight AS 'L/N', - (SELECT printf('%02d',(tNight/60))||':'||printf('%02d',(tNight%60)) WHERE tNight IS NOT NULL) AS 'Night', - (SELECT printf('%02d',(tIFR/60))||':'||printf('%02d',(tIFR%60)) WHERE tIFR IS NOT NULL) AS 'IFR', - (SELECT printf('%02d',(tPIC/60))||':'||printf('%02d',(tPIC%60)) WHERE tPIC IS NOT NULL) AS 'PIC', - (SELECT printf('%02d',(tSIC/60))||':'||printf('%02d',(tSIC%60)) WHERE tSIC IS NOT NULL) AS 'SIC', - (SELECT printf('%02d',(tDual/60))||':'||printf('%02d',(tDual%60)) WHERE tDual IS NOT NULL) AS 'Dual', - (SELECT printf('%02d',(tFI/60))||':'||printf('%02d',(tFI%60)) WHERE tFI IS NOT NULL) AS 'FI', - remarks AS 'Remarks' -FROM flights -INNER JOIN pilots on flights.pic = pilots.pilot_id -INNER JOIN tails on flights.acft = tails.tail_id ORDER BY date DESC; +CREATE VIEW viewEasa AS +SELECT + flight_id, + doft, + dept, + tofb, + dest, + tonb, + tail_id, + registration, + tSPSE, + tSPME, + tMP, + tblk, + pilot_id, + ldgDay, + ldgNight, + tNight, + tIFR, + tPIC, + tSIC, + tDUAL, + tFI, + remarks +FROM flights +INNER JOIN pilots on flights.pic = pilots.pilot_id +INNER JOIN tails on flights.acft = tails.tail_id ORDER BY doft DESC DROP VIEW IF EXISTS 'viewEasaSim'; -CREATE VIEW viewEasaSim AS SELECT flight_id, - doft as 'Date', - dept AS 'Dept', - printf('%02d',(tofb/60))||':'||printf('%02d',(tofb%60)) AS 'Time', - dest AS 'Dest', - printf('%02d',(tonb/60))||':'||printf('%02d',(tonb%60)) AS 'Time ', - CASE WHEN variant IS NOT NULL THEN make||' '||model||'-'||variant ELSE make||' '||model END AS 'Type', - registration AS 'Registration', - (SELECT printf('%02d',(tSPSE/60))||':'||printf('%02d',(tSPSE%60)) WHERE tSPSE IS NOT NULL) AS 'SP SE', - (SELECT printf('%02d',(tSPME/60))||':'||printf('%02d',(tSPME%60)) WHERE tSPME IS NOT NULL) AS 'SP ME', - (SELECT printf('%02d',(tMP/60))||':'||printf('%02d',(tMP%60)) WHERE tMP IS NOT NULL) AS 'MP', - printf('%02d',(tblk/60))||':'||printf('%02d',(tblk%60)) AS 'Total', - CASE WHEN pilot_id = 1 THEN alias ELSE lastname||', '||substr(firstname, 1, 1)||'.' END AS 'Name PIC', - ldgDay AS 'L/D', - ldgNight AS 'L/N', - (SELECT printf('%02d',(tNight/60))||':'||printf('%02d',(tNight%60)) WHERE tNight IS NOT NULL) AS 'Night', - (SELECT printf('%02d',(tIFR/60))||':'||printf('%02d',(tIFR%60)) WHERE tIFR IS NOT NULL) AS 'IFR', - (SELECT printf('%02d',(tPIC/60))||':'||printf('%02d',(tPIC%60)) WHERE tPIC IS NOT NULL) AS 'PIC', - (SELECT printf('%02d',(tSIC/60))||':'||printf('%02d',(tSIC%60)) WHERE tSIC IS NOT NULL) AS 'SIC', - (SELECT printf('%02d',(tDual/60))||':'||printf('%02d',(tDual%60)) WHERE tDual IS NOT NULL) AS 'Dual', - (SELECT printf('%02d',(tFI/60))||':'||printf('%02d',(tFI%60)) WHERE tFI IS NOT NULL) AS 'FI', - null AS 'Sim Type', - null AS 'Time of Session', - remarks AS 'Remarks' -FROM flights -INNER JOIN pilots on flights.pic = pilots.pilot_id -INNER JOIN tails on flights.acft = tails.tail_id -UNION -SELECT (session_id * -1), +CREATE VIEW viewEasaSim AS +SELECT flight_id, + flights.doft as 'Date', + flights.dept, + flights.tofb, + flights.dest, + flights.tonb, + tails.tail_id AS 'Type', + tails.registration AS 'Registration', + flights.tSPSE, + flights.tSPME, + flights.tMP, + flights.tblk, + pilots.pilot_id AS 'PIC', + flights.ldgDay, + flights.ldgNight, + flights.tNight, + flights.tIFR, + flights.tPIC, + flights.tSIC, + flights.tDual, + flights.tFI, + null AS 'deviceType', + null AS 'simTime', + flights.remarks +FROM flights +INNER JOIN pilots on flights.pic = pilots.pilot_id +INNER JOIN tails on flights.acft = tails.tail_id +UNION +SELECT (session_id * -1), + simulators.date, + null, null, null, null, + simulators.aircraftType, + simulators.registration, + null, null, null, null, + null, null, null, null, + null, null, null, null, + null, + simulators.deviceType, + simulators.totalTime, + simulators.remarks +FROM simulators +ORDER BY date DESC + +DROP VIEW IF EXISTS 'viewSimulators'; +CREATE VIEW viewSimulators AS +SELECT (session_id * -1), date, - null, null, null, null, - aircraftType, registration, - null, null, null, - 'SIM', - null, null, null, null, null, null, null, null, null, - deviceType, printf('%02d',(totalTime/60))||':'||printf('%02d',(totalTime%60)), + aircraftType, + deviceType, + totalTime, remarks -FROM simulators -ORDER BY date DESC; - -DROP VIEW IF EXISTS 'viewSimulators'; -CREATE VIEW viewSimulators AS SELECT (session_id * -1), - date as 'Date', - registration AS 'Registration', - aircraftType AS 'Aircraft Type', - deviceType 'Sim Type', - printf('%02d',(totalTime/60))||':'||printf('%02d',(totalTime%60)) AS 'Time of Session', - remarks AS 'Remarks' -FROM simulators -ORDER BY date DESC; +FROM simulators +ORDER BY date DESC DROP VIEW IF EXISTS 'viewTails'; CREATE VIEW viewTails AS diff --git a/assets/database/templates.qrc b/assets/database/templates.qrc index 4f23bc68..ebe4a76c 100644 --- a/assets/database/templates.qrc +++ b/assets/database/templates.qrc @@ -3,7 +3,6 @@ templates/aircraft.json templates/airports.json templates/changelog.json - templates/currencies.json database_schema.sql diff --git a/assets/database/templates/currencies.json b/assets/database/templates/currencies.json deleted file mode 100644 index 57f4fbf0..00000000 --- a/assets/database/templates/currencies.json +++ /dev/null @@ -1,32 +0,0 @@ -[ - { - "currency_id": 1, - "currencyName": "Licence", - "expiryDate": null - }, - { - "currency_id": 2, - "currencyName": "Type Rating", - "expiryDate": null - }, - { - "currency_id": 3, - "currencyName": "Line Check", - "expiryDate": null - }, - { - "currency_id": 4, - "currencyName": "Medical", - "expiryDate": null - }, - { - "currency_id": 5, - "currencyName": "Custom1", - "expiryDate": null - }, - { - "currency_id": 6, - "currencyName": "Custom2", - "expiryDate": null - } -] \ No newline at end of file diff --git a/src/gui/widgets/aircraftwidget.ui b/deprecated/aircraftwidget.ui similarity index 100% rename from src/gui/widgets/aircraftwidget.ui rename to deprecated/aircraftwidget.ui diff --git a/src/gui/widgets/airportwidget.cpp b/deprecated/airportwidget.cpp similarity index 100% rename from src/gui/widgets/airportwidget.cpp rename to deprecated/airportwidget.cpp diff --git a/src/gui/widgets/airportwidget.h b/deprecated/airportwidget.h similarity index 100% rename from src/gui/widgets/airportwidget.h rename to deprecated/airportwidget.h diff --git a/src/gui/widgets/airportwidget.ui b/deprecated/airportwidget.ui similarity index 100% rename from src/gui/widgets/airportwidget.ui rename to deprecated/airportwidget.ui diff --git a/src/gui/widgets/pilotswidget.cpp b/deprecated/pilotswidget.cpp similarity index 75% rename from src/gui/widgets/pilotswidget.cpp rename to deprecated/pilotswidget.cpp index 06396549..bb129f3b 100644 --- a/src/gui/widgets/pilotswidget.cpp +++ b/deprecated/pilotswidget.cpp @@ -59,7 +59,7 @@ void PilotsWidget::setupModelAndView() view->verticalHeader()->hide(); view->setAlternatingRowColors(true); view->setSortingEnabled(true); - sortColumn = Settings::read(Settings::UserData::PilotSortColumn).toInt(); + sortColumn = Settings::getPilotSortColumn(); view->sortByColumn(sortColumn, Qt::AscendingOrder); view->show(); @@ -70,10 +70,12 @@ void PilotsWidget::setupModelAndView() void PilotsWidget::connectSignalsAndSlots() { - QObject::connect(ui->tableView->selectionModel(), &QItemSelectionModel::selectionChanged, - this, &PilotsWidget::tableView_selectionChanged); - QObject::connect(ui->tableView->horizontalHeader(), &QHeaderView::sectionClicked, - this, &PilotsWidget::tableView_headerClicked); + QObject::connect(ui->pilotSearchLineEdit, &QLineEdit::textChanged, + this, &PilotsWidget::filterLineEdit_textChanged); + QObject::connect(view->horizontalHeader(), &QHeaderView::sectionClicked, + this, &PilotsWidget::newSortColumnSelected); + QObject::connect(view, &QTableView::clicked, + this, &PilotsWidget::editRequested); } void PilotsWidget::setUiEnabled(bool enabled) @@ -103,43 +105,28 @@ void PilotsWidget::onPilotsWidget_databaseUpdated() refreshView(); } -void PilotsWidget::on_pilotSearchLineEdit_textChanged(const QString &arg1) +void PilotsWidget::filterLineEdit_textChanged(const QString &arg1) { model->setFilter(QLatin1Char('\"') + ui->pilotsSearchComboBox->currentText() + QLatin1String("\" LIKE '%") + arg1 + QLatin1String("%' AND ID > 1")); } -void PilotsWidget::tableView_selectionChanged() +void PilotsWidget::editRequested(const QModelIndex &index) { - if (this->findChild() != nullptr) { - delete this->findChild(); - } - - auto selection = ui->tableView->selectionModel(); - selectedPilots.clear(); - - for (const auto& row : selection->selectedRows()) { - selectedPilots.append(row.data().toInt()); - DEB << "Selected Tails(s) with ID: " << selectedPilots; - } - if(selectedPilots.length() == 1) { - NewPilotDialog np = NewPilotDialog(selectedPilots.first(), this); - np.setWindowFlag(Qt::Widget); - ui->stackedWidget->addWidget(&np); - ui->stackedWidget->setCurrentWidget(&np); + int pilotID = model->index(index.row(), 0).data().toInt(); - setUiEnabled(false); - np.exec(); - refreshView(); - setUiEnabled(true); - } + NewPilotDialog np = NewPilotDialog(pilotID, this); + np.setWindowFlag(Qt::Widget); + ui->stackedWidget->addWidget(&np); + ui->stackedWidget->setCurrentWidget(&np); + np.exec(); + refreshView(); } -void PilotsWidget::tableView_headerClicked(int column) +void PilotsWidget::newSortColumnSelected(int newSortColumn) { - sortColumn = column; - Settings::write(Settings::UserData::PilotSortColumn, column); + Settings::setPilotSortColumn(newSortColumn); } void PilotsWidget::on_newPilotButton_clicked() @@ -209,7 +196,7 @@ void PilotsWidget::onDeleteUnsuccessful() } else { QString constrained_flights_string; for (int i=0; i")); + constrained_flights_string.append(OPL::FlightEntry(constrained_flights[i]).getFlightSummary() + QStringLiteral("    
")); if (i>10) { constrained_flights_string.append("
[...]
"); break; @@ -245,24 +232,3 @@ const QString PilotsWidget::getPilotName(const OPL::PilotEntry &pilot) const return pilot.getLastName() + QLatin1String(", ") + pilot.getFirstName(); } - -const QString PilotsWidget::getFlightSummary(const OPL::FlightEntry &flight) const -{ - - if(!flight.isValid()) - return QString(); - - auto tableData = flight.getData(); - QString flight_summary; - auto space = QLatin1Char(' '); - flight_summary.append(tableData.value(OPL::FlightEntry::DOFT).toString() + space); - flight_summary.append(tableData.value(OPL::FlightEntry::DEPT).toString() + space); - flight_summary.append(OPL::Time(tableData.value(OPL::FlightEntry::TOFB).toInt()).toString() - + space); - flight_summary.append(OPL::Time(tableData.value(OPL::FlightEntry::TONB).toInt()).toString() - + space); - flight_summary.append(tableData.value(OPL::FlightEntry::DEST).toString()); - - return flight_summary; - -} diff --git a/src/gui/widgets/pilotswidget.h b/deprecated/pilotswidget.h similarity index 85% rename from src/gui/widgets/pilotswidget.h rename to deprecated/pilotswidget.h index 77b8c10c..07945cf1 100644 --- a/src/gui/widgets/pilotswidget.h +++ b/deprecated/pilotswidget.h @@ -59,12 +59,34 @@ class PilotsWidget : public QWidget ~PilotsWidget(); private slots: - void tableView_selectionChanged(); - void tableView_headerClicked(int); + /*! + * \brief Creates a dialog to add a new Pilot to the database + */ void on_newPilotButton_clicked(); + + /*! + * \brief Deletes the selected pilot from the database if the selection is valid. + */ void on_deletePilotButton_clicked(); + + /*! + * \brief Informs the user about a error that ocurred when trying to delete an entry. + */ void onDeleteUnsuccessful(); - void on_pilotSearchLineEdit_textChanged(const QString &arg1); + + /*! + * \brief Sets a filter on the model + */ + void filterLineEdit_textChanged(const QString &arg1); + + /*! + * \brief Creates a new PilotWidget to allow editing of the selected item + */ + void editRequested(const QModelIndex &index); + /*! + * \brief Sorts the table on the selected column + */ + void newSortColumnSelected(int newSortColumn); public slots: /*! @@ -96,8 +118,6 @@ public slots: const QString getPilotName(const OPL::PilotEntry &pilot) const; - const QString getFlightSummary(const OPL::FlightEntry &flight) const; - void setupModelAndView(); void connectSignalsAndSlots(); diff --git a/src/gui/widgets/pilotswidget.ui b/deprecated/pilotswidget.ui similarity index 100% rename from src/gui/widgets/pilotswidget.ui rename to deprecated/pilotswidget.ui diff --git a/src/gui/widgets/tailswidget.cpp b/deprecated/tailswidget.cpp similarity index 98% rename from src/gui/widgets/tailswidget.cpp rename to deprecated/tailswidget.cpp index dc5591e3..9d6f739d 100644 --- a/src/gui/widgets/tailswidget.cpp +++ b/deprecated/tailswidget.cpp @@ -59,7 +59,7 @@ void TailsWidget::setupModelAndView() view->verticalHeader()->hide(); view->setAlternatingRowColors(true); - sortColumn = Settings::read(Settings::UserData::TailSortColumn).toInt(); + sortColumn = Settings::getTailSortColumn(); view->setSortingEnabled(true); view->sortByColumn(sortColumn, Qt::DescendingOrder); @@ -153,8 +153,7 @@ void TailsWidget::on_aircraftSearchLineEdit_textChanged(const QString &arg1) void TailsWidget::tableView_headerClicked(int column) { - sortColumn = column; - Settings::write(Settings::UserData::TailSortColumn, column); + Settings::setTailSortColumn(column); } void TailsWidget::on_deleteAircraftButton_clicked() diff --git a/src/gui/widgets/tailswidget.h b/deprecated/tailswidget.h similarity index 100% rename from src/gui/widgets/tailswidget.h rename to deprecated/tailswidget.h diff --git a/main.cpp b/main.cpp index ad54dfd6..fec9e912 100644 --- a/main.cpp +++ b/main.cpp @@ -33,50 +33,70 @@ #include #include + + +/*! + * \brief firstRun - is run if the application is run for the first time and launches + * the FirstRunDialog which guides the user through the initial set-up process. + */ +bool firstRun() +{ + if(FirstRunDialog().exec() == QDialog::Rejected){ + LOG << "Initial setup incomplete or unsuccessfull."; + return false; + } + + Settings::setSetupCompleted(true); + LOG << "Initial Setup Completed successfully"; + QMessageBox mb; + mb.setText("Initial set-up has been completed successfully.

Please re-start the application."); + mb.exec(); + return true; +} + + /*! * \brief init - Sets up the logging facilities, loads the user settings and sets * up the application style before the MainWindow is instantiated */ -void init() +bool init() { + // Check if another instance of the application is already running, we don't want + // different processes writing to the same database + RunGuard guard(QStringLiteral("opl_single_key")); + if ( !guard.tryToRun() ){ + LOG << "Another Instance of openPilotLog is already running. Exiting."; + return false; + } + LOG << "Setting up / verifying Application Directories..."; if(OPL::Paths::setup()) { LOG << "Application Directories... verified"; } else { + return false; LOG << "Unable to create directories."; } + LOG << "Setting up logging facilities..."; if(OPL::Log::init(true)) { LOG << "Logging enabled."; } else { LOG << "Unable to initalise logging."; } + LOG << "Reading Settings..."; - Settings::setup(); + Settings::init(); LOG << "Setting up application style..."; OPL::Style::setup(); // Translations to be done at a later stage //LOG << "Installing translator..."; //ATranslator::installTranslator(OPL::Translations::English); -} -/*! - * \brief firstRun - is run if the application is run for the first time and launches - * the FirstRunDialog which guides the user through the initial set-up process. - */ -int firstRun() -{ - if(FirstRunDialog().exec() == QDialog::Rejected){ - LOG << "Initial setup incomplete or unsuccessfull."; - return 1; - } + // Check for First Run and launch Setup Wizard + if(!Settings::getSetupCompleted()) + return firstRun(); - Settings::write(Settings::Main::SetupComplete, true); - LOG << "Initial Setup Completed successfully"; - QMessageBox mb; - mb.setText("Initial set-up has been completed successfully.

Please re-start the application."); - mb.exec(); - return 0; + return true; } int main(int argc, char *argv[]) @@ -86,20 +106,9 @@ int main(int argc, char *argv[]) QCoreApplication::setOrganizationDomain(ORGDOMAIN); QCoreApplication::setApplicationName(APPNAME); - // Check of another instance of the application is already running, we don't want - // different processes writing to the same database - RunGuard guard(QStringLiteral("opl_single_key")); - if ( !guard.tryToRun() ){ - LOG << "Another Instance of openPilotLog is already running. Exiting."; - return 0; - } - // Set Up the Application - init(); - - // Check for First Run and launch Setup Wizard - if (!Settings::read(Settings::Main::SetupComplete).toBool()) - return firstRun(); + if(!init()) + return 1; // Create Main Window and set Window Icon acc. to Platform MainWindow w; diff --git a/mainwindow.cpp b/mainwindow.cpp index b4004004..76893ff8 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -17,30 +17,23 @@ */ #include #include "mainwindow.h" +#include "src/gui/widgets/airporttableeditwidget.h" +#include "src/gui/widgets/logbooktableeditwidget.h" +#include "src/gui/widgets/pilottableeditwidget.h" +#include "src/gui/widgets/tailtableeditwidget.h" #include "ui_mainwindow.h" #include "src/database/database.h" #include "src/classes/style.h" #include "src/gui/dialogues/firstrundialog.h" -#include "src/gui/dialogues/newflightdialog.h" -#include "src/gui/dialogues/newsimdialog.h" -#include "src/gui/dialogues/newflightdialog.h" #include "src/database/databasecache.h" #include "src/classes/settings.h" // Quick and dirty Debug area void MainWindow::doDebugStuff() { - OPL::RowData_T xp = DB->getTotals(false); - LOG << "Totals without previous:"; - LOG << xp; - - xp = DB->getTotals(true); - LOG << "Totals with previous:"; - LOG << xp; - - OPL::FlightEntry fe = OPL::FlightEntry(); - LOG << "FLIGHT table: " << fe.getTableName(); - OPL::Row row = OPL::Row(); - LOG << "ROW table: " << row.getTableName(); +// LogbookTableEditWidget *widget = new LogbookTableEditWidget(this); +// widget->init(); +// widget->setWindowFlags(Qt::Dialog); +// widget->show(); } MainWindow::MainWindow(QWidget *parent) @@ -49,9 +42,6 @@ MainWindow::MainWindow(QWidget *parent) { ui->setupUi(this); init(); - - // set Startup Screen (Home Screen) - ui->stackedWidget->setCurrentWidget(homeWidget); } MainWindow::~MainWindow() @@ -66,6 +56,7 @@ void MainWindow::init() setupToolbar(); connectWidgets(); setActionIcons(OPL::Style::getStyleType()); + ui->stackedWidget->setCurrentWidget(homeWidget); } void MainWindow::setupToolbar() @@ -93,16 +84,20 @@ void MainWindow::initialiseWidgets() homeWidget = new HomeWidget(this); ui->stackedWidget->addWidget(homeWidget); - logbookWidget = new LogbookWidget(this); + logbookWidget = new LogbookTableEditWidget(this); + logbookWidget->init(); ui->stackedWidget->addWidget(logbookWidget); - aircraftWidget = new TailsWidget(this); - ui->stackedWidget->addWidget(aircraftWidget); + tailsWidget = new TailTableEditWidget(this); + tailsWidget->init(); + ui->stackedWidget->addWidget(tailsWidget); - pilotsWidget = new PilotsWidget(this); + pilotsWidget = new PilotTableEditWidget(this); + pilotsWidget->init(); ui->stackedWidget->addWidget(pilotsWidget); - airportWidget = new AirportWidget(this); + airportWidget = new AirportTableEditWidget(this); + airportWidget->init(); ui->stackedWidget->addWidget(airportWidget); settingsWidget = new SettingsWidget(this); @@ -122,8 +117,9 @@ void MainWindow::connectDatabase() WARN(tr("Error establishing database connection. The following error has ocurred:

%1") .arg(DB->lastError.text())); } - DBCache->init(); + // Load Cache + DBCache->init(); } void MainWindow::setActionIcons(OPL::Style::StyleType style) @@ -131,27 +127,27 @@ void MainWindow::setActionIcons(OPL::Style::StyleType style) switch (style){ case OPL::Style::StyleType::Light: LOG << "Setting Light Icon theme"; - ui->actionHome->setIcon(QIcon(OPL::Assets::ICON_TOOLBAR_HOME)); - ui->actionNewFlight->setIcon(QIcon(OPL::Assets::ICON_TOOLBAR_NEW_FLIGHT)); - ui->actionNewSim->setIcon(QIcon(OPL::Assets::ICON_TOOLBAR_NEW_FLIGHT)); // pending seperate icon - ui->actionLogbook->setIcon(QIcon(OPL::Assets::ICON_TOOLBAR_LOGBOOK)); - ui->actionAircraft->setIcon(QIcon(OPL::Assets::ICON_TOOLBAR_AIRCRAFT)); - ui->actionPilots->setIcon(QIcon(OPL::Assets::ICON_TOOLBAR_PILOT)); - ui->actionAirports->setIcon(QIcon(OPL::Assets::ICON_TOOLBAR_BACKUP)); - ui->actionSettings->setIcon(QIcon(OPL::Assets::ICON_TOOLBAR_SETTINGS)); - ui->actionQuit->setIcon(QIcon(OPL::Assets::ICON_TOOLBAR_QUIT)); + ui->actionHome->setIcon( QIcon(OPL::Assets::ICON_TOOLBAR_HOME)); + ui->actionNewFlight->setIcon( QIcon(OPL::Assets::ICON_TOOLBAR_NEW_FLIGHT)); + ui->actionNewSim->setIcon( QIcon(OPL::Assets::ICON_TOOLBAR_NEW_FLIGHT)); // TODO seperate icon + ui->actionLogbook->setIcon( QIcon(OPL::Assets::ICON_TOOLBAR_LOGBOOK)); + ui->actionAircraft->setIcon( QIcon(OPL::Assets::ICON_TOOLBAR_AIRCRAFT)); + ui->actionPilots->setIcon( QIcon(OPL::Assets::ICON_TOOLBAR_PILOT)); + ui->actionAirports->setIcon( QIcon(OPL::Assets::ICON_TOOLBAR_BACKUP)); + ui->actionSettings->setIcon( QIcon(OPL::Assets::ICON_TOOLBAR_SETTINGS)); + ui->actionQuit->setIcon( QIcon(OPL::Assets::ICON_TOOLBAR_QUIT)); break; case OPL::Style::StyleType::Dark: LOG << "Setting Dark Icon theme"; - ui->actionHome->setIcon(QIcon(OPL::Assets::ICON_TOOLBAR_HOME_DARK)); - ui->actionNewFlight->setIcon(QIcon(OPL::Assets::ICON_TOOLBAR_NEW_FLIGHT_DARK)); - ui->actionNewSim->setIcon(QIcon(OPL::Assets::ICON_TOOLBAR_NEW_FLIGHT_DARK)); // pending separate icon - ui->actionLogbook->setIcon(QIcon(OPL::Assets::ICON_TOOLBAR_LOGBOOK_DARK)); - ui->actionAircraft->setIcon(QIcon(OPL::Assets::ICON_TOOLBAR_AIRCRAFT_DARK)); - ui->actionPilots->setIcon(QIcon(OPL::Assets::ICON_TOOLBAR_PILOT_DARK)); - ui->actionAirports->setIcon(QIcon(OPL::Assets::ICON_TOOLBAR_BACKUP_DARK)); - ui->actionSettings->setIcon(QIcon(OPL::Assets::ICON_TOOLBAR_SETTINGS_DARK)); - ui->actionQuit->setIcon(QIcon(OPL::Assets::ICON_TOOLBAR_QUIT_DARK)); + ui->actionHome->setIcon( QIcon(OPL::Assets::ICON_TOOLBAR_HOME_DARK)); + ui->actionNewFlight->setIcon( QIcon(OPL::Assets::ICON_TOOLBAR_NEW_FLIGHT_DARK)); + ui->actionNewSim->setIcon( QIcon(OPL::Assets::ICON_TOOLBAR_NEW_FLIGHT_DARK)); // pending separate icon + ui->actionLogbook->setIcon( QIcon(OPL::Assets::ICON_TOOLBAR_LOGBOOK_DARK)); + ui->actionAircraft->setIcon( QIcon(OPL::Assets::ICON_TOOLBAR_AIRCRAFT_DARK)); + ui->actionPilots->setIcon( QIcon(OPL::Assets::ICON_TOOLBAR_PILOT_DARK)); + ui->actionAirports->setIcon( QIcon(OPL::Assets::ICON_TOOLBAR_BACKUP_DARK)); + ui->actionSettings->setIcon( QIcon(OPL::Assets::ICON_TOOLBAR_SETTINGS_DARK)); + ui->actionQuit->setIcon( QIcon(OPL::Assets::ICON_TOOLBAR_QUIT_DARK)); break; } } @@ -170,40 +166,19 @@ void MainWindow::nope() */ void MainWindow::connectWidgets() { - QObject::connect(DB, &OPL::Database::dataBaseUpdated, - homeWidget, &HomeWidget::refresh); - QObject::connect(settingsWidget, &SettingsWidget::settingChanged, - homeWidget, &HomeWidget::refresh); - - QObject::connect(DB, &OPL::Database::dataBaseUpdated, - logbookWidget, &LogbookWidget::refresh); - QObject::connect(settingsWidget, &SettingsWidget::settingChanged, - logbookWidget, &LogbookWidget::onLogbookWidget_viewSelectionChanged); - - QObject::connect(DB, &OPL::Database::dataBaseUpdated, - aircraftWidget, &TailsWidget::onAircraftWidget_dataBaseUpdated); - QObject::connect(settingsWidget, &SettingsWidget::settingChanged, - aircraftWidget, &TailsWidget::onAircraftWidget_settingChanged); - - QObject::connect(DB, &OPL::Database::dataBaseUpdated, - pilotsWidget, &PilotsWidget::onPilotsWidget_databaseUpdated); QObject::connect(settingsWidget, &SettingsWidget::settingChanged, - pilotsWidget, &PilotsWidget::onPilotsWidget_settingChanged); + logbookWidget, &LogbookTableEditWidget::viewSelectionChanged); + QObject::connect(this, &MainWindow::addFlightEntryRequested, + logbookWidget, &LogbookTableEditWidget::addEntryRequested); + QObject::connect(this, &MainWindow::addSimulatorEntryRequested, + logbookWidget, &LogbookTableEditWidget::addSimulatorEntryRequested); QObject::connect(settingsWidget, &SettingsWidget::settingChanged, this, &MainWindow::onStyleChanged); - - QObject::connect(DB, &OPL::Database::connectionReset, - logbookWidget, &LogbookWidget::repopulateModel); - QObject::connect(DB, &OPL::Database::connectionReset, - pilotsWidget, &PilotsWidget::repopulateModel); - QObject::connect(DB, &OPL::Database::connectionReset, - aircraftWidget, &TailsWidget::repopulateModel); } void MainWindow::onDatabaseInvalid() { QMessageBox db_error(this); - //db_error.setStandardButtons(QMessageBox::Yes | QMessageBox::No); db_error.addButton(tr("Restore Backup"), QMessageBox::ButtonRole::AcceptRole); db_error.addButton(tr("Create New Database"), QMessageBox::ButtonRole::RejectRole); db_error.addButton(tr("Abort"), QMessageBox::ButtonRole::DestructiveRole); @@ -235,7 +210,7 @@ void MainWindow::onDatabaseInvalid() LOG << "Initial setup incomplete or unsuccessfull."; on_actionQuit_triggered(); } - Settings::write(Settings::Main::SetupComplete, true); + Settings::setSetupCompleted(true); LOG << "Initial Setup Completed successfully"; } } @@ -251,8 +226,16 @@ void MainWindow::on_actionHome_triggered() void MainWindow::on_actionNewFlight_triggered() { - auto* nf = new NewFlightDialog(this); - nf->exec(); + ui->stackedWidget->setCurrentWidget(logbookWidget); + emit addFlightEntryRequested(); +} + +void MainWindow::on_actionNewSim_triggered() +{ + // auto nsd = NewSimDialog(this); + // nsd.exec(); + ui->stackedWidget->setCurrentWidget(logbookWidget); + emit addSimulatorEntryRequested(); } void MainWindow::on_actionLogbook_triggered() @@ -262,7 +245,7 @@ void MainWindow::on_actionLogbook_triggered() void MainWindow::on_actionAircraft_triggered() { - ui->stackedWidget->setCurrentWidget(aircraftWidget); + ui->stackedWidget->setCurrentWidget(tailsWidget); } void MainWindow::on_actionPilots_triggered() @@ -291,8 +274,4 @@ void MainWindow::on_actionDebug_triggered() ui->stackedWidget->setCurrentWidget(debugWidget); } -void MainWindow::on_actionNewSim_triggered() -{ - auto nsd = NewSimDialog(this); - nsd.exec(); -} + diff --git a/mainwindow.h b/mainwindow.h index 4dd0f34b..be252e46 100644 --- a/mainwindow.h +++ b/mainwindow.h @@ -29,17 +29,14 @@ #include #include +#include + #include "src/gui/widgets/homewidget.h" #include "src/gui/widgets/settingswidget.h" -#include "src/gui/widgets/logbookwidget.h" -#include "src/gui/widgets/tailswidget.h" -#include "src/gui/widgets/airportwidget.h" -#include "src/gui/widgets/airportwidget.h" -#include "src/gui/widgets/pilotswidget.h" +#include "src/gui/widgets/tableeditwidget.h" #include "src/gui/widgets/debugwidget.h" #include "src/classes/style.h" -enum Style {Light, Dark}; QT_BEGIN_NAMESPACE namespace Ui { class MainWindow; @@ -121,13 +118,13 @@ private slots: HomeWidget* homeWidget; - LogbookWidget* logbookWidget; - - TailsWidget* aircraftWidget; + LogbookTableEditWidget* logbookWidget; // This widget has a slot not present in TableEditWidget + + TableEditWidget* tailsWidget; - PilotsWidget* pilotsWidget; + TableEditWidget* pilotsWidget; - AirportWidget* airportWidget; + TableEditWidget* airportWidget; SettingsWidget* settingsWidget; @@ -172,18 +169,15 @@ private slots: */ void resizeEvent(QResizeEvent *event) override { - //DEB << "SIZE:" << this->size(); - int icon_size; - if (this->height() < 760) - icon_size = (this->height() / 16); - else - icon_size = (this->height() / 14); - - auto tool_bar = this->findChild(); - tool_bar->setIconSize(QSize(icon_size, icon_size)); + const auto icon_size = this->height() / 14; + const auto toolBar = this->findChild(); + toolBar->setIconSize(QSize(icon_size, icon_size)); event->accept(); } +signals: + void addFlightEntryRequested(); + void addSimulatorEntryRequested(); //void closeEvent(QCloseEvent *event) override; //TODO check and prompt for creation of backup? }; #endif // MAINWINDOW_H diff --git a/src/classes/date.cpp b/src/classes/date.cpp new file mode 100644 index 00000000..2b47bedb --- /dev/null +++ b/src/classes/date.cpp @@ -0,0 +1,49 @@ +#include "date.h" +#include + +namespace OPL { + +Date::Date(int julianDay, const DateTimeFormat &format) + : m_format(format) +{ + m_date = QDate::fromJulianDay(julianDay); +} + +Date::Date(const QString &textDate, const DateTimeFormat &format) + : m_format(format) +{ + switch(format.dateFormat()) { + case DateTimeFormat::DateFormat::Default: + m_date = QDate::fromString(textDate, Qt::ISODate); + break; + case DateTimeFormat::DateFormat::Custom: + m_date = QDate::fromString(textDate, format.dateFormatString()); + break; + case DateTimeFormat::DateFormat::SystemLocale: + m_date = QDate::fromString(QLocale::system().dateFormat(QLocale::ShortFormat)); + break; + default: + break; + } +} + +Date::Date(const QDate &date, const DateTimeFormat &format) + : m_format(format), m_date(date) +{} + +const QString Date::toString() const +{ + switch (m_format.dateFormat()) { + case DateTimeFormat::DateFormat::Default: + return m_date.toString(Qt::ISODate); + case DateTimeFormat::DateFormat::SystemLocale: + return m_date.toString(QLocale::system().dateFormat(QLocale::ShortFormat)); + case DateTimeFormat::DateFormat::Custom: + return m_date.toString(m_format.dateFormatString()); + default: + return QString(); + } +} + + +} // namespace OPL diff --git a/src/classes/date.h b/src/classes/date.h new file mode 100644 index 00000000..414ed98a --- /dev/null +++ b/src/classes/date.h @@ -0,0 +1,45 @@ +#ifndef DATE_H +#define DATE_H +#include +#include "src/opl.h" +namespace OPL { + +/*! + * \brief The Date class wraps the QDate class. + * \details The QDate class stores dates internally as a Julian Day number, + * an integer count of every day in a contiguous range, with 24 November 4714 BCE + * in the Gregorian calendar being Julian Day 0 (1 January 4713 BCE in the Julian calendar). + * + * Storing a given date as an integer value allows for easy conversion to localised strings + * as well as calculations like date ranges. + * + * Julian day is also used to store a date in the database. + */ +class Date +{ +public: + + Date() = delete; + Date(int julianDay, const DateTimeFormat &format); + Date(const QString &textDate, const DateTimeFormat &format); + Date(const QDate &date, const DateTimeFormat &format); + + const QString toString() const; + const bool isValid() const { return m_date.isValid(); } + + const inline int toJulianDay() const { return m_date.toJulianDay(); } + const static inline Date today(const DateTimeFormat &format) { return Date(QDate::currentDate().toJulianDay(), format); } + +// void setDateFormat(const DateFormat_ &format) {m_format = format} + // todo copy constructor + +private: + QDate m_date; + DateTimeFormat m_format; +}; + + + +} // namespace OPL + +#endif // DATE_H diff --git a/src/classes/easaftl.cpp b/src/classes/easaftl.cpp new file mode 100644 index 00000000..e2cae210 --- /dev/null +++ b/src/classes/easaftl.cpp @@ -0,0 +1,16 @@ +#include "easaftl.h" + +int EasaFTL::getLimit(OPL::Statistics::TimeFrame timeFrame) +{ + switch (timeFrame) { + case OPL::Statistics::TimeFrame::Rolling28Days: + return 100*60; // 100h + case OPL::Statistics::TimeFrame::Rolling12Months: + return 1000 * 60; // 1000h + case OPL::Statistics::TimeFrame::CalendarYear: + return 900 * 60; // 900h + default: + return 0; + break; + } +} diff --git a/src/classes/easaftl.h b/src/classes/easaftl.h new file mode 100644 index 00000000..d147f0c4 --- /dev/null +++ b/src/classes/easaftl.h @@ -0,0 +1,14 @@ +#ifndef EASAFTL_H +#define EASAFTL_H +#include "src/functions/statistics.h" + + +class EasaFTL +{ +public: + EasaFTL() = delete; + + static int getLimit(OPL::Statistics::TimeFrame timeFrame); +}; + +#endif // EASAFTL_H diff --git a/src/classes/settings.cpp b/src/classes/settings.cpp index a37f5cd3..0b2f1f11 100644 --- a/src/classes/settings.cpp +++ b/src/classes/settings.cpp @@ -19,50 +19,12 @@ #include #include "src/classes/paths.h" - -QMap Settings::mainMap = { - {Main::SetupComplete, QStringLiteral("setupComplete")}, - {Main::Style, QStringLiteral("style")}, - {Main::Font, QStringLiteral("font")}, - {Main::FontSize, QStringLiteral("fontSize")}, - {Main::UseSystemFont, QStringLiteral("useSystemFont")}, - {Main::LogbookView, QStringLiteral("logbookView")}, - {Main::DateFormat, QStringLiteral("dateFormat")}, -}; - -QMap Settings::userDataMap = { - {UserData::DisplaySelfAs, QStringLiteral("displayselfas")}, - {UserData::TailSortColumn, QStringLiteral("tailSortColumn")}, - {UserData::PilotSortColumn, QStringLiteral("pilotSortColumn")}, - {UserData::FtlWarningThreshold, QStringLiteral("ftlWarningThreshold")}, - {UserData::CurrWarningThreshold, QStringLiteral("currWarningThreshold")}, - {UserData::ShowToLgdCurrency, QStringLiteral("showToLdgCurrency")}, - {UserData::ShowLicCurrency, QStringLiteral("showLicCurrency")}, - {UserData::ShowTrCurrency, QStringLiteral("showTrCurrency")}, - {UserData::ShowLckCurrency, QStringLiteral("showLckCurrency")}, - {UserData::ShowMedCurrency, QStringLiteral("showMedCurrency")}, - {UserData::ShowCustom1Currency, QStringLiteral("showCustom1Currency")}, - {UserData::ShowCustom2Currency, QStringLiteral("showCustom2Currency")}, - {UserData::Custom1CurrencyName, QStringLiteral("custom1CurrencyName")}, - {UserData::Custom2CurrencyName, QStringLiteral("custom2CurrencyName")}, -}; - -QMap Settings::flightLoggingMap = { - {FlightLogging::Function, QStringLiteral("function")}, - {FlightLogging::Approach, QStringLiteral("approach")}, - {FlightLogging::NightLoggingEnabled,QStringLiteral("nightLoggingEnabled")}, - {FlightLogging::LogIFR, QStringLiteral("logIfr")}, - {FlightLogging::FlightNumberPrefix, QStringLiteral("flightnumberPrefix")}, - {FlightLogging::PilotFlying, QStringLiteral("pilotFlying")}, - {FlightLogging::NightAngle, QStringLiteral("nightangle")}, - //{FlightLogging::FlightTimeFormat, QStringLiteral("flightTimeFormat")}, -}; - -void Settings::setup() +void Settings::init() { + LOG << "Initialising application settings..."; QSettings::setDefaultFormat(QSettings::IniFormat); QSettings::setPath(QSettings::IniFormat, QSettings::UserScope, OPL::Paths::path(OPL::Paths::Settings)); - QSettings(); + Settings::settingsInstance = new QSettings(); } /*! @@ -70,70 +32,43 @@ void Settings::setup() */ void Settings::resetToDefaults() { - write(Main::Style, QStringLiteral("Fusion")); - write(Main::UseSystemFont, true); - write(Main::LogbookView, 0); - write(Main::DateFormat, 0); - - write(UserData::DisplaySelfAs, 0); - write(UserData::FtlWarningThreshold, 0.8); // To Do: UI Option - write(UserData::CurrWarningThreshold, 30); - write(UserData::ShowToLgdCurrency, true); - write(UserData::ShowLicCurrency, false); - write(UserData::ShowTrCurrency, false); - write(UserData::ShowLckCurrency, false); - write(UserData::ShowMedCurrency, false); - write(UserData::ShowCustom1Currency, false); - write(UserData::ShowCustom2Currency, false); - write(UserData::PilotSortColumn, 0); - write(UserData::TailSortColumn, 0); - - write(FlightLogging::PilotFlying, true); - write(FlightLogging::NightAngle, -6); + setApplicationStyle(QStringLiteral("Fusion")); + setUseSystemFont(true); + setLogbookView(OPL::LogbookView::Default); + setLogAsPilotFlying(true); + setNightAngle(-6); + setShowSelfAs(0); + setFtlWarningThreshold(0.8); + setCurrencyWarningThreshold(90); + setPilotSortColumn(0); + setTailSortColumn(0); + setDisplayFormat(OPL::DateTimeFormat()); + + sync(); } -// -// Read/Write -// - -QVariant Settings::read(const FlightLogging key) -{ return QSettings().value(groupOfKey(key)); } - -void Settings::write(const FlightLogging key, const QVariant &val) -{ QSettings().setValue(groupOfKey(key), val); } - -QVariant Settings::read(const Main key) -{ return QSettings().value(groupOfKey(key)); } - -void Settings::write(const Main key, const QVariant &val) -{ QSettings().setValue(groupOfKey(key), val); } - -QVariant Settings::read(const UserData key) -{ return QSettings().value(groupOfKey(key)); } - -void Settings::write(const UserData key, const QVariant &val) -{ QSettings().setValue(groupOfKey(key), val); } - -// -// QString conversion PATH -// -QString Settings::groupOfKey (const Settings::FlightLogging key) -{ return QStringLiteral("flightlogging/") + flightLoggingMap[key]; } +OPL::DateTimeFormat Settings::getDisplayFormat() +{ + using namespace OPL; -QString Settings::groupOfKey (const Settings::Main key) -{ return QStringLiteral("main/") + mainMap[key]; } + // date format + const DateTimeFormat::DateFormat dateFormat = static_cast( + settingsInstance->value(FORMAT_DATE_FORMAT, 0).toInt()); + const QString dateFormatString = settingsInstance->value(FORMAT_DATE_STRING, QStringLiteral("yyyy-MM-dd")).toString(); + // time format + const DateTimeFormat::TimeFormat timeFormat = static_cast( + settingsInstance->value(FORMAT_TIME_FORMAT, 0).toInt()); + const QString timeFormatString = settingsInstance->value(FORMAT_TIME_STRING, QStringLiteral("hh:mm")).toString(); -QString Settings::groupOfKey (const Settings::UserData key) -{ return QStringLiteral("userdata/") + userDataMap[key]; } + return DateTimeFormat(dateFormat, dateFormatString, timeFormat, timeFormatString); -// -// QString conversion ONLY KEY -// -QString Settings::stringOfKey (const Settings::FlightLogging key) -{ return flightLoggingMap[key]; } +} -QString Settings::stringOfKey (const Settings::Main key) -{ return mainMap[key]; } +void Settings::setDisplayFormat(const OPL::DateTimeFormat &format) +{ + settingsInstance->setValue(FORMAT_DATE_FORMAT, static_cast(format.dateFormat())); + settingsInstance->setValue(FORMAT_DATE_STRING, format.dateFormatString()); + settingsInstance->setValue(FORMAT_TIME_FORMAT, static_cast(format.timeFormat())); + settingsInstance->setValue(FORMAT_TIME_STRING, format.timeFormatString()); +} -QString Settings::stringOfKey (const Settings::UserData key) -{ return userDataMap[key]; } diff --git a/src/classes/settings.h b/src/classes/settings.h index bce3fa8d..c8700bfe 100644 --- a/src/classes/settings.h +++ b/src/classes/settings.h @@ -17,89 +17,233 @@ */ #ifndef SETTINGS_H #define SETTINGS_H +#include "src/opl.h" #include #include /*! - * \brief Thin wrapper for the QSettings class, - * simplifying reading and writing of settings. + * \brief A wrapper for the QSettings class, simplifying reading and writing of settings. */ class Settings { public: - enum class Main { - SetupComplete, - Style, - Font, - FontSize, - UseSystemFont, - LogbookView, - DateFormat, - }; - - enum class UserData { - DisplaySelfAs, - TailSortColumn, - PilotSortColumn, - FtlWarningThreshold, - CurrWarningThreshold, - ShowToLgdCurrency, - ShowLicCurrency, - ShowTrCurrency, - ShowLckCurrency, - ShowMedCurrency, - ShowCustom1Currency, - ShowCustom2Currency, - Custom1CurrencyName, - Custom2CurrencyName, - }; - - enum class FlightLogging { - Function, - Approach, - NightLoggingEnabled, - LogIFR, - FlightNumberPrefix, - PilotFlying, - NightAngle, - //FlightTimeFormat, - }; - - /*! - * \brief Should be called after QCoreApplication::set...Name have been called. - */ - static void setup(); + /*! + * \brief Initialise the default setting object. Call after QCoreApplication has been set up in main + */ + static void init(); + + /*! + * \brief Reset the application settings to its default values + */ static void resetToDefaults(); - static QVariant read(const Main key); - static void write(const Main key, const QVariant &val); + /*! + * \brief Writes any unsaved changes to permanent storage + * \note This function is called automatically from QSettings's destructor and by the event loop at regular intervals, + * so you normally don't need to call it yourself. + */ + static void sync() { settingsInstance->sync(); } + + /*! + * \brief Initial set-up of the application and database has been completed + */ + static bool getSetupCompleted() { return settingsInstance->value(MAIN_SETUP_COMPLETE, false).toBool(); } + + /*! + * \brief Initial set-up of the application and database has been completed + */ + static void setSetupCompleted(bool completed) { settingsInstance->setValue(MAIN_SETUP_COMPLETE, completed); } + + /*! + * \brief returns the name of the preferred application style (default: fusion) + */ + static const QString getApplicationStyle() { return settingsInstance->value(MAIN_STYLE, "Fusion").toString(); } + + /*! + * \brief set the name of the preferred application style + */ + static const void setApplicationStyle(const QString &style) { settingsInstance->setValue(MAIN_STYLE, style); } + + /*! + * \brief returns the name of the preferred application font (default: system font) + * \return + */ + static const QString getApplicationFontName() { return settingsInstance->value(MAIN_FONT_NAME, QString()).toString(); } + + /*! + * \brief set the name of the preferred application font + * \return + */ + static const void setApplicationFontName(const QString &fontName) { settingsInstance->setValue(MAIN_FONT_NAME, fontName); } + + /*! + * \brief returns the preferred font size (default: 10) + * \return + */ + static int getApplicationFontSize() { return settingsInstance->value(MAIN_FONT_SIZE, 10).toInt(); } + + /*! + * \brief sets the preferred font size + */ + static void setApplicationFontSize(int size) { settingsInstance->setValue(MAIN_FONT_SIZE, size); } + + /*! + * \brief returns if the default system font should be used (default: true) + */ + static bool getUseSystemFont() { return settingsInstance->value(MAIN_USE_SYSTEM_FONT, true).toBool(); } + + /*! + * \brief sets if the default system font should be used + */ + static void setUseSystemFont(bool value) { settingsInstance->setValue(MAIN_USE_SYSTEM_FONT, value); } + + /*! + * \brief returns the view to be used in the logbook widget + */ + static OPL::LogbookView getLogbookView() { return OPL::LogbookView(settingsInstance->value(MAIN_LOGBOOK_VIEW).toInt()); } + + /*! + * \brief sets the view to be used in the logbook widget + */ + static void setLogbookView(OPL::LogbookView view) { (settingsInstance->setValue(MAIN_LOGBOOK_VIEW, static_cast(view))); } + + /*! + * \brief returns the Display Format used in the application + */ + static OPL::DateTimeFormat getDisplayFormat(); + + /*! + * \brief sets the Display Format used in the application + */ + static void setDisplayFormat(const OPL::DateTimeFormat &format); + + /*! + * \brief returns the default pilot function for new flights + */ + static OPL::PilotFunction getPilotFunction() { return OPL::PilotFunction(settingsInstance->value(LOG_FUNCTION).toInt()); } - static QVariant read(const FlightLogging key); - static void write(const UserData key, const QVariant &val); + /*! + * \brief sets the default pilot function for new flights + */ + static void setPilotFunction(OPL::PilotFunction function) { settingsInstance->setValue(LOG_FUNCTION, static_cast(function)); } - static QVariant read(const UserData key); - static void write(const FlightLogging key, const QVariant &val); + /*! + * \brief returns the default approach type for new flights + */ + static const QString getApproachType() { return settingsInstance->value(LOG_APPROACH).toString(); } + /*! + * \brief sets the default approach type for new flights + */ + static void setApproachType(const QString &value) { settingsInstance->setValue(LOG_APPROACH, value); } /*! - * \brief Return string representation of group of key: "ini_header/key" + * \brief returns if automatic night time calculation is enabled for new flights */ - static QString groupOfKey(const Main key); - static QString groupOfKey(const FlightLogging key); - static QString groupOfKey(const UserData key); + static bool getNightLoggingEnabled() { return settingsInstance->value(LOG_NIGHT).toBool(); } /*! - * \brief Return string representation of key + * \brief sets if automatic night time calculation is enabled for new flights */ - static QString stringOfKey(const Main key); - static QString stringOfKey(const FlightLogging key); - static QString stringOfKey(const UserData key); + static void setNightLoggingEnabled(bool value) { settingsInstance->setValue(LOG_NIGHT, value); } + + /*! + * \brief returns the angle of elevation for night time calculation (default: -6 degrees) + */ + static int getNightAngle() { return settingsInstance->value(LOG_NIGHT_ANGLE, -6).toInt(); } + + /*! + * \brief sets the angle of elevation for night time calculation + */ + static void setNightAngle(int value) { settingsInstance->setValue(LOG_NIGHT_ANGLE, value); } + + /*! + * \brief returns if flight time should be logged as IFR for new flights + */ + static bool getLogIfr() { return settingsInstance->value(LOG_IFR).toBool(); } + + /*! + * \brief sets if flight time should be logged as IFR for new flights + */ + static void setLogIfr(bool value) { settingsInstance->setValue(LOG_IFR, value); } + + /*! + * \brief returns if new flights should be logged as Pilot Flying + */ + static bool getLogAsPilotFlying() { return settingsInstance->value(LOG_AS_PF).toBool(); } + + /*! + * \brief sets if new flights should be logged as Pilot Flying + */ + static void setLogAsPilotFlying(bool value) { settingsInstance->setValue(LOG_AS_PF, value); } + + /*! + * \brief returns the default Flight Number Prefix for new flights + */ + static const QString getFlightNumberPrefix() { return settingsInstance->value(LOG_PREFIX).toString(); } + + /*! + * \brief sets the default Flight Number Prefix for new flights + */ + static void setFlightNumberPrefix(const QString &value) { settingsInstance->setValue(LOG_PREFIX, value); } + + /*! + * \brief sets how the logbook owner is shown in the view + * \details + *
    + *
  • 0 - self + * + */ + static int getShowSelfAs() { return settingsInstance->value(SHOW_SELF_AS).toInt(); } + + static void setShowSelfAs(int value) { settingsInstance->setValue(SHOW_SELF_AS, value); } + + static int getTailSortColumn() { return settingsInstance->value(TAIL_SORT_COLUMN).toInt(); } + static void setTailSortColumn(int value) { settingsInstance->setValue(TAIL_SORT_COLUMN, value); } + + static int getPilotSortColumn() { return settingsInstance->value(PILOT_SORT_COLUMN).toInt(); } + static void setPilotSortColumn(int value) { settingsInstance->setValue(PILOT_SORT_COLUMN, value); } + + static double getFtlWarningThreshold() { return settingsInstance->value(FTL_WARNING_THR, 0.8).toDouble(); } + static void setFtlWarningThreshold(double value) { settingsInstance->setValue(FTL_WARNING_THR, value); } + + static int getCurrencyWarningThreshold() { return settingsInstance->value(CURR_WARNING_THR, 90).toInt(); } + static void setCurrencyWarningThreshold(int days) { settingsInstance->setValue(CURR_WARNING_THR, days); } + - static QSettings settings(); - static void sync() { QSettings().sync(); } private: - static QMap mainMap; - static QMap userDataMap; - static QMap flightLoggingMap; + + // keep an instance to avoid having to create a new QSettings object every time + static inline QSettings *settingsInstance; + + // Setting keys + const static inline QString CURRENCY_STUB = QStringLiteral("userdata/%1Currency"); + const static inline QString SHOW_SELF_AS = QStringLiteral("userdata/displaySelfAs"); + const static inline QString TAIL_SORT_COLUMN = QStringLiteral("userdata/tailSortColumn"); + const static inline QString PILOT_SORT_COLUMN = QStringLiteral("userdata/pilotSortColumn"); + const static inline QString FTL_WARNING_THR = QStringLiteral("ftlWarningThreshold"); + const static inline QString CURR_WARNING_THR = QStringLiteral("currWarningThreshold"); + + const static inline QString LOG_FUNCTION = QStringLiteral("flightlogging/function"); + const static inline QString LOG_APPROACH = QStringLiteral("flightlogging/approach"); + const static inline QString LOG_NIGHT = QStringLiteral("flightlogging/nightLoggingEnabled"); + const static inline QString LOG_NIGHT_ANGLE = QStringLiteral("flightlogging/nightangle"); + const static inline QString LOG_IFR = QStringLiteral("flightlogging/logIfr"); + const static inline QString LOG_AS_PF = QStringLiteral("flightlogging/pilotFlying"); + const static inline QString LOG_PREFIX = QStringLiteral("flightlogging/flightnumberPrefix"); + + const static inline QString MAIN_SETUP_COMPLETE = QStringLiteral("main/setupComplete"); + const static inline QString MAIN_STYLE = QStringLiteral("main/style"); + const static inline QString MAIN_FONT_NAME = QStringLiteral("main/font"); + const static inline QString MAIN_FONT_SIZE = QStringLiteral("main/fontSize"); + const static inline QString MAIN_USE_SYSTEM_FONT = QStringLiteral("main/useSystemFont"); + const static inline QString MAIN_LOGBOOK_VIEW = QStringLiteral("main/logbookView"); + + const static inline QString FORMAT_DATE_FORMAT = QStringLiteral("format/dateFormat"); + const static inline QString FORMAT_DATE_STRING = QStringLiteral("format/dateFormatString"); + const static inline QString FORMAT_TIME_FORMAT = QStringLiteral("format/timeFormat"); + const static inline QString FORMAT_TIME_STRING = QStringLiteral("format/timeFormatString"); + + }; #endif // SETTINGS_H diff --git a/src/classes/style.cpp b/src/classes/style.cpp index f9a48e54..f79e2994 100644 --- a/src/classes/style.cpp +++ b/src/classes/style.cpp @@ -50,21 +50,22 @@ QLatin1String Style::DARK_PALETTE = QLatin1String("Dark-Palette"); */ void Style::setup() { - if (!Settings::read(Settings::Main::SetupComplete).toBool()) // Use system default for first run + if (!Settings::getSetupCompleted()) // Use system default for first run return; +// if (!Settings::read(Settings::Main::SetupComplete).toBool()) // Use system default for first run +// return; // Set Font - if (!Settings::read(Settings::Main::UseSystemFont).toBool()) { - QFont font(Settings::read(Settings::Main::Font).toString()); - font.setPointSize(Settings::read(Settings::Main::FontSize).toUInt()); + if (!Settings::getUseSystemFont()) { + const QFont font(Settings::getApplicationFontName(), Settings::getApplicationFontSize()); qApp->setFont(font); LOG << "Application Font set: " << font.toString().split(',').first(); } // Set style, stylesheet or palette - QString style_setting = Settings::read(Settings::Main::Style).toString(); + const QString style_setting = Settings::getApplicationStyle(); if (style_setting == DARK_PALETTE) { Style::setStyle(Style::darkPalette()); - Settings::write(Settings::Main::Style, style_setting); + Settings::setApplicationStyle(style_setting); return; } for (const auto &style_name : styles) { diff --git a/src/classes/styleddatedelegate.cpp b/src/classes/styleddatedelegate.cpp new file mode 100644 index 00000000..296ef86b --- /dev/null +++ b/src/classes/styleddatedelegate.cpp @@ -0,0 +1,13 @@ +#include "styleddatedelegate.h" +#include "src/classes/date.h" + +StyledDateDelegate::StyledDateDelegate(const OPL::DateTimeFormat &dateFormat, QObject *parent) + : + QStyledItemDelegate(parent), + m_format(dateFormat) +{} + +QString StyledDateDelegate::displayText(const QVariant &value, const QLocale &locale) const +{ + return OPL::Date(value.toInt(), m_format).toString(); +} diff --git a/src/classes/styleddatedelegate.h b/src/classes/styleddatedelegate.h new file mode 100644 index 00000000..34ba68ed --- /dev/null +++ b/src/classes/styleddatedelegate.h @@ -0,0 +1,23 @@ +#ifndef STYLEDDATEDELEGATE_H +#define STYLEDDATEDELEGATE_H + +#include +#include "src/opl.h" + +/*! + * \brief The StyledDateDelegate class is used to display a database date value human-readable. + * \details The database stores dates as an integer representing the days elapsed since the + * beginning of the julian calendar. This integer has to be converted to a human-readable date + * according to the users selected date format. + */ +class StyledDateDelegate : public QStyledItemDelegate +{ +public: + StyledDateDelegate(const OPL::DateTimeFormat &dateFormat, QObject * parent = nullptr); + + QString displayText(const QVariant &value, const QLocale &locale) const override; +private: + OPL::DateTimeFormat m_format; +}; + +#endif // STYLEDDATEDELEGATE_H diff --git a/src/classes/styledpilotdelegate.cpp b/src/classes/styledpilotdelegate.cpp new file mode 100644 index 00000000..c194b437 --- /dev/null +++ b/src/classes/styledpilotdelegate.cpp @@ -0,0 +1,13 @@ +#include "styledpilotdelegate.h" +#include "src/database/databasecache.h" + +StyledPilotDelegate::StyledPilotDelegate(QObject *parent) + : QStyledItemDelegate{parent} +{ + +} + +QString StyledPilotDelegate::displayText(const QVariant &value, const QLocale &locale) const +{ + return DBCache->getPilotNamesMap().value(value.toInt()); +} diff --git a/src/classes/styledpilotdelegate.h b/src/classes/styledpilotdelegate.h new file mode 100644 index 00000000..37611d2d --- /dev/null +++ b/src/classes/styledpilotdelegate.h @@ -0,0 +1,20 @@ +#ifndef STYLEDPILOTDELEGATE_H +#define STYLEDPILOTDELEGATE_H + +#include + +/*! + * \brief The StyledPilotDelegate class is used to display a database date value human-readable. + * \details The database stores pilots as an integer representing a foreign key into a database + * of pilots. This delegate uses the Database cache to map this ID to a name and displays the name + * in the view. + */ +class StyledPilotDelegate : public QStyledItemDelegate +{ +public: + explicit StyledPilotDelegate(QObject *parent = nullptr); + + QString displayText(const QVariant &value, const QLocale &locale) const override; +}; + +#endif // STYLEDPILOTDELEGATE_H diff --git a/src/classes/styledregistrationdelegate.cpp b/src/classes/styledregistrationdelegate.cpp new file mode 100644 index 00000000..eae4278e --- /dev/null +++ b/src/classes/styledregistrationdelegate.cpp @@ -0,0 +1,13 @@ +#include "styledregistrationdelegate.h" +#include "src/database/databasecache.h" + +StyledRegistrationDelegate::StyledRegistrationDelegate(QObject *parent) + : QStyledItemDelegate{parent} +{ + +} + +QString StyledRegistrationDelegate::displayText(const QVariant &value, const QLocale &locale) const +{ + return DBCache->getTailsMap().value(value.toInt()); +} diff --git a/src/classes/styledregistrationdelegate.h b/src/classes/styledregistrationdelegate.h new file mode 100644 index 00000000..c93c3bcd --- /dev/null +++ b/src/classes/styledregistrationdelegate.h @@ -0,0 +1,20 @@ +#ifndef STYLEDREGISTRATIONDELEGATE_H +#define STYLEDREGISTRATIONDELEGATE_H + +#include + +/*! + * \brief The StyledRegistrationDelegate class is used to display a database date value human-readable. + * \details The database stores tails as an integer representing a foreign key into a database + * of tails. This delegate uses the Database cache to map this ID to a registration to display + * in the view. + */ +class StyledRegistrationDelegate : public QStyledItemDelegate +{ +public: + explicit StyledRegistrationDelegate(QObject *parent = nullptr); + + QString displayText(const QVariant &value, const QLocale &locale) const override; +}; + +#endif // STYLEDREGISTRATIONDELEGATE_H diff --git a/src/classes/styledtimedelegate.cpp b/src/classes/styledtimedelegate.cpp new file mode 100644 index 00000000..56b8df0b --- /dev/null +++ b/src/classes/styledtimedelegate.cpp @@ -0,0 +1,12 @@ +#include "styledtimedelegate.h" +#include "src/classes/time.h" + +StyledTimeDelegate::StyledTimeDelegate(const OPL::DateTimeFormat &format, QObject *parent) + : QStyledItemDelegate{parent}, m_format(format) +{} + +QString StyledTimeDelegate::displayText(const QVariant &value, const QLocale &locale) const +{ + const OPL::Time time(value.toInt(), m_format); + return time.toString(); +} diff --git a/src/classes/styledtimedelegate.h b/src/classes/styledtimedelegate.h new file mode 100644 index 00000000..ee722178 --- /dev/null +++ b/src/classes/styledtimedelegate.h @@ -0,0 +1,22 @@ +#ifndef STYLEDTIMEDELEGATE_H +#define STYLEDTIMEDELEGATE_H + +#include "src/opl.h" +#include + +/*! + * \brief The StyledTimeDelegate class is used to convert the database time format to a human-readable format. + * \details The database stores time values as an integer representing minutes elapsed since midnight. This + * delegate can be used in a QTableView to format the database value as "hh:mm" + */ +class StyledTimeDelegate : public QStyledItemDelegate +{ +public: + explicit StyledTimeDelegate(const OPL::DateTimeFormat &format, QObject *parent = nullptr); + + QString displayText(const QVariant &value, const QLocale &locale) const override; +private: + OPL::DateTimeFormat m_format; +}; + +#endif // STYLEDTIMEDELEGATE_H diff --git a/src/classes/styledtypedelegate.cpp b/src/classes/styledtypedelegate.cpp new file mode 100644 index 00000000..8825fa51 --- /dev/null +++ b/src/classes/styledtypedelegate.cpp @@ -0,0 +1,14 @@ +#include "styledtypedelegate.h" +#include "src/database/databasecache.h" + +StyledTypeDelegate::StyledTypeDelegate(QObject *parent) + : QStyledItemDelegate{parent} +{ + +} + +QString StyledTypeDelegate::displayText(const QVariant &value, const QLocale &locale) const +{ + Q_UNUSED(locale); + return DBCache->getTypesMap().value(value.toInt()); +} diff --git a/src/classes/styledtypedelegate.h b/src/classes/styledtypedelegate.h new file mode 100644 index 00000000..eec64e68 --- /dev/null +++ b/src/classes/styledtypedelegate.h @@ -0,0 +1,16 @@ +#ifndef STYLEDTYPEDELEGATE_H +#define STYLEDTYPEDELEGATE_H + +#include +#include + +class StyledTypeDelegate : public QStyledItemDelegate +{ + Q_OBJECT +public: + explicit StyledTypeDelegate(QObject *parent = nullptr); + + QString displayText(const QVariant &value, const QLocale &locale) const override; +}; + +#endif // STYLEDTYPEDELEGATE_H diff --git a/src/classes/time.cpp b/src/classes/time.cpp index db3e2c14..2d5f8027 100644 --- a/src/classes/time.cpp +++ b/src/classes/time.cpp @@ -1,20 +1,47 @@ #include "time.h" +#include "math.h" namespace OPL { -Time::Time() +Time::Time(const DateTimeFormat &format) + : m_format(format), + m_minutes(-1) +{} + +Time::Time(const QTime &qTime, const DateTimeFormat &format) { + m_minutes = qTime.isValid() ? qTime.minute() + qTime.hour() * 60 : -1; } +Time::Time(int32_t minutes, const DateTimeFormat &format) + : m_minutes(minutes), m_format(format) +{} + bool Time::isValidTimeOfDay() const { - return m_minutes <= MINUTES_PER_DAY; + return isValid() && m_minutes <= MINUTES_PER_DAY; +} + +bool Time::isValid() const +{ + return m_minutes >= 0; } const QString Time::toString() const { - // convert to hh:mm - return QString::number(m_minutes / 60).rightJustified(2, '0') + QLatin1Char(':') + QString::number(m_minutes % 60).rightJustified(2, '0'); + switch(m_format.timeFormat()) { + case DateTimeFormat::TimeFormat::Default: + return QString::number(m_minutes / 60).rightJustified(2, '0') + + ':' + + QString::number(m_minutes % 60).rightJustified(2, '0'); + case DateTimeFormat::TimeFormat::Decimal: + return QString::number(m_minutes / 60.0, 'f', 2); + break; + case DateTimeFormat::TimeFormat::Custom: + return QTime(0, m_minutes, 0).toString(m_format.timeFormatString()); + default: + return QString(); + } } int32_t Time::toMinutes() const @@ -22,20 +49,38 @@ int32_t Time::toMinutes() const return m_minutes; } -Time Time::fromString(const QString &timeString) +Time Time::fromString(const QString &timeString, const DateTimeFormat &format) { - const QStringList parts = timeString.split(QChar(':')); - if(parts.size() < 2) - return {}; - + switch(format.timeFormat()) { + case DateTimeFormat::TimeFormat::Default: + { + const auto qTime = QTime::fromString(timeString, QStringLiteral("hh:mm")); + return Time(qTime, format); + break; + } + case DateTimeFormat::TimeFormat::Decimal: + { + // try to convert string to double + bool ok = false; + const double timeValue = timeString.toDouble(&ok); - int32_t hours = parts[0].toInt(); - int32_t minutes = parts[1].toInt(); + if(!ok) { + return {-1, format}; + } - if(hours < 0 || minutes < 0) - return{}; + // extract integer and fractional part + double hours, minutes; + hours = modf(timeValue, &minutes); - return Time(hours * 60 + minutes); + // create the Time Object + return(Time(hours * 60 + minutes, format)); + break; + } + case DateTimeFormat::TimeFormat::Custom: + const auto qTime = QTime::fromString(timeString, format.timeFormatString()); + return Time(qTime, format); + break; + } } Time Time::blockTime(const Time &offBlocks, const Time &onBlocks) @@ -43,16 +88,18 @@ Time Time::blockTime(const Time &offBlocks, const Time &onBlocks) // make sure both times are in 24h range bool bothTimesAreValid = offBlocks.isValidTimeOfDay() && onBlocks.isValidTimeOfDay(); if(!bothTimesAreValid) - return {}; + return {-1, offBlocks.m_format}; // calculate the block time if(onBlocks.m_minutes > offBlocks.m_minutes) { // take-off and landing on the same day - return Time(onBlocks.m_minutes - offBlocks.m_minutes); + return Time(onBlocks.m_minutes - offBlocks.m_minutes, offBlocks.m_format); } else { + if(offBlocks.m_minutes == onBlocks.m_minutes) + return Time(0, offBlocks.m_format); // landing the day after take off int minutesToMidnight = MINUTES_PER_DAY - offBlocks.m_minutes; - return Time(minutesToMidnight + onBlocks.m_minutes); + return Time(minutesToMidnight + onBlocks.m_minutes, offBlocks.m_format); } } diff --git a/src/classes/time.h b/src/classes/time.h index a708cf52..65383c9c 100644 --- a/src/classes/time.h +++ b/src/classes/time.h @@ -1,6 +1,7 @@ #ifndef TIME_H #define TIME_H +#include "src/opl.h" #include namespace OPL { @@ -8,18 +9,17 @@ namespace OPL { * \brief The Time class handles conversions between user input / user-facing time data display and * database format. * \details Time data in the database is stored as an integer value of minutes, whereas the user-facing - * time data is normally displayed in the Qt::ISODATE format. A database value of 72 would for example be - * displayed as 01:12 (1 hour and 12 minutes). + * time data is normally displayed in accordance with the selected DateTimeFormat, by default "hh:mm". */ class Time { -private: - const static inline int MINUTES_PER_DAY = 24 * 60; - int32_t m_minutes = 0; - public: - Time(); - Time(int32_t minutes) : m_minutes(minutes) {}; + Time() = delete; + Time(const DateTimeFormat &format); + Time(const QTime &qTime, const DateTimeFormat &format); + Time(int32_t minutes, const DateTimeFormat &format);; + + enum TimeFrame {Day, Week, Year}; /** * @brief isValidTimeOfDay - determines whether the instance can be converted to a time hh:mm @@ -27,8 +27,13 @@ class Time */ bool isValidTimeOfDay() const; + /*! + * \brief a time is considered valid if it has a time value of >= 0 + */ + bool isValid() const; + /** - * @brief toString returns the time as hh:mm + * @brief toString returns the time in the specified format */ const QString toString() const; @@ -42,17 +47,49 @@ class Time * @param timeString the input string * @return the Time Object corresponding to the string, equivalent to 0 minutes if conversion fails. */ - static Time fromString(const QString& timeString); + static Time fromString(const QString& timeString, const DateTimeFormat &format); - /** - * @brief timeDifference returns the time difference between this time and another time object. - * @param other - The other time object - * @return the number of minutes of time difference, or 0 if one of the two objects is greater than 24h + /*! + * \brief Calculate the elapsed time between two events + * \param offBlocks - The start tmie + * \param onBlocks - the end time + * \return The elapsed time */ - int32_t timeElapsed(const Time &other); - static Time blockTime(const Time &offBlocks, const Time& onBlocks); + + /*! + * \brief Calculate elapsed time between two events + * \param offBlocks - The start time + * \param onBlocks - The end time + * \return the elapsed time in minutes + */ static int32_t blockMinutes(const Time &offBlocks, const Time& onBlocks); + + /*! + * \brief toMinutes returns the number of minutes in the given time frame + * \param count - The number of time frames (e.g. '7' days) + * \return + */ + static constexpr int timeFrameToMinutes(TimeFrame timeFrame, int count) { + switch (timeFrame) { + case Day: + return count * MINUTES_PER_DAY; + case Week: + return count * 7 * MINUTES_PER_DAY; + case Year: + return count * 7 * 52 * MINUTES_PER_DAY; + default: + return 0; + } + } + + +private: + static constexpr int MINUTES_PER_DAY = 24 * 60; + + const DateTimeFormat m_format; + int32_t m_minutes; + }; }// namespace OPL diff --git a/src/database/airportentry.cpp b/src/database/airportentry.cpp index f7508831..abd1c7cb 100644 --- a/src/database/airportentry.cpp +++ b/src/database/airportentry.cpp @@ -46,4 +46,27 @@ const QString AirportEntry::getIcaoCode() const return getData().value(ICAO).toString(); } +const QString AirportEntry::getAirportName() const +{ + return getData().value(NAME).toString(); +} + +const QString AirportEntry::getAirportDescriptor() const +{ + if(getIataCode().isEmpty()) { + if(getAirportName().isEmpty()) { + return getIcaoCode(); + } + return getIcaoCode() + + QStringLiteral(" - ") + + getAirportName(); + } + return getIcaoCode() + + QStringLiteral(" - ") + + getAirportName() + + QStringLiteral(" (") + + getIataCode() + + QLatin1Char(')'); +} + } // namespace OPL diff --git a/src/database/airportentry.h b/src/database/airportentry.h index aed9096c..c63ddf90 100644 --- a/src/database/airportentry.h +++ b/src/database/airportentry.h @@ -43,6 +43,18 @@ class AirportEntry : public Row */ const QString getIcaoCode() const; + /*! + * \brief Returns the airport common given name + */ + const QString getAirportName() const; + + /*! + * \brief return a string describing the airport + * \details The string consists of the Airport ICAO Code, and if available + * IATA Code and Airport Name + */ + const QString getAirportDescriptor() const; + /*! * \brief The ICAO code is a 4-letter alphanumeric identifier for airports */ diff --git a/src/database/currencyentry.cpp b/src/database/currencyentry.cpp index 42f57515..d23663e5 100644 --- a/src/database/currencyentry.cpp +++ b/src/database/currencyentry.cpp @@ -19,14 +19,6 @@ namespace OPL { -CurrencyEntry::CurrencyEntry() - : Row(DbTable::Currencies, 0) -{} - -CurrencyEntry::CurrencyEntry(const RowData_T &row_data) - : Row(DbTable::Currencies, 0, row_data) -{} - CurrencyEntry::CurrencyEntry(int row_id, const RowData_T &row_data) : Row(DbTable::Currencies, row_id, row_data) {} @@ -36,4 +28,28 @@ const QString CurrencyEntry::getTableName() const return TABLE_NAME; } +void CurrencyEntry::setName(const QString &displayName) +{ + auto data = getData(); + data.insert(NAME, displayName); + setData(data); +} + +const QString CurrencyEntry::getName() const +{ + return getData().value(NAME).toString(); +} + +void CurrencyEntry::setExpiryDate(const Date &date) +{ + auto data = getData(); + data.insert(EXPIRYDATE, date.toJulianDay()); + setData(data); +} + +const Date CurrencyEntry::getExpiryDate(const OPL::DateTimeFormat &format) const +{ + return OPL::Date(getData().value(EXPIRYDATE).toInt(), format); +} + } // namespace OPL diff --git a/src/database/currencyentry.h b/src/database/currencyentry.h index f3d0a2ba..32c1c882 100644 --- a/src/database/currencyentry.h +++ b/src/database/currencyentry.h @@ -18,6 +18,7 @@ #ifndef CURRENCYENTRY_H #define CURRENCYENTRY_H #include "src/database/row.h" +#include "src/classes/date.h" namespace OPL { @@ -29,23 +30,39 @@ namespace OPL { */ class CurrencyEntry : public Row { - const static inline QString TABLE_NAME = QStringLiteral("currencies"); public: - CurrencyEntry(); - CurrencyEntry(const RowData_T &row_data); + + enum Currency {Licence = 1, TypeRating = 2, LineCheck = 3, Medical = 4, Custom1 = 5, Custom2 = 6, TakeOffLanding = 7}; + + CurrencyEntry() = delete; + CurrencyEntry(const RowData_T &row_data) = delete; CurrencyEntry(int row_id, const RowData_T &row_data); const QString getTableName() const override; + void setName(const QString& displayName); + const QString getName() const; + + void setExpiryDate(const OPL::Date &date); + const OPL::Date getExpiryDate(const OPL::DateTimeFormat &format) const; + +private: + + /*! + * \brief The sql column name for the row id + */ + const static inline QString ROW_ID = QStringLiteral("currency_id"); + /*! + * \brief The sql column name for the display name + */ + const static inline QString NAME = QStringLiteral("currencyName"); /*! * \brief The sql column name for the expiry date */ const static inline QString EXPIRYDATE = QStringLiteral("expiryDate"); - /*! - * \brief The sql column name for the currency name - */ - const static inline QString CURRENCYNAME = QStringLiteral("currencyName"); + + const static inline QString TABLE_NAME = QStringLiteral("currencies"); }; } // namespace OPL diff --git a/src/database/database.cpp b/src/database/database.cpp index 82e0eb1d..d42d3205 100644 --- a/src/database/database.cpp +++ b/src/database/database.cpp @@ -517,6 +517,7 @@ const RowData_T Database::getTotals(bool includePreviousExperience) return entry_data; } + // name the return types for easy mapping to QLineEdit names statement = "SELECT" " SUM(tblk) AS tblk," " SUM(tSPSE) AS tSPSE," @@ -563,10 +564,10 @@ const RowData_T Database::getTotals(bool includePreviousExperience) int entryValue = entry_data.value(it.key()).toInt(); const QVariant sum = prevXpValue + entryValue; - it.value() = sum; + entry_data.insert(it.key(), sum); } - return prev_exp_data; + return entry_data; } QList Database::getForeignKeyConstraints(int foreign_row_id, OPL::DbTable table) diff --git a/src/database/database.h b/src/database/database.h index 9d0cb8ff..568be471 100644 --- a/src/database/database.h +++ b/src/database/database.h @@ -277,10 +277,10 @@ class Database : public QObject { /*! * \brief Retreives a currency entry from the database. See row class for details. */ - inline OPL::CurrencyEntry getCurrencyEntry(int row_id) + inline OPL::CurrencyEntry getCurrencyEntry(OPL::CurrencyEntry::Currency currency) { - const auto data = getRowData(OPL::DbTable::Currencies, row_id); - return OPL::CurrencyEntry(row_id, data); + const auto data = getRowData(OPL::DbTable::Currencies, currency); + return OPL::CurrencyEntry(currency, data); } /*! diff --git a/src/database/databasecache.cpp b/src/database/databasecache.cpp index 6cf68653..dd9d1174 100644 --- a/src/database/databasecache.cpp +++ b/src/database/databasecache.cpp @@ -58,6 +58,11 @@ const IdMap DatabaseCache::fetchMap(CompleterTarget target) case Tails: statement.append(QStringLiteral("SELECT ROWID, registration FROM tails")); break; + case Types: + statement.append(QStringLiteral("SELECT ROWID, make||' '||model FROM tails WHERE model IS NOT NULL AND variant IS NULL " + " UNION " + " SELECT ROWID, make||' '||model||'-'||variant FROM tails WHERE variant IS NOT NULL")); + break; case AircraftTypes: statement.append(QStringLiteral("SELECT ROWID, make||' '||model FROM aircraft WHERE model IS NOT NULL AND variant IS NULL " "UNION " @@ -132,6 +137,7 @@ void DatabaseCache::updateTails() reg = copy + " (" + reg + QLatin1Char(')'); } } + typesMap = fetchMap(Types); } void DatabaseCache::updateAirports() @@ -144,7 +150,7 @@ void DatabaseCache::updateAirports() void DatabaseCache::updateSimulators() { - TODO << "not yet implemented"; + TODO << "Simulators map not yet cached"; } void DatabaseCache::updatePilots() @@ -239,6 +245,11 @@ const IdMap &DatabaseCache::getTailsMap() const return tailsMap; } +const IdMap &DatabaseCache::getTypesMap() const +{ + return typesMap; +} + } // namespace OPL diff --git a/src/database/databasecache.h b/src/database/databasecache.h index ef76b869..6f7738f8 100644 --- a/src/database/databasecache.h +++ b/src/database/databasecache.h @@ -44,7 +44,7 @@ class DatabaseCache : public QObject DatabaseCache(DatabaseCache const&) = delete; void operator=(DatabaseCache const&) = delete; - enum CompleterTarget {PilotNames, Tails, AircraftTypes, AirportsAny, AirportsICAO, AirportNames, AirportsIATA, Companies}; + enum CompleterTarget {PilotNames, Tails, AircraftTypes, AirportsAny, AirportsICAO, AirportNames, AirportsIATA, Companies, Types}; void init(); @@ -52,6 +52,7 @@ class DatabaseCache : public QObject const IdMap &getAirportsMapIATA() const; const IdMap &getPilotNamesMap() const; const IdMap &getTailsMap() const; + const IdMap &getTypesMap() const; const QStringList &getPilotNamesList() const; const QStringList &getTailsList() const; @@ -74,7 +75,14 @@ class DatabaseCache : public QObject IdMap airportsMapIATA; IdMap airportsMapNames; IdMap pilotNamesMap; + /*! + * \brief key: tail_id / value: registration + */ IdMap tailsMap; + /*! + * \brief key: tail_id value: type string ("Boeing 737-800") + */ + IdMap typesMap; IdMap aircraftMap; // Lists QStringList pilotNamesList; diff --git a/src/database/flightentry.cpp b/src/database/flightentry.cpp index 9f685716..68fef2a2 100644 --- a/src/database/flightentry.cpp +++ b/src/database/flightentry.cpp @@ -16,6 +16,8 @@ *along with this program. If not, see . */ #include "flightentry.h" +#include "src/classes/date.h" +#include "src/classes/time.h" namespace OPL { @@ -36,4 +38,26 @@ const QString FlightEntry::getTableName() const return TABLE_NAME; } +const QString FlightEntry::getFlightSummary() const +{ + using namespace OPL; + if(!isValid()) + return QString(); + + auto tableData = getData(); + QString flight_summary; + const auto space = QLatin1Char(' '); + flight_summary.append(Date(tableData.value(FlightEntry::DOFT).toInt(), DateTimeFormat()).toString() + space); + flight_summary.append(tableData.value(FlightEntry::DEPT).toString() + space); + flight_summary.append(Time(tableData.value(FlightEntry::TOFB).toInt(), DateTimeFormat()).toString() + + space); + flight_summary.append(Time(tableData.value(FlightEntry::TONB).toInt(), DateTimeFormat()).toString() + + space); + flight_summary.append(tableData.value(FlightEntry::DEST).toString()); + + return flight_summary; +} + + + } // namespace OPL diff --git a/src/database/flightentry.h b/src/database/flightentry.h index c5a03db3..fd30fe19 100644 --- a/src/database/flightentry.h +++ b/src/database/flightentry.h @@ -34,6 +34,11 @@ class FlightEntry : public Row const QString getTableName() const override; + /*! + * \brief returns a String representation of the key data of this flight + */ + const QString getFlightSummary() const; + const static inline QString ROWID = QStringLiteral("flight_id"); const static inline QString DOFT = QStringLiteral("doft"); const static inline QString DEPT = QStringLiteral("dept"); diff --git a/src/database/tailentry.cpp b/src/database/tailentry.cpp index bd3a09de..a708d933 100644 --- a/src/database/tailentry.cpp +++ b/src/database/tailentry.cpp @@ -42,7 +42,22 @@ const QString TailEntry::registration() const } const QString TailEntry::type() const { - return getData().value(MAKE).toString(); + const auto &data = getData(); + + if(data.value(VARIANT).toString().isEmpty()) { + if(data.value(MODEL).toString().isEmpty()) { + return data.value(MAKE).toString(); + } + return data.value(MAKE).toString() + + QLatin1Char(' ') + + data.value(MODEL).toString(); + } + + return data.value(MAKE).toString() + + QLatin1Char(' ') + + data.value(MODEL).toString() + + QLatin1Char('-') + + data.value(VARIANT).toString(); } } // namespace OPL diff --git a/src/database/tailentry.h b/src/database/tailentry.h index 67686a1e..62f9d3aa 100644 --- a/src/database/tailentry.h +++ b/src/database/tailentry.h @@ -44,9 +44,9 @@ class TailEntry : public Row */ const QString registration() const; /*! - * \brief Return the aircraft type + * \brief Return the aircraft type (Make and - if available - Model and Variant) */ - const QString type() const; //TODO - Create String for make-model-variant + const QString type() const; /*! * \brief The entries row id in the database @@ -102,6 +102,11 @@ class TailEntry : public Row * <\ul> */ static const inline QString WEIGHT_CLASS = QStringLiteral("weightClass"); + + /*! + * \brief The aircraft type string ("Make Model-Variant") + */ + static const inline QString TYPE_STRING = QStringLiteral("typeString"); }; } // namespace OPL diff --git a/src/database/views/logbookviewinfo.h b/src/database/views/logbookviewinfo.h new file mode 100644 index 00000000..05f14165 --- /dev/null +++ b/src/database/views/logbookviewinfo.h @@ -0,0 +1,186 @@ +#ifndef LOGBOOKVIEWINFO_H +#define LOGBOOKVIEWINFO_H + +#include "src/opl.h" +#include + +namespace OPL { + + +/*! + * \brief The LogbookViewInfo class is a base class for classes that encapsulate information + * about a LogbookView + * \details In the logbook display, SQL views are used instead of raw table data, since in + * some cases data is aggregated from different tables. Using views avoid + */ +class LogbookViewInfo : public QObject { + Q_OBJECT // enable tr() +public: + + /*! + * \brief Return the column in the view which contains the date of flight + */ + static constexpr int getDateColumn(LogbookView view) + { + return DATE_COLUMNS.at(static_cast(view)); + } + + /*! + * \brief Return the column in the view which contains the aircrafts type + */ + static constexpr int getTypeColumn(LogbookView view) + { + return TYPE_COLUMNS.at(static_cast(view)); + } + + /*! + * \brief Return the column(s) in the view which contain pilot names + */ + static constexpr int getPicColumn(LogbookView view) + { + return PIC_COLUMNS.at(static_cast(view)); + } + + /*! + * \brief Return the column(s) in the view which contain Time entries + */ + static constexpr std::vector getTimeColumns(LogbookView view) + { + switch (view) { + case LogbookView::Default: + return { 3, 5, 6 }; + case LogbookView::DefaultWithSim: + return { 3, 5, 6, 11 }; + case LogbookView::Easa: + return { 3, 5, 8, 9, 10, 11, 15, 16, 17, 18, 19, 20 }; + case LogbookView::EasaWithSim: + return { 3, 5, 8, 9, 10, 11, 15, 16, 17, 18, 19, 20, 22 }; + default: + assert(((void)"View is not implemented", false)); + return { 0 }; + } + } + + // translations need to be done at runtime + static const QStringList getTableHeaders(LogbookView view) + { + switch (view) { + case LogbookView::Default: + return { + QStringLiteral("flight_id"), // flight id column - hidden + tr("Date of Flight"), + tr("Dept"), + tr("Time"), + tr("Dest"), + tr("Time"), + tr("Total"), + tr("Name PIC"), + tr("Type"), + tr("Registration"), + tr("Flight Number"), + tr("Remarks"), + }; + case LogbookView::DefaultWithSim: + return { + QStringLiteral("flight_id"), // flight id column - hidden + tr("Date of Flight"), + tr("Dept"), + tr("Time"), + tr("Dest"), + tr("Time"), + tr("Total"), + tr("Name PIC"), + tr("Type"), + tr("Registration"), + tr("Sim Type"), + tr("Time of Session"), + tr("Remarks"), + }; + case LogbookView::Easa: + return { + QStringLiteral("flight_id"), // flight id column - hidden + tr("Date of Flight"), + tr("Dept"), + tr("Time"), + tr("Dest"), + tr("Time"), + tr("Type"), + tr("Registration"), + tr("SP SE"), + tr("SP ME"), + tr("MP"), + tr("Total"), + tr("Name PIC"), + tr("L/D"), + tr("L/N"), + tr("Night"), + tr("IFR"), + tr("PIC"), + tr("SIC"), + tr("Dual"), + tr("FI"), + tr("Remarks"), + }; + case LogbookView::EasaWithSim: + return { + QStringLiteral("flight_id"), // flight id column - hidden + tr("Date of Flight"), + tr("Dept"), + tr("Time"), + tr("Dest"), + tr("Time"), + tr("Type"), + tr("Registration"), + tr("SP SE"), + tr("SP ME"), + tr("MP"), + tr("Total"), + tr("Name PIC"), + tr("L/D"), + tr("L/N"), + tr("Night"), + tr("IFR"), + tr("PIC"), + tr("SIC"), + tr("Dual"), + tr("FI"), + tr("Sim Type"), + tr("Time of Session"), + tr("Remarks"), + }; + default: + assert(((void)"View is not implemented", false)); + return {}; + } + } + +private: + + static constexpr std::array DATE_COLUMNS { + 1, // Default + 1, // Default With Sim + 1, // Easa + 1, // Easa With Sim + 1, // Simulator Only + }; + + static constexpr std::array TYPE_COLUMNS { + 8, // Default + 8, // Default With Sim + 6, // Easa + 6, // Easa With Sim + 1, // Simulator Only + }; + + static constexpr std::array PIC_COLUMNS { + 7, // Default + 7, // Default With Sim + 12, // Easa + 12, // Easa With Sim + -1, // Simulator Only + }; + +}; + +} // namespace OPL +#endif // LOGBOOKVIEWINFO_H diff --git a/src/functions/calc.cpp b/src/functions/calc.cpp index 7cae53de..097533c0 100644 --- a/src/functions/calc.cpp +++ b/src/functions/calc.cpp @@ -343,7 +343,7 @@ void OPL::Calc::updateAutoTimes(int acft_id) */ void OPL::Calc::updateNightTimes() { - const int night_angle = Settings::read(Settings::FlightLogging::NightAngle).toInt(); + int night_angle = Settings::getNightAngle(); //find all flights for aircraft auto statement = QStringLiteral("SELECT ROWID FROM flights"); diff --git a/src/functions/calc.h b/src/functions/calc.h index 7d3f83a8..30332b4b 100644 --- a/src/functions/calc.h +++ b/src/functions/calc.h @@ -144,8 +144,8 @@ struct NightTimeValues{ { nightMinutes = calculateNightTime(dept, dest, departure_time, block_minutes, night_angle); - nightTime = OPL::Time(nightMinutes); - totalTime = OPL::Time(block_minutes); + OPL::Time nightTime = OPL::Time(nightMinutes, DateTimeFormat()); + OPL::Time totalTime = OPL::Time(block_minutes, DateTimeFormat()); if (nightMinutes == 0) { // all day takeOffNight = false; @@ -167,13 +167,13 @@ struct NightTimeValues{ }; - NightTimeValues(bool to_night, bool ldg_night, int night_minutes, OPL::Time night_time, OPL::Time total_time) - : takeOffNight(to_night), landingNight(ldg_night), nightMinutes(night_minutes), nightTime(night_time), totalTime(total_time){}; +// NightTimeValues(bool to_night, bool ldg_night, int night_minutes, OPL::Time night_time, OPL::Time total_time) +// : takeOffNight(to_night), landingNight(ldg_night), nightMinutes(night_minutes), nightTime(night_time), totalTime(total_time){}; bool takeOffNight; bool landingNight; int nightMinutes; - OPL::Time nightTime; - OPL::Time totalTime; +// OPL::Time nightTime; +// OPL::Time totalTime; inline bool isAllDay() {return (!takeOffNight && !landingNight);} inline bool isAllNight() {return ( takeOffNight && landingNight);} diff --git a/src/functions/datetime.h b/src/functions/datetime.h index 4e9ec963..bee0d4ba 100644 --- a/src/functions/datetime.h +++ b/src/functions/datetime.h @@ -1,10 +1,17 @@ #ifndef DATETIME_H #define DATETIME_H #include "src/opl.h" +#include "src/classes/date.h" +#include "src/classes/time.h" namespace OPL { class DateTime { + +public: +// DateTime(const OPL::Date date, const OPL::Time &time); + + public: const inline static QString ISO_FORMAT_STRING = QStringLiteral("yyyy-MM-dd"); const inline static QString DE_FORMAT_STRING = QStringLiteral("dd.MM.yyyy"); @@ -69,11 +76,11 @@ class DateTime { * \brief dateTimeToString formats a QDateTime object into a string in a uniform way. * \return */ - static inline const QString dateTimeToString (const QDateTime& date_time, OPL::DateTimeFormat format) { + static inline const QString dateTimeToString (const QDateTime& date_time, OPL::DateTimeFormat_deprecated format) { switch (format) { - case OPL::DateTimeFormat::Default: + case OPL::DateTimeFormat_deprecated::Default: return date_time.toString(Qt::ISODate); - case OPL::DateTimeFormat::Backup: + case OPL::DateTimeFormat_deprecated::Backup: return date_time.toString(QStringLiteral("yyyy_MM_dd_T_hh_mm")); default: return QString(); diff --git a/src/functions/statistics.cpp b/src/functions/statistics.cpp index fbf92019..5779fb46 100644 --- a/src/functions/statistics.cpp +++ b/src/functions/statistics.cpp @@ -35,21 +35,21 @@ int OPL::Statistics::totalTime(TimeFrame time_frame) break; case TimeFrame::CalendarYear: start.setDate(QDate::currentDate().year(), 1, 1); - start_date = start.toString(Qt::ISODate); + start_date = QString::number(start.toJulianDay()); start_date.append(QLatin1Char('\'')); start_date.prepend(QLatin1Char('\'')); statement = QLatin1String("SELECT SUM(tblk) FROM flights WHERE doft >= ") + start_date; break; case TimeFrame::Rolling12Months: start = QDate::fromJulianDay(QDate::currentDate().toJulianDay() - 365); - start_date = start.toString(Qt::ISODate); + start_date = QString::number(start.toJulianDay()); start_date.append(QLatin1Char('\'')); start_date.prepend(QLatin1Char('\'')); statement = QLatin1String("SELECT SUM(tblk) FROM flights WHERE doft >= ") + start_date; break; case TimeFrame::Rolling28Days: start = QDate::fromJulianDay(QDate::currentDate().toJulianDay() - 28); - start_date = start.toString(Qt::ISODate); + start_date = QString::number(start.toJulianDay()); start_date.append(QLatin1Char('\'')); start_date.prepend(QLatin1Char('\'')); statement = QLatin1String("SELECT SUM(tblk) FROM flights WHERE doft >= ") + start_date; @@ -72,16 +72,17 @@ int OPL::Statistics::totalTime(TimeFrame time_frame) */ QVector OPL::Statistics::countTakeOffLanding(int days) { - QDate start = QDate::fromJulianDay(QDate::currentDate().toJulianDay() - days); - QString startdate = start.toString(Qt::ISODate); - startdate.append(QLatin1Char('\'')); - startdate.prepend(QLatin1Char('\'')); + QString startDate = QString::number(QDate::fromJulianDay(QDate::currentDate().toJulianDay() - days).toJulianDay()); + + // QString startdate = start.toString(Qt::ISODate); + startDate.append(QLatin1Char('\'')); + startDate.prepend(QLatin1Char('\'')); QString statement = QLatin1String("SELECT " " SUM(IFNULL(flights.toDay,0) + IFNULL(flights.toNight,0)) AS 'TO', " " SUM(IFNULL(flights.ldgDay,0) + IFNULL(flights.ldgNight,0)) AS 'LDG' " " FROM flights " - " WHERE doft >=") + startdate; + " WHERE doft >=") + startDate; QVector result = DB->customQuery(statement, 2); // make sure a value is returned instead of NULL diff --git a/src/gui/dialogues/entryeditdialog.cpp b/src/gui/dialogues/entryeditdialog.cpp new file mode 100644 index 00000000..b664088d --- /dev/null +++ b/src/gui/dialogues/entryeditdialog.cpp @@ -0,0 +1,13 @@ +#include "entryeditdialog.h" + +EntryEditDialog::EntryEditDialog(QWidget *parent) + : QDialog{parent} +{ + rowID = 0; +} + +EntryEditDialog::EntryEditDialog(int rowID, QWidget *parent) + : QDialog{parent}, rowID(rowID) +{ + +} diff --git a/src/gui/dialogues/entryeditdialog.h b/src/gui/dialogues/entryeditdialog.h new file mode 100644 index 00000000..4981eb80 --- /dev/null +++ b/src/gui/dialogues/entryeditdialog.h @@ -0,0 +1,35 @@ +#ifndef ENTRYEDITDIALOG_H +#define ENTRYEDITDIALOG_H + +#include +#include + +/*! + * \brief The EntryEditDialog class is a base class for Dialogs that enable editing of individual database entries + */ +class EntryEditDialog : public QDialog +{ + Q_OBJECT +public: + EntryEditDialog() = delete; + EntryEditDialog(QWidget *parent = nullptr); + EntryEditDialog(int rowID, QWidget *parent = nullptr); + + /*! + * \brief load an entry from the database for editing + * \param rowID - The row ID of the entry + */ + virtual void loadEntry(int rowID) = 0; + + /*! + * \brief delete an entry from the database + * \param rowID - the row ID to be deleted + * \return true on success + */ + virtual bool deleteEntry(int rowID) = 0; + +protected: + int rowID; +}; + +#endif // ENTRYEDITDIALOG_H diff --git a/src/gui/dialogues/exporttocsvdialog.cpp b/src/gui/dialogues/exporttocsvdialog.cpp index eac4c4dc..099bea89 100644 --- a/src/gui/dialogues/exporttocsvdialog.cpp +++ b/src/gui/dialogues/exporttocsvdialog.cpp @@ -56,7 +56,7 @@ void ExportToCsvDialog::selectRows() // create a QSqlTableModel based on the selected views const auto model = new QSqlTableModel(this); if(ui->viewComboBox->currentIndex() < 4) - model->setTable(OPL::GLOBALS->getViewIdentifier(OPL::DbViewName(ui->viewComboBox->currentIndex()))); + model->setTable(OPL::GLOBALS->getViewIdentifier(OPL::LogbookView(ui->viewComboBox->currentIndex()))); else model->setTable(exportView); model->select(); diff --git a/src/gui/dialogues/firstrundialog.cpp b/src/gui/dialogues/firstrundialog.cpp index fd16c2dc..1042fcf6 100644 --- a/src/gui/dialogues/firstrundialog.cpp +++ b/src/gui/dialogues/firstrundialog.cpp @@ -150,7 +150,7 @@ bool FirstRunDialog::finishSetup() } // if database file exists if (!DB->connect()) { - QMessageBox message_box(QMessageBox::Critical, tr("Database setup failed"), + QMessageBox message_box(QMessageBox::Critical, tr("Database setup failed (connect)"), tr("Errors have ocurred creating the database." "Without a working database The application will not be usable.
    " "The following error has ocurred:
    " @@ -160,7 +160,7 @@ bool FirstRunDialog::finishSetup() } if (!setupDatabase()) { - QMessageBox message_box(QMessageBox::Critical, tr("Database setup failed"), + QMessageBox message_box(QMessageBox::Critical, tr("Database setup failed (create schema)"), tr("Errors have ocurred creating the database." "Without a working database The application will not be usable.
    " "The following error has ocurred:
    %1" @@ -170,7 +170,7 @@ bool FirstRunDialog::finishSetup() } if (!createUserEntry()) { - QMessageBox message_box(QMessageBox::Critical, tr("Database setup failed"), + QMessageBox message_box(QMessageBox::Critical, tr("Database setup failed (user entry)"), tr("Unable to execute database query
    " "The following error has occured:
    %1" ).arg(DB->lastError.text())); @@ -179,7 +179,7 @@ bool FirstRunDialog::finishSetup() } if (!setupPreviousExperienceEntry()) { - QMessageBox message_box(QMessageBox::Critical, tr("Database setup failed"), + QMessageBox message_box(QMessageBox::Critical, tr("Database setup failed (previous Experience)"), tr("Unable to execute database query
    " "The following error has occured:
    %1" ).arg(DB->lastError.text())); @@ -187,14 +187,10 @@ bool FirstRunDialog::finishSetup() return false; } - if (!writeCurrencies()) { - QMessageBox message_box(QMessageBox::Critical, tr("Database setup failed"), - tr("Unable to execute database query
    " - "The following error has occured:
    %1" - ).arg(DB->lastError.text())); - message_box.exec(); - return false; - } + // non-critical error + if(!writeCurrencies()) + LOG << "Error writing currencies during initial setup."; + DB->disconnect(); // Connection will be re-established by MainWindow return true; } @@ -274,23 +270,24 @@ bool FirstRunDialog::verifyTemplates() void FirstRunDialog::writeSettings() { Settings::resetToDefaults(); + Settings::setPilotFunction(OPL::PilotFunction(ui->functionComboBox->currentIndex())); + Settings::setApproachType(ui->approachComboBox->currentText()); + Settings::setNightLoggingEnabled(ui->nightComboBox->currentIndex()); + Settings::setLogIfr(ui->rulesComboBox->currentIndex()); + Settings::setFlightNumberPrefix(ui->prefixLineEdit->text()); + Settings::setLogbookView(OPL::LogbookView(ui->logbookViewComboBox->currentIndex())); + Settings::setApplicationStyle(ui->styleComboBox->currentText()); - Settings::write(Settings::FlightLogging::Function, ui->functionComboBox->currentIndex()); - Settings::write(Settings::FlightLogging::Approach, ui->approachComboBox->currentIndex()); - Settings::write(Settings::FlightLogging::NightLoggingEnabled, ui->nightComboBox->currentIndex()); switch (ui->nightRulesComboBox->currentIndex()) { case 0: - Settings::write(Settings::FlightLogging::NightAngle, -6); + Settings::setNightAngle(-6); break; case 1: - Settings::write(Settings::FlightLogging::NightAngle, 0); + Settings::setNightAngle(0); break; } - Settings::write(Settings::FlightLogging::LogIFR, ui->rulesComboBox->currentIndex()); - Settings::write(Settings::FlightLogging::FlightNumberPrefix, ui->prefixLineEdit->text()); - Settings::write(Settings::UserData::DisplaySelfAs, ui->aliasComboBox->currentIndex()); - Settings::write(Settings::Main::LogbookView, ui->logbookViewComboBox->currentIndex()); - Settings::write(Settings::Main::Style, ui->styleComboBox->currentText()); + + Settings::setShowSelfAs(ui->aliasComboBox->currentIndex()); Settings::sync(); } @@ -352,39 +349,32 @@ bool FirstRunDialog::setupPreviousExperienceEntry() bool FirstRunDialog::writeCurrencies() { - const QMap currencies_list = { - {OPL::CurrencyName::Licence, ui->currLicDateEdit}, - {OPL::CurrencyName::TypeRating, ui->currTrDateEdit}, - {OPL::CurrencyName::LineCheck, ui->currLckDateEdit}, - {OPL::CurrencyName::Medical, ui->currMedDateEdit}, - {OPL::CurrencyName::Custom1, ui->currCustom1DateEdit}, - {OPL::CurrencyName::Custom2, ui->currCustom2DateEdit}, - }; - const QMap settings_list = { - {OPL::CurrencyName::Licence, Settings::UserData::ShowLicCurrency }, - {OPL::CurrencyName::TypeRating, Settings::UserData::ShowTrCurrency }, - {OPL::CurrencyName::LineCheck, Settings::UserData::ShowLckCurrency }, - {OPL::CurrencyName::Medical, Settings::UserData::ShowMedCurrency }, - {OPL::CurrencyName::Custom1, Settings::UserData::ShowCustom1Currency }, - {OPL::CurrencyName::Custom2, Settings::UserData::ShowCustom2Currency }, + const QList> currencies = { + { ui->currLicLabel->text(), ui->currLicDateEdit }, + { ui->currTrLabel->text(), ui->currTrDateEdit }, + { ui->currLckLabel->text(), ui->currLckDateEdit }, + { ui->currMedLabel->text(), ui->currMedDateEdit }, + { ui->currCustom1LineEdit->text(), ui->currCustom1DateEdit }, + { ui->currCustom2LineEdit->text(), ui->currCustom2DateEdit }, }; - QDate today = QDate::currentDate(); - for (const auto &date_edit : currencies_list) { - const auto enum_value = currencies_list.key(date_edit); - // only write dates that have been edited - if (date_edit->date() != today) { - OPL::RowData_T row_data = {{OPL::CurrencyEntry::EXPIRYDATE, date_edit->date().toString(Qt::ISODate)}}; - if (enum_value == OPL::CurrencyName::Custom1) - row_data.insert(OPL::CurrencyEntry::CURRENCYNAME, ui->currCustom1LineEdit->text()); - else if(enum_value == OPL::CurrencyName::Custom2) - row_data.insert(OPL::CurrencyEntry::CURRENCYNAME, ui->currCustom2LineEdit->text()); - - Settings::write(settings_list.value(enum_value), true); // Show selected currency on Home Screen - OPL::CurrencyEntry entry(static_cast(enum_value), row_data); - if (!DB->commit(entry)) - return false; + const QDate today = QDate::currentDate(); + + for(const auto &pair : currencies) { + // list 0-indexed, db row indexes start at 1 + OPL::CurrencyEntry currencyEntry = OPL::CurrencyEntry(currencies.indexOf(pair) + 1, OPL::RowData_T()); + + currencyEntry.setName(pair.first); + + // only set expiry date if user has modified it + const QDate date = pair.second->date(); + if(date != today) { + int julianDay = date.toJulianDay(); + currencyEntry.setExpiryDate(OPL::Date(julianDay, m_format)); } + + if(!DB->commit(currencyEntry)) + return false; } return true; } @@ -434,16 +424,6 @@ void FirstRunDialog::on_styleComboBox_currentTextChanged(const QString &new_styl } } -void FirstRunDialog::on_currCustom1LineEdit_editingFinished() -{ - Settings::write(Settings::UserData::Custom1CurrencyName, ui->currCustom1LineEdit->text()); -} - -void FirstRunDialog::on_currCustom2LineEdit_editingFinished() -{ - Settings::write(Settings::UserData::Custom2CurrencyName, ui->currCustom2LineEdit->text()); -} - void FirstRunDialog::on_importPushButton_clicked() { QString filename = QDir::toNativeSeparators(QFileDialog::getOpenFileName( diff --git a/src/gui/dialogues/firstrundialog.h b/src/gui/dialogues/firstrundialog.h index 9e116aed..d801581e 100644 --- a/src/gui/dialogues/firstrundialog.h +++ b/src/gui/dialogues/firstrundialog.h @@ -18,6 +18,7 @@ #ifndef FIRSTRUNDIALOG_H #define FIRSTRUNDIALOG_H +#include "src/database/currencyentry.h" #include #include #include @@ -85,8 +86,6 @@ private slots: void on_previousPushButton_clicked(); void on_nextPushButton_clicked(); void on_styleComboBox_currentTextChanged(const QString &new_style_setting); - void on_currCustom1LineEdit_editingFinished(); - void on_currCustom2LineEdit_editingFinished(); /*! * \brief Import an existing database instead of creating a new one @@ -97,6 +96,14 @@ private slots: Ui::FirstRunDialog *ui; bool useRessourceData; + //TODO load from settings + OPL::DateTimeFormat m_format = OPL::DateTimeFormat( + OPL::DateTimeFormat::DateFormat::Default, + QStringLiteral("yyyy-MM-dd"), + OPL::DateTimeFormat::TimeFormat::Default, + QStringLiteral("hh:mm") + ); + /*! * \brief finishSetup - once all the necessary data is entered by the user, this functions executes the steps necessary * to collect the data, process it and create the database diff --git a/src/gui/dialogues/firstrundialog.ui b/src/gui/dialogues/firstrundialog.ui index b6efa358..16fd0b36 100644 --- a/src/gui/dialogues/firstrundialog.ui +++ b/src/gui/dialogues/firstrundialog.ui @@ -175,7 +175,7 @@ - <html><head/><body><p align="center"><span style=" font-size:20pt;">Welcome to openPilotLog!</span></p><p align="center"><span style=" font-size:14pt;">The Free and Open Source Logbook application</span></p><p><br/></p><p>If you would like to keep track of your license and/or medical expiration dates, you can enter them here. By default, only Take-Off and Landing currency is shown on the home page, but any other currency can also be shown as well. This can be enabled in the settings menu.</p></body></html> + <html><head/><body><p align="center"><span style=" font-size:20pt;">Welcome to openPilotLog!</span></p><p align="center"><span style=" font-size:14pt;">The Free and Open Source Logbook application</span></p><p><br/></p><p>If you would like to keep track of your license and/or medical expiration dates, you can enter them here. Your currencies are tracked and you will be warned about impending expiries. These warnings can be disabled in the Settings Menu.</p></body></html> Qt::RichText diff --git a/src/gui/dialogues/newairportdialog.cpp b/src/gui/dialogues/newairportdialog.cpp index b10ac495..fdf60470 100644 --- a/src/gui/dialogues/newairportdialog.cpp +++ b/src/gui/dialogues/newairportdialog.cpp @@ -8,19 +8,18 @@ #include "src/database/row.h" NewAirportDialog::NewAirportDialog(QWidget *parent) : - QDialog(parent), ui(new Ui::NewAirportDialog) + EntryEditDialog(parent), ui(new Ui::NewAirportDialog) { - rowId = 0; // new entry + m_rowId = 0; // new entry ui->setupUi(this); setValidators(); loadTimeZones(); } NewAirportDialog::NewAirportDialog(int row_id, QWidget *parent) - : QDialog(parent), ui(new Ui::NewAirportDialog), rowId(row_id) + : EntryEditDialog(parent), ui(new Ui::NewAirportDialog), m_rowId(row_id) { ui->setupUi(this); - this->setWindowTitle(tr("Edit Airport")); setValidators(); loadTimeZones(); loadAirportData(row_id); @@ -47,8 +46,8 @@ void NewAirportDialog::loadTimeZones() void NewAirportDialog::loadAirportData(int row_id) { + this->setWindowTitle(tr("Edit Airport")); const auto airport_data = DB->getAirportEntry(row_id).getData(); - //const auto airport_data = airport.getData(); DEB << "Filling Airport Data: " << airport_data; ui->nameLineEdit->setText(airport_data.value(OPL::AirportEntry::NAME).toString()); @@ -118,7 +117,7 @@ void NewAirportDialog::on_buttonBox_accepted() {OPL::AirportEntry::COUNTRY, ui->countryLineEdit->text()}, }; - OPL::AirportEntry entry(rowId, airport_data); + OPL::AirportEntry entry(m_rowId, airport_data); if(DB->commit(entry)) QDialog::accept(); else { @@ -142,3 +141,16 @@ void NewAirportDialog::on_buttonBox_rejected() QDialog::reject(); } +// EntryEditDialog interface +void NewAirportDialog::loadEntry(int rowId) +{ + m_rowId = rowId; + loadAirportData(rowId); +} + +bool NewAirportDialog::deleteEntry(int rowId) +{ + auto entry = DB->getAirportEntry(rowId); + return DB->remove(entry); +} + diff --git a/src/gui/dialogues/newairportdialog.h b/src/gui/dialogues/newairportdialog.h index 8387be3d..e1c2a817 100644 --- a/src/gui/dialogues/newairportdialog.h +++ b/src/gui/dialogues/newairportdialog.h @@ -1,13 +1,13 @@ #ifndef NEWAIRPORTDIALOG_H #define NEWAIRPORTDIALOG_H -#include +#include "src/gui/dialogues/entryeditdialog.h" namespace Ui { class NewAirportDialog; } -class NewAirportDialog : public QDialog +class NewAirportDialog : public EntryEditDialog { Q_OBJECT @@ -17,6 +17,7 @@ class NewAirportDialog : public QDialog ~NewAirportDialog(); + private slots: void on_buttonBox_accepted(); @@ -28,12 +29,19 @@ private slots: private: Ui::NewAirportDialog *ui; + int m_rowId; + void setValidators(); void loadTimeZones(); bool confirmTimezone(); void loadAirportData(int row_id); bool verifyInput(); - int rowId; + + + // EntryEditDialog interface +public: + virtual void loadEntry(int rowId) override; + virtual bool deleteEntry(int rowId) override; }; #endif // NEWAIRPORTDIALOG_H diff --git a/src/gui/dialogues/newflightdialog.cpp b/src/gui/dialogues/newflightdialog.cpp index b0b14295..d79ecc5c 100644 --- a/src/gui/dialogues/newflightdialog.cpp +++ b/src/gui/dialogues/newflightdialog.cpp @@ -16,9 +16,11 @@ *along with this program. If not, see . */ #include "newflightdialog.h" +#include "QtWidgets/qcalendarwidget.h" #include "src/classes/time.h" #include "src/database/database.h" #include "src/database/databasecache.h" +#include "src/gui/dialogues/newairportdialog.h" #include "src/gui/verification/airportinput.h" #include "src/gui/verification/completerprovider.h" #include "src/gui/verification/pilotinput.h" @@ -28,6 +30,7 @@ #include "src/opl.h" #include "src/functions/datetime.h" #include "src/classes/settings.h" +#include "src/classes/date.h" #include "src/functions/calc.h" #include "src/gui/dialogues/newtaildialog.h" #include "src/gui/dialogues/newpilotdialog.h" @@ -37,28 +40,18 @@ NewFlightDialog::NewFlightDialog(QWidget *parent) - : QDialog(parent), + : EntryEditDialog(parent), ui(new Ui::NewFlightDialog) { init(); - // Set up UI (New Flight) - LOG << Settings::read(Settings::FlightLogging::Function); - if(Settings::read(Settings::FlightLogging::Function).toInt() == static_cast(OPL::PilotFunction::PIC)){ - ui->picNameLineEdit->setText(self); - ui->functionComboBox->setCurrentIndex(0); - emit ui->picNameLineEdit->editingFinished(); - } - if (Settings::read(Settings::FlightLogging::Function).toInt() == static_cast(OPL::PilotFunction::SIC)) { - ui->sicNameLineEdit->setText(self); - ui->functionComboBox->setCurrentIndex(2); - emit ui->sicNameLineEdit->editingFinished(); - } - ui->doftLineEdit->setText(OPL::DateTime::currentDate()); - emit ui->doftLineEdit->editingFinished(); + setPilotFunction(); + + ui->doftLineEdit->setText(OPL::Date::today(m_format).toString()); +// emit ui->doftLineEdit->editingFinished(); } NewFlightDialog::NewFlightDialog(int row_id, QWidget *parent) - : QDialog(parent), + : EntryEditDialog(parent), ui(new Ui::NewFlightDialog) { init(); @@ -71,6 +64,20 @@ NewFlightDialog::~NewFlightDialog() delete ui; } +void NewFlightDialog::setPilotFunction() +{ + const QString &self = DBCache->getPilotNamesMap().value(1); + if(Settings::getPilotFunction() == OPL::PilotFunction::PIC){ + ui->picNameLineEdit->setText(self); + ui->functionComboBox->setCurrentIndex(0); + } + if (Settings::getPilotFunction() == OPL::PilotFunction::SIC) { + ui->sicNameLineEdit->setText(self); + ui->functionComboBox->setCurrentIndex(2); + } + ui->pilotFlyingCheckBox->setCheckState(Qt::Checked); +} + void NewFlightDialog::init() { // Setup UI @@ -94,6 +101,11 @@ void NewFlightDialog::init() OPL::GLOBALS->loadApproachTypes(ui->approachComboBox); OPL::GLOBALS->loadPilotFunctios(ui->functionComboBox); + // allocate a widget for date selection + calendar = new QCalendarWidget(this); + calendar->setVisible(false); + calendar->setWindowFlags(Qt::Dialog); // pop-up calendar + setupRawInputValidation(); setupSignalsAndSlots(); readSettings(); @@ -140,6 +152,9 @@ void NewFlightDialog::setupSignalsAndSlots() for (const auto& line_edit : *pilotNameLineEdits) QObject::connect(line_edit, &QLineEdit::editingFinished, this, &NewFlightDialog::onPilotNameLineEdit_editingFinshed); + + QObject::connect(calendar, &QCalendarWidget::selectionChanged, + this, &NewFlightDialog::calendarDateSelected); } /*! @@ -167,10 +182,12 @@ bool NewFlightDialog::eventFilter(QObject *object, QEvent *event) */ void NewFlightDialog::readSettings() { - ui->functionComboBox->setCurrentIndex(Settings::read(Settings::FlightLogging::Function).toInt()); - ui->approachComboBox->setCurrentIndex(Settings::read(Settings::FlightLogging::Approach).toInt()); - ui->flightRulesComboBox->setCurrentIndex(Settings::read(Settings::FlightLogging::LogIFR).toInt()); - ui->flightNumberLineEdit->setText(Settings::read(Settings::FlightLogging::FlightNumberPrefix).toString()); + ui->functionComboBox->setCurrentIndex(static_cast(Settings::getPilotFunction())); + ui->approachComboBox->setCurrentText(Settings::getApproachType()); + ui->flightRulesComboBox->setCurrentIndex(Settings::getLogIfr()); + ui->flightNumberLineEdit->setText(Settings::getFlightNumberPrefix()); + + m_format = Settings::getDisplayFormat(); } /*! @@ -181,55 +198,54 @@ void NewFlightDialog::fillWithEntryData() { DEB << "Restoring Flight: "; DEB << flightEntry; + using namespace OPL; const auto &flight_data = flightEntry.getData(); // Date of Flight - ui->doftLineEdit->setText(flight_data.value(OPL::FlightEntry::DOFT).toString()); + const QDate date = QDate::fromJulianDay(flight_data.value(FlightEntry::DOFT).toInt()); + calendar->setSelectedDate(date); + ui->doftLineEdit->setText(Date(date, m_format).toString()); + // Location ui->deptLocationLineEdit->setText(flight_data.value(OPL::FlightEntry::DEPT).toString()); ui->destLocationLineEdit->setText(flight_data.value(OPL::FlightEntry::DEST).toString()); // Times - ui->tofbTimeLineEdit->setText(OPL::Time(flight_data.value(OPL::FlightEntry::TOFB).toInt()).toString()); - ui->tonbTimeLineEdit->setText(OPL::Time(flight_data.value(OPL::FlightEntry::TONB).toInt()).toString()); + ui->tofbTimeLineEdit->setText(OPL::Time(flight_data.value(OPL::FlightEntry::TOFB).toInt(), m_format).toString()); + ui->tonbTimeLineEdit->setText(OPL::Time(flight_data.value(OPL::FlightEntry::TONB).toInt(), m_format).toString()); ui->acftLineEdit->setText(DBCache->getTailsMap().value(flight_data.value(OPL::FlightEntry::ACFT).toInt())); ui->picNameLineEdit->setText(DBCache->getPilotNamesMap().value(flight_data.value(OPL::FlightEntry::PIC).toInt())); ui->sicNameLineEdit->setText(DBCache->getPilotNamesMap().value(flight_data.value(OPL::FlightEntry::SECONDPILOT).toInt())); ui->thirdPilotNameLineEdit->setText(DBCache->getPilotNamesMap().value(flight_data.value(OPL::FlightEntry::THIRDPILOT).toInt())); //Function - const QHash functions = { - {0, OPL::FlightEntry::TPIC}, - {1, OPL::FlightEntry::TPICUS}, - {2, OPL::FlightEntry::TSIC}, - {3, OPL::FlightEntry::TDUAL}, - {4, OPL::FlightEntry::TFI}, - }; for (int i = 0; i < 5; i++) { // QHash::iterator not guarenteed to be in ordetr - if(flight_data.value(functions.value(i)).toInt() != 0) + if(flight_data.value(pilotFuncionsMap.value(i)).toInt() != 0) ui->functionComboBox->setCurrentIndex(i); } // Approach ComboBox - const QString& app = flight_data.value(OPL::FlightEntry::APPROACHTYPE).toString(); - if(app != QString()){ + const QString app = flight_data.value(OPL::FlightEntry::APPROACHTYPE).toString(); + if(app != QString()) { ui->approachComboBox->setCurrentText(app); } - // Flight Rules + // Flight Rules, check if tIFR > 0 bool time_ifr = flight_data.value(OPL::FlightEntry::TIFR).toBool(); - int rulesIndex = time_ifr ? 1 : 0; - ui->flightRulesComboBox->setCurrentIndex(rulesIndex); + ui->flightRulesComboBox->setCurrentIndex(time_ifr); + // Take-Off and Landing - int takeOffCount = flight_data.value(OPL::FlightEntry::TODAY).toInt() + int takeOffCount = flight_data.value(OPL::FlightEntry::TODAY).toInt() + flight_data.value(OPL::FlightEntry::TONIGHT).toInt(); int landingCount = flight_data.value(OPL::FlightEntry::LDGDAY).toInt() + flight_data.value(OPL::FlightEntry::LDGNIGHT).toInt(); ui->takeOffSpinBox->setValue(takeOffCount); ui->landingSpinBox->setValue(landingCount); - // Remarks + ui->pilotFlyingCheckBox->setChecked(flight_data.value(OPL::FlightEntry::PILOTFLYING).toBool()); + + // Remarks and Flight Number ui->remarksLineEdit->setText(flight_data.value(OPL::FlightEntry::REMARKS).toString()); - // Flight Number ui->flightNumberLineEdit->setText(flight_data.value(OPL::FlightEntry::FLIGHTNUMBER).toString()); + // re-trigger input verification for(const auto &line_edit : *mandatoryLineEdits) emit line_edit->editingFinished(); } @@ -262,7 +278,10 @@ void NewFlightDialog::onGoodInputReceived(QLineEdit *line_edit) validationState.validate(mandatoryLineEdits->indexOf(line_edit)); if (validationState.timesValid()) { - updateBlockTimeLabel(); + const OPL::Time tofb = OPL::Time::fromString(ui->tofbTimeLineEdit->text(), m_format); + const OPL::Time tonb = OPL::Time::fromString(ui->tonbTimeLineEdit->text(), m_format); + const OPL::Time tblk = OPL::Time::blockTime(tofb, tonb); + ui->tblkDisplayLabel->setText(tblk.toString()); } } @@ -278,81 +297,90 @@ void NewFlightDialog::onBadInputReceived(QLineEdit *line_edit) validationState.printValidationStatus(); } -void NewFlightDialog::updateBlockTimeLabel() +bool NewFlightDialog::addNewDatabaseElement(QLineEdit *parent, OPL::DbTable table) { - const OPL::Time tofb = OPL::Time::fromString(ui->tofbTimeLineEdit->text()); - const OPL::Time tonb = OPL::Time::fromString(ui->tonbTimeLineEdit->text()); - const OPL::Time tblk = OPL::Time::blockTime(tofb, tonb); - - ui->tblkDisplayLabel->setText(tblk.toString()); -} + QDialog *dialog = nullptr; + if(userWantsToAddNewEntry(table)) { + switch (table) { + case OPL::DbTable::Pilots: + dialog = new NewPilotDialog(parent->text(), this); + break; + case OPL::DbTable::Tails: + dialog = new NewTailDialog(ui->acftLineEdit->text(), this); + break; + case OPL::DbTable::Airports: + dialog = new NewAirportDialog(this); + break; + default: + return false; + break; + } + } else + return false; -/*! - * \brief NewFlightDialog::addNewTail If the user input is not in the aircraftList, the user - * is prompted if he wants to add a new entry to the database - */ -bool NewFlightDialog::addNewTail(QLineEdit& parent_line_edit) -{ - QMessageBox::StandardButton reply; - reply = QMessageBox::question(this, tr("No Aircraft found"), - tr("No aircraft with this registration found.
    " - "If this is the first time you log a flight with this aircraft, " - "you have to add the registration to the database first." - "

    Would you like to add a new aircraft to the database?"), - QMessageBox::Yes|QMessageBox::No); - if (reply == QMessageBox::Yes) { - // create and open new aircraft dialog - NewTailDialog nt(ui->acftLineEdit->text(), this); - int ret = nt.exec(); - // update map and list, set line edit - if (ret == QDialog::Accepted) { - DEB << "New Tail Entry added:"; - DEB << DB->getTailEntry(DB->getLastEntry(OPL::DbTable::Tails)); - - // update Line Edit with newly added tail - parent_line_edit.setText(DBCache->getTailsMap().value(DB->getLastEntry(OPL::DbTable::Tails))); - emit parent_line_edit.editingFinished(); - return true; - } else { + // execute the dialog and check for success. Set the line edit to the newly created entry. + if(dialog->exec() == QDialog::Accepted) { + delete dialog; + int latestEntry = DB->getLastEntry(table); + switch (table) { + case OPL::DbTable::Pilots: + parent->setText(DBCache->getPilotNamesMap().value(latestEntry)); + break; + case OPL::DbTable::Tails: + parent->setText(DBCache->getTailsMap().value(latestEntry)); + break; + case OPL::DbTable::Airports: + parent->setText(DBCache->getAirportsMapICAO().value(latestEntry)); + break; + default: return false; + break; } } else { + delete dialog; return false; } + + // re-emit editing finished to trigger input validation + emit parent->editingFinished(); + return true; } -/*! - * \brief NewFlightDialog::addNewPilot If the user input is not in the pilotNameList, the user - * is prompted if he wants to add a new entry to the database - */ -bool NewFlightDialog::addNewPilot(QLineEdit& parent_line_edit) +bool NewFlightDialog::userWantsToAddNewEntry(OPL::DbTable table) { QMessageBox::StandardButton reply; - reply = QMessageBox::question(this, tr("No Pilot found"), - tr("No pilot found.
    Please enter the Name as" - "

    Lastname, Firstname


    " - "If this is the first time you log a flight with this pilot, " - "you have to add the pilot to the database first." - "

    Would you like to add a new pilot to the database?"), - QMessageBox::Yes|QMessageBox::No); - if (reply == QMessageBox::Yes) { - // create and open new pilot dialog - NewPilotDialog np(this); - int ret = np.exec(); - // update map and list, set line edit - if (ret == QDialog::Accepted) { - DEB << "New Pilot Entry added:"; - DEB << DB->getPilotEntry(DB->getLastEntry(OPL::DbTable::Pilots)); - - // update Line Edit with newly added pilot - parent_line_edit.setText(DBCache->getPilotNamesMap().value(DB->getLastEntry(OPL::DbTable::Pilots))); - emit parent_line_edit.editingFinished(); - return true; - } else { - return false; - } - } else - return false; + switch (table) { + case OPL::DbTable::Pilots: + reply = QMessageBox::question(this, tr("No Pilot found"), + tr("No pilot found.
    Please enter the Name as" + "

    Lastname, Firstname


    " + "If this is the first time you log a flight with this pilot, " + "you have to add the pilot to the database first." + "

    Would you like to add a new pilot to the database?"), + QMessageBox::Yes|QMessageBox::No, QMessageBox::StandardButton::Yes); + break; + case OPL::DbTable::Tails: + reply = QMessageBox::question(this, tr("No Aircraft found"), + tr("No aircraft with this registration found.
    " + "If this is the first time you log a flight with this aircraft, " + "you have to add the registration to the database first." + "

    Would you like to add a new aircraft to the database?"), + QMessageBox::Yes|QMessageBox::No, QMessageBox::StandardButton::Yes); + break; + case OPL::DbTable::Airports: + reply = QMessageBox::question(this, tr("No Airport found"), + tr("No Airport with this identifier found.
    " + "If this is the first time you log a flight to this airport, " + "you have to add the airport to the database first." + "

    Would you like to add a new airport to the database?"), + QMessageBox::Yes|QMessageBox::No, QMessageBox::StandardButton::Yes); + break; + default: + reply = QMessageBox::No; + break; + } + + return reply == QMessageBox::Yes; } /*! @@ -369,15 +397,15 @@ OPL::RowData_T NewFlightDialog::prepareFlightEntryData() OPL::RowData_T new_data; // Calculate Block and Night Time - const OPL::Time tofb = OPL::Time::fromString(ui->tofbTimeLineEdit->text()); - const OPL::Time tonb = OPL::Time::fromString(ui->tonbTimeLineEdit->text()); + const OPL::Time tofb = OPL::Time::fromString(ui->tofbTimeLineEdit->text(), m_format); + const OPL::Time tonb = OPL::Time::fromString(ui->tonbTimeLineEdit->text(), m_format); const int block_minutes = OPL::Time::blockMinutes(tofb, tonb); - QDateTime departure_date_time = OPL::DateTime::fromString(ui->doftLineEdit->text() + ui->tofbTimeLineEdit->text()); + const QDateTime departure_date_time = OPL::DateTime::fromString(ui->doftLineEdit->text() + ui->tofbTimeLineEdit->text()); const auto night_time_data = OPL::Calc::NightTimeValues(ui->deptLocationLineEdit->text(), ui->destLocationLineEdit->text(), - departure_date_time, block_minutes, Settings::read(Settings::FlightLogging::NightAngle).toInt()); + departure_date_time, block_minutes, Settings::getNightAngle()); // Mandatory data - new_data.insert(OPL::FlightEntry::DOFT, ui->doftLineEdit->text()); + new_data.insert(OPL::FlightEntry::DOFT, QDate::fromString(ui->doftLineEdit->text(), Qt::ISODate).toJulianDay()); new_data.insert(OPL::FlightEntry::DEPT, ui->deptLocationLineEdit->text()); new_data.insert(OPL::FlightEntry::TOFB, tofb.toMinutes()); new_data.insert(OPL::FlightEntry::DEST, ui->destLocationLineEdit->text()); @@ -461,7 +489,7 @@ OPL::RowData_T NewFlightDialog::prepareFlightEntryData() new_data.insert(OPL::FlightEntry::AUTOLAND, ui->landingSpinBox->value()); // Pilot flying / Pilot monitoring - bool isPilotFlying = toDay + toNight + ldgDay + ldgNight > 0; + bool isPilotFlying = toDay + toNight + ldgDay + ldgNight; new_data.insert(OPL::FlightEntry::PILOTFLYING, isPilotFlying); // Additional Data @@ -485,15 +513,28 @@ void NewFlightDialog::toUpper(const QString &text) void NewFlightDialog::onTimeLineEdit_editingFinished() { - auto line_edit = this->findChild(sender()->objectName()); - verifyUserInput(line_edit, TimeInput(line_edit->text())); + const auto line_edit = this->findChild(sender()->objectName()); + + if(!verifyUserInput(line_edit, TimeInput(line_edit->text(), m_format))) { + onBadInputReceived(line_edit); + } } void NewFlightDialog::onPilotNameLineEdit_editingFinshed() { auto line_edit = this->findChild(sender()->objectName()); + if(line_edit->text() == QString()) + return; + + // create a copy to refill line edit and pass through to creation dialog if verification fails + QString userInput = line_edit->text(); + if(!verifyUserInput(line_edit, PilotInput(line_edit->text()))) { - if(!addNewPilot(*line_edit)) + { + QSignalBlocker blocker(line_edit); + line_edit->setText(userInput); + } + if(!addNewDatabaseElement(line_edit, OPL::DbTable::Pilots)) onBadInputReceived(line_edit); } } @@ -514,17 +555,31 @@ void NewFlightDialog::onLocationLineEdit_editingFinished() name_label->setText(DBCache->getAirportsMapNames().value( DBCache->getAirportsMapICAO().key( line_edit->text()))); - } else + } else { name_label->setText("Unknown Airport"); + addNewDatabaseElement(line_edit, OPL::DbTable::Airports); + } } void NewFlightDialog::on_acftLineEdit_editingFinished() { const auto line_edit = ui->acftLineEdit; + if(line_edit->text().isEmpty()){ + return; + } + + const QString user_input = line_edit->text(); // keep around for adding new tail if needed - if(!verifyUserInput(line_edit, TailInput(line_edit->text()))) - if(!addNewTail(*line_edit)) + if(!verifyUserInput(line_edit, TailInput(line_edit->text()))) { + // re-populate user input to hand through to NewTailDialog + { + QSignalBlocker blocker(line_edit); + line_edit->setText(user_input); + } + if(!addNewDatabaseElement(line_edit, OPL::DbTable::Tails)) { onBadInputReceived(line_edit); + } + } const auto space = QLatin1Char(' '); if(line_edit->text().contains(space)) @@ -534,21 +589,19 @@ void NewFlightDialog::on_acftLineEdit_editingFinished() void NewFlightDialog::on_doftLineEdit_editingFinished() { const auto line_edit = ui->doftLineEdit; - auto text = ui->doftLineEdit->text(); - auto label = ui->doftDisplayLabel; - - TODO << "Non-default Date formats not implemented yet."; - OPL::DateFormat date_format = OPL::DateFormat::ISODate; - auto date = OPL::DateTime::parseInput(text, date_format); - if (date.isValid()) { - label->setText(date.toString(Qt::TextDate)); - line_edit->setText(OPL::DateTime::dateToString(date, date_format)); - onGoodInputReceived(line_edit); + OPL::Date date(ui->doftLineEdit->text(), m_format); + + + LOG << "Date: " << date.toString(); + LOG << "is valid? " << date.isValid(); + + line_edit->setText(date.toString()); + if(ui->doftLineEdit->text().isEmpty()) { + onBadInputReceived(line_edit); return; } - label->setText(tr("Invalid Date.")); - onBadInputReceived(line_edit); + onGoodInputReceived(line_edit); } void NewFlightDialog::on_pilotFlyingCheckBox_stateChanged(int arg1) @@ -577,18 +630,45 @@ void NewFlightDialog::on_approachComboBox_currentTextChanged(const QString &arg1 */ void NewFlightDialog::on_functionComboBox_currentIndexChanged(int index) { - if (index == static_cast(OPL::PilotFunction::PIC)) { + int picPilotId = DBCache->getPilotNamesMap().key(ui->picNameLineEdit->text()); + int sicPilotId = DBCache->getPilotNamesMap().key(ui->sicNameLineEdit->text()); + int thirdPilotId = DBCache->getPilotNamesMap().key(ui->thirdPilotNameLineEdit->text()); + const QString &self = DBCache->getPilotNamesMap().value(1); + + switch (index) { + case static_cast(OPL::PilotFunction::PIC): + DEB << "PIC selected..."; ui->picNameLineEdit->setText(self); emit ui->picNameLineEdit->editingFinished(); - if (DBCache->getPilotNamesMap().key(ui->picNameLineEdit->text()) - == DBCache->getPilotNamesMap().key(ui->sicNameLineEdit->text())) - ui->sicNameLineEdit->setText(QString()); - } else if (index == static_cast(OPL::PilotFunction::SIC)) { + picPilotId = DBCache->getPilotNamesMap().key(ui->picNameLineEdit->text()); + + if (picPilotId == sicPilotId) { + ui->sicNameLineEdit->setText(QString()); + onBadInputReceived(ui->sicNameLineEdit); + } + if (picPilotId == thirdPilotId) { + ui->thirdPilotNameLineEdit->setText(QString()); + onBadInputReceived(ui->thirdPilotNameLineEdit); + } + break; + case static_cast(OPL::PilotFunction::SIC): + DEB << "SIC selected..."; ui->sicNameLineEdit->setText(self); emit ui->sicNameLineEdit->editingFinished(); - if (DBCache->getPilotNamesMap().key(ui->picNameLineEdit->text()) - == DBCache->getPilotNamesMap().key(ui->sicNameLineEdit->text())) + sicPilotId = DBCache->getPilotNamesMap().key(ui->sicNameLineEdit->text()); + + if (sicPilotId == picPilotId) { ui->picNameLineEdit->setText(QString()); + onBadInputReceived(ui->picNameLineEdit); + } + if (sicPilotId == thirdPilotId) { + ui->thirdPilotNameLineEdit->setText(QString()); + onBadInputReceived(ui->thirdPilotNameLineEdit); + } + break; + default: + break; + } } @@ -601,7 +681,7 @@ void NewFlightDialog::on_functionComboBox_currentIndexChanged(int index) * \param error_msg - the error string displayed to the user * \return */ -bool NewFlightDialog::checkPilotFunctionsValid() +bool NewFlightDialog::pilotFunctionsInvalid() { int pic_id = DBCache->getPilotNamesMap().key(ui->picNameLineEdit->text()); int function_index = ui->functionComboBox->currentIndex(); @@ -610,17 +690,52 @@ bool NewFlightDialog::checkPilotFunctionsValid() if (!(function_index == static_cast(OPL::PilotFunction::PIC) || function_index == static_cast(OPL::PilotFunction::FI))) { INFO(tr("The PIC is set to %1 but the Pilot Function is set to %2") .arg(ui->picNameLineEdit->text(), ui->functionComboBox->currentText())); - return false; + return true; } } else { if (function_index == static_cast(OPL::PilotFunction::PIC) || function_index == static_cast(OPL::PilotFunction::FI)) { INFO(tr("The Pilot Function is set to %1, but the PIC is set to %2") .arg(ui->functionComboBox->currentText(), ui->picNameLineEdit->text())); - return false; + return true; } } - return true; + return false; +} + +bool NewFlightDialog::duplicateNamesPresent() +{ + const int picId = DBCache->getPilotNamesMap().key(ui->picNameLineEdit->text()); + const int sicId = DBCache->getPilotNamesMap().key(ui->sicNameLineEdit->text()); + const int thirdPilotId = DBCache->getPilotNamesMap().key(ui->thirdPilotNameLineEdit->text()); + + // this is a bit explicit but better point out to the user what the case is + if (picId == sicId) { + INFO(tr("PIC and SIC names are the same.")); + return true; + } + if (picId == thirdPilotId && picId > 0) { + INFO(tr("PIC and third Pilot names are the same.")); + return true; + } + if (sicId == thirdPilotId && sicId > 0) { + INFO(tr("SIC and third Pilot names are the same.")); + return true; + } + + return false; +} + +bool NewFlightDialog::flightTimeIsZero() +{ + const OPL::Time tofb = OPL::Time::fromString(ui->tofbTimeLineEdit->text(), m_format); + const OPL::Time tonb = OPL::Time::fromString(ui->tonbTimeLineEdit->text(), m_format); + const int block_minutes = OPL::Time::blockMinutes(tofb, tonb); + if(block_minutes == 0) { + INFO(tr("Total time of flight is zero.")); + return true; + } + return false; } /*! @@ -638,17 +753,12 @@ 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 { - {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(i)) + "
    "); - mandatoryLineEdits->at(i)->setStyleSheet(QStringLiteral("border: 1px solid red")); + missing_items.append(validationItemsDisplayNames.value(static_cast(i)) + "
    "); + mandatoryLineEdits->at(i)->setStyleSheet(OPL::CssStyles::RED_BORDER); } } @@ -660,7 +770,11 @@ void NewFlightDialog::on_buttonBox_accepted() return; } - if(!checkPilotFunctionsValid()) + if(pilotFunctionsInvalid()) + return; + if(duplicateNamesPresent()) + return; + if(flightTimeIsZero()) return; // If input verification passed, collect input and submit to database @@ -682,3 +796,30 @@ void NewFlightDialog::on_buttonBox_accepted() } } + +void NewFlightDialog::on_calendarPushButton_clicked() +{ + calendar->setVisible(true); +} + +void NewFlightDialog::calendarDateSelected() +{ + calendar->setVisible(false); + ui->doftLineEdit->setText(OPL::Date(calendar->selectedDate(), m_format).toString()); + emit ui->doftLineEdit->editingFinished(); +} + +// EntryEditDialog interface + +void NewFlightDialog::loadEntry(int rowID) +{ + flightEntry = DB->getFlightEntry(rowID); + fillWithEntryData(); +} + +bool NewFlightDialog::deleteEntry(int rowID) +{ + const auto entry = DB->getFlightEntry(rowID); + return DB->remove(entry); +} + diff --git a/src/gui/dialogues/newflightdialog.h b/src/gui/dialogues/newflightdialog.h index eaf2717e..899d5615 100644 --- a/src/gui/dialogues/newflightdialog.h +++ b/src/gui/dialogues/newflightdialog.h @@ -24,10 +24,14 @@ #include #include +#include "QtWidgets/qcalendarwidget.h" +#include "src/database/database.h" #include "src/database/flightentry.h" +#include "src/gui/dialogues/entryeditdialog.h" #include "src/gui/verification/userinput.h" #include "src/opl.h" #include "src/gui/verification/validationstate.h" +#include "src/classes/date.h" namespace Ui { class NewFlightDialog; @@ -57,7 +61,7 @@ class NewFlightDialog; * Once the user is satisfied with his entries, a final set of input verification is triggered and the entry is submitted to the database, * see on_buttonBox_accepted() and Database::commit() */ -class NewFlightDialog : public QDialog +class NewFlightDialog : public EntryEditDialog { Q_OBJECT @@ -74,9 +78,13 @@ class NewFlightDialog : public QDialog explicit NewFlightDialog(int row_id, QWidget* parent = nullptr); ~NewFlightDialog(); + + private: Ui::NewFlightDialog *ui; ValidationState validationState; + QCalendarWidget *calendar; + OPL::DateTimeFormat m_format; /*! * \brief a AFlightEntry object that is used to store either position data @@ -88,23 +96,47 @@ class NewFlightDialog : public QDialog /*! * \brief timeLineEdits - Line Edits for time Off Blocks and Time On Blocks */ - static const inline QList* timeLineEdits; + static const inline QList *timeLineEdits; /*! * \brief locationLineEdits - Line Edits for Departure and Destination Airports */ - static const inline QList* locationLineEdits; + static const inline QList *locationLineEdits; /*! * \brief pilotNameLineEdits - Line Edits for Pilot in Command, Second in Command (Co-Pilot) and Third Pilot */ - static const inline QList* pilotNameLineEdits; + static const inline QList *pilotNameLineEdits; /*! * \brief mandatoryLineEdits - Contains the Line Edits that are needed for logging a complete flight from A to B. * The list is ordered like the ValidationItem enum so that indexed access is possible using the enum. */ - static const inline QList* mandatoryLineEdits; - static const inline QLatin1String self = QLatin1String("self"); + static const inline QList *mandatoryLineEdits; + // static const inline QLatin1String self = QLatin1String("self"); + static const inline QHash pilotFuncionsMap = { + {0, OPL::FlightEntry::TPIC}, + {1, OPL::FlightEntry::TPICUS}, + {2, OPL::FlightEntry::TSIC}, + {3, OPL::FlightEntry::TDUAL}, + {4, OPL::FlightEntry::TFI}, + }; + static const inline QHash validationItemsDisplayNames = { + {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")}, + }; + /*! + * \brief init - set up the UI + */ void init(); + /*! + * \brief setPilotFunction - Reads the application setting and pre-fills the logbook owners + * desired function + */ + void setPilotFunction(); void setupRawInputValidation(); void setupSignalsAndSlots(); void readSettings(); @@ -124,14 +156,30 @@ class NewFlightDialog : public QDialog */ void onBadInputReceived(QLineEdit *line_edit); - void updateBlockTimeLabel(); - void setNightCheckboxes(); + /*! + * \brief addNewDatabaseElement Adds a new element to the database + * \param parent - The line edit that triggered the event + * \param table - The table to which the new element is added + * \return true on success + * \details The flights database has a couple of NOT NULL constraints which + * must be met before a new flight can be submitted. If the user enters a + * constrained field which does not exist in a related table (pilots, tails + * or airports), the user is prompted to add a new entry to one of those + * tables before proceeding to log a flight with the missing element. + */ + bool addNewDatabaseElement(QLineEdit* parent, OPL::DbTable table); - bool addNewTail(QLineEdit& parent_line_edit); - bool addNewPilot(QLineEdit& parent_line_edit); + /*! + * \brief userWantsToAddNewEntry - Asks the user if he wants to add a new entry to the database + * \param table - The table to which the entry will be committed. + * \return true if the reply is QMessageBox::Yes + */ + bool userWantsToAddNewEntry(OPL::DbTable table); - bool checkPilotFunctionsValid(); + bool pilotFunctionsInvalid(); + bool duplicateNamesPresent(); + bool flightTimeIsZero(); OPL::RowData_T prepareFlightEntryData(); const static inline auto CAT_3 = QLatin1String(OPL::GLOBALS->getApproachTypes()[3].toLatin1()); @@ -148,8 +196,17 @@ private slots: void on_approachComboBox_currentTextChanged(const QString &arg1); void on_functionComboBox_currentIndexChanged(int index); + void on_calendarPushButton_clicked(); + + void calendarDateSelected(); + protected: bool eventFilter(QObject* object, QEvent* event) override; + + // EntryEditDialog interface +public: + virtual void loadEntry(int rowID) override; + virtual bool deleteEntry(int rowID) override; }; diff --git a/src/gui/dialogues/newflightdialog.ui b/src/gui/dialogues/newflightdialog.ui index 2ec380fa..dd911b16 100644 --- a/src/gui/dialogues/newflightdialog.ui +++ b/src/gui/dialogues/newflightdialog.ui @@ -6,48 +6,67 @@ 0 0 - 767 - 362 + 799 + 391
    Add New Flight - - + + + + false + - 160 + 200 0 - 120 + 200 16777215 + + + true + + + + + - - - - - 160 - 0 - + + + + Function - - - 120 - 16777215 - + + + + + + Off Blocks - - + + + + PIC + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + + + 160 @@ -62,42 +81,35 @@ - - + + - Take Off + Aircraft + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter - - - - false - - - - 200 - 0 - - - - - 200 - 16777215 - - - - - true - + + + + Approach + + + + - + Third Pilot + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter - - + + 160 @@ -112,8 +124,12 @@ - - + + + + Date Of flight + + @@ -122,8 +138,8 @@ - - + + 160 @@ -136,17 +152,27 @@ 16777215 + + YYYY-MM-DD + - + + + + Flight Rules + + + + On Blocks - - + + 160 @@ -161,81 +187,68 @@ - - + + - Total Time + SIC + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter - - - - - 160 - 0 - - + + - 00:00 + Landing - - + + - true - - - Qt::Horizontal - - - QDialogButtonBox::Cancel|QDialogButtonBox::Ok + false - - - - - 160 + 200 0 - 120 + 200 16777215 - - - - - Destination + - - - - Aircraft + + + + + + + + 160 + 0 + - - Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + true + - - - - - Off Blocks + 00:00 - - + + 160 @@ -248,35 +261,40 @@ 16777215 - - YYYY-MM-DD - - - - - false - + + - 200 + 160 0 - 200 + 120 16777215 - - - - - + + + + + VFR + + + + + IFR + + + + + + 160 @@ -291,11 +309,8 @@ - - - - false - + + 200 @@ -308,40 +323,29 @@ 16777215 - - - true - - - - - - false - + + - 200 + 160 0 - 200 + 120 16777215 - - - - - + + 160 @@ -356,17 +360,23 @@ - - - - Remarks + + + + + 160 + 0 + - - Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + 120 + 16777215 + - + Flight Number @@ -376,59 +386,18 @@ - - - - PIC - - - Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter - - - - - - - SIC - - - Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter - - - - - - - Date of flight - - - - - - - Third Pilot - - - Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter - - - - - - - Function - - - - + - Approach + Total Time - - + + + + + 160 @@ -443,45 +412,24 @@ - - - - Flight Rules + + + + true - - - - - - - - - - - - - VFR - - - - - IFR - - - - - - - - Landing + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok - - - - - + + + + false + 200 @@ -499,8 +447,21 @@ - - + + + + Remarks + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + + + + + false + 200 @@ -513,6 +474,45 @@ 16777215 + + + true + + + + + + + + + + + Destination + + + + + + + + + + Take Off + + + + + + + + + + Pilot Flying + + + + + @@ -521,17 +521,18 @@
    + doftLineEdit deptLocationLineEdit destLocationLineEdit tofbTimeLineEdit tonbTimeLineEdit + acftLineEdit picNameLineEdit sicNameLineEdit thirdPilotNameLineEdit flightNumberLineEdit remarksLineEdit - takeOffSpinBox - approachComboBox + calendarPushButton diff --git a/src/gui/dialogues/newpilotdialog.cpp b/src/gui/dialogues/newpilotdialog.cpp index aaa8a8a3..a3c5ffe5 100644 --- a/src/gui/dialogues/newpilotdialog.cpp +++ b/src/gui/dialogues/newpilotdialog.cpp @@ -21,16 +21,18 @@ #include "src/opl.h" #include "src/database/database.h" -#include "src/database/row.h" /*! * \brief NewPilotDialog::NewPilotDialog - creates a new pilot dialog which can be used to add a new entry to the database */ -NewPilotDialog::NewPilotDialog(QWidget *parent) : - QDialog(parent), +NewPilotDialog::NewPilotDialog(QString userInput, QWidget* parent) + : EntryEditDialog{parent}, ui(new Ui::NewPilot) { setup(); + if(userInput != QString()) { + ui->lastnameLineEdit->setText(userInput.replace(0, 1, userInput.first(1).toUpper())); + } ui->lastnameLineEdit->setFocus(); } @@ -39,7 +41,7 @@ NewPilotDialog::NewPilotDialog(QWidget *parent) : * \param rowId - the rowid of the entry to be edited in the database */ NewPilotDialog::NewPilotDialog(int rowId, QWidget *parent) : - QDialog(parent), + EntryEditDialog{rowId, parent}, ui(new Ui::NewPilot) { setup(); @@ -58,7 +60,9 @@ NewPilotDialog::~NewPilotDialog() void NewPilotDialog::setup() { ui->setupUi(this); - ui->companyLineEdit->setCompleter(QCompleterProvider.getCompleter(CompleterProvider::Companies)); + auto completer = QCompleterProvider.getCompleter(CompleterProvider::Companies); + completer->setCompletionMode(QCompleter::InlineCompletion); + ui->companyLineEdit->setCompleter(completer); } void NewPilotDialog::on_buttonBox_accepted() @@ -107,3 +111,17 @@ void NewPilotDialog::submitForm() QDialog::accept(); } } + +bool NewPilotDialog::deleteEntry(int rowId) +{ + auto entry = DB->getPilotEntry(rowId); + return DB->remove(entry); + +} + +void NewPilotDialog::loadEntry(int rowId) +{ + pilotEntry = DB->getPilotEntry(rowId); + formFiller(); + ui->lastnameLineEdit->setFocus(); +} diff --git a/src/gui/dialogues/newpilotdialog.h b/src/gui/dialogues/newpilotdialog.h index d29ff90a..16081a1d 100644 --- a/src/gui/dialogues/newpilotdialog.h +++ b/src/gui/dialogues/newpilotdialog.h @@ -24,6 +24,7 @@ #include #include #include "src/database/pilotentry.h" +#include "src/gui/dialogues/entryeditdialog.h" namespace Ui { class NewPilot; @@ -41,14 +42,17 @@ class NewPilot; * come in all different forms and shapes around the world. In order to maintain a maximum * amount of flexibility, any unicode input is allowed. */ -class NewPilotDialog : public QDialog +class NewPilotDialog : public EntryEditDialog { Q_OBJECT public: - explicit NewPilotDialog(QWidget *parent = nullptr); + explicit NewPilotDialog(QString userInput = QString(), QWidget *parent = nullptr); explicit NewPilotDialog(int rowId, QWidget *parent = nullptr); ~NewPilotDialog(); + + + private slots: void on_buttonBox_accepted(); private: @@ -67,6 +71,12 @@ private slots: * \brief submitForm - retreives the input from the line edits and commits to the database. */ void submitForm(); + + // EntryEditDialog interface +public: + virtual bool deleteEntry(int rowId) override; + virtual void loadEntry(int rowId) override; + }; diff --git a/src/gui/dialogues/newsimdialog.cpp b/src/gui/dialogues/newsimdialog.cpp index 9207a1e9..1a5ede94 100644 --- a/src/gui/dialogues/newsimdialog.cpp +++ b/src/gui/dialogues/newsimdialog.cpp @@ -4,33 +4,33 @@ #include "ui_newsimdialog.h" #include "src/opl.h" #include "src/classes/time.h" -#include "src/functions/datetime.h" #include "src/database/database.h" +#include "src/classes/settings.h" #include /*! * \brief create a NewSimDialog to add a new Simulator Entry to the database */ -NewSimDialog::NewSimDialog(QWidget *parent) : - QDialog(parent), +NewSimDialog::NewSimDialog(QWidget *parent) + : EntryEditDialog(parent), ui(new Ui::NewSimDialog) { - //entry = ASimulatorEntry(); ui->setupUi(this); - ui->dateLineEdit->setText(OPL::DateTime::currentDate()); init(); + + ui->dateLineEdit->setText(OPL::Date::today(m_format).toString()); } /*! * \brief create a NewSimDialog to edit an existing Simulator Entry * \param row_id of the entry to be edited */ -NewSimDialog::NewSimDialog(int row_id, QWidget *parent) : - QDialog(parent), +NewSimDialog::NewSimDialog(int row_id, QWidget *parent) + : EntryEditDialog(parent), ui(new Ui::NewSimDialog) { - ui->setupUi(this); - entry = DB->getSimEntry(row_id); init(); + + entry = DB->getSimEntry(row_id); fillEntryData(); } @@ -47,6 +47,8 @@ void NewSimDialog::init() completer->setCompletionMode(QCompleter::PopupCompletion); completer->setFilterMode(Qt::MatchContains); ui->aircraftTypeLineEdit->setCompleter(completer); + + m_format = Settings::getDisplayFormat(); } /*! @@ -55,8 +57,8 @@ void NewSimDialog::init() void NewSimDialog::fillEntryData() { const auto& data = entry.getData(); - ui->dateLineEdit->setText(data.value(OPL::SimulatorEntry::DATE).toString()); - ui->totalTimeLineEdit->setText(OPL::Time(data.value(OPL::SimulatorEntry::TIME).toInt()).toString()); + ui->dateLineEdit->setText(OPL::Date(data.value(OPL::SimulatorEntry::DATE).toInt(), m_format).toString()); + ui->totalTimeLineEdit->setText(OPL::Time(data.value(OPL::SimulatorEntry::TIME).toInt(), m_format).toString()); ui->deviceTypeComboBox->setCurrentIndex(data.value(OPL::SimulatorEntry::TYPE).toInt()); ui->aircraftTypeLineEdit->setText(data.value(OPL::SimulatorEntry::ACFT).toString()); ui->registrationLineEdit->setText(data.value(OPL::SimulatorEntry::REG).toString()); @@ -70,12 +72,9 @@ NewSimDialog::~NewSimDialog() void NewSimDialog::on_dateLineEdit_editingFinished() { - auto text = ui->dateLineEdit->text(); - - OPL::DateFormat date_format = OPL::DateFormat::ISODate; - auto date = OPL::DateTime::parseInput(text, date_format); - if (date.isValid()) { - ui->dateLineEdit->setText(OPL::DateTime::dateToString(date, date_format)); + const auto date = OPL::Date(ui->dateLineEdit->text(), m_format); + if(date.isValid()) { + ui->dateLineEdit->setText(date.toString()); ui->dateLineEdit->setStyleSheet(QString()); return; } else { @@ -87,7 +86,7 @@ void NewSimDialog::on_dateLineEdit_editingFinished() void NewSimDialog::on_totalTimeLineEdit_editingFinished() { - const auto input = TimeInput(ui->totalTimeLineEdit->text()); + const auto input = TimeInput(ui->totalTimeLineEdit->text(), m_format); if(input.isValid()) return; else { @@ -122,9 +121,7 @@ void NewSimDialog::on_helpPushButton_clicked() bool NewSimDialog::verifyInput(QString& error_msg) { // Date - auto text = ui->dateLineEdit->text(); - OPL::DateFormat date_format = OPL::DateFormat::ISODate; - const auto date = OPL::DateTime::parseInput(text, date_format); + const auto date = OPL::Date(ui->dateLineEdit->text(), m_format); if (!date.isValid()) { ui->dateLineEdit->setStyleSheet(OPL::CssStyles::RED_BORDER); @@ -133,10 +130,10 @@ bool NewSimDialog::verifyInput(QString& error_msg) return false; } // Time - if(!TimeInput(ui->totalTimeLineEdit->text()).isValid()) + if(!TimeInput(ui->totalTimeLineEdit->text(), m_format).isValid()) return false; - const OPL::Time time = OPL::Time::fromString(ui->totalTimeLineEdit->text()); + const OPL::Time time = OPL::Time::fromString(ui->totalTimeLineEdit->text(), m_format); if (!time.isValidTimeOfDay()) { ui->totalTimeLineEdit->setStyleSheet(OPL::CssStyles::RED_BORDER); @@ -159,9 +156,10 @@ OPL::RowData_T NewSimDialog::collectInput() { OPL::RowData_T new_entry; // Date - new_entry.insert(OPL::SimulatorEntry::DATE, ui->dateLineEdit->text()); + const auto date = OPL::Date(ui->dateLineEdit->text(), m_format); + new_entry.insert(OPL::SimulatorEntry::DATE, date.toJulianDay()); // Time - new_entry.insert(OPL::SimulatorEntry::TIME, OPL::Time::fromString(ui->totalTimeLineEdit->text()).toMinutes()); + new_entry.insert(OPL::SimulatorEntry::TIME, OPL::Time::fromString(ui->totalTimeLineEdit->text(), m_format).toMinutes()); // Device Type new_entry.insert(OPL::SimulatorEntry::TYPE, ui->deviceTypeComboBox->currentText()); // Aircraft Type @@ -193,3 +191,17 @@ void NewSimDialog::on_buttonBox_accepted() else WARN(tr("Unable to commit entry to database. The following error has ocurred

    %1").arg(DB->lastError.text())); } + +// EntryEdit interface +void NewSimDialog::loadEntry(int rowID) +{ + entry = DB->getSimEntry(rowID); + init(); + fillEntryData(); +} + +bool NewSimDialog::deleteEntry(int rowID) +{ + const auto entry = DB->getSimEntry(rowID); + return DB->remove(entry); +} diff --git a/src/gui/dialogues/newsimdialog.h b/src/gui/dialogues/newsimdialog.h index fb2a0770..a9ce411a 100644 --- a/src/gui/dialogues/newsimdialog.h +++ b/src/gui/dialogues/newsimdialog.h @@ -2,7 +2,10 @@ #define NEWSIMDIALOG_H #include +#include "src/database/database.h" #include "src/database/simulatorentry.h" +#include "src/gui/dialogues/entryeditdialog.h" +#include "src/classes/date.h" namespace Ui { class NewSimDialog; @@ -17,7 +20,7 @@ class NewSimDialog; * * A QCompleter provides in-line completion for the aircraft type field. */ -class NewSimDialog : public QDialog +class NewSimDialog : public EntryEditDialog { Q_OBJECT @@ -39,12 +42,24 @@ private slots: private: Ui::NewSimDialog *ui; + //TODO load from settings + OPL::DateTimeFormat m_format = OPL::DateTimeFormat( + OPL::DateTimeFormat::DateFormat::Default, + QStringLiteral("yyyy-MM-dd"), + OPL::DateTimeFormat::TimeFormat::Default, + QStringLiteral("hh:mm") + ); + void init(); void fillEntryData(); bool verifyInput(QString &error_msg); OPL::RowData_T collectInput(); - OPL::SimulatorEntry entry; + + // EntryEditDialog interface +public: + virtual void loadEntry(int rowID) override; + virtual bool deleteEntry(int rowID) override; }; #endif // NEWSIMDIALOG_H diff --git a/src/gui/dialogues/newtaildialog.cpp b/src/gui/dialogues/newtaildialog.cpp index 77c9ad59..a1634ad7 100644 --- a/src/gui/dialogues/newtaildialog.cpp +++ b/src/gui/dialogues/newtaildialog.cpp @@ -22,7 +22,7 @@ #include "src/opl.h" NewTailDialog::NewTailDialog(const QString &new_registration, QWidget *parent) : - QDialog(parent), + EntryEditDialog(parent), ui(new Ui::NewTail) { ui->setupUi(this); @@ -38,7 +38,7 @@ NewTailDialog::NewTailDialog(const QString &new_registration, QWidget *parent) : } NewTailDialog::NewTailDialog(int row_id, QWidget *parent) : - QDialog(parent), + EntryEditDialog(parent), ui(new Ui::NewTail) { ui->setupUi(this); @@ -202,6 +202,12 @@ void NewTailDialog::submitForm() //create db object entry.setData(new_data); + + // add type string + auto data = entry.getData(); + data.insert(OPL::TailEntry::TYPE_STRING, entry.type()); + entry.setData(data); + LOG << "Commiting: " << entry; if (!DB->commit(entry)) { QMessageBox message_box(this); @@ -271,15 +277,32 @@ void NewTailDialog::onSearchCompleterActivated() const auto &text = ui->searchLineEdit->text(); if (aircraftList.contains(text)) { - DEB << "Template Selected. aircraft_id is: " << idMap.key(text); - //call autofiller for dialog - fillForm(DB->getAircraftEntry(idMap.key(text)), true); - ui->searchLineEdit->setStyleSheet(QStringLiteral("border: 1px solid green")); - ui->searchLabel->setText(text); - } else { - //for example, editing finished without selecting a result from Qcompleter - ui->searchLineEdit->setStyleSheet(QStringLiteral("border: 1px solid orange")); - } + DEB << "Template Selected. aircraft_id is: " << idMap.key(text); + //call autofiller for dialog + fillForm(DB->getAircraftEntry(idMap.key(text)), true); + ui->searchLineEdit->setStyleSheet(QStringLiteral("border: 1px solid green")); + ui->searchLabel->setText(text); + } else { + //for example, editing finished without selecting a result from Qcompleter + ui->searchLineEdit->setStyleSheet(QStringLiteral("border: 1px solid orange")); + } +} + +bool NewTailDialog::deleteEntry(int rowID) +{ + auto entry = DB->getTailEntry(rowID); + return DB->remove(entry); +} + +void NewTailDialog::loadEntry(int rowId) +{ + ui->searchLabel->hide(); + ui->searchLineEdit->hide(); + ui->line->hide(); + + setupValidators(); + entry = DB->getTailEntry(rowId); + fillForm(entry, false); } void NewTailDialog::on_registrationLineEdit_textChanged(const QString &arg1) diff --git a/src/gui/dialogues/newtaildialog.h b/src/gui/dialogues/newtaildialog.h index f2cdf240..437e2f0d 100644 --- a/src/gui/dialogues/newtaildialog.h +++ b/src/gui/dialogues/newtaildialog.h @@ -26,6 +26,7 @@ #include "src/database/row.h" #include "src/database/tailentry.h" +#include "src/gui/dialogues/entryeditdialog.h" namespace Ui { class NewTail; @@ -55,7 +56,7 @@ class NewTail; * * */ -class NewTailDialog : public QDialog +class NewTailDialog : public EntryEditDialog { Q_OBJECT @@ -64,7 +65,7 @@ class NewTailDialog : public QDialog * \brief NewTailDialog - create a new ATailEntry and submit it to the database * \param new_registration - when called from the NewFlightDialog, pre-fills the registration already entered. */ - explicit NewTailDialog(const QString& new_registration, QWidget *parent = nullptr); + explicit NewTailDialog(const QString &new_registration, QWidget *parent = nullptr); /*! * \brief NewTailDialog - edit an existing Tail Entry * \param row_id - the ROW_ID of the entry to be edited in the database @@ -73,6 +74,8 @@ class NewTailDialog : public QDialog ~NewTailDialog(); + + signals: void tailDataChanged(); private: @@ -102,6 +105,11 @@ private slots: void on_buttonBox_accepted(); void onSearchCompleterActivated(); + + // EntryEditDialog interface +public: + virtual bool deleteEntry(int rowID) override; + virtual void loadEntry(int rowId) override; }; #endif // NEWTAIL_H diff --git a/src/gui/verification/timeinput.cpp b/src/gui/verification/timeinput.cpp index 7f4ed810..aa869dd5 100644 --- a/src/gui/verification/timeinput.cpp +++ b/src/gui/verification/timeinput.cpp @@ -1,10 +1,11 @@ #include "timeinput.h" +#include "src/classes/time.h" #include "src/opl.h" #include bool TimeInput::isValid() const { - return QTime::fromString(input, OPL::Format::TIME_FORMAT).isValid(); + return OPL::Time::fromString(input, m_format).isValid(); } /*! @@ -14,8 +15,23 @@ bool TimeInput::isValid() const */ QString TimeInput::fixup() const { - QString fixed = input; + switch(m_format.timeFormat()) { + case OPL::DateTimeFormat::TimeFormat::Default: + return fixDefaultFormat(); + case OPL::DateTimeFormat::TimeFormat::Decimal: + return fixDecimalFormat(); + case OPL::DateTimeFormat::TimeFormat::Custom: + return input; // custom formats cannot be fixed + break; + } +} +const QString TimeInput::fixDefaultFormat() const +{ + if(input == QString()) + return QStringLiteral("00:00"); + // try inserting a ':' for hhmm inputs + QString fixed = input; if (input.contains(':')) { // contains seperator if(input.length() == 4) fixed.prepend(QLatin1Char('0')); @@ -29,6 +45,20 @@ QString TimeInput::fixup() const } } - QTime time = QTime::fromString(fixed, OPL::Format::TIME_FORMAT); - return time.toString(OPL::Format::TIME_FORMAT); + OPL::Time fixedTime = OPL::Time::fromString(fixed, m_format); + if(fixedTime.isValid()) { + return OPL::Time::fromString(fixed, m_format).toString(); + } else { + return QString(); + } + + +} + +const QString TimeInput::fixDecimalFormat() const +{ + // try to replace an erroneus decimal seperator + QString fixed = input; + return OPL::Time::fromString(fixed.replace(QLatin1Char(','), OPL::DECIMAL_SEPERATOR), m_format).toString(); } + diff --git a/src/gui/verification/timeinput.h b/src/gui/verification/timeinput.h index 87c7dd60..f86d5e82 100644 --- a/src/gui/verification/timeinput.h +++ b/src/gui/verification/timeinput.h @@ -1,13 +1,15 @@ #ifndef TIMEINPUT_H #define TIMEINPUT_H +#include "src/opl.h" #include "userinput.h" class TimeInput : public UserInput { public: TimeInput() = delete; - TimeInput(const QString &input) : UserInput(input) {} + TimeInput(const QString &input, const OPL::DateTimeFormat &format) + : UserInput(input), m_format(format) {} /*! * \brief Checks if a user entered String is a valid time input @@ -25,6 +27,10 @@ class TimeInput : public UserInput */ QString fixup() const override; private: + const OPL::DateTimeFormat &m_format; + + const QString fixDefaultFormat() const; + const QString fixDecimalFormat() const; }; #endif // TIMEINPUT_H diff --git a/src/gui/widgets/airporttableeditwidget.cpp b/src/gui/widgets/airporttableeditwidget.cpp new file mode 100644 index 00000000..cde2de3c --- /dev/null +++ b/src/gui/widgets/airporttableeditwidget.cpp @@ -0,0 +1,79 @@ +#include "airporttableeditwidget.h" +#include "src/database/database.h" +#include "src/gui/dialogues/newairportdialog.h" + +AirportTableEditWidget::AirportTableEditWidget(QWidget *parent) + : TableEditWidget(Vertical, parent) +{} + +void AirportTableEditWidget::setupModelAndView() +{ + m_model = new QSqlTableModel(this, DB->database()); + m_model->setTable(OPL::GLOBALS->getDbTableName(OPL::DbTable::Airports)); + m_model->select(); + + for(int i = 0; i < HEADER_NAMES.size(); i++) { + m_model->setHeaderData(i + 1, Qt::Horizontal, HEADER_NAMES.at(i)); + } + + m_view->setModel(m_model); + m_view->setSelectionMode(QAbstractItemView::SingleSelection); + m_view->setSelectionBehavior(QAbstractItemView::SelectRows); + m_view->setEditTriggers(QAbstractItemView::NoEditTriggers); + m_view->horizontalHeader()->setStretchLastSection(QHeaderView::Stretch); + m_view->resizeColumnsToContents(); + m_view->verticalHeader()->hide(); + m_view->setAlternatingRowColors(true); + m_view->hideColumn(COL_ROWID); + +} + +void AirportTableEditWidget::setupUI() +{ + // the base class does most of the setup + TableEditWidget::setupUI(); + + // only need to set the table specific labels and combo box items + m_addNewEntryPushButton->setText(tr("Add New Airport")); + m_deleteEntryPushButton->setText(tr("Delete Selected Airport")); + for(const int i : FILTER_COLUMNS) { + m_filterSelectionComboBox->addItem(HEADER_NAMES.at(i)); + } +} + +QString AirportTableEditWidget::deleteErrorString(int rowId) +{ + return tr("
    Unable to delete.

    The following error has ocurred: %1" + ).arg(DB->lastError.text()); +} + +QString AirportTableEditWidget::confirmDeleteString(int rowId) +{ + const auto entry = DB->getAirportEntry(rowId); + return tr("The following airport will be deleted:

    " + "%1
    " + "Deleting airports is irreversible.
    Do you want to proceed?" + ).arg(entry.getAirportDescriptor()); +} + +EntryEditDialog *AirportTableEditWidget::getEntryEditDialog(QWidget *parent) +{ + return new NewAirportDialog(parent); +} + +void AirportTableEditWidget::filterTextChanged(const QString &filterString) +{ + if(filterString.isEmpty()) { + m_model->setFilter(QString()); + return; + } + + int i = m_filterSelectionComboBox->currentIndex(); + const QString filter = + QLatin1Char('\"') + + HEADER_NAMES.at(FILTER_COLUMNS[i]) + + QLatin1String("\" LIKE '%") + + filterString + + QLatin1String("%'"); + m_model->setFilter(filter); +} diff --git a/src/gui/widgets/airporttableeditwidget.h b/src/gui/widgets/airporttableeditwidget.h new file mode 100644 index 00000000..ab52b07e --- /dev/null +++ b/src/gui/widgets/airporttableeditwidget.h @@ -0,0 +1,52 @@ +#ifndef AIRPORTTABLEEDITWIDGET_H +#define AIRPORTTABLEEDITWIDGET_H + +#include "tableeditwidget.h" +#include +#include "src/database/airportentry.h" + +class AirportTableEditWidget : public TableEditWidget +{ + Q_OBJECT +public: + AirportTableEditWidget() = delete; + AirportTableEditWidget(QWidget *parent = nullptr); + + // TableEditWidget interface + virtual void setupModelAndView() override; + virtual void setupUI() override; + virtual QString deleteErrorString(int rowId) override; + virtual QString confirmDeleteString(int rowId) override; + virtual EntryEditDialog *getEntryEditDialog(QWidget *parent) override; + + // table columns and header names + + const int COL_ROWID = 0; + + // used to display the Header Views and Fill the FilterComboBox + const QStringList HEADER_NAMES = { + tr("ICAO"), + tr("IATA"), + tr("Name"), + tr("Latitude"), + tr("Longitude"), + tr("Country"), + tr("Time Zone"), + }; + + // These are indexes into HEADER_NAMES + const int FILTER_COLUMNS[4] = {0, 1, 2, 5}; + + // The sql column names corresponding to the entries of the FilterComboBox index + const static inline QStringList FILTER_COLUMN_NAMES = { + OPL::AirportEntry::ICAO, + OPL::AirportEntry::IATA, + OPL::AirportEntry::NAME, + OPL::AirportEntry::TZ_OLSON, + }; + +private slots: + virtual void filterTextChanged(const QString &filterString) override; +}; + +#endif // AIRPORTTABLEEDITWIDGET_H diff --git a/src/gui/widgets/backupwidget.cpp b/src/gui/widgets/backupwidget.cpp index bfec0eff..d4712e72 100644 --- a/src/gui/widgets/backupwidget.cpp +++ b/src/gui/widgets/backupwidget.cpp @@ -22,6 +22,8 @@ #include "src/functions/datetime.h" #include "src/database/dbsummary.h" #include "src/gui/dialogues/firstrundialog.h" +#include "src/classes/settings.h" +#include "src/classes/styleddatedelegate.h" #include #include @@ -39,6 +41,12 @@ BackupWidget::BackupWidget(QWidget *parent) : model->setHorizontalHeaderLabels(QStringList{tr("Total Time"),tr("Flights"), tr("Aircraft"), tr("Pilots"), tr("Last Flight"), tr("Backup File")}); view = ui->tableView; + + + // julian day to Date Format + const auto dateDelegate = new StyledDateDelegate(Settings::getDisplayFormat(), model); + view->setItemDelegateForColumn(DATE_COLUMN, dateDelegate); + refresh(); } @@ -86,8 +94,8 @@ const QString BackupWidget::absoluteBackupPath() const QString BackupWidget::backupName() { auto owner = DB->getPilotEntry(1); - return QString("logbook_backup_%1_%2.db").arg( - OPL::DateTime::dateTimeToString(QDateTime::currentDateTime(), OPL::DateTimeFormat::Backup), + return QStringLiteral("logbook_backup_%1_%2.db").arg( + OPL::DateTime::dateTimeToString(QDateTime::currentDateTime(), OPL::DateTimeFormat_deprecated::Backup), owner.getLastName() ); } diff --git a/src/gui/widgets/backupwidget.h b/src/gui/widgets/backupwidget.h index 13819825..ee80ff78 100644 --- a/src/gui/widgets/backupwidget.h +++ b/src/gui/widgets/backupwidget.h @@ -96,6 +96,8 @@ private slots: QList selectedRows; void refresh(); + static constexpr int DATE_COLUMN = 4; + protected: /*! * \brief Handles change events, like updating the UI to new localisation diff --git a/src/gui/widgets/currencywidget.cpp b/src/gui/widgets/currencywidget.cpp new file mode 100644 index 00000000..98a79bf3 --- /dev/null +++ b/src/gui/widgets/currencywidget.cpp @@ -0,0 +1,264 @@ +#include "currencywidget.h" +#include "QtSql/qsqltablemodel.h" +#include "QtWidgets/qheaderview.h" +#include "qgridlayout.h" +#include "src/classes/easaftl.h" +#include "src/classes/styleddatedelegate.h" +#include "src/classes/time.h" +#include "src/database/database.h" +#include "src/functions/statistics.h" +#include "src/classes/settings.h" +#include +#include +#include + +CurrencyWidget::CurrencyWidget(QWidget *parent) + : QWidget{parent} +{ + m_format = Settings::getDisplayFormat(); + setupModelAndView(); + setupUI(); + + fillTakeOffAndLandingCurrencies(); + fillFlightTimeLimitations(); + + warnAboutExpiries(); +} + +void CurrencyWidget::setupModelAndView() +{ + model = new QSqlTableModel(this, DB->database()); + model->setTable(OPL::GLOBALS->getDbTableName(OPL::DbTable::Currencies)); + model->select(); + model->setHeaderData(EXPIRY_DATE_COLUMN, Qt::Horizontal, tr("Expiry Date")); + model->setHeaderData(CURRENCY_NAME_COLUMN, Qt::Horizontal, tr("Name")); + + view = new QTableView(this); + view->setModel(model); + view->setSelectionMode(QAbstractItemView::SingleSelection); + view->setSelectionBehavior(QAbstractItemView::SelectItems); + view->setEditTriggers(QAbstractItemView::NoEditTriggers); + view->resizeColumnsToContents(); + view->horizontalHeader()->setStretchLastSection(QHeaderView::Stretch); + view->verticalHeader()->hide(); + view->setAlternatingRowColors(true); + view->hideColumn(ROWID_COLUMN); + + const auto dateDelegate = new StyledDateDelegate(Settings::getDisplayFormat(), this); + view->setItemDelegateForColumn(EXPIRY_DATE_COLUMN, dateDelegate); +} + +void CurrencyWidget::setupUI() +{ + // create a 3-column grid layout + int colL = 0; // left column + int colM = 1; // middle column + int colR = 2; // right column + int allColSpan = 3; // span all columns + int singleRowSpan = 1; // span only a single row + int row = 0; + auto gridLayout = new QGridLayout(this); + + // Take-off and Landing Currency + gridLayout->addWidget(takeOffLandingHeaderLabel, row, colM, Qt::AlignCenter); + row++; + gridLayout->addWidget(getHorizontalLine(), row, colL, singleRowSpan, allColSpan); + row++; + + gridLayout->addWidget(takeOffCountLabel, row, colL, Qt::AlignLeft); + gridLayout->addWidget(takeOffCountDisplayLabel, row, colR, Qt::AlignRight); + row++; + + gridLayout->addWidget(landingCountLabel, row, colL, Qt::AlignLeft); + gridLayout->addWidget(landingCountDisplayLabel, row, colR, Qt::AlignRight); + row++; + + gridLayout->addWidget(takeOffLandingExpiryLabel, row, colL, Qt::AlignLeft); + gridLayout->addWidget(takeOffLandingExpiryDisplayLabel, row, colR, Qt::AlignRight); + row++; + + // Flight Time Limitations + gridLayout->addWidget(flightTimeHeaderLabel, row, colM, Qt::AlignCenter); + row++; + gridLayout->addWidget(getHorizontalLine(), row, colL, singleRowSpan, allColSpan); + row++; + + gridLayout->addWidget(flightTime28DaysLabel, row, colL, Qt::AlignLeft); + gridLayout->addWidget(flightTime28DaysDisplayLabel, row, colR, Qt::AlignRight); + row++; + + gridLayout->addWidget(flightTime365DaysLabel, row, colL, Qt::AlignLeft); + gridLayout->addWidget(flightTime365DaysDisplayLabel, row, colR, Qt::AlignRight); + row++; + + gridLayout->addWidget(flightTimeCalendarYearLabel, row, colL, Qt::AlignLeft); + gridLayout->addWidget(flightTimeCalendarYearDisplayLabel, row, colR, Qt::AlignRight); + row++; + + // Expiries (currencies table) + gridLayout->addWidget(expiriesHeaderLabel, row, colM, Qt::AlignCenter); + row++; + gridLayout->addWidget(getHorizontalLine(), row, colL, singleRowSpan, allColSpan); + row++; + gridLayout->addWidget(view, row, colL, singleRowSpan, allColSpan); + + // set the layout active + layout = gridLayout; + + // allocate a widget for date selection + calendar = new QCalendarWidget(this); + calendar->setVisible(false); + calendar->setWindowFlags(Qt::Dialog); // pop-up calendar + + + // connect signals + QObject::connect(view, &QTableView::activated, + this, &CurrencyWidget::editRequested); + QObject::connect(calendar, &QCalendarWidget::selectionChanged, + this, &CurrencyWidget::newExpiryDateSelected); +} + +void CurrencyWidget::fillTakeOffAndLandingCurrencies() +{ + const auto takeoff_landings = OPL::Statistics::countTakeOffLanding(); + LOG << "Currencies: " << takeoff_landings; + if(takeoff_landings.isEmpty() || takeoff_landings.size() != 2) + return; + + QList displayLabels = { + takeOffCountDisplayLabel, + landingCountDisplayLabel + }; + + for(int i = 0; i < 2; i++) { + int count = takeoff_landings[i].toInt(); + if(count < 3) + setLabelColour(displayLabels[i], Colour::Red); + displayLabels[i]->setText(displayLabels[i]->text().arg(count)); + } + QDate expiration_date = OPL::Statistics::currencyTakeOffLandingExpiry(); + if (expiration_date <= QDate::currentDate()) + setLabelColour(takeOffLandingExpiryDisplayLabel, Colour::Red); + takeOffLandingExpiryDisplayLabel->setText(expiration_date.toString(Qt::TextDate)); +} + +void CurrencyWidget::fillFlightTimeLimitations() +{ + const QList> limits = + { + { flightTime28DaysDisplayLabel, OPL::Statistics::TimeFrame::Rolling28Days }, + { flightTime365DaysDisplayLabel, OPL::Statistics::TimeFrame::Rolling12Months }, + { flightTimeCalendarYearDisplayLabel, OPL::Statistics::TimeFrame::CalendarYear }, + }; + + double ftlWarningThreshold = Settings::getFtlWarningThreshold(); + for (const auto &pair : limits) { + int accruedMinutes = OPL::Statistics::totalTime(pair.second); + int limitMinutes = EasaFTL::getLimit(pair.second); + pair.first->setText(OPL::Time(accruedMinutes, m_format).toString()); + + + if (accruedMinutes >= limitMinutes) + setLabelColour(pair.first, Colour::Red); + else if (accruedMinutes >= limitMinutes * ftlWarningThreshold) + setLabelColour(pair.first, Colour::Orange); + } +} + +void CurrencyWidget::editRequested(const QModelIndex &index) +{ + if(index.column() == EXPIRY_DATE_COLUMN) { + expiryDateEditRequested(index); + } + + if(index.column() == CURRENCY_NAME_COLUMN) { + displayNameEditRequested(index); + } +} + + + +void CurrencyWidget::refresh() +{ + model->select(); + fillTakeOffAndLandingCurrencies(); + fillFlightTimeLimitations(); + + view->resizeColumnsToContents(); + view->horizontalHeader()->setStretchLastSection(QHeaderView::Stretch); +} + +void CurrencyWidget::displayNameEditRequested(const QModelIndex &index) +{ + QString oldData = index.data().toString(); + bool textEdited; + const QString text = QInputDialog::getText( + this, + tr("Edit Currency Name"), + tr("Please enter a name for this currency"), + QLineEdit::Normal, + oldData, + &textEdited); + + if(textEdited) { + model->setData(index, text); + model->submitAll(); + } +} + +void CurrencyWidget::expiryDateEditRequested(const QModelIndex &index) +{ + const int selectedDate = index.data().toInt(); + + if(selectedDate > 0) { + const QSignalBlocker blocker(calendar); + calendar->setSelectedDate(QDate::fromJulianDay(selectedDate)); + calendar->show(); + } else { + calendar->show(); + } + // calendars date selected signal is connected to newExpiryDateSelected() +} + +void CurrencyWidget::newExpiryDateSelected() +{ + calendar->hide(); + model->setData(view->selectionModel()->currentIndex(), calendar->selectedDate().toJulianDay()); + model->submitAll(); + model->select(); +} + +void CurrencyWidget::warnAboutExpiries() +{ + int warningThreshold = Settings::getCurrencyWarningThreshold(); + if(warningThreshold < 1) { + return; + } + + const QDate today = QDate::currentDate(); + + for(int i = 0; i < model->rowCount(); i++) { + const QModelIndex dateIndex = model->index(i, EXPIRY_DATE_COLUMN); + if(dateIndex.data().toInt() == 0) { + continue; + } + const auto expiryDate = QDate::fromJulianDay(dateIndex.data().toInt()); + const auto warningDate = QDate::fromJulianDay(expiryDate.toJulianDay() - warningThreshold); + + if(today >= warningDate) { + const QString dateString = expiryDate.toString(Qt::ISODate); + const QString nameString = model->index(i, CURRENCY_NAME_COLUMN).data().toString(); + const QString daysLeft = QString::number(expiryDate.toJulianDay() - today.toJulianDay()); + + QString msg = tr("Your %1 expires in %2 days:

    %3").arg(nameString, daysLeft, dateString); + WARN(msg); + } + } +} + +QFrame *CurrencyWidget::getHorizontalLine() +{ + QFrame* newFrame = new QFrame(this); + newFrame->setFrameShape(QFrame::HLine); + return newFrame; +} diff --git a/src/gui/widgets/currencywidget.h b/src/gui/widgets/currencywidget.h new file mode 100644 index 00000000..828b8b39 --- /dev/null +++ b/src/gui/widgets/currencywidget.h @@ -0,0 +1,84 @@ +#ifndef CURRENCYWIDGET_H +#define CURRENCYWIDGET_H + +#include "src/opl.h" +#include +#include +#include +#include +#include +class CurrencyWidget : public QWidget +{ + Q_OBJECT + QLayout* layout; + QTableView *view; + QSqlTableModel *model; + QCalendarWidget *calendar; + QModelIndex lastSelection; + OPL::DateTimeFormat m_format; + + int ROWID_COLUMN = 0; + int CURRENCY_NAME_COLUMN = 1; + int EXPIRY_DATE_COLUMN = 2; + + void setupModelAndView(); + void setupUI(); + void fillTakeOffAndLandingCurrencies(); + void fillFlightTimeLimitations(); + void warnAboutExpiries(); + + void displayNameEditRequested(const QModelIndex &index); + void expiryDateEditRequested(const QModelIndex &index); + + QFrame *getHorizontalLine(); + QLabel *takeOffLandingHeaderLabel = new QLabel(tr("Take-off and Landing Currency<\b>"), this); + QLabel *takeOffCountLabel = new QLabel(tr("Take offs (last 90 days)"), this); + QLabel *takeOffCountDisplayLabel = new QLabel(QStringLiteral("%1"), this); + QLabel *landingCountLabel = new QLabel(tr("Landings (last 90 days)"), this); + QLabel *landingCountDisplayLabel = new QLabel(QStringLiteral("%1"), this); + QLabel *takeOffLandingExpiryLabel = new QLabel(tr("3 Take-offs and Landings expiry date"), this); + QLabel *takeOffLandingExpiryDisplayLabel = new QLabel(QStringLiteral("%1"), this); + QLabel *flightTimeHeaderLabel = new QLabel(tr("Flight Time Limitations"), this); + QLabel *flightTime28DaysLabel = new QLabel(tr("Flight time (last 28 days)"), this); + QLabel *flightTime28DaysDisplayLabel = new QLabel(QStringLiteral("%1"), this); + QLabel *flightTime365DaysLabel = new QLabel(tr("FLight time (last 365 days)"), this); + QLabel *flightTime365DaysDisplayLabel = new QLabel(QStringLiteral("%1"), this); + QLabel *flightTimeCalendarYearLabel = new QLabel(tr("Flight time (this calendar year"),this); + QLabel *flightTimeCalendarYearDisplayLabel = new QLabel(QStringLiteral("%1"), this); + QLabel *expiriesHeaderLabel = new QLabel(tr("Expiries<\b>"), this); + + enum class Colour {Red, Orange, None}; + inline void setLabelColour(QLabel* label, Colour colour) + { + switch (colour) { + case Colour::None: + label->setStyleSheet(QString()); + break; + case Colour::Red: + label->setStyleSheet(QStringLiteral("color: red")); + break; + case Colour::Orange: + label->setStyleSheet(QStringLiteral("color: orange")); + break; + default: + label->setStyleSheet(QString()); + break; + } + } + + +private slots: + void editRequested(const QModelIndex &index); + void newExpiryDateSelected(); + +public slots: + void refresh(); + +public: + explicit CurrencyWidget(QWidget *parent = nullptr); + +signals: + +}; + +#endif // CURRENCYWIDGET_H diff --git a/src/gui/widgets/debugwidget.cpp b/src/gui/widgets/debugwidget.cpp index 68a8cd2e..60af1496 100644 --- a/src/gui/widgets/debugwidget.cpp +++ b/src/gui/widgets/debugwidget.cpp @@ -300,6 +300,6 @@ void DebugWidget::on_debug2LineEdit_editingFinished() void DebugWidget::on_pushButton_clicked() { Settings::resetToDefaults(); - Settings::write(Settings::Main::SetupComplete, false); + Settings::setSetupCompleted(false); } diff --git a/src/gui/widgets/homewidget.cpp b/src/gui/widgets/homewidget.cpp index 822f2d9a..c139e653 100644 --- a/src/gui/widgets/homewidget.cpp +++ b/src/gui/widgets/homewidget.cpp @@ -16,46 +16,23 @@ *along with this program. If not, see . */ #include "homewidget.h" -#include "src/functions/statistics.h" +#include "src/gui/widgets/currencywidget.h" #include "src/gui/widgets/totalswidget.h" #include "ui_homewidget.h" #include "src/database/database.h" -#include "src/classes/time.h" -#include "src/classes/settings.h" -#include "src/database/row.h" - -// EASA FTL Limitations in minutes -// 100 hours per 28 days -static const int ROLLING_28_DAYS = 6000; -// 900 hours per calendar year -static const int CALENDAR_YEAR = 54000; -// 1000 hours per rolling 12 months -static const int ROLLING_12_MONTHS = 60000; -// Todo: Encapsulate and plan to also use non-EASA (FAA,...) options HomeWidget::HomeWidget(QWidget *parent) : QWidget(parent), ui(new Ui::HomeWidget) { ui->setupUi(this); - today = QDate::currentDate(); - ftlWarningThreshold = Settings::read(Settings::UserData::FtlWarningThreshold).toDouble(); - currWarningThreshold = Settings::read(Settings::UserData::CurrWarningThreshold).toInt(); - auto logo = QPixmap(OPL::Assets::LOGO); + + const auto logo = QPixmap(OPL::Assets::LOGO); ui->logoLabel->setPixmap(logo); ui->welcomeLabel->setText(tr("Welcome to openPilotLog, %1!").arg(getLogbookOwnerName())); - - limitationDisplayLabels = { - ui->TakeOffDisplayLabel, ui->LandingsDisplayLabel, - ui->FlightTime28dDisplayLabel, ui->FlightTimeCalYearDisplayLabel, - ui->FlightTime12mDisplayLabel - }; - - LOG << "Filling Home Widget..."; fillTotals(); - fillSelectedCurrencies(); - fillLimitations(); + fillCurrencies(); QObject::connect(DB, &OPL::Database::dataBaseUpdated, this, &HomeWidget::onPilotsDatabaseChanged); @@ -66,23 +43,20 @@ HomeWidget::~HomeWidget() delete ui; } -void HomeWidget::refresh() +void HomeWidget::fillTotals() { - LOG << "Updating HomeWidget..."; - const auto label_list = this->findChildren(); - for (const auto label : label_list) - label->setVisible(true); - for (const auto &label : qAsConst(limitationDisplayLabels)) - label->setStyleSheet(QString()); + auto tw = new TotalsWidget(TotalsWidget::TotalTimeWidget, this); + ui->tabWidget->insertTab(0, tw, tr("Totals")); +} - fillTotals(); - fillSelectedCurrencies(); - fillLimitations(); +void HomeWidget::fillCurrencies() +{ + auto cw = new CurrencyWidget(this); + ui->tabWidget->insertTab(1, cw, tr("Currencies")); } void HomeWidget::onPilotsDatabaseChanged(const OPL::DbTable table) { - // maybe logbook owner name has changed, redraw if (table == OPL::DbTable::Pilots) ui->welcomeLabel->setText(tr("Welcome to openPilotLog, %1!").arg(getLogbookOwnerName())); } @@ -94,144 +68,12 @@ void HomeWidget::changeEvent(QEvent *event) ui->retranslateUi(this); } -/*! - * \brief HomeWidget::fillTotals Retreives a Database Summary of Total Flight Time via the OPL::Statistics::totals - * function and parses the return to fill out the QLineEdits. - */ -void HomeWidget::fillTotals() -{ - auto tw = new TotalsWidget(TotalsWidget::TotalTimeWidget, this); - ui->totalsStackedWidget->addWidget(tw); - ui->totalsStackedWidget->setCurrentWidget(tw); -} - -void HomeWidget::fillCurrency(OPL::CurrencyName currency_name, QLabel* display_label) -{ - const auto currency_entry = DB->getCurrencyEntry(static_cast(currency_name)); - - if (currency_name == OPL::CurrencyName::Custom1) { - ui->currCustom1Label->setText(currency_entry.getData().value(OPL::CurrencyEntry::CURRENCYNAME).toString()); - } else if (currency_name == OPL::CurrencyName::Custom2) { - ui->currCustom2Label->setText(currency_entry.getData().value(OPL::CurrencyEntry::CURRENCYNAME).toString()); - } - - if (currency_entry.isValid()) { - const auto currency_date = QDate::fromString(currency_entry.getData().value( - OPL::CurrencyEntry::EXPIRYDATE).toString(), - Qt::ISODate); - display_label->setText(currency_date.toString(Qt::TextDate)); - setLabelColour(display_label, Colour::None); - - if (today >= currency_date) { // is expired - setLabelColour(display_label, Colour::Red); - return; - } else if (today.addDays(currWarningThreshold) >=currency_date) { // expires less than days from current Date - - setLabelColour(display_label, Colour::Orange); - } - } else { - display_label->setText(tr("Invalid Date")); - } -} - -/*! - * \brief HomeWidget::fillSelectedCurrencies Checks whether a currency is selected and - * retreives and displays relevant data. - */ -void HomeWidget::fillSelectedCurrencies() -{ - fillCurrencyTakeOffLanding(); - - Settings::read(Settings::UserData::ShowLicCurrency).toBool() ? - fillCurrency(OPL::CurrencyName::Licence, ui->currLicDisplayLabel) - : hideLabels(ui->currLicLabel, ui->currLicDisplayLabel); - Settings::read(Settings::UserData::ShowTrCurrency).toBool() ? - fillCurrency(OPL::CurrencyName::TypeRating, ui->currTrDisplayLabel) - : hideLabels(ui->currTrLabel, ui->currTrDisplayLabel); - Settings::read(Settings::UserData::ShowLckCurrency).toBool() ? - fillCurrency(OPL::CurrencyName::LineCheck, ui->currLckDisplayLabel) - : hideLabels(ui->currLckLabel, ui->currLckDisplayLabel); - Settings::read(Settings::UserData::ShowMedCurrency).toBool() ? - fillCurrency(OPL::CurrencyName::Medical, ui->currMedDisplayLabel) - : hideLabels(ui->currMedLabel, ui->currMedDisplayLabel); - Settings::read(Settings::UserData::ShowCustom1Currency).toBool() ? - fillCurrency(OPL::CurrencyName::Custom1, ui->currCustom1DisplayLabel) - : hideLabels(ui->currCustom1Label, ui->currCustom1DisplayLabel); - Settings::read(Settings::UserData::ShowCustom1Currency).toBool() ? - fillCurrency(OPL::CurrencyName::Custom1, ui->currCustom1DisplayLabel) - : hideLabels(ui->currCustom1Label, ui->currCustom1DisplayLabel); - Settings::read(Settings::UserData::ShowCustom2Currency).toBool() ? - fillCurrency(OPL::CurrencyName::Custom2, ui->currCustom2DisplayLabel) - : hideLabels(ui->currCustom2Label, ui->currCustom2DisplayLabel); -} - -/*! - * \brief HomeWidget::fillCurrencyTakeOffLanding Uses OPL::Statistics::countTakeOffLandings to determine - * the amount of Take-Offs and Landings in the last 90 days and displays data and notifications - * as required. - */ -void HomeWidget::fillCurrencyTakeOffLanding() -{ - const auto takeoff_landings = OPL::Statistics::countTakeOffLanding(); - if(takeoff_landings.isEmpty()) - return; - - ui->TakeOffDisplayLabel->setText(takeoff_landings[0].toString()); - if (takeoff_landings[0].toUInt() < 3) - setLabelColour(ui->TakeOffDisplayLabel, Colour::Red); - ui->LandingsDisplayLabel->setText(takeoff_landings[1].toString()); - if (takeoff_landings[1].toUInt() < 3) - setLabelColour(ui->LandingsDisplayLabel, Colour::Red); - - if (Settings::read(Settings::UserData::ShowToLgdCurrency).toBool()) { - QDate expiration_date = OPL::Statistics::currencyTakeOffLandingExpiry(); - if (expiration_date <= QDate::currentDate()) - setLabelColour(ui->currToLdgDisplayLabel, Colour::Red); - ui->currToLdgDisplayLabel->setText(expiration_date.toString(Qt::TextDate)); - } else { - ui->currToLdgLabel->hide(); - ui->currToLdgDisplayLabel->hide(); - } -} - -/*! - * \brief HomeWidget::fillLimitations Queries OPL::Statistics to obtain information regarding cumulative - * Flight Times and Calculates and Notifies about approaching Flight Time Limitations - */ -void HomeWidget::fillLimitations() -{ - int minutes = OPL::Statistics::totalTime(OPL::Statistics::TimeFrame::Rolling28Days); - ui->FlightTime28dDisplayLabel->setText(OPL::Time(minutes).toString()); - if (minutes >= ROLLING_28_DAYS) { - setLabelColour(ui->FlightTime28dDisplayLabel, Colour::Red); - } else if (minutes >= ROLLING_28_DAYS * ftlWarningThreshold) { - setLabelColour(ui->FlightTime28dDisplayLabel, Colour::Orange); - } - - minutes = OPL::Statistics::totalTime(OPL::Statistics::TimeFrame::Rolling12Months); - ui->FlightTime12mDisplayLabel->setText(OPL::Time(minutes).toString()); - if (minutes >= ROLLING_12_MONTHS) { - setLabelColour(ui->FlightTime12mDisplayLabel, Colour::Red); - } else if (minutes >= ROLLING_12_MONTHS * ftlWarningThreshold) { - setLabelColour(ui->FlightTime12mDisplayLabel, Colour::Orange); - } - - minutes = OPL::Statistics::totalTime(OPL::Statistics::TimeFrame::CalendarYear); - ui->FlightTimeCalYearDisplayLabel->setText(OPL::Time(minutes).toString()); - if (minutes >= CALENDAR_YEAR) { - setLabelColour(ui->FlightTimeCalYearDisplayLabel, Colour::Red); - } else if (minutes >= CALENDAR_YEAR * ftlWarningThreshold) { - setLabelColour(ui->FlightTimeCalYearDisplayLabel, Colour::Orange); - } -} - -const QString HomeWidget::getLogbookOwnerName() +const QString HomeWidget::getLogbookOwnerName() const { OPL::PilotEntry owner = DB->getLogbookOwner(); QString name = owner.getFirstName(); if(name.isEmpty()) { name = owner.getLastName(); } - DEB << "owner name: " << name; return name; } diff --git a/src/gui/widgets/homewidget.h b/src/gui/widgets/homewidget.h index 072d5116..e191572c 100644 --- a/src/gui/widgets/homewidget.h +++ b/src/gui/widgets/homewidget.h @@ -18,12 +18,12 @@ #ifndef HOMEWIDGET_H #define HOMEWIDGET_H +#include "src/opl.h" #include #include #include #include #include -#include "src/database/database.h" namespace Ui { class HomeWidget; @@ -62,10 +62,7 @@ class HomeWidget : public QWidget double ftlWarningThreshold; void fillTotals(); - void fillSelectedCurrencies(); - void fillCurrencyTakeOffLanding(); - void fillCurrency(OPL::CurrencyName currency_name, QLabel *display_label); - void fillLimitations(); + void fillCurrencies(); enum class Colour {Red, Orange, None}; inline void setLabelColour(QLabel* label, Colour colour) @@ -94,11 +91,9 @@ class HomeWidget : public QWidget /*! * \brief Retreives the users first name from the database. */ - const QString getLogbookOwnerName(); + const QString getLogbookOwnerName() const; public slots: - void refresh(); - void onPilotsDatabaseChanged(const OPL::DbTable table); protected: diff --git a/src/gui/widgets/homewidget.ui b/src/gui/widgets/homewidget.ui index ad35a565..7602d047 100644 --- a/src/gui/widgets/homewidget.ui +++ b/src/gui/widgets/homewidget.ui @@ -6,396 +6,24 @@ 0 0 - 1087 - 777 + 670 + 442 Form - - - - - - - Flight Time (last 28 days) - - - - - - - Qt::LeftToRight - - - 0 - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - Flight Time (this calendar year) - - - - - - - Qt::LeftToRight - - - 0 - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - Flight Time (last 12 calendar months) - - - - - - - Qt::LeftToRight - - - 0 - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - - - - - Take offs (last 90 days) - - - - - - - - true - - - - Qt::LeftToRight - - - 0 - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - Landings (last 90 days) - - - - - - - - true - - - - Qt::LeftToRight - - - 0 - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - - false - - - - Take-Off / Landing - - - - - - - - true - - - - - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - - false - - - - Licence - - - - - - - - true - - - - - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - - false - - - - Type Rating - - - - - - - - true - - - - - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - - false - - - - Line Check - - - - - - - - true - - - - - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - - false - - - - Medical - - - - - - - - true - - - - - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - - false - - - - Custom1 - - - - - - - - true - - - - - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - - false - - - - Custom2 - - - - - - - - true - - - - - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - - - Qt::Horizontal - - - - - - - - true - - - - Currency - - - Qt::AlignBottom|Qt::AlignHCenter - - - - - - - Qt::Horizontal - - - - - - - - true - - - - Your Totals - - - Qt::AlignBottom|Qt::AlignHCenter - - - - - - - Qt::Horizontal - - - + - + [openPilotLog Logo] Qt::AlignCenter - - - - Qt::Horizontal - - - - - - - Qt::Horizontal - - - @@ -406,27 +34,13 @@ - - - - - true - - - - Limitations - - - Qt::AlignBottom|Qt::AlignHCenter + + + + -1 - - - - - - diff --git a/src/gui/widgets/logbooktableeditwidget.cpp b/src/gui/widgets/logbooktableeditwidget.cpp new file mode 100644 index 00000000..151b4fd8 --- /dev/null +++ b/src/gui/widgets/logbooktableeditwidget.cpp @@ -0,0 +1,188 @@ +#include "logbooktableeditwidget.h" +#include "src/classes/settings.h" +#include "src/classes/styleddatedelegate.h" +#include "src/classes/styledpilotdelegate.h" +#include "src/classes/styledtimedelegate.h" +#include "src/classes/styledtypedelegate.h" +#include "src/database/database.h" +#include "src/database/views/logbookviewinfo.h" +#include "src/gui/dialogues/newflightdialog.h" +#include "src/gui/dialogues/newsimdialog.h" + +LogbookTableEditWidget::LogbookTableEditWidget(QWidget *parent) + : TableEditWidget(Vertical, parent) +{} + + +// TableEditWidget implementation + +void LogbookTableEditWidget::setupModelAndView() +{ + m_logbookView = Settings::getLogbookView(); + m_model = new QSqlTableModel(this, DB->database()); + m_model->setTable(OPL::GLOBALS->getViewIdentifier(m_logbookView)); + m_model->select(); + + const auto headers = OPL::LogbookViewInfo::getTableHeaders(m_logbookView); + for(int i = 0; i < headers.size(); i++) { + m_model->setHeaderData(i, Qt::Horizontal, headers[i]); + } + + m_view->setModel(m_model); + m_view->setSelectionMode(QAbstractItemView::SingleSelection); + m_view->setSelectionBehavior(QAbstractItemView::SelectRows); + m_view->setEditTriggers(QAbstractItemView::NoEditTriggers); + m_view->horizontalHeader()->setStretchLastSection(QHeaderView::Stretch); + m_view->verticalHeader()->hide(); + m_view->setAlternatingRowColors(true); + m_view->hideColumn(COL_ROWID); + + setupDelegates(); + m_view->resizeColumnsToContents(); +} + +void LogbookTableEditWidget::setupUI() +{ + TableEditWidget::setupUI(); + m_addNewEntryPushButton->setText(tr("Add new Flight")); + m_deleteEntryPushButton->setText(tr("Delete selected Entry")); + m_filterWidget->hide(); + m_stackedWidget->hide(); + + m_format = Settings::getDisplayFormat(); +} + +QString LogbookTableEditWidget::deleteErrorString(int rowId) +{ + return tr("
    Unable to delete.

    The following error has ocurred: %1" + ).arg(DB->lastError.text()); +} + +QString LogbookTableEditWidget::confirmDeleteString(int rowId) +{ + if(rowId > 0) { + const auto selectedEntry = DB->getFlightEntry(rowId); + return tr("The following flight will be deleted:

    " + "%1


    " + "Deleting flights is irreversible.
    Do you want to proceed?" + ).arg(selectedEntry.getFlightSummary()); + + } + + return tr("Deleting entries is irreversible.
    Do you want to proceed?"); +} + +EntryEditDialog *LogbookTableEditWidget::getEntryEditDialog(QWidget *parent) +{ + return new NewFlightDialog(parent); +} + +void LogbookTableEditWidget::filterTextChanged(const QString &filterString) +{} + +void LogbookTableEditWidget::editEntryRequested(const QModelIndex &selectedIndex) +{ + showEditWidget(); + const auto idx = m_view->selectionModel()->currentIndex(); + const auto rowId = m_model->index(idx.row(), 0).data().toInt(); + if(rowId > 0) { + auto nfd = NewFlightDialog(rowId, this); + m_stackedWidget->addWidget(&nfd); + m_stackedWidget->setCurrentWidget(&nfd); + nfd.exec(); + } else { + auto nsd = NewSimDialog(rowId * -1, this); + m_stackedWidget->addWidget(&nsd); + m_stackedWidget->setCurrentWidget(&nsd); + nsd.exec(); + } + hideEditWidget(); +} + +void LogbookTableEditWidget::deleteEntryRequested() +{ + const QModelIndex selectedIndex = m_view->selectionModel()->currentIndex(); + if(!selectedIndex.isValid()) { + WARN(tr("No entry selected.")); + return; + } + m_stackedWidget->hide(); + + int rowId = m_model->index(selectedIndex.row(), 0).data().toInt(); + m_view->selectionModel()->reset(); + + // get user confirmation + QMessageBox confirm(this); + confirm.setStandardButtons(QMessageBox::Yes | QMessageBox::No); + confirm.setDefaultButton(QMessageBox::No); + confirm.setIcon(QMessageBox::Question); + confirm.setWindowTitle(tr("Confirm Deletion")); + + confirm.setText(confirmDeleteString(rowId)); + if (confirm.exec() == QMessageBox::Yes) { + if(rowId > 0) { + const auto selectedEntry = DB->getFlightEntry(rowId); + if(!DB->remove(selectedEntry)) + WARN(deleteErrorString(rowId)); + } else { + const auto selectedEntry = DB->getSimEntry(rowId * - 1); + if(!DB->remove(selectedEntry)) + WARN(deleteErrorString(rowId)); + } + } +} + +// private implementations + +void LogbookTableEditWidget::addSimulatorEntryRequested() +{ + showEditWidget(); + + auto nsd = NewSimDialog(this); + m_stackedWidget->addWidget(&nsd); + m_stackedWidget->setCurrentWidget(&nsd); + nsd.exec(); + + hideEditWidget(); +} + +void LogbookTableEditWidget::viewSelectionChanged(SettingsWidget::SettingSignal widget) +{ + for(auto i = m_defaultDelegates.constBegin(); i != m_defaultDelegates.constEnd(); i++) { + m_view->setItemDelegateForColumn(i.key(), i.value()); + // should probably delete the old custom delegate, Qt docs say the + // view does not take ownership of the delegates so they might be leaking + // https://doc.qt.io/qt-6/qabstractitemdelegate.html + } + + if(widget == SettingsWidget::SettingSignal::LogbookWidget) + setupModelAndView(); +} + +void LogbookTableEditWidget::setupDelegates() +{ + // minutes to hh:mm + const auto timeDelegate = new StyledTimeDelegate(m_format, m_model); + for(const auto col : OPL::LogbookViewInfo::getTimeColumns(m_logbookView)) { + m_defaultDelegates.insert(col, m_view->itemDelegateForColumn(col)); + m_view->setItemDelegateForColumn(col, timeDelegate); + } + + // julian day to Date Format + const int dateCol = OPL::LogbookViewInfo::getDateColumn(m_logbookView); + const auto dateDelegate = new StyledDateDelegate(Settings::getDisplayFormat(), m_model); + m_defaultDelegates.insert(dateCol, m_view->itemDelegateForColumn(dateCol)); + m_view->setItemDelegateForColumn(dateCol, dateDelegate); + + // pilot_id to names + const int pilCol = OPL::LogbookViewInfo::getPicColumn(m_logbookView); + const auto pilotDelegate = new StyledPilotDelegate(m_model); + m_defaultDelegates.insert(pilCol, m_view->itemDelegateForColumn(pilCol)); + m_view->setItemDelegateForColumn(pilCol, pilotDelegate); + + // tail_id to aircraft type and registration + const int typeCol = OPL::LogbookViewInfo::getTypeColumn(m_logbookView); + const auto typeDelegate = new StyledTypeDelegate(m_model); + m_defaultDelegates.insert(typeCol, m_view->itemDelegateForColumn(typeCol)); + m_view->setItemDelegateForColumn(typeCol, typeDelegate); +} diff --git a/src/gui/widgets/logbooktableeditwidget.h b/src/gui/widgets/logbooktableeditwidget.h new file mode 100644 index 00000000..365ef0f0 --- /dev/null +++ b/src/gui/widgets/logbooktableeditwidget.h @@ -0,0 +1,67 @@ +#ifndef LOGBOOKTABLEEDITWIDGET_H +#define LOGBOOKTABLEEDITWIDGET_H + +#include "settingswidget.h" +#include "tableeditwidget.h" +#include "src/opl.h" +#include + +/*! + * \brief The LogbookTableEditWidget allows editing of logbook entries. + * + * \details This widget differs from the other TableEditWidget implementations in that + * instead of a table, it uses a [view](https://sqlite.org/lang_createview.html) as the source of the display model. + * + * The user can select a view from a list of available views in the SettingsWidget. + * + * Some of the selectable views aggregate data from the flights and simulators table. In those views, flight + * entries have positive row id's whereas the simulators have inverse (negative) row id's. This necessitates + * checking the row id's value before deciding on the apropriate EntryEditDialog. + */ +class LogbookTableEditWidget : public TableEditWidget +{ + Q_OBJECT +public: + LogbookTableEditWidget() = delete; + explicit LogbookTableEditWidget(QWidget *parent = nullptr); + +private: + OPL::LogbookView m_logbookView; + OPL::DateTimeFormat m_format; + + /*! + * \brief Set up the QStyledItemDelegate instances that transform the database values to user-readable values. + */ + void setupDelegates(); + + static constexpr int COL_ROWID = 0; + + // keep track of default and custom delegates set on certain columns + QHash m_defaultDelegates; + + // TableEditWidget interface +public: + virtual void setupModelAndView() override; + virtual void setupUI() override; + virtual QString deleteErrorString(int rowId) override; + virtual QString confirmDeleteString(int rowId) override; + virtual EntryEditDialog *getEntryEditDialog(QWidget *parent) override; + +public slots: + void viewSelectionChanged(SettingsWidget::SettingSignal widget); + +public slots: + virtual void filterTextChanged(const QString &filterString) override; + + virtual void editEntryRequested(const QModelIndex &selectedIndex) override; + virtual void deleteEntryRequested() override; + + /*! + * \brief add a new Simulator Entry to the datbase + * \details The Primary Entry handled by the LogbookTableEditWidget are Flights, which are stored in the flights table. + * However, this widget also handles adding simulator tables which are stored in the simulators table. + */ + void addSimulatorEntryRequested(); +}; + +#endif // LOGBOOKTABLEEDITWIDGET_H diff --git a/src/gui/widgets/logbookwidget.cpp b/src/gui/widgets/logbookwidget.cpp deleted file mode 100644 index 934df9e8..00000000 --- a/src/gui/widgets/logbookwidget.cpp +++ /dev/null @@ -1,322 +0,0 @@ -/* - *openPilotLog - A FOSS Pilot Logbook Application - *Copyright (C) 2020-2023 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 . - */ -#include "src/classes/time.h" -#include "src/database/database.h" -#include "logbookwidget.h" -#include "ui_logbookwidget.h" -#include "src/database/row.h" -#include "src/database/database.h" -#include "src/classes/settings.h" -#include "src/gui/dialogues/newflightdialog.h" -#include "src/gui/dialogues/newsimdialog.h" - -LogbookWidget::LogbookWidget(QWidget *parent) : - QWidget(parent), - ui(new Ui::LogbookWidget) -{ - ui->setupUi(this); - - OPL::GLOBALS->fillViewNamesComboBox(ui->viewsComboBox); - - //customContextMenu for tablewidget - menu = new QMenu(this); - menu->addAction(ui->actionEdit_Flight); - menu->addAction(ui->actionDelete_Flight); - - // Initalise the display Model and view - displayModel = new QSqlTableModel(this); - view = ui->tableView; - - setupModelAndView(Settings::read(Settings::Main::LogbookView).toInt()); - connectSignalsAndSlots(); - -} - -LogbookWidget::~LogbookWidget() -{ - delete ui; -} - -/* - * Functions - */ - -/*! - * \brief LogbookWidget::setupModelAndView configures the QTableView and populates the model with data - * according to the current view. - * \param view_id - retreived from Settings::Main::LogbookView - */ -void LogbookWidget::setupModelAndView(int view_id) -{ - displayModel->setTable(OPL::GLOBALS->getViewIdentifier(OPL::DbViewName(view_id))); - displayModel->select(); - - view->setModel(displayModel); - view->setSelectionBehavior(QAbstractItemView::SelectRows); - view->setSelectionMode(QAbstractItemView::ExtendedSelection); - view->setEditTriggers(QAbstractItemView::NoEditTriggers); - view->setContextMenuPolicy(Qt::CustomContextMenu); - view->resizeColumnsToContents(); - view->horizontalHeader()->setStretchLastSection(QHeaderView::Stretch); - view->verticalHeader()->hide(); - view->setAlternatingRowColors(true); - view->hideColumn(0); - view->show(); -} - -void LogbookWidget::connectSignalsAndSlots() -{ - selectionModel = view->selectionModel(); - QObject::connect(view->selectionModel(), &QItemSelectionModel::selectionChanged, - this, &LogbookWidget::flightsTableView_selectionChanged); -} - -const QString LogbookWidget::getFlightSummary(const OPL::FlightEntry &flight) const -{ - if(!flight.isValid()) - return QString(); - - auto tableData = flight.getData(); - QString flight_summary; - auto space = QLatin1Char(' '); - flight_summary.append(tableData.value(OPL::FlightEntry::DOFT).toString() + space); - flight_summary.append(tableData.value(OPL::FlightEntry::DEPT).toString() + space); - flight_summary.append(OPL::Time(tableData.value(OPL::FlightEntry::TOFB).toInt()).toString() - + space); - flight_summary.append(OPL::Time(tableData.value(OPL::FlightEntry::TONB).toInt()).toString() - + space); - flight_summary.append(tableData.value(OPL::FlightEntry::DEST).toString()); - - return flight_summary; -} - -void LogbookWidget::changeEvent(QEvent *event) -{ - if (event != nullptr) - if(event->type() == QEvent::LanguageChange) - ui->retranslateUi(this); -} - -/*! - * \brief LogbookWidget::flightsTableView_selectionChanged saves the selected row(s) - * to the selectedFlights member variable. - */ -void LogbookWidget::flightsTableView_selectionChanged() -{ - selectedEntries.clear(); - for (const auto& row : selectionModel->selectedRows()) { - selectedEntries.append(row.data().toInt()); - DEB << "Selected Flight(s) with ID: " << selectedEntries; - } - if (selectedEntries.length() == 1) { - if (isFlight(selectedEntries.first())) - on_actionEdit_Flight_triggered(); - else - on_actionEdit_Sim_triggered(); - } -} - -/*! - * \brief If a row is selected, query information - * about the affected row(s) and ask the user to confirm deletion. - */ -void LogbookWidget::on_actionDelete_Flight_triggered() -{ - DEB << "Flights selected: " << selectedEntries.length(); - if (selectedEntries.length() == 0) { - WARN(tr("
    No flight selected.
    ")); - return; - } else if (selectedEntries.length() > 0 && selectedEntries.length() <= 10) { - QVector flights_list; - - for (const auto &flight_id : qAsConst(selectedEntries)) { - flights_list.append(DB->getFlightEntry(flight_id)); - } - - QString flights_list_string; - - for (auto &flight : flights_list) { - flights_list_string.append(getFlightSummary(flight)); - flights_list_string.append(QStringLiteral("    
    ")); - } - - QMessageBox confirm(this); - confirm.setStandardButtons(QMessageBox::Yes | QMessageBox::No); - confirm.setDefaultButton(QMessageBox::No); - confirm.setIcon(QMessageBox::Question); - confirm.setWindowTitle("Delete Flight"); - confirm.setText(tr("The following flight(s) will be deleted:

    " - "%1
    " - "Deleting flights is irreversible.
    Do you want to proceed?" - ).arg(flights_list_string)); - if (confirm.exec() == QMessageBox::Yes) { - for (auto& flight : flights_list) { - DEB << "Deleting flight: " << flight; - if(!DB->remove(flight)) { - WARN(tr("
    Unable to delete.

    The following error has ocurred: %1" - ).arg(DB->lastError.text())); - return; - } - } - INFO(tr("%1 flights have been deleted successfully." - ).arg(QString::number(selectedEntries.length()))); - displayModel->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 Flight"); - confirm.setText(tr("You have selected %1 flights.

    " - "Deleting flights is irreversible.

    " - "Are you sure you want to proceed?" - ).arg(QString::number(selectedEntries.length()))); - if(confirm.exec() == QMessageBox::Yes) { - if (!DB->removeMany(OPL::DbTable::Flights, selectedEntries)) { - WARN(tr("Unable to delete. No changes have been made to the database. The following error has ocurred:

    %1").arg(DB->lastError.text())); - return; - } - INFO(tr("%1 flights have been deleted successfully." - ).arg(QString::number(selectedEntries.length()))); - displayModel->select(); - } - displayModel->select(); - } -} - - -void LogbookWidget::on_tableView_customContextMenuRequested(const QPoint &pos) -{ - menu->popup(ui->tableView->viewport()->mapToGlobal(pos)); -} - -void LogbookWidget::on_tableView_doubleClicked() -{ - on_actionEdit_Flight_triggered(); -} - -void LogbookWidget::on_flightSearchComboBox_currentIndexChanged(int) -{ - //emit ui->showAllButton->clicked(); -} - -/*! - * \brief LogbookWidget::refresh Refreshes the view to reflect changes in the database. - */ -void LogbookWidget::refresh() -{ - displayModel->select(); - view->resizeColumnsToContents(); -} - -void LogbookWidget::onLogbookWidget_viewSelectionChanged(SettingsWidget::SettingSignal signal) -{ - if (signal == SettingsWidget::SettingSignal::LogbookWidget) - setupModelAndView(Settings::read(Settings::Main::LogbookView).toInt()); -} - -//void LogbookWidget::on_showAllButton_clicked() -//{ -// ui->flightSearchLlineEdit->setText(QString()); -// displayModel->setFilter(QString()); -// displayModel->select(); -//} - -/*! - * \brief LogbookWidget::on_flightSearchLlineEdit_textChanged applies a filter to the - * display model allowing the user to search for flights by specified elements (date, aircraft, - * Pilot Name) - */ -void LogbookWidget::on_flightSearchLlineEdit_textChanged(const QString &arg1) -{ - if(arg1.length() == 0) { - displayModel->setFilter(""); - displayModel->select(); - return; - } - - if (ui->flightSearchComboBox->currentIndex() < 3) { - displayModel->setFilter(FILTER_MAP.value(ui->flightSearchComboBox->currentIndex()) - + arg1 + QLatin1String("%\"")); - return; - } else if (ui->flightSearchComboBox->currentIndex() == 3) { // registration - displayModel->setFilter(FILTER_MAP.value(ui->flightSearchComboBox->currentIndex()) - + arg1 + QLatin1String("%\"")); - return; - } else if (ui->flightSearchComboBox->currentIndex() == 4) { // Name Pic - displayModel->setFilter(FILTER_MAP.value(ui->flightSearchComboBox->currentIndex()) - + arg1 + QLatin1String("%\"")); - return; - } -} - -/*! - * \brief LogbookWidget::repopulateModel (public slot) - cleanly re-populates the model to cater for a change - * to the database connection (for example, when a backup is created or restored) - */ -void LogbookWidget::repopulateModel() -{ - // unset the current model and delete it to avoid leak - view->setModel(nullptr); - delete displayModel; - // create a new model and populate it - displayModel = new QSqlTableModel(this); - setupModelAndView(Settings::read(Settings::Main::LogbookView).toInt()); - connectSignalsAndSlots(); -} - -void LogbookWidget::on_viewsComboBox_currentIndexChanged(int index) -{ - setupModelAndView(index); -} - -void LogbookWidget::on_actionEdit_Flight_triggered() -{ - if(selectedEntries.length() == 1){ - NewFlightDialog nff(selectedEntries.first(), this); - ui->stackedWidget->addWidget(&nff); - ui->stackedWidget->setCurrentWidget(&nff); - nff.setWindowFlag(Qt::Widget); - nff.exec(); - displayModel->select(); - } else if (selectedEntries.isEmpty()) { - WARN(tr("
    No flight selected.
    ")); - } else { - WARN(tr("
    More than one flight selected." - "

    Editing multiple entries is not yet supported.")); - } -} - -void LogbookWidget::on_actionEdit_Sim_triggered() -{ - if (selectedEntries.length() == 1) { - NewSimDialog nsd((selectedEntries.first() * -1), this); // simulator entries have inverse row ID's in the model - ui->stackedWidget->addWidget(&nsd); - ui->stackedWidget->setCurrentWidget(&nsd); - nsd.setWindowFlag(Qt::Widget); - nsd.exec(); - displayModel->select(); - } else if (selectedEntries.isEmpty()) { - WARN(tr("
    No flight selected.
    ")); - } else { - WARN(tr("
    More than one flight selected." - "

    Editing multiple entries is not yet supported.")); - } -} - diff --git a/src/gui/widgets/logbookwidget.h b/src/gui/widgets/logbookwidget.h deleted file mode 100644 index 4bdaaaae..00000000 --- a/src/gui/widgets/logbookwidget.h +++ /dev/null @@ -1,116 +0,0 @@ -/* - *openPilotLog - A FOSS Pilot Logbook Application - *Copyright (C) 2020-2023 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 . - */ -#ifndef LOGBOOKWIDGET_H -#define LOGBOOKWIDGET_H - -#include -#include -#include -#include -#include -#include -#include -#include "src/database/flightentry.h" -#include "src/gui/widgets/settingswidget.h" - -namespace Ui { -class LogbookWidget; -} - -/*! - * \brief The LogbookWidget displays data from the database in a QSqlTableView fed by a QSqlQuery Model - * - * \details The LogbookWidget is the primary display interface for flights logged in the database. It fetches and stores - * flight data from the database via a QSqlQueryModel and displays it in a QTableView. With the way the flight data is - * written in the database, it would not be human-readable, so some processing is done on the database side to present - * a nicely formatted, human-readable display. This is achieved by means of a [SQL View](https://sqlite.org/lang_createview.html). - * - * The user can select a view from a list of available views in the SettingsWidget. - * - */ -class LogbookWidget : public QWidget -{ - Q_OBJECT - -public: - explicit LogbookWidget(QWidget *parent = nullptr); - ~LogbookWidget(); - -private slots: - void flightsTableView_selectionChanged(); - void on_tableView_customContextMenuRequested(const QPoint &pos); - void on_actionDelete_Flight_triggered(); - - void on_tableView_doubleClicked(); - void on_flightSearchLlineEdit_textChanged(const QString &arg1); - void on_flightSearchComboBox_currentIndexChanged(int); - - void on_viewsComboBox_currentIndexChanged(int index); - - void on_actionEdit_Flight_triggered(); - void on_actionEdit_Sim_triggered(); - -public slots: - void refresh(); - void onLogbookWidget_viewSelectionChanged(SettingsWidget::SettingSignal signal); - void repopulateModel(); - -private: - Ui::LogbookWidget *ui; - - QTableView* view; - - QSqlTableModel* displayModel; - - QItemSelectionModel* selectionModel; - - QMenu* menu; - - QList selectedEntries; - - void setupModelAndView(int view_id); - void connectSignalsAndSlots(); - - const QString getFlightSummary(const OPL::FlightEntry &flight) const; - - /*! - * \brief isFlight Determines whether an entry shown in a view is a Flight or a Simulator. - * \param model_row_id the row id in the QSqlTableModel used for displaying - * \details In the composite views (SQL UNION) with Simulators included, the row_id of the - * simulator entries is inverted to a negative value. A positive row id is thus a row id from - * the flights table, whereas a negative rowid is a row id from the simulators table. - */ - inline bool isFlight(int model_row_id) { return model_row_id > 0; } - - const static inline QHash FILTER_MAP = { - {0, QStringLiteral("Date LIKE \"%")}, - {1, QStringLiteral("Dept LIKE \"%")}, - {2, QStringLiteral("Dest LIKE \"%")}, - {3, QStringLiteral("Registration LIKE \"%")}, - {4, QStringLiteral("\"Name PIC\" LIKE \"%")} - }; - const static inline QRegularExpression NON_WORD_CHAR = QRegularExpression("\\W"); - -protected: - /*! - * \brief Handles change events, like updating the UI to new localisation - */ - void changeEvent(QEvent* event) override; -}; - -#endif // LOGBOOKWIDGET_H diff --git a/src/gui/widgets/logbookwidget.ui b/src/gui/widgets/logbookwidget.ui deleted file mode 100644 index 7733c6be..00000000 --- a/src/gui/widgets/logbookwidget.ui +++ /dev/null @@ -1,169 +0,0 @@ - - - LogbookWidget - - - - 0 - 0 - 1280 - 720 - - - - Form - - - - - - - - - - - - QAbstractItemView::SingleSelection - - - QAbstractItemView::SelectRows - - - - - - - - - - - - 200 - 0 - - - - Search Flight - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - <html><head/><body><p>Enter the searchterm you want to filter your flights by.</p><p>For dates, make sure to use the format YYYY-MM-DD</p></body></html> - - - <html><head/><body><p><br/></p></body></html> - - - - - - - - 200 - 0 - - - - Search by - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - - Date of Flight - - - - - Departure Airport - - - - - Destination Airport - - - - - Aircraft Registration - - - - - Pilot Name - - - - - - - - Select a Flight from the list to show or edit details. - - - Qt::AlignCenter - - - - - - - - - - - - - - - 284 - 410 - 200 - 16 - - - - - 200 - 0 - - - - - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - Edit Flight - - - - - Delete Flight - - - - - Edit_Sim - - - Edit an existing Simulator Entry - - - - - - diff --git a/src/gui/widgets/pilottableeditwidget.cpp b/src/gui/widgets/pilottableeditwidget.cpp new file mode 100644 index 00000000..0bf9fa5e --- /dev/null +++ b/src/gui/widgets/pilottableeditwidget.cpp @@ -0,0 +1,112 @@ +#include "pilottableeditwidget.h" +#include "src/database/database.h" +#include "src/gui/dialogues/entryeditdialog.h" +#include "src/gui/dialogues/newpilotdialog.h" +#include "src/opl.h" +#include + +PilotTableEditWidget::PilotTableEditWidget(QWidget *parent) + : TableEditWidget(Horizontal, parent) +{} + +void PilotTableEditWidget::setupModelAndView() +{ + m_model = new QSqlTableModel(this, DB->database()); + m_model->setTable(OPL::GLOBALS->getDbTableName(OPL::DbTable::Pilots)); + m_model->select(); + m_model->setHeaderData(COL_LASTNAME, Qt::Horizontal, tr("Last Name")); + m_model->setHeaderData(COL_FIRSTNAME, Qt::Horizontal, tr("First Name")); + m_model->setHeaderData(COL_COMPANY, Qt::Horizontal, tr("Company")); + m_model->setFilter(QStringLiteral("%1 > 1").arg(OPL::PilotEntry::ROWID)); // hide self + + m_view->setModel(m_model); + m_view->setSelectionMode(QAbstractItemView::SingleSelection); + m_view->setSelectionBehavior(QAbstractItemView::SelectRows); + m_view->setEditTriggers(QAbstractItemView::NoEditTriggers); + m_view->horizontalHeader()->setStretchLastSection(QHeaderView::Stretch); + m_view->resizeColumnsToContents(); + m_view->verticalHeader()->hide(); + m_view->setAlternatingRowColors(true); + for(const int i : COLS_TO_HIDE) + m_view->hideColumn(i); + +} + +void PilotTableEditWidget::setupUI() +{ + // the base class does most of the setup + TableEditWidget::setupUI(); + + // only need to set the table specific labels and combo box items + m_addNewEntryPushButton->setText(tr("Add New Pilot")); + m_deleteEntryPushButton->setText(tr("Delete Selected Pilot")); + m_filterSelectionComboBox->addItems(FILTER_COLUMNS); +} + +EntryEditDialog *PilotTableEditWidget::getEntryEditDialog(QWidget *parent) +{ + return new NewPilotDialog(QString(), parent); +} + +QString PilotTableEditWidget::deleteErrorString(int pilotId) +{ + const QList foreign_key_constraints = DB->getForeignKeyConstraints(pilotId, + OPL::DbTable::Pilots); + QList constrained_flights; + for (const auto &row_id : foreign_key_constraints) { + constrained_flights.append(DB->getFlightEntry(row_id)); + } + + // the error is a database error + if (constrained_flights.isEmpty()) { + return(tr("
    Unable to delete.

    The following error has ocurred:
    %1" + ).arg(DB->lastError.text())); + } else { + // the error is a foreign key constraint + QString constrained_flights_string; + for (int i=0; i")); + if (i>10) { + constrained_flights_string.append("
    [...]
    "); + break; + } + } + return(tr("Unable to delete.

    " + "This is most likely the case because a flight exists with the Pilot " + "you are trying to delete as PIC.

    " + "%1 flight(s) with this pilot have been found:


    " + "%2" + "

    You have to change or remove the conflicting flight(s) " + "before removing this pilot from the database.

    " + ).arg(QString::number(constrained_flights.length()), + constrained_flights_string)); + } +} + +QString PilotTableEditWidget::confirmDeleteString(int rowId) +{ + const auto entry = DB->getPilotEntry(rowId); + return tr("You are deleting the following pilot:

    " + "%1, %2

    Are you sure?").arg(entry.getLastName(), entry.getFirstName()); +} + +void PilotTableEditWidget::filterTextChanged(const QString &filterText) +{ + if(filterText.isEmpty()) { + m_model->setFilter(QStringLiteral("%1 > 1").arg(OPL::PilotEntry::ROWID)); // hide self + return; + } + + int i = m_filterSelectionComboBox->currentIndex(); + const QString filter = + QLatin1Char('\"') + + FILTER_COLUMN_NAMES.at(i) + + QLatin1String("\" LIKE '%") + + filterText + + QLatin1String("%' AND ") + + OPL::PilotEntry::ROWID + + QLatin1String(" > 1"); + m_model->setFilter(filter); +} + + diff --git a/src/gui/widgets/pilottableeditwidget.h b/src/gui/widgets/pilottableeditwidget.h new file mode 100644 index 00000000..e45fc4bd --- /dev/null +++ b/src/gui/widgets/pilottableeditwidget.h @@ -0,0 +1,52 @@ +#ifndef PILOTTABLEEDITWIDGET_H +#define PILOTTABLEEDITWIDGET_H + +#include "tableeditwidget.h" +#include "src/database/pilotentry.h" + +class PilotTableEditWidget : public TableEditWidget +{ + Q_OBJECT +public: + PilotTableEditWidget(QWidget *parent = nullptr); + + virtual void setupModelAndView() override; + virtual void setupUI() override; + virtual EntryEditDialog *getEntryEditDialog(QWidget *parent = nullptr) override; + + +private: + static constexpr int COL_ROWID = 0; + static constexpr int COL_LASTNAME = 1; + static constexpr int COL_FIRSTNAME = 2; + static constexpr int COL_COMPANY = 4; + static constexpr int COLS_TO_HIDE[5] = {0, 3, 5, 6, 7}; + + const QString COLUMN_1_NAME = tr("First Name"); + const QString COLUMN_2_NAME = tr("Last Name"); + const QString COLUMN_3_NAME = tr("Company"); + + const QStringList FILTER_COLUMNS = { COLUMN_1_NAME, COLUMN_2_NAME, COLUMN_3_NAME }; + const static inline QStringList FILTER_COLUMN_NAMES = { + OPL::PilotEntry::FIRSTNAME, + OPL::PilotEntry::LASTNAME, + OPL::PilotEntry::COMPANY }; + + /*! + * \brief Informs the user that deleting a Pilot has been unsuccessful + * + * \details Normally, when one of these entries can not be deleted, it is because of + * a [foreign key constraint](https://sqlite.org/foreignkeys.html), meaning that a flight + * is associated with the Pilot that was supposed to be deleted as Pilot-in-command. + * + * This function is used to inform the user and give hints on how to solve the problem. + */ + virtual QString deleteErrorString(int pilotId) override; + + virtual QString confirmDeleteString(int rowId) override; + +private slots: + virtual void filterTextChanged(const QString &filterText) override; +}; + +#endif // PILOTTABLEEDITWIDGET_H diff --git a/src/gui/widgets/settingswidget.cpp b/src/gui/widgets/settingswidget.cpp index 45cf4a21..e65bfc61 100644 --- a/src/gui/widgets/settingswidget.cpp +++ b/src/gui/widgets/settingswidget.cpp @@ -23,7 +23,6 @@ #include "src/classes/settings.h" #include "src/database/database.h" #include "src/opl.h" -#include "src/functions/datetime.h" #include "src/gui/widgets/backupwidget.h" SettingsWidget::SettingsWidget(QWidget *parent) : @@ -36,7 +35,6 @@ SettingsWidget::SettingsWidget(QWidget *parent) : loadBackupWidget(); loadPreviousExperienceWidget(); setupComboBoxes(); - setupDateEdits(); setupValidators(); readSettings(); } @@ -62,41 +60,9 @@ void SettingsWidget::setupComboBoxes(){ OPL::GLOBALS->loadPilotFunctios(ui->functionComboBox); OPL::GLOBALS->fillViewNamesComboBox(ui->logbookViewComboBox); OPL::GLOBALS->fillLanguageComboBox(ui->languageComboBox); - } -} - -void SettingsWidget::setupDateEdits() -{ - // Read Display Format Setting - int date_format_index = Settings::read(Settings::Main::DateFormat).toInt(); - const QString date_format_string = OPL::DateTime::getFormatString( - static_cast(date_format_index)); - const auto date_edits = this->findChildren(); - for (const auto &date_edit : date_edits) { - date_edit->setDisplayFormat(date_format_string); - } - // Fill currencies - const QList> currencies_list = { - {OPL::CurrencyName::Licence, ui->currLicDateEdit}, - {OPL::CurrencyName::TypeRating, ui->currTrDateEdit}, - {OPL::CurrencyName::LineCheck, ui->currLckDateEdit}, - {OPL::CurrencyName::Medical, ui->currMedDateEdit}, - {OPL::CurrencyName::Custom1, ui->currCustom1DateEdit}, - {OPL::CurrencyName::Custom2, ui->currCustom2DateEdit}, - }; - for (const auto &pair : currencies_list) { - const QSignalBlocker signal_blocker(pair.second); - const auto entry = DB->getCurrencyEntry(static_cast(pair.first)); - if (entry.isValid()) { // set date - const auto date = QDate::fromString( - entry.getData().value(OPL::CurrencyEntry::EXPIRYDATE).toString(), - Qt::ISODate); - if(date.isValid()) - pair.second->setDate(date); - } else { // set current date - pair.second->setDate(QDate::currentDate()); - } + // Set up the currency warning threshold spin box + ui->currencyWarningDaysSpinBox->setValue(Settings::getCurrencyWarningThreshold()); } } @@ -135,29 +101,18 @@ void SettingsWidget::readSettings() ui->emailLineEdit->setText(user_data.value(OPL::PilotEntry::EMAIL).toString()); // FLight Logging Tab - ui->functionComboBox->setCurrentIndex(Settings::read(Settings::FlightLogging::Function).toInt()); - ui->rulesComboBox->setCurrentIndex(Settings::read(Settings::FlightLogging::LogIFR).toInt()); - ui->approachComboBox->setCurrentIndex(Settings::read(Settings::FlightLogging::Approach).toInt()); - ui->nightComboBox->setCurrentIndex(Settings::read(Settings::FlightLogging::NightLoggingEnabled).toInt()); - ui->prefixLineEdit->setText(Settings::read(Settings::FlightLogging::FlightNumberPrefix).toString()); - - ui->logbookViewComboBox->setCurrentIndex(Settings::read(Settings::Main::LogbookView).toInt()); - ui->aliasComboBox->setCurrentIndex(Settings::read(Settings::UserData::DisplaySelfAs).toInt()); - - // Currencies Tab - ui->currToLdgCheckBox->setChecked(Settings::read(Settings::UserData::ShowToLgdCurrency).toBool()); - ui->currLicCheckBox->setChecked(Settings::read(Settings::UserData::ShowLicCurrency).toBool()); - ui->currTrCheckBox->setChecked(Settings::read(Settings::UserData::ShowTrCurrency).toBool()); - ui->currLckCheckBox->setChecked(Settings::read(Settings::UserData::ShowLckCurrency).toBool()); - ui->currMedCheckBox->setChecked(Settings::read(Settings::UserData::ShowMedCurrency).toBool()); - ui->currCustom1CheckBox->setChecked(Settings::read(Settings::UserData::ShowCustom1Currency).toBool()); - ui->currCustom2CheckBox->setChecked(Settings::read(Settings::UserData::ShowCustom2Currency).toBool()); - ui->currCustom1LineEdit->setText(Settings::read(Settings::UserData::Custom1CurrencyName).toString()); - ui->currCustom2LineEdit->setText(Settings::read(Settings::UserData::Custom2CurrencyName).toString()); + ui->functionComboBox->setCurrentIndex(static_cast(Settings::getPilotFunction())); + ui->rulesComboBox->setCurrentIndex(Settings::getLogIfr()); + ui->approachComboBox->setCurrentText(Settings::getApproachType()); + ui->nightComboBox->setCurrentIndex(Settings::getNightLoggingEnabled()); + ui->prefixLineEdit->setText(Settings::getFlightNumberPrefix()); + + ui->logbookViewComboBox->setCurrentIndex(static_cast(Settings::getLogbookView())); + ui->aliasComboBox->setCurrentIndex(Settings::getShowSelfAs()); // Misc Tab - ui->acftSortComboBox->setCurrentIndex(Settings::read(Settings::UserData::TailSortColumn).toInt()); - ui->pilotSortComboBox->setCurrentIndex(Settings::read(Settings::UserData::PilotSortColumn).toInt()); + ui->acftSortComboBox->setCurrentIndex(Settings::getTailSortColumn()); + ui->pilotSortComboBox->setCurrentIndex(Settings::getPilotSortColumn()); // Don't emit signals for OPL::Style changes during setup const QSignalBlocker style_blocker(ui->styleComboBox); @@ -165,10 +120,10 @@ void SettingsWidget::readSettings() const QSignalBlocker font_blocker_2(ui->fontComboBox); const QSignalBlocker font_blocker_3(ui->fontCheckBox); - ui->styleComboBox->setCurrentText(Settings::read(Settings::Main::Style).toString()); - ui->fontSpinBox->setValue(Settings::read(Settings::Main::FontSize).toUInt()); - ui->fontComboBox->setCurrentFont(QFont(Settings::read(Settings::Main::Font).toString())); - bool use_system_font = Settings::read(Settings::Main::UseSystemFont).toBool(); + ui->styleComboBox->setCurrentText(Settings::getApplicationStyle()); + ui->fontSpinBox->setValue(Settings::getApplicationFontSize()); + ui->fontComboBox->setCurrentFont(QFont(Settings::getApplicationFontName())); + bool use_system_font = Settings::getUseSystemFont(); ui->fontCheckBox->setChecked(use_system_font); if (!use_system_font) { ui->fontComboBox->setEnabled(true); @@ -179,7 +134,6 @@ void SettingsWidget::readSettings() void SettingsWidget::setupValidators() { ui->phoneLineEdit->setValidator(new QRegularExpressionValidator(OPL::RegEx::RX_PHONE_NUMBER, ui->phoneLineEdit)); - ui->emailLineEdit->setValidator(new QRegularExpressionValidator(OPL::RegEx::RX_EMAIL_ADDRESS, ui->emailLineEdit)); } /*! @@ -262,44 +216,43 @@ void SettingsWidget::on_phoneLineEdit_editingFinished() void SettingsWidget::on_aliasComboBox_currentIndexChanged(int index) { - Settings::write(Settings::UserData::DisplaySelfAs, index); + Settings::setShowSelfAs(index); updatePersonalDetails(); } void SettingsWidget::on_functionComboBox_currentIndexChanged(int arg1) { - Settings::write(Settings::FlightLogging::Function, arg1); + Settings::setPilotFunction(OPL::PilotFunction(arg1)); } void SettingsWidget::on_rulesComboBox_currentIndexChanged(int arg1) { - Settings::write(Settings::FlightLogging::LogIFR, arg1); + Settings::setLogIfr(arg1); } void SettingsWidget::on_approachComboBox_currentIndexChanged(int arg1) { - Settings::write(Settings::FlightLogging::Approach, arg1); + Settings::setApproachType(ui->approachComboBox->currentText()); } void SettingsWidget::on_nightComboBox_currentIndexChanged(int index) { - Settings::write(Settings::FlightLogging::NightLoggingEnabled, index); + Settings::setNightLoggingEnabled(index); switch (index) { case 1: - Settings::write(Settings::FlightLogging::NightAngle, -6); + Settings::setNightAngle(-6); break; case 2: - Settings::write(Settings::FlightLogging::NightAngle, 0); + Settings::setNightAngle(0); break; default: - Settings::write(Settings::FlightLogging::NightAngle, -6); + Settings::setNightAngle(-6); } } void SettingsWidget::on_prefixLineEdit_textChanged(const QString &arg1) { - Settings::write(Settings::FlightLogging::FlightNumberPrefix, arg1); - + Settings::setFlightNumberPrefix(arg1); } /* @@ -308,18 +261,19 @@ void SettingsWidget::on_prefixLineEdit_textChanged(const QString &arg1) void SettingsWidget::on_logbookViewComboBox_currentIndexChanged(int index) { - Settings::write(Settings::Main::LogbookView, index); +// Settings::write(Settings::Main::LogbookView, index); + Settings::setLogbookView(OPL::LogbookView(index)); emit settingChanged(SettingSignal::LogbookWidget); } void SettingsWidget::on_pilotSortComboBox_currentIndexChanged(int index) { - Settings::write(Settings::UserData::PilotSortColumn, index); + Settings::setPilotSortColumn(index); emit settingChanged(PilotsWidget); } void SettingsWidget::on_acftSortComboBox_currentIndexChanged(int index) { - Settings::write(Settings::UserData::TailSortColumn, index); + Settings::setTailSortColumn(index); emit settingChanged(AircraftWidget); } @@ -404,14 +358,16 @@ void SettingsWidget::on_styleComboBox_currentTextChanged(const QString& new_styl { if (new_style_setting == QLatin1String("Dark-Palette")) { OPL::Style::setStyle(OPL::Style::darkPalette()); - Settings::write(Settings::Main::Style, new_style_setting); +// Settings::write(Settings::Main::Style, new_style_setting); + Settings::setApplicationStyle(new_style_setting); emit settingChanged(MainWindow); return; } for (const auto &style_name : OPL::Style::styles) { if (new_style_setting == style_name) { OPL::Style::setStyle(style_name); - Settings::write(Settings::Main::Style, new_style_setting); +// Settings::write(Settings::Main::Style, new_style_setting); + Settings::setApplicationStyle(style_name); emit settingChanged(MainWindow); return; } @@ -420,7 +376,8 @@ void SettingsWidget::on_styleComboBox_currentTextChanged(const QString& new_styl for (const auto &style_sheet : OPL::Style::styleSheets) { if (new_style_setting == style_sheet.styleSheetName) { OPL::Style::setStyle(style_sheet); - Settings::write(Settings::Main::Style, new_style_setting); +// Settings::write(Settings::Main::Style, new_style_setting); + Settings::setApplicationStyle(new_style_setting); emit settingChanged(MainWindow); return; } @@ -430,7 +387,8 @@ void SettingsWidget::on_styleComboBox_currentTextChanged(const QString& new_styl void SettingsWidget::on_fontComboBox_currentFontChanged(const QFont &f) { qApp->setFont(f); - Settings::write(Settings::Main::Font, f.toString()); +// Settings::write(Settings::Main::Font, f.toString()); + Settings::setApplicationFontName(f.toString()); DEB << "Setting Font:" << f.toString(); } @@ -439,7 +397,8 @@ void SettingsWidget::on_fontSpinBox_valueChanged(int arg1) QFont f = qApp->font(); f.setPointSize(arg1); qApp->setFont(f); - Settings::write(Settings::Main::FontSize, arg1); +// Settings::write(Settings::Main::FontSize, arg1); + Settings::setApplicationFontSize(arg1); DEB << "Setting Font:" << f.toString(); } @@ -455,7 +414,7 @@ void SettingsWidget::on_fontCheckBox_stateChanged(int arg1) { ui->fontComboBox->setEnabled(true); ui->fontSpinBox->setEnabled(true); - Settings::write(Settings::Main::UseSystemFont, false); + Settings::setUseSystemFont(false); QFont font(ui->fontComboBox->currentFont()); font.setPointSize(ui->fontSpinBox->value()); qApp->setFont(font); @@ -466,7 +425,7 @@ void SettingsWidget::on_fontCheckBox_stateChanged(int arg1) { ui->fontComboBox->setEnabled(false); ui->fontSpinBox->setEnabled(false); - Settings::write(Settings::Main::UseSystemFont, true); + Settings::setUseSystemFont(true); INFO(tr("The application will be restarted for this change to take effect.")); qApp->quit(); QProcess::startDetached(qApp->arguments()[0], qApp->arguments()); @@ -496,184 +455,6 @@ void SettingsWidget::on_resetStylePushButton_clicked() ui->fontCheckBox->setChecked(true); } -void SettingsWidget::on_currLicDateEdit_userDateChanged(const QDate &date) -{ - const OPL::RowData_T row_data = {{OPL::CurrencyEntry::EXPIRYDATE, date.toString(Qt::ISODate)}}; - const OPL::CurrencyEntry entry(static_cast(OPL::CurrencyName::Licence), row_data); - if (!DB->commit(entry)) - WARN(tr("Unable to update currency. The following error has ocurred:
    %1").arg(DB->lastError.text())); - - emit settingChanged(HomeWidget); -} - -void SettingsWidget::on_currTrDateEdit_userDateChanged(const QDate &date) -{ - const OPL::RowData_T row_data = {{OPL::CurrencyEntry::EXPIRYDATE, date.toString(Qt::ISODate)}}; - const OPL::CurrencyEntry entry(static_cast(OPL::CurrencyName::TypeRating), row_data); - if (!DB->commit(entry)) - WARN(tr("Unable to update currency. The following error has ocurred:
    %1").arg(DB->lastError.text())); - - emit settingChanged(HomeWidget); -} - -void SettingsWidget::on_currLckDateEdit_userDateChanged(const QDate &date) -{ - const OPL::RowData_T row_data = {{OPL::CurrencyEntry::EXPIRYDATE, date.toString(Qt::ISODate)}}; - const OPL::CurrencyEntry entry(static_cast(OPL::CurrencyName::LineCheck), row_data); - if (!DB->commit(entry)) - WARN(tr("Unable to update currency. The following error has ocurred:
    %1").arg(DB->lastError.text())); - - emit settingChanged(HomeWidget); -} - -void SettingsWidget::on_currMedDateEdit_userDateChanged(const QDate &date) -{ - const OPL::RowData_T row_data = {{OPL::CurrencyEntry::EXPIRYDATE, date.toString(Qt::ISODate)}}; - const OPL::CurrencyEntry entry(static_cast(OPL::CurrencyName::Medical), row_data); - if (!DB->commit(entry)) - WARN(tr("Unable to update currency. The following error has ocurred:
    %1").arg(DB->lastError.text())); - - emit settingChanged(HomeWidget); -} - -void SettingsWidget::on_currCustom1DateEdit_userDateChanged(const QDate &date) -{ - const OPL::RowData_T row_data = {{OPL::CurrencyEntry::EXPIRYDATE, date.toString(Qt::ISODate)}, - {OPL::CurrencyEntry::CURRENCYNAME, ui->currCustom1LineEdit->text()}}; - const OPL::CurrencyEntry entry(static_cast(OPL::CurrencyName::Custom1), row_data); - DEB << entry; - if (!DB->commit(entry)) - WARN(tr("Unable to update currency. The following error has ocurred:

    %1").arg(DB->lastError.text())); - - emit settingChanged(HomeWidget); -} - -void SettingsWidget::on_currCustom2DateEdit_userDateChanged(const QDate &date) -{ - const OPL::RowData_T row_data = {{OPL::CurrencyEntry::EXPIRYDATE, date.toString(Qt::ISODate)}, - {OPL::CurrencyEntry::CURRENCYNAME, ui->currCustom2LineEdit->text()}}; - const OPL::CurrencyEntry entry(static_cast(OPL::CurrencyName::Custom2), row_data); - if (!DB->commit(entry)) - WARN(tr("Unable to update currency. The following error has ocurred:

    %1").arg(DB->lastError.text())); - - emit settingChanged(HomeWidget); -} - -void SettingsWidget::on_currToLdgCheckBox_stateChanged(int arg1) -{ - switch (arg1) { - case Qt::CheckState::Checked: - Settings::write(Settings::UserData::ShowToLgdCurrency, true); - break; - case Qt::CheckState::Unchecked: - Settings::write(Settings::UserData::ShowToLgdCurrency, false); - break; - default: - break; - } - emit settingChanged(HomeWidget); -} - -void SettingsWidget::on_currLicCheckBox_stateChanged(int arg1) -{ - switch (arg1) { - case Qt::CheckState::Checked: - Settings::write(Settings::UserData::ShowLicCurrency, true); - break; - case Qt::CheckState::Unchecked: - Settings::write(Settings::UserData::ShowLicCurrency, false); - break; - default: - break; - } - emit settingChanged(HomeWidget); -} - -void SettingsWidget::on_currTrCheckBox_stateChanged(int arg1) -{ - switch (arg1) { - case Qt::CheckState::Checked: - Settings::write(Settings::UserData::ShowTrCurrency, true); - break; - case Qt::CheckState::Unchecked: - Settings::write(Settings::UserData::ShowTrCurrency, false); - break; - default: - break; - } - emit settingChanged(HomeWidget); -} - -void SettingsWidget::on_currLckCheckBox_stateChanged(int arg1) -{ - switch (arg1) { - case Qt::CheckState::Checked: - Settings::write(Settings::UserData::ShowLckCurrency, true); - break; - case Qt::CheckState::Unchecked: - Settings::write(Settings::UserData::ShowLckCurrency, false); - break; - default: - break; - } - emit settingChanged(HomeWidget); -} - -void SettingsWidget::on_currMedCheckBox_stateChanged(int arg1) -{ - switch (arg1) { - case Qt::CheckState::Checked: - Settings::write(Settings::UserData::ShowMedCurrency, true); - break; - case Qt::CheckState::Unchecked: - Settings::write(Settings::UserData::ShowMedCurrency, false); - break; - default: - break; - } - emit settingChanged(HomeWidget); -} - -void SettingsWidget::on_currCustom1CheckBox_stateChanged(int arg1) -{ - switch (arg1) { - case Qt::CheckState::Checked: - Settings::write(Settings::UserData::ShowCustom1Currency, true); - break; - case Qt::CheckState::Unchecked: - Settings::write(Settings::UserData::ShowCustom1Currency, false); - break; - default: - break; - } - emit settingChanged(HomeWidget); -} - -void SettingsWidget::on_currCustom2CheckBox_stateChanged(int arg1) -{ - switch (arg1) { - case Qt::CheckState::Checked: - Settings::write(Settings::UserData::ShowCustom2Currency, true); - break; - case Qt::CheckState::Unchecked: - Settings::write(Settings::UserData::ShowCustom2Currency, false); - break; - default: - break; - } - emit settingChanged(HomeWidget); -} - -void SettingsWidget::on_currCustom1LineEdit_editingFinished() -{ - Settings::write(Settings::UserData::Custom1CurrencyName, ui->currCustom1LineEdit->text()); -} - -void SettingsWidget::on_currCustom2LineEdit_editingFinished() -{ - Settings::write(Settings::UserData::Custom2CurrencyName, ui->currCustom2LineEdit->text()); -} - void SettingsWidget::on_languageComboBox_activated(int arg1) { if (arg1 != 0) { @@ -684,10 +465,15 @@ void SettingsWidget::on_languageComboBox_activated(int arg1) } } - void SettingsWidget::on_exportPushButton_clicked() { auto exp = new ExportToCsvDialog(this); exp->exec(); } + +void SettingsWidget::on_currencyWarningDaysSpinBox_valueChanged(int arg1) +{ + Settings::setCurrencyWarningThreshold(arg1); +} + diff --git a/src/gui/widgets/settingswidget.h b/src/gui/widgets/settingswidget.h index 594e4d8b..7024eb39 100644 --- a/src/gui/widgets/settingswidget.h +++ b/src/gui/widgets/settingswidget.h @@ -74,24 +74,11 @@ private slots: void on_fontSpinBox_valueChanged(int arg1); void on_fontCheckBox_stateChanged(int arg1); void on_resetStylePushButton_clicked(); - void on_currLicDateEdit_userDateChanged(const QDate &date); - void on_currTrDateEdit_userDateChanged(const QDate &date); - void on_currLckDateEdit_userDateChanged(const QDate &date); - void on_currMedDateEdit_userDateChanged(const QDate &date); - void on_currCustom1DateEdit_userDateChanged(const QDate &date); - void on_currCustom2DateEdit_userDateChanged(const QDate &date); - void on_currToLdgCheckBox_stateChanged(int arg1); - void on_currLicCheckBox_stateChanged(int arg1); - void on_currTrCheckBox_stateChanged(int arg1); - void on_currLckCheckBox_stateChanged(int arg1); - void on_currMedCheckBox_stateChanged(int arg1); - void on_currCustom1CheckBox_stateChanged(int arg1); - void on_currCustom2CheckBox_stateChanged(int arg1); - void on_currCustom1LineEdit_editingFinished(); - void on_currCustom2LineEdit_editingFinished(); void on_languageComboBox_activated(int arg1); void on_exportPushButton_clicked(); + void on_currencyWarningDaysSpinBox_valueChanged(int arg1); + private: Ui::SettingsWidget *ui; @@ -101,8 +88,6 @@ private slots: void setupComboBoxes(); - void setupDateEdits(); - void loadBackupWidget(); void loadPreviousExperienceWidget(); diff --git a/src/gui/widgets/settingswidget.ui b/src/gui/widgets/settingswidget.ui index e7c1807d..9c5d5a2a 100644 --- a/src/gui/widgets/settingswidget.ui +++ b/src/gui/widgets/settingswidget.ui @@ -17,8 +17,261 @@ - 2 + 0 + + + General + + + + + + Setting this value to 0 disables warnings. + + + Warn about expiring currencies + + + + + + + + 0 + 0 + + + + Style + + + + + + + Reset to Default + + + + + + + + + + Font + + + + + + + Use System Font + + + true + + + + + + + false + + + + + + + false + + + + 80 + 16777215 + + + + 8 + + + 18 + + + 10 + + + + + + + + 0 + 0 + + + + <html><head/><body><p>Determines by which column to sort the display of Pilots in the Pilots Tab.</p></body></html> + + + <html><head/><body><p>Determines by which column to sort the display of Pilots in the Pilots Tab.</p></body></html> + + + Sort Pilots by + + + + + + + <html><head/><body><p>Determines by which column to sort the display of Pilots in the Pilots Tab.</p></body></html> + + + + Last Name + + + + + First Name + + + + + Company + + + + + + + + + 0 + 0 + + + + <html><head/><body><p>Determines by which column to sort the display of Aircaft in the Aircraft Tab.</p></body></html> + + + <html><head/><body><p>Determines by which column to sort the display of Aircaft in the Aircraft Tab.</p></body></html> + + + Sort Aircraft by + + + + + + + <html><head/><body><p>Determines by which column to sort the display of Aircaft in the Aircraft Tab.</p></body></html> + + + + Registration + + + + + Type + + + + + Company + + + + + + + + + 0 + 0 + + + + <html><head/><body><p>Determines how your logbook is displayed in the logbook tab. This has no influence on what details are logged, just on what is displayed by default.</p></body></html> + + + <html><head/><body><p>Determines how your logbook is displayed in the logbook tab. This has no influence on what details are logged, just on what is displayed by default.</p></body></html> + + + Logbook Display + + + + + + + <html><head/><body><p>Determines how your logbook is displayed in the logbook tab. This has no influence on what details are logged, just on what is displayed by default.</p></body></html> + + + + + + + <html><head/><body><p>How your own name is displayed in your logbook</p></body></html> + + + Show own name as + + + + + + + + self + + + + + SELF + + + + + Lastname, Firstname + + + + + + + + Language + + + + + + + + + + Setting this value to 0 disables warnings. + + + 30 + + + + + + + Setting this value to 0 disables warnings. + + + days before expiry + + + + + Personal @@ -320,668 +573,6 @@
    - - - Currencies - - - - - - - 140 - 0 - - - - Qt::LeftToRight - - - - - - - - - - - 280 - 0 - - - - - - - custom currency - - - - - - - - 140 - 0 - - - - Qt::LeftToRight - - - - - - - - - - - 280 - 0 - - - - Take-off / Landing (days) - - - - - - - Qt::Vertical - - - - 20 - 168 - - - - - - - - - 140 - 0 - - - - QDateTimeEdit::MonthSection - - - MM/dd/yyyy - - - true - - - Qt::UTC - - - - 2020 - 1 - 1 - - - - - - - - - 140 - 0 - - - - QDateTimeEdit::MonthSection - - - MM/dd/yyyy - - - true - - - Qt::UTC - - - - 2020 - 1 - 1 - - - - - - - - - 140 - 0 - - - - Qt::LeftToRight - - - - - - - - - - - 140 - 0 - - - - QDateTimeEdit::MonthSection - - - MM/dd/yyyy - - - true - - - Qt::UTC - - - - 2020 - 1 - 1 - - - - - - - - <html><head/><body><p>Number of days for TO/LDG currency. Default 90</p></body></html> - - - 90 - - - - - - - - 280 - 0 - - - - Medical - - - - - - - - 140 - 0 - - - - Qt::LeftToRight - - - - - - - - - - - 280 - 0 - - - - custom currency - - - - - - - - 140 - 0 - - - - QDateTimeEdit::MonthSection - - - MM/dd/yyyy - - - true - - - Qt::UTC - - - - 2020 - 1 - 1 - - - - - - - - - 280 - 0 - - - - Line Check - - - - - - - - 280 - 0 - - - - Type Rating - - - - - - - - 280 - 0 - - - - Licence - - - - - - - - 140 - 0 - - - - Qt::LeftToRight - - - - - - - - - - Qt::Vertical - - - - 20 - 167 - - - - - - - - - 140 - 0 - - - - QDateTimeEdit::MonthSection - - - MM/dd/yyyy - - - true - - - Qt::UTC - - - - 2020 - 1 - 1 - - - - - - - - - 140 - 0 - - - - Qt::LeftToRight - - - - - - - - - - - 140 - 0 - - - - QDateTimeEdit::MonthSection - - - MM/dd/yyyy - - - true - - - Qt::UTC - - - - 2020 - 1 - 1 - - - - - - - - - 140 - 0 - - - - Qt::LeftToRight - - - - - - - - - - - 140 - 0 - - - - Show on Home Page - - - Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft - - - - - - - - Appearance - - - - - - - 0 - 0 - - - - Style - - - - - - - Reset to Default - - - - - - - - - - Font - - - - - - - Use System Font - - - true - - - - - - - false - - - - - - - false - - - - 80 - 16777215 - - - - 8 - - - 18 - - - 10 - - - - - - - - 0 - 0 - - - - <html><head/><body><p>Determines by which column to sort the display of Pilots in the Pilots Tab.</p></body></html> - - - <html><head/><body><p>Determines by which column to sort the display of Pilots in the Pilots Tab.</p></body></html> - - - Sort Pilots by - - - - - - - <html><head/><body><p>Determines by which column to sort the display of Pilots in the Pilots Tab.</p></body></html> - - - - Last Name - - - - - First Name - - - - - Company - - - - - - - - - 0 - 0 - - - - <html><head/><body><p>Determines by which column to sort the display of Aircaft in the Aircraft Tab.</p></body></html> - - - <html><head/><body><p>Determines by which column to sort the display of Aircaft in the Aircraft Tab.</p></body></html> - - - Sort Aircraft by - - - - - - - <html><head/><body><p>Determines by which column to sort the display of Aircaft in the Aircraft Tab.</p></body></html> - - - - Registration - - - - - Type - - - - - Company - - - - - - - - - 0 - 0 - - - - <html><head/><body><p>Determines how your logbook is displayed in the logbook tab. This has no influence on what details are logged, just on what is displayed by default.</p></body></html> - - - <html><head/><body><p>Determines how your logbook is displayed in the logbook tab. This has no influence on what details are logged, just on what is displayed by default.</p></body></html> - - - Logbook Dispay - - - - - - - <html><head/><body><p>Determines how your logbook is displayed in the logbook tab. This has no influence on what details are logged, just on what is displayed by default.</p></body></html> - - - - - - - <html><head/><body><p>How your own name is displayed in your logbook</p></body></html> - - - Show own name as - - - - - - - - self - - - - - SELF - - - - - Lastname, Firstname - - - - - - - - Language - - - - - - - - Backups diff --git a/src/gui/widgets/tableeditwidget.cpp b/src/gui/widgets/tableeditwidget.cpp new file mode 100644 index 00000000..7e48fd73 --- /dev/null +++ b/src/gui/widgets/tableeditwidget.cpp @@ -0,0 +1,231 @@ +#include "tableeditwidget.h" +#include "src/database/database.h" +#include "src/opl.h" +#include +#include + +TableEditWidget::TableEditWidget(Orientation orientation, QWidget *parent) + : QWidget{parent}, m_orientation(orientation) +{} + +void TableEditWidget::init() +{ + setupUI(); + setupSignalsAndSlots(); +} + +void TableEditWidget::setupUI() +{ + // Setting up the model and view is done in the derived class + setupModelAndView(); + m_entryEditDialog = getEntryEditDialog(this); + m_stackedWidget->addWidget(m_entryEditDialog); + + // set up the UI + switch (m_orientation) { + case Horizontal: + setupHorizontalUI(); + break; + case Vertical: + setupVerticalUI(); + default: + break; + } + +} + +void TableEditWidget::setupHorizontalUI() +{ + // In the horizontal view, the editing widget is hidden on the right hand side + m_stackedWidget->hide(); + + // create a 2-column grid layout and fill the cells + int colL = 0; // left column + int colR = 1; // right column + int row = 0; + int allRowSpan = 4; // adjust as needed for stackedWidget to span all rows + + auto gridLayout = new QGridLayout(this); + + gridLayout->addWidget(m_view, row, colL); + gridLayout->addWidget(m_stackedWidget, row, colR, allRowSpan, 1); + row++; + + setupButtonWidget(); + gridLayout->addWidget(m_buttonWidget); + row++; + + setupFilterWidget(); + gridLayout->addWidget(m_filterWidget, row, colL); +} + +void TableEditWidget::setupVerticalUI() +{ + // create a single column grid layout and fill the cells + int col = 0; + int row = 0; + auto gridLayout = new QGridLayout(this); + + gridLayout->addWidget(m_view, row, col); + row++; + + gridLayout->addWidget(m_stackedWidget, row, col); + row++; + + setupButtonWidget(); + gridLayout->addWidget(m_buttonWidget); + row++; + + setupFilterWidget(); + m_stackedWidget->addWidget(m_filterWidget); + m_stackedWidget->setCurrentWidget(m_filterWidget); + gridLayout->addWidget(m_stackedWidget); +} + +void TableEditWidget::setupFilterWidget() +{ + // place the filter items in a grid layout so they occupy one cell in parent layout + QWidget *widget = new QWidget(this); + QGridLayout *layout = new QGridLayout(widget); + + // one row, three columns + layout->addWidget(new QLabel(tr("Filter"), this), 0, 0); + layout->addWidget(m_filterLineEdit, 0, 1); + layout->addWidget(m_filterSelectionComboBox, 0, 2); + + m_filterWidget = widget; +} + +void TableEditWidget::setupButtonWidget() +{ + auto buttonWidget = new QWidget(this); + auto buttonGridLayout = new QGridLayout(buttonWidget); + + switch (m_orientation) { + case Horizontal: + buttonGridLayout->addWidget(m_addNewEntryPushButton, 0, 0); + buttonGridLayout->addWidget(m_deleteEntryPushButton, 1, 0); + break; + case Vertical: + buttonGridLayout->addWidget(m_addNewEntryPushButton, 0, 0); + buttonGridLayout->addWidget(m_deleteEntryPushButton, 0, 1); + default: + break; + } + + m_buttonWidget = buttonWidget; +} + +void TableEditWidget::setupSignalsAndSlots() +{ + // refresh the view when the database is updated + QObject::connect(DB, &OPL::Database::dataBaseUpdated, + this, &TableEditWidget::databaseContentChanged); + // filter the view + QObject::connect(m_filterLineEdit, &QLineEdit::textChanged, + this, &TableEditWidget::filterTextChanged); + // sort the view by column + QObject::connect(m_view->horizontalHeader(),&QHeaderView::sectionClicked, + this, &TableEditWidget::sortColumnChanged); + // Edit an entry + QObject::connect(m_view, &QTableView::clicked, + this, &TableEditWidget::editEntryRequested); + // Add a new entry + QObject::connect(m_addNewEntryPushButton, &QPushButton::clicked, + this, &TableEditWidget::addEntryRequested); + // Delete a selected entry + QObject::connect(m_deleteEntryPushButton, &QPushButton::clicked, + this, &TableEditWidget::deleteEntryRequested); +} + +void TableEditWidget::addEntryRequested() +{ + cleanUpOldEditDialog(); + + m_entryEditDialog = getEntryEditDialog(this); + m_stackedWidget->addWidget(m_entryEditDialog); + m_stackedWidget->setCurrentWidget(m_entryEditDialog); + + showEditWidget(); + m_entryEditDialog->exec(); + hideEditWidget(); +} + +void TableEditWidget::editEntryRequested(const QModelIndex &selectedIndex) +{ + int rowIdToEdit = m_model->index(selectedIndex.row(), 0).data().toInt(); + + cleanUpOldEditDialog(); + + m_entryEditDialog = getEntryEditDialog(this); + m_entryEditDialog->loadEntry(rowIdToEdit); + m_stackedWidget->addWidget(m_entryEditDialog); + m_stackedWidget->setCurrentWidget(m_entryEditDialog); + + showEditWidget(); + m_entryEditDialog->exec(); + hideEditWidget(); +} + +void TableEditWidget::deleteEntryRequested() +{ + const QModelIndex selectedIndex = m_view->selectionModel()->currentIndex(); + if(!selectedIndex.isValid()) { + WARN(tr("No entry selected.")); + return; + } + m_stackedWidget->hide(); + + int rowId = m_model->index(selectedIndex.row(), 0).data().toInt(); + m_view->selectionModel()->reset(); + + // get user confirmation + QMessageBox confirm(this); + confirm.setStandardButtons(QMessageBox::Yes | QMessageBox::No); + confirm.setDefaultButton(QMessageBox::No); + confirm.setIcon(QMessageBox::Question); + confirm.setWindowTitle(tr("Confirm Deletion")); + + confirm.setText(confirmDeleteString(rowId)); + if (confirm.exec() == QMessageBox::Yes) { + auto editDialog = getEntryEditDialog(this); + if(!editDialog->deleteEntry(rowId)) + WARN(deleteErrorString(rowId)); + } + + // re-set stackedWidget for Vertical Layout + if(m_orientation == Vertical) { + m_stackedWidget->setCurrentWidget(m_filterWidget); + m_stackedWidget->show(); + } +} + +void TableEditWidget::sortColumnChanged(int newSortColumn) +{ + m_view->sortByColumn(newSortColumn, Qt::AscendingOrder); +} + +void TableEditWidget::databaseContentChanged() +{ + m_model->select(); + m_view->resizeColumnsToContents(); +} + +void TableEditWidget::showEditWidget() +{ + m_buttonWidget->hide(); + m_stackedWidget->show(); +} + +void TableEditWidget::hideEditWidget() +{ + m_stackedWidget->hide(); + m_buttonWidget->show(); +} + +void TableEditWidget::cleanUpOldEditDialog() +{ + if(m_stackedWidget->indexOf(m_entryEditDialog) != -1) { + delete m_entryEditDialog; + } +} diff --git a/src/gui/widgets/tableeditwidget.h b/src/gui/widgets/tableeditwidget.h new file mode 100644 index 00000000..25af31d8 --- /dev/null +++ b/src/gui/widgets/tableeditwidget.h @@ -0,0 +1,154 @@ +#ifndef TABLEEDITWIDGET_H +#define TABLEEDITWIDGET_H + +#include "src/gui/dialogues/entryeditdialog.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/*! + * \brief The TableEditWidget class is a base class for widgets which enable + * editing certain tables in the datbase. + * \details The TableEditWidget consists of a QTableView which displays the data + * from a given table. The data is held in a QSqlTableModel. The views edit triggers + * are disabled. Whenever a row is selected in the view, the selected entry is displayed + * for editing in a suitable EntryEditDialog which is responsible for the verification of + * the user input as well as reading and writing to and from the database. + * + * The TableEditWidget has two Orientation options: Horizontal and Vertical + * + * In the Horizontal layout, the table view is split horizontally to make space + * for the TableEditWidget on the right hand side, whereas on the Vertical layout + * it is split vertically with the TableEditWidget occupying the lower half of the screen. + * + * When implementing the TableEditWidget it is important to set up the model and call the + * Base Class implementation of setupUI before performing any specialisations. Before the + * TableEditWidget is shown, the init method must be run. + */ +class TableEditWidget : public QWidget +{ + Q_OBJECT +public: + /*! + * \brief Determines how the layout is created + * \details
      + *
    • Horizontal: The edit widget is shown besides the table
    • + *
    • Vertical: The edit widget is shown below the table
    • + *
    + */ + enum Orientation {Horizontal, Vertical}; + + /*! + * \brief Create a new TableEditWidget + */ + explicit TableEditWidget(Orientation orientation = Horizontal, QWidget *parent = nullptr); + + /*! + * \brief Initialises the dialog by calling its virtual setup functions. + * \attention Call this functian before showing the dialog. + */ + void init(); + + /*! + * \brief Set up the model and view of the widget + * \details Implement this function to initialise the protected members of this class. + * This includes setting the QSqlTableModel and QTableView */ + virtual void setupModelAndView() = 0; + + /*! + * \brief Set up the UI of the widget + * \details Implement this function to set appropriate labels to the protected members of this + * class. This includes setting appropriate labels on the Push Buttons as well as + * appropriate filter options in the filter Combo Box. Make sure to call the base class + * implementation first when overriding this method. + */ + virtual void setupUI(); + + /*! + * \brief create an error String when deleting a database entry has been unsuccessful + * \param rowId - the row id of the entry to be deleted + * \details When deleting an entry from a database fails, this can have different reasons + * depending on the table. This function returns an implementation-specific error string + * to inform the user about the failure and give hints on how to fix it + */ + virtual QString deleteErrorString(int rowId) = 0; + + /*! + * \brief return a String asking the user to confirm deletion of a given entry + * \param rowId - the row id of the entry to be deleted + * \brief The message string is displayed in a QMessageBox + */ + virtual QString confirmDeleteString(int rowId) = 0; + + /*! + * \brief get an apropriate Edit Dialog for the implementation + * \details The Edit Dialogs for different tables differ in the data they display + * and how they verify the user inputs. This method returns an apropriate + * EntryEditDialog for the selected table. + */ + virtual EntryEditDialog *getEntryEditDialog(QWidget *parent = nullptr) = 0; + +protected: + Orientation m_orientation; + QSqlTableModel *m_model = nullptr; + QTableView *m_view = new QTableView(this); + QWidget *m_filterWidget = nullptr; + QWidget *m_buttonWidget = nullptr; + EntryEditDialog* m_entryEditDialog = nullptr; + + QPushButton *m_addNewEntryPushButton = new QPushButton(this); + QPushButton *m_deleteEntryPushButton = new QPushButton(this); + + QStackedWidget *m_stackedWidget = new QStackedWidget(this); + QLineEdit *m_filterLineEdit = new QLineEdit(this); + QComboBox *m_filterSelectionComboBox = new QComboBox(this); + + virtual void showEditWidget(); + virtual void hideEditWidget(); + /*! + * \brief makes sure heap allocated widgets are destroyed when a user requests another add or edit before completing + * a proviously opened one. + */ + void cleanUpOldEditDialog(); + +private: + void setupHorizontalUI(); + void setupVerticalUI(); + void setupSignalsAndSlots(); + + /*! + * \brief Place the filter items in a widget to facilitate easier placement in parent layout + */ + void setupFilterWidget(); + + /*! + * \brief Place the new and edit buttons in a widget to facilitate easier placement in parent layout + */ + void setupButtonWidget(); + +public slots: + virtual void addEntryRequested(); + virtual void editEntryRequested(const QModelIndex &selectedIndex); + virtual void deleteEntryRequested(); + virtual void sortColumnChanged(int newSortColumn); + + /*! + * \brief Set a filter on the model + */ + virtual void filterTextChanged(const QString &filterString) = 0; + +public slots: + /*! + * \brief refresh the view after a Database change + */ + void databaseContentChanged(); + +}; + +#endif // TABLEEDITWIDGET_H diff --git a/src/gui/widgets/tailtableeditwidget.cpp b/src/gui/widgets/tailtableeditwidget.cpp new file mode 100644 index 00000000..84d0e332 --- /dev/null +++ b/src/gui/widgets/tailtableeditwidget.cpp @@ -0,0 +1,111 @@ +#include "tailtableeditwidget.h" +#include "src/database/database.h" +#include "src/gui/dialogues/newtaildialog.h" + +TailTableEditWidget::TailTableEditWidget(QWidget *parent) + : TableEditWidget(Horizontal, parent) +{} + +void TailTableEditWidget::setupModelAndView() +{ + m_model = new QSqlTableModel(this, DB->database()); + m_model->setTable(OPL::GLOBALS->getDbTableName(OPL::DbTable::Tails)); + m_model->select(); + m_model->setHeaderData(COL_REGISTRATION, Qt::Horizontal, COLUMN_NAME_REGISTRATION); + m_model->setHeaderData(COL_TYPE, Qt::Horizontal, COLUMN_NAME_TYPE); + m_model->setHeaderData(COL_COMPANY, Qt::Horizontal, COLUMN_NAME_COMPANY); + + m_view->setModel(m_model); + m_view->setSelectionMode(QAbstractItemView::SingleSelection); + m_view->setSelectionBehavior(QAbstractItemView::SelectRows); + m_view->setEditTriggers(QAbstractItemView::NoEditTriggers); + m_view->horizontalHeader()->setStretchLastSection(QHeaderView::Stretch); + m_view->resizeColumnsToContents(); + m_view->verticalHeader()->hide(); + m_view->setAlternatingRowColors(true); + for(const int i : COLS_TO_HIDE) + m_view->hideColumn(i); +} + +void TailTableEditWidget::setupUI() +{ + // the base class does most of the setup + TableEditWidget::setupUI(); + + // only need to set the table specific labels and combo box items + m_addNewEntryPushButton->setText(tr("Add New Tail")); + m_deleteEntryPushButton->setText(tr("Delete Selected Tail")); + m_filterSelectionComboBox->addItems(FILTER_COLUMNS); +} + +QString TailTableEditWidget::deleteErrorString(int rowId) +{ + QList foreign_key_constraints = DB->getForeignKeyConstraints(rowId, + OPL::DbTable::Tails); + QList constrained_flights; + for (const auto &row_id : qAsConst(foreign_key_constraints)) { + constrained_flights.append(DB->getFlightEntry(row_id)); + } + + QMessageBox message_box(this); + if (constrained_flights.isEmpty()) { + // error is a database error + return tr("
    Unable to delete.

    The following error has ocurred: %1" + ).arg(DB->lastError.text()); + } else { + QString constrained_flights_string; + for (int i=0; i")); + if (i>10) { + constrained_flights_string.append(QLatin1String("
    [...]
    ")); + break; + } + } + return (tr("Unable to delete.

    " + "This is most likely the case because a flight exists with the aircraft " + "you are trying to delete.

    " + "%1 flight(s) with this aircraft have been found:


    " + "%2" + "

    You have to change or remove the conflicting flight(s) " + "before removing this aircraft from the database.

    " + ).arg( + QString::number(constrained_flights.length()), + constrained_flights_string) + ); + } +} + +QString TailTableEditWidget::confirmDeleteString(int rowId) +{ + const auto entry = DB->getTailEntry(rowId); + return tr("You are deleting the following aircraft:

    " + "%1 (%2)

    Are you sure?" + ).arg( + entry.getData().value(OPL::TailEntry::REGISTRATION).toString(), + entry.type() + ); +} + +EntryEditDialog *TailTableEditWidget::getEntryEditDialog(QWidget *parent) +{ + QString empty; + return new NewTailDialog(empty, parent); +} + +void TailTableEditWidget::filterTextChanged(const QString &filterString) +{ + if(filterString.isEmpty()) { + m_model->setFilter(QString()); + return; + } + + int i = m_filterSelectionComboBox->currentIndex(); + const QString filter = + QLatin1Char('\"') + + FILTER_COLUMN_NAMES.at(i) + + QLatin1String("\" LIKE '%") + + filterString + + QLatin1String("%'"); + m_model->setFilter(filter); +} diff --git a/src/gui/widgets/tailtableeditwidget.h b/src/gui/widgets/tailtableeditwidget.h new file mode 100644 index 00000000..30583839 --- /dev/null +++ b/src/gui/widgets/tailtableeditwidget.h @@ -0,0 +1,50 @@ +#ifndef TAILTABLEEDITWIDGET_H +#define TAILTABLEEDITWIDGET_H + +#include "tableeditwidget.h" +#include "src/database/tailentry.h" + +class TailTableEditWidget : public TableEditWidget +{ + Q_OBJECT +public: + TailTableEditWidget() = delete; + explicit TailTableEditWidget(QWidget *parent = nullptr); + + + virtual void setupModelAndView() override; + virtual void setupUI() override; + virtual QString deleteErrorString(int rowId) override; + virtual QString confirmDeleteString(int rowId) override; + virtual EntryEditDialog *getEntryEditDialog(QWidget *parent) override; + +private: + const int COL_ROWID = 0; + const int COL_REGISTRATION = 1; + const int COL_TYPE = 10; + const int COL_COMPANY = 2; + + const int COLS_TO_HIDE[8] = {0, 3, 4, 5, 6, 7, 8, 9}; + + const QString COLUMN_NAME_REGISTRATION = tr("Registration"); + const QString COLUMN_NAME_TYPE = tr("Type"); + const QString COLUMN_NAME_COMPANY = tr("Company"); + + const QStringList FILTER_COLUMNS = { + COLUMN_NAME_REGISTRATION, + COLUMN_NAME_TYPE, + COLUMN_NAME_COMPANY, + }; + + const static inline QStringList FILTER_COLUMN_NAMES = { + OPL::TailEntry::REGISTRATION, + OPL::TailEntry::TYPE_STRING, + OPL::TailEntry::COMPANY + }; + +private slots: + + virtual void filterTextChanged(const QString &filterString) override; +}; + +#endif // TAILTABLEEDITWIDGET_H diff --git a/src/gui/widgets/totalswidget.cpp b/src/gui/widgets/totalswidget.cpp index 3b43cad3..403ef1b5 100644 --- a/src/gui/widgets/totalswidget.cpp +++ b/src/gui/widgets/totalswidget.cpp @@ -22,6 +22,7 @@ #include "src/opl.h" #include "src/classes/time.h" #include "ui_totalswidget.h" +#include "src/classes/settings.h" TotalsWidget::TotalsWidget(WidgetType widgetType, QWidget *parent) : QWidget(parent), @@ -43,6 +44,7 @@ TotalsWidget::~TotalsWidget() */ void TotalsWidget::setup(const WidgetType widgetType) { + m_format = Settings::getDisplayFormat(); const QList lineEdits = this->findChildren(); switch (widgetType) { @@ -90,6 +92,7 @@ void TotalsWidget::fillTotals(const WidgetType widgetType) break; case PreviousExperienceWidget: time_data = DB->getRowData(OPL::DbTable::PreviousExperience, ROW_ID); + break; } // fill the line edits with the data obtained @@ -106,7 +109,7 @@ void TotalsWidget::fillTotals(const WidgetType widgetType) line_edit->setText(field.toString()); } else { // line edits for total time - OPL::Time time = OPL::Time(field.toInt());// = Time(field.toInt()); + OPL::Time time = OPL::Time(field.toInt(), m_format); line_edit->setText(time.toString()); } } @@ -171,7 +174,7 @@ void TotalsWidget::timeLineEditEditingFinished() // write the updated value to the database const QString db_field = line_edit->objectName().remove(QLatin1String("LineEdit")); - const QVariant value = OPL::Time::fromString(line_edit->text()).toMinutes(); + const QVariant value = OPL::Time::fromString(line_edit->text(), m_format).toMinutes(); m_rowData.insert(db_field, value); LOG << "Added row data: " + db_field + ": " + value.toString(); @@ -181,7 +184,7 @@ void TotalsWidget::timeLineEditEditingFinished() // Read back the value and set the line edit to confirm input is correct and provide user feedback m_rowData = DB->getRowData(OPL::DbTable::PreviousExperience, ROW_ID); - OPL::Time new_time = OPL::Time(m_rowData.value(db_field).toInt()); + OPL::Time new_time = OPL::Time(m_rowData.value(db_field).toInt(), m_format); line_edit->setText(new_time.toString()); } diff --git a/src/gui/widgets/totalswidget.h b/src/gui/widgets/totalswidget.h index d36d0376..98bd345b 100644 --- a/src/gui/widgets/totalswidget.h +++ b/src/gui/widgets/totalswidget.h @@ -28,6 +28,18 @@ namespace Ui { class TotalsWidget; } +/*! + * \brief The TotalsWidget is used to display or edit total time values. + * \details This widget has two different purposes. + *
      + *
    • It displays the cumulative total time in the logbook
    • + *
    • It is used to enter and edit the total time accumulated in previous logbooks
    • + *
    + * + * In its total time form, the Widget functions purely as a display widget, whereas when used + * for previous experience, its fields are editable. + * + */ class TotalsWidget : public QWidget { Q_OBJECT @@ -48,6 +60,8 @@ class TotalsWidget : public QWidget /*! * \brief ROW_ID the row ID for previous experience entries (1) */ + + OPL::DateTimeFormat m_format; const static int ROW_ID = 1; void fillTotals(const WidgetType widgetType); void setup(const WidgetType widgetType); diff --git a/src/gui/widgets/totalswidget.ui b/src/gui/widgets/totalswidget.ui index 284b1518..4295c7ab 100644 --- a/src/gui/widgets/totalswidget.ui +++ b/src/gui/widgets/totalswidget.ui @@ -15,535 +15,531 @@ - - - - - - 120 - 16777215 - - - - Night - - - - - - - - 120 - 16777215 - - - - LDG Night - - - - - - - - 100 - 0 - - - - - 120 - 16777215 - - - - Qt::TabFocus - - - 0 - - - - - - - - 120 - 16777215 - - - - SP ME - - - - - - - - 100 - 0 - - - - - 120 - 16777215 - - - - Qt::TabFocus - - - 0 - - - - - - - - 100 - 0 - - - - - 120 - 16777215 - - - - Qt::TabFocus - - - - - - - - 100 - 0 - - - - - 120 - 16777215 - - - - Qt::TabFocus - - - - - - - - 120 - 16777215 - - - - DUAL - - - - - - - - 120 - 16777215 - - - - TO Night - - - - - - - - 100 - 0 - - - - - 120 - 16777215 - - - - Qt::TabFocus - - - - - - - - 100 - 0 - - - - - 120 - 16777215 - - - - Qt::TabFocus - - - - - - - - 100 - 0 - - - - - 120 - 16777215 - - - - Qt::TabFocus - - - - - - - - 100 - 0 - - - - - 120 - 16777215 - - - - Qt::TabFocus - - - 0 - - - - - - - - 120 - 16777215 - - - - PIC - - - - - - - - 120 - 16777215 - - - - PICus - - - - - - - - 120 - 16777215 - - - - IFR - - - - - - - - 100 - 0 - - - - - 120 - 16777215 - - - - Qt::TabFocus - - - - - - - - 120 - 16777215 - - - - Multi Pilot - - - - - - - - 100 - 0 - - - - - 120 - 16777215 - - - - Qt::TabFocus - - - - - - - - 100 - 0 - - - - - 120 - 16777215 - - - - Qt::TabFocus - - - 0 - - - - - - - - 120 - 16777215 - - - - LDG Day - - - - - - - - 120 - 16777215 - - - - Simulator - - - - - - - - 100 - 0 - - - - - 120 - 16777215 - - - - Qt::TabFocus - - - - - - - - 120 - 16777215 - - - - Total - - - - - - - - 120 - 16777215 - - - - SP SE - - - - - - - - 120 - 16777215 - - - - SIC - - - - - - - - 120 - 16777215 - - - - FI - - - - - - - - 100 - 0 - - - - - 120 - 16777215 - - - - Qt::TabFocus - - - - - - - - - - - 100 - 0 - - - - - 120 - 16777215 - - - - Qt::TabFocus - - - - - - - - 100 - 0 - - - - - 120 - 16777215 - - - - Qt::TabFocus - - - - - - - - 100 - 0 - - - - - 120 - 16777215 - - - - Qt::TabFocus - - - - - - - - 120 - 16777215 - - - - TO Day - - - - + + + + 120 + 16777215 + + + + Total + + + + + + + + 100 + 0 + + + + + 120 + 16777215 + + + + Qt::TabFocus + + + + + + + + + + + 120 + 16777215 + + + + PICus + + + + + + + + 100 + 0 + + + + + 120 + 16777215 + + + + Qt::TabFocus + + + + + + + + 120 + 16777215 + + + + SP SE + + + + + + + + 100 + 0 + + + + + 120 + 16777215 + + + + Qt::TabFocus + + + + + + + + 120 + 16777215 + + + + IFR + + + + + + + + 100 + 0 + + + + + 120 + 16777215 + + + + Qt::TabFocus + + + + + + + + 120 + 16777215 + + + + SP ME + + + + + + + + 100 + 0 + + + + + 120 + 16777215 + + + + Qt::TabFocus + + + + + + + + 120 + 16777215 + + + + Night + + + + + + + + 100 + 0 + + + + + 120 + 16777215 + + + + Qt::TabFocus + + + + + + + + 120 + 16777215 + + + + Multi Pilot + + + + + + + + 100 + 0 + + + + + 120 + 16777215 + + + + Qt::TabFocus + + + + + + + + 120 + 16777215 + + + + Simulator + + + + + + + + 100 + 0 + + + + + 120 + 16777215 + + + + Qt::TabFocus + + + + + + + + 120 + 16777215 + + + + PIC + + + + + + + + 100 + 0 + + + + + 120 + 16777215 + + + + Qt::TabFocus + + + + + + + + 120 + 16777215 + + + + TO Day + + + + + + + + 100 + 0 + + + + + 120 + 16777215 + + + + Qt::TabFocus + + + 0 + + + + + + + + 120 + 16777215 + + + + SIC + + + + + + + + 100 + 0 + + + + + 120 + 16777215 + + + + Qt::TabFocus + + + + + + + + 120 + 16777215 + + + + TO Night + + + + + + + + 100 + 0 + + + + + 120 + 16777215 + + + + Qt::TabFocus + + + 0 + + + + + + + + 120 + 16777215 + + + + DUAL + + + + + + + + 100 + 0 + + + + + 120 + 16777215 + + + + Qt::TabFocus + + + + + + + + 120 + 16777215 + + + + LDG Day + + + + + + + + 100 + 0 + + + + + 120 + 16777215 + + + + Qt::TabFocus + + + 0 + + + + + + + + 120 + 16777215 + + + + FI + + + + + + + + 100 + 0 + + + + + 120 + 16777215 + + + + Qt::TabFocus + + + + + + + + 120 + 16777215 + + + + LDG Night + + + + + + + + 100 + 0 + + + + + 120 + 16777215 + + + + Qt::TabFocus + + + 0 + +
    diff --git a/src/opl.h b/src/opl.h index 3a09669d..357f551d 100644 --- a/src/opl.h +++ b/src/opl.h @@ -70,16 +70,22 @@ namespace OPL { /** * @brief Defines the row ID for non-user entries in the database; */ -const static int STUB_ROW_ID = -1; +constexpr static int STUB_ROW_ID = -1; /** * @brief Defines a four-letter code for a non-extistent (dummy) airport: "XXXX" */ -const static char* STUB_AIRPORT_CODE = "XXXX"; +constexpr static auto STUB_AIRPORT_CODE = QLatin1String("XXXX"); /** * @brief Defines a registration for a non-existent (dummy) aircraft: "XX-XXX" */ -const static char* STUB_AIRCRAFT_REG = "XX-XXX"; +constexpr static auto STUB_AIRCRAFT_REG = QLatin1String("XX-XXX"); + +/*! + * \brief The decimal seperator used internally + */ +constexpr static char DECIMAL_SEPERATOR = '.'; + /*! * \brief The ANotificationHandler class handles displaying of user-directed messages. It displays @@ -118,6 +124,63 @@ struct ToLdgCount_T { : toDay(toDay), toNight(toNight), ldgDay(ldgDay), ldgNight(ldgNight) {} }; +/*! + * \brief The DateFormat struct encapsulates how date and time values are displayed. + * \details Stores how the user wishes to display and enter Date and Time Entries. + * These are stored numerically in the database and thus need to be converted to + * human-readably form. + */ +struct DateTimeFormat { + /*! + * \brief Enumerates how dates can be formatted to a localised format. + * \value Default - The Application standard, Equivalent to Qt::ISODate + * \value SystemLocale - The current system locale date format + */ + enum class DateFormat { Default, SystemLocale, Custom }; + + /*! + * \brief Enumerates how time values can be formatted + * \value Default - The application default is 'hh:mm' + * \value Decimal - Time as Decmial hours (01:30 == 1.5) + * \value Custom - A user-provided custom format string + */ + enum class TimeFormat { Default, Decimal, Custom }; + + /*! + * \brief Initialise a DateTimeFormat instance with default values + */ + DateTimeFormat() + : m_dateFormat(DateFormat::Default), + m_dateFormatString(QStringLiteral("yyyy-MM-dd")), + m_timeFormat(TimeFormat::Default), + m_timeFormatString(QStringLiteral("hh:mm")) + {} + + DateTimeFormat(DateFormat dateFormat_, + const QString &dateFormatString_, + TimeFormat timeFormat_, + const QString &timeFormatString_) + : + m_dateFormat(dateFormat_), + m_dateFormatString(dateFormatString_), + m_timeFormat(timeFormat_), + m_timeFormatString(timeFormatString_) + {} + + +public: + DateFormat dateFormat() const { return m_dateFormat; } + TimeFormat timeFormat() const { return m_timeFormat; } + const QString &dateFormatString() const { return m_dateFormatString; } + const QString &timeFormatString() const { return m_timeFormatString; } + +private: + DateFormat m_dateFormat; + TimeFormat m_timeFormat; + QString m_dateFormatString; + QString m_timeFormatString; +}; + /*! * \brief ADateFormats enumerates the accepted date formats for QDateEdits * \todo At the moment, only ISODate is accepet as a valid date format. @@ -126,7 +189,7 @@ enum class DateFormat {ISODate, DE, EN }; enum class FlightTimeFormat {Default, Decimal}; -enum class DateTimeFormat {Default, Backup}; +enum class DateTimeFormat_deprecated {Default, Backup}; /*! * \brief PilotFunction @@ -142,7 +205,7 @@ enum class Translation {English, German, Spanish}; /*! * \brief Enumerates the available SQL views in the database */ -enum class DbViewName {Default, DefaultWithSim, Easa, EasaWithSim, SimulatorOnly}; +enum class LogbookView {Default, DefaultWithSim, Easa, EasaWithSim, SimulatorOnly}; /*! * \brief Enumerates the Simulator Types: Flight and Navigation Procedures Trainer 1/2, Flight Simulation Training Device @@ -176,7 +239,7 @@ class OplGlobals : public QObject { inline const QStringList &getApproachTypes() const {return APPROACH_TYPES;} inline const QString getLanguageFilePath(Translation language) const {return L10N_FilePaths.value(language);} - inline const QString getViewIdentifier(DbViewName view_name) const {return DATABASE_VIEWS.value(view_name);} + inline const QString getViewIdentifier(LogbookView view_name) const {return DATABASE_VIEWS.value(view_name);} inline const QString getDbTableName(DbTable table_name) const {return DB_TABLES.value(table_name);} private: @@ -191,19 +254,19 @@ class OplGlobals : public QObject { {Translation::German, QStringLiteral("Deutsch")}, {Translation::Spanish, QStringLiteral("Español")}, }; - const static inline QMap DATABASE_VIEWS = { - {DbViewName::Default, QStringLiteral("viewDefault")}, - {DbViewName::DefaultWithSim, QStringLiteral("viewDefaultSim")}, - {DbViewName::Easa, QStringLiteral("viewEasa")}, - {DbViewName::EasaWithSim, QStringLiteral("viewEasaSim")}, - {DbViewName::SimulatorOnly, QStringLiteral("viewSimulators")}, + const static inline QMap DATABASE_VIEWS = { + {LogbookView::Default, QStringLiteral("viewDefault")}, + {LogbookView::DefaultWithSim, QStringLiteral("viewDefaultSim")}, + {LogbookView::Easa, QStringLiteral("viewEasa")}, + {LogbookView::EasaWithSim, QStringLiteral("viewEasaSim")}, + {LogbookView::SimulatorOnly, QStringLiteral("viewSimulators")}, }; - const QMap DATABASE_VIEW_DISPLAY_NAMES = { - {DbViewName::Default, tr("Default")}, - {DbViewName::DefaultWithSim, tr("Default with Simulator")}, - {DbViewName::Easa, tr("EASA-FCL")}, - {DbViewName::EasaWithSim, tr("EASA-FCL with Simulator")}, - {DbViewName::SimulatorOnly, tr("Simulator Sessions Only")}, + const QMap DATABASE_VIEW_DISPLAY_NAMES = { + {LogbookView::Default, tr("Default")}, + {LogbookView::DefaultWithSim, tr("Default with Simulator")}, + {LogbookView::Easa, tr("EASA-FCL")}, + {LogbookView::EasaWithSim, tr("EASA-FCL with Simulator")}, + {LogbookView::SimulatorOnly, tr("Simulator Sessions Only")}, }; const static inline QMap PILOT_FUNCTIONS = { {PilotFunction::PIC, QStringLiteral("PIC")}, @@ -265,7 +328,6 @@ const inline auto DATABASE_SCHEMA = QStringLiteral(":/database/da const inline auto DATABASE_TEMPLATE_AIRCRAFT = QStringLiteral(":/database/templates/aircraft.json"); const inline auto DATABASE_TEMPLATE_AIRPORT = QStringLiteral(":/database/templates/airports.json"); const inline auto DATABASE_TEMPLATE_CHANGELOG = QStringLiteral(":/database/templates/changelog.json"); -const inline auto DATABASE_TEMPLATE_CURRENCIES = QStringLiteral(":/database/templates/currencies.json"); const inline auto LOGO = QStringLiteral(":/icons/opl-icons/logos/logo_text.png"); const inline auto ICON_MAIN = QStringLiteral(":/icons/opl-icons/app/icon_main.png"); @@ -300,17 +362,16 @@ namespace CssStyles { const inline auto RED_BORDER = QStringLiteral("border: 1px solid red"); } // namespace Styles -namespace Format { +//namespace Format { -const inline auto TIME_FORMAT = QStringLiteral("hh:mm"); +//const inline auto TIME_FORMAT = QStringLiteral("hh:mm"); -} // namespace Format +//} // namespace Format namespace RegEx { const inline auto RX_PHONE_NUMBER = QRegularExpression(QStringLiteral("^[+]{0,1}[0-9\\-\\s]+")); -const inline auto RX_EMAIL_ADDRESS = QRegularExpression(QStringLiteral("\\A[a-z0-9!#$%&'*+/=?^_‘{|}~-]+(?:\\.[a-z0-9!#$%&'*+/=?^_‘{|}~-]+)*@")); -const inline auto RX_TIME_ENTRY = QRegularExpression(QStringLiteral("([01]?[0-9]|2[0-3]):?[0-5][0-9]?")); +const inline auto RX_TIME_ENTRY = QRegularExpression(QStringLiteral("^(?:(?:([01]?\\d|2[0-3])(?::?)([0-5]\\d))|(?:([01]?\\d|2[0-3])([0-5]\\d))|(?:([1-9]|[1-9]\\d)\\:([0-5]\\d)?)|(?:([01]?\\d|2[0-3])\\.([0-5]?\\d)))$")); const inline auto RX_AIRPORT_CODE = QRegularExpression(QStringLiteral("[a-zA-Z0-9]{1,4}")); } // namespace RegEx diff --git a/src/testing/importCrewlounge/processflights.cpp b/src/testing/importCrewlounge/processflights.cpp index d1fe2c2b..4d760b77 100644 --- a/src/testing/importCrewlounge/processflights.cpp +++ b/src/testing/importCrewlounge/processflights.cpp @@ -54,14 +54,14 @@ void ProcessFlights::processParsedData() auto time_off = QTime::fromString(row[4], QStringLiteral("hh:mm")); if (!time_off.isValid()) time_off = QTime::fromString(row[4], QStringLiteral("h:mm")); - int tofb = OPL::Time::fromString(time_off.toString()).toMinutes(); + int tofb = OPL::Time::fromString(time_off.toString(), OPL::DateTimeFormat()).toMinutes(); new_flight_data.insert(OPL::FlightEntry::TOFB, tofb); auto time_on = QTime::fromString(row[5], QStringLiteral("hh:mm")); if (!time_on.isValid()) time_on = QTime::fromString(row[5], QStringLiteral("h:mm")); - int tonb = OPL::Time::fromString(time_on.toString()).toMinutes(); + int tonb = OPL::Time::fromString(time_on.toString(), OPL::DateTimeFormat()).toMinutes(); new_flight_data.insert(OPL::FlightEntry::TONB, tonb); // map pilots diff --git a/src/testing/randomgenerator.cpp b/src/testing/randomgenerator.cpp index fbba29ac..fd47a349 100644 --- a/src/testing/randomgenerator.cpp +++ b/src/testing/randomgenerator.cpp @@ -19,8 +19,8 @@ const FlightEntry RandomGenerator::randomFlight() const QDateTime dest_dt = dept_dt.addSecs(QRandomGenerator::global()->bounded(900, 50000)); const QString doft = dept_dt.date().toString(Qt::ISODate); - OPL::Time tofb = OPL::Time::fromString(dept_dt.time().toString()); - OPL::Time tonb = OPL::Time::fromString(dest_dt.time().toString()); + OPL::Time tofb = OPL::Time::fromString(dept_dt.time().toString(Qt::ISODate), OPL::DateTimeFormat()); + OPL::Time tonb = OPL::Time::fromString(dest_dt.time().toString(Qt::ISODate), OPL::DateTimeFormat()); int pic = randomPilot(); int acft = randomTail();