newnewflightdialog.cpp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394
  1. #include "newnewflightdialog.h"
  2. #include "ui_newnewflightdialog.h"
  3. #include "src/opl.h"
  4. #include "src/functions/alog.h"
  5. #include "src/functions/adate.h"
  6. #include "src/classes/asettings.h"
  7. #include "src/functions/acalc.h"
  8. #include <QDateTime>
  9. #include <QCompleter>
  10. #include <QKeyEvent>
  11. NewNewFlightDialog::NewNewFlightDialog(ACompletionData &completion_data,
  12. QWidget *parent)
  13. : QDialog(parent),
  14. ui(new Ui::NewNewFlightDialog),
  15. completionData(completion_data)
  16. {
  17. ui->setupUi(this);
  18. init();
  19. }
  20. NewNewFlightDialog::~NewNewFlightDialog()
  21. {
  22. delete ui;
  23. }
  24. void NewNewFlightDialog::init()
  25. {
  26. // Initialise line edit lists
  27. timeLineEdits = {ui->tofbTimeLineEdit, ui->tonbTimeLineEdit};
  28. locationLineEdits = {ui->deptLocationLineEdit, ui->destLocationLineEdit};
  29. pilotNameLineEdits = {ui->picNameLineEdit, ui->sicNameLineEdit, ui->thirdPilotNameLineEdit};
  30. // {doft = 0, dept = 1, dest = 2, tofb = 3, tonb = 4, pic = 5, acft = 6}
  31. mandatoryLineEdits = {ui->doftLineEdit, ui->deptLocationLineEdit, ui->destLocationLineEdit,
  32. ui->tofbTimeLineEdit, ui->tonbTimeLineEdit,
  33. ui->picNameLineEdit, ui->acftLineEdit};
  34. setupRawInputValidation();
  35. setupSignalsAndSlots();
  36. //readSettings();
  37. ui->doftLineEdit->setText(QDate::currentDate().toString(Qt::ISODate));
  38. emit ui->doftLineEdit->editingFinished();
  39. }
  40. /*!
  41. * \brief NewNewFlightDialog::setupRawInputValidation outfits the line edits with QRegularExpresionValidators and QCompleters
  42. */
  43. void NewNewFlightDialog::setupRawInputValidation()
  44. {
  45. // Time Line Edits
  46. for (const auto& line_edit : qAsConst(timeLineEdits)) {
  47. auto validator = new QRegularExpressionValidator(QRegularExpression("([01]?[0-9]|2[0-3]):?[0-5][0-9]?"), line_edit);
  48. line_edit->setValidator(validator);
  49. }
  50. // Location Line Edits
  51. for (const auto& line_edit : qAsConst(locationLineEdits)) {
  52. auto validator = new QRegularExpressionValidator(QRegularExpression("[a-zA-Z0-9]{1,4}"), line_edit);
  53. line_edit->setValidator(validator);
  54. auto completer = new QCompleter(completionData.airportList, line_edit);
  55. completer->setCaseSensitivity(Qt::CaseInsensitive);
  56. completer->setCompletionMode(QCompleter::PopupCompletion);
  57. completer->setFilterMode(Qt::MatchContains);
  58. line_edit->setCompleter(completer);
  59. }
  60. // Name Line Edits
  61. for (const auto& line_edit : qAsConst(pilotNameLineEdits)) {
  62. auto completer = new QCompleter(completionData.pilotList, line_edit);
  63. completer->setCaseSensitivity(Qt::CaseInsensitive);
  64. completer->setCompletionMode(QCompleter::PopupCompletion);
  65. completer->setFilterMode(Qt::MatchContains);
  66. line_edit->setCompleter(completer);
  67. }
  68. // Acft Line Edit
  69. auto completer = new QCompleter(completionData.tailsList, ui->acftLineEdit);
  70. completer->setCaseSensitivity(Qt::CaseInsensitive);
  71. completer->setCompletionMode(QCompleter::PopupCompletion);
  72. completer->setFilterMode(Qt::MatchContains);
  73. ui->acftLineEdit->setCompleter(completer);
  74. }
  75. void NewNewFlightDialog::setupSignalsAndSlots()
  76. {
  77. for (const auto& line_edit : qAsConst(timeLineEdits))
  78. QObject::connect(line_edit, &QLineEdit::editingFinished,
  79. this, &NewNewFlightDialog::onTimeLineEdit_editingFinished);
  80. // Change text to upper case for location and acft line edits
  81. QObject::connect(ui->acftLineEdit, &QLineEdit::textChanged,
  82. this, &NewNewFlightDialog::toUpper);
  83. for (const auto& line_edit : qAsConst(locationLineEdits)) {
  84. QObject::connect(line_edit, &QLineEdit::textChanged,
  85. this, &NewNewFlightDialog::toUpper);
  86. QObject::connect(line_edit, &QLineEdit::editingFinished,
  87. this, &NewNewFlightDialog::onLocationLineEdit_editingFinished);
  88. }
  89. for (const auto& line_edit : qAsConst(pilotNameLineEdits))
  90. QObject::connect(line_edit, &QLineEdit::editingFinished,
  91. this, &NewNewFlightDialog::onPilotNameLineEdit_editingFinshed);
  92. }
  93. void NewNewFlightDialog::onGoodInputReceived(QLineEdit *line_edit)
  94. {
  95. DEB << line_edit->objectName() << " - Good input received - " << line_edit->text();
  96. line_edit->setStyleSheet(QString());
  97. if (mandatoryLineEdits.contains(line_edit))
  98. validationState.validate(mandatoryLineEdits.indexOf(line_edit));
  99. if (validationState.timesValid()) {
  100. DEB << "All mandatory Line Edits valid!";
  101. // Update Block Time Label
  102. QTime tblk = calculateBlockTime();
  103. ui->tblkDisplayLabel->setText(ATime::toString(tblk));
  104. if (validationState.allValid())
  105. updateNightCheckBoxes();
  106. }
  107. validationState.printValidationStatus();
  108. }
  109. void NewNewFlightDialog::onBadInputReceived(QLineEdit *line_edit)
  110. {
  111. DEB << line_edit->objectName() << " - Bad input received - " << line_edit->text();
  112. line_edit->setStyleSheet(QStringLiteral("border: 1px solid red"));
  113. line_edit->setText(QString());
  114. if (mandatoryLineEdits.contains(line_edit))
  115. validationState.invalidate(mandatoryLineEdits.indexOf(line_edit));
  116. validationState.printValidationStatus();
  117. }
  118. QTime NewNewFlightDialog::calculateBlockTime()
  119. {
  120. if (!validationState.timesValid())
  121. return {};
  122. const auto tofb = ATime::fromString(ui->tofbTimeLineEdit->text());
  123. const auto tonb = ATime::fromString(ui->tonbTimeLineEdit->text());
  124. return ATime::blocktime(tofb, tonb);
  125. }
  126. /*!
  127. * \brief NewNewFlightDialog::updateNightCheckBoxes updates the check boxes for take-off and landing
  128. * at night. Returns the number of minutes of night time.
  129. * \return
  130. */
  131. int NewNewFlightDialog::updateNightCheckBoxes()
  132. {
  133. if (!validationState.allValid())
  134. return 0;
  135. // Calculate Block Time
  136. const auto tofb = ATime::fromString(ui->tofbTimeLineEdit->text());
  137. const auto tonb = ATime::fromString(ui->tonbTimeLineEdit->text());
  138. const auto tblk = ATime::blocktime(tofb, tonb);
  139. const auto block_minutes = ATime::toMinutes(tblk);
  140. // Calculate Night Time
  141. const QString dept_date = ui->doftLineEdit->text() + 'T'
  142. + ATime::toString(tofb);
  143. const auto dept_date_time = QDateTime::fromString(dept_date, QStringLiteral("yyyy-MM-ddThh:mm"));
  144. const int night_angle = ASettings::read(ASettings::FlightLogging::NightAngle).toInt();
  145. const auto night_time = ATime::fromMinutes(ACalc::calculateNightTime(
  146. ui->deptLocationLineEdit->text(),
  147. ui->destLocationLineEdit->text(),
  148. dept_date_time,
  149. block_minutes,
  150. night_angle));
  151. const auto night_minutes = ATime::toMinutes(night_time);
  152. // set check boxes
  153. if (night_minutes == 0) {
  154. ui->toNightCheckBox->setCheckState(Qt::Unchecked);
  155. ui->ldgNightCheckBox->setCheckState(Qt::Unchecked);
  156. }
  157. else if (night_minutes == ATime::toMinutes(calculateBlockTime())) {
  158. ui->toNightCheckBox->setCheckState(Qt::Checked);
  159. ui->ldgNightCheckBox->setCheckState(Qt::Checked);
  160. } else {
  161. if(ACalc::isNight(ui->deptLocationLineEdit->text(), dept_date_time, night_angle)) {
  162. ui->toNightCheckBox->setCheckState(Qt::Checked);
  163. ui->ldgNightCheckBox->setCheckState(Qt::Unchecked);
  164. } else {
  165. ui->toNightCheckBox->setCheckState(Qt::Unchecked);
  166. ui->ldgNightCheckBox->setCheckState(Qt::Checked);
  167. }
  168. }
  169. return ATime::toMinutes(night_time);
  170. }
  171. // # Slots
  172. void NewNewFlightDialog::toUpper(const QString &text)
  173. {
  174. const auto line_edit = this->findChild<QLineEdit*>(sender()->objectName());
  175. {
  176. const QSignalBlocker blocker(line_edit);
  177. line_edit->setText(text.toUpper());
  178. }
  179. }
  180. void NewNewFlightDialog::onTimeLineEdit_editingFinished()
  181. {
  182. auto line_edit = this->findChild<QLineEdit*>(sender()->objectName());
  183. DEB << line_edit->objectName() << "Editing finished -" << line_edit->text();
  184. const QString time_string = ATime::formatTimeInput(line_edit->text());
  185. const QTime time = ATime::fromString(time_string);
  186. if (time.isValid()) {
  187. line_edit->setText(time_string);
  188. onGoodInputReceived(line_edit);
  189. } else {
  190. onBadInputReceived(line_edit);
  191. line_edit->setText(QString());
  192. }
  193. }
  194. void NewNewFlightDialog::onPilotNameLineEdit_editingFinshed()
  195. {
  196. auto line_edit = this->findChild<QLineEdit*>(sender()->objectName());
  197. DEB << line_edit->objectName() << "Editing Finished -" << line_edit->text();
  198. if(line_edit->text().contains(self, Qt::CaseInsensitive)) {
  199. DEB << "self recognized.";
  200. line_edit->setText(completionData.pilotsIdMap.value(1));
  201. onGoodInputReceived(line_edit);
  202. return;
  203. }
  204. if(completionData.pilotsIdMap.key(line_edit->text()) != 0) {
  205. DEB << "Mapped: " << line_edit->text() << completionData.pilotsIdMap.key(line_edit->text());
  206. onGoodInputReceived(line_edit);
  207. return;
  208. }
  209. if (line_edit->text().isEmpty()) {
  210. return;
  211. }
  212. if (!line_edit->completer()->currentCompletion().isEmpty()) {
  213. DEB << "Trying to fix input...";
  214. line_edit->setText(line_edit->completer()->currentCompletion());
  215. emit line_edit->editingFinished();
  216. return;
  217. }
  218. onBadInputReceived(line_edit);
  219. TODO << "Add new Pilot...";
  220. //addNewPilot(line_edit);
  221. }
  222. void NewNewFlightDialog::onLocationLineEdit_editingFinished()
  223. {
  224. const QString line_edit_name = sender()->objectName();
  225. const auto line_edit = this->findChild<QLineEdit*>(line_edit_name);
  226. DEB << line_edit->objectName() << "Editing Finished -" << line_edit->text();
  227. QLabel* name_label;
  228. if (line_edit_name.contains(QLatin1String("dept"))) {
  229. name_label = ui->deptNameLabel;
  230. } else {
  231. name_label = ui->destNameLabel;
  232. }
  233. const auto &text = line_edit->text();
  234. int airport_id = 0;
  235. // try to map iata or icao code to airport id;
  236. if (text.length() == 3) {
  237. airport_id = completionData.airportIataIdMap.key(text);
  238. } else {
  239. airport_id = completionData.airportIcaoIdMap.key(text);
  240. }
  241. // check result
  242. if (airport_id == 0) {
  243. // to do: prompt user how to handle unknown airport
  244. name_label->setText(tr("Unknown airport identifier"));
  245. onBadInputReceived(line_edit);
  246. return;
  247. }
  248. line_edit->setText(completionData.airportIcaoIdMap.value(airport_id));
  249. name_label->setText(completionData.airportNameIdMap.value(airport_id));
  250. onGoodInputReceived(line_edit);
  251. }
  252. void NewNewFlightDialog::on_acftLineEdit_editingFinished()
  253. {
  254. const auto line_edit = ui->acftLineEdit;
  255. int acft_id = completionData.tailsIdMap.key(line_edit->text());
  256. DEB << "acft_id: " << acft_id;
  257. if (acft_id != 0) { // Success
  258. auto acft = aDB->getTailEntry(acft_id);
  259. ui->acftDisplayLabel->setText(acft.type());
  260. onGoodInputReceived(line_edit);
  261. return;
  262. }
  263. // try to use a completion
  264. if (!line_edit->completer()->currentCompletion().isEmpty() && !line_edit->text().isEmpty()) {
  265. line_edit->setText(line_edit->completer()->currentCompletion().split(QLatin1Char(' ')).first());
  266. emit line_edit->editingFinished();
  267. return;
  268. }
  269. // Mark as bad input and prompt for adding new tail
  270. onBadInputReceived(line_edit);
  271. ui->acftDisplayLabel->setText(tr("Unknown Registration."));
  272. WARN("Add new tail..");
  273. }
  274. void NewNewFlightDialog::on_doftLineEdit_editingFinished()
  275. {
  276. const auto line_edit = ui->doftLineEdit;
  277. auto text = ui->doftLineEdit->text();
  278. auto label = ui->doftDisplayLabel;
  279. //DEB << line_edit->objectName() << "Editing finished - " << text;
  280. TODO << "Non-default Date formats not implemented yet.";
  281. Opl::Date::ADateFormat date_format = Opl::Date::ADateFormat::ISODate;
  282. auto date = ADate::parseInput(text, date_format);
  283. if (date.isValid()) {
  284. label->setText(date.toString(Qt::TextDate));
  285. line_edit->setText(ADate::toString(date, date_format));
  286. onGoodInputReceived(line_edit);
  287. return;
  288. }
  289. label->setText(tr("Invalid Date."));
  290. onBadInputReceived(line_edit);
  291. }
  292. void NewNewFlightDialog::on_pilotFlyingCheckBox_stateChanged(int arg1)
  293. {
  294. if (arg1 == Qt::CheckState::Checked) {
  295. ui->takeOffSpinBox->setValue(1);
  296. ui->landingSpinBox->setValue(1);
  297. } else {
  298. ui->takeOffSpinBox->setValue(0);
  299. ui->landingSpinBox->setValue(0);
  300. }
  301. }
  302. void NewNewFlightDialog::on_buttonBox_accepted()
  303. {
  304. // emit editing finished for all mandatory line edits to trigger input verification
  305. for (const auto &line_edit : qAsConst(mandatoryLineEdits)) {
  306. emit line_edit->editingFinished();
  307. }
  308. // If input verification is passed, continue, otherwise prompt user to correct
  309. // enum ValidationItem {doft = 0, dept = 1, dest = 2, tofb = 3, tonb = 4, pic = 5, acft = 6};
  310. if (!validationState.allValid()) {
  311. const auto display_names = QMap<ValidationItem, QString> {
  312. {ValidationItem::doft, QObject::tr("Date of Flight")}, {ValidationItem::dept, QObject::tr("Departure Airport")},
  313. {ValidationItem::dest, QObject::tr("Destination Airport")}, {ValidationItem::tofb, QObject::tr("Time Off Blocks")},
  314. {ValidationItem::tonb, QObject::tr("Time on Blocks")}, {ValidationItem::pic, QObject::tr("PIC Name")},
  315. {ValidationItem::acft, QObject::tr("Aircraft Registration")}
  316. };
  317. QString missing_items;
  318. for (int i=0; i < mandatoryLineEdits.size(); i++) {
  319. if (!validationState.validAt(i)){
  320. missing_items.append(display_names.value(static_cast<ValidationItem>(i)) + "<br>");
  321. mandatoryLineEdits[i]->setStyleSheet(QStringLiteral("border: 1px solid red"));
  322. }
  323. }
  324. INFO(tr("Not all mandatory entries are valid.<br>"
  325. "The following item(s) are empty or missing:"
  326. "<br><br><center><b>%1</b></center><br>"
  327. "Please go back and fill in the required data."
  328. ).arg(missing_items));
  329. return;
  330. }
  331. // If input verification passed, collect input and submit to database
  332. //auto newData = collectInput();
  333. //DEB << "Setting Data for flightEntry...";
  334. //flightEntry.setData(newData);
  335. //DEB << "Committing...";
  336. //if (!aDB->commit(flightEntry)) {
  337. // WARN(tr("The following error has ocurred:"
  338. // "<br><br>%1<br><br>"
  339. // "The entry has not been saved."
  340. // ).arg(aDB->lastError.text()));
  341. // return;
  342. //} else {
  343. // QDialog::accept();
  344. //}
  345. }