diff --git a/libs/command/kis_command_ids.h b/libs/command/kis_command_ids.h --- a/libs/command/kis_command_ids.h +++ b/libs/command/kis_command_ids.h @@ -32,7 +32,8 @@ ChangeEllipseShapeId, ChangeRectangleShapeId, ChangePathShapePointId, - ChangePathShapeControlPointId + ChangePathShapeControlPointId, + ChangePaletteId }; } diff --git a/libs/koplugin/KisMimeDatabase.cpp b/libs/koplugin/KisMimeDatabase.cpp --- a/libs/koplugin/KisMimeDatabase.cpp +++ b/libs/koplugin/KisMimeDatabase.cpp @@ -222,6 +222,11 @@ mimeType.suffixes = QStringList() << "gpl" << "pal" << "act" << "aco" << "colors" << "xml" << "sbz"; s_mimeDatabase << mimeType; + mimeType.mimeType = "krita/x-colorset"; + mimeType.description = i18nc("description of a file type", "Krita Color Palette"); + mimeType.suffixes = QStringList() << "kpl"; + s_mimeDatabase << mimeType; + mimeType.mimeType = "application/x-opencolorio-configuration"; mimeType.description = i18nc("description of a file type", "OpenColorIO Configuration"); mimeType.suffixes = QStringList() << "ocio"; diff --git a/libs/libkis/Palette.h b/libs/libkis/Palette.h --- a/libs/libkis/Palette.h +++ b/libs/libkis/Palette.h @@ -20,6 +20,8 @@ #define LIBKIS_PALETTE_H #include +#include + #include "kritalibkis_export.h" #include "libkis.h" #include "Resource.h" @@ -88,7 +90,7 @@ * @brief groupNames * @return the list of group names. This is list is in the order these groups are in the file. */ - QStringList groupNames(); + QStringList groupNames() const; /** * @brief addGroup * @param name of the new group @@ -108,12 +110,6 @@ * @return the total amount of entries in the whole group */ int colorsCountTotal(); - /** - * @brief colorsCountGroup - * @param name of the group to check. Empty is the default group. - * @return the amount of colors within that group. - */ - int colorsCountGroup(QString name); /** * @brief colorSetEntryByIndex @@ -121,14 +117,14 @@ * @param index the global index * @return the colorset entry */ - KoColorSetEntry colorSetEntryByIndex(int index); + KisSwatch colorSetEntryByIndex(int index); /** * @brief colorSetEntryFromGroup * @param index index in the group. * @param groupName the name of the group to get the color from. * @return the colorsetentry. */ - KoColorSetEntry colorSetEntryFromGroup(int index, const QString &groupName); + KisSwatch colorSetEntryFromGroup(int index, const QString &groupName); /** * @brief colorForEntry @@ -136,34 +132,20 @@ * @param entry the entry * @return the ManagedColorObject */ - ManagedColor *colorForEntry(KoColorSetEntry entry); + ManagedColor *colorForEntry(KisSwatch entry); /** * @brief addEntry * add an entry to a group. Gets appended to the end. * @param entry the entry * @param groupName the name of the group to add to. */ - void addEntry(KoColorSetEntry entry, QString groupName = QString()); + void addEntry(KisSwatch entry, QString groupName = QString()); /** * @brief removeEntry * remove the entry at @param index from the group @param groupName. */ void removeEntry(int index, const QString &groupName); - /** - * @brief insertEntry - * like addentry, but allows you to pick the index to insertBefore. - * @param index - * @param entry - * @param groupName - */ - void insertEntry(int index, KoColorSetEntry entry, QString groupName = QString()); - /** - * @brief editEntry - * Changes the entry at @param index by replacing it with @param entry. - * @param groupName the group at which the index is. - * @return whether it was successful. - */ - bool editEntry (int index, KoColorSetEntry entry, QString groupName = QString()); + /** * @brief changeGroupName * change the group name. diff --git a/libs/libkis/Palette.cpp b/libs/libkis/Palette.cpp --- a/libs/libkis/Palette.cpp +++ b/libs/libkis/Palette.cpp @@ -18,7 +18,10 @@ #include "Palette.h" #include +#include +#include #include +#include struct Palette::Private { KoColorSet *palette {0}; @@ -36,7 +39,7 @@ int Palette::numberOfEntries() const { if (!d->palette) return 0; - return d->palette->nColors(); + return d->palette->colorCount(); } int Palette::columnCount() @@ -63,7 +66,7 @@ return d->palette->setComment(comment); } -QStringList Palette::groupNames() +QStringList Palette::groupNames() const { if (!d->palette) return QStringList(); return d->palette->getGroupNames(); @@ -84,53 +87,55 @@ int Palette::colorsCountTotal() { if (!d->palette) return 0; - return d->palette->nColors(); + return d->palette->colorCount(); } -int Palette::colorsCountGroup(QString name) +KisSwatch Palette::colorSetEntryByIndex(int index) { - if (!d->palette) return 0; - return d->palette->nColorsGroup(name); -} - -KoColorSetEntry Palette::colorSetEntryByIndex(int index) -{ - if (!d->palette) return KoColorSetEntry(); - return d->palette->getColorGlobal(index); + if (!d->palette) return KisSwatch(); + int col = index % columnCount(); + int row = (index - col) / columnCount(); + return d->palette->getColorGlobal(col, row); + return KisSwatch(); } -KoColorSetEntry Palette::colorSetEntryFromGroup(int index, const QString &groupName) +KisSwatch Palette::colorSetEntryFromGroup(int index, const QString &groupName) { - if (!d->palette) return KoColorSetEntry(); - - return d->palette->getColorGroup(index, groupName); + if (!d->palette) return KisSwatch(); + int row = index % columnCount(); + return d->palette->getColorGroup((index - row) / columnCount(), row, groupName); } -ManagedColor *Palette::colorForEntry(KoColorSetEntry entry) +ManagedColor *Palette::colorForEntry(KisSwatch entry) { - if (!d->palette) return 0; + if (!d->palette) return Q_NULLPTR; ManagedColor *color = new ManagedColor(entry.color()); return color; } -void Palette::addEntry(KoColorSetEntry entry, QString groupName) +void Palette::addEntry(KisSwatch entry, QString groupName) { d->palette->add(entry, groupName); } void Palette::removeEntry(int index, const QString &groupName) { - d->palette->removeAt(index, groupName); -} - -void Palette::insertEntry(int index, KoColorSetEntry entry, QString groupName) -{ - d->palette->insertBefore(entry, index, groupName); -} + int col = index % columnCount(); + int tmp = index; + int row = (index - col) / columnCount(); + KisSwatchGroup *groupFoundIn = Q_NULLPTR; + Q_FOREACH(const QString &name, groupNames()) { + KisSwatchGroup *g = d->palette->getGroup(name); + tmp -= g->rowCount() * columnCount(); + if (tmp < 0) { + groupFoundIn = g; + break; + } + row -= g->rowCount(); -bool Palette::editEntry(int index, KoColorSetEntry entry, QString groupName) -{ - return d->palette->changeColorSetEntry(entry, groupName, index); + } + if (!groupFoundIn) { return; } + groupFoundIn->removeEntry(col, row); } bool Palette::changeGroupName(QString oldGroupName, QString newGroupName) diff --git a/libs/libkis/PaletteView.h b/libs/libkis/PaletteView.h --- a/libs/libkis/PaletteView.h +++ b/libs/libkis/PaletteView.h @@ -82,17 +82,16 @@ * fires when a swatch is selected with leftclick. * @param entry */ - void entrySelectedForeGround(KoColorSetEntry entry); + void entrySelectedForeGround(KisSwatch entry); /** * @brief entrySelectedBackGround * fires when a swatch is selected with rightclick. * @param entry */ - void entrySelectedBackGround(KoColorSetEntry entry); + void entrySelectedBackGround(KisSwatch entry); private: struct Private; const QScopedPointer d; - }; #endif // LIBKIS_PALETTE_VIEW_H diff --git a/libs/libkis/PaletteView.cpp b/libs/libkis/PaletteView.cpp --- a/libs/libkis/PaletteView.cpp +++ b/libs/libkis/PaletteView.cpp @@ -36,10 +36,10 @@ this->layout()->addWidget(d->widget); //forward signals. - connect(d->widget, SIGNAL(entrySelected(KoColorSetEntry)), - this, SIGNAL(entrySelectedForeGround(KoColorSetEntry))); - connect(d->widget, SIGNAL(entrySelectedBackGround(KoColorSetEntry)), - this, SIGNAL(entrySelectedBackGround(KoColorSetEntry))); + connect(d->widget, SIGNAL(entrySelected(KisSwatch)), + this, SIGNAL(entrySelectedForeGround(KisSwatch))); + connect(d->widget, SIGNAL(entrySelectedBackGround(KisSwatch)), + this, SIGNAL(entrySelectedBackGround(KisSwatch))); } PaletteView::~PaletteView() @@ -49,7 +49,7 @@ void PaletteView::setPalette(Palette *palette) { - d->model->setColorSet(palette->colorSet()); + d->model->setPalette(palette->colorSet()); d->widget->setPaletteModel(d->model); } @@ -79,5 +79,5 @@ void PaletteView::trySelectClosestColor(ManagedColor *color) { - d->widget->trySelectClosestColor(color->color()); + d->widget->selectClosestColor(color->color()); } diff --git a/libs/libqml/plugins/kritasketchplugin/models/PaletteColorsModel.cpp b/libs/libqml/plugins/kritasketchplugin/models/PaletteColorsModel.cpp --- a/libs/libqml/plugins/kritasketchplugin/models/PaletteColorsModel.cpp +++ b/libs/libqml/plugins/kritasketchplugin/models/PaletteColorsModel.cpp @@ -59,28 +59,31 @@ return 0; if (!d->colorSet) return 0; - return d->colorSet->nColors(); + return d->colorSet->colorCount(); } QVariant PaletteColorsModel::data(const QModelIndex &index, int role) const { QVariant result; + /* QColor color; if (index.isValid() && d->colorSet) { switch(role) { case ImageRole: - color = d->colorSet->getColorGlobal(index.row()).color().toQColor(); + color = d->colorSet->getColorGlobal(index.row(), index.column()).color().toQColor(); result = QString("image://color/%1,%2,%3,%4").arg(color.redF()).arg(color.greenF()).arg(color.blueF()).arg(color.alphaF()); break; case TextRole: - result = d->colorSet->getColorGlobal(index.row()).name(); + result = d->colorSet->getColorGlobal(index.row(), index.column()).name(); break; default: break; } } + */ + return result; } @@ -135,13 +138,14 @@ if ( !d->view ) return; - if (index >= 0 && index < (int)d->colorSet->nColors()) - { + if (index >= 0 && index < (int)d->colorSet->colorCount()) { + /* if (setBackgroundColor) d->view->resourceProvider()->setBGColor(d->colorSet->getColorGlobal(index).color()); else d->view->resourceProvider()->setFGColor( d->colorSet->getColorGlobal(index).color()); emit colorChanged(d->colorSet->getColorGlobal(index).color().toQColor(), setBackgroundColor); + */ } } diff --git a/libs/pigment/CMakeLists.txt b/libs/pigment/CMakeLists.txt --- a/libs/pigment/CMakeLists.txt +++ b/libs/pigment/CMakeLists.txt @@ -76,7 +76,8 @@ colorprofiles/KoDummyColorProfile.cpp resources/KoAbstractGradient.cpp resources/KoColorSet.cpp - resources/KoColorSetEntry.cpp + resources/KisSwatch.cpp + resources/KisSwatchGroup.cpp resources/KoPattern.cpp resources/KoResource.cpp resources/KoMD5Generator.cpp diff --git a/libs/pigment/resources/KoColorSetEntry.h b/libs/pigment/resources/KisSwatch.h rename from libs/pigment/resources/KoColorSetEntry.h rename to libs/pigment/resources/KisSwatch.h --- a/libs/pigment/resources/KoColorSetEntry.h +++ b/libs/pigment/resources/KisSwatch.h @@ -19,42 +19,47 @@ * Boston, MA 02110-1301, USA. */ -#ifndef KOCOLORSETENTRY_H -#define KOCOLORSETENTRY_H +#ifndef KISSWATCH_H +#define KISSWATCH_H #include "kritapigment_export.h" #include #include "KoColor.h" -class KRITAPIGMENT_EXPORT KoColorSetEntry +class KRITAPIGMENT_EXPORT KisSwatch { public: - KoColorSetEntry(); - KoColorSetEntry(const KoColor &color, const QString &name); + KisSwatch(); + KisSwatch(const KoColor &color, const QString &name = QString()); 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 { + bool operator==(const KisSwatch& rhs) const { return m_color == rhs.m_color && m_name == rhs.m_name; } + KisSwatch &operator =(const KisSwatch &source); + private: KoColor m_color; QString m_name; QString m_id; bool m_spotColor; + bool m_valid; }; -#endif // KOCOLORSETENTRY_H +#endif // KISSWATCH_H diff --git a/libs/pigment/resources/KoColorSetEntry.cpp b/libs/pigment/resources/KisSwatch.cpp rename from libs/pigment/resources/KoColorSetEntry.cpp rename to libs/pigment/resources/KisSwatch.cpp --- a/libs/pigment/resources/KoColorSetEntry.cpp +++ b/libs/pigment/resources/KisSwatch.cpp @@ -19,15 +19,53 @@ * Boston, MA 02110-1301, USA. */ -#include "KoColorSetEntry.h" +#include "KisSwatch.h" -KoColorSetEntry::KoColorSetEntry() + +KisSwatch::KisSwatch() : m_spotColor(false) + , m_valid(false) { } -KoColorSetEntry::KoColorSetEntry(const KoColor &color, const QString &name) +KisSwatch::KisSwatch(const KoColor &color, const QString &name) : m_color(color) , m_name(name) , m_spotColor(false) + , m_valid(true) { } +void KisSwatch::setName(const QString &name) +{ + m_name = name; + m_valid = true; +} + +void KisSwatch::setId(const QString &id) +{ + m_id = id; + m_valid = true; +} + +void KisSwatch::setColor(const KoColor &color) +{ + m_color = color; + m_valid = true; +} + +void KisSwatch::setSpotColor(bool spotColor) +{ + m_spotColor = spotColor; + m_valid = true; +} + +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; + m_valid = source.m_valid; + return *this; +} diff --git a/libs/pigment/resources/KisSwatchGroup.h b/libs/pigment/resources/KisSwatchGroup.h new file mode 100644 --- /dev/null +++ b/libs/pigment/resources/KisSwatchGroup.h @@ -0,0 +1,127 @@ +/* This file is part of the KDE project + Copyright (c) 2005 Boudewijn Rempt + Copyright (c) 2016 L. E. Segovia + Copyright (c) 2018 Michael Zhou + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + + */ + +#ifndef KISSWATCHGROUP_H +#define KISSWATCHGROUP_H + +#include "KisSwatch.h" + +#include "kritapigment_export.h" + +#include +#include +#include +#include + +/** + * @brief The KisSwatchGroup class stores a matrix of color swatches + * swatches can accessed using (x, y) coordinates. + * x is the column number from left to right and y is the row number from top + * to bottom. + * Both x and y start at 0 + * there could be empty entries, so the checkEntry(int, int) method must used + * whenever you want to get an entry from the matrix + */ +class KRITAPIGMENT_EXPORT KisSwatchGroup +{ +public /* struct */: + struct SwatchInfo { + QString group; + KisSwatch swatch; + int row; + int column; + }; + +public: + KisSwatchGroup(); + ~KisSwatchGroup(); + KisSwatchGroup(const KisSwatchGroup &rhs); + KisSwatchGroup &operator =(const KisSwatchGroup &rhs); + +public /* methods */: + void setName(const QString &name); + QString name() const; + + void setColumnCount(int columnCount); + int columnCount() const; + + void setRowCount(int newRowCount); + int rowCount() const; + + int colorCount() const; + + QList infoList() const; + + /** + * @brief checkEntry + * checks if position x and y has a valid entry + * both x and y start from 0 + * @param x + * @param y + * @return true if there is a valid entry at position (x, y) + */ + bool checkEntry(int column, int row) 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 column, int row); + /** + * @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 column, int row) 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 column, int row); + /** + * @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(); + +private /* member variables */: + struct Private; + QScopedPointer d; +}; + +#endif // KISSWATCHGROUP_H diff --git a/libs/pigment/resources/KisSwatchGroup.cpp b/libs/pigment/resources/KisSwatchGroup.cpp new file mode 100644 --- /dev/null +++ b/libs/pigment/resources/KisSwatchGroup.cpp @@ -0,0 +1,214 @@ +/* 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" + +struct KisSwatchGroup::Private { + typedef QMap Column; + + Private() + : name(QString()) + , colorMatrix(DEFAULT_COLUMN_COUNT) + , colorCount(0) + , rowCount(DEFAULT_ROW_COUNT) + { } + + static int DEFAULT_COLUMN_COUNT; + static int DEFAULT_ROW_COUNT; + + QString name; + QVector colorMatrix; + int colorCount; + int rowCount; +}; + +int KisSwatchGroup::Private::DEFAULT_COLUMN_COUNT = 16; +int KisSwatchGroup::Private::DEFAULT_ROW_COUNT = 20; + +KisSwatchGroup::KisSwatchGroup() + : d(new Private) +{ } + +KisSwatchGroup::~KisSwatchGroup() +{ } + +KisSwatchGroup::KisSwatchGroup(const KisSwatchGroup &rhs) + : d(new Private(*rhs.d)) +{ } + +KisSwatchGroup &KisSwatchGroup::operator =(const KisSwatchGroup &rhs) +{ + if (&rhs == this) { + return *this; + } + d.reset(new Private(*rhs.d)); + return *this; +} + +void KisSwatchGroup::setEntry(const KisSwatch &e, int column, int row) +{ + Q_ASSERT(column < d->colorMatrix.size() && column >= 0 && row >= 0); + if (row >= d->rowCount) { + setRowCount(row + 1); + } + if (!checkEntry(column, row)) { + d->colorCount++; + } + d->colorMatrix[column][row] = e; +} + +bool KisSwatchGroup::checkEntry(int column, int row) const +{ + if (row >= d->rowCount || column >= d->colorMatrix.size() || column < 0) { + return false; + } + if (!d->colorMatrix[column].contains(row)) { + return false; + } + return true; +} + +bool KisSwatchGroup::removeEntry(int column, int row) +{ + if (d->colorCount == 0) { + return false; + } + + if (row >= d->rowCount || column >= d->colorMatrix.size() || column < 0) { + return false; + } + + // QMap::remove returns 1 if key found else 0 + if (d->colorMatrix[column].remove(row)) { + d->colorCount -= 1; + return true; + } else { + return false; + } +} + +void KisSwatchGroup::setColumnCount(int columnCount) +{ + Q_ASSERT(columnCount >= 0); + if (columnCount < d->colorMatrix.size()) { + for (int i = d->colorMatrix.size() - 1; i <= columnCount; i-- ) { + d->colorCount -= d->colorMatrix[i].size(); + } + } + d->colorMatrix.resize(columnCount); +} + +int KisSwatchGroup::columnCount() const { + return d->colorMatrix.size(); +} + +KisSwatch KisSwatchGroup::getEntry(int column, int row) const +{ + Q_ASSERT(checkEntry(column, row)); + return d->colorMatrix[column][row]; +} + +void KisSwatchGroup::addEntry(const KisSwatch &e) +{ + if (columnCount() == 0) { + setColumnCount(Private::DEFAULT_COLUMN_COUNT); + } + + if (d->colorCount == 0) { + setEntry(e, 0, 0); + return; + } + + int y = 0; + for (const Private::Column &c : d->colorMatrix) { + if (c.isEmpty()) { continue; } + if (y < c.lastKey()) { + y = c.lastKey(); + } + } + for (int x = d->colorMatrix.size() - 1; x >= 0; x--) { + if (checkEntry(x, y)) { + // if the last entry's at the rightmost column, + // add e to the leftmost column of the next row + // and increase row count + if (++x == d->colorMatrix.size()) { + x = 0; + y++; + } + // else just add it to the right + setEntry(e, x, y); + break; + } + } +} + +void KisSwatchGroup::clear() +{ + d->colorMatrix.clear(); +} + +void KisSwatchGroup::setRowCount(int newRowCount) +{ + d->rowCount = newRowCount; + for (Private::Column &c : d->colorMatrix) { + for (int k : c.keys()) { + if (k >= newRowCount) { + c.remove(k); + d->colorCount--; + } + } + } +} + +int KisSwatchGroup::rowCount() const +{ + return d->rowCount; +} + +int KisSwatchGroup::colorCount() const +{ + return d->colorCount; +} + +QList KisSwatchGroup::infoList() const +{ + QList res; + int column = 0; + for (const Private::Column &c : d->colorMatrix) { + int i = 0; + for (const KisSwatch &s : c.values()) { + SwatchInfo info = {d->name, s, c.keys()[i++], column}; + res.append(info); + } + column++; + } + return res; +} + +void KisSwatchGroup::setName(const QString &name) +{ + d->name = name; +} + +QString KisSwatchGroup::name() const +{ + return d->name; +} diff --git a/libs/pigment/resources/KoColorSet.h b/libs/pigment/resources/KoColorSet.h --- a/libs/pigment/resources/KoColorSet.h +++ b/libs/pigment/resources/KoColorSet.h @@ -27,9 +27,11 @@ #include #include "KoColor.h" -#include "KoColorSetEntry.h" +#include "KisSwatch.h" +#include "KisSwatchGroup.h" /** + * Also called palette. * Open Gimp, Photoshop or RIFF palette files. This is a straight port * from the Gimp. */ @@ -37,7 +39,30 @@ { Q_OBJECT public: + static const QString GLOBAL_GROUP_NAME; + static const QString KPL_VERSION_ATTR; + static const QString KPL_GROUP_ROW_COUNT_ATTR; + static const QString KPL_PALETTE_COLUMN_COUNT_ATTR; + static const QString KPL_PALETTE_NAME_ATTR; + static const QString KPL_PALETTE_COMMENT_ATTR; + static const QString KPL_PALETTE_FILENAME_ATTR; + static const QString KPL_PALETTE_READONLY_ATTR; + static const QString KPL_COLOR_MODEL_ID_ATTR; + static const QString KPL_COLOR_DEPTH_ID_ATTR; + static const QString KPL_GROUP_NAME_ATTR; + static const QString KPL_SWATCH_ROW_ATTR; + static const QString KPL_SWATCH_COL_ATTR; + static const QString KPL_SWATCH_NAME_ATTR; + static const QString KPL_SWATCH_SPOT_ATTR; + static const QString KPL_SWATCH_ID_ATTR; + static const QString KPL_SWATCH_BITDEPTH_ATTR; + static const QString KPL_PALETTE_PROFILE_TAG; + static const QString KPL_SWATCH_POS_TAG; + static const QString KPL_SWATCH_TAG; + static const QString KPL_GROUP_TAG; + static const QString KPL_PALETTE_TAG; +public: enum PaletteType { UNKNOWN = 0, GPL, // GIMP @@ -57,50 +82,58 @@ * a Krita palette, * a Scribus palette or a SwatchBooker palette. */ - explicit KoColorSet(const QString &filename); - - /// Create an empty color set - KoColorSet(); + explicit KoColorSet(const QString &filename = QString()); - /// Explicit copy constructor (KoResource copy constructor is private) + // Explicit copy constructor (KoResource copy constructor is private) KoColorSet(const KoColorSet& rhs); +public /* overridden methods */: // KoResource ~KoColorSet() override; bool load() override; bool loadFromDevice(QIODevice *dev) override; bool save() override; bool saveToDevice(QIODevice* dev) const override; - QString defaultFileExtension() const override; + +public /* methods */: void setColumnCount(int columns); - int columnCount(); - /** - * @brief comment - * @return the comment. - */ - QString comment(); + int columnCount() const; void setComment(QString comment); + QString comment(); -public: + int rowCount() const; + quint32 colorCount() const; + + PaletteType paletteType() const; + void setPaletteType(PaletteType paletteType); /** - * @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. + * @brief isGlobal + * A global color set is a set stored in the config directory + * Such a color set would be opened every time Krita is launched. + * + * A non-global color set, on contrary, would be stored in a kra file, + * and would only be opened when that file is opened by Krita. + * @return */ - void add(const KoColorSetEntry &, QString groupName = QString()); + bool isGlobal() const; + void setIsGlobal(bool); + + bool isEditable() const; + void setIsEditable(bool isEditable); + + QByteArray toByteArray() const; + bool fromByteArray(QByteArray &data); /** - * @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. + * @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. */ - quint32 insertBefore(const KoColorSetEntry &, qint32 index, const QString &groupName = QString()); - - void removeAt(quint32 index, QString groupName = QString()); + void add(const KisSwatch &, const QString &groupName = GLOBAL_GROUP_NAME); + void setEntry(const KisSwatch &e, int x, int y, const QString &groupName = GLOBAL_GROUP_NAME); /** * @brief getColorGlobal @@ -108,7 +141,8 @@ * @param globalIndex the global index over the whole palette. * @return the entry. */ - KoColorSetEntry getColorGlobal(quint32 globalIndex); + KisSwatch getColorGlobal(quint32 x, quint32 y) const; + /** * @brief getColorGroup * A function for getting the color from a specific group. @@ -116,11 +150,7 @@ * @param index the index within the group. * @return the entry */ - KoColorSetEntry getColorGroup(quint32 index, QString groupName = QString()); - - QString findGroupByGlobalIndex(quint32 globalIndex, quint32 *index); - QString findGroupByColorName(const QString &name, quint32 *index); - QString findGroupByID(const QString &id,quint32 *index); + KisSwatch getColorGroup(quint32 x, quint32 y, QString groupName); /** * @brief getGroupNames @@ -128,21 +158,17 @@ */ QStringList getGroupNames(); - bool changeGroupName(QString oldGroupName, QString newGroupName); - - bool changeColorSetEntry(KoColorSetEntry entry, QString groupName, quint32 index); - /** - * @brief nColorsGroup - * @param groupName string name of the group, when not specified, returns unsorted colors. - * @return the amount of colors in this group. + * @brief getGroup + * @param name + * @return the group with the name given; global group if no parameter is given + * null pointer if not found. */ - quint32 nColorsGroup(QString groupName = QString()); - /** - * @brief nColors - * @return total colors in palette. - */ - quint32 nColors(); + KisSwatchGroup *getGroup(const QString &name); + KisSwatchGroup *getGlobalGroup(); + + bool changeGroupName(const QString &oldGroupName, const QString &newGroupName); + /** * @brief addGroup @@ -159,7 +185,7 @@ * @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()); + bool moveGroup(const QString &groupName, const QString &groupNameInsertBefore = GLOBAL_GROUP_NAME); /** * @brief removeGroup * Remove a group from the KoColorSet @@ -180,40 +206,11 @@ * 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 closestColorName - * convenience function to get the name of the closest match. - * @param color - * @param useGivenColorSpace - * @return - */ - QString closestColorName(KoColor color, bool useGivenColorSpace = true); + KisSwatchGroup::SwatchInfo getClosestColorInfo(KoColor compare, bool useGivenColorSpace = true); private: - - - 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(); - - - - struct Private; + class Private; const QScopedPointer d; }; #endif // KOCOLORSET - diff --git a/libs/pigment/resources/KoColorSet.cpp b/libs/pigment/resources/KoColorSet.cpp --- a/libs/pigment/resources/KoColorSet.cpp +++ b/libs/pigment/resources/KoColorSet.cpp @@ -20,112 +20,93 @@ #include #include -#include // qFromLittleEndian -#include -#include -#include #include -#include #include #include +#include +#include +#include +#include +#include #include -#include #include #include #include -#include -#include -#include -#include +#include #include +#include +#include +#include +#include +#include +#include // qFromLittleEndian #include #include #include -#include "KoColor.h" -#include "KoColorSetEntry.h" -#include "KoColorProfile.h" -#include "KoColorSpaceRegistry.h" -#include "KoColorModelStandardIds.h" - - -struct KoColorSet::Private { - KoColorSet::PaletteType paletteType; - QByteArray data; - QString comment; - qint32 columns {0}; // Set the default value that the GIMP uses... - QVector colors; //ungrouped colors - QStringList groupNames; //names of the groups, this is used to determine the order they are in. - QMap> groups; //grouped colors. -}; - -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; -} +#include +#include +#include +#include +#include +#include +#include "KisSwatch.h" + +#include "KoColorSet.h" +#include "KoColorSet_p.h" + +const QString KoColorSet::GLOBAL_GROUP_NAME = QString(); +const QString KoColorSet::KPL_VERSION_ATTR = "version"; +const QString KoColorSet::KPL_GROUP_ROW_COUNT_ATTR = "rows"; +const QString KoColorSet::KPL_PALETTE_COLUMN_COUNT_ATTR = "columns"; +const QString KoColorSet::KPL_PALETTE_NAME_ATTR = "name"; +const QString KoColorSet::KPL_PALETTE_COMMENT_ATTR = "comment"; +const QString KoColorSet::KPL_PALETTE_FILENAME_ATTR = "filename"; +const QString KoColorSet::KPL_PALETTE_READONLY_ATTR = "readonly"; +const QString KoColorSet::KPL_COLOR_MODEL_ID_ATTR = "colorModelId"; +const QString KoColorSet::KPL_COLOR_DEPTH_ID_ATTR = "colorDepthId"; +const QString KoColorSet::KPL_GROUP_NAME_ATTR = "name"; +const QString KoColorSet::KPL_SWATCH_ROW_ATTR = "row"; +const QString KoColorSet::KPL_SWATCH_COL_ATTR = "column"; +const QString KoColorSet::KPL_SWATCH_NAME_ATTR = "name"; +const QString KoColorSet::KPL_SWATCH_ID_ATTR = "id"; +const QString KoColorSet::KPL_SWATCH_SPOT_ATTR = "spot"; +const QString KoColorSet::KPL_SWATCH_BITDEPTH_ATTR = "bitdepth"; +const QString KoColorSet::KPL_PALETTE_PROFILE_TAG = "Profile"; +const QString KoColorSet::KPL_SWATCH_POS_TAG = "Position"; +const QString KoColorSet::KPL_SWATCH_TAG = "ColorSetEntry"; +const QString KoColorSet::KPL_GROUP_TAG = "Group"; +const QString KoColorSet::KPL_PALETTE_TAG = "ColorSet"; KoColorSet::KoColorSet(const QString& filename) : KoResource(filename) - , d(new Private()) + , d(new Private(this)) { -} - -KoColorSet::KoColorSet() - : KoResource(QString()) - , d(new Private()) -{ - + if (!filename.isEmpty()) { + QFileInfo f(filename); + setIsEditable(f.isWritable()); + } } /// Create an copied palette KoColorSet::KoColorSet(const KoColorSet& rhs) - : QObject(0) - , KoResource(QString()) - , d(new Private()) + : QObject(Q_NULLPTR) + , KoResource(rhs) + , d(new Private(this)) { - setFilename(rhs.filename()); + d->paletteType = rhs.d->paletteType; + d->data = rhs.d->data; d->comment = rhs.d->comment; - d->columns = rhs.d->columns; - d->colors = rhs.d->colors; d->groupNames = rhs.d->groupNames; d->groups = rhs.d->groups; - setValid(true); + d->isGlobal = rhs.d->isGlobal; + d->isEditable = rhs.d->isEditable; } KoColorSet::~KoColorSet() -{ -} +{ } bool KoColorSet::load() { @@ -135,8 +116,11 @@ warnPigment << "Can't open file " << filename(); return false; } - bool res = loadFromDevice(&file); + bool res = loadFromDevice(&file); file.close(); + if (!QFileInfo(filename()).isWritable()) { + setIsEditable(false); + } return res; } @@ -148,19 +132,24 @@ Q_ASSERT(d->data.size() != 0); - return init(); + return d->init(); } bool KoColorSet::save() { - QFile file(filename()); - if (!file.open(QIODevice::WriteOnly | QIODevice::Truncate)) { - return false; + if (d->isGlobal) { + // save to resource dir + QFile file(filename()); + if (!file.open(QIODevice::WriteOnly | QIODevice::Truncate)) { + return false; + } + saveToDevice(&file); + file.close(); + return true; + } else { + return true; // palette is not global, but still indicate that it's saved } - saveToDevice(&file); - file.close(); - return true; } bool KoColorSet::saveToDevice(QIODevice *dev) const @@ -168,10 +157,10 @@ bool res; switch(d->paletteType) { case GPL: - res = saveGpl(dev); + res = d->saveGpl(dev); break; default: - res = saveKpl(dev); + res = d->saveKpl(dev); } if (res) { KoResource::saveToDevice(dev); @@ -179,341 +168,132 @@ return res; } -bool KoColorSet::init() +QByteArray KoColorSet::toByteArray() const { - d->colors.clear(); // just in case this is a reload (eg by KoEditColorSetDialog), - d->groups.clear(); - d->groupNames.clear(); - - if (filename().isNull()) { - warnPigment << "Cannot load palette" << name() << "there is no filename set"; - return false; + QBuffer s; + s.open(QIODevice::WriteOnly); + if (!saveToDevice(&s)) { + warnPigment << "saving palette failed:" << name(); + return QByteArray(); } - if (d->data.isNull()) { - QFile file(filename()); - if (file.size() == 0) { - warnPigment << "Cannot load palette" << name() << "there is no data available"; - return false; - } - file.open(QIODevice::ReadOnly); - d->data = file.readAll(); - file.close(); - } - - bool res = false; - d->paletteType = detectFormat(filename(), d->data); - switch(d->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; - } - setValid(res); - - if (d->columns == 0) { - d->columns = 10; - } - - QImage img(d->columns * 4, (d->colors.size() / d->columns) * 4, QImage::Format_ARGB32); - QPainter gc(&img); - gc.fillRect(img.rect(), Qt::darkGray); - int counter = 0; - for(int i = 0; i < d->columns; ++i) { - for (int j = 0; j < (d->colors.size() / d->columns); ++j) { - if (counter < d->colors.size()) { - QColor c = d->colors.at(counter).color().toQColor(); - gc.fillRect(i * 4, j * 4, 4, 4, c); - counter++; - } - else { - break; - } - } - } - setImage(img); - - // save some memory - d->data.clear(); + s.close(); + s.open(QIODevice::ReadOnly); + QByteArray res = s.readAll(); + s.close(); return res; } -bool KoColorSet::saveGpl(QIODevice *dev) const +bool KoColorSet::fromByteArray(QByteArray &data) { - QTextStream stream(dev); - stream << "GIMP Palette\nName: " << name() << "\nColumns: " << d->columns << "\n#\n"; - - for (int i = 0; i < d->colors.size(); i++) { - const KoColorSetEntry& entry = d->colors.at(i); - 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; + QBuffer buf(&data); + buf.open(QIODevice::ReadOnly); + return loadFromDevice(&buf); } -quint32 KoColorSet::nColors() +KoColorSet::PaletteType KoColorSet::paletteType() const { - if (d->colors.isEmpty()) return 0; - - quint32 total = d->colors.size(); - if (!d->groups.empty()) { - Q_FOREACH (const QVector &group, d->groups.values()) { - total += group.size(); - } - } - return total; + return d->paletteType; } -quint32 KoColorSet::nColorsGroup(QString groupName) { - if (d->groups.contains(groupName)) { - return d->groups.value(groupName).size(); - } else if (groupName.isEmpty() && !d->colors.isEmpty()){ - return d->colors.size(); - } else { - return 0; - } -} - -quint32 KoColorSet::getIndexClosestColor(const KoColor color, bool useGivenColorSpace) +void KoColorSet::setPaletteType(PaletteType paletteType) { - quint32 closestIndex = 0; - quint8 highestPercentage = 0; - quint8 testPercentage = 0; - KoColor compare = color; - for (quint32 i=0; idifference(compare.data(), entry.data())); - if (testPercentage>highestPercentage) - { - closestIndex = i; - highestPercentage = testPercentage; - } - } - return closestIndex; + d->paletteType = paletteType; } -QString KoColorSet::closestColorName(const KoColor color, bool useGivenColorSpace) -{ - int i = getIndexClosestColor(color, useGivenColorSpace); - return getColorGlobal(i).name(); -} -void KoColorSet::add(const KoColorSetEntry & c, QString groupName) +quint32 KoColorSet::colorCount() const { - if (d->groups.contains(groupName) || d->groupNames.contains(groupName)) { - d->groups[groupName].push_back(c); - } else { - d->colors.push_back(c); - } + return d->groups[GLOBAL_GROUP_NAME].colorCount(); } -quint32 KoColorSet::insertBefore(const KoColorSetEntry &c, qint32 index, const QString &groupName) +void KoColorSet::add(const KisSwatch &c, 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; + KisSwatchGroup &modifiedGroup = d->groups.contains(groupName) + ? d->groups[groupName] : d->global(); + modifiedGroup.addEntry(c); } -void KoColorSet::removeAt(quint32 index, QString groupName) +void KoColorSet::setEntry(const KisSwatch &e, int x, int y, const QString &groupName) { - if (d->groups.contains(groupName)){ - if ((quint32)d->groups.value(groupName).size()>index) { - d->groups[groupName].remove(index); - } - } else { - if ((quint32)d->colors.size()>index) { - d->colors.remove(index); - } - } + KisSwatchGroup &modifiedGroup = d->groups.contains(groupName) + ? d->groups[groupName] : d->global(); + modifiedGroup.setEntry(e, x, y); } void KoColorSet::clear() { - d->colors.clear(); d->groups.clear(); + d->groupNames.clear(); + d->groups[GLOBAL_GROUP_NAME] = KisSwatchGroup(); + d->groupNames.append(GLOBAL_GROUP_NAME); } -KoColorSetEntry KoColorSet::getColorGlobal(quint32 index) -{ - KoColorSetEntry e; - quint32 groupIndex = index; - QString groupName = findGroupByGlobalIndex(index, &groupIndex); - e = getColorGroup(groupIndex, groupName); - return e; -} - -KoColorSetEntry KoColorSet::getColorGroup(quint32 index, QString groupName) +KisSwatch KoColorSet::getColorGlobal(quint32 x, quint32 y) const { - KoColorSetEntry e; - if (d->groups.contains(groupName)) { - if (nColorsGroup(groupName)>index) { - e = d->groups.value(groupName).at(index); - } else { - warnPigment<index) { - e = d->colors.at(index); + int yInGroup = y; + QString nameGroupFoundIn; + for (const QString &groupName : d->groupNames) { + if (yInGroup < d->groups[groupName].rowCount()) { + nameGroupFoundIn = groupName; + break; } else { - warnPigment<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; - } - } - + yInGroup -= d->groups[groupName].rowCount(); } } - return groupName; + const KisSwatchGroup &groupFoundIn = nameGroupFoundIn == GLOBAL_GROUP_NAME + ? d->global() : d->groups[nameGroupFoundIn]; + Q_ASSERT(groupFoundIn.checkEntry(x, yInGroup)); + return groupFoundIn.getEntry(x, yInGroup); } -QString KoColorSet::findGroupByColorName(const QString &name, quint32 *index) +KisSwatch KoColorSet::getColorGroup(quint32 x, quint32 y, QString groupName) { - *index = 0; - QString groupName = QString(); - for (int i = 0; icolors.size(); i++) { - if(d->colors.at(i).name() == name) { - *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).name() == name) { - *index = (quint32)i; - groupName = name; - 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; - } - } + KisSwatch e; + const KisSwatchGroup &sourceGroup = groupName == QString() + ? d->global() : d->groups[groupName]; + if (sourceGroup.checkEntry(x, y)) { + e = sourceGroup.getEntry(x, y); } - return groupName; + return e; } QStringList KoColorSet::getGroupNames() { - if (d->groupNames.size()groups.size()) { + if (d->groupNames.size() != d->groups.size()) { warnPigment << "mismatch between groups and the groupnames list."; return QStringList(d->groups.keys()); } return d->groupNames; } -bool KoColorSet::changeGroupName(QString oldGroupName, QString newGroupName) +bool KoColorSet::changeGroupName(const QString &oldGroupName, const QString &newGroupName) { - if (d->groupNames.contains(oldGroupName)==false) { + if (!d->groups.contains(oldGroupName)) { return false; } - QVector dummyList = d->groups.value(oldGroupName); + if (oldGroupName == newGroupName) { + return true; + } + d->groups[newGroupName] = d->groups[oldGroupName]; d->groups.remove(oldGroupName); - d->groups[newGroupName] = dummyList; + d->groups[newGroupName].setName(newGroupName); //rename the string in the stringlist; int index = d->groupNames.indexOf(oldGroupName); d->groupNames.replace(index, newGroupName); return true; } -bool KoColorSet::changeColorSetEntry(KoColorSetEntry entry, QString groupName, quint32 index) -{ - if (index>=nColorsGroup(groupName) || (d->groupNames.contains(groupName)==false && groupName.size()>0)) { - return false; - } - - if (groupName==QString()) { - d->colors[index] = entry; - } else { - d->groups[groupName][index] = entry; - } - return true; -} - void KoColorSet::setColumnCount(int columns) { - d->columns = columns; + d->groups[GLOBAL_GROUP_NAME].setColumnCount(columns); + for (KisSwatchGroup &g : d->groups.values()) { + g.setColumnCount(columns); + } } -int KoColorSet::columnCount() +int KoColorSet::columnCount() const { - return d->columns; + return d->groups[GLOBAL_GROUP_NAME].columnCount(); } QString KoColorSet::comment() @@ -532,7 +312,8 @@ return false; } d->groupNames.append(groupName); - d->groups[groupName] = QVector(); + d->groups[groupName] = KisSwatchGroup(); + d->groups[groupName].setName(groupName); return true; } @@ -541,12 +322,11 @@ 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); + if (groupNameInsertBefore != GLOBAL_GROUP_NAME && groupName != GLOBAL_GROUP_NAME) { + d->groupNames.removeAt(d->groupNames.indexOf(groupName)); + int index = d->groupNames.indexOf(groupNameInsertBefore); + d->groupNames.insert(index, groupName); } - d->groupNames.insert(index, groupName); return true; } @@ -555,17 +335,22 @@ if (!d->groups.contains(groupName)) { return false; } - if (keepColors) { - for (int i = 0; igroups.value(groupName).size(); i++) { - d->colors.append(d->groups.value(groupName).at(i)); - } + + if (groupName == GLOBAL_GROUP_NAME) { + return false; } - for(int n = 0; ngroupNames.size(); n++) { - if (d->groupNames.at(n) == groupName) { - d->groupNames.removeAt(n); + + if (keepColors) { + // put all colors directly below global + int startingRow = d->groups[GLOBAL_GROUP_NAME].rowCount(); + for (const KisSwatchGroup::SwatchInfo &info : d->groups[groupName].infoList()) { + d->groups[GLOBAL_GROUP_NAME].setEntry(info.swatch, + info.column, + info.row + startingRow); } } + d->groupNames.removeAt(d->groupNames.indexOf(groupName)); d->groups.remove(groupName); return true; } @@ -576,179 +361,124 @@ } -bool KoColorSet::loadGpl() +int KoColorSet::rowCount() const { - QString s = QString::fromUtf8(d->data.data(), d->data.count()); - - if (s.isEmpty() || s.isNull() || s.length() < 50) { - warnPigment << "Illegal Gimp palette file: " << filename(); - return false; + int res = 0; + for (const QString &name : d->groupNames) { + res += d->groups[name].rowCount(); } - - quint32 index = 0; - - QStringList lines = s.split('\n', QString::SkipEmptyParts); - - if (lines.size() < 3) { - warnPigment << "Not enough lines in palette file: " << filename(); - return false; - } - - QString columns; - qint32 r, g, b; - KoColorSetEntry e; - - // Read name - - - if (!lines[0].startsWith("GIMP") || !lines[1].toLower().contains("name")) { - warnPigment << "Illegal Gimp palette file: " << filename(); - return false; - } - - setName(i18n(lines[1].split(":")[1].trimmed().toLatin1())); - - index = 2; - - // Read columns - if (lines[index].toLower().contains("columns")) { - columns = lines[index].split(":")[1].trimmed(); - d->columns = columns.toInt(); - index = 3; - } - - - for (qint32 i = index; i < lines.size(); i++) { - - if (lines[i].startsWith('#')) { - d->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 = a[0].toInt(); - a.pop_front(); - g = a[0].toInt(); - a.pop_front(); - b = a[0].toInt(); - a.pop_front(); - - r = qBound(0, r, 255); - g = qBound(0, g, 255); - b = qBound(0, b, 255); - - e.setColor(KoColor(QColor(r, g, b), KoColorSpaceRegistry::instance()->rgb8())); - - QString name = a.join(" "); - e.setName(name.isEmpty() ? i18n("Untitled") : name); - - add(e); - } - } - return true; + return res; } -bool KoColorSet::loadAct() +KisSwatchGroup *KoColorSet::getGroup(const QString &name) { - QFileInfo info(filename()); - setName(info.baseName()); - KoColorSetEntry e; - for (int i = 0; i < d->data.size(); i += 3) { - quint8 r = d->data[i]; - quint8 g = d->data[i+1]; - quint8 b = d->data[i+2]; - e.setColor(KoColor(QColor(r, g, b), KoColorSpaceRegistry::instance()->rgb8())); - add(e); + if (!d->groups.contains(name)) { + return Q_NULLPTR; } - return true; + return &(d->groups[name]); } -struct RiffHeader { - quint32 riff; - quint32 size; - quint32 signature; - quint32 data; - quint32 datasize; - quint16 version; - quint16 colorcount; -}; - - -bool KoColorSet::loadRiff() +KisSwatchGroup *KoColorSet::getGlobalGroup() { - // http://worms2d.info/Palette_file - QFileInfo info(filename()); - setName(info.baseName()); - KoColorSetEntry e; + return getGroup(GLOBAL_GROUP_NAME); +} - RiffHeader header; - memcpy(&header, d->data.constData(), sizeof(RiffHeader)); - header.colorcount = qFromBigEndian(header.colorcount); +bool KoColorSet::isGlobal() const +{ + return d->isGlobal; +} - for (int i = sizeof(RiffHeader); - (i < (int)(sizeof(RiffHeader) + header.colorcount) && i < d->data.size()); - i += 4) { - quint8 r = d->data[i]; - quint8 g = d->data[i+1]; - quint8 b = d->data[i+2]; - e.setColor(KoColor(QColor(r, g, b), KoColorSpaceRegistry::instance()->rgb8())); - add(e); - } - return true; +void KoColorSet::setIsGlobal(bool isGlobal) +{ + d->isGlobal = isGlobal; } +bool KoColorSet::isEditable() const +{ + return d->isEditable; +} -bool KoColorSet::loadPsp() +void KoColorSet::setIsEditable(bool isEditable) { - QFileInfo info(filename()); - setName(info.baseName()); - KoColorSetEntry e; - qint32 r, g, b; + d->isEditable = isEditable; +} - QString s = QString::fromUtf8(d->data.data(), d->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; +KisSwatchGroup::SwatchInfo KoColorSet::getClosestColorInfo(KoColor compare, bool useGivenColorSpace) +{ + KisSwatchGroup::SwatchInfo res; - int entries = l[2].toInt(); + quint8 highestPercentage = 0; + quint8 testPercentage = 0; - for (int i = 0; i < entries; ++i) { + for (const QString &groupName : getGroupNames()) { + KisSwatchGroup *group = getGroup(groupName); + for (const KisSwatchGroup::SwatchInfo &currInfo : group->infoList()) { + KoColor color = currInfo.swatch.color(); + if (useGivenColorSpace == true && compare.colorSpace() != color.colorSpace()) { + color.convertTo(compare.colorSpace()); - QStringList a = l[i + 3].replace('\t', ' ').split(' ', QString::SkipEmptyParts); - - if (a.count() != 3) { - continue; + } else if (compare.colorSpace() != color.colorSpace()) { + compare.convertTo(color.colorSpace()); + } + testPercentage = (255 - compare.colorSpace()->difference(compare.data(), color.data())); + if (testPercentage > highestPercentage) + { + highestPercentage = testPercentage; + res = currInfo; + } } + } + return res; +} - r = a[0].toInt(); - a.pop_front(); - g = a[0].toInt(); - a.pop_front(); - b = a[0].toInt(); - a.pop_front(); - - r = qBound(0, r, 255); - g = qBound(0, g, 255); - b = qBound(0, b, 255); +/********************************KoColorSet::Private**************************/ - e.setColor(KoColor(QColor(r, g, b), - KoColorSpaceRegistry::instance()->rgb8())); +KoColorSet::Private::Private(KoColorSet *a_colorSet) + : colorSet(a_colorSet) + , isGlobal(true) + , isEditable(false) +{ + groups[KoColorSet::GLOBAL_GROUP_NAME] = KisSwatchGroup(); + groupNames.append(KoColorSet::GLOBAL_GROUP_NAME); +} - QString name = a.join(" "); - e.setName(name.isEmpty() ? i18n("Untitled") : name); +KoColorSet::PaletteType KoColorSet::Private::detectFormat(const QString &fileName, const QByteArray &ba) +{ + QFileInfo fi(fileName); - add(e); + // .pal + if (ba.startsWith("RIFF") && ba.indexOf("PAL data", 8)) { + return KoColorSet::RIFF_PAL; } - return true; + // .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) +void KoColorSet::Private::scribusParseColor(KoColorSet *set, QXmlStreamReader *xml) { - KoColorSetEntry colorEntry; + KisSwatch colorEntry; // It's a color, retrieve it QXmlStreamAttributes colorProperties = xml->attributes(); @@ -817,182 +547,321 @@ quint8 y = cmyk >> 8 & 0xff; quint8 k = cmyk & 0xff; - dbgPigment << "Color parsed: "<< c << m << y << k; + dbgPigment << "Color parsed: "<< c << m << y << k; + + currentColor.data()[0] = c; + currentColor.data()[1] = m; + currentColor.data()[2] = y; + currentColor.data()[3] = k; + currentColor.setOpacity(OPACITY_OPAQUE_U8); + colorEntry.setColor(currentColor); + + set->add(colorEntry); + + while(xml->readNextStartElement()) { + //ignore - these are all unknown or the /> element tag + xml->skipCurrentElement(); + } + return; + } + } + else { + xml->raiseError("Unknown color space for color " + colorEntry.name()); + } +} + +bool KoColorSet::Private::loadScribusXmlPalette(KoColorSet *set, QXmlStreamReader *xml) +{ + + //1. Get name + QXmlStreamAttributes paletteProperties = xml->attributes(); + QStringRef paletteName = paletteProperties.value("Name"); + dbgPigment << "Processed name of palette:" << paletteName; + set->setName(paletteName.toString()); + + //2. Inside the SCRIBUSCOLORS, there are lots of colors. Retrieve them + + while(xml->readNextStartElement()) { + QStringRef currentElement = xml->name(); + if(QStringRef::compare(currentElement, "COLOR", Qt::CaseInsensitive) == 0) { + scribusParseColor(set, xml); + } + else { + xml->skipCurrentElement(); + } + } + + if(xml->hasError()) { + return false; + } + + return true; +} + +quint16 KoColorSet::Private::readShort(QIODevice *io) { + quint16 val; + quint64 read = io->read((char*)&val, 2); + if (read != 2) return false; + return qFromBigEndian(val); +} + +bool KoColorSet::Private::init() +{ + // just in case this is a reload (eg by KoEditColorSetDialog), + groupNames.clear(); + groups.clear(); + groupNames.append(KoColorSet::GLOBAL_GROUP_NAME); + groups[KoColorSet::GLOBAL_GROUP_NAME] = KisSwatchGroup(); + + if (colorSet->filename().isNull()) { + warnPigment << "Cannot load palette" << colorSet->name() << "there is no filename set"; + return false; + } + if (data.isNull()) { + QFile file(colorSet->filename()); + if (file.size() == 0) { + warnPigment << "Cannot load palette" << colorSet->name() << "there is no data available"; + return false; + } + file.open(QIODevice::ReadOnly); + data = file.readAll(); + file.close(); + } + + bool res = false; + paletteType = detectFormat(colorSet->filename(), data); + switch(paletteType) { + case GPL: + res = loadGpl(); + break; + case ACT: + res = loadAct(); + break; + case RIFF_PAL: + res = loadRiff(); + break; + case PSP_PAL: + res = loadPsp(); + break; + case ACO: + res = loadAco(); + break; + case XML: + res = loadXml(); + break; + case KPL: + res = loadKpl(); + break; + case SBZ: + res = loadSbz(); + break; + default: + res = false; + } + colorSet->setValid(res); + + QImage img(global().columnCount() * 4, global().rowCount() * 4, QImage::Format_ARGB32); + QPainter gc(&img); + gc.fillRect(img.rect(), Qt::darkGray); + for (const KisSwatchGroup::SwatchInfo &info : global().infoList()) { + QColor c = info.swatch.color().toQColor(); + gc.fillRect(info.column * 4, info.row * 4, 4, 4, c); + } + colorSet->setImage(img); + colorSet->setValid(res); - currentColor.data()[0] = c; - currentColor.data()[1] = m; - currentColor.data()[2] = y; - currentColor.data()[3] = k; - currentColor.setOpacity(OPACITY_OPAQUE_U8); - colorEntry.setColor(currentColor); + data.clear(); + return res; +} - set->add(colorEntry); +bool KoColorSet::Private::saveGpl(QIODevice *dev) const +{ + Q_ASSERT(dev->isOpen()); + Q_ASSERT(dev->isWritable()); - while(xml->readNextStartElement()) { - //ignore - these are all unknown or the /> element tag - xml->skipCurrentElement(); + QTextStream stream(dev); + stream << "GIMP Palette\nName: " << colorSet->name() << "\nColumns: " << colorSet->columnCount() << "\n#\n"; + + /* + * Qt doesn't provide an interface to get a const reference to a QHash, that is + * the underlying data structure of groups. Therefore, directly use + * groups[KoColorSet::GLOBAL_GROUP_NAME] so that saveGpl can stay const + */ + + for (int y = 0; y < groups[KoColorSet::GLOBAL_GROUP_NAME].rowCount(); y++) { + for (int x = 0; x < colorSet->columnCount(); x++) { + if (!groups[KoColorSet::GLOBAL_GROUP_NAME].checkEntry(x, y)) { + continue; } - return; + const KisSwatch& entry = groups[KoColorSet::GLOBAL_GROUP_NAME].getEntry(x, y); + QColor c = entry.color().toQColor(); + stream << c.red() << " " << c.green() << " " << c.blue() << "\t"; + if (entry.name().isEmpty()) + stream << "Untitled\n"; + else + stream << entry.name() << "\n"; } } - else { - xml->raiseError("Unknown color space for color " + colorEntry.name()); - } + + return true; } -bool loadScribusXmlPalette(KoColorSet *set, QXmlStreamReader *xml) +bool KoColorSet::Private::loadGpl() { + QString s = QString::fromUtf8(data.data(), data.count()); - //1. Get name - QXmlStreamAttributes paletteProperties = xml->attributes(); - QStringRef paletteName = paletteProperties.value("Name"); - dbgPigment << "Processed name of palette:" << paletteName; - set->setName(paletteName.toString()); + if (s.isEmpty() || s.isNull() || s.length() < 50) { + warnPigment << "Illegal Gimp palette file: " << colorSet->filename(); + return false; + } - //2. Inside the SCRIBUSCOLORS, there are lots of colors. Retrieve them + quint32 index = 0; - while(xml->readNextStartElement()) { - QStringRef currentElement = xml->name(); - if(QStringRef::compare(currentElement, "COLOR", Qt::CaseInsensitive) == 0) { - scribusParseColor(set, xml); - } - else { - xml->skipCurrentElement(); - } + QStringList lines = s.split('\n', QString::SkipEmptyParts); + + if (lines.size() < 3) { + warnPigment << "Not enough lines in palette file: " << colorSet->filename(); + return false; } - if(xml->hasError()) { + 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; } - return true; -} + colorSet->setName(i18n(lines[1].split(":")[1].trimmed().toLatin1())); -bool KoColorSet::loadXml() { - bool res = false; + index = 2; - QXmlStreamReader *xml = new QXmlStreamReader(d->data); + // Read columns + int columns = 0; + if (lines[index].toLower().contains("columns")) { + columnsText = lines[index].split(":")[1].trimmed(); + columns = columnsText.toInt(); + global().setColumnCount(columns); + index = 3; + } - if (xml->readNextStartElement()) { - QStringRef paletteId = xml->name(); - if (QStringRef::compare(paletteId, "SCRIBUSCOLORS", Qt::CaseInsensitive) == 0) { // Scribus - dbgPigment << "XML palette: " << filename() << ", Scribus format"; - res = loadScribusXmlPalette(this, xml); - } - else { - // Unknown XML format - xml->raiseError("Unknown XML palette format. Expected SCRIBUSCOLORS, found " + paletteId); + + for (qint32 i = index; i < lines.size(); i++) { + if (lines[i].startsWith('#')) { + comment += lines[i].mid(1).trimmed() + ' '; + } else if (!lines[i].isEmpty()) { + QStringList a = lines[i].replace('\t', ' ').split(' ', QString::SkipEmptyParts); + + if (a.count() < 3) { + break; + } + + r = qBound(0, a[0].toInt(), 255); + g = qBound(0, a[1].toInt(), 255); + b = qBound(0, a[2].toInt(), 255); + + e.setColor(KoColor(QColor(r, g, b), KoColorSpaceRegistry::instance()->rgb8())); + + for (int i = 0; i != 3; i++) { + a.pop_front(); + } + QString name = a.join(" "); + e.setName(name.isEmpty() ? i18n("Untitled") : name); + + global().addEntry(e); } } + return true; +} - // If there is any error (it should be returned through the stream) - if (xml->hasError() || !res) { - warnPigment << "Illegal XML palette:" << filename(); - warnPigment << "Error (line"<< xml->lineNumber() << ", column" << xml->columnNumber() << "):" << xml->errorString(); - return false; +bool KoColorSet::Private::loadAct() +{ + QFileInfo info(colorSet->filename()); + colorSet->setName(info.baseName()); + KisSwatch e; + for (int i = 0; i < data.size(); i += 3) { + quint8 r = data[i]; + quint8 g = data[i+1]; + quint8 b = data[i+2]; + e.setColor(KoColor(QColor(r, g, b), KoColorSpaceRegistry::instance()->rgb8())); + global().addEntry(e); } - else { - dbgPigment << "XML palette parsed successfully:" << filename(); - return true; + return true; +} + +bool KoColorSet::Private::loadRiff() +{ + // http://worms2d.info/Palette_file + QFileInfo info(colorSet->filename()); + colorSet->setName(info.baseName()); + KisSwatch e; + + RiffHeader header; + memcpy(&header, data.constData(), sizeof(RiffHeader)); + header.colorcount = qFromBigEndian(header.colorcount); + + for (int i = sizeof(RiffHeader); + (i < (int)(sizeof(RiffHeader) + header.colorcount) && i < data.size()); + i += 4) { + quint8 r = data[i]; + quint8 g = data[i+1]; + quint8 b = data[i+2]; + e.setColor(KoColor(QColor(r, g, b), KoColorSpaceRegistry::instance()->rgb8())); + groups[KoColorSet::GLOBAL_GROUP_NAME].addEntry(e); } + return true; } -bool KoColorSet::saveKpl(QIODevice *dev) const + +bool KoColorSet::Private::loadPsp() { - QScopedPointer store(KoStore::createStore(dev, KoStore::Write, "application/x-krita-palette", KoStore::Zip)); - if (!store || store->bad()) return false; + QFileInfo info(colorSet->filename()); + colorSet->setName(info.baseName()); + KisSwatch e; + qint32 r, g, b; - QSet profiles; - QMap profileMap; + 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; - { - QDomDocument doc; - QDomElement root = doc.createElement("Colorset"); - root.setAttribute("version", "1.0"); - root.setAttribute("name", name()); - root.setAttribute("comment", d->comment); - root.setAttribute("columns", d->columns); - Q_FOREACH(const KoColorSetEntry &entry, d->colors) { - - // 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, d->groupNames) { - QDomElement gl = doc.createElement("Group"); - gl.setAttribute("name", groupName); - root.appendChild(gl); - Q_FOREACH(const KoColorSetEntry &entry, d->groups.value(groupName)) { + int entries = l[2].toInt(); - // 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); - } + for (int i = 0; i < entries; ++i) { + + QStringList a = l[i + 3].replace('\t', ' ').split(' ', QString::SkipEmptyParts); + + if (a.count() != 3) { + continue; } - 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; } - } + r = qBound(0, a[0].toInt(), 255); + g = qBound(0, a[1].toInt(), 255); + b = qBound(0, a[2].toInt(), 255); - QDomDocument doc; - QDomElement profileElement = doc.createElement("Profiles"); + e.setColor(KoColor(QColor(r, g, b), + KoColorSpaceRegistry::instance()->rgb8())); - 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); + QString name = a.join(" "); + e.setName(name.isEmpty() ? i18n("Untitled") : name); + groups[KoColorSet::GLOBAL_GROUP_NAME].addEntry(e); } - 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(); + return true; } -bool KoColorSet::loadKpl() +bool KoColorSet::Private::loadKpl() { - QBuffer buf(&d->data); + 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; + QScopedPointer store(KoStore::createStore(&buf, KoStore::Read, "krita/x-colorset", 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()); @@ -1002,13 +871,12 @@ QDomDocument doc; doc.setContent(ba); QDomElement e = doc.documentElement(); - QDomElement c = e.firstChildElement("Profiles"); + QDomElement c = e.firstChildElement(KPL_PALETTE_PROFILE_TAG); while (!c.isNull()) { - - QString name = c.attribute("name"); - QString filename = c.attribute("filename"); - QString colorModelId = c.attribute("colorModelId"); - QString colorDepthId = c.attribute("colorDepthId"); + QString name = c.attribute(KPL_PALETTE_NAME_ATTR); + QString filename = c.attribute(KPL_PALETTE_FILENAME_ATTR); + QString colorModelId = c.attribute(KPL_COLOR_MODEL_ID_ATTR); + QString colorDepthId = c.attribute(KPL_COLOR_DEPTH_ID_ATTR); if (!KoColorSpaceRegistry::instance()->profileByName(name)) { store->open(filename); QByteArray data; @@ -1023,7 +891,6 @@ } c = c.nextSiblingElement(); - } } @@ -1037,72 +904,37 @@ QDomDocument doc; doc.setContent(ba); QDomElement e = doc.documentElement(); - setName(e.attribute("name")); - d->comment = e.attribute("comment"); - d->columns = e.attribute("columns").toInt(); - - QDomElement c = e.firstChildElement("ColorSetEntry"); - while (!c.isNull()) { - QString colorDepthId = c.attribute("bitdepth", Integer8BitsColorDepthID.id()); - KoColorSetEntry entry; + colorSet->setName(e.attribute(KPL_PALETTE_NAME_ATTR)); + colorSet->setColumnCount(e.attribute(KPL_PALETTE_COLUMN_COUNT_ATTR).toInt()); + colorSet->setIsEditable(e.attribute(KPL_PALETTE_READONLY_ATTR) != "true"); + comment = e.attribute(KPL_PALETTE_COMMENT_ATTR); + loadKplGroup(doc, e, colorSet->getGlobalGroup()); - 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); - d->colors << entry; - - c = c.nextSiblingElement("ColorSetEntry"); - - } - QDomElement g = e.firstChildElement("Group"); + QDomElement g = e.firstChildElement(KPL_GROUP_TAG); while (!g.isNull()) { - QString groupName = g.attribute("name"); - addGroup(groupName); - QDomElement cg = g.firstChildElement("ColorSetEntry"); - while (!cg.isNull()) { - QString colorDepthId = cg.attribute("bitdepth", Integer8BitsColorDepthID.id()); - KoColorSetEntry 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); - add(entry, groupName); - - cg = cg.nextSiblingElement("ColorSetEntry"); - - } - g = g.nextSiblingElement("Group"); + QString groupName = g.attribute(KPL_GROUP_NAME_ATTR); + colorSet->addGroup(groupName); + loadKplGroup(doc, g, colorSet->getGroup(groupName)); + g = g.nextSiblingElement(KPL_GROUP_TAG); } - } - buf.close(); return true; } -quint16 readShort(QIODevice *io) { - quint16 val; - quint64 read = io->read((char*)&val, 2); - if (read != 2) return false; - return qFromBigEndian(val); -} - -bool KoColorSet::loadAco() +bool KoColorSet::Private::loadAco() { - QFileInfo info(filename()); - setName(info.baseName()); + QFileInfo info(colorSet->filename()); + colorSet->setName(info.baseName()); - QBuffer buf(&d->data); + QBuffer buf(&data); buf.open(QBuffer::ReadOnly); quint16 version = readShort(&buf); quint16 numColors = readShort(&buf); - KoColorSetEntry e; + KisSwatch e; if (version == 1 && buf.size() > 4+numColors*10) { buf.seek(4+numColors*10); @@ -1161,7 +993,7 @@ e.setColor(c); } else { - warnPigment << "Unsupported colorspace in palette" << filename() << "(" << colorSpace << ")"; + warnPigment << "Unsupported colorspace in palette" << colorSet->filename() << "(" << colorSpace << ")"; skip = true; } if (version == 2) { @@ -1174,21 +1006,21 @@ QTextCodec *Utf16Codec = QTextCodec::codecForName("UTF-16BE"); e.setName(Utf16Codec->toUnicode(ba)); } else { - warnPigment << "Version 2 name block is the wrong size" << filename(); + 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) { - add(e); + groups[KoColorSet::GLOBAL_GROUP_NAME].addEntry(e); } } return true; } -bool KoColorSet::loadSbz() { - QBuffer buf(&d->data); +bool KoColorSet::Private::loadSbz() { + QBuffer buf(&data); buf.open(QBuffer::ReadOnly); // &buf is a subclass of QIODevice @@ -1203,14 +1035,14 @@ QByteArray ba = store->read(store->size()); store->close(); - dbgPigment << "XML palette: " << filename() << ", SwatchBooker format"; + 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:" << filename(); + warnPigment << "Illegal XML palette:" << colorSet->filename(); warnPigment << "Error (line" << errorLine << ", column" << errorColumn << "):" << errorMessage; return false; } @@ -1228,8 +1060,8 @@ QDomElement title = metadata.firstChildElement("dc:title"); QString colorName = title.text(); colorName = colorName.isEmpty() ? i18n("Untitled") : colorName; - setName(colorName); - dbgPigment << "Processed name of palette:" << name(); + colorSet->setName(colorName); + dbgPigment << "Processed name of palette:" << colorSet->name(); // End reading properties // Now read colors... @@ -1260,13 +1092,13 @@ // We'll store colors here, and as we process swatches // we'll add them to the palette - QHash materialsBook; + QHash materialsBook; QHash fileColorSpaces; // Color processing for(; !colorElement.isNull(); colorElement = colorElement.nextSiblingElement("color")) { - KoColorSetEntry currentEntry; + KisSwatch currentEntry; // Set if color is spot currentEntry.setSpotColor(colorElement.attribute("usage") == "spot"); @@ -1530,7 +1362,7 @@ return false; } if (materialsBook.contains(id)) { - add(materialsBook.value(id)); + groups[KoColorSet::GLOBAL_GROUP_NAME].addEntry(materialsBook.value(id)); } else { warnPigment << "Invalid swatch definition (material not found) (line" << swatch.lineNumber() << ", column" << swatch.columnNumber() << ")"; @@ -1559,7 +1391,7 @@ return false; } if (materialsBook.contains(id)) { - add(materialsBook.value(id), currentGroupName); + groups[currentGroupName].addEntry(materialsBook.value(id)); } else { warnPigment << "Invalid swatch definition (material not found) (line" << groupSwatch.lineNumber() << ", column" << groupSwatch.columnNumber() << ")"; @@ -1575,3 +1407,159 @@ 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, "krita/x-colorset", KoStore::Zip)); + if (!store || store->bad()) return false; + + QSet colorSpaces; + + { + QDomDocument doc; + QDomElement root = doc.createElement(KPL_PALETTE_TAG); + root.setAttribute(KPL_VERSION_ATTR, "1.0"); + root.setAttribute(KPL_PALETTE_NAME_ATTR, colorSet->name()); + root.setAttribute(KPL_PALETTE_COMMENT_ATTR, comment); + root.setAttribute(KPL_PALETTE_READONLY_ATTR, + (colorSet->isEditable() || !colorSet->isGlobal()) ? "false" : "true"); + root.setAttribute(KPL_PALETTE_COLUMN_COUNT_ATTR, colorSet->columnCount()); + root.setAttribute(KPL_GROUP_ROW_COUNT_ATTR, groups[KoColorSet::GLOBAL_GROUP_NAME].rowCount()); + + saveKplGroup(doc, root, colorSet->getGroup(KoColorSet::GLOBAL_GROUP_NAME), colorSpaces); + + for (const QString &groupName : groupNames) { + if (groupName == KoColorSet::GLOBAL_GROUP_NAME) { continue; } + QDomElement gl = doc.createElement(KPL_GROUP_TAG); + gl.setAttribute(KPL_GROUP_NAME_ATTR, groupName); + root.appendChild(gl); + saveKplGroup(doc, gl, colorSet->getGroup(groupName), colorSpaces); + } + + doc.appendChild(root); + if (!store->open("colorset.xml")) { return false; } + QByteArray ba = doc.toByteArray(); + if (store->write(ba) != ba.size()) { return false; } + if (!store->close()) { return false; } + } + + QDomDocument doc; + QDomElement profileElement = doc.createElement("Profiles"); + + for (const KoColorSpace *colorSpace : colorSpaces) { + QString fn = QFileInfo(colorSpace->profile()->fileName()).fileName(); + if (!store->open(fn)) { return false; } + QByteArray profileRawData = colorSpace->profile()->rawData(); + if (!store->write(profileRawData)) { return false; } + if (!store->close()) { return false; } + QDomElement el = doc.createElement(KPL_PALETTE_PROFILE_TAG); + el.setAttribute(KPL_PALETTE_FILENAME_ATTR, fn); + el.setAttribute(KPL_PALETTE_NAME_ATTR, colorSpace->profile()->name()); + el.setAttribute(KPL_COLOR_MODEL_ID_ATTR, colorSpace->colorModelId().id()); + el.setAttribute(KPL_COLOR_DEPTH_ID_ATTR, colorSpace->colorDepthId().id()); + profileElement.appendChild(el); + + } + doc.appendChild(profileElement); + if (!store->open("profiles.xml")) { return false; } + QByteArray ba = doc.toByteArray(); + if (store->write(ba) != ba.size()) { return false; } + if (!store->close()) { return false; } + + return store->finalize(); +} + +void KoColorSet::Private::saveKplGroup(QDomDocument &doc, + QDomElement &groupEle, + const KisSwatchGroup *group, + QSet &colorSetSet) const +{ + groupEle.setAttribute(KPL_GROUP_ROW_COUNT_ATTR, QString::number(group->rowCount())); + + for (const SwatchInfoType &info : group->infoList()) { + const KoColorProfile *profile = info.swatch.color().colorSpace()->profile(); + // Only save non-builtin profiles.= + if (!profile->fileName().isEmpty()) { + colorSetSet.insert(info.swatch.color().colorSpace()); + } + QDomElement swatchEle = doc.createElement(KPL_SWATCH_TAG); + swatchEle.setAttribute(KPL_SWATCH_NAME_ATTR, info.swatch.name()); + swatchEle.setAttribute(KPL_SWATCH_ID_ATTR, info.swatch.id()); + swatchEle.setAttribute(KPL_SWATCH_SPOT_ATTR, info.swatch.spotColor() ? "true" : "false"); + swatchEle.setAttribute(KPL_SWATCH_BITDEPTH_ATTR, info.swatch.color().colorSpace()->colorDepthId().id()); + info.swatch.color().toXML(doc, swatchEle); + + QDomElement positionEle = doc.createElement(KPL_SWATCH_POS_TAG); + positionEle.setAttribute(KPL_SWATCH_ROW_ATTR, info.row); + positionEle.setAttribute(KPL_SWATCH_COL_ATTR, info.column); + swatchEle.appendChild(positionEle); + + groupEle.appendChild(swatchEle); + } +} + +void KoColorSet::Private::loadKplGroup(const QDomDocument &doc, const QDomElement &parentEle, KisSwatchGroup *group) +{ + Q_UNUSED(doc); + if (!parentEle.attribute(KPL_GROUP_ROW_COUNT_ATTR).isNull()) { + group->setRowCount(parentEle.attribute(KPL_GROUP_ROW_COUNT_ATTR).toInt()); + } + + for (QDomElement swatchEle = parentEle.firstChildElement(KPL_SWATCH_TAG); + !swatchEle.isNull(); + swatchEle = swatchEle.nextSiblingElement(KPL_SWATCH_TAG)) { + QString colorDepthId = swatchEle.attribute(KPL_SWATCH_BITDEPTH_ATTR, Integer8BitsColorDepthID.id()); + KisSwatch entry; + + entry.setColor(KoColor::fromXML(swatchEle.firstChildElement(), colorDepthId)); + entry.setName(swatchEle.attribute(KPL_SWATCH_NAME_ATTR)); + entry.setId(swatchEle.attribute(KPL_SWATCH_ID_ATTR)); + entry.setSpotColor(swatchEle.attribute(KPL_SWATCH_SPOT_ATTR, "false") == "true" ? true : false); + QDomElement positionEle = swatchEle.firstChildElement(KPL_SWATCH_POS_TAG); + if (!positionEle.isNull()) { + int rowNumber = positionEle.attribute(KPL_SWATCH_ROW_ATTR).toInt(); + int columnNumber = positionEle.attribute(KPL_SWATCH_COL_ATTR).toInt(); + if (columnNumber < 0 || + columnNumber >= colorSet->columnCount() || + rowNumber < 0 + ) { + warnPigment << "Swatch" << entry.name() + << "of palette" << colorSet->name() + << "has invalid position."; + continue; + } + group->setEntry(entry, columnNumber, rowNumber); + } else { + group->addEntry(entry); + } + } +} diff --git a/libs/pigment/resources/KoColorSet_p.h b/libs/pigment/resources/KoColorSet_p.h new file mode 100644 --- /dev/null +++ b/libs/pigment/resources/KoColorSet_p.h @@ -0,0 +1,74 @@ +#ifndef KOCOLORSET_P_H +#define KOCOLORSET_P_H + +#include +#include +#include +#include + +#include +#include + +#include "KoColorSet.h" + +struct RiffHeader { + quint32 riff; + quint32 size; + quint32 signature; + quint32 data; + quint32 datasize; + quint16 version; + quint16 colorcount; +}; + +class KoColorSet::Private +{ +private: + typedef KisSwatchGroup::SwatchInfo SwatchInfoType; + +public: + Private(KoColorSet *a_colorSet); + +public: + KisSwatchGroup &global() { + 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. + bool isGlobal; + bool isEditable; + +private: + KoColorSet::PaletteType detectFormat(const QString &fileName, const QByteArray &ba); + void scribusParseColor(KoColorSet *set, QXmlStreamReader *xml); + bool loadScribusXmlPalette(KoColorSet *set, QXmlStreamReader *xml); + quint16 readShort(QIODevice *io); + + void saveKplGroup(QDomDocument &doc, QDomElement &groupEle, + const KisSwatchGroup *group, QSet &colorSetSet) const; + void loadKplGroup(const QDomDocument &doc, const QDomElement &parentElement, KisSwatchGroup *group); +}; + +#endif // KOCOLORSET_P_H diff --git a/libs/pigment/resources/KoResource.cpp b/libs/pigment/resources/KoResource.cpp --- a/libs/pigment/resources/KoResource.cpp +++ b/libs/pigment/resources/KoResource.cpp @@ -55,9 +55,7 @@ KoResource::KoResource(const KoResource &rhs) : d(new Private(*rhs.d)) -{ - qDebug() << ">>>>>>>>>>>>>>>>>>" << filename() << name() << valid(); -} +{ } bool KoResource::saveToDevice(QIODevice *dev) const { diff --git a/libs/pigment/tests/CMakeLists.txt b/libs/pigment/tests/CMakeLists.txt --- a/libs/pigment/tests/CMakeLists.txt +++ b/libs/pigment/tests/CMakeLists.txt @@ -27,12 +27,12 @@ ecm_add_tests( TestColorConversion.cpp TestKoColorSpaceMaths.cpp + TestKisSwatchGroup.cpp + # TestKoColorSet.cpp NAME_PREFIX "libs-pigment-" LINK_LIBRARIES kritapigment Qt5::Test) - - add_executable(CCSGraph CCSGraph.cpp) target_link_libraries(CCSGraph kritapigment KF5::I18n) ecm_mark_as_test(CCSGraph) diff --git a/libs/pigment/tests/TestKisSwatchGroup.h b/libs/pigment/tests/TestKisSwatchGroup.h new file mode 100644 --- /dev/null +++ b/libs/pigment/tests/TestKisSwatchGroup.h @@ -0,0 +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/pigment/tests/TestKisSwatchGroup.cpp b/libs/pigment/tests/TestKisSwatchGroup.cpp new file mode 100644 --- /dev/null +++ b/libs/pigment/tests/TestKisSwatchGroup.cpp @@ -0,0 +1,103 @@ +#include "TestKisSwatchGroup.h" +#include + +void TestKisSwatchGroup::testAddingOneEntry() +{ + KisSwatch e; + e.setName("first"); + g.setEntry(e, 0, 0); + QVERIFY(g.checkEntry(0, 0)); + QVERIFY(!g.checkEntry(1, 2)); + QVERIFY(!g.checkEntry(10, 5)); + QCOMPARE(g.getEntry(0, 0), e); + QCOMPARE(g.colorCount(), 1); + testSwatches[QPair(0, 0)] = e; +} + +void TestKisSwatchGroup::testAddingMultipleEntries() +{ + KisSwatch e2; + e2.setName("second"); + g.setEntry(e2, 9, 3); + QCOMPARE(g.columnCount(), 16); + QVERIFY(g.checkEntry(9, 3)); + QVERIFY(!g.checkEntry(1, 2)); + QVERIFY(!g.checkEntry(10, 5)); + QVERIFY(g.checkEntry(0, 0)); + QCOMPARE(g.getEntry(0, 0).name(), QString("first")); + KisSwatch e3; + e3.setName("third"); + g.setEntry(e3, 4, 12); + QCOMPARE(g.colorCount(), 3); + QVERIFY(g.checkEntry(9, 3)); + QCOMPARE(g.getEntry(9, 3).name(), QString("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.colorCount(), 3); + QVERIFY(g.checkEntry(0, 0)); + QCOMPARE(g.getEntry(0, 0).name(), QString("fourth")); + testSwatches[QPair(0, 0)] = e4; +} + +void TestKisSwatchGroup::testRemoveEntries() +{ + testSwatches.remove(QPair(9, 3)); + QVERIFY(g.removeEntry(9, 3)); + QCOMPARE(g.colorCount(), testSwatches.size()); + QVERIFY(!g.removeEntry(13, 10)); + QVERIFY(!g.checkEntry(9, 3)); +} + +void TestKisSwatchGroup::testChangeColumnNumber() +{ + g.setColumnCount(20); + QCOMPARE(g.columnCount(), 20); + for (QPair p : testSwatches.keys()) { + QCOMPARE(testSwatches[p], g.getEntry(p.first, p.second)); + } + g.setColumnCount(10); + int keptCount = 0; + for (QPair p : testSwatches.keys()) { + if (p.first < 10) { + keptCount++; + QCOMPARE(testSwatches[p], g.getEntry(p.first, p.second)); + } + } + QCOMPARE(keptCount, g.colorCount()); +} + +void TestKisSwatchGroup::testAddEntry() +{ + KisSwatchGroup g2; + g2.setColumnCount(3); + g2.setRowCount(1); + for (int i = 0; i != 3; i++) { + g2.addEntry(KisSwatch()); + } + QCOMPARE(g2.rowCount(), 1); + QCOMPARE(g2.columnCount(), 3); + QCOMPARE(g2.colorCount(), 3); + g2.addEntry(KisSwatch()); + QCOMPARE(g2.rowCount(), 2); + QCOMPARE(g2.columnCount(), 3); + QCOMPARE(g2.colorCount(), 4); + g2.setRowCount(1); + QCOMPARE(g2.rowCount(), 1); + QCOMPARE(g2.columnCount(), 3); + QCOMPARE(g2.colorCount(), 3); + for (int i = 0; i != 4; i++) { + g2.addEntry(KisSwatch()); + } + QCOMPARE(g2.rowCount(), 3); + QCOMPARE(g2.columnCount(), 3); + QCOMPARE(g2.colorCount(), 7); +} + +QTEST_GUILESS_MAIN(TestKisSwatchGroup) diff --git a/libs/ui/CMakeLists.txt b/libs/ui/CMakeLists.txt --- a/libs/ui/CMakeLists.txt +++ b/libs/ui/CMakeLists.txt @@ -152,6 +152,7 @@ kis_multinode_property.cpp kis_stopgradient_editor.cpp KisWelcomePageWidget.cpp + KisChangePaletteCommand.cpp kisexiv2/kis_exif_io.cpp kisexiv2/kis_exiv2.cpp @@ -256,6 +257,8 @@ widgets/KoStrokeConfigWidget.cpp widgets/KoFillConfigWidget.cpp + KisPaletteEditor.cpp + dialogs/KisDlgPaletteEditor.cpp utils/kis_document_aware_spin_box_unit_manager.cpp @@ -485,6 +488,7 @@ forms/wdgsessionmanager.ui forms/wdgnewwindowlayout.ui forms/KisWelcomePage.ui + forms/WdgDlgPaletteEditor.ui brushhud/kis_dlg_brush_hud_config.ui dialogs/kis_delayed_save_dialog.ui diff --git a/libs/ui/KisChangePaletteCommand.h b/libs/ui/KisChangePaletteCommand.h new file mode 100644 --- /dev/null +++ b/libs/ui/KisChangePaletteCommand.h @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2018 Michael Zhou + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef KISCHANGEPALETTECOMMAND_H +#define KISCHANGEPALETTECOMMAND_H + +#include + +#include +#include + +class KisChangePaletteCommand : public KUndo2Command +{ +public: + static const char MagicString[]; +public: + KisChangePaletteCommand(); + ~KisChangePaletteCommand() override; + void undo() override + { + // palette modification shouldn't be undone; + } + void redo() override + { + // palette modification shouldn't be undone; + } + int id() const override; +}; + +#endif // KISCHANGEPALETTECOMMAND_H diff --git a/libs/ui/KisChangePaletteCommand.cpp b/libs/ui/KisChangePaletteCommand.cpp new file mode 100644 --- /dev/null +++ b/libs/ui/KisChangePaletteCommand.cpp @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2018 Michael Zhou + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include +#include + +#include "KisChangePaletteCommand.h" + +const char KisChangePaletteCommand::MagicString[] = "Edit palette"; + +KisChangePaletteCommand::KisChangePaletteCommand() + : KUndo2Command(kundo2_i18n(MagicString)) +{ + +} + +KisChangePaletteCommand::~KisChangePaletteCommand() +{ } + +int KisChangePaletteCommand::id() const +{ + return KisCommandUtils::ChangePaletteId; +} diff --git a/libs/ui/KisDocument.h b/libs/ui/KisDocument.h --- a/libs/ui/KisDocument.h +++ b/libs/ui/KisDocument.h @@ -30,6 +30,7 @@ #include #include #include +#include #include #include @@ -348,6 +349,9 @@ const KisGuidesConfig& guidesConfig() const; void setGuidesConfig(const KisGuidesConfig &data); + QList &paletteList(); + void setPaletteList(const QList &paletteList); + void clearUndoHistory(); diff --git a/libs/ui/KisDocument.cpp b/libs/ui/KisDocument.cpp --- a/libs/ui/KisDocument.cpp +++ b/libs/ui/KisDocument.cpp @@ -265,6 +265,7 @@ , imageIdleWatcher(2000 /*ms*/) , assistants(rhs.assistants) // WARNING: assistants should not store pointers to the document! , globalAssistantsColor(rhs.globalAssistantsColor) + , paletteList(rhs.paletteList) , gridConfig(rhs.gridConfig) , savingLock(&savingMutex) , batchMode(rhs.batchMode) @@ -328,6 +329,8 @@ KisSharedPtr referenceImagesLayer; + QList paletteList; + KisGridConfig gridConfig; StdLockableWrapper savingLock; @@ -1474,6 +1477,16 @@ d->gridConfig = config; } +QList &KisDocument::paletteList() +{ + return d->paletteList; +} + +void KisDocument::setPaletteList(const QList &paletteList) +{ + d->paletteList = paletteList; +} + const KisGuidesConfig& KisDocument::guidesConfig() const { return d->guidesConfig; diff --git a/libs/ui/KisPaletteEditor.h b/libs/ui/KisPaletteEditor.h new file mode 100644 --- /dev/null +++ b/libs/ui/KisPaletteEditor.h @@ -0,0 +1,148 @@ +/* + * Copyright (c) 2018 Michael Zhou + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef KISPALETTEMANAGER_H +#define KISPALETTEMANAGER_H + +#include +#include +#include + +#include + +class KoColorSet; +class KisPaletteModel; +class KisViewManager; +class KisSwatchGroup; +class KisViewManager; + +/** + * @brief The PaletteEditor class + * this class manipulates a KisPaletteModel using GUI elements and communicate + * with KisDocument + * + * Changes made in this class won't be done to the palette if the palette is + * read only (not editable, isEditable() == false) + */ +class KRITAUI_EXPORT KisPaletteEditor : public QObject +{ + Q_OBJECT +public: + struct PaletteInfo; + +public: + explicit KisPaletteEditor(QObject *parent = Q_NULLPTR); + ~KisPaletteEditor(); + + void setPaletteModel(KisPaletteModel *model); + void setView(KisViewManager *view); + + void addPalette(); + void importPalette(); + void removePalette(KoColorSet *); + + /** + * @brief rowNumberOfGroup + * @param oriName the original name of a group at the creation of the instance + * @return newest row number of the group + */ + int rowNumberOfGroup(const QString &oriName) const; + /** + * @brief oldNameFromNewName + * @param newName the current name of a group + * @return the name of the group at the creation of the instance + */ + QString oldNameFromNewName(const QString &newName) const; + /** + * @brief duplicateExistsFilename + * @param name + * @param global if this filename is going to be used for a global palette + * @return true if the a palette in the resource system that has filename + * name already exists else false + */ + bool duplicateExistsFilename(const QString &filename, bool global) const; + QString relativePathFromSaveLocation() const; + + void rename(const QString &newName); + void changeFilename(const QString &newName); + void changeColCount(int); + + /** + * @brief addGroup + * @param name original group name + * @param rowNumber + * @return new group's name if change accpeted, empty string if cancelled + */ + QString addGroup(); + /** + * @brief removeGroup + * @param name original group name + * @return true if change accepted, false if cancelled + */ + bool removeGroup(const QString &name); + /** + * @brief renameGroup + * @param oldName + * @return new name if change accpeted, empty string if cancelled + */ + QString renameGroup(const QString &oldName); + void changeGroupRowCount(const QString &name, int newRowCount); + void setGlobal(bool); + void setReadOnly(bool); + + void setEntry(const KoColor &color, const QModelIndex &index); + void removeEntry(const QModelIndex &index); + void modifyEntry(const QModelIndex &index); + void addEntry(const KoColor &color); + + bool isModified() const; + + /** + * @brief getModifiedGroup + * @param originalName name of the group at the creation of the instance + * @return the modified group + */ + const KisSwatchGroup &getModifiedGroup(const QString &originalName) const; + + /** + * @brief updatePalette + * MUST be called to make the changes into the resource server + */ + void updatePalette(); + +private Q_SLOTS: + void slotGroupNameChanged(const QString &newName); + void slotPaletteChanged(); + void slotPolluteDoc(); + +private: + QString newPaletteFileName(bool isGlobal); + QString newGroupName() const; + void setNonGlobal(); + void setGlobal(); + bool duplicateExistsGroupName(const QString &name) const; + bool duplicateExistsOriginalGroupName(const QString &name) const; + void uploadPaletteList() const; + QString filenameFromPath(const QString &path) const; + +private: + struct Private; + QScopedPointer m_d; +}; + +#endif // KISPALETTEMANAGER_H diff --git a/libs/ui/KisPaletteEditor.cpp b/libs/ui/KisPaletteEditor.cpp new file mode 100644 --- /dev/null +++ b/libs/ui/KisPaletteEditor.cpp @@ -0,0 +1,670 @@ +/* + * Copyright (c) 2018 Michael Zhou + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include "KisPaletteEditor.h" + +struct KisPaletteEditor::PaletteInfo { + QString name; + QString filename; + int columnCount; + bool isGlobal; + bool isReadOnly; + QHash groups; +}; + +struct KisPaletteEditor::Private +{ + bool isGlobalModified {false}; + bool isReadOnlyModified {false}; + bool isNameModified {false}; + bool isFilenameModified {false}; + bool isColumnCountModified {false}; + QSet modifiedGroupNames; // key is original group name + QSet newGroupNames; + QSet keepColorGroups; + QSet pathsToRemove; + QString groupBeingRenamed; + QPointer model; + QPointer view; + PaletteInfo modified; + QPointer query; + KoResourceServer *rServer; + + QPalette normalPalette; + QPalette warnPalette; +}; + +KisPaletteEditor::KisPaletteEditor(QObject *parent) + : QObject(parent) + , m_d(new Private) +{ + m_d->rServer = KoResourceServerProvider::instance()->paletteServer(); + m_d->warnPalette.setColor(QPalette::Text, Qt::red); +} + +KisPaletteEditor::~KisPaletteEditor() +{ } + +void KisPaletteEditor::setPaletteModel(KisPaletteModel *model) +{ + if (!model) { return; } + m_d->model = model; + slotPaletteChanged(); + connect(model, SIGNAL(sigPaletteChanged()), SLOT(slotPaletteChanged())); + connect(model, SIGNAL(sigPaletteModified()), SLOT(slotPolluteDoc())); +} + +void KisPaletteEditor::setView(KisViewManager *view) +{ + m_d->view = view; +} + +void KisPaletteEditor::addPalette() +{ + if (!m_d->view) { return; } + if (!m_d->view->document()) { return; } + KoColorSet *newColorSet = new KoColorSet(newPaletteFileName(false)); + newColorSet->setPaletteType(KoColorSet::KPL); + newColorSet->setIsGlobal(false); + newColorSet->setIsEditable(true); + newColorSet->setValid(true); + newColorSet->setName("New Palette"); + m_d->rServer->addResource(newColorSet); + m_d->rServer->removeFromBlacklist(newColorSet); + + uploadPaletteList(); +} + +void KisPaletteEditor::importPalette() +{ + KoFileDialog dialog(Q_NULLPTR, KoFileDialog::OpenFile, "Open Palette"); + dialog.setDefaultDir(QDir::homePath()); + dialog.setMimeTypeFilters(QStringList() << "krita/x-colorset" << "application/x-gimp-color-palette"); + QString filename = dialog.filename(); + if (filename.isEmpty()) { return; } + if (duplicateExistsFilename(filename, false)) { + QMessageBox message; + message.setWindowTitle(i18n("Can't Import Palette")); + message.setText(i18n("Can't import palette: there's already imported with the same filename")); + message.exec(); + return; + } + KoColorSet *colorSet = new KoColorSet(filename); + colorSet->load(); + QString name = filenameFromPath(colorSet->filename()); + if (duplicateExistsFilename(name, false)) { + colorSet->setFilename(newPaletteFileName(false)); + } else { + colorSet->setFilename(name); + } + colorSet->setIsGlobal(false); + m_d->rServer->addResource(colorSet); + m_d->rServer->removeFromBlacklist(colorSet); + + uploadPaletteList(); +} + +void KisPaletteEditor::removePalette(KoColorSet *cs) +{ + if (!m_d->view) { return; } + if (!m_d->view->document()) { return; } + if (!cs || !cs->isEditable()) { + return; + } + + if (cs->isGlobal()) { + m_d->rServer->removeResourceAndBlacklist(cs); + QFile::remove(cs->filename()); + return; + } + m_d->rServer->removeResourceFromServer(cs); + uploadPaletteList(); +} + +int KisPaletteEditor::rowNumberOfGroup(const QString &oriName) const +{ + if (!m_d->modified.groups.contains(oriName)) { return 0; } + return m_d->modified.groups[oriName].rowCount(); +} + +bool KisPaletteEditor::duplicateExistsGroupName(const QString &name) const +{ + if (name == m_d->groupBeingRenamed) { return false; } + Q_FOREACH (const KisSwatchGroup &g, m_d->modified.groups.values()) { + if (name == g.name()) { return true; } + } + return false; +} + +bool KisPaletteEditor::duplicateExistsOriginalGroupName(const QString &name) const +{ + return m_d->modified.groups.contains(name); +} + +QString KisPaletteEditor::oldNameFromNewName(const QString &newName) const +{ + Q_FOREACH (const QString &oldGroupName, m_d->modified.groups.keys()) { + if (m_d->modified.groups[oldGroupName].name() == newName) { + return oldGroupName; + } + } + return QString(); +} + +void KisPaletteEditor::rename(const QString &newName) +{ + if (newName.isEmpty()) { return; } + m_d->isNameModified = true; + m_d->modified.name = newName; +} + +void KisPaletteEditor::changeFilename(const QString &newName) +{ + if (newName.isEmpty()) { return; } + m_d->isFilenameModified = true; + m_d->pathsToRemove.insert(m_d->modified.filename); + if (m_d->modified.isGlobal) { + m_d->modified.filename = m_d->rServer->saveLocation() + newName; + } else { + m_d->modified.filename = newName; + } +} + +void KisPaletteEditor::changeColCount(int newCount) +{ + m_d->isColumnCountModified = true; + m_d->modified.columnCount = newCount; +} + +QString KisPaletteEditor::addGroup() +{ + KoDialog dlg; + m_d->query = &dlg; + + QVBoxLayout layout(&dlg); + dlg.mainWidget()->setLayout(&layout); + + QLabel lblName(i18n("Name"), &dlg); + layout.addWidget(&lblName); + QLineEdit leName(&dlg); + connect(&leName, SIGNAL(textChanged(QString)), SLOT(slotGroupNameChanged(QString))); + layout.addWidget(&leName); + QLabel lblRowCount(i18n("Row count"), &dlg); + layout.addWidget(&lblRowCount); + QSpinBox spxRow(&dlg); + spxRow.setValue(20); + layout.addWidget(&spxRow); + + if (dlg.exec() != QDialog::Accepted) { return QString(); } + if (duplicateExistsGroupName(leName.text())) { return QString(); } + + QString realName = leName.text(); + QString name = realName; + if (duplicateExistsOriginalGroupName(name)) { + name = newGroupName(); + } + m_d->modified.groups[name] = KisSwatchGroup(); + KisSwatchGroup &newGroup = m_d->modified.groups[name]; + newGroup.setName(realName); + m_d->newGroupNames.insert(name); + newGroup.setRowCount(spxRow.value()); + return realName; +} + +bool KisPaletteEditor::removeGroup(const QString &name) +{ + KoDialog window; + window.setWindowTitle(i18nc("@title:window", "Removing Group")); + QFormLayout editableItems(&window); + QCheckBox chkKeep(&window); + window.mainWidget()->setLayout(&editableItems); + editableItems.addRow(i18nc("Shows up when deleting a swatch group", "Keep the Colors"), &chkKeep); + if (window.exec() != KoDialog::Accepted) { return false; } + + m_d->modified.groups.remove(name); + m_d->newGroupNames.remove(name); + if (chkKeep.isChecked()) { + m_d->keepColorGroups.insert(name); + } + return true; +} + +QString KisPaletteEditor::renameGroup(const QString &oldName) +{ + if (oldName.isEmpty() || oldName == KoColorSet::GLOBAL_GROUP_NAME) { return QString(); } + + KoDialog dlg; + m_d->query = &dlg; + m_d->groupBeingRenamed = m_d->modified.groups[oldName].name(); + + QFormLayout form(&dlg); + dlg.mainWidget()->setLayout(&form); + + QLineEdit leNewName; + connect(&leNewName, SIGNAL(textChanged(QString)), SLOT(slotGroupNameChanged(QString))); + leNewName.setText(m_d->modified.groups[oldName].name()); + + form.addRow(i18nc("Renaming swatch group", "New name"), &leNewName); + + if (dlg.exec() != KoDialog::Accepted) { return QString(); } + if (leNewName.text().isEmpty()) { return QString(); } + if (duplicateExistsGroupName(leNewName.text())) { return QString(); } + + m_d->modified.groups[oldName].setName(leNewName.text()); + m_d->modifiedGroupNames.insert(oldName); + + return leNewName.text(); +} + +void KisPaletteEditor::slotGroupNameChanged(const QString &newName) +{ + QLineEdit *leGroupName = qobject_cast(sender()); + if (duplicateExistsGroupName(newName) || newName == QString()) { + leGroupName->setPalette(m_d->warnPalette); + if (m_d->query->button(KoDialog::Ok)) { + m_d->query->button(KoDialog::Ok)->setEnabled(false); + } + return; + } + leGroupName->setPalette(m_d->normalPalette); + if (m_d->query->button(KoDialog::Ok)) { + m_d->query->button(KoDialog::Ok)->setEnabled(true); + } +} + +void KisPaletteEditor::changeGroupRowCount(const QString &name, int newRowCount) +{ + if (!m_d->modified.groups.contains(name)) { return; } + m_d->modified.groups[name].setRowCount(newRowCount); + m_d->modifiedGroupNames.insert(name); +} + +void KisPaletteEditor::setGlobal(bool isGlobal) +{ + m_d->isGlobalModified = true; + m_d->modified.isGlobal = isGlobal; +} + +void KisPaletteEditor::setReadOnly(bool isReadOnly) +{ + if (!m_d->modified.isGlobal) { + QMessageBox message; + message.setWindowTitle(i18n("Can't set palette read only")); + message.setText(i18n("Only global palettes can be set read only.")); + message.exec(); + } + m_d->isReadOnlyModified = true; + m_d->modified.isReadOnly = isReadOnly; +} + +void KisPaletteEditor::setEntry(const KoColor &color, const QModelIndex &index) +{ + Q_ASSERT(m_d->model); + if (!m_d->model->colorSet()->isEditable()) { return; } + if (!m_d->view) { return; } + if (!m_d->view->document()) { return; } + m_d->model->setEntry(KisSwatch(color), index); +} + +void KisPaletteEditor::slotPolluteDoc() +{ + if ((!m_d->isGlobalModified && m_d->modified.isGlobal) == false) { + m_d->view->document()->addCommand(new KisChangePaletteCommand()); + } +} + +void KisPaletteEditor::removeEntry(const QModelIndex &index) +{ + Q_ASSERT(m_d->model); + if (!m_d->model->colorSet()->isEditable()) { return; } + if (!m_d->view) { return; } + if (!m_d->view->document()) { return; } + if (qvariant_cast(index.data(KisPaletteModel::IsGroupNameRole))) { + removeGroup(qvariant_cast(index.data(KisPaletteModel::GroupNameRole))); + updatePalette(); + } else { + m_d->model->removeEntry(index, false); + } + if (m_d->model->colorSet()->isGlobal()) { + m_d->model->colorSet()->save(); + return; + } +} + +void KisPaletteEditor::modifyEntry(const QModelIndex &index) +{ + if (!m_d->model->colorSet()->isEditable()) { return; } + if (!m_d->view) { return; } + if (!m_d->view->document()) { return; } + + KoDialog dlg; + QFormLayout *editableItems = new QFormLayout(&dlg); + dlg.mainWidget()->setLayout(editableItems); + QLineEdit *lnGroupName = new QLineEdit(&dlg); + + QString groupName = qvariant_cast(index.data(Qt::DisplayRole)); + if (qvariant_cast(index.data(KisPaletteModel::IsGroupNameRole))) { + renameGroup(groupName); + updatePalette(); + } else { + QLineEdit *lnIDName = new QLineEdit(&dlg); + KisColorButton *bnColor = new KisColorButton(&dlg); + QCheckBox *chkSpot = new QCheckBox(&dlg); + KisSwatch entry = m_d->model->getEntry(index); + chkSpot->setToolTip(i18nc("@info:tooltip", "A spot color is a color that the printer is able to print without mixing the paints it has available to it. The opposite is called a process color.")); + editableItems->addRow(i18n("ID"), lnIDName); + editableItems->addRow(i18nc("Name for a swatch group", "Name"), lnGroupName); + editableItems->addRow(i18n("Color"), bnColor); + editableItems->addRow(i18n("Spot"), chkSpot); + lnGroupName->setText(entry.name()); + lnIDName->setText(entry.id()); + bnColor->setColor(entry.color()); + chkSpot->setChecked(entry.spotColor()); + if (dlg.exec() == KoDialog::Accepted) { + entry.setName(lnGroupName->text()); + entry.setId(lnIDName->text()); + entry.setColor(bnColor->color()); + entry.setSpotColor(chkSpot->isChecked()); + m_d->model->setEntry(entry, index); + } + } +} + +void KisPaletteEditor::addEntry(const KoColor &color) +{ + Q_ASSERT(m_d->model); + if (!m_d->view) { return; } + if (!m_d->view->document()) { return; } + if (!m_d->model->colorSet()->isEditable()) { return; } + QScopedPointer window(new KoDialog); + window->setWindowTitle(i18nc("@title:window", "Add a new Colorset Entry")); + QFormLayout *editableItems = new QFormLayout(window.data()); + window->mainWidget()->setLayout(editableItems); + QComboBox *cmbGroups = new QComboBox(window.data()); + cmbGroups->addItems(m_d->model->colorSet()->getGroupNames()); + QLineEdit *lnIDName = new QLineEdit(window.data()); + QLineEdit *lnName = new QLineEdit(window.data()); + KisColorButton *bnColor = new KisColorButton(window.data()); + QCheckBox *chkSpot = new QCheckBox(window.data()); + chkSpot->setToolTip(i18nc("@info:tooltip", "A spot color is a color that the printer is able to print without mixing the paints it has available to it. The opposite is called a process color.")); + editableItems->addRow(i18n("Group"), cmbGroups); + editableItems->addRow(i18n("ID"), lnIDName); + editableItems->addRow(i18n("Name"), lnName); + editableItems->addRow(i18n("Color"), bnColor); + editableItems->addRow(i18nc("Spot color", "Spot"), chkSpot); + cmbGroups->setCurrentIndex(0); + lnName->setText(i18nc("Part of a default name for a color","Color") + + " " + + QString::number(m_d->model->colorSet()->colorCount()+1)); + lnIDName->setText(QString::number(m_d->model->colorSet()->colorCount() + 1)); + bnColor->setColor(color); + chkSpot->setChecked(false); + + if (window->exec() != KoDialog::Accepted) { return; } + + QString groupName = cmbGroups->currentText(); + + KisSwatch newEntry; + newEntry.setColor(bnColor->color()); + newEntry.setName(lnName->text()); + newEntry.setId(lnIDName->text()); + newEntry.setSpotColor(chkSpot->isChecked()); + m_d->model->addEntry(newEntry, groupName); + + if (m_d->model->colorSet()->isGlobal()) { + m_d->model->colorSet()->save(); + return; + } + m_d->modifiedGroupNames.insert(groupName); + m_d->modified.groups[groupName].addEntry(newEntry); +} + +void KisPaletteEditor::updatePalette() +{ + Q_ASSERT(m_d->model); + Q_ASSERT(m_d->model->colorSet()); + if (!m_d->model->colorSet()->isEditable()) { return; } + if (!m_d->view) { return; } + if (!m_d->view->document()) { return; } + KoColorSet *palette = m_d->model->colorSet(); + PaletteInfo &modified = m_d->modified; + + if (m_d->isColumnCountModified) { + palette->setColumnCount(modified.columnCount); + } + if (m_d->isNameModified) { + palette->setName(modified.name); + } + if (m_d->isFilenameModified) { + QString originalPath = palette->filename(); + palette->setFilename(modified.filename); + if (palette->isGlobal()) { + if (!palette->save()) { + palette->setFilename(newPaletteFileName(true)); + palette->save(); + } + QFile::remove(originalPath); + } + } + if (m_d->isGlobalModified) { + palette->setIsGlobal(modified.isGlobal); + if (modified.isGlobal) { + setGlobal(); + } else { + setNonGlobal(); + } + } + if (m_d->isReadOnlyModified) { + if (palette->isGlobal()) { + palette->setIsEditable(!m_d->modified.isReadOnly); + palette->save(); + } + } + Q_FOREACH (const QString &groupName, palette->getGroupNames()) { + if (!modified.groups.contains(groupName)) { + m_d->model->removeGroup(groupName, m_d->keepColorGroups.contains(groupName)); + } + } + m_d->keepColorGroups.clear(); + Q_FOREACH (const QString &groupName, palette->getGroupNames()) { + if (m_d->modifiedGroupNames.contains(groupName)) { + m_d->model->setRowNumber(groupName, modified.groups[groupName].rowCount()); + if (groupName != modified.groups[groupName].name()) { + m_d->model->renameGroup(groupName, modified.groups[groupName].name()); + modified.groups[modified.groups[groupName].name()] = modified.groups[groupName]; + modified.groups.remove(groupName); + } + } + } + m_d->modifiedGroupNames.clear(); + Q_FOREACH (const QString &newGroupName, m_d->newGroupNames) { + m_d->model->addGroup(modified.groups[newGroupName]); + } + m_d->newGroupNames.clear(); + + if (m_d->model->colorSet()->isGlobal()) { + m_d->model->colorSet()->save(); + } +} + +void KisPaletteEditor::slotPaletteChanged() +{ + Q_ASSERT(m_d->model); + if (!m_d->model->colorSet()) { return; } + KoColorSet *palette = m_d->model->colorSet(); + m_d->modified.groups.clear(); + m_d->keepColorGroups.clear(); + m_d->newGroupNames.clear(); + m_d->modifiedGroupNames.clear(); + + m_d->modified.name = palette->name(); + m_d->modified.filename = palette->filename(); + m_d->modified.columnCount = palette->columnCount(); + m_d->modified.isGlobal = palette->isGlobal(); + m_d->modified.isReadOnly = !palette->isEditable(); + + Q_FOREACH (const QString &groupName, palette->getGroupNames()) { + KisSwatchGroup *cs = palette->getGroup(groupName); + m_d->modified.groups[groupName] = KisSwatchGroup(*cs); + } +} + +void KisPaletteEditor::setGlobal() +{ + Q_ASSERT(m_d->model); + if (!m_d->view) { return; } + if (!m_d->view->document()) { return; } + if (!m_d->model->colorSet()) { return; } + + KoColorSet *colorSet = m_d->model->colorSet(); + QString saveLocation = m_d->rServer->saveLocation(); + QString name = filenameFromPath(colorSet->filename()); + + QFileInfo fileInfo(saveLocation + name); + + colorSet->setFilename(fileInfo.filePath()); + colorSet->setIsGlobal(true); + m_d->rServer->removeFromBlacklist(colorSet); + if (!colorSet->save()) { + QMessageBox message; + message.setWindowTitle(i18n("Saving palette failed")); + message.setText(i18n("Failed to save global palette file. Please set it to non-global, or you will lose the file when you close Krita")); + message.exec(); + } + + uploadPaletteList(); +} + +bool KisPaletteEditor::duplicateExistsFilename(const QString &filename, bool global) const +{ + QString prefix; + if (global) { + prefix = m_d->rServer->saveLocation(); + } + + Q_FOREACH (const KoResource *r, KoResourceServerProvider::instance()->paletteServer()->resources()) { + if (r->filename() == prefix + filename && r != m_d->model->colorSet()) { + return true; + } + } + + return false; +} + +QString KisPaletteEditor::relativePathFromSaveLocation() const +{ + return filenameFromPath(m_d->modified.filename); +} + +void KisPaletteEditor::setNonGlobal() +{ + Q_ASSERT(m_d->model); + if (!m_d->view) { return; } + if (!m_d->view->document()) { return; } + if (!m_d->model->colorSet()) { return; } + KoColorSet *colorSet = m_d->model->colorSet(); + QString name = filenameFromPath(colorSet->filename()); + QFile::remove(colorSet->filename()); + if (duplicateExistsFilename(name, false)) { + colorSet->setFilename(newPaletteFileName(false)); + } else { + colorSet->setFilename(name); + } + colorSet->setIsGlobal(false); + + uploadPaletteList(); +} + +QString KisPaletteEditor::newPaletteFileName(bool isGlobal) +{ + QSet nameSet; + + Q_FOREACH (const KoResource *r, m_d->rServer->resources()) { + nameSet.insert(r->filename()); + } + + KoColorSet tmpColorSet; + QString result = "new_palette_"; + + if (isGlobal) { + result = m_d->rServer->saveLocation() + result; + } + + int i = 0; + while (nameSet.contains(result + QString::number(i) + tmpColorSet.defaultFileExtension())) { + i++; + } + result = result + QString::number(i) + tmpColorSet.defaultFileExtension(); + return result; +} + +QString KisPaletteEditor::newGroupName() const +{ + QString prefix = "New Group "; + int i = 0; + while (m_d->modified.groups.contains(prefix + QString::number(i))) { + i++; + } + return prefix + QString::number(i); +} + +void KisPaletteEditor::uploadPaletteList() const +{ + QList list; + Q_FOREACH (KoResource * paletteResource, m_d->rServer->resources()) { + KoColorSet *palette = static_cast(paletteResource); + Q_ASSERT(palette); + if (!palette->isGlobal()) { + list.append(palette); + } + } + m_d->view->document()->setPaletteList(list); + m_d->view->document()->addCommand(new KisChangePaletteCommand()); +} + +QString KisPaletteEditor::filenameFromPath(const QString &path) const +{ + return QDir::fromNativeSeparators(path).section('/', -1, -1); +} diff --git a/libs/ui/dialogs/KisDlgPaletteEditor.h b/libs/ui/dialogs/KisDlgPaletteEditor.h new file mode 100644 --- /dev/null +++ b/libs/ui/dialogs/KisDlgPaletteEditor.h @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2018 Michael Zhou + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef KISDLGPALETTEEDITOR_H +#define KISDLGPALETTEEDITOR_H + +#include +#include +#include +#include +#include +#include + +#include "kritaui_export.h" + +class QAction; + +class KoColorSet; +class KisPaletteModel; +class KisSwatchGroup; +class KoDialog; +class KisViewManager; + +class KisPaletteEditor; +class Ui_WdgDlgPaletteEditor; + +/** + * @brief The KisDlgPaletteEditor class + * a dialog used by the palette docker to make modifications to a palette. + * it automatically uploads all changes into the resource server when + * the change is accepted + */ +class KRITAUI_EXPORT KisDlgPaletteEditor : public QDialog +{ + Q_OBJECT +public: + explicit KisDlgPaletteEditor(); + ~KisDlgPaletteEditor(); + +public: + void setPaletteModel(KisPaletteModel *); + KoColorSet* palette() const { return m_colorSet; } + void setView(KisViewManager *); + +private Q_SLOTS: + void slotDelGroup(); + void slotAddGroup(); + void slotRenGroup(); + + void slotGroupChosen(const QString &groupName); + + void slotRowCountChanged(int); + void slotSetGlobal(int); + void slotSetReadOnly(int); + + void slotNameChanged(); + void slotFilenameChanged(const QString &newFilename); + void slotFilenameInputFinished(); + void slotColCountChanged(int); + + void slotAccepted(); + +private: + QString oldNameFromNewName(const QString &newName) const; + +private: + QScopedPointer m_ui; + QScopedPointer m_actAddGroup; + QScopedPointer m_actDelGroup; + QScopedPointer m_actRenGroup; + QScopedPointer m_paletteEditor; + QPointer m_colorSet; + QString m_currentGroupOriginalName; + + QPalette m_normalPalette; + QPalette m_warnPalette; +}; + +#endif // KISKisDlgPaletteEditor_H diff --git a/libs/ui/dialogs/KisDlgPaletteEditor.cpp b/libs/ui/dialogs/KisDlgPaletteEditor.cpp new file mode 100644 --- /dev/null +++ b/libs/ui/dialogs/KisDlgPaletteEditor.cpp @@ -0,0 +1,234 @@ +/* + * Copyright (c) 2018 Michael Zhou + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "KisPaletteEditor.h" + +#include "ui_WdgDlgPaletteEditor.h" + +#include "KisDlgPaletteEditor.h" + +KisDlgPaletteEditor::KisDlgPaletteEditor() + : m_ui(new Ui_WdgDlgPaletteEditor) + , m_actAddGroup(new QAction(i18n("Add a group"))) + , m_actDelGroup(new QAction(i18n("Delete this group"))) + , m_actRenGroup(new QAction(i18n("Rename this group"))) + , m_paletteEditor(new KisPaletteEditor(this)) + , m_currentGroupOriginalName(KoColorSet::GLOBAL_GROUP_NAME) +{ + setWindowTitle(i18n("Palette Editor")); + + m_ui->setupUi(this); + m_ui->gbxPalette->setTitle(i18n("Palette settings")); + m_ui->labelFilename->setText(i18n("Filename")); + m_ui->labelName->setText(i18n("Palette Name")); + m_ui->bnAddGroup->setDefaultAction(m_actAddGroup.data()); + + m_ui->gbxGroup->setTitle(i18n("Group settings")); + m_ui->labelColCount->setText(i18n("Column count")); + m_ui->labelRowCount->setText(i18n("Row count")); + m_ui->bnDelGroup->setDefaultAction(m_actDelGroup.data()); + m_ui->bnRenGroup->setDefaultAction(m_actRenGroup.data()); + + connect(m_actAddGroup.data(), SIGNAL(triggered(bool)), SLOT(slotAddGroup())); + connect(m_actDelGroup.data(), SIGNAL(triggered(bool)), SLOT(slotDelGroup())); + connect(m_actRenGroup.data(), SIGNAL(triggered(bool)), SLOT(slotRenGroup())); + connect(m_ui->spinBoxRow, SIGNAL(valueChanged(int)), SLOT(slotRowCountChanged(int))); + connect(m_ui->spinBoxCol, SIGNAL(valueChanged(int)), SLOT(slotColCountChanged(int))); + connect(m_ui->lineEditName, SIGNAL(editingFinished()), SLOT(slotNameChanged())); + connect(m_ui->lineEditFilename, SIGNAL(textEdited(QString)), SLOT(slotFilenameChanged(QString))); + connect(m_ui->lineEditFilename, SIGNAL(editingFinished()), SLOT(slotFilenameInputFinished())); + connect(m_ui->ckxGlobal, SIGNAL(stateChanged(int)), SLOT(slotSetGlobal(int))); + connect(m_ui->ckxReadOnly, SIGNAL(stateChanged(int)), SLOT(slotSetReadOnly(int))); + + connect(this, SIGNAL(accepted()), SLOT(slotAccepted())); + + m_warnPalette.setColor(QPalette::Text, Qt::red); +} + +KisDlgPaletteEditor::~KisDlgPaletteEditor() +{ } + +void KisDlgPaletteEditor::setPaletteModel(KisPaletteModel *model) +{ + m_colorSet = model->colorSet(); + if (m_colorSet.isNull()) { + return; + } + m_paletteEditor->setPaletteModel(model); + + // don't let editor make changes when initializing + const QSignalBlocker blocker1(m_ui->lineEditFilename); + const QSignalBlocker blocker2(m_ui->lineEditName); + const QSignalBlocker blocker3(m_ui->spinBoxCol); + const QSignalBlocker blocker4(m_ui->spinBoxRow); + const QSignalBlocker blocker5(m_ui->ckxGlobal); + const QSignalBlocker blocker6(m_ui->ckxReadOnly); + const QSignalBlocker blocker7(m_ui->cbxGroup); + + m_ui->lineEditName->setText(m_colorSet->name()); + m_ui->lineEditFilename->setText(m_paletteEditor->relativePathFromSaveLocation()); + m_ui->spinBoxCol->setValue(m_colorSet->columnCount()); + m_ui->ckxGlobal->setCheckState(m_colorSet->isGlobal() ? Qt::Checked : Qt::Unchecked); + m_ui->ckxReadOnly->setCheckState(!m_colorSet->isEditable() ? Qt::Checked : Qt::Unchecked); + + Q_FOREACH (const QString & groupName, m_colorSet->getGroupNames()) { + m_ui->cbxGroup->addItem(groupName); + } + + connect(m_ui->cbxGroup, SIGNAL(currentTextChanged(QString)), SLOT(slotGroupChosen(QString))); + m_ui->cbxGroup->setCurrentText(KoColorSet::GLOBAL_GROUP_NAME); + m_ui->bnDelGroup->setEnabled(false); + m_ui->bnRenGroup->setEnabled(false); + + m_ui->spinBoxRow->setValue(m_paletteEditor->rowNumberOfGroup(KoColorSet::GLOBAL_GROUP_NAME)); + + bool canWrite = m_colorSet->isEditable(); + m_ui->lineEditName->setEnabled(canWrite); + m_ui->lineEditFilename->setEnabled(canWrite); + m_ui->spinBoxCol->setEnabled(canWrite); + m_ui->spinBoxRow->setEnabled(canWrite); + m_ui->ckxGlobal->setEnabled(canWrite); + m_ui->ckxReadOnly->setEnabled(canWrite && m_colorSet->isGlobal()); + m_ui->bnAddGroup->setEnabled(canWrite); +} + +void KisDlgPaletteEditor::setView(KisViewManager *view) +{ + m_paletteEditor->setView(view); +} + +void KisDlgPaletteEditor::slotAddGroup() +{ + QString newGroupName = m_paletteEditor->addGroup(); + if (!newGroupName.isEmpty()) { + m_ui->cbxGroup->addItem(newGroupName); + m_ui->cbxGroup->setCurrentIndex(m_ui->cbxGroup->count() - 1); + }; +} + +void KisDlgPaletteEditor::slotRenGroup() +{ + QString newName = m_paletteEditor->renameGroup(m_currentGroupOriginalName); + if (!newName.isEmpty()) { + int idx = m_ui->cbxGroup->currentIndex(); + m_ui->cbxGroup->removeItem(idx); + m_ui->cbxGroup->insertItem(idx, newName); + m_ui->cbxGroup->setCurrentIndex(idx); + } +} + +void KisDlgPaletteEditor::slotDelGroup() +{ + int deletedIdx = m_ui->cbxGroup->currentIndex(); + if (m_paletteEditor->removeGroup(m_currentGroupOriginalName)) { + m_ui->cbxGroup->setCurrentIndex(0); + m_ui->cbxGroup->removeItem(deletedIdx); + } +} + +void KisDlgPaletteEditor::slotGroupChosen(const QString &groupName) +{ + if (groupName == KoColorSet::GLOBAL_GROUP_NAME) { + m_ui->bnDelGroup->setEnabled(false); + m_ui->bnRenGroup->setEnabled(false); + } else { + m_ui->bnDelGroup->setEnabled(true); + m_ui->bnRenGroup->setEnabled(true); + } + m_currentGroupOriginalName = m_paletteEditor->oldNameFromNewName(groupName); + m_ui->spinBoxRow->setValue(m_paletteEditor->rowNumberOfGroup(m_currentGroupOriginalName)); +} + +void KisDlgPaletteEditor::slotRowCountChanged(int newCount) +{ + m_paletteEditor->changeGroupRowCount(m_currentGroupOriginalName, newCount); +} + +void KisDlgPaletteEditor::slotSetGlobal(int state) +{ + bool toGlobal = (state == Qt::Checked); + m_paletteEditor->setGlobal(toGlobal); + if (!toGlobal) { + m_ui->ckxReadOnly->setCheckState(Qt::Unchecked); + } + m_ui->ckxReadOnly->setEnabled(toGlobal); +} + +void KisDlgPaletteEditor::slotSetReadOnly(int state) +{ + m_paletteEditor->setReadOnly(state == Qt::Checked); +} + +void KisDlgPaletteEditor::slotNameChanged() +{ + m_paletteEditor->rename(qobject_cast(sender())->text()); +} + +void KisDlgPaletteEditor::slotFilenameChanged(const QString &newFilename) +{ + bool global = m_colorSet->isGlobal(); + if (m_paletteEditor->duplicateExistsFilename(newFilename, global)) { + m_ui->lineEditFilename->setPalette(m_warnPalette); + m_ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false); + return; + } + m_ui->lineEditFilename->setPalette(m_normalPalette); + m_ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(true); + m_paletteEditor->changeFilename(newFilename); +} + +void KisDlgPaletteEditor::slotFilenameInputFinished() +{ + QString newName = m_ui->lineEditFilename->text(); + bool global = m_colorSet->isGlobal(); + if (m_paletteEditor->duplicateExistsFilename(newName, global)) { + return; + } + m_paletteEditor->changeFilename(newName); +} + +void KisDlgPaletteEditor::slotColCountChanged(int newCount) +{ + m_paletteEditor->changeColCount(newCount); +} + +void KisDlgPaletteEditor::slotAccepted() +{ + m_paletteEditor->updatePalette(); +} diff --git a/libs/ui/forms/WdgDlgPaletteEditor.ui b/libs/ui/forms/WdgDlgPaletteEditor.ui new file mode 100644 --- /dev/null +++ b/libs/ui/forms/WdgDlgPaletteEditor.ui @@ -0,0 +1,214 @@ + + + WdgDlgPaletteEditor + + + + 0 + 0 + 524 + 481 + + + + Dialog + + + + + + + + + Palette settings + + + + + + Palette Name + + + + + + + + + + File name + + + + + + + + + + Column count + + + + + + + + + + + + Global + + + + + + + Read only + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + 0 + 0 + + + + Add a group + + + + + + + + + + Group settings + + + + + + + + + Row count + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + 0 + 0 + + + + Rename this group + + + + + + + + 0 + 0 + + + + Delete this group + + + + + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + + buttonBox + rejected() + WdgDlgPaletteEditor + reject() + + + 316 + 260 + + + 286 + 274 + + + + + buttonBox + accepted() + WdgDlgPaletteEditor + accept() + + + 248 + 254 + + + 157 + 274 + + + + + diff --git a/libs/ui/widgets/KisScreenColorPicker.cpp b/libs/ui/widgets/KisScreenColorPicker.cpp --- a/libs/ui/widgets/KisScreenColorPicker.cpp +++ b/libs/ui/widgets/KisScreenColorPicker.cpp @@ -272,3 +272,4 @@ return false; } +std::function KisDlgInternalColorSelector::s_screenColorPickerFactory = KisScreenColorPicker::createScreenColorPicker; diff --git a/libs/widgets/CMakeLists.txt b/libs/widgets/CMakeLists.txt --- a/libs/widgets/CMakeLists.txt +++ b/libs/widgets/CMakeLists.txt @@ -28,7 +28,6 @@ KoColorPopupAction.cpp KoColorSetWidget.cpp KoColorSlider.cpp - KoEditColorSetDialog.cpp KoTriangleColorSelector.cpp KoResourcePopupAction.cpp KoIconToolTip.cpp @@ -56,10 +55,6 @@ KoToolBoxFactory.cpp KoToolDocker.cpp - KisPaletteModel.cpp - kis_palette_delegate.cpp - kis_palette_view.cpp - KoPageLayoutWidget.cpp KoPageLayoutDialog.cpp KoShadowConfigWidget.cpp @@ -88,31 +83,35 @@ kis_color_input.cpp + # classes used by internal color selector kis_spinbox_color_selector.cpp - - KisDlgInternalColorSelector.cpp - KisVisualColorSelector.cpp KisVisualColorSelectorShape.cpp KisVisualEllipticalSelectorShape.cpp KisVisualRectangleSelectorShape.cpp KisVisualTriangleSelectorShape.cpp - kis_popup_button.cc - kis_color_button.cpp KisScreenColorPickerBase.cpp + KisDlgInternalColorSelector.cpp - KisColorsetChooser.cpp + KisPaletteModel.cpp + KisPaletteDelegate.cpp + kis_palette_view.cpp + KisPaletteListWidget.cpp + KisPaletteComboBox.cpp + + kis_popup_button.cc + kis_color_button.cpp ) ki18n_wrap_ui( kritawidgets_LIB_SRCS KoConfigAuthorPage.ui koDocumentInfoAboutWidget.ui koDocumentInfoAuthorWidget.ui - KoEditColorSet.ui wdg_file_name_requester.ui KoPageLayoutWidget.ui KoShadowConfigWidget.ui WdgDlgInternalColorSelector.ui + WdgPaletteListWidget.ui ) add_library(kritawidgets SHARED ${kritawidgets_LIB_SRCS}) diff --git a/libs/widgets/KisColorsetChooser.cpp b/libs/widgets/KisColorsetChooser.cpp deleted file mode 100644 --- a/libs/widgets/KisColorsetChooser.cpp +++ /dev/null @@ -1,161 +0,0 @@ -/* This file is part of the KDE project - * Copyright (C) 2013 Sven Langkamp - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public License - * along with this library; see the file COPYING.LIB. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301, USA. - */ - -#include "KisColorsetChooser.h" - -#include -#include -#include -#include -#include - -#include - -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include "kis_int_parse_spin_box.h" - -class ColorSetDelegate : public QAbstractItemDelegate -{ -public: - ColorSetDelegate(QObject * parent = 0) : QAbstractItemDelegate(parent) {} - ~ColorSetDelegate() override {} - /// reimplemented - void paint(QPainter *, const QStyleOptionViewItem &, const QModelIndex &) const override; - /// reimplemented - QSize sizeHint(const QStyleOptionViewItem & option, const QModelIndex &) const override { - return option.decorationSize; - } -}; - -void ColorSetDelegate::paint(QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex & index) const -{ - painter->save(); - if (! index.isValid()) - return; - - KoResource* resource = static_cast(index.internalPointer()); - KoColorSet* colorSet = static_cast(resource); - - if (option.state & QStyle::State_Selected) { - painter->fillRect(option.rect, option.palette.highlight()); - painter->setPen(option.palette.highlightedText().color()); - } - else { - painter->setBrush(option.palette.text().color()); - } - painter->drawText(option.rect.x() + 5, option.rect.y() + painter->fontMetrics().ascent() + 5, colorSet->name()); - - int size = 7; - for (quint32 i = 0; i < colorSet->nColors() && i*size < (quint32)option.rect.width(); i++) { - QRect rect(option.rect.x() + i*size, option.rect.y() + option.rect.height() - size, size, size); - painter->fillRect(rect, colorSet->getColorGlobal(i).color().toQColor()); - } - - painter->restore(); -} - -KisColorsetChooser::KisColorsetChooser(QWidget* parent): QWidget(parent) -{ - KoResourceServer * rserver = KoResourceServerProvider::instance()->paletteServer(); - QSharedPointer adapter(new KoResourceServerAdapter(rserver)); - m_itemChooser = new KoResourceItemChooser(adapter, this); - m_itemChooser->setItemDelegate(new ColorSetDelegate(this)); - m_itemChooser->showTaggingBar(true); - m_itemChooser->setFixedSize(250, 250); - m_itemChooser->setRowHeight(30); - m_itemChooser->setColumnCount(1); - connect(m_itemChooser, SIGNAL(resourceSelected(KoResource*)), - this, SLOT(resourceSelected(KoResource*))); - - KConfigGroup cfg = KSharedConfig::openConfig()->group(""); - m_itemChooser->configureKineticScrolling(cfg.readEntry("KineticScrollingGesture", 0), - cfg.readEntry("KineticScrollingSensitivity", 75), - cfg.readEntry("KineticScrollingScrollbar", true)); - - QPushButton* saveButton = new QPushButton(i18n("Save")); - connect(saveButton, SIGNAL(clicked(bool)), this, SLOT(slotSave())); - - m_nameEdit = new QLineEdit(this); - m_nameEdit->setPlaceholderText(i18n("Insert name")); - m_nameEdit->setClearButtonEnabled(true); - - m_columnEdit = new KisIntParseSpinBox(this); - m_columnEdit->setRange(1, 30); - m_columnEdit->setValue(10); - - QGridLayout* layout = new QGridLayout(this); - layout->addWidget(m_itemChooser, 0, 0, 1, 3); - layout->setColumnStretch(1, 1); - layout->addWidget(saveButton, 2, 2, 1, 1); - layout->addWidget(m_nameEdit, 1, 1, 1, 2); - layout->addWidget(new QLabel(i18n("Name:"), this), 1, 0, 1, 1); - layout->addWidget(m_columnEdit, 2, 1, 1, 1); - layout->addWidget(new QLabel(i18n("Columns:"), this), 2, 0, 1, 1); -} - -KisColorsetChooser::~KisColorsetChooser() -{ -} - -void KisColorsetChooser::resourceSelected(KoResource* resource) -{ - emit paletteSelected(static_cast(resource)); -} - -void KisColorsetChooser::slotSave() -{ - KoResourceServer * rserver = KoResourceServerProvider::instance()->paletteServer(); - - KoColorSet* colorset = new KoColorSet(); - colorset->setValid(true); - - QString saveLocation = rserver->saveLocation(); - QString name = m_nameEdit->text(); - int columns = m_columnEdit->value(); - - bool newName = false; - if(name.isEmpty()) { - newName = true; - name = i18n("Palette"); - } - QFileInfo fileInfo(saveLocation + name + colorset->defaultFileExtension()); - - int i = 1; - while (fileInfo.exists()) { - fileInfo.setFile(saveLocation + name + QString("%1").arg(i) + colorset->defaultFileExtension()); - i++; - } - colorset->setFilename(fileInfo.filePath()); - if(newName) { - name = i18n("Palette %1", i); - } - colorset->setName(name); - colorset->setColumnCount(columns); - rserver->addResource(colorset); -} diff --git a/libs/widgets/KisDlgInternalColorSelector.h b/libs/widgets/KisDlgInternalColorSelector.h --- a/libs/widgets/KisDlgInternalColorSelector.h +++ b/libs/widgets/KisDlgInternalColorSelector.h @@ -171,11 +171,6 @@ void slotChangePalette(KoColorSet *set); - void slotSetColorFromColorList(); - - void slotSetColorFromColorSetEntry(KoColorSetEntry entry); - - protected: void showEvent(QShowEvent *event); diff --git a/libs/widgets/KisDlgInternalColorSelector.cpp b/libs/widgets/KisDlgInternalColorSelector.cpp --- a/libs/widgets/KisDlgInternalColorSelector.cpp +++ b/libs/widgets/KisDlgInternalColorSelector.cpp @@ -30,7 +30,7 @@ #include "KoColorSpaceRegistry.h" #include #include -#include +#include #include #include #include @@ -62,7 +62,7 @@ const KoColorDisplayRendererInterface *displayRenderer; KisHexColorInput *hexColorInput = 0; KisPaletteModel *paletteModel = 0; - KisColorsetChooser *colorSetChooser = 0; + KisPaletteListWidget *paletteChooser = 0; KisScreenColorPickerBase *screenColorPicker = 0; }; @@ -71,7 +71,7 @@ , m_d(new Private) { setModal(config.modal); - this->setFocusPolicy(Qt::ClickFocus); + setFocusPolicy(Qt::ClickFocus); m_ui = new Ui_WdgDlgInternalColorSelector(); m_ui->setupUi(this); @@ -94,11 +94,15 @@ m_ui->visualSelector->hide(); } - if (!m_d->paletteModel) { - m_d->paletteModel = new KisPaletteModel(this); - m_ui->paletteBox->setPaletteModel(m_d->paletteModel); - } - m_ui->bnColorsetChooser->setIcon(KisIconUtils::loadIcon("hi16-palette_library")); + m_d->paletteChooser = new KisPaletteListWidget(this); + m_d->paletteModel = new KisPaletteModel(this); + m_ui->bnPaletteChooser->setIcon(KisIconUtils::loadIcon("hi16-palette_library")); + m_ui->paletteBox->setPaletteModel(m_d->paletteModel); + m_ui->paletteBox->setDisplayRenderer(displayRenderer); + m_ui->cmbNameList->setCompanionView(m_ui->paletteBox); + connect(m_d->paletteChooser, SIGNAL(sigPaletteSelected(KoColorSet*)), this, SLOT(slotChangePalette(KoColorSet*))); + connect(m_ui->cmbNameList, SIGNAL(sigColorSelected(KoColor)), SLOT(slotColorUpdated(KoColor))); + // For some bizare reason, the modal dialog doesn't like having the colorset set, so let's not. if (config.paletteBox) { //TODO: Add disable signal as well. Might be not necessary...? @@ -117,18 +121,13 @@ } } - connect(m_ui->paletteBox, SIGNAL(entrySelected(KoColorSetEntry)), this, SLOT(slotSetColorFromColorSetEntry(KoColorSetEntry))); - connect(m_ui->cmbNameList, SIGNAL(currentIndexChanged(int)), this, SLOT(slotSetColorFromColorList())); - //m_ui->paletteBox->setDisplayRenderer(displayRenderer); - m_d->colorSetChooser = new KisColorsetChooser(this); - connect(m_d->colorSetChooser, SIGNAL(paletteSelected(KoColorSet*)), this, SLOT(slotChangePalette(KoColorSet*))); - - m_ui->bnColorsetChooser->setPopupWidget(m_d->colorSetChooser); - + connect(m_ui->paletteBox, SIGNAL(sigColorSelected(KoColor)), this, + SLOT(slotColorUpdated(KoColor))); + m_ui->bnPaletteChooser->setPopupWidget(m_d->paletteChooser); } else { m_ui->paletteBox->setEnabled(false); m_ui->cmbNameList->setEnabled(false); - m_ui->bnColorsetChooser->setEnabled(false); + m_ui->bnPaletteChooser->setEnabled(false); } if (config.prevNextButtons) { @@ -222,7 +221,7 @@ m_ui->visualSelector->setDisplayRenderer(displayRenderer); m_ui->currentColor->setDisplayRenderer(displayRenderer); m_ui->previousColor->setDisplayRenderer(displayRenderer); - //m_ui->paletteBox->setDisplayRenderer(displayRenderer); + m_ui->paletteBox->setDisplayRenderer(displayRenderer); } else { m_d->displayRenderer = KoDumbColorDisplayRenderer::instance(); } @@ -284,6 +283,10 @@ m_d->hexColorInput->update(); } + if (source != m_ui->paletteBox) { + m_ui->paletteBox->selectClosestColor(m_d->currentColor); + } + m_ui->previousColor->setColor(m_d->previousColor); m_ui->currentColor->setColor(m_d->currentColor); @@ -330,57 +333,7 @@ if (!set) { return; } - m_d->paletteModel->setColorSet(set); - m_ui->cmbNameList->clear(); - for (quint32 i = 0; i< set->nColors(); i++) { - KoColorSetEntry entry = set->getColorGlobal(i); - QPixmap colorSquare = QPixmap(32, 32); - if (entry.spotColor()) { - QImage img = QImage(32, 32, QImage::Format_ARGB32); - QPainter circlePainter; - img.fill(Qt::transparent); - circlePainter.begin(&img); - QBrush brush = QBrush(Qt::SolidPattern); - brush.setColor(entry.color().toQColor()); - circlePainter.setBrush(brush); - QPen pen = circlePainter.pen(); - pen.setColor(Qt::transparent); - pen.setWidth(0); - circlePainter.setPen(pen); - circlePainter.drawEllipse(0, 0, 32, 32); - circlePainter.end(); - colorSquare = QPixmap::fromImage(img); - } else { - colorSquare.fill(entry.color().toQColor()); - } - QString name = entry.name(); - if (!entry.id().isEmpty()){ - name = entry.id() + " - " + entry.name(); - } - m_ui->cmbNameList->addSqueezedItem(QIcon(colorSquare), name); - } - QCompleter *completer = new QCompleter(m_ui->cmbNameList->model()); - completer->setCompletionMode(QCompleter::PopupCompletion); - completer->setCaseSensitivity(Qt::CaseInsensitive); - completer->setFilterMode(Qt::MatchContains); - m_ui->cmbNameList->setCompleter(completer); -} - -void KisDlgInternalColorSelector::slotSetColorFromColorList() -{ - int index = m_ui->cmbNameList->currentIndex(); - if (m_d->paletteModel) { - slotSetColorFromColorSetEntry(m_d->paletteModel->colorSet()->getColorGlobal(index)); - m_ui->paletteBox->blockSignals(true); - m_ui->paletteBox->selectionModel()->clearSelection(); - m_ui->paletteBox->selectionModel()->setCurrentIndex(m_d->paletteModel->indexFromId(index), QItemSelectionModel::Select); - m_ui->paletteBox->blockSignals(false); - } -} - -void KisDlgInternalColorSelector::slotSetColorFromColorSetEntry(KoColorSetEntry entry) -{ - slotColorUpdated(entry.color()); + m_d->paletteModel->setPalette(set); } void KisDlgInternalColorSelector::showEvent(QShowEvent *event) diff --git a/libs/widgets/KisPaletteComboBox.h b/libs/widgets/KisPaletteComboBox.h new file mode 100644 --- /dev/null +++ b/libs/widgets/KisPaletteComboBox.h @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2018 Michael Zhou + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef KISPALETTECOMBOBOX_H +#define KISPALETTECOMBOBOX_H + +#include "kritawidgets_export.h" + +#include +#include +#include +#include +#include +#include + +#include + +class KisPaletteView; + +/** + * @brief The KisPaletteComboBox class + * A combobox used with KisPaletteView + * + */ +class KRITAWIDGETS_EXPORT KisPaletteComboBox : public QComboBox +{ + Q_OBJECT +private /* typedef */: + typedef KisSwatchGroup::SwatchInfo SwatchInfoType; + typedef QPair SwatchPosType; // first is column #, second is row # + typedef QHash PosIdxMapType; + +public: + explicit KisPaletteComboBox(QWidget *parent = Q_NULLPTR); + ~KisPaletteComboBox(); + +Q_SIGNALS: + void sigColorSelected(const KoColor &); + +public /* methods */: + void setCompanionView(KisPaletteView *); + +private Q_SLOTS: + void setPaletteModel(const KisPaletteModel *); + void slotPaletteChanged(); + void slotSwatchSelected(const QModelIndex &index); + void slotIndexUpdated(int); + +private /* methods */: + QPixmap createColorSquare(const KisSwatch &swatch) const; + static bool swatchInfoLess(const SwatchInfoType &, const SwatchInfoType &); + +private /* member variables */: + QPointer m_model; + QPointer m_view; + QHash m_groupMapMap; + QVector m_idxSwatchMap; +}; + +#endif // KISPALETTECOMBOBOX_H diff --git a/libs/widgets/KisPaletteComboBox.cpp b/libs/widgets/KisPaletteComboBox.cpp new file mode 100644 --- /dev/null +++ b/libs/widgets/KisPaletteComboBox.cpp @@ -0,0 +1,162 @@ +/* + * Copyright (c) 2018 Michael Zhou + * + * 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. + */ + +// Qt +#include +#include +#include +#include + +// STL +#include + +#include "kis_palette_view.h" +#include "KisPaletteComboBox.h" + +KisPaletteComboBox::KisPaletteComboBox(QWidget *parent) + : QComboBox(parent) + , m_model(Q_NULLPTR) +{ + setEditable(true); + setInsertPolicy(NoInsert); + completer()->setCompletionMode(QCompleter::PopupCompletion); + completer()->setCaseSensitivity(Qt::CaseInsensitive); + completer()->setFilterMode(Qt::MatchContains); + connect(this, SIGNAL(currentIndexChanged(int)), SLOT(slotIndexUpdated(int))); +} + +KisPaletteComboBox::~KisPaletteComboBox() +{ } + +void KisPaletteComboBox::setPaletteModel(const KisPaletteModel *paletteModel) +{ + if (!m_model.isNull()) { + m_model->disconnect(this); + } + m_model = paletteModel; + if (m_model.isNull()) { return; } + slotPaletteChanged(); + connect(m_model, SIGNAL(sigPaletteChanged()), + SLOT(slotPaletteChanged())); + connect(m_model, SIGNAL(sigPaletteModified()), + SLOT(slotPaletteChanged())); +} + +void KisPaletteComboBox::setCompanionView(KisPaletteView *view) +{ + if (!m_view.isNull()) { + m_view->disconnect(this); + disconnect(m_view.data()); + } + m_view = view; + setPaletteModel(view->paletteModel()); + connect(view, SIGNAL(sigIndexSelected(QModelIndex)), + SLOT(slotSwatchSelected(QModelIndex))); + connect(this, SIGNAL(sigColorSelected(KoColor)), + view, SLOT(slotFGColorChanged(KoColor))); +} + +void KisPaletteComboBox::slotPaletteChanged() +{ + clear(); + m_groupMapMap.clear(); + m_idxSwatchMap.clear(); + + if (QPointer(m_model->colorSet()).isNull()) { return; } + + for (const QString &groupName : m_model->colorSet()->getGroupNames()) { + QVector infoList; + PosIdxMapType posIdxMap; + const KisSwatchGroup *group = m_model->colorSet()->getGroup(groupName); + for (const SwatchInfoType &info : group->infoList()) { + infoList.append(info); + } + std::sort(infoList.begin(), infoList.end(), swatchInfoLess); + for (const SwatchInfoType &info : infoList) { + const KisSwatch &swatch = info.swatch; + QString name = swatch.name(); + if (!swatch.id().isEmpty()){ + name = swatch.id() + " - " + swatch.name(); + } + addItem(QIcon(createColorSquare(swatch)), name); + posIdxMap[SwatchPosType(info.column, info.row)] = count() - 1; + m_idxSwatchMap.push_back(swatch); + } + m_groupMapMap[group->name()] = posIdxMap; + } + if (m_view.isNull()) { + setCurrentIndex(0); + } + QModelIndex idx = m_view->currentIndex(); + if (!idx.isValid()) { return; } + if (qvariant_cast(idx.data(KisPaletteModel::IsGroupNameRole))) { return; } + if (!qvariant_cast(idx.data(KisPaletteModel::CheckSlotRole))) { return; } + + blockSignals(true); // this is a passive selection; this shouldn't make others change + slotSwatchSelected(idx); + blockSignals(false); +} + +bool KisPaletteComboBox::swatchInfoLess(const SwatchInfoType &first, const SwatchInfoType &second) +{ + return first.swatch.name() < second.swatch.name(); +} + +QPixmap KisPaletteComboBox::createColorSquare(const KisSwatch &swatch) const +{ + QPixmap colorSquare(32, 32); + if (swatch.spotColor()) { + QImage img = QImage(32, 32, QImage::Format_ARGB32); + QPainter circlePainter; + img.fill(Qt::transparent); + circlePainter.begin(&img); + QBrush brush = QBrush(Qt::SolidPattern); + brush.setColor(swatch.color().toQColor()); + circlePainter.setBrush(brush); + QPen pen = circlePainter.pen(); + pen.setColor(Qt::transparent); + pen.setWidth(0); + circlePainter.setPen(pen); + circlePainter.drawEllipse(0, 0, 32, 32); + circlePainter.end(); + colorSquare = QPixmap::fromImage(img); + } else { + colorSquare.fill(swatch.color().toQColor()); + } + return colorSquare; +} + +void KisPaletteComboBox::slotSwatchSelected(const QModelIndex &index) +{ + if (!qvariant_cast(index.data(KisPaletteModel::CheckSlotRole))) { + return; + } + if (qvariant_cast(index.data(KisPaletteModel::IsGroupNameRole))) { + return; + } + QString gName = qvariant_cast(index.data(KisPaletteModel::GroupNameRole)); + int rowInGroup = qvariant_cast(index.data(KisPaletteModel::RowInGroupRole)); + setCurrentIndex(m_groupMapMap[gName][SwatchPosType(index.column(), rowInGroup)]); +} + +void KisPaletteComboBox::slotIndexUpdated(int idx) +{ + if (idx >= 0 && idx < m_idxSwatchMap.size()) { + emit sigColorSelected(m_idxSwatchMap[idx].color()); + } +} diff --git a/libs/widgets/kis_palette_delegate.h b/libs/widgets/KisPaletteDelegate.h rename from libs/widgets/kis_palette_delegate.h rename to libs/widgets/KisPaletteDelegate.h --- a/libs/widgets/kis_palette_delegate.h +++ b/libs/widgets/KisPaletteDelegate.h @@ -26,17 +26,27 @@ class KRITAWIDGETS_EXPORT KisPaletteDelegate : public QAbstractItemDelegate { +private: + static const int BORDER_WIDTH; public: KisPaletteDelegate(QObject * parent = 0); ~KisPaletteDelegate() override; - void setCrossedKeyword(const QString &value); + void setCrossedKeyword(const QString &value) + { + m_crossedKeyword = value; + } void paint(QPainter *, const QStyleOptionViewItem &, const QModelIndex &) const override; QSize sizeHint(const QStyleOptionViewItem & option, const QModelIndex &) const override; + void paintSwatch() const; + private: QString m_crossedKeyword; + void paintCrossedLine(const QStyleOptionViewItem &option, QPainter *painter) const; + void paintNonCrossed(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index, const bool isSelected) const; + void paintGroupName(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index, const bool isSelected) const; }; #endif /* __KIS_PALETTE_DELEGATE_H */ diff --git a/libs/widgets/KisPaletteDelegate.cpp b/libs/widgets/KisPaletteDelegate.cpp new file mode 100644 --- /dev/null +++ b/libs/widgets/KisPaletteDelegate.cpp @@ -0,0 +1,111 @@ +/* + * 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 +#include +#include // qBound + +#include +#include +#include "kis_debug.h" + +#include "KisPaletteDelegate.h" + +const int KisPaletteDelegate::BORDER_WIDTH = 3; + +KisPaletteDelegate::KisPaletteDelegate(QObject *parent) + : QAbstractItemDelegate(parent) +{ } + +KisPaletteDelegate::~KisPaletteDelegate() +{ } + +void KisPaletteDelegate::paintCrossedLine(const QStyleOptionViewItem &option, QPainter *painter) const +{ + QRect crossRect = kisGrowRect(option.rect, -qBound(2, option.rect.width() / 6, 4)); + + 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(); +} + +void KisPaletteDelegate::paintNonCrossed(QPainter *painter, const QStyleOptionViewItem &option, + const QModelIndex &index, const bool isSelected) const +{ +} + +void KisPaletteDelegate::paintGroupName(QPainter *painter, const QStyleOptionViewItem &option, + const QModelIndex &index, const bool isSelected) const +{ + QString name = qvariant_cast(index.data(Qt::DisplayRole)); + if (isSelected) { + painter->fillRect(option.rect, option.palette.highlight()); + } + QRect paintRect = kisGrowRect(option.rect, -BORDER_WIDTH); + painter->setBrush(QBrush(Qt::lightGray)); + painter->drawText(paintRect, name); +} + +void KisPaletteDelegate::paint(QPainter *painter, + const QStyleOptionViewItem &option, + const QModelIndex &index) const +{ + painter->save(); + + if (!index.isValid()) + return; + + const bool isSelected = option.state & QStyle::State_Selected; + + if (qvariant_cast(index.data(KisPaletteModel::IsGroupNameRole))) { + paintGroupName(painter, option, index, isSelected); + } else { + QRect paintRect = option.rect; + if (isSelected) { + painter->fillRect(option.rect, option.palette.highlight()); + paintRect = kisGrowRect(option.rect, -BORDER_WIDTH); + } + if (qvariant_cast(index.data(KisPaletteModel::CheckSlotRole))) { + QBrush brush = qvariant_cast(index.data(Qt::BackgroundRole)); + painter->fillRect(paintRect, brush); + } else { + QBrush lightBrush(Qt::gray); + QBrush darkBrush(Qt::darkGray); + painter->fillRect(paintRect, lightBrush); + painter->fillRect(QRect(paintRect.topLeft(), paintRect.center()), darkBrush); + painter->fillRect(QRect(paintRect.center(), paintRect.bottomRight()), darkBrush); + } + + QString name = qvariant_cast(index.data(Qt::DisplayRole)); + if (!m_crossedKeyword.isNull() && name.toLower().contains(m_crossedKeyword)) { + paintCrossedLine(option, painter); + } + } + + painter->restore(); +} + +QSize KisPaletteDelegate::sizeHint(const QStyleOptionViewItem &option, + const QModelIndex &) const +{ + return option.decorationSize; +} diff --git a/libs/widgets/KisPaletteListWidget.h b/libs/widgets/KisPaletteListWidget.h new file mode 100644 --- /dev/null +++ b/libs/widgets/KisPaletteListWidget.h @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2013 Sven Langkamp + * Copyright (c) 2018 Michael Zhou + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef KISPALETTELISTWIDGET_H +#define KISPALETTELISTWIDGET_H + +#include +#include +#include + +#include "kritawidgets_export.h" + +class KoResource; +class KoColorSet; + +class KisPaletteListWidgetPrivate; + +class KRITAWIDGETS_EXPORT KisPaletteListWidget : public QWidget +{ + Q_OBJECT +public: + explicit KisPaletteListWidget(QWidget *parent = nullptr); + virtual ~KisPaletteListWidget(); + +public: + void setAllowModification(bool allowModification); + +Q_SIGNALS: + void sigPaletteSelected(KoColorSet*); + void sigAddPalette(); + void sigRemovePalette(KoColorSet *); + void sigImportPalette(); + void sigExportPalette(KoColorSet *); + +public Q_SLOTS: + +private /* methods */: + QString newPaletteFileName(); + +private Q_SLOTS: + void slotPaletteResourceSelected(KoResource *); + void slotAdd(); + void slotRemove(); + void slotImport(); + void slotExport(); + +private: + QScopedPointer m_ui; + QScopedPointer m_d; +}; + +#endif // KISPALETTELISTWIDGET_H diff --git a/libs/widgets/KisPaletteListWidget.cpp b/libs/widgets/KisPaletteListWidget.cpp new file mode 100644 --- /dev/null +++ b/libs/widgets/KisPaletteListWidget.cpp @@ -0,0 +1,194 @@ +/* + * Copyright (c) 2013 Sven Langkamp + * Copyright (c) 2018 Michael Zhou + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include "KisPaletteListWidget.h" +#include "KisPaletteListWidget_p.h" + +KisPaletteListWidget::KisPaletteListWidget(QWidget *parent) + : QWidget(parent) + , m_ui(new Ui_WdgPaletteListWidget) + , m_d(new KisPaletteListWidgetPrivate(this)) +{ + m_d->allowModification = false; + + m_d->actAdd.reset(new QAction(KisIconUtils::loadIcon("list-add"), + i18n("Add a new palette"))); + m_d->actRemove.reset(new QAction(KisIconUtils::loadIcon("list-remove"), + i18n("Remove current palette"))); + m_d->actImport.reset(new QAction(KisIconUtils::loadIcon("document-import"), + i18n("Import a new palette from file"))); + m_d->actExport.reset(new QAction(KisIconUtils::loadIcon("document-export"), + i18n("Export current palette to file"))); + m_d->model->setColumnCount(1); + + m_ui->setupUi(this); + m_ui->bnAdd->setDefaultAction(m_d->actAdd.data()); + m_ui->bnRemove->setDefaultAction(m_d->actRemove.data()); + m_ui->bnImport->setDefaultAction(m_d->actImport.data()); + m_ui->bnExport->setDefaultAction(m_d->actExport.data()); + + m_ui->bnAdd->setEnabled(false); + m_ui->bnRemove->setEnabled(false); + m_ui->bnImport->setEnabled(false); + m_ui->bnExport->setEnabled(false); + + connect(m_d->actAdd.data(), SIGNAL(triggered()), SLOT(slotAdd())); + connect(m_d->actRemove.data(), SIGNAL(triggered()), SLOT(slotRemove())); + connect(m_d->actImport.data(), SIGNAL(triggered()), SLOT(slotImport())); + connect(m_d->actExport.data(), SIGNAL(triggered()), SLOT(slotExport())); + + m_d->itemChooser->setItemDelegate(m_d->delegate.data()); + m_d->itemChooser->setRowHeight(40); + m_d->itemChooser->setColumnCount(1); + m_d->itemChooser->showButtons(false); + m_d->itemChooser->showTaggingBar(true); + m_ui->viewPalette->setLayout(new QHBoxLayout(m_ui->viewPalette)); + m_ui->viewPalette->layout()->addWidget(m_d->itemChooser.data()); + + connect(m_d->itemChooser.data(), SIGNAL(resourceSelected(KoResource *)), SLOT(slotPaletteResourceSelected(KoResource*))); +} + +KisPaletteListWidget::~KisPaletteListWidget() +{ } + +void KisPaletteListWidget::slotPaletteResourceSelected(KoResource *r) +{ + KoColorSet *g = static_cast(r); + emit sigPaletteSelected(g); + if (!m_d->allowModification) { return; } + if (g->isEditable()) { + m_ui->bnRemove->setEnabled(true); + } else { + m_ui->bnRemove->setEnabled(false); + } +} + +void KisPaletteListWidget::slotAdd() +{ + if (!m_d->allowModification) { return; } + emit sigAddPalette(); + m_d->itemChooser->setCurrentItem(m_d->rAdapter->resources().size() - 1, 0); +} + +void KisPaletteListWidget::slotRemove() +{ + if (!m_d->allowModification) { return; } + if (m_d->itemChooser->currentResource()) { + KoColorSet *cs = static_cast(m_d->itemChooser->currentResource()); + emit sigRemovePalette(cs); + } + m_d->itemChooser->setCurrentItem(0, 0); +} + +void KisPaletteListWidget::slotImport() +{ + if (!m_d->allowModification) { return; } + emit sigImportPalette(); + m_d->itemChooser->setCurrentItem(m_d->rAdapter->resources().size()-1, 0); +} + +void KisPaletteListWidget::slotExport() +{ + if (!m_d->allowModification) { return; } + emit sigExportPalette(static_cast(m_d->itemChooser->currentResource())); +} + +void KisPaletteListWidget::setAllowModification(bool allowModification) +{ + m_d->allowModification = allowModification; + m_ui->bnAdd->setEnabled(allowModification); + m_ui->bnImport->setEnabled(allowModification); + m_ui->bnExport->setEnabled(allowModification); + KoColorSet *cs = static_cast(m_d->itemChooser->currentResource()); + m_ui->bnRemove->setEnabled(allowModification && cs && cs->isEditable()); +} + +/************************* KisPaletteListWidgetPrivate **********************/ + +KisPaletteListWidgetPrivate::KisPaletteListWidgetPrivate(KisPaletteListWidget *a_c) + : c(a_c) + , rAdapter(new KoResourceServerAdapter(KoResourceServerProvider::instance()->paletteServer())) + , itemChooser(new KoResourceItemChooser(rAdapter, a_c)) + , model(new Model(rAdapter, a_c)) + , delegate(new Delegate(a_c)) +{ } + +KisPaletteListWidgetPrivate::~KisPaletteListWidgetPrivate() +{ } + +/******************* KisPaletteListWidgetPrivate::Delegate ******************/ + +KisPaletteListWidgetPrivate::Delegate::Delegate(QObject *parent) + : QAbstractItemDelegate(parent) +{ } + +KisPaletteListWidgetPrivate::Delegate::~Delegate() +{ } + +void KisPaletteListWidgetPrivate::Delegate::paint(QPainter * painter, + const QStyleOptionViewItem & option, + const QModelIndex & index) const +{ + painter->save(); + if (!index.isValid()) + return; + + KoResource* resource = static_cast(index.internalPointer()); + KoColorSet* colorSet = static_cast(resource); + + QRect previewRect(option.rect.x() + 2, + option.rect.y() + 2, + option.rect.height() - 4, + option.rect.height() - 4); + + painter->drawImage(previewRect, colorSet->image()); + + if (option.state & QStyle::State_Selected) { + painter->fillRect(option.rect, option.palette.highlight()); + painter->drawImage(previewRect, colorSet->image()); + painter->setPen(option.palette.highlightedText().color()); + } else { + painter->setBrush(option.palette.text().color()); + } + painter->drawText(option.rect.x() + previewRect.width() + 10, + option.rect.y() + painter->fontMetrics().ascent() + 5, + colorSet->name()); + + painter->restore(); +} + +inline QSize KisPaletteListWidgetPrivate::Delegate::sizeHint(const QStyleOptionViewItem & option, + const QModelIndex &) const +{ + return option.decorationSize; +} diff --git a/libs/widgets/KisPaletteListWidget_p.h b/libs/widgets/KisPaletteListWidget_p.h new file mode 100644 --- /dev/null +++ b/libs/widgets/KisPaletteListWidget_p.h @@ -0,0 +1,67 @@ +#ifndef KISPALETTELISTWIDGET_P_H +#define KISPALETTELISTWIDGET_P_H + +#include +#include +#include +#include +#include +#include + +#include "KisPaletteListWidget.h" +#include "KoResourceModel.h" +#include "KoResourceItemView.h" +#include "KoResourceItemChooser.h" +#include "KoResourceServer.h" +#include "KoResourceServerAdapter.h" +#include "KoResourceServerProvider.h" +#include "KoColorSet.h" + +struct KisPaletteListWidgetPrivate +{ + class View; + class Delegate; + class Model; + KisPaletteListWidgetPrivate(KisPaletteListWidget *); + virtual ~KisPaletteListWidgetPrivate(); + + bool allowModification; + + QPointer c; + + QSharedPointer > rAdapter; + QSharedPointer itemChooser; + + QScopedPointer model; + QScopedPointer delegate; + + QScopedPointer actAdd; + QScopedPointer actImport; + QScopedPointer actExport; + QScopedPointer actModify; + QScopedPointer actRemove; +}; + +class KisPaletteListWidgetPrivate::Delegate : public QAbstractItemDelegate +{ +public: + Delegate(QObject *); + virtual ~Delegate(); + void paint(QPainter * painter, + const QStyleOptionViewItem & option, + const QModelIndex & index) const override; + QSize sizeHint(const QStyleOptionViewItem & option, const QModelIndex &) const override; +}; + +class KisPaletteListWidgetPrivate::Model : public KoResourceModel +{ +public: + Model(const QSharedPointer > &rAdapter, QObject *parent = Q_NULLPTR) + : KoResourceModel(rAdapter, parent) + { } + ~Model() override { } + + Qt::ItemFlags flags(const QModelIndex &index) const override + { return KoResourceModel::flags(index) | Qt::ItemIsUserCheckable; } +}; +#endif // KISPALETTELISTWIDGET_P_H diff --git a/libs/widgets/KisPaletteModel.h b/libs/widgets/KisPaletteModel.h --- a/libs/widgets/KisPaletteModel.h +++ b/libs/widgets/KisPaletteModel.h @@ -22,6 +22,7 @@ #include #include +#include #include @@ -30,71 +31,98 @@ #include class KoColorSet; +class KisPaletteView; /** * @brief The KisPaletteModel class - * This, together with kis_palette_view and kis_palette_delegate forms a mvc way to access kocolorsets. + * This, together with KisPaletteView and KisPaletteDelegate forms a mvc way to access kocolorsets. + * A display renderer is given to this model to convert KoColor to QColor when + * colors are requested */ class KRITAWIDGETS_EXPORT KisPaletteModel : public QAbstractTableModel { Q_OBJECT public: - KisPaletteModel(QObject* parent = 0); + explicit KisPaletteModel(QObject* parent = Q_NULLPTR); ~KisPaletteModel() override; enum AdditionalRoles { - IsHeaderRole = Qt::UserRole + 1, - ExpandCategoryRole = Qt::UserRole + 2, - RetrieveEntryRole = Qt::UserRole + 3 + IsGroupNameRole = Qt::UserRole + 1, + CheckSlotRole, + GroupNameRole, + RowInGroupRole }; +public /* overriden methods */: // QAbstractTableModel QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override; int rowCount(const QModelIndex& parent = QModelIndex()) const override; int columnCount(const QModelIndex& parent = QModelIndex()) const override; - Qt::ItemFlags flags(const QModelIndex& index) const override; + /** + * @brief index + * @param row + * @param column + * @param parent + * @return the index of for the data at row, column + * if the data is a color entry, the internal pointer points to the group + * the entry belongs to, and the row and column are row number and column + * number inside the group. + * if the data is a group, the row number and group number is Q_INFINIFY, + * and the internal pointer also points to the group + */ QModelIndex index(int row, int column, const QModelIndex& parent = QModelIndex()) const override; - void setColorSet(KoColorSet* colorSet); - KoColorSet* colorSet() const; + Qt::ItemFlags flags(const QModelIndex& index) const override; /** - * Installs a display renderer object for a palette that will - * convert the KoColor to the displayable QColor. Default is the - * dumb renderer. + * @brief dropMimeData + * This is an overridden function that handles dropped mimedata. + * right now only colorsetentries and colorsetgroups are handled. + * @return */ - void setDisplayRenderer(KoColorDisplayRendererInterface *displayRenderer); - + bool dropMimeData(const QMimeData *data, Qt::DropAction action, + int row, int column, const QModelIndex &parent) override; /** - * @brief indexFromId - * convenience function to get the tableindex from the global palette color. - * used by lazybrush. - * @param i - * @return index in table. + * @brief mimeData + * gives the mimedata for a kocolorsetentry or a kocolorsetgroup. + * @param indexes + * @return the mimedata for the given indices */ - QModelIndex indexFromId(int i) const; + QMimeData *mimeData(const QModelIndexList &indexes) const override; + + QStringList mimeTypes() const override; + + Qt::DropActions supportedDropActions() const override; /** - * @brief idFromIndex - * convenience function to get the global colorset entry id from the table index. - * If you just want to use this to get the kocolorsetentry, use colorsetEntryFromIndex instead. - * @param index - * @return + * @brief setData + * setData is not used as KoColor is not a QVariant + * use setEntry, addEntry and removeEntry instead */ - int idFromIndex(const QModelIndex &index) const; + // TODO Used QVariant::setValue and QVariant.value to implement this + // bool setData(const QModelIndex &index, const QVariant &value, int role) override; +Q_SIGNALS: + /** + * @brief sigPaletteModified + * emitted when palette associated with the model is modified + */ + void sigPaletteModified(); /** - * @brief colorSetEntryFromIndex - * This gives the colorset entry for the given table model index. - * @param index the QModelIndex - * @return the kocolorsetentry + * @brief sigPaletteChanged + * emitted when the palette associated with the model is made another one */ - KoColorSetEntry colorSetEntryFromIndex(const QModelIndex &index) const; + void sigPaletteChanged(); +public /* methods */: /** - * @brief addColorSetEntry + * @brief addEntry * proper function to handle adding entries. * @return whether successful. */ - bool addColorSetEntry(KoColorSetEntry entry, QString groupName=QString()); + bool addEntry(const KisSwatch &entry, + const QString &groupName = KoColorSet::GLOBAL_GROUP_NAME); + + void setEntry(const KisSwatch &entry, const QModelIndex &index); + /** * @brief removeEntry * proper function to remove the colorsetentry at the given index. @@ -104,46 +132,48 @@ * so hence the default is true. * @return if successful */ - bool removeEntry(QModelIndex index, bool keepColors=true); - /** - * @brief addGroup - * Adds a group to the list. - * @param groupName - * @return if successful - */ - bool addGroup(QString groupName = QString()); + bool removeEntry(const QModelIndex &index, bool keepColors=true); + void removeGroup(const QString &groupName, bool keepColors); + bool renameGroup(const QString &groupName, const QString &newName); + void addGroup(const KisSwatchGroup &group); + void setRowNumber(const QString &groupName, int rowCount); + void clear(); - bool removeRows(int row, int count, const QModelIndex &parent) override; + KisSwatch getEntry(const QModelIndex &index) const; - /** - * @brief dropMimeData - * This is an overridden function that handles dropped mimedata. - * right now only colorsetentries and colorsetgroups are handled. - * @return - */ - bool dropMimeData(const QMimeData *data, Qt::DropAction action, - int row, int column, const QModelIndex &parent) override; - /** - * @brief mimeData - * gives the mimedata for a kocolorsetentry or a kocolorsetgroup. - * @param indexes - * @return the mimedata for the given indices - */ - QMimeData *mimeData(const QModelIndexList &indexes) const override; - - QStringList mimeTypes() const override; - - Qt::DropActions supportedDropActions() const override; + void setPalette(KoColorSet* colorSet); + KoColorSet* colorSet() const; + QModelIndex indexForClosest(const KoColor &compare); + int indexRowForInfo(const KisSwatchGroup::SwatchInfo &info); +public Q_SLOTS: private Q_SLOTS: void slotDisplayConfigurationChanged(); + void slotPaletteModified(); + +private /* methods */: + QVariant dataForGroupNameRow(const QModelIndex &idx, int role) const; + QVariant dataForSwatch(const QModelIndex &idx, int role) const; + int rowNumberInGroup(int rowInModel) const; + int groupNameRowForRow(int rowInModel) const; + int groupNameRowForName(const QString &groupName); + void resetGroupNameRows(); + /** + * Installs a display renderer object for a palette that will + * convert the KoColor to the displayable QColor. Default is the + * dumb renderer. + */ + void setDisplayRenderer(const KoColorDisplayRendererInterface *displayRenderer); + + +private /* member variables */: + QPointer m_colorSet; + QPointer m_displayRenderer; + QMap m_rowGroupNameMap; -private: - KoColorSet* m_colorSet; - QPointer m_displayRenderer; - QModelIndex getLastEntryIndex(); +friend class KisPaletteView; }; #endif diff --git a/libs/widgets/KisPaletteModel.cpp b/libs/widgets/KisPaletteModel.cpp --- a/libs/widgets/KisPaletteModel.cpp +++ b/libs/widgets/KisPaletteModel.cpp @@ -1,5 +1,6 @@ /* * Copyright (c) 2013 Sven Langkamp + * Copyright (c) 2018 Michael Zhou * * 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 @@ -31,168 +32,38 @@ #include KisPaletteModel::KisPaletteModel(QObject* parent) - : QAbstractTableModel(parent), - m_colorSet(0), - m_displayRenderer(KoDumbColorDisplayRenderer::instance()) + : QAbstractTableModel(parent) + , m_colorSet(Q_NULLPTR) + , m_displayRenderer(KoDumbColorDisplayRenderer::instance()) { + connect(this, SIGNAL(sigPaletteModified()), SLOT(slotPaletteModified())); } KisPaletteModel::~KisPaletteModel() { } -void KisPaletteModel::setDisplayRenderer(KoColorDisplayRendererInterface *displayRenderer) -{ - if (displayRenderer) { - if (m_displayRenderer) { - disconnect(m_displayRenderer, 0, this, 0); - } - m_displayRenderer = displayRenderer; - connect(m_displayRenderer, SIGNAL(displayConfigurationChanged()), - SLOT(slotDisplayConfigurationChanged())); - } else { - m_displayRenderer = KoDumbColorDisplayRenderer::instance(); - } -} - -void KisPaletteModel::slotDisplayConfigurationChanged() -{ - beginResetModel(); - endResetModel(); -} - -QModelIndex KisPaletteModel::getLastEntryIndex() -{ - int endRow = rowCount(); - int endColumn = columnCount(); - if (m_colorSet->nColors()>0) { - QModelIndex i = this->index(endRow, endColumn, QModelIndex()); - while (qvariant_cast(i.data(RetrieveEntryRole)).isEmpty()) { - i = this->index(endRow, endColumn); - endColumn -=1; - if (endColumn<0) { - endColumn = columnCount(); - endRow-=1; - } - } - return i; - } - return QModelIndex(); -} - 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()); - } - 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 (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; - } - 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 { - if (indexInGroup < m_colorSet->nColorsGroup(indexGroupName)) { - entry = m_colorSet->getColorGroup(indexInGroup, 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; - } - } - } - } + if (!index.isValid()) { return QVariant(); } + bool groupNameRow = m_rowGroupNameMap.contains(index.row()); + if (role == IsGroupNameRole) { + return groupNameRow; + } + if (groupNameRow) { + return dataForGroupNameRow(index, role); + } else { + return dataForSwatch(index, role); } - 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; + return m_colorSet->rowCount() // count of color rows + + m_rowGroupNameMap.size() // rows for names + - 1; // global doesn't have a name } int KisPaletteModel::columnCount(const QModelIndex& /*parent*/) const @@ -200,66 +71,54 @@ 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::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) { + Q_UNUSED(parent); + Q_ASSERT(m_colorSet); + int groupNameRow = groupNameRowForRow(row); + KisSwatchGroup *group = m_colorSet->getGroup(m_rowGroupNameMap[groupNameRow]); + Q_ASSERT(group); + return createIndex(row, column, group); +} - //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); - } - //add the new rows to the totalrows we've looked at. - rowstotal += newrows; - } +void KisPaletteModel::resetGroupNameRows() +{ + m_rowGroupNameMap.clear(); + int row = -1; + for (const QString &groupName : m_colorSet->getGroupNames()) { + m_rowGroupNameMap[row] = groupName; + row += m_colorSet->getGroup(groupName)->rowCount(); + row += 1; // row for group name } - return QModelIndex(); } -void KisPaletteModel::setColorSet(KoColorSet* colorSet) +void KisPaletteModel::setPalette(KoColorSet* palette) { - m_colorSet = colorSet; beginResetModel(); + m_colorSet = palette; + if (palette) { + resetGroupNameRows(); + } endResetModel(); + emit sigPaletteChanged(); } KoColorSet* KisPaletteModel::colorSet() const @@ -267,171 +126,79 @@ return m_colorSet; } -QModelIndex KisPaletteModel::indexFromId(int i) const -{ - 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 +int KisPaletteModel::rowNumberInGroup(int rowInModel) const { - if (index.isValid()==false) { - return -1; - qWarning()<<"invalid index"; - } - int i=0; - QStringList entryList = qvariant_cast(data(index, RetrieveEntryRole)); - if (entryList.isEmpty()) { + if (m_rowGroupNameMap.contains(rowInModel)) { 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)); + QList rowNumberList = m_rowGroupNameMap.keys(); + for (auto it = rowNumberList.rbegin(); it != rowNumberList.rend(); it++) { + if (*it < rowInModel) { + return rowInModel - *it - 1; + } } - //then add the index. - i += entryList.at(1).toUInt(); - return i; + return rowInModel; } -KoColorSetEntry KisPaletteModel::colorSetEntryFromIndex(const QModelIndex &index) const +int KisPaletteModel::groupNameRowForName(const QString &groupName) { - KoColorSetEntry blank = KoColorSetEntry(); - if (!index.isValid()) { - return blank; - } - QStringList entryList = qvariant_cast(data(index, RetrieveEntryRole)); - if (entryList.isEmpty()) { - return blank; + for (auto it = m_rowGroupNameMap.begin(); it != m_rowGroupNameMap.end(); it++) { + if (it.value() == groupName) { + return it.key(); + } } - QString groupName = entryList.at(0); - quint32 indexInGroup = entryList.at(1).toUInt(); - return m_colorSet->getColorGroup(indexInGroup, groupName); + return -1; } -bool KisPaletteModel::addColorSetEntry(KoColorSetEntry entry, QString groupName) +bool KisPaletteModel::addEntry(const KisSwatch &entry, const QString &groupName) { - int col = m_colorSet->nColorsGroup(groupName)%columnCount(); - QModelIndex i = getLastEntryIndex(); - if (col+1 > columnCount()) { - beginInsertRows(QModelIndex(), i.row(), i.row()+1); - } - if ((int)m_colorSet->nColors() < columnCount()) { - beginInsertColumns(QModelIndex(), m_colorSet->nColors(), m_colorSet->nColors()+1); - } + beginInsertRows(QModelIndex(), rowCount(), rowCount() + 1); m_colorSet->add(entry, groupName); - if (col + 1 > columnCount()) { - endInsertRows(); - } - if (m_colorSet->nColors() < (quint32)columnCount()) { - endInsertColumns(); + endInsertRows(); + if (m_colorSet->isGlobal()) { + m_colorSet->save(); } + emit sigPaletteModified(); return true; } -bool KisPaletteModel::removeEntry(QModelIndex index, bool keepColors) +bool KisPaletteModel::removeEntry(const QModelIndex &index, bool keepColors) { - 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(); - } + if (!qvariant_cast(data(index, IsGroupNameRole))) { + static_cast(index.internalPointer())->removeEntry(index.column(), + rowNumberInGroup(index.row())); + emit dataChanged(index, index); } else { - beginRemoveRows(QModelIndex(), index.row(), index.row()-1); - m_colorSet->removeGroup(groupName, keepColors); - endRemoveRows(); + int groupNameRow = groupNameRowForRow(index.row()); + QString groupName = m_rowGroupNameMap[groupNameRow]; + removeGroup(groupName, keepColors); } + emit sigPaletteModified(); return true; } -bool KisPaletteModel::addGroup(QString groupName) +void KisPaletteModel::removeGroup(const QString &groupName, bool keepColors) { - QModelIndex i = getLastEntryIndex(); - beginInsertRows(QModelIndex(), i.row(), i.row()+1); - m_colorSet->addGroup(groupName); - endInsertRows(); - 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 - + int removeStart = groupNameRowForName(groupName); + int removedRowCount = m_colorSet->getGroup(groupName)->rowCount(); + int insertStart = m_colorSet->getGlobalGroup()->rowCount(); + beginRemoveRows(QModelIndex(), + removeStart, + removeStart + removedRowCount); + m_colorSet->removeGroup(groupName, keepColors); + resetGroupNameRows(); endRemoveRows(); - return true; + beginInsertRows(QModelIndex(), + insertStart, m_colorSet->getGlobalGroup()->rowCount()); + endInsertRows(); + emit sigPaletteModified(); } bool KisPaletteModel::dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) { + Q_UNUSED(row); + Q_UNUSED(column); if (!data->hasFormat("krita/x-colorsetentry") && !data->hasFormat("krita/x-colorsetgroup")) { return false; } @@ -439,143 +206,86 @@ 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()); - } + QModelIndex finalIndex = parent; + if (!finalIndex.isValid()) { return false; } if (data->hasFormat("krita/x-colorsetgroup")) { + // dragging group not supported for now 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); + QString groupNameDroppedOn = qvariant_cast(finalIndex.data(GroupNameRole)); + if (groupNameDroppedOn == KoColorSet::GLOBAL_GROUP_NAME) { + return false; + } + QString groupNameDragged; + stream >> groupNameDragged; + KisSwatchGroup *groupDragged = m_colorSet->getGroup(groupNameDragged); + int start = groupNameRowForName(groupNameDragged); + int end = start + groupDragged->rowCount(); + if (!beginMoveRows(QModelIndex(), start, end, QModelIndex(), groupNameRowForName(groupNameDroppedOn))) { + return false; + } + m_colorSet->moveGroup(groupNameDragged, groupNameDroppedOn); + resetGroupNameRows(); + endMoveRows(); + emit sigPaletteModified(); + if (m_colorSet->isGlobal()) { m_colorSet->save(); - endMoveRows(); - - ++endRow; } } - } else { - QByteArray encodedData = data->data("krita/x-colorsetentry"); - QDataStream stream(&encodedData, QIODevice::ReadOnly); + return true; + } - 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); + if (qvariant_cast(finalIndex.data(KisPaletteModel::IsGroupNameRole))) { + return true; + } - 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)); - } + QByteArray encodedData = data->data("krita/x-colorsetentry"); + QDataStream stream(&encodedData, QIODevice::ReadOnly); + + while (!stream.atEnd()) { + KisSwatch entry; + + QString name, id; + bool spotColor; + QString oldGroupName; + int oriRow; + int oriColumn; + QString colorXml; + + stream >> name >> id >> spotColor + >> oriRow >> oriColumn + >> 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)); - } + if (action == Qt::MoveAction){ + KisSwatchGroup *g = m_colorSet->getGroup(oldGroupName); + if (g) { + if (qvariant_cast(finalIndex.data(KisPaletteModel::CheckSlotRole))) { + g->setEntry(getEntry(finalIndex), oriColumn, oriRow); } 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); + g->removeEntry(oriColumn, oriRow); } + } + setEntry(entry, finalIndex); + emit sigPaletteModified(); + if (m_colorSet->isGlobal()) { 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; } } } @@ -589,20 +299,11 @@ 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(); - } + if (index.isValid() && qvariant_cast(index.data(CheckSlotRole))) { + QString mimeTypeName = "krita/x-colorsetentry"; + if (qvariant_cast(index.data(IsGroupNameRole))==false) { + KisSwatch entry = getEntry(index); QDomDocument doc; QDomElement root = doc.createElement("Color"); @@ -610,24 +311,18 @@ doc.appendChild(root); entry.color().toXML(doc, root); - stream << entry.name() - << entry.id() - << entry.spotColor() - << indexInGroup - << groupName + stream << entry.name() << entry.id() << entry.spotColor() + << rowNumberInGroup(index.row()) << index.column() + << qvariant_cast(index.data(GroupNameRole)) << doc.toString(); } else { mimeTypeName = "krita/x-colorsetgroup"; - QStringList entryList = qvariant_cast(index.data(RetrieveEntryRole)); - QString groupName = QString(); - if (!entryList.isEmpty()) { - groupName = entryList.at(0); - } + QString groupName = qvariant_cast(index.data(GroupNameRole)); stream << groupName; } + mimeData->setData(mimeTypeName, encodedData); } - mimeData->setData(mimeTypeName, encodedData); return mimeData; } @@ -640,3 +335,174 @@ { return Qt::MoveAction; } + +void KisPaletteModel::setEntry(const KisSwatch &entry, + const QModelIndex &index) +{ + KisSwatchGroup *group = static_cast(index.internalPointer()); + Q_ASSERT(group); + group->setEntry(entry, index.column(), rowNumberInGroup(index.row())); + emit sigPaletteModified(); + emit dataChanged(index, index); + if (m_colorSet->isGlobal()) { + m_colorSet->save(); + } +} + +bool KisPaletteModel::renameGroup(const QString &groupName, const QString &newName) +{ + beginResetModel(); + bool success = m_colorSet->changeGroupName(groupName, newName); + for (auto it = m_rowGroupNameMap.begin(); it != m_rowGroupNameMap.end(); it++) { + if (it.value() == groupName) { + m_rowGroupNameMap[it.key()] = newName; + break; + } + } + endResetModel(); + emit sigPaletteModified(); + return success; +} + +void KisPaletteModel::addGroup(const KisSwatchGroup &group) +{ + beginInsertRows(QModelIndex(), rowCount(), rowCount() + group.rowCount()); + m_colorSet->addGroup(group.name()); + *m_colorSet->getGroup(group.name()) = group; + endInsertColumns(); + + emit sigPaletteModified(); +} + +void KisPaletteModel::setRowNumber(const QString &groupName, int rowCount) +{ + beginResetModel(); + KisSwatchGroup *g = m_colorSet->getGroup(groupName); + if (g) { + g->setRowCount(rowCount); + } + endResetModel(); +} + +void KisPaletteModel::clear() +{ + beginResetModel(); + m_colorSet->clear(); + endResetModel(); +} + +QVariant KisPaletteModel::dataForGroupNameRow(const QModelIndex &idx, int role) const +{ + KisSwatchGroup *group = static_cast(idx.internalPointer()); + Q_ASSERT(group); + QString groupName = group->name(); + switch (role) { + case Qt::ToolTipRole: + case Qt::DisplayRole: { + return groupName; + } + case GroupNameRole: { + return groupName; + } + case CheckSlotRole: { + return true; + } + case RowInGroupRole: { + return -1; + } + default: { + return QVariant(); + } + } +} + +QVariant KisPaletteModel::dataForSwatch(const QModelIndex &idx, int role) const +{ + KisSwatchGroup *group = static_cast(idx.internalPointer()); + Q_ASSERT(group); + int rowInGroup = rowNumberInGroup(idx.row()); + bool entryPresent = group->checkEntry(idx.column(), rowInGroup); + KisSwatch entry; + if (entryPresent) { + entry = group->getEntry(idx.column(), rowInGroup); + } + switch (role) { + case Qt::ToolTipRole: + case Qt::DisplayRole: { + return entryPresent ? entry.name() : i18n("Empty slot"); + } + case Qt::BackgroundRole: { + QColor color(0, 0, 0, 0); + if (entryPresent) { + color = m_displayRenderer->toQColor(entry.color()); + } + return QBrush(color); + } + case GroupNameRole: { + return group->name(); + } + case CheckSlotRole: { + return entryPresent; + } + case RowInGroupRole: { + return rowInGroup; + } + default: { + return QVariant(); + } + } +} + +void KisPaletteModel::setDisplayRenderer(const KoColorDisplayRendererInterface *displayRenderer) +{ + if (displayRenderer) { + if (m_displayRenderer) { + disconnect(m_displayRenderer, 0, this, 0); + } + m_displayRenderer = displayRenderer; + connect(m_displayRenderer, SIGNAL(displayConfigurationChanged()), + SLOT(slotDisplayConfigurationChanged())); + } else { + m_displayRenderer = KoDumbColorDisplayRenderer::instance(); + } +} + +void KisPaletteModel::slotDisplayConfigurationChanged() +{ + beginResetModel(); + endResetModel(); +} + +void KisPaletteModel::slotPaletteModified() { + m_colorSet->setPaletteType(KoColorSet::KPL); +} + +QModelIndex KisPaletteModel::indexForClosest(const KoColor &compare) +{ + KisSwatchGroup::SwatchInfo info = colorSet()->getClosestColorInfo(compare); + return createIndex(indexRowForInfo(info), info.column, colorSet()->getGroup(info.group)); +} + +int KisPaletteModel::indexRowForInfo(const KisSwatchGroup::SwatchInfo &info) +{ + for (auto it = m_rowGroupNameMap.begin(); it != m_rowGroupNameMap.end(); it++) { + if (it.value() == info.group) { + return it.key() + info.row + 1; + } + } + return info.row; +} + +KisSwatch KisPaletteModel::getEntry(const QModelIndex &index) const +{ + KisSwatchGroup *group = static_cast(index.internalPointer()); + if (!group || !group->checkEntry(index.column(), rowNumberInGroup(index.row()))) { + return KisSwatch(); + } + return group->getEntry(index.column(), rowNumberInGroup(index.row())); +} + +int KisPaletteModel::groupNameRowForRow(int rowInModel) const +{ + return rowInModel - rowNumberInGroup(rowInModel) - 1; +} diff --git a/libs/widgets/KoColorPopupAction.h b/libs/widgets/KoColorPopupAction.h --- a/libs/widgets/KoColorPopupAction.h +++ b/libs/widgets/KoColorPopupAction.h @@ -78,7 +78,6 @@ void colorWasSelected(const KoColor &color, bool final); void colorWasEdited( const QColor &color ); void opacityWasChanged( int opacity ); - void slotTriggered(bool); private: class KoColorPopupActionPrivate; diff --git a/libs/widgets/KoColorPopupAction.cpp b/libs/widgets/KoColorPopupAction.cpp --- a/libs/widgets/KoColorPopupAction.cpp +++ b/libs/widgets/KoColorPopupAction.cpp @@ -113,12 +113,12 @@ connect(this, SIGNAL(triggered()), this, SLOT(emitColorChanged())); - connect(d->colorSetWidget, SIGNAL(colorChanged(const KoColor &, bool)), this, SLOT(colorWasSelected(const KoColor &, bool))); - - connect( d->colorChooser, SIGNAL( colorChanged( const QColor &) ), - this, SLOT( colorWasEdited( const QColor &) ) ); - connect( d->opacitySlider, SIGNAL(valueChanged(int)), - this, SLOT(opacityWasChanged(int))); + connect(d->colorSetWidget, SIGNAL(colorChanged(const KoColor &, bool)), + this, SLOT(colorWasSelected(const KoColor &, bool))); + connect(d->colorChooser, SIGNAL(colorChanged(const QColor &)), + this, SLOT(colorWasEdited( const QColor &))); + connect(d->opacitySlider, SIGNAL(valueChanged(int)), + this, SLOT(opacityWasChanged(int))); } KoColorPopupAction::~KoColorPopupAction() @@ -245,15 +245,3 @@ emitColorChanged(); } - -void KoColorPopupAction::slotTriggered(bool) -{ - if (d->firstTime) { - KoResourceServer* srv = KoResourceServerProvider::instance()->paletteServer(); - QList palettes = srv->resources(); - if (!palettes.empty()) { - d->colorSetWidget->setColorSet(palettes.first()); - } - d->firstTime = false; - } -} diff --git a/libs/widgets/KoColorSetWidget.h b/libs/widgets/KoColorSetWidget.h --- a/libs/widgets/KoColorSetWidget.h +++ b/libs/widgets/KoColorSetWidget.h @@ -24,11 +24,14 @@ #include #include -#include "kritawidgets_export.h" +#include #include +#include "kritawidgets_export.h" + class KoColor; class KoColorSet; +class KoColorPatch; /** * @short A colormanaged widget for choosing a color from a colorset @@ -95,11 +98,21 @@ */ void widgetSizeChanged(const QSize &size); -private: - Q_PRIVATE_SLOT(d, void colorTriggered(KoColorPatch *)) - Q_PRIVATE_SLOT(d, void addRemoveColors()) - Q_PRIVATE_SLOT(d, void setColorFromString(QString s)) +private Q_SLOTS: + /** + * @brief slotPatchTriggered + * Triggered when a recent patch is triggered + */ + void slotPatchTriggered(KoColorPatch *); + /** + * @brief slotEntrySelected + * Triggered when a color is choose from the palette view + */ + void slotColorSelectedByPalette(const KoColor &color); + void slotPaletteChoosen(KoColorSet *); + void slotNameListSelection(const KoColor &); +private: class KoColorSetWidgetPrivate; KoColorSetWidgetPrivate * const d; }; diff --git a/libs/widgets/KoColorSetWidget.cpp b/libs/widgets/KoColorSetWidget.cpp --- a/libs/widgets/KoColorSetWidget.cpp +++ b/libs/widgets/KoColorSetWidget.cpp @@ -20,7 +20,6 @@ #include "KoColorSetWidget.h" #include "KoColorSetWidget_p.h" -#include #include #include #include @@ -41,146 +40,25 @@ #include #include -#include #include -#include #include #include #include +#include -void KoColorSetWidget::KoColorSetWidgetPrivate::fillColors() -{ - delete colorSetContainer; - colorSetContainer = new QWidget(); - colorSetLayout = new QVBoxLayout(); - colorSetLayout->setMargin(3); - colorSetLayout->setSpacing(0); // otherwise the use can click where there is none - colorSetContainer->setBackgroundRole(QPalette::Dark); - - int patchSize = 12; - - int columns = 16; - if (colorSet) { - columns = colorSet->columnCount(); - } - colorSetContainer->setMinimumWidth(columns*patchSize+6); - colorSetContainer->setLayout(colorSetLayout); - patchWidgetList.clear(); - colornames.clear(); - colorNameCmb->clear(); - - QWidget *defaultGroupContainer = new QWidget(); - QGridLayout *colorGroupLayout = new QGridLayout(); - for(int i = 0; isetColumnMinimumWidth(i, patchSize); - } - defaultGroupContainer->setMinimumWidth(columns*patchSize); - defaultGroupContainer->setMaximumWidth(columns*patchSize); - colorGroupLayout->setSpacing(0); - colorGroupLayout->setMargin(0); - defaultGroupContainer->setLayout(colorGroupLayout); - if (colorSet) { - for( quint32 i = 0, p= 0; i < colorSet->nColorsGroup(); i++) { - KoColorPatch *patch = new KoColorPatch(colorSetContainer); - patch->setFrameStyle(QFrame::Plain | QFrame::Box); - patch->setLineWidth(1); - KoColorSetEntry c = colorSet->getColorGlobal(i); - patch->setColor(c.color()); - patch->setToolTip(c.name()); - connect(patch, SIGNAL(triggered(KoColorPatch *)), thePublic, SLOT(colorTriggered(KoColorPatch *))); - colorGroupLayout->addWidget(patch, p/columns, p%columns); - patch->setDisplayRenderer(displayRenderer); - patchWidgetList.append(patch); - colornames.append(c.name()); - QPixmap colorsquare = QPixmap(12,12); - colorsquare.fill(c.color().toQColor()); - colorNameCmb->addItem(QIcon(colorsquare), c.name()); - ++p; - } - colorSetLayout->addWidget(defaultGroupContainer); - Q_FOREACH(QString groupName, colorSet->getGroupNames()) { - QGroupBox *groupbox = new QGroupBox(); - groupbox->setTitle(groupName); - QGridLayout *groupLayout = new QGridLayout(); - for(int i = 0; isetColumnMinimumWidth(i, patchSize); - } - groupLayout->setSpacing(0); - groupLayout->setMargin(0); - groupbox->setMinimumWidth(columns*patchSize); - groupbox->setMaximumWidth(columns*patchSize); - groupbox->setLayout(groupLayout); - groupbox->setFlat(true); - for( quint32 i = 0, p= 0; i < colorSet->nColorsGroup(groupName); i++) { - KoColorPatch *patch = new KoColorPatch(colorSetContainer); - patch->setFrameStyle(QFrame::Plain | QFrame::Box); - patch->setLineWidth(1); - KoColorSetEntry c = colorSet->getColorGroup(i, groupName); - patch->setColor(c.color()); - patch->setToolTip(c.name()); - connect(patch, SIGNAL(triggered(KoColorPatch *)), thePublic, SLOT(colorTriggered(KoColorPatch *))); - groupLayout->addWidget(patch, p/columns, p%columns); - patch->setDisplayRenderer(displayRenderer); - patchWidgetList.append(patch); - colornames.append(c.name()); - QPixmap colorsquare = QPixmap(12,12); - colorsquare.fill(c.color().toQColor()); - colorNameCmb->addItem(QIcon(colorsquare), c.name()); - ++p; - } - colorSetLayout->addWidget(groupbox); - } - } - - scrollArea->setWidget(colorSetContainer); - connect(colorNameCmb, SIGNAL(activated(QString)), thePublic, SLOT(setColorFromString(QString)), Qt::UniqueConnection); -} - -void KoColorSetWidget::KoColorSetWidgetPrivate::addRemoveColors() -{ - KoResourceServer* srv = KoResourceServerProvider::instance()->paletteServer(); - QList palettes = srv->resources(); - - Q_ASSERT(colorSet); - KoEditColorSetDialog *dlg = new KoEditColorSetDialog(palettes, colorSet->name(), thePublic); - if (dlg->exec() == KoDialog::Accepted ) { // always reload the color set - KoColorSet * cs = dlg->activeColorSet(); - // check if the selected colorset is predefined - if( cs && !palettes.contains( cs ) ) { - int i = 1; - QFileInfo fileInfo; - QString savePath = srv->saveLocation(); - - do { - fileInfo.setFile(savePath + QString("%1.%2").arg(i++, 4, 10, QChar('0')).arg(colorSet->defaultFileExtension())); - } - while (fileInfo.exists()); - - cs->setFilename( fileInfo.filePath() ); - cs->setValid( true ); - - // add new colorset to predefined colorsets - if (!srv->addResource(cs)) { - - delete cs; - cs = 0; - } - } - if (cs) { - thePublic->setColorSet(cs); - } - } - delete dlg; -} +#include +#include +#include +#include void KoColorSetWidget::KoColorSetWidgetPrivate::addRecent(const KoColor &color) { - if(numRecents<6) { + if(numRecents < 6) { recentPatches[numRecents] = new KoColorPatch(thePublic); recentPatches[numRecents]->setFrameShape(QFrame::StyledPanel); recentPatches[numRecents]->setDisplayRenderer(displayRenderer); - recentsLayout->insertWidget(numRecents+1, recentPatches[numRecents]); - connect(recentPatches[numRecents], SIGNAL(triggered(KoColorPatch *)), thePublic, SLOT(colorTriggered(KoColorPatch *))); + recentsLayout->insertWidget(numRecents + 1, recentPatches[numRecents]); + connect(recentPatches[numRecents], SIGNAL(triggered(KoColorPatch *)), thePublic, SLOT(slotPatchTriggered(KoColorPatch *))); numRecents++; } // shift colors to the right @@ -205,22 +83,12 @@ KoColorSetWidget::KoColorSetWidget(QWidget *parent) : QFrame(parent) - ,d(new KoColorSetWidgetPrivate()) + , d(new KoColorSetWidgetPrivate()) { d->thePublic = this; - d->colorSet = 0; - - d->firstShowOfContainer = true; - - d->mainLayout = new QVBoxLayout(); - d->mainLayout->setMargin(4); - d->mainLayout->setSpacing(2); - - d->colorSetContainer = 0; d->numRecents = 0; - d->recentsLayout = new QHBoxLayout(); - d->mainLayout->addLayout(d->recentsLayout); + d->recentsLayout = new QHBoxLayout; d->recentsLayout->setMargin(0); d->recentsLayout->addWidget(new QLabel(i18n("Recent:"))); d->recentsLayout->addStretch(1); @@ -229,77 +97,62 @@ color.fromQColor(QColor(128,0,0)); d->addRecent(color); - d->scrollArea = new QScrollArea(); - d->scrollArea->setBackgroundRole(QPalette::Dark); - d->mainLayout->addWidget(d->scrollArea); - d->colorNameCmb = new QComboBox(this); - d->colorNameCmb->setEditable(true); - d->colorNameCmb->setInsertPolicy(QComboBox::NoInsert); - d->mainLayout->addWidget(d->colorNameCmb); - d->fillColors(); + d->paletteView = new KisPaletteView(this); + KisPaletteModel *paletteModel = new KisPaletteModel(d->paletteView); + d->paletteView->setPaletteModel(paletteModel); + d->paletteView->setDisplayRenderer(d->displayRenderer); + + d->paletteChooser = new KisPaletteListWidget(this); + d->paletteChooserButton = new KisPopupButton(this); + d->paletteChooserButton->setPopupWidget(d->paletteChooser); + d->paletteChooserButton->setIcon(KisIconUtils::loadIcon("hi16-palette_library")); + d->paletteChooserButton->setToolTip(i18n("Choose palette")); + d->colorNameCmb = new KisPaletteComboBox(this); + d->colorNameCmb->setCompanionView(d->paletteView); - d->addRemoveButton = new QToolButton(this); - d->addRemoveButton->setText(i18n("Add / Remove Colors...")); - d->addRemoveButton->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); - connect(d->addRemoveButton, SIGNAL(clicked()), SLOT(addRemoveColors())); - d->mainLayout->addWidget(d->addRemoveButton); + d->bottomLayout = new QHBoxLayout; + d->bottomLayout->addWidget(d->paletteChooserButton); + d->bottomLayout->addWidget(d->colorNameCmb); + d->bottomLayout->setStretch(0, 0); // minimize chooser button + d->bottomLayout->setStretch(1, 1); // maximize color name cmb + + d->mainLayout = new QVBoxLayout(this); + d->mainLayout->setMargin(4); + d->mainLayout->setSpacing(2); + d->mainLayout->addLayout(d->recentsLayout); + d->mainLayout->addWidget(d->paletteView); + d->mainLayout->addLayout(d->bottomLayout); setLayout(d->mainLayout); - KoColorSet *colorSet = new KoColorSet(); - d->colorSet = colorSet; - d->fillColors(); + connect(d->paletteChooser, SIGNAL(sigPaletteSelected(KoColorSet*)), + SLOT(slotPaletteChoosen(KoColorSet*))); + connect(d->paletteView, SIGNAL(sigColorSelected(KoColor)), + SLOT(slotColorSelectedByPalette(KoColor))); + connect(d->colorNameCmb, SIGNAL(sigColorSelected(KoColor)), + SLOT(slotNameListSelection(KoColor))); + + d->rServer = KoResourceServerProvider::instance()->paletteServer(); + QPointer defaultColorSet = d->rServer->resourceByName("Default"); + if (!defaultColorSet && d->rServer->resources().count() > 0) { + defaultColorSet = d->rServer->resources().first(); + } + setColorSet(defaultColorSet); } KoColorSetWidget::~KoColorSetWidget() { - KoResourceServer* srv = KoResourceServerProvider::instance()->paletteServer(); - QList palettes = srv->resources(); - if (!palettes.contains(d->colorSet)) { - delete d->colorSet; - } delete d; } -void KoColorSetWidget::KoColorSetWidgetPrivate::colorTriggered(KoColorPatch *patch) -{ - int i; - - emit thePublic->colorChanged(patch->color(), true); - - colorNameCmb->setCurrentIndex(colornames.indexOf(QRegExp(patch->toolTip()+"|Fixed"))); - - for (i = 0; i color()); -} - -void KoColorSetWidget::KoColorSetWidgetPrivate::setColorFromString(QString s) -{ - int i = colornames.indexOf(QRegExp(s+"|Fixed")); - i = qMax(i,0); - colorTriggered(patchWidgetList.at(i)); -} - void KoColorSetWidget::setColorSet(QPointer colorSet) { if (!colorSet) return; if (colorSet == d->colorSet) return; - KoResourceServer* srv = KoResourceServerProvider::instance()->paletteServer(); - QList palettes = srv->resources(); - if (!palettes.contains(d->colorSet)) { - delete d->colorSet; - } - + d->paletteView->paletteModel()->setPalette(colorSet.data()); d->colorSet = colorSet; - d->fillColors(); } KoColorSet* KoColorSetWidget::colorSet() @@ -311,9 +164,6 @@ { if (displayRenderer) { d->displayRenderer = displayRenderer; - Q_FOREACH(KoColorPatch *p, d->patchWidgetList) { - p->setDisplayRenderer(displayRenderer); - } for (int i=0; i<6; i++) { if (d->recentPatches[i]) { d->recentPatches[i]->setDisplayRenderer(displayRenderer); @@ -328,6 +178,40 @@ QFrame::resizeEvent(event); } +void KoColorSetWidget::slotColorSelectedByPalette(const KoColor &color) +{ + emit colorChanged(color, true); + d->addRecent(color); +} + +void KoColorSetWidget::slotPatchTriggered(KoColorPatch *patch) +{ + emit colorChanged(patch->color(), true); + + int i; + + for (i = 0; i < d->numRecents; i++) { + if(patch == d->recentPatches[i]) { + d->activateRecent(i); + break; + } + } + + if (i == d->numRecents) { // we didn't find it above + d->addRecent(patch->color()); + } +} + +void KoColorSetWidget::slotPaletteChoosen(KoColorSet *colorSet) +{ + d->colorSet = colorSet; + d->paletteView->paletteModel()->setPalette(colorSet); +} + +void KoColorSetWidget::slotNameListSelection(const KoColor &color) +{ + emit colorChanged(color, true); +} + //have to include this because of Q_PRIVATE_SLOT #include "moc_KoColorSetWidget.cpp" - diff --git a/libs/widgets/KoColorSetWidget_p.h b/libs/widgets/KoColorSetWidget_p.h --- a/libs/widgets/KoColorSetWidget_p.h +++ b/libs/widgets/KoColorSetWidget_p.h @@ -40,39 +40,42 @@ #include #include +#include +#include +#include +#include #include #include class KoColorPatch; +class KisPaletteView; class Q_DECL_HIDDEN KoColorSetWidget::KoColorSetWidgetPrivate { public: KoColorSetWidget *thePublic; QPointer colorSet; - QTimer m_timer; + + KisPaletteView *paletteView; + KisPaletteListWidget *paletteChooser; + KisPopupButton *paletteChooserButton; + QVBoxLayout *mainLayout; - bool firstShowOfContainer; - QWidget *colorSetContainer; - QScrollArea *scrollArea; QVBoxLayout *colorSetLayout; QHBoxLayout *recentsLayout; + QHBoxLayout *bottomLayout; + KoColorPatch *recentPatches[6]; QToolButton *addRemoveButton; - QComboBox *colorNameCmb; - QStringList colornames; + KisPaletteComboBox *colorNameCmb; int numRecents; - void colorTriggered(KoColorPatch *patch); + const KoColorDisplayRendererInterface *displayRenderer; + KoResourceServer *rServer; + void addRecent(const KoColor &); void activateRecent(int i); - void fillColors(); void addRemoveColors(); - void setColorFromString(QString s); - - QList patchWidgetList; - const KoColorDisplayRendererInterface *displayRenderer; - }; #endif diff --git a/libs/widgets/KoEditColorSet.ui b/libs/widgets/KoEditColorSet.ui deleted file mode 100644 --- a/libs/widgets/KoEditColorSet.ui +++ /dev/null @@ -1,88 +0,0 @@ - - KoEditColorSet - - - - 0 - 0 - 173 - 106 - - - - - - - true - - - - - - - QFrame::NoFrame - - - QFrame::Plain - - - - - - - - - QFrame::Box - - - QFrame::Sunken - - - - - - - Add - - - ... - - - - - - - Remove - - - ... - - - - - - - Open palette - - - ... - - - - - - - Save palette - - - ... - - - - - - - - - - diff --git a/libs/widgets/KoEditColorSetDialog.h b/libs/widgets/KoEditColorSetDialog.h deleted file mode 100644 --- a/libs/widgets/KoEditColorSetDialog.h +++ /dev/null @@ -1,105 +0,0 @@ -/* This file is part of the KDE project - * Copyright (C) 2007 Fredy Yanardi - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public License - * along with this library; see the file COPYING.LIB. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301, USA. - */ - -#ifndef KOEDITCOLORSET_H -#define KOEDITCOLORSET_H - -#include - -#include - -#include "kritawidgets_export.h" - -class QGridLayout; -class QScrollArea; -class KoColorPatch; -class KoColorSet; - -class KoEditColorSetWidget : public QWidget -{ - Q_OBJECT -public: - KoEditColorSetWidget(const QList &palettes, const QString &activePalette, QWidget *parent = 0); - ~KoEditColorSetWidget() override; - - /** - * Return the active color set. The caller takes ownership of that color set. - */ - KoColorSet *activeColorSet(); - -private Q_SLOTS: - void setActiveColorSet(int index); - void setTextLabel(KoColorPatch *patch); - void addColor(); - void removeColor(); - void open(); - void save(); - -private: - Ui::KoEditColorSet widget; - QList m_colorSets; - QGridLayout *m_gridLayout; - QScrollArea *m_scrollArea; - KoColorSet *m_activeColorSet; - KoColorPatch *m_activePatch; - uint m_initialColorSetCount; - bool m_activeColorSetRequested; -}; - -/** - * A dialog for editing palettes/color sets in an application. Example use of this dialog is in text color toolbar, - * the toolbar brings a set of colors from one palette, and a button brings this dialog for editing palettes. - * This dialog is able to: - * - Set active palette from a combobox - * - Add/remove color from a palette - * - Open new palette from a gimp palette file (.gpl) - * - Save changes to the file - * @see KoColorSetWidget - */ -class KRITAWIDGETS_EXPORT KoEditColorSetDialog : public KoDialog -{ - Q_OBJECT - -public: - /** - * Constructs a KoEditColorSetDialog. - * @param palettes all available palettes that are going to be edited. - * @param activePalette name of the palette which will be activated after this dialog is shown. - * @param parent the parent widget - */ - KoEditColorSetDialog(const QList &palettes, const QString &activePalette, QWidget *parent = 0); - - /** - * Returns the last active color set. - * The caller takes ownership of that color set. - * @return the last active KoColorSet in the dialog before the user press OK - */ - KoColorSet *activeColorSet(); - - /** - * Destructor - */ - ~KoEditColorSetDialog() override; - -private: - KoEditColorSetWidget *ui; -}; - -#endif - diff --git a/libs/widgets/KoEditColorSetDialog.cpp b/libs/widgets/KoEditColorSetDialog.cpp deleted file mode 100644 --- a/libs/widgets/KoEditColorSetDialog.cpp +++ /dev/null @@ -1,243 +0,0 @@ -/* This file is part of the KDE project - * Copyright (C) 2007 Fredy Yanardi - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public License - * along with this library; see the file COPYING.LIB. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301, USA. - */ - -#include "KoEditColorSetDialog.h" - -#include - -#include -#include -#include -#include -#include - -#include -#include - -#include -#include -#include -#include - -// debug -#include - -KoEditColorSetWidget::KoEditColorSetWidget(const QList &palettes, const QString &activePalette, QWidget *parent) - : QWidget(parent), - m_colorSets(palettes), - m_gridLayout(0), - m_activeColorSet(0), - m_activePatch(0), - m_initialColorSetCount(palettes.count()), - m_activeColorSetRequested(false) -{ - widget.setupUi(this); - foreach (KoColorSet *colorSet, m_colorSets) { - //colorSet->load(); resources are loaded at startup... - widget.selector->addItem(colorSet->name()); - } - connect(widget.selector, SIGNAL(currentIndexChanged(int)), this, SLOT(setActiveColorSet(int))); - - // A widget that shows all colors from active palette - // FIXME no need to handcode the QScrollArea if designer can add QScrollArea (Qt 4.4?) - m_scrollArea = new QScrollArea(widget.patchesFrame); - - int index = 0; - foreach (KoColorSet *set, m_colorSets) { - if (set->name() == activePalette) { - m_activeColorSet = set; - index = widget.selector->findText(set->name()); - widget.selector->setCurrentIndex(index); - } - } - if (!m_activeColorSet && !palettes.isEmpty()) { - m_activeColorSet = palettes.first(); - index = widget.selector->findText(m_activeColorSet->name()); - } - - int columns = 16; - if(m_activeColorSet) { - columns = m_activeColorSet->columnCount(); - if (columns==0){ - columns = 16; - } - } - m_scrollArea->setMinimumWidth(columns*(12+2)); - - QHBoxLayout *layout = new QHBoxLayout(); - layout->setContentsMargins(0, 0, 0, 0); - layout->addWidget(m_scrollArea); - widget.patchesFrame->setLayout(layout); - - widget.add->setIcon(koIcon("list-add")); - widget.remove->setIcon(koIcon("list-remove")); - widget.open->setIcon(koIcon("document-open")); - widget.save->setIcon(koIcon("document-save")); - - setEnabled(m_activeColorSet != 0); - setActiveColorSet(index); - widget.remove->setEnabled(false); // initially no color selected - - connect(widget.add, SIGNAL(clicked()), this, SLOT(addColor())); - connect(widget.remove, SIGNAL(clicked()), this, SLOT(removeColor())); - connect(widget.open, SIGNAL(clicked()), this, SLOT(open())); - connect(widget.save, SIGNAL(clicked()), this, SLOT(save())); -} - -KoEditColorSetWidget::~KoEditColorSetWidget() -{ - // only delete new color sets - uint colorSetCount = m_colorSets.count(); - for( uint i = m_initialColorSetCount; i < colorSetCount; ++i ) { - KoColorSet * cs = m_colorSets[i]; - // if the active color set was requested by activeColorSet() - // the caller takes ownership and then we do not delete it here - if( cs == m_activeColorSet && m_activeColorSetRequested ) - continue; - delete cs; - } -} - -void KoEditColorSetWidget::setActiveColorSet(int index) -{ - if (m_gridLayout) { - qDeleteAll(m_gridLayout->children()); - delete m_gridLayout; - m_activePatch = 0; - } - - QWidget *wdg = new QWidget(m_scrollArea); - m_gridLayout = new QGridLayout(); - m_gridLayout->setMargin(0); - m_gridLayout->setSpacing(2); - - m_activeColorSet = m_colorSets.value(index); - setEnabled(m_activeColorSet != 0); - int columns = 16; - - if (m_activeColorSet) { - columns = m_activeColorSet->columnCount(); - if (columns==0){columns=16;} - widget.remove->setEnabled(false); - for (quint32 i = 0; i < m_activeColorSet->nColors(); i++) { - KoColorPatch *patch = new KoColorPatch(widget.patchesFrame); - KoColorSetEntry c = m_activeColorSet->getColorGlobal(i); - patch->setColor(c.color()); - patch->setToolTip(c.name()); - connect(patch, SIGNAL(triggered(KoColorPatch *)), this, SLOT(setTextLabel(KoColorPatch *))); - m_gridLayout->addWidget(patch, i/columns, i%columns); - } - } - m_scrollArea->setMinimumWidth(columns*(12+2)); - - wdg->setLayout(m_gridLayout); - m_scrollArea->setWidget(wdg); -} - -void KoEditColorSetWidget::setTextLabel(KoColorPatch *patch) -{ - widget.colorName->setText(patch->toolTip()); - if (m_activePatch) { - m_activePatch->setFrameShape(QFrame::NoFrame); - m_activePatch->setFrameShadow(QFrame::Plain); - } - m_activePatch = patch; - m_activePatch->setFrameShape(QFrame::Panel); - m_activePatch->setFrameShadow(QFrame::Raised); - widget.remove->setEnabled(true); -} - -void KoEditColorSetWidget::addColor() -{ - QColor color; - - color = QColorDialog::getColor(color); - if (color.isValid()) { - KoColorSetEntry newEntry( - KoColor(color, KoColorSpaceRegistry::instance()->rgb8()), - QInputDialog::getText(this, i18n("Add Color To Palette"), i18n("Color name:"))); - KoColorPatch *patch = new KoColorPatch(widget.patchesFrame); - patch->setColor(newEntry.color()); - patch->setToolTip(newEntry.name()); - connect(patch, SIGNAL(triggered(KoColorPatch *)), this, SLOT(setTextLabel(KoColorPatch *))); - Q_ASSERT(m_gridLayout); - Q_ASSERT(m_activeColorSet); - m_gridLayout->addWidget(patch, m_activeColorSet->nColors()/m_activeColorSet->columnCount(), m_activeColorSet->nColors()%m_activeColorSet->columnCount()); - m_activeColorSet->add(newEntry); - } -} - -void KoEditColorSetWidget::removeColor() -{ - Q_ASSERT(m_activeColorSet); - for (quint32 i = 0; i < m_activeColorSet->nColors(); i++) { - KoColorSetEntry c = m_activeColorSet->getColorGlobal(i); - if (m_activePatch->color() == c.color()) { - m_activeColorSet->removeAt(i); - setActiveColorSet(widget.selector->currentIndex()); - break; - } - } -} - -void KoEditColorSetWidget::open() -{ - Q_ASSERT(m_activeColorSet); - KoFileDialog dialog(this, KoFileDialog::OpenFile, "OpenColorSet"); - dialog.setDefaultDir(m_activeColorSet->filename()); - dialog.setMimeTypeFilters(QStringList() << "application/x-gimp-color-palette"); - QString fileName = dialog.filename(); - KoColorSet *colorSet = new KoColorSet(fileName); - colorSet->load(); - m_colorSets.append(colorSet); - widget.selector->addItem(colorSet->name()); - widget.selector->setCurrentIndex(widget.selector->count() - 1); -} - -void KoEditColorSetWidget::save() -{ - Q_ASSERT(m_activeColorSet); - if (!m_activeColorSet->save()) - KMessageBox::error(0, i18n("Cannot write to palette file %1. Maybe it is read-only. ", m_activeColorSet->filename()), i18n("Palette")); -} - -KoColorSet *KoEditColorSetWidget::activeColorSet() -{ - m_activeColorSetRequested = true; - return m_activeColorSet; -} - -KoEditColorSetDialog::KoEditColorSetDialog(const QList &palettes, const QString &activePalette, QWidget *parent) - : KoDialog(parent) -{ - ui = new KoEditColorSetWidget(palettes, activePalette, this); - setMainWidget(ui); - setCaption(i18n("Add/Remove Colors")); - enableButton(KoDialog::Ok, ui->isEnabled()); -} - -KoEditColorSetDialog::~KoEditColorSetDialog() -{ - delete ui; -} - -KoColorSet *KoEditColorSetDialog::activeColorSet() -{ - return ui->activeColorSet(); -} diff --git a/libs/widgets/WdgDlgInternalColorSelector.ui b/libs/widgets/WdgDlgInternalColorSelector.ui --- a/libs/widgets/WdgDlgInternalColorSelector.ui +++ b/libs/widgets/WdgDlgInternalColorSelector.ui @@ -141,7 +141,7 @@ 0 - + 0 @@ -160,7 +160,7 @@ - + 0 @@ -217,11 +217,6 @@ QTableView
kis_palette_view.h
- - SqueezedComboBox - QComboBox -
squeezedcombobox.h
-
KisSpinboxColorSelector QWidget @@ -240,6 +235,11 @@
KoColorPatch.h
1
+ + KisPaletteComboBox + QComboBox +
KisPaletteComboBox.h
+
diff --git a/libs/widgets/WdgPaletteListWidget.ui b/libs/widgets/WdgPaletteListWidget.ui new file mode 100644 --- /dev/null +++ b/libs/widgets/WdgPaletteListWidget.ui @@ -0,0 +1,92 @@ + + + WdgPaletteListWidget + + + + 0 + 0 + 361 + 496 + + + + Form + + + + + + + + + 0 + 0 + + + + + + + + + + + 0 + 0 + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + .. + + + + + + + + .. + + + + + + + + .. + + + + + + + + .. + + + + + + + + + + + diff --git a/libs/widgets/kis_palette_view.h b/libs/widgets/kis_palette_view.h --- a/libs/widgets/kis_palette_view.h +++ b/libs/widgets/kis_palette_view.h @@ -21,35 +21,32 @@ #define __KIS_PALETTE_VIEW_H #include - +#include #include #include #include #include -#include #include #include "kritawidgets_export.h" class KisPaletteModel; class QWheelEvent; +class KoColorDisplayRendererInterface; - -class KRITAWIDGETS_EXPORT KisPaletteView : public KoTableView +class KRITAWIDGETS_EXPORT KisPaletteView : public QTableView { Q_OBJECT +private: + static int MININUM_ROW_HEIGHT; public: - KisPaletteView(QWidget *parent = 0); + explicit KisPaletteView(QWidget *parent = Q_NULLPTR); ~KisPaletteView() override; void setPaletteModel(KisPaletteModel *model); KisPaletteModel* paletteModel() const; - /** - * @brief updateRows - * update the rows so they have the proper columnspanning. - */ - void updateRows(); +public: /** * @brief setAllowModification @@ -58,6 +55,8 @@ */ void setAllowModification(bool allow); + void setDisplayRenderer(const KoColorDisplayRendererInterface *displayRenderer); + /** * @brief setCrossedKeyword * this apparently allows you to set keywords that can cross out colors. @@ -65,55 +64,55 @@ * @param value */ void setCrossedKeyword(const QString &value); + void removeSelectedEntry(); + /** + * @brief selectClosestColor + * select a color that's closest to parameter color + * @param color + */ + void selectClosestColor(const KoColor &color); -public Q_SLOTS: - void paletteModelChanged(); /** * add an entry with a dialog window. + * @warning deprecated. + * kept for compatibility with @ref PaletteView in @ref libkis */ bool addEntryWithDialog(KoColor color); - /** - * @brief addGroupWithDialog - * summons a little dialog to name the new group. - */ - bool addGroupWithDialog(); /** * remove entry with a dialog window.(Necessary for groups. + * @warning deprecated. + * kept for compatibility with @ref PaletteView in @ref libkis */ bool removeEntryWithDialog(QModelIndex index); /** - * This tries to select the closest color in the palette. - * This doesn't update the foreground color, just the visual selection. + * add entry with a dialog window. + * @warning deprecated. + * kept for compatibility with @ref PaletteView in @ref libkis */ - void trySelectClosestColor(KoColor color); + bool addGroupWithDialog(); + Q_SIGNALS: + void sigIndexSelected(const QModelIndex &index); + void sigColorSelected(const KoColor &); + +public Q_SLOTS: /** - * @brief entrySelected - * signals when an entry is selected. - * @param entry the selected entry. + * This tries to select the closest color in the palette. + * This doesn't update the foreground color, just the visual selection. */ - void entrySelected(KoColorSetEntry entry); - void entrySelectedBackGround(KoColorSetEntry entry); - void indexEntrySelected(QModelIndex index); -protected: - void mouseReleaseEvent(QMouseEvent *event) override; - void wheelEvent(QWheelEvent *event) override; + void slotFGColorChanged(const KoColor &); + +private Q_SLOTS: + void slotHorizontalHeaderResized(int, int, int newSize); + void slotAdditionalGuiUpdate(); + void slotCurrentSelectionChanged(const QModelIndex &newCurrent); + +private: + void resizeRows(int newSize); private: struct Private; const QScopedPointer m_d; -private Q_SLOTS: - /** - * @brief entrySelection - * the function that will emit entrySelected when the entry changes. - */ - void entrySelection(bool foreground = true); - /** - * @brief modifyEntry - * function for changing the entry at the given index. - * if modification isn't allow(@see setAllowModification), this does nothing. - */ - void modifyEntry(QModelIndex index); }; #endif /* __KIS_PALETTE_VIEW_H */ diff --git a/libs/widgets/kis_palette_view.cpp b/libs/widgets/kis_palette_view.cpp --- a/libs/widgets/kis_palette_view.cpp +++ b/libs/widgets/kis_palette_view.cpp @@ -30,42 +30,57 @@ #include #include #include - +#include #include +#include -#include "kis_palette_delegate.h" +#include "KisPaletteDelegate.h" #include "KisPaletteModel.h" #include "kis_color_button.h" +#include + +int KisPaletteView::MININUM_ROW_HEIGHT = 10; struct KisPaletteView::Private { - KisPaletteModel *model = nullptr; - bool allowPaletteModification = true; + QPointer model; + bool allowPaletteModification; // if modification is allowed from this widget }; KisPaletteView::KisPaletteView(QWidget *parent) - : KoTableView(parent) + : QTableView(parent) , m_d(new Private) { - setShowGrid(false); - horizontalHeader()->setVisible(false); - verticalHeader()->setVisible(false); - setItemDelegate(new KisPaletteDelegate()); - -// setDragEnabled(true); -// setDragDropMode(QAbstractItemView::InternalMove); - setDropIndicatorShown(true); + m_d->allowPaletteModification = false; - KConfigGroup cfg(KSharedConfig::openConfig()->group("")); - //QPalette pal(palette()); - //pal.setColor(QPalette::Base, cfg.getMDIBackgroundColor()); - //setAutoFillBackground(true); - //setPalette(pal); + setItemDelegate(new KisPaletteDelegate(this)); - int defaultSectionSize = cfg.readEntry("paletteDockerPaletteViewSectionSize", 12); - horizontalHeader()->setDefaultSectionSize(defaultSectionSize); - verticalHeader()->setDefaultSectionSize(defaultSectionSize); - connect(this, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(modifyEntry(QModelIndex))); + setShowGrid(true); + setDropIndicatorShown(true); + setDragDropMode(QAbstractItemView::InternalMove); + setSelectionMode(QAbstractItemView::SingleSelection); + setDragEnabled(false); + setAcceptDrops(false); + + /* + * without this, a cycle might be created: + * the view streches to right border, and this make it need a scroll bar; + * after the bar is added, the view shrinks to the bar, and this makes it + * no longer need the bar any more, and the bar is removed again + */ + setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn); + + // set the size of swatches + horizontalHeader()->setVisible(false); + verticalHeader()->setVisible(false); + horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch); + horizontalHeader()->setMinimumSectionSize(MININUM_ROW_HEIGHT); + verticalHeader()->setSectionResizeMode(QHeaderView::Fixed); + verticalHeader()->setMinimumSectionSize(MININUM_ROW_HEIGHT); + + connect(horizontalHeader(), SIGNAL(sectionResized(int,int,int)), + SLOT(slotHorizontalHeaderResized(int,int,int))); + setAutoFillBackground(true); } KisPaletteView::~KisPaletteView() @@ -83,18 +98,18 @@ bool KisPaletteView::addEntryWithDialog(KoColor color) { - KoDialog *window = new KoDialog(this); + QScopedPointer window(new KoDialog(this)); window->setWindowTitle(i18nc("@title:window", "Add a new Colorset Entry")); - QFormLayout *editableItems = new QFormLayout(window); + QFormLayout *editableItems = new QFormLayout(window.data()); window->mainWidget()->setLayout(editableItems); - QComboBox *cmbGroups = new QComboBox(window); + QComboBox *cmbGroups = new QComboBox(window.data()); QString defaultGroupName = i18nc("Name for default group", "Default"); cmbGroups->addItem(defaultGroupName); cmbGroups->addItems(m_d->model->colorSet()->getGroupNames()); - QLineEdit *lnIDName = new QLineEdit(window); - QLineEdit *lnName = new QLineEdit(window); - KisColorButton *bnColor = new KisColorButton(window); - QCheckBox *chkSpot = new QCheckBox(window); + QLineEdit *lnIDName = new QLineEdit(window.data()); + QLineEdit *lnName = new QLineEdit(window.data()); + KisColorButton *bnColor = new KisColorButton(window.data()); + QCheckBox *chkSpot = new QCheckBox(window.data()); chkSpot->setToolTip(i18nc("@info:tooltip", "A spot color is a color that the printer is able to print without mixing the paints it has available to it. The opposite is called a process color.")); editableItems->addRow(i18n("Group"), cmbGroups); editableItems->addRow(i18n("ID"), lnIDName); @@ -102,8 +117,8 @@ 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)); + lnName->setText(i18nc("Part of a default name for a color","Color")+" " + QString::number(m_d->model->colorSet()->colorCount()+1)); + lnIDName->setText(QString::number(m_d->model->colorSet()->colorCount() + 1)); bnColor->setColor(color); chkSpot->setChecked(false); @@ -112,19 +127,18 @@ if (groupName == defaultGroupName) { groupName = QString(); } - KoColorSetEntry newEntry; + KisSwatch 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(); + m_d->model->addEntry(newEntry, groupName); return true; } + return false; } -// should be move to colorSetChooser bool KisPaletteView::addGroupWithDialog() { KoDialog *window = new KoDialog(); @@ -135,8 +149,9 @@ editableItems->addRow(i18nc("Name for a group", "Name"), lnName); lnName->setText(i18nc("Part of default name for a new group", "Color Group")+""+QString::number(m_d->model->colorSet()->getGroupNames().size()+1)); if (window->exec() == KoDialog::Accepted) { - QString groupName = lnName->text(); - m_d->model->addGroup(groupName); + KisSwatchGroup group; + group.setName(lnName->text()); + m_d->model->addGroup(group); m_d->model->colorSet()->save(); return true; } @@ -145,82 +160,60 @@ bool KisPaletteView::removeEntryWithDialog(QModelIndex index) { - bool keepColors = true; - if (qvariant_cast(index.data(KisPaletteModel::IsHeaderRole))) { - KoDialog *window = new KoDialog(); + bool keepColors = false; + if (qvariant_cast(index.data(KisPaletteModel::IsGroupNameRole))) { + QScopedPointer window(new KoDialog(this)); window->setWindowTitle(i18nc("@title:window","Removing Group")); - QFormLayout *editableItems = new QFormLayout(); - QCheckBox *chkKeep = new QCheckBox(); + QFormLayout *editableItems = new QFormLayout(window.data()); + QCheckBox *chkKeep = new QCheckBox(window.data()); window->mainWidget()->setLayout(editableItems); - editableItems->addRow(i18nc("Shows up when deleting a group","Keep the Colors"), chkKeep); - chkKeep->setChecked(keepColors); - if (window->exec() == KoDialog::Accepted) { - keepColors = chkKeep->isChecked(); - m_d->model->removeEntry(index, keepColors); - m_d->model->colorSet()->save(); - } - } else { - m_d->model->removeEntry(index, keepColors); + editableItems->addRow(i18nc("Shows up when deleting a swatch group", "Keep the Colors"), chkKeep); + if (window->exec() != KoDialog::Accepted) { return false; } + keepColors = chkKeep->isChecked(); + } + m_d->model->removeEntry(index, keepColors); + if (m_d->model->colorSet()->isGlobal()) { m_d->model->colorSet()->save(); } return true; } -void KisPaletteView::trySelectClosestColor(KoColor color) +void KisPaletteView::selectClosestColor(const KoColor &color) { KoColorSet* color_set = m_d->model->colorSet(); - if (!color_set) + if (!color_set) { return; + } //also don't select if the color is the same as the current selection - if (selectedIndexes().size()>0) { - QModelIndex currentI = currentIndex(); - if (!currentI.isValid()) { - currentI = selectedIndexes().last(); - } - if (!currentI.isValid()) { - currentI = selectedIndexes().first(); - } - if (currentI.isValid()) { - if (m_d->model->colorSetEntryFromIndex(currentI).color()==color) { - return; - } - } + if (m_d->model->getEntry(currentIndex()).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; - } - entrySelection(foreground); + selectionModel()->clearSelection(); + QModelIndex index = m_d->model->indexForClosest(color); + selectionModel()->setCurrentIndex(index, QItemSelectionModel::Select); } -void KisPaletteView::paletteModelChanged() +void KisPaletteView::slotFGColorChanged(const KoColor &color) { - updateView(); - updateRows(); + selectClosestColor(color); } void KisPaletteView::setPaletteModel(KisPaletteModel *model) { if (m_d->model) { - disconnect(m_d->model, 0, this, 0); + disconnect(m_d->model, Q_NULLPTR, this, Q_NULLPTR); } 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())); - + slotAdditionalGuiUpdate(); + connect(model, SIGNAL(sigPaletteModified()), + SLOT(slotAdditionalGuiUpdate())); + connect(model, SIGNAL(sigPaletteChanged()), + SLOT(slotAdditionalGuiUpdate())); + + connect(selectionModel(), SIGNAL(currentChanged(QModelIndex, QModelIndex)), + SLOT(slotCurrentSelectionChanged(QModelIndex))); } KisPaletteModel* KisPaletteView::paletteModel() const @@ -228,115 +221,61 @@ return m_d->model; } -void KisPaletteView::updateRows() +void KisPaletteView::setAllowModification(bool allow) { - 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)); - } - } - } + m_d->allowPaletteModification = allow; + setDragEnabled(allow); + setAcceptDrops(allow); } -void KisPaletteView::setAllowModification(bool allow) +void KisPaletteView::slotHorizontalHeaderResized(int, int, int newSize) { - m_d->allowPaletteModification = allow; + resizeRows(newSize); + slotAdditionalGuiUpdate(); } +void KisPaletteView::resizeRows(int newSize) +{ + verticalHeader()->setDefaultSectionSize(newSize); + verticalHeader()->resizeSections(QHeaderView::Fixed); +} -void KisPaletteView::wheelEvent(QWheelEvent *event) +void KisPaletteView::removeSelectedEntry() { - 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); + if (selectedIndexes().size() <= 0) { + return; } + m_d->model->removeEntry(currentIndex()); } -void KisPaletteView::entrySelection(bool foreground) { - QModelIndex index; - if (selectedIndexes().size()<=0) { - return; +void KisPaletteView::slotAdditionalGuiUpdate() +{ + clearSpans(); + resizeRows(verticalHeader()->defaultSectionSize()); + for (int groupNameRowNumber : m_d->model->m_rowGroupNameMap.keys()) { + if (groupNameRowNumber == -1) { continue; } + setSpan(groupNameRowNumber, 0, 1, m_d->model->columnCount()); + setRowHeight(groupNameRowNumber, fontMetrics().lineSpacing() + 6); + verticalHeader()->resizeSection(groupNameRowNumber, fontMetrics().lineSpacing() + 6); } - if (selectedIndexes().last().isValid()) { - index = selectedIndexes().last(); - } else if (selectedIndexes().first().isValid()) { - index = selectedIndexes().first(); - } else { +} + +void KisPaletteView::slotCurrentSelectionChanged(const QModelIndex &newCurrent) +{ + if (!newCurrent.isValid()) { return; } + + emit sigIndexSelected(newCurrent); + if (qvariant_cast(newCurrent.data(KisPaletteModel::IsGroupNameRole))) { 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)); - } + if (qvariant_cast(newCurrent.data(KisPaletteModel::CheckSlotRole))) { + KisSwatch entry = m_d->model->getEntry(newCurrent); + emit sigColorSelected(entry.color()); } } -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(); - } - } - } +void KisPaletteView::setDisplayRenderer(const KoColorDisplayRendererInterface *displayRenderer) +{ + Q_ASSERT(m_d->model); + m_d->model->setDisplayRenderer(displayRenderer); } diff --git a/plugins/dockers/palettedocker/CMakeLists.txt b/plugins/dockers/palettedocker/CMakeLists.txt --- a/plugins/dockers/palettedocker/CMakeLists.txt +++ b/plugins/dockers/palettedocker/CMakeLists.txt @@ -1,4 +1,6 @@ -set(KRITA_PALETTEDOCKER_SOURCES palettedocker.cpp palettedocker_dock.cpp ) +set(KRITA_PALETTEDOCKER_SOURCES + palettedocker.cpp palettedocker_dock.cpp + ) ki18n_wrap_ui(KRITA_PALETTEDOCKER_SOURCES wdgpalettedock.ui diff --git a/plugins/dockers/palettedocker/palettedocker_dock.h b/plugins/dockers/palettedocker/palettedocker_dock.h --- a/plugins/dockers/palettedocker/palettedocker_dock.h +++ b/plugins/dockers/palettedocker/palettedocker_dock.h @@ -22,64 +22,91 @@ #include #include +#include +#include #include +#include +#include +#include #include -#include #include +#include #include #include #include +#include class KisViewManager; class KisCanvasResourceProvider; class KisWorkspaceResource; -class KisColorsetChooser; +class KisPaletteListWidget; class KisPaletteModel; -class Ui_WdgPaletteDock; +class KisPaletteEditor; +class Ui_WdgPaletteDock; -class PaletteDockerDock : public QDockWidget, public KisMainwindowObserver, public KoResourceServerObserver { +class PaletteDockerDock : public QDockWidget, public KisMainwindowObserver +{ Q_OBJECT public: PaletteDockerDock(); ~PaletteDockerDock() override; - QString observerName() override { return "PaletteDockerDock"; } - void setViewManager(KisViewManager* kisview) override; + +public: // QDockWidget void setCanvas(KoCanvasBase *canvas) override; void unsetCanvas() override; -public: // KoResourceServerObserver +public: // KisMainWindowObserver + void setViewManager(KisViewManager* kisview) override; - void unsetResourceServer() override; - void resourceAdded(KoColorSet *) override {} - void removingResource(KoColorSet *resource) override; - void resourceChanged(KoColorSet *resource) override; - void syncTaggedResourceView() override {} - void syncTagAddition(const QString&) override {} - void syncTagRemoval(const QString&) override {} private Q_SLOTS: - void addColorForeground(); - void removeColor(); - void entrySelected(KoColorSetEntry entry); - void entrySelectedBack(KoColorSetEntry entry); - void setColorSet(KoColorSet* colorSet); + void slotContextMenu(const QModelIndex &); + + void slotAddPalette(); + void slotRemovePalette(KoColorSet *); + void slotImportPalette(); + void slotExportPalette(KoColorSet *); - void setColorFromNameList(int index); + void slotAddColor(); + void slotRemoveColor(); + void slotEditEntry(); + void slotEditPalette(); + + void slotPaletteIndexSelected(const QModelIndex &index); + void slotPaletteIndexClicked(const QModelIndex &index); + void slotPaletteIndexDoubleClicked(const QModelIndex &index); + void slotNameListSelection(const KoColor &color); + void slotSetColorSet(KoColorSet* colorSet); void saveToWorkspace(KisWorkspaceResource* workspace); void loadFromWorkspace(KisWorkspaceResource* workspace); -private: - Ui_WdgPaletteDock* m_wdgPaletteDock; +private: + void setEntryByForeground(const QModelIndex &index); + void setFGColorByPalette(const KisSwatch &entry); + +private /* member variables */: + QScopedPointer m_ui; KisPaletteModel *m_model; - QSharedPointer m_serverAdapter; - KoColorSet *m_currentColorSet; - KisColorsetChooser *m_paletteChooser; + KisPaletteListWidget *m_paletteChooser; + + QPointer m_view; KisCanvasResourceProvider *m_resourceProvider; - QPointer m_canvas; + + KoResourceServer * const m_rServer; + + QPointer m_activeDocument; + QPointer m_currentColorSet; + QScopedPointer m_paletteEditor; + + QScopedPointer m_actAdd; + QScopedPointer m_actRemove; + QScopedPointer m_actModify; + QScopedPointer m_actEditPalette; + QMenu m_viewContextMenu; }; diff --git a/plugins/dockers/palettedocker/palettedocker_dock.cpp b/plugins/dockers/palettedocker/palettedocker_dock.cpp --- a/plugins/dockers/palettedocker/palettedocker_dock.cpp +++ b/plugins/dockers/palettedocker/palettedocker_dock.cpp @@ -27,16 +27,19 @@ #include #include #include +#include +#include +#include +#include +#include #include - #include #include - +#include #include -#include -#include #include +#include #include #include #include @@ -45,251 +48,263 @@ #include #include #include -#include -#include -#include -#include +#include +#include + +#include +#include +#include +#include + +#include +#include -#include "KisPaletteModel.h" #include "ui_wdgpalettedock.h" -#include "kis_palette_delegate.h" -#include "kis_palette_view.h" -#include PaletteDockerDock::PaletteDockerDock( ) : QDockWidget(i18n("Palette")) - , m_wdgPaletteDock(new Ui_WdgPaletteDock()) - , m_currentColorSet(0) - , m_resourceProvider(0) - , m_canvas(0) + , m_ui(new Ui_WdgPaletteDock()) + , m_model(new KisPaletteModel(this)) + , m_paletteChooser(new KisPaletteListWidget(this)) + , m_view(Q_NULLPTR) + , m_resourceProvider(Q_NULLPTR) + , m_rServer(KoResourceServerProvider::instance()->paletteServer()) + , m_activeDocument(Q_NULLPTR) + , m_paletteEditor(new KisPaletteEditor) + , m_actAdd(new QAction(KisIconUtils::loadIcon("list-add"), i18n("Add a color"))) + , m_actRemove(new QAction(KisIconUtils::loadIcon("edit-delete"), i18n("Delete color"))) + , m_actModify(new QAction(KisIconUtils::loadIcon("edit-rename"), i18n("Modify this spot"))) + , m_actEditPalette(new QAction(KisIconUtils::loadIcon("groupLayer"), i18n("Edit this palette"))) { - QWidget* mainWidget = new QWidget(this); + QWidget *mainWidget = new QWidget(this); setWidget(mainWidget); - m_wdgPaletteDock->setupUi(mainWidget); - m_wdgPaletteDock->bnAdd->setIcon(KisIconUtils::loadIcon("list-add")); - m_wdgPaletteDock->bnAdd->setIconSize(QSize(16, 16)); - m_wdgPaletteDock->bnRemove->setIcon(KisIconUtils::loadIcon("edit-delete")); - m_wdgPaletteDock->bnRemove->setIconSize(QSize(16, 16)); - m_wdgPaletteDock->bnAdd->setEnabled(false); - m_wdgPaletteDock->bnRemove->setEnabled(false); - m_wdgPaletteDock->bnAddDialog->setVisible(false); - m_wdgPaletteDock->bnAddGroup->setIcon(KisIconUtils::loadIcon("groupLayer")); - m_wdgPaletteDock->bnAddGroup->setIconSize(QSize(16, 16)); + m_ui->setupUi(mainWidget); + + m_ui->bnAdd->setDefaultAction(m_actAdd.data()); + m_ui->bnRemove->setDefaultAction(m_actRemove.data()); + m_ui->bnRename->setDefaultAction(m_actModify.data()); + m_ui->bnEditPalette->setDefaultAction(m_actEditPalette.data()); + + // to make sure their icons have the same size + m_ui->bnRemove->setIconSize(QSize(16, 16)); + m_ui->bnRename->setIconSize(QSize(16, 16)); + m_ui->bnAdd->setIconSize(QSize(16, 16)); + m_ui->bnEditPalette->setIconSize(QSize(16, 16)); + + m_ui->paletteView->setPaletteModel(m_model); + m_ui->paletteView->setAllowModification(true); + m_ui->cmbNameList->setCompanionView(m_ui->paletteView); + + m_paletteEditor->setPaletteModel(m_model); + + connect(m_actAdd.data(), SIGNAL(triggered()), SLOT(slotAddColor())); + connect(m_actRemove.data(), SIGNAL(triggered()), SLOT(slotRemoveColor())); + connect(m_actModify.data(), SIGNAL(triggered()), SLOT(slotEditEntry())); + connect(m_actEditPalette.data(), SIGNAL(triggered()), SLOT(slotEditPalette())); + connect(m_ui->paletteView, SIGNAL(sigIndexSelected(QModelIndex)), + SLOT(slotPaletteIndexSelected(QModelIndex))); + connect(m_ui->paletteView, SIGNAL(clicked(QModelIndex)), + SLOT(slotPaletteIndexClicked(QModelIndex))); + connect(m_ui->paletteView, SIGNAL(doubleClicked(QModelIndex)), + SLOT(slotPaletteIndexDoubleClicked(QModelIndex))); + + m_viewContextMenu.addAction(m_actModify.data()); + m_viewContextMenu.addAction(m_actRemove.data()); + connect(m_ui->paletteView, SIGNAL(pressed(QModelIndex)), SLOT(slotContextMenu(QModelIndex))); + + m_paletteChooser->setAllowModification(true); + connect(m_paletteChooser, SIGNAL(sigPaletteSelected(KoColorSet*)), SLOT(slotSetColorSet(KoColorSet*))); + connect(m_paletteChooser, SIGNAL(sigAddPalette()), SLOT(slotAddPalette())); + connect(m_paletteChooser, SIGNAL(sigImportPalette()), SLOT(slotImportPalette())); + connect(m_paletteChooser, SIGNAL(sigRemovePalette(KoColorSet*)), SLOT(slotRemovePalette(KoColorSet*))); + connect(m_paletteChooser, SIGNAL(sigExportPalette(KoColorSet*)), SLOT(slotExportPalette(KoColorSet*))); + + m_ui->bnColorSets->setIcon(KisIconUtils::loadIcon("hi16-palette_library")); + m_ui->bnColorSets->setToolTip(i18n("Choose palette")); + m_ui->bnColorSets->setPopupWidget(m_paletteChooser); - - m_model = new KisPaletteModel(this); - m_wdgPaletteDock->paletteView->setPaletteModel(m_model); - - connect(m_wdgPaletteDock->bnAdd, SIGNAL(clicked(bool)), this, SLOT(addColorForeground())); - connect(m_wdgPaletteDock->bnRemove, SIGNAL(clicked(bool)), this, SLOT(removeColor())); - connect(m_wdgPaletteDock->bnAddGroup, SIGNAL(clicked(bool)), m_wdgPaletteDock->paletteView, SLOT(addGroupWithDialog())); - - connect(m_wdgPaletteDock->paletteView, SIGNAL(entrySelected(KoColorSetEntry)), this, SLOT(entrySelected(KoColorSetEntry))); - connect(m_wdgPaletteDock->paletteView, SIGNAL(entrySelectedBackGround(KoColorSetEntry)), this, SLOT(entrySelectedBack(KoColorSetEntry))); - - KoResourceServer* rServer = KoResourceServerProvider::instance()->paletteServer(); - m_serverAdapter = QSharedPointer(new KoResourceServerAdapter(rServer)); - m_serverAdapter->connectToResourceServer(); - rServer->addObserver(this); - - m_paletteChooser = new KisColorsetChooser(this); - connect(m_paletteChooser, SIGNAL(paletteSelected(KoColorSet*)), this, SLOT(setColorSet(KoColorSet*))); - - m_wdgPaletteDock->bnColorSets->setIcon(KisIconUtils::loadIcon("hi16-palette_library")); - m_wdgPaletteDock->bnColorSets->setToolTip(i18n("Choose palette")); - m_wdgPaletteDock->bnColorSets->setPopupWidget(m_paletteChooser); - - connect(m_wdgPaletteDock->cmbNameList, SIGNAL(currentIndexChanged(int)), this, SLOT(setColorFromNameList(int))); KisConfig cfg(true); - QString defaultPalette = cfg.defaultPalette(); - KoColorSet* defaultColorSet = rServer->resourceByName(defaultPalette); - if (defaultColorSet) { - setColorSet(defaultColorSet); + QString defaultPaletteName = cfg.defaultPalette(); + KoColorSet* defaultPalette = m_rServer->resourceByName(defaultPaletteName); + if (defaultPalette) { + slotSetColorSet(defaultPalette); + } else { + m_ui->bnAdd->setEnabled(false); + m_ui->bnRename->setEnabled(false); + m_ui->bnRemove->setEnabled(false); + m_ui->bnEditPalette->setEnabled(false); + m_ui->paletteView->setAllowModification(false); } } PaletteDockerDock::~PaletteDockerDock() -{ - KoResourceServer* rServer = KoResourceServerProvider::instance()->paletteServer(); - rServer->removeObserver(this); - - if (m_currentColorSet) { - KisConfig cfg(true); - cfg.setDefaultPalette(m_currentColorSet->name()); - } - - delete m_wdgPaletteDock->paletteView->itemDelegate(); - delete m_wdgPaletteDock; -} +{ } void PaletteDockerDock::setViewManager(KisViewManager* kisview) { + m_view = kisview; m_resourceProvider = kisview->resourceProvider(); - connect(m_resourceProvider, SIGNAL(sigSavingWorkspace(KisWorkspaceResource*)), SLOT(saveToWorkspace(KisWorkspaceResource*))); - connect(m_resourceProvider, SIGNAL(sigLoadingWorkspace(KisWorkspaceResource*)), SLOT(loadFromWorkspace(KisWorkspaceResource*))); - connect(m_resourceProvider, SIGNAL(sigFGColorChanged(KoColor)),m_wdgPaletteDock->paletteView, SLOT(trySelectClosestColor(KoColor))); + connect(m_resourceProvider, SIGNAL(sigSavingWorkspace(KisWorkspaceResource*)), + SLOT(saveToWorkspace(KisWorkspaceResource*))); + connect(m_resourceProvider, SIGNAL(sigLoadingWorkspace(KisWorkspaceResource*)), + SLOT(loadFromWorkspace(KisWorkspaceResource*))); + connect(m_resourceProvider, SIGNAL(sigFGColorChanged(KoColor)), + m_ui->paletteView, SLOT(slotFGColorChanged(KoColor))); kisview->nodeManager()->disconnect(m_model); - - } -void PaletteDockerDock::setCanvas(KoCanvasBase *canvas) +void PaletteDockerDock::slotContextMenu(const QModelIndex &) { - setEnabled(canvas != 0); - if (canvas) { - KisCanvas2 *cv = qobject_cast(canvas); - m_model->setDisplayRenderer(cv->displayColorConverter()->displayRendererInterface()); + if (QApplication::mouseButtons() == Qt::RightButton) { + m_viewContextMenu.exec(QCursor::pos()); } - m_canvas = static_cast(canvas); } - -void PaletteDockerDock::unsetCanvas() +void PaletteDockerDock::slotAddPalette() { - setEnabled(false); - m_model->setDisplayRenderer(0); - m_canvas = 0; + m_paletteEditor->addPalette(); } -void PaletteDockerDock::unsetResourceServer() +void PaletteDockerDock::slotRemovePalette(KoColorSet *cs) { - KoResourceServer* rServer = KoResourceServerProvider::instance()->paletteServer(); - rServer->removeObserver(this); + m_paletteEditor->removePalette(cs); } -void PaletteDockerDock::removingResource(KoColorSet *resource) +void PaletteDockerDock::slotImportPalette() { - if (resource == m_currentColorSet) { - setColorSet(0); - } + m_paletteEditor->importPalette(); } -void PaletteDockerDock::resourceChanged(KoColorSet *resource) +void PaletteDockerDock::slotExportPalette(KoColorSet *palette) { - setColorSet(resource); + KoFileDialog dialog(this, KoFileDialog::SaveFile, "Save Palette"); + dialog.setDefaultDir(palette->filename()); + dialog.setMimeTypeFilters(QStringList() << "krita/x-colorset"); + QString newPath; + bool isStandAlone = palette->isGlobal(); + QString oriPath = palette->filename(); + if ((newPath = dialog.filename()).isEmpty()) { return; } + palette->setFilename(newPath); + palette->setIsGlobal(true); + palette->save(); + palette->setFilename(oriPath); + palette->setIsGlobal(isStandAlone); } - -void PaletteDockerDock::setColorSet(KoColorSet* colorSet) +void PaletteDockerDock::setCanvas(KoCanvasBase *canvas) { - m_model->setColorSet(colorSet); - m_wdgPaletteDock->paletteView->updateView(); - m_wdgPaletteDock->paletteView->updateRows(); - m_wdgPaletteDock->cmbNameList->clear(); - - - - if (colorSet && colorSet->nColors()>0) { - for (quint32 i = 0; i< colorSet->nColors(); i++) { - KoColorSetEntry entry = colorSet->getColorGlobal(i); - QPixmap colorSquare = QPixmap(32, 32); - if (entry.spotColor()) { - QImage img = QImage(32, 32, QImage::Format_ARGB32); - QPainter circlePainter; - img.fill(Qt::transparent); - circlePainter.begin(&img); - QBrush brush = QBrush(Qt::SolidPattern); - brush.setColor(entry.color().toQColor()); - circlePainter.setBrush(brush); - QPen pen = circlePainter.pen(); - pen.setColor(Qt::transparent); - pen.setWidth(0); - circlePainter.setPen(pen); - circlePainter.drawEllipse(0, 0, 32, 32); - circlePainter.end(); - colorSquare = QPixmap::fromImage(img); - } else { - colorSquare.fill(entry.color().toQColor()); - } - QString name = entry.name(); - if (!entry.id().isEmpty()){ - name = entry.id() + " - " + entry.name(); - } - m_wdgPaletteDock->cmbNameList->addSqueezedItem(QIcon(colorSquare), name); + setEnabled(canvas != Q_NULLPTR); + if (canvas) { + KisCanvas2 *cv = qobject_cast(canvas); + m_ui->paletteView->setDisplayRenderer(cv->displayColorConverter()->displayRendererInterface()); + } + + if (m_activeDocument) { + for (KoColorSet * &cs : m_activeDocument->paletteList()) { + KoColorSet *tmpAddr = cs; + cs = new KoColorSet(*cs); + m_rServer->removeResourceFromServer(tmpAddr); } } + if (m_view && m_view->document()) { + m_activeDocument = m_view->document(); + m_paletteEditor->setView(m_view); + + for (KoColorSet *cs : m_activeDocument->paletteList()) { + m_rServer->addResource(cs); + } + } + + if (!m_currentColorSet) { + slotSetColorSet(Q_NULLPTR); + } +} +void PaletteDockerDock::unsetCanvas() +{ + setEnabled(false); + m_ui->paletteView->setDisplayRenderer(Q_NULLPTR); + m_paletteEditor->setView(Q_NULLPTR); + for (KoResource *r : m_rServer->resources()) { + KoColorSet *c = static_cast(r); + if (!c->isGlobal()) { + m_rServer->removeResourceFromServer(c); + } + } + if (!m_currentColorSet) { + slotSetColorSet(Q_NULLPTR); + } +} - QCompleter *completer = new QCompleter(m_wdgPaletteDock->cmbNameList->model()); - completer->setCompletionMode(QCompleter::PopupCompletion); - completer->setCaseSensitivity(Qt::CaseInsensitive); - completer->setFilterMode(Qt::MatchContains); - m_wdgPaletteDock->cmbNameList->setCompleter(completer); - if (colorSet && colorSet->removable()) { - m_wdgPaletteDock->bnAdd->setEnabled(true); - m_wdgPaletteDock->bnRemove->setEnabled(true); +void PaletteDockerDock::slotSetColorSet(KoColorSet* colorSet) +{ + if (colorSet && colorSet->isEditable()) { + m_ui->bnAdd->setEnabled(true); + m_ui->bnRename->setEnabled(true); + m_ui->bnRemove->setEnabled(true); + m_ui->bnEditPalette->setEnabled(true); + m_ui->paletteView->setAllowModification(true); } else { - m_wdgPaletteDock->bnAdd->setEnabled(false); - m_wdgPaletteDock->bnRemove->setEnabled(false); + m_ui->bnAdd->setEnabled(false); + m_ui->bnRename->setEnabled(false); + m_ui->bnRemove->setEnabled(false); + m_ui->bnEditPalette->setEnabled(false); + m_ui->paletteView->setAllowModification(false); } + m_currentColorSet = colorSet; + m_model->setPalette(colorSet); + if (colorSet) { + KisConfig cfg(true); + cfg.setDefaultPalette(colorSet->name()); + m_ui->bnColorSets->setText(colorSet->name()); + } else { + m_ui->bnColorSets->setText(""); + } } -void PaletteDockerDock::setColorFromNameList(int index) +void PaletteDockerDock::slotEditPalette() { - if (m_model && m_currentColorSet) { - entrySelected(m_currentColorSet->getColorGlobal(index)); - m_wdgPaletteDock->paletteView->blockSignals(true); - m_wdgPaletteDock->cmbNameList->blockSignals(true); - m_wdgPaletteDock->cmbNameList->setCurrentIndex(index); - m_wdgPaletteDock->paletteView->selectionModel()->clearSelection(); - m_wdgPaletteDock->paletteView->selectionModel()->setCurrentIndex(m_model->indexFromId(index), QItemSelectionModel::Select); - m_wdgPaletteDock->paletteView->blockSignals(false); - m_wdgPaletteDock->cmbNameList->blockSignals(false); - } + KisDlgPaletteEditor dlg; + if (!m_currentColorSet) { return; } + dlg.setPaletteModel(m_model); + dlg.setView(m_view); + if (dlg.exec() != QDialog::Accepted){ return; } + + slotSetColorSet(m_currentColorSet); // update GUI } -void PaletteDockerDock::addColorForeground() + +void PaletteDockerDock::slotAddColor() { if (m_resourceProvider) { - //setup dialog - m_wdgPaletteDock->paletteView->addEntryWithDialog(m_resourceProvider->fgColor()); + m_paletteEditor->addEntry(m_resourceProvider->fgColor()); } } -void PaletteDockerDock::removeColor() +void PaletteDockerDock::slotRemoveColor() { - QModelIndex index = m_wdgPaletteDock->paletteView->currentIndex(); + QModelIndex index = m_ui->paletteView->currentIndex(); if (!index.isValid()) { return; } - m_wdgPaletteDock->paletteView->removeEntryWithDialog(index); + m_paletteEditor->removeEntry(index); + m_ui->bnRemove->setEnabled(false); } -void PaletteDockerDock::entrySelected(KoColorSetEntry entry) +void PaletteDockerDock::setFGColorByPalette(const KisSwatch &entry) { - if (m_wdgPaletteDock->paletteView->currentIndex().isValid()) { - quint32 index = m_model->idFromIndex(m_wdgPaletteDock->paletteView->currentIndex()); - m_wdgPaletteDock->cmbNameList->setCurrentIndex(index); - } if (m_resourceProvider) { m_resourceProvider->setFGColor(entry.color()); } - if (m_currentColorSet->removable()) { - m_wdgPaletteDock->bnRemove->setEnabled(true); - } -} - -void PaletteDockerDock::entrySelectedBack(KoColorSetEntry entry) -{ - if (m_wdgPaletteDock->paletteView->currentIndex().isValid()) { - quint32 index = m_model->idFromIndex(m_wdgPaletteDock->paletteView->currentIndex()); - m_wdgPaletteDock->cmbNameList->setCurrentIndex(index); - } - if (m_resourceProvider) { - m_resourceProvider->setBGColor(entry.color()); - } - if (m_currentColorSet->removable()) { - m_wdgPaletteDock->bnRemove->setEnabled(true); - } } void PaletteDockerDock::saveToWorkspace(KisWorkspaceResource* workspace) { - if (m_currentColorSet) { + if (!m_currentColorSet.isNull()) { workspace->setProperty("palette", m_currentColorSet->name()); } } @@ -300,9 +315,55 @@ KoResourceServer* rServer = KoResourceServerProvider::instance()->paletteServer(); KoColorSet* colorSet = rServer->resourceByName(workspace->getString("palette")); if (colorSet) { - setColorSet(colorSet); + slotSetColorSet(colorSet); } } } +void PaletteDockerDock::slotPaletteIndexSelected(const QModelIndex &index) +{ + bool occupied = qvariant_cast(index.data(KisPaletteModel::CheckSlotRole)); + if (occupied) { + if (!qvariant_cast(index.data(KisPaletteModel::IsGroupNameRole))) { + m_ui->bnRemove->setEnabled(true); + KisSwatch entry = m_model->getEntry(index); + setFGColorByPalette(entry); + } + } + if (!m_currentColorSet->isEditable()) { return; } + m_ui->bnRemove->setEnabled(occupied); +} + +void PaletteDockerDock::slotPaletteIndexClicked(const QModelIndex &index) +{ + if (!(qvariant_cast(index.data(KisPaletteModel::CheckSlotRole)))) { + setEntryByForeground(index); + } +} + +void PaletteDockerDock::slotPaletteIndexDoubleClicked(const QModelIndex &index) +{ + m_paletteEditor->modifyEntry(index); +} +void PaletteDockerDock::setEntryByForeground(const QModelIndex &index) +{ + m_paletteEditor->setEntry(m_resourceProvider->fgColor(), index); + if (m_currentColorSet->isEditable()) { + m_ui->bnRemove->setEnabled(true); + } +} + +void PaletteDockerDock::slotEditEntry() +{ + QModelIndex index = m_ui->paletteView->currentIndex(); + if (!index.isValid()) { + return; + } + m_paletteEditor->modifyEntry(index); +} + +void PaletteDockerDock::slotNameListSelection(const KoColor &color) +{ + m_resourceProvider->setFGColor(color); +} diff --git a/plugins/dockers/palettedocker/wdgpalettedock.ui b/plugins/dockers/palettedocker/wdgpalettedock.ui --- a/plugins/dockers/palettedocker/wdgpalettedock.ui +++ b/plugins/dockers/palettedocker/wdgpalettedock.ui @@ -6,8 +6,8 @@ 0 0 - 326 - 219 + 647 + 422
@@ -36,9 +36,16 @@
+ + + + true + + + - + Add foreground color @@ -46,6 +53,10 @@ ... + + + .. + 22 @@ -57,7 +68,7 @@ - + Delete color @@ -65,6 +76,10 @@ ... + + + .. + 22 @@ -76,49 +91,44 @@ - - + + - - - - - - ... + + + .. - - - - - 0 - 0 - + + + + Qt::Horizontal - - true + + + 40 + 20 + - + - - - - Add color + + + + + + + + ... - - - 22 - 22 - - - - false + + @@ -138,9 +148,9 @@
kis_palette_view.h
- SqueezedComboBox + KisPaletteComboBox QComboBox -
squeezedcombobox.h
+
KisPaletteComboBox.h
diff --git a/plugins/extensions/layersplit/dlg_layersplit.h b/plugins/extensions/layersplit/dlg_layersplit.h --- a/plugins/extensions/layersplit/dlg_layersplit.h +++ b/plugins/extensions/layersplit/dlg_layersplit.h @@ -21,7 +21,7 @@ #include #include -#include +#include #include #include "wdg_layersplit.h" @@ -56,7 +56,7 @@ private: WdgLayerSplit *m_page {0}; - KisColorsetChooser *m_colorSetChooser {0}; + KisPaletteListWidget *m_colorSetChooser {0}; KoColorSet *m_palette {0}; }; diff --git a/plugins/extensions/layersplit/dlg_layersplit.cpp b/plugins/extensions/layersplit/dlg_layersplit.cpp --- a/plugins/extensions/layersplit/dlg_layersplit.cpp +++ b/plugins/extensions/layersplit/dlg_layersplit.cpp @@ -46,9 +46,9 @@ m_page->intFuzziness->setRange(0, 200); m_page->intFuzziness->setSingleStep(1); - m_colorSetChooser = new KisColorsetChooser(); + m_colorSetChooser = new KisPaletteListWidget(); m_page->paletteChooser->setPopupWidget(m_colorSetChooser); - connect(m_colorSetChooser, SIGNAL(paletteSelected(KoColorSet*)), this, SLOT(slotSetPalette(KoColorSet*))); + connect(m_colorSetChooser, SIGNAL(sigPaletteSelected(KoColorSet*)), this, SLOT(slotSetPalette(KoColorSet*))); KisConfig cfg(true); m_page->intFuzziness->setValue(cfg.readEntry("layersplit/fuzziness", 20)); diff --git a/plugins/extensions/layersplit/layersplit.cpp b/plugins/extensions/layersplit/layersplit.cpp --- a/plugins/extensions/layersplit/layersplit.cpp +++ b/plugins/extensions/layersplit/layersplit.cpp @@ -149,7 +149,7 @@ if (!found) { QString name = ""; if (dlg.palette()) { - name = dlg.palette()->closestColorName(c); + name = dlg.palette()->getClosestColorInfo(c).swatch.name(); } if (name.toLower() == "untitled" || name.toLower() == "none" || name.toLower() == "") { diff --git a/plugins/extensions/pykrita/sip/krita/Palette.sip b/plugins/extensions/pykrita/sip/krita/Palette.sip --- a/plugins/extensions/pykrita/sip/krita/Palette.sip +++ b/plugins/extensions/pykrita/sip/krita/Palette.sip @@ -1,20 +1,4 @@ -/* -struct KoColorSetEntry { - %TypeHeaderCode - #include "KoColorSet.h" - %End -public: - KoColorSetEntry(); - - QString name; - QString id; - bool spotColor; - - bool operator==(const KoColorSetEntry& rhs) const; - -}; -*/ -class KoColorSetEntry; +class KisSwatch; class Palette : QObject { @@ -33,14 +17,11 @@ bool addGroup(QString name); bool removeGroup(QString name, bool keepColors); int colorsCountTotal(); - int colorsCountGroup(QString name); - KoColorSetEntry colorSetEntryByIndex(int index); - KoColorSetEntry colorSetEntryFromGroup(int index, const QString &groupName); - ManagedColor *colorForEntry(KoColorSetEntry entry) /Factory/; - void addEntry(KoColorSetEntry entry, QString groupName); + KisSwatch colorSetEntryByIndex(int index); + KisSwatch colorSetEntryFromGroup(int index, const QString &groupName); + ManagedColor *colorForEntry(KisSwatch entry) /Factory/; + void addEntry(KisSwatch entry, QString groupName); void removeEntry(int index, const QString &groupName); - void insertEntry(int index, KoColorSetEntry entry, QString groupName); - bool editEntry(int index, KoColorSetEntry entry, QString groupName); bool changeGroupName(QString oldGroupName, QString newGroupName); bool moveGroup(const QString &groupName, const QString &groupNameInsertBefore); bool save(); diff --git a/plugins/extensions/pykrita/sip/krita/PaletteView.sip b/plugins/extensions/pykrita/sip/krita/PaletteView.sip --- a/plugins/extensions/pykrita/sip/krita/PaletteView.sip +++ b/plugins/extensions/pykrita/sip/krita/PaletteView.sip @@ -14,7 +14,7 @@ bool removeSelectedEntryWithDialog(); void trySelectClosestColor(ManagedColor *color); Q_SIGNALS: - void entrySelectedForeGround(KoColorSetEntry entry); - void entrySelectedBackGround(KoColorSetEntry entry); + void entrySelectedForeGround(KisSwatch entry); + void entrySelectedBackGround(KisSwatch entry); private: }; diff --git a/plugins/impex/kra/kra_converter.cpp b/plugins/impex/kra/kra_converter.cpp --- a/plugins/impex/kra/kra_converter.cpp +++ b/plugins/impex/kra/kra_converter.cpp @@ -134,6 +134,10 @@ if (!result) { qWarning() << "saving binary data failed"; } + result = m_kraSaver->savePalettes(m_store, m_image, m_doc->url().toLocalFile()); + if (!result) { + qWarning() << "saving palettes data failed"; + } if (!m_store->finalize()) { return KisImageBuilder_RESULT_FAILURE; @@ -333,6 +337,7 @@ m_image->blockUpdates(); m_kraLoader->loadBinaryData(store, m_image, m_doc->localFilePath(), true); + m_kraLoader->loadPalettes(store, m_doc); m_image->unblockUpdates(); diff --git a/plugins/impex/libkra/kis_kra_loader.h b/plugins/impex/libkra/kis_kra_loader.h --- a/plugins/impex/libkra/kis_kra_loader.h +++ b/plugins/impex/libkra/kis_kra_loader.h @@ -53,6 +53,8 @@ void loadBinaryData(KoStore* store, KisImageSP image, const QString & uri, bool external); + void loadPalettes(KoStore *store, KisDocument *doc); + vKisNodeSP selectedNodes() const; // it's neater to follow the same design as with selectedNodes, so let's have a getter here diff --git a/plugins/impex/libkra/kis_kra_loader.cpp b/plugins/impex/libkra/kis_kra_loader.cpp --- a/plugins/impex/libkra/kis_kra_loader.cpp +++ b/plugins/impex/libkra/kis_kra_loader.cpp @@ -36,6 +36,7 @@ #include #include #include +#include #include #include @@ -70,6 +71,7 @@ #include #include "KisReferenceImagesLayer.h" #include "KisReferenceImage.h" +#include #include "KisDocument.h" #include "kis_config.h" @@ -136,6 +138,7 @@ QMap assistantsFilenames; QList assistants; QMap keyframeFilenames; + QVector paletteFilenames; QStringList errorMessages; QStringList warningMessages; }; @@ -360,6 +363,21 @@ } } + // reading palettes from XML + for (child = element.lastChild(); !child.isNull(); child = child.previousSibling()) { + QDomElement e = child.toElement(); + if (e.tagName() == PALETTES) { + for (QDomElement paletteElement = e.lastChildElement(); + !paletteElement.isNull(); + paletteElement = paletteElement.previousSiblingElement()) { + QString paletteName = paletteElement.attribute("filename"); + m_d->paletteFilenames.append(paletteName); + } + break; + } + } + + return image; } @@ -486,6 +504,22 @@ loadAssistants(store, uri, external); } +void KisKraLoader::loadPalettes(KoStore *store, KisDocument *doc) +{ + QList list; + Q_FOREACH (const QString &filename, m_d->paletteFilenames) { + KoColorSet *newPalette = new KoColorSet(filename); + store->open(m_d->imageName + PALETTE_PATH + filename); + QByteArray data = store->read(store->size()); + newPalette->fromByteArray(data); + newPalette->setIsGlobal(false); + newPalette->setIsEditable(true); + store->close(); + list.append(newPalette); + } + doc->setPaletteList(list); +} + vKisNodeSP KisKraLoader::selectedNodes() const { return m_d->selectedNodes; diff --git a/plugins/impex/libkra/kis_kra_saver.h b/plugins/impex/libkra/kis_kra_saver.h --- a/plugins/impex/libkra/kis_kra_saver.h +++ b/plugins/impex/libkra/kis_kra_saver.h @@ -44,6 +44,8 @@ bool saveBinaryData(KoStore* store, KisImageSP image, const QString & uri, bool external, bool includeMerge); + bool savePalettes(KoStore *store, KisImageSP image, const QString &uri); + /// @return a list with everything that went wrong while saving QStringList errorMessages() const; @@ -58,6 +60,7 @@ bool saveGuides(QDomDocument& doc, QDomElement& element); bool saveAudio(QDomDocument& doc, QDomElement& element); bool saveNodeKeyframes(KoStore *store, QString location, const KisNode *node); + void savePalettesToXML(QDomDocument& doc, QDomElement &element); struct Private; Private * const m_d; }; diff --git a/plugins/impex/libkra/kis_kra_saver.cpp b/plugins/impex/libkra/kis_kra_saver.cpp --- a/plugins/impex/libkra/kis_kra_saver.cpp +++ b/plugins/impex/libkra/kis_kra_saver.cpp @@ -26,6 +26,7 @@ #include #include #include +#include #include #include @@ -35,6 +36,9 @@ #include #include #include +#include +#include +#include #include #include @@ -130,9 +134,10 @@ saveWarningColor(doc, imageElement, image); saveCompositions(doc, imageElement, image); saveAssistantsList(doc, imageElement); - saveGrid(doc,imageElement); - saveGuides(doc,imageElement); - saveAudio(doc,imageElement); + saveGrid(doc, imageElement); + saveGuides(doc, imageElement); + saveAudio(doc, imageElement); + savePalettesToXML(doc, imageElement); QDomElement animationElement = doc.createElement("animation"); KisDomUtils::saveValue(&animationElement, "framerate", image->animationInterface()->framerate()); @@ -143,6 +148,42 @@ return imageElement; } +bool KisKraSaver::savePalettes(KoStore *store, KisImageSP image, const QString &uri) +{ + Q_UNUSED(image); + Q_UNUSED(uri); + + bool res = false; + if (m_d->doc->paletteList().size() == 0) { + return true; + } + for (const KoColorSet *palette : m_d->doc->paletteList()) { + if (!palette->isGlobal()) { + if (!store->open(m_d->imageName + PALETTE_PATH + palette->filename())) { + m_d->errorMessages << i18n("could not save palettes"); + return false; + } + store->write(palette->toByteArray()); + store->close(); + res = true; + } + } + return res; +} + +void KisKraSaver::savePalettesToXML(QDomDocument &doc, QDomElement &element) +{ + QDomElement ePalette = doc.createElement(PALETTES); + for (const KoColorSet *palette : m_d->doc->paletteList()) { + if (!palette->isGlobal()) { + QDomElement eFile = doc.createElement("palette"); + eFile.setAttribute("filename", palette->filename()); + ePalette.appendChild(eFile); + } + } + element.appendChild(ePalette); +} + bool KisKraSaver::saveKeyframes(KoStore *store, const QString &uri, bool external) { QMap::iterator it; diff --git a/plugins/impex/libkra/kis_kra_tags.h b/plugins/impex/libkra/kis_kra_tags.h --- a/plugins/impex/libkra/kis_kra_tags.h +++ b/plugins/impex/libkra/kis_kra_tags.h @@ -40,6 +40,7 @@ const QString LAYER_STYLES_PATH = "/annotations/layerstyles.asl"; const QString ASSISTANTS_PATH = "/assistants/"; const QString LAYER_PATH = "/layers/"; +const QString PALETTE_PATH = "/palettes/"; const QString ADJUSTMENT_LAYER = "adjustmentlayer"; const QString CHANNEL_FLAGS = "channelflags"; @@ -134,6 +135,7 @@ const QString COLORBYTEDATA = "ColorData"; const QString SIMPLECOLORDATA = "SimpleColorData"; // easier 8-bit color data that works well with XML const QString GLOBALASSISTANTSCOLOR = "GlobalAssistantsColor"; +const QString PALETTES = "Palettes"; } diff --git a/plugins/python/palette_docker/palette_docker.py b/plugins/python/palette_docker/palette_docker.py --- a/plugins/python/palette_docker/palette_docker.py +++ b/plugins/python/palette_docker/palette_docker.py @@ -147,8 +147,8 @@ palette = self.currentPalette self.colorComboBox.clear() self.colorList = list() - for i in range(palette.colorsCountTotal()): - entry = palette.colorSetEntryByIndex(i) + for info in palette.infoList(): + entry = info.swatch color = palette.colorForEntry(entry).colorForCanvas(self.canvas()) colorSquare = QPixmap(12, 12) if entry.spotColor() is True: diff --git a/plugins/tools/basictools/kis_tool_colorpicker.cc b/plugins/tools/basictools/kis_tool_colorpicker.cc --- a/plugins/tools/basictools/kis_tool_colorpicker.cc +++ b/plugins/tools/basictools/kis_tool_colorpicker.cc @@ -194,7 +194,7 @@ CHECK_MODE_SANITY_OR_RETURN(KisTool::PAINT_MODE); if (m_config->addPalette) { - KoColorSetEntry ent; + KisSwatch ent; ent.setColor(m_pickedColor); // We don't ask for a name, too intrusive here diff --git a/plugins/tools/tool_lazybrush/kis_tool_lazy_brush_options_widget.h b/plugins/tools/tool_lazybrush/kis_tool_lazy_brush_options_widget.h --- a/plugins/tools/tool_lazybrush/kis_tool_lazy_brush_options_widget.h +++ b/plugins/tools/tool_lazybrush/kis_tool_lazy_brush_options_widget.h @@ -29,6 +29,9 @@ class KoColor; +/** + * @brief The KisToolLazyBrushOptionsWidget class + */ class KisToolLazyBrushOptionsWidget : public QWidget { Q_OBJECT diff --git a/plugins/tools/tool_lazybrush/kis_tool_lazy_brush_options_widget.cpp b/plugins/tools/tool_lazybrush/kis_tool_lazy_brush_options_widget.cpp --- a/plugins/tools/tool_lazybrush/kis_tool_lazy_brush_options_widget.cpp +++ b/plugins/tools/tool_lazybrush/kis_tool_lazy_brush_options_widget.cpp @@ -37,8 +37,7 @@ struct KisToolLazyBrushOptionsWidget::Private { Private() - : transparentColorIndex(-1), - baseNodeChangedCompressor(500, KisSignalCompressor::FIRST_ACTIVE) + : baseNodeChangedCompressor(500, KisSignalCompressor::FIRST_ACTIVE) { } @@ -51,7 +50,7 @@ KisColorizeMaskSP activeMask; KoColorSet colorSet; - int transparentColorIndex = -1; + int transparentColorIndex; KisSignalCompressor baseNodeChangedCompressor; }; @@ -102,7 +101,7 @@ "that are placed outside the closed contours. 0% - no effect, 100% - max effect")); - connect(m_d->ui->colorView, SIGNAL(indexEntrySelected(QModelIndex)), this, SLOT(entrySelected(QModelIndex))); + connect(m_d->ui->colorView, SIGNAL(sigIndexSelected(QModelIndex)), this, SLOT(entrySelected(QModelIndex))); connect(m_d->ui->btnTransparent, SIGNAL(toggled(bool)), this, SLOT(slotMakeTransparent(bool))); connect(m_d->ui->btnRemove, SIGNAL(clicked()), this, SLOT(slotRemove())); @@ -115,16 +114,17 @@ connect(&m_d->baseNodeChangedCompressor, SIGNAL(timeout()), this, SLOT(slotUpdateNodeProperties())); - m_d->provider = provider; - const KoColorSpace *cs = KoColorSpaceRegistry::instance()->rgb8(); + m_d->colorSet.setIsGlobal(false); + m_d->colorSet.setIsEditable(true); + m_d->colorModel->setPalette(&m_d->colorSet); - m_d->colorSet.add(KoColorSetEntry(KoColor(Qt::red, cs), "color1")); - m_d->colorSet.add(KoColorSetEntry(KoColor(Qt::green, cs), "color2")); - m_d->colorSet.add(KoColorSetEntry(KoColor(Qt::blue, cs), "color3")); + const KoColorSpace *cs = KoColorSpaceRegistry::instance()->rgb8(); - m_d->colorModel->setColorSet(&m_d->colorSet); + m_d->colorModel->addEntry(KisSwatch(KoColor(Qt::red, cs), "color1")); + m_d->colorModel->addEntry(KisSwatch(KoColor(Qt::green, cs), "color2")); + m_d->colorModel->addEntry(KisSwatch(KoColor(Qt::blue, cs), "color3")); } KisToolLazyBrushOptionsWidget::~KisToolLazyBrushOptionsWidget() @@ -157,51 +157,52 @@ void KisToolLazyBrushOptionsWidget::entrySelected(QModelIndex index) { if (!index.isValid()) return; + if (!qvariant_cast(index.data(KisPaletteModel::CheckSlotRole))) return; - const int i = m_d->colorModel->idFromIndex(index); + KisSwatch entry = m_d->colorModel->getEntry(index); + m_d->provider->setFGColor(entry.color()); - if (i >= 0 && i < (int)m_d->colorSet.nColors()) { - KoColorSetEntry entry = m_d->colorModel->colorSetEntryFromIndex(index); - m_d->provider->setFGColor(entry.color()); - } + int idxInList = m_d->activeMask->keyStrokesColors().colors.indexOf(entry.color()); - const bool transparentChecked = i >= 0 && i == m_d->transparentColorIndex; - KisSignalsBlocker b(m_d->ui->btnTransparent); - m_d->ui->btnTransparent->setChecked(transparentChecked); + if (idxInList != -1) { + const bool transparentChecked = idxInList == m_d->transparentColorIndex; + KisSignalsBlocker b(m_d->ui->btnTransparent); + m_d->ui->btnTransparent->setChecked(transparentChecked); + } } void KisToolLazyBrushOptionsWidget::slotCurrentFgColorChanged(const KoColor &color) { - int selectedIndex = -1; + bool found = false; - for (quint32 i = 0; i < m_d->colorSet.nColors(); i++) { - KoColorSetEntry entry = m_d->colorSet.getColorGlobal(i); - if (entry.color() == color) { - selectedIndex = (int)i; - break; - } + QModelIndex candidateIdx = m_d->colorModel->indexForClosest(color); + if (m_d->colorModel->getEntry(candidateIdx).color() == color) { + found = true; } - m_d->ui->btnRemove->setEnabled(selectedIndex >= 0); - m_d->ui->btnTransparent->setEnabled(selectedIndex >= 0); + m_d->ui->btnRemove->setEnabled(found); + m_d->ui->btnTransparent->setEnabled(found); - if (selectedIndex < 0) { + if (!found) { KisSignalsBlocker b(m_d->ui->btnTransparent); m_d->ui->btnTransparent->setChecked(false); } - QModelIndex newIndex = - selectedIndex >= 0 ? - m_d->colorModel->indexFromId(selectedIndex) : QModelIndex(); + QModelIndex newIndex = found ? candidateIdx : QModelIndex(); - if (newIndex != m_d->ui->colorView->currentIndex()) { + if (!found) { + m_d->ui->colorView->selectionModel()->clear(); + } + if (newIndex.isValid() && newIndex != m_d->ui->colorView->currentIndex()) { m_d->ui->colorView->setCurrentIndex(newIndex); + m_d->ui->colorView->update(newIndex); } } void KisToolLazyBrushOptionsWidget::slotColorLabelsChanged() { - m_d->colorSet.clear(); + m_d->colorModel->clear(); + m_d->transparentColorIndex = -1; if (m_d->activeMask) { @@ -210,11 +211,10 @@ for (int i = 0; i < colors.colors.size(); i++) { const QString name = i == m_d->transparentColorIndex ? "transparent" : ""; - m_d->colorSet.add(KoColorSetEntry(colors.colors[i], name)); + m_d->colorModel->addEntry(KisSwatch(colors.colors[i], name)); } } - m_d->colorModel->setColorSet(&m_d->colorSet); slotCurrentFgColorChanged(m_d->provider->fgColor()); } @@ -289,15 +289,21 @@ KIS_ASSERT_RECOVER_RETURN(m_d->activeMask); QModelIndex index = m_d->ui->colorView->currentIndex(); + KisSwatch activeSwatch = m_d->colorModel->getEntry(index); if (!index.isValid()) return; - const int activeIndex = m_d->colorModel->idFromIndex(index); - KIS_ASSERT_RECOVER_RETURN(activeIndex >= 0); + int activeIndex = -1; KisColorizeMask::KeyStrokeColors colors; - for (quint32 i = 0; i < m_d->colorSet.nColors(); i++) { - colors.colors << m_d->colorSet.getColorGlobal(i).color(); + int i = 0; + Q_FOREACH (const QString &groupName, m_d->colorSet.getGroupNames()) { + KisSwatchGroup *group = m_d->colorSet.getGroup(groupName); + Q_FOREACH (const KisSwatchGroup::SwatchInfo &info, group->infoList()) { + colors.colors << info.swatch.color(); + if (activeSwatch == info.swatch) { activeIndex = i; } + i++; + } } colors.transparentIndex = value ? activeIndex : -1; @@ -312,11 +318,7 @@ QModelIndex index = m_d->ui->colorView->currentIndex(); if (!index.isValid()) return; - const int activeIndex = m_d->colorModel->idFromIndex(index); - KIS_ASSERT_RECOVER_RETURN(activeIndex >= 0); - - - const KoColor color = m_d->colorSet.getColorGlobal((quint32)activeIndex).color(); + const KoColor color = m_d->colorModel->getEntry(index).color(); m_d->activeMask->removeKeyStroke(color); }