File indexing completed on 2024-05-12 16:02:00

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