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