diff --git a/libs/pigment/resources/KisSwatch.cpp b/libs/pigment/resources/KisSwatch.cpp index 4c3994ad57..460cc791fb 100644 --- a/libs/pigment/resources/KisSwatch.cpp +++ b/libs/pigment/resources/KisSwatch.cpp @@ -1,33 +1,22 @@ /* 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 "KisSwatch.h" - -KisSwatch &KisSwatch::operator =(const KisSwatch &source) -{ - if (&source == this) - return *this; - m_color = source.m_color; - m_id = source.m_id; - m_name = source.m_name; - m_spotColor = source.m_spotColor; - return *this; -} diff --git a/libs/pigment/resources/KisSwatch.h b/libs/pigment/resources/KisSwatch.h index fc4f1a1530..35d1c587ae 100644 --- a/libs/pigment/resources/KisSwatch.h +++ b/libs/pigment/resources/KisSwatch.h @@ -1,41 +1,39 @@ /* 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 KISSWATCH_H #define KISSWATCH_H #include #include class KRITAPIGMENT_EXPORT KisSwatch : public KoColorSetEntry { public: KisSwatch() : KoColorSetEntry() { } KisSwatch(const KoColor &color, const QString &name) : KoColorSetEntry(color, name) { } virtual ~KisSwatch() { } -public: - KisSwatch &operator =(const KisSwatch &source); }; #endif // KISSWATCH_H diff --git a/libs/pigment/resources/KisSwatchGroup.cpp b/libs/pigment/resources/KisSwatchGroup.cpp index 3a17bbea51..209e1919ac 100644 --- a/libs/pigment/resources/KisSwatchGroup.cpp +++ b/libs/pigment/resources/KisSwatchGroup.cpp @@ -1,124 +1,130 @@ /* 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_N_COLUMN = 16; KisSwatchGroup::KisSwatchGroup() : m_colorMatrix(DEFAULT_N_COLUMN) , m_nColors(0) - , m_nLastRowEntries(0) { } void KisSwatchGroup::setEntry(const KisSwatch &e, int x, int y) { Q_ASSERT(x < m_colorMatrix.size() && x >= 0 && y >= 0); if (!checkEntry(x, y)) { m_nColors++; } m_colorMatrix[x][y] = e; } bool KisSwatchGroup::checkEntry(int x, int y) const { if (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_nColors == 0) { return false; } if (x >= m_colorMatrix.size() || x < 0) { return false; } // QMap::remove returns 1 if key found else 0 if (m_colorMatrix[x].remove(y)) { m_nColors -= 1; return true; } else { return false; } } void KisSwatchGroup::setNColumns(int nColumns) { Q_ASSERT(nColumns >= 0); if (nColumns < m_colorMatrix.size()) { for (int i = m_colorMatrix.size() - 1; i <= nColumns; i-- ) { m_nColors -= m_colorMatrix[i].size(); } } m_colorMatrix.resize(nColumns); } KisSwatch KisSwatchGroup::getEntry(int x, int y) const { Q_ASSERT(checkEntry(x, y)); return m_colorMatrix[x][y]; } int KisSwatchGroup::nRows() const { /* * shouldn't have too great a performance impact... * add a heap to keep track of last row if there is one */ int res = 0; Q_FOREACH (const Column &c, m_colorMatrix) { if (c.empty()) { continue; } res = res > c.lastKey() ? res : c.lastKey(); } return res + 1; } void KisSwatchGroup::addEntry(const KisSwatch &e) { if (nColumns() == 0) { setNColumns(DEFAULT_N_COLUMN); } + + if (m_nColors == 0) { + setEntry(e, 0, 0); + return; + } + int maxY = nRows() - 1; int y = maxY; for (int x = m_colorMatrix.size() - 1; x >= 0; x--) { if (checkEntry(x, maxY)) { // if the last entry's at the rightmost column, // add e to the leftmost column of the next row if (++x == m_colorMatrix.size()) { x = 0; y++; } // else just add it to the right setEntry(e, x, y); + break; } } } diff --git a/libs/pigment/resources/KisSwatchGroup.h b/libs/pigment/resources/KisSwatchGroup.h index 9454507766..2daf8953c1 100644 --- a/libs/pigment/resources/KisSwatchGroup.h +++ b/libs/pigment/resources/KisSwatchGroup.h @@ -1,115 +1,114 @@ /* 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 "kritapigment_export.h" #include #include // Used to keep track of the last row. Qt doesn't provide a priority queue... #include "KisSwatch.h" /** * @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 quint32 DEFAULT_N_COLUMN; private: typedef QMap Column; public: KisSwatchGroup(); void setName(const QString &name) { m_name = name; } QString name() const { return m_name; } void setNColumns(int nColumns); int nColumns() const { return m_colorMatrix.size(); } int nRows() const; int nColors() const { return m_nColors; } /** * @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: QString m_name; QVector m_colorMatrix; int m_nColors; - int m_nLastRowEntries; }; #endif // KISSWATCHGROUP_H diff --git a/libs/pigment/resources/KoColorSet.cpp b/libs/pigment/resources/KoColorSet.cpp index e33ba422bf..e15a65fa37 100644 --- a/libs/pigment/resources/KoColorSet.cpp +++ b/libs/pigment/resources/KoColorSet.cpp @@ -1,480 +1,486 @@ /* 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 "KoColor.h" #include "KoColorSet.h" #include "KisSwatch.h" #include "KoColorSet_p.h" KoColorSet::KoColorSet(const QString& filename) : KoResource(filename) , d(new Private(this)) { } /// Create an copied palette KoColorSet::KoColorSet(const KoColorSet& rhs) : QObject(0) , KoResource(QString()) , d(new Private(this)) { setFilename(rhs.filename()); d->comment = rhs.d->comment; d->groupNames = rhs.d->groupNames; d->groups = rhs.d->groups; setValid(true); } 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() { QFile file(filename()); if (!file.open(QIODevice::WriteOnly | QIODevice::Truncate)) { return false; } saveToDevice(&file); file.close(); return true; } 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; } quint32 KoColorSet::nColors() { return d->groups[Private::GLOBAL_GROUP_NAME].nColors(); } quint32 KoColorSet::nColorsGroup(QString groupName) { if (d->groups.contains(groupName)) { return d->groups[groupName].nColors(); } else if (groupName.isEmpty()) { return d->groups[Private::GLOBAL_GROUP_NAME].nColors(); } else { return 0; } } QString KoColorSet::closestColorName(const KoColor color, bool useGivenColorSpace) { - int closestX = 0, closestY = 0; quint8 highestPercentage = 0; quint8 testPercentage = 0; KoColor compare = color; QString res; for (int x = 0; x < d->groups[Private::GLOBAL_GROUP_NAME].nColumns(); 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(KisSwatch c, const QString &groupName) { KisSwatchGroup &modifiedGroup = d->groups.contains(groupName) - ? d->groups[groupName] : d->groups[QString()]; + ? d->groups[groupName] : d->global(); modifiedGroup.addEntry(c); } void KoColorSet::clear() { d->groups.clear(); d->groupNames.clear(); } KisSwatch KoColorSet::getColorGlobal(quint32 x, quint32 y) { KisSwatch e; int yInGroup = y; QString nameGroupFoundIn; for (const QString &groupName : d->groupNames) { if (yInGroup < d->groups[groupName].nRows()) { nameGroupFoundIn = groupName; break; } else { yInGroup -= d->groups[groupName].nRows(); } } - const KisSwatchGroup &groupFoundIn = d->groups[nameGroupFoundIn == QString() - ? Private::GLOBAL_GROUP_NAME : nameGroupFoundIn]; + 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; - bool entryFound; - const KisSwatchGroup &sourceGroup = d->groups.contains(groupName) - ? d->groups[groupName] : d->groups[QString()]; - // warnPigment << "Color group "<< groupName <<" not found"; - entryFound = sourceGroup.checkEntry(x, y); - if (entryFound) { + const KisSwatchGroup &sourceGroup = groupName == QString() + ? d->global() : d->groups[groupName]; + if (sourceGroup.checkEntry(x, y)) { e = sourceGroup.getEntry(x, y); - } else { - warnPigment << x << " " << y <<"doesn't exist in" << nColorsGroup(groupName); } return e; } QStringList KoColorSet::getGroupNames() { if (d->groupNames.size() != d->groups.size()) { warnPigment << "mismatch between groups and the groupnames list."; + warnPigment << "<" << d->groupNames.size() << " " << d->groups.size() << ">"; 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].setNColumns(columns); for (KisSwatchGroup &g : d->groups.values()) { // Q_FOREACH doesn't accept non const refs g.setNColumns(columns); } } int KoColorSet::columnCount() { return d->groups[Private::GLOBAL_GROUP_NAME].nColumns(); } 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].nRows(); for (int x = 0; x < d->groups[groupName].nColumns(); x++) { for (int y = 0; y < d->groups[groupName].nRows(); 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() { - int res = d->groups[Private::GLOBAL_GROUP_NAME].nRows(); - Q_FOREACH (QString name, d->groupNames) { + int res = 0; + for (const QString &name : d->groupNames) { res += d->groups[name].nRows(); } 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]); +} + /* QString KoColorSet::findGroupByGlobalIndex(quint32 x, quint32 y) { *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; } 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 < nColors(); 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; } 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); } } 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; } */ diff --git a/libs/pigment/resources/KoColorSet.h b/libs/pigment/resources/KoColorSet.h index 1a1fabffde..9d60250a2f 100644 --- a/libs/pigment/resources/KoColorSet.h +++ b/libs/pigment/resources/KoColorSet.h @@ -1,206 +1,214 @@ /* 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" /** * 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); ~KoColorSet() override; bool load() override; bool loadFromDevice(QIODevice *dev) override; bool save() override; bool saveToDevice(QIODevice* dev) const override; QString defaultFileExtension() const override; void setColumnCount(int columns); int columnCount(); int rowCount(); /** * @brief comment * @return the comment. */ QString comment(); void setComment(QString comment); public: /** * @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(KisSwatch, 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); /** * @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 nColorsGroup * @param groupName string name of the group, when not specified, returns unsorted colors. * @return the amount of colors in this group. */ quint32 nColorsGroup(QString groupName = QString()); /** * @brief nColors * @return total colors in palette. */ quint32 nColors(); /** * @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()); private: class Private; const QScopedPointer d; }; #endif // KOCOLORSET diff --git a/libs/pigment/resources/KoColorSetEntry.cpp b/libs/pigment/resources/KoColorSetEntry.cpp index 675efa1e1f..f062d660ca 100644 --- a/libs/pigment/resources/KoColorSetEntry.cpp +++ b/libs/pigment/resources/KoColorSetEntry.cpp @@ -1,34 +1,72 @@ /* * 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; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "KoColorSetEntry.h" int KoColorSetEntry::NULLPOSITON = -1; KoColorSetEntry::KoColorSetEntry() : m_spotColor(false) + , m_valid(false) { } KoColorSetEntry::KoColorSetEntry(const KoColor &color, const QString &name) : m_color(color) , m_name(name) , m_spotColor(false) + , m_valid(true) { } + +void KoColorSetEntry::setName(const QString &name) +{ + m_name = name; + m_valid = true; +} + +void KoColorSetEntry::setId(const QString &id) +{ + m_id = id; + m_valid = true; +} + +void KoColorSetEntry::setColor(const KoColor &color) +{ + m_color = color; + m_valid = true; +} + +void KoColorSetEntry::setSpotColor(bool spotColor) +{ + m_spotColor = spotColor; + m_valid = true; +} + +KoColorSetEntry &KoColorSetEntry::operator =(const KoColorSetEntry &source) +{ + if (&source == this) + return *this; + m_color = source.m_color; + m_id = source.m_id; + m_name = source.m_name; + m_spotColor = source.m_spotColor; + m_valid = source.m_valid; + return *this; +} diff --git a/libs/pigment/resources/KoColorSetEntry.h b/libs/pigment/resources/KoColorSetEntry.h index a33de9bc74..612beb1b5e 100644 --- a/libs/pigment/resources/KoColorSetEntry.h +++ b/libs/pigment/resources/KoColorSetEntry.h @@ -1,62 +1,67 @@ /* * 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; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #ifndef KOCOLORSETENTRY_H #define KOCOLORSETENTRY_H #include "kritapigment_export.h" #include #include "KoColor.h" class KRITAPIGMENT_EXPORT KoColorSetEntry { public: static int NULLPOSITON; public: KoColorSetEntry(); KoColorSetEntry(const KoColor &color, const QString &name); public: QString name() const { return m_name; } - void setName(const QString &name) { m_name = name; } + void setName(const QString &name); QString id() const { return m_id; } - void setId(const QString &id) { m_id = id; } + void setId(const QString &id); KoColor color() const { return m_color; } - void setColor(const KoColor &color) { m_color = color; } + void setColor(const KoColor &color); bool spotColor() const { return m_spotColor; } - void setSpotColor(bool spotColor) { m_spotColor = spotColor; } + void setSpotColor(bool spotColor); + + bool isValid() const { return m_valid; } public: bool operator==(const KoColorSetEntry& rhs) const { return m_color == rhs.m_color && m_name == rhs.m_name; } -protected: + KoColorSetEntry &operator =(const KoColorSetEntry &source); + +private: KoColor m_color; QString m_name; QString m_id; bool m_spotColor; + bool m_valid; }; #endif // KOCOLORSETENTRY_H diff --git a/libs/pigment/resources/KoColorSet_p.cpp b/libs/pigment/resources/KoColorSet_p.cpp index cd9866192e..9e23d328e4 100644 --- a/libs/pigment/resources/KoColorSet_p.cpp +++ b/libs/pigment/resources/KoColorSet_p.cpp @@ -1,1136 +1,1144 @@ #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 "KoColorSet_p.h" QString KoColorSet::Private::GLOBAL_GROUP_NAME = QString(); KoColorSet::PaletteType 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 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 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 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) { groups[GLOBAL_GROUP_NAME] = KisSwatchGroup(); + groupNames.append(GLOBAL_GROUP_NAME); } bool KoColorSet::Private::init() { - // global.clear(); // just in case this is a reload (eg by KoEditColorSetDialog), - groups.clear(); + // 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(groups[GLOBAL_GROUP_NAME].nColumns() * 4, groups[GLOBAL_GROUP_NAME].nRows() * 4, QImage::Format_ARGB32); + QImage img(global().nColumns() * 4, global().nRows() * 4, QImage::Format_ARGB32); QPainter gc(&img); gc.fillRect(img.rect(), Qt::darkGray); - for (int x = 0; x < groups[GLOBAL_GROUP_NAME].nColumns(); x++) { - for (int y = 0; y < groups[GLOBAL_GROUP_NAME].nRows(); y++) { - QColor c = groups[GLOBAL_GROUP_NAME].getEntry(x, y).color().toQColor(); + for (int x = 0; x < global().nColumns(); x++) { + for (int y = 0; y < global().nRows(); y++) { + QColor c = global().getEntry(x, y).color().toQColor(); gc.fillRect(x * 4, y * 4, 4, 4, c); } } colorSet->setImage(img); // save some memory data.clear(); return res; } bool KoColorSet::Private::saveGpl(QIODevice *dev) const { QTextStream stream(dev); int columns = 0; stream << "GIMP Palette\nName: " << colorSet->name() << "\nColumns: " << columns << "\n#\n"; - for (int y = 0; y < groups[GLOBAL_GROUP_NAME].nRows(); y++) { + for (int y = 0; y < global().nRows(); y++) { for (int x = 0; x < columns; x++) { - if (!groups[GLOBAL_GROUP_NAME].checkEntry(x, y)) { + if (!global().checkEntry(x, y)) { continue; } - const KisSwatch& entry = groups[GLOBAL_GROUP_NAME].getEntry(x, y); + const KisSwatch& entry = global().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(); - groups[GLOBAL_GROUP_NAME].setNColumns(columns); + global().setNColumns(columns); index = 3; } - int x = 0, y = 0; 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); - groups[GLOBAL_GROUP_NAME].addEntry(e); + global().addEntry(e); + if (colorSet->name() == "Markers") + warnPigment << "loadGpl - i:" << i; } } + warnPigment << "loadGpl - set name:" << colorSet->name(); + warnPigment << "loadGpl - color count:" << global().nColors(); + warnPigment << "loadGpl - row count:" << global().nRows(); + warnPigment << "loadGpl - column count:" << global().nColumns(); 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())); - groups[GLOBAL_GROUP_NAME].addEntry(e); + 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"); QDomElement c = e.firstChildElement("ColorSetEntry"); while (!c.isNull()) { QString colorDepthId = c.attribute("bitdepth", Integer8BitsColorDepthID.id()); KisSwatch entry; entry.setColor(KoColor::fromXML(c.firstChildElement(), colorDepthId)); entry.setName(c.attribute("name")); entry.setId(c.attribute("id")); entry.setSpotColor(c.attribute("spot", "false") == "true" ? true : false); groups[GLOBAL_GROUP_NAME].addEntry(entry); c = c.nextSiblingElement("ColorSetEntry"); } QDomElement g = e.firstChildElement("Group"); while (!g.isNull()) { QString groupName = g.attribute("name"); colorSet->addGroup(groupName); QDomElement cg = g.firstChildElement("ColorSetEntry"); while (!cg.isNull()) { QString colorDepthId = cg.attribute("bitdepth", Integer8BitsColorDepthID.id()); KisSwatch entry; entry.setColor(KoColor::fromXML(cg.firstChildElement(), colorDepthId)); entry.setName(cg.attribute("name")); entry.setId(cg.attribute("id")); entry.setSpotColor(cg.attribute("spot", "false") == "true" ? true : false); groups[groupName].addEntry(entry); cg = cg.nextSiblingElement("ColorSetEntry"); } 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 profiles; QMap profileMap; { 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].nColumns()); for (int x = 0; x < groups[GLOBAL_GROUP_NAME].nColumns(); x++) { for (int y = 0; y < groups[GLOBAL_GROUP_NAME].nRows(); y++) { // Only save non-builtin profiles.= /* const KoColorProfile *profile = entry.color().colorSpace()->profile(); if (!profile->fileName().isEmpty()) { profiles << profile; profileMap[profile] = entry.color().colorSpace(); } QDomElement el = doc.createElement("ColorSetEntry"); el.setAttribute("name", entry.name()); el.setAttribute("id", entry.id()); el.setAttribute("spot", entry.spotColor() ? "true" : "false"); el.setAttribute("bitdepth", entry.color().colorSpace()->colorDepthId().id()); entry.color().toXML(doc, el); root.appendChild(el); */ } } Q_FOREACH(const QString &groupName, groupNames) { QDomElement gl = doc.createElement("Group"); gl.setAttribute("name", groupName); root.appendChild(gl); for (int x = 0; x < groups[groupName].nColumns(); x++) { for (int y = 0; y < groups[groupName].nRows(); y++) { // Only save non-builtin profiles.= /* const KoColorProfile *profile = entry.color().colorSpace()->profile(); if (!profile->fileName().isEmpty()) { profiles << profile; profileMap[profile] = entry.color().colorSpace(); } QDomElement el = doc.createElement("ColorSetEntry"); el.setAttribute("name", entry.name()); el.setAttribute("id", entry.id()); el.setAttribute("spot", entry.spotColor() ? "true" : "false"); el.setAttribute("bitdepth", entry.color().colorSpace()->colorDepthId().id()); entry.color().toXML(doc, el); gl.appendChild(el); */ } } } 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"); Q_FOREACH(const KoColorProfile *profile, profiles) { QString fn = QFileInfo(profile->fileName()).fileName(); if (!store->open(fn)) { return false; } QByteArray profileRawData = 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", profile->name()); el.setAttribute("colorModelId", profileMap[profile]->colorModelId().id()); el.setAttribute("colorDepthId", profileMap[profile]->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(); } diff --git a/libs/pigment/resources/KoColorSet_p.h b/libs/pigment/resources/KoColorSet_p.h index 22b60a5751..f3464789ba 100644 --- a/libs/pigment/resources/KoColorSet_p.h +++ b/libs/pigment/resources/KoColorSet_p.h @@ -1,60 +1,70 @@ #ifndef KOCOLORSET_P_H #define KOCOLORSET_P_H #include #include #include #include #include #include "KoColorSet.h" struct RiffHeader { quint32 riff; quint32 size; quint32 signature; quint32 data; quint32 datasize; quint16 version; quint16 colorcount; }; KoColorSet::PaletteType detectFormat(const QString &fileName, const QByteArray &ba); void scribusParseColor(KoColorSet *set, QXmlStreamReader *xml); bool loadScribusXmlPalette(KoColorSet *set, QXmlStreamReader *xml); class KoColorSet::Private { public: Private(KoColorSet *a_colorSet); +public: + KisSwatchGroup &global() { + Q_ASSERT(groups.contains(GLOBAL_GROUP_NAME)); + return groups[GLOBAL_GROUP_NAME]; + } + const KisSwatchGroup &global() const { + Q_ASSERT(groups.contains(GLOBAL_GROUP_NAME)); + return groups[GLOBAL_GROUP_NAME]; + } + public: bool init(); bool saveGpl(QIODevice *dev) const; bool loadGpl(); bool loadAct(); bool loadRiff(); bool loadPsp(); bool loadAco(); bool loadXml(); bool loadSbz(); bool saveKpl(QIODevice *dev) const; bool loadKpl(); public: KoColorSet *colorSet; KoColorSet::PaletteType paletteType; QByteArray data; QString comment; QStringList groupNames; //names of the groups, this is used to determine the order they are in. QHash groups; //grouped colors. public: static QString GLOBAL_GROUP_NAME; }; #endif // KOCOLORSET_P_H diff --git a/libs/pigment/tests/TestKisSwatchGroup.cpp b/libs/pigment/tests/TestKisSwatchGroup.cpp index 9bb6ff3688..87b788117c 100644 --- a/libs/pigment/tests/TestKisSwatchGroup.cpp +++ b/libs/pigment/tests/TestKisSwatchGroup.cpp @@ -1,83 +1,99 @@ #include "TestKisSwatchGroup.h" #include void TestKisSwatchGroup::testAddingOneEntry() { KisSwatch e; e.setName("first"); g.setEntry(e, 0, 0); QVERIFY(g.checkEntry(0, 0)); QCOMPARE(g.nRows(), 1); QVERIFY(!g.checkEntry(1, 2)); QVERIFY(!g.checkEntry(10, 5)); QCOMPARE(g.getEntry(0, 0), e); QCOMPARE(g.nColors(), 1); testSwatches[QPair(0, 0)] = e; } void TestKisSwatchGroup::testAddingMultipleEntries() { KisSwatch e2; e2.setName("second"); g.setEntry(e2, 9, 3); QCOMPARE(g.nColumns(), 16); QVERIFY(g.checkEntry(9, 3)); QVERIFY(!g.checkEntry(1, 2)); QVERIFY(!g.checkEntry(10, 5)); QCOMPARE(g.nRows(), 4); QVERIFY(g.checkEntry(0, 0)); QCOMPARE(g.getEntry(0, 0).name(), "first"); KisSwatch e3; e3.setName("third"); g.setEntry(e3, 4, 12); QCOMPARE(g.nColors(), 3); QCOMPARE(g.nRows(), 13); QVERIFY(g.checkEntry(9, 3)); QCOMPARE(g.getEntry(9, 3).name(), "second"); testSwatches[QPair(9, 3)] = e2; testSwatches[QPair(4, 12)] = e3; } void TestKisSwatchGroup::testReplaceEntries() { KisSwatch e4; e4.setName("fourth"); g.setEntry(e4, 0, 0); QCOMPARE(g.nColors(), 3); QCOMPARE(g.nRows(), 13); QVERIFY(g.checkEntry(0, 0)); QCOMPARE(g.getEntry(0, 0).name(), "fourth"); testSwatches[QPair(0, 0)] = e4; } void TestKisSwatchGroup::testRemoveEntries() { testSwatches.remove(QPair(9, 3)); QVERIFY(g.removeEntry(9, 3)); QCOMPARE(g.nColors(), testSwatches.size()); QVERIFY(!g.removeEntry(13, 10)); QVERIFY(!g.checkEntry(9, 3)); } void TestKisSwatchGroup::testChangeColumnNumber() { g.setNColumns(20); QCOMPARE(g.nColumns(), 20); for (QPair p : testSwatches.keys()) { QCOMPARE(testSwatches[p], g.getEntry(p.first, p.second)); } g.setNColumns(10); int keptCount = 0; int newNRows = 0; for (QPair p : testSwatches.keys()) { if (p.first < 10) { keptCount++; newNRows = newNRows < (p.second + 1) ? (p.second + 1) : newNRows; QCOMPARE(testSwatches[p], g.getEntry(p.first, p.second)); } } QCOMPARE(keptCount, g.nColors()); QCOMPARE(newNRows, g.nRows()); } +void TestKisSwatchGroup::testAddEntry() +{ + KisSwatchGroup g2; + g2.setNColumns(3); + for (int i = 0; i != 3; i++) { + g2.addEntry(KisSwatch()); + } + QCOMPARE(g2.nRows(), 1); + QCOMPARE(g2.nColumns(), 3); + QCOMPARE(g2.nColors(), 3); + g2.addEntry(KisSwatch()); + QCOMPARE(g2.nRows(), 2); + QCOMPARE(g2.nColumns(), 3); + QCOMPARE(g2.nColors(), 4); +} + QTEST_GUILESS_MAIN(TestKisSwatchGroup) diff --git a/libs/pigment/tests/TestKisSwatchGroup.h b/libs/pigment/tests/TestKisSwatchGroup.h index d213f45c2e..ef0f3dd24f 100644 --- a/libs/pigment/tests/TestKisSwatchGroup.h +++ b/libs/pigment/tests/TestKisSwatchGroup.h @@ -1,24 +1,25 @@ #ifndef TESTKISSWATCHGROUP_H #define TESTKISSWATCHGROUP_H #include #include #include class TestKisSwatchGroup : public QObject { Q_OBJECT private Q_SLOTS: void testAddingOneEntry(); void testAddingMultipleEntries(); void testReplaceEntries(); void testRemoveEntries(); void testChangeColumnNumber(); + void testAddEntry(); private: KisSwatchGroup g; QHash, KisSwatch> testSwatches; }; #endif /* TESTKISSWATCHGROUP_H */ diff --git a/libs/widgets/KisPaletteModel.cpp b/libs/widgets/KisPaletteModel.cpp index c156029e18..bf1ebf6853 100644 --- a/libs/widgets/KisPaletteModel.cpp +++ b/libs/widgets/KisPaletteModel.cpp @@ -1,632 +1,550 @@ /* * 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() { } 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(); } QVariant KisPaletteModel::data(const QModelIndex& index, int role) const { - KoColorSetEntry entry; - if (m_colorSet && m_displayRenderer) { - //now to figure out whether we have a groupname row or not. - bool groupNameRow = false; - quint32 indexInGroup = 0; - QString indexGroupName = QString(); - - /* - int rowstotal = m_colorSet->nColorsGroup()/columnCount(); - if (index.row()<=rowstotal && (quint32)(index.row()*columnCount()+index.column())nColorsGroup()) { - indexInGroup = (quint32)(index.row()*columnCount()+index.column()); + Q_ASSERT(index.row() >= 0 && index.row() < rowCount()); + Q_ASSERT(index.column() >= 0 && index.column() < columnCount()); + + bool groupNameRow = index.row() == -1; + KisSwatchGroup *group = static_cast(index.internalPointer()); + QString groupName = group->name(); + if (groupNameRow) { + switch (role) { + case Qt::ToolTipRole: + case Qt::DisplayRole: { + return groupName; } - if (m_colorSet->nColorsGroup()==0) { - rowstotal+=1; //always add one for the default group when considering groups. + case IsHeaderRole: { + return true; } - */ - - /* - Q_FOREACH (QString groupName, m_colorSet->getGroupNames()){ - //we make an int for the rows added by the current group. - int newrows = 1+m_colorSet->nColorsGroup(groupName)/columnCount(); - if (m_colorSet->nColorsGroup(groupName)%columnCount() > 0) { - newrows+=1; - } - if (newrows==0) { - newrows+=1; //always add one for the group when considering groups. - } - quint32 tempIndex = (quint32)((index.row()-(rowstotal+2))*columnCount()+index.column()); - if (index.row() == rowstotal+1) { - //rowstotal+1 is taken up by the groupname. - indexGroupName = groupName; - groupNameRow = true; - } else if (index.row() > (rowstotal+1) && index.row() <= rowstotal+newrows && - tempIndexnColorsGroup(groupName)){ - //otherwise it's an index to the colors in the group. - indexGroupName = groupName; - indexInGroup = tempIndex; - } - //add the new rows to the totalrows we've looked at. - rowstotal += newrows; + case RetrieveEntryRole: { + QStringList entryList; + entryList.append(groupName); + entryList.append(QString::number(0)); + return entryList; } - */ - if (groupNameRow) { - switch (role) { - case Qt::ToolTipRole: - case Qt::DisplayRole: { - return indexGroupName; - } - case IsHeaderRole: { - return true; - } - case RetrieveEntryRole: { - QStringList entryList; - entryList.append(indexGroupName); - entryList.append(QString::number(0)); - return entryList; - } - } - } else { - entry = m_colorSet->getColorGroup(index.row(), index.column(), indexGroupName); - switch (role) { - case Qt::ToolTipRole: - case Qt::DisplayRole: { - return entry.name(); - } - case Qt::BackgroundRole: { - QColor color = m_displayRenderer->toQColor(entry.color()); - return QBrush(color); - } - case IsHeaderRole: { - return false; - } - case RetrieveEntryRole: { - QStringList entryList; - entryList.append(indexGroupName); - // entryList.append(QString::number(indexInGroup)); - return entryList; - } - } + default: // should not happen + return false; + } + } else { + KisSwatch entry = m_colorSet->getColorGroup(index.row(), index.column(), groupName); + if (!(m_colorSet->getGroup(groupName)->checkEntry(index.row(), index.column()))) { + return false; + } + switch (role) { + case Qt::ToolTipRole: + case Qt::DisplayRole: { + return entry.name(); + } + case Qt::BackgroundRole: { + QColor color = m_displayRenderer->toQColor(entry.color()); + return QBrush(color); + } + case IsHeaderRole: { + return false; + } + case RetrieveEntryRole: { + QStringList entryList; + entryList.append(groupName); + return entryList; + } + default: // should not happen + return false; } } - return QVariant(); } int KisPaletteModel::rowCount(const QModelIndex& /*parent*/) const { - /* - if (!m_colorSet) { + if (!m_colorSet) return 0; - } - if (m_colorSet->nColors()==0) { - return 0; - } - if (columnCount() > 0) { - int countedrows = m_colorSet->nColorsGroup("")/columnCount(); - if (m_colorSet->nColorsGroup()%columnCount() > 0) { - countedrows+=1; - } - if (m_colorSet->nColorsGroup()==0) { - countedrows+=1; - } - Q_FOREACH (QString groupName, m_colorSet->getGroupNames()) { - countedrows += 1; //add one for the name; - countedrows += 1+(m_colorSet->nColorsGroup(groupName)/ columnCount()); - if (m_colorSet->nColorsGroup(groupName)%columnCount() > 0) { - countedrows+=1; - } - if (m_colorSet->nColorsGroup(groupName)==0) { - countedrows+=1; - } - } - countedrows +=1; //Our code up till now doesn't take 0 into account. - return countedrows; - } - return m_colorSet->nColors()/15 + 1; - */ - if (m_colorSet) { - return m_colorSet->rowCount() // count of color rows - + m_colorSet->getGroupNames().size() // rows from names - + 1; // take 0 into account - } - 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(); } - return 15; + 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 { - if (m_colorSet) { - return QAbstractTableModel::index(row, column, parent); - /* - //make an int to hold the amount of rows we've looked at. The initial is the total rows in the default group. - int rowstotal = m_colorSet->nColorsGroup()/columnCount(); - if (row<=rowstotal && (quint32)(row*columnCount()+column)nColorsGroup()) { - //if the total rows are in the default group, we just return an index. - return QAbstractTableModel::index(row, column, parent); - } else if(row<0 && column<0) { - return QAbstractTableModel::index(0, 0, parent); - } - if (m_colorSet->nColorsGroup()==0) { - rowstotal+=1; //always add one for the default group when considering groups. - } - Q_FOREACH (QString groupName, m_colorSet->getGroupNames()){ - //we make an int for the rows added by the current group. - int newrows = 1+m_colorSet->nColorsGroup(groupName)/columnCount(); - if (m_colorSet->nColorsGroup(groupName)%columnCount() > 0) { - newrows+=1; - } - if (m_colorSet->nColorsGroup(groupName)==0) { - newrows+=1; //always add one for the group when considering groups. - } - if (rowstotal + newrows>rowCount()) { - newrows = rowCount() - rowstotal; - } - quint32 tempIndex = (quint32)((row-(rowstotal+2))*columnCount()+column); - if (row == rowstotal+1) { - //rowstotal+1 is taken up by the groupname. - return QAbstractTableModel::index(row, 0, parent); - } else if (row > (rowstotal+1) && row <= rowstotal+newrows && tempIndexnColorsGroup(groupName)){ - //otherwise it's an index to the colors in the group. - return QAbstractTableModel::index(row, column, parent); + Q_UNUSED(parent); + int yInGroup = row; + KisSwatchGroup *group = Q_NULLPTR; + Q_ASSERT(m_colorSet); + for (const QString &currGroupName : m_colorSet->getGroupNames()) { + if (yInGroup >= m_colorSet->getGroup(currGroupName)->nRows()) { + // minus 1 for group name line + yInGroup = yInGroup - m_colorSet->getGroup(currGroupName)->nRows() - 1; + } else { + group = m_colorSet->getGroup(currGroupName); + if (!group->checkEntry(column, yInGroup)) { + return QModelIndex(); } - //add the new rows to the totalrows we've looked at. - rowstotal += newrows; + break; } - */ } - return QModelIndex(); + Q_ASSERT(group); + return createIndex(column, yInGroup, group); } void KisPaletteModel::setColorSet(KoColorSet* colorSet) { m_colorSet = colorSet; beginResetModel(); endResetModel(); } KoColorSet* KisPaletteModel::colorSet() const { return m_colorSet; } QModelIndex KisPaletteModel::indexFromId(int i) const { return QModelIndex(); /* QModelIndex index = QModelIndex(); if (!colorSet() || colorSet()->nColors() == 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 { /* KoColorSetEntry blank = KoColorSetEntry(); if (!index.isValid()) { return blank; } QStringList entryList = qvariant_cast(data(index, RetrieveEntryRole)); if (entryList.isEmpty()) { return blank; } QString groupName = entryList.at(0); quint32 indexInGroup = entryList.at(1).toUInt(); return m_colorSet->getColorGroup(indexInGroup, groupName); */ return KisSwatch(); } bool KisPaletteModel::addColorSetEntry(KoColorSetEntry entry, QString groupName) { return true; } bool KisPaletteModel::removeEntry(QModelIndex index, bool keepColors) { return true; /* QStringList entryList = qvariant_cast(index.data(RetrieveEntryRole)); if (entryList.empty()) { return false; } QString groupName = entryList.at(0); quint32 indexInGroup = entryList.at(1).toUInt(); if (qvariant_cast(index.data(IsHeaderRole))==false) { if (index.column()-1<0 && m_colorSet->nColorsGroup(groupName)%columnCount() <1 && index.row()-1>0 && m_colorSet->nColorsGroup(groupName)/columnCount()>0) { beginRemoveRows(QModelIndex(), index.row(), index.row()-1); } m_colorSet->removeAt(indexInGroup, groupName); if (index.column()-1<0 && m_colorSet->nColorsGroup(groupName)%columnCount() <1 && index.row()-1>0 && m_colorSet->nColorsGroup(groupName)/columnCount()>0) { endRemoveRows(); } } else { beginRemoveRows(QModelIndex(), index.row(), index.row()-1); m_colorSet->removeGroup(groupName, keepColors); endRemoveRows(); } return true; */ } bool KisPaletteModel::removeRows(int row, int count, const QModelIndex &parent) { /* Q_ASSERT(!parent.isValid()); int beginRow = qMax(0, row); int endRow = qMin(row + count - 1, (int)m_colorSet->nColors() - 1); beginRemoveRows(parent, beginRow, endRow); // Find the palette entry at row, count, remove from KoColorSet endRemoveRows(); return true; */ 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(IsHeaderRole))){ 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(IsHeaderRole))==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(IsHeaderRole))==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; } diff --git a/libs/widgets/kis_palette_delegate.cpp b/libs/widgets/kis_palette_delegate.cpp index 72ee7e4bf9..b5fe48c5c8 100644 --- a/libs/widgets/kis_palette_delegate.cpp +++ b/libs/widgets/kis_palette_delegate.cpp @@ -1,89 +1,89 @@ /* * 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_delegate.h" #include #include #include #include "kis_debug.h" #include KisPaletteDelegate::KisPaletteDelegate(QObject *parent) : QAbstractItemDelegate(parent) { } KisPaletteDelegate::~KisPaletteDelegate() { } void KisPaletteDelegate::setCrossedKeyword(const QString &value) { m_crossedKeyword = value; } void KisPaletteDelegate::paint(QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex & index) const { painter->save(); - if (! index.isValid()) + if (!index.isValid()) return; const bool isSelected = option.state & QStyle::State_Selected; const int minSize = qMin(option.rect.width(), option.rect.height()); const int maxWidth = qBound(2, minSize / 6, 4); const int width = isSelected ? maxWidth : 1; if (qvariant_cast(index.data(KisPaletteModel::IsHeaderRole))) { QString name = qvariant_cast(index.data(Qt::DisplayRole)); if (isSelected) { painter->fillRect(option.rect, option.palette.highlight()); } QRect paintRect = kisGrowRect(option.rect, -width); painter->drawText(paintRect, name); } else { if (isSelected) { painter->fillRect(option.rect, option.palette.highlight()); } QRect paintRect = kisGrowRect(option.rect, -width); QBrush brush = qvariant_cast(index.data(Qt::BackgroundRole)); painter->fillRect(paintRect, brush); } painter->restore(); QString name = qvariant_cast(index.data(Qt::DisplayRole)); if (!m_crossedKeyword.isNull() && name.toLower().contains(m_crossedKeyword)) { QRect crossRect = kisGrowRect(option.rect, -maxWidth); painter->save(); painter->setRenderHint(QPainter::Antialiasing, true); painter->setPen(QPen(Qt::white, 2.5)); painter->drawLine(crossRect.topLeft(), crossRect.bottomRight()); painter->setPen(QPen(Qt::red, 1.0)); painter->drawLine(crossRect.topLeft(), crossRect.bottomRight()); painter->restore(); } } QSize KisPaletteDelegate::sizeHint(const QStyleOptionViewItem & option, const QModelIndex &) const { return option.decorationSize; } diff --git a/libs/widgets/kis_palette_view.cpp b/libs/widgets/kis_palette_view.cpp index 580bd96847..b35c950736 100644 --- a/libs/widgets/kis_palette_view.cpp +++ b/libs/widgets/kis_palette_view.cpp @@ -1,354 +1,354 @@ /* * Copyright (c) 2016 Dmitry Kazakov * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_palette_view.h" #include #include #include #include #include #include #include #include #include #include #include #include #include "kis_palette_delegate.h" #include "KisPaletteModel.h" #include "kis_color_button.h" struct KisPaletteView::Private { Private() : model(Q_NULLPTR) , allowPaletteModification(true) { 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"); } KisPaletteModel *model; QMenu entryClickedMenu; bool allowPaletteModification; }; KisPaletteView::KisPaletteView(QWidget *parent) : KoTableView(parent) , m_d(new Private) { setShowGrid(false); horizontalHeader()->setVisible(false); verticalHeader()->setVisible(false); setItemDelegate(new KisPaletteDelegate()); // setDragEnabled(true); // setDragDropMode(QAbstractItemView::InternalMove); setDropIndicatorShown(true); KConfigGroup cfg(KSharedConfig::openConfig()->group("")); //QPalette pal(palette()); //pal.setColor(QPalette::Base, cfg.getMDIBackgroundColor()); //setAutoFillBackground(true); //setPalette(pal); int defaultSectionSize = cfg.readEntry("paletteDockerPaletteViewSectionSize", 12); horizontalHeader()->setDefaultSectionSize(defaultSectionSize); verticalHeader()->setDefaultSectionSize(defaultSectionSize); connect(this, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(modifyEntry(QModelIndex))); } 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()->nColors()+1)); lnIDName->setText(QString::number(m_d->model->colorSet()->nColors()+1)); bnColor->setColor(color); chkSpot->setChecked(false); if (window->exec() == KoDialog::Accepted) { QString groupName = cmbGroups->currentText(); if (groupName == defaultGroupName) { groupName = QString(); } KoColorSetEntry newEntry; newEntry.setColor(bnColor->color()); newEntry.setName(lnName->text()); newEntry.setId(lnIDName->text()); newEntry.setSpotColor(chkSpot->isChecked()); m_d->model->addColorSetEntry(newEntry, groupName); m_d->model->colorSet()->save(); return true; } return false; */ return true; } bool KisPaletteView::removeEntryWithDialog(QModelIndex index) { return true; /* bool keepColors = true; if (qvariant_cast(index.data(KisPaletteModel::IsHeaderRole))) { 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) { /* KoColorSet* color_set = m_d->model->colorSet(); if (!color_set) return; //also don't select if the color is the same as the current selection if (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); */ } void KisPaletteView::mouseReleaseEvent(QMouseEvent *event) { bool foreground = false; if (event->button()== Qt::LeftButton) { foreground = true; } else if (event->button() == Qt::RightButton) { - m_d->entryClickedMenu.exec(); + m_d->entryClickedMenu.exec(QCursor::pos()); } entrySelection(foreground); } void KisPaletteView::paletteModelChanged() { /* updateView(); updateRows(); */ } void KisPaletteView::setPaletteModel(KisPaletteModel *model) { if (m_d->model) { disconnect(m_d->model, 0, this, 0); } m_d->model = model; setModel(model); paletteModelChanged(); connect(m_d->model, SIGNAL(layoutChanged(QList,QAbstractItemModel::LayoutChangeHint)), this, SLOT(paletteModelChanged())); connect(m_d->model, SIGNAL(rowsMoved(QModelIndex,int,int,QModelIndex,int)), this, SLOT(paletteModelChanged())); connect(m_d->model, SIGNAL(rowsInserted(QModelIndex,int,int)), this, SLOT(paletteModelChanged())); connect(m_d->model, SIGNAL(rowsRemoved(QModelIndex,int,int)), this, SLOT(paletteModelChanged())); connect(m_d->model, SIGNAL(modelReset()), this, SLOT(paletteModelChanged())); } KisPaletteModel* KisPaletteView::paletteModel() const { return m_d->model; } void KisPaletteView::updateRows() { /* this->clearSpans(); if (m_d->model) { for (int r=0; r<=m_d->model->rowCount(); r++) { QModelIndex index = m_d->model->index(r, 0); if (qvariant_cast(index.data(KisPaletteModel::IsHeaderRole))) { setSpan(r, 0, 1, m_d->model->columnCount()); setRowHeight(r, this->fontMetrics().lineSpacing()+6); } else { this->setRowHeight(r, this->columnWidth(0)); } } } */ } void KisPaletteView::setAllowModification(bool allow) { m_d->allowPaletteModification = allow; } void KisPaletteView::wheelEvent(QWheelEvent *event) { if (event->modifiers() & Qt::ControlModifier) { int numDegrees = event->delta() / 8; int numSteps = numDegrees / 7; int curSize = horizontalHeader()->sectionSize(0); int setSize = numSteps + curSize; if ( (event->delta() <= 0) && (setSize <= 8) ) { // Ignore scroll-zooming down below a certain size } else { horizontalHeader()->setDefaultSectionSize(setSize); verticalHeader()->setDefaultSectionSize(setSize); KConfigGroup cfg(KSharedConfig::openConfig()->group("")); cfg.writeEntry("paletteDockerPaletteViewSectionSize", setSize); } event->accept(); } else { KoTableView::wheelEvent(event); } } void KisPaletteView::entrySelection(bool foreground) { /* QModelIndex index; if (selectedIndexes().size()<=0) { return; } if (selectedIndexes().last().isValid()) { index = selectedIndexes().last(); } else if (selectedIndexes().first().isValid()) { index = selectedIndexes().first(); } else { return; } if (qvariant_cast(index.data(KisPaletteModel::IsHeaderRole))==false) { KoColorSetEntry entry = m_d->model->colorSetEntryFromIndex(index); if (foreground) { emit(entrySelected(entry)); emit(indexEntrySelected(index)); } else { emit(entrySelectedBackGround(entry)); emit(indexEntrySelected(index)); } } */ } void KisPaletteView::modifyEntry(QModelIndex index) { /* if (m_d->allowPaletteModification) { KoDialog *group = new KoDialog(this); QFormLayout *editableItems = new QFormLayout(group); group->mainWidget()->setLayout(editableItems); QLineEdit *lnIDName = new QLineEdit(group); QLineEdit *lnGroupName = new QLineEdit(group); KisColorButton *bnColor = new KisColorButton(group); QCheckBox *chkSpot = new QCheckBox(group); if (qvariant_cast(index.data(KisPaletteModel::IsHeaderRole))) { QString groupName = qvariant_cast(index.data(Qt::DisplayRole)); editableItems->addRow(i18nc("Name for a colorgroup","Name"), lnGroupName); lnGroupName->setText(groupName); if (group->exec() == KoDialog::Accepted) { m_d->model->colorSet()->changeGroupName(groupName, lnGroupName->text()); m_d->model->colorSet()->save(); updateRows(); } //rename the group. } else { KoColorSetEntry entry = m_d->model->colorSetEntryFromIndex(index); QStringList entryList = qvariant_cast(index.data(KisPaletteModel::RetrieveEntryRole)); 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 (group->exec() == KoDialog::Accepted) { entry.setName(lnGroupName->text()); entry.setId(lnIDName->text()); entry.setColor(bnColor->color()); entry.setSpotColor(chkSpot->isChecked()); m_d->model->colorSet()->changeColorSetEntry(entry, entryList.at(0), entryList.at(1).toUInt()); m_d->model->colorSet()->save(); } } } */ }