diff --git a/plugins/projectmanagerview/projectmanagerviewplugin.cpp b/plugins/projectmanagerview/projectmanagerviewplugin.cpp index b3cb948392..633566118d 100644 --- a/plugins/projectmanagerview/projectmanagerviewplugin.cpp +++ b/plugins/projectmanagerview/projectmanagerviewplugin.cpp @@ -1,586 +1,604 @@ /* This file is part of KDevelop Copyright 2004 Roberto Raggi Copyright 2007 Andreas Pakulat This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "projectmanagerviewplugin.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "projectmanagerview.h" #include "builditembuilderjob.h" using namespace KDevelop; K_PLUGIN_FACTORY(ProjectManagerFactory, registerPlugin(); ) K_EXPORT_PLUGIN(ProjectManagerFactory(KAboutData("kdevprojectmanagerview","kdevprojectmanagerview", ki18n("Project Management View"), "0.1", ki18n("Toolview to do all the project management stuff"), KAboutData::License_GPL))) class KDevProjectManagerViewFactory: public KDevelop::IToolViewFactory { public: KDevProjectManagerViewFactory( ProjectManagerViewPlugin *plugin ): mplugin( plugin ) {} virtual QWidget* create( QWidget *parent = 0 ) { return new ProjectManagerView( mplugin, parent ); } virtual Qt::DockWidgetArea defaultPosition() { return Qt::LeftDockWidgetArea; } virtual QString id() const { return "org.kdevelop.ProjectsView"; } virtual QList< QAction* > contextMenuActions(QWidget* viewWidget) const { QList actions; foreach(QAction* action, viewWidget->actions()) { if (!qobject_cast(action)) { actions << action; } } return actions; } private: ProjectManagerViewPlugin *mplugin; }; class ProjectManagerViewPluginPrivate { public: ProjectManagerViewPluginPrivate() {} KDevProjectManagerViewFactory *factory; QList ctxProjectItemList; KAction* m_buildAll; KAction* m_build; KAction* m_install; KAction* m_clean; KAction* m_configure; KAction* m_prune; }; ProjectManagerViewPlugin::ProjectManagerViewPlugin( QObject *parent, const QVariantList& ) : IPlugin( ProjectManagerFactory::componentData(), parent ), d(new ProjectManagerViewPluginPrivate) { d->m_buildAll = new KAction( i18n("Build all Projects"), this ); d->m_buildAll->setIcon(KIcon("run-build")); connect( d->m_buildAll, SIGNAL(triggered()), this, SLOT(buildAllProjects()) ); actionCollection()->addAction( "project_buildall", d->m_buildAll ); d->m_build = new KAction( i18n("Build Selection"), this ); d->m_build->setShortcut( Qt::Key_F8 ); d->m_build->setIcon(KIcon("run-build")); d->m_build->setEnabled( false ); connect( d->m_build, SIGNAL(triggered()), this, SLOT(buildProjectItems()) ); actionCollection()->addAction( "project_build", d->m_build ); d->m_install = new KAction( i18n("Install Selection"), this ); d->m_install->setIcon(KIcon("run-build-install")); d->m_install->setEnabled( false ); connect( d->m_install, SIGNAL(triggered()), this, SLOT(installProjectItems()) ); actionCollection()->addAction( "project_install", d->m_install ); d->m_clean = new KAction( i18n("Clean Selection"), this ); d->m_clean->setIcon(KIcon("run-build-clean")); d->m_clean->setEnabled( false ); connect( d->m_clean, SIGNAL(triggered()), this, SLOT(cleanProjectItems()) ); actionCollection()->addAction( "project_clean", d->m_clean ); d->m_configure = new KAction( i18n("Configure Selection"), this ); d->m_configure->setIcon(KIcon("run-build-configure")); d->m_configure->setEnabled( false ); connect( d->m_configure, SIGNAL(triggered()), this, SLOT(configureProjectItems()) ); actionCollection()->addAction( "project_configure", d->m_configure ); d->m_prune = new KAction( i18n("Prune Selection"), this ); d->m_prune->setIcon(KIcon("run-build-prune")); d->m_prune->setEnabled( false ); connect( d->m_prune, SIGNAL(triggered()), this, SLOT(pruneProjectItems()) ); actionCollection()->addAction( "project_prune", d->m_prune ); // only add the action so that its known in the actionCollection // and so that it's shortcut etc. pp. is restored // apparently that is not possible to be done in the view itself *sigh* actionCollection()->addAction( "locate_document" ); setXMLFile( "kdevprojectmanagerview.rc" ); d->factory = new KDevProjectManagerViewFactory( this ); core()->uiController()->addToolView( i18n("Projects"), d->factory ); connect( core()->selectionController(), SIGNAL(selectionChanged(KDevelop::Context*)), SLOT(updateActionState(KDevelop::Context*))); connect( ICore::self()->projectController()->buildSetModel(), SIGNAL(rowsInserted(const QModelIndex&, int, int)), SLOT(updateFromBuildSetChange())); connect( ICore::self()->projectController()->buildSetModel(), SIGNAL(rowsRemoved(const QModelIndex&, int, int)), SLOT(updateFromBuildSetChange())); connect( ICore::self()->projectController()->buildSetModel(), SIGNAL(modelReset()), SLOT(updateFromBuildSetChange())); } void ProjectManagerViewPlugin::updateFromBuildSetChange() { updateActionState( core()->selectionController()->currentSelection() ); } void ProjectManagerViewPlugin::updateActionState( KDevelop::Context* ctx ) { bool isEmpty = ICore::self()->projectController()->buildSetModel()->items().isEmpty(); if( isEmpty ) { isEmpty = !ctx || ctx->type() != Context::ProjectItemContext || dynamic_cast( ctx )->items().isEmpty(); } d->m_build->setEnabled( !isEmpty ); d->m_install->setEnabled( !isEmpty ); d->m_clean->setEnabled( !isEmpty ); d->m_configure->setEnabled( !isEmpty ); d->m_prune->setEnabled( !isEmpty ); } ProjectManagerViewPlugin::~ProjectManagerViewPlugin() { delete d; } void ProjectManagerViewPlugin::unload() { kDebug() << "unloading manager view"; core()->uiController()->removeToolView(d->factory); } ContextMenuExtension ProjectManagerViewPlugin::contextMenuExtension( KDevelop::Context* context ) { d->ctxProjectItemList.clear(); if( context->type() != KDevelop::Context::ProjectItemContext ) return IPlugin::contextMenuExtension( context ); KDevelop::ProjectItemContext* ctx = dynamic_cast( context ); QList items = ctx->items(); if( items.isEmpty() ) return IPlugin::contextMenuExtension( context ); //TODO: also needs: removeTarget, removeFileFromTarget, runTargetsFromContextMenu ContextMenuExtension menuExt; bool needsCreateFile = true; bool needsCreateFolder = true; bool needsCloseProjects = true; bool needsBuildItems = true; bool needsFolderItems = true; bool needsRemoveAndRename = true; + bool needsRemoveTargetFiles = true; //needsCreateFile if there is one item and it's a folder or target needsCreateFile &= (items.count() == 1) && (items.first()->folder() || items.first()->target()); //needsCreateFolder if there is one item and it's a folder needsCreateFolder &= (items.count() == 1) && (items.first()->folder()); foreach( ProjectBaseItem* item, items ) { d->ctxProjectItemList << item; //needsBuildItems if items are limited to targets and buildfolders needsBuildItems &= item->target() || item->type() == ProjectBaseItem::BuildFolder; //needsCloseProjects if items are limited to top level folders (Project Folders) needsCloseProjects &= item->folder() && !item->folder()->parent(); //needsFolderItems if items are limited to folders needsFolderItems &= (bool)item->folder(); //needsRemove if items are limited to non-top-level folders or files that don't belong to targets needsRemoveAndRename &= (item->folder() && item->parent()) || (item->file() && !item->parent()->target()); + + //needsRemoveTargets if items are limited to file items with target parents + needsRemoveTargetFiles &= (item->file() && item->parent()->target()); } if ( needsCreateFile ) { KAction* action = new KAction( i18n( "Create File" ), this ); action->setIcon(KIcon("document-new")); connect( action, SIGNAL(triggered()), this, SLOT(createFileFromContextMenu()) ); menuExt.addAction( ContextMenuExtension::FileGroup, action ); } if ( needsCreateFolder ) { KAction* action = new KAction( i18n( "Create Folder" ), this ); action->setIcon(KIcon("folder-new")); connect( action, SIGNAL(triggered()), this, SLOT(createFolderFromContextMenu()) ); menuExt.addAction( ContextMenuExtension::FileGroup, action ); } if ( needsBuildItems ) { KAction* action = new KAction( i18n( "Build" ), this ); action->setIcon(KIcon("run-build")); connect( action, SIGNAL( triggered() ), this, SLOT(buildItemsFromContextMenu()) ); menuExt.addAction( ContextMenuExtension::BuildGroup, action ); action = new KAction( i18n( "Install" ), this ); action->setIcon(KIcon("run-install")); connect( action, SIGNAL( triggered() ), this, SLOT(installItemsFromContextMenu()) ); menuExt.addAction( ContextMenuExtension::BuildGroup, action ); action = new KAction( i18n( "Clean" ), this ); action->setIcon(KIcon("run-clean")); connect( action, SIGNAL( triggered() ), this, SLOT(cleanItemsFromContextMenu()) ); menuExt.addAction( ContextMenuExtension::BuildGroup, action ); action = new KAction( i18n( "Add to Buildset" ), this ); connect( action, SIGNAL(triggered() ), this, SLOT(addItemsFromContextMenuToBuildset() ) ); menuExt.addAction( ContextMenuExtension::BuildGroup, action ); } if ( needsCloseProjects ) { KAction* close = new KAction( i18np( "Close Project", "Close Projects", items.count() ), this ); close->setIcon(KIcon("project-development-close")); connect( close, SIGNAL(triggered()), this, SLOT(closeProjects()) ); menuExt.addAction( ContextMenuExtension::ProjectGroup, close ); } if ( needsFolderItems ) { KAction* action = new KAction( i18n( "Reload" ), this ); action->setIcon(KIcon("view-refresh")); connect( action, SIGNAL(triggered()), this, SLOT(reloadFromContextMenu()) ); menuExt.addAction( ContextMenuExtension::FileGroup, action ); } if ( needsRemoveAndRename ) { KAction* remove = new KAction( i18n( "Remove" ), this ); remove->setIcon(KIcon("user-trash")); connect( remove, SIGNAL(triggered()), this, SLOT(removeFromContextMenu()) ); menuExt.addAction( ContextMenuExtension::FileGroup, remove ); KAction* rename = new KAction( i18n( "Rename" ), this ); rename->setIcon(KIcon("edit-rename")); connect( rename, SIGNAL(triggered()), this, SLOT(renameItemFromContextMenu()) ); menuExt.addAction( ContextMenuExtension::FileGroup, rename ); } + if ( needsRemoveTargetFiles ) { + KAction* remove = new KAction( i18n( "Remove From Target" ), this ); + remove->setIcon(KIcon("user-trash")); + connect( remove, SIGNAL(triggered()), this, SLOT(removeTargetFilesFromContextMenu()) ); + menuExt.addAction( ContextMenuExtension::FileGroup, remove ); + } return menuExt; } KDevelop::IProjectBuilder* ProjectManagerViewPlugin::getProjectBuilder( KDevelop::ProjectBaseItem* item ) { if( !item ) return 0; IProject* project = item->project(); if (!project) return 0; ProjectFolderItem* prjitem = project->projectItem(); IPlugin* fmgr = project->managerPlugin(); IBuildSystemManager* mgr = fmgr->extension(); if( mgr ) { return mgr->builder( prjitem ); } return 0; } void ProjectManagerViewPlugin::closeProjects() { QList projectsToClose; foreach( KDevelop::ProjectBaseItem* item, d->ctxProjectItemList ) { if( !projectsToClose.contains( item->project() ) ) { projectsToClose << item->project(); } } d->ctxProjectItemList.clear(); foreach( KDevelop::IProject* proj, projectsToClose ) { core()->projectController()->closeProject( proj ); } } void ProjectManagerViewPlugin::installItemsFromContextMenu() { ICore::self()->runController()->registerJob( new BuildItemBuilderJob( KDevelop::BuilderJob::Install, d->ctxProjectItemList ) ); d->ctxProjectItemList.clear(); } void ProjectManagerViewPlugin::cleanItemsFromContextMenu() { ICore::self()->runController()->registerJob( new BuildItemBuilderJob( KDevelop::BuilderJob::Clean, d->ctxProjectItemList ) ); d->ctxProjectItemList.clear(); } void ProjectManagerViewPlugin::buildItemsFromContextMenu() { ICore::self()->runController()->registerJob( new BuildItemBuilderJob( KDevelop::BuilderJob::Build, d->ctxProjectItemList ) ); d->ctxProjectItemList.clear(); } void ProjectManagerViewPlugin::buildAllProjects() { QList items; foreach( KDevelop::IProject* project, core()->projectController()->projects() ) { items << project->projectItem(); } ICore::self()->runController()->registerJob( new BuildItemBuilderJob( KDevelop::BuilderJob::Build, items ) ); } void ProjectManagerViewPlugin::runBuilderJob( KDevelop::BuilderJob::BuildType t ) { QList items; if( !ICore::self()->projectController()->buildSetModel()->items().isEmpty() ) { ICore::self()->runController()->registerJob( new BuildItemBuilderJob( t, ICore::self()->projectController()->buildSetModel()->items() ) ); } else { KDevelop::ProjectItemContext* ctx = dynamic_cast(ICore::self()->selectionController()->currentSelection()); ICore::self()->runController()->registerJob( new BuildItemBuilderJob( t, ctx->items() ) ); } } void ProjectManagerViewPlugin::installProjectItems() { runBuilderJob( KDevelop::BuilderJob::Install ); } void ProjectManagerViewPlugin::pruneProjectItems() { runBuilderJob( KDevelop::BuilderJob::Prune ); } void ProjectManagerViewPlugin::configureProjectItems() { runBuilderJob( KDevelop::BuilderJob::Configure ); } void ProjectManagerViewPlugin::cleanProjectItems() { runBuilderJob( KDevelop::BuilderJob::Clean ); } void ProjectManagerViewPlugin::buildProjectItems() { runBuilderJob( KDevelop::BuilderJob::Build ); } void ProjectManagerViewPlugin::addItemsFromContextMenuToBuildset( ) { foreach( KDevelop::ProjectBaseItem* item, d->ctxProjectItemList ) { ICore::self()->projectController()->buildSetModel()->addProjectItem( item ); } } void ProjectManagerViewPlugin::runTargetsFromContextMenu( ) { foreach( KDevelop::ProjectBaseItem* item, d->ctxProjectItemList ) { KDevelop::ProjectExecutableTargetItem* t=item->executable(); if(t) { kDebug() << "Running target: " << t->text() << t->builtUrl(); } } } void ProjectManagerViewPlugin::projectConfiguration( ) { if( !d->ctxProjectItemList.isEmpty() ) { core()->projectController()->configureProject( d->ctxProjectItemList.at( 0 )->project() ); } } void ProjectManagerViewPlugin::reloadFromContextMenu( ) { QList< KDevelop::ProjectFolderItem* > folders; foreach( KDevelop::ProjectBaseItem* item, d->ctxProjectItemList ) { if ( item->folder() ) { // since reloading should be recursive, only pass the upper-most items bool found = false; foreach ( KDevelop::ProjectFolderItem* existing, folders ) { if ( existing->url().isParentOf(item->folder()->url()) ) { // simply skip this child found = true; break; } else if ( item->folder()->url().isParentOf(existing->url()) ) { // remove the child in the list and add the current item instead folders.removeOne(existing); // continue since there could be more than one existing child } } if ( !found ) { folders << item->folder(); } } } foreach( KDevelop::ProjectFolderItem* folder, folders ) { folder->project()->projectFileManager()->reload(folder); } } void ProjectManagerViewPlugin::createFolderFromContextMenu( ) { foreach( KDevelop::ProjectBaseItem* item, d->ctxProjectItemList ) { if ( item->folder() ) { QWidget* window(ICore::self()->uiController()->activeMainWindow()->window()); QString name = QInputDialog::getText ( window, i18n ( "Create Folder in %1", item->folder()->url().prettyUrl() ), i18n ( "Folder Name" ) ); if (!name.isEmpty()) { KUrl url = item->folder()->url(); url.addPath( name ); item->project()->projectFileManager()->addFolder( url, item->folder() ); } } } } -bool projectItemUrlLessThan(KDevelop::ProjectBaseItem* item1, KDevelop::ProjectBaseItem* item2) -{ - return item1->url().path() < item2->url().path(); -} void ProjectManagerViewPlugin::removeFromContextMenu() { //copy the list of selected items and sort it to guarantee parents will come before children QMap< IProjectFileManager*, QList > filteredItems; filteredItems[0] += d->ctxProjectItemList; - qSort(filteredItems[0].begin(), filteredItems[0].end(), projectItemUrlLessThan); + qSort(filteredItems[0].begin(), filteredItems[0].end(), ProjectBaseItem::urlLessThan); KUrl lastFolder; QStringList itemPaths; foreach( KDevelop::ProjectBaseItem* item, filteredItems[0] ) { Q_ASSERT(item->folder() || item->file()); Q_ASSERT(!item->file() || !item->file()->parent()->target()); if (item->folder() || item->file()) { //make sure no children of folders that will be deleted are listed if (lastFolder.isParentOf(item->url())) { continue; } else if (item->folder()) { lastFolder = item->url(); } filteredItems[item->project()->projectFileManager()] << item; itemPaths << item->url().path(); } } if (KMessageBox::warningYesNoList( QApplication::activeWindow(), i18np("Do you really want to delete this item?", "Do you really want to delete these %1 items?", itemPaths.size()), itemPaths, i18n("Delete Files"), KStandardGuiItem::del(), KStandardGuiItem::cancel() ) == KMessageBox::No) { return; } //Go though projectmanagers, have them remove the files and folders that they own QMap< IProjectFileManager*, QList >::iterator it; for (it = filteredItems.begin(); it != filteredItems.end(); ++it) { if (it.key()) it.key()->removeFilesAndFolders(it.value()); } } +void ProjectManagerViewPlugin::removeTargetFilesFromContextMenu() +{ + QList items = d->ctxProjectItemList; + QMap< IBuildSystemManager*, QList > itemsByBuildSystem; + foreach(ProjectBaseItem *item, items) + itemsByBuildSystem[item->project()->buildSystemManager()].append(item->file()); + + QMap< IBuildSystemManager*, QList >::iterator it; + for (it = itemsByBuildSystem.begin(); it != itemsByBuildSystem.end(); ++it) + it.key()->removeFilesFromTargets(it.value()); +} + void ProjectManagerViewPlugin::renameItemFromContextMenu() { QWidget* window=ICore::self()->uiController()->activeMainWindow()->window(); foreach( KDevelop::ProjectBaseItem* item, d->ctxProjectItemList ) { if ((item->type()!=ProjectBaseItem::BuildFolder && item->type()!=ProjectBaseItem::Folder && item->type()!=ProjectBaseItem::File) || !item->parent()) { continue; } const QString src = item->text(); //Change QInputDialog->KFileSaveDialog? QString name = QInputDialog::getText( window, i18n("Rename..."), i18n("New name for '%1'", item->text()), QLineEdit::Normal, item->text() ); if (!name.isEmpty() && name != src) { ProjectBaseItem::RenameStatus status = item->rename( name ); QWidget* window(QApplication::activeWindow()); switch(status) { case ProjectBaseItem::RenameOk: break; case ProjectBaseItem::ExistingItemSameName: KMessageBox::error(window, i18n("There is already a file named '%1'", name)); break; case ProjectBaseItem::ProjectManagerRenameFailed: KMessageBox::error(window, i18n("Could not rename '%1'", name)); break; case ProjectBaseItem::InvalidNewName: KMessageBox::error(window, i18n("'%1' is not a valid file name", name)); break; } } } } ProjectFileItem* createFile(const ProjectFolderItem* item) { QWidget* window = ICore::self()->uiController()->activeMainWindow()->window(); QString name = QInputDialog::getText(window, i18n("Create File in %1", item->url().prettyUrl()), i18n("File Name")); if(name.isEmpty()) return 0; KUrl url=item->url(); url.addPath( name ); ProjectFileItem* ret=item->project()->projectFileManager()->addFile( url, item->folder() ); ICore::self()->documentController()->openDocument( url ); return ret; } void ProjectManagerViewPlugin::createFileFromContextMenu( ) { foreach( KDevelop::ProjectBaseItem* item, d->ctxProjectItemList ) { if ( item->folder() ) { createFile(item->folder()); } else if ( item->target() ) { ProjectFolderItem* folder=dynamic_cast(item->parent()); if(folder) { ProjectFileItem* f=createFile(folder); if(f) - item->project()->buildSystemManager()->addFileToTarget(f, item->target()); + item->project()->buildSystemManager()->addFilesToTarget(QList() << f, item->target()); } } } } #include "projectmanagerviewplugin.moc" diff --git a/plugins/projectmanagerview/projectmanagerviewplugin.h b/plugins/projectmanagerview/projectmanagerviewplugin.h index 5f3e06f7b9..3410fb6525 100644 --- a/plugins/projectmanagerview/projectmanagerviewplugin.h +++ b/plugins/projectmanagerview/projectmanagerviewplugin.h @@ -1,90 +1,91 @@ /* This file is part of KDevelop Copyright 2004 Roberto Raggi Copyright 2007 Andreas Pakulat This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KDEVPROJECTMANAGERVIEWPLUGIN_H #define KDEVPROJECTMANAGERVIEWPLUGIN_H #include #include #include #include class KUrl; class ProjectBuildSetModel; namespace KDevelop { class ProjectBaseItem; class ProjectBuilder; class ProjectFileItem; class ProjectFolderItem; class ProjectTargetItem; class IProjectBuilder; class IProject; class ContextMenuExtension; class Context; } class ProjectManagerView; class ProjectManagerViewPlugin: public KDevelop::IPlugin { Q_OBJECT public: public: ProjectManagerViewPlugin(QObject *parent, const QVariantList & = QVariantList() ); virtual ~ProjectManagerViewPlugin(); // Plugin methods virtual void unload(); KDevelop::ContextMenuExtension contextMenuExtension( KDevelop::Context* ); public Q_SLOTS: void buildProjectItems(); void installProjectItems(); void cleanProjectItems(); protected Q_SLOTS: void closeProjects(); void buildItemsFromContextMenu(); void installItemsFromContextMenu(); void cleanItemsFromContextMenu(); void configureProjectItems(); void pruneProjectItems(); void buildAllProjects(); void addItemsFromContextMenuToBuildset(); void projectConfiguration(); void runTargetsFromContextMenu(); void reloadFromContextMenu(); void createFolderFromContextMenu(); void createFileFromContextMenu(); void removeFromContextMenu(); + void removeTargetFilesFromContextMenu(); void renameItemFromContextMenu(); void updateActionState( KDevelop::Context* ctx ); void updateFromBuildSetChange(); private: static KDevelop::IProjectBuilder* getProjectBuilder( KDevelop::ProjectBaseItem* item ); QList recurseAndFetchCheckedItems( KDevelop::ProjectBaseItem* item ); void runBuilderJob( KDevelop::BuilderJob::BuildType ); class ProjectManagerViewPluginPrivate* const d; }; #endif diff --git a/project/abstractfilemanagerplugin.cpp b/project/abstractfilemanagerplugin.cpp index 6d23a1570a..a7e40da154 100644 --- a/project/abstractfilemanagerplugin.cpp +++ b/project/abstractfilemanagerplugin.cpp @@ -1,538 +1,544 @@ /*************************************************************************** * This file is part of KDevelop * * Copyright 2010 Milian Wolff * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU Library 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 Library 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 "abstractfilemanagerplugin.h" #include "filemanagerlistjob.h" #include "projectmodel.h" #include "helper.h" #include #include #include #include #include #include #include #include #include #include #include #define ifDebug(x) using namespace KDevelop; //BEGIN Helper namespace { /** * Returns the parent folder item for a given item or the project root item if there is no parent. */ ProjectFolderItem* getParentFolder(ProjectBaseItem* item) { if ( item->parent() ) { return static_cast(item->parent()); } else { return item->project()->projectItem(); } } } //END Helper //BEGIN Private struct AbstractFileManagerPlugin::Private { explicit Private(AbstractFileManagerPlugin* qq) : q(qq) { } AbstractFileManagerPlugin* q; /// @p forceRecursion if true, existing folders will be re-read no matter what KJob* eventuallyReadFolder( ProjectFolderItem* item, const bool forceRecursion = false ); void addJobItems(FileManagerListJob* job, ProjectFolderItem* baseItem, const KIO::UDSEntryList& entries, const bool forceRecursion); void deleted(const QString &path); void created(const QString &path); void projectClosing(IProject* project); void jobFinished(KJob* job); /// Stops watching the given folder for changes, only useful for local files. void stopWatcher(ProjectFolderItem* folder); /// Continues watching the given folder for changes. void continueWatcher(ProjectFolderItem* folder); /// Common renaming function. bool rename( ProjectBaseItem* item, const KUrl& destination); void removeFolder(ProjectFolderItem* folder); QMap m_watchers; QMap > m_projectJobs; }; void AbstractFileManagerPlugin::Private::projectClosing(IProject* project) { if ( m_projectJobs.contains(project) ) { // make sure the import job does not live longer than the project // see also addLotsOfFiles test foreach( FileManagerListJob* job, m_projectJobs[project] ) { kDebug(9517) << "killing project job:" << job; job->kill(); } m_projectJobs.remove(project); } delete m_watchers.take(project); } KJob* AbstractFileManagerPlugin::Private::eventuallyReadFolder( ProjectFolderItem* item, const bool forceRecursion ) { FileManagerListJob* listJob = new FileManagerListJob( item, forceRecursion ); m_projectJobs[ item->project() ] << listJob; kDebug(9517) << "adding job" << listJob << item << item->url() << "for project" << item->project(); ICore::self()->runController()->registerJob( listJob ); q->connect( listJob, SIGNAL(finished(KJob*)), q, SLOT(jobFinished(KJob*)) ); q->connect( listJob, SIGNAL(entries(FileManagerListJob*, ProjectFolderItem*, KIO::UDSEntryList, bool)), q, SLOT(addJobItems(FileManagerListJob*, ProjectFolderItem*, KIO::UDSEntryList, bool)) ); return listJob; } void AbstractFileManagerPlugin::Private::jobFinished(KJob* job) { FileManagerListJob* gmlJob = qobject_cast(job); Q_ASSERT(gmlJob); ifDebug(kDebug() << job << gmlJob;) m_projectJobs[ gmlJob->item()->project() ].removeOne( gmlJob ); } void AbstractFileManagerPlugin::Private::addJobItems(FileManagerListJob* job, ProjectFolderItem* baseItem, const KIO::UDSEntryList& entries, const bool forceRecursion) { if ( entries.empty() ) { return; } kDebug(9517) << "reading entries of" << baseItem->url(); // build lists of valid files and folders with relative urls to the project folder KUrl::List files; KUrl::List folders; foreach ( const KIO::UDSEntry& entry, entries ) { QString name = entry.stringValue( KIO::UDSEntry::UDS_NAME ); if (name == "." || name == "..") { continue; } KUrl url = baseItem->url(); url.addPath( name ); if (entry.isDir()) { url.adjustPath(KUrl::AddTrailingSlash); } if ( !q->isValid( url, entry.isDir(), baseItem->project() ) ) { continue; } else { if ( entry.isDir() ) { if( entry.isLink() ) { KUrl linkedUrl = baseItem->url(); linkedUrl.cd(entry.stringValue( KIO::UDSEntry::UDS_LINK_DEST )); // make sure we don't end in an infinite loop if( linkedUrl.isParentOf( baseItem->project()->folder() ) || baseItem->project()->folder().isParentOf( linkedUrl ) || linkedUrl == baseItem->project()->folder() ) { continue; } } folders << url; } else { files << url; } } } ifDebug(kDebug(9517) << "valid folders:" << folders;) ifDebug(kDebug(9517) << "valid files:" << files;) // remove obsolete rows for ( int j = 0; j < baseItem->rowCount(); ++j ) { if ( ProjectFolderItem* f = baseItem->child(j)->folder() ) { // check if this is still a valid folder int index = folders.indexOf( f->url() ); if ( index == -1 ) { // folder got removed or is now invalid removeFolder(f); --j; } else { // this folder already exists in the view folders.removeAt( index ); if ( forceRecursion ) { //no need to add this item, but we still want to recurse into it job->addSubDir( f ); } } } else if ( ProjectFileItem* f = baseItem->child(j)->file() ) { // check if this is still a valid file int index = files.indexOf( f->url() ); if ( index == -1 ) { // file got removed or is now invalid ifDebug(kDebug(9517) << "removing file:" << f << f->url();) baseItem->removeRow( j ); --j; } else { // this file already exists in the view files.removeAt( index ); } } } // add new rows foreach ( const KUrl& url, files ) { ProjectFileItem* file = q->createFileItem( baseItem->project(), url, baseItem ); emit q->fileAdded( file ); } foreach ( const KUrl& url, folders ) { ProjectFolderItem* folder = q->createFolderItem( baseItem->project(), url, baseItem ); emit q->folderAdded( folder ); job->addSubDir( folder ); } } void AbstractFileManagerPlugin::Private::created(const QString &path) { kDebug(9517) << "created:" << path; QFileInfo info(path); KUrl url = KUrl(path); if (info.isDir()) { url.adjustPath(KUrl::AddTrailingSlash); } KUrl parent = url.upUrl(); foreach ( IProject* p, m_watchers.keys() ) { if ( !q->isValid(url, info.isDir(), p) ) { continue; } if ( info.isDir() ) { bool found = false; foreach ( ProjectFolderItem* folder, p->foldersForUrl(url) ) { // exists already in this project, happens e.g. when we restart the dirwatcher // or if we delete and remove folders consecutively https://bugs.kde.org/show_bug.cgi?id=260741 kDebug(9517) << "force reload of" << url << folder; eventuallyReadFolder( folder, true ); found = true; } if ( found ) { continue; } } else if (!p->filesForUrl(url).isEmpty()) { // also gets triggered for kate's backup files continue; } foreach ( ProjectFolderItem* parentItem, p->foldersForUrl(parent) ) { if ( info.isDir() ) { ProjectFolderItem* folder = q->createFolderItem( p, url, parentItem ); emit q->folderAdded( folder ); eventuallyReadFolder( folder ); } else { ProjectFileItem* file = q->createFileItem( p, url, parentItem ); emit q->fileAdded( file ); } } } } void AbstractFileManagerPlugin::Private::deleted(const QString &path) { if ( QFile::exists(path) ) { // stopDirScan... return; } kDebug(9517) << "deleted:" << path; KUrl url = KUrl(path); foreach ( IProject* p, m_watchers.keys() ) { if ( url.equals(p->folder(), KUrl::CompareWithoutTrailingSlash) ) { KMessageBox::error(qApp->activeWindow(), i18n("The base folder of project %1" " got deleted or moved outside of KDevelop.\n" "The project has to be closed.", p->name()), i18n("Project Folder Deleted") ); ICore::self()->projectController()->closeProject(p); continue; } foreach ( ProjectFolderItem* item, p->foldersForUrl(url) ) { removeFolder(item); } foreach ( ProjectFileItem* item, p->filesForUrl(url) ) { emit q->fileRemoved(item); ifDebug(kDebug(9517) << "removing file" << item;) item->parent()->removeRow(item->row()); } } } bool AbstractFileManagerPlugin::Private::rename(ProjectBaseItem* item, const KUrl& destination) { if ( !q->isValid(destination, true, item->project()) ) { int cancel = KMessageBox::warningContinueCancel( qApp->activeWindow(), i18n("You tried to rename '%1' to '%2', but the latter is filtered and will be hidden.\n" "Do you want to continue?", item->text(), destination.fileName()), QString(), KStandardGuiItem::cont(), KStandardGuiItem::cancel(), "GenericManagerRenameToFiltered" ); if ( cancel == KMessageBox::Cancel ) { return false; } } foreach ( ProjectFolderItem* parent, item->project()->foldersForUrl(destination.upUrl()) ) { if ( parent->folder() ) { stopWatcher(parent); const KUrl source = item->url(); bool success = renameUrl( item->project(), source, destination ); if ( success ) { item->setUrl( destination ); item->parent()->takeRow( item->row() ); parent->appendRow( item ); if (item->file()) { emit q->fileRenamed(source, item->file()); } else { Q_ASSERT(item->folder()); emit q->folderRenamed(source, item->folder()); } } continueWatcher(parent); return success; } } return false; } void AbstractFileManagerPlugin::Private::stopWatcher(ProjectFolderItem* folder) { if ( !folder->url().isLocalFile() ) { return; } Q_ASSERT(m_watchers.contains(folder->project())); m_watchers[folder->project()]->stopDirScan(folder->url().toLocalFile()); } void AbstractFileManagerPlugin::Private::continueWatcher(ProjectFolderItem* folder) { if ( !folder->url().isLocalFile() ) { return; } Q_ASSERT(m_watchers.contains(folder->project())); m_watchers[folder->project()]->restartDirScan(folder->url().toLocalFile()); } void AbstractFileManagerPlugin::Private::removeFolder(ProjectFolderItem* folder) { ifDebug(kDebug(9517) << "removing folder:" << folder << folder->url();) foreach(FileManagerListJob* job, m_projectJobs[folder->project()]) { if (job->item() == folder) { kDebug(9517) << "killing list job for removed folder" << job << folder->url(); job->kill(); } } folder->parent()->removeRow( folder->row() ); } //END Private //BEGIN Plugin AbstractFileManagerPlugin::AbstractFileManagerPlugin( const KComponentData& instance, QObject *parent, const QVariantList & /*args*/ ) : IProjectFileManager(), IPlugin( instance, parent ), d(new Private(this)) { KDEV_USE_EXTENSION_INTERFACE( IProjectFileManager ) connect(core()->projectController(), SIGNAL(projectClosing(KDevelop::IProject*)), this, SLOT(projectClosing(KDevelop::IProject*))); } AbstractFileManagerPlugin::~AbstractFileManagerPlugin() { delete d; } IProjectFileManager::Features AbstractFileManagerPlugin::features() const { return Features( Folders | Files ); } QList AbstractFileManagerPlugin::parse( ProjectFolderItem *item ) { // we are async, can't return anything here kDebug(9517) << "note: parse will always return an empty list"; Q_UNUSED(item); return QList(); } ProjectFolderItem *AbstractFileManagerPlugin::import( IProject *project ) { ProjectFolderItem *projectRoot = createFolderItem( project, project->folder(), 0 ); emit folderAdded( projectRoot ); kDebug(9517) << "imported new project" << project->name() << "at" << projectRoot->url(); ///TODO: check if this works for remote files when something gets changed through another KDE app if ( project->folder().isLocalFile() ) { d->m_watchers[project] = new KDirWatch( project ); connect(d->m_watchers[project], SIGNAL(created(QString)), this, SLOT(created(QString))); connect(d->m_watchers[project], SIGNAL(deleted(QString)), this, SLOT(deleted(QString))); d->m_watchers[project]->addDir(project->folder().toLocalFile(), KDirWatch::WatchSubDirs | KDirWatch:: WatchFiles ); } return projectRoot; } KJob* AbstractFileManagerPlugin::createImportJob(ProjectFolderItem* item) { return d->eventuallyReadFolder(item); } bool AbstractFileManagerPlugin::reload( ProjectFolderItem* item ) { kDebug(9517) << "reloading item" << item->url(); d->eventuallyReadFolder( item->folder(), true ); return true; } ProjectFolderItem* AbstractFileManagerPlugin::addFolder( const KUrl& url, ProjectFolderItem * parent ) { kDebug(9517) << "adding folder" << url << "to" << parent->url(); ProjectFolderItem* created = 0; d->stopWatcher(parent); if ( createFolder(url) ) { created = createFolderItem( parent->project(), url, parent ); emit folderAdded(created); } d->continueWatcher(parent); return created; } ProjectFileItem* AbstractFileManagerPlugin::addFile( const KUrl& url, ProjectFolderItem * parent ) { kDebug(9517) << "adding file" << url << "to" << parent->url(); ProjectFileItem* created = 0; d->stopWatcher(parent); if ( createFile(url) ) { created = createFileItem( parent->project(), url, parent ); emit fileAdded(created); } d->continueWatcher(parent); return created; } bool AbstractFileManagerPlugin::renameFolder( ProjectFolderItem * folder, const KUrl& url ) { kDebug(9517) << "trying to rename a folder:" << folder->url() << url; return d->rename(folder, url); } bool AbstractFileManagerPlugin::renameFile( ProjectFileItem * file, const KUrl& url ) { kDebug(9517) << "trying to rename a file:" << file->url() << url; return d->rename(file, url); } bool AbstractFileManagerPlugin::removeFilesAndFolders(QList items) { bool success = true; foreach(ProjectBaseItem* item, items) { Q_ASSERT(item->folder() || item->file()); ProjectFolderItem* parent = getParentFolder(item); d->stopWatcher(parent); success &= removeUrl(parent->project(), item->url(), true); if ( success ) { if (item->file()) { emit fileRemoved(item->file()); } else { Q_ASSERT(item->folder()); emit folderRemoved(item->folder()); } item->parent()->removeRow( item->row() ); } d->continueWatcher(parent); if ( !success ) break; } return success; } +bool AbstractFileManagerPlugin::moveFilesAndFolders(QList< ProjectBaseItem* > items, ProjectFolderItem* newParent) +{ + qDebug() << "Implement me"; + return false; +} + bool AbstractFileManagerPlugin::isValid( const KUrl& /*url*/, const bool /*isFolder*/, IProject* /*project*/ ) const { return true; } ProjectFileItem* AbstractFileManagerPlugin::createFileItem( IProject* project, const KUrl& url, ProjectBaseItem* parent ) { return new ProjectFileItem( project, url, parent ); } ProjectFolderItem* AbstractFileManagerPlugin::createFolderItem( IProject* project, const KUrl& url, ProjectBaseItem* parent ) { return new ProjectFolderItem( project, url, parent ); } KDirWatch* AbstractFileManagerPlugin::projectWatcher( IProject* project ) const { return d->m_watchers.value( project, 0 ); } //END Plugin #include "abstractfilemanagerplugin.moc" diff --git a/project/abstractfilemanagerplugin.h b/project/abstractfilemanagerplugin.h index 3404744aa2..a02b55828d 100644 --- a/project/abstractfilemanagerplugin.h +++ b/project/abstractfilemanagerplugin.h @@ -1,131 +1,132 @@ /*************************************************************************** * This file is part of KDevelop * * Copyright 2010 Milian Wolff * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU Library 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 Library 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. * ***************************************************************************/ #ifndef ABSTRACTGENERICMANAGER_H #define ABSTRACTGENERICMANAGER_H #include "projectexport.h" #include "interfaces/iprojectfilemanager.h" #include class KDirWatch; namespace KDevelop { class FileManagerListJob; /** * This class can be used as a common base for file managers. * * It supports remote files using KIO and uses KDirWatch to synchronize with on-disk changes. */ class KDEVPLATFORMPROJECT_EXPORT AbstractFileManagerPlugin : public IPlugin, public virtual IProjectFileManager { Q_OBJECT Q_INTERFACES( KDevelop::IProjectFileManager ) public: explicit AbstractFileManagerPlugin( const KComponentData& instance, QObject *parent = 0, const QVariantList &args = QVariantList() ); virtual ~AbstractFileManagerPlugin(); // // IProjectFileManager interface // virtual Features features() const; virtual ProjectFolderItem* addFolder( const KUrl& folder, ProjectFolderItem *parent ); virtual ProjectFileItem* addFile( const KUrl& file, ProjectFolderItem *parent ); virtual bool removeFilesAndFolders( QList items); virtual bool renameFolder( ProjectFolderItem *folder, const KUrl& url ); virtual bool renameFile( ProjectFileItem *file, const KUrl& url ); + virtual bool moveFilesAndFolders ( QList< KDevelop::ProjectBaseItem* > items, KDevelop::ProjectFolderItem *newParent ); virtual QList parse( ProjectFolderItem *item ); virtual ProjectFolderItem *import( IProject *project ); virtual bool reload(ProjectFolderItem* item); virtual KJob* createImportJob(ProjectFolderItem* item); protected: // // AbstractFileManagerPlugin interface // /** * Filter interface making it possible to hide files and folders from a project. * * The default implementation will show all files. * * @return True when @p url should belong to @p project, false otherwise. */ virtual bool isValid(const KUrl& url, const bool isFolder, IProject* project) const; /** * Customization hook enabling you to create custom FolderItems if required. * * The default implementation will return a simple @c ProjectFolderItem */ virtual ProjectFolderItem* createFolderItem( KDevelop::IProject* project, const KUrl& url, KDevelop::ProjectBaseItem* parent = 0); /** * Customization hook enabling you to create custom FileItems if required. * * The default implementation will return a simple @c ProjectFileItem */ virtual ProjectFileItem* createFileItem( KDevelop::IProject* project, const KUrl& url, KDevelop::ProjectBaseItem* parent); /** * @return the @c KDirWatch for the given @p project. */ KDirWatch* projectWatcher( KDevelop::IProject* project ) const; Q_SIGNALS: void folderAdded(KDevelop::ProjectFolderItem* folder); void folderRemoved(KDevelop::ProjectFolderItem* folder); void folderRenamed(const KUrl& oldFolder, KDevelop::ProjectFolderItem* newFolder); void fileAdded(KDevelop::ProjectFileItem* file); void fileRemoved(KDevelop::ProjectFileItem* file); void fileRenamed(const KUrl& oldFile, KDevelop::ProjectFileItem* newFile); private: struct Private; // friend class Private; Private* const d; Q_PRIVATE_SLOT(d, KJob* eventuallyReadFolder( ProjectFolderItem* item, const bool forceRecursion = false )) Q_PRIVATE_SLOT(d, void addJobItems(FileManagerListJob* job, ProjectFolderItem* baseItem, const KIO::UDSEntryList& entries, const bool forceRecursion)) Q_PRIVATE_SLOT(d, void deleted(const QString &path)) Q_PRIVATE_SLOT(d, void created(const QString &path)) Q_PRIVATE_SLOT(d, void projectClosing(KDevelop::IProject* project)) Q_PRIVATE_SLOT(d, void jobFinished(KJob* job)) }; } #endif // ABSTRACTGENERICMANAGER_H diff --git a/project/interfaces/ibuildsystemmanager.h b/project/interfaces/ibuildsystemmanager.h index eba69848d9..08dea7fddd 100644 --- a/project/interfaces/ibuildsystemmanager.h +++ b/project/interfaces/ibuildsystemmanager.h @@ -1,132 +1,130 @@ /* This file is part of KDevelop Copyright 2006 Matt Rogers Copyright 2006 Hamish Rodda Copyright 2007 Andreas Pakulat This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef IBUILDSYSTEMMANAGER_H #define IBUILDSYSTEMMANAGER_H #include #include "iprojectfilemanager.h" #include "../projectexport.h" namespace KDevelop { class IProjectBuilder; class ProjectTargetItem; -typedef QList FileItemList; -typedef QPair TargetFilePair; /** * Manages the build system of the project. * * Use/Implement the IProjectFileManager interface to manage files. * * @author Matt Rogers , Hamish Rodda */ class KDEVPLATFORMPROJECT_EXPORT IBuildSystemManager : public virtual IProjectFileManager { public: virtual ~IBuildSystemManager(); enum BuildFeature { Includes /**< This project supports passing include directives to the compiler */, Defines /**< This project supports passing preprocessor defines to compiler */ }; Q_DECLARE_FLAGS( BuildFeatures, BuildFeature ) /** * Provide access to the builder. This method never returns * null, if it does thats a bug in the plugin. A BuildSystemManager * always has a project builder associated with it. */ virtual IProjectBuilder* builder(ProjectFolderItem*) const = 0; /** * Provide a list of include directories. */ virtual KUrl::List includeDirectories(ProjectBaseItem*) const = 0; /** * Provide a list of preprocessor defines for the project item */ virtual QHash defines(ProjectBaseItem*) const = 0; /** * Create a new target * * Creates the target specified by @p target to the folder @p parent and * modifies the underlying build system if needed */ virtual ProjectTargetItem* createTarget(const QString& target, ProjectFolderItem *parent) = 0; /** * Remove a target * * Removes the target specified by @p target and * modifies the underlying build system if needed. */ virtual bool removeTarget(ProjectTargetItem *target) = 0; /** * Get a list of all the targets in this project * * The list returned by this function should be checked to verify it is not * empty before using it * * @return The list of targets for this project * @todo implement */ virtual QList targets(ProjectFolderItem*) const = 0; /** * Add a file to a target * * Adds the file specified by @p file to the target @p parent and modifies * the underlying build system if needed. */ - virtual bool addFileToTarget(ProjectFileItem *file, ProjectTargetItem *parent) = 0; + virtual bool addFilesToTarget(QList files, ProjectTargetItem *target) = 0; /** * Remove files from targets * * Removes the files from the targets they are paired with (@p targetFiles) * Files are not removed from the folders or the filesystem. */ - virtual bool removeFilesFromTargets(QList targetFiles) = 0; + virtual bool removeFilesFromTargets( QList files ) = 0; /** * Get the toplevel build directory for the project */ virtual KUrl buildDirectory(ProjectBaseItem*) const = 0; }; } Q_DECLARE_OPERATORS_FOR_FLAGS( KDevelop::IBuildSystemManager::BuildFeatures ) Q_DECLARE_INTERFACE( KDevelop::IBuildSystemManager, "org.kdevelop.IBuildSystemManager" ) #endif diff --git a/project/interfaces/iprojectfilemanager.h b/project/interfaces/iprojectfilemanager.h index aad8beb410..a6816b0073 100644 --- a/project/interfaces/iprojectfilemanager.h +++ b/project/interfaces/iprojectfilemanager.h @@ -1,163 +1,175 @@ /* This file is part of KDevelop Copyright 2004 Roberto Raggi Copyright 2006 Matt Rogers Copyright 2006 Hamish Rodda Copyright 2007 Andreas Pakulat #include #include "../projectexport.h" class KJob; namespace KDevelop { class IProject; class ProjectBaseItem; class ProjectFolderItem; class ProjectFileItem; /** * @short An interface to project file management * * FileManager is the class you want to implement for integrating * a project manager in KDevelop. For build systems, implement its * child class, BuildManager. * * These classes \e do \e not cause files, folders etc. to be created * or removed on disk. They simply read from and write to the file(s) * which describe the structure (eg. CMakeLists.txt for cmake, Makefile.am for automake, etc). * * @author Roberto Raggi, Matt Rogers, Hamish Rodda */ class KDEVPLATFORMPROJECT_EXPORT IProjectFileManager { public: virtual ~IProjectFileManager(); /** Features the file manager supports */ enum Feature { None = 0 , ///< This manager supports nothing Folders = 1 << 0, ///< Folders are supported by the manager Targets = 1 << 1, ///< Targets are supported by the manager Files = 1 << 2 ///< Files are supported by the manager }; Q_DECLARE_FLAGS( Features, Feature ) /** * @return the Features supported by the filemanager */ virtual Features features() const = 0; /** * This method initialize the model item @arg dom * @return The list of the sub folders */ virtual QList parse(ProjectFolderItem *dom) = 0; /** * This method creates the root item from the file @arg fileName * @return The created item */ virtual ProjectFolderItem *import(IProject *project) = 0; /** * This method creates an import job for the given @arg item * * The default implementation should be suitable for most needs, * it'll create an instance of @class ImportProjectJob * * @return a job that imports the project */ virtual KJob* createImportJob(ProjectFolderItem* item); /** * Add a folder to the project and create it on disk. * * Adds the folder specified by @p folder to @p parent and modifies the * underlying build system if needed */ virtual ProjectFolderItem* addFolder(const KUrl& folder, ProjectFolderItem *parent) = 0; /** * Add a file to a folder and create it on disk. * * Adds the file specified by @p file to the folder @p parent and modifies * the underlying build system if needed. The file is not added to a target */ virtual ProjectFileItem* addFile(const KUrl& file, ProjectFolderItem *parent) = 0; /** * Remove files or folders from the project and delete them from disk * * Removes the files or folders specified by @p items and * modifies the underlying build system if needed. + * + * Note: Do not attempt to move subitems along with their parents */ virtual bool removeFilesAndFolders( QList items) = 0; + /** + * Move files and folders within a given project + * + * Moves the files or folders specified by @p items to @p newParent and + * modifies the underlying build system as needed + * + * Note: Do not attempt to move subitems along with their parents + */ + virtual bool moveFilesAndFolders( QList< KDevelop::ProjectBaseItem* > items, KDevelop::ProjectFolderItem* newParent ) = 0; + /** * Rename a file in the project * * Renames the file specified by @p oldFile to @p newFile * */ virtual bool renameFile(ProjectFileItem* oldFile, const KUrl& newFile) = 0; /** * Rename a folder in the project * * Renames the folder specified by @p oldFile to @p newFile */ virtual bool renameFolder(ProjectFolderItem* oldFolder, const KUrl& newFolder ) = 0; /** * Reload an item in the project * * Reloads the item specified by @p item */ virtual bool reload(ProjectFolderItem* item) = 0; Q_SIGNALS: void folderAdded( ProjectFolderItem* folder ); void folderRemoved( ProjectFolderItem* folder ); void folderRenamed( const KUrl& oldFolder, ProjectFolderItem* newFolder ); void fileAdded(ProjectFileItem* file); void fileRemoved(ProjectFileItem* file); void fileRenamed(const KUrl& oldFile, ProjectFileItem* newFile); }; } Q_DECLARE_OPERATORS_FOR_FLAGS( KDevelop::IProjectFileManager::Features ) Q_DECLARE_INTERFACE( KDevelop::IProjectFileManager, "org.kdevelop.IProjectFileManager") #endif diff --git a/project/projectmodel.cpp b/project/projectmodel.cpp index 981bd3f698..dfccb8ee37 100644 --- a/project/projectmodel.cpp +++ b/project/projectmodel.cpp @@ -1,1016 +1,1031 @@ /* This file is part of KDevelop Copyright 2005 Roberto Raggi Copyright 2007 Andreas Pakulat Copyright 2007 Aleix Pol This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "projectmodel.h" #include #include #include #include #include #include #include #include #include #include #include "interfaces/iprojectfilemanager.h" #include #include #include #include #include #include #include // Utility function to determine between direct connection and blocking-queued-connection // for emitting of signals for data changes/row addition/removal // BlockingQueuedConnection is necessary here as slots connected to the "aboutToBe" signals // expect the actual model content to not have changed yet. So we need to make sure the // signal is delivered before we really do something. static Qt::ConnectionType getConnectionTypeForSignalDelivery( KDevelop::ProjectModel* model ) { if( QThread::currentThread() == model->thread() ) { return Qt::DirectConnection; } else { return Qt::BlockingQueuedConnection; } } namespace KDevelop { QStringList removeProjectBasePath( const QStringList& fullpath, KDevelop::ProjectBaseItem* item ) { QStringList result = fullpath; if( item ) { KDevelop::ProjectModel* model = KDevelop::ICore::self()->projectController()->projectModel(); QStringList basePath = model->pathFromIndex( model->indexFromItem( item ) ); if( basePath.count() >= fullpath.count() ) { return QStringList(); } for( int i = 0; i < basePath.count(); i++ ) { result.takeFirst(); } } return result; } QStringList joinProjectBasePath( const QStringList& partialpath, KDevelop::ProjectBaseItem* item ) { QStringList basePath; if( item ) { KDevelop::ProjectModel* model = KDevelop::ICore::self()->projectController()->projectModel(); basePath = model->pathFromIndex( model->indexFromItem( item ) ); } return basePath + partialpath; } class ProjectModelPrivate { public: ProjectModelPrivate( ProjectModel* model ): model( model ) { } ProjectBaseItem* rootItem; ProjectModel* model; ProjectBaseItem* itemFromIndex( const QModelIndex& idx ) { if( !idx.isValid() ) { return rootItem; } if( idx.model() != model ) { return 0; } return model->itemFromIndex( idx ); } }; class ProjectBaseItemPrivate { public: ProjectBaseItemPrivate() : project(0), parent(0), row(-1), model(0) {} IProject* project; ProjectBaseItem* parent; int row; QList children; QString text; ProjectBaseItem::ProjectItemType type; Qt::ItemFlags flags; ProjectModel* model; KUrl m_url; }; ProjectBaseItem::ProjectBaseItem( IProject* project, const QString &name, ProjectBaseItem *parent ) : d_ptr(new ProjectBaseItemPrivate) { Q_D(ProjectBaseItem); d->project = project; d->text = name; d->flags = Qt::ItemIsSelectable | Qt::ItemIsEnabled; if( parent ) { parent->appendRow( this ); } } ProjectBaseItem::~ProjectBaseItem() { Q_D(ProjectBaseItem); if( parent() ) { parent()->takeRow( d->row ); } else if( model() ) { model()->takeRow( d->row ); } removeRows(0, d->children.size()); delete d; } ProjectBaseItem* ProjectBaseItem::child( int row ) const { Q_D(const ProjectBaseItem); if( row < 0 || row >= d->children.length() ) { return 0; } return d->children.at( row ); } QList< ProjectBaseItem* > ProjectBaseItem::children() const { Q_D(const ProjectBaseItem); return d->children; } ProjectBaseItem* ProjectBaseItem::takeRow(int row) { Q_D(ProjectBaseItem); Q_ASSERT(row >= 0 && row < d->children.size()); if( model() ) { QMetaObject::invokeMethod( model(), "rowsAboutToBeRemoved", getConnectionTypeForSignalDelivery( model() ), Q_ARG(QModelIndex, index()), Q_ARG(int, row), Q_ARG(int, row) ); } ProjectBaseItem* olditem = d->children.takeAt( row ); olditem->d_func()->parent = 0; olditem->d_func()->row = -1; olditem->setModel( 0 ); for(int i=row; id_func()->row--; Q_ASSERT(child(i)->d_func()->row==i); } if( model() ) { QMetaObject::invokeMethod( model(), "rowsRemoved", getConnectionTypeForSignalDelivery( model() ), Q_ARG(QModelIndex, index()), Q_ARG(int, row), Q_ARG(int, row) ); } return olditem; } void ProjectBaseItem::removeRow( int row ) { delete takeRow( row ); } void ProjectBaseItem::removeRows(int row, int count) { if (!count) { return; } Q_D(ProjectBaseItem); Q_ASSERT(row >= 0 && row + count <= d->children.size()); if( model() ) { QMetaObject::invokeMethod( model(), "rowsAboutToBeRemoved", getConnectionTypeForSignalDelivery( model() ), Q_ARG(QModelIndex, index()), Q_ARG(int, row), Q_ARG(int, row + count - 1) ); } QList toRemove; #if QT_VERSION >= 0x040700 toRemove.reserve(count); #endif if (row == 0 && count == d->children.size()) { // optimize shutdown for big projects toRemove = d->children; d->children.clear(); } else { for (int i = row; i < count; ++i) { toRemove << d->children.takeAt( row ); } for(int i = row; i < d->children.size(); ++i) { d->children.at(i)->d_func()->row--; Q_ASSERT(child(i)->d_func()->row==i); } } foreach(ProjectBaseItem* item, toRemove) { item->d_func()->parent = 0; item->d_func()->row = -1; item->setModel( 0 ); delete item; } if( model() ) { QMetaObject::invokeMethod( model(), "rowsRemoved", getConnectionTypeForSignalDelivery( model() ), Q_ARG(QModelIndex, index()), Q_ARG(int, row), Q_ARG(int, row + count - 1) ); } } QModelIndex ProjectBaseItem::index() const { if( model() ) { return model()->indexFromItem( this ); } return QModelIndex(); } int ProjectBaseItem::rowCount() const { Q_D(const ProjectBaseItem); return d->children.count(); } int ProjectBaseItem::type() const { return ProjectBaseItem::BaseItem; } ProjectModel* ProjectBaseItem::model() const { Q_D(const ProjectBaseItem); return d->model; } ProjectBaseItem* ProjectBaseItem::parent() const { Q_D(const ProjectBaseItem); if( model() && model()->d->rootItem == d->parent ) { return 0; } return d->parent; } int ProjectBaseItem::row() const { Q_D(const ProjectBaseItem); return d->row; } QString ProjectBaseItem::text() const { Q_D(const ProjectBaseItem); if( project() && !parent() ) { return project()->name(); } else { Q_ASSERT(!d->text.isEmpty()); return d->text; } } void ProjectBaseItem::setModel( ProjectModel* model ) { Q_D(ProjectBaseItem); d->model = model; foreach( ProjectBaseItem* item, d->children ) { item->setModel( model ); } } void ProjectBaseItem::setRow( int row ) { Q_D(ProjectBaseItem); d->row = row; } void ProjectBaseItem::setText( const QString& text ) { Q_D(ProjectBaseItem); d->text = text; if( model() ) { QMetaObject::invokeMethod( model(), "dataChanged", getConnectionTypeForSignalDelivery( model() ), Q_ARG(QModelIndex, index()), Q_ARG(QModelIndex, index()) ); } } ProjectBaseItem::RenameStatus ProjectBaseItem::rename(const QString& newname) { setText( newname ); return RenameOk; } KDevelop::ProjectBaseItem::ProjectItemType baseType( int type ) { if( type == KDevelop::ProjectBaseItem::Folder || type == KDevelop::ProjectBaseItem::BuildFolder ) return KDevelop::ProjectBaseItem::Folder; if( type == KDevelop::ProjectBaseItem::Target || type == KDevelop::ProjectBaseItem::ExecutableTarget || type == KDevelop::ProjectBaseItem::LibraryTarget) return KDevelop::ProjectBaseItem::Target; return static_cast( type ); } bool ProjectBaseItem::lessThan( const KDevelop::ProjectBaseItem* item ) const { if(item->type() >= KDevelop::ProjectBaseItem::CustomProjectItemType ) { // For custom types we want to make sure that if they override lessThan, then we // prefer their lessThan implementation return !item->lessThan( this ); } KDevelop::ProjectBaseItem::ProjectItemType leftType=baseType(type()), rightType=baseType(item->type()); if(leftType==rightType) { if(leftType==KDevelop::ProjectBaseItem::File) { return file()->fileName().compare(item->file()->fileName(), Qt::CaseInsensitive) < 0; } return this->text()text(); } else { return leftTypeurl().path() < item2->url().path(); +} + IProject* ProjectBaseItem::project() const { Q_D(const ProjectBaseItem); return d->project; } void ProjectBaseItem::appendRow( ProjectBaseItem* item ) { Q_D(ProjectBaseItem); if( !item ) { return; } if( item->parent() ) { // Proper way is to first removeRow() on the original parent, then appendRow on this one kWarning() << "Ignoring double insertion of item" << item; return; } int startrow,endrow; if( model() ) { startrow = endrow = d->children.count(); QMetaObject::invokeMethod( model(), "rowsAboutToBeInserted", getConnectionTypeForSignalDelivery( model() ), Q_ARG(QModelIndex, index()), Q_ARG(int, startrow), Q_ARG(int, endrow) ); } d->children.append( item ); item->setRow( d->children.count() - 1 ); item->d_func()->parent = this; item->setModel( model() ); if( model() ) { QMetaObject::invokeMethod( model(), "rowsInserted", getConnectionTypeForSignalDelivery( model() ), Q_ARG( QModelIndex, index() ), Q_ARG( int, startrow ), Q_ARG( int, endrow ) ); } } KUrl ProjectBaseItem::url( ) const { Q_D(const ProjectBaseItem); return d->m_url; } QString ProjectBaseItem::baseName() const { return text(); } void ProjectBaseItem::setUrl( const KUrl& url ) { Q_D(ProjectBaseItem); d->m_url = url; const QString baseName = url.fileName(); setText( baseName ); } Qt::ItemFlags ProjectBaseItem::flags() { Q_D(ProjectBaseItem); return d->flags; } +Qt::DropActions ProjectModel::supportedDropActions() const +{ + return (Qt::DropActions)(Qt::MoveAction); +} + void ProjectBaseItem::setFlags(Qt::ItemFlags flags) { Q_D(ProjectBaseItem); d->flags = flags; if(d->model) d->model->dataChanged(index(), index()); } QString ProjectBaseItem::iconName() const { return ""; } ProjectFolderItem *ProjectBaseItem::folder() const { return 0; } ProjectTargetItem *ProjectBaseItem::target() const { return 0; } ProjectExecutableTargetItem *ProjectBaseItem::executable() const { return 0; } ProjectFileItem *ProjectBaseItem::file() const { return 0; } QList ProjectBaseItem::folderList() const { QList lst; for ( int i = 0; i < rowCount(); ++i ) { ProjectBaseItem* item = child( i ); if ( item->type() == Folder || item->type() == BuildFolder ) { ProjectFolderItem *kdevitem = dynamic_cast( item ); if ( kdevitem ) lst.append( kdevitem ); } } return lst; } QList ProjectBaseItem::targetList() const { QList lst; for ( int i = 0; i < rowCount(); ++i ) { ProjectBaseItem* item = child( i ); if ( item->type() == Target || item->type() == LibraryTarget || item->type() == ExecutableTarget ) { ProjectTargetItem *kdevitem = dynamic_cast( item ); if ( kdevitem ) lst.append( kdevitem ); } } return lst; } QList ProjectBaseItem::fileList() const { QList lst; for ( int i = 0; i < rowCount(); ++i ) { ProjectBaseItem* item = child( i ); Q_ASSERT(item); if ( item && item->type() == File ) { ProjectFileItem *kdevitem = dynamic_cast( item ); if ( kdevitem ) lst.append( kdevitem ); } } return lst; } void ProjectModel::resetModel() { reset(); } void ProjectModel::clear() { d->rootItem->removeRows(0, d->rootItem->rowCount()); } ProjectFolderItem::ProjectFolderItem( IProject* project, const KUrl & dir, ProjectBaseItem * parent ) : ProjectBaseItem( project, dir.fileName(), parent ) { + setFlags(flags() | Qt::ItemIsDropEnabled); + if (project->folder() != dir) + setFlags(flags() | Qt::ItemIsDragEnabled); setUrl( dir ); } ProjectFolderItem::~ProjectFolderItem() { } void ProjectFolderItem::setUrl( const KUrl& url ) { KUrl copy(url); copy.adjustPath(KUrl::AddTrailingSlash); ProjectBaseItem::setUrl(copy); propagateRename(url); } ProjectFolderItem *ProjectFolderItem::folder() const { return const_cast(this); } int ProjectFolderItem::type() const { return ProjectBaseItem::Folder; } QString ProjectFolderItem::folderName() const { return baseName(); } void ProjectFolderItem::propagateRename(const KUrl& newBase) const { KUrl url = newBase; url.addPath("dummy"); foreach( KDevelop::ProjectBaseItem* child, children() ) { url.setFileName( child->text() ); child->setUrl( url ); const ProjectFolderItem* folder = child->folder(); if ( folder ) { folder->propagateRename( url ); } } } ProjectBaseItem::RenameStatus ProjectFolderItem::rename(const QString& newname) { //TODO: Same as ProjectFileItem, so should be shared somehow KUrl dest = url().upUrl(); dest.addPath(newname); if( !newname.contains('/') ) { KIO::UDSEntry entry; //There exists a file with that name? if( !KIO::NetAccess::stat(dest, entry, 0) ) { if( !project() || project()->projectFileManager()->renameFolder(this, dest) ) { return ProjectBaseItem::RenameOk; } else { return ProjectBaseItem::ProjectManagerRenameFailed; } } else { return ProjectBaseItem::ExistingItemSameName; } } else { return ProjectBaseItem::InvalidNewName; } } bool ProjectFolderItem::hasFileOrFolder(const QString& name) const { foreach ( ProjectBaseItem* item, children() ) { if ( (item->type() == Folder || item->type() == File || item->type() == BuildFolder) && name == item->baseName() ) { return true; } } return false; } ProjectBuildFolderItem::ProjectBuildFolderItem( IProject* project, const KUrl &dir, ProjectBaseItem *parent) : ProjectFolderItem( project, dir, parent ) { } QString ProjectFolderItem::iconName() const { return "folder"; } int ProjectBuildFolderItem::type() const { return ProjectBaseItem::BuildFolder; } QString ProjectBuildFolderItem::iconName() const { return "folder-development"; } ProjectFileItem::ProjectFileItem( IProject* project, const KUrl & file, ProjectBaseItem * parent ) : ProjectBaseItem( project, file.fileName(), parent ) { + setFlags(flags() | Qt::ItemIsDragEnabled); setUrl( file ); // Need to this manually here as setUrl() is virtual and hence the above // only calls the version in ProjectBaseItem and not ours if( project ) { project->addToFileSet( KDevelop::IndexedString(file) ); } } ProjectFileItem::~ProjectFileItem() { if( project() ) { project()->removeFromFileSet(KDevelop::IndexedString(url())); } } QString ProjectFileItem::iconName() const { return KMimeType::findByUrl(url(), 0, false, true)->iconName(url()); } ProjectBaseItem::RenameStatus ProjectFileItem::rename(const QString& newname) { KUrl dest = url().upUrl(); dest.addPath(newname); if( !newname.contains('/') ) { KIO::UDSEntry entry; //There exists a file with that name? if( !KIO::NetAccess::stat(dest, entry, 0) ) { if( !project() || project()->projectFileManager()->renameFile(this, dest) ) { return ProjectBaseItem::RenameOk; } else { return ProjectBaseItem::ProjectManagerRenameFailed; } } else { return ProjectBaseItem::ExistingItemSameName; } } else { return ProjectBaseItem::InvalidNewName; } } QString ProjectFileItem::fileName() const { return baseName(); } void ProjectFileItem::setUrl( const KUrl& url ) { if( project() ) { if(!this->url().isEmpty()) project()->removeFromFileSet( KDevelop::IndexedString(this->url()) ); project()->addToFileSet( KDevelop::IndexedString(url) ); } ProjectBaseItem::setUrl( url ); } int ProjectFileItem::type() const { return ProjectBaseItem::File; } ProjectFileItem *ProjectFileItem::file() const { return const_cast( this ); } ProjectTargetItem::ProjectTargetItem( IProject* project, const QString &name, ProjectBaseItem *parent ) : ProjectBaseItem( project, name, parent ) { + setFlags(flags() | Qt::ItemIsDropEnabled); } QString ProjectTargetItem::iconName() const { return "system-run"; } int ProjectTargetItem::type() const { return ProjectBaseItem::Target; } ProjectTargetItem *ProjectTargetItem::target() const { return const_cast( this ); } ProjectExecutableTargetItem::ProjectExecutableTargetItem( IProject* project, const QString &name, ProjectBaseItem *parent ) : ProjectTargetItem(project, name, parent) { } ProjectExecutableTargetItem *ProjectExecutableTargetItem::executable() const { return const_cast( this ); } int ProjectExecutableTargetItem::type() const { return ProjectBaseItem::ExecutableTarget; } ProjectLibraryTargetItem::ProjectLibraryTargetItem( IProject* project, const QString &name, ProjectBaseItem *parent ) : ProjectTargetItem(project, name, parent) {} int ProjectLibraryTargetItem::type() const { return ProjectBaseItem::LibraryTarget; } QModelIndex ProjectModel::pathToIndex(const QStringList& tofetch_) const { if(tofetch_.isEmpty()) return QModelIndex(); QStringList tofetch(tofetch_); if(tofetch.last().isEmpty()) tofetch.takeLast(); QModelIndex current=index(0,0, QModelIndex()); QModelIndex ret; for(int a = 0; a < tofetch.size(); ++a) { const QString& currentName = tofetch[a]; bool matched = false; QModelIndexList l = match(current, Qt::DisplayRole, currentName, -1, Qt::MatchExactly); foreach(const QModelIndex& idx, l) { //If this is not the last item, only match folders, as there may be targets and folders with the same name if(a == tofetch.size()-1 || itemFromIndex(idx)->folder()) { ret = idx; current = index(0,0, ret); matched = true; break; } } if(!matched) { ret = QModelIndex(); break; } } Q_ASSERT(!ret.isValid() || data(ret).toString()==tofetch.last()); return ret; } QStringList ProjectModel::pathFromIndex(const QModelIndex& index) const { if (!index.isValid()) return QStringList(); QModelIndex idx = index; QStringList list; do { QString t = data(idx, Qt::DisplayRole).toString(); list.prepend(t); QModelIndex parent = idx.parent(); idx = parent.sibling(parent.row(), index.column()); } while (idx.isValid()); return list; } int ProjectModel::columnCount( const QModelIndex& ) const { return 1; } int ProjectModel::rowCount( const QModelIndex& parent ) const { ProjectBaseItem* item = d->itemFromIndex( parent ); return item ? item->rowCount() : 0; } QModelIndex ProjectModel::parent( const QModelIndex& child ) const { if( child.isValid() ) { ProjectBaseItem* item = static_cast( child.internalPointer() ); return indexFromItem( item ); } return QModelIndex(); } QModelIndex ProjectModel::indexFromItem( const ProjectBaseItem* item ) const { if( item && item->d_func()->parent ) { return createIndex( item->row(), 0, item->d_func()->parent ); } return QModelIndex(); } ProjectBaseItem* ProjectModel::itemFromIndex( const QModelIndex& index ) const { if( index.row() >= 0 && index.column() == 0 && index.model() == this ) { ProjectBaseItem* parent = static_cast( index.internalPointer() ); if( parent ) { return parent->child( index.row() ); } } return 0; } QVariant ProjectModel::data( const QModelIndex& index, int role ) const { if( ( role == Qt::DisplayRole || role == Qt::ToolTipRole || role == Qt::DecorationRole ) && index.isValid() ) { ProjectBaseItem* item = itemFromIndex( index ); if( item ) { switch(role) { case Qt::DecorationRole: return item->iconName(); case Qt::ToolTipRole: return item->url().prettyUrl(); default: return item->text(); } } } return QVariant(); } ProjectModel::ProjectModel( QObject *parent ) : QAbstractItemModel( parent ), d( new ProjectModelPrivate( this ) ) { d->rootItem = new ProjectBaseItem( 0, "", 0 ); d->rootItem->setModel( this ); } ProjectModel::~ProjectModel() { } ProjectVisitor::ProjectVisitor() { } QModelIndex ProjectModel::index( int row, int column, const QModelIndex& parent ) const { ProjectBaseItem* parentItem = d->itemFromIndex( parent ); if( parentItem && row >= 0 && row < parentItem->rowCount() && column == 0 ) { return createIndex( row, column, parentItem ); } return QModelIndex(); } void ProjectModel::appendRow( ProjectBaseItem* item ) { d->rootItem->appendRow( item ); } void ProjectModel::removeRow( int row ) { d->rootItem->removeRow( row ); } ProjectBaseItem* ProjectModel::takeRow( int row ) { return d->rootItem->takeRow( row ); } bool ProjectModel::hasChildren(const QModelIndex& parent) const { bool b = QAbstractItemModel::hasChildren(parent); return b; } Qt::ItemFlags ProjectModel::flags(const QModelIndex& index) const { ProjectBaseItem* item = itemFromIndex( index ); if(item) return item->flags(); else return 0; } bool ProjectModel::insertColumns(int, int, const QModelIndex&) { // Not supported return false; } bool ProjectModel::insertRows(int, int, const QModelIndex&) { // Not supported return false; } bool ProjectModel::setData(const QModelIndex&, const QVariant&, int) { // Not supported return false; } void ProjectVisitor::visit ( IProject* prj ) { visit( prj->projectItem() ); } void ProjectVisitor::visit ( ProjectBuildFolderItem* folder ) { foreach( ProjectFileItem* item, folder->fileList() ) { visit( item ); } foreach( ProjectTargetItem* item, folder->targetList() ) { if( item->type() == ProjectBaseItem::LibraryTarget ) { visit( dynamic_cast( item ) ); } else if( item->type() == ProjectBaseItem::ExecutableTarget ) { visit( dynamic_cast( item ) ); } } foreach( ProjectFolderItem* item, folder->folderList() ) { if( item->type() == ProjectBaseItem::BuildFolder ) { visit( dynamic_cast( item ) ); } else if( item->type() == ProjectBaseItem::Folder ) { visit( dynamic_cast( item ) ); } } } void ProjectVisitor::visit ( ProjectExecutableTargetItem* exec ) { foreach( ProjectFileItem* item, exec->fileList() ) { visit( item ); } } void ProjectVisitor::visit ( ProjectFolderItem* folder ) { foreach( ProjectFileItem* item, folder->fileList() ) { visit( item ); } foreach( ProjectTargetItem* item, folder->targetList() ) { if( item->type() == ProjectBaseItem::LibraryTarget ) { visit( dynamic_cast( item ) ); } else if( item->type() == ProjectBaseItem::ExecutableTarget ) { visit( dynamic_cast( item ) ); } } foreach( ProjectFolderItem* item, folder->folderList() ) { if( item->type() == ProjectBaseItem::BuildFolder ) { visit( dynamic_cast( item ) ); } else if( item->type() == ProjectBaseItem::Folder ) { visit( dynamic_cast( item ) ); } } } void ProjectVisitor::visit ( ProjectFileItem* ) { } void ProjectVisitor::visit ( ProjectLibraryTargetItem* lib ) { foreach( ProjectFileItem* item, lib->fileList() ) { visit( item ); } } ProjectVisitor::~ProjectVisitor() { } } #include "projectmodel.moc" diff --git a/project/projectmodel.h b/project/projectmodel.h index d1cb3cc688..a0b79a7d33 100644 --- a/project/projectmodel.h +++ b/project/projectmodel.h @@ -1,381 +1,383 @@ /* This file is part of KDevelop Copyright 2005 Roberto Raggi Copyright 2007 Andreas Pakulat Copyright 2007 Aleix Pol This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KDEVPROJECTMODEL_H #define KDEVPROJECTMODEL_H #include #include "projectexport.h" #include #include template struct QPair; template class QList; namespace KDevelop { class IProject; class ProjectFolderItem; class ProjectBuildFolderItem; class ProjectFileItem; class ProjectTargetItem; class ProjectExecutableTargetItem; class ProjectLibraryTargetItem; class ProjectModel; class IndexedString; class KDEVPLATFORMPROJECT_EXPORT ProjectVisitor { public: ProjectVisitor(); virtual ~ProjectVisitor(); virtual void visit( IProject* ); virtual void visit( ProjectBuildFolderItem* ); virtual void visit( ProjectExecutableTargetItem* ); virtual void visit( ProjectFolderItem* ); virtual void visit( ProjectFileItem* ); virtual void visit( ProjectLibraryTargetItem* ); }; /** * Interface that allows a developer to implement the three basic types of * items you would see in a multi-project * \li Folder * \li Project * \li Custom Target * \li Library Target * \li Executable Target * \li File */ class KDEVPLATFORMPROJECT_EXPORT ProjectBaseItem { public: ProjectBaseItem( IProject*, const QString &name, ProjectBaseItem *parent = 0 ); virtual ~ProjectBaseItem(); enum ProjectItemType { BaseItem = 0 /** item is a base item */, BuildFolder = 1 /** item is a buildable folder */, Folder = 2 /** item is a folder */, ExecutableTarget = 3 /** item is an executable target */, LibraryTarget = 4 /** item is a library target */, Target = 5 /** item is a target */, File = 6 /** item is a file */, CustomProjectItemType = 100 /** type which should be used as base for custom types */ }; enum RenameStatus { RenameOk = 0, ExistingItemSameName = 1, ProjectManagerRenameFailed = 2, InvalidNewName = 3 }; /** @returns Returns the project that the item belongs to. */ IProject* project() const; /** @returns If this item is a folder, it returns a pointer to the folder, otherwise returns a 0 pointer. */ virtual ProjectFolderItem *folder() const; /** @returns If this item is a target, it returns a pointer to the target, otherwise returns a 0 pointer. */ virtual ProjectTargetItem *target() const; /** @returns If this item is a file, it returns a pointer to the file, otherwise returns a 0 pointer. */ virtual ProjectFileItem *file() const; /** @returns If this item is a file, it returns a pointer to the file, otherwise returns a 0 pointer. */ virtual ProjectExecutableTargetItem *executable() const; /** @returns Returns a list of the folders that have this object as the parent. */ QList folderList() const; /** @returns Returns a list of the targets that have this object as the parent. */ QList targetList() const; /** @returns Returns a list of the files that have this object as the parent. */ QList fileList() const; virtual bool lessThan( const KDevelop::ProjectBaseItem* ) const; + static bool urlLessThan(KDevelop::ProjectBaseItem* item1, KDevelop::ProjectBaseItem* item2); /** @returns the @p row item in the list of children of this item or 0 if there is no such child. */ ProjectBaseItem* child( int row ) const; /** @returns the list of children of this item. */ QList children() const; /** @returns a valid QModelIndex for usage with the model API for this item. */ QModelIndex index() const; /** @returns The parent item if this item has one, else it return 0. */ virtual ProjectBaseItem* parent() const; /** @returns the displayed text of this item. */ QString text() const; /** @returns the row in the list of children of this items parent, or -1. */ int row() const; /** @returns the number of children of this item, or 0 if there are none. */ int rowCount() const; /** @returns the model to which this item belongs, or 0 if its not associated to a model. */ ProjectModel* model() const; /** * Adds a new child item to this item. */ void appendRow( ProjectBaseItem* item ); /** * Removes and deletes the item at the given @p row if there is one. */ void removeRow( int row ); /** * Removes and deletes the @p count items after the given @p row if there is one. */ void removeRows( int row, int count ); /** * Returns and removes the item at the given @p row if there is one. */ ProjectBaseItem* takeRow( int row ); /** @returns RTTI info, allows to know the type of item */ virtual int type() const; /** @returns a string to pass to KIcon as icon-name suitable to represent this item. */ virtual QString iconName() const; /** * Set the url of this item. * Note this function never renames the item in the project manager or on the filesystem, * it only changes the url and possibly the text nothing else. */ virtual void setUrl( const KUrl& ); /** Get the url of this item (if any) */ KUrl url() const; /** Gets the basename of this url (if any) * convenience function, returns the same as @c text() for most items */ QString baseName() const; /** * Renames the item to the new name. * @returns status information wether the renaming succeeded. */ virtual RenameStatus rename( const QString& newname ); /** * Default flags: Qt::ItemIsEnabled | Qt::ItemIsSelectable * * @returns the flags supported by the item */ virtual Qt::ItemFlags flags(); /** * Sets what flags should be returned by ::flags() method. */ void setFlags(Qt::ItemFlags flags); protected: /** * Allows to change the displayed text of this item. * * Most items assume text == baseName so this is *not* public. * * @param text the new text */ void setText( const QString& text ); class ProjectBaseItemPrivate* const d_ptr; ProjectBaseItem( ProjectBaseItemPrivate& dd ); void setRow( int row ); void setModel( ProjectModel* model ); private: Q_DECLARE_PRIVATE(ProjectBaseItem) friend class ProjectModel; }; /** * Implementation of the ProjectBaseItem interface that is specific to a * folder */ class KDEVPLATFORMPROJECT_EXPORT ProjectFolderItem: public ProjectBaseItem { public: ProjectFolderItem( IProject*, const KUrl &dir, ProjectBaseItem *parent = 0 ); virtual ~ProjectFolderItem(); virtual void setUrl(const KUrl& ); virtual ProjectFolderItem *folder() const; ///Reimplemented from QStandardItem virtual int type() const; /** Get the folder name, equal to url().fileName() but faster (precomputed) */ QString folderName() const; /** @returns Returns whether this folder directly contains the specified file or folder. */ bool hasFileOrFolder(const QString& name) const; virtual QString iconName() const; virtual RenameStatus rename(const QString& newname); void propagateRename( const KUrl& newBase ) const; }; /** * Folder which contains buildable targets as part of a buildable project */ class KDEVPLATFORMPROJECT_EXPORT ProjectBuildFolderItem: public ProjectFolderItem { public: ProjectBuildFolderItem( IProject*, const KUrl &dir, ProjectBaseItem *parent = 0 ); ///Reimplemented from QStandardItem virtual int type() const; virtual QString iconName() const; }; /** * Object which represents a target in a build system. * * This object contains all properties specific to a target. */ class KDEVPLATFORMPROJECT_EXPORT ProjectTargetItem: public ProjectBaseItem { public: ProjectTargetItem( IProject*, const QString &name, ProjectBaseItem *parent = 0 ); ///Reimplemented from QStandardItem virtual int type() const; virtual ProjectTargetItem *target() const; virtual QString iconName() const; }; /** * Object which represents an executable target in a build system. * * This object contains all properties specific to an executable. */ class KDEVPLATFORMPROJECT_EXPORT ProjectExecutableTargetItem: public ProjectTargetItem { public: ProjectExecutableTargetItem( IProject*, const QString &name, ProjectBaseItem *parent = 0 ); virtual ProjectExecutableTargetItem *executable() const; virtual int type() const; virtual KUrl builtUrl() const=0; virtual KUrl installedUrl() const=0; }; /** * Object which represents a library target in a build system. * * This object contains all properties specific to a library. */ class KDEVPLATFORMPROJECT_EXPORT ProjectLibraryTargetItem: public ProjectTargetItem { public: ProjectLibraryTargetItem(IProject* project, const QString &name, ProjectBaseItem *parent = 0 ); virtual int type() const; }; /** * Object which represents a file. */ class KDEVPLATFORMPROJECT_EXPORT ProjectFileItem: public ProjectBaseItem { public: ProjectFileItem( IProject*, const KUrl& file, ProjectBaseItem *parent = 0 ); ~ProjectFileItem(); ///Reimplemented from QStandardItem virtual int type() const; virtual ProjectFileItem *file() const; /** Get the file name, equal to url().fileName() but faster (precomputed) */ QString fileName() const; virtual void setUrl( const KUrl& ); virtual QString iconName() const; virtual RenameStatus rename(const QString& newname); }; /** * Class providing some convenience methods for accessing the project model * @todo: maybe switch to QAbstractItemModel, would make the implementation * for at least the checkbox-behaviour easier */ class KDEVPLATFORMPROJECT_EXPORT ProjectModel: public QAbstractItemModel { Q_OBJECT public: ProjectModel( QObject *parent = 0 ); virtual ~ProjectModel(); void resetModel(); void clear(); void appendRow( ProjectBaseItem* item ); void removeRow( int row ); ProjectBaseItem* takeRow( int row ); QModelIndex pathToIndex(const QStringList& tofetch) const; QStringList pathFromIndex(const QModelIndex& index) const; QModelIndex indexFromItem( const ProjectBaseItem* item ) const; ProjectBaseItem* itemFromIndex( const QModelIndex& ) const; virtual int columnCount( const QModelIndex& parent = QModelIndex() ) const; virtual QVariant data( const QModelIndex& index, int role = Qt::DisplayRole ) const; virtual QModelIndex parent( const QModelIndex& child ) const; virtual int rowCount( const QModelIndex& parent = QModelIndex() ) const; virtual QModelIndex index(int row, int column, const QModelIndex& parent = QModelIndex()) const; virtual bool hasChildren(const QModelIndex& parent = QModelIndex()) const; virtual bool setData(const QModelIndex& index, const QVariant& value, int role = Qt::EditRole); virtual bool insertColumns(int column, int count, const QModelIndex& parent = QModelIndex()); virtual bool insertRows(int row, int count, const QModelIndex& parent = QModelIndex()); virtual Qt::ItemFlags flags(const QModelIndex& index) const; + virtual Qt::DropActions supportedDropActions() const; private: class ProjectModelPrivate* const d; friend class ProjectBaseItem; }; KDEVPLATFORMPROJECT_EXPORT QStringList joinProjectBasePath( const QStringList& partialpath, KDevelop::ProjectBaseItem* item ); KDEVPLATFORMPROJECT_EXPORT QStringList removeProjectBasePath( const QStringList& fullpath, KDevelop::ProjectBaseItem* item ); } #endif // KDEVPROJECTMODEL_H diff --git a/shell/kross/xmltokross/dummybsm.h b/shell/kross/xmltokross/dummybsm.h index 5b8b0c6710..a4812cb452 100644 --- a/shell/kross/xmltokross/dummybsm.h +++ b/shell/kross/xmltokross/dummybsm.h @@ -1,75 +1,76 @@ /*************************************************************************** * Copyright 2008 Aleix Pol * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU Library 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 Library 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. * ***************************************************************************/ #ifndef DUMMYBSM_H #define DUMMYBSM_H #include #include #include #include class DummyBSM : public KDevelop::IPlugin, public KDevelop::IBuildSystemManager { Q_OBJECT Q_INTERFACES( KDevelop::IBuildSystemManager ) Q_INTERFACES( KDevelop::IProjectFileManager ) public: explicit DummyBSM(QObject *parent=0, const QVariantList& args=QVariantList(), const KUrl::List& cf=KUrl::List()); QList parse( KDevelop::ProjectFolderItem* dom ) { Q_UNUSED( dom ); return QList(); } KDevelop::ProjectFolderItem* import(KDevelop::IProject *project ); KDevelop::IProjectBuilder* builder(KDevelop::ProjectFolderItem*) const { return 0; } KUrl buildDirectory(KDevelop::ProjectBaseItem*) const { return m_buildDir; } KUrl::List includeDirectories(KDevelop::ProjectBaseItem *) const { qDebug("jojooooooooooojo"); return m_includeDirectories; } QHash defines(KDevelop::ProjectBaseItem *) const { return m_defines; } QList targets() const; QList targets(KDevelop::ProjectFolderItem*) const; KDevelop::ProjectFolderItem* addFolder( const KUrl& /*folder */, KDevelop::ProjectFolderItem* /*parent*/ ) { return 0; } KDevelop::ProjectTargetItem* createTarget( const QString&, KDevelop::ProjectFolderItem* ) { return 0; } KDevelop::ProjectFileItem* addFile( const KUrl&, KDevelop::ProjectFolderItem* ) { return 0; } - bool addFileToTarget( KDevelop::ProjectFileItem*, KDevelop::ProjectTargetItem* ) { return false; } + bool addFilesToTarget( QList, KDevelop::ProjectTargetItem* ) { return false; } bool removeTarget( KDevelop::ProjectTargetItem* ) { return false; } bool removeFilesAndFolders(QList< KDevelop::ProjectBaseItem* > ) { return false; } - bool removeFilesFromTargets( QList ) { return false; } + bool removeFilesFromTargets( QList ) { return false; } bool renameFile(KDevelop::ProjectFileItem*, const KUrl&) { return false; } bool renameFolder(KDevelop::ProjectFolderItem*, const KUrl&) { return false; } + bool moveFilesAndFolders( QList< KDevelop::ProjectBaseItem* >, KDevelop::ProjectFolderItem* ) { return false; }; QHash environment(KDevelop::ProjectBaseItem *) const { return QHash(); } Features features() const { return Targets | Files | Folders; } void setBuildDir(const KUrl& buildDir) { m_buildDir=buildDir; } void setIncludeDirectories(const KUrl::List& id) { m_includeDirectories=id; } void setDefinesDirectories(const QHash& defs) { m_defines=defs; } bool reload(KDevelop::ProjectFolderItem*) { return true; } private: KUrl::List m_controlledFiles; KUrl m_buildDir; KUrl::List m_includeDirectories; QHash m_defines; KDevelop::ProjectTargetItem* m_target; KDevelop::ProjectFolderItem* m_folder; }; #endif