/* *openPilot Log - A FOSS Pilot Logbook Application *Copyright (C) 2020 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 "newtaildialog.h" #include "ui_newtail.h" #include "src/testing/adebug.h" static const auto REG_VALID = QPair { "registrationLineEdit", QRegularExpression("\\w+-\\w+")}; static const auto MAKE_VALID = QPair { "makeLineEdit", QRegularExpression("[-a-zA-Z\\s]+")}; static const auto MODEL_VALID = QPair { "modelLineEdit", QRegularExpression("[\\s\\w-]+")}; static const auto VARIANT_VALID = QPair { "variantLineEdit", QRegularExpression("[\\s\\w-]+")}; static const auto LINE_EDIT_VALIDATORS = QVector({REG_VALID, MAKE_VALID, MODEL_VALID, VARIANT_VALID}); NewTailDialog::NewTailDialog(QString new_registration, QWidget *parent) : QDialog(parent), ui(new Ui::NewTail) { DEB("new NewTailDialog (experimental)"); ui->setupUi(this); connectSignals(); setupCompleter(); setupValidators(); ui->registrationLineEdit->setText(new_registration); ui->searchLineEdit->setStyleSheet("border: 1px solid blue"); ui->searchLineEdit->setFocus(); entry = experimental::ATailEntry(); } NewTailDialog::NewTailDialog(int row_id, QWidget *parent) : QDialog(parent), ui(new Ui::NewTail) { using namespace experimental; DEB("New experimental New Pilot Dialog (edit existing)"); ui->setupUi(this); ui->searchLabel->hide(); ui->searchLineEdit->hide(); ui->line->hide(); connectSignals(); setupValidators(); entry = aDB()->getTailEntry(row_id); fillForm(entry); } NewTailDialog::~NewTailDialog() { DEB("Deleting NewTailDialog\n"); delete ui; } /// Functions /*! * \brief NewTail::setupCompleter obtains a QMap for auto completion * and obtains a QStringList for QCompleter. This function then sets up the search line edit where * the user can select a template from the aircraft database to pre-fill the form with the details * for the selected type. */ void NewTailDialog::setupCompleter() { using namespace experimental; idMap = aDB()->getIdMap(ADataBase::aircraft); aircraftList = aDB()->getCompletionList(experimental::ADataBase::aircraft); QCompleter *completer = new QCompleter(aircraftList, ui->searchLineEdit); completer->setCaseSensitivity(Qt::CaseInsensitive); completer->setCompletionMode(QCompleter::PopupCompletion); completer->setFilterMode(Qt::MatchContains); ui->searchLineEdit->setCompleter(completer); QObject::connect(completer, static_cast(&QCompleter::activated), this, &NewTailDialog::onSearchCompleterActivated); QObject::connect(completer, static_cast(&QCompleter::highlighted), this, &NewTailDialog::onSearchCompleterActivated); } void NewTailDialog::setupValidators() { for(const auto& pair : LINE_EDIT_VALIDATORS){ auto line_edit = this->findChild(pair.first); auto validator = new QRegularExpressionValidator(pair.second, line_edit); line_edit->setValidator(validator); } } void NewTailDialog::connectSignals() { using namespace experimental; QObject::connect(aDB(), &ADataBase::sqlSuccessful, this, &NewTailDialog::onCommitSuccessful); QObject::connect(aDB(), &ADataBase::sqlError, this, &NewTailDialog::onCommitUnsuccessful); } /*! * \brief NewTailDialog::fillForm populates the Dialog with the * information contained in an entry object. This can be either * a template (AAircraft, used when creating a new entry) or * a tail (ATail, used when editing an existing entry) * \param entry */ void NewTailDialog::fillForm(experimental::AEntry entry) { DEB("Filling Form for (experimental) a/c" << entry.getPosition()); //fill Line Edits auto line_edits = this->findChildren(); for (const auto &le : line_edits) { QString name = le->objectName().remove("LineEdit"); QString value = entry.getData().value(name); le->setText(value); } //select comboboxes QVector operation = {entry.getData().value("singleengine"), entry.getData().value("multiengine")}; QVector ppNumber = {entry.getData().value("singlepilot"), entry.getData().value("multipilot")}; QVector ppType = {entry.getData().value("unpowered"), entry.getData().value("piston"), entry.getData().value("turboprop"), entry.getData().value("jet") }; QVector weight = {entry.getData().value("light"), entry.getData().value("medium"), entry.getData().value("heavy"), entry.getData().value("super") }; ui->operationComboBox->setCurrentIndex(operation.indexOf("1") + 1); ui->ppNumberComboBox->setCurrentIndex(ppNumber.indexOf("1") + 1); ui->ppTypeComboBox->setCurrentIndex(ppType.indexOf("1") + 1); ui->weightComboBox->setCurrentIndex(weight.indexOf("1") + 1); } /*! * \brief NewTail::verify A simple check for empty recommended fields in the form * \return true if all reconmmended fields are populated */ bool NewTailDialog::verify() { auto recommended_line_edits = this->findChildren("registrationLineEdit"); recommended_line_edits.append(this->findChild("makeLineEdit")); recommended_line_edits.append(this->findChild("modelLineEdit")); auto recommended_combo_boxes = this->findChildren("operationComboBox"); recommended_combo_boxes.append(this->findChild("ppNumberComboBox")); recommended_combo_boxes.append(this->findChild("ppTypeComboBox")); for (const auto &le : recommended_line_edits) { if (le->text() != "") { DEB("Good: " << le); recommended_line_edits.removeOne(le); le->setStyleSheet(""); } else { le->setStyleSheet("border: 1px solid red"); DEB("Not Good: " << le); } } for (const auto &cb : recommended_combo_boxes) { if (cb->currentIndex() != 0) { recommended_combo_boxes.removeOne(cb); cb->setStyleSheet(""); } else { cb->setStyleSheet("background: orange"); DEB("Not Good: " << cb); } } if (recommended_line_edits.isEmpty() && recommended_combo_boxes.isEmpty()) { return true; } else { return false; } } /*! * \brief NewTail::submitForm collects input from Line Edits and creates * or updates a database entry and commits or updates the database * \param edRole editExisting or createNew */ void NewTailDialog::submitForm() { DEB("Creating Database Object..."); using namespace experimental; TableData new_data; //retreive Line Edits auto line_edits = this->findChildren(); line_edits.removeOne(this->findChild("searchLineEdit")); for (const auto &le : line_edits) { QString name = le->objectName().remove("LineEdit"); new_data.insert(name, le->text()); } //prepare comboboxes QVector operation = {"singlepilot", "multipilot"}; QVector ppNumber = {"singleengine", "multiengine"}; QVector ppType = {"unpowered", "piston", "turboprop", "jet" }; QVector weight = {"light", "medium", "heavy", "super" }; if (ui->operationComboBox->currentIndex() != 0) { new_data.insert(operation[ui->operationComboBox->currentIndex() - 1], QLatin1String("1")); } if (ui->ppNumberComboBox->currentIndex() != 0) { new_data.insert(ppNumber[ui->ppNumberComboBox->currentIndex() - 1], QLatin1String("1")); } if (ui->ppTypeComboBox->currentIndex() != 0) { new_data.insert(ppType[ui->ppTypeComboBox->currentIndex() - 1], QLatin1String("1")); } if (ui->weightComboBox->currentIndex() != 0) { new_data.insert(weight[ui->weightComboBox->currentIndex() - 1], QLatin1String("1")); } //create db object entry.setData(new_data); aDB()->commit(entry); } /// Slots void NewTailDialog::on_operationComboBox_currentIndexChanged(int index) { if (index != 0) { ui->operationComboBox->setStyleSheet(""); } } void NewTailDialog::on_ppTypeComboBox_currentIndexChanged(int index) { if (index != 0) { ui->ppTypeComboBox->setStyleSheet(""); } } void NewTailDialog::on_ppNumberComboBox_currentIndexChanged(int index) { if (index != 0) { ui->ppNumberComboBox->setStyleSheet(""); } } void NewTailDialog::on_weightComboBox_currentIndexChanged(int index) { if (index != 0) { ui->weightComboBox->setStyleSheet(""); } } void NewTailDialog::on_buttonBox_accepted() { DEB("Button Box Accepted."); if (ui->registrationLineEdit->text().isEmpty()) { auto nope = QMessageBox(this); nope.setText("Registration cannot be empty."); nope.exec(); return; } if (!verify()) { if (!ASettings::read("userdata/acAllowIncomplete").toInt()) { auto nope = QMessageBox(this); nope.setIcon(QMessageBox::Warning); nope.setText("Some or all recommended fields are empty.\nPlease go back and " "complete the form.\n\nYou can allow logging incomplete tail entries on the settings page."); nope.exec(); return; } else { QMessageBox::StandardButton reply; reply = QMessageBox::question(this, "Warning", "Some recommended fields are empty.\n\n" "If you do not fill out the aircraft details, " "it will be impossible to automatically determine Single/Multi Pilot Times or Single/Multi Engine Time." "This will also impact statistics and auto-logging capabilites.\n\n" "It is highly recommended to fill in all the details.\n\n" "Are you sure you want to proceed?", QMessageBox::Yes | QMessageBox::No); if (reply == QMessageBox::Yes) { submitForm(); } } } DEB("Form verified"); submitForm(); } void NewTailDialog::onSearchCompleterActivated() { DEB("Search completer activated!"); const auto &text = ui->searchLineEdit->text(); if (aircraftList.contains(text)) { DEB("Template Selected. aircraft_id is: " << idMap.value(text)); //call autofiller for dialog using namespace experimental; fillForm(aDB()->getAircraftEntry(idMap.value(text))); ui->searchLineEdit->setStyleSheet("border: 1px solid green"); ui->searchLabel->setText(text); } else { //for example, editing finished without selecting a result from Qcompleter ui->searchLineEdit->setStyleSheet("border: 1px solid orange"); } } void NewTailDialog::on_registrationLineEdit_textChanged(const QString &arg1) { ui->registrationLineEdit->setText(arg1.toUpper()); } void NewTailDialog::onCommitSuccessful() { ACalc::updateAutoTimes(entry.getPosition().second); // To do: update to use new db architecture with new ATailEntry accept(); } void NewTailDialog::onCommitUnsuccessful(const QSqlError &sqlError, const QString &) { auto mb = QMessageBox(this); mb.setIcon(QMessageBox::Critical); mb.setText("The following error has ocurred.\n\n" + sqlError.text() + "\n\nYour entry has not been saved."); mb.exec(); }