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: class WithEditable Chris@1741: { Chris@1741: protected: Chris@1742: WithEditable(int editableId) : m_editableId(editableId) { } Chris@1741: Chris@1741: std::shared_ptr getEditable() { Chris@1742: auto editable = AnyById::getAs(m_editableId); 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@1742: Chris@1742: private: Chris@1742: int m_editableId; Chris@1741: }; Chris@1741: Chris@1648: /** Chris@1741: * Command to add an event to an editable containing events, with Chris@1742: * undo. The id must be that of a type that can be retrieved from the Chris@1742: * AnyById store and dynamic_cast to EventEditable. Chris@1648: */ Chris@1741: class AddEventCommand : public Command, Chris@1742: public WithEditable Chris@1648: { Chris@1648: public: Chris@1742: AddEventCommand(int editableId, const Event &e, QString name) : Chris@1742: WithEditable(editableId), 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@1648: }; Chris@1648: Chris@1648: /** Chris@1648: * Command to remove an event from an editable containing events, with Chris@1742: * undo. The id must be that of a type that can be retrieved from the Chris@1742: * AnyById store and dynamic_cast to EventEditable. Chris@1648: */ Chris@1741: class RemoveEventCommand : public Command, Chris@1742: public WithEditable Chris@1648: { Chris@1648: public: Chris@1742: RemoveEventCommand(int editableId, const Event &e, QString name) : Chris@1742: WithEditable(editableId), 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@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@1742: * point are collapsed. The id must be that of a type that can be Chris@1742: * retrieved from the AnyById store and dynamic_cast to EventEditable. Chris@1648: */ Chris@1648: class ChangeEventsCommand : public MacroCommand Chris@1648: { Chris@1648: public: Chris@1742: ChangeEventsCommand(int editableId, QString name) : Chris@1742: MacroCommand(name), m_editableId(editableId) { } Chris@1648: Chris@1648: void add(Event e) { Chris@1742: addCommand(new AddEventCommand(m_editableId, e, getName()), true); Chris@1648: } Chris@1648: void remove(Event e) { Chris@1742: addCommand(new RemoveEventCommand(m_editableId, e, getName()), 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@1742: RemoveEventCommand *r = Chris@1742: dynamic_cast(command); Chris@1742: AddEventCommand *a = Chris@1742: 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@1742: int m_editableId; Chris@1648: }; Chris@1648: Chris@1648: #endif