Chris@302: /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */
Chris@302: 
Chris@302: /*
Chris@302:     Sonic Visualiser
Chris@302:     An audio file viewer and annotation editor.
Chris@302:     Centre for Digital Music, Queen Mary, University of London.
Chris@302:     This file copyright 2006-2007 Chris Cannam and QMUL.
Chris@302:     
Chris@302:     This program is free software; you can redistribute it and/or
Chris@302:     modify it under the terms of the GNU General Public License as
Chris@302:     published by the Free Software Foundation; either version 2 of the
Chris@302:     License, or (at your option) any later version.  See the file
Chris@302:     COPYING included with this distribution for more information.
Chris@302: */
Chris@302: 
Chris@302: #ifndef _IMAGE_MODEL_H_
Chris@302: #define _IMAGE_MODEL_H_
Chris@302: 
Chris@302: #include "SparseModel.h"
Chris@302: #include "base/XmlExportable.h"
Chris@302: #include "base/RealTime.h"
Chris@302: 
Chris@423: #include <QStringList>
Chris@423: 
Chris@302: /**
Chris@302:  * Image point type for use in a SparseModel.  This represents an
Chris@302:  * image, identified by filename, at a given time.  The filename can
Chris@302:  * be empty, in which case we instead have a space to put an image in.
Chris@302:  */
Chris@302: 
Chris@302: struct ImagePoint : public XmlExportable
Chris@302: {
Chris@302: public:
Chris@1110:     ImagePoint(sv_frame_t _frame) : frame(_frame) { }
Chris@1110:     ImagePoint(sv_frame_t _frame, QString _image, QString _label) :
Chris@302:         frame(_frame), image(_image), label(_label) { }
Chris@302: 
Chris@302:     int getDimensions() const { return 1; }
Chris@302:     
Chris@1110:     sv_frame_t frame;
Chris@302:     QString image;
Chris@302:     QString label;
Chris@338: 
Chris@338:     QString getLabel() const { return label; }
Chris@302:     
Chris@314:     void toXml(QTextStream &stream,
Chris@314:                QString indent = "",
Chris@314:                QString extraAttributes = "") const
Chris@302:     {
Chris@314: 	stream <<
Chris@314:             QString("%1<point frame=\"%2\" image=\"%3\" label=\"%4\" %5/>\n")
Chris@302: 	    .arg(indent).arg(frame)
Chris@302:             .arg(encodeEntities(image))
Chris@302:             .arg(encodeEntities(label))
Chris@302:             .arg(extraAttributes);
Chris@302:     }
Chris@302: 
Chris@1060:     QString toDelimitedDataString(QString delimiter, DataExportOptions, sv_samplerate_t sampleRate) const
Chris@302:     {
Chris@302:         QStringList list;
Chris@302:         list << RealTime::frame2RealTime(frame, sampleRate).toString().c_str();
Chris@302:         list << image;
Chris@318:         if (label != "") list << label;
Chris@302:         return list.join(delimiter);
Chris@302:     }
Chris@302: 
Chris@302:     struct Comparator {
Chris@302: 	bool operator()(const ImagePoint &p1,
Chris@302: 			const ImagePoint &p2) const {
Chris@302: 	    if (p1.frame != p2.frame) return p1.frame < p2.frame;
Chris@302:             if (p1.label != p2.label) return p1.label < p2.label;
Chris@302: 	    return p1.image < p2.image;
Chris@302: 	}
Chris@302:     };
Chris@302:     
Chris@302:     struct OrderComparator {
Chris@302: 	bool operator()(const ImagePoint &p1,
Chris@302: 			const ImagePoint &p2) const {
Chris@302: 	    return p1.frame < p2.frame;
Chris@302: 	}
Chris@302:     };
Chris@302: };
Chris@302: 
Chris@302: 
Chris@302: // Make this a class rather than a typedef so it can be predeclared.
Chris@302: 
Chris@302: class ImageModel : public SparseModel<ImagePoint>
Chris@302: {
Chris@423:     Q_OBJECT
Chris@423: 
Chris@302: public:
Chris@1040:     ImageModel(sv_samplerate_t sampleRate, int resolution, bool notifyOnAdd = true) :
Chris@302: 	SparseModel<ImagePoint>(sampleRate, resolution, notifyOnAdd)
Chris@302:     { }
Chris@302: 
Chris@345:     QString getTypeName() const { return tr("Image"); }
Chris@345: 
Chris@302:     virtual void toXml(QTextStream &out,
Chris@302:                        QString indent = "",
Chris@302:                        QString extraAttributes = "") const
Chris@302:     {
Chris@302:         SparseModel<ImagePoint>::toXml
Chris@302: 	    (out, 
Chris@302:              indent,
Chris@302: 	     QString("%1 subtype=\"image\"")
Chris@302: 	     .arg(extraAttributes));
Chris@302:     }
Chris@302: 
Chris@302:     /**
Chris@302:      * Command to change the image for a point.
Chris@302:      */
Chris@302:     class ChangeImageCommand : public Command
Chris@302:     {
Chris@302:     public:
Chris@302:         ChangeImageCommand(ImageModel *model,
Chris@302:                            const ImagePoint &point,
Chris@302:                            QString newImage,
Chris@302:                            QString newLabel) :
Chris@302: 	    m_model(model), m_oldPoint(point), m_newPoint(point) {
Chris@302: 	    m_newPoint.image = newImage;
Chris@302:             m_newPoint.label = newLabel;
Chris@302: 	}
Chris@302: 
Chris@302: 	virtual QString getName() const { return tr("Edit Image"); }
Chris@302: 
Chris@302: 	virtual void execute() { 
Chris@302: 	    m_model->deletePoint(m_oldPoint);
Chris@302: 	    m_model->addPoint(m_newPoint);
Chris@302: 	    std::swap(m_oldPoint, m_newPoint);
Chris@302: 	}
Chris@302: 
Chris@302: 	virtual void unexecute() { execute(); }
Chris@302: 
Chris@302:     private:
Chris@302: 	ImageModel *m_model;
Chris@302: 	ImagePoint m_oldPoint;
Chris@302: 	ImagePoint m_newPoint;
Chris@302:     };
Chris@424: 
Chris@424:     /**
Chris@424:      * TabularModel methods.  
Chris@424:      */
Chris@424:     
Chris@424:     virtual int getColumnCount() const
Chris@424:     {
Chris@424:         return 4;
Chris@424:     }
Chris@424: 
Chris@424:     virtual QString getHeading(int column) const
Chris@424:     {
Chris@424:         switch (column) {
Chris@424:         case 0: return tr("Time");
Chris@424:         case 1: return tr("Frame");
Chris@424:         case 2: return tr("Image");
Chris@424:         case 3: return tr("Label");
Chris@424:         default: return tr("Unknown");
Chris@424:         }
Chris@424:     }
Chris@424: 
Chris@424:     virtual QVariant getData(int row, int column, int role) const
Chris@424:     {
Chris@425:         if (column < 2) {
Chris@425:             return SparseModel<ImagePoint>::getData
Chris@425:                 (row, column, role);
Chris@425:         }
Chris@425: 
Chris@608:         PointListConstIterator i = getPointListIteratorForRow(row);
Chris@424:         if (i == m_points.end()) return QVariant();
Chris@424: 
Chris@424:         switch (column) {
Chris@424:         case 2: return i->image;
Chris@424:         case 3: return i->label;
Chris@424:         default: return QVariant();
Chris@424:         }
Chris@424:     }
Chris@424: 
Chris@424:     virtual Command *getSetDataCommand(int row, int column, const QVariant &value, int role)
Chris@424:     {
Chris@425:         if (column < 2) {
Chris@425:             return SparseModel<ImagePoint>::getSetDataCommand
Chris@425:                 (row, column, value, role);
Chris@425:         }
Chris@425: 
Chris@740:         if (role != Qt::EditRole) return 0;
Chris@424:         PointListIterator i = getPointListIteratorForRow(row);
Chris@740:         if (i == m_points.end()) return 0;
Chris@424:         EditCommand *command = new EditCommand(this, tr("Edit Data"));
Chris@424: 
Chris@424:         Point point(*i);
Chris@424:         command->deletePoint(point);
Chris@424: 
Chris@424:         switch (column) {
Chris@424:         case 2: point.image = value.toString(); break;
Chris@424:         case 3: point.label = value.toString(); break;
Chris@424:         }
Chris@424: 
Chris@424:         command->addPoint(point);
Chris@424:         return command->finish();
Chris@424:     }
Chris@424: 
Chris@424:     virtual bool isColumnTimeValue(int column) const
Chris@424:     {
Chris@424:         return (column < 2); 
Chris@424:     }
Chris@424: 
Chris@424:     virtual SortType getSortType(int column) const
Chris@424:     {
Chris@424:         if (column > 2) return SortAlphabetical;
Chris@424:         return SortNumeric;
Chris@424:     }
Chris@302: };
Chris@302: 
Chris@302: 
Chris@302: #endif
Chris@302: 
Chris@302: 
Chris@302: