1 /* 2 * Kaidan - A user-friendly XMPP client for every device! 3 * 4 * Copyright (C) 2016-2021 Kaidan developers and contributors 5 * (see the LICENSE file for a full list of copyright authors) 6 * 7 * Kaidan is free software: you can redistribute it and/or modify 8 * it under the terms of the GNU General Public License as published by 9 * the Free Software Foundation, either version 3 of the License, or 10 * (at your option) any later version. 11 * 12 * In addition, as a special exception, the author of Kaidan gives 13 * permission to link the code of its release with the OpenSSL 14 * project's "OpenSSL" library (or with modified versions of it that 15 * use the same license as the "OpenSSL" library), and distribute the 16 * linked executables. You must obey the GNU General Public License in 17 * all respects for all of the code used other than "OpenSSL". If you 18 * modify this file, you may extend this exception to your version of 19 * the file, but you are not obligated to do so. If you do not wish to 20 * do so, delete this exception statement from your version. 21 * 22 * Kaidan is distributed in the hope that it will be useful, 23 * but WITHOUT ANY WARRANTY; without even the implied warranty of 24 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 25 * GNU General Public License for more details. 26 * 27 * You should have received a copy of the GNU General Public License 28 * along with Kaidan. If not, see <http://www.gnu.org/licenses/>. 29 */ 30 31 #pragma once 32 33 #include <QAbstractListModel> 34 #include <QList> 35 36 #include <functional> 37 38 template <typename T> 39 class MediaSettingModel : public QAbstractListModel 40 { 41 public: 42 enum CustomRoles { 43 ValueRole = Qt::UserRole, 44 DescriptionRole 45 }; 46 47 using ToString = std::function<QString(const T &, const void *userData)>; 48 49 using QAbstractListModel::QAbstractListModel; 50 51 explicit MediaSettingModel(MediaSettingModel::ToString toString, 52 const void *userData = nullptr, QObject *parent = nullptr) QAbstractListModel(parent)53 : QAbstractListModel(parent) 54 , m_toString(toString) 55 , m_userData(userData) { 56 } 57 58 int rowCount(const QModelIndex &parent = QModelIndex()) const override { 59 return parent == QModelIndex() ? m_values.count() : 0; 60 } 61 62 QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override { 63 if (hasIndex(index.row(), index.column(), index.parent())) { 64 switch (role) { 65 case MediaSettingModel::CustomRoles::ValueRole: 66 return QVariant::fromValue(m_values[index.row()]); 67 case MediaSettingModel::CustomRoles::DescriptionRole: 68 return toString(m_values[index.row()]); 69 } 70 } 71 72 return { }; 73 } 74 roleNames()75 QHash<int, QByteArray> roleNames() const override { 76 static const QHash<int, QByteArray> roles { 77 { MediaSettingModel::CustomRoles::ValueRole, QByteArrayLiteral("value") }, 78 { MediaSettingModel::CustomRoles::DescriptionRole, QByteArrayLiteral("description") } 79 }; 80 81 return roles; 82 } 83 toString()84 MediaSettingModel::ToString toString() const { 85 return m_toString; 86 } 87 setToString(MediaSettingModel::ToString toString)88 void setToString(MediaSettingModel::ToString toString) { 89 m_toString = toString; 90 91 emit toStringChanged(); 92 93 const int count = rowCount(); 94 95 if (count > 0) { 96 emit dataChanged(index(0, 0), index(count -1, 0)); 97 } 98 } 99 userData()100 const void *userData() const { 101 return m_userData; 102 } 103 setUserData(const void * userData)104 void setUserData(const void *userData) { 105 if (m_userData == userData) { 106 return; 107 } 108 109 m_userData = userData; 110 111 emit userDataChanged(); 112 113 const int count = rowCount(); 114 115 if (count > 0) { 116 emit dataChanged(index(0, 0), index(count -1, 0)); 117 } 118 } 119 values()120 QList<T> values() const { 121 return m_values; 122 } 123 setValues(const QList<T> & values)124 void setValues(const QList<T> &values) { 125 if (m_values == values) { 126 return; 127 } 128 129 const int newCurrentIndex = m_currentIndex != -1 ? values.indexOf(currentValue()) : -1; 130 const bool curIdxChanged = newCurrentIndex != m_currentIndex; 131 132 beginResetModel(); 133 m_values = values; 134 m_currentIndex = newCurrentIndex; 135 endResetModel(); 136 137 emit valuesChanged(); 138 139 if (curIdxChanged) { 140 emit currentIndexChanged(); 141 } 142 } 143 currentIndex()144 int currentIndex() const { 145 return m_currentIndex; 146 } 147 setCurrentIndex(int currentIndex)148 void setCurrentIndex(int currentIndex) { 149 if (currentIndex < 0 || currentIndex >= m_values.count() 150 || m_currentIndex == currentIndex) { 151 return; 152 } 153 154 m_currentIndex = currentIndex; 155 emit currentIndexChanged(); 156 } 157 currentValue()158 T currentValue() const { 159 return m_currentIndex >= 0 && m_currentIndex < m_values.count() 160 ? m_values[m_currentIndex] 161 : T(); 162 } 163 setCurrentValue(const T & currentValue)164 void setCurrentValue(const T ¤tValue) { 165 setCurrentIndex(indexOf(currentValue)); 166 } 167 currentDescription()168 QString currentDescription() const { 169 return m_currentIndex >= 0 && m_currentIndex < m_values.count() 170 ? toString(currentValue()) 171 : QString(); 172 } 173 setValuesAndCurrentIndex(const QList<T> & values,int currentIndex)174 void setValuesAndCurrentIndex(const QList<T> &values, int currentIndex) { 175 if (m_values == values && m_currentIndex == currentIndex) { 176 return; 177 } 178 179 beginResetModel(); 180 m_values = values; 181 m_currentIndex = currentIndex >= 0 && currentIndex < m_values.count() 182 ? currentIndex 183 : -1; 184 endResetModel(); 185 186 emit valuesChanged(); 187 emit currentIndexChanged(); 188 } 189 setValuesAndCurrentValue(const QList<T> & values,const T & currentValue)190 void setValuesAndCurrentValue(const QList<T> &values, const T ¤tValue) { 191 setValuesAndCurrentIndex(values, values.indexOf(currentValue)); 192 } 193 194 // Invokables clear()195 virtual void clear() { 196 beginResetModel(); 197 m_currentIndex = -1; 198 m_values.clear(); 199 endResetModel(); 200 201 emit valuesChanged(); 202 emit currentIndexChanged(); 203 } 204 indexOf(const T & value)205 virtual int indexOf(const T &value) const { 206 return m_values.indexOf(value); 207 } 208 value(int index)209 virtual T value(int index) const { 210 if (index < 0 || index >= m_values.count()) { 211 return { }; 212 } 213 214 return m_values[index]; 215 } 216 description(int index)217 virtual QString description(int index) const { 218 if (index < 0 || index >= m_values.count()) { 219 return { }; 220 } 221 222 return toString(m_values[index]); 223 } 224 toString(const T & value)225 virtual QString toString(const T &value) const { 226 if (m_toString) { 227 return m_toString(value, m_userData); 228 } 229 230 return QVariant::fromValue(value).toString(); 231 } 232 233 // Signals 234 virtual void toStringChanged() = 0; 235 virtual void userDataChanged() = 0; 236 virtual void valuesChanged() = 0; 237 virtual void currentIndexChanged() = 0; 238 239 private: 240 MediaSettingModel::ToString m_toString; 241 const void *m_userData = nullptr; 242 int m_currentIndex = -1; 243 QList<T> m_values; 244 }; 245 246 #define DECL_MEDIA_SETTING_MODEL(NAME, TYPE, TO_STRING) \ 247 class MediaSettings##NAME##Model : public MediaSettingModel<TYPE> { \ 248 Q_OBJECT \ 249 \ 250 Q_PROPERTY(MediaSettings##NAME##Model::ToString toString READ toString WRITE setToString NOTIFY toStringChanged) \ 251 Q_PROPERTY(const void *userData READ userData WRITE setUserData NOTIFY userDataChanged) \ 252 Q_PROPERTY(QList<TYPE> values READ values WRITE setValues NOTIFY valuesChanged) \ 253 Q_PROPERTY(int rowCount READ rowCount NOTIFY valuesChanged) \ 254 Q_PROPERTY(int currentIndex READ currentIndex WRITE setCurrentIndex NOTIFY currentIndexChanged) \ 255 Q_PROPERTY(TYPE currentValue READ currentValue WRITE setCurrentValue NOTIFY currentIndexChanged) \ 256 Q_PROPERTY(QString currentDescription READ currentDescription NOTIFY currentIndexChanged) \ 257 \ 258 using MSMT = MediaSettingModel<TYPE>; \ 259 \ 260 public: \ 261 using MSMT::MSMT; \ 262 explicit MediaSettings##NAME##Model(const void *userData, QObject *parent = nullptr) \ 263 : MSMT(TO_STRING, userData, parent) \ 264 { } \ 265 \ 266 explicit MediaSettings##NAME##Model(QObject *parent = nullptr) \ 267 : MSMT(parent) \ 268 { } \ 269 \ 270 using MSMT::toString; \ 271 Q_INVOKABLE void clear() override { MSMT::clear(); } \ 272 Q_INVOKABLE int indexOf(const TYPE &value) const override { return MSMT::indexOf(value); } \ 273 Q_INVOKABLE TYPE value(int index) const override { return MSMT::value(index); } \ 274 Q_INVOKABLE QString description(int index) const override { return MSMT::description(index); } \ 275 Q_INVOKABLE QString toString(const TYPE &value) const override { return MSMT::toString(value); } \ 276 \ 277 Q_SIGNALS: \ 278 void toStringChanged() override; \ 279 void userDataChanged() override; \ 280 void valuesChanged() override; \ 281 void currentIndexChanged() override; \ 282 } 283