Revision control
Copy as Markdown
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
// Qt-Security score:significant reason:default
#include "qtgradientstopsmodel_p.h"
#include <QtGui/qcolor.h>
#include <QtCore/qhash.h>
QT_BEGIN_NAMESPACE
class QtGradientStopPrivate
{
public:
qreal m_position = 0;
QColor m_color = Qt::white;
QtGradientStopsModel *m_model = nullptr;
};
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(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_model = model;
}
QtGradientStop::~QtGradientStop()
{
}
class QtGradientStopsModelPrivate
{
QtGradientStopsModel *q_ptr = nullptr;
Q_DECLARE_PUBLIC(QtGradientStopsModel)
public:
QMap<qreal, QtGradientStop *> m_posToStop;
QHash<QtGradientStop *, qreal> m_stopToPos;
QHash<QtGradientStop *, bool> m_selection;
QtGradientStop *m_current = nullptr;
};
QtGradientStopsModel::QtGradientStopsModel(QObject *parent)
: QObject(parent), d_ptr(new QtGradientStopsModelPrivate)
{
d_ptr->q_ptr = this;
}
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;
auto itStop = gradStops.constFind(pos);
if (itStop == gradStops.constBegin()) {
++itStop;
return itStop.value()->color();
}
if (itStop == --gradStops.constEnd()) {
--itStop;
return itStop.value()->color();
}
auto itPrev = itStop;
auto 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, 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, 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();
auto itStop = stopList.cbegin();
while (itStop != stopList.constEnd()) {
QtGradientStop *stop = itStop.value();
if (isSelected(stop))
return stop;
++itStop;
};
return 0;
}
QtGradientStop *QtGradientStopsModel::lastSelected() const
{
PositionStopMap stopList = stops();
auto itStop = stopList.cend();
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;
auto itStop = forward ? stopList.cbegin() : stopList.cend();
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);
}
void QtGradientStopsModel::flipAll()
{
QMap<qreal, QtGradientStop *> stopsMap = stops();
QHash<QtGradientStop *, bool> swappedList;
for (auto itStop = stopsMap.keyValueEnd(), begin = stopsMap.keyValueBegin(); itStop != begin;) {
--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