summaryrefslogtreecommitdiffstats
path: root/src/server/events
diff options
context:
space:
mode:
authorDavid Robillard <d@drobilla.net>2016-07-30 13:10:13 -0400
committerDavid Robillard <d@drobilla.net>2016-07-30 15:32:49 -0400
commitc8ae7295e911c62cf9dedf90187656937cc18cbb (patch)
tree0ba1207bf2c63278e9932eed2d04f961144a02db /src/server/events
parente74c6a3b388ded66fcc4cfb1fa5bece881b63113 (diff)
downloadingen-c8ae7295e911c62cf9dedf90187656937cc18cbb.tar.gz
ingen-c8ae7295e911c62cf9dedf90187656937cc18cbb.tar.bz2
ingen-c8ae7295e911c62cf9dedf90187656937cc18cbb.zip
Add undo support
Diffstat (limited to 'src/server/events')
-rw-r--r--src/server/events/Connect.cpp8
-rw-r--r--src/server/events/Connect.hpp3
-rw-r--r--src/server/events/Copy.cpp8
-rw-r--r--src/server/events/Copy.hpp3
-rw-r--r--src/server/events/CreateBlock.cpp8
-rw-r--r--src/server/events/CreateBlock.hpp3
-rw-r--r--src/server/events/CreateGraph.cpp8
-rw-r--r--src/server/events/CreateGraph.hpp1
-rw-r--r--src/server/events/CreatePort.cpp8
-rw-r--r--src/server/events/CreatePort.hpp3
-rw-r--r--src/server/events/Delete.cpp14
-rw-r--r--src/server/events/Delete.hpp3
-rw-r--r--src/server/events/Delta.cpp57
-rw-r--r--src/server/events/Delta.hpp6
-rw-r--r--src/server/events/Disconnect.cpp8
-rw-r--r--src/server/events/Disconnect.hpp6
-rw-r--r--src/server/events/DisconnectAll.cpp10
-rw-r--r--src/server/events/DisconnectAll.hpp3
-rw-r--r--src/server/events/Mark.cpp65
-rw-r--r--src/server/events/Mark.hpp55
-rw-r--r--src/server/events/Move.cpp8
-rw-r--r--src/server/events/Move.hpp3
-rw-r--r--src/server/events/Undo.cpp77
-rw-r--r--src/server/events/Undo.hpp54
24 files changed, 396 insertions, 26 deletions
diff --git a/src/server/events/Connect.cpp b/src/server/events/Connect.cpp
index 8880322d..f0ba39bb 100644
--- a/src/server/events/Connect.cpp
+++ b/src/server/events/Connect.cpp
@@ -1,6 +1,6 @@
/*
This file is part of Ingen.
- Copyright 2007-2015 David Robillard <http://drobilla.net/>
+ Copyright 2007-2016 David Robillard <http://drobilla.net/>
Ingen is free software: you can redistribute it and/or modify it under the
terms of the GNU Affero General Public License as published by the Free
@@ -168,6 +168,12 @@ Connect::post_process()
}
}
+void
+Connect::undo(Interface& target)
+{
+ target.disconnect(_tail_path, _head_path);
+}
+
} // namespace Events
} // namespace Server
} // namespace Ingen
diff --git a/src/server/events/Connect.hpp b/src/server/events/Connect.hpp
index f6b6cccc..bd15d6d3 100644
--- a/src/server/events/Connect.hpp
+++ b/src/server/events/Connect.hpp
@@ -1,6 +1,6 @@
/*
This file is part of Ingen.
- Copyright 2007-2015 David Robillard <http://drobilla.net/>
+ Copyright 2007-2016 David Robillard <http://drobilla.net/>
Ingen is free software: you can redistribute it and/or modify it under the
terms of the GNU Affero General Public License as published by the Free
@@ -55,6 +55,7 @@ public:
bool pre_process();
void execute(ProcessContext& context);
void post_process();
+ void undo(Interface& target);
private:
const Raul::Path _tail_path;
diff --git a/src/server/events/Copy.cpp b/src/server/events/Copy.cpp
index eed68d75..34a63e58 100644
--- a/src/server/events/Copy.cpp
+++ b/src/server/events/Copy.cpp
@@ -1,6 +1,6 @@
/*
This file is part of Ingen.
- Copyright 2007-2015 David Robillard <http://drobilla.net/>
+ Copyright 2007-2016 David Robillard <http://drobilla.net/>
Ingen is free software: you can redistribute it and/or modify it under the
terms of the GNU Affero General Public License as published by the Free
@@ -211,6 +211,12 @@ Copy::post_process()
}
}
+void
+Copy::undo(Interface& target)
+{
+ target.del(_new_uri);
+}
+
} // namespace Events
} // namespace Server
} // namespace Ingen
diff --git a/src/server/events/Copy.hpp b/src/server/events/Copy.hpp
index 2677ba53..a1726cc6 100644
--- a/src/server/events/Copy.hpp
+++ b/src/server/events/Copy.hpp
@@ -1,6 +1,6 @@
/*
This file is part of Ingen.
- Copyright 2007-2015 David Robillard <http://drobilla.net/>
+ Copyright 2007-2016 David Robillard <http://drobilla.net/>
Ingen is free software: you can redistribute it and/or modify it under the
terms of the GNU Affero General Public License as published by the Free
@@ -49,6 +49,7 @@ public:
bool pre_process();
void execute(ProcessContext& context);
void post_process();
+ void undo(Interface& target);
private:
bool engine_to_engine();
diff --git a/src/server/events/CreateBlock.cpp b/src/server/events/CreateBlock.cpp
index 7ba35d1a..cde15622 100644
--- a/src/server/events/CreateBlock.cpp
+++ b/src/server/events/CreateBlock.cpp
@@ -1,6 +1,6 @@
/*
This file is part of Ingen.
- Copyright 2007-2015 David Robillard <http://drobilla.net/>
+ Copyright 2007-2016 David Robillard <http://drobilla.net/>
Ingen is free software: you can redistribute it and/or modify it under the
terms of the GNU Affero General Public License as published by the Free
@@ -164,6 +164,12 @@ CreateBlock::post_process()
}
}
+void
+CreateBlock::undo(Interface& target)
+{
+ target.del(_block->uri());
+}
+
} // namespace Events
} // namespace Server
} // namespace Ingen
diff --git a/src/server/events/CreateBlock.hpp b/src/server/events/CreateBlock.hpp
index 40d72f52..1282fe8b 100644
--- a/src/server/events/CreateBlock.hpp
+++ b/src/server/events/CreateBlock.hpp
@@ -1,6 +1,6 @@
/*
This file is part of Ingen.
- Copyright 2007-2015 David Robillard <http://drobilla.net/>
+ Copyright 2007-2016 David Robillard <http://drobilla.net/>
Ingen is free software: you can redistribute it and/or modify it under the
terms of the GNU Affero General Public License as published by the Free
@@ -50,6 +50,7 @@ public:
bool pre_process();
void execute(ProcessContext& context);
void post_process();
+ void undo(Interface& target);
private:
Raul::Path _path;
diff --git a/src/server/events/CreateGraph.cpp b/src/server/events/CreateGraph.cpp
index e6ad0cb4..93191437 100644
--- a/src/server/events/CreateGraph.cpp
+++ b/src/server/events/CreateGraph.cpp
@@ -1,6 +1,6 @@
/*
This file is part of Ingen.
- Copyright 2007-2015 David Robillard <http://drobilla.net/>
+ Copyright 2007-2016 David Robillard <http://drobilla.net/>
Ingen is free software: you can redistribute it and/or modify it under the
terms of the GNU Affero General Public License as published by the Free
@@ -213,6 +213,12 @@ CreateGraph::post_process()
_child_events.clear();
}
+void
+CreateGraph::undo(Interface& target)
+{
+ target.del(_graph->uri());
+}
+
} // namespace Events
} // namespace Server
} // namespace Ingen
diff --git a/src/server/events/CreateGraph.hpp b/src/server/events/CreateGraph.hpp
index cf40fb41..efeabf48 100644
--- a/src/server/events/CreateGraph.hpp
+++ b/src/server/events/CreateGraph.hpp
@@ -47,6 +47,7 @@ public:
bool pre_process();
void execute(ProcessContext& context);
void post_process();
+ void undo(Interface& target);
GraphImpl* graph() { return _graph; }
diff --git a/src/server/events/CreatePort.cpp b/src/server/events/CreatePort.cpp
index 0f711f4f..0e512852 100644
--- a/src/server/events/CreatePort.cpp
+++ b/src/server/events/CreatePort.cpp
@@ -1,6 +1,6 @@
/*
This file is part of Ingen.
- Copyright 2007-2015 David Robillard <http://drobilla.net/>
+ Copyright 2007-2016 David Robillard <http://drobilla.net/>
Ingen is free software: you can redistribute it and/or modify it under the
terms of the GNU Affero General Public License as published by the Free
@@ -197,6 +197,12 @@ CreatePort::post_process()
delete _old_ports_array;
}
+void
+CreatePort::undo(Interface& target)
+{
+ target.del(_graph_port->uri());
+}
+
} // namespace Events
} // namespace Server
} // namespace Ingen
diff --git a/src/server/events/CreatePort.hpp b/src/server/events/CreatePort.hpp
index a2dd55ce..754a238f 100644
--- a/src/server/events/CreatePort.hpp
+++ b/src/server/events/CreatePort.hpp
@@ -1,6 +1,6 @@
/*
This file is part of Ingen.
- Copyright 2007-2015 David Robillard <http://drobilla.net/>
+ Copyright 2007-2016 David Robillard <http://drobilla.net/>
Ingen is free software: you can redistribute it and/or modify it under the
terms of the GNU Affero General Public License as published by the Free
@@ -54,6 +54,7 @@ public:
bool pre_process();
void execute(ProcessContext& context);
void post_process();
+ void undo(Interface& target);
private:
enum class Flow {
diff --git a/src/server/events/Delete.cpp b/src/server/events/Delete.cpp
index 06a5cb95..7b27e11f 100644
--- a/src/server/events/Delete.cpp
+++ b/src/server/events/Delete.cpp
@@ -1,6 +1,6 @@
/*
This file is part of Ingen.
- Copyright 2007-2015 David Robillard <http://drobilla.net/>
+ Copyright 2007-2016 David Robillard <http://drobilla.net/>
Ingen is free software: you can redistribute it and/or modify it under the
terms of the GNU Affero General Public License as published by the Free
@@ -162,6 +162,18 @@ Delete::post_process()
}
}
+void
+Delete::undo(Interface& target)
+{
+ auto i = _removed_objects.find(_path);
+ if (i != _removed_objects.end()) {
+ target.put(_uri, i->second->properties());
+ if (_disconnect_event) {
+ _disconnect_event->undo(target);
+ }
+ }
+}
+
} // namespace Events
} // namespace Server
} // namespace Ingen
diff --git a/src/server/events/Delete.hpp b/src/server/events/Delete.hpp
index 4403d4da..c6e38839 100644
--- a/src/server/events/Delete.hpp
+++ b/src/server/events/Delete.hpp
@@ -1,6 +1,6 @@
/*
This file is part of Ingen.
- Copyright 2007-2015 David Robillard <http://drobilla.net/>
+ Copyright 2007-2016 David Robillard <http://drobilla.net/>
Ingen is free software: you can redistribute it and/or modify it under the
terms of the GNU Affero General Public License as published by the Free
@@ -57,6 +57,7 @@ public:
bool pre_process();
void execute(ProcessContext& context);
void post_process();
+ void undo(Interface& target);
private:
Raul::URI _uri;
diff --git a/src/server/events/Delta.cpp b/src/server/events/Delta.cpp
index 83312814..a765932b 100644
--- a/src/server/events/Delta.cpp
+++ b/src/server/events/Delta.cpp
@@ -277,6 +277,7 @@ Delta::pre_process()
_old_bindings = _engine.control_bindings()->remove(port);
}
if (_object) {
+ _removed.emplace(key, value);
_object->remove_property(key, value);
} else if (is_engine && key == uris.ingen_loadedBundle) {
LilvWorld* lworld = _engine.world()->lilv_world();
@@ -299,8 +300,23 @@ Delta::pre_process()
// Remove all added properties if this is a put or set
if (_object && (_type == Type::PUT || _type == Type::SET)) {
- for (const auto& p : _properties) {
- _object->remove_property(p.first, uris.patch_wildcard);
+ for (auto p = _properties.begin();
+ p != _properties.end();
+ p = _properties.upper_bound(p->first)) {
+ for (auto q = _object->properties().find(p->first);
+ q != _object->properties().end() && q->first == p->first;) {
+ auto next = q;
+ ++next;
+
+ if (!_properties.contains(q->first, q->second)) {
+ const auto r = std::make_pair(q->first, q->second);
+ _object->properties().erase(q);
+ _object->on_property_removed(r.first, r.second);
+ _removed.insert(r);
+ }
+
+ q = next;
+ }
}
}
@@ -311,7 +327,9 @@ Delta::pre_process()
if (obj) {
Resource& resource = *obj;
if (value != uris.patch_wildcard) {
- resource.add_property(key, value, value.context());
+ if (resource.add_property(key, value, value.context())) {
+ _added.emplace(key, value);
+ }
}
BlockImpl* block = NULL;
@@ -589,12 +607,16 @@ Delta::post_process()
/* Kludge to avoid feedback for set events only. The GUI
depends on put responses to e.g. initially place blocks.
Some more sensible way of controlling this is needed. */
- _engine.broadcaster()->set_ignore_client(_request_client);
+ if (_mode == Mode::NORMAL) {
+ _engine.broadcaster()->set_ignore_client(_request_client);
+ }
_engine.broadcaster()->set_property(
_subject,
- (*_properties.begin()).first,
- (*_properties.begin()).second);
- _engine.broadcaster()->clear_ignore_client();
+ _properties.begin()->first,
+ _properties.begin()->second);
+ if (_mode == Mode::NORMAL) {
+ _engine.broadcaster()->clear_ignore_client();
+ }
break;
case Type::PUT:
if (_type == Type::PUT && _subject.substr(0, 5) == "file:") {
@@ -614,6 +636,27 @@ Delta::post_process()
}
}
+void
+Delta::undo(Interface& target)
+{
+ const Ingen::URIs& uris = _engine.world()->uris();
+
+ if (_create_event) {
+ _create_event->undo(target);
+ } else if (_type == Type::PATCH) {
+ target.delta(_subject, _added, _removed);
+ } else if (_type == Type::SET || _type == Type::PUT) {
+ if (_removed.size() == 1) {
+ target.set_property(
+ _subject, _removed.begin()->first, _removed.begin()->second);
+ } else if (_removed.empty()) {
+ target.delta(_subject, _added, {});
+ } else {
+ target.put(_subject, _removed);
+ }
+ }
+}
+
} // namespace Events
} // namespace Server
} // namespace Ingen
diff --git a/src/server/events/Delta.hpp b/src/server/events/Delta.hpp
index e9d1970b..b1f2d66a 100644
--- a/src/server/events/Delta.hpp
+++ b/src/server/events/Delta.hpp
@@ -1,6 +1,6 @@
/*
This file is part of Ingen.
- Copyright 2007-2015 David Robillard <http://drobilla.net/>
+ Copyright 2007-2016 David Robillard <http://drobilla.net/>
Ingen is free software: you can redistribute it and/or modify it under the
terms of the GNU Affero General Public License as published by the Free
@@ -76,6 +76,7 @@ public:
bool pre_process();
void execute(ProcessContext& context);
void post_process();
+ void undo(Interface& target);
private:
enum class SpecialType {
@@ -107,6 +108,9 @@ private:
ControlBindings::Key _binding;
Type _type;
+ Resource::Properties _added;
+ Resource::Properties _removed;
+
SPtr<ControlBindings::Bindings> _old_bindings;
boost::optional<Resource> _preset;
diff --git a/src/server/events/Disconnect.cpp b/src/server/events/Disconnect.cpp
index 6f84dc1a..13f419ce 100644
--- a/src/server/events/Disconnect.cpp
+++ b/src/server/events/Disconnect.cpp
@@ -1,6 +1,6 @@
/*
This file is part of Ingen.
- Copyright 2007-2015 David Robillard <http://drobilla.net/>
+ Copyright 2007-2016 David Robillard <http://drobilla.net/>
Ingen is free software: you can redistribute it and/or modify it under the
terms of the GNU Affero General Public License as published by the Free
@@ -219,6 +219,12 @@ Disconnect::post_process()
}
}
+void
+Disconnect::undo(Interface& target)
+{
+ target.connect(_tail_path, _head_path);
+}
+
} // namespace Events
} // namespace Server
} // namespace Ingen
diff --git a/src/server/events/Disconnect.hpp b/src/server/events/Disconnect.hpp
index 64e08246..8a69dac4 100644
--- a/src/server/events/Disconnect.hpp
+++ b/src/server/events/Disconnect.hpp
@@ -1,6 +1,6 @@
/*
This file is part of Ingen.
- Copyright 2007-2015 David Robillard <http://drobilla.net/>
+ Copyright 2007-2016 David Robillard <http://drobilla.net/>
Ingen is free software: you can redistribute it and/or modify it under the
terms of the GNU Affero General Public License as published by the Free
@@ -57,6 +57,7 @@ public:
bool pre_process();
void execute(ProcessContext& context);
void post_process();
+ void undo(Interface& target);
class Impl {
public:
@@ -67,7 +68,8 @@ public:
bool execute(ProcessContext& context, bool set_head_buffers);
- inline InputPort* head() { return _head; }
+ inline OutputPort* tail() { return _tail; }
+ inline InputPort* head() { return _head; }
private:
Engine& _engine;
diff --git a/src/server/events/DisconnectAll.cpp b/src/server/events/DisconnectAll.cpp
index bd4fef7d..ee19797e 100644
--- a/src/server/events/DisconnectAll.cpp
+++ b/src/server/events/DisconnectAll.cpp
@@ -1,6 +1,6 @@
/*
This file is part of Ingen.
- Copyright 2007-2015 David Robillard <http://drobilla.net/>
+ Copyright 2007-2016 David Robillard <http://drobilla.net/>
Ingen is free software: you can redistribute it and/or modify it under the
terms of the GNU Affero General Public License as published by the Free
@@ -164,6 +164,14 @@ DisconnectAll::post_process()
}
}
+void
+DisconnectAll::undo(Interface& target)
+{
+ for (auto& i : _impls) {
+ target.connect(i->tail()->path(), i->head()->path());
+ }
+}
+
} // namespace Events
} // namespace Server
} // namespace Ingen
diff --git a/src/server/events/DisconnectAll.hpp b/src/server/events/DisconnectAll.hpp
index 039e3f54..f8123a45 100644
--- a/src/server/events/DisconnectAll.hpp
+++ b/src/server/events/DisconnectAll.hpp
@@ -1,6 +1,6 @@
/*
This file is part of Ingen.
- Copyright 2007-2015 David Robillard <http://drobilla.net/>
+ Copyright 2007-2016 David Robillard <http://drobilla.net/>
Ingen is free software: you can redistribute it and/or modify it under the
terms of the GNU Affero General Public License as published by the Free
@@ -59,6 +59,7 @@ public:
bool pre_process();
void execute(ProcessContext& context);
void post_process();
+ void undo(Interface& target);
private:
typedef std::list<Disconnect::Impl*> Impls;
diff --git a/src/server/events/Mark.cpp b/src/server/events/Mark.cpp
new file mode 100644
index 00000000..0e14f008
--- /dev/null
+++ b/src/server/events/Mark.cpp
@@ -0,0 +1,65 @@
+/*
+ This file is part of Ingen.
+ Copyright 2016 David Robillard <http://drobilla.net/>
+
+ Ingen is free software: you can redistribute it and/or modify it under the
+ terms of the GNU Affero General Public License as published by the Free
+ Software Foundation, either version 3 of the License, or any later version.
+
+ Ingen is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU Affero General Public License for details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with Ingen. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "Engine.hpp"
+#include "UndoStack.hpp"
+#include "events/Mark.hpp"
+
+namespace Ingen {
+namespace Server {
+namespace Events {
+
+Mark::Mark(Engine& engine,
+ SPtr<Interface> client,
+ int32_t id,
+ SampleCount timestamp,
+ Type type)
+ : Event(engine, client, id, timestamp)
+ , _type(type)
+{}
+
+bool
+Mark::pre_process()
+{
+ UndoStack* const stack = ((_mode == Mode::UNDO)
+ ? _engine.redo_stack()
+ : _engine.undo_stack());
+
+ switch (_type) {
+ case Type::BUNDLE_START:
+ stack->start_entry();
+ break;
+ case Type::BUNDLE_END:
+ stack->finish_entry();
+ break;
+ }
+
+ return Event::pre_process_done(Status::SUCCESS);
+}
+
+void
+Mark::execute(ProcessContext& context)
+{}
+
+void
+Mark::post_process()
+{
+ respond();
+}
+
+} // namespace Events
+} // namespace Server
+} // namespace Ingen
diff --git a/src/server/events/Mark.hpp b/src/server/events/Mark.hpp
new file mode 100644
index 00000000..995df746
--- /dev/null
+++ b/src/server/events/Mark.hpp
@@ -0,0 +1,55 @@
+/*
+ This file is part of Ingen.
+ Copyright 2007-2016 David Robillard <http://drobilla.net/>
+
+ Ingen is free software: you can redistribute it and/or modify it under the
+ terms of the GNU Affero General Public License as published by the Free
+ Software Foundation, either version 3 of the License, or any later version.
+
+ Ingen is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU Affero General Public License for details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with Ingen. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef INGEN_EVENTS_MARK_HPP
+#define INGEN_EVENTS_MARK_HPP
+
+#include "Event.hpp"
+
+namespace Ingen {
+namespace Server {
+
+class Engine;
+
+namespace Events {
+
+/** Set properties of a graph object.
+ * \ingroup engine
+ */
+class Mark : public Event
+{
+public:
+ enum class Type { BUNDLE_START, BUNDLE_END };
+
+ Mark(Engine& engine,
+ SPtr<Interface> client,
+ int32_t id,
+ SampleCount timestamp,
+ Type type);
+
+ bool pre_process();
+ void execute(ProcessContext& context);
+ void post_process();
+
+private:
+ Type _type;
+};
+
+} // namespace Events
+} // namespace Server
+} // namespace Ingen
+
+#endif // INGEN_EVENTS_MARK_HPP
diff --git a/src/server/events/Move.cpp b/src/server/events/Move.cpp
index 2c689fe5..a51617cb 100644
--- a/src/server/events/Move.cpp
+++ b/src/server/events/Move.cpp
@@ -1,6 +1,6 @@
/*
This file is part of Ingen.
- Copyright 2007-2015 David Robillard <http://drobilla.net/>
+ Copyright 2007-2016 David Robillard <http://drobilla.net/>
Ingen is free software: you can redistribute it and/or modify it under the
terms of the GNU Affero General Public License as published by the Free
@@ -87,6 +87,12 @@ Move::post_process()
}
}
+void
+Move::undo(Interface& target)
+{
+ target.move(_new_path, _old_path);
+}
+
} // namespace Events
} // namespace Server
} // namespace Ingen
diff --git a/src/server/events/Move.hpp b/src/server/events/Move.hpp
index ae811138..74d32c61 100644
--- a/src/server/events/Move.hpp
+++ b/src/server/events/Move.hpp
@@ -1,6 +1,6 @@
/*
This file is part of Ingen.
- Copyright 2007-2015 David Robillard <http://drobilla.net/>
+ Copyright 2007-2016 David Robillard <http://drobilla.net/>
Ingen is free software: you can redistribute it and/or modify it under the
terms of the GNU Affero General Public License as published by the Free
@@ -48,6 +48,7 @@ public:
bool pre_process();
void execute(ProcessContext& context);
void post_process();
+ void undo(Interface& target);
private:
const Raul::Path _old_path;
diff --git a/src/server/events/Undo.cpp b/src/server/events/Undo.cpp
new file mode 100644
index 00000000..28b8e188
--- /dev/null
+++ b/src/server/events/Undo.cpp
@@ -0,0 +1,77 @@
+/*
+ This file is part of Ingen.
+ Copyright 2016 David Robillard <http://drobilla.net/>
+
+ Ingen is free software: you can redistribute it and/or modify it under the
+ terms of the GNU Affero General Public License as published by the Free
+ Software Foundation, either version 3 of the License, or any later version.
+
+ Ingen is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU Affero General Public License for details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with Ingen. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "ingen/AtomReader.hpp"
+
+#include "Engine.hpp"
+#include "EventWriter.hpp"
+#include "Undo.hpp"
+
+namespace Ingen {
+namespace Server {
+namespace Events {
+
+Undo::Undo(Engine& engine,
+ SPtr<Interface> client,
+ int32_t id,
+ SampleCount timestamp,
+ bool is_redo)
+ : Event(engine, client, id, timestamp)
+ , _is_redo(is_redo)
+{}
+
+bool
+Undo::pre_process()
+{
+ UndoStack* stack = _is_redo ? _engine.redo_stack() : _engine.undo_stack();
+ Event::Mode mode = _is_redo ? Event::Mode::REDO : Event::Mode::UNDO;
+
+ if (stack->empty()) {
+ return Event::pre_process_done(Status::NOT_FOUND);
+ }
+
+ _entry = stack->pop();
+ _engine.interface()->set_event_mode(mode);
+ if (_entry.events.size() > 1) {
+ _engine.interface()->bundle_begin();
+ }
+
+ for (const LV2_Atom* ev : _entry.events) {
+ _engine.atom_interface()->write(ev);
+ }
+
+ if (_entry.events.size() > 1) {
+ _engine.interface()->bundle_end();
+ }
+ _engine.interface()->set_event_mode(mode);
+
+ return Event::pre_process_done(Status::SUCCESS);
+}
+
+void
+Undo::execute(ProcessContext& context)
+{
+}
+
+void
+Undo::post_process()
+{
+ respond();
+}
+
+} // namespace Events
+} // namespace Server
+} // namespace Ingen
diff --git a/src/server/events/Undo.hpp b/src/server/events/Undo.hpp
new file mode 100644
index 00000000..fff06b8d
--- /dev/null
+++ b/src/server/events/Undo.hpp
@@ -0,0 +1,54 @@
+/*
+ This file is part of Ingen.
+ Copyright 2016 David Robillard <http://drobilla.net/>
+
+ Ingen is free software: you can redistribute it and/or modify it under the
+ terms of the GNU Affero General Public License as published by the Free
+ Software Foundation, either version 3 of the License, or any later version.
+
+ Ingen is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU Affero General Public License for details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with Ingen. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef INGEN_EVENTS_UNDO_HPP
+#define INGEN_EVENTS_UNDO_HPP
+
+#include "Event.hpp"
+#include "UndoStack.hpp"
+#include "types.hpp"
+
+namespace Ingen {
+namespace Server {
+namespace Events {
+
+/** A request to undo the last change to the engine.
+ *
+ * \ingroup engine
+ */
+class Undo : public Event
+{
+public:
+ Undo(Engine& engine,
+ SPtr<Interface> client,
+ int32_t id,
+ SampleCount timestamp,
+ bool is_redo);
+
+ bool pre_process();
+ void execute(ProcessContext& context);
+ void post_process();
+
+private:
+ UndoStack::Entry _entry;
+ bool _is_redo;
+};
+
+} // namespace Events
+} // namespace Server
+} // namespace Ingen
+
+#endif // INGEN_EVENTS_UNDO_HPP