diff --git a/libs/ui/KisImportExportManager.cpp b/libs/ui/KisImportExportManager.cpp
index 8335afaf94..112fd6a832 100644
--- a/libs/ui/KisImportExportManager.cpp
+++ b/libs/ui/KisImportExportManager.cpp
@@ -1,697 +1,703 @@
/*
* Copyright (C) 2016 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 "KisImportExportManager.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
#include
#include
#include
#include
#include "kis_config.h"
#include "KisImportExportFilter.h"
#include "KisDocument.h"
#include
#include
#include "kis_painter.h"
#include "kis_guides_config.h"
#include "kis_grid_config.h"
#include "kis_popup_button.h"
#include
#include "kis_async_action_feedback.h"
#include "KisReferenceImagesLayer.h"
// static cache for import and export mimetypes
QStringList KisImportExportManager::m_importMimeTypes;
QStringList KisImportExportManager::m_exportMimeTypes;
class Q_DECL_HIDDEN KisImportExportManager::Private
{
public:
KoUpdaterPtr updater;
QString cachedExportFilterMimeType;
QSharedPointer cachedExportFilter;
};
struct KisImportExportManager::ConversionResult {
ConversionResult()
{
}
ConversionResult(const QFuture &futureStatus)
: m_isAsync(true),
m_futureStatus(futureStatus)
{
}
ConversionResult(KisImportExportErrorCode status)
: m_isAsync(false),
m_status(status)
{
}
bool isAsync() const {
return m_isAsync;
}
QFuture futureStatus() const {
// if the result is not async, then it means some failure happened,
// just return a cancelled future
KIS_SAFE_ASSERT_RECOVER_NOOP(m_isAsync || !m_status.isOk());
return m_futureStatus;
}
KisImportExportErrorCode status() const {
return m_status;
}
void setStatus(KisImportExportErrorCode value) {
m_status = value;
}
private:
bool m_isAsync = false;
QFuture m_futureStatus;
KisImportExportErrorCode m_status = ImportExportCodes::InternalError;
};
KisImportExportManager::KisImportExportManager(KisDocument* document)
: m_document(document)
, d(new Private)
{
}
KisImportExportManager::~KisImportExportManager()
{
delete d;
}
KisImportExportErrorCode KisImportExportManager::importDocument(const QString& location, const QString& mimeType)
{
ConversionResult result = convert(Import, location, location, mimeType, false, 0, false);
KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(!result.isAsync(), ImportExportCodes::InternalError);
return result.status();
}
KisImportExportErrorCode KisImportExportManager::exportDocument(const QString& location, const QString& realLocation, const QByteArray& mimeType, bool showWarnings, KisPropertiesConfigurationSP exportConfiguration)
{
ConversionResult result = convert(Export, location, realLocation, mimeType, showWarnings, exportConfiguration, false);
KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(!result.isAsync(), ImportExportCodes::InternalError);
return result.status();
}
QFuture KisImportExportManager::exportDocumentAsyc(const QString &location, const QString &realLocation, const QByteArray &mimeType,
KisImportExportErrorCode &status, bool showWarnings, KisPropertiesConfigurationSP exportConfiguration)
{
ConversionResult result = convert(Export, location, realLocation, mimeType, showWarnings, exportConfiguration, true);
KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(result.isAsync() ||
!result.status().isOk(), QFuture());
status = result.status();
return result.futureStatus();
}
// The static method to figure out to which parts of the
// graph this mimetype has a connection to.
QStringList KisImportExportManager::supportedMimeTypes(Direction direction)
{
// Find the right mimetype by the extension
QSet mimeTypes;
// mimeTypes << KisDocument::nativeFormatMimeType() << "application/x-krita-paintoppreset" << "image/openraster";
if (direction == KisImportExportManager::Import) {
if (m_importMimeTypes.isEmpty()) {
QListlist = KoJsonTrader::instance()->query("Krita/FileFilter", "");
Q_FOREACH(QPluginLoader *loader, list) {
QJsonObject json = loader->metaData().value("MetaData").toObject();
Q_FOREACH(const QString &mimetype, json.value("X-KDE-Import").toString().split(",", QString::SkipEmptyParts)) {
//qDebug() << "Adding import mimetype" << mimetype << KisMimeDatabase::descriptionForMimeType(mimetype) << "from plugin" << loader;
mimeTypes << mimetype;
}
}
qDeleteAll(list);
m_importMimeTypes = mimeTypes.toList();
}
return m_importMimeTypes;
}
else if (direction == KisImportExportManager::Export) {
if (m_exportMimeTypes.isEmpty()) {
QListlist = KoJsonTrader::instance()->query("Krita/FileFilter", "");
Q_FOREACH(QPluginLoader *loader, list) {
QJsonObject json = loader->metaData().value("MetaData").toObject();
Q_FOREACH(const QString &mimetype, json.value("X-KDE-Export").toString().split(",", QString::SkipEmptyParts)) {
//qDebug() << "Adding export mimetype" << mimetype << KisMimeDatabase::descriptionForMimeType(mimetype) << "from plugin" << loader;
mimeTypes << mimetype;
}
}
qDeleteAll(list);
m_exportMimeTypes = mimeTypes.toList();
}
return m_exportMimeTypes;
}
return QStringList();
}
KisImportExportFilter *KisImportExportManager::filterForMimeType(const QString &mimetype, KisImportExportManager::Direction direction)
{
int weight = -1;
KisImportExportFilter *filter = 0;
QListlist = KoJsonTrader::instance()->query("Krita/FileFilter", "");
Q_FOREACH(QPluginLoader *loader, list) {
QJsonObject json = loader->metaData().value("MetaData").toObject();
QString directionKey = direction == Export ? "X-KDE-Export" : "X-KDE-Import";
if (json.value(directionKey).toString().split(",", QString::SkipEmptyParts).contains(mimetype)) {
KLibFactory *factory = qobject_cast(loader->instance());
if (!factory) {
warnUI << loader->errorString();
continue;
}
QObject* obj = factory->create(0);
if (!obj || !obj->inherits("KisImportExportFilter")) {
delete obj;
continue;
}
KisImportExportFilter *f = qobject_cast(obj);
if (!f) {
delete obj;
continue;
}
int w = json.value("X-KDE-Weight").toInt();
if (w > weight) {
delete filter;
filter = f;
f->setObjectName(loader->fileName());
weight = w;
}
}
}
qDeleteAll(list);
if (filter) {
filter->setMimeType(mimetype);
}
return filter;
}
bool KisImportExportManager::batchMode(void) const
{
return m_document->fileBatchMode();
}
void KisImportExportManager::setUpdater(KoUpdaterPtr updater)
{
d->updater = updater;
}
QString KisImportExportManager::askForAudioFileName(const QString &defaultDir, QWidget *parent)
{
KoFileDialog dialog(parent, KoFileDialog::ImportFiles, "ImportAudio");
if (!defaultDir.isEmpty()) {
dialog.setDefaultDir(defaultDir);
}
QStringList mimeTypes;
mimeTypes << "audio/mpeg";
mimeTypes << "audio/ogg";
mimeTypes << "audio/vorbis";
mimeTypes << "audio/vnd.wave";
mimeTypes << "audio/flac";
dialog.setMimeTypeFilters(mimeTypes);
dialog.setCaption(i18nc("@titile:window", "Open Audio"));
return dialog.filename();
}
KisImportExportManager::ConversionResult KisImportExportManager::convert(KisImportExportManager::Direction direction, const QString &location, const QString& realLocation, const QString &mimeType, bool showWarnings, KisPropertiesConfigurationSP exportConfiguration, bool isAsync)
{
// export configuration is supported for export only
KIS_SAFE_ASSERT_RECOVER_NOOP(direction == Export || !bool(exportConfiguration));
QString typeName = mimeType;
if (typeName.isEmpty()) {
typeName = KisMimeDatabase::mimeTypeForFile(location, direction == KisImportExportManager::Export ? false : true);
}
QSharedPointer filter;
/**
* Fetching a filter from the registry is a really expensive operation,
* because it blocks all the threads. Cache the filter if possible.
*/
if (direction == KisImportExportManager::Export &&
d->cachedExportFilter &&
d->cachedExportFilterMimeType == typeName) {
filter = d->cachedExportFilter;
} else {
filter = toQShared(filterForMimeType(typeName, direction));
if (direction == Export) {
d->cachedExportFilter = filter;
d->cachedExportFilterMimeType = typeName;
}
}
if (!filter) {
return KisImportExportErrorCode(ImportExportCodes::FileFormatIncorrect);
}
filter->setFilename(location);
filter->setRealFilename(realLocation);
filter->setBatchMode(batchMode());
filter->setMimeType(typeName);
if (!d->updater.isNull()) {
// WARNING: The updater is not guaranteed to be persistent! If you ever want
// to add progress reporting to "Save also as .kra", make sure you create
// a separate KoProgressUpdater for that!
// WARNING2: the failsafe completion of the updater happens in the destructor
// the filter.
filter->setUpdater(d->updater);
}
QByteArray from, to;
if (direction == Export) {
from = m_document->nativeFormatMimeType();
to = typeName.toLatin1();
}
else {
from = typeName.toLatin1();
to = m_document->nativeFormatMimeType();
}
KIS_ASSERT_RECOVER_RETURN_VALUE(
direction == Import || direction == Export,
KisImportExportErrorCode(ImportExportCodes::InternalError)); // "bad conversion graph"
ConversionResult result = KisImportExportErrorCode(ImportExportCodes::OK);
if (direction == Import) {
KisUsageLogger::log(QString("Importing %1 to %2. Location: %3. Real location: %4. Batchmode: %5")
.arg(QString::fromLatin1(from))
.arg(QString::fromLatin1(to))
.arg(location)
.arg(realLocation)
.arg(batchMode()));
// async importing is not yet supported!
KIS_SAFE_ASSERT_RECOVER_NOOP(!isAsync);
if (0 && !batchMode()) {
KisAsyncActionFeedback f(i18n("Opening document..."), 0);
result = f.runAction(std::bind(&KisImportExportManager::doImport, this, location, filter));
} else {
result = doImport(location, filter);
}
}
else /* if (direction == Export) */ {
if (!exportConfiguration) {
exportConfiguration = filter->lastSavedConfiguration(from, to);
}
if (exportConfiguration) {
fillStaticExportConfigurationProperties(exportConfiguration);
}
bool alsoAsKra = false;
bool askUser = askUserAboutExportConfiguration(filter, exportConfiguration,
from, to,
batchMode(), showWarnings,
&alsoAsKra);
if (!batchMode() && !askUser) {
return KisImportExportErrorCode(ImportExportCodes::Cancelled);
}
KisUsageLogger::log(QString("Converting from %1 to %2. Location: %3. Real location: %4. Batchmode: %5. Configuration: %6")
.arg(QString::fromLatin1(from))
.arg(QString::fromLatin1(to))
.arg(location)
.arg(realLocation)
.arg(batchMode())
.arg(exportConfiguration ? exportConfiguration->toXML() : "none"));
if (isAsync) {
result = QtConcurrent::run(std::bind(&KisImportExportManager::doExport, this, location, filter, exportConfiguration, alsoAsKra));
// we should explicitly report that the exporting has been initiated
result.setStatus(ImportExportCodes::OK);
} else if (!batchMode()) {
KisAsyncActionFeedback f(i18n("Saving document..."), 0);
result = f.runAction(std::bind(&KisImportExportManager::doExport, this, location, filter, exportConfiguration, alsoAsKra));
} else {
result = doExport(location, filter, exportConfiguration, alsoAsKra);
}
if (exportConfiguration && !batchMode() && showWarnings) {
KisConfig(false).setExportConfiguration(typeName, exportConfiguration);
}
}
return result;
}
void KisImportExportManager::fillStaticExportConfigurationProperties(KisPropertiesConfigurationSP exportConfiguration, KisImageSP image)
{
KisPaintDeviceSP dev = image->projection();
const KoColorSpace* cs = dev->colorSpace();
const bool isThereAlpha =
KisPainter::checkDeviceHasTransparency(image->projection());
exportConfiguration->setProperty(KisImportExportFilter::ImageContainsTransparencyTag, isThereAlpha);
exportConfiguration->setProperty(KisImportExportFilter::ColorModelIDTag, cs->colorModelId().id());
exportConfiguration->setProperty(KisImportExportFilter::ColorDepthIDTag, cs->colorDepthId().id());
const bool sRGB =
(cs->profile()->name().contains(QLatin1String("srgb"), Qt::CaseInsensitive) &&
!cs->profile()->name().contains(QLatin1String("g10")));
exportConfiguration->setProperty(KisImportExportFilter::sRGBTag, sRGB);
}
void KisImportExportManager::fillStaticExportConfigurationProperties(KisPropertiesConfigurationSP exportConfiguration)
{
return fillStaticExportConfigurationProperties(exportConfiguration, m_document->image());
}
bool KisImportExportManager::askUserAboutExportConfiguration(
QSharedPointer filter,
KisPropertiesConfigurationSP exportConfiguration,
const QByteArray &from,
const QByteArray &to,
const bool batchMode,
const bool showWarnings,
bool *alsoAsKra)
{
// prevents the animation renderer from running this code
const QString mimeUserDescription = KisMimeDatabase::descriptionForMimeType(to);
QStringList warnings;
QStringList errors;
{
KisPreExportChecker checker;
checker.check(m_document->image(), filter->exportChecks());
warnings = checker.warnings();
errors = checker.errors();
}
KisConfigWidget *wdg = 0;
if (QThread::currentThread() == qApp->thread()) {
wdg = filter->createConfigurationWidget(0, from, to);
+
+ if(wdg) {
+ KisPart *kisPart = KisPart::instance();
+ KisViewManager *manager = kisPart->currentMainwindow()->viewManager();
+ wdg->setView(manager);
+ }
}
// Extra checks that cannot be done by the checker, because the checker only has access to the image.
if (!m_document->assistants().isEmpty() && to != m_document->nativeFormatMimeType()) {
warnings.append(i18nc("image conversion warning", "The image contains assistants. The assistants will not be saved."));
}
if (m_document->referenceImagesLayer() && m_document->referenceImagesLayer()->shapeCount() > 0 && to != m_document->nativeFormatMimeType()) {
warnings.append(i18nc("image conversion warning", "The image contains reference images. The reference images will not be saved."));
}
if (m_document->guidesConfig().hasGuides() && to != m_document->nativeFormatMimeType()) {
warnings.append(i18nc("image conversion warning", "The image contains guides. The guides will not be saved."));
}
if (!m_document->gridConfig().isDefault() && to != m_document->nativeFormatMimeType()) {
warnings.append(i18nc("image conversion warning", "The image contains a custom grid configuration. The configuration will not be saved."));
}
if (!batchMode && !errors.isEmpty()) {
QString error = ""
+ i18n("Error: cannot save this image as a %1.", mimeUserDescription)
+ " Reasons:
"
+ "";
Q_FOREACH(const QString &w, errors) {
error += "\n- " + w + "
";
}
error += "
";
QMessageBox::critical(KisPart::instance()->currentMainwindow(), i18nc("@title:window", "Krita: Export Error"), error);
return false;
}
if (!batchMode && (wdg || !warnings.isEmpty())) {
KoDialog dlg;
dlg.setButtons(KoDialog::Ok | KoDialog::Cancel);
dlg.setWindowTitle(mimeUserDescription);
QWidget *page = new QWidget(&dlg);
QVBoxLayout *layout = new QVBoxLayout(page);
if (showWarnings && !warnings.isEmpty()) {
QHBoxLayout *hLayout = new QHBoxLayout();
QLabel *labelWarning = new QLabel();
labelWarning->setPixmap(KisIconUtils::loadIcon("warning").pixmap(32, 32));
hLayout->addWidget(labelWarning);
KisPopupButton *bn = new KisPopupButton(0);
bn->setText(i18nc("Keep the extra space at the end of the sentence, please", "Warning: saving as %1 will lose information from your image. ", mimeUserDescription));
hLayout->addWidget(bn);
layout->addLayout(hLayout);
QTextBrowser *browser = new QTextBrowser();
browser->setMinimumWidth(bn->width());
bn->setPopupWidget(browser);
QString warning = ""
+ i18n("You will lose information when saving this image as a %1.", mimeUserDescription);
if (warnings.size() == 1) {
warning += " Reason:
";
}
else {
warning += " Reasons:
";
}
warning += "";
Q_FOREACH(const QString &w, warnings) {
warning += "\n- " + w + "
";
}
warning += "
";
browser->setHtml(warning);
}
if (wdg) {
QGroupBox *box = new QGroupBox(i18n("Options"));
QVBoxLayout *boxLayout = new QVBoxLayout(box);
wdg->setConfiguration(exportConfiguration);
boxLayout->addWidget(wdg);
layout->addWidget(box);
}
QCheckBox *chkAlsoAsKra = 0;
if (showWarnings && !warnings.isEmpty()) {
chkAlsoAsKra = new QCheckBox(i18n("Also save your image as a Krita file."));
chkAlsoAsKra->setChecked(KisConfig(true).readEntry("AlsoSaveAsKra", false));
layout->addWidget(chkAlsoAsKra);
}
dlg.setMainWidget(page);
dlg.resize(dlg.minimumSize());
if (showWarnings || wdg) {
if (!dlg.exec()) {
return false;
}
}
*alsoAsKra = false;
if (chkAlsoAsKra) {
KisConfig(false).writeEntry("AlsoSaveAsKra", chkAlsoAsKra->isChecked());
*alsoAsKra = chkAlsoAsKra->isChecked();
}
if (wdg) {
*exportConfiguration = *wdg->configuration();
}
}
return true;
}
KisImportExportErrorCode KisImportExportManager::doImport(const QString &location, QSharedPointer filter)
{
QFile file(location);
if (!file.exists()) {
return ImportExportCodes::FileNotExist;
}
if (filter->supportsIO() && !file.open(QFile::ReadOnly)) {
return KisImportExportErrorCode(KisImportExportErrorCannotRead(file.error()));
}
KisImportExportErrorCode status = filter->convert(m_document, &file, KisPropertiesConfigurationSP());
if (file.isOpen()) {
file.close();
}
return status;
}
KisImportExportErrorCode KisImportExportManager::doExport(const QString &location, QSharedPointer filter, KisPropertiesConfigurationSP exportConfiguration, bool alsoAsKra)
{
KisImportExportErrorCode status =
doExportImpl(location, filter, exportConfiguration);
if (alsoAsKra && status.isOk()) {
QString kraLocation = location + ".kra";
QByteArray mime = m_document->nativeFormatMimeType();
QSharedPointer filter(
filterForMimeType(QString::fromLatin1(mime), Export));
KIS_SAFE_ASSERT_RECOVER_NOOP(filter);
if (filter) {
filter->setFilename(kraLocation);
KisPropertiesConfigurationSP kraExportConfiguration =
filter->lastSavedConfiguration(mime, mime);
status = doExportImpl(kraLocation, filter, kraExportConfiguration);
} else {
status = ImportExportCodes::FileFormatIncorrect;
}
}
return status;
}
// Temporary workaround until QTBUG-57299 is fixed.
#ifndef Q_OS_WIN
#define USE_QSAVEFILE
#endif
KisImportExportErrorCode KisImportExportManager::doExportImpl(const QString &location, QSharedPointer filter, KisPropertiesConfigurationSP exportConfiguration)
{
#ifdef USE_QSAVEFILE
QSaveFile file(location);
file.setDirectWriteFallback(true);
if (filter->supportsIO() && !file.open(QFile::WriteOnly)) {
#else
QFileInfo fi(location);
QTemporaryFile file(fi.absolutePath() + ".XXXXXX.kra");
if (filter->supportsIO() && !file.open()) {
#endif
KisImportExportErrorCannotWrite result(file.error());
#ifdef USE_QSAVEFILE
file.cancelWriting();
#endif
return result;
}
KisImportExportErrorCode status = filter->convert(m_document, &file, exportConfiguration);
if (filter->supportsIO()) {
if (!status.isOk()) {
#ifdef USE_QSAVEFILE
file.cancelWriting();
#endif
} else {
#ifdef USE_QSAVEFILE
if (!file.commit()) {
qWarning() << "Could not commit QSaveFile";
status = KisImportExportErrorCannotWrite(file.error());
}
#else
file.flush();
file.close();
QFile target(location);
if (target.exists()) {
// There should already be a .kra~ backup
target.remove();
}
if (!file.copy(location)) {
file.setAutoRemove(false);
return KisImportExportErrorCannotWrite(file.error());
}
#endif
}
}
// Do some minimal verification
QString verificationResult = filter->verify(location);
qDebug() << verificationResult;
if (!verificationResult.isEmpty()) {
status = KisImportExportErrorCode(ImportExportCodes::ErrorWhileWriting);
m_document->setErrorMessage(verificationResult);
}
return status;
}
#include
diff --git a/plugins/impex/brush/kis_brush_export.cpp b/plugins/impex/brush/kis_brush_export.cpp
index 530d159374..0a28862b41 100644
--- a/plugins/impex/brush/kis_brush_export.cpp
+++ b/plugins/impex/brush/kis_brush_export.cpp
@@ -1,286 +1,292 @@
/*
* Copyright (c) 2016 Boudewijn Rempt
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include "kis_brush_export.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
struct KisBrushExportOptions {
qreal spacing;
bool mask;
int brushStyle;
int dimensions;
qint32 ranks[KisPipeBrushParasite::MaxDim];
qint32 selectionModes[KisPipeBrushParasite::MaxDim];
QString name;
};
K_PLUGIN_FACTORY_WITH_JSON(KisBrushExportFactory, "krita_brush_export.json", registerPlugin();)
KisBrushExport::KisBrushExport(QObject *parent, const QVariantList &) : KisImportExportFilter(parent)
{
}
KisBrushExport::~KisBrushExport()
{
}
KisImportExportErrorCode KisBrushExport::convert(KisDocument *document, QIODevice *io, KisPropertiesConfigurationSP configuration)
{
// XXX: Loading the parasite itself was commented out -- needs investigation
// KisAnnotationSP annotation = document->savingImage()->annotation("ImagePipe Parasite");
// KisPipeBrushParasite parasite;
// if (annotation) {
// QBuffer buf(const_cast(&annotation->annotation()));
// buf.open(QBuffer::ReadOnly);
// parasite.loadFromDevice(&buf);
// buf.close();
// }
KisBrushExportOptions exportOptions;
if (document->savingImage()->dynamicPropertyNames().contains("brushspacing")) {
exportOptions.spacing = document->savingImage()->property("brushspacing").toFloat();
}
else {
exportOptions.spacing = configuration->getInt("spacing");
}
if (!configuration->getString("name").isEmpty()) {
exportOptions.name = configuration->getString("name");
}
else {
exportOptions.name = document->savingImage()->objectName();
}
exportOptions.mask = configuration->getBool("mask");
exportOptions.brushStyle = configuration->getInt("brushStyle");
exportOptions.dimensions = configuration->getInt("dimensions");
for (int i = 0; i < KisPipeBrushParasite::MaxDim; ++i) {
exportOptions.selectionModes[i] = configuration->getInt("selectionMode" + QString::number(i));
exportOptions.ranks[i] = configuration->getInt("rank" + QString::number(i));
}
KisGbrBrush *brush = 0;
if (mimeType() == "image/x-gimp-brush") {
brush = new KisGbrBrush(filename());
}
else if (mimeType() == "image/x-gimp-brush-animated") {
brush = new KisImagePipeBrush(filename());
}
else {
return ImportExportCodes::FileFormatIncorrect;
}
qApp->processEvents(); // For vector layers to be updated
QRect rc = document->savingImage()->bounds();
brush->setSpacing(exportOptions.spacing);
KisImagePipeBrush *pipeBrush = dynamic_cast(brush);
if (pipeBrush) {
// Create parasite. XXX: share with KisCustomBrushWidget
QVector< QVector > devices;
devices.push_back(QVector());
KoProperties properties;
properties.setProperty("visible", true);
QList layers = document->savingImage()->root()->childNodes(QStringList("KisLayer"), properties);
Q_FOREACH (KisNodeSP node, layers) {
// push_front to behave exactly as gimp for gih creation
devices[0].push_front(node->projection().data());
}
QVector modes;
for (int i = 0; i < KisPipeBrushParasite::MaxDim; ++i) {
switch (exportOptions.selectionModes[i]) {
case 0: modes.push_back(KisParasite::Constant); break;
case 1: modes.push_back(KisParasite::Random); break;
case 2: modes.push_back(KisParasite::Incremental); break;
case 3: modes.push_back(KisParasite::Pressure); break;
case 4: modes.push_back(KisParasite::Angular); break;
case 5: modes.push_back(KisParasite::Velocity); break;
default: modes.push_back(KisParasite::Incremental);
}
}
KisPipeBrushParasite parasite;
parasite.dim = exportOptions.dimensions;
parasite.ncells = devices.at(0).count();
+ int maxRanks = 0;
for (int i = 0; i < KisPipeBrushParasite::MaxDim; ++i) {
// ### This can mask some bugs, be careful here in the future
parasite.rank[i] = exportOptions.ranks[i];
parasite.selection[i] = modes.at(i);
+ maxRanks += exportOptions.ranks[i];
+ }
+
+ if (maxRanks > layers.count()) {
+ return ImportExportCodes::FileFormatIncorrect;
}
// XXX needs movement!
parasite.setBrushesCount();
pipeBrush->setParasite(parasite);
pipeBrush->setDevices(devices, rc.width(), rc.height());
if(exportOptions.mask) {
QVector brushes = pipeBrush->brushes();
Q_FOREACH(KisGbrBrush* brush, brushes) {
brush->setHasColor(false);
}
}
}
else {
if (exportOptions.mask){
QImage image = document->savingImage()->projection()->convertToQImage(0, 0, 0, rc.width(), rc.height(), KoColorConversionTransformation::internalRenderingIntent(), KoColorConversionTransformation::internalConversionFlags());
brush->setImage(image);
brush->setBrushTipImage(image);
} else {
brush->initFromPaintDev(document->savingImage()->projection(),0,0,rc.width(), rc.height());
}
}
brush->setName(exportOptions.name);
// brushes are created after devices are loaded, mask mode must b after that
brush->setUseColorAsMask(exportOptions.mask);
brush->setWidth(rc.width());
brush->setHeight(rc.height());
if (brush->saveToDevice(io)) {
return ImportExportCodes::OK;
}
else {
return ImportExportCodes::Failure;
}
}
KisPropertiesConfigurationSP KisBrushExport::defaultConfiguration(const QByteArray &/*from*/, const QByteArray &/*to*/) const
{
KisPropertiesConfigurationSP cfg = new KisPropertiesConfiguration();
cfg->setProperty("spacing", 1.0);
cfg->setProperty("name", "");
cfg->setProperty("mask", true);
cfg->setProperty("brushStyle", 0);
cfg->setProperty("dimensions", 1);
for (int i = 0; i < KisPipeBrushParasite::MaxDim; ++i) {
cfg->setProperty("selectionMode" + QString::number(i), 2);
cfg->getInt("rank" + QString::number(i), 0);
}
return cfg;
}
KisConfigWidget *KisBrushExport::createConfigurationWidget(QWidget *parent, const QByteArray &/*from*/, const QByteArray &to) const
{
KisWdgOptionsBrush *wdg = new KisWdgOptionsBrush(parent);
if (to == "image/x-gimp-brush") {
wdg->groupBox->setVisible(false);
wdg->animStyleGroup->setVisible(false);
}
else if (to == "image/x-gimp-brush-animated") {
wdg->groupBox->setVisible(true);
wdg->animStyleGroup->setVisible(true);
}
// preload gih name with chosen filename
QFileInfo fileLocation(filename());
wdg->nameLineEdit->setText(fileLocation.baseName());
return wdg;
}
void KisBrushExport::initializeCapabilities()
{
QList > supportedColorModels;
supportedColorModels << QPair()
<< QPair(RGBAColorModelID, Integer8BitsColorDepthID)
<< QPair(GrayAColorModelID, Integer8BitsColorDepthID);
addSupportedColorModels(supportedColorModels, "Gimp Brushes");
if (mimeType() == "image/x-gimp-brush-animated") {
addCapability(KisExportCheckRegistry::instance()->get("MultiLayerCheck")->create(KisExportCheckBase::SUPPORTED));
}
}
void KisWdgOptionsBrush::setConfiguration(const KisPropertiesConfigurationSP cfg)
{
spacingWidget->setSpacing(false, cfg->getDouble("spacing"));
if(nameLineEdit->text().isEmpty()){
nameLineEdit->setText(cfg->getString("name"));
}
colorAsMask->setChecked(cfg->getBool("mask"));
brushStyle->setCurrentIndex(cfg->getInt("brushStyle"));
dimensionSpin->setValue(cfg->getInt("dimensions"));
QLayoutItem *item;
BrushPipeSelectionModeHelper *bp;
for (int i = 0; i < dimensionSpin->maximum(); ++i) {
if((item = dimRankLayout->itemAt(i)) != 0) {
bp = dynamic_cast(item->widget());
bp->cmbSelectionMode.setCurrentIndex(cfg->getInt("selectionMode" + QString::number(i)));
bp->rank.setValue(cfg->getInt("rank" + QString::number(i)));
}
}
}
KisPropertiesConfigurationSP KisWdgOptionsBrush::configuration() const
{
KisPropertiesConfigurationSP cfg = new KisPropertiesConfiguration();
cfg->setProperty("spacing", spacingWidget->spacing());
cfg->setProperty("name", nameLineEdit->text());
cfg->setProperty("mask", colorAsMask->isChecked());
cfg->setProperty("brushStyle", brushStyle->currentIndex());
cfg->setProperty("dimensions", dimensionSpin->value());
QLayoutItem *item;
BrushPipeSelectionModeHelper *bp;
for (int i = 0; i < dimensionSpin->maximum(); ++i) {
if((item = dimRankLayout->itemAt(i)) != 0) {
bp = dynamic_cast(item->widget());
cfg->setProperty("selectionMode" + QString::number(i), bp->cmbSelectionMode.currentIndex());
cfg->setProperty("rank" + QString::number(i), bp->rank.value());
}
}
return cfg;
}
#include "kis_brush_export.moc"
diff --git a/plugins/impex/brush/kis_brush_export.h b/plugins/impex/brush/kis_brush_export.h
index fa69edeb2c..063499b412 100644
--- a/plugins/impex/brush/kis_brush_export.h
+++ b/plugins/impex/brush/kis_brush_export.h
@@ -1,170 +1,250 @@
/*
* Copyright (c) 2016 Boudewijn Rempt
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifndef _KIS_Brush_EXPORT_H_
#define _KIS_Brush_EXPORT_H_
#include
#include
#include
#include
#include
#include
#include
class SelectionModeComboBox : public QComboBox
{
Q_OBJECT
public:
SelectionModeComboBox(QWidget *parent)
: QComboBox(parent)
{
this->addItem(i18n("Constant"));
this->addItem(i18n("Random"));
this->addItem(i18n("Incremental"));
this->addItem(i18n("Pressure"));
this->addItem(i18n("Angular"));
this->addItem(i18n("Velocity"));
}
};
class BrushPipeSelectionModeHelper : public QWidget
{
Q_OBJECT
public:
- BrushPipeSelectionModeHelper(QWidget *parent)
+ BrushPipeSelectionModeHelper(QWidget *parent, int dimension)
: QWidget(parent)
, cmbSelectionMode(this)
, rank(this)
, rankLbl(this)
, horizLayout(this)
+ , dimension(dimension)
{
horizLayout.setSpacing(6);
horizLayout.setMargin(0);
QSizePolicy sizePolicy(QSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Fixed));
sizePolicy.setHorizontalStretch(1);
sizePolicy.setVerticalStretch(0);
this->setSizePolicy(sizePolicy);
cmbSelectionMode.setSizePolicy(sizePolicy);
cmbSelectionMode.setCurrentIndex(2);
rank.setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Preferred));
rankLbl.setSizePolicy(QSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred));
rankLbl.setText(i18n("Rank"));
horizLayout.addWidget(&rankLbl);
horizLayout.addWidget(&rank);
horizLayout.addWidget(&cmbSelectionMode);
+ connect(&rank, SIGNAL(valueChanged(int)), this, SLOT(slotRankChanged()));
+
this->hide();
this->setEnabled(false);
}
SelectionModeComboBox cmbSelectionMode;
QSpinBox rank;
QLabel rankLbl;
QHBoxLayout horizLayout;
+ int dimension;
+
+
+Q_SIGNALS:
+ void rankChanged(int rankEmitter);
+
+public Q_SLOTS:
+ void slotRankChanged()
+ {
+ emit rankChanged(dimension);
+ }
+
};
+#include
+#include
+#include
+
class KisWdgOptionsBrush : public KisConfigWidget, public Ui::WdgExportGih
{
Q_OBJECT
public:
KisWdgOptionsBrush(QWidget *parent)
: KisConfigWidget(parent)
, currentDimensions(0)
+ , m_layersCount(0)
+ , m_view(0)
{
setupUi(this);
- connect(this->brushStyle, SIGNAL(currentIndexChanged(int)), SLOT(enableSelectionMedthod(int)));
+ connect(this->brushStyle, SIGNAL(currentIndexChanged(int)), SLOT(enableSelectionMethod(int)));
connect(this->dimensionSpin, SIGNAL(valueChanged(int)), SLOT(activateDimensionRanks()));
- enableSelectionMedthod(brushStyle->currentIndex());
+ enableSelectionMethod(brushStyle->currentIndex());
+ BrushPipeSelectionModeHelper *bp;
for (int i = 0; i < this->dimensionSpin->maximum(); i++) {
- dimRankLayout->addWidget(new BrushPipeSelectionModeHelper(0));
+ bp = new BrushPipeSelectionModeHelper(0, i);
+ connect(bp, SIGNAL(rankChanged(int)), SLOT(recalculateRanks(int)));
+ dimRankLayout->addWidget(bp);
}
activateDimensionRanks();
}
void setConfiguration(const KisPropertiesConfigurationSP cfg) override;
KisPropertiesConfigurationSP configuration() const override;
public Q_SLOTS:
- void enableSelectionMedthod(int value) {
+
+ void enableSelectionMethod(int value) {
if (value == 0) {
animStyleGroup->setEnabled(false);
} else {
animStyleGroup->setEnabled(true);
}
}
+
void activateDimensionRanks()
{
QLayoutItem *item;
BrushPipeSelectionModeHelper *bp;
int dim = this->dimensionSpin->value();
if(dim >= currentDimensions) {
for (int i = currentDimensions; i < dim; ++i) {
if((item = dimRankLayout->itemAt(i)) != 0) {
bp = dynamic_cast(item->widget());
bp->setEnabled(true);
bp->show();
}
}
}
else {
for (int i = currentDimensions -1; i >= dim; --i) {
if((item = dimRankLayout->itemAt(i)) != 0) {
bp = dynamic_cast(item->widget());
bp->setEnabled(false);
bp->hide();
}
}
}
currentDimensions = dim;
}
+ void recalculateRanks(int rankDimension = 0) {
+// currentDimensions;
+ int rankSum = 0;
+ int maxDim = this->dimensionSpin->maximum();
+
+ QVector bp;
+ QLayoutItem *item;
+
+ for (int i = 0; i < maxDim; ++i) {
+ if((item = dimRankLayout->itemAt(i)) != 0) {
+ bp.push_back(dynamic_cast(item->widget()));
+ rankSum += bp.at(i)->rank.value();
+ }
+ }
+
+ BrushPipeSelectionModeHelper *currentBrushHelper;
+ BrushPipeSelectionModeHelper *callerBrushHelper = bp.at(rankDimension);
+ QVectorIterator bpIterator(bp);
+
+ while (rankSum > m_layersCount && bpIterator.hasNext()) {
+ currentBrushHelper = bpIterator.next();
+
+ if(currentBrushHelper != callerBrushHelper) {
+ int currentValue = currentBrushHelper->rank.value();
+ currentBrushHelper->rank.setValue(currentValue -1);
+ rankSum -= currentValue;
+ }
+ }
+
+ if (rankSum > m_layersCount) {
+ callerBrushHelper->rank.setValue(m_layersCount);
+ }
+
+ if (rankSum == 0) {
+ bp.at(0)->rank.setValue(m_layersCount);
+ return;
+ }
+ }
+
+ void setView(KisViewManager *view) override
+ {
+ if (view) {
+ m_view = view;
+ KoProperties properties;
+ properties.setProperty("visible", true);
+ m_layersCount = m_view->image()->root()->childNodes(QStringList("KisLayer"), properties).count();
+
+ recalculateRanks();
+ }
+ }
+
+
private:
int currentDimensions;
+ int m_layersCount;
+ KisViewManager *m_view;
};
class KisBrushExport : public KisImportExportFilter
{
Q_OBJECT
public:
KisBrushExport(QObject *parent, const QVariantList &);
~KisBrushExport() override;
KisImportExportErrorCode convert(KisDocument *document, QIODevice *io, KisPropertiesConfigurationSP configuration = 0) override;
KisPropertiesConfigurationSP defaultConfiguration(const QByteArray& from = "", const QByteArray& to = "") const override;
KisConfigWidget *createConfigurationWidget(QWidget *parent, const QByteArray& from = "", const QByteArray& to = "") const override;
void initializeCapabilities() override;
};
#endif