Debugger: Fix async tooltip handling

For now, only the gdb engine can handle complex tooltips
requiring async re-evaluation, cdb and lldb will show
and expand only items that are available in the Locals view.

This patch disables also the save/restore feature for
pinned tooltips.

Task-number: QTCREATORBUG-13255
Task-number: QTCREATORBUG-13052
Change-Id: Ic25616fede0f5c4343a92b631f01e60bfc5e9d81
Reviewed-by: David Schulz <david.schulz@theqtcompany.com>
Reviewed-by: Christian Stenger <christian.stenger@theqtcompany.com>
This commit is contained in:
hjk
2014-11-11 11:00:32 +01:00
parent 620ded8093
commit 011ddc09c4
11 changed files with 425 additions and 402 deletions

View File

@@ -442,28 +442,14 @@ void CdbEngine::syncVerboseLog(bool verboseLog)
} }
bool CdbEngine::setToolTipExpression(TextEditor::TextEditorWidget *editorWidget, bool CdbEngine::setToolTipExpression(TextEditor::TextEditorWidget *editorWidget,
const DebuggerToolTipContext &contextIn) const DebuggerToolTipContext &context)
{ {
if (debug) Q_UNUSED(editorWidget);
qDebug() << Q_FUNC_INFO; Q_UNUSED(context);
// Need a stopped debuggee and a cpp file in a valid frame // Tooltips matching local variables are already handled in the
if (state() != InferiorStopOk || !isCppEditor(editorWidget) || stackHandler()->currentIndex() < 0) // base class. We don't handle anything else here in CDB
return false; // as it can slow debugging down.
// Determine expression and function return false;
int line;
int column;
DebuggerToolTipContext context = contextIn;
QString exp = fixCppExpression(cppExpressionAt(editorWidget, context.position, &line, &column, &context.function));
// Are we in the current stack frame
if (context.function.isEmpty() || exp.isEmpty() || context.function != stackHandler()->currentFrame().function)
return false;
// Show tooltips of local variables only. Anything else can slow debugging down.
const WatchData *localVariable = watchHandler()->findCppLocalVariable(exp);
if (!localVariable)
return false;
context.iname = localVariable->iname;
DebuggerToolTipManager::showToolTip(context, this);
return true;
} }
// Determine full path to the CDB extension library. // Determine full path to the CDB extension library.

View File

@@ -78,7 +78,7 @@ public:
// Factory function that returns 0 if the debug engine library cannot be found. // Factory function that returns 0 if the debug engine library cannot be found.
virtual bool setToolTipExpression(TextEditor::TextEditorWidget *editorWidget, virtual bool setToolTipExpression(TextEditor::TextEditorWidget *editorWidget,
const DebuggerToolTipContext &ctx); const DebuggerToolTipContext &context);
virtual void setupEngine(); virtual void setupEngine();
virtual void setupInferior(); virtual void setupInferior();
virtual void runEngine(); virtual void runEngine();

View File

@@ -269,6 +269,7 @@ public slots:
m_watchHandler.resetLocation(); m_watchHandler.resetLocation();
m_threadsHandler.resetLocation(); m_threadsHandler.resetLocation();
m_disassemblerAgent.resetLocation(); m_disassemblerAgent.resetLocation();
DebuggerToolTipManager::resetLocation();
} }
public: public:

View File

@@ -47,6 +47,7 @@
#include <utils/tooltip/tipcontents.h> #include <utils/tooltip/tipcontents.h>
#include <utils/qtcassert.h> #include <utils/qtcassert.h>
#include <QAbstractItemModel>
#include <QApplication> #include <QApplication>
#include <QClipboard> #include <QClipboard>
#include <QDebug> #include <QDebug>
@@ -68,9 +69,8 @@ using namespace TextEditor;
namespace Debugger { namespace Debugger {
namespace Internal { namespace Internal {
class DebuggerToolTipWidget; //#define DEBUG(x) qDebug() << x
QList<QPointer<DebuggerToolTipWidget>> m_tooltips; #define DEBUG(x)
bool m_debugModeActive;
// Expire tooltips after n days on (no longer load them) in order // Expire tooltips after n days on (no longer load them) in order
// to avoid them piling up. // to avoid them piling up.
@@ -98,12 +98,11 @@ const char modelColumnCountAttributeC[] = "columncount";
const char modelRowElementC[] = "row"; const char modelRowElementC[] = "row";
const char modelItemElementC[] = "item"; const char modelItemElementC[] = "item";
static void purgeClosedToolTips() static void purgeClosedToolTips();
{
for (int i = m_tooltips.size(); --i >= 0; ) class DebuggerToolTipHolder;
if (!m_tooltips.at(i)) QList<QPointer<DebuggerToolTipHolder>> m_tooltips;
m_tooltips.removeAt(i); bool m_debugModeActive;
}
// Forward a stream reader across end elements looking for the // Forward a stream reader across end elements looking for the
// next start element of a desired type. // next start element of a desired type.
@@ -407,7 +406,7 @@ static QDebug operator<<(QDebug d, const QAbstractItemModel &model)
QTextStream str(&s); QTextStream str(&s);
Debugger::Internal::DumpTreeModelVisitor v(&model, Debugger::Internal::DumpTreeModelVisitor::DebugMode, str); Debugger::Internal::DumpTreeModelVisitor v(&model, Debugger::Internal::DumpTreeModelVisitor::DebugMode, str);
v.run(); v.run();
qDebug().nospace() << s; qCDebug(tooltip).nospace() << s;
return d; return d;
} }
*/ */
@@ -443,7 +442,7 @@ public:
{ {
const QModelIndex nameIndex = sourceModel()->index(sourceRow, 0, sourceParent); const QModelIndex nameIndex = sourceModel()->index(sourceRow, 0, sourceParent);
const QByteArray iname = nameIndex.data(LocalsINameRole).toByteArray(); const QByteArray iname = nameIndex.data(LocalsINameRole).toByteArray();
// qDebug() << "ACCEPTING FILTER" << iname // DEBUG("ACCEPTING FILTER" << iname
// << (iname == m_iname || isSubIname(iname, m_iname) || isSubIname(m_iname, iname)); // << (iname == m_iname || isSubIname(iname, m_iname) || isSubIname(m_iname, iname));
return iname == m_iname || isSubIname(iname, m_iname) || isSubIname(m_iname, iname); return iname == m_iname || isSubIname(iname, m_iname) || isSubIname(m_iname, iname);
} }
@@ -612,68 +611,149 @@ QString DebuggerToolTipManager::treeModelClipboardContents(const QAbstractItemMo
class DebuggerToolTipWidget : public QWidget class DebuggerToolTipWidget : public QWidget
{ {
public: public:
DebuggerToolTipWidget(const DebuggerToolTipContext &context); DebuggerToolTipWidget()
{
setAttribute(Qt::WA_DeleteOnClose);
bool isPinned() const { return m_isPinned; } isPinned = false;
QString fileName() const { return m_context.fileName; } const QIcon pinIcon(QLatin1String(":/debugger/images/pin.xpm"));
QString function() const { return m_context.function; }
int position() const { return m_context.position; }
const DebuggerToolTipContext &context() const { return m_context; } pinButton = new QToolButton;
pinButton->setIcon(pinIcon);
auto copyButton = new QToolButton;
copyButton->setIcon(QIcon(QLatin1String(Core::Constants::ICON_COPY)));
titleLabel = new DraggableLabel(this);
titleLabel->setMinimumWidth(40); // Ensure a draggable area even if text is empty.
titleLabel->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Preferred);
auto toolBar = new QToolBar(this);
toolBar->setProperty("_q_custom_style_disabled", QVariant(true));
const QList<QSize> pinIconSizes = pinIcon.availableSizes();
if (!pinIconSizes.isEmpty())
toolBar->setIconSize(pinIconSizes.front());
toolBar->addWidget(pinButton);
toolBar->addWidget(copyButton);
toolBar->addWidget(titleLabel);
treeView = new DebuggerToolTipTreeView(this);
treeView->setFocusPolicy(Qt::NoFocus);
auto mainLayout = new QVBoxLayout(this);
mainLayout->setSizeConstraint(QLayout::SetFixedSize);
mainLayout->setContentsMargins(0, 0, 0, 0);
mainLayout->addWidget(toolBar);
mainLayout->addWidget(treeView);
connect(copyButton, &QAbstractButton::clicked, [this] {
QString clipboardText = DebuggerToolTipManager::treeModelClipboardContents(treeView->model());
QClipboard *clipboard = QApplication::clipboard();
clipboard->setText(clipboardText, QClipboard::Selection);
clipboard->setText(clipboardText, QClipboard::Clipboard);
});
DEBUG("CREATE DEBUGGERTOOLTIP WIDGET");
}
~DebuggerToolTipWidget()
{
DEBUG("DESTROY DEBUGGERTOOLTIP WIDGET");
}
void closeEvent(QCloseEvent *)
{
DEBUG("CLOSE DEBUGGERTOOLTIP WIDGET");
}
void enterEvent(QEvent *)
{
DEBUG("ENTER DEBUGGERTOOLTIP WIDGET");
}
void leaveEvent(QEvent *)
{
DEBUG("LEAVE DEBUGGERTOOLTIP WIDGET");
if (BaseTextEditor *editor = BaseTextEditor::currentTextEditor())
editor->editorWidget()->activateWindow();
}
void pin()
{
if (isPinned)
return;
isPinned = true;
pinButton->setIcon(style()->standardIcon(QStyle::SP_DockWidgetCloseButton));
if (parentWidget()) {
// We are currently within a text editor tooltip:
// Rip out of parent widget and re-show as a tooltip
Utils::WidgetContent::pinToolTip(this);
} else {
// We have just be restored from session data.
setWindowFlags(Qt::ToolTip);
}
titleLabel->active = true; // User can now drag
}
public:
bool isPinned;
QToolButton *pinButton;
DraggableLabel *titleLabel;
DebuggerToolTipTreeView *treeView;
};
/////////////////////////////////////////////////////////////////////////
//
// DebuggerToolTipHolder
//
/////////////////////////////////////////////////////////////////////////
class DebuggerToolTipHolder : public QObject
{
public:
DebuggerToolTipHolder(const DebuggerToolTipContext &context);
~DebuggerToolTipHolder();
enum State { New, Pending, Acquired, Released };
void acquireEngine(); void acquireEngine();
void releaseEngine(); void releaseEngine();
void saveSessionData(QXmlStreamWriter &w) const; void saveSessionData(QXmlStreamWriter &w) const;
void setWatchModel(WatchModelBase *watchModel);
void handleStackFrameCompleted(const QString &frameFile, const QString &frameFunction); void handleStackFrameCompleted(const QString &frameFile, const QString &frameFunction);
void copy();
void positionShow(const TextEditorWidget *editorWidget); void positionShow(const TextEditorWidget *editorWidget);
void pin();
void handleItemIsExpanded(const QModelIndex &sourceIdx) void handleItemIsExpanded(const QModelIndex &sourceIdx);
{ void updateTooltip(const QString &frameFile, const QString &frameFunction);
QTC_ASSERT(m_filterModel.sourceModel() == sourceIdx.model(), return);
QModelIndex mappedIdx = m_filterModel.mapFromSource(sourceIdx); void setState(State newState);
if (!m_treeView->isExpanded(mappedIdx))
m_treeView->expand(mappedIdx);
}
public: public:
bool m_isPinned; QPointer<DebuggerToolTipWidget> widget;
QToolButton *m_toolButton; QPointer<DebuggerEngine> engine;
DraggableLabel *m_titleLabel; QDate creationDate;
QDate m_creationDate; DebuggerToolTipContext context;
DebuggerToolTipTreeView *m_treeView; //!< Pointing to either m_defaultModel oder m_filterModel TooltipFilterModel filterModel; //!< Pointing to a valid watchModel
DebuggerToolTipContext m_context; QStandardItemModel defaultModel;
TooltipFilterModel m_filterModel; //!< Pointing to a valid watchModel
QStandardItemModel m_defaultModel; State state;
}; };
static void hideAllToolTips() static void hideAllToolTips()
{ {
purgeClosedToolTips(); purgeClosedToolTips();
foreach (const QPointer<DebuggerToolTipWidget> &tw, m_tooltips) foreach (const DebuggerToolTipHolder *tooltip, m_tooltips)
tw->hide(); tooltip->widget->hide();
} }
void DebuggerToolTipWidget::pin() void DebuggerToolTipHolder::handleItemIsExpanded(const QModelIndex &sourceIdx)
{ {
if (m_isPinned) QTC_ASSERT(filterModel.sourceModel() == sourceIdx.model(), return);
return; QModelIndex mappedIdx = filterModel.mapFromSource(sourceIdx);
m_isPinned = true; QTC_ASSERT(widget.data(), return);
m_toolButton->setIcon(style()->standardIcon(QStyle::SP_DockWidgetCloseButton)); if (!widget->treeView->isExpanded(mappedIdx))
widget->treeView->expand(mappedIdx);
if (parentWidget()) {
// We are currently within a text editor tooltip:
// Rip out of parent widget and re-show as a tooltip
Utils::WidgetContent::pinToolTip(this);
} else {
// We have just be restored from session data.
setWindowFlags(Qt::ToolTip);
}
m_titleLabel->active = true; // User can now drag
} }
/*! /*!
@@ -752,159 +832,154 @@ QDebug operator<<(QDebug d, const DebuggerToolTipContext &c)
of them. On closing or session changes, the contents it saved. of them. On closing or session changes, the contents it saved.
*/ */
DebuggerToolTipHolder::DebuggerToolTipHolder(const DebuggerToolTipContext &context_)
static QString msgReleasedText() { return DebuggerToolTipWidget::tr("Previous"); }
DebuggerToolTipWidget::DebuggerToolTipWidget(const DebuggerToolTipContext &context)
{ {
setFocusPolicy(Qt::NoFocus); widget = new DebuggerToolTipWidget;
widget->setObjectName(QLatin1String("DebuggerTreeViewToolTipWidget: ") + QLatin1String(context_.iname));
m_isPinned = false; context = context_;
m_context = context; context.creationDate = QDate::currentDate();
m_filterModel.m_iname = context.iname;
const QIcon pinIcon(QLatin1String(":/debugger/images/pin.xpm")); state = New;
m_toolButton = new QToolButton; filterModel.m_iname = context.iname;
m_toolButton->setIcon(pinIcon);
auto copyButton = new QToolButton; QObject::connect(widget->pinButton, &QAbstractButton::clicked, [this] {
copyButton->setIcon(QIcon(QLatin1String(Core::Constants::ICON_COPY))); if (widget->isPinned) {
widget->close();
m_titleLabel = new DraggableLabel(this); } else {
m_titleLabel->setText(msgReleasedText()); widget->pin();
m_titleLabel->setMinimumWidth(40); // Ensure a draggable area even if text is empty. }
m_titleLabel->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Preferred);
auto toolBar = new QToolBar(this);
toolBar->setProperty("_q_custom_style_disabled", QVariant(true));
const QList<QSize> pinIconSizes = pinIcon.availableSizes();
if (!pinIconSizes.isEmpty())
toolBar->setIconSize(pinIconSizes.front());
toolBar->addWidget(m_toolButton);
toolBar->addWidget(copyButton);
toolBar->addWidget(m_titleLabel);
m_treeView = new DebuggerToolTipTreeView(this);
m_treeView->setFocusPolicy(Qt::NoFocus);
auto mainLayout = new QVBoxLayout(this);
mainLayout->setSizeConstraint(QLayout::SetFixedSize);
mainLayout->setContentsMargins(0, 0, 0, 0);
mainLayout->addWidget(toolBar);
mainLayout->addWidget(m_treeView);
connect(m_toolButton, &QAbstractButton::clicked, [this]() {
if (m_isPinned)
close();
else
pin();
}); });
DEBUG("CREATE DEBUGGERTOOLTIPHOLDER" << context.iname);
connect(copyButton, &QAbstractButton::clicked, this, &DebuggerToolTipWidget::copy);
} }
void DebuggerToolTipWidget::setWatchModel(WatchModelBase *watchModel) DebuggerToolTipHolder::~DebuggerToolTipHolder()
{ {
QTC_ASSERT(watchModel, return); DEBUG("DESTROY DEBUGGERTOOLTIPHOLDER" << context.iname << " STATE: " << state);
m_filterModel.setSourceModel(watchModel); delete widget; widget.clear();
connect(watchModel, &WatchModelBase::itemIsExpanded,
this, &DebuggerToolTipWidget::handleItemIsExpanded, Qt::UniqueConnection);
connect(watchModel, &WatchModelBase::columnAdjustmentRequested,
m_treeView, &DebuggerToolTipTreeView::computeSize, Qt::UniqueConnection);
} }
void DebuggerToolTipWidget::handleStackFrameCompleted(const QString &frameFile, const QString &frameFunction) void DebuggerToolTipHolder::updateTooltip(const QString &frameFile, const QString &frameFunction)
{ {
const bool sameFrame = m_context.matchesFrame(frameFile, frameFunction); const bool sameFrame = context.matchesFrame(frameFile, frameFunction);
const bool isAcquired = m_treeView->model() == &m_filterModel; DEBUG("UPDATE TOOLTIP: STATE " << state << context.iname
if (isAcquired && !sameFrame) << "PINNED: " << widget->isPinned << "SAME FRAME: " << sameFrame);
releaseEngine();
else if (!isAcquired && sameFrame)
acquireEngine();
if (isAcquired) { if (state == Pending) {
m_treeView->expand(m_filterModel.index(0, 0)); acquireEngine();
WatchTreeView::reexpand(m_treeView, m_filterModel.index(0, 0)); } else if (state == Acquired && !sameFrame) {
releaseEngine();
} else if (state == Released && sameFrame) {
acquireEngine();
}
if (!widget->isPinned) {
DEBUG("SHOW UNPINNED");
const Utils::WidgetContent widgetContent(widget, true);
Utils::ToolTip::show(context.mousePosition, widgetContent, Internal::mainWindow());
}
if (state == Acquired) {
// Save data to stream and restore to the backup m_defaultModel.
// Doing it on releaseEngine() is too later.
defaultModel.removeRows(0, defaultModel.rowCount());
TreeModelCopyVisitor v(&filterModel, &defaultModel);
v.run();
widget->treeView->expand(filterModel.index(0, 0));
WatchTreeView::reexpand(widget->treeView, filterModel.index(0, 0));
} }
} }
void DebuggerToolTipWidget::acquireEngine() void DebuggerToolTipHolder::setState(DebuggerToolTipHolder::State newState)
{ {
m_titleLabel->setText(m_context.expression); bool ok = (state == New && newState == Pending)
m_treeView->setModel(&m_filterModel); || (state == Pending && (newState == Acquired || newState == Released))
m_treeView->setRootIndex(m_filterModel.index(0, 0)); || (state == Acquired && (newState == Released))
m_treeView->expand(m_filterModel.index(0, 0)); || (state == Released && (newState == Acquired));
WatchTreeView::reexpand(m_treeView, m_filterModel.index(0, 0));
// FIXME: These happen when a tooltip is re-used in findOrCreate.
ok = ok
|| (state == Acquired && newState == Pending)
|| (state == Released && newState == Pending);
DEBUG("TRANSITION STATE FROM " << state << " TO " << newState);
QTC_ASSERT(ok, qDebug() << "Unexpected tooltip state transition from "
<< state << " to " << newState);
state = newState;
} }
void DebuggerToolTipWidget::releaseEngine() void DebuggerToolTipHolder::acquireEngine()
{ {
// Save data to stream and restore to the backup m_defaultModel. DEBUG("ACQUIRE ENGINE: STATE " << state);
m_defaultModel.removeRows(0, m_defaultModel.rowCount()); setState(Acquired);
TreeModelCopyVisitor v(&m_filterModel, &m_defaultModel);
v.run();
m_titleLabel->setText(msgReleasedText()); QTC_ASSERT(widget, return);
m_treeView->setModel(&m_defaultModel); widget->titleLabel->setText(context.expression);
m_treeView->setRootIndex(m_defaultModel.index(0, 0)); widget->treeView->setModel(&filterModel);
m_treeView->expandAll(); widget->treeView->setRootIndex(filterModel.index(0, 0));
widget->treeView->expand(filterModel.index(0, 0));
WatchTreeView::reexpand(widget->treeView, filterModel.index(0, 0));
} }
void DebuggerToolTipWidget::copy() void DebuggerToolTipHolder::releaseEngine()
{ {
QString clipboardText = DebuggerToolTipManager::treeModelClipboardContents(m_treeView->model()); DEBUG("RELEASE ENGINE: STATE " << state);
QClipboard *clipboard = QApplication::clipboard(); setState(Released);
clipboard->setText(clipboardText, QClipboard::Selection);
clipboard->setText(clipboardText, QClipboard::Clipboard); QTC_ASSERT(widget, return);
widget->titleLabel->setText(DebuggerToolTipManager::tr("%1 (Previous)").arg(context.expression));
widget->treeView->setModel(&defaultModel);
widget->treeView->setRootIndex(defaultModel.index(0, 0));
widget->treeView->expandAll();
} }
void DebuggerToolTipWidget::positionShow(const TextEditorWidget *editorWidget) void DebuggerToolTipHolder::positionShow(const TextEditorWidget *editorWidget)
{ {
// Figure out new position of tooltip using the text edit. // Figure out new position of tooltip using the text edit.
// If the line changed too much, close this tip. // If the line changed too much, close this tip.
QTC_ASSERT(editorWidget, return); QTC_ASSERT(editorWidget, return);
QTextCursor cursor = editorWidget->textCursor(); QTextCursor cursor = editorWidget->textCursor();
cursor.setPosition(m_context.position); cursor.setPosition(context.position);
const int line = cursor.blockNumber(); const int line = cursor.blockNumber();
if (qAbs(m_context.line - line) > 2) { if (qAbs(context.line - line) > 2) {
close(); widget->close();
return ; return ;
} }
const QPoint screenPos = editorWidget->toolTipPosition(cursor) + m_titleLabel->m_offset; const QPoint screenPos = editorWidget->toolTipPosition(cursor) + widget->titleLabel->m_offset;
const QRect toolTipArea = QRect(screenPos, QSize(sizeHint())); const QRect toolTipArea = QRect(screenPos, QSize(widget->sizeHint()));
const QRect plainTextArea = QRect(editorWidget->mapToGlobal(QPoint(0, 0)), editorWidget->size()); const QRect plainTextArea = QRect(editorWidget->mapToGlobal(QPoint(0, 0)), editorWidget->size());
const bool visible = plainTextArea.intersects(toolTipArea); const bool visible = plainTextArea.intersects(toolTipArea);
// qDebug() << "DebuggerToolTipWidget::positionShow() " << this << m_context // DEBUG("DebuggerToolTipWidget::positionShow() " << this << m_context
// << " line: " << line << " plainTextPos " << toolTipArea // << " line: " << line << " plainTextPos " << toolTipArea
// << " offset: " << m_titleLabel->m_offset // << " offset: " << m_titleLabel->m_offset
// << " Area: " << plainTextArea << " Screen pos: " // << " Area: " << plainTextArea << " Screen pos: "
// << screenPos << te.widget << " visible=" << visible; // << screenPos << te.widget << " visible=" << visible);
if (!visible) { if (visible) {
hide(); widget->move(screenPos);
return; widget->show();
} else {
widget->hide();
} }
move(screenPos);
show();
} }
static DebuggerToolTipWidget *findOrCreateWidget(const DebuggerToolTipContext &context) static DebuggerToolTipHolder *findOrCreateTooltip(const DebuggerToolTipContext &context)
{ {
foreach (const QPointer<DebuggerToolTipWidget> &tw, m_tooltips) purgeClosedToolTips();
if (tw && tw->m_context.isSame(context))
return tw;
auto tw = new DebuggerToolTipWidget(context); for (int i = 0, n = m_tooltips.size(); i != n; ++i) {
tw->setAttribute(Qt::WA_DeleteOnClose); DebuggerToolTipHolder *tooltip = m_tooltips.at(i);
tw->setObjectName(QLatin1String("DebuggerTreeViewToolTipWidget: ") + QLatin1String(context.iname)); if (tooltip->context.isSame(context))
tw->m_context.creationDate = QDate::currentDate(); return tooltip;
}
m_tooltips.push_back(tw); auto newTooltip = new DebuggerToolTipHolder(context);
m_tooltips.push_back(newTooltip);
return tw; return newTooltip;
} }
static void restoreTreeModel(QXmlStreamReader &r, QStandardItemModel *m) static void restoreTreeModel(QXmlStreamReader &r, QStandardItemModel *m)
@@ -975,27 +1050,26 @@ static void loadSessionDataHelper(QXmlStreamReader &r)
context.iname = attributes.value(QLatin1String(treeInameAttributeC)).toString().toLatin1(); context.iname = attributes.value(QLatin1String(treeInameAttributeC)).toString().toLatin1();
context.expression = attributes.value(QLatin1String(treeExpressionAttributeC)).toString(); context.expression = attributes.value(QLatin1String(treeExpressionAttributeC)).toString();
const QStringRef className = attributes.value(QLatin1String(toolTipClassAttributeC)); // const QStringRef className = attributes.value(QLatin1String(toolTipClassAttributeC));
context.engineType = attributes.value(QLatin1String(engineTypeAttributeC)).toString(); context.engineType = attributes.value(QLatin1String(engineTypeAttributeC)).toString();
context.creationDate = dateFromString(attributes.value(QLatin1String(dateAttributeC)).toString()); context.creationDate = dateFromString(attributes.value(QLatin1String(dateAttributeC)).toString());
bool readTree = context.isValid(); bool readTree = context.isValid();
if (!context.creationDate.isValid() || context.creationDate.daysTo(QDate::currentDate()) > toolTipsExpiryDays) { if (!context.creationDate.isValid() || context.creationDate.daysTo(QDate::currentDate()) > toolTipsExpiryDays) {
// qDebug() << "Expiring tooltip " << context.fileName << '@' << context.position << " from " << creationDate; // DEBUG("Expiring tooltip " << context.fileName << '@' << context.position << " from " << creationDate)
//readTree = false;
} else { //if (className != QLatin1String("Debugger::Internal::DebuggerToolTipWidget")) {
//qWarning("Unable to create debugger tool tip widget of class %s", qPrintable(className.toString()));
//readTree = false; //readTree = false;
} else if (className != QLatin1String("Debugger::Internal::DebuggerToolTipWidget")) {
qWarning("Unable to create debugger tool tip widget of class %s", qPrintable(className.toString()));
readTree = false;
} }
if (readTree) { if (readTree) {
DebuggerToolTipWidget *tw = findOrCreateWidget(context); DebuggerToolTipHolder *tw = findOrCreateTooltip(context);
restoreTreeModel(r, &tw->m_defaultModel); restoreTreeModel(r, &tw->defaultModel);
tw->pin(); tw->widget->pin();
tw->acquireEngine(); tw->widget->titleLabel->setText(DebuggerToolTipManager::tr("%1 (Restored").arg(context.expression));
tw->m_titleLabel->setText(DebuggerToolTipManager::tr("Restored")); tw->widget->treeView->setModel(&tw->defaultModel);
tw->m_treeView->setModel(&tw->m_defaultModel); tw->widget->treeView->setRootIndex(tw->defaultModel.index(0, 0));
tw->m_treeView->setRootIndex(tw->m_defaultModel.index(0, 0)); tw->widget->treeView->expandAll();
tw->m_treeView->expandAll();
} else { } else {
r.readElementText(QXmlStreamReader::SkipChildElements); // Skip r.readElementText(QXmlStreamReader::SkipChildElements); // Skip
} }
@@ -1003,29 +1077,30 @@ static void loadSessionDataHelper(QXmlStreamReader &r)
r.readNext(); // Skip </tree> r.readNext(); // Skip </tree>
} }
void DebuggerToolTipWidget::saveSessionData(QXmlStreamWriter &w) const void DebuggerToolTipHolder::saveSessionData(QXmlStreamWriter &w) const
{ {
w.writeStartElement(QLatin1String(toolTipElementC)); w.writeStartElement(QLatin1String(toolTipElementC));
QXmlStreamAttributes attributes; QXmlStreamAttributes attributes;
attributes.append(QLatin1String(toolTipClassAttributeC), QString::fromLatin1(metaObject()->className())); // attributes.append(QLatin1String(toolTipClassAttributeC), QString::fromLatin1(metaObject()->className()));
attributes.append(QLatin1String(fileNameAttributeC), m_context.fileName); attributes.append(QLatin1String(fileNameAttributeC), context.fileName);
if (!m_context.function.isEmpty()) if (!context.function.isEmpty())
attributes.append(QLatin1String(functionAttributeC), m_context.function); attributes.append(QLatin1String(functionAttributeC), context.function);
attributes.append(QLatin1String(textPositionAttributeC), QString::number(m_context.position)); attributes.append(QLatin1String(textPositionAttributeC), QString::number(context.position));
attributes.append(QLatin1String(textLineAttributeC), QString::number(m_context.line)); attributes.append(QLatin1String(textLineAttributeC), QString::number(context.line));
attributes.append(QLatin1String(textColumnAttributeC), QString::number(m_context.column)); attributes.append(QLatin1String(textColumnAttributeC), QString::number(context.column));
attributes.append(QLatin1String(dateAttributeC), m_creationDate.toString(QLatin1String("yyyyMMdd"))); attributes.append(QLatin1String(dateAttributeC), creationDate.toString(QLatin1String("yyyyMMdd")));
if (m_titleLabel->m_offset.x()) QPoint offset = widget->titleLabel->m_offset;
attributes.append(QLatin1String(offsetXAttributeC), QString::number(m_titleLabel->m_offset.x())); if (offset.x())
if (m_titleLabel->m_offset.y()) attributes.append(QLatin1String(offsetXAttributeC), QString::number(offset.x()));
attributes.append(QLatin1String(offsetYAttributeC), QString::number(m_titleLabel->m_offset.y())); if (offset.y())
attributes.append(QLatin1String(engineTypeAttributeC), m_context.engineType); attributes.append(QLatin1String(offsetYAttributeC), QString::number(offset.y()));
attributes.append(QLatin1String(treeExpressionAttributeC), m_context.expression); attributes.append(QLatin1String(engineTypeAttributeC), context.engineType);
attributes.append(QLatin1String(treeInameAttributeC), QLatin1String(m_context.iname)); attributes.append(QLatin1String(treeExpressionAttributeC), context.expression);
attributes.append(QLatin1String(treeInameAttributeC), QLatin1String(context.iname));
w.writeAttributes(attributes); w.writeAttributes(attributes);
w.writeStartElement(QLatin1String(treeElementC)); w.writeStartElement(QLatin1String(treeElementC));
XmlWriterTreeModelVisitor v(&m_filterModel, w); XmlWriterTreeModelVisitor v(&filterModel, w);
v.run(); v.run();
w.writeEndElement(); w.writeEndElement();
@@ -1047,18 +1122,16 @@ void DebuggerToolTipWidget::saveSessionData(QXmlStreamWriter &w) const
(by file name and function) acquire the engine, others release. (by file name and function) acquire the engine, others release.
*/ */
static DebuggerToolTipManager *m_instance = 0;
DebuggerToolTipManager::DebuggerToolTipManager() DebuggerToolTipManager::DebuggerToolTipManager()
{ {
m_instance = this;
} }
DebuggerToolTipManager::~DebuggerToolTipManager() DebuggerToolTipManager::~DebuggerToolTipManager()
{ {
} m_instance = 0;
void DebuggerToolTipManager::registerEngine(DebuggerEngine *)
{
loadSessionData();
} }
void DebuggerToolTipManager::slotUpdateVisibleToolTips() void DebuggerToolTipManager::slotUpdateVisibleToolTips()
@@ -1084,11 +1157,26 @@ void DebuggerToolTipManager::slotUpdateVisibleToolTips()
} }
// Reposition and show all tooltips of that file. // Reposition and show all tooltips of that file.
foreach (const QPointer<DebuggerToolTipWidget> &tw, m_tooltips) { foreach (DebuggerToolTipHolder *tooltip, m_tooltips) {
if (tw->fileName() == fileName) if (tooltip->context.fileName == fileName)
tw->positionShow(toolTipEditor->editorWidget()); tooltip->positionShow(toolTipEditor->editorWidget());
else else
tw->hide(); tooltip->widget->hide();
}
}
void DebuggerToolTipManager::slotItemIsExpanded(const QModelIndex &idx)
{
foreach (DebuggerToolTipHolder *tooltip, m_tooltips)
tooltip->handleItemIsExpanded(idx);
}
void DebuggerToolTipManager::slotColumnAdjustmentRequested()
{
foreach (DebuggerToolTipHolder *tooltip, m_tooltips) {
QTC_ASSERT(tooltip, continue);
QTC_ASSERT(tooltip->widget, continue);
tooltip->widget->treeView->computeSize();
} }
} }
@@ -1111,17 +1199,42 @@ void DebuggerToolTipManager::updateEngine(DebuggerEngine *engine)
function = frame.function; function = frame.function;
} }
} }
foreach (const QPointer<DebuggerToolTipWidget> &tw, m_tooltips) foreach (DebuggerToolTipHolder *tooltip, m_tooltips)
tw->handleStackFrameCompleted(fileName, function); tooltip->updateTooltip(fileName, function);
slotUpdateVisibleToolTips(); // Move out when stepping in same file. slotUpdateVisibleToolTips(); // Move tooltip when stepping in same file.
}
void DebuggerToolTipManager::registerEngine(DebuggerEngine *engine)
{
DEBUG("REGISTER ENGINE");
WatchModelBase *watchModel = engine->watchHandler()->model();
connect(watchModel, &WatchModelBase::itemIsExpanded,
m_instance, &DebuggerToolTipManager::slotItemIsExpanded);
connect(watchModel, &WatchModelBase::columnAdjustmentRequested,
m_instance, &DebuggerToolTipManager::slotColumnAdjustmentRequested);
} }
void DebuggerToolTipManager::deregisterEngine(DebuggerEngine *engine) void DebuggerToolTipManager::deregisterEngine(DebuggerEngine *engine)
{ {
DEBUG("DEREGISTER ENGINE");
QTC_ASSERT(engine, return); QTC_ASSERT(engine, return);
foreach (const QPointer<DebuggerToolTipWidget> &tw, m_tooltips)
if (tw && tw->m_context.engineType == engine->objectName()) // FIXME: For now remove all.
tw->releaseEngine(); purgeClosedToolTips();
foreach (const DebuggerToolTipHolder *tooltip, m_tooltips)
tooltip->widget->close();
purgeClosedToolTips();
return;
WatchModelBase *watchModel = engine->watchHandler()->model();
disconnect(watchModel, &WatchModelBase::itemIsExpanded,
m_instance, &DebuggerToolTipManager::slotItemIsExpanded);
disconnect(watchModel, &WatchModelBase::columnAdjustmentRequested,
m_instance, &DebuggerToolTipManager::slotColumnAdjustmentRequested);
foreach (DebuggerToolTipHolder *tooltip, m_tooltips)
if (tooltip->context.engineType == engine->objectName())
tooltip->releaseEngine();
saveSessionData(); saveSessionData();
} }
@@ -1130,20 +1243,6 @@ bool DebuggerToolTipManager::hasToolTips()
return !m_tooltips.isEmpty(); return !m_tooltips.isEmpty();
} }
void DebuggerToolTipManager::showToolTip
(const DebuggerToolTipContext &context, DebuggerEngine *engine)
{
QTC_ASSERT(engine, return);
QTC_ASSERT(!context.expression.isEmpty(), qDebug(" BUT EMPTY"); return);
DebuggerToolTipWidget *tw = findOrCreateWidget(context);
tw->setWatchModel(engine->watchHandler()->model());
tw->acquireEngine();
const Utils::WidgetContent widgetContent(tw, true);
Utils::ToolTip::show(context.mousePosition, widgetContent, Internal::mainWindow());
}
void DebuggerToolTipManager::sessionAboutToChange() void DebuggerToolTipManager::sessionAboutToChange()
{ {
closeAllToolTips(); closeAllToolTips();
@@ -1151,6 +1250,8 @@ void DebuggerToolTipManager::sessionAboutToChange()
void DebuggerToolTipManager::loadSessionData() void DebuggerToolTipManager::loadSessionData()
{ {
return; // FIXME
const QString data = sessionValue(sessionSettingsKeyC).toString(); const QString data = sessionValue(sessionSettingsKeyC).toString();
QXmlStreamReader r(data); QXmlStreamReader r(data);
r.readNextStartElement(); r.readNextStartElement();
@@ -1161,6 +1262,8 @@ void DebuggerToolTipManager::loadSessionData()
void DebuggerToolTipManager::saveSessionData() void DebuggerToolTipManager::saveSessionData()
{ {
return; // FIXME
QString data; QString data;
purgeClosedToolTips(); purgeClosedToolTips();
@@ -1168,9 +1271,9 @@ void DebuggerToolTipManager::saveSessionData()
w.writeStartDocument(); w.writeStartDocument();
w.writeStartElement(QLatin1String(sessionDocumentC)); w.writeStartElement(QLatin1String(sessionDocumentC));
w.writeAttribute(QLatin1String(sessionVersionAttributeC), QLatin1String("1.0")); w.writeAttribute(QLatin1String(sessionVersionAttributeC), QLatin1String("1.0"));
foreach (const QPointer<DebuggerToolTipWidget> &tw, m_tooltips) foreach (DebuggerToolTipHolder *tooltip, m_tooltips)
if (tw->isPinned()) if (tooltip->widget->isPinned)
tw->saveSessionData(w); tooltip->saveSessionData(w);
w.writeEndDocument(); w.writeEndDocument();
setSessionValue(sessionSettingsKeyC, QVariant(data)); setSessionValue(sessionSettingsKeyC, QVariant(data));
@@ -1178,12 +1281,18 @@ void DebuggerToolTipManager::saveSessionData()
void DebuggerToolTipManager::closeAllToolTips() void DebuggerToolTipManager::closeAllToolTips()
{ {
purgeClosedToolTips(); foreach (DebuggerToolTipHolder *tooltip, m_tooltips)
foreach (const QPointer<DebuggerToolTipWidget> &tw, m_tooltips) tooltip->widget->close();
tw->close();
m_tooltips.clear(); m_tooltips.clear();
} }
void DebuggerToolTipManager::resetLocation()
{
purgeClosedToolTips();
foreach (DebuggerToolTipHolder *tooltip, m_tooltips)
tooltip->widget->pin();
}
static void slotTooltipOverrideRequested static void slotTooltipOverrideRequested
(TextEditorWidget *editorWidget, const QPoint &point, int pos, bool *handled) (TextEditorWidget *editorWidget, const QPoint &point, int pos, bool *handled)
{ {
@@ -1208,29 +1317,49 @@ static void slotTooltipOverrideRequested
context.expression = fixCppExpression(raw); context.expression = fixCppExpression(raw);
if (context.expression.isEmpty()) { if (context.expression.isEmpty()) {
const Utils::WidgetContent widgetContent(new QLabel(DebuggerToolTipManager::tr("No valid expression")), true); const Utils::TextContent text(DebuggerToolTipManager::tr("No valid expression"));
Utils::ToolTip::show(context.mousePosition, widgetContent, Internal::mainWindow()); Utils::ToolTip::show(context.mousePosition, text, Internal::mainWindow());
*handled = true; *handled = true;
return; return;
} }
// Prefer a filter on an existing local variable if it can be found. // Prefer a filter on an existing local variable if it can be found.
if (const WatchData *localVariable = engine->watchHandler()->findCppLocalVariable(context.expression)) { const WatchData *localVariable = engine->watchHandler()->findCppLocalVariable(context.expression);
if (localVariable) {
context.expression = QLatin1String(localVariable->exp); context.expression = QLatin1String(localVariable->exp);
if (context.expression.isEmpty()) if (context.expression.isEmpty())
context.expression = localVariable->name; context.expression = localVariable->name;
context.iname = localVariable->iname; context.iname = localVariable->iname;
DebuggerToolTipManager::showToolTip(context, engine); } else {
context.iname = "tooltip." + context.expression.toLatin1().toHex();
}
DebuggerToolTipHolder *tooltip = findOrCreateTooltip(context);
if (tooltip->state == DebuggerToolTipHolder::Pending) {
DEBUG("FOUND PENDING TOOLTIP, WAITING...");
*handled = true; *handled = true;
return; return;
} }
context.iname = "tooltip." + context.expression.toLatin1().toHex(); tooltip->filterModel.setSourceModel(engine->watchHandler()->model());
tooltip->widget->titleLabel->setText(DebuggerToolTipManager::tr("Updating"));
tooltip->setState(DebuggerToolTipHolder::Pending);
*handled = engine->setToolTipExpression(editorWidget, context); if (localVariable) {
tooltip->acquireEngine();
// Other tooltip, close all in case mouse never entered the tooltip DEBUG("SYNC IN STATE" << tooltip->state);
// and no leave was triggered. const Utils::WidgetContent widgetContent(tooltip->widget, true);
Utils::ToolTip::show(context.mousePosition, widgetContent, Internal::mainWindow());
*handled = true;
} else {
DEBUG("ASYNC TIP IN STATE" << tooltip->state);
*handled = engine->setToolTipExpression(editorWidget, context);
if (!*handled) {
const Utils::TextContent text(DebuggerToolTipManager::tr("Expression too complex"));
Utils::ToolTip::show(context.mousePosition, text, Internal::mainWindow());
tooltip->widget->close();
}
}
} }
@@ -1284,13 +1413,14 @@ void DebuggerToolTipManager::leavingDebugMode()
} }
} }
DebuggerToolTipContexts DebuggerToolTipManager::treeWidgetExpressions DebuggerToolTipContexts DebuggerToolTipManager::pendingTooltips(DebuggerEngine *engine)
(DebuggerEngine *, const QString &fileName, const QString &function)
{ {
StackFrame frame = engine->stackHandler()->currentFrame();
DebuggerToolTipContexts rc; DebuggerToolTipContexts rc;
foreach (const QPointer<DebuggerToolTipWidget> &tw, m_tooltips) { foreach (DebuggerToolTipHolder *tooltip, m_tooltips) {
if (tw && tw->context().matchesFrame(fileName, function)) if (tooltip->context.iname.startsWith("tooltip")
rc.push_back(tw->context()); && tooltip->context.matchesFrame(frame.file, frame.function))
rc.push_back(tooltip->context);
} }
return rc; return rc;
} }
@@ -1304,9 +1434,9 @@ bool DebuggerToolTipManager::eventFilter(QObject *o, QEvent *e)
const QMoveEvent *me = static_cast<const QMoveEvent *>(e); const QMoveEvent *me = static_cast<const QMoveEvent *>(e);
const QPoint dist = me->pos() - me->oldPos(); const QPoint dist = me->pos() - me->oldPos();
purgeClosedToolTips(); purgeClosedToolTips();
foreach (const QPointer<DebuggerToolTipWidget> &tw, m_tooltips) foreach (DebuggerToolTipHolder *tooltip, m_tooltips)
if (tw->isVisible()) if (tooltip->widget && tooltip->widget->isVisible())
tw->move(tw->pos() + dist); tooltip->widget->move(tooltip->widget->pos() + dist);
} }
break; break;
case QEvent::WindowStateChange: { // Hide/Show along with parent (toplevel) case QEvent::WindowStateChange: { // Hide/Show along with parent (toplevel)
@@ -1315,8 +1445,8 @@ bool DebuggerToolTipManager::eventFilter(QObject *o, QEvent *e)
const bool isMinimized = static_cast<const QWidget *>(o)->windowState() & Qt::WindowMinimized; const bool isMinimized = static_cast<const QWidget *>(o)->windowState() & Qt::WindowMinimized;
if (wasMinimized ^ isMinimized) { if (wasMinimized ^ isMinimized) {
purgeClosedToolTips(); purgeClosedToolTips();
foreach (const QPointer<DebuggerToolTipWidget> &tw, m_tooltips) foreach (DebuggerToolTipHolder *tooltip, m_tooltips)
tw->setVisible(!isMinimized); tooltip->widget->setVisible(!isMinimized);
} }
} }
break; break;
@@ -1326,5 +1456,16 @@ bool DebuggerToolTipManager::eventFilter(QObject *o, QEvent *e)
return false; return false;
} }
static void purgeClosedToolTips()
{
for (int i = m_tooltips.size(); --i >= 0; ) {
DebuggerToolTipHolder *tooltip = m_tooltips.at(i);
if (!tooltip || !tooltip->widget) {
DEBUG("PURGE TOOLTIP, LEFT: " << m_tooltips.size());
m_tooltips.removeAt(i);
}
}
}
} // namespace Internal } // namespace Internal
} // namespace Debugger } // namespace Debugger

View File

@@ -83,12 +83,7 @@ public:
static void updateEngine(DebuggerEngine *engine); static void updateEngine(DebuggerEngine *engine);
static bool hasToolTips(); static bool hasToolTips();
// Collect all expressions of DebuggerTreeViewToolTipWidget static DebuggerToolTipContexts pendingTooltips(DebuggerEngine *engine);
static DebuggerToolTipContexts treeWidgetExpressions(DebuggerEngine *engine,
const QString &fileName, const QString &function = QString());
static void showToolTip(const DebuggerToolTipContext &context,
DebuggerEngine *engine);
virtual bool eventFilter(QObject *, QEvent *); virtual bool eventFilter(QObject *, QEvent *);
@@ -100,9 +95,12 @@ public:
static void loadSessionData(); static void loadSessionData();
static void saveSessionData(); static void saveSessionData();
static void closeAllToolTips(); static void closeAllToolTips();
static void resetLocation();
public slots: public slots:
static void slotUpdateVisibleToolTips(); static void slotUpdateVisibleToolTips();
void slotItemIsExpanded(const QModelIndex &idx);
void slotColumnAdjustmentRequested();
}; };
} // namespace Internal } // namespace Internal

View File

@@ -3632,43 +3632,16 @@ void GdbEngine::handleRegisterListValues(const GdbResponse &response)
// //
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////
//void GdbEngine::showToolTip()
//{
// const QString expression = m_toolTipContext.expression;
// if (DebuggerToolTipManager::debug())
// qDebug() << "GdbEngine::showToolTip " << expression << m_toolTipContext.iname << m_toolTipContext;
// if (m_toolTipContext.iname.startsWith("tooltip")
// && (!boolSetting(UseToolTipsInMainEditor)
// || !watchHandler()->isValidToolTip(m_toolTipContext.iname))) {
// watchHandler()->removeData(m_toolTipContext.iname);
// return;
// }
// DebuggerToolTipManager::showToolTip(m_toolTipContext, this);
//}
void GdbEngine::resetLocation()
{
m_toolTipContext.expression.clear();
DebuggerEngine::resetLocation();
}
bool GdbEngine::setToolTipExpression(TextEditor::TextEditorWidget *editor, bool GdbEngine::setToolTipExpression(TextEditor::TextEditorWidget *editor,
const DebuggerToolTipContext &context) const DebuggerToolTipContext &context)
{ {
if (state() != InferiorStopOk || !isCppEditor(editor)) { if (state() != InferiorStopOk || !isCppEditor(editor))
//qDebug() << "SUPPRESSING DEBUGGER TOOLTIP, INFERIOR NOT STOPPED "
// " OR NOT A CPPEDITOR";
return false; return false;
}
m_toolTipContext = context;
// qDebug() << "GdbEngine::setToolTipExpression2 " << exp << m_toolTipContext;
UpdateParameters params; UpdateParameters params;
params.tryPartial = true; params.tryPartial = true;
params.tooltipOnly = true; params.tooltipOnly = true;
params.tooltipExpression = context.expression;
params.varList = context.iname; params.varList = context.iname;
updateLocalsPython(params); updateLocalsPython(params);
return true; return true;
@@ -3736,10 +3709,6 @@ void GdbEngine::rebuildWatchModel()
showMessage(_("<Rebuild Watchmodel %1>").arg(count), LogMiscInput); showMessage(_("<Rebuild Watchmodel %1>").arg(count), LogMiscInput);
showStatusMessage(tr("Finished retrieving data"), 400); showStatusMessage(tr("Finished retrieving data"), 400);
if (m_toolTipContext.isValid()) {
DebuggerToolTipManager::showToolTip(m_toolTipContext, this);
m_toolTipContext = DebuggerToolTipContext();
}
DebuggerToolTipManager::updateEngine(this); DebuggerToolTipManager::updateEngine(this);
} }
@@ -4829,42 +4798,16 @@ void GdbEngine::updateLocalsPython(const UpdateParameters &params)
+ " displaystringlimit:" + " displaystringlimit:"
+ action(DisplayStringLimit)->value().toByteArray(); + action(DisplayStringLimit)->value().toByteArray();
// Re-create tooltip items that are not filters on existing local variables in
// the tooltip model.
QByteArray watchers; QByteArray watchers;
const QString fileName = stackHandler()->currentFrame().file; DebuggerToolTipContexts toolTips = DebuggerToolTipManager::pendingTooltips(this);
const QString function = stackHandler()->currentFrame().function; foreach (const DebuggerToolTipContext &p, toolTips) {
if (!fileName.isEmpty()) { if (!watchers.isEmpty())
// Re-create tooltip items that are not filters on existing local variables in watchers += "##";
// the tooltip model. watchers += p.expression.toLatin1();
DebuggerToolTipContexts toolTips = watchers += '#';
DebuggerToolTipManager::treeWidgetExpressions(this, fileName, function); watchers += p.iname;
const QString currentExpression = m_toolTipContext.expression;
if (!currentExpression.isEmpty()) {
int currentIndex = -1;
for (int i = 0; i < toolTips.size(); ++i) {
if (toolTips.at(i).expression == currentExpression) {
currentIndex = i;
break;
}
}
if (currentIndex < 0) {
DebuggerToolTipContext context;
context.expression = currentExpression;
context.iname = tooltipIName(currentExpression);
toolTips.push_back(context);
}
}
foreach (const DebuggerToolTipContext &p, toolTips) {
if (p.iname.startsWith("tooltip")) {
if (!watchers.isEmpty())
watchers += "##";
watchers += p.expression.toLatin1();
watchers += '#';
watchers += p.iname;
}
}
} }
QHash<QByteArray, int> watcherNames = handler->watcherNames(); QHash<QByteArray, int> watcherNames = handler->watcherNames();

View File

@@ -298,7 +298,6 @@ private: ////////// View & Data Stuff //////////
void selectThread(ThreadId threadId); void selectThread(ThreadId threadId);
void activateFrame(int index); void activateFrame(int index);
void resetLocation();
// //
// Breakpoint specific stuff // Breakpoint specific stuff
@@ -469,7 +468,6 @@ protected:
void showExecutionError(const QString &message); void showExecutionError(const QString &message);
static QByteArray tooltipIName(const QString &exp); static QByteArray tooltipIName(const QString &exp);
DebuggerToolTipContext m_toolTipContext;
// For short-circuiting stack and thread list evaluation. // For short-circuiting stack and thread list evaluation.
bool m_stackNeeded; bool m_stackNeeded;

View File

@@ -74,11 +74,6 @@ using namespace Utils;
namespace Debugger { namespace Debugger {
namespace Internal { namespace Internal {
static QByteArray tooltipIName(const QString &exp)
{
return "tooltip." + exp.toLatin1().toHex();
}
/////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////
// //
// LldbEngine // LldbEngine
@@ -815,41 +810,25 @@ void LldbEngine::refreshSymbols(const GdbMi &symbols)
// //
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////
static WatchData m_toolTip;
static QPoint m_toolTipPos;
static QHash<QString, WatchData> m_toolTipCache;
void LldbEngine::showToolTip()
{
if (m_toolTipContext.expression.isEmpty())
return;
//const QString expression = m_toolTipContext->expression;
// qDebug() << "LldbEngine::showToolTip " << expression << m_toolTipContext->iname << (*m_toolTipContext);
DebuggerToolTipManager::showToolTip(m_toolTipContext, this);
// Prevent tooltip from re-occurring (classic GDB, QTCREATORBUG-4711).
m_toolTipContext.expression.clear();
}
void LldbEngine::resetLocation() void LldbEngine::resetLocation()
{ {
m_toolTipContext.expression.clear();
DebuggerEngine::resetLocation(); DebuggerEngine::resetLocation();
} }
bool LldbEngine::setToolTipExpression(TextEditor::TextEditorWidget *editorWidget, const DebuggerToolTipContext &context) bool LldbEngine::setToolTipExpression(TextEditor::TextEditorWidget *editorWidget, const DebuggerToolTipContext &context)
{ {
return false; // FIXME
if (state() != InferiorStopOk || !isCppEditor(editorWidget)) { if (state() != InferiorStopOk || !isCppEditor(editorWidget)) {
//qDebug() << "SUPPRESSING DEBUGGER TOOLTIP, INFERIOR NOT STOPPED " //qDebug() << "SUPPRESSING DEBUGGER TOOLTIP, INFERIOR NOT STOPPED "
// " OR NOT A CPPEDITOR"; // " OR NOT A CPPEDITOR";
return false; return false;
} }
m_toolTipContext = context;
UpdateParameters params; UpdateParameters params;
params.tryPartial = true; params.tryPartial = true;
params.tooltipOnly = true; params.tooltipOnly = true;
params.tooltipExpression = context.expression;
params.varList = context.iname; params.varList = context.iname;
doUpdateLocals(params); doUpdateLocals(params);
@@ -924,6 +903,7 @@ void LldbEngine::doUpdateLocals(UpdateParameters params)
cmd.arg("tooltiponly", params.tooltipOnly); cmd.arg("tooltiponly", params.tooltipOnly);
cmd.beginList("watchers"); cmd.beginList("watchers");
// Watchers // Watchers
QHashIterator<QByteArray, int> it(WatchHandler::watcherNames()); QHashIterator<QByteArray, int> it(WatchHandler::watcherNames());
while (it.hasNext()) { while (it.hasNext()) {
@@ -933,38 +913,16 @@ void LldbEngine::doUpdateLocals(UpdateParameters params)
.arg("exp", it.key().toHex()) .arg("exp", it.key().toHex())
.endGroup(); .endGroup();
} }
// Tooltip
const StackFrame frame = stackHandler()->currentFrame();
if (!frame.file.isEmpty()) {
// Re-create tooltip items that are not filters on existing local variables in
// the tooltip model.
DebuggerToolTipContexts toolTips =
DebuggerToolTipManager::treeWidgetExpressions(this, frame.file, frame.function);
const QString currentExpression = m_toolTipContext.expression; // Tooltips
if (!currentExpression.isEmpty()) { DebuggerToolTipContexts toolTips = DebuggerToolTipManager::pendingTooltips(this);
int currentIndex = -1; foreach (const DebuggerToolTipContext &p, toolTips) {
for (int i = 0; i < toolTips.size(); ++i) { cmd.beginGroup()
if (toolTips.at(i).expression == currentExpression) { .arg("iname", p.iname)
currentIndex = i; .arg("exp", p.expression.toLatin1().toHex())
break; .endGroup();
}
}
if (currentIndex < 0) {
DebuggerToolTipContext context;
context.expression = currentExpression;
context.iname = tooltipIName(currentExpression);
toolTips.push_back(context);
}
}
foreach (const DebuggerToolTipContext &p, toolTips) {
if (p.iname.startsWith("tooltip"))
cmd.beginGroup()
.arg("iname", p.iname)
.arg("exp", p.expression.toLatin1().toHex())
.endGroup();
}
} }
cmd.endList(); cmd.endList();
//cmd.arg("resultvarname", m_resultVarName); //cmd.arg("resultvarname", m_resultVarName);
@@ -1083,7 +1041,7 @@ void LldbEngine::refreshLocals(const GdbMi &vars)
} }
handler->insertData(list); handler->insertData(list);
showToolTip(); DebuggerToolTipManager::updateEngine(this);
} }
void LldbEngine::refreshStack(const GdbMi &stack) void LldbEngine::refreshStack(const GdbMi &stack)

View File

@@ -218,9 +218,6 @@ private:
QMap<QPointer<DisassemblerAgent>, int> m_disassemblerAgents; QMap<QPointer<DisassemblerAgent>, int> m_disassemblerAgents;
QMap<QPointer<MemoryAgent>, int> m_memoryAgents; QMap<QPointer<MemoryAgent>, int> m_memoryAgents;
QHash<int, QPointer<QObject> > m_memoryAgentTokens; QHash<int, QPointer<QObject> > m_memoryAgentTokens;
DebuggerToolTipContext m_toolTipContext;
void showToolTip();
// Console handling. // Console handling.
Q_SLOT void stubError(const QString &msg); Q_SLOT void stubError(const QString &msg);

View File

@@ -2015,10 +2015,10 @@ const WatchData *WatchHandler::findCppLocalVariable(const QString &name) const
QByteArray iname = localsPrefix + name.toLatin1(); QByteArray iname = localsPrefix + name.toLatin1();
if (const WatchData *wd = findData(iname)) if (const WatchData *wd = findData(iname))
return wd; return wd;
// Nope, try a 'local.this.m_foo'. // // Nope, try a 'local.this.m_foo'.
iname.insert(localsPrefix.size(), "this."); // iname.insert(localsPrefix.size(), "this.");
if (const WatchData *wd = findData(iname)) // if (const WatchData *wd = findData(iname))
return wd; // return wd;
return 0; return 0;
} }

View File

@@ -121,6 +121,7 @@ public:
bool tryPartial; bool tryPartial;
bool tooltipOnly; bool tooltipOnly;
QByteArray varList; QByteArray varList;
QString tooltipExpression;
}; };
typedef QHash<QString, QStringList> DumperTypeFormats; // Type name -> Dumper Formats typedef QHash<QString, QStringList> DumperTypeFormats; // Type name -> Dumper Formats