Chris@1648: /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ Chris@1648: Chris@1648: /* Chris@1648: Sonic Visualiser Chris@1648: An audio file viewer and annotation editor. Chris@1648: Centre for Digital Music, Queen Mary, University of London. Chris@1648: This file copyright 2006 Chris Cannam. Chris@1648: Chris@1648: This program is free software; you can redistribute it and/or Chris@1648: modify it under the terms of the GNU General Public License as Chris@1648: published by the Free Software Foundation; either version 2 of the Chris@1648: License, or (at your option) any later version. See the file Chris@1648: COPYING included with this distribution for more information. Chris@1648: */ Chris@1648: Chris@1648: #ifndef SV_EVENT_COMMANDS_H Chris@1648: #define SV_EVENT_COMMANDS_H Chris@1648: Chris@1648: #include "base/Event.h" Chris@1648: #include "base/Command.h" Chris@1741: #include "base/ById.h" Chris@1648: Chris@1648: /** Chris@1648: * Interface for classes that can be modified through these commands Chris@1648: */ Chris@1648: class EventEditable Chris@1648: { Chris@1648: public: Chris@1648: virtual void add(Event e) = 0; Chris@1648: virtual void remove(Event e) = 0; Chris@1648: }; Chris@1648: Chris@1741: template Chris@1741: class WithEditable Chris@1741: { Chris@1741: typedef typename Base::Id Id; Chris@1741: Id m_id; Chris@1741: Chris@1741: protected: Chris@1741: WithEditable(Id id) : m_id(id) { } Chris@1741: Chris@1741: std::shared_ptr getEditable() { Chris@1741: auto base = StaticById::get(m_id); Chris@1741: if (!base) return {}; // acceptable - item has expired Chris@1741: auto editable = std::dynamic_pointer_cast(base); Chris@1741: if (!editable) { Chris@1741: SVCERR << "WARNING: Id passed to EventEditable command is not that of an EventEditable" << endl; Chris@1741: } Chris@1741: return editable; Chris@1741: } Chris@1741: }; Chris@1741: Chris@1648: /** Chris@1741: * Command to add an event to an editable containing events, with Chris@1741: * undo. The template parameter must be a type that can be Chris@1741: * dynamic_cast to EventEditable and that has a ById store. Chris@1648: */ Chris@1741: template Chris@1741: class AddEventCommand : public Command, Chris@1741: public WithEditable Chris@1648: { Chris@1648: public: Chris@1741: AddEventCommand(typename Base::Id editable, const Event &e, QString name) : Chris@1741: WithEditable(editable), m_event(e), m_name(name) { } Chris@1648: Chris@1648: QString getName() const override { return m_name; } Chris@1648: Event getEvent() const { return m_event; } Chris@1648: Chris@1741: void execute() override { Chris@1741: auto editable = getEditable(); Chris@1741: if (editable) editable->add(m_event); Chris@1741: } Chris@1741: void unexecute() override { Chris@1741: auto editable = getEditable(); Chris@1741: if (editable) editable->remove(m_event); Chris@1741: } Chris@1648: Chris@1648: private: Chris@1648: Event m_event; Chris@1648: QString m_name; Chris@1741: using WithEditable::getEditable; Chris@1648: }; Chris@1648: Chris@1648: /** Chris@1648: * Command to remove an event from an editable containing events, with Chris@1741: * undo. The template parameter must be a type that implements Chris@1741: * EventBase and that has a ById store. Chris@1648: */ Chris@1741: template Chris@1741: class RemoveEventCommand : public Command, Chris@1741: public WithEditable Chris@1648: { Chris@1648: public: Chris@1741: RemoveEventCommand(typename Base::Id editable, const Event &e, QString name) : Chris@1741: WithEditable(editable), m_event(e), m_name(name) { } Chris@1648: Chris@1648: QString getName() const override { return m_name; } Chris@1648: Event getEvent() const { return m_event; } Chris@1648: Chris@1741: void execute() override { Chris@1741: auto editable = getEditable(); Chris@1741: if (editable) editable->remove(m_event); Chris@1741: } Chris@1741: void unexecute() override { Chris@1741: auto editable = getEditable(); Chris@1741: if (editable) editable->add(m_event); Chris@1741: } Chris@1648: Chris@1648: private: Chris@1648: Event m_event; Chris@1648: QString m_name; Chris@1741: using WithEditable::getEditable; Chris@1648: }; Chris@1648: Chris@1648: /** Chris@1648: * Command to add or remove a series of events to or from an editable, Chris@1648: * with undo. Creates and immediately executes a sub-command for each Chris@1648: * add/remove requested. Consecutive add/remove pairs for the same Chris@1741: * point are collapsed. The template parameter must be a type that Chris@1741: * implements EventBase and that has a ById store. Chris@1648: */ Chris@1741: template Chris@1648: class ChangeEventsCommand : public MacroCommand Chris@1648: { Chris@1741: typedef typename Base::Id Id; Chris@1741: Chris@1648: public: Chris@1741: ChangeEventsCommand(Id editable, QString name) : Chris@1648: MacroCommand(name), m_editable(editable) { } Chris@1648: Chris@1648: void add(Event e) { Chris@1741: addCommand(new AddEventCommand(m_editable, e, getName()), Chris@1741: true); Chris@1648: } Chris@1648: void remove(Event e) { Chris@1741: addCommand(new RemoveEventCommand(m_editable, e, getName()), Chris@1741: true); Chris@1648: } Chris@1648: Chris@1648: /** Chris@1648: * Stack an arbitrary other command in the same sequence. Chris@1648: */ Chris@1648: void addCommand(Command *command) override { addCommand(command, true); } Chris@1648: Chris@1648: /** Chris@1648: * If any points have been added or deleted, return this Chris@1648: * command (so the caller can add it to the command history). Chris@1648: * Otherwise delete the command and return NULL. Chris@1648: */ Chris@1648: ChangeEventsCommand *finish() { Chris@1648: if (!m_commands.empty()) { Chris@1648: return this; Chris@1648: } else { Chris@1648: delete this; Chris@1648: return nullptr; Chris@1648: } Chris@1648: } Chris@1648: Chris@1648: protected: Chris@1648: virtual void addCommand(Command *command, bool executeFirst) { Chris@1648: Chris@1648: if (executeFirst) command->execute(); Chris@1648: Chris@1648: if (m_commands.empty()) { Chris@1648: MacroCommand::addCommand(command); Chris@1648: return; Chris@1648: } Chris@1648: Chris@1741: RemoveEventCommand *r = Chris@1741: dynamic_cast *>(command); Chris@1741: AddEventCommand *a = Chris@1741: dynamic_cast *>(*m_commands.rbegin()); Chris@1648: if (r && a) { Chris@1648: if (a->getEvent() == r->getEvent()) { Chris@1648: deleteCommand(a); Chris@1648: return; Chris@1648: } Chris@1648: } Chris@1648: Chris@1648: MacroCommand::addCommand(command); Chris@1648: } Chris@1648: Chris@1741: Id m_editable; Chris@1648: }; Chris@1648: Chris@1648: #endif