Krita Source Code Documentation
Loading...
Searching...
No Matches
kis_multinode_property.h
Go to the documentation of this file.
1/*
2 * SPDX-FileCopyrightText: 2016 Dmitry Kazakov <dimula73@gmail.com>
3 *
4 * SPDX-License-Identifier: GPL-2.0-or-later
5 */
6
7#ifndef __KIS_MULTINODE_PROPERTY_H
8#define __KIS_MULTINODE_PROPERTY_H
9
10#include <QObject>
11#include <QCheckBox>
12#include <QPointer>
13#include <QRegExp>
14#include <QBitArray>
15
16#include <kundo2command.h>
17#include <KoColorSpace.h>
18#include <KoChannelInfo.h>
19
20#include "kis_node.h"
21#include "kis_layer.h"
22#include "kis_layer_utils.h"
23
24#include "kritaui_export.h"
25
28template <class PropertyAdapter> class MultinodePropertyBoolConnector;
29template <class PropertyAdapter> class KisMultinodeProperty;
30
31/******************************************************************/
32/* Adapters */
33/******************************************************************/
34
36 static KisNodeList filterNodes(KisNodeList nodes) { return nodes; }
37
38 void setNumNodes(int numNodes) { m_numNodes = numNodes; }
39 int m_numNodes = 0;
40};
41
43 typedef QString ValueType;
45 static const bool forceIgnoreByDefault = false;
46
48 return node->compositeOpId();
49 }
50
51 static void setPropForNode(KisNodeSP node, const ValueType &value, int index) {
52 Q_UNUSED(index);
54 }
55};
56
57struct NameAdapter : public BaseAdapter {
58 typedef QString ValueType;
60 static const bool forceIgnoreByDefault = true;
61
63 return m_numNodes == 1 ? node->name() : stripName(node->name());
64 }
65
66 void setPropForNode(KisNodeSP node, const ValueType &value, int index) {
67 QString name;
68
69 if (index < 0 || m_numNodes == 1) {
70 name = value;
71 } else {
72 name = QString("%1 %2").arg(stripName(value)).arg(index);
73 }
74
75 node->setName(name);
76 }
77
78private:
79 static QString stripName(QString name) {
80 QRegExp rexp("^(.+) (\\d{1,3})$");
81 int pos = rexp.indexIn(name);
82 if (pos > -1) {
83 name = rexp.cap(1);
84 }
85
86 return name;
87 }
88};
89
91 typedef int ValueType;
93 static const bool forceIgnoreByDefault = false;
94
96 return node->colorLabelIndex();
97 }
98
99 static void setPropForNode(KisNodeSP node, const ValueType &value, int index) {
100 Q_UNUSED(index);
102 }
103};
104
106 typedef int ValueType;
108 static const bool forceIgnoreByDefault = false;
109
111 return qRound(node->opacity() / 255.0 * 100);
112 }
113
114 static void setPropForNode(KisNodeSP node, const ValueType &value, int index) {
115 Q_UNUSED(index);
116 node->setOpacity(qRound(value * 255.0 / 100));
117 }
118};
119
120inline uint qHash(const KisBaseNode::Property &prop, uint seed = 0) {
121 return qHash(prop.name, seed);
122}
123
125 typedef bool ValueType;
127 static const bool forceIgnoreByDefault = false;
128
129 LayerPropertyAdapter(const QString &propName) : m_propName(propName) {}
130
133 Q_FOREACH (const KisBaseNode::Property &prop, props) {
134 if (prop.name == m_propName) {
135 return prop.state.toBool();
136 }
137 }
138
139 return false;
140 }
141
142 void setPropForNode(KisNodeSP node, const ValueType &value, int index) {
143 Q_UNUSED(index);
144 bool stateChanged = false;
145
147 KisBaseNode::PropertyList::iterator it = props.begin();
148 KisBaseNode::PropertyList::iterator end = props.end();
149 for (; it != end; ++it) {
150 if (it->name == m_propName) {
151 it->state = value;
152 stateChanged = true;
153 break;
154 }
155 }
156
157 if (stateChanged) {
158 node->setSectionModelProperties(props);
159 }
160 }
161
162 QString name() const {
163 return m_propName;
164 }
165
167 QHash<QString, std::pair<KisBaseNode::Property, int>> adapters;
168
169 Q_FOREACH (KisNodeSP node, nodes) {
170 int sortingIndex = 0;
172 Q_FOREACH (const KisBaseNode::Property &prop, props) {
173 if (prop.state.type() != QVariant::Bool) continue;
174
175 if (!adapters.contains(prop.id)) {
176 adapters.insert(prop.id, std::make_pair(prop, sortingIndex));
177 } else {
178 adapters[prop.id].second = qMin(adapters[prop.id].second, sortingIndex);
179 }
180 sortingIndex++;
181 }
182 }
183
184 QMultiMap<int, KisBaseNode::Property> sortedAdapters;
185 auto it = adapters.constBegin();
186 auto end = adapters.constEnd();
187 for (; it != end; ++it) {
189 int sortingIndex = 0;
190 std::tie(prop, sortingIndex) = it.value();
191
192 sortedAdapters.insert(sortingIndex, prop);
193 }
194
195 return sortedAdapters.values();
196 }
197
198private:
199 QString m_propName;
200};
201
203 typedef bool ValueType;
205 static const bool forceIgnoreByDefault = false;
206
207 struct Property {
208 Property(QString _name, int _channelIndex) : name(_name), channelIndex(_channelIndex) {}
209 QString name;
211 };
213
214 ChannelFlagAdapter(const Property &prop) : m_prop(prop) {}
215
217 KisLayerSP layer = toLayer(node);
218 Q_ASSERT(layer);
219
220 QBitArray flags = layer->channelFlags();
221 if (flags.isEmpty()) return true;
222
223 return flags.testBit(m_prop.channelIndex);
224 }
225
226 void setPropForNode(KisNodeSP node, const ValueType &value, int index) {
227 Q_UNUSED(index);
228 KisLayerSP layer = toLayer(node);
229 Q_ASSERT(layer);
230
231 QBitArray flags = layer->channelFlags();
232 if (flags.isEmpty()) {
233 flags = QBitArray(layer->colorSpace()->channelCount(), true);
234 }
235
236 if (flags.testBit(m_prop.channelIndex) != value) {
237 flags.setBit(m_prop.channelIndex, value);
238 layer->setChannelFlags(flags);
239 }
240 }
241
242 QString name() const {
243 return m_prop.name;
244 }
245
247 PropertyList props;
248
249 {
250 bool nodesDiffer = KisLayerUtils::checkNodesDiffer<const KoColorSpace*>(nodes, [](KisNodeSP node) { return node->colorSpace(); });
251
252 if (nodesDiffer) {
253 return props;
254 }
255 }
256
257
258 const QList<KoChannelInfo*> channels = nodes.first()->colorSpace()->channels();
259
260 int index = 0;
261 Q_FOREACH (KoChannelInfo *info, channels) {
262 props << Property(info->name(), index);
263 index++;
264 }
265
266 return props;
267 }
268
270 KisNodeList filteredNodes;
271 Q_FOREACH (KisNodeSP node, nodes) {
272 if (toLayer(node)) {
273 filteredNodes << node;
274 }
275 }
276 return filteredNodes;
277 }
278private:
280 return qobject_cast<KisLayer*>(node.data());
281 }
282private:
284};
285
286/******************************************************************/
287/* MultinodePropertyConnectorInterface */
288/******************************************************************/
289
290class KRITAUI_EXPORT MultinodePropertyConnectorInterface : public QObject
291{
292 Q_OBJECT
293public:
295
299 virtual void connectIgnoreCheckBox(QCheckBox *ignoreBox) = 0;
300 void connectValueChangedSignal(const QObject *receiver, const char *method, Qt::ConnectionType type = Qt::AutoConnection);
301
308 virtual void connectAutoEnableWidget(QWidget *widget);
309
310Q_SIGNALS:
312
313protected Q_SLOTS:
314 virtual void slotIgnoreCheckBoxChanged(int state) = 0;
315
316public:
320 virtual void notifyValueChanged();
321 virtual void notifyIgnoreChanged() = 0;
322};
323
324/******************************************************************/
325/* MultinodePropertyBaseConnector */
326/******************************************************************/
327
329{
330public:
332
333 void connectIgnoreCheckBox(QCheckBox *ignoreBox) override;
334 void notifyIgnoreChanged() override;
335
336 void connectAutoEnableWidget(QWidget *widget) override;
337
338protected:
339 void slotIgnoreCheckBoxChanged(int state) override;
340
341private:
344};
345
346/******************************************************************/
347/* MultinodePropertyBoolConnector */
348/******************************************************************/
349
350template <class PropertyAdapter>
352{
354public:
356 : m_parent(parent)
357 {
358 }
359
360 void connectIgnoreCheckBox(QCheckBox *ignoreBox) override {
361 m_ignoreBox = ignoreBox;
362
365
366 m_ignoreBox->setTristate(false);
367 } else {
368 m_ignoreBox->setTristate(true);
369 }
370 connect(m_ignoreBox, SIGNAL(stateChanged(int)), SLOT(slotIgnoreCheckBoxChanged(int)));
371 }
372
373 void notifyIgnoreChanged() override {
374 // noop
375 }
376
377 void notifyValueChanged() override {
378 if (m_ignoreBox) {
379 Qt::CheckState newState =
380 m_parent->isIgnored() ? Qt::PartiallyChecked :
381 bool(m_parent->value()) ? Qt::Checked :
382 Qt::Unchecked;
383
384 if (m_ignoreBox->checkState() != newState) {
385 m_ignoreBox->setCheckState(newState);
386 }
387 }
389 }
390protected:
391 void slotIgnoreCheckBoxChanged(int state) override {
392 if (state == Qt::PartiallyChecked) {
393 m_parent->setIgnored(true);
394 } else {
395 m_parent->setIgnored(false);
396 m_parent->setValue(bool(state == Qt::Checked));
397 }
398 }
399
400private:
403};
404
405/******************************************************************/
406/* MultinodePropertyUndoCommand */
407/******************************************************************/
408
409template <class PropertyAdapter>
411{
412public:
413 typedef typename PropertyAdapter::ValueType ValueType;
414public:
415 MultinodePropertyUndoCommand(PropertyAdapter propAdapter,
416 KisNodeList nodes,
417 const QList<ValueType> &oldValues,
418 ValueType newValue,
419 KUndo2Command *parent = 0)
420 : KUndo2Command(parent),
421 m_propAdapter(propAdapter),
422 m_nodes(nodes),
423 m_oldValues(oldValues),
424 m_newValue(newValue)
425 {
426 }
427
428 void undo() override {
429 int index = 0;
430 Q_FOREACH (KisNodeSP node, m_nodes) {
431 m_propAdapter.setPropForNode(node, m_oldValues[index], -1);
432 index++;
433 }
434 }
435
436 void redo() override {
437 int index = 0;
438 Q_FOREACH (KisNodeSP node, m_nodes) {
439 m_propAdapter.setPropForNode(node, m_newValue, index);
440 index++;
441 }
442 }
443
444private:
445 PropertyAdapter m_propAdapter;
449};
450
451/******************************************************************/
452/* KisMultinodePropertyInterface */
453/******************************************************************/
454
456{
457public:
460
461 virtual void rereadCurrentValue() = 0;
462
463 virtual void setIgnored(bool value) = 0;
464 virtual bool isIgnored() const = 0;
465
466 virtual bool savedValuesDiffer() const = 0;
467 virtual bool haveTheOnlyNode() const = 0;
468
469 virtual void connectValueChangedSignal(const QObject *receiver, const char *method, Qt::ConnectionType type = Qt::AutoConnection) = 0;
470 virtual void connectIgnoreCheckBox(QCheckBox *ignoreBox) = 0;
471
472 virtual void connectAutoEnableWidget(QWidget *widget) = 0;
473
475};
476
478
479/******************************************************************/
480/* KisMultinodeProperty */
481/******************************************************************/
482
483template <class PropertyAdapter>
485{
486public:
487 typedef typename PropertyAdapter::ValueType ValueType;
488 typedef typename PropertyAdapter::ConnectorType ConnectorType;
489public:
490 KisMultinodeProperty(KisNodeList nodes, PropertyAdapter adapter = PropertyAdapter())
491 : m_nodes(PropertyAdapter::filterNodes(nodes)),
492 m_savedValuesDiffer(false),
493 m_propAdapter(adapter),
494 m_connector(new ConnectorType(this))
495 {
496 Q_ASSERT(!m_nodes.isEmpty());
497 m_propAdapter.setNumNodes(m_nodes.size());
498
499 ValueType lastValue = m_propAdapter.propForNode(m_nodes.first());
500 Q_FOREACH (KisNodeSP node, m_nodes) {
501 ValueType value = m_propAdapter.propForNode(node);
502 m_savedValues.append(value);
503
504 if (value != lastValue) {
505 m_savedValuesDiffer = true;
506 }
507
508 lastValue = value;
509 }
510
512 m_nodes.size() > 1 && PropertyAdapter::forceIgnoreByDefault ?
513 true : m_savedValuesDiffer;
514
516 }
518
519 void rereadCurrentValue() override {
520 if (m_isIgnored) return;
521
522 ValueType lastValue = m_propAdapter.propForNode(m_nodes.first());
523 Q_FOREACH (KisNodeSP node, m_nodes) {
524 ValueType value = m_propAdapter.propForNode(node);
525
526 if (value != lastValue) {
527 qWarning() << "WARNING: multiprops: values differ after reread!";
528 }
529
530 lastValue = value;
531 }
532
533 if (lastValue != m_currentValue) {
534 m_currentValue = lastValue;
535 m_connector->notifyValueChanged();
536 }
537 }
538
539 void setValue(const ValueType &value) {
540 Q_ASSERT(!m_isIgnored);
541 if (value == m_currentValue) return;
542
543 int index = 0;
544
545 Q_FOREACH (KisNodeSP node, m_nodes) {
546 m_propAdapter.setPropForNode(node, value, index);
547 index++;
548 }
549
551 m_connector->notifyValueChanged();
552 }
553
554 ValueType value() const {
555 return m_currentValue;
556 }
557
558 void setIgnored(bool value) override {
559 if (value == m_isIgnored) return;
560
562 if (m_isIgnored) {
563 int index = 0;
564 Q_FOREACH (KisNodeSP node, m_nodes) {
565 m_propAdapter.setPropForNode(node, m_savedValues[index], -1);
566 index++;
567 }
569 } else {
570 int index = 0;
571 Q_FOREACH (KisNodeSP node, m_nodes) {
572 m_propAdapter.setPropForNode(node, m_currentValue, index);
573 index++;
574 }
575 }
576
577 m_connector->notifyValueChanged();
578 m_connector->notifyIgnoreChanged();
579 }
580
581 bool isIgnored() const override {
582 return m_isIgnored;
583 }
584
591
592 // TODO: disconnect methods...
593 void connectIgnoreCheckBox(QCheckBox *ignoreBox) override {
594 m_connector->connectIgnoreCheckBox(ignoreBox);
595 }
596
597 void connectValueChangedSignal(const QObject *receiver, const char *method, Qt::ConnectionType type = Qt::AutoConnection) override {
598 m_connector->connectValueChangedSignal(receiver, method, type);
599 }
600
601 void connectAutoEnableWidget(QWidget *widget) override {
602 m_connector->connectAutoEnableWidget(widget);
603 }
604
609 bool savedValuesDiffer() const override {
610 return m_savedValuesDiffer;
611 }
612
613 bool haveTheOnlyNode() const override {
614 return m_nodes.size() == 1;
615 }
616
617private:
619 return m_savedValues.first();
620 }
621
622private:
625
628
630 PropertyAdapter m_propAdapter;
631 QScopedPointer<MultinodePropertyConnectorInterface> m_connector;
632};
633
634
639
640#endif /* __KIS_MULTINODE_PROPERTY_H */
float value(const T *src, size_t ch)
unsigned int uint
connect(this, SIGNAL(optionsChanged()), this, SLOT(saveOptions()))
virtual KUndo2Command * createPostExecutionUndoCommand()=0
virtual void setIgnored(bool value)=0
virtual void rereadCurrentValue()=0
virtual void connectValueChangedSignal(const QObject *receiver, const char *method, Qt::ConnectionType type=Qt::AutoConnection)=0
virtual bool savedValuesDiffer() const =0
virtual bool haveTheOnlyNode() const =0
virtual void connectIgnoreCheckBox(QCheckBox *ignoreBox)=0
virtual bool isIgnored() const =0
virtual void connectAutoEnableWidget(QWidget *widget)=0
PropertyAdapter::ConnectorType ConnectorType
KUndo2Command * createPostExecutionUndoCommand() override
bool savedValuesDiffer() const override
KisMultinodeProperty(KisNodeList nodes, PropertyAdapter adapter=PropertyAdapter())
QScopedPointer< MultinodePropertyConnectorInterface > m_connector
bool isIgnored() const override
void connectAutoEnableWidget(QWidget *widget) override
PropertyAdapter::ValueType ValueType
QList< ValueType > m_savedValues
void connectValueChangedSignal(const QObject *receiver, const char *method, Qt::ConnectionType type=Qt::AutoConnection) override
void setValue(const ValueType &value)
bool haveTheOnlyNode() const override
void connectIgnoreCheckBox(QCheckBox *ignoreBox) override
void setIgnored(bool value) override
QString name() const
virtual quint32 channelCount() const =0
KisMultinodePropertyInterface * m_parent
void connectIgnoreCheckBox(QCheckBox *ignoreBox) override
KisMultinodeProperty< PropertyAdapter > PropertyType
void slotIgnoreCheckBoxChanged(int state) override
MultinodePropertyBoolConnector(PropertyType *parent)
virtual void connectIgnoreCheckBox(QCheckBox *ignoreBox)=0
virtual void slotIgnoreCheckBoxChanged(int state)=0
PropertyAdapter::ValueType ValueType
MultinodePropertyUndoCommand(PropertyAdapter propAdapter, KisNodeList nodes, const QList< ValueType > &oldValues, ValueType newValue, KUndo2Command *parent=0)
#define KIS_ASSERT_RECOVER(cond)
Definition kis_assert.h:55
KisMultinodeProperty< ColorLabelAdapter > KisMultinodeColorLabelProperty
KisMultinodeProperty< OpacityAdapter > KisMultinodeOpacityProperty
uint qHash(const KisBaseNode::Property &prop, uint seed=0)
KisMultinodeProperty< NameAdapter > KisMultinodeNameProperty
KisMultinodeProperty< CompositeOpAdapter > KisMultinodeCompositeOpProperty
QSharedPointer< KisMultinodePropertyInterface > KisMultinodePropertyInterfaceSP
static KisNodeList filterNodes(KisNodeList nodes)
void setNumNodes(int numNodes)
Property(QString _name, int _channelIndex)
MultinodePropertyBoolConnector< ChannelFlagAdapter > ConnectorType
ChannelFlagAdapter(const Property &prop)
static KisLayerSP toLayer(KisNodeSP node)
static KisNodeList filterNodes(KisNodeList nodes)
void setPropForNode(KisNodeSP node, const ValueType &value, int index)
static PropertyList adaptersList(KisNodeList nodes)
ValueType propForNode(KisNodeSP node)
QList< Property > PropertyList
static const bool forceIgnoreByDefault
static const bool forceIgnoreByDefault
static ValueType propForNode(KisNodeSP node)
MultinodePropertyBaseConnector ConnectorType
static void setPropForNode(KisNodeSP node, const ValueType &value, int index)
static ValueType propForNode(KisNodeSP node)
static const bool forceIgnoreByDefault
MultinodePropertyBaseConnector ConnectorType
static void setPropForNode(KisNodeSP node, const ValueType &value, int index)
const QString & compositeOpId() const
void setOpacity(quint8 val)
void setColorLabelIndex(int index)
void setName(const QString &name)
virtual const KoColorSpace * colorSpace() const =0
int colorLabelIndex() const
virtual PropertyList sectionModelProperties() const
QString name() const
void setCompositeOpId(const QString &compositeOpId)
quint8 opacity() const
virtual void setSectionModelProperties(const PropertyList &properties)
QBitArray channelFlags
Definition kis_layer.cc:167
const KoColorSpace * colorSpace() const override
returns the image's colorSpace or null, if there is no image
Definition kis_layer.cc:225
virtual void setChannelFlags(const QBitArray &channelFlags)
Definition kis_layer.cc:342
LayerPropertyAdapter(const QString &propName)
ValueType propForNode(KisNodeSP node)
static KisBaseNode::PropertyList adaptersList(KisNodeList nodes)
void setPropForNode(KisNodeSP node, const ValueType &value, int index)
static const bool forceIgnoreByDefault
MultinodePropertyBoolConnector< LayerPropertyAdapter > ConnectorType
ValueType propForNode(KisNodeSP node)
void setPropForNode(KisNodeSP node, const ValueType &value, int index)
static const bool forceIgnoreByDefault
MultinodePropertyBaseConnector ConnectorType
static QString stripName(QString name)
static const bool forceIgnoreByDefault
static void setPropForNode(KisNodeSP node, const ValueType &value, int index)
MultinodePropertyBaseConnector ConnectorType
static ValueType propForNode(KisNodeSP node)