2
0

newflight.cpp 38 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087
  1. /*
  2. *openPilot Log - A FOSS Pilot Logbook Application
  3. *Copyright (C) 2020 Felix Turowsky
  4. *
  5. *This program is free software: you can redistribute it and/or modify
  6. *it under the terms of the GNU General Public License as published by
  7. *the Free Software Foundation, either version 3 of the License, or
  8. *(at your option) any later version.
  9. *
  10. *This program is distributed in the hope that it will be useful,
  11. *but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. *MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. *GNU General Public License for more details.
  14. *
  15. *You should have received a copy of the GNU General Public License
  16. *along with this program. If not, see <https://www.gnu.org/licenses/>.
  17. */
  18. #include "newflight.h"
  19. #include "ui_newflight.h"
  20. #include "dbman.cpp"
  21. /// =======================================================
  22. /// Debug / WIP section
  23. /// =======================================================
  24. #define DEBUG(expr) \
  25. qDebug() << "~DEBUG" << __func__ << expr
  26. void NewFlight::on_verifyButton_clicked()//debug button
  27. {
  28. //fillExtrasLineEdits();
  29. //qDebug() << testlist;
  30. qDebug() << parent();
  31. }
  32. /*!
  33. * \brief NewFlight::nope for features that are not yet implemented
  34. */
  35. void NewFlight::nope()
  36. {
  37. QMessageBox nope(this); //error box
  38. nope.setText("This feature is not yet available!");
  39. nope.exec();
  40. }
  41. /// =======================================================
  42. /// Initialising variables used for storing user input
  43. /// Variables are initalised invalid to later fill them with
  44. /// meaningful inputs once they have been validated
  45. QVector<QString> flight;
  46. QDate date(QDate::currentDate());
  47. QString doft(QDate::currentDate().toString(Qt::ISODate));
  48. QString dept = "INVA";
  49. QString dest = "INVA";
  50. QTime tofb;
  51. QTime tonb;
  52. QTime tblk;
  53. QString pic = "-1";
  54. QString acft = "-1";
  55. // extras
  56. QString secondPilot = "-1";
  57. QString thirdPilot = "-1";
  58. QString pilotFunction = "-1";
  59. QString pilotTask = "-1";
  60. QString takeoff = "0";
  61. QString landing = "0";
  62. QString autoland = "0";
  63. QString approachType = "-1";
  64. // extra times
  65. QString tSPSE = "00:00";
  66. QString tSPME = "00:00";
  67. QString tMP = "00:00";
  68. /* If on submitting, not all checks are passed, user input is stored
  69. * in the scratchpad table to later re-fill the form enabling correction.*/
  70. bool hasOldInput = dbFlight::checkScratchpad();
  71. /// Raw Input validation
  72. 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
  73. const QString IATA = "[\\w]{3}";
  74. const QString ICAO = "[\\w0-9]{4}";
  75. const QString LOC_REGEX_PATTERN = IATA + "|" + ICAO;
  76. const QString AIRCRAFT_REGEX_PATTERN = "[\\w0-9]+-?([\\w0-9]?)+";
  77. const QString PILOT_NAME_REGEX_PATTERN = "[\\w]+,? ?[\\w]+";
  78. /// Invalid characters (validators keep text even if it returns Invalid, see `onInputRejected` below)
  79. const QString TIME_INVALID_RGX = "[^\\d:]";
  80. const QString LOC_INVALID_RGX = "[^\\w0-9]";
  81. const QString AIRCRAFT_INVALID_RGX = "[^A-Z0-9\\-]";
  82. const QString PILOT_NAME_INVALID_RGX = "[^a-zA-Z, ]";
  83. /// Input max lengths
  84. const qint8 TIME_MAX_LENGTH = 5; //to allow for ':' e.g. "08:45"
  85. const qint8 LOC_MAX_LENGTH = 4; //!
  86. const qint8 AIRCRAFT_MAX_LENGTH = 10;
  87. const qint8 PILOT_NAME_MAX_LENGTH = 15;
  88. /*!
  89. * \brief setLineEditValidator set Validators for QLineEdits that end with Time, Loc,
  90. * Aircraft or Name
  91. */
  92. static inline void setupLineEdit(QLineEdit* line_edit)
  93. {
  94. auto line_edit_objectName = line_edit->objectName();
  95. DEBUG("Setting validators for " << line_edit_objectName);
  96. static const
  97. QVector<std::tuple<QRegularExpression, QRegularExpression, qint8>> objectName_inputValidation_rgxs = {
  98. {QRegularExpression("\\w+Time"), QRegularExpression(TIME_REGEX_PATTERN), TIME_MAX_LENGTH},
  99. {QRegularExpression("\\w+Loc"), QRegularExpression(LOC_REGEX_PATTERN), LOC_MAX_LENGTH},
  100. {QRegularExpression("\\w+Acft"), QRegularExpression(AIRCRAFT_REGEX_PATTERN), AIRCRAFT_MAX_LENGTH},
  101. {QRegularExpression("\\w+Name"), QRegularExpression(PILOT_NAME_REGEX_PATTERN), PILOT_NAME_MAX_LENGTH},
  102. };
  103. auto validator = new StrictRegularExpressionValidator();
  104. for(auto tuple : objectName_inputValidation_rgxs)
  105. {
  106. auto objName_rgx = std::get<0>(tuple);
  107. auto input_rgx = std::get<1>(tuple);
  108. auto max_length = std::get<2>(tuple);
  109. if(objName_rgx.match(line_edit_objectName).hasMatch())
  110. {
  111. validator->setRegularExpression(input_rgx);
  112. line_edit->setValidator(validator);
  113. line_edit->setMaxLength(max_length);
  114. return;
  115. }
  116. }
  117. DEBUG("Couldnt find QLineEdit" << line_edit_objectName);
  118. }
  119. /*!
  120. * \brief NewFlight::storeSettings Commits current selection for auto-logging
  121. * to the database.
  122. */
  123. void NewFlight::storeSettings()
  124. {
  125. qDebug() << "Storing Settings...";
  126. dbSettings::storeSetting(100, ui->FunctionComboBox->currentText());
  127. dbSettings::storeSetting(101, ui->ApproachComboBox->currentText());
  128. dbSettings::storeSetting(102, QString::number(ui->PilotFlyingCheckBox->isChecked()));
  129. dbSettings::storeSetting(103, QString::number(ui->PilotMonitoringCheckBox->isChecked()));
  130. dbSettings::storeSetting(104, QString::number(ui->TakeoffSpinBox->value()));
  131. dbSettings::storeSetting(105, QString::number(ui->TakeoffCheckBox->isChecked()));
  132. dbSettings::storeSetting(106, QString::number(ui->LandingSpinBox->value()));
  133. dbSettings::storeSetting(107, QString::number(ui->LandingCheckBox->isChecked()));
  134. dbSettings::storeSetting(108, QString::number(ui->AutolandSpinBox->value()));
  135. dbSettings::storeSetting(109, QString::number(ui->AutolandCheckBox->isChecked()));
  136. dbSettings::storeSetting(110, QString::number(ui->IfrCheckBox->isChecked()));
  137. dbSettings::storeSetting(111, QString::number(ui->VfrCheckBox->isChecked()));
  138. }
  139. /*!
  140. * \brief NewFlight::restoreSettings Retreives auto-logging settings from database
  141. * and sets up ui accordingly
  142. */
  143. void NewFlight::restoreSettings()
  144. {
  145. qDebug() << "Restoring Settings...";//crashes if db is empty due to QVector index out of range.
  146. ui->FunctionComboBox->setCurrentText(dbSettings::retreiveSetting(100));
  147. ui->ApproachComboBox->setCurrentText(dbSettings::retreiveSetting(101));
  148. ui->PilotFlyingCheckBox->setChecked(dbSettings::retreiveSetting(102).toInt());
  149. ui->PilotMonitoringCheckBox->setChecked(dbSettings::retreiveSetting(103).toInt());
  150. ui->TakeoffSpinBox->setValue(dbSettings::retreiveSetting(104).toInt());
  151. ui->TakeoffCheckBox->setChecked(dbSettings::retreiveSetting(105).toInt());
  152. ui->LandingSpinBox->setValue(dbSettings::retreiveSetting(106).toInt());
  153. ui->LandingCheckBox->setChecked(dbSettings::retreiveSetting(107).toInt());
  154. ui->AutolandSpinBox->setValue(dbSettings::retreiveSetting(108).toInt());
  155. ui->AutolandCheckBox->setChecked(dbSettings::retreiveSetting(109).toInt());
  156. ui->IfrCheckBox->setChecked(dbSettings::retreiveSetting(110).toInt());
  157. ui->VfrCheckBox->setChecked(dbSettings::retreiveSetting(111).toInt());
  158. ui->flightNumberPrefixLabel->setText(dbSettings::retreiveSetting(50) + QLatin1Char('-'));
  159. }
  160. /*
  161. * Window Construction
  162. */
  163. NewFlight::NewFlight(QWidget *parent, QStringList locationList,
  164. QStringList registrationList,
  165. QStringList pilotNameList) :
  166. QDialog(parent),
  167. ui(new Ui::NewFlight)
  168. {
  169. ui->setupUi(this);
  170. // Set up Line Edits with QValidators and set Max length
  171. auto line_edits = ui->flightDataTab->findChildren<QLineEdit*>() +
  172. ui->extraTimes->findChildren<QLineEdit*>();
  173. for(auto line_edit : line_edits)
  174. {
  175. setupLineEdit(line_edit);
  176. }
  177. // Airport Line Edits Auto Completion
  178. auto *locationCompleter = new QCompleter(locationList);
  179. locationCompleter->setCaseSensitivity(Qt::CaseInsensitive);
  180. locationCompleter->setCompletionMode(QCompleter::PopupCompletion);
  181. ui->newDeptLocLineEdit->setCompleter(locationCompleter);
  182. ui->newDestLocLineEdit->setCompleter(locationCompleter);
  183. // Aircraft Line Edits Auto Completion
  184. auto *aircraftCompleter = new QCompleter(registrationList);
  185. aircraftCompleter->setCaseSensitivity(Qt::CaseInsensitive);
  186. aircraftCompleter->setCompletionMode(QCompleter::PopupCompletion);
  187. aircraftCompleter->setFilterMode(Qt::MatchContains);
  188. ui->newAcft->setCompleter(aircraftCompleter);
  189. // To Do: Aircraft and Pilot Names Completer
  190. // Groups for CheckBoxes
  191. QButtonGroup *FlightRulesGroup = new QButtonGroup(this);
  192. FlightRulesGroup->addButton(ui->IfrCheckBox);
  193. FlightRulesGroup->addButton(ui->VfrCheckBox);
  194. QButtonGroup *PilotTaskGroup = new QButtonGroup(this);
  195. PilotTaskGroup->addButton(ui->PilotFlyingCheckBox);
  196. PilotTaskGroup->addButton(ui->PilotMonitoringCheckBox);
  197. ui->deptTZ->setFocusPolicy(Qt::NoFocus);
  198. ui->destTZ->setFocusPolicy(Qt::NoFocus);
  199. ui->newDoft->setDate(QDate::currentDate());
  200. // Visually mark mandatory fields
  201. ui->newDeptLocLineEdit->setStyleSheet("border: 1px solid orange");
  202. ui->newDestLocLineEdit->setStyleSheet("border: 1px solid orange");
  203. ui->newDeptTimeLineEdit->setStyleSheet("border: 1px solid orange");
  204. ui->newDestTimeLineEdit->setStyleSheet("border: 1px solid orange");
  205. ui->newPicNameLineEdit->setStyleSheet("border: 1px solid orange");
  206. ui->newAcft->setStyleSheet("border: 1px solid orange");
  207. restoreSettings(); // settings for auto-logging are stored in the database.
  208. //Restore inputs if commiting to DB has been rejected.
  209. qDebug() << "Hasoldinput? = " << hasOldInput;
  210. if(hasOldInput) // Re-populate the Form
  211. {
  212. flight = dbFlight::retreiveScratchpad();
  213. qDebug() << "Re-Filling Form from Scratchpad";
  214. returnInput(flight);
  215. }
  216. ui->newDeptLocLineEdit->setFocus();
  217. }
  218. NewFlight::~NewFlight()
  219. {
  220. delete ui;
  221. }
  222. /*
  223. * Slots
  224. */
  225. /*!
  226. * \brief onInputRejected Set `line_edit`'s border to red and check if `rgx` matches
  227. * in order to keep text on line.
  228. */
  229. static void onInputRejected(QLineEdit* line_edit, QRegularExpression rgx){
  230. DEBUG("Input rejected" << line_edit->text());
  231. line_edit->setStyleSheet("border: 1px solid red");
  232. if(auto text = line_edit->text();
  233. rgx.match(text).hasMatch() == false)
  234. {
  235. line_edit->setText(line_edit->text());
  236. }
  237. }
  238. /*!
  239. * \brief onEditingFinished signal is emitted if input passed raw validation
  240. */
  241. static void onEditingFinished(QLineEdit* line_edit){
  242. DEBUG("Input accepted" << line_edit->text() << line_edit->metaObject()->className());
  243. line_edit->setStyleSheet("");
  244. }
  245. void NewFlight::on_deptTZ_currentTextChanged(const QString &arg1)
  246. {
  247. if(arg1 == "Local"){nope();} // currently only UTC time logging is supported
  248. ui->deptTZ->setCurrentIndex(0);
  249. }
  250. void NewFlight::on_destTZ_currentIndexChanged(const QString &arg1)
  251. {
  252. if(arg1 == "Local"){nope();} // currently only UTC time logging is supported
  253. ui->destTZ->setCurrentIndex(0);
  254. }
  255. /// Departure
  256. void NewFlight::on_newDeptLocLineEdit_inputRejected()
  257. {
  258. ui->newDeptLocLineEdit->setText(ui->newDeptLocLineEdit->text().toUpper());
  259. onInputRejected(ui->newDeptLocLineEdit, QRegularExpression(LOC_INVALID_RGX));
  260. }
  261. void NewFlight::on_newDeptLocLineEdit_textEdited(const QString &arg1)
  262. {
  263. ui->newDeptLocLineEdit->setText(arg1.toUpper());
  264. }
  265. void NewFlight::on_newDeptLocLineEdit_editingFinished()
  266. {
  267. QStringList locationList = dbAirport::retreiveIataIcaoList(); //To be moved outside of dialog eventually
  268. //DEBUG(locationList);
  269. auto line_edit = ui->newDeptLocLineEdit;
  270. onEditingFinished(line_edit); //reset style sheet
  271. dept = line_edit->text();
  272. // check if iata exists, replace with icao code if it does.
  273. if(dept.length() == 3){
  274. int index = locationList.indexOf(dept);
  275. if(index == -1){// Not in locationList
  276. DEBUG("Airport not found.");
  277. emit line_edit->inputRejected();
  278. }else{
  279. dept = locationList[index -1];
  280. }
  281. }
  282. // Check if 4-letter code is in locationList
  283. if(dept.length() == 4 && locationList.indexOf(dept) == -1){
  284. DEBUG("Airport not found.");
  285. emit line_edit->inputRejected();
  286. }
  287. line_edit->setText(dept);
  288. DEBUG("Departure set: " << dept);
  289. }
  290. void NewFlight::on_newDeptTimeLineEdit_inputRejected()
  291. {
  292. onInputRejected(ui->newDeptTimeLineEdit, QRegularExpression(TIME_INVALID_RGX));
  293. }
  294. void NewFlight::on_newDeptTimeLineEdit_editingFinished()
  295. {
  296. ui->newDeptTimeLineEdit->setText(calc::formatTimeInput(ui->newDeptTimeLineEdit->text()));
  297. tofb = QTime::fromString(ui->newDeptTimeLineEdit->text(),"hh:mm");
  298. auto line_edit = ui->newDeptTimeLineEdit;
  299. onEditingFinished(line_edit);
  300. if(tofb.isValid()){
  301. // continue
  302. DEBUG("Time Off Blocks is valid:" << tofb);
  303. }else{
  304. emit line_edit->inputRejected();
  305. }
  306. }
  307. /// Destination
  308. void NewFlight::on_newDestLocLineEdit_inputRejected()
  309. {
  310. ui->newDestLocLineEdit->setText(ui->newDestLocLineEdit->text().toUpper());
  311. onInputRejected(ui->newDestLocLineEdit, QRegularExpression(LOC_INVALID_RGX));
  312. }
  313. void NewFlight::on_newDestLocLineEdit_textEdited(const QString &arg1)
  314. {
  315. ui->newDestLocLineEdit->setText(arg1.toUpper());
  316. }
  317. void NewFlight::on_newDestLocLineEdit_editingFinished()
  318. {
  319. QStringList locationList = dbAirport::retreiveIataIcaoList(); //To be moved outside of dialog eventually
  320. auto line_edit = ui->newDestLocLineEdit;
  321. onEditingFinished(line_edit); //reset style sheet
  322. dest = line_edit->text();
  323. // check if iata exists, replace with icao code if it does.
  324. if(dest.length() == 3){
  325. int index = locationList.indexOf(dest);
  326. if(index == -1){// Not in locationList
  327. DEBUG("Airport not found.");
  328. emit line_edit->inputRejected();
  329. }else{
  330. dest = locationList[index -1];
  331. }
  332. }
  333. // Check if 4-letter code is in locationList
  334. if(dest.length() == 4 && locationList.indexOf(dest) == -1){
  335. DEBUG("Airport not found.");
  336. emit line_edit->inputRejected();
  337. }
  338. line_edit->setText(dest);
  339. DEBUG("Destination set: " << dest);
  340. }
  341. void NewFlight::on_newDestTimeLineEdit_inputRejected()
  342. {
  343. onInputRejected(ui->newDestTimeLineEdit, QRegularExpression(TIME_INVALID_RGX));
  344. }
  345. void NewFlight::on_newDestTimeLineEdit_editingFinished()
  346. {
  347. ui->newDestTimeLineEdit->setText(calc::formatTimeInput(ui->newDestTimeLineEdit->text()));
  348. tonb = QTime::fromString(ui->newDestTimeLineEdit->text(),"hh:mm");
  349. auto line_edit = ui->newDestTimeLineEdit;
  350. onEditingFinished(line_edit);
  351. if(tonb.isValid()){
  352. // continue
  353. DEBUG("Time On Blocks is valid:" << tonb);
  354. }else{
  355. emit line_edit->inputRejected();
  356. }
  357. }
  358. /// Date
  359. void NewFlight::on_newDoft_editingFinished()
  360. {
  361. date = ui->newDoft->date();
  362. doft = date.toString(Qt::ISODate);
  363. }
  364. /// Aircraft
  365. void NewFlight::on_newAcft_inputRejected()
  366. {
  367. onInputRejected(ui->newAcft, QRegularExpression(AIRCRAFT_INVALID_RGX));
  368. }
  369. void NewFlight::on_newAcft_editingFinished()
  370. {
  371. acft = "-1";// set invalid
  372. QString registration;
  373. auto registrationList = dbAircraft::retreiveRegistrationList();
  374. auto line_edit = ui->newAcft;
  375. QStringList match = registrationList.filter(line_edit->text(), Qt::CaseInsensitive);
  376. if(match.length() != 0) {
  377. acft = match[0];
  378. line_edit->setText(acft);
  379. onEditingFinished(line_edit);
  380. }else{
  381. DEBUG("Registration not in List!");
  382. emit line_edit->inputRejected();
  383. QMessageBox::StandardButton reply;
  384. reply = QMessageBox::question(this, "No aircraft found", "No aircraft found.\n Would you like to add a new aircraft to the database?",
  385. QMessageBox::Yes|QMessageBox::No);
  386. if (reply == QMessageBox::Yes) {
  387. NewAcft na(this);
  388. na.exec();
  389. ui->newAcft->setText("");
  390. ui->newAcft->setFocus();
  391. }
  392. }
  393. }
  394. /// Pilot(s)
  395. void NewFlight::on_newPicNameLineEdit_inputRejected()
  396. {
  397. onInputRejected(ui->newPicNameLineEdit, QRegularExpression(PILOT_NAME_INVALID_RGX));
  398. }
  399. void NewFlight::on_newPicNameLineEdit_textEdited(const QString &arg1)
  400. {
  401. if(arg1.length()>2)
  402. {
  403. QStringList picList = dbPilots::newPicGetString(arg1);
  404. QCompleter *picCompleter = new QCompleter(picList, this);
  405. picCompleter->setCaseSensitivity(Qt::CaseInsensitive);
  406. picCompleter->setCompletionMode(QCompleter::UnfilteredPopupCompletion);
  407. ui->newPicNameLineEdit->setCompleter(picCompleter);
  408. }
  409. }
  410. void NewFlight::on_newPicNameLineEdit_editingFinished()
  411. {
  412. pic = "-1"; // set invalid
  413. if(ui->newPicNameLineEdit->text() == "self")
  414. {
  415. pic = "1";
  416. }else
  417. {
  418. QString picname;
  419. QStringList picList = dbPilots::newPicGetString(ui->newPicNameLineEdit->text());
  420. qDebug() << picList;
  421. if(picList.length()!= 0)
  422. {
  423. picname = picList[0];
  424. ui->newPicNameLineEdit->setText(picname);
  425. pic = dbPilots::newPicGetId(picname);
  426. }else
  427. {
  428. QMessageBox::StandardButton reply;
  429. reply = QMessageBox::question(this, "No Pilot found", "No pilot found.\n Would you like to add a new pilot to the database?",
  430. QMessageBox::Yes|QMessageBox::No);
  431. if (reply == QMessageBox::Yes)
  432. {
  433. qDebug() << "Add new pilot selected";
  434. nope();
  435. }
  436. }
  437. }
  438. }
  439. /*!
  440. * ===================================================
  441. * The above entris are mandatory for logging a flight,
  442. * the rest of the entries are either optional or can
  443. * be determined from the entries already made.
  444. */
  445. void NewFlight::on_secondPilotNameLineEdit_textEdited(const QString &arg1)
  446. {
  447. if(arg1.length()>2)
  448. {
  449. QStringList picList = dbPilots::newPicGetString(arg1);
  450. QCompleter *picCompleter = new QCompleter(picList, this);
  451. picCompleter->setCaseSensitivity(Qt::CaseInsensitive);
  452. picCompleter->setCompletionMode(QCompleter::UnfilteredPopupCompletion);
  453. ui->secondPilotNameLineEdit->setCompleter(picCompleter);
  454. }
  455. }
  456. void NewFlight::on_secondPilotNameLineEdit_editingFinished()
  457. {
  458. secondPilot = "-1"; // set invalid
  459. if(ui->secondPilotNameLineEdit->text() == "self")
  460. {
  461. secondPilot = "1";
  462. }else
  463. {
  464. QString picname;
  465. QStringList picList = dbPilots::newPicGetString(ui->secondPilotNameLineEdit->text());
  466. qDebug() << picList;
  467. if(picList.length()!= 0)
  468. {
  469. picname = picList[0];
  470. ui->secondPilotNameLineEdit->setText(picname);
  471. secondPilot = dbPilots::newPicGetId(picname);
  472. }else
  473. {
  474. ui->secondPilotNameLineEdit->setStyleSheet("border: 1px solid red");
  475. /*QMessageBox::StandardButton reply;
  476. reply = QMessageBox::question(this, "No Pilot found", "No pilot found.\n Would you like to add a new pilot to the database?",
  477. QMessageBox::Yes|QMessageBox::No);
  478. if (reply == QMessageBox::Yes)
  479. {
  480. qDebug() << "Add new pilot selected";
  481. nope();
  482. }*/
  483. }
  484. }
  485. }
  486. void NewFlight::on_thirdPilotNameLineEdit_textEdited(const QString &arg1)
  487. {
  488. if(arg1.length()>2)
  489. {
  490. QStringList picList = dbPilots::newPicGetString(arg1);
  491. QCompleter *picCompleter = new QCompleter(picList, this);
  492. picCompleter->setCaseSensitivity(Qt::CaseInsensitive);
  493. picCompleter->setCompletionMode(QCompleter::UnfilteredPopupCompletion);
  494. ui->thirdPilotNameLineEdit->setCompleter(picCompleter);
  495. }
  496. }
  497. void NewFlight::on_thirdPilotNameLineEdit_editingFinished()
  498. {
  499. thirdPilot = "-1"; // set invalid
  500. if(ui->thirdPilotNameLineEdit->text() == "self")
  501. {
  502. thirdPilot = "1";
  503. }else
  504. {
  505. QString picname;
  506. QStringList picList = dbPilots::newPicGetString(ui->thirdPilotNameLineEdit->text());
  507. qDebug() << picList;
  508. if(picList.length()!= 0)
  509. {
  510. picname = picList[0];
  511. ui->thirdPilotNameLineEdit->setText(picname);
  512. thirdPilot = dbPilots::newPicGetId(picname);
  513. }else
  514. {
  515. ui->thirdPilotNameLineEdit->setStyleSheet("border: 1px solid red");
  516. /*QMessageBox::StandardButton reply;
  517. reply = QMessageBox::question(this, "No Pilot found", "No pilot found.\n Would you like to add a new pilot to the database?",
  518. QMessageBox::Yes|QMessageBox::No);
  519. if (reply == QMessageBox::Yes)
  520. {
  521. qDebug() << "Add new pilot selected";
  522. nope();
  523. }*/
  524. }
  525. }
  526. }
  527. void NewFlight::on_FlightNumberLineEdit_editingFinished()
  528. {
  529. qDebug() << "tbd: FlightNumber slot";
  530. // To Do: Store input in variable, perform some checks
  531. // Setting for optional Prefix (e.g. LH, LX etc.)
  532. }
  533. /*
  534. * Extras Tab - These are for user convenience. From many of
  535. * these selections, determinations can be made on how to log
  536. * details, so that the user does not have to enter each item
  537. * manually. See also fillExtrasLineEdits()
  538. */
  539. void NewFlight::on_setAsDefaultButton_clicked()
  540. {
  541. storeSettings();
  542. }
  543. void NewFlight::on_restoreDefaultButton_clicked()
  544. {
  545. restoreSettings();
  546. }
  547. /*!
  548. * \brief On a given flight, time can either be logged as Pilot Flying (PF) or
  549. * Pilot Monitoring (PM). Cases where controls are changed during the flight
  550. * are rare and can be logged by manually editing the extras.
  551. */
  552. void NewFlight::on_PilotFlyingCheckBox_stateChanged(int)
  553. {
  554. if(ui->PilotFlyingCheckBox->isChecked()){
  555. ui->TakeoffSpinBox->setValue(1);
  556. ui->TakeoffCheckBox->setCheckState(Qt::Checked);
  557. ui->LandingSpinBox->setValue(1);
  558. ui->LandingCheckBox->setCheckState(Qt::Checked);
  559. }else if(!ui->PilotFlyingCheckBox->isChecked()){
  560. ui->TakeoffSpinBox->setValue(0);
  561. ui->TakeoffCheckBox->setCheckState(Qt::Unchecked);
  562. ui->LandingSpinBox->setValue(0);
  563. ui->LandingCheckBox->setCheckState(Qt::Unchecked);
  564. }
  565. }
  566. void NewFlight::on_ApproachComboBox_currentTextChanged(const QString &arg1)
  567. {
  568. if(arg1 == "ILS CAT III"){ //for a CAT III approach an Autoland is mandatory, so we can preselect it.
  569. ui->AutolandCheckBox->setCheckState(Qt::Checked);
  570. ui->AutolandSpinBox->setValue(1);
  571. }else{
  572. ui->AutolandCheckBox->setCheckState(Qt::Unchecked);
  573. ui->AutolandSpinBox->setValue(0);
  574. }
  575. approachType = arg1;
  576. qDebug() << "Approach Type: " << approachType;
  577. }
  578. /*
  579. * Extra Times - These line edits should be filled out automatically,
  580. * based on the ui selections and the user provided input. However,
  581. * manual adjustments are possible to cater for situations where for
  582. * example one portion of the flight is logged under different rules
  583. * than the rest of it.
  584. *
  585. * For example,
  586. * if we know the aircraft details we can determine how to log these times.
  587. * Some times are mutually exclusive, others can be combined.
  588. *
  589. * For example,
  590. * for a commercial Passenger flight, the commander can log all time as
  591. * Total Time and PIC time. If the aircraft is a multi-engine jet he can
  592. * also log Multi-Pilot time, and if he is an instructor, instructor time.
  593. *
  594. * It is not possible, however to log flight time as VFR or IFR time
  595. * simultaneously, as a flight at any given point in time can only follow
  596. * one set of rules. It is possible, to change flight rules and log the first
  597. * x minutes as VFR and the rest of it as IFR, for example. Hence the need
  598. * for the possibility to edit these times manually.
  599. *
  600. * The most complex time to determine is night time, see documentation of
  601. * the calc class for details.
  602. *
  603. * In General, the idea is to automatically fill as much as possible, but
  604. * if the user decides to change these times, accept the inputs, as long as
  605. * they are generally valid. We cannot cater for all possibilities, so as long
  606. * as the time the user has input is a valid time <= Total Time, it can be
  607. * accepted to the database.
  608. */
  609. void NewFlight::on_spseTimeLineEdit_editingFinished()
  610. {
  611. ui->spseTimeLineEdit->setText(calc::formatTimeInput(ui->spseTimeLineEdit->text()));
  612. if(ui->spseTimeLineEdit->text() == ""){
  613. ui->spseTimeLineEdit->setStyleSheet("border: 1px solid red");
  614. ui->spseTimeLineEdit->setFocus();
  615. }else{
  616. ui->spseTimeLineEdit->setStyleSheet("");
  617. }
  618. }
  619. void NewFlight::on_spmeTimeLineEdit_editingFinished()
  620. {
  621. ui->spmeTimeLineEdit->setText(calc::formatTimeInput(ui->spmeTimeLineEdit->text()));
  622. if(ui->spmeTimeLineEdit->text() == ""){
  623. ui->spmeTimeLineEdit->setStyleSheet("border: 1px solid red");
  624. ui->spmeTimeLineEdit->setFocus();
  625. }else{
  626. ui->spmeTimeLineEdit->setStyleSheet("");
  627. }
  628. }
  629. void NewFlight::on_mpTimeLineEdit_editingFinished()
  630. {
  631. ui->mpTimeLineEdit->setText(calc::formatTimeInput(ui->mpTimeLineEdit->text()));
  632. if(ui->mpTimeLineEdit->text() == ""){
  633. ui->mpTimeLineEdit->setStyleSheet("border: 1px solid red");
  634. ui->mpTimeLineEdit->setFocus();
  635. }else{
  636. ui->mpTimeLineEdit->setStyleSheet("");
  637. }
  638. }
  639. void NewFlight::on_totalTimeLineEdit_editingFinished()
  640. {
  641. ui->totalTimeLineEdit->setText(calc::formatTimeInput(ui->totalTimeLineEdit->text()));
  642. if(ui->totalTimeLineEdit->text() == ""){
  643. ui->totalTimeLineEdit->setStyleSheet("border: 1px solid red");
  644. ui->totalTimeLineEdit->setFocus();
  645. }else{
  646. ui->totalTimeLineEdit->setStyleSheet("");
  647. }
  648. }
  649. void NewFlight::on_ifrTimeLineEdit_editingFinished()
  650. {
  651. ui->ifrTimeLineEdit->setText(calc::formatTimeInput(ui->ifrTimeLineEdit->text()));
  652. if(ui->ifrTimeLineEdit->text() == ""){
  653. ui->ifrTimeLineEdit->setStyleSheet("border: 1px solid red");
  654. ui->ifrTimeLineEdit->setFocus();
  655. }else{
  656. ui->ifrTimeLineEdit->setStyleSheet("");
  657. }
  658. }
  659. void NewFlight::on_vfrTimeLineEdit_editingFinished()
  660. {
  661. ui->vfrTimeLineEdit->setText(calc::formatTimeInput(ui->vfrTimeLineEdit->text()));
  662. if(ui->vfrTimeLineEdit->text() == ""){
  663. ui->vfrTimeLineEdit->setStyleSheet("border: 1px solid red");
  664. ui->vfrTimeLineEdit->setFocus();
  665. }else{
  666. ui->vfrTimeLineEdit->setStyleSheet("");
  667. }
  668. }
  669. void NewFlight::on_nightTimeLineEdit_editingFinished()
  670. {
  671. ui->nightTimeLineEdit->setText(calc::formatTimeInput(ui->nightTimeLineEdit->text()));
  672. if(ui->nightTimeLineEdit->text() == ""){
  673. ui->nightTimeLineEdit->setStyleSheet("border: 1px solid red");
  674. ui->nightTimeLineEdit->setFocus();
  675. }else{
  676. ui->nightTimeLineEdit->setStyleSheet("");
  677. }
  678. }
  679. void NewFlight::on_xcTimeLineEdit_editingFinished()
  680. {
  681. ui->xcTimeLineEdit->setText(calc::formatTimeInput(ui->xcTimeLineEdit->text()));
  682. if(ui->xcTimeLineEdit->text() == ""){
  683. ui->xcTimeLineEdit->setStyleSheet("border: 1px solid red");
  684. ui->xcTimeLineEdit->setFocus();
  685. }else{
  686. ui->xcTimeLineEdit->setStyleSheet("");
  687. }
  688. }
  689. void NewFlight::on_picTimeLineEdit_editingFinished()
  690. {
  691. ui->picTimeLineEdit->setText(calc::formatTimeInput(ui->picTimeLineEdit->text()));
  692. if(ui->picTimeLineEdit->text() == ""){
  693. ui->picTimeLineEdit->setStyleSheet("border: 1px solid red");
  694. ui->picTimeLineEdit->setFocus();
  695. }else{
  696. ui->picTimeLineEdit->setStyleSheet("");
  697. }
  698. }
  699. void NewFlight::on_copTimeLineEdit_editingFinished()
  700. {
  701. ui->copTimeLineEdit->setText(calc::formatTimeInput(ui->copTimeLineEdit->text()));
  702. if(ui->copTimeLineEdit->text() == ""){
  703. ui->copTimeLineEdit->setStyleSheet("border: 1px solid red");
  704. ui->copTimeLineEdit->setFocus();
  705. }else{
  706. ui->copTimeLineEdit->setStyleSheet("");
  707. }
  708. }
  709. void NewFlight::on_dualTimeLineEdit_editingFinished()
  710. {
  711. ui->dualTimeLineEdit->setText(calc::formatTimeInput(ui->dualTimeLineEdit->text()));
  712. if(ui->dualTimeLineEdit->text() == ""){
  713. ui->dualTimeLineEdit->setStyleSheet("border: 1px solid red");
  714. ui->dualTimeLineEdit->setFocus();
  715. }else{
  716. ui->dualTimeLineEdit->setStyleSheet("");
  717. }
  718. }
  719. void NewFlight::on_fiTimeLineEdit_editingFinished()
  720. {
  721. ui->fiTimeLineEdit->setText(calc::formatTimeInput(ui->fiTimeLineEdit->text()));
  722. if(ui->fiTimeLineEdit->text() == ""){
  723. ui->fiTimeLineEdit->setStyleSheet("border: 1px solid red");
  724. ui->fiTimeLineEdit->setFocus();
  725. }else{
  726. ui->fiTimeLineEdit->setStyleSheet("");
  727. }
  728. }
  729. void NewFlight::on_simTimeLineEdit_editingFinished()
  730. {
  731. ui->simTimeLineEdit->setText(calc::formatTimeInput(ui->simTimeLineEdit->text()));
  732. if(ui->simTimeLineEdit->text() == ""){
  733. ui->simTimeLineEdit->setStyleSheet("border: 1px solid red");
  734. ui->simTimeLineEdit->setFocus();
  735. }else{
  736. ui->simTimeLineEdit->setStyleSheet("");
  737. }
  738. }
  739. /*
  740. * Control Buttons
  741. */
  742. void NewFlight::on_buttonBox_accepted()
  743. {
  744. on_newDoft_editingFinished();// - activate slots in case user has been active in last input before clicking submit
  745. //on_newTonb_editingFinished();
  746. //on_newTofb_editingFinished();
  747. //on_newDept_editingFinished();
  748. //on_newDest_editingFinished();
  749. on_newAcft_editingFinished();
  750. on_newPicNameLineEdit_editingFinished();
  751. QVector<QString> flight;
  752. flight = collectInput();
  753. if(verifyInput())
  754. {
  755. dbFlight::commitFlight(flight);
  756. qDebug() << flight << "Has been commited.";
  757. QMessageBox msgBox(this);
  758. msgBox.setText("Flight has been commited.");
  759. msgBox.exec();
  760. }else
  761. {
  762. qDebug() << "Invalid Input. No entry has been made in the database.";
  763. dbFlight::commitToScratchpad(flight);
  764. QMessageBox msgBox(this);
  765. msgBox.setText("Invalid entries detected. Please check your input.");
  766. msgBox.exec();
  767. //NewFlight nf(this);
  768. //nf.exec();
  769. }
  770. }
  771. void NewFlight::on_buttonBox_rejected()
  772. {
  773. qDebug() << "NewFlight: Rejected\n";
  774. }
  775. /// Input Verification and Collection
  776. QVector<QString> NewFlight::collectInput()
  777. {
  778. // Collect input from the user fields and return a vector containing the flight information
  779. QString doft = date.toString(Qt::ISODate); // Format Date
  780. QTime tblk = calc::blocktime(tofb,tonb); // Calculate Blocktime
  781. // Prepare Vector for commit to database
  782. flight = dbFlight::createFlightVectorFromInput(doft, dept, tofb, dest, tonb, tblk, pic, acft);
  783. qDebug() << "Created flight vector:" << flight;
  784. return flight;
  785. }
  786. /*!
  787. * \brief NewFlight::verifyInput checks if input exists in database.
  788. * \return
  789. */
  790. bool NewFlight::verifyInput()
  791. {
  792. bool deptValid = false;
  793. bool tofbValid = false;
  794. bool destValid = false;
  795. bool tonbValid = false;
  796. bool tblkValid = false;
  797. bool picValid = false;
  798. bool acftValid = false;
  799. QTime tblk = calc::blocktime(tofb,tonb);
  800. int checktblk = calc::time_to_minutes(tblk);
  801. bool doftValid = true; //doft assumed to be always valid due to QDateTimeEdit constraints
  802. qDebug() << "NewFlight::verifyInput() says: Date:" << doft << " - Valid?\t" << doftValid;
  803. deptValid = dbAirport::checkICAOValid(dept);
  804. qDebug() << "NewFlight::verifyInput() says: Departure is:\t" << dept << " - Valid?\t" << deptValid;
  805. destValid = dbAirport::checkICAOValid(dest);
  806. qDebug() << "NewFlight::verifyInput() says: Destination is:\t" << dest << " - Valid?\t" << destValid;
  807. 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.
  808. qDebug() << "NewFlight::verifyInput() says: tofb is:\t\t" << tofb.toString("hh:mm") << " - Valid?\t" << tofbValid;
  809. 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
  810. qDebug() << "NewFlight::verifyInput() says: tonb is:\t\t" << tonb.toString("hh:mm")<< " - Valid?\t" << tonbValid;
  811. picValid = (pic.toInt() > 0); // pic should be a pilot_id retreived from the database
  812. qDebug() << "NewFlight::verifyInput() says: pic is pilotd_id:\t" << pic << " - Valid?\t" << picValid;
  813. if(!picValid)
  814. {
  815. QMessageBox msgBox(this);
  816. msgBox.setText("You cannot log a flight without a valid pilot");
  817. msgBox.exec();
  818. }
  819. acftValid = (acft.toInt() > 0);
  820. qDebug() << "NewFlight::verifyInput() says: acft is tail_id:\t" << acft << " - Valid?\t" << acftValid;
  821. if(!acftValid)
  822. {
  823. QMessageBox msgBox(this);
  824. msgBox.setText("You cannot log a flight without a valid aircraft");
  825. msgBox.exec();
  826. }
  827. tblkValid = checktblk != 0;
  828. qDebug() << "NewFlight::verifyInput() says: tblk is:\t\t" << tblk.toString("hh:mm") << " - Valid?\t" << tblkValid;
  829. if(deptValid && tofbValid && destValid && tonbValid
  830. && tblkValid && picValid && acftValid)
  831. {
  832. qDebug() << "====================================================";
  833. qDebug() << "NewFlight::verifyInput() says: All checks passed! Very impressive. ";
  834. return 1;
  835. }else
  836. {
  837. qDebug() << "====================================================";
  838. qDebug() << "NewFlight::verifyInput() says: Flight is invalid.";
  839. return 0;
  840. }
  841. return 0;
  842. }
  843. void NewFlight::returnInput(QVector<QString> flight)
  844. {
  845. // Re-populates the input masks with the selected fields if input was erroneous to allow for corrections to be made
  846. qDebug() << "return Input: " << flight;
  847. ui->newDoft->setDate(QDate::fromString(flight[1],Qt::ISODate));
  848. ui->newDeptLocLineEdit->setText(flight[2]);
  849. ui->newDeptTimeLineEdit->setText(flight[3]);
  850. ui->newDestLocLineEdit->setText(flight[4]);
  851. ui->newDestTimeLineEdit->setText(flight[5]);
  852. // flight[6] is blocktime
  853. ui->newPicNameLineEdit->setText(dbPilots::retreivePilotNameFromID(flight[7]));
  854. ui->newAcft->setText(dbAircraft::retreiveRegistration(flight[8]));
  855. }
  856. /*!
  857. * \brief NewFlight::fillExtrasLineEdits Fills the flight time line edits according to ui selections.
  858. * Neccessary inputs are valid Date, Departure Time and Place, Destination Time and Place,
  859. * PIC name (pilot_id) and Aircraft (tail_id)
  860. */
  861. void NewFlight::fillExtrasLineEdits()
  862. {
  863. QString blockTime = calc::blocktime(tofb,tonb).toString("hh:mm");
  864. DEBUG(blockTime);
  865. QVector<QString> aircraftDetails = dbAircraft::retreiveAircraftDetails(dbAircraft::retreiveAircraftId(acft));
  866. DEBUG("aircraftDetails: " << aircraftDetails);
  867. if(!aircraftDetails.isEmpty()){// valid aircraft
  868. // SP SE
  869. if(aircraftDetails[0] == "1" && aircraftDetails[2] == "1"){
  870. DEBUG("SPSE yes");
  871. tSPSE = blockTime;
  872. ui->spseTimeLineEdit->setText(blockTime);
  873. }
  874. // SP ME
  875. if(aircraftDetails[0] == "1" && aircraftDetails[3] == "1"){
  876. DEBUG("SP ME yes");
  877. tSPME = blockTime;
  878. ui->spmeTimeLineEdit->setText(blockTime);
  879. }
  880. // MP
  881. if(aircraftDetails[1] == "1"){
  882. DEBUG("Multipilot yes");
  883. tMP = blockTime;
  884. ui->mpTimeLineEdit->setText(blockTime);
  885. }
  886. }else{DEBUG("Aircraft Details Empty");}//invalid aircraft
  887. // TOTAL
  888. ui->totalTimeLineEdit->setText(blockTime);
  889. // IFR
  890. if(ui->IfrCheckBox->isChecked()){
  891. ui->ifrTimeLineEdit->setText(blockTime);
  892. ui->vfrTimeLineEdit->setText("");
  893. }
  894. // VFR
  895. if(ui->VfrCheckBox->isChecked()){
  896. ui->vfrTimeLineEdit->setText(blockTime);
  897. ui->ifrTimeLineEdit->setText("");
  898. }
  899. // Night
  900. QString deptDate = date.toString(Qt::ISODate) + 'T' + tofb.toString("hh:mm");
  901. QDateTime deptDateTime = QDateTime::fromString(deptDate,"yyyy-MM-ddThh:mm");
  902. int tblk = calc::time_to_minutes(calc::blocktime(tofb,tonb));
  903. QString nightTime = calc::minutes_to_string(
  904. QString::number(
  905. calc::calculateNightTime(
  906. dept, dest, deptDateTime, tblk)));
  907. ui->nightTimeLineEdit->setText(nightTime);
  908. // XC - Cross-country flight, if more than 50nm long
  909. if(calc::greatCircleDistanceBetweenAirports(dept,dest) >= 50){
  910. qDebug() << "Cross-country Flight: nm = " << calc::greatCircleDistanceBetweenAirports(dept,dest);
  911. ui->xcTimeLineEdit->setText(blockTime);
  912. }else{ui->xcTimeLineEdit->setText("00:00");}
  913. // Function times
  914. switch (ui->FunctionComboBox->currentIndex()) {
  915. case 0://PIC
  916. ui->picTimeLineEdit->setText(blockTime);
  917. ui->copTimeLineEdit->setText("");
  918. ui->dualTimeLineEdit->setText("");
  919. ui->fiTimeLineEdit->setText("");
  920. break;
  921. case 1://Co-Pilot
  922. ui->picTimeLineEdit->setText("");
  923. ui->copTimeLineEdit->setText(blockTime);
  924. ui->dualTimeLineEdit->setText("");
  925. ui->fiTimeLineEdit->setText("");
  926. break;
  927. case 2://Dual
  928. ui->picTimeLineEdit->setText("");
  929. ui->copTimeLineEdit->setText("");
  930. ui->dualTimeLineEdit->setText(blockTime);
  931. ui->fiTimeLineEdit->setText("");
  932. break;
  933. case 3://Instructor
  934. ui->picTimeLineEdit->setText("");
  935. ui->copTimeLineEdit->setText("");
  936. ui->dualTimeLineEdit->setText("");
  937. ui->fiTimeLineEdit->setText(blockTime);
  938. }
  939. // SIM
  940. }
  941. /*!
  942. * \brief In case the user wants to manually edit the extra times
  943. * he can do so in this tab.
  944. */
  945. void NewFlight::on_tabWidget_currentChanged(int index)
  946. {
  947. if(index == 1){// Edit Details tab
  948. if(verifyInput()){
  949. fillExtrasLineEdits();
  950. }else{
  951. auto errorbox = new QMessageBox;
  952. errorbox->setText("Invalid Inputs. Unable to determine times automatically.\nPlease verify your inputs.");
  953. errorbox->exec();
  954. }
  955. }
  956. }