newflight.cpp 37 KB

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