File indexing completed on 2024-03-24 05:49:42
0001 /* 0002 Copyright © 2014-2017 Harald Sitter <sitter@kde.org> 0003 Copyright © 2016 David Rosca <nowrep@gmail.com> 0004 0005 This library is free software; you can redistribute it and/or 0006 modify it under the terms of the GNU Lesser General Public 0007 License as published by the Free Software Foundation; either 0008 version 2.1 of the License, or (at your option) version 3, or any 0009 later version accepted by the membership of KDE e.V. (or its 0010 successor approved by the membership of KDE e.V.), which shall 0011 act as a proxy defined in Section 6 of version 3 of the license. 0012 0013 This library is distributed in the hope that it will be useful, 0014 but WITHOUT ANY WARRANTY; without even the implied warranty of 0015 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 0016 Lesser General Public License for more details. 0017 0018 You should have received a copy of the GNU Lesser General Public 0019 License along with this library. If not, see <http://www.gnu.org/licenses/>. 0020 */ 0021 0022 #include "AbstractModel.h" 0023 0024 #include <QMetaProperty> 0025 0026 #include "Debug.h" 0027 0028 AbstractModel::AbstractModel(QMap<QString, QObject *> *map, QObject *parent) 0029 : QAbstractListModel(parent) 0030 , m_map(map) 0031 { 0032 } 0033 0034 QHash<int, QByteArray> AbstractModel::roleNames() const 0035 { 0036 if (!m_roles.empty()) { 0037 qCDebug(PLASMAPK) << "returning roles" << m_roles; 0038 return m_roles; 0039 } 0040 Q_UNREACHABLE(); 0041 return QHash<int, QByteArray>(); 0042 } 0043 0044 int AbstractModel::rowCount(const QModelIndex &parent) const 0045 { 0046 Q_UNUSED(parent); 0047 return m_map->count(); 0048 } 0049 0050 QVariant AbstractModel::data(const QModelIndex &index, int role) const 0051 { 0052 auto data = m_map->values().at(index.row()); 0053 Q_ASSERT(data); 0054 if (role == ObjectRole) { 0055 return QVariant::fromValue(data); 0056 } 0057 int property = m_objectProperties.value(role, -1); 0058 if (property == -1) { 0059 qCDebug(PLASMAPK) << "failed to resolve property " << role; 0060 return QVariant(); 0061 } 0062 return data->metaObject()->property(property).read(data); 0063 } 0064 0065 int AbstractModel::role(const QByteArray &roleName) const 0066 { 0067 qCDebug(PLASMAPK) << roleName << m_roles.key(roleName, -1); 0068 return m_roles.key(roleName, -1); 0069 } 0070 0071 void AbstractModel::initRoleNames(const QMetaObject &qobjectMetaObject) 0072 { 0073 m_roles[ObjectRole] = QByteArrayLiteral("ModelObject"); 0074 0075 QMetaEnum enumerator; 0076 for (int i = 0; i < metaObject()->enumeratorCount(); ++i) { 0077 if (metaObject()->enumerator(i).name() == QLatin1String("ItemRole")) { 0078 enumerator = metaObject()->enumerator(i); 0079 break; 0080 } 0081 } 0082 0083 for (int i = 0; i < enumerator.keyCount(); ++i) { 0084 // Clip the Role suffix and glue it in the hash. 0085 const int roleLength = 4; 0086 QByteArray key(enumerator.key(i)); 0087 // Enum values must end in Role or the enum is crap 0088 Q_ASSERT(key.right(roleLength) == QByteArrayLiteral("Role")); 0089 key.chop(roleLength); 0090 m_roles[enumerator.value(i)] = key; 0091 } 0092 0093 int maxEnumValue = -1; 0094 for (auto it = m_roles.constBegin(); it != m_roles.constEnd(); ++it) { 0095 if (it.key() > maxEnumValue) { 0096 maxEnumValue = it.key(); 0097 } 0098 } 0099 Q_ASSERT(maxEnumValue != -1); 0100 auto mo = qobjectMetaObject; 0101 for (int i = 0; i < mo.propertyCount(); ++i) { 0102 QMetaProperty property = mo.property(i); 0103 QString name(property.name()); 0104 // Coerece first char to be lower case to ensure we are consistent there. 0105 name.replace(0, 1, name.at(0).toLower()); 0106 m_roles[++maxEnumValue] = name.toLatin1(); 0107 m_objectProperties.insert(maxEnumValue, i); 0108 if (!property.hasNotifySignal()) { 0109 continue; 0110 } 0111 m_signalIndexToProperties.insert(property.notifySignalIndex(), i); 0112 } 0113 qCDebug(PLASMAPK) << m_roles; 0114 0115 // Connect to property changes also with objects already in model 0116 for (int i = 0; i < m_map->keys().count(); ++i) { 0117 onDataAdded(i); 0118 } 0119 } 0120 0121 void AbstractModel::propertyChanged() 0122 { 0123 if (!sender() || senderSignalIndex() == -1) { 0124 return; 0125 } 0126 int propertyIndex = m_signalIndexToProperties.value(senderSignalIndex(), -1); 0127 if (propertyIndex == -1) { 0128 return; 0129 } 0130 int role = m_objectProperties.key(propertyIndex, -1); 0131 if (role == -1) { 0132 return; 0133 } 0134 int index = m_map->values().indexOf(sender()); 0135 qCDebug(PLASMAPK) << "PROPERTY CHANGED (" << index << ") :: " << role << roleNames().value(role); 0136 emit dataChanged(createIndex(index, 0), createIndex(index, 0), {role}); 0137 } 0138 0139 void AbstractModel::onDataAdded(int index) 0140 { 0141 beginInsertRows(QModelIndex(), index, index); 0142 QObject *data = m_map->values().at(index); 0143 const QMetaObject *mo = data->metaObject(); 0144 // We have all the data changed notify signals already stored 0145 auto keys = m_signalIndexToProperties.keys(); 0146 foreach (int index, keys) { 0147 QMetaMethod meth = mo->method(index); 0148 connect(data, meth, this, propertyChangedMetaMethod()); 0149 } 0150 endInsertRows(); 0151 } 0152 0153 void AbstractModel::onDataRemoved(int index) 0154 { 0155 beginRemoveRows(QModelIndex(), index, index); 0156 endRemoveRows(); 0157 } 0158 0159 QMetaMethod AbstractModel::propertyChangedMetaMethod() const 0160 { 0161 auto mo = metaObject(); 0162 int methodIndex = mo->indexOfMethod("propertyChanged()"); 0163 if (methodIndex == -1) { 0164 return QMetaMethod(); 0165 } 0166 return mo->method(methodIndex); 0167 }