changeset 1648:86bbccb79c9b single-point

Switch to a single external set of commands for modifying editables with events
author Chris Cannam
date Fri, 15 Mar 2019 10:57:35 +0000
parents 29a20719796e
children 1cc9a0d4b1b6
files data/model/EventCommands.h data/model/NoteModel.h files.pri
diffstat 3 files changed, 149 insertions(+), 63 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/data/model/EventCommands.h	Fri Mar 15 10:57:35 2019 +0000
@@ -0,0 +1,140 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */
+
+/*
+    Sonic Visualiser
+    An audio file viewer and annotation editor.
+    Centre for Digital Music, Queen Mary, University of London.
+    This file copyright 2006 Chris Cannam.
+    
+    This program is free software; you can redistribute it and/or
+    modify it under the terms of the GNU General Public License as
+    published by the Free Software Foundation; either version 2 of the
+    License, or (at your option) any later version.  See the file
+    COPYING included with this distribution for more information.
+*/
+
+#ifndef SV_EVENT_COMMANDS_H
+#define SV_EVENT_COMMANDS_H
+
+#include "base/Event.h"
+#include "base/Command.h"
+
+/**
+ * Interface for classes that can be modified through these commands
+ */
+class EventEditable
+{
+public:
+    virtual void add(Event e) = 0;
+    virtual void remove(Event e) = 0;
+};
+
+/**
+ * Command to add an event to an editable containing events, with undo.
+ */
+class AddEventCommand : public Command
+{
+public:
+    AddEventCommand(EventEditable *editable, const Event &e, QString name) :
+        m_editable(editable), m_event(e), m_name(name) { }
+
+    QString getName() const override { return m_name; }
+    Event getEvent() const { return m_event; }
+
+    void execute() override { m_editable->add(m_event); }
+    void unexecute() override { m_editable->remove(m_event); }
+
+private:
+    EventEditable *m_editable;
+    Event m_event;
+    QString m_name;
+};
+
+/**
+ * Command to remove an event from an editable containing events, with
+ * undo.
+ */
+class RemoveEventCommand : public Command
+{
+public:
+    RemoveEventCommand(EventEditable *editable, const Event &e, QString name) :
+        m_editable(editable), m_event(e), m_name(name) { }
+
+    QString getName() const override { return m_name; }
+    Event getEvent() const { return m_event; }
+
+    void execute() override { m_editable->remove(m_event); }
+    void unexecute() override { m_editable->add(m_event); }
+
+private:
+    EventEditable *m_editable;
+    Event m_event;
+    QString m_name;
+};
+
+/**
+ * Command to add or remove a series of events to or from an editable,
+ * with undo. Creates and immediately executes a sub-command for each
+ * add/remove requested. Consecutive add/remove pairs for the same
+ * point are collapsed.
+ */
+class ChangeEventsCommand : public MacroCommand
+{
+public:
+    ChangeEventsCommand(EventEditable *editable, QString name) :
+        MacroCommand(name), m_editable(editable) { }
+
+    void add(Event e) {
+        addCommand(new AddEventCommand(m_editable, e, getName()), true);
+    }
+    void remove(Event e) {
+        addCommand(new RemoveEventCommand(m_editable, e, getName()), true);
+    }
+
+    /**
+     * Stack an arbitrary other command in the same sequence.
+     */
+    void addCommand(Command *command) override { addCommand(command, true); }
+
+    /**
+     * If any points have been added or deleted, return this
+     * command (so the caller can add it to the command history).
+     * Otherwise delete the command and return NULL.
+     */
+    ChangeEventsCommand *finish() {
+        if (!m_commands.empty()) {
+            return this;
+        } else {
+            delete this;
+            return nullptr;
+        }
+    }
+
+protected:
+    virtual void addCommand(Command *command, bool executeFirst) {
+        
+        if (executeFirst) command->execute();
+
+        if (m_commands.empty()) {
+            MacroCommand::addCommand(command);
+            return;
+        }
+        
+        RemoveEventCommand *r =
+            dynamic_cast<RemoveEventCommand *>(command);
+        AddEventCommand *a =
+            dynamic_cast<AddEventCommand *>(*m_commands.rbegin());
+        if (r && a) {
+            if (a->getEvent() == r->getEvent()) {
+                deleteCommand(a);
+                return;
+            }
+        }
+        
+        MacroCommand::addCommand(command);
+    }
+
+    EventEditable *m_editable;
+};
+
+#endif
--- a/data/model/NoteModel.h	Thu Mar 14 15:31:59 2019 +0000
+++ b/data/model/NoteModel.h	Fri Mar 15 10:57:35 2019 +0000
@@ -18,6 +18,7 @@
 
 #include "Model.h"
 #include "TabularModel.h"
+#include "EventCommands.h"
 #include "base/UnitDatabase.h"
 #include "base/EventSeries.h"
 #include "base/NoteData.h"
@@ -32,7 +33,8 @@
 
 class NoteModel : public Model,
                   public TabularModel,
-                  public NoteExportable
+                  public NoteExportable,
+                  public EventEditable
 {
     Q_OBJECT
     
@@ -192,67 +194,9 @@
     }
 
     /**
-     * Editing commands and methods.
+     * Editing methods.
      */
-    
-    class EditCommand : public Command
-    {
-    public:
-        //!!! borrowed ptr
-        EditCommand(NoteModel *model, QString name) :
-            m_model(model), m_executed(false), m_name(name) { }
-
-        QString getName() const override {
-            return m_name;
-        }
-
-        void setName(QString name) {
-            m_name = name;
-        }
-
-        void add(Event e) {
-            m_adding.insert(e);
-            m_model->add(e);
-            m_executed = true;
-        }
-
-        void remove(Event e) {
-            m_removing.insert(e);
-            m_model->remove(e);
-            m_executed = true;
-        }
-        
-        void execute() override {
-            if (m_executed) return;
-            for (const Event &e: m_adding) m_model->add(e);
-            for (const Event &e: m_removing) m_model->remove(e);
-            m_executed = true;
-        }
-
-        void unexecute() override {
-            for (const Event &e: m_removing) m_model->add(e);
-            for (const Event &e: m_adding) m_model->remove(e);
-            m_executed = false;
-        }
-
-        EditCommand *finish() {
-            if (m_adding.empty() && m_removing.empty()) {
-                delete this;
-                return nullptr;
-            } else {
-                return this;
-            }
-        }
-
-    private:
-        NoteModel *m_model;
-        bool m_executed;
-        std::set<Event> m_adding;
-        std::set<Event> m_removing;
-        QString m_name;
-    };
-
-    void add(Event e) {
+    void add(Event e) override {
 
         bool allChange = false;
            
@@ -293,7 +237,7 @@
         }
     }
     
-    void remove(Event e) {
+    void remove(Event e) override {
         {
             QMutexLocker locker(&m_mutex);
             m_events.remove(e);
@@ -381,7 +325,8 @@
         case 5: e1 = e0.withLabel(value.toString()); break;
         }
 
-        EditCommand *command = new EditCommand(this, tr("Edit Data"));
+        ChangeEventsCommand *command =
+            new ChangeEventsCommand(this, tr("Edit Data"));
         command->remove(e0);
         command->add(e1);
         return command->finish();
--- a/files.pri	Thu Mar 14 15:31:59 2019 +0000
+++ b/files.pri	Fri Mar 15 10:57:35 2019 +0000
@@ -80,6 +80,7 @@
            data/model/DenseThreeDimensionalModel.h \
            data/model/DenseTimeValueModel.h \
            data/model/EditableDenseThreeDimensionalModel.h \
+           data/model/EventCommands.h \
            data/model/FFTModel.h \
            data/model/ImageModel.h \
            data/model/IntervalModel.h \