ProjectExplorer: Fix index confusion in app output pane

There's a QTabWidget and a list of RunControlTabs, whose indexes got
confused in several places.
Fix this and decrease the likelihood of the problem occurring again by
never using indexes for the RunControlTab list.

Fixes: QTCREATORBUG-27743
Change-Id: I8a83b0c802fe76a09b4eba0da999a52fdeb6ddac
Reviewed-by: <github-actions-qt-creator@cristianadam.eu>
Reviewed-by: Christian Stenger <christian.stenger@qt.io>
This commit is contained in:
Christian Kandeler
2022-07-05 18:09:19 +02:00
parent fd41105191
commit 7f94e68b74
2 changed files with 105 additions and 100 deletions

View File

@@ -259,42 +259,44 @@ AppOutputPane::~AppOutputPane()
delete m_handler; delete m_handler;
} }
int AppOutputPane::currentIndex() const AppOutputPane::RunControlTab *AppOutputPane::currentTab()
{ {
if (const QWidget *w = m_tabWidget->currentWidget()) return tabFor(m_tabWidget->currentWidget());
return indexOf(w); }
return -1;
const AppOutputPane::RunControlTab *AppOutputPane::currentTab() const
{
return tabFor(m_tabWidget->currentWidget());
} }
RunControl *AppOutputPane::currentRunControl() const RunControl *AppOutputPane::currentRunControl() const
{ {
const int index = currentIndex(); if (const RunControlTab * const tab = currentTab())
if (index != -1) return tab->runControl;
return m_runControlTabs.at(index).runControl;
return nullptr; return nullptr;
} }
int AppOutputPane::indexOf(const RunControl *rc) const AppOutputPane::RunControlTab *AppOutputPane::tabFor(const RunControl *rc)
{ {
for (int i = m_runControlTabs.size() - 1; i >= 0; i--) const auto it = std::find_if(m_runControlTabs.begin(), m_runControlTabs.end(),
if (m_runControlTabs.at(i).runControl == rc) [rc](RunControlTab &t) { return t.runControl == rc; });
return i; if (it == m_runControlTabs.end())
return -1; return nullptr;
return it;
} }
int AppOutputPane::indexOf(const QWidget *outputWindow) const AppOutputPane::RunControlTab *AppOutputPane::tabFor(const QWidget *outputWindow)
{ {
for (int i = m_runControlTabs.size() - 1; i >= 0; i--) const auto it = std::find_if(m_runControlTabs.begin(), m_runControlTabs.end(),
if (m_runControlTabs.at(i).window == outputWindow) [outputWindow](RunControlTab &t) { return t.window == outputWindow; });
return i; if (it == m_runControlTabs.end())
return -1; return nullptr;
return it;
} }
int AppOutputPane::tabWidgetIndexOf(int runControlIndex) const const AppOutputPane::RunControlTab *AppOutputPane::tabFor(const QWidget *outputWindow) const
{ {
if (runControlIndex >= 0 && runControlIndex < m_runControlTabs.size()) return const_cast<AppOutputPane *>(this)->tabFor(outputWindow);
return m_tabWidget->indexOf(m_runControlTabs.at(runControlIndex).window);
return -1;
} }
void AppOutputPane::updateCloseActions() void AppOutputPane::updateCloseActions()
@@ -366,10 +368,9 @@ void AppOutputPane::setFocus()
void AppOutputPane::updateFilter() void AppOutputPane::updateFilter()
{ {
const int index = currentIndex(); if (RunControlTab * const tab = currentTab()) {
if (index != -1) { tab->window->updateFilterProperties(filterText(), filterCaseSensitivity(),
m_runControlTabs.at(index).window->updateFilterProperties( filterUsesRegexp(), filterIsInverted());
filterText(), filterCaseSensitivity(), filterUsesRegexp(), filterIsInverted());
} }
} }
@@ -409,28 +410,30 @@ void AppOutputPane::createNewOutputWindow(RunControl *rc)
const CommandLine thisCommand = rc->commandLine(); const CommandLine thisCommand = rc->commandLine();
const FilePath thisWorkingDirectory = rc->workingDirectory(); const FilePath thisWorkingDirectory = rc->workingDirectory();
const Environment thisEnvironment = rc->environment(); const Environment thisEnvironment = rc->environment();
const int tabIndex = Utils::indexOf(m_runControlTabs, [&](const RunControlTab &tab) { const auto tab = std::find_if(m_runControlTabs.begin(), m_runControlTabs.end(),
[&](const RunControlTab &tab) {
if (!tab.runControl || tab.runControl->isRunning()) if (!tab.runControl || tab.runControl->isRunning())
return false; return false;
return thisCommand == tab.runControl->commandLine() return thisCommand == tab.runControl->commandLine()
&& thisWorkingDirectory == tab.runControl->workingDirectory() && thisWorkingDirectory == tab.runControl->workingDirectory()
&& thisEnvironment == tab.runControl->environment(); && thisEnvironment == tab.runControl->environment();
}); });
if (tabIndex != -1) { if (tab != m_runControlTabs.end()) {
RunControlTab &tab = m_runControlTabs[tabIndex];
// Reuse this tab // Reuse this tab
if (tab.runControl) if (tab->runControl)
tab.runControl->initiateFinish(); tab->runControl->initiateFinish();
tab.runControl = rc; tab->runControl = rc;
tab.window->reset(); tab->window->reset();
rc->setupFormatter(tab.window->outputFormatter()); rc->setupFormatter(tab->window->outputFormatter());
handleOldOutput(tab.window); handleOldOutput(tab->window);
// Update the title. // Update the title.
const int tabIndex = m_tabWidget->indexOf(tab->window);
QTC_ASSERT(tabIndex != -1, return);
m_tabWidget->setTabText(tabIndex, rc->displayName()); m_tabWidget->setTabText(tabIndex, rc->displayName());
tab.window->scrollToBottom(); tab->window->scrollToBottom();
qCDebug(appOutputLog) << "AppOutputPane::createNewOutputWindow: Reusing tab" qCDebug(appOutputLog) << "AppOutputPane::createNewOutputWindow: Reusing tab"
<< tabIndex << "for" << rc; << tabIndex << "for" << rc;
return; return;
@@ -492,29 +495,29 @@ void AppOutputPane::updateFromSettings()
void AppOutputPane::appendMessage(RunControl *rc, const QString &out, OutputFormat format) void AppOutputPane::appendMessage(RunControl *rc, const QString &out, OutputFormat format)
{ {
const int index = indexOf(rc); RunControlTab * const tab = tabFor(rc);
if (index != -1) { if (!tab)
Core::OutputWindow *window = m_runControlTabs.at(index).window; return;
QString stringToWrite;
if (format == NormalMessageFormat || format == ErrorMessageFormat) { QString stringToWrite;
stringToWrite = QTime::currentTime().toString(); if (format == NormalMessageFormat || format == ErrorMessageFormat) {
stringToWrite += ": "; stringToWrite = QTime::currentTime().toString();
} stringToWrite += ": ";
stringToWrite += out; }
window->appendMessage(stringToWrite, format); stringToWrite += out;
if (format != NormalMessageFormat) { tab->window->appendMessage(stringToWrite, format);
RunControlTab &tab = m_runControlTabs[index];
switch (tab.behaviorOnOutput) { if (format != NormalMessageFormat) {
case AppOutputPaneMode::FlashOnOutput: switch (tab->behaviorOnOutput) {
flash(); case AppOutputPaneMode::FlashOnOutput:
break; flash();
case AppOutputPaneMode::PopupOnFirstOutput: break;
tab.behaviorOnOutput = AppOutputPaneMode::FlashOnOutput; case AppOutputPaneMode::PopupOnFirstOutput:
Q_FALLTHROUGH(); tab->behaviorOnOutput = AppOutputPaneMode::FlashOnOutput;
case AppOutputPaneMode::PopupOnOutput: Q_FALLTHROUGH();
popup(NoModeSwitch); case AppOutputPaneMode::PopupOnOutput:
break; popup(NoModeSwitch);
} break;
} }
} }
} }
@@ -567,42 +570,39 @@ void AppOutputPane::loadSettings()
void AppOutputPane::showTabFor(RunControl *rc) void AppOutputPane::showTabFor(RunControl *rc)
{ {
m_tabWidget->setCurrentIndex(tabWidgetIndexOf(indexOf(rc))); if (RunControlTab * const tab = tabFor(rc))
m_tabWidget->setCurrentWidget(tab->window);
} }
void AppOutputPane::setBehaviorOnOutput(RunControl *rc, AppOutputPaneMode mode) void AppOutputPane::setBehaviorOnOutput(RunControl *rc, AppOutputPaneMode mode)
{ {
const int index = indexOf(rc); if (RunControlTab * const tab = tabFor(rc))
if (index != -1) tab->behaviorOnOutput = mode;
m_runControlTabs[index].behaviorOnOutput = mode;
} }
void AppOutputPane::reRunRunControl() void AppOutputPane::reRunRunControl()
{ {
const int index = currentIndex(); RunControlTab * const tab = currentTab();
const RunControlTab &tab = m_runControlTabs.at(index); QTC_ASSERT(tab, return);
QTC_ASSERT(tab.runControl, return); QTC_ASSERT(tab->runControl, return);
QTC_ASSERT(index != -1 && !tab.runControl->isRunning(), return); QTC_ASSERT(!tab->runControl->isRunning(), return);
handleOldOutput(tab.window); handleOldOutput(tab->window);
tab.window->scrollToBottom(); tab->window->scrollToBottom();
tab.runControl->initiateReStart(); tab->runControl->initiateReStart();
} }
void AppOutputPane::attachToRunControl() void AppOutputPane::attachToRunControl()
{ {
const int index = currentIndex(); RunControl * const rc = currentRunControl();
QTC_ASSERT(index != -1, return); QTC_ASSERT(rc, return);
RunControl *rc = m_runControlTabs.at(index).runControl; QTC_ASSERT(rc->isRunning(), return);
QTC_ASSERT(rc && rc->isRunning(), return);
ExtensionSystem::Invoker<void>(debuggerPlugin(), "attachExternalApplication", rc); ExtensionSystem::Invoker<void>(debuggerPlugin(), "attachExternalApplication", rc);
} }
void AppOutputPane::stopRunControl() void AppOutputPane::stopRunControl()
{ {
const int index = currentIndex(); RunControl * const rc = currentRunControl();
QTC_ASSERT(index != -1, return);
RunControl *rc = m_runControlTabs.at(index).runControl;
QTC_ASSERT(rc, return); QTC_ASSERT(rc, return);
if (rc->isRunning()) { if (rc->isRunning()) {
@@ -632,22 +632,22 @@ QList<RunControl *> AppOutputPane::allRunControls() const
void AppOutputPane::closeTab(int tabIndex, CloseTabMode closeTabMode) void AppOutputPane::closeTab(int tabIndex, CloseTabMode closeTabMode)
{ {
int index = indexOf(m_tabWidget->widget(tabIndex)); QWidget * const tabWidget = m_tabWidget->widget(tabIndex);
QTC_ASSERT(index != -1, return); RunControlTab *tab = tabFor(tabWidget);
QTC_ASSERT(tab, return);
RunControl *runControl = m_runControlTabs[index].runControl; RunControl *runControl = tab->runControl;
Core::OutputWindow *window = m_runControlTabs[index].window; Core::OutputWindow *window = tab->window;
qCDebug(appOutputLog) << "AppOutputPane::closeTab tab" << tabIndex << runControl << window; qCDebug(appOutputLog) << "AppOutputPane::closeTab tab" << tabIndex << runControl << window;
// Prompt user to stop // Prompt user to stop
if (closeTabMode == CloseTabWithPrompt) { if (closeTabMode == CloseTabWithPrompt) {
QWidget *tabWidget = m_tabWidget->widget(tabIndex);
if (runControl && runControl->isRunning() && !runControl->promptToStop()) if (runControl && runControl->isRunning() && !runControl->promptToStop())
return; return;
// The event loop has run, thus the ordering might have changed, a tab might // The event loop has run, thus the ordering might have changed, a tab might
// have been closed, so do some strange things... // have been closed, so do some strange things...
tabIndex = m_tabWidget->indexOf(tabWidget); tabIndex = m_tabWidget->indexOf(tabWidget);
index = indexOf(tabWidget); tab = tabFor(tabWidget);
if (tabIndex == -1 || index == -1) if (tabIndex == -1 || !tab)
return; return;
} }
@@ -656,7 +656,8 @@ void AppOutputPane::closeTab(int tabIndex, CloseTabMode closeTabMode)
if (runControl) if (runControl)
runControl->initiateFinish(); // Will self-destruct. runControl->initiateFinish(); // Will self-destruct.
m_runControlTabs.removeAt(index); Utils::erase(m_runControlTabs, [tab](const RunControlTab &t) {
return t.runControl == tab->runControl; });
updateCloseActions(); updateCloseActions();
setFilteringEnabled(m_tabWidget->count() > 0); setFilteringEnabled(m_tabWidget->count() > 0);
@@ -732,12 +733,11 @@ void AppOutputPane::enableButtons(const RunControl *rc)
void AppOutputPane::tabChanged(int i) void AppOutputPane::tabChanged(int i)
{ {
const int index = indexOf(m_tabWidget->widget(i)); RunControlTab * const controlTab = tabFor(m_tabWidget->widget(i));
if (i != -1 && index != -1) { if (i != -1 && controlTab) {
const RunControlTab &controlTab = m_runControlTabs[index]; controlTab->window->updateFilterProperties(filterText(), filterCaseSensitivity(),
controlTab.window->updateFilterProperties(filterText(), filterCaseSensitivity(), filterUsesRegexp(), filterIsInverted());
filterUsesRegexp(), filterIsInverted()); enableButtons(controlTab->runControl);
enableButtons(controlTab.runControl);
} else { } else {
enableDefaultButtons(); enableDefaultButtons();
} }
@@ -747,12 +747,15 @@ void AppOutputPane::contextMenuRequested(const QPoint &pos, int index)
{ {
const QList<QAction *> actions = {m_closeCurrentTabAction, m_closeAllTabsAction, m_closeOtherTabsAction}; const QList<QAction *> actions = {m_closeCurrentTabAction, m_closeAllTabsAction, m_closeOtherTabsAction};
QAction *action = QMenu::exec(actions, m_tabWidget->mapToGlobal(pos), nullptr, m_tabWidget); QAction *action = QMenu::exec(actions, m_tabWidget->mapToGlobal(pos), nullptr, m_tabWidget);
const int currentIdx = index != -1 ? index : currentIndex(); if (action == m_closeAllTabsAction) {
closeTabs(AppOutputPane::CloseTabWithPrompt);
return;
}
const int currentIdx = index != -1 ? index : m_tabWidget->currentIndex();
if (action == m_closeCurrentTabAction) { if (action == m_closeCurrentTabAction) {
if (currentIdx >= 0) if (currentIdx >= 0)
closeTab(currentIdx); closeTab(currentIdx);
} else if (action == m_closeAllTabsAction) {
closeTabs(AppOutputPane::CloseTabWithPrompt);
} else if (action == m_closeOtherTabsAction) { } else if (action == m_closeOtherTabsAction) {
for (int t = m_tabWidget->count() - 1; t >= 0; t--) for (int t = m_tabWidget->count() - 1; t >= 0; t--)
if (t != currentIdx) if (t != currentIdx)
@@ -781,16 +784,17 @@ void AppOutputPane::slotRunControlFinished()
void AppOutputPane::slotRunControlFinished2(RunControl *sender) void AppOutputPane::slotRunControlFinished2(RunControl *sender)
{ {
const int senderIndex = indexOf(sender); const RunControlTab * const tab = tabFor(sender);
// This slot is queued, so the stop() call in closeTab might lead to this slot, after closeTab already cleaned up // This slot is queued, so the stop() call in closeTab might lead to this slot, after closeTab already cleaned up
if (senderIndex == -1) if (!tab)
return; return;
// Enable buttons for current // Enable buttons for current
RunControl *current = currentRunControl(); RunControl *current = currentRunControl();
qCDebug(appOutputLog) << "AppOutputPane::runControlFinished" << sender << senderIndex qCDebug(appOutputLog) << "AppOutputPane::runControlFinished" << sender
<< m_tabWidget->indexOf(tab->window)
<< "current" << current << m_runControlTabs.size(); << "current" << current << m_runControlTabs.size();
if (current && current == sender) if (current && current == sender)

View File

@@ -132,11 +132,12 @@ private:
void closeTab(int index, CloseTabMode cm = CloseTabWithPrompt); void closeTab(int index, CloseTabMode cm = CloseTabWithPrompt);
bool optionallyPromptToStop(RunControl *runControl); bool optionallyPromptToStop(RunControl *runControl);
int indexOf(const RunControl *) const; RunControlTab *tabFor(const RunControl *rc);
int indexOf(const QWidget *outputWindow) const; RunControlTab *tabFor(const QWidget *outputWindow);
int currentIndex() const; const RunControlTab *tabFor(const QWidget *outputWindow) const;
RunControlTab *currentTab();
const RunControlTab *currentTab() const;
RunControl *currentRunControl() const; RunControl *currentRunControl() const;
int tabWidgetIndexOf(int runControlIndex) const;
void handleOldOutput(Core::OutputWindow *window) const; void handleOldOutput(Core::OutputWindow *window) const;
void updateCloseActions(); void updateCloseActions();
void updateFilter() override; void updateFilter() override;