File indexing completed on 2024-05-12 05:00:04

0001 /* This file is part of the KDE project
0002     SPDX-FileCopyrightText: 2023 Stefano Crocco <stefano.crocco@alice.it>
0003 
0004     SPDX-License-Identifier: LGPL-2.0-or-later
0005 */
0006 
0007 #ifndef DOWNLOADEREXTENSION_H
0008 #define DOWNLOADEREXTENSION_H
0009 
0010 #include <KJob>
0011 #include <KParts/OpenUrlArguments>
0012 
0013 #include <QObject>
0014 #include <QUrl>
0015 
0016 #include "browserarguments.h"
0017 
0018 #include <libkonq_export.h>
0019 
0020 class QUrl;
0021 
0022 namespace KParts {
0023     class ReadOnlyPart;
0024 }
0025 
0026 namespace KonqInterfaces {
0027 
0028 class DownloaderJob;
0029 
0030 /**
0031  * @brief Interface for parts which need to download files themselves before they're opened or embedded instead of letting Konqueror do it
0032  *
0033  * This interface provides two pure virtual methods, downloadJob(), which returns a DownloaderJob, a `KJob` whose task is to perform the download and part(),
0034  * and a signal, downloadAndOpenUrl(), to be used instead of `KParts::BrowserExtension::openUrlRequest`.
0035  *
0036  * Most likely, when creating a class implementing this interface, you'll also need to create a concrete class deriving from DownloadJob.
0037  *
0038  * @warning When a part requests to download the URL itself, Konqueror doesn't try to auto-detect the mimetype of the URL, assuming that the
0039  * part has already tried to do so and passed the correct value to downloadAndOpenUrl().
0040  *
0041  * @note The part doesn't need to implement this interface itself: it can be implemented by any of its children.
0042  */
0043 class LIBKONQ_EXPORT DownloaderExtension : public QObject
0044 {
0045     Q_OBJECT
0046 
0047 public:
0048 
0049     /**
0050      * @brief Constructor
0051      * @param parent the parent object
0052      */
0053     DownloaderExtension(QObject *parent = nullptr);
0054 
0055     /**
0056      * @brief Destructor
0057      */
0058     virtual ~DownloaderExtension();
0059 
0060     /**
0061      * @brief Retrieves a the `KJob` to download the given URL.
0062      *
0063      * This method is called by Konqueror in response to a downloadAndOpenUrl() signal
0064      * This method will return a job which, when started, will download the URL.
0065      *
0066      * Implementations of this method may return a new job or an existing job. @p id is a number
0067      * used to distinguish between different download operations for the same URL (in case, for example,
0068      * the user asks a second download of the same file before the first one has finished).
0069      *
0070      * @param url the URL to download
0071      * @param id the id of the download. It's used to distinguish different downloads for the same URLs. If this method is called
0072      * multiple times with the same @p url and @p id, implementations can either return the same job or
0073      * different jobs. They're _required_ to return different jobs when called with different @p url
0074      * and/or @p id.
0075      * @param parent the object to give as parent to the returned object. If this method is called multiple
0076      * times with the same @p url and @p id, and the implementation returns the same job, this will be
0077      * ignored in all calls after the first
0078      * @return the job to use to perform the download or `nullptr` if such a job can't be created. In theory,
0079      * this should never happen, unless this method is called for an URL for which the part didn't request
0080      * a download.
0081      */
0082     virtual DownloaderJob* downloadJob(const QUrl &url, quint32 id, QObject *parent=nullptr)=0;
0083 
0084     /**
0085      * @brief The part associated with the extension
0086      * @return The part the extension belongs to
0087      */
0088     virtual KParts::ReadOnlyPart* part() const = 0;
0089 
0090     /**
0091      * @brief Returns the given object or any of its children casted to a DownloaderInterface*
0092      *
0093      * This is essentially as `QObject::findChildren` except that it works even if DownloaderInterface
0094      * itself isn't a `QObject`.
0095      * @param obj the object which should be cast or have one of its children cast to a DownloaderInterface
0096      * @return @p obj or one of its children cast to a DownloaderInterface or `nullptr` if neither @p obj
0097      * nor any of its children are derived from DownloaderInterface
0098      */
0099     static DownloaderExtension *downloader(QObject* obj);
0100 
0101 signals:
0102 
0103     /**
0104      * @brief Signal emitted by the extension to signal the application to open an URL after the part has finished
0105      * downloading the URL
0106      *
0107      * It has the same role of `KParts::BrowserExtension::openUrlRequest`. The main difference (aside from the @p downloadId argument)
0108      * is the fact that, unlike `KParts::BrowserExtension::openUrlRequest`, the handler for this signal is called synchronously.
0109      * This is important for the `WebEnginePart` implementation, where it allows to set the download path before having to accept the
0110      * download request.
0111      *
0112      * @param url the URL to download
0113      * @param downloadId an unique identifier for the download
0114      * @param arguments information about how to open the URL
0115      * @param browserArguments other information about how to open the URL
0116      * @param temp whether the downloaded file, if opened or embedded, should be deleted after use. This will be ignored if the user chooses to save the URL
0117      */
0118     void downloadAndOpenUrl(const QUrl &url, quint32 downloadId, const KParts::OpenUrlArguments &arguments = KParts::OpenUrlArguments(),
0119                             const BrowserArguments &browserArguments = BrowserArguments(), bool temp=true);
0120 };
0121 
0122 /**
0123  * @brief Class allowing a part to perform a download
0124  */
0125 class LIBKONQ_EXPORT DownloaderJob : public KJob {
0126     Q_OBJECT
0127 public:
0128 
0129     /**
0130      * @brief Default constructor
0131      * @param parent the parent object
0132      */
0133     DownloaderJob(QObject* parent=nullptr);
0134 
0135     /**
0136      * @brief destructor
0137      */
0138     ~DownloaderJob() override {};
0139 
0140     /**
0141      * @brief Changes the path where the URL will be downloaded.
0142      *
0143      * @warning This function must be called from within a slot (or lambda) connected to the
0144      * `BrowserExtension::openUrlRequest` emitted by the part and before `start()` is called.
0145      * If called in other circumstances, whether it has any effect or not is up to the implementation.
0146      * @param path the new download path. It must be an absolute path.
0147      * @return `true` if the download path has been changed succesfully and `false` otherwise
0148      */
0149     virtual bool setDownloadPath(const QString &path) = 0;
0150 
0151     /**
0152      * @brief The path where the URL will be saved
0153      * @return the path where the URL will be saved. This path is decided by the
0154      * part requesting the download
0155      */
0156     virtual QString downloadPath() const = 0;
0157 
0158     /**
0159      * @brief Whether or not the download path can be changed
0160      *
0161      * Implementations are required to allow changing the download path from the slot connected
0162      * with the `BrowserExtension::openUrlRequest` signal and only before the job is started.
0163      * Implementations may also allow to change the download path later.
0164      * @return `true` if the download path can be changed and `false` otherwise.
0165      */
0166     virtual bool canChangeDownloadPath() const = 0;
0167 
0168     /**
0169      * @brief Whether or not the download has finished
0170      */
0171     virtual bool finished() const = 0;
0172 
0173     /**
0174      * @brief The URL to download
0175      * @return the URL to download
0176      */
0177     virtual QUrl url() const = 0;
0178 
0179     /**
0180      * @brief Convenience function to start the download
0181      *
0182      * This sets the job up, setting the Ui delegate and the job tracker interface for the job and changing the
0183      * download path, connects the \link DownloaderJob::downloadResult downloadResult \endlink signal to the given
0184      * functor and starts the job.
0185      *
0186      * @param destPath the path where the URL should be downloaded. If empty, the download path won't be changed
0187      * @param widget the widget to use for the Ui delegate
0188      * @param context the receiver or the context of the signal (depending on whether functor is actually a lambda or functor
0189      *  or a pointer to member function)
0190      * @param functor the lambda, functor or pointer to member function to connect to the
0191      * \link DownloaderJob::downloadResult downloadResult \endlink signal
0192      * @warning In Qt5, functor cannot be a member function. If it is, no connection is done
0193      */
0194     template <typename Functor>
0195     void startDownload(const QString &destPath, QWidget *widget, QObject *context, Functor functor);
0196 
0197     /**
0198      * @brief Convenience function to start the download
0199      *
0200      * @overload
0201      *
0202      * As startDownload(const QString &, QWidget*, QObject*, Functor), except that it doesn't change the download path
0203      * @see startDownload(const QString &, QWidget*, QObject*, Functor)
0204      */
0205     template <typename Functor>
0206     void startDownload(QWidget *widget, QObject *context, Functor functor);
0207 
0208 private:
0209 
0210     /**
0211      * @brief Helper function called by startDownload()
0212      *
0213      * It sets up the job UI delegate and tracker interface and, optionally, changes the
0214      * download destination.
0215      *
0216      * @param widget the widget to use for the Ui delegate
0217      * @param destPath the new download destination. If empty, the download destination won't be changed
0218      */
0219     void prepareDownloadJob(QWidget *widget, const QString &destPath={});
0220 
0221 signals:
0222 
0223     /**
0224      * @brief Signal emitted when a download has finished
0225      *
0226      * This is a convenience signal which is emitted in response to `KJob::result()`. It
0227      * provides a the job already cast to DownoaderJob and the local URL where the remote
0228      * URL was saved to. You can connect to this signal as you would to `KJob::result()`.
0229      *
0230      * @param job a pointer to the job. It's the same as the `job` argument in `KJob::result()`
0231      * but you don't need to cast it to a DownloaderJob
0232      * @param url the URL where the remote URL was saved to. There are three possibilities:
0233      *  - if an error occurred, it will have the `error` scheme
0234      *  - if the user canceled the download, it will be empty
0235      *  - if the download was completed successfully, it will be a local URL
0236      */
0237     void downloadResult(DownloaderJob *job, const QUrl &url);
0238 };
0239 }
0240 
0241 template<typename Functor>
0242 void KonqInterfaces::DownloaderJob::startDownload(const QString& destPath, QWidget* widget, QObject* context, Functor functor)
0243 {
0244     prepareDownloadJob(widget, destPath);
0245 //With KF5, this call doesn't compile
0246 #if QT_VERSION_MAJOR > 5
0247     connect(this, &DownloaderJob::downloadResult, context, functor);
0248 #endif
0249     start();
0250 }
0251 template<typename Functor>
0252 void KonqInterfaces::DownloaderJob::startDownload(QWidget* widget, QObject* context, Functor functor)
0253 {
0254     prepareDownloadJob(widget, {});
0255 //With KF5, this call doesn't compile
0256 #if QT_VERSION_MAJOR > 5
0257     connect(this, &DownloaderJob::downloadResult, context, functor);
0258 #endif
0259     start();
0260 }
0261 
0262 #endif // DOWNLOADEREXTENSION_H