annotate data/model/EventCommands.h @ 1741:9d82b164f264 by-id

Work on commands, and some other model updates
author Chris Cannam
date Thu, 27 Jun 2019 13:08:10 +0100
parents 86bbccb79c9b
children 52705a328b34
rev   line source
Chris@1648 1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
Chris@1648 2
Chris@1648 3 /*
Chris@1648 4 Sonic Visualiser
Chris@1648 5 An audio file viewer and annotation editor.
Chris@1648 6 Centre for Digital Music, Queen Mary, University of London.
Chris@1648 7 This file copyright 2006 Chris Cannam.
Chris@1648 8
Chris@1648 9 This program is free software; you can redistribute it and/or
Chris@1648 10 modify it under the terms of the GNU General Public License as
Chris@1648 11 published by the Free Software Foundation; either version 2 of the
Chris@1648 12 License, or (at your option) any later version. See the file
Chris@1648 13 COPYING included with this distribution for more information.
Chris@1648 14 */
Chris@1648 15
Chris@1648 16 #ifndef SV_EVENT_COMMANDS_H
Chris@1648 17 #define SV_EVENT_COMMANDS_H
Chris@1648 18
Chris@1648 19 #include "base/Event.h"
Chris@1648 20 #include "base/Command.h"
Chris@1741 21 #include "base/ById.h"
Chris@1648 22
Chris@1648 23 /**
Chris@1648 24 * Interface for classes that can be modified through these commands
Chris@1648 25 */
Chris@1648 26 class EventEditable
Chris@1648 27 {
Chris@1648 28 public:
Chris@1648 29 virtual void add(Event e) = 0;
Chris@1648 30 virtual void remove(Event e) = 0;
Chris@1648 31 };
Chris@1648 32
Chris@1741 33 template <typename Base>
Chris@1741 34 class WithEditable
Chris@1741 35 {
Chris@1741 36 typedef typename Base::Id Id;
Chris@1741 37 Id m_id;
Chris@1741 38
Chris@1741 39 protected:
Chris@1741 40 WithEditable(Id id) : m_id(id) { }
Chris@1741 41
Chris@1741 42 std::shared_ptr<EventEditable> getEditable() {
Chris@1741 43 auto base = StaticById<Base, Id>::get(m_id);
Chris@1741 44 if (!base) return {}; // acceptable - item has expired
Chris@1741 45 auto editable = std::dynamic_pointer_cast<EventEditable>(base);
Chris@1741 46 if (!editable) {
Chris@1741 47 SVCERR << "WARNING: Id passed to EventEditable command is not that of an EventEditable" << endl;
Chris@1741 48 }
Chris@1741 49 return editable;
Chris@1741 50 }
Chris@1741 51 };
Chris@1741 52
Chris@1648 53 /**
Chris@1741 54 * Command to add an event to an editable containing events, with
Chris@1741 55 * undo. The template parameter must be a type that can be
Chris@1741 56 * dynamic_cast to EventEditable and that has a ById store.
Chris@1648 57 */
Chris@1741 58 template <typename Base>
Chris@1741 59 class AddEventCommand : public Command,
Chris@1741 60 public WithEditable<Base>
Chris@1648 61 {
Chris@1648 62 public:
Chris@1741 63 AddEventCommand(typename Base::Id editable, const Event &e, QString name) :
Chris@1741 64 WithEditable<Base>(editable), m_event(e), m_name(name) { }
Chris@1648 65
Chris@1648 66 QString getName() const override { return m_name; }
Chris@1648 67 Event getEvent() const { return m_event; }
Chris@1648 68
Chris@1741 69 void execute() override {
Chris@1741 70 auto editable = getEditable();
Chris@1741 71 if (editable) editable->add(m_event);
Chris@1741 72 }
Chris@1741 73 void unexecute() override {
Chris@1741 74 auto editable = getEditable();
Chris@1741 75 if (editable) editable->remove(m_event);
Chris@1741 76 }
Chris@1648 77
Chris@1648 78 private:
Chris@1648 79 Event m_event;
Chris@1648 80 QString m_name;
Chris@1741 81 using WithEditable<Base>::getEditable;
Chris@1648 82 };
Chris@1648 83
Chris@1648 84 /**
Chris@1648 85 * Command to remove an event from an editable containing events, with
Chris@1741 86 * undo. The template parameter must be a type that implements
Chris@1741 87 * EventBase and that has a ById store.
Chris@1648 88 */
Chris@1741 89 template <typename Base>
Chris@1741 90 class RemoveEventCommand : public Command,
Chris@1741 91 public WithEditable<Base>
Chris@1648 92 {
Chris@1648 93 public:
Chris@1741 94 RemoveEventCommand(typename Base::Id editable, const Event &e, QString name) :
Chris@1741 95 WithEditable<Base>(editable), m_event(e), m_name(name) { }
Chris@1648 96
Chris@1648 97 QString getName() const override { return m_name; }
Chris@1648 98 Event getEvent() const { return m_event; }
Chris@1648 99
Chris@1741 100 void execute() override {
Chris@1741 101 auto editable = getEditable();
Chris@1741 102 if (editable) editable->remove(m_event);
Chris@1741 103 }
Chris@1741 104 void unexecute() override {
Chris@1741 105 auto editable = getEditable();
Chris@1741 106 if (editable) editable->add(m_event);
Chris@1741 107 }
Chris@1648 108
Chris@1648 109 private:
Chris@1648 110 Event m_event;
Chris@1648 111 QString m_name;
Chris@1741 112 using WithEditable<Base>::getEditable;
Chris@1648 113 };
Chris@1648 114
Chris@1648 115 /**
Chris@1648 116 * Command to add or remove a series of events to or from an editable,
Chris@1648 117 * with undo. Creates and immediately executes a sub-command for each
Chris@1648 118 * add/remove requested. Consecutive add/remove pairs for the same
Chris@1741 119 * point are collapsed. The template parameter must be a type that
Chris@1741 120 * implements EventBase and that has a ById store.
Chris@1648 121 */
Chris@1741 122 template <typename Base>
Chris@1648 123 class ChangeEventsCommand : public MacroCommand
Chris@1648 124 {
Chris@1741 125 typedef typename Base::Id Id;
Chris@1741 126
Chris@1648 127 public:
Chris@1741 128 ChangeEventsCommand(Id editable, QString name) :
Chris@1648 129 MacroCommand(name), m_editable(editable) { }
Chris@1648 130
Chris@1648 131 void add(Event e) {
Chris@1741 132 addCommand(new AddEventCommand<Base>(m_editable, e, getName()),
Chris@1741 133 true);
Chris@1648 134 }
Chris@1648 135 void remove(Event e) {
Chris@1741 136 addCommand(new RemoveEventCommand<Base>(m_editable, e, getName()),
Chris@1741 137 true);
Chris@1648 138 }
Chris@1648 139
Chris@1648 140 /**
Chris@1648 141 * Stack an arbitrary other command in the same sequence.
Chris@1648 142 */
Chris@1648 143 void addCommand(Command *command) override { addCommand(command, true); }
Chris@1648 144
Chris@1648 145 /**
Chris@1648 146 * If any points have been added or deleted, return this
Chris@1648 147 * command (so the caller can add it to the command history).
Chris@1648 148 * Otherwise delete the command and return NULL.
Chris@1648 149 */
Chris@1648 150 ChangeEventsCommand *finish() {
Chris@1648 151 if (!m_commands.empty()) {
Chris@1648 152 return this;
Chris@1648 153 } else {
Chris@1648 154 delete this;
Chris@1648 155 return nullptr;
Chris@1648 156 }
Chris@1648 157 }
Chris@1648 158
Chris@1648 159 protected:
Chris@1648 160 virtual void addCommand(Command *command, bool executeFirst) {
Chris@1648 161
Chris@1648 162 if (executeFirst) command->execute();
Chris@1648 163
Chris@1648 164 if (m_commands.empty()) {
Chris@1648 165 MacroCommand::addCommand(command);
Chris@1648 166 return;
Chris@1648 167 }
Chris@1648 168
Chris@1741 169 RemoveEventCommand<Base> *r =
Chris@1741 170 dynamic_cast<RemoveEventCommand<Base> *>(command);
Chris@1741 171 AddEventCommand<Base> *a =
Chris@1741 172 dynamic_cast<AddEventCommand<Base> *>(*m_commands.rbegin());
Chris@1648 173 if (r && a) {
Chris@1648 174 if (a->getEvent() == r->getEvent()) {
Chris@1648 175 deleteCommand(a);
Chris@1648 176 return;
Chris@1648 177 }
Chris@1648 178 }
Chris@1648 179
Chris@1648 180 MacroCommand::addCommand(command);
Chris@1648 181 }
Chris@1648 182
Chris@1741 183 Id m_editable;
Chris@1648 184 };
Chris@1648 185
Chris@1648 186 #endif