File indexing completed on 2024-12-22 04:13:09

0001 /*
0002  *  kis_cmb_composite.cc - part of KImageShop/Krayon/Krita
0003  *
0004  *  SPDX-FileCopyrightText: 2004 Boudewijn Rempt (boud@valdyas.org)
0005  *  SPDX-FileCopyrightText: 2011 Silvio Heinrich <plassy@web.de>
0006  *
0007  *  SPDX-License-Identifier: GPL-2.0-or-later
0008  */
0009 
0010 #include "kis_cmb_composite.h"
0011 
0012 #include <KoCompositeOp.h>
0013 #include <KoCompositeOpRegistry.h>
0014 
0015 #include "kis_composite_ops_model.h"
0016 #include "kis_categorized_item_delegate.h"
0017 #include <kis_action.h>
0018 #include <QWheelEvent>
0019 #include "kis_action_manager.h"
0020 
0021 //////////////////////////////////////////////////////////////////////////////////////////
0022 // ---- KisCompositeOpListWidget ------------------------------------------------------ //
0023 
0024 KisCompositeOpListWidget::KisCompositeOpListWidget(QWidget* parent):
0025     KisCategorizedListView(parent),
0026     m_model(new KisSortedCompositeOpListModel(false, this))
0027 {
0028     setModel(m_model);
0029     setItemDelegate(new KisCategorizedItemDelegate(this));
0030 }
0031 
0032 KisCompositeOpListWidget::~KisCompositeOpListWidget()
0033 {
0034 }
0035 
0036 KoID KisCompositeOpListWidget::selectedCompositeOp() const {
0037     KoID op;
0038 
0039     if (m_model->entryAt(op, currentIndex())) {
0040         return op;
0041     }
0042 
0043     return KoCompositeOpRegistry::instance().getDefaultCompositeOp();
0044 }
0045 
0046 void KisCompositeOpListWidget::setCompositeOp(const KoID &id)
0047 {
0048     const QModelIndex index = m_model->indexOf(id);
0049     if (index.isValid()) {
0050         setCurrentIndex(index);
0051     } else {
0052         qWarning() << "KisCompositeOpListWidget::setCompositeOp: ailed to find index for blendmode" << ppVar(id);
0053     }
0054 }
0055 
0056 //////////////////////////////////////////////////////////////////////////////////////////
0057 // ---- KisCompositeOpComboBox -------------------------------------------------------- //
0058 
0059 KisCompositeOpComboBox::KisCompositeOpComboBox(QWidget* parent)
0060     : KisCompositeOpComboBox(false, parent)
0061 {
0062 }
0063 
0064 KisCompositeOpComboBox::KisCompositeOpComboBox(bool limitToLayerStyles, QWidget* parent)
0065     : KisSqueezedComboBox(parent),
0066       m_model(new KisSortedCompositeOpListModel(limitToLayerStyles, this)),
0067       m_allowToHidePopup(true)
0068 {
0069     m_view = new KisCategorizedListView();
0070     m_view->setCompositeBoxControl(true);
0071 
0072     setMaxVisibleItems(100);
0073     setSizeAdjustPolicy(AdjustToContents);
0074     m_view->setResizeMode(QListView::Adjust);
0075 
0076     setToolTip(i18n("Blending Mode"));
0077 
0078     setModel(m_model);
0079     setView(m_view);
0080     setItemDelegate(new KisCategorizedItemDelegate(this));
0081 
0082     connect(m_view, SIGNAL(sigCategoryToggled(QModelIndex,bool)), SLOT(slotCategoryToggled(QModelIndex,bool)));
0083     connect(m_view, SIGNAL(sigEntryChecked(QModelIndex)), SLOT(slotEntryChecked(QModelIndex)));
0084 
0085     selectCompositeOp(KoCompositeOpRegistry::instance().getDefaultCompositeOp());
0086 }
0087 
0088 KisCompositeOpComboBox::~KisCompositeOpComboBox()
0089 {
0090     delete m_view;
0091 }
0092 
0093 void KisCompositeOpComboBox::connectBlendmodeActions(KisActionManager *manager)
0094 {
0095     KisAction *action = 0;
0096 
0097     action = manager->createAction("Next Blending Mode");
0098     connect(action, SIGNAL(triggered()), SLOT(slotNextBlendingMode()));
0099 
0100     action = manager->createAction("Previous Blending Mode");
0101     connect(action, SIGNAL(triggered()), SLOT(slotPreviousBlendingMode()));
0102 
0103     action = manager->createAction("Select Normal Blending Mode");
0104     connect(action, SIGNAL(triggered()), SLOT(slotNormal()));
0105 
0106     action = manager->createAction("Select Dissolve Blending Mode");
0107     connect(action, SIGNAL(triggered()), SLOT(slotDissolve()));
0108 
0109     action = manager->createAction("Select Behind Blending Mode");
0110     connect(action, SIGNAL(triggered()), SLOT(slotBehind()));
0111 
0112     action = manager->createAction("Select Clear Blending Mode");
0113     connect(action, SIGNAL(triggered()), SLOT(slotClear()));
0114 
0115     action = manager->createAction("Select Darken Blending Mode");
0116     connect(action, SIGNAL(triggered()), SLOT(slotDarken()));
0117 
0118     action = manager->createAction("Select Multiply Blending Mode");
0119     connect(action, SIGNAL(triggered()), SLOT(slotMultiply()));
0120 
0121     action = manager->createAction("Select Color Burn Blending Mode");
0122     connect(action, SIGNAL(triggered()), SLOT(slotColorBurn()));
0123 
0124     action = manager->createAction("Select Linear Burn Blending Mode");
0125     connect(action, SIGNAL(triggered()), SLOT(slotLinearBurn()));
0126 
0127     action = manager->createAction("Select Lighten Blending Mode");
0128     connect(action, SIGNAL(triggered()), SLOT(slotLighten()));
0129 
0130     action = manager->createAction("Select Screen Blending Mode");
0131     connect(action, SIGNAL(triggered()), SLOT(slotScreen()));
0132 
0133     action = manager->createAction("Select Color Dodge Blending Mode");
0134     connect(action, SIGNAL(triggered()), SLOT(slotColorDodge()));
0135 
0136     action = manager->createAction("Select Linear Dodge Blending Mode");
0137     connect(action, SIGNAL(triggered()), SLOT(slotLinearDodge()));
0138 
0139     action = manager->createAction("Select Overlay Blending Mode");
0140     connect(action, SIGNAL(triggered()), SLOT(slotOverlay()));
0141 
0142     action = manager->createAction("Select Hard Overlay Blending Mode");
0143     connect(action, SIGNAL(triggered()), SLOT(slotHardOverlay()));
0144 
0145     action = manager->createAction("Select Soft Light Blending Mode");
0146     connect(action, SIGNAL(triggered()), SLOT(slotSoftLight()));
0147 
0148     action = manager->createAction("Select Hard Light Blending Mode");
0149     connect(action, SIGNAL(triggered()), SLOT(slotHardLight()));
0150 
0151     action = manager->createAction("Select Vivid Light Blending Mode");
0152     connect(action, SIGNAL(triggered()), SLOT(slotVividLight()));
0153 
0154     action = manager->createAction("Select Linear Light Blending Mode");
0155     connect(action, SIGNAL(triggered()), SLOT(slotLinearLight()));
0156 
0157     action = manager->createAction("Select Pin Light Blending Mode");
0158     connect(action, SIGNAL(triggered()), SLOT(slotPinLight()));
0159 
0160     action = manager->createAction("Select Hard Mix Blending Mode");
0161     connect(action, SIGNAL(triggered()), SLOT(slotHardMix()));
0162 
0163     action = manager->createAction("Select Difference Blending Mode");
0164     connect(action, SIGNAL(triggered()), SLOT(slotDifference()));
0165 
0166     action = manager->createAction("Select Exclusion Blending Mode");
0167     connect(action, SIGNAL(triggered()), SLOT(slotExclusion()));
0168 
0169     action = manager->createAction("Select Hue Blending Mode");
0170     connect(action, SIGNAL(triggered()), SLOT(slotHue()));
0171 
0172     action = manager->createAction("Select Saturation Blending Mode");
0173     connect(action, SIGNAL(triggered()), SLOT(slotSaturation()));
0174 
0175     action = manager->createAction("Select Color Blending Mode");
0176     connect(action, SIGNAL(triggered()), SLOT(slotColor()));
0177 
0178     action = manager->createAction("Select Luminosity Blending Mode");
0179     connect(action, SIGNAL(triggered()), SLOT(slotLuminosity()));
0180 }
0181 
0182 void KisCompositeOpComboBox::validate(const KoColorSpace *cs) {
0183     m_model->validate(cs);
0184 }
0185 
0186 void KisCompositeOpComboBox::selectCompositeOp(const KoID &op) {
0187     KoID currentOp;
0188     if (m_model->entryAt(currentOp, m_model->index(currentIndex(), 0)) &&
0189             currentOp == op) {
0190 
0191         return;
0192     }
0193 
0194     QModelIndex index = m_model->indexOf(op);
0195 
0196     setCurrentIndex(index.row());
0197     emit activated(index.row());
0198     emit activated(op.name());
0199 }
0200 
0201 KoID KisCompositeOpComboBox::selectedCompositeOp() const {
0202     KoID op;
0203 
0204     if (m_model->entryAt(op, m_model->index(currentIndex(), 0))) {
0205         return op;
0206     }
0207     return KoCompositeOpRegistry::instance().getDefaultCompositeOp();
0208 }
0209 
0210 void KisCompositeOpComboBox::slotCategoryToggled(const QModelIndex& index, bool toggled)
0211 {
0212     Q_UNUSED(index);
0213     Q_UNUSED(toggled);
0214 
0215     //NOTE: this will (should) fit the size of the
0216     //      popup widget to the view
0217     //      don't know if this is expected behaviour
0218     //      on all supported platforms.
0219     //      There is nothing written about this in the docs.
0220     showPopup();
0221 }
0222 
0223 void KisCompositeOpComboBox::slotEntryChecked(const QModelIndex& index)
0224 {
0225     Q_UNUSED(index);
0226     m_allowToHidePopup = false;
0227 }
0228 
0229 void KisCompositeOpComboBox::hidePopup()
0230 {
0231     if (m_allowToHidePopup) {
0232         QComboBox::hidePopup();
0233     }
0234     else  {
0235         QComboBox::showPopup();
0236     }
0237 
0238     m_allowToHidePopup = true;
0239 }
0240 
0241 void KisCompositeOpComboBox::slotNextBlendingMode()
0242 {
0243     selectNeighbouringBlendMode(true);
0244 }
0245 
0246 void KisCompositeOpComboBox::slotPreviousBlendingMode()
0247 {
0248     selectNeighbouringBlendMode(false);
0249 }
0250 
0251 void KisCompositeOpComboBox::slotNormal()
0252 {
0253     selectCompositeOp(KoCompositeOpRegistry::instance().getKoID(COMPOSITE_OVER));
0254 }
0255 
0256 void KisCompositeOpComboBox::slotDissolve()
0257 {
0258     selectCompositeOp(KoCompositeOpRegistry::instance().getKoID(COMPOSITE_DISSOLVE));
0259 }
0260 
0261 void KisCompositeOpComboBox::slotBehind()
0262 {
0263     selectCompositeOp(KoCompositeOpRegistry::instance().getKoID(COMPOSITE_BEHIND));
0264 }
0265 
0266 void KisCompositeOpComboBox::slotClear()
0267 {
0268     selectCompositeOp(KoCompositeOpRegistry::instance().getKoID(COMPOSITE_CLEAR));
0269 }
0270 
0271 void KisCompositeOpComboBox::slotDarken()
0272 {
0273     selectCompositeOp(KoCompositeOpRegistry::instance().getKoID(COMPOSITE_DARKEN));
0274 }
0275 
0276 void KisCompositeOpComboBox::slotMultiply()
0277 {
0278     selectCompositeOp(KoCompositeOpRegistry::instance().getKoID(COMPOSITE_MULT));
0279 }
0280 
0281 void KisCompositeOpComboBox::slotColorBurn()
0282 {
0283     selectCompositeOp(KoCompositeOpRegistry::instance().getKoID(COMPOSITE_BURN));
0284 }
0285 
0286 void KisCompositeOpComboBox::slotLinearBurn()
0287 {
0288     selectCompositeOp(KoCompositeOpRegistry::instance().getKoID(COMPOSITE_LINEAR_BURN));
0289 }
0290 
0291 void KisCompositeOpComboBox::slotLighten()
0292 {
0293     selectCompositeOp(KoCompositeOpRegistry::instance().getKoID(COMPOSITE_LIGHTEN));
0294 }
0295 
0296 void KisCompositeOpComboBox::slotScreen()
0297 {
0298     selectCompositeOp(KoCompositeOpRegistry::instance().getKoID(COMPOSITE_SCREEN));
0299 }
0300 
0301 void KisCompositeOpComboBox::slotColorDodge()
0302 {
0303     selectCompositeOp(KoCompositeOpRegistry::instance().getKoID(COMPOSITE_DODGE));
0304 }
0305 
0306 void KisCompositeOpComboBox::slotLinearDodge()
0307 {
0308     selectCompositeOp(KoCompositeOpRegistry::instance().getKoID(COMPOSITE_LINEAR_DODGE));
0309 }
0310 
0311 void KisCompositeOpComboBox::slotOverlay()
0312 {
0313     selectCompositeOp(KoCompositeOpRegistry::instance().getKoID(COMPOSITE_OVERLAY));
0314 }
0315 
0316 void KisCompositeOpComboBox::slotHardOverlay()
0317 {
0318     selectCompositeOp(KoCompositeOpRegistry::instance().getKoID(COMPOSITE_HARD_OVERLAY));
0319 }
0320 
0321 void KisCompositeOpComboBox::slotSoftLight()
0322 {
0323     selectCompositeOp(KoCompositeOpRegistry::instance().getKoID(COMPOSITE_SOFT_LIGHT_PHOTOSHOP));
0324 }
0325 
0326 void KisCompositeOpComboBox::slotHardLight()
0327 {
0328     selectCompositeOp(KoCompositeOpRegistry::instance().getKoID(COMPOSITE_HARD_LIGHT));
0329 }
0330 
0331 void KisCompositeOpComboBox::slotVividLight()
0332 {
0333     selectCompositeOp(KoCompositeOpRegistry::instance().getKoID(COMPOSITE_VIVID_LIGHT));
0334 }
0335 
0336 void KisCompositeOpComboBox::slotLinearLight()
0337 {
0338     selectCompositeOp(KoCompositeOpRegistry::instance().getKoID(COMPOSITE_LINEAR_LIGHT));
0339 }
0340 
0341 void KisCompositeOpComboBox::slotPinLight()
0342 {
0343     selectCompositeOp(KoCompositeOpRegistry::instance().getKoID(COMPOSITE_PIN_LIGHT));
0344 }
0345 
0346 void KisCompositeOpComboBox::slotHardMix()
0347 {
0348     selectCompositeOp(KoCompositeOpRegistry::instance().getKoID(COMPOSITE_HARD_MIX_PHOTOSHOP));
0349 }
0350 
0351 void KisCompositeOpComboBox::slotDifference()
0352 {
0353     selectCompositeOp(KoCompositeOpRegistry::instance().getKoID(COMPOSITE_DIFF));
0354 }
0355 
0356 void KisCompositeOpComboBox::slotExclusion()
0357 {
0358     selectCompositeOp(KoCompositeOpRegistry::instance().getKoID(COMPOSITE_EXCLUSION));
0359 }
0360 
0361 void KisCompositeOpComboBox::slotHue()
0362 {
0363     selectCompositeOp(KoCompositeOpRegistry::instance().getKoID(COMPOSITE_HUE));
0364 }
0365 
0366 void KisCompositeOpComboBox::slotSaturation()
0367 {
0368     selectCompositeOp(KoCompositeOpRegistry::instance().getKoID(COMPOSITE_SATURATION));
0369 }
0370 
0371 void KisCompositeOpComboBox::slotColor()
0372 {
0373     selectCompositeOp(KoCompositeOpRegistry::instance().getKoID(COMPOSITE_COLOR));
0374 }
0375 
0376 void KisCompositeOpComboBox::slotLuminosity()
0377 {
0378     selectCompositeOp(KoCompositeOpRegistry::instance().getKoID(COMPOSITE_LUMINIZE));
0379 }
0380 
0381 void KisCompositeOpComboBox::selectNeighbouringBlendMode(bool down)
0382 {
0383     const int rowCount = count();
0384     int newIndex = currentIndex();
0385 
0386     QAbstractItemModel *model = this->model();
0387     KoID op;
0388 
0389     if (!down) {
0390         newIndex--;
0391         while ((newIndex >= 0) &&
0392                (!(model->flags(model->index(newIndex, modelColumn(), rootModelIndex())) & Qt::ItemIsEnabled) ||
0393                 !m_model->entryAt(op, m_model->index(newIndex, modelColumn()))))
0394 
0395             newIndex--;
0396     } else {
0397         newIndex++;
0398         while (newIndex < rowCount &&
0399                (!(model->index(newIndex, modelColumn(), rootModelIndex()).flags() & Qt::ItemIsEnabled) ||
0400                 !m_model->entryAt(op, m_model->index(newIndex, modelColumn()))))
0401 
0402             newIndex++;
0403     }
0404 
0405     if (newIndex >= 0 && newIndex < rowCount && newIndex != currentIndex()) {
0406         setCurrentIndex(newIndex);
0407 
0408         emit activated(newIndex);
0409         if (m_model->entryAt(op, m_model->index(newIndex, 0))) {
0410             emit activated(op.name());
0411         }
0412     }
0413 }
0414 
0415 void KisCompositeOpComboBox::wheelEvent(QWheelEvent *e)
0416 {
0417     /**
0418      * This code is a copy of QComboBox::wheelEvent. It does the same thing,
0419      * except that it skips "Category" items, by checking m_model->entryAt()
0420      * on each step.
0421      */
0422 
0423     QStyleOptionComboBox opt;
0424     initStyleOption(&opt);
0425 
0426 #if (QT_VERSION >= QT_VERSION_CHECK(5, 10, 0))
0427     if (style()->styleHint(QStyle::SH_ComboBox_AllowWheelScrolling, &opt, this)) {
0428 #else
0429     if (1) {
0430 #endif
0431 
0432         if (e->delta() != 0) {
0433             selectNeighbouringBlendMode(e->delta() < 0);
0434         }
0435 
0436         e->accept();
0437     } else {
0438         KisSqueezedComboBox::wheelEvent(e);
0439     }
0440 }
0441 
0442 void KisCompositeOpComboBox::keyPressEvent(QKeyEvent *e)
0443 {
0444     /**
0445      * This code is a copy of QComboBox::keyPressEvent. It does the same thing,
0446      * except that it skips "Category" items, by checking m_model->entryAt()
0447      * on each step.
0448      */
0449 
0450     enum Move { NoMove=0 , MoveUp , MoveDown , MoveFirst , MoveLast};
0451 
0452     Move move = NoMove;
0453     int newIndex = currentIndex();
0454     switch (e->key()) {
0455     case Qt::Key_Up:
0456         if (e->modifiers() & Qt::ControlModifier)
0457             break; // pass to line edit for auto completion
0458         Q_FALLTHROUGH();
0459     case Qt::Key_PageUp:
0460         move = MoveUp;
0461         break;
0462     case Qt::Key_Down:
0463         if (e->modifiers() & Qt::AltModifier) {
0464             showPopup();
0465             return;
0466         } else if (e->modifiers() & Qt::ControlModifier)
0467             break; // pass to line edit for auto completion
0468         Q_FALLTHROUGH();
0469     case Qt::Key_PageDown:
0470         move = MoveDown;
0471         break;
0472     case Qt::Key_Home:
0473         move = MoveFirst;
0474         break;
0475     case Qt::Key_End:
0476         move = MoveLast;
0477         break;
0478     case Qt::Key_F4:
0479         if (!e->modifiers()) {
0480             showPopup();
0481             return;
0482         }
0483         break;
0484     case Qt::Key_Space:
0485         showPopup();
0486         return;
0487     default:
0488         break;
0489     }
0490 
0491     const int rowCount = count();
0492 
0493     if (move != NoMove) {
0494         KoID op;
0495 
0496         e->accept();
0497         switch (move) {
0498         case MoveFirst:
0499             newIndex = -1;
0500             Q_FALLTHROUGH();
0501         case MoveDown:
0502             newIndex++;
0503             while (newIndex < rowCount &&
0504                    (!(model()->index(newIndex, modelColumn(), rootModelIndex()).flags() & Qt::ItemIsEnabled) ||
0505                     !m_model->entryAt(op, m_model->index(newIndex, modelColumn()))))
0506                 newIndex++;
0507             break;
0508         case MoveLast:
0509             newIndex = rowCount;
0510             Q_FALLTHROUGH();
0511         case MoveUp:
0512             newIndex--;
0513             while ((newIndex >= 0) &&
0514                    (!(model()->flags(model()->index(newIndex, modelColumn(), rootModelIndex())) & Qt::ItemIsEnabled) ||
0515                     !m_model->entryAt(op, m_model->index(newIndex, modelColumn()))))
0516                 newIndex--;
0517             break;
0518         default:
0519             e->ignore();
0520             break;
0521         }
0522 
0523         if (newIndex >= 0 && newIndex < rowCount && newIndex != currentIndex()) {
0524             setCurrentIndex(newIndex);
0525             emit activated(newIndex);
0526 
0527             if (m_model->entryAt(op, m_model->index(newIndex, 0))) {
0528                 emit activated(op.name());
0529             }
0530         }
0531     } else {
0532         KisSqueezedComboBox::keyPressEvent(e);
0533     }
0534 }
0535 
0536 KisLayerStyleCompositeOpComboBox::KisLayerStyleCompositeOpComboBox(QWidget* parent)
0537     : KisCompositeOpComboBox(true, parent)
0538 {
0539 }