diff --git a/libs/flake/resources/KisSeExprScript.cpp b/libs/flake/resources/KisSeExprScript.cpp index c115a65652..c6ea6648b9 100644 --- a/libs/flake/resources/KisSeExprScript.cpp +++ b/libs/flake/resources/KisSeExprScript.cpp @@ -1,214 +1,242 @@ /* * This file is part of Krita * * Copyright (c) 2020 L. E. Segovia * * 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 #include #include #include #include #include #include #include #include "KisSeExprScript.h" struct KisSeExprScript::Private { QString script; QByteArray data; + bool dirtyPreset = false; }; KisSeExprScript::KisSeExprScript(const QString &filename) : KoResource(filename), d(new Private) { } KisSeExprScript::KisSeExprScript(const QImage &image, const QString &script, const QString &name, const QString &folderName) : KoResource(QString()), d(new Private) { setScript(script); setImage(image); setName(name); QFileInfo fileInfo(folderName + QDir::separator() + name + defaultFileExtension()); int i = 1; while (fileInfo.exists()) { fileInfo.setFile(folderName + QDir::separator() + name + QString::number(i) + defaultFileExtension()); i++; } setFilename(fileInfo.filePath()); } KisSeExprScript::~KisSeExprScript() { delete d; } bool KisSeExprScript::load() { QFile file(filename()); if (file.size() == 0) return false; if (!file.open(QIODevice::ReadOnly)) { warnFlake << "Can't open file " << filename(); return false; } bool result = loadFromDevice(&file); file.close(); return result; } bool KisSeExprScript::loadFromDevice(QIODevice *dev) { if (!dev->isOpen()) dev->open(QIODevice::ReadOnly); d->data = dev->readAll(); // TODO: test KIS_ASSERT_RECOVER_RETURN_VALUE(d->data.size() != 0, false); if (filename().isNull()) { warnFlake << "Cannot load SeExpr script" << name() << ", there is no filename set"; return false; } if (d->data.isNull()) { QFile file(filename()); if (file.size() == 0) { warnFlake << "Cannot load SeExpr script" << name() << "there is no data available"; return false; } file.open(QIODevice::ReadOnly); d->data = file.readAll(); file.close(); } QBuffer buf(&d->data); buf.open(QBuffer::ReadOnly); QScopedPointer store(KoStore::createStore(&buf, KoStore::Read, "application/x-krita-seexpr-script", KoStore::Zip)); if (!store || store->bad()) return false; bool storeOpened = store->open("script.se"); if (!storeOpened) { return false; } d->script = QString(store->read(store->size())); store->close(); if (store->open("preview.png")) { KoStoreDevice previewDev(store.data()); previewDev.open(QIODevice::ReadOnly); QImage preview = QImage(); preview.load(&previewDev, "PNG"); setImage(preview); (void)store->close(); } buf.close(); + QFileInfo fileinfo(filename()); + // The name of a SeExpr script is its basename + // KoResourceServer uses its filename -- amyspark + setName(fileinfo.baseName()); setValid(true); + setDirty(false); return true; } bool KisSeExprScript::save() { QFile file(filename()); if (!file.open(QIODevice::WriteOnly | QIODevice::Truncate)) { return false; } saveToDevice(&file); file.close(); return true; } bool KisSeExprScript::saveToDevice(QIODevice *dev) const { KoStore *store(KoStore::createStore(dev, KoStore::Write, "application/x-krita-seexpr-script", KoStore::Zip)); if (!store || store->bad()) return false; if (!store->open("script.se")) { return false; } KoStoreDevice storeDev(store); storeDev.open(QIODevice::WriteOnly); storeDev.write(d->script.toUtf8()); if (!store->close()) { return false; } if (!store->open("preview.png")) { return false; } KoStoreDevice previewDev(store); previewDev.open(QIODevice::WriteOnly); image().save(&previewDev, "PNG"); if (!store->close()) { return false; } return store->finalize(); } QString KisSeExprScript::defaultFileExtension() const { return QString(".kse"); } QString KisSeExprScript::script() const { return d->script; } void KisSeExprScript::setScript(const QString &script) { d->script = script; } + +KisSeExprScript* KisSeExprScript::clone() const +{ + KisSeExprScript* scr = new KisSeExprScript(filename()); + scr->setScript(script()); + scr->setImage(image()); + scr->setName(name()); + scr->setValid(valid()); + scr->setDirty(isDirty()); + + return scr; +} + +void KisSeExprScript::setDirty(bool value) +{ + d->dirtyPreset = value; +} + +bool KisSeExprScript::isDirty() const +{ + return d->dirtyPreset; +} diff --git a/libs/flake/resources/KisSeExprScript.h b/libs/flake/resources/KisSeExprScript.h index 100df0a24c..1ae6258aea 100644 --- a/libs/flake/resources/KisSeExprScript.h +++ b/libs/flake/resources/KisSeExprScript.h @@ -1,91 +1,103 @@ /* * This file is part of Krita * * Copyright (c) 2020 L. E. Segovia * * 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 KIS_SEEXPR_SCRIPT_H #define KIS_SEEXPR_SCRIPT_H -#include +#include +#include #include - #include +#include + +class KisSeExprScript; + +typedef KisSharedPtr KisSeExprScriptSP; /** * KoResource container for SeExpr scripts. */ -class KRITAFLAKE_EXPORT KisSeExprScript : public KoResource +class KRITAFLAKE_EXPORT KisSeExprScript : public KoResource, public KisShared { public: /** * Creates a new KisSeExprScript object using @p filename. No file is opened * in the constructor, you have to call load. * * @param filename the file name to save and load from. */ KisSeExprScript(const QString &filename); /** * Creates a new SeExpr script resource with the given @p image thumbnail, * @p script, @p name, @p and folder name. * * @param image the thumbnail of the texture this script will render * @param script the actual script's contents * @param name the name of the script * @param folderName the folder name */ KisSeExprScript(const QImage &image, const QString &script, const QString &name, const QString &folderName); ~KisSeExprScript(); public: /** * Load this resource. * @return true if loading the resource succeeded. */ bool load() override; bool loadFromDevice(QIODevice *dev) override; /** * Save this resource. * @return true if saving the resource succeeded. */ bool save() override; bool saveToDevice(QIODevice* dev) const override; QString defaultFileExtension() const override; /** * @brief script the actual script * @return a valid SeExpr script. It is guaranteed to be in UTF-8. */ QString script() const; -private: + /** + * @brief set SeExpr expression script + */ void setScript(const QString &script); + KisSeExprScript* clone() const; + + bool isDirty() const; + void setDirty(bool value); + private: struct Private; Private *const d; }; Q_DECLARE_METATYPE(KisSeExprScript*) -#endif // KO_SEEXPR_SCRIPT_H +#endif // KIS_SEEXPR_SCRIPT_H diff --git a/libs/ui/KisApplication.cpp b/libs/ui/KisApplication.cpp index 42520e97fd..0da4572978 100644 --- a/libs/ui/KisApplication.cpp +++ b/libs/ui/KisApplication.cpp @@ -1,980 +1,980 @@ /* * Copyright (C) 1998, 1999 Torben Weis * Copyright (C) 2012 Boudewijn Rempt * * 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 "KisApplication.h" #include #ifdef Q_OS_WIN #include #include #endif #ifdef Q_OS_MACOS #include "osx.h" #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "KoConfig.h" #include #include #include #include "thememanager.h" #include "KisPrintJob.h" #include "KisDocument.h" #include "KisMainWindow.h" #include "KisAutoSaveRecoveryDialog.h" #include "KisPart.h" #include #include "kis_md5_generator.h" #include "kis_splash_screen.h" #include "kis_config.h" #include "flake/kis_shape_selection.h" #include #include #include #include #include #include #include #include "kisexiv2/kis_exiv2.h" #include "KisApplicationArguments.h" #include #include "kis_action_registry.h" #include #include #include #include "kis_image_barrier_locker.h" #include "opengl/kis_opengl.h" #include "kis_spin_box_unit_manager.h" #include "kis_document_aware_spin_box_unit_manager.h" #include "KisViewManager.h" #include "kis_workspace_resource.h" #include #include #include #include "widgets/KisScreenColorPicker.h" #include "KisDlgInternalColorSelector.h" #include #include #include "kis_file_layer.h" #include "kis_group_layer.h" #include "kis_node_commands_adapter.h" #include namespace { const QTime appStartTime(QTime::currentTime()); } class KisApplication::Private { public: Private() {} QPointer splashScreen; KisAutoSaveRecoveryDialog *autosaveDialog {0}; QPointer mainWindow; // The first mainwindow we create on startup bool batchRun {false}; QVector earlyRemoteArguments; }; class KisApplication::ResetStarting { public: ResetStarting(KisSplashScreen *splash, int fileCount) : m_splash(splash) , m_fileCount(fileCount) { } ~ResetStarting() { if (m_splash) { m_splash->hide(); } } QPointer m_splash; int m_fileCount; }; KisApplication::KisApplication(const QString &key, int &argc, char **argv) : QtSingleApplication(key, argc, argv) , d(new Private) { #ifdef Q_OS_MACOS setMouseCoalescingEnabled(false); #endif QCoreApplication::addLibraryPath(QCoreApplication::applicationDirPath()); setApplicationDisplayName("Krita"); setApplicationName("krita"); // Note: Qt docs suggest we set this, but if we do, we get resource paths of the form of krita/krita, which is weird. // setOrganizationName("krita"); setOrganizationDomain("krita.org"); QString version = KritaVersionWrapper::versionString(true); setApplicationVersion(version); setWindowIcon(KisIconUtils::loadIcon("krita")); if (qgetenv("KRITA_NO_STYLE_OVERRIDE").isEmpty()) { QStringList styles = QStringList() << "breeze" << "fusion" << "plastique"; if (!styles.contains(style()->objectName().toLower())) { Q_FOREACH (const QString & style, styles) { if (!setStyle(style)) { qDebug() << "No" << style << "available."; } else { qDebug() << "Set style" << style; break; } } } } else { qDebug() << "Style override disabled, using" << style()->objectName(); } } #if defined(Q_OS_WIN) && defined(ENV32BIT) typedef BOOL (WINAPI *LPFN_ISWOW64PROCESS) (HANDLE, PBOOL); LPFN_ISWOW64PROCESS fnIsWow64Process; BOOL isWow64() { BOOL bIsWow64 = FALSE; //IsWow64Process is not available on all supported versions of Windows. //Use GetModuleHandle to get a handle to the DLL that contains the function //and GetProcAddress to get a pointer to the function if available. fnIsWow64Process = (LPFN_ISWOW64PROCESS) GetProcAddress( GetModuleHandle(TEXT("kernel32")),"IsWow64Process"); if(0 != fnIsWow64Process) { if (!fnIsWow64Process(GetCurrentProcess(),&bIsWow64)) { //handle error } } return bIsWow64; } #endif void KisApplication::initializeGlobals(const KisApplicationArguments &args) { int dpiX = args.dpiX(); int dpiY = args.dpiY(); if (dpiX > 0 && dpiY > 0) { KoDpi::setDPI(dpiX, dpiY); } } void KisApplication::addResourceTypes() { // qDebug() << "addResourceTypes();"; // All Krita's resource types KoResourcePaths::addResourceType("markers", "data", "/styles/"); KoResourcePaths::addResourceType("kis_pics", "data", "/pics/"); KoResourcePaths::addResourceType("kis_images", "data", "/images/"); KoResourcePaths::addResourceType("metadata_schema", "data", "/metadata/schemas/"); KoResourcePaths::addResourceType("kis_brushes", "data", "/brushes/"); KoResourcePaths::addResourceType("kis_taskset", "data", "/taskset/"); KoResourcePaths::addResourceType("kis_taskset", "data", "/taskset/"); KoResourcePaths::addResourceType("gmic_definitions", "data", "/gmic/"); KoResourcePaths::addResourceType("kis_resourcebundles", "data", "/bundles/"); KoResourcePaths::addResourceType("kis_defaultpresets", "data", "/defaultpresets/"); KoResourcePaths::addResourceType("kis_paintoppresets", "data", "/paintoppresets/"); KoResourcePaths::addResourceType("kis_workspaces", "data", "/workspaces/"); KoResourcePaths::addResourceType("kis_windowlayouts", "data", "/windowlayouts/"); KoResourcePaths::addResourceType("kis_sessions", "data", "/sessions/"); KoResourcePaths::addResourceType("psd_layer_style_collections", "data", "/asl"); KoResourcePaths::addResourceType("ko_patterns", "data", "/patterns/", true); KoResourcePaths::addResourceType("ko_gradients", "data", "/gradients/"); KoResourcePaths::addResourceType("ko_gradients", "data", "/gradients/", true); KoResourcePaths::addResourceType("ko_palettes", "data", "/palettes/", true); KoResourcePaths::addResourceType("kis_shortcuts", "data", "/shortcuts/"); KoResourcePaths::addResourceType("kis_actions", "data", "/actions"); KoResourcePaths::addResourceType("kis_actions", "data", "/pykrita"); KoResourcePaths::addResourceType("icc_profiles", "data", "/color/icc"); KoResourcePaths::addResourceType("icc_profiles", "data", "/profiles/"); KoResourcePaths::addResourceType("ko_effects", "data", "/effects/"); KoResourcePaths::addResourceType("tags", "data", "/tags/"); KoResourcePaths::addResourceType("templates", "data", "/templates"); KoResourcePaths::addResourceType("pythonscripts", "data", "/pykrita"); KoResourcePaths::addResourceType("symbols", "data", "/symbols"); KoResourcePaths::addResourceType("preset_icons", "data", "/preset_icons"); KoResourcePaths::addResourceType("ko_gamutmasks", "data", "/gamutmasks/", true); #if defined HAVE_SEEXPR - KoResourcePaths::addResourceType("kis_seexpr_scripts", "data", "/seexpr/", true); + KoResourcePaths::addResourceType("kis_seexpr_scripts", "data", "/seexpr_scripts/", true); #endif // // Extra directories to look for create resources. (Does anyone actually use that anymore?) // KoResourcePaths::addResourceDir("ko_gradients", "/usr/share/create/gradients/gimp"); // KoResourcePaths::addResourceDir("ko_gradients", QDir::homePath() + QString("/.create/gradients/gimp")); // KoResourcePaths::addResourceDir("ko_patterns", "/usr/share/create/patterns/gimp"); // KoResourcePaths::addResourceDir("ko_patterns", QDir::homePath() + QString("/.create/patterns/gimp")); // KoResourcePaths::addResourceDir("kis_brushes", "/usr/share/create/brushes/gimp"); // KoResourcePaths::addResourceDir("kis_brushes", QDir::homePath() + QString("/.create/brushes/gimp")); // KoResourcePaths::addResourceDir("ko_palettes", "/usr/share/create/swatches"); // KoResourcePaths::addResourceDir("ko_palettes", QDir::homePath() + QString("/.create/swatches")); // Make directories for all resources we can save, and tags QDir d; d.mkpath(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/tags/"); d.mkpath(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/asl/"); d.mkpath(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/bundles/"); d.mkpath(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/brushes/"); d.mkpath(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/gradients/"); d.mkpath(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/paintoppresets/"); d.mkpath(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/palettes/"); d.mkpath(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/patterns/"); d.mkpath(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/taskset/"); d.mkpath(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/workspaces/"); d.mkpath(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/input/"); d.mkpath(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/pykrita/"); d.mkpath(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/symbols/"); d.mkpath(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/color-schemes/"); d.mkpath(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/preset_icons/"); d.mkpath(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/preset_icons/tool_icons/"); d.mkpath(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/preset_icons/emblem_icons/"); d.mkpath(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/gamutmasks/"); #if defined HAVE_SEEXPR - d.mkpath(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/seexpr/"); + d.mkpath(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/seexpr_scripts/"); #endif } void KisApplication::loadResources() { // qDebug() << "loadResources();"; setSplashScreenLoadingText(i18n("Loading Resources...")); processEvents(); KoResourceServerProvider::instance(); setSplashScreenLoadingText(i18n("Loading Brush Presets...")); processEvents(); KisResourceServerProvider::instance(); setSplashScreenLoadingText(i18n("Loading Brushes...")); processEvents(); KisBrushServer::instance()->brushServer(); setSplashScreenLoadingText(i18n("Loading Bundles...")); processEvents(); KisResourceBundleServerProvider::instance(); } void KisApplication::loadResourceTags() { // qDebug() << "loadResourceTags()"; KoResourceServerProvider::instance()->patternServer()->loadTags(); KoResourceServerProvider::instance()->gradientServer()->loadTags(); KoResourceServerProvider::instance()->paletteServer()->loadTags(); KoResourceServerProvider::instance()->svgSymbolCollectionServer()->loadTags(); KisBrushServer::instance()->brushServer()->loadTags(); KisResourceServerProvider::instance()->workspaceServer()->loadTags(); KisResourceServerProvider::instance()->layerStyleCollectionServer()->loadTags(); KisResourceBundleServerProvider::instance()->resourceBundleServer()->loadTags(); KisResourceServerProvider::instance()->paintOpPresetServer()->loadTags(); KisResourceServerProvider::instance()->paintOpPresetServer()->clearOldSystemTags(); } void KisApplication::loadPlugins() { // qDebug() << "loadPlugins();"; KoShapeRegistry* r = KoShapeRegistry::instance(); r->add(new KisShapeSelectionFactory()); KoColorSpaceRegistry::instance(); KisActionRegistry::instance(); KisFilterRegistry::instance(); KisGeneratorRegistry::instance(); KisPaintOpRegistry::instance(); KoToolRegistry::instance(); KoDockRegistry::instance(); } void KisApplication::loadGuiPlugins() { // XXX_EXIV: make the exiv io backends real plugins setSplashScreenLoadingText(i18n("Loading Plugins Exiv/IO...")); processEvents(); // qDebug() << "loading exiv2"; KisExiv2::initialize(); } bool KisApplication::start(const KisApplicationArguments &args) { KisConfig cfg(false); #if defined(Q_OS_WIN) #ifdef ENV32BIT if (isWow64() && !cfg.readEntry("WarnedAbout32Bits", false)) { QMessageBox::information(0, i18nc("@title:window", "Krita: Warning"), i18n("You are running a 32 bits build on a 64 bits Windows.\n" "This is not recommended.\n" "Please download and install the x64 build instead.")); cfg.writeEntry("WarnedAbout32Bits", true); } #endif #endif QString opengl = cfg.canvasState(); if (opengl == "OPENGL_NOT_TRIED" ) { cfg.setCanvasState("TRY_OPENGL"); } else if (opengl != "OPENGL_SUCCESS" && opengl != "TRY_OPENGL") { cfg.setCanvasState("OPENGL_FAILED"); } setSplashScreenLoadingText(i18n("Initializing Globals")); processEvents(); initializeGlobals(args); const bool doNewImage = args.doNewImage(); const bool doTemplate = args.doTemplate(); const bool exportAs = args.exportAs(); const bool exportSequence = args.exportSequence(); const QString exportFileName = args.exportFileName(); d->batchRun = (exportAs || exportSequence || !exportFileName.isEmpty()); const bool needsMainWindow = (!exportAs && !exportSequence); // only show the mainWindow when no command-line mode option is passed bool showmainWindow = (!exportAs && !exportSequence); // would be !batchRun; const bool showSplashScreen = !d->batchRun && qEnvironmentVariableIsEmpty("NOSPLASH"); if (showSplashScreen && d->splashScreen) { d->splashScreen->show(); d->splashScreen->repaint(); processEvents(); } KoHashGeneratorProvider::instance()->setGenerator("MD5", new KisMD5Generator()); KConfigGroup group(KSharedConfig::openConfig(), "theme"); Digikam::ThemeManager themeManager; themeManager.setCurrentTheme(group.readEntry("Theme", "Krita dark")); ResetStarting resetStarting(d->splashScreen, args.filenames().count()); // remove the splash when done Q_UNUSED(resetStarting); // Make sure we can save resources and tags setSplashScreenLoadingText(i18n("Adding resource types")); processEvents(); addResourceTypes(); // Load the plugins loadPlugins(); // Load all resources loadResources(); // Load all the tags loadResourceTags(); // Load the gui plugins loadGuiPlugins(); KisPart *kisPart = KisPart::instance(); if (needsMainWindow) { // show a mainWindow asap, if we want that setSplashScreenLoadingText(i18n("Loading Main Window...")); processEvents(); bool sessionNeeded = true; auto sessionMode = cfg.sessionOnStartup(); if (!args.session().isEmpty()) { sessionNeeded = !kisPart->restoreSession(args.session()); } else if (sessionMode == KisConfig::SOS_ShowSessionManager) { showmainWindow = false; sessionNeeded = false; kisPart->showSessionManager(); } else if (sessionMode == KisConfig::SOS_PreviousSession) { KConfigGroup sessionCfg = KSharedConfig::openConfig()->group("session"); const QString &sessionName = sessionCfg.readEntry("previousSession"); sessionNeeded = !kisPart->restoreSession(sessionName); } if (sessionNeeded) { kisPart->startBlankSession(); } if (!args.windowLayout().isEmpty()) { KoResourceServer * rserver = KisResourceServerProvider::instance()->windowLayoutServer(); KisWindowLayoutResource* windowLayout = rserver->resourceByName(args.windowLayout()); if (windowLayout) { windowLayout->applyLayout(); } } if (showmainWindow) { d->mainWindow = kisPart->currentMainwindow(); if (!args.workspace().isEmpty()) { KoResourceServer * rserver = KisResourceServerProvider::instance()->workspaceServer(); KisWorkspaceResource* workspace = rserver->resourceByName(args.workspace()); if (workspace) { d->mainWindow->restoreWorkspace(workspace); } } if (args.canvasOnly()) { d->mainWindow->viewManager()->switchCanvasOnly(true); } if (args.fullScreen()) { d->mainWindow->showFullScreen(); } } else { d->mainWindow = kisPart->createMainWindow(); } } short int numberOfOpenDocuments = 0; // number of documents open // Check for autosave files that can be restored, if we're not running a batchrun (test) if (!d->batchRun) { checkAutosaveFiles(); } setSplashScreenLoadingText(QString()); // done loading, so clear out label processEvents(); //configure the unit manager KisSpinBoxUnitManagerFactory::setDefaultUnitManagerBuilder(new KisDocumentAwareSpinBoxUnitManagerBuilder()); connect(this, &KisApplication::aboutToQuit, &KisSpinBoxUnitManagerFactory::clearUnitManagerBuilder); //ensure the builder is destroyed when the application leave. //the new syntax slot syntax allow to connect to a non q_object static method. // Create a new image, if needed if (doNewImage) { KisDocument *doc = args.createDocumentFromArguments(); if (doc) { kisPart->addDocument(doc); d->mainWindow->addViewAndNotifyLoadingCompleted(doc); } } // Get the command line arguments which we have to parse int argsCount = args.filenames().count(); if (argsCount > 0) { // Loop through arguments for (int argNumber = 0; argNumber < argsCount; argNumber++) { QString fileName = args.filenames().at(argNumber); // are we just trying to open a template? if (doTemplate) { // called in mix with batch options? ignore and silently skip if (d->batchRun) { continue; } if (createNewDocFromTemplate(fileName, d->mainWindow)) { ++numberOfOpenDocuments; } // now try to load } else { if (exportAs) { QString outputMimetype = KisMimeDatabase::mimeTypeForFile(exportFileName, false); if (outputMimetype == "application/octetstream") { dbgKrita << i18n("Mimetype not found, try using the -mimetype option") << endl; return false; } KisDocument *doc = kisPart->createDocument(); doc->setFileBatchMode(d->batchRun); bool result = doc->openUrl(QUrl::fromLocalFile(fileName)); if (!result) { errKrita << "Could not load " << fileName << ":" << doc->errorMessage(); QTimer::singleShot(0, this, SLOT(quit())); return false; } if (exportFileName.isEmpty()) { errKrita << "Export destination is not specified for" << fileName << "Please specify export destination with --export-filename option"; QTimer::singleShot(0, this, SLOT(quit())); return false; } qApp->processEvents(); // For vector layers to be updated doc->setFileBatchMode(true); if (!doc->exportDocumentSync(QUrl::fromLocalFile(exportFileName), outputMimetype.toLatin1())) { errKrita << "Could not export " << fileName << "to" << exportFileName << ":" << doc->errorMessage(); } QTimer::singleShot(0, this, SLOT(quit())); return true; } else if (exportSequence) { KisDocument *doc = kisPart->createDocument(); doc->setFileBatchMode(d->batchRun); doc->openUrl(QUrl::fromLocalFile(fileName)); qApp->processEvents(); // For vector layers to be updated if (!doc->image()->animationInterface()->hasAnimation()) { errKrita << "This file has no animation." << endl; QTimer::singleShot(0, this, SLOT(quit())); return false; } doc->setFileBatchMode(true); int sequenceStart = 0; KisAsyncAnimationFramesSaveDialog exporter(doc->image(), doc->image()->animationInterface()->fullClipRange(), exportFileName, sequenceStart, false, 0); exporter.setBatchMode(d->batchRun); KisAsyncAnimationFramesSaveDialog::Result result = exporter.regenerateRange(0); if (result == KisAsyncAnimationFramesSaveDialog::RenderFailed) { errKrita << i18n("Failed to render animation frames!") << endl; } QTimer::singleShot(0, this, SLOT(quit())); return true; } else if (d->mainWindow) { if (fileName.endsWith(".bundle")) { d->mainWindow->installBundle(fileName); } else { KisMainWindow::OpenFlags flags = d->batchRun ? KisMainWindow::BatchMode : KisMainWindow::None; if (d->mainWindow->openDocument(QUrl::fromLocalFile(fileName), flags)) { // Normal case, success numberOfOpenDocuments++; } } } } } } //add an image as file-layer if (!args.fileLayer().isEmpty()){ if (d->mainWindow->viewManager()->image()){ KisFileLayer *fileLayer = new KisFileLayer(d->mainWindow->viewManager()->image(), "", args.fileLayer(), KisFileLayer::None, d->mainWindow->viewManager()->image()->nextLayerName(), OPACITY_OPAQUE_U8); QFileInfo fi(fileLayer->path()); if (fi.exists()){ KisNodeCommandsAdapter adapter(d->mainWindow->viewManager()); adapter.addNode(fileLayer, d->mainWindow->viewManager()->activeNode()->parent(), d->mainWindow->viewManager()->activeNode()); } else{ QMessageBox::warning(nullptr, i18nc("@title:window", "Krita:Warning"), i18n("Cannot add %1 as a file layer: the file does not exist.", fileLayer->path())); } } else if (this->isRunning()){ QMessageBox::warning(nullptr, i18nc("@title:window", "Krita:Warning"), i18n("Cannot add the file layer: no document is open.\n\n" "You can create a new document using the --new-image option, or you can open an existing file.\n\n" "If you instead want to add the file layer to a document in an already running instance of Krita, check the \"Allow only one instance of Krita\" checkbox in the settings (Settings -> General -> Window).")); } else { QMessageBox::warning(nullptr, i18nc("@title:window", "Krita: Warning"), i18n("Cannot add the file layer: no document is open.\n" "You can either create a new file using the --new-image option, or you can open an existing file.")); } } // fixes BUG:369308 - Krita crashing on splash screen when loading. // trying to open a file before Krita has loaded can cause it to hang and crash if (d->splashScreen) { d->splashScreen->displayLinks(true); d->splashScreen->displayRecentFiles(true); } Q_FOREACH(const QByteArray &message, d->earlyRemoteArguments) { executeRemoteArguments(message, d->mainWindow); } KisUsageLogger::writeSysInfo(KisUsageLogger::screenInformation()); // not calling this before since the program will quit there. return true; } KisApplication::~KisApplication() { } void KisApplication::setSplashScreen(QWidget *splashScreen) { d->splashScreen = qobject_cast(splashScreen); } void KisApplication::setSplashScreenLoadingText(QString textToLoad) { if (d->splashScreen) { //d->splashScreen->loadingLabel->setText(textToLoad); d->splashScreen->setLoadingText(textToLoad); d->splashScreen->repaint(); } } void KisApplication::hideSplashScreen() { if (d->splashScreen) { // hide the splashscreen to see the dialog d->splashScreen->hide(); } } bool KisApplication::notify(QObject *receiver, QEvent *event) { try { return QApplication::notify(receiver, event); } catch (std::exception &e) { qWarning("Error %s sending event %i to object %s", e.what(), event->type(), qPrintable(receiver->objectName())); } catch (...) { qWarning("Error sending event %i to object %s", event->type(), qPrintable(receiver->objectName())); } return false; } void KisApplication::executeRemoteArguments(QByteArray message, KisMainWindow *mainWindow) { KisApplicationArguments args = KisApplicationArguments::deserialize(message); const bool doTemplate = args.doTemplate(); const bool doNewImage = args.doNewImage(); const int argsCount = args.filenames().count(); bool documentCreated = false; // Create a new image, if needed if (doNewImage) { KisDocument *doc = args.createDocumentFromArguments(); if (doc) { KisPart::instance()->addDocument(doc); d->mainWindow->addViewAndNotifyLoadingCompleted(doc); } } if (argsCount > 0) { // Loop through arguments for (int argNumber = 0; argNumber < argsCount; ++argNumber) { QString filename = args.filenames().at(argNumber); // are we just trying to open a template? if (doTemplate) { documentCreated |= createNewDocFromTemplate(filename, mainWindow); } else if (QFile(filename).exists()) { KisMainWindow::OpenFlags flags = d->batchRun ? KisMainWindow::BatchMode : KisMainWindow::None; documentCreated |= mainWindow->openDocument(QUrl::fromLocalFile(filename), flags); } } } //add an image as file-layer if called in another process and singleApplication is enabled if (!args.fileLayer().isEmpty()){ if (argsCount > 0 && !documentCreated){ //arg was passed but document was not created so don't add the file layer. QMessageBox::warning(mainWindow, i18nc("@title:window", "Krita:Warning"), i18n("Couldn't open file %1",args.filenames().at(argsCount - 1))); } else if (mainWindow->viewManager()->image()){ KisFileLayer *fileLayer = new KisFileLayer(mainWindow->viewManager()->image(), "", args.fileLayer(), KisFileLayer::None, mainWindow->viewManager()->image()->nextLayerName(), OPACITY_OPAQUE_U8); QFileInfo fi(fileLayer->path()); if (fi.exists()){ KisNodeCommandsAdapter adapter(d->mainWindow->viewManager()); adapter.addNode(fileLayer, d->mainWindow->viewManager()->activeNode()->parent(), d->mainWindow->viewManager()->activeNode()); } else{ QMessageBox::warning(mainWindow, i18nc("@title:window", "Krita:Warning"), i18n("Cannot add %1 as a file layer: the file does not exist.", fileLayer->path())); } } else { QMessageBox::warning(mainWindow, i18nc("@title:window", "Krita:Warning"), i18n("Cannot add the file layer: no document is open.")); } } } void KisApplication::remoteArguments(QByteArray message, QObject *socket) { Q_UNUSED(socket); // check if we have any mainwindow KisMainWindow *mw = qobject_cast(qApp->activeWindow()); if (!mw && KisPart::instance()->mainWindows().size() > 0) { mw = KisPart::instance()->mainWindows().first(); } if (!mw) { d->earlyRemoteArguments << message; return; } executeRemoteArguments(message, mw); } void KisApplication::fileOpenRequested(const QString &url) { KisMainWindow *mainWindow = KisPart::instance()->mainWindows().first(); if (mainWindow) { KisMainWindow::OpenFlags flags = d->batchRun ? KisMainWindow::BatchMode : KisMainWindow::None; mainWindow->openDocument(QUrl::fromLocalFile(url), flags); } } void KisApplication::checkAutosaveFiles() { if (d->batchRun) return; #ifdef Q_OS_WIN QDir dir = QDir::temp(); #else QDir dir = QDir::home(); #endif // Check for autosave files from a previous run. There can be several, and // we want to offer a restore for every one. Including a nice thumbnail! // Hidden autosave files QStringList filters = QStringList() << QString(".krita-*-*-autosave.kra"); // all autosave files for our application QStringList autosaveFiles = dir.entryList(filters, QDir::Files | QDir::Hidden); // Visible autosave files filters = QStringList() << QString("krita-*-*-autosave.kra"); autosaveFiles += dir.entryList(filters, QDir::Files); // Allow the user to make their selection if (autosaveFiles.size() > 0) { if (d->splashScreen) { // hide the splashscreen to see the dialog d->splashScreen->hide(); } d->autosaveDialog = new KisAutoSaveRecoveryDialog(autosaveFiles, activeWindow()); QDialog::DialogCode result = (QDialog::DialogCode) d->autosaveDialog->exec(); if (result == QDialog::Accepted) { QStringList filesToRecover = d->autosaveDialog->recoverableFiles(); Q_FOREACH (const QString &autosaveFile, autosaveFiles) { if (!filesToRecover.contains(autosaveFile)) { KisUsageLogger::log(QString("Removing autosave file %1").arg(dir.absolutePath() + "/" + autosaveFile)); QFile::remove(dir.absolutePath() + "/" + autosaveFile); } } autosaveFiles = filesToRecover; } else { autosaveFiles.clear(); } if (autosaveFiles.size() > 0) { QList autosaveUrls; Q_FOREACH (const QString &autoSaveFile, autosaveFiles) { const QUrl url = QUrl::fromLocalFile(dir.absolutePath() + QLatin1Char('/') + autoSaveFile); autosaveUrls << url; } if (d->mainWindow) { Q_FOREACH (const QUrl &url, autosaveUrls) { KisMainWindow::OpenFlags flags = d->batchRun ? KisMainWindow::BatchMode : KisMainWindow::None; d->mainWindow->openDocument(url, flags | KisMainWindow::RecoveryFile); } } } // cleanup delete d->autosaveDialog; d->autosaveDialog = nullptr; } } bool KisApplication::createNewDocFromTemplate(const QString &fileName, KisMainWindow *mainWindow) { QString templatePath; const QUrl templateUrl = QUrl::fromLocalFile(fileName); if (QFile::exists(fileName)) { templatePath = templateUrl.toLocalFile(); dbgUI << "using full path..."; } else { QString desktopName(fileName); const QString templatesResourcePath = QStringLiteral("templates/"); QStringList paths = KoResourcePaths::findAllResources("data", templatesResourcePath + "*/" + desktopName); if (paths.isEmpty()) { paths = KoResourcePaths::findAllResources("data", templatesResourcePath + desktopName); } if (paths.isEmpty()) { QMessageBox::critical(0, i18nc("@title:window", "Krita"), i18n("No template found for: %1", desktopName)); } else if (paths.count() > 1) { QMessageBox::critical(0, i18nc("@title:window", "Krita"), i18n("Too many templates found for: %1", desktopName)); } else { templatePath = paths.at(0); } } if (!templatePath.isEmpty()) { QUrl templateBase; templateBase.setPath(templatePath); KDesktopFile templateInfo(templatePath); QString templateName = templateInfo.readUrl(); QUrl templateURL; templateURL.setPath(templateBase.adjusted(QUrl::RemoveFilename|QUrl::StripTrailingSlash).path() + '/' + templateName); if (templateURL.scheme().isEmpty()) { templateURL.setScheme("file"); } KisMainWindow::OpenFlags batchFlags = d->batchRun ? KisMainWindow::BatchMode : KisMainWindow::None; if (mainWindow->openDocument(templateURL, KisMainWindow::Import | batchFlags)) { dbgUI << "Template loaded..."; return true; } else { QMessageBox::critical(0, i18nc("@title:window", "Krita"), i18n("Template %1 failed to load.", templateURL.toDisplayString())); } } return false; } void KisApplication::resetConfig() { KIS_ASSERT_RECOVER_RETURN(qApp->thread() == QThread::currentThread()); KSharedConfigPtr config = KSharedConfig::openConfig(); config->markAsClean(); // find user settings file const QString configPath = QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation); QString kritarcPath = configPath + QStringLiteral("/kritarc"); QFile kritarcFile(kritarcPath); if (kritarcFile.exists()) { if (kritarcFile.open(QFile::ReadWrite)) { QString backupKritarcPath = kritarcPath + QStringLiteral(".backup"); QFile backupKritarcFile(backupKritarcPath); if (backupKritarcFile.exists()) { backupKritarcFile.remove(); } QMessageBox::information(0, i18nc("@title:window", "Krita"), i18n("Krita configurations reset!\n\n" "Backup file was created at: %1\n\n" "Restart Krita for changes to take effect.", backupKritarcPath), QMessageBox::Ok, QMessageBox::Ok); // clear file kritarcFile.rename(backupKritarcPath); kritarcFile.close(); } else { QMessageBox::warning(0, i18nc("@title:window", "Krita"), i18n("Failed to clear %1\n\n" "Please make sure no other program is using the file and try again.", kritarcPath), QMessageBox::Ok, QMessageBox::Ok); } } // reload from disk; with the user file settings cleared, // this should load any default configuration files shipping with the program config->reparseConfiguration(); config->sync(); // Restore to default workspace KConfigGroup cfg = KSharedConfig::openConfig()->group("MainWindow"); QString currentWorkspace = cfg.readEntry("CurrentWorkspace", "Default"); KoResourceServer *rserver = KisResourceServerProvider::instance()->workspaceServer(); KisWorkspaceResource *workspace = rserver->resourceByName(currentWorkspace); if (workspace) { d->mainWindow->restoreWorkspace(workspace); } } void KisApplication::askresetConfig() { bool ok = QMessageBox::question(0, i18nc("@title:window", "Krita"), i18n("Do you want to clear the settings file?"), QMessageBox::Yes | QMessageBox::No, QMessageBox::No) == QMessageBox::Yes; if (ok) { resetConfig(); } } diff --git a/libs/ui/KisResourceBundle.cpp b/libs/ui/KisResourceBundle.cpp index 4aa4e950d5..ac3accd8a7 100644 --- a/libs/ui/KisResourceBundle.cpp +++ b/libs/ui/KisResourceBundle.cpp @@ -1,1193 +1,1200 @@ /* * 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 KisResourceBundle::KisResourceBundle(QString const& fileName) : KoResource(fileName), m_bundleVersion("1") { setName(QFileInfo(fileName).completeBaseName()); m_metadata["generator"] = "Krita (" + KritaVersionWrapper::versionString(true) + ")"; } KisResourceBundle::~KisResourceBundle() { } QString KisResourceBundle::defaultFileExtension() const { return QString(".bundle"); } bool KisResourceBundle::load() { if (filename().isEmpty()) return false; QScopedPointer resourceStore(KoStore::createStore(filename(), KoStore::Read, "application/x-krita-resourcebundle", KoStore::Zip)); if (!resourceStore || resourceStore->bad()) { warnKrita << "Could not open store on bundle" << filename(); m_installed = false; setValid(false); return false; } else { m_metadata.clear(); bool toRecreate = false; if (resourceStore->open("META-INF/manifest.xml")) { if (!m_manifest.load(resourceStore->device())) { warnKrita << "Could not open manifest for bundle" << filename(); return false; } resourceStore->close(); Q_FOREACH (KisResourceBundleManifest::ResourceReference ref, m_manifest.files()) { if (!resourceStore->open(ref.resourcePath)) { warnKrita << "Bundle is broken. File" << ref.resourcePath << "is missing"; toRecreate = true; } else { resourceStore->close(); } } if(toRecreate) { warnKrita << "Due to missing files and wrong entries in the manifest, " << filename() << " will be recreated."; } } else { warnKrita << "Could not load META-INF/manifest.xml"; return false; } bool versionFound = false; if (resourceStore->open("meta.xml")) { KoXmlDocument doc; if (!doc.setContent(resourceStore->device())) { warnKrita << "Could not parse meta.xml for" << filename(); return false; } // First find the manifest:manifest node. KoXmlNode n = doc.firstChild(); for (; !n.isNull(); n = n.nextSibling()) { if (!n.isElement()) { continue; } if (n.toElement().tagName() == "meta:meta") { break; } } if (n.isNull()) { warnKrita << "Could not find manifest node for bundle" << filename(); return false; } const KoXmlElement metaElement = n.toElement(); for (n = metaElement.firstChild(); !n.isNull(); n = n.nextSibling()) { if (n.isElement()) { KoXmlElement e = n.toElement(); if (e.tagName() == "meta:generator") { m_metadata.insert("generator", e.firstChild().toText().data()); } else if (e.tagName() == "dc:author") { m_metadata.insert("author", e.firstChild().toText().data()); } else if (e.tagName() == "dc:title") { m_metadata.insert("title", e.firstChild().toText().data()); } else if (e.tagName() == "dc:description") { m_metadata.insert("description", e.firstChild().toText().data()); } else if (e.tagName() == "meta:initial-creator") { m_metadata.insert("author", e.firstChild().toText().data()); } else if (e.tagName() == "dc:creator") { m_metadata.insert("author", e.firstChild().toText().data()); } else if (e.tagName() == "meta:creation-date") { m_metadata.insert("created", e.firstChild().toText().data()); } else if (e.tagName() == "meta:dc-date") { m_metadata.insert("updated", e.firstChild().toText().data()); } else if (e.tagName() == "meta:meta-userdefined") { if (e.attribute("meta:name") == "tag") { m_bundletags << e.attribute("meta:value"); } else { m_metadata.insert(e.attribute("meta:name"), e.attribute("meta:value")); } } else if(e.tagName() == "meta:bundle-version") { m_metadata.insert("bundle-version", e.firstChild().toText().data()); versionFound = true; } } } resourceStore->close(); } else { warnKrita << "Could not load meta.xml"; return false; } if (resourceStore->open("preview.png")) { // Workaround for some OS (Debian, Ubuntu), where loading directly from the QIODevice // fails with "libpng error: IDAT: CRC error" QByteArray data = resourceStore->device()->readAll(); QBuffer buffer(&data); m_thumbnail.load(&buffer, "PNG"); resourceStore->close(); } else { warnKrita << "Could not open preview.png"; } /* * If no version is found it's an old bundle with md5 hashes to fix, or if some manifest resource entry * doesn't not correspond to a file the bundle is "broken", in both cases we need to recreate the bundle. */ if (!versionFound) { m_metadata.insert("bundle-version", "1"); warnKrita << filename() << " has an old version and possibly wrong resources md5, so it will be recreated."; toRecreate = true; } if (toRecreate) { recreateBundle(resourceStore); } m_installed = true; setValid(true); setImage(m_thumbnail); } return true; } bool KisResourceBundle::loadFromDevice(QIODevice *) { return false; } bool saveResourceToStore(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", QDateTime::currentDateTime().toOffsetFromUtc(0).toString(Qt::ISODate)); QDir bundleDir = KoResourcePaths::saveLocation("data", "bundles"); bundleDir.cdUp(); QScopedPointer store(KoStore::createStore(filename(), KoStore::Write, "application/x-krita-resourcebundle", KoStore::Zip)); if (!store || store->bad()) return false; Q_FOREACH (const QString &resType, m_manifest.types()) { if (resType == "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(); } } } } else if (resType == "ko_gamutmasks") { KoResourceServer* gamutMaskServer = KoResourceServerProvider::instance()->gamutMaskServer(); Q_FOREACH (const KisResourceBundleManifest::ResourceReference &ref, m_manifest.files(resType)) { KoResource *res = gamutMaskServer->resourceByMD5(ref.md5sum); if (!res) res = gamutMaskServer->resourceByFilename(QFileInfo(ref.resourcePath).fileName()); if (!saveResourceToStore(res, store.data(), "gamutmasks")) { if (res) { warnKrita << "Could not save resource" << resType << res->name(); } else { warnKrita << "could not find resource for" << QFileInfo(ref.resourcePath).fileName(); } } } } #if defined HAVE_SEEXPR else if (resType == "kis_seexpr_scripts") { KoResourceServer *seExprScriptServer = KoResourceServerProvider::instance()->seExprScriptServer(); Q_FOREACH (const KisResourceBundleManifest::ResourceReference &ref, m_manifest.files(resType)) { KoResource *res = seExprScriptServer->resourceByMD5(ref.md5sum); if (!res) res = seExprScriptServer->resourceByFilename(QFileInfo(ref.resourcePath).fileName()); - if (!saveResourceToStore(res, store.data(), "seexpr")) + if (!saveResourceToStore(res, store.data(), "seexpr_scripts")) { if (res) { warnKrita << "Could not save resource" << resType << res->name(); } else { warnKrita << "could not find resource for" << QFileInfo(ref.resourcePath).fileName(); } } } } #endif } 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 resource on the server KisBrushSP res2 = brushServer->resourceByName(res->name()); if (res2) { res->setName(res->name()+"("+res->shortFilename()+")"); } // file name is more important than the regular name because the // it is the way how it is called up from the brushpreset settings. // Therefore just adjust the resource name and only refuse to load // when the filename is different. res2 = brushServer->resourceByFilename(res->shortFilename()); if (!res2) {//if it doesn't exist... brushServer->addResource(res, false);//add it! if (!m_brushesMd5Installed.contains(res->md5())) { m_brushesMd5Installed.append(res->md5()); } if (ref.md5sum!=res->md5()) { md5Mismatch.append(res->name()); } Q_FOREACH (const QString &tag, ref.tagList) { brushServer->addTag(res.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 resource 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"; } } } else if (resType == "gamutmasks") { KoResourceServer* gamutMaskServer = KoResourceServerProvider::instance()->gamutMaskServer(); Q_FOREACH (const KisResourceBundleManifest::ResourceReference &ref, m_manifest.files(resType)) { if (resourceStore->isOpen()) resourceStore->close(); dbgResources << "\tInstalling" << ref.resourcePath; KoGamutMask *res = gamutMaskServer->createResource(QString("bundle://%1:%2").arg(filename()).arg(ref.resourcePath)); if (!res) { warnKrita << "Could not create resource for" << ref.resourcePath; continue; } if (!resourceStore->open(ref.resourcePath)) { warnKrita << "Failed to open" << ref.resourcePath << "from bundle" << filename(); continue; } if (!res->loadFromDevice(resourceStore->device())) { warnKrita << "Failed to load" << ref.resourcePath << "from bundle" << filename(); continue; } dbgResources << "\t\tresource:" << res->name(); //find the resource on the server KoGamutMask *res2 = gamutMaskServer->resourceByName(res->name()); if (!res2) {//if it doesn't exist... gamutMaskServer->addResource(res, false);//add it! if (!m_gamutMasksMd5Installed.contains(res->md5())) { m_gamutMasksMd5Installed.append(res->md5()); } if (ref.md5sum!=res->md5()) { md5Mismatch.append(res->name()); } Q_FOREACH (const QString &tag, ref.tagList) { gamutMaskServer->addTag(res, tag); } //gamutMaskServer->addTag(res, name()); } else { //warnKrita << "Didn't install" << res->name()<<"It already exists on the server"; } } } #if defined HAVE_SEEXPR - else if (resType == "seexpr") + else if (resType == "seexpr_scripts") { KoResourceServer *seExprScriptServer = KoResourceServerProvider::instance()->seExprScriptServer(); Q_FOREACH (const KisResourceBundleManifest::ResourceReference &ref, m_manifest.files(resType)) { if (resourceStore->isOpen()) resourceStore->close(); dbgResources << "\tInstalling" << ref.resourcePath; KisSeExprScript *res = seExprScriptServer->createResource(QString("bundle://%1:%2").arg(filename()).arg(ref.resourcePath)); if (!res) { warnKrita << "Could not create resource for" << ref.resourcePath; continue; } if (!resourceStore->open(ref.resourcePath)) { warnKrita << "Failed to open" << ref.resourcePath << "from bundle" << filename(); continue; } if (!res->loadFromDevice(resourceStore->device())) { warnKrita << "Failed to load" << ref.resourcePath << "from bundle" << filename(); continue; } dbgResources << "\t\tresource:" << res->name(); //find the resource on the server KisSeExprScript *res2 = seExprScriptServer->resourceByName(res->name()); if (!res2) { //if it doesn't exist... seExprScriptServer->addResource(res, false); //add it! if (!m_seExprScriptsMd5Installed.contains(res->md5())) { m_seExprScriptsMd5Installed.append(res->md5()); } if (ref.md5sum != res->md5()) { md5Mismatch.append(res->name()); } Q_FOREACH (const QString &tag, ref.tagList) { seExprScriptServer->addTag(res, tag); } //seExprScriptServer->addTag(res, name()); } else { //warnKrita << "Didn't install" << res->name()<<"It already exists on the server"; } } } #endif } m_installed = true; if(!md5Mismatch.isEmpty()){ QString message = i18n("The following resources had mismatching MD5 sums. They may have gotten corrupted, for example, during download."); QMessageBox bundleFeedback; bundleFeedback.setIcon(QMessageBox::Warning); Q_FOREACH (QString name, md5Mismatch) { message.append("\n"); message.append(name); } bundleFeedback.setText(message); bundleFeedback.exec(); } return true; } bool KisResourceBundle::uninstall() { m_installed = false; QStringList tags = getTagsList(); tags << m_manifest.tags(); //tags << name(); KoResourceServer* gradientServer = KoResourceServerProvider::instance()->gradientServer(); //Q_FOREACH (const KisResourceBundleManifest::ResourceReference &ref, m_manifest.files("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); } } KoResourceServer* gamutMaskServer = KoResourceServerProvider::instance()->gamutMaskServer(); //Q_FOREACH (const KisResourceBundleManifest::ResourceReference &ref, m_manifest.files("gamutmasks")) { Q_FOREACH (const QByteArray md5, m_gamutMasksMd5Installed) { KoGamutMask *res = gamutMaskServer->resourceByMD5(md5); if (res) { gamutMaskServer->removeResourceFromServer(res); } } #if defined HAVE_SEEXPR KoResourceServer *seExprScriptServer = KoResourceServerProvider::instance()->seExprScriptServer(); - //Q_FOREACH (const KisResourceBundleManifest::ResourceReference &ref, m_manifest.files("seexpr")) { + //Q_FOREACH (const KisResourceBundleManifest::ResourceReference &ref, m_manifest.files("seexpr_scripts")) { Q_FOREACH (const QByteArray md5, m_seExprScriptsMd5Installed) { KisSeExprScript *res = seExprScriptServer->resourceByMD5(md5); if (res) { seExprScriptServer->removeResourceFromServer(res); } } #endif Q_FOREACH(const QString &tag, tags) { paintoppresetServer->tagCategoryRemoved(tag); workspaceServer->tagCategoryRemoved(tag); paletteServer->tagCategoryRemoved(tag); brushServer->tagCategoryRemoved(tag); patternServer->tagCategoryRemoved(tag); gradientServer->tagCategoryRemoved(tag); gamutMaskServer->tagCategoryRemoved(tag); } return true; } void KisResourceBundle::addMeta(const QString &type, const QString &value) { m_metadata.insert(type, value); } const QString KisResourceBundle::getMeta(const QString &type, const QString &defaultValue) const { if (m_metadata.contains(type)) { return m_metadata[type]; } else { return defaultValue; } } void KisResourceBundle::addResource(QString fileType, QString filePath, QStringList fileTagList, const QByteArray md5sum) { m_manifest.addResource(fileType, filePath, fileTagList, md5sum); } QList KisResourceBundle::getTagsList() { return QList::fromSet(m_bundletags); } bool KisResourceBundle::isInstalled() { return m_installed; } QStringList KisResourceBundle::resourceTypes() const { return m_manifest.types(); } QList KisResourceBundle::resources(const QString &resType) const { QList references = m_manifest.files(resType); QList ret; Q_FOREACH (const KisResourceBundleManifest::ResourceReference &ref, references) { if (resType == "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(); } else if (resType == "gamutmasks") { KoResourceServer* gamutMaskServer = KoResourceServerProvider::instance()->gamutMaskServer(); KoResource *res = gamutMaskServer->resourceByMD5(ref.md5sum); if (res) ret << res; } +#if defined HAVE_SEEXPR + else if (resType == "seexpr_scripts") { + KoResourceServer *seExprScriptServer = KoResourceServerProvider::instance()->seExprScriptServer(); + KoResource *res = seExprScriptServer->resourceByMD5(ref.md5sum); + if (res) ret << res; + } +#endif } return ret; } void KisResourceBundle::setThumbnail(QString filename) { if (QFileInfo(filename).exists()) { m_thumbnail = QImage(filename); m_thumbnail = m_thumbnail.scaled(256, 256, Qt::KeepAspectRatio, Qt::SmoothTransformation); } else { m_thumbnail = QImage(256, 256, QImage::Format_ARGB32); QPainter gc(&m_thumbnail); gc.fillRect(0, 0, 256, 256, Qt::red); gc.end(); } setImage(m_thumbnail); } void KisResourceBundle::writeMeta(const char *metaTag, const QString &metaKey, KoXmlWriter *writer) { if (m_metadata.contains(metaKey)) { writer->startElement(metaTag); writer->addTextNode(m_metadata[metaKey].toUtf8()); writer->endElement(); } } void KisResourceBundle::writeUserDefinedMeta(const QString &metaKey, KoXmlWriter *writer) { if (m_metadata.contains(metaKey)) { writer->startElement("meta:meta-userdefined"); writer->addAttribute("meta:name", metaKey); writer->addAttribute("meta:value", m_metadata[metaKey]); writer->endElement(); } } void KisResourceBundle::setInstalled(bool install) { m_installed = install; } void KisResourceBundle::saveMetadata(QScopedPointer &store) { QBuffer buf; store->open("meta.xml"); buf.open(QBuffer::WriteOnly); KoXmlWriter metaWriter(&buf); metaWriter.startDocument("office:document-meta"); metaWriter.startElement("meta:meta"); writeMeta("meta:generator", "generator", &metaWriter); metaWriter.startElement("meta:bundle-version"); metaWriter.addTextNode(m_bundleVersion.toUtf8()); metaWriter.endElement(); writeMeta("dc:author", "author", &metaWriter); writeMeta("dc:title", "filename", &metaWriter); writeMeta("dc:description", "description", &metaWriter); writeMeta("meta:initial-creator", "author", &metaWriter); writeMeta("dc:creator", "author", &metaWriter); writeMeta("meta:creation-date", "created", &metaWriter); writeMeta("meta:dc-date", "updated", &metaWriter); writeUserDefinedMeta("email", &metaWriter); writeUserDefinedMeta("license", &metaWriter); writeUserDefinedMeta("website", &metaWriter); Q_FOREACH (const QString &tag, m_bundletags) { metaWriter.startElement("meta:meta-userdefined"); metaWriter.addAttribute("meta:name", "tag"); metaWriter.addAttribute("meta:value", tag); metaWriter.endElement(); } metaWriter.endElement(); // meta:meta metaWriter.endDocument(); buf.close(); store->write(buf.data()); store->close(); } void KisResourceBundle::saveManifest(QScopedPointer &store) { store->open("META-INF/manifest.xml"); QBuffer buf; buf.open(QBuffer::WriteOnly); m_manifest.save(&buf); buf.close(); store->write(buf.data()); store->close(); } void KisResourceBundle::recreateBundle(QScopedPointer &oldStore) { // Save a copy of the unmodified bundle, so that if anything goes bad the user doesn't lose it QFile file(filename()); file.copy(filename() + ".old"); QString newStoreName = filename() + ".tmp"; { QScopedPointer store(KoStore::createStore(newStoreName, KoStore::Write, "application/x-krita-resourcebundle", KoStore::Zip)); KoHashGenerator *generator = KoHashGeneratorProvider::instance()->getGenerator("MD5"); KisResourceBundleManifest newManifest; addMeta("updated", QDateTime::currentDateTime().toString(Qt::ISODate)); Q_FOREACH (KisResourceBundleManifest::ResourceReference ref, m_manifest.files()) { // Wrong manifest entry found, skip it if(!oldStore->open(ref.resourcePath)) continue; store->open(ref.resourcePath); QByteArray data = oldStore->device()->readAll(); oldStore->close(); store->write(data); store->close(); QByteArray result = generator->generateHash(data); newManifest.addResource(ref.fileTypeName, ref.resourcePath, ref.tagList, result); } m_manifest = newManifest; if (!m_thumbnail.isNull()) { QByteArray byteArray; QBuffer buffer(&byteArray); m_thumbnail.save(&buffer, "PNG"); if (!store->open("preview.png")) warnKrita << "Could not open preview.png"; if (store->write(byteArray) != buffer.size()) warnKrita << "Could not write preview.png"; store->close(); } saveManifest(store); saveMetadata(store); store->finalize(); } // Remove the current bundle and then move the tmp one to be the correct one file.setFileName(filename()); if (!file.remove()) { qWarning() << "Could not remove" << filename() << file.errorString(); } QFile f(newStoreName); Q_ASSERT(f.exists()); if (!f.copy(filename())) { qWarning() << "Could not copy the tmp file to the store" << filename() << newStoreName << QFile(newStoreName).exists() << f.errorString(); } } int KisResourceBundle::resourceCount() const { return m_manifest.files().count(); } diff --git a/plugins/extensions/resourcemanager/dlg_bundle_manager.cpp b/plugins/extensions/resourcemanager/dlg_bundle_manager.cpp index bdf4ddfca0..2154c6944f 100644 --- a/plugins/extensions/resourcemanager/dlg_bundle_manager.cpp +++ b/plugins/extensions/resourcemanager/dlg_bundle_manager.cpp @@ -1,433 +1,438 @@ /* * 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" #include #include #include #define ICON_SIZE 48 DlgBundleManager::DlgBundleManager(ResourceManager *resourceManager, KisActionManager* actionMgr, QWidget *parent) : KoDialog(parent) , m_page(new QWidget()) , m_ui(new Ui::WdgDlgBundleManager) , m_currentBundle(0) , m_resourceManager(resourceManager) { 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::SingleSelection); 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::SingleSelection); 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->bnImportBrushes, SIGNAL(clicked()), SLOT(slotImportResource())); connect(m_ui->bnImportGradients, SIGNAL(clicked()), SLOT(slotImportResource())); connect(m_ui->bnImportPalettes, SIGNAL(clicked()), SLOT(slotImportResource())); connect(m_ui->bnImportPatterns, SIGNAL(clicked()), SLOT(slotImportResource())); connect(m_ui->bnImportPresets, SIGNAL(clicked()), SLOT(slotImportResource())); connect(m_ui->bnImportWorkspaces, SIGNAL(clicked()), SLOT(slotImportResource())); connect(m_ui->bnImportBundles, 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 = KisResourceBundleServerProvider::instance()->resourceBundleServer(); m_ui->listInactive->clear(); m_ui->listActive->clear(); Q_FOREACH (const QString &f, bundleServer->blackListedFiles()) { KisResourceBundle *bundle = new KisResourceBundle(f); bundle->load(); if (bundle->valid()) { bundle->setInstalled(false); m_blacklistedBundles[f] = bundle; } } fillListWidget(m_blacklistedBundles.values(), m_ui->listInactive); Q_FOREACH (KisResourceBundle *bundle, bundleServer->resources()) { if (bundle->valid()) { m_activeBundles[bundle->filename()] = bundle; } } fillListWidget(m_activeBundles.values(), m_ui->listActive); } void DlgBundleManager::accept() { KoResourceServer *bundleServer = KisResourceBundleServerProvider::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(); QString name = item->text(); KisResourceBundle *bundle = bundleServer->resourceByMD5(ba); QMessageBox bundleFeedback; bundleFeedback.setIcon(QMessageBox::Warning); QString feedback = "bundlefeedback"; if (!bundle) { // Get it from the blacklisted bundles Q_FOREACH (KisResourceBundle *b2, m_blacklistedBundles.values()) { if (b2->md5() == ba) { bundle = b2; break; } } } if (bundle) { bool isKrita3Bundle = false; if (bundle->filename().endsWith("Krita_3_Default_Resources.bundle")) { isKrita3Bundle = true; KConfigGroup group = KSharedConfig::openConfig()->group("BundleHack"); group.writeEntry("HideKrita3Bundle", false); } else { 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 \"%1\" to resource server", name); bundleFeedback.setText(feedback); bundleFeedback.exec(); } if (!isKrita3Bundle) { if (!bundleServer->removeFromBlacklist(bundle)) { feedback = i18n("Couldn't remove bundle \"%1\" from blacklist", name); bundleFeedback.setText(feedback); bundleFeedback.exec(); } } } else { if (!isKrita3Bundle) { bundleServer->removeFromBlacklist(bundle); } //let's assume that bundles that 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 \"%1\" doesn't exist!", name); 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(); KisResourceBundle *bundle = bundleServer->resourceByMD5(ba); bool isKrits3Bundle = false; if (bundle) { if (bundle->filename().contains("Krita_3_Default_Resources.bundle")) { isKrits3Bundle = true; KConfigGroup group = KSharedConfig::openConfig()->group("BundleHack"); group.writeEntry("HideKrita3Bundle", true); } if (bundle->isInstalled()) { bundle->uninstall(); if (!isKrits3Bundle) { 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->clear(); m_ui->lblAuthor->clear(); m_ui->lblEmail->clear(); m_ui->lblLicense->clear(); m_ui->lblWebsite->clear(); m_ui->lblDescription->clear(); m_ui->lblCreated->clear(); m_ui->lblUpdated->clear(); 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 = KisResourceBundleServerProvider::instance()->resourceBundleServer(); KisResourceBundle *bundle = bundleServer->resourceByMD5(ba); if (!bundle) { // Get it from the blacklisted bundles Q_FOREACH (KisResourceBundle *b2, m_blacklistedBundles.values()) { if (b2->md5() == ba) { bundle = b2; break; } } } if (bundle) { QFontMetrics metrics(this->font()); m_currentBundle = bundle; m_ui->bnEditBundle->setEnabled(true); m_ui->lblName->setText(bundle->name()); m_ui->lblAuthor->setText(metrics.elidedText(bundle->getMeta("author"), Qt::ElideRight, m_ui->lblAuthor->width())); m_ui->lblAuthor->setToolTip(bundle->getMeta("author")); m_ui->lblEmail->setText(metrics.elidedText(bundle->getMeta("email"), Qt::ElideRight, m_ui->lblEmail->width())); m_ui->lblEmail->setToolTip(bundle->getMeta("email")); m_ui->lblLicense->setText(metrics.elidedText(bundle->getMeta("license"), Qt::ElideRight, m_ui->lblLicense->width())); m_ui->lblLicense->setToolTip(bundle->getMeta("license")); m_ui->lblWebsite->setText(metrics.elidedText(bundle->getMeta("website"), Qt::ElideRight, m_ui->lblWebsite->width())); m_ui->lblWebsite->setToolTip(bundle->getMeta("website")); m_ui->lblDescription->setPlainText(bundle->getMeta("description")); if (QDateTime::fromString(bundle->getMeta("created"), Qt::ISODate).isValid()) { m_ui->lblCreated->setText(QDateTime::fromString(bundle->getMeta("created"), Qt::ISODate).toLocalTime().toString(Qt::DefaultLocaleShortDate)); } else { m_ui->lblCreated->setText(QDate::fromString(bundle->getMeta("created"), "dd/MM/yyyy").toString(Qt::DefaultLocaleShortDate)); } if (QDateTime::fromString(bundle->getMeta("updated"), Qt::ISODate).isValid()) { m_ui->lblUpdated->setText(QDateTime::fromString(bundle->getMeta("updated"), Qt::ISODate).toLocalTime().toString(Qt::DefaultLocaleShortDate)); } else { m_ui->lblUpdated->setText(QDate::fromString(bundle->getMeta("updated"), "dd/MM/yyyy").toString(Qt::DefaultLocaleShortDate)); } 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")); } else if (resType == "gamutmasks") { toplevel->setText(0, i18n("Gamut Masks")); } +#if defined HAVE_SEEXPR + else if (resType == "seexpr_scripts") { + toplevel->setText(0, i18n("SeExpr Scripts")); + } +#endif 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); m_activeBundles.remove(m_currentBundle->filename()); m_currentBundle = 0; if (dlg.exec() != QDialog::Accepted) { return; } m_currentBundle = m_resourceManager->saveBundle(dlg); refreshListData(); } } void DlgBundleManager::fillListWidget(QList bundles, QListWidget *w) { w->setIconSize(QSize(ICON_SIZE, ICON_SIZE)); w->setSelectionMode(QAbstractItemView::MultiSelection); Q_FOREACH (KisResourceBundle *bundle, bundles) { QPixmap pixmap(ICON_SIZE, ICON_SIZE); pixmap.fill(Qt::gray); 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(); } QListWidgetItem *item = new QListWidgetItem(pixmap, bundle->name()); item->setData(Qt::UserRole, bundle->md5()); w->addItem(item); } } void DlgBundleManager::slotImportResource() { if (m_actionManager) { QObject *button = sender(); QString buttonName = button->objectName(); KisAction *action = 0; if (buttonName == "bnImportBundles") { action = m_actionManager->actionByName("import_bundles"); } else if (buttonName == "bnImportBrushes") { action = m_actionManager->actionByName("import_brushes"); } else if (buttonName == "bnImportGradients") { action = m_actionManager->actionByName("import_gradients"); } else if (buttonName == "bnImportPalettes") { action = m_actionManager->actionByName("import_palettes"); } else if (buttonName == "bnImportPatterns") { action = m_actionManager->actionByName("import_patterns"); } else if (buttonName == "bnImportPresets") { action = m_actionManager->actionByName("import_presets"); } else if (buttonName == "bnImportWorkspaces") { action = m_actionManager->actionByName("import_workspaces"); } else { warnUI << "Unhandled bundle manager import button " << buttonName; return; } action->trigger(); refreshListData(); } } void DlgBundleManager::slotCreateBundle() { if (m_actionManager) { KisAction *action = m_actionManager->actionByName("create_bundle"); action->trigger(); refreshListData(); } } 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/plugins/extensions/resourcemanager/dlg_create_bundle.cpp b/plugins/extensions/resourcemanager/dlg_create_bundle.cpp index 7e998437e9..c010f3ecaa 100644 --- a/plugins/extensions/resourcemanager/dlg_create_bundle.cpp +++ b/plugins/extensions/resourcemanager/dlg_create_bundle.cpp @@ -1,467 +1,505 @@ /* * 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 "KisResourceBundle.h" #define ICON_SIZE 48 DlgCreateBundle::DlgCreateBundle(KisResourceBundle *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))); Q_FOREACH (const QString & resType, bundle->resourceTypes()) { if (resType == "gradients") { Q_FOREACH (const KoResource *res, bundle->resources(resType)) { if (res) { m_selectedGradients << res->shortFilename(); } } } else if (resType == "patterns") { Q_FOREACH (const KoResource *res, bundle->resources(resType)) { if (res) { m_selectedPatterns << res->shortFilename(); } } } else if (resType == "brushes") { Q_FOREACH (const KoResource *res, bundle->resources(resType)) { if (res) { m_selectedBrushes << res->shortFilename(); } } } else if (resType == "palettes") { Q_FOREACH (const KoResource *res, bundle->resources(resType)) { if (res) { m_selectedPalettes << res->shortFilename(); } } } else if (resType == "workspaces") { Q_FOREACH (const KoResource *res, bundle->resources(resType)) { if (res) { m_selectedWorkspaces << res->shortFilename(); } } } else if (resType == "paintoppresets") { Q_FOREACH (const KoResource *res, bundle->resources(resType)) { if (res) { m_selectedPresets << res->shortFilename(); } } } else if (resType == "gamutmasks") { Q_FOREACH (const KoResource *res, bundle->resources(resType)) { if (res) { m_selectedGamutMasks << res->shortFilename(); } } } +#if defined HAVE_SEEXPR + else if (resType == "seexpr_scripts") { + Q_FOREACH (const KoResource *res, bundle->resources(resType)) { + if (res) { + m_selectedSeExprScripts << res->shortFilename(); + } + } + } +#endif } } else { setCaption(i18n("Create Resource Bundle")); KisConfig cfg(true); 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", QStandardPaths::writableLocation(QStandardPaths::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("Gamut Masks"), QString("gamutmasks")); +#if defined HAVE_SEEXPR + m_ui->cmbResourceTypes->addItem(i18n("SeExpr Scripts"), QString("seexpr_scripts")); +#endif 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_bundle) { 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(false); 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()); } else if (resourceType == "gamutmasks") { m_selectedGamutMasks.append(item->data(Qt::UserRole).toString()); } +#if defined HAVE_SEEXPR + else if (resourceType == "seexpr_scripts") { + m_selectedSeExprScripts.append(item->data(Qt::UserRole).toString()); + } +#endif } 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()); } else if (resourceType == "gamutmasks") { m_selectedGamutMasks.removeAll(item->data(Qt::UserRole).toString()); } +#if defined HAVE_SEEXPR + else if (resourceType == "seexpr_scripts") { + m_selectedSeExprScripts.removeAll(item->data(Qt::UserRole).toString()); + } +#endif } 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); } } } else if (resourceType == "gamutmasks") { KoResourceServer* server = KoResourceServerProvider::instance()->gamutMaskServer(); Q_FOREACH (KoResource *res, server->resources()) { QListWidgetItem *item = new QListWidgetItem(imageToIcon(res->image()), res->name()); item->setData(Qt::UserRole, res->shortFilename()); if (m_selectedGamutMasks.contains(res->shortFilename())) { m_ui->tableSelected->addItem(item); } else { m_ui->tableAvailable->addItem(item); } } } +#if defined HAVE_SEEXPR + else if (resourceType == "seexpr_scripts") { + KoResourceServer* server = KoResourceServerProvider::instance()->seExprScriptServer(); + Q_FOREACH (KoResource *res, server->resources()) { + QListWidgetItem *item = new QListWidgetItem(imageToIcon(res->image()), res->name()); + item->setData(Qt::UserRole, res->shortFilename()); + + if (m_selectedSeExprScripts.contains(res->shortFilename())) { + m_ui->tableSelected->addItem(item); + } + else { + m_ui->tableAvailable->addItem(item); + } + } + } +#endif } void DlgCreateBundle::getPreviewImage() { KoFileDialog dialog(this, KoFileDialog::OpenFile, "BundlePreviewImage"); dialog.setCaption(i18n("Select file to use as bundle icon")); dialog.setDefaultDir(QStandardPaths::writableLocation(QStandardPaths::PicturesLocation)); dialog.setMimeTypeFilters(KisImportExportManager::supportedMimeTypes(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/plugins/extensions/resourcemanager/dlg_create_bundle.h b/plugins/extensions/resourcemanager/dlg_create_bundle.h index 9253ecd74e..b7a3c1c1b7 100644 --- a/plugins/extensions/resourcemanager/dlg_create_bundle.h +++ b/plugins/extensions/resourcemanager/dlg_create_bundle.h @@ -1,83 +1,91 @@ /* * 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 +#include "config-seexpr.h" + class KisResourceBundle; namespace Ui { class WdgDlgCreateBundle; } class DlgCreateBundle : public KoDialog { Q_OBJECT public: explicit DlgCreateBundle(KisResourceBundle *bundle = 0, QWidget *parent = 0); ~DlgCreateBundle() override; 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; } QStringList selectedGamutMasks() const { return m_selectedGamutMasks; } +#if defined HAVE_SEEXPR + QStringList selectedSeExprScripts() const { return m_selectedSeExprScripts; } +#endif private Q_SLOTS: void accept() override; 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; QStringList m_selectedGamutMasks; +#if defined HAVE_SEEXPR + QStringList m_selectedSeExprScripts; +#endif QString m_previewImage; KisResourceBundle *m_bundle; }; #endif // KOBUNDLECREATIONWIDGET_H diff --git a/plugins/extensions/resourcemanager/resourcemanager.cpp b/plugins/extensions/resourcemanager/resourcemanager.cpp index 0fe45768f9..27dc9bf702 100644 --- a/plugins/extensions/resourcemanager/resourcemanager.cpp +++ b/plugins/extensions/resourcemanager/resourcemanager.cpp @@ -1,337 +1,353 @@ /* * 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 #include #include #include #include +#include "config-seexpr.h" + #include "dlg_bundle_manager.h" #include "dlg_create_bundle.h" class ResourceManager::Private { public: Private() { brushServer = KisBrushServer::instance()->brushServer(); paintopServer = KisResourceServerProvider::instance()->paintOpPresetServer(); gradientServer = KoResourceServerProvider::instance()->gradientServer(); patternServer = KoResourceServerProvider::instance()->patternServer(); paletteServer = KoResourceServerProvider::instance()->paletteServer(); workspaceServer = KisResourceServerProvider::instance()->workspaceServer(); gamutMaskServer = KoResourceServerProvider::instance()->gamutMaskServer(); +#if defined HAVE_SEEXPR + seExprScriptServer = KoResourceServerProvider::instance() ->seExprScriptServer(); +#endif } KisBrushResourceServer* brushServer; KisPaintOpPresetResourceServer * paintopServer; KoResourceServer* gradientServer; KoResourceServer *patternServer; KoResourceServer* paletteServer; KoResourceServer* workspaceServer; KoResourceServer* gamutMaskServer; +#if defined HAVE_SEEXPR + KoResourceServer* seExprScriptServer; +#endif }; K_PLUGIN_FACTORY_WITH_JSON(ResourceManagerFactory, "kritaresourcemanager.json", registerPlugin();) ResourceManager::ResourceManager(QObject *parent, const QVariantList &) : KisActionPlugin(parent) , d(new Private()) { KisAction *action = new KisAction(i18n("Import Bundles..."), this); addAction("import_bundles", action); connect(action, SIGNAL(triggered()), this, SLOT(slotImportBundles())); action = new KisAction(i18n("Import Brushes..."), this); addAction("import_brushes", action); connect(action, SIGNAL(triggered()), this, SLOT(slotImportBrushes())); action = new KisAction(i18n("Import Gradients..."), this); addAction("import_gradients", action); connect(action, SIGNAL(triggered()), this, SLOT(slotImportGradients())); action = new KisAction(i18n("Import Palettes..."), this); addAction("import_palettes", action); connect(action, SIGNAL(triggered()), this, SLOT(slotImportPalettes())); action = new KisAction(i18n("Import Patterns..."), this); addAction("import_patterns", action); connect(action, SIGNAL(triggered()), this, SLOT(slotImportPatterns())); action = new KisAction(i18n("Import Presets..."), this); addAction("import_presets", action); connect(action, SIGNAL(triggered()), this, SLOT(slotImportPresets())); action = new KisAction(i18n("Import Workspaces..."), this); addAction("import_workspaces", action); connect(action, SIGNAL(triggered()), this, SLOT(slotImportWorkspaces())); 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::slotCreateBundle() { DlgCreateBundle dlgCreateBundle; if (dlgCreateBundle.exec() != QDialog::Accepted) { return; } saveBundle(dlgCreateBundle); } KisResourceBundle *ResourceManager::saveBundle(const DlgCreateBundle &dlgCreateBundle) { QString bundlePath = dlgCreateBundle.saveLocation() + "/" + dlgCreateBundle.bundleName() + ".bundle"; KisResourceBundle *newBundle = new KisResourceBundle(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()); newBundle->setThumbnail(dlgCreateBundle.previewImage()); 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()); KisPaintOpSettingsSP settings = preset->settings(); QStringList requiredFiles = settings->getStringList(KisPaintOpUtils::RequiredBrushFilesListTag); requiredFiles << settings->getString(KisPaintOpUtils::RequiredBrushFileTag); KritaUtils::makeContainerUnique(requiredFiles); Q_FOREACH (const QString &brushFile, requiredFiles) { KisBrush *brush = d->brushServer->resourceByFilename(brushFile).data(); if (brush) { newBundle->addResource("kis_brushes", brushFile, d->brushServer->assignedTagsList(brush), brush->md5()); } else { qWarning() << "There is no brush with name" << brushFile; } } } 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()); } res = dlgCreateBundle.selectedGamutMasks(); Q_FOREACH (const QString &r, res) { KoResource *res = d->gamutMaskServer->resourceByFilename(r); newBundle->addResource("ko_gamutmasks", res->filename(), d->gamutMaskServer->assignedTagsList(res), res->md5()); } +#if defined HAVE_SEEXPR + res = dlgCreateBundle.selectedSeExprScripts(); + Q_FOREACH (const QString &r, res) { + KoResource *res = d->seExprScriptServer->resourceByFilename(r); + newBundle->addResource("kis_seexpr_scripts", res->filename(), d->gamutMaskServer->assignedTagsList(res), res->md5()); + } +#endif + newBundle->addMeta("fileName", bundlePath); newBundle->addMeta("created", QDateTime::currentDateTime().toOffsetFromUtc(0).toString(Qt::ISODate)); if (!newBundle->save()) { QMessageBox::critical(viewManager()->mainWindow(), i18nc("@title:window", "Krita"), i18n("Could not create the new bundle.")); } else { newBundle->setValid(true); if (QDir(KisResourceBundleServerProvider::instance()->resourceBundleServer()->saveLocation()) != QDir(QFileInfo(bundlePath).path())) { newBundle->setFilename(KisResourceBundleServerProvider::instance()->resourceBundleServer()->saveLocation() + "/" + dlgCreateBundle.bundleName() + ".bundle"); } if (KisResourceBundleServerProvider::instance()->resourceBundleServer()->resourceByName(newBundle->name())) { KisResourceBundleServerProvider::instance()->resourceBundleServer()->removeResourceFromServer( KisResourceBundleServerProvider::instance()->resourceBundleServer()->resourceByName(newBundle->name())); } KisResourceBundleServerProvider::instance()->resourceBundleServer()->addResource(newBundle, true); newBundle->load(); } return newBundle; } void ResourceManager::slotManageBundles() { DlgBundleManager* dlg = new DlgBundleManager(this, viewManager()->actionManager(), KisPart::instance()->currentMainwindow()); if (dlg->exec() != QDialog::Accepted) { return; } } QStringList ResourceManager::importResources(const QString &title, const QStringList &mimes) const { KoFileDialog dialog(viewManager()->mainWindow(), KoFileDialog::OpenFiles, "krita_resources"); dialog.setDefaultDir(QStandardPaths::writableLocation(QStandardPaths::HomeLocation)); dialog.setCaption(title); dialog.setMimeTypeFilters(mimes); return dialog.filenames(); } void ResourceManager::slotImportBrushes() { QStringList resources = importResources(i18n("Import Brushes"), QStringList() << "image/x-gimp-brush" << "image/x-gimp-x-gimp-brush-animated" << "image/x-adobe-brushlibrary" << "image/png" << "image/svg+xml"); Q_FOREACH (const QString &res, resources) { d->brushServer->importResourceFile(res); } } void ResourceManager::slotImportPresets() { QStringList resources = importResources(i18n("Import Presets"), QStringList() << "application/x-krita-paintoppreset"); Q_FOREACH (const QString &res, resources) { d->paintopServer->importResourceFile(res); } } void ResourceManager::slotImportGradients() { QStringList resources = importResources(i18n("Import Gradients"), QStringList() << "image/svg+xml" << "application/x-gimp-gradient"); Q_FOREACH (const QString &res, resources) { d->gradientServer->importResourceFile(res); } } void ResourceManager::slotImportBundles() { QStringList resources = importResources(i18n("Import Bundles"), QStringList() << "application/x-krita-bundle"); Q_FOREACH (const QString &res, resources) { KisResourceBundle *bundle = KisResourceBundleServerProvider::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.", res)); } } else { QMessageBox::warning(0, i18nc("@title:window", "Krita"), i18n("Could not load bundle %1.", res)); } QFileInfo fi(res); QString newFilename = KisResourceBundleServerProvider::instance()->resourceBundleServer()->saveLocation() + fi.completeBaseName() + bundle->defaultFileExtension(); QFileInfo fileInfo(newFilename); int i = 1; while (fileInfo.exists()) { fileInfo.setFile(KisResourceBundleServerProvider::instance()->resourceBundleServer()->saveLocation() + fi.completeBaseName() + QString("%1").arg(i) + bundle->defaultFileExtension()); i++; } bundle->setFilename(fileInfo.filePath()); QFile::copy(res, newFilename); KisResourceBundleServerProvider::instance()->resourceBundleServer()->addResource(bundle, false); } } void ResourceManager::slotImportPatterns() { QStringList resources = importResources(i18n("Import Patterns"), QStringList() << "image/png" << "image/svg+xml" << "application/x-gimp-pattern" << "image/jpeg" << "image/tiff" << "image/bmp" << "image/xpg"); Q_FOREACH (const QString &res, resources) { d->patternServer->importResourceFile(res); } } void ResourceManager::slotImportPalettes() { QStringList resources = importResources(i18n("Import Palettes"), QStringList() << "image/x-gimp-color-palette"); Q_FOREACH (const QString &res, resources) { d->paletteServer->importResourceFile(res); } } void ResourceManager::slotImportWorkspaces() { QStringList resources = importResources(i18n("Import Workspaces"), QStringList() << "application/x-krita-workspace"); Q_FOREACH (const QString &res, resources) { d->workspaceServer->importResourceFile(res); } } #include "resourcemanager.moc" diff --git a/plugins/generators/seexpr/CMakeLists.txt b/plugins/generators/seexpr/CMakeLists.txt index f60cf70497..6cef25102a 100644 --- a/plugins/generators/seexpr/CMakeLists.txt +++ b/plugins/generators/seexpr/CMakeLists.txt @@ -1,19 +1,21 @@ set(kritaseexprgenerator_SOURCES SeExprVariable.cpp SeExprExpressionContext.cpp generator.cpp kis_wdg_seexpr.cpp + kis_wdg_seexpr_presets_save.cpp ) ki18n_wrap_ui(kritaseexprgenerator_SOURCES wdgseexpr.ui + wdgseexprsavepreset.ui ) add_library(kritaseexprgenerator MODULE ${kritaseexprgenerator_SOURCES}) target_link_libraries(kritaseexprgenerator kritaui SeExpr2::SeExpr2 SeExpr2::SeExpr2Editor) install(TARGETS kritaseexprgenerator DESTINATION ${KRITA_PLUGIN_INSTALL_DIR}) add_subdirectory(tests) diff --git a/plugins/generators/seexpr/kis_wdg_seexpr.cpp b/plugins/generators/seexpr/kis_wdg_seexpr.cpp index 151b29aea9..5dbaa999fa 100644 --- a/plugins/generators/seexpr/kis_wdg_seexpr.cpp +++ b/plugins/generators/seexpr/kis_wdg_seexpr.cpp @@ -1,133 +1,320 @@ /* * This file is part of Krita * * Copyright (c) 2020 L. E. Segovia * * 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 +#include #include #include #include #include #include #include "generator.h" #include "kis_wdg_seexpr.h" #include "SeExprExpressionContext.h" #include "ui_wdgseexpr.h" KisWdgSeExpr::KisWdgSeExpr(QWidget *parent) : KisConfigWidget(parent), - updateCompressor(1000, KisSignalCompressor::Mode::POSTPONE) + updateCompressor(1000, KisSignalCompressor::Mode::POSTPONE), + m_currentPreset(new KisSeExprScript(i18n("Untitled"))), + m_saveDialog(new KisWdgSeExprPresetsSave(this)), + m_isCreatingPresetFromScratch(true) { m_widget = new Ui_WdgSeExpr(); m_widget->setupUi(this); m_widget->txtEditor->setControlCollectionWidget(m_widget->wdgControls); + + m_widget->renameBrushPresetButton->setIcon(KisIconUtils::loadIcon("dirty-preset")); // edit icon + + m_widget->reloadPresetButton->setIcon(KisIconUtils::loadIcon("updateColorize")); + m_widget->reloadPresetButton->setToolTip(i18n("Reload the preset")); + m_widget->dirtyPresetIndicatorButton->setIcon(KisIconUtils::loadIcon("warning")); + m_widget->dirtyPresetIndicatorButton->setToolTip(i18n("The settings for this preset have changed from their default.")); + KisDialogStateSaver::restoreState(m_widget->txtEditor, "krita/generators/seexpr"); // Manually restore SeExpr state. KisDialogStateSaver uses setPlainText, not text itself m_widget->txtEditor->setExpr(m_widget->txtEditor->exprTe->toPlainText()); m_widget->txtEditor->registerExtraVariable("$u", i18nc("SeExpr variable", "Normalized X axis coordinate of the image from its top-left corner")); m_widget->txtEditor->registerExtraVariable("$v", i18nc("SeExpr variable", "Normalized Y axis coordinate of the image from its top-left corner")); m_widget->txtEditor->registerExtraVariable("$w", i18nc("SeExpr variable", "Image width")); m_widget->txtEditor->registerExtraVariable("$h", i18nc("SeExpr variable", "Image height")); m_widget->txtEditor->updateCompleter(); - connect(m_widget->scriptSelectorWidget, SIGNAL(resourceSelected(KoResource*)), SLOT(slotScriptResourceSelected(KoResource*))); - connect(m_widget->txtEditor, SIGNAL(apply()), &updateCompressor, SLOT(start())); - connect(m_widget->txtEditor, SIGNAL(preview()), &updateCompressor, SLOT(start())); + connect(m_widget->scriptSelectorWidget, SIGNAL(resourceSelected(KoResource*)), this, SLOT(slotResourceSelected(KoResource*))); + connect(m_saveDialog, SIGNAL(resourceSelected(KoResource*)), this, SLOT(slotResourceSelected(KoResource*))); + + connect(m_widget->renameBrushPresetButton, SIGNAL(clicked(bool)), + this, SLOT(slotRenamePresetActivated())); + connect(m_widget->cancelBrushNameUpdateButton, SIGNAL(clicked(bool)), + this, SLOT(slotRenamePresetDeactivated())); + connect(m_widget->updateBrushNameButton, SIGNAL(clicked(bool)), + this, SLOT(slotSaveRenameCurrentPreset())); + connect(m_widget->renameBrushNameTextField, SIGNAL(returnPressed()), + this, SLOT(slotSaveRenameCurrentPreset())); + + connect(m_widget->saveBrushPresetButton, SIGNAL(clicked()), + this, SLOT(slotSaveBrushPreset())); + connect(m_widget->saveNewBrushPresetButton, SIGNAL(clicked()), + this, SLOT(slotSaveNewBrushPreset())); + + connect(m_widget->reloadPresetButton, SIGNAL(clicked()), + this, SLOT(slotReloadPresetClicked())); + + connect(m_widget->txtEditor, SIGNAL(apply()), + &updateCompressor, SLOT(start())); + connect(m_widget->txtEditor, SIGNAL(preview()), + &updateCompressor, SLOT(start())); connect(&updateCompressor, SIGNAL(timeout()), this, SLOT(isValid())); + + togglePresetRenameUIActive(false); // reset the UI state of renaming a preset if we are changing presets + slotUpdatePresetSettings(); // disable everything until a preset is selected } KisWdgSeExpr::~KisWdgSeExpr() { KisDialogStateSaver::saveState(m_widget->txtEditor, "krita/generators/seexpr"); + delete m_saveDialog; delete m_widget; } inline const Ui_WdgSeExpr *KisWdgSeExpr::widget() const { return m_widget; } void KisWdgSeExpr::setConfiguration(const KisPropertiesConfigurationSP config) { Q_ASSERT(!config->getString("script").isEmpty()); QString script = config->getString("script"); - widget()->txtEditor->setExpr(script, true); + m_widget->txtEditor->setExpr(script, true); } KisPropertiesConfigurationSP KisWdgSeExpr::configuration() const { KisFilterConfigurationSP config = new KisFilterConfiguration("seexpr", 1); - QVariant v(widget()->txtEditor->getExpr()); + QVariant v(m_widget->txtEditor->getExpr()); config->setProperty("script", v); return config; } -void KisWdgSeExpr::slotScriptResourceSelected(KoResource *r) +void KisWdgSeExpr::slotResourceSelected(KoResource *r) { KisSeExprScript *g = static_cast(r); if (g) { - widget()->txtEditor->setExpr(g->script(), true); + // ALWAYS have a manageable copy of the preset + m_currentPreset = g->clone(); + + m_isCreatingPresetFromScratch = false; + + m_widget->txtEditor->setExpr(g->script(), true); + + QString formattedBrushName = g->name().replace("_", " "); + m_widget->currentBrushNameLabel->setText(formattedBrushName); + m_widget->renameBrushNameTextField->setText(g->name()); + // get the preset image and pop it into the thumbnail area on the top of the brush editor + m_widget->presetThumbnailicon->setPixmap(QPixmap::fromImage(g->image().scaled(55, 55, Qt::KeepAspectRatio, Qt::SmoothTransformation))); + + togglePresetRenameUIActive(false); // reset the UI state of renaming a brush if we are changing brush presets + slotUpdatePresetSettings(); // check to see if the dirty preset icon needs to be shown + updateCompressor.start(); } } +void KisWdgSeExpr::slotRenamePresetActivated() +{ + togglePresetRenameUIActive(true); +} + +void KisWdgSeExpr::slotRenamePresetDeactivated() +{ + togglePresetRenameUIActive(false); +} + +void KisWdgSeExpr::togglePresetRenameUIActive(bool isRenaming) +{ + // This function doesn't really do anything except get the UI in a state to rename a brush preset + m_widget->renameBrushNameTextField->setVisible(isRenaming); + m_widget->updateBrushNameButton->setVisible(isRenaming); + m_widget->cancelBrushNameUpdateButton->setVisible(isRenaming); + + // hide these below areas while renaming + m_widget->currentBrushNameLabel->setVisible(!isRenaming); + m_widget->renameBrushPresetButton->setVisible(!isRenaming); + m_widget->saveBrushPresetButton->setEnabled(!isRenaming); + m_widget->saveBrushPresetButton->setVisible(!isRenaming); + m_widget->saveNewBrushPresetButton->setEnabled(!isRenaming); + m_widget->saveNewBrushPresetButton->setVisible(!isRenaming); +} + +void KisWdgSeExpr::slotSaveRenameCurrentPreset() +{ + slotReloadPresetClicked(); + + KisSeExprScriptSP curPreset = m_currentPreset; + + if (!curPreset) + return; + + KoResourceServer *rServer = KoResourceServerProvider::instance()->seExprScriptServer(); + QString saveLocation = rServer->saveLocation(); + + QString originalPresetName = curPreset->name(); + QString renamedPresetName = m_widget->renameBrushNameTextField->text(); + QString originalPresetPathAndFile = saveLocation + originalPresetName + curPreset->defaultFileExtension(); + QString renamedPresetPathAndFile = saveLocation + renamedPresetName + curPreset->defaultFileExtension(); + + KisSeExprScript* newPreset = curPreset->clone(); + newPreset->setFilename(renamedPresetPathAndFile); // this also contains the path + newPreset->setName(renamedPresetName); + newPreset->setImage(curPreset->image()); // use existing thumbnail (might not need to do this) + newPreset->setDirty(false); + rServer->addResource(newPreset); + + slotResourceSelected(newPreset); // refresh and select our freshly renamed resource + + // Now blacklist the original file + if (rServer->resourceByName(originalPresetName)) { + rServer->removeResourceAndBlacklist(curPreset.data()); + } + + togglePresetRenameUIActive(false); // this returns the UI to its original state after saving + + slotUpdatePresetSettings(); // update visibility of dirty preset and icon +} + +void KisWdgSeExpr::slotUpdatePresetSettings() +{ + // hide options on UI if we are creating a brush preset from scratch to prevent confusion + if (m_isCreatingPresetFromScratch) { + m_widget->presetThumbnailicon->setVisible(false); + m_widget->dirtyPresetIndicatorButton->setVisible(false); + m_widget->reloadPresetButton->setVisible(false); + m_widget->saveBrushPresetButton->setVisible(false); + m_widget->renameBrushPresetButton->setVisible(false); + } else { + // In SeExpr's case, there is never a default preset -- amyspark + if (!m_currentPreset) { + return; + } + + bool isPresetDirty = m_currentPreset->isDirty(); + + m_widget->presetThumbnailicon->setVisible(true); + // don't need to reload or overwrite a clean preset + m_widget->dirtyPresetIndicatorButton->setVisible(isPresetDirty); + m_widget->reloadPresetButton->setVisible(isPresetDirty); + m_widget->saveBrushPresetButton->setEnabled(isPresetDirty); + m_widget->renameBrushPresetButton->setVisible(true); + } +} + +void KisWdgSeExpr::slotSaveBrushPreset() +{ + KisFilterConfigurationSP currentConfiguration = static_cast(configuration().data()); + + m_saveDialog->useNewPresetDialog(false); // this mostly just makes sure we keep the existing brush preset name when saving + m_saveDialog->setCurrentPreset(m_currentPreset); + m_saveDialog->setCurrentRenderConfiguration(currentConfiguration); + m_saveDialog->loadExistingThumbnail(); // This makes sure we use the existing preset icon when updating the existing brush preset + m_saveDialog->savePreset(); + + // refresh the view settings so the brush doesn't appear dirty + slotUpdatePresetSettings(); +} + +void KisWdgSeExpr::slotSaveNewBrushPreset() +{ + KisFilterConfigurationSP currentConfiguration = static_cast(configuration().data()); + + m_saveDialog->useNewPresetDialog(true); + m_saveDialog->setCurrentPreset(m_currentPreset); + m_saveDialog->setCurrentRenderConfiguration(currentConfiguration); + m_saveDialog->showDialog(); +} + +void KisWdgSeExpr::slotReloadPresetClicked() +{ + auto *rserver = KoResourceServerProvider::instance()->seExprScriptServer(); + auto preset = rserver->resourceByName(m_currentPreset->name()); + if (preset) { + preset->load(); + + KIS_ASSERT(!m_currentPreset->isDirty()); + + slotResourceSelected(preset); + } +} + void KisWdgSeExpr::isValid() { - SeExprExpressionContext expression(widget()->txtEditor->getExpr()); + QString script = m_widget->txtEditor->getExpr(); + SeExprExpressionContext expression(script); expression.setDesiredReturnType(SeExpr2::ExprType().FP(3)); expression.m_vars["u"] = new SeExprVariable(); expression.m_vars["v"] = new SeExprVariable(); expression.m_vars["w"] = new SeExprVariable(); expression.m_vars["h"] = new SeExprVariable(); - widget()->txtEditor->clearErrors(); + m_widget->txtEditor->clearErrors(); if (!expression.isValid()) { auto errors = expression.getErrors(); for (auto occurrence: errors) { QString message = ErrorMessages::message(occurrence.error); for (auto arg : occurrence.ids) { message = message.arg(QString::fromStdString(arg)); } - widget()->txtEditor->addError(occurrence.startPos, occurrence.endPos, message); + m_widget->txtEditor->addError(occurrence.startPos, occurrence.endPos, message); } } // Should not happen now, but I've left it for completeness's sake else if (!expression.returnType().isFP(3)) { QString type = QString::fromStdString(expression.returnType().toString()); - widget()->txtEditor->addError(1, 1, tr2i18n("Expected this script to output color, got '%1'").arg(type)); + m_widget->txtEditor->addError(1, 1, tr2i18n("Expected this script to output color, got '%1'").arg(type)); } else { - widget()->txtEditor->clearErrors(); + m_widget->txtEditor->clearErrors(); emit sigConfigurationUpdated(); + + if (m_currentPreset) + { + if (m_currentPreset->script() != m_widget->txtEditor->getExpr()) + { + m_currentPreset->setScript(m_widget->txtEditor->getExpr());m_currentPreset->setDirty(true); + } + slotUpdatePresetSettings(); + } } } diff --git a/plugins/generators/seexpr/kis_wdg_seexpr.h b/plugins/generators/seexpr/kis_wdg_seexpr.h index 2227c58143..ecbd9a9878 100644 --- a/plugins/generators/seexpr/kis_wdg_seexpr.h +++ b/plugins/generators/seexpr/kis_wdg_seexpr.h @@ -1,53 +1,67 @@ /* * This file is part of Krita * * Copyright (c) 2020 L. E. Segovia * * 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 KIS_WDG_SEEXPR_H #define KIS_WDG_SEEXPR_H #include #include - #include +#include "kis_wdg_seexpr_presets_save.h" + class Ui_WdgSeExpr; class KisWdgSeExpr : public KisConfigWidget { Q_OBJECT public: KisWdgSeExpr(QWidget* parent = 0); ~KisWdgSeExpr() override; public: inline const Ui_WdgSeExpr* widget() const; void setConfiguration(const KisPropertiesConfigurationSP) override; KisPropertiesConfigurationSP configuration() const override; private Q_SLOTS: void isValid(); - void slotScriptResourceSelected(KoResource* resource); + void slotResourceSelected(KoResource* resource); + void slotRenamePresetActivated(); + void slotRenamePresetDeactivated(); + void slotSaveRenameCurrentPreset(); + void slotUpdatePresetSettings(); + void slotSaveBrushPreset(); + void slotSaveNewBrushPreset(); + void slotReloadPresetClicked(); private: Ui_WdgSeExpr *m_widget; KisSignalCompressor updateCompressor; + KisSeExprScriptSP m_currentPreset; + KisWdgSeExprPresetsSave *m_saveDialog; + + bool m_isCreatingPresetFromScratch; + + void togglePresetRenameUIActive(bool isRenaming); }; #endif diff --git a/plugins/generators/seexpr/kis_wdg_seexpr_presets_save.cpp b/plugins/generators/seexpr/kis_wdg_seexpr_presets_save.cpp new file mode 100644 index 0000000000..c6f27dc775 --- /dev/null +++ b/plugins/generators/seexpr/kis_wdg_seexpr_presets_save.cpp @@ -0,0 +1,230 @@ +/* + * This file is part of Krita + * + * Copyright (c) 2020 L. E. Segovia + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include "kis_wdg_seexpr_presets_save.h" + +KisWdgSeExprPresetsSave::KisWdgSeExprPresetsSave(QWidget *parent) + : KisWdgSeExprSavePreset(parent), + m_currentPreset(nullptr) +{ + // we will default to reusing the previous preset thumbnail + // have that checked by default, hide the other elements, and load the last preset image + connect(loadExistingThumbnailButton, SIGNAL(clicked(bool)), this, SLOT(loadExistingThumbnail())); + connect(loadImageIntoThumbnailButton, SIGNAL(clicked(bool)), this, SLOT(loadImageFromFile())); + connect(loadRenderFromScriptButton, SIGNAL(clicked(bool)), this, SLOT(renderScriptToThumbnail())); + connect(clearPresetThumbnailButton, SIGNAL(clicked(bool)), presetThumbnailWidget, SLOT(clear())); + + connect(buttons, SIGNAL(accepted()), this, SLOT(savePreset())); + connect(buttons, SIGNAL(rejected()), this, SLOT(close())); +} + +KisWdgSeExprPresetsSave::~KisWdgSeExprPresetsSave() +{ +} + +void KisWdgSeExprPresetsSave::setCurrentPreset(KisSeExprScriptSP resource) +{ + m_currentPreset = resource; +} + +void KisWdgSeExprPresetsSave::setCurrentRenderConfiguration(KisFilterConfigurationSP config) +{ + m_currentConfiguration = config; +} + +void KisWdgSeExprPresetsSave::showDialog() +{ + setModal(true); + + // set the name of the current preset area. + KisSeExprScriptSP preset = m_currentPreset; + + // UI will look a bit different if we are saving a new preset + if (m_useNewPresetDialog) { + setWindowTitle(i18n("Save New SeExpr Preset")); + newPresetNameTextField->setVisible(true); + clearPresetThumbnailButton->setVisible(true); + loadImageIntoThumbnailButton->setVisible(true); + + if (preset) { + if (!preset->name().isEmpty()) { + newPresetNameTextField->setText(preset->name().append(" ").append(i18n("Copy"))); + } + else { + newPresetNameTextField->clear(); + } + } + + } else { + setWindowTitle(i18n("Save SeExpr Preset")); + newPresetNameTextField->setVisible(false); + + if(preset) { + newPresetNameTextField->setText(preset->name()); + } + } + + if (preset) { + presetThumbnailWidget->setPixmap(QPixmap::fromImage(preset->image())); + } + + show(); +} + +void KisWdgSeExprPresetsSave::loadImageFromFile() +{ + // create a dialog to retrieve an image file. + KoFileDialog dialog(0, KoFileDialog::OpenFile, "OpenDocument"); + dialog.setMimeTypeFilters(KisImportExportManager::supportedMimeTypes(KisImportExportManager::Import)); + dialog.setDefaultDir(QStandardPaths::writableLocation(QStandardPaths::HomeLocation)); + QString filename = dialog.filename(); // the filename() returns the entire path & file name, not just the file name + + if (!filename.isEmpty()) { // empty if "cancel" is pressed + // take that file and load it into the thumbnail are + const QImage imageToLoad(filename); + + presetThumbnailWidget->clear(); // clear the background in case our new image has transparency + presetThumbnailWidget->setPixmap(QPixmap::fromImage(imageToLoad)); + } +} + +void KisWdgSeExprPresetsSave::loadExistingThumbnail() +{ + presetThumbnailWidget->setPixmap(QPixmap::fromImage(m_currentPreset->image())); +} + +void KisWdgSeExprPresetsSave::renderScriptToThumbnail() +{ + if (m_currentConfiguration) + { + // TODO add some sort of progress marker? + KisPaintDeviceSP src = new KisPaintDevice(KoColorSpaceRegistry::instance()->rgb8()); + KisFillPainter fillPainter(src); + fillPainter.fillRect(0, 0, 256, 256, m_currentConfiguration); + + QImage thumbnail = src->convertToQImage(nullptr, 0, 0, 256, 256); + presetThumbnailWidget->setPixmap(QPixmap::fromImage(thumbnail)); + } +} + +void KisWdgSeExprPresetsSave::savePreset() +{ + KisSeExprScriptSP curPreset = m_currentPreset; + if (!curPreset) + return; + + KisSeExprScript* oldPreset = curPreset->clone(); // tags are not cloned with this + oldPreset->load(); + auto *rServer = KoResourceServerProvider::instance()->seExprScriptServer(); + QString saveLocation = rServer->saveLocation(); + + // if we are saving a new brush, use what we type in for the input + QString presetName = m_useNewPresetDialog ? newPresetNameTextField->text() : curPreset->name(); + QString currentPresetFileName = saveLocation + presetName.replace(" ", "_") + curPreset->defaultFileExtension(); + bool isSavingOverExistingPreset = rServer->resourceByName(presetName); + + // make a back up of the existing preset if we are saving over it + if (isSavingOverExistingPreset) { + QString currentDate = QDate::currentDate().toString(Qt::ISODate); + QString currentTime = QTime::currentTime().toString(Qt::ISODate).remove(QChar(':')); + QString presetFilename = saveLocation + presetName.replace(" ", "_") + "_backup_" + currentDate + "-" + currentTime + oldPreset->defaultFileExtension(); + oldPreset->setFilename(presetFilename); + oldPreset->setName(presetName); + oldPreset->setDirty(false); + oldPreset->setValid(true); + + // add backup resource to the blacklist + rServer->addResource(oldPreset); + rServer->removeResourceAndBlacklist(oldPreset); + + QStringList tags; + tags = rServer->assignedTagsList(curPreset.data()); + Q_FOREACH (const QString &tag, tags) { + rServer->addTag(oldPreset, tag); + } + } + + if (m_useNewPresetDialog) { + KisSeExprScript* newPreset = curPreset->clone(); + newPreset->setFilename(currentPresetFileName); + newPreset->setName(presetName); + newPreset->setImage(presetThumbnailWidget->pixmap()->toImage()); + newPreset->setScript(m_currentConfiguration->getString("script")); + newPreset->setDirty(false); + newPreset->setValid(true); + + // keep tags if we are saving over existing preset + if (isSavingOverExistingPreset) { + QStringList tags; + tags = rServer->assignedTagsList(curPreset.data()); + Q_FOREACH (const QString &tag, tags) { + rServer->addTag(newPreset, tag); + } + } + + rServer->addResource(newPreset); + + // trying to get brush preset to load after it is created + emit resourceSelected(newPreset); + } else { // saving a preset that is replacing an existing one + + if (curPreset->filename().contains(saveLocation) == false || curPreset->filename().contains(presetName) == false) { + rServer->removeResourceAndBlacklist(curPreset.data()); + curPreset->setFilename(currentPresetFileName); + curPreset->setName(presetName); + } + + if (!rServer->resourceByFilename(curPreset->filename())) { + // this is necessary so that we can get the preset afterwards. + rServer->addResource(curPreset.data(), false, false); + rServer->removeFromBlacklist(curPreset.data()); + } + if (curPreset->image().isNull()) { + curPreset->setImage(presetThumbnailWidget->pixmap()->toImage()); + } + + curPreset->setScript(m_currentConfiguration->getString("script")); + + // we should not load() the brush right after saving because it will reset all our saved + // eraser size and opacity values + curPreset->save(); + } + + // HACK ALERT! the server does not notify the observers + // automatically, so we need to call theupdate manually! + rServer->tagCategoryMembersChanged(); + + close(); // we are done... so close the save brush dialog +} + +void KisWdgSeExprPresetsSave::useNewPresetDialog(bool show) +{ + m_useNewPresetDialog = show; +} + +#include "moc_kis_wdg_seexpr_presets_save.cpp" diff --git a/plugins/generators/seexpr/kis_wdg_seexpr_presets_save.h b/plugins/generators/seexpr/kis_wdg_seexpr_presets_save.h new file mode 100644 index 0000000000..98a87fcc1a --- /dev/null +++ b/plugins/generators/seexpr/kis_wdg_seexpr_presets_save.h @@ -0,0 +1,77 @@ +/* + * This file is part of Krita + * + * Copyright (c) 2020 L. E. Segovia + * + * 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 KIS_WDG_SEEXPR_PRESETS_SAVE_H +#define KIS_WDG_SEEXPR_PRESETS_SAVE_H + +#include +#include + +#include "ui_wdgseexprsavepreset.h" + +/** + * Dialog for saving SeExpr presets. + * + * Based on Scott Petrovic's KisPaintOpPresetSaveDialog. + */ +class KisWdgSeExprSavePreset : public QDialog, public Ui::WdgSeExprSavePreset +{ + Q_OBJECT + +public: + KisWdgSeExprSavePreset(QWidget *parent) + : QDialog(parent) + { + setupUi(this); + } +}; + +class KisWdgSeExprPresetsSave : public KisWdgSeExprSavePreset +{ + Q_OBJECT + +public: + KisWdgSeExprPresetsSave(QWidget *parent); + virtual ~KisWdgSeExprPresetsSave(); + + void showDialog(); + + /// determines if we should show the save as dialog (true) or save in the background (false) + void useNewPresetDialog(bool show); + +Q_SIGNALS: + // Triggers after resource is saved + void resourceSelected(KoResource* resource); + +public Q_SLOTS: + void setCurrentPreset(KisSeExprScriptSP resource); + void setCurrentRenderConfiguration(KisFilterConfigurationSP config); + void loadImageFromFile(); + void savePreset(); + void loadExistingThumbnail(); + void renderScriptToThumbnail(); + +private: + bool m_useNewPresetDialog; + KisSeExprScriptSP m_currentPreset; + KisFilterConfigurationSP m_currentConfiguration; +}; + +#endif diff --git a/plugins/generators/seexpr/wdgseexpr.ui b/plugins/generators/seexpr/wdgseexpr.ui index ce98a97ccd..88a5a9da3d 100644 --- a/plugins/generators/seexpr/wdgseexpr.ui +++ b/plugins/generators/seexpr/wdgseexpr.ui @@ -1,129 +1,420 @@ WdgSeExpr 0 0 500 480 500 0 0 0 0 0 0 - 0 + 1 Script - + 0 0 Options + + + + + + + + + 0 + 0 + + + + + 55 + 55 + + + + + + + true + + + + + + + Qt::Vertical + + + + 20 + 0 + + + + + + + + + + 0 + + + QLayout::SetMinimumSize + + + + + 10 + + + QLayout::SetMinimumSize + + + + + + 10 + 75 + true + + + + + + + + + 24 + 24 + + + + + 24 + 24 + + + + + + + + + + + + 180 + 0 + + + + + + + + + 0 + 0 + + + + Save + + + + + + + Cancel + + + + + + + Qt::Horizontal + + + + 20 + 20 + + + + + + + + + + Qt::Vertical + + + + 20 + 0 + + + + + + + + + + Qt::Horizontal + + + QSizePolicy::MinimumExpanding + + + + 0 + 0 + + + + + + + + 0 + + + + + Save New Brush Preset... + + + + + + + Overwrite Brush + + + + + + + 6 + + + 0 + + + 6 + + + + + Qt::Horizontal + + + + 0 + 23 + + + + + + + + + 0 + 0 + + + + + 23 + 23 + + + + + 23 + 23 + + + + + + + + + + + + 0 + 0 + + + + + 23 + 23 + + + + + 23 + 23 + + + + + + + true + + + + + + + + + Qt::Vertical + + + + 20 + 1 + + + + + + + + Qt::Vertical + + + + 20 + 0 + + + + + + + + 0 5 true 0 0 - 98 - 28 + 466 + 172 0 0 0 4 KisSeExprScriptChooser QWidget
kis_seexpr_script_chooser.h
1
ExprControlCollection QWidget
SeExpr2/UI/ExprControlCollection.h
1
ExprEditor QWidget
SeExpr2/UI/ExprEditor.h
1
diff --git a/plugins/generators/seexpr/wdgseexprsavepreset.ui b/plugins/generators/seexpr/wdgseexprsavepreset.ui new file mode 100644 index 0000000000..bce89dbb98 --- /dev/null +++ b/plugins/generators/seexpr/wdgseexprsavepreset.ui @@ -0,0 +1,161 @@ + + + WdgSeExprSavePreset + + + + 0 + 0 + 457 + 332 + + + + + 0 + 0 + + + + Save SeExpr Preset + + + + 13 + + + + + + + Preset name: + + + + + + + + 220 + 0 + + + + + + + + + + 18 + + + + + 2 + + + + + + 0 + 0 + + + + + 200 + 200 + + + + + 200 + 200 + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + 6 + + + + + Load Existing Thumbnail + + + + + + + Load Image + + + + + + + &Render Script to Thumbnail + + + + + + + Clear Thumbnail + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + + QDialogButtonBox::Cancel|QDialogButtonBox::Save + + + + + + + +