/* *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 "newflight.h" #include "ui_newflight.h" #include "dbman.cpp" /// ======================================================= /// Debug / WIP section /// ======================================================= #define DEBUG(expr) \ qDebug() << "~DEBUG" << __func__ << expr void NewFlight::on_verifyButton_clicked()//debug button { //fillExtrasLineEdits(); //qDebug() << testlist; qDebug() << parent(); } /*! * \brief NewFlight::nope for features that are not yet implemented */ void NewFlight::nope() { QMessageBox nope(this); //error box nope.setText("This feature is not yet available!"); nope.exec(); } /// ======================================================= /// Initialising variables used for storing user input /// Variables are initalised invalid to later fill them with /// meaningful inputs once they have been validated QVector flight; QDate date(QDate::currentDate()); QString doft(QDate::currentDate().toString(Qt::ISODate)); QString dept = "INVA"; QString dest = "INVA"; QTime tofb; QTime tonb; QTime tblk; QString pic = "-1"; QString acft = "-1"; // extras QString secondPilot = "-1"; QString thirdPilot = "-1"; QString pilotFunction = "-1"; QString pilotTask = "-1"; QString takeoff = "0"; QString landing = "0"; QString autoland = "0"; QString approachType = "-1"; // extra times QString tSPSE = "00:00"; QString tSPME = "00:00"; QString tMP = "00:00"; /* If on submitting, not all checks are passed, user input is stored * in the scratchpad table to later re-fill the form enabling correction.*/ bool hasOldInput = dbFlight::checkScratchpad(); /// Raw Input validation const QString TIME_REGEX_PATTERN = "([01]?[0-9]?|2[0-3]):?[0-5][0-9]?";// We only want to allow inputs that make sense as a time, e.g. 99:99 is not a valid time const QString IATA = "[\\w]{3}"; const QString ICAO = "[\\w0-9]{4}"; const QString LOC_REGEX_PATTERN = IATA + "|" + ICAO; const QString AIRCRAFT_REGEX_PATTERN = "[\\w0-9]+-?([\\w0-9]?)+"; const QString PILOT_NAME_REGEX_PATTERN = "[\\w]+,? ?[\\w]+"; /// Invalid characters (validators keep text even if it returns Invalid, see `onInputRejected` below) const QString TIME_INVALID_RGX = "[^\\d:]"; const QString LOC_INVALID_RGX = "[^\\w0-9]"; const QString AIRCRAFT_INVALID_RGX = "[^A-Z0-9\\-]"; const QString PILOT_NAME_INVALID_RGX = "[^a-zA-Z, ]"; /// Input max lengths const qint8 TIME_MAX_LENGTH = 5; //to allow for ':' e.g. "08:45" const qint8 LOC_MAX_LENGTH = 4; //! const qint8 AIRCRAFT_MAX_LENGTH = 10; const qint8 PILOT_NAME_MAX_LENGTH = 15; /*! * \brief setLineEditValidator set Validators for QLineEdits that end with Time, Loc, * Aircraft or Name */ static inline void setupLineEdit(QLineEdit* line_edit) { auto line_edit_objectName = line_edit->objectName(); DEBUG("Setting validators for " << line_edit_objectName); static const QVector> objectName_inputValidation_rgxs = { {QRegularExpression("\\w+Time"), QRegularExpression(TIME_REGEX_PATTERN), TIME_MAX_LENGTH}, {QRegularExpression("\\w+Loc"), QRegularExpression(LOC_REGEX_PATTERN), LOC_MAX_LENGTH}, {QRegularExpression("\\w+Acft"), QRegularExpression(AIRCRAFT_REGEX_PATTERN), AIRCRAFT_MAX_LENGTH}, {QRegularExpression("\\w+Name"), QRegularExpression(PILOT_NAME_REGEX_PATTERN), PILOT_NAME_MAX_LENGTH}, }; auto validator = new StrictRegularExpressionValidator(); for(auto tuple : objectName_inputValidation_rgxs) { auto objName_rgx = std::get<0>(tuple); auto input_rgx = std::get<1>(tuple); auto max_length = std::get<2>(tuple); if(objName_rgx.match(line_edit_objectName).hasMatch()) { validator->setRegularExpression(input_rgx); line_edit->setValidator(validator); line_edit->setMaxLength(max_length); return; } } DEBUG("Couldnt find QLineEdit" << line_edit_objectName); } /*! * \brief NewFlight::storeSettings Commits current selection for auto-logging * to the database. */ void NewFlight::storeSettings() { qDebug() << "Storing Settings..."; dbSettings::storeSetting(100, ui->FunctionComboBox->currentText()); dbSettings::storeSetting(101, ui->ApproachComboBox->currentText()); dbSettings::storeSetting(102, QString::number(ui->PilotFlyingCheckBox->isChecked())); dbSettings::storeSetting(103, QString::number(ui->PilotMonitoringCheckBox->isChecked())); dbSettings::storeSetting(104, QString::number(ui->TakeoffSpinBox->value())); dbSettings::storeSetting(105, QString::number(ui->TakeoffCheckBox->isChecked())); dbSettings::storeSetting(106, QString::number(ui->LandingSpinBox->value())); dbSettings::storeSetting(107, QString::number(ui->LandingCheckBox->isChecked())); dbSettings::storeSetting(108, QString::number(ui->AutolandSpinBox->value())); dbSettings::storeSetting(109, QString::number(ui->AutolandCheckBox->isChecked())); dbSettings::storeSetting(110, QString::number(ui->IfrCheckBox->isChecked())); dbSettings::storeSetting(111, QString::number(ui->VfrCheckBox->isChecked())); } /*! * \brief NewFlight::restoreSettings Retreives auto-logging settings from database * and sets up ui accordingly */ void NewFlight::restoreSettings() { qDebug() << "Restoring Settings...";//crashes if db is empty due to QVector index out of range. ui->FunctionComboBox->setCurrentText(dbSettings::retreiveSetting(100)); ui->ApproachComboBox->setCurrentText(dbSettings::retreiveSetting(101)); ui->PilotFlyingCheckBox->setChecked(dbSettings::retreiveSetting(102).toInt()); ui->PilotMonitoringCheckBox->setChecked(dbSettings::retreiveSetting(103).toInt()); ui->TakeoffSpinBox->setValue(dbSettings::retreiveSetting(104).toInt()); ui->TakeoffCheckBox->setChecked(dbSettings::retreiveSetting(105).toInt()); ui->LandingSpinBox->setValue(dbSettings::retreiveSetting(106).toInt()); ui->LandingCheckBox->setChecked(dbSettings::retreiveSetting(107).toInt()); ui->AutolandSpinBox->setValue(dbSettings::retreiveSetting(108).toInt()); ui->AutolandCheckBox->setChecked(dbSettings::retreiveSetting(109).toInt()); ui->IfrCheckBox->setChecked(dbSettings::retreiveSetting(110).toInt()); ui->VfrCheckBox->setChecked(dbSettings::retreiveSetting(111).toInt()); ui->flightNumberPrefixLabel->setText(dbSettings::retreiveSetting(50) + QLatin1Char('-')); } /* * Window Construction */ NewFlight::NewFlight(QWidget *parent, QStringList locationList, QStringList registrationList, QStringList pilotNameList) : QDialog(parent), ui(new Ui::NewFlight) { ui->setupUi(this); // Set up Line Edits with QValidators and set Max length auto line_edits = ui->flightDataTab->findChildren() + ui->extraTimes->findChildren(); for(auto line_edit : line_edits) { setupLineEdit(line_edit); } // Airport Line Edits Auto Completion auto *locationCompleter = new QCompleter(locationList); locationCompleter->setCaseSensitivity(Qt::CaseInsensitive); locationCompleter->setCompletionMode(QCompleter::PopupCompletion); ui->newDeptLocLineEdit->setCompleter(locationCompleter); ui->newDestLocLineEdit->setCompleter(locationCompleter); // Aircraft Line Edits Auto Completion auto *aircraftCompleter = new QCompleter(registrationList); aircraftCompleter->setCaseSensitivity(Qt::CaseInsensitive); aircraftCompleter->setCompletionMode(QCompleter::PopupCompletion); aircraftCompleter->setFilterMode(Qt::MatchContains); ui->newAcft->setCompleter(aircraftCompleter); // To Do: Aircraft and Pilot Names Completer // Groups for CheckBoxes QButtonGroup *FlightRulesGroup = new QButtonGroup(this); FlightRulesGroup->addButton(ui->IfrCheckBox); FlightRulesGroup->addButton(ui->VfrCheckBox); QButtonGroup *PilotTaskGroup = new QButtonGroup(this); PilotTaskGroup->addButton(ui->PilotFlyingCheckBox); PilotTaskGroup->addButton(ui->PilotMonitoringCheckBox); ui->deptTZ->setFocusPolicy(Qt::NoFocus); ui->destTZ->setFocusPolicy(Qt::NoFocus); ui->newDoft->setDate(QDate::currentDate()); // Visually mark mandatory fields ui->newDeptLocLineEdit->setStyleSheet("border: 1px solid orange"); ui->newDestLocLineEdit->setStyleSheet("border: 1px solid orange"); ui->newDeptTimeLineEdit->setStyleSheet("border: 1px solid orange"); ui->newDestTimeLineEdit->setStyleSheet("border: 1px solid orange"); ui->newPicNameLineEdit->setStyleSheet("border: 1px solid orange"); ui->newAcft->setStyleSheet("border: 1px solid orange"); restoreSettings(); // settings for auto-logging are stored in the database. //Restore inputs if commiting to DB has been rejected. qDebug() << "Hasoldinput? = " << hasOldInput; if(hasOldInput) // Re-populate the Form { flight = dbFlight::retreiveScratchpad(); qDebug() << "Re-Filling Form from Scratchpad"; returnInput(flight); } ui->newDeptLocLineEdit->setFocus(); } NewFlight::~NewFlight() { delete ui; } /* * Slots */ /*! * \brief onInputRejected Set `line_edit`'s border to red and check if `rgx` matches * in order to keep text on line. */ static void onInputRejected(QLineEdit* line_edit, QRegularExpression rgx){ DEBUG("Input rejected" << line_edit->text()); line_edit->setStyleSheet("border: 1px solid red"); if(auto text = line_edit->text(); rgx.match(text).hasMatch() == false) { line_edit->setText(line_edit->text()); } } /*! * \brief onEditingFinished signal is emitted if input passed raw validation */ static void onEditingFinished(QLineEdit* line_edit){ DEBUG("Input accepted" << line_edit->text() << line_edit->metaObject()->className()); line_edit->setStyleSheet(""); } void NewFlight::on_deptTZ_currentTextChanged(const QString &arg1) { if(arg1 == "Local"){nope();} // currently only UTC time logging is supported ui->deptTZ->setCurrentIndex(0); } void NewFlight::on_destTZ_currentIndexChanged(const QString &arg1) { if(arg1 == "Local"){nope();} // currently only UTC time logging is supported ui->destTZ->setCurrentIndex(0); } /// Departure void NewFlight::on_newDeptLocLineEdit_inputRejected() { ui->newDeptLocLineEdit->setText(ui->newDeptLocLineEdit->text().toUpper()); onInputRejected(ui->newDeptLocLineEdit, QRegularExpression(LOC_INVALID_RGX)); } void NewFlight::on_newDeptLocLineEdit_textEdited(const QString &arg1) { ui->newDeptLocLineEdit->setText(arg1.toUpper()); } void NewFlight::on_newDeptLocLineEdit_editingFinished() { QStringList locationList = dbAirport::retreiveIataIcaoList(); //To be moved outside of dialog eventually //DEBUG(locationList); auto line_edit = ui->newDeptLocLineEdit; onEditingFinished(line_edit); //reset style sheet dept = line_edit->text(); // check if iata exists, replace with icao code if it does. if(dept.length() == 3){ int index = locationList.indexOf(dept); if(index == -1){// Not in locationList DEBUG("Airport not found."); emit line_edit->inputRejected(); }else{ dept = locationList[index -1]; } } // Check if 4-letter code is in locationList if(dept.length() == 4 && locationList.indexOf(dept) == -1){ DEBUG("Airport not found."); emit line_edit->inputRejected(); } line_edit->setText(dept); DEBUG("Departure set: " << dept); } void NewFlight::on_newDeptTimeLineEdit_inputRejected() { onInputRejected(ui->newDeptTimeLineEdit, QRegularExpression(TIME_INVALID_RGX)); } void NewFlight::on_newDeptTimeLineEdit_editingFinished() { ui->newDeptTimeLineEdit->setText(calc::formatTimeInput(ui->newDeptTimeLineEdit->text())); tofb = QTime::fromString(ui->newDeptTimeLineEdit->text(),"hh:mm"); auto line_edit = ui->newDeptTimeLineEdit; onEditingFinished(line_edit); if(tofb.isValid()){ // continue DEBUG("Time Off Blocks is valid:" << tofb); }else{ emit line_edit->inputRejected(); } } /// Destination void NewFlight::on_newDestLocLineEdit_inputRejected() { ui->newDestLocLineEdit->setText(ui->newDestLocLineEdit->text().toUpper()); onInputRejected(ui->newDestLocLineEdit, QRegularExpression(LOC_INVALID_RGX)); } void NewFlight::on_newDestLocLineEdit_textEdited(const QString &arg1) { ui->newDestLocLineEdit->setText(arg1.toUpper()); } void NewFlight::on_newDestLocLineEdit_editingFinished() { QStringList locationList = dbAirport::retreiveIataIcaoList(); //To be moved outside of dialog eventually auto line_edit = ui->newDestLocLineEdit; onEditingFinished(line_edit); //reset style sheet dest = line_edit->text(); // check if iata exists, replace with icao code if it does. if(dest.length() == 3){ int index = locationList.indexOf(dest); if(index == -1){// Not in locationList DEBUG("Airport not found."); emit line_edit->inputRejected(); }else{ dest = locationList[index -1]; } } // Check if 4-letter code is in locationList if(dest.length() == 4 && locationList.indexOf(dest) == -1){ DEBUG("Airport not found."); emit line_edit->inputRejected(); } line_edit->setText(dest); DEBUG("Destination set: " << dest); } void NewFlight::on_newDestTimeLineEdit_inputRejected() { onInputRejected(ui->newDestTimeLineEdit, QRegularExpression(TIME_INVALID_RGX)); } void NewFlight::on_newDestTimeLineEdit_editingFinished() { ui->newDestTimeLineEdit->setText(calc::formatTimeInput(ui->newDestTimeLineEdit->text())); tonb = QTime::fromString(ui->newDestTimeLineEdit->text(),"hh:mm"); auto line_edit = ui->newDestTimeLineEdit; onEditingFinished(line_edit); if(tonb.isValid()){ // continue DEBUG("Time On Blocks is valid:" << tonb); }else{ emit line_edit->inputRejected(); } } /// Date void NewFlight::on_newDoft_editingFinished() { date = ui->newDoft->date(); doft = date.toString(Qt::ISODate); } /// Aircraft void NewFlight::on_newAcft_inputRejected() { onInputRejected(ui->newAcft, QRegularExpression(AIRCRAFT_INVALID_RGX)); } void NewFlight::on_newAcft_editingFinished() { acft = "-1";// set invalid QString registration; auto registrationList = dbAircraft::retreiveRegistrationList(); auto line_edit = ui->newAcft; QStringList match = registrationList.filter(line_edit->text(), Qt::CaseInsensitive); if(match.length() != 0) { acft = match[0]; line_edit->setText(acft); onEditingFinished(line_edit); }else{ DEBUG("Registration not in List!"); emit line_edit->inputRejected(); QMessageBox::StandardButton reply; reply = QMessageBox::question(this, "No aircraft found", "No aircraft found.\n Would you like to add a new aircraft to the database?", QMessageBox::Yes|QMessageBox::No); if (reply == QMessageBox::Yes) { NewAcft na(this); na.exec(); ui->newAcft->setText(""); ui->newAcft->setFocus(); } } } /// Pilot(s) void NewFlight::on_newPicNameLineEdit_inputRejected() { onInputRejected(ui->newPicNameLineEdit, QRegularExpression(PILOT_NAME_INVALID_RGX)); } void NewFlight::on_newPicNameLineEdit_textEdited(const QString &arg1) { if(arg1.length()>2) { QStringList picList = dbPilots::newPicGetString(arg1); QCompleter *picCompleter = new QCompleter(picList, this); picCompleter->setCaseSensitivity(Qt::CaseInsensitive); picCompleter->setCompletionMode(QCompleter::UnfilteredPopupCompletion); ui->newPicNameLineEdit->setCompleter(picCompleter); } } void NewFlight::on_newPicNameLineEdit_editingFinished() { pic = "-1"; // set invalid if(ui->newPicNameLineEdit->text() == "self") { pic = "1"; }else { QString picname; QStringList picList = dbPilots::newPicGetString(ui->newPicNameLineEdit->text()); qDebug() << picList; if(picList.length()!= 0) { picname = picList[0]; ui->newPicNameLineEdit->setText(picname); pic = dbPilots::newPicGetId(picname); }else { QMessageBox::StandardButton reply; reply = QMessageBox::question(this, "No Pilot found", "No pilot found.\n Would you like to add a new pilot to the database?", QMessageBox::Yes|QMessageBox::No); if (reply == QMessageBox::Yes) { qDebug() << "Add new pilot selected"; nope(); } } } } /*! * =================================================== * The above entris are mandatory for logging a flight, * the rest of the entries are either optional or can * be determined from the entries already made. */ void NewFlight::on_secondPilotNameLineEdit_textEdited(const QString &arg1) { if(arg1.length()>2) { QStringList picList = dbPilots::newPicGetString(arg1); QCompleter *picCompleter = new QCompleter(picList, this); picCompleter->setCaseSensitivity(Qt::CaseInsensitive); picCompleter->setCompletionMode(QCompleter::UnfilteredPopupCompletion); ui->secondPilotNameLineEdit->setCompleter(picCompleter); } } void NewFlight::on_secondPilotNameLineEdit_editingFinished() { secondPilot = "-1"; // set invalid if(ui->secondPilotNameLineEdit->text() == "self") { secondPilot = "1"; }else { QString picname; QStringList picList = dbPilots::newPicGetString(ui->secondPilotNameLineEdit->text()); qDebug() << picList; if(picList.length()!= 0) { picname = picList[0]; ui->secondPilotNameLineEdit->setText(picname); secondPilot = dbPilots::newPicGetId(picname); }else { ui->secondPilotNameLineEdit->setStyleSheet("border: 1px solid red"); /*QMessageBox::StandardButton reply; reply = QMessageBox::question(this, "No Pilot found", "No pilot found.\n Would you like to add a new pilot to the database?", QMessageBox::Yes|QMessageBox::No); if (reply == QMessageBox::Yes) { qDebug() << "Add new pilot selected"; nope(); }*/ } } } void NewFlight::on_thirdPilotNameLineEdit_textEdited(const QString &arg1) { if(arg1.length()>2) { QStringList picList = dbPilots::newPicGetString(arg1); QCompleter *picCompleter = new QCompleter(picList, this); picCompleter->setCaseSensitivity(Qt::CaseInsensitive); picCompleter->setCompletionMode(QCompleter::UnfilteredPopupCompletion); ui->thirdPilotNameLineEdit->setCompleter(picCompleter); } } void NewFlight::on_thirdPilotNameLineEdit_editingFinished() { thirdPilot = "-1"; // set invalid if(ui->thirdPilotNameLineEdit->text() == "self") { thirdPilot = "1"; }else { QString picname; QStringList picList = dbPilots::newPicGetString(ui->thirdPilotNameLineEdit->text()); qDebug() << picList; if(picList.length()!= 0) { picname = picList[0]; ui->thirdPilotNameLineEdit->setText(picname); thirdPilot = dbPilots::newPicGetId(picname); }else { ui->thirdPilotNameLineEdit->setStyleSheet("border: 1px solid red"); /*QMessageBox::StandardButton reply; reply = QMessageBox::question(this, "No Pilot found", "No pilot found.\n Would you like to add a new pilot to the database?", QMessageBox::Yes|QMessageBox::No); if (reply == QMessageBox::Yes) { qDebug() << "Add new pilot selected"; nope(); }*/ } } } void NewFlight::on_FlightNumberLineEdit_editingFinished() { qDebug() << "tbd: FlightNumber slot"; // To Do: Store input in variable, perform some checks // Setting for optional Prefix (e.g. LH, LX etc.) } /* * Extras Tab - These are for user convenience. From many of * these selections, determinations can be made on how to log * details, so that the user does not have to enter each item * manually. See also fillExtrasLineEdits() */ void NewFlight::on_setAsDefaultButton_clicked() { storeSettings(); } void NewFlight::on_restoreDefaultButton_clicked() { restoreSettings(); } /*! * \brief On a given flight, time can either be logged as Pilot Flying (PF) or * Pilot Monitoring (PM). Cases where controls are changed during the flight * are rare and can be logged by manually editing the extras. */ void NewFlight::on_PilotFlyingCheckBox_stateChanged(int) { if(ui->PilotFlyingCheckBox->isChecked()){ ui->TakeoffSpinBox->setValue(1); ui->TakeoffCheckBox->setCheckState(Qt::Checked); ui->LandingSpinBox->setValue(1); ui->LandingCheckBox->setCheckState(Qt::Checked); }else if(!ui->PilotFlyingCheckBox->isChecked()){ ui->TakeoffSpinBox->setValue(0); ui->TakeoffCheckBox->setCheckState(Qt::Unchecked); ui->LandingSpinBox->setValue(0); ui->LandingCheckBox->setCheckState(Qt::Unchecked); } } void NewFlight::on_ApproachComboBox_currentTextChanged(const QString &arg1) { if(arg1 == "ILS CAT III"){ //for a CAT III approach an Autoland is mandatory, so we can preselect it. ui->AutolandCheckBox->setCheckState(Qt::Checked); ui->AutolandSpinBox->setValue(1); }else{ ui->AutolandCheckBox->setCheckState(Qt::Unchecked); ui->AutolandSpinBox->setValue(0); } approachType = arg1; qDebug() << "Approach Type: " << approachType; } /* * Extra Times - These line edits should be filled out automatically, * based on the ui selections and the user provided input. However, * manual adjustments are possible to cater for situations where for * example one portion of the flight is logged under different rules * than the rest of it. * * For example, * if we know the aircraft details we can determine how to log these times. * Some times are mutually exclusive, others can be combined. * * For example, * for a commercial Passenger flight, the commander can log all time as * Total Time and PIC time. If the aircraft is a multi-engine jet he can * also log Multi-Pilot time, and if he is an instructor, instructor time. * * It is not possible, however to log flight time as VFR or IFR time * simultaneously, as a flight at any given point in time can only follow * one set of rules. It is possible, to change flight rules and log the first * x minutes as VFR and the rest of it as IFR, for example. Hence the need * for the possibility to edit these times manually. * * The most complex time to determine is night time, see documentation of * the calc class for details. * * In General, the idea is to automatically fill as much as possible, but * if the user decides to change these times, accept the inputs, as long as * they are generally valid. We cannot cater for all possibilities, so as long * as the time the user has input is a valid time <= Total Time, it can be * accepted to the database. */ void NewFlight::on_spseTimeLineEdit_editingFinished() { ui->spseTimeLineEdit->setText(calc::formatTimeInput(ui->spseTimeLineEdit->text())); if(ui->spseTimeLineEdit->text() == ""){ ui->spseTimeLineEdit->setStyleSheet("border: 1px solid red"); ui->spseTimeLineEdit->setFocus(); }else{ ui->spseTimeLineEdit->setStyleSheet(""); } } void NewFlight::on_spmeTimeLineEdit_editingFinished() { ui->spmeTimeLineEdit->setText(calc::formatTimeInput(ui->spmeTimeLineEdit->text())); if(ui->spmeTimeLineEdit->text() == ""){ ui->spmeTimeLineEdit->setStyleSheet("border: 1px solid red"); ui->spmeTimeLineEdit->setFocus(); }else{ ui->spmeTimeLineEdit->setStyleSheet(""); } } void NewFlight::on_mpTimeLineEdit_editingFinished() { ui->mpTimeLineEdit->setText(calc::formatTimeInput(ui->mpTimeLineEdit->text())); if(ui->mpTimeLineEdit->text() == ""){ ui->mpTimeLineEdit->setStyleSheet("border: 1px solid red"); ui->mpTimeLineEdit->setFocus(); }else{ ui->mpTimeLineEdit->setStyleSheet(""); } } void NewFlight::on_totalTimeLineEdit_editingFinished() { ui->totalTimeLineEdit->setText(calc::formatTimeInput(ui->totalTimeLineEdit->text())); if(ui->totalTimeLineEdit->text() == ""){ ui->totalTimeLineEdit->setStyleSheet("border: 1px solid red"); ui->totalTimeLineEdit->setFocus(); }else{ ui->totalTimeLineEdit->setStyleSheet(""); } } void NewFlight::on_ifrTimeLineEdit_editingFinished() { ui->ifrTimeLineEdit->setText(calc::formatTimeInput(ui->ifrTimeLineEdit->text())); if(ui->ifrTimeLineEdit->text() == ""){ ui->ifrTimeLineEdit->setStyleSheet("border: 1px solid red"); ui->ifrTimeLineEdit->setFocus(); }else{ ui->ifrTimeLineEdit->setStyleSheet(""); } } void NewFlight::on_vfrTimeLineEdit_editingFinished() { ui->vfrTimeLineEdit->setText(calc::formatTimeInput(ui->vfrTimeLineEdit->text())); if(ui->vfrTimeLineEdit->text() == ""){ ui->vfrTimeLineEdit->setStyleSheet("border: 1px solid red"); ui->vfrTimeLineEdit->setFocus(); }else{ ui->vfrTimeLineEdit->setStyleSheet(""); } } void NewFlight::on_nightTimeLineEdit_editingFinished() { ui->nightTimeLineEdit->setText(calc::formatTimeInput(ui->nightTimeLineEdit->text())); if(ui->nightTimeLineEdit->text() == ""){ ui->nightTimeLineEdit->setStyleSheet("border: 1px solid red"); ui->nightTimeLineEdit->setFocus(); }else{ ui->nightTimeLineEdit->setStyleSheet(""); } } void NewFlight::on_xcTimeLineEdit_editingFinished() { ui->xcTimeLineEdit->setText(calc::formatTimeInput(ui->xcTimeLineEdit->text())); if(ui->xcTimeLineEdit->text() == ""){ ui->xcTimeLineEdit->setStyleSheet("border: 1px solid red"); ui->xcTimeLineEdit->setFocus(); }else{ ui->xcTimeLineEdit->setStyleSheet(""); } } void NewFlight::on_picTimeLineEdit_editingFinished() { ui->picTimeLineEdit->setText(calc::formatTimeInput(ui->picTimeLineEdit->text())); if(ui->picTimeLineEdit->text() == ""){ ui->picTimeLineEdit->setStyleSheet("border: 1px solid red"); ui->picTimeLineEdit->setFocus(); }else{ ui->picTimeLineEdit->setStyleSheet(""); } } void NewFlight::on_copTimeLineEdit_editingFinished() { ui->copTimeLineEdit->setText(calc::formatTimeInput(ui->copTimeLineEdit->text())); if(ui->copTimeLineEdit->text() == ""){ ui->copTimeLineEdit->setStyleSheet("border: 1px solid red"); ui->copTimeLineEdit->setFocus(); }else{ ui->copTimeLineEdit->setStyleSheet(""); } } void NewFlight::on_dualTimeLineEdit_editingFinished() { ui->dualTimeLineEdit->setText(calc::formatTimeInput(ui->dualTimeLineEdit->text())); if(ui->dualTimeLineEdit->text() == ""){ ui->dualTimeLineEdit->setStyleSheet("border: 1px solid red"); ui->dualTimeLineEdit->setFocus(); }else{ ui->dualTimeLineEdit->setStyleSheet(""); } } void NewFlight::on_fiTimeLineEdit_editingFinished() { ui->fiTimeLineEdit->setText(calc::formatTimeInput(ui->fiTimeLineEdit->text())); if(ui->fiTimeLineEdit->text() == ""){ ui->fiTimeLineEdit->setStyleSheet("border: 1px solid red"); ui->fiTimeLineEdit->setFocus(); }else{ ui->fiTimeLineEdit->setStyleSheet(""); } } void NewFlight::on_simTimeLineEdit_editingFinished() { ui->simTimeLineEdit->setText(calc::formatTimeInput(ui->simTimeLineEdit->text())); if(ui->simTimeLineEdit->text() == ""){ ui->simTimeLineEdit->setStyleSheet("border: 1px solid red"); ui->simTimeLineEdit->setFocus(); }else{ ui->simTimeLineEdit->setStyleSheet(""); } } /* * Control Buttons */ void NewFlight::on_buttonBox_accepted() { on_newDoft_editingFinished();// - activate slots in case user has been active in last input before clicking submit //on_newTonb_editingFinished(); //on_newTofb_editingFinished(); //on_newDept_editingFinished(); //on_newDest_editingFinished(); on_newAcft_editingFinished(); on_newPicNameLineEdit_editingFinished(); QVector flight; flight = collectInput(); if(verifyInput()) { dbFlight::commitFlight(flight); qDebug() << flight << "Has been commited."; QMessageBox msgBox(this); msgBox.setText("Flight has been commited."); msgBox.exec(); }else { qDebug() << "Invalid Input. No entry has been made in the database."; dbFlight::commitToScratchpad(flight); QMessageBox msgBox(this); msgBox.setText("Invalid entries detected. Please check your input."); msgBox.exec(); //NewFlight nf(this); //nf.exec(); } } void NewFlight::on_buttonBox_rejected() { qDebug() << "NewFlight: Rejected\n"; } /// Input Verification and Collection QVector NewFlight::collectInput() { // Collect input from the user fields and return a vector containing the flight information QString doft = date.toString(Qt::ISODate); // Format Date QTime tblk = calc::blocktime(tofb,tonb); // Calculate Blocktime // Prepare Vector for commit to database flight = dbFlight::createFlightVectorFromInput(doft, dept, tofb, dest, tonb, tblk, pic, acft); qDebug() << "Created flight vector:" << flight; return flight; } /*! * \brief NewFlight::verifyInput checks if input exists in database. * \return */ bool NewFlight::verifyInput() { bool deptValid = false; bool tofbValid = false; bool destValid = false; bool tonbValid = false; bool tblkValid = false; bool picValid = false; bool acftValid = false; QTime tblk = calc::blocktime(tofb,tonb); int checktblk = calc::time_to_minutes(tblk); bool doftValid = true; //doft assumed to be always valid due to QDateTimeEdit constraints qDebug() << "NewFlight::verifyInput() says: Date:" << doft << " - Valid?\t" << doftValid; deptValid = dbAirport::checkICAOValid(dept); qDebug() << "NewFlight::verifyInput() says: Departure is:\t" << dept << " - Valid?\t" << deptValid; destValid = dbAirport::checkICAOValid(dest); qDebug() << "NewFlight::verifyInput() says: Destination is:\t" << dest << " - Valid?\t" << destValid; tofbValid = (unsigned)(calc::time_to_minutes(tofb)-0) <= (1440-0) && tofb.toString("hh:mm") != ""; // Make sure time is within range, DB 1 day = 1440 minutes. 0 is allowed (midnight) & that it is not empty. qDebug() << "NewFlight::verifyInput() says: tofb is:\t\t" << tofb.toString("hh:mm") << " - Valid?\t" << tofbValid; tonbValid = (unsigned)(calc::time_to_minutes(tonb)-0) <= (1440-0) && tonb.toString("hh:mm") != ""; // Make sure time is within range, DB 1 day = 1440 minutes qDebug() << "NewFlight::verifyInput() says: tonb is:\t\t" << tonb.toString("hh:mm")<< " - Valid?\t" << tonbValid; picValid = (pic.toInt() > 0); // pic should be a pilot_id retreived from the database qDebug() << "NewFlight::verifyInput() says: pic is pilotd_id:\t" << pic << " - Valid?\t" << picValid; if(!picValid) { QMessageBox msgBox(this); msgBox.setText("You cannot log a flight without a valid pilot"); msgBox.exec(); } acftValid = (acft.toInt() > 0); qDebug() << "NewFlight::verifyInput() says: acft is tail_id:\t" << acft << " - Valid?\t" << acftValid; if(!acftValid) { QMessageBox msgBox(this); msgBox.setText("You cannot log a flight without a valid aircraft"); msgBox.exec(); } tblkValid = checktblk != 0; qDebug() << "NewFlight::verifyInput() says: tblk is:\t\t" << tblk.toString("hh:mm") << " - Valid?\t" << tblkValid; if(deptValid && tofbValid && destValid && tonbValid && tblkValid && picValid && acftValid) { qDebug() << "===================================================="; qDebug() << "NewFlight::verifyInput() says: All checks passed! Very impressive. "; return 1; }else { qDebug() << "===================================================="; qDebug() << "NewFlight::verifyInput() says: Flight is invalid."; return 0; } return 0; } void NewFlight::returnInput(QVector flight) { // Re-populates the input masks with the selected fields if input was erroneous to allow for corrections to be made qDebug() << "return Input: " << flight; ui->newDoft->setDate(QDate::fromString(flight[1],Qt::ISODate)); ui->newDeptLocLineEdit->setText(flight[2]); ui->newDeptTimeLineEdit->setText(flight[3]); ui->newDestLocLineEdit->setText(flight[4]); ui->newDestTimeLineEdit->setText(flight[5]); // flight[6] is blocktime ui->newPicNameLineEdit->setText(dbPilots::retreivePilotNameFromID(flight[7])); ui->newAcft->setText(dbAircraft::retreiveRegistration(flight[8])); } /*! * \brief NewFlight::fillExtrasLineEdits Fills the flight time line edits according to ui selections. * Neccessary inputs are valid Date, Departure Time and Place, Destination Time and Place, * PIC name (pilot_id) and Aircraft (tail_id) */ void NewFlight::fillExtrasLineEdits() { QString blockTime = calc::blocktime(tofb,tonb).toString("hh:mm"); DEBUG(blockTime); QVector aircraftDetails = dbAircraft::retreiveAircraftDetails(dbAircraft::retreiveAircraftId(acft)); DEBUG("aircraftDetails: " << aircraftDetails); if(!aircraftDetails.isEmpty()){// valid aircraft // SP SE if(aircraftDetails[0] == "1" && aircraftDetails[2] == "1"){ DEBUG("SPSE yes"); tSPSE = blockTime; ui->spseTimeLineEdit->setText(blockTime); } // SP ME if(aircraftDetails[0] == "1" && aircraftDetails[3] == "1"){ DEBUG("SP ME yes"); tSPME = blockTime; ui->spmeTimeLineEdit->setText(blockTime); } // MP if(aircraftDetails[1] == "1"){ DEBUG("Multipilot yes"); tMP = blockTime; ui->mpTimeLineEdit->setText(blockTime); } }else{DEBUG("Aircraft Details Empty");}//invalid aircraft // TOTAL ui->totalTimeLineEdit->setText(blockTime); // IFR if(ui->IfrCheckBox->isChecked()){ ui->ifrTimeLineEdit->setText(blockTime); ui->vfrTimeLineEdit->setText(""); } // VFR if(ui->VfrCheckBox->isChecked()){ ui->vfrTimeLineEdit->setText(blockTime); ui->ifrTimeLineEdit->setText(""); } // Night QString deptDate = date.toString(Qt::ISODate) + 'T' + tofb.toString("hh:mm"); QDateTime deptDateTime = QDateTime::fromString(deptDate,"yyyy-MM-ddThh:mm"); int tblk = calc::time_to_minutes(calc::blocktime(tofb,tonb)); QString nightTime = calc::minutes_to_string( QString::number( calc::calculateNightTime( dept, dest, deptDateTime, tblk))); ui->nightTimeLineEdit->setText(nightTime); // XC - Cross-country flight, if more than 50nm long if(calc::greatCircleDistanceBetweenAirports(dept,dest) >= 50){ qDebug() << "Cross-country Flight: nm = " << calc::greatCircleDistanceBetweenAirports(dept,dest); ui->xcTimeLineEdit->setText(blockTime); }else{ui->xcTimeLineEdit->setText("00:00");} // Function times switch (ui->FunctionComboBox->currentIndex()) { case 0://PIC ui->picTimeLineEdit->setText(blockTime); ui->copTimeLineEdit->setText(""); ui->dualTimeLineEdit->setText(""); ui->fiTimeLineEdit->setText(""); break; case 1://Co-Pilot ui->picTimeLineEdit->setText(""); ui->copTimeLineEdit->setText(blockTime); ui->dualTimeLineEdit->setText(""); ui->fiTimeLineEdit->setText(""); break; case 2://Dual ui->picTimeLineEdit->setText(""); ui->copTimeLineEdit->setText(""); ui->dualTimeLineEdit->setText(blockTime); ui->fiTimeLineEdit->setText(""); break; case 3://Instructor ui->picTimeLineEdit->setText(""); ui->copTimeLineEdit->setText(""); ui->dualTimeLineEdit->setText(""); ui->fiTimeLineEdit->setText(blockTime); } // SIM } /*! * \brief In case the user wants to manually edit the extra times * he can do so in this tab. */ void NewFlight::on_tabWidget_currentChanged(int index) { if(index == 1){// Edit Details tab if(verifyInput()){ fillExtrasLineEdits(); }else{ auto errorbox = new QMessageBox; errorbox->setText("Invalid Inputs. Unable to determine times automatically.\nPlease verify your inputs."); errorbox->exec(); } } }