blob: 0afbf49bbcc13d45c41cdfa0547f132ba1468162 [file] [log] [blame]
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the tools applications of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 3 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL3 included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 3 requirements
** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 2.0 or (at your option) the GNU General
** Public license version 3 or any later version approved by the KDE Free
** Qt Foundation. The licenses are as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-2.0.html and
** https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include "qtgradientstopsmodel.h"
#include <QtGui/QColor>
QT_BEGIN_NAMESPACE
class QtGradientStopPrivate
{
public:
qreal m_position;
QColor m_color;
QtGradientStopsModel *m_model;
};
qreal QtGradientStop::position() const
{
return d_ptr->m_position;
}
QColor QtGradientStop::color() const
{
return d_ptr->m_color;
}
QtGradientStopsModel *QtGradientStop::gradientModel() const
{
return d_ptr->m_model;
}
void QtGradientStop::setColor(const QColor &color)
{
d_ptr->m_color = color;
}
void QtGradientStop::setPosition(qreal position)
{
d_ptr->m_position = position;
}
QtGradientStop::QtGradientStop(QtGradientStopsModel *model)
: d_ptr(new QtGradientStopPrivate())
{
d_ptr->m_position = 0;
d_ptr->m_color = Qt::white;
d_ptr->m_model = model;
}
QtGradientStop::~QtGradientStop()
{
}
class QtGradientStopsModelPrivate
{
QtGradientStopsModel *q_ptr;
Q_DECLARE_PUBLIC(QtGradientStopsModel)
public:
QMap<qreal, QtGradientStop *> m_posToStop;
QMap<QtGradientStop *, qreal> m_stopToPos;
QMap<QtGradientStop *, bool> m_selection;
QtGradientStop *m_current;
};
QtGradientStopsModel::QtGradientStopsModel(QObject *parent)
: QObject(parent), d_ptr(new QtGradientStopsModelPrivate)
{
d_ptr->q_ptr = this;
d_ptr->m_current = 0;
}
QtGradientStopsModel::~QtGradientStopsModel()
{
clear();
}
QtGradientStopsModel::PositionStopMap QtGradientStopsModel::stops() const
{
return d_ptr->m_posToStop;
}
QtGradientStop *QtGradientStopsModel::at(qreal pos) const
{
if (d_ptr->m_posToStop.contains(pos))
return d_ptr->m_posToStop[pos];
return 0;
}
QColor QtGradientStopsModel::color(qreal pos) const
{
PositionStopMap gradStops = stops();
if (gradStops.isEmpty())
return QColor::fromRgbF(pos, pos, pos, 1.0);
if (gradStops.contains(pos))
return gradStops[pos]->color();
gradStops[pos] = 0;
PositionStopMap::ConstIterator itStop = gradStops.constFind(pos);
if (itStop == gradStops.constBegin()) {
++itStop;
return itStop.value()->color();
}
if (itStop == --gradStops.constEnd()) {
--itStop;
return itStop.value()->color();
}
PositionStopMap::ConstIterator itPrev = itStop;
PositionStopMap::ConstIterator itNext = itStop;
--itPrev;
++itNext;
double prevX = itPrev.key();
double nextX = itNext.key();
double coefX = (pos - prevX) / (nextX - prevX);
QColor prevCol = itPrev.value()->color();
QColor nextCol = itNext.value()->color();
QColor newColor;
newColor.setRgbF((nextCol.redF() - prevCol.redF() ) * coefX + prevCol.redF(),
(nextCol.greenF() - prevCol.greenF()) * coefX + prevCol.greenF(),
(nextCol.blueF() - prevCol.blueF() ) * coefX + prevCol.blueF(),
(nextCol.alphaF() - prevCol.alphaF()) * coefX + prevCol.alphaF());
return newColor;
}
QList<QtGradientStop *> QtGradientStopsModel::selectedStops() const
{
return d_ptr->m_selection.keys();
}
QtGradientStop *QtGradientStopsModel::currentStop() const
{
return d_ptr->m_current;
}
bool QtGradientStopsModel::isSelected(QtGradientStop *stop) const
{
if (d_ptr->m_selection.contains(stop))
return true;
return false;
}
QtGradientStop *QtGradientStopsModel::addStop(qreal pos, const QColor &color)
{
qreal newPos = pos;
if (pos < 0.0)
newPos = 0.0;
if (pos > 1.0)
newPos = 1.0;
if (d_ptr->m_posToStop.contains(newPos))
return 0;
QtGradientStop *stop = new QtGradientStop();
stop->setPosition(newPos);
stop->setColor(color);
d_ptr->m_posToStop[newPos] = stop;
d_ptr->m_stopToPos[stop] = newPos;
emit stopAdded(stop);
return stop;
}
void QtGradientStopsModel::removeStop(QtGradientStop *stop)
{
if (!d_ptr->m_stopToPos.contains(stop))
return;
if (currentStop() == stop)
setCurrentStop(0);
selectStop(stop, false);
emit stopRemoved(stop);
qreal pos = d_ptr->m_stopToPos[stop];
d_ptr->m_stopToPos.remove(stop);
d_ptr->m_posToStop.remove(pos);
delete stop;
}
void QtGradientStopsModel::moveStop(QtGradientStop *stop, qreal newPos)
{
if (!d_ptr->m_stopToPos.contains(stop))
return;
if (d_ptr->m_posToStop.contains(newPos))
return;
if (newPos > 1.0)
newPos = 1.0;
else if (newPos < 0.0)
newPos = 0.0;
emit stopMoved(stop, newPos);
const qreal oldPos = stop->position();
stop->setPosition(newPos);
d_ptr->m_stopToPos[stop] = newPos;
d_ptr->m_posToStop.remove(oldPos);
d_ptr->m_posToStop[newPos] = stop;
}
void QtGradientStopsModel::swapStops(QtGradientStop *stop1, QtGradientStop *stop2)
{
if (stop1 == stop2)
return;
if (!d_ptr->m_stopToPos.contains(stop1))
return;
if (!d_ptr->m_stopToPos.contains(stop2))
return;
emit stopsSwapped(stop1, stop2);
const qreal pos1 = stop1->position();
const qreal pos2 = stop2->position();
stop1->setPosition(pos2);
stop2->setPosition(pos1);
d_ptr->m_stopToPos[stop1] = pos2;
d_ptr->m_stopToPos[stop2] = pos1;
d_ptr->m_posToStop[pos1] = stop2;
d_ptr->m_posToStop[pos2] = stop1;
}
void QtGradientStopsModel::changeStop(QtGradientStop *stop, const QColor &newColor)
{
if (!d_ptr->m_stopToPos.contains(stop))
return;
if (stop->color() == newColor)
return;
emit stopChanged(stop, newColor);
stop->setColor(newColor);
}
void QtGradientStopsModel::selectStop(QtGradientStop *stop, bool select)
{
if (!d_ptr->m_stopToPos.contains(stop))
return;
bool selected = d_ptr->m_selection.contains(stop);
if (select == selected)
return;
emit stopSelected(stop, select);
if (select)
d_ptr->m_selection[stop] = true;
else
d_ptr->m_selection.remove(stop);
}
void QtGradientStopsModel::setCurrentStop(QtGradientStop *stop)
{
if (stop && !d_ptr->m_stopToPos.contains(stop))
return;
if (stop == currentStop())
return;
emit currentStopChanged(stop);
d_ptr->m_current = stop;
}
QtGradientStop *QtGradientStopsModel::firstSelected() const
{
PositionStopMap stopList = stops();
PositionStopMap::ConstIterator itStop = stopList.constBegin();
while (itStop != stopList.constEnd()) {
QtGradientStop *stop = itStop.value();
if (isSelected(stop))
return stop;
++itStop;
};
return 0;
}
QtGradientStop *QtGradientStopsModel::lastSelected() const
{
PositionStopMap stopList = stops();
PositionStopMap::ConstIterator itStop = stopList.constEnd();
while (itStop != stopList.constBegin()) {
--itStop;
QtGradientStop *stop = itStop.value();
if (isSelected(stop))
return stop;
};
return 0;
}
QtGradientStopsModel *QtGradientStopsModel::clone() const
{
QtGradientStopsModel *model = new QtGradientStopsModel();
QMap<qreal, QtGradientStop *> stopsToClone = stops();
for (auto it = stopsToClone.cbegin(), end = stopsToClone.cend(); it != end; ++it)
model->addStop(it.key(), it.value()->color());
// clone selection and current also
return model;
}
void QtGradientStopsModel::moveStops(double newPosition)
{
QtGradientStop *current = currentStop();
if (!current)
return;
double newPos = newPosition;
if (newPos > 1)
newPos = 1;
else if (newPos < 0)
newPos = 0;
if (newPos == current->position())
return;
double offset = newPos - current->position();
QtGradientStop *first = firstSelected();
QtGradientStop *last = lastSelected();
if (first && last) { // multiselection
double maxOffset = 1.0 - last->position();
double minOffset = -first->position();
if (offset > maxOffset)
offset = maxOffset;
else if (offset < minOffset)
offset = minOffset;
}
if (offset == 0)
return;
bool forward = (offset > 0) ? false : true;
PositionStopMap stopList;
const auto selected = selectedStops();
for (QtGradientStop *stop : selected)
stopList[stop->position()] = stop;
stopList[current->position()] = current;
PositionStopMap::ConstIterator itStop = forward ? stopList.constBegin() : stopList.constEnd();
while (itStop != (forward ? stopList.constEnd() : stopList.constBegin())) {
if (!forward)
--itStop;
QtGradientStop *stop = itStop.value();
double pos = stop->position() + offset;
if (pos > 1)
pos = 1;
if (pos < 0)
pos = 0;
if (current == stop)
pos = newPos;
QtGradientStop *oldStop = at(pos);
if (oldStop && !stopList.values().contains(oldStop))
removeStop(oldStop);
moveStop(stop, pos);
if (forward)
++itStop;
}
}
void QtGradientStopsModel::clear()
{
const auto stopsList = stops().values();
for (QtGradientStop *stop : stopsList)
removeStop(stop);
}
void QtGradientStopsModel::clearSelection()
{
const auto stopsList = selectedStops();
for (QtGradientStop *stop : stopsList)
selectStop(stop, false);
}
namespace {
template <typename BidirectionalIterator>
std::reverse_iterator<BidirectionalIterator> rev(BidirectionalIterator it)
{ return std::reverse_iterator<BidirectionalIterator>(it); }
}
void QtGradientStopsModel::flipAll()
{
QMap<qreal, QtGradientStop *> stopsMap = stops();
QMap<QtGradientStop *, bool> swappedList;
for (auto itStop = rev(stopsMap.keyValueEnd()), end = rev(stopsMap.keyValueBegin()); itStop != end; ++itStop) {
QtGradientStop *stop = (*itStop).second;
if (swappedList.contains(stop))
continue;
const double newPos = 1.0 - (*itStop).first;
if (stopsMap.contains(newPos)) {
QtGradientStop *swapped = stopsMap.value(newPos);
swappedList[swapped] = true;
swapStops(stop, swapped);
} else {
moveStop(stop, newPos);
}
}
}
void QtGradientStopsModel::selectAll()
{
const auto stopsMap = stops();
for (auto it = stopsMap.cbegin(), end = stopsMap.cend(); it != end; ++it)
selectStop(it.value(), true);
}
void QtGradientStopsModel::deleteStops()
{
const auto selected = selectedStops();
for (QtGradientStop *stop : selected)
removeStop(stop);
QtGradientStop *current = currentStop();
if (current)
removeStop(current);
}
QT_END_NAMESPACE