/* *openPilotLog - A FOSS Pilot Logbook Application *Copyright (C) 2020-2021 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 "aircraftwidget.h" #include "ui_aircraftwidget.h" #include "src/gui/dialogues/newtaildialog.h" #include "src/classes/asettings.h" #include "src/database/adatabase.h" #include "src/classes/atailentry.h" #include "src/classes/aflightentry.h" #include "src/functions/alog.h" AircraftWidget::AircraftWidget(QWidget *parent) : QWidget(parent), ui(new Ui::AircraftWidget) { ui->setupUi(this); ui->tableView->setMinimumWidth(this->width()/2); ui->stackedWidget->setMinimumWidth(this->width()/2); setupModelAndView(); } AircraftWidget::~AircraftWidget() { delete ui; } void AircraftWidget::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 = ASettings::read(ASettings::UserData::TailSortColumn).toInt(); view->setSortingEnabled(true); view->sortByColumn(sortColumn, Qt::DescendingOrder); view->show(); selection = view->selectionModel(); connectSignalsAndSlots(); } void AircraftWidget::connectSignalsAndSlots() { QObject::connect(ui->tableView->selectionModel(), &QItemSelectionModel::selectionChanged, this, &AircraftWidget::tableView_selectionChanged); QObject::connect(ui->tableView->horizontalHeader(), &QHeaderView::sectionClicked, this, &AircraftWidget::tableView_headerClicked); } /* * Slots */ void AircraftWidget::onAircraftWidget_settingChanged(SettingsWidget::SettingSignal signal) { if (signal != SettingsWidget::AircraftWidget) return; setupModelAndView(); } void AircraftWidget::onAircraftWidget_dataBaseUpdated() { refreshView(); } void AircraftWidget::onNewTailDialog_editingFinished() { refreshView(); } void AircraftWidget::on_newAircraftButton_clicked() { NewTailDialog nt(QString(), this); QObject::connect(&nt, &QDialog::accepted, this, &AircraftWidget::onNewTailDialog_editingFinished); QObject::connect(&nt, &QDialog::rejected, this, &AircraftWidget::onNewTailDialog_editingFinished); nt.exec(); } /*! * \brief Displays a dialog in which the Tail can be edited */ void AircraftWidget::tableView_selectionChanged() { if (this->findChild() != nullptr) delete this->findChild(); selectedTails.clear(); for (const auto& row : selection->selectedRows()) { selectedTails << row.data().toInt(); DEB << "Selected Tails(s) with ID: " << selectedTails; } if(selectedTails.length() == 1) { auto* nt = new NewTailDialog(selectedTails.first(), this); QObject::connect(nt, &QDialog::accepted, this, &AircraftWidget::onNewTailDialog_editingFinished); QObject::connect(nt, &QDialog::rejected, this, &AircraftWidget::onNewTailDialog_editingFinished); ui->stackedWidget->addWidget(nt); ui->stackedWidget->setCurrentWidget(nt); nt->setWindowFlag(Qt::Widget); nt->setAttribute(Qt::WA_DeleteOnClose); nt->exec(); } } /*! * \brief Acts as a filter on the display model */ void AircraftWidget::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 AircraftWidget::tableView_headerClicked(int column) { sortColumn = column; ASettings::write(ASettings::UserData::TailSortColumn, column); } void AircraftWidget::on_deleteAircraftButton_clicked() { if (selectedTails.length() == 0) { QMessageBox message_box(this); message_box.setText(tr("No Aircraft selected.")); message_box.exec(); } else if (selectedTails.length() > 1) { QMessageBox message_box(this); message_box.setText(tr("Deleting multiple entries is currently not supported")); message_box.exec(); /// [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 = aDB->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.registration(), entry.type())); if (message_box.exec() == QMessageBox::Yes) { if(!aDB->remove(entry)) onDeleteUnsuccessful(); } } refreshView(); } /*! * \brief Informs the user that deleting a database entry has been unsuccessful * * \abstract 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 AircraftWidget::onDeleteUnsuccessful() { QList foreign_key_constraints = aDB->getForeignKeyConstraints(selectedTails.first(), ADatabaseTarget::tails); QList constrained_flights; for (const auto &row_id : qAsConst(foreign_key_constraints)) { constrained_flights.append(aDB->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(aDB->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 AircraftWidget::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(); }