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

0001 /*  This file is part of the KDE project
0002 
0003     SPDX-FileCopyrightText: 1999 Matthias Elter <elter@kde.org>
0004     SPDX-FileCopyrightText: 2003 Patrick Julien <freak@codepimps.org>
0005     SPDX-FileCopyrightText: 2005 Sven Langkamp <sven.langkamp@gmail.com>
0006     SPDX-FileCopyrightText: 2007 Jan Hambrecht <jaham@gmx.net>
0007     SPDX-FileCopyrightText: 2011 Srikanth Tiyyagura <srikanth.tulasiram@gmail.com>
0008     SPDX-FileCopyrightText: 2013 Sascha Suelzer <s.suelzer@gmail.com>
0009     SPDX-FileCopyrightText: 2003-2019 Boudewijn Rempt <boud@valdyas.org>
0010 
0011     SPDX-License-Identifier: LGPL-2.1-or-later
0012  */
0013 
0014 #ifndef KORESOURCESERVER_H
0015 #define KORESOURCESERVER_H
0016 
0017 #include <QString>
0018 #include <QStringList>
0019 #include <QList>
0020 #include <QFileInfo>
0021 #include <QApplication>
0022 #include <QThread>
0023 #include <QMutex>
0024 #include <QMutexLocker>
0025 #include <QDir>
0026 #include <QTemporaryFile>
0027 
0028 #include "KoResource.h"
0029 #include "KoResourcePaths.h"
0030 #include "ksharedconfig.h"
0031 
0032 #include <KisGlobalResourcesInterface.h>
0033 #include <KisResourceLocator.h>
0034 #include <KisResourceModel.h>
0035 #include <KisTagModel.h>
0036 #include <kis_assert.h>
0037 #include <kis_debug.h>
0038 
0039 #include <ResourceDebug.h>
0040 
0041 class KoResource;
0042 
0043 template <class T>
0044 class KoResourceServerObserver
0045 {
0046 public:
0047     virtual ~KoResourceServerObserver() {}
0048 
0049     virtual void unsetResourceServer() = 0;
0050 
0051     /**
0052      * Will be called by the resource server after a resource is added
0053      * @param resource the added resource
0054      */
0055     virtual void resourceAdded(QSharedPointer<T> resource) = 0;
0056 
0057     /**
0058      * Will be called by the resource server before a resource will be removed
0059      * @param resource the resource which is going to be removed
0060      */
0061     virtual void removingResource(QSharedPointer<T> resource) = 0;
0062 
0063     /**
0064      * Will be called by the resource server when a resource is changed
0065      * @param resource the resource which is going to be removed
0066      */
0067     virtual void resourceChanged(QSharedPointer<T> resource) = 0;
0068 
0069 
0070 };
0071 
0072 /**
0073  * KoResourceServer is a shim around KisResourceModel. It knows
0074  * nothing by its own, and does nothing on its own. It can only
0075  * be used in the gui thread.
0076  */
0077 template <class T>
0078 class KoResourceServer
0079 {
0080 public:
0081 
0082     typedef KoResourceServerObserver<T> ObserverType;
0083 
0084     KoResourceServer(const QString& type)
0085         : m_resourceModel(new KisResourceModel(type))
0086         , m_tagModel(new KisTagModel(type))
0087         , m_type(type)
0088     {
0089         Q_ASSERT(!type.isEmpty());
0090         KIS_SAFE_ASSERT_RECOVER_NOOP(QThread::currentThread() == qApp->thread());
0091         if (QThread::currentThread() != qApp->thread()) {
0092             qDebug().noquote() << kisBacktrace();
0093         }
0094 
0095     }
0096 
0097     virtual ~KoResourceServer()
0098     {
0099         delete m_resourceModel;
0100         delete m_tagModel;
0101         Q_FOREACH (ObserverType* observer, m_observers) {
0102             observer->unsetResourceServer();
0103         }
0104     }
0105 
0106     /// @return the active resource model
0107     KisResourceModel *resourceModel() const
0108     {
0109         KIS_SAFE_ASSERT_RECOVER_NOOP(QThread::currentThread() == qApp->thread());
0110         if (QThread::currentThread() != qApp->thread()) {
0111             qDebug().noquote() << kisBacktrace();
0112         }
0113 
0114         return m_resourceModel;
0115     }
0116 
0117     /// Return the first resource available
0118     QSharedPointer<T> firstResource() const
0119     {
0120 
0121         KIS_SAFE_ASSERT_RECOVER_NOOP(QThread::currentThread() == qApp->thread());
0122         if (QThread::currentThread() != qApp->thread()) {
0123             qDebug().noquote() << kisBacktrace();
0124         }
0125 
0126         return m_resourceModel->resourceForIndex(m_resourceModel->index(0, 0)).dynamicCast<T>();
0127     }
0128 
0129     int resourceCount() const {
0130 
0131         KIS_SAFE_ASSERT_RECOVER_NOOP(QThread::currentThread() == qApp->thread());
0132         return m_resourceModel->rowCount();
0133     }
0134 
0135     /// Adds an already loaded resource to the server
0136     bool addResource(QSharedPointer<T> resource, bool save = true) {
0137 
0138         KIS_SAFE_ASSERT_RECOVER_NOOP(QThread::currentThread() == qApp->thread());
0139         if (QThread::currentThread() != qApp->thread()) {
0140             qDebug().noquote() << kisBacktrace();
0141         }
0142 
0143         if (!resource || !resource->valid()) {
0144             warnResource << "Tried to add an invalid resource!" << resource;
0145             return false;
0146         }
0147 
0148         if (m_resourceModel->addResource(resource, save ? QString() : "memory")) {
0149             notifyResourceAdded(resource);
0150             return true;
0151         }
0152 
0153         return false;
0154     }
0155 
0156     /// Remove a resource from Resource Server but not from a file
0157     bool removeResourceFromServer(QSharedPointer<T> resource){
0158 
0159         KIS_SAFE_ASSERT_RECOVER_NOOP(QThread::currentThread() == qApp->thread());
0160         if (QThread::currentThread() != qApp->thread()) {
0161             qDebug().noquote() << kisBacktrace();
0162         }
0163 
0164         if (m_resourceModel->setResourceInactive(m_resourceModel->indexForResource(resource))) {
0165             notifyRemovingResource(resource);
0166             return true;
0167         }
0168         return false;
0169     }
0170 
0171     /// Returns path where to save user defined and imported resources to
0172     QString saveLocation() {
0173         return KisResourceLocator::instance()->resourceLocationBase() + m_type;
0174     }
0175 
0176     /**
0177      * Creates a new resource from a given file and adds them to the resource server
0178      * The base implementation does only load one resource per file, override to implement collections
0179      * @param filename file name of the resource file to be imported
0180      * @param fileCreation decides whether to create the file in the saveLocation() directory
0181      */
0182     KoResourceSP importResourceFile(const QString &filename, const bool allowOverwrite)
0183     {
0184 
0185         KIS_SAFE_ASSERT_RECOVER_NOOP(QThread::currentThread() == qApp->thread());
0186         if (QThread::currentThread() != qApp->thread()) {
0187             qDebug().noquote() << kisBacktrace();
0188         }
0189 
0190         return m_resourceModel->importResourceFile(filename, allowOverwrite);
0191     }
0192 
0193     /// Removes the resource file from the resource server
0194     void removeResourceFile(const QString & filename)
0195     {
0196         QFileInfo fi(filename);
0197 
0198         QSharedPointer<T> resource = resourceByFilename(fi.fileName());
0199         if (!resource) {
0200             warnResource << "Resource file do not exist ";
0201             return;
0202         }
0203         removeResourceFromServer(resource);
0204     }
0205 
0206     /**
0207      * Adds an observer to the server
0208      * @param observer the observer to be added
0209      * @param notifyLoadedResources determines if the observer should be notified about the already loaded resources
0210      */
0211     void addObserver(ObserverType* observer)
0212     {
0213         if (observer && !m_observers.contains(observer)) {
0214             m_observers.append(observer);
0215         }
0216     }
0217 
0218     /**
0219      * Removes an observer from the server
0220      * @param observer the observer to be removed
0221      */
0222     void removeObserver(ObserverType* observer)
0223     {
0224         int index = m_observers.indexOf(observer);
0225         if (index < 0) {
0226             return;
0227         }
0228         m_observers.removeAt( index );
0229     }
0230 
0231 private:
0232 
0233     QSharedPointer<T> resourceByFilename(const QString& filename) const
0234     {
0235         KIS_SAFE_ASSERT_RECOVER_NOOP(QThread::currentThread() == qApp->thread());
0236         if (QThread::currentThread() != qApp->thread()) {
0237             qDebug().noquote() << kisBacktrace();
0238         }
0239 
0240 
0241         if (filename.isEmpty() || filename.isNull()) {
0242             return nullptr;
0243         }
0244         QVector<KoResourceSP> resources = m_resourceModel->resourcesForFilename(filename);
0245 
0246         if (resources.size() > 0) {
0247             return resources.first().dynamicCast<T>();
0248         }
0249 
0250         return nullptr;
0251     }
0252 
0253 
0254     QSharedPointer<T> resourceByName(const QString& name) const
0255     {
0256         KIS_SAFE_ASSERT_RECOVER_NOOP(QThread::currentThread() == qApp->thread());
0257         if (QThread::currentThread() != qApp->thread()) {
0258             qDebug().noquote() << kisBacktrace();
0259         }
0260 
0261         if (name.isEmpty() || name.isNull()) {
0262             return nullptr;
0263         }
0264 
0265         QVector<KoResourceSP> resources = m_resourceModel->resourcesForName(name);
0266 
0267         if (resources.size() > 0) {
0268             return resources.first().dynamicCast<T>();
0269         }
0270 
0271         return nullptr;
0272 
0273     }
0274 
0275     QSharedPointer<T> resourceByMD5(const QString& md5) const
0276     {
0277         KIS_SAFE_ASSERT_RECOVER_NOOP(QThread::currentThread() == qApp->thread());
0278         if (QThread::currentThread() != qApp->thread()) {
0279             qDebug().noquote() << kisBacktrace();
0280         }
0281         if (md5.isEmpty() || md5.isNull()) {
0282             return nullptr;
0283         }
0284         QVector<KoResourceSP> resources = m_resourceModel->resourcesForMD5(md5);
0285 
0286         if (resources.size() > 0) {
0287             return resources.first().dynamicCast<T>();
0288         }
0289 
0290         return nullptr;
0291     }
0292 
0293 public:
0294 
0295     /**
0296      * @brief resource retrieves a resource. If the md5sum is not empty, the resource
0297      * will only be retrieved if a resource with that md5sum exists. If it is empty,
0298      * a fallback to filename or name is possible.
0299      * @param md5 This is the hex-encoded md5sum as stored in e.g. configuration objects
0300      * @param fileName A filename without the path
0301      * @param name The name of the resource
0302      * @return a resource, or nullptr
0303      */
0304     QSharedPointer<T> resource(const QString &md5, const QString &fileName, const QString &name)
0305     {
0306         return KisGlobalResourcesInterface::instance()->source<T>(m_type).bestMatch(md5, fileName, name);
0307     }
0308 
0309     /**
0310      * Call after changing the content of a resource and saving it;
0311      * Notifies the connected views.
0312      */
0313     bool updateResource(QSharedPointer<T> resource)
0314     {
0315         KIS_SAFE_ASSERT_RECOVER_NOOP(QThread::currentThread() == qApp->thread());
0316         if (QThread::currentThread() != qApp->thread()) {
0317             qDebug().noquote() << kisBacktrace();
0318         }
0319         bool result = m_resourceModel->updateResource(resource);
0320         notifyResourceChanged(resource);
0321         return result;
0322     }
0323 
0324     /**
0325      * Reloads the resource from the persistent storage
0326      */
0327     bool reloadResource(QSharedPointer<T> resource)
0328     {
0329         KIS_SAFE_ASSERT_RECOVER_NOOP(QThread::currentThread() == qApp->thread());
0330         if (QThread::currentThread() != qApp->thread()) {
0331             qDebug().noquote() << kisBacktrace();
0332         }
0333         bool result = m_resourceModel->reloadResource(resource);
0334         notifyResourceChanged(resource);
0335 
0336         return result;
0337     }
0338 
0339     QVector<KisTagSP> assignedTagsList(KoResourceSP resource) const
0340     {
0341         if (resource.isNull()) {
0342             return QVector<KisTagSP>();
0343         }
0344         return m_resourceModel->tagsForResource(resource->resourceId());
0345     }
0346 
0347 protected:
0348 
0349     void notifyResourceAdded(QSharedPointer<T> resource)
0350     {
0351         Q_FOREACH (ObserverType* observer, m_observers) {
0352             observer->resourceAdded(resource);
0353         }
0354     }
0355 
0356     void notifyRemovingResource(QSharedPointer<T> resource)
0357     {
0358         Q_FOREACH (ObserverType* observer, m_observers) {
0359             observer->removingResource(resource);
0360         }
0361     }
0362 
0363     void notifyResourceChanged(QSharedPointer<T> resource)
0364     {
0365         Q_FOREACH (ObserverType* observer, m_observers) {
0366             observer->resourceChanged(resource);
0367         }
0368     }
0369 
0370 private:
0371 
0372     QList<ObserverType*> m_observers;
0373     KisResourceModel *m_resourceModel {0};
0374     KisTagModel *m_tagModel {0};
0375     QString m_type;
0376 };
0377 
0378 #endif // KORESOURCESERVER_H