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"