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"