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 // Construct the class.
38 DomainSettingsDialog::DomainSettingsDialog(const int &startType, const QString &domainName) : QDialog(nullptr)
40 // Set the window title.
41 setWindowTitle(i18nc("The domain settings dialog window title", "Domain Settings"));
43 // Set the window modality.
44 setWindowModality(Qt::WindowModality::ApplicationModal);;
46 // Instantiate the domain settings dialog UI.
47 Ui::DomainSettingsDialog domainSettingsDialogUi;
50 domainSettingsDialogUi.setupUi(this);
52 // Get handles for the widgets.
53 domainsListViewPointer = domainSettingsDialogUi.domainsListView;
54 domainSettingsWidgetPointer = domainSettingsDialogUi.domainSettingsWidget;
55 domainNameLineEditPointer = domainSettingsDialogUi.domainNameLineEdit;
56 javaScriptComboBoxPointer = domainSettingsDialogUi.javaScriptComboBox;
57 javaScriptLabelPointer = domainSettingsDialogUi.javaScriptLabel;
58 localStorageComboBoxPointer = domainSettingsDialogUi.localStorageComboBox;
59 localStorageLabelPointer = domainSettingsDialogUi.localStorageLabel;
60 domStorageComboBoxPointer = domainSettingsDialogUi.domStorageComboBox;
61 domStorageLabelPointer = domainSettingsDialogUi.domStorageLabel;
62 userAgentComboBoxPointer = domainSettingsDialogUi.userAgentComboBox;
63 userAgentLabelPointer = domainSettingsDialogUi.userAgentLabel;
64 zoomFactorComboBoxPointer = domainSettingsDialogUi.zoomFactorComboBox;
65 customZoomFactorSpinBoxPointer = domainSettingsDialogUi.customZoomFactorSpinBox;
66 QPushButton *addDomainButtonPointer = domainSettingsDialogUi.addDomainButton;
67 deleteDomainButtonPointer = domainSettingsDialogUi.deleteDomainButton;
68 QDialogButtonBox *dialogButtonBoxPointer = domainSettingsDialogUi.dialogButtonBox;
69 applyButtonPointer = dialogButtonBoxPointer->button(QDialogButtonBox::StandardButton::Apply);
70 resetButtonPointer = dialogButtonBoxPointer->button(QDialogButtonBox::StandardButton::Reset);
72 // Create a table model.
73 domainsTableModelPointer = new QSqlTableModel(nullptr, QSqlDatabase::database(DomainsDatabaseHelper::CONNECTION_NAME));
75 // Set the table for the model.
76 domainsTableModelPointer->setTable(DomainsDatabaseHelper::DOMAINS_TABLE);
78 // Set the edit strategy to be manual.
79 domainsTableModelPointer->setEditStrategy(QSqlTableModel::EditStrategy::OnManualSubmit);
81 // Sort the output alphabetically.
82 domainsTableModelPointer->setSort(1, Qt::SortOrder::AscendingOrder);
84 // Set the model for the list view.
85 domainsListViewPointer->setModel(domainsTableModelPointer);
87 // Set the visible column to be the domain name.
88 domainsListViewPointer->setModelColumn(1);
90 // Get the domains selection model pointer.
91 domainsSelectionModelPointer = domainsListViewPointer->selectionModel();
93 // Disable editing of the list view.
94 domainsListViewPointer->setEditTriggers(QAbstractItemView::NoEditTriggers);
96 // Read the data from the database and apply it to the table model.
97 domainsTableModelPointer->select();
99 // Setup the dialog according to the start type.
102 case SHOW_ALL_DOMAINS:
104 // Select the first entry in the list view.
105 domainsListViewPointer->setCurrentIndex(domainsTableModelPointer->index(0, domainsTableModelPointer->fieldIndex(DomainsDatabaseHelper::DOMAIN_NAME)));
107 // Populate the domain settings.
108 domainSelected(domainsSelectionModelPointer->currentIndex());
115 // Add the new domain.
116 addDomain(domainName);
123 // Find the index for the new domain. `1` returns the first match.
124 QModelIndexList newDomainIndex = domainsTableModelPointer->match(domainsTableModelPointer->index(0, domainsTableModelPointer->fieldIndex(DomainsDatabaseHelper::DOMAIN_NAME)),
125 Qt::DisplayRole, domainName, 1, Qt::MatchWrap);
127 // Move to the new domain.
128 domainsListViewPointer->setCurrentIndex(newDomainIndex[0]);
130 // Populate the domain settings.
131 domainSelected(domainsSelectionModelPointer->currentIndex());
137 // Handle clicks on the domains.
138 connect(domainsListViewPointer, SIGNAL(activated(QModelIndex)), this, SLOT(domainSelected(QModelIndex)));
140 // Process changes to the domain settings.
141 connect(domainNameLineEditPointer, SIGNAL(textEdited(QString)), this, SLOT(domainNameChanged(QString)));
142 connect(javaScriptComboBoxPointer, SIGNAL(currentIndexChanged(int)), this, SLOT(javaScriptChanged(int)));
143 connect(localStorageComboBoxPointer, SIGNAL(currentIndexChanged(int)), this, SLOT(localStorageChanged(int)));
144 connect(domStorageComboBoxPointer, SIGNAL(currentIndexChanged(int)), this, SLOT(domStorageChanged(int)));
145 connect(userAgentComboBoxPointer, SIGNAL(currentTextChanged(QString)), this, SLOT(userAgentChanged(QString)));
146 connect(zoomFactorComboBoxPointer, SIGNAL(currentIndexChanged(int)), this, SLOT(zoomFactorComboBoxChanged(int)));
147 connect(customZoomFactorSpinBoxPointer, SIGNAL(valueChanged(double)), this, SLOT(customZoomFactorChanged(double)));
149 // Connect the buttons.
150 connect(addDomainButtonPointer, SIGNAL(clicked()), this, SLOT(showAddMessageBox()));
151 connect(deleteDomainButtonPointer, SIGNAL(clicked()), this, SLOT(showDeleteMessageBox()));
152 connect(resetButtonPointer, SIGNAL(clicked()), this, SLOT(reset()));
153 connect(dialogButtonBoxPointer, SIGNAL(accepted()), this, SLOT(ok()));
154 connect(applyButtonPointer, SIGNAL(clicked()), this, SLOT(apply()));
155 connect(dialogButtonBoxPointer, SIGNAL(rejected()), this, SLOT(cancel()));
161 void DomainSettingsDialog::addDomain(const QString &domainName) const
163 // Create a new domain record.
164 QSqlRecord newDomainRecord = QSqlDatabase::database(DomainsDatabaseHelper::CONNECTION_NAME).record(DomainsDatabaseHelper::DOMAINS_TABLE);
166 // Set the values for the new domain.
167 newDomainRecord.setValue(domainsTableModelPointer->fieldIndex(DomainsDatabaseHelper::DOMAIN_NAME), domainName);
168 newDomainRecord.setValue(domainsTableModelPointer->fieldIndex(DomainsDatabaseHelper::JAVASCRIPT), DomainsDatabaseHelper::SYSTEM_DEFAULT);
169 newDomainRecord.setValue(domainsTableModelPointer->fieldIndex(DomainsDatabaseHelper::DOM_STORAGE), DomainsDatabaseHelper::SYSTEM_DEFAULT);
170 newDomainRecord.setValue(domainsTableModelPointer->fieldIndex(DomainsDatabaseHelper::USER_AGENT), UserAgentHelper::SYSTEM_DEFAULT_DATABASE);
171 newDomainRecord.setValue(domainsTableModelPointer->fieldIndex(DomainsDatabaseHelper::ZOOM_FACTOR), DomainsDatabaseHelper::SYSTEM_DEFAULT);
172 newDomainRecord.setValue(domainsTableModelPointer->fieldIndex(DomainsDatabaseHelper::CUSTOM_ZOOM_FACTOR), 1.0);
174 // Insert the new domain. `-1` appends it to the end.
175 domainsTableModelPointer->insertRecord(-1, newDomainRecord);
177 // Submit all pending changes.
178 domainsTableModelPointer->submitAll();
180 // Find the index for the new domain. `-1` allows for multiple entries to be returned.
181 QModelIndexList newDomainIndex = domainsTableModelPointer->match(domainsTableModelPointer->index(0, domainsTableModelPointer->fieldIndex(DomainsDatabaseHelper::DOMAIN_NAME)),
182 Qt::DisplayRole, domainName, -1, Qt::MatchWrap);
184 // Move to the new domain. If there are multiple domains with the same name, the new one should be the last in the list.
185 domainsListViewPointer->setCurrentIndex(newDomainIndex[newDomainIndex.size() - 1]);
187 // Populate the domain settings.
188 domainSelected(domainsSelectionModelPointer->currentIndex());
194 void DomainSettingsDialog::apply() const
196 // Get the current index.
197 QModelIndex currentIndex = domainsListViewPointer->currentIndex();
199 // Get the ID of the current index row.
200 QVariant currentId = currentIndex.siblingAtColumn(domainsTableModelPointer->fieldIndex(DomainsDatabaseHelper::_ID)).data();
202 // Submit all pending changes.
203 domainsTableModelPointer->submitAll();
205 // Find the new index for the selected id. The `1` keeps searching after the first match.
206 QModelIndexList newIndexList = domainsTableModelPointer->match(currentIndex.siblingAtColumn(domainsTableModelPointer->fieldIndex(DomainsDatabaseHelper::_ID)), Qt::DisplayRole, currentId,
209 // Select the new index.
210 domainsListViewPointer->setCurrentIndex(newIndexList[0].siblingAtColumn(domainsTableModelPointer->fieldIndex(DomainsDatabaseHelper::DOMAIN_NAME)));
215 // Emit the domain settings updated signal.
216 emit domainSettingsUpdated();
219 void DomainSettingsDialog::cancel()
221 // Revert all pending changes.
222 domainsTableModelPointer->revertAll();
228 void DomainSettingsDialog::customZoomFactorChanged(const double &newValue) const
230 // Update the domains table model.
231 domainsTableModelPointer->setData(domainsSelectionModelPointer->currentIndex().siblingAtColumn(domainsTableModelPointer->fieldIndex(DomainsDatabaseHelper::CUSTOM_ZOOM_FACTOR)), newValue);
237 void DomainSettingsDialog::domStorageChanged(const int &newIndex) const
239 // Update the domains table model.
240 domainsTableModelPointer->setData(domainsSelectionModelPointer->currentIndex().siblingAtColumn(domainsTableModelPointer->fieldIndex(DomainsDatabaseHelper::DOM_STORAGE)), newIndex);
242 // Populate the DOM storage label.
243 populateDomStorageLabel();
249 void DomainSettingsDialog::domainNameChanged(const QString &updatedDomainName) const
251 // Update the domains table model.
252 domainsTableModelPointer->setData(domainsSelectionModelPointer->currentIndex(), updatedDomainName);
258 void DomainSettingsDialog::domainSelected(const QModelIndex &modelIndex) const
260 // Populate the domain name line edit pointer.
261 domainNameLineEditPointer->setText(modelIndex.data().toString());
263 // Populate the JavaScript combo box.
264 javaScriptComboBoxPointer->setCurrentIndex(modelIndex.siblingAtColumn(domainsTableModelPointer->fieldIndex(DomainsDatabaseHelper::JAVASCRIPT)).data().toInt());
266 // Populate the local storage combo box.
267 localStorageComboBoxPointer->setCurrentIndex(modelIndex.siblingAtColumn(domainsTableModelPointer->fieldIndex(DomainsDatabaseHelper::LOCAL_STORAGE)).data().toInt());
269 // Populate the DOM storage combo box.
270 domStorageComboBoxPointer->setCurrentIndex(modelIndex.siblingAtColumn(domainsTableModelPointer->fieldIndex(DomainsDatabaseHelper::DOM_STORAGE)).data().toInt());
272 // Get the user agent string.
273 QString userAgent = modelIndex.siblingAtColumn(domainsTableModelPointer->fieldIndex(DomainsDatabaseHelper::USER_AGENT)).data().toString();
275 // Get the user agent index.
276 int userAgentIndex = UserAgentHelper::getDomainSettingsUserAgentIndex(userAgent);
278 // Set the user agent combo box index.
279 userAgentComboBoxPointer->setCurrentIndex(userAgentIndex);
281 // Set the custom user agent if specified.
282 if (userAgentIndex == -1) userAgentComboBoxPointer->setCurrentText(userAgent);
284 // Get the zoom factor combo box index.
285 int zoomFactorComboBoxIndex = modelIndex.siblingAtColumn(domainsTableModelPointer->fieldIndex(DomainsDatabaseHelper::ZOOM_FACTOR)).data().toInt();
287 // Populate the zoom factor combo box.
288 zoomFactorComboBoxPointer->setCurrentIndex(zoomFactorComboBoxIndex);
290 // Populate the custom zoom factor spin box according to the zoom factor combo box.
291 if (zoomFactorComboBoxIndex == 0) // System default zoom factor is selected.
293 // Display the default zoom factor.
294 customZoomFactorSpinBoxPointer->setValue(Settings::zoomFactor());
296 else // Custom zoom factor is selected.
298 // Display the custom zoom factor from the domain settings.
299 customZoomFactorSpinBoxPointer->setValue(modelIndex.siblingAtColumn(domainsTableModelPointer->fieldIndex(DomainsDatabaseHelper::CUSTOM_ZOOM_FACTOR)).data().toDouble());
302 // Set the initial status of the custom zoom factor spin box.
303 customZoomFactorSpinBoxPointer->setEnabled(zoomFactorComboBoxIndex);
305 // Populate the labels.
306 populateJavaScriptLabel();
307 populateLocalStorageLabel();
308 populateDomStorageLabel();
309 populateUserAgentLabel(userAgentComboBoxPointer->currentText());
315 void DomainSettingsDialog::javaScriptChanged(const int &newIndex) const
317 // Update the domains table model.
318 domainsTableModelPointer->setData(domainsSelectionModelPointer->currentIndex().siblingAtColumn(domainsTableModelPointer->fieldIndex(DomainsDatabaseHelper::JAVASCRIPT)), newIndex);
320 // Populate the JavaScript label.
321 populateJavaScriptLabel();
327 void DomainSettingsDialog::localStorageChanged(const int &newIndex) const
329 // Update the domains table model.
330 domainsTableModelPointer->setData(domainsSelectionModelPointer->currentIndex().siblingAtColumn(domainsTableModelPointer->fieldIndex(DomainsDatabaseHelper::LOCAL_STORAGE)), newIndex);
332 // Poplate the local storage label.
333 populateLocalStorageLabel();
339 void DomainSettingsDialog::ok()
341 // Submit all pending changes.
342 domainsTableModelPointer->submitAll();
344 // Emit the domain settings updated signal.
345 domainSettingsUpdated();
351 void DomainSettingsDialog::populateDomStorageLabel() const
353 // Populate the label according to the currently selected index.
354 switch (domStorageComboBoxPointer->currentIndex())
356 // Set the text according to the system default.
357 case (DomainsDatabaseHelper::SYSTEM_DEFAULT):
359 if (Settings::domStorageEnabled())
360 domStorageLabelPointer->setText(i18nc("Domain settings DOM storage label.", "DOM storage enabled"));
362 domStorageLabelPointer->setText(i18nc("Domain settings DOM storage label.", "DOM storage disabled"));
367 // Set the disabled text in bold.
368 case (DomainsDatabaseHelper::DISABLED):
370 domStorageLabelPointer->setText(i18nc("Domain settings DOM storage label. The <b> tags should be retained.", "<b>DOM storage disabled</b>"));
375 // Set the enabled text in bold.
376 case (DomainsDatabaseHelper::ENABLED):
378 domStorageLabelPointer->setText(i18nc("Domain settings DOM storage label. The <b> tags should be retained.", "<b>DOM storage enabled</b>"));
385 void DomainSettingsDialog::populateJavaScriptLabel() const
387 // Populate the label according to the currently selected index.
388 switch (javaScriptComboBoxPointer->currentIndex())
390 // Set the text according to the system default.
391 case (DomainsDatabaseHelper::SYSTEM_DEFAULT):
393 if (Settings::javaScriptEnabled())
394 javaScriptLabelPointer->setText(i18nc("Domain settings JavaScript label.", "JavaScript enabled"));
396 javaScriptLabelPointer->setText(i18nc("Domain settings JavaScript label.", "JavaScript disabled"));
401 // Set the disabled text in bold.
402 case (DomainsDatabaseHelper::DISABLED):
404 javaScriptLabelPointer->setText(i18nc("Domain settings JavaScript label. The <b> tags should be retained.", "<b>JavaScript disabled</b>"));
409 // Set the enabled text in bold.
410 case (DomainsDatabaseHelper::ENABLED):
412 javaScriptLabelPointer->setText(i18nc("Domain settings JavaScript label. The <b> tags should be retained.", "<b>JavaScript enabled</b>"));
419 void DomainSettingsDialog::populateLocalStorageLabel() const
421 // Populate the label according to the currently selected index.
422 switch (localStorageComboBoxPointer->currentIndex())
424 // Set the text according to the system default.
425 case (DomainsDatabaseHelper::SYSTEM_DEFAULT):
427 if (Settings::localStorageEnabled())
428 localStorageLabelPointer->setText(i18nc("Domain settings local storage label.", "Local storage enabled"));
430 localStorageLabelPointer->setText(i18nc("Domain settings local storage label.", "Local storage disabled"));
435 // Set the disabled text in bold.
436 case (DomainsDatabaseHelper::DISABLED):
438 localStorageLabelPointer->setText(i18nc("Domain settings local storage label. The <b> tags should be retained.", "<b>Local storage disabled</b>"));
443 // Set the enabled text in bold.
444 case (DomainsDatabaseHelper::ENABLED):
446 localStorageLabelPointer->setText(i18nc("Domain settings local storage label. The <b> tabs should be retained.", "<b>Local storage enabled</b>"));
454 void DomainSettingsDialog::populateUserAgentLabel(const QString &userAgentName) const
456 // Populate the label according to the type.
457 if (userAgentName == UserAgentHelper::SYSTEM_DEFAULT_TRANSLATED)
459 // Display the system default user agent name.
460 userAgentLabelPointer->setText(UserAgentHelper::getTranslatedUserAgentNameFromDatabaseName(Settings::userAgent()));
464 // Display the user agent name in bold.
465 userAgentLabelPointer->setText("<strong>" + userAgentName + "</strong>");
469 void DomainSettingsDialog::reset() const
471 // Cancel all pending changes.
472 domainsTableModelPointer->revertAll();
474 // Repopulate the domain settings.
475 domainSelected(domainsListViewPointer->currentIndex());
481 void DomainSettingsDialog::showAddMessageBox()
483 // Create an OK flag.
486 // Display a dialog to request the new domain name from the user.
487 QString newDomainName = QInputDialog::getText(this, i18nc("Add domain dialog title", "Add Domain"),
488 i18nc("Add domain message. The \n\n are newline codes that should be retained",
489 "Add a new domain. Doing so will also save any pending changes that have been made to other domains.\n\n"
490 "*. may be prepended to a domain to include all subdomains (eg. *.stoutner.com)."),
491 QLineEdit::Normal, QString(), &okClicked);
493 // Add the new domain if the user clicked OK.
494 if (okClicked) addDomain(newDomainName);
497 void DomainSettingsDialog::showDeleteMessageBox() const
499 // Instantiate a delete dialog message box.
500 QMessageBox deleteDialogMessageBox;
503 deleteDialogMessageBox.setIcon(QMessageBox::Warning);
505 // Set the window title.
506 deleteDialogMessageBox.setWindowTitle(i18nc("Delete domain dialog title", "Delete Domain"));
509 deleteDialogMessageBox.setText(i18nc("Delete domain dialog main message", "Delete the current domain?"));
511 // Set the informative text.
512 deleteDialogMessageBox.setInformativeText(i18nc("Delete domain dialog secondary message", "Doing so will also save any pending changes that have been made to other domains."));
514 // Set the standard buttons.
515 deleteDialogMessageBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No);
517 // Set the default button.
518 deleteDialogMessageBox.setDefaultButton(QMessageBox::No);
520 // Display the dialog and capture the return value.
521 int returnValue = deleteDialogMessageBox.exec();
523 // Delete the domain if instructed.
524 if (returnValue == QMessageBox::Yes)
526 // Get the current index.
527 QModelIndex currentIndex = domainsListViewPointer->currentIndex();
529 // Delete the current row.
530 domainsTableModelPointer->removeRow(domainsSelectionModelPointer->currentIndex().row());
532 // Submit all pending changes.
533 domainsTableModelPointer->submitAll();
535 // Select the row next to the deleted item if one exists.
536 if (domainsTableModelPointer->rowCount() > 0)
538 // Check the row of the deleted item.
539 if (currentIndex.row() == 0) // The first row was deleted.
541 // Reselect the current index.
542 domainsListViewPointer->setCurrentIndex(currentIndex);
544 else // A subsequent row was deleted.
546 // Select the crow above the deleted itemm.
547 domainsListViewPointer->setCurrentIndex(currentIndex.siblingAtRow(currentIndex.row() - 1));
550 // Populate the domain settings.
551 domainSelected(domainsListViewPointer->currentIndex());
559 void DomainSettingsDialog::updateUi() const
561 // Update the delete button status.
562 deleteDomainButtonPointer->setEnabled(domainsSelectionModelPointer->hasSelection());
564 // Update the apply button status.
565 applyButtonPointer->setEnabled(domainsTableModelPointer->isDirty());
567 // Update the reset button status.
568 resetButtonPointer->setEnabled(domainsTableModelPointer->isDirty());
570 // Display the domain settings if there is at least one domain.
571 domainSettingsWidgetPointer->setVisible(domainsTableModelPointer->rowCount() > 0);
574 void DomainSettingsDialog::userAgentChanged(const QString &updatedUserAgent) const
576 // Update the domains table model.
577 domainsTableModelPointer->setData(domainsSelectionModelPointer->currentIndex().siblingAtColumn(domainsTableModelPointer->fieldIndex(DomainsDatabaseHelper::USER_AGENT)),
578 UserAgentHelper::getDatabaseUserAgentNameFromTranslatedName(updatedUserAgent));
580 // Populate the user agent label.
581 populateUserAgentLabel(updatedUserAgent);
587 void DomainSettingsDialog::zoomFactorComboBoxChanged(const int &newIndex) const
589 // Get the current model index.
590 QModelIndex modelIndex = domainsSelectionModelPointer->currentIndex();
592 // Update the domains table model.
593 domainsTableModelPointer->setData(modelIndex.siblingAtColumn(domainsTableModelPointer->fieldIndex(DomainsDatabaseHelper::ZOOM_FACTOR)), newIndex);
595 // Populate the custom zoom factor spin box according to the zoom factor combo box.
596 if (newIndex == 0) // System default zoom factor is selected.
598 // Display the default zoom factor.
599 customZoomFactorSpinBoxPointer->setValue(Settings::zoomFactor());
601 else // Custom zoom factor is selected.
603 // Display the custom zoom factor from the domain settings.
604 customZoomFactorSpinBoxPointer->setValue(modelIndex.siblingAtColumn(domainsTableModelPointer->fieldIndex(DomainsDatabaseHelper::CUSTOM_ZOOM_FACTOR)).data().toDouble());
607 // Update the status of the custom zoom factor spin box.
608 customZoomFactorSpinBoxPointer->setEnabled(newIndex);