Skip to content

Commit 9bd8566

Browse files
committed
Add UndoAddOn and supports undo/redo
1 parent 115f2bc commit 9bd8566

File tree

10 files changed

+157
-45
lines changed

10 files changed

+157
-45
lines changed

src/libs/application/dspxmodel/src/EntityObject.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ namespace dspx {
2222
EntityObject::~EntityObject() {
2323
Q_D(EntityObject);
2424
if (d->model && d->handle) {
25-
d->model->strategy()->destroyEntity(d->handle);
25+
2626
}
2727
}
2828

src/libs/application/dspxmodel/src/Model.cpp

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,9 @@ namespace dspx {
6969

7070
void ModelPrivate::handleNotifications() {
7171
Q_Q(Model);
72+
QObject::connect(strategy, &ModelStrategy::destroyEntityNotified, q, [=, this](Handle handle) {
73+
handleEntityDestroyed(handle);
74+
});
7275
QObject::connect(strategy, &ModelStrategy::insertIntoSequenceContainerNotified, q, [=, this](Handle sequenceContainerEntity, Handle entity) {
7376
if (auto sequenceContainerObject = mapToObject(sequenceContainerEntity)) {
7477
sequenceContainerObject->handleInsertIntoSequenceContainer(entity);
@@ -314,7 +317,9 @@ namespace dspx {
314317

315318
void Model::destroyItem(EntityObject *object) {
316319
Q_D(Model);
317-
// TODO do some checks
320+
auto handle = object->handle();
321+
object->d_func()->handle = {};
322+
d->strategy->destroyEntity(handle);
318323
object->deleteLater();
319324
}
320325

src/libs/application/dspxmodel/src/UndoableModelStrategy.cpp

Lines changed: 33 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -17,14 +17,16 @@ namespace dspx {
1717
}
1818

1919
CreateEntityCommand::~CreateEntityCommand() {
20-
if (m_object && m_object->parent() != m_strategy) {
21-
delete m_object;
20+
// Delete object if command is in undone state
21+
if (m_object && m_undone) {
22+
delete m_object.data();
2223
}
2324
}
2425

2526
void CreateEntityCommand::undo() {
2627
// Detach object from strategy but keep it alive
2728
m_object->setParent(nullptr);
29+
m_undone = true;
2830
Q_EMIT m_strategy->destroyEntityNotified(entity());
2931
}
3032

@@ -36,6 +38,7 @@ namespace dspx {
3638
// Subsequent times: re-parent the existing object
3739
m_object->setParent(m_strategy);
3840
}
41+
m_undone = false;
3942
Q_EMIT m_strategy->createEntityNotified(entity(), m_entityType);
4043
}
4144

@@ -49,8 +52,9 @@ namespace dspx {
4952
}
5053

5154
DestroyEntityCommand::~DestroyEntityCommand() {
52-
if (m_object && m_object->parent() != m_strategy) {
53-
delete m_object;
55+
// Delete object if command is NOT in undone state (i.e., destroy was executed)
56+
if (m_object && !m_undone) {
57+
delete m_object.data();
5458
}
5559
}
5660

@@ -94,10 +98,12 @@ namespace dspx {
9498

9599
void DestroyEntityCommand::undo() {
96100
recursivelyCreate(m_object);
101+
m_undone = true;
97102
}
98103

99104
void DestroyEntityCommand::redo() {
100105
recursivelyDestroy(m_object);
106+
m_undone = false;
101107
}
102108

103109
//================================================================
@@ -134,24 +140,27 @@ namespace dspx {
134140
}
135141

136142
TakeFromSequenceContainerCommand::~TakeFromSequenceContainerCommand() {
137-
if (m_object && m_object->parent() != handle_cast<QObject>(m_container)) {
138-
delete m_object;
143+
// Delete object if command is NOT in undone state (i.e., take was executed)
144+
if (m_object && !m_undone) {
145+
delete m_object.data();
139146
}
140147
}
141148

142149
void TakeFromSequenceContainerCommand::undo() {
143150
auto containerObj = handle_cast<BasicModelStrategySequenceContainerEntity>(m_container);
144151
containerObj->sequence.insert(m_object);
145152
m_object->setParent(containerObj);
146-
Q_EMIT m_strategy->insertIntoSequenceContainerNotified(m_container, {reinterpret_cast<quintptr>(m_object)});
153+
m_undone = true;
154+
Q_EMIT m_strategy->insertIntoSequenceContainerNotified(m_container, {reinterpret_cast<quintptr>(m_object.data())});
147155
}
148156

149157
void TakeFromSequenceContainerCommand::redo() {
150158
auto containerObj = handle_cast<BasicModelStrategySequenceContainerEntity>(m_container);
151159
containerObj->sequence.remove(m_object);
152160
m_object->setParent(m_strategy);
153-
Q_EMIT m_strategy->takeFromContainerNotified({reinterpret_cast<quintptr>(m_object)}, m_container,
154-
{reinterpret_cast<quintptr>(m_object)});
161+
m_undone = false;
162+
Q_EMIT m_strategy->takeFromContainerNotified({reinterpret_cast<quintptr>(m_object.data())}, m_container,
163+
{reinterpret_cast<quintptr>(m_object.data())});
155164
}
156165

157166
InsertIntoListContainerCommand::InsertIntoListContainerCommand(
@@ -185,15 +194,17 @@ namespace dspx {
185194
}
186195

187196
TakeFromListContainerCommand::~TakeFromListContainerCommand() {
188-
if (m_object && m_object->parent() != handle_cast<QObject>(m_container)) {
189-
delete m_object;
197+
// Delete object if command is NOT in undone state (i.e., take was executed)
198+
if (m_object && !m_undone) {
199+
delete m_object.data();
190200
}
191201
}
192202

193203
void TakeFromListContainerCommand::undo() {
194204
auto containerObj = handle_cast<BasicModelStrategyListContainerEntity>(m_container);
195205
containerObj->list.insert(m_index, m_object);
196206
m_object->setParent(containerObj);
207+
m_undone = true;
197208
Q_EMIT m_strategy->insertIntoListContainerNotified(m_container, entity(), m_index);
198209
}
199210

@@ -205,11 +216,12 @@ namespace dspx {
205216
auto containerObj = handle_cast<BasicModelStrategyListContainerEntity>(m_container);
206217
containerObj->list.removeAt(m_index);
207218
m_object->setParent(m_strategy);
219+
m_undone = false;
208220
Q_EMIT m_strategy->takeFromListContainerNotified(entity(), m_container, m_index);
209221
}
210222

211223
Handle TakeFromListContainerCommand::entity() const {
212-
return {reinterpret_cast<quintptr>(m_object)};
224+
return {reinterpret_cast<quintptr>(m_object.data())};
213225
}
214226

215227
InsertIntoMapContainerCommand::InsertIntoMapContainerCommand(UndoableModelStrategy *strategy,
@@ -222,7 +234,7 @@ namespace dspx {
222234

223235
InsertIntoMapContainerCommand::~InsertIntoMapContainerCommand() {
224236
if (m_oldObject) {
225-
delete m_oldObject;
237+
delete m_oldObject.data();
226238
}
227239
}
228240

@@ -238,7 +250,7 @@ namespace dspx {
238250
containerObj->map.insert(m_key, m_oldObject);
239251
m_oldObject->setParent(containerObj);
240252
Q_EMIT m_strategy->insertIntoMapContainerNotified(m_container,
241-
{reinterpret_cast<quintptr>(m_oldObject)}, m_key);
253+
{reinterpret_cast<quintptr>(m_oldObject.data())}, m_key);
242254
m_oldObject = nullptr; // Transfer ownership back to container
243255
}
244256
}
@@ -250,7 +262,7 @@ namespace dspx {
250262
if (containerObj->map.contains(m_key)) {
251263
m_oldObject = containerObj->map.take(m_key);
252264
m_oldObject->setParent(nullptr); // Take ownership
253-
Q_EMIT m_strategy->takeFromMapContainerNotified({reinterpret_cast<quintptr>(m_oldObject)}, m_container,
265+
Q_EMIT m_strategy->takeFromMapContainerNotified({reinterpret_cast<quintptr>(m_oldObject.get())}, m_container,
254266
m_key);
255267
}
256268

@@ -266,15 +278,17 @@ namespace dspx {
266278
}
267279

268280
TakeFromMapContainerCommand::~TakeFromMapContainerCommand() {
269-
if (m_object && m_object->parent() != handle_cast<QObject>(m_container)) {
270-
delete m_object;
281+
// Delete object if command is NOT in undone state (i.e., take was executed)
282+
if (m_object && !m_undone) {
283+
delete m_object.data();
271284
}
272285
}
273286

274287
void TakeFromMapContainerCommand::undo() {
275288
auto containerObj = handle_cast<BasicModelStrategyMapContainerEntity>(m_container);
276289
containerObj->map.insert(m_key, m_object);
277290
m_object->setParent(containerObj);
291+
m_undone = true;
278292
Q_EMIT m_strategy->insertIntoMapContainerNotified(m_container, entity(), m_key);
279293
}
280294

@@ -286,11 +300,12 @@ namespace dspx {
286300
auto containerObj = handle_cast<BasicModelStrategyMapContainerEntity>(m_container);
287301
containerObj->map.remove(m_key);
288302
m_object->setParent(m_strategy);
303+
m_undone = false;
289304
Q_EMIT m_strategy->takeFromMapContainerNotified(entity(), m_container, m_key);
290305
}
291306

292307
Handle TakeFromMapContainerCommand::entity() const {
293-
return {reinterpret_cast<quintptr>(m_object)};
308+
return {reinterpret_cast<quintptr>(m_object.data())};
294309
}
295310

296311
RotateListContainerCommand::RotateListContainerCommand(UndoableModelStrategy *strategy,

src/libs/application/dspxmodel/src/UndoableModelStrategy_p.h

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ namespace dspx {
3232
UndoableModelStrategy *m_strategy;
3333
BasicModelStrategy::Entity m_entityType;
3434
QPointer<BasicModelStrategyEntity> m_object;
35+
bool m_undone = false;
3536
};
3637

3738
class DestroyEntityCommand : public QUndoCommand {
@@ -45,6 +46,7 @@ namespace dspx {
4546
private:
4647
UndoableModelStrategy *m_strategy;
4748
QPointer<BasicModelStrategyEntity> m_object;
49+
bool m_undone = false;
4850
void recursivelyDestroy(BasicModelStrategyEntity *object);
4951
void recursivelyCreate(BasicModelStrategyEntity *object);
5052
};
@@ -80,7 +82,8 @@ namespace dspx {
8082
private:
8183
UndoableModelStrategy *m_strategy;
8284
Handle m_container;
83-
BasicModelStrategyEntity *m_object;
85+
QPointer<BasicModelStrategyEntity> m_object;
86+
bool m_undone = false;
8487
};
8588

8689
class InsertIntoListContainerCommand : public QUndoCommand {
@@ -110,8 +113,9 @@ namespace dspx {
110113
private:
111114
UndoableModelStrategy *m_strategy;
112115
Handle m_container;
113-
BasicModelStrategyEntity *m_object;
116+
QPointer<BasicModelStrategyEntity> m_object;
114117
int m_index;
118+
bool m_undone = false;
115119
};
116120

117121
class InsertIntoMapContainerCommand : public QUndoCommand {
@@ -128,7 +132,7 @@ namespace dspx {
128132
Handle m_container;
129133
Handle m_entity;
130134
QString m_key;
131-
BasicModelStrategyEntity *m_oldObject;
135+
QPointer<BasicModelStrategyEntity> m_oldObject;
132136
};
133137

134138
class TakeFromMapContainerCommand : public QUndoCommand {
@@ -143,8 +147,9 @@ namespace dspx {
143147
private:
144148
UndoableModelStrategy *m_strategy;
145149
Handle m_container;
146-
BasicModelStrategyEntity *m_object;
150+
QPointer<BasicModelStrategyEntity> m_object;
147151
QString m_key;
152+
bool m_undone = false;
148153
};
149154

150155
class RotateListContainerCommand : public QUndoCommand {

src/plugins/coreplugin/internal/CorePlugin.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@
6262
#include <coreplugin/internal/RecentFileAddOn.h>
6363
#include <coreplugin/internal/TimeIndicatorPage.h>
6464
#include <coreplugin/internal/TimelineAddOn.h>
65+
#include <coreplugin/internal/UndoAddOn.h>
6566
#include <coreplugin/internal/ViewVisibilityAddOn.h>
6667
#include <coreplugin/internal/WorkspaceAddOn.h>
6768
#include <coreplugin/ProjectWindowInterface.h>
@@ -323,6 +324,7 @@ namespace Core::Internal {
323324
HomeWindowInterfaceRegistry::instance()->attach<FindActionsAddOn>();
324325
ProjectWindowInterfaceRegistry::instance()->attach<FindActionsAddOn>();
325326
ProjectWindowInterfaceRegistry::instance()->attach<EditActionsAddOn>();
327+
ProjectWindowInterfaceRegistry::instance()->attach<UndoAddOn>();
326328
ProjectWindowInterfaceRegistry::instance()->attach<TimelineAddOn>();
327329
ProjectWindowInterfaceRegistry::instance()->attach<ProjectStartupTimerAddOn>();
328330
HomeWindowInterfaceRegistry::instance()->attach<RecentFileAddOn>();
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
#include "UndoAddOn.h"
2+
3+
#include <QQmlComponent>
4+
5+
#include <CoreApi/runtimeinterface.h>
6+
7+
#include <QAKQuick/quickactioncontext.h>
8+
9+
#include <coreplugin/ProjectWindowInterface.h>
10+
11+
namespace Core::Internal {
12+
13+
UndoAddOn::UndoAddOn(QObject *parent) : WindowInterfaceAddOn(parent) {
14+
}
15+
16+
UndoAddOn::~UndoAddOn() = default;
17+
18+
void UndoAddOn::initialize() {
19+
auto windowInterface = windowHandle()->cast<ProjectWindowInterface>();
20+
QQmlComponent component(RuntimeInterface::qmlEngine(), "DiffScope.Core", "UndoAddOnActions");
21+
if (component.isError()) {
22+
qFatal() << component.errorString();
23+
}
24+
auto o = component.createWithInitialProperties({
25+
{"addOn", QVariant::fromValue(this)},
26+
});
27+
o->setParent(this);
28+
QMetaObject::invokeMethod(o, "registerToContext", windowInterface->actionContext());
29+
}
30+
31+
void UndoAddOn::extensionsInitialized() {
32+
}
33+
34+
bool UndoAddOn::delayedInitialize() {
35+
return WindowInterfaceAddOn::delayedInitialize();
36+
}
37+
38+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
#ifndef DIFFSCOPE_COREPLUGIN_UNDOADDON_H
2+
#define DIFFSCOPE_COREPLUGIN_UNDOADDON_H
3+
4+
#include <qqmlintegration.h>
5+
6+
#include <CoreApi/windowinterface.h>
7+
8+
namespace Core::Internal {
9+
10+
class UndoAddOn : public WindowInterfaceAddOn {
11+
Q_OBJECT
12+
QML_ELEMENT
13+
QML_UNCREATABLE("")
14+
public:
15+
explicit UndoAddOn(QObject *parent = nullptr);
16+
~UndoAddOn() override;
17+
18+
void initialize() override;
19+
void extensionsInitialized() override;
20+
bool delayedInitialize() override;
21+
};
22+
23+
}
24+
25+
#endif //DIFFSCOPE_COREPLUGIN_UNDOADDON_H

src/plugins/coreplugin/project/ProjectTimeline.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ namespace Core {
5858
musicTimeline->setTempo(pos, (*tempoSet.begin())->value());
5959
}
6060
tempoPosMap.remove(tempo);
61-
QObject::disconnect(tempo, &dspx::Tempo::valueChanged, q, nullptr);
61+
QObject::disconnect(tempo, nullptr, q, nullptr);
6262
}
6363
void ProjectTimelinePrivate::handleTimeSignatureInsertedOrUpdated(dspx::TimeSignature *timeSignature) {
6464
Q_Q(ProjectTimeline);
@@ -106,7 +106,7 @@ namespace Core {
106106
musicTimeline->setTimeSignature(measure, {item->numerator(), item->denominator()});
107107
}
108108
timeSignatureMeasureMap.remove(timeSignature);
109-
QObject::disconnect(timeSignature, &dspx::TimeSignature::numeratorChanged, q, nullptr);
109+
QObject::disconnect(timeSignature, nullptr, q, nullptr);
110110
}
111111

112112
ProjectTimeline::ProjectTimeline(DspxDocument *document, QObject *parent) : QObject(parent), d_ptr(new ProjectTimelinePrivate) {

src/plugins/coreplugin/qml/actions/EditActionsAddOnActions.qml

Lines changed: 0 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -10,25 +10,6 @@ ActionCollection {
1010
required property QtObject addOn
1111
readonly property ProjectWindowInterface windowHandle: addOn?.windowHandle ?? null
1212

13-
// Basic edit actions
14-
ActionItem {
15-
actionId: "org.diffscope.core.edit.undo"
16-
Action {
17-
onTriggered: {
18-
// TODO: Implement undo functionality
19-
}
20-
}
21-
}
22-
23-
ActionItem {
24-
actionId: "org.diffscope.core.edit.redo"
25-
Action {
26-
onTriggered: {
27-
// TODO: Implement redo functionality
28-
}
29-
}
30-
}
31-
3213
component EditAction: Action {
3314
required property int flag
3415
enabled: d.windowHandle.mainEditActionsHandlerRegistry.enabledActions & flag

0 commit comments

Comments
 (0)