diff --git a/libs/libkis/PaletteView.cpp b/libs/libkis/PaletteView.cpp index 1725396125..fa4319078a 100644 --- a/libs/libkis/PaletteView.cpp +++ b/libs/libkis/PaletteView.cpp @@ -1,84 +1,84 @@ /* * Copyright (c) 2017 Wolthera van Hövell tot Westerflier * * This program 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 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 Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include struct PaletteView::Private { KisPaletteModel *model = 0; KisPaletteView *widget = 0; bool allowPaletteModification = true; }; PaletteView::PaletteView(QWidget *parent) : QWidget(parent), d(new Private) { d->widget = new KisPaletteView(this); d->model = new KisPaletteModel(); d->widget->setPaletteModel(d->model); this->setLayout(new QVBoxLayout()); this->layout()->addWidget(d->widget); //forward signals. connect(d->widget, SIGNAL(entrySelected(KisSwatch)), this, SIGNAL(entrySelectedForeGround(KisSwatch))); connect(d->widget, SIGNAL(entrySelectedBackGround(KisSwatch)), this, SIGNAL(entrySelectedBackGround(KisSwatch))); } PaletteView::~PaletteView() { delete d->model; } void PaletteView::setPalette(Palette *palette) { d->model->setColorSet(palette->colorSet()); d->widget->setPaletteModel(d->model); } bool PaletteView::addEntryWithDialog(ManagedColor *color) { if (d->model->colorSet()) { return d->widget->addEntryWithDialog(color->color()); } return false; } bool PaletteView::addGroupWithDialog() { if (d->model->colorSet()) { // d->widget->addGroupWithDialog(); return false; } return false; } bool PaletteView::removeSelectedEntryWithDialog() { if (d->model->colorSet()) { return d->widget->removeEntryWithDialog(d->widget->currentIndex()); } return false; } void PaletteView::trySelectClosestColor(ManagedColor *color) { - d->widget->trySelectClosestColor(color->color()); + d->widget->selectClosestColor(color->color()); } diff --git a/libs/pigment/resources/KisSwatchGroup.cpp b/libs/pigment/resources/KisSwatchGroup.cpp index a89e3f5e7f..1c6c486cd4 100644 --- a/libs/pigment/resources/KisSwatchGroup.cpp +++ b/libs/pigment/resources/KisSwatchGroup.cpp @@ -1,152 +1,152 @@ /* 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; KisSwatchGroup::KisSwatchGroup() : m_colorMatrix(DEFAULT_COLUMN_COUNT) , m_rowCount(DEFAULT_ROW_COUNT) , m_colorCount(0) { } void KisSwatchGroup::setEntry(const KisSwatch &e, int x, int y) { Q_ASSERT(x < m_rowCount && x < m_colorMatrix.size() && x >= 0 && y >= 0); if (!checkEntry(x, y)) { m_colorCount++; } m_colorMatrix[x][y] = e; } bool KisSwatchGroup::checkEntry(int x, int y) const { if (y >= m_rowCount || x >= m_colorMatrix.size() || x < 0) { return false; } if (!m_colorMatrix[x].contains(y)) { return false; } return true; } bool KisSwatchGroup::removeEntry(int x, int y) { if (m_colorCount == 0) { return false; } if (x >= m_rowCount || x >= m_colorMatrix.size() || x < 0) { return false; } // QMap::remove returns 1 if key found else 0 if (m_colorMatrix[x].remove(y)) { m_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(); } } m_colorMatrix.resize(columnCount); } KisSwatch KisSwatchGroup::getEntry(int x, int y) const { Q_ASSERT(checkEntry(x, y)); return m_colorMatrix[x][y]; } void KisSwatchGroup::addEntry(const KisSwatch &e) { if (columnCount() == 0) { setColumnCount(DEFAULT_COLUMN_COUNT); } if (m_colorCount == 0) { setEntry(e, 0, 0); return; } int y = 0; for (const Column &c : m_colorMatrix) { if (c.isEmpty()) { continue; } if (y < c.lastKey()) { y = c.lastKey(); } } for (int x = m_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()) { x = 0; y++; if (y == m_rowCount) { m_rowCount++; } } // else just add it to the right setEntry(e, x, y); break; } } } void KisSwatchGroup::setRowCount(int newRowCount) { m_rowCount = newRowCount; for (Column &c : m_colorMatrix) { for (int k : c.keys()) { if (k >= newRowCount) { c.remove(k); m_colorCount--; } } } } QList KisSwatchGroup::infoList() const { QList res; int column = 0; for (const Column &c : m_colorMatrix) { int row = 0; for (const KisSwatch &s : c.values()) { - SwatchInfo i = {s, c.keys()[row++], column}; + SwatchInfo i = {m_name, s, c.keys()[row++], column}; res.append(i); } column++; } return res; } diff --git a/libs/pigment/resources/KisSwatchGroup.h b/libs/pigment/resources/KisSwatchGroup.h index f05094983e..a6fff8dfa4 100644 --- a/libs/pigment/resources/KisSwatchGroup.h +++ b/libs/pigment/resources/KisSwatchGroup.h @@ -1,130 +1,131 @@ /* 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 /** * @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 /* methods */: KisSwatchGroup(); void setName(const QString &name) { m_name = name; } QString name() const { return m_name; } void setColumnCount(int columnCount); int columnCount() const { return m_colorMatrix.size(); } void setRowCount(int newRowCount); int rowCount() const { return m_rowCount; } int colorCount() const { return m_colorCount; } 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(); } private /* member variables */: QString m_name; QVector m_colorMatrix; int m_colorCount; int m_rowCount; }; #endif // KISSWATCHGROUP_H diff --git a/libs/pigment/resources/KoColorSet.cpp b/libs/pigment/resources/KoColorSet.cpp index e1e22f539a..46c5380756 100644 --- a/libs/pigment/resources/KoColorSet.cpp +++ b/libs/pigment/resources/KoColorSet.cpp @@ -1,1687 +1,1554 @@ /* 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" KoColorSet::KoColorSet(const QString& filename) : KoResource(filename) , d(new Private(this)) { if (!filename.isEmpty()) { setValid(true); } } /// Create an copied palette KoColorSet::KoColorSet(const KoColorSet& rhs) : QObject(0) , 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; } return true; // is not global } 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[Private::GLOBAL_GROUP_NAME].colorCount(); } QString KoColorSet::closestColorName(const KoColor color, bool useGivenColorSpace) { quint8 highestPercentage = 0; quint8 testPercentage = 0; KoColor compare = color; QString res; for (int x = 0; x < d->groups[Private::GLOBAL_GROUP_NAME].columnCount(); x++) { for (int y = 0; y < rowCount(); y++ ) { KisSwatch entry = getColorGlobal(x, y); KoColor color = entry.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) { res = entry.name(); highestPercentage = testPercentage; } } } return res; } 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)==false) { return false; } KisSwatchGroup tmp = d->groups.value(oldGroupName); d->groups.remove(oldGroupName); d->groups[newGroupName] = tmp; //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[Private::GLOBAL_GROUP_NAME].setColumnCount(columns); for (KisSwatchGroup &g : d->groups.values()) { // Q_FOREACH doesn't accept non const refs g.setColumnCount(columns); } } int KoColorSet::columnCount() const { return d->groups[Private::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(); 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[Private::GLOBAL_GROUP_NAME].rowCount(); for (int x = 0; x < d->groups[groupName].columnCount(); x++) { for (int y = 0; y < d->groups[groupName].rowCount(); y++) { if (d->groups[groupName].checkEntry(x, y)) { d->groups[Private::GLOBAL_GROUP_NAME].setEntry(d->groups[groupName].getEntry(x, y), x, y + startingRow); } } } } for (int n = 0; ngroupNames.size(); n++) { if (d->groupNames.at(n) == groupName) { d->groupNames.removeAt(n); } } 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]); } bool KoColorSet::isGlobal() const { return d->isGlobal; } void KoColorSet::setIsGlobal(bool isGlobal) { d->isGlobal = isGlobal; } -/* -QString KoColorSet::findGroupByGlobalIndex(quint32 x, quint32 y) +KisSwatchGroup::SwatchInfo KoColorSet::getClosestColorInfo(KoColor compare, bool useGivenColorSpace) { - *index = globalIndex; - QString groupName = QString(); - if ((quint32)d->colors.size()<=*index) { - *index -= (quint32)d->colors.size(); - if (!d->groups.empty() || !d->groupNames.empty()) { - QStringList groupNames = getGroupNames(); - Q_FOREACH (QString name, groupNames) { - quint32 size = (quint32)d->groups.value(name).size(); - if (size<=*index) { - *index -= size; - } else { - groupName = name; - return groupName; - } - } - - } - } - return groupName; -} + KisSwatchGroup::SwatchInfo res; -QString KoColorSet::findGroupByColorName(const QString &name, quint32 *x, quint32 *y) -{ - QString groupName = QString(); - Q_FOREACH (const KisSwatchGroup::Column & col, d->global.colors()) { - Q_FOREACH (const KisSwatch & entry, col.values()) { - if (entry.name() == name) { - *x = static_cast(entry.x()); - *y = static_cast(entry.y()); - return groupName; - } - } - } - QStringList groupNames = getGroupNames(); - Q_FOREACH (QString name, groupNames) { - Q_FOREACH (const KisSwatchGroup::Column & col, d->groups[name].colors()) { - Q_FOREACH (const KisSwatch & entry, col.values()) { - if (entry.name() == name) { - *x = static_cast(entry.x()); - *y = static_cast(entry.y()); - return groupName; - } - } - } - } - return groupName; -} - -QString KoColorSet::findGroupByID(const QString &id, quint32 *index) { - *index = 0; - QString groupName = QString(); - for (int i = 0; icolors.size(); i++) { - if(d->colors.at(i).id() == id) { - *index = (quint32)i; - return groupName; - } - } - QStringList groupNames = getGroupNames(); - Q_FOREACH (QString name, groupNames) { - for (int i=0; igroups[name].size(); i++) { - if(d->groups[name].at(i).id() == id) { - *index = (quint32)i; - groupName = name; - return groupName; - } - } - } - return groupName; - return QString(); -} - -const KisSwatchGroup &KoColorSet::getGroupByName(const QString &groupName, bool &success) const -{ - if (groupName.isEmpty()) { - success = true; - return d->global; - } - if (d->groupNames.contains(groupName)) { - success = true; - return d->groups[groupName]; - } - success = false; - return d->global; -} - -bool KoColorSet::changeColorSetEntry(KisSwatch entry, - QString groupName) -{ - if (!d->groupNames.contains(groupName)) { - return false; - } - if (groupName.isEmpty()) { - d->global.setEntry(entry); - } else { - d->groups[groupName].setEntry(entry); - } - return true; -} - -quint32 KoColorSet::getIndexClosestColor(const KoColor color, bool useGivenColorSpace) -{ - quint32 closestIndex = 0; quint8 highestPercentage = 0; quint8 testPercentage = 0; - KoColor compare = color; - for (quint32 i=0; i < colorCount(); i++) { - KoColor entry = getColorGlobal(i).color(); - if (useGivenColorSpace == true && compare.colorSpace() != entry.colorSpace()) { - entry.convertTo(compare.colorSpace()); - } else if(compare.colorSpace()!=entry.colorSpace()) { - compare.convertTo(entry.colorSpace()); - } - testPercentage = (255 - compare.colorSpace()->difference(compare.data(), entry.data())); - if (testPercentage>highestPercentage) - { - closestIndex = i; - highestPercentage = testPercentage; - } - } - return closestIndex; -} + 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()); -void KoColorSet::removeAt(quint32 x, quint32 y, QString groupName) -{ - if (d->groups.contains(groupName)){ - if ((quint32)d->groups.value(groupName).size()>x) { - d->groups[groupName].remove(x); - } - } else { - if ((quint32)d->colors.size()>x) { - d->colors.remove(x); + } 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; + } } } - if (x >= d->columns || x < 0) - return; - if (d->groups.contains(groupName)){ - d->groups[groupName].removeEntry(x, y); - } else { - d->global.removeEntry(x, y); - } -} - -quint32 KoColorSet::insertBefore(const KisSwatch &c, qint32 index, const QString &groupName) -{ - quint32 newIndex = index; - if (d->groups.contains(groupName)) { - d->groups[groupName].insert(index, c); - } else if (groupName.isEmpty()){ - d->colors.insert(index, c); - } else { - warnPigment << "Couldn't find group to insert to"; - } - return newIndex; - return 0; + return res; } -*/ /********************************KoColorSet::Private**************************/ QString KoColorSet::Private::GLOBAL_GROUP_NAME = QString(); 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); } KoColorSet::Private::Private(KoColorSet *a_colorSet) : colorSet(a_colorSet) , isGlobal(true) { groups[GLOBAL_GROUP_NAME] = KisSwatchGroup(); groupNames.append(GLOBAL_GROUP_NAME); } bool KoColorSet::Private::init() { // just in case this is a reload (eg by KoEditColorSetDialog), groupNames.clear(); groups.clear(); groupNames.append(GLOBAL_GROUP_NAME); groups[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[GLOBAL_GROUP_NAME] so that saveGpl can stay const */ for (int y = 0; y < groups[GLOBAL_GROUP_NAME].rowCount(); y++) { for (int x = 0; x < colorSet->columnCount(); x++) { if (!groups[GLOBAL_GROUP_NAME].checkEntry(x, y)) { continue; } const KisSwatch& entry = groups[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())); 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[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[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(GLOBAL_GROUP_NAME)); 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[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[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[GLOBAL_GROUP_NAME].columnCount()); root.setAttribute("rows", colorSet->rowCount()); saveKplGroup(doc, root, colorSet->getGroup(GLOBAL_GROUP_NAME), colorSpaces); for (const QString &groupName : groupNames) { if (groupName == 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("column").isNull()) { group->setColumnCount(parentEle.attribute("column").toInt()); } if (!parentEle.attribute("row").isNull()) { group->setColumnCount(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() || rowNumber < 0 || rowNumber >= group->rowCount() ) { warnPigment << "Swatch" << entry.name() << "from group" << group->name() << "of palette" << colorSet->name() << "has invalid position."; continue; } group->setEntry(entry, columnNumber, rowNumber); } else { group->addEntry(entry); } } } diff --git a/libs/pigment/resources/KoColorSet.h b/libs/pigment/resources/KoColorSet.h index 55445c01be..6a2a91a673 100644 --- a/libs/pigment/resources/KoColorSet.h +++ b/libs/pigment/resources/KoColorSet.h @@ -1,221 +1,198 @@ /* 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 */ #ifndef KOCOLORSET #define KOCOLORSET #include #include #include #include #include #include "KoColor.h" #include "KisSwatch.h" #include "KisSwatchGroup.h" /** * Also called palette. * Open Gimp, Photoshop or RIFF palette files. This is a straight port * from the Gimp. */ class KRITAPIGMENT_EXPORT KoColorSet : public QObject, public KoResource { Q_OBJECT public: enum PaletteType { UNKNOWN = 0, GPL, // GIMP RIFF_PAL, // RIFF ACT, // Photoshop binary PSP_PAL, // PaintShop Pro ACO, // Photoshop Swatches XML, // XML palette (Scribus) KPL, // KoColor-based XML palette SBZ // SwatchBooker }; /** * Load a color set from a file. This can be a Gimp * palette, a RIFF palette, a Photoshop palette, * a Krita palette, * a Scribus palette or a SwatchBooker palette. */ explicit KoColorSet(const QString &filename = QString()); // Explicit copy constructor (KoResource copy constructor is private) KoColorSet(const KoColorSet& rhs); public /* overridden methods */: // KoResource ~KoColorSet() override; bool load() override; bool loadFromDevice(QIODevice *dev) override; bool save() override; bool saveToDevice(QIODevice* dev) const override; QString defaultFileExtension() const override; public /* methods */: void setColumnCount(int columns); int columnCount() const; void setComment(QString comment); QString comment(); int rowCount() const; quint32 colorCount() const; PaletteType paletteType() const; void setPaletteType(PaletteType paletteType); /** * @brief isGlobal * A global color set is a set stored in the config directory * Such a color set would be opened every time Krita is launched. * * A non-global color set, on contrary, would be stored in a kra file, * and would only be opened when that file is opened by Krita. * @return */ bool isGlobal() const; void setIsGlobal(bool); QByteArray toByteArray() const; bool fromByteArray(QByteArray &data); /** * @brief add Add a color to the palette. * @param groupName color to add the group to. If empty, it will be added to the unsorted. */ void add(const KisSwatch &, const QString &groupName = QString()); void setEntry(const KisSwatch &e, int x, int y, const QString &groupName = QString()); /** * @brief getColorGlobal * A function for getting a color based on a global index. Useful for itterating through all color entries. * @param globalIndex the global index over the whole palette. * @return the entry. */ KisSwatch getColorGlobal(quint32 x, quint32 y) const; /** * @brief getColorGroup * A function for getting the color from a specific group. * @param groupName the name of the group, will give unosrted when not defined. * @param index the index within the group. * @return the entry */ KisSwatch getColorGroup(quint32 x, quint32 y, QString groupName = QString()); /** * @brief getGroupNames * @return returns a list of group names, excluding the unsorted group. */ QStringList getGroupNames(); /** * @brief getGroup * @param name * @return the group with the name given; global group if no parameter is given * null pointer if not found. */ KisSwatchGroup *getGroup(const QString &name = QString()); bool changeGroupName(QString oldGroupName, QString newGroupName); /** * @brief addGroup * Adds a new group. * @param groupName the name of the new group. When not specified, this will fail. * @return whether thegroup was made. */ bool addGroup(const QString &groupName); /** * @brief moveGroup * Move a group in the internal stringlist. * @param groupName the groupname to move. * @param groupNameInsertBefore the groupname to insert before. Empty means it will be added to the end. * @return */ bool moveGroup(const QString &groupName, const QString &groupNameInsertBefore = QString()); /** * @brief removeGroup * Remove a group from the KoColorSet * @param groupName the name of the group you want to remove. * @param keepColors Whether you wish to keep the colorsetentries. These will be added to the unsorted. * @return whether it could find the group to remove. */ bool removeGroup(const QString &groupName, bool keepColors = true); void clear(); /** * @brief closestColorName * convenience function to get the name of the closest match. * @param color * @param useGivenColorSpace * @return */ QString closestColorName(KoColor color, bool useGivenColorSpace = true); - /* - * No one seems to be using these methods... - */ - // QString findGroupByGlobalIndex(quint32 x, quint32 y); - // QString findGroupByColorName(const QString &name, quint32 *x, quint32 *y); - // QString findGroupByID(const QString &id,quint32 *index); - // void removeAt(quint32 x, quint32 y, QString groupName = QString()); - /** - * @brief getGroupByName - * @param groupName - * @param if success - * @return group found - */ - // const KisSwatchGroup &getGroupByName(const QString &groupName, bool &success) const; - // bool changeColorSetEntry(KisSwatch entry, QString groupName); /** * @brief getIndexClosestColor * function that matches the color to all colors in the colorset, and returns the index * of the closest match. * @param color the color you wish to compare. * @param useGivenColorSpace whether to use the color space of the color given * when the two colors' colorspaces don't match. Else it'll use the entry's colorspace. * @return returns the int of the closest match. */ - // quint32 getIndexClosestColor(KoColor color, bool useGivenColorSpace = true); - /** - * @brief insertBefore insert color before index into group. - * @param index - * @param groupName name of the group that the color goes into. - * @return new index of index after the prepending. - */ - // quint32 insertBefore(const KisSwatch &, qint32 index, const QString &groupName = QString()); - + KisSwatchGroup::SwatchInfo getClosestColorInfo(KoColor compare, bool useGivenColorSpace = true); private: class Private; const QScopedPointer d; }; #endif // KOCOLORSET diff --git a/libs/widgets/KisPaletteComboBox.cpp b/libs/widgets/KisPaletteComboBox.cpp index 4e2cfd0445..08e0213ca6 100644 --- a/libs/widgets/KisPaletteComboBox.cpp +++ b/libs/widgets/KisPaletteComboBox.cpp @@ -1,93 +1,93 @@ // Qt #include #include #include // STL #include #include "KisPaletteComboBox.h" KisPaletteComboBox::KisPaletteComboBox(QWidget *parent) : QComboBox(parent) , m_completer(new QCompleter(QComboBox::model(), this)) , m_model(Q_NULLPTR) { m_completer->setCompletionMode(QCompleter::PopupCompletion); m_completer->setCaseSensitivity(Qt::CaseInsensitive); m_completer->setFilterMode(Qt::MatchContains); setCompleter(m_completer.data()); } KisPaletteComboBox::~KisPaletteComboBox() { } void KisPaletteComboBox::setPaletteModel(const KisPaletteModel *paletteModel) { disconnect(); m_model = paletteModel; if (m_model.isNull()) { return; } slotPaletteChanged(); connect(m_model, SIGNAL(modelReset()), SLOT(slotPaletteChanged())); connect(m_model, SIGNAL(dataChanged(const QModelIndex &, const QModelIndex&, const QVector &)), SLOT(slotPaletteChanged())); } void KisPaletteComboBox::slotPaletteChanged() { if (QPointer(m_model->colorSet()).isNull()) { return; } clear(); QVector infoList; for (const QString &groupName : m_model->colorSet()->getGroupNames()) { const KisSwatchGroup *group = m_model->colorSet()->getGroup(groupName); for (const SwatchInfoType &info : group->infoList()) { infoList.append(info); } } - std::sort(infoList.begin(), infoList.end(), swatchLess); + std::sort(infoList.begin(), infoList.end(), swatchInfoLess); for (int i = 0; i != infoList.size(); i++) { const SwatchInfoType &info = infoList[i]; const KisSwatch &swatch = info.swatch; QPixmap colorSquare = createColorSquare(swatch); QString name = swatch.name(); if (!swatch.id().isEmpty()){ name = swatch.id() + " - " + swatch.name(); } addItem(QIcon(colorSquare), name); m_posIdxMap[SwatchPosType(info.column, info.row)] = i; } } -bool KisPaletteComboBox::swatchLess(const SwatchInfoType &first, const SwatchInfoType &second) +bool KisPaletteComboBox::swatchInfoLess(const SwatchInfoType &first, const SwatchInfoType &second) { return first.swatch.name() < second.swatch.name(); } QPixmap KisPaletteComboBox::createColorSquare(const KisSwatch &swatch) const { QPixmap colorSquare(32, 32); if (swatch.spotColor()) { QImage img = QImage(32, 32, QImage::Format_ARGB32); QPainter circlePainter; img.fill(Qt::transparent); circlePainter.begin(&img); QBrush brush = QBrush(Qt::SolidPattern); brush.setColor(swatch.color().toQColor()); circlePainter.setBrush(brush); QPen pen = circlePainter.pen(); pen.setColor(Qt::transparent); pen.setWidth(0); circlePainter.setPen(pen); circlePainter.drawEllipse(0, 0, 32, 32); circlePainter.end(); colorSquare = QPixmap::fromImage(img); } else { colorSquare.fill(swatch.color().toQColor()); } return colorSquare; } void KisPaletteComboBox::slotSwatchSelected(const QModelIndex &index) { setCurrentIndex(m_posIdxMap[SwatchPosType(index.column(), index.row())]); } diff --git a/libs/widgets/KisPaletteComboBox.h b/libs/widgets/KisPaletteComboBox.h index 03688098eb..7a6fd6d76b 100644 --- a/libs/widgets/KisPaletteComboBox.h +++ b/libs/widgets/KisPaletteComboBox.h @@ -1,44 +1,44 @@ #ifndef KISPALETTECOLORNAMELIST_H #define KISPALETTECOLORNAMELIST_H #include "kritawidgets_export.h" #include #include #include #include #include #include #include #include class KRITAWIDGETS_EXPORT KisPaletteComboBox : public QComboBox { Q_OBJECT private /* typedef */: typedef KisSwatchGroup::SwatchInfo SwatchInfoType; typedef QPair SwatchPosType; // first is column #, second is row # public: explicit KisPaletteComboBox(QWidget *parent = Q_NULLPTR); ~KisPaletteComboBox(); public /* methods */: void setPaletteModel(const KisPaletteModel *); private Q_SLOTS: void slotPaletteChanged(); void slotSwatchSelected(const QModelIndex &index); private /* methods */: QPixmap createColorSquare(const KisSwatch &swatch) const; - static bool swatchLess(const SwatchInfoType &, const SwatchInfoType &); + static bool swatchInfoLess(const SwatchInfoType &, const SwatchInfoType &); private /* member variables */: QScopedPointer m_completer; QPointer m_model; QHash, int> m_posIdxMap; // map from pos in table to idx here }; #endif // KISPALETTECOLORNAMELIST_H diff --git a/libs/widgets/KisPaletteModel.cpp b/libs/widgets/KisPaletteModel.cpp index 9f5065ab17..dc3d262aac 100644 --- a/libs/widgets/KisPaletteModel.cpp +++ b/libs/widgets/KisPaletteModel.cpp @@ -1,534 +1,540 @@ /* * 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 infinity when it's group name row bool groupNameRow = index.row() == Q_INFINITY; 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_colorSet->getGroupNames().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); int yInGroup = row; KisSwatchGroup *group = Q_NULLPTR; for (const QString &currGroupName : m_colorSet->getGroupNames()) { if (yInGroup >= m_colorSet->getGroup(currGroupName)->rowCount()) { // minus 1 for group name line yInGroup = yInGroup - m_colorSet->getGroup(currGroupName)->rowCount() - 1; } else { group = m_colorSet->getGroup(currGroupName); break; } } Q_ASSERT(group); // signals that it's a group name row; // wanted to use -1, but indexes with negative row/column are not valid if (yInGroup == -1) { yInGroup = Q_INFINITY; column = Q_INFINITY; } return createIndex(yInGroup, column, group); } void KisPaletteModel::setColorSet(KoColorSet* colorSet) { beginResetModel(); m_colorSet = colorSet; endResetModel(); } KoColorSet* KisPaletteModel::colorSet() const { return m_colorSet; } QModelIndex KisPaletteModel::indexFromId(int i) const { return QModelIndex(); /* QModelIndex index = QModelIndex(); if (!colorSet() || colorSet()->colorCount() == 0) { return index; } if (i > (int)colorSet()->nColors()) { qWarning()<<"index is too big"<nColors(); index = this->index(0,0); } if (i < (int)colorSet()->nColorsGroup(0)) { index = QAbstractTableModel::index(i/columnCount(), i % columnCount()); if (!index.isValid()) { index = QAbstractTableModel::index(0, 0, QModelIndex()); } return index; } else { int rowstotal = 1 + m_colorSet->nColorsGroup() / columnCount(); if (m_colorSet->nColorsGroup() == 0) { rowstotal += 1; } int totalIndexes = colorSet()->nColorsGroup(); Q_FOREACH (QString groupName, m_colorSet->getGroupNames()){ if (i + 1 <= (int)(totalIndexes + colorSet()->nColorsGroup(groupName)) && i + 1 > (int)totalIndexes) { int col = (i - totalIndexes) % columnCount(); int row = rowstotal + 1 + ((i - totalIndexes) / columnCount()); index = this->index(row, col); return index; } else { rowstotal += 1 + m_colorSet->nColorsGroup(groupName) / columnCount(); totalIndexes += colorSet()->nColorsGroup(groupName); if (m_colorSet->nColorsGroup(groupName)%columnCount() > 0) { rowstotal += 1; } if (m_colorSet->nColorsGroup(groupName)==0) { rowstotal += 1; //always add one for the group when considering groups. } } } } return index; */ } int KisPaletteModel::idFromIndex(const QModelIndex &index) const { /* if (index.isValid()==false) { return -1; qWarning()<<"invalid index"; } int i=0; QStringList entryList = qvariant_cast(data(index, RetrieveEntryRole)); if (entryList.isEmpty()) { return -1; qWarning()<<"invalid index, there's no data to retrieve here"; } if (entryList.at(0)==QString()) { return entryList.at(1).toUInt(); } i = colorSet()->nColorsGroup(""); //find at which position the group is. int groupIndex = colorSet()->getGroupNames().indexOf(entryList.at(0)); //add all the groupsizes onto it till we get to our group. for(int g=0; gnColorsGroup(colorSet()->getGroupNames().at(g)); } //then add the index. i += entryList.at(1).toUInt(); return i; */ return 0; } KisSwatch KisPaletteModel::colorSetEntryFromIndex(const QModelIndex &index) const { return KisSwatch(); } bool KisPaletteModel::addEntry(KisSwatch entry, QString groupName) { KisSwatchGroup *group = m_colorSet->getGroup(groupName); if (group->checkEntry(group->columnCount(), group->rowCount())) { beginInsertRows(QModelIndex(), rowCount(), rowCount()); m_colorSet->add(entry, groupName); endInsertRows(); } else { m_colorSet->add(entry, groupName); } m_colorSet->save(); return true; } bool KisPaletteModel::removeEntry(QModelIndex index, bool keepColors) { if (qvariant_cast(data(index, IsGroupNameRole))==false) { static_cast(index.internalPointer())->removeEntry(index.column(), index.row()); } else { QString groupName = static_cast(index.internalPointer())->name(); beginRemoveRows(QModelIndex(), index.row(), index.row()); m_colorSet->removeGroup(groupName, keepColors); endRemoveRows(); } 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(), index.row()); emit dataChanged(index, index); } QVariant KisPaletteModel::dataForGroupNameRow(const QModelIndex &idx, int role) const { KisSwatchGroup *group = static_cast(idx.internalPointer()); Q_ASSERT(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(), idx.row()); KisSwatch entry; if (entryPresent) { entry = group->getEntry(idx.column(), 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)); +} diff --git a/libs/widgets/KisPaletteModel.h b/libs/widgets/KisPaletteModel.h index 45eb630ef6..959ec02218 100644 --- a/libs/widgets/KisPaletteModel.h +++ b/libs/widgets/KisPaletteModel.h @@ -1,169 +1,170 @@ /* * 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. */ #ifndef KIS_PALETTEMODEL_H #define KIS_PALETTEMODEL_H #include #include #include #include "kritawidgets_export.h" #include #include class KoColorSet; /** * @brief The KisPaletteModel class * This, together with KisPaletteView and KisPaletteDelegate forms a mvc way to access kocolorsets. * A display renderer is given to this model to convert KoColor to QColor when * colors are requested */ class KRITAWIDGETS_EXPORT KisPaletteModel : public QAbstractTableModel { Q_OBJECT public: KisPaletteModel(QObject* parent = 0); ~KisPaletteModel() override; enum AdditionalRoles { IsGroupNameRole = Qt::UserRole + 1, RetrieveEntryRole = Qt::UserRole + 3, CheckSlotRole = Qt::UserRole + 4 }; public /* overriden methods */: // QAbstractTableModel QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override; int rowCount(const QModelIndex& parent = QModelIndex()) const override; int columnCount(const QModelIndex& parent = QModelIndex()) const override; /** * @brief index * @param row * @param column * @param parent * @return the index of for the data at row, column * if the data is a color entry, the internal pointer points to the group * the entry belongs to, and the row and column are row number and column * number inside the group. * if the data is a group, the row number and group number is Q_INFINIFY, * and the internal pointer also points to the group */ QModelIndex index(int row, int column, const QModelIndex& parent = QModelIndex()) const override; Qt::ItemFlags flags(const QModelIndex& index) const override; /** * @brief dropMimeData * This is an overridden function that handles dropped mimedata. * right now only colorsetentries and colorsetgroups are handled. * @return */ bool dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) override; /** * @brief mimeData * gives the mimedata for a kocolorsetentry or a kocolorsetgroup. * @param indexes * @return the mimedata for the given indices */ QMimeData *mimeData(const QModelIndexList &indexes) const override; QStringList mimeTypes() const override; Qt::DropActions supportedDropActions() const override; /** * @brief setData * setData is not used as KoColor is not a QVariant * use setEntry, addEntry and removeEntry instead bool setData(const QModelIndex &index, const QVariant &value, int role) override; */ public /* methods */: /** * @brief addEntry * proper function to handle adding entries. * @return whether successful. */ bool addEntry(KisSwatch entry, QString groupName=QString()); void setEntry(const KisSwatch &entry, const QModelIndex &index); /** * @brief removeEntry * proper function to remove the colorsetentry at the given index. * The consolidtes both removeentry and removegroup. * @param keepColors: This bool determines whether, when deleting a group, * the colors should be added to the default group. This is usually desirable, * so hence the default is true. * @return if successful */ bool removeEntry(QModelIndex index, bool keepColors=true); void setColorSet(KoColorSet* colorSet); KoColorSet* colorSet() const; /** * Installs a display renderer object for a palette that will * convert the KoColor to the displayable QColor. Default is the * dumb renderer. */ void setDisplayRenderer(const KoColorDisplayRendererInterface *displayRenderer); /** * @brief indexFromId * convenience function to get the tableindex from the global palette color. * used by lazybrush. * @param i * @return index in table. */ QModelIndex indexFromId(int i) const; /** * @brief idFromIndex * convenience function to get the global colorset entry id from the table index. * If you just want to use this to get the kocolorsetentry, use colorsetEntryFromIndex instead. * @param index * @return */ int idFromIndex(const QModelIndex &index) const; /** * @brief colorSetEntryFromIndex * This gives the colorset entry for the given table model index. * @param index the QModelIndex * @return the kocolorsetentry */ KisSwatch colorSetEntryFromIndex(const QModelIndex &index) const; + QModelIndex indexForClosest(const KoColor &compare); public Q_SLOTS: private Q_SLOTS: void slotDisplayConfigurationChanged(); private /* methods */: QVariant dataForGroupNameRow(const QModelIndex &idx, int role) const; QVariant dataForSwatch(const QModelIndex &idx, int role) const; private /* member variables */: QPointer m_colorSet; QPointer m_displayRenderer; }; #endif diff --git a/libs/widgets/kis_palette_view.cpp b/libs/widgets/kis_palette_view.cpp index cd584153bd..bc20da7a16 100644 --- a/libs/widgets/kis_palette_view.cpp +++ b/libs/widgets/kis_palette_view.cpp @@ -1,325 +1,305 @@ /* * 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 // qFloor #include #include #include #include #include #include "KisPaletteDelegate.h" #include "KisPaletteModel.h" #include "kis_color_button.h" #include int KisPaletteView::MINROWHEIGHT = 10; struct KisPaletteView::Private { - Private() - { - entryClickedMenu.addAction("Add foreground color"); - entryClickedMenu.addAction("Choose a color to add"); - entryClickedMenu.addAction("Rename this spot"); - entryClickedMenu.addAction("Switch with another spot"); - entryClickedMenu.addAction("Delete color"); - } QPointer model; - QMenu entryClickedMenu; bool allowPaletteModification; - QAction actAdd; - QAction actAddWithDlg; - QAction actModify; - QAction actSwitch; - QAction actRemove; }; KisPaletteView::KisPaletteView(QWidget *parent) : QTableView(parent) , m_d(new Private) { m_d->allowPaletteModification = true; setItemDelegate(new KisPaletteDelegate(this)); setShowGrid(true); setDropIndicatorShown(true); setDragEnabled(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::ScrollBarAlwaysOff); // set the size of swatches horizontalHeader()->setVisible(false); verticalHeader()->setVisible(false); horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch); horizontalHeader()->setMinimumSectionSize(MINROWHEIGHT); horizontalHeader()->setMaximumSectionSize(MINROWHEIGHT); verticalHeader()->setSectionResizeMode(QHeaderView::Fixed); verticalHeader()->setMinimumSectionSize(MINROWHEIGHT); verticalHeader()->setDefaultSectionSize(MINROWHEIGHT); connect(horizontalHeader(), SIGNAL(sectionResized(int,int,int)), SLOT(slotResizeVerticalHeader(int,int,int))); - connect(this, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(slotModifyEntry(QModelIndex))); + connect(this, SIGNAL(doubleClicked(QModelIndex)), SLOT(slotModifyEntry(QModelIndex))); KConfigGroup cfg(KSharedConfig::openConfig()->group("")); QPalette pal(palette()); // pal.setColor(QPalette::Base, cfg.getMDIBackgroundColor()); setAutoFillBackground(true); setPalette(pal); } 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) { KoDialog *window = new KoDialog(this); window->setWindowTitle(i18nc("@title:window", "Add a new Colorset Entry")); QFormLayout *editableItems = new QFormLayout(window); window->mainWidget()->setLayout(editableItems); QComboBox *cmbGroups = new QComboBox(window); QString defaultGroupName = i18nc("Name for default group", "Default"); cmbGroups->addItem(defaultGroupName); cmbGroups->addItems(m_d->model->colorSet()->getGroupNames()); QLineEdit *lnIDName = new QLineEdit(window); QLineEdit *lnName = new QLineEdit(window); KisColorButton *bnColor = new KisColorButton(window); QCheckBox *chkSpot = new QCheckBox(window); 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; if (qvariant_cast(index.data(KisPaletteModel::IsGroupNameRole))) { KoDialog *window = new KoDialog(); window->setWindowTitle(i18nc("@title:window","Removing Group")); QFormLayout *editableItems = new QFormLayout(); QCheckBox *chkKeep = new QCheckBox(); 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(); } } else { m_d->model->removeEntry(index, keepColors); m_d->model->colorSet()->save(); } return true; } -void KisPaletteView::trySelectClosestColor(KoColor color) +void KisPaletteView::selectClosestColor(const KoColor &color) { - /* KoColorSet* color_set = m_d->model->colorSet(); - if (!color_set) + if (!color_set) { return; + } //also don't select if the color is the same as the current selection if (selectedIndexes().size() > 0) { QModelIndex currentI = currentIndex(); if (!currentI.isValid()) { currentI = selectedIndexes().last(); } if (!currentI.isValid()) { currentI = selectedIndexes().first(); } if (currentI.isValid()) { if (m_d->model->colorSetEntryFromIndex(currentI).color()==color) { return; } } } - quint32 i = color_set->getIndexClosestColor(color); - QModelIndex index = m_d->model->indexFromId(i); - this->selectionModel()->clearSelection(); - this->selectionModel()->setCurrentIndex(index, QItemSelectionModel::Select); - */ + + selectionModel()->clearSelection(); + QModelIndex index = m_d->model->indexForClosest(color); + selectionModel()->setCurrentIndex(index, QItemSelectionModel::Select); } void KisPaletteView::mouseReleaseEvent(QMouseEvent *event) { - if (event->button() == Qt::RightButton) { - m_d->entryClickedMenu.exec(QCursor::pos()); - return; - } - if (selectedIndexes().size() <= 0) { return; } QModelIndex index = currentIndex(); if (qvariant_cast(index.data(KisPaletteModel::IsGroupNameRole)) == false) { emit sigIndexSelected(index); } } void KisPaletteView::setPaletteModel(KisPaletteModel *model) { if (m_d->model) { disconnect(m_d->model, 0, this, 0); } m_d->model = model; setModel(model); } KisPaletteModel* KisPaletteView::paletteModel() const { return m_d->model; } void KisPaletteView::setAllowModification(bool allow) { m_d->allowPaletteModification = allow; } void KisPaletteView::wheelEvent(QWheelEvent *event) { QTableView::wheelEvent(event); } void KisPaletteView::modifyEntry(QModelIndex index) { if (m_d->allowPaletteModification) { KoDialog *dlg = new KoDialog(); QFormLayout *editableItems = new QFormLayout(dlg); dlg->mainWidget()->setLayout(editableItems); QLineEdit *lnIDName = new QLineEdit(dlg); QLineEdit *lnGroupName = new QLineEdit(dlg); KisColorButton *bnColor = new KisColorButton(dlg); QCheckBox *chkSpot = new QCheckBox(dlg); if (qvariant_cast(index.data(KisPaletteModel::IsGroupNameRole))) { 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) { m_d->model->colorSet()->changeGroupName(groupName, lnGroupName->text()); m_d->model->colorSet()->save(); } //rename the group. } else { 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(i18n("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->colorSet()->setEntry(entry, index.column(), index.row()); m_d->model->colorSet()->save(); } } delete dlg; } update(index); } void KisPaletteView::slotModifyEntry(const QModelIndex &index) { modifyEntry(index); } void KisPaletteView::slotResizeVerticalHeader(int, int, int newSize) { verticalHeader()->setDefaultSectionSize(newSize); verticalHeader()->setMaximumSectionSize(newSize); verticalHeader()->resizeSections(QHeaderView::Fixed); } void KisPaletteView::removeSelectedEntry() { if (selectedIndexes().size() <= 0) { return; } m_d->model->removeEntry(currentIndex()); } diff --git a/libs/widgets/kis_palette_view.h b/libs/widgets/kis_palette_view.h index a5801e021d..e7fc23700e 100644 --- a/libs/widgets/kis_palette_view.h +++ b/libs/widgets/kis_palette_view.h @@ -1,106 +1,111 @@ /* * Copyright (c) 2016 Dmitry Kazakov * Copyright (c) 2017 Wolthera van Hövell tot Westerflier * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef __KIS_PALETTE_VIEW_H #define __KIS_PALETTE_VIEW_H #include #include #include #include #include #include #include #include "kritawidgets_export.h" class KisPaletteModel; class QWheelEvent; class KRITAWIDGETS_EXPORT KisPaletteView : public QTableView { Q_OBJECT private: static int MINROWHEIGHT; public: KisPaletteView(QWidget *parent = 0); ~KisPaletteView() override; void setPaletteModel(KisPaletteModel *model); KisPaletteModel* paletteModel() const; public: /** * @brief setAllowModification * Set whether doubleclick calls up a modification window. This is to prevent users from editing * the palette when the palette is intended to be a list of items. */ void setAllowModification(bool allow); /** * @brief setCrossedKeyword * this apparently allows you to set keywords that can cross out colors. * This is implemented to mark the lazybrush "transparent" color. * @param value */ void setCrossedKeyword(const QString &value); /** * add an entry with a dialog window. */ bool addEntryWithDialog(KoColor color); /** * remove entry with a dialog window.(Necessary for groups. */ bool removeEntryWithDialog(QModelIndex index); void removeSelectedEntry(); /** * @brief modifyEntry * function for changing the entry at the given index. * if modification isn't allow(@see setAllowModification), this does nothing. */ void modifyEntry(QModelIndex index); + /** + * @brief selectClosestColor + * select a color that's closest to parameter color + * @param color + */ + void selectClosestColor(const KoColor &color); Q_SIGNALS: void sigIndexSelected(const QModelIndex &index); public Q_SLOTS: /** * This tries to select the closest color in the palette. * This doesn't update the foreground color, just the visual selection. */ - void trySelectClosestColor(KoColor color); void slotModifyEntry(const QModelIndex &); protected: void mouseReleaseEvent(QMouseEvent *event) override; void wheelEvent(QWheelEvent *event) override; private Q_SLOTS: void slotResizeVerticalHeader(int, int, int newSize); private: private: struct Private; const QScopedPointer m_d; }; #endif /* __KIS_PALETTE_VIEW_H */ diff --git a/plugins/dockers/palettedocker/palettedocker_dock.cpp b/plugins/dockers/palettedocker/palettedocker_dock.cpp index 58d5f04453..2e8ad19dab 100644 --- a/plugins/dockers/palettedocker/palettedocker_dock.cpp +++ b/plugins/dockers/palettedocker/palettedocker_dock.cpp @@ -1,288 +1,293 @@ /* * 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 "palettedocker_dock.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "KisPaletteModel.h" #include "KisPaletteDelegate.h" #include "kis_palette_view.h" #include #include "ui_wdgpalettedock.h" PaletteDockerDock::PaletteDockerDock( ) : QDockWidget(i18n("Palette")) , m_ui(new Ui_WdgPaletteDock()) , m_currentColorSet(0) , m_view(0) , m_resourceProvider(0) , m_canvas(0) , m_saver(new PaletteListSaver(this)) , m_actAdd(new QAction(KisIconUtils::loadIcon("list-add"), i18n("Add foreground color"))) , m_actAddWithDlg(new QAction(KisIconUtils::loadIcon("list-add"), i18n("Choose a color to add"))) , m_actSwitch(new QAction(i18n("Switch with another spot"))) , m_actRemove(new QAction(KisIconUtils::loadIcon("edit-delete"), i18n("Delete color"))) , m_actModify(new QAction(KisIconUtils::loadIcon("edit-rename"), i18n("Modify this spot"))) { QWidget* mainWidget = new QWidget(this); setWidget(mainWidget); m_ui->setupUi(mainWidget); m_ui->bnAdd->setDefaultAction(m_actAdd.data()); m_ui->bnRemove->setDefaultAction(m_actRemove.data()); m_ui->bnRename->setDefaultAction(m_actModify.data()); // to make sure their icons have the same size m_ui->bnRemove->setIconSize(QSize(16, 16)); m_ui->bnRename->setIconSize(QSize(16, 16)); m_ui->bnAdd->setIconSize(QSize(16, 16)); m_ui->bnAdd->setEnabled(false); m_ui->bnRemove->setEnabled(false); // m_wdgPaletteDock->bnAddGroup->setIcon(KisIconUtils::loadIcon("groupLayer")); m_model = new KisPaletteModel(this); m_ui->paletteView->setPaletteModel(m_model); m_ui->cmbNameList->setPaletteModel(m_model); connect(m_actAdd.data(), SIGNAL(triggered()), SLOT(slotAddColor())); connect(m_actRemove.data(), SIGNAL(triggered()), SLOT(slotRemoveColor())); connect(m_actModify.data(), SIGNAL(triggered()), SLOT(slotEditEntry())); connect(m_ui->paletteView, SIGNAL(sigIndexSelected(QModelIndex)), SLOT(slotPaletteIndexSelected(QModelIndex))); connect(m_ui->paletteView, SIGNAL(sigIndexSelected(QModelIndex)), m_ui->cmbNameList, SLOT(slotSwatchSelected(QModelIndex))); m_viewContextMenu.addAction(m_actAddWithDlg.data()); m_viewContextMenu.addAction(m_actRemove.data()); m_viewContextMenu.addAction(m_actModify.data()); m_viewContextMenu.addAction(m_actSwitch.data()); m_paletteChooser = new KisPaletteListWidget(this); connect(m_paletteChooser, SIGNAL(sigPaletteSelected(KoColorSet*)), SLOT(slotSetColorSet(KoColorSet*))); m_ui->bnColorSets->setIcon(KisIconUtils::loadIcon("hi16-palette_library")); m_ui->bnColorSets->setToolTip(i18n("Choose palette")); m_ui->bnColorSets->setPopupWidget(m_paletteChooser); connect(m_ui->cmbNameList, SIGNAL(currentIndexChanged(int)), SLOT(slotNameListSelection(int))); KisConfig cfg(true); QString defaultPaletteName = cfg.defaultPalette(); KoResourceServer* rServer = KoResourceServerProvider::instance()->paletteServer(); KoColorSet* defaultPalette = rServer->resourceByName(defaultPaletteName); if (defaultPalette) { slotSetColorSet(defaultPalette); } connect(m_paletteChooser, SIGNAL(sigPaletteListChanged()), m_saver.data(), SLOT(slotSetPaletteList())); } PaletteDockerDock::~PaletteDockerDock() { if (m_currentColorSet) { KisConfig cfg(true); cfg.setDefaultPalette(m_currentColorSet->name()); } delete m_ui; } void PaletteDockerDock::setViewManager(KisViewManager* kisview) { m_view = kisview; m_resourceProvider = kisview->resourceProvider(); connect(m_resourceProvider, SIGNAL(sigSavingWorkspace(KisWorkspaceResource*)), SLOT(saveToWorkspace(KisWorkspaceResource*))); connect(m_resourceProvider, SIGNAL(sigLoadingWorkspace(KisWorkspaceResource*)), SLOT(loadFromWorkspace(KisWorkspaceResource*))); - connect(m_resourceProvider, SIGNAL(sigFGColorChanged(KoColor)),m_ui->paletteView, SLOT(trySelectClosestColor(KoColor))); + connect(m_resourceProvider, SIGNAL(sigFGColorChanged(KoColor)), SLOT(slotFGColorChanged(KoColor))); kisview->nodeManager()->disconnect(m_model); } void PaletteDockerDock::setCanvas(KoCanvasBase *canvas) { setEnabled(canvas != 0); if (canvas) { KisCanvas2 *cv = qobject_cast(canvas); m_model->setDisplayRenderer(cv->displayColorConverter()->displayRendererInterface()); } m_canvas = static_cast(canvas); } void PaletteDockerDock::unsetCanvas() { setEnabled(false); m_model->setDisplayRenderer(0); m_canvas = 0; } void PaletteDockerDock::slotSetColorSet(KoColorSet* colorSet) { m_currentColorSet = colorSet; m_model->setColorSet(colorSet); m_ui->bnColorSets->setText(colorSet->name()); } void PaletteDockerDock::resetNameList(const KoColorSet *colorSet) { if (!colorSet || !(colorSet->colorCount()>0)) { return; } if (colorSet && colorSet->removable()) { m_ui->bnAdd->setEnabled(true); m_ui->bnRemove->setEnabled(true); } else { m_ui->bnAdd->setEnabled(false); m_ui->bnRemove->setEnabled(false); } } void PaletteDockerDock::slotNameListSelection(int index) { /* if (m_model && m_currentColorSet) { KisPaletteView *view = m_wdgPaletteDock->paletteView; view->blockSignals(true); m_wdgPaletteDock->cmbNameList->blockSignals(true); m_wdgPaletteDock->cmbNameList->setCurrentIndex(index); view->selectionModel()->clearSelection(); view->selectionModel()->setCurrentIndex(QModelIndex(m_indexList[index].first, m_indexList[index].second), QItemSelectionModel::Select); view->blockSignals(false); m_wdgPaletteDock->cmbNameList->blockSignals(false); } */ } void PaletteDockerDock::slotAddColor() { if (m_resourceProvider) { m_ui->paletteView->addEntryWithDialog(m_resourceProvider->fgColor()); } } void PaletteDockerDock::slotRemoveColor() { QModelIndex index = m_ui->paletteView->currentIndex(); if (!index.isValid()) { return; } m_ui->paletteView->removeEntryWithDialog(index); } void PaletteDockerDock::setForegroundColor(const KisSwatch &entry) { if (m_resourceProvider) { m_resourceProvider->setFGColor(entry.color()); } } void PaletteDockerDock::saveToWorkspace(KisWorkspaceResource* workspace) { if (m_currentColorSet) { workspace->setProperty("palette", m_currentColorSet->name()); } } void PaletteDockerDock::loadFromWorkspace(KisWorkspaceResource* workspace) { if (workspace->hasProperty("palette")) { KoResourceServer* rServer = KoResourceServerProvider::instance()->paletteServer(); KoColorSet* colorSet = rServer->resourceByName(workspace->getString("palette")); if (colorSet) { slotSetColorSet(colorSet); } } } void PaletteDockerDock::slotPaletteIndexSelected(const QModelIndex &index) { bool slotEmpty = !(qvariant_cast(index.data(KisPaletteModel::CheckSlotRole))); if (slotEmpty) { setEntryByForeground(index); } else { KisSwatchGroup *group = static_cast(index.internalPointer()); Q_ASSERT(group); KisSwatch entry = group->getEntry(index.column(), index.row()); setForegroundColor(entry); } } void PaletteDockerDock::setEntryByForeground(const QModelIndex &index) { m_model->setEntry(KisSwatch(m_resourceProvider->fgColor()), index); if (m_currentColorSet->removable()) { m_ui->bnRemove->setEnabled(true); } } void PaletteDockerDock::slotEditEntry() { QModelIndex index = m_ui->paletteView->currentIndex(); if (!index.isValid()) { return; } m_ui->paletteView->modifyEntry(index); } void PaletteDockerDock::slotImportPalette() { KoFileDialog dialog(this, KoFileDialog::OpenFile, "OpenColorSet"); dialog.setDefaultDir(m_currentColorSet->filename()); dialog.setMimeTypeFilters(QStringList() << "application/x-gimp-color-palette"); QString fileName = dialog.filename(); KoColorSet *colorSet = new KoColorSet(fileName); colorSet->load(); } + +void PaletteDockerDock::slotFGColorChanged(const KoColor &color) +{ + m_ui->paletteView->selectClosestColor(color); +} diff --git a/plugins/dockers/palettedocker/palettedocker_dock.h b/plugins/dockers/palettedocker/palettedocker_dock.h index 56ec7266f6..869c159620 100644 --- a/plugins/dockers/palettedocker/palettedocker_dock.h +++ b/plugins/dockers/palettedocker/palettedocker_dock.h @@ -1,116 +1,117 @@ /* * 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. */ #ifndef PALETTEDOCKER_DOCK_H #define PALETTEDOCKER_DOCK_H #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "PaletteListSaver.h" class KisViewManager; class KisCanvasResourceProvider; class KisWorkspaceResource; class KisPaletteListWidget; class KisPaletteModel; class Ui_WdgPaletteDock; class PaletteDockerDock : public QDockWidget, public KisMainwindowObserver { Q_OBJECT public: PaletteDockerDock(); ~PaletteDockerDock() override; public: // QDockWidget void setCanvas(KoCanvasBase *canvas) override; void unsetCanvas() override; public: // KisMainWindowObserver void setViewManager(KisViewManager* kisview) override; private Q_SLOTS: void slotImportPalette(); void slotAddColor(); void slotRemoveColor(); void slotEditEntry(); void slotPaletteIndexSelected(const QModelIndex &index); void setEntryByForeground(const QModelIndex &index); void setForegroundColor(const KisSwatch &entry); void slotSetColorSet(KoColorSet* colorSet); + void slotFGColorChanged(const KoColor &color); void slotNameListSelection(int index); void saveToWorkspace(KisWorkspaceResource* workspace); void loadFromWorkspace(KisWorkspaceResource* workspace); private /* methods */: void resetNameList(const KoColorSet *colorSet); private /* friends */: /** * @brief PaletteListSaver * saves non-global palette list to KisDocument. * Actually, this should be implemented in * KisPaletteListWidget, but that class is in the * library kritawidgets, while KisDocument is in * kritaui, which depends on kritawidgets... * * Hope one day kritaui can finally be cleaned up... */ friend class PaletteListSaver; private /* member variables */: Ui_WdgPaletteDock* m_ui; KisPaletteModel *m_model; KoColorSet *m_currentColorSet; KisPaletteListWidget *m_paletteChooser; KisViewManager *m_view; KisCanvasResourceProvider *m_resourceProvider; QPointer m_canvas; QScopedPointer m_saver; QScopedPointer m_actAdd; QScopedPointer m_actAddWithDlg; QScopedPointer m_actSwitch; QScopedPointer m_actRemove; QScopedPointer m_actModify; QMenu m_viewContextMenu; }; #endif