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

0001 /*
0002     SPDX-FileCopyrightText: 2004 Roberto Raggi <roberto@kdevelop.org>
0003     SPDX-FileCopyrightText: 2007 Andreas Pakulat <apaku@gmx.de>
0004     SPDX-FileCopyrightText: 2016, 2017 Alexander Potashev <aspotashev@gmail.com>
0005 
0006     SPDX-License-Identifier: LGPL-2.0-or-later
0007 */
0008 
0009 #include "projectmanagerviewplugin.h"
0010 
0011 #include <QApplication>
0012 #include <QAction>
0013 #include <QClipboard>
0014 #include <QInputDialog>
0015 #include <QList>
0016 #include <QMimeData>
0017 #include <QUrl>
0018 
0019 #include <KActionCollection>
0020 #include <KLocalizedString>
0021 #include <KMessageBox>
0022 #include <KMessageBox_KDevCompat>
0023 #include <KParts/MainWindow>
0024 #include <KPluginFactory>
0025 #include <KIO/Paste>
0026 #include <KFileItem>
0027 #include <KUrlMimeData>
0028 
0029 #include <project/projectmodel.h>
0030 #include <project/projectbuildsetmodel.h>
0031 #include <interfaces/icore.h>
0032 #include <interfaces/iproject.h>
0033 #include <project/interfaces/iprojectfilemanager.h>
0034 #include <project/interfaces/ibuildsystemmanager.h>
0035 #include <interfaces/iuicontroller.h>
0036 #include <interfaces/iruncontroller.h>
0037 #include <interfaces/idocumentcontroller.h>
0038 #include <util/jobstatus.h>
0039 #include <util/path.h>
0040 #include <interfaces/iprojectcontroller.h>
0041 #include <interfaces/context.h>
0042 #include <interfaces/contextmenuextension.h>
0043 #include <interfaces/iselectioncontroller.h>
0044 #include <sublime/message.h>
0045 #include <serialization/indexedstring.h>
0046 
0047 #include "projectmanagerview.h"
0048 #include "debug.h"
0049 #include "cutcopypastehelpers.h"
0050 
0051 using namespace KDevelop;
0052 
0053 K_PLUGIN_FACTORY_WITH_JSON(ProjectManagerFactory, "kdevprojectmanagerview.json", registerPlugin<ProjectManagerViewPlugin>();)
0054 
0055 namespace {
0056 
0057 QAction* createSeparatorAction()
0058 {
0059     auto* separator = new QAction(nullptr);
0060     separator->setSeparator(true);
0061     return separator;
0062 }
0063 
0064 // Returns nullptr iff the list of URLs to copy/cut was empty
0065 QMimeData* createClipboardMimeData(const bool cut)
0066 {
0067     auto* ctx = dynamic_cast<KDevelop::ProjectItemContext*>(
0068         ICore::self()->selectionController()->currentSelection());
0069     QList<QUrl> urls;
0070     QList<QUrl> mostLocalUrls;
0071     const auto& items = ctx->items();
0072     for (const ProjectBaseItem* item : items) {
0073         if (item->folder() || item->file()) {
0074             const QUrl& url = item->path().toUrl();
0075             urls << url;
0076             mostLocalUrls << KFileItem(url).mostLocalUrl();
0077         }
0078     }
0079     qCDebug(PLUGIN_PROJECTMANAGERVIEW) << urls;
0080 
0081     if (urls.isEmpty()) {
0082         return nullptr;
0083     }
0084 
0085     auto* mimeData = new QMimeData;
0086     KIO::setClipboardDataCut(mimeData, cut);
0087     KUrlMimeData::setUrls(urls, mostLocalUrls, mimeData);
0088     return mimeData;
0089 }
0090 
0091 } // anonymous namespace
0092 
0093 class KDevProjectManagerViewFactory: public KDevelop::IToolViewFactory
0094 {
0095     public:
0096         explicit KDevProjectManagerViewFactory( ProjectManagerViewPlugin *plugin ): mplugin( plugin )
0097         {}
0098         QWidget* create( QWidget *parent = nullptr ) override
0099         {
0100             return new ProjectManagerView( mplugin, parent );
0101         }
0102         Qt::DockWidgetArea defaultPosition() const override
0103         {
0104             return Qt::LeftDockWidgetArea;
0105         }
0106         QString id() const override
0107         {
0108             return QStringLiteral("org.kdevelop.ProjectsView");
0109         }
0110     private:
0111         ProjectManagerViewPlugin *mplugin;
0112 };
0113 
0114 class ProjectManagerViewPluginPrivate
0115 {
0116 public:
0117     ProjectManagerViewPluginPrivate()
0118     {}
0119     KDevProjectManagerViewFactory *factory;
0120     QList<QPersistentModelIndex> ctxProjectItemList;
0121     QAction* m_buildAll;
0122     QAction* m_build;
0123     QAction* m_install;
0124     QAction* m_clean;
0125     QAction* m_configure;
0126     QAction* m_prune;
0127 };
0128 
0129 static QList<ProjectBaseItem*> itemsFromIndexes(const QList<QPersistentModelIndex>& indexes)
0130 {
0131     QList<ProjectBaseItem*> items;
0132     ProjectModel* model = ICore::self()->projectController()->projectModel();
0133     items.reserve(indexes.size());
0134     for (const QModelIndex& index : indexes) {
0135         items += model->itemFromIndex(index);
0136     }
0137     return items;
0138 }
0139 
0140 ProjectManagerViewPlugin::ProjectManagerViewPlugin( QObject *parent, const QVariantList& )
0141         : IPlugin( QStringLiteral("kdevprojectmanagerview"), parent ), d(new ProjectManagerViewPluginPrivate)
0142 {
0143     d->m_buildAll = new QAction(i18nc("@action", "Build All Projects"), this);
0144     d->m_buildAll->setIcon(QIcon::fromTheme(QStringLiteral("run-build")));
0145     connect( d->m_buildAll, &QAction::triggered, this, &ProjectManagerViewPlugin::buildAllProjects );
0146     actionCollection()->addAction( QStringLiteral("project_buildall"), d->m_buildAll );
0147 
0148     d->m_build = new QAction(i18nc("@action", "Build Selection"), this);
0149     d->m_build->setIconText(i18nc("@action:intoolbar", "Build"));
0150     actionCollection()->setDefaultShortcut( d->m_build, Qt::Key_F8 );
0151     d->m_build->setIcon(QIcon::fromTheme(QStringLiteral("run-build")));
0152     d->m_build->setEnabled( false );
0153     connect( d->m_build, &QAction::triggered, this, &ProjectManagerViewPlugin::buildProjectItems );
0154     actionCollection()->addAction( QStringLiteral("project_build"), d->m_build );
0155     d->m_install = new QAction(i18nc("@action", "Install Selection"), this);
0156     d->m_install->setIconText(i18nc("@action:intoolbar", "Install"));
0157     d->m_install->setIcon(QIcon::fromTheme(QStringLiteral("run-build-install")));
0158     actionCollection()->setDefaultShortcut(d->m_install, Qt::SHIFT | Qt::Key_F8);
0159     d->m_install->setEnabled( false );
0160     connect( d->m_install, &QAction::triggered, this, &ProjectManagerViewPlugin::installProjectItems );
0161     actionCollection()->addAction( QStringLiteral("project_install"), d->m_install );
0162     d->m_clean = new QAction(i18nc("@action", "Clean Selection"), this);
0163     d->m_clean->setIconText(i18nc("@action:intoolbar", "Clean"));
0164     d->m_clean->setIcon(QIcon::fromTheme(QStringLiteral("run-build-clean")));
0165     d->m_clean->setEnabled( false );
0166     connect( d->m_clean, &QAction::triggered, this, &ProjectManagerViewPlugin::cleanProjectItems );
0167     actionCollection()->addAction( QStringLiteral("project_clean"), d->m_clean );
0168     d->m_configure = new QAction(i18nc("@action", "Configure Selection"), this);
0169     d->m_configure->setMenuRole( QAction::NoRole ); // OSX: Be explicit about role, prevent hiding due to conflict with "Preferences..." menu item 
0170     d->m_configure->setIconText(i18nc("@action:intoolbar", "Configure"));
0171     d->m_configure->setIcon(QIcon::fromTheme(QStringLiteral("run-build-configure")));
0172     d->m_configure->setEnabled( false );
0173     connect( d->m_configure, &QAction::triggered, this, &ProjectManagerViewPlugin::configureProjectItems );
0174     actionCollection()->addAction( QStringLiteral("project_configure"), d->m_configure );
0175     d->m_prune = new QAction(i18nc("@action", "Prune Selection"), this);
0176     d->m_prune->setIconText(i18nc("@action:intoolbar", "Prune"));
0177     d->m_prune->setIcon(QIcon::fromTheme(QStringLiteral("run-build-prune")));
0178     d->m_prune->setEnabled( false );
0179     connect( d->m_prune, &QAction::triggered, this, &ProjectManagerViewPlugin::pruneProjectItems );
0180     actionCollection()->addAction( QStringLiteral("project_prune"), d->m_prune );
0181 
0182     // only add the action so that its known in the actionCollection
0183     // and so that it's shortcut etc. pp. is restored
0184     // apparently that is not possible to be done in the view itself *sigh*
0185     auto locateDocumentAction = actionCollection()->addAction(QStringLiteral("locate_document"));
0186     locateDocumentAction->setShortcutContext(Qt::WidgetWithChildrenShortcut);
0187     actionCollection()->setDefaultShortcut(locateDocumentAction, Qt::CTRL | Qt::Key_Less);
0188 
0189     setXMLFile( QStringLiteral("kdevprojectmanagerview.rc") );
0190     d->factory = new KDevProjectManagerViewFactory( this );
0191     core()->uiController()->addToolView(i18nc("@title:window", "Projects"), d->factory);
0192     connect(core()->selectionController(), &ISelectionController::selectionChanged,
0193              this, &ProjectManagerViewPlugin::updateActionState);
0194     connect(ICore::self()->projectController()->buildSetModel(), &KDevelop::ProjectBuildSetModel::rowsInserted,
0195              this, &ProjectManagerViewPlugin::updateFromBuildSetChange);
0196     connect(ICore::self()->projectController()->buildSetModel(), &KDevelop::ProjectBuildSetModel::rowsRemoved,
0197              this, &ProjectManagerViewPlugin::updateFromBuildSetChange);
0198     connect(ICore::self()->projectController()->buildSetModel(), &KDevelop::ProjectBuildSetModel::modelReset,
0199              this, &ProjectManagerViewPlugin::updateFromBuildSetChange);
0200 }
0201 
0202 void ProjectManagerViewPlugin::updateFromBuildSetChange()
0203 {
0204     updateActionState( core()->selectionController()->currentSelection() );
0205 }
0206 
0207 void ProjectManagerViewPlugin::updateActionState( KDevelop::Context* ctx )
0208 {
0209     bool isEmpty = ICore::self()->projectController()->buildSetModel()->items().isEmpty();
0210     if( isEmpty )
0211     {
0212         isEmpty = !ctx || ctx->type() != Context::ProjectItemContext || static_cast<ProjectItemContext*>(ctx)->items().isEmpty();
0213     }
0214     d->m_build->setEnabled( !isEmpty );
0215     d->m_install->setEnabled( !isEmpty );
0216     d->m_clean->setEnabled( !isEmpty );
0217     d->m_configure->setEnabled( !isEmpty );
0218     d->m_prune->setEnabled( !isEmpty );
0219 }
0220 
0221 ProjectManagerViewPlugin::~ProjectManagerViewPlugin()
0222 {
0223     delete d;
0224 }
0225 
0226 void ProjectManagerViewPlugin::unload()
0227 {
0228     qCDebug(PLUGIN_PROJECTMANAGERVIEW) << "unloading manager view";
0229     core()->uiController()->removeToolView(d->factory);
0230 }
0231 
0232 ContextMenuExtension ProjectManagerViewPlugin::contextMenuExtension(KDevelop::Context* context, QWidget* parent)
0233 {
0234     if( context->type() != KDevelop::Context::ProjectItemContext )
0235         return IPlugin::contextMenuExtension(context, parent);
0236 
0237     auto* ctx = static_cast<KDevelop::ProjectItemContext*>(context);
0238     const QList<KDevelop::ProjectBaseItem*> items = ctx->items();
0239 
0240     d->ctxProjectItemList.clear();
0241 
0242     if( items.isEmpty() )
0243         return IPlugin::contextMenuExtension(context, parent);
0244 
0245     //TODO: also needs: removeTarget, removeFileFromTarget, runTargetsFromContextMenu
0246     ContextMenuExtension menuExt;
0247     bool needsCreateFile = true;
0248     bool needsCreateFolder = true;
0249     bool needsCloseProjects = true;
0250     bool needsBuildItems = true;
0251     bool needsFolderItems = true;
0252     bool needsCutRenameRemove = true;
0253     bool needsRemoveTargetFiles = true;
0254     bool needsPaste = true;
0255 
0256     //needsCreateFile if there is one item and it's a folder or target
0257     needsCreateFile &= (items.count() == 1) && (items.first()->folder() || items.first()->target());
0258     //needsCreateFolder if there is one item and it's a folder
0259     needsCreateFolder &= (items.count() == 1) && (items.first()->folder());
0260     needsPaste = needsCreateFolder;
0261 
0262     d->ctxProjectItemList.reserve(items.size());
0263     for (ProjectBaseItem* item : items) {
0264         d->ctxProjectItemList << item->index();
0265         //needsBuildItems if items are limited to targets and buildfolders
0266         needsBuildItems &= item->target() || item->type() == ProjectBaseItem::BuildFolder;
0267 
0268         //needsCloseProjects if items are limited to top level folders (Project Folders)
0269         needsCloseProjects &= item->folder() && !item->folder()->parent();
0270 
0271         //needsFolderItems if items are limited to folders
0272         needsFolderItems &= (bool)item->folder();
0273 
0274         //needsRemove if items are limited to non-top-level folders or files that don't belong to targets
0275         needsCutRenameRemove &= (item->folder() && item->parent()) || (item->file() && !item->parent()->target());
0276 
0277         //needsRemoveTargets if items are limited to file items with target parents
0278         needsRemoveTargetFiles &= (item->file() && item->parent()->target());
0279     }
0280 
0281     if ( needsCreateFile ) {
0282         auto* action = new QAction(i18nc("@action:inmenu", "Create &File..."), parent);
0283         action->setIcon(QIcon::fromTheme(QStringLiteral("document-new")));
0284         connect( action, &QAction::triggered, this, &ProjectManagerViewPlugin::createFileFromContextMenu );
0285         menuExt.addAction( ContextMenuExtension::FileGroup, action );
0286     }
0287     if ( needsCreateFolder ) {
0288         auto* action = new QAction(i18nc("@action:inmenu", "Create F&older..."), parent);
0289         action->setIcon(QIcon::fromTheme(QStringLiteral("folder-new")));
0290         connect( action, &QAction::triggered, this, &ProjectManagerViewPlugin::createFolderFromContextMenu );
0291         menuExt.addAction( ContextMenuExtension::FileGroup, action );
0292     }
0293 
0294     if ( needsBuildItems ) {
0295         auto* action = new QAction(i18nc("@action:inmenu", "&Build"), parent);
0296         action->setIcon(QIcon::fromTheme(QStringLiteral("run-build")));
0297         connect( action, &QAction::triggered, this, &ProjectManagerViewPlugin::buildItemsFromContextMenu );
0298         menuExt.addAction( ContextMenuExtension::BuildGroup, action );
0299         action = new QAction(i18nc("@action:inmenu", "&Install"), parent);
0300         action->setIcon(QIcon::fromTheme(QStringLiteral("run-build-install")));
0301         connect( action, &QAction::triggered, this, &ProjectManagerViewPlugin::installItemsFromContextMenu );
0302         menuExt.addAction( ContextMenuExtension::BuildGroup, action );
0303         action = new QAction(i18nc("@action:inmenu", "&Clean"), parent);
0304         action->setIcon(QIcon::fromTheme(QStringLiteral("run-build-clean")));
0305         connect( action, &QAction::triggered, this, &ProjectManagerViewPlugin::cleanItemsFromContextMenu );
0306         menuExt.addAction( ContextMenuExtension::BuildGroup, action );
0307         action = new QAction(i18nc("@action:inmenu", "&Add to Build Set"), parent);
0308         action->setIcon(QIcon::fromTheme(QStringLiteral("list-add")));
0309         connect( action, &QAction::triggered, this, &ProjectManagerViewPlugin::addItemsFromContextMenuToBuildset );
0310         menuExt.addAction( ContextMenuExtension::BuildGroup, action );
0311     }
0312 
0313     if ( needsCloseProjects ) {
0314         auto* close = new QAction(i18ncp("@action:inmenu", "C&lose Project", "Close Projects", items.count()), parent);
0315         close->setIcon(QIcon::fromTheme(QStringLiteral("project-development-close")));
0316         connect( close, &QAction::triggered, this, &ProjectManagerViewPlugin::closeProjects );
0317         menuExt.addAction( ContextMenuExtension::ProjectGroup, close );
0318     }
0319     if ( needsFolderItems ) {
0320         auto* action = new QAction(i18nc("@action:inmenu", "&Reload"), parent);
0321         action->setIcon(QIcon::fromTheme(QStringLiteral("view-refresh")));
0322         connect( action, &QAction::triggered, this, &ProjectManagerViewPlugin::reloadFromContextMenu );
0323         menuExt.addAction( ContextMenuExtension::FileGroup, action );
0324     }
0325 
0326     // Populating cut/copy/paste group
0327     if ( !menuExt.actions(ContextMenuExtension::FileGroup).isEmpty() ) {
0328         menuExt.addAction( ContextMenuExtension::FileGroup, createSeparatorAction() );
0329     }
0330     if ( needsCutRenameRemove ) {
0331         QAction* cut = KStandardAction::cut(this, SLOT(cutFromContextMenu()), this);
0332         cut->setShortcutContext(Qt::WidgetShortcut);
0333         menuExt.addAction(ContextMenuExtension::FileGroup, cut);
0334     }
0335     {
0336         QAction* copy = KStandardAction::copy(this, SLOT(copyFromContextMenu()), this);
0337         copy->setShortcutContext(Qt::WidgetShortcut);
0338         menuExt.addAction( ContextMenuExtension::FileGroup, copy );
0339     }
0340     if (needsPaste) {
0341         QAction* paste = KStandardAction::paste(this, SLOT(pasteFromContextMenu()), this);
0342         paste->setShortcutContext(Qt::WidgetShortcut);
0343         menuExt.addAction( ContextMenuExtension::FileGroup, paste );
0344     }
0345 
0346     // Populating rename/remove group
0347     {
0348         menuExt.addAction( ContextMenuExtension::FileGroup, createSeparatorAction() );
0349     }
0350     if ( needsCutRenameRemove ) {
0351         auto* remove = new QAction(i18nc("@action:inmenu", "Remo&ve"), parent);
0352         remove->setIcon(QIcon::fromTheme(QStringLiteral("user-trash")));
0353         connect( remove, &QAction::triggered, this, &ProjectManagerViewPlugin::removeFromContextMenu );
0354         menuExt.addAction( ContextMenuExtension::FileGroup, remove );
0355         auto* rename = new QAction(i18nc("@action:inmenu", "Re&name..."), parent);
0356         rename->setIcon(QIcon::fromTheme(QStringLiteral("edit-rename")));
0357         connect( rename, &QAction::triggered, this, &ProjectManagerViewPlugin::renameItemFromContextMenu );
0358         menuExt.addAction( ContextMenuExtension::FileGroup, rename );
0359     }
0360     if ( needsRemoveTargetFiles ) {
0361         auto* remove = new QAction(i18nc("@action:inmenu", "Remove from &Target"), parent);
0362         remove->setIcon(QIcon::fromTheme(QStringLiteral("user-trash")));
0363         connect( remove, &QAction::triggered, this, &ProjectManagerViewPlugin::removeTargetFilesFromContextMenu );
0364         menuExt.addAction( ContextMenuExtension::FileGroup, remove );
0365     }
0366 
0367     if ( needsCutRenameRemove || needsRemoveTargetFiles ) {
0368         menuExt.addAction(ContextMenuExtension::FileGroup, createSeparatorAction());
0369     }
0370 
0371     return menuExt;
0372 }
0373 
0374 void ProjectManagerViewPlugin::closeProjects()
0375 {
0376     QList<KDevelop::IProject*> projectsToClose;
0377     ProjectModel* model = ICore::self()->projectController()->projectModel();
0378     for (const QModelIndex& index : qAsConst(d->ctxProjectItemList)) {
0379         KDevelop::ProjectBaseItem* item = model->itemFromIndex(index);
0380         if( !projectsToClose.contains( item->project() ) )
0381         {
0382             projectsToClose << item->project();
0383         }
0384     }
0385     d->ctxProjectItemList.clear();
0386     for (KDevelop::IProject* proj : qAsConst(projectsToClose)) {
0387         core()->projectController()->closeProject( proj );
0388     }
0389 }
0390 
0391 
0392 void ProjectManagerViewPlugin::installItemsFromContextMenu()
0393 {
0394     runBuilderJob( BuilderJob::Install, itemsFromIndexes(d->ctxProjectItemList) );
0395     d->ctxProjectItemList.clear();
0396 }
0397 
0398 void ProjectManagerViewPlugin::cleanItemsFromContextMenu()
0399 {
0400     runBuilderJob( BuilderJob::Clean, itemsFromIndexes( d->ctxProjectItemList ) );
0401     d->ctxProjectItemList.clear();
0402 }
0403 
0404 void ProjectManagerViewPlugin::buildItemsFromContextMenu()
0405 {
0406     runBuilderJob( BuilderJob::Build, itemsFromIndexes( d->ctxProjectItemList ) );
0407     d->ctxProjectItemList.clear();
0408 }
0409 
0410 QList<ProjectBaseItem*> ProjectManagerViewPlugin::collectAllProjects()
0411 {
0412     QList<KDevelop::ProjectBaseItem*> items;
0413     const auto projects = core()->projectController()->projects();
0414     items.reserve(projects.size());
0415     for (auto* project : projects) {
0416         items << project->projectItem();
0417     }
0418     return items;
0419 }
0420 
0421 void ProjectManagerViewPlugin::buildAllProjects()
0422 {
0423     runBuilderJob( BuilderJob::Build, collectAllProjects() );
0424 }
0425 
0426 QList<ProjectBaseItem*> ProjectManagerViewPlugin::collectItems()
0427 {
0428     QList<ProjectBaseItem*> items;
0429     const QList<BuildItem> buildItems = ICore::self()->projectController()->buildSetModel()->items();
0430     if( !buildItems.isEmpty() )
0431     {
0432         for (const BuildItem& buildItem : buildItems) {
0433             if( ProjectBaseItem* item = buildItem.findItem() )
0434             {
0435                 items << item;
0436             }
0437         }
0438 
0439     } else
0440     {
0441         auto* ctx = static_cast<KDevelop::ProjectItemContext*>(ICore::self()->selectionController()->currentSelection());
0442         items = ctx->items();
0443     }
0444 
0445     return items;
0446 }
0447 
0448 void ProjectManagerViewPlugin::runBuilderJob( BuilderJob::BuildType type, const QList<ProjectBaseItem*>& items )
0449 {
0450     auto* builder = new BuilderJob;
0451     builder->addItems( type, items );
0452     builder->updateJobName();
0453     ICore::self()->uiController()->registerStatus(new JobStatus(builder));
0454     ICore::self()->runController()->registerJob( builder );
0455 }
0456 
0457 void ProjectManagerViewPlugin::installProjectItems()
0458 {
0459     runBuilderJob( KDevelop::BuilderJob::Install, collectItems() );
0460 }
0461 
0462 void ProjectManagerViewPlugin::pruneProjectItems()
0463 {
0464     runBuilderJob( KDevelop::BuilderJob::Prune, collectItems() );
0465 }
0466 
0467 void ProjectManagerViewPlugin::configureProjectItems()
0468 {
0469     runBuilderJob( KDevelop::BuilderJob::Configure, collectItems() );
0470 }
0471 
0472 void ProjectManagerViewPlugin::cleanProjectItems()
0473 {
0474     runBuilderJob( KDevelop::BuilderJob::Clean, collectItems() );
0475 }
0476 
0477 void ProjectManagerViewPlugin::buildProjectItems()
0478 {
0479     runBuilderJob( KDevelop::BuilderJob::Build, collectItems() );
0480 }
0481 
0482 void ProjectManagerViewPlugin::addItemsFromContextMenuToBuildset( )
0483 {
0484     const auto items = itemsFromIndexes(d->ctxProjectItemList);
0485     for (KDevelop::ProjectBaseItem* item : items) {
0486         ICore::self()->projectController()->buildSetModel()->addProjectItem( item );
0487     }
0488 }
0489 
0490 void ProjectManagerViewPlugin::runTargetsFromContextMenu( )
0491 {
0492     const auto items = itemsFromIndexes(d->ctxProjectItemList);
0493     for (KDevelop::ProjectBaseItem* item : items) {
0494         KDevelop::ProjectExecutableTargetItem* t=item->executable();
0495         if(t)
0496         {
0497             qCDebug(PLUGIN_PROJECTMANAGERVIEW) << "Running target: " << t->text() << t->builtUrl();
0498         }
0499     }
0500 }
0501 
0502 void ProjectManagerViewPlugin::projectConfiguration( )
0503 {
0504     if( !d->ctxProjectItemList.isEmpty() )
0505     {
0506         ProjectModel* model = ICore::self()->projectController()->projectModel();
0507         core()->projectController()->configureProject( model->itemFromIndex(d->ctxProjectItemList.at( 0 ))->project() );
0508     }
0509 }
0510 
0511 void ProjectManagerViewPlugin::reloadFromContextMenu( )
0512 {
0513     QList< KDevelop::ProjectFolderItem* > folders;
0514     const auto items = itemsFromIndexes(d->ctxProjectItemList);
0515     for (KDevelop::ProjectBaseItem* item : items) {
0516         if ( item->folder() ) {
0517             // since reloading should be recursive, only pass the upper-most items
0518             bool found = false;
0519             const auto currentFolders = folders;
0520             for (KDevelop::ProjectFolderItem* existing : currentFolders) {
0521                 if ( existing->path().isParentOf(item->folder()->path()) ) {
0522                     // simply skip this child
0523                     found = true;
0524                     break;
0525                 } else if ( item->folder()->path().isParentOf(existing->path()) ) {
0526                     // remove the child in the list and add the current item instead
0527                     folders.removeOne(existing);
0528                     // continue since there could be more than one existing child
0529                 }
0530             }
0531             if ( !found ) {
0532                 folders << item->folder();
0533             }
0534         }
0535     }
0536     for (KDevelop::ProjectFolderItem* folder : qAsConst(folders)) {
0537         folder->project()->projectFileManager()->reload(folder);
0538     }
0539 }
0540 
0541 void ProjectManagerViewPlugin::createFolderFromContextMenu( )
0542 {
0543     const auto items = itemsFromIndexes(d->ctxProjectItemList);
0544     for (KDevelop::ProjectBaseItem* item : items) {
0545         if ( item->folder() ) {
0546             QWidget* window(ICore::self()->uiController()->activeMainWindow()->window());
0547             QString name = QInputDialog::getText ( window,
0548                 i18nc("@title:window", "Create Folder in %1", item->folder()->path().pathOrUrl() ),
0549                 i18nc("@label:textbox", "Folder name:")
0550             );
0551             if (!name.isEmpty()) {
0552                 item->project()->projectFileManager()->addFolder( Path(item->path(), name), item->folder() );
0553             }
0554         }
0555     }
0556 }
0557 
0558 void ProjectManagerViewPlugin::removeFromContextMenu()
0559 {
0560     removeItems(itemsFromIndexes( d->ctxProjectItemList ));
0561 }
0562 
0563 void ProjectManagerViewPlugin::removeItems(const QList< ProjectBaseItem* >& items)
0564 {
0565     if (items.isEmpty()) {
0566         return;
0567     }
0568 
0569     //copy the list of selected items and sort it to guarantee parents will come before children
0570     QList<KDevelop::ProjectBaseItem*> sortedItems = items;
0571     std::sort(sortedItems.begin(), sortedItems.end(), ProjectBaseItem::pathLessThan);
0572 
0573     Path lastFolder;
0574     QHash< IProjectFileManager*, QList<KDevelop::ProjectBaseItem*> > filteredItems;
0575     QStringList itemPaths;
0576     for (KDevelop::ProjectBaseItem* item : qAsConst(sortedItems)) {
0577         if (item->isProjectRoot()) {
0578             continue;
0579         } else if (item->folder() || item->file()) {
0580             //make sure no children of folders that will be deleted are listed
0581             if (lastFolder.isParentOf(item->path())) {
0582                 continue;
0583             } else if (item->folder()) {
0584                 lastFolder = item->path();
0585             }
0586 
0587             IProjectFileManager* manager = item->project()->projectFileManager();
0588             if (manager) {
0589                 filteredItems[manager] << item;
0590                 itemPaths << item->path().pathOrUrl();
0591             }
0592         }
0593     }
0594 
0595     if (filteredItems.isEmpty()) {
0596         return;
0597     }
0598 
0599     if (KMessageBox::warningTwoActionsList(QApplication::activeWindow(),
0600                                            i18np("Do you really want to delete this item?",
0601                                                  "Do you really want to delete these %1 items?", itemPaths.size()),
0602                                            itemPaths, i18nc("@title:window", "Delete Files"), KStandardGuiItem::del(),
0603                                            KStandardGuiItem::cancel())
0604         == KMessageBox::SecondaryAction) {
0605         return;
0606     }
0607 
0608     //Go though projectmanagers, have them remove the files and folders that they own
0609     QHash< IProjectFileManager*, QList<KDevelop::ProjectBaseItem*> >::iterator it;
0610     for (it = filteredItems.begin(); it != filteredItems.end(); ++it)
0611     {
0612         Q_ASSERT(it.key());
0613         it.key()->removeFilesAndFolders(it.value());
0614     }
0615 }
0616 
0617 void ProjectManagerViewPlugin::removeTargetFilesFromContextMenu()
0618 {
0619     const QList<ProjectBaseItem*> items = itemsFromIndexes( d->ctxProjectItemList );
0620     QHash< IBuildSystemManager*, QList<KDevelop::ProjectFileItem*> > itemsByBuildSystem;
0621     for (ProjectBaseItem* item : items) {
0622         itemsByBuildSystem[item->project()->buildSystemManager()].append(item->file());
0623     }
0624 
0625     QHash< IBuildSystemManager*, QList<KDevelop::ProjectFileItem*> >::iterator it;
0626     for (it = itemsByBuildSystem.begin(); it != itemsByBuildSystem.end(); ++it)
0627         it.key()->removeFilesFromTargets(it.value());
0628 }
0629 
0630 void ProjectManagerViewPlugin::renameItemFromContextMenu()
0631 {
0632     renameItems(itemsFromIndexes( d->ctxProjectItemList ));
0633 }
0634 
0635 void ProjectManagerViewPlugin::renameItems(const QList< ProjectBaseItem* >& items)
0636 {
0637     if (items.isEmpty()) {
0638         return;
0639     }
0640 
0641     QWidget* window = ICore::self()->uiController()->activeMainWindow()->window();
0642 
0643     for (KDevelop::ProjectBaseItem* item : items) {
0644         if ((item->type()!=ProjectBaseItem::BuildFolder
0645                 && item->type()!=ProjectBaseItem::Folder
0646                 && item->type()!=ProjectBaseItem::File) || !item->parent())
0647         {
0648             continue;
0649         }
0650 
0651         const QString src = item->text();
0652 
0653         //Change QInputDialog->KFileSaveDialog?
0654         QString name = QInputDialog::getText(
0655             window, i18nc("@window:title", "Rename"),
0656             i18nc("@label:textbox", "New name for '%1':", item->text()),
0657             QLineEdit::Normal, item->text()
0658         );
0659 
0660         if (!name.isEmpty() && name != src) {
0661             ProjectBaseItem::RenameStatus status = item->rename( name );
0662 
0663             QString errorMessageText;
0664             switch(status) {
0665                 case ProjectBaseItem::RenameOk:
0666                     break;
0667                 case ProjectBaseItem::ExistingItemSameName:
0668                     errorMessageText = i18n("There is already a file named '%1'", name);
0669                     break;
0670                 case ProjectBaseItem::ProjectManagerRenameFailed:
0671                     errorMessageText = i18n("Could not rename '%1'", name);
0672                     break;
0673                 case ProjectBaseItem::InvalidNewName:
0674                     errorMessageText = i18n("'%1' is not a valid file name", name);
0675                     break;
0676             }
0677             if (!errorMessageText.isEmpty()) {
0678                 auto* message = new Sublime::Message(errorMessageText, Sublime::Message::Error);
0679                 ICore::self()->uiController()->postMessage(message);
0680             }
0681         }
0682     }
0683 }
0684 
0685 ProjectFileItem* createFile(const ProjectFolderItem* item)
0686 {
0687     QWidget* window = ICore::self()->uiController()->activeMainWindow()->window();
0688     QString name = QInputDialog::getText(window, i18nc("@title:window", "Create File in %1", item->path().pathOrUrl()), i18nc("@label:textbox", "File name:"));
0689 
0690     if(name.isEmpty())
0691         return nullptr;
0692 
0693     ProjectFileItem* ret = item->project()->projectFileManager()->addFile( Path(item->path(), name), item->folder() );
0694     if (ret) {
0695         ICore::self()->documentController()->openDocument( ret->path().toUrl() );
0696     }
0697     return ret;
0698 }
0699 
0700 void ProjectManagerViewPlugin::createFileFromContextMenu( )
0701 {
0702     const auto items = itemsFromIndexes(d->ctxProjectItemList);
0703     for (KDevelop::ProjectBaseItem* item : items) {
0704         if ( item->folder() ) {
0705             createFile(item->folder());
0706         } else if ( item->target() ) {
0707             auto* folder=dynamic_cast<ProjectFolderItem*>(item->parent());
0708             if(folder)
0709             {
0710                 ProjectFileItem* f=createFile(folder);
0711                 if(f)
0712                     item->project()->buildSystemManager()->addFilesToTarget(QList<ProjectFileItem*>() << f, item->target());
0713             }
0714         }
0715     }
0716 }
0717 
0718 void ProjectManagerViewPlugin::copyFromContextMenu()
0719 {
0720     qApp->clipboard()->setMimeData(createClipboardMimeData(false));
0721 }
0722 
0723 void ProjectManagerViewPlugin::cutFromContextMenu()
0724 {
0725     qApp->clipboard()->setMimeData(createClipboardMimeData(true));
0726 }
0727 
0728 static void selectItemsByPaths(ProjectManagerView* view, const Path::List& paths)
0729 {
0730     KDevelop::ProjectModel* projectModel = KDevelop::ICore::self()->projectController()->projectModel();
0731 
0732     QList<ProjectBaseItem*> newItems;
0733     for (const Path& path : paths) {
0734         QList<ProjectBaseItem*> items = projectModel->itemsForPath(IndexedString(path.path()));
0735         newItems.append(items);
0736         for (ProjectBaseItem* item : qAsConst(items)) {
0737             view->expandItem(item->parent());
0738         }
0739     }
0740     view->selectItems(newItems);
0741 }
0742 
0743 void ProjectManagerViewPlugin::pasteFromContextMenu()
0744 {
0745     auto* ctx = static_cast<KDevelop::ProjectItemContext*>(ICore::self()->selectionController()->currentSelection());
0746     if (ctx->items().count() != 1) {
0747         return; //do nothing if multiple or none items are selected
0748     }
0749 
0750     ProjectBaseItem* destItem = ctx->items().at(0);
0751     if (!destItem->folder()) {
0752         return; //do nothing if the target is not a directory
0753     }
0754 
0755     const QMimeData* data = qApp->clipboard()->mimeData();
0756     qCDebug(PLUGIN_PROJECTMANAGERVIEW) << data->urls();
0757     Path::List origPaths = toPathList(data->urls());
0758     const bool isCut = KIO::isClipboardDataCut(data);
0759 
0760     const CutCopyPasteHelpers::SourceToDestinationMap map = CutCopyPasteHelpers::mapSourceToDestination(origPaths, destItem->folder()->path());
0761 
0762     const QVector<CutCopyPasteHelpers::TaskInfo> tasks = CutCopyPasteHelpers::copyMoveItems(
0763         map.filteredPaths, destItem,
0764         isCut ? CutCopyPasteHelpers::Operation::CUT : CutCopyPasteHelpers::Operation::COPY);
0765 
0766     // Select new items in the project manager view
0767     auto* itemCtx = dynamic_cast<ProjectManagerViewItemContext*>(ICore::self()->selectionController()->currentSelection());
0768     if (itemCtx) {
0769         Path::List finalPathsList;
0770         for (const auto& task : tasks) {
0771             if (task.m_status == CutCopyPasteHelpers::TaskStatus::SUCCESS && task.m_type != CutCopyPasteHelpers::TaskType::DELETION) {
0772                 finalPathsList.reserve(finalPathsList.size() + task.m_src.size());
0773                 for (const Path& src : task.m_src) {
0774                     finalPathsList.append(map.finalPaths[src]);
0775                 }
0776             }
0777         }
0778 
0779         selectItemsByPaths(itemCtx->view(), finalPathsList);
0780     }
0781 
0782     // If there was a single failure, display a warning dialog.
0783     const bool anyFailed = std::any_of(tasks.begin(), tasks.end(),
0784                                        [](const CutCopyPasteHelpers::TaskInfo& task) {
0785                                            return task.m_status != CutCopyPasteHelpers::TaskStatus::SUCCESS;
0786                                        });
0787     if (anyFailed) {
0788         QWidget* window = ICore::self()->uiController()->activeMainWindow()->window();
0789         showWarningDialogForFailedPaste(window, tasks);
0790     }
0791 }
0792 
0793 #include "projectmanagerviewplugin.moc"
0794 #include "moc_projectmanagerviewplugin.cpp"