Files
qt-creator/src/plugins/projectexplorer/appoutputpane.cpp
hjk cd5ba75505 ProjectExplorer: Use a real 'finishing' phase for RunControls
Instead of a blunt delete() RunControl::initiateFinish() is triggered
by the closing of the application output instead. The rampdown process
is basically the same as stop() now, except for the other success
signal (new finished()) and the final self-destruction of the
runcontrol.

stop() itself triggers initiateStop() on all running workers in
parallel (before it was in the order of start). This gives
downstream complex worker combinations the flexibility to use any
order it wants by ignoring stop() on 'uninteresting' workers,
and centralizing rampdown e.g. in the main worker. That setup should
be rare in practice, but seems needed in some profiler cases.

Change-Id: I986a152a663754206709ed4df0d4568847afad17
Reviewed-by: Christian Kandeler <christian.kandeler@qt.io>
2017-07-17 12:28:50 +00:00

783 lines
24 KiB
C++

/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://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 https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#include "appoutputpane.h"
#include "projectexplorer.h"
#include "projectexplorericons.h"
#include "projectexplorersettings.h"
#include "runconfiguration.h"
#include "session.h"
#include "windebuginterface.h"
#include <coreplugin/actionmanager/actionmanager.h>
#include <coreplugin/actionmanager/command.h>
#include <coreplugin/outputwindow.h>
#include <coreplugin/find/basetextfind.h>
#include <coreplugin/coreconstants.h>
#include <coreplugin/icore.h>
#include <texteditor/fontsettings.h>
#include <texteditor/texteditorsettings.h>
#include <texteditor/behaviorsettings.h>
#include <extensionsystem/pluginmanager.h>
#include <extensionsystem/invoker.h>
#include <utils/algorithm.h>
#include <utils/outputformatter.h>
#include <utils/qtcassert.h>
#include <utils/utilsicons.h>
#include <QAction>
#include <QVBoxLayout>
#include <QTabWidget>
#include <QToolButton>
#include <QTabBar>
#include <QMenu>
#include <QDebug>
enum { debug = 0 };
using namespace ProjectExplorer;
using namespace ProjectExplorer::Internal;
static QObject *debuggerPlugin()
{
return ExtensionSystem::PluginManager::getObjectByName(QLatin1String("DebuggerPlugin"));
}
static QString msgAttachDebuggerTooltip(const QString &handleDescription = QString())
{
return handleDescription.isEmpty() ?
AppOutputPane::tr("Attach debugger to this process") :
AppOutputPane::tr("Attach debugger to %1").arg(handleDescription);
}
static void replaceAllChildWidgets(QLayout *layout, const QList<QWidget *> &newChildren)
{
while (QLayoutItem *child = layout->takeAt(0))
delete child;
foreach (QWidget *widget, newChildren)
layout->addWidget(widget);
}
namespace {
const char SETTINGS_KEY[] = "ProjectExplorer/AppOutput/Zoom";
const char C_APP_OUTPUT[] = "ProjectExplorer.ApplicationOutput";
}
namespace ProjectExplorer {
namespace Internal {
class TabWidget : public QTabWidget
{
Q_OBJECT
public:
TabWidget(QWidget *parent = nullptr);
signals:
void contextMenuRequested(const QPoint &pos, int index);
protected:
bool eventFilter(QObject *object, QEvent *event) override;
private:
void slotContextMenuRequested(const QPoint &pos);
int m_tabIndexForMiddleClick = -1;
};
}
}
TabWidget::TabWidget(QWidget *parent)
: QTabWidget(parent)
{
tabBar()->installEventFilter(this);
setContextMenuPolicy(Qt::CustomContextMenu);
connect(this, &QWidget::customContextMenuRequested,
this, &TabWidget::slotContextMenuRequested);
}
bool TabWidget::eventFilter(QObject *object, QEvent *event)
{
if (object == tabBar()) {
if (event->type() == QEvent::MouseButtonPress) {
QMouseEvent *me = static_cast<QMouseEvent *>(event);
if (me->button() == Qt::MiddleButton) {
m_tabIndexForMiddleClick = tabBar()->tabAt(me->pos());
event->accept();
return true;
}
} else if (event->type() == QEvent::MouseButtonRelease) {
QMouseEvent *me = static_cast<QMouseEvent *>(event);
if (me->button() == Qt::MiddleButton) {
int tab = tabBar()->tabAt(me->pos());
if (tab != -1 && tab == m_tabIndexForMiddleClick)
emit tabCloseRequested(tab);
m_tabIndexForMiddleClick = -1;
event->accept();
return true;
}
}
}
return QTabWidget::eventFilter(object, event);
}
void TabWidget::slotContextMenuRequested(const QPoint &pos)
{
emit contextMenuRequested(pos, tabBar()->tabAt(pos));
}
AppOutputPane::RunControlTab::RunControlTab(RunControl *rc, Core::OutputWindow *w) :
runControl(rc), window(w)
{
if (rc && w)
w->setFormatter(rc->outputFormatter());
}
AppOutputPane::AppOutputPane() :
m_mainWidget(new QWidget),
m_tabWidget(new TabWidget),
m_stopAction(new QAction(tr("Stop"), this)),
m_closeCurrentTabAction(new QAction(tr("Close Tab"), this)),
m_closeAllTabsAction(new QAction(tr("Close All Tabs"), this)),
m_closeOtherTabsAction(new QAction(tr("Close Other Tabs"), this)),
m_reRunButton(new QToolButton),
m_stopButton(new QToolButton),
m_attachButton(new QToolButton),
m_zoomInButton(new QToolButton),
m_zoomOutButton(new QToolButton),
m_formatterWidget(new QWidget)
{
setObjectName(QLatin1String("AppOutputPane")); // Used in valgrind engine
// Rerun
m_reRunButton->setIcon(Utils::Icons::RUN_SMALL_TOOLBAR.icon());
m_reRunButton->setToolTip(tr("Re-run this run-configuration"));
m_reRunButton->setAutoRaise(true);
m_reRunButton->setEnabled(false);
connect(m_reRunButton, &QToolButton::clicked,
this, &AppOutputPane::reRunRunControl);
// Stop
m_stopAction->setIcon(Utils::Icons::STOP_SMALL_TOOLBAR.icon());
m_stopAction->setToolTip(tr("Stop Running Program"));
m_stopAction->setEnabled(false);
Core::Command *cmd = Core::ActionManager::registerAction(m_stopAction, Constants::STOP);
cmd->setDescription(m_stopAction->toolTip());
m_stopButton->setDefaultAction(cmd->action());
m_stopButton->setAutoRaise(true);
connect(m_stopAction, &QAction::triggered,
this, &AppOutputPane::stopRunControl);
// Attach
m_attachButton->setToolTip(msgAttachDebuggerTooltip());
m_attachButton->setEnabled(false);
m_attachButton->setIcon(Icons::DEBUG_START_SMALL_TOOLBAR.icon());
m_attachButton->setAutoRaise(true);
connect(m_attachButton, &QToolButton::clicked,
this, &AppOutputPane::attachToRunControl);
m_zoomInButton->setToolTip(tr("Increase Font Size"));
m_zoomInButton->setIcon(Utils::Icons::PLUS_TOOLBAR.icon());
m_zoomInButton->setAutoRaise(true);
connect(m_zoomInButton, &QToolButton::clicked,
this, &AppOutputPane::zoomIn);
m_zoomOutButton->setToolTip(tr("Decrease Font Size"));
m_zoomOutButton->setIcon(Utils::Icons::MINUS.icon());
m_zoomOutButton->setAutoRaise(true);
connect(m_zoomOutButton, &QToolButton::clicked,
this, &AppOutputPane::zoomOut);
auto formatterWidgetsLayout = new QHBoxLayout;
formatterWidgetsLayout->setContentsMargins(QMargins());
m_formatterWidget->setLayout(formatterWidgetsLayout);
// Spacer (?)
QVBoxLayout *layout = new QVBoxLayout;
layout->setMargin(0);
m_tabWidget->setDocumentMode(true);
m_tabWidget->setTabsClosable(true);
m_tabWidget->setMovable(true);
connect(m_tabWidget, &QTabWidget::tabCloseRequested,
this, [this](int index) { closeTab(index); });
layout->addWidget(m_tabWidget);
connect(m_tabWidget, &QTabWidget::currentChanged, this, &AppOutputPane::tabChanged);
connect(m_tabWidget, &TabWidget::contextMenuRequested,
this, &AppOutputPane::contextMenuRequested);
m_mainWidget->setLayout(layout);
connect(TextEditor::TextEditorSettings::instance(), &TextEditor::TextEditorSettings::fontSettingsChanged,
this, &AppOutputPane::updateFontSettings);
connect(TextEditor::TextEditorSettings::instance(), &TextEditor::TextEditorSettings::behaviorSettingsChanged,
this, &AppOutputPane::updateBehaviorSettings);
connect(SessionManager::instance(), &SessionManager::aboutToUnloadSession,
this, &AppOutputPane::aboutToUnloadSession);
connect(ProjectExplorerPlugin::instance(), &ProjectExplorerPlugin::settingsChanged,
this, &AppOutputPane::updateFromSettings);
#ifdef Q_OS_WIN
connect(this, &AppOutputPane::allRunControlsFinished,
WinDebugInterface::instance(), &WinDebugInterface::stop);
#endif
QSettings *settings = Core::ICore::settings();
m_zoom = settings->value(QLatin1String(SETTINGS_KEY), 0).toFloat();
connect(Core::ICore::instance(), &Core::ICore::saveSettingsRequested,
this, &AppOutputPane::saveSettings);
}
AppOutputPane::~AppOutputPane()
{
if (debug)
qDebug() << "OutputPane::~OutputPane: Entries left" << m_runControlTabs.size();
foreach (const RunControlTab &rt, m_runControlTabs) {
delete rt.window;
delete rt.runControl;
}
delete m_mainWidget;
}
void AppOutputPane::saveSettings()
{
QSettings *settings = Core::ICore::settings();
settings->setValue(QLatin1String(SETTINGS_KEY), m_zoom);
}
int AppOutputPane::currentIndex() const
{
if (const QWidget *w = m_tabWidget->currentWidget())
return indexOf(w);
return -1;
}
RunControl *AppOutputPane::currentRunControl() const
{
const int index = currentIndex();
if (index != -1)
return m_runControlTabs.at(index).runControl;
return nullptr;
}
int AppOutputPane::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 AppOutputPane::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 AppOutputPane::tabWidgetIndexOf(int runControlIndex) const
{
if (runControlIndex >= 0 && runControlIndex < m_runControlTabs.size())
return m_tabWidget->indexOf(m_runControlTabs.at(runControlIndex).window);
return -1;
}
void AppOutputPane::updateCloseActions()
{
const int tabCount = m_tabWidget->count();
m_closeCurrentTabAction->setEnabled(tabCount > 0);
m_closeAllTabsAction->setEnabled(tabCount > 0);
m_closeOtherTabsAction->setEnabled(tabCount > 1);
}
bool AppOutputPane::aboutToClose() const
{
return Utils::allOf(m_runControlTabs, [](const RunControlTab &rt) {
return !rt.runControl->isRunning() || rt.runControl->promptToStop();
});
}
void AppOutputPane::aboutToUnloadSession()
{
closeTabs(CloseTabWithPrompt);
}
QWidget *AppOutputPane::outputWidget(QWidget *)
{
return m_mainWidget;
}
QList<QWidget*> AppOutputPane::toolBarWidgets() const
{
return { m_reRunButton, m_stopButton, m_attachButton, m_zoomInButton,
m_zoomOutButton, m_formatterWidget };
}
QString AppOutputPane::displayName() const
{
return tr("Application Output");
}
int AppOutputPane::priorityInStatusBar() const
{
return 60;
}
void AppOutputPane::clearContents()
{
Core::OutputWindow *currentWindow = qobject_cast<Core::OutputWindow *>(m_tabWidget->currentWidget());
if (currentWindow)
currentWindow->clear();
}
void AppOutputPane::visibilityChanged(bool /* b */)
{
}
bool AppOutputPane::hasFocus() const
{
QWidget *widget = m_tabWidget->currentWidget();
if (!widget)
return false;
return widget->window()->focusWidget() == widget;
}
bool AppOutputPane::canFocus() const
{
return m_tabWidget->currentWidget();
}
void AppOutputPane::setFocus()
{
if (m_tabWidget->currentWidget())
m_tabWidget->currentWidget()->setFocus();
}
void AppOutputPane::updateFontSettings()
{
QFont f = TextEditor::TextEditorSettings::fontSettings().font();
foreach (const RunControlTab &rcTab, m_runControlTabs)
rcTab.window->setBaseFont(f);
}
void AppOutputPane::updateBehaviorSettings()
{
bool zoomEnabled = TextEditor::TextEditorSettings::behaviorSettings().m_scrollWheelZooming;
foreach (const RunControlTab &rcTab, m_runControlTabs)
rcTab.window->setWheelZoomEnabled(zoomEnabled);
}
void AppOutputPane::createNewOutputWindow(RunControl *rc)
{
connect(rc, &RunControl::aboutToStart,
this, &AppOutputPane::slotRunControlChanged);
connect(rc, &RunControl::started,
this, &AppOutputPane::slotRunControlChanged);
connect(rc, &RunControl::stopped,
this, &AppOutputPane::slotRunControlFinished);
connect(rc, &RunControl::applicationProcessHandleChanged,
this, &AppOutputPane::enableDefaultButtons);
connect(rc, &RunControl::appendMessageRequested,
this, &AppOutputPane::appendMessage);
// First look if we can reuse a tab
const int tabIndex = Utils::indexOf(m_runControlTabs, [rc](const RunControlTab &tab) {
return rc->canReUseOutputPane(tab.runControl);
});
if (tabIndex != -1) {
RunControlTab &tab = m_runControlTabs[tabIndex];
// Reuse this tab
if (tab.runControl)
tab.runControl->initiateFinish();
tab.runControl = rc;
tab.window->setFormatter(rc ? rc->outputFormatter() : nullptr);
handleOldOutput(tab.window);
// Update the title.
m_tabWidget->setTabText(tabIndex, rc->displayName());
tab.window->scrollToBottom();
if (debug)
qDebug() << "OutputPane::createNewOutputWindow: Reusing tab" << tabIndex << " for " << rc;
return;
}
// Create new
static int counter = 0;
Core::Id contextId = Core::Id(C_APP_OUTPUT).withSuffix(counter++);
Core::Context context(contextId);
Core::OutputWindow *ow = new Core::OutputWindow(context, m_tabWidget);
ow->setWindowTitle(tr("Application Output Window"));
ow->setWindowIcon(Icons::WINDOW.icon());
ow->setWordWrapEnabled(ProjectExplorerPlugin::projectExplorerSettings().wrapAppOutput);
ow->setMaxLineCount(ProjectExplorerPlugin::projectExplorerSettings().maxAppOutputLines);
ow->setWheelZoomEnabled(TextEditor::TextEditorSettings::behaviorSettings().m_scrollWheelZooming);
ow->setBaseFont(TextEditor::TextEditorSettings::fontSettings().font());
ow->setFontZoom(m_zoom);
connect(ow, &Core::OutputWindow::wheelZoom, this, [this, ow]() {
m_zoom = ow->fontZoom();
foreach (const RunControlTab &tab, m_runControlTabs)
tab.window->setFontZoom(m_zoom);
});
Aggregation::Aggregate *agg = new Aggregation::Aggregate;
agg->add(ow);
agg->add(new Core::BaseTextFind(ow));
m_runControlTabs.push_back(RunControlTab(rc, ow));
m_tabWidget->addTab(ow, rc->displayName());
if (debug)
qDebug() << "OutputPane::createNewOutputWindow: Adding tab for " << rc;
updateCloseActions();
}
void AppOutputPane::handleOldOutput(Core::OutputWindow *window) const
{
if (ProjectExplorerPlugin::projectExplorerSettings().cleanOldAppOutput)
window->clear();
else
window->grayOutOldContent();
}
void AppOutputPane::updateFromSettings()
{
foreach (const RunControlTab &tab, m_runControlTabs) {
tab.window->setWordWrapEnabled(ProjectExplorerPlugin::projectExplorerSettings().wrapAppOutput);
tab.window->setMaxLineCount(ProjectExplorerPlugin::projectExplorerSettings().maxAppOutputLines);
}
}
void AppOutputPane::appendMessage(RunControl *rc, const QString &out, Utils::OutputFormat format)
{
const int index = indexOf(rc);
if (index != -1) {
Core::OutputWindow *window = m_runControlTabs.at(index).window;
window->appendMessage(out, format);
if (format != Utils::NormalMessageFormat) {
if (m_runControlTabs.at(index).behaviorOnOutput == Flash)
flash();
else
popup(NoModeSwitch);
}
}
}
void AppOutputPane::showTabFor(RunControl *rc)
{
m_tabWidget->setCurrentIndex(tabWidgetIndexOf(indexOf(rc)));
}
void AppOutputPane::setBehaviorOnOutput(RunControl *rc, AppOutputPane::BehaviorOnOutput mode)
{
const int index = indexOf(rc);
if (index != -1)
m_runControlTabs[index].behaviorOnOutput = mode;
}
void AppOutputPane::reRunRunControl()
{
const int index = currentIndex();
QTC_ASSERT(index != -1 && !m_runControlTabs.at(index).runControl->isRunning(), return);
RunControlTab &tab = m_runControlTabs[index];
handleOldOutput(tab.window);
tab.window->scrollToBottom();
tab.runControl->initiateReStart();
}
void AppOutputPane::attachToRunControl()
{
const int index = currentIndex();
QTC_ASSERT(index != -1, return);
RunControl *rc = m_runControlTabs.at(index).runControl;
QTC_ASSERT(rc->isRunning(), return);
ExtensionSystem::Invoker<void>(debuggerPlugin(), "attachExternalApplication", rc);
}
void AppOutputPane::stopRunControl()
{
const int index = currentIndex();
QTC_ASSERT(index != -1, return);
RunControl *rc = m_runControlTabs.at(index).runControl;
QTC_ASSERT(rc, return);
if (rc->isRunning() && optionallyPromptToStop(rc))
rc->initiateStop();
else if (rc->isStarting()) {
QTC_CHECK(false);
rc->initiateStop();
}
if (debug)
qDebug() << "OutputPane::stopRunControl " << rc;
}
bool AppOutputPane::closeTabs(CloseTabMode mode)
{
bool allClosed = true;
for (int t = m_tabWidget->count() - 1; t >= 0; t--)
if (!closeTab(t, mode))
allClosed = false;
if (debug)
qDebug() << "OutputPane::closeTabs() returns " << allClosed;
return allClosed;
}
QList<RunControl *> AppOutputPane::allRunControls() const
{
return Utils::transform<QList>(m_runControlTabs,[](const RunControlTab &tab) {
return tab.runControl.data();
});
}
bool AppOutputPane::closeTab(int tabIndex, CloseTabMode closeTabMode)
{
int index = indexOf(m_tabWidget->widget(tabIndex));
QTC_ASSERT(index != -1, return true);
if (debug)
qDebug() << "OutputPane::closeTab tab " << tabIndex << m_runControlTabs[index].runControl
<< m_runControlTabs[index].window;
// Prompt user to stop
if (m_runControlTabs[index].runControl->isRunning()) {
switch (closeTabMode) {
case CloseTabNoPrompt:
break;
case CloseTabWithPrompt:
QWidget *tabWidget = m_tabWidget->widget(tabIndex);
if (!m_runControlTabs[index].runControl->promptToStop())
return false;
// The event loop has run, thus the ordering might have changed, a tab might
// have been closed, so do some strange things...
tabIndex = m_tabWidget->indexOf(tabWidget);
index = indexOf(tabWidget);
if (tabIndex == -1 || index == -1)
return false;
break;
}
if (m_runControlTabs[index].runControl->isRunning()) { // yes it might have stopped already, then just close
m_runControlTabs[index].runControl->initiateStop();
return false;
}
}
m_tabWidget->removeTab(tabIndex);
delete m_runControlTabs[index].window;
m_runControlTabs[index].runControl->initiateFinish(); // Will self-destruct.
m_runControlTabs[index].runControl = 0;
m_runControlTabs.removeAt(index);
updateCloseActions();
if (m_runControlTabs.isEmpty())
hide();
return true;
}
bool AppOutputPane::optionallyPromptToStop(RunControl *runControl)
{
ProjectExplorerSettings settings = ProjectExplorerPlugin::projectExplorerSettings();
if (!runControl->promptToStop(&settings.prompToStopRunControl))
return false;
ProjectExplorerPlugin::setProjectExplorerSettings(settings);
return true;
}
void AppOutputPane::projectRemoved()
{
tabChanged(m_tabWidget->currentIndex());
}
void AppOutputPane::enableDefaultButtons()
{
enableButtons(currentRunControl());
}
void AppOutputPane::zoomIn()
{
foreach (const RunControlTab &tab, m_runControlTabs)
tab.window->zoomIn(1);
if (m_runControlTabs.isEmpty())
return;
m_zoom = m_runControlTabs.first().window->fontZoom();
}
void AppOutputPane::zoomOut()
{
foreach (const RunControlTab &tab, m_runControlTabs)
tab.window->zoomOut(1);
if (m_runControlTabs.isEmpty())
return;
m_zoom = m_runControlTabs.first().window->fontZoom();
}
void AppOutputPane::enableButtons(const RunControl *rc)
{
if (rc) {
const bool isRunning = rc->isRunning();
m_reRunButton->setEnabled(rc->isStopped() && rc->supportsReRunning());
m_reRunButton->setIcon(rc->icon().icon());
m_stopAction->setEnabled(isRunning);
if (isRunning && debuggerPlugin() && rc->applicationProcessHandle().isValid()) {
m_attachButton->setEnabled(true);
Utils::ProcessHandle h = rc->applicationProcessHandle();
QString tip = h.isValid() ? RunControl::tr("PID %1").arg(h.pid())
: RunControl::tr("Invalid");
m_attachButton->setToolTip(msgAttachDebuggerTooltip(tip));
} else {
m_attachButton->setEnabled(false);
m_attachButton->setToolTip(msgAttachDebuggerTooltip());
}
m_zoomInButton->setEnabled(true);
m_zoomOutButton->setEnabled(true);
replaceAllChildWidgets(m_formatterWidget->layout(), rc->outputFormatter() ?
rc->outputFormatter()->toolbarWidgets() :
QList<QWidget *>());
} else {
m_reRunButton->setEnabled(false);
m_reRunButton->setIcon(Utils::Icons::RUN_SMALL_TOOLBAR.icon());
m_attachButton->setEnabled(false);
m_attachButton->setToolTip(msgAttachDebuggerTooltip());
m_stopAction->setEnabled(false);
m_zoomInButton->setEnabled(false);
m_zoomOutButton->setEnabled(false);
}
m_formatterWidget->setVisible(m_formatterWidget->layout()->count());
}
void AppOutputPane::tabChanged(int i)
{
const int index = indexOf(m_tabWidget->widget(i));
if (i != -1 && index != -1) {
enableButtons(m_runControlTabs.at(index).runControl);
} else {
enableDefaultButtons();
}
}
void AppOutputPane::contextMenuRequested(const QPoint &pos, int index)
{
QList<QAction *> actions = QList<QAction *>() << m_closeCurrentTabAction << m_closeAllTabsAction << m_closeOtherTabsAction;
QAction *action = QMenu::exec(actions, m_tabWidget->mapToGlobal(pos), 0, m_tabWidget);
const int currentIdx = index != -1 ? index : currentIndex();
if (action == m_closeCurrentTabAction) {
if (currentIdx >= 0)
closeTab(currentIdx);
} else if (action == m_closeAllTabsAction) {
closeTabs(AppOutputPane::CloseTabWithPrompt);
} else if (action == m_closeOtherTabsAction) {
for (int t = m_tabWidget->count() - 1; t >= 0; t--)
if (t != currentIdx)
closeTab(t);
}
}
void AppOutputPane::slotRunControlChanged()
{
RunControl *current = currentRunControl();
if (current && current == sender())
enableButtons(current); // RunControl::isRunning() cannot be trusted in signal handler.
}
void AppOutputPane::slotRunControlFinished()
{
RunControl *rc = qobject_cast<RunControl *>(sender());
QTimer::singleShot(0, this, [this, rc]() { slotRunControlFinished2(rc); });
if (rc->outputFormatter())
rc->outputFormatter()->flush();
}
void AppOutputPane::slotRunControlFinished2(RunControl *sender)
{
const int senderIndex = indexOf(sender);
// This slot is queued, so the stop() call in closeTab might lead to this slot, after closeTab already cleaned up
if (senderIndex == -1)
return;
// Enable buttons for current
RunControl *current = currentRunControl();
if (debug)
qDebug() << "OutputPane::runControlFinished" << sender << senderIndex
<< " current " << current << m_runControlTabs.size();
if (current && current == sender)
enableButtons(current);
ProjectExplorerPlugin::instance()->updateRunActions();
if (!isRunning())
emit allRunControlsFinished();
}
bool AppOutputPane::isRunning() const
{
return Utils::anyOf(m_runControlTabs, [](const RunControlTab &rt) {
return rt.runControl->isRunning();
});
}
bool AppOutputPane::canNext() const
{
return false;
}
bool AppOutputPane::canPrevious() const
{
return false;
}
void AppOutputPane::goToNext()
{
}
void AppOutputPane::goToPrev()
{
}
bool AppOutputPane::canNavigate() const
{
return false;
}
#include "appoutputpane.moc"