1 /*
2 * kis_cmb_composite.cc - part of KImageShop/Krayon/Krita
3 *
4 * Copyright (c) 2004 Boudewijn Rempt (boud@valdyas.org)
5 * Copyright (c) 2011 Silvio Heinrich <plassy@web.de>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 */
21
22 #include "kis_cmb_composite.h"
23
24 #include <KoCompositeOp.h>
25 #include <KoCompositeOpRegistry.h>
26
27 #include "kis_composite_ops_model.h"
28 #include "kis_categorized_item_delegate.h"
29 #include <kis_action.h>
30 #include <QWheelEvent>
31 #include "kis_action_manager.h"
32
33 //////////////////////////////////////////////////////////////////////////////////////////
34 // ---- KisCompositeOpListWidget ------------------------------------------------------ //
35
KisCompositeOpListWidget(QWidget * parent)36 KisCompositeOpListWidget::KisCompositeOpListWidget(QWidget* parent):
37 KisCategorizedListView(parent),
38 m_model(new KisSortedCompositeOpListModel(false, this))
39 {
40 setModel(m_model);
41 setItemDelegate(new KisCategorizedItemDelegate(this));
42 }
43
~KisCompositeOpListWidget()44 KisCompositeOpListWidget::~KisCompositeOpListWidget()
45 {
46 }
47
selectedCompositeOp() const48 KoID KisCompositeOpListWidget::selectedCompositeOp() const {
49 KoID op;
50
51 if (m_model->entryAt(op, currentIndex())) {
52 return op;
53 }
54
55 return KoCompositeOpRegistry::instance().getDefaultCompositeOp();
56 }
57
58 //////////////////////////////////////////////////////////////////////////////////////////
59 // ---- KisCompositeOpComboBox -------------------------------------------------------- //
60
KisCompositeOpComboBox(QWidget * parent)61 KisCompositeOpComboBox::KisCompositeOpComboBox(QWidget* parent)
62 : KisCompositeOpComboBox(false, parent)
63 {
64 }
65
KisCompositeOpComboBox(bool limitToLayerStyles,QWidget * parent)66 KisCompositeOpComboBox::KisCompositeOpComboBox(bool limitToLayerStyles, QWidget* parent)
67 : KisSqueezedComboBox(parent),
68 m_model(new KisSortedCompositeOpListModel(limitToLayerStyles, this)),
69 m_allowToHidePopup(true)
70 {
71 m_view = new KisCategorizedListView();
72 m_view->setCompositeBoxControl(true);
73
74 setMaxVisibleItems(100);
75 setSizeAdjustPolicy(AdjustToContents);
76 m_view->setResizeMode(QListView::Adjust);
77
78 setToolTip(i18n("Blending Mode"));
79
80 setModel(m_model);
81 setView(m_view);
82 setItemDelegate(new KisCategorizedItemDelegate(this));
83
84 connect(m_view, SIGNAL(sigCategoryToggled(QModelIndex,bool)), SLOT(slotCategoryToggled(QModelIndex,bool)));
85 connect(m_view, SIGNAL(sigEntryChecked(QModelIndex)), SLOT(slotEntryChecked(QModelIndex)));
86
87 selectCompositeOp(KoCompositeOpRegistry::instance().getDefaultCompositeOp());
88 }
89
~KisCompositeOpComboBox()90 KisCompositeOpComboBox::~KisCompositeOpComboBox()
91 {
92 delete m_view;
93 }
94
connectBlendmodeActions(KisActionManager * manager)95 void KisCompositeOpComboBox::connectBlendmodeActions(KisActionManager *manager)
96 {
97 KisAction *action = 0;
98
99 action = manager->createAction("Next Blending Mode");
100 connect(action, SIGNAL(triggered()), SLOT(slotNextBlendingMode()));
101
102 action = manager->createAction("Previous Blending Mode");
103 connect(action, SIGNAL(triggered()), SLOT(slotPreviousBlendingMode()));
104
105 action = manager->createAction("Select Normal Blending Mode");
106 connect(action, SIGNAL(triggered()), SLOT(slotNormal()));
107
108 action = manager->createAction("Select Dissolve Blending Mode");
109 connect(action, SIGNAL(triggered()), SLOT(slotDissolve()));
110
111 action = manager->createAction("Select Behind Blending Mode");
112 connect(action, SIGNAL(triggered()), SLOT(slotBehind()));
113
114 action = manager->createAction("Select Clear Blending Mode");
115 connect(action, SIGNAL(triggered()), SLOT(slotClear()));
116
117 action = manager->createAction("Select Darken Blending Mode");
118 connect(action, SIGNAL(triggered()), SLOT(slotDarken()));
119
120 action = manager->createAction("Select Multiply Blending Mode");
121 connect(action, SIGNAL(triggered()), SLOT(slotMultiply()));
122
123 action = manager->createAction("Select Color Burn Blending Mode");
124 connect(action, SIGNAL(triggered()), SLOT(slotColorBurn()));
125
126 action = manager->createAction("Select Linear Burn Blending Mode");
127 connect(action, SIGNAL(triggered()), SLOT(slotLinearBurn()));
128
129 action = manager->createAction("Select Lighten Blending Mode");
130 connect(action, SIGNAL(triggered()), SLOT(slotLighten()));
131
132 action = manager->createAction("Select Screen Blending Mode");
133 connect(action, SIGNAL(triggered()), SLOT(slotScreen()));
134
135 action = manager->createAction("Select Color Dodge Blending Mode");
136 connect(action, SIGNAL(triggered()), SLOT(slotColorDodge()));
137
138 action = manager->createAction("Select Linear Dodge Blending Mode");
139 connect(action, SIGNAL(triggered()), SLOT(slotLinearDodge()));
140
141 action = manager->createAction("Select Overlay Blending Mode");
142 connect(action, SIGNAL(triggered()), SLOT(slotOverlay()));
143
144 action = manager->createAction("Select Hard Overlay Blending Mode");
145 connect(action, SIGNAL(triggered()), SLOT(slotHardOverlay()));
146
147 action = manager->createAction("Select Soft Light Blending Mode");
148 connect(action, SIGNAL(triggered()), SLOT(slotSoftLight()));
149
150 action = manager->createAction("Select Hard Light Blending Mode");
151 connect(action, SIGNAL(triggered()), SLOT(slotHardLight()));
152
153 action = manager->createAction("Select Vivid Light Blending Mode");
154 connect(action, SIGNAL(triggered()), SLOT(slotVividLight()));
155
156 action = manager->createAction("Select Linear Light Blending Mode");
157 connect(action, SIGNAL(triggered()), SLOT(slotLinearLight()));
158
159 action = manager->createAction("Select Pin Light Blending Mode");
160 connect(action, SIGNAL(triggered()), SLOT(slotPinLight()));
161
162 action = manager->createAction("Select Hard Mix Blending Mode");
163 connect(action, SIGNAL(triggered()), SLOT(slotHardMix()));
164
165 action = manager->createAction("Select Difference Blending Mode");
166 connect(action, SIGNAL(triggered()), SLOT(slotDifference()));
167
168 action = manager->createAction("Select Exclusion Blending Mode");
169 connect(action, SIGNAL(triggered()), SLOT(slotExclusion()));
170
171 action = manager->createAction("Select Hue Blending Mode");
172 connect(action, SIGNAL(triggered()), SLOT(slotHue()));
173
174 action = manager->createAction("Select Saturation Blending Mode");
175 connect(action, SIGNAL(triggered()), SLOT(slotSaturation()));
176
177 action = manager->createAction("Select Color Blending Mode");
178 connect(action, SIGNAL(triggered()), SLOT(slotColor()));
179
180 action = manager->createAction("Select Luminosity Blending Mode");
181 connect(action, SIGNAL(triggered()), SLOT(slotLuminosity()));
182 }
183
validate(const KoColorSpace * cs)184 void KisCompositeOpComboBox::validate(const KoColorSpace *cs) {
185 m_model->validate(cs);
186 }
187
selectCompositeOp(const KoID & op)188 void KisCompositeOpComboBox::selectCompositeOp(const KoID &op) {
189 KoID currentOp;
190 if (m_model->entryAt(currentOp, m_model->index(currentIndex(), 0)) &&
191 currentOp == op) {
192
193 return;
194 }
195
196 QModelIndex index = m_model->indexOf(op);
197
198 setCurrentIndex(index.row());
199 emit activated(index.row());
200 emit activated(op.name());
201 }
202
selectedCompositeOp() const203 KoID KisCompositeOpComboBox::selectedCompositeOp() const {
204 KoID op;
205
206 if (m_model->entryAt(op, m_model->index(currentIndex(), 0))) {
207 return op;
208 }
209 return KoCompositeOpRegistry::instance().getDefaultCompositeOp();
210 }
211
slotCategoryToggled(const QModelIndex & index,bool toggled)212 void KisCompositeOpComboBox::slotCategoryToggled(const QModelIndex& index, bool toggled)
213 {
214 Q_UNUSED(index);
215 Q_UNUSED(toggled);
216
217 //NOTE: this will (should) fit the size of the
218 // popup widget to the view
219 // don't know if this is expected behaviour
220 // on all supported platforms.
221 // There is nothing written about this in the docs.
222 showPopup();
223 }
224
slotEntryChecked(const QModelIndex & index)225 void KisCompositeOpComboBox::slotEntryChecked(const QModelIndex& index)
226 {
227 Q_UNUSED(index);
228 m_allowToHidePopup = false;
229 }
230
hidePopup()231 void KisCompositeOpComboBox::hidePopup()
232 {
233 if (m_allowToHidePopup) {
234 QComboBox::hidePopup();
235 }
236 else {
237 QComboBox::showPopup();
238 }
239
240 m_allowToHidePopup = true;
241 }
242
slotNextBlendingMode()243 void KisCompositeOpComboBox::slotNextBlendingMode()
244 {
245 selectNeighbouringBlendMode(true);
246 }
247
slotPreviousBlendingMode()248 void KisCompositeOpComboBox::slotPreviousBlendingMode()
249 {
250 selectNeighbouringBlendMode(false);
251 }
252
slotNormal()253 void KisCompositeOpComboBox::slotNormal()
254 {
255 selectCompositeOp(KoCompositeOpRegistry::instance().getKoID(COMPOSITE_OVER));
256 }
257
slotDissolve()258 void KisCompositeOpComboBox::slotDissolve()
259 {
260 selectCompositeOp(KoCompositeOpRegistry::instance().getKoID(COMPOSITE_DISSOLVE));
261 }
262
slotBehind()263 void KisCompositeOpComboBox::slotBehind()
264 {
265 selectCompositeOp(KoCompositeOpRegistry::instance().getKoID(COMPOSITE_BEHIND));
266 }
267
slotClear()268 void KisCompositeOpComboBox::slotClear()
269 {
270 selectCompositeOp(KoCompositeOpRegistry::instance().getKoID(COMPOSITE_CLEAR));
271 }
272
slotDarken()273 void KisCompositeOpComboBox::slotDarken()
274 {
275 selectCompositeOp(KoCompositeOpRegistry::instance().getKoID(COMPOSITE_DARKEN));
276 }
277
slotMultiply()278 void KisCompositeOpComboBox::slotMultiply()
279 {
280 selectCompositeOp(KoCompositeOpRegistry::instance().getKoID(COMPOSITE_MULT));
281 }
282
slotColorBurn()283 void KisCompositeOpComboBox::slotColorBurn()
284 {
285 selectCompositeOp(KoCompositeOpRegistry::instance().getKoID(COMPOSITE_BURN));
286 }
287
slotLinearBurn()288 void KisCompositeOpComboBox::slotLinearBurn()
289 {
290 selectCompositeOp(KoCompositeOpRegistry::instance().getKoID(COMPOSITE_LINEAR_BURN));
291 }
292
slotLighten()293 void KisCompositeOpComboBox::slotLighten()
294 {
295 selectCompositeOp(KoCompositeOpRegistry::instance().getKoID(COMPOSITE_LIGHTEN));
296 }
297
slotScreen()298 void KisCompositeOpComboBox::slotScreen()
299 {
300 selectCompositeOp(KoCompositeOpRegistry::instance().getKoID(COMPOSITE_SCREEN));
301 }
302
slotColorDodge()303 void KisCompositeOpComboBox::slotColorDodge()
304 {
305 selectCompositeOp(KoCompositeOpRegistry::instance().getKoID(COMPOSITE_DODGE));
306 }
307
slotLinearDodge()308 void KisCompositeOpComboBox::slotLinearDodge()
309 {
310 selectCompositeOp(KoCompositeOpRegistry::instance().getKoID(COMPOSITE_LINEAR_DODGE));
311 }
312
slotOverlay()313 void KisCompositeOpComboBox::slotOverlay()
314 {
315 selectCompositeOp(KoCompositeOpRegistry::instance().getKoID(COMPOSITE_OVERLAY));
316 }
317
slotHardOverlay()318 void KisCompositeOpComboBox::slotHardOverlay()
319 {
320 selectCompositeOp(KoCompositeOpRegistry::instance().getKoID(COMPOSITE_HARD_OVERLAY));
321 }
322
slotSoftLight()323 void KisCompositeOpComboBox::slotSoftLight()
324 {
325 selectCompositeOp(KoCompositeOpRegistry::instance().getKoID(COMPOSITE_SOFT_LIGHT_PHOTOSHOP));
326 }
327
slotHardLight()328 void KisCompositeOpComboBox::slotHardLight()
329 {
330 selectCompositeOp(KoCompositeOpRegistry::instance().getKoID(COMPOSITE_HARD_LIGHT));
331 }
332
slotVividLight()333 void KisCompositeOpComboBox::slotVividLight()
334 {
335 selectCompositeOp(KoCompositeOpRegistry::instance().getKoID(COMPOSITE_VIVID_LIGHT));
336 }
337
slotLinearLight()338 void KisCompositeOpComboBox::slotLinearLight()
339 {
340 selectCompositeOp(KoCompositeOpRegistry::instance().getKoID(COMPOSITE_LINEAR_LIGHT));
341 }
342
slotPinLight()343 void KisCompositeOpComboBox::slotPinLight()
344 {
345 selectCompositeOp(KoCompositeOpRegistry::instance().getKoID(COMPOSITE_PIN_LIGHT));
346 }
347
slotHardMix()348 void KisCompositeOpComboBox::slotHardMix()
349 {
350 selectCompositeOp(KoCompositeOpRegistry::instance().getKoID(COMPOSITE_HARD_MIX_PHOTOSHOP));
351 }
352
slotDifference()353 void KisCompositeOpComboBox::slotDifference()
354 {
355 selectCompositeOp(KoCompositeOpRegistry::instance().getKoID(COMPOSITE_DIFF));
356 }
357
slotExclusion()358 void KisCompositeOpComboBox::slotExclusion()
359 {
360 selectCompositeOp(KoCompositeOpRegistry::instance().getKoID(COMPOSITE_EXCLUSION));
361 }
362
slotHue()363 void KisCompositeOpComboBox::slotHue()
364 {
365 selectCompositeOp(KoCompositeOpRegistry::instance().getKoID(COMPOSITE_HUE));
366 }
367
slotSaturation()368 void KisCompositeOpComboBox::slotSaturation()
369 {
370 selectCompositeOp(KoCompositeOpRegistry::instance().getKoID(COMPOSITE_SATURATION));
371 }
372
slotColor()373 void KisCompositeOpComboBox::slotColor()
374 {
375 selectCompositeOp(KoCompositeOpRegistry::instance().getKoID(COMPOSITE_COLOR));
376 }
377
slotLuminosity()378 void KisCompositeOpComboBox::slotLuminosity()
379 {
380 selectCompositeOp(KoCompositeOpRegistry::instance().getKoID(COMPOSITE_LUMINIZE));
381 }
382
selectNeighbouringBlendMode(bool down)383 void KisCompositeOpComboBox::selectNeighbouringBlendMode(bool down)
384 {
385 const int rowCount = count();
386 int newIndex = currentIndex();
387
388 QAbstractItemModel *model = this->model();
389 KoID op;
390
391 if (!down) {
392 newIndex--;
393 while ((newIndex >= 0) &&
394 (!(model->flags(model->index(newIndex, modelColumn(), rootModelIndex())) & Qt::ItemIsEnabled) ||
395 !m_model->entryAt(op, m_model->index(newIndex, modelColumn()))))
396
397 newIndex--;
398 } else {
399 newIndex++;
400 while (newIndex < rowCount &&
401 (!(model->index(newIndex, modelColumn(), rootModelIndex()).flags() & Qt::ItemIsEnabled) ||
402 !m_model->entryAt(op, m_model->index(newIndex, modelColumn()))))
403
404 newIndex++;
405 }
406
407 if (newIndex >= 0 && newIndex < rowCount && newIndex != currentIndex()) {
408 setCurrentIndex(newIndex);
409
410 emit activated(newIndex);
411 if (m_model->entryAt(op, m_model->index(newIndex, 0))) {
412 emit activated(op.name());
413 }
414 }
415 }
416
wheelEvent(QWheelEvent * e)417 void KisCompositeOpComboBox::wheelEvent(QWheelEvent *e)
418 {
419 /**
420 * This code is a copy of QComboBox::wheelEvent. It does the same thing,
421 * except that it skips "Category" items, by checking m_model->entryAt()
422 * on each step.
423 */
424
425 QStyleOptionComboBox opt;
426 initStyleOption(&opt);
427
428 #if (QT_VERSION >= QT_VERSION_CHECK(5, 10, 0))
429 if (style()->styleHint(QStyle::SH_ComboBox_AllowWheelScrolling, &opt, this)) {
430 #else
431 if (1) {
432 #endif
433
434 if (e->delta() != 0) {
435 selectNeighbouringBlendMode(e->delta() < 0);
436 }
437
438 e->accept();
439 } else {
440 KisSqueezedComboBox::wheelEvent(e);
441 }
442 }
443
444 void KisCompositeOpComboBox::keyPressEvent(QKeyEvent *e)
445 {
446 /**
447 * This code is a copy of QComboBox::keyPressEvent. It does the same thing,
448 * except that it skips "Category" items, by checking m_model->entryAt()
449 * on each step.
450 */
451
452 enum Move { NoMove=0 , MoveUp , MoveDown , MoveFirst , MoveLast};
453
454 Move move = NoMove;
455 int newIndex = currentIndex();
456 switch (e->key()) {
457 case Qt::Key_Up:
458 if (e->modifiers() & Qt::ControlModifier)
459 break; // pass to line edit for auto completion
460 Q_FALLTHROUGH();
461 case Qt::Key_PageUp:
462 move = MoveUp;
463 break;
464 case Qt::Key_Down:
465 if (e->modifiers() & Qt::AltModifier) {
466 showPopup();
467 return;
468 } else if (e->modifiers() & Qt::ControlModifier)
469 break; // pass to line edit for auto completion
470 Q_FALLTHROUGH();
471 case Qt::Key_PageDown:
472 move = MoveDown;
473 break;
474 case Qt::Key_Home:
475 move = MoveFirst;
476 break;
477 case Qt::Key_End:
478 move = MoveLast;
479 break;
480 case Qt::Key_F4:
481 if (!e->modifiers()) {
482 showPopup();
483 return;
484 }
485 break;
486 case Qt::Key_Space:
487 showPopup();
488 return;
489 default:
490 break;
491 }
492
493 const int rowCount = count();
494
495 if (move != NoMove) {
496 KoID op;
497
498 e->accept();
499 switch (move) {
500 case MoveFirst:
501 newIndex = -1;
502 Q_FALLTHROUGH();
503 case MoveDown:
504 newIndex++;
505 while (newIndex < rowCount &&
506 (!(model()->index(newIndex, modelColumn(), rootModelIndex()).flags() & Qt::ItemIsEnabled) ||
507 !m_model->entryAt(op, m_model->index(newIndex, modelColumn()))))
508 newIndex++;
509 break;
510 case MoveLast:
511 newIndex = rowCount;
512 Q_FALLTHROUGH();
513 case MoveUp:
514 newIndex--;
515 while ((newIndex >= 0) &&
516 (!(model()->flags(model()->index(newIndex, modelColumn(), rootModelIndex())) & Qt::ItemIsEnabled) ||
517 !m_model->entryAt(op, m_model->index(newIndex, modelColumn()))))
518 newIndex--;
519 break;
520 default:
521 e->ignore();
522 break;
523 }
524
525 if (newIndex >= 0 && newIndex < rowCount && newIndex != currentIndex()) {
526 setCurrentIndex(newIndex);
527 emit activated(newIndex);
528
529 if (m_model->entryAt(op, m_model->index(newIndex, 0))) {
530 emit activated(op.name());
531 }
532 }
533 } else {
534 KisSqueezedComboBox::keyPressEvent(e);
535 }
536 }
537
538 KisLayerStyleCompositeOpComboBox::KisLayerStyleCompositeOpComboBox(QWidget* parent)
539 : KisCompositeOpComboBox(true, parent)
540 {
541 }
542