/*
*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 "pilotswidget.h"
#include "ui_pilotswidget.h"
#include "src/gui/dialogues/newpilotdialog.h"
#include "src/opl.h"
#include "src/database/database.h"
#include "src/database/row.h"
#include "src/classes/time.h"
#include "src/classes/settings.h"
PilotsWidget::PilotsWidget(QWidget *parent) :
QWidget(parent),
ui(new Ui::PilotsWidget)
{
ui->setupUi(this);
ui->tableView->setMinimumWidth(this->width()/2);
ui->stackedWidget->setMinimumWidth(this->width()/2);
setupModelAndView();
}
PilotsWidget::~PilotsWidget()
{
delete ui;
}
void PilotsWidget::setupModelAndView()
{
model = new QSqlTableModel(this);
model->setTable(QStringLiteral("viewPilots"));
model->setFilter(QStringLiteral("ID > 1")); // Don't show self
model->select();
view = ui->tableView;
view->setModel(model);
view->setSelectionBehavior(QAbstractItemView::SelectRows);
view->setSelectionMode(QAbstractItemView::SingleSelection);
view->setEditTriggers(QAbstractItemView::NoEditTriggers);
view->horizontalHeader()->setStretchLastSection(QHeaderView::Stretch);
view->hideColumn(0);
view->resizeColumnsToContents();
view->verticalHeader()->hide();
view->setAlternatingRowColors(true);
view->setSortingEnabled(true);
sortColumn = Settings::getPilotSortColumn();
view->sortByColumn(sortColumn, Qt::AscendingOrder);
view->show();
selectionModel = view->selectionModel();
connectSignalsAndSlots();
}
void PilotsWidget::connectSignalsAndSlots()
{
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)
{
ui->tableView->setEnabled(enabled);
ui->newPilotButton->setEnabled(enabled);
ui->deletePilotButton->setEnabled(enabled);
ui->pilotSearchLineEdit->setEnabled(enabled);
ui->pilotsSearchComboBox->setEnabled(enabled);
}
void PilotsWidget::changeEvent(QEvent *event)
{
if (event != nullptr)
if(event->type() == QEvent::LanguageChange)
ui->retranslateUi(this);
}
void PilotsWidget::onPilotsWidget_settingChanged(SettingsWidget::SettingSignal signal)
{
if (signal == SettingsWidget::PilotsWidget)
setupModelAndView();
}
void PilotsWidget::onPilotsWidget_databaseUpdated()
{
refreshView();
}
void PilotsWidget::filterLineEdit_textChanged(const QString &arg1)
{
model->setFilter(QLatin1Char('\"') + ui->pilotsSearchComboBox->currentText()
+ QLatin1String("\" LIKE '%") + arg1
+ QLatin1String("%' AND ID > 1"));
}
void PilotsWidget::editRequested(const QModelIndex &index)
{
int pilotID = model->index(index.row(), 0).data().toInt();
NewPilotDialog np = NewPilotDialog(pilotID, this);
np.setWindowFlag(Qt::Widget);
ui->stackedWidget->addWidget(&np);
ui->stackedWidget->setCurrentWidget(&np);
np.exec();
refreshView();
}
void PilotsWidget::newSortColumnSelected(int newSortColumn)
{
Settings::setPilotSortColumn(newSortColumn);
}
void PilotsWidget::on_newPilotButton_clicked()
{
NewPilotDialog np = NewPilotDialog(this);
np.exec();
refreshView();
}
void PilotsWidget::on_deletePilotButton_clicked()
{
if (selectedPilots.length() == 0) {
INFO(tr("No Pilot selected."));
} else if (selectedPilots.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 (selectedPilots.length() == 1) {
auto entry = DB->getPilotEntry(selectedPilots.first());
QMessageBox confirm(this);
confirm.setStandardButtons(QMessageBox::Yes | QMessageBox::No);
confirm.setDefaultButton(QMessageBox::No);
confirm.setIcon(QMessageBox::Question);
confirm.setWindowTitle(tr("Delete Pilot"));
confirm.setText(tr("You are deleting the following pilot:
"
"%1
Are you sure?").arg(getPilotName(entry)));
if (confirm.exec() == QMessageBox::Yes) {
if(!DB->remove(entry))
onDeleteUnsuccessful();
}
}
refreshView();
ui->stackedWidget->setCurrentIndex(0);
ui->pilotSearchLineEdit->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 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.
*/
void PilotsWidget::onDeleteUnsuccessful()
{
const QList foreign_key_constraints = DB->getForeignKeyConstraints(selectedPilots.first(),
OPL::DbTable::Pilots);
QList constrained_flights;
for (const auto &row_id : foreign_key_constraints) {
constrained_flights.append(DB->getFlightEntry(row_id));
}
if (constrained_flights.isEmpty()) {
WARN(tr("
Unable to delete.
The following error has ocurred:
%1"
).arg(DB->lastError.text()));
return;
} else {
QString constrained_flights_string;
for (int i=0; i"));
if (i>10) {
constrained_flights_string.append("
[...]
");
break;
}
}
WARN(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));
}
}
void PilotsWidget::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 PilotsWidget::getPilotName(const OPL::PilotEntry &pilot) const
{
if (!pilot.isValid())
return QString();
return pilot.getLastName() + QLatin1String(", ") + pilot.getFirstName();
}