diff --git a/src/models.h b/src/models.h --- a/src/models.h +++ b/src/models.h @@ -33,6 +33,8 @@ class MapBaseQObject; class Sink; class Source; +class AbstractModelPrivate; +class SinkModelPrivate; class KF5PULSEAUDIOQT_EXPORT AbstractModel : public QAbstractListModel { @@ -42,7 +44,7 @@ PulseObjectRole = Qt::UserRole + 1 }; - ~AbstractModel() override; + virtual ~AbstractModel() override; QHash roleNames() const Q_DECL_FINAL; int rowCount(const QModelIndex &parent = QModelIndex()) const Q_DECL_FINAL; QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const Q_DECL_OVERRIDE; @@ -63,12 +65,8 @@ void onDataRemoved(int index); QMetaMethod propertyChangedMetaMethod() const; - const MapBaseQObject *m_map; - QHash m_roles; - QHash m_objectProperties; - QHash m_signalIndexToProperties; + AbstractModelPrivate *d; -private: // Prevent leaf-classes from default constructing as we want to enforce // them passing us a context or explicit nullptrs. AbstractModel() {} @@ -79,6 +77,8 @@ Q_OBJECT public: CardModel(QObject *parent = nullptr); +private: + void *d; }; class KF5PULSEAUDIOQT_EXPORT SinkModel : public AbstractModel @@ -93,6 +93,7 @@ Q_ENUMS(ItemRole) SinkModel(QObject *parent = nullptr); + virtual ~SinkModel(); Sink *defaultSink() const; Sink *preferredSink() const; QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const Q_DECL_OVERRIDE; @@ -106,15 +107,16 @@ void sinkRemoved(int index); void updatePreferredSink(); Sink *findPreferredSink() const; - - Sink *m_preferredSink; + SinkModelPrivate *d; }; class KF5PULSEAUDIOQT_EXPORT SinkInputModel : public AbstractModel { Q_OBJECT public: SinkInputModel(QObject *parent = nullptr); +private: + void *d; }; class KF5PULSEAUDIOQT_EXPORT SourceModel : public AbstractModel @@ -133,27 +135,36 @@ Q_SIGNALS: void defaultSourceChanged(); + +private: + void *d; }; class KF5PULSEAUDIOQT_EXPORT SourceOutputModel : public AbstractModel { Q_OBJECT public: SourceOutputModel(QObject *parent = nullptr); +private: + void *d; }; class KF5PULSEAUDIOQT_EXPORT StreamRestoreModel : public AbstractModel { Q_OBJECT public: StreamRestoreModel(QObject *parent = nullptr); +private: + void *d; }; class KF5PULSEAUDIOQT_EXPORT ModuleModel : public AbstractModel { Q_OBJECT public: ModuleModel(QObject *parent = nullptr); +private: + void *d; }; } // PulseAudioQt diff --git a/src/models.cpp b/src/models.cpp --- a/src/models.cpp +++ b/src/models.cpp @@ -35,27 +35,28 @@ #include "context_p.h" #include +#include "models_p.h" namespace PulseAudioQt { AbstractModel::AbstractModel(const MapBaseQObject *map, QObject *parent) : QAbstractListModel(parent) - , m_map(map) + , d(new AbstractModelPrivate(this, map)) { Context::instance()->ref(); - connect(m_map, &MapBaseQObject::aboutToBeAdded, this, [this](int index) { + connect(d->m_map, &MapBaseQObject::aboutToBeAdded, this, [this](int index) { beginInsertRows(QModelIndex(), index, index); }); - connect(m_map, &MapBaseQObject::added, this, [this](int index) { + connect(d->m_map, &MapBaseQObject::added, this, [this](int index) { onDataAdded(index); endInsertRows(); }); - connect(m_map, &MapBaseQObject::aboutToBeRemoved, this, [this](int index) { + connect(d->m_map, &MapBaseQObject::aboutToBeRemoved, this, [this](int index) { beginRemoveRows(QModelIndex(), index, index); }); - connect(m_map, &MapBaseQObject::removed, this, [this](int index) { + connect(d->m_map, &MapBaseQObject::removed, this, [this](int index) { Q_UNUSED(index); endRemoveRows(); }); @@ -66,13 +67,24 @@ //deref context after we've deleted this object //see https://bugs.kde.org/show_bug.cgi?id=371215 Context::instance()->unref(); + delete d; +} + +AbstractModelPrivate::AbstractModelPrivate(AbstractModel *q, const MapBaseQObject *map) + : q(q) + , m_map(map) +{ +} + +AbstractModelPrivate::~AbstractModelPrivate() +{ } QHash AbstractModel::roleNames() const { - if (!m_roles.empty()) { - qCDebug(PULSEAUDIOQT) << "returning roles" << m_roles; - return m_roles; + if (!d->m_roles.empty()) { + qCDebug(PULSEAUDIOQT) << "returning roles" << d->m_roles; + return d->m_roles; } Q_UNREACHABLE(); return QHash(); @@ -83,22 +95,22 @@ if (parent.isValid()) { return 0; } - return m_map->count(); + return d->m_map->count(); } QVariant AbstractModel::data(const QModelIndex &index, int role) const { if (!hasIndex(index.row(), index.column())) { return QVariant(); } - QObject *data = m_map->objectAt(index.row()); + QObject *data = d->m_map->objectAt(index.row()); Q_ASSERT(data); if (role == PulseObjectRole) { return QVariant::fromValue(data); } else if (role == Qt::DisplayRole) { return static_cast(data)->properties().value(QStringLiteral("name")).toString(); } - int property = m_objectProperties.value(role, -1); + int property = d->m_objectProperties.value(role, -1); if (property == -1) { return QVariant(); } @@ -110,19 +122,19 @@ if (!hasIndex(index.row(), index.column())) { return false; } - int propertyIndex = m_objectProperties.value(role, -1); + int propertyIndex = d->m_objectProperties.value(role, -1); if (propertyIndex == -1) { return false; } - QObject *data = m_map->objectAt(index.row()); + QObject *data = d->m_map->objectAt(index.row()); auto property = data->metaObject()->property(propertyIndex); return property.write(data, value); } int AbstractModel::role(const QByteArray &roleName) const { - qCDebug(PULSEAUDIOQT) << roleName << m_roles.key(roleName, -1); - return m_roles.key(roleName, -1); + qCDebug(PULSEAUDIOQT) << roleName << d->m_roles.key(roleName, -1); + return d->m_roles.key(roleName, -1); } Context *AbstractModel::context() const @@ -132,7 +144,7 @@ void AbstractModel::initRoleNames(const QMetaObject &qobjectMetaObject) { - m_roles[PulseObjectRole] = QByteArrayLiteral("PulseObject"); + d->m_roles[PulseObjectRole] = QByteArrayLiteral("PulseObject"); QMetaEnum enumerator; for (int i = 0; i < metaObject()->enumeratorCount(); ++i) { @@ -149,11 +161,11 @@ // Enum values must end in Role or the enum is crap Q_ASSERT(key.right(roleLength) == QByteArrayLiteral("Role")); key.chop(roleLength); - m_roles[enumerator.value(i)] = key; + d->m_roles[enumerator.value(i)] = key; } int maxEnumValue = -1; - for (auto it = m_roles.constBegin(); it != m_roles.constEnd(); ++it) { + for (auto it = d->m_roles.constBegin(); it != d->m_roles.constEnd(); ++it) { if (it.key() > maxEnumValue) { maxEnumValue = it.key(); } @@ -164,17 +176,17 @@ QMetaProperty property = mo.property(i); QString name(property.name()); name.replace(0, 1, name.at(0).toUpper()); - m_roles[++maxEnumValue] = name.toLatin1(); - m_objectProperties.insert(maxEnumValue, i); + d->m_roles[++maxEnumValue] = name.toLatin1(); + d->m_objectProperties.insert(maxEnumValue, i); if (!property.hasNotifySignal()) { continue; } - m_signalIndexToProperties.insert(property.notifySignalIndex(), i); + d->m_signalIndexToProperties.insert(property.notifySignalIndex(), i); } - qCDebug(PULSEAUDIOQT) << m_roles; + qCDebug(PULSEAUDIOQT) << d->m_roles; // Connect to property changes also with objects already in model - for (int i = 0; i < m_map->count(); ++i) { + for (int i = 0; i < d->m_map->count(); ++i) { onDataAdded(i); } } @@ -184,25 +196,25 @@ if (!sender() || senderSignalIndex() == -1) { return; } - int propertyIndex = m_signalIndexToProperties.value(senderSignalIndex(), -1); + int propertyIndex = d->m_signalIndexToProperties.value(senderSignalIndex(), -1); if (propertyIndex == -1) { return; } - int role = m_objectProperties.key(propertyIndex, -1); + int role = d->m_objectProperties.key(propertyIndex, -1); if (role == -1) { return; } - int index = m_map->indexOfObject(sender()); + int index = d->m_map->indexOfObject(sender()); qCDebug(PULSEAUDIOQT) << "PROPERTY CHANGED (" << index << ") :: " << role << roleNames().value(role); Q_EMIT dataChanged(createIndex(index, 0), createIndex(index, 0), {role}); } void AbstractModel::onDataAdded(int index) { - QObject *data = m_map->objectAt(index); + QObject *data = d->m_map->objectAt(index); const QMetaObject *mo = data->metaObject(); // We have all the data changed notify signals already stored - auto keys = m_signalIndexToProperties.keys(); + auto keys = d->m_signalIndexToProperties.keys(); foreach (int index, keys) { QMetaMethod meth = mo->method(index); connect(data, meth, this, propertyChangedMetaMethod()); @@ -221,7 +233,7 @@ SinkModel::SinkModel(QObject *parent) : AbstractModel(&context()->d->m_sinks, parent) - , m_preferredSink(nullptr) + , d(new SinkModelPrivate(this)) { initRoleNames(Sink::staticMetaObject); @@ -238,14 +250,31 @@ }); } +SinkModel::~SinkModel() +{ + delete d; +} + + +SinkModelPrivate::SinkModelPrivate(SinkModel *q) + : q(q) + , m_preferredSink(nullptr) +{ +} + +SinkModelPrivate::~SinkModelPrivate() +{ +} + + Sink *SinkModel::defaultSink() const { return context()->server()->defaultSink(); } Sink *SinkModel::preferredSink() const { - return m_preferredSink; + return d->m_preferredSink; } QVariant SinkModel::data(const QModelIndex &index, int role) const @@ -279,9 +308,9 @@ { Sink *sink = findPreferredSink(); - if (sink != m_preferredSink) { + if (sink != d->m_preferredSink) { qCDebug(PULSEAUDIOQT) << "Changing preferred sink to" << sink << (sink ? sink->name() : ""); - m_preferredSink = sink; + d->m_preferredSink = sink; Q_EMIT preferredSinkChanged(); } } diff --git a/src/models_p.h b/src/models_p.h new file mode 100644 --- /dev/null +++ b/src/models_p.h @@ -0,0 +1,32 @@ +#pragma once +#include "maps.h" + +namespace PulseAudioQt +{ + +class AbstractModelPrivate +{ +public: + + explicit AbstractModelPrivate(AbstractModel *q, const MapBaseQObject *map); + virtual ~AbstractModelPrivate(); + + AbstractModel *q; + const MapBaseQObject *m_map; + QHash m_roles; + QHash m_objectProperties; + QHash m_signalIndexToProperties; + +}; + +class SinkModelPrivate +{ +public: + explicit SinkModelPrivate(SinkModel *q); + virtual ~SinkModelPrivate(); + + SinkModel *q; + Sink *m_preferredSink; + +}; +}