Warning, file /office/calligra/libs/widgets/KoResourceServer.h was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).
0001 /* This file is part of the KDE project 0002 0003 Copyright (c) 1999 Matthias Elter <elter@kde.org> 0004 Copyright (c) 2003 Patrick Julien <freak@codepimps.org> 0005 Copyright (c) 2005 Sven Langkamp <sven.langkamp@gmail.com> 0006 Copyright (c) 2007 Jan Hambrecht <jaham@gmx.net> 0007 Copyright (C) 2011 Srikanth Tiyyagura <srikanth.tulasiram@gmail.com> 0008 Copyright (c) 2013 Sascha Suelzer <s.suelzer@gmail.com> 0009 0010 This library is free software; you can redistribute it and/or 0011 modify it under the terms of the GNU Lesser General Public 0012 License as published by the Free Software Foundation; either 0013 version 2.1 of the License, or (at your option) any later version. 0014 0015 This library is distributed in the hope that it will be useful, 0016 but WITHOUT ANY WARRANTY; without even the implied warranty of 0017 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 0018 Lesser General Public License for more details. 0019 0020 You should have received a copy of the GNU Lesser General Public 0021 License along with this library; if not, write to the Free Software 0022 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 0023 */ 0024 0025 #ifndef KORESOURCESERVER_H 0026 #define KORESOURCESERVER_H 0027 0028 #include <QMutex> 0029 #include <QString> 0030 #include <QStringList> 0031 #include <QList> 0032 #include <QFileInfo> 0033 #include <QDir> 0034 0035 #include <QTemporaryFile> 0036 #include <QDomDocument> 0037 #include "KoResource.h" 0038 #include "KoResourceServerPolicies.h" 0039 #include "KoResourceServerObserver.h" 0040 #include "KoResourceTagStore.h" 0041 #include "KoResourcePaths.h" 0042 0043 #include "kowidgets_export.h" 0044 #include "WidgetsDebug.h" 0045 0046 class KoResource; 0047 0048 /** 0049 * KoResourceServerBase is the base class of all resource servers 0050 */ 0051 class KOWIDGETS_EXPORT KoResourceServerBase { 0052 0053 public: 0054 /** 0055 * Constructs a KoResourceServerBase 0056 * @param type type, has to be the same as used by KoResourcePaths 0057 * @param extensions the file extensions separate by ':', e.g. "*.kgr:*.svg:*.ggr" 0058 */ 0059 KoResourceServerBase(const QString& type, const QString& extensions) 0060 : m_type(type) 0061 , m_extensions(extensions) 0062 { 0063 } 0064 0065 virtual ~KoResourceServerBase() {} 0066 0067 virtual int resourceCount() const = 0; 0068 virtual void loadResources(QStringList filenames) = 0; 0069 virtual QStringList blackListedFiles() const = 0; 0070 virtual QStringList queryResources(const QString &query) const = 0; 0071 QString type() const { return m_type; } 0072 0073 /** 0074 * File extensions for resources of the server 0075 * @returns the file extensions separated by ':', e.g. "*.kgr:*.svg:*.ggr" 0076 */ 0077 QString extensions() const { return m_extensions; } 0078 0079 QStringList fileNames() const 0080 { 0081 QStringList extensionList = m_extensions.split(':'); 0082 QStringList fileNames; 0083 0084 foreach (const QString &extension, extensionList) { 0085 fileNames += KoResourcePaths::findAllResources(type().toLatin1(), extension, KoResourcePaths::Recursive | KoResourcePaths::NoDuplicates); 0086 0087 } 0088 return fileNames; 0089 } 0090 0091 protected: 0092 0093 friend class KoResourceTagStore; 0094 virtual KoResource *byMd5(const QByteArray &md5) const = 0; 0095 virtual KoResource *byFileName(const QString &fileName) const = 0; 0096 0097 private: 0098 QString m_type; 0099 QString m_extensions; 0100 0101 protected: 0102 0103 QMutex m_loadLock; 0104 0105 }; 0106 0107 /** 0108 * KoResourceServer manages the resources of one type. It stores, 0109 * loads and saves the resources. To keep track of changes the server 0110 * can be observed with a KoResourceServerObserver 0111 * 0112 * The \p Policy template parameter defines the way how the lifetime 0113 0114 * of a resource is handled. There are to predefined policies: 0115 0116 * 0117 * o PointerStoragePolicy --- usual pointers with ownership over 0118 * the resource. 0119 0120 * o SharedPointerStoragePolicy --- shared pointers. The server does no 0121 * extra handling for the lifetime of 0122 * the resource. 0123 * 0124 * Use the former for usual resources and the latter for shared pointer based 0125 * ones. 0126 */ 0127 0128 template <class T, class Policy = PointerStoragePolicy<T> > 0129 class KoResourceServer : public KoResourceServerBase 0130 { 0131 public: 0132 typedef typename Policy::PointerType PointerType; 0133 typedef KoResourceServerObserver<T, Policy> ObserverType; 0134 KoResourceServer(const QString& type, const QString& extensions) 0135 : KoResourceServerBase(type, extensions) 0136 { 0137 m_blackListFile = KoResourcePaths::locateLocal("data", "krita/" + type + ".blacklist"); 0138 m_blackListFileNames = readBlackListFile(); 0139 m_tagStore = new KoResourceTagStore(this); 0140 m_tagStore->loadTags(); 0141 } 0142 0143 ~KoResourceServer() override 0144 { 0145 if (m_tagStore) { 0146 delete m_tagStore; 0147 } 0148 0149 foreach(ObserverType* observer, m_observers) { 0150 observer->unsetResourceServer(); 0151 } 0152 0153 foreach(PointerType res, m_resources) { 0154 Policy::deleteResource(res); 0155 } 0156 0157 m_resources.clear(); 0158 0159 } 0160 0161 int resourceCount() const override { 0162 return m_resources.size(); 0163 } 0164 0165 /** 0166 * Loads a set of resources and adds them to the resource server. 0167 * If a filename appears twice the resource will only be added once. Resources that can't 0168 * be loaded or and invalid aren't added to the server. 0169 * @param filenames list of filenames to be loaded 0170 */ 0171 void loadResources(QStringList filenames) override { 0172 0173 QStringList uniqueFiles; 0174 0175 while (!filenames.empty()) { 0176 0177 QString front = filenames.first(); 0178 filenames.pop_front(); 0179 0180 // In the save location, people can use sub-folders... And then they probably want 0181 // to load both versions! See https://bugs.kde.org/show_bug.cgi?id=321361. 0182 QString fname; 0183 if (front.contains(saveLocation())) { 0184 fname = front.split(saveLocation())[1]; 0185 } 0186 else { 0187 fname = QFileInfo(front).fileName(); 0188 } 0189 0190 // XXX: Don't load resources with the same filename. Actually, we should look inside 0191 // the resource to find out whether they are really the same, but for now this 0192 // will prevent the same brush etc. showing up twice. 0193 if (!uniqueFiles.contains(fname)) { 0194 m_loadLock.lock(); 0195 uniqueFiles.append(fname); 0196 QList<PointerType> resources = createResources(front); 0197 foreach(PointerType resource, resources) { 0198 Q_CHECK_PTR(resource); 0199 if (resource->load() && resource->valid() && !resource->md5().isEmpty()) { 0200 QByteArray md5 = resource->md5(); 0201 m_resourcesByMd5[md5] = resource; 0202 0203 m_resourcesByFilename[resource->shortFilename()] = resource; 0204 0205 if (resource->name().isEmpty()) { 0206 resource->setName(fname); 0207 } 0208 if (m_resourcesByName.contains(resource->name())) { 0209 resource->setName(resource->name() + "(" + resource->shortFilename() + ")"); 0210 } 0211 m_resourcesByName[resource->name()] = resource; 0212 notifyResourceAdded(resource); 0213 } 0214 else { 0215 warnWidgets << "Loading resource " << front << "failed"; 0216 Policy::deleteResource(resource); 0217 } 0218 } 0219 m_loadLock.unlock(); 0220 } 0221 } 0222 0223 m_resources = sortedResources(); 0224 0225 foreach(ObserverType* observer, m_observers) { 0226 observer->syncTaggedResourceView(); 0227 } 0228 0229 debugWidgets << "done loading resources for type " << type(); 0230 } 0231 0232 0233 /// Adds an already loaded resource to the server 0234 bool addResource(PointerType resource, bool save = true, bool infront = false) { 0235 if (!resource->valid()) { 0236 warnWidgets << "Tried to add an invalid resource!"; 0237 return false; 0238 } 0239 0240 if (save) { 0241 QFileInfo fileInfo(resource->filename()); 0242 0243 QDir d(fileInfo.path()); 0244 if (!d.exists()) { 0245 d.mkdir(fileInfo.path()); 0246 } 0247 0248 if (fileInfo.exists()) { 0249 QString filename = fileInfo.path() + "/" + fileInfo.baseName() + "XXXXXX" + "." + fileInfo.suffix(); 0250 debugWidgets << "fileName is " << filename; 0251 QTemporaryFile file(filename); 0252 if (file.open()) { 0253 debugWidgets << "now " << file.fileName(); 0254 resource->setFilename(file.fileName()); 0255 } 0256 } 0257 0258 if (!resource->save()) { 0259 warnWidgets << "Could not save resource!"; 0260 return false; 0261 } 0262 } 0263 0264 Q_ASSERT(!resource->filename().isEmpty() || !resource->name().isEmpty()); 0265 if (resource->filename().isEmpty()) { 0266 resource->setFilename(resource->name()); 0267 } 0268 else if (resource->name().isEmpty()) { 0269 resource->setName(resource->filename()); 0270 } 0271 0272 m_resourcesByFilename[resource->shortFilename()] = resource; 0273 m_resourcesByMd5[resource->md5()] = resource; 0274 m_resourcesByName[resource->name()] = resource; 0275 if (infront) { 0276 m_resources.insert(0, resource); 0277 } 0278 else { 0279 m_resources.append(resource); 0280 } 0281 0282 notifyResourceAdded(resource); 0283 0284 return true; 0285 } 0286 0287 /*Removes a given resource from the blacklist. 0288 */ 0289 bool removeFromBlacklist(PointerType resource) { 0290 if (m_blackListFileNames.contains(resource->filename())) { 0291 m_blackListFileNames.removeAll(resource->filename()); 0292 writeBlackListFile(); 0293 } else{ 0294 warnWidgets<<"Doesn't contain filename"; 0295 return false; 0296 } 0297 0298 0299 //then return true// 0300 return true; 0301 } 0302 /// Remove a resource from Resource Server but not from a file 0303 bool removeResourceFromServer(PointerType resource){ 0304 if ( !m_resourcesByFilename.contains( resource->shortFilename() ) ) { 0305 return false; 0306 } 0307 m_resourcesByMd5.remove(resource->md5()); 0308 m_resourcesByName.remove(resource->name()); 0309 m_resourcesByFilename.remove(resource->shortFilename()); 0310 m_resources.removeAt(m_resources.indexOf(resource)); 0311 m_tagStore->removeResource(resource); 0312 notifyRemovingResource(resource); 0313 0314 Policy::deleteResource(resource); 0315 return true; 0316 } 0317 0318 /// Remove a resource from the resourceserver and blacklist it 0319 0320 bool removeResourceAndBlacklist(PointerType resource) { 0321 0322 if ( !m_resourcesByFilename.contains( resource->shortFilename() ) ) { 0323 return false; 0324 } 0325 m_resourcesByMd5.remove(resource->md5()); 0326 m_resourcesByName.remove(resource->name()); 0327 m_resourcesByFilename.remove(resource->shortFilename()); 0328 m_resources.removeAt(m_resources.indexOf(resource)); 0329 m_tagStore->removeResource(resource); 0330 notifyRemovingResource(resource); 0331 0332 m_blackListFileNames.append(resource->filename()); 0333 writeBlackListFile(); 0334 Policy::deleteResource(resource); 0335 return true; 0336 } 0337 0338 QList<PointerType> resources() { 0339 m_loadLock.lock(); 0340 QList<PointerType> resourceList = m_resources; 0341 foreach(PointerType r, m_resourceBlackList) { 0342 resourceList.removeOne(r); 0343 } 0344 m_loadLock.unlock(); 0345 return resourceList; 0346 } 0347 0348 /// Returns path where to save user defined and imported resources to 0349 virtual QString saveLocation() { 0350 return KoResourcePaths::saveLocation(type().toLatin1()); 0351 } 0352 0353 /** 0354 * Creates a new resource from a given file and adds them to the resource server 0355 * The base implementation does only load one resource per file, override to implement collections 0356 * @param filename file name of the resource file to be imported 0357 * @param fileCreation decides whether to create the file in the saveLocation() directory 0358 */ 0359 virtual bool importResourceFile(const QString & filename , bool fileCreation=true) { 0360 0361 QFileInfo fi(filename); 0362 if (!fi.exists()) 0363 return false; 0364 if ( fi.size() == 0) 0365 return false; 0366 0367 PointerType resource = createResource( filename ); 0368 resource->load(); 0369 if (!resource->valid()) { 0370 warnWidgets << "Import failed! Resource is not valid"; 0371 Policy::deleteResource(resource); 0372 0373 return false; 0374 0375 } 0376 0377 if (fileCreation) { 0378 Q_ASSERT(!resource->defaultFileExtension().isEmpty()); 0379 Q_ASSERT(!saveLocation().isEmpty()); 0380 0381 QString newFilename = saveLocation() + fi.baseName() + resource->defaultFileExtension(); 0382 QFileInfo fileInfo(newFilename); 0383 0384 int i = 1; 0385 while (fileInfo.exists()) { 0386 fileInfo.setFile(saveLocation() + fi.baseName() + QString("%1").arg(i) + resource->defaultFileExtension()); 0387 i++; 0388 } 0389 resource->setFilename(fileInfo.filePath()); 0390 } 0391 0392 0393 if(!addResource(resource)) { 0394 Policy::deleteResource(resource); 0395 } 0396 0397 return true; 0398 } 0399 0400 /// Removes the resource file from the resource server 0401 virtual void removeResourceFile(const QString & filename) 0402 { 0403 QFileInfo fi(filename); 0404 0405 PointerType resource = resourceByFilename(fi.fileName()); 0406 if (!resource) { 0407 warnWidgets << "Resource file do not exist "; 0408 return; 0409 } 0410 0411 if (!removeResourceFromServer(resource)) 0412 return; 0413 } 0414 0415 0416 /** 0417 * Addes an observer to the server 0418 * @param observer the observer to be added 0419 * @param notifyLoadedResources determines if the observer should be notified about the already loaded resources 0420 */ 0421 void addObserver(ObserverType* observer, bool notifyLoadedResources = true) 0422 { 0423 m_loadLock.lock(); 0424 if(observer && !m_observers.contains(observer)) { 0425 m_observers.append(observer); 0426 0427 if(notifyLoadedResources) { 0428 foreach(PointerType resource, m_resourcesByFilename) { 0429 observer->resourceAdded(resource); 0430 0431 } 0432 } 0433 } 0434 m_loadLock.unlock(); 0435 } 0436 0437 /** 0438 * Removes an observer from the server 0439 * @param observer the observer to be removed 0440 */ 0441 void removeObserver(ObserverType* observer) 0442 { 0443 int index = m_observers.indexOf( observer ); 0444 if( index < 0 ) 0445 return; 0446 0447 m_observers.removeAt( index ); 0448 } 0449 0450 PointerType resourceByFilename(const QString& filename) const 0451 { 0452 if (m_resourcesByFilename.contains(filename)) { 0453 return m_resourcesByFilename[filename]; 0454 } 0455 return 0; 0456 } 0457 0458 0459 PointerType resourceByName( const QString& name ) const 0460 { 0461 if (m_resourcesByName.contains(name)) { 0462 return m_resourcesByName[name]; 0463 } 0464 return 0; 0465 } 0466 0467 PointerType resourceByMD5(const QByteArray& md5) const 0468 { 0469 return m_resourcesByMd5.value(md5); 0470 } 0471 0472 /** 0473 * Call after changing the content of a resource; 0474 * Notifies the connected views. 0475 */ 0476 void updateResource( PointerType resource ) 0477 { 0478 notifyResourceChanged(resource); 0479 } 0480 0481 QStringList blackListedFiles() const override 0482 { 0483 return m_blackListFileNames; 0484 } 0485 0486 void removeBlackListedFiles() { 0487 QStringList remainingFiles; // Files that can't be removed e.g. no rights will stay blacklisted 0488 foreach(const QString &filename, m_blackListFileNames) { 0489 QFile file( filename ); 0490 if( ! file.remove() ) { 0491 remainingFiles.append(filename); 0492 } 0493 } 0494 m_blackListFileNames = remainingFiles; 0495 writeBlackListFile(); 0496 } 0497 0498 QStringList tagNamesList() const 0499 { 0500 return m_tagStore->tagNamesList(); 0501 } 0502 0503 // don't use these method directly since it doesn't update views! 0504 void addTag( KoResource* resource,const QString& tag) 0505 { 0506 m_tagStore->addTag(resource,tag); 0507 } 0508 0509 // don't use these method directly since it doesn't update views! 0510 void delTag( KoResource* resource,const QString& tag) 0511 { 0512 m_tagStore->delTag(resource,tag); 0513 } 0514 0515 QStringList searchTag(const QString& lineEditText) 0516 { 0517 return m_tagStore->searchTag(lineEditText); 0518 } 0519 0520 void tagCategoryAdded(const QString& tag) 0521 { 0522 m_tagStore->serializeTags(); 0523 foreach(ObserverType* observer, m_observers) { 0524 observer->syncTagAddition(tag); 0525 } 0526 } 0527 0528 void tagCategoryRemoved(const QString& tag) 0529 { 0530 m_tagStore->delTag(tag); 0531 m_tagStore->serializeTags(); 0532 foreach(ObserverType* observer, m_observers) { 0533 observer->syncTagRemoval(tag); 0534 } 0535 } 0536 0537 void tagCategoryMembersChanged() 0538 { 0539 m_tagStore->serializeTags(); 0540 foreach(ObserverType* observer, m_observers) { 0541 observer->syncTaggedResourceView(); 0542 } 0543 } 0544 0545 QStringList queryResources(const QString &query) const override 0546 { 0547 return m_tagStore->searchTag(query); 0548 } 0549 0550 QStringList assignedTagsList(KoResource* resource) const 0551 { 0552 return m_tagStore->assignedTagsList(resource); 0553 } 0554 0555 0556 /** 0557 * Create one or more resources from a single file. By default one resource is created. 0558 * Override to create more resources from the file. 0559 * @param filename the filename of the resource or resource collection 0560 */ 0561 virtual QList<PointerType> createResources( const QString & filename ) 0562 { 0563 QList<PointerType> createdResources; 0564 createdResources.append(createResource(filename)); 0565 return createdResources; 0566 } 0567 0568 virtual PointerType createResource( const QString & filename ) = 0; 0569 0570 /// Return the currently stored resources in alphabetical order, overwrite for customized sorting 0571 virtual QList<PointerType> sortedResources() 0572 { 0573 QMap<QString, PointerType> sortedNames; 0574 foreach(const QString &name, m_resourcesByName.keys()) { 0575 sortedNames.insert(name.toLower(), m_resourcesByName[name]); 0576 } 0577 return sortedNames.values(); 0578 } 0579 0580 protected: 0581 0582 void notifyResourceAdded(PointerType resource) 0583 { 0584 foreach(ObserverType* observer, m_observers) { 0585 observer->resourceAdded(resource); 0586 } 0587 } 0588 0589 void notifyRemovingResource(PointerType resource) 0590 { 0591 foreach(ObserverType* observer, m_observers) { 0592 observer->removingResource(resource); 0593 } 0594 } 0595 0596 void notifyResourceChanged(PointerType resource) 0597 { 0598 foreach(ObserverType* observer, m_observers) { 0599 observer->resourceChanged(resource); 0600 } 0601 } 0602 0603 /// Reads the xml file and returns the filenames as a list 0604 QStringList readBlackListFile() 0605 { 0606 QStringList filenameList; 0607 0608 QFile f(m_blackListFile); 0609 if (!f.open(QIODevice::ReadOnly)) { 0610 return filenameList; 0611 } 0612 0613 QDomDocument doc; 0614 if (!doc.setContent(&f)) { 0615 warnWidgets << "The file could not be parsed."; 0616 return filenameList; 0617 } 0618 0619 QDomElement root = doc.documentElement(); 0620 if (root.tagName() != "resourceFilesList") { 0621 warnWidgets << "The file doesn't seem to be of interest."; 0622 return filenameList; 0623 } 0624 0625 QDomElement file = root.firstChildElement("file"); 0626 0627 while (!file.isNull()) { 0628 QDomNode n = file.firstChild(); 0629 QDomElement e = n.toElement(); 0630 if (e.tagName() == "name") { 0631 filenameList.append((e.text()).replace(QString("~"),QDir::homePath())); 0632 } 0633 file = file.nextSiblingElement("file"); 0634 } 0635 return filenameList; 0636 } 0637 0638 /// write the blacklist file entries to an xml file 0639 void writeBlackListFile() 0640 { 0641 QDir().mkpath(QFileInfo(m_blackListFile).path()); 0642 QFile f(m_blackListFile); 0643 if (!f.open(QIODevice::WriteOnly | QIODevice::Text)) { 0644 warnWidgets << "Cannot write meta information to '" << m_blackListFile << "'." << endl; 0645 return; 0646 } 0647 0648 QDomDocument doc; 0649 QDomElement root; 0650 0651 QDomDocument docTemp("m_blackListFile"); 0652 doc = docTemp; 0653 doc.appendChild(doc.createProcessingInstruction("xml", "version=\"1.0\" encoding=\"UTF-8\"")); 0654 root = doc.createElement("resourceFilesList"); 0655 doc.appendChild(root); 0656 0657 foreach(QString filename, m_blackListFileNames) { 0658 QDomElement fileEl = doc.createElement("file"); 0659 QDomElement nameEl = doc.createElement("name"); 0660 QDomText nameText = doc.createTextNode(filename.replace(QDir::homePath(),QString("~"))); 0661 nameEl.appendChild(nameText); 0662 fileEl.appendChild(nameEl); 0663 root.appendChild(fileEl); 0664 } 0665 0666 QTextStream metastream(&f); 0667 metastream << doc.toString(); 0668 f.close(); 0669 } 0670 0671 protected: 0672 0673 KoResource* byMd5(const QByteArray &md5) const override 0674 { 0675 return Policy::toResourcePointer(resourceByMD5(md5)); 0676 } 0677 0678 KoResource* byFileName(const QString &fileName) const override 0679 { 0680 return Policy::toResourcePointer(resourceByFilename(fileName)); 0681 } 0682 0683 private: 0684 0685 QHash<QString, PointerType> m_resourcesByName; 0686 QHash<QString, PointerType> m_resourcesByFilename; 0687 QHash<QByteArray, PointerType> m_resourcesByMd5; 0688 0689 QList<PointerType> m_resourceBlackList; 0690 QList<PointerType> m_resources; ///< list of resources in order of addition 0691 QList<ObserverType*> m_observers; 0692 QString m_blackListFile; 0693 QStringList m_blackListFileNames; 0694 KoResourceTagStore* m_tagStore; 0695 0696 }; 0697 0698 template <class T, class Policy = PointerStoragePolicy<T> > 0699 class KoResourceServerSimpleConstruction : public KoResourceServer<T, Policy> 0700 { 0701 public: 0702 KoResourceServerSimpleConstruction(const QString& type, const QString& extensions) 0703 : KoResourceServer<T, Policy>(type, extensions) 0704 { 0705 } 0706 0707 typename KoResourceServer<T, Policy>::PointerType createResource( const QString & filename ) override { 0708 return new T(filename); 0709 } 0710 }; 0711 0712 #endif // KORESOURCESERVER_H