forked from qt-creator/qt-creator
Fix a crash when setting up toolbar editor actions
LanguageClient::updateEditorToolBar() contained the static hashes that were keeping raw pointers to different objects. However, the lifetime of these objects wasn't controlled, so it could happen that we were operating on dangling pointers. In fact, like in the bugreport, some text editor widget could have been deleted (together with his outline action), while later another one (coincidently with the same address) could have appeared. The old mapped widget still pointed to the removed action so we crash. Instead of keeping a static hashes we attach the custom child object to the widget and keep there QPointers to all objects we are interested in. Fixes: QTCREATORBUG-26588 Change-Id: I335d9848ea85372baf3328772f0a249cee242dcd Reviewed-by: David Schulz <david.schulz@qt.io>
This commit is contained in:
@@ -202,6 +202,18 @@ void updateCodeActionRefactoringMarker(Client *client,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static const char clientExtrasName[] = "__qtcreator_client_extras__";
|
||||||
|
|
||||||
|
class ClientExtras : public QObject
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ClientExtras(QObject *parent) : QObject(parent) { setObjectName(clientExtrasName); };
|
||||||
|
|
||||||
|
QPointer<QAction> m_popupAction;
|
||||||
|
QPointer<Client> m_client;
|
||||||
|
QPointer<QAction> m_outlineAction;
|
||||||
|
};
|
||||||
|
|
||||||
void updateEditorToolBar(Core::IEditor *editor)
|
void updateEditorToolBar(Core::IEditor *editor)
|
||||||
{
|
{
|
||||||
auto *textEditor = qobject_cast<BaseTextEditor *>(editor);
|
auto *textEditor = qobject_cast<BaseTextEditor *>(editor);
|
||||||
@@ -214,70 +226,67 @@ void updateEditorToolBar(Core::IEditor *editor)
|
|||||||
TextDocument *document = textEditor->textDocument();
|
TextDocument *document = textEditor->textDocument();
|
||||||
Client *client = LanguageClientManager::clientForDocument(textEditor->textDocument());
|
Client *client = LanguageClientManager::clientForDocument(textEditor->textDocument());
|
||||||
|
|
||||||
static QHash<QWidget *, QAction *> actions;
|
ClientExtras *extras = widget->findChild<ClientExtras *>(clientExtrasName,
|
||||||
|
Qt::FindDirectChildrenOnly);
|
||||||
if (actions.contains(widget)) {
|
if (!extras) {
|
||||||
auto action = actions[widget];
|
if (!client)
|
||||||
|
return;
|
||||||
|
extras = new ClientExtras(widget);
|
||||||
|
}
|
||||||
|
if (extras->m_popupAction) {
|
||||||
if (client) {
|
if (client) {
|
||||||
action->setText(client->name());
|
extras->m_popupAction->setText(client->name());
|
||||||
} else {
|
} else {
|
||||||
widget->toolBar()->removeAction(action);
|
widget->toolBar()->removeAction(extras->m_popupAction);
|
||||||
actions.remove(widget);
|
delete extras->m_popupAction;
|
||||||
delete action;
|
|
||||||
}
|
}
|
||||||
} else if (client) {
|
} else if (client) {
|
||||||
const QIcon icon = Utils::Icon({{":/languageclient/images/languageclient.png",
|
const QIcon icon = Utils::Icon({{":/languageclient/images/languageclient.png",
|
||||||
Utils::Theme::IconsBaseColor}})
|
Utils::Theme::IconsBaseColor}}).icon();
|
||||||
.icon();
|
extras->m_popupAction = widget->toolBar()->addAction(
|
||||||
actions[widget] = widget->toolBar()->addAction(
|
icon, client->name(), [document = QPointer(document)] {
|
||||||
icon, client->name(), [document]() {
|
auto menu = new QMenu;
|
||||||
auto menu = new QMenu;
|
auto clientsGroup = new QActionGroup(menu);
|
||||||
auto clientsGroup = new QActionGroup(menu);
|
clientsGroup->setExclusive(true);
|
||||||
clientsGroup->setExclusive(true);
|
for (auto client : LanguageClientManager::clientsSupportingDocument(document)) {
|
||||||
for (auto client : LanguageClientManager::clientsSupportingDocument(document)) {
|
auto action = clientsGroup->addAction(client->name());
|
||||||
auto action = clientsGroup->addAction(client->name());
|
auto reopen = [action, client = QPointer(client), document] {
|
||||||
auto reopen = [action, client = QPointer<Client>(client), document]() {
|
if (!client)
|
||||||
if (!client)
|
return;
|
||||||
return;
|
LanguageClientManager::openDocumentWithClient(document, client);
|
||||||
LanguageClientManager::openDocumentWithClient(document, client);
|
action->setChecked(true);
|
||||||
action->setChecked(true);
|
};
|
||||||
};
|
action->setCheckable(true);
|
||||||
action->setCheckable(true);
|
action->setChecked(client == LanguageClientManager::clientForDocument(document));
|
||||||
action->setChecked(client == LanguageClientManager::clientForDocument(document));
|
QObject::connect(action, &QAction::triggered, reopen);
|
||||||
QObject::connect(action, &QAction::triggered, reopen);
|
}
|
||||||
}
|
menu->addActions(clientsGroup->actions());
|
||||||
menu->addActions(clientsGroup->actions());
|
if (!clientsGroup->actions().isEmpty())
|
||||||
menu->addAction("Inspect Language Clients", []() {
|
menu->addSeparator();
|
||||||
LanguageClientManager::showInspector();
|
menu->addAction("Inspect Language Clients", [] {
|
||||||
});
|
LanguageClientManager::showInspector();
|
||||||
menu->addAction("Manage...", []() {
|
|
||||||
Core::ICore::showOptionsDialog(Constants::LANGUAGECLIENT_SETTINGS_PAGE);
|
|
||||||
});
|
|
||||||
menu->popup(QCursor::pos());
|
|
||||||
});
|
});
|
||||||
QObject::connect(widget, &QWidget::destroyed, [widget]() {
|
menu->addAction("Manage...", [] {
|
||||||
actions.remove(widget);
|
Core::ICore::showOptionsDialog(Constants::LANGUAGECLIENT_SETTINGS_PAGE);
|
||||||
|
});
|
||||||
|
menu->popup(QCursor::pos());
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
static QHash<QWidget *, QPair<Client *, QAction *>> outlines;
|
if (!extras->m_client || extras->m_client != client ||
|
||||||
|
!LanguageClientOutlineWidgetFactory::clientSupportsDocumentSymbols(client, document)) {
|
||||||
if (outlines.contains(widget)) {
|
if (extras->m_outlineAction) {
|
||||||
auto outline = outlines[widget];
|
widget->toolBar()->removeAction(extras->m_outlineAction);
|
||||||
if (outline.first != client
|
delete extras->m_outlineAction;
|
||||||
|| !LanguageClientOutlineWidgetFactory::clientSupportsDocumentSymbols(client,
|
|
||||||
document)) {
|
|
||||||
auto oldAction = outline.second;
|
|
||||||
widget->toolBar()->removeAction(oldAction);
|
|
||||||
delete oldAction;
|
|
||||||
outlines.remove(widget);
|
|
||||||
}
|
}
|
||||||
|
extras->m_client.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!outlines.contains(widget)) {
|
if (!extras->m_client) {
|
||||||
if (QWidget *comboBox = LanguageClientOutlineWidgetFactory::createComboBox(client, editor)) {
|
if (QWidget *comboBox = LanguageClientOutlineWidgetFactory::createComboBox(client, editor)) {
|
||||||
outlines[widget] = {client,
|
extras->m_client = client;
|
||||||
widget->insertExtraToolBarWidget(TextEditorWidget::Left, comboBox)};
|
extras->m_outlineAction = widget->insertExtraToolBarWidget(TextEditorWidget::Left,
|
||||||
|
comboBox);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user