aircraftwidget.cpp 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259
  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 "aircraftwidget.h"
  19. #include "ui_aircraftwidget.h"
  20. #include "src/opl.h"
  21. #include "src/gui/dialogues/newtaildialog.h"
  22. #include "src/classes/asettings.h"
  23. #include "src/database/adatabase.h"
  24. #include "src/classes/atailentry.h"
  25. #include "src/classes/aflightentry.h"
  26. #include "src/functions/alog.h"
  27. AircraftWidget::AircraftWidget(QWidget *parent) :
  28. QWidget(parent),
  29. ui(new Ui::AircraftWidget)
  30. {
  31. ui->setupUi(this);
  32. ui->tableView->setMinimumWidth(this->width()/2);
  33. ui->stackedWidget->setMinimumWidth(this->width()/2);
  34. setupModelAndView();
  35. }
  36. AircraftWidget::~AircraftWidget()
  37. {
  38. delete ui;
  39. }
  40. void AircraftWidget::setupModelAndView()
  41. {
  42. model = new QSqlTableModel(this);
  43. model->setTable(QStringLiteral("viewTails"));
  44. model->select();
  45. view = ui->tableView;
  46. view->setModel(model);
  47. view->setSelectionBehavior(QAbstractItemView::SelectRows);
  48. view->setSelectionMode(QAbstractItemView::SingleSelection); // For now, editing multiple entries is not supported.
  49. view->setEditTriggers(QAbstractItemView::NoEditTriggers);
  50. view->horizontalHeader()->setStretchLastSection(QHeaderView::Stretch);
  51. view->hideColumn(0);
  52. view->resizeColumnsToContents();
  53. view->verticalHeader()->hide();
  54. view->setAlternatingRowColors(true);
  55. sortColumn = ASettings::read(ASettings::UserData::TailSortColumn).toInt();
  56. view->setSortingEnabled(true);
  57. view->sortByColumn(sortColumn, Qt::DescendingOrder);
  58. view->show();
  59. selection = view->selectionModel();
  60. connectSignalsAndSlots();
  61. }
  62. void AircraftWidget::connectSignalsAndSlots()
  63. {
  64. QObject::connect(ui->tableView->selectionModel(), &QItemSelectionModel::selectionChanged,
  65. this, &AircraftWidget::tableView_selectionChanged);
  66. QObject::connect(ui->tableView->horizontalHeader(), &QHeaderView::sectionClicked,
  67. this, &AircraftWidget::tableView_headerClicked);
  68. }
  69. /*
  70. * Slots
  71. */
  72. void AircraftWidget::onAircraftWidget_settingChanged(SettingsWidget::SettingSignal signal)
  73. {
  74. if (signal != SettingsWidget::AircraftWidget)
  75. return;
  76. setupModelAndView();
  77. }
  78. void AircraftWidget::onAircraftWidget_dataBaseUpdated()
  79. {
  80. refreshView();
  81. }
  82. void AircraftWidget::changeEvent(QEvent *event)
  83. {
  84. if (event != nullptr)
  85. if(event->type() == QEvent::LanguageChange)
  86. ui->retranslateUi(this);
  87. }
  88. void AircraftWidget::onNewTailDialog_editingFinished()
  89. {
  90. refreshView();
  91. }
  92. void AircraftWidget::on_newAircraftButton_clicked()
  93. {
  94. NewTailDialog nt(QString(), this);
  95. QObject::connect(&nt, &QDialog::accepted,
  96. this, &AircraftWidget::onNewTailDialog_editingFinished);
  97. QObject::connect(&nt, &QDialog::rejected,
  98. this, &AircraftWidget::onNewTailDialog_editingFinished);
  99. nt.exec();
  100. }
  101. /*!
  102. * \brief Displays a dialog in which the Tail can be edited
  103. */
  104. void AircraftWidget::tableView_selectionChanged()
  105. {
  106. if (this->findChild<NewTailDialog*>() != nullptr)
  107. delete this->findChild<NewTailDialog*>();
  108. selectedTails.clear();
  109. const auto selected_rows = selection->selectedRows();
  110. for (const auto& row : selected_rows) {
  111. selectedTails << row.data().toInt();
  112. DEB << "Selected Tails(s) with ID: " << selectedTails;
  113. }
  114. if(selectedTails.length() == 1) {
  115. auto* nt = new NewTailDialog(selectedTails.first(), this);
  116. QObject::connect(nt, &QDialog::accepted,
  117. this, &AircraftWidget::onNewTailDialog_editingFinished);
  118. QObject::connect(nt, &QDialog::rejected,
  119. this, &AircraftWidget::onNewTailDialog_editingFinished);
  120. ui->stackedWidget->addWidget(nt);
  121. ui->stackedWidget->setCurrentWidget(nt);
  122. nt->setWindowFlag(Qt::Widget);
  123. nt->setAttribute(Qt::WA_DeleteOnClose);
  124. nt->exec();
  125. }
  126. }
  127. /*!
  128. * \brief Acts as a filter on the display model
  129. */
  130. void AircraftWidget::on_aircraftSearchLineEdit_textChanged(const QString &arg1)
  131. {
  132. if(ui->aircraftSearchComboBox->currentIndex() == 0){
  133. ui->aircraftSearchLineEdit->setText(arg1.toUpper());
  134. }
  135. model->setFilter(ui->aircraftSearchComboBox->currentText()
  136. + QLatin1String(" LIKE \"%")
  137. + arg1 + QLatin1String("%\""));
  138. }
  139. void AircraftWidget::tableView_headerClicked(int column)
  140. {
  141. sortColumn = column;
  142. ASettings::write(ASettings::UserData::TailSortColumn, column);
  143. }
  144. void AircraftWidget::on_deleteAircraftButton_clicked()
  145. {
  146. if (selectedTails.length() == 0) {
  147. INFO(tr("No Aircraft selected."));
  148. } else if (selectedTails.length() > 1) {
  149. WARN(tr("Deleting multiple entries is currently not supported"));
  150. /// [F] to do: for (const auto& row_id : selectedPilots) { do batchDelete }
  151. /// I am not sure if enabling this functionality for this widget is a good idea.
  152. /// On the one hand, deleting many entries could be useful in a scenario where
  153. /// for example, the user has changed airlines and does not want to have his 'old'
  154. /// colleagues polluting his logbook anymore.
  155. /// On the other hand we could run into issues with foreign key constraints on the
  156. /// flights table (see on_delete_unsuccessful) below.
  157. /// I think batch-editing should be implemented at some point, but batch-deleting should not.
  158. } else if (selectedTails.length() == 1) {
  159. auto entry = aDB->getTailEntry(selectedTails.first());
  160. QMessageBox message_box(this);
  161. message_box.setStandardButtons(QMessageBox::Yes | QMessageBox::No);
  162. message_box.setDefaultButton(QMessageBox::No);
  163. message_box.setIcon(QMessageBox::Question);
  164. message_box.setWindowTitle(tr("Delete Aircraft"));
  165. message_box.setText(tr("You are deleting the following aircraft:<br><br><b><tt>"
  166. "%1 - (%2)</b></tt><br><br>Are you sure?"
  167. ).arg(entry.registration(),
  168. entry.type()));
  169. if (message_box.exec() == QMessageBox::Yes) {
  170. if(!aDB->remove(entry))
  171. onDeleteUnsuccessful();
  172. }
  173. }
  174. refreshView();
  175. }
  176. /*!
  177. * \brief Informs the user that deleting a database entry has been unsuccessful
  178. *
  179. * \details Normally, when one of these entries can not be deleted, it is because of
  180. * a [foreign key constraint](https://sqlite.org/foreignkeys.html), meaning that a flight
  181. * is associated with the aircraft that was supposed to be deleted.
  182. *
  183. * This function is used to inform the user and give hints on how to solve the problem.
  184. */
  185. void AircraftWidget::onDeleteUnsuccessful()
  186. {
  187. QList<int> foreign_key_constraints = aDB->getForeignKeyConstraints(selectedTails.first(),
  188. ADatabaseTable::tails);
  189. QList<AFlightEntry> constrained_flights;
  190. for (const auto &row_id : qAsConst(foreign_key_constraints)) {
  191. constrained_flights.append(aDB->getFlightEntry(row_id));
  192. }
  193. QMessageBox message_box(this);
  194. if (constrained_flights.isEmpty()) {
  195. message_box.setText(tr("<br>Unable to delete.<br><br>The following error has ocurred: %1"
  196. ).arg(aDB->lastError.text()));
  197. message_box.exec();
  198. return;
  199. } else {
  200. QString constrained_flights_string;
  201. for (int i=0; i<constrained_flights.length(); i++) {
  202. constrained_flights_string.append(constrained_flights[i].summary() + QLatin1String("&nbsp;&nbsp;&nbsp;&nbsp;<br>"));
  203. if (i>10) {
  204. constrained_flights_string.append(QLatin1String("<br>[...]<br>"));
  205. break;
  206. }
  207. }
  208. message_box.setText(tr("Unable to delete.<br><br>"
  209. "This is most likely the case because a flight exists with the aircraft "
  210. "you are trying to delete.<br><br>"
  211. "%1 flight(s) with this aircraft have been found:<br><br><br><b><tt>"
  212. "%2"
  213. "</b></tt><br><br>You have to change or remove the conflicting flight(s) "
  214. "before removing this aircraft from the database.<br><br>"
  215. ).arg(QString::number(constrained_flights.length()), constrained_flights_string));
  216. message_box.setIcon(QMessageBox::Critical);
  217. message_box.exec();
  218. }
  219. }
  220. void AircraftWidget::repopulateModel()
  221. {
  222. // unset the current model and delete it to avoid leak
  223. view->setModel(nullptr);
  224. delete model;
  225. // create a new model and populate it
  226. model = new QSqlTableModel(this);
  227. setupModelAndView();
  228. connectSignalsAndSlots();
  229. }