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 qCDebug(PULSEAUDIOQT) << "returning roles" << d->m_roles;
69 return d->m_roles;
70 }
71 Q_UNREACHABLE();
73}
74
75int AbstractModel::rowCount(const QModelIndex &parent) const
76{
77 if (parent.isValid()) {
78 return 0;
79 }
80 return d->m_map->count();
81}
82
83QVariant AbstractModel::data(const QModelIndex &index, int role) const
84{
85 if (!hasIndex(index.row(), index.column())) {
86 return QVariant();
87 }
88 QObject *data = d->m_map->objectAt(index.row());
89 Q_ASSERT(data);
90 if (role == PulseObjectRole) {
91 return QVariant::fromValue(data);
92 } else if (role == Qt::DisplayRole) {
93 return static_cast<PulseObject *>(data)->name();
94 }
95 int property = d->m_objectProperties.value(role, -1);
96 if (property == -1) {
97 return QVariant();
98 }
99 return data->metaObject()->property(property).read(data);
100}
101
102bool AbstractModel::setData(const QModelIndex &index, const QVariant &value, int role)
103{
104 if (!hasIndex(index.row(), index.column())) {
105 return false;
106 }
107 int propertyIndex = d->m_objectProperties.value(role, -1);
108 if (propertyIndex == -1) {
109 return false;
110 }
111 QObject *data = d->m_map->objectAt(index.row());
112 auto property = data->metaObject()->property(propertyIndex);
113 return property.write(data, value);
114}
115
116int AbstractModel::role(const QByteArray &roleName) const
117{
118 qCDebug(PULSEAUDIOQT) << roleName << d->m_roles.key(roleName, -1);
119 return d->m_roles.key(roleName, -1);
120}
121
122Context *AbstractModel::context() const
123{
124 return Context::instance();
125}
126
127void AbstractModel::initRoleNames(const QMetaObject &qobjectMetaObject)
128{
129 d->m_roles[PulseObjectRole] = QByteArrayLiteral("PulseObject");
130
131 QMetaEnum enumerator;
132 for (int i = 0; i < metaObject()->enumeratorCount(); ++i) {
133 if (metaObject()->enumerator(i).name() == QLatin1String("ItemRole")) {
134 enumerator = metaObject()->enumerator(i);
135 break;
136 }
137 }
138
139 for (int i = 0; i < enumerator.keyCount(); ++i) {
140 // Clip the Role suffix and glue it in the hash.
141 const int roleLength = 4;
142 QByteArray key(enumerator.key(i));
143 // Enum values must end in Role or the enum is crap
144 Q_ASSERT(key.right(roleLength) == QByteArrayLiteral("Role"));
145 key.chop(roleLength);
146 d->m_roles[enumerator.value(i)] = key;
147 }
148
149 int maxEnumValue = -1;
150 for (auto it = d->m_roles.constBegin(); it != d->m_roles.constEnd(); ++it) {
151 if (it.key() > maxEnumValue) {
152 maxEnumValue = it.key();
153 }
154 }
155 Q_ASSERT(maxEnumValue != -1);
156 auto mo = qobjectMetaObject;
157 for (int i = 0; i < mo.propertyCount(); ++i) {
158 QMetaProperty property = mo.property(i);
159 QString name(property.name());
160 name.replace(0, 1, name.at(0).toUpper());
161 d->m_roles[++maxEnumValue] = name.toLatin1();
162 d->m_objectProperties.insert(maxEnumValue, i);
163 if (!property.hasNotifySignal()) {
164 continue;
165 }
166 d->m_signalIndexToProperties.insert(property.notifySignalIndex(), i);
167 }
168 qCDebug(PULSEAUDIOQT) << d->m_roles;
169
170 // Connect to property changes also with objects already in model
171 for (int i = 0; i < d->m_map->count(); ++i) {
172 onDataAdded(i);
173 }
174}
175
176void AbstractModel::propertyChanged()
177{
178 if (!sender() || senderSignalIndex() == -1) {
179 return;
180 }
181 int propertyIndex = d->m_signalIndexToProperties.value(senderSignalIndex(), -1);
182 if (propertyIndex == -1) {
183 return;
184 }
185 int role = d->m_objectProperties.key(propertyIndex, -1);
186 if (role == -1) {
187 return;
188 }
189 int index = d->m_map->indexOfObject(sender());
190 qCDebug(PULSEAUDIOQT) << "PROPERTY CHANGED (" << index << ") :: " << role << roleNames().value(role);
191 Q_EMIT dataChanged(createIndex(index, 0), createIndex(index, 0), {role});
192}
193
194void AbstractModel::onDataAdded(int index)
195{
196 QObject *data = d->m_map->objectAt(index);
197 const QMetaObject *mo = data->metaObject();
198 // We have all the data changed notify signals already stored
199 const auto keys = d->m_signalIndexToProperties.keys();
200 for (const auto &index : keys) {
201 QMetaMethod meth = mo->method(index);
202 connect(data, meth, this, propertyChangedMetaMethod());
203 }
204}
205
206QMetaMethod AbstractModel::propertyChangedMetaMethod() const
207{
208 auto mo = metaObject();
209 int methodIndex = mo->indexOfMethod("propertyChanged()");
210 if (methodIndex == -1) {
211 return QMetaMethod();
212 }
213 return mo->method(methodIndex);
214}
215
216SinkModel::SinkModel(QObject *parent)
217 : AbstractModel(&context()->d->m_sinks, parent)
218{
219 initRoleNames(Sink::staticMetaObject);
220}
221
222QVariant SinkModel::data(const QModelIndex &index, int role) const
223{
224 if (role == SortByDefaultRole) {
225 // Workaround QTBUG-1548
226 const QString pulseIndex = data(index, AbstractModel::role(QByteArrayLiteral("Index"))).toString();
227 const QString defaultDevice = data(index, AbstractModel::role(QByteArrayLiteral("Default"))).toString();
228 return defaultDevice + pulseIndex;
229 }
230 return AbstractModel::data(index, role);
231}
232
233SourceModel::SourceModel(QObject *parent)
234 : AbstractModel(&context()->d->m_sources, parent)
235{
236 initRoleNames(Source::staticMetaObject);
237}
238
239QVariant SourceModel::data(const QModelIndex &index, int role) const
240{
241 if (role == SortByDefaultRole) {
242 // Workaround QTBUG-1548
243 const QString pulseIndex = data(index, AbstractModel::role(QByteArrayLiteral("Index"))).toString();
244 const QString defaultDevice = data(index, AbstractModel::role(QByteArrayLiteral("Default"))).toString();
245 return defaultDevice + pulseIndex;
246 }
247 return AbstractModel::data(index, role);
248}
249
250SinkInputModel::SinkInputModel(QObject *parent)
251 : AbstractModel(&context()->d->m_sinkInputs, parent)
252{
253 initRoleNames(SinkInput::staticMetaObject);
254}
255
256SourceOutputModel::SourceOutputModel(QObject *parent)
257 : AbstractModel(&context()->d->m_sourceOutputs, parent)
258{
259 initRoleNames(SourceOutput::staticMetaObject);
260}
261
262CardModel::CardModel(QObject *parent)
263 : AbstractModel(&context()->d->m_cards, parent)
264{
265 initRoleNames(Card::staticMetaObject);
266}
267
268StreamRestoreModel::StreamRestoreModel(QObject *parent)
269 : AbstractModel(&context()->d->m_streamRestores, parent)
270{
271 initRoleNames(StreamRestore::staticMetaObject);
272}
273
274ModuleModel::ModuleModel(QObject *parent)
275 : AbstractModel(&context()->d->m_modules, parent)
276{
277 initRoleNames(Module::staticMetaObject);
278}
279
280} // PulseAudioQt
QString name(StandardShortcut 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 Tue Mar 26 2024 11:20:07 by doxygen 1.10.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.