File indexing completed on 2024-04-28 04:37:23
0001 /* 0002 SPDX-FileCopyrightText: 2010 Aleix Pol Gonzalez <aleixpol@kde.org> 0003 0004 SPDX-License-Identifier: GPL-2.0-or-later 0005 */ 0006 0007 #include "projectsourcepage.h" 0008 #include "ui_projectsourcepage.h" 0009 #include <interfaces/iplugin.h> 0010 #include <interfaces/icore.h> 0011 #include <interfaces/iplugincontroller.h> 0012 #include <interfaces/iruncontroller.h> 0013 #include <vcs/interfaces/ibasicversioncontrol.h> 0014 #include <vcs/widgets/vcslocationwidget.h> 0015 #include <vcs/vcsjob.h> 0016 #include <vcs/vcslocation.h> 0017 0018 #include <QVBoxLayout> 0019 0020 #include <KSharedConfig> 0021 #include <KConfigGroup> 0022 #include <KLocalizedString> 0023 #include <KMessageBox> 0024 0025 #include <interfaces/iprojectprovider.h> 0026 0027 using namespace KDevelop; 0028 0029 static const int FROM_FILESYSTEM_SOURCE_INDEX = 0; 0030 0031 ProjectSourcePage::ProjectSourcePage(const QUrl& initial, const QUrl& repoUrl, IPlugin* preSelectPlugin, 0032 QWidget* parent) 0033 : QWidget(parent) 0034 { 0035 m_ui = new Ui::ProjectSourcePage; 0036 m_ui->setupUi(this); 0037 0038 m_ui->status->setCloseButtonVisible(false); 0039 m_ui->status->setMessageType(KMessageWidget::Error); 0040 0041 m_ui->workingDir->setUrl(initial); 0042 m_ui->workingDir->setMode(KFile::Directory); 0043 0044 m_ui->sources->addItem(QIcon::fromTheme(QStringLiteral("folder")), i18nc("@item:inlistbox", "From File System")); 0045 m_plugins.append(nullptr); 0046 0047 int preselectIndex = -1; 0048 IPluginController* pluginManager = ICore::self()->pluginController(); 0049 const QList<IPlugin*> vcsPlugins = pluginManager->allPluginsForExtension( QStringLiteral("org.kdevelop.IBasicVersionControl") ); 0050 m_plugins.reserve(m_plugins.size() + vcsPlugins.size()); 0051 for (IPlugin* p : vcsPlugins) { 0052 if (p == preSelectPlugin) { 0053 preselectIndex = m_plugins.count(); 0054 } 0055 m_plugins.append(p); 0056 m_ui->sources->addItem(QIcon::fromTheme(pluginManager->pluginInfo(p).iconName()), p->extension<IBasicVersionControl>()->name()); 0057 } 0058 0059 const QList<IPlugin*> projectPlugins = pluginManager->allPluginsForExtension( QStringLiteral("org.kdevelop.IProjectProvider") ); 0060 m_plugins.reserve(m_plugins.size() + projectPlugins.size()); 0061 for (IPlugin* p : projectPlugins) { 0062 if (p == preSelectPlugin) { 0063 preselectIndex = m_plugins.count(); 0064 } 0065 m_plugins.append(p); 0066 m_ui->sources->addItem(QIcon::fromTheme(pluginManager->pluginInfo(p).iconName()), p->extension<IProjectProvider>()->name()); 0067 } 0068 0069 if (preselectIndex == -1) { 0070 // "From File System" is quite unlikely to be what the user wants, so default to first real plugin... 0071 const int defaultIndex = (m_plugins.count() > 1) ? 1 : 0; 0072 KConfigGroup configGroup = KSharedConfig::openConfig()->group("Providers"); 0073 preselectIndex = configGroup.readEntry("LastProviderIndex", defaultIndex); 0074 } 0075 preselectIndex = qBound(0, preselectIndex, m_ui->sources->count() - 1); 0076 m_ui->sources->setCurrentIndex(preselectIndex); 0077 setSourceWidget(preselectIndex, repoUrl); 0078 0079 // connect as last step, otherwise KMessageWidget could get both animatedHide() and animatedShow() 0080 // during setup and due to a bug will ignore any but the first call 0081 // Only fixed for KF5 5.32 0082 connect(m_ui->workingDir, &KUrlRequester::textChanged, this, &ProjectSourcePage::reevaluateCorrection); 0083 connect(m_ui->sources, QOverload<int>::of(&QComboBox::currentIndexChanged), 0084 this, &ProjectSourcePage::setSourceIndex); 0085 connect(m_ui->get, &QPushButton::clicked, this, &ProjectSourcePage::checkoutVcsProject); 0086 } 0087 0088 ProjectSourcePage::~ProjectSourcePage() 0089 { 0090 KConfigGroup configGroup = KSharedConfig::openConfig()->group("Providers"); 0091 configGroup.writeEntry("LastProviderIndex", m_ui->sources->currentIndex()); 0092 0093 delete m_ui; 0094 } 0095 0096 void ProjectSourcePage::setSourceIndex(int index) 0097 { 0098 setSourceWidget(index, QUrl()); 0099 } 0100 0101 void ProjectSourcePage::setSourceWidget(int index, const QUrl& repoUrl) 0102 { 0103 m_locationWidget = nullptr; 0104 m_providerWidget = nullptr; 0105 QLayoutItem *child; 0106 while ((child = m_ui->remoteWidgetLayout->takeAt(0)) != nullptr) { 0107 delete child->widget(); 0108 delete child; 0109 } 0110 0111 IBasicVersionControl* vcIface = vcsPerIndex(index); 0112 IProjectProvider* providerIface; 0113 bool found=false; 0114 if(vcIface) { 0115 found=true; 0116 m_locationWidget=vcIface->vcsLocation(m_ui->sourceBox); 0117 connect(m_locationWidget, &VcsLocationWidget::changed, this, &ProjectSourcePage::locationChanged); 0118 0119 // set after connect, to trigger handler 0120 if (!repoUrl.isEmpty()) { 0121 m_locationWidget->setLocation(repoUrl); 0122 } 0123 m_ui->remoteWidgetLayout->addWidget(m_locationWidget); 0124 } else { 0125 providerIface = providerPerIndex(index); 0126 if(providerIface) { 0127 found=true; 0128 m_providerWidget=providerIface->providerWidget(m_ui->sourceBox); 0129 connect(m_providerWidget, &IProjectProviderWidget::changed, this, &ProjectSourcePage::projectChanged); 0130 0131 m_ui->remoteWidgetLayout->addWidget(m_providerWidget); 0132 } 0133 } 0134 reevaluateCorrection(); 0135 0136 m_ui->sourceBox->setVisible(found); 0137 } 0138 0139 IBasicVersionControl* ProjectSourcePage::vcsPerIndex(int index) 0140 { 0141 IPlugin* p = m_plugins.value(index); 0142 if(!p) 0143 return nullptr; 0144 else 0145 return p->extension<KDevelop::IBasicVersionControl>(); 0146 } 0147 0148 IProjectProvider* ProjectSourcePage::providerPerIndex(int index) 0149 { 0150 IPlugin* p = m_plugins.value(index); 0151 if(!p) 0152 return nullptr; 0153 else 0154 return p->extension<KDevelop::IProjectProvider>(); 0155 } 0156 0157 VcsJob* ProjectSourcePage::jobPerCurrent() 0158 { 0159 QUrl url=m_ui->workingDir->url(); 0160 IPlugin* p=m_plugins[m_ui->sources->currentIndex()]; 0161 VcsJob* job=nullptr; 0162 0163 if(auto* iface=p->extension<IBasicVersionControl>()) { 0164 Q_ASSERT(iface && m_locationWidget); 0165 job=iface->createWorkingCopy(m_locationWidget->location(), url); 0166 } else if(m_providerWidget) { 0167 job=m_providerWidget->createWorkingCopy(url); 0168 } 0169 return job; 0170 } 0171 0172 void ProjectSourcePage::checkoutVcsProject() 0173 { 0174 QUrl url=m_ui->workingDir->url(); 0175 QDir d(url.toLocalFile()); 0176 if(!url.isLocalFile() && !d.exists()) { 0177 bool corr = d.mkpath(d.path()); 0178 if(!corr) { 0179 KMessageBox::error(nullptr, i18n("Could not create the directory: %1", d.path())); 0180 return; 0181 } 0182 } 0183 0184 VcsJob* job=jobPerCurrent(); 0185 if (!job) { 0186 return; 0187 } 0188 0189 m_ui->sources->setEnabled(false); 0190 m_ui->sourceBox->setEnabled(false); 0191 m_ui->workingDir->setEnabled(false); 0192 m_ui->get->setEnabled(false); 0193 m_ui->creationProgress->setValue(m_ui->creationProgress->minimum()); 0194 connect(job, &VcsJob::result, this, &ProjectSourcePage::projectReceived); 0195 connect(job, &KJob::percentChanged, this, &ProjectSourcePage::progressChanged); 0196 connect(job, &VcsJob::infoMessage, this, &ProjectSourcePage::infoMessage); 0197 ICore::self()->runController()->registerJob(job); 0198 } 0199 0200 void ProjectSourcePage::progressChanged(KJob*, unsigned long value) 0201 { 0202 m_ui->creationProgress->setValue(value); 0203 } 0204 0205 void ProjectSourcePage::infoMessage(KJob* , const QString& text, const QString& /*rich*/) 0206 { 0207 m_ui->creationProgress->setFormat(i18nc("Format of the progress bar text. progress and info", 0208 "%1 : %p%", text)); 0209 } 0210 0211 void ProjectSourcePage::projectReceived(KJob* job) 0212 { 0213 if (job->error()) { 0214 m_ui->creationProgress->setValue(0); 0215 } else { 0216 m_ui->creationProgress->setValue(m_ui->creationProgress->maximum()); 0217 } 0218 0219 reevaluateCorrection(); 0220 m_ui->creationProgress->setFormat(QStringLiteral("%p%")); 0221 } 0222 0223 void ProjectSourcePage::reevaluateCorrection() 0224 { 0225 //TODO: Probably we should just ignore remote URL's, I don't think we're ever going 0226 //to support checking out to remote directories 0227 const QUrl cwd = m_ui->workingDir->url(); 0228 const QDir dir = cwd.toLocalFile(); 0229 0230 // case where we import a project from local file system 0231 if (m_ui->sources->currentIndex() == FROM_FILESYSTEM_SOURCE_INDEX) { 0232 emit isCorrect(dir.exists()); 0233 return; 0234 } 0235 0236 // all other cases where remote locations need to be specified 0237 bool correct=!cwd.isRelative() && (!cwd.isLocalFile() || QDir(cwd.adjusted(QUrl::RemoveFilename).toLocalFile()).exists()); 0238 emit isCorrect(correct && m_ui->creationProgress->value() == m_ui->creationProgress->maximum()); 0239 0240 const bool validWidget = ((m_locationWidget && m_locationWidget->isCorrect()) || 0241 (m_providerWidget && m_providerWidget->isCorrect())); 0242 const bool isFolderEmpty = (correct && cwd.isLocalFile() && dir.exists() && dir.entryList(QDir::AllEntries | QDir::NoDotAndDotDot).isEmpty()); 0243 const bool validToCheckout = correct && validWidget && (!dir.exists() || isFolderEmpty); 0244 0245 m_ui->get->setEnabled(validToCheckout); 0246 m_ui->creationProgress->setEnabled(validToCheckout); 0247 0248 if(!correct) 0249 setStatus(i18n("You need to specify a valid or nonexistent directory to check out a project")); 0250 else if(!m_ui->get->isEnabled() && m_ui->workingDir->isEnabled() && !validWidget) 0251 setStatus(i18n("You need to specify the source for your remote project")); 0252 else if(!m_ui->get->isEnabled() && m_ui->workingDir->isEnabled() && !isFolderEmpty) 0253 setStatus(i18n("You need to specify an empty folder as your project destination")); 0254 else 0255 clearStatus(); 0256 } 0257 0258 void ProjectSourcePage::locationChanged() 0259 { 0260 Q_ASSERT(m_locationWidget); 0261 if(m_locationWidget->isCorrect()) { 0262 QString currentUrl = m_ui->workingDir->text(); 0263 currentUrl.truncate(currentUrl.lastIndexOf(QLatin1Char('/'))+1); 0264 0265 QUrl current = QUrl::fromUserInput(currentUrl + m_locationWidget->projectName()); 0266 m_ui->workingDir->setUrl(current); 0267 } 0268 else 0269 reevaluateCorrection(); 0270 } 0271 0272 void ProjectSourcePage::projectChanged(const QString& name) 0273 { 0274 Q_ASSERT(m_providerWidget); 0275 QString currentUrl = m_ui->workingDir->text(); 0276 currentUrl.truncate(currentUrl.lastIndexOf(QLatin1Char('/'))+1); 0277 0278 QUrl current = QUrl::fromUserInput(currentUrl + name); 0279 m_ui->workingDir->setUrl(current); 0280 } 0281 0282 void ProjectSourcePage::setStatus(const QString& message) 0283 { 0284 m_ui->status->setText(message); 0285 m_ui->status->animatedShow(); 0286 } 0287 0288 void ProjectSourcePage::clearStatus() 0289 { 0290 m_ui->status->animatedHide(); 0291 } 0292 0293 QUrl ProjectSourcePage::workingDir() const 0294 { 0295 return m_ui->workingDir->url(); 0296 } 0297 0298 #include "moc_projectsourcepage.cpp"