newtaildialog.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331
  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 "newtaildialog.h"
  19. #include "ui_newtail.h"
  20. #include "src/testing/adebug.h"
  21. static const auto REG_VALID = QPair<QString, QRegularExpression> {
  22. "registrationLineEdit", QRegularExpression("\\w+-\\w+")};
  23. static const auto MAKE_VALID = QPair<QString, QRegularExpression> {
  24. "makeLineEdit", QRegularExpression("[-a-zA-Z\\s]+")};
  25. static const auto MODEL_VALID = QPair<QString, QRegularExpression> {
  26. "modelLineEdit", QRegularExpression("[\\s\\w-]+")};
  27. static const auto VARIANT_VALID = QPair<QString, QRegularExpression> {
  28. "variantLineEdit", QRegularExpression("[\\s\\w-]+")};
  29. static const auto LINE_EDIT_VALIDATORS = QVector({REG_VALID, MAKE_VALID, MODEL_VALID, VARIANT_VALID});
  30. NewTailDialog::NewTailDialog(QString new_registration, QWidget *parent) :
  31. QDialog(parent),
  32. ui(new Ui::NewTail)
  33. {
  34. DEB("new NewTailDialog (experimental)");
  35. ui->setupUi(this);
  36. setupCompleter();
  37. setupValidators();
  38. ui->registrationLineEdit->setText(new_registration);
  39. ui->searchLineEdit->setStyleSheet("border: 1px solid blue");
  40. ui->searchLineEdit->setFocus();
  41. entry = experimental::ATailEntry();
  42. }
  43. NewTailDialog::NewTailDialog(int row_id, QWidget *parent) :
  44. QDialog(parent),
  45. ui(new Ui::NewTail)
  46. {
  47. using namespace experimental;
  48. DEB("New experimental New Pilot Dialog (edit existing)");
  49. ui->setupUi(this);
  50. ui->searchLabel->hide();
  51. ui->searchLineEdit->hide();
  52. ui->line->hide();
  53. setupValidators();
  54. entry = aDB()->getTailEntry(row_id);
  55. fillForm(entry, false);
  56. }
  57. NewTailDialog::~NewTailDialog()
  58. {
  59. DEB("Deleting NewTailDialog\n");
  60. delete ui;
  61. }
  62. /// Functions
  63. /*!
  64. * \brief NewTail::setupCompleter obtains a QMap<QString searchstring, int aircaft_id> for auto completion
  65. * and obtains a QStringList for QCompleter. This function then sets up the search line edit where
  66. * the user can select a template from the aircraft database to pre-fill the form with the details
  67. * for the selected type.
  68. */
  69. void NewTailDialog::setupCompleter()
  70. {
  71. using namespace experimental;
  72. idMap = aDB()->getIdMap(ADataBase::aircraft);
  73. aircraftList = aDB()->getCompletionList(experimental::ADataBase::aircraft);
  74. QCompleter *completer = new QCompleter(aircraftList, ui->searchLineEdit);
  75. completer->setCaseSensitivity(Qt::CaseInsensitive);
  76. completer->setCompletionMode(QCompleter::PopupCompletion);
  77. completer->setFilterMode(Qt::MatchContains);
  78. ui->searchLineEdit->setCompleter(completer);
  79. QObject::connect(completer, static_cast<void(QCompleter::*)(const QString &)>(&QCompleter::activated),
  80. this, &NewTailDialog::onSearchCompleterActivated);
  81. QObject::connect(completer, static_cast<void(QCompleter::*)(const QString &)>(&QCompleter::highlighted),
  82. this, &NewTailDialog::onSearchCompleterActivated);
  83. }
  84. void NewTailDialog::setupValidators()
  85. {
  86. for(const auto& pair : LINE_EDIT_VALIDATORS){
  87. auto line_edit = this->findChild<QLineEdit*>(pair.first);
  88. auto validator = new QRegularExpressionValidator(pair.second, line_edit);
  89. line_edit->setValidator(validator);
  90. }
  91. }
  92. /*!
  93. * \brief NewTailDialog::fillForm populates the Dialog with the
  94. * information contained in an entry object. This can be either
  95. * a template (AAircraft, used when creating a new entry) or
  96. * a tail (ATail, used when editing an existing entry)
  97. * \param entry
  98. */
  99. void NewTailDialog::fillForm(experimental::AEntry entry, bool is_template)
  100. {
  101. DEB("Filling Form for (experimental) a/c" << entry.getPosition());
  102. //fill Line Edits
  103. auto line_edits = this->findChildren<QLineEdit *>();
  104. if (is_template)
  105. line_edits.removeOne(ui->registrationLineEdit);
  106. for (const auto &le : line_edits) {
  107. QString name = le->objectName().remove("LineEdit");
  108. QString value = entry.getData().value(name);
  109. le->setText(value);
  110. }
  111. //select comboboxes
  112. QVector<QString> operation = {entry.getData().value("singleengine"), entry.getData().value("multiengine")};
  113. QVector<QString> ppNumber = {entry.getData().value("singlepilot"), entry.getData().value("multipilot")};
  114. QVector<QString> ppType = {entry.getData().value("unpowered"), entry.getData().value("piston"),
  115. entry.getData().value("turboprop"), entry.getData().value("jet")
  116. };
  117. QVector<QString> weight = {entry.getData().value("light"), entry.getData().value("medium"),
  118. entry.getData().value("heavy"), entry.getData().value("super")
  119. };
  120. ui->operationComboBox->setCurrentIndex(operation.indexOf("1") + 1);
  121. ui->ppNumberComboBox->setCurrentIndex(ppNumber.indexOf("1") + 1);
  122. ui->ppTypeComboBox->setCurrentIndex(ppType.indexOf("1") + 1);
  123. ui->weightComboBox->setCurrentIndex(weight.indexOf("1") + 1);
  124. }
  125. /*!
  126. * \brief NewTail::verify A simple check for empty recommended fields in the form
  127. * \return true if all reconmmended fields are populated
  128. */
  129. bool NewTailDialog::verify()
  130. {
  131. auto recommended_line_edits = this->findChildren<QLineEdit *>("registrationLineEdit");
  132. recommended_line_edits.append(this->findChild<QLineEdit *>("makeLineEdit"));
  133. recommended_line_edits.append(this->findChild<QLineEdit *>("modelLineEdit"));
  134. auto recommended_combo_boxes = this->findChildren<QComboBox *>("operationComboBox");
  135. recommended_combo_boxes.append(this->findChild<QComboBox *>("ppNumberComboBox"));
  136. recommended_combo_boxes.append(this->findChild<QComboBox *>("ppTypeComboBox"));
  137. for (const auto &le : recommended_line_edits) {
  138. if (le->text() != "") {
  139. DEB("Good: " << le);
  140. recommended_line_edits.removeOne(le);
  141. le->setStyleSheet("");
  142. } else {
  143. le->setStyleSheet("border: 1px solid red");
  144. DEB("Not Good: " << le);
  145. }
  146. }
  147. for (const auto &cb : recommended_combo_boxes) {
  148. if (cb->currentIndex() != 0) {
  149. recommended_combo_boxes.removeOne(cb);
  150. cb->setStyleSheet("");
  151. } else {
  152. cb->setStyleSheet("background: orange");
  153. DEB("Not Good: " << cb);
  154. }
  155. }
  156. if (recommended_line_edits.isEmpty() && recommended_combo_boxes.isEmpty()) {
  157. return true;
  158. } else {
  159. return false;
  160. }
  161. }
  162. /*!
  163. * \brief NewTail::submitForm collects input from Line Edits and creates
  164. * or updates a database entry and commits or updates the database
  165. * \param edRole editExisting or createNew
  166. */
  167. void NewTailDialog::submitForm()
  168. {
  169. DEB("Creating Database Object...");
  170. using namespace experimental;
  171. TableData new_data;
  172. //retreive Line Edits
  173. auto line_edits = this->findChildren<QLineEdit *>();
  174. line_edits.removeOne(this->findChild<QLineEdit *>("searchLineEdit"));
  175. for (const auto &le : line_edits) {
  176. QString name = le->objectName().remove("LineEdit");
  177. new_data.insert(name, le->text());
  178. }
  179. //prepare comboboxes
  180. QVector<QString> operation = {"singlepilot", "multipilot"};
  181. QVector<QString> ppNumber = {"singleengine", "multiengine"};
  182. QVector<QString> ppType = {"unpowered", "piston",
  183. "turboprop", "jet"
  184. };
  185. QVector<QString> weight = {"light", "medium",
  186. "heavy", "super"
  187. };
  188. if (ui->operationComboBox->currentIndex() != 0) {
  189. new_data.insert(operation[ui->operationComboBox->currentIndex() - 1], QLatin1String("1"));
  190. }
  191. if (ui->ppNumberComboBox->currentIndex() != 0) {
  192. new_data.insert(ppNumber[ui->ppNumberComboBox->currentIndex() - 1], QLatin1String("1"));
  193. }
  194. if (ui->ppTypeComboBox->currentIndex() != 0) {
  195. new_data.insert(ppType[ui->ppTypeComboBox->currentIndex() - 1], QLatin1String("1"));
  196. }
  197. if (ui->weightComboBox->currentIndex() != 0) {
  198. new_data.insert(weight[ui->weightComboBox->currentIndex() - 1], QLatin1String("1"));
  199. }
  200. //create db object
  201. entry.setData(new_data);
  202. if (!aDB()->commit(entry)) {
  203. auto message_box = QMessageBox(this);
  204. message_box.setText("The following error has ocurred:\n\n"
  205. + aDB()->lastError
  206. + "\n\nThe entry has not been saved.");
  207. message_box.exec();
  208. return;
  209. } else {
  210. QDialog::accept();
  211. }
  212. }
  213. /// Slots
  214. void NewTailDialog::on_operationComboBox_currentIndexChanged(int index)
  215. {
  216. if (index != 0) {
  217. ui->operationComboBox->setStyleSheet("");
  218. }
  219. }
  220. void NewTailDialog::on_ppTypeComboBox_currentIndexChanged(int index)
  221. {
  222. if (index != 0) {
  223. ui->ppTypeComboBox->setStyleSheet("");
  224. }
  225. }
  226. void NewTailDialog::on_ppNumberComboBox_currentIndexChanged(int index)
  227. {
  228. if (index != 0) {
  229. ui->ppNumberComboBox->setStyleSheet("");
  230. }
  231. }
  232. void NewTailDialog::on_weightComboBox_currentIndexChanged(int index)
  233. {
  234. if (index != 0) {
  235. ui->weightComboBox->setStyleSheet("");
  236. }
  237. }
  238. void NewTailDialog::on_buttonBox_accepted()
  239. {
  240. DEB("Button Box Accepted.");
  241. if (ui->registrationLineEdit->text().isEmpty()) {
  242. auto nope = QMessageBox(this);
  243. nope.setText("Registration cannot be empty.");
  244. nope.exec();
  245. return;
  246. }
  247. if (!verify()) {
  248. if (!ASettings::read("userdata/acAllowIncomplete").toInt()) {
  249. auto nope = QMessageBox(this);
  250. nope.setIcon(QMessageBox::Warning);
  251. nope.setText("Some or all recommended fields are empty.\nPlease go back and "
  252. "complete the form.\n\nYou can allow logging incomplete tail entries on the settings page.");
  253. nope.exec();
  254. return;
  255. } else {
  256. QMessageBox::StandardButton reply;
  257. reply = QMessageBox::question(this, "Warning",
  258. "Some recommended fields are empty.\n\n"
  259. "If you do not fill out the aircraft details, "
  260. "it will be impossible to automatically determine Single/Multi Pilot Times or Single/Multi Engine Time."
  261. "This will also impact statistics and auto-logging capabilites.\n\n"
  262. "It is highly recommended to fill in all the details.\n\n"
  263. "Are you sure you want to proceed?",
  264. QMessageBox::Yes | QMessageBox::No);
  265. if (reply == QMessageBox::Yes) {
  266. submitForm();
  267. }
  268. }
  269. }
  270. DEB("Form verified");
  271. submitForm();
  272. }
  273. void NewTailDialog::onSearchCompleterActivated()
  274. {
  275. DEB("Search completer activated!");
  276. const auto &text = ui->searchLineEdit->text();
  277. if (aircraftList.contains(text)) {
  278. DEB("Template Selected. aircraft_id is: " << idMap.value(text));
  279. //call autofiller for dialog
  280. using namespace experimental;
  281. fillForm(aDB()->getAircraftEntry(idMap.value(text)), true);
  282. ui->searchLineEdit->setStyleSheet("border: 1px solid green");
  283. ui->searchLabel->setText(text);
  284. } else {
  285. //for example, editing finished without selecting a result from Qcompleter
  286. ui->searchLineEdit->setStyleSheet("border: 1px solid orange");
  287. }
  288. }
  289. void NewTailDialog::on_registrationLineEdit_textChanged(const QString &arg1)
  290. {
  291. ui->registrationLineEdit->setText(arg1.toUpper());
  292. }