diff --git a/src/plugins/debugger/cdb/cdbengine.cpp b/src/plugins/debugger/cdb/cdbengine.cpp index d72e5747fe87b908c2cc7dbd7122f9583b3486f2..469fe6fccf22c6a1f82e5ea65b2179fa9a7881fb 100644 --- a/src/plugins/debugger/cdb/cdbengine.cpp +++ b/src/plugins/debugger/cdb/cdbengine.cpp @@ -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); } diff --git a/src/plugins/debugger/cdb/cdbengine.h b/src/plugins/debugger/cdb/cdbengine.h index 2d4e02f02b0c70dea7bccc64ebda41c09279945c..5fa6f8bfb046b3a9d484761d8d2615fe22ff6702 100644 --- a/src/plugins/debugger/cdb/cdbengine.h +++ b/src/plugins/debugger/cdb/cdbengine.h @@ -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); diff --git a/src/plugins/debugger/debugger.pro b/src/plugins/debugger/debugger.pro index d8e85d72dca5797bcfcebc9fb7e554a9cb62fbab..3e0d32ba6c2503a2f0f23bdf3deafbb9b6bfcacc 100644 --- a/src/plugins/debugger/debugger.pro +++ b/src/plugins/debugger/debugger.pro @@ -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 \ diff --git a/src/plugins/debugger/debugger.qrc b/src/plugins/debugger/debugger.qrc index c5aa9cc39ef89721871a13242fd724652525ea02..0f7ebee1ab57357e46ae69a012d77761d741c4e6 100644 --- a/src/plugins/debugger/debugger.qrc +++ b/src/plugins/debugger/debugger.qrc @@ -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> diff --git a/src/plugins/debugger/debuggercore.h b/src/plugins/debugger/debuggercore.h index b3126fc88a0e897f7a9be0edde05f351404dbd5c..76e491b7ab13bd5c00afffda7997b77e0f990db0 100644 --- a/src/plugins/debugger/debuggercore.h +++ b/src/plugins/debugger/debuggercore.h @@ -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. diff --git a/src/plugins/debugger/debuggerengine.cpp b/src/plugins/debugger/debuggerengine.cpp index 38fc254ce4ec0f9e87ea06eced08c516a25a814e..3ba459976114a971593de7820291010e2f5f3fd2 100644 --- a/src/plugins/debugger/debuggerengine.cpp +++ b/src/plugins/debugger/debuggerengine.cpp @@ -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 &) { } diff --git a/src/plugins/debugger/debuggerengine.h b/src/plugins/debugger/debuggerengine.h index c70766b88288612655d7439e66d78c5df0bdd2b4..c3b13daeed97cf234b91f4bcd1eb6102e0cb2261 100644 --- a/src/plugins/debugger/debuggerengine.h +++ b/src/plugins/debugger/debuggerengine.h @@ -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 diff --git a/src/plugins/debugger/debuggerplugin.cpp b/src/plugins/debugger/debuggerplugin.cpp index 7af762a428b3cd21d6d6d64f92c900e55cba4a14..263fcd3f98eba129a609f28718f99bdb9430acc1 100644 --- a/src/plugins/debugger/debuggerplugin.cpp +++ b/src/plugins/debugger/debuggerplugin.cpp @@ -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(); } diff --git a/src/plugins/debugger/debuggerrunner.cpp b/src/plugins/debugger/debuggerrunner.cpp index 0ccb78e7d05cbc69d5157c3c938bbf4c90777a37..6ad856cff50449d2d282e7ab37412c9753b30862 100644 --- a/src/plugins/debugger/debuggerrunner.cpp +++ b/src/plugins/debugger/debuggerrunner.cpp @@ -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. diff --git a/src/plugins/debugger/debuggertooltip.cpp b/src/plugins/debugger/debuggertooltip.cpp deleted file mode 100644 index fb1790e61279edc176e2f45d0d2c4887caaab9be..0000000000000000000000000000000000000000 --- a/src/plugins/debugger/debuggertooltip.cpp +++ /dev/null @@ -1,221 +0,0 @@ -/************************************************************************** -** -** 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" diff --git a/src/plugins/debugger/debuggertooltip.h b/src/plugins/debugger/debuggertooltip.h deleted file mode 100644 index 9e7fec0ca0e36011ddd44d8e75a36165afbafd66..0000000000000000000000000000000000000000 --- a/src/plugins/debugger/debuggertooltip.h +++ /dev/null @@ -1,56 +0,0 @@ -/************************************************************************** -** -** 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; -class QPoint; -class QAbstractItemModel; -QT_END_NAMESPACE - -namespace Debugger { -namespace Internal { - -void showDebuggerToolTip(const QPoint &point, const QModelIndex &rootIndex); -// Show tooltip filtering a row of a source model. -void showDebuggerToolTip(const QPoint &point, QAbstractItemModel *model, int row); -void hideDebuggerToolTip(int delay = 0); - -} // namespace Internal -} // namespace Debugger - -#endif // DEBUGGER_DEBUGGERTOOLTIP_H diff --git a/src/plugins/debugger/debuggertooltipmanager.cpp b/src/plugins/debugger/debuggertooltipmanager.cpp new file mode 100644 index 0000000000000000000000000000000000000000..65aab92d06c3db37baa1a47d8166180efa719fd7 --- /dev/null +++ b/src/plugins/debugger/debuggertooltipmanager.cpp @@ -0,0 +1,1242 @@ +/************************************************************************** +** +** 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 "debuggertooltipmanager.h" +#include "watchutils.h" +#include "debuggerengine.h" +#include "watchhandler.h" +#include "stackhandler.h" +#include "debuggercore.h" + +#include <coreplugin/icore.h> +#include <coreplugin/modemanager.h> +#include <coreplugin/imode.h> +#include <coreplugin/editormanager/editormanager.h> +#include <texteditor/itexteditor.h> + +#include <utils/qtcassert.h> + +#include <QtGui/QToolButton> +#include <QtGui/QToolBar> +#include <QtGui/QVBoxLayout> +#include <QtGui/QStyle> +#include <QtGui/QIcon> +#include <QtGui/QApplication> +#include <QtGui/QMainWindow> +#include <QtGui/QMoveEvent> +#include <QtGui/QDesktopWidget> +#include <QtGui/QScrollBar> +#include <QtGui/QSortFilterProxyModel> +#include <QtGui/QStandardItemModel> +#include <QtGui/QStandardItem> +#include <QtGui/QPlainTextEdit> +#include <QtGui/QTextCursor> +#include <QtGui/QTextDocument> +#include <QtGui/QLabel> +#include <QtGui/QMenu> +#include <QtGui/QAction> + +#include <QtCore/QVariant> +#include <QtCore/QStack> +#include <QtCore/QDebug> +#include <QtCore/QTimer> + +enum { debugToolTips = 0 }; + +// Expire tooltips after n days on (no longer load them) in order +// to avoid them piling up. +enum { toolTipsExpiryDays = 6 }; + +static const char sessionSettingsKeyC[] = "DebuggerToolTips"; +static const char sessionDocumentC[] = "DebuggerToolTips"; +static const char sessionVersionAttributeC[] = "version"; +static const char toolTipElementC[] = "DebuggerToolTip"; +static const char toolTipClassAttributeC[] = "class"; +static const char fileNameAttributeC[] = "name"; +static const char functionAttributeC[] = "function"; +static const char textPositionAttributeC[] = "position"; +static const char textLineAttributeC[] = "line"; +static const char textColumnAttributeC[] = "column"; +static const char engineTypeAttributeC[] = "engine"; +static const char dateAttributeC[] = "date"; +static const char treeElementC[] = "tree"; +static const char treeModelAttributeC[] = "model"; // Locals/Watches +static const char treeExpressionAttributeC[] = "expression"; // Locals/Watches +static const char modelElementC[] = "model"; +static const char modelColumnCountAttributeC[] = "columncount"; +static const char modelRowElementC[] = "row"; +static const char modelItemElementC[] = "item"; + +// Forward a stream reader across end elements looking for the +// next start element of a desired type. +static inline bool readStartElement(QXmlStreamReader &r, const char *name) +{ + if (debugToolTips > 1) + qDebug("readStartElement: looking for '%s', currently at: %s/%s", + name, qPrintable(r.tokenString()), qPrintable(r.name().toString())); + while (r.tokenType() != QXmlStreamReader::StartElement || r.name() != QLatin1String(name)) + switch (r.readNext()) { + case QXmlStreamReader::EndDocument: + return false; + case QXmlStreamReader::NoToken: + case QXmlStreamReader::Invalid: + qWarning("'%s'/'%s' encountered while looking for start element '%s'.", + qPrintable(r.tokenString()), qPrintable(r.name().toString()), name); + return false; + default: + break; + } + return true; +} + +static inline void debugMode(const QAbstractItemModel *model) +{ + QDebug nospace = qDebug().nospace(); + nospace << model << '\n'; + for (int r = 0; r < model->rowCount(); r++) + nospace << '#' << r << ' ' << model->data(model->index(r, 0)).toString() << '\n'; +} + +static inline QPlainTextEdit *plainTextEditor(Core::IEditor *ie, QString *fileName = 0) +{ + if (const Core::IFile *file = ie->file()) + if (qobject_cast<TextEditor::ITextEditor *>(ie)) + if (QPlainTextEdit *pe = qobject_cast<QPlainTextEdit *>(ie->widget())) { + if (fileName) + *fileName = file->fileName(); + return pe; + } + return 0; +} + +static inline QPlainTextEdit *currentPlainTextEditor(QString *fileName = 0) +{ + if (Core::IEditor *ie = Core::EditorManager::instance()->currentEditor()) + return plainTextEditor(ie, fileName); + return 0; +} + +namespace Debugger { +namespace Internal { + +/* Helper for building a QStandardItemModel of a tree form (see TreeModelVisitor). + * The recursion/building is based on the scheme: \code +<row><item1><item2> + <row><item11><item12></row> +</row> +\endcode */ + +class StandardItemTreeModelBuilder { +public: + typedef QList<QStandardItem *> StandardItemRow; + + explicit StandardItemTreeModelBuilder(QStandardItemModel *m, Qt::ItemFlags f = Qt::ItemIsSelectable); + + inline void addItem(const QString &); + inline void startRow(); + inline void endRow(); + +private: + inline void pushRow(); + + QStandardItemModel *m_model; + const Qt::ItemFlags m_flags; + StandardItemRow m_row; + QStack<QStandardItem *> m_rowParents; +}; + +StandardItemTreeModelBuilder::StandardItemTreeModelBuilder(QStandardItemModel *m, Qt::ItemFlags f) : + m_model(m), m_flags(f) +{ + m_model->removeRows(0, m_model->rowCount()); +} + +void StandardItemTreeModelBuilder::addItem(const QString &s) +{ + QStandardItem *item = new QStandardItem(s); + item->setFlags(m_flags); + m_row.push_back(item); +} + +void StandardItemTreeModelBuilder::pushRow() +{ + if (m_rowParents.isEmpty()) { + m_model->appendRow(m_row); + } else { + m_rowParents.top()->appendRow(m_row); + } + m_rowParents.push(m_row.front()); + m_row.clear(); +} + +void StandardItemTreeModelBuilder::startRow() +{ + // Push parent in case rows are nested. This is a Noop for the very first row. + if (!m_row.isEmpty()) + pushRow(); +} + +void StandardItemTreeModelBuilder::endRow() +{ + if (!m_row.isEmpty()) // Push row if no child rows have been encountered + pushRow(); + m_rowParents.pop(); +} + +/* Helper visitor base class for recursing over a tree model + * (see StandardItemTreeModelBuilder for the scheme). */ +class TreeModelVisitor { +public: + inline virtual void run() { run(QModelIndex()); } + +protected: + TreeModelVisitor(const QAbstractItemModel *model) : m_model(model) {} + + virtual void rowStarted() {} + virtual void handleItem(const QModelIndex &m) = 0; + virtual void rowEnded() {} + + const QAbstractItemModel *model() const { return m_model; } + +private: + void run(const QModelIndex &parent); + + const QAbstractItemModel *m_model; +}; + +void TreeModelVisitor::run(const QModelIndex &parent) +{ + const int columnCount = m_model->columnCount(parent); + const int rowCount = m_model->rowCount(parent); + for (int r = 0; r < rowCount; r++) { + rowStarted(); + QModelIndex left; + for (int c = 0; c < columnCount; c++) { + const QModelIndex index = m_model->index(r, c, parent); + handleItem(index); + if (!c) + left = index; + } + if (left.isValid()) + run(left); + rowEnded(); + } +} + +// Visitor writing out a tree model in XML format. +class XmlWriterTreeModelVisitor : public TreeModelVisitor { +public: + explicit XmlWriterTreeModelVisitor(const QAbstractItemModel *model, QXmlStreamWriter &w); + + virtual void run(); + +protected: + virtual void rowStarted() { m_writer.writeStartElement(QLatin1String(modelRowElementC)); } + virtual void handleItem(const QModelIndex &m); + virtual void rowEnded() { m_writer.writeEndElement(); } + +private: + const QString m_modelItemElement; + QXmlStreamWriter &m_writer; +}; + +XmlWriterTreeModelVisitor::XmlWriterTreeModelVisitor(const QAbstractItemModel *model, QXmlStreamWriter &w) : + TreeModelVisitor(model), m_modelItemElement(QLatin1String(modelItemElementC)), m_writer(w) +{ +} + +void XmlWriterTreeModelVisitor::run() +{ + m_writer.writeStartElement(QLatin1String(modelElementC)); + const int columnCount = model()->columnCount(); + m_writer.writeAttribute(QLatin1String(modelColumnCountAttributeC), QString::number(columnCount)); + TreeModelVisitor::run(); + m_writer.writeEndElement(); +} + +void XmlWriterTreeModelVisitor::handleItem(const QModelIndex &m) +{ + const QString value = m.data(Qt::DisplayRole).toString(); + if (value.isEmpty()) { + m_writer.writeEmptyElement(m_modelItemElement); + } else { + m_writer.writeTextElement(m_modelItemElement, value); + } +} + +// TreeModelVisitor for debugging models +class DumpTreeModelVisitor : public TreeModelVisitor { +public: + explicit DumpTreeModelVisitor(const QAbstractItemModel *model, QTextStream &s); + +protected: + virtual void rowStarted(); + virtual void handleItem(const QModelIndex &m); + virtual void rowEnded(); + +private: + QTextStream &m_stream; + int m_level; +}; + +DumpTreeModelVisitor::DumpTreeModelVisitor(const QAbstractItemModel *model, QTextStream &s) : + TreeModelVisitor(model), m_stream(s), m_level(0) +{ + m_stream << model->metaObject()->className() << '/' << model->objectName(); +} + +void DumpTreeModelVisitor::rowStarted() +{ + m_level++; + m_stream << '\n' << QString(2 * m_level, QLatin1Char(' ')); +} + +void DumpTreeModelVisitor::handleItem(const QModelIndex &m) +{ + if (m.column()) + m_stream << '|'; + m_stream << '\'' << m.data().toString() << '\''; +} + +void DumpTreeModelVisitor::rowEnded() +{ + m_level--; +} + +} // namespace Internal +} // namespace Debugger + +static inline QDebug operator<<(QDebug d, const QAbstractItemModel &model) +{ + QString s; + QTextStream str(&s); + Debugger::Internal::DumpTreeModelVisitor v(&model, str); + v.run(); + qDebug().nospace() << s; + return d; +} + +namespace Debugger { +namespace Internal { + +// Visitor building a QStandardItem from a tree model (copy). +class TreeModelCopyVisitor : public TreeModelVisitor { +public: + explicit TreeModelCopyVisitor(const QAbstractItemModel *source, QStandardItemModel *target) : + TreeModelVisitor(source), m_builder(target) {} + +protected: + virtual void rowStarted() { m_builder.startRow(); } + virtual void handleItem(const QModelIndex &m) { m_builder.addItem(m.data().toString()); } + virtual void rowEnded() { m_builder.endRow(); } + +private: + StandardItemTreeModelBuilder m_builder; +}; + +/*! + \class PinnableToolTipWidget + + A pinnable tooltip that goes from State 'Unpinned' (button showing + 'Pin') to 'Pinned' (button showing 'Close'). + It consists of a title toolbar and a vertical main layout. +*/ + +PinnableToolTipWidget::PinnableToolTipWidget(QWidget *parent) : + QWidget(parent), + m_pinState(Unpinned), + m_mainVBoxLayout(new QVBoxLayout), + m_toolBar(new QToolBar), + m_toolButton(new QToolButton), + m_menu(new QMenu) +{ + setWindowFlags(Qt::ToolTip | Qt::WindowStaysOnTopHint); + setAttribute(Qt::WA_DeleteOnClose); + + m_mainVBoxLayout->setSizeConstraint(QLayout::SetFixedSize); + m_mainVBoxLayout->setContentsMargins(0, 0, 0, 0); + + const QIcon pinIcon(QLatin1String(":/debugger/images/pin.xpm")); + const QList<QSize> pinIconSizes = pinIcon.availableSizes(); + + m_toolButton->setIcon(pinIcon); + m_menu->addAction(tr("Close All"), this, SIGNAL(closeAllRequested())); + m_toolButton->setMenu(m_menu); + m_toolButton->setPopupMode(QToolButton::MenuButtonPopup); + connect(m_toolButton, SIGNAL(clicked()), this, SLOT(toolButtonClicked())); + + m_toolBar->setProperty("_q_custom_style_disabled", QVariant(true)); + if (!pinIconSizes.isEmpty()) + m_toolBar->setIconSize(pinIconSizes.front()); + m_toolBar->addWidget(m_toolButton); + + m_mainVBoxLayout->addWidget(m_toolBar); + + setLayout(m_mainVBoxLayout); +} + +void PinnableToolTipWidget::addWidget(QWidget *w) +{ + w->setFocusPolicy(Qt::NoFocus); + m_mainVBoxLayout->addWidget(w); +} + +void PinnableToolTipWidget::addToolBarWidget(QWidget *w) +{ + m_toolBar->addWidget(w); +} + +void PinnableToolTipWidget::pin() +{ + if (m_pinState == Unpinned) { + m_pinState = Pinned; + m_toolButton->setIcon(style()->standardIcon(QStyle::SP_DockWidgetCloseButton)); + } +} + +void PinnableToolTipWidget::toolButtonClicked() +{ + switch (m_pinState) { + case Unpinned: + pin(); + break; + case Pinned: + close(); + break; + } +} + +void PinnableToolTipWidget::leaveEvent(QEvent *) +{ + if (m_pinState == Unpinned && QApplication::keyboardModifiers() == Qt::NoModifier) { + if (debugToolTips) + qDebug("ToolTipWidget::leaveEvent: closing %p", this); + close(); + } +} + +/*! + \class DebuggerToolTipContext + + File name and position where the tooltip is anchored. Redundant position/line column + information is used to detect if the underlying file has been changed + on restoring. +*/ + +DebuggerToolTipContext::DebuggerToolTipContext() : position(0), line(0), column(0) +{ +} + +DebuggerToolTipContext DebuggerToolTipContext::fromEditor(Core::IEditor *ie, int pos) +{ + DebuggerToolTipContext rc; + if (const Core::IFile *file = ie->file()) { + if (const TextEditor::ITextEditor *te = qobject_cast<const TextEditor::ITextEditor *>(ie)) { + rc.fileName = file->fileName(); + rc.position = pos; + te->convertPosition(pos, &rc.line, &rc.column); + } + } + return rc; +} + +QDebug operator<<(QDebug d, const DebuggerToolTipContext &c) +{ + QDebug nsp = d.nospace(); + nsp << c.fileName << '@' << c.line << ',' << c.column << " (" << c.position << ')'; + if (!c.function.isEmpty()) + nsp << ' ' << c.function << "()"; + return d; +} + +/*! + \class AbstractDebuggerToolTipWidget + + Base class for a tool tip widget associated with file name + and position with functionality to + \list + \o Save and restore values (session data) in XML form + \o Acquire and release the engine: When the debugger stops at a file, all + matching tooltips acquire the engine, that is, display the engine data. + When continuing or switching away from the frame, the tooltips release the + engine, that is, store the data internally and keep displaying them + marked as 'previous'. + \endlist + When restoring the data from a session, all tooltips start in 'released' mode. + + Stored tooltips expire after toolTipsExpiryDays while loading to prevent + them accumulating. + + In addition, if the stored line number diverges too much from the current line + number in positionShow(), the tooltip is also closed/discarded. +*/ + +static inline QString msgReleasedText() { return AbstractDebuggerToolTipWidget::tr("Previous"); } + +AbstractDebuggerToolTipWidget::AbstractDebuggerToolTipWidget(QWidget *parent) : + PinnableToolTipWidget(parent), + m_titleLabel(new QLabel), m_engineAcquired(false), + m_creationDate(QDate::currentDate()) +{ + m_titleLabel->setText(msgReleasedText()); + addToolBarWidget(m_titleLabel); +} + +bool AbstractDebuggerToolTipWidget::matches(const QString &fileName, + const QString &engineType, + const QString &function) const +{ + if (fileName.isEmpty() || m_context.fileName != fileName) + return false; + // Optional. + if (!engineType.isEmpty() && engineType != m_engineType) + return false; + if (function.isEmpty() || m_context.function.isEmpty()) + return true; + return function == m_context.function; +} + +void AbstractDebuggerToolTipWidget::acquireEngine(Debugger::DebuggerEngine *engine) +{ + QTC_ASSERT(engine, return; ) + + + if (debugToolTips) + qDebug() << this << " acquiring" << engine << m_engineAcquired; + if (m_engineAcquired) + return; + doAcquireEngine(engine); + m_engineType = engine->objectName(); + m_engineAcquired = true; + m_titleLabel->setText(QString()); +} + +void AbstractDebuggerToolTipWidget::releaseEngine() +{ + // Release engine of same type + if (!m_engineAcquired) + return; + if (debugToolTips) + qDebug() << "releaseEngine" << this; + doReleaseEngine(); + m_titleLabel->setText(msgReleasedText()); + m_engineAcquired = false; +} + +bool AbstractDebuggerToolTipWidget::positionShow(const QPlainTextEdit *pe) +{ + // Figure out new position of tooltip using the text edit. + // If the line changed too much, close this tip. + QTC_ASSERT(pe, return false; ) + QTextCursor cursor(pe->document()); + cursor.setPosition(m_context.position); + const int line = cursor.blockNumber(); + if (qAbs(m_context.line - line) > 2) { + if (debugToolTips) + qDebug() << "Closing " << this << " in positionShow() lines " + << line << m_context.line; + close(); + return false; + } + const QRect plainTextToolTipArea = QRect(pe->cursorRect(cursor).topLeft(), QSize(sizeHint())); + const QRect plainTextArea = QRect(QPoint(0, 0), QPoint(pe->width(), pe->height())); + const QPoint screenPos = pe->mapToGlobal(plainTextToolTipArea.topLeft()); + const bool visible = plainTextArea.contains(plainTextToolTipArea); + if (debugToolTips) + qDebug() << "DebuggerToolTipWidget::positionShow() " << m_context + << " line: " << line << " plainTextPos " << plainTextToolTipArea + << " Area: " << plainTextArea << " Screen pos: " + << screenPos << pe << " visible=" << visible + << " on " << pe->parentWidget() + << " at " << pe->mapToGlobal(QPoint(0, 0)); + + if (!visible) { + hide(); + return false; + } + + move(screenPos); + show(); + return true; +} + + // Parse a 'yyyyMMdd' date +static inline QDate dateFromString(const QString &date) +{ + return date.size() == 8 ? + QDate(date.left(4).toInt(), date.mid(4, 2).toInt(), date.mid(6, 2).toInt()) : + QDate(); +} + +AbstractDebuggerToolTipWidget *AbstractDebuggerToolTipWidget::loadSessionData(QXmlStreamReader &r) +{ + if (debugToolTips) + qDebug() << ">DebuggerToolTipWidget::loadSessionData" << r.tokenString() << r.name(); + AbstractDebuggerToolTipWidget *rc = AbstractDebuggerToolTipWidget::loadSessionDataI(r); + if (debugToolTips) + qDebug() << "<DebuggerToolTipWidget::loadSessionData" << r.tokenString() << r.name() << " returns " << rc; + return rc; +} + +AbstractDebuggerToolTipWidget *AbstractDebuggerToolTipWidget::loadSessionDataI(QXmlStreamReader &r) +{ + if (!readStartElement(r, toolTipElementC)) + return 0; + const QXmlStreamAttributes attributes = r.attributes(); + DebuggerToolTipContext context; + context.fileName = attributes.value(QLatin1String(fileNameAttributeC)).toString(); + context.position = attributes.value(QLatin1String(textPositionAttributeC)).toString().toInt(); + context.line = attributes.value(QLatin1String(textLineAttributeC)).toString().toInt(); + context.column = attributes.value(QLatin1String(textColumnAttributeC)).toString().toInt(); + context.function = attributes.value(QLatin1String(functionAttributeC)).toString(); + const QString className = attributes.value(QLatin1String(toolTipClassAttributeC)).toString(); + const QString engineType = attributes.value(QLatin1String(engineTypeAttributeC)).toString(); + const QDate creationDate = dateFromString(attributes.value(QLatin1String(dateAttributeC)).toString()); + if (!creationDate.isValid() || creationDate.daysTo(QDate::currentDate()) > toolTipsExpiryDays) { + if (debugToolTips) + qDebug() << "Expiring tooltip " << context.fileName << '@' << context.position << " from " << creationDate; + r.readElementText(QXmlStreamReader::SkipChildElements); // Skip + return 0; + } + if (debugToolTips) + qDebug() << "Creating tooltip " << context << " from " << creationDate; + AbstractDebuggerToolTipWidget *rc = 0; + if (className == "Debugger::Internal::DebuggerTreeViewToolTipWidget") + rc = new DebuggerTreeViewToolTipWidget; + if (rc) { + rc->setContext(context); + rc->setEngineType(engineType); + rc->doLoadSessionData(r); + rc->setCreationDate(creationDate); + rc->pin(); + } else { + qWarning("Unable to create debugger tool tip widget of class %s", qPrintable(className)); + r.readElementText(QXmlStreamReader::SkipChildElements); // Skip + } + return rc; +} + +void AbstractDebuggerToolTipWidget::saveSessionData(QXmlStreamWriter &w) const +{ + w.writeStartElement(QLatin1String(toolTipElementC)); + QXmlStreamAttributes attributes; + attributes.append(QLatin1String(toolTipClassAttributeC), QString::fromAscii(metaObject()->className())); + attributes.append(QLatin1String(fileNameAttributeC), m_context.fileName); + if (!m_context.function.isEmpty()) + attributes.append(QLatin1String(functionAttributeC), m_context.function); + attributes.append(QLatin1String(textPositionAttributeC), QString::number(m_context.position)); + attributes.append(QLatin1String(textLineAttributeC), QString::number(m_context.line)); + attributes.append(QLatin1String(textColumnAttributeC), QString::number(m_context.column)); + attributes.append(QLatin1String(dateAttributeC), m_creationDate.toString(QLatin1String("yyyyMMdd"))); + if (!m_engineType.isEmpty()) + attributes.append(QLatin1String(engineTypeAttributeC), m_engineType); + w.writeAttributes(attributes); + doSaveSessionData(w); + w.writeEndElement(); +} + +// Model for tooltips filtering a local variable using the locals model, +// taking the expression. +class DebuggerToolTipExpressionFilterModel : public QSortFilterProxyModel +{ +public: + explicit DebuggerToolTipExpressionFilterModel(QAbstractItemModel *model, const QString &exp, QObject *parent = 0); + virtual bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const; + +private: + const QString m_expression; +}; + +DebuggerToolTipExpressionFilterModel::DebuggerToolTipExpressionFilterModel(QAbstractItemModel *model, + const QString &exp, + QObject *parent) : + QSortFilterProxyModel(parent), m_expression(exp) +{ + setSourceModel(model); +} + +bool DebuggerToolTipExpressionFilterModel::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const +{ + // Match on expression for top level, else pass through. + if (sourceParent.isValid()) + return true; + const QModelIndex expIndex = sourceModel()->index(sourceRow, 0, sourceParent); + return expIndex.data().toString() == m_expression; +} + +/*! + \class DebuggerToolTipTreeView + + A treeview that adapts its size to the model contents (also while expanding) + to be used within DebuggerTreeViewToolTipWidget. + +*/ + +DebuggerToolTipTreeView::DebuggerToolTipTreeView(QWidget *parent) : + QTreeView(parent) +{ + setHeaderHidden(true); + + 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); +} + +QAbstractItemModel *DebuggerToolTipTreeView::swapModel(QAbstractItemModel *newModel) +{ + QAbstractItemModel *previousModel = model(); + if (previousModel != newModel) { + if (previousModel) + previousModel->disconnect(SIGNAL(rowsInserted(QModelIndex,int,int)), this); + setModel(newModel); + connect(newModel, SIGNAL(rowsInserted(QModelIndex,int,int)), + this, SLOT(computeSize()), Qt::QueuedConnection); + computeSize(); + } + return previousModel; +} + +int DebuggerToolTipTreeView::computeHeight(const QModelIndex &index) const +{ + int s = rowHeight(index); + const int rowCount = model()->rowCount(index); + for (int i = 0; i < rowCount; ++i) + s += computeHeight(model()->index(i, 0, index)); + return s; +} + +void DebuggerToolTipTreeView::computeSize() +{ + int columns = 30; // Decoration + int rows = 0; + bool rootDecorated = false; + + if (model()) { + const int columnCount = model()->columnCount(); + rootDecorated = model()->rowCount() > 0; + if (rootDecorated) + for (int i = 0; i < columnCount; ++i) { + resizeColumnToContents(i); + columns += sizeHintForColumn(i); + } + if (columns < 100) + columns = 100; // Prevent toolbar from shrinking when displaying 'Previous' + 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); + setRootIsDecorated(rootDecorated); +} + +/*! + \class DebuggerTreeViewToolTipWidget + + Tool tip widget for tree views with functionality to save/restore tree + model contents to XML. + With the engine acquired, it sets a filter model (by expression) on + one of the engine's models (debuggerModel). + On release, it serializes and restores the data to a QStandardItemModel + (defaultModel) and displays that. + +*/ + +DebuggerTreeViewToolTipWidget::DebuggerTreeViewToolTipWidget(QWidget *parent) : + AbstractDebuggerToolTipWidget(parent), + m_debuggerModel(TooltipsWatch), + m_treeView(new DebuggerToolTipTreeView), + m_defaultModel(new QStandardItemModel(this)) +{ + addWidget(m_treeView); +} + +void DebuggerTreeViewToolTipWidget::doAcquireEngine(Debugger::DebuggerEngine *engine) +{ + // Create a filter model on the debugger's model and switch to it. + QAbstractItemModel *model = 0; + switch (m_debuggerModel) { + case LocalsWatch: + model = engine->localsModel(); + break; + case WatchersWatch: + model = engine->watchersModel(); + break; + case TooltipsWatch: + model = engine->toolTipsModel(); + break; + } + QTC_ASSERT(model, return ;) + DebuggerToolTipExpressionFilterModel *filterModel = + new DebuggerToolTipExpressionFilterModel(model, m_expression); + m_treeView->swapModel(filterModel); +} + +void DebuggerTreeViewToolTipWidget::doReleaseEngine() +{ + // Save data to stream and restore to the m_defaultModel (QStandardItemModel) + m_defaultModel->removeRows(0, m_defaultModel->rowCount()); + if (const QAbstractItemModel *model = m_treeView->model()) { + TreeModelCopyVisitor v(model, m_defaultModel); + v.run(); + } + delete m_treeView->swapModel(m_defaultModel); +} + +void DebuggerTreeViewToolTipWidget::restoreTreeModel(QXmlStreamReader &r, QStandardItemModel *m) +{ + StandardItemTreeModelBuilder builder(m); + int columnCount = 1; + bool withinModel = true; + while (withinModel && !r.atEnd()) { + const QXmlStreamReader::TokenType token = r.readNext(); + switch (token) { + case QXmlStreamReader::StartElement: { + const QStringRef element = r.name(); + // Root model element with column count. + if (element == QLatin1String(modelElementC)) { + if (const int cc = r.attributes().value(QLatin1String(modelColumnCountAttributeC)).toString().toInt()) + columnCount = cc; + m->setColumnCount(columnCount); + } else if (element == QLatin1String(modelRowElementC)) { + builder.startRow(); + } else if (element == QLatin1String(modelItemElementC)) { + builder.addItem(r.readElementText()); + } + } + break; // StartElement + case QXmlStreamReader::EndElement: { + const QStringRef element = r.name(); + // Row closing: pop off parent. + if (element == QLatin1String(modelRowElementC)) { + builder.endRow(); + } else if (element == QLatin1String(modelElementC)) { + withinModel = false; + } + } + break; // EndElement + default: + break; + } // switch + } // while +} + +void DebuggerTreeViewToolTipWidget::doSaveSessionData(QXmlStreamWriter &w) const +{ + w.writeStartElement(QLatin1String(treeElementC)); + QXmlStreamAttributes attributes; + attributes.append(QLatin1String(treeModelAttributeC), QString::number(m_debuggerModel)); + attributes.append(QLatin1String(treeExpressionAttributeC), m_expression); + w.writeAttributes(attributes); + if (QAbstractItemModel *model = m_treeView->model()) { + XmlWriterTreeModelVisitor v(model, w); + v.run(); + } + w.writeEndElement(); +} + +void DebuggerTreeViewToolTipWidget::doLoadSessionData(QXmlStreamReader &r) +{ + if (!readStartElement(r, treeElementC)) + return; + // Restore data to default model and show that. + const QXmlStreamAttributes attributes = r.attributes(); + m_debuggerModel = attributes.value(QLatin1String(treeModelAttributeC)).toString().toInt(); + m_expression = attributes.value(QLatin1String(treeExpressionAttributeC)).toString(); + if (debugToolTips) + qDebug() << "DebuggerTreeViewToolTipWidget::doLoadSessionData() " << m_debuggerModel << m_expression; + setObjectName(QLatin1String("DebuggerTreeViewToolTipWidget: ") + m_expression); + restoreTreeModel(r, m_defaultModel); + r.readNext(); // Skip </tree> + m_treeView->swapModel(m_defaultModel); +} + +/*! + \class DebuggerToolTipManager + + Manages the tooltip widgets, listens on editor scroll and main window move + events and takes care of repositioning the tooltips. + + Listens to editor change and mode change. In debug mode, if there tooltips + for the current editor (by file name), position and show them. + + In addition, listen on state and stack frame change of the engine. + If a stack frame is activated, have all matching tooltips (by file name) + acquire the engine, other release. + +*/ + +DebuggerToolTipManager *DebuggerToolTipManager::m_instance = 0; + +DebuggerToolTipManager::DebuggerToolTipManager(QObject *parent) : + QObject(parent), m_debugModeActive(false) +{ + DebuggerToolTipManager::m_instance = this; +} + +DebuggerToolTipManager::~DebuggerToolTipManager() +{ + DebuggerToolTipManager::m_instance = 0; +} + +void DebuggerToolTipManager::registerEngine(DebuggerEngine *engine) +{ + connect(engine, SIGNAL(stateChanged(Debugger::DebuggerState)), + this, SLOT(slotDebuggerStateChanged(Debugger::DebuggerState))); + connect(engine, SIGNAL(stackFrameCompleted()), this, SLOT(slotStackFrameCompleted())); +} + +void DebuggerToolTipManager::add(const QPoint &p, AbstractDebuggerToolTipWidget *toolTipWidget) +{ + closeUnpinnedToolTips(); + toolTipWidget->move(p); + toolTipWidget->show(); + add(toolTipWidget); +} + +void DebuggerToolTipManager::add(AbstractDebuggerToolTipWidget *toolTipWidget) +{ + QTC_ASSERT(toolTipWidget->context().isValid(), return; ) + connect(toolTipWidget, SIGNAL(closeAllRequested()), this, SLOT(closeAllToolTips())); + m_tooltips.push_back(toolTipWidget); +} + +DebuggerToolTipManager::DebuggerToolTipWidgetList &DebuggerToolTipManager::purgeClosedToolTips() +{ + if (!m_tooltips.isEmpty()) { + for (DebuggerToolTipWidgetList::iterator it = m_tooltips.begin(); it != m_tooltips.end() ; ) { + if (it->isNull()) { + it = m_tooltips.erase(it); + } else { + ++it; + } + } + } + return m_tooltips; +} + +void DebuggerToolTipManager::moveToolTipsBy(const QPoint &distance) +{ + foreach (const QPointer<AbstractDebuggerToolTipWidget> &tw, purgeClosedToolTips()) + if (tw->isVisible()) + tw->move (tw->pos() + distance); +} + +bool DebuggerToolTipManager::eventFilter(QObject *, QEvent *e) +{ + // Move along with parent (toplevel) + if (e->type() == QEvent::Move && isActive()) { + const QMoveEvent *me = static_cast<const QMoveEvent *>(e); + moveToolTipsBy(me->pos() - me->oldPos()); + } + return false; +} + +void DebuggerToolTipManager::sessionAboutToChange() +{ + closeAllToolTips(); +} + +void DebuggerToolTipManager::loadSessionData() +{ + const QString data = debuggerCore()->sessionValue(QLatin1String(sessionSettingsKeyC)).toString(); + if (data.isEmpty()) + return; + QXmlStreamReader r(data); + r.readNextStartElement(); + if (r.tokenType() != QXmlStreamReader::StartElement || r.name() != QLatin1String(sessionDocumentC)) + return; + const double version = r.attributes().value(QLatin1String(sessionVersionAttributeC)).toString().toDouble(); + while (!r.atEnd()) + if (AbstractDebuggerToolTipWidget *tw = AbstractDebuggerToolTipWidget::loadSessionData(r)) + add(tw); + + if (debugToolTips) + qDebug() << "DebuggerToolTipManager::loadSessionData version " << version << " restored " << m_tooltips.size(); + slotUpdateVisibleToolTips(); +} + +void DebuggerToolTipManager::saveSessionData() +{ + QString data; + if (!purgeClosedToolTips().isEmpty()) { + QXmlStreamWriter w(&data); + w.writeStartDocument(); + w.writeStartElement(QLatin1String(sessionDocumentC)); + w.writeAttribute(QLatin1String(sessionVersionAttributeC), QLatin1String("1.0")); + foreach (const QPointer<AbstractDebuggerToolTipWidget> &tw, m_tooltips) + tw->saveSessionData(w); + w.writeEndDocument(); + } + if (debugToolTips) + qDebug() << "DebuggerToolTipManager::saveSessionData" << m_tooltips.size() << data ; + debuggerCore()->setSessionValue(QLatin1String(sessionSettingsKeyC), QVariant(data)); +} + +void DebuggerToolTipManager::closeAllToolTips() +{ + if (debugToolTips) + qDebug() << "DebuggerToolTipManager::closeAllToolTips"; + + foreach (const QPointer<AbstractDebuggerToolTipWidget> &tw, purgeClosedToolTips()) + tw->close(); + m_tooltips.clear(); +} + +void DebuggerToolTipManager::closeUnpinnedToolTips() +{ + if (debugToolTips) + qDebug() << "DebuggerToolTipManager::closeUnpinnedToolTips"; + + // Filter out unpinned ones, purge null on that occasion + for (DebuggerToolTipWidgetList::iterator it = m_tooltips.begin(); it != m_tooltips.end() ; ) { + if (it->isNull()) { + it = m_tooltips.erase(it); + } else if ((*it)->pinState() == AbstractDebuggerToolTipWidget::Unpinned) { + (*it)->close(); + it = m_tooltips.erase(it); + } else { + ++it; + } + } +} + +void DebuggerToolTipManager::hide() +{ + foreach (const QPointer<AbstractDebuggerToolTipWidget> &tw, purgeClosedToolTips()) + tw->hide(); +} + +void DebuggerToolTipManager::slotUpdateVisibleToolTips() +{ + if (purgeClosedToolTips().isEmpty()) + return; + if (!m_debugModeActive) { + hide(); + return; + } + + QString fileName; + QPlainTextEdit *plainTextEdit = currentPlainTextEditor(&fileName); + + if (debugToolTips) + qDebug() << "DebuggerToolTipManager::slotUpdateVisibleToolTips() " << fileName << sender(); + + if (fileName.isEmpty() || !plainTextEdit) { + hide(); + return; + } + + // Reposition and show all tooltips of that file. + foreach (const QPointer<AbstractDebuggerToolTipWidget> &tw, m_tooltips) { + if (tw->fileName() == fileName) { + tw->positionShow(plainTextEdit); + } else { + tw->hide(); + } + } +} + +void DebuggerToolTipManager::slotDebuggerStateChanged(Debugger::DebuggerState state) +{ + const QObject *engine = sender(); + QTC_ASSERT(engine, return; ) + + const QString name = engine->objectName(); + if (debugToolTips) + qDebug() << "DebuggerToolTipWidget::debuggerStateChanged" + << engine << Debugger::DebuggerEngine::stateName(state); + + // Release at earliest possible convenience. + switch (state) { + case InferiorRunRequested: + case DebuggerNotReady: + case InferiorShutdownRequested: + case EngineShutdownRequested: + case DebuggerFinished: + case EngineShutdownOk: + foreach (const QPointer<AbstractDebuggerToolTipWidget> &tw, purgeClosedToolTips()) + if (tw->engineType() == name) + tw->releaseEngine(); + break; + default: + break; + } +} + +void DebuggerToolTipManager::slotStackFrameCompleted() +{ + if (purgeClosedToolTips().isEmpty()) + return; + DebuggerEngine *engine = qobject_cast<DebuggerEngine *>(sender()); + QTC_ASSERT(engine, return ; ) + + // Stack frame changed: All tooltips of that file acquire the engine, + // all others release (arguable, this could be more precise?) + QString fileName; + int lineNumber = 0; + // Get the current frame + const QString engineName = engine->objectName(); + QString function; + const int index = engine->stackHandler()->currentIndex(); + if (index >= 0) { + const StackFrame frame = engine->stackHandler()->currentFrame(); + if (frame.usable) { + fileName = frame.file; + lineNumber = frame.line; + function = frame.function; + } + } + if (debugToolTips) + qDebug("DebuggerToolTipWidget::slotStackFrameCompleted(%s, %s@%d, %s())", + qPrintable(engineName), qPrintable(fileName), lineNumber, + qPrintable(function)); + unsigned acquiredCount = 0; + foreach (const QPointer<AbstractDebuggerToolTipWidget> &tw, m_tooltips) { + if (tw->matches(fileName, engineName, function)) { + tw->acquireEngine(engine); + acquiredCount++; + } else { + tw->releaseEngine(); + } + } + slotUpdateVisibleToolTips(); // Move out when stepping in same file. + if (debugToolTips) + qDebug() << "DebuggerToolTipWidget::slotStackFrameCompleted()" + << engineName << fileName << lineNumber << " acquired " << acquiredCount; +} + +bool DebuggerToolTipManager::debug() +{ + return debugToolTips; +} + +void DebuggerToolTipManager::slotEditorOpened(Core::IEditor *e) +{ + // Move tooltip along when scrolled. + if (QPlainTextEdit *plainTextEdit = plainTextEditor(e)) { + connect(plainTextEdit->verticalScrollBar(), SIGNAL(valueChanged(int)), + this, SLOT(slotUpdateVisibleToolTips())); + } +} + +void DebuggerToolTipManager::debugModeEntered() +{ + if (debugToolTips) + qDebug("DebuggerToolTipManager::debugModeEntered"); + + // Hook up all signals in debug mode. + if (!m_debugModeActive) { + m_debugModeActive = true; + QWidget *topLevel = Core::ICore::instance()->mainWindow()->topLevelWidget(); + topLevel->installEventFilter(this); + Core::EditorManager *em = Core::EditorManager::instance(); + connect(em, SIGNAL(currentEditorChanged(Core::IEditor*)), + this, SLOT(slotUpdateVisibleToolTips())); + connect(em, SIGNAL(editorOpened(Core::IEditor*)), + this, SLOT(slotEditorOpened(Core::IEditor*))); + foreach (Core::IEditor *e, em->openedEditors()) + slotEditorOpened(e); + // Position tooltips delayed once all the editor placeholder layouting is done. + if (!m_tooltips.isEmpty()) + QTimer::singleShot(0, this, SLOT(slotUpdateVisibleToolTips())); + } +} + +void DebuggerToolTipManager::leavingDebugMode() +{ + if (debugToolTips) + qDebug("DebuggerToolTipManager::leavingDebugMode"); + + // Remove all signals in debug mode. + if (m_debugModeActive) { + m_debugModeActive = false; + hide(); + QWidget *topLevel = Core::ICore::instance()->mainWindow()->topLevelWidget(); + topLevel->removeEventFilter(this); + Core::EditorManager *em = Core::EditorManager::instance(); + foreach (Core::IEditor *e, em->openedEditors()) + if (QPlainTextEdit *plainTextEdit = plainTextEditor(e)) + plainTextEdit->verticalScrollBar()->disconnect(this); + em->disconnect(this); + } +} + +QStringList DebuggerToolTipManager::treeWidgetExpressions(const QString &fileName, + const QString &engineType, + const QString &function) const +{ + QStringList rc; + foreach (const QPointer<AbstractDebuggerToolTipWidget> &tw, m_tooltips) + if (!tw.isNull() && tw->matches(fileName, engineType, function)) + if (const DebuggerTreeViewToolTipWidget *ttw = qobject_cast<const DebuggerTreeViewToolTipWidget *>(tw.data())) + rc.push_back(ttw->expression()); + if (debugToolTips) + qDebug() << "DebuggerToolTipManager::treeWidgetExpressions" + << fileName << engineType << function << rc; + return rc; +} + +} // namespace Internal +} // namespace Debugger diff --git a/src/plugins/debugger/debuggertooltipmanager.h b/src/plugins/debugger/debuggertooltipmanager.h new file mode 100644 index 0000000000000000000000000000000000000000..c50dd02d14597c5045afca828c84de58513bd958 --- /dev/null +++ b/src/plugins/debugger/debuggertooltipmanager.h @@ -0,0 +1,278 @@ +/************************************************************************** +** +** 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_INTERNAL_DEBUGGERTOOLTIPMANAGER_H +#define DEBUGGER_INTERNAL_DEBUGGERTOOLTIPMANAGER_H + +#include "debuggerconstants.h" + +#include <QtGui/QTreeView> + +#include <QtCore/QPointer> +#include <QtCore/QList> +#include <QtCore/QXmlStreamWriter> +#include <QtCore/QXmlStreamReader> +#include <QtCore/QDate> + +QT_BEGIN_NAMESPACE +class QVBoxLayout; +class QToolButton; +class QSortFilterProxyModel; +class QStandardItemModel; +class QPlainTextEdit; +class QLabel; +class QToolBar; +class QMenu; +class QDebug; +QT_END_NAMESPACE + +namespace Core { +class IEditor; +class IMode; +} + +namespace Debugger { +class DebuggerEngine; + +namespace Internal { + +class PinnableToolTipWidget : public QWidget +{ + Q_OBJECT + +public: + enum PinState + { + Unpinned, + Pinned + }; + + explicit PinnableToolTipWidget(QWidget *parent = 0); + PinState pinState() const { return m_pinState; } + + void addWidget(QWidget *w); + void addToolBarWidget(QWidget *w); + +public slots: + void pin(); + +signals: + void closeAllRequested(); + +protected: + virtual void leaveEvent(QEvent *ev); + +private slots: + void toolButtonClicked(); + +private: + PinState m_pinState; + QVBoxLayout *m_mainVBoxLayout; + QToolBar *m_toolBar; + QToolButton *m_toolButton; + QMenu *m_menu; +}; + +class DebuggerToolTipContext +{ +public: + DebuggerToolTipContext(); + static DebuggerToolTipContext fromEditor(Core::IEditor *ed, int pos); + bool isValid() const { return !fileName.isEmpty(); } + + QString fileName; + int position; + int line; + int column; + QString function; //!< Optional function. This must be set by the engine as it is language-specific. +}; + +QDebug operator<<(QDebug, const DebuggerToolTipContext &); + +class AbstractDebuggerToolTipWidget : public PinnableToolTipWidget +{ + Q_OBJECT + +public: + explicit AbstractDebuggerToolTipWidget(QWidget *parent = 0); + bool engineAcquired() const { return m_engineAcquired; } + + QString fileName() const { return m_context.fileName; } + QString function() const { return m_context.function; } + int position() const { return m_context.position; } + // Check for a match at position. + bool matches(const QString &fileName, + const QString &engineType = QString(), + const QString &function= QString()) const; + + const DebuggerToolTipContext &context() const { return m_context; } + void setContext(const DebuggerToolTipContext &c) { m_context = c; } + + QString engineType() const { return m_engineType; } + void setEngineType(const QString &e) { m_engineType = e; } + + QDate creationDate() const { return m_creationDate; } + void setCreationDate(const QDate &d) { m_creationDate = d; } + + static AbstractDebuggerToolTipWidget *loadSessionData(QXmlStreamReader &r); + +public slots: + void saveSessionData(QXmlStreamWriter &w) const; + + void acquireEngine(Debugger::DebuggerEngine *engine); + void releaseEngine(); + bool positionShow(const QPlainTextEdit *pe); + +protected: + virtual void doAcquireEngine(Debugger::DebuggerEngine *engine) = 0; + virtual void doReleaseEngine() = 0; + virtual void doSaveSessionData(QXmlStreamWriter &w) const = 0; + virtual void doLoadSessionData(QXmlStreamReader &r) = 0; + +private: + static AbstractDebuggerToolTipWidget *loadSessionDataI(QXmlStreamReader &r); + QLabel *m_titleLabel; + bool m_engineAcquired; + QString m_engineType; + DebuggerToolTipContext m_context; + QDate m_creationDate; +}; + +class DebuggerToolTipTreeView : public QTreeView +{ + Q_OBJECT + +public: + explicit DebuggerToolTipTreeView(QWidget *parent = 0); + + QAbstractItemModel *swapModel(QAbstractItemModel *model); + + QSize sizeHint() const { return m_size; } + + int computeHeight(const QModelIndex &index) const; + +public slots: + void computeSize(); + +private: + void init(QAbstractItemModel *model); + + QSize m_size; +}; + +class DebuggerTreeViewToolTipWidget : public AbstractDebuggerToolTipWidget +{ + Q_OBJECT + +public: + explicit DebuggerTreeViewToolTipWidget(QWidget *parent = 0); + + int debuggerModel() const { return m_debuggerModel; } + void setDebuggerModel(int m) { m_debuggerModel = m; } + QString expression() const { return m_expression; } + void setExpression(const QString &e) { m_expression = e; } + +protected: + virtual void doAcquireEngine(Debugger::DebuggerEngine *engine); + virtual void doReleaseEngine(); + virtual void doSaveSessionData(QXmlStreamWriter &w) const; + virtual void doLoadSessionData(QXmlStreamReader &r); + +private: + static void restoreTreeModel(QXmlStreamReader &r, QStandardItemModel *m); + + int m_debuggerModel; + QString m_expression; + + DebuggerToolTipTreeView *m_treeView; + QStandardItemModel *m_defaultModel; +}; + +class DebuggerToolTipManager : public QObject +{ + Q_OBJECT +public: + explicit DebuggerToolTipManager(QObject *parent = 0); + virtual ~DebuggerToolTipManager(); + + static DebuggerToolTipManager *instance() { return m_instance; } + void registerEngine(DebuggerEngine *engine); + + // Collect all expressions of DebuggerTreeViewToolTipWidget + QStringList treeWidgetExpressions(const QString &fileName, + const QString &engineType = QString(), + const QString &function= QString()) const; + + void add(const QPoint &p, AbstractDebuggerToolTipWidget *w); + + virtual bool eventFilter(QObject *, QEvent *); + + static bool debug(); + +signals: + +public slots: + void debugModeEntered(); + void leavingDebugMode(); + void sessionAboutToChange(); + void loadSessionData(); + void saveSessionData(); + void closeAllToolTips(); + void closeUnpinnedToolTips(); + void hide(); + +private slots: + void slotUpdateVisibleToolTips(); + void slotDebuggerStateChanged(Debugger::DebuggerState); + void slotStackFrameCompleted(); + void slotEditorOpened(Core::IEditor *); + +private: + typedef QList<QPointer<AbstractDebuggerToolTipWidget> > DebuggerToolTipWidgetList; + + inline bool isActive() const { return !m_tooltips.isEmpty(); } + void add(AbstractDebuggerToolTipWidget *toolTipWidget); + void moveToolTipsBy(const QPoint &distance); + // Purge out closed (null) tooltips and return list for convenience + DebuggerToolTipWidgetList &purgeClosedToolTips(); + + static DebuggerToolTipManager *m_instance; + + DebuggerToolTipWidgetList m_tooltips; + bool m_debugModeActive; +}; + +} // namespace Internal +} // namespace Debugger + +#endif // DEBUGGER_INTERNAL_DEBUGGERTOOLTIPMANAGER_H diff --git a/src/plugins/debugger/gdb/classicgdbengine.cpp b/src/plugins/debugger/gdb/classicgdbengine.cpp index 6e12e6b665ffe187fef96ca896ebb1106952cb44..ad9bad466bae6bffd3afa6f66ad0ab3d31e6e82f 100644 --- a/src/plugins/debugger/gdb/classicgdbengine.cpp +++ b/src/plugins/debugger/gdb/classicgdbengine.cpp @@ -119,7 +119,7 @@ void GdbEngine::updateLocalsClassic(const QVariant &cookie) //PENDING_DEBUG("\nRESET PENDING"); //m_toolTipCache.clear(); - m_toolTipExpression.clear(); + clearToolTip(); watchHandler()->beginCycle(); QByteArray level = QByteArray::number(currentFrame()); diff --git a/src/plugins/debugger/gdb/gdbengine.cpp b/src/plugins/debugger/gdb/gdbengine.cpp index a4054c79c04f9799d91f99dce87590f432bf31df..7ff42ed1aa58cb7109ee9b6aa1d9a6b0a91d1fe4 100644 --- a/src/plugins/debugger/gdb/gdbengine.cpp +++ b/src/plugins/debugger/gdb/gdbengine.cpp @@ -52,7 +52,7 @@ #include "debuggerplugin.h" #include "debuggerrunner.h" #include "debuggerstringutils.h" -#include "debuggertooltip.h" +#include "debuggertooltipmanager.h" #include "disassembleragent.h" #include "gdbmi.h" #include "gdboptionspage.h" @@ -74,6 +74,7 @@ #include "logwindow.h" #include <coreplugin/icore.h> +#include <coreplugin/ifile.h> #include <projectexplorer/toolchain.h> #include <texteditor/itexteditor.h> #include <utils/qtcassert.h> @@ -101,10 +102,18 @@ #endif #include <ctype.h> - namespace Debugger { namespace Internal { +class GdbToolTipContext : public DebuggerToolTipContext +{ +public: + GdbToolTipContext(const DebuggerToolTipContext &c) : DebuggerToolTipContext(c) {} + + QPoint mousePosition; + QString expression; +}; + static const char winPythonVersionC[] = "python2.5"; enum { debugPending = 0 }; @@ -1103,7 +1112,7 @@ void GdbEngine::handleQuerySources(const GdbResponse &response) void GdbEngine::handleExecuteJumpToLine(const GdbResponse &response) { if (response.resultClass == GdbResultRunning) { - notifyInferiorRunOk(); + doNotifyInferiorRunOk(); // All is fine. Waiting for the temporary breakpoint to be hit. } else if (response.resultClass == GdbResultDone) { // This happens on old gdb. Trigger the effect of a '*stopped'. @@ -1116,7 +1125,7 @@ void GdbEngine::handleExecuteJumpToLine(const GdbResponse &response) void GdbEngine::handleExecuteRunToLine(const GdbResponse &response) { if (response.resultClass == GdbResultRunning) { - notifyInferiorRunOk(); + doNotifyInferiorRunOk(); // All is fine. Waiting for the temporary breakpoint to be hit. } else if (response.resultClass == GdbResultDone) { // This happens on old gdb. Trigger the effect of a '*stopped'. @@ -1275,7 +1284,7 @@ void GdbEngine::handleStopResponse(const GdbMi &data) // be handled in the result handler. // -- or -- // *stopped arriving earlier than ^done response to an -exec-step - notifyInferiorRunOk(); + doNotifyInferiorRunOk(); notifyInferiorSpontaneousStop(); } else if (state() == InferiorStopOk && isQmlStepBreakpoint2(bkptno)) { // That's expected. @@ -1630,7 +1639,7 @@ void GdbEngine::handleExecuteContinue(const GdbResponse &response) { QTC_ASSERT(state() == InferiorRunRequested, qDebug() << state()); if (response.resultClass == GdbResultRunning) { - notifyInferiorRunOk(); + doNotifyInferiorRunOk(); return; } QByteArray msg = response.data.findChild("msg").data(); @@ -1871,6 +1880,12 @@ void GdbEngine::continueInferiorInternal() postCommand("-exec-continue", RunRequest, CB(handleExecuteContinue)); } +void GdbEngine::doNotifyInferiorRunOk() +{ + clearToolTip(); + notifyInferiorRunOk(); +} + void GdbEngine::autoContinueInferior() { resetLocation(); @@ -1909,7 +1924,7 @@ void GdbEngine::handleExecuteStep(const GdbResponse &response) } QTC_ASSERT(state() == InferiorRunRequested, qDebug() << state()); if (response.resultClass == GdbResultRunning) { - notifyInferiorRunOk(); + doNotifyInferiorRunOk(); return; } QByteArray msg = response.data.findChild("msg").data(); @@ -1972,7 +1987,7 @@ void GdbEngine::handleExecuteNext(const GdbResponse &response) } QTC_ASSERT(state() == InferiorRunRequested, qDebug() << state()); if (response.resultClass == GdbResultRunning) { - notifyInferiorRunOk(); + doNotifyInferiorRunOk(); return; } QTC_ASSERT(state() == InferiorStopOk, qDebug() << state()); @@ -3366,7 +3381,12 @@ bool GdbEngine::supportsThreads() const bool GdbEngine::showToolTip() { - const QByteArray iname = tooltipIName(m_toolTipExpression); + if (m_toolTipContext.isNull()) + return false; + const QString expression = m_toolTipContext->expression; + const QByteArray iname = tooltipIName(m_toolTipContext->expression); + if (DebuggerToolTipManager::debug()) + qDebug() << "GdbEngine::showToolTip " << expression << iname << (*m_toolTipContext); if (!debuggerCore()->boolSetting(UseToolTipsInMainEditor)) { watchHandler()->removeData(iname); @@ -3376,15 +3396,29 @@ bool GdbEngine::showToolTip() const QModelIndex index = watchHandler()->itemIndex(iname); if (!index.isValid()) { watchHandler()->removeData(iname); - hideDebuggerToolTip(); return false; } - showDebuggerToolTip(m_toolTipPos, index); + DebuggerTreeViewToolTipWidget *tw = new DebuggerTreeViewToolTipWidget; + tw->setDebuggerModel(TooltipsWatch); + tw->setExpression(expression); + tw->setContext(*m_toolTipContext); + tw->acquireEngine(this); + DebuggerToolTipManager::instance()->add(m_toolTipContext->mousePosition, tw); return true; } +QString GdbEngine::tooltipExpression() const +{ + return m_toolTipContext.isNull() ? QString() : m_toolTipContext->expression; +} + +void GdbEngine::clearToolTip() +{ + m_toolTipContext.reset(); +} + void GdbEngine::setToolTipExpression(const QPoint &mousePos, - TextEditor::ITextEditor *editor, int cursorPos) + TextEditor::ITextEditor *editor, const DebuggerToolTipContext &contextIn) { if (state() != InferiorStopOk || !isCppEditor(editor)) { //qDebug() << "SUPPRESSING DEBUGGER TOOLTIP, INFERIOR NOT STOPPED " @@ -3392,9 +3426,13 @@ void GdbEngine::setToolTipExpression(const QPoint &mousePos, return; } - m_toolTipPos = mousePos; + DebuggerToolTipContext context = contextIn; int line, column; - QString exp = cppExpressionAt(editor, cursorPos, &line, &column); + QString exp = cppExpressionAt(editor, context.position, &line, &column, &context.function); + if (DebuggerToolTipManager::debug()) + qDebug() << "GdbEngine::setToolTipExpression1 " << exp << context; + if (exp.isEmpty()) + return; // Extract the first identifier, everything else is considered // too dangerous. @@ -3414,36 +3452,12 @@ void GdbEngine::setToolTipExpression(const QPoint &mousePos, } exp = exp.mid(pos1, pos2 - pos1); - if (!exp.isEmpty() && exp == m_toolTipExpression) { - showToolTip(); + if (exp.isEmpty() || exp.startsWith(_c('#')) || !hasLetterOrNumber(exp) || isKeyWord(exp)) return; - } - - m_toolTipExpression = exp; - - // FIXME: enable caching - //if (showToolTip()) - // return; - if (exp.isEmpty() || exp.startsWith(_c('#'))) { - //QToolTip::hideText(); - return; - } - - if (!hasLetterOrNumber(exp)) { - //QToolTip::showText(m_toolTipPos, - // tr("'%1' contains no identifier").arg(exp)); - return; - } - - if (isKeyWord(exp)) + if (exp.startsWith(_c('"')) && exp.endsWith(_c('"'))) return; - if (exp.startsWith(_c('"')) && exp.endsWith(_c('"'))) { - //QToolTip::showText(m_toolTipPos, tr("String literal %1").arg(exp)); - return; - } - if (exp.startsWith(__("++")) || exp.startsWith(__("--"))) exp = exp.mid(2); @@ -3453,29 +3467,19 @@ void GdbEngine::setToolTipExpression(const QPoint &mousePos, if (exp.startsWith(_c('<')) || exp.startsWith(_c('['))) return; - if (hasSideEffects(exp)) { - //QToolTip::showText(m_toolTipPos, - // tr("Cowardly refusing to evaluate expression '%1' " - // "with potential side effects").arg(exp)); + if (hasSideEffects(exp) || exp.isEmpty()) return; - } - // Gdb crashes when creating a variable object with the name - // of the type of 'this' -/* - for (int i = 0; i != m_currentLocals.childCount(); ++i) { - if (m_currentLocals.childAt(i).exp == "this") { - qDebug() << "THIS IN ROW" << i; - if (m_currentLocals.childAt(i).type.startsWith(exp)) { - QToolTip::showText(m_toolTipPos, - tr("%1: type of current 'this'").arg(exp)); - qDebug() << " TOOLTIP CRASH SUPPRESSED"; - return; - } - break; - } + if (!m_toolTipContext.isNull() && m_toolTipContext->expression == exp) { + showToolTip(); + return; } -*/ + + m_toolTipContext.reset(new GdbToolTipContext(context)); + m_toolTipContext->mousePosition = mousePos; + m_toolTipContext->expression = exp; + if (DebuggerToolTipManager::debug()) + qDebug() << "GdbEngine::setToolTipExpression2 " << exp << (*m_toolTipContext); if (isSynchronous()) { updateLocals(QVariant()); @@ -4587,13 +4591,6 @@ void GdbEngine::resetCommandQueue() } } -void GdbEngine::removeTooltip() -{ - m_toolTipExpression.clear(); - m_toolTipPos = QPoint(); - DebuggerEngine::removeTooltip(); -} - void GdbEngine::handleRemoteSetupDone(int gdbServerPort, int qmlPort) { m_gdbAdapter->handleRemoteSetupDone(gdbServerPort, qmlPort); diff --git a/src/plugins/debugger/gdb/gdbengine.h b/src/plugins/debugger/gdb/gdbengine.h index d452c74cb1dabab44bf5203c494e99ceb1e5956f..3a1af99c3f51d65b1dfe785b4c684d6d077ade1f 100644 --- a/src/plugins/debugger/gdb/gdbengine.h +++ b/src/plugins/debugger/gdb/gdbengine.h @@ -60,6 +60,7 @@ class AbstractGdbAdapter; class AbstractGdbProcess; class GdbResponse; class GdbMi; +class GdbToolTipContext; class WatchData; class DisassemblerAgentCookie; @@ -325,6 +326,7 @@ private: ////////// Inferior Management ////////// void executeNextI(); void continueInferiorInternal(); + void doNotifyInferiorRunOk(); void autoContinueInferior(); void continueInferior(); void interruptInferior(); @@ -474,8 +476,7 @@ private: ////////// View & Data Stuff ////////// // Watch specific stuff // virtual void setToolTipExpression(const QPoint &mousePos, - TextEditor::ITextEditor *editor, int cursorPos); - + TextEditor::ITextEditor *editor, const DebuggerToolTipContext &); virtual void assignValueInDebugger(const WatchData *data, const QString &expr, const QVariant &value); @@ -555,10 +556,11 @@ private: ////////// View & Data Stuff ////////// AbstractGdbProcess *gdbProc() const; void showExecutionError(const QString &message); - void removeTooltip(); static QByteArray tooltipIName(const QString &exp); - QString m_toolTipExpression; - QPoint m_toolTipPos; + QString tooltipExpression() const; + void clearToolTip(); + QScopedPointer<GdbToolTipContext> m_toolTipContext; + // For short-circuiting stack and thread list evaluation. bool m_stackNeeded; diff --git a/src/plugins/debugger/gdb/pythongdbengine.cpp b/src/plugins/debugger/gdb/pythongdbengine.cpp index cdf97a364bcd959bec443bdf38d5543786e538e6..dff647e17c5d6f3662c6206eb3f9309f803a8246 100644 --- a/src/plugins/debugger/gdb/pythongdbengine.cpp +++ b/src/plugins/debugger/gdb/pythongdbengine.cpp @@ -37,6 +37,7 @@ #include "debuggeractions.h" #include "debuggercore.h" #include "debuggerstringutils.h" +#include "debuggertooltipmanager.h" #include "breakhandler.h" #include "watchhandler.h" @@ -64,10 +65,21 @@ void GdbEngine::updateLocalsPython(bool tryPartial, const QByteArray &varList) expanded += "formats:" + handler->individualFormatRequests(); QByteArray watchers; - if (!m_toolTipExpression.isEmpty()) { - watchers += m_toolTipExpression.toLatin1(); - watchers += '#'; - watchers += tooltipIName(m_toolTipExpression); + const QString fileName = stackHandler()->currentFrame().file; + const QString function = stackHandler()->currentFrame().function; + if (!fileName.isEmpty()) { + QStringList expressions = + DebuggerToolTipManager::instance()->treeWidgetExpressions(fileName, objectName(), function); + const QString currentExpression = tooltipExpression(); + if (!currentExpression.isEmpty() && !expressions.contains(currentExpression)) + expressions.push_back(currentExpression); + foreach (const QString &te, expressions) { + if (!watchers.isEmpty()) + watchers += "##"; + watchers += te.toLatin1(); + watchers += '#'; + watchers += tooltipIName(te); + } } QHash<QByteArray, int> watcherNames = handler->watcherNames(); @@ -120,7 +132,6 @@ void GdbEngine::handleStackFramePython(const GdbResponse &response) PRECONDITION; if (response.resultClass == GdbResultDone) { const bool partial = response.cookie.toBool(); - Q_UNUSED(partial); //qDebug() << "READING " << (partial ? "PARTIAL" : "FULL"); QByteArray out = response.data.findChild("consolestreamoutput").data(); while (out.endsWith(' ') || out.endsWith('\n')) @@ -192,6 +203,8 @@ void GdbEngine::handleStackFramePython(const GdbResponse &response) //PENDING_DEBUG("\n\n .... AND TRIGGERS MODEL UPDATE\n"); rebuildWatchModel(); } + if (!partial) + emit stackFrameCompleted(); } else { showMessage(_("DUMPER FAILED: " + response.toString())); } diff --git a/src/plugins/debugger/images/pin.xpm b/src/plugins/debugger/images/pin.xpm new file mode 100644 index 0000000000000000000000000000000000000000..0759becb67f684586ae6fb13d23a2b64905a7e6a --- /dev/null +++ b/src/plugins/debugger/images/pin.xpm @@ -0,0 +1,19 @@ +/* XPM */ +static const char * pin_xpm[] = { +"12 9 7 1", +" c None", +". c #000000", +"+ c #515151", +"@ c #A8A8A8", +"# c #A9A9A9", +"$ c #999999", +"% c #696969", +" . ", +" ......+", +" .@@@@@.", +" .#####.", +"+.....$$$$$.", +" .%%%%%.", +" .......", +" ......+", +" . "}; diff --git a/src/plugins/debugger/pdb/pdbengine.cpp b/src/plugins/debugger/pdb/pdbengine.cpp index 5f9d8562ee66f9c8f4721037b8775e7444717356..a0547b7383e5a6b0e325bfedca13486aa8ef8719 100644 --- a/src/plugins/debugger/pdb/pdbengine.cpp +++ b/src/plugins/debugger/pdb/pdbengine.cpp @@ -41,6 +41,7 @@ #include "debuggerdialogs.h" #include "debuggerplugin.h" #include "debuggerstringutils.h" +#include "debuggertooltipmanager.h" #include "breakhandler.h" #include "moduleshandler.h" @@ -456,11 +457,10 @@ static QPoint m_toolTipPos; static QHash<QString, WatchData> m_toolTipCache; void PdbEngine::setToolTipExpression(const QPoint &mousePos, - TextEditor::ITextEditor *editor, int cursorPos) + TextEditor::ITextEditor *editor, const DebuggerToolTipContext &ctx) { Q_UNUSED(mousePos) Q_UNUSED(editor) - Q_UNUSED(cursorPos) if (state() != InferiorStopOk) { //SDEBUG("SUPPRESSING DEBUGGER TOOLTIP, INFERIOR NOT STOPPED"); @@ -474,7 +474,7 @@ void PdbEngine::setToolTipExpression(const QPoint &mousePos, int line; int column; - QString exp = cppExpressionAt(editor, cursorPos, &line, &column); + QString exp = cppExpressionAt(editor, ctx.position, &line, &column); /* if (m_toolTipCache.contains(exp)) { diff --git a/src/plugins/debugger/pdb/pdbengine.h b/src/plugins/debugger/pdb/pdbengine.h index db00d408fbd4759de21a1495be195b33cb284c9f..8da4377e42cf97a51b538b04a40d61688b8618df 100644 --- a/src/plugins/debugger/pdb/pdbengine.h +++ b/src/plugins/debugger/pdb/pdbengine.h @@ -80,7 +80,7 @@ private: void shutdownEngine(); void setToolTipExpression(const QPoint &mousePos, - TextEditor::ITextEditor *editor, int cursorPos); + TextEditor::ITextEditor *editor, const DebuggerToolTipContext &); void continueInferior(); void interruptInferior(); diff --git a/src/plugins/debugger/qml/qmlcppengine.cpp b/src/plugins/debugger/qml/qmlcppengine.cpp index 6326366f10ebeae42990d0ab11aa6ea9e22da686..f3b31fa08e9e4691a8d067ca18ddaf1a9ec9e4a0 100644 --- a/src/plugins/debugger/qml/qmlcppengine.cpp +++ b/src/plugins/debugger/qml/qmlcppengine.cpp @@ -138,9 +138,9 @@ QmlCppEngine::~QmlCppEngine() } void QmlCppEngine::setToolTipExpression(const QPoint & mousePos, - TextEditor::ITextEditor *editor, int cursorPos) + TextEditor::ITextEditor *editor, const DebuggerToolTipContext &ctx) { - d->m_activeEngine->setToolTipExpression(mousePos, editor, cursorPos); + d->m_activeEngine->setToolTipExpression(mousePos, editor, ctx); } void QmlCppEngine::updateWatchData(const WatchData &data, diff --git a/src/plugins/debugger/qml/qmlcppengine.h b/src/plugins/debugger/qml/qmlcppengine.h index 20fb497f0533edd7565eac30ef226d7efadb48dc..f0cf10e6fb32b9c27d09aa0c4651620c1bfd4d9f 100644 --- a/src/plugins/debugger/qml/qmlcppengine.h +++ b/src/plugins/debugger/qml/qmlcppengine.h @@ -19,7 +19,7 @@ public: ~QmlCppEngine(); void setToolTipExpression(const QPoint &mousePos, - TextEditor::ITextEditor * editor, int cursorPos); + TextEditor::ITextEditor * editor, const DebuggerToolTipContext &); void updateWatchData(const WatchData &data, const WatchUpdateFlags &flags); diff --git a/src/plugins/debugger/qml/qmlengine.cpp b/src/plugins/debugger/qml/qmlengine.cpp index e97cf49335dcfd24f9185848da22089f9172345e..b1a5cbe59d31a8ba377c2d25c96c22f796054d1b 100644 --- a/src/plugins/debugger/qml/qmlengine.cpp +++ b/src/plugins/debugger/qml/qmlengine.cpp @@ -42,7 +42,7 @@ #include "debuggermainwindow.h" #include "debuggerrunner.h" #include "debuggerstringutils.h" -#include "debuggertooltip.h" +#include "debuggertooltipmanager.h" #include "breakhandler.h" #include "moduleshandler.h" @@ -564,11 +564,11 @@ void QmlEngine::requestModuleSymbols(const QString &moduleName) ////////////////////////////////////////////////////////////////////// void QmlEngine::setToolTipExpression(const QPoint &mousePos, - TextEditor::ITextEditor *editor, int cursorPos) + TextEditor::ITextEditor *editor, const DebuggerToolTipContext &ctx) { // This is processed by QML inspector, which has dependencies to // the qml js editor. Makes life easier. - emit tooltipRequested(mousePos, editor, cursorPos); + emit tooltipRequested(mousePos, editor, ctx.position); } ////////////////////////////////////////////////////////////////////// diff --git a/src/plugins/debugger/qml/qmlengine.h b/src/plugins/debugger/qml/qmlengine.h index 4b5cd4832c8358109b1b79e7b748c6917c43ecff..3b900af02ed4fe8e025da2bd6d1057bc11dbc073 100644 --- a/src/plugins/debugger/qml/qmlengine.h +++ b/src/plugins/debugger/qml/qmlengine.h @@ -85,7 +85,7 @@ private: void shutdownEngine(); void setToolTipExpression(const QPoint &mousePos, - TextEditor::ITextEditor *editor, int cursorPos); + TextEditor::ITextEditor *editor, const DebuggerToolTipContext &); void continueInferior(); void interruptInferior(); diff --git a/src/plugins/debugger/script/scriptengine.cpp b/src/plugins/debugger/script/scriptengine.cpp index d441e72d27e8d17691e549144ed3df132a159a11..6b6a1e33d611a65a3a5f1daa4cdd8326b80ce702 100644 --- a/src/plugins/debugger/script/scriptengine.cpp +++ b/src/plugins/debugger/script/scriptengine.cpp @@ -46,6 +46,7 @@ #include "stackhandler.h" #include "watchhandler.h" #include "watchutils.h" +#include "debuggertooltipmanager.h" #include <utils/qtcassert.h> @@ -512,11 +513,10 @@ static QPoint m_toolTipPos; static QHash<QString, WatchData> m_toolTipCache; void ScriptEngine::setToolTipExpression(const QPoint &mousePos, - TextEditor::ITextEditor *editor, int cursorPos) + TextEditor::ITextEditor *editor, const DebuggerToolTipContext &ctx) { Q_UNUSED(mousePos) Q_UNUSED(editor) - Q_UNUSED(cursorPos) if (state() != InferiorStopOk) { //SDEBUG("SUPPRESSING DEBUGGER TOOLTIP, INFERIOR NOT STOPPED"); @@ -530,7 +530,7 @@ void ScriptEngine::setToolTipExpression(const QPoint &mousePos, int line; int column; - QString exp = cppExpressionAt(editor, cursorPos, &line, &column); + QString exp = cppExpressionAt(editor, ctx.position, &line, &column); /* if (m_toolTipCache.contains(exp)) { diff --git a/src/plugins/debugger/script/scriptengine.h b/src/plugins/debugger/script/scriptengine.h index 93220af84c729fdc2d04918e6ad15b715a7e6cf2..b34ae49b5818657925b36bd85809c28fae61c443 100644 --- a/src/plugins/debugger/script/scriptengine.h +++ b/src/plugins/debugger/script/scriptengine.h @@ -74,7 +74,7 @@ private: void executeNextI(); void setToolTipExpression(const QPoint &mousePos, - TextEditor::ITextEditor *editor, int cursorPos); + TextEditor::ITextEditor *editor, const DebuggerToolTipContext &); void setupEngine(); void setupInferior(); void runEngine(); diff --git a/src/plugins/debugger/tcf/tcfengine.cpp b/src/plugins/debugger/tcf/tcfengine.cpp index f256705279f8e18810211e028cb26398462a054e..a06910f6c1038127b12220736692e5b2bddd82f8 100644 --- a/src/plugins/debugger/tcf/tcfengine.cpp +++ b/src/plugins/debugger/tcf/tcfengine.cpp @@ -535,11 +535,10 @@ static WatchData m_toolTip; static QPoint m_toolTipPos; static QHash<QString, WatchData> m_toolTipCache; -void TcfEngine::setToolTipExpression(const QPoint &mousePos, TextEditor::ITextEditor *editor, int cursorPos) +void TcfEngine::setToolTipExpression(const QPoint &mousePos, TextEditor::ITextEditor *editor, const DebuggerToolTipContext &) { Q_UNUSED(mousePos) Q_UNUSED(editor) - Q_UNUSED(cursorPos) } ////////////////////////////////////////////////////////////////////// diff --git a/src/plugins/debugger/tcf/tcfengine.h b/src/plugins/debugger/tcf/tcfengine.h index 3545e8d607b0fd24d0b391d3d2c974edb0a3b9cd..d3ade0a2e7e57371e6935cfcf628df4ed6485cdf 100644 --- a/src/plugins/debugger/tcf/tcfengine.h +++ b/src/plugins/debugger/tcf/tcfengine.h @@ -83,7 +83,7 @@ private: void shutdownEngine(); void setToolTipExpression(const QPoint &mousePos, - TextEditor::ITextEditor *editor, int cursorPos); + TextEditor::ITextEditor *editor, const DebuggerToolTipContext &); void continueInferior(); void interruptInferior(); diff --git a/src/plugins/debugger/watchhandler.h b/src/plugins/debugger/watchhandler.h index e7c64ea227dd569230abf2789b6a2b67e74254f6..10d96826ec0e53c265b540dea3710e0cf45d374f 100644 --- a/src/plugins/debugger/watchhandler.h +++ b/src/plugins/debugger/watchhandler.h @@ -75,8 +75,8 @@ private: virtual ~WatchModel(); public: - int rowCount(const QModelIndex &idx) const; - int columnCount(const QModelIndex &idx) const; + virtual int rowCount(const QModelIndex &idx = QModelIndex()) const; + virtual int columnCount(const QModelIndex &idx) const; private: QVariant data(const QModelIndex &index, int role) const;