File indexing completed on 2024-04-28 03:59:01

0001 /*
0002     This file is part of the KDE libraries
0003     SPDX-FileCopyrightText: 1997 Martin Jones <mjones@kde.org>
0004     SPDX-FileCopyrightText: 1999 Cristian Tibirna <ctibirna@kde.org>
0005 
0006     SPDX-License-Identifier: LGPL-2.0-or-later
0007 */
0008 
0009 #include "kcolorbutton.h"
0010 
0011 #include <QApplication>
0012 #include <QClipboard>
0013 #include <QColorDialog>
0014 #include <QDrag>
0015 #include <QMimeData>
0016 #include <QMouseEvent>
0017 #include <QPainter>
0018 #include <QPointer>
0019 #include <QStyle>
0020 #include <QStyleOptionButton>
0021 #include <qdrawutil.h>
0022 
0023 class KColorButtonPrivate
0024 {
0025 public:
0026     KColorButtonPrivate(KColorButton *qq);
0027 
0028     void chooseColor();
0029     void colorChosen();
0030 
0031     KColorButton *q;
0032     QColor m_defaultColor;
0033     bool m_bdefaultColor : 1;
0034     bool m_alphaChannel : 1;
0035 
0036     QColor col;
0037     QPoint mPos;
0038 
0039     QPointer<QColorDialog> dialogPtr;
0040 
0041     void initStyleOption(QStyleOptionButton *opt) const;
0042 };
0043 
0044 /////////////////////////////////////////////////////////////////////
0045 // Functions duplicated from KColorMimeData
0046 // Should be kept in sync
0047 void populateMimeData(QMimeData *mimeData, const QColor &color)
0048 {
0049     mimeData->setColorData(color);
0050     mimeData->setText(color.name());
0051 }
0052 
0053 bool canDecode(const QMimeData *mimeData)
0054 {
0055     if (mimeData->hasColor()) {
0056         return true;
0057     }
0058     if (mimeData->hasText()) {
0059         const QString colorName = mimeData->text();
0060         if ((colorName.length() >= 4) && (colorName[0] == QLatin1Char('#'))) {
0061             return true;
0062         }
0063     }
0064     return false;
0065 }
0066 
0067 QColor fromMimeData(const QMimeData *mimeData)
0068 {
0069     if (mimeData->hasColor()) {
0070         return mimeData->colorData().value<QColor>();
0071     }
0072     if (canDecode(mimeData)) {
0073         return QColor(mimeData->text());
0074     }
0075     return QColor();
0076 }
0077 
0078 QDrag *createDrag(const QColor &color, QObject *dragsource)
0079 {
0080     QDrag *drag = new QDrag(dragsource);
0081     QMimeData *mime = new QMimeData;
0082     populateMimeData(mime, color);
0083     drag->setMimeData(mime);
0084     QPixmap colorpix(25, 20);
0085     colorpix.fill(color);
0086     QPainter p(&colorpix);
0087     p.setPen(Qt::black);
0088     p.drawRect(0, 0, 24, 19);
0089     p.end();
0090     drag->setPixmap(colorpix);
0091     drag->setHotSpot(QPoint(-5, -7));
0092     return drag;
0093 }
0094 /////////////////////////////////////////////////////////////////////
0095 
0096 KColorButtonPrivate::KColorButtonPrivate(KColorButton *qq)
0097     : q(qq)
0098 {
0099     m_bdefaultColor = false;
0100     m_alphaChannel = false;
0101     q->setAcceptDrops(true);
0102 
0103     QObject::connect(q, &KColorButton::clicked, q, [this]() {
0104         chooseColor();
0105     });
0106 }
0107 
0108 KColorButton::KColorButton(QWidget *parent)
0109     : QPushButton(parent)
0110     , d(new KColorButtonPrivate(this))
0111 {
0112 }
0113 
0114 KColorButton::KColorButton(const QColor &c, QWidget *parent)
0115     : QPushButton(parent)
0116     , d(new KColorButtonPrivate(this))
0117 {
0118     d->col = c;
0119 }
0120 
0121 KColorButton::KColorButton(const QColor &c, const QColor &defaultColor, QWidget *parent)
0122     : QPushButton(parent)
0123     , d(new KColorButtonPrivate(this))
0124 {
0125     d->col = c;
0126     setDefaultColor(defaultColor);
0127 }
0128 
0129 KColorButton::~KColorButton() = default;
0130 
0131 QColor KColorButton::color() const
0132 {
0133     return d->col;
0134 }
0135 
0136 void KColorButton::setColor(const QColor &c)
0137 {
0138     if (d->col != c) {
0139         d->col = c;
0140         update();
0141         Q_EMIT changed(d->col);
0142     }
0143 }
0144 
0145 void KColorButton::setAlphaChannelEnabled(bool alpha)
0146 {
0147     d->m_alphaChannel = alpha;
0148 }
0149 
0150 bool KColorButton::isAlphaChannelEnabled() const
0151 {
0152     return d->m_alphaChannel;
0153 }
0154 
0155 QColor KColorButton::defaultColor() const
0156 {
0157     return d->m_defaultColor;
0158 }
0159 
0160 void KColorButton::setDefaultColor(const QColor &c)
0161 {
0162     d->m_bdefaultColor = c.isValid();
0163     d->m_defaultColor = c;
0164 }
0165 
0166 void KColorButtonPrivate::initStyleOption(QStyleOptionButton *opt) const
0167 {
0168     opt->initFrom(q);
0169     opt->state |= q->isDown() ? QStyle::State_Sunken : QStyle::State_Raised;
0170     opt->features = QStyleOptionButton::None;
0171     if (q->isDefault()) {
0172         opt->features |= QStyleOptionButton::DefaultButton;
0173     }
0174     opt->text.clear();
0175     opt->icon = QIcon();
0176 }
0177 
0178 void KColorButton::paintEvent(QPaintEvent *)
0179 {
0180     QPainter painter(this);
0181     QStyle *style = QWidget::style();
0182 
0183     // First, we need to draw the bevel.
0184     QStyleOptionButton butOpt;
0185     d->initStyleOption(&butOpt);
0186     style->drawControl(QStyle::CE_PushButtonBevel, &butOpt, &painter, this);
0187 
0188     // OK, now we can muck around with drawing out pretty little color box
0189     // First, sort out where it goes
0190     QRect labelRect = style->subElementRect(QStyle::SE_PushButtonContents, &butOpt, this);
0191     int shift = style->pixelMetric(QStyle::PM_ButtonMargin, &butOpt, this) / 2;
0192     labelRect.adjust(shift, shift, -shift, -shift);
0193     int x;
0194     int y;
0195     int w;
0196     int h;
0197     labelRect.getRect(&x, &y, &w, &h);
0198 
0199     if (isChecked() || isDown()) {
0200         x += style->pixelMetric(QStyle::PM_ButtonShiftHorizontal, &butOpt, this);
0201         y += style->pixelMetric(QStyle::PM_ButtonShiftVertical, &butOpt, this);
0202     }
0203 
0204     QColor fillCol = isEnabled() ? d->col : palette().color(backgroundRole());
0205     qDrawShadePanel(&painter, x, y, w, h, palette(), true, 1, nullptr);
0206     if (fillCol.isValid()) {
0207         const QRect rect(x + 1, y + 1, w - 2, h - 2);
0208         if (fillCol.alpha() < 255) {
0209             QPixmap chessboardPattern(16, 16);
0210             QPainter patternPainter(&chessboardPattern);
0211             patternPainter.fillRect(0, 0, 8, 8, Qt::black);
0212             patternPainter.fillRect(8, 8, 8, 8, Qt::black);
0213             patternPainter.fillRect(0, 8, 8, 8, Qt::white);
0214             patternPainter.fillRect(8, 0, 8, 8, Qt::white);
0215             patternPainter.end();
0216             painter.fillRect(rect, QBrush(chessboardPattern));
0217         }
0218         painter.fillRect(rect, fillCol);
0219     }
0220 
0221     if (hasFocus()) {
0222         QRect focusRect = style->subElementRect(QStyle::SE_PushButtonFocusRect, &butOpt, this);
0223         QStyleOptionFocusRect focusOpt;
0224         focusOpt.initFrom(this);
0225         focusOpt.rect = focusRect;
0226         focusOpt.backgroundColor = palette().window().color();
0227         style->drawPrimitive(QStyle::PE_FrameFocusRect, &focusOpt, &painter, this);
0228     }
0229 }
0230 
0231 QSize KColorButton::sizeHint() const
0232 {
0233     QStyleOptionButton opt;
0234     d->initStyleOption(&opt);
0235     return style()->sizeFromContents(QStyle::CT_PushButton, &opt, QSize(40, 15), this);
0236 }
0237 
0238 QSize KColorButton::minimumSizeHint() const
0239 {
0240     QStyleOptionButton opt;
0241     d->initStyleOption(&opt);
0242     return style()->sizeFromContents(QStyle::CT_PushButton, &opt, QSize(3, 3), this);
0243 }
0244 
0245 void KColorButton::dragEnterEvent(QDragEnterEvent *event)
0246 {
0247     event->setAccepted(canDecode(event->mimeData()) && isEnabled());
0248 }
0249 
0250 void KColorButton::dropEvent(QDropEvent *event)
0251 {
0252     QColor c = fromMimeData(event->mimeData());
0253     if (c.isValid()) {
0254         setColor(c);
0255     }
0256 }
0257 
0258 void KColorButton::keyPressEvent(QKeyEvent *e)
0259 {
0260     int key = e->key() | e->modifiers();
0261 
0262     if (QKeySequence::keyBindings(QKeySequence::Copy).contains(key)) {
0263         QMimeData *mime = new QMimeData;
0264         populateMimeData(mime, color());
0265         QApplication::clipboard()->setMimeData(mime, QClipboard::Clipboard);
0266     } else if (QKeySequence::keyBindings(QKeySequence::Paste).contains(key)) {
0267         QColor color = fromMimeData(QApplication::clipboard()->mimeData(QClipboard::Clipboard));
0268         setColor(color);
0269     } else {
0270         QPushButton::keyPressEvent(e);
0271     }
0272 }
0273 
0274 void KColorButton::mousePressEvent(QMouseEvent *e)
0275 {
0276     d->mPos = e->pos();
0277     QPushButton::mousePressEvent(e);
0278 }
0279 
0280 void KColorButton::mouseMoveEvent(QMouseEvent *e)
0281 {
0282     if ((e->buttons() & Qt::LeftButton) &&
0283             (e->pos() - d->mPos).manhattanLength() > QApplication::startDragDistance()) {
0284         createDrag(color(), this)->exec();
0285         setDown(false);
0286     }
0287 }
0288 
0289 void KColorButtonPrivate::chooseColor()
0290 {
0291     QColorDialog *dialog = dialogPtr.data();
0292     if (dialog) {
0293         dialog->show();
0294         dialog->raise();
0295         dialog->activateWindow();
0296         return;
0297     }
0298 
0299     dialog = new QColorDialog(q);
0300     dialog->setCurrentColor(q->color());
0301     dialog->setOption(QColorDialog::ShowAlphaChannel, m_alphaChannel);
0302     dialog->setAttribute(Qt::WA_DeleteOnClose);
0303     QObject::connect(dialog, &QDialog::accepted, q, [this]() {
0304         colorChosen();
0305     });
0306     dialogPtr = dialog;
0307     dialog->show();
0308 }
0309 
0310 void KColorButtonPrivate::colorChosen()
0311 {
0312     QColorDialog *dialog = dialogPtr.data();
0313     if (!dialog) {
0314         return;
0315     }
0316 
0317     if (dialog->selectedColor().isValid()) {
0318         q->setColor(dialog->selectedColor());
0319     } else if (m_bdefaultColor) {
0320         q->setColor(m_defaultColor);
0321     }
0322 }
0323 
0324 #include "moc_kcolorbutton.cpp"