1 /***************************************************************************
2 **
3 ** Copyright (C) 2016 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of Qt Creator.
7 **
8 ** Commercial License Usage
9 ** Licensees holding valid commercial Qt licenses may use this file in
10 ** accordance with the commercial license agreement provided with the
11 ** Software or, alternatively, in accordance with the terms contained in
12 ** a written agreement between you and The Qt Company. For licensing terms
13 ** and conditions see https://www.qt.io/terms-conditions. For further
14 ** information use the contact form at https://www.qt.io/contact-us.
15 **
16 ** GNU General Public License Usage
17 ** Alternatively, this file may be used under the terms of the GNU
18 ** General Public License version 3 as published by the Free Software
19 ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
20 ** included in the packaging of this file. Please review the following
21 ** information to ensure the GNU General Public License requirements will
22 ** be met: https://www.gnu.org/licenses/gpl-3.0.html.
23 **
24 ****************************************************************************/
25 
26 #include "projectexplorer.h"
27 
28 #include "appoutputpane.h"
29 #include "buildpropertiessettings.h"
30 #include "buildsteplist.h"
31 #include "buildsystem.h"
32 #include "compileoutputwindow.h"
33 #include "configtaskhandler.h"
34 #include "customexecutablerunconfiguration.h"
35 #include "customparserssettingspage.h"
36 #include "customwizard/customwizard.h"
37 #include "deployablefile.h"
38 #include "deployconfiguration.h"
39 #include "desktoprunconfiguration.h"
40 #include "environmentwidget.h"
41 #include "extraabi.h"
42 #include "gcctoolchainfactories.h"
43 #ifdef WITH_JOURNALD
44 #include "journaldwatcher.h"
45 #endif
46 #include "jsonwizard/jsonwizardfactory.h"
47 #include "jsonwizard/jsonwizardgeneratorfactory.h"
48 #include "jsonwizard/jsonwizardpagefactory_p.h"
49 #include "namedwidget.h"
50 #include "project.h"
51 #include "projectexplorersettings.h"
52 #include "projectexplorersettingspage.h"
53 #include "projectmanager.h"
54 #include "removetaskhandler.h"
55 #include "runconfigurationaspects.h"
56 #include "kitfeatureprovider.h"
57 #include "kitmanager.h"
58 #include "kitoptionspage.h"
59 #include "parseissuesdialog.h"
60 #include "target.h"
61 #include "toolchainmanager.h"
62 #include "toolchainoptionspage.h"
63 #include "copytaskhandler.h"
64 #include "showineditortaskhandler.h"
65 #include "vcsannotatetaskhandler.h"
66 #include "allprojectsfilter.h"
67 #include "allprojectsfind.h"
68 #include "buildmanager.h"
69 #include "buildsettingspropertiespage.h"
70 #include "currentprojectfind.h"
71 #include "currentprojectfilter.h"
72 #include "editorsettingspropertiespage.h"
73 #include "codestylesettingspropertiespage.h"
74 #include "dependenciespanel.h"
75 #include "foldernavigationwidget.h"
76 #include "appoutputpane.h"
77 #include "processstep.h"
78 #include "kitinformation.h"
79 #include "projectfilewizardextension.h"
80 #include "projectmanager.h"
81 #include "projecttreewidget.h"
82 #include "projectwindow.h"
83 #include "runsettingspropertiespage.h"
84 #include "session.h"
85 #include "projectnodes.h"
86 #include "sessiondialog.h"
87 #include "buildconfiguration.h"
88 #include "miniprojecttargetselector.h"
89 #include "taskhub.h"
90 #include "customtoolchain.h"
91 #include "selectablefilesmodel.h"
92 #include "customwizard/customwizard.h"
93 #include "devicesupport/desktopdevice.h"
94 #include "devicesupport/desktopdevicefactory.h"
95 #include "devicesupport/devicemanager.h"
96 #include "devicesupport/devicesettingspage.h"
97 #include "devicesupport/sshsettingspage.h"
98 #include "targetsettingspanel.h"
99 #include "projectpanelfactory.h"
100 #include "projectexplorericons.h"
101 #include "simpleprojectwizard.h"
102 
103 #include "windebuginterface.h"
104 #include "msvctoolchain.h"
105 
106 #include "projecttree.h"
107 #include "projectwelcomepage.h"
108 
109 #include <app/app_version.h>
110 #include <coreplugin/actionmanager/actioncontainer.h>
111 #include <coreplugin/actionmanager/actionmanager.h>
112 #include <coreplugin/actionmanager/command.h>
113 #include <coreplugin/coreconstants.h>
114 #include <coreplugin/diffservice.h>
115 #include <coreplugin/documentmanager.h>
116 #include <coreplugin/editormanager/documentmodel.h>
117 #include <coreplugin/editormanager/editormanager.h>
118 #include <coreplugin/fileutils.h>
119 #include <coreplugin/findplaceholder.h>
120 #include <coreplugin/icore.h>
121 #include <coreplugin/idocument.h>
122 #include <coreplugin/idocumentfactory.h>
123 #include <coreplugin/imode.h>
124 #include <coreplugin/iversioncontrol.h>
125 #include <coreplugin/locator/directoryfilter.h>
126 #include <coreplugin/minisplitter.h>
127 #include <coreplugin/modemanager.h>
128 #include <coreplugin/outputpane.h>
129 #include <coreplugin/vcsmanager.h>
130 #include <extensionsystem/pluginmanager.h>
131 #include <extensionsystem/pluginspec.h>
132 #include <ssh/sshconnection.h>
133 #include <ssh/sshsettings.h>
134 #include <texteditor/findinfiles.h>
135 #include <texteditor/textdocument.h>
136 #include <texteditor/texteditorconstants.h>
137 
138 #include <utils/algorithm.h>
139 #include <utils/fileutils.h>
140 #include <utils/macroexpander.h>
141 #include <utils/mimetypes/mimedatabase.h>
142 #include <utils/parameteraction.h>
143 #include <utils/processhandle.h>
144 #include <utils/proxyaction.h>
145 #include <utils/qtcassert.h>
146 #include <utils/removefiledialog.h>
147 #include <utils/stringutils.h>
148 #include <utils/utilsicons.h>
149 
150 #include <QAction>
151 #include <QActionGroup>
152 #include <QApplication>
153 #include <QDir>
154 #include <QFileDialog>
155 #include <QFileInfo>
156 #include <QInputDialog>
157 #include <QMenu>
158 #include <QMessageBox>
159 #include <QPair>
160 #include <QSettings>
161 #include <QThreadPool>
162 #include <QTimer>
163 
164 #include <functional>
165 #include <memory>
166 #include <vector>
167 
168 /*!
169     \namespace ProjectExplorer
170     The ProjectExplorer namespace contains the classes to explore projects.
171 */
172 
173 /*!
174     \namespace ProjectExplorer::Internal
175     The ProjectExplorer::Internal namespace is the internal namespace of the
176     ProjectExplorer plugin.
177     \internal
178 */
179 
180 /*!
181     \class ProjectExplorer::ProjectExplorerPlugin
182 
183     \brief The ProjectExplorerPlugin class contains static accessor and utility
184     functions to obtain the current project, open projects, and so on.
185 */
186 
187 using namespace Core;
188 using namespace ProjectExplorer::Internal;
189 using namespace Utils;
190 
191 namespace ProjectExplorer {
192 
193 namespace Constants {
194 const int  P_MODE_SESSION         = 85;
195 
196 // Actions
197 const char NEWPROJECT[]           = "ProjectExplorer.NewProject";
198 const char LOAD[]                 = "ProjectExplorer.Load";
199 const char UNLOAD[]               = "ProjectExplorer.Unload";
200 const char UNLOADCM[]             = "ProjectExplorer.UnloadCM";
201 const char CLEARSESSION[]         = "ProjectExplorer.ClearSession";
202 const char BUILDALLCONFIGS[]      = "ProjectExplorer.BuildProjectForAllConfigs";
203 const char BUILDPROJECTONLY[]     = "ProjectExplorer.BuildProjectOnly";
204 const char BUILDCM[]              = "ProjectExplorer.BuildCM";
205 const char BUILDDEPENDCM[]        = "ProjectExplorer.BuildDependenciesCM";
206 const char BUILDSESSION[]         = "ProjectExplorer.BuildSession";
207 const char BUILDSESSIONALLCONFIGS[] = "ProjectExplorer.BuildSessionForAllConfigs";
208 const char REBUILDPROJECTONLY[]   = "ProjectExplorer.RebuildProjectOnly";
209 const char REBUILD[]              = "ProjectExplorer.Rebuild";
210 const char REBUILDALLCONFIGS[]    = "ProjectExplorer.RebuildProjectForAllConfigs";
211 const char REBUILDCM[]            = "ProjectExplorer.RebuildCM";
212 const char REBUILDDEPENDCM[]      = "ProjectExplorer.RebuildDependenciesCM";
213 const char REBUILDSESSION[]       = "ProjectExplorer.RebuildSession";
214 const char REBUILDSESSIONALLCONFIGS[] = "ProjectExplorer.RebuildSessionForAllConfigs";
215 const char DEPLOYPROJECTONLY[]    = "ProjectExplorer.DeployProjectOnly";
216 const char DEPLOY[]               = "ProjectExplorer.Deploy";
217 const char DEPLOYCM[]             = "ProjectExplorer.DeployCM";
218 const char DEPLOYSESSION[]        = "ProjectExplorer.DeploySession";
219 const char CLEANPROJECTONLY[]     = "ProjectExplorer.CleanProjectOnly";
220 const char CLEAN[]                = "ProjectExplorer.Clean";
221 const char CLEANALLCONFIGS[]      = "ProjectExplorer.CleanProjectForAllConfigs";
222 const char CLEANCM[]              = "ProjectExplorer.CleanCM";
223 const char CLEANDEPENDCM[]        = "ProjectExplorer.CleanDependenciesCM";
224 const char CLEANSESSION[]         = "ProjectExplorer.CleanSession";
225 const char CLEANSESSIONALLCONFIGS[] = "ProjectExplorer.CleanSessionForAllConfigs";
226 const char CANCELBUILD[]          = "ProjectExplorer.CancelBuild";
227 const char RUN[]                  = "ProjectExplorer.Run";
228 const char RUNWITHOUTDEPLOY[]     = "ProjectExplorer.RunWithoutDeploy";
229 const char RUNCONTEXTMENU[]       = "ProjectExplorer.RunContextMenu";
230 const char ADDEXISTINGFILES[]     = "ProjectExplorer.AddExistingFiles";
231 const char ADDEXISTINGDIRECTORY[] = "ProjectExplorer.AddExistingDirectory";
232 const char ADDNEWSUBPROJECT[]     = "ProjectExplorer.AddNewSubproject";
233 const char REMOVEPROJECT[]        = "ProjectExplorer.RemoveProject";
234 const char OPENFILE[]             = "ProjectExplorer.OpenFile";
235 const char SEARCHONFILESYSTEM[]   = "ProjectExplorer.SearchOnFileSystem";
236 const char OPENTERMINALHERE[]     = "ProjectExplorer.OpenTerminalHere";
237 const char DUPLICATEFILE[]        = "ProjectExplorer.DuplicateFile";
238 const char DELETEFILE[]           = "ProjectExplorer.DeleteFile";
239 const char DIFFFILE[]             = "ProjectExplorer.DiffFile";
240 const char SETSTARTUP[]           = "ProjectExplorer.SetStartup";
241 const char PROJECTTREE_COLLAPSE_ALL[] = "ProjectExplorer.CollapseAll";
242 const char PROJECTTREE_EXPAND_ALL[] = "ProjectExplorer.ExpandAll";
243 
244 const char SELECTTARGET[]         = "ProjectExplorer.SelectTarget";
245 const char SELECTTARGETQUICK[]    = "ProjectExplorer.SelectTargetQuick";
246 
247 // Action priorities
248 const int  P_ACTION_RUN            = 100;
249 const int  P_ACTION_BUILDPROJECT   = 80;
250 
251 // Menus
252 const char M_RECENTPROJECTS[]     = "ProjectExplorer.Menu.Recent";
253 const char M_UNLOADPROJECTS[]     = "ProjectExplorer.Menu.Unload";
254 const char M_SESSION[]            = "ProjectExplorer.Menu.Session";
255 
256 const char RUNMENUCONTEXTMENU[]   = "Project.RunMenu";
257 const char FOLDER_OPEN_LOCATIONS_CONTEXT_MENU[]  = "Project.F.OpenLocation.CtxMenu";
258 const char PROJECT_OPEN_LOCATIONS_CONTEXT_MENU[]  = "Project.P.OpenLocation.CtxMenu";
259 
260 
261 const char RECENTPROJECTS_FILE_NAMES_KEY[] = "ProjectExplorer/RecentProjects/FileNames";
262 const char RECENTPROJECTS_DISPLAY_NAMES_KEY[] = "ProjectExplorer/RecentProjects/DisplayNames";
263 const char BUILD_BEFORE_DEPLOY_SETTINGS_KEY[] = "ProjectExplorer/Settings/BuildBeforeDeploy";
264 const char DEPLOY_BEFORE_RUN_SETTINGS_KEY[] = "ProjectExplorer/Settings/DeployBeforeRun";
265 const char SAVE_BEFORE_BUILD_SETTINGS_KEY[] = "ProjectExplorer/Settings/SaveBeforeBuild";
266 const char USE_JOM_SETTINGS_KEY[] = "ProjectExplorer/Settings/UseJom";
267 const char AUTO_RESTORE_SESSION_SETTINGS_KEY[] = "ProjectExplorer/Settings/AutoRestoreLastSession";
268 const char ADD_LIBRARY_PATHS_TO_RUN_ENV_SETTINGS_KEY[] =
269         "ProjectExplorer/Settings/AddLibraryPathsToRunEnv";
270 const char PROMPT_TO_STOP_RUN_CONTROL_SETTINGS_KEY[] =
271         "ProjectExplorer/Settings/PromptToStopRunControl";
272 const char AUTO_CREATE_RUN_CONFIGS_SETTINGS_KEY[] =
273         "ProjectExplorer/Settings/AutomaticallyCreateRunConfigurations";
274 const char ENVIRONMENT_ID_SETTINGS_KEY[] = "ProjectExplorer/Settings/EnvironmentId";
275 const char STOP_BEFORE_BUILD_SETTINGS_KEY[] = "ProjectExplorer/Settings/StopBeforeBuild";
276 const char TERMINAL_MODE_SETTINGS_KEY[] = "ProjectExplorer/Settings/TerminalMode";
277 const char CLOSE_FILES_WITH_PROJECT_SETTINGS_KEY[]
278     = "ProjectExplorer/Settings/CloseFilesWithProject";
279 const char CLEAR_ISSUES_ON_REBUILD_SETTINGS_KEY[] = "ProjectExplorer/Settings/ClearIssuesOnRebuild";
280 const char ABORT_BUILD_ALL_ON_ERROR_SETTINGS_KEY[]
281     = "ProjectExplorer/Settings/AbortBuildAllOnError";
282 const char LOW_BUILD_PRIORITY_SETTINGS_KEY[] = "ProjectExplorer/Settings/LowBuildPriority";
283 
284 const char CUSTOM_PARSER_COUNT_KEY[] = "ProjectExplorer/Settings/CustomParserCount";
285 const char CUSTOM_PARSER_PREFIX_KEY[] = "ProjectExplorer/Settings/CustomParser";
286 
287 } // namespace Constants
288 
289 
sysEnv(const Project *)290 static Utils::optional<Utils::Environment> sysEnv(const Project *)
291 {
292     return Utils::Environment::systemEnvironment();
293 }
294 
buildEnv(const Project * project)295 static Utils::optional<Utils::Environment> buildEnv(const Project *project)
296 {
297     if (!project || !project->activeTarget() || !project->activeTarget()->activeBuildConfiguration())
298         return {};
299     return project->activeTarget()->activeBuildConfiguration()->environment();
300 }
301 
runConfigForNode(const Target * target,const ProjectNode * node)302 static const RunConfiguration *runConfigForNode(const Target *target, const ProjectNode *node)
303 {
304     if (node && node->productType() == ProductType::App) {
305         const QString buildKey = node->buildKey();
306         for (const RunConfiguration * const rc : target->runConfigurations()) {
307             if (rc->buildKey() == buildKey)
308                 return rc;
309         }
310     }
311     return target->activeRunConfiguration();
312 }
313 
canOpenTerminalWithRunEnv(const Project * project,const ProjectNode * node)314 static bool canOpenTerminalWithRunEnv(const Project *project, const ProjectNode *node)
315 {
316     if (!project)
317         return false;
318     const Target * const target = project->activeTarget();
319     if (!target)
320         return false;
321     const RunConfiguration * const runConfig = runConfigForNode(target, node);
322     if (!runConfig)
323         return false;
324     IDevice::ConstPtr device = runConfig->runnable().device;
325     if (!device)
326         device = DeviceKitAspect::device(target->kit());
327     return device && device->canOpenTerminal();
328 }
329 
currentBuildConfiguration()330 static BuildConfiguration *currentBuildConfiguration()
331 {
332     const Project * const project = ProjectTree::currentProject();
333     const Target * const target = project ? project->activeTarget() : nullptr;
334     return target ? target->activeBuildConfiguration() : nullptr;
335 }
336 
activeTarget()337 static Target *activeTarget()
338 {
339     const Project * const project = SessionManager::startupProject();
340     return project ? project->activeTarget() : nullptr;
341 }
342 
activeBuildConfiguration()343 static BuildConfiguration *activeBuildConfiguration()
344 {
345     const Target * const target = activeTarget();
346     return target ? target->activeBuildConfiguration() : nullptr;
347 }
348 
activeRunConfiguration()349 static RunConfiguration *activeRunConfiguration()
350 {
351     const Target * const target = activeTarget();
352     return target ? target->activeRunConfiguration() : nullptr;
353 }
354 
isTextFile(const QString & fileName)355 static bool isTextFile(const QString &fileName)
356 {
357     return Utils::mimeTypeForFile(fileName).inherits(
358                 TextEditor::Constants::C_TEXTEDITOR_MIMETYPE_TEXT);
359 }
360 
361 class ProjectsMode : public IMode
362 {
363 public:
ProjectsMode()364     ProjectsMode()
365     {
366         setContext(Context(Constants::C_PROJECTEXPLORER));
367         setDisplayName(QCoreApplication::translate("ProjectExplorer::ProjectsMode", "Projects"));
368         setIcon(Utils::Icon::modeIcon(Icons::MODE_PROJECT_CLASSIC,
369                                       Icons::MODE_PROJECT_FLAT, Icons::MODE_PROJECT_FLAT_ACTIVE));
370         setPriority(Constants::P_MODE_SESSION);
371         setId(Constants::MODE_SESSION);
372         setContextHelp("Managing Projects");
373     }
374 };
375 
376 
377 class ProjectEnvironmentWidget : public NamedWidget
378 {
379     Q_DECLARE_TR_FUNCTIONS(ProjectEnvironmentWidget)
380 
381 public:
ProjectEnvironmentWidget(Project * project)382     explicit ProjectEnvironmentWidget(Project *project) : NamedWidget(tr("Project Environment"))
383     {
384         const auto vbox = new QVBoxLayout(this);
385         vbox->setContentsMargins(0, 0, 0, 0);
386         const auto envWidget = new EnvironmentWidget(this, EnvironmentWidget::TypeLocal);
387         envWidget->setOpenTerminalFunc({});
388         envWidget->expand();
389         vbox->addWidget(envWidget);
390         connect(envWidget, &EnvironmentWidget::userChangesChanged,
391                 this, [project, envWidget] {
392             project->setAdditionalEnvironment(envWidget->userChanges());
393         });
394         envWidget->setUserChanges(project->additionalEnvironment());
395     }
396 };
397 
398 
399 class ProjectExplorerPluginPrivate : public QObject
400 {
401     Q_DECLARE_TR_FUNCTIONS(ProjectExplorer::ProjectExplorerPlugin)
402 
403 public:
404     ProjectExplorerPluginPrivate();
405 
406     void updateContextMenuActions(Node *currentNode);
407     void updateLocationSubMenus();
408     void executeRunConfiguration(RunConfiguration *, Utils::Id mode);
409     QPair<bool, QString> buildSettingsEnabledForSession();
410     QPair<bool, QString> buildSettingsEnabled(const Project *pro);
411 
412     void addToRecentProjects(const QString &fileName, const QString &displayName);
413     void startRunControl(RunControl *runControl);
414     void showOutputPaneForRunControl(RunControl *runControl);
415 
416     void updateActions();
417     void updateContext();
418     void updateDeployActions();
419     void updateRunWithoutDeployMenu();
420 
421     void buildQueueFinished(bool success);
422 
423     void loadAction();
424     void handleUnloadProject();
425     void unloadProjectContextMenu();
426     void closeAllProjects();
427     void showSessionManager();
428     void updateSessionMenu();
429     void setSession(QAction *action);
430 
431     void determineSessionToRestoreAtStartup();
432     void restoreSession();
433     void runProjectContextMenu();
434     void savePersistentSettings();
435 
436     void addNewFile();
437     void handleAddExistingFiles();
438     void addExistingDirectory();
439     void addNewSubproject();
440     void addExistingProjects();
441     void removeProject();
442     void openFile();
443     void searchOnFileSystem();
444     void showInGraphicalShell();
445     void removeFile();
446     void duplicateFile();
447     void deleteFile();
448     void handleRenameFile();
449     void handleSetStartupProject();
450     void setStartupProject(ProjectExplorer::Project *project);
451     bool closeAllFilesInProject(const Project *project);
452 
453     void updateRecentProjectMenu();
454     void clearRecentProjects();
455     void openRecentProject(const QString &fileName);
456     void removeFromRecentProjects(const QString &fileName, const QString &displayName);
457     void updateUnloadProjectMenu();
458     using EnvironmentGetter = std::function<Utils::optional<Utils::Environment>(const Project *project)>;
459     void openTerminalHere(const EnvironmentGetter &env);
460     void openTerminalHereWithRunEnv();
461 
462     void invalidateProject(ProjectExplorer::Project *project);
463 
464     void projectAdded(ProjectExplorer::Project *pro);
465     void projectRemoved(ProjectExplorer::Project *pro);
466     void projectDisplayNameChanged(ProjectExplorer::Project *pro);
467 
468     void doUpdateRunActions();
469 
470     void currentModeChanged(Utils::Id mode, Utils::Id oldMode);
471 
472     void updateWelcomePage();
473 
474     void checkForShutdown();
475     void timerEvent(QTimerEvent *) override;
476 
477     QList<QPair<QString, QString> > recentProjects() const;
478 
479 public:
480     QMenu *m_sessionMenu;
481     QMenu *m_openWithMenu;
482     QMenu *m_openTerminalMenu;
483 
484     QMultiMap<int, QObject*> m_actionMap;
485     QAction *m_sessionManagerAction;
486     QAction *m_newAction;
487     QAction *m_loadAction;
488     Utils::ParameterAction *m_unloadAction;
489     Utils::ParameterAction *m_unloadActionContextMenu;
490     QAction *m_closeAllProjects;
491     QAction *m_buildProjectOnlyAction;
492     Utils::ParameterAction *m_buildProjectForAllConfigsAction;
493     Utils::ParameterAction *m_buildAction;
494     Utils::ParameterAction *m_buildForRunConfigAction;
495     Utils::ProxyAction *m_modeBarBuildAction;
496     QAction *m_buildActionContextMenu;
497     QAction *m_buildDependenciesActionContextMenu;
498     QAction *m_buildSessionAction;
499     QAction *m_buildSessionForAllConfigsAction;
500     QAction *m_rebuildProjectOnlyAction;
501     QAction *m_rebuildAction;
502     QAction *m_rebuildProjectForAllConfigsAction;
503     QAction *m_rebuildActionContextMenu;
504     QAction *m_rebuildDependenciesActionContextMenu;
505     QAction *m_rebuildSessionAction;
506     QAction *m_rebuildSessionForAllConfigsAction;
507     QAction *m_cleanProjectOnlyAction;
508     QAction *m_deployProjectOnlyAction;
509     QAction *m_deployAction;
510     QAction *m_deployActionContextMenu;
511     QAction *m_deploySessionAction;
512     QAction *m_cleanAction;
513     QAction *m_cleanProjectForAllConfigsAction;
514     QAction *m_cleanActionContextMenu;
515     QAction *m_cleanDependenciesActionContextMenu;
516     QAction *m_cleanSessionAction;
517     QAction *m_cleanSessionForAllConfigsAction;
518     QAction *m_runAction;
519     QAction *m_runActionContextMenu;
520     QAction *m_runWithoutDeployAction;
521     QAction *m_cancelBuildAction;
522     QAction *m_addNewFileAction;
523     QAction *m_addExistingFilesAction;
524     QAction *m_addExistingDirectoryAction;
525     QAction *m_addNewSubprojectAction;
526     QAction *m_addExistingProjectsAction;
527     QAction *m_removeFileAction;
528     QAction *m_duplicateFileAction;
529     QAction *m_removeProjectAction;
530     QAction *m_deleteFileAction;
531     QAction *m_renameFileAction;
532     QAction *m_filePropertiesAction = nullptr;
533     QAction *m_diffFileAction;
534     QAction *m_openFileAction;
535     QAction *m_projectTreeCollapseAllAction;
536     QAction *m_projectTreeExpandAllAction;
537     QAction *m_projectTreeExpandNodeAction = nullptr;
538     Utils::ParameterAction *m_closeProjectFilesActionFileMenu;
539     Utils::ParameterAction *m_closeProjectFilesActionContextMenu;
540     QAction *m_searchOnFileSystem;
541     QAction *m_showInGraphicalShell;
542     QAction *m_openTerminalHere;
543     QAction *m_openTerminalHereBuildEnv;
544     QAction *m_openTerminalHereRunEnv;
545     Utils::ParameterAction *m_setStartupProjectAction;
546     QAction *m_projectSelectorAction;
547     QAction *m_projectSelectorActionMenu;
548     QAction *m_projectSelectorActionQuick;
549     QAction *m_runSubProject;
550 
551     ProjectWindow *m_proWindow = nullptr;
552     QString m_sessionToRestoreAtStartup;
553 
554     QStringList m_profileMimeTypes;
555     int m_activeRunControlCount = 0;
556     int m_shutdownWatchDogId = -1;
557 
558     QHash<QString, std::function<Project *(const Utils::FilePath &)>> m_projectCreators;
559     QList<QPair<QString, QString> > m_recentProjects; // pair of filename, displayname
560     static const int m_maxRecentProjects = 25;
561 
562     QString m_lastOpenDirectory;
563     QPointer<RunConfiguration> m_delayedRunConfiguration;
564     QString m_projectFilterString;
565     MiniProjectTargetSelector * m_targetSelector;
566     ProjectExplorerSettings m_projectExplorerSettings;
567     BuildPropertiesSettings m_buildPropertiesSettings;
568     QList<CustomParserSettings> m_customParsers;
569     bool m_shouldHaveRunConfiguration = false;
570     bool m_shuttingDown = false;
571     Utils::Id m_runMode = Constants::NO_RUN_MODE;
572 
573     ToolChainManager *m_toolChainManager = nullptr;
574     QStringList m_arguments;
575 
576 #ifdef WITH_JOURNALD
577     JournaldWatcher m_journalWatcher;
578 #endif
579     QThreadPool m_threadPool;
580 
581     DeviceManager m_deviceManager{true};
582 
583 #ifdef Q_OS_WIN
584     WinDebugInterface m_winDebugInterface;
585     MsvcToolChainFactory m_mscvToolChainFactory;
586     ClangClToolChainFactory m_clangClToolChainFactory;
587 #else
588     LinuxIccToolChainFactory m_linuxToolChainFactory;
589 #endif
590 
591 #ifndef Q_OS_MACOS
592     MingwToolChainFactory m_mingwToolChainFactory; // Mingw offers cross-compiling to windows
593 #endif
594 
595     GccToolChainFactory m_gccToolChainFactory;
596     ClangToolChainFactory m_clangToolChainFactory;
597     CustomToolChainFactory m_customToolChainFactory;
598 
599     DesktopDeviceFactory m_desktopDeviceFactory;
600 
601     ToolChainOptionsPage m_toolChainOptionsPage;
602     KitOptionsPage m_kitOptionsPage;
603 
604     TaskHub m_taskHub;
605 
606     ProjectWelcomePage m_welcomePage;
607 
608     CustomWizardMetaFactory<CustomProjectWizard> m_customProjectWizard{IWizardFactory::ProjectWizard};
609     CustomWizardMetaFactory<CustomWizard> m_fileWizard{IWizardFactory::FileWizard};
610 
611     ProjectsMode m_projectsMode;
612 
613     CopyTaskHandler m_copyTaskHandler;
614     ShowInEditorTaskHandler m_showInEditorTaskHandler;
615     VcsAnnotateTaskHandler m_vcsAnnotateTaskHandler;
616     RemoveTaskHandler m_removeTaskHandler;
617     ConfigTaskHandler m_configTaskHandler{Task::compilerMissingTask(), Constants::KITS_SETTINGS_PAGE_ID};
618 
619     SessionManager m_sessionManager;
620     AppOutputPane m_outputPane;
621 
622     ProjectTree m_projectTree;
623 
624     AllProjectsFilter m_allProjectsFilter;
625     CurrentProjectFilter m_currentProjectFilter;
626     DirectoryFilter m_allProjectDirectoriesFilter;
627 
628     ProcessStepFactory m_processStepFactory;
629 
630     AllProjectsFind m_allProjectsFind;
631     CurrentProjectFind m_curretProjectFind;
632 
633     CustomExecutableRunConfigurationFactory m_customExecutableRunConfigFactory;
634     CustomExecutableRunWorkerFactory m_customExecutableRunWorkerFactory;
635 
636     ProjectFileWizardExtension m_projectFileWizardExtension;
637 
638     // Settings pages
639     ProjectExplorerSettingsPage m_projectExplorerSettingsPage;
640     BuildPropertiesSettingsPage m_buildPropertiesSettingsPage{&m_buildPropertiesSettings};
641     AppOutputSettingsPage m_appOutputSettingsPage;
642     CompileOutputSettingsPage m_compileOutputSettingsPage;
643     DeviceSettingsPage m_deviceSettingsPage;
644     SshSettingsPage m_sshSettingsPage;
645     CustomParsersSettingsPage m_customParsersSettingsPage;
646 
647     ProjectTreeWidgetFactory m_projectTreeFactory;
648     FolderNavigationWidgetFactory m_folderNavigationWidgetFactory;
649     DefaultDeployConfigurationFactory m_defaultDeployConfigFactory;
650 
651     IDocumentFactory m_documentFactory;
652 
653     DeviceTypeKitAspect deviceTypeKitAspect;
654     DeviceKitAspect deviceKitAspect;
655     BuildDeviceKitAspect buildDeviceKitAspect;
656     ToolChainKitAspect toolChainKitAspect;
657     SysRootKitAspect sysRootKitAspect;
658     EnvironmentKitAspect environmentKitAspect;
659 
660     DesktopQmakeRunConfigurationFactory qmakeRunConfigFactory;
661     QbsRunConfigurationFactory qbsRunConfigFactory;
662     CMakeRunConfigurationFactory cmakeRunConfigFactory;
663 
664     RunWorkerFactory desktopRunWorkerFactory{
665         RunWorkerFactory::make<SimpleTargetRunner>(),
666         {ProjectExplorer::Constants::NORMAL_RUN_MODE},
667         {qmakeRunConfigFactory.runConfigurationId(),
668          qbsRunConfigFactory.runConfigurationId(),
669          cmakeRunConfigFactory.runConfigurationId()}
670     };
671 
672 };
673 
674 static ProjectExplorerPlugin *m_instance = nullptr;
675 static ProjectExplorerPluginPrivate *dd = nullptr;
676 
ProjectExplorerPlugin()677 ProjectExplorerPlugin::ProjectExplorerPlugin()
678 {
679     m_instance = this;
680 }
681 
~ProjectExplorerPlugin()682 ProjectExplorerPlugin::~ProjectExplorerPlugin()
683 {
684     delete dd->m_proWindow; // Needs access to the kit manager.
685     JsonWizardFactory::destroyAllFactories();
686 
687     // Force sequence of deletion:
688     KitManager::destroy(); // remove all the profile information
689     delete dd->m_toolChainManager;
690     ProjectPanelFactory::destroyFactories();
691     delete dd;
692     dd = nullptr;
693     m_instance = nullptr;
694 
695 #ifdef WITH_TESTS
696     deleteTestToolchains();
697 #endif
698 }
699 
instance()700 ProjectExplorerPlugin *ProjectExplorerPlugin::instance()
701 {
702     return m_instance;
703 }
704 
initialize(const QStringList & arguments,QString * error)705 bool ProjectExplorerPlugin::initialize(const QStringList &arguments, QString *error)
706 {
707     Q_UNUSED(error)
708 
709     dd = new ProjectExplorerPluginPrivate;
710 
711     qRegisterMetaType<ProjectExplorer::BuildSystem *>();
712     qRegisterMetaType<ProjectExplorer::RunControl *>();
713     qRegisterMetaType<ProjectExplorer::DeployableFile>("ProjectExplorer::DeployableFile");
714 
715     handleCommandLineArguments(arguments);
716 
717     dd->m_toolChainManager = new ToolChainManager;
718 
719     // Register languages
720     ToolChainManager::registerLanguage(Constants::C_LANGUAGE_ID, tr("C"));
721     ToolChainManager::registerLanguage(Constants::CXX_LANGUAGE_ID, tr("C++"));
722 
723     IWizardFactory::registerFeatureProvider(new KitFeatureProvider);
724 
725     IWizardFactory::registerFactoryCreator([]() -> QList<IWizardFactory *> {
726         QList<IWizardFactory *> result;
727         result << CustomWizard::createWizards();
728         result << JsonWizardFactory::createWizardFactories();
729         result << new SimpleProjectWizard;
730         return result;
731     });
732 
733     connect(&dd->m_welcomePage, &ProjectWelcomePage::manageSessions,
734             dd, &ProjectExplorerPluginPrivate::showSessionManager);
735 
736     SessionManager *sessionManager = &dd->m_sessionManager;
737     connect(sessionManager, &SessionManager::projectAdded,
738             this, &ProjectExplorerPlugin::fileListChanged);
739     connect(sessionManager, &SessionManager::aboutToRemoveProject,
740             dd, &ProjectExplorerPluginPrivate::invalidateProject);
741     connect(sessionManager, &SessionManager::projectRemoved,
742             this, &ProjectExplorerPlugin::fileListChanged);
743     connect(sessionManager, &SessionManager::projectAdded,
744             dd, &ProjectExplorerPluginPrivate::projectAdded);
745     connect(sessionManager, &SessionManager::projectRemoved,
746             dd, &ProjectExplorerPluginPrivate::projectRemoved);
747     connect(sessionManager, &SessionManager::projectDisplayNameChanged,
748             dd, &ProjectExplorerPluginPrivate::projectDisplayNameChanged);
749     connect(sessionManager, &SessionManager::dependencyChanged,
750             dd, &ProjectExplorerPluginPrivate::updateActions);
751     connect(sessionManager, &SessionManager::sessionLoaded,
752             dd, &ProjectExplorerPluginPrivate::updateActions);
753     connect(sessionManager,
754             &SessionManager::sessionLoaded,
755             dd,
756             &ProjectExplorerPluginPrivate::updateWelcomePage);
757 
758     connect(sessionManager, &SessionManager::projectAdded, dd, [](ProjectExplorer::Project *project) {
759         dd->m_allProjectDirectoriesFilter.addDirectory(project->projectDirectory().toString());
760     });
761     connect(sessionManager,
762             &SessionManager::projectRemoved,
763             dd,
764             [](ProjectExplorer::Project *project) {
765                 dd->m_allProjectDirectoriesFilter.removeDirectory(
766                     project->projectDirectory().toString());
767             });
768 
769     ProjectTree *tree = &dd->m_projectTree;
770     connect(tree, &ProjectTree::currentProjectChanged, dd, [] {
771         dd->updateContextMenuActions(ProjectTree::currentNode());
772     });
773     connect(tree, &ProjectTree::nodeActionsChanged, dd, [] {
774         dd->updateContextMenuActions(ProjectTree::currentNode());
775     });
776     connect(tree, &ProjectTree::currentNodeChanged,
777             dd, &ProjectExplorerPluginPrivate::updateContextMenuActions);
778     connect(tree, &ProjectTree::currentProjectChanged,
779             dd, &ProjectExplorerPluginPrivate::updateActions);
780     connect(tree, &ProjectTree::currentProjectChanged, this, [](Project *project) {
781         TextEditor::FindInFiles::instance()->setBaseDirectory(project ? project->projectDirectory()
782                                                                       : Utils::FilePath());
783     });
784 
785     // For JsonWizard:
786     JsonWizardFactory::registerPageFactory(new FieldPageFactory);
787     JsonWizardFactory::registerPageFactory(new FilePageFactory);
788     JsonWizardFactory::registerPageFactory(new KitsPageFactory);
789     JsonWizardFactory::registerPageFactory(new ProjectPageFactory);
790     JsonWizardFactory::registerPageFactory(new SummaryPageFactory);
791 
792     JsonWizardFactory::registerGeneratorFactory(new FileGeneratorFactory);
793     JsonWizardFactory::registerGeneratorFactory(new ScannerGeneratorFactory);
794 
795     dd->m_proWindow = new ProjectWindow;
796 
797     Context projectTreeContext(Constants::C_PROJECT_TREE);
798 
799     auto splitter = new MiniSplitter(Qt::Vertical);
800     splitter->addWidget(dd->m_proWindow);
801     splitter->addWidget(new OutputPanePlaceHolder(Constants::MODE_SESSION, splitter));
802     dd->m_projectsMode.setWidget(splitter);
803     dd->m_projectsMode.setEnabled(false);
804 
805     ICore::addPreCloseListener([]() -> bool { return coreAboutToClose(); });
806 
807     connect(SessionManager::instance(), &SessionManager::projectRemoved,
808             &dd->m_outputPane, &AppOutputPane::projectRemoved);
809 
810     // ProjectPanelFactories
811     auto panelFactory = new ProjectPanelFactory;
812     panelFactory->setPriority(30);
813     panelFactory->setDisplayName(QCoreApplication::translate("EditorSettingsPanelFactory", "Editor"));
814     panelFactory->setIcon(":/projectexplorer/images/EditorSettings.png");
815     panelFactory->setCreateWidgetFunction([](Project *project) { return new EditorSettingsWidget(project); });
816     ProjectPanelFactory::registerFactory(panelFactory);
817 
818     panelFactory = new ProjectPanelFactory;
819     panelFactory->setPriority(40);
820     panelFactory->setDisplayName(QCoreApplication::translate("CodeStyleSettingsPanelFactory", "Code Style"));
821     panelFactory->setIcon(":/projectexplorer/images/CodeStyleSettings.png");
822     panelFactory->setCreateWidgetFunction([](Project *project) { return new CodeStyleSettingsWidget(project); });
823     ProjectPanelFactory::registerFactory(panelFactory);
824 
825     panelFactory = new ProjectPanelFactory;
826     panelFactory->setPriority(50);
827     panelFactory->setDisplayName(QCoreApplication::translate("DependenciesPanelFactory", "Dependencies"));
828     panelFactory->setIcon(":/projectexplorer/images/ProjectDependencies.png");
829     panelFactory->setCreateWidgetFunction([](Project *project) { return new DependenciesWidget(project); });
830     ProjectPanelFactory::registerFactory(panelFactory);
831 
832     panelFactory = new ProjectPanelFactory;
833     panelFactory->setPriority(60);
834     panelFactory->setDisplayName(QCoreApplication::translate("EnvironmentPanelFactory", "Environment"));
835     panelFactory->setCreateWidgetFunction([](Project *project) {
836         return new ProjectEnvironmentWidget(project);
837     });
838     ProjectPanelFactory::registerFactory(panelFactory);
839 
840     RunConfiguration::registerAspect<CustomParsersAspect>();
841 
842     // context menus
843     ActionContainer *msessionContextMenu =
844         ActionManager::createMenu(Constants::M_SESSIONCONTEXT);
845     ActionContainer *mprojectContextMenu =
846         ActionManager::createMenu(Constants::M_PROJECTCONTEXT);
847     ActionContainer *msubProjectContextMenu =
848         ActionManager::createMenu(Constants::M_SUBPROJECTCONTEXT);
849     ActionContainer *mfolderContextMenu =
850         ActionManager::createMenu(Constants::M_FOLDERCONTEXT);
851     ActionContainer *mfileContextMenu =
852         ActionManager::createMenu(Constants::M_FILECONTEXT);
853 
854     ActionContainer *mfile =
855         ActionManager::actionContainer(Core::Constants::M_FILE);
856     ActionContainer *menubar =
857         ActionManager::actionContainer(Core::Constants::MENU_BAR);
858 
859     // context menu sub menus:
860     ActionContainer *folderOpenLocationCtxMenu =
861             ActionManager::createMenu(Constants::FOLDER_OPEN_LOCATIONS_CONTEXT_MENU);
862     folderOpenLocationCtxMenu->menu()->setTitle(tr("Open..."));
863     folderOpenLocationCtxMenu->setOnAllDisabledBehavior(ActionContainer::Hide);
864 
865     ActionContainer *projectOpenLocationCtxMenu =
866             ActionManager::createMenu(Constants::PROJECT_OPEN_LOCATIONS_CONTEXT_MENU);
867     projectOpenLocationCtxMenu->menu()->setTitle(tr("Open..."));
868     projectOpenLocationCtxMenu->setOnAllDisabledBehavior(ActionContainer::Hide);
869 
870     // build menu
871     ActionContainer *mbuild =
872         ActionManager::createMenu(Constants::M_BUILDPROJECT);
873     mbuild->menu()->setTitle(tr("&Build"));
874     menubar->addMenu(mbuild, Core::Constants::G_VIEW);
875 
876     // debug menu
877     ActionContainer *mdebug =
878         ActionManager::createMenu(Constants::M_DEBUG);
879     mdebug->menu()->setTitle(tr("&Debug"));
880     menubar->addMenu(mdebug, Core::Constants::G_VIEW);
881 
882     ActionContainer *mstartdebugging =
883         ActionManager::createMenu(Constants::M_DEBUG_STARTDEBUGGING);
884     mstartdebugging->menu()->setTitle(tr("&Start Debugging"));
885     mdebug->addMenu(mstartdebugging, Core::Constants::G_DEFAULT_ONE);
886 
887     //
888     // Groups
889     //
890 
891     mbuild->appendGroup(Constants::G_BUILD_ALLPROJECTS);
892     mbuild->appendGroup(Constants::G_BUILD_PROJECT);
893     mbuild->appendGroup(Constants::G_BUILD_PRODUCT);
894     mbuild->appendGroup(Constants::G_BUILD_SUBPROJECT);
895     mbuild->appendGroup(Constants::G_BUILD_FILE);
896     mbuild->appendGroup(Constants::G_BUILD_ALLPROJECTS_ALLCONFIGURATIONS);
897     mbuild->appendGroup(Constants::G_BUILD_PROJECT_ALLCONFIGURATIONS);
898     mbuild->appendGroup(Constants::G_BUILD_CANCEL);
899     mbuild->appendGroup(Constants::G_BUILD_BUILD);
900     mbuild->appendGroup(Constants::G_BUILD_RUN);
901 
902     msessionContextMenu->appendGroup(Constants::G_SESSION_BUILD);
903     msessionContextMenu->appendGroup(Constants::G_SESSION_REBUILD);
904     msessionContextMenu->appendGroup(Constants::G_SESSION_FILES);
905     msessionContextMenu->appendGroup(Constants::G_SESSION_OTHER);
906     msessionContextMenu->appendGroup(Constants::G_PROJECT_TREE);
907 
908     mprojectContextMenu->appendGroup(Constants::G_PROJECT_FIRST);
909     mprojectContextMenu->appendGroup(Constants::G_PROJECT_BUILD);
910     mprojectContextMenu->appendGroup(Constants::G_PROJECT_RUN);
911     mprojectContextMenu->appendGroup(Constants::G_PROJECT_REBUILD);
912     mprojectContextMenu->appendGroup(Constants::G_FOLDER_LOCATIONS);
913     mprojectContextMenu->appendGroup(Constants::G_PROJECT_FILES);
914     mprojectContextMenu->appendGroup(Constants::G_PROJECT_LAST);
915     mprojectContextMenu->appendGroup(Constants::G_PROJECT_TREE);
916 
917     mprojectContextMenu->addMenu(projectOpenLocationCtxMenu, Constants::G_FOLDER_LOCATIONS);
918     connect(mprojectContextMenu->menu(), &QMenu::aboutToShow,
919             dd, &ProjectExplorerPluginPrivate::updateLocationSubMenus);
920 
921     msubProjectContextMenu->appendGroup(Constants::G_PROJECT_FIRST);
922     msubProjectContextMenu->appendGroup(Constants::G_PROJECT_BUILD);
923     msubProjectContextMenu->appendGroup(Constants::G_PROJECT_RUN);
924     msubProjectContextMenu->appendGroup(Constants::G_FOLDER_LOCATIONS);
925     msubProjectContextMenu->appendGroup(Constants::G_PROJECT_FILES);
926     msubProjectContextMenu->appendGroup(Constants::G_PROJECT_LAST);
927     msubProjectContextMenu->appendGroup(Constants::G_PROJECT_TREE);
928 
929     msubProjectContextMenu->addMenu(projectOpenLocationCtxMenu, Constants::G_FOLDER_LOCATIONS);
930     connect(msubProjectContextMenu->menu(), &QMenu::aboutToShow,
931             dd, &ProjectExplorerPluginPrivate::updateLocationSubMenus);
932 
933     ActionContainer *runMenu = ActionManager::createMenu(Constants::RUNMENUCONTEXTMENU);
934     runMenu->setOnAllDisabledBehavior(ActionContainer::Hide);
935     const QIcon runSideBarIcon = Utils::Icon::sideBarIcon(Icons::RUN, Icons::RUN_FLAT);
936     const QIcon runIcon = Utils::Icon::combinedIcon({Utils::Icons::RUN_SMALL.icon(),
937                                                      runSideBarIcon});
938 
939     runMenu->menu()->setIcon(runIcon);
940     runMenu->menu()->setTitle(tr("Run"));
941     msubProjectContextMenu->addMenu(runMenu, ProjectExplorer::Constants::G_PROJECT_RUN);
942 
943     mfolderContextMenu->appendGroup(Constants::G_FOLDER_LOCATIONS);
944     mfolderContextMenu->appendGroup(Constants::G_FOLDER_FILES);
945     mfolderContextMenu->appendGroup(Constants::G_FOLDER_OTHER);
946     mfolderContextMenu->appendGroup(Constants::G_FOLDER_CONFIG);
947     mfolderContextMenu->appendGroup(Constants::G_PROJECT_TREE);
948 
949     mfileContextMenu->appendGroup(Constants::G_FILE_OPEN);
950     mfileContextMenu->appendGroup(Constants::G_FILE_OTHER);
951     mfileContextMenu->appendGroup(Constants::G_FILE_CONFIG);
952     mfileContextMenu->appendGroup(Constants::G_PROJECT_TREE);
953 
954     // Open Terminal submenu
955     ActionContainer * const openTerminal =
956             ActionManager::createMenu(ProjectExplorer::Constants::M_OPENTERMINALCONTEXT);
957     openTerminal->setOnAllDisabledBehavior(ActionContainer::Show);
958     dd->m_openTerminalMenu = openTerminal->menu();
959     dd->m_openTerminalMenu->setTitle(Core::FileUtils::msgTerminalWithAction());
960 
961     // "open with" submenu
962     ActionContainer * const openWith =
963             ActionManager::createMenu(ProjectExplorer::Constants::M_OPENFILEWITHCONTEXT);
964     openWith->setOnAllDisabledBehavior(ActionContainer::Show);
965     dd->m_openWithMenu = openWith->menu();
966     dd->m_openWithMenu->setTitle(tr("Open With"));
967 
968     mfolderContextMenu->addMenu(folderOpenLocationCtxMenu, Constants::G_FOLDER_LOCATIONS);
969     connect(mfolderContextMenu->menu(), &QMenu::aboutToShow,
970             dd, &ProjectExplorerPluginPrivate::updateLocationSubMenus);
971 
972     //
973     // Separators
974     //
975 
976     Command *cmd;
977 
978     msessionContextMenu->addSeparator(projectTreeContext, Constants::G_SESSION_REBUILD);
979 
980     msessionContextMenu->addSeparator(projectTreeContext, Constants::G_SESSION_FILES);
981     mprojectContextMenu->addSeparator(projectTreeContext, Constants::G_PROJECT_FILES);
982     msubProjectContextMenu->addSeparator(projectTreeContext, Constants::G_PROJECT_FILES);
983     mfile->addSeparator(Core::Constants::G_FILE_PROJECT);
984     mbuild->addSeparator(Constants::G_BUILD_ALLPROJECTS);
985     mbuild->addSeparator(Constants::G_BUILD_PROJECT);
986     mbuild->addSeparator(Constants::G_BUILD_PRODUCT);
987     mbuild->addSeparator(Constants::G_BUILD_SUBPROJECT);
988     mbuild->addSeparator(Constants::G_BUILD_FILE);
989     mbuild->addSeparator(Constants::G_BUILD_ALLPROJECTS_ALLCONFIGURATIONS);
990     mbuild->addSeparator(Constants::G_BUILD_PROJECT_ALLCONFIGURATIONS);
991     msessionContextMenu->addSeparator(Constants::G_SESSION_OTHER);
992     mbuild->addSeparator(Constants::G_BUILD_CANCEL);
993     mbuild->addSeparator(Constants::G_BUILD_BUILD);
994     mbuild->addSeparator(Constants::G_BUILD_RUN);
995     mprojectContextMenu->addSeparator(Constants::G_PROJECT_REBUILD);
996 
997     //
998     // Actions
999     //
1000 
1001     // new action
1002     dd->m_newAction = new QAction(tr("New Project..."), this);
1003     cmd = ActionManager::registerAction(dd->m_newAction, Constants::NEWPROJECT);
1004     cmd->setDefaultKeySequence(QKeySequence(tr("Ctrl+Shift+N")));
1005     msessionContextMenu->addAction(cmd, Constants::G_SESSION_FILES);
1006 
1007     // open action
1008     dd->m_loadAction = new QAction(tr("Load Project..."), this);
1009     cmd = ActionManager::registerAction(dd->m_loadAction, Constants::LOAD);
1010     if (!Utils::HostOsInfo::isMacHost())
1011         cmd->setDefaultKeySequence(QKeySequence(tr("Ctrl+Shift+O")));
1012     msessionContextMenu->addAction(cmd, Constants::G_SESSION_FILES);
1013 
1014     // Default open action
1015     dd->m_openFileAction = new QAction(tr("Open File"), this);
1016     cmd = ActionManager::registerAction(dd->m_openFileAction, Constants::OPENFILE,
1017                        projectTreeContext);
1018     mfileContextMenu->addAction(cmd, Constants::G_FILE_OPEN);
1019 
1020     dd->m_searchOnFileSystem = new QAction(Core::FileUtils::msgFindInDirectory(), this);
1021     cmd = ActionManager::registerAction(dd->m_searchOnFileSystem, Constants::SEARCHONFILESYSTEM, projectTreeContext);
1022 
1023     mfileContextMenu->addAction(cmd, Constants::G_FILE_OTHER);
1024     mfolderContextMenu->addAction(cmd, Constants::G_FOLDER_CONFIG);
1025     msubProjectContextMenu->addAction(cmd, Constants::G_PROJECT_LAST);
1026     mprojectContextMenu->addAction(cmd, Constants::G_PROJECT_LAST);
1027 
1028     dd->m_showInGraphicalShell = new QAction(Core::FileUtils::msgGraphicalShellAction(), this);
1029     cmd = ActionManager::registerAction(dd->m_showInGraphicalShell,
1030                                         Core::Constants::SHOWINGRAPHICALSHELL,
1031                                         projectTreeContext);
1032     mfileContextMenu->addAction(cmd, Constants::G_FILE_OPEN);
1033     mfolderContextMenu->addAction(cmd, Constants::G_FOLDER_FILES);
1034 
1035     // Open Terminal Here menu
1036     dd->m_openTerminalHere = new QAction(Core::FileUtils::msgTerminalHereAction(), this);
1037     cmd = ActionManager::registerAction(dd->m_openTerminalHere, Constants::OPENTERMINALHERE,
1038                                         projectTreeContext);
1039 
1040     mfileContextMenu->addAction(cmd, Constants::G_FILE_OPEN);
1041     mfolderContextMenu->addAction(cmd, Constants::G_FOLDER_FILES);
1042     msubProjectContextMenu->addAction(cmd, Constants::G_PROJECT_LAST);
1043     mprojectContextMenu->addAction(cmd, Constants::G_PROJECT_LAST);
1044 
1045     mfileContextMenu->addMenu(openTerminal, Constants::G_FILE_OPEN);
1046     mfolderContextMenu->addMenu(openTerminal, Constants::G_FOLDER_FILES);
1047     msubProjectContextMenu->addMenu(openTerminal, Constants::G_PROJECT_LAST);
1048     mprojectContextMenu->addMenu(openTerminal, Constants::G_PROJECT_LAST);
1049 
1050 
1051     dd->m_openTerminalHereBuildEnv = new QAction(tr("Build Environment"), this);
1052     dd->m_openTerminalHereRunEnv = new QAction(tr("Run Environment"), this);
1053     cmd = ActionManager::registerAction(dd->m_openTerminalHereBuildEnv,
1054                                         "ProjectExplorer.OpenTerminalHereBuildEnv",
1055                                         projectTreeContext);
1056     dd->m_openTerminalMenu->addAction(dd->m_openTerminalHereBuildEnv);
1057 
1058     cmd = ActionManager::registerAction(dd->m_openTerminalHereRunEnv,
1059                                         "ProjectExplorer.OpenTerminalHereRunEnv",
1060                                         projectTreeContext);
1061     dd->m_openTerminalMenu->addAction(dd->m_openTerminalHereRunEnv);
1062 
1063     // Open With menu
1064     mfileContextMenu->addMenu(openWith, Constants::G_FILE_OPEN);
1065 
1066     // recent projects menu
1067     ActionContainer *mrecent =
1068         ActionManager::createMenu(Constants::M_RECENTPROJECTS);
1069     mrecent->menu()->setTitle(tr("Recent P&rojects"));
1070     mrecent->setOnAllDisabledBehavior(ActionContainer::Show);
1071     mfile->addMenu(mrecent, Core::Constants::G_FILE_OPEN);
1072     connect(mfile->menu(), &QMenu::aboutToShow,
1073             dd, &ProjectExplorerPluginPrivate::updateRecentProjectMenu);
1074 
1075     // session menu
1076     ActionContainer *msession = ActionManager::createMenu(Constants::M_SESSION);
1077     msession->menu()->setTitle(tr("S&essions"));
1078     msession->setOnAllDisabledBehavior(ActionContainer::Show);
1079     mfile->addMenu(msession, Core::Constants::G_FILE_OPEN);
1080     dd->m_sessionMenu = msession->menu();
1081     connect(mfile->menu(), &QMenu::aboutToShow,
1082             dd, &ProjectExplorerPluginPrivate::updateSessionMenu);
1083 
1084     // session manager action
1085     dd->m_sessionManagerAction = new QAction(tr("&Manage..."), this);
1086     dd->m_sessionMenu->addAction(dd->m_sessionManagerAction);
1087     dd->m_sessionMenu->addSeparator();
1088     cmd->setDefaultKeySequence(QKeySequence());
1089 
1090 
1091     // unload action
1092     dd->m_unloadAction = new Utils::ParameterAction(tr("Close Project"), tr("Close Pro&ject \"%1\""),
1093                                                       Utils::ParameterAction::AlwaysEnabled, this);
1094     cmd = ActionManager::registerAction(dd->m_unloadAction, Constants::UNLOAD);
1095     cmd->setAttribute(Command::CA_UpdateText);
1096     cmd->setDescription(dd->m_unloadAction->text());
1097     mfile->addAction(cmd, Core::Constants::G_FILE_PROJECT);
1098 
1099     dd->m_closeProjectFilesActionFileMenu = new Utils::ParameterAction(
1100                 tr("Close All Files in Project"), tr("Close All Files in Project \"%1\""),
1101                 Utils::ParameterAction::AlwaysEnabled, this);
1102     cmd = ActionManager::registerAction(dd->m_closeProjectFilesActionFileMenu,
1103                                         "ProjectExplorer.CloseProjectFilesFileMenu");
1104     cmd->setAttribute(Command::CA_UpdateText);
1105     cmd->setDescription(dd->m_closeProjectFilesActionFileMenu->text());
1106     mfile->addAction(cmd, Core::Constants::G_FILE_PROJECT);
1107 
1108     ActionContainer *munload =
1109         ActionManager::createMenu(Constants::M_UNLOADPROJECTS);
1110     munload->menu()->setTitle(tr("Close Pro&ject"));
1111     munload->setOnAllDisabledBehavior(ActionContainer::Show);
1112     mfile->addMenu(munload, Core::Constants::G_FILE_PROJECT);
1113     connect(mfile->menu(), &QMenu::aboutToShow,
1114             dd, &ProjectExplorerPluginPrivate::updateUnloadProjectMenu);
1115 
1116     // unload session action
1117     dd->m_closeAllProjects = new QAction(tr("Close All Projects and Editors"), this);
1118     cmd = ActionManager::registerAction(dd->m_closeAllProjects, Constants::CLEARSESSION);
1119     mfile->addAction(cmd, Core::Constants::G_FILE_PROJECT);
1120     msessionContextMenu->addAction(cmd, Constants::G_SESSION_FILES);
1121 
1122     // build session action
1123     const QIcon sideBarIcon = Utils::Icon::sideBarIcon(Icons::BUILD, Icons::BUILD_FLAT);
1124     const QIcon buildIcon = Utils::Icon::combinedIcon({Icons::BUILD_SMALL.icon(), sideBarIcon});
1125     dd->m_buildSessionAction = new QAction(buildIcon, tr("Build All Projects"), this);
1126     cmd = ActionManager::registerAction(dd->m_buildSessionAction, Constants::BUILDSESSION);
1127     cmd->setDefaultKeySequence(QKeySequence(tr("Ctrl+Shift+B")));
1128     mbuild->addAction(cmd, Constants::G_BUILD_ALLPROJECTS);
1129     msessionContextMenu->addAction(cmd, Constants::G_SESSION_BUILD);
1130 
1131     dd->m_buildSessionForAllConfigsAction
1132             = new QAction(buildIcon, tr("Build All Projects for All Configurations"), this);
1133     cmd = ActionManager::registerAction(dd->m_buildSessionForAllConfigsAction,
1134                                         Constants::BUILDSESSIONALLCONFIGS);
1135     mbuild->addAction(cmd, Constants::G_BUILD_ALLPROJECTS_ALLCONFIGURATIONS);
1136     msessionContextMenu->addAction(cmd, Constants::G_SESSION_BUILD);
1137 
1138     // deploy session
1139     dd->m_deploySessionAction = new QAction(tr("Deploy"), this);
1140     dd->m_deploySessionAction->setWhatsThis(tr("Deploy All Projects"));
1141     cmd = ActionManager::registerAction(dd->m_deploySessionAction, Constants::DEPLOYSESSION);
1142     cmd->setDescription(dd->m_deploySessionAction->whatsThis());
1143     mbuild->addAction(cmd, Constants::G_BUILD_ALLPROJECTS);
1144     msessionContextMenu->addAction(cmd, Constants::G_SESSION_BUILD);
1145 
1146     // rebuild session action
1147     dd->m_rebuildSessionAction = new QAction(Icons::REBUILD.icon(), tr("Rebuild"),
1148                                              this);
1149     dd->m_rebuildSessionAction->setWhatsThis(tr("Rebuild All Projects"));
1150     cmd = ActionManager::registerAction(dd->m_rebuildSessionAction, Constants::REBUILDSESSION);
1151     cmd->setDescription(dd->m_rebuildSessionAction->whatsThis());
1152     mbuild->addAction(cmd, Constants::G_BUILD_ALLPROJECTS);
1153     msessionContextMenu->addAction(cmd, Constants::G_SESSION_REBUILD);
1154 
1155     dd->m_rebuildSessionForAllConfigsAction
1156             = new QAction(Icons::REBUILD.icon(), tr("Rebuild"),
1157                           this);
1158     dd->m_rebuildSessionForAllConfigsAction->setWhatsThis(
1159         tr("Rebuild All Projects for All Configurations"));
1160     cmd = ActionManager::registerAction(dd->m_rebuildSessionForAllConfigsAction,
1161                                         Constants::REBUILDSESSIONALLCONFIGS);
1162     cmd->setDescription(dd->m_rebuildSessionForAllConfigsAction->whatsThis());
1163     mbuild->addAction(cmd, Constants::G_BUILD_ALLPROJECTS_ALLCONFIGURATIONS);
1164     msessionContextMenu->addAction(cmd, Constants::G_SESSION_REBUILD);
1165 
1166     // clean session
1167     dd->m_cleanSessionAction = new QAction(Utils::Icons::CLEAN.icon(), tr("Clean"),
1168                                            this);
1169     dd->m_cleanSessionAction->setWhatsThis(tr("Clean All Projects"));
1170     cmd = ActionManager::registerAction(dd->m_cleanSessionAction, Constants::CLEANSESSION);
1171     cmd->setDescription(dd->m_cleanSessionAction->whatsThis());
1172     mbuild->addAction(cmd, Constants::G_BUILD_ALLPROJECTS);
1173     msessionContextMenu->addAction(cmd, Constants::G_SESSION_REBUILD);
1174 
1175     dd->m_cleanSessionForAllConfigsAction = new QAction(Utils::Icons::CLEAN.icon(),
1176             tr("Clean"), this);
1177     dd->m_cleanSessionForAllConfigsAction->setWhatsThis(
1178         tr("Clean All Projects for All Configurations"));
1179     cmd = ActionManager::registerAction(dd->m_cleanSessionForAllConfigsAction,
1180                                         Constants::CLEANSESSIONALLCONFIGS);
1181     cmd->setDescription(dd->m_cleanSessionForAllConfigsAction->whatsThis());
1182     mbuild->addAction(cmd, Constants::G_BUILD_ALLPROJECTS_ALLCONFIGURATIONS);
1183     msessionContextMenu->addAction(cmd, Constants::G_SESSION_REBUILD);
1184 
1185     // build action
1186     dd->m_buildAction = new Utils::ParameterAction(tr("Build Project"), tr("Build Project \"%1\""),
1187                                                      Utils::ParameterAction::AlwaysEnabled, this);
1188     dd->m_buildAction->setIcon(buildIcon);
1189     cmd = ActionManager::registerAction(dd->m_buildAction, Constants::BUILD);
1190     cmd->setAttribute(Command::CA_UpdateText);
1191     cmd->setDescription(dd->m_buildAction->text());
1192     cmd->setDefaultKeySequence(QKeySequence(tr("Ctrl+B")));
1193     mbuild->addAction(cmd, Constants::G_BUILD_PROJECT);
1194 
1195     dd->m_buildProjectForAllConfigsAction
1196             = new Utils::ParameterAction(tr("Build Project for All Configurations"),
1197                                          tr("Build Project \"%1\" for All Configurations"),
1198                                          Utils::ParameterAction::AlwaysEnabled, this);
1199     dd->m_buildProjectForAllConfigsAction->setIcon(buildIcon);
1200     cmd = ActionManager::registerAction(dd->m_buildProjectForAllConfigsAction,
1201                                         Constants::BUILDALLCONFIGS);
1202     cmd->setAttribute(Command::CA_UpdateText);
1203     cmd->setDescription(dd->m_buildProjectForAllConfigsAction->text());
1204     mbuild->addAction(cmd, Constants::G_BUILD_PROJECT_ALLCONFIGURATIONS);
1205 
1206     // Add to mode bar
1207     dd->m_modeBarBuildAction = new Utils::ProxyAction(this);
1208     dd->m_modeBarBuildAction->setObjectName("Build"); // used for UI introduction
1209     dd->m_modeBarBuildAction->initialize(cmd->action());
1210     dd->m_modeBarBuildAction->setAttribute(Utils::ProxyAction::UpdateText);
1211     dd->m_modeBarBuildAction->setAction(cmd->action());
1212     ModeManager::addAction(dd->m_modeBarBuildAction, Constants::P_ACTION_BUILDPROJECT);
1213 
1214     // build for run config
1215     dd->m_buildForRunConfigAction = new Utils::ParameterAction(
1216                 tr("Build for &Run Configuration"), tr("Build for &Run Configuration \"%1\""),
1217                 Utils::ParameterAction::EnabledWithParameter, this);
1218     dd->m_buildForRunConfigAction->setIcon(buildIcon);
1219     cmd = ActionManager::registerAction(dd->m_buildForRunConfigAction,
1220                                         "ProjectExplorer.BuildForRunConfig");
1221     cmd->setAttribute(Command::CA_UpdateText);
1222     cmd->setDescription(dd->m_buildForRunConfigAction->text());
1223     mbuild->addAction(cmd, Constants::G_BUILD_BUILD);
1224 
1225     // deploy action
1226     dd->m_deployAction = new QAction(tr("Deploy"), this);
1227     dd->m_deployAction->setWhatsThis(tr("Deploy Project"));
1228     cmd = ActionManager::registerAction(dd->m_deployAction, Constants::DEPLOY);
1229     cmd->setAttribute(Command::CA_UpdateText);
1230     cmd->setDescription(dd->m_deployAction->whatsThis());
1231     mbuild->addAction(cmd, Constants::G_BUILD_PROJECT);
1232 
1233     // rebuild action
1234     dd->m_rebuildAction = new QAction(Icons::REBUILD.icon(), tr("Rebuild"), this);
1235     dd->m_rebuildAction->setWhatsThis(tr("Rebuild Project"));
1236     cmd = ActionManager::registerAction(dd->m_rebuildAction, Constants::REBUILD);
1237     cmd->setAttribute(Command::CA_UpdateText);
1238     cmd->setDescription(dd->m_rebuildAction->whatsThis());
1239     mbuild->addAction(cmd, Constants::G_BUILD_PROJECT);
1240 
1241     dd->m_rebuildProjectForAllConfigsAction
1242             = new QAction(Icons::REBUILD.icon(), tr("Rebuild"), this);
1243     dd->m_rebuildProjectForAllConfigsAction->setWhatsThis(
1244         tr("Rebuild Project for All Configurations"));
1245     cmd = ActionManager::registerAction(dd->m_rebuildProjectForAllConfigsAction,
1246                                         Constants::REBUILDALLCONFIGS);
1247     cmd->setAttribute(Command::CA_UpdateText);
1248     cmd->setDescription(dd->m_rebuildProjectForAllConfigsAction->whatsThis());
1249     mbuild->addAction(cmd, Constants::G_BUILD_PROJECT_ALLCONFIGURATIONS);
1250 
1251     // clean action
1252     dd->m_cleanAction = new QAction(Utils::Icons::CLEAN.icon(), tr("Clean"), this);
1253     dd->m_cleanAction->setWhatsThis(tr("Clean Project"));
1254     cmd = ActionManager::registerAction(dd->m_cleanAction, Constants::CLEAN);
1255     cmd->setAttribute(Command::CA_UpdateText);
1256     cmd->setDescription(dd->m_cleanAction->whatsThis());
1257     mbuild->addAction(cmd, Constants::G_BUILD_PROJECT);
1258 
1259     dd->m_cleanProjectForAllConfigsAction
1260             = new QAction(Utils::Icons::CLEAN.icon(), tr("Clean"), this);
1261     dd->m_cleanProjectForAllConfigsAction->setWhatsThis(tr("Clean Project for All Configurations"));
1262     cmd = ActionManager::registerAction(dd->m_cleanProjectForAllConfigsAction,
1263                                         Constants::CLEANALLCONFIGS);
1264     cmd->setAttribute(Command::CA_UpdateText);
1265     cmd->setDescription(dd->m_cleanProjectForAllConfigsAction->whatsThis());
1266     mbuild->addAction(cmd, Constants::G_BUILD_PROJECT_ALLCONFIGURATIONS);
1267 
1268     // cancel build action
1269     dd->m_cancelBuildAction = new QAction(Utils::Icons::STOP_SMALL.icon(), tr("Cancel Build"), this);
1270     cmd = ActionManager::registerAction(dd->m_cancelBuildAction, Constants::CANCELBUILD);
1271     cmd->setDefaultKeySequence(QKeySequence(useMacShortcuts ? tr("Meta+Backspace") : tr("Alt+Backspace")));
1272     mbuild->addAction(cmd, Constants::G_BUILD_CANCEL);
1273 
1274     // run action
1275     dd->m_runAction = new QAction(runIcon, tr("Run"), this);
1276     cmd = ActionManager::registerAction(dd->m_runAction, Constants::RUN);
1277     cmd->setAttribute(Command::CA_UpdateText);
1278 
1279     cmd->setDefaultKeySequence(QKeySequence(tr("Ctrl+R")));
1280     mbuild->addAction(cmd, Constants::G_BUILD_RUN);
1281 
1282     cmd->action()->setObjectName("Run"); // used for UI introduction
1283     ModeManager::addAction(cmd->action(), Constants::P_ACTION_RUN);
1284 
1285     // Run without deployment action
1286     dd->m_runWithoutDeployAction = new QAction(tr("Run Without Deployment"), this);
1287     cmd = ActionManager::registerAction(dd->m_runWithoutDeployAction, Constants::RUNWITHOUTDEPLOY);
1288     mbuild->addAction(cmd, Constants::G_BUILD_RUN);
1289 
1290     // build action with dependencies (context menu)
1291     dd->m_buildDependenciesActionContextMenu = new QAction(tr("Build"), this);
1292     cmd = ActionManager::registerAction(dd->m_buildDependenciesActionContextMenu, Constants::BUILDDEPENDCM, projectTreeContext);
1293     cmd->setAttribute(Command::CA_UpdateText);
1294     mprojectContextMenu->addAction(cmd, Constants::G_PROJECT_BUILD);
1295 
1296     // build action (context menu)
1297     dd->m_buildActionContextMenu = new QAction(tr("Build Without Dependencies"), this);
1298     cmd = ActionManager::registerAction(dd->m_buildActionContextMenu, Constants::BUILDCM, projectTreeContext);
1299     cmd->setAttribute(Command::CA_UpdateText);
1300     mprojectContextMenu->addAction(cmd, Constants::G_PROJECT_BUILD);
1301 
1302     // rebuild action with dependencies (context menu)
1303     dd->m_rebuildDependenciesActionContextMenu = new QAction(tr("Rebuild"), this);
1304     cmd = ActionManager::registerAction(dd->m_rebuildDependenciesActionContextMenu, Constants::REBUILDDEPENDCM, projectTreeContext);
1305     cmd->setAttribute(Command::CA_UpdateText);
1306     mprojectContextMenu->addAction(cmd, Constants::G_PROJECT_REBUILD);
1307 
1308     // rebuild action (context menu)
1309     dd->m_rebuildActionContextMenu = new QAction(tr("Rebuild Without Dependencies"), this);
1310     cmd = ActionManager::registerAction(dd->m_rebuildActionContextMenu, Constants::REBUILDCM, projectTreeContext);
1311     cmd->setAttribute(Command::CA_UpdateText);
1312     mprojectContextMenu->addAction(cmd, Constants::G_PROJECT_REBUILD);
1313 
1314     // clean action with dependencies (context menu)
1315     dd->m_cleanDependenciesActionContextMenu = new QAction(tr("Clean"), this);
1316     cmd = ActionManager::registerAction(dd->m_cleanDependenciesActionContextMenu, Constants::CLEANDEPENDCM, projectTreeContext);
1317     cmd->setAttribute(Command::CA_UpdateText);
1318     mprojectContextMenu->addAction(cmd, Constants::G_PROJECT_REBUILD);
1319 
1320     // clean action (context menu)
1321     dd->m_cleanActionContextMenu = new QAction(tr("Clean Without Dependencies"), this);
1322     cmd = ActionManager::registerAction(dd->m_cleanActionContextMenu, Constants::CLEANCM, projectTreeContext);
1323     cmd->setAttribute(Command::CA_UpdateText);
1324     mprojectContextMenu->addAction(cmd, Constants::G_PROJECT_REBUILD);
1325 
1326     // build without dependencies action
1327     dd->m_buildProjectOnlyAction = new QAction(tr("Build Without Dependencies"), this);
1328     ActionManager::registerAction(dd->m_buildProjectOnlyAction, Constants::BUILDPROJECTONLY);
1329 
1330     // rebuild without dependencies action
1331     dd->m_rebuildProjectOnlyAction = new QAction(tr("Rebuild Without Dependencies"), this);
1332     ActionManager::registerAction(dd->m_rebuildProjectOnlyAction, Constants::REBUILDPROJECTONLY);
1333 
1334     // deploy without dependencies action
1335     dd->m_deployProjectOnlyAction = new QAction(tr("Deploy Without Dependencies"), this);
1336     ActionManager::registerAction(dd->m_deployProjectOnlyAction, Constants::DEPLOYPROJECTONLY);
1337 
1338     // clean without dependencies action
1339     dd->m_cleanProjectOnlyAction = new QAction(tr("Clean Without Dependencies"), this);
1340     ActionManager::registerAction(dd->m_cleanProjectOnlyAction, Constants::CLEANPROJECTONLY);
1341 
1342     // deploy action (context menu)
1343     dd->m_deployActionContextMenu = new QAction(tr("Deploy"), this);
1344     cmd = ActionManager::registerAction(dd->m_deployActionContextMenu, Constants::DEPLOYCM, projectTreeContext);
1345     mprojectContextMenu->addAction(cmd, Constants::G_PROJECT_RUN);
1346 
1347     dd->m_runActionContextMenu = new QAction(runIcon, tr("Run"), this);
1348     cmd = ActionManager::registerAction(dd->m_runActionContextMenu, Constants::RUNCONTEXTMENU, projectTreeContext);
1349     mprojectContextMenu->addAction(cmd, Constants::G_PROJECT_RUN);
1350     msubProjectContextMenu->addAction(cmd, Constants::G_PROJECT_RUN);
1351 
1352     // add new file action
1353     dd->m_addNewFileAction = new QAction(this);
1354     cmd = ActionManager::registerAction(dd->m_addNewFileAction, Constants::ADDNEWFILE,
1355                        projectTreeContext);
1356     mprojectContextMenu->addAction(cmd, Constants::G_PROJECT_FILES);
1357     msubProjectContextMenu->addAction(cmd, Constants::G_PROJECT_FILES);
1358     mfolderContextMenu->addAction(cmd, Constants::G_FOLDER_FILES);
1359 
1360     // add existing file action
1361     dd->m_addExistingFilesAction = new QAction(tr("Add Existing Files..."), this);
1362     cmd = ActionManager::registerAction(dd->m_addExistingFilesAction, Constants::ADDEXISTINGFILES,
1363                        projectTreeContext);
1364     mprojectContextMenu->addAction(cmd, Constants::G_PROJECT_FILES);
1365     msubProjectContextMenu->addAction(cmd, Constants::G_PROJECT_FILES);
1366     mfolderContextMenu->addAction(cmd, Constants::G_FOLDER_FILES);
1367 
1368     // add existing projects action
1369     dd->m_addExistingProjectsAction = new QAction(tr("Add Existing Projects..."), this);
1370     cmd = ActionManager::registerAction(dd->m_addExistingProjectsAction,
1371                                         "ProjectExplorer.AddExistingProjects", projectTreeContext);
1372     mprojectContextMenu->addAction(cmd, Constants::G_PROJECT_FILES);
1373     msubProjectContextMenu->addAction(cmd, Constants::G_PROJECT_FILES);
1374 
1375     // add existing directory action
1376     dd->m_addExistingDirectoryAction = new QAction(tr("Add Existing Directory..."), this);
1377     cmd = ActionManager::registerAction(dd->m_addExistingDirectoryAction,
1378                                               Constants::ADDEXISTINGDIRECTORY,
1379                                               projectTreeContext);
1380     mprojectContextMenu->addAction(cmd, Constants::G_PROJECT_FILES);
1381     msubProjectContextMenu->addAction(cmd, Constants::G_PROJECT_FILES);
1382     mfolderContextMenu->addAction(cmd, Constants::G_FOLDER_FILES);
1383 
1384     // new subproject action
1385     dd->m_addNewSubprojectAction = new QAction(tr("New Subproject..."), this);
1386     cmd = ActionManager::registerAction(dd->m_addNewSubprojectAction, Constants::ADDNEWSUBPROJECT,
1387                        projectTreeContext);
1388     mprojectContextMenu->addAction(cmd, Constants::G_PROJECT_FILES);
1389     msubProjectContextMenu->addAction(cmd, Constants::G_PROJECT_FILES);
1390 
1391     dd->m_closeProjectFilesActionContextMenu = new Utils::ParameterAction(
1392                 tr("Close All Files"), tr("Close All Files in Project \"%1\""),
1393                 Utils::ParameterAction::EnabledWithParameter, this);
1394     cmd = ActionManager::registerAction(dd->m_closeProjectFilesActionContextMenu,
1395                                         "ProjectExplorer.CloseAllFilesInProjectContextMenu");
1396     cmd->setAttribute(Command::CA_UpdateText);
1397     cmd->setDescription(dd->m_closeProjectFilesActionContextMenu->text());
1398     mprojectContextMenu->addAction(cmd, Constants::G_PROJECT_LAST);
1399 
1400     // unload project again, in right position
1401     dd->m_unloadActionContextMenu = new Utils::ParameterAction(tr("Close Project"), tr("Close Project \"%1\""),
1402                                                               Utils::ParameterAction::EnabledWithParameter, this);
1403     cmd = ActionManager::registerAction(dd->m_unloadActionContextMenu, Constants::UNLOADCM);
1404     cmd->setAttribute(Command::CA_UpdateText);
1405     cmd->setDescription(dd->m_unloadActionContextMenu->text());
1406     mprojectContextMenu->addAction(cmd, Constants::G_PROJECT_LAST);
1407 
1408     // file properties action
1409     dd->m_filePropertiesAction = new QAction(tr("Properties..."), this);
1410     cmd = ActionManager::registerAction(dd->m_filePropertiesAction, Constants::FILEPROPERTIES,
1411                        projectTreeContext);
1412     mfileContextMenu->addAction(cmd, Constants::G_FILE_OTHER);
1413 
1414     // remove file action
1415     dd->m_removeFileAction = new QAction(this);
1416     cmd = ActionManager::registerAction(dd->m_removeFileAction, Constants::REMOVEFILE,
1417                        projectTreeContext);
1418     cmd->setDefaultKeySequences({QKeySequence::Delete, QKeySequence::Backspace});
1419     mfileContextMenu->addAction(cmd, Constants::G_FILE_OTHER);
1420 
1421     // duplicate file action
1422     dd->m_duplicateFileAction = new QAction(tr("Duplicate File..."), this);
1423     cmd = ActionManager::registerAction(dd->m_duplicateFileAction, Constants::DUPLICATEFILE,
1424                        projectTreeContext);
1425     mfileContextMenu->addAction(cmd, Constants::G_FILE_OTHER);
1426 
1427     //: Remove project from parent profile (Project explorer view); will not physically delete any files.
1428     dd->m_removeProjectAction = new QAction(tr("Remove Project..."), this);
1429     cmd = ActionManager::registerAction(dd->m_removeProjectAction, Constants::REMOVEPROJECT,
1430                        projectTreeContext);
1431     msubProjectContextMenu->addAction(cmd, Constants::G_PROJECT_FILES);
1432 
1433     // delete file action
1434     dd->m_deleteFileAction = new QAction(tr("Delete File..."), this);
1435     cmd = ActionManager::registerAction(dd->m_deleteFileAction, Constants::DELETEFILE,
1436                              projectTreeContext);
1437     cmd->setDefaultKeySequences({QKeySequence::Delete, QKeySequence::Backspace});
1438     mfileContextMenu->addAction(cmd, Constants::G_FILE_OTHER);
1439 
1440     // renamefile action
1441     dd->m_renameFileAction = new QAction(this);
1442     cmd = ActionManager::registerAction(dd->m_renameFileAction, Constants::RENAMEFILE,
1443                        projectTreeContext);
1444     mfileContextMenu->addAction(cmd, Constants::G_FILE_OTHER);
1445 
1446     // diff file action
1447     dd->m_diffFileAction = TextEditor::TextDocument::createDiffAgainstCurrentFileAction(
1448         this, &ProjectTree::currentFilePath);
1449     cmd = ActionManager::registerAction(dd->m_diffFileAction, Constants::DIFFFILE, projectTreeContext);
1450     mfileContextMenu->addAction(cmd, Constants::G_FILE_OTHER);
1451 
1452     // Not yet used by anyone, so hide for now
1453 //    mfolder->addAction(cmd, Constants::G_FOLDER_FILES);
1454 //    msubProject->addAction(cmd, Constants::G_FOLDER_FILES);
1455 //    mproject->addAction(cmd, Constants::G_FOLDER_FILES);
1456 
1457     // set startup project action
1458     dd->m_setStartupProjectAction = new Utils::ParameterAction(tr("Set as Active Project"),
1459                                                               tr("Set \"%1\" as Active Project"),
1460                                                               Utils::ParameterAction::AlwaysEnabled, this);
1461     cmd = ActionManager::registerAction(dd->m_setStartupProjectAction, Constants::SETSTARTUP,
1462                              projectTreeContext);
1463     cmd->setAttribute(Command::CA_UpdateText);
1464     cmd->setDescription(dd->m_setStartupProjectAction->text());
1465     mprojectContextMenu->addAction(cmd, Constants::G_PROJECT_FIRST);
1466 
1467     // Collapse & Expand.
1468     const Id treeGroup = Constants::G_PROJECT_TREE;
1469 
1470     dd->m_projectTreeExpandNodeAction = new QAction(tr("Expand"), this);
1471     connect(dd->m_projectTreeExpandNodeAction, &QAction::triggered,
1472             ProjectTree::instance(), &ProjectTree::expandCurrentNodeRecursively);
1473     Command * const expandNodeCmd = ActionManager::registerAction(
1474                 dd->m_projectTreeExpandNodeAction, "ProjectExplorer.ExpandNode",
1475                 projectTreeContext);
1476     dd->m_projectTreeCollapseAllAction = new QAction(tr("Collapse All"), this);
1477     Command * const collapseCmd = ActionManager::registerAction(
1478                 dd->m_projectTreeCollapseAllAction, Constants::PROJECTTREE_COLLAPSE_ALL,
1479                 projectTreeContext);
1480     dd->m_projectTreeExpandAllAction = new QAction(tr("Expand All"), this);
1481     Command * const expandCmd = ActionManager::registerAction(
1482                 dd->m_projectTreeExpandAllAction, Constants::PROJECTTREE_EXPAND_ALL,
1483                 projectTreeContext);
1484     for (Core::ActionContainer * const ac : {mfileContextMenu, msubProjectContextMenu,
1485          mfolderContextMenu, mprojectContextMenu, msessionContextMenu}) {
1486         ac->addSeparator(treeGroup);
1487         ac->addAction(expandNodeCmd, treeGroup);
1488         ac->addAction(collapseCmd, treeGroup);
1489         ac->addAction(expandCmd, treeGroup);
1490     }
1491 
1492     // target selector
1493     dd->m_projectSelectorAction = new QAction(this);
1494     dd->m_projectSelectorAction->setObjectName("KitSelector"); // used for UI introduction
1495     dd->m_projectSelectorAction->setCheckable(true);
1496     dd->m_projectSelectorAction->setEnabled(false);
1497     dd->m_targetSelector = new MiniProjectTargetSelector(dd->m_projectSelectorAction, ICore::dialogParent());
1498     connect(dd->m_projectSelectorAction, &QAction::triggered,
1499             dd->m_targetSelector, &QWidget::show);
1500     ModeManager::addProjectSelector(dd->m_projectSelectorAction);
1501 
1502     dd->m_projectSelectorActionMenu = new QAction(this);
1503     dd->m_projectSelectorActionMenu->setEnabled(false);
1504     dd->m_projectSelectorActionMenu->setText(tr("Open Build and Run Kit Selector..."));
1505     connect(dd->m_projectSelectorActionMenu, &QAction::triggered,
1506             dd->m_targetSelector,
1507             &MiniProjectTargetSelector::toggleVisible);
1508     cmd = ActionManager::registerAction(dd->m_projectSelectorActionMenu, Constants::SELECTTARGET);
1509     mbuild->addAction(cmd, Constants::G_BUILD_RUN);
1510 
1511     dd->m_projectSelectorActionQuick = new QAction(this);
1512     dd->m_projectSelectorActionQuick->setEnabled(false);
1513     dd->m_projectSelectorActionQuick->setText(tr("Quick Switch Kit Selector"));
1514     connect(dd->m_projectSelectorActionQuick, &QAction::triggered,
1515             dd->m_targetSelector, &MiniProjectTargetSelector::nextOrShow);
1516     cmd = ActionManager::registerAction(dd->m_projectSelectorActionQuick, Constants::SELECTTARGETQUICK);
1517     cmd->setDefaultKeySequence(QKeySequence(tr("Ctrl+T")));
1518 
1519     connect(ICore::instance(), &ICore::saveSettingsRequested,
1520             dd, &ProjectExplorerPluginPrivate::savePersistentSettings);
1521     connect(EditorManager::instance(), &EditorManager::autoSaved, this, [] {
1522         if (!dd->m_shuttingDown && !SessionManager::loadingSession())
1523             SessionManager::save();
1524     });
1525     connect(qApp, &QApplication::applicationStateChanged, this, [](Qt::ApplicationState state) {
1526         if (!dd->m_shuttingDown && state == Qt::ApplicationActive)
1527             dd->updateWelcomePage();
1528     });
1529 
1530     QSettings *s = ICore::settings();
1531     const QStringList fileNames = s->value(Constants::RECENTPROJECTS_FILE_NAMES_KEY).toStringList();
1532     const QStringList displayNames = s->value(Constants::RECENTPROJECTS_DISPLAY_NAMES_KEY)
1533                                          .toStringList();
1534     if (fileNames.size() == displayNames.size()) {
1535         for (int i = 0; i < fileNames.size(); ++i) {
1536             dd->m_recentProjects.append(qMakePair(fileNames.at(i), displayNames.at(i)));
1537         }
1538     }
1539 
1540     const QVariant buildBeforeDeploy = s->value(Constants::BUILD_BEFORE_DEPLOY_SETTINGS_KEY);
1541     const QString buildBeforeDeployString = buildBeforeDeploy.toString();
1542     if (buildBeforeDeployString == "true") { // backward compatibility with QtC < 4.12
1543         dd->m_projectExplorerSettings.buildBeforeDeploy = BuildBeforeRunMode::WholeProject;
1544     } else if (buildBeforeDeployString == "false") {
1545         dd->m_projectExplorerSettings.buildBeforeDeploy = BuildBeforeRunMode::Off;
1546     } else if (buildBeforeDeploy.isValid()) {
1547         dd->m_projectExplorerSettings.buildBeforeDeploy
1548                 = static_cast<BuildBeforeRunMode>(buildBeforeDeploy.toInt());
1549     }
1550 
1551     static const ProjectExplorerSettings defaultSettings;
1552 
1553     dd->m_projectExplorerSettings.deployBeforeRun
1554         = s->value(Constants::DEPLOY_BEFORE_RUN_SETTINGS_KEY, defaultSettings.deployBeforeRun)
1555               .toBool();
1556     dd->m_projectExplorerSettings.saveBeforeBuild
1557         = s->value(Constants::SAVE_BEFORE_BUILD_SETTINGS_KEY, defaultSettings.saveBeforeBuild)
1558               .toBool();
1559     dd->m_projectExplorerSettings.useJom
1560         = s->value(Constants::USE_JOM_SETTINGS_KEY, defaultSettings.useJom).toBool();
1561     dd->m_projectExplorerSettings.autorestoreLastSession
1562         = s->value(Constants::AUTO_RESTORE_SESSION_SETTINGS_KEY,
1563                    defaultSettings.autorestoreLastSession)
1564               .toBool();
1565     dd->m_projectExplorerSettings.addLibraryPathsToRunEnv
1566         = s->value(Constants::ADD_LIBRARY_PATHS_TO_RUN_ENV_SETTINGS_KEY,
1567                    defaultSettings.addLibraryPathsToRunEnv)
1568               .toBool();
1569     dd->m_projectExplorerSettings.prompToStopRunControl
1570         = s->value(Constants::PROMPT_TO_STOP_RUN_CONTROL_SETTINGS_KEY,
1571                    defaultSettings.prompToStopRunControl)
1572               .toBool();
1573     dd->m_projectExplorerSettings.automaticallyCreateRunConfigurations
1574         = s->value(Constants::AUTO_CREATE_RUN_CONFIGS_SETTINGS_KEY,
1575                    defaultSettings.automaticallyCreateRunConfigurations)
1576               .toBool();
1577     dd->m_projectExplorerSettings.environmentId =
1578             QUuid(s->value(Constants::ENVIRONMENT_ID_SETTINGS_KEY).toByteArray());
1579     if (dd->m_projectExplorerSettings.environmentId.isNull())
1580         dd->m_projectExplorerSettings.environmentId = QUuid::createUuid();
1581     int tmp = s->value(Constants::STOP_BEFORE_BUILD_SETTINGS_KEY,
1582                        int(defaultSettings.stopBeforeBuild))
1583                   .toInt();
1584     if (tmp < 0 || tmp > int(StopBeforeBuild::SameApp))
1585         tmp = int(defaultSettings.stopBeforeBuild);
1586     dd->m_projectExplorerSettings.stopBeforeBuild = StopBeforeBuild(tmp);
1587     dd->m_projectExplorerSettings.terminalMode = static_cast<TerminalMode>(
1588         s->value(Constants::TERMINAL_MODE_SETTINGS_KEY, int(defaultSettings.terminalMode)).toInt());
1589     dd->m_projectExplorerSettings.closeSourceFilesWithProject
1590         = s->value(Constants::CLOSE_FILES_WITH_PROJECT_SETTINGS_KEY,
1591                    defaultSettings.closeSourceFilesWithProject)
1592               .toBool();
1593     dd->m_projectExplorerSettings.clearIssuesOnRebuild
1594         = s->value(Constants::CLEAR_ISSUES_ON_REBUILD_SETTINGS_KEY,
1595                    defaultSettings.clearIssuesOnRebuild)
1596               .toBool();
1597     dd->m_projectExplorerSettings.abortBuildAllOnError
1598         = s->value(Constants::ABORT_BUILD_ALL_ON_ERROR_SETTINGS_KEY,
1599                    defaultSettings.abortBuildAllOnError)
1600               .toBool();
1601     dd->m_projectExplorerSettings.lowBuildPriority
1602         = s->value(Constants::LOW_BUILD_PRIORITY_SETTINGS_KEY, defaultSettings.lowBuildPriority)
1603               .toBool();
1604 
1605     dd->m_buildPropertiesSettings.readSettings(s);
1606 
1607     const int customParserCount = s->value(Constants::CUSTOM_PARSER_COUNT_KEY).toInt();
1608     for (int i = 0; i < customParserCount; ++i) {
1609         CustomParserSettings settings;
1610         settings.fromMap(s->value(Constants::CUSTOM_PARSER_PREFIX_KEY
1611                                   + QString::number(i)).toMap());
1612         dd->m_customParsers << settings;
1613     }
1614 
1615     auto buildManager = new BuildManager(this, dd->m_cancelBuildAction);
1616     connect(buildManager, &BuildManager::buildStateChanged,
1617             dd, &ProjectExplorerPluginPrivate::updateActions);
1618     connect(buildManager, &BuildManager::buildQueueFinished,
1619             dd, &ProjectExplorerPluginPrivate::buildQueueFinished, Qt::QueuedConnection);
1620 
1621     connect(dd->m_sessionManagerAction, &QAction::triggered,
1622             dd, &ProjectExplorerPluginPrivate::showSessionManager);
1623     connect(dd->m_newAction, &QAction::triggered,
1624             dd, &ProjectExplorerPlugin::openNewProjectDialog);
1625     connect(dd->m_loadAction, &QAction::triggered,
1626             dd, &ProjectExplorerPluginPrivate::loadAction);
1627     connect(dd->m_buildProjectOnlyAction, &QAction::triggered, dd, [] {
1628         BuildManager::buildProjectWithoutDependencies(SessionManager::startupProject());
1629     });
1630     connect(dd->m_buildAction, &QAction::triggered, dd, [] {
1631         BuildManager::buildProjectWithDependencies(SessionManager::startupProject());
1632     });
1633     connect(dd->m_buildProjectForAllConfigsAction, &QAction::triggered, dd, [] {
1634         BuildManager::buildProjectWithDependencies(SessionManager::startupProject(),
1635                                                    ConfigSelection::All);
1636     });
1637     connect(dd->m_buildActionContextMenu, &QAction::triggered, dd, [] {
1638         BuildManager::buildProjectWithoutDependencies(ProjectTree::currentProject());
1639     });
1640     connect(dd->m_buildForRunConfigAction, &QAction::triggered, dd, [] {
1641         const Project * const project = SessionManager::startupProject();
1642         QTC_ASSERT(project, return);
1643         const Target * const target = project->activeTarget();
1644         QTC_ASSERT(target, return);
1645         const RunConfiguration * const runConfig = target->activeRunConfiguration();
1646         QTC_ASSERT(runConfig, return);
1647         ProjectNode * const productNode = runConfig->productNode();
1648         QTC_ASSERT(productNode, return);
1649         QTC_ASSERT(productNode->isProduct(), return);
1650         productNode->build();
1651     });
1652     connect(dd->m_buildDependenciesActionContextMenu, &QAction::triggered, dd, [] {
1653         BuildManager::buildProjectWithDependencies(ProjectTree::currentProject());
1654     });
1655     connect(dd->m_buildSessionAction, &QAction::triggered, dd, [] {
1656         BuildManager::buildProjects(SessionManager::projectOrder(), ConfigSelection::Active);
1657     });
1658     connect(dd->m_buildSessionForAllConfigsAction, &QAction::triggered, dd, [] {
1659         BuildManager::buildProjects(SessionManager::projectOrder(), ConfigSelection::All);
1660     });
1661     connect(dd->m_rebuildProjectOnlyAction, &QAction::triggered, dd, [] {
1662         BuildManager::rebuildProjectWithoutDependencies(SessionManager::startupProject());
1663     });
1664     connect(dd->m_rebuildAction, &QAction::triggered, dd, [] {
1665         BuildManager::rebuildProjectWithDependencies(SessionManager::startupProject(),
1666                                                      ConfigSelection::Active);
1667     });
1668     connect(dd->m_rebuildProjectForAllConfigsAction, &QAction::triggered, dd, [] {
1669         BuildManager::rebuildProjectWithDependencies(SessionManager::startupProject(),
1670                                                      ConfigSelection::All);
1671     });
1672     connect(dd->m_rebuildActionContextMenu, &QAction::triggered, dd, [] {
1673         BuildManager::rebuildProjectWithoutDependencies(ProjectTree::currentProject());
1674     });
1675     connect(dd->m_rebuildDependenciesActionContextMenu, &QAction::triggered, dd, [] {
1676         BuildManager::rebuildProjectWithDependencies(ProjectTree::currentProject(),
1677                                                      ConfigSelection::Active);
1678     });
1679     connect(dd->m_rebuildSessionAction, &QAction::triggered, dd, [] {
1680         BuildManager::rebuildProjects(SessionManager::projectOrder(), ConfigSelection::Active);
1681     });
1682     connect(dd->m_rebuildSessionForAllConfigsAction, &QAction::triggered, dd, [] {
1683         BuildManager::rebuildProjects(SessionManager::projectOrder(), ConfigSelection::All);
1684     });
1685     connect(dd->m_deployProjectOnlyAction, &QAction::triggered, dd, [] {
1686         BuildManager::deployProjects({SessionManager::startupProject()});
1687     });
1688     connect(dd->m_deployAction, &QAction::triggered, dd, [] {
1689         BuildManager::deployProjects(SessionManager::projectOrder(SessionManager::startupProject()));
1690     });
1691     connect(dd->m_deployActionContextMenu, &QAction::triggered, dd, [] {
1692         BuildManager::deployProjects({ProjectTree::currentProject()});
1693     });
1694     connect(dd->m_deploySessionAction, &QAction::triggered, dd, [] {
1695         BuildManager::deployProjects(SessionManager::projectOrder());
1696     });
1697     connect(dd->m_cleanProjectOnlyAction, &QAction::triggered, dd, [] {
1698         BuildManager::cleanProjectWithoutDependencies(SessionManager::startupProject());
1699     });
1700     connect(dd->m_cleanAction, &QAction::triggered, dd, [] {
1701         BuildManager::cleanProjectWithDependencies(SessionManager::startupProject(),
1702                                                    ConfigSelection::Active);
1703     });
1704     connect(dd->m_cleanProjectForAllConfigsAction, &QAction::triggered, dd, [] {
1705         BuildManager::cleanProjectWithDependencies(SessionManager::startupProject(),
1706                                                    ConfigSelection::All);
1707     });
1708     connect(dd->m_cleanActionContextMenu, &QAction::triggered, dd, [] {
1709         BuildManager::cleanProjectWithoutDependencies(ProjectTree::currentProject());
1710     });
1711     connect(dd->m_cleanDependenciesActionContextMenu, &QAction::triggered, dd, [] {
1712         BuildManager::cleanProjectWithDependencies(ProjectTree::currentProject(),
1713                                                    ConfigSelection::Active);
1714     });
1715     connect(dd->m_cleanSessionAction, &QAction::triggered, dd, [] {
1716         BuildManager::cleanProjects(SessionManager::projectOrder(), ConfigSelection::Active);
1717     });
1718     connect(dd->m_cleanSessionForAllConfigsAction, &QAction::triggered, dd, [] {
1719         BuildManager::cleanProjects(SessionManager::projectOrder(), ConfigSelection::All);
1720     });
1721     connect(dd->m_runAction, &QAction::triggered,
1722             dd, []() { runStartupProject(Constants::NORMAL_RUN_MODE); });
1723     connect(dd->m_runActionContextMenu, &QAction::triggered,
1724             dd, &ProjectExplorerPluginPrivate::runProjectContextMenu);
1725     connect(dd->m_runWithoutDeployAction, &QAction::triggered,
1726             dd, []() { runStartupProject(Constants::NORMAL_RUN_MODE, true); });
1727     connect(dd->m_cancelBuildAction, &QAction::triggered,
1728             BuildManager::instance(), &BuildManager::cancel);
1729     connect(dd->m_unloadAction, &QAction::triggered,
1730             dd, &ProjectExplorerPluginPrivate::handleUnloadProject);
1731     connect(dd->m_unloadActionContextMenu, &QAction::triggered,
1732             dd, &ProjectExplorerPluginPrivate::unloadProjectContextMenu);
1733     connect(dd->m_closeAllProjects, &QAction::triggered,
1734             dd, &ProjectExplorerPluginPrivate::closeAllProjects);
1735     connect(dd->m_addNewFileAction, &QAction::triggered,
1736             dd, &ProjectExplorerPluginPrivate::addNewFile);
1737     connect(dd->m_addExistingFilesAction, &QAction::triggered,
1738             dd, &ProjectExplorerPluginPrivate::handleAddExistingFiles);
1739     connect(dd->m_addExistingDirectoryAction, &QAction::triggered,
1740             dd, &ProjectExplorerPluginPrivate::addExistingDirectory);
1741     connect(dd->m_addNewSubprojectAction, &QAction::triggered,
1742             dd, &ProjectExplorerPluginPrivate::addNewSubproject);
1743     connect(dd->m_addExistingProjectsAction, &QAction::triggered,
1744             dd, &ProjectExplorerPluginPrivate::addExistingProjects);
1745     connect(dd->m_removeProjectAction, &QAction::triggered,
1746             dd, &ProjectExplorerPluginPrivate::removeProject);
1747     connect(dd->m_openFileAction, &QAction::triggered,
1748             dd, &ProjectExplorerPluginPrivate::openFile);
1749     connect(dd->m_searchOnFileSystem, &QAction::triggered,
1750             dd, &ProjectExplorerPluginPrivate::searchOnFileSystem);
1751     connect(dd->m_showInGraphicalShell, &QAction::triggered,
1752             dd, &ProjectExplorerPluginPrivate::showInGraphicalShell);
1753 
1754     connect(dd->m_openTerminalHere, &QAction::triggered, dd, []() { dd->openTerminalHere(sysEnv); });
1755     connect(dd->m_openTerminalHereBuildEnv, &QAction::triggered, dd, []() { dd->openTerminalHere(buildEnv); });
1756     connect(dd->m_openTerminalHereRunEnv, &QAction::triggered, dd, []() { dd->openTerminalHereWithRunEnv(); });
1757 
1758     connect(dd->m_filePropertiesAction, &QAction::triggered, this, []() {
1759                 const Node *currentNode = ProjectTree::currentNode();
1760                 QTC_ASSERT(currentNode && currentNode->asFileNode(), return);
1761                 ProjectTree::CurrentNodeKeeper nodeKeeper;
1762                 DocumentManager::showFilePropertiesDialog(currentNode->filePath());
1763             });
1764     connect(dd->m_removeFileAction, &QAction::triggered,
1765             dd, &ProjectExplorerPluginPrivate::removeFile);
1766     connect(dd->m_duplicateFileAction, &QAction::triggered,
1767             dd, &ProjectExplorerPluginPrivate::duplicateFile);
1768     connect(dd->m_deleteFileAction, &QAction::triggered,
1769             dd, &ProjectExplorerPluginPrivate::deleteFile);
1770     connect(dd->m_renameFileAction, &QAction::triggered,
1771             dd, &ProjectExplorerPluginPrivate::handleRenameFile);
1772     connect(dd->m_setStartupProjectAction, &QAction::triggered,
1773             dd, &ProjectExplorerPluginPrivate::handleSetStartupProject);
1774     connect(dd->m_closeProjectFilesActionFileMenu, &QAction::triggered,
1775             dd, [] { dd->closeAllFilesInProject(SessionManager::projects().first()); });
1776     connect(dd->m_closeProjectFilesActionContextMenu, &QAction::triggered,
1777             dd, [] { dd->closeAllFilesInProject(ProjectTree::currentProject()); });
1778     connect(dd->m_projectTreeCollapseAllAction, &QAction::triggered,
1779             ProjectTree::instance(), &ProjectTree::collapseAll);
1780     connect(dd->m_projectTreeExpandAllAction, &QAction::triggered,
1781             ProjectTree::instance(), &ProjectTree::expandAll);
1782 
1783     connect(this, &ProjectExplorerPlugin::settingsChanged,
1784             dd, &ProjectExplorerPluginPrivate::updateRunWithoutDeployMenu);
1785 
1786     connect(ICore::instance(), &ICore::newItemDialogStateChanged, dd, [] {
1787         dd->updateContextMenuActions(ProjectTree::currentNode());
1788     });
1789 
1790     dd->updateWelcomePage();
1791 
1792     // FIXME: These are mostly "legacy"/"convenience" entries, relying on
1793     // the global entry point ProjectExplorer::currentProject(). They should
1794     // not be used in the Run/Build configuration pages.
1795     // TODO: Remove the CurrentProject versions in ~4.16
1796     Utils::MacroExpander *expander = Utils::globalMacroExpander();
1797     expander->registerFileVariables(Constants::VAR_CURRENTPROJECT_PREFIX,
1798         tr("Current project's main file."),
1799         []() -> FilePath {
1800             FilePath projectFilePath;
1801             if (Project *project = ProjectTree::currentProject())
1802                 projectFilePath = project->projectFilePath();
1803             return projectFilePath;
1804         }, false);
1805     expander->registerFileVariables("CurrentDocument:Project",
1806         tr("Main file of the project the current document belongs to."),
1807         []() -> FilePath {
1808             FilePath projectFilePath;
1809             if (Project *project = ProjectTree::currentProject())
1810                 projectFilePath = project->projectFilePath();
1811             return projectFilePath;
1812         }, false);
1813 
1814     expander->registerVariable(Constants::VAR_CURRENTPROJECT_NAME,
1815         tr("The name of the current project."),
1816         []() -> QString {
1817             Project *project = ProjectTree::currentProject();
1818             return project ? project->displayName() : QString();
1819         }, false);
1820     expander->registerVariable("CurrentDocument:Project:Name",
1821         tr("The name of the project the current document belongs to."),
1822         []() -> QString {
1823             Project *project = ProjectTree::currentProject();
1824             return project ? project->displayName() : QString();
1825         });
1826 
1827     expander->registerPrefix(Constants::VAR_CURRENTBUILD_ENV,
1828                              BuildConfiguration::tr("Variables in the current build environment."),
1829                              [](const QString &var) {
1830                                  if (BuildConfiguration *bc = currentBuildConfiguration())
1831                                      return bc->environment().expandedValueForKey(var);
1832                                  return QString();
1833                              }, false);
1834     const char currentBuildEnvVar[] = "CurrentDocument:Project:BuildConfig:Env";
1835     expander->registerPrefix(currentBuildEnvVar,
1836                              BuildConfiguration::tr(
1837                                  "Variables in the active build environment "
1838                                  "of the project containing the currently open document."),
1839                              [](const QString &var) {
1840                                  if (BuildConfiguration *bc = currentBuildConfiguration())
1841                                      return bc->environment().expandedValueForKey(var);
1842                                  return QString();
1843                              });
1844     Utils::EnvironmentProvider::addProvider(
1845         {currentBuildEnvVar, tr("Current Build Environment"), []() {
1846              if (BuildConfiguration *bc = currentBuildConfiguration())
1847                  return bc->environment();
1848              return Utils::Environment::systemEnvironment();
1849          }});
1850     Utils::EnvironmentProvider::addProvider(
1851         {"CurrentDocument:Project:RunConfig:Env", tr("Current Run Environment"), []() {
1852              const Project *const project = ProjectTree::currentProject();
1853              const Target *const target = project ? project->activeTarget() : nullptr;
1854              const RunConfiguration *const rc = target ? target->activeRunConfiguration() : nullptr;
1855              if (rc) {
1856                  if (auto envAspect = rc->aspect<EnvironmentAspect>())
1857                      return envAspect->environment();
1858              }
1859              return Utils::Environment::systemEnvironment();
1860          }});
1861 
1862     // Global variables for the active project.
1863     expander->registerVariable("ActiveProject:Name", tr("The name of the active project."),
1864         []() -> QString {
1865             if (const Project * const project = SessionManager::startupProject())
1866                 return project->displayName();
1867             return {};
1868         });
1869     expander->registerFileVariables("ActiveProject", tr("Active project's main file."),
1870         []() -> FilePath {
1871             if (const Project * const project = SessionManager::startupProject())
1872                 return project->projectFilePath();
1873             return {};
1874         });
1875     expander->registerVariable("ActiveProject:Kit:Name",
1876         "The name of the active project's active kit.", // TODO: tr()
1877         []() -> QString {
1878             if (const Target * const target = activeTarget())
1879                 return target->kit()->displayName();
1880             return {};
1881     });
1882     expander->registerVariable("ActiveProject:BuildConfig:Name",
1883         "The name of the active project's active build configuration.", // TODO: tr()
1884         []() -> QString {
1885             if (const BuildConfiguration * const bc = activeBuildConfiguration())
1886                 return bc->displayName();
1887             return {};
1888     });
1889     expander->registerVariable("ActiveProject:BuildConfig:Type",
1890         tr("The type of the active project's active build configuration."),
1891         []() -> QString {
1892             const BuildConfiguration * const bc = activeBuildConfiguration();
1893             const BuildConfiguration::BuildType type = bc
1894                     ? bc->buildType() : BuildConfiguration::Unknown;
1895             return BuildConfiguration::buildTypeName(type);
1896     });
1897     expander->registerVariable("ActiveProject:BuildConfig:Path",
1898         tr("Full build path of the active project's active build configuration."),
1899         []() -> QString {
1900             if (const BuildConfiguration * const bc = activeBuildConfiguration())
1901                 return bc->buildDirectory().toUserOutput();
1902             return {};
1903     });
1904     const char activeBuildEnvVar[] = "ActiveProject:BuildConfig:Env";
1905     Utils::EnvironmentProvider::addProvider(
1906         {activeBuildEnvVar, tr("Active build environment of the active project."), [] {
1907              if (const BuildConfiguration * const bc = activeBuildConfiguration())
1908                  return bc->environment();
1909              return Utils::Environment::systemEnvironment();
1910          }});
1911     expander->registerPrefix(activeBuildEnvVar,
1912                              BuildConfiguration::tr("Variables in the active build environment "
1913                                                     "of the active project."),
1914                              [](const QString &var) {
1915                                  if (BuildConfiguration * const bc = activeBuildConfiguration())
1916                                      return bc->environment().expandedValueForKey(var);
1917                                  return QString();
1918                              });
1919 
1920     expander->registerVariable("ActiveProject:RunConfig:Name",
1921         tr("Name of the active project's active run configuration."),
1922         []() -> QString {
1923             if (const RunConfiguration * const rc = activeRunConfiguration())
1924                 return rc->displayName();
1925             return QString();
1926         });
1927     expander->registerFileVariables("ActiveProject:RunConfig:Executable",
1928         tr("The executable of the active project's active run configuration."),
1929         []() -> FilePath {
1930             if (const RunConfiguration * const rc = activeRunConfiguration())
1931                 return rc->commandLine().executable();
1932             return {};
1933         });
1934     const char activeRunEnvVar[] = "ActiveProject:RunConfig:Env";
1935     Utils::EnvironmentProvider::addProvider(
1936         {activeRunEnvVar, tr("Active run environment of the active project."), [] {
1937              if (const RunConfiguration *const rc = activeRunConfiguration()) {
1938                  if (auto envAspect = rc->aspect<EnvironmentAspect>())
1939                      return envAspect->environment();
1940              }
1941              return Utils::Environment::systemEnvironment();
1942          }});
1943     expander->registerPrefix(
1944         activeRunEnvVar,
1945         tr("Variables in the environment of the active project's active run configuration."),
1946         [](const QString &var) {
1947             if (const RunConfiguration * const rc = activeRunConfiguration()) {
1948                 if (const auto envAspect = rc->aspect<EnvironmentAspect>())
1949                     return envAspect->environment().expandedValueForKey(var);
1950             }
1951             return QString();
1952         });
1953     expander->registerVariable("ActiveProject:RunConfig:WorkingDir",
1954         tr("The working directory of the active project's active run configuration."),
1955         [] {
1956             if (const RunConfiguration * const rc = activeRunConfiguration()) {
1957                 if (const auto wdAspect = rc->aspect<WorkingDirectoryAspect>())
1958                     return wdAspect->workingDirectory(rc->macroExpander()).toString();
1959             }
1960             return QString();
1961     });
1962 
1963     const auto fileHandler = [] {
1964         return SessionManager::sessionNameToFileName(SessionManager::activeSession());
1965     };
1966     expander->registerFileVariables("Session", tr("File where current session is saved."),
1967                                     fileHandler);
1968     expander->registerVariable("Session:Name", tr("Name of current session."), [] {
1969         return SessionManager::activeSession();
1970     });
1971 
1972     DeviceManager::instance()->addDevice(IDevice::Ptr(new DesktopDevice));
1973 
1974     return true;
1975 }
1976 
loadAction()1977 void ProjectExplorerPluginPrivate::loadAction()
1978 {
1979     QString dir = dd->m_lastOpenDirectory;
1980 
1981     // for your special convenience, we preselect a pro file if it is
1982     // the current file
1983     if (const IDocument *document = EditorManager::currentDocument()) {
1984         const QString fn = document->filePath().toString();
1985         const bool isProject = dd->m_profileMimeTypes.contains(document->mimeType());
1986         dir = isProject ? fn : QFileInfo(fn).absolutePath();
1987     }
1988 
1989     QString filename = QFileDialog::getOpenFileName(ICore::dialogParent(),
1990                                                     tr("Load Project"), dir,
1991                                                     dd->m_projectFilterString);
1992     if (filename.isEmpty())
1993         return;
1994 
1995     ProjectExplorerPlugin::OpenProjectResult result = ProjectExplorerPlugin::openProject(filename);
1996     if (!result)
1997         ProjectExplorerPlugin::showOpenProjectError(result);
1998 
1999     updateActions();
2000 }
2001 
unloadProjectContextMenu()2002 void ProjectExplorerPluginPrivate::unloadProjectContextMenu()
2003 {
2004     if (Project *p = ProjectTree::currentProject())
2005         ProjectExplorerPlugin::unloadProject(p);
2006 }
2007 
handleUnloadProject()2008 void ProjectExplorerPluginPrivate::handleUnloadProject()
2009 {
2010     QList<Project *> projects = SessionManager::projects();
2011     QTC_ASSERT(!projects.isEmpty(), return);
2012 
2013     ProjectExplorerPlugin::unloadProject(projects.first());
2014 }
2015 
unloadProject(Project * project)2016 void ProjectExplorerPlugin::unloadProject(Project *project)
2017 {
2018     if (BuildManager::isBuilding(project)) {
2019         QMessageBox box;
2020         QPushButton *closeAnyway = box.addButton(tr("Cancel Build && Unload"), QMessageBox::AcceptRole);
2021         QPushButton *cancelClose = box.addButton(tr("Do Not Unload"), QMessageBox::RejectRole);
2022         box.setDefaultButton(cancelClose);
2023         box.setWindowTitle(tr("Unload Project %1?").arg(project->displayName()));
2024         box.setText(tr("The project %1 is currently being built.").arg(project->displayName()));
2025         box.setInformativeText(tr("Do you want to cancel the build process and unload the project anyway?"));
2026         box.exec();
2027         if (box.clickedButton() != closeAnyway)
2028             return;
2029         BuildManager::cancel();
2030     }
2031 
2032     if (projectExplorerSettings().closeSourceFilesWithProject && !dd->closeAllFilesInProject(project))
2033         return;
2034 
2035     dd->addToRecentProjects(project->projectFilePath().toString(), project->displayName());
2036 
2037     SessionManager::removeProject(project);
2038     dd->updateActions();
2039 }
2040 
closeAllProjects()2041 void ProjectExplorerPluginPrivate::closeAllProjects()
2042 {
2043     if (!EditorManager::closeAllDocuments())
2044         return; // Action has been cancelled
2045 
2046     SessionManager::closeAllProjects();
2047     updateActions();
2048 
2049     ModeManager::activateMode(Core::Constants::MODE_WELCOME);
2050 }
2051 
extensionsInitialized()2052 void ProjectExplorerPlugin::extensionsInitialized()
2053 {
2054     // Register factories for all project managers
2055     QStringList allGlobPatterns;
2056 
2057     const QString filterSeparator = QLatin1String(";;");
2058     QStringList filterStrings;
2059 
2060     dd->m_documentFactory.setOpener([](QString fileName) {
2061         const QFileInfo fi(fileName);
2062         if (fi.isDir())
2063             fileName = FolderNavigationWidget::projectFilesInDirectory(fi.absoluteFilePath()).value(0, fileName);
2064 
2065         OpenProjectResult result = ProjectExplorerPlugin::openProject(fileName);
2066         if (!result)
2067             showOpenProjectError(result);
2068         return nullptr;
2069     });
2070 
2071     dd->m_documentFactory.addMimeType(QStringLiteral("inode/directory"));
2072     for (auto it = dd->m_projectCreators.cbegin(); it != dd->m_projectCreators.cend(); ++it) {
2073         const QString &mimeType = it.key();
2074         dd->m_documentFactory.addMimeType(mimeType);
2075         Utils::MimeType mime = Utils::mimeTypeForName(mimeType);
2076         allGlobPatterns.append(mime.globPatterns());
2077         filterStrings.append(mime.filterString());
2078         dd->m_profileMimeTypes += mimeType;
2079     }
2080 
2081     QString allProjectsFilter = tr("All Projects");
2082     allProjectsFilter += QLatin1String(" (") + allGlobPatterns.join(QLatin1Char(' '))
2083             + QLatin1Char(')');
2084     filterStrings.prepend(allProjectsFilter);
2085     dd->m_projectFilterString = filterStrings.join(filterSeparator);
2086 
2087     BuildManager::extensionsInitialized();
2088 
2089     QSsh::SshSettings::loadSettings(Core::ICore::settings());
2090     const auto searchPathRetriever = [] {
2091         Utils::FilePaths searchPaths = {Core::ICore::libexecPath()};
2092         if (Utils::HostOsInfo::isWindowsHost()) {
2093             const QString gitBinary = Core::ICore::settings()->value("Git/BinaryPath", "git")
2094                     .toString();
2095             const QStringList rawGitSearchPaths = Core::ICore::settings()->value("Git/Path")
2096                     .toString().split(':', Qt::SkipEmptyParts);
2097             const Utils::FilePaths gitSearchPaths = Utils::transform(rawGitSearchPaths,
2098                     [](const QString &rawPath) { return Utils::FilePath::fromString(rawPath); });
2099             const Utils::FilePath fullGitPath = Utils::Environment::systemEnvironment()
2100                     .searchInPath(gitBinary, gitSearchPaths);
2101             if (!fullGitPath.isEmpty()) {
2102                 searchPaths << fullGitPath.parentDir()
2103                             << fullGitPath.parentDir().parentDir() + "/usr/bin";
2104             }
2105         }
2106         return searchPaths;
2107     };
2108     QSsh::SshSettings::setExtraSearchPathRetriever(searchPathRetriever);
2109 
2110     const auto parseIssuesAction = new QAction(tr("Parse Build Output..."), this);
2111     ActionContainer *mtools = ActionManager::actionContainer(Core::Constants::M_TOOLS);
2112     Command * const cmd = ActionManager::registerAction(parseIssuesAction,
2113                                                         "ProjectExplorer.ParseIssuesAction");
2114     connect(parseIssuesAction, &QAction::triggered, this, [] {
2115         ParseIssuesDialog dlg(ICore::dialogParent());
2116         dlg.exec();
2117     });
2118     mtools->addAction(cmd);
2119 
2120     // delay restoring kits until UI is shown for improved perceived startup performance
2121     QTimer::singleShot(0, this, &ProjectExplorerPlugin::restoreKits);
2122 }
2123 
restoreKits()2124 void ProjectExplorerPlugin::restoreKits()
2125 {
2126     dd->determineSessionToRestoreAtStartup();
2127     ExtraAbi::load(); // Load this before Toolchains!
2128     DeviceManager::instance()->load();
2129     ToolChainManager::restoreToolChains();
2130     KitManager::restoreKits();
2131     QTimer::singleShot(0, dd, &ProjectExplorerPluginPrivate::restoreSession); // delay a bit...
2132 }
2133 
updateRunWithoutDeployMenu()2134 void ProjectExplorerPluginPrivate::updateRunWithoutDeployMenu()
2135 {
2136     m_runWithoutDeployAction->setVisible(m_projectExplorerSettings.deployBeforeRun);
2137 }
2138 
aboutToShutdown()2139 ExtensionSystem::IPlugin::ShutdownFlag ProjectExplorerPlugin::aboutToShutdown()
2140 {
2141     disconnect(ModeManager::instance(), &ModeManager::currentModeChanged,
2142                dd, &ProjectExplorerPluginPrivate::currentModeChanged);
2143     ProjectTree::aboutToShutDown();
2144     ToolChainManager::aboutToShutdown();
2145     SessionManager::closeAllProjects();
2146 
2147     dd->m_shuttingDown = true;
2148 
2149     // Attempt to synchronously shutdown all run controls.
2150     // If that fails, fall back to asynchronous shutdown (Debugger run controls
2151     // might shutdown asynchronously).
2152     if (dd->m_activeRunControlCount == 0)
2153         return SynchronousShutdown;
2154 
2155     dd->m_outputPane.closeTabs(AppOutputPane::CloseTabNoPrompt /* No prompt any more */);
2156     dd->m_shutdownWatchDogId = dd->startTimer(10 * 1000); // Make sure we shutdown *somehow*
2157     return AsynchronousShutdown;
2158 }
2159 
showSessionManager()2160 void ProjectExplorerPlugin::showSessionManager()
2161 {
2162     dd->showSessionManager();
2163 }
2164 
openNewProjectDialog()2165 void ProjectExplorerPlugin::openNewProjectDialog()
2166 {
2167     if (!ICore::isNewItemDialogRunning()) {
2168         ICore::showNewItemDialog(tr("New Project", "Title of dialog"),
2169                                  Utils::filtered(IWizardFactory::allWizardFactories(),
2170                                  [](IWizardFactory *f) { return !f->supportedProjectTypes().isEmpty(); }));
2171     } else {
2172         ICore::raiseWindow(ICore::newItemDialog());
2173     }
2174 }
2175 
showSessionManager()2176 void ProjectExplorerPluginPrivate::showSessionManager()
2177 {
2178     SessionManager::save();
2179     SessionDialog sessionDialog(ICore::dialogParent());
2180     sessionDialog.setAutoLoadSession(dd->m_projectExplorerSettings.autorestoreLastSession);
2181     sessionDialog.exec();
2182     dd->m_projectExplorerSettings.autorestoreLastSession = sessionDialog.autoLoadSession();
2183 
2184     updateActions();
2185 
2186     if (ModeManager::currentModeId() == Core::Constants::MODE_WELCOME)
2187         updateWelcomePage();
2188 }
2189 
setStartupProject(Project * project)2190 void ProjectExplorerPluginPrivate::setStartupProject(Project *project)
2191 {
2192     if (!project)
2193         return;
2194     SessionManager::setStartupProject(project);
2195     updateActions();
2196 }
2197 
closeAllFilesInProject(const Project * project)2198 bool ProjectExplorerPluginPrivate::closeAllFilesInProject(const Project *project)
2199 {
2200     QTC_ASSERT(project, return false);
2201     QList<DocumentModel::Entry *> openFiles = DocumentModel::entries();
2202     Utils::erase(openFiles, [project](const DocumentModel::Entry *entry) {
2203         return entry->pinned || !project->isKnownFile(entry->fileName());
2204     });
2205     for (const Project * const otherProject : SessionManager::projects()) {
2206         if (otherProject == project)
2207             continue;
2208         Utils::erase(openFiles, [otherProject](const DocumentModel::Entry *entry) {
2209             return otherProject->isKnownFile(entry->fileName());
2210         });
2211     }
2212     return EditorManager::closeDocuments(openFiles);
2213 }
2214 
savePersistentSettings()2215 void ProjectExplorerPluginPrivate::savePersistentSettings()
2216 {
2217     if (dd->m_shuttingDown)
2218         return;
2219 
2220     if (!SessionManager::loadingSession())  {
2221         for (Project *pro : SessionManager::projects())
2222             pro->saveSettings();
2223 
2224         SessionManager::save();
2225     }
2226 
2227     QtcSettings *s = ICore::settings();
2228     if (SessionManager::isDefaultVirgin()) {
2229         s->remove(Constants::STARTUPSESSION_KEY);
2230     } else {
2231         s->setValue(Constants::STARTUPSESSION_KEY, SessionManager::activeSession());
2232         s->setValue(Constants::LASTSESSION_KEY, SessionManager::activeSession());
2233     }
2234     s->remove(QLatin1String("ProjectExplorer/RecentProjects/Files"));
2235 
2236     QStringList fileNames;
2237     QStringList displayNames;
2238     QList<QPair<QString, QString> >::const_iterator it, end;
2239     end = dd->m_recentProjects.constEnd();
2240     for (it = dd->m_recentProjects.constBegin(); it != end; ++it) {
2241         fileNames << (*it).first;
2242         displayNames << (*it).second;
2243     }
2244 
2245     s->setValueWithDefault(Constants::RECENTPROJECTS_FILE_NAMES_KEY, fileNames);
2246     s->setValueWithDefault(Constants::RECENTPROJECTS_DISPLAY_NAMES_KEY, displayNames);
2247 
2248     static const ProjectExplorerSettings defaultSettings;
2249 
2250     s->setValueWithDefault(Constants::BUILD_BEFORE_DEPLOY_SETTINGS_KEY,
2251                            int(dd->m_projectExplorerSettings.buildBeforeDeploy),
2252                            int(defaultSettings.buildBeforeDeploy));
2253     s->setValueWithDefault(Constants::DEPLOY_BEFORE_RUN_SETTINGS_KEY,
2254                            dd->m_projectExplorerSettings.deployBeforeRun,
2255                            defaultSettings.deployBeforeRun);
2256     s->setValueWithDefault(Constants::SAVE_BEFORE_BUILD_SETTINGS_KEY,
2257                            dd->m_projectExplorerSettings.saveBeforeBuild,
2258                            defaultSettings.saveBeforeBuild);
2259     s->setValueWithDefault(Constants::USE_JOM_SETTINGS_KEY,
2260                            dd->m_projectExplorerSettings.useJom,
2261                            defaultSettings.useJom);
2262     s->setValueWithDefault(Constants::AUTO_RESTORE_SESSION_SETTINGS_KEY,
2263                            dd->m_projectExplorerSettings.autorestoreLastSession,
2264                            defaultSettings.autorestoreLastSession);
2265     s->setValueWithDefault(Constants::ADD_LIBRARY_PATHS_TO_RUN_ENV_SETTINGS_KEY,
2266                            dd->m_projectExplorerSettings.addLibraryPathsToRunEnv,
2267                            defaultSettings.addLibraryPathsToRunEnv);
2268     s->setValueWithDefault(Constants::PROMPT_TO_STOP_RUN_CONTROL_SETTINGS_KEY,
2269                            dd->m_projectExplorerSettings.prompToStopRunControl,
2270                            defaultSettings.prompToStopRunControl);
2271     s->setValueWithDefault(Constants::TERMINAL_MODE_SETTINGS_KEY,
2272                            int(dd->m_projectExplorerSettings.terminalMode),
2273                            int(defaultSettings.terminalMode));
2274     s->setValueWithDefault(Constants::CLOSE_FILES_WITH_PROJECT_SETTINGS_KEY,
2275                            dd->m_projectExplorerSettings.closeSourceFilesWithProject,
2276                            defaultSettings.closeSourceFilesWithProject);
2277     s->setValueWithDefault(Constants::CLEAR_ISSUES_ON_REBUILD_SETTINGS_KEY,
2278                            dd->m_projectExplorerSettings.clearIssuesOnRebuild,
2279                            defaultSettings.clearIssuesOnRebuild);
2280     s->setValueWithDefault(Constants::ABORT_BUILD_ALL_ON_ERROR_SETTINGS_KEY,
2281                            dd->m_projectExplorerSettings.abortBuildAllOnError,
2282                            defaultSettings.abortBuildAllOnError);
2283     s->setValueWithDefault(Constants::LOW_BUILD_PRIORITY_SETTINGS_KEY,
2284                            dd->m_projectExplorerSettings.lowBuildPriority,
2285                            defaultSettings.lowBuildPriority);
2286     s->setValueWithDefault(Constants::AUTO_CREATE_RUN_CONFIGS_SETTINGS_KEY,
2287                            dd->m_projectExplorerSettings.automaticallyCreateRunConfigurations,
2288                            defaultSettings.automaticallyCreateRunConfigurations);
2289     s->setValueWithDefault(Constants::ENVIRONMENT_ID_SETTINGS_KEY,
2290                            dd->m_projectExplorerSettings.environmentId.toByteArray());
2291     s->setValueWithDefault(Constants::STOP_BEFORE_BUILD_SETTINGS_KEY,
2292                            int(dd->m_projectExplorerSettings.stopBeforeBuild),
2293                            int(defaultSettings.stopBeforeBuild));
2294 
2295     dd->m_buildPropertiesSettings.writeSettings(s);
2296 
2297     s->setValueWithDefault(Constants::CUSTOM_PARSER_COUNT_KEY, int(dd->m_customParsers.count()), 0);
2298     for (int i = 0; i < dd->m_customParsers.count(); ++i) {
2299         s->setValue(Constants::CUSTOM_PARSER_PREFIX_KEY + QString::number(i),
2300                     dd->m_customParsers.at(i).toMap());
2301     }
2302 }
2303 
openProjectWelcomePage(const QString & fileName)2304 void ProjectExplorerPlugin::openProjectWelcomePage(const QString &fileName)
2305 {
2306     OpenProjectResult result = openProject(fileName);
2307     if (!result)
2308         showOpenProjectError(result);
2309 }
2310 
openProject(const QString & fileName)2311 ProjectExplorerPlugin::OpenProjectResult ProjectExplorerPlugin::openProject(const QString &fileName)
2312 {
2313     OpenProjectResult result = openProjects(QStringList(fileName));
2314     Project *project = result.project();
2315     if (!project)
2316         return result;
2317     dd->addToRecentProjects(fileName, project->displayName());
2318     SessionManager::setStartupProject(project);
2319     return result;
2320 }
2321 
showOpenProjectError(const OpenProjectResult & result)2322 void ProjectExplorerPlugin::showOpenProjectError(const OpenProjectResult &result)
2323 {
2324     if (result)
2325         return;
2326 
2327     // Potentially both errorMessage and alreadyOpen could contain information
2328     // that should be shown to the user.
2329     // BUT, if Creator opens only a single project, this can lead
2330     // to either
2331     // - No error
2332     // - A errorMessage
2333     // - A single project in alreadyOpen
2334 
2335     // The only place where multiple projects are opened is in session restore
2336     // where the already open case should never happen, thus
2337     // the following code uses those assumptions to make the code simpler
2338 
2339     QString errorMessage = result.errorMessage();
2340     if (!errorMessage.isEmpty()) {
2341         // ignore alreadyOpen
2342         QMessageBox::critical(ICore::dialogParent(), tr("Failed to Open Project"), errorMessage);
2343     } else {
2344         // ignore multiple alreadyOpen
2345         Project *alreadyOpen = result.alreadyOpen().constFirst();
2346         ProjectTree::highlightProject(alreadyOpen,
2347                                       tr("<h3>Project already open</h3>"));
2348     }
2349 }
2350 
appendError(QString & errorString,const QString & error)2351 static void appendError(QString &errorString, const QString &error)
2352 {
2353     if (error.isEmpty())
2354         return;
2355 
2356     if (!errorString.isEmpty())
2357         errorString.append(QLatin1Char('\n'));
2358     errorString.append(error);
2359 }
2360 
openProjects(const QStringList & fileNames)2361 ProjectExplorerPlugin::OpenProjectResult ProjectExplorerPlugin::openProjects(const QStringList &fileNames)
2362 {
2363     QList<Project*> openedPro;
2364     QList<Project *> alreadyOpen;
2365     QString errorString;
2366     foreach (const QString &fileName, fileNames) {
2367         QTC_ASSERT(!fileName.isEmpty(), continue);
2368 
2369         const QFileInfo fi(fileName);
2370         const auto filePath = Utils::FilePath::fromString(fi.absoluteFilePath());
2371         Project *found = Utils::findOrDefault(SessionManager::projects(),
2372                                               Utils::equal(&Project::projectFilePath, filePath));
2373         if (found) {
2374             alreadyOpen.append(found);
2375             SessionManager::reportProjectLoadingProgress();
2376             continue;
2377         }
2378 
2379         Utils::MimeType mt = Utils::mimeTypeForFile(fileName);
2380         if (ProjectManager::canOpenProjectForMimeType(mt)) {
2381             if (!filePath.toFileInfo().isFile()) {
2382                 appendError(errorString,
2383                             tr("Failed opening project \"%1\": Project is not a file.").arg(fileName));
2384             } else if (Project *pro = ProjectManager::openProject(mt, filePath)) {
2385                 QString restoreError;
2386                 Project::RestoreResult restoreResult = pro->restoreSettings(&restoreError);
2387                 if (restoreResult == Project::RestoreResult::Ok) {
2388                     connect(pro, &Project::fileListChanged,
2389                             m_instance, &ProjectExplorerPlugin::fileListChanged);
2390                     SessionManager::addProject(pro);
2391                     openedPro += pro;
2392                 } else {
2393                     if (restoreResult == Project::RestoreResult::Error)
2394                         appendError(errorString, restoreError);
2395                     delete pro;
2396                 }
2397             }
2398         } else {
2399             appendError(errorString, tr("Failed opening project \"%1\": No plugin can open project type \"%2\".")
2400                         .arg(QDir::toNativeSeparators(fileName))
2401                         .arg(mt.name()));
2402         }
2403         if (fileNames.size() > 1)
2404             SessionManager::reportProjectLoadingProgress();
2405     }
2406     dd->updateActions();
2407 
2408     bool switchToProjectsMode = Utils::anyOf(openedPro, &Project::needsConfiguration);
2409 
2410     if (!openedPro.isEmpty()) {
2411         if (switchToProjectsMode)
2412             ModeManager::activateMode(Constants::MODE_SESSION);
2413         else
2414             ModeManager::activateMode(Core::Constants::MODE_EDIT);
2415         ModeManager::setFocusToCurrentMode();
2416     }
2417 
2418     return OpenProjectResult(openedPro, alreadyOpen, errorString);
2419 }
2420 
updateWelcomePage()2421 void ProjectExplorerPluginPrivate::updateWelcomePage()
2422 {
2423     m_welcomePage.reloadWelcomeScreenData();
2424 }
2425 
currentModeChanged(Id mode,Id oldMode)2426 void ProjectExplorerPluginPrivate::currentModeChanged(Id mode, Id oldMode)
2427 {
2428     if (oldMode == Constants::MODE_SESSION) {
2429         // Saving settings directly in a mode change is not a good idea, since the mode change
2430         // can be part of a bigger change. Save settings after that bigger change had a chance to
2431         // complete.
2432         QTimer::singleShot(0, ICore::instance(), [] { ICore::saveSettings(ICore::ModeChanged); });
2433     }
2434     if (mode == Core::Constants::MODE_WELCOME)
2435         updateWelcomePage();
2436 }
2437 
determineSessionToRestoreAtStartup()2438 void ProjectExplorerPluginPrivate::determineSessionToRestoreAtStartup()
2439 {
2440     // Process command line arguments first:
2441     const bool lastSessionArg = m_instance->pluginSpec()->arguments().contains("-lastsession");
2442     m_sessionToRestoreAtStartup = lastSessionArg ? SessionManager::startupSession() : QString();
2443     QStringList arguments = ExtensionSystem::PluginManager::arguments();
2444     if (!lastSessionArg) {
2445         QStringList sessions = SessionManager::sessions();
2446         // We have command line arguments, try to find a session in them
2447         // Default to no session loading
2448         foreach (const QString &arg, arguments) {
2449             if (sessions.contains(arg)) {
2450                 // Session argument
2451                 m_sessionToRestoreAtStartup = arg;
2452                 break;
2453             }
2454         }
2455     }
2456     // Handle settings only after command line arguments:
2457     if (m_sessionToRestoreAtStartup.isEmpty() && m_projectExplorerSettings.autorestoreLastSession)
2458         m_sessionToRestoreAtStartup = SessionManager::startupSession();
2459 
2460     if (!m_sessionToRestoreAtStartup.isEmpty())
2461         ModeManager::activateMode(Core::Constants::MODE_EDIT);
2462 }
2463 
2464 // Return a list of glob patterns for project files ("*.pro", etc), use first, main pattern only.
projectFileGlobs()2465 QStringList ProjectExplorerPlugin::projectFileGlobs()
2466 {
2467     QStringList result;
2468     for (auto it = dd->m_projectCreators.cbegin(); it != dd->m_projectCreators.cend(); ++it) {
2469         Utils::MimeType mimeType = Utils::mimeTypeForName(it.key());
2470         if (mimeType.isValid()) {
2471             const QStringList patterns = mimeType.globPatterns();
2472             if (!patterns.isEmpty())
2473                 result.append(patterns.front());
2474         }
2475     }
2476     return result;
2477 }
2478 
sharedThreadPool()2479 QThreadPool *ProjectExplorerPlugin::sharedThreadPool()
2480 {
2481     return &(dd->m_threadPool);
2482 }
2483 
targetSelector()2484 MiniProjectTargetSelector *ProjectExplorerPlugin::targetSelector()
2485 {
2486     return dd->m_targetSelector;
2487 }
2488 
2489 /*!
2490     This function is connected to the ICore::coreOpened signal.  If
2491     there was no session explicitly loaded, it creates an empty new
2492     default session and puts the list of recent projects and sessions
2493     onto the welcome page.
2494 */
restoreSession()2495 void ProjectExplorerPluginPrivate::restoreSession()
2496 {
2497     // We have command line arguments, try to find a session in them
2498     QStringList arguments = ExtensionSystem::PluginManager::arguments();
2499     if (!dd->m_sessionToRestoreAtStartup.isEmpty() && !arguments.isEmpty())
2500         arguments.removeOne(dd->m_sessionToRestoreAtStartup);
2501 
2502     // Massage the argument list.
2503     // Be smart about directories: If there is a session of that name, load it.
2504     //   Other than that, look for project files in it. The idea is to achieve
2505     //   'Do what I mean' functionality when starting Creator in a directory with
2506     //   the single command line argument '.' and avoid editor warnings about not
2507     //   being able to open directories.
2508     // In addition, convert "filename" "+45" or "filename" ":23" into
2509     //   "filename+45"   and "filename:23".
2510     if (!arguments.isEmpty()) {
2511         const QStringList sessions = SessionManager::sessions();
2512         for (int a = 0; a < arguments.size(); ) {
2513             const QString &arg = arguments.at(a);
2514             const QFileInfo fi(arg);
2515             if (fi.isDir()) {
2516                 const QDir dir(fi.absoluteFilePath());
2517                 // Does the directory name match a session?
2518                 if (dd->m_sessionToRestoreAtStartup.isEmpty()
2519                     && sessions.contains(dir.dirName())) {
2520                     dd->m_sessionToRestoreAtStartup = dir.dirName();
2521                     arguments.removeAt(a);
2522                     continue;
2523                 }
2524             } // Done directories.
2525             // Converts "filename" "+45" or "filename" ":23" into "filename+45" and "filename:23"
2526             if (a && (arg.startsWith(QLatin1Char('+')) || arg.startsWith(QLatin1Char(':')))) {
2527                 arguments[a - 1].append(arguments.takeAt(a));
2528                 continue;
2529             }
2530             ++a;
2531         } // for arguments
2532     } // !arguments.isEmpty()
2533     // Restore latest session or what was passed on the command line
2534 
2535     SessionManager::loadSession(!dd->m_sessionToRestoreAtStartup.isEmpty()
2536                                 ? dd->m_sessionToRestoreAtStartup : QString(), true);
2537 
2538     // update welcome page
2539     connect(ModeManager::instance(), &ModeManager::currentModeChanged,
2540             dd, &ProjectExplorerPluginPrivate::currentModeChanged);
2541     connect(&dd->m_welcomePage, &ProjectWelcomePage::requestProject,
2542             m_instance, &ProjectExplorerPlugin::openProjectWelcomePage);
2543     dd->m_arguments = arguments;
2544     // delay opening projects from the command line even more
2545     QTimer::singleShot(0, m_instance, []() {
2546         ICore::openFiles(dd->m_arguments, ICore::OpenFilesFlags(ICore::CanContainLineAndColumnNumbers | ICore::SwitchMode));
2547         emit m_instance->finishedInitialization();
2548     });
2549     updateActions();
2550 }
2551 
executeRunConfiguration(RunConfiguration * runConfiguration,Utils::Id runMode)2552 void ProjectExplorerPluginPrivate::executeRunConfiguration(RunConfiguration *runConfiguration, Utils::Id runMode)
2553 {
2554     const Tasks runConfigIssues = runConfiguration->checkForIssues();
2555     if (!runConfigIssues.isEmpty()) {
2556         for (const Task &t : runConfigIssues)
2557             TaskHub::addTask(t);
2558         // TODO: Insert an extra task with a "link" to the run settings page?
2559         TaskHub::requestPopup();
2560         return;
2561     }
2562 
2563     auto runControl = new RunControl(runMode);
2564     runControl->setRunConfiguration(runConfiguration);
2565 
2566     // A user needed interaction may have cancelled the run
2567     // (by example asking for a process pid or server url).
2568     if (!runControl->createMainWorker()) {
2569         delete runControl;
2570         return;
2571     }
2572 
2573     startRunControl(runControl);
2574 }
2575 
startRunControl(RunControl * runControl)2576 void ProjectExplorerPlugin::startRunControl(RunControl *runControl)
2577 {
2578     dd->startRunControl(runControl);
2579 }
2580 
showOutputPaneForRunControl(RunControl * runControl)2581 void ProjectExplorerPlugin::showOutputPaneForRunControl(RunControl *runControl)
2582 {
2583     dd->showOutputPaneForRunControl(runControl);
2584 }
2585 
startRunControl(RunControl * runControl)2586 void ProjectExplorerPluginPrivate::startRunControl(RunControl *runControl)
2587 {
2588     m_outputPane.createNewOutputWindow(runControl);
2589     m_outputPane.flash(); // one flash for starting
2590     m_outputPane.showTabFor(runControl);
2591     Utils::Id runMode = runControl->runMode();
2592     const auto popupMode = runMode == Constants::NORMAL_RUN_MODE
2593             ? m_outputPane.settings().runOutputMode
2594             : runMode == Constants::DEBUG_RUN_MODE
2595                 ? m_outputPane.settings().debugOutputMode
2596                 : AppOutputPaneMode::FlashOnOutput;
2597     m_outputPane.setBehaviorOnOutput(runControl, popupMode);
2598     connect(runControl, &QObject::destroyed, this, &ProjectExplorerPluginPrivate::checkForShutdown,
2599             Qt::QueuedConnection);
2600     ++m_activeRunControlCount;
2601     runControl->initiateStart();
2602     doUpdateRunActions();
2603 }
2604 
showOutputPaneForRunControl(RunControl * runControl)2605 void ProjectExplorerPluginPrivate::showOutputPaneForRunControl(RunControl *runControl)
2606 {
2607     m_outputPane.showTabFor(runControl);
2608     m_outputPane.popup(IOutputPane::NoModeSwitch | IOutputPane::WithFocus);
2609 }
2610 
checkForShutdown()2611 void ProjectExplorerPluginPrivate::checkForShutdown()
2612 {
2613     --m_activeRunControlCount;
2614     QTC_ASSERT(m_activeRunControlCount >= 0, m_activeRunControlCount = 0);
2615     if (m_shuttingDown && m_activeRunControlCount == 0)
2616         emit m_instance->asynchronousShutdownFinished();
2617 }
2618 
timerEvent(QTimerEvent * ev)2619 void ProjectExplorerPluginPrivate::timerEvent(QTimerEvent *ev)
2620 {
2621     if (m_shutdownWatchDogId == ev->timerId())
2622         emit m_instance->asynchronousShutdownFinished();
2623 }
2624 
initiateInlineRenaming()2625 void ProjectExplorerPlugin::initiateInlineRenaming()
2626 {
2627     dd->handleRenameFile();
2628 }
2629 
buildQueueFinished(bool success)2630 void ProjectExplorerPluginPrivate::buildQueueFinished(bool success)
2631 {
2632     updateActions();
2633 
2634     bool ignoreErrors = true;
2635     if (!m_delayedRunConfiguration.isNull() && success && BuildManager::getErrorTaskCount() > 0) {
2636         ignoreErrors = QMessageBox::question(ICore::dialogParent(),
2637                                              ProjectExplorerPlugin::tr("Ignore All Errors?"),
2638                                              ProjectExplorerPlugin::tr("Found some build errors in current task.\n"
2639                                                 "Do you want to ignore them?"),
2640                                              QMessageBox::Yes | QMessageBox::No,
2641                                              QMessageBox::No) == QMessageBox::Yes;
2642     }
2643     if (m_delayedRunConfiguration.isNull() && m_shouldHaveRunConfiguration) {
2644         QMessageBox::warning(ICore::dialogParent(),
2645                              ProjectExplorerPlugin::tr("Run Configuration Removed"),
2646                              ProjectExplorerPlugin::tr("The configuration that was supposed to run is no longer "
2647                                 "available."), QMessageBox::Ok);
2648     }
2649 
2650     if (success && ignoreErrors && !m_delayedRunConfiguration.isNull()) {
2651         executeRunConfiguration(m_delayedRunConfiguration.data(), m_runMode);
2652     } else {
2653         if (BuildManager::tasksAvailable())
2654             BuildManager::showTaskWindow();
2655     }
2656     m_delayedRunConfiguration = nullptr;
2657     m_shouldHaveRunConfiguration = false;
2658     m_runMode = Constants::NO_RUN_MODE;
2659     doUpdateRunActions();
2660 }
2661 
recentProjects() const2662 QList<QPair<QString, QString> > ProjectExplorerPluginPrivate::recentProjects() const
2663 {
2664     return Utils::filtered(dd->m_recentProjects, [](const QPair<QString, QString> &p) {
2665         return QFileInfo(p.first).isFile();
2666     });
2667 }
2668 
updateActions()2669 void ProjectExplorerPluginPrivate::updateActions()
2670 {
2671     const Project *const project = SessionManager::startupProject();
2672     const Project *const currentProject = ProjectTree::currentProject(); // for context menu actions
2673 
2674     const QPair<bool, QString> buildActionState = buildSettingsEnabled(project);
2675     const QPair<bool, QString> buildActionContextState = buildSettingsEnabled(currentProject);
2676     const QPair<bool, QString> buildSessionState = buildSettingsEnabledForSession();
2677     const bool isBuilding = BuildManager::isBuilding(project);
2678 
2679     const QString projectName = project ? project->displayName() : QString();
2680     const QString projectNameContextMenu = currentProject ? currentProject->displayName() : QString();
2681 
2682     m_unloadAction->setParameter(projectName);
2683     m_unloadActionContextMenu->setParameter(projectNameContextMenu);
2684     m_closeProjectFilesActionFileMenu->setParameter(projectName);
2685     m_closeProjectFilesActionContextMenu->setParameter(projectNameContextMenu);
2686 
2687     // mode bar build action
2688     QAction * const buildAction = ActionManager::command(Constants::BUILD)->action();
2689     m_modeBarBuildAction->setAction(isBuilding
2690                                     ? ActionManager::command(Constants::CANCELBUILD)->action()
2691                                     : buildAction);
2692     m_modeBarBuildAction->setIcon(isBuilding
2693                                   ? Icons::CANCELBUILD_FLAT.icon()
2694                                   : buildAction->icon());
2695 
2696     const RunConfiguration * const runConfig = project && project->activeTarget()
2697             ? project->activeTarget()->activeRunConfiguration() : nullptr;
2698 
2699     // Normal actions
2700     m_buildAction->setParameter(projectName);
2701     m_buildProjectForAllConfigsAction->setParameter(projectName);
2702     if (runConfig)
2703         m_buildForRunConfigAction->setParameter(runConfig->displayName());
2704 
2705     m_buildAction->setEnabled(buildActionState.first);
2706     m_buildProjectForAllConfigsAction->setEnabled(buildActionState.first);
2707     m_rebuildAction->setEnabled(buildActionState.first);
2708     m_rebuildProjectForAllConfigsAction->setEnabled(buildActionState.first);
2709     m_cleanAction->setEnabled(buildActionState.first);
2710     m_cleanProjectForAllConfigsAction->setEnabled(buildActionState.first);
2711 
2712     // The last condition is there to prevent offering this action for custom run configurations.
2713     m_buildForRunConfigAction->setEnabled(buildActionState.first
2714             && runConfig && project->canBuildProducts()
2715             && !runConfig->buildTargetInfo().projectFilePath.isEmpty());
2716 
2717     m_buildAction->setToolTip(buildActionState.second);
2718     m_buildProjectForAllConfigsAction->setToolTip(buildActionState.second);
2719     m_rebuildAction->setToolTip(buildActionState.second);
2720     m_rebuildProjectForAllConfigsAction->setToolTip(buildActionState.second);
2721     m_cleanAction->setToolTip(buildActionState.second);
2722     m_cleanProjectForAllConfigsAction->setToolTip(buildActionState.second);
2723 
2724     // Context menu actions
2725     m_setStartupProjectAction->setParameter(projectNameContextMenu);
2726     m_setStartupProjectAction->setVisible(currentProject != project);
2727 
2728     const bool hasDependencies = SessionManager::projectOrder(currentProject).size() > 1;
2729     m_buildActionContextMenu->setVisible(hasDependencies);
2730     m_rebuildActionContextMenu->setVisible(hasDependencies);
2731     m_cleanActionContextMenu->setVisible(hasDependencies);
2732 
2733     m_buildActionContextMenu->setEnabled(buildActionContextState.first);
2734     m_rebuildActionContextMenu->setEnabled(buildActionContextState.first);
2735     m_cleanActionContextMenu->setEnabled(buildActionContextState.first);
2736 
2737     m_buildDependenciesActionContextMenu->setEnabled(buildActionContextState.first);
2738     m_rebuildDependenciesActionContextMenu->setEnabled(buildActionContextState.first);
2739     m_cleanDependenciesActionContextMenu->setEnabled(buildActionContextState.first);
2740 
2741     m_buildActionContextMenu->setToolTip(buildActionState.second);
2742     m_rebuildActionContextMenu->setToolTip(buildActionState.second);
2743     m_cleanActionContextMenu->setToolTip(buildActionState.second);
2744 
2745     // build project only
2746     m_buildProjectOnlyAction->setEnabled(buildActionState.first);
2747     m_rebuildProjectOnlyAction->setEnabled(buildActionState.first);
2748     m_cleanProjectOnlyAction->setEnabled(buildActionState.first);
2749 
2750     m_buildProjectOnlyAction->setToolTip(buildActionState.second);
2751     m_rebuildProjectOnlyAction->setToolTip(buildActionState.second);
2752     m_cleanProjectOnlyAction->setToolTip(buildActionState.second);
2753 
2754     // Session actions
2755     m_closeAllProjects->setEnabled(SessionManager::hasProjects());
2756     m_unloadAction->setVisible(SessionManager::projects().size() <= 1);
2757     m_unloadAction->setEnabled(SessionManager::projects().size() == 1);
2758     m_unloadActionContextMenu->setEnabled(SessionManager::hasProjects());
2759     m_closeProjectFilesActionFileMenu->setVisible(SessionManager::projects().size() <= 1);
2760     m_closeProjectFilesActionFileMenu->setEnabled(SessionManager::projects().size() == 1);
2761     m_closeProjectFilesActionContextMenu->setEnabled(SessionManager::hasProjects());
2762 
2763     ActionContainer *aci =
2764         ActionManager::actionContainer(Constants::M_UNLOADPROJECTS);
2765     aci->menu()->menuAction()->setVisible(SessionManager::projects().size() > 1);
2766 
2767     m_buildSessionAction->setEnabled(buildSessionState.first);
2768     m_buildSessionForAllConfigsAction->setEnabled(buildSessionState.first);
2769     m_rebuildSessionAction->setEnabled(buildSessionState.first);
2770     m_rebuildSessionForAllConfigsAction->setEnabled(buildSessionState.first);
2771     m_cleanSessionAction->setEnabled(buildSessionState.first);
2772     m_cleanSessionForAllConfigsAction->setEnabled(buildSessionState.first);
2773 
2774     m_buildSessionAction->setToolTip(buildSessionState.second);
2775     m_buildSessionForAllConfigsAction->setToolTip(buildSessionState.second);
2776     m_rebuildSessionAction->setToolTip(buildSessionState.second);
2777     m_rebuildSessionForAllConfigsAction->setToolTip(buildSessionState.second);
2778     m_cleanSessionAction->setToolTip(buildSessionState.second);
2779     m_cleanSessionForAllConfigsAction->setToolTip(buildSessionState.second);
2780 
2781     m_cancelBuildAction->setEnabled(BuildManager::isBuilding());
2782 
2783     const bool hasProjects = SessionManager::hasProjects();
2784     m_projectSelectorAction->setEnabled(hasProjects);
2785     m_projectSelectorActionMenu->setEnabled(hasProjects);
2786     m_projectSelectorActionQuick->setEnabled(hasProjects);
2787 
2788     updateDeployActions();
2789     updateRunWithoutDeployMenu();
2790 }
2791 
saveModifiedFiles()2792 bool ProjectExplorerPlugin::saveModifiedFiles()
2793 {
2794     QList<IDocument *> documentsToSave = DocumentManager::modifiedDocuments();
2795     if (!documentsToSave.isEmpty()) {
2796         if (dd->m_projectExplorerSettings.saveBeforeBuild) {
2797             bool cancelled = false;
2798             DocumentManager::saveModifiedDocumentsSilently(documentsToSave, &cancelled);
2799             if (cancelled)
2800                 return false;
2801         } else {
2802             bool cancelled = false;
2803             bool alwaysSave = false;
2804             if (!DocumentManager::saveModifiedDocuments(documentsToSave, QString(), &cancelled,
2805                                                         tr("Always save files before build"), &alwaysSave)) {
2806                 if (cancelled)
2807                     return false;
2808             }
2809 
2810             if (alwaysSave)
2811                 dd->m_projectExplorerSettings.saveBeforeBuild = true;
2812         }
2813     }
2814     return true;
2815 }
2816 
2817 //NBS handle case where there is no activeBuildConfiguration
2818 // because someone delete all build configurations
2819 
ProjectExplorerPluginPrivate()2820 ProjectExplorerPluginPrivate::ProjectExplorerPluginPrivate()
2821     : m_allProjectDirectoriesFilter("Files in All Project Directories")
2822 {
2823     m_allProjectDirectoriesFilter.setDisplayName(m_allProjectDirectoriesFilter.id().toString());
2824     // shared with "Files in Any Project":
2825     m_allProjectDirectoriesFilter.setDefaultShortcutString("a");
2826     m_allProjectDirectoriesFilter.setDefaultIncludedByDefault(false); // but not included in default
2827     m_allProjectDirectoriesFilter.setFilters({});
2828     m_allProjectDirectoriesFilter.setIsCustomFilter(false);
2829 }
2830 
runProjectContextMenu()2831 void ProjectExplorerPluginPrivate::runProjectContextMenu()
2832 {
2833     const Node *node = ProjectTree::currentNode();
2834     const ProjectNode *projectNode = node ? node->asProjectNode() : nullptr;
2835     if (projectNode == ProjectTree::currentProject()->rootProjectNode() || !projectNode) {
2836         ProjectExplorerPlugin::runProject(ProjectTree::currentProject(),
2837                                           Constants::NORMAL_RUN_MODE);
2838     } else {
2839         auto act = qobject_cast<QAction *>(sender());
2840         if (!act)
2841             return;
2842         auto *rc = act->data().value<RunConfiguration *>();
2843         if (!rc)
2844             return;
2845         ProjectExplorerPlugin::runRunConfiguration(rc, Constants::NORMAL_RUN_MODE);
2846     }
2847 }
2848 
hasBuildSettings(const Project * pro)2849 static bool hasBuildSettings(const Project *pro)
2850 {
2851     return Utils::anyOf(SessionManager::projectOrder(pro), [](const Project *project) {
2852         return project
2853                 && project->activeTarget()
2854                 && project->activeTarget()->activeBuildConfiguration();
2855     });
2856 }
2857 
subprojectEnabledState(const Project * pro)2858 static QPair<bool, QString> subprojectEnabledState(const Project *pro)
2859 {
2860     QPair<bool, QString> result;
2861     result.first = true;
2862 
2863     const QList<Project *> &projects = SessionManager::projectOrder(pro);
2864     foreach (Project *project, projects) {
2865         if (project && project->activeTarget()
2866             && project->activeTarget()->activeBuildConfiguration()
2867             && !project->activeTarget()->activeBuildConfiguration()->isEnabled()) {
2868             result.first = false;
2869             result.second
2870                 += QCoreApplication::translate("ProjectExplorerPluginPrivate",
2871                                                "Building \"%1\" is disabled: %2<br>")
2872                        .arg(project->displayName(),
2873                             project->activeTarget()->activeBuildConfiguration()->disabledReason());
2874         }
2875     }
2876 
2877     return result;
2878 }
2879 
buildSettingsEnabled(const Project * pro)2880 QPair<bool, QString> ProjectExplorerPluginPrivate::buildSettingsEnabled(const Project *pro)
2881 {
2882     QPair<bool, QString> result;
2883     result.first = true;
2884     if (!pro) {
2885         result.first = false;
2886         result.second = tr("No project loaded.");
2887     } else if (BuildManager::isBuilding(pro)) {
2888         result.first = false;
2889         result.second = tr("Currently building the active project.");
2890     } else if (pro->needsConfiguration()) {
2891         result.first = false;
2892         result.second = tr("The project %1 is not configured.").arg(pro->displayName());
2893     } else if (!hasBuildSettings(pro)) {
2894         result.first = false;
2895         result.second = tr("Project has no build settings.");
2896     } else {
2897         result = subprojectEnabledState(pro);
2898     }
2899     return result;
2900 }
2901 
buildSettingsEnabledForSession()2902 QPair<bool, QString> ProjectExplorerPluginPrivate::buildSettingsEnabledForSession()
2903 {
2904     QPair<bool, QString> result;
2905     result.first = true;
2906     if (!SessionManager::hasProjects()) {
2907         result.first = false;
2908         result.second = tr("No project loaded.");
2909     } else if (BuildManager::isBuilding()) {
2910         result.first = false;
2911         result.second = tr("A build is in progress.");
2912     } else if (!hasBuildSettings(nullptr)) {
2913         result.first = false;
2914         result.second = tr("Project has no build settings.");
2915     } else {
2916         result = subprojectEnabledState(nullptr);
2917     }
2918     return result;
2919 }
2920 
coreAboutToClose()2921 bool ProjectExplorerPlugin::coreAboutToClose()
2922 {
2923     if (!m_instance)
2924         return true;
2925     if (BuildManager::isBuilding()) {
2926         QMessageBox box;
2927         QPushButton *closeAnyway = box.addButton(tr("Cancel Build && Close"), QMessageBox::AcceptRole);
2928         QPushButton *cancelClose = box.addButton(tr("Do Not Close"), QMessageBox::RejectRole);
2929         box.setDefaultButton(cancelClose);
2930         box.setWindowTitle(tr("Close %1?").arg(Core::Constants::IDE_DISPLAY_NAME));
2931         box.setText(tr("A project is currently being built."));
2932         box.setInformativeText(tr("Do you want to cancel the build process and close %1 anyway?")
2933                                .arg(Core::Constants::IDE_DISPLAY_NAME));
2934         box.exec();
2935         if (box.clickedButton() != closeAnyway)
2936             return false;
2937     }
2938     return dd->m_outputPane.aboutToClose();
2939 }
2940 
handleCommandLineArguments(const QStringList & arguments)2941 void ProjectExplorerPlugin::handleCommandLineArguments(const QStringList &arguments)
2942 {
2943     CustomWizard::setVerbose(arguments.count(QLatin1String("-customwizard-verbose")));
2944     JsonWizardFactory::setVerbose(arguments.count(QLatin1String("-customwizard-verbose")));
2945 
2946     const int kitForBinaryOptionIndex = arguments.indexOf("-ensure-kit-for-binary");
2947     if (kitForBinaryOptionIndex != -1) {
2948         if (kitForBinaryOptionIndex == arguments.count() - 1) {
2949             qWarning() << "The \"-ensure-kit-for-binary\" option requires a file path argument.";
2950         } else {
2951             const Utils::FilePath binary =
2952                     Utils::FilePath::fromString(arguments.at(kitForBinaryOptionIndex + 1));
2953             if (binary.isEmpty() || !binary.exists())
2954                 qWarning() << QString("No such file \"%1\".").arg(binary.toUserOutput());
2955             else
2956                 KitManager::setBinaryForKit(binary);
2957         }
2958     }
2959 }
2960 
hasDeploySettings(Project * pro)2961 static bool hasDeploySettings(Project *pro)
2962 {
2963     return Utils::anyOf(SessionManager::projectOrder(pro), [](Project *project) {
2964         return project->activeTarget()
2965                 && project->activeTarget()->activeDeployConfiguration();
2966     });
2967 }
2968 
runProject(Project * pro,Utils::Id mode,const bool forceSkipDeploy)2969 void ProjectExplorerPlugin::runProject(Project *pro, Utils::Id mode, const bool forceSkipDeploy)
2970 {
2971     if (!pro)
2972         return;
2973 
2974     if (Target *target = pro->activeTarget())
2975         if (RunConfiguration *rc = target->activeRunConfiguration())
2976             runRunConfiguration(rc, mode, forceSkipDeploy);
2977 }
2978 
runStartupProject(Utils::Id runMode,bool forceSkipDeploy)2979 void ProjectExplorerPlugin::runStartupProject(Utils::Id runMode, bool forceSkipDeploy)
2980 {
2981     runProject(SessionManager::startupProject(), runMode, forceSkipDeploy);
2982 }
2983 
runRunConfiguration(RunConfiguration * rc,Utils::Id runMode,const bool forceSkipDeploy)2984 void ProjectExplorerPlugin::runRunConfiguration(RunConfiguration *rc,
2985                                                 Utils::Id runMode,
2986                                                 const bool forceSkipDeploy)
2987 {
2988     if (!rc->isEnabled())
2989         return;
2990     const auto delay = [rc, runMode] {
2991         dd->m_runMode = runMode;
2992         dd->m_delayedRunConfiguration = rc;
2993         dd->m_shouldHaveRunConfiguration = true;
2994     };
2995     const BuildForRunConfigStatus buildStatus = forceSkipDeploy
2996             ? BuildManager::isBuilding(rc->project())
2997                 ? BuildForRunConfigStatus::Building : BuildForRunConfigStatus::NotBuilding
2998             : BuildManager::potentiallyBuildForRunConfig(rc);
2999     switch (buildStatus) {
3000     case BuildForRunConfigStatus::BuildFailed:
3001         return;
3002     case BuildForRunConfigStatus::Building:
3003         QTC_ASSERT(dd->m_runMode == Constants::NO_RUN_MODE, return);
3004         delay();
3005         break;
3006     case BuildForRunConfigStatus::NotBuilding:
3007         if (rc->isEnabled())
3008             dd->executeRunConfiguration(rc, runMode);
3009         else
3010             delay();
3011         break;
3012     }
3013 
3014     dd->doUpdateRunActions();
3015 }
3016 
runningRunControlProcesses()3017 QList<QPair<Runnable, Utils::ProcessHandle>> ProjectExplorerPlugin::runningRunControlProcesses()
3018 {
3019     QList<QPair<Runnable, Utils::ProcessHandle>> processes;
3020     foreach (RunControl *rc, allRunControls()) {
3021         if (rc->isRunning())
3022             processes << qMakePair(rc->runnable(), rc->applicationProcessHandle());
3023     }
3024     return processes;
3025 }
3026 
allRunControls()3027 QList<RunControl *> ProjectExplorerPlugin::allRunControls()
3028 {
3029     return dd->m_outputPane.allRunControls();
3030 }
3031 
projectAdded(Project * pro)3032 void ProjectExplorerPluginPrivate::projectAdded(Project *pro)
3033 {
3034     Q_UNUSED(pro)
3035     m_projectsMode.setEnabled(true);
3036 }
3037 
projectRemoved(Project * pro)3038 void ProjectExplorerPluginPrivate::projectRemoved(Project *pro)
3039 {
3040     Q_UNUSED(pro)
3041     m_projectsMode.setEnabled(SessionManager::hasProjects());
3042 }
3043 
projectDisplayNameChanged(Project * pro)3044 void ProjectExplorerPluginPrivate::projectDisplayNameChanged(Project *pro)
3045 {
3046     addToRecentProjects(pro->projectFilePath().toString(), pro->displayName());
3047     updateActions();
3048 }
3049 
updateDeployActions()3050 void ProjectExplorerPluginPrivate::updateDeployActions()
3051 {
3052     Project *project = SessionManager::startupProject();
3053 
3054     bool enableDeployActions = project
3055             && !BuildManager::isBuilding(project)
3056             && hasDeploySettings(project);
3057     Project *currentProject = ProjectTree::currentProject();
3058     bool enableDeployActionsContextMenu = currentProject
3059                               && !BuildManager::isBuilding(currentProject)
3060                               && hasDeploySettings(currentProject);
3061 
3062     if (m_projectExplorerSettings.buildBeforeDeploy != BuildBeforeRunMode::Off) {
3063         if (hasBuildSettings(project)
3064                 && !buildSettingsEnabled(project).first)
3065             enableDeployActions = false;
3066         if (hasBuildSettings(currentProject)
3067                 && !buildSettingsEnabled(currentProject).first)
3068             enableDeployActionsContextMenu = false;
3069     }
3070 
3071     const QString projectName = project ? project->displayName() : QString();
3072     bool hasProjects = SessionManager::hasProjects();
3073 
3074     m_deployAction->setEnabled(enableDeployActions);
3075 
3076     m_deployActionContextMenu->setEnabled(enableDeployActionsContextMenu);
3077 
3078     m_deployProjectOnlyAction->setEnabled(enableDeployActions);
3079 
3080     bool enableDeploySessionAction = true;
3081     if (m_projectExplorerSettings.buildBeforeDeploy != BuildBeforeRunMode::Off) {
3082         auto hasDisabledBuildConfiguration = [](Project *project) {
3083             return project && project->activeTarget()
3084                     && project->activeTarget()->activeBuildConfiguration()
3085                     && !project->activeTarget()->activeBuildConfiguration()->isEnabled();
3086         };
3087 
3088         if (Utils::anyOf(SessionManager::projectOrder(nullptr), hasDisabledBuildConfiguration))
3089             enableDeploySessionAction = false;
3090     }
3091     if (!hasProjects || !hasDeploySettings(nullptr) || BuildManager::isBuilding())
3092         enableDeploySessionAction = false;
3093     m_deploySessionAction->setEnabled(enableDeploySessionAction);
3094 
3095     doUpdateRunActions();
3096 }
3097 
canRunStartupProject(Utils::Id runMode,QString * whyNot)3098 bool ProjectExplorerPlugin::canRunStartupProject(Utils::Id runMode, QString *whyNot)
3099 {
3100     Project *project = SessionManager::startupProject();
3101     if (!project) {
3102         if (whyNot)
3103             *whyNot = tr("No active project.");
3104         return false;
3105     }
3106 
3107     if (project->needsConfiguration()) {
3108         if (whyNot)
3109             *whyNot = tr("The project \"%1\" is not configured.").arg(project->displayName());
3110         return false;
3111     }
3112 
3113     Target *target = project->activeTarget();
3114     if (!target) {
3115         if (whyNot)
3116             *whyNot = tr("The project \"%1\" has no active kit.").arg(project->displayName());
3117         return false;
3118     }
3119 
3120     RunConfiguration *activeRC = target->activeRunConfiguration();
3121     if (!activeRC) {
3122         if (whyNot)
3123             *whyNot = tr("The kit \"%1\" for the project \"%2\" has no active run configuration.")
3124                 .arg(target->displayName(), project->displayName());
3125         return false;
3126     }
3127 
3128     if (!activeRC->isEnabled()) {
3129         if (whyNot)
3130             *whyNot = activeRC->disabledReason();
3131         return false;
3132     }
3133 
3134     if (dd->m_projectExplorerSettings.buildBeforeDeploy != BuildBeforeRunMode::Off
3135             && dd->m_projectExplorerSettings.deployBeforeRun
3136             && !BuildManager::isBuilding(project)
3137             && hasBuildSettings(project)) {
3138         QPair<bool, QString> buildState = dd->buildSettingsEnabled(project);
3139         if (!buildState.first) {
3140             if (whyNot)
3141                 *whyNot = buildState.second;
3142             return false;
3143         }
3144 
3145         if (BuildManager::isBuilding()) {
3146             if (whyNot)
3147                 *whyNot = tr("A build is still in progress.");
3148              return false;
3149         }
3150     }
3151 
3152     // shouldn't actually be shown to the user...
3153     if (!RunControl::canRun(runMode,
3154                             DeviceTypeKitAspect::deviceTypeId(target->kit()),
3155                             activeRC->id())) {
3156         if (whyNot)
3157             *whyNot = tr("Cannot run \"%1\".").arg(activeRC->displayName());
3158         return false;
3159     }
3160 
3161     if (dd->m_delayedRunConfiguration && dd->m_delayedRunConfiguration->project() == project) {
3162         if (whyNot)
3163             *whyNot = tr("A run action is already scheduled for the active project.");
3164         return false;
3165     }
3166 
3167     return true;
3168 }
3169 
doUpdateRunActions()3170 void ProjectExplorerPluginPrivate::doUpdateRunActions()
3171 {
3172     QString whyNot;
3173     const bool state = ProjectExplorerPlugin::canRunStartupProject(Constants::NORMAL_RUN_MODE, &whyNot);
3174     m_runAction->setEnabled(state);
3175     m_runAction->setToolTip(whyNot);
3176     m_runWithoutDeployAction->setEnabled(state);
3177 
3178     emit m_instance->runActionsUpdated();
3179 }
3180 
addToRecentProjects(const QString & fileName,const QString & displayName)3181 void ProjectExplorerPluginPrivate::addToRecentProjects(const QString &fileName, const QString &displayName)
3182 {
3183     if (fileName.isEmpty())
3184         return;
3185     QString prettyFileName(QDir::toNativeSeparators(fileName));
3186 
3187     QList<QPair<QString, QString> >::iterator it;
3188     for (it = m_recentProjects.begin(); it != m_recentProjects.end();)
3189         if ((*it).first == prettyFileName)
3190             it = m_recentProjects.erase(it);
3191         else
3192             ++it;
3193 
3194     if (m_recentProjects.count() > m_maxRecentProjects)
3195         m_recentProjects.removeLast();
3196     m_recentProjects.prepend(qMakePair(prettyFileName, displayName));
3197     QFileInfo fi(prettyFileName);
3198     m_lastOpenDirectory = fi.absolutePath();
3199     emit m_instance->recentProjectsChanged();
3200 }
3201 
updateUnloadProjectMenu()3202 void ProjectExplorerPluginPrivate::updateUnloadProjectMenu()
3203 {
3204     ActionContainer *aci = ActionManager::actionContainer(Constants::M_UNLOADPROJECTS);
3205     QMenu *menu = aci->menu();
3206     menu->clear();
3207     for (Project *project : SessionManager::projects()) {
3208         QAction *action = menu->addAction(tr("Close Project \"%1\"").arg(project->displayName()));
3209         connect(action, &QAction::triggered,
3210                 [project] { ProjectExplorerPlugin::unloadProject(project); } );
3211     }
3212 }
3213 
updateRecentProjectMenu()3214 void ProjectExplorerPluginPrivate::updateRecentProjectMenu()
3215 {
3216     using StringPairListConstIterator = QList<QPair<QString, QString> >::const_iterator;
3217     ActionContainer *aci = ActionManager::actionContainer(Constants::M_RECENTPROJECTS);
3218     QMenu *menu = aci->menu();
3219     menu->clear();
3220 
3221     int acceleratorKey = 1;
3222     auto projects = recentProjects();
3223     //projects (ignore sessions, they used to be in this list)
3224     const StringPairListConstIterator end = projects.constEnd();
3225     for (StringPairListConstIterator it = projects.constBegin(); it != end; ++it, ++acceleratorKey) {
3226         const QString fileName = it->first;
3227         if (fileName.endsWith(QLatin1String(".qws")))
3228             continue;
3229 
3230         const QString actionText = ActionManager::withNumberAccelerator(
3231                     Utils::withTildeHomePath(fileName), acceleratorKey);
3232         QAction *action = menu->addAction(actionText);
3233         connect(action, &QAction::triggered, this, [this, fileName] {
3234             openRecentProject(fileName);
3235         });
3236     }
3237     const bool hasRecentProjects = !projects.empty();
3238     menu->setEnabled(hasRecentProjects);
3239 
3240     // add the Clear Menu item
3241     if (hasRecentProjects) {
3242         menu->addSeparator();
3243         QAction *action = menu->addAction(QCoreApplication::translate(
3244                                           "Core", Core::Constants::TR_CLEAR_MENU));
3245         connect(action, &QAction::triggered,
3246                 this, &ProjectExplorerPluginPrivate::clearRecentProjects);
3247     }
3248     emit m_instance->recentProjectsChanged();
3249 }
3250 
clearRecentProjects()3251 void ProjectExplorerPluginPrivate::clearRecentProjects()
3252 {
3253     m_recentProjects.clear();
3254     updateWelcomePage();
3255 }
3256 
openRecentProject(const QString & fileName)3257 void ProjectExplorerPluginPrivate::openRecentProject(const QString &fileName)
3258 {
3259     if (!fileName.isEmpty()) {
3260         ProjectExplorerPlugin::OpenProjectResult result
3261                 = ProjectExplorerPlugin::openProject(fileName);
3262         if (!result)
3263             ProjectExplorerPlugin::showOpenProjectError(result);
3264     }
3265 }
3266 
removeFromRecentProjects(const QString & fileName,const QString & displayName)3267 void ProjectExplorerPluginPrivate::removeFromRecentProjects(const QString &fileName,
3268                                                             const QString &displayName)
3269 {
3270     QTC_ASSERT(!fileName.isEmpty() && !displayName.isEmpty(), return);
3271     QTC_CHECK(m_recentProjects.removeOne(QPair<QString, QString>(fileName, displayName)));
3272 }
3273 
invalidateProject(Project * project)3274 void ProjectExplorerPluginPrivate::invalidateProject(Project *project)
3275 {
3276     disconnect(project, &Project::fileListChanged,
3277                m_instance, &ProjectExplorerPlugin::fileListChanged);
3278     updateActions();
3279 }
3280 
updateContextMenuActions(Node * currentNode)3281 void ProjectExplorerPluginPrivate::updateContextMenuActions(Node *currentNode)
3282 {
3283     m_addExistingFilesAction->setEnabled(false);
3284     m_addExistingDirectoryAction->setEnabled(false);
3285     m_addNewFileAction->setEnabled(false);
3286     m_addNewSubprojectAction->setEnabled(false);
3287     m_addExistingProjectsAction->setEnabled(false);
3288     m_removeProjectAction->setEnabled(false);
3289     m_removeFileAction->setEnabled(false);
3290     m_duplicateFileAction->setEnabled(false);
3291     m_deleteFileAction->setEnabled(false);
3292     m_renameFileAction->setEnabled(false);
3293     m_diffFileAction->setEnabled(false);
3294 
3295     m_addExistingFilesAction->setVisible(true);
3296     m_addExistingDirectoryAction->setVisible(true);
3297     m_addNewFileAction->setVisible(true);
3298     m_addNewSubprojectAction->setVisible(true);
3299     m_addExistingProjectsAction->setVisible(true);
3300     m_removeProjectAction->setVisible(true);
3301     m_removeFileAction->setVisible(true);
3302     m_duplicateFileAction->setVisible(false);
3303     m_deleteFileAction->setVisible(true);
3304     m_runActionContextMenu->setVisible(false);
3305     m_diffFileAction->setVisible(DiffService::instance());
3306 
3307     m_openTerminalHere->setVisible(true);
3308     m_openTerminalHereBuildEnv->setVisible(false);
3309     m_openTerminalHereRunEnv->setVisible(false);
3310 
3311     m_showInGraphicalShell->setVisible(true);
3312     m_searchOnFileSystem->setVisible(true);
3313 
3314     ActionContainer *runMenu = ActionManager::actionContainer(Constants::RUNMENUCONTEXTMENU);
3315     runMenu->menu()->clear();
3316     runMenu->menu()->menuAction()->setVisible(false);
3317 
3318     if (currentNode && currentNode->managingProject()) {
3319         ProjectNode *pn;
3320         if (const ContainerNode *cn = currentNode->asContainerNode())
3321             pn = cn->rootProjectNode();
3322         else
3323             pn = const_cast<ProjectNode*>(currentNode->asProjectNode());
3324 
3325         Project *project = ProjectTree::currentProject();
3326         m_openTerminalHereBuildEnv->setVisible(bool(buildEnv(project)));
3327         m_openTerminalHereRunEnv->setVisible(canOpenTerminalWithRunEnv(project, pn));
3328 
3329         if (pn && project) {
3330             if (pn == project->rootProjectNode()) {
3331                 m_runActionContextMenu->setVisible(true);
3332             } else {
3333                 QList<RunConfiguration *> runConfigs;
3334                 if (Target *t = project->activeTarget()) {
3335                     const QString buildKey = pn->buildKey();
3336                     for (RunConfiguration *rc : t->runConfigurations()) {
3337                         if (rc->buildKey() == buildKey)
3338                             runConfigs.append(rc);
3339                     }
3340                 }
3341                 if (runConfigs.count() == 1) {
3342                     m_runActionContextMenu->setVisible(true);
3343                     m_runActionContextMenu->setData(QVariant::fromValue(runConfigs.first()));
3344                 } else if (runConfigs.count() > 1) {
3345                     runMenu->menu()->menuAction()->setVisible(true);
3346                     foreach (RunConfiguration *rc, runConfigs) {
3347                         auto *act = new QAction(runMenu->menu());
3348                         act->setData(QVariant::fromValue(rc));
3349                         act->setText(tr("Run %1").arg(rc->displayName()));
3350                         runMenu->menu()->addAction(act);
3351                         connect(act, &QAction::triggered,
3352                                 this, &ProjectExplorerPluginPrivate::runProjectContextMenu);
3353                     }
3354                 }
3355             }
3356         }
3357 
3358         auto supports = [currentNode](ProjectAction action) {
3359             return currentNode->supportsAction(action, currentNode);
3360         };
3361 
3362         bool canEditProject = true;
3363         if (project && project->activeTarget()) {
3364             const BuildSystem * const bs = project->activeTarget()->buildSystem();
3365             if (bs->isParsing() || bs->isWaitingForParse())
3366                 canEditProject = false;
3367         }
3368         if (currentNode->asFolderNode()) {
3369             // Also handles ProjectNode
3370             m_addNewFileAction->setEnabled(canEditProject && supports(AddNewFile)
3371                                               && !ICore::isNewItemDialogRunning());
3372             m_addNewSubprojectAction->setEnabled(canEditProject && currentNode->isProjectNodeType()
3373                                                     && supports(AddSubProject)
3374                                                     && !ICore::isNewItemDialogRunning());
3375             m_addExistingProjectsAction->setEnabled(canEditProject
3376                                                     && currentNode->isProjectNodeType()
3377                                                     && supports(AddExistingProject));
3378             m_removeProjectAction->setEnabled(canEditProject && currentNode->isProjectNodeType()
3379                                                     && supports(RemoveSubProject));
3380             m_addExistingFilesAction->setEnabled(canEditProject && supports(AddExistingFile));
3381             m_addExistingDirectoryAction->setEnabled(canEditProject
3382                                                      && supports(AddExistingDirectory));
3383             m_renameFileAction->setEnabled(canEditProject && supports(Rename));
3384         } else if (auto fileNode = currentNode->asFileNode()) {
3385             // Enable and show remove / delete in magic ways:
3386             // If both are disabled show Remove
3387             // If both are enabled show both (can't happen atm)
3388             // If only removeFile is enabled only show it
3389             // If only deleteFile is enable only show it
3390             bool isTypeProject = fileNode->fileType() == FileType::Project;
3391             bool enableRemove = canEditProject && !isTypeProject && supports(RemoveFile);
3392             m_removeFileAction->setEnabled(enableRemove);
3393             bool enableDelete = canEditProject && !isTypeProject && supports(EraseFile);
3394             m_deleteFileAction->setEnabled(enableDelete);
3395             m_deleteFileAction->setVisible(enableDelete);
3396 
3397             m_removeFileAction->setVisible(!enableDelete || enableRemove);
3398             m_renameFileAction->setEnabled(canEditProject && !isTypeProject && supports(Rename));
3399             const bool currentNodeIsTextFile = isTextFile(
3400                         currentNode->filePath().toString());
3401             m_diffFileAction->setEnabled(DiffService::instance()
3402                         && currentNodeIsTextFile && TextEditor::TextDocument::currentTextDocument());
3403 
3404             const bool canDuplicate = canEditProject && supports(AddNewFile)
3405                     && currentNode->asFileNode()->fileType() != FileType::Project;
3406             m_duplicateFileAction->setVisible(canDuplicate);
3407             m_duplicateFileAction->setEnabled(canDuplicate);
3408 
3409             EditorManager::populateOpenWithMenu(m_openWithMenu,
3410                                                 currentNode->filePath().toString());
3411         }
3412 
3413         if (supports(HidePathActions)) {
3414             m_openTerminalHere->setVisible(false);
3415             m_showInGraphicalShell->setVisible(false);
3416             m_searchOnFileSystem->setVisible(false);
3417         }
3418 
3419         if (supports(HideFileActions)) {
3420             m_deleteFileAction->setVisible(false);
3421             m_removeFileAction->setVisible(false);
3422         }
3423 
3424         if (supports(HideFolderActions)) {
3425             m_addNewFileAction->setVisible(false);
3426             m_addNewSubprojectAction->setVisible(false);
3427             m_addExistingProjectsAction->setVisible(false);
3428             m_removeProjectAction->setVisible(false);
3429             m_addExistingFilesAction->setVisible(false);
3430             m_addExistingDirectoryAction->setVisible(false);
3431         }
3432     }
3433 }
3434 
updateLocationSubMenus()3435 void ProjectExplorerPluginPrivate::updateLocationSubMenus()
3436 {
3437     static QList<QAction *> actions;
3438     qDeleteAll(actions); // This will also remove these actions from the menus!
3439     actions.clear();
3440 
3441     ActionContainer *projectMenuContainer
3442             = ActionManager::actionContainer(Constants::PROJECT_OPEN_LOCATIONS_CONTEXT_MENU);
3443     QMenu *projectMenu = projectMenuContainer->menu();
3444     QTC_CHECK(projectMenu->actions().isEmpty());
3445 
3446     ActionContainer *folderMenuContainer
3447             = ActionManager::actionContainer(Constants::FOLDER_OPEN_LOCATIONS_CONTEXT_MENU);
3448     QMenu *folderMenu = folderMenuContainer->menu();
3449     QTC_CHECK(folderMenu->actions().isEmpty());
3450 
3451     const FolderNode *const fn
3452             = ProjectTree::currentNode() ? ProjectTree::currentNode()->asFolderNode() : nullptr;
3453     const QVector<FolderNode::LocationInfo> locations = fn ? fn->locationInfo()
3454                                                            : QVector<FolderNode::LocationInfo>();
3455 
3456     const bool isVisible = !locations.isEmpty();
3457     projectMenu->menuAction()->setVisible(isVisible);
3458     folderMenu->menuAction()->setVisible(isVisible);
3459 
3460     if (!isVisible)
3461         return;
3462 
3463     unsigned int lastPriority = 0;
3464     for (const FolderNode::LocationInfo &li : locations) {
3465         if (li.priority != lastPriority) {
3466             projectMenu->addSeparator();
3467             folderMenu->addSeparator();
3468             lastPriority = li.priority;
3469         }
3470         const int line = li.line;
3471         const Utils::FilePath path = li.path;
3472         QString displayName = fn->filePath() == li.path
3473                                   ? li.displayName
3474                                   : tr("%1 in %2").arg(li.displayName).arg(li.path.toUserOutput());
3475         auto *action = new QAction(displayName, nullptr);
3476         connect(action, &QAction::triggered, this, [line, path]() {
3477             Core::EditorManager::openEditorAt(Link(path, line));
3478         });
3479 
3480         projectMenu->addAction(action);
3481         folderMenu->addAction(action);
3482 
3483         actions.append(action);
3484     }
3485 }
3486 
addNewFile()3487 void ProjectExplorerPluginPrivate::addNewFile()
3488 {
3489     Node *currentNode = ProjectTree::currentNode();
3490     QTC_ASSERT(currentNode, return);
3491     QString location = currentNode->directory();
3492 
3493     QVariantMap map;
3494     // store void pointer to avoid QVariant to use qobject_cast, which might core-dump when trying
3495     // to access meta data on an object that get deleted in the meantime:
3496     map.insert(QLatin1String(Constants::PREFERRED_PROJECT_NODE), QVariant::fromValue(static_cast<void *>(currentNode)));
3497     map.insert(Constants::PREFERRED_PROJECT_NODE_PATH, currentNode->filePath().toString());
3498     if (Project *p = ProjectTree::currentProject()) {
3499         const QStringList profileIds = Utils::transform(p->targets(), [](const Target *t) {
3500             return t->id().toString();
3501         });
3502         map.insert(QLatin1String(Constants::PROJECT_KIT_IDS), profileIds);
3503         map.insert(Constants::PROJECT_POINTER, QVariant::fromValue(static_cast<void *>(p)));
3504     }
3505     ICore::showNewItemDialog(ProjectExplorerPlugin::tr("New File", "Title of dialog"),
3506                              Utils::filtered(IWizardFactory::allWizardFactories(),
3507                                              [](IWizardFactory *f) {
3508                                  return f->supportedProjectTypes().isEmpty();
3509                              }),
3510                              location, map);
3511 }
3512 
addNewSubproject()3513 void ProjectExplorerPluginPrivate::addNewSubproject()
3514 {
3515     Node* currentNode = ProjectTree::currentNode();
3516     QTC_ASSERT(currentNode, return);
3517     QString location = currentNode->directory();
3518 
3519     if (currentNode->isProjectNodeType()
3520             && currentNode->supportsAction(AddSubProject, currentNode)) {
3521         QVariantMap map;
3522         map.insert(QLatin1String(Constants::PREFERRED_PROJECT_NODE), QVariant::fromValue(currentNode));
3523         Project *project = ProjectTree::currentProject();
3524         Utils::Id projectType;
3525         if (project) {
3526             const QStringList profileIds = Utils::transform(ProjectTree::currentProject()->targets(),
3527                                                             [](const Target *t) {
3528                                                                 return t->id().toString();
3529                                                             });
3530             map.insert(QLatin1String(Constants::PROJECT_KIT_IDS), profileIds);
3531             projectType = project->id();
3532         }
3533 
3534         ICore::showNewItemDialog(tr("New Subproject", "Title of dialog"),
3535                                  Utils::filtered(IWizardFactory::allWizardFactories(),
3536                                                  [projectType](IWizardFactory *f) {
3537                                                      return projectType.isValid() ? f->supportedProjectTypes().contains(projectType)
3538                                                                                   : !f->supportedProjectTypes().isEmpty(); }),
3539                                  location, map);
3540     }
3541 }
3542 
addExistingProjects()3543 void ProjectExplorerPluginPrivate::addExistingProjects()
3544 {
3545     Node * const currentNode = ProjectTree::currentNode();
3546     if (!currentNode)
3547         return;
3548     ProjectNode *projectNode = currentNode->asProjectNode();
3549     if (!projectNode && currentNode->asContainerNode())
3550         projectNode = currentNode->asContainerNode()->rootProjectNode();
3551     QTC_ASSERT(projectNode, return);
3552     const QString dir = currentNode->directory();
3553     QStringList subProjectFilePaths = QFileDialog::getOpenFileNames(
3554                 ICore::dialogParent(), tr("Choose Project File"), dir,
3555                 projectNode->subProjectFileNamePatterns().join(";;"));
3556     if (!ProjectTree::hasNode(projectNode))
3557         return;
3558     const QList<Node *> childNodes = projectNode->nodes();
3559     Utils::erase(subProjectFilePaths, [childNodes](const QString &filePath) {
3560         return Utils::anyOf(childNodes, [filePath](const Node *n) {
3561             return n->filePath().toString() == filePath;
3562         });
3563     });
3564     if (subProjectFilePaths.empty())
3565         return;
3566     QStringList failedProjects;
3567     QStringList addedProjects;
3568     for (const QString &filePath : qAsConst(subProjectFilePaths)) {
3569         if (projectNode->addSubProject(filePath))
3570             addedProjects << filePath;
3571         else
3572             failedProjects << filePath;
3573     }
3574     if (!failedProjects.empty()) {
3575         const QString message = tr("The following subprojects could not be added to project "
3576                                    "\"%1\":").arg(projectNode->managingProject()->displayName());
3577         QMessageBox::warning(ICore::dialogParent(), tr("Adding Subproject Failed"),
3578                              message + "\n  " + failedProjects.join("\n  "));
3579         return;
3580     }
3581     VcsManager::promptToAdd(dir, addedProjects);
3582 }
3583 
handleAddExistingFiles()3584 void ProjectExplorerPluginPrivate::handleAddExistingFiles()
3585 {
3586     Node *node = ProjectTree::currentNode();
3587     FolderNode *folderNode = node ? node->asFolderNode() : nullptr;
3588 
3589     QTC_ASSERT(folderNode, return);
3590 
3591     QStringList fileNames = QFileDialog::getOpenFileNames(ICore::dialogParent(),
3592         tr("Add Existing Files"), node->directory());
3593     if (fileNames.isEmpty())
3594         return;
3595 
3596     ProjectExplorerPlugin::addExistingFiles(folderNode,
3597                                             Utils::transform(fileNames, &FilePath::fromString));
3598 }
3599 
addExistingDirectory()3600 void ProjectExplorerPluginPrivate::addExistingDirectory()
3601 {
3602     Node *node = ProjectTree::currentNode();
3603     FolderNode *folderNode = node ? node->asFolderNode() : nullptr;
3604 
3605     QTC_ASSERT(folderNode, return);
3606 
3607     SelectableFilesDialogAddDirectory dialog(Utils::FilePath::fromString(node->directory()),
3608                                              Utils::FilePaths(), ICore::dialogParent());
3609     dialog.setAddFileFilter({});
3610 
3611     if (dialog.exec() == QDialog::Accepted)
3612         ProjectExplorerPlugin::addExistingFiles(folderNode, dialog.selectedFiles());
3613 }
3614 
addExistingFiles(FolderNode * folderNode,const FilePaths & filePaths)3615 void ProjectExplorerPlugin::addExistingFiles(FolderNode *folderNode, const FilePaths &filePaths)
3616 {
3617     // can happen when project is not yet parsed or finished parsing while the dialog was open:
3618     if (!folderNode || !ProjectTree::hasNode(folderNode))
3619         return;
3620 
3621     const QString dir = folderNode->directory();
3622     FilePaths fileNames = filePaths;
3623     FilePaths notAdded;
3624     folderNode->addFiles(fileNames, &notAdded);
3625 
3626     if (!notAdded.isEmpty()) {
3627         const QString message = tr("Could not add following files to project %1:")
3628                 .arg(folderNode->managingProject()->displayName()) + QLatin1Char('\n');
3629         QMessageBox::warning(ICore::dialogParent(), tr("Adding Files to Project Failed"),
3630                              message + FilePath::formatFilePaths(notAdded, "\n"));
3631         fileNames = Utils::filtered(fileNames,
3632                                     [&notAdded](const FilePath &f) { return !notAdded.contains(f); });
3633     }
3634 
3635     VcsManager::promptToAdd(dir, Utils::transform(fileNames, &FilePath::toString));
3636 }
3637 
removeProject()3638 void ProjectExplorerPluginPrivate::removeProject()
3639 {
3640     Node *node = ProjectTree::currentNode();
3641     if (!node)
3642         return;
3643     ProjectNode *projectNode = node->managingProject();
3644     if (projectNode) {
3645         Utils::RemoveFileDialog removeFileDialog(node->filePath().toString(), ICore::dialogParent());
3646         removeFileDialog.setDeleteFileVisible(false);
3647         if (removeFileDialog.exec() == QDialog::Accepted)
3648             projectNode->removeSubProject(node->filePath().toString());
3649     }
3650 }
3651 
openFile()3652 void ProjectExplorerPluginPrivate::openFile()
3653 {
3654     const Node *currentNode = ProjectTree::currentNode();
3655     QTC_ASSERT(currentNode, return);
3656     EditorManager::openEditor(currentNode->filePath());
3657 }
3658 
searchOnFileSystem()3659 void ProjectExplorerPluginPrivate::searchOnFileSystem()
3660 {
3661     const Node *currentNode = ProjectTree::currentNode();
3662     QTC_ASSERT(currentNode, return);
3663     TextEditor::FindInFiles::findOnFileSystem(currentNode->path());
3664 }
3665 
showInGraphicalShell()3666 void ProjectExplorerPluginPrivate::showInGraphicalShell()
3667 {
3668     Node *currentNode = ProjectTree::currentNode();
3669     QTC_ASSERT(currentNode, return);
3670     Core::FileUtils::showInGraphicalShell(ICore::dialogParent(), currentNode->path());
3671 }
3672 
openTerminalHere(const EnvironmentGetter & env)3673 void ProjectExplorerPluginPrivate::openTerminalHere(const EnvironmentGetter &env)
3674 {
3675     const Node *currentNode = ProjectTree::currentNode();
3676     QTC_ASSERT(currentNode, return);
3677 
3678     const auto environment = env(ProjectTree::projectForNode(currentNode));
3679     if (!environment)
3680         return;
3681 
3682     Core::FileUtils::openTerminal(currentNode->directory(), environment.value());
3683 }
3684 
openTerminalHereWithRunEnv()3685 void ProjectExplorerPluginPrivate::openTerminalHereWithRunEnv()
3686 {
3687     const Node *currentNode = ProjectTree::currentNode();
3688     QTC_ASSERT(currentNode, return);
3689 
3690     const Project * const project = ProjectTree::projectForNode(currentNode);
3691     QTC_ASSERT(project, return);
3692     const Target * const target = project->activeTarget();
3693     QTC_ASSERT(target, return);
3694     const RunConfiguration * const runConfig = runConfigForNode(target,
3695                                                                 currentNode->asProjectNode());
3696     QTC_ASSERT(runConfig, return);
3697 
3698     const Runnable runnable = runConfig->runnable();
3699     IDevice::ConstPtr device = runnable.device;
3700     if (!device)
3701         device = DeviceKitAspect::device(target->kit());
3702     QTC_ASSERT(device && device->canOpenTerminal(), return);
3703     const QString workingDir = device->type() == Constants::DESKTOP_DEVICE_TYPE
3704             ? currentNode->directory() : runnable.workingDirectory;
3705     device->openTerminal(runnable.environment, workingDir);
3706 }
3707 
removeFile()3708 void ProjectExplorerPluginPrivate::removeFile()
3709 {
3710     const Node *currentNode = ProjectTree::currentNode();
3711     QTC_ASSERT(currentNode && currentNode->asFileNode(), return);
3712 
3713     ProjectTree::CurrentNodeKeeper nodeKeeper;
3714 
3715     const Utils::FilePath filePath = currentNode->filePath();
3716     using NodeAndPath = QPair<const Node *, Utils::FilePath>;
3717     QList<NodeAndPath> filesToRemove{qMakePair(currentNode, currentNode->filePath())};
3718     QList<NodeAndPath> siblings;
3719     for (const Node * const n : ProjectTree::siblingsWithSameBaseName(currentNode))
3720         siblings << qMakePair(n, n->filePath());
3721 
3722     Utils::RemoveFileDialog removeFileDialog(filePath.toString(), ICore::dialogParent());
3723     if (removeFileDialog.exec() != QDialog::Accepted)
3724         return;
3725 
3726     const bool deleteFile = removeFileDialog.isDeleteFileChecked();
3727 
3728     if (!siblings.isEmpty()) {
3729         const QMessageBox::StandardButton reply = QMessageBox::question(
3730                     Core::ICore::dialogParent(), tr("Remove More Files?"),
3731                     tr("Remove these files as well?\n    %1")
3732                     .arg(Utils::transform<QStringList>(siblings, [](const NodeAndPath &np) {
3733             return np.second.fileName();
3734         }).join("\n    ")));
3735         if (reply == QMessageBox::Yes)
3736             filesToRemove << siblings;
3737     }
3738 
3739     for (const NodeAndPath &file : qAsConst(filesToRemove)) {
3740         // Nodes can become invalid if the project was re-parsed while the dialog was open
3741         if (!ProjectTree::hasNode(file.first)) {
3742             QMessageBox::warning(ICore::dialogParent(), tr("Removing File Failed"),
3743                                  tr("File \"%1\" was not removed, because the project has changed "
3744                                     "in the meantime.\nPlease try again.")
3745                                  .arg(file.second.toUserOutput()));
3746             return;
3747         }
3748 
3749         // remove from project
3750         FolderNode *folderNode = file.first->asFileNode()->parentFolderNode();
3751         QTC_ASSERT(folderNode, return);
3752 
3753         const Utils::FilePath &currentFilePath = file.second;
3754         const RemovedFilesFromProject status = folderNode->removeFiles({currentFilePath});
3755         const bool success = status == RemovedFilesFromProject::Ok
3756                 || (status == RemovedFilesFromProject::Wildcard
3757                     && removeFileDialog.isDeleteFileChecked());
3758         if (!success) {
3759             TaskHub::addTask(BuildSystemTask(Task::Error,
3760                     tr("Could not remove file \"%1\" from project \"%2\".")
3761                         .arg(currentFilePath.toUserOutput(), folderNode->managingProject()->displayName()),
3762                     folderNode->managingProject()->filePath()));
3763         }
3764     }
3765 
3766     std::vector<std::unique_ptr<FileChangeBlocker>> changeGuards;
3767     FilePaths pathList;
3768     for (const NodeAndPath &file : qAsConst(filesToRemove)) {
3769         pathList << file.second;
3770         changeGuards.emplace_back(std::make_unique<FileChangeBlocker>(file.second));
3771     }
3772 
3773     Core::FileUtils::removeFiles(pathList, deleteFile);
3774 }
3775 
duplicateFile()3776 void ProjectExplorerPluginPrivate::duplicateFile()
3777 {
3778     Node *currentNode = ProjectTree::currentNode();
3779     QTC_ASSERT(currentNode && currentNode->asFileNode(), return);
3780 
3781     ProjectTree::CurrentNodeKeeper nodeKeeper;
3782 
3783     FileNode *fileNode = currentNode->asFileNode();
3784     QString filePath = currentNode->filePath().toString();
3785     QFileInfo sourceFileInfo(filePath);
3786     QString baseName = sourceFileInfo.baseName();
3787 
3788     QString newFileName = sourceFileInfo.fileName();
3789     int copyTokenIndex = newFileName.lastIndexOf(baseName)+baseName.length();
3790     newFileName.insert(copyTokenIndex, tr("_copy"));
3791 
3792     bool okPressed;
3793     newFileName = QInputDialog::getText(ICore::dialogParent(), tr("Choose File Name"),
3794             tr("New file name:"), QLineEdit::Normal, newFileName, &okPressed);
3795     if (!okPressed)
3796         return;
3797     if (!ProjectTree::hasNode(currentNode))
3798         return;
3799 
3800     const QString newFilePath = sourceFileInfo.path() + '/' + newFileName;
3801     FolderNode *folderNode = fileNode->parentFolderNode();
3802     QTC_ASSERT(folderNode, return);
3803     QFile sourceFile(filePath);
3804     if (!sourceFile.copy(newFilePath)) {
3805         QMessageBox::critical(ICore::dialogParent(), tr("Duplicating File Failed"),
3806                              tr("Failed to copy file \"%1\" to \"%2\": %3.")
3807                              .arg(QDir::toNativeSeparators(filePath),
3808                                   QDir::toNativeSeparators(newFilePath), sourceFile.errorString()));
3809         return;
3810     }
3811     if (!folderNode->addFiles({FilePath::fromString(newFilePath)})) {
3812         QMessageBox::critical(ICore::dialogParent(), tr("Duplicating File Failed"),
3813                               tr("Failed to add new file \"%1\" to the project.")
3814                               .arg(QDir::toNativeSeparators(newFilePath)));
3815     }
3816 }
3817 
deleteFile()3818 void ProjectExplorerPluginPrivate::deleteFile()
3819 {
3820     Node *currentNode = ProjectTree::currentNode();
3821     QTC_ASSERT(currentNode && currentNode->asFileNode(), return);
3822 
3823     ProjectTree::CurrentNodeKeeper nodeKeeper;
3824 
3825     FileNode *fileNode = currentNode->asFileNode();
3826 
3827     FilePath filePath = currentNode->filePath();
3828     QMessageBox::StandardButton button =
3829             QMessageBox::question(ICore::dialogParent(),
3830                                   tr("Delete File"),
3831                                   tr("Delete %1 from file system?")
3832                                   .arg(filePath.toUserOutput()),
3833                                   QMessageBox::Yes | QMessageBox::No);
3834     if (button != QMessageBox::Yes)
3835         return;
3836 
3837     FolderNode *folderNode = fileNode->parentFolderNode();
3838     QTC_ASSERT(folderNode, return);
3839 
3840     folderNode->deleteFiles({filePath});
3841 
3842     FileChangeBlocker changeGuard(currentNode->filePath());
3843     if (IVersionControl *vc =
3844             VcsManager::findVersionControlForDirectory(filePath.absolutePath().toString())) {
3845         vc->vcsDelete(filePath.toString());
3846     }
3847     if (filePath.exists()) {
3848         if (!filePath.removeFile())
3849             QMessageBox::warning(ICore::dialogParent(), tr("Deleting File Failed"),
3850                                  tr("Could not delete file %1.")
3851                                  .arg(filePath.toUserOutput()));
3852     }
3853 }
3854 
handleRenameFile()3855 void ProjectExplorerPluginPrivate::handleRenameFile()
3856 {
3857     QWidget *focusWidget = QApplication::focusWidget();
3858     while (focusWidget) {
3859         auto treeWidget = qobject_cast<ProjectTreeWidget*>(focusWidget);
3860         if (treeWidget) {
3861             treeWidget->editCurrentItem();
3862             return;
3863         }
3864         focusWidget = focusWidget->parentWidget();
3865     }
3866 }
3867 
renameFile(Node * node,const QString & newFileName)3868 void ProjectExplorerPlugin::renameFile(Node *node, const QString &newFileName)
3869 {
3870     const FilePath oldFilePath = node->filePath().absoluteFilePath();
3871     FolderNode *folderNode = node->parentFolderNode();
3872     QTC_ASSERT(folderNode, return);
3873     const QString projectFileName = folderNode->managingProject()->filePath().fileName();
3874 
3875     const FilePath newFilePath = FilePath::fromString(newFileName);
3876 
3877     if (oldFilePath == newFilePath)
3878         return;
3879 
3880     auto handleGuards = Core::HandleIncludeGuards::No;
3881     if (node->asFileNode() && node->asFileNode()->fileType() == FileType::Header)
3882         handleGuards = Core::HandleIncludeGuards::Yes;
3883     if (!folderNode->canRenameFile(oldFilePath, newFilePath)) {
3884         QTimer::singleShot(0, [oldFilePath, newFilePath, projectFileName, handleGuards] {
3885             int res = QMessageBox::question(ICore::dialogParent(),
3886                                             tr("Project Editing Failed"),
3887                                             tr("The project file %1 cannot be automatically changed.\n\n"
3888                                                "Rename %2 to %3 anyway?")
3889                                             .arg(projectFileName)
3890                                             .arg(oldFilePath.toUserOutput())
3891                                             .arg(newFilePath.toUserOutput()));
3892             if (res == QMessageBox::Yes) {
3893                 QTC_CHECK(Core::FileUtils::renameFile(oldFilePath, newFilePath, handleGuards));
3894             }
3895         });
3896         return;
3897     }
3898 
3899     if (Core::FileUtils::renameFile(oldFilePath, newFilePath, handleGuards)) {
3900         // Tell the project plugin about rename
3901         if (!folderNode->renameFile(oldFilePath, newFilePath)) {
3902             const QString renameFileError = tr("The file %1 was renamed to %2, but the project "
3903                                                "file %3 could not be automatically changed.")
3904                                                 .arg(oldFilePath.toUserOutput())
3905                                                 .arg(newFilePath.toUserOutput())
3906                                                 .arg(projectFileName);
3907 
3908             QTimer::singleShot(0, [renameFileError]() {
3909                 QMessageBox::warning(ICore::dialogParent(),
3910                                      tr("Project Editing Failed"),
3911                                      renameFileError);
3912             });
3913         }
3914     } else {
3915         const QString renameFileError = tr("The file %1 could not be renamed %2.")
3916                                             .arg(oldFilePath.toUserOutput())
3917                                             .arg(newFilePath.toUserOutput());
3918 
3919         QTimer::singleShot(0, [renameFileError]() {
3920             QMessageBox::warning(ICore::dialogParent(), tr("Cannot Rename File"), renameFileError);
3921         });
3922     }
3923 }
3924 
handleSetStartupProject()3925 void ProjectExplorerPluginPrivate::handleSetStartupProject()
3926 {
3927     setStartupProject(ProjectTree::currentProject());
3928 }
3929 
updateSessionMenu()3930 void ProjectExplorerPluginPrivate::updateSessionMenu()
3931 {
3932     m_sessionMenu->clear();
3933     dd->m_sessionMenu->addAction(dd->m_sessionManagerAction);
3934     dd->m_sessionMenu->addSeparator();
3935     auto *ag = new QActionGroup(m_sessionMenu);
3936     connect(ag, &QActionGroup::triggered, this, &ProjectExplorerPluginPrivate::setSession);
3937     const QString activeSession = SessionManager::activeSession();
3938 
3939     const QStringList sessions = SessionManager::sessions();
3940     for (int i = 0; i < sessions.size(); ++i) {
3941         const QString &session = sessions[i];
3942 
3943         const QString actionText = ActionManager::withNumberAccelerator(Utils::quoteAmpersands(
3944                                                                             session),
3945                                                                         i + 1);
3946         QAction *act = ag->addAction(actionText);
3947         act->setData(session);
3948         act->setCheckable(true);
3949         if (session == activeSession)
3950             act->setChecked(true);
3951     }
3952     m_sessionMenu->addActions(ag->actions());
3953     m_sessionMenu->setEnabled(true);
3954 }
3955 
setSession(QAction * action)3956 void ProjectExplorerPluginPrivate::setSession(QAction *action)
3957 {
3958     QString session = action->data().toString();
3959     if (session != SessionManager::activeSession())
3960         SessionManager::loadSession(session);
3961 }
3962 
setProjectExplorerSettings(const ProjectExplorerSettings & pes)3963 void ProjectExplorerPlugin::setProjectExplorerSettings(const ProjectExplorerSettings &pes)
3964 {
3965     QTC_ASSERT(dd->m_projectExplorerSettings.environmentId == pes.environmentId, return);
3966 
3967     if (dd->m_projectExplorerSettings == pes)
3968         return;
3969     dd->m_projectExplorerSettings = pes;
3970     emit m_instance->settingsChanged();
3971 }
3972 
projectExplorerSettings()3973 const ProjectExplorerSettings &ProjectExplorerPlugin::projectExplorerSettings()
3974 {
3975     return dd->m_projectExplorerSettings;
3976 }
3977 
setAppOutputSettings(const AppOutputSettings & settings)3978 void ProjectExplorerPlugin::setAppOutputSettings(const AppOutputSettings &settings)
3979 {
3980     dd->m_outputPane.setSettings(settings);
3981 }
3982 
appOutputSettings()3983 const AppOutputSettings &ProjectExplorerPlugin::appOutputSettings()
3984 {
3985     return dd->m_outputPane.settings();
3986 }
3987 
buildPropertiesSettings()3988 BuildPropertiesSettings &ProjectExplorerPlugin::buildPropertiesSettings()
3989 {
3990     return dd->m_buildPropertiesSettings;
3991 }
3992 
showQtSettings()3993 void ProjectExplorerPlugin::showQtSettings()
3994 {
3995     dd->m_buildPropertiesSettings.showQtSettings.setValue(true);
3996 }
3997 
setCustomParsers(const QList<CustomParserSettings> & settings)3998 void ProjectExplorerPlugin::setCustomParsers(const QList<CustomParserSettings> &settings)
3999 {
4000     if (dd->m_customParsers != settings) {
4001         dd->m_customParsers = settings;
4002         emit m_instance->customParsersChanged();
4003     }
4004 }
4005 
addCustomParser(const CustomParserSettings & settings)4006 void ProjectExplorerPlugin::addCustomParser(const CustomParserSettings &settings)
4007 {
4008     QTC_ASSERT(settings.id.isValid(), return);
4009     QTC_ASSERT(!contains(dd->m_customParsers, [&settings](const CustomParserSettings &s) {
4010         return s.id == settings.id;
4011     }), return);
4012 
4013     dd->m_customParsers << settings;
4014     emit m_instance->customParsersChanged();
4015 }
4016 
removeCustomParser(Id id)4017 void ProjectExplorerPlugin::removeCustomParser(Id id)
4018 {
4019     Utils::erase(dd->m_customParsers, [id](const CustomParserSettings &s) {
4020         return s.id == id;
4021     });
4022     emit m_instance->customParsersChanged();
4023 }
4024 
customParsers()4025 const QList<CustomParserSettings> ProjectExplorerPlugin::customParsers()
4026 {
4027     return dd->m_customParsers;
4028 }
4029 
projectFilePatterns()4030 QStringList ProjectExplorerPlugin::projectFilePatterns()
4031 {
4032     QStringList patterns;
4033     for (auto it = dd->m_projectCreators.cbegin(); it != dd->m_projectCreators.cend(); ++it) {
4034         Utils::MimeType mt = Utils::mimeTypeForName(it.key());
4035         if (mt.isValid())
4036             patterns.append(mt.globPatterns());
4037     }
4038     return patterns;
4039 }
4040 
isProjectFile(const Utils::FilePath & filePath)4041 bool ProjectExplorerPlugin::isProjectFile(const Utils::FilePath &filePath)
4042 {
4043     Utils::MimeType mt = Utils::mimeTypeForFile(filePath);
4044     for (auto it = dd->m_projectCreators.cbegin(); it != dd->m_projectCreators.cend(); ++it) {
4045         if (mt.inherits(it.key()))
4046             return true;
4047     }
4048     return false;
4049 }
4050 
openOpenProjectDialog()4051 void ProjectExplorerPlugin::openOpenProjectDialog()
4052 {
4053     const QString path = DocumentManager::useProjectsDirectory()
4054                              ? DocumentManager::projectsDirectory().toString()
4055                              : QString();
4056     const QStringList files = DocumentManager::getOpenFileNames(dd->m_projectFilterString, path);
4057     if (!files.isEmpty())
4058         ICore::openFiles(files, ICore::SwitchMode);
4059 }
4060 
4061 /*!
4062     Returns the current build directory template.
4063 
4064     \sa setBuildDirectoryTemplate
4065 */
buildDirectoryTemplate()4066 QString ProjectExplorerPlugin::buildDirectoryTemplate()
4067 {
4068     return dd->m_buildPropertiesSettings.buildDirectoryTemplate.value();
4069 }
4070 
defaultBuildDirectoryTemplate()4071 QString ProjectExplorerPlugin::defaultBuildDirectoryTemplate()
4072 {
4073     return dd->m_buildPropertiesSettings.defaultBuildDirectoryTemplate();
4074 }
4075 
updateActions()4076 void ProjectExplorerPlugin::updateActions()
4077 {
4078     dd->updateActions();
4079 }
4080 
activateProjectPanel(Utils::Id panelId)4081 void ProjectExplorerPlugin::activateProjectPanel(Utils::Id panelId)
4082 {
4083     Core::ModeManager::activateMode(Constants::MODE_SESSION);
4084     dd->m_proWindow->activateProjectPanel(panelId);
4085 }
4086 
clearRecentProjects()4087 void ProjectExplorerPlugin::clearRecentProjects()
4088 {
4089     dd->clearRecentProjects();
4090 }
4091 
removeFromRecentProjects(const QString & fileName,const QString & displayName)4092 void ProjectExplorerPlugin::removeFromRecentProjects(const QString &fileName,
4093                                                      const QString &displayName)
4094 {
4095     dd->removeFromRecentProjects(fileName, displayName);
4096 }
4097 
updateRunActions()4098 void ProjectExplorerPlugin::updateRunActions()
4099 {
4100     dd->doUpdateRunActions();
4101 }
4102 
buildSystemOutput()4103 OutputWindow *ProjectExplorerPlugin::buildSystemOutput()
4104 {
4105     return dd->m_proWindow->buildSystemOutput();
4106 }
4107 
recentProjects()4108 QList<QPair<QString, QString> > ProjectExplorerPlugin::recentProjects()
4109 {
4110     return dd->recentProjects();
4111 }
4112 
registerProjectCreator(const QString & mimeType,const std::function<Project * (const Utils::FilePath &)> & creator)4113 void ProjectManager::registerProjectCreator(const QString &mimeType,
4114     const std::function<Project *(const Utils::FilePath &)> &creator)
4115 {
4116     dd->m_projectCreators[mimeType] = creator;
4117 }
4118 
openProject(const Utils::MimeType & mt,const Utils::FilePath & fileName)4119 Project *ProjectManager::openProject(const Utils::MimeType &mt, const Utils::FilePath &fileName)
4120 {
4121     if (mt.isValid()) {
4122         for (auto it = dd->m_projectCreators.cbegin(); it != dd->m_projectCreators.cend(); ++it) {
4123             if (mt.matchesName(it.key()))
4124                 return it.value()(fileName);
4125         }
4126     }
4127     return nullptr;
4128 }
4129 
canOpenProjectForMimeType(const Utils::MimeType & mt)4130 bool ProjectManager::canOpenProjectForMimeType(const Utils::MimeType &mt)
4131 {
4132     if (mt.isValid()) {
4133         for (auto it = dd->m_projectCreators.cbegin(); it != dd->m_projectCreators.cend(); ++it) {
4134             if (mt.matchesName(it.key()))
4135                 return true;
4136         }
4137     }
4138     return false;
4139 }
4140 
4141 } // namespace ProjectExplorer
4142