]> gitweb.stoutner.com Git - PrivacyBrowserPC.git/commitdiff
Implement file downloads.
authorSoren Stoutner <soren@stoutner.com>
Sat, 9 Jul 2022 23:29:04 +0000 (16:29 -0700)
committerSoren Stoutner <soren@stoutner.com>
Sat, 9 Jul 2022 23:29:04 +0000 (16:29 -0700)
CMakeLists.txt
src/CMakeLists.txt
src/dialogs/CMakeLists.txt
src/dialogs/SaveDialog.cpp [new file with mode: 0644]
src/dialogs/SaveDialog.h [new file with mode: 0644]
src/uis/DomainSettingsDialog.ui
src/uis/SaveDialog.ui [new file with mode: 0644]
src/views/BrowserView.cpp
src/views/BrowserView.h
src/windows/BrowserWindow.cpp
src/windows/BrowserWindow.h

index d6408ec8db956ca1163cf2348afc640dece8e842..ff4b909c293507591cd1496f227b2478fe12b05e 100644 (file)
@@ -61,6 +61,7 @@ find_package(KF5 ${KDE_FRAMEWORKS_MIN_VERSION} REQUIRED COMPONENTS
     DBusAddons
     DocTools
     I18n
+    KIO
     XmlGui
 )
 
index 335a27d06975e7c32af2ea2db9b05f0e88418285..499230e852b4d80e134a01072e17c90d07a11f71 100644 (file)
@@ -40,6 +40,7 @@ ki18n_wrap_ui(privacy-browser
     uis/CookiesDialog.ui
     uis/DomainSettingsDialog.ui
     uis/DurableCookiesDialog.ui
+    uis/SaveDialog.ui
     uis/SettingsGeneral.ui
     uis/SettingsPrivacy.ui
 )
@@ -59,6 +60,8 @@ target_link_libraries(privacy-browser
     KF5::DBusAddons
     KF5::DocTools
     KF5::I18n
+    KF5::KIOCore
+    KF5::KIOWidgets
     KF5::XmlGui
 )
 
index 2b722cca588fa4c4333dff1faad664c01d76d878..7120f4d053f607efa04fe7a40560169635c1cff0 100644 (file)
@@ -22,4 +22,5 @@ target_sources(privacy-browser PRIVATE
     CookiesDialog.cpp
     DomainSettingsDialog.cpp
     DurableCookiesDialog.cpp
+    SaveDialog.cpp
 )
diff --git a/src/dialogs/SaveDialog.cpp b/src/dialogs/SaveDialog.cpp
new file mode 100644 (file)
index 0000000..fff857d
--- /dev/null
@@ -0,0 +1,94 @@
+/*
+ * Copyright © 2022 Soren Stoutner <soren@stoutner.com>.
+ *
+ * This file is part of Privacy Browser PC <https://www.stoutner.com/privacy-browser-pc>.
+ *
+ * Privacy Browser PC is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Privacy Browser PC is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Privacy Browser PC.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+// Application headers.
+#include "SaveDialog.h"
+#include "ui_SaveDialog.h"
+
+// KDE Frameworks headers.
+#include <KLocalizedString>
+
+// Qt toolkit headers.
+#include <QMimeDatabase>
+#include <QPushButton>
+#include <QShortcut>
+#include <QStandardPaths>
+
+SaveDialog::SaveDialog(QWebEngineDownloadItem *downloadItemPointer)
+{
+    // Set the dialog window title.
+    setWindowTitle(i18nc("The save dialog window title", "Save"));
+
+    // Set the window modality.
+    setWindowModality(Qt::WindowModality::ApplicationModal);
+
+    // Instantiate the save dialog UI.
+    Ui::SaveDialog saveDialogUi;
+
+    // Setup the UI.
+    saveDialogUi.setupUi(this);
+
+    // Get handles for the widgets.
+    QLabel *urlLabelPointer = saveDialogUi.urlLabel;
+    QLabel *filetypeLabelPointer = saveDialogUi.fileTypeLabel;
+    QLabel *mimeTypeLabelPointer = saveDialogUi.mimeTypeLabel;
+    QLabel *sizeLabelPointer = saveDialogUi.sizeLabel;
+    QDialogButtonBox *dialogButtonBoxPointer = saveDialogUi.dialogButtonBox;
+    QPushButton *saveButtonPointer = dialogButtonBoxPointer->button(QDialogButtonBox::Save);
+
+    // Get the URL and the suggested file name.
+    downloadUrl = downloadItemPointer->url();
+    suggestedFileName = downloadItemPointer->suggestedFileName();
+    QString mimeType = downloadItemPointer->mimeType();
+
+    // Get a MIME type database.
+    QMimeDatabase mimeDatabase;
+
+    // Populate the labels.
+    urlLabelPointer->setText("<b>" + downloadUrl.toString() + "</b>");
+    filetypeLabelPointer->setText("<b>" + mimeDatabase.mimeTypeForName(mimeType).comment() + "</b>");
+    mimeTypeLabelPointer->setText("<b>" + mimeType + "</b>");
+
+    // Populate the download size label.
+    if (downloadItemPointer->totalBytes() == -1)  // The file size is unknown.
+        sizeLabelPointer->setText(i18nc("Unknown download file size.  The bold style should be preserved.", "<b>unknown</b>"));
+    else  // The file size is known.  Format it according to the locale.
+        sizeLabelPointer->setText(ki18nc("Download file size.  The bold style should be preserved.", "<b>%1 bytes</b>").subs(downloadItemPointer->totalBytes()).toString());
+
+    // Connect the buttons.
+    connect(saveButtonPointer, SIGNAL(clicked()), this, SLOT(showFileDialog()));
+    connect(dialogButtonBoxPointer, SIGNAL(rejected()), this, SLOT(reject()));
+
+    // Create the keyboard shortcuts.
+    QShortcut *sShortcutPointer = new QShortcut(QKeySequence(i18nc("The save key shortcut.", "s")), this);
+    QShortcut *cShortcutPointer = new QShortcut(QKeySequence(i18nc("The close key shortcut.", "c")), this);
+
+    // Connect the shortcuts.
+    connect(sShortcutPointer, SIGNAL(activated()), this, SLOT(showFileDialog()));
+    connect(cShortcutPointer, SIGNAL(activated()), this, SLOT(reject()));
+}
+
+void SaveDialog::showFileDialog()
+{
+    // Show the file picker dialog.
+    emit showSaveFilePickerDialog(downloadUrl, suggestedFileName);
+
+    // Close the dialog.
+    reject();
+}
diff --git a/src/dialogs/SaveDialog.h b/src/dialogs/SaveDialog.h
new file mode 100644 (file)
index 0000000..36354c1
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ * Copyright © 2022 Soren Stoutner <soren@stoutner.com>.
+ *
+ * This file is part of Privacy Browser PC <https://www.stoutner.com/privacy-browser-pc>.
+ *
+ * Privacy Browser PC is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Privacy Browser PC is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Privacy Browser PC.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef SAVEDIALOG_H
+#define SAVEDIALOG_H
+
+// Qt toolkit headers.
+#include <QDialog>
+#include <QUrl>
+#include <QWebEngineDownloadItem>
+
+class SaveDialog : public QDialog
+{
+    // Include the Q_OBJECT macro.
+    Q_OBJECT
+
+public:
+    // The primary constructor.
+    explicit SaveDialog(QWebEngineDownloadItem *downloadItemPointer);
+
+signals:
+    // The signals.
+    void showSaveFilePickerDialog(QUrl &downloadUrl, QString &suggestedFileName);
+
+private Q_SLOTS:
+    // The private slots.
+    void showFileDialog();
+
+private:
+    // The private variables.
+    QUrl downloadUrl;
+    QString suggestedFileName;
+};
+#endif
index 9cf73247d81a440e49663e7b827bb063556fbdff..d7eac8e1d0fa2cd6f240f567e6cba41be174cdbd 100644 (file)
@@ -90,7 +90,7 @@
                             <layout class="QFormLayout">
                                 <!-- Domain name. -->
                                 <item row="0" column="0">
-                                    <widget class="QLabel" name="domainNameLabel">
+                                    <widget class="QLabel">
                                         <property name="text">
                                             <string>Domain name</string>
                                         </property>
diff --git a/src/uis/SaveDialog.ui b/src/uis/SaveDialog.ui
new file mode 100644 (file)
index 0000000..489b8c8
--- /dev/null
@@ -0,0 +1,137 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+  Copyright © 2022 Soren Stoutner <soren@stoutner.com>.
+
+  This file is part of Privacy Browser PC <https://www.stoutner.com/privacy-browser-android>.
+
+  Privacy Browser PC is free software: you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation, either version 3 of the License, or
+  (at your option) any later version.
+
+  Privacy Browser PC is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+  You should have received a copy of the GNU General Public License
+  along with Privacy Browser PC.  If not, see <http://www.gnu.org/licenses/>. -->
+
+<ui version="4.0">
+    <class>SaveDialog</class>
+
+    <widget class="QWidget">
+        <layout class="QVBoxLayout">
+            <!-- Header question. -->
+            <item>
+                <widget class="QLabel">
+                    <property name="text">
+                        <string>&lt;b&gt;Would you like to download the following file?&lt;/b&gt;</string>
+                    </property>
+
+                    <property name="textFormat">
+                        <enum>Qt::RichText</enum>
+                    </property>
+
+                    <property name="alignment">
+                        <set>Qt::AlignCenter</set>
+                    </property>
+
+                    <property name="margin">
+                        <number>10</number>
+                    </property>
+                </widget>
+            </item>
+
+            <!-- File details. -->
+            <item>
+                <widget class="QGroupBox">
+                    <property name="title">
+                        <string>File details</string>
+                    </property>
+
+                    <!-- Labels. -->
+                    <layout class="QFormLayout">
+                        <!-- URL. -->
+                        <item row="0" column="0">
+                            <widget class="QLabel">
+                                <property name="text">
+                                    <string>URL:</string>
+                                </property>
+                            </widget>
+                        </item>
+
+                        <item row="0" column="1">
+                            <widget class="QLabel" name="urlLabel">
+                                <property name="textFormat">
+                                    <enum>Qt::RichText</enum>
+                                </property>
+                            </widget>
+                        </item>
+
+                        <!-- File type. -->
+                        <item row="1" column="0">
+                            <widget class="QLabel">
+                                <property name="text">
+                                    <string>File type:</string>
+                                </property>
+                            </widget>
+                        </item>
+
+                        <item row="1" column="1">
+                            <widget class="QLabel" name="fileTypeLabel">
+                                <property name="textFormat">
+                                    <enum>Qt::RichText</enum>
+                                </property>
+                            </widget>
+                        </item>
+
+                        <!-- MIME type. -->
+                        <item row="2" column="0">
+                            <widget class="QLabel">
+                                <property name="text">
+                                    <string>MIME type:</string>
+                                </property>
+                            </widget>
+                        </item>
+
+                        <item row="2" column="1">
+                            <widget class="QLabel" name="mimeTypeLabel">
+                                <property name="textFormat">
+                                    <enum>Qt::RichText</enum>
+                                </property>
+                            </widget>
+                        </item>
+
+                        <!-- Size. -->
+                        <item row="3" column="0">
+                            <widget class="QLabel">
+                                <property name="text">
+                                    <string>Size:</string>
+                                </property>
+                            </widget>
+                        </item>
+
+                        <item row="3" column="1">
+                            <widget class="QLabel" name="sizeLabel">
+                                <property name="textFormat">
+                                    <enum>Qt::RichText</enum>
+                                </property>
+                            </widget>
+                        </item>
+                    </layout>
+                </widget>
+            </item>
+
+            <!-- Dialog buttons. -->
+            <item>
+                <widget class="QDialogButtonBox" name="dialogButtonBox">
+                    <property name="standardButtons">
+                        <set>QDialogButtonBox::Save | QDialogButtonBox::Cancel</set>
+                    </property>
+                </widget>
+            </item>
+        </layout>
+    </widget>
+</ui>
index b3f593ff93352a1d023a895207cb5832af21e036..2a23d8126ca54b80aa8afdf7747c79be853cacf0 100644 (file)
 #include "ui_BrowserView.h"
 #include "databases/CookiesDatabase.h"
 #include "databases/DomainsDatabase.h"
+#include "dialogs/SaveDialog.h"
 #include "filters/MouseEventFilter.h"
 #include "helpers/SearchEngineHelper.h"
 #include "helpers/UserAgentHelper.h"
 #include "interceptors/UrlRequestInterceptor.h"
 #include "windows/BrowserWindow.h"
 
-// Qt framework headers.
+// KDE Framework headers.
+#include <KIO/FileCopyJob>
+#include <KIO/JobUiDelegate>
+
+// Qt toolkit headers.
 #include <QAction>
+#include <QFileDialog>
 #include <QPrintDialog>
 #include <QPrintPreviewDialog>
 #include <QPrinter>
@@ -161,6 +167,9 @@ BrowserView::BrowserView(QWidget *parent) : QWidget(parent)
     // Set the URL request interceptor.
     webEngineProfilePointer->setUrlRequestInterceptor(urlRequestInterceptorPointer);
 
+    // Handle file downloads.
+    connect(webEngineProfilePointer, SIGNAL(downloadRequested(QWebEngineDownloadItem *)), this, SLOT(showSaveDialog(QWebEngineDownloadItem *)));
+
     // Reapply the domain settings when the host changes.
     connect(urlRequestInterceptorPointer, SIGNAL(applyDomainSettings(QString)), this, SLOT(applyDomainSettingsWithoutReloading(QString)));
 
@@ -640,6 +649,54 @@ void BrowserView::refresh() const
     webEngineViewPointer->reload();
 }
 
+void BrowserView::showSaveDialog(QWebEngineDownloadItem *downloadItemPointer) const
+{
+    // Instantiate the save dialog.
+    SaveDialog *saveDialogPointer = new SaveDialog(downloadItemPointer);
+
+    // Connect the save button.
+    connect(saveDialogPointer, SIGNAL(showSaveFilePickerDialog(QUrl &, QString &)), this, SLOT(showSaveFilePickerDialog(QUrl &, QString &)));
+
+    // Show the dialog.
+    saveDialogPointer->show();
+}
+
+void BrowserView::showSaveFilePickerDialog(QUrl &downloadUrl, QString &suggestedFileName)
+{
+    // Create a save file dialog.
+    QFileDialog *saveFileDialogPointer = new QFileDialog(this, i18nc("Save file dialog caption", "Save File"), QStandardPaths::writableLocation(QStandardPaths::DownloadLocation));
+
+    // 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 windows 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();
+}
+
 void BrowserView::toggleDomStorage() const
 {
     // Toggle DOM storage.
index e2b52758dbd14fe3cdc30499b8b4c88e32e08144..8007aa73e35d3a57340a2b430501de6d0f33c314 100644 (file)
@@ -106,6 +106,8 @@ private Q_SLOTS:
     void loadStarted() const;
     void pageLinkHovered(const QString &linkUrl) const;
     void printWebpage(QPrinter *printerPointer) const;
+    void showSaveDialog(QWebEngineDownloadItem *downloadItemPointer) const;
+    void showSaveFilePickerDialog(QUrl &downloadUrl, QString &suggestedFileName);
     void updateUrl(const QUrl &url) const;
 
 private:
index 33aef29f77ad309caf13e4acc3ad3d5c6b1cf30a..2484061633266d0e7e3d5d49ddb57c38aaed5ad1 100644 (file)
@@ -189,8 +189,8 @@ BrowserWindow::BrowserWindow() : KXmlGuiWindow()
 
     // Display dialogs.
     connect(zoomFactorActionPointer, SIGNAL(triggered()), this, SLOT(getZoomFactorFromUser()));
-    connect(cookiesActionPointer, SIGNAL(triggered()), this, SLOT(openCookiesDialog()));
-    connect(domainSettingsActionPointer, SIGNAL(triggered()), this, SLOT(openDomainSettings()));
+    connect(cookiesActionPointer, SIGNAL(triggered()), this, SLOT(showCookiesDialog()));
+    connect(domainSettingsActionPointer, SIGNAL(triggered()), this, SLOT(showDomainSettingsDialog()));
 
     // Connect the URL toolbar actions.
     connect(javaScriptActionPointer, SIGNAL(triggered()), this, SLOT(toggleJavaScript()));
@@ -447,7 +447,27 @@ void BrowserWindow::loadUrlFromLineEdit(const QString &url) const
     browserViewPointer->loadUrlFromLineEdit(url);
 }
 
-void BrowserWindow::openCookiesDialog()
+void BrowserWindow::refresh() const
+{
+    // Remove the focus from the URL line edit.
+    urlLineEditPointer->clearFocus();
+
+    // Refresh the web page.
+    browserViewPointer->refresh();
+}
+
+void BrowserWindow::removeCookieFromList(const QNetworkCookie &cookie) const
+{
+    //qDebug() << "Remove cookie:  " << cookie.toRawForm();
+
+    // Remove the cookie from the list.
+    cookieListPointer->remove(cookie);
+
+    // Update the action text.
+    cookiesActionPointer->setText(i18nc("The Cookies action, which also displays the number of cookies", "Cookies - %1", cookieListPointer->size()));
+}
+
+void BrowserWindow::showCookiesDialog()
 {
     // Remove the focus from the URL line edit.
     urlLineEditPointer->clearFocus();
@@ -464,7 +484,7 @@ void BrowserWindow::openCookiesDialog()
     connect(cookiesDialogPointer, SIGNAL(deleteCookie(QNetworkCookie)), browserViewPointer, SLOT(deleteCookieFromStore(QNetworkCookie)));
 }
 
-void BrowserWindow::openDomainSettings() const
+void BrowserWindow::showDomainSettingsDialog() const
 {
     // Remove the focus from the URL line edit.
     urlLineEditPointer->clearFocus();
@@ -479,26 +499,6 @@ void BrowserWindow::openDomainSettings() const
     connect(domainSettingsDialogPointer, SIGNAL(domainSettingsUpdated()), browserViewPointer, SLOT(applyDomainSettingsAndReload()));
 }
 
-void BrowserWindow::refresh() const
-{
-    // Remove the focus from the URL line edit.
-    urlLineEditPointer->clearFocus();
-
-    // Refresh the web page.
-    browserViewPointer->refresh();
-}
-
-void BrowserWindow::removeCookieFromList(const QNetworkCookie &cookie) const
-{
-    //qDebug() << "Remove cookie:  " << cookie.toRawForm();
-
-    // Remove the cookie from the list.
-    cookieListPointer->remove(cookie);
-
-    // Update the action text.
-    cookiesActionPointer->setText(i18nc("The Cookies action, which also displays the number of cookies", "Cookies - %1", cookieListPointer->size()));
-}
-
 void BrowserWindow::showProgressBar(const int &progress) const
 {
     // Set the progress bar value.
index aa9e2ee0351a2c41c04178775d244f0bf1dbec7f..c05f9422c85da35d6d6cf9512e45cf3d9f372168 100644 (file)
@@ -60,11 +60,11 @@ private Q_SLOTS:
     void getZoomFactorFromUser();
     void home() const;
     void loadUrlFromLineEdit(const QString &url) const;
-    void openCookiesDialog();
-    void openDomainSettings() const;
     void refresh() const;
     void removeCookieFromList(const QNetworkCookie &cookie) const;
     void settingsConfigure();
+    void showCookiesDialog();
+    void showDomainSettingsDialog() const;
     void showProgressBar(const int &progress) const;
     void toggleDomStorage() const;
     void toggleJavaScript() const;