2
0

oldnewflightdialog.cpp 50 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232
  1. /*
  2. *openPilotLog - A FOSS Pilot Logbook Application
  3. *Copyright (C) 2020-2021 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 "newflightdialog.h"
  19. #include "ui_newflight.h"
  20. #include "src/gui/dialogues/newtaildialog.h"
  21. #include "src/gui/dialogues/newpilotdialog.h"
  22. #include "src/functions/acalc.h"
  23. #include "src/testing/atimer.h"
  24. #include "src/database/adatabase.h"
  25. #include "src/opl.h"
  26. #include "src/functions/adate.h"
  27. #include "src/functions/alog.h"
  28. const auto LOC_LINE_EDIT_NAME = QLatin1String("Loc");
  29. const auto ACFT_LINE_EDIT_NAME = QLatin1String("acft");
  30. const auto TIME_LINE_EDIT_NAME = QLatin1String("Time");
  31. const auto PILOT_LINE_EDIT_NAME = QLatin1String("Name");
  32. const auto SELF = QLatin1String("self");
  33. NewFlightDialog::NewFlightDialog(ACompletionData &completion_data,
  34. QWidget *parent)
  35. : QDialog(parent),
  36. ui(new Ui::NewFlight),
  37. completionData(completion_data)
  38. {
  39. ui->setupUi(this);
  40. flightEntry = AFlightEntry();
  41. setup();
  42. if (ASettings::read(ASettings::FlightLogging::FunctionComboBox).toString() == QLatin1String("SIC")) {
  43. ui->picNameLineEdit->setText(QString());
  44. ui->secondPilotNameLineEdit->setText(SELF);
  45. }
  46. if(ASettings::read(ASettings::FlightLogging::FunctionComboBox).toString() == QLatin1String("PIC")){
  47. ui->picNameLineEdit->setText(SELF);
  48. emit ui->picNameLineEdit->editingFinished();
  49. }
  50. CRIT("Time empty bug - check verification logic");
  51. }
  52. NewFlightDialog::NewFlightDialog(ACompletionData &completion_data,
  53. int row_id,
  54. QWidget *parent)
  55. : QDialog(parent),
  56. ui(new Ui::NewFlight),
  57. completionData(completion_data)
  58. {
  59. ui->setupUi(this);
  60. flightEntry = aDB->getFlightEntry(row_id);
  61. setup();
  62. formFiller();
  63. }
  64. NewFlightDialog::~NewFlightDialog()
  65. {
  66. LOG << "Closing NF Dialog";
  67. delete ui;
  68. }
  69. ///////////////////////////////////////////////////////////////////////////////////////////////////
  70. /// setup and maintenance of dialog ///
  71. ///////////////////////////////////////////////////////////////////////////////////////////////////
  72. void NewFlightDialog::setup()
  73. {
  74. for (const auto & approach : Opl::ApproachTypes){
  75. ui->ApproachComboBox->addItem(approach);
  76. }
  77. updateEnabled = true;
  78. setupButtonGroups();
  79. setupRawInputValidation();
  80. setupSignalsAndSlots();
  81. readSettings();
  82. setupUi();
  83. ui->deptLocLineEdit->setFocus();
  84. }
  85. void NewFlightDialog::setupButtonGroups()
  86. {
  87. QButtonGroup *FlightRulesGroup = new QButtonGroup(this);
  88. FlightRulesGroup->addButton(ui->IfrCheckBox);
  89. FlightRulesGroup->addButton(ui->VfrCheckBox);
  90. QButtonGroup *PilotTaskGroup = new QButtonGroup(this);
  91. PilotTaskGroup->addButton(ui->PilotFlyingCheckBox);
  92. PilotTaskGroup->addButton(ui->PilotMonitoringCheckBox);
  93. }
  94. void NewFlightDialog::setupRawInputValidation()
  95. {
  96. // Prepare Regular Expressions for QValidators
  97. const auto name_rx = QLatin1String("((\\p{L}+)?('|\\-|,)?(\\p{L}+)?)");
  98. const auto add_name_rx = QLatin1String("(\\s?(\\p{L}+('|\\-|,)?\\p{L}+?))?");
  99. const auto time_valid_rx = QRegularExpression("([01]?[0-9]|2[0-3]):?[0-5][0-9]?");
  100. const auto loc_valid_rx = QRegularExpression("[a-zA-Z0-9]{1,4}");
  101. const auto acft_valid_rx = QRegularExpression("\\w+\\-?(\\w+)?");
  102. const auto name_valid_rx = QRegularExpression(
  103. name_rx + add_name_rx + add_name_rx + add_name_rx + ",?\\s?" // up to 4 first names
  104. + name_rx + add_name_rx + add_name_rx + add_name_rx ); // up to 4 last names
  105. // create the structs holding the inititalisation data (completion lists and RXs) and store them in a map
  106. const auto location_data = ValidationSetupData(&completionData.airportList, &loc_valid_rx);
  107. const auto pilot_data = ValidationSetupData(&completionData.pilotList, &name_valid_rx);
  108. const auto aircraft_data = ValidationSetupData(&completionData.tailsList, &acft_valid_rx);
  109. const auto time_data = ValidationSetupData(&time_valid_rx);
  110. const QHash<const QLatin1String*, const ValidationSetupData*> init_data_map = {
  111. {&LOC_LINE_EDIT_NAME, &location_data},
  112. {&PILOT_LINE_EDIT_NAME, &pilot_data},
  113. {&ACFT_LINE_EDIT_NAME, &aircraft_data},
  114. {&TIME_LINE_EDIT_NAME, &time_data}
  115. };
  116. //get and set up line edits
  117. const QList<QLineEdit*> line_edits = ui->flightDataTab->findChildren<QLineEdit*>();
  118. const QList<const QLatin1String*> keys_list = init_data_map.keys();
  119. for (const auto &line_edit : line_edits) {
  120. for (const auto &key : keys_list ) {
  121. if (line_edit->objectName().contains(*key)) {
  122. // Fetch Data from the map and set up the line edit
  123. const ValidationSetupData* init_data = init_data_map.value(key);
  124. DEB << "Setting up: " << line_edit->objectName();
  125. // Set Validator
  126. auto validator = new QRegularExpressionValidator(*init_data->validationRegEx, line_edit);
  127. line_edit->setValidator(validator);
  128. // Set Completer
  129. if (init_data->completionData) {
  130. auto completer = new QCompleter(*init_data->completionData, line_edit);
  131. completer->setCaseSensitivity(Qt::CaseInsensitive);
  132. completer->setCompletionMode(QCompleter::PopupCompletion);
  133. completer->setFilterMode(Qt::MatchContains);
  134. line_edit->setCompleter(completer);
  135. }
  136. }
  137. }
  138. }
  139. // populate Mandatory Line Edits list and prepare QBitArray
  140. mandatoryLineEditsValid.resize(7);
  141. mandatoryLineEdits = {
  142. ui->doftLineEdit,
  143. ui->deptLocLineEdit,
  144. ui->destLocLineEdit,
  145. ui->tofbTimeLineEdit,
  146. ui->tonbTimeLineEdit,
  147. ui->picNameLineEdit,
  148. ui->acftLineEdit,
  149. };
  150. primaryTimeLineEdits = {
  151. ui->tofbTimeLineEdit,
  152. ui->tonbTimeLineEdit
  153. };
  154. pilotsLineEdits = {
  155. ui->picNameLineEdit,
  156. ui->secondPilotNameLineEdit,
  157. ui->thirdPilotNameLineEdit
  158. };
  159. }
  160. void NewFlightDialog::setupSignalsAndSlots()
  161. {
  162. const auto line_edits = this->findChildren<QLineEdit*>();
  163. for (const auto &line_edit : line_edits){
  164. line_edit->installEventFilter(this);
  165. if(line_edit->objectName().contains(LOC_LINE_EDIT_NAME)){
  166. QObject::connect(line_edit, &QLineEdit::textChanged,
  167. this, &NewFlightDialog::onToUpperTriggered_textChanged);
  168. }
  169. if(line_edit->objectName().contains(ACFT_LINE_EDIT_NAME)){
  170. QObject::connect(line_edit, &QLineEdit::textChanged,
  171. this, &NewFlightDialog::onToUpperTriggered_textChanged);
  172. }
  173. if(line_edit->objectName().contains(PILOT_LINE_EDIT_NAME)){
  174. QObject::connect(line_edit, &QLineEdit::editingFinished,
  175. this, &NewFlightDialog::onPilotNameLineEdit_editingFinished);
  176. }
  177. if(line_edit->objectName().contains(TIME_LINE_EDIT_NAME)){
  178. QObject::connect(line_edit, &QLineEdit::editingFinished,
  179. this, &NewFlightDialog::onTimeLineEdit_editingFinished);
  180. }
  181. }
  182. #if QT_VERSION >= QT_VERSION_CHECK(5, 9, 0)
  183. for (const auto &line_edit : qAsConst(mandatoryLineEdits)) {
  184. if(line_edit->objectName().contains(QStringLiteral("doft")))
  185. break;
  186. QObject::connect(line_edit->completer(), QOverload<const QString &>::of(&QCompleter::highlighted),
  187. this, &NewFlightDialog::onCompleter_highlighted);
  188. QObject::connect(line_edit->completer(), QOverload<const QString &>::of(&QCompleter::activated),
  189. this, &NewFlightDialog::onCompleter_activated);
  190. }
  191. #endif
  192. QObject::connect(ui->calendarWidget, &QCalendarWidget::clicked,
  193. this, &NewFlightDialog::onCalendarWidget_clicked);
  194. }
  195. void NewFlightDialog::setupUi()
  196. {
  197. ui->flightDataTabWidget->setCurrentIndex(0);
  198. ui->flightDataTabWidget->removeTab(2); // hide calendar widget
  199. ui->doftLineEdit->setText(QDate::currentDate().toString(Qt::ISODate));
  200. emit ui->doftLineEdit->editingFinished();
  201. ui->doftLineEdit->setToolTip(tr("Date Format: %1").arg(ADate::getFormatString(Opl::Date::ADateFormat::ISODate)));
  202. }
  203. void NewFlightDialog::readSettings()
  204. {
  205. DEB << "Reading Settings...";
  206. QSettings settings;
  207. ui->FunctionComboBox->setCurrentText(ASettings::read(ASettings::FlightLogging::Function).toString());
  208. ui->ApproachComboBox->setCurrentIndex(ASettings::read(ASettings::FlightLogging::Approach).toInt());
  209. ASettings::read(ASettings::FlightLogging::PilotFlying).toBool() ? ui->PilotFlyingCheckBox->setChecked(true)
  210. : ui->PilotMonitoringCheckBox->setChecked(true);
  211. ui->TakeoffSpinBox->setValue(ASettings::read(ASettings::FlightLogging::NumberTakeoffs).toInt());
  212. ui->TakeoffSpinBox->value() > 0 ? ui->TakeoffCheckBox->setChecked(true)
  213. : ui->TakeoffCheckBox->setChecked(false);
  214. ui->LandingSpinBox->setValue(ASettings::read(ASettings::FlightLogging::NumberLandings).toInt());
  215. ui->LandingSpinBox->value() > 0 ? ui->LandingCheckBox->setChecked(true)
  216. : ui->LandingCheckBox->setChecked(false);
  217. if (ASettings::read(ASettings::FlightLogging::LogIFR).toBool()) {
  218. ui->IfrCheckBox->setChecked(true);
  219. } else {
  220. ui->VfrCheckBox->setChecked(true);
  221. }
  222. ui->FlightNumberLineEdit->setText(ASettings::read(ASettings::FlightLogging::FlightNumberPrefix).toString());
  223. // Debug
  224. ASettings::write(ASettings::FlightLogging::FlightTimeFormat, Opl::Time::Default);
  225. TODO << "Support for Decimal Logging is not implemented yet.";
  226. flightTimeFormat = static_cast<Opl::Time::FlightTimeFormat>(
  227. ASettings::read(ASettings::FlightLogging::FlightTimeFormat).toInt());
  228. }
  229. void NewFlightDialog::writeSettings()
  230. {
  231. DEB << "Writing Settings...";
  232. ASettings::write(ASettings::FlightLogging::Function, ui->FunctionComboBox->currentText());
  233. ASettings::write(ASettings::FlightLogging::Approach, ui->ApproachComboBox->currentIndex());
  234. ASettings::write(ASettings::FlightLogging::PilotFlying, ui->PilotFlyingCheckBox->isChecked());
  235. ASettings::write(ASettings::FlightLogging::NumberTakeoffs, ui->TakeoffSpinBox->value());
  236. ASettings::write(ASettings::FlightLogging::NumberLandings, ui->LandingSpinBox->value());
  237. ASettings::write(ASettings::FlightLogging::LogIFR, ui->IfrCheckBox->isChecked());
  238. }
  239. bool NewFlightDialog::eventFilter(QObject* object, QEvent* event)
  240. {
  241. auto line_edit = qobject_cast<QLineEdit*>(object);
  242. if (line_edit != nullptr) {
  243. if (mandatoryLineEdits.contains(line_edit) && event->type() == QEvent::FocusIn) {
  244. //invalidateMandatoryLineEdit(mandatoryLineEdit(mandatoryLineEdits.indexOf(line_edit)));
  245. //DEB << "Editing " << line_edit->objectName();
  246. // set verification bit to false when entering a mandatory line edit
  247. return false;
  248. }
  249. if (mandatoryLineEdits.contains(line_edit) && event->type() == QEvent::KeyPress) {
  250. // show completion menu when pressing down arrow
  251. QKeyEvent* keyEvent = static_cast<QKeyEvent*>(event);
  252. if (keyEvent->key() == Qt::Key_Down) {
  253. //DEB << "Key down event.";
  254. line_edit->completer()->complete();
  255. }
  256. return false;
  257. }
  258. if (line_edit->objectName().contains(QStringLiteral("Name")) && event->type() == QEvent::KeyPress) {
  259. // show completion menu when pressing down arrow
  260. QKeyEvent* keyEvent = static_cast<QKeyEvent*>(event);
  261. if (keyEvent->key() == Qt::Key_Down) {
  262. //DEB << "Key down event.";
  263. line_edit->completer()->complete();
  264. }
  265. return false;
  266. }
  267. }
  268. return false;
  269. }
  270. ///////////////////////////////////////////////////////////////////////////////////////////////////
  271. /// Methods - Input Processing ///
  272. ///////////////////////////////////////////////////////////////////////////////////////////////////
  273. /*!
  274. * \brief Fills the deductable items in the Dialog based on mandatory data ui selections.
  275. */
  276. void NewFlightDialog::fillDeductibleData()
  277. {
  278. //zero out labels and line edits to delete previous calculations
  279. QList<QLineEdit*> LE = {ui->tSPSETimeLineEdit, ui->tSPMETimeLineEdit, ui->tMPTimeLineEdit, ui->tIFRTimeLineEdit,
  280. ui->tNIGHTTimeLineEdit,ui->tPICTimeLineEdit, ui->tPICUSTimeLineEdit, ui->tSICTimeLineEdit,
  281. ui->tDUALTimeLineEdit, ui->tFITimeLineEdit,};
  282. QList<QLabel*> LB = {ui->tSPSELabel, ui->tSPMELabel, ui->tMPLabel, ui->tIFRLabel, ui->tNIGHTLabel,
  283. ui->tPICLabel, ui->tPICUSLabel, ui->tSICLabel, ui->tDUALLabel, ui->tFILabel};
  284. for(const auto& widget : LE) {widget->setText(QString());}
  285. for(const auto& widget : LB) {widget->setText(Opl::Db::NULL_TIME_hhmm);}
  286. //Calculate block time
  287. const auto tofb = ATime::fromString(ui->tofbTimeLineEdit->text());
  288. const auto tonb = ATime::fromString(ui->tonbTimeLineEdit->text());
  289. const auto tblk = ATime::blocktime(tofb, tonb);
  290. const auto block_time_string = ATime::toString(tblk, flightTimeFormat);
  291. ui->tblkTimeLineEdit->setText(block_time_string);
  292. // get acft data and fill deductible entries
  293. auto acft = aDB->getTailEntry(completionData.tailsIdMap.key(ui->acftLineEdit->text()));
  294. // TOTAL
  295. ui->tblkLabel->setText(QLatin1String("<b>") + block_time_string + QLatin1String("</b>"));
  296. // IFR
  297. if(ui->IfrCheckBox->isChecked()){
  298. ui->tIFRTimeLineEdit->setText(block_time_string);
  299. ui->tIFRLabel->setText(block_time_string);
  300. }
  301. // Function times
  302. switch (ui->FunctionComboBox->currentIndex()) {
  303. case 0://PIC
  304. ui->tPICTimeLineEdit->setText(block_time_string);
  305. ui->tPICLabel->setText(block_time_string);
  306. break;
  307. case 1://PICus
  308. ui->tPICUSTimeLineEdit->setText(block_time_string);
  309. ui->tPICUSLabel->setText(block_time_string);
  310. break;
  311. case 2://Co-Pilot
  312. ui->tSICTimeLineEdit->setText(block_time_string);
  313. ui->tSICLabel->setText(block_time_string);
  314. break;
  315. case 3://Dual
  316. ui->tDUALTimeLineEdit->setText(block_time_string);
  317. ui->tDUALLabel->setText(block_time_string);
  318. break;
  319. case 4://Instructor
  320. ui->tFITimeLineEdit->setText(block_time_string);
  321. ui->tFILabel->setText(block_time_string);
  322. ui->tPICTimeLineEdit->setText(block_time_string);
  323. ui->tPICLabel->setText(block_time_string);
  324. }
  325. // if location data is available, fill night time
  326. if (locLineEditsValid()) {
  327. const auto block_minutes = ATime::toMinutes(tblk);
  328. QString dept_date = ui->doftLineEdit->text() + 'T' + ATime::toString(tofb);
  329. auto dept_date_time = QDateTime::fromString(dept_date, QStringLiteral("yyyy-MM-ddThh:mm"));
  330. const int night_angle = ASettings::read(ASettings::FlightLogging::NightAngle).toInt();
  331. auto night_time = ATime::qTimefromMinutes(ACalc::calculateNightTime(
  332. ui->deptLocLineEdit->text(),
  333. ui->destLocLineEdit->text(),
  334. dept_date_time,
  335. block_minutes,
  336. night_angle));
  337. ui->tNIGHTTimeLineEdit->setText(ATime::toString(night_time, flightTimeFormat));
  338. ui->tNIGHTLabel->setText(ATime::toString(night_time, flightTimeFormat));
  339. }
  340. // if acft data is available, fill additional times
  341. if (acftLineEditValid()) {
  342. if (acft.getData().isEmpty())
  343. WARN("Error: No valid aircraft object available, unable to deterime auto times.");
  344. // SP SE
  345. if(acft.getData().value(Opl::Db::TAILS_MULTIPILOT).toInt() == 0
  346. && acft.getData().value(Opl::Db::TAILS_MULTIENGINE).toInt() == 0){
  347. ui->tSPSETimeLineEdit->setText(block_time_string);
  348. ui->tSPSELabel->setText(block_time_string);
  349. }
  350. // SP ME
  351. if(acft.getData().value(Opl::Db::TAILS_MULTIPILOT).toInt() == 0
  352. && acft.getData().value(Opl::Db::TAILS_MULTIENGINE).toInt() == 1){
  353. ui->tSPMETimeLineEdit->setText(block_time_string);
  354. ui->tSPMELabel->setText(block_time_string);
  355. }
  356. // MP
  357. if(acft.getData().value(Opl::Db::TAILS_MULTIPILOT).toInt() == 1){
  358. ui->tMPTimeLineEdit->setText(block_time_string);
  359. ui->tMPLabel->setText(block_time_string);
  360. }
  361. }
  362. }
  363. /*!
  364. * \brief Collect input and create a Data map for the entry object.
  365. *
  366. * This function should only be called if input validation has been passed, since
  367. * no input validation is done in this step and input data is assumed to be valid.
  368. * \return
  369. */
  370. RowData_T NewFlightDialog::collectInput()
  371. {
  372. RowData_T newData;
  373. //DEB << "Collecting Input...";
  374. //Block Time
  375. const auto tofb = ATime::fromString(ui->tofbTimeLineEdit->text());
  376. const auto tonb = ATime::fromString(ui->tonbTimeLineEdit->text());
  377. const auto tblk = ATime::blocktime(tofb, tonb);
  378. const auto block_minutes = ATime::toMinutes(tblk);
  379. // Mandatory data
  380. newData.insert(Opl::Db::FLIGHTS_DOFT, ui->doftLineEdit->text());
  381. newData.insert(Opl::Db::FLIGHTS_DEPT, ui->deptLocLineEdit->text());
  382. newData.insert(Opl::Db::FLIGHTS_TOFB, ATime::toMinutes(tofb));
  383. newData.insert(Opl::Db::FLIGHTS_DEST, ui->destLocLineEdit->text());
  384. newData.insert(Opl::Db::FLIGHTS_TONB, ATime::toMinutes(tonb));
  385. newData.insert(Opl::Db::FLIGHTS_TBLK, block_minutes);
  386. // Aircraft
  387. newData.insert(Opl::Db::FLIGHTS_ACFT, completionData.tailsIdMap.key(ui->acftLineEdit->text()));
  388. // Pilots
  389. newData.insert(Opl::Db::FLIGHTS_PIC, completionData.pilotsIdMap.key(ui->picNameLineEdit->text()));
  390. newData.insert(Opl::Db::FLIGHTS_SECONDPILOT, completionData.pilotsIdMap.key(ui->secondPilotNameLineEdit->text()));
  391. newData.insert(Opl::Db::FLIGHTS_THIRDPILOT, completionData.pilotsIdMap.key(ui->thirdPilotNameLineEdit->text()));
  392. // Extra Times
  393. ui->tSPSETimeLineEdit->text().isEmpty() ?
  394. newData.insert(Opl::Db::FLIGHTS_TSPSE, QString())
  395. : newData.insert(Opl::Db::FLIGHTS_TSPSE, stringToMinutes(
  396. ui->tSPSETimeLineEdit->text(), flightTimeFormat));
  397. ui->tSPMETimeLineEdit->text().isEmpty() ?
  398. newData.insert(Opl::Db::FLIGHTS_TSPME, QString())
  399. : newData.insert(Opl::Db::FLIGHTS_TSPME, stringToMinutes(
  400. ui->tSPMETimeLineEdit->text(), flightTimeFormat));
  401. ui->tMPTimeLineEdit->text().isEmpty() ?
  402. newData.insert(Opl::Db::FLIGHTS_TMP, QString())
  403. : newData.insert(Opl::Db::FLIGHTS_TMP, stringToMinutes(
  404. ui->tMPTimeLineEdit->text(), flightTimeFormat));
  405. if (ui->IfrCheckBox->isChecked()) {
  406. newData.insert(Opl::Db::FLIGHTS_TIFR, block_minutes);
  407. } else {
  408. newData.insert(Opl::Db::FLIGHTS_TIFR, QString());
  409. }
  410. // Night
  411. const QString dept_date = ui->doftLineEdit->text() + 'T'
  412. + ATime::toString(tofb);
  413. const auto dept_date_time = QDateTime::fromString(dept_date, QStringLiteral("yyyy-MM-ddThh:mm"));
  414. const int night_angle = ASettings::read(ASettings::FlightLogging::NightAngle).toInt();
  415. const auto night_time = ATime::qTimefromMinutes(ACalc::calculateNightTime(
  416. ui->deptLocLineEdit->text(),
  417. ui->destLocLineEdit->text(),
  418. dept_date_time,
  419. block_minutes,
  420. night_angle));
  421. const auto night_minutes = ATime::toMinutes(night_time);
  422. newData.insert(Opl::Db::FLIGHTS_TNIGHT, night_minutes);
  423. // Function times - This is a little explicit but these are mutually exclusive so its better to be safe than sorry here.
  424. switch (ui->FunctionComboBox->currentIndex()) {
  425. case 0://PIC
  426. newData.insert(Opl::Db::FLIGHTS_TPIC, block_minutes);
  427. newData.insert(Opl::Db::FLIGHTS_TPICUS, QString());
  428. newData.insert(Opl::Db::FLIGHTS_TSIC, QString());
  429. newData.insert(Opl::Db::FLIGHTS_TDUAL, QString());
  430. newData.insert(Opl::Db::FLIGHTS_TFI, QString());
  431. break;
  432. case 1://PICUS
  433. newData.insert(Opl::Db::FLIGHTS_TPIC, QString());
  434. newData.insert(Opl::Db::FLIGHTS_TPICUS, block_minutes);
  435. newData.insert(Opl::Db::FLIGHTS_TSIC, QString());
  436. newData.insert(Opl::Db::FLIGHTS_TDUAL, QString());
  437. newData.insert(Opl::Db::FLIGHTS_TFI, QString());
  438. break;
  439. case 2://Co-Pilot
  440. newData.insert(Opl::Db::FLIGHTS_TPIC, QString());
  441. newData.insert(Opl::Db::FLIGHTS_TPICUS, QString());
  442. newData.insert(Opl::Db::FLIGHTS_TSIC, block_minutes);
  443. newData.insert(Opl::Db::FLIGHTS_TDUAL, QString());
  444. newData.insert(Opl::Db::FLIGHTS_TFI, QString());
  445. break;
  446. case 3://Dual
  447. newData.insert(Opl::Db::FLIGHTS_TPIC, QString());
  448. newData.insert(Opl::Db::FLIGHTS_TPICUS, QString());
  449. newData.insert(Opl::Db::FLIGHTS_TSIC, QString());
  450. newData.insert(Opl::Db::FLIGHTS_TDUAL, block_minutes);
  451. newData.insert(Opl::Db::FLIGHTS_TFI, QString());
  452. break;
  453. case 4://Instructor
  454. newData.insert(Opl::Db::FLIGHTS_TPIC, block_minutes);
  455. newData.insert(Opl::Db::FLIGHTS_TPICUS, QString());
  456. newData.insert(Opl::Db::FLIGHTS_TSIC, QString());
  457. newData.insert(Opl::Db::FLIGHTS_TDUAL, QString());
  458. newData.insert(Opl::Db::FLIGHTS_TFI, block_minutes);
  459. }
  460. // Pilot Flying
  461. newData.insert(Opl::Db::FLIGHTS_PILOTFLYING, ui->PilotFlyingCheckBox->isChecked());
  462. // TO and LDG - again a bit explicit, but we need to check for both night to day as well as day to night transitions.
  463. if (ui->TakeoffCheckBox->isChecked()) {
  464. if (night_minutes == 0) { // all day
  465. newData.insert(Opl::Db::FLIGHTS_TODAY, ui->TakeoffSpinBox->value());
  466. newData.insert(Opl::Db::FLIGHTS_TONIGHT, 0);
  467. } else if (night_minutes == block_minutes) { // all night
  468. newData.insert(Opl::Db::FLIGHTS_TODAY, 0);
  469. newData.insert(Opl::Db::FLIGHTS_TONIGHT, ui->TakeoffSpinBox->value());
  470. } else {
  471. if(ACalc::isNight(ui->deptLocLineEdit->text(), dept_date_time, night_angle)) {
  472. newData.insert(Opl::Db::FLIGHTS_TODAY, 0);
  473. newData.insert(Opl::Db::FLIGHTS_TONIGHT, ui->TakeoffSpinBox->value());
  474. } else {
  475. newData.insert(Opl::Db::FLIGHTS_TODAY, ui->TakeoffSpinBox->value());
  476. newData.insert(Opl::Db::FLIGHTS_TONIGHT, 0);
  477. }
  478. }
  479. } else {
  480. newData.insert(Opl::Db::FLIGHTS_TODAY, 0);
  481. newData.insert(Opl::Db::FLIGHTS_TONIGHT, 0);
  482. }
  483. if (ui->LandingCheckBox->isChecked()) {
  484. if (night_minutes == 0) { // all day
  485. newData.insert(Opl::Db::FLIGHTS_LDGDAY, ui->LandingSpinBox->value());
  486. newData.insert(Opl::Db::FLIGHTS_LDGNIGHT, 0);
  487. } else if (night_minutes == block_minutes) { // all night
  488. newData.insert(Opl::Db::FLIGHTS_LDGDAY, 0);
  489. newData.insert(Opl::Db::FLIGHTS_LDGNIGHT, ui->LandingSpinBox->value());
  490. } else { //check
  491. const QString dest_date = ui->doftLineEdit->text() + 'T'
  492. + ATime::toString(tonb);
  493. const auto dest_date_time = QDateTime::fromString(dest_date, QStringLiteral("yyyy-MM-ddThh:mm"));
  494. if (ACalc::isNight(ui->destLocLineEdit->text(), dest_date_time, night_angle)) {
  495. newData.insert(Opl::Db::FLIGHTS_LDGDAY, 0);
  496. newData.insert(Opl::Db::FLIGHTS_LDGNIGHT, ui->LandingSpinBox->value());
  497. } else {
  498. newData.insert(Opl::Db::FLIGHTS_LDGDAY, ui->LandingSpinBox->value());
  499. newData.insert(Opl::Db::FLIGHTS_LDGNIGHT, 0);
  500. }
  501. }
  502. } else {
  503. newData.insert(Opl::Db::FLIGHTS_LDGDAY, 0);
  504. newData.insert(Opl::Db::FLIGHTS_LDGNIGHT, 0);
  505. }
  506. newData.insert(Opl::Db::FLIGHTS_AUTOLAND, ui->AutolandSpinBox->value());
  507. newData.insert(Opl::Db::FLIGHTS_APPROACHTYPE, ui->ApproachComboBox->currentText());
  508. newData.insert(Opl::Db::FLIGHTS_FLIGHTNUMBER, ui->FlightNumberLineEdit->text());
  509. newData.insert(Opl::Db::FLIGHTS_REMARKS, ui->RemarksLineEdit->text());
  510. DEB << "New Flight Data: " << newData;
  511. return newData;
  512. }
  513. void NewFlightDialog::formFiller()
  514. {
  515. DEB << "Filling Line Edits...";
  516. // get Line Edits
  517. const auto line_edits = this->findChildren<QLineEdit *>();
  518. QStringList line_edits_names;
  519. for (const auto& le : line_edits) {
  520. line_edits_names << le->objectName();
  521. }
  522. ui->acftLineEdit->setText(flightEntry.getRegistration());
  523. line_edits_names.removeOne(QStringLiteral("acftLineEdit"));
  524. const auto data_keys = flightEntry.getData().keys();
  525. for (const auto& data_key : data_keys) {
  526. auto rx = QRegularExpression(data_key + QStringLiteral("LineEdit"));//acftLineEdit
  527. for(const auto& leName : qAsConst(line_edits_names)){
  528. if(rx.match(leName).hasMatch()) {
  529. //DEB << "Loc Match found: " << key << " - " << leName);
  530. auto line_edit = this->findChild<QLineEdit *>(leName);
  531. if(line_edit != nullptr){
  532. line_edit->setText(flightEntry.getData().value(data_key).toString());
  533. line_edits_names.removeOne(leName);
  534. }
  535. break;
  536. }
  537. }
  538. rx = QRegularExpression(data_key + QStringLiteral("Loc\\w+?"));
  539. for(const auto& leName : qAsConst(line_edits_names)){
  540. if(rx.match(leName).hasMatch()) {
  541. //DEB << "Loc Match found: " << key << " - " << leName);
  542. auto line_edit = this->findChild<QLineEdit *>(leName);
  543. if(line_edit != nullptr){
  544. line_edit->setText(flightEntry.getData().value(data_key).toString());
  545. line_edits_names.removeOne(leName);
  546. }
  547. break;
  548. }
  549. }
  550. rx = QRegularExpression(data_key + QStringLiteral("Time\\w+?"));
  551. for(const auto& leName : qAsConst(line_edits_names)){
  552. if(rx.match(leName).hasMatch()) {
  553. //DEB << "Time Match found: " << key << " - " << leName);
  554. auto line_edits = this->findChild<QLineEdit *>(leName);
  555. if(line_edits != nullptr){
  556. DEB << "Setting " << line_edits->objectName() << " to " << ATime::toString(flightEntry.getData().value(data_key).toInt(), flightTimeFormat);
  557. line_edits->setText(ATime::toString(flightEntry.getData().value(data_key).toInt(),
  558. flightTimeFormat));
  559. line_edits_names.removeOne(leName);
  560. }
  561. break;
  562. }
  563. }
  564. rx = QRegularExpression(data_key + QStringLiteral("Name\\w+?"));
  565. for(const auto& leName : qAsConst(line_edits_names)){
  566. if(rx.match(leName).hasMatch()) {
  567. auto line_edits = this->findChild<QLineEdit *>(leName);
  568. if(line_edits != nullptr){
  569. DEB << completionData.pilotsIdMap.value(1);
  570. line_edits->setText(completionData.pilotsIdMap.value(flightEntry.getData().value(data_key).toInt()));
  571. line_edits_names.removeOne(leName);
  572. }
  573. break;
  574. }
  575. }
  576. }
  577. //FunctionComboBox
  578. QList<QLineEdit*> function_combo_boxes = {ui->tPICTimeLineEdit, ui->tPICUSTimeLineEdit,
  579. ui->tSICTimeLineEdit, ui->tDUALTimeLineEdit,
  580. ui->tFITimeLineEdit};
  581. for(const auto& line_edit : function_combo_boxes){
  582. if(line_edit->text() != QStringLiteral("00:00")){
  583. QString name = line_edit->objectName();
  584. name.chop(12);
  585. name.remove(0,1);
  586. ui->FunctionComboBox->setCurrentText(name);
  587. }
  588. }
  589. // Approach Combo Box
  590. const QString& app = flightEntry.getData().value(Opl::Db::FLIGHTS_APPROACHTYPE).toString();
  591. if(app != QString()){
  592. ui->ApproachComboBox->setCurrentText(app);
  593. }
  594. // Task and Rules
  595. qint8 PF = flightEntry.getData().value(Opl::Db::FLIGHTS_PILOTFLYING).toInt();
  596. if (PF > 0) {
  597. ui->PilotFlyingCheckBox->setChecked(true);
  598. } else {
  599. ui->PilotMonitoringCheckBox->setChecked(true);
  600. }
  601. qint8 FR = flightEntry.getData().value(Opl::Db::FLIGHTS_TIFR).toInt();
  602. if (FR > 0) {
  603. ui->IfrCheckBox->setChecked(true);
  604. } else {
  605. ui->tIFRTimeLineEdit->setText(QString());
  606. ui->VfrCheckBox->setChecked(true);
  607. }
  608. // Take Off and Landing
  609. qint8 TO = flightEntry.getData().value(Opl::Db::FLIGHTS_TODAY).toInt()
  610. + flightEntry.getData().value(Opl::Db::FLIGHTS_TONIGHT).toInt();
  611. qint8 LDG = flightEntry.getData().value(Opl::Db::FLIGHTS_LDGDAY).toInt()
  612. + flightEntry.getData().value(Opl::Db::FLIGHTS_LDGNIGHT).toInt();
  613. if(TO > 0) {
  614. ui->TakeoffCheckBox->setChecked(true);
  615. ui->TakeoffSpinBox->setValue(TO);
  616. } else {
  617. ui->TakeoffCheckBox->setChecked(false);
  618. ui->TakeoffSpinBox->setValue(0);
  619. }
  620. if(LDG > 0) {
  621. ui->LandingCheckBox->setChecked(true);
  622. ui->LandingSpinBox->setValue(LDG);
  623. } else {
  624. ui->LandingCheckBox->setChecked(false);
  625. ui->LandingSpinBox->setValue(0);
  626. }
  627. qint8 AL = flightEntry.getData().value(Opl::Db::FLIGHTS_AUTOLAND).toInt();
  628. if(AL > 0) {
  629. ui->AutolandCheckBox->setChecked(true);
  630. ui->AutolandSpinBox->setValue(AL);
  631. }
  632. for(const auto& le : qAsConst(mandatoryLineEdits)){
  633. emit le->editingFinished();
  634. }
  635. }
  636. bool NewFlightDialog::isLessOrEqualThanBlockTime(const QString time_string)
  637. {
  638. if (!allMandatoryLineEditsValid()){
  639. INFO(tr("Unable to determine total block time.<br>"
  640. "Please fill out all Mandatory Fields<br>"
  641. "before manually editing these times."));
  642. return false;
  643. }
  644. auto extra_time = ATime::fromString(time_string, flightTimeFormat);
  645. auto block_time = ATime::blocktime(ATime::fromString(
  646. ui->tofbTimeLineEdit->text(), flightTimeFormat),
  647. ATime::fromString(
  648. ui->tonbTimeLineEdit->text(), flightTimeFormat));
  649. if (extra_time <= block_time) {
  650. return true;
  651. } else {
  652. WARN(tr("The flight time you have entered is longer than the total blocktime:"
  653. "<br><center><b>%1</b></center>"
  654. ).arg(ATime::toString(block_time, flightTimeFormat)));
  655. return false;
  656. }
  657. }
  658. /*!
  659. * \brief NewFlight::addNewTailMessageBox If the user input is not in the aircraftList, the user
  660. * is prompted if he wants to add a new entry to the database
  661. */
  662. void NewFlightDialog::addNewTail(QLineEdit *parent_line_edit)
  663. {
  664. QMessageBox::StandardButton reply;
  665. reply = QMessageBox::question(this, tr("No Aircraft found"),
  666. tr("No aircraft with this registration found.<br>"
  667. "If this is the first time you log a flight with this aircraft, "
  668. "you have to add the registration to the database first."
  669. "<br><br>Would you like to add a new aircraft to the database?"),
  670. QMessageBox::Yes|QMessageBox::No);
  671. if (reply == QMessageBox::Yes) {
  672. // create and open new aircraft dialog
  673. NewTailDialog na(ui->acftLineEdit->text(), this);
  674. int ret = na.exec();
  675. // update map and list, set line edit
  676. if (ret == QDialog::Accepted) {
  677. DEB << "New Tail Entry added. Id:" << aDB->getLastEntry(ADatabaseTable::tails);
  678. // update completion Data and Completer
  679. completionData.updateTails();
  680. auto new_model = new QStringListModel(completionData.tailsList, parent_line_edit->completer());
  681. parent_line_edit->completer()->setModel(new_model); //setModel deletes old model if it has the completer as parent
  682. // update Line Edit
  683. parent_line_edit->setText(completionData.tailsIdMap.value(aDB->getLastEntry(ADatabaseTable::tails)));
  684. emit parent_line_edit->editingFinished();
  685. } else {
  686. parent_line_edit->setText(QString());
  687. parent_line_edit->setFocus();
  688. }
  689. } else {
  690. parent_line_edit->setText(QString());
  691. parent_line_edit->setFocus();
  692. }
  693. }
  694. /*!
  695. * \brief NewFlight::addNewPilot If the user input is not in the pilotNameList, the user
  696. * is prompted if he wants to add a new entry to the database
  697. */
  698. void NewFlightDialog::addNewPilot(QLineEdit *parent_line_edit)
  699. {
  700. QMessageBox::StandardButton reply;
  701. reply = QMessageBox::question(this, tr("No Pilot found"),
  702. tr("No pilot found.<br>Please enter the Name as"
  703. "<br><br><center><b>Lastname, Firstname</b></center><br><br>"
  704. "If this is the first time you log a flight with this pilot, "
  705. "you have to add the pilot to the database first."
  706. "<br><br>Would you like to add a new pilot to the database?"),
  707. QMessageBox::Yes|QMessageBox::No);
  708. if (reply == QMessageBox::Yes) {
  709. // create and open new pilot dialog
  710. NewPilotDialog np(this);
  711. int ret = np.exec();
  712. // update map and list, set line edit
  713. if (ret == QDialog::Accepted) {
  714. DEB << "New Pilot Entry added. Id:" << aDB->getLastEntry(ADatabaseTable::pilots);
  715. // update completion Data and Completer
  716. completionData.updatePilots();
  717. auto new_model = new QStringListModel(completionData.pilotList, parent_line_edit->completer());
  718. parent_line_edit->completer()->setModel(new_model); //setModel deletes old model if it has the completer as parent
  719. // update Line Edit
  720. parent_line_edit->setText(completionData.pilotsIdMap.value(aDB->getLastEntry(ADatabaseTable::pilots)));
  721. emit parent_line_edit->editingFinished();
  722. } else {
  723. parent_line_edit->setText(QString());
  724. parent_line_edit->setFocus();
  725. }
  726. } else {
  727. parent_line_edit->setText(QString());
  728. }
  729. }
  730. ///////////////////////////////////////////////////////////////////////////////////////////////////
  731. /// Flight Data Tab Slots ///
  732. ///////////////////////////////////////////////////////////////////////////////////////////////////
  733. void NewFlightDialog::on_cancelButton_clicked()
  734. {
  735. DEB << "Cancel Button clicked.";
  736. reject();
  737. }
  738. void NewFlightDialog::on_submitButton_clicked()
  739. {
  740. // emit editing finished for all mandatory line edits to trigger input verification
  741. for (const auto &line_edit : qAsConst(mandatoryLineEdits)) {
  742. emit line_edit->editingFinished();
  743. }
  744. // If input verification is passed, continue, otherwise prompt user to correct
  745. if (!allMandatoryLineEditsValid()) {
  746. const auto display_names = QMap<int, QString> {
  747. {0, QObject::tr("Date of Flight")}, {1, QObject::tr("Departure Airport")},
  748. {2, QObject::tr("Destination Airport")}, {3, QObject::tr("Time Off Blocks")},
  749. {4, QObject::tr("Time on Blocks")}, {5, QObject::tr("PIC Name")},
  750. {6, QObject::tr("Aircraft Registration")}
  751. };
  752. QString missing_items;
  753. for (int i=0; i < mandatoryLineEdits.size(); i++) {
  754. if (!mandatoryLineEditsValid[i]){
  755. missing_items.append(display_names.value(i) + "<br>");
  756. mandatoryLineEdits[i]->setStyleSheet(QStringLiteral("border: 1px solid red"));
  757. }
  758. }
  759. INFO(tr("Not all mandatory entries are valid.<br>"
  760. "The following item(s) are empty or missing:"
  761. "<br><br><center><b>%1</b></center><br>"
  762. "Please go back and fill in the required data."
  763. ).arg(missing_items));
  764. return;
  765. }
  766. // If input verification passed, collect input and submit to database
  767. auto newData = collectInput();
  768. DEB << "Setting Data for flightEntry...";
  769. flightEntry.setData(newData);
  770. DEB << "Committing...";
  771. if (!aDB->commit(flightEntry)) {
  772. WARN(tr("The following error has ocurred:"
  773. "<br><br>%1<br><br>"
  774. "The entry has not been saved."
  775. ).arg(aDB->lastError.text()));
  776. return;
  777. } else {
  778. QDialog::accept();
  779. }
  780. }
  781. /*
  782. * Shared Slots
  783. */
  784. void NewFlightDialog::onGoodInputReceived(QLineEdit *line_edit)
  785. {
  786. DEB << line_edit->objectName() << " - Good input received - " << line_edit->text();
  787. line_edit->setStyleSheet(QString());
  788. if (mandatoryLineEdits.contains(line_edit))
  789. validateMandatoryLineEdit(mandatoryLineEdit(mandatoryLineEdits.indexOf(line_edit)));
  790. validationStatus();
  791. if (timeLineEditsValid())
  792. onMandatoryLineEditsFilled();
  793. }
  794. void NewFlightDialog::onBadInputReceived(QLineEdit *line_edit)
  795. {
  796. DEB << line_edit->objectName() << " - Bad input received - " << line_edit->text();
  797. line_edit->setStyleSheet(QStringLiteral("border: 1px solid red"));
  798. if (mandatoryLineEdits.contains(line_edit))
  799. invalidateMandatoryLineEdit(mandatoryLineEdit(mandatoryLineEdits.indexOf(line_edit)));
  800. validationStatus();
  801. }
  802. // capitalize input for dept, dest and registration input
  803. void NewFlightDialog::onToUpperTriggered_textChanged(const QString &text)
  804. {
  805. auto sender_object = sender();
  806. auto line_edit = this->findChild<QLineEdit*>(sender_object->objectName());
  807. //DEB << "Text changed - " << line_edit->objectName() << line_edit->text();
  808. {
  809. const QSignalBlocker blocker(line_edit);
  810. line_edit->setText(text.toUpper());
  811. }
  812. }
  813. // update is disabled if the user chose to manually edit extra times
  814. void NewFlightDialog::onMandatoryLineEditsFilled()
  815. {
  816. if (updateEnabled)
  817. fillDeductibleData();
  818. }
  819. // make sure that when using keyboard to scroll through completer sugggestions, line edit is up to date
  820. void NewFlightDialog::onCompleter_highlighted(const QString &completion)
  821. {
  822. auto sender_object = sender();
  823. auto line_edit = this->findChild<QLineEdit*>(sender_object->objectName());
  824. DEB << "Completer highlighted - " << line_edit->objectName() << completion;
  825. line_edit->setText(completion);
  826. }
  827. void NewFlightDialog::onCompleter_activated(const QString &)
  828. {
  829. auto sender_object = sender();
  830. auto line_edit = this->findChild<QLineEdit*>(sender_object->objectName());
  831. DEB << "Line edit " << line_edit->objectName() << "completer activated.";
  832. emit line_edit->editingFinished();
  833. }
  834. /*
  835. * Date of Flight
  836. */
  837. void NewFlightDialog::on_doftLineEdit_editingFinished()
  838. {
  839. auto line_edit = ui->doftLineEdit;
  840. auto text = ui->doftLineEdit->text();
  841. auto label = ui->doftDisplayLabel;
  842. //DEB << line_edit->objectName() << "Editing finished - " << text;
  843. TODO << "Non-default Date formats not implemented yet.";
  844. Opl::Date::ADateFormat date_format = Opl::Date::ADateFormat::ISODate;
  845. auto date = ADate::parseInput(text, date_format);
  846. if (date.isValid()) {
  847. label->setText(date.toString(Qt::TextDate));
  848. line_edit->setText(ADate::toString(date, date_format));
  849. onGoodInputReceived(line_edit);
  850. return;
  851. }
  852. label->setText(tr("Invalid Date."));
  853. onBadInputReceived(line_edit);
  854. }
  855. void NewFlightDialog::onCalendarWidget_clicked(const QDate &date)
  856. {
  857. const auto& le = ui->doftLineEdit;
  858. le->blockSignals(false);
  859. ui->calendarWidget->hide();
  860. ui->placeLabel1->resize(ui->placeLabel2->size());
  861. le->setText(date.toString(Qt::ISODate));
  862. emit le->editingFinished();
  863. }
  864. void NewFlightDialog::on_calendarPushButton_clicked()
  865. {
  866. const auto& cw = ui->calendarWidget;
  867. const auto& le = ui->doftLineEdit;
  868. const auto& anchor = ui->placeLabel1;
  869. if(cw->isVisible()){
  870. le->blockSignals(false);
  871. cw->hide();
  872. anchor->resize(ui->placeLabel2->size());
  873. le->setFocus();
  874. } else {
  875. le->blockSignals(true);
  876. // Determine size based on layout coordinates
  877. int c1 = anchor->pos().rx();
  878. int c2 = le->pos().rx();
  879. int z = le->size().rwidth();
  880. int x = (c2 - c1)+ z;
  881. c1 = anchor->pos().ry();
  882. c2 = ui->acftLineEdit->pos().ry();
  883. z = ui->acftLineEdit->size().height();
  884. int y = (c2 - c1) + z;
  885. //Re-size calendar and parent label accordingly
  886. anchor->resize(x, y);
  887. cw->setParent(ui->placeLabel1);
  888. cw->setGeometry(QRect(0, 0, x, y));
  889. cw->show();
  890. cw->setFocus();
  891. }
  892. }
  893. void NewFlightDialog::validationStatus()
  894. {
  895. QString deb_string("Validation State:\tdoft\tdept\tdest\ttofb\ttonb\tpic\tacft\n");
  896. deb_string += "\t\t\t\t\t" + QString::number(mandatoryLineEditsValid[0]);
  897. deb_string += "\t" + QString::number(mandatoryLineEditsValid[1]);
  898. deb_string += "\t" + QString::number(mandatoryLineEditsValid[2]);
  899. deb_string += "\t" + QString::number(mandatoryLineEditsValid[3]);
  900. deb_string += "\t" + QString::number(mandatoryLineEditsValid[4]);
  901. deb_string += "\t" + QString::number(mandatoryLineEditsValid[5]);
  902. deb_string += "\t" + QString::number(mandatoryLineEditsValid[6]);
  903. qDebug().noquote() << deb_string;
  904. }
  905. /*
  906. * Location Line Edits
  907. */
  908. void NewFlightDialog::on_deptLocLineEdit_editingFinished()
  909. {
  910. onLocationEditingFinished(ui->deptLocLineEdit, ui->deptNameLabel);
  911. }
  912. void NewFlightDialog::on_destLocLineEdit_editingFinished()
  913. {
  914. onLocationEditingFinished(ui->destLocLineEdit, ui->destNameLabel);
  915. }
  916. void NewFlightDialog::onLocationEditingFinished(QLineEdit *line_edit, QLabel *name_label)
  917. {
  918. const auto &text = line_edit->text();
  919. //DEB << line_edit->objectName() << " Editing finished. " << text);
  920. int airport_id = 0;
  921. // try to map iata or icao code to airport id;
  922. if (text.length() == 3) {
  923. airport_id = completionData.airportIataIdMap.key(text);
  924. } else {
  925. airport_id = completionData.airportIcaoIdMap.key(text);
  926. }
  927. // check result
  928. if (airport_id == 0) {
  929. // to do: prompt user how to handle unknown airport
  930. name_label->setText(tr("Unknown airport identifier"));
  931. onBadInputReceived(line_edit);
  932. return;
  933. }
  934. line_edit->setText(completionData.airportIcaoIdMap.value(airport_id));
  935. name_label->setText(completionData.airportNameIdMap.value(airport_id));
  936. onGoodInputReceived(line_edit);
  937. }
  938. /*
  939. * Time Line Edits
  940. */
  941. void NewFlightDialog::onTimeLineEdit_editingFinished()
  942. {
  943. auto sender_object = sender();
  944. auto line_edit = this->findChild<QLineEdit*>(sender_object->objectName());
  945. DEB << line_edit->objectName() << "Editing Finished -" << line_edit->text();
  946. line_edit->setText(ATime::formatTimeInput(line_edit->text()));
  947. const auto time = ATime::fromString(line_edit->text());
  948. if(time.isValid()){
  949. if(primaryTimeLineEdits.contains(line_edit)) {
  950. onGoodInputReceived(line_edit);
  951. } else { // is extra time line edit
  952. if (!isLessOrEqualThanBlockTime(line_edit->text())) {
  953. line_edit->setText(QString());
  954. line_edit->setFocus();
  955. return;
  956. }
  957. }
  958. } else {
  959. if (!line_edit->text().isEmpty())
  960. onBadInputReceived(line_edit);
  961. }
  962. }
  963. /*
  964. * Aircraft Line Edit
  965. */
  966. void NewFlightDialog::on_acftLineEdit_editingFinished()
  967. {
  968. TODO << "Looking up and matching tails is currently broken...";
  969. auto line_edit = ui->acftLineEdit;
  970. int acft_id = completionData.tailsIdMap.key(line_edit->text());
  971. DEB << "acft_id: " << acft_id;
  972. if (acft_id != 0) { // Success
  973. //DEB << "Mapped: " << line_edit->text() << completionData.tailsIdMap.value(line_edit->text());
  974. auto acft = aDB->getTailEntry(acft_id);
  975. ui->acftTypeLabel->setText(acft.type());
  976. ui->picCompanyLabel->setText(acft.getData().value(Opl::Db::TAILS_COMPANY).toString());
  977. onGoodInputReceived(line_edit);
  978. return;
  979. }
  980. if (!line_edit->completer()->currentCompletion().isEmpty()) {
  981. line_edit->setText(line_edit->completer()->currentCompletion().split(QLatin1Char(' ')).first());
  982. emit line_edit->editingFinished();
  983. return;
  984. }
  985. // If no success mark as bad input
  986. onBadInputReceived(line_edit);
  987. ui->acftTypeLabel->setText(tr("Unknown Registration."));
  988. if (line_edit->text() != QString())
  989. addNewTail(line_edit);
  990. }
  991. /*
  992. * Pilot Line Edits
  993. */
  994. void NewFlightDialog::onPilotNameLineEdit_editingFinished()
  995. {
  996. auto sender_object = sender();
  997. auto line_edit = this->findChild<QLineEdit*>(sender_object->objectName());
  998. //DEB << line_edit->objectName() << "Editing Finished -" << line_edit->text());
  999. if(line_edit->text().contains(SELF, Qt::CaseInsensitive)) {
  1000. DEB << "self recognized.";
  1001. line_edit->setText(completionData.pilotsIdMap.value(1));
  1002. onGoodInputReceived(line_edit);
  1003. return;
  1004. }
  1005. if(completionData.pilotsIdMap.key(line_edit->text()) != 0) {
  1006. DEB << "Mapped: " << line_edit->text() << completionData.pilotsIdMap.key(line_edit->text());
  1007. onGoodInputReceived(line_edit);
  1008. return;
  1009. }
  1010. if (line_edit->text().isEmpty()) {
  1011. return;
  1012. }
  1013. if (!line_edit->completer()->currentCompletion().isEmpty()) {
  1014. DEB << "Trying to fix input...";
  1015. line_edit->setText(line_edit->completer()->currentCompletion());
  1016. emit line_edit->editingFinished();
  1017. return;
  1018. }
  1019. onBadInputReceived(line_edit);
  1020. addNewPilot(line_edit);
  1021. }
  1022. ///////////////////////////////////////////////////////////////////////////////////////////////////
  1023. /// Auto Logging Tab Slots ///
  1024. ///////////////////////////////////////////////////////////////////////////////////////////////////
  1025. void NewFlightDialog::on_setAsDefaultButton_clicked()
  1026. {
  1027. writeSettings();
  1028. }
  1029. void NewFlightDialog::on_restoreDefaultButton_clicked()
  1030. {
  1031. readSettings();
  1032. }
  1033. void NewFlightDialog::on_PilotFlyingCheckBox_stateChanged(int)
  1034. {
  1035. DEB << "PF checkbox state changed.";
  1036. if(ui->PilotFlyingCheckBox->isChecked()){
  1037. ui->TakeoffSpinBox->setValue(1);
  1038. ui->TakeoffCheckBox->setCheckState(Qt::Checked);
  1039. ui->LandingSpinBox->setValue(1);
  1040. ui->LandingCheckBox->setCheckState(Qt::Checked);
  1041. }else if(!ui->PilotFlyingCheckBox->isChecked()){
  1042. ui->TakeoffSpinBox->setValue(0);
  1043. ui->TakeoffCheckBox->setCheckState(Qt::Unchecked);
  1044. ui->LandingSpinBox->setValue(0);
  1045. ui->LandingCheckBox->setCheckState(Qt::Unchecked);
  1046. }
  1047. }
  1048. void NewFlightDialog::on_IfrCheckBox_stateChanged(int)
  1049. {
  1050. if (timeLineEditsValid() && updateEnabled)
  1051. onMandatoryLineEditsFilled();
  1052. }
  1053. void NewFlightDialog::on_manualEditingCheckBox_stateChanged(int arg1)
  1054. {
  1055. if (!(timeLineEditsValid()) && ui->manualEditingCheckBox->isChecked()) {
  1056. INFO(tr("Before editing times manually, please fill out the required fields "
  1057. "in the flight data tab, so that total time can be calculated."));
  1058. ui->manualEditingCheckBox->setChecked(false);
  1059. return;
  1060. }
  1061. QList<QLineEdit*> LE = {ui->tSPSETimeLineEdit, ui->tSPMETimeLineEdit, ui->tMPTimeLineEdit, ui->tIFRTimeLineEdit,
  1062. ui->tNIGHTTimeLineEdit,ui->tPICTimeLineEdit, ui->tPICUSTimeLineEdit, ui->tSICTimeLineEdit,
  1063. ui->tDUALTimeLineEdit, ui->tFITimeLineEdit};
  1064. switch (arg1) {
  1065. case 0:
  1066. for(const auto& le : LE){
  1067. le->setFocusPolicy(Qt::NoFocus);
  1068. le->setStyleSheet(QString());
  1069. }
  1070. updateEnabled = true;
  1071. if (allMandatoryLineEditsValid() && updateEnabled)
  1072. onMandatoryLineEditsFilled();
  1073. break;
  1074. case 2:
  1075. for(const auto& le : LE){
  1076. le->setFocusPolicy(Qt::StrongFocus);
  1077. }
  1078. updateEnabled = false;
  1079. break;
  1080. default:
  1081. break;
  1082. }
  1083. }
  1084. void NewFlightDialog::on_ApproachComboBox_currentTextChanged(const QString &arg1)
  1085. {
  1086. if(arg1 == QLatin1String("ILS CAT III")){ //for a CAT III approach an Autoland is mandatory, so we can preselect it.
  1087. ui->AutolandCheckBox->setCheckState(Qt::Checked);
  1088. ui->AutolandSpinBox->setValue(1);
  1089. }else{
  1090. ui->AutolandCheckBox->setCheckState(Qt::Unchecked);
  1091. ui->AutolandSpinBox->setValue(0);
  1092. }
  1093. if (arg1 != QLatin1String("VISUAL"))
  1094. ui->IfrCheckBox->setChecked(true);
  1095. }
  1096. void NewFlightDialog::on_FunctionComboBox_currentIndexChanged(int)
  1097. {
  1098. if (updateEnabled)
  1099. fillDeductibleData();
  1100. }
  1101. // [F]: Not a priority right now.
  1102. void NewFlightDialog::on_deptTZComboBox_currentIndexChanged(int index)
  1103. {
  1104. if (index > 0) {
  1105. INFO(tr("Currently only logging in UTC time is supported."));
  1106. ui->deptTZComboBox->setCurrentIndex(0);
  1107. }
  1108. }
  1109. void NewFlightDialog::on_destTZComboBox_currentIndexChanged(int index)
  1110. {
  1111. if (index > 0) {
  1112. INFO(tr("Currently only logging in UTC time is supported."));
  1113. ui->destTZComboBox->setCurrentIndex(0);}
  1114. }