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}
53
54AbstractModelPrivate::AbstractModelPrivate(AbstractModel *q, const MapBaseQObject *map)
55 : q(q)
56 , m_map(map)
57{
58}
59
60AbstractModelPrivate::~AbstractModelPrivate()
61{
62}
63
64QHash<int, QByteArray> AbstractModel::roleNames() const
65{
66 if (!d->m_roles.empty()) {
67 return d->m_roles;
68 }
69 Q_UNREACHABLE();
71}
72
73int AbstractModel::rowCount(const QModelIndex &parent) const
74{
75 if (parent.isValid()) {
76 return 0;
77 }
78 return d->m_map->count();
79}
80
81QVariant AbstractModel::data(const QModelIndex &index, int role) const
82{
83 if (!hasIndex(index.row(), index.column())) {
84 return QVariant();
85 }
86 QObject *data = d->m_map->objectAt(index.row());
87 Q_ASSERT(data);
88 if (role == PulseObjectRole) {
89 return QVariant::fromValue(data);
90 } else if (role == Qt::DisplayRole) {
91 return static_cast<PulseObject *>(data)->name();
92 }
93 int property = d->m_objectProperties.value(role, -1);
94 if (property == -1) {
95 return QVariant();
96 }
97 return data->metaObject()->property(property).read(data);
98}
99
100bool AbstractModel::setData(const QModelIndex &index, const QVariant &value, int role)
101{
102 if (!hasIndex(index.row(), index.column())) {
103 return false;
104 }
105 int propertyIndex = d->m_objectProperties.value(role, -1);
106 if (propertyIndex == -1) {
107 return false;
108 }
109 QObject *data = d->m_map->objectAt(index.row());
110 auto property = data->metaObject()->property(propertyIndex);
111 return property.write(data, value);
112}
113
114int AbstractModel::role(const QByteArray &roleName) const
115{
116 qCDebug(PULSEAUDIOQT) << roleName << d->m_roles.key(roleName, -1);
117 return d->m_roles.key(roleName, -1);
118}
119
120Context *AbstractModel::context() const
121{
122 return Context::instance();
123}
124
125void AbstractModel::initRoleNames(const QMetaObject &qobjectMetaObject)
126{
127 d->m_roles[PulseObjectRole] = QByteArrayLiteral("PulseObject");
128
129 QMetaEnum enumerator;
130 for (int i = 0; i < metaObject()->enumeratorCount(); ++i) {
131 if (QString::fromLatin1(metaObject()->enumerator(i).name()) == QLatin1String("ItemRole")) {
132 enumerator = metaObject()->enumerator(i);
133 break;
134 }
135 }
136
137 for (int i = 0; i < enumerator.keyCount(); ++i) {
138 // Clip the Role suffix and glue it in the hash.
139 const int roleLength = 4;
140 QByteArray key(enumerator.key(i));
141 // Enum values must end in Role or the enum is crap
142 Q_ASSERT(key.right(roleLength) == QByteArrayLiteral("Role"));
143 key.chop(roleLength);
144 d->m_roles[enumerator.value(i)] = key;
145 }
146
147 int maxEnumValue = -1;
148 for (auto it = d->m_roles.constBegin(); it != d->m_roles.constEnd(); ++it) {
149 if (it.key() > maxEnumValue) {
150 maxEnumValue = it.key();
151 }
152 }
153 Q_ASSERT(maxEnumValue != -1);
154 auto mo = qobjectMetaObject;
155 for (int i = 0; i < mo.propertyCount(); ++i) {
156 QMetaProperty property = mo.property(i);
157 auto name = QString::fromUtf8(property.name());
158 name.replace(0, 1, name.at(0).toUpper());
159 d->m_roles[++maxEnumValue] = name.toLatin1();
160 d->m_objectProperties.insert(maxEnumValue, i);
161 if (!property.hasNotifySignal()) {
162 continue;
163 }
164 d->m_signalIndexToProperties.insert(property.notifySignalIndex(), i);
165 }
166 qCDebug(PULSEAUDIOQT) << d->m_roles;
167
168 // Connect to property changes also with objects already in model
169 for (int i = 0; i < d->m_map->count(); ++i) {
170 onDataAdded(i);
171 }
172}
173
174void AbstractModel::propertyChanged()
175{
176 if (!sender() || senderSignalIndex() == -1) {
177 return;
178 }
179 int propertyIndex = d->m_signalIndexToProperties.value(senderSignalIndex(), -1);
180 if (propertyIndex == -1) {
181 return;
182 }
183 int role = d->m_objectProperties.key(propertyIndex, -1);
184 if (role == -1) {
185 return;
186 }
187 int index = d->m_map->indexOfObject(sender());
188 qCDebug(PULSEAUDIOQT) << "PROPERTY CHANGED (" << index << ") :: " << role << roleNames().value(role);
189 Q_EMIT dataChanged(createIndex(index, 0), createIndex(index, 0), {role});
190}
191
192void AbstractModel::onDataAdded(int index)
193{
194 QObject *data = d->m_map->objectAt(index);
195 const QMetaObject *mo = data->metaObject();
196 // We have all the data changed notify signals already stored
197 const auto keys = d->m_signalIndexToProperties.keys();
198 for (const auto &index : keys) {
199 QMetaMethod meth = mo->method(index);
200 connect(data, meth, this, propertyChangedMetaMethod());
201 }
202}
203
204QMetaMethod AbstractModel::propertyChangedMetaMethod() const
205{
206 auto mo = metaObject();
207 int methodIndex = mo->indexOfMethod("propertyChanged()");
208 if (methodIndex == -1) {
209 return QMetaMethod();
210 }
211 return mo->method(methodIndex);
212}
213
214SinkModel::SinkModel(QObject *parent)
215 : AbstractModel(&context()->d->m_sinks, parent)
216{
217 initRoleNames(Sink::staticMetaObject);
218}
219
220QVariant SinkModel::data(const QModelIndex &index, int role) const
221{
222 if (role == SortByDefaultRole) {
223 // Workaround QTBUG-1548
224 const QString pulseIndex = data(index, AbstractModel::role(QByteArrayLiteral("Index"))).toString();
225 const QString defaultDevice = data(index, AbstractModel::role(QByteArrayLiteral("Default"))).toString();
226 const QString ret = defaultDevice + pulseIndex;
227 return ret;
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 const QString ret = defaultDevice + pulseIndex;
245 return ret;
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(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 fromLatin1(QByteArrayView str)
QString fromUtf8(QByteArrayView str)
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 Sat Dec 21 2024 16:57:59 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.