From 6840c1d19812372076c7fadf35ca0d3eae4dcb72 Mon Sep 17 00:00:00 2001 From: Friedemann Kleint Date: Fri, 20 Aug 2010 14:19:25 +0200 Subject: [PATCH] Debugger: Refactor run control termination. Fix breakage introduced by the new asynchronous stop() methods of the debugger run controls. Allow for RunControl::stop() to be asynchronous by introducing a return enumeration indicating that. Introduce additional method aboutToStop() asking user to quit (tie that to the RunControl instead of having to hack the behaviour elsewhere). If asynchronous stop is detected, terminate the ProjectExplorer asynchronously. This makes the behaviour consistent across switching sessions/ closing outputwindow tabs and quitting Qt Creator. Reviewed-by: dt Rubber-stamped-by: hjk --- src/plugins/debugger/debuggerplugin.cpp | 118 ++----- src/plugins/debugger/debuggerplugin.h | 5 - src/plugins/debugger/debuggerrunner.cpp | 25 +- src/plugins/debugger/debuggerrunner.h | 3 +- .../applicationrunconfiguration.cpp | 3 +- .../applicationrunconfiguration.h | 2 +- .../corelistenercheckingforrunningbuild.cpp | 23 +- .../corelistenercheckingforrunningbuild.h | 9 +- src/plugins/projectexplorer/outputwindow.cpp | 300 ++++++++++++------ src/plugins/projectexplorer/outputwindow.h | 37 ++- .../projectexplorer/projectexplorer.cpp | 31 +- src/plugins/projectexplorer/projectexplorer.h | 1 + .../projectexplorer/runconfiguration.cpp | 23 +- .../projectexplorer/runconfiguration.h | 10 +- .../qmlprojectruncontrol.cpp | 3 +- .../qmlprojectmanager/qmlprojectruncontrol.h | 2 +- .../qt-maemo/maemoruncontrol.cpp | 3 +- .../qt-maemo/maemoruncontrol.h | 8 +- .../qt-s60/s60devicerunconfiguration.cpp | 16 +- .../qt-s60/s60devicerunconfiguration.h | 5 +- .../qt-s60/s60emulatorrunconfiguration.cpp | 3 +- .../qt-s60/s60emulatorrunconfiguration.h | 2 +- 22 files changed, 376 insertions(+), 256 deletions(-) diff --git a/src/plugins/debugger/debuggerplugin.cpp b/src/plugins/debugger/debuggerplugin.cpp index d65636d6212..a1c88ba96ea 100644 --- a/src/plugins/debugger/debuggerplugin.cpp +++ b/src/plugins/debugger/debuggerplugin.cpp @@ -454,21 +454,6 @@ public: } }; - -/////////////////////////////////////////////////////////////////////// -// -// DebuggerListener: Close the debugging session if running. -// -/////////////////////////////////////////////////////////////////////// - -class DebuggerListener : public Core::ICoreListener -{ -public: - DebuggerListener() {} - virtual bool coreAboutToClose(); -}; - - /////////////////////////////////////////////////////////////////////// // // LocationMark @@ -1554,7 +1539,6 @@ bool DebuggerPluginPrivate::initialize(const QStringList &arguments, QString *er m_plugin->addAutoReleasedObject(op); m_plugin->addAutoReleasedObject(new DebuggingHelperOptionPage); - m_plugin->addAutoReleasedObject(new DebuggerListener); m_locationMark = 0; //setSimpleDockWidgetArrangement(LANG_CPP); @@ -2597,23 +2581,13 @@ void DebuggerPlugin::clearCppCodeModelSnapshot() ExtensionSystem::IPlugin::ShutdownFlag DebuggerPlugin::aboutToShutdown() { + disconnect(sessionManager(), SIGNAL(startupProjectChanged(ProjectExplorer::Project*)), d, 0); writeSettings(); if (d->m_uiSwitcher) d->m_uiSwitcher->aboutToShutdown(); - //if (d->m_engine) - // d->m_engine->shutdown(); - - // FIXME: Notify all engines instead. - QTimer::singleShot(0, this, SLOT(emitShutdownFinished())); - return AsynchronousShutdown; + return SynchronousShutdown; } -void DebuggerPlugin::emitShutdownFinished() -{ - emit asynchronousShutdownFinished(); -} - - void DebuggerPlugin::showMessage(const QString &msg, int channel, int timeout) { //qDebug() << "PLUGIN OUTPUT: " << channel << msg; @@ -2760,63 +2734,35 @@ bool DebuggerPlugin::isRegisterViewVisible() const return d->m_registerDock->toggleViewAction()->isChecked(); } -bool DebuggerPlugin::coreAboutToClose() +static inline bool canShutDown(DebuggerState s) { - // FIXME: Iterate over all running debuggers. - // Ask to terminate the session. - bool cleanTermination = false; - switch (d->state()) { - case DebuggerNotReady: - case DebuggerFinished: - case InferiorUnrunnable: + switch (s) { + case DebuggerNotReady: + case DebuggerFinished: + case InferiorUnrunnable: return true; - case EngineSetupRequested: - case EngineSetupOk: - case EngineSetupFailed: - case InferiorSetupRequested: - case InferiorSetupFailed: - case EngineRunRequested: - case InferiorRunRequested: - case InferiorRunOk: - case InferiorStopRequested: - case InferiorStopOk: - case InferiorShutdownRequested: - case EngineShutdownRequested: - case InferiorShutdownOk: - case InferiorShutdownFailed: - case InferiorStopFailed: - case EngineRunFailed: - case InferiorRunFailed: - case EngineShutdownOk: - case EngineShutdownFailed: - return false; - } - - const QString question = cleanTermination ? - QCoreApplication::translate("Debugger::Internal::DebuggerListener", - "A debugging session is still in progress.\n" - "Would you like to terminate it?") : - QCoreApplication::translate("Debugger::Internal::DebuggerListener", - "A debugging session is still in progress. " - "Terminating the session in the current" - " state (%1) can leave the target in an inconsistent state." - " Would you still like to terminate it?") - .arg(_(DebuggerEngine::stateName(d->state()))); - - const QString title - = QCoreApplication::translate("Debugger::Internal::DebuggerListener", - "Close Debugging Session"); - QMessageBox::StandardButton answer = - QMessageBox::question(DebuggerUISwitcher::instance()->mainWindow(), - title, question, - QMessageBox::Yes|QMessageBox::No, QMessageBox::Yes); - - if (answer != QMessageBox::Yes) - return false; - - d->exitDebugger(); - QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents); - return true; + case EngineSetupRequested: + case EngineSetupOk: + case EngineSetupFailed: + case EngineRunRequested: + case InferiorSetupFailed: + case InferiorShutdownOk: + case InferiorShutdownFailed: + case EngineShutdownRequested: + case EngineRunFailed: + case EngineShutdownOk: + case EngineShutdownFailed: + case InferiorSetupRequested: + case InferiorRunRequested: + case InferiorRunOk: + case InferiorStopRequested: + case InferiorStopOk: + case InferiorStopFailed: + case InferiorShutdownRequested: + case InferiorRunFailed: + break; + } + return false; } ////////////////////////////////////////////////////////////////////// @@ -2836,12 +2782,6 @@ void DebuggerPlugin::runTest(const QString &fileName) } */ -bool DebuggerListener::coreAboutToClose() -{ - DebuggerPlugin *plugin = DebuggerPlugin::instance(); - return plugin && plugin->coreAboutToClose(); -} - } // namespace Debugger #include "debuggerplugin.moc" diff --git a/src/plugins/debugger/debuggerplugin.h b/src/plugins/debugger/debuggerplugin.h index a62e008a228..181617bf338 100644 --- a/src/plugins/debugger/debuggerplugin.h +++ b/src/plugins/debugger/debuggerplugin.h @@ -97,13 +97,9 @@ public slots: private: friend class Internal::DebuggerEngine; - friend class Internal::DebuggerListener; - friend class DebuggerPluginPrivate; friend class DebuggerRunControl; - bool coreAboutToClose(); - void resetLocation(); void gotoLocation(const QString &fileName, int lineNumber, bool setMarker); void activatePreviousMode(); @@ -122,7 +118,6 @@ private: QMessageBox *showMessageBox(int icon, const QString &title, const QString &text, int buttons = 0); - Q_SLOT void emitShutdownFinished(); bool initialize(const QStringList &arguments, QString *errorMessage); ShutdownFlag aboutToShutdown(); void extensionsInitialized(); diff --git a/src/plugins/debugger/debuggerrunner.cpp b/src/plugins/debugger/debuggerrunner.cpp index 66935a4d121..7895c28f0c2 100644 --- a/src/plugins/debugger/debuggerrunner.cpp +++ b/src/plugins/debugger/debuggerrunner.cpp @@ -34,6 +34,7 @@ #include "debuggerengine.h" #include "debuggerplugin.h" #include "debuggerstringutils.h" +#include "debuggeruiswitcher.h" #ifdef Q_OS_WIN # include "peutils.h" @@ -48,6 +49,7 @@ #include // For LocalApplication* #include +#include #include #include @@ -59,6 +61,7 @@ #include #include #include +#include using namespace ProjectExplorer; using namespace Debugger::Internal; @@ -509,11 +512,27 @@ void DebuggerRunControl::showMessage(const QString &msg, int channel) } } -void DebuggerRunControl::stop() +bool DebuggerRunControl::aboutToStop() const { - m_running = false; - QTC_ASSERT(m_engine, return); + QTC_ASSERT(isRunning(), return true;) + + const QString question = tr("A debugging session are still in progress. " + "Terminating the session in the current" + " state can leave the target in an inconsistent state." + " Would you still like to terminate it?"); + + const QMessageBox::StandardButton answer = + QMessageBox::question(DebuggerUISwitcher::instance()->mainWindow(), + tr("Close Debugging Session"), question, + QMessageBox::Yes|QMessageBox::No); + return answer == QMessageBox::Yes; +} + +RunControl::StopResult DebuggerRunControl::stop() +{ + QTC_ASSERT(m_engine, return StoppedSynchronously); m_engine->quitDebugger(); + return AsynchronousStop; } void DebuggerRunControl::debuggingFinished() diff --git a/src/plugins/debugger/debuggerrunner.h b/src/plugins/debugger/debuggerrunner.h index 6f983b3bd34..08f26801769 100644 --- a/src/plugins/debugger/debuggerrunner.h +++ b/src/plugins/debugger/debuggerrunner.h @@ -93,7 +93,8 @@ public: // ProjectExplorer::RunControl virtual void start(); - virtual void stop(); + virtual bool aboutToStop() const; + virtual StopResult stop(); virtual bool isRunning() const; QString displayName() const; diff --git a/src/plugins/projectexplorer/applicationrunconfiguration.cpp b/src/plugins/projectexplorer/applicationrunconfiguration.cpp index 05ae8f87650..3467dc61cd9 100644 --- a/src/plugins/projectexplorer/applicationrunconfiguration.cpp +++ b/src/plugins/projectexplorer/applicationrunconfiguration.cpp @@ -123,9 +123,10 @@ void LocalApplicationRunControl::start() emit appendMessage(this, tr("Starting %1...").arg(QDir::toNativeSeparators(m_executable)), false); } -void LocalApplicationRunControl::stop() +LocalApplicationRunControl::StopResult LocalApplicationRunControl::stop() { m_applicationLauncher.stop(); + return StoppedSynchronously; } bool LocalApplicationRunControl::isRunning() const diff --git a/src/plugins/projectexplorer/applicationrunconfiguration.h b/src/plugins/projectexplorer/applicationrunconfiguration.h index a9c4241a580..2325841e771 100644 --- a/src/plugins/projectexplorer/applicationrunconfiguration.h +++ b/src/plugins/projectexplorer/applicationrunconfiguration.h @@ -84,7 +84,7 @@ public: LocalApplicationRunControl(LocalApplicationRunConfiguration *runConfiguration, QString mode); virtual ~LocalApplicationRunControl(); virtual void start(); - virtual void stop(); + virtual StopResult stop(); virtual bool isRunning() const; private slots: void processExited(int exitCode); diff --git a/src/plugins/projectexplorer/corelistenercheckingforrunningbuild.cpp b/src/plugins/projectexplorer/corelistenercheckingforrunningbuild.cpp index 667614095f0..9e7de557ba7 100644 --- a/src/plugins/projectexplorer/corelistenercheckingforrunningbuild.cpp +++ b/src/plugins/projectexplorer/corelistenercheckingforrunningbuild.cpp @@ -28,33 +28,18 @@ **************************************************************************/ #include "corelistenercheckingforrunningbuild.h" -#include "buildmanager.h" - -#include -#include +#include "projectexplorer.h" namespace ProjectExplorer { namespace Internal { -CoreListenerCheckingForRunningBuild::CoreListenerCheckingForRunningBuild(BuildManager *manager) - : Core::ICoreListener(0), m_manager(manager) +CoreListener::CoreListener() { } -bool CoreListenerCheckingForRunningBuild::coreAboutToClose() +bool CoreListener::coreAboutToClose() { - if (m_manager->isBuilding()) { - QMessageBox box; - QPushButton *closeAnyway = box.addButton(tr("Cancel Build && Close"), QMessageBox::AcceptRole); - QPushButton *cancelClose = box.addButton(tr("Do not Close"), QMessageBox::RejectRole); - box.setDefaultButton(cancelClose); - box.setWindowTitle(tr("Close Qt Creator?")); - box.setText(tr("A project is currently being built.")); - box.setInformativeText(tr("Do you want to cancel the build process and close Qt Creator anyway?")); - box.exec(); - return (box.clickedButton() == closeAnyway); - } - return true; + return ProjectExplorerPlugin::instance()->coreAboutToClose(); } } diff --git a/src/plugins/projectexplorer/corelistenercheckingforrunningbuild.h b/src/plugins/projectexplorer/corelistenercheckingforrunningbuild.h index 011f79adc86..3e6ac38c8df 100644 --- a/src/plugins/projectexplorer/corelistenercheckingforrunningbuild.h +++ b/src/plugins/projectexplorer/corelistenercheckingforrunningbuild.h @@ -34,20 +34,15 @@ namespace ProjectExplorer { -class BuildManager; - namespace Internal { -class CoreListenerCheckingForRunningBuild : public Core::ICoreListener +class CoreListener : public Core::ICoreListener { Q_OBJECT public: - explicit CoreListenerCheckingForRunningBuild(BuildManager *manager); + CoreListener(); bool coreAboutToClose(); - -private: - BuildManager *m_manager; }; } // namespace Internal diff --git a/src/plugins/projectexplorer/outputwindow.cpp b/src/plugins/projectexplorer/outputwindow.cpp index ad111bfc2a9..4b37ca08e64 100644 --- a/src/plugins/projectexplorer/outputwindow.cpp +++ b/src/plugins/projectexplorer/outputwindow.cpp @@ -47,6 +47,7 @@ #include #include #include +#include #include #include @@ -62,13 +63,26 @@ #include #include -using namespace ProjectExplorer::Internal; -using namespace ProjectExplorer; +#include static const int MaxBlockCount = 100000; -OutputPane::OutputPane() - : m_mainWidget(new QWidget) +enum { debug = 0 }; + +namespace ProjectExplorer { +namespace Internal { + +OutputPane::RunControlTab::RunControlTab(RunControl *rc, OutputWindow *w) : + runControl(rc), window(w), asyncClosing(false) +{ +} + +OutputPane::OutputPane() : + m_mainWidget(new QWidget), + m_tabWidget(new QTabWidget), + m_stopAction(new QAction(QIcon(QLatin1String(Constants::ICON_STOP)), tr("Stop"), this)), + m_reRunButton(new QToolButton), + m_stopButton(new QToolButton) { m_runIcon.addFile(Constants::ICON_RUN); m_runIcon.addFile(Constants::ICON_RUN_SMALL); @@ -77,7 +91,6 @@ OutputPane::OutputPane() m_debugIcon.addFile(Constants::ICON_DEBUG_SMALL); // Rerun - m_reRunButton = new QToolButton; m_reRunButton->setIcon(m_runIcon); m_reRunButton->setToolTip(tr("Re-run this run-configuration")); m_reRunButton->setAutoRaise(true); @@ -89,13 +102,11 @@ OutputPane::OutputPane() Core::ActionManager *am = Core::ICore::instance()->actionManager(); Core::Context globalcontext(Core::Constants::C_GLOBAL); - m_stopAction = new QAction(QIcon(Constants::ICON_STOP), tr("Stop"), this); m_stopAction->setToolTip(tr("Stop")); m_stopAction->setEnabled(false); Core::Command *cmd = am->registerAction(m_stopAction, Constants::STOP, globalcontext); - m_stopButton = new QToolButton; m_stopButton->setDefaultAction(cmd->action()); m_stopButton->setAutoRaise(true); @@ -106,7 +117,6 @@ OutputPane::OutputPane() QVBoxLayout *layout = new QVBoxLayout; layout->setMargin(0); - m_tabWidget = new QTabWidget; m_tabWidget->setDocumentMode(true); m_tabWidget->setTabsClosable(true); m_tabWidget->setMovable(true); @@ -117,36 +127,69 @@ OutputPane::OutputPane() m_mainWidget->setLayout(layout); - connect(Core::ICore::instance(), SIGNAL(coreAboutToClose()), - this, SLOT(coreAboutToClose())); - connect(ProjectExplorer::ProjectExplorerPlugin::instance()->session(), SIGNAL(aboutToUnloadSession()), this, SLOT(aboutToUnloadSession())); } -void OutputPane::coreAboutToClose() +OutputPane::~OutputPane() { - while (m_tabWidget->count()) { - RunControl *rc = runControlForTab(0); - if (rc->isRunning()) - rc->stop(); - closeTab(0); - } + if (debug) + qDebug() << "OutputPane::~OutputPane: Entries left" << m_runControlTabs.size(); + + foreach(const RunControlTab &rt, m_runControlTabs) + delete rt.runControl; + delete m_mainWidget; +} + +int OutputPane::currentIndex() const +{ + if (const QWidget *w = m_tabWidget->currentWidget()) + return indexOf(w); + return -1; +} + +RunControl *OutputPane::currentRunControl() const +{ + const int index = currentIndex(); + if (index != -1) + return m_runControlTabs.at(index).runControl; + return 0; +} + +int OutputPane::indexOf(const RunControl *rc) const +{ + for (int i = m_runControlTabs.size() - 1; i >= 0; i--) + if (m_runControlTabs.at(i).runControl == rc) + return i; + return -1; +} + +int OutputPane::indexOf(const QWidget *outputWindow) const +{ + for (int i = m_runControlTabs.size() - 1; i >= 0; i--) + if (m_runControlTabs.at(i).window == outputWindow) + return i; + return -1; +} + +int OutputPane::tabWidgetIndexOf(int runControlIndex) const +{ + if (runControlIndex >= 0 && runControlIndex < m_runControlTabs.size()) + return m_tabWidget->indexOf(m_runControlTabs.at(runControlIndex).window); + return -1; +} + +bool OutputPane::aboutToClose() const +{ + foreach(const RunControlTab &rt, m_runControlTabs) + if (rt.runControl->isRunning() && !rt.runControl->aboutToStop()) + return false; + return true; } void OutputPane::aboutToUnloadSession() { - int i = 0; - while (i < m_tabWidget->count()) { - bool closed = closeTab(i); - if (!closed) // skip to next one - ++i; - } -} - -OutputPane::~OutputPane() -{ - delete m_mainWidget; + closeTabs(true); } QWidget *OutputPane::outputWidget(QWidget *) @@ -204,106 +247,136 @@ void OutputPane::createNewOutputWindow(RunControl *rc) this, SLOT(runControlFinished())); // First look if we can reuse a tab - bool found = false; - for (int i = 0; i < m_tabWidget->count(); ++i) { - RunControl *old = runControlForTab(i); - if (old->sameRunConfiguration(rc) && !old->isRunning()) { + const int size = m_runControlTabs.size(); + for (int i = 0; i < size; i++) { + RunControlTab &tab =m_runControlTabs[i]; + if (tab.runControl->sameRunConfiguration(rc) && !tab.runControl->isRunning()) { // Reuse this tab - delete old; - m_outputWindows.remove(old); - OutputWindow *ow = static_cast(m_tabWidget->widget(i)); - ow->grayOutOldContent(); - ow->verticalScrollBar()->setValue(ow->verticalScrollBar()->maximum()); - ow->setFormatter(rc->outputFormatter()); - m_outputWindows.insert(rc, ow); - found = true; - break; + delete tab.runControl; + tab.runControl = rc; + tab.window->grayOutOldContent(); + tab.window->scrollToBottom(); + tab.window->setFormatter(rc->outputFormatter()); + if (debug) + qDebug() << "OutputPane::createNewOutputWindow: Reusing tab" << i << " for " << rc; + return; } } - if (!found) { - OutputWindow *ow = new OutputWindow(m_tabWidget); - ow->setWindowTitle(tr("Application Output Window")); - ow->setWindowIcon(QIcon(QLatin1String(Qt4ProjectManager::Constants::ICON_WINDOW))); - ow->setFormatter(rc->outputFormatter()); - Aggregation::Aggregate *agg = new Aggregation::Aggregate; - agg->add(ow); - agg->add(new Find::BaseTextFind(ow)); - m_outputWindows.insert(rc, ow); - m_tabWidget->addTab(ow, rc->displayName()); - } + // Create new + OutputWindow *ow = new OutputWindow(m_tabWidget); + ow->setWindowTitle(tr("Application Output Window")); + ow->setWindowIcon(QIcon(QLatin1String(Qt4ProjectManager::Constants::ICON_WINDOW))); + ow->setFormatter(rc->outputFormatter()); + Aggregation::Aggregate *agg = new Aggregation::Aggregate; + agg->add(ow); + agg->add(new Find::BaseTextFind(ow)); + m_runControlTabs.push_back(RunControlTab(rc, ow)); + m_tabWidget->addTab(ow, rc->displayName()); + if (debug) + qDebug() << "OutputPane::createNewOutputWindow: Adding tab for " << rc; } void OutputPane::appendApplicationOutput(RunControl *rc, const QString &out, bool onStdErr) { - OutputWindow *ow = m_outputWindows.value(rc); - ow->appendApplicationOutput(out, onStdErr); + const int index = indexOf(rc); + if (index != -1) + m_runControlTabs.at(index).window->appendApplicationOutput(out, onStdErr); } void OutputPane::appendApplicationOutputInline(RunControl *rc, const QString &out, bool onStdErr) { - OutputWindow *ow = m_outputWindows.value(rc); - ow->appendApplicationOutputInline(out, onStdErr); + const int index = indexOf(rc); + if (index != -1) + m_runControlTabs.at(index).window->appendApplicationOutputInline(out, onStdErr); } void OutputPane::appendMessage(RunControl *rc, const QString &out, bool isError) { - OutputWindow *ow = m_outputWindows.value(rc); - ow->appendMessage(out, isError); + const int index = indexOf(rc); + if (index != -1) + m_runControlTabs.at(index).window->appendMessage(out, isError); } void OutputPane::showTabFor(RunControl *rc) { - OutputWindow *ow = m_outputWindows.value(rc); - m_tabWidget->setCurrentWidget(ow); + m_tabWidget->setCurrentIndex(tabWidgetIndexOf(indexOf(rc))); } void OutputPane::reRunRunControl() { - int index = m_tabWidget->currentIndex(); - RunControl *rc = runControlForTab(index); - OutputWindow *ow = static_cast(m_tabWidget->widget(index)); - if (ProjectExplorerPlugin::instance()->projectExplorerSettings().cleanOldAppOutput) - ow->clear(); - else - ow->grayOutOldContent(); - ow->verticalScrollBar()->setValue(ow->verticalScrollBar()->maximum()); - rc->start(); + const int index = currentIndex(); + QTC_ASSERT(index != -1 && !m_runControlTabs.at(index).runControl->isRunning(), return;) + + RunControlTab &tab = m_runControlTabs[index]; + if (ProjectExplorerPlugin::instance()->projectExplorerSettings().cleanOldAppOutput) { + tab.window->clear(); + } else { + tab.window->grayOutOldContent(); + } + tab.window->scrollToBottom(); + tab.runControl->start(); } void OutputPane::stopRunControl() { - RunControl *rc = runControlForTab(m_tabWidget->currentIndex()); - rc->stop(); + const int index = currentIndex(); + QTC_ASSERT(index != -1 && m_runControlTabs.at(index).runControl->isRunning(), return;) + + RunControl *rc = m_runControlTabs.at(index).runControl; + if (rc->isRunning() && rc->aboutToStop()) + rc->stop(); + + if (debug) + qDebug() << "OutputPane::stopRunControl " << rc; +} + +bool OutputPane::closeTabs(bool prompt) +{ + bool allClosed = true; + for (int t = m_tabWidget->count() - 1; t >= 0; t--) + if (!closeTab(t, prompt)) + allClosed = false; + if (debug) + qDebug() << "OutputPane::closeTabs() returns " << allClosed; + return allClosed; } bool OutputPane::closeTab(int index) { - OutputWindow *ow = static_cast(m_tabWidget->widget(index)); - RunControl *rc = m_outputWindows.key(ow); + return closeTab(index, true); +} - if (rc->isRunning()) { - QMessageBox messageBox(QMessageBox::Warning, - tr("Unable to close"), - tr("%1 is still running.").arg(rc->displayName()), - QMessageBox::Cancel | QMessageBox::Yes, - ow->window()); - messageBox.setInformativeText(tr("Force it to quit?")); - messageBox.setDefaultButton(QMessageBox::Yes); - messageBox.button(QMessageBox::Yes)->setText(tr("Force Quit")); - messageBox.button(QMessageBox::Cancel)->setText(tr("Keep Running")); +bool OutputPane::closeTab(int tabIndex, bool prompt) +{ + const int index = indexOf(m_tabWidget->widget(tabIndex)); + QTC_ASSERT(index != -1, return true;) - if (messageBox.exec() != QMessageBox::Yes) + RunControlTab &tab = m_runControlTabs[index]; + + if (debug) + qDebug() << "OutputPane::closeTab tab " << tabIndex << tab.runControl + << tab.window << tab.asyncClosing; + // Prompt user to stop + if (tab.runControl->isRunning()) { + if (prompt && !tab.runControl->aboutToStop()) return false; - - rc->stop(); + if (tab.runControl->stop() == RunControl::AsynchronousStop) { + tab.asyncClosing = true; + return false; + } } - m_tabWidget->removeTab(index); - delete ow; - delete rc; + m_tabWidget->removeTab(tabIndex); + if (tab.asyncClosing) { // We were invoked from its finished() signal. + tab.runControl->deleteLater(); + } else { + delete tab.runControl; + } + delete tab.window; + m_runControlTabs.removeAt(index); return true; } @@ -318,7 +391,10 @@ void OutputPane::tabChanged(int i) m_stopAction->setEnabled(false); m_reRunButton->setEnabled(false); } else { - RunControl *rc = runControlForTab(i); + const int index = indexOf(m_tabWidget->widget(i)); + QTC_ASSERT(index != -1, return; ) + + RunControl *rc = m_runControlTabs.at(index).runControl; m_stopAction->setEnabled(rc->isRunning()); m_reRunButton->setEnabled(!rc->isRunning()); m_reRunButton->setIcon(rc->runMode() == Constants::DEBUGMODE ? m_debugIcon : m_runIcon); @@ -327,27 +403,47 @@ void OutputPane::tabChanged(int i) void OutputPane::runControlStarted() { - RunControl *rc = runControlForTab(m_tabWidget->currentIndex()); - if (rc == qobject_cast(sender())) { + RunControl *current = currentRunControl(); + if (current && current == sender()) { m_reRunButton->setEnabled(false); m_stopAction->setEnabled(true); - m_reRunButton->setIcon(rc->runMode() == Constants::DEBUGMODE ? m_debugIcon : m_runIcon); + m_reRunButton->setIcon(current->runMode() == Constants::DEBUGMODE ? m_debugIcon : m_runIcon); } } void OutputPane::runControlFinished() { - RunControl *rc = runControlForTab(m_tabWidget->currentIndex()); - if (rc == qobject_cast(sender())) { - m_reRunButton->setEnabled(rc); + RunControl *senderRunControl = qobject_cast(sender()); + const int senderIndex = indexOf(senderRunControl); + + QTC_ASSERT(senderIndex != -1, return; ) + + // Enable buttons for current + RunControl *current = currentRunControl(); + + if (debug) + qDebug() << "OutputPane::runControlFinished" << senderRunControl << senderIndex + << " current " << current << m_runControlTabs.size(); + + if (current && current == sender()) { + m_reRunButton->setEnabled(true); m_stopAction->setEnabled(false); - m_reRunButton->setIcon(rc->runMode() == Constants::DEBUGMODE ? m_debugIcon : m_runIcon); + m_reRunButton->setIcon(current->runMode() == Constants::DEBUGMODE ? m_debugIcon : m_runIcon); } + // Check for asynchronous close. Close the tab. + if (m_runControlTabs.at(senderIndex).asyncClosing) + closeTab(tabWidgetIndexOf(senderIndex), false); + + if (!isRunning()) + emit allRunControlsFinished(); } -RunControl* OutputPane::runControlForTab(int index) const +bool OutputPane::isRunning() const { - return m_outputWindows.key(qobject_cast(m_tabWidget->widget(index))); + foreach(const RunControlTab &rt, m_runControlTabs) + if (rt.runControl->isRunning()) + return true; + return false; } bool OutputPane::canNext() @@ -441,7 +537,6 @@ void OutputWindow::mousePressEvent(QMouseEvent * e) QPlainTextEdit::mousePressEvent(e); } - void OutputWindow::mouseReleaseEvent(QMouseEvent *e) { m_mousePressed = false; @@ -631,3 +726,6 @@ void OutputWindow::enableUndoRedo() setMaximumBlockCount(0); setUndoRedoEnabled(true); } + +} // namespace Internal +} // namespace ProjectExplorer diff --git a/src/plugins/projectexplorer/outputwindow.h b/src/plugins/projectexplorer/outputwindow.h index 4cc3eecb031..ebe0ecd6587 100644 --- a/src/plugins/projectexplorer/outputwindow.h +++ b/src/plugins/projectexplorer/outputwindow.h @@ -32,9 +32,8 @@ #include -#include -#include #include +#include QT_BEGIN_NAMESPACE class QTabWidget; @@ -59,13 +58,15 @@ namespace Internal { class OutputWindow; +struct OutputPanePrivate; + class OutputPane : public Core::IOutputPane { Q_OBJECT public: OutputPane(); - ~OutputPane(); + virtual ~OutputPane(); QWidget *outputWidget(QWidget *); QList toolBarWidgets() const; @@ -85,11 +86,16 @@ public: void showTabFor(RunControl *rc); + bool aboutToClose() const; + bool closeTabs(bool prompt); + +signals: + void allRunControlsFinished(); + public slots: // ApplicationOutputspecifics void createNewOutputWindow(RunControl *rc); void projectRemoved(); - void coreAboutToClose(); void appendApplicationOutput(ProjectExplorer::RunControl *rc, const QString &out, bool onStdErr); @@ -108,11 +114,27 @@ private slots: void aboutToUnloadSession(); private: - RunControl *runControlForTab(int index) const; + struct RunControlTab { + explicit RunControlTab(RunControl *runControl = 0, + OutputWindow *window = 0); + RunControl* runControl; + OutputWindow *window; + // Is the run control stopping asynchronously, close the tab once it finishes + bool asyncClosing; + }; + + bool isRunning() const; + bool closeTab(int index, bool prompt); + + int indexOf(const RunControl *) const; + int indexOf(const QWidget *outputWindow) const; + int currentIndex() const; + RunControl *currentRunControl() const; + int tabWidgetIndexOf(int runControlIndex) const; QWidget *m_mainWidget; QTabWidget *m_tabWidget; - QHash m_outputWindows; + QList m_runControlTabs; QAction *m_stopAction; QToolButton *m_reRunButton; QToolButton *m_stopButton; @@ -148,9 +170,10 @@ public: QPlainTextEdit::clear(); } + void scrollToBottom(); + protected: bool isScrollbarAtBottom() const; - void scrollToBottom(); virtual void mousePressEvent(QMouseEvent *e); virtual void mouseReleaseEvent(QMouseEvent *e); diff --git a/src/plugins/projectexplorer/projectexplorer.cpp b/src/plugins/projectexplorer/projectexplorer.cpp index 6b12b85091e..d8584451a1c 100644 --- a/src/plugins/projectexplorer/projectexplorer.cpp +++ b/src/plugins/projectexplorer/projectexplorer.cpp @@ -297,7 +297,7 @@ bool ProjectExplorerPlugin::initialize(const QStringList &arguments, QString *er connect(d->m_buildManager, SIGNAL(buildQueueFinished(bool)), this, SLOT(buildQueueFinished(bool))); - addAutoReleasedObject(new CoreListenerCheckingForRunningBuild(d->m_buildManager)); + addAutoReleasedObject(new CoreListener); d->m_outputPane = new OutputPane; addAutoReleasedObject(d->m_outputPane); @@ -956,8 +956,14 @@ ExtensionSystem::IPlugin::ShutdownFlag ProjectExplorerPlugin::aboutToShutdown() d->m_proWindow->aboutToShutdown(); // disconnect from session d->m_session->clear(); d->m_projectsMode = 0; -// d->m_proWindow->saveConfigChanges(); - return SynchronousShutdown; + // Attempt to synchronously shutdown all run controls. + // If that fails, fall back to asynchronous shutdown (Debugger run controls + // might shutdown asynchronously). + if (d->m_outputPane->closeTabs(false /* No prompt any more */)) + return SynchronousShutdown; + connect(d->m_outputPane, SIGNAL(allRunControlsFinished()), + this, SIGNAL(asynchronousShutdownFinished())); + return AsynchronousShutdown; } void ProjectExplorerPlugin::newProject() @@ -1647,6 +1653,25 @@ bool ProjectExplorerPlugin::hasBuildSettings(Project *pro) return false; } +bool ProjectExplorerPlugin::coreAboutToClose() +{ + if (d->m_buildManager->isBuilding()) { + QMessageBox box; + QPushButton *closeAnyway = box.addButton(tr("Cancel Build && Close"), QMessageBox::AcceptRole); + QPushButton *cancelClose = box.addButton(tr("Do not Close"), QMessageBox::RejectRole); + box.setDefaultButton(cancelClose); + box.setWindowTitle(tr("Close Qt Creator?")); + box.setText(tr("A project is currently being built.")); + box.setInformativeText(tr("Do you want to cancel the build process and close Qt Creator anyway?")); + box.exec(); + if (box.clickedButton() != closeAnyway) + return false; + } + if (!d->m_outputPane->aboutToClose()) + return false; + return true; +} + bool ProjectExplorerPlugin::hasDeploySettings(Project *pro) { const QList & projects = d->m_session->projectOrder(pro); diff --git a/src/plugins/projectexplorer/projectexplorer.h b/src/plugins/projectexplorer/projectexplorer.h index 6233616e684..9ca130fbacc 100644 --- a/src/plugins/projectexplorer/projectexplorer.h +++ b/src/plugins/projectexplorer/projectexplorer.h @@ -110,6 +110,7 @@ public: // internal public for FlatModel void renameFile(Node *node, const QString &to); static QStringList projectFilePatterns(); + bool coreAboutToClose(); signals: void aboutToShowContextMenu(ProjectExplorer::Project *project, diff --git a/src/plugins/projectexplorer/runconfiguration.cpp b/src/plugins/projectexplorer/runconfiguration.cpp index 7d52e7bdfe1..16e4e9ed660 100644 --- a/src/plugins/projectexplorer/runconfiguration.cpp +++ b/src/plugins/projectexplorer/runconfiguration.cpp @@ -35,8 +35,13 @@ #include "buildconfiguration.h" #include +#include +#include #include +#include +#include +#include #ifdef Q_OS_MAC #include @@ -250,7 +255,23 @@ QString RunControl::displayName() const return m_displayName; } -bool RunControl::sameRunConfiguration(RunControl *other) +bool RunControl::aboutToStop() const +{ + QTC_ASSERT(isRunning(), return true;) + + QMessageBox messageBox(QMessageBox::Warning, + tr("Application Still Running"), + tr("%1 is still running.").arg(displayName()), + QMessageBox::Cancel | QMessageBox::Yes, + Core::ICore::instance()->mainWindow()); + messageBox.setInformativeText(tr("Force it to quit?")); + messageBox.setDefaultButton(QMessageBox::Yes); + messageBox.button(QMessageBox::Yes)->setText(tr("Force Quit")); + messageBox.button(QMessageBox::Cancel)->setText(tr("Keep Running")); + return messageBox.exec() == QMessageBox::Yes; +} + +bool RunControl::sameRunConfiguration(const RunControl *other) const { return other->m_runConfiguration.data() == m_runConfiguration.data(); } diff --git a/src/plugins/projectexplorer/runconfiguration.h b/src/plugins/projectexplorer/runconfiguration.h index 352d6326d28..6dd881a9151 100644 --- a/src/plugins/projectexplorer/runconfiguration.h +++ b/src/plugins/projectexplorer/runconfiguration.h @@ -159,14 +159,20 @@ class PROJECTEXPLORER_EXPORT RunControl : public QObject { Q_OBJECT public: + enum StopResult { + StoppedSynchronously, // Stopped. + AsynchronousStop, // Stop sequence has been started + }; + explicit RunControl(RunConfiguration *runConfiguration, QString mode); virtual ~RunControl(); virtual void start() = 0; - virtual void stop() = 0; // Warning: assumed to be synchroneous! + virtual bool aboutToStop() const; + virtual StopResult stop() = 0; virtual bool isRunning() const = 0; virtual QString displayName() const; - bool sameRunConfiguration(RunControl *other); + bool sameRunConfiguration(const RunControl *other) const; OutputFormatter *outputFormatter(); QString runMode() const; diff --git a/src/plugins/qmlprojectmanager/qmlprojectruncontrol.cpp b/src/plugins/qmlprojectmanager/qmlprojectruncontrol.cpp index 8f480105571..8d19e78b5b3 100644 --- a/src/plugins/qmlprojectmanager/qmlprojectruncontrol.cpp +++ b/src/plugins/qmlprojectmanager/qmlprojectruncontrol.cpp @@ -94,9 +94,10 @@ void QmlRunControl::start() m_commandLineArguments.join(QLatin1String(" "))), false); } -void QmlRunControl::stop() +RunControl::StopResult QmlRunControl::stop() { m_applicationLauncher.stop(); + return StoppedSynchronously; } bool QmlRunControl::isRunning() const diff --git a/src/plugins/qmlprojectmanager/qmlprojectruncontrol.h b/src/plugins/qmlprojectmanager/qmlprojectruncontrol.h index 8a1bc940a2e..cf260c2eca7 100644 --- a/src/plugins/qmlprojectmanager/qmlprojectruncontrol.h +++ b/src/plugins/qmlprojectmanager/qmlprojectruncontrol.h @@ -48,7 +48,7 @@ public: // RunControl virtual void start(); - virtual void stop(); + virtual StopResult stop(); virtual bool isRunning() const; private slots: diff --git a/src/plugins/qt4projectmanager/qt-maemo/maemoruncontrol.cpp b/src/plugins/qt4projectmanager/qt-maemo/maemoruncontrol.cpp index 7d8d72b44df..270e4cce723 100644 --- a/src/plugins/qt4projectmanager/qt-maemo/maemoruncontrol.cpp +++ b/src/plugins/qt4projectmanager/qt-maemo/maemoruncontrol.cpp @@ -93,9 +93,10 @@ void MaemoRunControl::start() } } -void MaemoRunControl::stop() +ProjectExplorer::RunControl::StopResult MaemoRunControl::stop() { m_runner->stop(); + return StoppedSynchronously; } void MaemoRunControl::handleSshError(const QString &error) diff --git a/src/plugins/qt4projectmanager/qt-maemo/maemoruncontrol.h b/src/plugins/qt4projectmanager/qt-maemo/maemoruncontrol.h index 3797703cde7..204fb1e3429 100644 --- a/src/plugins/qt4projectmanager/qt-maemo/maemoruncontrol.h +++ b/src/plugins/qt4projectmanager/qt-maemo/maemoruncontrol.h @@ -53,6 +53,10 @@ public: explicit MaemoRunControl(ProjectExplorer::RunConfiguration *runConfig); virtual ~MaemoRunControl(); + virtual void start(); + virtual StopResult stop(); + virtual bool isRunning() const; + private slots: void startExecution(); void handleSshError(const QString &error); @@ -63,10 +67,6 @@ private slots: void handleProgressReport(const QString &progressString); private: - virtual void start(); - virtual void stop(); - virtual bool isRunning() const; - void setFinished(); void handleError(const QString &errString); diff --git a/src/plugins/qt4projectmanager/qt-s60/s60devicerunconfiguration.cpp b/src/plugins/qt4projectmanager/qt-s60/s60devicerunconfiguration.cpp index 5060d799762..6368145faf8 100644 --- a/src/plugins/qt4projectmanager/qt-s60/s60devicerunconfiguration.cpp +++ b/src/plugins/qt4projectmanager/qt-s60/s60devicerunconfiguration.cpp @@ -392,10 +392,11 @@ void S60DeviceRunControl::start() startLaunching(); } -void S60DeviceRunControl::stop() +RunControl::StopResult S60DeviceRunControl::stop() { if (m_launcher) m_launcher->terminate(); + return AsynchronousStop; } bool S60DeviceRunControl::isRunning() const @@ -660,15 +661,20 @@ S60DeviceDebugRunControl::~S60DeviceDebugRunControl() // FIXME: Needed? m_debuggerRunControl->deleteLater(); } -void S60DeviceDebugRunControl::stop() +bool S60DeviceDebugRunControl::aboutToStop() const { - QTC_ASSERT(m_debuggerRunControl, return) - if (m_debuggerRunControl->state() == Debugger::DebuggerNotReady) - m_debuggerRunControl->stop(); + return m_debuggerRunControl ? m_debuggerRunControl->aboutToStop() : true; + } + +RunControl::StopResult S60DeviceDebugRunControl::stop() +{ + QTC_ASSERT(m_debuggerRunControl, return StoppedSynchronously) + if (m_debugProgress) m_debugProgress->reportCanceled(); delete m_debugProgress; m_debugProgress = 0; + return m_debuggerRunControl->stop(); } bool S60DeviceDebugRunControl::isRunning() const diff --git a/src/plugins/qt4projectmanager/qt-s60/s60devicerunconfiguration.h b/src/plugins/qt4projectmanager/qt-s60/s60devicerunconfiguration.h index 49b825455e0..00dabf5aba4 100644 --- a/src/plugins/qt4projectmanager/qt-s60/s60devicerunconfiguration.h +++ b/src/plugins/qt4projectmanager/qt-s60/s60devicerunconfiguration.h @@ -131,7 +131,7 @@ public: explicit S60DeviceRunControl(ProjectExplorer::RunConfiguration *runConfiguration, QString mode); ~S60DeviceRunControl(); virtual void start(); - virtual void stop(); + virtual StopResult stop(); virtual bool isRunning() const; static QMessageBox *createTrkWaitingMessageBox(const QString &port, QWidget *parent = 0); @@ -189,7 +189,8 @@ public: explicit S60DeviceDebugRunControl(S60DeviceRunConfiguration *runConfiguration, QString mode); virtual ~S60DeviceDebugRunControl(); virtual void start(); - virtual void stop(); + virtual bool aboutToStop() const; + virtual StopResult stop(); virtual bool isRunning() const; protected: diff --git a/src/plugins/qt4projectmanager/qt-s60/s60emulatorrunconfiguration.cpp b/src/plugins/qt4projectmanager/qt-s60/s60emulatorrunconfiguration.cpp index 20dc6b8c2c1..c828a7f4f94 100644 --- a/src/plugins/qt4projectmanager/qt-s60/s60emulatorrunconfiguration.cpp +++ b/src/plugins/qt4projectmanager/qt-s60/s60emulatorrunconfiguration.cpp @@ -326,9 +326,10 @@ void S60EmulatorRunControl::start() emit appendMessage(this, tr("Starting %1...").arg(QDir::toNativeSeparators(m_executable)), false); } -void S60EmulatorRunControl::stop() +RunControl::StopResult S60EmulatorRunControl::stop() { m_applicationLauncher.stop(); + return StoppedSynchronously; } bool S60EmulatorRunControl::isRunning() const diff --git a/src/plugins/qt4projectmanager/qt-s60/s60emulatorrunconfiguration.h b/src/plugins/qt4projectmanager/qt-s60/s60emulatorrunconfiguration.h index 62f0435e5f2..ccf2a372ee2 100644 --- a/src/plugins/qt4projectmanager/qt-s60/s60emulatorrunconfiguration.h +++ b/src/plugins/qt4projectmanager/qt-s60/s60emulatorrunconfiguration.h @@ -134,7 +134,7 @@ public: explicit S60EmulatorRunControl(S60EmulatorRunConfiguration *runConfiguration, QString mode); ~S60EmulatorRunControl() {} void start(); - void stop(); + virtual StopResult stop(); bool isRunning() const; private slots: