From 42a7a211de6f271b0e8b1679d648ad684c6eae30 Mon Sep 17 00:00:00 2001 From: Boudewijn Rempt Date: Fri, 22 Jan 2016 14:31:01 +0100 Subject: [PATCH] BUG:352149 Sanitize the order of resource and tag loading This now loads the bundles at the end of loading all resources. --- .../extensions/resourcemanager/CMakeLists.txt | 4 - .../resourcemanager/dlg_bundle_manager.cpp | 25 +- .../resourcemanager/dlg_bundle_manager.h | 10 +- .../resourcemanager/dlg_create_bundle.cpp | 4 +- .../resourcemanager/dlg_create_bundle.h | 6 +- .../resourcemanager/resourcemanager.cpp | 62 +- .../resourcemanager/resourcemanager.h | 23 +- krita/ui/CMakeLists.txt | 2 + krita/ui/KisApplication.cpp | 1 + krita/ui/KisResourceBundle.cpp | 989 ++++++++++++++++++ krita/ui/KisResourceBundle.h | 159 +++ krita/ui/KisResourceBundleManifest.cpp | 233 +++++ krita/ui/KisResourceBundleManifest.h | 100 ++ krita/ui/kis_resource_server_provider.cpp | 21 + krita/ui/kis_resource_server_provider.h | 9 +- krita/ui/resourcemanager/CMakeLists.txt | 19 + krita/ui/resourcemanager/NOTES | 1 + .../ui/resourcemanager/dlg_bundle_manager.cpp | 356 +++++++ krita/ui/resourcemanager/dlg_bundle_manager.h | 70 ++ .../ui/resourcemanager/dlg_create_bundle.cpp | 387 +++++++ krita/ui/resourcemanager/dlg_create_bundle.h | 80 ++ .../resourcemanager/kritaresourcemanager.json | 9 + .../resourcemanager/resourcebundle.cpp | 0 .../resourcemanager/resourcebundle.h | 0 .../resourcebundle_manifest.cpp | 0 .../resourcemanager/resourcebundle_manifest.h | 0 krita/ui/resourcemanager/resourcemanager.cpp | 332 ++++++ krita/ui/resourcemanager/resourcemanager.h | 64 ++ krita/ui/resourcemanager/resourcemanager.rc | 13 + .../resourcemanager/tests/CMakeLists.txt | 0 .../tests/ResourceBundleTest.cpp | 0 .../tests/ResourceBundleTest.h | 0 .../resourcemanager/tests/data/thumb.png | Bin .../ui/resourcemanager/wdgdlgbundlemanager.ui | 539 ++++++++++ .../ui/resourcemanager/wdgdlgcreatebundle.ui | 466 +++++++++ krita/ui/tests/CMakeLists.txt | 7 + krita/ui/tests/ResourceBundleTest.cpp | 179 ++++ krita/ui/tests/ResourceBundleTest.h | 39 + krita/ui/tests/data/thumb.png | Bin 0 -> 2175 bytes libs/widgets/KoResourceServerProvider.cpp | 1 + 40 files changed, 4104 insertions(+), 106 deletions(-) create mode 100644 krita/ui/KisResourceBundle.cpp create mode 100644 krita/ui/KisResourceBundle.h create mode 100644 krita/ui/KisResourceBundleManifest.cpp create mode 100644 krita/ui/KisResourceBundleManifest.h create mode 100644 krita/ui/resourcemanager/CMakeLists.txt create mode 100644 krita/ui/resourcemanager/NOTES create mode 100644 krita/ui/resourcemanager/dlg_bundle_manager.cpp create mode 100644 krita/ui/resourcemanager/dlg_bundle_manager.h create mode 100644 krita/ui/resourcemanager/dlg_create_bundle.cpp create mode 100644 krita/ui/resourcemanager/dlg_create_bundle.h create mode 100644 krita/ui/resourcemanager/kritaresourcemanager.json rename krita/{plugins/extensions => ui}/resourcemanager/resourcebundle.cpp (100%) rename krita/{plugins/extensions => ui}/resourcemanager/resourcebundle.h (100%) rename krita/{plugins/extensions => ui}/resourcemanager/resourcebundle_manifest.cpp (100%) rename krita/{plugins/extensions => ui}/resourcemanager/resourcebundle_manifest.h (100%) create mode 100644 krita/ui/resourcemanager/resourcemanager.cpp create mode 100644 krita/ui/resourcemanager/resourcemanager.h create mode 100644 krita/ui/resourcemanager/resourcemanager.rc rename krita/{plugins/extensions => ui}/resourcemanager/tests/CMakeLists.txt (100%) rename krita/{plugins/extensions => ui}/resourcemanager/tests/ResourceBundleTest.cpp (100%) rename krita/{plugins/extensions => ui}/resourcemanager/tests/ResourceBundleTest.h (100%) rename krita/{plugins/extensions => ui}/resourcemanager/tests/data/thumb.png (100%) create mode 100644 krita/ui/resourcemanager/wdgdlgbundlemanager.ui create mode 100644 krita/ui/resourcemanager/wdgdlgcreatebundle.ui create mode 100644 krita/ui/tests/ResourceBundleTest.cpp create mode 100644 krita/ui/tests/ResourceBundleTest.h create mode 100644 krita/ui/tests/data/thumb.png diff --git a/krita/plugins/extensions/resourcemanager/CMakeLists.txt b/krita/plugins/extensions/resourcemanager/CMakeLists.txt index 70f259f14e..a2600b159a 100644 --- a/krita/plugins/extensions/resourcemanager/CMakeLists.txt +++ b/krita/plugins/extensions/resourcemanager/CMakeLists.txt @@ -1,9 +1,5 @@ -add_subdirectory( tests ) - set(kritaresourcemanager_SOURCES resourcemanager.cpp - resourcebundle_manifest.cpp - resourcebundle.cpp dlg_create_bundle.cpp dlg_bundle_manager.cpp ) diff --git a/krita/plugins/extensions/resourcemanager/dlg_bundle_manager.cpp b/krita/plugins/extensions/resourcemanager/dlg_bundle_manager.cpp index ae1891dee0..2089afaca8 100644 --- a/krita/plugins/extensions/resourcemanager/dlg_bundle_manager.cpp +++ b/krita/plugins/extensions/resourcemanager/dlg_bundle_manager.cpp @@ -31,6 +31,7 @@ #include #include "kis_action.h" +#include #define ICON_SIZE 48 @@ -82,14 +83,14 @@ DlgBundleManager::DlgBundleManager(KisActionManager* actionMgr, QWidget *parent) void DlgBundleManager::refreshListData() { - KoResourceServer *bundleServer = ResourceBundleServerProvider::instance()->resourceBundleServer(); + KoResourceServer *bundleServer = KisResourceServerProvider::instance()->resourceBundleServer(); m_ui->listInactive->clear(); m_ui->listActive->clear(); Q_FOREACH (const QString &f, bundleServer->blackListedFiles()) { - ResourceBundle *bundle = new ResourceBundle(f); + KisResourceBundle *bundle = new KisResourceBundle(f); bundle->load(); if (bundle->valid()) { bundle->setInstalled(false); @@ -98,7 +99,7 @@ void DlgBundleManager::refreshListData() } fillListWidget(m_blacklistedBundles.values(), m_ui->listInactive); - Q_FOREACH (ResourceBundle *bundle, bundleServer->resources()) { + Q_FOREACH (KisResourceBundle *bundle, bundleServer->resources()) { if (bundle->valid()) { m_activeBundles[bundle->filename()] = bundle; } @@ -108,19 +109,19 @@ void DlgBundleManager::refreshListData() void DlgBundleManager::accept() { - KoResourceServer *bundleServer = ResourceBundleServerProvider::instance()->resourceBundleServer(); + KoResourceServer *bundleServer = KisResourceServerProvider::instance()->resourceBundleServer(); for (int i = 0; i < m_ui->listActive->count(); ++i) { QListWidgetItem *item = m_ui->listActive->item(i); QByteArray ba = item->data(Qt::UserRole).toByteArray(); - ResourceBundle *bundle = bundleServer->resourceByMD5(ba); + KisResourceBundle *bundle = bundleServer->resourceByMD5(ba); QMessageBox bundleFeedback; bundleFeedback.setIcon(QMessageBox::Warning); QString feedback = "bundlefeedback"; if (!bundle) { // Get it from the blacklisted bundles - Q_FOREACH (ResourceBundle *b2, m_blacklistedBundles.values()) { + Q_FOREACH (KisResourceBundle *b2, m_blacklistedBundles.values()) { if (b2->md5() == ba) { bundle = b2; break; @@ -160,7 +161,7 @@ void DlgBundleManager::accept() for (int i = 0; i < m_ui->listInactive->count(); ++i) { QListWidgetItem *item = m_ui->listInactive->item(i); QByteArray ba = item->data(Qt::UserRole).toByteArray(); - ResourceBundle *bundle = bundleServer->resourceByMD5(ba); + KisResourceBundle *bundle = bundleServer->resourceByMD5(ba); if (bundle && bundle->isInstalled()) { bundle->uninstall(); @@ -207,12 +208,12 @@ void DlgBundleManager::itemSelected(QListWidgetItem *current, QListWidgetItem *) else { QByteArray ba = current->data(Qt::UserRole).toByteArray(); - KoResourceServer *bundleServer = ResourceBundleServerProvider::instance()->resourceBundleServer(); - ResourceBundle *bundle = bundleServer->resourceByMD5(ba); + KoResourceServer *bundleServer = KisResourceServerProvider::instance()->resourceBundleServer(); + KisResourceBundle *bundle = bundleServer->resourceByMD5(ba); if (!bundle) { // Get it from the blacklisted bundles - Q_FOREACH (ResourceBundle *b2, m_blacklistedBundles.values()) { + Q_FOREACH (KisResourceBundle *b2, m_blacklistedBundles.values()) { if (b2->md5() == ba) { bundle = b2; break; @@ -291,12 +292,12 @@ void DlgBundleManager::editBundle() } } -void DlgBundleManager::fillListWidget(QList bundles, QListWidget *w) +void DlgBundleManager::fillListWidget(QList bundles, QListWidget *w) { w->setIconSize(QSize(ICON_SIZE, ICON_SIZE)); w->setSelectionMode(QAbstractItemView::MultiSelection); - Q_FOREACH (ResourceBundle *bundle, bundles) { + Q_FOREACH (KisResourceBundle *bundle, bundles) { QPixmap pixmap(ICON_SIZE, ICON_SIZE); if (!bundle->image().isNull()) { QImage scaled = bundle->image().scaled(ICON_SIZE, ICON_SIZE, Qt::KeepAspectRatio, Qt::SmoothTransformation); diff --git a/krita/plugins/extensions/resourcemanager/dlg_bundle_manager.h b/krita/plugins/extensions/resourcemanager/dlg_bundle_manager.h index 83f422bc74..317fa9b1dc 100644 --- a/krita/plugins/extensions/resourcemanager/dlg_bundle_manager.h +++ b/krita/plugins/extensions/resourcemanager/dlg_bundle_manager.h @@ -24,7 +24,7 @@ #include #include "kis_action_manager.h" -class ResourceBundle; +class KisResourceBundle; class QListWidget; class QListWidgetItem; @@ -57,13 +57,13 @@ private: QWidget *m_page; Ui::WdgDlgBundleManager *m_ui; - void fillListWidget(QList bundles, QListWidget *w); + void fillListWidget(QList bundles, QListWidget *w); void refreshListData(); - QMap m_blacklistedBundles; - QMap m_activeBundles; + QMap m_blacklistedBundles; + QMap m_activeBundles; - ResourceBundle *m_currentBundle; + KisResourceBundle *m_currentBundle; KisActionManager* m_actionManager; }; diff --git a/krita/plugins/extensions/resourcemanager/dlg_create_bundle.cpp b/krita/plugins/extensions/resourcemanager/dlg_create_bundle.cpp index 21981e6845..4d355644d9 100644 --- a/krita/plugins/extensions/resourcemanager/dlg_create_bundle.cpp +++ b/krita/plugins/extensions/resourcemanager/dlg_create_bundle.cpp @@ -43,11 +43,11 @@ #include -#include "resourcebundle.h" +#include "KisResourceBundle.h" #define ICON_SIZE 48 -DlgCreateBundle::DlgCreateBundle(ResourceBundle *bundle, QWidget *parent) +DlgCreateBundle::DlgCreateBundle(KisResourceBundle *bundle, QWidget *parent) : KoDialog(parent) , m_ui(new Ui::WdgDlgCreateBundle) , m_bundle(bundle) diff --git a/krita/plugins/extensions/resourcemanager/dlg_create_bundle.h b/krita/plugins/extensions/resourcemanager/dlg_create_bundle.h index c4661df688..d55ed1bd5a 100644 --- a/krita/plugins/extensions/resourcemanager/dlg_create_bundle.h +++ b/krita/plugins/extensions/resourcemanager/dlg_create_bundle.h @@ -21,7 +21,7 @@ #include -class ResourceBundle; +class KisResourceBundle; namespace Ui { @@ -33,7 +33,7 @@ class DlgCreateBundle : public KoDialog Q_OBJECT public: - explicit DlgCreateBundle(ResourceBundle *bundle = 0, QWidget *parent = 0); + explicit DlgCreateBundle(KisResourceBundle *bundle = 0, QWidget *parent = 0); ~DlgCreateBundle(); QString bundleName() const; @@ -74,7 +74,7 @@ private: QString m_previewImage; - ResourceBundle *m_bundle; + KisResourceBundle *m_bundle; }; #endif // KOBUNDLECREATIONWIDGET_H diff --git a/krita/plugins/extensions/resourcemanager/resourcemanager.cpp b/krita/plugins/extensions/resourcemanager/resourcemanager.cpp index 70412d8d42..5a7dd46349 100644 --- a/krita/plugins/extensions/resourcemanager/resourcemanager.cpp +++ b/krita/plugins/extensions/resourcemanager/resourcemanager.cpp @@ -48,40 +48,11 @@ #include "dlg_bundle_manager.h" #include "dlg_create_bundle.h" -Q_GLOBAL_STATIC(ResourceBundleServerProvider, s_instance) - -ResourceBundleServerProvider::ResourceBundleServerProvider() -{ - // user-local - m_resourceBundleServer = new KoResourceServerSimpleConstruction("kis_resourcebundles", "*.bundle"); - if (!QFileInfo(m_resourceBundleServer->saveLocation()).exists()) { - QDir().mkpath(m_resourceBundleServer->saveLocation()); - } -} - - -ResourceBundleServerProvider *ResourceBundleServerProvider::instance() -{ - return s_instance; -} - -ResourceBundleServerProvider::~ResourceBundleServerProvider() -{ - delete m_resourceBundleServer; -} - -KoResourceServer *ResourceBundleServerProvider::resourceBundleServer() -{ - return m_resourceBundleServer; -} - - class ResourceManager::Private { public: Private() - : loader(0) { brushServer = KisBrushServer::instance()->brushServer(false); paintopServer = KisResourceServerProvider::instance()->paintOpPresetServer(false); @@ -98,8 +69,6 @@ public: KoResourceServer* paletteServer; KoResourceServer* workspaceServer; - QThread *loader; - }; K_PLUGIN_FACTORY_WITH_JSON(ResourceManagerFactory, "kritaresourcemanager.json", registerPlugin();) @@ -184,7 +153,7 @@ void ResourceManager::slotImport() } else if (resourceType == "bundles") { Q_FOREACH (const QString &res, resources) { - ResourceBundle *bundle = ResourceBundleServerProvider::instance()->resourceBundleServer()->createResource(res); + KisResourceBundle *bundle = KisResourceServerProvider::instance()->resourceBundleServer()->createResource(res); bundle->load(); if (bundle->valid()) { if (!bundle->install()) { @@ -196,17 +165,17 @@ void ResourceManager::slotImport() } QFileInfo fi(res); - QString newFilename = ResourceBundleServerProvider::instance()->resourceBundleServer()->saveLocation() + fi.baseName() + bundle->defaultFileExtension(); + QString newFilename = KisResourceServerProvider::instance()->resourceBundleServer()->saveLocation() + fi.baseName() + bundle->defaultFileExtension(); QFileInfo fileInfo(newFilename); int i = 1; while (fileInfo.exists()) { - fileInfo.setFile(ResourceBundleServerProvider::instance()->resourceBundleServer()->saveLocation() + fi.baseName() + QString("%1").arg(i) + bundle->defaultFileExtension()); + fileInfo.setFile(KisResourceServerProvider::instance()->resourceBundleServer()->saveLocation() + fi.baseName() + QString("%1").arg(i) + bundle->defaultFileExtension()); i++; } bundle->setFilename(fileInfo.filePath()); QFile::copy(res, newFilename); - ResourceBundleServerProvider::instance()->resourceBundleServer()->addResource(bundle, false); + KisResourceServerProvider::instance()->resourceBundleServer()->addResource(bundle, false); } } else if (resourceType == "patterns") { @@ -238,7 +207,7 @@ void ResourceManager::slotCreateBundle() } QString bundlePath = dlgCreateBundle.saveLocation() + "/" + dlgCreateBundle.bundleName() + ".bundle"; - ResourceBundle* newBundle = new ResourceBundle(bundlePath); + KisResourceBundle* newBundle = new KisResourceBundle(bundlePath); newBundle->addMeta("name", dlgCreateBundle.bundleName()); newBundle->addMeta("author", dlgCreateBundle.authorName()); @@ -307,26 +276,5 @@ void ResourceManager::slotManageBundles() } -void ResourceManager::loadBundles() -{ - d->loader = new KoResourceLoaderThread(ResourceBundleServerProvider::instance()->resourceBundleServer()); - connect(d->loader, SIGNAL(finished()), this, SLOT(bundlesLoaded())); - d->loader->start(); -} - -void ResourceManager::bundlesLoaded() -{ - delete d->loader; - d->loader = 0; - - Q_FOREACH (ResourceBundle *bundle, ResourceBundleServerProvider::instance()->resourceBundleServer()->resources()) { - if (!bundle->install()) { - warnKrita << "Could not install resources for bundle" << bundle->name(); - } - } - -} - - #include "resourcemanager.moc" diff --git a/krita/plugins/extensions/resourcemanager/resourcemanager.h b/krita/plugins/extensions/resourcemanager/resourcemanager.h index 109d186362..7178ec8092 100644 --- a/krita/plugins/extensions/resourcemanager/resourcemanager.h +++ b/krita/plugins/extensions/resourcemanager/resourcemanager.h @@ -22,26 +22,10 @@ #include #include - +# #include -#include "resourcebundle.h" - -class ResourceBundleServerProvider { -public: - static ResourceBundleServerProvider *instance(); - ResourceBundleServerProvider(); - ~ResourceBundleServerProvider(); - - KoResourceServer *resourceBundleServer(); - -private: - - KoResourceServer *m_resourceBundleServer; - - Q_DISABLE_COPY(ResourceBundleServerProvider) - -}; +#include "KisResourceBundle.h" class ResourceManager : public KisViewPlugin { @@ -54,8 +38,7 @@ private Q_SLOTS: void slotImport(); void slotCreateBundle(); void slotManageBundles(); - void loadBundles(); - void bundlesLoaded(); + private: class Private; Private *const d; diff --git a/krita/ui/CMakeLists.txt b/krita/ui/CMakeLists.txt index 4943778403..dc5bb2e196 100644 --- a/krita/ui/CMakeLists.txt +++ b/krita/ui/CMakeLists.txt @@ -331,6 +331,8 @@ set(kritaui_LIB_SRCS qtsingleapplication/qtlocalpeer.cpp qtsingleapplication/qtsingleapplication.cpp + KisResourceBundle.cpp + KisResourceBundleManifest.cpp kis_md5_generator.cpp KisApplicationArguments.cpp diff --git a/krita/ui/KisApplication.cpp b/krita/ui/KisApplication.cpp index e88113c30f..c00bc15a61 100644 --- a/krita/ui/KisApplication.cpp +++ b/krita/ui/KisApplication.cpp @@ -290,6 +290,7 @@ void loadResources() KisBrushServer::instance()->brushServer(true); // load paintop presets KisResourceServerProvider::instance()->paintOpPresetServer(true); + KisResourceServerProvider::instance()->resourceBundleServer(); } void loadPlugins() diff --git a/krita/ui/KisResourceBundle.cpp b/krita/ui/KisResourceBundle.cpp new file mode 100644 index 0000000000..4c62ee7032 --- /dev/null +++ b/krita/ui/KisResourceBundle.cpp @@ -0,0 +1,989 @@ +/* + * 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 (" + CalligraVersionWrapper::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(KoResource *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", "krita/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 == "ko_gradients") { + KoResourceServer* gradientServer = KoResourceServerProvider::instance()->gradientServer(); + Q_FOREACH (const KisResourceBundleManifest::ResourceReference &ref, m_manifest.files(resType)) { + KoResource *res = gradientServer->resourceByMD5(ref.md5sum); + if (!res) res = gradientServer->resourceByFilename(QFileInfo(ref.resourcePath).fileName()); + if (!saveResourceToStore(res, store.data(), "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 == "ko_patterns") { + KoResourceServer* patternServer = KoResourceServerProvider::instance()->patternServer(); + Q_FOREACH (const KisResourceBundleManifest::ResourceReference &ref, m_manifest.files(resType)) { + KoResource *res = patternServer->resourceByMD5(ref.md5sum); + if (!res) res = patternServer->resourceByFilename(QFileInfo(ref.resourcePath).fileName()); + if (!saveResourceToStore(res, store.data(), "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 == "kis_brushes") { + KisBrushResourceServer* 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()); + KoResource *res = brush.data(); + if (!saveResourceToStore(res, store.data(), "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 == "ko_palettes") { + KoResourceServer* paletteServer = KoResourceServerProvider::instance()->paletteServer(); + Q_FOREACH (const KisResourceBundleManifest::ResourceReference &ref, m_manifest.files(resType)) { + KoResource *res = paletteServer->resourceByMD5(ref.md5sum); + if (!res) res = paletteServer->resourceByFilename(QFileInfo(ref.resourcePath).fileName()); + if (!saveResourceToStore(res, store.data(), "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 == "kis_workspaces") { + KoResourceServer< KisWorkspaceResource >* workspaceServer = KisResourceServerProvider::instance()->workspaceServer(); + Q_FOREACH (const KisResourceBundleManifest::ResourceReference &ref, m_manifest.files(resType)) { + KoResource *res = workspaceServer->resourceByMD5(ref.md5sum); + if (!res) res = workspaceServer->resourceByFilename(QFileInfo(ref.resourcePath).fileName()); + if (!saveResourceToStore(res, store.data(), "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 == "kis_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.data(), store.data(), "paintoppresets")) { + 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 == "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; + KoAbstractGradient *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(); + + KoAbstractGradient *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 == "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; + KoPattern *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(); + + KoPattern *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 == "brushes") { + KisBrushResourceServer *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 resouce on the server + KisBrushSP res2 = brushServer->resourceByName(res->name()); + 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.data(), tag); + } + brushServer->addTag(res.data(), name()); + } + else { + //warnKrita << "Didn't install" << res->name()<<"It already exists on the server"; + } + } + } + else if (resType == "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; + KoColorSet *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 resouce on the server + KoColorSet *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 == "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; + KisWorkspaceResource *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. + KisWorkspaceResource *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 == "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.data(), tag); + } + paintoppresetServer->addTag(res.data(), 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; + + + KoResourceServer* gradientServer = KoResourceServerProvider::instance()->gradientServer(); + //Q_FOREACH (const KisResourceBundleManifest::ResourceReference &ref, m_manifest.files("gradients")) { + Q_FOREACH (const QByteArray md5, m_gradientsMd5Installed) { + KoAbstractGradient *res = gradientServer->resourceByMD5(md5); + if (res) { + gradientServer->removeResourceFromServer(res); + } + } + + KoResourceServer* patternServer = KoResourceServerProvider::instance()->patternServer(); + //Q_FOREACH (const KisResourceBundleManifest::ResourceReference &ref, m_manifest.files("patterns")) { + Q_FOREACH (const QByteArray md5, m_patternsMd5Installed) { + KoPattern *res = patternServer->resourceByMD5(md5); + if (res) { + patternServer->removeResourceFromServer(res); + } + } + + KisBrushResourceServer *brushServer = KisBrushServer::instance()->brushServer(); + //Q_FOREACH (const KisResourceBundleManifest::ResourceReference &ref, m_manifest.files("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("palettes")) { + Q_FOREACH (const QByteArray md5, m_palettesMd5Installed) { + KoColorSet *res = paletteServer->resourceByMD5(md5); + if (res) { + paletteServer->removeResourceFromServer(res); + } + } + KoResourceServer< KisWorkspaceResource >* workspaceServer = KisResourceServerProvider::instance()->workspaceServer(); + //Q_FOREACH (const KisResourceBundleManifest::ResourceReference &ref, m_manifest.files("workspaces")) { + Q_FOREACH (const QByteArray md5, m_workspacesMd5Installed) { + KisWorkspaceResource *res = workspaceServer->resourceByMD5(md5); + if (res) { + workspaceServer->removeResourceFromServer(res); + } + } + + KisPaintOpPresetResourceServer* paintoppresetServer = KisResourceServerProvider::instance()->paintOpPresetServer(); + //Q_FOREACH (const KisResourceBundleManifest::ResourceReference &ref, m_manifest.files("paintoppresets")) { + Q_FOREACH (const QByteArray md5, m_presetsMd5Installed) { + KisPaintOpPresetSP res = paintoppresetServer->resourceByMD5(md5); + if (res) { + paintoppresetServer->removeResourceFromServer(res); + } + } + + 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() +{ + return m_manifest.types(); +} + +QList KisResourceBundle::resources(const QString &resType) +{ + QList references = m_manifest.files(resType); + + QList ret; + Q_FOREACH (const KisResourceBundleManifest::ResourceReference &ref, references) { + if (resType == "gradients") { + KoResourceServer* gradientServer = KoResourceServerProvider::instance()->gradientServer(); + KoResource *res = gradientServer->resourceByMD5(ref.md5sum); + if (res) ret << res; + } + else if (resType == "patterns") { + KoResourceServer* patternServer = KoResourceServerProvider::instance()->patternServer(); + KoResource *res = patternServer->resourceByMD5(ref.md5sum); + if (res) ret << res; + } + else if (resType == "brushes") { + KisBrushResourceServer *brushServer = KisBrushServer::instance()->brushServer(); + KoResource *res = brushServer->resourceByMD5(ref.md5sum).data(); + if (res) ret << res; + } + else if (resType == "palettes") { + KoResourceServer* paletteServer = KoResourceServerProvider::instance()->paletteServer(); + KoResource *res = paletteServer->resourceByMD5(ref.md5sum); + if (res) ret << res; + } + else if (resType == "workspaces") { + KoResourceServer< KisWorkspaceResource >* workspaceServer = KisResourceServerProvider::instance()->workspaceServer(); + KoResource *res = workspaceServer->resourceByMD5(ref.md5sum); + if (res) ret << res; + } + else if (resType == "paintoppresets") { + KisPaintOpPresetResourceServer* paintoppresetServer = KisResourceServerProvider::instance()->paintOpPresetServer(); + KisPaintOpPresetSP res = paintoppresetServer->resourceByMD5(ref.md5sum); + if (res) ret << res.data(); + } + } + 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); + Q_ASSERT(!m_thumbnail.isNull()); + } + else { + m_thumbnail = QImage(256, 256, QImage::Format_ARGB32); + QPainter gc(&m_thumbnail); + gc.fillRect(0, 0, 256, 256, Qt::red); + gc.end(); + } +} + +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()); + file.remove(); + file.setFileName(newStoreName); + file.rename(filename()); +} diff --git a/krita/ui/KisResourceBundle.h b/krita/ui/KisResourceBundle.h new file mode 100644 index 0000000000..c3a102f53b --- /dev/null +++ b/krita/ui/KisResourceBundle.h @@ -0,0 +1,159 @@ +/* + * 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 "KoResource.h" +#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 bundlePath the path of the bundle + */ + KisResourceBundle(QString const& fileName); + + /** + * @brief ~ResourceBundle : Dtor + */ + virtual ~KisResourceBundle(); + + /** + * @brief defaultFileExtension + * @return the default file extension which should be when saving the resource + */ + QString defaultFileExtension() const; + + /** + * @brief load : Load this resource. + * @return true if succeed, false otherwise. + */ + bool load(); + virtual bool loadFromDevice(QIODevice *dev); + + /** + * @brief save : Save this resource. + * @return true if succeed, false otherwise. + */ + bool save(); + + virtual bool saveToDevice(QIODevice* dev) const; + + /** + * @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 addFile : Add a file to the bundle + * @param fileType type of the resource file + * @param filePath path of the resource file + */ + 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(); + QList resources(const QString &resType); + +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; + QString m_bundleVersion; + +}; + +#endif // KORESOURCEBUNDLE_H diff --git a/krita/ui/KisResourceBundleManifest.cpp b/krita/ui/KisResourceBundleManifest.cpp new file mode 100644 index 0000000000..3caff27ace --- /dev/null +++ b/krita/ui/KisResourceBundleManifest.cpp @@ -0,0 +1,233 @@ +/* This file is part of the KDE project + Copyright (C) 2014, Victor Lafon + + 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, see . +*/ +#include "KisResourceBundleManifest.h" + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include "KoPattern.h" +#include "KoAbstractGradient.h" + +#include "kis_brush_server.h" +#include "kis_resource_server_provider.h" +#include "kis_paintop_preset.h" +#include "kis_workspace_resource.h" + +QString resourceTypeToManifestType(const QString &type) { + if (type.startsWith("ko_")) { + return type.mid(3); + } + else if (type.startsWith("kis_")) { + return type.mid(4); + } + else { + return type; + } +} + +QString manifestTypeToResourceType(const QString &type) { + if (type == "patterns" || type == "gradients" || type == "palettes") { + return "ko_" + type; + } + else { + return "kis_" + type; + } +} + +KisResourceBundleManifest::KisResourceBundleManifest() +{ +} + +KisResourceBundleManifest::~KisResourceBundleManifest() +{ +} + +bool KisResourceBundleManifest::load(QIODevice *device) +{ + m_resources.clear(); + if (!device->isOpen()) { + if (!device->open(QIODevice::ReadOnly)) { + return false; + } + } + + KoXmlDocument manifestDocument; + QString errorMessage; + int errorLine; + int errorColumn; + if (!manifestDocument.setContent(device, true, &errorMessage, &errorLine, &errorColumn)) { + return false; + } + + if (!errorMessage.isEmpty()) { + warnKrita << "Error parsing manifest" << errorMessage << "line" << errorLine << "column" << errorColumn; + return false; + } + + // First find the manifest:manifest node. + KoXmlNode n = manifestDocument.firstChild(); + for (; !n.isNull(); n = n.nextSibling()) { + if (!n.isElement()) { + continue; + } + if (n.toElement().localName() == "manifest" && n.toElement().namespaceURI() == KoXmlNS::manifest) { + break; + } + } + + if (n.isNull()) { + // "Could not find manifest:manifest"; + return false; + } + + // Now loop through the children of the manifest:manifest and + // store all the manifest:file-entry elements. + const KoXmlElement manifestElement = n.toElement(); + for (n = manifestElement.firstChild(); !n.isNull(); n = n.nextSibling()) { + + if (!n.isElement()) + continue; + + KoXmlElement el = n.toElement(); + if (!(el.localName() == "file-entry" && el.namespaceURI() == KoXmlNS::manifest)) + continue; + + QString fullPath = el.attributeNS(KoXmlNS::manifest, "full-path", QString()); + QString mediaType = el.attributeNS(KoXmlNS::manifest, "media-type", QString("")); + QString md5sum = el.attributeNS(KoXmlNS::manifest, "md5sum", QString("")); + QString version = el.attributeNS(KoXmlNS::manifest, "version", QString()); + + QStringList tagList; + KoXmlNode tagNode = n.firstChildElement().firstChildElement(); + while (!tagNode.isNull()) { + if (tagNode.firstChild().isText()) { + tagList.append(tagNode.firstChild().toText().data()); + } + tagNode = tagNode.nextSibling(); + } + + // Only if fullPath is valid, should we store this entry. + // If not, we don't bother to find out exactly what is wrong, we just skip it. + if (!fullPath.isNull() && !mediaType.isEmpty() && !md5sum.isEmpty()) { + addResource(mediaType, fullPath, tagList, QByteArray::fromHex(md5sum.toLatin1())); + } + } + + return true; +} + +bool KisResourceBundleManifest::save(QIODevice *device) +{ + if (!device->isOpen()) { + if (!device->open(QIODevice::WriteOnly)) { + return false; + } + } + KoXmlWriter manifestWriter(device); + manifestWriter.startDocument("manifest:manifest"); + manifestWriter.startElement("manifest:manifest"); + manifestWriter.addAttribute("xmlns:manifest", KoXmlNS::manifest); + manifestWriter.addAttribute("manifest:version", "1.2"); + manifestWriter.addManifestEntry("/", "application/x-krita-resourcebundle"); + + Q_FOREACH (QString resourceType, m_resources.uniqueKeys()) { + Q_FOREACH (const ResourceReference &resource, m_resources[resourceType].values()) { + manifestWriter.startElement("manifest:file-entry"); + manifestWriter.addAttribute("manifest:media-type", resourceTypeToManifestType(resourceType)); + manifestWriter.addAttribute("manifest:full-path", resourceTypeToManifestType(resourceType) + "/" + QFileInfo(resource.resourcePath).fileName()); + manifestWriter.addAttribute("manifest:md5sum", QString(resource.md5sum.toHex())); + if (!resource.tagList.isEmpty()) { + manifestWriter.startElement("manifest:tags"); + Q_FOREACH (const QString tag, resource.tagList) { + manifestWriter.startElement("manifest:tag"); + manifestWriter.addTextNode(tag); + manifestWriter.endElement(); + } + manifestWriter.endElement(); + } + manifestWriter.endElement(); + } + } + + manifestWriter.endElement(); + manifestWriter.endDocument(); + + return true; +} + +void KisResourceBundleManifest::addResource(const QString &fileTypeName, const QString &fileName, const QStringList &fileTagList, const QByteArray &md5) +{ + ResourceReference ref(fileName, fileTagList, fileTypeName, md5); + if (!m_resources.contains(fileTypeName)) { + m_resources[fileTypeName] = QMap(); + } + m_resources[fileTypeName].insert(fileName, ref); +} + +QStringList KisResourceBundleManifest::types() const +{ + return m_resources.keys(); +} + +QStringList KisResourceBundleManifest::tags() const +{ + QSet tags; + Q_FOREACH (const QString &type, m_resources.keys()) { + Q_FOREACH (const ResourceReference &ref, m_resources[type].values()) { + tags += ref.tagList.toSet(); + } + } + return QStringList::fromSet(tags); +} + +QList KisResourceBundleManifest::files(const QString &type) const +{ + // If no type is specified we return all the resources + if(type.isEmpty()) { + QList resources; + QList >::iterator i; + QList > values = m_resources.values(); + for(i = values.begin(); i != values.end(); ++i) { + resources.append(i->values()); + } + + return resources; + } + else if (!m_resources.contains(type)) { + return QList(); + } + return m_resources[type].values(); +} + +void KisResourceBundleManifest::removeFile(QString fileName) +{ + QList tags; + Q_FOREACH (const QString &type, m_resources.keys()) { + if (m_resources[type].contains(fileName)) { + m_resources[type].remove(fileName); + } + } +} + diff --git a/krita/ui/KisResourceBundleManifest.h b/krita/ui/KisResourceBundleManifest.h new file mode 100644 index 0000000000..3351b99dea --- /dev/null +++ b/krita/ui/KisResourceBundleManifest.h @@ -0,0 +1,100 @@ +/* This file is part of the KDE project + Copyright (C) 2014, Victor Lafon + + 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, see . +*/ + +#ifndef KOXMLRESOURCEBUNDLEMANIFEST_H +#define KOXMLRESOURCEBUNDLEMANIFEST_H + +#include +#include +#include +#include + +#include + +class QIODevice; + +class KRITAUI_EXPORT KisResourceBundleManifest +{ +public: + + struct ResourceReference { + + ResourceReference(const QString &_resourcePath, const QList &_tagList, const QString &_fileTypeName, const QByteArray &_md5) { + resourcePath = _resourcePath; + tagList = _tagList; + fileTypeName = _fileTypeName; + md5sum = _md5; + } + + QString resourcePath; + QList tagList; + QString fileTypeName; + QByteArray md5sum; + }; + + /** + * @brief ResourceBundleManifest : Ctor + * @param xmlName the name of the XML file to be created + */ + KisResourceBundleManifest(); + + /** + * @brief ~ResourceBundleManifest : Dtor + */ + virtual ~KisResourceBundleManifest(); + + + /** + * @brief load the ResourceBundleManifest from the given device + */ + bool load(QIODevice *device); + + /** + * @brief save the ResourceBundleManifest to the given device + */ + bool save(QIODevice *device); + + /** + * @brief addTag : Add a file tag as a child of the fileType tag. + * @param fileType the type of the file to be added + * @param fileName the name of the file to be added + * @param emptyFile true if the file is empty + * @return the element corresponding to the created tag. + */ + void addResource(const QString &fileType, const QString &fileName, const QStringList &tagFileList, const QByteArray &md5); + + + QStringList types() const; + + QStringList tags() const; + + QList files(const QString &type = QString()) const; + + /** + * @brief removeFile : remove a file from the manifest + * @param fileName : the name of the file to be removed + * @return the list of resource tags to be removed from meta file. + */ + void removeFile(QString fileName); + +private: + QMap > m_resources; +}; + + +#endif // KOXMLRESOURCEBUNDLEMANIFEST_H + diff --git a/krita/ui/kis_resource_server_provider.cpp b/krita/ui/kis_resource_server_provider.cpp index ac40cc1569..b2ccb684e4 100644 --- a/krita/ui/kis_resource_server_provider.cpp +++ b/krita/ui/kis_resource_server_provider.cpp @@ -45,6 +45,7 @@ Q_GLOBAL_STATIC(KisResourceServerProvider, s_instance) + typedef KoResourceServerSimpleConstruction > KisPaintOpPresetResourceServer; typedef KoResourceServerAdapter > KisPaintOpPresetResourceServerAdapter; @@ -55,6 +56,7 @@ inline bool isRunningInKrita() { KisResourceServerProvider::KisResourceServerProvider() + : m_resourceBundleServer(0) { KisBrushServer *brushServer = KisBrushServer::instance(); @@ -62,6 +64,7 @@ KisResourceServerProvider::KisResourceServerProvider() if (!QFileInfo(m_paintOpPresetServer->saveLocation()).exists()) { QDir().mkpath(m_paintOpPresetServer->saveLocation()); } + m_paintOpPresetThread = new KoResourceLoaderThread(m_paintOpPresetServer); m_paintOpPresetThread->loadSynchronously(); // if (!isRunningInKrita()) { @@ -110,6 +113,24 @@ KisResourceServerProvider* KisResourceServerProvider::instance() return s_instance; } +KoResourceServer *KisResourceServerProvider::resourceBundleServer() +{ + + if (!m_resourceBundleServer) { + m_resourceBundleServer = new KoResourceServerSimpleConstruction("kis_resourcebundles", "*.bundle"); + + KoResourceLoaderThread bundleLoader(m_resourceBundleServer); + bundleLoader.loadSynchronously(); + Q_FOREACH (KisResourceBundle *bundle, m_resourceBundleServer->resources()) { + if (!bundle->install()) { + warnKrita << "Could not install resources for bundle" << bundle->name(); + } + } + } + + return m_resourceBundleServer; +} + KisPaintOpPresetResourceServer* KisResourceServerProvider::paintOpPresetServer(bool block) { diff --git a/krita/ui/kis_resource_server_provider.h b/krita/ui/kis_resource_server_provider.h index f029197d71..235c166e7c 100644 --- a/krita/ui/kis_resource_server_provider.h +++ b/krita/ui/kis_resource_server_provider.h @@ -28,6 +28,7 @@ #include +#include "KisResourceBundle.h" #include "kritaui_export.h" class KoResourceLoaderThread; @@ -47,6 +48,7 @@ public: static KisResourceServerProvider* instance(); + KoResourceServer *resourceBundleServer(); KisPaintOpPresetResourceServer* paintOpPresetServer(bool block = true); KoResourceServer* workspaceServer(bool block = true); KoResourceServer* layerStyleCollectionServer(bool block = true); @@ -61,9 +63,10 @@ private: KisResourceServerProvider(const KisResourceServerProvider&); KisResourceServerProvider operator=(const KisResourceServerProvider&); - KisPaintOpPresetResourceServer* m_paintOpPresetServer; - KoResourceServer* m_workspaceServer; - KoResourceServer* m_layerStyleCollectionServer; + KisPaintOpPresetResourceServer *m_paintOpPresetServer; + KoResourceServer *m_workspaceServer; + KoResourceServer *m_layerStyleCollectionServer; + KoResourceServer *m_resourceBundleServer; private: diff --git a/krita/ui/resourcemanager/CMakeLists.txt b/krita/ui/resourcemanager/CMakeLists.txt new file mode 100644 index 0000000000..70f259f14e --- /dev/null +++ b/krita/ui/resourcemanager/CMakeLists.txt @@ -0,0 +1,19 @@ +add_subdirectory( tests ) + +set(kritaresourcemanager_SOURCES + resourcemanager.cpp + resourcebundle_manifest.cpp + resourcebundle.cpp + dlg_create_bundle.cpp + dlg_bundle_manager.cpp +) + +ki18n_wrap_ui(kritaresourcemanager_SOURCES + wdgdlgcreatebundle.ui + wdgdlgbundlemanager.ui +) + +add_library(kritaresourcemanager MODULE ${kritaresourcemanager_SOURCES}) +target_link_libraries(kritaresourcemanager kritawidgets kritaui kritalibpaintop) +install(TARGETS kritaresourcemanager DESTINATION ${CALLIGRA_PLUGIN_INSTALL_DIR}) +install(FILES resourcemanager.rc DESTINATION ${DATA_INSTALL_DIR}/kritaplugins) diff --git a/krita/ui/resourcemanager/NOTES b/krita/ui/resourcemanager/NOTES new file mode 100644 index 0000000000..a84495b368 --- /dev/null +++ b/krita/ui/resourcemanager/NOTES @@ -0,0 +1 @@ +* make it possible to add resources when creating a bundle that are not loaded already in Krita diff --git a/krita/ui/resourcemanager/dlg_bundle_manager.cpp b/krita/ui/resourcemanager/dlg_bundle_manager.cpp new file mode 100644 index 0000000000..ae1891dee0 --- /dev/null +++ b/krita/ui/resourcemanager/dlg_bundle_manager.cpp @@ -0,0 +1,356 @@ +/* + * 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 "dlg_bundle_manager.h" +#include "ui_wdgdlgbundlemanager.h" + +#include "resourcemanager.h" +#include "dlg_create_bundle.h" + +#include +#include +#include +#include +#include +#include + +#include +#include "kis_action.h" + +#define ICON_SIZE 48 + +DlgBundleManager::DlgBundleManager(KisActionManager* actionMgr, QWidget *parent) + : KoDialog(parent) + , m_page(new QWidget()) + , m_ui(new Ui::WdgDlgBundleManager) + , m_currentBundle(0) +{ + setCaption(i18n("Manage Resource Bundles")); + m_ui->setupUi(m_page); + setMainWidget(m_page); + resize(m_page->sizeHint()); + setButtons(Ok | Cancel); + setDefaultButton(Ok); + + m_ui->listActive->setIconSize(QSize(ICON_SIZE, ICON_SIZE)); + m_ui->listActive->setSelectionMode(QAbstractItemView::ExtendedSelection); + connect(m_ui->listActive, SIGNAL(currentItemChanged(QListWidgetItem*,QListWidgetItem*)), SLOT(itemSelected(QListWidgetItem*,QListWidgetItem*))); + connect(m_ui->listActive, SIGNAL(itemClicked(QListWidgetItem*)), SLOT(itemSelected(QListWidgetItem*))); + + m_ui->listInactive->setIconSize(QSize(ICON_SIZE, ICON_SIZE)); + m_ui->listInactive->setSelectionMode(QAbstractItemView::ExtendedSelection); + connect(m_ui->listInactive, SIGNAL(currentItemChanged(QListWidgetItem*,QListWidgetItem*)), SLOT(itemSelected(QListWidgetItem*,QListWidgetItem*))); + connect(m_ui->listInactive, SIGNAL(itemClicked(QListWidgetItem*)), SLOT(itemSelected(QListWidgetItem*))); + + m_ui->bnAdd->setIcon(KisIconUtils::loadIcon("arrow-right")); + connect(m_ui->bnAdd, SIGNAL(clicked()), SLOT(addSelected())); + + m_ui->bnRemove->setIcon(KisIconUtils::loadIcon("arrow-left")); + connect(m_ui->bnRemove, SIGNAL(clicked()), SLOT(removeSelected())); + + m_ui->listBundleContents->setHeaderLabel(i18n("Resource")); + m_ui->listBundleContents->setSelectionMode(QAbstractItemView::NoSelection); + + m_actionManager = actionMgr; + + refreshListData(); + + connect(m_ui->bnEditBundle, SIGNAL(clicked()), SLOT(editBundle())); + + connect(m_ui->importBundleButton, SIGNAL(clicked()), SLOT(slotImportResource())); + connect(m_ui->createBundleButton, SIGNAL(clicked()), SLOT(slotCreateBundle())); + connect(m_ui->deleteBackupFilesButton, SIGNAL(clicked()), SLOT(slotDeleteBackupFiles())); + connect(m_ui->openResourceFolderButton, SIGNAL(clicked()), SLOT(slotOpenResourceFolder())); + +} + + +void DlgBundleManager::refreshListData() +{ + KoResourceServer *bundleServer = ResourceBundleServerProvider::instance()->resourceBundleServer(); + + m_ui->listInactive->clear(); + m_ui->listActive->clear(); + + + Q_FOREACH (const QString &f, bundleServer->blackListedFiles()) { + ResourceBundle *bundle = new ResourceBundle(f); + bundle->load(); + if (bundle->valid()) { + bundle->setInstalled(false); + m_blacklistedBundles[f] = bundle; + } + } + fillListWidget(m_blacklistedBundles.values(), m_ui->listInactive); + + Q_FOREACH (ResourceBundle *bundle, bundleServer->resources()) { + if (bundle->valid()) { + m_activeBundles[bundle->filename()] = bundle; + } + } + fillListWidget(m_activeBundles.values(), m_ui->listActive); +} + +void DlgBundleManager::accept() +{ + KoResourceServer *bundleServer = ResourceBundleServerProvider::instance()->resourceBundleServer(); + + for (int i = 0; i < m_ui->listActive->count(); ++i) { + QListWidgetItem *item = m_ui->listActive->item(i); + QByteArray ba = item->data(Qt::UserRole).toByteArray(); + ResourceBundle *bundle = bundleServer->resourceByMD5(ba); + QMessageBox bundleFeedback; + bundleFeedback.setIcon(QMessageBox::Warning); + QString feedback = "bundlefeedback"; + + if (!bundle) { + // Get it from the blacklisted bundles + Q_FOREACH (ResourceBundle *b2, m_blacklistedBundles.values()) { + if (b2->md5() == ba) { + bundle = b2; + break; + } + } + } + + if (bundle) { + if(!bundle->isInstalled()){ + bundle->install(); + //this removes the bundle from the blacklist and add it to the server without saving or putting it in front// + if(!bundleServer->addResource(bundle, false, false)){ + + feedback = i18n("Couldn't add bundle to resource server"); + bundleFeedback.setText(feedback); + bundleFeedback.exec(); + } + if(!bundleServer->removeFromBlacklist(bundle)){ + feedback = i18n("Couldn't remove bundle from blacklist"); + bundleFeedback.setText(feedback); + bundleFeedback.exec(); + } + } + else { + bundleServer->removeFromBlacklist(bundle); + //let's asume that bundles who exist and are installed have to be removed from the blacklist, and if they were already this returns false, so that's not a problem. + } + } + else{ + QString feedback = i18n("Bundle doesn't exist!"); + bundleFeedback.setText(feedback); + bundleFeedback.exec(); + + } + } + + for (int i = 0; i < m_ui->listInactive->count(); ++i) { + QListWidgetItem *item = m_ui->listInactive->item(i); + QByteArray ba = item->data(Qt::UserRole).toByteArray(); + ResourceBundle *bundle = bundleServer->resourceByMD5(ba); + + if (bundle && bundle->isInstalled()) { + bundle->uninstall(); + bundleServer->removeResourceAndBlacklist(bundle); + } + } + + + KoDialog::accept(); +} + +void DlgBundleManager::addSelected() +{ + + Q_FOREACH (QListWidgetItem *item, m_ui->listActive->selectedItems()) { + m_ui->listInactive->addItem(m_ui->listActive->takeItem(m_ui->listActive->row(item))); + } + +} + +void DlgBundleManager::removeSelected() +{ + Q_FOREACH (QListWidgetItem *item, m_ui->listInactive->selectedItems()) { + m_ui->listActive->addItem(m_ui->listInactive->takeItem(m_ui->listInactive->row(item))); + } +} + +void DlgBundleManager::itemSelected(QListWidgetItem *current, QListWidgetItem *) +{ + if (!current) { + m_ui->lblName->setText(""); + m_ui->lblAuthor->setText(""); + m_ui->lblEmail->setText(""); + m_ui->lblLicense->setText(""); + m_ui->lblWebsite->setText(""); + m_ui->lblDescription->setPlainText(""); + m_ui->lblCreated->setText(""); + m_ui->lblUpdated->setText(""); + m_ui->lblPreview->setPixmap(QPixmap::fromImage(QImage())); + m_ui->listBundleContents->clear(); + m_ui->bnEditBundle->setEnabled(false); + m_currentBundle = 0; + } + else { + + QByteArray ba = current->data(Qt::UserRole).toByteArray(); + KoResourceServer *bundleServer = ResourceBundleServerProvider::instance()->resourceBundleServer(); + ResourceBundle *bundle = bundleServer->resourceByMD5(ba); + + if (!bundle) { + // Get it from the blacklisted bundles + Q_FOREACH (ResourceBundle *b2, m_blacklistedBundles.values()) { + if (b2->md5() == ba) { + bundle = b2; + break; + } + } + } + + if (bundle) { + + m_currentBundle = bundle; + m_ui->bnEditBundle->setEnabled(true); + + m_ui->lblName->setText(bundle->name()); + m_ui->lblAuthor->setText(bundle->getMeta("author")); + m_ui->lblEmail->setText(bundle->getMeta("email")); + m_ui->lblLicense->setText(bundle->getMeta("license")); + m_ui->lblWebsite->setText(bundle->getMeta("website")); + m_ui->lblDescription->setPlainText(bundle->getMeta("description")); + m_ui->lblCreated->setText(bundle->getMeta("created")); + m_ui->lblUpdated->setText(bundle->getMeta("updated")); + m_ui->lblPreview->setPixmap(QPixmap::fromImage(bundle->image().scaled(128, 128, Qt::KeepAspectRatio, Qt::SmoothTransformation))); + m_ui->listBundleContents->clear(); + + Q_FOREACH (const QString & resType, bundle->resourceTypes()) { + + QTreeWidgetItem *toplevel = new QTreeWidgetItem(); + if (resType == "gradients") { + toplevel->setText(0, i18n("Gradients")); + } + else if (resType == "patterns") { + toplevel->setText(0, i18n("Patterns")); + } + else if (resType == "brushes") { + toplevel->setText(0, i18n("Brushes")); + } + else if (resType == "palettes") { + toplevel->setText(0, i18n("Palettes")); + } + else if (resType == "workspaces") { + toplevel->setText(0, i18n("Workspaces")); + } + else if (resType == "paintoppresets") { + toplevel->setText(0, i18n("Brush Presets")); + } + + m_ui->listBundleContents->addTopLevelItem(toplevel); + + Q_FOREACH (const KoResource *res, bundle->resources(resType)) { + if (res) { + QTreeWidgetItem *i = new QTreeWidgetItem(); + i->setIcon(0, QIcon(QPixmap::fromImage(res->image()))); + i->setText(0, res->name()); + toplevel->addChild(i); + } + } + } + } + else { + m_currentBundle = 0; + } + } +} + +void DlgBundleManager::itemSelected(QListWidgetItem *current) +{ + itemSelected(current, 0); +} + +void DlgBundleManager::editBundle() +{ + if (m_currentBundle) { + DlgCreateBundle dlg(m_currentBundle); + if (dlg.exec() != QDialog::Accepted) { + return; + } + } +} + +void DlgBundleManager::fillListWidget(QList bundles, QListWidget *w) +{ + w->setIconSize(QSize(ICON_SIZE, ICON_SIZE)); + w->setSelectionMode(QAbstractItemView::MultiSelection); + + Q_FOREACH (ResourceBundle *bundle, bundles) { + QPixmap pixmap(ICON_SIZE, ICON_SIZE); + if (!bundle->image().isNull()) { + QImage scaled = bundle->image().scaled(ICON_SIZE, ICON_SIZE, Qt::KeepAspectRatio, Qt::SmoothTransformation); + int x = (ICON_SIZE - scaled.width()) / 2; + int y = (ICON_SIZE - scaled.height()) / 2; + QPainter gc(&pixmap); + gc.drawImage(x, y, scaled); + gc.end(); + } + else { + pixmap.fill(Qt::gray); + } + + QListWidgetItem *item = new QListWidgetItem(pixmap, bundle->name()); + item->setData(Qt::UserRole, bundle->md5()); + w->addItem(item); + } +} + + +void DlgBundleManager::slotImportResource() { + + if (m_actionManager) + { + KisAction *action = m_actionManager->actionByName("import_resources"); + action->trigger(); + refreshListData(); + } +} + +void DlgBundleManager::slotCreateBundle() { + + if (m_actionManager) + { + KisAction *action = m_actionManager->actionByName("create_bundle"); + action->trigger(); + } +} + +void DlgBundleManager::slotDeleteBackupFiles() { + + if (m_actionManager) + { + KisAction *action = m_actionManager->actionByName("edit_blacklist_cleanup"); + action->trigger(); + } +} + +void DlgBundleManager::slotOpenResourceFolder() { + + if (m_actionManager) + { + KisAction *action = m_actionManager->actionByName("open_resources_directory"); + action->trigger(); + } +} + diff --git a/krita/ui/resourcemanager/dlg_bundle_manager.h b/krita/ui/resourcemanager/dlg_bundle_manager.h new file mode 100644 index 0000000000..83f422bc74 --- /dev/null +++ b/krita/ui/resourcemanager/dlg_bundle_manager.h @@ -0,0 +1,70 @@ +/* + * 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 DLG_BUNDLE_MANAGER_H +#define DLG_BUNDLE_MANAGER_H + +#include + +#include +#include "kis_action_manager.h" + +class ResourceBundle; +class QListWidget; +class QListWidgetItem; + +namespace Ui +{ +class WdgDlgBundleManager; +} + +class DlgBundleManager : public KoDialog +{ + Q_OBJECT +public: + explicit DlgBundleManager(KisActionManager* actionMgr, QWidget *parent = 0); + +private Q_SLOTS: + + void accept(); + void addSelected(); + void removeSelected(); + void itemSelected(QListWidgetItem *current, QListWidgetItem *previous); + void itemSelected(QListWidgetItem *current); + void editBundle(); + void slotImportResource() ; + void slotCreateBundle() ; + void slotDeleteBackupFiles(); + void slotOpenResourceFolder(); + +private: + + QWidget *m_page; + Ui::WdgDlgBundleManager *m_ui; + + void fillListWidget(QList bundles, QListWidget *w); + void refreshListData(); + + QMap m_blacklistedBundles; + QMap m_activeBundles; + + ResourceBundle *m_currentBundle; + KisActionManager* m_actionManager; +}; + +#endif // DLG_BUNDLE_MANAGER_H diff --git a/krita/ui/resourcemanager/dlg_create_bundle.cpp b/krita/ui/resourcemanager/dlg_create_bundle.cpp new file mode 100644 index 0000000000..21981e6845 --- /dev/null +++ b/krita/ui/resourcemanager/dlg_create_bundle.cpp @@ -0,0 +1,387 @@ +/* + * 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 "dlg_create_bundle.h" + +#include "ui_wdgdlgcreatebundle.h" + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +#include "resourcebundle.h" + +#define ICON_SIZE 48 + +DlgCreateBundle::DlgCreateBundle(ResourceBundle *bundle, QWidget *parent) + : KoDialog(parent) + , m_ui(new Ui::WdgDlgCreateBundle) + , m_bundle(bundle) +{ + m_page = new QWidget(); + m_ui->setupUi(m_page); + setMainWidget(m_page); + setFixedSize(m_page->sizeHint()); + setButtons(Ok | Cancel); + setDefaultButton(Ok); + setButtonText(Ok, i18n("Save")); + + connect(m_ui->bnSelectSaveLocation, SIGNAL(clicked()), SLOT(selectSaveLocation())); + + KoDocumentInfo info; + info.updateParameters(); + + if (bundle) { + + setCaption(i18n("Edit Resource Bundle")); + + m_ui->lblSaveLocation->setText(QFileInfo(bundle->filename()).absolutePath()); + m_ui->editBundleName->setText(bundle->name()); + m_ui->editAuthor->setText(bundle->getMeta("author")); + m_ui->editEmail->setText(bundle->getMeta("email")); + m_ui->editLicense->setText(bundle->getMeta("license")); + m_ui->editWebsite->setText(bundle->getMeta("website")); + m_ui->editDescription->document()->setPlainText(bundle->getMeta("description")); + m_ui->lblPreview->setPixmap(QPixmap::fromImage(bundle->image().scaled(256, 256, Qt::KeepAspectRatio, Qt::SmoothTransformation))); + } + else { + + setCaption(i18n("Create Resource Bundle")); + + KisConfig cfg; + + m_ui->editAuthor->setText(cfg.readEntry("BundleAuthorName", info.authorInfo("creator"))); + m_ui->editEmail->setText(cfg.readEntry("BundleAuthorEmail", info.authorInfo("email"))); + m_ui->editWebsite->setText(cfg.readEntry("BundleWebsite", "http://")); + m_ui->editLicense->setText(cfg.readEntry("BundleLicense", "CC-BY-SA")); + m_ui->lblSaveLocation->setText(cfg.readEntry("BundleExportLocation", QDesktopServices::storageLocation(QDesktopServices::DocumentsLocation))); + } + + m_ui->bnAdd->setIcon(KisIconUtils::loadIcon("arrow-right")); + connect(m_ui->bnAdd, SIGNAL(clicked()), SLOT(addSelected())); + + m_ui->bnRemove->setIcon(KisIconUtils::loadIcon("arrow-left")); + connect(m_ui->bnRemove, SIGNAL(clicked()), SLOT(removeSelected())); + + m_ui->cmbResourceTypes->addItem(i18n("Brushes"), QString("brushes")); + m_ui->cmbResourceTypes->addItem(i18n("Brush Presets"), QString("presets")); + m_ui->cmbResourceTypes->addItem(i18n("Gradients"), QString("gradients")); + m_ui->cmbResourceTypes->addItem(i18n("Patterns"), QString("patterns")); + m_ui->cmbResourceTypes->addItem(i18n("Palettes"), QString("palettes")); + m_ui->cmbResourceTypes->addItem(i18n("Workspaces"), QString("workspaces")); + connect(m_ui->cmbResourceTypes, SIGNAL(activated(int)), SLOT(resourceTypeSelected(int))); + + m_ui->tableAvailable->setIconSize(QSize(ICON_SIZE, ICON_SIZE)); + m_ui->tableAvailable->setSelectionMode(QAbstractItemView::ExtendedSelection); + + m_ui->tableSelected->setIconSize(QSize(ICON_SIZE, ICON_SIZE)); + m_ui->tableSelected->setSelectionMode(QAbstractItemView::ExtendedSelection); + + connect(m_ui->bnGetPreview, SIGNAL(clicked()), SLOT(getPreviewImage())); + + resourceTypeSelected(0); +} + +DlgCreateBundle::~DlgCreateBundle() +{ + delete m_ui; +} + +QString DlgCreateBundle::bundleName() const +{ + return m_ui->editBundleName->text().replace(" ", "_"); +} + +QString DlgCreateBundle::authorName() const +{ + return m_ui->editAuthor->text(); +} + +QString DlgCreateBundle::email() const +{ + return m_ui->editEmail->text(); +} + +QString DlgCreateBundle::website() const +{ + return m_ui->editWebsite->text(); +} + +QString DlgCreateBundle::license() const +{ + return m_ui->editLicense->text(); +} + +QString DlgCreateBundle::description() const +{ + return m_ui->editDescription->document()->toPlainText(); +} + +QString DlgCreateBundle::saveLocation() const +{ + return m_ui->lblSaveLocation->text(); +} + +QString DlgCreateBundle::previewImage() const +{ + return m_previewImage; +} + +void DlgCreateBundle::accept() +{ + QString name = m_ui->editBundleName->text().remove(" "); + + if (name.isEmpty()) { + m_ui->editBundleName->setStyleSheet(QString(" border: 1px solid red")); + QMessageBox::warning(this, i18nc("@title:window", "Krita"), i18n("The resource bundle name cannot be empty.")); + return; + } + else { + QFileInfo fileInfo(m_ui->lblSaveLocation->text() + "/" + name + ".bundle"); + + if (fileInfo.exists()) { + m_ui->editBundleName->setStyleSheet("border: 1px solid red"); + QMessageBox::warning(this, i18nc("@title:window", "Krita"), i18n("A bundle with this name already exists.")); + return; + } + else { + if (!m_bundle) { + KisConfig cfg; + cfg.writeEntry("BunleExportLocation", m_ui->lblSaveLocation->text()); + cfg.writeEntry("BundleAuthorName", m_ui->editAuthor->text()); + cfg.writeEntry("BundleAuthorEmail", m_ui->editEmail->text()); + cfg.writeEntry("BundleWebsite", m_ui->editWebsite->text()); + cfg.writeEntry("BundleLicense", m_ui->editLicense->text()); + } + KoDialog::accept(); + } + } +} + +void DlgCreateBundle::selectSaveLocation() +{ + KoFileDialog dialog(this, KoFileDialog::OpenDirectory, "resourcebundlesavelocation"); + dialog.setDefaultDir(m_ui->lblSaveLocation->text()); + dialog.setCaption(i18n("Select a directory to save the bundle")); + QString location = dialog.filename(); + m_ui->lblSaveLocation->setText(location); +} + +void DlgCreateBundle::addSelected() +{ + int row = m_ui->tableAvailable->currentRow(); + + Q_FOREACH (QListWidgetItem *item, m_ui->tableAvailable->selectedItems()) { + m_ui->tableSelected->addItem(m_ui->tableAvailable->takeItem(m_ui->tableAvailable->row(item))); + QString resourceType = m_ui->cmbResourceTypes->itemData(m_ui->cmbResourceTypes->currentIndex()).toString(); + if (resourceType == "brushes") { + m_selectedBrushes.append(item->data(Qt::UserRole).toString()); + } + else if (resourceType == "presets") { + m_selectedPresets.append(item->data(Qt::UserRole).toString()); + } + else if (resourceType == "gradients") { + m_selectedGradients.append(item->data(Qt::UserRole).toString()); + + } + else if (resourceType == "patterns") { + m_selectedPatterns.append(item->data(Qt::UserRole).toString()); + } + else if (resourceType == "palettes") { + m_selectedPalettes.append(item->data(Qt::UserRole).toString()); + } + else if (resourceType == "workspaces") { + m_selectedWorkspaces.append(item->data(Qt::UserRole).toString()); + } + } + + m_ui->tableAvailable->setCurrentRow(row); +} + +void DlgCreateBundle::removeSelected() +{ + int row = m_ui->tableSelected->currentRow(); + + Q_FOREACH (QListWidgetItem *item, m_ui->tableSelected->selectedItems()) { + m_ui->tableAvailable->addItem(m_ui->tableSelected->takeItem(m_ui->tableSelected->row(item))); + QString resourceType = m_ui->cmbResourceTypes->itemData(m_ui->cmbResourceTypes->currentIndex()).toString(); + if (resourceType == "brushes") { + m_selectedBrushes.removeAll(item->data(Qt::UserRole).toString()); + } + else if (resourceType == "presets") { + m_selectedPresets.removeAll(item->data(Qt::UserRole).toString()); + } + else if (resourceType == "gradients") { + m_selectedGradients.removeAll(item->data(Qt::UserRole).toString()); + + } + else if (resourceType == "patterns") { + m_selectedPatterns.removeAll(item->data(Qt::UserRole).toString()); + } + else if (resourceType == "palettes") { + m_selectedPalettes.removeAll(item->data(Qt::UserRole).toString()); + } + else if (resourceType == "workspaces") { + m_selectedWorkspaces.removeAll(item->data(Qt::UserRole).toString()); + } + } + + m_ui->tableSelected->setCurrentRow(row); +} + +QPixmap imageToIcon(const QImage &img) { + QPixmap pixmap(ICON_SIZE, ICON_SIZE); + pixmap.fill(); + QImage scaled = img.scaled(ICON_SIZE, ICON_SIZE, Qt::KeepAspectRatio, Qt::SmoothTransformation); + int x = (ICON_SIZE - scaled.width()) / 2; + int y = (ICON_SIZE - scaled.height()) / 2; + QPainter gc(&pixmap); + gc.drawImage(x, y, scaled); + gc.end(); + return pixmap; +} + +void DlgCreateBundle::resourceTypeSelected(int idx) +{ + QString resourceType = m_ui->cmbResourceTypes->itemData(idx).toString(); + + m_ui->tableAvailable->clear(); + m_ui->tableSelected->clear(); + + if (resourceType == "brushes") { + KisBrushResourceServer *server = KisBrushServer::instance()->brushServer(); + Q_FOREACH (KisBrushSP res, server->resources()) { + QListWidgetItem *item = new QListWidgetItem(imageToIcon(res->image()), res->name()); + item->setData(Qt::UserRole, res->shortFilename()); + + if (m_selectedBrushes.contains(res->shortFilename())) { + m_ui->tableSelected->addItem(item); + } + else { + m_ui->tableAvailable->addItem(item); + } + } + } + else if (resourceType == "presets") { + KisPaintOpPresetResourceServer* server = KisResourceServerProvider::instance()->paintOpPresetServer(); + Q_FOREACH (KisPaintOpPresetSP res, server->resources()) { + QListWidgetItem *item = new QListWidgetItem(imageToIcon(res->image()), res->name()); + item->setData(Qt::UserRole, res->shortFilename()); + + if (m_selectedPresets.contains(res->shortFilename())) { + m_ui->tableSelected->addItem(item); + } + else { + m_ui->tableAvailable->addItem(item); + } + } + } + else if (resourceType == "gradients") { + KoResourceServer* server = KoResourceServerProvider::instance()->gradientServer(); + Q_FOREACH (KoResource *res, server->resources()) { + if (res->filename()!="Foreground to Transparent" && res->filename()!="Foreground to Background") { + //technically we should read from the file-name whether or not the file can be opened, but this works for now. The problem is making sure that bundle-resource know where they are stored.// + //dbgKrita<filename(); + QListWidgetItem *item = new QListWidgetItem(imageToIcon(res->image()), res->name()); + item->setData(Qt::UserRole, res->shortFilename()); + + if (m_selectedGradients.contains(res->shortFilename())) { + m_ui->tableSelected->addItem(item); + } + else { + m_ui->tableAvailable->addItem(item); + } + } + } + } + else if (resourceType == "patterns") { + KoResourceServer* server = KoResourceServerProvider::instance()->patternServer(); + Q_FOREACH (KoResource *res, server->resources()) { + QListWidgetItem *item = new QListWidgetItem(imageToIcon(res->image()), res->name()); + item->setData(Qt::UserRole, res->shortFilename()); + + if (m_selectedPatterns.contains(res->shortFilename())) { + m_ui->tableSelected->addItem(item); + } + else { + m_ui->tableAvailable->addItem(item); + } + } + } + else if (resourceType == "palettes") { + KoResourceServer* server = KoResourceServerProvider::instance()->paletteServer(); + Q_FOREACH (KoResource *res, server->resources()) { + QListWidgetItem *item = new QListWidgetItem(imageToIcon(res->image()), res->name()); + item->setData(Qt::UserRole, res->shortFilename()); + + if (m_selectedPalettes.contains(res->shortFilename())) { + m_ui->tableSelected->addItem(item); + } + else { + m_ui->tableAvailable->addItem(item); + } + } + } + else if (resourceType == "workspaces") { + KoResourceServer* server = KisResourceServerProvider::instance()->workspaceServer(); + Q_FOREACH (KoResource *res, server->resources()) { + QListWidgetItem *item = new QListWidgetItem(imageToIcon(res->image()), res->name()); + item->setData(Qt::UserRole, res->shortFilename()); + + if (m_selectedWorkspaces.contains(res->shortFilename())) { + m_ui->tableSelected->addItem(item); + } + else { + m_ui->tableAvailable->addItem(item); + } + } + } +} + +void DlgCreateBundle::getPreviewImage() +{ + KoFileDialog dialog(this, KoFileDialog::OpenFile, "BundlePreviewImage"); + dialog.setCaption(i18n("Select file to use as dynamic file layer.")); + dialog.setDefaultDir(QDesktopServices::storageLocation(QDesktopServices::PicturesLocation)); + dialog.setMimeTypeFilters(KisImportExportManager::mimeFilter("application/x-krita", KisImportExportManager::Import)); + m_previewImage = dialog.filename(); + QImage img(m_previewImage); + img = img.scaled(256, 256, Qt::KeepAspectRatio, Qt::SmoothTransformation); + m_ui->lblPreview->setPixmap(QPixmap::fromImage(img)); +} + + diff --git a/krita/ui/resourcemanager/dlg_create_bundle.h b/krita/ui/resourcemanager/dlg_create_bundle.h new file mode 100644 index 0000000000..c4661df688 --- /dev/null +++ b/krita/ui/resourcemanager/dlg_create_bundle.h @@ -0,0 +1,80 @@ +/* + * 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 KOBUNDLECREATIONWIDGET_H +#define KOBUNDLECREATIONWIDGET_H + +#include + +class ResourceBundle; + +namespace Ui +{ +class WdgDlgCreateBundle; +} + +class DlgCreateBundle : public KoDialog +{ + Q_OBJECT + +public: + explicit DlgCreateBundle(ResourceBundle *bundle = 0, QWidget *parent = 0); + ~DlgCreateBundle(); + + QString bundleName() const; + QString authorName() const; + QString email() const; + QString website() const; + QString license() const; + QString description() const; + QString saveLocation() const; + QString previewImage() const; + + QStringList selectedBrushes() const { return m_selectedBrushes; } + QStringList selectedPresets() const { return m_selectedPresets; } + QStringList selectedGradients() const { return m_selectedGradients; } + QStringList selectedPatterns() const { return m_selectedPatterns; } + QStringList selectedPalettes() const { return m_selectedPalettes; } + QStringList selectedWorkspaces() const { return m_selectedWorkspaces; } + +private Q_SLOTS: + + void accept(); + void selectSaveLocation(); + void addSelected(); + void removeSelected(); + void resourceTypeSelected(int idx); + void getPreviewImage(); + +private: + QWidget *m_page; + Ui::WdgDlgCreateBundle *m_ui; + + QStringList m_selectedBrushes; + QStringList m_selectedPresets; + QStringList m_selectedGradients; + QStringList m_selectedPatterns; + QStringList m_selectedPalettes; + QStringList m_selectedWorkspaces; + + QString m_previewImage; + + ResourceBundle *m_bundle; +}; + +#endif // KOBUNDLECREATIONWIDGET_H diff --git a/krita/ui/resourcemanager/kritaresourcemanager.json b/krita/ui/resourcemanager/kritaresourcemanager.json new file mode 100644 index 0000000000..7905c5ce8e --- /dev/null +++ b/krita/ui/resourcemanager/kritaresourcemanager.json @@ -0,0 +1,9 @@ +{ + "Id": "Resource Manager", + "Type": "Service", + "X-KDE-Library": "kritaresourcemanager", + "X-KDE-ServiceTypes": [ + "Krita/ViewPlugin" + ], + "X-Krita-Version": "28" +} diff --git a/krita/plugins/extensions/resourcemanager/resourcebundle.cpp b/krita/ui/resourcemanager/resourcebundle.cpp similarity index 100% rename from krita/plugins/extensions/resourcemanager/resourcebundle.cpp rename to krita/ui/resourcemanager/resourcebundle.cpp diff --git a/krita/plugins/extensions/resourcemanager/resourcebundle.h b/krita/ui/resourcemanager/resourcebundle.h similarity index 100% rename from krita/plugins/extensions/resourcemanager/resourcebundle.h rename to krita/ui/resourcemanager/resourcebundle.h diff --git a/krita/plugins/extensions/resourcemanager/resourcebundle_manifest.cpp b/krita/ui/resourcemanager/resourcebundle_manifest.cpp similarity index 100% rename from krita/plugins/extensions/resourcemanager/resourcebundle_manifest.cpp rename to krita/ui/resourcemanager/resourcebundle_manifest.cpp diff --git a/krita/plugins/extensions/resourcemanager/resourcebundle_manifest.h b/krita/ui/resourcemanager/resourcebundle_manifest.h similarity index 100% rename from krita/plugins/extensions/resourcemanager/resourcebundle_manifest.h rename to krita/ui/resourcemanager/resourcebundle_manifest.h diff --git a/krita/ui/resourcemanager/resourcemanager.cpp b/krita/ui/resourcemanager/resourcemanager.cpp new file mode 100644 index 0000000000..70412d8d42 --- /dev/null +++ b/krita/ui/resourcemanager/resourcemanager.cpp @@ -0,0 +1,332 @@ +/* + * resourcemanager.cc -- Part of Krita + * + * Copyright (c) 2004 Boudewijn Rempt (boud@valdyas.org) + * + * 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 "resourcemanager.h" + +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "dlg_bundle_manager.h" +#include "dlg_create_bundle.h" + +Q_GLOBAL_STATIC(ResourceBundleServerProvider, s_instance) + +ResourceBundleServerProvider::ResourceBundleServerProvider() +{ + // user-local + m_resourceBundleServer = new KoResourceServerSimpleConstruction("kis_resourcebundles", "*.bundle"); + if (!QFileInfo(m_resourceBundleServer->saveLocation()).exists()) { + QDir().mkpath(m_resourceBundleServer->saveLocation()); + } +} + + +ResourceBundleServerProvider *ResourceBundleServerProvider::instance() +{ + return s_instance; +} + +ResourceBundleServerProvider::~ResourceBundleServerProvider() +{ + delete m_resourceBundleServer; +} + +KoResourceServer *ResourceBundleServerProvider::resourceBundleServer() +{ + return m_resourceBundleServer; +} + + +class ResourceManager::Private { + +public: + + Private() + : loader(0) + { + brushServer = KisBrushServer::instance()->brushServer(false); + paintopServer = KisResourceServerProvider::instance()->paintOpPresetServer(false); + gradientServer = KoResourceServerProvider::instance()->gradientServer(false); + patternServer = KoResourceServerProvider::instance()->patternServer(false); + paletteServer = KoResourceServerProvider::instance()->paletteServer(false); + workspaceServer = KisResourceServerProvider::instance()->workspaceServer(false); + } + + KisBrushResourceServer* brushServer; + KisPaintOpPresetResourceServer * paintopServer; + KoResourceServer* gradientServer; + KoResourceServer *patternServer; + KoResourceServer* paletteServer; + KoResourceServer* workspaceServer; + + QThread *loader; + +}; + +K_PLUGIN_FACTORY_WITH_JSON(ResourceManagerFactory, "kritaresourcemanager.json", registerPlugin();) + +ResourceManager::ResourceManager(QObject *parent, const QVariantList &) + : KisViewPlugin(parent) + , d(new Private()) +{ + QTimer::singleShot(0, this, SLOT(loadBundles())); + + KisAction *action = new KisAction(i18n("Import Resources or Bundles..."), this); + addAction("import_resources", action); + connect(action, SIGNAL(triggered()), this, SLOT(slotImport())); + + action = new KisAction(i18n("Create Resource Bundle..."), this); + addAction("create_bundle", action); + connect(action, SIGNAL(triggered()), this, SLOT(slotCreateBundle())); + + action = new KisAction(i18n("Manage Resources..."), this); + addAction("manage_bundles", action); + connect(action, SIGNAL(triggered()), this, SLOT(slotManageBundles())); + +} + +ResourceManager::~ResourceManager() +{ +} + +void ResourceManager::slotImport() +{ + KoFileDialog dialog(m_view->mainWindow(), KoFileDialog::OpenFiles, "krita_resources"); + dialog.setCaption(i18n("Add Resources")); + + QMap filterToTypeMap; + filterToTypeMap[i18n("Krita Brush Presets (*.kpp)")] = "presets"; + filterToTypeMap[i18n("GIMP Brushes (*.gbr)")] = "brushes"; + filterToTypeMap[i18n("Imagepipe Brushes (*.gih)")] = "brushes"; + filterToTypeMap[i18n("Photoshop Brushes (*.abr)")] = "brushes"; + filterToTypeMap[i18n("PNG Brushes (*.png)")] = "brushes"; + filterToTypeMap[i18n("SVG Brushes (*.svg)")] = "brushes"; + filterToTypeMap[i18n("GIMP Gradients (*.ggr)")] = "gradients"; + filterToTypeMap[i18n("SVG Gradients (*.svg)")] = "gradients"; + filterToTypeMap[i18n("Karbon Gradients (*.kgr)")] = "gradients"; + filterToTypeMap[i18n("Resource Bundles (*.bundle)")] = "bundles"; + filterToTypeMap[i18n("GIMP Patterns (*.pat)")] = "patterns"; + filterToTypeMap[i18n("JPEG Patterns (*.jpg)")] = "patterns"; + filterToTypeMap[i18n("GIF Patterns (*.gif)")] = "patterns"; + filterToTypeMap[i18n("PNG Patterns (*.png)")] = "patterns"; + filterToTypeMap[i18n("TIFF Patterns (*.tif)")] = "patterns"; + filterToTypeMap[i18n("XPM Patterns (*.xpm)")] = "patterns"; + filterToTypeMap[i18n("BMP Patterns (*.bmp)")] = "patterns"; + filterToTypeMap[i18n("Palettes (*.gpl *.pal *.act *.aco *.colors)")] = "palettes"; + filterToTypeMap[i18n("Workspaces (*.kts)")] = "workspaces"; + + QStringList nameFilters = filterToTypeMap.keys(); + + dialog.setNameFilters(nameFilters, nameFilters[13]); // start with resource bundle as default type (filterToTypeMap is alphabetized) + + QStringList resources = dialog.filenames(); + QString resourceType = dialog.selectedNameFilter(); + if (!filterToTypeMap.contains(resourceType)) { + QMessageBox::warning(0, i18nc("@title:window", "Krita"), i18n("The selected resource type is unknown.")); + return; + } + + resourceType = filterToTypeMap[resourceType]; + + if (resourceType == "brushes") { + Q_FOREACH (const QString &res, resources) { + d->brushServer->importResourceFile(res); + } + } + else if (resourceType == "presets") { + Q_FOREACH (const QString &res, resources) { + d->paintopServer->importResourceFile(res); + } + } + else if (resourceType == "gradients") { + Q_FOREACH (const QString &res, resources) { + d->gradientServer->importResourceFile(res); + } + } + else if (resourceType == "bundles") { + Q_FOREACH (const QString &res, resources) { + ResourceBundle *bundle = ResourceBundleServerProvider::instance()->resourceBundleServer()->createResource(res); + bundle->load(); + if (bundle->valid()) { + if (!bundle->install()) { + QMessageBox::warning(0, i18nc("@title:window", "Krita"), i18n("Could not install the resources for bundle %1.").arg(res)); + } + } + else { + QMessageBox::warning(0, i18nc("@title:window", "Krita"), i18n("Could not load bundle %1.").arg(res)); + } + + QFileInfo fi(res); + QString newFilename = ResourceBundleServerProvider::instance()->resourceBundleServer()->saveLocation() + fi.baseName() + bundle->defaultFileExtension(); + QFileInfo fileInfo(newFilename); + + int i = 1; + while (fileInfo.exists()) { + fileInfo.setFile(ResourceBundleServerProvider::instance()->resourceBundleServer()->saveLocation() + fi.baseName() + QString("%1").arg(i) + bundle->defaultFileExtension()); + i++; + } + bundle->setFilename(fileInfo.filePath()); + QFile::copy(res, newFilename); + ResourceBundleServerProvider::instance()->resourceBundleServer()->addResource(bundle, false); + } + } + else if (resourceType == "patterns") { + Q_FOREACH (const QString &res, resources) { + d->patternServer->importResourceFile(res); + } + } + else if (resourceType == "palettes") { + Q_FOREACH (const QString &res, resources) { + d->paletteServer->importResourceFile(res); + } + } + else if (resourceType == "workspaces") { + Q_FOREACH (const QString &res, resources) { + d->workspaceServer->importResourceFile(res); + } + } + else { + warnKrita << "Trying to add a resource of an undefined type"; + } + +} + +void ResourceManager::slotCreateBundle() +{ + DlgCreateBundle dlgCreateBundle; + if (dlgCreateBundle.exec() != QDialog::Accepted) { + return; + } + + QString bundlePath = dlgCreateBundle.saveLocation() + "/" + dlgCreateBundle.bundleName() + ".bundle"; + ResourceBundle* newBundle = new ResourceBundle(bundlePath); + + newBundle->addMeta("name", dlgCreateBundle.bundleName()); + newBundle->addMeta("author", dlgCreateBundle.authorName()); + newBundle->addMeta("email", dlgCreateBundle.email()); + newBundle->addMeta("license", dlgCreateBundle.license()); + newBundle->addMeta("website", dlgCreateBundle.website()); + newBundle->addMeta("description", dlgCreateBundle.description()); + + QStringList res = dlgCreateBundle.selectedBrushes(); + Q_FOREACH (const QString &r, res) { + KoResource *res = d->brushServer->resourceByFilename(r).data(); + newBundle->addResource("kis_brushes", res->filename(), d->brushServer->assignedTagsList(res), res->md5()); + } + + res = dlgCreateBundle.selectedGradients(); + Q_FOREACH (const QString &r, res) { + KoResource *res = d->gradientServer->resourceByFilename(r); + newBundle->addResource("ko_gradients", res->filename(), d->gradientServer->assignedTagsList(res), res->md5()); + } + + res = dlgCreateBundle.selectedPalettes(); + Q_FOREACH (const QString &r, res) { + KoResource *res = d->paletteServer->resourceByFilename(r); + newBundle->addResource("ko_palettes", res->filename(), d->paletteServer->assignedTagsList(res), res->md5()); + } + + res = dlgCreateBundle.selectedPatterns(); + Q_FOREACH (const QString &r, res) { + KoResource *res = d->patternServer->resourceByFilename(r); + newBundle->addResource("ko_patterns", res->filename(), d->patternServer->assignedTagsList(res), res->md5()); + } + + res = dlgCreateBundle.selectedPresets(); + Q_FOREACH (const QString &r, res) { + KisPaintOpPresetSP preset = d->paintopServer->resourceByFilename(r); + KoResource *res = preset.data(); + newBundle->addResource("kis_paintoppresets", res->filename(), d->paintopServer->assignedTagsList(res), res->md5()); + } + + res = dlgCreateBundle.selectedWorkspaces(); + Q_FOREACH (const QString &r, res) { + KoResource *res = d->workspaceServer->resourceByFilename(r); + newBundle->addResource("kis_workspaces", res->filename(), d->workspaceServer->assignedTagsList(res), res->md5()); + } + + newBundle->addMeta("fileName", bundlePath); + newBundle->addMeta("created", QDate::currentDate().toString("dd/MM/yyyy")); + + newBundle->setThumbnail(dlgCreateBundle.previewImage()); + + if (!newBundle->save()) { + QMessageBox::critical(m_view->mainWindow(), i18nc("@title:window", "Krita"), i18n("Could not create the new bundle.")); + } + + +} + +void ResourceManager::slotManageBundles() +{ + + + DlgBundleManager* dlg = new DlgBundleManager(m_view->actionManager()); + if (dlg->exec() != QDialog::Accepted) { + return; + } + +} + +void ResourceManager::loadBundles() +{ + d->loader = new KoResourceLoaderThread(ResourceBundleServerProvider::instance()->resourceBundleServer()); + connect(d->loader, SIGNAL(finished()), this, SLOT(bundlesLoaded())); + d->loader->start(); +} + +void ResourceManager::bundlesLoaded() +{ + delete d->loader; + d->loader = 0; + + Q_FOREACH (ResourceBundle *bundle, ResourceBundleServerProvider::instance()->resourceBundleServer()->resources()) { + if (!bundle->install()) { + warnKrita << "Could not install resources for bundle" << bundle->name(); + } + } + +} + + + +#include "resourcemanager.moc" diff --git a/krita/ui/resourcemanager/resourcemanager.h b/krita/ui/resourcemanager/resourcemanager.h new file mode 100644 index 0000000000..109d186362 --- /dev/null +++ b/krita/ui/resourcemanager/resourcemanager.h @@ -0,0 +1,64 @@ +/* + * resourcemanager.h -- Part of Krita + * + * Copyright (c) 2014 Boudewijn Rempt (boud@valdyas.org) + * + * 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 RESOURCEMANAGER_H +#define RESOURCEMANAGER_H + +#include +#include + +#include + +#include "resourcebundle.h" + +class ResourceBundleServerProvider { +public: + static ResourceBundleServerProvider *instance(); + ResourceBundleServerProvider(); + ~ResourceBundleServerProvider(); + + KoResourceServer *resourceBundleServer(); + +private: + + KoResourceServer *m_resourceBundleServer; + + Q_DISABLE_COPY(ResourceBundleServerProvider) + +}; + +class ResourceManager : public KisViewPlugin +{ + Q_OBJECT +public: + ResourceManager(QObject *parent, const QVariantList &); + virtual ~ResourceManager(); + +private Q_SLOTS: + void slotImport(); + void slotCreateBundle(); + void slotManageBundles(); + void loadBundles(); + void bundlesLoaded(); +private: + class Private; + Private *const d; +}; + +#endif // RESOURCEMANAGER_H diff --git a/krita/ui/resourcemanager/resourcemanager.rc b/krita/ui/resourcemanager/resourcemanager.rc new file mode 100644 index 0000000000..6d8116433c --- /dev/null +++ b/krita/ui/resourcemanager/resourcemanager.rc @@ -0,0 +1,13 @@ + + + + &Edit + &Resources + + + + + + + + diff --git a/krita/plugins/extensions/resourcemanager/tests/CMakeLists.txt b/krita/ui/resourcemanager/tests/CMakeLists.txt similarity index 100% rename from krita/plugins/extensions/resourcemanager/tests/CMakeLists.txt rename to krita/ui/resourcemanager/tests/CMakeLists.txt diff --git a/krita/plugins/extensions/resourcemanager/tests/ResourceBundleTest.cpp b/krita/ui/resourcemanager/tests/ResourceBundleTest.cpp similarity index 100% rename from krita/plugins/extensions/resourcemanager/tests/ResourceBundleTest.cpp rename to krita/ui/resourcemanager/tests/ResourceBundleTest.cpp diff --git a/krita/plugins/extensions/resourcemanager/tests/ResourceBundleTest.h b/krita/ui/resourcemanager/tests/ResourceBundleTest.h similarity index 100% rename from krita/plugins/extensions/resourcemanager/tests/ResourceBundleTest.h rename to krita/ui/resourcemanager/tests/ResourceBundleTest.h diff --git a/krita/plugins/extensions/resourcemanager/tests/data/thumb.png b/krita/ui/resourcemanager/tests/data/thumb.png similarity index 100% rename from krita/plugins/extensions/resourcemanager/tests/data/thumb.png rename to krita/ui/resourcemanager/tests/data/thumb.png diff --git a/krita/ui/resourcemanager/wdgdlgbundlemanager.ui b/krita/ui/resourcemanager/wdgdlgbundlemanager.ui new file mode 100644 index 0000000000..2204c9a8ab --- /dev/null +++ b/krita/ui/resourcemanager/wdgdlgbundlemanager.ui @@ -0,0 +1,539 @@ + + + WdgDlgBundleManager + + + + 0 + 0 + 767 + 577 + + + + + + + Qt::Vertical + + + false + + + + + 0 + 10 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + Active Bundles + + + + + + + 0 + 0 + + + + + 0 + 200 + + + + true + + + QAbstractItemView::ExtendedSelection + + + 1 + + + + + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + ... + + + + + + + ... + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + + + 0 + 0 + + + + Inactive Bundles + + + + + + + 0 + 0 + + + + + 0 + 200 + + + + QAbstractItemView::ExtendedSelection + + + 1 + + + + + + + + + + + + QLayout::SetMinimumSize + + + + + Import Bundle/Resource + + + + + + + + 0 + 0 + + + + Create Bundle + + + + + + + + 0 + 0 + + + + Delete Backup Files + + + + + + + + 0 + 0 + + + + Open Resource Folder + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + + 0 + 0 + + + + + 0 + 0 + + + + Selected Bundle + + + + + + + 0 + 0 + + + + Qt::Horizontal + + + false + + + + + 10 + 0 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + Bundle Name + + + Qt::AutoText + + + + + + + false + + + &Edit + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + 0 + 10 + + + + + 0 + 50 + + + + true + + + + + + + + + + + + 0 + 0 + + + + + 128 + 128 + + + + + 128 + 128 + + + + QFrame::Box + + + + + + Qt::AlignCenter + + + + + + + Qt::Vertical + + + + 20 + 0 + + + + + + + + + + 3 + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + Author: + + + false + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + + + + + License: + + + + + + + + + + + + + + Created: + + + + + + + Email: + + + + + + + Updated: + + + + + + + + + + + + + + Website: + + + + + + + + + + + + + + Qt::Vertical + + + + 20 + 0 + + + + + + + + + + + + + + 0 + 0 + + + + + 200 + 0 + + + + + 1 + + + + + + + + + + + + + + diff --git a/krita/ui/resourcemanager/wdgdlgcreatebundle.ui b/krita/ui/resourcemanager/wdgdlgcreatebundle.ui new file mode 100644 index 0000000000..fbb3b6285e --- /dev/null +++ b/krita/ui/resourcemanager/wdgdlgcreatebundle.ui @@ -0,0 +1,466 @@ + + + WdgDlgCreateBundle + + + + 0 + 0 + 894 + 394 + + + + + 0 + 0 + + + + New Bundle... + + + 1.000000000000000 + + + + 15 + + + 15 + + + 15 + + + 15 + + + 20 + + + + + + + + + Type: + + + + + + + + 0 + 0 + + + + QComboBox::InsertAlphabetically + + + + + + + + + + + + 0 + 0 + + + + + 0 + + + + + + 0 + 250 + + + + 2 + + + + + + + Available + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + ... + + + + + + + ... + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + 0 + 0 + + + + + + + + 0 + 250 + + + + 2 + + + + + + + Selected + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + + + + + QFormLayout::AllNonFixedFieldsGrow + + + Qt::AlignRight|Qt::AlignTop|Qt::AlignTrailing + + + + + Bundle Name: + + + + + + + + + + Description: + + + + + + + + 16777215 + 120 + + + + + + + + Author: + + + false + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + + + + + + + + + + + + Email: + + + + + + + + + + Website: + + + + + + + http:// + + + + + + + License: + + + + + + + + + + + + + + Save to: + + + + + + + 3 + + + + + + 0 + 0 + + + + + 0 + 20 + + + + + 16777215 + 25 + + + + QFrame::StyledPanel + + + QFrame::Sunken + + + TextLabel + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + 0 + + + + + + + ... + + + + + + + + + + 0 + 0 + + + + Icon: + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop + + + + + + + 2 + + + 0 + + + 0 + + + + + + 0 + 0 + + + + + 64 + 64 + + + + + 64 + 64 + + + + + 0 + 0 + + + + QFrame::StyledPanel + + + QFrame::Sunken + + + + + + + + + + + 0 + 0 + + + + ... + + + + + + + (256 x 256) + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + + diff --git a/krita/ui/tests/CMakeLists.txt b/krita/ui/tests/CMakeLists.txt index 87928ca90b..3a109b48fd 100644 --- a/krita/ui/tests/CMakeLists.txt +++ b/krita/ui/tests/CMakeLists.txt @@ -235,4 +235,11 @@ set(kis_animation_frame_cache_test_SRCS kis_animation_frame_cache_test.cpp ) kde4_add_unit_test(KisAnimationFrameCacheTest TESTNAME kritaui-animation_frame_cache_test ${kis_animation_frame_cache_test_SRCS}) target_link_libraries(KisAnimationFrameCacheTest kritaui kritaimage ${QT_QTTEST_LIBRARY}) +########### next target ############### + +set(ResourceBundleTest_SRCS ResourceBundleTest.cpp ../KisResourceBundle.cpp ../KisResourceBundleManifest.cpp) +kde4_add_unit_test(ResourceBundleTest TESTNAME krita-resourcemanager-ResourceBundleTest ${ResourceBundleTest_SRCS}) +target_link_libraries(ResourceBundleTest kritaui kritalibbrush kritalibpaintop Qt5::Test ) + + endif () diff --git a/krita/ui/tests/ResourceBundleTest.cpp b/krita/ui/tests/ResourceBundleTest.cpp new file mode 100644 index 0000000000..c7cce516f9 --- /dev/null +++ b/krita/ui/tests/ResourceBundleTest.cpp @@ -0,0 +1,179 @@ +/* + * 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 +#include +#include + +#include + +#include +#include "kis_workspace_resource.h" +#include "kis_paintop_preset.h" +#include "kis_brush_server.h" + +#include "ResourceBundleTest.h" +#include "KisResourceBundle.h" + +#include + +void ResourceBundleTest::testCreateBundle() +{ + QString testbundle = QString(FILES_DATA_DIR) + "/" + "testcreatebundle.bundle"; + KisResourceBundle bundle(testbundle); + QVERIFY(!bundle.isInstalled()); + QVERIFY(!bundle.valid()); + QVERIFY(bundle.getTagsList().isEmpty()); + QVERIFY(bundle.filename() == QString(FILES_DATA_DIR) + "/" + "testcreatebundle.bundle"); + QCOMPARE(bundle.shortFilename(), QString("testcreatebundle.bundle")); + QCOMPARE(bundle.name(), QString("testcreatebundle")); + QCOMPARE(bundle.defaultFileExtension(), QString(".bundle")); + + bundle.addMeta("author", "TestAuthor"); + QCOMPARE(bundle.getMeta("author"), QString("TestAuthor")); + + bundle.addMeta("email", "TestEmail"); + QCOMPARE(bundle.getMeta("email"), QString("TestEmail")); + + bundle.addMeta("license", "TestLicense"); + QCOMPARE(bundle.getMeta("license"), QString("TestLicense")); + + bundle.addMeta("website", "TestWebsite"); + QCOMPARE(bundle.getMeta("website"), QString("TestWebsite")); + + bundle.addMeta("created", "TestCreated"); + QCOMPARE(bundle.getMeta("created"), QString("TestCreated")); + + bundle.addMeta("updated", "TestUpdated"); + QCOMPARE(bundle.getMeta("updated"), QString("TestUpdated")); + + bundle.addMeta("description", "Test Description"); + QCOMPARE(bundle.getMeta("description"), QString("Test Description")); + + QVERIFY(bundle.getTagsList().isEmpty()); +} + + +void ResourceBundleTest::testLoadSave() +{ + KisResourceBundle bundle(QString(FILES_OUTPUT_DIR) + "/" + "testloadsavebundle.bundle"); + bundle.addMeta("author", "TestAuthor"); + bundle.addMeta("email", "TestEmail"); + bundle.addMeta("license", "TestLicense"); + bundle.addMeta("website", "TestWebsite"); + bundle.addMeta("created", "TestCreated"); + bundle.addMeta("updated", "TestUpdated"); + bundle.addMeta("description", "Test Description"); + bundle.setThumbnail(QString(FILES_DATA_DIR) + "/" + "thumb.png"); + + int tagCount = 0; + + KoResourceServer* gradientServer = KoResourceServerProvider::instance()->gradientServer(); + gradientServer->loadResources(gradientServer->fileNames()); + QVERIFY(gradientServer->resourceCount() > 0); + Q_FOREACH (KoAbstractGradient* gradient, gradientServer->resources()) { + if (gradient->name() == "Foreground to Transparent" || gradient->name() == "Foreground to Background") continue; + gradientServer->addTag(gradient, QString("testtag: %1").arg(tagCount)); + tagCount++; + bundle.addResource(gradientServer->type(), gradient->filename(), gradientServer->assignedTagsList(gradient), gradient->md5()); + } + + KoResourceServer* patternServer = KoResourceServerProvider::instance()->patternServer(); + QVERIFY(patternServer->resourceCount() > 0); + Q_FOREACH (KoPattern* pattern, patternServer->resources()) { + patternServer->addTag(pattern, QString("testtag: %1").arg(tagCount)); + tagCount++; + bundle.addResource(patternServer->type(), pattern->filename(), patternServer->assignedTagsList(pattern), pattern->md5()); + } + + KisBrushResourceServer* brushServer = KisBrushServer::instance()->brushServer(); + + QVERIFY(brushServer->resourceCount() > 0); + + Q_FOREACH (KisBrushSP brush, brushServer->resources()) { + brushServer->addTag(brush.data(), QString("testtag: %1").arg(tagCount)); + tagCount++; + bundle.addResource(brushServer->type(), brush->filename(), brushServer->assignedTagsList(brush.data()), brush->md5()); + } + + + KoResourceServer* paletteServer = KoResourceServerProvider::instance()->paletteServer(); + QVERIFY(paletteServer->resourceCount() > 0); + Q_FOREACH (KoColorSet* palette, paletteServer->resources()) { + paletteServer->addTag(palette, QString("testtag: %1").arg(tagCount)); + tagCount++; + bundle.addResource(paletteServer->type(), palette->filename(), paletteServer->assignedTagsList(palette), palette->md5()); + } + + + KoResourceServer< KisWorkspaceResource >* workspaceServer = KisResourceServerProvider::instance()->workspaceServer(); + QVERIFY(workspaceServer->resourceCount() > 0); + Q_FOREACH (KisWorkspaceResource* workspace, workspaceServer->resources()) { + workspaceServer->addTag(workspace, QString("testtag: %1").arg(tagCount)); + tagCount++; + bundle.addResource(workspaceServer->type(), workspace->filename(), workspaceServer->assignedTagsList(workspace), workspace->md5()); + } + + KisPaintOpPresetResourceServer * paintopServer = KisResourceServerProvider::instance()->paintOpPresetServer(); + QVERIFY(paintopServer->resourceCount() > 0); + Q_FOREACH (KisPaintOpPresetSP preset, paintopServer->resources()) { + paintopServer->addTag(preset.data(), QString("testtag: %1").arg(tagCount)); + tagCount++; + bundle.addResource(paintopServer->type(), preset->filename(), paintopServer->assignedTagsList(preset.data()), preset->md5()); + } + + //QCOMPARE(bundle.getTagsList(), ); + + bool res = bundle.save(); + QCOMPARE(bundle.getMeta("updated"), QDate::currentDate().toString("dd/MM/yyyy")); + QVERIFY(res); + + KisResourceBundle bundle2(QString(FILES_OUTPUT_DIR) + "/" + "testloadsavebundle.bundle"); + res = bundle2.load(); + + QVERIFY(res); + + // load sets installed to true + QVERIFY(bundle2.isInstalled()); + QVERIFY(bundle2.valid()); + //QCOMPARE(bundle2.getTagsList().size(), tagCount); + QVERIFY(bundle2.filename() == QString(FILES_OUTPUT_DIR) + "/" + "testloadsavebundle.bundle"); + QCOMPARE(bundle2.shortFilename(), QString("testloadsavebundle.bundle")); + QCOMPARE(bundle2.name(), QString("testloadsavebundle")); + QCOMPARE(bundle2.defaultFileExtension(), QString(".bundle")); + QCOMPARE(bundle2.getMeta("author"), QString("TestAuthor")); + QCOMPARE(bundle2.getMeta("email"), QString("TestEmail")); + QCOMPARE(bundle2.getMeta("license"), QString("TestLicense")); + QCOMPARE(bundle2.getMeta("website"), QString("TestWebsite")); + QCOMPARE(bundle2.getMeta("created"), QString("TestCreated")); + QCOMPARE(bundle2.getMeta("updated"), QDate::currentDate().toString("dd/MM/yyyy")); + QCOMPARE(bundle2.getMeta("description"), QString("Test Description")); + + QImage img = bundle2.image(); + QVERIFY(!img.isNull()); +} + +void ResourceBundleTest::testInstallUninstall() +{ + +} + + +QTEST_MAIN(ResourceBundleTest) + diff --git a/krita/ui/tests/ResourceBundleTest.h b/krita/ui/tests/ResourceBundleTest.h new file mode 100644 index 0000000000..b557072cdd --- /dev/null +++ b/krita/ui/tests/ResourceBundleTest.h @@ -0,0 +1,39 @@ +/* + * 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_TEST_H +#define KORESOURCEBUNDLE_TEST_H + +#include + +class ResourceBundleTest : public QObject +{ + Q_OBJECT + +private Q_SLOTS: + + void testCreateBundle(); + void testLoadSave(); + void testInstallUninstall(); + + +}; + +#endif // KORESOURCEBUNDLE_TEST_H + diff --git a/krita/ui/tests/data/thumb.png b/krita/ui/tests/data/thumb.png new file mode 100644 index 0000000000000000000000000000000000000000..3e63ab37fe4a1a7113f103bcbfc1a37f182299dc GIT binary patch literal 2175 zcmV-_2!QvAP)CJj4hnW~$WR=l1*wap)}bJ!lv=^6OOht7khCF55pi^I?a)QV z#VY;--NZj22!cB4ptFO52vXunTWAp-e8bKAaPB?#;{Za|P)%oG1~4nGol7O9)xw$- zdV+umL`)zotB$ph&8G2ZecU17_uII?7yefxMa@xxzz}&(wQQF>PtI()md|zaxLTHr zwn>hWqoz?)yZ7;5B5IYDW%kMT zaF9a^Nk~vo!zK*4h_hFi+3&tY+m}I=9+iI!+py^^gOgpTQL@Kl`7V zI{Iv)bAq@83_g3_`!o$Qp03Ui(Sae}^bZBpK004N}bILDGNiEOJ%2NnR z%`GSa04^y7&{Qj?JOBU&jY&j7R7l6AR)0)WR}_AfM|nU6fifD?K_#`ghLMIKnSweP z2=c?gQU%d0!iX`t1VrW^h(K-hQNl%$aKgM89~B;RX}L4vQkPZ zrNX0>vafxtE2SW0CoeDWop;~;&OP5b-@OE}HFI`$4hsu&a&mHUalz}EGiTP1ad&qo z{*N6vaA0|P*<`V$r6t_gk2!ku=;-LE(P-@L?LBtvSW;3_Vq)UJv zYHMq42fTgzwyCMf*VmUBK(3!uDwVQWth%~7s|UH-B#457f*m_{q@|_VI;WrpGrzfVq;?`CMImZ>U6sE=g)5#7uGQ_Fn|y+ zl%{D03BY2p*bc_c%`H7W9UMeNL<|oP+kOIhckkZK$;sIeCMG7PySv-^11NC+2M~*Z!G%#UVO(v6ViEsuaJDiYo=nW4KpPruXdj3>C{7Yd$ zTHOyzcEv_f)U8{$TwPtQ9(2r!6DK@9J-4x%=;&zu+;mm>mvM3N5fPHaPr~$b8r#6_ z)&V*w`lJNfq{Y6IPB6G6ckiiT6*cyB@{HoO{G#fK+Cq+ip8p{ ztH(zF5{rc~F|nTRq>m3bGxIc37Zw(7`nM3ES9YsGS(lfWXJ%%$wzdup4M9ijF|x9< z7y!`t`t|F^#>T-vyTU?4OeSMYjP!D0F&jJ$B+0wbeV!wS%?SW`zw z2c)F0ug?l*uC$q%8Pq!(8+lkh4+Su{~oSR+kKD2t`zM#RME1o@jW}6s^L{e5(2B8BCz^r~V4CE^>FVD}<@6DSx z{T5px*aXEJ8IdRzN_nTOzh0>)&tc^F*i+^=tyatBa6XOXqkEtiwn!-{DG*Z-eD&&8 z$90Sla6fhGluGpq{P*?r*L-!l>n1rmp&lF>5s8F;e!k1gMkW;xE*~1Wn|3o(oSU0# zCu`&5^Gaiczd&V?-mRW_Uq;WU}UcOd3{@3dq4nRB-&e6Rt^>c^ZdJ{-)|@W z+R_jZ5P%)TuD;PpYq+u9B?I2)YPH7b=#NQ~(yFz3Jq_-RMp~gz@`Zb$$_jG;^ z`}eObEf`l=ucn-%<5HkzMMXu{c{CagDRs$qPejr!x*a$uh4IdsJNKQ_*2s@3uLGnowNh3(o?7_Q0AV4+4 z^YZ0OR1}&KyZDtWSFr!r*Vp3{&NS;6w1yki*!Q!O;1SXAr54_ql5zr z*U#|oljgqNPE!v&=G!=iKJye1fAl71UoAG1xy&XW2!|?3=z^hxV;YZjh~&^%KHlc@ z^=DB|KbE?C@Z8KC=xJF!C6o1{XPE3+7$W++WlDus5JH7WD4M2dd8f8NVT(v$%ek(A z*cVFe3U)G+Y-G+41g+l5%aDT`Fr-w=bdZ*juHr%+FNC7XRMalBQ`gb|8n}b zh-gvqgyPtYSF4rG3FbeM7Z4nh34&q(aNi1sc{SFj_k1{NE!|#Barrb!Q^Iq!-4w`A z=XHY1qi7=$@&&y`fDO1}k-I#}jWTXM=xle|e*oRatl=45G?V}U002ovPDHLkV1jdU B^c4UA literal 0 HcmV?d00001 diff --git a/libs/widgets/KoResourceServerProvider.cpp b/libs/widgets/KoResourceServerProvider.cpp index e2d43b1914..b640e1be1b 100644 --- a/libs/widgets/KoResourceServerProvider.cpp +++ b/libs/widgets/KoResourceServerProvider.cpp @@ -153,6 +153,7 @@ void KoResourceLoaderThread::barrier() struct Q_DECL_HIDDEN KoResourceServerProvider::Private { + KoResourceServer* patternServer; KoResourceServer* gradientServer; KoResourceServer* paletteServer; -- GitLab