Chris@437: /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */
Chris@437: 
Chris@437: /*
Chris@437:     Sonic Visualiser
Chris@437:     An audio file viewer and annotation editor.
Chris@437:     Centre for Digital Music, Queen Mary, University of London.
Chris@437:     This file copyright 2006-2008 Chris Cannam and QMUL.
Chris@437:     
Chris@437:     This program is free software; you can redistribute it and/or
Chris@437:     modify it under the terms of the GNU General Public License as
Chris@437:     published by the Free Software Foundation; either version 2 of the
Chris@437:     License, or (at your option) any later version.  See the file
Chris@437:     COPYING included with this distribution for more information.
Chris@437: */
Chris@437: 
Chris@437: #ifndef _INTERVAL_MODEL_H_
Chris@437: #define _INTERVAL_MODEL_H_
Chris@437: 
Chris@437: #include "SparseValueModel.h"
Chris@437: #include "base/RealTime.h"
Chris@437: 
Chris@437: /**
Chris@437:  * Model containing sparse data (points with some properties) of which
Chris@437:  * the properties include a duration and an arbitrary float value.
Chris@437:  * The other properties depend on the point type.
Chris@437:  */
Chris@437: 
Chris@437: template <typename PointType>
Chris@437: class IntervalModel : public SparseValueModel<PointType>
Chris@437: {
Chris@437: public:
Chris@1040:     IntervalModel(sv_samplerate_t sampleRate, int resolution,
Chris@437:                   bool notifyOnAdd = true) :
Chris@437: 	SparseValueModel<PointType>(sampleRate, resolution, notifyOnAdd)
Chris@437:     { }
Chris@437: 
Chris@1040:     IntervalModel(sv_samplerate_t sampleRate, int resolution,
Chris@437:                   float valueMinimum, float valueMaximum,
Chris@437:                   bool notifyOnAdd = true) :
Chris@437: 	SparseValueModel<PointType>(sampleRate, resolution,
Chris@437:                                     valueMinimum, valueMaximum,
Chris@437:                                     notifyOnAdd)
Chris@437:     { }
Chris@437: 
Chris@437:     /**
Chris@437:      * PointTypes have a duration, so this returns all points that span any
Chris@437:      * of the given range (as well as the usual additional few before
Chris@437:      * and after).  Consequently this can be very slow (optimised data
Chris@437:      * structures still to be done!).
Chris@437:      */
Chris@1055:     virtual typename SparseValueModel<PointType>::PointList getPoints(sv_frame_t start, sv_frame_t end) const;
Chris@437: 
Chris@437:     /**
Chris@437:      * PointTypes have a duration, so this returns all points that span the
Chris@437:      * given frame.  Consequently this can be very slow (optimised
Chris@437:      * data structures still to be done!).
Chris@437:      */
Chris@1038:     virtual typename SparseValueModel<PointType>::PointList getPoints(sv_frame_t frame) const;
Chris@437: 
Chris@459:     virtual const typename SparseModel<PointType>::PointList &getPoints() const {
Chris@459:         return SparseModel<PointType>::getPoints(); 
Chris@459:     }
Chris@459: 
Chris@437:     /**
Chris@437:      * TabularModel methods.  
Chris@437:      */
Chris@437: 
Chris@437:     virtual QVariant getData(int row, int column, int role) const
Chris@437:     {
Chris@437:         if (column < 2) {
Chris@437:             return SparseValueModel<PointType>::getData
Chris@437:                 (row, column, role);
Chris@437:         }
Chris@437: 
Chris@437:         typename SparseModel<PointType>::PointList::const_iterator i
Chris@437:             = SparseModel<PointType>::getPointListIteratorForRow(row);
Chris@437:         if (i == SparseModel<PointType>::m_points.end()) return QVariant();
Chris@437: 
Chris@437:         switch (column) {
Chris@437:         case 2:
Chris@437:             if (role == Qt::EditRole || role == TabularModel::SortRole) return i->value;
Chris@437:             else return QString("%1 %2").arg(i->value).arg
Chris@437:                      (IntervalModel<PointType>::getScaleUnits());
Chris@437:         case 3: return int(i->duration); //!!! could be better presented
Chris@437:         default: return QVariant();
Chris@437:         }
Chris@437:     }
Chris@437: 
Chris@437:     virtual Command *getSetDataCommand(int row, int column, const QVariant &value, int role)
Chris@437:     {
Chris@438:         typedef IntervalModel<PointType> I;
Chris@438: 
Chris@437:         if (column < 2) {
Chris@437:             return SparseValueModel<PointType>::getSetDataCommand
Chris@437:                 (row, column, value, role);
Chris@437:         }
Chris@437: 
Chris@740:         if (role != Qt::EditRole) return 0;
Chris@438:         typename I::PointList::const_iterator i
Chris@438:             = I::getPointListIteratorForRow(row);
Chris@740:         if (i == I::m_points.end()) return 0;
Chris@438:         typename I::EditCommand *command = new typename I::EditCommand
Chris@438:             (this, I::tr("Edit Data"));
Chris@437: 
Chris@437:         PointType point(*i);
Chris@437:         command->deletePoint(point);
Chris@437: 
Chris@437:         switch (column) {
Chris@970:         // column cannot be 0 or 1, those cases were handled above
Chris@1038:         case 2: point.value = float(value.toDouble()); break;
Chris@437:         case 3: point.duration = value.toInt(); break;
Chris@437:         }
Chris@437: 
Chris@437:         command->addPoint(point);
Chris@437:         return command->finish();
Chris@437:     }
Chris@437: 
Chris@437:     virtual bool isColumnTimeValue(int column) const
Chris@437:     {
Chris@618:         // NB duration is not a "time value" -- that's for columns
Chris@618:         // whose sort ordering is exactly that of the frame time
Chris@618:         return (column < 2);
Chris@437:     }
Chris@437: };
Chris@437: 
Chris@437: template <typename PointType>
Chris@437: typename SparseValueModel<PointType>::PointList
Chris@1038: IntervalModel<PointType>::getPoints(sv_frame_t start, sv_frame_t end) const
Chris@437: {
Chris@437:     typedef IntervalModel<PointType> I;
Chris@437: 
Chris@437:     if (start > end) return typename I::PointList();
Chris@437: 
Chris@437:     QMutex &mutex(I::m_mutex);
Chris@437:     QMutexLocker locker(&mutex);
Chris@437: 
Chris@437:     PointType endPoint(end);
Chris@437:     
Chris@608:     typename I::PointListConstIterator endItr = I::m_points.upper_bound(endPoint);
Chris@437: 
Chris@437:     if (endItr != I::m_points.end()) ++endItr;
Chris@437:     if (endItr != I::m_points.end()) ++endItr;
Chris@437: 
Chris@437:     typename I::PointList rv;
Chris@437: 
Chris@608:     for (typename I::PointListConstIterator i = endItr; i != I::m_points.begin(); ) {
Chris@437:         --i;
Chris@437:         if (i->frame < start) {
Chris@1038:             if (i->frame + i->duration >= start) {
Chris@437:                 rv.insert(*i);
Chris@437:             }
Chris@437:         } else if (i->frame <= end) {
Chris@437:             rv.insert(*i);
Chris@437:         }
Chris@437:     }
Chris@437: 
Chris@437:     return rv;
Chris@437: }
Chris@437: 
Chris@437: template <typename PointType>
Chris@437: typename SparseValueModel<PointType>::PointList
Chris@1038: IntervalModel<PointType>::getPoints(sv_frame_t frame) const
Chris@437: {
Chris@437:     typedef IntervalModel<PointType> I;
Chris@437: 
Chris@437:     QMutex &mutex(I::m_mutex);
Chris@437:     QMutexLocker locker(&mutex);
Chris@437: 
Chris@437:     if (I::m_resolution == 0) return typename I::PointList();
Chris@437: 
Chris@1038:     sv_frame_t start = (frame / I::m_resolution) * I::m_resolution;
Chris@1038:     sv_frame_t end = start + I::m_resolution;
Chris@437: 
Chris@437:     PointType endPoint(end);
Chris@437:     
Chris@608:     typename I::PointListConstIterator endItr = I::m_points.upper_bound(endPoint);
Chris@437: 
Chris@437:     typename I::PointList rv;
Chris@437: 
Chris@608:     for (typename I::PointListConstIterator i = endItr; i != I::m_points.begin(); ) {
Chris@437:         --i;
Chris@437:         if (i->frame < start) {
Chris@1038:             if (i->frame + i->duration >= start) {
Chris@437:                 rv.insert(*i);
Chris@437:             }
Chris@437:         } else if (i->frame <= end) {
Chris@437:             rv.insert(*i);
Chris@437:         }
Chris@437:     }
Chris@437: 
Chris@437:     return rv;
Chris@437: }
Chris@437: 
Chris@437: #endif