File indexing completed on 2024-11-24 04:53:13
0001 /* Copyright (C) 2006 - 2014 Jan Kundrát <jkt@flaska.net> 0002 0003 This file is part of the Trojita Qt IMAP e-mail client, 0004 http://trojita.flaska.net/ 0005 0006 This program is free software; you can redistribute it and/or 0007 modify it under the terms of the GNU General Public License as 0008 published by the Free Software Foundation; either version 2 of 0009 the License or (at your option) version 3 or any later version 0010 accepted by the membership of KDE e.V. (or its successor approved 0011 by the membership of KDE e.V.), which shall act as a proxy 0012 defined in Section 14 of version 3 of the license. 0013 0014 This program is distributed in the hope that it will be useful, 0015 but WITHOUT ANY WARRANTY; without even the implied warranty of 0016 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 0017 GNU General Public License for more details. 0018 0019 You should have received a copy of the GNU General Public License 0020 along with this program. If not, see <http://www.gnu.org/licenses/>. 0021 */ 0022 0023 #include "SubtreeModel.h" 0024 #include "MailboxModel.h" 0025 #include "Model.h" 0026 0027 namespace Imap 0028 { 0029 namespace Mailbox 0030 { 0031 0032 class SubtreeClassAdaptor { 0033 public: 0034 virtual ~SubtreeClassAdaptor() {} 0035 virtual QModelIndex parentCreateIndex(const QAbstractItemModel *sourceModel, 0036 const int row, const int column, void *internalPointer) const = 0; 0037 virtual void assertCorrectClass(const QAbstractItemModel *model) const = 0; 0038 }; 0039 0040 0041 template <typename SourceModel> 0042 class SubtreeClassSpecificItem: public SubtreeClassAdaptor { 0043 virtual QModelIndex parentCreateIndex(const QAbstractItemModel *sourceModel, 0044 const int row, const int column, void *internalPointer) const override { 0045 return qobject_cast<const SourceModel*>(sourceModel)->createIndex(row, column, internalPointer); 0046 } 0047 void assertCorrectClass(const QAbstractItemModel *model) const override { 0048 Q_ASSERT(qobject_cast<const SourceModel*>(model)); 0049 } 0050 }; 0051 0052 0053 SubtreeModelOfMailboxModel::SubtreeModelOfMailboxModel(QObject *parent): 0054 SubtreeModel(parent, new SubtreeClassSpecificItem<MailboxModel>()) 0055 { 0056 } 0057 0058 SubtreeModelOfModel::SubtreeModelOfModel(QObject *parent): 0059 SubtreeModel(parent, new SubtreeClassSpecificItem<Model>()) 0060 { 0061 } 0062 0063 SubtreeModel::SubtreeModel(QObject *parent, SubtreeClassAdaptor *classSpecificAdaptor): 0064 QAbstractProxyModel(parent), m_classAdaptor(classSpecificAdaptor), m_usingInvalidRoot(false) 0065 { 0066 connect(this, &QAbstractItemModel::layoutChanged, this, &SubtreeModel::validityChanged); 0067 connect(this, &QAbstractItemModel::modelReset, this, &SubtreeModel::validityChanged); 0068 connect(this, &QAbstractItemModel::rowsInserted, this, &SubtreeModel::validityChanged); 0069 connect(this, &QAbstractItemModel::rowsRemoved, this, &SubtreeModel::validityChanged); 0070 } 0071 0072 SubtreeModel::~SubtreeModel() 0073 { 0074 delete m_classAdaptor; 0075 } 0076 0077 void SubtreeModel::setSourceModel(QAbstractItemModel *sourceModel) 0078 { 0079 m_classAdaptor->assertCorrectClass(sourceModel); 0080 0081 if (this->sourceModel()) { 0082 disconnect(this->sourceModel(), nullptr, this, nullptr); 0083 } 0084 QAbstractProxyModel::setSourceModel(sourceModel); 0085 if (sourceModel) { 0086 // FIXME: will need to be expanded when the source model supports more signals... 0087 connect(sourceModel, &QAbstractItemModel::modelAboutToBeReset, this, &SubtreeModel::handleModelAboutToBeReset); 0088 connect(sourceModel, &QAbstractItemModel::modelReset, this, &SubtreeModel::handleModelReset); 0089 connect(sourceModel, &QAbstractItemModel::layoutAboutToBeChanged, this, &QAbstractItemModel::layoutAboutToBeChanged); 0090 connect(sourceModel, &QAbstractItemModel::layoutChanged, this, &QAbstractItemModel::layoutChanged); 0091 connect(sourceModel, &QAbstractItemModel::dataChanged, this, &SubtreeModel::handleDataChanged); 0092 connect(sourceModel, &QAbstractItemModel::rowsAboutToBeRemoved, this, &SubtreeModel::handleRowsAboutToBeRemoved); 0093 connect(sourceModel, &QAbstractItemModel::rowsRemoved, this, &SubtreeModel::handleRowsRemoved); 0094 connect(sourceModel, &QAbstractItemModel::rowsAboutToBeInserted, this, &SubtreeModel::handleRowsAboutToBeInserted); 0095 connect(sourceModel, &QAbstractItemModel::rowsInserted, this, &SubtreeModel::handleRowsInserted); 0096 } 0097 } 0098 0099 void SubtreeModel::setRootItem(QModelIndex rootIndex) 0100 { 0101 beginResetModel(); 0102 if (rootIndex.model() != m_rootIndex.model() && rootIndex.model()) { 0103 setSourceModel(const_cast<QAbstractItemModel*>(rootIndex.model())); 0104 } 0105 m_rootIndex = rootIndex; 0106 m_usingInvalidRoot = !m_rootIndex.isValid(); 0107 endResetModel(); 0108 } 0109 0110 void SubtreeModel::setRootItemByOffset(const int row) 0111 { 0112 setRootItem(mapToSource(index(row, 0))); 0113 } 0114 0115 void SubtreeModel::setRootOneLevelUp() 0116 { 0117 setRootItem(parentOfRoot()); 0118 } 0119 0120 void SubtreeModel::setOriginalRoot() 0121 { 0122 setRootItem(QModelIndex()); 0123 } 0124 0125 QModelIndex SubtreeModel::parentOfRoot() const 0126 { 0127 return m_rootIndex.parent(); 0128 } 0129 0130 void SubtreeModel::handleModelAboutToBeReset() 0131 { 0132 beginResetModel(); 0133 } 0134 0135 void SubtreeModel::handleModelReset() 0136 { 0137 endResetModel(); 0138 } 0139 0140 void SubtreeModel::handleDataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight) 0141 { 0142 QModelIndex first = mapFromSource(topLeft); 0143 QModelIndex second = mapFromSource(bottomRight); 0144 0145 if (! first.isValid() || ! second.isValid()) { 0146 if (m_usingInvalidRoot) { 0147 emit dataChanged(QModelIndex(), QModelIndex()); 0148 } else { 0149 // It's something completely alien... 0150 } 0151 return; 0152 } 0153 0154 if (first.parent() == second.parent() && first.column() == second.column()) { 0155 emit dataChanged(first, second); 0156 } else { 0157 // FIXME: batched updates aren't supported yet 0158 Q_ASSERT(false); 0159 } 0160 } 0161 0162 QModelIndex SubtreeModel::mapToSource(const QModelIndex &proxyIndex) const 0163 { 0164 if (!proxyIndex.isValid()) 0165 return m_rootIndex; 0166 0167 if (!sourceModel()) 0168 return QModelIndex(); 0169 0170 return m_classAdaptor->parentCreateIndex(sourceModel(), proxyIndex.row(), proxyIndex.column(), proxyIndex.internalPointer()); 0171 } 0172 0173 QModelIndex SubtreeModel::mapFromSource(const QModelIndex &sourceIndex) const 0174 { 0175 if (!sourceIndex.isValid()) 0176 return QModelIndex(); 0177 0178 Q_ASSERT(sourceIndex.model() == sourceModel()); 0179 0180 if (!isVisibleIndex(sourceIndex)) 0181 return QModelIndex(); 0182 0183 Q_ASSERT(sourceIndex.column() == 0); 0184 0185 if (sourceIndex.internalPointer() == m_rootIndex.internalPointer()) 0186 return QModelIndex(); 0187 0188 return createIndex(sourceIndex.row(), 0, sourceIndex.internalPointer()); 0189 } 0190 0191 /** @short Return true iff the passed source index is in the source model located in the current subtree */ 0192 bool SubtreeModel::isVisibleIndex(QModelIndex sourceIndex) const 0193 { 0194 if (m_usingInvalidRoot) { 0195 // everything is visible in this case 0196 return true; 0197 } 0198 0199 if (!m_rootIndex.isValid()) { 0200 return false; 0201 } 0202 while (sourceIndex.isValid()) { 0203 if (sourceIndex == m_rootIndex) 0204 return true; 0205 sourceIndex = sourceIndex.parent(); 0206 } 0207 return false; 0208 } 0209 0210 QModelIndex SubtreeModel::index(int row, int column, const QModelIndex &parent) const 0211 { 0212 if (!sourceModel()) 0213 return QModelIndex(); 0214 0215 if (row < 0 || column != 0) 0216 return QModelIndex(); 0217 0218 if (parent.isValid()) { 0219 return mapFromSource(mapToSource(parent).model()->index(row, column, mapToSource(parent))); 0220 } else if (m_rootIndex.isValid() || m_usingInvalidRoot) { 0221 return mapFromSource(sourceModel()->index(row, column, m_rootIndex)); 0222 } else { 0223 return QModelIndex(); 0224 } 0225 } 0226 0227 QModelIndex SubtreeModel::parent(const QModelIndex &child) const 0228 { 0229 return mapFromSource(mapToSource(child).parent()); 0230 } 0231 0232 int SubtreeModel::rowCount(const QModelIndex &parent) const 0233 { 0234 if (!sourceModel()) 0235 return 0; 0236 if (parent.isValid()) { 0237 return sourceModel()->rowCount(mapToSource(parent)); 0238 } else if (m_rootIndex.isValid() || m_usingInvalidRoot) { 0239 return sourceModel()->rowCount(m_rootIndex); 0240 } else { 0241 return 0; 0242 } 0243 } 0244 0245 int SubtreeModel::columnCount(const QModelIndex &parent) const 0246 { 0247 if (!sourceModel()) 0248 return 0; 0249 if (parent.isValid()) { 0250 return sourceModel()->columnCount(mapToSource(parent)); 0251 } else if (m_rootIndex.isValid() || m_usingInvalidRoot) { 0252 return sourceModel()->columnCount(m_rootIndex); 0253 } else { 0254 return 0; 0255 } 0256 } 0257 0258 void SubtreeModel::handleRowsAboutToBeRemoved(const QModelIndex &parent, int first, int last) 0259 { 0260 if (!isVisibleIndex(parent)) 0261 return; 0262 beginRemoveRows(mapFromSource(parent), first, last); 0263 } 0264 0265 void SubtreeModel::handleRowsRemoved(const QModelIndex &parent, int first, int last) 0266 { 0267 Q_UNUSED(first); 0268 Q_UNUSED(last); 0269 0270 if (!m_usingInvalidRoot && !m_rootIndex.isValid()) { 0271 // This is our chance to report back about everything being deleted 0272 beginResetModel(); 0273 endResetModel(); 0274 return; 0275 } 0276 0277 if (!isVisibleIndex(parent)) 0278 return; 0279 endRemoveRows(); 0280 } 0281 0282 void SubtreeModel::handleRowsAboutToBeInserted(const QModelIndex &parent, int first, int last) 0283 { 0284 if (!isVisibleIndex(parent)) 0285 return; 0286 beginInsertRows(mapFromSource(parent), first, last); 0287 } 0288 0289 void SubtreeModel::handleRowsInserted(const QModelIndex &parent, int first, int last) 0290 { 0291 Q_UNUSED(first); 0292 Q_UNUSED(last); 0293 0294 if (!isVisibleIndex(parent)) 0295 return; 0296 endInsertRows(); 0297 } 0298 0299 bool SubtreeModel::itemsValid() const 0300 { 0301 return m_usingInvalidRoot || m_rootIndex.isValid(); 0302 } 0303 0304 QModelIndex SubtreeModel::rootIndex() const 0305 { 0306 return m_rootIndex; 0307 } 0308 0309 bool SubtreeModel::hasChildren(const QModelIndex &parent) const 0310 { 0311 if (!sourceModel()) 0312 return false; 0313 if (parent.isValid()) { 0314 return sourceModel()->hasChildren(mapToSource(parent)); 0315 } else if (m_rootIndex.isValid() || m_usingInvalidRoot) { 0316 return sourceModel()->hasChildren(m_rootIndex); 0317 } else { 0318 return false; 0319 } 0320 } 0321 0322 /** @short Reimplemented; this is needed because QAbstractProxyModel does not provide this forward */ 0323 bool SubtreeModel::dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) 0324 { 0325 if (!sourceModel()) 0326 return false; 0327 if (!m_usingInvalidRoot && !m_rootIndex.isValid()) 0328 return false; 0329 return sourceModel()->dropMimeData(data, action, row, column, 0330 parent.isValid() ? mapToSource(parent) : QModelIndex(m_rootIndex)); 0331 } 0332 0333 QHash<int,QByteArray> SubtreeModel::roleNames() const 0334 { 0335 if (sourceModel()) 0336 return sourceModel()->roleNames(); 0337 else 0338 return QHash<int, QByteArray>(); 0339 } 0340 0341 } 0342 }