File indexing completed on 2024-05-19 04:40:05

0001 /*
0002     SPDX-FileCopyrightText: 2010 Aleix Pol <aleixpol@kde.org>
0003 
0004     Split into separate class
0005     SPDX-FileCopyrightText: 2011 Andrey Batyiev <batyiev@gmail.com>
0006 
0007     SPDX-License-Identifier: LGPL-2.0-or-later
0008 */
0009 
0010 #include "vcsfilechangesmodel.h"
0011 
0012 #include "debug.h"
0013 
0014 #include <QIcon>
0015 #include <QMimeDatabase>
0016 
0017 #include <KLocalizedString>
0018 
0019 #include <interfaces/icore.h>
0020 #include <interfaces/iprojectcontroller.h>
0021 
0022 #include <vcs/vcsstatusinfo.h>
0023 
0024 namespace KDevelop
0025 {
0026 
0027 static QString stateToString(KDevelop::VcsStatusInfo::State state)
0028 {
0029     switch(state)
0030     {
0031         case KDevelop::VcsStatusInfo::ItemAdded:
0032             return i18nc("@item file was added to versioncontrolsystem", "Added");
0033         case KDevelop::VcsStatusInfo::ItemDeleted:
0034             return i18nc("@item file was deleted from versioncontrolsystem", "Deleted");
0035         case KDevelop::VcsStatusInfo::ItemHasConflicts:
0036             return i18nc("@item file is conflicting (versioncontrolsystem)", "Has Conflicts");
0037         case KDevelop::VcsStatusInfo::ItemModified:
0038             return i18nc("@item version controlled file was modified", "Modified");
0039         case KDevelop::VcsStatusInfo::ItemUpToDate:
0040             return i18nc("@item file is up to date in versioncontrolsystem", "Up To Date");
0041         case KDevelop::VcsStatusInfo::ItemUnknown:
0042         case KDevelop::VcsStatusInfo::ItemUserState:
0043             return i18nc("@item file is not known to versioncontrolsystem", "Unknown");
0044     }
0045     return i18nc("@item Unknown VCS file status, probably a backend error", "?");
0046 }
0047 
0048 static QIcon stateToIcon(KDevelop::VcsStatusInfo::State state)
0049 {
0050     switch(state)
0051     {
0052         case KDevelop::VcsStatusInfo::ItemAdded:
0053             return QIcon::fromTheme(QStringLiteral("vcs-added"));
0054         case KDevelop::VcsStatusInfo::ItemDeleted:
0055             return QIcon::fromTheme(QStringLiteral("vcs-removed"));
0056         case KDevelop::VcsStatusInfo::ItemHasConflicts:
0057             return QIcon::fromTheme(QStringLiteral("vcs-conflicting"));
0058         case KDevelop::VcsStatusInfo::ItemModified:
0059             return QIcon::fromTheme(QStringLiteral("vcs-locally-modified"));
0060         case KDevelop::VcsStatusInfo::ItemUpToDate:
0061             return QIcon::fromTheme(QStringLiteral("vcs-normal"));
0062         case KDevelop::VcsStatusInfo::ItemUnknown:
0063         case KDevelop::VcsStatusInfo::ItemUserState:
0064             return QIcon::fromTheme(QStringLiteral("unknown"));
0065     }
0066     return QIcon::fromTheme(QStringLiteral("dialog-error"));
0067 }
0068 
0069 VcsFileChangesSortProxyModel::VcsFileChangesSortProxyModel(QObject* parent)
0070     : QSortFilterProxyModel(parent)
0071 {
0072 }
0073 
0074 bool VcsFileChangesSortProxyModel::lessThan(const QModelIndex& source_left, const QModelIndex& source_right) const
0075 {
0076     const auto leftStatus = source_left.data(VcsFileChangesModel::StateRole).value<VcsStatusInfo::State>();
0077     const auto rightStatus = source_right.data(VcsFileChangesModel::StateRole).value<VcsStatusInfo::State>();
0078     if (leftStatus != rightStatus) {
0079         return leftStatus < rightStatus;
0080     }
0081 
0082     const QString leftPath = source_left.data(VcsFileChangesModel::UrlRole).toString();
0083     const QString rightPath = source_right.data(VcsFileChangesModel::UrlRole).toString();
0084     return QString::localeAwareCompare(leftPath, rightPath) < 0;
0085 }
0086 
0087 class VcsStatusInfoItem : public QStandardItem
0088 {
0089 public:
0090     explicit VcsStatusInfoItem(const VcsStatusInfo& info)
0091         : QStandardItem()
0092         , m_info(info) {}
0093 
0094     void setStatus(const VcsStatusInfo& info) {
0095         m_info = info;
0096         emitDataChanged();
0097     }
0098 
0099     QVariant data(int role) const override
0100     {
0101         switch(role) {
0102             case Qt::DisplayRole:
0103                 return stateToString(m_info.state());
0104             case Qt::DecorationRole:
0105                 return stateToIcon(m_info.state());
0106             case VcsFileChangesModel::VcsStatusInfoRole:
0107                 return QVariant::fromValue(m_info);
0108             case VcsFileChangesModel::UrlRole:
0109                 return m_info.url();
0110             case VcsFileChangesModel::StateRole:
0111                 return QVariant::fromValue(m_info.state());
0112         }
0113         return {};
0114     }
0115 
0116 private:
0117     VcsStatusInfo m_info;
0118 };
0119 
0120 class VcsFileChangesModelPrivate
0121 {
0122 public:
0123     bool allowSelection;
0124 };
0125 
0126 VcsFileChangesModel::VcsFileChangesModel(QObject *parent, bool allowSelection)
0127     : QStandardItemModel(parent)
0128     , d_ptr(new VcsFileChangesModelPrivate{allowSelection})
0129 {
0130     setColumnCount(2);
0131     setHeaderData(0, Qt::Horizontal, i18nc("@title:column", "Filename"));
0132     setHeaderData(1, Qt::Horizontal, i18nc("@title:column", "Status"));
0133 }
0134 
0135 VcsFileChangesModel::~VcsFileChangesModel()
0136 {
0137 }
0138 
0139 int VcsFileChangesModel::updateState(QStandardItem *parent, const KDevelop::VcsStatusInfo &status)
0140 {
0141      Q_D(VcsFileChangesModel);
0142 
0143    if(status.state()==VcsStatusInfo::ItemUnknown || status.state()==VcsStatusInfo::ItemUpToDate) {
0144         removeUrl(status.url());
0145         return -1;
0146     } else {
0147         QStandardItem* item = fileItemForUrl(parent, status.url());
0148         if(!item) {
0149             QString path = ICore::self()->projectController()->prettyFileName(status.url(), KDevelop::IProjectController::FormatPlain);
0150             QMimeType mime = status.url().isLocalFile()
0151                 ? QMimeDatabase().mimeTypeForFile(status.url().toLocalFile(), QMimeDatabase::MatchExtension)
0152                 : QMimeDatabase().mimeTypeForUrl(status.url());
0153             QIcon icon = QIcon::fromTheme(mime.iconName());
0154             item = new QStandardItem(icon, path);
0155             auto itStatus = new VcsStatusInfoItem(status);
0156 
0157             if(d->allowSelection) {
0158                 item->setCheckable(true);
0159                 item->setCheckState(status.state() == VcsStatusInfo::ItemUnknown ? Qt::Unchecked : Qt::Checked);
0160             }
0161 
0162             parent->appendRow({ item, itStatus });
0163         } else {
0164             QStandardItem *parent = item->parent();
0165             if(parent == nullptr)
0166                 parent = invisibleRootItem();
0167             auto statusInfoItem = static_cast<VcsStatusInfoItem*>(parent->child(item->row(), 1));
0168             statusInfoItem->setStatus(status);
0169         }
0170 
0171         return item->row();
0172     }
0173 }
0174 
0175 QVariant VcsFileChangesModel::data(const QModelIndex &index, int role) const
0176 {
0177     if (role >= VcsStatusInfoRole && index.column()==0) {
0178         return QStandardItemModel::data(index.sibling(index.row(), 1), role);
0179     }
0180     return QStandardItemModel::data(index, role);
0181 }
0182 
0183 QStandardItem* VcsFileChangesModel::fileItemForUrl(QStandardItem* parent, const QUrl& url) const
0184 {
0185     Q_ASSERT(parent);
0186     if (!parent) {
0187         qCWarning(VCS) << "null QStandardItem passed to" << Q_FUNC_INFO;
0188         return nullptr;
0189     }
0190 
0191     for(int i=0, c=parent->rowCount(); i<c; i++) {
0192         QStandardItem* item = parent->child(i);
0193         if(indexFromItem(item).data(UrlRole).toUrl() == url) {
0194             return parent->child(i);
0195         }
0196     }
0197     return nullptr;
0198 }
0199 
0200 void VcsFileChangesModel::setAllChecked(bool checked)
0201 {
0202     Q_D(VcsFileChangesModel);
0203 
0204     if(!d->allowSelection)
0205         return;
0206     QStandardItem* parent = invisibleRootItem();
0207     for(int i = 0, c = parent->rowCount(); i < c; i++) {
0208         QStandardItem* item = parent->child(i);
0209         item->setCheckState(checked ? Qt::Checked : Qt::Unchecked);
0210     }
0211 }
0212 
0213 QList<QUrl> VcsFileChangesModel::checkedUrls(QStandardItem *parent) const
0214 {
0215     Q_D(const VcsFileChangesModel);
0216 
0217     Q_ASSERT(parent);
0218     if (!parent) {
0219         qCWarning(VCS) << "null QStandardItem passed to" << Q_FUNC_INFO;
0220         return {};
0221     }
0222 
0223     QList<QUrl> ret;
0224     for(int i = 0, c = parent->rowCount(); i < c; i++) {
0225         QStandardItem* item = parent->child(i);
0226         if(!d->allowSelection || item->checkState() == Qt::Checked) {
0227             ret << indexFromItem(item).data(UrlRole).toUrl();
0228         }
0229     }
0230     return ret;
0231 }
0232 
0233 QList<QUrl> VcsFileChangesModel::urls(QStandardItem *parent) const
0234 {
0235     Q_ASSERT(parent);
0236     if (!parent) {
0237         qCWarning(VCS) << "null QStandardItem passed to" << Q_FUNC_INFO;
0238         return {};
0239     }
0240 
0241     QList<QUrl> ret;
0242     const int c = parent->rowCount();
0243     ret.reserve(c);
0244     for (int i = 0; i < c; i++) {
0245         QStandardItem* item = parent->child(i);
0246         ret << indexFromItem(item).data(UrlRole).toUrl();
0247     }
0248     return ret;
0249 }
0250 
0251 void VcsFileChangesModel::checkUrls(QStandardItem *parent, const QList<QUrl>& urls) const
0252 {
0253     Q_D(const VcsFileChangesModel);
0254 
0255     Q_ASSERT(parent);
0256     if (!parent) {
0257         qCWarning(VCS) << "null QStandardItem passed to" << Q_FUNC_INFO;
0258         return;
0259     }
0260 
0261     if(!d->allowSelection)
0262         return;
0263 
0264     const QSet<QUrl> urlSet(urls.begin(), urls.end());
0265     for(int i = 0, c = parent->rowCount(); i < c; i++) {
0266         QStandardItem* item = parent->child(i);
0267         item->setCheckState(urlSet.contains(indexFromItem(item).data(UrlRole).toUrl()) ?
0268             Qt::Checked :
0269             Qt::Unchecked);
0270     }
0271 }
0272 
0273 void VcsFileChangesModel::setIsCheckbable(bool checkable)
0274 {
0275     Q_D(VcsFileChangesModel);
0276 
0277     d->allowSelection = checkable;
0278 }
0279 
0280 bool VcsFileChangesModel::isCheckable() const
0281 {
0282     Q_D(const VcsFileChangesModel);
0283 
0284     return d->allowSelection;
0285 }
0286 
0287 bool VcsFileChangesModel::removeUrl(const QUrl& url)
0288 {
0289     const auto matches = match(index(0, 0), UrlRole, url, 1, Qt::MatchExactly);
0290     if (matches.isEmpty())
0291         return false;
0292 
0293     const auto& idx = matches.first();
0294     return removeRow(idx.row(), idx.parent());
0295 }
0296 
0297 }
0298 
0299 #include "moc_vcsfilechangesmodel.cpp"