pilotswidget.cpp 8.8 KB

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