diff --git a/libs/pigment/resources/KisSwatchGroup.cpp b/libs/pigment/resources/KisSwatchGroup.cpp index dae1873402..156abcc73a 100644 --- a/libs/pigment/resources/KisSwatchGroup.cpp +++ b/libs/pigment/resources/KisSwatchGroup.cpp @@ -1,156 +1,217 @@ /* This file is part of the KDE project Copyright (c) 2005 Boudewijn Rempt Copyright (c) 2016 L. E. Segovia Copyright (c) 2018 Michael Zhou This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "KisSwatchGroup.h" -quint32 KisSwatchGroup::DEFAULT_COLUMN_COUNT = 16; -quint32 KisSwatchGroup::DEFAULT_ROW_COUNT = 20; +struct KisSwatchGroup::Private { + typedef QMap Column; + + Private() + : name(QString()) + , colorMatrix(DEFAULT_COLUMN_COUNT) + , colorCount(0) + , rowCount(DEFAULT_ROW_COUNT) + { } + + static quint32 DEFAULT_COLUMN_COUNT; + static quint32 DEFAULT_ROW_COUNT; + + QString name; + QVector colorMatrix; + int colorCount; + int rowCount; +}; + +quint32 KisSwatchGroup::Private::DEFAULT_COLUMN_COUNT = 16; +quint32 KisSwatchGroup::Private::DEFAULT_ROW_COUNT = 20; KisSwatchGroup::KisSwatchGroup() - : m_name(QString()) - , m_colorMatrix(DEFAULT_COLUMN_COUNT) - , m_colorCount(0) - , m_rowCount(DEFAULT_ROW_COUNT) + : d(new Private) +{ } + +KisSwatchGroup::~KisSwatchGroup() +{ } + +KisSwatchGroup::KisSwatchGroup(const KisSwatchGroup &rhs) + : d(new Private(*rhs.d)) { } +KisSwatchGroup &KisSwatchGroup::operator =(const KisSwatchGroup &rhs) +{ + if (&rhs == this) { + return *this; + } + d.reset(new Private(*rhs.d)); + return *this; +} + void KisSwatchGroup::setEntry(const KisSwatch &e, int x, int y) { - Q_ASSERT(x < m_colorMatrix.size() && x >= 0 && y >= 0); - if (y >= m_rowCount) { + Q_ASSERT(x < d->colorMatrix.size() && x >= 0 && y >= 0); + if (y >= d->rowCount) { setRowCount(y); } if (!checkEntry(x, y)) { - m_colorCount++; + d->colorCount++; } - m_colorMatrix[x][y] = e; + d->colorMatrix[x][y] = e; } bool KisSwatchGroup::checkEntry(int x, int y) const { - if (y >= m_rowCount || x >= m_colorMatrix.size() || x < 0) { + if (y >= d->rowCount || x >= d->colorMatrix.size() || x < 0) { return false; } - if (!m_colorMatrix[x].contains(y)) { + if (!d->colorMatrix[x].contains(y)) { return false; } return true; } bool KisSwatchGroup::removeEntry(int x, int y) { - if (m_colorCount == 0) { + if (d->colorCount == 0) { return false; } - if (y >= m_rowCount || x >= m_colorMatrix.size() || x < 0) { + if (y >= d->rowCount || x >= d->colorMatrix.size() || x < 0) { return false; } // QMap::remove returns 1 if key found else 0 - if (m_colorMatrix[x].remove(y)) { - m_colorCount -= 1; + if (d->colorMatrix[x].remove(y)) { + d->colorCount -= 1; return true; } else { return false; } } void KisSwatchGroup::setColumnCount(int columnCount) { Q_ASSERT(columnCount >= 0); - if (columnCount < m_colorMatrix.size()) { - for (int i = m_colorMatrix.size() - 1; i <= columnCount; i-- ) { - m_colorCount -= m_colorMatrix[i].size(); + if (columnCount < d->colorMatrix.size()) { + for (int i = d->colorMatrix.size() - 1; i <= columnCount; i-- ) { + d->colorCount -= d->colorMatrix[i].size(); } } - m_colorMatrix.resize(columnCount); + d->colorMatrix.resize(columnCount); +} + +int KisSwatchGroup::columnCount() const { + return d->colorMatrix.size(); } KisSwatch KisSwatchGroup::getEntry(int x, int y) const { Q_ASSERT(checkEntry(x, y)); - return m_colorMatrix[x][y]; + return d->colorMatrix[x][y]; } void KisSwatchGroup::addEntry(const KisSwatch &e) { if (columnCount() == 0) { - setColumnCount(DEFAULT_COLUMN_COUNT); + setColumnCount(Private::DEFAULT_COLUMN_COUNT); } - if (m_colorCount == 0) { + if (d->colorCount == 0) { setEntry(e, 0, 0); return; } int y = 0; - for (const Column &c : m_colorMatrix) { + for (const Private::Column &c : d->colorMatrix) { if (c.isEmpty()) { continue; } if (y < c.lastKey()) { y = c.lastKey(); } } - for (int x = m_colorMatrix.size() - 1; x >= 0; x--) { + for (int x = d->colorMatrix.size() - 1; x >= 0; x--) { if (checkEntry(x, y)) { // if the last entry's at the rightmost column, // add e to the leftmost column of the next row // and increase row count - if (++x == m_colorMatrix.size()) { + if (++x == d->colorMatrix.size()) { x = 0; y++; - if (y == m_rowCount) { - m_rowCount++; + if (y == d->rowCount) { + d->rowCount++; } } // else just add it to the right setEntry(e, x, y); break; } } } +void KisSwatchGroup::clear() +{ + d->colorMatrix.clear(); +} + void KisSwatchGroup::setRowCount(int newRowCount) { - m_rowCount = newRowCount; - for (Column &c : m_colorMatrix) { + d->rowCount = newRowCount; + for (Private::Column &c : d->colorMatrix) { for (int k : c.keys()) { if (k >= newRowCount) { c.remove(k); - m_colorCount--; + d->colorCount--; } } } } +int KisSwatchGroup::rowCount() const +{ + return d->rowCount; +} + +int KisSwatchGroup::colorCount() const +{ + return d->colorCount; +} + QList KisSwatchGroup::infoList() const { QList res; int column = 0; - for (const Column &c : m_colorMatrix) { + for (const Private::Column &c : d->colorMatrix) { int row = 0; for (const KisSwatch &s : c.values()) { - SwatchInfo i = {m_name, s, c.keys()[row++], column}; + SwatchInfo i = {d->name, s, c.keys()[row++], column}; res.append(i); } column++; } return res; } + +void KisSwatchGroup::setName(const QString &name) +{ + d->name = name; +} + +QString KisSwatchGroup::name() const +{ + return d->name; +} diff --git a/libs/pigment/resources/KisSwatchGroup.h b/libs/pigment/resources/KisSwatchGroup.h index 2394b33d54..8e4d34df7f 100644 --- a/libs/pigment/resources/KisSwatchGroup.h +++ b/libs/pigment/resources/KisSwatchGroup.h @@ -1,132 +1,127 @@ /* This file is part of the KDE project Copyright (c) 2005 Boudewijn Rempt Copyright (c) 2016 L. E. Segovia Copyright (c) 2018 Michael Zhou This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef KISSWATCHGROUP_H #define KISSWATCHGROUP_H #include "KisSwatch.h" #include "kritapigment_export.h" #include #include #include +#include /** * @brief The KisSwatchGroup class stores a matrix of color swatches * swatches can accessed using (x, y) coordinates. * x is the column number from left to right and y is the row number from top * to bottom. * Both x and y start at 0 * there could be empty entries, so the checkEntry(int, int) method must used * whenever you want to get an entry from the matrix */ class KRITAPIGMENT_EXPORT KisSwatchGroup { -private /* static variables */: - static quint32 DEFAULT_COLUMN_COUNT; - static quint32 DEFAULT_ROW_COUNT; - -private /* typedef */: - typedef QMap Column; - public /* struct */: struct SwatchInfo { QString group; KisSwatch swatch; int row; int column; }; public: KisSwatchGroup(); + ~KisSwatchGroup(); + KisSwatchGroup(const KisSwatchGroup &rhs); + KisSwatchGroup &operator =(const KisSwatchGroup &rhs); public /* methods */: - void setName(const QString &name) { m_name = name; } - QString name() const { return m_name; } + void setName(const QString &name); + QString name() const; void setColumnCount(int columnCount); - int columnCount() const { return m_colorMatrix.size(); } + int columnCount() const; void setRowCount(int newRowCount); - int rowCount() const { return m_rowCount; } + int rowCount() const; - int colorCount() const { return m_colorCount; } + int colorCount() const; QList infoList() const; /** * @brief checkEntry * checks if position x and y has a valid entry * both x and y start from 0 * @param x * @param y * @return true if there is a valid entry at position (x, y) */ bool checkEntry(int x, int y) const; /** * @brief setEntry * sets the entry at position (x, y) to be e * @param e * @param x * @param y */ void setEntry(const KisSwatch &e, int x, int y); /** * @brief getEntry * used to get the swatch entry at position (x, y) * there is an assertion to make sure that this position isn't empty, * so checkEntry(int, int) must be used before this method to ensure * a valid entry can be found * @param x * @param y * @return the swatch entry at position (x, y) */ KisSwatch getEntry(int x, int y) const; /** * @brief removeEntry * removes the entry at position (x, y) * @param x * @param y * @return true if these is an entry at (x, y) */ bool removeEntry(int x, int y); /** * @brief addEntry * adds the entry e to the right of the rightmost entry in the last row * if the rightmost entry in the last row is in the right most column, * add e to the leftmost column of a new row * * when column is set to 0, resize number of columns to default * @param e */ void addEntry(const KisSwatch &e); - void clear() { m_colorMatrix.clear(); } + void clear(); private /* member variables */: - QString m_name; - QVector m_colorMatrix; - int m_colorCount; - int m_rowCount; + struct Private; + QScopedPointer d; }; #endif // KISSWATCHGROUP_H diff --git a/libs/pigment/resources/KoColorSet.cpp b/libs/pigment/resources/KoColorSet.cpp index c500ff1677..635868fef0 100644 --- a/libs/pigment/resources/KoColorSet.cpp +++ b/libs/pigment/resources/KoColorSet.cpp @@ -1,1536 +1,1529 @@ /* This file is part of the KDE project Copyright (c) 2005 Boudewijn Rempt Copyright (c) 2016 L. E. Segovia This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // qFromLittleEndian #include #include #include #include #include #include #include #include #include #include "KisSwatch.h" #include "KoColorSet.h" #include "KoColorSet_p.h" QString KoColorSet::GLOBAL_GROUP_NAME = QString(); KoColorSet::KoColorSet(const QString& filename) : KoResource(filename) , d(new Private(this)) { if (!filename.isEmpty()) { setValid(true); QFileInfo f(filename); setIsEditable(f.isWritable()); } } /// Create an copied palette KoColorSet::KoColorSet(const KoColorSet& rhs) : QObject(Q_NULLPTR) , KoResource(rhs) , d(new Private(this)) { d->comment = rhs.d->comment; d->groupNames = rhs.d->groupNames; d->groups = rhs.d->groups; } KoColorSet::~KoColorSet() { } bool KoColorSet::load() { QFile file(filename()); if (file.size() == 0) return false; if (!file.open(QIODevice::ReadOnly)) { warnPigment << "Can't open file " << filename(); return false; } bool res = loadFromDevice(&file); file.close(); return res; } bool KoColorSet::loadFromDevice(QIODevice *dev) { if (!dev->isOpen()) dev->open(QIODevice::ReadOnly); d->data = dev->readAll(); Q_ASSERT(d->data.size() != 0); return d->init(); } bool KoColorSet::save() { if (d->isGlobal) { // save to resource dir QFile file(filename()); if (!file.open(QIODevice::WriteOnly | QIODevice::Truncate)) { return false; } saveToDevice(&file); file.close(); return true; } else { return true; // palette is not global, but still indicate that it's saved } } bool KoColorSet::saveToDevice(QIODevice *dev) const { bool res; switch(d->paletteType) { case GPL: res = d->saveGpl(dev); break; default: res = d->saveKpl(dev); } if (res) { KoResource::saveToDevice(dev); } return res; } QByteArray KoColorSet::toByteArray() const { QBuffer s; s.open(QIODevice::WriteOnly); if (!saveToDevice(&s)) { warnPigment << "saving palette failed:" << name(); return QByteArray(); } s.close(); s.open(QIODevice::ReadOnly); QByteArray res = s.readAll(); s.close(); return res; } bool KoColorSet::fromByteArray(QByteArray &data) { QBuffer buf(&data); buf.open(QIODevice::ReadOnly); return loadFromDevice(&buf); } KoColorSet::PaletteType KoColorSet::paletteType() const { return d->paletteType; } void KoColorSet::setPaletteType(PaletteType paletteType) { d->paletteType = paletteType; } quint32 KoColorSet::colorCount() const { return d->groups[GLOBAL_GROUP_NAME].colorCount(); } void KoColorSet::add(const KisSwatch &c, const QString &groupName) { KisSwatchGroup &modifiedGroup = d->groups.contains(groupName) ? d->groups[groupName] : d->global(); modifiedGroup.addEntry(c); } void KoColorSet::setEntry(const KisSwatch &e, int x, int y, const QString &groupName) { KisSwatchGroup &modifiedGroup = d->groups.contains(groupName) ? d->groups[groupName] : d->global(); modifiedGroup.setEntry(e, x, y); } void KoColorSet::clear() { d->groups.clear(); d->groupNames.clear(); } KisSwatch KoColorSet::getColorGlobal(quint32 x, quint32 y) const { KisSwatch e; int yInGroup = y; QString nameGroupFoundIn; for (const QString &groupName : d->groupNames) { if (yInGroup < d->groups[groupName].rowCount()) { nameGroupFoundIn = groupName; break; } else { yInGroup -= d->groups[groupName].rowCount(); } } const KisSwatchGroup &groupFoundIn = nameGroupFoundIn == QString() ? d->global() : d->groups[nameGroupFoundIn]; Q_ASSERT(groupFoundIn.checkEntry(x, yInGroup)); return groupFoundIn.getEntry(x, yInGroup); } KisSwatch KoColorSet::getColorGroup(quint32 x, quint32 y, QString groupName) { KisSwatch e; const KisSwatchGroup &sourceGroup = groupName == QString() ? d->global() : d->groups[groupName]; if (sourceGroup.checkEntry(x, y)) { e = sourceGroup.getEntry(x, y); } return e; } QStringList KoColorSet::getGroupNames() { if (d->groupNames.size() != d->groups.size()) { warnPigment << "mismatch between groups and the groupnames list."; return QStringList(d->groups.keys()); } return d->groupNames; } bool KoColorSet::changeGroupName(QString oldGroupName, QString newGroupName) { if (!d->groupNames.contains(oldGroupName)) { return false; } d->groups[newGroupName] = d->groups[oldGroupName]; d->groups.remove(oldGroupName); d->groups[newGroupName].setName(newGroupName); //rename the string in the stringlist; int index = d->groupNames.indexOf(oldGroupName); d->groupNames.replace(index, newGroupName); return true; } void KoColorSet::setColumnCount(int columns) { d->groups[GLOBAL_GROUP_NAME].setColumnCount(columns); for (KisSwatchGroup &g : d->groups.values()) { g.setColumnCount(columns); } } int KoColorSet::columnCount() const { return d->groups[GLOBAL_GROUP_NAME].columnCount(); } QString KoColorSet::comment() { return d->comment; } void KoColorSet::setComment(QString comment) { d->comment = comment; } bool KoColorSet::addGroup(const QString &groupName) { if (d->groups.contains(groupName) || d->groupNames.contains(groupName)) { return false; } d->groupNames.append(groupName); d->groups[groupName] = KisSwatchGroup(); d->groups[groupName].setName(groupName); return true; } bool KoColorSet::moveGroup(const QString &groupName, const QString &groupNameInsertBefore) { if (d->groupNames.contains(groupName)==false || d->groupNames.contains(groupNameInsertBefore)==false) { return false; } d->groupNames.removeAt(d->groupNames.indexOf(groupName)); int index = d->groupNames.size(); if (groupNameInsertBefore!=QString()) { index = d->groupNames.indexOf(groupNameInsertBefore); } d->groupNames.insert(index, groupName); return true; } bool KoColorSet::removeGroup(const QString &groupName, bool keepColors) { if (!d->groups.contains(groupName)) { return false; } if (keepColors) { // put all colors directly below global int startingRow = d->groups[GLOBAL_GROUP_NAME].rowCount(); for (const KisSwatchGroup::SwatchInfo &info : d->groups[groupName].infoList()) { d->groups[GLOBAL_GROUP_NAME].setEntry(info.swatch, info.column, info.row + startingRow); } } d->groupNames.removeAt(d->groupNames.indexOf(groupName)); d->groups.remove(groupName); return true; } QString KoColorSet::defaultFileExtension() const { return QString(".kpl"); } int KoColorSet::rowCount() const { int res = 0; for (const QString &name : d->groupNames) { res += d->groups[name].rowCount(); } return res; } KisSwatchGroup *KoColorSet::getGroup(const QString &name) { - if (name.isEmpty()) { - return &(d->global()); - } if (!d->groups.contains(name)) { return Q_NULLPTR; } return &(d->groups[name]); } KisSwatchGroup *KoColorSet::getGlobalGroup() { return getGroup(GLOBAL_GROUP_NAME); } bool KoColorSet::isGlobal() const { return d->isGlobal; } void KoColorSet::setIsGlobal(bool isGlobal) { d->isGlobal = isGlobal; } bool KoColorSet::isEditable() const { return d->isEditable; } void KoColorSet::setIsEditable(bool isEditable) { d->isEditable = isEditable; } KisSwatchGroup::SwatchInfo KoColorSet::getClosestColorInfo(KoColor compare, bool useGivenColorSpace) { KisSwatchGroup::SwatchInfo res; quint8 highestPercentage = 0; quint8 testPercentage = 0; for (const QString &groupName : getGroupNames()) { KisSwatchGroup *group = getGroup(groupName); for (const KisSwatchGroup::SwatchInfo &currInfo : group->infoList()) { KoColor color = currInfo.swatch.color(); if (useGivenColorSpace == true && compare.colorSpace() != color.colorSpace()) { color.convertTo(compare.colorSpace()); } else if (compare.colorSpace() != color.colorSpace()) { compare.convertTo(color.colorSpace()); } testPercentage = (255 - compare.colorSpace()->difference(compare.data(), color.data())); if (testPercentage > highestPercentage) { highestPercentage = testPercentage; res = currInfo; } } } return res; } /********************************KoColorSet::Private**************************/ KoColorSet::Private::Private(KoColorSet *a_colorSet) : colorSet(a_colorSet) , isGlobal(true) , isEditable(false) { groups[KoColorSet::GLOBAL_GROUP_NAME] = KisSwatchGroup(); groupNames.append(KoColorSet::GLOBAL_GROUP_NAME); } KoColorSet::PaletteType KoColorSet::Private::detectFormat(const QString &fileName, const QByteArray &ba) { QFileInfo fi(fileName); // .pal if (ba.startsWith("RIFF") && ba.indexOf("PAL data", 8)) { return KoColorSet::RIFF_PAL; } // .gpl else if (ba.startsWith("GIMP Palette")) { return KoColorSet::GPL; } // .pal else if (ba.startsWith("JASC-PAL")) { return KoColorSet::PSP_PAL; } else if (fi.suffix().toLower() == "aco") { return KoColorSet::ACO; } else if (fi.suffix().toLower() == "act") { return KoColorSet::ACT; } else if (fi.suffix().toLower() == "xml") { return KoColorSet::XML; } else if (fi.suffix().toLower() == "kpl") { return KoColorSet::KPL; } else if (fi.suffix().toLower() == "sbz") { return KoColorSet::SBZ; } return KoColorSet::UNKNOWN; } void KoColorSet::Private::scribusParseColor(KoColorSet *set, QXmlStreamReader *xml) { KisSwatch colorEntry; // It's a color, retrieve it QXmlStreamAttributes colorProperties = xml->attributes(); QStringRef colorName = colorProperties.value("NAME"); colorEntry.setName(colorName.isEmpty() || colorName.isNull() ? i18n("Untitled") : colorName.toString()); // RGB or CMYK? if (colorProperties.hasAttribute("RGB")) { dbgPigment << "Color " << colorProperties.value("NAME") << ", RGB " << colorProperties.value("RGB"); KoColor currentColor(KoColorSpaceRegistry::instance()->rgb8()); QStringRef colorValue = colorProperties.value("RGB"); if (colorValue.length() != 7 && colorValue.at(0) != '#') { // Color is a hexadecimal number xml->raiseError("Invalid rgb8 color (malformed): " + colorValue); return; } else { bool rgbOk; quint32 rgb = colorValue.mid(1).toUInt(&rgbOk, 16); if (!rgbOk) { xml->raiseError("Invalid rgb8 color (unable to convert): " + colorValue); return; } quint8 r = rgb >> 16 & 0xff; quint8 g = rgb >> 8 & 0xff; quint8 b = rgb & 0xff; dbgPigment << "Color parsed: "<< r << g << b; currentColor.data()[0] = r; currentColor.data()[1] = g; currentColor.data()[2] = b; currentColor.setOpacity(OPACITY_OPAQUE_U8); colorEntry.setColor(currentColor); set->add(colorEntry); while(xml->readNextStartElement()) { //ignore - these are all unknown or the /> element tag xml->skipCurrentElement(); } return; } } else if (colorProperties.hasAttribute("CMYK")) { dbgPigment << "Color " << colorProperties.value("NAME") << ", CMYK " << colorProperties.value("CMYK"); KoColor currentColor(KoColorSpaceRegistry::instance()->colorSpace(CMYKAColorModelID.id(), Integer8BitsColorDepthID.id(), QString())); QStringRef colorValue = colorProperties.value("CMYK"); if (colorValue.length() != 9 && colorValue.at(0) != '#') { // Color is a hexadecimal number xml->raiseError("Invalid cmyk color (malformed): " % colorValue); return; } else { bool cmykOk; quint32 cmyk = colorValue.mid(1).toUInt(&cmykOk, 16); // cmyk uses the full 32 bits if (!cmykOk) { xml->raiseError("Invalid cmyk color (unable to convert): " % colorValue); return; } quint8 c = cmyk >> 24 & 0xff; quint8 m = cmyk >> 16 & 0xff; quint8 y = cmyk >> 8 & 0xff; quint8 k = cmyk & 0xff; dbgPigment << "Color parsed: "<< c << m << y << k; currentColor.data()[0] = c; currentColor.data()[1] = m; currentColor.data()[2] = y; currentColor.data()[3] = k; currentColor.setOpacity(OPACITY_OPAQUE_U8); colorEntry.setColor(currentColor); set->add(colorEntry); while(xml->readNextStartElement()) { //ignore - these are all unknown or the /> element tag xml->skipCurrentElement(); } return; } } else { xml->raiseError("Unknown color space for color " + colorEntry.name()); } } bool KoColorSet::Private::loadScribusXmlPalette(KoColorSet *set, QXmlStreamReader *xml) { //1. Get name QXmlStreamAttributes paletteProperties = xml->attributes(); QStringRef paletteName = paletteProperties.value("Name"); dbgPigment << "Processed name of palette:" << paletteName; set->setName(paletteName.toString()); //2. Inside the SCRIBUSCOLORS, there are lots of colors. Retrieve them while(xml->readNextStartElement()) { QStringRef currentElement = xml->name(); if(QStringRef::compare(currentElement, "COLOR", Qt::CaseInsensitive) == 0) { scribusParseColor(set, xml); } else { xml->skipCurrentElement(); } } if(xml->hasError()) { return false; } return true; } quint16 KoColorSet::Private::readShort(QIODevice *io) { quint16 val; quint64 read = io->read((char*)&val, 2); if (read != 2) return false; return qFromBigEndian(val); } bool KoColorSet::Private::init() { // just in case this is a reload (eg by KoEditColorSetDialog), groupNames.clear(); groups.clear(); groupNames.append(KoColorSet::GLOBAL_GROUP_NAME); groups[KoColorSet::GLOBAL_GROUP_NAME] = KisSwatchGroup(); if (colorSet->filename().isNull()) { warnPigment << "Cannot load palette" << colorSet->name() << "there is no filename set"; return false; } if (data.isNull()) { QFile file(colorSet->filename()); if (file.size() == 0) { warnPigment << "Cannot load palette" << colorSet->name() << "there is no data available"; return false; } file.open(QIODevice::ReadOnly); data = file.readAll(); file.close(); } bool res = false; paletteType = detectFormat(colorSet->filename(), data); switch(paletteType) { case GPL: res = loadGpl(); break; case ACT: res = loadAct(); break; case RIFF_PAL: res = loadRiff(); break; case PSP_PAL: res = loadPsp(); break; case ACO: res = loadAco(); break; case XML: res = loadXml(); break; case KPL: res = loadKpl(); break; case SBZ: res = loadSbz(); break; default: res = false; } colorSet->setValid(res); QImage img(global().columnCount() * 4, global().rowCount() * 4, QImage::Format_ARGB32); QPainter gc(&img); gc.fillRect(img.rect(), Qt::darkGray); for (const KisSwatchGroup::SwatchInfo &info : global().infoList()) { QColor c = info.swatch.color().toQColor(); gc.fillRect(info.column * 4, info.row * 4, 4, 4, c); } colorSet->setImage(img); colorSet->setValid(res); - // save some memory data.clear(); return res; } bool KoColorSet::Private::saveGpl(QIODevice *dev) const { Q_ASSERT(dev->isOpen()); Q_ASSERT(dev->isWritable()); QTextStream stream(dev); stream << "GIMP Palette\nName: " << colorSet->name() << "\nColumns: " << colorSet->columnCount() << "\n#\n"; /* * Qt doesn't provide an interface to get a const reference to a QHash, that is * the underlying data structure of groups. Therefore, directly use * groups[KoColorSet::GLOBAL_GROUP_NAME] so that saveGpl can stay const */ for (int y = 0; y < groups[KoColorSet::GLOBAL_GROUP_NAME].rowCount(); y++) { for (int x = 0; x < colorSet->columnCount(); x++) { if (!groups[KoColorSet::GLOBAL_GROUP_NAME].checkEntry(x, y)) { continue; } const KisSwatch& entry = groups[KoColorSet::GLOBAL_GROUP_NAME].getEntry(x, y); QColor c = entry.color().toQColor(); stream << c.red() << " " << c.green() << " " << c.blue() << "\t"; if (entry.name().isEmpty()) stream << "Untitled\n"; else stream << entry.name() << "\n"; } } return true; } bool KoColorSet::Private::loadGpl() { QString s = QString::fromUtf8(data.data(), data.count()); if (s.isEmpty() || s.isNull() || s.length() < 50) { warnPigment << "Illegal Gimp palette file: " << colorSet->filename(); return false; } quint32 index = 0; QStringList lines = s.split('\n', QString::SkipEmptyParts); if (lines.size() < 3) { warnPigment << "Not enough lines in palette file: " << colorSet->filename(); return false; } QString columnsText; qint32 r, g, b; KisSwatch e; // Read name if (!lines[0].startsWith("GIMP") || !lines[1].toLower().contains("name")) { warnPigment << "Illegal Gimp palette file: " << colorSet->filename(); return false; } colorSet->setName(i18n(lines[1].split(":")[1].trimmed().toLatin1())); index = 2; // Read columns int columns = 0; if (lines[index].toLower().contains("columns")) { columnsText = lines[index].split(":")[1].trimmed(); columns = columnsText.toInt(); global().setColumnCount(columns); index = 3; } for (qint32 i = index; i < lines.size(); i++) { if (lines[i].startsWith('#')) { comment += lines[i].mid(1).trimmed() + ' '; } else if (!lines[i].isEmpty()) { QStringList a = lines[i].replace('\t', ' ').split(' ', QString::SkipEmptyParts); if (a.count() < 3) { break; } r = qBound(0, a[0].toInt(), 255); g = qBound(0, a[1].toInt(), 255); b = qBound(0, a[2].toInt(), 255); e.setColor(KoColor(QColor(r, g, b), KoColorSpaceRegistry::instance()->rgb8())); for (int i = 0; i != 3; i++) { a.pop_front(); } QString name = a.join(" "); e.setName(name.isEmpty() ? i18n("Untitled") : name); global().addEntry(e); } } return true; } bool KoColorSet::Private::loadAct() { QFileInfo info(colorSet->filename()); colorSet->setName(info.baseName()); KisSwatch e; for (int i = 0; i < data.size(); i += 3) { quint8 r = data[i]; quint8 g = data[i+1]; quint8 b = data[i+2]; e.setColor(KoColor(QColor(r, g, b), KoColorSpaceRegistry::instance()->rgb8())); global().addEntry(e); } return true; } bool KoColorSet::Private::loadRiff() { // http://worms2d.info/Palette_file QFileInfo info(colorSet->filename()); colorSet->setName(info.baseName()); KisSwatch e; RiffHeader header; memcpy(&header, data.constData(), sizeof(RiffHeader)); header.colorcount = qFromBigEndian(header.colorcount); for (int i = sizeof(RiffHeader); (i < (int)(sizeof(RiffHeader) + header.colorcount) && i < data.size()); i += 4) { quint8 r = data[i]; quint8 g = data[i+1]; quint8 b = data[i+2]; e.setColor(KoColor(QColor(r, g, b), KoColorSpaceRegistry::instance()->rgb8())); groups[KoColorSet::GLOBAL_GROUP_NAME].addEntry(e); } return true; } bool KoColorSet::Private::loadPsp() { QFileInfo info(colorSet->filename()); colorSet->setName(info.baseName()); KisSwatch e; qint32 r, g, b; QString s = QString::fromUtf8(data.data(), data.count()); QStringList l = s.split('\n', QString::SkipEmptyParts); if (l.size() < 4) return false; if (l[0] != "JASC-PAL") return false; if (l[1] != "0100") return false; int entries = l[2].toInt(); for (int i = 0; i < entries; ++i) { QStringList a = l[i + 3].replace('\t', ' ').split(' ', QString::SkipEmptyParts); if (a.count() != 3) { continue; } r = qBound(0, a[0].toInt(), 255); g = qBound(0, a[1].toInt(), 255); b = qBound(0, a[2].toInt(), 255); e.setColor(KoColor(QColor(r, g, b), KoColorSpaceRegistry::instance()->rgb8())); QString name = a.join(" "); e.setName(name.isEmpty() ? i18n("Untitled") : name); groups[KoColorSet::GLOBAL_GROUP_NAME].addEntry(e); } return true; } bool KoColorSet::Private::loadKpl() { QBuffer buf(&data); buf.open(QBuffer::ReadOnly); QScopedPointer store(KoStore::createStore(&buf, KoStore::Read, "application/x-krita-palette", KoStore::Zip)); if (!store || store->bad()) { return false; } if (store->hasFile("profiles.xml")) { if (!store->open("profiles.xml")) { return false; } QByteArray data; data.resize(store->size()); QByteArray ba = store->read(store->size()); store->close(); QDomDocument doc; doc.setContent(ba); QDomElement e = doc.documentElement(); QDomElement c = e.firstChildElement("Profiles"); while (!c.isNull()) { QString name = c.attribute("name"); QString filename = c.attribute("filename"); QString colorModelId = c.attribute("colorModelId"); QString colorDepthId = c.attribute("colorDepthId"); if (!KoColorSpaceRegistry::instance()->profileByName(name)) { store->open(filename); QByteArray data; data.resize(store->size()); data = store->read(store->size()); store->close(); const KoColorProfile *profile = KoColorSpaceRegistry::instance()->createColorProfile(colorModelId, colorDepthId, data); if (profile && profile->valid()) { KoColorSpaceRegistry::instance()->addProfile(profile); } } c = c.nextSiblingElement(); } } { if (!store->open("colorset.xml")) { return false; } QByteArray data; data.resize(store->size()); QByteArray ba = store->read(store->size()); store->close(); QDomDocument doc; doc.setContent(ba); QDomElement e = doc.documentElement(); colorSet->setName(e.attribute("name")); colorSet->setColumnCount(e.attribute("columns").toInt()); comment = e.attribute("comment"); - loadKplGroup(doc, e, colorSet->getGroup(KoColorSet::GLOBAL_GROUP_NAME)); + loadKplGroup(doc, e, colorSet->getGlobalGroup()); QDomElement g = e.firstChildElement("Group"); while (!g.isNull()) { QString groupName = g.attribute("name"); colorSet->addGroup(groupName); loadKplGroup(doc, g, colorSet->getGroup(groupName)); g = g.nextSiblingElement("Group"); } } buf.close(); return true; } bool KoColorSet::Private::loadAco() { QFileInfo info(colorSet->filename()); colorSet->setName(info.baseName()); QBuffer buf(&data); buf.open(QBuffer::ReadOnly); quint16 version = readShort(&buf); quint16 numColors = readShort(&buf); KisSwatch e; if (version == 1 && buf.size() > 4+numColors*10) { buf.seek(4+numColors*10); version = readShort(&buf); numColors = readShort(&buf); } const quint16 quint16_MAX = 65535; for (int i = 0; i < numColors && !buf.atEnd(); ++i) { quint16 colorSpace = readShort(&buf); quint16 ch1 = readShort(&buf); quint16 ch2 = readShort(&buf); quint16 ch3 = readShort(&buf); quint16 ch4 = readShort(&buf); bool skip = false; if (colorSpace == 0) { // RGB const KoColorProfile *srgb = KoColorSpaceRegistry::instance()->rgb8()->profile(); KoColor c(KoColorSpaceRegistry::instance()->rgb16(srgb)); reinterpret_cast(c.data())[0] = ch3; reinterpret_cast(c.data())[1] = ch2; reinterpret_cast(c.data())[2] = ch1; c.setOpacity(OPACITY_OPAQUE_U8); e.setColor(c); } else if (colorSpace == 1) { // HSB QColor qc; qc.setHsvF(ch1 / 65536.0, ch2 / 65536.0, ch3 / 65536.0); KoColor c(qc, KoColorSpaceRegistry::instance()->rgb16()); c.setOpacity(OPACITY_OPAQUE_U8); e.setColor(c); } else if (colorSpace == 2) { // CMYK KoColor c(KoColorSpaceRegistry::instance()->colorSpace(CMYKAColorModelID.id(), Integer16BitsColorDepthID.id(), QString())); reinterpret_cast(c.data())[0] = quint16_MAX - ch1; reinterpret_cast(c.data())[1] = quint16_MAX - ch2; reinterpret_cast(c.data())[2] = quint16_MAX - ch3; reinterpret_cast(c.data())[3] = quint16_MAX - ch4; c.setOpacity(OPACITY_OPAQUE_U8); e.setColor(c); } else if (colorSpace == 7) { // LAB KoColor c = KoColor(KoColorSpaceRegistry::instance()->lab16()); reinterpret_cast(c.data())[0] = ch3; reinterpret_cast(c.data())[1] = ch2; reinterpret_cast(c.data())[2] = ch1; c.setOpacity(OPACITY_OPAQUE_U8); e.setColor(c); } else if (colorSpace == 8) { // GRAY KoColor c(KoColorSpaceRegistry::instance()->colorSpace(GrayAColorModelID.id(), Integer16BitsColorDepthID.id(), QString())); reinterpret_cast(c.data())[0] = ch1 * (quint16_MAX / 10000); c.setOpacity(OPACITY_OPAQUE_U8); e.setColor(c); } else { warnPigment << "Unsupported colorspace in palette" << colorSet->filename() << "(" << colorSpace << ")"; skip = true; } if (version == 2) { quint16 v2 = readShort(&buf); //this isn't a version, it's a marker and needs to be skipped. Q_UNUSED(v2); quint16 size = readShort(&buf) -1; //then comes the length if (size>0) { QByteArray ba = buf.read(size*2); if (ba.size() == size*2) { QTextCodec *Utf16Codec = QTextCodec::codecForName("UTF-16BE"); e.setName(Utf16Codec->toUnicode(ba)); } else { warnPigment << "Version 2 name block is the wrong size" << colorSet->filename(); } } v2 = readShort(&buf); //end marker also needs to be skipped. Q_UNUSED(v2); } if (!skip) { groups[KoColorSet::GLOBAL_GROUP_NAME].addEntry(e); } } return true; } bool KoColorSet::Private::loadSbz() { QBuffer buf(&data); buf.open(QBuffer::ReadOnly); // &buf is a subclass of QIODevice QScopedPointer store(KoStore::createStore(&buf, KoStore::Read, "application/x-swatchbook", KoStore::Zip)); if (!store || store->bad()) return false; if (store->hasFile("swatchbook.xml")) { // Try opening... if (!store->open("swatchbook.xml")) { return false; } QByteArray data; data.resize(store->size()); QByteArray ba = store->read(store->size()); store->close(); dbgPigment << "XML palette: " << colorSet->filename() << ", SwatchBooker format"; QDomDocument doc; int errorLine, errorColumn; QString errorMessage; bool status = doc.setContent(ba, &errorMessage, &errorLine, &errorColumn); if (!status) { warnPigment << "Illegal XML palette:" << colorSet->filename(); warnPigment << "Error (line" << errorLine << ", column" << errorColumn << "):" << errorMessage; return false; } QDomElement e = doc.documentElement(); // SwatchBook // Start reading properties... QDomElement metadata = e.firstChildElement("metadata"); if (e.isNull()) { warnPigment << "Palette metadata not found"; return false; } QDomElement title = metadata.firstChildElement("dc:title"); QString colorName = title.text(); colorName = colorName.isEmpty() ? i18n("Untitled") : colorName; colorSet->setName(colorName); dbgPigment << "Processed name of palette:" << colorSet->name(); // End reading properties // Now read colors... QDomElement materials = e.firstChildElement("materials"); if (materials.isNull()) { warnPigment << "Materials (color definitions) not found"; return false; } // This one has lots of "color" elements QDomElement colorElement = materials.firstChildElement("color"); if (colorElement.isNull()) { warnPigment << "Color definitions not found (line" << materials.lineNumber() << ", column" << materials.columnNumber() << ")"; return false; } // Also read the swatch book... QDomElement book = e.firstChildElement("book"); if (book.isNull()) { warnPigment << "Palette book (swatch composition) not found (line" << e.lineNumber() << ", column" << e.columnNumber() << ")"; return false; } // Which has lots of "swatch"es (todo: support groups) QDomElement swatch = book.firstChildElement(); if (swatch.isNull()) { warnPigment << "Swatches/groups definition not found (line" << book.lineNumber() << ", column" << book.columnNumber() << ")"; return false; } // We'll store colors here, and as we process swatches // we'll add them to the palette QHash materialsBook; QHash fileColorSpaces; // Color processing for(; !colorElement.isNull(); colorElement = colorElement.nextSiblingElement("color")) { KisSwatch currentEntry; // Set if color is spot currentEntry.setSpotColor(colorElement.attribute("usage") == "spot"); // inside contains id and name // one or more define the color QDomElement currentColorMetadata = colorElement.firstChildElement("metadata"); QDomNodeList currentColorValues = colorElement.elementsByTagName("values"); // Get color name QDomElement colorTitle = currentColorMetadata.firstChildElement("dc:title"); QDomElement colorId = currentColorMetadata.firstChildElement("dc:identifier"); // Is there an id? (we need that at the very least for identifying a color) if (colorId.text().isEmpty()) { warnPigment << "Unidentified color (line" << colorId.lineNumber()<< ", column" << colorId.columnNumber() << ")"; return false; } if (materialsBook.contains(colorId.text())) { warnPigment << "Duplicated color definition (line" << colorId.lineNumber()<< ", column" << colorId.columnNumber() << ")"; return false; } // Get a valid color name currentEntry.setId(colorId.text()); currentEntry.setName(colorTitle.text().isEmpty() ? colorId.text() : colorTitle.text()); // Get a valid color definition if (currentColorValues.isEmpty()) { warnPigment << "Color definitions not found (line" << colorElement.lineNumber() << ", column" << colorElement.columnNumber() << ")"; return false; } bool firstDefinition = false; const KoColorProfile *srgb = KoColorSpaceRegistry::instance()->rgb8()->profile(); // Priority: Lab, otherwise the first definition found for(int j = 0; j < currentColorValues.size(); j++) { QDomNode colorValue = currentColorValues.at(j); QDomElement colorValueE = colorValue.toElement(); QString model = colorValueE.attribute("model", QString()); // sRGB,RGB,HSV,HSL,CMY,CMYK,nCLR: 0 -> 1 // YIQ: Y 0 -> 1 : IQ -0.5 -> 0.5 // Lab: L 0 -> 100 : ab -128 -> 127 // XYZ: 0 -> ~100 if (model == "Lab") { QStringList lab = colorValueE.text().split(" "); if (lab.length() != 3) { warnPigment << "Invalid Lab color definition (line" << colorValueE.lineNumber() << ", column" << colorValueE.columnNumber() << ")"; } float l = lab.at(0).toFloat(&status); float a = lab.at(1).toFloat(&status); float b = lab.at(2).toFloat(&status); if (!status) { warnPigment << "Invalid float definition (line" << colorValueE.lineNumber() << ", column" << colorValueE.columnNumber() << ")"; } KoColor c(KoColorSpaceRegistry::instance()->colorSpace(LABAColorModelID.id(), Float32BitsColorDepthID.id(), QString())); reinterpret_cast(c.data())[0] = l; reinterpret_cast(c.data())[1] = a; reinterpret_cast(c.data())[2] = b; c.setOpacity(OPACITY_OPAQUE_F); firstDefinition = true; currentEntry.setColor(c); break; // Immediately add this one } else if (model == "sRGB" && !firstDefinition) { QStringList rgb = colorValueE.text().split(" "); if (rgb.length() != 3) { warnPigment << "Invalid sRGB color definition (line" << colorValueE.lineNumber() << ", column" << colorValueE.columnNumber() << ")"; } float r = rgb.at(0).toFloat(&status); float g = rgb.at(1).toFloat(&status); float b = rgb.at(2).toFloat(&status); if (!status) { warnPigment << "Invalid float definition (line" << colorValueE.lineNumber() << ", column" << colorValueE.columnNumber() << ")"; } KoColor c(KoColorSpaceRegistry::instance()->colorSpace(RGBAColorModelID.id(), Float32BitsColorDepthID.id(), srgb)); reinterpret_cast(c.data())[0] = r; reinterpret_cast(c.data())[1] = g; reinterpret_cast(c.data())[2] = b; c.setOpacity(OPACITY_OPAQUE_F); currentEntry.setColor(c); firstDefinition = true; } else if (model == "XYZ" && !firstDefinition) { QStringList xyz = colorValueE.text().split(" "); if (xyz.length() != 3) { warnPigment << "Invalid XYZ color definition (line" << colorValueE.lineNumber() << ", column" << colorValueE.columnNumber() << ")"; } float x = xyz.at(0).toFloat(&status); float y = xyz.at(1).toFloat(&status); float z = xyz.at(2).toFloat(&status); if (!status) { warnPigment << "Invalid float definition (line" << colorValueE.lineNumber() << ", column" << colorValueE.columnNumber() << ")"; } KoColor c(KoColorSpaceRegistry::instance()->colorSpace(XYZAColorModelID.id(), Float32BitsColorDepthID.id(), QString())); reinterpret_cast(c.data())[0] = x; reinterpret_cast(c.data())[1] = y; reinterpret_cast(c.data())[2] = z; c.setOpacity(OPACITY_OPAQUE_F); currentEntry.setColor(c); firstDefinition = true; } // The following color spaces admit an ICC profile (in SwatchBooker) else if (model == "CMYK" && !firstDefinition) { QStringList cmyk = colorValueE.text().split(" "); if (cmyk.length() != 4) { warnPigment << "Invalid CMYK color definition (line" << colorValueE.lineNumber() << ", column" << colorValueE.columnNumber() << ")"; } float c = cmyk.at(0).toFloat(&status); float m = cmyk.at(1).toFloat(&status); float y = cmyk.at(2).toFloat(&status); float k = cmyk.at(3).toFloat(&status); if (!status) { warnPigment << "Invalid float definition (line" << colorValueE.lineNumber() << ", column" << colorValueE.columnNumber() << ")"; } QString space = colorValueE.attribute("space"); const KoColorSpace *colorSpace = KoColorSpaceRegistry::instance()->colorSpace(CMYKAColorModelID.id(), Float32BitsColorDepthID.id(), QString()); if (!space.isEmpty()) { // Try loading the profile and add it to the registry if (!fileColorSpaces.contains(space)) { store->enterDirectory("profiles"); store->open(space); QByteArray data; data.resize(store->size()); data = store->read(store->size()); store->close(); const KoColorProfile *profile = KoColorSpaceRegistry::instance()->createColorProfile(CMYKAColorModelID.id(), Float32BitsColorDepthID.id(), data); if (profile && profile->valid()) { KoColorSpaceRegistry::instance()->addProfile(profile); colorSpace = KoColorSpaceRegistry::instance()->colorSpace(CMYKAColorModelID.id(), Float32BitsColorDepthID.id(), profile); fileColorSpaces.insert(space, colorSpace); } } else { colorSpace = fileColorSpaces.value(space); } } KoColor color(colorSpace); reinterpret_cast(color.data())[0] = c; reinterpret_cast(color.data())[1] = m; reinterpret_cast(color.data())[2] = y; reinterpret_cast(color.data())[3] = k; color.setOpacity(OPACITY_OPAQUE_F); currentEntry.setColor(color); firstDefinition = true; } else if (model == "GRAY" && !firstDefinition) { QString gray = colorValueE.text(); float g = gray.toFloat(&status); if (!status) { warnPigment << "Invalid float definition (line" << colorValueE.lineNumber() << ", column" << colorValueE.columnNumber() << ")"; } QString space = colorValueE.attribute("space"); const KoColorSpace *colorSpace = KoColorSpaceRegistry::instance()->colorSpace(GrayAColorModelID.id(), Float32BitsColorDepthID.id(), QString()); if (!space.isEmpty()) { // Try loading the profile and add it to the registry if (!fileColorSpaces.contains(space)) { store->enterDirectory("profiles"); store->open(space); QByteArray data; data.resize(store->size()); data = store->read(store->size()); store->close(); const KoColorProfile *profile = KoColorSpaceRegistry::instance()->createColorProfile(CMYKAColorModelID.id(), Float32BitsColorDepthID.id(), data); if (profile && profile->valid()) { KoColorSpaceRegistry::instance()->addProfile(profile); colorSpace = KoColorSpaceRegistry::instance()->colorSpace(CMYKAColorModelID.id(), Float32BitsColorDepthID.id(), profile); fileColorSpaces.insert(space, colorSpace); } } else { colorSpace = fileColorSpaces.value(space); } } KoColor c(colorSpace); reinterpret_cast(c.data())[0] = g; c.setOpacity(OPACITY_OPAQUE_F); currentEntry.setColor(c); firstDefinition = true; } else if (model == "RGB" && !firstDefinition) { QStringList rgb = colorValueE.text().split(" "); if (rgb.length() != 3) { warnPigment << "Invalid RGB color definition (line" << colorValueE.lineNumber() << ", column" << colorValueE.columnNumber() << ")"; } float r = rgb.at(0).toFloat(&status); float g = rgb.at(1).toFloat(&status); float b = rgb.at(2).toFloat(&status); if (!status) { warnPigment << "Invalid float definition (line" << colorValueE.lineNumber() << ", column" << colorValueE.columnNumber() << ")"; } QString space = colorValueE.attribute("space"); const KoColorSpace *colorSpace = KoColorSpaceRegistry::instance()->colorSpace(RGBAColorModelID.id(), Float32BitsColorDepthID.id(), srgb); if (!space.isEmpty()) { // Try loading the profile and add it to the registry if (!fileColorSpaces.contains(space)) { store->enterDirectory("profiles"); store->open(space); QByteArray data; data.resize(store->size()); data = store->read(store->size()); store->close(); const KoColorProfile *profile = KoColorSpaceRegistry::instance()->createColorProfile(RGBAColorModelID.id(), Float32BitsColorDepthID.id(), data); if (profile && profile->valid()) { KoColorSpaceRegistry::instance()->addProfile(profile); colorSpace = KoColorSpaceRegistry::instance()->colorSpace(RGBAColorModelID.id(), Float32BitsColorDepthID.id(), profile); fileColorSpaces.insert(space, colorSpace); } } else { colorSpace = fileColorSpaces.value(space); } } KoColor c(colorSpace); reinterpret_cast(c.data())[0] = r; reinterpret_cast(c.data())[1] = g; reinterpret_cast(c.data())[2] = b; c.setOpacity(OPACITY_OPAQUE_F); currentEntry.setColor(c); firstDefinition = true; } else { warnPigment << "Color space not implemented:" << model << "(line" << colorValueE.lineNumber() << ", column "<< colorValueE.columnNumber() << ")"; } } if (firstDefinition) { materialsBook.insert(currentEntry.id(), currentEntry); } else { warnPigment << "No supported color spaces for the current color (line" << colorElement.lineNumber() << ", column "<< colorElement.columnNumber() << ")"; return false; } } // End colors // Now decide which ones will go into the palette for(;!swatch.isNull(); swatch = swatch.nextSiblingElement()) { QString type = swatch.tagName(); if (type.isEmpty() || type.isNull()) { warnPigment << "Invalid swatch/group definition (no id) (line" << swatch.lineNumber() << ", column" << swatch.columnNumber() << ")"; return false; } else if (type == "swatch") { QString id = swatch.attribute("material"); if (id.isEmpty() || id.isNull()) { warnPigment << "Invalid swatch definition (no material id) (line" << swatch.lineNumber() << ", column" << swatch.columnNumber() << ")"; return false; } if (materialsBook.contains(id)) { groups[KoColorSet::GLOBAL_GROUP_NAME].addEntry(materialsBook.value(id)); } else { warnPigment << "Invalid swatch definition (material not found) (line" << swatch.lineNumber() << ", column" << swatch.columnNumber() << ")"; return false; } } else if (type == "group") { QDomElement groupMetadata = swatch.firstChildElement("metadata"); if (groupMetadata.isNull()) { warnPigment << "Invalid group definition (missing metadata) (line" << groupMetadata.lineNumber() << ", column" << groupMetadata.columnNumber() << ")"; return false; } QDomElement groupTitle = metadata.firstChildElement("dc:title"); if (groupTitle.isNull()) { warnPigment << "Invalid group definition (missing title) (line" << groupTitle.lineNumber() << ", column" << groupTitle.columnNumber() << ")"; return false; } QString currentGroupName = groupTitle.text(); QDomElement groupSwatch = swatch.firstChildElement("swatch"); while(!groupSwatch.isNull()) { QString id = groupSwatch.attribute("material"); if (id.isEmpty() || id.isNull()) { warnPigment << "Invalid swatch definition (no material id) (line" << groupSwatch.lineNumber() << ", column" << groupSwatch.columnNumber() << ")"; return false; } if (materialsBook.contains(id)) { groups[currentGroupName].addEntry(materialsBook.value(id)); } else { warnPigment << "Invalid swatch definition (material not found) (line" << groupSwatch.lineNumber() << ", column" << groupSwatch.columnNumber() << ")"; return false; } groupSwatch = groupSwatch.nextSiblingElement("swatch"); } } } // End palette } buf.close(); return true; } bool KoColorSet::Private::loadXml() { bool res = false; QXmlStreamReader *xml = new QXmlStreamReader(data); if (xml->readNextStartElement()) { QStringRef paletteId = xml->name(); if (QStringRef::compare(paletteId, "SCRIBUSCOLORS", Qt::CaseInsensitive) == 0) { // Scribus dbgPigment << "XML palette: " << colorSet->filename() << ", Scribus format"; res = loadScribusXmlPalette(colorSet, xml); } else { // Unknown XML format xml->raiseError("Unknown XML palette format. Expected SCRIBUSCOLORS, found " + paletteId); } } // If there is any error (it should be returned through the stream) if (xml->hasError() || !res) { warnPigment << "Illegal XML palette:" << colorSet->filename(); warnPigment << "Error (line"<< xml->lineNumber() << ", column" << xml->columnNumber() << "):" << xml->errorString(); return false; } else { dbgPigment << "XML palette parsed successfully:" << colorSet->filename(); return true; } } bool KoColorSet::Private::saveKpl(QIODevice *dev) const { QScopedPointer store(KoStore::createStore(dev, KoStore::Write, "application/x-krita-palette", KoStore::Zip)); if (!store || store->bad()) return false; QSet colorSpaces; { QDomDocument doc; QDomElement root = doc.createElement("Colorset"); root.setAttribute("version", "1.0"); root.setAttribute("name", colorSet->name()); root.setAttribute("comment", comment); root.setAttribute("columns", groups[KoColorSet::GLOBAL_GROUP_NAME].columnCount()); - root.setAttribute("rows", colorSet->rowCount()); + root.setAttribute("rows", groups[KoColorSet::GLOBAL_GROUP_NAME].rowCount()); saveKplGroup(doc, root, colorSet->getGroup(KoColorSet::GLOBAL_GROUP_NAME), colorSpaces); for (const QString &groupName : groupNames) { if (groupName == KoColorSet::GLOBAL_GROUP_NAME) { continue; } QDomElement gl = doc.createElement("Group"); gl.setAttribute("name", groupName); root.appendChild(gl); saveKplGroup(doc, gl, colorSet->getGroup(groupName), colorSpaces); } doc.appendChild(root); if (!store->open("colorset.xml")) { return false; } QByteArray ba = doc.toByteArray(); if (store->write(ba) != ba.size()) { return false; } if (!store->close()) { return false; } } QDomDocument doc; QDomElement profileElement = doc.createElement("Profiles"); for (const KoColorSpace *colorSpace : colorSpaces) { QString fn = QFileInfo(colorSpace->profile()->fileName()).fileName(); if (!store->open(fn)) { return false; } QByteArray profileRawData = colorSpace->profile()->rawData(); if (!store->write(profileRawData)) { return false; } if (!store->close()) { return false; } QDomElement el = doc.createElement("Profile"); el.setAttribute("filename", fn); el.setAttribute("name", colorSpace->profile()->name()); el.setAttribute("colorModelId", colorSpace->colorModelId().id()); el.setAttribute("colorDepthId", colorSpace->colorDepthId().id()); profileElement.appendChild(el); } doc.appendChild(profileElement); if (!store->open("profiles.xml")) { return false; } QByteArray ba = doc.toByteArray(); if (store->write(ba) != ba.size()) { return false; } if (!store->close()) { return false; } return store->finalize(); } void KoColorSet::Private::saveKplGroup(QDomDocument &doc, QDomElement &parentEle, const KisSwatchGroup *group, QSet &colorSetSet) const { parentEle.setAttribute("rows", QString::number(group->rowCount())); parentEle.setAttribute("columns", QString::number(group->columnCount())); for (const SwatchInfoType & info : group->infoList()) { const KoColorProfile *profile = info.swatch.color().colorSpace()->profile(); // Only save non-builtin profiles.= if (!profile->fileName().isEmpty()) { colorSetSet.insert(info.swatch.color().colorSpace()); } QDomElement swatchEle = doc.createElement("ColorSetEntry"); swatchEle.setAttribute("name", info.swatch.name()); swatchEle.setAttribute("id", info.swatch.id()); swatchEle.setAttribute("spot", info.swatch.spotColor() ? "true" : "false"); swatchEle.setAttribute("bitdepth", info.swatch.color().colorSpace()->colorDepthId().id()); info.swatch.color().toXML(doc, swatchEle); QDomElement positionEle = doc.createElement("Position"); positionEle.setAttribute("row", info.row); positionEle.setAttribute("column", info.column); swatchEle.appendChild(positionEle); parentEle.appendChild(swatchEle); } } void KoColorSet::Private::loadKplGroup(const QDomDocument &doc, const QDomElement &parentEle, KisSwatchGroup *group) { Q_UNUSED(doc); - if (!parentEle.attribute("columns").isNull()) { - group->setColumnCount(parentEle.attribute("column").toInt()); - } if (!parentEle.attribute("rows").isNull()) { group->setRowCount(parentEle.attribute("row").toInt()); } for (QDomElement swatchEle = parentEle.firstChildElement("ColorSetEntry"); !swatchEle.isNull(); swatchEle = swatchEle.nextSiblingElement("ColorSetEntry")) { QString colorDepthId = swatchEle.attribute("bitdepth", Integer8BitsColorDepthID.id()); KisSwatch entry; entry.setColor(KoColor::fromXML(swatchEle.firstChildElement(), colorDepthId)); entry.setName(swatchEle.attribute("name")); entry.setId(swatchEle.attribute("id")); entry.setSpotColor(swatchEle.attribute("spot", "false") == "true" ? true : false); QDomElement positionEle = swatchEle.firstChildElement("Position"); if (!positionEle.isNull()) { int rowNumber = positionEle.attribute("row").toInt(); int columnNumber = positionEle.attribute("column").toInt(); if (columnNumber < 0 || - columnNumber >= group->columnCount() || + columnNumber >= colorSet->columnCount() || rowNumber < 0 ) { warnPigment << "Swatch" << entry.name() << "of palette" << colorSet->name() << "has invalid position."; continue; } group->setEntry(entry, columnNumber, rowNumber); } else { group->addEntry(entry); } } } diff --git a/libs/widgets/KisDlgPaletteEditor.cpp b/libs/widgets/KisDlgPaletteEditor.cpp index 9fe41b8b62..5f46f46ba5 100644 --- a/libs/widgets/KisDlgPaletteEditor.cpp +++ b/libs/widgets/KisDlgPaletteEditor.cpp @@ -1,182 +1,240 @@ #include #include #include #include #include #include #include #include +#include +#include #include #include -#include -#include #include #include "KisDlgPaletteEditor.h" KisDlgPaletteEditor::KisDlgPaletteEditor() : m_ui(new Ui_WdgDlgPaletteEditor) , m_actAddGroup(new QAction(i18n("Add a group"))) , m_actDelGroup(new QAction(i18n("Delete this group"))) - , m_group(Q_NULLPTR) + , m_actRenGroup(new QAction(i18n("Rename this group"))) + , m_currentGroupOriginalName(KoColorSet::GLOBAL_GROUP_NAME) { m_ui->setupUi(this); m_ui->gbxPalette->setTitle(i18n("Palette settings")); m_ui->labelFilename->setText(i18n("Filename")); m_ui->labelName->setText(i18n("Palette Name")); m_ui->bnAddGroup->setDefaultAction(m_actAddGroup.data()); m_ui->gbxGroup->setTitle(i18n("Group settings")); m_ui->labelColCount->setText(i18n("Column count")); m_ui->labelRowCount->setText(i18n("Row count")); m_ui->bnDelGroup->setDefaultAction(m_actDelGroup.data()); + m_ui->bnRenGroup->setDefaultAction(m_actRenGroup.data()); connect(m_actAddGroup.data(), SIGNAL(triggered(bool)), SLOT(slotAddGroup())); connect(m_actDelGroup.data(), SIGNAL(triggered(bool)), SLOT(slotDelGroup())); + connect(m_actRenGroup.data(), SIGNAL(triggered(bool)), SLOT(slotRenGroup())); connect(m_ui->spinBoxRow, SIGNAL(valueChanged(int)), SLOT(slotRowCountChanged(int))); } KisDlgPaletteEditor::~KisDlgPaletteEditor() { } void KisDlgPaletteEditor::setPalette(KoColorSet *colorSet) { m_colorSet = colorSet; if (m_colorSet.isNull()) { return; } - if (!m_original.isNull()) { - delete m_original.data(); - } m_original.reset(new OriginalPaletteInfo); m_ui->lineEditName->setText(m_colorSet->name()); m_original->name = m_colorSet->name(); m_ui->lineEditFilename->setText(m_colorSet->filename()); m_original->filename = m_colorSet->filename(); m_ui->spinBoxCol->setValue(m_colorSet->columnCount()); m_original->columnCount = m_colorSet->columnCount(); m_ui->ckxGlobal->setCheckState(m_colorSet->isGlobal() ? Qt::Checked : Qt::Unchecked); m_original->isGlobal = m_colorSet->isGlobal(); m_ui->ckxReadOnly->setCheckState(!m_colorSet->isEditable() ? Qt::Checked : Qt::Unchecked); m_original->isReadOnly = !m_colorSet->isEditable(); for (const QString & groupName : m_colorSet->getGroupNames()) { KisSwatchGroup *group = m_colorSet->getGroup(groupName); m_groups[groupName] = GroupInfoType(groupName, group->rowCount()); m_original->groups[groupName] = GroupInfoType(groupName, group->rowCount()); m_ui->cbxGroup->addItem(groupName); } - m_group = m_colorSet->getGlobalGroup(); - m_ui->cbxGroup->setCurrentText(KoColorSet::GLOBAL_GROUP_NAME); connect(m_ui->cbxGroup, SIGNAL(currentTextChanged(QString)), SLOT(slotGroupChosen(QString))); - - m_ui->spinBoxRow->setValue(m_group->rowCount()); - - if (!m_colorSet->isEditable()) { - m_ui->lineEditName->setEnabled(false); - m_ui->lineEditFilename->setEnabled(false); - m_ui->spinBoxCol->setEnabled(false); - m_ui->spinBoxRow->setEnabled(false); - m_ui->ckxGlobal->setEnabled(false); - m_ui->ckxReadOnly->setEnabled(false); - m_ui->bnAddGroup->setEnabled(false); - m_ui->bnDelGroup->setEnabled(false); - } + m_ui->cbxGroup->setCurrentText(KoColorSet::GLOBAL_GROUP_NAME); + m_ui->bnDelGroup->setEnabled(false); + m_ui->bnRenGroup->setEnabled(false); + + m_ui->spinBoxRow->setValue(m_groups[KoColorSet::GLOBAL_GROUP_NAME].second); + + bool canWrite = m_colorSet->isEditable(); + m_ui->lineEditName->setEnabled(canWrite); + m_ui->lineEditFilename->setEnabled(canWrite); + m_ui->spinBoxCol->setEnabled(canWrite); + m_ui->spinBoxRow->setEnabled(canWrite); + m_ui->ckxGlobal->setEnabled(canWrite); + m_ui->ckxReadOnly->setEnabled(canWrite); + m_ui->bnAddGroup->setEnabled(canWrite); + m_ui->bnDelGroup->setEnabled(canWrite); + m_ui->bnRenGroup->setEnabled(canWrite); } QString KisDlgPaletteEditor::name() const { return m_ui->lineEditName->text(); } QString KisDlgPaletteEditor::filename() const { return m_ui->lineEditFilename->text(); } int KisDlgPaletteEditor::columnCount() const { return m_ui->spinBoxCol->value(); } bool KisDlgPaletteEditor::isGlobal() const { return m_ui->ckxGlobal->checkState() == Qt::Checked; } bool KisDlgPaletteEditor::isReadOnly() const { return m_ui->ckxReadOnly->checkState() == Qt::Checked; } bool KisDlgPaletteEditor::isModified() const { Q_ASSERT(!m_original.isNull()); return m_original->isReadOnly != isReadOnly() || m_original->isGlobal != isGlobal() || m_original->name != name() || m_original->filename != filename() || m_original->columnCount != columnCount() || m_original->groups != m_groups; } +bool KisDlgPaletteEditor::groupRemoved(const QString &groupName) const +{ + return m_original->groups.contains(groupName) && !m_groups.contains(groupName); +} + +QString KisDlgPaletteEditor::groupRenamedTo(const QString &oriGroupName) const +{ + if (!m_groups.contains(oriGroupName) || m_groups[oriGroupName].first == oriGroupName) { + return QString(); + } + return m_groups[oriGroupName].first; +} + void KisDlgPaletteEditor::slotAddGroup() { KoDialog dlg; QVBoxLayout layout(&dlg); dlg.mainWidget()->setLayout(&layout); QLabel lblName(i18n("Name"), &dlg); layout.addWidget(&lblName); QLineEdit leName(&dlg); layout.addWidget(&leName); QLabel lblRowCount(i18n("Row count"), &dlg); layout.addWidget(&lblRowCount); QSpinBox spxRow(&dlg); layout.addWidget(&spxRow); if (dlg.exec() != QDialog::Accepted) { return; } if (m_colorSet->getGroup(leName.text())) { QMessageBox msgNameDuplicate; msgNameDuplicate.setText(i18n("Group already exists")); msgNameDuplicate.setWindowTitle(i18n("Group already exists! Group not added.")); msgNameDuplicate.exec(); return; } m_colorSet->addGroup(leName.text()); m_colorSet->getGroup(leName.text())->setRowCount(spxRow.value()); m_groups.insert(leName.text(), GroupInfoType(leName.text(), spxRow.value())); + m_ui->cbxGroup->addItem(leName.text()); +} - qDebug() << "Adding a group"; +void KisDlgPaletteEditor::slotRenGroup() +{ + KoDialog dlg; + QFormLayout form(&dlg); + dlg.mainWidget()->setLayout(&form); + QLineEdit leNewName; + leNewName.setText(m_groups[m_currentGroupOriginalName].first); + form.addRow(i18nc("Renaming swatch group", "New name"), &leNewName); + if (dlg.exec() != KoDialog::Accepted) { return; } + if (leNewName.text().isEmpty()) { return; } + if (m_groups.contains(leNewName.text())) { return; } + m_groups[m_currentGroupOriginalName].first = leNewName.text(); + int idx = m_ui->cbxGroup->currentIndex(); + m_ui->cbxGroup->removeItem(idx); + m_ui->cbxGroup->insertItem(idx, leNewName.text()); + m_ui->cbxGroup->setCurrentIndex(idx); } void KisDlgPaletteEditor::slotDelGroup() { - if (m_group->name() == KoColorSet::GLOBAL_GROUP_NAME) { + if (m_currentGroupOriginalName == KoColorSet::GLOBAL_GROUP_NAME) { QMessageBox msgNameDuplicate; msgNameDuplicate.setText(i18n("Can't delete group")); msgNameDuplicate.setWindowTitle(i18n("Can't delete main swatch of a palette.")); msgNameDuplicate.exec(); return; } - m_ui->cbxGroup->removeItem(m_ui->cbxGroup->findText(m_group->name())); - m_colorSet->removeGroup(m_group->name()); + KoDialog window(this); + window.setWindowTitle(i18nc("@title:window","Removing Group")); + QFormLayout editableItems(&window); + QCheckBox chkKeep(&window); + window.mainWidget()->setLayout(&editableItems); + editableItems.addRow(i18nc("Shows up when deleting a swatch group", "Keep the Colors"), &chkKeep); + if (window.exec() != KoDialog::Accepted) { return; } + m_ui->cbxGroup->removeItem(m_ui->cbxGroup->findText(m_groups[m_currentGroupOriginalName].first)); m_ui->cbxGroup->setCurrentIndex(0); - qDebug() << "Deleting current group"; + m_groups.remove(m_currentGroupOriginalName); } void KisDlgPaletteEditor::slotGroupChosen(const QString &groupName) { - m_group = m_colorSet->getGroup(groupName); - m_ui->spinBoxRow->setValue(m_group->rowCount()); + if (groupName == KoColorSet::GLOBAL_GROUP_NAME) { + m_ui->bnDelGroup->setEnabled(false); + m_ui->bnRenGroup->setEnabled(false); + } else { + m_ui->bnDelGroup->setEnabled(true); + m_ui->bnRenGroup->setEnabled(true); + } + QString oldName = oldNameFromNewName(groupName); + if (oldName.isEmpty()) { + oldName = KoColorSet::GLOBAL_GROUP_NAME; + } + m_currentGroupOriginalName = oldName; + m_ui->spinBoxRow->setValue(m_groups[oldName].second); } -int KisDlgPaletteEditor::groupRowNumber(const QString &name) +int KisDlgPaletteEditor::groupRowNumber(const QString &groupName) { - return m_groups[name].second; + return m_groups[groupName].second; } void KisDlgPaletteEditor::slotRowCountChanged(int newCount) { - m_groups[m_group->name()].second = newCount; + m_groups[m_currentGroupOriginalName].second = newCount; +} + +QString KisDlgPaletteEditor::oldNameFromNewName(const QString &newName) +{ + for (const QString &oldGroupName : m_groups.keys()) { + if (m_groups[oldGroupName].first == newName) { + return oldGroupName; + } + } + return QString(); } diff --git a/libs/widgets/KisDlgPaletteEditor.h b/libs/widgets/KisDlgPaletteEditor.h index 2a797f8638..de843e4796 100644 --- a/libs/widgets/KisDlgPaletteEditor.h +++ b/libs/widgets/KisDlgPaletteEditor.h @@ -1,65 +1,87 @@ #ifndef KISDLGPALETTEEDITOR_H #define KISDLGPALETTEEDITOR_H #include #include #include #include #include class QAction; class KoColorSet; class KisSwatchGroup; class Ui_WdgDlgPaletteEditor; class KisDlgPaletteEditor : public QDialog { Q_OBJECT private: typedef QPair GroupInfoType; // first is name, second is rowCount struct OriginalPaletteInfo { QString name; QString filename; int columnCount; bool isGlobal; bool isReadOnly; QHash groups; }; public: explicit KisDlgPaletteEditor(); ~KisDlgPaletteEditor(); public: void setPalette(KoColorSet *); KoColorSet* palette() const { return m_colorSet; } QString name() const; QString filename() const; int columnCount() const; bool isGlobal() const; bool isReadOnly() const; bool isModified() const; - int groupRowNumber(const QString &); + /** + * @brief groupRemoved + * @param groupName original group name + * @return if the group is removed + */ + bool groupRemoved(const QString &groupName) const; + /** + * @brief groupRenamedTo + * @param groupName original group name + * @return new group name if renamed; emptry string if not renamed or not + * allowed to be renamed + */ + QString groupRenamedTo(const QString &oriGroupName) const; + /** + * @brief groupRowNumber + * @param groupName ORIGINAL group name + * @return new group row number of the group + */ + int groupRowNumber(const QString &groupName); private Q_SLOTS: void slotDelGroup(); void slotAddGroup(); + void slotRenGroup(); + void slotGroupChosen(const QString &groupName); void slotRowCountChanged(int); private: + QString oldNameFromNewName(const QString &newName); private: QScopedPointer m_ui; QScopedPointer m_actAddGroup; QScopedPointer m_actDelGroup; + QScopedPointer m_actRenGroup; QPointer m_colorSet; QScopedPointer m_original; - QHash m_groups; - KisSwatchGroup *m_group; + QHash m_groups; // first is original group name + QString m_currentGroupOriginalName; }; #endif // KISDLGPALETTEEDITOR_H diff --git a/libs/widgets/KisPaletteListWidget.cpp b/libs/widgets/KisPaletteListWidget.cpp index 57014d8218..88a4aa9e8d 100644 --- a/libs/widgets/KisPaletteListWidget.cpp +++ b/libs/widgets/KisPaletteListWidget.cpp @@ -1,260 +1,267 @@ #include #include #include #include #include #include #include +#include #include #include "KisDlgPaletteEditor.h" #include #include "KisPaletteListWidget.h" #include "KisPaletteListWidget_p.h" /* bool KisPaletteView::addGroupWithDialog() { KoDialog *window = new KoDialog(); window->setWindowTitle(i18nc("@title:window","Add a new group")); QFormLayout *editableItems = new QFormLayout(); window->mainWidget()->setLayout(editableItems); QLineEdit *lnName = new QLineEdit(); editableItems->addRow(i18nc("Name for a group", "Name"), lnName); lnName->setText(i18nc("Part of default name for a new group", "Color Group")+""+QString::number(m_d->model->colorSet()->getGroupNames().size()+1)); if (window->exec() == KoDialog::Accepted) { QString groupName = lnName->text(); m_d->model->addGroup(groupName); m_d->model->colorSet()->save(); return true; } return false; } */ KisPaletteListWidget::KisPaletteListWidget(QWidget *parent) : QWidget(parent) , m_ui(new Ui_WdgPaletteListWidget) , m_d(new KisPaletteListWidgetPrivate(this)) { m_d->actAdd.reset(new QAction(KisIconUtils::loadIcon("list-add"), i18n("Add a new palette"))); m_d->actRemove.reset(new QAction(KisIconUtils::loadIcon("list-remove"), i18n("Remove current palette"))); m_d->actModify.reset(new QAction(KisIconUtils::loadIcon("edit-rename"), i18n("Rename choosen palette"))); m_d->actImport.reset(new QAction(KisIconUtils::loadIcon("document-import"), i18n("Import a new palette from file"))); m_d->actExport.reset(new QAction(KisIconUtils::loadIcon("document-export"), i18n("Export current palette to file"))); m_d->model->setColumnCount(1); m_ui->setupUi(this); m_ui->bnAdd->setDefaultAction(m_d->actAdd.data()); m_ui->bnRemove->setDefaultAction(m_d->actRemove.data()); m_ui->bnEdit->setDefaultAction(m_d->actModify.data()); m_ui->bnImport->setDefaultAction(m_d->actImport.data()); m_ui->bnExport->setDefaultAction(m_d->actExport.data()); connect(m_d->actAdd.data(), SIGNAL(triggered()), SLOT(slotAdd())); connect(m_d->actRemove.data(), SIGNAL(triggered()), SLOT(slotRemove())); connect(m_d->actModify.data(), SIGNAL(triggered()), SLOT(slotModify())); connect(m_d->actImport.data(), SIGNAL(triggered()), SLOT(slotImport())); connect(m_d->actExport.data(), SIGNAL(triggered()), SLOT(slotExport())); m_d->itemChooser->setItemDelegate(m_d->delegate.data()); m_d->itemChooser->setRowHeight(40); m_d->itemChooser->setColumnCount(1); m_d->itemChooser->showButtons(false); m_ui->viewPalette->setLayout(new QHBoxLayout(m_ui->viewPalette)); m_ui->viewPalette->layout()->addWidget(m_d->itemChooser.data()); connect(m_d->itemChooser.data(), SIGNAL(resourceSelected(KoResource *)), SLOT(slotPaletteResourceSelected(KoResource*))); } KisPaletteListWidget::~KisPaletteListWidget() { } void KisPaletteListWidget::slotPaletteResourceSelected(KoResource *r) { emit sigPaletteSelected(static_cast(r)); } void KisPaletteListWidget::slotAdd() { KoColorSet *newColorSet = new KoColorSet(newPaletteFileName()); newColorSet->setPaletteType(KoColorSet::KPL); newColorSet->setIsGlobal(false); newColorSet->setIsEditable(true); newColorSet->setName("New Palette"); m_d->rAdapter->addResource(newColorSet); m_d->itemChooser->setCurrentResource(newColorSet); emit sigPaletteListChanged(); } void KisPaletteListWidget::slotRemove() { if (m_d->itemChooser->currentResource()) { KoColorSet *group = static_cast(m_d->itemChooser->currentResource()); if (!group || !group->isEditable()) { return; } m_d->rAdapter->removeResource(m_d->itemChooser->currentResource()); if (group->isGlobal()) { QFile::remove(m_d->itemChooser->currentResource()->filename()); } } m_d->itemChooser->setCurrentItem(0, 0); emit sigPaletteListChanged(); } void KisPaletteListWidget::slotModify() { KisDlgPaletteEditor dlg; KoColorSet *colorSet = static_cast(m_d->itemChooser->currentResource()); if (!colorSet) { return; } dlg.setPalette(colorSet); if (dlg.exec() != QDialog::Accepted){ return; } if (!dlg.isModified()) { return; } colorSet->setName(dlg.name()); colorSet->setColumnCount(dlg.columnCount()); for (const QString &groupName : colorSet->getGroupNames()) { colorSet->getGroup(groupName)->setRowCount(dlg.groupRowNumber(groupName)); } if (dlg.isGlobal()) { setPaletteGlobal(colorSet); } else { setPaletteNonGlobal(colorSet); } + for (const QString &groupName : colorSet->getGroupNames()) { + if (dlg.groupRemoved(groupName)) { + colorSet->removeGroup(groupName); + continue; + } + colorSet->getGroup(groupName)->setRowCount(dlg.groupRowNumber(groupName)); + if (!dlg.groupRenamedTo(groupName).isEmpty()) { + colorSet->changeGroupName(groupName, dlg.groupRenamedTo(groupName)); + } + } for (const KoResource *r : m_d->rAdapter->resources()) { if (r != colorSet && r->filename() == dlg.filename()) { QMessageBox msgFilenameDuplicate; msgFilenameDuplicate.setWindowTitle(i18n("Duplicate filename")); msgFilenameDuplicate.setText(i18n("Duplicate filename! Palette not saved.")); msgFilenameDuplicate.exec(); return; } } colorSet->setFilename(dlg.filename()); emit sigPaletteSelected(colorSet); // to update elements in the docker emit sigPaletteListChanged(); } void KisPaletteListWidget::slotImport() { emit sigPaletteListChanged(); } void KisPaletteListWidget::slotExport() { } void KisPaletteListWidget::setPaletteGlobal(KoColorSet *colorSet) { if (QPointer(colorSet).isNull()) { return; } KoResourceServer *rserver = KoResourceServerProvider::instance()->paletteServer(); QString saveLocation = rserver->saveLocation(); QString name = colorSet->filename(); - qDebug() << saveLocation; - qDebug() << name; QFileInfo fileInfo(saveLocation + name); colorSet->setFilename(fileInfo.filePath()); - qDebug() << fileInfo.filePath(); colorSet->setIsGlobal(true); - qDebug() << colorSet->save(); } void KisPaletteListWidget::setPaletteNonGlobal(KoColorSet *colorSet) { if (QPointer(colorSet).isNull()) { return; } QString filename = newPaletteFileName(); QFile::remove(colorSet->filename()); colorSet->setFilename(filename); colorSet->setIsGlobal(false); } QString KisPaletteListWidget::newPaletteFileName() { KoColorSet tmpColorSet; QString result = "new_palette_"; QSet nameSet; QList rlist = m_d->rAdapter->resources(); for (const KoResource *r : rlist) { nameSet.insert(r->filename()); } int i = 0; while (nameSet.contains(result + QString::number(i) + tmpColorSet.defaultFileExtension())) { i++; } result = result + QString::number(i) + tmpColorSet.defaultFileExtension(); return result; } /************************* KisPaletteListWidgetPrivate **********************/ KisPaletteListWidgetPrivate::KisPaletteListWidgetPrivate(KisPaletteListWidget *a_c) : c(a_c) , rAdapter(new KoResourceServerAdapter(KoResourceServerProvider::instance()->paletteServer())) , itemChooser(new KoResourceItemChooser(rAdapter, a_c)) , model(new Model(rAdapter, a_c)) , delegate(new Delegate(a_c)) { } KisPaletteListWidgetPrivate::~KisPaletteListWidgetPrivate() { } /******************* KisPaletteListWidgetPrivate::Delegate ******************/ KisPaletteListWidgetPrivate::Delegate::Delegate(QObject *parent) : QAbstractItemDelegate(parent) { } KisPaletteListWidgetPrivate::Delegate::~Delegate() { } void KisPaletteListWidgetPrivate::Delegate::paint(QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex & index) const { painter->save(); if (!index.isValid()) return; KoResource* resource = static_cast(index.internalPointer()); KoColorSet* colorSet = static_cast(resource); QRect previewRect(option.rect.x() + 2, option.rect.y() + 2, option.rect.height() - 4, option.rect.height() - 4); painter->drawImage(previewRect, colorSet->image()); if (option.state & QStyle::State_Selected) { painter->fillRect(option.rect, option.palette.highlight()); painter->drawImage(previewRect, colorSet->image()); painter->setPen(option.palette.highlightedText().color()); } else { painter->setBrush(option.palette.text().color()); } painter->drawText(option.rect.x() + previewRect.width() + 10, option.rect.y() + painter->fontMetrics().ascent() + 5, colorSet->name()); painter->restore(); } inline QSize KisPaletteListWidgetPrivate::Delegate::sizeHint(const QStyleOptionViewItem & option, const QModelIndex &) const { return option.decorationSize; } diff --git a/libs/widgets/KisPaletteModel.cpp b/libs/widgets/KisPaletteModel.cpp index 94a5929859..6c0d6127d4 100644 --- a/libs/widgets/KisPaletteModel.cpp +++ b/libs/widgets/KisPaletteModel.cpp @@ -1,501 +1,499 @@ /* * Copyright (c) 2013 Sven Langkamp * * 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 "KisPaletteModel.h" #include #include #include #include #include #include #include #include #include KisPaletteModel::KisPaletteModel(QObject* parent) : QAbstractTableModel(parent) , m_colorSet(0) , m_displayRenderer(KoDumbColorDisplayRenderer::instance()) { } KisPaletteModel::~KisPaletteModel() { } QVariant KisPaletteModel::data(const QModelIndex& index, int role) const { // row is set to -1 when it's group name row bool groupNameRow = index.row() != 0 && m_groupNameRows.contains(index.row()); if (groupNameRow) { return dataForGroupNameRow(index, role); } else { return dataForSwatch(index, role); } } int KisPaletteModel::rowCount(const QModelIndex& /*parent*/) const { if (!m_colorSet) return 0; return m_colorSet->rowCount() // count of color rows + m_groupNameRows.size() // rows for names - 1; // global doesn't have a name } int KisPaletteModel::columnCount(const QModelIndex& /*parent*/) const { if (m_colorSet && m_colorSet->columnCount() > 0) { return m_colorSet->columnCount(); } if (!m_colorSet) { return 0; } return 16; } Qt::ItemFlags KisPaletteModel::flags(const QModelIndex& index) const { if (index.isValid()) { return Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsUserCheckable | Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled; } return Qt::ItemIsDropEnabled; } QModelIndex KisPaletteModel::index(int row, int column, const QModelIndex& parent) const { Q_UNUSED(parent); Q_ASSERT(m_colorSet); KisSwatchGroup *group = Q_NULLPTR; int groupNameRow = groupNameRowForRow(row); group = m_colorSet->getGroup(m_groupNameRows[groupNameRow]); Q_ASSERT(group); - qDebug() << "KisPaletteModel::index" << row << column << group; return createIndex(row, column, group); } void KisPaletteModel::setColorSet(KoColorSet* colorSet) { beginResetModel(); m_groupNameRows.clear(); m_colorSet = colorSet; int row = -1; for (const QString &groupName : m_colorSet->getGroupNames()) { m_groupNameRows[row] = groupName; row += m_colorSet->getGroup(groupName)->rowCount(); row += 1; // row for group name } endResetModel(); } KoColorSet* KisPaletteModel::colorSet() const { return m_colorSet; } int KisPaletteModel::rowNumberInGroup(int rowInModel) const { if (m_groupNameRows.contains(rowInModel)) { return -1; } for (auto it = m_groupNameRows.keys().rbegin(); it != m_groupNameRows.keys().rend(); it++) { if (*it < rowInModel) { return rowInModel - *it - 1; } } return rowInModel; } bool KisPaletteModel::addEntry(const KisSwatch &entry, const QString &groupName) { KisSwatchGroup *group = m_colorSet->getGroup(groupName); - if (group->checkEntry(group->columnCount(), group->rowCount())) { + if (group->checkEntry(m_colorSet->columnCount(), group->rowCount())) { beginInsertRows(QModelIndex(), rowCount(), rowCount()); m_colorSet->add(entry, groupName); endInsertRows(); } else { beginResetModel(); m_colorSet->add(entry, groupName); endResetModel(); } m_colorSet->save(); return true; } bool KisPaletteModel::removeEntry(const QModelIndex &index, bool keepColors) { if (!qvariant_cast(data(index, IsGroupNameRole))) { static_cast(index.internalPointer())->removeEntry(index.column(), rowNumberInGroup(index.row())); emit dataChanged(index, index); } else { beginResetModel(); int groupNameRow = groupNameRowForRow(index.row()); QString groupName = m_groupNameRows[groupNameRow]; m_colorSet->removeGroup(groupName, keepColors); m_groupNameRows.remove(groupNameRow); endResetModel(); } return true; } bool KisPaletteModel::dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) { return true; /* if (!data->hasFormat("krita/x-colorsetentry") && !data->hasFormat("krita/x-colorsetgroup")) { return false; } if (action == Qt::IgnoreAction) { return false; } int endRow; int endColumn; if (!parent.isValid()) { if (row < 0) { endRow = indexFromId(m_colorSet->nColors()).row(); endColumn = indexFromId(m_colorSet->nColors()).column(); } else { endRow = qMin(row, indexFromId(m_colorSet->nColors()).row()); endColumn = qMin(column, m_colorSet->columnCount()); } } else { endRow = qMin(parent.row(), rowCount()); endColumn = qMin(parent.column(), columnCount()); } if (data->hasFormat("krita/x-colorsetgroup")) { QByteArray encodedData = data->data("krita/x-colorsetgroup"); QDataStream stream(&encodedData, QIODevice::ReadOnly); while (!stream.atEnd()) { QString groupName; stream >> groupName; QModelIndex index = this->index(endRow, 0); if (index.isValid()) { QStringList entryList = qvariant_cast(index.data(RetrieveEntryRole)); QString groupDroppedOn = QString(); if (!entryList.isEmpty()) { groupDroppedOn = entryList.at(0); } int groupIndex = colorSet()->getGroupNames().indexOf(groupName); beginMoveRows( QModelIndex(), groupIndex, groupIndex, QModelIndex(), endRow); m_colorSet->moveGroup(groupName, groupDroppedOn); m_colorSet->save(); endMoveRows(); ++endRow; } } } else { QByteArray encodedData = data->data("krita/x-colorsetentry"); QDataStream stream(&encodedData, QIODevice::ReadOnly); while (!stream.atEnd()) { KoColorSetEntry entry; QString oldGroupName; int indexInGroup; QString colorXml; QString name, id; bool spotColor; stream >> name >> id >> spotColor >> indexInGroup >> oldGroupName >> colorXml; entry.setName(name); entry.setId(id); entry.setSpotColor(spotColor); QDomDocument doc; doc.setContent(colorXml); QDomElement e = doc.documentElement(); QDomElement c = e.firstChildElement(); if (!c.isNull()) { QString colorDepthId = c.attribute("bitdepth", Integer8BitsColorDepthID.id()); entry.setColor(KoColor::fromXML(c, colorDepthId)); } QModelIndex index = this->index(endRow, endColumn); if (qvariant_cast(index.data(IsGroupNameRole))){ endRow+=1; } if (index.isValid()) { // this is to figure out the row of the old color. // That way we can in turn avoid moverows from complaining the // index is out of bounds when using index. // Makes me wonder if we shouldn't just insert the index of the // old color when requesting the mimetype... int i = indexInGroup; if (oldGroupName != QString()) { colorSet()->nColorsGroup(""); //find at which position the group is. int groupIndex = colorSet()->getGroupNames().indexOf(oldGroupName); //add all the groupsizes onto it till we get to our group. for(int g=0; gnColorsGroup(colorSet()->getGroupNames().at(g)); } } QModelIndex indexOld = indexFromId(i); if (action == Qt::MoveAction){ if (indexOld.row()!=qMax(endRow, 0) && indexOld.row()!=qMax(endRow+1,1)) { beginMoveRows(QModelIndex(), indexOld.row(), indexOld.row(), QModelIndex(), qMax(endRow+1,1)); } if (indexOld.column()!=qMax(endColumn, 0) && indexOld.column()!=qMax(endColumn+1,1)) { beginMoveColumns(QModelIndex(), indexOld.column(), indexOld.column(), QModelIndex(), qMax(endColumn+1,1)); } } else { beginInsertRows(QModelIndex(), endRow, endRow); } QStringList entryList = qvariant_cast(index.data(RetrieveEntryRole)); QString entryInGroup = "0"; QString groupName = QString(); if (!entryList.isEmpty()) { groupName = entryList.at(0); entryInGroup = entryList.at(1); } int location = entryInGroup.toInt(); // Insert the entry if (groupName==oldGroupName && qvariant_cast(index.data(IsGroupNameRole))==true) { groupName=QString(); location=m_colorSet->nColorsGroup(); } m_colorSet->insertBefore(entry, location, groupName); if (groupName==oldGroupName && locationremoveAt(indexInGroup, oldGroupName); } m_colorSet->save(); if (action == Qt::MoveAction){ if (indexOld.row()!=qMax(endRow, 0) && indexOld.row()!=qMax(endRow+1,1)) { endMoveRows(); } if (indexOld.column()!=qMax(endColumn, 0) && indexOld.column()!=qMax(endColumn+1,1)) { endMoveColumns(); } } else { endInsertRows(); } ++endRow; } } } return true; */ } QMimeData *KisPaletteModel::mimeData(const QModelIndexList &indexes) const { return new QMimeData(); /* QMimeData *mimeData = new QMimeData(); QByteArray encodedData; QDataStream stream(&encodedData, QIODevice::WriteOnly); QString mimeTypeName = "krita/x-colorsetentry"; //Q_FOREACH(const QModelIndex &index, indexes) { QModelIndex index = indexes.last(); if (index.isValid()) { if (qvariant_cast(index.data(IsGroupNameRole))==false) { KoColorSetEntry entry = colorSetEntryFromIndex(index); QStringList entryList = qvariant_cast(index.data(RetrieveEntryRole)); QString groupName = QString(); int indexInGroup = 0; if (!entryList.isEmpty()) { groupName = entryList.at(0); QString iig = entryList.at(1); indexInGroup = iig.toInt(); } QDomDocument doc; QDomElement root = doc.createElement("Color"); root.setAttribute("bitdepth", entry.color().colorSpace()->colorDepthId().id()); doc.appendChild(root); entry.color().toXML(doc, root); stream << entry.name() << entry.id() << entry.spotColor() << indexInGroup << groupName << doc.toString(); } else { mimeTypeName = "krita/x-colorsetgroup"; QStringList entryList = qvariant_cast(index.data(RetrieveEntryRole)); QString groupName = QString(); if (!entryList.isEmpty()) { groupName = entryList.at(0); } stream << groupName; } } mimeData->setData(mimeTypeName, encodedData); return mimeData; */ } QStringList KisPaletteModel::mimeTypes() const { return QStringList() << "krita/x-colorsetentry" << "krita/x-colorsetgroup"; } Qt::DropActions KisPaletteModel::supportedDropActions() const { return Qt::MoveAction; } void KisPaletteModel::setEntry(const KisSwatch &entry, const QModelIndex &index) { KisSwatchGroup *group = static_cast(index.internalPointer()); Q_ASSERT(group); group->setEntry(entry, index.column(), rowNumberInGroup(index.row())); emit dataChanged(index, index); if (m_colorSet->isGlobal()) { m_colorSet->save(); } } bool KisPaletteModel::renameGroup(const QString &groupName, const QString &newName) { beginResetModel(); bool success = m_colorSet->changeGroupName(groupName, newName); for (auto it = m_groupNameRows.begin(); it != m_groupNameRows.end(); it++) { if (it.value() == groupName) { m_groupNameRows[it.key()] = newName; break; } } endResetModel(); return success; } QVariant KisPaletteModel::dataForGroupNameRow(const QModelIndex &idx, int role) const { KisSwatchGroup *group = static_cast(idx.internalPointer()); Q_ASSERT(group); - qDebug() << "KisPaletteModel::dataForGroupNameRow" << idx.row() << idx.column() << group; QString groupName = group->name(); switch (role) { case Qt::ToolTipRole: case Qt::DisplayRole: { return groupName; } case IsGroupNameRole: { return true; } case CheckSlotRole: { return true; } default: { return QVariant(); } } } QVariant KisPaletteModel::dataForSwatch(const QModelIndex &idx, int role) const { KisSwatchGroup *group = static_cast(idx.internalPointer()); Q_ASSERT(group); bool entryPresent = group->checkEntry(idx.column(), rowNumberInGroup(idx.row())); KisSwatch entry; if (entryPresent) { entry = group->getEntry(idx.column(), rowNumberInGroup(idx.row())); } switch (role) { case Qt::ToolTipRole: case Qt::DisplayRole: { return entryPresent ? entry.name() : i18n("Empty slot"); } case Qt::BackgroundRole: { QColor color(0, 0, 0, 0); if (entryPresent) { color = m_displayRenderer->toQColor(entry.color()); } return QBrush(color); } case IsGroupNameRole: { return false; } case CheckSlotRole: { return entryPresent; } default: { return QVariant(); } } } void KisPaletteModel::setDisplayRenderer(const KoColorDisplayRendererInterface *displayRenderer) { if (displayRenderer) { if (m_displayRenderer) { disconnect(m_displayRenderer, 0, this, 0); } m_displayRenderer = displayRenderer; connect(m_displayRenderer, SIGNAL(displayConfigurationChanged()), SLOT(slotDisplayConfigurationChanged())); } else { m_displayRenderer = KoDumbColorDisplayRenderer::instance(); } } void KisPaletteModel::slotDisplayConfigurationChanged() { beginResetModel(); endResetModel(); } QModelIndex KisPaletteModel::indexForClosest(const KoColor &compare) { KisSwatchGroup::SwatchInfo info = colorSet()->getClosestColorInfo(compare); return createIndex(info.row, info.column, colorSet()->getGroup(info.group)); } KisSwatch KisPaletteModel::getEntry(const QModelIndex &index) { KisSwatchGroup *group = static_cast(index.internalPointer()); if (!group || !group->checkEntry(index.column(), rowNumberInGroup(index.row()))) { return KisSwatch(); } return group->getEntry(index.column(), rowNumberInGroup(index.row())); } int KisPaletteModel::groupNameRowForRow(int rowInModel) const { return rowInModel - rowNumberInGroup(rowInModel) - 1; } diff --git a/libs/widgets/WdgDlgPaletteEditor.ui b/libs/widgets/WdgDlgPaletteEditor.ui index b8808a93ec..0986510f00 100644 --- a/libs/widgets/WdgDlgPaletteEditor.ui +++ b/libs/widgets/WdgDlgPaletteEditor.ui @@ -1,201 +1,214 @@ WdgDlgPaletteEditor 0 0 - 510 - 470 + 524 + 479 Dialog Palette settings Palette Name File name Column count Global Read only Qt::Vertical 20 40 0 0 Add a group Group settings Row count Qt::Vertical 20 40 + + + + + 0 + 0 + + + + Rename this group + + + 0 0 Delete this group Qt::Horizontal QDialogButtonBox::Cancel|QDialogButtonBox::Ok buttonBox rejected() WdgDlgPaletteEditor reject() 316 260 286 274 buttonBox accepted() WdgDlgPaletteEditor accept() 248 254 157 274 diff --git a/libs/widgets/kis_palette_view.cpp b/libs/widgets/kis_palette_view.cpp index cf51e71ca2..aa1bee9a49 100644 --- a/libs/widgets/kis_palette_view.cpp +++ b/libs/widgets/kis_palette_view.cpp @@ -1,320 +1,326 @@ /* * Copyright (c) 2016 Dmitry Kazakov * * 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_palette_view.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "KisPaletteDelegate.h" #include "KisPaletteModel.h" #include "kis_color_button.h" #include int KisPaletteView::MININUM_ROW_HEIGHT = 10; struct KisPaletteView::Private { QPointer model; bool allowPaletteModification; // if modification is allowed from this widget }; KisPaletteView::KisPaletteView(QWidget *parent) : QTableView(parent) , m_d(new Private) { m_d->allowPaletteModification = false; setItemDelegate(new KisPaletteDelegate(this)); setShowGrid(true); setDropIndicatorShown(true); setDragEnabled(true); setAcceptDrops(true); setDragDropMode(QAbstractItemView::InternalMove); setSelectionMode(QAbstractItemView::SingleSelection); /* * without this, a cycle might be created: * the view streches to right border, and this make it need a scroll bar; * after the bar is added, the view shrinks to the bar, and this makes it * no longer need the bar any more, and the bar is removed again */ setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn); // set the size of swatches horizontalHeader()->setVisible(false); verticalHeader()->setVisible(false); horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch); horizontalHeader()->setMinimumSectionSize(MININUM_ROW_HEIGHT); verticalHeader()->setSectionResizeMode(QHeaderView::Fixed); verticalHeader()->setMinimumSectionSize(MININUM_ROW_HEIGHT); connect(horizontalHeader(), SIGNAL(sectionResized(int,int,int)), SLOT(slotHorizontalHeaderResized(int,int,int))); connect(this, SIGNAL(doubleClicked(QModelIndex)), SLOT(slotModifyEntry(QModelIndex))); setAutoFillBackground(true); } KisPaletteView::~KisPaletteView() { } void KisPaletteView::setCrossedKeyword(const QString &value) { /* KisPaletteDelegate *delegate = dynamic_cast(itemDelegate()); KIS_ASSERT_RECOVER_RETURN(delegate); delegate->setCrossedKeyword(value); */ } bool KisPaletteView::addEntryWithDialog(KoColor color) { QScopedPointer window(new KoDialog(this)); window->setWindowTitle(i18nc("@title:window", "Add a new Colorset Entry")); QFormLayout *editableItems = new QFormLayout(window.data()); window->mainWidget()->setLayout(editableItems); QComboBox *cmbGroups = new QComboBox(window.data()); QString defaultGroupName = i18nc("Name for default group", "Default"); cmbGroups->addItem(defaultGroupName); cmbGroups->addItems(m_d->model->colorSet()->getGroupNames()); QLineEdit *lnIDName = new QLineEdit(window.data()); QLineEdit *lnName = new QLineEdit(window.data()); KisColorButton *bnColor = new KisColorButton(window.data()); QCheckBox *chkSpot = new QCheckBox(window.data()); chkSpot->setToolTip(i18nc("@info:tooltip", "A spot color is a color that the printer is able to print without mixing the paints it has available to it. The opposite is called a process color.")); editableItems->addRow(i18n("Group"), cmbGroups); editableItems->addRow(i18n("ID"), lnIDName); editableItems->addRow(i18n("Name"), lnName); editableItems->addRow(i18n("Color"), bnColor); editableItems->addRow(i18nc("Spot color", "Spot"), chkSpot); cmbGroups->setCurrentIndex(0); lnName->setText(i18nc("Part of a default name for a color","Color")+" "+QString::number(m_d->model->colorSet()->colorCount()+1)); lnIDName->setText(QString::number(m_d->model->colorSet()->colorCount()+1)); bnColor->setColor(color); chkSpot->setChecked(false); if (window->exec() == KoDialog::Accepted) { QString groupName = cmbGroups->currentText(); if (groupName == defaultGroupName) { groupName = QString(); } KisSwatch newEntry; newEntry.setColor(bnColor->color()); newEntry.setName(lnName->text()); newEntry.setId(lnIDName->text()); newEntry.setSpotColor(chkSpot->isChecked()); m_d->model->addEntry(newEntry, groupName); return true; } return false; } bool KisPaletteView::removeEntryWithDialog(QModelIndex index) { - bool keepColors = true; + bool keepColors = false; if (qvariant_cast(index.data(KisPaletteModel::IsGroupNameRole))) { - KoDialog *window = new KoDialog(); + QScopedPointer window(new KoDialog(this)); window->setWindowTitle(i18nc("@title:window","Removing Group")); - QFormLayout *editableItems = new QFormLayout(); - QCheckBox *chkKeep = new QCheckBox(); + QFormLayout *editableItems = new QFormLayout(window.data()); + QCheckBox *chkKeep = new QCheckBox(window.data()); window->mainWidget()->setLayout(editableItems); - editableItems->addRow(i18nc("Shows up when deleting a group","Keep the Colors"), chkKeep); - chkKeep->setChecked(keepColors); - if (window->exec() == KoDialog::Accepted) { - keepColors = chkKeep->isChecked(); - m_d->model->removeEntry(index, keepColors); - m_d->model->colorSet()->save(); - } + editableItems->addRow(i18nc("Shows up when deleting a swatch group", "Keep the Colors"), chkKeep); + if (window->exec() != KoDialog::Accepted) { return false; } + keepColors = chkKeep->isChecked(); + m_d->model->removeEntry(index, keepColors); + m_d->model->colorSet()->save(); } else { m_d->model->removeEntry(index, keepColors); if (m_d->model->colorSet()->isGlobal()) { m_d->model->colorSet()->save(); } } return true; } void KisPaletteView::selectClosestColor(const KoColor &color) { KoColorSet* color_set = m_d->model->colorSet(); if (!color_set) { return; } //also don't select if the color is the same as the current selection if (m_d->model->getEntry(currentIndex()).color() == color) { return; } selectionModel()->clearSelection(); QModelIndex index = m_d->model->indexForClosest(color); selectionModel()->setCurrentIndex(index, QItemSelectionModel::Select); } void KisPaletteView::slotFGColorChanged(const KoColor &color) { selectClosestColor(color); } void KisPaletteView::mouseReleaseEvent(QMouseEvent *event) { if (selectedIndexes().size() <= 0) { return; } QModelIndex index = currentIndex(); if (qvariant_cast(index.data(KisPaletteModel::IsGroupNameRole)) == false) { emit sigIndexSelected(index); KisSwatch entry = m_d->model->colorSet()->getColorGlobal(index.column(), index.row()); emit sigColorSelected(entry.color()); } QTableView::mouseReleaseEvent(event); } void KisPaletteView::setPaletteModel(KisPaletteModel *model) { if (m_d->model) { disconnect(m_d->model, 0, this, 0); } m_d->model = model; setModel(model); slotAdditionalGuiUpdate(); connect(model, SIGNAL(dataChanged(QModelIndex,QModelIndex,QVector)), SLOT(slotAdditionalGuiUpdate())); connect(model, SIGNAL(modelReset()), SLOT(slotAdditionalGuiUpdate())); } KisPaletteModel* KisPaletteView::paletteModel() const { return m_d->model; } void KisPaletteView::setAllowModification(bool allow) { m_d->allowPaletteModification = allow; } void KisPaletteView::modifyEntry(QModelIndex index) { if (!m_d->allowPaletteModification) { return; } if (!m_d->model->colorSet()->isEditable()) { return; } KoDialog *dlg = new KoDialog(); QFormLayout *editableItems = new QFormLayout(dlg); dlg->mainWidget()->setLayout(editableItems); QLineEdit *lnGroupName = new QLineEdit(dlg); if (qvariant_cast(index.data(KisPaletteModel::IsGroupNameRole))) { //rename the group. QString groupName = qvariant_cast(index.data(Qt::DisplayRole)); editableItems->addRow(i18nc("Name for a colorgroup","Name"), lnGroupName); lnGroupName->setText(groupName); if (dlg->exec() == KoDialog::Accepted) { + if (lnGroupName->text().isEmpty()) { + return; + } + for (const QString &groupName: m_d->model->colorSet()->getGroupNames()) { + if (groupName == lnGroupName->text()) { + return; + } + } m_d->model->renameGroup(groupName, lnGroupName->text()); } } else { QLineEdit *lnIDName = new QLineEdit(dlg); KisColorButton *bnColor = new KisColorButton(dlg); QCheckBox *chkSpot = new QCheckBox(dlg); KisSwatchGroup *group = static_cast(index.internalPointer()); Q_ASSERT(group); KisSwatch entry = group->getEntry(index.column(), index.row()); chkSpot->setToolTip(i18nc("@info:tooltip", "A spot color is a color that the printer is able to print without mixing the paints it has available to it. The opposite is called a process color.")); editableItems->addRow(i18n("ID"), lnIDName); editableItems->addRow(i18nc("Name for a swatch group", "Name"), lnGroupName); editableItems->addRow(i18n("Color"), bnColor); editableItems->addRow(i18n("Spot"), chkSpot); lnGroupName->setText(entry.name()); lnIDName->setText(entry.id()); bnColor->setColor(entry.color()); chkSpot->setChecked(entry.spotColor()); if (dlg->exec() == KoDialog::Accepted) { entry.setName(lnGroupName->text()); entry.setId(lnIDName->text()); entry.setColor(bnColor->color()); entry.setSpotColor(chkSpot->isChecked()); m_d->model->setEntry(entry, index); emit m_d->model->dataChanged(index, index); } } delete dlg; update(index); } void KisPaletteView::slotModifyEntry(const QModelIndex &index) { modifyEntry(index); } void KisPaletteView::slotHorizontalHeaderResized(int, int, int newSize) { resizeRows(newSize); slotAdditionalGuiUpdate(); } void KisPaletteView::resizeRows(int newSize) { verticalHeader()->setDefaultSectionSize(newSize); verticalHeader()->resizeSections(QHeaderView::Fixed); } void KisPaletteView::removeSelectedEntry() { if (selectedIndexes().size() <= 0) { return; } m_d->model->removeEntry(currentIndex()); } void KisPaletteView::slotAdditionalGuiUpdate() { clearSpans(); resizeRows(verticalHeader()->defaultSectionSize()); for (int groupNameRowNumber : m_d->model->m_groupNameRows.keys()) { if (groupNameRowNumber == -1) { continue; } setSpan(groupNameRowNumber, 0, 1, m_d->model->columnCount()); setRowHeight(groupNameRowNumber, fontMetrics().lineSpacing() + 20); verticalHeader()->resizeSection(groupNameRowNumber, fontMetrics().lineSpacing() + 20); } } void KisPaletteView::setDisplayRenderer(const KoColorDisplayRendererInterface *displayRenderer) { Q_ASSERT(m_d->model); m_d->model->setDisplayRenderer(displayRenderer); }