mirror of
https://bitbucket.org/smil3y/kde-extraapps.git
synced 2025-02-26 20:03:10 +00:00
895 lines
29 KiB
C++
895 lines
29 KiB
C++
/* KDevelop CMake Support
|
|
*
|
|
* Copyright 2006 Matt Rogers <mattr@kde.org>
|
|
* Copyright 2007-2013 Aleix Pol <aleixpol@kde.org>
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License
|
|
* as published by the Free Software Foundation; either version 2
|
|
* of the License, or (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
|
* 02110-1301, USA.
|
|
*/
|
|
|
|
#include "cmakemanager.h"
|
|
#include "cmakeedit.h"
|
|
|
|
#include <QDir>
|
|
#include <QThread>
|
|
#include <QFileSystemWatcher>
|
|
#include <QTimer>
|
|
|
|
#include <KPluginFactory>
|
|
#include <KPluginLoader>
|
|
#include <KAboutData>
|
|
#include <KUrl>
|
|
#include <KAction>
|
|
#include <KMessageBox>
|
|
#include <ktexteditor/document.h>
|
|
#include <KStandardDirs>
|
|
|
|
#include <interfaces/icore.h>
|
|
#include <interfaces/idocumentcontroller.h>
|
|
#include <interfaces/iprojectcontroller.h>
|
|
#include <interfaces/iproject.h>
|
|
#include <interfaces/iplugincontroller.h>
|
|
#include <interfaces/iruncontroller.h>
|
|
#include <interfaces/ilanguagecontroller.h>
|
|
#include <interfaces/contextmenuextension.h>
|
|
#include <interfaces/context.h>
|
|
#include <interfaces/idocumentation.h>
|
|
#include <util/environmentgrouplist.h>
|
|
#include <language/highlighting/codehighlighting.h>
|
|
#include <project/projectmodel.h>
|
|
#include <project/helper.h>
|
|
#include <project/interfaces/iprojectbuilder.h>
|
|
#include <project/projectfiltermanager.h>
|
|
#include <language/codecompletion/codecompletion.h>
|
|
#include <language/duchain/duchainlock.h>
|
|
#include <language/duchain/use.h>
|
|
#include <language/duchain/duchain.h>
|
|
|
|
#include "cmakenavigationwidget.h"
|
|
#include "cmakecachereader.h"
|
|
#include "cmakecodecompletionmodel.h"
|
|
#include <generationexpressionsolver.h>
|
|
#include "icmakedocumentation.h"
|
|
|
|
#ifdef CMAKEDEBUGVISITOR
|
|
#include "cmakedebugvisitor.h"
|
|
#endif
|
|
|
|
#include "ui_cmakepossibleroots.h"
|
|
#include "cmakemodelitems.h"
|
|
#include "cmakeprojectdata.h"
|
|
#include "cmakecommitchangesjob.h"
|
|
#include "cmakeimportjob.h"
|
|
#include "cmakeutils.h"
|
|
|
|
Q_DECLARE_METATYPE(KDevelop::IProject*);
|
|
|
|
using namespace KDevelop;
|
|
|
|
K_PLUGIN_FACTORY(CMakeSupportFactory, registerPlugin<CMakeManager>(); )
|
|
K_EXPORT_PLUGIN(CMakeSupportFactory(KAboutData("kdevcmakemanager","kdevcmake", ki18n("CMake Manager"), "0.1", ki18n("Support for managing CMake projects"), KAboutData::License_GPL)))
|
|
|
|
const QString DIALOG_CAPTION = i18n("KDevelop - CMake Support");
|
|
|
|
CMakeManager::CMakeManager( QObject* parent, const QVariantList& )
|
|
: KDevelop::IPlugin( CMakeSupportFactory::componentData(), parent )
|
|
, m_filter( new ProjectFilterManager( this ) )
|
|
{
|
|
KDEV_USE_EXTENSION_INTERFACE( KDevelop::IBuildSystemManager )
|
|
KDEV_USE_EXTENSION_INTERFACE( KDevelop::IProjectFileManager )
|
|
KDEV_USE_EXTENSION_INTERFACE( KDevelop::ILanguageSupport )
|
|
KDEV_USE_EXTENSION_INTERFACE( ICMakeManager)
|
|
|
|
if (hasError()) {
|
|
return;
|
|
}
|
|
|
|
m_highlight = new KDevelop::CodeHighlighting(this);
|
|
|
|
new CodeCompletion(this, new CMakeCodeCompletionModel(this), name());
|
|
|
|
connect(ICore::self()->projectController(), SIGNAL(projectClosing(KDevelop::IProject*)), SLOT(projectClosing(KDevelop::IProject*)));
|
|
|
|
m_fileSystemChangeTimer = new QTimer(this);
|
|
m_fileSystemChangeTimer->setSingleShot(true);
|
|
m_fileSystemChangeTimer->setInterval(100);
|
|
connect(m_fileSystemChangeTimer,SIGNAL(timeout()),SLOT(filesystemBuffererTimeout()));
|
|
}
|
|
|
|
bool CMakeManager::hasError() const
|
|
{
|
|
return KStandardDirs::findExe("cmake").isEmpty();
|
|
}
|
|
|
|
QString CMakeManager::errorDescription() const
|
|
{
|
|
return hasError() ? i18n("cmake is not installed") : QString();
|
|
}
|
|
|
|
CMakeManager::~CMakeManager()
|
|
{}
|
|
|
|
Path CMakeManager::buildDirectory(KDevelop::ProjectBaseItem *item) const
|
|
{
|
|
CMakeFolderItem *fi=dynamic_cast<CMakeFolderItem*>(item);
|
|
Path ret;
|
|
ProjectBaseItem* parent = fi ? fi->formerParent() : item->parent();
|
|
if (parent)
|
|
ret=buildDirectory(parent);
|
|
else
|
|
ret=Path(CMake::currentBuildDir(item->project()));
|
|
|
|
if(fi)
|
|
ret.addPath(fi->buildDir());
|
|
return ret;
|
|
}
|
|
|
|
KDevelop::ProjectFolderItem* CMakeManager::import( KDevelop::IProject *project )
|
|
{
|
|
kDebug(9042) << "== migrating cmake settings";
|
|
CMake::attemptMigrate(project);
|
|
kDebug(9042) << "== completed cmake migration";
|
|
|
|
kDebug(9042) << "== updating cmake settings from model";
|
|
int buildDirCount = CMake::buildDirCount(project);
|
|
for( int i = 0; i < buildDirCount; ++i )
|
|
CMake::updateConfig( project, i );
|
|
kDebug(9042) << "== completed updating cmake settings";
|
|
|
|
const Path cmakeInfoFile(project->projectFile().parent(), "CMakeLists.txt");
|
|
|
|
Path folderPath = project->path();
|
|
kDebug(9042) << "file is" << cmakeInfoFile.toLocalFile();
|
|
|
|
if ( !cmakeInfoFile.isLocalFile() )
|
|
{
|
|
kWarning() << "error. not a local file. CMake support doesn't handle remote projects";
|
|
return 0;
|
|
}
|
|
|
|
if(CMake::hasProjectRootRelative(project))
|
|
{
|
|
QString relative=CMake::projectRootRelative(project);
|
|
folderPath = folderPath.cd(relative);
|
|
}
|
|
else
|
|
{
|
|
KDialog chooseRoot;
|
|
QWidget *e=new QWidget(&chooseRoot);
|
|
Ui::CMakePossibleRoots ui;
|
|
ui.setupUi(e);
|
|
chooseRoot.setMainWidget(e);
|
|
for(Path aux = folderPath; QFile::exists(aux.toLocalFile()+"/CMakeLists.txt"); aux = aux.parent())
|
|
ui.candidates->addItem(aux.toLocalFile());
|
|
|
|
if(ui.candidates->count()>1)
|
|
{
|
|
connect(ui.candidates, SIGNAL(itemActivated(QListWidgetItem*)), &chooseRoot,SLOT(accept()));
|
|
ui.candidates->setMinimumSize(384,192);
|
|
int a=chooseRoot.exec();
|
|
if(!a || !ui.candidates->currentItem())
|
|
{
|
|
return 0;
|
|
}
|
|
const Path choice(ui.candidates->currentItem()->text());
|
|
CMake::setProjectRootRelative(project, folderPath.relativePath(choice));
|
|
folderPath = choice;
|
|
}
|
|
else
|
|
{
|
|
CMake::setProjectRootRelative(project, "./");
|
|
}
|
|
}
|
|
|
|
CMakeFolderItem* rootItem = new CMakeFolderItem(project, project->path(), QString(), 0 );
|
|
QFileSystemWatcher* w = new QFileSystemWatcher(project);
|
|
w->setObjectName(project->name()+"_ProjectWatcher");
|
|
connect(w, SIGNAL(fileChanged(QString)), SLOT(dirtyFile(QString)));
|
|
connect(w, SIGNAL(directoryChanged(QString)), SLOT(directoryChanged(QString)));
|
|
m_watchers[project] = w;
|
|
kDebug(9042) << "Added watcher for project " << project << project->name();
|
|
m_filter->add(project);
|
|
|
|
KUrl cachefile=CMake::currentBuildDir(project);
|
|
if( cachefile.isEmpty() ) {
|
|
CMake::checkForNeedingConfigure(project);
|
|
} else {
|
|
cachefile.addPath("CMakeCache.txt");
|
|
w->addPath(cachefile.toLocalFile());
|
|
}
|
|
|
|
Q_ASSERT(rootItem->rowCount()==0);
|
|
return rootItem;
|
|
}
|
|
|
|
QList<ProjectFolderItem*> CMakeManager::parse(ProjectFolderItem*)
|
|
{ return QList< ProjectFolderItem* >(); }
|
|
|
|
|
|
KJob* CMakeManager::createImportJob(ProjectFolderItem* dom)
|
|
{
|
|
KJob* job = new CMakeImportJob(dom, this);
|
|
connect(job, SIGNAL(finished(KJob*)), SLOT(importFinished(KJob*)));
|
|
return job;
|
|
}
|
|
|
|
QList<KDevelop::ProjectTargetItem*> CMakeManager::targets() const
|
|
{
|
|
QList<KDevelop::ProjectTargetItem*> ret;
|
|
foreach(IProject* p, m_watchers.keys())
|
|
{
|
|
ret+=p->projectItem()->targetList();
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
Path::List CMakeManager::includeDirectories(KDevelop::ProjectBaseItem *item) const
|
|
{
|
|
IProject* project = item->project();
|
|
// kDebug(9042) << "Querying inc dirs for " << item;
|
|
while(item)
|
|
{
|
|
if(CompilationDataAttached* includer = dynamic_cast<CompilationDataAttached*>( item )) {
|
|
QStringList dirs = includer->includeDirectories(item);
|
|
//Here there's the possibility that it might not be a target. We should make sure that's not the case
|
|
ProjectTargetItem* tItem = dynamic_cast<ProjectTargetItem*>(item);
|
|
return CMake::resolveSystemDirs(project, processGeneratorExpression(dirs, project, tItem));
|
|
}
|
|
item = item->parent();
|
|
// kDebug(9042) << "Looking for an includer: " << item;
|
|
}
|
|
// No includer found, so no include-directories to be returned;
|
|
return Path::List();
|
|
}
|
|
|
|
QHash< QString, QString > CMakeManager::defines(KDevelop::ProjectBaseItem *item ) const
|
|
{
|
|
CompilationDataAttached* att=0;
|
|
ProjectBaseItem* it=item;
|
|
// kDebug(9042) << "Querying defines for " << item << dynamic_cast<ProjectTargetItem*>(item);
|
|
while(!att && item)
|
|
{
|
|
att = dynamic_cast<CompilationDataAttached*>( item );
|
|
it = item;
|
|
item = item->parent();
|
|
// kDebug(9042) << "Looking for a folder: " << folder << item;
|
|
}
|
|
if( !att ) {
|
|
// Not a CMake folder, so no defines to be returned;
|
|
return QHash<QString,QString>();
|
|
}
|
|
|
|
CMakeFolderItem* folder = dynamic_cast<CMakeFolderItem*>(it);
|
|
CMakeDefinitions defs = att->definitions(folder ? folder->formerParent() : dynamic_cast<CMakeFolderItem*>(item));
|
|
//qDebug() << "lalala" << defs << it->url();
|
|
return defs;
|
|
}
|
|
|
|
KDevelop::IProjectBuilder * CMakeManager::builder() const
|
|
{
|
|
IPlugin* i = core()->pluginController()->pluginForExtension( "org.kdevelop.IProjectBuilder", "KDevCMakeBuilder");
|
|
Q_ASSERT(i);
|
|
KDevelop::IProjectBuilder* _builder = i->extension<KDevelop::IProjectBuilder>();
|
|
Q_ASSERT(_builder );
|
|
return _builder ;
|
|
}
|
|
|
|
bool CMakeManager::reload(KDevelop::ProjectFolderItem* folder)
|
|
{
|
|
kDebug(9032) << "reloading" << folder->path();
|
|
IProject* p = folder->project();
|
|
if(!p->isReady())
|
|
return false;
|
|
|
|
CMakeFolderItem* fi = dynamic_cast<CMakeFolderItem*>(folder);
|
|
for(ProjectBaseItem* it = folder; !fi && it->parent();) {
|
|
it = it->parent();
|
|
fi = dynamic_cast<CMakeFolderItem*>(it);
|
|
}
|
|
Q_ASSERT(fi && "at least the root item should be a CMakeFolderItem");
|
|
|
|
KJob *job=createImportJob(fi);
|
|
connect(job, SIGNAL(result(KJob*)), SLOT(importFinished(KJob*)));
|
|
p->setReloadJob(job);
|
|
ICore::self()->runController()->registerJob( job );
|
|
return true;
|
|
}
|
|
|
|
void CMakeManager::importFinished(KJob* j)
|
|
{
|
|
CMakeImportJob* job = qobject_cast<CMakeImportJob*>(j);
|
|
Q_ASSERT(job);
|
|
*m_projectsData[job->project()] = job->projectData();
|
|
}
|
|
|
|
void CMakeManager::deletedWatchedDirectory(IProject* p, const KUrl& dir)
|
|
{
|
|
if(p->folder().equals(dir, KUrl::CompareWithoutTrailingSlash)) {
|
|
ICore::self()->projectController()->closeProject(p);
|
|
} else {
|
|
if(dir.fileName()=="CMakeLists.txt") {
|
|
QList<ProjectFolderItem*> folders = p->foldersForUrl(dir.upUrl());
|
|
foreach(ProjectFolderItem* folder, folders)
|
|
reload(folder);
|
|
} else {
|
|
qDeleteAll(p->itemsForUrl(dir));
|
|
}
|
|
}
|
|
}
|
|
|
|
void CMakeManager::directoryChanged(const QString& dir)
|
|
{
|
|
m_fileSystemChangedBuffer << dir;
|
|
m_fileSystemChangeTimer->start();
|
|
}
|
|
|
|
void CMakeManager::filesystemBuffererTimeout()
|
|
{
|
|
Q_FOREACH(const QString& file, m_fileSystemChangedBuffer) {
|
|
realDirectoryChanged(file);
|
|
}
|
|
m_fileSystemChangedBuffer.clear();
|
|
}
|
|
|
|
void CMakeManager::realDirectoryChanged(const QString& dir)
|
|
{
|
|
KUrl path(dir);
|
|
IProject* p=ICore::self()->projectController()->findProjectForUrl(dir);
|
|
if(!p || !p->isReady()) {
|
|
if(p) {
|
|
m_fileSystemChangedBuffer << dir;
|
|
m_fileSystemChangeTimer->start();
|
|
}
|
|
return;
|
|
}
|
|
|
|
if(!QFile::exists(dir)) {
|
|
path.adjustPath(KUrl::AddTrailingSlash);
|
|
deletedWatchedDirectory(p, path);
|
|
} else
|
|
dirtyFile(dir);
|
|
}
|
|
|
|
void CMakeManager::dirtyFile(const QString & dirty)
|
|
{
|
|
const KUrl dirtyFile(dirty);
|
|
IProject* p=ICore::self()->projectController()->findProjectForUrl(dirtyFile);
|
|
|
|
kDebug() << "dirty FileSystem: " << dirty << (p ? p->isReady() : 0);
|
|
if(p)
|
|
{
|
|
if(dirtyFile.fileName() == "CMakeLists.txt") {
|
|
QList<ProjectFileItem*> files=p->filesForUrl(dirtyFile);;
|
|
|
|
Q_ASSERT(files.count()==1);
|
|
CMakeFolderItem *folderItem=static_cast<CMakeFolderItem*>(files.first()->parent());
|
|
#if 0
|
|
KUrl relative=KUrl::relativeUrl(projectBaseUrl, dir);
|
|
initializeProject(proj, dir);
|
|
KUrl current=projectBaseUrl;
|
|
QStringList subs=relative.toLocalFile().split("/");
|
|
subs.append(QString());
|
|
for(; !subs.isEmpty(); current.cd(subs.takeFirst()))
|
|
{
|
|
parseOnly(proj, current);
|
|
}
|
|
#endif
|
|
reload(folderItem);
|
|
}
|
|
else if(QFileInfo(dirty).isDir() && p->isReady())
|
|
{
|
|
QList<ProjectFolderItem*> folders=p->foldersForPath(IndexedString(dirty));
|
|
Q_ASSERT(folders.isEmpty() || folders.size()==1);
|
|
|
|
if(!folders.isEmpty()) {
|
|
CMakeCommitChangesJob* job = new CMakeCommitChangesJob(Path(dirtyFile), this, p);
|
|
job->start();
|
|
}
|
|
}
|
|
}
|
|
else if(dirtyFile.fileName()=="CMakeCache.txt")
|
|
{
|
|
//we first have to check from which project is this builddir
|
|
foreach(KDevelop::IProject* pp, m_watchers.uniqueKeys()) {
|
|
KUrl buildDir = CMake::currentBuildDir(pp);
|
|
if(dirtyFile.upUrl().equals(buildDir, KUrl::CompareWithoutTrailingSlash)) {
|
|
reload(pp->projectItem());
|
|
}
|
|
}
|
|
}
|
|
else if(dirty.endsWith(".cmake"))
|
|
{
|
|
foreach(KDevelop::IProject* project, m_watchers.uniqueKeys())
|
|
{
|
|
if(m_watchers[project]->files().contains(dirty))
|
|
reload(project->projectItem());
|
|
}
|
|
}
|
|
}
|
|
|
|
QList< KDevelop::ProjectTargetItem * > CMakeManager::targets(KDevelop::ProjectFolderItem * folder) const
|
|
{
|
|
return folder->targetList();
|
|
}
|
|
|
|
QString CMakeManager::name() const
|
|
{
|
|
return "CMake";
|
|
}
|
|
|
|
KDevelop::ParseJob * CMakeManager::createParseJob(const IndexedString &/*url*/)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
KDevelop::ILanguage * CMakeManager::language()
|
|
{
|
|
return core()->languageController()->language(name());
|
|
}
|
|
|
|
KDevelop::ICodeHighlighting* CMakeManager::codeHighlighting() const
|
|
{
|
|
return m_highlight;
|
|
}
|
|
|
|
ContextMenuExtension CMakeManager::contextMenuExtension( KDevelop::Context* context )
|
|
{
|
|
if( context->type() != KDevelop::Context::ProjectItemContext )
|
|
return IPlugin::contextMenuExtension( context );
|
|
|
|
KDevelop::ProjectItemContext* ctx = dynamic_cast<KDevelop::ProjectItemContext*>( context );
|
|
QList<KDevelop::ProjectBaseItem*> items = ctx->items();
|
|
|
|
if( items.isEmpty() )
|
|
return IPlugin::contextMenuExtension( context );
|
|
|
|
m_clickedItems = items;
|
|
ContextMenuExtension menuExt;
|
|
if(items.count()==1 && dynamic_cast<DUChainAttatched*>(items.first()))
|
|
{
|
|
KAction* action = new KAction( i18n( "Jump to Target Definition" ), this );
|
|
connect( action, SIGNAL(triggered()), this, SLOT(jumpToDeclaration()) );
|
|
menuExt.addAction( ContextMenuExtension::ProjectGroup, action );
|
|
}
|
|
|
|
return menuExt;
|
|
}
|
|
|
|
void CMakeManager::jumpToDeclaration()
|
|
{
|
|
DUChainAttatched* du=dynamic_cast<DUChainAttatched*>(m_clickedItems.first());
|
|
if(du)
|
|
{
|
|
KTextEditor::Cursor c;
|
|
KUrl url;
|
|
{
|
|
KDevelop::DUChainReadLocker lock;
|
|
Declaration* decl = du->declaration().data();
|
|
if(!decl)
|
|
return;
|
|
c = decl->rangeInCurrentRevision().start.textCursor();
|
|
url = decl->url().toUrl();
|
|
}
|
|
|
|
ICore::self()->documentController()->openDocument(url, c);
|
|
}
|
|
}
|
|
|
|
// TODO: Port to Path API
|
|
bool CMakeManager::moveFilesAndFolders(const QList< ProjectBaseItem* > &items, ProjectFolderItem* toFolder)
|
|
{
|
|
using namespace CMakeEdit;
|
|
|
|
ApplyChangesWidget changesWidget;
|
|
changesWidget.setCaption(DIALOG_CAPTION);
|
|
changesWidget.setInformation(i18n("Move files and folders within CMakeLists as follows:"));
|
|
|
|
bool cmakeSuccessful = true;
|
|
CMakeFolderItem *nearestCMakeFolderItem = nearestCMakeFolder(toFolder);
|
|
IProject* project=toFolder->project();
|
|
|
|
KUrl::List movedUrls;
|
|
KUrl::List oldUrls;
|
|
foreach(ProjectBaseItem *movedItem, items)
|
|
{
|
|
QList<ProjectBaseItem*> dirtyItems = cmakeListedItemsAffectedByUrlChange(project, movedItem->url());
|
|
KUrl movedItemNewUrl = toFolder->url();
|
|
movedItemNewUrl.addPath(movedItem->baseName());
|
|
if (movedItem->folder())
|
|
movedItemNewUrl.adjustPath(KUrl::AddTrailingSlash);
|
|
foreach(ProjectBaseItem* dirtyItem, dirtyItems)
|
|
{
|
|
KUrl dirtyItemNewUrl = afterMoveUrl(dirtyItem->url(), movedItem->url(), movedItemNewUrl);
|
|
if (CMakeFolderItem* folder = dynamic_cast<CMakeFolderItem*>(dirtyItem))
|
|
{
|
|
cmakeSuccessful &= changesWidgetRemoveCMakeFolder(folder, &changesWidget);
|
|
cmakeSuccessful &= changesWidgetAddFolder(dirtyItemNewUrl, nearestCMakeFolderItem, &changesWidget);
|
|
}
|
|
else if (dirtyItem->parent()->target())
|
|
{
|
|
cmakeSuccessful &= changesWidgetMoveTargetFile(dirtyItem, dirtyItemNewUrl, &changesWidget);
|
|
}
|
|
}
|
|
|
|
oldUrls += movedItem->url();
|
|
movedUrls += movedItemNewUrl;
|
|
}
|
|
|
|
if (changesWidget.hasDocuments() && cmakeSuccessful)
|
|
cmakeSuccessful &= changesWidget.exec() && changesWidget.applyAllChanges();
|
|
|
|
if (!cmakeSuccessful)
|
|
{
|
|
if (KMessageBox::questionYesNo( QApplication::activeWindow(),
|
|
i18n("Changes to CMakeLists failed, abort move?"),
|
|
DIALOG_CAPTION ) == KMessageBox::Yes)
|
|
return false;
|
|
}
|
|
|
|
KUrl::List::const_iterator it1=oldUrls.constBegin(), it1End=oldUrls.constEnd();
|
|
KUrl::List::const_iterator it2=movedUrls.constBegin();
|
|
Q_ASSERT(oldUrls.size()==movedUrls.size());
|
|
for(; it1!=it1End; ++it1, ++it2)
|
|
{
|
|
if (!KDevelop::renameUrl(project, *it1, *it2))
|
|
return false;
|
|
|
|
QList<ProjectBaseItem*> renamedItems = project->itemsForUrl(*it2);
|
|
bool dir = QFileInfo(it2->toLocalFile()).isDir();
|
|
foreach(ProjectBaseItem* item, renamedItems) {
|
|
if(dir)
|
|
emit folderRenamed(Path(*it1), item->folder());
|
|
else
|
|
emit fileRenamed(Path(*it1), item->file());
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool CMakeManager::copyFilesAndFolders(const KDevelop::Path::List &items, KDevelop::ProjectFolderItem* toFolder)
|
|
{
|
|
IProject* project = toFolder->project();
|
|
foreach(const Path& path, items) {
|
|
if (!KDevelop::copyUrl(project, path.toUrl(), toFolder->url()))
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool CMakeManager::removeFilesAndFolders(const QList<KDevelop::ProjectBaseItem*> &items)
|
|
{
|
|
using namespace CMakeEdit;
|
|
|
|
IProject* p = 0;
|
|
QList<QUrl> urls;
|
|
foreach(ProjectBaseItem* item, items)
|
|
{
|
|
Q_ASSERT(item->folder() || item->file());
|
|
|
|
urls += item->url();
|
|
if(!p)
|
|
p = item->project();
|
|
}
|
|
|
|
//First do CMakeLists changes
|
|
ApplyChangesWidget changesWidget;
|
|
changesWidget.setCaption(DIALOG_CAPTION);
|
|
changesWidget.setInformation(i18n("Remove files and folders from CMakeLists as follows:"));
|
|
|
|
bool cmakeSuccessful = changesWidgetRemoveItems(cmakeListedItemsAffectedByItemsChanged(items).toSet(), &changesWidget);
|
|
|
|
if (changesWidget.hasDocuments() && cmakeSuccessful)
|
|
cmakeSuccessful &= changesWidget.exec() && changesWidget.applyAllChanges();
|
|
|
|
if (!cmakeSuccessful)
|
|
{
|
|
if (KMessageBox::questionYesNo( QApplication::activeWindow(),
|
|
i18n("Changes to CMakeLists failed, abort deletion?"),
|
|
DIALOG_CAPTION ) == KMessageBox::Yes)
|
|
return false;
|
|
}
|
|
|
|
bool ret = true;
|
|
//Then delete the files/folders
|
|
foreach(const QUrl& file, urls)
|
|
{
|
|
ret &= KDevelop::removeUrl(p, file, QDir(file.toLocalFile()).exists());
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
bool CMakeManager::removeFilesFromTargets(const QList<ProjectFileItem*> &files)
|
|
{
|
|
using namespace CMakeEdit;
|
|
|
|
ApplyChangesWidget changesWidget;
|
|
changesWidget.setCaption(DIALOG_CAPTION);
|
|
changesWidget.setInformation(i18n("Modify project targets as follows:"));
|
|
|
|
if (!files.isEmpty() &&
|
|
changesWidgetRemoveFilesFromTargets(files, &changesWidget) &&
|
|
changesWidget.exec() &&
|
|
changesWidget.applyAllChanges()) {
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
ProjectFolderItem* CMakeManager::addFolder(const Path& folder, ProjectFolderItem* parent)
|
|
{
|
|
using namespace CMakeEdit;
|
|
|
|
CMakeFolderItem *cmakeParent = nearestCMakeFolder(parent);
|
|
if(!cmakeParent)
|
|
return 0;
|
|
|
|
ApplyChangesWidget changesWidget;
|
|
changesWidget.setCaption(DIALOG_CAPTION);
|
|
changesWidget.setInformation(i18n("Create folder '%1':", folder.lastPathSegment()));
|
|
|
|
///FIXME: use path in changes widget
|
|
changesWidgetAddFolder(folder.toUrl(), cmakeParent, &changesWidget);
|
|
|
|
if(changesWidget.exec() && changesWidget.applyAllChanges())
|
|
{
|
|
if(KDevelop::createFolder(folder.toUrl())) { //If saved we create the folder then the CMakeLists.txt file
|
|
Path newCMakeLists(folder, "CMakeLists.txt");
|
|
KDevelop::createFile( newCMakeLists.toUrl() );
|
|
} else
|
|
KMessageBox::error(0, i18n("Could not save the change."),
|
|
DIALOG_CAPTION);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
KDevelop::ProjectFileItem* CMakeManager::addFile( const Path& file, KDevelop::ProjectFolderItem* parent)
|
|
{
|
|
KDevelop::ProjectFileItem* created = 0;
|
|
if ( KDevelop::createFile(file.toUrl()) ) {
|
|
QList< ProjectFileItem* > files = parent->project()->filesForPath(IndexedString(file.pathOrUrl()));
|
|
if(!files.isEmpty())
|
|
created = files.first();
|
|
else
|
|
created = new KDevelop::ProjectFileItem( parent->project(), file, parent );
|
|
}
|
|
return created;
|
|
}
|
|
|
|
bool CMakeManager::addFilesToTarget(const QList< ProjectFileItem* > &_files, ProjectTargetItem* target)
|
|
{
|
|
using namespace CMakeEdit;
|
|
|
|
const QSet<QString> headerExt = QSet<QString>() << ".h" << ".hpp" << ".hxx";
|
|
QList< ProjectFileItem* > files = _files;
|
|
for (int i = files.count() - 1; i >= 0; --i)
|
|
{
|
|
QString fileName = files[i]->fileName();
|
|
QString fileExt = fileName.mid(fileName.lastIndexOf('.'));
|
|
QList<ProjectBaseItem*> sameUrlItems = files[i]->project()->itemsForUrl(files[i]->url());
|
|
if (headerExt.contains(fileExt))
|
|
files.removeAt(i);
|
|
else foreach(ProjectBaseItem* item, sameUrlItems)
|
|
{
|
|
if (item->parent() == target)
|
|
{
|
|
files.removeAt(i);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if(files.isEmpty())
|
|
return true;
|
|
|
|
ApplyChangesWidget changesWidget;
|
|
changesWidget.setCaption(DIALOG_CAPTION);
|
|
changesWidget.setInformation(i18n("Modify target '%1' as follows:", target->baseName()));
|
|
|
|
bool success = changesWidgetAddFilesToTarget(files, target, &changesWidget) &&
|
|
changesWidget.exec() &&
|
|
changesWidget.applyAllChanges();
|
|
|
|
if(!success)
|
|
KMessageBox::error(0, i18n("CMakeLists changes failed."), DIALOG_CAPTION);
|
|
|
|
return success;
|
|
}
|
|
|
|
//TODO: port to Path API
|
|
bool CMakeManager::renameFileOrFolder(ProjectBaseItem *item, const Path &newPath)
|
|
{
|
|
using namespace CMakeEdit;
|
|
|
|
ApplyChangesWidget changesWidget;
|
|
changesWidget.setCaption(DIALOG_CAPTION);
|
|
changesWidget.setInformation(i18n("Rename '%1' to '%2':", item->text(),
|
|
newPath.lastPathSegment()));
|
|
|
|
bool cmakeSuccessful = true, changedCMakeLists=false;
|
|
IProject* project=item->project();
|
|
const Path oldPath=item->path();
|
|
KUrl oldUrl=oldPath.toUrl();
|
|
if (item->file())
|
|
{
|
|
QList<ProjectBaseItem*> targetFiles = cmakeListedItemsAffectedByUrlChange(project, oldUrl);
|
|
foreach(ProjectBaseItem* targetFile, targetFiles)
|
|
///FIXME: use path in changes widget
|
|
cmakeSuccessful &= changesWidgetMoveTargetFile(targetFile, newPath.toUrl(), &changesWidget);
|
|
}
|
|
else if (CMakeFolderItem *folder = dynamic_cast<CMakeFolderItem*>(item))
|
|
///FIXME: use path in changes widget
|
|
cmakeSuccessful &= changesWidgetRenameFolder(folder, newPath.toUrl(), &changesWidget);
|
|
|
|
item->setPath(newPath);
|
|
if (changesWidget.hasDocuments() && cmakeSuccessful) {
|
|
changedCMakeLists = changesWidget.exec() && changesWidget.applyAllChanges();
|
|
cmakeSuccessful &= changedCMakeLists;
|
|
}
|
|
|
|
if (!cmakeSuccessful)
|
|
{
|
|
if (KMessageBox::questionYesNo( QApplication::activeWindow(),
|
|
i18n("Changes to CMakeLists failed, abort rename?"),
|
|
DIALOG_CAPTION ) == KMessageBox::Yes)
|
|
return false;
|
|
}
|
|
|
|
bool ret = KDevelop::renameUrl(project, oldUrl, newPath.toUrl());
|
|
if(!ret) {
|
|
item->setPath(oldPath);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
bool CMakeManager::renameFile(ProjectFileItem *item, const Path &newPath)
|
|
{
|
|
return renameFileOrFolder(item, newPath);
|
|
}
|
|
|
|
bool CMakeManager::renameFolder(ProjectFolderItem* item, const Path &newPath)
|
|
{
|
|
return renameFileOrFolder(item, newPath);
|
|
}
|
|
|
|
QWidget* CMakeManager::specialLanguageObjectNavigationWidget(const KUrl& url, const KDevelop::SimpleCursor& position)
|
|
{
|
|
KDevelop::TopDUContextPointer top= TopDUContextPointer(KDevelop::DUChain::self()->chainForDocument(url));
|
|
Declaration *decl=0;
|
|
if(top)
|
|
{
|
|
int useAt=top->findUseAt(top->transformToLocalRevision(position));
|
|
if(useAt>=0)
|
|
{
|
|
Use u=top->uses()[useAt];
|
|
decl=u.usedDeclaration(top->topContext());
|
|
}
|
|
}
|
|
|
|
CMakeNavigationWidget* doc=0;
|
|
if(decl)
|
|
{
|
|
doc=new CMakeNavigationWidget(top, decl);
|
|
}
|
|
else
|
|
{
|
|
const IDocument* d=ICore::self()->documentController()->documentForUrl(url);
|
|
const KTextEditor::Document* e=d->textDocument();
|
|
KTextEditor::Cursor start=position.textCursor(), end=position.textCursor(), step(0,1);
|
|
for(QChar i=e->character(start); i.isLetter() || i=='_'; i=e->character(start-=step))
|
|
{}
|
|
start+=step;
|
|
|
|
for(QChar i=e->character(end); i.isLetter() || i=='_'; i=e->character(end+=step))
|
|
{}
|
|
|
|
QString id=e->text(KTextEditor::Range(start, end));
|
|
ICMakeDocumentation* docu=CMake::cmakeDocumentation();
|
|
if( docu )
|
|
{
|
|
KSharedPtr<IDocumentation> desc=docu->description(id, url);
|
|
if(!desc.isNull())
|
|
{
|
|
doc=new CMakeNavigationWidget(top, desc);
|
|
}
|
|
}
|
|
}
|
|
|
|
return doc;
|
|
}
|
|
|
|
QPair<QString, QString> CMakeManager::cacheValue(KDevelop::IProject* project, const QString& id) const
|
|
{
|
|
QPair<QString, QString> ret;
|
|
if(project==0 && !m_projectsData.isEmpty())
|
|
{
|
|
project=m_projectsData.keys().first();
|
|
}
|
|
|
|
// kDebug() << "cache value " << id << project << (m_projectsData.contains(project) && m_projectsData[project].cache.contains(id));
|
|
CMakeProjectData* data = m_projectsData[project];
|
|
if(data && data->cache.contains(id))
|
|
{
|
|
const CacheEntry& e=data->cache.value(id);
|
|
ret.first=e.value;
|
|
ret.second=e.doc;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
void CMakeManager::projectClosing(IProject* p)
|
|
{
|
|
delete m_projectsData.take(p);
|
|
delete m_watchers.take(p);
|
|
|
|
m_filter->remove(p);
|
|
|
|
kDebug(9042) << "Project closed" << p;
|
|
}
|
|
|
|
QStringList CMakeManager::processGeneratorExpression(const QStringList& expr, IProject* project, ProjectTargetItem* target) const
|
|
{
|
|
QStringList ret;
|
|
const CMakeProjectData* data = m_projectsData[project];
|
|
GenerationExpressionSolver exec(data->properties, data->targetAlias);
|
|
if(target)
|
|
exec.setTargetName(target->text());
|
|
|
|
exec.defineVariable("INSTALL_PREFIX", data->vm.value("CMAKE_INSTALL_PREFIX").join(QString()));
|
|
for(QStringList::const_iterator it = expr.constBegin(), itEnd = expr.constEnd(); it!=itEnd; ++it) {
|
|
QStringList val = exec.run(*it).split(';');
|
|
ret += val;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
void CMakeManager::addPending(const Path& path, CMakeFolderItem* folder)
|
|
{
|
|
m_pending.insert(path, folder);
|
|
}
|
|
|
|
CMakeFolderItem* CMakeManager::takePending(const Path& path)
|
|
{
|
|
return m_pending.take(path);
|
|
}
|
|
|
|
void CMakeManager::addWatcher(IProject* p, const QString& path)
|
|
{
|
|
if (QFileSystemWatcher* watcher = m_watchers.value(p)) {
|
|
watcher->addPath(path);
|
|
} else {
|
|
kWarning() << "Could not find a watcher for project" << p << p->name() << ", path " << path;
|
|
Q_ASSERT(false);
|
|
}
|
|
}
|
|
|
|
CMakeProjectData CMakeManager::projectData(IProject* project)
|
|
{
|
|
Q_ASSERT(QThread::currentThread() == project->thread());
|
|
CMakeProjectData* data = m_projectsData[project];
|
|
if(!data) {
|
|
data = new CMakeProjectData;
|
|
m_projectsData[project] = data;
|
|
}
|
|
return *data;
|
|
}
|
|
|
|
ProjectFilterManager* CMakeManager::filterManager() const
|
|
{
|
|
return m_filter;
|
|
}
|