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, ¬Added);
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 [¬Added](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 ¤tFilePath = 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