/* *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 "tailswidget.h" #include "src/classes/time.h" #include "ui_aircraftwidget.h" #include "src/opl.h" #include "src/classes/settings.h" #include "src/database/database.h" #include "src/gui/dialogues/newtaildialog.h" TailsWidget::TailsWidget(QWidget *parent) : QWidget(parent), ui(new Ui::AircraftWidget) { ui->setupUi(this); ui->tableView->setMinimumWidth(this->width()/2); ui->stackedWidget->setMinimumWidth(this->width()/2); setupModelAndView(); } TailsWidget::~TailsWidget() { delete ui; } void TailsWidget::setupModelAndView() { model = new QSqlTableModel(this); model->setTable(QStringLiteral("viewTails")); model->select(); view = ui->tableView; view->setModel(model); view->setSelectionBehavior(QAbstractItemView::SelectRows); view->setSelectionMode(QAbstractItemView::SingleSelection); // For now, editing multiple entries is not supported. view->setEditTriggers(QAbstractItemView::NoEditTriggers); view->horizontalHeader()->setStretchLastSection(QHeaderView::Stretch); view->hideColumn(0); view->resizeColumnsToContents(); view->verticalHeader()->hide(); view->setAlternatingRowColors(true); sortColumn = Settings::getTailSortColumn(); view->setSortingEnabled(true); view->sortByColumn(sortColumn, Qt::DescendingOrder); view->show(); selection = view->selectionModel(); connectSignalsAndSlots(); } void TailsWidget::connectSignalsAndSlots() { QObject::connect(ui->tableView->selectionModel(), &QItemSelectionModel::selectionChanged, this, &TailsWidget::tableView_selectionChanged); QObject::connect(ui->tableView->horizontalHeader(), &QHeaderView::sectionClicked, this, &TailsWidget::tableView_headerClicked); } /* * Slots */ void TailsWidget::onAircraftWidget_settingChanged(SettingsWidget::SettingSignal signal) { if (signal != SettingsWidget::AircraftWidget) return; setupModelAndView(); } void TailsWidget::onAircraftWidget_dataBaseUpdated() { refreshView(); } void TailsWidget::changeEvent(QEvent *event) { if (event != nullptr) if(event->type() == QEvent::LanguageChange) ui->retranslateUi(this); } void TailsWidget::on_newAircraftButton_clicked() { NewTailDialog nt(QString(), this); setUiEnabled(false); nt.exec(); refreshView(); setUiEnabled(true); } /*! * \brief Displays a dialog in which the Tail can be edited */ void TailsWidget::tableView_selectionChanged() { if (this->findChild() != nullptr) delete this->findChild(); selectedTails.clear(); const auto selected_rows = selection->selectedRows(); for (const auto& row : selected_rows) { selectedTails << row.data().toInt(); DEB << "Selected Tails(s) with ID: " << selectedTails; } if(selectedTails.length() == 1) { NewTailDialog nt(selectedTails.first(), this); nt.setWindowFlag(Qt::Widget); ui->stackedWidget->addWidget(&nt); ui->stackedWidget->setCurrentWidget(&nt); setUiEnabled(false); nt.exec(); refreshView(); setUiEnabled(true); } } /*! * \brief Acts as a filter on the display model */ void TailsWidget::on_aircraftSearchLineEdit_textChanged(const QString &arg1) { if(ui->aircraftSearchComboBox->currentIndex() == 0){ ui->aircraftSearchLineEdit->setText(arg1.toUpper()); } model->setFilter(ui->aircraftSearchComboBox->currentText() + QLatin1String(" LIKE \"%") + arg1 + QLatin1String("%\"")); } void TailsWidget::tableView_headerClicked(int column) { Settings::setTailSortColumn(column); } void TailsWidget::on_deleteAircraftButton_clicked() { if (selectedTails.length() == 0) { INFO(tr("No Aircraft selected.")); } else if (selectedTails.length() > 1) { WARN(tr("Deleting multiple entries is currently not supported")); /// [F] to do: for (const auto& row_id : selectedPilots) { do batchDelete } /// I am not sure if enabling this functionality for this widget is a good idea. /// On the one hand, deleting many entries could be useful in a scenario where /// for example, the user has changed airlines and does not want to have his 'old' /// colleagues polluting his logbook anymore. /// On the other hand we could run into issues with foreign key constraints on the /// flights table (see on_delete_unsuccessful) below. /// I think batch-editing should be implemented at some point, but batch-deleting should not. } else if (selectedTails.length() == 1) { auto entry = DB->getTailEntry(selectedTails.first()); QMessageBox message_box(this); message_box.setStandardButtons(QMessageBox::Yes | QMessageBox::No); message_box.setDefaultButton(QMessageBox::No); message_box.setIcon(QMessageBox::Question); message_box.setWindowTitle(tr("Delete Aircraft")); message_box.setText(tr("You are deleting the following aircraft:

" "%1 - (%2)

Are you sure?" ).arg(entry.getData().value(OPL::TailEntry::REGISTRATION).toString(), getAircraftTypeString(entry))); if (message_box.exec() == QMessageBox::Yes) { if(!DB->remove(entry)) onDeleteUnsuccessful(); } } refreshView(); ui->stackedWidget->setCurrentIndex(0); ui->aircraftSearchLineEdit->setText(QString()); } /*! * \brief Informs the user that deleting a database entry 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 aircraft that was supposed to be deleted. * * This function is used to inform the user and give hints on how to solve the problem. */ void TailsWidget::onDeleteUnsuccessful() { QList foreign_key_constraints = DB->getForeignKeyConstraints(selectedTails.first(), 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()) { message_box.setText(tr("
Unable to delete.

The following error has ocurred: %1" ).arg(DB->lastError.text())); message_box.exec(); return; } else { QString constrained_flights_string; for (int i=0; i")); if (i>10) { constrained_flights_string.append(QLatin1String("
[...]
")); break; } } message_box.setText(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)); message_box.setIcon(QMessageBox::Critical); message_box.exec(); } } void TailsWidget::setUiEnabled(bool enabled) { ui->newAircraftButton->setEnabled(enabled); ui->deleteAircraftButton->setEnabled(enabled); ui->tableView->setEnabled(enabled); ui->aircraftSearchComboBox->setEnabled(enabled); ui->aircraftSearchComboBox->setEnabled(enabled); } void TailsWidget::repopulateModel() { // unset the current model and delete it to avoid leak view->setModel(nullptr); delete model; // create a new model and populate it model = new QSqlTableModel(this); setupModelAndView(); connectSignalsAndSlots(); } const QString TailsWidget::getAircraftTypeString(const OPL::Row &row) const { QString type_string; if (!row.getData().value(OPL::TailEntry::MAKE).toString().isEmpty()) type_string.append(row.getData().value(OPL::TailEntry::MAKE).toString() + QLatin1Char(' ')); if (!row.getData().value(OPL::TailEntry::MODEL).toString().isEmpty()) type_string.append(row.getData().value(OPL::TailEntry::MODEL).toString()); if (!row.getData().value(OPL::TailEntry::VARIANT).toString().isEmpty()) type_string.append(QLatin1Char('-') + row.getData().value(OPL::TailEntry::VARIANT).toString()); return type_string; } const QString TailsWidget::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; }