// 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

#include "qtcolorline.h"
#include "qdrawutil.h"

#include <QtGui/QPainter>
#include <QtGui/QPaintEvent>
#include <QtWidgets/QStyleOption>
#include <QtGui/QRegion>

QT_BEGIN_NAMESPACE

class QtColorLinePrivate
{
    QtColorLine *q_ptr;
    Q_DECLARE_PUBLIC(QtColorLine)
public:
    QtColorLinePrivate();

    QColor color() const;
    void setColor(const QColor &color);

    QtColorLine::ColorComponent colorComponent() const;
    void setColorComponent(QtColorLine::ColorComponent component);

    void setIndicatorSize(int size);
    int indicatorSize() const;

    void setIndicatorSpace(int space);
    int indicatorSpace() const;

    void setFlip(bool flip);
    bool flip() const;

    void setBackgroundCheckered(bool checkered);
    bool isBackgroundCheckered() const;

    void setOrientation(Qt::Orientation orientation);
    Qt::Orientation orientation() const;

    void resizeEvent(QResizeEvent *event);
    void paintEvent(QPaintEvent *event);
    void mousePressEvent(QMouseEvent *event);
    void mouseMoveEvent(QMouseEvent *event);
    void mouseReleaseEvent(QMouseEvent *event);
    void mouseDoubleClickEvent(QMouseEvent *event);
private:
    void checkColor();
    bool isMainPixmapValid() const;
    void validate();
    void recreateMainPixmap();
    QSize pixmapSizeFromGeometrySize(const QSize &geometrySize) const;
    QPixmap gradientPixmap(int size, Qt::Orientation orientation, const QColor &begin, const QColor &end, bool flipped = false) const;
    QPixmap gradientPixmap(Qt::Orientation orientation, const QColor &begin, const QColor &end, bool flipped = false) const;
    QPixmap hueGradientPixmap(int size, Qt::Orientation orientation, bool flipped = false,
                int saturation = 0xFF, int value = 0xFF, int alpha = 0xFF) const;
    QPixmap hueGradientPixmap(Qt::Orientation orientation, bool flipped = false,
                int saturation = 0xFF, int value = 0xFF, int alpha = 0xFF) const;

    QList<QRect> rects(const QPointF &point) const;

    QColor colorFromPoint(const QPointF &point) const;
    QPointF pointFromColor(const QColor &color) const;

    QColor m_color;
    QtColorLine::ColorComponent m_component;
    bool m_flipped;
    bool m_backgroundCheckered;
    Qt::Orientation m_orientation;
    bool m_dragging;
    bool m_combiningAlpha;
    int m_indicatorSize;
    int m_indicatorSpace;
    QPointF m_point;
    QPoint m_clickOffset;

    QPixmap m_mainPixmap;
    QPixmap m_alphalessPixmap;
    QPixmap m_semiAlphaPixmap;
    QSize m_pixmapSize;

    struct PixData {
        QSize size;
        QColor color;
        QtColorLine::ColorComponent component;
        bool flipped;
        Qt::Orientation orientation;
    };

    PixData m_lastValidMainPixmapData;
};

QtColorLinePrivate::QtColorLinePrivate()
    : m_color(Qt::black), m_component(QtColorLine::Value),
        m_flipped(false), m_backgroundCheckered(true), m_orientation(Qt::Horizontal), m_dragging(false), m_combiningAlpha(false)
{
    m_indicatorSize = 22;
    m_indicatorSpace = 0;
    m_pixmapSize = QSize(0, 0);
    m_point = pointFromColor(m_color);
}

void QtColorLinePrivate::setColor(const QColor &color)
{
    if (m_color == color)
        return;
    if (!color.isValid())
        return;
    if (m_dragging) // Warning perhaps here, recursive call
        return;
    m_color = color;
    checkColor();
    m_point = pointFromColor(m_color);
    q_ptr->update();
}

QColor QtColorLinePrivate::color() const
{
    return m_color;
}

void QtColorLinePrivate::setColorComponent(QtColorLine::ColorComponent component)
{
    if (m_component == component)
        return;
    if (m_dragging) // Warning perhaps here, recursive call
        return;
    m_component = component;
    checkColor();
    m_point = pointFromColor(m_color);
    q_ptr->update();
}

QtColorLine::ColorComponent QtColorLinePrivate::colorComponent() const
{
    return m_component;
}

void QtColorLinePrivate::setIndicatorSize(int size)
{
    if (size <= 0)
        return;
    if (m_dragging) // Warning perhaps here, recursive call
        return;
    if (m_indicatorSize == size)
        return;
    m_indicatorSize = size;
    m_pixmapSize = pixmapSizeFromGeometrySize(q_ptr->contentsRect().size());
    q_ptr->update();
    q_ptr->updateGeometry();
}

int QtColorLinePrivate::indicatorSize() const
{
    return m_indicatorSize;
}

void QtColorLinePrivate::setIndicatorSpace(int space)
{
    if (space < 0)
        return;
    if (m_dragging) // Warning perhaps here, recursive call
        return;
    if (m_indicatorSpace == space)
        return;
    m_indicatorSpace = space;
    m_pixmapSize = pixmapSizeFromGeometrySize(q_ptr->contentsRect().size());
    q_ptr->update();
}

int QtColorLinePrivate::indicatorSpace() const
{
    return m_indicatorSpace;
}

void QtColorLinePrivate::setFlip(bool flip)
{
    if (m_dragging) // Warning perhaps here, recursive call
        return;
    if (m_flipped == flip)
        return;
    m_flipped = flip;
    m_point = pointFromColor(m_color);
    q_ptr->update();
}

bool QtColorLinePrivate::flip() const
{
    return m_flipped;
}

void QtColorLinePrivate::setBackgroundCheckered(bool checkered)
{
    if (m_backgroundCheckered == checkered)
        return;
    m_backgroundCheckered = checkered;
    q_ptr->update();
}

bool QtColorLinePrivate::isBackgroundCheckered() const
{
    return m_backgroundCheckered;
}

void QtColorLinePrivate::setOrientation(Qt::Orientation orientation)
{
    if (m_dragging) // Warning perhaps here, recursive call
        return;
    if (m_orientation == orientation)
        return;

    m_orientation = orientation;
    if (!q_ptr->testAttribute(Qt::WA_WState_OwnSizePolicy)) {
        QSizePolicy sp = q_ptr->sizePolicy();
        sp.transpose();
        q_ptr->setSizePolicy(sp);
        q_ptr->setAttribute(Qt::WA_WState_OwnSizePolicy, false);
    }
    m_point = pointFromColor(m_color);
    q_ptr->update();
    q_ptr->updateGeometry();
}

Qt::Orientation QtColorLinePrivate::orientation() const
{
    return m_orientation;
}

void QtColorLinePrivate::checkColor()
{
    switch (m_component) {
        case QtColorLine::Red:
        case QtColorLine::Green:
        case QtColorLine::Blue:
            if (m_color.spec() != QColor::Rgb)
                m_color = m_color.toRgb();
            break;
        case QtColorLine::Hue:
        case QtColorLine::Saturation:
        case QtColorLine::Value:
            if (m_color.spec() != QColor::Hsv)
                m_color = m_color.toHsv();
            break;
        default:
            break;
    }
    if (m_color.spec() == QColor::Hsv) {
        if (m_color.hue() == 360 || m_color.hue() == -1) {
            m_color.setHsvF(0.0, m_color.saturationF(), m_color.valueF(), m_color.alphaF());
        }
    }
}

bool QtColorLinePrivate::isMainPixmapValid() const
{
    if (m_mainPixmap.isNull()) {
        if (m_pixmapSize.isEmpty())
            return true;
        else
            return false;
    }
    if (m_lastValidMainPixmapData.component != m_component)
        return false;
    if (m_lastValidMainPixmapData.size != m_pixmapSize)
        return false;
    if (m_lastValidMainPixmapData.flipped != m_flipped)
        return false;
    if (m_lastValidMainPixmapData.orientation != m_orientation)
        return false;
    if (m_lastValidMainPixmapData.color == m_color)
        return true;
    switch (m_component) {
        case QtColorLine::Red:
            if (m_color.green() == m_lastValidMainPixmapData.color.green() &&
                m_color.blue() == m_lastValidMainPixmapData.color.blue() &&
                (!m_combiningAlpha || m_color.alpha() == m_lastValidMainPixmapData.color.alpha()))
                return true;
            break;
        case QtColorLine::Green:
            if (m_color.red() == m_lastValidMainPixmapData.color.red() &&
                m_color.blue() == m_lastValidMainPixmapData.color.blue() &&
                (!m_combiningAlpha || m_color.alpha() == m_lastValidMainPixmapData.color.alpha()))
                return true;
            break;
        case QtColorLine::Blue:
            if (m_color.red() == m_lastValidMainPixmapData.color.red() &&
                m_color.green() == m_lastValidMainPixmapData.color.green() &&
                (!m_combiningAlpha || m_color.alpha() == m_lastValidMainPixmapData.color.alpha()))
                return true;
            break;
        case QtColorLine::Hue:
            if (m_color.saturation() == m_lastValidMainPixmapData.color.saturation() &&
                m_color.value() == m_lastValidMainPixmapData.color.value() &&
                (!m_combiningAlpha || m_color.alpha() == m_lastValidMainPixmapData.color.alpha()))
                return true;
            break;
        case QtColorLine::Saturation:
            if (m_color.hue() == m_lastValidMainPixmapData.color.hue() &&
                m_color.value() == m_lastValidMainPixmapData.color.value() &&
                (!m_combiningAlpha || m_color.alpha() == m_lastValidMainPixmapData.color.alpha()))
                return true;
            break;
        case QtColorLine::Value:
            if (m_color.hue() == m_lastValidMainPixmapData.color.hue() &&
                m_color.saturation() == m_lastValidMainPixmapData.color.saturation() &&
                (!m_combiningAlpha || m_color.alpha() == m_lastValidMainPixmapData.color.alpha()))
                return true;
            break;
        case QtColorLine::Alpha:
            if (m_color.hue() == m_lastValidMainPixmapData.color.hue() &&
                m_color.saturation() == m_lastValidMainPixmapData.color.saturation() &&
                m_color.value() == m_lastValidMainPixmapData.color.value())
                return true;
    }
    return false;
}

void QtColorLinePrivate::validate()
{
    if (isMainPixmapValid())
        return;

    recreateMainPixmap();
}

QPixmap QtColorLinePrivate::gradientPixmap(Qt::Orientation orientation, const QColor &begin, const QColor &end, bool flipped) const
{
    int size = m_pixmapSize.width();
    if (orientation == Qt::Vertical)
        size = m_pixmapSize.height();
    return gradientPixmap(size, orientation, begin, end, flipped);
}

QPixmap QtColorLinePrivate::gradientPixmap(int size, Qt::Orientation orientation,
            const QColor &begin, const QColor &end, bool flipped) const
{
    int gradW = size;
    int gradH = size;
    int w = size;
    int h = size;
    if (orientation == Qt::Horizontal) {
        gradH = 0;
        h = 1;
    } else {
        gradW = 0;
        w = 1;
    }
    QColor c1 = begin;
    QColor c2 = end;
    if (flipped) {
        c1 = end;
        c2 = begin;
    }
    QLinearGradient lg(0, 0, gradW, gradH);
    lg.setColorAt(0, c1);
    lg.setColorAt(1, c2);
    QImage img(w, h, QImage::Format_ARGB32);
    QPainter p(&img);
    p.setCompositionMode(QPainter::CompositionMode_Source);
    p.fillRect(QRect(0, 0, w, h), lg);
    return QPixmap::fromImage(img);
}

QPixmap QtColorLinePrivate::hueGradientPixmap(Qt::Orientation orientation, bool flipped,
                int saturation, int value, int alpha) const
{
    int size = m_pixmapSize.width();
    if (orientation == Qt::Vertical)
        size = m_pixmapSize.height();
    return hueGradientPixmap(size, orientation, flipped, saturation, value, alpha);
}

QPixmap QtColorLinePrivate::hueGradientPixmap(int size, Qt::Orientation orientation, bool flipped,
                int saturation, int value, int alpha) const
{
    int gradW = size + 1;
    int gradH = size + 1;
    int w = size;
    int h = size;
    if (orientation == Qt::Horizontal) {
        gradH = 0;
        h = 1;
    } else {
        gradW = 0;
        w = 1;
    }
    QList<QColor> colorList;
    colorList << QColor::fromHsv(0, saturation, value, alpha);
    colorList << QColor::fromHsv(60, saturation, value, alpha);
    colorList << QColor::fromHsv(120, saturation, value, alpha);
    colorList << QColor::fromHsv(180, saturation, value, alpha);
    colorList << QColor::fromHsv(240, saturation, value, alpha);
    colorList << QColor::fromHsv(300, saturation, value, alpha);
    colorList << QColor::fromHsv(0, saturation, value, alpha);
    QLinearGradient lg(0, 0, gradW, gradH);
    for (int i = 0; i <= 6; i++)
        lg.setColorAt(double(i) / 6.0, flipped ? colorList.at(6 - i) : colorList.at(i));
    QImage img(w, h, QImage::Format_ARGB32);
    QPainter p(&img);
    p.setCompositionMode(QPainter::CompositionMode_Source);
    p.fillRect(QRect(0, 0, w, h), lg);
    return QPixmap::fromImage(img);
}

void QtColorLinePrivate::recreateMainPixmap()
{
    m_lastValidMainPixmapData.size = m_pixmapSize;
    m_lastValidMainPixmapData.component = m_component;
    m_lastValidMainPixmapData.color = m_color;
    m_lastValidMainPixmapData.flipped = m_flipped;
    m_lastValidMainPixmapData.orientation = m_orientation;

    if (m_pixmapSize.isEmpty()) {
        m_mainPixmap = QPixmap();
        m_alphalessPixmap = QPixmap();
        m_semiAlphaPixmap = QPixmap();
        return;
    }

    if (m_mainPixmap.size() != m_pixmapSize) {
        m_mainPixmap = QPixmap(m_pixmapSize);
        m_alphalessPixmap = QPixmap(m_pixmapSize);
        m_semiAlphaPixmap = QPixmap(m_pixmapSize);
    }

    Qt::Orientation orient = m_orientation;
    const bool flip = m_flipped;

    const int r = m_color.red();
    const int g = m_color.green();
    const int b = m_color.blue();
    const int h = m_color.hue();
    const int s = m_color.saturation();
    const int v = m_color.value();
    const int a = m_color.alpha();
    const double coef = 0.5;
    const int semi = qRound(a * coef + 0xFF * (1.0 - coef));

    if (m_component == QtColorLine::Hue) {
        m_alphalessPixmap = hueGradientPixmap(orient, flip, s, v, 0xFF);
        if (m_combiningAlpha) {
            m_mainPixmap = hueGradientPixmap(orient, flip, s, v, a);
            m_semiAlphaPixmap = hueGradientPixmap(orient, flip, s, v, semi);
        }
    } else if (m_component == QtColorLine::Saturation) {
        m_alphalessPixmap = gradientPixmap(orient, QColor::fromHsv(h, 0, v, 0xFF), QColor::fromHsv(h, 0xFF, v, 0xFF), flip);
        if (m_combiningAlpha) {
            m_mainPixmap = gradientPixmap(orient, QColor::fromHsv(h, 0, v, a), QColor::fromHsv(h, 0xFF, v, a), flip);
            m_semiAlphaPixmap = gradientPixmap(orient, QColor::fromHsv(h, 0, v, semi), QColor::fromHsv(h, 0xFF, v, semi), flip);
        }
    } else if (m_component == QtColorLine::Value) {
        m_alphalessPixmap = gradientPixmap(orient, QColor::fromRgb(0, 0, 0, 0xFF), QColor::fromHsv(h, s, 0xFF, 0xFF), flip);
        if (m_combiningAlpha) {
            m_mainPixmap = gradientPixmap(orient, QColor::fromRgb(0, 0, 0, a), QColor::fromHsv(h, s, 0xFF, a), flip);
            m_semiAlphaPixmap = gradientPixmap(orient, QColor::fromRgb(0, 0, 0, semi), QColor::fromHsv(h, s, 0xFF, semi), flip);
        }
    } else if (m_component == QtColorLine::Red) {
        m_alphalessPixmap = gradientPixmap(orient, QColor::fromRgb(0, g, b, 0xFF), QColor::fromRgb(0xFF, g, b, 0xFF), flip);
        if (m_combiningAlpha) {
            m_mainPixmap = gradientPixmap(orient, QColor::fromRgb(0, g, b, a), QColor::fromRgb(0xFF, g, b, a), flip);
            m_semiAlphaPixmap = gradientPixmap(orient, QColor::fromRgb(0, g, b, semi), QColor::fromRgb(0xFF, g, b, semi), flip);
        }
    } else if (m_component == QtColorLine::Green) {
        m_alphalessPixmap = gradientPixmap(orient, QColor::fromRgb(r, 0, b, 0xFF), QColor::fromRgb(r, 0xFF, b, 0xFF), flip);
        if (m_combiningAlpha) {
            m_mainPixmap = gradientPixmap(orient, QColor::fromRgb(r, 0, b, a), QColor::fromRgb(r, 0xFF, b, a), flip);
            m_semiAlphaPixmap = gradientPixmap(orient, QColor::fromRgb(r, 0, b, semi), QColor::fromRgb(r, 0xFF, b, semi), flip);
        }
    } else if (m_component == QtColorLine::Blue) {
        m_alphalessPixmap = gradientPixmap(orient, QColor::fromRgb(r, g, 0, 0xFF), QColor::fromRgb(r, g, 0xFF, 0xFF), flip);
        if (m_combiningAlpha) {
            m_mainPixmap = gradientPixmap(orient, QColor::fromRgb(r, g, 0, a), QColor::fromRgb(r, g, 0xFF, a), flip);
            m_semiAlphaPixmap = gradientPixmap(orient, QColor::fromRgb(r, g, 0, semi), QColor::fromRgb(r, g, 0xFF, semi), flip);
        }
    } else if (m_component == QtColorLine::Alpha) {
        m_mainPixmap = gradientPixmap(orient, QColor::fromRgb(r, g, b, 0), QColor::fromRgb(r, g, b, 0xFF), flip);

//        m_alphalessPixmap = gradientPixmap(orient, QColor::fromRgb(r, g, b, 0xFF), QColor::fromRgb(r, g, b, 0xFF), flip);
//        m_semiAlphaPixmap = gradientPixmap(orient, QColor::fromRgb(r, g, b, semi), QColor::fromRgb(r, g, b, semi), flip);
    }
    if (!m_combiningAlpha && m_component != QtColorLine::Alpha)
        m_mainPixmap = m_alphalessPixmap;
}

QSize QtColorLinePrivate::pixmapSizeFromGeometrySize(
        const QSize &geometrySize) const
{
    QSize size(m_indicatorSize + 2 * m_indicatorSpace - 1,
                m_indicatorSize + 2 * m_indicatorSpace - 1);
    if (m_orientation == Qt::Horizontal)
        size.setHeight(0);
    else
        size.setWidth(0);
    return geometrySize - size;
}

QColor QtColorLinePrivate::colorFromPoint(const QPointF &point) const
{
    QPointF p = point;
    if (p.x() < 0)
        p.setX(0.0);
    else if (p.x() > 1)
        p.setX(1.0);
    if (p.y() < 0)
        p.setY(0.0);
    else if (p.y() > 1)
        p.setY(1.0);

    double pos = p.x();
    if (m_orientation == Qt::Vertical)
        pos = p.y();
    if (m_flipped)
        pos = 1.0 - pos;
    QColor c;
    qreal hue;
    switch (m_component) {
        case QtColorLine::Red:
            c.setRgbF(pos, m_color.greenF(), m_color.blueF(), m_color.alphaF());
            break;
        case QtColorLine::Green:
            c.setRgbF(m_color.redF(), pos, m_color.blueF(), m_color.alphaF());
            break;
        case QtColorLine::Blue:
            c.setRgbF(m_color.redF(), m_color.greenF(), pos, m_color.alphaF());
            break;
        case QtColorLine::Hue:
            hue = pos;
            hue *= 35999.0 / 36000.0;
            c.setHsvF(hue, m_color.saturationF(), m_color.valueF(), m_color.alphaF());
            break;
        case QtColorLine::Saturation:
            c.setHsvF(m_color.hueF(), pos, m_color.valueF(), m_color.alphaF());
            break;
        case QtColorLine::Value:
            c.setHsvF(m_color.hueF(), m_color.saturationF(), pos, m_color.alphaF());
            break;
        case QtColorLine::Alpha:
            c.setHsvF(m_color.hueF(), m_color.saturationF(), m_color.valueF(), pos);
            break;
    }
    return c;
}

QPointF QtColorLinePrivate::pointFromColor(const QColor &color) const
{
    qreal hue = color.hueF();
    if (color.hue() == 360)
        hue = 0.0;
    else
        hue *= 36000.0 / 35999.0;

    double pos = 0.0;
    switch (m_component) {
        case QtColorLine::Red:
            pos = color.redF();
            break;
        case QtColorLine::Green:
            pos = color.greenF();
            break;
        case QtColorLine::Blue:
            pos = color.blueF();
            break;
        case QtColorLine::Hue:
            pos = hue;
            break;
        case QtColorLine::Saturation:
            pos = color.saturationF();
            break;
        case QtColorLine::Value:
            pos = color.valueF();
            break;
        case QtColorLine::Alpha:
            pos = color.alphaF();
            break;
    }
    if (m_flipped)
        pos = 1.0 - pos;
    QPointF p(pos, pos);
    if (m_orientation == Qt::Horizontal)
        p.setY(0);
    else
        p.setX(0);
    return p;
}

QList<QRect> QtColorLinePrivate::rects(const QPointF &point) const
{
    QRect r = q_ptr->geometry();
    r.moveTo(0, 0);

    int x1 = (int)((r.width() - m_indicatorSize - 2 * m_indicatorSpace) * point.x() + 0.5);
    int x2 = x1 + m_indicatorSize + 2 * m_indicatorSpace;
    int y1 = (int)((r.height() - m_indicatorSize - 2 * m_indicatorSpace) * point.y() + 0.5);
    int y2 = y1 + m_indicatorSize + 2 * m_indicatorSpace;

    QList<QRect> rects;
    if (m_orientation == Qt::Horizontal) {
        // r0 r1 r2
        QRect r0(0, 0, x1, r.height());
        QRect r1(x1 + m_indicatorSpace, 0, m_indicatorSize, r.height());
        QRect r2(x2, 0, r.width() - x2, r.height());

        rects << r0 << r1 << r2;
    } else {
        // r0
        // r1
        // r2
        QRect r0(0, 0, r.width(), y1);
        QRect r1(0, y1 + m_indicatorSpace, r.width(), m_indicatorSize);
        QRect r2(0, y2, r.width(), r.height() - y2);

        rects << r0 << r1 << r2;
    }
    return rects;
}

void QtColorLinePrivate::resizeEvent(QResizeEvent *event)
{
    m_pixmapSize = pixmapSizeFromGeometrySize(event->size());
}

void QtColorLinePrivate::paintEvent(QPaintEvent *)
{
    QRect rect = q_ptr->rect();

    QList<QRect> r = rects(m_point);

    QColor c = colorFromPoint(m_point);
    if (!m_combiningAlpha && m_component != QtColorLine::Alpha)
        c.setAlpha(0xFF);

    QPainter p(q_ptr);
    if (q_ptr->isEnabled()) {
        if (m_backgroundCheckered) {
            int pixSize = 20;
            QPixmap pm(2 * pixSize, 2 * pixSize);
            QPainter pmp(&pm);
            pmp.fillRect(0, 0, pixSize, pixSize, Qt::white);
            pmp.fillRect(pixSize, pixSize, pixSize, pixSize, Qt::white);
            pmp.fillRect(0, pixSize, pixSize, pixSize, Qt::black);
            pmp.fillRect(pixSize, 0, pixSize, pixSize, Qt::black);
            pmp.end();

            p.setBrushOrigin((rect.width() % pixSize + pixSize) / 2, (rect.height() % pixSize + pixSize) / 2);

            QRegion region(r[1].adjusted(4, 4, -4, -4));
            region += QRect(rect.topLeft(), QPoint(r[1].left() + 0, rect.bottom()));
            region += QRect(QPoint(r[1].right() - 0, rect.top()), rect.bottomRight());
            region += QRect(rect.topLeft(), QPoint(rect.right(), r[1].top() + 0));
            region += QRect(QPoint(rect.left(), r[1].bottom() - 0), rect.bottomRight());
            p.setClipRegion(region);
            p.fillRect(rect, pm);
            p.setBrushOrigin(0, 0);
            p.setClipping(false);
        }

        validate();

        QSize fieldSize = pixmapSizeFromGeometrySize(q_ptr->geometry().size());

        QPoint posOnField = r[1].topLeft() - QPoint(m_indicatorSpace, m_indicatorSpace);
        int x = posOnField.x();
        int y = posOnField.y();
        int w = fieldSize.width();
        int h = fieldSize.height();

        QRect r0, r2;
        if (m_orientation == Qt::Horizontal) {
            r0 = QRect(0, 0, x, m_pixmapSize.height());
            r2 = QRect(x + 1, 0, w - x - 1, m_pixmapSize.height());
        } else {
            r0 = QRect(0, 0, m_pixmapSize.width(), y);
            r2 = QRect(0, y + 1, m_pixmapSize.width(), h - y - 1);
        }

        p.setBrush(m_mainPixmap);
        p.setPen(Qt::NoPen);
        if (r[0].isValid()) {
            p.drawRect(r[0]);
        }
        if (r[2].isValid()) {
            p.setBrushOrigin(r[2].topLeft() - r2.topLeft());
            p.drawRect(r[2]);
        }
        if (m_indicatorSpace) {
            p.setBrush(c);
            if (m_orientation == Qt::Horizontal) {
                p.drawRect(r[1].adjusted(-m_indicatorSpace, 0, -r[1].width(), 0));
                p.drawRect(r[1].adjusted(r[1].width(), 0, m_indicatorSpace, 0));
            } else {
                p.drawRect(r[1].adjusted(0, -m_indicatorSpace, 0, -r[1].height()));
                p.drawRect(r[1].adjusted(0, r[1].height(), 0, m_indicatorSpace));
            }
        }

        QPen pen(c);
        p.setPen(pen);
        p.setBrush(Qt::NoBrush);
        if (r[1].isValid()) {
            p.drawRect(r[1].adjusted(0, 0, -1, -1));
        //    p.drawRect(r[1].adjusted(1, 1, -2, -2));
        }
        double coef = 9.0 / 10;
        p.setPen(Qt::NoPen);
        if (m_component != QtColorLine::Alpha && m_combiningAlpha) {
            p.setBrush(m_alphalessPixmap);
            if (r[0].isValid()) {
                p.setBrushOrigin(QPoint(0, 0));
                QRect thinRect1 = r[0];
                QRect thinRect2 = r[0];
                QRect thinRect = r[0];
                if (m_orientation == Qt::Horizontal) {
                    thinRect1.adjust(0, qRound(thinRect1.height() * coef), 0, 0);
                    thinRect2.adjust(0, 0, 0, -qRound(thinRect2.height() * coef));
                    thinRect.adjust(0, qRound(thinRect.height() * coef), 0, -qRound(thinRect.height() * coef));
                } else {
                    thinRect1.adjust(qRound(thinRect1.width() * coef), 0, 0, 0);
                    thinRect2.adjust(0, 0, -qRound(thinRect2.width() * coef), 0);
                    thinRect.adjust(qRound(thinRect.width() * coef), 0, -qRound(thinRect.width() * coef), 0);
                }
                p.drawRect(thinRect1);
                p.drawRect(thinRect2);
                //p.drawRect(thinRect);
            }
            if (r[2].isValid()) {
                p.setBrushOrigin(r[2].topLeft() - r2.topLeft());
                QRect thinRect1 = r[2];
                QRect thinRect2 = r[2];
                QRect thinRect = r[2];
                if (m_orientation == Qt::Horizontal) {
                    thinRect1.adjust(0, qRound(thinRect1.height() * coef), 0, 0);
                    thinRect2.adjust(0, 0, 0, -qRound(thinRect2.height() * coef));
                    thinRect.adjust(0, qRound(thinRect.height() * coef), 0, -qRound(thinRect.height() * coef));
                } else {
                    thinRect1.adjust(qRound(thinRect1.width() * coef), 0, 0, 0);
                    thinRect2.adjust(0, 0, -qRound(thinRect2.width() * coef), 0);
                    thinRect.adjust(qRound(thinRect.width() * coef), 0, -qRound(thinRect.width() * coef), 0);
                }
                p.drawRect(thinRect1);
                p.drawRect(thinRect2);
                //p.drawRect(thinRect);
            }
            /*

*/





            p.setPen(Qt::NoPen);

            p.setBrush(m_semiAlphaPixmap);
            if (r[0].isValid()) {
                p.setBrushOrigin(QPoint(0, 0));
                QRect thinRect1 = r[0];
                QRect thinRect2 = r[0];
                QRect thinRect = r[0];
                if (m_orientation == Qt::Horizontal) {
                    thinRect1.adjust(0, qRound(thinRect1.height() * coef) - 1, 0, 0);
                    thinRect1.setBottom(thinRect1.top());
                    thinRect2.adjust(0, 0, 0, -qRound(thinRect2.height() * coef) + 1);
                    thinRect2.setTop(thinRect2.bottom());
                    thinRect.adjust(0, qRound(thinRect.height() * coef), 0, -qRound(thinRect.height() * coef));
                } else {
                    thinRect1.adjust(qRound(thinRect1.width() * coef) - 1, 0, 0, 0);
                    thinRect1.setRight(thinRect1.left());
                    thinRect2.adjust(0, 0, -qRound(thinRect2.width() * coef) + 1, 0);
                    thinRect2.setLeft(thinRect2.right());
                    thinRect.adjust(qRound(thinRect.width() * coef), 0, -qRound(thinRect.width() * coef), 0);
                }
                p.drawRect(thinRect1);
                p.drawRect(thinRect2);
                //p.drawRect(thinRect);
            }
            if (r[2].isValid()) {
                p.setBrushOrigin(r[2].topLeft() - r2.topLeft());
                QRect thinRect1 = r[2];
                QRect thinRect2 = r[2];
                QRect thinRect = r[2];
                if (m_orientation == Qt::Horizontal) {
                    thinRect1.adjust(0, qRound(thinRect1.height() * coef) - 1, 0, 0);
                    thinRect1.setBottom(thinRect1.top());
                    thinRect2.adjust(0, 0, 0, -qRound(thinRect2.height() * coef) + 1);
                    thinRect2.setTop(thinRect2.bottom());
                    thinRect.adjust(0, qRound(thinRect.height() * coef), 0, -qRound(thinRect.height() * coef));
                } else {
                    thinRect1.adjust(qRound(thinRect1.width() * coef) - 1, 0, 0, 0);
                    thinRect1.setRight(thinRect1.left());
                    thinRect2.adjust(0, 0, -qRound(thinRect2.width() * coef) + 1, 0);
                    thinRect2.setLeft(thinRect2.right());
                    thinRect.adjust(qRound(thinRect.width() * coef), 0, -qRound(thinRect.width() * coef), 0);
                }
                p.drawRect(thinRect1);
                p.drawRect(thinRect2);
                //p.drawRect(thinRect);
            }
            p.setBrush(m_alphalessPixmap);
            QRegion region;
            if (m_orientation == Qt::Horizontal) {
                region += r[1].adjusted(0, qRound(r[1].height() * coef), 0, 0);
                region += r[1].adjusted(0, 0, 0, -qRound(r[1].height() * coef));
                p.setClipRegion(region);
            } else {
                region += r[1].adjusted(qRound(r[1].width() * coef), 0, 0, 0);
                region += r[1].adjusted(0, 0, -qRound(r[1].width() * coef), 0);
                p.setClipRegion(region);
            }
            p.setClipRegion(region);
            p.setBrush(Qt::NoBrush);
            p.setPen(QPen(QColor(c.rgb())));

            p.drawRect(r[1].adjusted(0, 0, -1, -1));
        //    p.drawRect(r[1].adjusted(1, 1, -2, -2));
/*
            p.setBrush(m_semiAlphaPixmap);
            if (m_orientation == Qt::Horizontal) {
                QRect top = r[1].adjusted(0, 0, 0, -qRound(r[1].height() * coef) + 1);
                top.setTop(top.bottom());
                QRect bottom = r[1].adjusted(0, qRound(r[1].height() * coef) - 1, 0, 0);
                top.setBottom(bottom.top());
                p.setClipRect(top);
                p.setClipRect(bottom, Qt::UniteClip);
            } else {

            }
            QColor semiColor(c.rgb());
            semiColor.setAlpha((c.alpha() + 0xFF) / 2);
            p.setPen(QPen(semiColor));
            p.drawRect(r[1].adjusted(0, 0, -1, -1));
        //    p.drawRect(r[1].adjusted(1, 1, -2, -2));
*/
            p.setClipping(false);
        }
    }

    p.setBrush(Qt::NoBrush);
    int lw = 4;
    //int br = 1;
    int br = 0;
    r[1].adjust(br, br, -br, -br);
    if (r[1].adjusted(lw, lw, -lw, -lw).isValid()) {
        QStyleOptionFrame opt;
        opt.initFrom(q_ptr);
        opt.rect = r[1];
        opt.lineWidth = 2;
        opt.midLineWidth = 1;
        if (m_dragging)
            opt.state |= QStyle::State_Sunken;
        else
            opt.state |= QStyle::State_Raised;
        q_ptr->style()->drawPrimitive(QStyle::PE_Frame, &opt, &p, q_ptr);
        QRect colorRect = r[1].adjusted(lw, lw, -lw, -lw);
        if (q_ptr->isEnabled()) {
            p.fillRect(colorRect, c);
            const QColor frameColor(0, 0, 0, 38);
            p.setPen(frameColor);
            p.drawRect(colorRect.adjusted(0, 0, -1, -1));
            /*
            p.fillRect(colorRect.width() / 4 + colorRect.left(),
                       colorRect.height() / 4 + colorRect.top(),
                       colorRect.width() / 2,
                       colorRect.height() / 2,
                       QColor(c.rgb()));
            */
            /*
            if (m_component != QtColorLine::Alpha) {
                p.fillRect(colorRect.adjusted(0, colorRect.height() * 4 / 5, 0, 0), QColor(c.rgb()));
                p.fillRect(colorRect.adjusted(0, 0, 0, -colorRect.height() * 4 / 5), QColor(c.rgb()));
            }
            */
        }
    }
}

void QtColorLinePrivate::mousePressEvent(QMouseEvent *event)
{
    if (event->button() != Qt::LeftButton)
        return;

    QList<QRect> r = rects(m_point);
    QPoint clickPos = event->position().toPoint();

    QPoint posOnField = r[1].topLeft() - QPoint(m_indicatorSpace, m_indicatorSpace);
    m_clickOffset = posOnField - clickPos;

    if (!r[1].contains(clickPos))
        return;
    m_dragging = true;
    q_ptr->update();
}

void QtColorLinePrivate::mouseMoveEvent(QMouseEvent *event)
{
    if (!m_dragging)
        return;
    QPoint newPos = event->position().toPoint();

    QSize fieldSize = q_ptr->geometry().size() -
            QSize(m_indicatorSize + 2 * m_indicatorSpace - 1, m_indicatorSize + 2 * m_indicatorSpace - 1);
    QPoint newPosOnField = newPos + m_clickOffset;
    if (newPosOnField.x() < 0)
        newPosOnField.setX(0);
    else if (newPosOnField.x() > fieldSize.width())
        newPosOnField.setX(fieldSize.width());
    if (newPosOnField.y() < 0)
        newPosOnField.setY(0);
    else if (newPosOnField.y() > fieldSize.height())
        newPosOnField.setY(fieldSize.height());

    const double x = double(newPosOnField.x()) / fieldSize.width();
    const double y = double(newPosOnField.y()) / fieldSize.height();
    m_point = QPointF(x, y);
    QColor color = colorFromPoint(m_point);
    if (m_color == color)
        return;
    m_color = color;
    emit q_ptr->colorChanged(color); // maybe before internal set, 1 line above
    q_ptr->update();
}

void QtColorLinePrivate::mouseReleaseEvent(QMouseEvent *event)
{
    if (event->button() != Qt::LeftButton)
        return;
    m_dragging = false;
    q_ptr->update();
}

void QtColorLinePrivate::mouseDoubleClickEvent(QMouseEvent *event)
{
    if (event->button() != Qt::LeftButton)
        return;

    QList<QRect> r = rects(m_point);
    QPoint clickPos = event->position().toPoint();
    if (!r[0].contains(clickPos) && !r[2].contains(clickPos))
        return;
    QPoint newPosOnField = clickPos;
    if (r[2].contains(clickPos))
        newPosOnField -= QPoint(m_indicatorSize + 2 * m_indicatorSpace - 2, m_indicatorSize + 2 * m_indicatorSpace - 2);
    QSize fieldSize = q_ptr->geometry().size() -
            QSize(m_indicatorSize + 2 * m_indicatorSpace - 1, m_indicatorSize + 2 * m_indicatorSpace - 1);

    const double x = double(newPosOnField.x()) / fieldSize.width();
    const double y = double(newPosOnField.y()) / fieldSize.height();
    m_point = QPointF(x, y);
    QColor color = colorFromPoint(m_point);
    if (m_color == color)
        return;
    m_color = color;
    emit q_ptr->colorChanged(color); // maybe before internal set, 1 line above
    q_ptr->update();
}

////////////////////////////////////////////////////

QtColorLine::QtColorLine(QWidget *parent)
    : QWidget(parent), d_ptr(new QtColorLinePrivate)
{
    d_ptr->q_ptr = this;

    setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed));
}

QtColorLine::~QtColorLine()
{
}

QSize QtColorLine::minimumSizeHint() const
{
    return QSize(d_ptr->m_indicatorSize, d_ptr->m_indicatorSize);
}

QSize QtColorLine::sizeHint() const
{
    return QSize(d_ptr->m_indicatorSize, d_ptr->m_indicatorSize);
}

void QtColorLine::setColor(const QColor &color)
{
    d_ptr->setColor(color);
}

QColor QtColorLine::color() const
{
    return d_ptr->color();
}

void QtColorLine::setColorComponent(QtColorLine::ColorComponent component)
{
    d_ptr->setColorComponent(component);
}

QtColorLine::ColorComponent QtColorLine::colorComponent() const
{
    return d_ptr->colorComponent();
}

void QtColorLine::setIndicatorSize(int size)
{
    d_ptr->setIndicatorSize(size);
}

int QtColorLine::indicatorSize() const
{
    return d_ptr->indicatorSize();
}

void QtColorLine::setIndicatorSpace(int space)
{
    d_ptr->setIndicatorSpace(space);
}

int QtColorLine::indicatorSpace() const
{
    return d_ptr->indicatorSpace();
}

void QtColorLine::setFlip(bool flip)
{
    d_ptr->setFlip(flip);
}

bool QtColorLine::flip() const
{
    return d_ptr->flip();
}

void QtColorLine::setBackgroundCheckered(bool checkered)
{
    d_ptr->setBackgroundCheckered(checkered);
}

bool QtColorLine::isBackgroundCheckered() const
{
    return d_ptr->isBackgroundCheckered();
}

void QtColorLine::setOrientation(Qt::Orientation orientation)
{
    d_ptr->setOrientation(orientation);
}

Qt::Orientation QtColorLine::orientation() const
{
    return d_ptr->orientation();
}
void QtColorLine::resizeEvent(QResizeEvent *event)
{
    d_ptr->resizeEvent(event);
}

void QtColorLine::paintEvent(QPaintEvent *event)
{
    d_ptr->paintEvent(event);
}

void QtColorLine::mousePressEvent(QMouseEvent *event)
{
    d_ptr->mousePressEvent(event);
}

void QtColorLine::mouseMoveEvent(QMouseEvent *event)
{
    d_ptr->mouseMoveEvent(event);
}

void QtColorLine::mouseReleaseEvent(QMouseEvent *event)
{
    d_ptr->mouseReleaseEvent(event);
}

void QtColorLine::mouseDoubleClickEvent(QMouseEvent *event)
{
    d_ptr->mouseDoubleClickEvent(event);
}

QT_END_NAMESPACE
