
/*
   SPDX-FileCopyrightText: 2003-2007 Clarence Dang <dang@kde.org>

   SPDX-License-Identifier: BSD-2-Clause
*/

#define DEBUG_KP_DUAL_COLOR_BUTTON 0

#include "kpDualColorButton.h"

#include "views/kpView.h"

#include "kpLogCategories.h"
#include <KColorMimeData>

#include <QApplication>
#include <QBitmap>
#include <QColorDialog>
#include <QDrag>
#include <QMouseEvent>
#include <QPainter>
#include <qdrawutil.h>

//---------------------------------------------------------------------

kpDualColorButton::kpDualColorButton(QWidget *parent)
    : QFrame(parent)
    , m_dragStartPoint(KP_INVALID_POINT)
{
    setSizePolicy(QSizePolicy::Fixed /*horizontal*/, QSizePolicy::Fixed /*vertical*/);
    setFrameStyle(QFrame::Panel | QFrame::Sunken);

    m_color[0] = kpColor(0, 0, 0); // black
    m_color[1] = kpColor(255, 255, 255); // white

    setAcceptDrops(true);
}

//---------------------------------------------------------------------

kpColor kpDualColorButton::color(int which) const
{
    Q_ASSERT(which == 0 || which == 1);

    return m_color[which];
}

//---------------------------------------------------------------------

kpColor kpDualColorButton::foregroundColor() const
{
    return color(0);
}

//---------------------------------------------------------------------

kpColor kpDualColorButton::backgroundColor() const
{
    return color(1);
}

//---------------------------------------------------------------------

void kpDualColorButton::setColor(int which, const kpColor &color)
{
    Q_ASSERT(which == 0 || which == 1);

    if (m_color[which] == color) {
        return;
    }

    m_oldColor[which] = m_color[which];
    m_color[which] = color;
    update();

    if (which == 0) {
        Q_EMIT foregroundColorChanged(color);
    } else {
        Q_EMIT backgroundColorChanged(color);
    }
}

//---------------------------------------------------------------------

void kpDualColorButton::setForegroundColor(const kpColor &color)
{
    setColor(0, color);
}

//---------------------------------------------------------------------

void kpDualColorButton::setBackgroundColor(const kpColor &color)
{
    setColor(1, color);
}

//---------------------------------------------------------------------

// public
kpColor kpDualColorButton::oldForegroundColor() const
{
    return m_oldColor[0];
}

//---------------------------------------------------------------------

// public
kpColor kpDualColorButton::oldBackgroundColor() const
{
    return m_oldColor[1];
}

//---------------------------------------------------------------------

// public virtual [base QWidget]
QSize kpDualColorButton::sizeHint() const
{
    return {52, 52};
}

//---------------------------------------------------------------------

// protected
QRect kpDualColorButton::swapPixmapRect() const
{
    QPixmap swapPixmap = QStringLiteral(":/icons/colorbutton_swap_16x16");

    return {contentsRect().width() - swapPixmap.width(), 0, swapPixmap.width(), swapPixmap.height()};
}

//---------------------------------------------------------------------

// protected
QRect kpDualColorButton::foregroundBackgroundRect() const
{
    QRect cr(contentsRect());
    return {cr.width() / 8, cr.height() / 8, cr.width() * 6 / 8, cr.height() * 6 / 8};
}

//---------------------------------------------------------------------

// protected
QRect kpDualColorButton::foregroundRect() const
{
    QRect fbr(foregroundBackgroundRect());
    return {fbr.x(), fbr.y(), fbr.width() * 3 / 4, fbr.height() * 3 / 4};
}

//---------------------------------------------------------------------

// protected
QRect kpDualColorButton::backgroundRect() const
{
    QRect fbr(foregroundBackgroundRect());
    return {fbr.x() + fbr.width() / 4, fbr.y() + fbr.height() / 4, fbr.width() * 3 / 4, fbr.height() * 3 / 4};
}

//---------------------------------------------------------------------

// protected virtual
void kpDualColorButton::dragEnterEvent(QDragEnterEvent *e)
{
#if DEBUG_KP_DUAL_COLOR_BUTTON
    qCDebug(kpLogWidgets) << "kpDualColorButton::dragEnterEvent() canDecode=" << KColorMimeData::canDecode(e->mimeData());
#endif
    e->accept();
}

//---------------------------------------------------------------------

// protected virtual [base QWidget]
void kpDualColorButton::dragMoveEvent(QDragMoveEvent *e)
{
#if DEBUG_KP_DUAL_COLOR_BUTTON
    qCDebug(kpLogWidgets) << "kpDualColorButton::dragMoveEvent() canDecode=" << KColorMimeData::canDecode(e->mimeData());
#endif
    e->setAccepted((foregroundRect().contains(e->position().toPoint()) ||
                    backgroundRect().contains(e->position().toPoint())) && KColorMimeData::canDecode(e->mimeData()));
}

//---------------------------------------------------------------------

// protected virtual [base QWidget]
void kpDualColorButton::dropEvent(QDropEvent *e)
{
    QColor col = KColorMimeData::fromMimeData(e->mimeData());
#if DEBUG_KP_DUAL_COLOR_BUTTON
    qCDebug(kpLogWidgets) << "kpDualColorButton::dropEvent() col=" << (int *)col.rgba() << " (with alpha=" << (int *)col.rgba() << ")";
#endif

    if (col.isValid()) {
        if (foregroundRect().contains(e->position().toPoint())) {
            setForegroundColor(kpColor(col.rgba()));
        } else if (backgroundRect().contains(e->position().toPoint())) {
            setBackgroundColor(kpColor(col.rgba()));
        }
    }
}

//---------------------------------------------------------------------

// protected virtual [base QWidget]
void kpDualColorButton::mousePressEvent(QMouseEvent *e)
{
#if DEBUG_KP_DUAL_COLOR_BUTTON
    qCDebug(kpLogWidgets) << "kpDualColorButton::mousePressEvent() pos=" << e->position().toPoint();
#endif

    m_dragStartPoint = KP_INVALID_POINT;

    if (e->button() == Qt::LeftButton) {
        m_dragStartPoint = e->position().toPoint();
    }
}

//---------------------------------------------------------------------

void kpDualColorButton::mouseMoveEvent(QMouseEvent *e)
{
#if DEBUG_KP_DUAL_COLOR_BUTTON
    qCDebug(kpLogWidgets) << "kpDualColorButton::mouseMoveEvent() pos=" << e->position().toPoint() << " buttons=" << e->buttons() << " dragStartPoint=" << m_dragStartPoint
                          << endl;
#endif

    if (m_dragStartPoint == KP_INVALID_POINT) {
        return;
    }

    if (!(e->buttons() & Qt::LeftButton)) {
        m_dragStartPoint = KP_INVALID_POINT;
        return;
    }

    const int delay = QApplication::startDragDistance();
    if (e->position().toPoint().x() < m_dragStartPoint.x() - delay ||
        e->position().toPoint().x() > m_dragStartPoint.x() + delay ||
        e->position().toPoint().y() < m_dragStartPoint.y() - delay ||
        e->position().toPoint().y() > m_dragStartPoint.y() + delay) {
#if DEBUG_KP_DUAL_COLOR_BUTTON
        qCDebug(kpLogWidgets) << "\tstarting drag as long as it's in a rectangle";
#endif

        kpColor color;

        if (foregroundRect().contains(m_dragStartPoint)) {
            color = foregroundColor();
        } else if (backgroundRect().contains(m_dragStartPoint)) {
            color = backgroundColor();
        }

#if DEBUG_KP_DUAL_COLOR_BUTTON
        qCDebug(kpLogWidgets) << "\tcolor.isValid=" << color.isValid() << " rgb=" << (color.isValid() ? (int *)color.toQRgb() : 0) << endl;
#endif

        if (color.isValid()) {
            if (!color.isTransparent()) {
                KColorMimeData::createDrag(color.toQColor(), this)->exec();
            }
        }

        m_dragStartPoint = KP_INVALID_POINT;
    }
}

//---------------------------------------------------------------------

// protected virtual [base QWidget]
void kpDualColorButton::mouseReleaseEvent(QMouseEvent *e)
{
    m_dragStartPoint = KP_INVALID_POINT;

    if (swapPixmapRect().contains(e->position().toPoint()) && m_color[0] != m_color[1]) {
#if DEBUG_KP_DUAL_COLOR_BUTTON && 1
        qCDebug(kpLogWidgets) << "kpDualColorButton::mouseReleaseEvent() swap colors:";
#endif
        m_oldColor[0] = m_color[0];
        m_oldColor[1] = m_color[1];

        kpColor temp = m_color[0];
        m_color[0] = m_color[1];
        m_color[1] = temp;

        update();

        Q_EMIT colorsSwapped(m_color[0], m_color[1]);
        Q_EMIT foregroundColorChanged(m_color[0]);
        Q_EMIT backgroundColorChanged(m_color[1]);
    }
}

//---------------------------------------------------------------------

// protected virtual [base QWidget]
void kpDualColorButton::mouseDoubleClickEvent(QMouseEvent *e)
{
    int whichColor = -1;

    if (foregroundRect().contains(e->position().toPoint())) {
        whichColor = 0;
    } else if (backgroundRect().contains(e->position().toPoint())) {
        whichColor = 1;
    }

    if (whichColor == 0 || whichColor == 1) {
        QColorDialog dialog(this);
        dialog.setCurrentColor(color(whichColor).toQColor());
        dialog.setOptions(QColorDialog::ShowAlphaChannel);
        if (dialog.exec() == QDialog::Accepted) {
            setColor(whichColor, kpColor(dialog.currentColor().rgba()));
        }
    }
}

//---------------------------------------------------------------------

// protected virtual [base QWidget]
void kpDualColorButton::paintEvent(QPaintEvent *e)
{
#if DEBUG_KP_DUAL_COLOR_BUTTON && 1
    qCDebug(kpLogWidgets) << "kpDualColorButton::draw() rect=" << rect() << " contentsRect=" << contentsRect() << endl;
#endif

    // Draw frame first.
    QFrame::paintEvent(e);

    QPainter painter(this);

    // Fill with background.
    if (isEnabled()) {
        kpView::drawTransparentBackground(&painter, contentsRect().topLeft() /*checkerboard top-left*/, contentsRect(), true /*preview*/);
    } else {
        // Use default widget background.
    }

    painter.translate(contentsRect().x(), contentsRect().y());

    // Draw "Swap Colors" button (top-right).
    QPixmap swapPixmap = QStringLiteral(":/icons/colorbutton_swap_16x16");
    if (!isEnabled()) {
        // Don't let the fill() touch the mask.
        QBitmap swapBitmapMask = swapPixmap.mask();
        swapPixmap.setMask(QBitmap());

        // Grey out the opaque parts of "swapPixmap".
        swapPixmap.fill(palette().color(QPalette::Dark));

        swapPixmap.setMask(swapBitmapMask);
    }
    painter.drawPixmap(swapPixmapRect().topLeft(), swapPixmap);

    // Draw background color patch.
    QRect bgRect = backgroundRect();
    QRect bgRectInside = QRect(bgRect.x() + 2, bgRect.y() + 2, bgRect.width() - 4, bgRect.height() - 4);
    if (isEnabled()) {
#if DEBUG_KP_DUAL_COLOR_BUTTON && 1
        qCDebug(kpLogWidgets) << "\tbackgroundColor=" << (int *)m_color[1].toQRgb() << endl;
#endif
        if (m_color[1].isTransparent()) { // only if fully transparent
            painter.drawPixmap(bgRectInside, QStringLiteral(":/icons/color_transparent_26x26"));
        } else {
            painter.fillRect(bgRectInside, m_color[1].toQColor());
        }
    } else {
        painter.fillRect(bgRectInside, palette().color(QPalette::Button));
    }
    qDrawShadePanel(&painter, bgRect, palette(), false /*not sunken*/, 2 /*lineWidth*/, nullptr /*never fill*/);

    // Draw foreground color patch.
    // Must be drawn after background patch since we're on top.
    QRect fgRect = foregroundRect();
    QRect fgRectInside = QRect(fgRect.x() + 2, fgRect.y() + 2, fgRect.width() - 4, fgRect.height() - 4);
    if (isEnabled()) {
#if DEBUG_KP_DUAL_COLOR_BUTTON && 1
        qCDebug(kpLogWidgets) << "\tforegroundColor=" << (int *)m_color[0].toQRgb() << endl;
#endif
        if (m_color[0].isTransparent()) { // only if fully transparent
            painter.drawPixmap(fgRectInside, QStringLiteral(":/icons/color_transparent_26x26"));
        } else {
            painter.fillRect(fgRectInside, m_color[0].toQColor());
        }
    } else {
        painter.fillRect(fgRectInside, palette().color(QPalette::Button));
    }

    qDrawShadePanel(&painter, fgRect, palette(), false /*not sunken*/, 2 /*lineWidth*/, nullptr /*never fill*/);
}

#include "moc_kpDualColorButton.cpp"
