]> gitweb.stoutner.com Git - PrivacyBrowserPC.git/blobdiff - src/widgets/TabWidget.cpp
Add a default folder icon to the edit folder dialog. https://redmine.stoutner.com...
[PrivacyBrowserPC.git] / src / widgets / TabWidget.cpp
index 76ff617c3e180f3e253884d277dfd2a536f94dce..f65c79ea2177b404eef157f441245dab7175ed73 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright 2022-2023 Soren Stoutner <soren@stoutner.com>.
+ * Copyright 2022-2024 Soren Stoutner <soren@stoutner.com>.
  *
  * This file is part of Privacy Browser PC <https://www.stoutner.com/privacy-browser-pc>.
  *
  */
 
 // Application headers.
+#include "DevToolsWebEngineView.h"
 #include "TabWidget.h"
 #include "Settings.h"
 #include "ui_AddTabWidget.h"
+#include "ui_Tab.h"
 #include "ui_TabWidget.h"
 #include "databases/CookiesDatabase.h"
 #include "dialogs/SaveDialog.h"
 QString TabWidget::webEngineDefaultUserAgent = QLatin1String("");
 
 // Construct the class.
-TabWidget::TabWidget(QWidget *parent) : QWidget(parent)
+TabWidget::TabWidget(QWidget *windowPointer) : QWidget(windowPointer)
 {
+    // Create a QProcess to check if KDE is running.
+    QProcess *checkIfRunningKdeQProcessPointer = new QProcess();
+
+    // Create an argument string list that contains `ksmserver` (KDE Session Manager).
+    QStringList argument = QStringList(QLatin1String("ksmserver"));
+
+    // Run `pidof` to check for the presence of `ksmserver`.
+    checkIfRunningKdeQProcessPointer->start(QLatin1String("pidof"), argument);
+
+    // Monitor any standard output.
+    connect(checkIfRunningKdeQProcessPointer, &QProcess::readyReadStandardOutput, [this]
+    {
+        // If there is any standard output, `ksmserver` is running.
+        isRunningKde = true;
+    });
+
     // Instantiate the user agent helper.
     userAgentHelperPointer = new UserAgentHelper();
 
@@ -78,6 +96,9 @@ TabWidget::TabWidget(QWidget *parent) : QWidget(parent)
     // Set the loading favorite icon movie file name.
     loadingFavoriteIconMoviePointer->setFileName(QStringLiteral(":/icons/loading.gif"));
 
+    // Stop the loading favorite icon movie if the window is destroyed.  Otherwise, the app will crash if there is more than one window open and a window is closed while at tab is loading.
+    connect(windowPointer, SIGNAL(destroyed()), this, SLOT(stopLoadingFavoriteIconMovie()));
+
     // Add the first tab.
     addFirstTab();
 
@@ -102,14 +123,22 @@ TabWidget::TabWidget(QWidget *parent) : QWidget(parent)
 
 TabWidget::~TabWidget()
 {
+    // Get the number of tabs.
+    int numberOfTabs = qTabWidgetPointer->count();
+
     // Manually delete each WebEngine page.
-    for (int i = 0; i < qTabWidgetPointer->count(); ++i)
+    for (int i = 0; i < numberOfTabs; ++i)
     {
-        // Get the privacy WebEngine view.
-        PrivacyWebEngineView *privacyWebEngineViewPointer = qobject_cast<PrivacyWebEngineView *>(qTabWidgetPointer->widget(i));
+        // Get the tab splitter widget.
+        QWidget *tabSplitterWidgetPointer = qTabWidgetPointer->widget(i);
 
-        // Deletion the WebEngine page to prevent the following error:  `Release of profile requested but WebEnginePage still not deleted. Expect troubles !`
+        // Get the WebEngine views.
+        PrivacyWebEngineView *privacyWebEngineViewPointer = tabSplitterWidgetPointer->findChild<PrivacyWebEngineView *>();
+        DevToolsWebEngineView *devToolsWebEngineViewPointer = tabSplitterWidgetPointer->findChild<DevToolsWebEngineView *>();
+
+        // Deletion the WebEngine pages to prevent the following error:  `Release of profile requested but WebEnginePage still not deleted. Expect troubles !`
         delete privacyWebEngineViewPointer->page();
+        delete devToolsWebEngineViewPointer->page();
     }
 }
 
@@ -149,27 +178,57 @@ void TabWidget::addFirstTab()
     qTabWidgetPointer->currentWidget()->setFocus();
 }
 
-PrivacyWebEngineView* TabWidget::addTab(const bool removeUrlLineEditFocus, const bool backgroundTab)
+PrivacyWebEngineView* TabWidget::addTab(const bool removeUrlLineEditFocus, const bool adjacent, const bool backgroundTab, const QString urlString)
 {
-    // Create a privacy WebEngine view.
+    // Create a splitter widget.
+    QSplitter *splitterPointer = new QSplitter();
+
+    // Set the splitter to be vertical.
+    splitterPointer->setOrientation(Qt::Vertical);
+
+    // Set the splitter handle size.
+    splitterPointer->setHandleWidth(5);
+
+    // Create the WebEngines.
     PrivacyWebEngineView *privacyWebEngineViewPointer = new PrivacyWebEngineView();
+    DevToolsWebEngineView *devToolsWebEngineViewPointer = new DevToolsWebEngineView();
+
+    // Add the WebEngines to the splitter.
+    splitterPointer->addWidget(privacyWebEngineViewPointer);
+    splitterPointer->addWidget(devToolsWebEngineViewPointer);
+
+    // Initialize the new tab index.
+    int newTabIndex = 0;
 
     // Add a new tab.
-    int newTabIndex = qTabWidgetPointer->addTab(privacyWebEngineViewPointer, i18nc("New tab label.", "New Tab"));
+    if (adjacent)  // Add the new tab adjacent to the current tab.
+        newTabIndex = qTabWidgetPointer->insertTab((qTabWidgetPointer->currentIndex() + 1), splitterPointer, i18nc("New tab label.", "New Tab"));
+    else  // Add the new tab at the end of the list.
+        newTabIndex = qTabWidgetPointer->addTab(splitterPointer, i18nc("New tab label.", "New Tab"));
 
     // Set the default tab icon.
     qTabWidgetPointer->setTabIcon(newTabIndex, defaultFavoriteIcon);
 
-    // Get handles for the WebEngine page and profile.
+    // Get handles for the WebEngine components.
     QWebEnginePage *webEnginePagePointer = privacyWebEngineViewPointer->page();
     QWebEngineProfile *webEngineProfilePointer = webEnginePagePointer->profile();
-
-    // Get handles for the web engine elements.
     QWebEngineCookieStore *webEngineCookieStorePointer = webEngineProfilePointer->cookieStore();
     QWebEngineSettings *webEngineSettingsPointer = webEnginePagePointer->settings();
 
+    // Set the development tools WebEngine.  This must be done here to preserve the bottom half of the window as the initial development tools size.
+    webEnginePagePointer->setDevToolsPage(devToolsWebEngineViewPointer->page());
+
+    // Initially hide the development tools WebEngine.
+    devToolsWebEngineViewPointer->setVisible(false);
+
+    // Initially disable the development tools WebEngine.
+    webEnginePagePointer->setDevToolsPage(nullptr);
+
+    // Disable JavaScript on the development tools WebEngine to prevent error messages from being written to the console.
+    devToolsWebEngineViewPointer->settings()->setAttribute(QWebEngineSettings::JavascriptEnabled, false);
+
     // Update the URL line edit when the URL changes.
-    connect(privacyWebEngineViewPointer, &PrivacyWebEngineView::urlChanged, [privacyWebEngineViewPointer, this] (const QUrl &newUrl)
+    connect(privacyWebEngineViewPointer, &PrivacyWebEngineView::urlChanged, [this, privacyWebEngineViewPointer] (const QUrl &newUrl)
     {
         // Only update the UI if this is the current tab.
         if (privacyWebEngineViewPointer == currentPrivacyWebEngineViewPointer)
@@ -184,10 +243,10 @@ PrivacyWebEngineView* TabWidget::addTab(const bool removeUrlLineEditFocus, const
     });
 
     // Update the title when it changes.
-    connect(privacyWebEngineViewPointer, &PrivacyWebEngineView::titleChanged, [this, privacyWebEngineViewPointer] (const QString &title)
+    connect(privacyWebEngineViewPointer, &PrivacyWebEngineView::titleChanged, [this, splitterPointer] (const QString &title)
     {
         // Get the index for this tab.
-        int tabIndex = qTabWidgetPointer->indexOf(privacyWebEngineViewPointer);
+        int tabIndex = qTabWidgetPointer->indexOf(splitterPointer);
 
         // Update the title for this tab.
         qTabWidgetPointer->setTabText(tabIndex, title);
@@ -198,10 +257,10 @@ PrivacyWebEngineView* TabWidget::addTab(const bool removeUrlLineEditFocus, const
     });
 
     // Connect the loading favorite icon movie to the tab icon.
-    connect(loadingFavoriteIconMoviePointer, &QMovie::frameChanged, [privacyWebEngineViewPointer, this]
+    connect(loadingFavoriteIconMoviePointer, &QMovie::frameChanged, [this, splitterPointer, privacyWebEngineViewPointer]
     {
         // Get the index for this tab.
-        int tabIndex = qTabWidgetPointer->indexOf(privacyWebEngineViewPointer);
+        int tabIndex = qTabWidgetPointer->indexOf(splitterPointer);
 
         // Display the loading favorite icon if this tab is loading.
         if (privacyWebEngineViewPointer->isLoading)
@@ -209,7 +268,7 @@ PrivacyWebEngineView* TabWidget::addTab(const bool removeUrlLineEditFocus, const
     });
 
     // Update the icon when it changes.
-    connect(privacyWebEngineViewPointer, &PrivacyWebEngineView::iconChanged, [privacyWebEngineViewPointer, this] (const QIcon &newFavoriteIcon)
+    connect(privacyWebEngineViewPointer, &PrivacyWebEngineView::iconChanged, [this, splitterPointer, privacyWebEngineViewPointer] (const QIcon &newFavoriteIcon)
     {
         // Store the favorite icon in the privacy web engine view.
         if (newFavoriteIcon.isNull())
@@ -218,7 +277,7 @@ PrivacyWebEngineView* TabWidget::addTab(const bool removeUrlLineEditFocus, const
             privacyWebEngineViewPointer->favoriteIcon = newFavoriteIcon;
 
         // Get the index for this tab.
-        int tabIndex = qTabWidgetPointer->indexOf(privacyWebEngineViewPointer);
+        int tabIndex = qTabWidgetPointer->indexOf(splitterPointer);
 
         // Update the icon for this tab.
         if (newFavoriteIcon.isNull())
@@ -228,7 +287,7 @@ PrivacyWebEngineView* TabWidget::addTab(const bool removeUrlLineEditFocus, const
     });
 
     // Update the progress bar and the favorite icon when a load is started.
-    connect(privacyWebEngineViewPointer, &PrivacyWebEngineView::loadStarted, [privacyWebEngineViewPointer, this] ()
+    connect(privacyWebEngineViewPointer, &PrivacyWebEngineView::loadStarted, [this, privacyWebEngineViewPointer] ()
     {
         // Set the privacy web engine view to be loading.
         privacyWebEngineViewPointer->isLoading = true;
@@ -245,7 +304,7 @@ PrivacyWebEngineView* TabWidget::addTab(const bool removeUrlLineEditFocus, const
     });
 
     // Update the progress bar when a load progresses.
-    connect(privacyWebEngineViewPointer, &PrivacyWebEngineView::loadProgress, [privacyWebEngineViewPointer, this] (const int progress)
+    connect(privacyWebEngineViewPointer, &PrivacyWebEngineView::loadProgress, [this, privacyWebEngineViewPointer] (const int progress)
     {
         // Store the load progress.
         privacyWebEngineViewPointer->loadProgressInt = progress;
@@ -256,7 +315,7 @@ PrivacyWebEngineView* TabWidget::addTab(const bool removeUrlLineEditFocus, const
     });
 
     // Update the progress bar when a load finishes.
-    connect(privacyWebEngineViewPointer, &PrivacyWebEngineView::loadFinished, [privacyWebEngineViewPointer, this] ()
+    connect(privacyWebEngineViewPointer, &PrivacyWebEngineView::loadFinished, [this, splitterPointer, privacyWebEngineViewPointer] ()
     {
         // Set the privacy web engine view to be not loading.
         privacyWebEngineViewPointer->isLoading = false;
@@ -269,7 +328,7 @@ PrivacyWebEngineView* TabWidget::addTab(const bool removeUrlLineEditFocus, const
             emit hideProgressBar();
 
         // Get the index for this tab.
-        int tabIndex = qTabWidgetPointer->indexOf(privacyWebEngineViewPointer);
+        int tabIndex = qTabWidgetPointer->indexOf(splitterPointer);
 
         // Display the current favorite icon
         qTabWidgetPointer->setTabIcon(tabIndex, privacyWebEngineViewPointer->favoriteIcon);
@@ -277,14 +336,17 @@ PrivacyWebEngineView* TabWidget::addTab(const bool removeUrlLineEditFocus, const
         // Create a no tabs loading variable.
         bool noTabsLoading = true;
 
+        // Get the number of tabs.
+        int numberOfTabs = qTabWidgetPointer->count();
+
         // Check to see if any other tabs are loading.
-        for (int i = 0; i < qTabWidgetPointer->count(); i++)
+        for (int i = 0; i < numberOfTabs; i++)
         {
             // Get the privacy WebEngine view for the tab.
-            PrivacyWebEngineView *webEngineViewPointer = qobject_cast<PrivacyWebEngineView*>(qTabWidgetPointer->widget(i));
+            PrivacyWebEngineView *privacyWebEngineViewPointer = qTabWidgetPointer->widget(i)->findChild<PrivacyWebEngineView *>();
 
-            // Check to see if it is currently loading.
-            if (webEngineViewPointer->isLoading)
+            // Check to see if it is currently loading.  If at least one tab is loading, this flag will end up being marked `false` when the for loop has finished.
+            if (privacyWebEngineViewPointer->isLoading)
                 noTabsLoading = false;
         }
 
@@ -294,7 +356,7 @@ PrivacyWebEngineView* TabWidget::addTab(const bool removeUrlLineEditFocus, const
     });
 
     // Display HTTP Ping blocked dialogs.
-    connect(privacyWebEngineViewPointer, &PrivacyWebEngineView::displayHttpPingBlockedDialog, [privacyWebEngineViewPointer, this] (const QString &httpPingUrl)
+    connect(privacyWebEngineViewPointer, &PrivacyWebEngineView::displayHttpPingBlockedDialog, [this, privacyWebEngineViewPointer] (const QString &httpPingUrl)
     {
         // Only display the HTTP Ping blocked dialog if this is the current tab.
         if (privacyWebEngineViewPointer == currentPrivacyWebEngineViewPointer)
@@ -319,12 +381,12 @@ PrivacyWebEngineView* TabWidget::addTab(const bool removeUrlLineEditFocus, const
         }
     });
 
-    // Update the zoom factor when changed by CTRL-Scrolling.  This can be modified when <https://redmine.stoutner.com/issues/845> is fixed.
+    // Update the zoom actions when changed by CTRL-Scrolling.  This can be modified when <https://redmine.stoutner.com/issues/845> is fixed.
     connect(webEnginePagePointer, &QWebEnginePage::contentsSizeChanged, [webEnginePagePointer, this] ()
     {
-        // Only update the zoom factor action text if this is the current tab.
+        // Only update the zoom actions if this is the current tab.
         if (webEnginePagePointer == currentWebEnginePagePointer)
-            emit updateZoomFactorAction(webEnginePagePointer->zoomFactor());
+            emit updateZoomActions(webEnginePagePointer->zoomFactor());
     });
 
     // Display find text results.
@@ -372,8 +434,8 @@ PrivacyWebEngineView* TabWidget::addTab(const bool removeUrlLineEditFocus, const
     // Don't allow JavaScript to open windows.
     webEngineSettingsPointer->setAttribute(QWebEngineSettings::JavascriptCanOpenWindows, false);
 
-    // Allow keyboard navigation.
-    webEngineSettingsPointer->setAttribute(QWebEngineSettings::SpatialNavigationEnabled, true);
+    // Allow keyboard navigation between links and input fields.
+    webEngineSettingsPointer->setAttribute(QWebEngineSettings::SpatialNavigationEnabled, Settings::spatialNavigation());
 
     // Enable full screen support.
     webEngineSettingsPointer->setAttribute(QWebEngineSettings::FullScreenSupportEnabled, true);
@@ -391,7 +453,7 @@ PrivacyWebEngineView* TabWidget::addTab(const bool removeUrlLineEditFocus, const
     webEngineSettingsPointer->setAttribute(QWebEngineSettings::PluginsEnabled, true);
 
     // Update the cookies action.
-    connect(privacyWebEngineViewPointer, &PrivacyWebEngineView::updateCookiesAction, [privacyWebEngineViewPointer, this] (const int numberOfCookies)
+    connect(privacyWebEngineViewPointer, &PrivacyWebEngineView::updateCookiesAction, [this, privacyWebEngineViewPointer] (const int numberOfCookies)
     {
         // Update the cookie action if the specified privacy WebEngine view is the current privacy WebEngine view.
         if (privacyWebEngineViewPointer == currentPrivacyWebEngineViewPointer)
@@ -429,6 +491,9 @@ PrivacyWebEngineView* TabWidget::addTab(const bool removeUrlLineEditFocus, const
     if (removeUrlLineEditFocus)
         emit clearUrlLineEditFocus();
 
+    if (urlString != nullptr)
+        privacyWebEngineViewPointer->load(QUrl::fromUserInput(urlString));
+
     // Return the privacy WebEngine view pointer.
     return privacyWebEngineViewPointer;
 }
@@ -441,6 +506,18 @@ void TabWidget::applyApplicationSettings()
     else
         qTabWidgetPointer->setTabPosition(QTabWidget::South);
 
+    // Get the number of tabs.
+    int numberOfTabs = qTabWidgetPointer->count();
+
+    // Apply the spatial navigation settings to each WebEngine.
+    for (int i = 0; i < numberOfTabs; ++i) {
+        // Get the WebEngine view pointer.
+        PrivacyWebEngineView *privacyWebEngineViewPointer = qTabWidgetPointer->widget(i)->findChild<PrivacyWebEngineView *>();
+
+        // Apply the spatial navigation settings to each page.
+        privacyWebEngineViewPointer->page()->settings()->setAttribute(QWebEngineSettings::SpatialNavigationEnabled, Settings::spatialNavigation());
+    }
+
     // Set the search engine URL.
     searchEngineUrl = SearchEngineHelper::getSearchUrl(Settings::searchEngine());
 
@@ -450,8 +527,17 @@ void TabWidget::applyApplicationSettings()
 
 void TabWidget::applyDomainSettingsAndReload()
 {
-    // Apply the domain settings.  `true` reloads the website.
-    currentPrivacyWebEngineViewPointer->applyDomainSettings(currentPrivacyWebEngineViewPointer->url().host(), true);
+    // Get the number of tabs.
+    int numberOfTabs = qTabWidgetPointer->count();
+
+    // Apply the domain settings to each WebEngine.
+    for (int i = 0; i < numberOfTabs; ++i) {
+        // Get the WebEngine view pointer.
+        PrivacyWebEngineView *privacyWebEngineViewPointer = qTabWidgetPointer->widget(i)->findChild<PrivacyWebEngineView *>();
+
+        // Apply the spatial navigation settings to each page.
+        privacyWebEngineViewPointer->applyDomainSettings(privacyWebEngineViewPointer->url().host(), true);
+    }
 }
 
 void TabWidget::applyOnTheFlySearchEngine(QAction *searchEngineActionPointer)
@@ -487,10 +573,10 @@ void TabWidget::applyOnTheFlyUserAgent(QAction *userAgentActionPointer) const
     currentPrivacyWebEngineViewPointer->reload();
 }
 
-void TabWidget::applyOnTheFlyZoomFactor(const double &zoomFactor) const
+void TabWidget::applyOnTheFlyZoomFactor(const double zoomFactorDouble) const
 {
     // Set the zoom factor.
-    currentPrivacyWebEngineViewPointer->setZoomFactor(zoomFactor);
+    currentPrivacyWebEngineViewPointer->setZoomFactor(zoomFactorDouble);
 }
 
 void TabWidget::applySpellCheckLanguages() const
@@ -502,10 +588,10 @@ void TabWidget::applySpellCheckLanguages() const
     for (int i = 0; i < numberOfTabs; ++i)
     {
         // Get the WebEngine view pointer.
-        PrivacyWebEngineView *webEngineViewPointer = qobject_cast<PrivacyWebEngineView *>(qTabWidgetPointer->currentWidget());
+        PrivacyWebEngineView *privacyWebEngineViewPointer = qTabWidgetPointer->widget(i)->findChild<PrivacyWebEngineView *>();
 
         // Get the WebEngine page pointer.
-        QWebEnginePage *webEnginePagePointer = webEngineViewPointer->page();
+        QWebEnginePage *webEnginePagePointer = privacyWebEngineViewPointer->page();
 
         // Get the WebEngine profile pointer.
         QWebEngineProfile *webEngineProfilePointer = webEnginePagePointer->profile();
@@ -535,20 +621,29 @@ void TabWidget::deleteCookieFromStore(const QNetworkCookie &cookie) const
 
 void TabWidget::deleteTab(const int tabIndex)
 {
-    // Get the privacy WebEngine view.
-    PrivacyWebEngineView *privacyWebEngineViewPointer = qobject_cast<PrivacyWebEngineView *>(qTabWidgetPointer->widget(tabIndex));
+    // Get the tab splitter widget.
+    QWidget *tabSplitterWidgetPointer = qTabWidgetPointer->widget(tabIndex);
+
+    // Get the WebEngine views.
+    PrivacyWebEngineView *privacyWebEngineViewPointer = tabSplitterWidgetPointer->findChild<PrivacyWebEngineView *>();
+    DevToolsWebEngineView *devToolsWebEngineViewPointer = tabSplitterWidgetPointer->findChild<DevToolsWebEngineView *>();
 
     // Process the tab delete according to the number of tabs.
     if (qTabWidgetPointer->count() > 1)  // There is more than one tab.
     {
-        // Delete the tab.
+        // Remove the tab.
         qTabWidgetPointer->removeTab(tabIndex);
 
-        // Delete the WebEngine page to prevent the following error:  `Release of profile requested but WebEnginePage still not deleted. Expect troubles !`
+        // Delete the WebEngine pages to prevent the following error:  `Release of profile requested but WebEnginePage still not deleted. Expect troubles !`
         delete privacyWebEngineViewPointer->page();
+        delete devToolsWebEngineViewPointer->page();
 
-        // Delete the privacy WebEngine view.
+        // Delete the WebEngine views.
         delete privacyWebEngineViewPointer;
+        delete devToolsWebEngineViewPointer;
+
+        // Delete the tab splitter widget.
+        delete tabSplitterWidgetPointer;
     }
     else  // There is only one tab.
     {
@@ -587,7 +682,7 @@ void TabWidget::findText(const QString &text) const
 
 void TabWidget::findTextFinished(const QWebEngineFindTextResult &findTextResult)
 {
-    // Update the find text UI if it wasn't simply wiping the current find text selection.  Otherwise the UI temporarially flashes `0/0`.
+    // Update the find text UI if it wasn't simply wiping the current find text selection.  Otherwise the UI temporarily flashes `0/0`.
     if (wipingCurrentFindTextSelection)  // The current selection is being wiped.
     {
         // Reset the flag.
@@ -624,6 +719,30 @@ std::list<QNetworkCookie>* TabWidget::getCookieList() const
     return currentPrivacyWebEngineViewPointer->cookieListPointer;
 }
 
+QIcon TabWidget::getCurrentTabFavoritIcon() const
+{
+    // Return the current Privacy WebEngine favorite icon.
+    return currentPrivacyWebEngineViewPointer->favoriteIcon;
+}
+
+QString TabWidget::getCurrentTabTitle() const
+{
+    // Return the current Privacy WebEngine title.
+    return currentPrivacyWebEngineViewPointer->title();
+}
+
+QString TabWidget::getCurrentTabUrl() const
+{
+    // Return the current Privacy WebEngine URL as a string.
+    return currentPrivacyWebEngineViewPointer->url().toString();
+}
+
+QString TabWidget::getCurrentUserAgent() const
+{
+    // Return the current WebEngine user agent.
+    return currentWebEngineProfilePointer->httpUserAgent();
+}
+
 QString& TabWidget::getDomainSettingsName() const
 {
     // Return the domain settings name.
@@ -669,7 +788,7 @@ void TabWidget::loadInitialWebsite()
 void TabWidget::loadUrlFromLineEdit(QString url) const
 {
     // Decide if the text is more likely to be a URL or a search.
-    if (url.startsWith("file://"))  // The text is likely a file URL.
+    if (url.startsWith("file://") || url.startsWith("view-source:"))  // The text is likely a file or view source URL.
     {
         // Load the URL.
         currentPrivacyWebEngineViewPointer->load(QUrl::fromUserInput(url));
@@ -725,6 +844,12 @@ void TabWidget::pageLinkHovered(const QString &linkUrl) const
     emit linkHovered(linkUrl);
 }
 
+void TabWidget::stopLoadingFavoriteIconMovie() const
+{
+    // Stop the loading favorite icon movie.  Otherwise, the browser will crash if a second window is closed while a tab in it is loading.  <https://redmine.stoutner.com/issues/1010>
+    loadingFavoriteIconMoviePointer->stop();
+}
+
 void TabWidget::print() const
 {
     // Create a printer.
@@ -781,10 +906,49 @@ void TabWidget::printWebpage(QPrinter *printerPointer) const
 
 void TabWidget::refresh() const
 {
+    // Reset the HTTP authentication dialog counter.
+    currentPrivacyWebEngineViewPointer->httpAuthenticationDialogsDisplayed = 0;
+
     // Reload the website.
     currentPrivacyWebEngineViewPointer->reload();
 }
 
+void TabWidget::reloadAndBypassCache() const
+{
+    // Reload the website, bypassing the cache.
+    currentWebEnginePagePointer->triggerAction(QWebEnginePage::ReloadAndBypassCache);
+}
+
+void TabWidget::saveArchive()
+{
+    // Get the suggested file name.
+    QString suggestedFileName = currentPrivacyWebEngineViewPointer->title() + ".mht";
+
+    // Get the download directory.
+    QString downloadDirectory = Settings::downloadDirectory();
+
+    // Resolve the system download directory if specified.
+    if (downloadDirectory == QLatin1String("System Download Directory"))
+        downloadDirectory = QStandardPaths::writableLocation(QStandardPaths::DownloadLocation);
+
+    // Get a file path from the file picker.
+    QString saveFilePath = QFileDialog::getSaveFileName(this, i18nc("Save file dialog caption", "Save File"), downloadDirectory + QLatin1Char('/') + suggestedFileName);
+
+    // Save the webpage as an archive if the file save path is populated.
+    if (!saveFilePath.isEmpty())
+    {
+        // Update the download directory if specified.
+        if (Settings::autoUpateDownloadDirectory())
+            updateDownloadDirectory(saveFilePath);
+
+        // Set the saving archive flag.  Otherwise, a second download tries to run.
+        savingArchive = true;
+
+        // Save the archive.
+        currentWebEnginePagePointer->save(saveFilePath);
+    }
+}
+
 void TabWidget::setTabBarVisible(const bool visible) const
 {
     // Set the tab bar visibility.
@@ -793,162 +957,211 @@ void TabWidget::setTabBarVisible(const bool visible) const
 
 void TabWidget::showSaveDialog(QWebEngineDownloadItem *webEngineDownloadItemPointer)
 {
-    // Get the download attributes.
-    QUrl downloadUrl = webEngineDownloadItemPointer->url();
-    QString mimeTypeString = webEngineDownloadItemPointer->mimeType();
-    QString suggestedFileName = webEngineDownloadItemPointer->suggestedFileName();
-    int totalBytes = webEngineDownloadItemPointer->totalBytes();
-
-    // Check to see if local storage (cookies) is enabled.
-    if (currentPrivacyWebEngineViewPointer->localStorageEnabled)  // Local storage (cookies) is enabled.  Use WebEngine's downloader.
+    // Only show the save dialog if an archive is not currently being saved.  Otherwise, two save dialogs will be shown.
+    if (!savingArchive)
     {
-        // Instantiate the save dialog.
-        SaveDialog *saveDialogPointer = new SaveDialog(downloadUrl, mimeTypeString, totalBytes);
+        // Get the download attributes.
+        QUrl downloadUrl = webEngineDownloadItemPointer->url();
+        QString mimeTypeString = webEngineDownloadItemPointer->mimeType();
+        QString suggestedFileName = webEngineDownloadItemPointer->suggestedFileName();
+        int totalBytes = webEngineDownloadItemPointer->totalBytes();
+
+        // Check to see if Privacy Browser is not running KDE or if local storage (cookies) is enabled.
+        if (!isRunningKde || currentPrivacyWebEngineViewPointer->localStorageEnabled)  // KDE is not running or local storage (cookies) is enabled.  Use WebEngine's downloader.
+        {
+            // Instantiate the save dialog.
+            SaveDialog *saveDialogPointer = new SaveDialog(this, downloadUrl, mimeTypeString, totalBytes);
 
-        // Display the save dialog.
-        int saveDialogResult = saveDialogPointer->exec();
+            // Display the save dialog.
+            int saveDialogResult = saveDialogPointer->exec();
 
-        // Process the save dialog results.
-        if (saveDialogResult == QDialog::Accepted)  // Save was selected.
-        {
-            // Get the download directory.
-            QString downloadDirectory = Settings::downloadLocation();
+            // Process the save dialog results.
+            if (saveDialogResult == QDialog::Accepted)  // Save was selected.
+            {
+                // Get the download directory.
+                QString downloadDirectory = Settings::downloadDirectory();
 
-            // Resolve the system download directory if specified.
-            if (downloadDirectory == QLatin1String("System Download Directory"))
-                downloadDirectory = QStandardPaths::writableLocation(QStandardPaths::DownloadLocation);
+                // Resolve the system download directory if specified.
+                if (downloadDirectory == QLatin1String("System Download Directory"))
+                    downloadDirectory = QStandardPaths::writableLocation(QStandardPaths::DownloadLocation);
 
-            // Display a save file dialog.
-            QString saveFilePath = QFileDialog::getSaveFileName(this, i18nc("Save file dialog caption", "Save File"), downloadDirectory + QLatin1Char('/') + suggestedFileName);
+                // Get a file path from the file picker.
+                QString saveFilePath = QFileDialog::getSaveFileName(this, i18nc("Save file dialog caption", "Save File"), downloadDirectory + QLatin1Char('/') + suggestedFileName);
 
-            // Process the save file path.
-            if (!saveFilePath.isEmpty())  // The file save path is populated.
-            {
-                // Create a save file path file info.
-                QFileInfo saveFilePathFileInfo = QFileInfo(saveFilePath);
+                // Process the save file path.
+                if (!saveFilePath.isEmpty())  // The file save path is populated.
+                {
+                    // Update the download directory if specified.
+                    if (Settings::autoUpateDownloadDirectory())
+                        updateDownloadDirectory(saveFilePath);
 
-                // Get the canonical save path and file name.
-                QString absoluteSavePath = saveFilePathFileInfo.absolutePath();
-                QString saveFileName = saveFilePathFileInfo.fileName();
+                    // Create a save file path file info.
+                    QFileInfo saveFilePathFileInfo = QFileInfo(saveFilePath);
 
-                // Set the download directory and file name.
-                webEngineDownloadItemPointer->setDownloadDirectory(absoluteSavePath);
-                webEngineDownloadItemPointer->setDownloadFileName(saveFileName);
+                    // Get the canonical save path and file name.
+                    QString absoluteSavePath = saveFilePathFileInfo.absolutePath();
+                    QString saveFileName = saveFilePathFileInfo.fileName();
 
-                // Create a file download notification.
-                KNotification *fileDownloadNotificationPointer = new KNotification(QLatin1String("FileDownload"));
+                    // Set the download directory and file name.
+                    webEngineDownloadItemPointer->setDownloadDirectory(absoluteSavePath);
+                    webEngineDownloadItemPointer->setDownloadFileName(saveFileName);
 
-                // Set the notification title.
-                fileDownloadNotificationPointer->setTitle(i18nc("Download notification title", "Download"));
+                    // Create a file download notification.
+                    KNotification *fileDownloadNotificationPointer = new KNotification(QLatin1String("FileDownload"));
 
-                // Set the notification text.
-                fileDownloadNotificationPointer->setText(i18nc("Downloading notification text", "Downloading %1", saveFileName));
+                    // Set the notification title.
+                    fileDownloadNotificationPointer->setTitle(i18nc("Download notification title", "Download"));
 
-                // Set the notification icon.
-                fileDownloadNotificationPointer->setIconName(QLatin1String("download"));
+                    // Set the notification text.
+                    fileDownloadNotificationPointer->setText(i18nc("Downloading notification text", "Downloading %1", saveFileName));
 
-                // Set the action list cancel button.
-                fileDownloadNotificationPointer->setActions(QStringList({i18nc("Download notification action","Cancel")}));
+                    // Get the download icon from the theme.
+                    QIcon downloadIcon = QIcon::fromTheme(QLatin1String("download"), QIcon::fromTheme(QLatin1String("document-save")));
 
-                // Set the notification to display indefinitely.
-                fileDownloadNotificationPointer->setFlags(KNotification::Persistent);
+                    // Set the notification icon.
+                    fileDownloadNotificationPointer->setIconName(downloadIcon.name());
 
-                // Prevent the notification from being autodeleted if it is closed.  Otherwise, the updates to the notification below cause a crash.
-                fileDownloadNotificationPointer->setAutoDelete(false);
+                    // Set the action list cancel button.
+                    fileDownloadNotificationPointer->setActions(QStringList({i18nc("Download notification action","Cancel")}));
 
-                // Display the notification.
-                fileDownloadNotificationPointer->sendEvent();
+                    // Prevent the notification from being autodeleted if it is closed.  Otherwise, the updates to the notification below cause a crash.
+                    fileDownloadNotificationPointer->setAutoDelete(false);
 
-                // Handle clicks on the cancel button.
-                connect(fileDownloadNotificationPointer, &KNotification::action1Activated, [webEngineDownloadItemPointer, saveFileName] ()
-                {
-                    // Cancel the download.
-                    webEngineDownloadItemPointer->cancel();
+                    // Handle clicks on the cancel button.
+                    connect(fileDownloadNotificationPointer, &KNotification::action1Activated, [webEngineDownloadItemPointer, saveFileName] ()
+                    {
+                        // Cancel the download.
+                        webEngineDownloadItemPointer->cancel();
 
-                    // Create a file download notification.
-                    KNotification *canceledDownloadNotificationPointer = new KNotification(QLatin1String("FileDownload"));
+                        // Create a file download notification.
+                        KNotification *canceledDownloadNotificationPointer = new KNotification(QLatin1String("FileDownload"));
 
-                    // Set the notification title.
-                    canceledDownloadNotificationPointer->setTitle(i18nc("Download notification title", "Download"));
+                        // Set the notification title.
+                        canceledDownloadNotificationPointer->setTitle(i18nc("Download notification title", "Download"));
 
-                    // Set the new text.
-                    canceledDownloadNotificationPointer->setText(i18nc("Download canceled notification", "%1 download canceled", saveFileName));
+                        // Set the new text.
+                        canceledDownloadNotificationPointer->setText(i18nc("Download canceled notification", "%1 download canceled", saveFileName));
 
-                    // Set the notification icon.
-                    canceledDownloadNotificationPointer->setIconName(QLatin1String("download"));
+                        // Set the notification icon.
+                        canceledDownloadNotificationPointer->setIconName(QLatin1String("download"));
 
-                    // Display the notification.
-                    canceledDownloadNotificationPointer->sendEvent();
-                });
+                        // Display the notification.
+                        canceledDownloadNotificationPointer->sendEvent();
+                    });
 
-                // Update the notification when the download progresses.
-                connect(webEngineDownloadItemPointer, &QWebEngineDownloadItem::downloadProgress, [fileDownloadNotificationPointer, saveFileName] (qint64 bytesReceived, qint64 totalBytes)
-                {
-                    // Set the new text.  Total bytes will be 0 if the download size is unknown.
-                    if (totalBytes > 0)
+                    // Update the notification when the download progresses.
+                    connect(webEngineDownloadItemPointer, &QWebEngineDownloadItem::downloadProgress, [fileDownloadNotificationPointer, saveFileName] (qint64 bytesReceived, qint64 totalBytes)
                     {
-                        // Calculate the download percentage.
-                        int downloadPercentage = 100 * bytesReceived / totalBytes;
-
-                        // Set the file download notification text.
-                        fileDownloadNotificationPointer->setText(i18nc("Download progress notification text", "%1\% of %2 downloaded (%3 of %4 bytes)", downloadPercentage, saveFileName,
-                                                                    bytesReceived, totalBytes));
-                    }
-                    else
+                        // Set the new text.  Total bytes will be 0 if the download size is unknown.
+                        if (totalBytes > 0)
+                        {
+                            // Calculate the download percentage.
+                            int downloadPercentage = 100 * bytesReceived / totalBytes;
+
+                            // Set the file download notification text.
+                            fileDownloadNotificationPointer->setText(i18nc("Download progress notification text", "%1\% of %2 downloaded (%3 of %4 bytes)", downloadPercentage, saveFileName,
+                                                                        bytesReceived, totalBytes));
+                        }
+                        else
+                        {
+                            // Set the file download notification text.
+                            fileDownloadNotificationPointer->setText(i18nc("Download progress notification text", "%1:  %2 bytes downloaded", saveFileName, bytesReceived));
+                        }
+
+                        // Display the updated notification.
+                        fileDownloadNotificationPointer->update();
+                    });
+
+                    // Update the notification when the download finishes.  The save file name must be copied into the lambda or a crash occurs.
+                    connect(webEngineDownloadItemPointer, &QWebEngineDownloadItem::finished, [fileDownloadNotificationPointer, saveFileName, saveFilePath] ()
                     {
-                        // Set the file download notification text.
-                        fileDownloadNotificationPointer->setText(i18nc("Download progress notification text", "%1:  %2 bytes downloaded", saveFileName, bytesReceived));
-                    }
-
-                    // Display the updated notification.
-                    fileDownloadNotificationPointer->update();
-                });
+                        // Set the new text.
+                        fileDownloadNotificationPointer->setText(i18nc("Download finished notification text", "%1 download finished", saveFileName));
 
-                // Update the notification when the download finishes.  The save file name must be copied into the lambda or a crash occurs.
-                connect(webEngineDownloadItemPointer, &QWebEngineDownloadItem::finished, [fileDownloadNotificationPointer, saveFileName, saveFilePath] ()
-                {
-                    // Set the new text.
-                    fileDownloadNotificationPointer->setText(i18nc("Download finished notification text", "%1 download finished", saveFileName));
+                        // Set the URL so the file options will be displayed.
+                        fileDownloadNotificationPointer->setUrls(QList<QUrl> {QUrl(saveFilePath)});
 
-                    // Set the URL so the file options will be displayed.
-                    fileDownloadNotificationPointer->setUrls(QList<QUrl> {QUrl(saveFilePath)});
+                        // Remove the actions from the notification.
+                        fileDownloadNotificationPointer->setActions(QStringList());
 
-                    // Remove the actions from the notification.
-                    fileDownloadNotificationPointer->setActions(QStringList());
+                        // Set the notification to disappear after a timeout.
+                        fileDownloadNotificationPointer->setFlags(KNotification::CloseOnTimeout);
 
-                    // Set the notification to disappear after a timeout.
-                    fileDownloadNotificationPointer->setFlags(KNotification::CloseOnTimeout);
+                        // Display the updated notification.
+                        fileDownloadNotificationPointer->update();
+                    });
 
-                    // Display the updated notification.
-                    fileDownloadNotificationPointer->update();
-                });
+                    // Display the notification.
+                    fileDownloadNotificationPointer->sendEvent();
 
-                // Start the download.
-                webEngineDownloadItemPointer->accept();
+                    // Start the download.
+                    webEngineDownloadItemPointer->accept();
+                }
+                else  // The file save path is not populated.
+                {
+                    // Cancel the download.
+                    webEngineDownloadItemPointer->cancel();
+                }
             }
-            else  // The file save path is not populated.
+            else  // Cancel was selected.
             {
                 // Cancel the download.
                 webEngineDownloadItemPointer->cancel();
             }
         }
-        else  // Cancel was selected.
+        else  // KDE is running and local storage (cookies) is disabled.  Use KDE's native downloader.
+            // This must use the show command to launch a separate dialog which cancels WebEngine's automatic background download of the file to a temporary location.
         {
-            // Cancel the download.
-            webEngineDownloadItemPointer->cancel();
+            // Instantiate the save dialog.  `true` instructs it to use the native downloader
+            SaveDialog *saveDialogPointer = new SaveDialog(this, downloadUrl, mimeTypeString, totalBytes, suggestedFileName, true);
+
+            // Connect the save button.
+            connect(saveDialogPointer, SIGNAL(useNativeKdeDownloader(QUrl &, QString &)), this, SLOT(useNativeKdeDownloader(QUrl &, QString &)));
+
+            // Show the dialog.
+            saveDialogPointer->show();
         }
     }
-    else  // Local storage (cookies) is disabled.  Use KDE's native downloader.
-          // This must use the show command to launch a separate dialog which cancels WebEngine's automatic background download of the file to a temporary location.
+
+    // Reset the saving archive flag.
+    savingArchive = false;
+}
+
+void TabWidget::stop() const
+{
+    // Stop the loading of the current privacy WebEngine.
+    currentPrivacyWebEngineViewPointer->stop();
+}
+
+void TabWidget::toggleDeveloperTools(const bool enabled) const
+{
+    // Get a handle for the current developer tools WebEngine.
+    DevToolsWebEngineView *devToolsWebEngineViewPointer = qTabWidgetPointer->currentWidget()->findChild<DevToolsWebEngineView *>();
+
+    if (enabled)
     {
-        // Instantiate the save dialog.  `true` instructs it to use the native downloader
-        SaveDialog *saveDialogPointer = new SaveDialog(downloadUrl, mimeTypeString, totalBytes, suggestedFileName, true);
+        // Set the zoom factor on the development tools WebEngine.
+        devToolsWebEngineViewPointer->setZoomFactor(currentWebEnginePagePointer->zoomFactor());
 
-        // Connect the save button.
-        connect(saveDialogPointer, SIGNAL(useNativeDownloader(QUrl &, QString &)), this, SLOT(useNativeDownloader(QUrl &, QString &)));
+        // Enable the development tools.
+        currentWebEnginePagePointer->setDevToolsPage(devToolsWebEngineViewPointer->page());
 
-        // Show the dialog.
-        saveDialogPointer->show();
+        // Enable JavaScript on the development tools WebEngine.
+        devToolsWebEngineViewPointer->settings()->setAttribute(QWebEngineSettings::JavascriptEnabled, true);
+
+        // Display the developer tools.
+        devToolsWebEngineViewPointer->setVisible(true);
+    }
+    else
+    {
+        // Disable JavaScript on the development tools WebEngine to prevent error messages from being written to the console.
+        devToolsWebEngineViewPointer->settings()->setAttribute(QWebEngineSettings::JavascriptEnabled, false);
+
+        // Disable the development tools.
+        currentWebEnginePagePointer->setDevToolsPage(nullptr);
+
+        // Hide the developer tools.
+        devToolsWebEngineViewPointer->setVisible(false);
     }
 }
 
@@ -1003,25 +1216,41 @@ void TabWidget::toggleLocalStorage()
     currentPrivacyWebEngineViewPointer->reload();
 }
 
+void TabWidget::updateDownloadDirectory(QString newDownloadDirectory) const
+{
+    // Remove the file name from the save file path.
+    newDownloadDirectory.truncate(newDownloadDirectory.lastIndexOf(QLatin1Char('/')));
+
+    // Update the download location.
+    Settings::setDownloadDirectory(newDownloadDirectory);
+
+    // Get a handle for the KConfig skeleton.
+    KConfigSkeleton *kConfigSkeletonPointer = Settings::self();
+
+    // Write the settings to disk.
+    kConfigSkeletonPointer->save();
+}
+
 void TabWidget::updateUiFromWebEngineView(const PrivacyWebEngineView *privacyWebEngineViewPointer) const
 {
     // Only update the UI if the signal was emitted from the current privacy WebEngine.
     if (privacyWebEngineViewPointer == currentPrivacyWebEngineViewPointer)
     {
         // Update the UI.
+        emit updateDefaultZoomFactor(currentPrivacyWebEngineViewPointer->defaultZoomFactor);
         emit updateDomainSettingsIndicator(currentPrivacyWebEngineViewPointer->domainSettingsName != QLatin1String(""));
         emit updateJavaScriptAction(currentWebEngineSettingsPointer->testAttribute(QWebEngineSettings::JavascriptEnabled));
         emit updateLocalStorageAction(currentPrivacyWebEngineViewPointer->localStorageEnabled);
         emit updateDomStorageAction(currentWebEngineSettingsPointer->testAttribute(QWebEngineSettings::LocalStorageEnabled));
         emit updateUserAgentActions(currentWebEngineProfilePointer->httpUserAgent(), true);
-        emit updateZoomFactorAction(currentPrivacyWebEngineViewPointer->zoomFactor());
+        emit updateZoomActions(currentPrivacyWebEngineViewPointer->zoomFactor());
     }
 }
 
 void TabWidget::updateUiWithTabSettings()
 {
     // Update the current WebEngine pointers.
-    currentPrivacyWebEngineViewPointer = qobject_cast<PrivacyWebEngineView *>(qTabWidgetPointer->currentWidget());
+    currentPrivacyWebEngineViewPointer = qTabWidgetPointer->currentWidget()->findChild<PrivacyWebEngineView *>();
     currentWebEngineSettingsPointer = currentPrivacyWebEngineViewPointer->settings();
     currentWebEnginePagePointer = currentPrivacyWebEngineViewPointer->page();
     currentWebEngineProfilePointer = currentWebEnginePagePointer->profile();
@@ -1031,15 +1260,20 @@ void TabWidget::updateUiWithTabSettings()
     // Clear the URL line edit focus.
     emit clearUrlLineEditFocus();
 
+    // Get a handle for the development tools WebEngine view.
+    DevToolsWebEngineView *devToolsWebEngineViewPointer = qTabWidgetPointer->currentWidget()->findChild<DevToolsWebEngineView *>();
+
     // Update the actions.
+    emit updateDefaultZoomFactor(currentPrivacyWebEngineViewPointer->defaultZoomFactor);
     emit updateBackAction(currentWebEngineHistoryPointer->canGoBack());
     emit updateCookiesAction(currentPrivacyWebEngineViewPointer->cookieListPointer->size());
+    emit updateDeveloperToolsAction(devToolsWebEngineViewPointer->isVisible());
     emit updateDomStorageAction(currentWebEngineSettingsPointer->testAttribute(QWebEngineSettings::LocalStorageEnabled));
     emit updateForwardAction(currentWebEngineHistoryPointer->canGoForward());
     emit updateJavaScriptAction(currentWebEngineSettingsPointer->testAttribute(QWebEngineSettings::JavascriptEnabled));
     emit updateLocalStorageAction(currentPrivacyWebEngineViewPointer->localStorageEnabled);
     emit updateUserAgentActions(currentWebEngineProfilePointer->httpUserAgent(), true);
-    emit updateZoomFactorAction(currentPrivacyWebEngineViewPointer->zoomFactor());
+    emit updateZoomActions(currentPrivacyWebEngineViewPointer->zoomFactor());
 
     // Update the URL.
     emit updateWindowTitle(currentPrivacyWebEngineViewPointer->title());
@@ -1057,10 +1291,10 @@ void TabWidget::updateUiWithTabSettings()
         emit hideProgressBar();
 }
 
-void TabWidget::useNativeDownloader(QUrl &downloadUrl, QString &suggestedFileName)
+void TabWidget::useNativeKdeDownloader(QUrl &downloadUrl, QString &suggestedFileName)
 {
     // Get the download directory.
-    QString downloadDirectory = Settings::downloadLocation();
+    QString downloadDirectory = Settings::downloadDirectory();
 
     // Resolve the system download directory if specified.
     if (downloadDirectory == QLatin1String("System Download Directory"))
@@ -1079,15 +1313,20 @@ void TabWidget::useNativeDownloader(QUrl &downloadUrl, QString &suggestedFileNam
     saveFileDialogPointer->setWindowModality(Qt::WindowModal);
 
     // Process the saving of the file.  The save file dialog pointer must be captured directly instead of by reference or nasty crashes occur.
-    auto saveFile = [saveFileDialogPointer, downloadUrl] ()
+    auto saveFile = [saveFileDialogPointer, downloadUrl, this] ()
     {
         // Get the save location.  The dialog box should only allow the selecting of one file location.
         QUrl saveLocation = saveFileDialogPointer->selectedUrls().value(0);
 
+        // Update the download directory if specified.
+        if (Settings::autoUpateDownloadDirectory())
+            updateDownloadDirectory(saveLocation.toLocalFile());
+
         // Create a file copy job.  `-1` creates the file with default permissions.
         KIO::FileCopyJob *fileCopyJobPointer = KIO::file_copy(downloadUrl, saveLocation, -1, KIO::Overwrite);
 
-        // Set the download job to display any error messages.
+        // Set the download job to display any warning and error messages.
+        fileCopyJobPointer->uiDelegate()->setAutoWarningHandlingEnabled(true);
         fileCopyJobPointer->uiDelegate()->setAutoErrorHandlingEnabled(true);
 
         // Start the download.