PulseAudio Qt Bindings

models.cpp
1/*
2 SPDX-FileCopyrightText: 2014-2015 Harald Sitter <sitter@kde.org>
3 SPDX-FileCopyrightText: 2016 David Rosca <nowrep@gmail.com>
4
5 SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
6*/
7
8#include "models.h"
9
10#include "card.h"
11#include "context.h"
12#include "context_p.h"
13#include "debug.h"
14#include "maps.h"
15#include "module.h"
16#include "server.h"
17#include "sink.h"
18#include "sinkinput.h"
19#include "source.h"
20#include "sourceoutput.h"
21#include "streamrestore.h"
22
23#include "models_p.h"
24#include <QMetaEnum>
25
26namespace PulseAudioQt
27{
28AbstractModel::AbstractModel(const MapBaseQObject *map, QObject *parent)
29 : QAbstractListModel(parent)
30 , d(new AbstractModelPrivate(this, map))
31{
32 connect(d->m_map, &MapBaseQObject::aboutToBeAdded, this, [this](int index) {
33 beginInsertRows(QModelIndex(), index, index);
34 });
35 connect(d->m_map, &MapBaseQObject::added, this, [this](int index) {
36 onDataAdded(index);
37 endInsertRows();
38 Q_EMIT countChanged();
39 });
40 connect(d->m_map, &MapBaseQObject::aboutToBeRemoved, this, [this](int index) {
41 beginRemoveRows(QModelIndex(), index, index);
42 });
43 connect(d->m_map, &MapBaseQObject::removed, this, [this](int index) {
44 Q_UNUSED(index);
45 endRemoveRows();
46 Q_EMIT countChanged();
47 });
48}
49
50AbstractModel::~AbstractModel()
51{
52 delete d;
53}
54
55AbstractModelPrivate::AbstractModelPrivate(AbstractModel *q, const MapBaseQObject *map)
56 : q(q)
57 , m_map(map)
58{
59}
60
61AbstractModelPrivate::~AbstractModelPrivate()
62{
63}
64
65QHash<int, QByteArray> AbstractModel::roleNames() const
66{
67 if (!d->m_roles.empty()) {
68 return d->m_roles;
69 }
70 Q_UNREACHABLE();
72}
73
74int AbstractModel::rowCount(const QModelIndex &parent) const
75{
76 if (parent.isValid()) {
77 return 0;
78 }
79 return d->m_map->count();
80}
81
82QVariant AbstractModel::data(const QModelIndex &index, int role) const
83{
84 if (!hasIndex(index.row(), index.column())) {
85 return QVariant();
86 }
87 QObject *data = d->m_map->objectAt(index.row());
88 Q_ASSERT(data);
89 if (role == PulseObjectRole) {
90 return QVariant::fromValue(data);
91 } else if (role == Qt::DisplayRole) {
92 return static_cast<PulseObject *>(data)->name();
93 }
94 int property = d->m_objectProperties.value(role, -1);
95 if (property == -1) {
96 return QVariant();
97 }
98 return data->metaObject()->property(property).read(data);
99}
100
101bool AbstractModel::setData(const QModelIndex &index, const QVariant &value, int role)
102{
103 if (!hasIndex(index.row(), index.column())) {
104 return false;
105 }
106 int propertyIndex = d->m_objectProperties.value(role, -1);
107 if (propertyIndex == -1) {
108 return false;
109 }
110 QObject *data = d->m_map->objectAt(index.row());
111 auto property = data->metaObject()->property(propertyIndex);
112 return property.write(data, value);
113}
114
115int AbstractModel::role(const QByteArray &roleName) const
116{
117 qCDebug(PULSEAUDIOQT) << roleName << d->m_roles.key(roleName, -1);
118 return d->m_roles.key(roleName, -1);
119}
120
121Context *AbstractModel::context() const
122{
123 return Context::instance();
124}
125
126void AbstractModel::initRoleNames(const QMetaObject &qobjectMetaObject)
127{
128 d->m_roles[PulseObjectRole] = QByteArrayLiteral("PulseObject");
129
130 QMetaEnum enumerator;
131 for (int i = 0; i < metaObject()->enumeratorCount(); ++i) {
132 if (metaObject()->enumerator(i).name() == QLatin1String("ItemRole")) {
133 enumerator = metaObject()->enumerator(i);
134 break;
135 }
136 }
137
138 for (int i = 0; i < enumerator.keyCount(); ++i) {
139 // Clip the Role suffix and glue it in the hash.
140 const int roleLength = 4;
141 QByteArray key(enumerator.key(i));
142 // Enum values must end in Role or the enum is crap
143 Q_ASSERT(key.right(roleLength) == QByteArrayLiteral("Role"));
144 key.chop(roleLength);
145 d->m_roles[enumerator.value(i)] = key;
146 }
147
148 int maxEnumValue = -1;
149 for (auto it = d->m_roles.constBegin(); it != d->m_roles.constEnd(); ++it) {
150 if (it.key() > maxEnumValue) {
151 maxEnumValue = it.key();
152 }
153 }
154 Q_ASSERT(maxEnumValue != -1);
155 auto mo = qobjectMetaObject;
156 for (int i = 0; i < mo.propertyCount(); ++i) {
157 QMetaProperty property = mo.property(i);
158 QString name(property.name());
159 name.replace(0, 1, name.at(0).toUpper());
160 d->m_roles[++maxEnumValue] = name.toLatin1();
161 d->m_objectProperties.insert(maxEnumValue, i);
162 if (!property.hasNotifySignal()) {
163 continue;
164 }
165 d->m_signalIndexToProperties.insert(property.notifySignalIndex(), i);
166 }
167 qCDebug(PULSEAUDIOQT) << d->m_roles;
168
169 // Connect to property changes also with objects already in model
170 for (int i = 0; i < d->m_map->count(); ++i) {
171 onDataAdded(i);
172 }
173}
174
175void AbstractModel::propertyChanged()
176{
177 if (!sender() || senderSignalIndex() == -1) {
178 return;
179 }
180 int propertyIndex = d->m_signalIndexToProperties.value(senderSignalIndex(), -1);
181 if (propertyIndex == -1) {
182 return;
183 }
184 int role = d->m_objectProperties.key(propertyIndex, -1);
185 if (role == -1) {
186 return;
187 }
188 int index = d->m_map->indexOfObject(sender());
189 qCDebug(PULSEAUDIOQT) << "PROPERTY CHANGED (" << index << ") :: " << role << roleNames().value(role);
190 Q_EMIT dataChanged(createIndex(index, 0), createIndex(index, 0), {role});
191}
192
193void AbstractModel::onDataAdded(int index)
194{
195 QObject *data = d->m_map->objectAt(index);
196 const QMetaObject *mo = data->metaObject();
197 // We have all the data changed notify signals already stored
198 const auto keys = d->m_signalIndexToProperties.keys();
199 for (const auto &index : keys) {
200 QMetaMethod meth = mo->method(index);
201 connect(data, meth, this, propertyChangedMetaMethod());
202 }
203}
204
205QMetaMethod AbstractModel::propertyChangedMetaMethod() const
206{
207 auto mo = metaObject();
208 int methodIndex = mo->indexOfMethod("propertyChanged()");
209 if (methodIndex == -1) {
210 return QMetaMethod();
211 }
212 return mo->method(methodIndex);
213}
214
215SinkModel::SinkModel(QObject *parent)
216 : AbstractModel(&context()->d->m_sinks, parent)
217{
218 initRoleNames(Sink::staticMetaObject);
219}
220
221QVariant SinkModel::data(const QModelIndex &index, int role) const
222{
223 if (role == SortByDefaultRole) {
224 // Workaround QTBUG-1548
225 const QString pulseIndex = data(index, AbstractModel::role(QByteArrayLiteral("Index"))).toString();
226 const QString defaultDevice = data(index, AbstractModel::role(QByteArrayLiteral("Default"))).toString();
227 return defaultDevice + pulseIndex;
228 }
229 return AbstractModel::data(index, role);
230}
231
232SourceModel::SourceModel(QObject *parent)
233 : AbstractModel(&context()->d->m_sources, parent)
234{
235 initRoleNames(Source::staticMetaObject);
236}
237
238QVariant SourceModel::data(const QModelIndex &index, int role) const
239{
240 if (role == SortByDefaultRole) {
241 // Workaround QTBUG-1548
242 const QString pulseIndex = data(index, AbstractModel::role(QByteArrayLiteral("Index"))).toString();
243 const QString defaultDevice = data(index, AbstractModel::role(QByteArrayLiteral("Default"))).toString();
244 return defaultDevice + pulseIndex;
245 }
246 return AbstractModel::data(index, role);
247}
248
249SinkInputModel::SinkInputModel(QObject *parent)
250 : AbstractModel(&context()->d->m_sinkInputs, parent)
251{
252 initRoleNames(SinkInput::staticMetaObject);
253}
254
255SourceOutputModel::SourceOutputModel(QObject *parent)
256 : AbstractModel(&context()->d->m_sourceOutputs, parent)
257{
258 initRoleNames(SourceOutput::staticMetaObject);
259}
260
261CardModel::CardModel(QObject *parent)
262 : AbstractModel(&context()->d->m_cards, parent)
263{
264 initRoleNames(Card::staticMetaObject);
265}
266
267StreamRestoreModel::StreamRestoreModel(QObject *parent)
268 : AbstractModel(&context()->d->m_streamRestores, parent)
269{
270 initRoleNames(StreamRestore::staticMetaObject);
271}
272
273ModuleModel::ModuleModel(QObject *parent)
274 : AbstractModel(&context()->d->m_modules, parent)
275{
276 initRoleNames(Module::staticMetaObject);
277}
278
279} // PulseAudioQt
QString name(StandardAction id)
The primary namespace of PulseAudioQt.
Definition card.cpp:17
QByteArray & insert(qsizetype i, QByteArrayView data)
char32_t toUpper(char32_t ucs4)
const char * key(int index) const const
int keyCount() const const
int value(int index) const const
int indexOfMethod(const char *method) const const
QMetaMethod method(int index) const const
QMetaProperty property(int index) const const
QVariant read(const QObject *object) const const
bool write(QObject *object, QVariant &&v) const const
int column() const const
bool isValid() const const
int row() const const
virtual const QMetaObject * metaObject() const const
const QChar at(qsizetype position) const const
QString & replace(QChar before, QChar after, Qt::CaseSensitivity cs)
QByteArray toLatin1() const const
DisplayRole
QFuture< void > map(Iterator begin, Iterator end, MapFunctor &&function)
QFuture< ArgsType< Signal > > connect(Sender *sender, Signal signal)
QVariant fromValue(T &&value)
T value() const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Fri Oct 11 2024 12:12:38 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.