File indexing completed on 2024-12-22 05:15:21

0001 /*
0002     SPDX-FileCopyrightText: 2015 Eike Hein <hein@kde.org>
0003     SPDX-FileCopyrightText: 2017 Ivan Cukic <ivan.cukic@kde.org>
0004 
0005     SPDX-License-Identifier: GPL-2.0-or-later
0006 */
0007 
0008 #include "placeholdermodel.h"
0009 #include "actionlist.h"
0010 #include "debug.h"
0011 #include <chrono>
0012 
0013 using namespace std::chrono_literals;
0014 
0015 PlaceholderModel::PlaceholderModel(QObject *parent)
0016     : AbstractModel(parent)
0017     , m_dropPlaceholderIndex(-1)
0018     , m_isTriggerInhibited(false)
0019 {
0020     connect(&m_triggerInhibitor, &QTimer::timeout, this, [&] {
0021         qCDebug(KICKER_DEBUG) << "%%% Inhibit stopped";
0022         m_isTriggerInhibited = false;
0023     });
0024 
0025     m_triggerInhibitor.setInterval(500ms);
0026     m_triggerInhibitor.setSingleShot(true);
0027 }
0028 
0029 void PlaceholderModel::inhibitTriggering()
0030 {
0031     qCDebug(KICKER_DEBUG) << "%%% Inhibit started";
0032     m_isTriggerInhibited = true;
0033     m_triggerInhibitor.start();
0034 }
0035 
0036 PlaceholderModel::~PlaceholderModel()
0037 {
0038 }
0039 
0040 QString PlaceholderModel::description() const
0041 {
0042     if (auto abstractModel = qobject_cast<AbstractModel *>(m_sourceModel)) {
0043         return abstractModel->description();
0044 
0045     } else {
0046         return QString();
0047     }
0048 }
0049 
0050 QAbstractItemModel *PlaceholderModel::sourceModel() const
0051 {
0052     return m_sourceModel;
0053 }
0054 
0055 void PlaceholderModel::setSourceModel(QAbstractItemModel *sourceModel)
0056 {
0057     disconnectSignals();
0058 
0059     beginResetModel();
0060 
0061     m_sourceModel = sourceModel;
0062 
0063     connectSignals();
0064 
0065     endResetModel();
0066 
0067     Q_EMIT countChanged();
0068     Q_EMIT sourceModelChanged();
0069     Q_EMIT descriptionChanged();
0070 }
0071 
0072 bool PlaceholderModel::canFetchMore(const QModelIndex &parent) const
0073 {
0074     return m_sourceModel && m_sourceModel->canFetchMore(indexToSourceIndex(parent));
0075 }
0076 
0077 void PlaceholderModel::fetchMore(const QModelIndex &parent)
0078 {
0079     if (m_sourceModel) {
0080         m_sourceModel->fetchMore(indexToSourceIndex(parent));
0081     }
0082 }
0083 
0084 QModelIndex PlaceholderModel::index(int row, int column, const QModelIndex &parent) const
0085 {
0086     Q_UNUSED(parent)
0087 
0088     return m_sourceModel ? createIndex(row, column) : QModelIndex();
0089 }
0090 
0091 QModelIndex PlaceholderModel::parent(const QModelIndex &index) const
0092 {
0093     Q_UNUSED(index)
0094 
0095     return QModelIndex();
0096 }
0097 
0098 QVariant PlaceholderModel::data(const QModelIndex &index, int role) const
0099 {
0100     const auto row = index.row();
0101 
0102     if (m_dropPlaceholderIndex == row) {
0103         switch (role) {
0104         case Kicker::IsDropPlaceholderRole:
0105             return true;
0106 
0107             // TODO: Maybe it would be nice to show something here?
0108             // case Qt::DisplayRole:
0109             //     return "placeholder";
0110             //
0111             // case Qt::DecorationRole:
0112             //     return "select";
0113 
0114         default:
0115             return QVariant();
0116         }
0117     }
0118 
0119     return m_sourceModel ? m_sourceModel->data(indexToSourceIndex(index), role) : QVariant();
0120 }
0121 
0122 int PlaceholderModel::rowCount(const QModelIndex &parent) const
0123 {
0124     if (!m_sourceModel || parent.isValid()) {
0125         return 0;
0126     }
0127 
0128     return m_sourceModel->rowCount() + (m_dropPlaceholderIndex != -1 ? 1 : 0);
0129 }
0130 
0131 QModelIndex PlaceholderModel::indexToSourceIndex(const QModelIndex &index) const
0132 {
0133     if (!m_sourceModel || !index.isValid()) {
0134         return QModelIndex();
0135     }
0136 
0137     const auto row = index.row();
0138     const auto column = index.column();
0139 
0140     return index.parent().isValid() ?
0141                                     // We do not support tree models
0142         QModelIndex()
0143                                     :
0144 
0145                                     // If we are on top-level, lets add a placeholder
0146         m_sourceModel->index(row - (m_dropPlaceholderIndex != -1 && row > m_dropPlaceholderIndex ? 1 : 0), column, QModelIndex());
0147 }
0148 
0149 int PlaceholderModel::sourceRowToRow(int sourceRow) const
0150 {
0151     return sourceRow + (m_dropPlaceholderIndex != -1 && sourceRow >= m_dropPlaceholderIndex ? 1 : 0);
0152 }
0153 
0154 int PlaceholderModel::rowToSourceRow(int row) const
0155 {
0156     return row == m_dropPlaceholderIndex ? -1 : row - (m_dropPlaceholderIndex != -1 && row > m_dropPlaceholderIndex ? 1 : 0);
0157 }
0158 
0159 QModelIndex PlaceholderModel::sourceIndexToIndex(const QModelIndex &sourceIndex) const
0160 {
0161     if (!m_sourceModel || !sourceIndex.isValid()) {
0162         return QModelIndex();
0163     }
0164 
0165     const auto sourceRow = sourceIndex.row();
0166     const auto sourceColumn = sourceIndex.column();
0167 
0168     return sourceIndex.parent().isValid() ?
0169                                           // We do not support tree-models
0170         QModelIndex()
0171                                           :
0172 
0173                                           // If we are on top-level, lets add a placeholder
0174         index(sourceRowToRow(sourceRow), sourceColumn, QModelIndex());
0175 }
0176 
0177 bool PlaceholderModel::trigger(int row, const QString &actionId, const QVariant &argument)
0178 {
0179     if (m_isTriggerInhibited)
0180         return false;
0181 
0182     if (auto abstractModel = qobject_cast<AbstractModel *>(m_sourceModel)) {
0183         return abstractModel->trigger(rowToSourceRow(row), actionId, argument);
0184 
0185     } else {
0186         return false;
0187     }
0188 }
0189 
0190 QString PlaceholderModel::labelForRow(int row)
0191 {
0192     if (auto abstractModel = qobject_cast<AbstractModel *>(m_sourceModel)) {
0193         return abstractModel->labelForRow(rowToSourceRow(row));
0194 
0195     } else {
0196         return QString();
0197     }
0198 }
0199 
0200 AbstractModel *PlaceholderModel::modelForRow(int row)
0201 {
0202     if (auto abstractModel = qobject_cast<AbstractModel *>(m_sourceModel)) {
0203         return abstractModel->modelForRow(rowToSourceRow(row));
0204 
0205     } else {
0206         return nullptr;
0207     }
0208 }
0209 
0210 AbstractModel *PlaceholderModel::favoritesModel()
0211 {
0212     if (auto abstractModel = qobject_cast<AbstractModel *>(m_sourceModel)) {
0213         return abstractModel->favoritesModel();
0214 
0215     } else {
0216         return AbstractModel::favoritesModel();
0217     }
0218 }
0219 
0220 int PlaceholderModel::separatorCount() const
0221 {
0222     if (auto abstractModel = qobject_cast<AbstractModel *>(m_sourceModel)) {
0223         return abstractModel->separatorCount();
0224 
0225     } else {
0226         return 0;
0227     }
0228 }
0229 
0230 void PlaceholderModel::reset()
0231 {
0232     beginResetModel();
0233     endResetModel();
0234     Q_EMIT countChanged();
0235     Q_EMIT separatorCountChanged();
0236 }
0237 
0238 void PlaceholderModel::connectSignals()
0239 {
0240     if (!m_sourceModel) {
0241         return;
0242     }
0243 
0244     const auto sourceModelPtr = m_sourceModel.data();
0245 
0246     connect(sourceModelPtr, SIGNAL(destroyed()), this, SLOT(reset()));
0247 
0248     connect(sourceModelPtr, &QAbstractItemModel::dataChanged, this, [this](const QModelIndex &from, const QModelIndex &to, const QList<int> &roles) {
0249         Q_EMIT dataChanged(sourceIndexToIndex(from), sourceIndexToIndex(to), roles);
0250     });
0251 
0252     connect(sourceModelPtr, &QAbstractItemModel::rowsAboutToBeInserted, this, [this](const QModelIndex &parent, int from, int to) {
0253         if (parent.isValid()) {
0254             qCWarning(KICKER_DEBUG) << "We do not support tree models";
0255 
0256         } else {
0257             beginInsertRows(QModelIndex(), sourceRowToRow(from), sourceRowToRow(to));
0258         }
0259     });
0260 
0261     connect(sourceModelPtr, &QAbstractItemModel::rowsInserted, this, [this] {
0262         endInsertRows();
0263         Q_EMIT countChanged();
0264     });
0265 
0266     connect(sourceModelPtr,
0267             &QAbstractItemModel::rowsAboutToBeMoved,
0268             this,
0269             [this](const QModelIndex &source, int from, int to, const QModelIndex &dest, int destRow) {
0270                 if (source.isValid() || dest.isValid()) {
0271                     qCWarning(KICKER_DEBUG) << "We do not support tree models";
0272 
0273                 } else {
0274                     beginMoveRows(QModelIndex(), sourceRowToRow(from), sourceRowToRow(to), QModelIndex(), sourceRowToRow(destRow));
0275                 }
0276             });
0277 
0278     connect(sourceModelPtr, &QAbstractItemModel::rowsMoved, this, [this] {
0279         endMoveRows();
0280     });
0281 
0282     connect(sourceModelPtr, &QAbstractItemModel::rowsAboutToBeRemoved, this, [this](const QModelIndex &parent, int from, int to) {
0283         if (parent.isValid()) {
0284             qCWarning(KICKER_DEBUG) << "We do not support tree models";
0285 
0286         } else {
0287             beginRemoveRows(QModelIndex(), sourceRowToRow(from), sourceRowToRow(to));
0288         }
0289     });
0290 
0291     connect(sourceModelPtr, &QAbstractItemModel::rowsRemoved, this, [this] {
0292         endRemoveRows();
0293         Q_EMIT countChanged();
0294     });
0295 
0296     connect(sourceModelPtr, &QAbstractItemModel::modelAboutToBeReset, this, [this] {
0297         beginResetModel();
0298     });
0299 
0300     connect(sourceModelPtr, &QAbstractItemModel::modelReset, this, [this] {
0301         endResetModel();
0302         Q_EMIT countChanged();
0303     });
0304 
0305     // We do not have persistant indices
0306     // connect(sourceModelPtr, &QAbstractItemModel::layoutAboutToBeChanged),
0307     //         this, &PlaceholderModel::layoutAboutToBeChanged);
0308     // connect(sourceModelPtr, &QAbstractItemModel::layoutChanged),
0309     //         this, SIGNAL(layoutChanged(QList<QPersistentModelIndex>,QAbstractItemModel::LayoutChangeHint)),
0310     //         Qt::UniqueConnection);
0311 }
0312 
0313 void PlaceholderModel::disconnectSignals()
0314 {
0315     if (!m_sourceModel) {
0316         return;
0317     }
0318 
0319     disconnect(m_sourceModel, nullptr, this, nullptr);
0320 }
0321 
0322 int PlaceholderModel::dropPlaceholderIndex() const
0323 {
0324     return m_dropPlaceholderIndex;
0325 }
0326 
0327 void PlaceholderModel::setDropPlaceholderIndex(int index)
0328 {
0329     if (index == m_dropPlaceholderIndex)
0330         return;
0331 
0332     inhibitTriggering();
0333 
0334     if (index == -1 && m_dropPlaceholderIndex != -1) {
0335         // Removing the placeholder
0336         beginRemoveRows(QModelIndex(), m_dropPlaceholderIndex, m_dropPlaceholderIndex);
0337         m_dropPlaceholderIndex = index;
0338         endRemoveRows();
0339 
0340         Q_EMIT countChanged();
0341 
0342     } else if (index != -1 && m_dropPlaceholderIndex == -1) {
0343         // Creating the placeholder
0344         beginInsertRows(QModelIndex(), index, index);
0345         m_dropPlaceholderIndex = index;
0346         endInsertRows();
0347 
0348         Q_EMIT countChanged();
0349 
0350     } else if (m_dropPlaceholderIndex != index) {
0351         // Moving the placeholder
0352         int modelTo = index + (index > m_dropPlaceholderIndex ? 1 : 0);
0353 
0354         if (beginMoveRows(QModelIndex(), m_dropPlaceholderIndex, m_dropPlaceholderIndex, QModelIndex(), modelTo)) {
0355             m_dropPlaceholderIndex = index;
0356             endMoveRows();
0357         }
0358     }
0359 
0360     Q_EMIT dropPlaceholderIndexChanged();
0361 }