From: Soren Stoutner Date: Thu, 12 May 2022 20:46:50 +0000 (-0700) Subject: Change the cookie implementation to a QTreeView. X-Git-Tag: v0.1~35 X-Git-Url: https://gitweb.stoutner.com/?a=commitdiff_plain;h=cba9a47f00b59f59f76f1b5195086285ca0cdb59;p=PrivacyBrowserPC.git Change the cookie implementation to a QTreeView. --- diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index a7ee578..9729c95 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -35,9 +35,8 @@ kconfig_add_kcfg_files(privacy-browser settings/Settings.kcfgc) # Use KDE Frameworks to handle internationalization of the following UI files. ki18n_wrap_ui(privacy-browser - ui/AddCookieDialog.ui + ui/AddOrEditCookieDialog.ui ui/BrowserView.ui - ui/CookieDisplayWidget.ui ui/CookiesDialog.ui ui/DomainSettingsDialog.ui ui/SettingsGeneral.ui diff --git a/src/dialogs/AddCookieDialog.cpp b/src/dialogs/AddCookieDialog.cpp deleted file mode 100644 index a81d433..0000000 --- a/src/dialogs/AddCookieDialog.cpp +++ /dev/null @@ -1,121 +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 "AddCookieDialog.h" -#include "ui_AddCookieDialog.h" - -// KDE Framework headers. -#include - -// Qt toolkit header. -#include -#include - -AddCookieDialog::AddCookieDialog() : QDialog(nullptr) -{ - // Set the dialog window title. - setWindowTitle(i18nc("The add cookie dialog window title", "Add Cookie")); - - // Set the window modality. - setWindowModality(Qt::WindowModality::ApplicationModal); - - // Instantiate the cookie settings dialog UI. - Ui::AddCookieDialog addCookieDialogUi; - - // Setup the UI. - addCookieDialogUi.setupUi(this); - - // Get handles for the views. - domainLineEditPointer = addCookieDialogUi.domainLineEdit; - nameLineEditPointer = addCookieDialogUi.nameLineEdit; - expirationCheckBoxPointer = addCookieDialogUi.expirationCheckBox; - expirationDateTimeEditPointer = addCookieDialogUi.expirationDateTimeEdit; - pathLineEditPointer = addCookieDialogUi.pathLineEdit; - httpOnlyCheckBoxPointer = addCookieDialogUi.httpOnlyCheckBox; - secureCheckBoxPointer = addCookieDialogUi.secureCheckBox; - valueLineEditPointer = addCookieDialogUi.valueLineEdit; - QDialogButtonBox *dialogButtonBoxPointer = addCookieDialogUi.dialogButtonBox; - saveButtonPointer = dialogButtonBoxPointer->button(QDialogButtonBox::Save); - - // Connect the line edits. - connect(domainLineEditPointer, SIGNAL(textEdited(QString)), this, SLOT(updateUi())); - connect(nameLineEditPointer, SIGNAL(textEdited(QString)), this, SLOT(updateUi())); - connect(pathLineEditPointer, SIGNAL(textEdited(QString)), this, SLOT(updateUi())); - connect(valueLineEditPointer, SIGNAL(textEdited(QString)), this, SLOT(updateUi())); - - // Connect the check boxes. - connect(expirationCheckBoxPointer, SIGNAL(stateChanged(int)), this, SLOT(updateExpirationDateTimeState(int))); - - // Connect the buttons. - connect(dialogButtonBoxPointer, SIGNAL(accepted()), this, SLOT(saveCookie())); - connect(dialogButtonBoxPointer, SIGNAL(rejected()), this, SLOT(reject())); - - // Update the UI. - updateUi(); -} - -void AddCookieDialog::saveCookie() -{ - // Create the variables. - QNetworkCookie cookie; - - // Populate the cookie. - cookie.setDomain(domainLineEditPointer->text()); - cookie.setName(nameLineEditPointer->text().toUtf8()); - cookie.setPath(pathLineEditPointer->text()); - cookie.setHttpOnly(httpOnlyCheckBoxPointer->isChecked()); - cookie.setSecure(secureCheckBoxPointer->isChecked()); - cookie.setValue(valueLineEditPointer->text().toUtf8()); - - // Populate the expiration date if it is specified. - if (expirationCheckBoxPointer->isChecked()) cookie.setExpirationDate(expirationDateTimeEditPointer->dateTime()); - - // Add the cookie. - emit addCookie(cookie); - - // Close the dialog. - reject(); -} - -void AddCookieDialog::updateExpirationDateTimeState(const int &newState) const -{ - // Update the state of the of the expiration date time edit. - switch (newState) - { - case Qt::Unchecked: - // Disable the expiration date time. - expirationDateTimeEditPointer->setEnabled(false); - - break; - - case Qt::Checked: - // Enable the expiration date time edit. - expirationDateTimeEditPointer->setEnabled(true); - - break; - } -} - -void AddCookieDialog::updateUi() const -{ - // Update the state of the save button based on all the required fields containing text. - saveButtonPointer->setDisabled(domainLineEditPointer->text().isEmpty() || nameLineEditPointer->text().isEmpty() || pathLineEditPointer->text().isEmpty() || - valueLineEditPointer->text().isEmpty()); -} diff --git a/src/dialogs/AddCookieDialog.h b/src/dialogs/AddCookieDialog.h deleted file mode 100644 index f7354ce..0000000 --- a/src/dialogs/AddCookieDialog.h +++ /dev/null @@ -1,60 +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 ADDCOOKIEDIALOG_H -#define ADDCOOKIEDIALOG_H - -// Qt toolkit headers. -#include -#include -#include -#include - -class AddCookieDialog : public QDialog -{ - // Include the Q_OBJECT macro. - Q_OBJECT - -public: - // The default constructor. - explicit AddCookieDialog(); - -signals: - // The signals. - void addCookie(const QNetworkCookie &cookie) const; - -private Q_SLOTS: - // The private slots. - void saveCookie(); - void updateExpirationDateTimeState(const int &newState) const; - void updateUi() const; - -private: - // The private variables. - QLineEdit *domainLineEditPointer; - QCheckBox *expirationCheckBoxPointer; - QDateTimeEdit *expirationDateTimeEditPointer; - QCheckBox *httpOnlyCheckBoxPointer; - QLineEdit *nameLineEditPointer; - QLineEdit *pathLineEditPointer; - QPushButton *saveButtonPointer; - QCheckBox *secureCheckBoxPointer; - QLineEdit *valueLineEditPointer; -}; -#endif diff --git a/src/dialogs/AddOrEditCookieDialog.cpp b/src/dialogs/AddOrEditCookieDialog.cpp new file mode 100644 index 0000000..1b0a944 --- /dev/null +++ b/src/dialogs/AddOrEditCookieDialog.cpp @@ -0,0 +1,163 @@ +/* + * 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 "AddOrEditCookieDialog.h" +#include "ui_AddOrEditCookieDialog.h" + +// KDE Framework headers. +#include + +// Qt toolkit header. +#include +#include + +// Define the public static constants. +const int AddOrEditCookieDialog::AddCookie = 0; +const int AddOrEditCookieDialog::EditCookie = 1; + +AddOrEditCookieDialog::AddOrEditCookieDialog(const int &dialogType, const QNetworkCookie *cookiePointer) : QDialog(nullptr) +{ + // Set the dialog window title according to the dialog type. + if (dialogType == AddCookie) + setWindowTitle(i18nc("The add cookie dialog window title.", "Add Cookie")); + else + setWindowTitle(i18nc("The edit cookie dialog window title.", "Edit Cookie")); + + // Populate the is edit dialog boolean. + isEditDialog = (dialogType == EditCookie); + + // Set the window modality. + setWindowModality(Qt::WindowModality::ApplicationModal); + + // Instantiate the cookie settings dialog UI. + Ui::AddOrEditCookieDialog addOrEditCookieDialogUi; + + // Setup the UI. + addOrEditCookieDialogUi.setupUi(this); + + // Get handles for the widgets. + domainLineEditPointer = addOrEditCookieDialogUi.domainLineEdit; + nameLineEditPointer = addOrEditCookieDialogUi.nameLineEdit; + expirationCheckBoxPointer = addOrEditCookieDialogUi.expirationCheckBox; + expirationDateTimeEditPointer = addOrEditCookieDialogUi.expirationDateTimeEdit; + pathLineEditPointer = addOrEditCookieDialogUi.pathLineEdit; + httpOnlyCheckBoxPointer = addOrEditCookieDialogUi.httpOnlyCheckBox; + secureCheckBoxPointer = addOrEditCookieDialogUi.secureCheckBox; + valueLineEditPointer = addOrEditCookieDialogUi.valueLineEdit; + QDialogButtonBox *dialogButtonBoxPointer = addOrEditCookieDialogUi.dialogButtonBox; + saveButtonPointer = dialogButtonBoxPointer->button(QDialogButtonBox::Save); + + // Populate the dialog if editing a cookie. + if (isEditDialog) + { + // Store the old cookie. + oldCookie = *cookiePointer; + + // Populate the widgets. + domainLineEditPointer->setText(oldCookie.domain()); + nameLineEditPointer->setText(oldCookie.name()); + pathLineEditPointer->setText(oldCookie.path()); + httpOnlyCheckBoxPointer->setChecked(oldCookie.isHttpOnly()); + secureCheckBoxPointer->setChecked(oldCookie.isSecure()); + valueLineEditPointer->setText(oldCookie.value()); + + // Populate the expiration date if it exists. + if (!oldCookie.isSessionCookie()) + { + // Check the expiration box. + expirationCheckBoxPointer->setChecked(true); + + // Enable the expiration date time edit. + expirationDateTimeEditPointer->setEnabled(true); + + // Set the expiration date. + expirationDateTimeEditPointer->setDateTime(oldCookie.expirationDate()); + } + } + + // Connect the line edits. + connect(domainLineEditPointer, SIGNAL(textEdited(QString)), this, SLOT(updateUi())); + connect(nameLineEditPointer, SIGNAL(textEdited(QString)), this, SLOT(updateUi())); + connect(pathLineEditPointer, SIGNAL(textEdited(QString)), this, SLOT(updateUi())); + connect(valueLineEditPointer, SIGNAL(textEdited(QString)), this, SLOT(updateUi())); + + // Connect the check boxes. + connect(expirationCheckBoxPointer, SIGNAL(stateChanged(int)), this, SLOT(updateExpirationDateTimeState(int))); + + // Connect the buttons. + connect(dialogButtonBoxPointer, SIGNAL(accepted()), this, SLOT(saveCookie())); + connect(dialogButtonBoxPointer, SIGNAL(rejected()), this, SLOT(reject())); + + // Update the UI. + updateUi(); +} + +void AddOrEditCookieDialog::saveCookie() +{ + // Delete the old cookie if this is an edit dialog. + if (isEditDialog) + emit deleteCookie(oldCookie); + + // Create the variables. + QNetworkCookie cookie; + + // Populate the cookie. + cookie.setDomain(domainLineEditPointer->text()); + cookie.setName(nameLineEditPointer->text().toUtf8()); + cookie.setPath(pathLineEditPointer->text()); + cookie.setHttpOnly(httpOnlyCheckBoxPointer->isChecked()); + cookie.setSecure(secureCheckBoxPointer->isChecked()); + cookie.setValue(valueLineEditPointer->text().toUtf8()); + + // Populate the expiration date if it is specified. + if (expirationCheckBoxPointer->isChecked()) cookie.setExpirationDate(expirationDateTimeEditPointer->dateTime()); + + // Add the cookie. + emit addCookie(cookie); + + // Close the dialog. + reject(); +} + +void AddOrEditCookieDialog::updateExpirationDateTimeState(const int &newState) const +{ + // Update the state of the of the expiration date time edit. + switch (newState) + { + case Qt::Unchecked: + // Disable the expiration date time. + expirationDateTimeEditPointer->setEnabled(false); + + break; + + case Qt::Checked: + // Enable the expiration date time edit. + expirationDateTimeEditPointer->setEnabled(true); + + break; + } +} + +void AddOrEditCookieDialog::updateUi() const +{ + // Update the state of the save button based on all the required fields containing text. + saveButtonPointer->setDisabled(domainLineEditPointer->text().isEmpty() || nameLineEditPointer->text().isEmpty() || pathLineEditPointer->text().isEmpty() || + valueLineEditPointer->text().isEmpty()); +} diff --git a/src/dialogs/AddOrEditCookieDialog.h b/src/dialogs/AddOrEditCookieDialog.h new file mode 100644 index 0000000..5bc763f --- /dev/null +++ b/src/dialogs/AddOrEditCookieDialog.h @@ -0,0 +1,69 @@ +/* + * 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 ADDOREDITCOOKIEDIALOG_H +#define ADDOREDITCOOKIEDIALOG_H + +// Qt toolkit headers. +#include +#include +#include +#include + +class AddOrEditCookieDialog : public QDialog +{ + // Include the Q_OBJECT macro. + Q_OBJECT + +public: + // The primary constructor. + explicit AddOrEditCookieDialog(const int &dialogType, const QNetworkCookie *cookiePointer = nullptr); + + // The public static constants. + static const int AddCookie; + static const int EditCookie; + +signals: + // The signals. + void addCookie(const QNetworkCookie &cookie) const; + void deleteCookie(const QNetworkCookie &cookie) const; + +private Q_SLOTS: + // The private slots. + void saveCookie(); + void updateExpirationDateTimeState(const int &newState) const; + void updateUi() const; + +private: + // The private widgets. + QLineEdit *domainLineEditPointer; + QCheckBox *expirationCheckBoxPointer; + QDateTimeEdit *expirationDateTimeEditPointer; + QCheckBox *httpOnlyCheckBoxPointer; + QLineEdit *nameLineEditPointer; + QLineEdit *pathLineEditPointer; + QPushButton *saveButtonPointer; + QCheckBox *secureCheckBoxPointer; + QLineEdit *valueLineEditPointer; + + // The private variables. + QNetworkCookie oldCookie; + bool isEditDialog; +}; +#endif diff --git a/src/dialogs/CMakeLists.txt b/src/dialogs/CMakeLists.txt index 5eaf0f4..55f66fd 100644 --- a/src/dialogs/CMakeLists.txt +++ b/src/dialogs/CMakeLists.txt @@ -18,7 +18,7 @@ # List the sources to include in the executable. target_sources(privacy-browser PRIVATE - AddCookieDialog.cpp + AddOrEditCookieDialog.cpp CookiesDialog.cpp DomainSettingsDialog.cpp ) diff --git a/src/dialogs/CookiesDialog.cpp b/src/dialogs/CookiesDialog.cpp index 972bc67..5763cb9 100644 --- a/src/dialogs/CookiesDialog.cpp +++ b/src/dialogs/CookiesDialog.cpp @@ -18,9 +18,8 @@ */ // Application headers. -#include "AddCookieDialog.h" +#include "AddOrEditCookieDialog.h" #include "CookiesDialog.h" -#include "ui_CookieDisplayWidget.h" #include "ui_CookiesDialog.h" // KDE Frameworks headers. @@ -29,9 +28,78 @@ // Qt toolkit headers. #include #include +#include #include -CookiesDialog::CookiesDialog(QList *originalCookieListPointer) : QDialog(nullptr), cookieListPointer(originalCookieListPointer) +bool cookieSortPredicate(const QNetworkCookie &leftHandCookie, const QNetworkCookie &rightHandCookie) +{ + // Check to see if the domains are identical. + if (leftHandCookie.domain() == rightHandCookie.domain()) + { + // Check to see if the names are identical. + if (leftHandCookie.name() == rightHandCookie.name()) + { + // Sort the cookies by the path. + return (leftHandCookie.path() < rightHandCookie.path()); + } + else // The name are not identical. + { + // Sort the cookies by the name. + return (leftHandCookie.name() < rightHandCookie.name()); + } + } + else // The domains are not identical. + { + // Get copies of the domains. + QString leftHandDomain = leftHandCookie.domain(); + QString rightHandDomain = rightHandCookie.domain(); + + // Get the top level domains. + QString leftHandTopLevelDomain = leftHandDomain.section('.', -1); + QString rightHandTopLevelDomain = rightHandDomain.section('.', -1); + + // Get the second level domains. + QString leftHandSecondLevelDomain = leftHandDomain.section('.', -2); + QString rightHandSecondLevelDomain = rightHandDomain.section('.', -2); + + // Get the third level domains. + QString leftHandThirdLevelDomain = leftHandDomain.section('.', -3); + QString rightHandThirdLevelDomain = rightHandDomain.section('.', -3); + + // Check to see if the top level domains are the same. + if (leftHandTopLevelDomain == rightHandTopLevelDomain) + { + // Check to see if the second level domains are the same. + if (leftHandSecondLevelDomain == rightHandSecondLevelDomain) + { + // Check to see if the third level domains are the same. + if (leftHandThirdLevelDomain == rightHandThirdLevelDomain) + { + // Sort the cookies by the full domain because they share the same third level domain. + return (leftHandDomain < rightHandDomain); + } + else // The second level domains are the same, but the third level domains are different. + { + // Sort the cookies by the third level domains. + return (leftHandThirdLevelDomain < rightHandThirdLevelDomain); + } + } + else // The top level domains are the same, but the second level domains are diferent. + { + // Sort the cookies by the second level domain. + return (leftHandSecondLevelDomain < rightHandSecondLevelDomain); + } + } + else // The top level domains are different. + { + // Sort the cookies by the top level domain. + return (leftHandTopLevelDomain < rightHandTopLevelDomain); + } + + } +} + +CookiesDialog::CookiesDialog(std::forward_list *originalCookieListPointer) : QDialog(nullptr), cookieListPointer(originalCookieListPointer) { // Set the dialog window title. setWindowTitle(i18nc("The cookies dialog window title", "Cookies")); @@ -45,39 +113,147 @@ CookiesDialog::CookiesDialog(QList *originalCookieListPointer) : // Setup the UI. cookiesDialogUi.setupUi(this); - // Get a handle for the scroll area. - QScrollArea *scrollAreaPointer = cookiesDialogUi.scrollArea; + // Get a handle for the tree view. + treeViewPointer = cookiesDialogUi.treeView; - // Create the scroll area widget. - QWidget *scrollAreaWidgetPointer = new QWidget(); + // Initialize the standard item model. + standardItemModelPointer = new QStandardItemModel(); - // Set the scroll area widget. - scrollAreaPointer->setWidget(scrollAreaWidgetPointer); + // Set the column count. + standardItemModelPointer->setColumnCount(6); - // Create a scroll area VBox layout. - QVBoxLayout *scrollAreaVBoxLayoutPointer = new QVBoxLayout(); + // Set the header data. + standardItemModelPointer->setHeaderData(0, Qt::Horizontal, i18nc("The cookie Name header.", "Name")); + standardItemModelPointer->setHeaderData(1, Qt::Horizontal, i18nc("The cookie Path header.", "Path")); + standardItemModelPointer->setHeaderData(2, Qt::Horizontal, i18nc("The cookie Expiration Date header.", "Expiration Date")); + standardItemModelPointer->setHeaderData(3, Qt::Horizontal, i18nc("The cookie HTTP Only header.", "HTTP Only")); + standardItemModelPointer->setHeaderData(4, Qt::Horizontal, i18nc("The cookie Secure header.", "Secure")); + standardItemModelPointer->setHeaderData(5, Qt::Horizontal, i18nc("The cookie Value header.", "Value")); - // Set the scroll area widget layout. - scrollAreaWidgetPointer->setLayout(scrollAreaVBoxLayoutPointer); + // Set the header tool tips. + standardItemModelPointer->horizontalHeaderItem(0)->setToolTip(i18nc("The cookie Name tool tip.", + "The name identifies the cookie. Each cookie has a unique combination of domain, name, and path.")); + standardItemModelPointer->horizontalHeaderItem(1)->setToolTip(i18nc("The cookie Path tool tip.", "Websites can restrict cookie access to subpath of their URL.")); + standardItemModelPointer->horizontalHeaderItem(2)->setToolTip(i18nc("The cookie Expiration Date tool tip.", + "Cookies without an expiration date are known as session cookies and are expected to be deleted every time the browser closes.")); + standardItemModelPointer->horizontalHeaderItem(3)->setToolTip(i18nc("The cookie HTTP Only tool tip.", + "Restrict cookie access to HTTP (and HTTPS). This prevents JavaScript from accessing the cookie, which hardens it against cross-site scripting attacks.")); + standardItemModelPointer->horizontalHeaderItem(4)->setToolTip(i18nc("The cookie Secure tool tip.", "Only allow the cookie to be transferred across HTTPS (as opposed to HTTP).")); + standardItemModelPointer->horizontalHeaderItem(5)->setToolTip(i18nc("The cookie Value tool tip.", "The value contains the cookie data.")); - // Create the cookies VBox layout. - cookiesVBoxLayoutPointer = new QVBoxLayout(); + // Sort the cookie list. + cookieListPointer->sort(cookieSortPredicate); - // Populate the scroll area VBox layout. The stretch prevents the cookies from expanding vertically if they are smaller than the dialog. - scrollAreaVBoxLayoutPointer->addLayout(cookiesVBoxLayoutPointer); - scrollAreaVBoxLayoutPointer->addStretch(); + // Create the current domain string. + QString currentDomainString = ""; + + // Create the current domain standard item pointer. + QStandardItem *currentDomainStandardItemPointer; // Populate the VBoxLayout. for (QNetworkCookie cookie : *cookieListPointer) { - // Add the cookie to the layout. - addCookieToLayout(cookie); + // Get the cookie domain. + QString cookieDomain = cookie.domain(); + + // Check to see if the cookie is a member of the current domain. + if (cookieDomain != currentDomainString) // Create a new domain in the tree. + { + // Create a list for the domain standard items. + QList domainStandardItemList; + + // Create the new domain standard items. + QStandardItem *domainStandardItemPointer = new QStandardItem(cookieDomain); + QStandardItem *pathStandardItemPointer = new QStandardItem(QStringLiteral("")); + QStandardItem *expirationDateStandardItemPointer = new QStandardItem(QStringLiteral("")); + QStandardItem *isHttpOnlyStandardItemPointer = new QStandardItem(QStringLiteral("")); + QStandardItem *isSecureStandardItemPointer = new QStandardItem(QStringLiteral("")); + QStandardItem *valueStandardItemPointer = new QStandardItem(QStringLiteral("")); + + // Disable editing of the domain. + domainStandardItemPointer->setEditable(false); + pathStandardItemPointer->setEditable(false); + expirationDateStandardItemPointer->setEditable(false); + isHttpOnlyStandardItemPointer->setEditable(false); + isSecureStandardItemPointer->setEditable(false); + valueStandardItemPointer->setEditable(false); + + // Populate the domain standard item list. + domainStandardItemList.append(domainStandardItemPointer); + domainStandardItemList.append(pathStandardItemPointer); + domainStandardItemList.append(expirationDateStandardItemPointer); + domainStandardItemList.append(isHttpOnlyStandardItemPointer); + domainStandardItemList.append(isSecureStandardItemPointer); + domainStandardItemList.append(valueStandardItemPointer); + + // Add the domain to the tree. + standardItemModelPointer->invisibleRootItem()->appendRow(domainStandardItemList); + + // Update the current domain string. + currentDomainString = cookieDomain; + + // Update the current domain standard item pointer. + currentDomainStandardItemPointer = domainStandardItemPointer; + } + + // Create a list for the cookie standard items. + QList cookieStandardItemList; + + // Create the cookie standard items. + QStandardItem *nameStandardItemPointer = new QStandardItem(QString(cookie.name())); + QStandardItem *pathStandardItemPointer = new QStandardItem(QString(cookie.path())); + QStandardItem *expirationDateStandardItemPointer = new QStandardItem(QString(cookie.expirationDate().toString())); + QStandardItem *isHttpOnlyStandardItemPointer = new QStandardItem(QString(cookie.isHttpOnly() ? i18n("yes") : i18n("no"))); + QStandardItem *isSecureStandardItemPointer = new QStandardItem(QString(cookie.isSecure() ? i18n("yes") : i18n("no"))); + QStandardItem *valueStandardItemPointer = new QStandardItem(QString(cookie.value())); + + // Disable editing of the cookie standard items. + nameStandardItemPointer->setEditable(false); + pathStandardItemPointer->setEditable(false); + expirationDateStandardItemPointer->setEditable(false); + isHttpOnlyStandardItemPointer->setEditable(false); + isSecureStandardItemPointer->setEditable(false); + valueStandardItemPointer->setEditable(false); + + // Populate the cookie standard item list. + cookieStandardItemList.append(nameStandardItemPointer); + cookieStandardItemList.append(pathStandardItemPointer); + cookieStandardItemList.append(expirationDateStandardItemPointer); + cookieStandardItemList.append(isHttpOnlyStandardItemPointer); + cookieStandardItemList.append(isSecureStandardItemPointer); + cookieStandardItemList.append(valueStandardItemPointer); + + // Add the cookie to the tree. + currentDomainStandardItemPointer->appendRow(cookieStandardItemList); } + // Auto resize the headers. + treeViewPointer->header()->setSectionResizeMode(QHeaderView::ResizeToContents); + + // Disable stretching the last section. Otherwise, the Value field will be truncated to the width of the window when a row is expanded. + treeViewPointer->header()->setStretchLastSection(false); + + // Don't elide the Value field (or any other field). + treeViewPointer->setTextElideMode(Qt::ElideNone); + + // Indicate that all the rows are the same height, wich improves performance. + treeViewPointer->setUniformRowHeights(true); + + // Set the tree view model. + treeViewPointer->setModel(standardItemModelPointer); + + // Get a handle for the tree view selection model. + treeViewSelectionModelPointer = treeViewPointer->selectionModel(); + + // Listen for selection changes. + connect(treeViewSelectionModelPointer, SIGNAL(selectionChanged(QItemSelection, QItemSelection)), this, SLOT(updateUi())); + // Get handles for the buttons. addCookieButtonPointer = cookiesDialogUi.addCookieButton; + editCookieButtonPointer = cookiesDialogUi.editCookieButton; + deleteCookieButtonPointer = cookiesDialogUi.deleteCookieButton; QDialogButtonBox *dialogButtonBoxPointer = cookiesDialogUi.dialogButtonBox; - QPushButton *cancelButtonPointer = dialogButtonBoxPointer->button(QDialogButtonBox::Close); + QPushButton *closeButtonPointer = dialogButtonBoxPointer->button(QDialogButtonBox::Close); // Add a delete all button to the dialog button box. deleteAllButtonPointer = dialogButtonBoxPointer->addButton(i18nc("Delete all cookies button", "Delete all"), QDialogButtonBox::ActionRole); @@ -86,12 +262,35 @@ CookiesDialog::CookiesDialog(QList *originalCookieListPointer) : deleteAllButtonPointer->setIcon(QIcon::fromTheme("delete")); // Connect the buttons. - connect(addCookieButtonPointer, SIGNAL(clicked()), this, SLOT(showAddCookieMessageBox())); + connect(addCookieButtonPointer, SIGNAL(clicked()), this, SLOT(showAddCookieDialog())); + connect(editCookieButtonPointer, SIGNAL(clicked()), this, SLOT(showEditCookieDialog())); + connect(deleteCookieButtonPointer, SIGNAL(clicked()), this, SLOT(showDeleteCookieMessageBox())); connect(deleteAllButtonPointer, SIGNAL(clicked()), this, SLOT(showDeleteAllMessageBox())); connect(dialogButtonBoxPointer, SIGNAL(rejected()), this, SLOT(reject())); // Set the cancel button to be the default. - cancelButtonPointer->setDefault(true); + closeButtonPointer->setDefault(true); + + // Create the keyboard shortcuts. + QShortcut *aShortcutPointer = new QShortcut(QKeySequence(i18nc("The add cookie key shortcut.", "a")), this); + QShortcut *eShortcutPointer = new QShortcut(QKeySequence(i18nc("The edit cookie key shorcut.", "e")), this); + QShortcut *dShortcutPointer = new QShortcut(QKeySequence(i18nc("The delete cookie key shortcut.", "d")), this); + QShortcut *deleteShortcutPointer = new QShortcut(QKeySequence::Delete, this); + QShortcut *lShortcutPointer = new QShortcut(QKeySequence(i18nc("The delete all key shortcut.", "l")), this); + QShortcut *cShortcutPointer = new QShortcut(QKeySequence(i18nc("The close key shortcut.", "c")), this); + QShortcut *quitShortcutPointer = new QShortcut(QKeySequence::Quit, this); + + // Connect the keyboard shortcuts to the buttons. + connect(aShortcutPointer, SIGNAL(activated()), addCookieButtonPointer, SLOT(click())); + connect(eShortcutPointer, SIGNAL(activated()), editCookieButtonPointer, SLOT(click())); + connect(dShortcutPointer, SIGNAL(activated()), deleteCookieButtonPointer, SLOT(click())); + connect(deleteShortcutPointer, SIGNAL(activated()), deleteCookieButtonPointer, SLOT(click())); + connect(lShortcutPointer, SIGNAL(activated()), deleteAllButtonPointer, SLOT(click())); + connect(cShortcutPointer, SIGNAL(activated()), closeButtonPointer, SLOT(click())); + connect(quitShortcutPointer, SIGNAL(activated()), closeButtonPointer, SLOT(click())); + + // Edit a cookie when it is double clicked. + connect(treeViewPointer, SIGNAL(doubleClicked(QModelIndex)), editCookieButtonPointer, SLOT(click())); // Update the UI. updateUi(); @@ -102,57 +301,218 @@ void CookiesDialog::addCookieFromDialog(const QNetworkCookie &cookie) const // Add the cookie to the cookie list and the cookie store. emit addCookie(cookie); - // Add the cookie to the VBox layout. - addCookieToLayout(cookie); + // Get the new domain string. + QString newDomain = cookie.domain(); + + // Check to see if the domain already exists in the model. + QList currentDomainStandardItemList = standardItemModelPointer->findItems(newDomain); + + // Create a domain standard item pointer. + QStandardItem *domainStandardItemPointer; + + // Prepare the domain standard item pointer. + if (currentDomainStandardItemList.isEmpty()) // The domain doesn't currently exist in the tree. + { + // Create a list for the domain standard items. + QList domainStandardItemList; + + // Create the new domain standard items. + domainStandardItemPointer = new QStandardItem(newDomain); + QStandardItem *pathStandardItemPointer = new QStandardItem(QStringLiteral("")); + QStandardItem *expirationDateStandardItemPointer = new QStandardItem(QStringLiteral("")); + QStandardItem *isHttpOnlyStandardItemPointer = new QStandardItem(QStringLiteral("")); + QStandardItem *isSecureStandardItemPointer = new QStandardItem(QStringLiteral("")); + QStandardItem *valueStandardItemPointer = new QStandardItem(QStringLiteral("")); + + // Disable editing of the domain. + domainStandardItemPointer->setEditable(false); + pathStandardItemPointer->setEditable(false); + expirationDateStandardItemPointer->setEditable(false); + isHttpOnlyStandardItemPointer->setEditable(false); + isSecureStandardItemPointer->setEditable(false); + valueStandardItemPointer->setEditable(false); + + // Populate the domain standard item list. + domainStandardItemList.append(domainStandardItemPointer); + domainStandardItemList.append(pathStandardItemPointer); + domainStandardItemList.append(expirationDateStandardItemPointer); + domainStandardItemList.append(isHttpOnlyStandardItemPointer); + domainStandardItemList.append(isSecureStandardItemPointer); + domainStandardItemList.append(valueStandardItemPointer); + + // Create the insert domain row number. + int insertDomainRowNumber = 0; + + // Get the number of domains in the tree. + int numberOfDomains = standardItemModelPointer->invisibleRootItem()->rowCount(); + + // Get the new domain strings. + QString newDomainTopLevelDomain = newDomain.section('.', -1); + QString newDomainSecondLevelDomain = newDomain.section('.', -2); + QString newDomainThirdLevelDomain = newDomain.section('.', -3); + + // Iterate through all the domains. + for (int i = 0; i < numberOfDomains; ++i) + { + // Get the current domain strings. + QString currentDomain = standardItemModelPointer->invisibleRootItem()->child(i, 0)->index().data().toString(); + QString currentDomainTopLevelDomain = currentDomain.section('.', -1); + QString currentDomainSecondLevelDomain = currentDomain.section('.', -2); + QString currentDomainThirdLevelDomain = currentDomain.section('.', -3); + + // Check to see if the new domain should be inserted after the current domain. + if (newDomainTopLevelDomain > currentDomainTopLevelDomain) + { + // Insert the new domain after the current domain. + insertDomainRowNumber = i + 1; + } + else if ((newDomainTopLevelDomain == currentDomainTopLevelDomain) && (newDomainSecondLevelDomain > currentDomainSecondLevelDomain)) + { + // Insert the new domain after the current domain. + insertDomainRowNumber = i + 1; + } + else if ((newDomainSecondLevelDomain == currentDomainSecondLevelDomain) && (newDomainThirdLevelDomain > currentDomainThirdLevelDomain)) + { + // Insert the new domain after the current domain. + insertDomainRowNumber = i + 1; + } + else if ((newDomainThirdLevelDomain == currentDomainThirdLevelDomain) && (newDomain > currentDomain)) + { + // Insert the new domain after the current domain. + insertDomainRowNumber = i + 1; + } + } + + // Add the domain to the tree. + standardItemModelPointer->invisibleRootItem()->insertRow(insertDomainRowNumber, domainStandardItemList); + } + else // The domain already exists in the tree. + { + // Use the current domain standard item. + domainStandardItemPointer = currentDomainStandardItemList[0]; + } + + // Get strings for the new cookie name and path (used later in the placement of the row). + QString newCookieName = QString(cookie.name()); + QString newCookiePath = QString(cookie.path()); + + // Create a list for the cookie standard items. + QList cookieStandardItemList; + + // Create the cookie standard items. + QStandardItem *nameStandardItemPointer = new QStandardItem(newCookieName); + QStandardItem *pathStandardItemPointer = new QStandardItem(newCookiePath); + QStandardItem *expirationDateStandardItemPointer = new QStandardItem(QString(cookie.expirationDate().toString())); + QStandardItem *isHttpOnlyStandardItemPointer = new QStandardItem(QString(cookie.isHttpOnly() ? i18n("yes") : i18n("no"))); + QStandardItem *isSecureStandardItemPointer = new QStandardItem(QString(cookie.isSecure() ? i18n("yes") : i18n("no"))); + QStandardItem *valueStandardItemPointer = new QStandardItem(QString(cookie.value())); + + // Disable editing of the cookie standard items. + nameStandardItemPointer->setEditable(false); + pathStandardItemPointer->setEditable(false); + expirationDateStandardItemPointer->setEditable(false); + isHttpOnlyStandardItemPointer->setEditable(false); + isSecureStandardItemPointer->setEditable(false); + valueStandardItemPointer->setEditable(false); + + // Populate the cookie standard item list. + cookieStandardItemList.append(nameStandardItemPointer); + cookieStandardItemList.append(pathStandardItemPointer); + cookieStandardItemList.append(expirationDateStandardItemPointer); + cookieStandardItemList.append(isHttpOnlyStandardItemPointer); + cookieStandardItemList.append(isSecureStandardItemPointer); + cookieStandardItemList.append(valueStandardItemPointer); + + // Create the insert cookie row number. + int insertCookieRowNumber = 0; + + // Get the number of cookies in the domain. + int numberOfCookies = domainStandardItemPointer->rowCount(); + + // Iterate through the cookies for this domain. + for (int i = 0; i < numberOfCookies; ++i) + { + // Get the current cookie name and path at the indicated row. + QString currentCookieName = domainStandardItemPointer->child(i, 0)->index().data().toString(); + QString currentCookiePath = domainStandardItemPointer->child(i, 1)->index().data().toString(); + + // Check to see if the new cookie should be inserted after the current cookie. + if (newCookieName > currentCookieName) + { + // Insert the new cookie after the current cookie. + insertCookieRowNumber = i + 1; + } + else if ((newCookieName == currentCookieName) && (newCookiePath > currentCookiePath)) + { + // Insert the new cookie after the current cookie. + insertCookieRowNumber = i + 1; + } + } + + // Add the cookie to the tree. + domainStandardItemPointer->insertRow(insertCookieRowNumber, cookieStandardItemList); + + // Get the new cookie model index. + QModelIndex newCookieIndex = nameStandardItemPointer->index(); + + // Set the new cookie to be the current index. + treeViewPointer->setCurrentIndex(newCookieIndex); + + // Expand the parent of the new cookie. + treeViewPointer->expand(newCookieIndex.parent()); } -void CookiesDialog::addCookieToLayout(const QNetworkCookie &cookie) const +void CookiesDialog::deleteCookie(const QModelIndex &modelIndex) const { - // Create a cookie display widget. - QWidget *cookieDisplayWidgetPointer = new QWidget(); + // Create a partial cookie. + QNetworkCookie partialCookie; - // Instantiate the cookie widget dialog UI. - Ui::CookieDisplayWidget cookieDisplayWidgetUi; + // Populate the partial cookie from the current model index. + partialCookie.setDomain(modelIndex.parent().siblingAtColumn(0).data().toString()); + partialCookie.setName(modelIndex.siblingAtColumn(0).data().toString().toUtf8()); + partialCookie.setPath(modelIndex.siblingAtColumn(1).data().toString()); - // Setup the UI. - cookieDisplayWidgetUi.setupUi(cookieDisplayWidgetPointer); - - // Get handles for the views. - QLabel *domainLabelPointer = cookieDisplayWidgetUi.domainLabel; - QLabel *nameLabelPointer = cookieDisplayWidgetUi.nameLabel; - QLabel *expirationDateLabelPointer = cookieDisplayWidgetUi.expirationDateLabel; - QLabel *pathLabelPointer = cookieDisplayWidgetUi.pathLabel; - QCheckBox *httpOnlyCheckBoxPointer = cookieDisplayWidgetUi.httpOnlyCheckBox; - QCheckBox *secureCheckBoxPointer = cookieDisplayWidgetUi.secureCheckBox; - QLabel *valueLabelPointer = cookieDisplayWidgetUi.valueLabel; - - // Populate the views. - domainLabelPointer->setText("" + cookie.domain() + ""); - nameLabelPointer->setText("" + cookie.name() + ""); - expirationDateLabelPointer->setText("" + cookie.expirationDate().toString() + ""); - pathLabelPointer->setText("" + cookie.path() + ""); - httpOnlyCheckBoxPointer->setChecked(cookie.isHttpOnly()); - secureCheckBoxPointer->setChecked(cookie.isSecure()); - valueLabelPointer->setText("" + cookie.value() + ""); - - // Add the cookie display widget to the cookies VBox layout. - cookiesVBoxLayoutPointer->addWidget(cookieDisplayWidgetPointer); - - // Create a line. - QFrame *lineFrame = new QFrame(); - - // Format the line. - lineFrame->setFrameShape(QFrame::HLine); - lineFrame->setFrameShadow(QFrame::Sunken); - - // Add the line to the cookies VBox layout. - cookiesVBoxLayoutPointer->addWidget(lineFrame); + // Create a cookie to delete. + QNetworkCookie cookieToDelete; + + // Search for the partial cookie in the cookie list. + for (QNetworkCookie cookie : *cookieListPointer) + { + // Store the cookie to delete if it has the same identifier as the partial cookie. + if (cookie.hasSameIdentifier(partialCookie)) + cookieToDelete = cookie; + } + + // Remove the cookie from the tree view. + standardItemModelPointer->removeRow(modelIndex.row(), modelIndex.parent()); + + // Delete the cookie from the cookie list and cookie store. + emit deleteCookie(cookieToDelete); } -void CookiesDialog::showAddCookieMessageBox() const +void CookiesDialog::deleteCookieFromDialog(const QNetworkCookie &cookie) const +{ + // Get the current model index. + QModelIndex currentIndex = treeViewSelectionModelPointer->currentIndex(); + + // Get the parent index. + QModelIndex parentIndex = currentIndex.parent(); + + // Remove the cookie from the tree view. + standardItemModelPointer->removeRow(currentIndex.row(), parentIndex); + + // Remove the domain from the tree view if its only cookie has been deleted. + if (standardItemModelPointer->rowCount(parentIndex) == 0) + standardItemModelPointer->removeRow(parentIndex.row(), parentIndex.parent()); + + // Delete the cookie from the cookie list and cookie store. + emit deleteCookie(cookie); +} + +void CookiesDialog::showAddCookieDialog() const { // Instantiate an add cookie dialog. - QDialog *addCookieDialogPointer = new AddCookieDialog(); + QDialog *addCookieDialogPointer = new AddOrEditCookieDialog(AddOrEditCookieDialog::AddCookie); // Show the dialog. addCookieDialogPointer->show(); @@ -190,29 +550,153 @@ void CookiesDialog::showDeleteAllMessageBox() const // Delete all the cookies. emit deleteAllCookies(); - // Clear the cookie list. - cookieListPointer->clear(); + // Clear the standard item model. + standardItemModelPointer->clear(); + + // Update the UI. + updateUi(); + } +} + +void CookiesDialog::showDeleteCookieMessageBox() const +{ + // Get the current model index. + QModelIndex currentIndex = treeViewSelectionModelPointer->currentIndex(); + + // Get the parent model index. + QModelIndex parentIndex = currentIndex.parent(); + + // Determine if a domain is selected. + bool isDomain = standardItemModelPointer->hasChildren(currentIndex); - // Create a layout item pointer. - QLayoutItem *layoutItemPointer; + // Instantiate a delete cookie message box. + QMessageBox deleteCookieMessageBox; - // Delete each cookie widget. - while ((layoutItemPointer = cookiesVBoxLayoutPointer->takeAt(0)) != nullptr) + // Set the icon. + deleteCookieMessageBox.setIcon(QMessageBox::Warning); + + if (isDomain) + { + // Get the number of cookies. + int numberOfCookiesToDelete = standardItemModelPointer->rowCount(currentIndex); + + // Set the window title. + deleteCookieMessageBox.setWindowTitle(i18nc("Delete cookies dialog title", "Delete %1 Cookies", numberOfCookiesToDelete)); + + // Set the text. + deleteCookieMessageBox.setText(i18nc("Delete cookies dialog text", "Delete %1 cookies?", numberOfCookiesToDelete)); + } + else + { + // Set the window title. + deleteCookieMessageBox.setWindowTitle(i18nc("Delete cookie dialog title", "Delete 1 Cookie")); + + // Set the text. + deleteCookieMessageBox.setText(i18nc("Delete cookie dialog text", "Delete 1 cookie?")); + } + + // Set the standard buttons. + deleteCookieMessageBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No); + + // Set the default button. + deleteCookieMessageBox.setDefaultButton(QMessageBox::No); + + // Display the dialog and capture the return value. + int returnValue = deleteCookieMessageBox.exec(); + + // Delete the cookie if instructed. + if (returnValue == QMessageBox::Yes) + { + // Delete the cookies according to the selection. + if (isDomain) // A domain is selected. + { + // Get the number of cookies. + int numberOfCookies = standardItemModelPointer->rowCount(currentIndex); + + // Delete each child cookie. + for (int i = 0; i < numberOfCookies; ++i) + { + // Delete the cookie for the first child. Once this is deleted, the second child will become the first child. + deleteCookie(standardItemModelPointer->index(0, 0, currentIndex)); + } + + // Delete the domain from the tree view. + standardItemModelPointer->removeRow(currentIndex.row(), parentIndex); + } + else // A single cookie is selected. { - // Delete the widget. - delete layoutItemPointer->widget(); + // Delete the cookie. + deleteCookie(currentIndex); - // Delete the layout. - delete layoutItemPointer; + // Remove the domain row if its only cookie has been deleted. + if (standardItemModelPointer->rowCount(parentIndex) == 0) + standardItemModelPointer->removeRow(parentIndex.row(), parentIndex.parent()); } + } +} - // Update the UI. - updateUi(); +void CookiesDialog::showEditCookieDialog() const +{ + // Get the current model index. + QModelIndex currentIndex = treeViewSelectionModelPointer->currentIndex(); + + // Create a partial cookie. + QNetworkCookie partialCookie; + + // Populate the partial cookie from the current model index. + partialCookie.setDomain(currentIndex.parent().siblingAtColumn(0).data().toString()); + partialCookie.setName(currentIndex.siblingAtColumn(0).data().toString().toUtf8()); + partialCookie.setPath(currentIndex.siblingAtColumn(1).data().toString()); + + // Create a cookie to edit. + QNetworkCookie cookieToEdit; + + // Search for the partial cookie in the cookie list. + for (QNetworkCookie cookie : *cookieListPointer) + { + // Store the cookie to edit if it has the same identifier as the partial cookie. + if (cookie.hasSameIdentifier(partialCookie)) + cookieToEdit = cookie; } + + // Instantiate an edit cookie dialog. + QDialog *editCookieDialogPointer = new AddOrEditCookieDialog(AddOrEditCookieDialog::EditCookie, &cookieToEdit); + + // Show the dialog. + editCookieDialogPointer->show(); + + // Process cookie events. + connect(editCookieDialogPointer, SIGNAL(addCookie(QNetworkCookie)), this, SLOT(addCookieFromDialog(QNetworkCookie))); + connect(editCookieDialogPointer, SIGNAL(deleteCookie(QNetworkCookie)), this, SLOT(deleteCookieFromDialog(QNetworkCookie))); } void CookiesDialog::updateUi() const { + // Get the current index of the first column. + QModelIndex currentIndex = treeViewSelectionModelPointer->currentIndex().siblingAtColumn(0); + // Set the status of the buttons. - deleteAllButtonPointer->setEnabled(cookieListPointer->count() > 0); + editCookieButtonPointer->setEnabled(treeViewSelectionModelPointer->hasSelection() && !standardItemModelPointer->hasChildren(currentIndex)); + deleteCookieButtonPointer->setEnabled(treeViewSelectionModelPointer->hasSelection());; + deleteAllButtonPointer->setEnabled(standardItemModelPointer->hasChildren(standardItemModelPointer->invisibleRootItem()->index())); + + // Update the delete cookie button text. + if (deleteCookieButtonPointer->isEnabled()) // The button is enabled. + { + if (standardItemModelPointer->hasChildren(currentIndex)) // A domain is selected. + { + // Update the button text. + deleteCookieButtonPointer->setText(i18nc("Delete cookies button.", "&Delete %1 cookies", standardItemModelPointer->rowCount(currentIndex))); + } + else // A single cookie is selected. + { + // Update the button text. + deleteCookieButtonPointer->setText(i18nc("Delete cookies button.", "&Delete 1 cookie")); + } + } + else // The button is disabled. + { + // Reset the button text. + deleteCookieButtonPointer->setText(i18nc("Delete cookie button.", "&Delete cookie")); + } } diff --git a/src/dialogs/CookiesDialog.h b/src/dialogs/CookiesDialog.h index 89f9c62..770a846 100644 --- a/src/dialogs/CookiesDialog.h +++ b/src/dialogs/CookiesDialog.h @@ -22,8 +22,13 @@ // Qt toolkit headers. #include +#include #include -#include +#include +#include + +// C++ headers. +#include class CookiesDialog : public QDialog { @@ -32,28 +37,36 @@ class CookiesDialog : public QDialog public: // The primary constructor. - explicit CookiesDialog(QList *cookieListPointer); + explicit CookiesDialog(std::forward_list *cookieListPointer); signals: // The signals. void addCookie(const QNetworkCookie &cookie) const; void deleteAllCookies() const; + void deleteCookie(const QNetworkCookie &cookie) const; private Q_SLOTS: // The private slots. void addCookieFromDialog(const QNetworkCookie &cookie) const; - void showAddCookieMessageBox() const; + void deleteCookieFromDialog(const QNetworkCookie &cookie) const; + void showAddCookieDialog() const; void showDeleteAllMessageBox() const; + void showDeleteCookieMessageBox() const; + void showEditCookieDialog() const; + void updateUi() const; private: // The private variables. + QItemSelectionModel *treeViewSelectionModelPointer; QPushButton *addCookieButtonPointer; - QList *cookieListPointer; - QVBoxLayout *cookiesVBoxLayoutPointer; + std::forward_list *cookieListPointer; QPushButton *deleteAllButtonPointer; + QPushButton *deleteCookieButtonPointer; + QPushButton *editCookieButtonPointer; + QStandardItemModel *standardItemModelPointer; + QTreeView *treeViewPointer; // The private functions. - void addCookieToLayout(const QNetworkCookie &cookie) const; - void updateUi() const; + void deleteCookie(const QModelIndex &modelIndex) const; }; #endif diff --git a/src/helpers/UserAgentHelper.h b/src/helpers/UserAgentHelper.h index 839a5ed..5c8286a 100644 --- a/src/helpers/UserAgentHelper.h +++ b/src/helpers/UserAgentHelper.h @@ -29,7 +29,7 @@ public: // The default constructor. UserAgentHelper(); - // The public constants. + // The public static constants. static const QString CHROMIUM_LINUX_DATABASE; static const QString CHROMIUM_LINUX_TRANSLATED; static const QString CHROMIUM_LINUX_USER_AGENT; @@ -56,7 +56,7 @@ public: static const QString WEB_ENGINE_DEFAULT_DATABASE; static const QString WEB_ENGINE_DEFAULT_TRANSLATED; - // The public functions. + // The public static functions. static QString getDatabaseUserAgentNameFromTranslatedName(const QString &translatedUserAgentName); static int getDomainSettingsUserAgentIndex(const QString &userAgentName); static QString getUserAgentFromDatabaseName(const QString &userAgentDatabaseName); diff --git a/src/ui/AddCookieDialog.ui b/src/ui/AddCookieDialog.ui deleted file mode 100644 index 413224a..0000000 --- a/src/ui/AddCookieDialog.ui +++ /dev/null @@ -1,292 +0,0 @@ - - - - - - AddCookieDialog - - - - - - - Qt::AlignLeft - - - - - - - Cookies prepended by a period are accessible to all subdomains. - - - - Qt::RichText - - - - Domain&nbsp; - - - - - - - - Cookies prepended by a period are accessible to all subdomains. - - - - - - - - - The identifier of the cookie, which is unique when combined with the domain and the path. - - - - Qt::RichText - - - - &nbsp;&nbsp;&nbsp;&nbsp;Name&nbsp; - - - - - - - - The identifier of the cookie, which is unique when combined with the domain and the path. - - - - - - - - - - Qt::AlignLeft - - - - - - - Cookies without an expiration date are known as session cookies and are expected to be deleted every time the browser closes. - - - - - - - - Cookies without an expiration date are known as session cookies and are expected to be deleted every time the browser closes. - - - - Qt::RichText - - - - Expiration date&nbsp; - - - - - - - - Cookies without an expiration date are known as session cookies and are expected to be deleted every time the browser closes. - - - - - 2030 - 1 - 1 - 0 - 0 - 0 - - - - - true - - - - false - - - - - - - - - Websites can restrict cookie access to subpath of their URL. - - - - Qt::RichText - - - - &nbsp;&nbsp;&nbsp;&nbsp;Path&nbsp; - - - - - - - - Websites can restrict cookie access to subpath of their URL. - - - - / - - - - - - - - - Qt::RichText - - - - &nbsp;&nbsp;&nbsp;&nbsp; - - - - - - - - - Restrict cookie access to HTTP (and HTTPS). This prevents JavaScript from accessing the cookie, which hardens it against cross-site scripting attacks. - - - - true - - - - - - - - Restrict cookie access to HTTP (and HTTPS). This prevents JavaScript from accessing the cookie, which hardens it against cross-site scripting attacks. - - - - Qt::RichText - - - - HTTP only&nbsp;&nbsp;&nbsp;&nbsp; - - - - - - - - - Only allow the cookie to be transferred across HTTPS (as opposed to HTTP). - - - - true - - - - - - - - Only allow the cookie to be transferred across HTTPS (as opposed to HTTP). - - - - Secure - - - - - - - - - - Qt::AlignLeft - - - - - - - The value contains the cookie data. - - - - Qt::RichText - - - - Value&nbsp; - - - - - - - - The value contains the cookie data. - - - - - - - - - - - Qt::Vertical - - - - - - - - - QDialogButtonBox::Save | QDialogButtonBox::Cancel - - - - - - diff --git a/src/ui/AddOrEditCookieDialog.ui b/src/ui/AddOrEditCookieDialog.ui new file mode 100644 index 0000000..adeaa4e --- /dev/null +++ b/src/ui/AddOrEditCookieDialog.ui @@ -0,0 +1,303 @@ + + + + + + AddOrEditCookieDialog + + + + + + + + Qt::AlignLeft + + + + + + + Cookies prepended by a period are accessible to all subdomains. + + + + Domain + + + + + + + + + + + + Qt::RichText + + + + + &nbsp;&nbsp;&nbsp; + + + + + + + + + The name identifies the cookie. Each cookie has a unique combination of domain, name, and path. + + + + Name + + + + + + + + + + + + + + + Qt::AlignLeft + + + + + + + Websites can restrict cookie access to subpath of their URL. + + + + Path + + + + + + + + / + + + + + + + + Qt::RichText + + + + + &nbsp;&nbsp;&nbsp; + + + + + + + + + Cookies without an expiration date are known as session cookies and are expected to be deleted every time the browser closes. + + + + + + + + Cookies without an expiration date are known as session cookies and are expected to be deleted every time the browser closes. + + + + Expiration date + + + + + + + + Qt::RichText + + + + + &nbsp; + + + + + + + + + 2030 + 1 + 1 + 0 + 0 + 0 + + + + + true + + + + false + + + + + + + + Qt::RichText + + + + + &nbsp;&nbsp;&nbsp; + + + + + + + + + Restrict cookie access to HTTP (and HTTPS). This prevents JavaScript from accessing the cookie, which hardens it against cross-site scripting attacks. + + + + true + + + + + + + + Restrict cookie access to HTTP (and HTTPS). This prevents JavaScript from accessing the cookie, which hardens it against cross-site scripting attacks. + + + + HTTP only + + + + + + + + Qt::RichText + + + + + &nbsp;&nbsp;&nbsp; + + + + + + + + + Only allow the cookie to be transferred across HTTPS (as opposed to HTTP). + + + + true + + + + + + + + Only allow the cookie to be transferred across HTTPS (as opposed to HTTP). + + + + Secure + + + + + + + + + + + Qt::AlignLeft + + + + + + + The value contains the cookie data. + + + + Value + + + + + + + + + + + + + + + Qt::Vertical + + + + + + + + + QDialogButtonBox::Save | QDialogButtonBox::Cancel + + + + + + diff --git a/src/ui/CookieDisplayWidget.ui b/src/ui/CookieDisplayWidget.ui deleted file mode 100644 index 21665ff..0000000 --- a/src/ui/CookieDisplayWidget.ui +++ /dev/null @@ -1,287 +0,0 @@ - - - - - - CookieDisplayWidget - - - - - - - Qt::AlignLeft - - - - - - - Cookies prepended by a period are accessible to all subdomains. - - - - Qt::RichText - - - - <font size="+1">Domain:&nbsp;</font> - - - - - - - - Cookies prepended by a period are accessible to all subdomains. - - - - Qt::RichText - - - - Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse - - - - - - - - - The identifier of the cookie, which is unique when combined with the domain and the path. - - - - Qt::RichText - - - - <font size="+1">&nbsp;&nbsp;&nbsp;&nbsp;Name:&nbsp;</font> - - - - - - - - The identifier of the cookie, which is unique when combined with the domain and the path. - - - - Qt::RichText - - - - Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse - - - - - - - - - - Qt::AlignLeft - - - - - - - Cookies without an expiration date are known as session cookies and are expected to be deleted every time the browser closes. - - - - Qt::RichText - - - - Expiration date:&nbsp; - - - - - - - - Cookies without an expiration date are known as session cookies and are expected to be deleted every time the browser closes. - - - - Qt::RichText - - - - Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse - - - - - - - - - Websites can restrict cookie access to a subpath of their URL. - - - - Qt::RichText - - - - &nbsp;&nbsp;&nbsp;&nbsp;Path:&nbsp; - - - - - - - - Websites can restrict cookie access to a subpath of their URL. - - - - Qt::RichText - - - - Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse - - - - - - - - - Qt::RichText - - - - &nbsp;&nbsp;&nbsp;&nbsp; - - - - - - - - - Restrict cookie access to HTTP (and HTTPS). This prevents JavaScript from accessing the cookie, which hardens it against cross-site scripting attacks. - - - - false - - - - - - - - Restrict cookie access to HTTP (and HTTPS). This prevents JavaScript from accessing the cookie, which hardens it against cross-site scripting attacks. - - - - Qt::RichText - - - - HTTP only&nbsp;&nbsp;&nbsp;&nbsp; - - - - - - - - - Only allow the cookie to be transferred across HTTPS (as opposed to HTTP). - - - - false - - - - - - - - Only allow the cookie to be transferred across HTTPS (as opposed to HTTP). - - - - Secure - - - - - - - - - - Qt::AlignLeft - - - - - - - The value contains the cookie data. - - - - Qt::RichText - - - - Value:&nbsp; - - - - - - - - The value contains the cookie data. - - - - true - - - - Qt::RichText - - - - Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse - - - - - - - - diff --git a/src/ui/CookiesDialog.ui b/src/ui/CookiesDialog.ui index 6816c50..9c3c2dc 100644 --- a/src/ui/CookiesDialog.ui +++ b/src/ui/CookiesDialog.ui @@ -35,11 +35,7 @@ - - - true - - + @@ -48,7 +44,7 @@ - Add cookie + &Add cookie @@ -59,9 +55,9 @@ - + - Edit cookie + &Edit cookie @@ -72,9 +68,9 @@ - + - Delete cookie + De&lete cookie diff --git a/src/views/BrowserView.cpp b/src/views/BrowserView.cpp index b5c915b..82e7e3e 100644 --- a/src/views/BrowserView.cpp +++ b/src/views/BrowserView.cpp @@ -59,8 +59,9 @@ BrowserView::BrowserView(QWidget *parent) : QWidget(parent) webEngineSettingsPointer = webEngineViewPointer->settings(); webEngineCookieStorePointer = webEngineProfilePointer->cookieStore(); - // Store a copy of each cookie when it is added. + // Process cookie changes. connect(webEngineCookieStorePointer, SIGNAL(cookieAdded(QNetworkCookie)), this, SLOT(cookieAdded(QNetworkCookie))); + connect(webEngineCookieStorePointer, SIGNAL(cookieRemoved(QNetworkCookie)), this, SLOT(cookieRemoved(QNetworkCookie))); // Store a copy of the WebEngine default user agent. webEngineDefaultUserAgent = webEngineProfilePointer->httpUserAgent(); @@ -338,12 +339,24 @@ void BrowserView::cookieAdded(const QNetworkCookie &cookie) const emit addCookie(cookie); } +void BrowserView::cookieRemoved(const QNetworkCookie &cookie) const +{ + // Remove the cookie from the cookie list. + emit removeCookie(cookie); +} + void BrowserView::deleteAllCookies() const { // Delete all the cookies. webEngineCookieStorePointer->deleteAllCookies(); } +void BrowserView::deleteCookieFromStore(const QNetworkCookie &cookie) const +{ + // Delete the cookie. + webEngineCookieStorePointer->deleteCookie(cookie); +} + void BrowserView::forward() const { // Go forward. diff --git a/src/views/BrowserView.h b/src/views/BrowserView.h index fb60f03..ae1c06a 100644 --- a/src/views/BrowserView.h +++ b/src/views/BrowserView.h @@ -54,6 +54,7 @@ public: signals: // The signals. void addCookie(const QNetworkCookie &cookie) const; + void removeCookie(const QNetworkCookie &cookie) const; void clearUrlLineEditFocus() const; void hideProgressBar() const; void linkHovered(const QString &linkUrl) const; @@ -78,6 +79,7 @@ public Q_SLOTS: void applyOnTheFlyUserAgent(QAction *userAgentActionPointer) const; void back() const; void deleteAllCookies() const; + void deleteCookieFromStore(const QNetworkCookie &cookie) const; void forward() const; void home() const; void loadUrlFromLineEdit(QString url) const; @@ -88,6 +90,7 @@ public Q_SLOTS: private Q_SLOTS: // The private slots. void cookieAdded(const QNetworkCookie &cookie) const; + void cookieRemoved(const QNetworkCookie &cookie) const; void loadFinished() const; void loadProgress(const int &progress) const; void loadStarted() const; diff --git a/src/windows/BrowserWindow.cpp b/src/windows/BrowserWindow.cpp index c70bab1..2cfc251 100644 --- a/src/windows/BrowserWindow.cpp +++ b/src/windows/BrowserWindow.cpp @@ -238,10 +238,11 @@ BrowserWindow::BrowserWindow() : KXmlGuiWindow() connect(browserViewPointer, SIGNAL(updateDomainSettingsIndicator(bool, QString)), this, SLOT(updateDomainSettingsIndicator(bool, QString))); // Initialize the cookie list. - cookieListPointer = new QList; + cookieListPointer = new std::forward_list; - // Add new cookies to the list. + // Process cookie changes. connect(browserViewPointer, SIGNAL(addCookie(QNetworkCookie)), this, SLOT(addCookieToList(QNetworkCookie))); + connect(browserViewPointer, SIGNAL(removeCookie(QNetworkCookie)), this, SLOT(removeCookieFromList(QNetworkCookie))); // Load the initial website. browserViewPointer->loadInitialWebsite(); @@ -249,15 +250,9 @@ BrowserWindow::BrowserWindow() : KXmlGuiWindow() void BrowserWindow::addCookieToList(const QNetworkCookie &newCookie) const { - // Check to see if the list already contains a cookie with this ID. - for (QNetworkCookie existingCookie : *cookieListPointer) - { - // Remove the old version of the cookie. - if (existingCookie.hasSameIdentifier(newCookie)) cookieListPointer->removeOne(existingCookie); - } - + qDebug() << "Add cookie: " << newCookie.toRawForm(); // Add the new cookie to the list. - cookieListPointer->append(newCookie); + cookieListPointer->push_front(newCookie); } void BrowserWindow::addOrEditDomainSettings() const @@ -379,6 +374,7 @@ void BrowserWindow::openCookiesDialog() // Connect the dialog signals. connect(cookiesDialogPointer, SIGNAL(addCookie(QNetworkCookie)), browserViewPointer, SLOT(addCookieToStore(QNetworkCookie))); connect(cookiesDialogPointer, SIGNAL(deleteAllCookies()), browserViewPointer, SLOT(deleteAllCookies())); + connect(cookiesDialogPointer, SIGNAL(deleteCookie(QNetworkCookie)), browserViewPointer, SLOT(deleteCookieFromStore(QNetworkCookie))); } void BrowserWindow::openDomainSettings() const @@ -405,6 +401,14 @@ void BrowserWindow::refresh() const browserViewPointer->refresh(); } +void BrowserWindow::removeCookieFromList(const QNetworkCookie &cookie) const +{ + qDebug() << "Remove cookie: " << cookie.toRawForm(); + + // Remove the cookie from the list. + cookieListPointer->remove(cookie); +} + void BrowserWindow::showProgressBar(const int &progress) const { // Set the progress bar value. diff --git a/src/windows/BrowserWindow.h b/src/windows/BrowserWindow.h index 016879b..a35e060 100644 --- a/src/windows/BrowserWindow.h +++ b/src/windows/BrowserWindow.h @@ -31,6 +31,9 @@ #include #include +// C++ headers. +#include + class BrowserWindow : public KXmlGuiWindow { // Include the Q_OBJECT macro. @@ -57,6 +60,7 @@ private Q_SLOTS: void openCookiesDialog(); void openDomainSettings() const; void refresh() const; + void removeCookieFromList(const QNetworkCookie &cookie) const; void settingsConfigure(); void showProgressBar(const int &progress) const; void toggleJavaScript() const; @@ -75,7 +79,7 @@ private: // The private variables. BrowserView *browserViewPointer; KConfigDialog *configDialogPointer; - QList *cookieListPointer; + std::forward_list *cookieListPointer; QString currentDomainSettingsDomain; QUrl currentUrl; double currentZoomFactor;