From 16118809a11aa423f453a03c47f5263e9dd8b662 Mon Sep 17 00:00:00 2001 From: Soren Stoutner Date: Sat, 19 Mar 2022 11:21:53 -0700 Subject: [PATCH] Switch the Domain Settings implementation to a Dialog. --- src/CMakeLists.txt | 3 +- src/DomainSettingsDialog.ui | 167 ++++++++++ src/DomainSettingsView.ui | 122 ------- src/UrlRequestInterceptor.cpp | 2 +- src/UrlRequestInterceptor.h | 2 +- src/dialogs/CMakeLists.txt | 22 ++ src/dialogs/DomainSettingsDialog.cpp | 303 ++++++++++++++++++ .../DomainSettingsDialog.h} | 41 ++- src/helpers/DomainsDatabaseHelper.cpp | 84 ++++- src/helpers/DomainsDatabaseHelper.h | 13 + src/ui.rc/CMakeLists.txt | 1 - src/ui.rc/domain_settings_ui.rc | 43 --- src/views/BrowserView.cpp | 137 +++++--- src/views/BrowserView.h | 6 +- src/views/CMakeLists.txt | 1 - src/views/DomainSettingsView.cpp | 82 ----- src/windows/BrowserWindow.cpp | 3 +- src/windows/CMakeLists.txt | 1 - src/windows/DomainSettingsWindow.cpp | 72 ----- src/windows/DomainSettingsWindow.h | 38 --- 20 files changed, 704 insertions(+), 439 deletions(-) create mode 100644 src/DomainSettingsDialog.ui delete mode 100644 src/DomainSettingsView.ui create mode 100644 src/dialogs/CMakeLists.txt create mode 100644 src/dialogs/DomainSettingsDialog.cpp rename src/{views/DomainSettingsView.h => dialogs/DomainSettingsDialog.h} (59%) delete mode 100644 src/ui.rc/domain_settings_ui.rc delete mode 100644 src/views/DomainSettingsView.cpp delete mode 100644 src/windows/DomainSettingsWindow.cpp delete mode 100644 src/windows/DomainSettingsWindow.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index d9f46da..c679f99 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -38,7 +38,7 @@ kconfig_add_kcfg_files(privacy-browser Settings.kcfgc) # Use KDE Frameworks to handle internationalization of the following UI files. ki18n_wrap_ui(privacy-browser BrowserView.ui - DomainSettingsView.ui + DomainSettingsDialog.ui SettingsPrivacy.ui SettingsGeneral.ui ) @@ -62,6 +62,7 @@ target_link_libraries(privacy-browser ) # Add the subdirectories. +add_subdirectory(dialogs) add_subdirectory(helpers) add_subdirectory(views) add_subdirectory(windows) diff --git a/src/DomainSettingsDialog.ui b/src/DomainSettingsDialog.ui new file mode 100644 index 0000000..e6f9aca --- /dev/null +++ b/src/DomainSettingsDialog.ui @@ -0,0 +1,167 @@ + + + + + + DomainSettingsDialog + + + + + 0 + 0 + 700 + 500 + + + + + + + + + + + + + + + + + + + + Add Domain + + + + + + + + + + + + + Delete domain + + + + + + + + + + + + + Qt::Horizontal + + + + + + + + + + + + + + + + + + + Domain name + + + + + + + + *. may be prepended to a domain to include all subdomains (eg. *.stoutner.com). + + + + + + + + + JavaScript + + + + + + + + JavaScript allows websites to run programs (scripts) on the device. + + + + + System default + + + + + + JavaScript disabled + + + + + + JavaScript enabled + + + + + + + + + + + + + Qt::Vertical + + + + + + + + + QDialogButtonBox::Reset | QDialogButtonBox::Ok | QDialogButtonBox::Apply | QDialogButtonBox::Cancel + + + + + + + + diff --git a/src/DomainSettingsView.ui b/src/DomainSettingsView.ui deleted file mode 100644 index f847f3c..0000000 --- a/src/DomainSettingsView.ui +++ /dev/null @@ -1,122 +0,0 @@ - - - - - - DomainSettingsView - - - - - - 0 - 0 - 700 - 500 - - - - - - - - 0 - - - - - 0 - - - - 0 - - - - 0 - - - - 0 - - - - - - - - - - - - - - - Domain name - - - - - - - - *. may be prepended to a domain to include all subdomains (eg. *.stoutner.com). - - - - - - - - - JavaScript - - - - - - - - JavaScript allows websites to run programs (scripts) on the device. - - - - - System default - - - - - - JavaScript disabled - - - - - - JavaScript enabled - - - - - - - - - diff --git a/src/UrlRequestInterceptor.cpp b/src/UrlRequestInterceptor.cpp index 12be0ed..10f9862 100644 --- a/src/UrlRequestInterceptor.cpp +++ b/src/UrlRequestInterceptor.cpp @@ -41,7 +41,7 @@ void UrlRequestInterceptor::interceptRequest(QWebEngineUrlRequestInfo &urlReques // Reapply the domain settings if the host is changing. if (requestingHost != requestedHost) { - emit applyDomainSettings(); + emit applyDomainSettings(requestedHost); } } diff --git a/src/UrlRequestInterceptor.h b/src/UrlRequestInterceptor.h index 8f3306b..721d523 100644 --- a/src/UrlRequestInterceptor.h +++ b/src/UrlRequestInterceptor.h @@ -37,6 +37,6 @@ public: signals: // The signals. - void applyDomainSettings() const; + void applyDomainSettings(const QString hostname) const; }; #endif diff --git a/src/dialogs/CMakeLists.txt b/src/dialogs/CMakeLists.txt new file mode 100644 index 0000000..e2effdb --- /dev/null +++ b/src/dialogs/CMakeLists.txt @@ -0,0 +1,22 @@ +# Copyright © 2022 Soren Stoutner . +# +# This file is part of Privacy Browser PC . +# +# Privacy Browser PC is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Privacy Browser PC is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Privacy Browser PC. If not, see . + + +# List the sources to include in the executable. +target_sources(privacy-browser PRIVATE + DomainSettingsDialog.cpp +) diff --git a/src/dialogs/DomainSettingsDialog.cpp b/src/dialogs/DomainSettingsDialog.cpp new file mode 100644 index 0000000..0ebfb14 --- /dev/null +++ b/src/dialogs/DomainSettingsDialog.cpp @@ -0,0 +1,303 @@ +/* + * Copyright © 2022 Soren Stoutner . + * + * This file is part of Privacy Browser PC . + * + * Privacy Browser PC is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Privacy Browser PC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Privacy Browser PC. If not, see . + */ + +// Application headers. +#include "DomainSettingsDialog.h" +#include "ui_DomainSettingsDialog.h" +#include "helpers/DomainsDatabaseHelper.h" + +// Qt toolkit headers. +#include +#include +#include + +DomainSettingsDialog::DomainSettingsDialog(QWidget *parent) : QDialog(parent) +{ + // Instantiate the domain settings view UI. + Ui::DomainSettingsDialog domainSettingsDialogUi; + + // Setup the UI. + domainSettingsDialogUi.setupUi(this); + + // Get handles for the views. + domainsListViewPointer = domainSettingsDialogUi.domainsListView; + domainSettingsWidgetPointer = domainSettingsDialogUi.domainSettingsWidget; + domainNameLineEditPointer = domainSettingsDialogUi.domainNameLineEdit; + javaScriptComboBoxPointer = domainSettingsDialogUi.javaScriptComboBox; + QPushButton *addDomainButtonPointer = domainSettingsDialogUi.addDomainButton; + deleteDomainButtonPointer = domainSettingsDialogUi.deleteDomainButton; + QDialogButtonBox *dialogButtonBoxPointer = domainSettingsDialogUi.dialogButtonBox; + applyButtonPointer = dialogButtonBoxPointer->button(QDialogButtonBox::StandardButton::Apply); + resetButtonPointer = dialogButtonBoxPointer->button(QDialogButtonBox::StandardButton::Reset); + + // Create a table model. + domainsTableModelPointer = new QSqlTableModel(nullptr, QSqlDatabase::database(DomainsDatabaseHelper::CONNECTION_NAME)); + + // Set the table for the model. + domainsTableModelPointer->setTable(DomainsDatabaseHelper::DOMAINS_TABLE); + + // Set the edit strategy to be manual. + domainsTableModelPointer->setEditStrategy(QSqlTableModel::EditStrategy::OnManualSubmit); + + // Sort the output alphabetically. + domainsTableModelPointer->setSort(1, Qt::SortOrder::AscendingOrder); + + // Set the model for the list view. + domainsListViewPointer->setModel(domainsTableModelPointer); + + // Set the visible column to be the domain name. + domainsListViewPointer->setModelColumn(1); + + // Disable editing of the list view. + domainsListViewPointer->setEditTriggers(QAbstractItemView::NoEditTriggers); + + // Read the data from the database and apply it to the table model. + domainsTableModelPointer->select(); + + // Select the first entry in the list view. + domainsListViewPointer->setCurrentIndex(domainsTableModelPointer->index(0, domainsTableModelPointer->fieldIndex(DomainsDatabaseHelper::DOMAIN_NAME))); + + // Populate the domain settings. + domainSelected(domainsListViewPointer->selectionModel()->currentIndex()); + + // Handle clicks on the domains. + connect(domainsListViewPointer, SIGNAL(activated(QModelIndex)), this, SLOT(domainSelected(QModelIndex))); + + // Connect the domain settings. + connect(domainNameLineEditPointer, SIGNAL(textEdited(QString)), this, SLOT(domainNameChanged(QString))); + connect(javaScriptComboBoxPointer, SIGNAL(currentIndexChanged(int)), this, SLOT(javaScriptChanged(int))); + + // Connect the buttons. + connect(addDomainButtonPointer, SIGNAL(released()), this, SLOT(showAddMessageBox())); + connect(deleteDomainButtonPointer, SIGNAL(released()), this, SLOT(showDeleteMessageBox())); + connect(resetButtonPointer, SIGNAL(released()), this, SLOT(reset())); + connect(dialogButtonBoxPointer, SIGNAL(accepted()), this, SLOT(ok())); + connect(applyButtonPointer, SIGNAL(released()), this, SLOT(apply())); + connect(dialogButtonBoxPointer, SIGNAL(rejected()), this, SLOT(cancel())); + + // Update the UI. + updateUi(); +} + +void DomainSettingsDialog::apply() const +{ + // Get the current index. + QModelIndex currentIndex = domainsListViewPointer->currentIndex(); + + // Get the ID of the current index row. + QVariant currentId = currentIndex.siblingAtColumn(domainsTableModelPointer->fieldIndex(DomainsDatabaseHelper::_ID)).data(); + + // Submit all pending changes. + domainsTableModelPointer->submitAll(); + + // Find the new index for the selected id. The `1` keeps searching after the first match. + QModelIndexList newIndexList = domainsTableModelPointer->match(currentIndex.siblingAtColumn(domainsTableModelPointer->fieldIndex(DomainsDatabaseHelper::_ID)), Qt::DisplayRole, currentId, + 1, Qt::MatchWrap); + + // Select the new index. + domainsListViewPointer->setCurrentIndex(newIndexList[0].siblingAtColumn(domainsTableModelPointer->fieldIndex(DomainsDatabaseHelper::DOMAIN_NAME))); + + // Update the UI. + updateUi(); +} + +void DomainSettingsDialog::cancel() +{ + // Revert all pending changes. + domainsTableModelPointer->revertAll(); + + // Close the dialog. + reject(); +} + +void DomainSettingsDialog::domainNameChanged(QString updatedDomainName) const +{ + // Update the domains table model. + domainsTableModelPointer->setData(domainsListViewPointer->selectionModel()->currentIndex(), updatedDomainName); + + // Update the UI. + updateUi(); +} + + +void DomainSettingsDialog::domainSelected(QModelIndex modelIndex) const +{ + // Populate the domain name line edit pointer. + domainNameLineEditPointer->setText(modelIndex.data().toString()); + + // Populate the JavaScript combo box. + javaScriptComboBoxPointer->setCurrentIndex(modelIndex.siblingAtColumn(domainsTableModelPointer->fieldIndex(DomainsDatabaseHelper::JAVASCRIPT)).data().toInt()); + + // Update the UI. + updateUi(); +} + +void DomainSettingsDialog::javaScriptChanged(int newIndex) const +{ + // Update the domains table model. + domainsTableModelPointer->setData(domainsListViewPointer->selectionModel()->currentIndex().siblingAtColumn(domainsTableModelPointer->fieldIndex(DomainsDatabaseHelper::JAVASCRIPT)), + newIndex); + + // Update the UI. + updateUi(); +} + + +void DomainSettingsDialog::ok() +{ + // Submit all pending changes. + domainsTableModelPointer->submitAll(); + + // Close the dialog. + accept(); +} + +void DomainSettingsDialog::reset() const +{ + // Cancel all pending changes. + domainsTableModelPointer->revertAll(); + + // Repopulate the domain name line edit. + domainNameLineEditPointer->setText(domainsListViewPointer->currentIndex().data().toString()); + + // Update the UI. + updateUi(); +} + +void DomainSettingsDialog::showAddMessageBox() +{ + // Create an OK flag. + bool okClicked; + + // Display a dialog to request the new domain name from the user. + QString newDomainName = QInputDialog::getText(this, i18nc("Add domain dialog title", "Add Domain"), + i18nc("Add domain message. The \n\n are newline codes that should be retained", + "Add a new domain. Doing so will also save any pending changes that have been made to other domains.\n\n" + "*. may be prepended to a domain to include all subdomains (eg. *.stoutner.com)."), + QLineEdit::Normal, QString(), &okClicked); + + // Add the new domain if the user clicked OK. + if (okClicked) + { + // Create a new domain record. + QSqlRecord newDomainRecord = QSqlDatabase::database(DomainsDatabaseHelper::CONNECTION_NAME).record(DomainsDatabaseHelper::DOMAINS_TABLE); + + // Add the new domain name. + newDomainRecord.setValue(domainsTableModelPointer->fieldIndex(DomainsDatabaseHelper::DOMAIN_NAME), newDomainName); + + // Set the default value of `0` for the other columns. + newDomainRecord.setValue(domainsTableModelPointer->fieldIndex(DomainsDatabaseHelper::JAVASCRIPT), 0); + + // Insert the new domain. `-1` appends it to the end. + domainsTableModelPointer->insertRecord(-1, newDomainRecord); + + // Submit all pending changes. + domainsTableModelPointer->submitAll(); + + // Find the index for the new domain. `-1` allows for multiple entries to be returned. + QModelIndexList newDomainIndex = domainsTableModelPointer->match(domainsTableModelPointer->index(0, domainsTableModelPointer->fieldIndex(DomainsDatabaseHelper::DOMAIN_NAME)), + Qt::DisplayRole, newDomainName, -1, Qt::MatchWrap); + + // Move to the new domain. If there are multiple domains with the same name, the new one should be the last in the list. + domainsListViewPointer->setCurrentIndex(newDomainIndex[newDomainIndex.size() - 1]); + + // Populate the domain settings. + domainSelected(domainsListViewPointer->selectionModel()->currentIndex()); + + // Update the UI. + updateUi(); + } +} + +void DomainSettingsDialog::showDeleteMessageBox() const +{ + // Instantiate a delete dialog message box. + QMessageBox deleteDialogMessageBox; + + // Set the icon. + deleteDialogMessageBox.setIcon(QMessageBox::Warning); + + // Set the window title. + deleteDialogMessageBox.setWindowTitle(i18nc("Delete domain dialog title", "Delete Domain")); + + // Set the text. + deleteDialogMessageBox.setText(i18nc("Delete domain main message", "Delete the current domain?")); + + // Set the informative text. + deleteDialogMessageBox.setInformativeText(i18nc("Delete domain secondary message", "Doing so will also save any pending changes that have been made to other domains.")); + + // Set the standard buttons. + deleteDialogMessageBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No); + + // Set the default button. + deleteDialogMessageBox.setDefaultButton(QMessageBox::No); + + // Display the dialog and capture the return value. + int returnValue = deleteDialogMessageBox.exec(); + + if (returnValue == QMessageBox::Yes) + { + // Get the current index. + QModelIndex currentIndex = domainsListViewPointer->currentIndex(); + + // Delete the current row. + domainsTableModelPointer->removeRow(domainsListViewPointer->selectionModel()->currentIndex().row()); + + // Submit all pending changes. + domainsTableModelPointer->submitAll(); + + // Select the row next to the deleted item if one exists. + if (domainsTableModelPointer->rowCount() > 0) + { + // Check the row of the deleted item. + if (currentIndex.row() == 0) // The first row was deleted. + { + // Reselect the current index. + domainsListViewPointer->setCurrentIndex(currentIndex); + } + else // A subsequent row was deleted. + { + // Select the crow above the deleted itemm. + domainsListViewPointer->setCurrentIndex(currentIndex.siblingAtRow(currentIndex.row() - 1)); + } + + // Populate the domain settings. + domainSelected(domainsListViewPointer->currentIndex()); + } + + // Update the Ui. + updateUi(); + } +} + +void DomainSettingsDialog::updateUi() const +{ + // Update the delete button status. + deleteDomainButtonPointer->setEnabled(domainsListViewPointer->selectionModel()->hasSelection()); + + // Update the apply button status. + applyButtonPointer->setEnabled(domainsTableModelPointer->isDirty()); + + // Update the reset button status. + resetButtonPointer->setEnabled(domainsTableModelPointer->isDirty()); + + // Display the domain settings if there is at least one domain. + domainSettingsWidgetPointer->setVisible(domainsTableModelPointer->rowCount() > 0); +} + diff --git a/src/views/DomainSettingsView.h b/src/dialogs/DomainSettingsDialog.h similarity index 59% rename from src/views/DomainSettingsView.h rename to src/dialogs/DomainSettingsDialog.h index 04b2a2c..3ae50b5 100644 --- a/src/views/DomainSettingsView.h +++ b/src/dialogs/DomainSettingsDialog.h @@ -17,38 +17,49 @@ * along with Privacy Browser PC. If not, see . */ -#ifndef DOMAINSETTINGSVIEW_H -#define DOMAINSETTINGSVIEW_H +#ifndef DOMAINSETTINGSDIALOG_H +#define DOMAINSETTINGSDIALOG_H -// Qt framework headers. -#include -#include - -// KDE Framework headers. +// KDE Frameworks headers. #include -class DomainSettingsView : public QWidget +// Qt toolkit headers. +#include +#include + +class DomainSettingsDialog : public QDialog { // Include the Q_OBJECT macro. Q_OBJECT public: // The primary constructor. - explicit DomainSettingsView(QWidget *parent); - -public Q_SLOTS: - // The public slots. - void addDomain(); - void deleteDomain(); + explicit DomainSettingsDialog(QWidget *parent = nullptr); private Q_SLOTS: // The private slots. - void domainSelected(QModelIndex modelIndex); + void apply() const; + void cancel(); + void domainNameChanged(QString updatedDomainName) const; + void domainSelected(QModelIndex modelIndex) const; + void javaScriptChanged(int newIndex) const; + void ok(); + void reset() const; + void showAddMessageBox(); + void showDeleteMessageBox() const; private: + // The private functions. + void updateUi() const; + // The private variables. + QPushButton *applyButtonPointer; + QPushButton *deleteDomainButtonPointer; QListView *domainsListViewPointer; KLineEdit *domainNameLineEditPointer; + QWidget *domainSettingsWidgetPointer; QSqlTableModel *domainsTableModelPointer; + QComboBox *javaScriptComboBoxPointer; + QPushButton *resetButtonPointer; }; #endif diff --git a/src/helpers/DomainsDatabaseHelper.cpp b/src/helpers/DomainsDatabaseHelper.cpp index 8d127cf..b801a0b 100644 --- a/src/helpers/DomainsDatabaseHelper.cpp +++ b/src/helpers/DomainsDatabaseHelper.cpp @@ -20,13 +20,18 @@ // Application headers. #include "DomainsDatabaseHelper.h" -// Qt framework headers. -#include - -// Define the static constants. +// Define the public static domain constants. const QString DomainsDatabaseHelper::CONNECTION_NAME = "domains_database"; const QString DomainsDatabaseHelper::DOMAINS_TABLE = "domains"; +// Define the private static schema constants. +const int DomainsDatabaseHelper::SCHEMA_VERSION = 1; + +// Define the public static database field names. +const QString DomainsDatabaseHelper::_ID = "_id"; +const QString DomainsDatabaseHelper::DOMAIN_NAME = "domain_name"; +const QString DomainsDatabaseHelper::JAVASCRIPT = "javascript"; + // The default constructor. DomainsDatabaseHelper::DomainsDatabaseHelper() {} @@ -44,7 +49,35 @@ void DomainsDatabaseHelper::addDatabase() // Check to see if the domains table already exists. if (domainsDatabase.tables().contains(DOMAINS_TABLE)) { - // Run schema update code. + // Query the database schema version. + QSqlQuery getSchemaVersionQuery = domainsDatabase.exec("PRAGMA user_version"); + + // Move to the first record. + getSchemaVersionQuery.first(); + + // Get the current schema version. + int currentSchemaVersion = getSchemaVersionQuery.value(0).toInt(); + + // Check to see if the schama has been updated. + if (SCHEMA_VERSION > currentSchemaVersion) + { + // Run schema update code. + switch (currentSchemaVersion) + { + // Upgrade from schema version 0. + case 0: + { + // Add the JavaScript column. + domainsDatabase.exec("ALTER TABLE " + DOMAINS_TABLE + " ADD COLUMN " + JAVASCRIPT + " INTEGER DEFAULT 0"); + + // Set the default value. + domainsDatabase.exec("UPDATE " + DOMAINS_TABLE + " SET " + JAVASCRIPT + " = 0" ); + } + } + + // Update the schema version. + domainsDatabase.exec("PRAGMA user_version = " + QString::number(SCHEMA_VERSION)); + } } else { @@ -52,9 +85,10 @@ void DomainsDatabaseHelper::addDatabase() QSqlQuery createTableQuery(domainsDatabase); // Prepare the create table query. - createTableQuery.prepare("CREATE TABLE " + DOMAINS_TABLE + "(" - "_id INTEGER PRIMARY KEY, " - "domain_name TEXT)" + createTableQuery.prepare("CREATE TABLE " + DOMAINS_TABLE + "(" + + _ID + " INTEGER PRIMARY KEY, " + + DOMAIN_NAME + " TEXT, " + + JAVASCRIPT + " INTEGER DEFAULT 0)" ); // Execute the query. @@ -63,6 +97,9 @@ void DomainsDatabaseHelper::addDatabase() // Log any errors. qDebug().noquote().nospace() << "Error creating table: " << domainsDatabase.lastError(); } + + // Set the schema version. + domainsDatabase.exec("PRAGMA user_version = " + QString::number(SCHEMA_VERSION)); } } else // Opening the database failed. @@ -71,3 +108,34 @@ void DomainsDatabaseHelper::addDatabase() qDebug().noquote().nospace() << "Error opening database: " << domainsDatabase.lastError(); } }; + +QSqlQuery DomainsDatabaseHelper::getDomainQuery(const QString &hostname) +{ + // Get a handle for the domains database. + QSqlDatabase domainsDatabase = QSqlDatabase::database(CONNECTION_NAME); + + // Instantiate a domain lookup query. + QSqlQuery domainLookupQuery(domainsDatabase); + + // Create a hostname field. + QSqlField hostnameField(DOMAIN_NAME, QVariant::String); + + // Set the hostname field value. + hostnameField.setValue(hostname); + + // SQL escape the hostname field. + QString sqlEscapedHostname = domainsDatabase.driver()->formatValue(hostnameField); + + // Prepare the domain lookup query. + domainLookupQuery.prepare("SELECT * FROM " + DOMAINS_TABLE + " WHERE " + DOMAIN_NAME + " = " + sqlEscapedHostname); + + // Execute the query. + domainLookupQuery.exec(); + + // Move to the first entry. + domainLookupQuery.first(); + + // Return the query. + return domainLookupQuery; +} + diff --git a/src/helpers/DomainsDatabaseHelper.h b/src/helpers/DomainsDatabaseHelper.h index 9faa4c9..b02a4cf 100644 --- a/src/helpers/DomainsDatabaseHelper.h +++ b/src/helpers/DomainsDatabaseHelper.h @@ -31,9 +31,22 @@ public: // The public functions. static void addDatabase(); + static QSqlQuery getDomainQuery(const QString &hostname); + + // The public int constants. + static const int SYSTEM_DEFAULT = 0; + static const int DISABLED = 1; + static const int ENABLED = 2; // The public constants. + static const QString _ID; static const QString CONNECTION_NAME; + static const QString DOMAIN_NAME; static const QString DOMAINS_TABLE; + static const QString JAVASCRIPT; + +private: + // The private static constants. + static const int SCHEMA_VERSION; }; #endif diff --git a/src/ui.rc/CMakeLists.txt b/src/ui.rc/CMakeLists.txt index c3ebd72..2c8c30e 100644 --- a/src/ui.rc/CMakeLists.txt +++ b/src/ui.rc/CMakeLists.txt @@ -19,5 +19,4 @@ # Install Privacy Browser's RC (Runtime Configuration) files. install(FILES browser_ui.rc - domain_settings_ui.rc DESTINATION ${KDE_INSTALL_KXMLGUI5DIR}/privacybrowser) diff --git a/src/ui.rc/domain_settings_ui.rc b/src/ui.rc/domain_settings_ui.rc deleted file mode 100644 index ca64ac0..0000000 --- a/src/ui.rc/domain_settings_ui.rc +++ /dev/null @@ -1,43 +0,0 @@ - - - - - - - - - Main Toolbar - - - - - - - Apply Toolbar - - - - - - diff --git a/src/views/BrowserView.cpp b/src/views/BrowserView.cpp index 44c170a..aa9ffff 100644 --- a/src/views/BrowserView.cpp +++ b/src/views/BrowserView.cpp @@ -23,10 +23,11 @@ #include "Settings.h" #include "ui_BrowserView.h" #include "UrlRequestInterceptor.h" +#include "dialogs/DomainSettingsDialog.h" +#include "helpers/DomainsDatabaseHelper.h" #include "helpers/SearchEngineHelper.h" #include "helpers/UserAgentHelper.h" #include "windows/BrowserWindow.h" -#include "windows/DomainSettingsWindow.h" // Qt framework headers. #include @@ -72,6 +73,13 @@ BrowserView::BrowserView(QWidget *parent) : QWidget(parent) connect(javaScriptButtonPointer, SIGNAL(clicked()), this, SLOT(toggleJavaScript())); connect(domainSettingsButtonPointer, SIGNAL(clicked()), this, SLOT(openDomainSettings())); + // Get the URL line edit palettes. + noDomainSettingsPalette = urlLineEditPointer->palette(); + domainSettingsPalette = urlLineEditPointer->palette(); + + // Modify the domain settings palette. + domainSettingsPalette.setColor(QPalette::Base, Qt::green); + // Instantiate the mouse event pointer. MouseEventFilter *mouseEventFilterPointer = new MouseEventFilter(webEngineViewPointer); @@ -88,7 +96,7 @@ BrowserView::BrowserView(QWidget *parent) : QWidget(parent) webEngineProfilePointer->setUrlRequestInterceptor(urlRequestInterceptorPointer); // Reapply the domain settings when the host changes. - connect(urlRequestInterceptorPointer, SIGNAL(applyDomainSettings()), this, SLOT(applyDomainSettingsWithoutReloading())); + connect(urlRequestInterceptorPointer, SIGNAL(applyDomainSettings(QString)), this, SLOT(applyDomainSettingsWithoutReloading(QString))); // Disable the cache. webEngineProfilePointer->setHttpCacheType(QWebEngineProfile::NoCache); @@ -113,23 +121,83 @@ void BrowserView::applyApplicationSettings() void BrowserView::applyDomainSettingsAndReload() const { // Apply the domain settings. `true` reloads the website. - applyDomainSettings(true); + applyDomainSettings(webEngineViewPointer->url().host(), true); } // This exists as a separate function from `applyDomainSettings()` so it can be listed as a slot and function without the need for a boolean argument. -void BrowserView::applyDomainSettingsWithoutReloading() const +void BrowserView::applyDomainSettingsWithoutReloading(const QString &hostname) const { // Apply the domain settings `false` does not reload the website. - applyDomainSettings(false); + applyDomainSettings(hostname, false); } -void BrowserView::applyDomainSettings(bool reloadWebsite) const +void BrowserView::applyDomainSettings(const QString &hostname, const bool reloadWebsite) const { - // Set the JavaScript status. - webEngineSettingsPointer->setAttribute(QWebEngineSettings::JavascriptEnabled, Settings::javaScript()); + // Get the record for the hostname. + QSqlQuery domainQuery = DomainsDatabaseHelper::getDomainQuery(hostname); + + // Check if the hostname has domain settings. + if (domainQuery.isValid()) // The hostname has domain settings. + { + + + // Get the domain record. + QSqlRecord domainRecord = domainQuery.record(); + + // Set the JavaScript status. + switch (domainRecord.field(DomainsDatabaseHelper::JAVASCRIPT).value().toInt()) + { + case (DomainsDatabaseHelper::SYSTEM_DEFAULT): + { + // Set the default JavaScript status. + webEngineSettingsPointer->setAttribute(QWebEngineSettings::JavascriptEnabled, Settings::javaScript()); + + break; + } + + case (DomainsDatabaseHelper::DISABLED): + { + // Disable JavaScript. + webEngineSettingsPointer->setAttribute(QWebEngineSettings::JavascriptEnabled, false); + + break; + } + + case (DomainsDatabaseHelper::ENABLED): + { + // Enable JavaScript. + webEngineSettingsPointer->setAttribute(QWebEngineSettings::JavascriptEnabled, true); + + break; + } + } + + // Apply the user agent. + webEngineProfilePointer->setHttpUserAgent(UserAgentHelper::getUserAgent(Settings::userAgent())); + + // Set the zoom factor. + webEngineViewPointer->setZoomFactor(Settings::zoomFactor()); + + // Apply the domain settings palette to the URL line edit. + urlLineEditPointer->setPalette(domainSettingsPalette); + } + else // The hostname does not have domain settings. + { + // Set the JavaScript status. + webEngineSettingsPointer->setAttribute(QWebEngineSettings::JavascriptEnabled, Settings::javaScript()); + + // Apply the user agent. + webEngineProfilePointer->setHttpUserAgent(UserAgentHelper::getUserAgent(Settings::userAgent())); + + // Set the zoom factor. + webEngineViewPointer->setZoomFactor(Settings::zoomFactor()); + + // Apply the no domain settings palette to the URL line edit. + urlLineEditPointer->setPalette(noDomainSettingsPalette); + } // Update the JavaScript button. - if (Settings::javaScript()) + if (webEngineSettingsPointer->testAttribute(QWebEngineSettings::JavascriptEnabled)) { javaScriptButtonPointer->setIcon(QIcon(":/icons/javascript-warning")); } @@ -138,12 +206,6 @@ void BrowserView::applyDomainSettings(bool reloadWebsite) const javaScriptButtonPointer->setIcon(QIcon(":/icons/privacy-mode")); } - // Apply the user agent. - webEngineProfilePointer->setHttpUserAgent(UserAgentHelper::getUserAgent(Settings::userAgent())); - - // Set the zoom factor. - webEngineViewPointer->setZoomFactor(Settings::zoomFactor()); - // Emit the on-the-fly menu update signals. emit userAgentUpdated(Settings::userAgent()); emit zoomFactorUpdated(Settings::zoomFactor()); @@ -242,45 +304,20 @@ void BrowserView::loadUrlFromTextBox(QString urlFromUser) const void BrowserView::openDomainSettings() const { - // Get a list of the top level widgets. - const QWidgetList topLevelWidgets = QApplication::topLevelWidgets(); + // Instantiate the domain settings window. + DomainSettingsDialog *domainSettingsDialogPointer = new DomainSettingsDialog(); - // Initialize a domain settings window exists boolean. - bool domainSettingsWindowExists = false; + // Set the dialog window title. + domainSettingsDialogPointer->setWindowTitle(i18nc("The domain settings dialog title", "Domain Settings")); - // Iterate through the top level widgets. - for (QWidget *widgetPointer : topLevelWidgets) - { - // Check for an existing domain settings window. - if (widgetPointer->objectName() == QStringLiteral("domain_settings")) - { - // Show the existing domain settings window if it is hidden. - widgetPointer->show(); - - // Raise the existing domain settings window if it is below other windows. - widgetPointer->raise(); - - // Restore the existing domain settings window if it has been minimized. - if (widgetPointer->isMinimized()) { - widgetPointer->showNormal(); - } - - // Activate the existing domain settings window, which brings its virtual desktop into focus. - widgetPointer->activateWindow(); + // Resize the dialog window. + domainSettingsDialogPointer->resize(1500, 1000); - // Update the domain settings window exists boolean. - domainSettingsWindowExists = true; - } - } + // Set the modality. + domainSettingsDialogPointer->setWindowModality(Qt::WindowModality::WindowModal);; - if (!domainSettingsWindowExists) - { - // Instantiate the domain settings window. - DomainSettingsWindow *domainSettingsWindowPointer = new DomainSettingsWindow(); - - // Show the window. - domainSettingsWindowPointer->show(); - } + // Show the dialog. + domainSettingsDialogPointer->show(); } void BrowserView::pageLinkHovered(const QString &linkUrl) const diff --git a/src/views/BrowserView.h b/src/views/BrowserView.h index 2804783..2685216 100644 --- a/src/views/BrowserView.h +++ b/src/views/BrowserView.h @@ -53,7 +53,7 @@ public Q_SLOTS: // The public slots. void applyApplicationSettings(); void applyDomainSettingsAndReload() const; - void applyDomainSettingsWithoutReloading() const; + void applyDomainSettingsWithoutReloading(const QString &hostname) const; void applyOnTheFlySearchEngine(QAction *searchEngineActionPointer); void applyOnTheFlyUserAgent(QAction *userAgentActionPointer) const; @@ -69,8 +69,10 @@ private Q_SLOTS: private: // The private variables. QPushButton *backButtonPointer; + QPalette domainSettingsPalette; QPushButton *forwardButtonPointer; QPushButton *javaScriptButtonPointer; + QPalette noDomainSettingsPalette; QString searchEngineUrl; KLineEdit *urlLineEditPointer; QWebEngineHistory *webEngineHistoryPointer; @@ -79,6 +81,6 @@ private: QWebEngineView *webEngineViewPointer; // The private functions. - void applyDomainSettings(bool reloadWebsite) const; + void applyDomainSettings(const QString &hostname, const bool reloadWebsite) const; }; #endif diff --git a/src/views/CMakeLists.txt b/src/views/CMakeLists.txt index cd64952..2929781 100644 --- a/src/views/CMakeLists.txt +++ b/src/views/CMakeLists.txt @@ -19,5 +19,4 @@ # List the sources to include in the executable. target_sources(privacy-browser PRIVATE BrowserView.cpp - DomainSettingsView.cpp ) diff --git a/src/views/DomainSettingsView.cpp b/src/views/DomainSettingsView.cpp deleted file mode 100644 index ff1131f..0000000 --- a/src/views/DomainSettingsView.cpp +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright © 2022 Soren Stoutner . - * - * This file is part of Privacy Browser PC . - * - * Privacy Browser PC is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Privacy Browser PC is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Privacy Browser PC. If not, see . - */ - -// Application headers. -#include "DomainSettingsView.h" -#include "ui_DomainSettingsView.h" -#include "helpers/DomainsDatabaseHelper.h" - -DomainSettingsView::DomainSettingsView(QWidget *parent) : QWidget(parent) -{ - // Instantiate the domain settings view UI. - Ui::DomainSettingsView domainSettingsViewUi; - - // Setup the UI. - domainSettingsViewUi.setupUi(this); - - // Get handles for the views. - domainsListViewPointer = domainSettingsViewUi.domainsListView; - domainNameLineEditPointer = domainSettingsViewUi.domainNameLineEdit; - - // Create a table model. - domainsTableModelPointer = new QSqlTableModel(0, QSqlDatabase::database(DomainsDatabaseHelper::CONNECTION_NAME)); - - // Set the table for the model. - domainsTableModelPointer->setTable(DomainsDatabaseHelper::DOMAINS_TABLE); - - // Set the model for the list view. - domainsListViewPointer->setModel(domainsTableModelPointer); - - // Set the visible column to be the domain name. - domainsListViewPointer->setModelColumn(1); - - // Disable editing of the list view. - domainsListViewPointer->setEditTriggers(QAbstractItemView::NoEditTriggers); - - // Handle clicks on the domains. - connect(domainsListViewPointer, SIGNAL(activated(QModelIndex)), this, SLOT(domainSelected(QModelIndex))); - - // Read the data from the database and apply it to the table model. - domainsTableModelPointer->select(); - - // Select the first entry in the list view. - domainsListViewPointer->setCurrentIndex(domainsTableModelPointer->index(0, 1)); - - // Populate the domain settings. - domainSelected(domainsListViewPointer->selectionModel()->currentIndex()); -} - -void DomainSettingsView::addDomain() -{ - // Insert a row. - domainsTableModelPointer->insertRows(domainsTableModelPointer->rowCount(), 1); -} - -void DomainSettingsView::deleteDomain() -{ - // Delete the current row. - domainsTableModelPointer->removeRow(domainsListViewPointer->selectionModel()->currentIndex().row()); -} - - -void DomainSettingsView::domainSelected(QModelIndex modelIndex) -{ - // Populate the domain name line edit pointer. - domainNameLineEditPointer->setText(modelIndex.data().toString()); -} diff --git a/src/windows/BrowserWindow.cpp b/src/windows/BrowserWindow.cpp index e7bb620..1ab6cfc 100644 --- a/src/windows/BrowserWindow.cpp +++ b/src/windows/BrowserWindow.cpp @@ -153,10 +153,11 @@ void BrowserWindow::getZoomFactorFromUser() bool okClicked; // Display a dialog to get the new zoom factor from the user. Format the double to display two decimals and have a 0.25 step. - double newZoomFactor = QInputDialog::getDouble(this, i18nc("The tile of the on-the-fly zoom factor dialog", "On-The-Fly Zoom Factor"), + double newZoomFactor = QInputDialog::getDouble(this, i18nc("The on-the-fly zoom factor dialog title", "On-The-Fly Zoom Factor"), i18nc("The instruction text of the on-the-fly zoom factor dialog", "Enter a zoom factor between 0.25 and 5.00"), currentZoomFactor, .025, 5.00, 2, &okClicked, Qt::WindowFlags(), 0.25); + // Update the zoom factor if the user clicked OK. if (okClicked) { // Update the current zoom factor. diff --git a/src/windows/CMakeLists.txt b/src/windows/CMakeLists.txt index 7f3cbf0..5fd76c4 100644 --- a/src/windows/CMakeLists.txt +++ b/src/windows/CMakeLists.txt @@ -19,5 +19,4 @@ # List the sources to include in the executable. target_sources(privacy-browser PRIVATE BrowserWindow.cpp - DomainSettingsWindow.cpp ) diff --git a/src/windows/DomainSettingsWindow.cpp b/src/windows/DomainSettingsWindow.cpp deleted file mode 100644 index af4b3e7..0000000 --- a/src/windows/DomainSettingsWindow.cpp +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright © 2022 Soren Stoutner . - * - * This file is part of Privacy Browser PC . - * - * Privacy Browser PC is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Privacy Browser PC is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Privacy Browser PC. If not, see . - */ - -// Application headers. -#include "DomainSettingsWindow.h" -#include "views/DomainSettingsView.h" - -// KDE Frameworks headers. -#include -#include - -DomainSettingsWindow::DomainSettingsWindow() : KXmlGuiWindow() -{ - // Instantiate the domain settings view pointer. - DomainSettingsView *domainSettingsViewPointer = new DomainSettingsView(this); - - // Set the domain settings view as the central widget. - setCentralWidget(domainSettingsViewPointer); - - // Set the object name. - this->setObjectName(QStringLiteral("domain_settings")); - - // Set the window title. - setCaption(i18nc("The Domain Settings window title", "Domain Settings")); - - // Get a handle for the action collection. - KActionCollection *actionCollectionPointer = this->actionCollection(); - - // Add the custom actions. - QAction *addDomainActionPointer = actionCollectionPointer->addAction(QStringLiteral("add_domain")); - QAction *deleteDomainActionPointer = actionCollectionPointer->addAction(QStringLiteral("delete_domain")); - QAction *okActionPointer = actionCollectionPointer->addAction(QStringLiteral("ok")); - QAction *applyActionPointer = actionCollectionPointer->addAction(QStringLiteral("apply")); - QAction *cancelActionPointer = actionCollectionPointer->addAction(QStringLiteral("cancel")); - - // Set the action text. - addDomainActionPointer->setText(i18nc("Add domain toolbar button", "Add Domain")); - deleteDomainActionPointer->setText(i18nc("Delete domain toolbar button", "Delete Domain")); - okActionPointer->setText(i18nc("OK toolbar button", "OK")); - applyActionPointer->setText(i18nc("Apply toolbar button", "Apply")); - cancelActionPointer->setText(i18nc("Cancel toolbar button", "Cancel")); - - // Set the action icons. - addDomainActionPointer->setIcon(QIcon::fromTheme(QStringLiteral("document-new"))); - deleteDomainActionPointer->setIcon(QIcon::fromTheme(QStringLiteral("delete"))); - okActionPointer->setIcon(QIcon::fromTheme(QStringLiteral("dialog-ok"))); - applyActionPointer->setIcon(QIcon::fromTheme(QStringLiteral("dialog-ok-apply"))); - cancelActionPointer->setIcon(QIcon::fromTheme(QStringLiteral("dialog-cancel"))); - - // Connect the button signals. - connect(addDomainActionPointer, SIGNAL(triggered()), domainSettingsViewPointer, SLOT(addDomain())); - connect(deleteDomainActionPointer, SIGNAL(triggered()), domainSettingsViewPointer, SLOT(deleteDomain())); - - // Setup the GUI without a status bar based on the domain_settings_ui.rc file. - setupGUI(StandardWindowOption::ToolBar | StandardWindowOption::Keys | StandardWindowOption::Save | StandardWindowOption::Create, ("domain_settings_ui.rc")); -} diff --git a/src/windows/DomainSettingsWindow.h b/src/windows/DomainSettingsWindow.h deleted file mode 100644 index 2301951..0000000 --- a/src/windows/DomainSettingsWindow.h +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright © 2022 Soren Stoutner . - * - * This file is part of Privacy Browser PC . - * - * Privacy Browser PC is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Privacy Browser PC is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Privacy Browser PC. If not, see . - */ - -#ifndef DOMAINSETTINGSWINDOW_H -#define DOMAINSETTINGSWINDOW_H - -// KDE Frameworks headers. -#include - -// Qt toolkit headers. -#include - -class DomainSettingsWindow : public KXmlGuiWindow -{ - // Include the Q_OBJECT macro. - Q_OBJECT - -public: - // The default constructor. - DomainSettingsWindow(); -}; -#endif -- 2.43.0