Change the cookie implementation to a QTreeView.
authorSoren Stoutner <soren@stoutner.com>
Thu, 12 May 2022 20:46:50 +0000 (13:46 -0700)
committerSoren Stoutner <soren@stoutner.com>
Thu, 12 May 2022 20:46:50 +0000 (13:46 -0700)
17 files changed:
src/CMakeLists.txt
src/dialogs/AddCookieDialog.cpp [deleted file]
src/dialogs/AddCookieDialog.h [deleted file]
src/dialogs/AddOrEditCookieDialog.cpp [new file with mode: 0644]
src/dialogs/AddOrEditCookieDialog.h [new file with mode: 0644]
src/dialogs/CMakeLists.txt
src/dialogs/CookiesDialog.cpp
src/dialogs/CookiesDialog.h
src/helpers/UserAgentHelper.h
src/ui/AddCookieDialog.ui [deleted file]
src/ui/AddOrEditCookieDialog.ui [new file with mode: 0644]
src/ui/CookieDisplayWidget.ui [deleted file]
src/ui/CookiesDialog.ui
src/views/BrowserView.cpp
src/views/BrowserView.h
src/windows/BrowserWindow.cpp
src/windows/BrowserWindow.h

index a7ee5784d2135a53689ce1fbbd2d85cdae6800af..9729c95e3151f9ec34180c0006d0c02ac5f60be2 100644 (file)
@@ -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 (file)
index a81d433..0000000
+++ /dev/null
@@ -1,121 +0,0 @@
-/*
- * Copyright © 2022 Soren Stoutner <soren@stoutner.com>.
- *
- * This file is part of Privacy Browser PC <https://www.stoutner.com/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 <http://www.gnu.org/licenses/>.
- */
-
-// Application headers.
-#include "AddCookieDialog.h"
-#include "ui_AddCookieDialog.h"
-
-// KDE Framework headers.
-#include <KLocalizedString>
-
-// Qt toolkit header.
-#include <QPushButton>
-#include <QUrl>
-
-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 (file)
index f7354ce..0000000
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * Copyright © 2022 Soren Stoutner <soren@stoutner.com>.
- *
- * This file is part of Privacy Browser PC <https://www.stoutner.com/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 <http://www.gnu.org/licenses/>.
- */
-
-#ifndef ADDCOOKIEDIALOG_H
-#define ADDCOOKIEDIALOG_H
-
-// Qt toolkit headers.
-#include <QCheckBox>
-#include <QDateTimeEdit>
-#include <QDialog>
-#include <QNetworkCookie>
-
-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 (file)
index 0000000..1b0a944
--- /dev/null
@@ -0,0 +1,163 @@
+/*
+ * Copyright © 2022 Soren Stoutner <soren@stoutner.com>.
+ *
+ * This file is part of Privacy Browser PC <https://www.stoutner.com/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 <http://www.gnu.org/licenses/>.
+ */
+
+// Application headers.
+#include "AddOrEditCookieDialog.h"
+#include "ui_AddOrEditCookieDialog.h"
+
+// KDE Framework headers.
+#include <KLocalizedString>
+
+// Qt toolkit header.
+#include <QPushButton>
+#include <QUrl>
+
+// 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 (file)
index 0000000..5bc763f
--- /dev/null
@@ -0,0 +1,69 @@
+/*
+ * Copyright © 2022 Soren Stoutner <soren@stoutner.com>.
+ *
+ * This file is part of Privacy Browser PC <https://www.stoutner.com/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 <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef ADDOREDITCOOKIEDIALOG_H
+#define ADDOREDITCOOKIEDIALOG_H
+
+// Qt toolkit headers.
+#include <QCheckBox>
+#include <QDateTimeEdit>
+#include <QDialog>
+#include <QNetworkCookie>
+
+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
index 5eaf0f46f53235a67f0643c78aec0ecb1f5a70e5..55f66fdf226a8463dd4621ea2123cfe6dcf46cd2 100644 (file)
@@ -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
 )
index 972bc67d1d49b132a9fb4634f9631ff22804012c..5763cb9c2ead6d6c68d8939e5b726bbb10cdc825 100644 (file)
@@ -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.
 // Qt toolkit headers.
 #include <QDateTime>
 #include <QMessageBox>
+#include <QShortcut>
 #include <QUrl>
 
-CookiesDialog::CookiesDialog(QList<QNetworkCookie> *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<QNetworkCookie> *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<QNetworkCookie> *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<QStandardItem*> 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<QStandardItem*> 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<QNetworkCookie> *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<QStandardItem*> 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<QStandardItem*> 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<QStandardItem*> 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("<font size=\"+1\"><b>" + cookie.domain() + "</b></font>");
-    nameLabelPointer->setText("<font size=\"+1\"><b>" + cookie.name() + "</b></font>");
-    expirationDateLabelPointer->setText("<b>" + cookie.expirationDate().toString() + "</b>");
-    pathLabelPointer->setText("<b>" + cookie.path() + "</b>");
-    httpOnlyCheckBoxPointer->setChecked(cookie.isHttpOnly());
-    secureCheckBoxPointer->setChecked(cookie.isSecure());
-    valueLabelPointer->setText("<b>" + cookie.value() + "</b>");
-
-    // 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"));
+    }
 }
index 89f9c623b9f46e4b6037820589f274a6945252b4..770a84681bfcd7b3c5bd9af7ce6203f1bafa8da3 100644 (file)
 
 // Qt toolkit headers.
 #include <QDialog>
+#include <QItemSelectionModel>
 #include <QNetworkCookie>
-#include <QVBoxLayout>
+#include <QStandardItemModel>
+#include <QTreeView>
+
+// C++ headers.
+#include <forward_list>
 
 class CookiesDialog : public QDialog
 {
@@ -32,28 +37,36 @@ class CookiesDialog : public QDialog
 
 public:
     // The primary constructor.
-    explicit CookiesDialog(QList<QNetworkCookie> *cookieListPointer);
+    explicit CookiesDialog(std::forward_list<QNetworkCookie> *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<QNetworkCookie> *cookieListPointer;
-    QVBoxLayout *cookiesVBoxLayoutPointer;
+    std::forward_list<QNetworkCookie> *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
index 839a5ed917f8dc2414719e9ead9e5cb033a55d98..5c8286afdedccbf87577b6e309140a081e74f297 100644 (file)
@@ -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 (file)
index 413224a..0000000
+++ /dev/null
@@ -1,292 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-
-<!--
-  Copyright © 2022 Soren Stoutner <soren@stoutner.com>.
-
-  This file is part of Privacy Browser PC <https://www.stoutner.com/privacy-browser-android>.
-
-  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 <http://www.gnu.org/licenses/>. -->
-
-<ui version="4.0">
-    <class>AddCookieDialog</class>
-
-    <widget class="QWidget">
-        <layout class="QVBoxLayout">
-            <item>
-                <layout class="QHBoxLayout">
-                    <property name="alignment">
-                        <enum>Qt::AlignLeft</enum>
-                    </property>
-
-                    <!-- Domain. -->
-                    <item>
-                        <widget class="QLabel">
-                            <property name="toolTip">
-                                <string>Cookies prepended by a period are accessible to all subdomains.</string>
-                            </property>
-
-                            <property name="textFormat">
-                                <enum>Qt::RichText</enum>
-                            </property>
-
-                            <property name="text">
-                                <string>Domain&amp;nbsp;</string>
-                            </property>
-                        </widget>
-                    </item>
-
-                    <item>
-                        <widget class="QLineEdit" name="domainLineEdit">
-                            <property name="toolTip">
-                                <string>Cookies prepended by a period are accessible to all subdomains.</string>
-                            </property>
-                        </widget>
-                    </item>
-
-                    <!-- Name. -->
-                    <item>
-                        <widget class="QLabel">
-                            <property name="toolTip">
-                                <string>The identifier of the cookie, which is unique when combined with the domain and the path.</string>
-                            </property>
-
-                            <property name="textFormat">
-                                <enum>Qt::RichText</enum>
-                            </property>
-
-                            <property name="text">
-                                <string>&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Name&amp;nbsp;</string>
-                            </property>
-                        </widget>
-                    </item>
-
-                    <item>
-                        <widget class="QLineEdit" name="nameLineEdit">
-                            <property name="toolTip">
-                                <string>The identifier of the cookie, which is unique when combined with the domain and the path.</string>
-                            </property>
-                        </widget>
-                    </item>
-                </layout>
-            </item>
-
-            <item>
-                <layout class="QHBoxLayout">
-                    <property name="alignment">
-                        <enum>Qt::AlignLeft</enum>
-                    </property>
-
-                    <!-- Expiration date. -->
-                    <item>
-                        <widget class="QCheckBox" name="expirationCheckBox">
-                            <property name="toolTip">
-                                <string>Cookies without an expiration date are known as session cookies and are expected to be deleted every time the browser closes.</string>
-                            </property>
-                        </widget>
-                    </item>
-
-                    <item>
-                        <widget class="QLabel">
-                            <property name="toolTip">
-                                <string>Cookies without an expiration date are known as session cookies and are expected to be deleted every time the browser closes.</string>
-                            </property>
-
-                            <property name="textFormat">
-                                <enum>Qt::RichText</enum>
-                            </property>
-
-                            <property name="text">
-                                <string>Expiration date&amp;nbsp;</string>
-                            </property>
-                        </widget>
-                    </item>
-
-                    <item>
-                        <widget class="QDateTimeEdit" name="expirationDateTimeEdit">
-                            <property name="toolTip">
-                                <string>Cookies without an expiration date are known as session cookies and are expected to be deleted every time the browser closes.</string>
-                            </property>
-
-                            <property name="dateTime">
-                                <datetime>
-                                    <year>2030</year>
-                                    <month>1</month>
-                                    <day>1</day>
-                                    <hour>0</hour>
-                                    <minute>0</minute>
-                                    <second>0</second>
-                                </datetime>
-                            </property>
-
-                            <property name="calendarPopup">
-                                <bool>true</bool>
-                            </property>
-
-                            <property name="enabled">
-                                <bool>false</bool>
-                            </property>
-                        </widget>
-                    </item>
-
-                    <!-- Path. -->
-                    <item>
-                        <widget class="QLabel">
-                            <property name="toolTip">
-                                <string>Websites can restrict cookie access to subpath of their URL.</string>
-                            </property>
-
-                            <property name="textFormat">
-                                <enum>Qt::RichText</enum>
-                            </property>
-
-                            <property name="text">
-                                <string>&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Path&amp;nbsp;</string>
-                            </property>
-                        </widget>
-                    </item>
-
-                    <item>
-                        <widget class="QLineEdit" name="pathLineEdit">
-                            <property name="toolTip">
-                                <string>Websites can restrict cookie access to subpath of their URL.</string>
-                            </property>
-
-                            <property name="text">
-                                <string>/</string>
-                            </property>
-                        </widget>
-                    </item>
-
-                    <!-- A spacer label.  Necessary to add space before the check box. -->
-                    <item>
-                        <widget class="QLabel">
-                            <property name="textFormat">
-                                <enum>Qt::RichText</enum>
-                            </property>
-
-                            <property name="text">
-                                <string>&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;</string>
-                            </property>
-                        </widget>
-                    </item>
-
-                    <!-- HTTP only. -->
-                    <item>
-                        <widget class="QCheckBox" name="httpOnlyCheckBox">
-                            <property name="toolTip">
-                                <string>Restrict cookie access to HTTP (and HTTPS). This prevents JavaScript from accessing the cookie, which hardens it against cross-site scripting attacks.</string>
-                            </property>
-
-                            <property name="checked">
-                                <bool>true</bool>
-                            </property>
-                        </widget>
-                    </item>
-
-                    <item>
-                        <widget class="QLabel">
-                            <property name="toolTip">
-                                <string>Restrict cookie access to HTTP (and HTTPS). This prevents JavaScript from accessing the cookie, which hardens it against cross-site scripting attacks.</string>
-                            </property>
-
-                            <property name="textFormat">
-                                <enum>Qt::RichText</enum>
-                            </property>
-
-                            <property name="text">
-                                <string>HTTP only&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;</string>
-                            </property>
-                        </widget>
-                    </item>
-
-                    <!-- Secure. -->
-                    <item>
-                        <widget class="QCheckBox" name="secureCheckBox">
-                            <property name="toolTip">
-                                <string>Only allow the cookie to be transferred across HTTPS (as opposed to HTTP).</string>
-                            </property>
-
-                            <property name="checked">
-                                <bool>true</bool>
-                            </property>
-                        </widget>
-                    </item>
-
-                    <item>
-                        <widget class="QLabel">
-                            <property name="toolTip">
-                                <string>Only allow the cookie to be transferred across HTTPS (as opposed to HTTP).</string>
-                            </property>
-
-                            <property name="text">
-                                <string>Secure</string>
-                            </property>
-                        </widget>
-                    </item>
-                </layout>
-            </item>
-
-            <item>
-                <layout class="QHBoxLayout">
-                    <property name="alignment">
-                        <enum>Qt::AlignLeft</enum>
-                    </property>
-
-                    <!-- Value. -->
-                    <item>
-                        <widget class="QLabel">
-                            <property name="toolTip">
-                                <string>The value contains the cookie data.</string>
-                            </property>
-
-                            <property name="textFormat">
-                                <enum>Qt::RichText</enum>
-                            </property>
-
-                            <property name="text">
-                                <string>Value&amp;nbsp;</string>
-                            </property>
-                        </widget>
-                    </item>
-
-                    <item>
-                        <widget class="QLineEdit" name="valueLineEdit">
-                            <property name="toolTip">
-                                <string>The value contains the cookie data.</string>
-                            </property>
-                        </widget>
-                    </item>
-                </layout>
-            </item>
-
-            <!-- Spacer. -->
-            <item>
-                <spacer>
-                    <property name="orientation">
-                        <enum>Qt::Vertical</enum>
-                    </property>
-                </spacer>
-            </item>
-
-            <!-- Dialog buttons. -->
-            <item>
-                <widget class="QDialogButtonBox" name="dialogButtonBox">
-                    <property name="standardButtons">
-                        <set>QDialogButtonBox::Save | QDialogButtonBox::Cancel</set>
-                    </property>
-                </widget>
-            </item>
-        </layout>
-    </widget>
-</ui>
diff --git a/src/ui/AddOrEditCookieDialog.ui b/src/ui/AddOrEditCookieDialog.ui
new file mode 100644 (file)
index 0000000..adeaa4e
--- /dev/null
@@ -0,0 +1,303 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+  Copyright © 2022 Soren Stoutner <soren@stoutner.com>.
+
+  This file is part of Privacy Browser PC <https://www.stoutner.com/privacy-browser-android>.
+
+  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 <http://www.gnu.org/licenses/>. -->
+
+<ui version="4.0">
+    <class>AddOrEditCookieDialog</class>
+
+    <widget class="QWidget">
+        <layout class="QVBoxLayout">
+            <item>
+                <!-- First row. -->
+                <layout class="QHBoxLayout">
+                    <property name="alignment">
+                        <enum>Qt::AlignLeft</enum>
+                    </property>
+
+                    <!-- Domain. -->
+                    <item>
+                        <widget class="QLabel">
+                            <property name="toolTip">
+                                <string>Cookies prepended by a period are accessible to all subdomains.</string>
+                            </property>
+
+                            <property name="text">
+                                <string>Domain</string>
+                            </property>
+                        </widget>
+                    </item>
+
+                    <item>
+                        <widget class="QLineEdit" name="domainLineEdit" />
+                    </item>
+
+                    <item>
+                        <widget class="QLabel">
+                            <property name="textFormat">
+                                <enum>Qt::RichText</enum>
+                            </property>
+
+                            <!-- Spacer label. -->
+                            <property name="text">
+                                <string>&amp;nbsp;&amp;nbsp;&amp;nbsp;</string>
+                            </property>
+                        </widget>
+                    </item>
+
+                    <!-- Name. -->
+                    <item>
+                        <widget class="QLabel">
+                            <property name="toolTip">
+                                <string>The name identifies the cookie.  Each cookie has a unique combination of domain, name, and path.</string>
+                            </property>
+
+                            <property name="text">
+                                <string>Name</string>
+                            </property>
+                        </widget>
+                    </item>
+
+                    <item>
+                        <widget class="QLineEdit" name="nameLineEdit" />
+                    </item>
+                </layout>
+            </item>
+
+            <!-- Second row. -->
+            <item>
+                <layout class="QHBoxLayout">
+                    <property name="alignment">
+                        <enum>Qt::AlignLeft</enum>
+                    </property>
+
+                    <!-- Path. -->
+                    <item>
+                        <widget class="QLabel">
+                            <property name="toolTip">
+                                <string>Websites can restrict cookie access to subpath of their URL.</string>
+                            </property>
+
+                            <property name="text">
+                                <string>Path</string>
+                            </property>
+                        </widget>
+                    </item>
+
+                    <item>
+                        <widget class="QLineEdit" name="pathLineEdit">
+                            <property name="text">
+                                <string>/</string>
+                            </property>
+                        </widget>
+                    </item>
+
+                    <item>
+                        <widget class="QLabel">
+                            <property name="textFormat">
+                                <enum>Qt::RichText</enum>
+                            </property>
+
+                            <!-- Spacer label. -->
+                            <property name="text">
+                                <string>&amp;nbsp;&amp;nbsp;&amp;nbsp;</string>
+                            </property>
+                        </widget>
+                    </item>
+
+                    <!-- Expiration date. -->
+                    <item>
+                        <widget class="QCheckBox" name="expirationCheckBox">
+                            <property name="toolTip">
+                                <string>Cookies without an expiration date are known as session cookies and are expected to be deleted every time the browser closes.</string>
+                            </property>
+                        </widget>
+                    </item>
+
+                    <item>
+                        <widget class="QLabel">
+                            <property name="toolTip">
+                                <string>Cookies without an expiration date are known as session cookies and are expected to be deleted every time the browser closes.</string>
+                            </property>
+
+                            <property name="text">
+                                <string>Expiration date</string>
+                            </property>
+                        </widget>
+                    </item>
+
+                    <item>
+                        <widget class="QLabel">
+                            <property name="textFormat">
+                                <enum>Qt::RichText</enum>
+                            </property>
+
+                            <!-- Spacer label. -->
+                            <property name="text">
+                                <string>&amp;nbsp;</string>
+                            </property>
+                        </widget>
+                    </item>
+
+                    <item>
+                        <widget class="QDateTimeEdit" name="expirationDateTimeEdit">
+                            <property name="dateTime">
+                                <datetime>
+                                    <year>2030</year>
+                                    <month>1</month>
+                                    <day>1</day>
+                                    <hour>0</hour>
+                                    <minute>0</minute>
+                                    <second>0</second>
+                                </datetime>
+                            </property>
+
+                            <property name="calendarPopup">
+                                <bool>true</bool>
+                            </property>
+
+                            <property name="enabled">
+                                <bool>false</bool>
+                            </property>
+                        </widget>
+                    </item>
+
+                    <item>
+                        <widget class="QLabel">
+                            <property name="textFormat">
+                                <enum>Qt::RichText</enum>
+                            </property>
+
+                            <!-- Spacer label. -->
+                            <property name="text">
+                                <string>&amp;nbsp;&amp;nbsp;&amp;nbsp;</string>
+                            </property>
+                        </widget>
+                    </item>
+
+                    <!-- HTTP only. -->
+                    <item>
+                        <widget class="QCheckBox" name="httpOnlyCheckBox">
+                            <property name="toolTip">
+                                <string>Restrict cookie access to HTTP (and HTTPS). This prevents JavaScript from accessing the cookie, which hardens it against cross-site scripting attacks.</string>
+                            </property>
+
+                            <property name="checked">
+                                <bool>true</bool>
+                            </property>
+                        </widget>
+                    </item>
+
+                    <item>
+                        <widget class="QLabel">
+                            <property name="toolTip">
+                                <string>Restrict cookie access to HTTP (and HTTPS). This prevents JavaScript from accessing the cookie, which hardens it against cross-site scripting attacks.</string>
+                            </property>
+
+                            <property name="text">
+                                <string>HTTP only</string>
+                            </property>
+                        </widget>
+                    </item>
+
+                    <item>
+                        <widget class="QLabel">
+                            <property name="textFormat">
+                                <enum>Qt::RichText</enum>
+                            </property>
+
+                            <!-- Spacer label. -->
+                            <property name="text">
+                                <string>&amp;nbsp;&amp;nbsp;&amp;nbsp;</string>
+                            </property>
+                        </widget>
+                    </item>
+
+                    <!-- Secure. -->
+                    <item>
+                        <widget class="QCheckBox" name="secureCheckBox">
+                            <property name="toolTip">
+                                <string>Only allow the cookie to be transferred across HTTPS (as opposed to HTTP).</string>
+                            </property>
+
+                            <property name="checked">
+                                <bool>true</bool>
+                            </property>
+                        </widget>
+                    </item>
+
+                    <item>
+                        <widget class="QLabel">
+                            <property name="toolTip">
+                                <string>Only allow the cookie to be transferred across HTTPS (as opposed to HTTP).</string>
+                            </property>
+
+                            <property name="text">
+                                <string>Secure</string>
+                            </property>
+                        </widget>
+                    </item>
+                </layout>
+            </item>
+
+            <!-- Third row. -->
+            <item>
+                <layout class="QHBoxLayout">
+                    <property name="alignment">
+                        <enum>Qt::AlignLeft</enum>
+                    </property>
+
+                    <!-- Value. -->
+                    <item>
+                        <widget class="QLabel">
+                            <property name="toolTip">
+                                <string>The value contains the cookie data.</string>
+                            </property>
+
+                            <property name="text">
+                                <string>Value</string>
+                            </property>
+                        </widget>
+                    </item>
+
+                    <item>
+                        <widget class="QLineEdit" name="valueLineEdit" />
+                    </item>
+                </layout>
+            </item>
+
+            <!-- Spacer. -->
+            <item>
+                <spacer>
+                    <property name="orientation">
+                        <enum>Qt::Vertical</enum>
+                    </property>
+                </spacer>
+            </item>
+
+            <!-- Dialog buttons. -->
+            <item>
+                <widget class="QDialogButtonBox" name="dialogButtonBox">
+                    <property name="standardButtons">
+                        <set>QDialogButtonBox::Save | QDialogButtonBox::Cancel</set>
+                    </property>
+                </widget>
+            </item>
+        </layout>
+    </widget>
+</ui>
diff --git a/src/ui/CookieDisplayWidget.ui b/src/ui/CookieDisplayWidget.ui
deleted file mode 100644 (file)
index 21665ff..0000000
+++ /dev/null
@@ -1,287 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-
-<!--
-  Copyright © 2022 Soren Stoutner <soren@stoutner.com>.
-
-  This file is part of Privacy Browser PC <https://www.stoutner.com/privacy-browser-android>.
-
-  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 <http://www.gnu.org/licenses/>. -->
-
-<ui version="4.0">
-    <class>CookieDisplayWidget</class>
-
-    <widget class="QWidget">
-        <layout class="QVBoxLayout">
-            <item>
-                <layout class="QHBoxLayout">
-                    <property name="alignment">
-                        <enum>Qt::AlignLeft</enum>
-                    </property>
-
-                    <!-- Domain. -->
-                    <item>
-                        <widget class="QLabel">
-                            <property name="toolTip">
-                                <string>Cookies prepended by a period are accessible to all subdomains.</string>
-                            </property>
-
-                            <property name="textFormat">
-                                <enum>Qt::RichText</enum>
-                            </property>
-
-                            <property name="text">
-                                <string>&lt;font size=&quot;+1&quot;&gt;Domain:&amp;nbsp;&lt;/font&gt;</string>
-                            </property>
-                        </widget>
-                    </item>
-
-                    <item>
-                        <widget class="QLabel" name="domainLabel">
-                            <property name="toolTip">
-                                <string>Cookies prepended by a period are accessible to all subdomains.</string>
-                            </property>
-
-                            <property name="textFormat">
-                                <enum>Qt::RichText</enum>
-                            </property>
-
-                            <property name="textInteractionFlags">
-                                <set>Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
-                            </property>
-                        </widget>
-                    </item>
-
-                    <!-- Name. -->
-                    <item>
-                        <widget class="QLabel">
-                            <property name="toolTip">
-                                <string>The identifier of the cookie, which is unique when combined with the domain and the path.</string>
-                            </property>
-
-                            <property name="textFormat">
-                                <enum>Qt::RichText</enum>
-                            </property>
-
-                            <property name="text">
-                                <string>&lt;font size=&quot;+1&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Name:&amp;nbsp;&lt;/font&gt;</string>
-                            </property>
-                        </widget>
-                    </item>
-
-                    <item>
-                        <widget class="QLabel" name="nameLabel">
-                            <property name="toolTip">
-                                <string>The identifier of the cookie, which is unique when combined with the domain and the path.</string>
-                            </property>
-
-                            <property name="textFormat">
-                                <enum>Qt::RichText</enum>
-                            </property>
-
-                            <property name="textInteractionFlags">
-                                <set>Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
-                            </property>
-                        </widget>
-                    </item>
-                </layout>
-            </item>
-
-            <item>
-                <layout class="QHBoxLayout">
-                    <property name="alignment">
-                        <enum>Qt::AlignLeft</enum>
-                    </property>
-
-                    <!-- Expiration date. -->
-                    <item>
-                        <widget class="QLabel">
-                            <property name="toolTip">
-                                <string>Cookies without an expiration date are known as session cookies and are expected to be deleted every time the browser closes.</string>
-                            </property>
-
-                            <property name="textFormat">
-                                <enum>Qt::RichText</enum>
-                            </property>
-
-                            <property name="text">
-                                <string>Expiration date:&amp;nbsp;</string>
-                            </property>
-                        </widget>
-                    </item>
-
-                    <item>
-                        <widget class="QLabel" name="expirationDateLabel">
-                            <property name="toolTip">
-                                <string>Cookies without an expiration date are known as session cookies and are expected to be deleted every time the browser closes.</string>
-                            </property>
-
-                            <property name="textFormat">
-                                <enum>Qt::RichText</enum>
-                            </property>
-
-                            <property name="textInteractionFlags">
-                                <set>Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
-                            </property>
-                        </widget>
-                    </item>
-
-                    <!-- Path. -->
-                    <item>
-                        <widget class="QLabel">
-                            <property name="toolTip">
-                                <string>Websites can restrict cookie access to a subpath of their URL.</string>
-                            </property>
-
-                            <property name="textFormat">
-                                <enum>Qt::RichText</enum>
-                            </property>
-
-                            <property name="text">
-                                <string>&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Path:&amp;nbsp;</string>
-                            </property>
-                        </widget>
-                    </item>
-
-                    <item>
-                        <widget class="QLabel" name="pathLabel">
-                            <property name="toolTip">
-                                <string>Websites can restrict cookie access to a subpath of their URL.</string>
-                            </property>
-
-                            <property name="textFormat">
-                                <enum>Qt::RichText</enum>
-                            </property>
-
-                            <property name="textInteractionFlags">
-                                <set>Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
-                            </property>
-                        </widget>
-                    </item>
-
-                    <!-- A spacer label.  Necessary to add space before the check box. -->
-                    <item>
-                        <widget class="QLabel">
-                            <property name="textFormat">
-                                <enum>Qt::RichText</enum>
-                            </property>
-
-                            <property name="text">
-                                <string>&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;</string>
-                            </property>
-                        </widget>
-                    </item>
-
-                    <!-- HTTP only. -->
-                    <item>
-                        <widget class="QCheckBox" name="httpOnlyCheckBox">
-                            <property name="toolTip">
-                                <string>Restrict cookie access to HTTP (and HTTPS). This prevents JavaScript from accessing the cookie, which hardens it against cross-site scripting attacks.</string>
-                            </property>
-
-                            <property name="enabled">
-                                <bool>false</bool>
-                            </property>
-                        </widget>
-                    </item>
-
-                    <item>
-                        <widget class="QLabel">
-                            <property name="toolTip">
-                                <string>Restrict cookie access to HTTP (and HTTPS). This prevents JavaScript from accessing the cookie, which hardens it against cross-site scripting attacks.</string>
-                            </property>
-
-                            <property name="textFormat">
-                                <enum>Qt::RichText</enum>
-                            </property>
-
-                            <property name="text">
-                                <string>HTTP only&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;</string>
-                            </property>
-                        </widget>
-                    </item>
-
-                    <!-- Secure. -->
-                    <item>
-                        <widget class="QCheckBox" name="secureCheckBox">
-                            <property name="toolTip">
-                                <string>Only allow the cookie to be transferred across HTTPS (as opposed to HTTP).</string>
-                            </property>
-
-                            <property name="enabled">
-                                <bool>false</bool>
-                            </property>
-                        </widget>
-                    </item>
-
-                    <item>
-                        <widget class="QLabel">
-                            <property name="toolTip">
-                                <string>Only allow the cookie to be transferred across HTTPS (as opposed to HTTP).</string>
-                            </property>
-
-                            <property name="text">
-                                <string>Secure</string>
-                            </property>
-                        </widget>
-                    </item>
-                </layout>
-            </item>
-
-            <item>
-                <layout class="QHBoxLayout">
-                    <property name="alignment">
-                        <enum>Qt::AlignLeft</enum>
-                    </property>
-
-                    <!-- Value. -->
-                    <item>
-                        <widget class="QLabel">
-                            <property name="toolTip">
-                                <string>The value contains the cookie data.</string>
-                            </property>
-
-                            <property name="textFormat">
-                                <enum>Qt::RichText</enum>
-                            </property>
-
-                            <property name="text">
-                                <string>Value:&amp;nbsp;</string>
-                            </property>
-                        </widget>
-                    </item>
-
-                    <item>
-                        <widget class="QLabel" name="valueLabel">
-                            <property name="toolTip">
-                                <string>The value contains the cookie data.</string>
-                            </property>
-
-                            <property name="wordWrap">
-                                <bool>true</bool>
-                            </property>
-
-                            <property name="textFormat">
-                                <enum>Qt::RichText</enum>
-                            </property>
-
-                            <property name="textInteractionFlags">
-                                <set>Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
-                            </property>
-                        </widget>
-                    </item>
-                </layout>
-            </item>
-        </layout>
-    </widget>
-</ui>
index 6816c50cac86f5052cdce347c50fd6d516b74db2..9c3c2dcda9c0092d5366ff8ea506f26a43183e23 100644 (file)
         <layout class="QVBoxLayout">
             <item>
                 <!-- Scroll area. -->
-                <widget class="QScrollArea" name="scrollArea">
-                    <property name="widgetResizable">
-                        <bool>true</bool>
-                    </property>
-                </widget>
+                <widget class="QTreeView" name="treeView" />
             </item>
 
             <item>
@@ -48,7 +44,7 @@
                     <item>
                         <widget class="QPushButton" name="addCookieButton">
                             <property name="text">
-                                <string>Add cookie</string>
+                                <string>&amp;Add cookie</string>
                             </property>
 
                             <property name="icon">
@@ -59,9 +55,9 @@
 
                     <!-- Edit cookie button. -->
                     <item>
-                        <widget class="QPushButton" name="editDomainButton">
+                        <widget class="QPushButton" name="editCookieButton">
                             <property name="text">
-                                <string>Edit cookie</string>
+                                <string>&amp;Edit cookie</string>
                             </property>
 
                             <property name="icon">
@@ -72,9 +68,9 @@
 
                     <!-- Delete domain button. -->
                     <item>
-                        <widget class="QPushButton" name="deleteDomainButton">
+                        <widget class="QPushButton" name="deleteCookieButton">
                             <property name="text">
-                                <string>Delete cookie</string>
+                                <string>De&amp;lete cookie</string>
                             </property>
 
                             <property name="icon">
index b5c915bc1b1e9e3556b3b5c8501dd5643a372d20..82e7e3ee0d0614d4e32e78e8984a2d6da3a8f59c 100644 (file)
@@ -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.
index fb60f038cd46f0f5fd3073874beadfe4c252c9fb..ae1c06ae3bc0b997f2168877eec9368ba6f8db07 100644 (file)
@@ -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;
index c70bab18cbffbc5539d43b12b858eb2fb051eac2..2cfc251151648d13fcdd54d1eb2b4a135ccb84c0 100644 (file)
@@ -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<QNetworkCookie>;
+    cookieListPointer = new std::forward_list<QNetworkCookie>;
 
-    // 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.
index 016879bec25f8744223f4ca6c6b4d1330f6cdfdd..a35e060c12bc6dfb32a9fcddff63d12f4164134c 100644 (file)
@@ -31,6 +31,9 @@
 #include <QLabel>
 #include <QProgressBar>
 
+// C++ headers.
+#include <forward_list>
+
 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<QNetworkCookie> *cookieListPointer;
+    std::forward_list<QNetworkCookie> *cookieListPointer;
     QString currentDomainSettingsDomain;
     QUrl currentUrl;
     double currentZoomFactor;