pilotswidget.cpp 8.4 KB

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