changeset 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 fe3f7f8df3a3
children 52705a328b34
files data/model/EventCommands.h data/model/ImageModel.h data/model/Labeller.h data/model/Model.h data/model/NoteModel.h data/model/Path.h data/model/RegionModel.h data/model/SparseOneDimensionalModel.h data/model/SparseTimeValueModel.h data/model/TextModel.h data/model/test/TestSparseModels.h
diffstat 11 files changed, 113 insertions(+), 51 deletions(-) [+]
line wrap: on
line diff
--- a/data/model/EventCommands.h	Wed Jun 26 17:25:20 2019 +0100
+++ b/data/model/EventCommands.h	Thu Jun 27 13:08:10 2019 +0100
@@ -18,6 +18,7 @@
 
 #include "base/Event.h"
 #include "base/Command.h"
+#include "base/ById.h"
 
 /**
  * Interface for classes that can be modified through these commands
@@ -29,66 +30,111 @@
     virtual void remove(Event e) = 0;
 };
 
+template <typename Base>
+class WithEditable
+{
+    typedef typename Base::Id Id;
+    Id m_id;
+    
+protected:
+    WithEditable(Id id) : m_id(id) { }
+
+    std::shared_ptr<EventEditable> getEditable() {
+        auto base = StaticById<Base, Id>::get(m_id);
+        if (!base) return {}; // acceptable - item has expired
+        auto editable = std::dynamic_pointer_cast<EventEditable>(base);
+        if (!editable) {
+            SVCERR << "WARNING: Id passed to EventEditable command is not that of an EventEditable" << endl;
+        }
+        return editable;
+    }
+};
+
 /**
- * Command to add an event to an editable containing events, with undo.
+ * Command to add an event to an editable containing events, with
+ * undo.  The template parameter must be a type that can be
+ * dynamic_cast to EventEditable and that has a ById store.
  */
-class AddEventCommand : public Command
+template <typename Base>
+class AddEventCommand : public Command,
+                        public WithEditable<Base>
 {
 public:
-    AddEventCommand(EventEditable *editable, const Event &e, QString name) :
-        m_editable(editable), m_event(e), m_name(name) { }
+    AddEventCommand(typename Base::Id editable, const Event &e, QString name) :
+        WithEditable<Base>(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); }
+    void execute() override {
+        auto editable = getEditable();
+        if (editable) editable->add(m_event);
+    }
+    void unexecute() override {
+        auto editable = getEditable();
+        if (editable) editable->remove(m_event);
+    }
 
 private:
-    EventEditable *m_editable;
     Event m_event;
     QString m_name;
+    using WithEditable<Base>::getEditable;
 };
 
 /**
  * Command to remove an event from an editable containing events, with
- * undo.
+ * undo.  The template parameter must be a type that implements
+ * EventBase and that has a ById store.
  */
-class RemoveEventCommand : public Command
+template <typename Base>
+class RemoveEventCommand : public Command,
+                           public WithEditable<Base>
 {
 public:
-    RemoveEventCommand(EventEditable *editable, const Event &e, QString name) :
-        m_editable(editable), m_event(e), m_name(name) { }
+    RemoveEventCommand(typename Base::Id editable, const Event &e, QString name) :
+        WithEditable<Base>(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); }
+    void execute() override {
+        auto editable = getEditable();
+        if (editable) editable->remove(m_event);
+    }
+    void unexecute() override {
+        auto editable = getEditable();
+        if (editable) editable->add(m_event);
+    }
 
 private:
-    EventEditable *m_editable;
     Event m_event;
     QString m_name;
+    using WithEditable<Base>::getEditable;
 };
 
 /**
  * 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.
+ * point are collapsed.  The template parameter must be a type that
+ * implements EventBase and that has a ById store.
  */
+template <typename Base>
 class ChangeEventsCommand : public MacroCommand
 {
+    typedef typename Base::Id Id;
+    
 public:
-    ChangeEventsCommand(EventEditable *editable, QString name) :
+    ChangeEventsCommand(Id editable, QString name) :
         MacroCommand(name), m_editable(editable) { }
 
     void add(Event e) {
-        addCommand(new AddEventCommand(m_editable, e, getName()), true);
+        addCommand(new AddEventCommand<Base>(m_editable, e, getName()),
+                   true);
     }
     void remove(Event e) {
-        addCommand(new RemoveEventCommand(m_editable, e, getName()), true);
+        addCommand(new RemoveEventCommand<Base>(m_editable, e, getName()),
+                   true);
     }
 
     /**
@@ -120,10 +166,10 @@
             return;
         }
         
-        RemoveEventCommand *r =
-            dynamic_cast<RemoveEventCommand *>(command);
-        AddEventCommand *a =
-            dynamic_cast<AddEventCommand *>(*m_commands.rbegin());
+        RemoveEventCommand<Base> *r =
+            dynamic_cast<RemoveEventCommand<Base> *>(command);
+        AddEventCommand<Base> *a =
+            dynamic_cast<AddEventCommand<Base> *>(*m_commands.rbegin());
         if (r && a) {
             if (a->getEvent() == r->getEvent()) {
                 deleteCommand(a);
@@ -134,7 +180,7 @@
         MacroCommand::addCommand(command);
     }
 
-    EventEditable *m_editable;
+    Id m_editable;
 };
 
 #endif
--- a/data/model/ImageModel.h	Wed Jun 26 17:25:20 2019 +0100
+++ b/data/model/ImageModel.h	Thu Jun 27 13:08:10 2019 +0100
@@ -231,8 +231,7 @@
         case 3: e1 = e0.withLabel(value.toString()); break;
         }
 
-        ChangeEventsCommand *command =
-            new ChangeEventsCommand(this, tr("Edit Data"));
+        auto command = new ChangeEventsCommand<Model>(getId(), tr("Edit Data"));
         command->remove(e0);
         command->add(e1);
         return command->finish();
--- a/data/model/Labeller.h	Wed Jun 26 17:25:20 2019 +0100
+++ b/data/model/Labeller.h	Thu Jun 27 13:08:10 2019 +0100
@@ -225,13 +225,16 @@
      * Relabel all events in the given event vector that lie within
      * the given multi-selection, according to the labelling
      * properties of this labeller.  Return a command that has been
-     * executed but not yet added to the history.
+     * executed but not yet added to the history. The template
+     * parameter must be a type that can be dynamic_cast to
+     * EventEditable and that has a ById store.
      */
-    Command *labelAll(EventEditable *editable,
+    template <typename EditableBase>
+    Command *labelAll(typename EditableBase::Id editable,
                       MultiSelection *ms,
                       const EventVector &allEvents) {
 
-        ChangeEventsCommand *command = new ChangeEventsCommand
+        auto command = new ChangeEventsCommand<EditableBase>
             (editable, tr("Label Points"));
 
         Event prev;
@@ -270,14 +273,17 @@
      * that event lies within the given multi-selection, add n-1 new
      * events at equally spaced intervals between it and the following
      * event.  Return a command that has been executed but not yet
-     * added to the history.
+     * added to the history. The template parameter must be a type
+     * that can be dynamic_cast to EventEditable and that has a ById
+     * store.
      */
-    Command *subdivide(EventEditable *editable,
+    template <typename EditableBase>
+    Command *subdivide(typename EditableBase::Id editable,
                        MultiSelection *ms,
                        const EventVector &allEvents,
                        int n) {
 
-        ChangeEventsCommand *command = new ChangeEventsCommand
+        auto command = new ChangeEventsCommand<EditableBase>
             (editable, tr("Subdivide Points"));
 
         for (auto i = allEvents.begin(); i != allEvents.end(); ++i) {
@@ -319,14 +325,17 @@
      * multi-selection, and a number n, remove all but every nth event
      * from the vector within the extents of the multi-selection.
      * Return a command that has been executed but not yet added to
-     * the history.
+     * the history. The template parameter must be a type
+     * that can be dynamic_cast to EventEditable and that has a ById
+     * store.
      */
-    Command *winnow(EventEditable *editable,
+    template <typename EditableBase>
+    Command *winnow(typename EditableBase::Id editable,
                     MultiSelection *ms,
                     const EventVector &allEvents,
                     int n) {
 
-        ChangeEventsCommand *command = new ChangeEventsCommand
+        auto command = new ChangeEventsCommand<EditableBase>
             (editable, tr("Winnow Points"));
 
         int counter = 0;
--- a/data/model/Model.h	Wed Jun 26 17:25:20 2019 +0100
+++ b/data/model/Model.h	Thu Jun 27 13:08:10 2019 +0100
@@ -362,7 +362,7 @@
     sv_frame_t m_extendTo;
 };
 
-typedef Model::ModelId ModelId;
-typedef StaticById<Model, ModelId> ModelById;
+typedef Model::Id ModelId;
+typedef StaticById<Model, Model::Id> ModelById;
 
 #endif
--- a/data/model/NoteModel.h	Wed Jun 26 17:25:20 2019 +0100
+++ b/data/model/NoteModel.h	Thu Jun 27 13:08:10 2019 +0100
@@ -308,8 +308,7 @@
         case 5: e1 = e0.withLabel(value.toString()); break;
         }
 
-        ChangeEventsCommand *command =
-            new ChangeEventsCommand(this, tr("Edit Data"));
+        auto command = new ChangeEventsCommand<Model>(getId(), tr("Edit Data"));
         command->remove(e0);
         command->add(e1);
         return command->finish();
--- a/data/model/Path.h	Wed Jun 26 17:25:20 2019 +0100
+++ b/data/model/Path.h	Thu Jun 27 13:08:10 2019 +0100
@@ -96,6 +96,16 @@
 
         // For historical reasons we serialise a Path as a model,
         // although the class itself no longer is.
+
+        // We also write start and end frames - which our API no
+        // longer exposes - just for backward compatibility
+
+        sv_frame_t start = 0;
+        sv_frame_t end = 0;
+        if (!m_points.empty()) {
+            start = m_points.begin()->frame;
+            end = m_points.rbegin()->frame + m_resolution;
+        }
         
         // Our dataset doesn't have its own export ID, we just use
         // ours. Actually any model could do that, since datasets
@@ -104,11 +114,14 @@
         
         out << indent;
         out << QString("<model id=\"%1\" name=\"\" sampleRate=\"%2\" "
-                       "type=\"sparse\" dimensions=\"2\" resolution=\"%3\" "
-                       "notifyOnAdd=\"true\" dataset=\"%4\" "
-                       "subtype=\"path\" %5/>\n")
+                       "start=\"%3\" end=\"%4\" type=\"sparse\" "
+                       "dimensions=\"2\" resolution=\"%5\" "
+                       "notifyOnAdd=\"true\" dataset=\"%6\" "
+                       "subtype=\"path\" %7/>\n")
             .arg(getExportId())
             .arg(m_sampleRate)
+            .arg(start)
+            .arg(end)
             .arg(m_resolution)
             .arg(getExportId())
             .arg(extraAttributes);
--- a/data/model/RegionModel.h	Wed Jun 26 17:25:20 2019 +0100
+++ b/data/model/RegionModel.h	Thu Jun 27 13:08:10 2019 +0100
@@ -291,8 +291,7 @@
         case 4: e1 = e0.withLabel(value.toString()); break;
         }
 
-        ChangeEventsCommand *command =
-            new ChangeEventsCommand(this, tr("Edit Data"));
+        auto command = new ChangeEventsCommand<Model>(getId(), tr("Edit Data"));
         command->remove(e0);
         command->add(e1);
         return command->finish();
--- a/data/model/SparseOneDimensionalModel.h	Wed Jun 26 17:25:20 2019 +0100
+++ b/data/model/SparseOneDimensionalModel.h	Thu Jun 27 13:08:10 2019 +0100
@@ -239,8 +239,7 @@
         case 2: e1 = e0.withLabel(value.toString()); break;
         }
 
-        ChangeEventsCommand *command =
-            new ChangeEventsCommand(this, tr("Edit Data"));
+        auto command = new ChangeEventsCommand<Model>(getId(), tr("Edit Data"));
         command->remove(e0);
         command->add(e1);
         return command->finish();
--- a/data/model/SparseTimeValueModel.h	Wed Jun 26 17:25:20 2019 +0100
+++ b/data/model/SparseTimeValueModel.h	Thu Jun 27 13:08:10 2019 +0100
@@ -289,8 +289,7 @@
         case 3: e1 = e0.withLabel(value.toString()); break;
         }
 
-        ChangeEventsCommand *command =
-            new ChangeEventsCommand(this, tr("Edit Data"));
+        auto command = new ChangeEventsCommand<Model>(getId(), tr("Edit Data"));
         command->remove(e0);
         command->add(e1);
         return command->finish();
--- a/data/model/TextModel.h	Wed Jun 26 17:25:20 2019 +0100
+++ b/data/model/TextModel.h	Thu Jun 27 13:08:10 2019 +0100
@@ -226,8 +226,7 @@
         case 3: e1 = e0.withLabel(value.toString()); break;
         }
 
-        ChangeEventsCommand *command =
-            new ChangeEventsCommand(this, tr("Edit Data"));
+        auto command = new ChangeEventsCommand<Model>(getId(), tr("Edit Data"));
         command->remove(e0);
         command->add(e1);
         return command->finish();
--- a/data/model/test/TestSparseModels.h	Wed Jun 26 17:25:20 2019 +0100
+++ b/data/model/test/TestSparseModels.h	Thu Jun 27 13:08:10 2019 +0100
@@ -18,7 +18,7 @@
 #include "../SparseOneDimensionalModel.h"
 #include "../NoteModel.h"
 #include "../TextModel.h"
-#include "../PathModel.h"
+#include "../Path.h"
 #include "../ImageModel.h"
 
 #include <QObject>
@@ -252,7 +252,7 @@
     }
     
     void path_xml() {
-        PathModel m(100, 10, false);
+        Path m(100, 10);
         PathPoint p1(20, 30);
         PathPoint p2(40, 60);
         PathPoint p3(50, 49);