diff --git a/src/plugins/projectexplorer/appoutputpane.cpp b/src/plugins/projectexplorer/appoutputpane.cpp index 7ce0a65c10d..a79c9b19ae4 100644 --- a/src/plugins/projectexplorer/appoutputpane.cpp +++ b/src/plugins/projectexplorer/appoutputpane.cpp @@ -535,6 +535,13 @@ bool AppOutputPane::closeTabs(CloseTabMode mode) return allClosed; } +QList AppOutputPane::allRunControls() const +{ + return Utils::transform(m_runControlTabs,[](const RunControlTab &tab) { + return tab.runControl; + }); +} + bool AppOutputPane::closeTab(int index) { return closeTab(index, CloseTabWithPrompt); diff --git a/src/plugins/projectexplorer/appoutputpane.h b/src/plugins/projectexplorer/appoutputpane.h index 824edf6d0aa..101bd9c7b55 100644 --- a/src/plugins/projectexplorer/appoutputpane.h +++ b/src/plugins/projectexplorer/appoutputpane.h @@ -92,6 +92,8 @@ public: bool aboutToClose() const; bool closeTabs(CloseTabMode mode); + QList allRunControls() const; + signals: void allRunControlsFinished(); void runControlStarted(ProjectExplorer::RunControl *rc); diff --git a/src/plugins/projectexplorer/projectexplorer.cpp b/src/plugins/projectexplorer/projectexplorer.cpp index a02a5bcf95b..266d00e4ad0 100644 --- a/src/plugins/projectexplorer/projectexplorer.cpp +++ b/src/plugins/projectexplorer/projectexplorer.cpp @@ -92,6 +92,7 @@ #include "devicesupport/devicesettingspage.h" #include "targetsettingspanel.h" #include "projectpanelfactory.h" +#include "waitforstopdialog.h" #ifdef Q_OS_WIN # include "windebuginterface.h" @@ -1160,6 +1161,13 @@ bool ProjectExplorerPlugin::initialize(const QStringList &arguments, QString *er QUuid(s->value(QLatin1String("ProjectExplorer/Settings/EnvironmentId")).toByteArray()); if (dd->m_projectExplorerSettings.environmentId.isNull()) dd->m_projectExplorerSettings.environmentId = QUuid::createUuid(); + int tmp = s->value(QLatin1String("ProjectExplorer/Settings/StopBeforeBuild"), + Utils::HostOsInfo::isWindowsHost() ? 1 : 0).toInt(); + dd->m_projectExplorerSettings.stopBeforeBuild = ProjectExplorerSettings::StopBeforeBuild(tmp); + if (tmp < 0 || tmp > ProjectExplorerSettings::StopAll) + tmp = Utils::HostOsInfo::isWindowsHost() ? 1 : 0; + + dd->m_projectExplorerSettings.stopBeforeBuild = ProjectExplorerSettings::StopBeforeBuild(tmp); connect(dd->m_sessionManagerAction, &QAction::triggered, dd, &ProjectExplorerPluginPrivate::showSessionManager); @@ -1646,6 +1654,7 @@ void ProjectExplorerPluginPrivate::savePersistentSettings() s->setValue(QLatin1String("ProjectExplorer/Settings/PromptToStopRunControl"), dd->m_projectExplorerSettings.prompToStopRunControl); s->setValue(QLatin1String("ProjectExplorer/Settings/MaxAppOutputLines"), dd->m_projectExplorerSettings.maxAppOutputLines); s->setValue(QLatin1String("ProjectExplorer/Settings/EnvironmentId"), dd->m_projectExplorerSettings.environmentId.toByteArray()); + s->setValue(QLatin1String("ProjectExplorer/Settings/StopBeforeBuild"), dd->m_projectExplorerSettings.stopBeforeBuild); } void ProjectExplorerPlugin::openProjectWelcomePage(const QString &fileName) @@ -2293,6 +2302,46 @@ int ProjectExplorerPluginPrivate::queue(QList projects, QList ste if (!m_instance->saveModifiedFiles()) return -1; + if (m_projectExplorerSettings.stopBeforeBuild != ProjectExplorerSettings::StopNone) { + QList toStop; + foreach (RunControl *rc, m_outputPane->allRunControls()) { + if (rc->isRunning() + && (m_projectExplorerSettings.stopBeforeBuild == ProjectExplorerSettings::StopAll + || projects.contains(rc->project()))) + toStop << rc; + } + + if (!toStop.isEmpty()) { + bool stopThem = true; + if (m_projectExplorerSettings.prompToStopRunControl) { + QStringList names = Utils::transform(toStop, &RunControl::displayName); + if (QMessageBox::question(ICore::mainWindow(), tr("Stop Applications"), + tr("Stop these applications before building?") + + QLatin1String("\n\n") + + names.join(QLatin1Char('\n'))) + == QMessageBox::No) { + stopThem = false; + } + } + + QList asyncStop; + if (stopThem) { + foreach (RunControl *rc, toStop) { + if (rc->stop() == RunControl::AsynchronousStop) + asyncStop << rc; + } + } + + if (!asyncStop.isEmpty()) { + WaitForStopDialog dialog(asyncStop); + dialog.exec(); + + if (dialog.canceled()) + return -1; + } + } + } + QList stepLists; QStringList names; QStringList preambleMessage; diff --git a/src/plugins/projectexplorer/projectexplorer.pro b/src/plugins/projectexplorer/projectexplorer.pro index d80732f7d5d..9ce399bbb7f 100644 --- a/src/plugins/projectexplorer/projectexplorer.pro +++ b/src/plugins/projectexplorer/projectexplorer.pro @@ -153,7 +153,8 @@ HEADERS += projectexplorer.h \ projectwelcomepage.h \ projectpanelfactory.h \ projecttree.h \ - expanddata.h + expanddata.h \ + waitforstopdialog.h SOURCES += projectexplorer.cpp \ abi.cpp \ @@ -293,7 +294,8 @@ SOURCES += projectexplorer.cpp \ projectwelcomepage.cpp \ projectpanelfactory.cpp \ projecttree.cpp \ - expanddata.cpp + expanddata.cpp \ + waitforstopdialog.cpp FORMS += processstep.ui \ editorsettingspropertiespage.ui \ diff --git a/src/plugins/projectexplorer/projectexplorer.qbs b/src/plugins/projectexplorer/projectexplorer.qbs index 21f53556f0d..4cba524ee7b 100644 --- a/src/plugins/projectexplorer/projectexplorer.qbs +++ b/src/plugins/projectexplorer/projectexplorer.qbs @@ -153,6 +153,7 @@ QtcPlugin { "toolchainoptionspage.cpp", "toolchainoptionspage.h", "unconfiguredprojectpanel.cpp", "unconfiguredprojectpanel.h", "vcsannotatetaskhandler.cpp", "vcsannotatetaskhandler.h", + "waitforstopdialog.cpp", "waitforstopdialog.h", "xcodebuildparser.cpp", "xcodebuildparser.h" ] } diff --git a/src/plugins/projectexplorer/projectexplorersettings.h b/src/plugins/projectexplorer/projectexplorersettings.h index 7caa541bc22..ee27265b32d 100644 --- a/src/plugins/projectexplorer/projectexplorersettings.h +++ b/src/plugins/projectexplorer/projectexplorersettings.h @@ -39,6 +39,8 @@ namespace Internal { class ProjectExplorerSettings { public: + enum StopBeforeBuild { StopNone = 0, StopSameProject = 1, StopAll = 2 }; + ProjectExplorerSettings() : buildBeforeDeploy(true), deployBeforeRun(true), saveBeforeBuild(false), showCompilerOutput(false), @@ -46,7 +48,7 @@ public: cleanOldAppOutput(false), mergeStdErrAndStdOut(false), wrapAppOutput(true), useJom(true), autorestoreLastSession(false), prompToStopRunControl(false), - maxAppOutputLines(100000) + maxAppOutputLines(100000), stopBeforeBuild(StopBeforeBuild::StopNone) { } bool buildBeforeDeploy; @@ -62,6 +64,7 @@ public: bool autorestoreLastSession; // This option is set in the Session Manager! bool prompToStopRunControl; int maxAppOutputLines; + StopBeforeBuild stopBeforeBuild; // Add a UUid which is used to identify the development environment. // This is used to warn the user when he is trying to open a .user file that was created @@ -84,7 +87,8 @@ inline bool operator==(const ProjectExplorerSettings &p1, const ProjectExplorerS && p1.autorestoreLastSession == p2.autorestoreLastSession && p1.prompToStopRunControl == p2.prompToStopRunControl && p1.maxAppOutputLines == p2.maxAppOutputLines - && p1.environmentId == p2.environmentId; + && p1.environmentId == p2.environmentId + && p1.stopBeforeBuild == p2.stopBeforeBuild; } } // namespace ProjectExplorer diff --git a/src/plugins/projectexplorer/projectexplorersettingspage.cpp b/src/plugins/projectexplorer/projectexplorersettingspage.cpp index e652f01c7f0..3b56da928ed 100644 --- a/src/plugins/projectexplorer/projectexplorersettingspage.cpp +++ b/src/plugins/projectexplorer/projectexplorersettingspage.cpp @@ -83,6 +83,7 @@ ProjectExplorerSettings ProjectExplorerSettingsWidget::settings() const pes.prompToStopRunControl = m_ui.promptToStopRunControlCheckBox->isChecked(); pes.maxAppOutputLines = m_ui.maxAppOutputBox->value(); pes.environmentId = m_environmentId; + pes.stopBeforeBuild = ProjectExplorerSettings::StopBeforeBuild(m_ui.stopBeforeBuildComboBox->currentIndex()); return pes; } @@ -101,6 +102,7 @@ void ProjectExplorerSettingsWidget::setSettings(const ProjectExplorerSettings & m_ui.promptToStopRunControlCheckBox->setChecked(pes.prompToStopRunControl); m_ui.maxAppOutputBox->setValue(pes.maxAppOutputLines); m_environmentId = pes.environmentId; + m_ui.stopBeforeBuildComboBox->setCurrentIndex(pes.stopBeforeBuild); } QString ProjectExplorerSettingsWidget::projectsDirectory() const diff --git a/src/plugins/projectexplorer/projectexplorersettingspage.ui b/src/plugins/projectexplorer/projectexplorersettingspage.ui index 0c3d4259d39..91908fa4cb1 100644 --- a/src/plugins/projectexplorer/projectexplorersettingspage.ui +++ b/src/plugins/projectexplorer/projectexplorersettingspage.ui @@ -183,6 +183,49 @@ + + + + + Stop applications before building: + + + + + + + + None + + + + + Same Project + + + + + All + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + 0 @@ -215,7 +258,7 @@ - + 12 diff --git a/src/plugins/projectexplorer/runconfiguration.cpp b/src/plugins/projectexplorer/runconfiguration.cpp index 8c3fdf8254c..abb506f70d3 100644 --- a/src/plugins/projectexplorer/runconfiguration.cpp +++ b/src/plugins/projectexplorer/runconfiguration.cpp @@ -521,7 +521,11 @@ RunControl::RunControl(RunConfiguration *runConfiguration, Core::Id mode) if (runConfiguration) { m_displayName = runConfiguration->displayName(); m_outputFormatter = runConfiguration->createOutputFormatter(); + + if (runConfiguration->target()) + m_project = m_runConfiguration->target()->project(); } + // We need to ensure that there's always a OutputFormatter if (!m_outputFormatter) m_outputFormatter = new Utils::OutputFormatter(); @@ -559,6 +563,11 @@ RunConfiguration *RunControl::runConfiguration() const return m_runConfiguration.data(); } +Project *RunControl::project() const +{ + return m_project.data(); +} + ProcessHandle RunControl::applicationProcessHandle() const { return m_applicationProcessHandle; diff --git a/src/plugins/projectexplorer/runconfiguration.h b/src/plugins/projectexplorer/runconfiguration.h index ff511a84a79..3e5f18a0ee9 100644 --- a/src/plugins/projectexplorer/runconfiguration.h +++ b/src/plugins/projectexplorer/runconfiguration.h @@ -301,6 +301,7 @@ public: Abi abi() const; RunConfiguration *runConfiguration() const; + Project *project() const; bool sameRunConfiguration(const RunControl *other) const; Utils::OutputFormatter *outputFormatter(); @@ -331,6 +332,7 @@ private: Core::Id m_runMode; QString m_icon; const QPointer m_runConfiguration; + QPointer m_project; Utils::OutputFormatter *m_outputFormatter; // A handle to the actual application process. diff --git a/src/plugins/projectexplorer/waitforstopdialog.cpp b/src/plugins/projectexplorer/waitforstopdialog.cpp new file mode 100644 index 00000000000..8f6529c6363 --- /dev/null +++ b/src/plugins/projectexplorer/waitforstopdialog.cpp @@ -0,0 +1,93 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms and +** conditions see http://www.qt.io/terms-conditions. For further information +** use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#include "waitforstopdialog.h" + +#include + +#include +#include +#include +#include + +using namespace ProjectExplorer; +using namespace ProjectExplorer::Internal; + +WaitForStopDialog::WaitForStopDialog(QList runControls) + : m_runControls(runControls) +{ + setWindowTitle(tr("Waiting for Applications to Stop")); + + QVBoxLayout *layout = new QVBoxLayout(); + setLayout(layout); + + m_progressLabel = new QLabel; + layout->addWidget(m_progressLabel); + + QPushButton *cancelButton = new QPushButton(tr("Cancel")); + connect(cancelButton, &QPushButton::clicked, + this, &QDialog::close); + layout->addWidget(cancelButton); + + updateProgressText(); + + foreach (RunControl *rc, runControls) + connect(rc, &RunControl::finished, this, &WaitForStopDialog::runControlFinished); + + m_timer.start(); +} + +bool WaitForStopDialog::canceled() +{ + return !m_runControls.isEmpty(); +} + +void WaitForStopDialog::updateProgressText() +{ + QString text = tr("Waiting for applications to stop.") + QLatin1String("\n\n"); + QStringList names = Utils::transform(m_runControls, &RunControl::displayName); + text += names.join(QLatin1Char('\n')); + m_progressLabel->setText(text); +} + +void WaitForStopDialog::runControlFinished() +{ + RunControl *rc = qobject_cast(sender()); + m_runControls.removeOne(rc); + + if (m_runControls.isEmpty()) { + if (m_timer.elapsed() < 1000) + QTimer::singleShot(1000 - m_timer.elapsed(), this, &QDialog::close); + else + QDialog::close(); + } else { + updateProgressText(); + } +} diff --git a/src/plugins/projectexplorer/waitforstopdialog.h b/src/plugins/projectexplorer/waitforstopdialog.h new file mode 100644 index 00000000000..42530caccc7 --- /dev/null +++ b/src/plugins/projectexplorer/waitforstopdialog.h @@ -0,0 +1,66 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms and +** conditions see http://www.qt.io/terms-conditions. For further information +** use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#ifndef WAITFORSTOPDIALOG_H +#define WAITFORSTOPDIALOG_H + +#include +#include +#include + +#include "runconfiguration.h" + +QT_BEGIN_NAMESPACE +class QLabel; +QT_END_NAMESPACE + +namespace ProjectExplorer { +namespace Internal { + +class WaitForStopDialog : public QDialog +{ +public: + WaitForStopDialog(QList runControls); + + bool canceled(); +private: + void updateProgressText(); + void runControlFinished(); + + QList m_runControls; + QLabel *m_progressLabel; + QTime m_timer; +}; + +} // namespace Internal +} // namespace ProjectExplorer + +#endif // WAITFORSTOPDIALOG_H +