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 }