Commit 0ac879e3 authored by Friedemann Kleint's avatar Friedemann Kleint

Debugger: Make tooltips pinnable.

Replace old debugger tooltip by a new ToolTipManager which
has a list of AbstractDebuggerToolTipWidget with the functionality
to 'acquire' an engine (display its data) and 'release' it
(store engine data and display them as 'previous') and serialization
to XML session data.
DebuggerTreeViewToolTipWidget implements AbstractDebuggerToolTipWidget
for tree model acting as  a filter on watch models.

Rubber-stamped-by: hjk
parent 80b2b71a
......@@ -50,7 +50,7 @@
#include "disassembleragent.h"
#include "memoryagent.h"
#include "debuggerrunner.h"
#include "debuggertooltip.h"
#include "debuggertooltipmanager.h"
#include "cdbparsehelpers.h"
#include "watchutils.h"
#include "gdb/gdbmi.h"
......@@ -471,7 +471,9 @@ void CdbEngine::syncOperateByInstruction(bool operateByInstruction)
postCommand(m_operateByInstruction ? QByteArray("l-s") : QByteArray("l+s"), 0);
}
void CdbEngine::setToolTipExpression(const QPoint &mousePos, TextEditor::ITextEditor *editor, int cursorPos)
void CdbEngine::setToolTipExpression(const QPoint &mousePos,
TextEditor::ITextEditor *editor,
const DebuggerToolTipContext &contextIn)
{
if (debug)
qDebug() << Q_FUNC_INFO;
......@@ -481,10 +483,10 @@ void CdbEngine::setToolTipExpression(const QPoint &mousePos, TextEditor::ITextEd
// Determine expression and function
int line;
int column;
QString function;
const QString exp = cppExpressionAt(editor, cursorPos, &line, &column, &function);
DebuggerToolTipContext context = contextIn;
const QString exp = cppExpressionAt(editor, context.position, &line, &column, &context.function);
// Are we in the current stack frame
if (function.isEmpty() || exp.isEmpty() || function != stackHandler()->currentFrame().function)
if (context.function.isEmpty() || exp.isEmpty() || context.function != stackHandler()->currentFrame().function)
return;
// No numerical or any other expressions [yet]
if (!(exp.at(0).isLetter() || exp.at(0) == QLatin1Char('_')))
......@@ -492,9 +494,12 @@ void CdbEngine::setToolTipExpression(const QPoint &mousePos, TextEditor::ITextEd
const QByteArray iname = QByteArray(localsPrefixC) + exp.toAscii();
const QModelIndex index = watchHandler()->itemIndex(iname);
if (index.isValid()) {
showDebuggerToolTip(mousePos, watchHandler()->modelForIName(iname), index.row());
} else {
hideDebuggerToolTip();
DebuggerTreeViewToolTipWidget *tw = new DebuggerTreeViewToolTipWidget;
tw->setContext(context);
tw->setDebuggerModel(LocalsWatch);
tw->setExpression(exp);
tw->acquireEngine(this);
DebuggerToolTipManager::instance()->add(mousePos, tw);
}
}
......@@ -1295,11 +1300,11 @@ void CdbEngine::activateFrame(int index)
}
} else {
gotoLocation(frame);
updateLocals();
updateLocals(true);
}
}
void CdbEngine::updateLocals()
void CdbEngine::updateLocals(bool forNewStackFrame)
{
typedef QHash<QByteArray, int> WatcherHash;
......@@ -1362,7 +1367,7 @@ void CdbEngine::updateLocals()
// Required arguments: frame
str << blankSeparator << frameIndex;
watchHandler()->beginCycle();
postExtensionCommand("locals", arguments, 0, &CdbEngine::handleLocals);
postExtensionCommand("locals", arguments, 0, &CdbEngine::handleLocals, 0, QVariant(forNewStackFrame));
}
void CdbEngine::selectThread(int index)
......@@ -1571,6 +1576,9 @@ void CdbEngine::handleLocals(const CdbExtensionCommandPtr &reply)
foreach (const WatchData &wd, watchData)
nsp << wd.toString() <<'\n';
}
const bool forNewStackFrame = reply->cookie.toBool();
if (forNewStackFrame)
emit stackFrameCompleted();
} else {
showMessage(QString::fromLatin1(reply->errorMessage), LogError);
}
......
......@@ -85,7 +85,8 @@ public:
virtual ~CdbEngine();
// Factory function that returns 0 if the debug engine library cannot be found.
virtual void setToolTipExpression(const QPoint &mousePos, TextEditor::ITextEditor *editor, int cursorPos);
virtual void setToolTipExpression(const QPoint &mousePos, TextEditor::ITextEditor *editor,
const DebuggerToolTipContext &ctx);
virtual void setupEngine();
virtual void setupInferior();
virtual void runEngine();
......@@ -215,7 +216,7 @@ private:
QString normalizeFileName(const QString &f);
void updateLocalVariable(const QByteArray &iname);
void updateLocals();
void updateLocals(bool forNewStackFrame = false);
int elapsedLogTime() const;
void addLocalsOptions(ByteArrayInputStream &s) const;
unsigned parseStackTrace(const GdbMi &data, bool sourceStepInto);
......
......@@ -33,7 +33,6 @@ HEADERS += breakhandler.h \
debuggerstartparameters.h \
debuggerstreamops.h \
debuggerstringutils.h \
debuggertooltip.h \
disassembleragent.h \
disassemblerlines.h \
logwindow.h \
......@@ -60,7 +59,8 @@ HEADERS += breakhandler.h \
threaddata.h \
threadshandler.h \
watchdelegatewidgets.h \
debuggerruncontrolfactory.h
debuggerruncontrolfactory.h \
debuggertooltipmanager.h
SOURCES += breakhandler.cpp \
breakpoint.cpp \
......@@ -74,7 +74,6 @@ SOURCES += breakhandler.cpp \
debuggerplugin.cpp \
debuggerrunner.cpp \
debuggerstreamops.cpp \
debuggertooltip.cpp \
disassembleragent.cpp \
disassemblerlines.cpp \
logwindow.cpp \
......@@ -100,7 +99,8 @@ SOURCES += breakhandler.cpp \
watchutils.cpp \
watchwindow.cpp \
stackframe.cpp \
watchdelegatewidgets.cpp
watchdelegatewidgets.cpp \
debuggertooltipmanager.cpp
FORMS += attachexternaldialog.ui \
attachcoredialog.ui \
......
......@@ -29,5 +29,6 @@
<file>images/breakpoint_pending_24.png</file>
<file>images/location_16.png</file>
<file>images/location_24.png</file>
<file>images/pin.xpm</file>
</qresource>
</RCC>
......@@ -61,6 +61,7 @@ namespace Internal {
class BreakHandler;
class SnapshotHandler;
class Symbol;
class DebuggerToolTipManager;
class DebuggerCore : public QObject
{
......@@ -109,6 +110,8 @@ public:
virtual Utils::SavedAction *action(int code) const = 0;
virtual bool boolSetting(int code) const = 0;
virtual QString stringSetting(int code) const = 0;
virtual DebuggerToolTipManager *toolTipManager() const = 0;
};
// This is the only way to access the global object.
......
......@@ -38,7 +38,6 @@
#include "debuggerplugin.h"
#include "debuggerrunner.h"
#include "debuggerstringutils.h"
#include "debuggertooltip.h"
#include "debuggerstartparameters.h"
#include "memoryagent.h"
......@@ -357,11 +356,6 @@ void DebuggerEngine::showStatusMessage(const QString &msg, int timeout) const
showMessage(msg, StatusBar, timeout);
}
void DebuggerEngine::removeTooltip()
{
watchHandler()->removeTooltip();
hideDebuggerToolTip();
}
void DebuggerEngine::frameUp()
{
......@@ -474,6 +468,14 @@ QAbstractItemModel *DebuggerEngine::returnModel() const
return model;
}
QAbstractItemModel *DebuggerEngine::toolTipsModel() const
{
QAbstractItemModel *model = watchHandler()->model(TooltipsWatch);
if (model->objectName().isEmpty()) // Make debugging easier.
model->setObjectName(objectName() + QLatin1String("TooltipsModel"));
return model;
}
QAbstractItemModel *DebuggerEngine::sourceFilesModel() const
{
QAbstractItemModel *model = sourceFilesHandler()->model();
......@@ -1238,7 +1240,7 @@ DebuggerRunControl *DebuggerEngine::runControl() const
}
void DebuggerEngine::setToolTipExpression
(const QPoint &, TextEditor::ITextEditor *, int)
(const QPoint &, TextEditor::ITextEditor *, const DebuggerToolTipContext &)
{
}
......
......@@ -81,6 +81,7 @@ class ThreadsHandler;
class WatchHandler;
class BreakpointParameters;
class QmlCppEngine;
class DebuggerToolTipContext;
struct WatchUpdateFlags
{
......@@ -137,7 +138,7 @@ public:
DebuggerStartParameters &startParameters();
virtual void setToolTipExpression(const QPoint & mousePos,
TextEditor::ITextEditor *editor, int cursorPos);
TextEditor::ITextEditor *editor, const Internal::DebuggerToolTipContext &);
virtual void updateWatchData(const Internal::WatchData &data,
const Internal::WatchUpdateFlags & flags = Internal::WatchUpdateFlags());
......@@ -183,7 +184,6 @@ public:
virtual void assignValueInDebugger(const Internal::WatchData *data,
const QString &expr, const QVariant &value);
virtual void removeTooltip();
virtual void selectThread(int index);
virtual void handleRemoteSetupDone(int gdbServerPort, int qmlPort);
......@@ -204,6 +204,7 @@ public:
virtual QAbstractItemModel *localsModel() const;
virtual QAbstractItemModel *watchersModel() const;
virtual QAbstractItemModel *returnModel() const;
virtual QAbstractItemModel *toolTipsModel() const;
virtual QAbstractItemModel *sourceFilesModel() const;
void progressPing();
......@@ -253,6 +254,8 @@ public:
signals:
void stateChanged(const Debugger::DebuggerState &state);
// A new stack frame is on display including locals.
void stackFrameCompleted();
void updateViewsRequested();
/*
* For "external" clients of a debugger run control that needs to do
......
......@@ -43,7 +43,6 @@
#include "debuggerrunner.h"
#include "debuggerruncontrolfactory.h"
#include "debuggerstringutils.h"
#include "debuggertooltip.h"
#include "breakpoint.h"
#include "breakhandler.h"
......@@ -62,6 +61,7 @@
#include "watchhandler.h"
#include "watchwindow.h"
#include "watchutils.h"
#include "debuggertooltipmanager.h"
#include "snapshothandler.h"
#include "threadshandler.h"
......@@ -1170,6 +1170,8 @@ public slots:
bool parseArguments(const QStringList &args,
unsigned *enabledEngines, QString *errorMessage);
DebuggerToolTipManager *toolTipManager() const { return m_toolTipManager; }
public:
DebuggerMainWindow *m_mainWindow;
DebuggerRunControlFactory *m_debuggerRunControlFactory;
......@@ -1246,9 +1248,11 @@ public:
QSettings *m_coreSettings;
bool m_gdbBinariesChanged;
uint m_cmdLineEnabledEngines;
DebuggerToolTipManager *m_toolTipManager;
};
DebuggerPluginPrivate::DebuggerPluginPrivate(DebuggerPlugin *plugin)
DebuggerPluginPrivate::DebuggerPluginPrivate(DebuggerPlugin *plugin) :
m_toolTipManager(new DebuggerToolTipManager(this))
{
qRegisterMetaType<WatchData>("WatchData");
qRegisterMetaType<ContextData>("ContextData");
......@@ -1956,8 +1960,12 @@ void DebuggerPluginPrivate::showToolTip(ITextEditor *editor,
if (!currentEngine()->canDisplayTooltip())
return;
QTC_ASSERT(handled, return);
*handled = true;
currentEngine()->setToolTipExpression(point, editor, pos);
const DebuggerToolTipContext context = DebuggerToolTipContext::fromEditor(editor, pos);
if (context.isValid()) {
*handled = true;
currentEngine()->setToolTipExpression(point, editor, context);
}
}
DebuggerRunControl *DebuggerPluginPrivate::createDebugger
......@@ -2036,7 +2044,7 @@ void DebuggerPluginPrivate::cleanupViews()
{
m_reverseDirectionAction->setChecked(false);
m_reverseDirectionAction->setEnabled(false);
hideDebuggerToolTip();
m_toolTipManager->closeUnpinnedToolTips();
if (!boolSetting(CloseBuffersOnExit))
return;
......@@ -2089,7 +2097,7 @@ void DebuggerPluginPrivate::setInitialState()
setBusyCursor(false);
m_reverseDirectionAction->setChecked(false);
m_reverseDirectionAction->setEnabled(false);
hideDebuggerToolTip();
m_toolTipManager->closeAllToolTips();
m_startExternalAction->setEnabled(true);
m_attachExternalAction->setEnabled(true);
......@@ -2286,12 +2294,15 @@ void DebuggerPluginPrivate::onModeChanged(IMode *mode)
m_mainWindow->onModeChanged(mode);
if (mode->id() != Constants::MODE_DEBUG)
if (mode->id() != Constants::MODE_DEBUG) {
m_toolTipManager->leavingDebugMode();
return;
}
EditorManager *editorManager = EditorManager::instance();
if (editorManager->currentEditor())
editorManager->currentEditor()->widget()->setFocus();
m_toolTipManager->debugModeEntered();
}
void DebuggerPluginPrivate::showSettingsDialog()
......@@ -2345,11 +2356,13 @@ void DebuggerPluginPrivate::sessionLoaded()
{
m_breakHandler->loadSessionData();
dummyEngine()->watchHandler()->loadSessionData();
m_toolTipManager->loadSessionData();
}
void DebuggerPluginPrivate::aboutToUnloadSession()
{
m_breakHandler->removeSessionData();
m_toolTipManager->sessionAboutToChange();
// Stop debugging the active project when switching sessions.
// Note that at startup, session switches may occur, which interfere
// with command-line debugging startup.
......@@ -2362,6 +2375,7 @@ void DebuggerPluginPrivate::aboutToUnloadSession()
void DebuggerPluginPrivate::aboutToSaveSession()
{
dummyEngine()->watchHandler()->loadSessionData();
m_toolTipManager->saveSessionData();
m_breakHandler->saveSessionData();
}
......
......@@ -43,6 +43,7 @@
#include "debuggerstartparameters.h"
#include "gdb/gdboptionspage.h"
#include "lldb/lldbenginehost.h"
#include "debuggertooltipmanager.h"
#ifdef Q_OS_WIN
# include "peutils.h"
......@@ -344,7 +345,9 @@ DebuggerRunControl::DebuggerRunControl(RunConfiguration *runConfiguration,
break;
}
if (!d->m_engine) {
if (d->m_engine) {
DebuggerToolTipManager::instance()->registerEngine(d->m_engine);
} else {
// Could not find anything suitable.
debuggingFinished();
// Create Message box with possibility to go to settings.
......
/**************************************************************************
**
** This file is part of Qt Creator
**
** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
**
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** No Commercial Usage
**
** This file contains pre-release code and may not be distributed.
** You may use this file in accordance with the terms and conditions
** contained in the Technology Preview License Agreement accompanying
** this package.
**
** GNU Lesser General Public License Usage
**
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Nokia gives you certain additional
** rights. These rights are described in the Nokia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** If you have questions regarding the use of this file, please contact
** Nokia at qt-info@nokia.com.
**
**************************************************************************/
#include "debuggertooltip.h"
#include <utils/qtcassert.h>
#include <QtCore/QtDebug>
#include <QtCore/QPointer>
#include <QtGui/QApplication>
#include <QtGui/QDesktopWidget>
#include <QtGui/QHeaderView>
#include <QtGui/QScrollBar>
#include <QtGui/QTreeView>
#include <QtGui/QSortFilterProxyModel>
namespace Debugger {
namespace Internal {
class ToolTipWidget : public QTreeView
{
Q_OBJECT
public:
ToolTipWidget(QWidget *parent);
QSize sizeHint() const { return m_size; }
void done();
void run(const QPoint &point, const QModelIndex &index);
int computeHeight(const QModelIndex &index) const;
Q_SLOT void computeSize();
void leaveEvent(QEvent *ev);
private:
QSize m_size;
};
static QPointer<ToolTipWidget> theToolTipWidget;
ToolTipWidget::ToolTipWidget(QWidget *parent)
: QTreeView(parent)
{
setWindowFlags(Qt::ToolTip | Qt::WindowStaysOnTopHint);
setFocusPolicy(Qt::NoFocus);
header()->hide();
setUniformRowHeights(true);
setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
connect(this, SIGNAL(collapsed(QModelIndex)), this, SLOT(computeSize()),
Qt::QueuedConnection);
connect(this, SIGNAL(expanded(QModelIndex)), this, SLOT(computeSize()),
Qt::QueuedConnection);
}
int ToolTipWidget::computeHeight(const QModelIndex &index) const
{
int s = rowHeight(index);
for (int i = 0; i < model()->rowCount(index); ++i)
s += computeHeight(model()->index(i, 0, index));
return s;
}
void ToolTipWidget::computeSize()
{
int columns = 0;
for (int i = 0; i < 3; ++i) {
resizeColumnToContents(i);
columns += sizeHintForColumn(i);
}
int rows = computeHeight(QModelIndex());
// Fit tooltip to screen, showing/hiding scrollbars as needed.
// Add a bit of space to account for tooltip border, and not
// touch the border of the screen.
QPoint pos(x(), y());
QRect desktopRect = QApplication::desktop()->availableGeometry(pos);
const int maxWidth = desktopRect.right() - pos.x() - 5 - 5;
const int maxHeight = desktopRect.bottom() - pos.y() - 5 - 5;
if (columns > maxWidth)
rows += horizontalScrollBar()->height();
if (rows > maxHeight) {
setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
rows = maxHeight;
columns += verticalScrollBar()->width();
} else {
setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
}
if (columns > maxWidth) {
setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
columns = maxWidth;
} else {
setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
}
m_size = QSize(columns + 5, rows + 5);
setMinimumSize(m_size);
setMaximumSize(m_size);
}
void ToolTipWidget::done()
{
deleteLater();
}
void ToolTipWidget::run(const QPoint &point, const QModelIndex &index)
{
QAbstractItemModel *model = const_cast<QAbstractItemModel *>(index.model());
move(point);
setModel(model);
// Track changes in filter models.
connect(model, SIGNAL(rowsInserted(QModelIndex,int,int)),
this, SLOT(computeSize()), Qt::QueuedConnection);
computeSize();
setRootIsDecorated(model->hasChildren(index));
}
void ToolTipWidget::leaveEvent(QEvent *ev)
{
Q_UNUSED(ev);
if (QApplication::keyboardModifiers() == Qt::NoModifier)
hide();
}
void showDebuggerToolTip(const QPoint &point, const QModelIndex &index)
{
if (index.model()) {
if (!theToolTipWidget)
theToolTipWidget = new ToolTipWidget(0);
theToolTipWidget->run(point, index);
theToolTipWidget->show();
} else if (theToolTipWidget) {
theToolTipWidget->done();
theToolTipWidget = 0;
}
}
// Model for tooltips filtering a local variable using the locals model.
class ToolTipRowFilterModel : public QSortFilterProxyModel
{
public:
// Index points the variable to be filtered.
explicit ToolTipRowFilterModel(QAbstractItemModel *model, int row, QObject *parent = 0);
virtual bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const;
private:
const int m_row;
};
ToolTipRowFilterModel::ToolTipRowFilterModel(QAbstractItemModel *model, int row, QObject *parent) :
QSortFilterProxyModel(parent), m_row(row)
{
setSourceModel(model);
}
bool ToolTipRowFilterModel::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const
{
// Match on row for top level, else pass through.
return sourceParent.isValid() || sourceRow == m_row;
}
// Show tooltip filtering a row of a source model.
void showDebuggerToolTip(const QPoint &point, QAbstractItemModel *model, int row)
{
// Create a filter model parented on the widget to display column
ToolTipRowFilterModel *filterModel = new ToolTipRowFilterModel(model, row);
showDebuggerToolTip(point, filterModel->index(0, 0));
QTC_ASSERT(theToolTipWidget, return; )
filterModel->setParent(theToolTipWidget);
}
void hideDebuggerToolTip(int delay)
{
Q_UNUSED(delay)
if (theToolTipWidget)
theToolTipWidget->done();
}
} // namespace Internal
} // namespace Debugger
#include "debuggertooltip.moc"
/**************************************************************************
**
** This file is part of Qt Creator
**
** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
**
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** No Commercial Usage
**
** This file contains pre-release code and may not be distributed.
** You may use this file in accordance with the terms and conditions
** contained in the Technology Preview License Agreement accompanying
** this package.
**
** GNU Lesser General Public License Usage
**
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Nokia gives you certain additional
** rights. These rights are described in the Nokia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** If you have questions regarding the use of this file, please contact
** Nokia at qt-info@nokia.com.
**
**************************************************************************/
#ifndef DEBUGGER_DEBUGGERTOOLTIP_H
#define DEBUGGER_DEBUGGERTOOLTIP_H
#include <QtCore/QtGlobal>
QT_BEGIN_NAMESPACE
class QModelIndex;