Commit 89daca00 authored by Dmitry Kazakov's avatar Dmitry Kazakov

Add undo-redo copabilities to the Layer Properties dialog

parent 7167c6be
...@@ -26,7 +26,7 @@ ...@@ -26,7 +26,7 @@
class KisStrokesFacade; class KisStrokesFacade;
class KisSavedCommandBase : public KUndo2Command class KRITAIMAGE_EXPORT KisSavedCommandBase : public KUndo2Command
{ {
public: public:
KisSavedCommandBase(const KUndo2MagicString &name, KisStrokesFacade *strokesFacade); KisSavedCommandBase(const KUndo2MagicString &name, KisStrokesFacade *strokesFacade);
...@@ -48,7 +48,7 @@ private: ...@@ -48,7 +48,7 @@ private:
bool m_skipOneRedo; bool m_skipOneRedo;
}; };
class KisSavedCommand : public KisSavedCommandBase class KRITAIMAGE_EXPORT KisSavedCommand : public KisSavedCommandBase
{ {
public: public:
KisSavedCommand(KUndo2CommandSP command, KisStrokesFacade *strokesFacade); KisSavedCommand(KUndo2CommandSP command, KisStrokesFacade *strokesFacade);
...@@ -73,15 +73,15 @@ private: ...@@ -73,15 +73,15 @@ private:
KUndo2CommandSP m_command; KUndo2CommandSP m_command;
}; };
class KisSavedMacroCommand : public KisSavedCommandBase class KRITAIMAGE_EXPORT KisSavedMacroCommand : public KisSavedCommandBase
{ {
public: public:
KisSavedMacroCommand(const KUndo2MagicString &name, KisStrokesFacade *strokesFacade); KisSavedMacroCommand(const KUndo2MagicString &name, KisStrokesFacade *strokesFacade);
~KisSavedMacroCommand(); ~KisSavedMacroCommand();
void addCommand(KUndo2CommandSP command, void addCommand(KUndo2CommandSP command,
KisStrokeJobData::Sequentiality sequentiality, KisStrokeJobData::Sequentiality sequentiality = KisStrokeJobData::SEQUENTIAL,
KisStrokeJobData::Exclusivity exclusivity); KisStrokeJobData::Exclusivity exclusivity = KisStrokeJobData::NORMAL);
void performCancel(KisStrokeId id, bool strokeUndo); void performCancel(KisStrokeId id, bool strokeUndo);
......
...@@ -810,4 +810,20 @@ namespace KisLayerUtils { ...@@ -810,4 +810,20 @@ namespace KisLayerUtils {
mergeMultipleLayersImpl(image, mergedNodes, 0, true, kundo2_i18n("Flatten Image")); mergeMultipleLayersImpl(image, mergedNodes, 0, true, kundo2_i18n("Flatten Image"));
} }
KisSimpleUpdateCommand::KisSimpleUpdateCommand(KisNodeList nodes, bool finalize, KUndo2Command *parent)
: FlipFlopCommand(finalize, parent),
m_nodes(nodes)
{
}
void KisSimpleUpdateCommand::end()
{
updateNodes(m_nodes);
}
void KisSimpleUpdateCommand::updateNodes(const KisNodeList &nodes)
{
Q_FOREACH(KisNodeSP node, nodes) {
node->setDirty(node->extent());
}
}
} }
...@@ -87,6 +87,16 @@ namespace KisLayerUtils ...@@ -87,6 +87,16 @@ namespace KisLayerUtils
private: private:
bool checkIsSourceForClone(KisNodeSP src, const QList<KisNodeSP> &nodes); bool checkIsSourceForClone(KisNodeSP src, const QList<KisNodeSP> &nodes);
}; };
class KRITAIMAGE_EXPORT KisSimpleUpdateCommand : public KisCommandUtils::FlipFlopCommand
{
public:
KisSimpleUpdateCommand(KisNodeList nodes, bool finalize, KUndo2Command *parent = 0);
void end();
static void updateNodes(const KisNodeList &nodes);
private:
KisNodeList m_nodes;
};
}; };
#endif /* __KIS_LAYER_UTILS_H */ #endif /* __KIS_LAYER_UTILS_H */
...@@ -41,6 +41,8 @@ ...@@ -41,6 +41,8 @@
#include "kis_image.h" #include "kis_image.h"
#include "kis_layer_properties_icons.h" #include "kis_layer_properties_icons.h"
#include "kis_signal_compressor.h" #include "kis_signal_compressor.h"
#include "commands_new/kis_saved_commands.h"
#include "kis_post_execution_undo_adapter.h"
struct KisDlgLayerProperties::Private struct KisDlgLayerProperties::Private
...@@ -191,7 +193,18 @@ KisDlgLayerProperties::~KisDlgLayerProperties() ...@@ -191,7 +193,18 @@ KisDlgLayerProperties::~KisDlgLayerProperties()
d->updatesCompressor.stop(); d->updatesCompressor.stop();
updatePreview(); updatePreview();
} }
// TODO: save the undo command
KisPostExecutionUndoAdapter *adapter =
d->view->image()->postExecutionUndoAdapter();
KisSavedMacroCommand *macro = adapter->createMacro(kundo2_i18n("Change Layer Properties"));
macro->addCommand(toQShared(new KisLayerUtils::KisSimpleUpdateCommand(d->nodes, false)));
Q_FOREACH(auto prop, d->allProperties()) {
if (!prop->isIgnored()) {
macro->addCommand(toQShared(prop->createPostExecutionUndoCommand()));
}
}
macro->addCommand(toQShared(new KisLayerUtils::KisSimpleUpdateCommand(d->nodes, true)));
adapter->addMacro(macro);
} }
else /* if (result() == QDialog::Rejected) */ { else /* if (result() == QDialog::Rejected) */ {
Q_FOREACH(auto prop, d->allProperties()) { Q_FOREACH(auto prop, d->allProperties()) {
...@@ -254,7 +267,5 @@ void KisDlgLayerProperties::slotFlagsValueChangedInternally() ...@@ -254,7 +267,5 @@ void KisDlgLayerProperties::slotFlagsValueChangedInternally()
void KisDlgLayerProperties::updatePreview() void KisDlgLayerProperties::updatePreview()
{ {
Q_FOREACH(KisNodeSP node, d->nodes) { KisLayerUtils::KisSimpleUpdateCommand::updateNodes(d->nodes);
node->setDirty(node->extent());
}
} }
...@@ -25,6 +25,7 @@ ...@@ -25,6 +25,7 @@
#include <QRegExp> #include <QRegExp>
#include <QBitArray> #include <QBitArray>
#include <kundo2command.h>
#include <KoColorSpace.h> #include <KoColorSpace.h>
#include <KoChannelInfo.h> #include <KoChannelInfo.h>
...@@ -402,6 +403,52 @@ private: ...@@ -402,6 +403,52 @@ private:
PropertyType *m_parent; PropertyType *m_parent;
}; };
/******************************************************************/
/* MultinodePropertyUndoCommand */
/******************************************************************/
template <class PropertyAdapter>
class MultinodePropertyUndoCommand : public KUndo2Command
{
public:
typedef typename PropertyAdapter::ValueType ValueType;
public:
MultinodePropertyUndoCommand(PropertyAdapter propAdapter,
KisNodeList nodes,
const QList<ValueType> &oldValues,
ValueType newValue,
KUndo2Command *parent = 0)
: KUndo2Command(parent),
m_propAdapter(propAdapter),
m_nodes(nodes),
m_oldValues(oldValues),
m_newValue(newValue)
{
}
void undo() {
int index = 0;
Q_FOREACH (KisNodeSP node, m_nodes) {
m_propAdapter.setPropForNode(node, m_oldValues[index], -1);
index++;
}
}
void redo() {
int index = 0;
Q_FOREACH (KisNodeSP node, m_nodes) {
m_propAdapter.setPropForNode(node, m_newValue, index);
index++;
}
}
private:
PropertyAdapter m_propAdapter;
KisNodeList m_nodes;
QList<ValueType> m_oldValues;
ValueType m_newValue;
};
/******************************************************************/ /******************************************************************/
/* KisMultinodePropertyInterface */ /* KisMultinodePropertyInterface */
/******************************************************************/ /******************************************************************/
...@@ -422,6 +469,8 @@ public: ...@@ -422,6 +469,8 @@ public:
virtual void connectValueChangedSignal(const QObject *receiver, const char *method, Qt::ConnectionType type = Qt::AutoConnection) = 0; virtual void connectValueChangedSignal(const QObject *receiver, const char *method, Qt::ConnectionType type = Qt::AutoConnection) = 0;
virtual void connectIgnoreCheckBox(QCheckBox *ignoreBox) = 0; virtual void connectIgnoreCheckBox(QCheckBox *ignoreBox) = 0;
virtual KUndo2Command* createPostExecutionUndoCommand() = 0;
}; };
typedef QSharedPointer<KisMultinodePropertyInterface> KisMultinodePropertyInterfaceSP; typedef QSharedPointer<KisMultinodePropertyInterface> KisMultinodePropertyInterfaceSP;
...@@ -532,6 +581,13 @@ public: ...@@ -532,6 +581,13 @@ public:
return m_isIgnored; return m_isIgnored;
} }
KUndo2Command* createPostExecutionUndoCommand() {
KIS_ASSERT_RECOVER(!m_isIgnored) { return new KUndo2Command(); }
return new MultinodePropertyUndoCommand<PropertyAdapter>(m_propAdapter, m_nodes,
m_savedValues, m_currentValue);
}
// TODO: disconnect methods... // TODO: disconnect methods...
void connectIgnoreCheckBox(QCheckBox *ignoreBox) { void connectIgnoreCheckBox(QCheckBox *ignoreBox) {
m_connector->connectIgnoreCheckBox(ignoreBox); m_connector->connectIgnoreCheckBox(ignoreBox);
......
...@@ -39,6 +39,7 @@ void KisMultinodePropertyTest::test() ...@@ -39,6 +39,7 @@ void KisMultinodePropertyTest::test()
nodes << layer2; nodes << layer2;
nodes << layer3; nodes << layer3;
// Test uniform initial state
{ {
QScopedPointer<QCheckBox> box(new QCheckBox("test ignore")); QScopedPointer<QCheckBox> box(new QCheckBox("test ignore"));
KisMultinodeCompositeOpProperty prop(nodes); KisMultinodeCompositeOpProperty prop(nodes);
...@@ -78,6 +79,8 @@ void KisMultinodePropertyTest::test() ...@@ -78,6 +79,8 @@ void KisMultinodePropertyTest::test()
QCOMPARE(box->isChecked(), true); QCOMPARE(box->isChecked(), true);
} }
// Test non-uniform initial state
layer1->setCompositeOpId(COMPOSITE_ALPHA_DARKEN); layer1->setCompositeOpId(COMPOSITE_ALPHA_DARKEN);
layer2->setCompositeOpId(COMPOSITE_OVER); layer2->setCompositeOpId(COMPOSITE_OVER);
layer3->setCompositeOpId(COMPOSITE_OVER); layer3->setCompositeOpId(COMPOSITE_OVER);
...@@ -124,6 +127,38 @@ void KisMultinodePropertyTest::test() ...@@ -124,6 +127,38 @@ void KisMultinodePropertyTest::test()
QCOMPARE(box->isEnabled(), true); QCOMPARE(box->isEnabled(), true);
QCOMPARE(box->isChecked(), false); QCOMPARE(box->isChecked(), false);
} }
// Test undo-redo
{
QScopedPointer<QCheckBox> box(new QCheckBox("test ignore"));
KisMultinodeCompositeOpProperty prop(nodes);
prop.connectIgnoreCheckBox(box.data());
QCOMPARE(layer1->compositeOpId(), COMPOSITE_ALPHA_DARKEN);
QCOMPARE(layer2->compositeOpId(), COMPOSITE_OVER);
QCOMPARE(layer3->compositeOpId(), COMPOSITE_OVER);
prop.setIgnored(false);
QCOMPARE(layer1->compositeOpId(), COMPOSITE_ALPHA_DARKEN);
QCOMPARE(layer2->compositeOpId(), COMPOSITE_ALPHA_DARKEN);
QCOMPARE(layer3->compositeOpId(), COMPOSITE_ALPHA_DARKEN);
QScopedPointer<KUndo2Command> cmd(prop.createPostExecutionUndoCommand());
cmd->undo();
QCOMPARE(layer1->compositeOpId(), COMPOSITE_ALPHA_DARKEN);
QCOMPARE(layer2->compositeOpId(), COMPOSITE_OVER);
QCOMPARE(layer3->compositeOpId(), COMPOSITE_OVER);
cmd->redo();
QCOMPARE(layer1->compositeOpId(), COMPOSITE_ALPHA_DARKEN);
QCOMPARE(layer2->compositeOpId(), COMPOSITE_ALPHA_DARKEN);
QCOMPARE(layer3->compositeOpId(), COMPOSITE_ALPHA_DARKEN);
}
} }
QTEST_MAIN(KisMultinodePropertyTest) QTEST_MAIN(KisMultinodePropertyTest)
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment