File indexing completed on 2023-09-24 09:38:53
0001 /* 0002 SPDX-FileCopyrightText: 2014-2015 Harald Sitter <sitter@kde.org> 0003 0004 SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL 0005 */ 0006 0007 #ifndef MAPS_H 0008 #define MAPS_H 0009 0010 #include "debug.h" 0011 #include <QMap> 0012 #include <QObject> 0013 0014 #include <pulse/ext-stream-restore.h> 0015 #include <pulse/pulseaudio.h> 0016 0017 namespace QPulseAudio 0018 { 0019 // Used for typedefs. 0020 class Card; 0021 class Client; 0022 class Sink; 0023 class SinkInput; 0024 class Source; 0025 class SourceOutput; 0026 class StreamRestore; 0027 class Module; 0028 0029 /** 0030 * @see MapBase 0031 * This class is nothing more than the QObject base since moc cannot handle 0032 * templates. 0033 */ 0034 class MapBaseQObject : public QObject 0035 { 0036 Q_OBJECT 0037 0038 public: 0039 virtual int count() const = 0; 0040 virtual QObject *objectAt(int index) const = 0; 0041 virtual int indexOfObject(QObject *object) const = 0; 0042 0043 Q_SIGNALS: 0044 void aboutToBeAdded(int index); 0045 void added(int index); 0046 void aboutToBeRemoved(int index); 0047 void removed(int index); 0048 }; 0049 0050 /** 0051 * Maps a specific index to a specific object pointer. 0052 * This is used to give the unique arbitrary PulseAudio index of a PulseObject a 0053 * serialized list index. Namely it enables us to translate a discrete list 0054 * index to a pulse index to an object, and any permutation thereof. 0055 */ 0056 template<typename Type, typename PAInfo> 0057 class MapBase : public MapBaseQObject 0058 { 0059 public: 0060 ~MapBase() override = default; 0061 0062 const QMap<quint32, Type *> &data() const 0063 { 0064 return m_data; 0065 } 0066 0067 int count() const override 0068 { 0069 return m_data.count(); 0070 } 0071 0072 int indexOfObject(QObject *object) const override 0073 { 0074 int index = 0; 0075 QMapIterator<quint32, Type *> it(m_data); 0076 while (it.hasNext()) { 0077 it.next(); 0078 if (it.value() == object) { 0079 return index; 0080 } 0081 index++; 0082 } 0083 return -1; 0084 } 0085 0086 QObject *objectAt(int index) const override 0087 { 0088 return (m_data.constBegin() + index).value(); 0089 } 0090 0091 void reset() 0092 { 0093 while (!m_data.isEmpty()) { 0094 removeEntry(m_data.lastKey()); 0095 } 0096 m_pendingRemovals.clear(); 0097 } 0098 0099 void insert(Type *object) 0100 { 0101 Q_ASSERT(!m_data.contains(object->index())); 0102 0103 int modelIndex = 0; 0104 for (auto it = m_data.constBegin(); it != m_data.constEnd(); ++it) { 0105 if (object->index() < it.key()) { 0106 break; 0107 } 0108 modelIndex++; 0109 } 0110 0111 Q_EMIT aboutToBeAdded(modelIndex); 0112 m_data.insert(object->index(), object); 0113 Q_ASSERT(modelIndex == m_data.keys().indexOf(object->index())); 0114 Q_EMIT added(modelIndex); 0115 } 0116 0117 // Context is passed in as parent because context needs to include the maps 0118 // so we'd cause a circular dep if we were to try to use the instance here. 0119 // Plus that's weird separation anyway. 0120 void updateEntry(const PAInfo *info, QObject *parent) 0121 { 0122 Q_ASSERT(info); 0123 0124 if (m_pendingRemovals.remove(info->index)) { 0125 // Was already removed again. 0126 return; 0127 } 0128 0129 auto *obj = m_data.value(info->index, nullptr); 0130 if (!obj) { 0131 obj = new Type(parent); 0132 } 0133 obj->update(info); 0134 0135 if (!m_data.contains(info->index)) { 0136 insert(obj); 0137 } 0138 } 0139 0140 void removeEntry(quint32 index) 0141 { 0142 if (!m_data.contains(index)) { 0143 m_pendingRemovals.insert(index); 0144 } else { 0145 const int modelIndex = m_data.keys().indexOf(index); 0146 Q_EMIT aboutToBeRemoved(modelIndex); 0147 delete m_data.take(index); 0148 Q_EMIT removed(modelIndex); 0149 } 0150 } 0151 0152 protected: 0153 QMap<quint32, Type *> m_data; 0154 QSet<quint32> m_pendingRemovals; 0155 }; 0156 0157 using SinkMap = MapBase<Sink, pa_sink_info>; 0158 using SinkInputMap = MapBase<SinkInput, pa_sink_input_info>; 0159 using SourceMap = MapBase<Source, pa_source_info>; 0160 using SourceOutputMap = MapBase<SourceOutput, pa_source_output_info>; 0161 using ClientMap = MapBase<Client, pa_client_info>; 0162 using CardMap = MapBase<Card, pa_card_info>; 0163 using ModuleMap = MapBase<Module, pa_module_info>; 0164 using StreamRestoreMap = MapBase<StreamRestore, pa_ext_stream_restore_info>; 0165 0166 } // QPulseAudio 0167 0168 #endif // MAPS_H