]> gitweb.stoutner.com Git - PrivacyBrowserPC.git/blobdiff - src/dialogs/CookiesDialog.cpp
Rename Local Storage to DOM Storage. https://redmine.stoutner.com/issues/852
[PrivacyBrowserPC.git] / src / dialogs / CookiesDialog.cpp
index 3321c6569f9f069f582f7f8c3e04f8fdae97fec8..b200891566a8ef8def175108394de306e1cda854 100644 (file)
  */
 
 // Application headers.
+#include "AddOrEditCookieDialog.h"
 #include "CookiesDialog.h"
 #include "ui_CookiesDialog.h"
-#include "ui_CookieWidget.h"
 
-// The KDE Frameworks headers.
+// KDE Frameworks headers.
 #include <KLocalizedString>
 
-// The Qt toolkit headers.
+// Qt toolkit headers.
 #include <QDateTime>
 #include <QMessageBox>
+#include <QShortcut>
+#include <QUrl>
 
-CookiesDialog::CookiesDialog(QList<QNetworkCookie> *originalCookieListPointer) : QDialog(nullptr), cookieListPointer(originalCookieListPointer)
+// Construct the class.
+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::list<QNetworkCookie> *originalCookieListPointer) : QDialog(nullptr), cookieListPointer(originalCookieListPointer)
 {
     // Set the dialog window title.
     setWindowTitle(i18nc("The cookies dialog window title", "Cookies"));
 
     // Set the window modality.
-    setWindowModality(Qt::WindowModality::WindowModal);
+    setWindowModality(Qt::WindowModality::ApplicationModal);
 
     // Instantiate the cookie settings dialog UI.
     Ui::CookiesDialog cookiesDialogUi;
@@ -43,68 +114,147 @@ CookiesDialog::CookiesDialog(QList<QNetworkCookie> *originalCookieListPointer) :
     // Setup the UI.
     cookiesDialogUi.setupUi(this);
 
-    // Create the scroll area widget.
-    QWidget *scrollAreaWidgetPointer = new QWidget();
+    // Get a handle for the tree view.
+    treeViewPointer = cookiesDialogUi.treeView;
+
+    // Initialize the standard item model.
+    standardItemModelPointer = new QStandardItemModel();
+
+    // Set the column count.
+    standardItemModelPointer->setColumnCount(6);
+
+    // 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 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."));
+
+    // Sort the cookie list.
+    cookieListPointer->sort(cookieSortPredicate);
+
+    // Create the current domain string.
+    QString currentDomainString = "";
 
-    // Create the cookies VBox layout.
-    cookiesVBoxLayoutPointer = new QVBoxLayout();
+    // Create the current domain standard item pointer.
+    QStandardItem *currentDomainStandardItemPointer;
 
     // Populate the VBoxLayout.
     for (QNetworkCookie cookie : *cookieListPointer)
     {
-        // Create a cookie display widget.
-        QWidget *cookieDisplayWidgetPointer = new QWidget();
-
-        // Instantiate the cookie widget dialog UI.
-        Ui::CookieWidget cookieWidgetUi;
-
-        // Setup the UI.
-        cookieWidgetUi.setupUi(cookieDisplayWidgetPointer);
-
-        // Get handles for the views.
-        QLabel *domainLabelPointer = cookieWidgetUi.domainLabel;
-        QLabel *nameLabelPointer = cookieWidgetUi.nameLabel;
-        QLabel *expirationDateLabelPointer = cookieWidgetUi.expirationDateLabel;
-        QLabel *pathLabelPointer = cookieWidgetUi.pathLabel;
-        QCheckBox *httpOnlyCheckBoxPointer = cookieWidgetUi.httpOnlyCheckBox;
-        QCheckBox *secureCheckBoxPointer = cookieWidgetUi.secureCheckBox;
-        QLabel *valueLabelPointer = cookieWidgetUi.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 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);
+        // 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);
     }
 
-    // Set the scroll area widget layout.
-    scrollAreaWidgetPointer->setLayout(cookiesVBoxLayoutPointer);
+    // 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 scroll area.
-    QScrollArea *scrollAreaPointer = cookiesDialogUi.scrollArea;
+    // Get a handle for the tree view selection model.
+    treeViewSelectionModelPointer = treeViewPointer->selectionModel();
 
-    // Set the scroll area widget.
-    scrollAreaPointer->setWidget(scrollAreaWidgetPointer);
+    // 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);
@@ -113,16 +263,265 @@ CookiesDialog::CookiesDialog(QList<QNetworkCookie> *originalCookieListPointer) :
     deleteAllButtonPointer->setIcon(QIcon::fromTheme("delete"));
 
     // Connect the buttons.
-    connect(deleteAllButtonPointer, SIGNAL(released()), this, SLOT(showDeleteAllMessageBox()));
+    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();
 };
 
+void CookiesDialog::addCookieFromDialog(const QNetworkCookie &cookie) const
+{
+    // Add the cookie to the cookie list and the cookie store.
+    emit addCookie(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::deleteCookie(const QModelIndex &modelIndex) const
+{
+    // Create a partial cookie.
+    QNetworkCookie partialCookie;
+
+    // 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());
+
+    // 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::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 AddOrEditCookieDialog(AddOrEditCookieDialog::AddCookie);
+
+    // Show the dialog.
+    addCookieDialogPointer->show();
+
+    // Add the cookie if directed.
+    connect(addCookieDialogPointer, SIGNAL(addCookie(QNetworkCookie)), this, SLOT(addCookieFromDialog(QNetworkCookie)));
+}
+
 void CookiesDialog::showDeleteAllMessageBox() const
 {
     // Instantiate a delete all message box.
@@ -152,29 +551,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);
+
+    // Instantiate a delete cookie message box.
+    QMessageBox deleteCookieMessageBox;
+
+    // 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?"));
+    }
 
-        // Create a layout item pointer.
-        QLayoutItem *layoutItemPointer;
+    // Set the standard buttons.
+    deleteCookieMessageBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No);
 
-        // Delete each cookie widget.
-        while ((layoutItemPointer = cookiesVBoxLayoutPointer->takeAt(0)) != nullptr)
+    // 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.
         {
-            // Delete the widget.
-            delete layoutItemPointer->widget();
+            // 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 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"));
+    }
 }