view common/Objects.h @ 53:bcea875d8d2f tip

More build fixes
author Chris Cannam
date Thu, 16 Oct 2014 19:03:51 +0100
parents 5f23d5b29aaf
children
line wrap: on
line source
/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */

#ifndef _CLASSICAL_DATA_OBJECTS_H_
#define _CLASSICAL_DATA_OBJECTS_H_

#include <dataquay/Uri.h>
#include <dataquay/Node.h>

#include <QObject>
#include <QMetaType>
#include <QString>
#include <QStringList>
#include <QSharedPointer>
#include <QSet>
#include <QMutex>
#include <QMutexLocker>
#include <QMap>

#include <iostream>

#include "data/fileio/FileSource.h"

namespace ClassicalData {

//!!! need to review ownership more carefully -- and use deletion notify signals?

class Year
{
public:
    Year() : m_year(0) { }
    Year(int y) : m_year(y) { }

    int toInt() const { return m_year; }
    operator int() { return m_year; }

    struct Encoder : public Dataquay::Node::VariantEncoder {
        QString fromVariant(const QVariant &v) {
            int y = v.value<Year>().toInt();
            if (y == 0) return "";
            QString s = QString("%1").arg(y);
            return s;
        }
        QVariant toVariant(const QString &s) {
            if (s == "") return QVariant::fromValue<Year>(Year());
            QVariant v = QVariant::fromValue<Year>(s.toInt());
            return v;
        }
    };

private:
    int m_year;
};

class HistoricalEvent : public QObject
{
    Q_OBJECT

    Q_PROPERTY(ClassicalData::Year year READ year WRITE setYear NOTIFY yearChanged STORED true)
    Q_PROPERTY(QString place READ place WRITE setPlace NOTIFY placeChanged STORED true)
    Q_PROPERTY(bool approximate READ approximate WRITE setApproximate NOTIFY approximateChanged STORED true)

public:
    HistoricalEvent() : m_year(0), m_place(), m_approximate(false) { }
    HistoricalEvent(Year y) : m_year(y), m_approximate(false) { }
    HistoricalEvent(Year y, QString p) : m_year(y), m_place(p), m_approximate(false) { }
    HistoricalEvent(const HistoricalEvent &h) : QObject(), m_year(h.m_year), m_place(h.m_place), m_approximate(h.m_approximate) { }

    Year year() const { return m_year; }
    void setYear(Year y) { m_year = y; emit yearChanged(y); }
    QString place() const { return m_place; }
    void setPlace(QString p) { m_place = p; emit placeChanged(p); }
    bool approximate() const { return m_approximate; }
    void setApproximate(bool a) { m_approximate = a; emit approximateChanged(a); }
    
signals:
    void yearChanged(Year);
    void placeChanged(QString);
    void approximateChanged(bool);

private:
    Year m_year;
    QString m_place;
    bool m_approximate;
};

class Birth : public HistoricalEvent
{
    Q_OBJECT

public:
    Birth() : HistoricalEvent() { }
    Birth(Year y) : HistoricalEvent(y) { }
    Birth(Year y, QString p) : HistoricalEvent(y, p) { }
};

class Death : public HistoricalEvent
{
    Q_OBJECT

public:
    Death() : HistoricalEvent() { }
    Death(Year y) : HistoricalEvent(y) { }
    Death(Year y, QString p) : HistoricalEvent(y, p) { }
};

class Composer;
class Work;

class Composition : public HistoricalEvent
{
    Q_OBJECT
    
    Q_PROPERTY(ClassicalData::Composer *composer READ composer WRITE setComposer NOTIFY composerChanged STORED true)
    Q_PROPERTY(QSet<ClassicalData::Work *> works READ works WRITE setWorks NOTIFY worksChanged STORED true)
    Q_PROPERTY(QString composerName READ getComposerName WRITE setComposerName STORED false)

public:
    Composition() : HistoricalEvent(), m_composer(0) { }
    Composition(Year y) : HistoricalEvent(y), m_composer(0) { }
    Composition(Year y, QString p) : HistoricalEvent(y, p), m_composer(0) { }

    Composer *composer() { return m_composer; }
    void setComposer(Composer *c) { m_composer = c; emit composerChanged(c); }

    QSet<Work *> works() { return m_works; }
    void setWorks(QSet<Work *> c) { m_works = c; emit worksChanged(c); }
    void addWork(Work *w) { m_works.insert(w); emit worksChanged(m_works); }

    // Not a storable property; set temporarily while composer record
    // is found, or else looked up from composer after composer has
    // been found
    QString getComposerName() const;
    void setComposerName(QString n) { m_cname = n; }

signals:
    void composerChanged(Composer *);
    void worksChanged(QSet<Work *>);

private:
    Composer *m_composer;
    QSet<Work *> m_works;
    QString m_cname;
};

class Document : public QObject
{
    Q_OBJECT

    Q_PROPERTY(Dataquay::Uri uri READ uri WRITE setUri NOTIFY uriChanged STORED true)
    Q_PROPERTY(QString siteName READ siteName WRITE setSiteName NOTIFY siteNameChanged STORED true)
    Q_PROPERTY(QObject *topic READ topic WRITE setTopic NOTIFY topicChanged STORED true)

public:
    Document(QObject *parent = 0) : QObject(parent), m_topic(0) { }
    
    Dataquay::Uri uri() const { return m_uri; }
    void setUri(Dataquay::Uri uri) { m_uri = uri; emit uriChanged(uri); }

    QString siteName() const { return m_siteName; }
    void setSiteName(QString n) { m_siteName = n; emit siteNameChanged(n); }

    QObject *topic() const { return m_topic; } // I don't own this
    void setTopic(QObject *t) { m_topic = t; emit topicChanged(t); }

signals:
    void uriChanged(Dataquay::Uri);
    void siteNameChanged(QString);
    void topicChanged(QObject *);
    
private:
    Dataquay::Uri m_uri;
    QString m_siteName;
    QObject *m_topic;
};


class NamedEntity : public QObject
{
    Q_OBJECT

    Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged STORED true)
    Q_PROPERTY(Dataquay::Uri uri READ uri WRITE setUri NOTIFY uriChanged STORED true)
    Q_PROPERTY(QSet<QString> aliases READ aliases WRITE setAliases NOTIFY aliasesChanged STORED true)
    Q_PROPERTY(QString remarks READ remarks WRITE setRemarks NOTIFY remarksChanged STORED true)
    Q_PROPERTY(QSet<ClassicalData::Document*> pages READ pages WRITE setPages NOTIFY pagesChanged STORED true)
    Q_PROPERTY(QSet<Dataquay::Uri> otherURIs READ otherURIs WRITE setOtherURIs NOTIFY otherURIsChanged STORED true)

public:
    NamedEntity(QObject *parent = 0) : QObject(parent) { }
    ~NamedEntity() { }

    QString name() const { return m_name; }
    virtual void setName(QString n) { m_name = n; emit nameChanged(n); }
    
    Dataquay::Uri uri() const { return m_uri; }
    void setUri(Dataquay::Uri uri) { m_uri = uri; emit uriChanged(uri); }

    QString remarks() const { return m_remarks; }
    void setRemarks(QString n) { m_remarks = n; emit remarksChanged(n); }
    
    QSet<QString> aliases() const { return m_aliases; }
    virtual void setAliases(QSet<QString> l) { m_aliases = l; emit aliasesChanged(l); }
    virtual void addAlias(QString a) { m_aliases.insert(a); emit aliasesChanged(m_aliases); }

    QSet<Document *> pages() const { return m_pages; }
    void addPage(Document *p) { m_pages.insert(p); emit pagesChanged(m_pages); }
    void setPages(QSet<Document *> p) {
        m_pages = p;
        emit pagesChanged(p);
    }

    QSet<Dataquay::Uri> otherURIs() const { return m_otherURIs; }
    void addOtherURI(Dataquay::Uri u) { m_otherURIs.insert(u); emit otherURIsChanged(m_otherURIs); }
    void setOtherURIs(QSet<Dataquay::Uri> u) { m_otherURIs = u; emit otherURIsChanged(u); }

signals:
    void uriChanged(Dataquay::Uri);
    void nameChanged(QString);
    void remarksChanged(QString);
    void aliasesChanged(QSet<QString>);
    void pagesChanged(QSet<Document *>);
    void otherURIsChanged(QSet<Dataquay::Uri>);

protected:
    Dataquay::Uri m_uri;
    QString m_name;
    QString m_remarks;
    QSet<QString> m_aliases;
    QSet<Document *> m_pages;
    QSet<Dataquay::Uri> m_otherURIs;
};

class Movement;

class Form;

class Work : public NamedEntity
{
    Q_OBJECT

    Q_PROPERTY(QString key READ key WRITE setKey NOTIFY keyChanged STORED true)
    Q_PROPERTY(QString opus READ opus WRITE setOpus NOTIFY opusChanged STORED true)
    Q_PROPERTY(QString catalogue READ catalogue WRITE setCatalogue NOTIFY catalogueChanged STORED true)
    Q_PROPERTY(QString number READ number WRITE setNumber NOTIFY numberChanged STORED true)
    Q_PROPERTY(QSet<ClassicalData::Form*> forms READ forms WRITE setForms NOTIFY formsChanged STORED true)
    Q_PROPERTY(ClassicalData::Work* partOf READ partOf WRITE setPartOf NOTIFY partOfChanged STORED true)
    Q_PROPERTY(QSet<ClassicalData::Work*> parts READ parts WRITE setParts NOTIFY partsChanged STORED true)
    Q_PROPERTY(QSet<ClassicalData::Movement*> movements READ movements WRITE setMovements NOTIFY movementsChanged STORED true)
    Q_PROPERTY(ClassicalData::Composition *composition READ composition WRITE setComposition NOTIFY compositionChanged STORED true)

public:
    Work(QObject *parent = 0) : NamedEntity(parent), m_partOf(0), m_composition(0) { }

    QString key() const { return m_key; }
    void setKey(QString n) { m_key = n; emit keyChanged(n); }

    /// Should omit Op prefix, e.g. in opus 102 no 2, this would be "102"
    QString opus() const { return m_opus; }
    void setOpus(QString n) { m_opus = n; emit opusChanged(n); }

    /// For part of a catalogue entry, e.g. in opus 102 no 2, this
    /// would be "2".  Should normally be used only with partOf
    QString number() const { return m_number; }
    void setNumber(QString n) { m_number = n; emit numberChanged(n); }

    /// Including catalogue prefix, e.g. "BWV 1066"; only for non-opus numbers
    QString catalogue() const { return m_catalogue; }
    void setCatalogue(QString n) { m_catalogue = n; emit catalogueChanged(n); }

    QSet<Form *> forms() const { return m_forms; }
    void setForms(QSet<Form *> f) { m_forms = f; emit formsChanged(f); }
    void addForm(Form *f) { m_forms.insert(f); emit formsChanged(m_forms); }

    Work *partOf() const { return m_partOf; }
    void setPartOf(Work *w) { m_partOf = w; emit partOfChanged(w); }

    QSet<Work *> parts() const { return m_parts; }
    void setParts(QSet<Work *> l) { m_parts = l; emit partsChanged(l); }
    void addPart(Work *w) { m_parts.insert(w); emit partsChanged(m_parts); }

    QSet<Movement *> movements() const { return m_movements; }
    void setMovements(QSet<Movement *> l) { m_movements = l; emit movementsChanged(l); }
    void addMovement(Movement *w) { m_movements.insert(w); emit movementsChanged(m_movements); }

    Composition *composition() { return m_composition; }
    const Composition *composition() const { return m_composition; }
    void setComposition(Composition *c) { m_composition = c; emit compositionChanged(c); }

    Composer *getComposer() const {
        if (m_composition) {
            return m_composition->composer();
        } else {
            return 0;
        }
    }

    QString getComposerName() const;

    struct Ordering {
        bool operator()(Work *, Work *);
    };

    /**
     * Compare the ordering of two strings that are known to contain
     * catalogue number texts, such as "Op. 1 no 4" and "Op. 3 no 2"
     * (which should compare in that order).  Return value is as for
     * strcmp.
     */
    //!!! todo: unit tests
    static int compareCatalogueNumberTexts(QString a, QString b);

    /**
     * Where data (possibly a title string, including opus number or
     * equivalent) appears to contain some text of a suitable form for
     * use with compareCatalogueNumberTexts, extract and return each
     * example.
     */
    static QStringList extractCatalogueNumberTexts(QString data);

    /// Return name composed from title and catalogue, e.g. Symphony no. 4, Op. 42
    QString getDisplayName() const;

signals:
    void keyChanged(QString);
    void opusChanged(QString);
    void catalogueChanged(QString);
    void numberChanged(QString);
    void formsChanged(QSet<Form *>);
    void partOfChanged(Work *);
    void partsChanged(QSet<Work *>);
    void movementsChanged(QSet<Movement *>);
    void compositionChanged(Composition *);    

private:
    QString m_key;
    QString m_opus;
    QString m_catalogue;
    QString m_number;
    QSet<Form *> m_forms;
    Work *m_partOf;
    QSet<Work *> m_parts;
    QSet<Movement *> m_movements;
    Composition *m_composition;
};

class Movement : public NamedEntity
{
    Q_OBJECT

    Q_PROPERTY(QString key READ key WRITE setKey NOTIFY keyChanged STORED true)
    Q_PROPERTY(QString number READ number WRITE setNumber NOTIFY numberChanged STORED true)
    Q_PROPERTY(ClassicalData::Work* partOf READ partOf WRITE setPartOf NOTIFY partOfChanged STORED true)
    Q_PROPERTY(QSet<ClassicalData::Movement*> parts READ parts WRITE setParts NOTIFY partsChanged STORED true) // movements can be nested
    Q_PROPERTY(ClassicalData::Composition *composition READ composition WRITE setComposition NOTIFY compositionChanged STORED true)

public:
    Movement(QObject *parent = 0) : NamedEntity(parent), m_partOf(0), m_composition(0) { }

    QString key() const { return m_key; }
    void setKey(QString n) { m_key = n; emit keyChanged(n); }

    QString number() const { return m_number; }
    void setNumber(QString n) { m_number = n; emit numberChanged(n); }

    Work *partOf() const { return m_partOf; }
    void setPartOf(Work *w) { m_partOf = w; emit partOfChanged(w); }

    QSet<Movement *> parts() const { return m_parts; }
    void setParts(QSet<Movement *> l) { m_parts = l; emit partsChanged(l); }
    void addPart(Movement *w) { m_parts.insert(w); emit partsChanged(m_parts); }

    Composition *composition() { return m_composition; }
    const Composition *composition() const { return m_composition; }
    void setComposition(Composition *c) { m_composition = c; emit compositionChanged(c); }

signals:
    void keyChanged(QString);
    void numberChanged(QString);
    void partOfChanged(Work *);
    void partsChanged(QSet<Movement *>);
    void compositionChanged(Composition *);

private:
    QString m_key;
    QString m_number;
    Work *m_partOf;
    QSet<Movement *> m_parts;
    Composition *m_composition;
};

class Composer : public NamedEntity
{
    Q_OBJECT

    Q_PROPERTY(QString gender READ gender WRITE setGender NOTIFY genderChanged STORED true)
    Q_PROPERTY(QSet<QString> nationality READ nationality WRITE setNationality NOTIFY nationalityChanged STORED true)
    Q_PROPERTY(QSet<Dataquay::Uri> geonameURIs READ geonameURIs WRITE setGeonameURIs NOTIFY geonameURIsChanged STORED true)
    Q_PROPERTY(QString period READ period WRITE setPeriod NOTIFY periodChanged STORED true)
    Q_PROPERTY(ClassicalData::Birth *birth READ birth WRITE setBirth NOTIFY birthChanged STORED true)
    Q_PROPERTY(ClassicalData::Death *death READ death WRITE setDeath NOTIFY deathChanged STORED true)

    Q_PROPERTY(QString surname READ getSurname STORED false)
    Q_PROPERTY(QString forenames READ getForenames STORED false)

public:
    Composer(QObject *parent = 0) :
        NamedEntity(parent), m_birth(0), m_death(0), m_namesCached(false) { }

    QString gender() const { return m_gender; }
    void setGender(QString n) { m_gender = n; emit genderChanged(n); }

    QSet<QString> nationality() const { return m_nationality; }
    void setNationality(QSet<QString> n) { m_nationality = n; emit nationalityChanged(n); }
    void addNationality(QString n) { m_nationality.insert(n); emit nationalityChanged(m_nationality); }

    QSet<Dataquay::Uri> geonameURIs() const { return m_geonameURIs; }
    void setGeonameURIs(QSet<Dataquay::Uri> n) { m_geonameURIs = n; emit geonameURIsChanged(n); }
    void addGeonameURI(Dataquay::Uri n) { m_geonameURIs.insert(n); emit geonameURIsChanged(m_geonameURIs); }

    QString period() const { return m_period; }
    void setPeriod(QString n) { m_period = n; emit periodChanged(n); }

    Birth *birth() { return m_birth; }
    const Birth *birth() const { return m_birth; }
    void setBirth(Birth *b) { m_birth = b; emit birthChanged(b); }

    Death *death() { return m_death; }
    const Death *death() const { return m_death; }
    void setDeath(Death *d) { m_death = d; emit deathChanged(d); }

    virtual void setName(QString n) {
        NamedEntity::setName(n);
        m_namesCached = false;
    }
    virtual void setAliases(QSet<QString> l) {
        NamedEntity::setAliases(l);
        m_namesCached = false;
    }
    virtual void addAlias(QString a) { 
        NamedEntity::addAlias(a);
        m_namesCached = false;
    }

    QString getSurname() const;
    QString getForenames() const;
    QString getSortName(bool caps) const;
    QString getDisplayDates() const;

    /**
     * Given another composer, return true if the other composer's
     * dates match outs.  This is mostly intended (like
     * matchCatalogueName) for use in merging distinct catalogues.
     * Matching is somewhat fuzzy; more slack is cut when the dates
     * are very long ago or are marked as approximate.
     */
    bool matchDates(const Composer *other) const; // "well enough"

    /**
     * Given another name which is intended to be a well-formatted
     * catalogue name for a composer (but which may differ in
     * ordering, number of forenames, and perhaps in spelling), test
     * whether the name is a plausible match for our own.  This is
     * mostly intended (like matchDates) for use in merging distinct
     * catalogues.  Return true if the given name is highly likely to
     * match our own.
     */
    bool matchCatalogueName(QString otherName) const;

    /**
     * Given another name which is believed to be a user-entered
     * composer name with unpredictable formatting and spelling (and
     * probably incomplete), return an estimate for the likelihood
     * that the intended composer was this one.  Higher return values
     * indicate greater confidence; a value of 1.0 or more indicates
     * that all of the input is at least close to perfectly matched.
     */
    float matchFuzzyName(QString name) const;

    /**
     * Given another name which is believed to be a user-entered
     * composer name with unpredictable formatting and spelling (and
     * probably incomplete), return an estimate for the likelihood
     * that the intended composer was this one.  Higher return values
     * indicate greater confidence; a value of 1.0 or more indicates
     * that all of the input is at least close to perfectly matched.
     * The supplied name should have been lower-cased and split on
     * non-alphabetical characters.
     */
    float matchFuzzyName(QStringList name) const;

    /**
     * Given a string that is in the process of being typed by the
     * user, return an estimate of the likelihood that the text is
     * intended to become this composer's name.  If quick is true, do
     * a quick(er) scan only and avoid very expensive tests.
     */
    float matchTyping(QString text) const;

    /**
     * Given a string that is in the process of being typed by the
     * user, return an estimate of the likelihood that the text is
     * intended to become this composer's name.  In contrast to
     * matchTyping, do a quick(er) scan only, avoiding more expensive
     * tests.
     */
    float matchTypingQuick(QString text) const;

    /**
     * Merge data from the given composer into this composer record.
     * That is, add the composer's name and aliases as aliases of this
     * composer, copy its dates where we lack them, etc.  In all
     * cases, values that exist in this composer already are preferred
     * over values from the "other" composer.
     */
    void mergeFrom(Composer *c);

    /**
     * Return the supplied name reduced into a "simplified" form,
     * eliminating many of the differences often found particularly in
     * European language names that have been anglicised.  Used in
     * catalogue and fuzzy name matching.
     */
    static QString reduceName(QString name);

signals:
    void genderChanged(QString);
    void nationalityChanged(QSet<QString>);
    void geonameURIsChanged(QSet<Dataquay::Uri>);
    void periodChanged(QString);
    void birthChanged(Birth *);
    void deathChanged(Death *);

private:
    QString m_gender;
    QSet<QString> m_nationality;
    QSet<Dataquay::Uri> m_geonameURIs;
    QString m_period;
    Birth *m_birth;
    Death *m_death;

    void cacheNames() const;
    float doMatchTyping(QString, bool) const;
    mutable bool m_namesCached;
    mutable QString m_surname;
    mutable QString m_forenames;
    mutable QStringList m_surnameElements;
    mutable QStringList m_connectiveElements;
    mutable QStringList m_forenameElements;
    mutable QStringList m_otherElements;
    mutable QStringList m_reducedSurnameElements;
    mutable QStringList m_reducedForenameElements;
};

class Form : public NamedEntity
{
    Q_OBJECT

    Q_PROPERTY(Dataquay::Uri uri READ uri)

public:
    Form(QObject *parent = 0) : NamedEntity(parent) { }

    static Form *getFormByName(QString name) {
        QMutexLocker locker(&m_mutex);
        if (!m_map.contains(name)) {
            Form *f = new Form();
            f->setName(name);
            m_map[name] = f;
        }
        return m_map[name];
    }

    Dataquay::Uri uri() const {
        return Dataquay::Uri
            (QString(":form_%1").arg(name()).toLower().replace(' ', '_'));
    }

private:
    static QMap<QString, Form *> m_map;
    static QMutex m_mutex;
};

class AudioFileTag : public QObject
{
    Q_OBJECT

    Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged STORED true)
    Q_PROPERTY(QString value READ value WRITE setValue NOTIFY valueChanged STORED true)

public:
    AudioFileTag(QObject *parent = 0) : QObject(parent) { }
    AudioFileTag(QString name, QString value, QObject *parent = 0) :
        QObject(parent), m_name(name), m_value(value) { }

    QString name() const { return m_name; }
    void setName(QString n) { m_name = n; emit nameChanged(m_name); }

    QString value() const { return m_value; }
    void setValue(QString v) { m_value = v; emit valueChanged(m_value); }
    
signals:
    void nameChanged(QString);
    void valueChanged(QString);

private:
    QString m_name;
    QString m_value;
};
    
// Separate AudioFile and Signal, to correspond with MO

class AudioFile : public QObject
{
    Q_OBJECT

    Q_PROPERTY(QString hash READ hash WRITE setHash NOTIFY hashChanged STORED true)
    Q_PROPERTY(Dataquay::Uri uri READ uri WRITE setUri NOTIFY uriChanged STORED true)
    Q_PROPERTY(QSet<ClassicalData::AudioFileTag *> tags READ tags WRITE setTags NOTIFY tagsChanged STORED true)

public:
    AudioFile(QObject *parent = 0);
    AudioFile(FileSource file, QObject *parent = 0);
    ~AudioFile();

    /// The URI is set automatically from the FileSource at construction
    Dataquay::Uri uri() const { return m_uri; }
    void setUri(Dataquay::Uri u) { m_uri = u; emit uriChanged(u); }

    /// The hash is set automatically from the FileSource at construction
    QString hash() const { return m_hash; }
    void setHash(QString hash) { m_hash = hash; emit hashChanged(hash); }

    /// I take ownership of all tags
    QSet<AudioFileTag *> tags() { return m_tags; }
    void setTags(QSet<AudioFileTag *> t);
    void addTag(AudioFileTag *t);

signals:
    void uriChanged(Dataquay::Uri);
    void hashChanged(QString);
    void tagsChanged(QSet<AudioFileTag *>);

private:
    Dataquay::Uri m_uri;
    QString m_hash;
    QSet<AudioFileTag *> m_tags;
};

class Signal : public QObject
{
    Q_OBJECT

    Q_PROPERTY(QString ofaFingerprint READ ofaFingerprint WRITE setOfaFingerprint NOTIFY ofaFingerprintChanged STORED true)
    Q_PROPERTY(QString puid READ puid WRITE setPuid NOTIFY puidChanged STORED true)
    Q_PROPERTY(QSet<ClassicalData::AudioFile *> availableAs READ availableAs WRITE setAvailableAs NOTIFY availableAsChanged STORED true)
    Q_PROPERTY(ClassicalData::Composer *composer READ composer WRITE setComposer NOTIFY composerChanged STORED true)
    Q_PROPERTY(ClassicalData::Work *work READ work WRITE setWork NOTIFY workChanged STORED true)
    Q_PROPERTY(ClassicalData::Movement *movement READ movement WRITE setMovement NOTIFY movementChanged STORED true)

public:
    Signal() : m_composer(0), m_work(0), m_movement(0) { }

    QString ofaFingerprint() const { return m_ofaFingerprint; }
    void setOfaFingerprint(QString fprint) { m_ofaFingerprint = fprint; emit ofaFingerprintChanged(fprint); }
    
    QString puid() const { return m_puid; }
    void setPuid(QString puid) { m_puid = puid; emit puidChanged(puid); }

    QSet<AudioFile *> availableAs() const { return m_availableAs; }
    void setAvailableAs(QSet<AudioFile *> aa) { m_availableAs = aa; emit availableAsChanged(m_availableAs); }
    void addAvailableAs(AudioFile *aa) { m_availableAs.insert(aa); emit availableAsChanged(m_availableAs); }

    Composer *composer() const { return m_composer; }
    void setComposer(Composer *c) { m_composer = c; emit composerChanged(c); }

    Work *work() const { return m_work; }
    void setWork(Work *w) { m_work = w; emit workChanged(w); }

    Movement *movement() const { return m_movement; }
    void setMovement(Movement *m) { m_movement = m; emit movementChanged(m); }

signals:
    void ofaFingerprintChanged(QString);
    void puidChanged(QString);
    void composerChanged(Composer *);
    void workChanged(Work *);
    void movementChanged(Movement *);
    void availableAsChanged(QSet<AudioFile *>);

private:
    QString m_ofaFingerprint;
    QString m_puid;
    Composer *m_composer;
    Work *m_work;
    Movement *m_movement;
    QSet<AudioFile *> m_availableAs;
};

}

Q_DECLARE_METATYPE(ClassicalData::Year);
Q_DECLARE_METATYPE(ClassicalData::HistoricalEvent*);
Q_DECLARE_METATYPE(ClassicalData::Birth*);
Q_DECLARE_METATYPE(ClassicalData::Death*);
Q_DECLARE_METATYPE(ClassicalData::Composition*);
Q_DECLARE_METATYPE(ClassicalData::Work*);
Q_DECLARE_METATYPE(ClassicalData::Movement*);
Q_DECLARE_METATYPE(ClassicalData::Document*);
Q_DECLARE_METATYPE(ClassicalData::AudioFileTag*);
Q_DECLARE_METATYPE(ClassicalData::AudioFile*);
Q_DECLARE_METATYPE(ClassicalData::Signal*);
Q_DECLARE_METATYPE(QSet<QString>);
Q_DECLARE_METATYPE(QSet<Dataquay::Uri>);
Q_DECLARE_METATYPE(QSet<ClassicalData::Work*>);
Q_DECLARE_METATYPE(QSet<ClassicalData::Movement*>);
Q_DECLARE_METATYPE(QSet<ClassicalData::Document*>);
Q_DECLARE_METATYPE(QSet<ClassicalData::AudioFileTag*>);
Q_DECLARE_METATYPE(QSet<ClassicalData::AudioFile*>);
Q_DECLARE_METATYPE(QSet<ClassicalData::Signal*>);
Q_DECLARE_METATYPE(ClassicalData::Composer*);
Q_DECLARE_METATYPE(ClassicalData::Form*);
Q_DECLARE_METATYPE(QSet<ClassicalData::Form*>);
Q_DECLARE_METATYPE(ClassicalData::NamedEntity*);
	
#endif