diff --git a/libs/brush/kis_brush_server.cpp b/libs/brush/kis_brush_server.cpp index 02a243b529..b27cff05b1 100644 --- a/libs/brush/kis_brush_server.cpp +++ b/libs/brush/kis_brush_server.cpp @@ -1,140 +1,140 @@ /* * Copyright (c) 2008 Boudewijn Rempt * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_brush_server.h" #include #include #include #include #include #include #include #include "kis_abr_brush.h" #include "kis_abr_brush_collection.h" #include "kis_gbr_brush.h" #include "kis_imagepipe_brush.h" #include "kis_png_brush.h" #include "kis_svg_brush.h" Q_GLOBAL_STATIC(KisBrushServer, s_instance) class BrushResourceServer : public KoResourceServer { public: BrushResourceServer() : KoResourceServer(ResourceType::Brushes) { } ///Reimplemented bool importResourceFile(const QString& filename, bool fileCreation = true) override { QFileInfo fi(filename); if (fi.exists() == false) return false; if (fi.size() == 0) return false; if (fi.suffix().toLower() == "abr") { if (fileCreation) { QFile::copy(filename, saveLocation() + fi.fileName()); } QList collectionResources = createResources(filename); Q_FOREACH (KisBrushSP brush, collectionResources) { addResource(brush); } } else { return KoResourceServer::importResourceFile(filename, fileCreation); } qApp->processEvents(QEventLoop::AllEvents); return true; } private: ///Reimplemented - QList createResources(const QString & filename) override { + QList createResources(const QString & filename) { QList brushes; QString fileExtension = QFileInfo(filename).suffix().toLower(); if (fileExtension == "abr") { KisAbrBrushCollection collection(filename); collection.load(); Q_FOREACH (KisAbrBrushSP abrBrush, collection.brushes()) { // abrBrush->setBrushTipImage(QImage()); brushes.append(abrBrush); addTag(abrBrush, collection.filename()); } } else { brushes.append(createResource(filename)); } return brushes; } ///Reimplemented KisBrushSP createResource(const QString & /*filename*/) override { return 0; // QString fileExtension = QFileInfo(filename).suffix().toLower(); // KisBrushSP brush; // if (fileExtension == "gbr") { // brush = KisBrushSP(new KisGbrBrush(filename)); // } // else if (fileExtension == "gih") { // brush = KisBrushSP(new KisImagePipeBrush(filename)); // } // else if (fileExtension == "png") { // brush = KisBrushSP(new KisPngBrush(filename)); // } // else if (fileExtension == "svg") { // brush = KisBrushSP(new KisSvgBrush(filename)); // } // return brush; } }; KisBrushServer::KisBrushServer() { m_brushServer = new BrushResourceServer(); } KisBrushServer::~KisBrushServer() { delete m_brushServer; } KisBrushServer* KisBrushServer::instance() { return s_instance; } KoResourceServer* KisBrushServer::brushServer() { return m_brushServer; } diff --git a/libs/ui/KisResourceBundle.cpp b/libs/ui/KisResourceBundle.cpp index ae3fea2b04..db9fddd093 100644 --- a/libs/ui/KisResourceBundle.cpp +++ b/libs/ui/KisResourceBundle.cpp @@ -1,1098 +1,649 @@ /* * Copyright (c) 2014 Victor Lafon metabolic.ewilan@hotmail.fr * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "KisResourceBundle.h" #include "KisResourceBundleManifest.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include KisResourceBundle::KisResourceBundle(QString const& fileName) : KoResource(fileName), m_bundleVersion("1") { setName(QFileInfo(fileName).baseName()); m_metadata["generator"] = "Krita (" + KritaVersionWrapper::versionString(true) + ")"; } KisResourceBundle::~KisResourceBundle() { } QString KisResourceBundle::defaultFileExtension() const { return QString(".bundle"); } bool KisResourceBundle::load() { if (filename().isEmpty()) return false; QScopedPointer resourceStore(KoStore::createStore(filename(), KoStore::Read, "application/x-krita-resourcebundle", KoStore::Zip)); if (!resourceStore || resourceStore->bad()) { warnKrita << "Could not open store on bundle" << filename(); m_installed = false; setValid(false); return false; } else { m_metadata.clear(); bool toRecreate = false; if (resourceStore->open("META-INF/manifest.xml")) { if (!m_manifest.load(resourceStore->device())) { warnKrita << "Could not open manifest for bundle" << filename(); return false; } resourceStore->close(); Q_FOREACH (KisResourceBundleManifest::ResourceReference ref, m_manifest.files()) { if (!resourceStore->open(ref.resourcePath)) { warnKrita << "Bundle is broken. File" << ref.resourcePath << "is missing"; toRecreate = true; } else { resourceStore->close(); } } if(toRecreate) { warnKrita << "Due to missing files and wrong entries in the manifest, " << filename() << " will be recreated."; } } else { warnKrita << "Could not load META-INF/manifest.xml"; return false; } bool versionFound = false; if (resourceStore->open("meta.xml")) { KoXmlDocument doc; if (!doc.setContent(resourceStore->device())) { warnKrita << "Could not parse meta.xml for" << filename(); return false; } // First find the manifest:manifest node. KoXmlNode n = doc.firstChild(); for (; !n.isNull(); n = n.nextSibling()) { if (!n.isElement()) { continue; } if (n.toElement().tagName() == "meta:meta") { break; } } if (n.isNull()) { warnKrita << "Could not find manifest node for bundle" << filename(); return false; } const KoXmlElement metaElement = n.toElement(); for (n = metaElement.firstChild(); !n.isNull(); n = n.nextSibling()) { if (n.isElement()) { KoXmlElement e = n.toElement(); if (e.tagName() == "meta:generator") { m_metadata.insert("generator", e.firstChild().toText().data()); } else if (e.tagName() == "dc:author") { m_metadata.insert("author", e.firstChild().toText().data()); } else if (e.tagName() == "dc:title") { m_metadata.insert("title", e.firstChild().toText().data()); } else if (e.tagName() == "dc:description") { m_metadata.insert("description", e.firstChild().toText().data()); } else if (e.tagName() == "meta:initial-creator") { m_metadata.insert("author", e.firstChild().toText().data()); } else if (e.tagName() == "dc:creator") { m_metadata.insert("author", e.firstChild().toText().data()); } else if (e.tagName() == "meta:creation-date") { m_metadata.insert("created", e.firstChild().toText().data()); } else if (e.tagName() == "meta:dc-date") { m_metadata.insert("updated", e.firstChild().toText().data()); } else if (e.tagName() == "meta:meta-userdefined") { if (e.attribute("meta:name") == "tag") { m_bundletags << e.attribute("meta:value"); } else { m_metadata.insert(e.attribute("meta:name"), e.attribute("meta:value")); } } else if(e.tagName() == "meta:bundle-version") { m_metadata.insert("bundle-version", e.firstChild().toText().data()); versionFound = true; } } } resourceStore->close(); } else { warnKrita << "Could not load meta.xml"; return false; } if (resourceStore->open("preview.png")) { // Workaround for some OS (Debian, Ubuntu), where loading directly from the QIODevice // fails with "libpng error: IDAT: CRC error" QByteArray data = resourceStore->device()->readAll(); QBuffer buffer(&data); m_thumbnail.load(&buffer, "PNG"); resourceStore->close(); } else { warnKrita << "Could not open preview.png"; } /* * If no version is found it's an old bundle with md5 hashes to fix, or if some manifest resource entry * doesn't not correspond to a file the bundle is "broken", in both cases we need to recreate the bundle. */ if (!versionFound) { m_metadata.insert("bundle-version", "1"); warnKrita << filename() << " has an old version and possibly wrong resources md5, so it will be recreated."; toRecreate = true; } if (toRecreate) { recreateBundle(resourceStore); } m_installed = true; setValid(true); setImage(m_thumbnail); } return true; } bool KisResourceBundle::loadFromDevice(QIODevice *) { return false; } bool saveResourceToStore(KoResourceSP resource, KoStore *store, const QString &resType) { if (!resource) { warnKrita << "No Resource"; return false; } if (!resource->valid()) { warnKrita << "Resource is not valid"; return false; } if (!store || store->bad()) { warnKrita << "No Store or Store is Bad"; return false; } QByteArray ba; QBuffer buf; QFileInfo fi(resource->filename()); if (fi.exists() && fi.isReadable()) { QFile f(resource->filename()); if (!f.open(QFile::ReadOnly)) { warnKrita << "Could not open resource" << resource->filename(); return false; } ba = f.readAll(); if (ba.size() == 0) { warnKrita << "Resource is empty" << resource->filename(); return false; } f.close(); buf.setBuffer(&ba); } else { warnKrita << "Could not find the resource " << resource->filename() << " or it isn't readable"; return false; } if (!buf.open(QBuffer::ReadOnly)) { warnKrita << "Could not open buffer"; return false; } Q_ASSERT(!store->hasFile(resType + "/" + resource->shortFilename())); if (!store->open(resType + "/" + resource->shortFilename())) { warnKrita << "Could not open file in store for resource"; return false; } bool res = (store->write(buf.data()) == buf.size()); store->close(); return res; } bool KisResourceBundle::save() { if (filename().isEmpty()) return false; addMeta("updated", QDate::currentDate().toString("dd/MM/yyyy")); QDir bundleDir = KoResourcePaths::saveLocation("data", "bundles"); bundleDir.cdUp(); QScopedPointer store(KoStore::createStore(filename(), KoStore::Write, "application/x-krita-resourcebundle", KoStore::Zip)); if (!store || store->bad()) return false; Q_FOREACH (const QString &resType, m_manifest.types()) { if (resType == ResourceType::Gradients) { KoResourceServer* gradientServer = KoResourceServerProvider::instance()->gradientServer(); Q_FOREACH (const KisResourceBundleManifest::ResourceReference &ref, m_manifest.files(resType)) { KoResourceSP res = gradientServer->resourceByMD5(ref.md5sum); if (!res) res = gradientServer->resourceByFilename(QFileInfo(ref.resourcePath).fileName()); if (!saveResourceToStore(res, store.data(), ResourceType::Gradients)) { if (res) { warnKrita << "Could not save resource" << resType << res->name(); } else { warnKrita << "could not find resource for" << QFileInfo(ref.resourcePath).fileName(); } } } } else if (resType == ResourceType::Patterns) { KoResourceServer* patternServer = KoResourceServerProvider::instance()->patternServer(); Q_FOREACH (const KisResourceBundleManifest::ResourceReference &ref, m_manifest.files(resType)) { KoResourceSP res = patternServer->resourceByMD5(ref.md5sum); if (!res) res = patternServer->resourceByFilename(QFileInfo(ref.resourcePath).fileName()); if (!saveResourceToStore(res, store.data(), ResourceType::Patterns)) { if (res) { warnKrita << "Could not save resource" << resType << res->name(); } else { warnKrita << "could not find resource for" << QFileInfo(ref.resourcePath).fileName(); } } } } else if (resType == ResourceType::Brushes) { KoResourceServer* brushServer = KisBrushServer::instance()->brushServer(); Q_FOREACH (const KisResourceBundleManifest::ResourceReference &ref, m_manifest.files(resType)) { KisBrushSP brush = brushServer->resourceByMD5(ref.md5sum); if (!brush) brush = brushServer->resourceByFilename(QFileInfo(ref.resourcePath).fileName()); KoResourceSP res = brush; if (!saveResourceToStore(res, store.data(), ResourceType::Brushes)) { if (res) { warnKrita << "Could not save resource" << resType << res->name(); } else { warnKrita << "could not find resource for" << QFileInfo(ref.resourcePath).fileName(); } } } } else if (resType == ResourceType::Palettes) { KoResourceServer* paletteServer = KoResourceServerProvider::instance()->paletteServer(); Q_FOREACH (const KisResourceBundleManifest::ResourceReference &ref, m_manifest.files(resType)) { KoResourceSP res = paletteServer->resourceByMD5(ref.md5sum); if (!res) res = paletteServer->resourceByFilename(QFileInfo(ref.resourcePath).fileName()); if (!saveResourceToStore(res, store.data(), ResourceType::Palettes)) { if (res) { warnKrita << "Could not save resource" << resType << res->name(); } else { warnKrita << "could not find resource for" << QFileInfo(ref.resourcePath).fileName(); } } } } else if (resType == ResourceType::Workspaces) { KoResourceServer< KisWorkspaceResource >* workspaceServer = KisResourceServerProvider::instance()->workspaceServer(); Q_FOREACH (const KisResourceBundleManifest::ResourceReference &ref, m_manifest.files(resType)) { KoResourceSP res = workspaceServer->resourceByMD5(ref.md5sum); if (!res) res = workspaceServer->resourceByFilename(QFileInfo(ref.resourcePath).fileName()); if (!saveResourceToStore(res, store.data(), ResourceType::Workspaces)) { if (res) { warnKrita << "Could not save resource" << resType << res->name(); } else { warnKrita << "could not find resource for" << QFileInfo(ref.resourcePath).fileName(); } } } } else if (resType == ResourceType::PaintOpPresets) { KisPaintOpPresetResourceServer* paintoppresetServer = KisResourceServerProvider::instance()->paintOpPresetServer(); Q_FOREACH (const KisResourceBundleManifest::ResourceReference &ref, m_manifest.files(resType)) { KisPaintOpPresetSP res = paintoppresetServer->resourceByMD5(ref.md5sum); if (!res) res = paintoppresetServer->resourceByFilename(QFileInfo(ref.resourcePath).fileName()); if (!saveResourceToStore(res, store.data(), ResourceType::PaintOpPresets)) { if (res) { warnKrita << "Could not save resource" << resType << res->name(); } else { warnKrita << "could not find resource for" << QFileInfo(ref.resourcePath).fileName(); } } } } else if (resType == ResourceType::GamutMasks) { KoResourceServer* gamutMaskServer = KoResourceServerProvider::instance()->gamutMaskServer(); Q_FOREACH (const KisResourceBundleManifest::ResourceReference &ref, m_manifest.files(resType)) { KoResourceSP res = gamutMaskServer->resourceByMD5(ref.md5sum); if (!res) res = gamutMaskServer->resourceByFilename(QFileInfo(ref.resourcePath).fileName()); if (!saveResourceToStore(res, store.data(), ResourceType::GamutMasks)) { if (res) { warnKrita << "Could not save resource" << resType << res->name(); } else { warnKrita << "could not find resource for" << QFileInfo(ref.resourcePath).fileName(); } } } } } if (!m_thumbnail.isNull()) { QByteArray byteArray; QBuffer buffer(&byteArray); m_thumbnail.save(&buffer, "PNG"); if (!store->open("preview.png")) warnKrita << "Could not open preview.png"; if (store->write(byteArray) != buffer.size()) warnKrita << "Could not write preview.png"; store->close(); } saveManifest(store); saveMetadata(store); store->finalize(); return true; } bool KisResourceBundle::saveToDevice(QIODevice */*dev*/) const { return false; } -bool KisResourceBundle::install() -{ - QStringList md5Mismatch; - if (filename().isEmpty()) { - warnKrita << "Cannot install bundle: no file name" << this; - return false; - } - QScopedPointer resourceStore(KoStore::createStore(filename(), KoStore::Read, "application/x-krita-resourcebundle", KoStore::Zip)); - - if (!resourceStore || resourceStore->bad()) { - warnKrita << "Cannot open the resource bundle: invalid zip file?"; - return false; - } - - Q_FOREACH (const QString &resType, m_manifest.types()) { - dbgResources << "Installing resource type" << resType; - if (resType == ResourceType::Gradients) { - KoResourceServer* gradientServer = KoResourceServerProvider::instance()->gradientServer(); - Q_FOREACH (const KisResourceBundleManifest::ResourceReference &ref, m_manifest.files(resType)) { - - if (resourceStore->isOpen()) resourceStore->close(); - - dbgResources << "\tInstalling" << ref.resourcePath; - KoAbstractGradientSP res = gradientServer->createResource(QString("bundle://%1:%2").arg(filename()).arg(ref.resourcePath)); - if (!res) { - warnKrita << "Could not create resource for" << ref.resourcePath; - continue; - } - if (!resourceStore->open(ref.resourcePath)) { - warnKrita << "Failed to open" << ref.resourcePath << "from bundle" << filename(); - continue; - } - if (!res->loadFromDevice(resourceStore->device())) { - warnKrita << "Failed to load" << ref.resourcePath << "from bundle" << filename(); - continue; - } - dbgResources << "\t\tresource:" << res->name(); - - KoAbstractGradientSP res2 = gradientServer->resourceByName(res->name()); - if (!res2) {//if it doesn't exist... - gradientServer->addResource(res, false);//add it! - - if (!m_gradientsMd5Installed.contains(res->md5())) { - m_gradientsMd5Installed.append(res->md5()); - } - if (ref.md5sum!=res->md5()) { - md5Mismatch.append(res->name()); - } - - Q_FOREACH (const QString &tag, ref.tagList) { - gradientServer->addTag(res, tag); - } - //gradientServer->addTag(res, name()); - } - else { - //warnKrita << "Didn't install" << res->name()<<"It already exists on the server"; - } - - } - } - else if (resType == ResourceType::Patterns) { - KoResourceServer* patternServer = KoResourceServerProvider::instance()->patternServer(); - Q_FOREACH (const KisResourceBundleManifest::ResourceReference &ref, m_manifest.files(resType)) { - - if (resourceStore->isOpen()) resourceStore->close(); - - dbgResources << "\tInstalling" << ref.resourcePath; - KoPatternSP res = patternServer->createResource(QString("bundle://%1:%2").arg(filename()).arg(ref.resourcePath)); - if (!res) { - warnKrita << "Could not create resource for" << ref.resourcePath; - continue; - } - if (!resourceStore->open(ref.resourcePath)) { - warnKrita << "Failed to open" << ref.resourcePath << "from bundle" << filename(); - continue; - } - if (!res->loadFromDevice(resourceStore->device())) { - warnKrita << "Failed to load" << ref.resourcePath << "from bundle" << filename(); - continue; - } - dbgResources << "\t\tresource:" << res->name(); - - KoPatternSP res2 = patternServer->resourceByName(res->name()); - if (!res2) {//if it doesn't exist... - patternServer->addResource(res, false);//add it! - - if (!m_patternsMd5Installed.contains(res->md5())) { - m_patternsMd5Installed.append(res->md5()); - } - if (ref.md5sum!=res->md5()) { - md5Mismatch.append(res->name()); - } - - Q_FOREACH (const QString &tag, ref.tagList) { - patternServer->addTag(res, tag); - } - //patternServer->addTag(res, name()); - } - - } - } - else if (resType == ResourceType::Brushes) { - KoResourceServer *brushServer = KisBrushServer::instance()->brushServer(); - Q_FOREACH (const KisResourceBundleManifest::ResourceReference &ref, m_manifest.files(resType)) { - - if (resourceStore->isOpen()) resourceStore->close(); - - dbgResources << "\tInstalling" << ref.resourcePath; - KisBrushSP res = brushServer->createResource(QString("bundle://%1:%2").arg(filename()).arg(ref.resourcePath)); - if (!res) { - warnKrita << "Could not create resource for" << ref.resourcePath; - continue; - } - if (!resourceStore->open(ref.resourcePath)) { - warnKrita << "Failed to open" << ref.resourcePath << "from bundle" << filename(); - continue; - } - if (!res->loadFromDevice(resourceStore->device())) { - warnKrita << "Failed to load" << ref.resourcePath << "from bundle" << filename(); - continue; - } - dbgResources << "\t\tresource:" << res->name(); - - //find the resource on the server - KisBrushSP res2 = brushServer->resourceByName(res->name()); - if (res2) { - res->setName(res->name()+"("+res->shortFilename()+")"); - } - // file name is more important than the regular name because the - // it is the way how it is called up from the brushpreset settings. - // Therefore just adjust the resource name and only refuse to load - // when the filename is different. - res2 = brushServer->resourceByFilename(res->shortFilename()); - if (!res2) {//if it doesn't exist... - brushServer->addResource(res, false);//add it! - - if (!m_brushesMd5Installed.contains(res->md5())) { - m_brushesMd5Installed.append(res->md5()); - } - if (ref.md5sum!=res->md5()) { - md5Mismatch.append(res->name()); - } - - Q_FOREACH (const QString &tag, ref.tagList) { - brushServer->addTag(res, tag); - } - //brushServer->addTag(res, name()); - } - else { - //warnKrita << "Didn't install" << res->name()<<"It already exists on the server"; - } - } - } - else if (resType == ResourceType::Palettes) { - KoResourceServer* paletteServer = KoResourceServerProvider::instance()->paletteServer(); - Q_FOREACH (const KisResourceBundleManifest::ResourceReference &ref, m_manifest.files(resType)) { - - if (resourceStore->isOpen()) resourceStore->close(); - - dbgResources << "\tInstalling" << ref.resourcePath; - KoColorSetSP res = paletteServer->createResource(QString("bundle://%1:%2").arg(filename()).arg(ref.resourcePath)); - - if (!res) { - warnKrita << "Could not create resource for" << ref.resourcePath; - continue; - } - if (!resourceStore->open(ref.resourcePath)) { - warnKrita << "Failed to open" << ref.resourcePath << "from bundle" << filename(); - continue; - } - if (!res->loadFromDevice(resourceStore->device())) { - warnKrita << "Failed to load" << ref.resourcePath << "from bundle" << filename(); - continue; - } - dbgResources << "\t\tresource:" << res->name(); - - //find the resource on the server - KoColorSetSP res2 = paletteServer->resourceByName(res->name()); - if (!res2) {//if it doesn't exist... - paletteServer->addResource(res, false);//add it! - - if (!m_palettesMd5Installed.contains(res->md5())) { - m_palettesMd5Installed.append(res->md5()); - } - if (ref.md5sum!=res->md5()) { - md5Mismatch.append(res->name()); - } - - Q_FOREACH (const QString &tag, ref.tagList) { - paletteServer->addTag(res, tag); - } - //paletteServer->addTag(res, name()); - } - else { - //warnKrita << "Didn't install" << res->name()<<"It already exists on the server"; - } - } - } - else if (resType == ResourceType::Workspaces) { - KoResourceServer< KisWorkspaceResource >* workspaceServer = KisResourceServerProvider::instance()->workspaceServer(); - Q_FOREACH (const KisResourceBundleManifest::ResourceReference &ref, m_manifest.files(resType)) { - - if (resourceStore->isOpen()) resourceStore->close(); - - dbgResources << "\tInstalling" << ref.resourcePath; - KisWorkspaceResourceSP res = workspaceServer->createResource(QString("bundle://%1:%2").arg(filename()).arg(ref.resourcePath)); - if (!res) { - warnKrita << "Could not create resource for" << ref.resourcePath; - continue; - } - if (!resourceStore->open(ref.resourcePath)) { - warnKrita << "Failed to open" << ref.resourcePath << "from bundle" << filename(); - continue; - } - if (!res->loadFromDevice(resourceStore->device())) { - warnKrita << "Failed to load" << ref.resourcePath << "from bundle" << filename(); - continue; - } - dbgResources << "\t\tresource:" << res->name(); - - //the following tries to find the resource by name. - KisWorkspaceResourceSP res2 = workspaceServer->resourceByName(res->name()); - if (!res2) {//if it doesn't exist... - workspaceServer->addResource(res, false);//add it! - - if (!m_workspacesMd5Installed.contains(res->md5())) { - m_workspacesMd5Installed.append(res->md5()); - } - if (ref.md5sum!=res->md5()) { - md5Mismatch.append(res->name()); - } - - Q_FOREACH (const QString &tag, ref.tagList) { - workspaceServer->addTag(res, tag); - } - //workspaceServer->addTag(res, name()); - } - else { - //warnKrita << "Didn't install" << res->name()<<"It already exists on the server"; - } - - } - } - else if (resType == ResourceType::PaintOpPresets) { - KisPaintOpPresetResourceServer* paintoppresetServer = KisResourceServerProvider::instance()->paintOpPresetServer(); - Q_FOREACH (const KisResourceBundleManifest::ResourceReference &ref, m_manifest.files(resType)) { - - if (resourceStore->isOpen()) resourceStore->close(); - - dbgResources << "\tInstalling" << ref.resourcePath; - KisPaintOpPresetSP res = paintoppresetServer->createResource(QString("bundle://%1:%2").arg(filename()).arg(ref.resourcePath)); - - if (!res) { - warnKrita << "Could not create resource for" << ref.resourcePath; - continue; - } - if (!resourceStore->open(ref.resourcePath)) { - warnKrita << "Failed to open" << ref.resourcePath << "from bundle" << filename(); - continue; - } - // Workaround for some OS (Debian, Ubuntu), where loading directly from the QIODevice - // fails with "libpng error: IDAT: CRC error" - QByteArray data = resourceStore->device()->readAll(); - QBuffer buffer(&data); - if (!res->loadFromDevice(&buffer)) { - warnKrita << "Failed to load" << ref.resourcePath << "from bundle" << filename(); - continue; - } - dbgResources << "\t\tresource:" << res->name() << "File:" << res->filename(); - - //the following tries to find the resource by name. - KisPaintOpPresetSP res2 = paintoppresetServer->resourceByName(res->name()); - if (!res2) {//if it doesn't exist... - paintoppresetServer->addResource(res, false);//add it! - if (!m_presetsMd5Installed.contains(res->md5())){ - m_presetsMd5Installed.append(res->md5()); - } - if (ref.md5sum!=res->md5()) { - md5Mismatch.append(res->name()); - } - - Q_FOREACH (const QString &tag, ref.tagList) { - paintoppresetServer->addTag(res, tag); - } - //paintoppresetServer->addTag(res, name()); - } - else { - //warnKrita << "Didn't install" << res->name()<<"It already exists on the server"; - } - - } - } - else if (resType == ResourceType::GamutMasks) { - KoResourceServer* gamutMaskServer = KoResourceServerProvider::instance()->gamutMaskServer(); - Q_FOREACH (const KisResourceBundleManifest::ResourceReference &ref, m_manifest.files(resType)) { - - if (resourceStore->isOpen()) resourceStore->close(); - - dbgResources << "\tInstalling" << ref.resourcePath; - KoGamutMaskSP res = gamutMaskServer->createResource(QString("bundle://%1:%2").arg(filename()).arg(ref.resourcePath)); - - if (!res) { - warnKrita << "Could not create resource for" << ref.resourcePath; - continue; - } - if (!resourceStore->open(ref.resourcePath)) { - warnKrita << "Failed to open" << ref.resourcePath << "from bundle" << filename(); - continue; - } - if (!res->loadFromDevice(resourceStore->device())) { - warnKrita << "Failed to load" << ref.resourcePath << "from bundle" << filename(); - continue; - } - dbgResources << "\t\tresource:" << res->name(); - - //find the resource on the server - KoGamutMaskSP res2 = gamutMaskServer->resourceByName(res->name()); - if (!res2) {//if it doesn't exist... - gamutMaskServer->addResource(res, false);//add it! - - if (!m_gamutMasksMd5Installed.contains(res->md5())) { - m_gamutMasksMd5Installed.append(res->md5()); - } - if (ref.md5sum!=res->md5()) { - md5Mismatch.append(res->name()); - } - - Q_FOREACH (const QString &tag, ref.tagList) { - gamutMaskServer->addTag(res, tag); - } - //gamutMaskServer->addTag(res, name()); - } - else { - //warnKrita << "Didn't install" << res->name()<<"It already exists on the server"; - } - } - } - } - m_installed = true; - if(!md5Mismatch.isEmpty()){ - QString message = i18n("The following resources had mismatching MD5 sums. They may have gotten corrupted, for example, during download."); - QMessageBox bundleFeedback; - bundleFeedback.setIcon(QMessageBox::Warning); - Q_FOREACH (QString name, md5Mismatch) { - message.append("\n"); - message.append(name); - } - bundleFeedback.setText(message); - bundleFeedback.exec(); - } - return true; -} - -bool KisResourceBundle::uninstall() -{ - - m_installed = false; - QStringList tags = getTagsList(); - tags << m_manifest.tags(); - //tags << name(); - - KoResourceServer* gradientServer = KoResourceServerProvider::instance()->gradientServer(); - //Q_FOREACH (const KisResourceBundleManifest::ResourceReference &ref, m_manifest.files(ResourceType::Gradients)) { - Q_FOREACH (const QByteArray md5, m_gradientsMd5Installed) { - KoAbstractGradientSP res = gradientServer->resourceByMD5(md5); - if (res) { - gradientServer->removeResourceFromServer(res); - } - } - - KoResourceServer* patternServer = KoResourceServerProvider::instance()->patternServer(); - //Q_FOREACH (const KisResourceBundleManifest::ResourceReference &ref, m_manifest.files(ResourceType::Patterns)) { - Q_FOREACH (const QByteArray md5, m_patternsMd5Installed) { - KoPatternSP res = patternServer->resourceByMD5(md5); - if (res) { - patternServer->removeResourceFromServer(res); - } - } - - KoResourceServer *brushServer = KisBrushServer::instance()->brushServer(); - //Q_FOREACH (const KisResourceBundleManifest::ResourceReference &ref, m_manifest.files(ResourceType::Brushes)) { - Q_FOREACH (const QByteArray md5, m_brushesMd5Installed) { - KisBrushSP res = brushServer->resourceByMD5(md5); - if (res) { - brushServer->removeResourceFromServer(res); - } - } - - KoResourceServer* paletteServer = KoResourceServerProvider::instance()->paletteServer(); - //Q_FOREACH (const KisResourceBundleManifest::ResourceReference &ref, m_manifest.files(ResourceType::Palettes)) { - Q_FOREACH (const QByteArray md5, m_palettesMd5Installed) { - KoColorSetSP res = paletteServer->resourceByMD5(md5); - if (res) { - paletteServer->removeResourceFromServer(res); - } - } - - KoResourceServer< KisWorkspaceResource >* workspaceServer = KisResourceServerProvider::instance()->workspaceServer(); - //Q_FOREACH (const KisResourceBundleManifest::ResourceReference &ref, m_manifest.files(ResourceType::Workspaces)) { - Q_FOREACH (const QByteArray md5, m_workspacesMd5Installed) { - KisWorkspaceResourceSP res = workspaceServer->resourceByMD5(md5); - if (res) { - workspaceServer->removeResourceFromServer(res); - } - } - KisPaintOpPresetResourceServer* paintoppresetServer = KisResourceServerProvider::instance()->paintOpPresetServer(); - //Q_FOREACH (const KisResourceBundleManifest::ResourceReference &ref, m_manifest.files(ResourceType::PaintOpPresets)) { - Q_FOREACH (const QByteArray md5, m_presetsMd5Installed) { - KisPaintOpPresetSP res = paintoppresetServer->resourceByMD5(md5); - if (res) { - paintoppresetServer->removeResourceFromServer(res); - } - } - - KoResourceServer* gamutMaskServer = KoResourceServerProvider::instance()->gamutMaskServer(); - //Q_FOREACH (const KisResourceBundleManifest::ResourceReference &ref, m_manifest.files(ResourceType::GamutMasks)) { - Q_FOREACH (const QByteArray md5, m_gamutMasksMd5Installed) { - KoGamutMaskSP res = gamutMaskServer->resourceByMD5(md5); - if (res) { - gamutMaskServer->removeResourceFromServer(res); - } - } - - Q_FOREACH(const QString &tag, tags) { - paintoppresetServer->tagCategoryRemoved(tag); - workspaceServer->tagCategoryRemoved(tag); - paletteServer->tagCategoryRemoved(tag); - brushServer->tagCategoryRemoved(tag); - patternServer->tagCategoryRemoved(tag); - gradientServer->tagCategoryRemoved(tag); - gamutMaskServer->tagCategoryRemoved(tag); - } - - - return true; -} - void KisResourceBundle::addMeta(const QString &type, const QString &value) { m_metadata.insert(type, value); } const QString KisResourceBundle::getMeta(const QString &type, const QString &defaultValue) const { if (m_metadata.contains(type)) { return m_metadata[type]; } else { return defaultValue; } } void KisResourceBundle::addResource(QString fileType, QString filePath, QStringList fileTagList, const QByteArray md5sum) { m_manifest.addResource(fileType, filePath, fileTagList, md5sum); } QList KisResourceBundle::getTagsList() { return QList::fromSet(m_bundletags); } - -bool KisResourceBundle::isInstalled() -{ - return m_installed; -} - - QStringList KisResourceBundle::resourceTypes() const { return m_manifest.types(); } QList KisResourceBundle::resources(const QString &resType) const { QList references = m_manifest.files(resType); QList ret; Q_FOREACH (const KisResourceBundleManifest::ResourceReference &ref, references) { if (resType == ResourceType::Gradients) { KoResourceServer* gradientServer = KoResourceServerProvider::instance()->gradientServer(); KoResourceSP res = gradientServer->resourceByMD5(ref.md5sum); if (res) ret << res; } else if (resType == ResourceType::Patterns) { KoResourceServer* patternServer = KoResourceServerProvider::instance()->patternServer(); KoResourceSP res = patternServer->resourceByMD5(ref.md5sum); if (res) ret << res; } else if (resType == ResourceType::Brushes) { KoResourceServer *brushServer = KisBrushServer::instance()->brushServer(); KoResourceSP res = brushServer->resourceByMD5(ref.md5sum); if (res) ret << res; } else if (resType == ResourceType::Palettes) { KoResourceServer* paletteServer = KoResourceServerProvider::instance()->paletteServer(); KoResourceSP res = paletteServer->resourceByMD5(ref.md5sum); if (res) ret << res; } else if (resType == ResourceType::Workspaces) { KoResourceServer< KisWorkspaceResource >* workspaceServer = KisResourceServerProvider::instance()->workspaceServer(); KoResourceSP res = workspaceServer->resourceByMD5(ref.md5sum); if (res) ret << res; } else if (resType == ResourceType::PaintOpPresets) { KisPaintOpPresetResourceServer* paintoppresetServer = KisResourceServerProvider::instance()->paintOpPresetServer(); KisPaintOpPresetSP res = paintoppresetServer->resourceByMD5(ref.md5sum); if (res) ret << res; } else if (resType == ResourceType::GamutMasks) { KoResourceServer* gamutMaskServer = KoResourceServerProvider::instance()->gamutMaskServer(); KoResourceSP res = gamutMaskServer->resourceByMD5(ref.md5sum); if (res) ret << res; } } return ret; } void KisResourceBundle::setThumbnail(QString filename) { if (QFileInfo(filename).exists()) { m_thumbnail = QImage(filename); m_thumbnail = m_thumbnail.scaled(256, 256, Qt::KeepAspectRatio, Qt::SmoothTransformation); } else { m_thumbnail = QImage(256, 256, QImage::Format_ARGB32); QPainter gc(&m_thumbnail); gc.fillRect(0, 0, 256, 256, Qt::red); gc.end(); } setImage(m_thumbnail); } void KisResourceBundle::writeMeta(const char *metaTag, const QString &metaKey, KoXmlWriter *writer) { if (m_metadata.contains(metaKey)) { writer->startElement(metaTag); writer->addTextNode(m_metadata[metaKey].toUtf8()); writer->endElement(); } } void KisResourceBundle::writeUserDefinedMeta(const QString &metaKey, KoXmlWriter *writer) { if (m_metadata.contains(metaKey)) { writer->startElement("meta:meta-userdefined"); writer->addAttribute("meta:name", metaKey); writer->addAttribute("meta:value", m_metadata[metaKey]); writer->endElement(); } } -void KisResourceBundle::setInstalled(bool install) -{ - m_installed = install; -} - void KisResourceBundle::saveMetadata(QScopedPointer &store) { QBuffer buf; store->open("meta.xml"); buf.open(QBuffer::WriteOnly); KoXmlWriter metaWriter(&buf); metaWriter.startDocument("office:document-meta"); metaWriter.startElement("meta:meta"); writeMeta("meta:generator", "generator", &metaWriter); metaWriter.startElement("meta:bundle-version"); metaWriter.addTextNode(m_bundleVersion.toUtf8()); metaWriter.endElement(); writeMeta("dc:author", "author", &metaWriter); writeMeta("dc:title", "filename", &metaWriter); writeMeta("dc:description", "description", &metaWriter); writeMeta("meta:initial-creator", "author", &metaWriter); writeMeta("dc:creator", "author", &metaWriter); writeMeta("meta:creation-date", "created", &metaWriter); writeMeta("meta:dc-date", "updated", &metaWriter); writeUserDefinedMeta("email", &metaWriter); writeUserDefinedMeta("license", &metaWriter); writeUserDefinedMeta("website", &metaWriter); Q_FOREACH (const QString &tag, m_bundletags) { metaWriter.startElement("meta:meta-userdefined"); metaWriter.addAttribute("meta:name", "tag"); metaWriter.addAttribute("meta:value", tag); metaWriter.endElement(); } metaWriter.endElement(); // meta:meta metaWriter.endDocument(); buf.close(); store->write(buf.data()); store->close(); } void KisResourceBundle::saveManifest(QScopedPointer &store) { store->open("META-INF/manifest.xml"); QBuffer buf; buf.open(QBuffer::WriteOnly); m_manifest.save(&buf); buf.close(); store->write(buf.data()); store->close(); } void KisResourceBundle::recreateBundle(QScopedPointer &oldStore) { // Save a copy of the unmodified bundle, so that if anything goes bad the user doesn't lose it QFile file(filename()); file.copy(filename() + ".old"); QString newStoreName = filename() + ".tmp"; { QScopedPointer store(KoStore::createStore(newStoreName, KoStore::Write, "application/x-krita-resourcebundle", KoStore::Zip)); KoHashGenerator *generator = KoHashGeneratorProvider::instance()->getGenerator("MD5"); KisResourceBundleManifest newManifest; addMeta("updated", QDate::currentDate().toString("dd/MM/yyyy")); Q_FOREACH (KisResourceBundleManifest::ResourceReference ref, m_manifest.files()) { // Wrong manifest entry found, skip it if(!oldStore->open(ref.resourcePath)) continue; store->open(ref.resourcePath); QByteArray data = oldStore->device()->readAll(); oldStore->close(); store->write(data); store->close(); QByteArray result = generator->generateHash(data); newManifest.addResource(ref.fileTypeName, ref.resourcePath, ref.tagList, result); } m_manifest = newManifest; if (!m_thumbnail.isNull()) { QByteArray byteArray; QBuffer buffer(&byteArray); m_thumbnail.save(&buffer, "PNG"); if (!store->open("preview.png")) warnKrita << "Could not open preview.png"; if (store->write(byteArray) != buffer.size()) warnKrita << "Could not write preview.png"; store->close(); } saveManifest(store); saveMetadata(store); store->finalize(); } // Remove the current bundle and then move the tmp one to be the correct one file.setFileName(filename()); if (!file.remove()) { qWarning() << "Could not remove" << filename() << file.errorString(); } QFile f(newStoreName); Q_ASSERT(f.exists()); if (!f.copy(filename())) { qWarning() << "Could not copy the tmp file to the store" << filename() << newStoreName << QFile(newStoreName).exists() << f.errorString(); } } int KisResourceBundle::resourceCount() const { return m_manifest.files().count(); } diff --git a/libs/ui/KisResourceBundle.h b/libs/ui/KisResourceBundle.h index b223b7baba..d89b86bc80 100644 --- a/libs/ui/KisResourceBundle.h +++ b/libs/ui/KisResourceBundle.h @@ -1,165 +1,144 @@ /* * Copyright (c) 2014 Victor Lafon metabolic.ewilan@hotmail.fr * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #ifndef KORESOURCEBUNDLE_H #define KORESOURCEBUNDLE_H #include #include #include #include #include "KisResourceBundleManifest.h" #include "kritaui_export.h" class KoStore; /** * @brief The ResourceBundle class * @details Describe the resource bundles as KoResources */ class KRITAUI_EXPORT KisResourceBundle : public KoResource { public: /** * @brief ResourceBundle : Ctor * * @param fileName the path of the bundle */ KisResourceBundle(QString const& fileName); /** * @brief ~ResourceBundle : Dtor */ ~KisResourceBundle() override; /** * @brief defaultFileExtension * @return the default file extension which should be when saving the resource */ QString defaultFileExtension() const override; /** * @brief load : Load this resource. * @return true if succeed, false otherwise. */ bool load() override; bool loadFromDevice(QIODevice *dev) override; /** * @brief save : Save this resource. * @return true if succeed, false otherwise. */ bool save() override; bool saveToDevice(QIODevice* dev) const override; - /** - * @brief install : Install the contents of the resource bundle. - */ - bool install(); - - /** - * @brief uninstall : Uninstall the resource bundle. - */ - bool uninstall(); - /** * @brief addMeta : Add a Metadata to the resource * @param type type of the metadata * @param value value of the metadata */ void addMeta(const QString &type, const QString &value); const QString getMeta(const QString &type, const QString &defaultValue = QString()) const; /** * @brief Add a file to the bundle * @param fileType type of the resource file * @param filePath path of the resource file * @param fileTagList the list of tags * @param md5sum the file MD5 checksum */ void addResource(QString fileType, QString filePath, QStringList fileTagList, const QByteArray md5sum); QList getTagsList(); - /** - * @brief isInstalled - * @return true if the bundle is installed, false otherwise. - */ - bool isInstalled(); - /** - * @brief setInstalled - * This allows you to set installed or uninstalled upon loading. This is used with blacklists. - */ - void setInstalled(bool install); - void setThumbnail(QString); /** * @brief saveMetadata: saves bundle metadata * @param store bundle where to save the metadata */ void saveMetadata(QScopedPointer &store); /** * @brief saveManifest: saves bundle manifest * @param store bundle where to save the manifest */ void saveManifest(QScopedPointer &store); /** * @brief recreateBundle * It recreates the bundle by copying the old bundle information to a new store * and recalculating the md5 of each resource. * @param oldStore the old store to be recreated. */ void recreateBundle(QScopedPointer &oldStore); QStringList resourceTypes() const; QList resources(const QString &resType = QString()) const; int resourceCount() const; private: void writeMeta(const char *metaTag, const QString &metaKey, KoXmlWriter *writer); void writeUserDefinedMeta(const QString &metaKey, KoXmlWriter *writer); private: QImage m_thumbnail; KisResourceBundleManifest m_manifest; QMap m_metadata; QSet m_bundletags; bool m_installed; QList m_gradientsMd5Installed; QList m_patternsMd5Installed; QList m_brushesMd5Installed; QList m_palettesMd5Installed; QList m_workspacesMd5Installed; QList m_presetsMd5Installed; QList m_gamutMasksMd5Installed; QString m_bundleVersion; }; typedef QSharedPointer KisResourceBundleSP; #endif // KORESOURCEBUNDLE_H diff --git a/libs/ui/kis_favorite_resource_manager.cpp b/libs/ui/kis_favorite_resource_manager.cpp index b18d41bd52..6063471911 100644 --- a/libs/ui/kis_favorite_resource_manager.cpp +++ b/libs/ui/kis_favorite_resource_manager.cpp @@ -1,354 +1,355 @@ /* This file is part of the KDE project Copyright 2009 Vera Lukman Copyright 2011 Sven Langkamp This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License version 2 as published by the Free Software Foundation. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include #include #include #include #include #include #include #include "kis_favorite_resource_manager.h" #include "kis_popup_palette.h" #include "kis_paintop_box.h" #include "KisViewManager.h" #include "KisResourceServerProvider.h" #include "kis_min_heap.h" #include "kis_config.h" #include "kis_config_notifier.h" +#include class KisFavoriteResourceManager::ColorDataList { public: static const int MAX_RECENT_COLOR = 12; ColorDataList() { m_key = 0; } ~ColorDataList() { qDeleteAll(m_guiList); } int size() { return m_guiList.size(); } int leastUsedGuiPos() { return findPos(m_priorityList.valueAt(0)); } const KoColor& guiColor(int pos) { Q_ASSERT_X(pos < size(), "ColorDataList::guiColor", "index out of bound"); Q_ASSERT_X(pos >= 0, "ColorDataList::guiColor", "negative index"); return m_guiList.at(pos)->data; } void append(const KoColor& data) { int pos = findPos(data); if (pos > -1) updateKey(pos); else appendNew(data); } void appendNew(const KoColor& data) { if (size() >= ColorDataList::MAX_RECENT_COLOR) removeLeastUsed(); PriorityNode * node; node = new PriorityNode (); node->data = data; node->key = m_key++; m_priorityList.append(node); int pos = guiInsertPos(data); pos >= m_guiList.size() ? m_guiList.append(node) : m_guiList.insert(pos, node); node = 0; } void removeLeastUsed() { Q_ASSERT_X(size() >= 0, "ColorDataList::removeLeastUsed", "index out of bound"); if (size() <= 0) return; int pos = findPos(m_priorityList.valueAt(0)); m_guiList.removeAt(pos); m_priorityList.remove(0); } void updateKey(int guiPos) { if (m_guiList.at(guiPos)->key == m_key - 1) return; m_priorityList.changeKey(m_guiList.at(guiPos)->pos, m_key++); } /*find position of the color on the gui list*/ int findPos(const KoColor& color) { int low = 0, high = size(), mid = 0; while (low < high) { mid = (low + high) / 2; if (hsvComparison(color, m_guiList.at(mid)->data) == 0) return mid; else if (hsvComparison(color, m_guiList.at(mid)->data) < 0) high = mid; else low = mid + 1; } return -1; } private: int m_key; int guiInsertPos(const KoColor& color) { int low = 0, high = size() - 1, mid = (low + high) / 2; while (low < high) { hsvComparison(color, m_guiList[mid]->data) == -1 ? high = mid : low = mid + 1; mid = (low + high) / 2; } if (m_guiList.size() > 0) { if (hsvComparison(color, m_guiList[mid]->data) == 1) ++mid; } return mid; } /*compares c1 and c2 based on HSV. c1 < c2, returns -1 c1 = c2, returns 0 c1 > c2, returns 1 */ int hsvComparison(const KoColor& c1, const KoColor& c2) { QColor qc1 = c1.toQColor(); QColor qc2 = c2.toQColor(); if (qc1.hue() < qc2.hue()) return -1; if (qc1.hue() > qc2.hue()) return 1; // hue is the same, ok let's compare saturation if (qc1.saturation() < qc2.saturation()) return -1; if (qc1.saturation() > qc2.saturation()) return 1; // oh, also saturation is same? if (qc1.value() < qc2.value()) return -1; if (qc1.value() > qc2.value()) return 1; // user selected two similar colors return 0; } KisMinHeap m_priorityList; QList *> m_guiList; }; KisFavoriteResourceManager::KisFavoriteResourceManager(KisPaintopBox *paintopBox) : m_paintopBox(paintopBox) , m_colorList(0) , m_blockUpdates(false) , m_initialized(false) { KisConfig cfg(true); m_maxPresets = cfg.favoritePresets(); m_colorList = new ColorDataList(); connect(KisConfigNotifier::instance(), SIGNAL(configChanged()), SLOT(configChanged())); KisPaintOpPresetResourceServer * rServer = KisResourceServerProvider::instance()->paintOpPresetServer(); rServer->addObserver(this); } KisFavoriteResourceManager::~KisFavoriteResourceManager() { KisPaintOpPresetResourceServer *rServer = KisResourceServerProvider::instance()->paintOpPresetServer(); rServer->removeObserver(this); delete m_colorList; } void KisFavoriteResourceManager::unsetResourceServer() { // ... } QVector KisFavoriteResourceManager::favoritePresetList() { init(); return m_favoritePresetsList; } QList KisFavoriteResourceManager::favoritePresetImages() { init(); QList images; Q_FOREACH (KisPaintOpPresetSP preset, m_favoritePresetsList) { if (preset) { images.append(preset->image()); } } return images; } void KisFavoriteResourceManager::setCurrentTag(const QString& tagName) { m_currentTag = tagName; KisConfig(false).writeEntry("favoritePresetsTag", tagName); updateFavoritePresets(); } void KisFavoriteResourceManager::slotChangeActivePaintop(int pos) { if (pos < 0 || pos >= m_favoritePresetsList.size()) return; KoResourceSP resource = m_favoritePresetsList.at(pos);; m_paintopBox->resourceSelected(resource); emit hidePalettes(); } int KisFavoriteResourceManager::numFavoritePresets() { init(); return m_favoritePresetsList.size(); } //Recent Colors void KisFavoriteResourceManager::slotUpdateRecentColor(int pos) { // Do not update the key, the colour might be selected but it is not used yet. So we are not supposed // to update the colour priority when we select it. m_colorList->updateKey(pos); emit setSelectedColor(pos); emit sigSetFGColor(m_colorList->guiColor(pos)); emit hidePalettes(); } void KisFavoriteResourceManager::slotAddRecentColor(const KoColor& color) { m_colorList->append(color); int pos = m_colorList->findPos(color); emit setSelectedColor(pos); } void KisFavoriteResourceManager::slotChangeFGColorSelector(KoColor c) { emit sigChangeFGColorSelector(c); } void KisFavoriteResourceManager::removingResource(QSharedPointer resource) { if (m_blockUpdates) { return; } if (m_favoritePresetsList.contains(resource)) { updateFavoritePresets(); } } void KisFavoriteResourceManager::resourceAdded(QSharedPointer /*resource*/) { if (m_blockUpdates) { return; } updateFavoritePresets(); } void KisFavoriteResourceManager::resourceChanged(QSharedPointer /*resource*/) { } void KisFavoriteResourceManager::setBlockUpdates(bool block) { m_blockUpdates = block; if (!block) { updateFavoritePresets(); } } void KisFavoriteResourceManager::syncTaggedResourceView() { if (m_blockUpdates) { return; } updateFavoritePresets(); } void KisFavoriteResourceManager::syncTagAddition(const QString& /*tag*/) {} void KisFavoriteResourceManager::syncTagRemoval(const QString& /*tag*/) {} int KisFavoriteResourceManager::recentColorsTotal() { return m_colorList->size(); } const KoColor& KisFavoriteResourceManager::recentColorAt(int pos) { return m_colorList->guiColor(pos); } void KisFavoriteResourceManager::slotSetBGColor(const KoColor c) { m_bgColor = c; } KoColor KisFavoriteResourceManager::bgColor() const { return m_bgColor; } bool sortPresetByName(KisPaintOpPresetSP preset1, KisPaintOpPresetSP preset2) { return preset1->name() < preset2->name(); } void KisFavoriteResourceManager::updateFavoritePresets() { m_favoritePresetsList.clear(); - KisPaintOpPresetResourceServer* rServer = KisResourceServerProvider::instance()->paintOpPresetServer(); - QStringList presetFilenames = rServer->searchTag(m_currentTag); - for(int i = 0; i < qMin(m_maxPresets, presetFilenames.size()); i++) { - KisPaintOpPresetSP pr = rServer->resourceByFilename(presetFilenames.at(i)); - m_favoritePresetsList.append(pr); - std::sort(m_favoritePresetsList.begin(), m_favoritePresetsList.end(), sortPresetByName); - } +// KisPaintOpPresetResourceServer* rServer = KisResourceServerProvider::instance()->paintOpPresetServer(); +// QStringList presetFilenames = rServer->searchTag(m_currentTag); +// for(int i = 0; i < qMin(m_maxPresets, presetFilenames.size()); i++) { +// KisPaintOpPresetSP pr = rServer->resourceByFilename(presetFilenames.at(i)); +// m_favoritePresetsList.append(pr); +// std::sort(m_favoritePresetsList.begin(), m_favoritePresetsList.end(), sortPresetByName); +// } emit updatePalettes(); } void KisFavoriteResourceManager::configChanged() { KisConfig cfg(true); m_maxPresets = cfg.favoritePresets(); updateFavoritePresets(); } void KisFavoriteResourceManager::init() { if (!m_initialized) { m_initialized = true; KisResourceServerProvider::instance()->paintOpPresetServer(); m_currentTag = KisConfig(true).readEntry("favoritePresetsTag", "★ My Favorites"); updateFavoritePresets(); } } diff --git a/libs/ui/kis_popup_palette.cpp b/libs/ui/kis_popup_palette.cpp index c883f3ec13..f9a3de743e 100644 --- a/libs/ui/kis_popup_palette.cpp +++ b/libs/ui/kis_popup_palette.cpp @@ -1,919 +1,927 @@ /* This file is part of the KDE project Copyright 2009 Vera Lukman Copyright 2011 Sven Langkamp Copyright 2016 Scott Petrovic This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License version 2 as published by the Free Software Foundation. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +#include +#include +#include +#include +#include + +#include #include "kis_canvas2.h" #include "kis_config.h" #include "kis_popup_palette.h" #include "kis_favorite_resource_manager.h" #include "kis_icon_utils.h" -#include "KisResourceServerProvider.h" #include #include #include #include -#include -#include -#include -#include -#include #include "kis_signal_compressor.h" #include "brushhud/kis_brush_hud.h" #include "brushhud/kis_round_hud_button.h" #include "kis_signals_blocker.h" #include "kis_canvas_controller.h" #include "kis_acyclic_signal_connector.h" - +#include class PopupColorTriangle : public KoTriangleColorSelector { public: PopupColorTriangle(const KoColorDisplayRendererInterface *displayRenderer, QWidget* parent) : KoTriangleColorSelector(displayRenderer, parent) , m_dragging(false) { } ~PopupColorTriangle() override {} void tabletEvent(QTabletEvent* event) override { event->accept(); QMouseEvent* mouseEvent = 0; switch (event->type()) { case QEvent::TabletPress: mouseEvent = new QMouseEvent(QEvent::MouseButtonPress, event->pos(), Qt::LeftButton, Qt::LeftButton, event->modifiers()); m_dragging = true; mousePressEvent(mouseEvent); break; case QEvent::TabletMove: mouseEvent = new QMouseEvent(QEvent::MouseMove, event->pos(), (m_dragging) ? Qt::LeftButton : Qt::NoButton, (m_dragging) ? Qt::LeftButton : Qt::NoButton, event->modifiers()); mouseMoveEvent(mouseEvent); break; case QEvent::TabletRelease: mouseEvent = new QMouseEvent(QEvent::MouseButtonRelease, event->pos(), Qt::LeftButton, Qt::LeftButton, event->modifiers()); m_dragging = false; mouseReleaseEvent(mouseEvent); break; default: break; } delete mouseEvent; } private: bool m_dragging; }; KisPopupPalette::KisPopupPalette(KisViewManager* viewManager, KisCoordinatesConverter* coordinatesConverter ,KisFavoriteResourceManager* manager, const KoColorDisplayRendererInterface *displayRenderer, KisCanvasResourceProvider *provider, QWidget *parent) : QWidget(parent, Qt::FramelessWindowHint) , m_coordinatesConverter(coordinatesConverter) , m_viewManager(viewManager) , m_actionManager(viewManager->actionManager()) , m_resourceManager(manager) , m_displayRenderer(displayRenderer) , m_colorChangeCompressor(new KisSignalCompressor(50, KisSignalCompressor::POSTPONE)) , m_actionCollection(viewManager->actionCollection()) , m_acyclicConnector(new KisAcyclicSignalConnector(this)) { // some UI controls are defined and created based off these variables const int borderWidth = 3; if (KisConfig(true).readEntry("popuppalette/usevisualcolorselector", false)) { m_triangleColorSelector = new KisVisualColorSelector(this); } else { m_triangleColorSelector = new PopupColorTriangle(displayRenderer, this); } m_triangleColorSelector->setDisplayRenderer(displayRenderer); m_triangleColorSelector->setConfig(true,false); m_triangleColorSelector->move(m_popupPaletteSize/2-m_colorHistoryInnerRadius+borderWidth, m_popupPaletteSize/2-m_colorHistoryInnerRadius+borderWidth); m_triangleColorSelector->resize(m_colorHistoryInnerRadius*2-borderWidth*2, m_colorHistoryInnerRadius*2-borderWidth*2); m_triangleColorSelector->setVisible(true); KoColor fgcolor(Qt::black, KoColorSpaceRegistry::instance()->rgb8()); if (m_resourceManager) { fgcolor = provider->fgColor(); } m_triangleColorSelector->slotSetColor(fgcolor); QRegion maskedRegion(0, 0, m_triangleColorSelector->width(), m_triangleColorSelector->height(), QRegion::Ellipse ); m_triangleColorSelector->setMask(maskedRegion); //setAttribute(Qt::WA_TranslucentBackground, true); connect(m_triangleColorSelector, SIGNAL(sigNewColor(KoColor)), m_colorChangeCompressor.data(), SLOT(start())); connect(m_colorChangeCompressor.data(), SIGNAL(timeout()), SLOT(slotEmitColorChanged())); connect(KisConfigNotifier::instance(), SIGNAL(configChanged()), m_triangleColorSelector, SLOT(configurationChanged())); m_acyclicConnector->connectForwardKoColor(m_resourceManager, SIGNAL(sigChangeFGColorSelector(KoColor)), this, SLOT(slotExternalFgColorChanged(KoColor))); m_acyclicConnector->connectBackwardKoColor(this, SIGNAL(sigChangefGColor(KoColor)), m_resourceManager, SIGNAL(sigSetFGColor(KoColor))); connect(this, SIGNAL(sigChangeActivePaintop(int)), m_resourceManager, SLOT(slotChangeActivePaintop(int))); connect(this, SIGNAL(sigUpdateRecentColor(int)), m_resourceManager, SLOT(slotUpdateRecentColor(int))); connect(m_resourceManager, SIGNAL(setSelectedColor(int)), SLOT(slotSetSelectedColor(int))); connect(m_resourceManager, SIGNAL(updatePalettes()), SLOT(slotUpdate())); connect(m_resourceManager, SIGNAL(hidePalettes()), SLOT(slotHide())); // This is used to handle a bug: // If pop up palette is visible and a new colour is selected, the new colour // will be added when the user clicks on the canvas to hide the palette // In general, we want to be able to store recent color if the pop up palette // is not visible m_timer.setSingleShot(true); connect(this, SIGNAL(sigTriggerTimer()), this, SLOT(slotTriggerTimer())); connect(&m_timer, SIGNAL(timeout()), this, SLOT(slotEnableChangeFGColor())); connect(this, SIGNAL(sigEnableChangeFGColor(bool)), m_resourceManager, SIGNAL(sigEnableChangeColor(bool))); setCursor(Qt::ArrowCursor); setMouseTracking(true); setHoveredPreset(-1); setHoveredColor(-1); setSelectedColor(-1); m_brushHud = new KisBrushHud(provider, parent); m_brushHud->setFixedHeight(int(m_popupPaletteSize)); m_brushHud->setVisible(false); const int auxButtonSize = 35; m_settingsButton = new KisRoundHudButton(this); m_settingsButton->setGeometry(m_popupPaletteSize - 2.2 * auxButtonSize, m_popupPaletteSize - auxButtonSize, auxButtonSize, auxButtonSize); connect(m_settingsButton, SIGNAL(clicked()), SLOT(slotShowTagsPopup())); KisConfig cfg(true); m_brushHudButton = new KisRoundHudButton(this); m_brushHudButton->setCheckable(true); m_brushHudButton->setGeometry(m_popupPaletteSize - 1.0 * auxButtonSize, m_popupPaletteSize - auxButtonSize, auxButtonSize, auxButtonSize); connect(m_brushHudButton, SIGNAL(toggled(bool)), SLOT(showHudWidget(bool))); m_brushHudButton->setChecked(cfg.showBrushHud()); // add some stuff below the pop-up palette that will make it easier to use for tablet people QVBoxLayout* vLayout = new QVBoxLayout(this); // main layout QSpacerItem* verticalSpacer = new QSpacerItem(0, 0, QSizePolicy::Fixed, QSizePolicy::Expanding); vLayout->addSpacerItem(verticalSpacer); // this should push the box to the bottom QHBoxLayout* hLayout = new QHBoxLayout(); vLayout->addLayout(hLayout); mirrorMode = new KisHighlightedToolButton(this); mirrorMode->setFixedSize(35, 35); mirrorMode->setToolTip(i18n("Mirror Canvas")); mirrorMode->setDefaultAction(m_actionCollection->action("mirror_canvas")); canvasOnlyButton = new KisHighlightedToolButton(this); canvasOnlyButton->setFixedSize(35, 35); canvasOnlyButton->setToolTip(i18n("Canvas Only")); canvasOnlyButton->setDefaultAction(m_actionCollection->action("view_show_canvas_only")); zoomToOneHundredPercentButton = new QPushButton(this); zoomToOneHundredPercentButton->setText(i18n("100%")); zoomToOneHundredPercentButton->setFixedHeight(35); zoomToOneHundredPercentButton->setToolTip(i18n("Zoom to 100%")); connect(zoomToOneHundredPercentButton, SIGNAL(clicked(bool)), this, SLOT(slotZoomToOneHundredPercentClicked())); zoomCanvasSlider = new QSlider(Qt::Horizontal, this); zoomSliderMinValue = 10; // set in % zoomSliderMaxValue = 200; // set in % zoomCanvasSlider->setRange(zoomSliderMinValue, zoomSliderMaxValue); zoomCanvasSlider->setFixedHeight(35); zoomCanvasSlider->setValue(m_coordinatesConverter->zoomInPercent()); zoomCanvasSlider->setSingleStep(1); zoomCanvasSlider->setPageStep(1); connect(zoomCanvasSlider, SIGNAL(valueChanged(int)), this, SLOT(slotZoomSliderChanged(int))); connect(zoomCanvasSlider, SIGNAL(sliderPressed()), this, SLOT(slotZoomSliderPressed())); connect(zoomCanvasSlider, SIGNAL(sliderReleased()), this, SLOT(slotZoomSliderReleased())); slotUpdateIcons(); hLayout->addWidget(mirrorMode); hLayout->addWidget(canvasOnlyButton); hLayout->addWidget(zoomToOneHundredPercentButton); hLayout->addWidget(zoomCanvasSlider); setVisible(true); setVisible(false); opacityChange = new QGraphicsOpacityEffect(this); setGraphicsEffect(opacityChange); // Prevent tablet events from being captured by the canvas setAttribute(Qt::WA_NoMousePropagation, true); } void KisPopupPalette::slotExternalFgColorChanged(const KoColor &color) { //hack to get around cmyk for now. if (color.colorSpace()->colorChannelCount()>3) { KoColor c(KoColorSpaceRegistry::instance()->rgb8()); c.fromKoColor(color); m_triangleColorSelector->slotSetColor(c); } else { m_triangleColorSelector->slotSetColor(color); } } void KisPopupPalette::slotEmitColorChanged() { if (isVisible()) { update(); emit sigChangefGColor(m_triangleColorSelector->getCurrentColor()); } } //setting KisPopupPalette properties int KisPopupPalette::hoveredPreset() const { return m_hoveredPreset; } void KisPopupPalette::setHoveredPreset(int x) { m_hoveredPreset = x; } int KisPopupPalette::hoveredColor() const { return m_hoveredColor; } void KisPopupPalette::setHoveredColor(int x) { m_hoveredColor = x; } int KisPopupPalette::selectedColor() const { return m_selectedColor; } void KisPopupPalette::setSelectedColor(int x) { m_selectedColor = x; } void KisPopupPalette::slotTriggerTimer() { m_timer.start(750); } void KisPopupPalette::slotEnableChangeFGColor() { emit sigEnableChangeFGColor(true); } void KisPopupPalette::slotZoomSliderChanged(int zoom) { emit zoomLevelChanged(zoom); } void KisPopupPalette::slotZoomSliderPressed() { m_isZoomingCanvas = true; } void KisPopupPalette::slotZoomSliderReleased() { m_isZoomingCanvas = false; } void KisPopupPalette::adjustLayout(const QPoint &p) { KIS_ASSERT_RECOVER_RETURN(m_brushHud); if (isVisible() && parentWidget()) { float hudMargin = 30.0; const QRect fitRect = kisGrowRect(parentWidget()->rect(), -20.0); // -20 is widget margin const QPoint paletteCenterOffset(m_popupPaletteSize / 2, m_popupPaletteSize / 2); QRect paletteRect = rect(); paletteRect.moveTo(p - paletteCenterOffset); if (m_brushHudButton->isChecked()) { m_brushHud->updateGeometry(); paletteRect.adjust(0, 0, m_brushHud->width() + hudMargin, 0); } paletteRect = kisEnsureInRect(paletteRect, fitRect); move(paletteRect.topLeft()); m_brushHud->move(paletteRect.topLeft() + QPoint(m_popupPaletteSize + hudMargin, 0)); m_lastCenterPoint = p; } } void KisPopupPalette::slotUpdateIcons() { zoomToOneHundredPercentButton->setIcon(m_actionCollection->action("zoom_to_100pct")->icon()); m_brushHud->updateIcons(); m_brushHudButton->setOnOffIcons(KisIconUtils::loadIcon("arrow-left"), KisIconUtils::loadIcon("arrow-right")); } void KisPopupPalette::showHudWidget(bool visible) { KIS_ASSERT_RECOVER_RETURN(m_brushHud); const bool reallyVisible = visible && m_brushHudButton->isChecked(); if (reallyVisible) { m_brushHud->updateProperties(); } m_brushHud->setVisible(reallyVisible); adjustLayout(m_lastCenterPoint); KisConfig cfg(false); cfg.setShowBrushHud(visible); } void KisPopupPalette::showPopupPalette(const QPoint &p) { showPopupPalette(!isVisible()); adjustLayout(p); } void KisPopupPalette::showPopupPalette(bool show) { if (show) { m_hadMousePressSinceOpening = false; m_timeSinceOpening.start(); // don't set the zoom slider if we are outside of the zoom slider bounds. It will change the zoom level to within // the bounds and cause the canvas to jump between the slider's min and max if (m_coordinatesConverter->zoomInPercent() > zoomSliderMinValue && m_coordinatesConverter->zoomInPercent() < zoomSliderMaxValue ){ KisSignalsBlocker b(zoomCanvasSlider); zoomCanvasSlider->setValue(m_coordinatesConverter->zoomInPercent()); // sync the zoom slider } emit sigEnableChangeFGColor(!show); } else { emit sigTriggerTimer(); } setVisible(show); m_brushHud->setVisible(show && m_brushHudButton->isChecked()); } //redefinition of setVariable function to change the scope to private void KisPopupPalette::setVisible(bool b) { QWidget::setVisible(b); } void KisPopupPalette::setParent(QWidget *parent) { m_brushHud->setParent(parent); QWidget::setParent(parent); } QSize KisPopupPalette::sizeHint() const { return QSize(m_popupPaletteSize, m_popupPaletteSize + 50); // last number is the space for the toolbar below } void KisPopupPalette::resizeEvent(QResizeEvent*) { } void KisPopupPalette::paintEvent(QPaintEvent* e) { Q_UNUSED(e); QPainter painter(this); QPen pen(palette().color(QPalette::Text)); pen.setWidth(3); painter.setPen(pen); painter.setRenderHint(QPainter::Antialiasing); painter.setRenderHint(QPainter::SmoothPixmapTransform); // painting background color indicator QPainterPath bgColor; bgColor.addEllipse(QPoint( 50, 80), 30, 30); painter.fillPath(bgColor, m_displayRenderer->toQColor(m_resourceManager->bgColor())); painter.drawPath(bgColor); // painting foreground color indicator QPainterPath fgColor; fgColor.addEllipse(QPoint( 60, 50), 30, 30); painter.fillPath(fgColor, m_displayRenderer->toQColor(m_triangleColorSelector->getCurrentColor())); painter.drawPath(fgColor); // create a circle background that everything else will go into QPainterPath backgroundContainer; float shrinkCircleAmount = 3;// helps the circle when the stroke is put around it QRectF circleRect(shrinkCircleAmount, shrinkCircleAmount, m_popupPaletteSize - shrinkCircleAmount*2,m_popupPaletteSize - shrinkCircleAmount*2); backgroundContainer.addEllipse( circleRect ); painter.fillPath(backgroundContainer,palette().brush(QPalette::Background)); painter.drawPath(backgroundContainer); // create a path slightly inside the container circle. this will create a 'track' to indicate that we can rotate the canvas // with the indicator QPainterPath rotationTrackPath; shrinkCircleAmount = 18; QRectF circleRect2(shrinkCircleAmount, shrinkCircleAmount, m_popupPaletteSize - shrinkCircleAmount*2,m_popupPaletteSize - shrinkCircleAmount*2); rotationTrackPath.addEllipse( circleRect2 ); pen.setWidth(1); painter.setPen(pen); painter.drawPath(rotationTrackPath); // this thing will help indicate where the starting brush preset is at. // also what direction they go to give sor order to the presets populated /* pen.setWidth(6); pen.setCapStyle(Qt::RoundCap); painter.setPen(pen); painter.drawArc(circleRect, (16*90), (16*-30)); // span angle (last parameter) is in 16th of degrees QPainterPath brushDir; brushDir.arcMoveTo(circleRect, 60); brushDir.lineTo(brushDir.currentPosition().x()-5, brushDir.currentPosition().y() - 14); painter.drawPath(brushDir); brushDir.lineTo(brushDir.currentPosition().x()-2, brushDir.currentPosition().y() + 6); painter.drawPath(brushDir); */ // the following things needs to be based off the center, so let's translate the painter painter.translate(m_popupPaletteSize / 2, m_popupPaletteSize / 2); // create the canvas rotation handle QPainterPath rotationIndicator = drawRotationIndicator(m_coordinatesConverter->rotationAngle(), true); painter.fillPath(rotationIndicator,palette().brush(QPalette::Text)); // hover indicator for the canvas rotation if (m_isOverCanvasRotationIndicator == true) { painter.save(); QPen pen(palette().color(QPalette::Highlight)); pen.setWidth(2); painter.setPen(pen); painter.drawPath(rotationIndicator); painter.restore(); } // create a reset canvas rotation indicator to bring the canvas back to 0 degrees QPainterPath resetRotationIndicator = drawRotationIndicator(0, false); QPen resetPen(palette().color(QPalette::Text)); resetPen.setWidth(1); painter.save(); painter.setPen(resetPen); painter.drawPath(resetRotationIndicator); painter.restore(); // painting favorite brushes QList images(m_resourceManager->favoritePresetImages()); // painting favorite brushes pixmap/icon QPainterPath presetPath; for (int pos = 0; pos < numSlots(); pos++) { painter.save(); presetPath = createPathFromPresetIndex(pos); if (pos < images.size()) { painter.setClipPath(presetPath); QRect bounds = presetPath.boundingRect().toAlignedRect(); painter.drawImage(bounds.topLeft() , images.at(pos).scaled(bounds.size() , Qt::KeepAspectRatioByExpanding, Qt::SmoothTransformation)); } else { painter.fillPath(presetPath, palette().brush(QPalette::Window)); // brush slot that has no brush in it } QPen pen = painter.pen(); pen.setWidth(1); painter.setPen(pen); painter.drawPath(presetPath); painter.restore(); } if (hoveredPreset() > -1) { presetPath = createPathFromPresetIndex(hoveredPreset()); QPen pen(palette().color(QPalette::Highlight)); pen.setWidth(3); painter.setPen(pen); painter.drawPath(presetPath); } // paint recent colors area. painter.setPen(Qt::NoPen); float rotationAngle = -360.0 / m_resourceManager->recentColorsTotal(); // there might be no recent colors at the start, so paint a placeholder if (m_resourceManager->recentColorsTotal() == 0) { painter.setBrush(Qt::transparent); QPainterPath emptyRecentColorsPath(drawDonutPathFull(0, 0, m_colorHistoryInnerRadius, m_colorHistoryOuterRadius)); painter.setPen(QPen(palette().color(QPalette::Background).lighter(150), 2, Qt::SolidLine, Qt::FlatCap, Qt::MiterJoin)); painter.drawPath(emptyRecentColorsPath); } else { for (int pos = 0; pos < m_resourceManager->recentColorsTotal(); pos++) { QPainterPath recentColorsPath(drawDonutPathAngle(m_colorHistoryInnerRadius, m_colorHistoryOuterRadius, m_resourceManager->recentColorsTotal())); //accessing recent color of index pos painter.fillPath(recentColorsPath, m_displayRenderer->toQColor( m_resourceManager->recentColorAt(pos) )); painter.drawPath(recentColorsPath); painter.rotate(rotationAngle); } } // painting hovered color if (hoveredColor() > -1) { painter.setPen(QPen(palette().color(QPalette::Highlight), 2, Qt::SolidLine, Qt::FlatCap, Qt::MiterJoin)); if (m_resourceManager->recentColorsTotal() == 1) { QPainterPath path_ColorDonut(drawDonutPathFull(0, 0, m_colorHistoryInnerRadius, m_colorHistoryOuterRadius)); painter.drawPath(path_ColorDonut); } else { painter.rotate((m_resourceManager->recentColorsTotal() + hoveredColor()) *rotationAngle); QPainterPath path(drawDonutPathAngle(m_colorHistoryInnerRadius, m_colorHistoryOuterRadius, m_resourceManager->recentColorsTotal())); painter.drawPath(path); painter.rotate(hoveredColor() * -1 * rotationAngle); } } // painting selected color if (selectedColor() > -1) { painter.setPen(QPen(palette().color(QPalette::Highlight).darker(130), 2, Qt::SolidLine, Qt::FlatCap, Qt::MiterJoin)); if (m_resourceManager->recentColorsTotal() == 1) { QPainterPath path_ColorDonut(drawDonutPathFull(0, 0, m_colorHistoryInnerRadius, m_colorHistoryOuterRadius)); painter.drawPath(path_ColorDonut); } else { painter.rotate((m_resourceManager->recentColorsTotal() + selectedColor()) *rotationAngle); QPainterPath path(drawDonutPathAngle(m_colorHistoryInnerRadius, m_colorHistoryOuterRadius, m_resourceManager->recentColorsTotal())); painter.drawPath(path); painter.rotate(selectedColor() * -1 * rotationAngle); } } // if we are actively rotating the canvas or zooming, make the panel slightly transparent to see the canvas better if(m_isRotatingCanvasIndicator || m_isZoomingCanvas) { opacityChange->setOpacity(0.4); } else { opacityChange->setOpacity(1.0); } } QPainterPath KisPopupPalette::drawDonutPathFull(int x, int y, int inner_radius, int outer_radius) { QPainterPath path; path.addEllipse(QPointF(x, y), outer_radius, outer_radius); path.addEllipse(QPointF(x, y), inner_radius, inner_radius); path.setFillRule(Qt::OddEvenFill); return path; } QPainterPath KisPopupPalette::drawDonutPathAngle(int inner_radius, int outer_radius, int limit) { QPainterPath path; path.moveTo(-0.999 * outer_radius * sin(M_PI / limit), 0.999 * outer_radius * cos(M_PI / limit)); path.arcTo(-1 * outer_radius, -1 * outer_radius, 2 * outer_radius, 2 * outer_radius, -90.0 - 180.0 / limit, 360.0 / limit); path.arcTo(-1 * inner_radius, -1 * inner_radius, 2 * inner_radius, 2 * inner_radius, -90.0 + 180.0 / limit, - 360.0 / limit); path.closeSubpath(); return path; } QPainterPath KisPopupPalette::drawRotationIndicator(qreal rotationAngle, bool canDrag) { // used for canvas rotation. This function gets called twice. Once by the canvas rotation indicator, // and another time by the reset canvas position float canvasRotationRadians = qDegreesToRadians(rotationAngle - 90); // -90 will make 0 degrees be at the top float rotationDialXPosition = qCos(canvasRotationRadians) * (m_popupPaletteSize/2 - 10); // m_popupPaletteSize/2 = radius float rotationDialYPosition = qSin(canvasRotationRadians) * (m_popupPaletteSize/2 - 10); QPainterPath canvasRotationIndicator; int canvasIndicatorSize = 15; float canvasIndicatorMiddle = canvasIndicatorSize/2; QRect indicatorRectangle = QRect( rotationDialXPosition - canvasIndicatorMiddle, rotationDialYPosition - canvasIndicatorMiddle, canvasIndicatorSize, canvasIndicatorSize ); if (canDrag) { m_canvasRotationIndicatorRect = indicatorRectangle; } else { m_resetCanvasRotationIndicatorRect = indicatorRectangle; } canvasRotationIndicator.addEllipse(indicatorRectangle.x(), indicatorRectangle.y(), indicatorRectangle.width(), indicatorRectangle.height() ); return canvasRotationIndicator; } void KisPopupPalette::mouseMoveEvent(QMouseEvent *event) { QPointF point = event->localPos(); event->accept(); setToolTip(QString()); setHoveredPreset(-1); setHoveredColor(-1); // calculate if we are over the canvas rotation knob // before we started painting, we moved the painter to the center of the widget, so the X/Y positions are offset. we need to // correct them first before looking for a click event intersection float rotationCorrectedXPos = m_canvasRotationIndicatorRect.x() + (m_popupPaletteSize / 2); float rotationCorrectedYPos = m_canvasRotationIndicatorRect.y() + (m_popupPaletteSize / 2); QRect correctedCanvasRotationIndicator = QRect(rotationCorrectedXPos, rotationCorrectedYPos, m_canvasRotationIndicatorRect.width(), m_canvasRotationIndicatorRect.height()); if (correctedCanvasRotationIndicator.contains(point.x(), point.y())) { m_isOverCanvasRotationIndicator = true; } else { m_isOverCanvasRotationIndicator = false; } if (m_isRotatingCanvasIndicator) { // we are rotating the canvas, so calculate the rotation angle based off the center // calculate the angle we are at first QPoint widgetCenterPoint = QPoint(m_popupPaletteSize/2, m_popupPaletteSize/2); float dX = point.x() - widgetCenterPoint.x(); float dY = point.y() - widgetCenterPoint.y(); float finalAngle = qAtan2(dY,dX) * 180 / M_PI; // what we need if we have two points, but don't know the angle finalAngle = finalAngle + 90; // add 90 degrees so 0 degree position points up float angleDifference = finalAngle - m_coordinatesConverter->rotationAngle(); // the rotation function accepts diffs, so find it out KisCanvasController *canvasController = dynamic_cast(m_viewManager->canvasBase()->canvasController()); canvasController->rotateCanvas(angleDifference); emit sigUpdateCanvas(); } // don't highlight the presets if we are in the middle of rotating the canvas if (m_isRotatingCanvasIndicator == false) { QPainterPath pathColor(drawDonutPathFull(m_popupPaletteSize / 2, m_popupPaletteSize / 2, m_colorHistoryInnerRadius, m_colorHistoryOuterRadius)); { int pos = calculatePresetIndex(point, m_resourceManager->numFavoritePresets()); if (pos >= 0 && pos < m_resourceManager->numFavoritePresets()) { setToolTip(m_resourceManager->favoritePresetList().at(pos).data()->name()); setHoveredPreset(pos); } } if (pathColor.contains(point)) { int pos = calculateIndex(point, m_resourceManager->recentColorsTotal()); if (pos >= 0 && pos < m_resourceManager->recentColorsTotal()) { setHoveredColor(pos); } } } update(); } void KisPopupPalette::mousePressEvent(QMouseEvent *event) { QPointF point = event->localPos(); event->accept(); /** * Tablet support code generates a spurious right-click right after opening * the window, so we should ignore it. Next right-click will be used for * closing the popup palette */ if (!m_hadMousePressSinceOpening && m_timeSinceOpening.elapsed() > 100) { m_hadMousePressSinceOpening = true; } if (event->button() == Qt::LeftButton) { //in favorite brushes area int pos = calculateIndex(point, m_resourceManager->numFavoritePresets()); if (pos >= 0 && pos < m_resourceManager->numFavoritePresets() && isPointInPixmap(point, pos)) { //setSelectedBrush(pos); update(); } if (m_isOverCanvasRotationIndicator) { m_isRotatingCanvasIndicator = true; } // reset the canvas if we are over the reset canvas rotation indicator float rotationCorrectedXPos = m_resetCanvasRotationIndicatorRect.x() + (m_popupPaletteSize / 2); float rotationCorrectedYPos = m_resetCanvasRotationIndicatorRect.y() + (m_popupPaletteSize / 2); QRect correctedResetCanvasRotationIndicator = QRect(rotationCorrectedXPos, rotationCorrectedYPos, m_resetCanvasRotationIndicatorRect.width(), m_resetCanvasRotationIndicatorRect.height()); if (correctedResetCanvasRotationIndicator.contains(point.x(), point.y())) { float angleDifference = -m_coordinatesConverter->rotationAngle(); // the rotation function accepts diffs KisCanvasController *canvasController = dynamic_cast(m_viewManager->canvasBase()->canvasController()); canvasController->rotateCanvas(angleDifference); emit sigUpdateCanvas(); } } } void KisPopupPalette::slotShowTagsPopup() { - KisPaintOpPresetResourceServer *rServer = KisResourceServerProvider::instance()->paintOpPresetServer(); - QStringList tags = rServer->tagNamesList(); - std::sort(tags.begin(), tags.end()); - - if (!tags.isEmpty()) { - QMenu menu; - Q_FOREACH (const QString& tag, tags) { - menu.addAction(tag); - } - - QAction *action = menu.exec(QCursor::pos()); - if (action) { - m_resourceManager->setCurrentTag(action->text()); - } - } else { - QWhatsThis::showText(QCursor::pos(), - i18n("There are no tags available to show in this popup. To add presets, you need to tag them and then select the tag here.")); - } +// KisTagModel *model = new KisTagModel(ResourceType::PaintOpPresets); +// QSet tags; +// for (int i = 0; i < model.rowCount(); ++i) { +// QModelIndex idx = model.index(i, 0); +// tags << model.data(idx, Qt::DisplayRole).toString(); +// } + +// std::sort(tags.begin(), tags.end()); + +// if (!tags.isEmpty()) { +// QMenu menu; +// Q_FOREACH (const QString& tag, tags) { +// menu.addAction(tag); +// } + +// QAction *action = menu.exec(QCursor::pos()); +// if (action) { +// m_resourceManager->setCurrentTag(action->text()); +// } +// } else { +// QWhatsThis::showText(QCursor::pos(), +// i18n("There are no tags available to show in this popup. To add presets, you need to tag them and then select the tag here.")); +// } + +// delete model; } void KisPopupPalette::slotZoomToOneHundredPercentClicked() { QAction *action = m_actionCollection->action("zoom_to_100pct"); if (action) { action->trigger(); } // also move the zoom slider to 100% position so they are in sync zoomCanvasSlider->setValue(100); } void KisPopupPalette::tabletEvent(QTabletEvent *event) { event->ignore(); } void KisPopupPalette::mouseReleaseEvent(QMouseEvent *event) { QPointF point = event->localPos(); event->accept(); // see a comment in KisPopupPalette::mousePressEvent if (m_hadMousePressSinceOpening && event->buttons() == Qt::NoButton && event->button() == Qt::RightButton) { showPopupPalette(false); return; } m_isOverCanvasRotationIndicator = false; m_isRotatingCanvasIndicator = false; if (event->button() == Qt::LeftButton) { QPainterPath pathColor(drawDonutPathFull(m_popupPaletteSize / 2, m_popupPaletteSize / 2, m_colorHistoryInnerRadius, m_colorHistoryOuterRadius)); //in favorite brushes area if (hoveredPreset() > -1) { //setSelectedBrush(hoveredBrush()); emit sigChangeActivePaintop(hoveredPreset()); } if (pathColor.contains(point)) { int pos = calculateIndex(point, m_resourceManager->recentColorsTotal()); if (pos >= 0 && pos < m_resourceManager->recentColorsTotal()) { emit sigUpdateRecentColor(pos); } } } } int KisPopupPalette::calculateIndex(QPointF point, int n) { calculatePresetIndex(point, n); //translate to (0,0) point.setX(point.x() - m_popupPaletteSize / 2); point.setY(point.y() - m_popupPaletteSize / 2); //rotate float smallerAngle = M_PI / 2 + M_PI / n - atan2(point.y(), point.x()); float radius = sqrt((float)point.x() * point.x() + point.y() * point.y()); point.setX(radius * cos(smallerAngle)); point.setY(radius * sin(smallerAngle)); //calculate brush index int pos = floor(acos(point.x() / radius) * n / (2 * M_PI)); if (point.y() < 0) pos = n - pos - 1; return pos; } bool KisPopupPalette::isPointInPixmap(QPointF &point, int pos) { if (createPathFromPresetIndex(pos).contains(point + QPointF(-m_popupPaletteSize / 2, -m_popupPaletteSize / 2))) { return true; } return false; } KisPopupPalette::~KisPopupPalette() { } QPainterPath KisPopupPalette::createPathFromPresetIndex(int index) { qreal angleSlice = 360.0 / numSlots() ; // how many degrees each slice will get // the starting angle of the slice we need to draw. the negative sign makes us go clockwise. // adding 90 degrees makes us start at the top. otherwise we would start at the right qreal startingAngle = -(index * angleSlice) + 90; // the radius will get smaller as the amount of presets shown increases. 10 slots == 41 qreal presetRadius = m_colorHistoryOuterRadius * qSin(qDegreesToRadians(angleSlice/2)) / (1-qSin(qDegreesToRadians(angleSlice/2))); QPainterPath path; float pathX = (m_colorHistoryOuterRadius + presetRadius) * qCos(qDegreesToRadians(startingAngle)) - presetRadius; float pathY = -(m_colorHistoryOuterRadius + presetRadius) * qSin(qDegreesToRadians(startingAngle)) - presetRadius; float pathDiameter = 2 * presetRadius; // distance is used to calculate the X/Y in addition to the preset circle size path.addEllipse(pathX, pathY, pathDiameter, pathDiameter); return path; } int KisPopupPalette::calculatePresetIndex(QPointF point, int /*n*/) { for(int i = 0; i < numSlots(); i++) { QPointF adujustedPoint = point - QPointF(m_popupPaletteSize/2, m_popupPaletteSize/2); if(createPathFromPresetIndex(i).contains(adujustedPoint)) { return i; } } return -1; } int KisPopupPalette::numSlots() { KisConfig config(true); return qMax(config.favoritePresets(), 10); } diff --git a/libs/ui/widgets/kis_paintop_presets_save.cpp b/libs/ui/widgets/kis_paintop_presets_save.cpp index d689b3d96e..266c422a59 100644 --- a/libs/ui/widgets/kis_paintop_presets_save.cpp +++ b/libs/ui/widgets/kis_paintop_presets_save.cpp @@ -1,271 +1,271 @@ /* This file is part of the KDE project * Copyright (C) 2017 Scott Petrovic * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "widgets/kis_paintop_presets_save.h" #include #include #include #include #include #include #include "KisImportExportManager.h" #include "QDesktopServices" #include "KisResourceServerProvider.h" #include KisPresetSaveWidget::KisPresetSaveWidget(QWidget * parent) : KisPaintOpPresetSaveDialog(parent) { // this is setting the area we will "capture" for saving the brush preset. It can potentially be a different // area that the entire scratchpad brushPresetThumbnailWidget->setCutoutOverlayRect(QRect(0, 0, brushPresetThumbnailWidget->height(), brushPresetThumbnailWidget->width())); // we will default to reusing the previous preset thumbnail // have that checked by default, hide the other elements, and load the last preset image connect(clearBrushPresetThumbnailButton, SIGNAL(clicked(bool)), brushPresetThumbnailWidget, SLOT(fillDefault())); connect(loadImageIntoThumbnailButton, SIGNAL(clicked(bool)), this, SLOT(loadImageFromFile())); connect(loadScratchPadThumbnailButton, SIGNAL(clicked(bool)), this, SLOT(loadScratchpadThumbnail())); connect(loadExistingThumbnailButton, SIGNAL(clicked(bool)), this, SLOT(loadExistingThumbnail())); connect(loadIconLibraryThumbnailButton, SIGNAL(clicked(bool)), this, SLOT(loadImageFromLibrary())); connect(savePresetButton, SIGNAL(clicked(bool)), this, SLOT(savePreset())); connect(cancelButton, SIGNAL(clicked(bool)), this, SLOT(close())); } KisPresetSaveWidget::~KisPresetSaveWidget() { } void KisPresetSaveWidget::scratchPadSetup(KisCanvasResourceProvider* resourceProvider) { m_resourceProvider = resourceProvider; brushPresetThumbnailWidget->setupScratchPad(m_resourceProvider, Qt::white); } void KisPresetSaveWidget::showDialog() { setModal(true); // set the name of the current brush preset area. KisPaintOpPresetSP preset = m_resourceProvider->currentPreset(); // UI will look a bit different if we are saving a new brush if (m_useNewBrushDialog) { setWindowTitle(i18n("Save New Brush Preset")); newBrushNameTexField->setVisible(true); clearBrushPresetThumbnailButton->setVisible(true); loadImageIntoThumbnailButton->setVisible(true); currentBrushNameLabel->setVisible(false); if (preset) { newBrushNameTexField->setText(preset->name().append(" ").append(i18n("Copy"))); } } else { setWindowTitle(i18n("Save Brush Preset")); if (preset) { currentBrushNameLabel->setText(preset->name()); } newBrushNameTexField->setVisible(false); currentBrushNameLabel->setVisible(true); } brushPresetThumbnailWidget->paintPresetImage(); show(); } void KisPresetSaveWidget::loadImageFromFile() { // create a dialog to retrieve an image file. KoFileDialog dialog(0, KoFileDialog::OpenFile, "OpenDocument"); dialog.setMimeTypeFilters(KisImportExportManager::supportedMimeTypes(KisImportExportManager::Import)); dialog.setDefaultDir(QStandardPaths::writableLocation(QStandardPaths::HomeLocation)); QString filename = dialog.filename(); // the filename() returns the entire path & file name, not just the file name if (filename != "") { // empty if "cancel" is pressed // take that file and load it into the thumbnail are const QImage imageToLoad(filename); brushPresetThumbnailWidget->fillTransparent(); // clear the background in case our new image has transparency brushPresetThumbnailWidget->paintCustomImage(imageToLoad); } } void KisPresetSaveWidget::loadScratchpadThumbnail() { brushPresetThumbnailWidget->paintCustomImage(scratchPadThumbnailArea); } void KisPresetSaveWidget::loadExistingThumbnail() { brushPresetThumbnailWidget->paintPresetImage(); } void KisPresetSaveWidget::loadImageFromLibrary() { //add dialog code here. QDialog *dlg = new QDialog(this); dlg->setWindowTitle(i18n("Preset Icon Library")); QVBoxLayout *layout = new QVBoxLayout(); dlg->setLayout(layout); KisPaintopPresetIconLibrary *libWidget = new KisPaintopPresetIconLibrary(dlg); layout->addWidget(libWidget); QDialogButtonBox *buttons = new QDialogButtonBox(QDialogButtonBox::Ok|QDialogButtonBox::Cancel, dlg); connect(buttons, SIGNAL(accepted()), dlg, SLOT(accept())); connect(buttons, SIGNAL(rejected()), dlg, SLOT(reject())); layout->addWidget(buttons); //if dialog accepted, get image. if (dlg->exec()==QDialog::Accepted) { QImage presetImage = libWidget->getImage(); brushPresetThumbnailWidget->paintCustomImage(presetImage); } } void KisPresetSaveWidget::setFavoriteResourceManager(KisFavoriteResourceManager * favManager) { m_favoriteResourceManager = favManager; } void KisPresetSaveWidget::savePreset() { KisPaintOpPresetSP curPreset = m_resourceProvider->currentPreset(); if (!curPreset) return; m_favoriteResourceManager->setBlockUpdates(true); KisPaintOpPresetSP oldPreset = curPreset->clone(); // tags are not cloned with this oldPreset->load(); KisPaintOpPresetResourceServer * rServer = KisResourceServerProvider::instance()->paintOpPresetServer(); QString saveLocation = rServer->saveLocation(); // if we are saving a new brush, use what we type in for the input QString presetName = m_useNewBrushDialog ? newBrushNameTexField->text() : curPreset->name(); QString currentPresetFileName = saveLocation + presetName.replace(" ", "_") + curPreset->defaultFileExtension(); bool isSavingOverExistingPreset = rServer->resourceByName(presetName); // make a back up of the existing preset if we are saving over it if (isSavingOverExistingPreset) { QString currentDate = QDate::currentDate().toString(Qt::ISODate); QString currentTime = QTime::currentTime().toString(Qt::ISODate).remove(QChar(':')); QString presetFilename = saveLocation + presetName.replace(" ", "_") + "_backup_" + currentDate + "-" + currentTime + oldPreset->defaultFileExtension(); oldPreset->setFilename(presetFilename); oldPreset->setName(presetName); oldPreset->setDirty(false); oldPreset->setValid(true); // add backup resource to the blacklist rServer->addResource(oldPreset); rServer->removeResourceAndBlacklist(oldPreset); QStringList tags; tags = rServer->assignedTagsList(curPreset); Q_FOREACH (const QString & tag, tags) { rServer->addTag(oldPreset, tag); } } if (m_useNewBrushDialog) { KisPaintOpPresetSP newPreset = curPreset->clone(); newPreset->setFilename(currentPresetFileName); newPreset->setName(presetName); newPreset->setImage(brushPresetThumbnailWidget->cutoutOverlay()); newPreset->setDirty(false); newPreset->setValid(true); // keep tags if we are saving over existing preset if (isSavingOverExistingPreset) { QStringList tags; tags = rServer->assignedTagsList(curPreset); Q_FOREACH (const QString & tag, tags) { rServer->addTag(newPreset, tag); } } rServer->addResource(newPreset); // trying to get brush preset to load after it is created emit resourceSelected(newPreset); } else { // saving a preset that is replacing an existing one if (curPreset->filename().contains(saveLocation) == false || curPreset->filename().contains(presetName) == false) { rServer->removeResourceAndBlacklist(curPreset); curPreset->setFilename(currentPresetFileName); curPreset->setName(presetName); } if (!rServer->resourceByFilename(curPreset->filename())){ //this is necessary so that we can get the preset afterwards. rServer->addResource(curPreset, false); //rServer->removeFromBlacklist(curPreset); } if (curPreset->image().isNull()) { curPreset->setImage(brushPresetThumbnailWidget->cutoutOverlay()); } // we should not load() the brush right after saving because it will reset all our saved // eraser size and opacity values curPreset->save(); } - // HACK ALERT! the server does not notify the observers - // automatically, so we need to call theupdate manually! - rServer->tagCategoryMembersChanged(); +// // HACK ALERT! the server does not notify the observers +// // automatically, so we need to call theupdate manually! +// rServer->tagCategoryMembersChanged(); m_favoriteResourceManager->setBlockUpdates(false); close(); // we are done... so close the save brush dialog } void KisPresetSaveWidget::saveScratchPadThumbnailArea(QImage image) { scratchPadThumbnailArea = image; } void KisPresetSaveWidget::useNewBrushDialog(bool show) { m_useNewBrushDialog = show; } #include "moc_kis_paintop_presets_save.cpp" diff --git a/libs/widgets/CMakeLists.txt b/libs/widgets/CMakeLists.txt index e678aba10f..ee39988924 100644 --- a/libs/widgets/CMakeLists.txt +++ b/libs/widgets/CMakeLists.txt @@ -1,125 +1,124 @@ add_subdirectory( tests ) include_directories(${CMAKE_CURRENT_BINARY_DIR}) set(kritawidgets_LIB_SRCS KoGradientEditWidget.cpp KoVBox.cpp KoDialog.cpp KoZoomWidget.cpp KoAspectButton.cpp KoPagePreviewWidget.cpp KoSliderCombo.cpp KoColorPopupButton.cpp KoConfigAuthorPage.cpp KoUnitDoubleSpinBox.cpp KoZoomAction.cpp KoZoomController.cpp KoZoomInput.cpp KoZoomHandler.cpp KoZoomMode.cpp KoDpi.cpp KoColorPatch.cpp KoColorPopupAction.cpp KoColorSetWidget.cpp KoColorSlider.cpp KoTriangleColorSelector.cpp KoResourcePopupAction.cpp - KoResourceTagStore.cpp KoRuler.cpp KoResourceServerProvider.cpp KoLineStyleSelector.cpp KoLineStyleItemDelegate.cpp KoLineStyleModel.cpp KoTitledTabWidget.cpp KoToolBoxButton.cpp KoToolBox.cpp KoToolBoxDocker.cpp KoToolBoxFactory.cpp KoToolDocker.cpp KoPageLayoutWidget.cpp KoPageLayoutDialog.cpp KoShadowConfigWidget.cpp KoMarkerSelector.cpp KoMarkerModel.cpp KoMarkerItemDelegate.cpp KoDocumentInfoDlg.cpp WidgetsDebug.cpp kis_file_name_requester.cpp kis_double_parse_spin_box.cpp kis_double_parse_unit_spin_box.cpp kis_int_parse_spin_box.cpp KisColorSelectorInterface.cpp KoAnchorSelectionWidget.cpp KisGradientSlider.cpp KisGradientSliderWidget.cpp kis_color_input.cpp # classes used by internal color selector kis_spinbox_color_selector.cpp KisVisualColorSelector.cpp KisVisualColorSelectorShape.cpp KisVisualEllipticalSelectorShape.cpp KisVisualRectangleSelectorShape.cpp KisVisualTriangleSelectorShape.cpp KisScreenColorPickerBase.cpp KisDlgInternalColorSelector.cpp KisPaletteModel.cpp KisPaletteDelegate.cpp kis_palette_view.cpp KisPaletteListWidget.cpp KisPaletteComboBox.cpp kis_popup_button.cc kis_color_button.cpp ) ki18n_wrap_ui( kritawidgets_LIB_SRCS KoConfigAuthorPage.ui koDocumentInfoAboutWidget.ui koDocumentInfoAuthorWidget.ui wdg_file_name_requester.ui KoPageLayoutWidget.ui KoShadowConfigWidget.ui WdgDlgInternalColorSelector.ui WdgPaletteListWidget.ui ) add_library(kritawidgets SHARED ${kritawidgets_LIB_SRCS}) generate_export_header(kritawidgets BASE_NAME kritawidgets) target_link_libraries(kritawidgets kritaodf kritaglobal kritaflake kritapigment kritawidgetutils kritaresources kritaresourcewidgets Qt5::PrintSupport KF5::CoreAddons KF5::ConfigGui KF5::GuiAddons KF5::WidgetsAddons KF5::ConfigCore KF5::Completion ) if(X11_FOUND) target_link_libraries(kritawidgets Qt5::X11Extras ${X11_LIBRARIES}) endif() set_target_properties(kritawidgets PROPERTIES VERSION ${GENERIC_KRITA_LIB_VERSION} SOVERSION ${GENERIC_KRITA_LIB_SOVERSION} ) install(TARGETS kritawidgets ${INSTALL_TARGETS_DEFAULT_ARGS}) diff --git a/libs/widgets/KoResourceServer.h b/libs/widgets/KoResourceServer.h index 58c707fc05..bf06c111df 100644 --- a/libs/widgets/KoResourceServer.h +++ b/libs/widgets/KoResourceServer.h @@ -1,545 +1,367 @@ /* This file is part of the KDE project Copyright (c) 1999 Matthias Elter Copyright (c) 2003 Patrick Julien Copyright (c) 2005 Sven Langkamp Copyright (c) 2007 Jan Hambrecht Copyright (C) 2011 Srikanth Tiyyagura Copyright (c) 2013 Sascha Suelzer This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef KORESOURCESERVER_H #define KORESOURCESERVER_H #include #include #include #include #include #include #include #include #include "KoResource.h" #include "KoResourceServerObserver.h" -#include "KoResourceTagStore.h" #include "KoResourcePaths.h" #include #include #include #include #include "kritawidgets_export.h" #include "WidgetsDebug.h" class KoResource; /** * KoResourceServerBase is the base class of all resource servers */ class KRITAWIDGETS_EXPORT KoResourceServerBase { public: /** * Constructs a KoResourceServerBase * @param type type, has to be the same as used by KoResourcePaths */ KoResourceServerBase(const QString& type) : m_resourceModel(KisResourceModelProvider::resourceModel(type)) , m_type(type) { qDebug() << "Creating KoResourceServerBase" << m_type; } virtual ~KoResourceServerBase() {} virtual int resourceCount() const = 0; - virtual QStringList queryResources(const QString &query) const = 0; - QString type() const { return m_type; } protected: KisResourceModel *m_resourceModel {0}; - - friend class KoResourceTagStore; - virtual KoResourceSP byMd5(const QByteArray &md5) const = 0; - virtual KoResourceSP byFileName(const QString &fileName) const = 0; - -private: QString m_type; }; /** * KoResourceServer manages the resources of one type. It stores, * loads and saves the resources. To keep track of changes the server * can be observed with a KoResourceServerObserver */ template class KoResourceServer : public KoResourceServerBase { public: typedef KoResourceServerObserver ObserverType; KoResourceServer(const QString& type) : KoResourceServerBase(type) { - m_blackListFile = KoResourcePaths::locateLocal("data", type + ".blacklist"); - m_tagStore = new KoResourceTagStore(this); } ~KoResourceServer() override { - if (m_tagStore) { - delete m_tagStore; - } - Q_FOREACH (ObserverType* observer, m_observers) { observer->unsetResourceServer(); } - m_resources.clear(); } int resourceCount() const override { return m_resourceModel->rowCount(); } - void loadTags() { - m_tagStore->loadTags(); - } - - void clearOldSystemTags() { - m_tagStore->clearOldSystemTags(); - } - /// Adds an already loaded resource to the server bool addResource(QSharedPointer resource, bool save = true) { if (!resource->valid()) { warnWidgets << "Tried to add an invalid resource!"; return false; } if (save) { QFileInfo fileInfo(resource->filename()); QDir d(fileInfo.path()); if (!d.exists()) { d.mkdir(fileInfo.path()); } if (fileInfo.exists()) { QString filename = fileInfo.path() + "/" + fileInfo.baseName() + "XXXXXX" + "." + fileInfo.suffix(); debugWidgets << "fileName is " << filename; QTemporaryFile file(filename); if (file.open()) { debugWidgets << "now " << file.fileName(); resource->setFilename(file.fileName()); } } if (!resource->save()) { warnWidgets << "Could not save resource!"; return false; } } Q_ASSERT(!resource->filename().isEmpty() || !resource->name().isEmpty()); if (resource->filename().isEmpty()) { resource->setFilename(resource->name()); } else if (resource->name().isEmpty()) { resource->setName(resource->filename()); } - m_resourcesByFilename[resource->shortFilename()] = resource; - addResourceToMd5Registry(resource); - m_resourcesByName[resource->name()] = resource; - notifyResourceAdded(resource); return true; } /// Remove a resource from Resource Server but not from a file bool removeResourceFromServer(QSharedPointer resource){ - if ( !m_resourcesByFilename.contains( resource->shortFilename() ) ) { - return false; - } - removeResourceFromMd5Registry(resource); - m_resourcesByName.remove(resource->name()); - m_resourcesByFilename.remove(resource->shortFilename()); - m_resources.removeAt(m_resources.indexOf(resource)); - m_tagStore->removeResource(resource); - notifyRemovingResource(resource); - return true; } /// Remove a resource from the resourceserver and blacklist it bool removeResourceAndBlacklist(QSharedPointer resource) { - - if ( !m_resourcesByFilename.contains( resource->shortFilename() ) ) { - return false; - } - removeResourceFromMd5Registry(resource); - m_resourcesByName.remove(resource->name()); - m_resourcesByFilename.remove(resource->shortFilename()); - m_resources.removeAt(m_resources.indexOf(resource)); - m_tagStore->removeResource(resource); - notifyRemovingResource(resource); return true; } QList> resources() { QList> resourceList; for (int row = 0; row < m_resourceModel->rowCount(); ++row) { resourceList << m_resourceModel->resourceForIndex(m_resourceModel->index(row, 0)).dynamicCast(); } return resourceList; } /// Returns path where to save user defined and imported resources to virtual QString saveLocation() { - return KoResourcePaths::saveLocation(type().toLatin1()); + return KoResourcePaths::saveLocation(m_type.toLatin1()); } /** * Creates a new resource from a given file and adds them to the resource server * The base implementation does only load one resource per file, override to implement collections * @param filename file name of the resource file to be imported * @param fileCreation decides whether to create the file in the saveLocation() directory */ virtual bool importResourceFile(const QString & filename , bool fileCreation=true) { QFileInfo fi(filename); if (!fi.exists()) return false; if ( fi.size() == 0) return false; QSharedPointer resource = createResource( filename ); resource->load(); if (!resource->valid()) { warnWidgets << "Import failed! Resource is not valid"; return false; } if (fileCreation) { Q_ASSERT(!resource->defaultFileExtension().isEmpty()); Q_ASSERT(!saveLocation().isEmpty()); QString newFilename = saveLocation() + fi.baseName() + resource->defaultFileExtension(); QFileInfo fileInfo(newFilename); int i = 1; while (fileInfo.exists()) { fileInfo.setFile(saveLocation() + fi.baseName() + QString("%1").arg(i) + resource->defaultFileExtension()); i++; } resource->setFilename(fileInfo.filePath()); } return true; } /// Removes the resource file from the resource server void removeResourceFile(const QString & filename) { QFileInfo fi(filename); QSharedPointer resource = resourceByFilename(fi.fileName()); if (!resource) { warnWidgets << "Resource file do not exist "; return; } removeResourceFromServer(resource); } /** * Addes an observer to the server * @param observer the observer to be added * @param notifyLoadedResources determines if the observer should be notified about the already loaded resources */ void addObserver(ObserverType* observer, bool notifyLoadedResources = true) { if (observer && !m_observers.contains(observer)) { m_observers.append(observer); if(notifyLoadedResources) { - Q_FOREACH (QSharedPointer resource, m_resourcesByFilename) { - observer->resourceAdded(resource); +// Q_FOREACH (QSharedPointer resource, ByFilename) { +// observer->resourceAdded(resource); - } +// } } } } /** * Removes an observer from the server * @param observer the observer to be removed */ void removeObserver(ObserverType* observer) { int index = m_observers.indexOf( observer ); if( index < 0 ) return; m_observers.removeAt( index ); } QSharedPointer resourceByFilename(const QString& /*filename*/) const { // if (m_resourcesByFilename.contains(filename)) { // return m_resourcesByFilename[filename]; // } return 0; } QSharedPointer resourceByName( const QString& /*name */) const { // if (m_resourcesByName.contains(name)) { // return m_resourcesByName[name]; // } return 0; } QSharedPointer resourceByMD5(const QByteArray& /*md5*/) const { // return m_resourcesByMd5.value(md5); return 0; } /** * Call after changing the content of a resource; * Notifies the connected views. */ void updateResource(QSharedPointer resource) { notifyResourceChanged(resource); } - QStringList tagNamesList() const - { - return m_tagStore->tagNamesList(); - } // don't use these method directly since it doesn't update views! void addTag(KoResourceSP resource, const QString& tag) { - m_tagStore->addTag(resource, tag); +// m_tagStore->addTag(resource, tag); } // don't use these method directly since it doesn't update views! void delTag(KoResourceSP resource, const QString& tag) { - m_tagStore->delTag(resource, tag); +// m_tagStore->delTag(resource, tag); } - QStringList searchTag(const QString& lineEditText) - { - return m_tagStore->searchTag(lineEditText); - } - - void tagCategoryAdded(const QString& tag) - { - m_tagStore->serializeTags(); - Q_FOREACH (ObserverType* observer, m_observers) { - observer->syncTagAddition(tag); - } - } - - void tagCategoryRemoved(const QString& tag) - { - m_tagStore->delTag(tag); - m_tagStore->serializeTags(); - Q_FOREACH (ObserverType* observer, m_observers) { - observer->syncTagRemoval(tag); - } - } - - void tagCategoryMembersChanged() - { - m_tagStore->serializeTags(); - Q_FOREACH (ObserverType* observer, m_observers) { - observer->syncTaggedResourceView(); - } - } - - QStringList queryResources(const QString &query) const override - { - return m_tagStore->searchTag(query); - } QStringList assignedTagsList(KoResourceSP resource) const { - return m_tagStore->assignedTagsList(resource); + return QStringList(); //m_tagStore->assignedTagsList(resource); } - /** - * Create one or more resources from a single file. By default one resource is created. - * Override to create more resources from the file. - * @param filename the filename of the resource or resource collection - */ - virtual QList> createResources( const QString & filename ) - { - QList> createdResources; - createdResources.append(createResource(filename)); - return createdResources; - } - - virtual QSharedPointer createResource( const QString & filename ) = 0; + virtual QSharedPointer createResource(const QString & filename) = 0; /// Return the currently stored resources in alphabetical order, overwrite for customized sorting virtual QList> sortedResources() { QMap> sortedNames; - Q_FOREACH (const QString &name, m_resourcesByName.keys()) { - sortedNames.insert(name.toLower(), m_resourcesByName[name]); - } +// Q_FOREACH (const QString &name, m_resourcesByName.keys()) { +// sortedNames.insert(name.toLower(), m_resourcesByName[name]); +// } return sortedNames.values(); } protected: void notifyResourceAdded(QSharedPointer resource) { Q_FOREACH (ObserverType* observer, m_observers) { observer->resourceAdded(resource); } } void notifyRemovingResource(QSharedPointer resource) { Q_FOREACH (ObserverType* observer, m_observers) { observer->removingResource(resource); } } void notifyResourceChanged(QSharedPointer resource) { Q_FOREACH (ObserverType* observer, m_observers) { observer->resourceChanged(resource); } } - /// Reads the xml file and returns the filenames as a list - QStringList readBlackListFile() - { - QStringList filenameList; - - QFile f(m_blackListFile); - if (!f.open(QIODevice::ReadOnly)) { - return filenameList; - } - - QDomDocument doc; - if (!doc.setContent(&f)) { - warnWidgets << "The file could not be parsed."; - return filenameList; - } - - QDomElement root = doc.documentElement(); - if (root.tagName() != "resourceFilesList") { - warnWidgets << "The file doesn't seem to be of interest."; - return filenameList; - } - - QDomElement file = root.firstChildElement("file"); - - while (!file.isNull()) { - QDomNode n = file.firstChild(); - QDomElement e = n.toElement(); - if (e.tagName() == "name") { - // If the krita bundle has landed in the blacklist, skip it. - if (type() == "kis_resourcebundles") { -// qDebug() << "Checking for not reading bundle" << e.text(); - if (e.text().endsWith("Krita_3_Default_Resources.bundle")) { - file = file.nextSiblingElement("file"); - } - } - filenameList.append(e.text().replace(QString("~"), QDir::homePath())); - } - file = file.nextSiblingElement("file"); - } -// if (type() == "kis_resourcebundles") { -// qDebug() << "Read bundle blacklist" << filenameList; -// } - return filenameList; - } - -protected: - - KoResourceSP byMd5(const QByteArray &/*md5*/) const override - { - return 0;//Policy::toResourcePointer(resourceByMD5(md5)); - } - - KoResourceSP byFileName(const QString &/*fileName*/) const override - { - return 0;//Policy::toResourcePointer(resourceByFilename(fileName)); - } - private: - void addResourceToMd5Registry(QSharedPointer resource) { - const QByteArray md5 = resource->md5(); - if (!md5.isEmpty()) { - m_resourcesByMd5.insert(md5, resource); - } - } - - void removeResourceFromMd5Registry(QSharedPointer resource) { - const QByteArray md5 = resource->md5(); - if (!md5.isEmpty()) { - m_resourcesByMd5.remove(md5); - } - } - -private: - - QHash> m_resourcesByName; - QHash> m_resourcesByFilename; - QHash> m_resourcesByMd5; - QList> m_resourceBlackList; - QList> m_resources; ///< list of resources in order of addition QList m_observers; - QString m_blackListFile; - KoResourceTagStore* m_tagStore; }; template class KoResourceServerSimpleConstruction : public KoResourceServer { public: KoResourceServerSimpleConstruction(const QString& type) : KoResourceServer(type) { } QSharedPointer createResource( const QString & filename ) override { return QSharedPointer(new T(filename)); } }; #endif // KORESOURCESERVER_H diff --git a/libs/widgets/KoResourceTagStore.cpp b/libs/widgets/KoResourceTagStore.cpp deleted file mode 100644 index 9d8646ab3a..0000000000 --- a/libs/widgets/KoResourceTagStore.cpp +++ /dev/null @@ -1,417 +0,0 @@ -/* This file is part of the KDE project - - Copyright (c) 2011 Sven Langkamp - Copyright (C) 2011 Srikanth Tiyyagura - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include "KoResourceTagStore.h" - -#include -#include -#include -#include -#include -#include -#include - -#include - -#define BLACKLISTED "blacklisted" ///< xml tag for blacklisted tags - -static const QStringList krita3PresetSystemTags = {"Ink", "Block", "Wet", "FX", "Erasers", "Circle", "Smudge", "Mix", "PixelArt", "ink", "sketch", "demo", "paint"}; - -class Q_DECL_HIDDEN KoResourceTagStore::Private -{ -public: - QMultiHash md5ToTag; - QMultiHash identifierToTag; - - QHash tagList; - - QStringList blacklistedTags; - - KoResourceServerBase *resourceServer; -}; - -KoResourceTagStore::KoResourceTagStore(KoResourceServerBase *resourceServer) - : d(new Private) -{ - d->resourceServer = resourceServer; -} - -KoResourceTagStore::~KoResourceTagStore() -{ - serializeTags(); - delete d; -} - -QStringList KoResourceTagStore::assignedTagsList(const KoResourceSP resource) const -{ - if (!resource) return QStringList(); - - QStringList tags = d->md5ToTag.values(resource->md5()); - tags += d->identifierToTag.values(resource->filename()); - tags.removeDuplicates(); - return tags; -} - -void KoResourceTagStore::removeResource(const KoResourceSP resource) -{ - QStringList tags = assignedTagsList(resource); - - d->md5ToTag.remove(resource->md5()); - d->identifierToTag.remove(resource->filename()); - - Q_FOREACH (const QString &tag, tags) { - if (d->tagList.contains(tag)) { - if (d->tagList[tag] > 0) { - d->tagList[tag]--; - } - } - } -} - -QStringList KoResourceTagStore::tagNamesList() const -{ - QStringList tagList = d->tagList.uniqueKeys(); - Q_FOREACH(const QString &tag, d->blacklistedTags) { - tagList.removeAll(tag); - } - return tagList; -} - -void KoResourceTagStore::addTag(KoResourceSP resource, const QString& tag) -{ -// if (d->resourceServer->type() == ResourceType::PaintOpPresets && resource) { -// qDebug() << "\t\t\taddTag" << tag << resource->filename() << d->tagList[tag] << d->md5ToTag.value(resource->md5()) << d->identifierToTag.values(resource->filename()); -// } - - if (d->blacklistedTags.contains(tag)) { - d->blacklistedTags.removeAll(tag); - } - - if (!d->tagList.contains(tag)) { - d->tagList.insert(tag, 0); - } - - if (resource) { - bool added = false; - - if (!d->md5ToTag.contains(resource->md5(), tag)) { - added = true; - d->md5ToTag.insert(resource->md5(), tag); - } - - if (!d->identifierToTag.contains(resource->filename())) { - added = true; - d->identifierToTag.insert(resource->filename(), tag); - } - - if (added) { - d->tagList[tag]++; - } - } -// if (d->resourceServer->type() == ResourceType::PaintOpPresets && resource) { -// qDebug() << "\t\t\t\tafter addTag" << tag << resource->filename() << d->tagList[tag] << d->md5ToTag.value(resource->md5()) << d->identifierToTag.values(resource->filename()); -// } - -} - -void KoResourceTagStore::delTag(KoResourceSP resource, const QString& tag) -{ - int res = d->md5ToTag.remove(resource->md5(), tag); - res += d->identifierToTag.remove(resource->filename(), tag); - - if (res > 0) { // decrease the usecount for this tag - if (d->tagList.contains(tag)) { - if (d->tagList[tag] > 0) { - d->tagList[tag]--; - } - } - } -} - -void KoResourceTagStore::delTag(const QString& tag) -{ - Q_FOREACH (const QByteArray &res, d->md5ToTag.keys(tag)) { - d->md5ToTag.remove(res, tag); - } - Q_FOREACH (const QString &identifier, d->identifierToTag.keys(tag)) { - d->identifierToTag.remove(identifier, tag); - } - - Q_ASSERT(!d->md5ToTag.values().contains(tag)); - Q_ASSERT(!d->identifierToTag.values().contains(tag)); - d->tagList.remove(tag); - d->blacklistedTags << tag; - serializeTags(); -} - -QStringList KoResourceTagStore::searchTag(const QString& query) const -{ - QStringList tagsList = query.split(QRegExp("[,]\\s*"), QString::SkipEmptyParts); - if (tagsList.isEmpty()) { - return QStringList(); - } - - QSet resources; - - Q_FOREACH (QString tag, tagsList) { - Q_FOREACH (const QByteArray &md5, d->md5ToTag.keys(tag)) { - KoResourceSP res = d->resourceServer->byMd5(md5); - if (res) - resources << res; - } - Q_FOREACH (const QString &identifier, d->identifierToTag.keys(tag)) { - KoResourceSP res = d->resourceServer->byFileName(identifier); - if (res) - resources << res; - } - } - - QStringList filenames; - Q_FOREACH (const KoResourceSP res, resources) { - if (res) { - filenames << res->shortFilename(); - } - } - return filenames; -} - -void KoResourceTagStore::loadTags() -{ - QStringList tagFiles = KoResourcePaths::findDirs("tags"); - Q_FOREACH (const QString &tagFile, tagFiles) { - QString fileName = tagFile + d->resourceServer->type() + "_tags.xml"; - if (QFileInfo(fileName).exists()) { - readXMLFile(fileName); - } - } -} - -void KoResourceTagStore::clearOldSystemTags() -{ - if (d->resourceServer->type() == ResourceType::PaintOpPresets) { -// qDebug() << "clearOldSystemTags" << d->tagList; - Q_FOREACH(const QString &systemTag, krita3PresetSystemTags) { -// qDebug() << "\t" << systemTag << d->tagList[systemTag]; - if (d->tagList[systemTag] == 0) { - d->tagList.remove(systemTag); - } - } - } -} - -void KoResourceTagStore::writeXMLFile(const QString &tagstore) -{ - QFile f(tagstore); - if (!f.open(QIODevice::WriteOnly | QIODevice::Text)) { - warnWidgets << "Cannot write meta information to '" << tagstore << "'."; - return; - } - QDomDocument doc; - QDomElement root; - - QDomDocument docTemp("tags"); - doc = docTemp; - doc.appendChild(doc.createProcessingInstruction("xml", "version=\"1.0\" encoding=\"UTF-8\"")); - root = doc.createElement("tags"); - doc.appendChild(root); - - QSet taggedResources; - Q_FOREACH (const QByteArray &md5, d->md5ToTag.keys()) { - KoResourceSP res = d->resourceServer->byMd5(md5); - if (res) { - taggedResources << res; - } - } - - Q_FOREACH (const QString &identifier, d->identifierToTag.keys()) { - KoResourceSP res = d->resourceServer->byFileName(identifier); - if (res) { - taggedResources << res; - } - } - - Q_FOREACH (const KoResourceSP res, taggedResources) { - - QDomElement resourceEl = doc.createElement("resource"); - resourceEl.setAttribute("identifier", res->filename().replace(QDir::homePath(), QString("~"))); - resourceEl.setAttribute("md5", QString(res->md5().toBase64())); - - Q_FOREACH (const QString &tag, assignedTagsList(res)) { - QDomElement tagEl = doc.createElement("tag"); - tagEl.setAttribute(BLACKLISTED, "false"); - tagEl.appendChild(doc.createTextNode(tag)); - resourceEl.appendChild(tagEl); - } - root.appendChild(resourceEl); - - } - - // Now write empty tags - Q_FOREACH (const QString &tag, d->tagList.uniqueKeys()) { - if (d->tagList[tag] == 0) { - QDomElement resourceEl = doc.createElement("resource"); - resourceEl.setAttribute("identifier", "dummy"); - QDomElement tagEl = doc.createElement("tag"); - tagEl.setAttribute(BLACKLISTED, d->blacklistedTags.contains(tag) ? "true" : "false"); - tagEl.appendChild(doc.createTextNode(tag)); - resourceEl.appendChild(tagEl); - root.appendChild(resourceEl); - } - } - - // Now write blacklisted tags. - Q_FOREACH (const QString &tag, d->blacklistedTags) { - if (d->tagList[tag] == 0) { - QDomElement resourceEl = doc.createElement("resource"); - resourceEl.setAttribute("identifier", "dummy"); - QDomElement tagEl = doc.createElement("tag"); - tagEl.setAttribute(BLACKLISTED, "true"); - tagEl.appendChild(doc.createTextNode(tag)); - resourceEl.appendChild(tagEl); - root.appendChild(resourceEl); - } - } - - QTextStream metastream(&f); - metastream.setCodec("UTF-8"); - metastream << doc.toString(); - - f.close(); - -} - -void KoResourceTagStore::readXMLFile(const QString &tagstore) -{ - QString inputFile; - if (QFile::exists(tagstore)) { - inputFile = tagstore; - } - else { - return; - } - -// qDebug() << "\treadXMLFile()." << tagstore << d->resourceServer->type() << "Server has" << d->resourceServer->resourceCount() << "resources"; - if (d->resourceServer->type() == ResourceType::PaintOpPresets) { -// Q_FOREACH(const QString &line, kisBacktrace().split("\n")) { -// qDebug() << line; -// } - } - - QFile f(inputFile); - if (!f.open(QIODevice::ReadOnly)) { - qWarning() << "Could not open tag file" << tagstore; - return; - } - - QDomDocument doc; - if (!doc.setContent(&f)) { - warnWidgets << "The file could not be parsed."; - return; - } - - QDomElement root = doc.documentElement(); - if (root.tagName() != "tags") { - warnWidgets << "The file doesn't seem to be of interest."; - return; - } - - QDomNodeList resourceNodesList = root.childNodes(); - - for (int i = 0; i < resourceNodesList.count(); i++) { - - QByteArray resourceMD5; - QString identifier; - - QDomElement element = resourceNodesList.at(i).toElement(); - if (element.tagName() == "resource") { - - KoResourceSP resByMd5 = 0; - KoResourceSP resByFileName = 0; - - if (element.hasAttribute("md5")) { - resourceMD5 = QByteArray::fromBase64(element.attribute("md5").toLatin1()); - resByMd5 = d->resourceServer->byMd5(resourceMD5); - } - - if (element.hasAttribute("identifier")) { - identifier = element.attribute("identifier"); - QFileInfo fi(identifier); - resByFileName = d->resourceServer->byFileName(fi.fileName()); - } - -// qDebug() << "\t\tmd5" << QString::fromLatin1(resourceMD5.toHex()) << "resByMd5" << resByMd5 << "identifier" << identifier << "resByFileName" << resByFileName; - - if (identifier == "dummy") { - QDomNodeList tagNodesList = resourceNodesList.at(i).childNodes(); - for (int j = 0; j < tagNodesList.count() ; j++) { - QDomElement tagEl = tagNodesList.at(j).toElement(); - bool blacklisted = (tagEl.attribute(BLACKLISTED, "false") == "true"); - if (blacklisted || d->blacklistedTags.contains(tagEl.text())) { - if (!d->blacklistedTags.contains(tagEl.text())) { - d->blacklistedTags << tagEl.text(); - } - } - else { - addTag(0, tagEl.text()); - } - } - } - else { - KoResourceSP res = 0; - - if (resByMd5 && resByFileName && (resByMd5 != resByFileName)) { - warnWidgets << "MD5sum and filename point to different resources -- was the resource renamed? We go with md5"; - res = resByMd5; - } - else if (!resByMd5 && resByFileName) { - // We didn't find the resource by md5, but did find it by filename, so take that one - res = resByFileName; - } - else { - res = resByMd5; - } - - QDomNodeList tagNodesList = resourceNodesList.at(i).childNodes(); - for (int j = 0; j < tagNodesList.count() ; j++) { - QDomElement tagEl = tagNodesList.at(j).toElement(); - bool blacklisted = (tagEl.attribute(BLACKLISTED, "false") == "true"); - if (blacklisted || d->blacklistedTags.contains(tagEl.text())) { - if (!d->blacklistedTags.contains(tagEl.text())) { - d->blacklistedTags << tagEl.text(); - } - } - else { - if (res) { - addTag(res, tagEl.text()); - } - d->md5ToTag.insert(resourceMD5, tagEl.text()); - d->identifierToTag.insert(identifier, tagEl.text()); - } - } - } - } - } -// qDebug() << "Done reading XML file from" << tagstore << d->tagList; -} - -void KoResourceTagStore::serializeTags() -{ - writeXMLFile(KoResourcePaths::saveLocation("tags") + d->resourceServer->type() + "_tags.xml"); -} diff --git a/libs/widgets/KoResourceTagStore.h b/libs/widgets/KoResourceTagStore.h deleted file mode 100644 index 4263d64982..0000000000 --- a/libs/widgets/KoResourceTagStore.h +++ /dev/null @@ -1,85 +0,0 @@ -/* This file is part of the KDE project - - Copyright (c) 2011 Sven Langkamp - Copyright (c) 2011 Srikanth Tiyyagura - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef KORESOURCETAGSTORE_H -#define KORESOURCETAGSTORE_H - - -#include -#include "kritawidgets_export.h" - -#include - -class KoResourceServerBase; -class QStringList; -class QString; - -/** - * KoResourceTagging allows to add and delete tags to resources and also search resources using tags - */ -class KRITAWIDGETS_EXPORT KoResourceTagStore -{ -public: - - /** - * Constructs a KoResourceTagging object - * - */ - explicit KoResourceTagStore(KoResourceServerBase *resourceServer); - ~KoResourceTagStore(); - - QStringList assignedTagsList(const KoResourceSP resource) const; - - /// remote the given resource from the tagstore - void removeResource(const KoResourceSP resource); - - /// Add the given tag to the tag store. The resource can be empty, in which case - /// the tag is added but unused - void addTag(KoResourceSP resource, const QString& tag); - - /// Remove the given tag for the given resource. It will be blacklisted if there are no users left. - void delTag(KoResourceSP resource, const QString& tag); - - /// Remove the tag altogether. It will be blacklisted, too. - void delTag(const QString& tag); - - /// @return a list of all the tags in this store - QStringList tagNamesList() const; - - /// Return a list of filenames for the given tag - QStringList searchTag(const QString& query) const; - - void loadTags(); - void clearOldSystemTags(); - - void serializeTags(); - -private: - friend class KoResourceTaggingTest; - - void readXMLFile(const QString &tagstore); - void writeXMLFile(const QString &tagstore); - - class Private; - Private * const d; -}; - - -#endif // KORESOURCETAGSTORE_H diff --git a/libs/widgets/tests/CMakeLists.txt b/libs/widgets/tests/CMakeLists.txt index efa12a79c7..e7f0ccedc6 100644 --- a/libs/widgets/tests/CMakeLists.txt +++ b/libs/widgets/tests/CMakeLists.txt @@ -1,18 +1,17 @@ set( EXECUTABLE_OUTPUT_PATH ${CMAKE_CURRENT_BINARY_DIR} ) add_definitions(-DFILES_DATA_DIR="${CMAKE_CURRENT_SOURCE_DIR}/data/") add_definitions(-DFILES_OUTPUT_DIR="${CMAKE_CURRENT_BINARY_DIR}") #add_subdirectory(filedialogtester) include(ECMAddTests) include(KritaAddBrokenUnitTest) ecm_add_tests( zoomhandler_test.cpp zoomcontroller_test.cpp - KoResourceTaggingTest.cpp kis_parse_spin_boxes_test.cpp KoAnchorSelectionWidgetTest.cpp NAME_PREFIX "libs-widgets-" LINK_LIBRARIES kritawidgets Qt5::Test) diff --git a/libs/widgets/tests/KoResourceTaggingTest.cpp b/libs/widgets/tests/KoResourceTaggingTest.cpp deleted file mode 100644 index 48451bce7f..0000000000 --- a/libs/widgets/tests/KoResourceTaggingTest.cpp +++ /dev/null @@ -1,171 +0,0 @@ -/* - * Copyright (c) 2011 Srikanth Tiyyagura - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -#include "KoResourceTaggingTest.h" - -#include -#include - -#include -#include - -#include -#include -#include "KoResourceServerProvider.h" - -#include "sdk/tests/kistest.h" - -void KoResourceTaggingTest::testInitialization() -{ - // All Krita's resource types - KoResourcePaths::addResourceType("gmic_definitions", "data", "/gmic/"); - KoResourcePaths::addResourceType("icc_profiles", "data", "/color/icc"); - KoResourcePaths::addResourceType("icc_profiles", "data", "/profiles/"); - KoResourcePaths::addResourceType("kis_actions", "data", "/actions"); - KoResourcePaths::addResourceType(ResourceType::Brushes, "data", "/brushes/"); - KoResourcePaths::addResourceType("kis_defaultpresets", "data", "/defaultpresets/"); - KoResourcePaths::addResourceType("kis_images", "data", "/images/"); - KoResourcePaths::addResourceType(ResourceType::PaintOpPresets, "data", "/paintoppresets/"); - KoResourcePaths::addResourceType("kis_pics", "data", "/pics/"); - KoResourcePaths::addResourceType("kis_resourcebundles", "data", "/bundles/"); - KoResourcePaths::addResourceType("kis_shortcuts", "data", "/shortcuts/"); - KoResourcePaths::addResourceType("kis_taskset", "data", "/taskset/"); - KoResourcePaths::addResourceType("kis_taskset", "data", "/taskset/"); - KoResourcePaths::addResourceType(ResourceType::WindowLayouts, "data", "/windowlayouts/"); - KoResourcePaths::addResourceType(ResourceType::Workspaces, "data", "/workspaces/"); - KoResourcePaths::addResourceType(ResourceType::FilterEffects, "data", "/effects/"); - KoResourcePaths::addResourceType(ResourceType::Gradients, "data", "/gradients/"); - KoResourcePaths::addResourceType(ResourceType::Palettes, "data", "/palettes/"); - KoResourcePaths::addResourceType(ResourceType::Patterns, "data", "/patterns/"); - KoResourcePaths::addResourceType("metadata_schema", "data", "/metadata/schemas/"); - KoResourcePaths::addResourceType("psd_layer_style_collections", "data", "/asl"); - KoResourcePaths::addResourceType("tags", "data", "/tags/"); - - KoResourceTagStore tagStore(KoResourceServerProvider::instance()->patternServer()); - QVERIFY(tagStore.tagNamesList().isEmpty()); - QVERIFY(tagStore.assignedTagsList(0).isEmpty()); - QVERIFY(tagStore.searchTag("bla").isEmpty()); -} - -void KoResourceTaggingTest::testTagging() -{ - KoResourceServer* patServer = KoResourceServerProvider::instance()->patternServer(); - KoResourceTagStore tagStore(patServer); - KoResourceSP res = patServer->resources().first(); - QVERIFY(res); - QVERIFY(patServer->resourceByFilename(res->shortFilename()) == res); - - tagStore.addTag(res, "test1"); - QVERIFY(tagStore.tagNamesList().size() == 1); - QStringList resources = tagStore.searchTag("test1"); - QVERIFY(resources.size() == 1); - KoResourceSP res2 = patServer->resourceByFilename(resources.first()); - QVERIFY(res == res2); - - tagStore.addTag(res, "test2"); - QVERIFY(tagStore.tagNamesList().size() == 2); - resources = tagStore.searchTag("test1"); - QVERIFY(resources.size() == 1); - res2 = patServer->resourceByFilename(resources.first()); - QVERIFY(res == res2); - - tagStore.addTag(res, "test2"); - QVERIFY(tagStore.tagNamesList().size() == 2); - - resources = tagStore.searchTag("test2"); - QVERIFY(resources.size() == 1); - res2 = patServer->resourceByFilename(resources.first()); - QVERIFY(res == res2); - - resources = tagStore.searchTag("test1,test2"); - QVERIFY(resources.size() == 1); - res2 = patServer->resourceByFilename(resources.first()); - QVERIFY(res == res2); - - tagStore.delTag(res, "test1"); - QVERIFY(tagStore.tagNamesList().size() == 2); - resources = tagStore.searchTag("test1"); - QVERIFY(resources.size() == 0); - - resources = tagStore.searchTag("test2"); - QVERIFY(resources.size() == 1); - res2 = patServer->resourceByFilename(resources.first()); - QVERIFY(res == res2); - - tagStore.delTag("test1"); - QVERIFY(tagStore.tagNamesList().size() == 1); - -} - -void KoResourceTaggingTest::testReadWriteXML() -{ - KoResourceServer* patServer = KoResourceServerProvider::instance()->patternServer(); - KoResourceTagStore tagStore(patServer); - - QList patterns = patServer->resources(); - Q_ASSERT(patterns.size() > 5); - tagStore.addTag(patterns[0], "test0"); - tagStore.addTag(patterns[1], "test1"); - tagStore.addTag(patterns[2], "test2"); - tagStore.addTag(patterns[2], "test2"); - tagStore.addTag(patterns[2], "test1"); - tagStore.addTag(patterns[3], "test3"); - tagStore.addTag(patterns[4], "test4"); - tagStore.addTag(patterns[5], "test5"); - tagStore.addTag(patterns[5], "test5.1"); - tagStore.addTag(patterns[5], "test5.2"); - tagStore.addTag(0, "dummytest"); - - QVERIFY(tagStore.tagNamesList().size() == 9); - - tagStore.writeXMLFile(QString(FILES_OUTPUT_DIR) + "/" + "kis_pattern_tags.xml"); - - KoResourceTagStore tagStore2(patServer); - tagStore2.readXMLFile(QString(FILES_OUTPUT_DIR) + "/" + "kis_pattern_tags.xml"); - - QVERIFY(tagStore2.tagNamesList().size() == 9); - QStringList resources = tagStore2.searchTag("test0"); - QVERIFY(resources.size() == 1); - QVERIFY(patServer->resourceByFilename(resources[0]) == patterns[0]); - - resources = tagStore2.searchTag("test1"); - QVERIFY(resources.size() == 2); - - resources = tagStore2.searchTag("test2"); - QVERIFY(resources.size() == 1); - - resources = tagStore2.searchTag("test3"); - QVERIFY(resources.size() == 1); - - resources = tagStore2.searchTag("test4"); - QVERIFY(resources.size() == 1); - - resources = tagStore2.searchTag("test5"); - QVERIFY(resources.size() == 1); - - resources = tagStore2.searchTag("test5.1"); - QVERIFY(resources.size() == 1); - - resources = tagStore2.searchTag("test5.2"); - QVERIFY(resources.size() == 1); - - resources = tagStore2.searchTag("dummytest"); - QVERIFY(resources.size() == 0); -} - -KISTEST_MAIN(KoResourceTaggingTest) diff --git a/libs/widgets/tests/KoResourceTaggingTest.h b/libs/widgets/tests/KoResourceTaggingTest.h deleted file mode 100644 index 748ee63e5d..0000000000 --- a/libs/widgets/tests/KoResourceTaggingTest.h +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright (c) 2011 Srikanth Tiyyagura - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -#ifndef KORESOURCETAGGING_TEST_H -#define KORESOURCETAGGING_TEST_H - -#include -#include -#include "KoResourceTagStore.h" - -class KoResourceTaggingTest : public QObject -{ - Q_OBJECT - -private Q_SLOTS: - - // tests - void testInitialization(); - void testTagging(); - void testReadWriteXML(); -}; - -#endif diff --git a/plugins/tools/karbonplugins/tools/filterEffectTool/FilterResourceServerProvider.cpp b/plugins/tools/karbonplugins/tools/filterEffectTool/FilterResourceServerProvider.cpp index f1a093caca..12eae619ba 100644 --- a/plugins/tools/karbonplugins/tools/filterEffectTool/FilterResourceServerProvider.cpp +++ b/plugins/tools/karbonplugins/tools/filterEffectTool/FilterResourceServerProvider.cpp @@ -1,60 +1,59 @@ /* This file is part of the KDE project Copyright (c) 1999 Matthias Elter Copyright (c) 2003 Patrick Julien Copyright (c) 2005 Sven Langkamp This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "FilterResourceServerProvider.h" #include "FilterEffectResource.h" #include #include #include #include #include #include FilterResourceServerProvider *FilterResourceServerProvider::m_singleton = 0; FilterResourceServerProvider::FilterResourceServerProvider() { m_filterEffectServer = new KoResourceServerSimpleConstruction(ResourceType::FilterEffects); if (!QFileInfo(m_filterEffectServer->saveLocation()).exists()) { QDir().mkpath(m_filterEffectServer->saveLocation()); } - m_filterEffectServer->loadTags(); } FilterResourceServerProvider::~FilterResourceServerProvider() { delete m_filterEffectServer; } FilterResourceServerProvider *FilterResourceServerProvider::instance() { if (FilterResourceServerProvider::m_singleton == 0) { FilterResourceServerProvider::m_singleton = new FilterResourceServerProvider(); } return FilterResourceServerProvider::m_singleton; } KoResourceServer *FilterResourceServerProvider::filterEffectServer() { return m_filterEffectServer; }