2 * Copyright © 2022 Soren Stoutner <soren@stoutner.com>.
4 * This file is part of Privacy Browser PC <https://www.stoutner.com/privacy-browser-pc>.
6 * Privacy Browser PC is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
11 * Privacy Browser PC is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with Privacy Browser PC. If not, see <http://www.gnu.org/licenses/>.
20 // Application headers.
21 #include "DomainSettingsDialog.h"
22 #include "ui_DomainSettingsDialog.h"
23 #include "helpers/DomainsDatabaseHelper.h"
25 // Qt toolkit headers.
26 #include <QInputDialog>
27 #include <QMessageBox>
28 #include <QPushButton>
30 DomainSettingsDialog::DomainSettingsDialog(QWidget *parent) : QDialog(parent)
32 // Instantiate the domain settings view UI.
33 Ui::DomainSettingsDialog domainSettingsDialogUi;
36 domainSettingsDialogUi.setupUi(this);
38 // Get handles for the views.
39 domainsListViewPointer = domainSettingsDialogUi.domainsListView;
40 domainSettingsWidgetPointer = domainSettingsDialogUi.domainSettingsWidget;
41 domainNameLineEditPointer = domainSettingsDialogUi.domainNameLineEdit;
42 javaScriptComboBoxPointer = domainSettingsDialogUi.javaScriptComboBox;
43 QPushButton *addDomainButtonPointer = domainSettingsDialogUi.addDomainButton;
44 deleteDomainButtonPointer = domainSettingsDialogUi.deleteDomainButton;
45 QDialogButtonBox *dialogButtonBoxPointer = domainSettingsDialogUi.dialogButtonBox;
46 applyButtonPointer = dialogButtonBoxPointer->button(QDialogButtonBox::StandardButton::Apply);
47 resetButtonPointer = dialogButtonBoxPointer->button(QDialogButtonBox::StandardButton::Reset);
49 // Create a table model.
50 domainsTableModelPointer = new QSqlTableModel(nullptr, QSqlDatabase::database(DomainsDatabaseHelper::CONNECTION_NAME));
52 // Set the table for the model.
53 domainsTableModelPointer->setTable(DomainsDatabaseHelper::DOMAINS_TABLE);
55 // Set the edit strategy to be manual.
56 domainsTableModelPointer->setEditStrategy(QSqlTableModel::EditStrategy::OnManualSubmit);
58 // Sort the output alphabetically.
59 domainsTableModelPointer->setSort(1, Qt::SortOrder::AscendingOrder);
61 // Set the model for the list view.
62 domainsListViewPointer->setModel(domainsTableModelPointer);
64 // Set the visible column to be the domain name.
65 domainsListViewPointer->setModelColumn(1);
67 // Disable editing of the list view.
68 domainsListViewPointer->setEditTriggers(QAbstractItemView::NoEditTriggers);
70 // Read the data from the database and apply it to the table model.
71 domainsTableModelPointer->select();
73 // Select the first entry in the list view.
74 domainsListViewPointer->setCurrentIndex(domainsTableModelPointer->index(0, domainsTableModelPointer->fieldIndex(DomainsDatabaseHelper::DOMAIN_NAME)));
76 // Populate the domain settings.
77 domainSelected(domainsListViewPointer->selectionModel()->currentIndex());
79 // Handle clicks on the domains.
80 connect(domainsListViewPointer, SIGNAL(activated(QModelIndex)), this, SLOT(domainSelected(QModelIndex)));
82 // Connect the domain settings.
83 connect(domainNameLineEditPointer, SIGNAL(textEdited(QString)), this, SLOT(domainNameChanged(QString)));
84 connect(javaScriptComboBoxPointer, SIGNAL(currentIndexChanged(int)), this, SLOT(javaScriptChanged(int)));
86 // Connect the buttons.
87 connect(addDomainButtonPointer, SIGNAL(released()), this, SLOT(showAddMessageBox()));
88 connect(deleteDomainButtonPointer, SIGNAL(released()), this, SLOT(showDeleteMessageBox()));
89 connect(resetButtonPointer, SIGNAL(released()), this, SLOT(reset()));
90 connect(dialogButtonBoxPointer, SIGNAL(accepted()), this, SLOT(ok()));
91 connect(applyButtonPointer, SIGNAL(released()), this, SLOT(apply()));
92 connect(dialogButtonBoxPointer, SIGNAL(rejected()), this, SLOT(cancel()));
98 void DomainSettingsDialog::apply() const
100 // Get the current index.
101 QModelIndex currentIndex = domainsListViewPointer->currentIndex();
103 // Get the ID of the current index row.
104 QVariant currentId = currentIndex.siblingAtColumn(domainsTableModelPointer->fieldIndex(DomainsDatabaseHelper::_ID)).data();
106 // Submit all pending changes.
107 domainsTableModelPointer->submitAll();
109 // Find the new index for the selected id. The `1` keeps searching after the first match.
110 QModelIndexList newIndexList = domainsTableModelPointer->match(currentIndex.siblingAtColumn(domainsTableModelPointer->fieldIndex(DomainsDatabaseHelper::_ID)), Qt::DisplayRole, currentId,
113 // Select the new index.
114 domainsListViewPointer->setCurrentIndex(newIndexList[0].siblingAtColumn(domainsTableModelPointer->fieldIndex(DomainsDatabaseHelper::DOMAIN_NAME)));
120 void DomainSettingsDialog::cancel()
122 // Revert all pending changes.
123 domainsTableModelPointer->revertAll();
129 void DomainSettingsDialog::domainNameChanged(QString updatedDomainName) const
131 // Update the domains table model.
132 domainsTableModelPointer->setData(domainsListViewPointer->selectionModel()->currentIndex(), updatedDomainName);
139 void DomainSettingsDialog::domainSelected(QModelIndex modelIndex) const
141 // Populate the domain name line edit pointer.
142 domainNameLineEditPointer->setText(modelIndex.data().toString());
144 // Populate the JavaScript combo box.
145 javaScriptComboBoxPointer->setCurrentIndex(modelIndex.siblingAtColumn(domainsTableModelPointer->fieldIndex(DomainsDatabaseHelper::JAVASCRIPT)).data().toInt());
151 void DomainSettingsDialog::javaScriptChanged(int newIndex) const
153 // Update the domains table model.
154 domainsTableModelPointer->setData(domainsListViewPointer->selectionModel()->currentIndex().siblingAtColumn(domainsTableModelPointer->fieldIndex(DomainsDatabaseHelper::JAVASCRIPT)),
162 void DomainSettingsDialog::ok()
164 // Submit all pending changes.
165 domainsTableModelPointer->submitAll();
171 void DomainSettingsDialog::reset() const
173 // Cancel all pending changes.
174 domainsTableModelPointer->revertAll();
176 // Repopulate the domain name line edit.
177 domainNameLineEditPointer->setText(domainsListViewPointer->currentIndex().data().toString());
183 void DomainSettingsDialog::showAddMessageBox()
185 // Create an OK flag.
188 // Display a dialog to request the new domain name from the user.
189 QString newDomainName = QInputDialog::getText(this, i18nc("Add domain dialog title", "Add Domain"),
190 i18nc("Add domain message. The \n\n are newline codes that should be retained",
191 "Add a new domain. Doing so will also save any pending changes that have been made to other domains.\n\n"
192 "*. may be prepended to a domain to include all subdomains (eg. *.stoutner.com)."),
193 QLineEdit::Normal, QString(), &okClicked);
195 // Add the new domain if the user clicked OK.
198 // Create a new domain record.
199 QSqlRecord newDomainRecord = QSqlDatabase::database(DomainsDatabaseHelper::CONNECTION_NAME).record(DomainsDatabaseHelper::DOMAINS_TABLE);
201 // Add the new domain name.
202 newDomainRecord.setValue(domainsTableModelPointer->fieldIndex(DomainsDatabaseHelper::DOMAIN_NAME), newDomainName);
204 // Set the default value of `0` for the other columns.
205 newDomainRecord.setValue(domainsTableModelPointer->fieldIndex(DomainsDatabaseHelper::JAVASCRIPT), 0);
207 // Insert the new domain. `-1` appends it to the end.
208 domainsTableModelPointer->insertRecord(-1, newDomainRecord);
210 // Submit all pending changes.
211 domainsTableModelPointer->submitAll();
213 // Find the index for the new domain. `-1` allows for multiple entries to be returned.
214 QModelIndexList newDomainIndex = domainsTableModelPointer->match(domainsTableModelPointer->index(0, domainsTableModelPointer->fieldIndex(DomainsDatabaseHelper::DOMAIN_NAME)),
215 Qt::DisplayRole, newDomainName, -1, Qt::MatchWrap);
217 // Move to the new domain. If there are multiple domains with the same name, the new one should be the last in the list.
218 domainsListViewPointer->setCurrentIndex(newDomainIndex[newDomainIndex.size() - 1]);
220 // Populate the domain settings.
221 domainSelected(domainsListViewPointer->selectionModel()->currentIndex());
228 void DomainSettingsDialog::showDeleteMessageBox() const
230 // Instantiate a delete dialog message box.
231 QMessageBox deleteDialogMessageBox;
234 deleteDialogMessageBox.setIcon(QMessageBox::Warning);
236 // Set the window title.
237 deleteDialogMessageBox.setWindowTitle(i18nc("Delete domain dialog title", "Delete Domain"));
240 deleteDialogMessageBox.setText(i18nc("Delete domain main message", "Delete the current domain?"));
242 // Set the informative text.
243 deleteDialogMessageBox.setInformativeText(i18nc("Delete domain secondary message", "Doing so will also save any pending changes that have been made to other domains."));
245 // Set the standard buttons.
246 deleteDialogMessageBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No);
248 // Set the default button.
249 deleteDialogMessageBox.setDefaultButton(QMessageBox::No);
251 // Display the dialog and capture the return value.
252 int returnValue = deleteDialogMessageBox.exec();
254 if (returnValue == QMessageBox::Yes)
256 // Get the current index.
257 QModelIndex currentIndex = domainsListViewPointer->currentIndex();
259 // Delete the current row.
260 domainsTableModelPointer->removeRow(domainsListViewPointer->selectionModel()->currentIndex().row());
262 // Submit all pending changes.
263 domainsTableModelPointer->submitAll();
265 // Select the row next to the deleted item if one exists.
266 if (domainsTableModelPointer->rowCount() > 0)
268 // Check the row of the deleted item.
269 if (currentIndex.row() == 0) // The first row was deleted.
271 // Reselect the current index.
272 domainsListViewPointer->setCurrentIndex(currentIndex);
274 else // A subsequent row was deleted.
276 // Select the crow above the deleted itemm.
277 domainsListViewPointer->setCurrentIndex(currentIndex.siblingAtRow(currentIndex.row() - 1));
280 // Populate the domain settings.
281 domainSelected(domainsListViewPointer->currentIndex());
289 void DomainSettingsDialog::updateUi() const
291 // Update the delete button status.
292 deleteDomainButtonPointer->setEnabled(domainsListViewPointer->selectionModel()->hasSelection());
294 // Update the apply button status.
295 applyButtonPointer->setEnabled(domainsTableModelPointer->isDirty());
297 // Update the reset button status.
298 resetButtonPointer->setEnabled(domainsTableModelPointer->isDirty());
300 // Display the domain settings if there is at least one domain.
301 domainSettingsWidgetPointer->setVisible(domainsTableModelPointer->rowCount() > 0);