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;