kde-extraapps/kdevelop/projectmanagers/cmake/cmakemanager.cpp

896 lines
29 KiB
C++
Raw Normal View History

/* 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;
}