File indexing completed on 2024-04-28 04:39:10

0001 /*
0002     SPDX-FileCopyrightText: 2013 Aleix Pol <aleixpol@kde.org>
0003 
0004     SPDX-License-Identifier: LGPL-2.0-or-later
0005 */
0006 
0007 #include "vcsoverlayproxymodel.h"
0008 #include <projectchangesmodel.h>
0009 #include <interfaces/icore.h>
0010 #include <interfaces/iprojectcontroller.h>
0011 #include <interfaces/iproject.h>
0012 #include <interfaces/iruncontroller.h>
0013 #include <interfaces/iplugin.h>
0014 #include <vcs/interfaces/ibranchingversioncontrol.h>
0015 #include <vcs/vcsjob.h>
0016 #include <project/projectmodel.h>
0017 #include <util/path.h>
0018 
0019 #include <KLocalizedString>
0020 
0021 #include <QPointer>
0022 
0023 
0024 using namespace KDevelop;
0025 
0026 using SafeProjectPointer = QPointer<KDevelop::IProject>;
0027 
0028 VcsOverlayProxyModel::VcsOverlayProxyModel(QObject* parent): QIdentityProxyModel(parent)
0029 {
0030     connect(ICore::self()->projectController(), &IProjectController::projectOpened,
0031                                               this, &VcsOverlayProxyModel::addProject);
0032     connect(ICore::self()->projectController(), &IProjectController::projectClosing,
0033                                               this, &VcsOverlayProxyModel::removeProject);
0034 
0035     const auto projects = ICore::self()->projectController()->projects();
0036     for (const auto project : projects) {
0037         addProject(project);
0038     }
0039 }
0040 
0041 QVariant VcsOverlayProxyModel::data(const QModelIndex& proxyIndex, int role) const
0042 {
0043     if(role == VcsStatusRole) {
0044         if (!proxyIndex.parent().isValid()) {
0045             auto* p = qobject_cast<IProject*>(proxyIndex.data(ProjectModel::ProjectRole).value<QObject*>());
0046             return m_branchName.value(p);
0047         } else {
0048             ProjectChangesModel* model = ICore::self()->projectController()->changesModel();
0049             const QUrl url = proxyIndex.data(ProjectModel::UrlRole).toUrl();
0050             const auto idx = model->match(model->index(0, 0), ProjectChangesModel::UrlRole, url, 1, Qt::MatchExactly).value(0);
0051             return idx.sibling(idx.row(), 1).data(Qt::DisplayRole);
0052         }
0053     } else
0054         return QIdentityProxyModel::data(proxyIndex, role);
0055 }
0056 
0057 void VcsOverlayProxyModel::addProject(IProject* p)
0058 {
0059     IPlugin* plugin = p->versionControlPlugin();
0060     if(!plugin)
0061         return;
0062 
0063     // TODO: Show revision in case we're in "detached" state
0064     auto* branchingExtension = plugin->extension<KDevelop::IBranchingVersionControl>();
0065     if(branchingExtension) {
0066         const QUrl url = p->path().toUrl();
0067         branchingExtension->registerRepositoryForCurrentBranchChanges(url);
0068         //can't use new signal/slot syntax here, IBranchingVersionControl is not a QObject
0069         connect(plugin, SIGNAL(repositoryBranchChanged(QUrl)), SLOT(repositoryBranchChanged(QUrl)));
0070         repositoryBranchChanged(url);
0071     }
0072 }
0073 
0074 void VcsOverlayProxyModel::repositoryBranchChanged(const QUrl& url)
0075 {
0076     const QList<IProject*> allProjects = ICore::self()->projectController()->projects();
0077     for (IProject* project : allProjects) {
0078         const bool isExactMatch = url.matches(project->path().toUrl(), QUrl::StripTrailingSlash);
0079         const bool isParentOf = url.isParentOf(project->path().toUrl());
0080         if (isParentOf || isExactMatch) {
0081             // example projects in KDevelop:
0082             // - /path/to/mygitrepo/:          isParentOf=0 isExactMatch=1,
0083             // - /path/to/mygitrepo/myproject: isParentOf=1 isExactMatch=0
0084             // - /path/to/norepo:              isParentOf=0 isExactMatch=0
0085             // isParentOf=1 isExactMatch=1 is not a valid combination
0086 
0087             IPlugin* v = project->versionControlPlugin();
0088             Q_ASSERT(!isExactMatch || v); // project url == 'change' url => project should be associated with a VCS plugin
0089             if (!v) {
0090                 continue;
0091             }
0092 
0093             auto* branching = v->extension<IBranchingVersionControl>();
0094             Q_ASSERT(branching);
0095             VcsJob* job = branching->currentBranch(url);
0096             connect(job, &VcsJob::resultsReady, this, &VcsOverlayProxyModel::branchNameReady);
0097             job->setProperty("project", QVariant::fromValue<SafeProjectPointer>(project));
0098             ICore::self()->runController()->registerJob(job);
0099         }
0100     }
0101 }
0102 
0103 void VcsOverlayProxyModel::branchNameReady(KDevelop::VcsJob* job)
0104 {
0105     const QString noBranchStr = i18nc("Version control: Currently not on a branch", "(no branch)");
0106 
0107     if(job->status()==VcsJob::JobSucceeded) {
0108         SafeProjectPointer p = job->property("project").value<SafeProjectPointer>();
0109         QModelIndex index = indexFromProject(p);
0110         if(index.isValid()) {
0111             IProject* project = p.data();
0112             const auto branchName =  job->fetchResults().toString();
0113             m_branchName[project] = branchName.isEmpty() ? noBranchStr : branchName;
0114             emit dataChanged(index, index);
0115         }
0116     }
0117 }
0118 
0119 void VcsOverlayProxyModel::removeProject(IProject* p)
0120 {
0121     m_branchName.remove(p);
0122 }
0123 
0124 QModelIndex VcsOverlayProxyModel::indexFromProject(QObject* project)
0125 {
0126     for(int i=0; i<rowCount(); i++) {
0127         QModelIndex idx = index(i,0);
0128         if(idx.data(ProjectModel::ProjectRole).value<QObject*>()==project) {
0129             return idx;
0130         }
0131     }
0132     return QModelIndex();
0133 }
0134 
0135 #include "moc_vcsoverlayproxymodel.cpp"