X-Git-Url: https://gitweb.stoutner.com/?p=PrivacyBrowserPC.git;a=blobdiff_plain;f=src%2Fwidgets%2FTabWidget.cpp;h=4edb304d0db6c45ed5d757a6d4a5f6cd20ad5596;hp=59e424331fd78643d2ae7927a56c87239ee300ff;hb=06a69a2d38bf73c0c5219f94c345b19142bb1646;hpb=fb2760a23bc59d63c74e18c92628ef03ccd8bf3a diff --git a/src/widgets/TabWidget.cpp b/src/widgets/TabWidget.cpp index 59e4243..4edb304 100644 --- a/src/widgets/TabWidget.cpp +++ b/src/widgets/TabWidget.cpp @@ -1,5 +1,5 @@ /* - * Copyright © 2022 Soren Stoutner . + * Copyright 2022 Soren Stoutner . * * This file is part of Privacy Browser PC . * @@ -27,13 +27,13 @@ #include "dialogs/SaveDialog.h" #include "filters/MouseEventFilter.h" #include "helpers/SearchEngineHelper.h" -#include "helpers/UserAgentHelper.h" #include "interceptors/UrlRequestInterceptor.h" #include "windows/BrowserWindow.h" // KDE Framework headers. #include #include +#include // Qt toolkit headers. #include @@ -45,11 +45,14 @@ #include // Initialize the public static variables. -QString TabWidget::webEngineDefaultUserAgent = QStringLiteral(""); +QString TabWidget::webEngineDefaultUserAgent = QLatin1String(""); // Construct the class. TabWidget::TabWidget(QWidget *parent) : QWidget(parent) { + // Instantiate the user agent helper. + userAgentHelperPointer = new UserAgentHelper(); + // Instantiate the UIs. Ui::TabWidget tabWidgetUi; Ui::AddTabWidget addTabWidgetUi; @@ -112,14 +115,14 @@ void TabWidget::addCookieToStore(QNetworkCookie cookie, QWebEngineCookieStore *w QUrl url; // Check to see if the domain does not start with a `.` because Qt makes this harder than it should be. - if (!cookie.domain().startsWith(QStringLiteral("."))) + if (!cookie.domain().startsWith(QLatin1String("."))) { // Populate the URL. url.setHost(cookie.domain()); - url.setScheme(QStringLiteral("https")); + url.setScheme(QLatin1String("https")); // Clear the domain from the cookie. - cookie.setDomain(QStringLiteral("")); + cookie.setDomain(QLatin1String("")); } // Add the cookie to the store. @@ -141,7 +144,7 @@ void TabWidget::addFirstTab() tabWidgetPointer->currentWidget()->setFocus(); } -void TabWidget::addTab() +PrivacyWebEngineView* TabWidget::addTab(const bool removeUrlLineEditFocus, const bool backgroundTab) { // Create a privacy WebEngine view. PrivacyWebEngineView *privacyWebEngineViewPointer = new PrivacyWebEngineView(); @@ -153,7 +156,7 @@ void TabWidget::addTab() tabWidgetPointer->setTabIcon(newTabIndex, defaultTabIcon); // Create an off-the-record profile (the default when no profile name is specified). - QWebEngineProfile *webEngineProfilePointer = new QWebEngineProfile(QStringLiteral("")); + QWebEngineProfile *webEngineProfilePointer = new QWebEngineProfile(QLatin1String("")); // Create a WebEngine page. QWebEnginePage *webEnginePagePointer = new QWebEnginePage(webEngineProfilePointer); @@ -166,13 +169,58 @@ void TabWidget::addTab() QWebEngineSettings *webEngineSettingsPointer = webEnginePagePointer->settings(); // Update the URL line edit when the URL changes. - connect(privacyWebEngineViewPointer, SIGNAL(urlChanged(const QUrl)), this, SLOT(updateUrl(const QUrl))); + connect(privacyWebEngineViewPointer, &PrivacyWebEngineView::urlChanged, [privacyWebEngineViewPointer, this] (const QUrl &newUrl) + { + // Only update the UI if this is the current tab. + if (privacyWebEngineViewPointer == currentPrivacyWebEngineViewPointer) + { + // Update the URL line edit. + emit updateUrlLineEdit(newUrl); + // Update the status of the forward and back buttons. + emit updateBackAction(currentWebEngineHistoryPointer->canGoBack()); + emit updateForwardAction(currentWebEngineHistoryPointer->canGoForward()); + } - // Update the progress bar. - connect(privacyWebEngineViewPointer, SIGNAL(loadStarted()), this, SLOT(loadStarted())); - connect(privacyWebEngineViewPointer, SIGNAL(loadProgress(const int)), this, SLOT(loadProgress(const int))); - connect(privacyWebEngineViewPointer, SIGNAL(loadFinished(const bool)), this, SLOT(loadFinished())); + // Reapply the zoom factor. This is a bug in QWebEngineView that resets the zoom with every load. It can be removed once is fixed. + privacyWebEngineViewPointer->setZoomFactor(currentZoomFactor); + }); + + // Update the progress bar when a load is started. + connect(privacyWebEngineViewPointer, &PrivacyWebEngineView::loadStarted, [privacyWebEngineViewPointer, this] () + { + // Store the load progress. + privacyWebEngineViewPointer->loadProgressInt = 0; + + // Show the progress bar if this is the current tab. + if (privacyWebEngineViewPointer == currentPrivacyWebEngineViewPointer) + emit showProgressBar(0); + }); + + // Update the progress bar when a load progresses. + connect(privacyWebEngineViewPointer, &PrivacyWebEngineView::loadProgress, [privacyWebEngineViewPointer, this] (const int progress) + { + // Store the load progress. + privacyWebEngineViewPointer->loadProgressInt = progress; + + // Update the progress bar if this is the current tab. + if (privacyWebEngineViewPointer == currentPrivacyWebEngineViewPointer) + emit showProgressBar(progress); + }); + + // Update the progress bar when a load finishes. + connect(privacyWebEngineViewPointer, &PrivacyWebEngineView::loadFinished, [privacyWebEngineViewPointer, this] () + { + // Store the load progress. + privacyWebEngineViewPointer->loadProgressInt = -1; + + // Hide the progress bar if this is the current tab. + if (privacyWebEngineViewPointer == currentPrivacyWebEngineViewPointer) + emit hideProgressBar(); + }); + + // Display find text results. + connect(webEnginePagePointer, SIGNAL(findTextFinished(const QWebEngineFindTextResult &)), this, SLOT(findTextFinished(const QWebEngineFindTextResult &))); // Handle full screen requests. connect(webEnginePagePointer, SIGNAL(fullScreenRequested(QWebEngineFullScreenRequest)), this, SLOT(fullScreenRequested(QWebEngineFullScreenRequest))); @@ -237,16 +285,13 @@ void TabWidget::addTab() // Limit WebRTC to public IP addresses. webEngineSettingsPointer->setAttribute(QWebEngineSettings::WebRTCPublicInterfacesOnly, true); - // Define an update cookie count lambda. - auto updateCookieCount = [privacyWebEngineViewPointer, this] (const int numberOfCookies) + // Update the cookies action. + connect(privacyWebEngineViewPointer, &PrivacyWebEngineView::updateCookiesAction, [privacyWebEngineViewPointer, this] (const int numberOfCookies) { // Update the cookie action if the specified privacy WebEngine view is the current privacy WebEngine view. if (privacyWebEngineViewPointer == currentPrivacyWebEngineViewPointer) emit updateCookiesAction(numberOfCookies); - }; - - // Update the cookies action. - connect(privacyWebEngineViewPointer, &PrivacyWebEngineView::updateCookiesAction, this, updateCookieCount); + }); // Process cookie changes. connect(webEngineCookieStorePointer, SIGNAL(cookieAdded(QNetworkCookie)), privacyWebEngineViewPointer, SLOT(addCookieToList(QNetworkCookie))); @@ -259,21 +304,22 @@ void TabWidget::addTab() for (QNetworkCookie *cookiePointer : *durableCookiesListPointer) addCookieToStore(*cookiePointer, webEngineCookieStorePointer); - // Define an update tab title lambda. - auto updateTabTitle = [privacyWebEngineViewPointer, this] (const QString &title) + // Update the title when it changes. + connect(privacyWebEngineViewPointer, &PrivacyWebEngineView::titleChanged, [this, privacyWebEngineViewPointer] (const QString &title) { // Get the index for this tab. int tabIndex = tabWidgetPointer->indexOf(privacyWebEngineViewPointer); // Update the title for this tab. tabWidgetPointer->setTabText(tabIndex, title); - }; - // Update the title when it changes. - connect(privacyWebEngineViewPointer, &PrivacyWebEngineView::titleChanged, this, updateTabTitle); + // Update the window title if this is the current tab. + if (tabIndex == tabWidgetPointer->currentIndex()) + emit updateWindowTitle(title); + }); - // Define an update tab icon lambda. - auto updateTabIcon = [privacyWebEngineViewPointer, this] (const QIcon &icon) + // Update the icon when it changes. + connect(privacyWebEngineViewPointer, &PrivacyWebEngineView::iconChanged, [privacyWebEngineViewPointer, this] (const QIcon &icon) { // Get the index for this tab. int tabIndex = tabWidgetPointer->indexOf(privacyWebEngineViewPointer); @@ -283,13 +329,27 @@ void TabWidget::addTab() tabWidgetPointer->setTabIcon(tabIndex, defaultTabIcon); else tabWidgetPointer->setTabIcon(tabIndex, icon); - }; + }); - // Update the icon when it changes. - connect(privacyWebEngineViewPointer, &PrivacyWebEngineView::iconChanged, this, updateTabIcon); + // Enable spell checking. + webEngineProfilePointer->setSpellCheckEnabled(true); + + // Set the spell check language. + webEngineProfilePointer->setSpellCheckLanguages({QLatin1String("en_US")}); + + // Populate the zoom factor. This is necessary if a URL is being loaded, like a local URL, that does not trigger `applyDomainSettings()`. + privacyWebEngineViewPointer->setZoomFactor(Settings::zoomFactor()); + + // Move to the new tab if it is not a background tab. + if (!backgroundTab) + tabWidgetPointer->setCurrentIndex(newTabIndex); + + // Clear the URL line edit focus so that it populates correctly when opening a new tab from the context menu. + if (removeUrlLineEditFocus) + emit clearUrlLineEditFocus(); - // Move to the new tab. - tabWidgetPointer->setCurrentIndex(newTabIndex); + // Return the privacy WebEngine view pointer. + return privacyWebEngineViewPointer; } void TabWidget::applyApplicationSettings() @@ -397,7 +457,7 @@ void TabWidget::applyDomainSettings(const QString &hostname, const bool reloadWe // Set the DOM storage status. switch (domainRecord.field(DomainsDatabase::DOM_STORAGE).value().toInt()) { - // Set the default DOM storage status. + // Set the default DOM storage status. QWebEngineSettings confusingly calls this local storage. case (DomainsDatabase::SYSTEM_DEFAULT): { currentWebEngineSettingsPointer->setAttribute(QWebEngineSettings::LocalStorageEnabled, Settings::domStorageEnabled()); @@ -405,7 +465,7 @@ void TabWidget::applyDomainSettings(const QString &hostname, const bool reloadWe break; } - // Disable DOM storage. + // Disable DOM storage. QWebEngineSettings confusingly calls this local storage. case (DomainsDatabase::DISABLED): { currentWebEngineSettingsPointer->setAttribute(QWebEngineSettings::LocalStorageEnabled, false); @@ -413,7 +473,7 @@ void TabWidget::applyDomainSettings(const QString &hostname, const bool reloadWe break; } - // Enable DOM storage. + // Enable DOM storage. QWebEngineSettings confusingly calls this local storage. case (DomainsDatabase::ENABLED): { currentWebEngineSettingsPointer->setAttribute(QWebEngineSettings::LocalStorageEnabled, true); @@ -443,7 +503,7 @@ void TabWidget::applyDomainSettings(const QString &hostname, const bool reloadWe else // The hostname does not have domain settings. { // Reset the domain settings name. - currentPrivacyWebEngineViewPointer->domainSettingsName = QStringLiteral(""); + currentPrivacyWebEngineViewPointer->domainSettingsName = QLatin1String(""); // Set the JavaScript status. currentWebEngineSettingsPointer->setAttribute(QWebEngineSettings::JavascriptEnabled, Settings::javaScriptEnabled()); @@ -465,7 +525,7 @@ void TabWidget::applyDomainSettings(const QString &hostname, const bool reloadWe } // Update the UI. - emit updateDomainSettingsIndicator(currentPrivacyWebEngineViewPointer->domainSettingsName != QStringLiteral("")); + emit updateDomainSettingsIndicator(currentPrivacyWebEngineViewPointer->domainSettingsName != QLatin1String("")); emit updateJavaScriptAction(currentWebEngineSettingsPointer->testAttribute(QWebEngineSettings::JavascriptEnabled)); emit updateLocalStorageAction(currentPrivacyWebEngineViewPointer->localStorageEnabled); emit updateDomStorageAction(currentWebEngineSettingsPointer->testAttribute(QWebEngineSettings::LocalStorageEnabled)); @@ -501,7 +561,7 @@ void TabWidget::applyOnTheFlyUserAgent(QAction *userAgentActionPointer) const userAgentName.remove('&'); // Apply the user agent. - currentWebEngineProfilePointer->setHttpUserAgent(UserAgentHelper::getUserAgentFromTranslatedName(userAgentName)); + currentWebEngineProfilePointer->setHttpUserAgent(userAgentHelperPointer->getUserAgentFromTranslatedName(userAgentName)); // Update the user agent actions. emit updateUserAgentActions(currentWebEngineProfilePointer->httpUserAgent(), false); @@ -562,6 +622,52 @@ void TabWidget::deleteTab(const int tabIndex) } } +void TabWidget::findPrevious(const QString &text) const +{ + // Store the current text. + currentPrivacyWebEngineViewPointer->findString = text; + + // Find the previous text in the current privacy WebEngine. + if (currentPrivacyWebEngineViewPointer->findCaseSensitive) + currentPrivacyWebEngineViewPointer->findText(text, QWebEnginePage::FindCaseSensitively|QWebEnginePage::FindBackward); + else + currentPrivacyWebEngineViewPointer->findText(text, QWebEnginePage::FindBackward); +} + +void TabWidget::findText(const QString &text) const +{ + // Store the current text. + currentPrivacyWebEngineViewPointer->findString = text; + + // Find the text in the current privacy WebEngine. + if (currentPrivacyWebEngineViewPointer->findCaseSensitive) + currentPrivacyWebEngineViewPointer->findText(text, QWebEnginePage::FindCaseSensitively); + else + currentPrivacyWebEngineViewPointer->findText(text); + + // Clear the currently selected text in the WebEngine page if the find text is empty. + if (text.isEmpty()) + currentWebEnginePagePointer->action(QWebEnginePage::Unselect)->activate(QAction::Trigger); +} + +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`. + if (wipingCurrentFindTextSelection) // The current selection is being wiped. + { + // Reset the flag. + wipingCurrentFindTextSelection = false; + } + else // A new search has been performed. + { + // Store the result. + currentPrivacyWebEngineViewPointer->findTextResult = findTextResult; + + // Update the UI. + emit updateFindTextResults(findTextResult); + } +} + void TabWidget::forward() const { // Go forward. @@ -595,10 +701,13 @@ void TabWidget::home() const currentPrivacyWebEngineViewPointer->load(QUrl::fromUserInput(Settings::homepage())); } -void TabWidget::loadFinished() const +PrivacyWebEngineView* TabWidget::loadBlankInitialWebsite() { - // Hide the progress bar. - emit hideProgressBar(); + // Apply the application settings. + applyApplicationSettings(); + + // Return the current privacy WebEngine view pointer. + return currentPrivacyWebEngineViewPointer; } void TabWidget::loadInitialWebsite() @@ -622,18 +731,6 @@ void TabWidget::loadInitialWebsite() } } -void TabWidget::loadProgress(const int &progress) const -{ - // Show the progress bar. - emit showProgressBar(progress); -} - -void TabWidget::loadStarted() const -{ - // Show the progress bar. - emit showProgressBar(0); -} - void TabWidget::loadUrlFromLineEdit(QString url) const { // Decide if the text is more likely to be a URL or a search. @@ -759,59 +856,159 @@ void TabWidget::setTabBarVisible(const bool visible) const tabWidgetPointer->tabBar()->setVisible(visible); } -void TabWidget::showSaveDialog(QWebEngineDownloadItem *downloadItemPointer) const +void TabWidget::showSaveDialog(QWebEngineDownloadItem *webEngineDownloadItemPointer) { - // Instantiate the save dialog. - SaveDialog *saveDialogPointer = new SaveDialog(downloadItemPointer); + // 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. + { + // Instantiate the save dialog. + SaveDialog *saveDialogPointer = new SaveDialog(downloadUrl, mimeTypeString, totalBytes); - // Connect the save button. - connect(saveDialogPointer, SIGNAL(showSaveFilePickerDialog(QUrl &, QString &)), this, SLOT(showSaveFilePickerDialog(QUrl &, QString &))); + // Display the save dialog. + int saveDialogResult = saveDialogPointer->exec(); - // Show the dialog. - saveDialogPointer->show(); -} + // Process the save dialog results. + if (saveDialogResult == QDialog::Accepted) // Save was selected. + { + // Get the download directory. + QString downloadDirectory = Settings::downloadLocation(); -void TabWidget::showSaveFilePickerDialog(QUrl &downloadUrl, QString &suggestedFileName) -{ - // Get the download location. - QString downloadDirectory = Settings::downloadLocation(); + // 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 == QStringLiteral("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); - // Create a save file dialog. - QFileDialog *saveFileDialogPointer = new QFileDialog(this, i18nc("Save file dialog caption", "Save File"), downloadDirectory); + // 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); - // Tell the dialog to use a save button. - saveFileDialogPointer->setAcceptMode(QFileDialog::AcceptSave); + // Get the canonical save path and file name. + QString absoluteSavePath = saveFilePathFileInfo.absolutePath(); + QString saveFileName = saveFilePathFileInfo.fileName(); - // Populate the file name from the download item pointer. - saveFileDialogPointer->selectFile(suggestedFileName); + // Set the download directory and file name. + webEngineDownloadItemPointer->setDownloadDirectory(absoluteSavePath); + webEngineDownloadItemPointer->setDownloadFileName(saveFileName); - // Prevent interaction with the parent window while the dialog is open. - saveFileDialogPointer->setWindowModality(Qt::WindowModal); + // Create a file download notification. + KNotification *fileDownloadNotificationPointer = new KNotification(QLatin1String("FileDownload")); - // 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] () { - // Get the save location. The dialog box should only allow the selecting of one file location. - QUrl saveLocation = saveFileDialogPointer->selectedUrls().value(0); + // Set the notification title. + fileDownloadNotificationPointer->setTitle(i18nc("Download notification title", "Download")); - // 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 notification text. + fileDownloadNotificationPointer->setText(i18nc("Downloading notification text", "Downloading %1", saveFileName)); - // Set the download job to display any error messages. - fileCopyJobPointer->uiDelegate()->setAutoErrorHandlingEnabled(true); + // Set the notification icon. + fileDownloadNotificationPointer->setIconName(QLatin1String("download")); - // Start the download. - fileCopyJobPointer->start(); - }; + // Set the action list cancel button. + fileDownloadNotificationPointer->setActions(QStringList({i18nc("Download notification action","Cancel")})); - // Handle clicks on the save button. - connect(saveFileDialogPointer, &QDialog::accepted, this, saveFile); + // Set the notification to display indefinitely. + fileDownloadNotificationPointer->setFlags(KNotification::Persistent); - // Show the dialog. - saveFileDialogPointer->show(); + // Prevent the notification from being autodeleted if it is closed. Otherwise, the updates to the notification below cause a crash. + fileDownloadNotificationPointer->setAutoDelete(false); + + // Display the notification. + fileDownloadNotificationPointer->sendEvent(); + + // 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")); + + // 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 notification icon. + canceledDownloadNotificationPointer->setIconName(QLatin1String("download")); + + // Display the notification. + canceledDownloadNotificationPointer->sendEvent(); + }); + + // 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 new text. Total bytes will be 0 if the download size is unknown. + if (totalBytes > 0) + fileDownloadNotificationPointer->setText(i18nc("Download progress notification text", "%1\% of %2 downloaded (%3 of %4 bytes)", downloadPercentage, saveFileName, + bytesReceived, totalBytes)); + else + 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 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(saveFilePath)}); + + // Remove the actions from the notification. + fileDownloadNotificationPointer->setActions(QStringList()); + + // Set the notification to disappear after a timeout. + fileDownloadNotificationPointer->setFlags(KNotification::CloseOnTimeout); + + // Display the updated notification. + fileDownloadNotificationPointer->update(); + }); + + // Start the download. + webEngineDownloadItemPointer->accept(); + } + else // The file save path is not populated. + { + // Cancel the download. + webEngineDownloadItemPointer->cancel(); + } + } + else // Cancel was selected. + { + // Cancel the download. + webEngineDownloadItemPointer->cancel(); + } + } + 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. + { + // Instantiate the save dialog. `true` instructs it to use the native downloader + SaveDialog *saveDialogPointer = new SaveDialog(downloadUrl, mimeTypeString, totalBytes, suggestedFileName, true); + + // Connect the save button. + connect(saveDialogPointer, SIGNAL(useNativeDownloader(QUrl &, QString &)), this, SLOT(useNativeDownloader(QUrl &, QString &))); + + // Show the dialog. + saveDialogPointer->show(); + } } void TabWidget::toggleDomStorage() const @@ -826,6 +1023,21 @@ void TabWidget::toggleDomStorage() const currentPrivacyWebEngineViewPointer->reload(); } +void TabWidget::toggleFindCaseSensitive(const QString &text) +{ + // Toggle find case sensitive. + currentPrivacyWebEngineViewPointer->findCaseSensitive = !currentPrivacyWebEngineViewPointer->findCaseSensitive; + + // Set the wiping current find text selection flag. + wipingCurrentFindTextSelection = true; + + // Wipe the previous search. Otherwise currently highlighted words will remain highlighted. + findText(QLatin1String("")); + + // Update the find text. + findText(text); +} + void TabWidget::toggleJavaScript() const { // Toggle JavaScript. @@ -863,26 +1075,72 @@ void TabWidget::updateUiWithTabSettings() // Clear the URL line edit focus. emit clearUrlLineEditFocus(); - // Update the UI. - emit updateDomainSettingsIndicator(currentPrivacyWebEngineViewPointer->domainSettingsName != QStringLiteral("")); + // Update the actions. + emit updateBackAction(currentWebEngineHistoryPointer->canGoBack()); + emit updateCookiesAction(currentPrivacyWebEngineViewPointer->cookieListPointer->size()); + emit updateDomStorageAction(currentWebEngineSettingsPointer->testAttribute(QWebEngineSettings::LocalStorageEnabled)); + emit updateForwardAction(currentWebEngineHistoryPointer->canGoForward()); 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()); + + // Update the URL. + emit updateWindowTitle(currentPrivacyWebEngineViewPointer->title()); + emit updateDomainSettingsIndicator(currentPrivacyWebEngineViewPointer->domainSettingsName != QLatin1String("")); emit updateUrlLineEdit(currentPrivacyWebEngineViewPointer->url()); - emit updateCookiesAction(currentPrivacyWebEngineViewPointer->cookieListPointer->size()); + + // Update the find text. + emit updateFindText(currentPrivacyWebEngineViewPointer->findString, currentPrivacyWebEngineViewPointer->findCaseSensitive); + emit updateFindTextResults(currentPrivacyWebEngineViewPointer->findTextResult); + + // Update the progress bar. + if (currentPrivacyWebEngineViewPointer->loadProgressInt >= 0) + emit showProgressBar(currentPrivacyWebEngineViewPointer->loadProgressInt); + else + emit hideProgressBar(); } -void TabWidget::updateUrl(const QUrl &url) const +void TabWidget::useNativeDownloader(QUrl &downloadUrl, QString &suggestedFileName) { - // Update the URL line edit. - emit updateUrlLineEdit(url); + // Get the download directory. + QString downloadDirectory = Settings::downloadLocation(); - // Update the status of the forward and back buttons. - emit updateBackAction(currentWebEngineHistoryPointer->canGoBack()); - emit updateForwardAction(currentWebEngineHistoryPointer->canGoForward()); + // Resolve the system download directory if specified. + if (downloadDirectory == QLatin1String("System Download Directory")) + downloadDirectory = QStandardPaths::writableLocation(QStandardPaths::DownloadLocation); + + // Create a save file dialog. + QFileDialog *saveFileDialogPointer = new QFileDialog(this, i18nc("Save file dialog caption", "Save File"), downloadDirectory); - // Reapply the zoom factor. This is a bug in QWebEngineView that resets the zoom with every load. - currentPrivacyWebEngineViewPointer->setZoomFactor(currentZoomFactor); + // Tell the dialog to use a save button. + saveFileDialogPointer->setAcceptMode(QFileDialog::AcceptSave); + + // Populate the file name from the download item pointer. + saveFileDialogPointer->selectFile(suggestedFileName); + + // Prevent interaction with the parent window while the dialog is open. + 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] () + { + // Get the save location. The dialog box should only allow the selecting of one file location. + QUrl saveLocation = saveFileDialogPointer->selectedUrls().value(0); + + // 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. + fileCopyJobPointer->uiDelegate()->setAutoErrorHandlingEnabled(true); + + // Start the download. + fileCopyJobPointer->start(); + }; + + // Handle clicks on the save button. + connect(saveFileDialogPointer, &QDialog::accepted, this, saveFile); + + // Show the dialog. + saveFileDialogPointer->show(); }