changeset 420:50a956688baa

* reorganise tabular data editor model support
author Chris Cannam
date Wed, 11 Jun 2008 16:13:25 +0000
parents 64e7bbb255d3
children 397fe91dc8e0
files data/data.pro data/model/ModelDataTableModel.cpp data/model/ModelDataTableModel.h data/model/SparseModel.h data/model/SparseOneDimensionalModel.h data/model/SparseTimeValueModel.h data/model/TabularModel.h
diffstat 7 files changed, 352 insertions(+), 281 deletions(-) [+]
line wrap: on
line diff
--- a/data/data.pro	Wed Jun 11 12:54:18 2008 +0000
+++ b/data/data.pro	Wed Jun 11 16:13:25 2008 +0000
@@ -62,6 +62,7 @@
            model/SparseOneDimensionalModel.h \
            model/SparseTimeValueModel.h \
            model/SparseValueModel.h \
+           model/TabularModel.h \
            model/TextModel.h \
            model/WaveFileModel.h \
            model/WritableWaveFileModel.h \
--- a/data/model/ModelDataTableModel.cpp	Wed Jun 11 12:54:18 2008 +0000
+++ b/data/model/ModelDataTableModel.cpp	Wed Jun 11 16:13:25 2008 +0000
@@ -15,19 +15,20 @@
 
 #include "ModelDataTableModel.h"
 
-#include "SparseTimeValueModel.h"
-#include "SparseOneDimensionalModel.h"
-#include "SparseModel.h"
+#include "TabularModel.h"
+#include "Model.h"
 
 #include <algorithm>
+#include <iostream>
 
-ModelDataTableModel::ModelDataTableModel(Model *m) :
+ModelDataTableModel::ModelDataTableModel(TabularModel *m) :
     m_model(m)
 {
-    connect(m, SIGNAL(modelChanged()), this, SLOT(modelChanged()));
-    connect(m, SIGNAL(modelChanged(size_t, size_t)),
+    Model *baseModel = dynamic_cast<Model *>(m);
+
+    connect(baseModel, SIGNAL(modelChanged()), this, SLOT(modelChanged()));
+    connect(baseModel, SIGNAL(modelChanged(size_t, size_t)),
             this, SLOT(modelChanged(size_t, size_t)));
-    rebuildRowVector();
 }
 
 ModelDataTableModel::~ModelDataTableModel()
@@ -37,191 +38,22 @@
 QVariant
 ModelDataTableModel::data(const QModelIndex &index, int role) const
 {
-    if (role != Qt::DisplayRole && role != Qt::EditRole) {
-        return QVariant();
-    }
-
-    bool withUnit = (role == Qt::DisplayRole);
-
     if (!index.isValid()) return QVariant();
-
-    int row = index.row(), col = index.column();
-
-    if (row < 0 || row >= m_rows.size()) return QVariant();
-
-    if (dynamic_cast<const SparseOneDimensionalModel *>(m_model)) {
-        return dataSparse<SparseOneDimensionalModel::Point>(row, col, withUnit);
-    } else if (dynamic_cast<const SparseTimeValueModel *>(m_model)) {
-        return dataSparse<SparseTimeValueModel::Point>(row, col, withUnit);
-    }
-
-    return QVariant();
-}
-
-template <typename PointType>
-QVariant
-ModelDataTableModel::dataSparse(int row, int col, bool withUnit) const
-{
-    size_t frame = m_rows[row];
-    
-    // This is just garbage.  This would be a reasonable enough way to
-    // handle this in a dynamically typed language but it's hopeless
-    // in C++.  The design is simply wrong.  We need virtual helper
-    // methods in the model itself.
-
-    typedef SparseModel<PointType> ModelType;
-    typedef std::multiset<PointType, typename PointType::OrderComparator> 
-        PointListType;
-
-    const ModelType *sm = dynamic_cast<const ModelType *>(m_model);
-    const PointListType &points = sm->getPoints(frame);
-
-    // it is possible to have more than one point at the same frame
-
-    int indexAtFrame = 0;
-    int ri = row;
-    while (ri > 0 && m_rows[ri-1] == m_rows[row]) { --ri; ++indexAtFrame; }
-
-    for (typename PointListType::const_iterator i = points.begin();
-         i != points.end(); ++i) {
-
-        const PointType *point = &(*i);
-        if (point->frame < frame) continue;
-        if (point->frame > frame) return QVariant();
-        if (indexAtFrame > 0) { --indexAtFrame; continue; }
-
-        switch (col) {
-
-        case 0:
-        {
-            RealTime rt = RealTime::frame2RealTime(frame, m_model->getSampleRate());
-            std::cerr << "Returning time " << rt << std::endl;
-            return QVariant(rt.toText().c_str());
-        }
-
-        case 1:
-            std::cerr << "Returning frame " << frame << std::endl;
-            return QVariant(int(frame));
-
-        case 2:
-            if (dynamic_cast<const SparseOneDimensionalModel *>(m_model)) {
-                const SparseOneDimensionalModel::Point *cp = 
-                    reinterpret_cast<const SparseOneDimensionalModel::Point *>(point);
-                std::cerr << "Returning label \"" << cp->label.toStdString() << "\"" << std::endl;
-                return QVariant(cp->label);
-            } else if (dynamic_cast<const SparseTimeValueModel *>(m_model)) {
-                const SparseTimeValueModel::Point *cp = 
-                    reinterpret_cast<const SparseTimeValueModel::Point *>(point);
-                std::cerr << "Returning value " << cp->value << std::endl;
-                if (withUnit) {
-                    return QVariant(QString("%1 %2").arg(cp->value)
-                                    .arg(dynamic_cast<const SparseTimeValueModel *>(m_model)->getScaleUnits()));
-                } else {
-                    return cp->value;
-                }
-            } else return QVariant();
-
-        case 3: 
-            if (dynamic_cast<const SparseOneDimensionalModel *>(m_model)) {
-                return QVariant();
-            } else if (dynamic_cast<const SparseTimeValueModel *>(m_model)) {
-                return reinterpret_cast<const SparseTimeValueModel::Point *>(point)->label;
-            } else return QVariant();
-        }
-    }
-
-    return QVariant();
+    return m_model->getData(getUnsorted(index.row()), index.column(), role);
 }
 
 bool
 ModelDataTableModel::setData(const QModelIndex &index, const QVariant &value, int role)
 {
-    if (role != Qt::EditRole) {
-        std::cerr << "setData: ignoring role " << role << std::endl;
+    if (!index.isValid()) return false;
+    Command *command = m_model->setData(getUnsorted(index.row()),
+                                        index.column(), value, role);
+    if (command) {
+        emit executeCommand(command);
+        return true;
+    } else {
         return false;
     }
-
-    //!!! see comment about disgustuality of this whole process, in
-    //dataSparse above
-
-    if (!index.isValid()) return false;
-
-    int row = index.row(), col = index.column();
-
-    if (row < 0 || row >= m_rows.size()) return false;
-
-    if (dynamic_cast<const SparseOneDimensionalModel *>(m_model)) {
-        return setDataSparse<SparseOneDimensionalModel::Point>(row, col, value);
-    } else if (dynamic_cast<const SparseTimeValueModel *>(m_model)) {
-        return setDataSparse<SparseTimeValueModel::Point>(row, col, value);
-    }
-
-    return false;
-}
-
-template <typename PointType>
-bool
-ModelDataTableModel::setDataSparse(int row, int col, QVariant value)
-{
-    size_t frame = m_rows[row];
-    
-    typedef SparseModel<PointType> ModelType;
-    typedef std::multiset<PointType, typename PointType::OrderComparator> 
-        PointListType;
-    typedef typename ModelType::EditCommand EditCommandType;
-
-    ModelType *sm = dynamic_cast<ModelType *>(m_model);
-    const PointListType &points = sm->getPoints(frame);
-
-    // it is possible to have more than one point at the same frame
-
-    int indexAtFrame = 0;
-    int ri = row;
-    while (ri > 0 && m_rows[ri-1] == m_rows[row]) { --ri; ++indexAtFrame; }
-
-    for (typename PointListType::const_iterator i = points.begin();
-         i != points.end(); ++i) {
-
-        const PointType *point = &(*i);
-        if (point->frame < frame) continue;
-        if (point->frame > frame) return false;
-        if (indexAtFrame > 0) { --indexAtFrame; continue; }
-
-        switch (col) {
-
-        case 0:
-        {
-/*
-            RealTime rt = RealTime::frame2RealTime(frame, m_model->getSampleRate());
-            std::cerr << "Returning time " << rt << std::endl;
-            return QVariant(rt.toText().c_str());
-*/
-        }
-
-        case 1:
-        {
-            EditCommandType *command = 
-                new EditCommandType(sm, tr("Edit point time"));
-            PointType newPoint(*point);
-            newPoint.frame = value.toInt(); //!!! check validity
-            command->deletePoint(*point);
-            command->addPoint(newPoint);
-            command = command->finish();
-            if (command) emit executeCommand(command);
-            return true;
-        }
-//            std::cerr << "Returning frame " << frame << std::endl;
-//            return QVariant(frame); //!!! RealTime
-
-        case 2:
-            break;
-
-        case 3: 
-            break;
-        }
-    }
-
-    return false;
 }
 
 Qt::ItemFlags
@@ -236,19 +68,7 @@
 ModelDataTableModel::headerData(int section, Qt::Orientation orientation, int role) const
 {
     if (orientation == Qt::Horizontal && role == Qt::DisplayRole) {
-        if (section == 0) return QVariant(tr("Time"));
-        if (section == 1) return QVariant(tr("Frame"));
-        else if (section == 2) {
-            if (dynamic_cast<const SparseOneDimensionalModel *>(m_model)) {
-                return QVariant(tr("Label"));
-            } else if (dynamic_cast<const SparseTimeValueModel *>(m_model)) {
-                return QVariant(tr("Value"));
-            }
-        } else if (section == 3) {
-            if (dynamic_cast<const SparseTimeValueModel *>(m_model)) {
-                return QVariant(tr("Label"));
-            }
-        }
+        return m_model->getHeading(section);
     }
     return QVariant();
 }
@@ -269,98 +89,94 @@
 ModelDataTableModel::rowCount(const QModelIndex &parent) const
 {
     if (parent.isValid()) return 0;
-    return m_rows.size();
+    return m_model->getRowCount();
 }
 
 int
 ModelDataTableModel::columnCount(const QModelIndex &parent) const
 {
     if (parent.isValid()) return 0;
-    if (!canHandleModelType(m_model)) return 0;
-
-    if (dynamic_cast<SparseOneDimensionalModel *>(m_model)) {
-        return 3;
-    } else if (dynamic_cast<SparseTimeValueModel *>(m_model)) {
-        return 4;
-    }
-
-    return 2;
+    return m_model->getColumnCount();
 }
 
 QModelIndex 
 ModelDataTableModel::getModelIndexForFrame(size_t frame) const
 {
-    std::vector<size_t>::const_iterator i =
-        std::lower_bound(m_rows.begin(), m_rows.end(), frame);
-    size_t dist = std::distance(m_rows.begin(), i);
-    return createIndex(dist, 0, 0);
+    int row = m_model->getRowForFrame(frame);
+    return createIndex(getSorted(row), 0, 0);
 }
 
 size_t 
 ModelDataTableModel::getFrameForModelIndex(const QModelIndex &index) const
 {
-    int row = index.row();
-    if (m_rows.empty()) return 0;
-    if (row < 0) row == 0;
-    if (row > m_rows.size()-1) row = m_rows.size()-1;
-    return m_rows[row];
+    return m_model->getFrameForRow(getUnsorted(index.row()));
+}
+
+void
+ModelDataTableModel::sort(int column, Qt::SortOrder sortOrder)
+{
+    std::cerr << "ModelDataTableModel::sort(" << column << ", " << sortOrder
+              << ")" << std::endl;
+    m_sortColumn = column;
+    m_sortOrdering = sortOrder;
+    m_sort.clear();
+    emit layoutChanged();
 }
 
 void
 ModelDataTableModel::modelChanged()
 {
-    rebuildRowVector();
+    m_sort.clear();
     emit layoutChanged();
 }
 
 void 
 ModelDataTableModel::modelChanged(size_t f0, size_t f1)
 {
-    std::cerr << "ModelDataTableModel::modelChanged(" << f0 << "," << f1 << ")" << std::endl;
-    //!!! highly inefficient
-    rebuildRowVector();
+    //!!! inefficient
+    m_sort.clear();
     emit layoutChanged();
 }
 
-void
-ModelDataTableModel::rebuildRowVector()
+int
+ModelDataTableModel::getSorted(int row)
 {
-    if (!canHandleModelType(m_model)) return;
+    if (m_model->isColumnTimeValue(m_sortColumn)) {
+        if (m_sortOrdering == Qt::AscendingOrder) {
+            return row;
+        } else {
+            return rowCount() - row - 1;
+        }
+    }
 
-    m_rows.clear();
-
-    if (dynamic_cast<SparseOneDimensionalModel *>(m_model)) {
-        rebuildRowVectorSparse<SparseOneDimensionalModel::Point>();
-    } else if (dynamic_cast<SparseTimeValueModel *>(m_model)) {
-        rebuildRowVectorSparse<SparseTimeValueModel::Point>();
+    if (m_sort.empty()) {
+        resort();
     }
+    if (row < 0 || row >= m_sort.size()) return 0;
+    return m_sort[row];
 }
 
-template <typename PointType>
-void
-ModelDataTableModel::rebuildRowVectorSparse()
+int
+ModelDataTableModel::getUnsorted(int row)
 {
-    // gah
-
-    typedef SparseModel<PointType> ModelType;
-    typedef std::multiset<PointType, typename PointType::OrderComparator> 
-        PointListType;
-
-    ModelType *sm = dynamic_cast<ModelType *>(m_model);
-    const PointListType &points = sm->getPoints();
-
-    for (typename PointListType::const_iterator i = points.begin();
-         i != points.end(); ++i) {
-        m_rows.push_back(i->frame);
+    if (m_model->isColumnTimeValue(m_sortColumn)) {
+        if (m_sortOrdering == Qt::AscendingOrder) {
+            return row;
+        } else {
+            return rowCount() - row - 1;
+        }
     }
+//!!! need the reverse of this
+    if (m_sort.empty()) {
+        resort();
+    }
+    if (row < 0 || row >= m_sort.size()) return 0;
+    return m_sort[row];
 }
 
-bool
-ModelDataTableModel::canHandleModelType(Model *m)
+void
+ModelDataTableModel::resort()
 {
-    if (dynamic_cast<SparseOneDimensionalModel *>(m)) return true;
-    if (dynamic_cast<SparseTimeValueModel *>(m)) return true;
-    return false;
+    //...
 }
 
-
--- a/data/model/ModelDataTableModel.h	Wed Jun 11 12:54:18 2008 +0000
+++ b/data/model/ModelDataTableModel.h	Wed Jun 11 16:13:25 2008 +0000
@@ -18,8 +18,9 @@
 
 #include <QAbstractItemModel>
 
-#include "Model.h"
+#include <vector>
 
+class TabularModel;
 class Command;
 
 class ModelDataTableModel : public QAbstractItemModel
@@ -27,7 +28,7 @@
     Q_OBJECT
 
 public:
-    ModelDataTableModel(Model *m);
+    ModelDataTableModel(TabularModel *m);
     virtual ~ModelDataTableModel();
 
     QVariant data(const QModelIndex &index, int role) const;
@@ -50,7 +51,7 @@
     QModelIndex getModelIndexForFrame(size_t frame) const;
     size_t getFrameForModelIndex(const QModelIndex &) const;
 
-    static bool canHandleModelType(Model *);
+    void sort(int column, Qt::SortOrder order = Qt::AscendingOrder);
 
 signals:
     void frameSelected(size_t);
@@ -61,19 +62,14 @@
     void modelChanged(size_t, size_t);
 
 protected:
-    // We need to have some sort of map between row and time in sample
-    // frames.  I guess this will do for now.
-
-    std::vector<size_t> m_rows; // contains sample frame
-
-    Model *m_model;
-
-    void rebuildRowVector();
-    template <typename PointType> void rebuildRowVectorSparse();
-    template <typename PointType> QVariant dataSparse(int row, int col,
-                                                      bool withUnit) const;
-    template <typename PointType> bool setDataSparse(int row, int col,
-                                                     QVariant value);
+    TabularModel *m_model;
+    int m_sortColumn;
+    Qt::SortOrder m_sortOrdering;
+    typedef std::vector<int> RowList;
+    RowList m_sort;
+    int getSorted(int row);
+    int getUnsorted(int row);
+    void resort();
 };
 
 #endif
--- a/data/model/SparseModel.h	Wed Jun 11 12:54:18 2008 +0000
+++ b/data/model/SparseModel.h	Wed Jun 11 16:13:25 2008 +0000
@@ -17,22 +17,26 @@
 #define _SPARSE_MODEL_H_
 
 #include "Model.h"
+#include "TabularModel.h"
 #include "base/Command.h"
 
 #include <iostream>
 
 #include <set>
+#include <vector>
+#include <algorithm>
+
 #include <QMutex>
 #include <QTextStream>
 
-
 /**
  * Model containing sparse data (points with some properties).  The
  * properties depend on the point type.
  */
 
 template <typename PointType>
-class SparseModel : public Model
+class SparseModel : public Model,
+                    public TabularModel
 {
 public:
     SparseModel(size_t sampleRate, size_t resolution,
@@ -258,8 +262,38 @@
 	PointType m_newPoint;
     };
 
+    /**
+     * TabularModel methods.  
+     */
+
+    virtual int getRowCount() const
+    {
+        return m_points.size();
+    }
+
+    virtual long getFrameForRow(int row) const
+    {
+        PointListIterator i = getPointListIteratorForRow(row);
+        if (i == m_points.end()) return 0;
+        return i->frame;
+    }
+
+    virtual int getRowForFrame(long frame) const
+    {
+        if (m_rows.empty()) rebuildRowVector();
+        std::vector<long>::iterator i =
+            std::lower_bound(m_rows.begin(), m_rows.end(), frame);
+        return std::distance(m_rows.begin(), i);
+    }
+
+    //!!! just for now
+    virtual int getColumnCount() const { return 1; }
+    virtual QString getHeading(int column) const { return tr("Unknown"); }
+    virtual QVariant getData(int row, int column, int role) const {
+        return QVariant();
+    }
+    virtual bool isColumnTimeValue(int column) const { return true; }
     
-
 protected:
     size_t m_sampleRate;
     size_t m_resolution;
@@ -272,6 +306,47 @@
     size_t m_pointCount;
     mutable QMutex m_mutex;
     int m_completion;
+
+    void getPointIterators(long frame,
+                           PointListIterator &startItr,
+                           PointListIterator &endItr) const;
+
+    // This is only used if the model is called on to act in
+    // TabularModel mode
+    mutable std::vector<long> m_rows; // map from row number to frame
+    void rebuildRowVector() const
+    {
+        m_rows.clear();
+        for (PointListIterator i = m_points.begin(); i != m_points.end(); ++i) {
+            m_rows.push_back(i->frame);
+        }
+    }
+
+    PointListIterator getPointListIteratorForRow(int row) const
+    {
+        if (m_rows.empty()) rebuildRowVector();
+        if (row < 0 || row + 1 > m_rows.size()) return m_points.end();
+
+        size_t frame = m_rows[row];
+        int indexAtFrame = 0;
+        int ri = row;
+        while (ri > 0 && m_rows[ri-1] == m_rows[row]) { --ri; ++indexAtFrame; }
+        int initialIndexAtFrame = indexAtFrame;
+
+        PointListIterator i0, i1;
+        getPointIterators(frame, i0, i1);
+        PointListIterator i = i0;
+
+        for (i = i0; i != i1; ++i) {
+            if (indexAtFrame > 0) { --indexAtFrame; continue; }
+            return i;
+        }
+
+        if (indexAtFrame > 0) {
+            std::cerr << "WARNING: SparseModel::getPointListIteratorForRow: No iterator available for row " << row << " (frame = " << frame << ", index at frame = " << initialIndexAtFrame << ", leftover index " << indexAtFrame << ")" << std::endl;
+        }
+        return i;
+    }
 };
 
 
@@ -370,17 +445,8 @@
 typename SparseModel<PointType>::PointList
 SparseModel<PointType>::getPoints(long frame) const
 {
-    QMutexLocker locker(&m_mutex);
-
-    if (m_resolution == 0) return PointList();
-
-    long start = (frame / m_resolution) * m_resolution;
-    long end = start + m_resolution;
-
-    PointType startPoint(start), endPoint(end);
-    
-    PointListIterator startItr = m_points.lower_bound(startPoint);
-    PointListIterator   endItr = m_points.upper_bound(endPoint);
+    PointListIterator startItr, endItr;
+    getPointIterators(frame, startItr, endItr);
 
     PointList rv;
 
@@ -392,6 +458,29 @@
 }
 
 template <typename PointType>
+void
+SparseModel<PointType>::getPointIterators(long frame,
+                                          PointListIterator &startItr,
+                                          PointListIterator &endItr) const
+{
+    QMutexLocker locker(&m_mutex);
+
+    if (m_resolution == 0) {
+        startItr = m_points.end();
+        endItr = m_points.end();
+        return;
+    }
+
+    long start = (frame / m_resolution) * m_resolution;
+    long end = start + m_resolution;
+
+    PointType startPoint(start), endPoint(end);
+    
+    startItr = m_points.lower_bound(startPoint);
+      endItr = m_points.upper_bound(endPoint);
+}
+
+template <typename PointType>
 typename SparseModel<PointType>::PointList
 SparseModel<PointType>::getPreviousPoints(long originFrame) const
 {
@@ -443,6 +532,7 @@
 	QMutexLocker locker(&m_mutex);
 	m_resolution = resolution;
     }
+    m_rows.clear();
     emit modelChanged();
 }
 
@@ -455,6 +545,7 @@
 	m_points.clear();
         m_pointCount = 0;
     }
+    m_rows.clear();
     emit modelChanged();
 }
 
@@ -476,6 +567,7 @@
     // alternative is to notify on setCompletion).
 
     if (m_notifyOnAdd) {
+        m_rows.clear(); //!!! inefficient
 	emit modelChanged(point.frame, point.frame + m_resolution);
     } else {
 	if (m_sinceLastNotifyMin == -1 ||
@@ -510,6 +602,7 @@
     }
 //    std::cout << "SparseOneDimensionalModel: emit modelChanged("
 //	      << point.frame << ")" << std::endl;
+    m_rows.clear(); //!!! inefficient
     emit modelChanged(point.frame, point.frame + m_resolution);
 }
 
@@ -529,6 +622,7 @@
             }
 
 	    m_notifyOnAdd = true; // henceforth
+            m_rows.clear(); //!!! inefficient
 	    emit modelChanged();
 
 	} else if (!m_notifyOnAdd) {
@@ -536,6 +630,7 @@
 	    if (update &&
                 m_sinceLastNotifyMin >= 0 &&
 		m_sinceLastNotifyMax >= 0) {
+                m_rows.clear(); //!!! inefficient
 		emit modelChanged(m_sinceLastNotifyMin, m_sinceLastNotifyMax);
 		m_sinceLastNotifyMin = m_sinceLastNotifyMax = -1;
 	    } else {
--- a/data/model/SparseOneDimensionalModel.h	Wed Jun 11 12:54:18 2008 +0000
+++ b/data/model/SparseOneDimensionalModel.h	Wed Jun 11 16:13:25 2008 +0000
@@ -95,7 +95,8 @@
         return "<plugin program=\"tap\"/>";
     }
 
-    int getIndexOf(const Point &point) {
+    int getIndexOf(const Point &point)
+    {
 	// slow
 	int i = 0;
 	Point::Comparator comparator;
@@ -107,6 +108,47 @@
     }
 
     QString getTypeName() const { return tr("Sparse 1-D"); }
+
+    /**
+     * TabularModel methods.  
+     */
+    
+    virtual int getColumnCount() const
+    {
+        return 3;
+    }
+
+    virtual QString getHeading(int column) const
+    {
+        switch (column) {
+        case 0: return tr("Time");
+        case 1: return tr("Frame");
+        case 2: return tr("Label");
+        default: return tr("Unknown");
+        }
+    }
+
+    virtual QVariant getData(int row, int column, int role) const
+    {
+        if (role != Qt::EditRole && role != Qt::DisplayRole) return QVariant();
+        PointListIterator i = getPointListIteratorForRow(row);
+        if (i == m_points.end()) return QVariant();
+
+        switch (column) {
+        case 0: {
+            RealTime rt = RealTime::frame2RealTime(i->frame, getSampleRate());
+            return QVariant(rt.toText().c_str());
+        }
+        case 1: return QVariant(int(i->frame));
+        case 2: return QVariant(i->label);
+        default: return QVariant();
+        }
+    }
+
+    virtual bool isColumnTimeValue(int column) const
+    {
+        return (column < 2); 
+    }
 };
 
 #endif
--- a/data/model/SparseTimeValueModel.h	Wed Jun 11 12:54:18 2008 +0000
+++ b/data/model/SparseTimeValueModel.h	Wed Jun 11 16:13:25 2008 +0000
@@ -97,6 +97,74 @@
     }
 
     QString getTypeName() const { return tr("Sparse Time-Value"); }
+
+    /**
+     * TabularModel methods.  
+     */
+    
+    virtual int getColumnCount() const
+    {
+        return 4;
+    }
+
+    virtual QString getHeading(int column) const
+    {
+        switch (column) {
+        case 0: return tr("Time");
+        case 1: return tr("Frame");
+        case 2: return tr("Value");
+        case 3: return tr("Label");
+        default: return tr("Unknown");
+        }
+    }
+
+    virtual QVariant getData(int row, int column, int role) const
+    {
+        if (role != Qt::EditRole && role != Qt::DisplayRole) return QVariant();
+        PointListIterator i = getPointListIteratorForRow(row);
+        if (i == m_points.end()) return QVariant();
+
+        switch (column) {
+        case 0: {
+            RealTime rt = RealTime::frame2RealTime(i->frame, getSampleRate());
+            return rt.toText().c_str();
+        }
+        case 1: return int(i->frame);
+        case 2:
+            if (role == Qt::EditRole) return i->value;
+            else return QString("%1 %2").arg(i->value).arg(getScaleUnits());
+        case 3: return i->label;
+        default: return QVariant();
+        }
+    }
+
+    virtual Command *setData(int row, int column, QVariant value, int role) 
+    {
+        if (role != Qt::EditRole) return false;
+        PointListIterator i = getPointListIteratorForRow(row);
+        if (i == m_points.end()) return false;
+        EditCommand *command = new EditCommand(this, tr("Edit Data"));
+
+        Point point(*i);
+        command->deletePoint(point);
+
+        switch (column) {
+        case 0: break; 
+        case 1: break;
+        case 2: point.value = value.toDouble();
+            std::cerr << "setting value of point at " << point.frame << " to " << point.value << std::endl;
+            break;
+        case 3: point.label = value.toString(); break;
+        }
+
+        command->addPoint(point);
+        return command->finish();
+    }
+
+    virtual bool isColumnTimeValue(int column) const
+    {
+        return (column < 2); 
+    }
 };
 
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/data/model/TabularModel.h	Wed Jun 11 16:13:25 2008 +0000
@@ -0,0 +1,53 @@
+/* -*- 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 2008 QMUL.
+    
+    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 _TABULAR_MODEL_H_
+#define _TABULAR_MODEL_H_
+
+#include <QVariant>
+#include <QString>
+
+class Command;
+
+/**
+ * TabularModel is an abstract base class for models that support
+ * direct access to data in a tabular form.  A model that implements
+ * TabularModel may be displayed and, perhaps, edited in a data
+ * spreadsheet window.
+ *
+ * This is very like a cut-down QAbstractItemModel.  It assumes a
+ * relationship between row number and frame time.
+ */
+
+class TabularModel
+{
+public:
+    virtual int getRowCount() const = 0;
+    virtual int getColumnCount() const = 0;
+
+    virtual QString getHeading(int column) const = 0;
+    virtual QVariant getData(int row, int column, int role) const = 0;
+
+    virtual long getFrameForRow(int row) const = 0;
+    virtual int getRowForFrame(long frame) const = 0;
+
+    virtual bool isColumnTimeValue(int col) const = 0;
+
+    virtual bool isEditable() const { return false; }
+    virtual Command *setData(int row, int column, const QVariant &, int role)
+    { return 0; }
+};
+
+#endif