File indexing completed on 2024-05-12 15:59:51

0001 /*
0002  *  SPDX-FileCopyrightText: 2020 Dmitry Kazakov <dimula73@gmail.com>
0003  *
0004  *  SPDX-License-Identifier: GPL-2.0-or-later
0005  */
0006 #ifndef KISRESOURCESINTERFACE_H
0007 #define KISRESOURCESINTERFACE_H
0008 
0009 #include "kritaresources_export.h"
0010 
0011 #include <QScopedPointer>
0012 #include <KoResource.h>
0013 #include <QHash>
0014 #include <KoResourceLoadResult.h>
0015 
0016 class QString;
0017 class QByteArray;
0018 class KisResourcesInterfacePrivate;
0019 
0020 /**
0021  * @brief a provider-like interface class for accessing resource sources in Krita.
0022  *
0023  * Main differences to KoResourceServer and KisResourceModel:
0024  *
0025  *  1) It is a polymorphic class. Therefore, we are not obliged to pass
0026        a pointer to the global gui-only resource storage everywhere. Instead,
0027        we can create temporary storages and pass them to the strokes, when needed.
0028 
0029     2) The class doesn't depend on any specific resource types. Its baseline
0030        implementation of resourceInterface->source(type) returns a source
0031        working with KoResourceSP only. But when needed, the caller may request
0032        a typed version via resourcesInterface->source<KisBrush>(type). It
0033        will instantiate a templated wrapper **in the caller's** object file,
0034        not in kritaresources library. It solves linking problem:
0035        we have a source for KisBrush objects in kritaresources library,
0036        even though this library doesn't link kritabrush.
0037 
0038     3) Since strokes may have local storages for the resources, we operate
0039        with resources sources using shared pointers, not raw pointers.
0040  */
0041 class KRITARESOURCES_EXPORT KisResourcesInterface
0042 {
0043 public:
0044     class KRITARESOURCES_EXPORT ResourceSourceAdapter
0045     {
0046     public:
0047         ResourceSourceAdapter(const QString &type);
0048         virtual ~ResourceSourceAdapter();
0049 //protected:
0050         friend class KisResourcesInterface;
0051         virtual QVector<KoResourceSP> resourcesForFilename(const QString& filename) const = 0;
0052         virtual QVector<KoResourceSP> resourcesForName(const QString& name) const = 0;
0053         virtual QVector<KoResourceSP> resourcesForMD5(const QString& md5) const = 0;
0054 public:
0055         /**
0056          * @brief bestMatch retrieves a resource, prefarably by md5, but with filename and name
0057          * as fallback for older files that do not store the md5sum. If the resource is
0058          * not found by md5 and the md5 isn't empty, then it will try to fallback to searching
0059          * by filename, but will show a warning in case sanity checks are enabled.
0060          *
0061          * If multiple resources with the same md5 exist, then it prefers the one
0062          * with the same filename and name.
0063          *
0064          * @return a resource, or 0 of the resource doesn't exist.
0065          */
0066 
0067         KoResourceSP bestMatch(const QString md5, const QString filename, const QString name);
0068 
0069         /**
0070          * Same as bestMatch(), but returns KoResourceLoadResult. In case the
0071          * resource is not found in the backend storage, the load-result
0072          * will be set in FailedLink state
0073          *
0074          */
0075         KoResourceLoadResult bestMatchLoadResult(const QString md5, const QString filename, const QString name);
0076 
0077         virtual KoResourceSP fallbackResource() const = 0;
0078 
0079     private:
0080         Q_DISABLE_COPY(ResourceSourceAdapter);
0081         const QString m_type;
0082     };
0083 
0084     template <typename T>
0085     class TypedResourceSourceAdapter
0086     {
0087     public:
0088         TypedResourceSourceAdapter(ResourceSourceAdapter *adapter)
0089             : m_source(adapter)
0090         {
0091         }
0092 private:
0093         QVector<QSharedPointer<T>> resourcesForFilename(const QString& filename) const
0094         {
0095             QVector<QSharedPointer<T>> r;
0096             Q_FOREACH(KoResourceSP resource, m_source->resourcesForFilename(filename)) {
0097                 r << resource.dynamicCast<T>();
0098             }
0099             return r;
0100         }
0101 
0102         QVector<QSharedPointer<T>> resourcesForName(const QString& name) const
0103         {
0104             QVector<QSharedPointer<T>> r;
0105             Q_FOREACH(KoResourceSP resource, m_source->resourcesForName(name)) {
0106                 r << resource.dynamicCast<T>();
0107             }
0108             return r;
0109         }
0110 
0111         QVector<QSharedPointer<T>> resourcesForMD5(const QString& md5) const
0112         {
0113             QVector<QSharedPointer<T>> r;
0114             Q_FOREACH(KoResourceSP resource, m_source->resourcesForMD5(md5)) {
0115                 r << resource.dynamicCast<T>();
0116             }
0117             return r;
0118         }
0119 public:
0120         /**
0121          * @brief resource retrieves a resource, prefarably by md5, but with filename and name
0122          * as fallback for older files that do not store the md5sum. Note that if the resource is
0123          * not found by md5 if the md5 isn't empty, we do NOT then look by filename.
0124          *
0125          * If multiple resources with the same md5 exist, then it prefers the one
0126          * with the same filename and name.
0127          *
0128          * @return a resource, or 0 of the resource doesn't exist.
0129          */
0130 
0131         QSharedPointer<T>  bestMatch(const QString md5, const QString filename, const QString name) {
0132             return m_source->bestMatch(md5, filename, name).dynamicCast<T>();
0133         }
0134 
0135         /**
0136          * Same as bestMatch(), but returns KoResourceLoadResult. In case the
0137          * resource is not found in the backend storage, the load-result
0138          * will be set in FailedLink state
0139          *
0140          */
0141         KoResourceLoadResult bestMatchLoadResult(const QString md5, const QString filename, const QString name) {
0142             return m_source->bestMatchLoadResult(md5, filename, name);
0143         }
0144 
0145         QSharedPointer<T> fallbackResource() const
0146         {
0147             return m_source->fallbackResource().dynamicCast<T>();
0148         }
0149 
0150     protected:
0151         ResourceSourceAdapter *m_source;
0152     };
0153 
0154 public:
0155     KisResourcesInterface();
0156     virtual ~KisResourcesInterface();
0157 
0158     /**
0159      * A basic implementation that returns a source for a specific type
0160      * of the resource. Please take into account that this source object will
0161      * return un-casted resources of type KoResourceSP. If you want to have a
0162      * proper resource (in most of the cases), use a `server<T>(type)` instead.
0163      */
0164     ResourceSourceAdapter& source(const QString &type) const;
0165 
0166     /**
0167      * The main fetcher of resource source for resources of a specific type.
0168      *
0169      * Usage:
0170      *
0171      * \code{.cpp}
0172      *
0173      * auto source = resourceInterface->source<KisBrush>(ResourceType::Brushes);
0174      * KisBrushSP brush = source.resourceByMd5(md5)
0175      *
0176      * \endcode
0177      *
0178      */
0179     template<typename T>
0180     TypedResourceSourceAdapter<T> source(const QString &type) const {
0181         return TypedResourceSourceAdapter<T>(&this->source(type));
0182     }
0183 
0184 protected:
0185     KisResourcesInterface(KisResourcesInterfacePrivate *dd);
0186     virtual ResourceSourceAdapter* createSourceImpl(const QString &type) const = 0;
0187 
0188 protected:
0189     QScopedPointer<KisResourcesInterfacePrivate> d_ptr;
0190 
0191 private:
0192     Q_DECLARE_PRIVATE(KisResourcesInterface)
0193 };
0194 
0195 using KisResourcesInterfaceSP = QSharedPointer<KisResourcesInterface>;
0196 
0197 #endif // KISRESOURCESINTERFACE_H