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"
23 #include "ui_DomainSettingsDialog.h"
24 #include "helpers/DomainsDatabaseHelper.h"
25 #include "helpers/UserAgentHelper.h"
27 // Qt toolkit headers.
28 #include <QInputDialog>
29 #include <QMessageBox>
30 #include <QPushButton>
32 // Define the public static int constants.
33 const int DomainSettingsDialog::SHOW_ALL_DOMAINS = 0;
34 const int DomainSettingsDialog::ADD_DOMAIN = 1;
35 const int DomainSettingsDialog::EDIT_DOMAIN = 2;
37 DomainSettingsDialog::DomainSettingsDialog(const int &startType, const QString &domainName) : QDialog(nullptr)
39 // Set the window title.
40 setWindowTitle(i18nc("The domain settings dialog window title", "Domain Settings"));
42 // Set the window modality.
43 setWindowModality(Qt::WindowModality::WindowModal);;
45 // Instantiate the domain settings dialog UI.
46 Ui::DomainSettingsDialog domainSettingsDialogUi;
49 domainSettingsDialogUi.setupUi(this);
51 // Get handles for the views.
52 domainsListViewPointer = domainSettingsDialogUi.domainsListView;
53 domainSettingsWidgetPointer = domainSettingsDialogUi.domainSettingsWidget;
54 domainNameLineEditPointer = domainSettingsDialogUi.domainNameLineEdit;
55 javaScriptComboBoxPointer = domainSettingsDialogUi.javaScriptComboBox;
56 javaScriptLabelPointer = domainSettingsDialogUi.javaScriptLabel;
57 localStorageComboBoxPointer = domainSettingsDialogUi.localStorageComboBox;
58 localStorageLabelPointer = domainSettingsDialogUi.localStorageLabel;
59 userAgentComboBoxPointer = domainSettingsDialogUi.userAgentComboBox;
60 userAgentLabelPointer = domainSettingsDialogUi.userAgentLabel;
61 zoomFactorComboBoxPointer = domainSettingsDialogUi.zoomFactorComboBox;
62 customZoomFactorSpinBoxPointer = domainSettingsDialogUi.customZoomFactorSpinBox;
63 QPushButton *addDomainButtonPointer = domainSettingsDialogUi.addDomainButton;
64 deleteDomainButtonPointer = domainSettingsDialogUi.deleteDomainButton;
65 QDialogButtonBox *dialogButtonBoxPointer = domainSettingsDialogUi.dialogButtonBox;
66 applyButtonPointer = dialogButtonBoxPointer->button(QDialogButtonBox::StandardButton::Apply);
67 resetButtonPointer = dialogButtonBoxPointer->button(QDialogButtonBox::StandardButton::Reset);
69 // Create a table model.
70 domainsTableModelPointer = new QSqlTableModel(nullptr, QSqlDatabase::database(DomainsDatabaseHelper::CONNECTION_NAME));
72 // Set the table for the model.
73 domainsTableModelPointer->setTable(DomainsDatabaseHelper::DOMAINS_TABLE);
75 // Set the edit strategy to be manual.
76 domainsTableModelPointer->setEditStrategy(QSqlTableModel::EditStrategy::OnManualSubmit);
78 // Sort the output alphabetically.
79 domainsTableModelPointer->setSort(1, Qt::SortOrder::AscendingOrder);
81 // Set the model for the list view.
82 domainsListViewPointer->setModel(domainsTableModelPointer);
84 // Set the visible column to be the domain name.
85 domainsListViewPointer->setModelColumn(1);
87 // Disable editing of the list view.
88 domainsListViewPointer->setEditTriggers(QAbstractItemView::NoEditTriggers);
90 // Read the data from the database and apply it to the table model.
91 domainsTableModelPointer->select();
93 // Setup the dialog according to the start type.
96 case SHOW_ALL_DOMAINS:
98 // Select the first entry in the list view.
99 domainsListViewPointer->setCurrentIndex(domainsTableModelPointer->index(0, domainsTableModelPointer->fieldIndex(DomainsDatabaseHelper::DOMAIN_NAME)));
101 // Populate the domain settings.
102 domainSelected(domainsListViewPointer->selectionModel()->currentIndex());
109 // Add the new domain.
110 addDomain(domainName);
117 // Find the index for the new domain. `1` returns the first match.
118 QModelIndexList newDomainIndex = domainsTableModelPointer->match(domainsTableModelPointer->index(0, domainsTableModelPointer->fieldIndex(DomainsDatabaseHelper::DOMAIN_NAME)),
119 Qt::DisplayRole, domainName, 1, Qt::MatchWrap);
121 // Move to the new domain.
122 domainsListViewPointer->setCurrentIndex(newDomainIndex[0]);
124 // Populate the domain settings.
125 domainSelected(domainsListViewPointer->selectionModel()->currentIndex());
129 // Handle clicks on the domains.
130 connect(domainsListViewPointer, SIGNAL(activated(QModelIndex)), this, SLOT(domainSelected(QModelIndex)));
132 // Connect the domain settings.
133 connect(domainNameLineEditPointer, SIGNAL(textEdited(QString)), this, SLOT(domainNameChanged(QString)));
134 connect(javaScriptComboBoxPointer, SIGNAL(currentIndexChanged(int)), this, SLOT(javaScriptChanged(int)));
135 connect(localStorageComboBoxPointer, SIGNAL(currentIndexChanged(int)), this, SLOT(localStorageChanged(int)));
136 connect(userAgentComboBoxPointer, SIGNAL(currentTextChanged(QString)), this, SLOT(userAgentChanged(QString)));
137 connect(zoomFactorComboBoxPointer, SIGNAL(currentIndexChanged(int)), this, SLOT(zoomFactorComboBoxChanged(int)));
138 connect(customZoomFactorSpinBoxPointer, SIGNAL(valueChanged(double)), this, SLOT(customZoomFactorChanged(double)));
140 // Connect the buttons.
141 connect(addDomainButtonPointer, SIGNAL(released()), this, SLOT(showAddMessageBox()));
142 connect(deleteDomainButtonPointer, SIGNAL(released()), this, SLOT(showDeleteMessageBox()));
143 connect(resetButtonPointer, SIGNAL(released()), this, SLOT(reset()));
144 connect(dialogButtonBoxPointer, SIGNAL(accepted()), this, SLOT(ok()));
145 connect(applyButtonPointer, SIGNAL(released()), this, SLOT(apply()));
146 connect(dialogButtonBoxPointer, SIGNAL(rejected()), this, SLOT(cancel()));
152 void DomainSettingsDialog::addDomain(const QString &domainName) const
154 // Create a new domain record.
155 QSqlRecord newDomainRecord = QSqlDatabase::database(DomainsDatabaseHelper::CONNECTION_NAME).record(DomainsDatabaseHelper::DOMAINS_TABLE);
157 // Set the values for the new domain.
158 newDomainRecord.setValue(domainsTableModelPointer->fieldIndex(DomainsDatabaseHelper::DOMAIN_NAME), domainName);
159 newDomainRecord.setValue(domainsTableModelPointer->fieldIndex(DomainsDatabaseHelper::JAVASCRIPT), DomainsDatabaseHelper::SYSTEM_DEFAULT);
160 newDomainRecord.setValue(domainsTableModelPointer->fieldIndex(DomainsDatabaseHelper::LOCAL_STORAGE), DomainsDatabaseHelper::SYSTEM_DEFAULT);
161 newDomainRecord.setValue(domainsTableModelPointer->fieldIndex(DomainsDatabaseHelper::USER_AGENT), UserAgentHelper::SYSTEM_DEFAULT_DATABASE);
162 newDomainRecord.setValue(domainsTableModelPointer->fieldIndex(DomainsDatabaseHelper::ZOOM_FACTOR), DomainsDatabaseHelper::SYSTEM_DEFAULT);
163 newDomainRecord.setValue(domainsTableModelPointer->fieldIndex(DomainsDatabaseHelper::CUSTOM_ZOOM_FACTOR), 1.0);
165 // Insert the new domain. `-1` appends it to the end.
166 domainsTableModelPointer->insertRecord(-1, newDomainRecord);
168 // Submit all pending changes.
169 domainsTableModelPointer->submitAll();
171 // Find the index for the new domain. `-1` allows for multiple entries to be returned.
172 QModelIndexList newDomainIndex = domainsTableModelPointer->match(domainsTableModelPointer->index(0, domainsTableModelPointer->fieldIndex(DomainsDatabaseHelper::DOMAIN_NAME)),
173 Qt::DisplayRole, domainName, -1, Qt::MatchWrap);
175 // Move to the new domain. If there are multiple domains with the same name, the new one should be the last in the list.
176 domainsListViewPointer->setCurrentIndex(newDomainIndex[newDomainIndex.size() - 1]);
178 // Populate the domain settings.
179 domainSelected(domainsListViewPointer->selectionModel()->currentIndex());
186 void DomainSettingsDialog::apply() const
188 // Get the current index.
189 QModelIndex currentIndex = domainsListViewPointer->currentIndex();
191 // Get the ID of the current index row.
192 QVariant currentId = currentIndex.siblingAtColumn(domainsTableModelPointer->fieldIndex(DomainsDatabaseHelper::_ID)).data();
194 // Submit all pending changes.
195 domainsTableModelPointer->submitAll();
197 // Find the new index for the selected id. The `1` keeps searching after the first match.
198 QModelIndexList newIndexList = domainsTableModelPointer->match(currentIndex.siblingAtColumn(domainsTableModelPointer->fieldIndex(DomainsDatabaseHelper::_ID)), Qt::DisplayRole, currentId,
201 // Select the new index.
202 domainsListViewPointer->setCurrentIndex(newIndexList[0].siblingAtColumn(domainsTableModelPointer->fieldIndex(DomainsDatabaseHelper::DOMAIN_NAME)));
207 // Emit the domain settings updated signal.
208 emit domainSettingsUpdated();
211 void DomainSettingsDialog::cancel()
213 // Revert all pending changes.
214 domainsTableModelPointer->revertAll();
220 void DomainSettingsDialog::customZoomFactorChanged(const double &newValue) const
222 // Update the domains table model.
223 domainsTableModelPointer->setData(domainsListViewPointer->selectionModel()->currentIndex().siblingAtColumn(domainsTableModelPointer->fieldIndex(DomainsDatabaseHelper::CUSTOM_ZOOM_FACTOR)),
230 void DomainSettingsDialog::domainNameChanged(const QString &updatedDomainName) const
232 // Update the domains table model.
233 domainsTableModelPointer->setData(domainsListViewPointer->selectionModel()->currentIndex(), updatedDomainName);
239 void DomainSettingsDialog::domainSelected(const QModelIndex &modelIndex) const
241 // Populate the domain name line edit pointer.
242 domainNameLineEditPointer->setText(modelIndex.data().toString());
244 // Populate the JavaScript combo box.
245 javaScriptComboBoxPointer->setCurrentIndex(modelIndex.siblingAtColumn(domainsTableModelPointer->fieldIndex(DomainsDatabaseHelper::JAVASCRIPT)).data().toInt());
247 // Populate the local storage combo box.
248 localStorageComboBoxPointer->setCurrentIndex(modelIndex.siblingAtColumn(domainsTableModelPointer->fieldIndex(DomainsDatabaseHelper::LOCAL_STORAGE)).data().toInt());
250 // Get the user agent string.
251 QString userAgent = modelIndex.siblingAtColumn(domainsTableModelPointer->fieldIndex(DomainsDatabaseHelper::USER_AGENT)).data().toString();
253 // Get the user agent index.
254 int userAgentIndex = UserAgentHelper::getDomainSettingsUserAgentIndex(userAgent);
256 // Set the user agent combo box index.
257 userAgentComboBoxPointer->setCurrentIndex(userAgentIndex);
259 // Set the custom user agent if specified.
260 if (userAgentIndex == -1) userAgentComboBoxPointer->setCurrentText(userAgent);
262 // Get the zoom factor combo box index.
263 int zoomFactorComboBoxIndex = modelIndex.siblingAtColumn(domainsTableModelPointer->fieldIndex(DomainsDatabaseHelper::ZOOM_FACTOR)).data().toInt();
265 // Populate the zoom factor combo box.
266 zoomFactorComboBoxPointer->setCurrentIndex(zoomFactorComboBoxIndex);
268 // Populate the custom zoom factor spin box according to the zoom factor combo box.
269 if (zoomFactorComboBoxIndex == 0) // System default zoom factor is selected.
271 // Display the default zoom factor.
272 customZoomFactorSpinBoxPointer->setValue(Settings::zoomFactor());
274 else // Custom zoom factor is selected.
276 // Display the custom zoom factor from the domain settings.
277 customZoomFactorSpinBoxPointer->setValue(modelIndex.siblingAtColumn(domainsTableModelPointer->fieldIndex(DomainsDatabaseHelper::CUSTOM_ZOOM_FACTOR)).data().toDouble());
280 // Set the initial status of the custom zoom factor spin box.
281 customZoomFactorSpinBoxPointer->setEnabled(zoomFactorComboBoxIndex);
283 // Populate the labels.
284 populateJavaScriptLabel();
285 populateLocalStorageLabel();
286 populateUserAgentLabel(userAgentComboBoxPointer->currentText());
292 void DomainSettingsDialog::javaScriptChanged(const int &newIndex) const
294 // Update the domains table model.
295 domainsTableModelPointer->setData(domainsListViewPointer->selectionModel()->currentIndex().siblingAtColumn(domainsTableModelPointer->fieldIndex(DomainsDatabaseHelper::JAVASCRIPT)),
298 // Populate the JavaScript label.
299 populateJavaScriptLabel();
305 void DomainSettingsDialog::localStorageChanged(const int &newIndex) const
307 // Update the domains table model.
308 domainsTableModelPointer->setData(domainsListViewPointer->selectionModel()->currentIndex().siblingAtColumn(domainsTableModelPointer->fieldIndex(DomainsDatabaseHelper::LOCAL_STORAGE)),
311 // Populate the local storage label.
312 populateLocalStorageLabel();
318 void DomainSettingsDialog::ok()
320 // Submit all pending changes.
321 domainsTableModelPointer->submitAll();
323 // Emit the domain settings updated signal.
324 domainSettingsUpdated();
330 void DomainSettingsDialog::populateJavaScriptLabel() const
332 // Populate the label according to the currently selected index.
333 switch (javaScriptComboBoxPointer->currentIndex())
335 case (DomainsDatabaseHelper::SYSTEM_DEFAULT):
337 // Set the text according to the system default.
338 if (Settings::javaScript()) javaScriptLabelPointer->setText(i18nc("Domains settings label", "JavaScript enabled"));
339 else javaScriptLabelPointer->setText(i18nc("Domain settings label", "JavaScript disabled"));
344 case (DomainsDatabaseHelper::DISABLED):
346 // Set the label text in bold.
347 javaScriptLabelPointer->setText(i18nc("Domain settings label. The <strong> tags should be retained.", "<strong>JavaScript disabled</strong>"));
352 case (DomainsDatabaseHelper::ENABLED):
354 // Set the label text in bold.
355 javaScriptLabelPointer->setText(i18nc("Domains settings label. The <strong> tags should be retained.", "<strong>JavaScript enabled</strong>"));
362 void DomainSettingsDialog::populateLocalStorageLabel() const
364 // Populate the label according to the currently selected index.
365 switch (localStorageComboBoxPointer->currentIndex())
367 case (DomainsDatabaseHelper::SYSTEM_DEFAULT):
369 // Set the text according to the system default.
370 if (Settings::localStorage()) localStorageLabelPointer->setText(i18nc("Local storage label", "Local storage enabled"));
371 else localStorageLabelPointer->setText(i18nc("Local storage label", "Local storage disabled"));
376 case (DomainsDatabaseHelper::DISABLED):
378 // Set the label text in bold.
379 localStorageLabelPointer->setText(i18nc("Local storage label. The <string> tags should be retained.", "<strong>Local storage disabled</strong>"));
384 case (DomainsDatabaseHelper::ENABLED):
386 // Set the label text in bold.
387 localStorageLabelPointer->setText(i18nc("Local storage label. The <strong> tags should be retained.", "<strong>Local storage enabled</strong>"));
394 void DomainSettingsDialog::populateUserAgentLabel(const QString &userAgentName) const
396 // Populate the label according to the type.
397 if (userAgentName == UserAgentHelper::SYSTEM_DEFAULT_TRANSLATED)
399 // Display the system default user agent name.
400 userAgentLabelPointer->setText(UserAgentHelper::getTranslatedUserAgentNameFromDatabaseName(Settings::userAgent()));
404 // Display the user agent name in bold.
405 userAgentLabelPointer->setText("<strong>" + userAgentName + "</strong>");
409 void DomainSettingsDialog::reset() const
411 // Cancel all pending changes.
412 domainsTableModelPointer->revertAll();
414 // Repopulate the domain settings.
415 domainSelected(domainsListViewPointer->currentIndex());
421 void DomainSettingsDialog::showAddMessageBox()
423 // Create an OK flag.
426 // Display a dialog to request the new domain name from the user.
427 QString newDomainName = QInputDialog::getText(this, i18nc("Add domain dialog title", "Add Domain"),
428 i18nc("Add domain message. The \n\n are newline codes that should be retained",
429 "Add a new domain. Doing so will also save any pending changes that have been made to other domains.\n\n"
430 "*. may be prepended to a domain to include all subdomains (eg. *.stoutner.com)."),
431 QLineEdit::Normal, QString(), &okClicked);
433 // Add the new domain if the user clicked OK.
434 if (okClicked) addDomain(newDomainName);
437 void DomainSettingsDialog::showDeleteMessageBox() const
439 // Instantiate a delete dialog message box.
440 QMessageBox deleteDialogMessageBox;
443 deleteDialogMessageBox.setIcon(QMessageBox::Warning);
445 // Set the window title.
446 deleteDialogMessageBox.setWindowTitle(i18nc("Delete domain dialog title", "Delete Domain"));
449 deleteDialogMessageBox.setText(i18nc("Delete domain dialog main message", "Delete the current domain?"));
451 // Set the informative text.
452 deleteDialogMessageBox.setInformativeText(i18nc("Delete domain dialog secondary message", "Doing so will also save any pending changes that have been made to other domains."));
454 // Set the standard buttons.
455 deleteDialogMessageBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No);
457 // Set the default button.
458 deleteDialogMessageBox.setDefaultButton(QMessageBox::No);
460 // Display the dialog and capture the return value.
461 int returnValue = deleteDialogMessageBox.exec();
463 // Delete the domain if instructed.
464 if (returnValue == QMessageBox::Yes)
466 // Get the current index.
467 QModelIndex currentIndex = domainsListViewPointer->currentIndex();
469 // Delete the current row.
470 domainsTableModelPointer->removeRow(domainsListViewPointer->selectionModel()->currentIndex().row());
472 // Submit all pending changes.
473 domainsTableModelPointer->submitAll();
475 // Select the row next to the deleted item if one exists.
476 if (domainsTableModelPointer->rowCount() > 0)
478 // Check the row of the deleted item.
479 if (currentIndex.row() == 0) // The first row was deleted.
481 // Reselect the current index.
482 domainsListViewPointer->setCurrentIndex(currentIndex);
484 else // A subsequent row was deleted.
486 // Select the crow above the deleted itemm.
487 domainsListViewPointer->setCurrentIndex(currentIndex.siblingAtRow(currentIndex.row() - 1));
490 // Populate the domain settings.
491 domainSelected(domainsListViewPointer->currentIndex());
499 void DomainSettingsDialog::updateUi() const
501 // Update the delete button status.
502 deleteDomainButtonPointer->setEnabled(domainsListViewPointer->selectionModel()->hasSelection());
504 // Update the apply button status.
505 applyButtonPointer->setEnabled(domainsTableModelPointer->isDirty());
507 // Update the reset button status.
508 resetButtonPointer->setEnabled(domainsTableModelPointer->isDirty());
510 // Display the domain settings if there is at least one domain.
511 domainSettingsWidgetPointer->setVisible(domainsTableModelPointer->rowCount() > 0);
514 void DomainSettingsDialog::userAgentChanged(const QString &updatedUserAgent) const
516 // Update the domains table model.
517 domainsTableModelPointer->setData(domainsListViewPointer->selectionModel()->currentIndex().siblingAtColumn(domainsTableModelPointer->fieldIndex(DomainsDatabaseHelper::USER_AGENT)),
518 UserAgentHelper::getDatabaseUserAgentNameFromTranslatedName(updatedUserAgent));
520 // Populate the user agent label.
521 populateUserAgentLabel(updatedUserAgent);
527 void DomainSettingsDialog::zoomFactorComboBoxChanged(const int &newIndex) const
529 // Get the current model index.
530 QModelIndex modelIndex = domainsListViewPointer->selectionModel()->currentIndex();
532 // Update the domains table model.
533 domainsTableModelPointer->setData(modelIndex.siblingAtColumn(domainsTableModelPointer->fieldIndex(DomainsDatabaseHelper::ZOOM_FACTOR)), newIndex);
535 // Populate the custom zoom factor spin box according to the zoom factor combo box.
536 if (newIndex == 0) // System default zoom factor is selected.
538 // Display the default zoom factor.
539 customZoomFactorSpinBoxPointer->setValue(Settings::zoomFactor());
541 else // Custom zoom factor is selected.
543 // Display the custom zoom factor from the domain settings.
544 customZoomFactorSpinBoxPointer->setValue(modelIndex.siblingAtColumn(domainsTableModelPointer->fieldIndex(DomainsDatabaseHelper::CUSTOM_ZOOM_FACTOR)).data().toDouble());
547 // Update the status of the custom zoom factor spin box.
548 customZoomFactorSpinBoxPointer->setEnabled(newIndex);