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