/* SPDX-FileCopyrightText: 2014-2015 Harald Sitter SPDX-FileCopyrightText: 2018 David Rosca SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL */ #pragma once #include #include #include #include #include #include #include "card_p.h" #include "client_p.h" #include "debug_object.h" #include "module_p.h" #include "pulseobject_p.h" #include "sink_p.h" #include "sinkinput_p.h" #include "source_p.h" #include "sourceoutput_p.h" #include "streamrestore_p.h" namespace PulseAudioQt { // Used for typedefs. class Card; class Client; class Sink; class SinkInput; class Source; class SourceOutput; class StreamRestore; class Module; /** * @see MapBase * This class is nothing more than the QObject base since moc cannot handle * templates. */ class MapBaseQObject : public QObject { Q_OBJECT public: virtual int count() const = 0; virtual QObject *objectAt(int index) const = 0; virtual int indexOfObject(QObject *object) const = 0; void disconnectSignals() { disconnect(this, &MapBaseQObject::aboutToBeAdded, nullptr, nullptr); disconnect(this, &MapBaseQObject::added, nullptr, nullptr); disconnect(this, &MapBaseQObject::aboutToBeRemoved, nullptr, nullptr); disconnect(this, &MapBaseQObject::removed, nullptr, nullptr); }; Q_SIGNALS: void aboutToBeAdded(int index); void added(int index, QObject *object); void aboutToBeRemoved(int index); void removed(int index, QObject *object); }; /** * Maps a specific index to a specific object pointer. * This is used to give the unique arbitrary PulseAudio index of a PulseObject a * serialized list index. Namely it enables us to translate a discrete list * index to a pulse index to an object, and any permutation thereof. */ template class MapBase : public MapBaseQObject { public: ~MapBase() override = default; const QList &data() const { return m_data; } int count() const override { return m_data.count(); } int indexOfObject(QObject *object) const override { return m_data.indexOf(static_cast(object)); } QObject *objectAt(int index) const override { return m_data.at(index); } void reset() { while (!m_hash.isEmpty()) { removeEntry(m_data.at(m_data.count() - 1)->index()); } m_pendingRemovals.clear(); } void insert(Type *object) { Q_ASSERT(!m_data.contains(object)); const int modelIndex = m_data.count(); Q_EMIT aboutToBeAdded(modelIndex); m_data.append(object); m_hash[object->index()] = object; Q_EMIT added(modelIndex, object); } // Context is passed in as parent because context needs to include the maps // so we'd cause a circular dep if we were to try to use the instance here. // Plus that's weird separation anyway. void updateEntry(const PAInfo *info, QObject *parent) { Q_ASSERT(info); if (m_pendingRemovals.remove(info->index)) { // Was already removed again. return; } auto *obj = m_hash.value(info->index); auto type = QLatin1String("create"); if (!obj) { obj = new Type(parent); obj->d->update(info); insert(obj); } else { type = QLatin1String("update"); obj->d->update(info); } #if !defined(UNDER_TEST) qCDebug(PAOBJECT).noquote() << type << qobject_cast(obj)->d.get(); #endif } void removeEntry(quint32 index) { if (!m_hash.contains(index)) { m_pendingRemovals.insert(index); } else { const int modelIndex = m_data.indexOf(m_hash.value(index)); Q_EMIT aboutToBeRemoved(modelIndex); m_data.removeAt(modelIndex); auto object = m_hash.take(index); Q_EMIT removed(modelIndex, object); delete object; } } protected: QList m_data; QHash m_hash; QSet m_pendingRemovals; }; typedef MapBase CardMap; typedef MapBase ClientMap; typedef MapBase SinkInputMap; typedef MapBase SinkMap; typedef MapBase SourceMap; typedef MapBase SourceOutputMap; typedef MapBase StreamRestoreMap; typedef MapBase ModuleMap; } // PulseAudioQt